]> ToastFreeware Gitweb - chrisu/seepark.git/blob - web/static/c3.js
04eb151a3d4925a1c00ab3238337973699741675
[chrisu/seepark.git] / web / static / c3.js
1 /* @license C3.js v0.6.7 | (c) C3 Team and other contributors | http://c3js.org/ */
2 (function (global, factory) {
3     typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
4     typeof define === 'function' && define.amd ? define(factory) :
5     (global.c3 = factory());
6 }(this, (function () { 'use strict';
7
8     function ChartInternal(api) {
9         var $$ = this;
10         $$.d3 = window.d3 ? window.d3 : typeof require !== 'undefined' ? require("d3") : undefined;
11         $$.api = api;
12         $$.config = $$.getDefaultConfig();
13         $$.data = {};
14         $$.cache = {};
15         $$.axes = {};
16     }
17
18     function Chart(config) {
19         var $$ = this.internal = new ChartInternal(this);
20         $$.loadConfig(config);
21
22         $$.beforeInit(config);
23         $$.init();
24         $$.afterInit(config);
25
26         // bind "this" to nested API
27         (function bindThis(fn, target, argThis) {
28             Object.keys(fn).forEach(function (key) {
29                 target[key] = fn[key].bind(argThis);
30                 if (Object.keys(fn[key]).length > 0) {
31                     bindThis(fn[key], target[key], argThis);
32                 }
33             });
34         })(Chart.prototype, this, this);
35     }
36
37     function AxisInternal(component, params) {
38         var internal = this;
39         internal.component = component;
40         internal.params = params || {};
41
42         internal.d3 = component.d3;
43         internal.scale = internal.d3.scaleLinear();
44         internal.range;
45         internal.orient = "bottom";
46         internal.innerTickSize = 6;
47         internal.outerTickSize = this.params.withOuterTick ? 6 : 0;
48         internal.tickPadding = 3;
49         internal.tickValues = null;
50         internal.tickFormat;
51         internal.tickArguments;
52
53         internal.tickOffset = 0;
54         internal.tickCulling = true;
55         internal.tickCentered;
56         internal.tickTextCharSize;
57         internal.tickTextRotate = internal.params.tickTextRotate;
58         internal.tickLength;
59
60         internal.axis = internal.generateAxis();
61     }
62
63     AxisInternal.prototype.axisX = function (selection, x, tickOffset) {
64         selection.attr("transform", function (d) {
65             return "translate(" + Math.ceil(x(d) + tickOffset) + ", 0)";
66         });
67     };
68     AxisInternal.prototype.axisY = function (selection, y) {
69         selection.attr("transform", function (d) {
70             return "translate(0," + Math.ceil(y(d)) + ")";
71         });
72     };
73     AxisInternal.prototype.scaleExtent = function (domain) {
74         var start = domain[0],
75             stop = domain[domain.length - 1];
76         return start < stop ? [start, stop] : [stop, start];
77     };
78     AxisInternal.prototype.generateTicks = function (scale) {
79         var internal = this;
80         var i,
81             domain,
82             ticks = [];
83         if (scale.ticks) {
84             return scale.ticks.apply(scale, internal.tickArguments);
85         }
86         domain = scale.domain();
87         for (i = Math.ceil(domain[0]); i < domain[1]; i++) {
88             ticks.push(i);
89         }
90         if (ticks.length > 0 && ticks[0] > 0) {
91             ticks.unshift(ticks[0] - (ticks[1] - ticks[0]));
92         }
93         return ticks;
94     };
95     AxisInternal.prototype.copyScale = function () {
96         var internal = this;
97         var newScale = internal.scale.copy(),
98             domain;
99         if (internal.params.isCategory) {
100             domain = internal.scale.domain();
101             newScale.domain([domain[0], domain[1] - 1]);
102         }
103         return newScale;
104     };
105     AxisInternal.prototype.textFormatted = function (v) {
106         var internal = this,
107             formatted = internal.tickFormat ? internal.tickFormat(v) : v;
108         return typeof formatted !== 'undefined' ? formatted : '';
109     };
110     AxisInternal.prototype.updateRange = function () {
111         var internal = this;
112         internal.range = internal.scale.rangeExtent ? internal.scale.rangeExtent() : internal.scaleExtent(internal.scale.range());
113         return internal.range;
114     };
115     AxisInternal.prototype.updateTickTextCharSize = function (tick) {
116         var internal = this;
117         if (internal.tickTextCharSize) {
118             return internal.tickTextCharSize;
119         }
120         var size = {
121             h: 11.5,
122             w: 5.5
123         };
124         tick.select('text').text(function (d) {
125             return internal.textFormatted(d);
126         }).each(function (d) {
127             var box = this.getBoundingClientRect(),
128                 text = internal.textFormatted(d),
129                 h = box.height,
130                 w = text ? box.width / text.length : undefined;
131             if (h && w) {
132                 size.h = h;
133                 size.w = w;
134             }
135         }).text('');
136         internal.tickTextCharSize = size;
137         return size;
138     };
139     AxisInternal.prototype.isVertical = function () {
140         return this.orient === 'left' || this.orient === 'right';
141     };
142     AxisInternal.prototype.tspanData = function (d, i, scale) {
143         var internal = this;
144         var splitted = internal.params.tickMultiline ? internal.splitTickText(d, scale) : [].concat(internal.textFormatted(d));
145
146         if (internal.params.tickMultiline && internal.params.tickMultilineMax > 0) {
147             splitted = internal.ellipsify(splitted, internal.params.tickMultilineMax);
148         }
149
150         return splitted.map(function (s) {
151             return { index: i, splitted: s, length: splitted.length };
152         });
153     };
154     AxisInternal.prototype.splitTickText = function (d, scale) {
155         var internal = this,
156             tickText = internal.textFormatted(d),
157             maxWidth = internal.params.tickWidth,
158             subtext,
159             spaceIndex,
160             textWidth,
161             splitted = [];
162
163         if (Object.prototype.toString.call(tickText) === "[object Array]") {
164             return tickText;
165         }
166
167         if (!maxWidth || maxWidth <= 0) {
168             maxWidth = internal.isVertical() ? 95 : internal.params.isCategory ? Math.ceil(scale(1) - scale(0)) - 12 : 110;
169         }
170
171         function split(splitted, text) {
172             spaceIndex = undefined;
173             for (var i = 1; i < text.length; i++) {
174                 if (text.charAt(i) === ' ') {
175                     spaceIndex = i;
176                 }
177                 subtext = text.substr(0, i + 1);
178                 textWidth = internal.tickTextCharSize.w * subtext.length;
179                 // if text width gets over tick width, split by space index or crrent index
180                 if (maxWidth < textWidth) {
181                     return split(splitted.concat(text.substr(0, spaceIndex ? spaceIndex : i)), text.slice(spaceIndex ? spaceIndex + 1 : i));
182                 }
183             }
184             return splitted.concat(text);
185         }
186
187         return split(splitted, tickText + "");
188     };
189     AxisInternal.prototype.ellipsify = function (splitted, max) {
190         if (splitted.length <= max) {
191             return splitted;
192         }
193
194         var ellipsified = splitted.slice(0, max);
195         var remaining = 3;
196         for (var i = max - 1; i >= 0; i--) {
197             var available = ellipsified[i].length;
198
199             ellipsified[i] = ellipsified[i].substr(0, available - remaining).padEnd(available, '.');
200
201             remaining -= available;
202
203             if (remaining <= 0) {
204                 break;
205             }
206         }
207
208         return ellipsified;
209     };
210     AxisInternal.prototype.updateTickLength = function () {
211         var internal = this;
212         internal.tickLength = Math.max(internal.innerTickSize, 0) + internal.tickPadding;
213     };
214     AxisInternal.prototype.lineY2 = function (d) {
215         var internal = this,
216             tickPosition = internal.scale(d) + (internal.tickCentered ? 0 : internal.tickOffset);
217         return internal.range[0] < tickPosition && tickPosition < internal.range[1] ? internal.innerTickSize : 0;
218     };
219     AxisInternal.prototype.textY = function () {
220         var internal = this,
221             rotate = internal.tickTextRotate;
222         return rotate ? 11.5 - 2.5 * (rotate / 15) * (rotate > 0 ? 1 : -1) : internal.tickLength;
223     };
224     AxisInternal.prototype.textTransform = function () {
225         var internal = this,
226             rotate = internal.tickTextRotate;
227         return rotate ? "rotate(" + rotate + ")" : "";
228     };
229     AxisInternal.prototype.textTextAnchor = function () {
230         var internal = this,
231             rotate = internal.tickTextRotate;
232         return rotate ? rotate > 0 ? "start" : "end" : "middle";
233     };
234     AxisInternal.prototype.tspanDx = function () {
235         var internal = this,
236             rotate = internal.tickTextRotate;
237         return rotate ? 8 * Math.sin(Math.PI * (rotate / 180)) : 0;
238     };
239     AxisInternal.prototype.tspanDy = function (d, i) {
240         var internal = this,
241             dy = internal.tickTextCharSize.h;
242         if (i === 0) {
243             if (internal.isVertical()) {
244                 dy = -((d.length - 1) * (internal.tickTextCharSize.h / 2) - 3);
245             } else {
246                 dy = ".71em";
247             }
248         }
249         return dy;
250     };
251
252     AxisInternal.prototype.generateAxis = function () {
253         var internal = this,
254             d3 = internal.d3,
255             params = internal.params;
256         function axis(g, transition) {
257             var self;
258             g.each(function () {
259                 var g = axis.g = d3.select(this);
260
261                 var scale0 = this.__chart__ || internal.scale,
262                     scale1 = this.__chart__ = internal.copyScale();
263
264                 var ticksValues = internal.tickValues ? internal.tickValues : internal.generateTicks(scale1),
265                     ticks = g.selectAll(".tick").data(ticksValues, scale1),
266                     tickEnter = ticks.enter().insert("g", ".domain").attr("class", "tick").style("opacity", 1e-6),
267
268                 // MEMO: No exit transition. The reason is this transition affects max tick width calculation because old tick will be included in the ticks.
269                 tickExit = ticks.exit().remove(),
270                     tickUpdate = ticks.merge(tickEnter),
271                     tickTransform,
272                     tickX,
273                     tickY;
274
275                 if (params.isCategory) {
276                     internal.tickOffset = Math.ceil((scale1(1) - scale1(0)) / 2);
277                     tickX = internal.tickCentered ? 0 : internal.tickOffset;
278                     tickY = internal.tickCentered ? internal.tickOffset : 0;
279                 } else {
280                     internal.tickOffset = tickX = 0;
281                 }
282
283                 internal.updateRange();
284                 internal.updateTickLength();
285                 internal.updateTickTextCharSize(g.select('.tick'));
286
287                 var lineUpdate = tickUpdate.select("line").merge(tickEnter.append("line")),
288                     textUpdate = tickUpdate.select("text").merge(tickEnter.append("text"));
289
290                 var tspans = tickUpdate.selectAll('text').selectAll('tspan').data(function (d, i) {
291                     return internal.tspanData(d, i, scale1);
292                 }),
293                     tspanEnter = tspans.enter().append('tspan'),
294                     tspanUpdate = tspanEnter.merge(tspans).text(function (d) {
295                     return d.splitted;
296                 });
297                 tspans.exit().remove();
298
299                 var path = g.selectAll(".domain").data([0]),
300                     pathUpdate = path.enter().append("path").merge(path).attr("class", "domain");
301
302                 // TODO: each attr should be one function and change its behavior by internal.orient, probably
303                 switch (internal.orient) {
304                     case "bottom":
305                         {
306                             tickTransform = internal.axisX;
307                             lineUpdate.attr("x1", tickX).attr("x2", tickX).attr("y2", function (d, i) {
308                                 return internal.lineY2(d, i);
309                             });
310                             textUpdate.attr("x", 0).attr("y", function (d, i) {
311                                 return internal.textY(d, i);
312                             }).attr("transform", function (d, i) {
313                                 return internal.textTransform(d, i);
314                             }).style("text-anchor", function (d, i) {
315                                 return internal.textTextAnchor(d, i);
316                             });
317                             tspanUpdate.attr('x', 0).attr("dy", function (d, i) {
318                                 return internal.tspanDy(d, i);
319                             }).attr('dx', function (d, i) {
320                                 return internal.tspanDx(d, i);
321                             });
322                             pathUpdate.attr("d", "M" + internal.range[0] + "," + internal.outerTickSize + "V0H" + internal.range[1] + "V" + internal.outerTickSize);
323                             break;
324                         }
325                     case "top":
326                         {
327                             // TODO: rotated tick text
328                             tickTransform = internal.axisX;
329                             lineUpdate.attr("x1", tickX).attr("x2", tickX).attr("y2", function (d, i) {
330                                 return -1 * internal.lineY2(d, i);
331                             });
332                             textUpdate.attr("x", 0).attr("y", function (d, i) {
333                                 return -1 * internal.textY(d, i) - (params.isCategory ? 2 : internal.tickLength - 2);
334                             }).attr("transform", function (d, i) {
335                                 return internal.textTransform(d, i);
336                             }).style("text-anchor", function (d, i) {
337                                 return internal.textTextAnchor(d, i);
338                             });
339                             tspanUpdate.attr('x', 0).attr("dy", function (d, i) {
340                                 return internal.tspanDy(d, i);
341                             }).attr('dx', function (d, i) {
342                                 return internal.tspanDx(d, i);
343                             });
344                             pathUpdate.attr("d", "M" + internal.range[0] + "," + -internal.outerTickSize + "V0H" + internal.range[1] + "V" + -internal.outerTickSize);
345                             break;
346                         }
347                     case "left":
348                         {
349                             tickTransform = internal.axisY;
350                             lineUpdate.attr("x2", -internal.innerTickSize).attr("y1", tickY).attr("y2", tickY);
351                             textUpdate.attr("x", -internal.tickLength).attr("y", internal.tickOffset).style("text-anchor", "end");
352                             tspanUpdate.attr('x', -internal.tickLength).attr("dy", function (d, i) {
353                                 return internal.tspanDy(d, i);
354                             });
355                             pathUpdate.attr("d", "M" + -internal.outerTickSize + "," + internal.range[0] + "H0V" + internal.range[1] + "H" + -internal.outerTickSize);
356                             break;
357                         }
358                     case "right":
359                         {
360                             tickTransform = internal.axisY;
361                             lineUpdate.attr("x2", internal.innerTickSize).attr("y1", tickY).attr("y2", tickY);
362                             textUpdate.attr("x", internal.tickLength).attr("y", internal.tickOffset).style("text-anchor", "start");
363                             tspanUpdate.attr('x', internal.tickLength).attr("dy", function (d, i) {
364                                 return internal.tspanDy(d, i);
365                             });
366                             pathUpdate.attr("d", "M" + internal.outerTickSize + "," + internal.range[0] + "H0V" + internal.range[1] + "H" + internal.outerTickSize);
367                             break;
368                         }
369                 }
370                 if (scale1.rangeBand) {
371                     var x = scale1,
372                         dx = x.rangeBand() / 2;
373                     scale0 = scale1 = function scale1(d) {
374                         return x(d) + dx;
375                     };
376                 } else if (scale0.rangeBand) {
377                     scale0 = scale1;
378                 } else {
379                     tickExit.call(tickTransform, scale1, internal.tickOffset);
380                 }
381                 tickEnter.call(tickTransform, scale0, internal.tickOffset);
382                 self = (transition ? tickUpdate.transition(transition) : tickUpdate).style('opacity', 1).call(tickTransform, scale1, internal.tickOffset);
383             });
384             return self;
385         }
386         axis.scale = function (x) {
387             if (!arguments.length) {
388                 return internal.scale;
389             }
390             internal.scale = x;
391             return axis;
392         };
393         axis.orient = function (x) {
394             if (!arguments.length) {
395                 return internal.orient;
396             }
397             internal.orient = x in { top: 1, right: 1, bottom: 1, left: 1 } ? x + "" : "bottom";
398             return axis;
399         };
400         axis.tickFormat = function (format) {
401             if (!arguments.length) {
402                 return internal.tickFormat;
403             }
404             internal.tickFormat = format;
405             return axis;
406         };
407         axis.tickCentered = function (isCentered) {
408             if (!arguments.length) {
409                 return internal.tickCentered;
410             }
411             internal.tickCentered = isCentered;
412             return axis;
413         };
414         axis.tickOffset = function () {
415             return internal.tickOffset;
416         };
417         axis.tickInterval = function () {
418             var interval, length;
419             if (params.isCategory) {
420                 interval = internal.tickOffset * 2;
421             } else {
422                 length = axis.g.select('path.domain').node().getTotalLength() - internal.outerTickSize * 2;
423                 interval = length / axis.g.selectAll('line').size();
424             }
425             return interval === Infinity ? 0 : interval;
426         };
427         axis.ticks = function () {
428             if (!arguments.length) {
429                 return internal.tickArguments;
430             }
431             internal.tickArguments = arguments;
432             return axis;
433         };
434         axis.tickCulling = function (culling) {
435             if (!arguments.length) {
436                 return internal.tickCulling;
437             }
438             internal.tickCulling = culling;
439             return axis;
440         };
441         axis.tickValues = function (x) {
442             if (typeof x === 'function') {
443                 internal.tickValues = function () {
444                     return x(internal.scale.domain());
445                 };
446             } else {
447                 if (!arguments.length) {
448                     return internal.tickValues;
449                 }
450                 internal.tickValues = x;
451             }
452             return axis;
453         };
454         return axis;
455     };
456
457     var CLASS = {
458         target: 'c3-target',
459         chart: 'c3-chart',
460         chartLine: 'c3-chart-line',
461         chartLines: 'c3-chart-lines',
462         chartBar: 'c3-chart-bar',
463         chartBars: 'c3-chart-bars',
464         chartText: 'c3-chart-text',
465         chartTexts: 'c3-chart-texts',
466         chartArc: 'c3-chart-arc',
467         chartArcs: 'c3-chart-arcs',
468         chartArcsTitle: 'c3-chart-arcs-title',
469         chartArcsBackground: 'c3-chart-arcs-background',
470         chartArcsGaugeUnit: 'c3-chart-arcs-gauge-unit',
471         chartArcsGaugeMax: 'c3-chart-arcs-gauge-max',
472         chartArcsGaugeMin: 'c3-chart-arcs-gauge-min',
473         selectedCircle: 'c3-selected-circle',
474         selectedCircles: 'c3-selected-circles',
475         eventRect: 'c3-event-rect',
476         eventRects: 'c3-event-rects',
477         eventRectsSingle: 'c3-event-rects-single',
478         eventRectsMultiple: 'c3-event-rects-multiple',
479         zoomRect: 'c3-zoom-rect',
480         brush: 'c3-brush',
481         dragZoom: 'c3-drag-zoom',
482         focused: 'c3-focused',
483         defocused: 'c3-defocused',
484         region: 'c3-region',
485         regions: 'c3-regions',
486         title: 'c3-title',
487         tooltipContainer: 'c3-tooltip-container',
488         tooltip: 'c3-tooltip',
489         tooltipName: 'c3-tooltip-name',
490         shape: 'c3-shape',
491         shapes: 'c3-shapes',
492         line: 'c3-line',
493         lines: 'c3-lines',
494         bar: 'c3-bar',
495         bars: 'c3-bars',
496         circle: 'c3-circle',
497         circles: 'c3-circles',
498         arc: 'c3-arc',
499         arcLabelLine: 'c3-arc-label-line',
500         arcs: 'c3-arcs',
501         area: 'c3-area',
502         areas: 'c3-areas',
503         empty: 'c3-empty',
504         text: 'c3-text',
505         texts: 'c3-texts',
506         gaugeValue: 'c3-gauge-value',
507         grid: 'c3-grid',
508         gridLines: 'c3-grid-lines',
509         xgrid: 'c3-xgrid',
510         xgrids: 'c3-xgrids',
511         xgridLine: 'c3-xgrid-line',
512         xgridLines: 'c3-xgrid-lines',
513         xgridFocus: 'c3-xgrid-focus',
514         ygrid: 'c3-ygrid',
515         ygrids: 'c3-ygrids',
516         ygridLine: 'c3-ygrid-line',
517         ygridLines: 'c3-ygrid-lines',
518         axis: 'c3-axis',
519         axisX: 'c3-axis-x',
520         axisXLabel: 'c3-axis-x-label',
521         axisY: 'c3-axis-y',
522         axisYLabel: 'c3-axis-y-label',
523         axisY2: 'c3-axis-y2',
524         axisY2Label: 'c3-axis-y2-label',
525         legendBackground: 'c3-legend-background',
526         legendItem: 'c3-legend-item',
527         legendItemEvent: 'c3-legend-item-event',
528         legendItemTile: 'c3-legend-item-tile',
529         legendItemHidden: 'c3-legend-item-hidden',
530         legendItemFocused: 'c3-legend-item-focused',
531         dragarea: 'c3-dragarea',
532         EXPANDED: '_expanded_',
533         SELECTED: '_selected_',
534         INCLUDED: '_included_'
535     };
536
537     var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
538       return typeof obj;
539     } : function (obj) {
540       return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
541     };
542
543     var classCallCheck = function (instance, Constructor) {
544       if (!(instance instanceof Constructor)) {
545         throw new TypeError("Cannot call a class as a function");
546       }
547     };
548
549     var defineProperty = function (obj, key, value) {
550       if (key in obj) {
551         Object.defineProperty(obj, key, {
552           value: value,
553           enumerable: true,
554           configurable: true,
555           writable: true
556         });
557       } else {
558         obj[key] = value;
559       }
560
561       return obj;
562     };
563
564     var asHalfPixel = function asHalfPixel(n) {
565         return Math.ceil(n) + 0.5;
566     };
567     var ceil10 = function ceil10(v) {
568         return Math.ceil(v / 10) * 10;
569     };
570     var diffDomain = function diffDomain(d) {
571         return d[1] - d[0];
572     };
573     var getOption = function getOption(options, key, defaultValue) {
574         return isDefined(options[key]) ? options[key] : defaultValue;
575     };
576     var getPathBox = function getPathBox(path) {
577         var box = path.getBoundingClientRect(),
578             items = [path.pathSegList.getItem(0), path.pathSegList.getItem(1)],
579             minX = items[0].x,
580             minY = Math.min(items[0].y, items[1].y);
581         return { x: minX, y: minY, width: box.width, height: box.height };
582     };
583     var hasValue = function hasValue(dict, value) {
584         var found = false;
585         Object.keys(dict).forEach(function (key) {
586             if (dict[key] === value) {
587                 found = true;
588             }
589         });
590         return found;
591     };
592     var isArray = function isArray(o) {
593         return Array.isArray(o);
594     };
595     var isDefined = function isDefined(v) {
596         return typeof v !== 'undefined';
597     };
598     var isEmpty = function isEmpty(o) {
599         return typeof o === 'undefined' || o === null || isString(o) && o.length === 0 || (typeof o === 'undefined' ? 'undefined' : _typeof(o)) === 'object' && Object.keys(o).length === 0;
600     };
601     var isFunction = function isFunction(o) {
602         return typeof o === 'function';
603     };
604     var isString = function isString(o) {
605         return typeof o === 'string';
606     };
607     var isUndefined = function isUndefined(v) {
608         return typeof v === 'undefined';
609     };
610     var isValue = function isValue(v) {
611         return v || v === 0;
612     };
613     var notEmpty = function notEmpty(o) {
614         return !isEmpty(o);
615     };
616     var sanitise = function sanitise(str) {
617         return typeof str === 'string' ? str.replace(/</g, '&lt;').replace(/>/g, '&gt;') : str;
618     };
619
620     var Axis = function Axis(owner) {
621         classCallCheck(this, Axis);
622
623         this.owner = owner;
624         this.d3 = owner.d3;
625         this.internal = AxisInternal;
626     };
627
628     Axis.prototype.init = function init() {
629         var $$ = this.owner,
630             config = $$.config,
631             main = $$.main;
632         $$.axes.x = main.append("g").attr("class", CLASS.axis + ' ' + CLASS.axisX).attr("clip-path", config.axis_x_inner ? "" : $$.clipPathForXAxis).attr("transform", $$.getTranslate('x')).style("visibility", config.axis_x_show ? 'visible' : 'hidden');
633         $$.axes.x.append("text").attr("class", CLASS.axisXLabel).attr("transform", config.axis_rotated ? "rotate(-90)" : "").style("text-anchor", this.textAnchorForXAxisLabel.bind(this));
634         $$.axes.y = main.append("g").attr("class", CLASS.axis + ' ' + CLASS.axisY).attr("clip-path", config.axis_y_inner ? "" : $$.clipPathForYAxis).attr("transform", $$.getTranslate('y')).style("visibility", config.axis_y_show ? 'visible' : 'hidden');
635         $$.axes.y.append("text").attr("class", CLASS.axisYLabel).attr("transform", config.axis_rotated ? "" : "rotate(-90)").style("text-anchor", this.textAnchorForYAxisLabel.bind(this));
636
637         $$.axes.y2 = main.append("g").attr("class", CLASS.axis + ' ' + CLASS.axisY2)
638         // clip-path?
639         .attr("transform", $$.getTranslate('y2')).style("visibility", config.axis_y2_show ? 'visible' : 'hidden');
640         $$.axes.y2.append("text").attr("class", CLASS.axisY2Label).attr("transform", config.axis_rotated ? "" : "rotate(-90)").style("text-anchor", this.textAnchorForY2AxisLabel.bind(this));
641     };
642     Axis.prototype.getXAxis = function getXAxis(scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition, withoutRotateTickText) {
643         var $$ = this.owner,
644             config = $$.config,
645             axisParams = {
646             isCategory: $$.isCategorized(),
647             withOuterTick: withOuterTick,
648             tickMultiline: config.axis_x_tick_multiline,
649             tickMultilineMax: config.axis_x_tick_multiline ? Number(config.axis_x_tick_multilineMax) : 0,
650             tickWidth: config.axis_x_tick_width,
651             tickTextRotate: withoutRotateTickText ? 0 : config.axis_x_tick_rotate,
652             withoutTransition: withoutTransition
653         },
654             axis = new this.internal(this, axisParams).axis.scale(scale).orient(orient);
655
656         if ($$.isTimeSeries() && tickValues && typeof tickValues !== "function") {
657             tickValues = tickValues.map(function (v) {
658                 return $$.parseDate(v);
659             });
660         }
661
662         // Set tick
663         axis.tickFormat(tickFormat).tickValues(tickValues);
664         if ($$.isCategorized()) {
665             axis.tickCentered(config.axis_x_tick_centered);
666             if (isEmpty(config.axis_x_tick_culling)) {
667                 config.axis_x_tick_culling = false;
668             }
669         }
670
671         return axis;
672     };
673     Axis.prototype.updateXAxisTickValues = function updateXAxisTickValues(targets, axis) {
674         var $$ = this.owner,
675             config = $$.config,
676             tickValues;
677         if (config.axis_x_tick_fit || config.axis_x_tick_count) {
678             tickValues = this.generateTickValues($$.mapTargetsToUniqueXs(targets), config.axis_x_tick_count, $$.isTimeSeries());
679         }
680         if (axis) {
681             axis.tickValues(tickValues);
682         } else {
683             $$.xAxis.tickValues(tickValues);
684             $$.subXAxis.tickValues(tickValues);
685         }
686         return tickValues;
687     };
688     Axis.prototype.getYAxis = function getYAxis(scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition, withoutRotateTickText) {
689         var $$ = this.owner,
690             config = $$.config,
691             axisParams = {
692             withOuterTick: withOuterTick,
693             withoutTransition: withoutTransition,
694             tickTextRotate: withoutRotateTickText ? 0 : config.axis_y_tick_rotate
695         },
696             axis = new this.internal(this, axisParams).axis.scale(scale).orient(orient).tickFormat(tickFormat);
697         if ($$.isTimeSeriesY()) {
698             axis.ticks(config.axis_y_tick_time_type, config.axis_y_tick_time_interval);
699         } else {
700             axis.tickValues(tickValues);
701         }
702         return axis;
703     };
704     Axis.prototype.getId = function getId(id) {
705         var config = this.owner.config;
706         return id in config.data_axes ? config.data_axes[id] : 'y';
707     };
708     Axis.prototype.getXAxisTickFormat = function getXAxisTickFormat() {
709         // #2251 previously set any negative values to a whole number,
710         // however both should be truncated according to the users format specification
711         var $$ = this.owner,
712             config = $$.config;
713         var format = $$.isTimeSeries() ? $$.defaultAxisTimeFormat : $$.isCategorized() ? $$.categoryName : function (v) {
714             return v;
715         };
716
717         if (config.axis_x_tick_format) {
718             if (isFunction(config.axis_x_tick_format)) {
719                 format = config.axis_x_tick_format;
720             } else if ($$.isTimeSeries()) {
721                 format = function format(date) {
722                     return date ? $$.axisTimeFormat(config.axis_x_tick_format)(date) : "";
723                 };
724             }
725         }
726         return isFunction(format) ? function (v) {
727             return format.call($$, v);
728         } : format;
729     };
730     Axis.prototype.getTickValues = function getTickValues(tickValues, axis) {
731         return tickValues ? tickValues : axis ? axis.tickValues() : undefined;
732     };
733     Axis.prototype.getXAxisTickValues = function getXAxisTickValues() {
734         return this.getTickValues(this.owner.config.axis_x_tick_values, this.owner.xAxis);
735     };
736     Axis.prototype.getYAxisTickValues = function getYAxisTickValues() {
737         return this.getTickValues(this.owner.config.axis_y_tick_values, this.owner.yAxis);
738     };
739     Axis.prototype.getY2AxisTickValues = function getY2AxisTickValues() {
740         return this.getTickValues(this.owner.config.axis_y2_tick_values, this.owner.y2Axis);
741     };
742     Axis.prototype.getLabelOptionByAxisId = function getLabelOptionByAxisId(axisId) {
743         var $$ = this.owner,
744             config = $$.config,
745             option;
746         if (axisId === 'y') {
747             option = config.axis_y_label;
748         } else if (axisId === 'y2') {
749             option = config.axis_y2_label;
750         } else if (axisId === 'x') {
751             option = config.axis_x_label;
752         }
753         return option;
754     };
755     Axis.prototype.getLabelText = function getLabelText(axisId) {
756         var option = this.getLabelOptionByAxisId(axisId);
757         return isString(option) ? option : option ? option.text : null;
758     };
759     Axis.prototype.setLabelText = function setLabelText(axisId, text) {
760         var $$ = this.owner,
761             config = $$.config,
762             option = this.getLabelOptionByAxisId(axisId);
763         if (isString(option)) {
764             if (axisId === 'y') {
765                 config.axis_y_label = text;
766             } else if (axisId === 'y2') {
767                 config.axis_y2_label = text;
768             } else if (axisId === 'x') {
769                 config.axis_x_label = text;
770             }
771         } else if (option) {
772             option.text = text;
773         }
774     };
775     Axis.prototype.getLabelPosition = function getLabelPosition(axisId, defaultPosition) {
776         var option = this.getLabelOptionByAxisId(axisId),
777             position = option && (typeof option === 'undefined' ? 'undefined' : _typeof(option)) === 'object' && option.position ? option.position : defaultPosition;
778         return {
779             isInner: position.indexOf('inner') >= 0,
780             isOuter: position.indexOf('outer') >= 0,
781             isLeft: position.indexOf('left') >= 0,
782             isCenter: position.indexOf('center') >= 0,
783             isRight: position.indexOf('right') >= 0,
784             isTop: position.indexOf('top') >= 0,
785             isMiddle: position.indexOf('middle') >= 0,
786             isBottom: position.indexOf('bottom') >= 0
787         };
788     };
789     Axis.prototype.getXAxisLabelPosition = function getXAxisLabelPosition() {
790         return this.getLabelPosition('x', this.owner.config.axis_rotated ? 'inner-top' : 'inner-right');
791     };
792     Axis.prototype.getYAxisLabelPosition = function getYAxisLabelPosition() {
793         return this.getLabelPosition('y', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top');
794     };
795     Axis.prototype.getY2AxisLabelPosition = function getY2AxisLabelPosition() {
796         return this.getLabelPosition('y2', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top');
797     };
798     Axis.prototype.getLabelPositionById = function getLabelPositionById(id) {
799         return id === 'y2' ? this.getY2AxisLabelPosition() : id === 'y' ? this.getYAxisLabelPosition() : this.getXAxisLabelPosition();
800     };
801     Axis.prototype.textForXAxisLabel = function textForXAxisLabel() {
802         return this.getLabelText('x');
803     };
804     Axis.prototype.textForYAxisLabel = function textForYAxisLabel() {
805         return this.getLabelText('y');
806     };
807     Axis.prototype.textForY2AxisLabel = function textForY2AxisLabel() {
808         return this.getLabelText('y2');
809     };
810     Axis.prototype.xForAxisLabel = function xForAxisLabel(forHorizontal, position) {
811         var $$ = this.owner;
812         if (forHorizontal) {
813             return position.isLeft ? 0 : position.isCenter ? $$.width / 2 : $$.width;
814         } else {
815             return position.isBottom ? -$$.height : position.isMiddle ? -$$.height / 2 : 0;
816         }
817     };
818     Axis.prototype.dxForAxisLabel = function dxForAxisLabel(forHorizontal, position) {
819         if (forHorizontal) {
820             return position.isLeft ? "0.5em" : position.isRight ? "-0.5em" : "0";
821         } else {
822             return position.isTop ? "-0.5em" : position.isBottom ? "0.5em" : "0";
823         }
824     };
825     Axis.prototype.textAnchorForAxisLabel = function textAnchorForAxisLabel(forHorizontal, position) {
826         if (forHorizontal) {
827             return position.isLeft ? 'start' : position.isCenter ? 'middle' : 'end';
828         } else {
829             return position.isBottom ? 'start' : position.isMiddle ? 'middle' : 'end';
830         }
831     };
832     Axis.prototype.xForXAxisLabel = function xForXAxisLabel() {
833         return this.xForAxisLabel(!this.owner.config.axis_rotated, this.getXAxisLabelPosition());
834     };
835     Axis.prototype.xForYAxisLabel = function xForYAxisLabel() {
836         return this.xForAxisLabel(this.owner.config.axis_rotated, this.getYAxisLabelPosition());
837     };
838     Axis.prototype.xForY2AxisLabel = function xForY2AxisLabel() {
839         return this.xForAxisLabel(this.owner.config.axis_rotated, this.getY2AxisLabelPosition());
840     };
841     Axis.prototype.dxForXAxisLabel = function dxForXAxisLabel() {
842         return this.dxForAxisLabel(!this.owner.config.axis_rotated, this.getXAxisLabelPosition());
843     };
844     Axis.prototype.dxForYAxisLabel = function dxForYAxisLabel() {
845         return this.dxForAxisLabel(this.owner.config.axis_rotated, this.getYAxisLabelPosition());
846     };
847     Axis.prototype.dxForY2AxisLabel = function dxForY2AxisLabel() {
848         return this.dxForAxisLabel(this.owner.config.axis_rotated, this.getY2AxisLabelPosition());
849     };
850     Axis.prototype.dyForXAxisLabel = function dyForXAxisLabel() {
851         var $$ = this.owner,
852             config = $$.config,
853             position = this.getXAxisLabelPosition();
854         if (config.axis_rotated) {
855             return position.isInner ? "1.2em" : -25 - ($$.config.axis_x_inner ? 0 : this.getMaxTickWidth('x'));
856         } else {
857             return position.isInner ? "-0.5em" : config.axis_x_height ? config.axis_x_height - 10 : "3em";
858         }
859     };
860     Axis.prototype.dyForYAxisLabel = function dyForYAxisLabel() {
861         var $$ = this.owner,
862             position = this.getYAxisLabelPosition();
863         if ($$.config.axis_rotated) {
864             return position.isInner ? "-0.5em" : "3em";
865         } else {
866             return position.isInner ? "1.2em" : -10 - ($$.config.axis_y_inner ? 0 : this.getMaxTickWidth('y') + 10);
867         }
868     };
869     Axis.prototype.dyForY2AxisLabel = function dyForY2AxisLabel() {
870         var $$ = this.owner,
871             position = this.getY2AxisLabelPosition();
872         if ($$.config.axis_rotated) {
873             return position.isInner ? "1.2em" : "-2.2em";
874         } else {
875             return position.isInner ? "-0.5em" : 15 + ($$.config.axis_y2_inner ? 0 : this.getMaxTickWidth('y2') + 15);
876         }
877     };
878     Axis.prototype.textAnchorForXAxisLabel = function textAnchorForXAxisLabel() {
879         var $$ = this.owner;
880         return this.textAnchorForAxisLabel(!$$.config.axis_rotated, this.getXAxisLabelPosition());
881     };
882     Axis.prototype.textAnchorForYAxisLabel = function textAnchorForYAxisLabel() {
883         var $$ = this.owner;
884         return this.textAnchorForAxisLabel($$.config.axis_rotated, this.getYAxisLabelPosition());
885     };
886     Axis.prototype.textAnchorForY2AxisLabel = function textAnchorForY2AxisLabel() {
887         var $$ = this.owner;
888         return this.textAnchorForAxisLabel($$.config.axis_rotated, this.getY2AxisLabelPosition());
889     };
890     Axis.prototype.getMaxTickWidth = function getMaxTickWidth(id, withoutRecompute) {
891         var $$ = this.owner,
892             config = $$.config,
893             maxWidth = 0,
894             targetsToShow,
895             scale,
896             axis,
897             dummy,
898             svg;
899         if (withoutRecompute && $$.currentMaxTickWidths[id]) {
900             return $$.currentMaxTickWidths[id];
901         }
902         if ($$.svg) {
903             targetsToShow = $$.filterTargetsToShow($$.data.targets);
904             if (id === 'y') {
905                 scale = $$.y.copy().domain($$.getYDomain(targetsToShow, 'y'));
906                 axis = this.getYAxis(scale, $$.yOrient, config.axis_y_tick_format, $$.yAxisTickValues, false, true, true);
907             } else if (id === 'y2') {
908                 scale = $$.y2.copy().domain($$.getYDomain(targetsToShow, 'y2'));
909                 axis = this.getYAxis(scale, $$.y2Orient, config.axis_y2_tick_format, $$.y2AxisTickValues, false, true, true);
910             } else {
911                 scale = $$.x.copy().domain($$.getXDomain(targetsToShow));
912                 axis = this.getXAxis(scale, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, false, true, true);
913                 this.updateXAxisTickValues(targetsToShow, axis);
914             }
915             dummy = $$.d3.select('body').append('div').classed('c3', true);
916             svg = dummy.append("svg").style('visibility', 'hidden').style('position', 'fixed').style('top', 0).style('left', 0), svg.append('g').call(axis).each(function () {
917                 $$.d3.select(this).selectAll('text').each(function () {
918                     var box = this.getBoundingClientRect();
919                     if (maxWidth < box.width) {
920                         maxWidth = box.width;
921                     }
922                 });
923                 dummy.remove();
924             });
925         }
926         $$.currentMaxTickWidths[id] = maxWidth <= 0 ? $$.currentMaxTickWidths[id] : maxWidth;
927         return $$.currentMaxTickWidths[id];
928     };
929
930     Axis.prototype.updateLabels = function updateLabels(withTransition) {
931         var $$ = this.owner;
932         var axisXLabel = $$.main.select('.' + CLASS.axisX + ' .' + CLASS.axisXLabel),
933             axisYLabel = $$.main.select('.' + CLASS.axisY + ' .' + CLASS.axisYLabel),
934             axisY2Label = $$.main.select('.' + CLASS.axisY2 + ' .' + CLASS.axisY2Label);
935         (withTransition ? axisXLabel.transition() : axisXLabel).attr("x", this.xForXAxisLabel.bind(this)).attr("dx", this.dxForXAxisLabel.bind(this)).attr("dy", this.dyForXAxisLabel.bind(this)).text(this.textForXAxisLabel.bind(this));
936         (withTransition ? axisYLabel.transition() : axisYLabel).attr("x", this.xForYAxisLabel.bind(this)).attr("dx", this.dxForYAxisLabel.bind(this)).attr("dy", this.dyForYAxisLabel.bind(this)).text(this.textForYAxisLabel.bind(this));
937         (withTransition ? axisY2Label.transition() : axisY2Label).attr("x", this.xForY2AxisLabel.bind(this)).attr("dx", this.dxForY2AxisLabel.bind(this)).attr("dy", this.dyForY2AxisLabel.bind(this)).text(this.textForY2AxisLabel.bind(this));
938     };
939     Axis.prototype.getPadding = function getPadding(padding, key, defaultValue, domainLength) {
940         var p = typeof padding === 'number' ? padding : padding[key];
941         if (!isValue(p)) {
942             return defaultValue;
943         }
944         if (padding.unit === 'ratio') {
945             return padding[key] * domainLength;
946         }
947         // assume padding is pixels if unit is not specified
948         return this.convertPixelsToAxisPadding(p, domainLength);
949     };
950     Axis.prototype.convertPixelsToAxisPadding = function convertPixelsToAxisPadding(pixels, domainLength) {
951         var $$ = this.owner,
952             length = $$.config.axis_rotated ? $$.width : $$.height;
953         return domainLength * (pixels / length);
954     };
955     Axis.prototype.generateTickValues = function generateTickValues(values, tickCount, forTimeSeries) {
956         var tickValues = values,
957             targetCount,
958             start,
959             end,
960             count,
961             interval,
962             i,
963             tickValue;
964         if (tickCount) {
965             targetCount = isFunction(tickCount) ? tickCount() : tickCount;
966             // compute ticks according to tickCount
967             if (targetCount === 1) {
968                 tickValues = [values[0]];
969             } else if (targetCount === 2) {
970                 tickValues = [values[0], values[values.length - 1]];
971             } else if (targetCount > 2) {
972                 count = targetCount - 2;
973                 start = values[0];
974                 end = values[values.length - 1];
975                 interval = (end - start) / (count + 1);
976                 // re-construct unique values
977                 tickValues = [start];
978                 for (i = 0; i < count; i++) {
979                     tickValue = +start + interval * (i + 1);
980                     tickValues.push(forTimeSeries ? new Date(tickValue) : tickValue);
981                 }
982                 tickValues.push(end);
983             }
984         }
985         if (!forTimeSeries) {
986             tickValues = tickValues.sort(function (a, b) {
987                 return a - b;
988             });
989         }
990         return tickValues;
991     };
992     Axis.prototype.generateTransitions = function generateTransitions(duration) {
993         var $$ = this.owner,
994             axes = $$.axes;
995         return {
996             axisX: duration ? axes.x.transition().duration(duration) : axes.x,
997             axisY: duration ? axes.y.transition().duration(duration) : axes.y,
998             axisY2: duration ? axes.y2.transition().duration(duration) : axes.y2,
999             axisSubX: duration ? axes.subx.transition().duration(duration) : axes.subx
1000         };
1001     };
1002     Axis.prototype.redraw = function redraw(duration, isHidden) {
1003         var $$ = this.owner,
1004             transition = duration ? $$.d3.transition().duration(duration) : null;
1005         $$.axes.x.style("opacity", isHidden ? 0 : 1).call($$.xAxis, transition);
1006         $$.axes.y.style("opacity", isHidden ? 0 : 1).call($$.yAxis, transition);
1007         $$.axes.y2.style("opacity", isHidden ? 0 : 1).call($$.y2Axis, transition);
1008         $$.axes.subx.style("opacity", isHidden ? 0 : 1).call($$.subXAxis, transition);
1009     };
1010
1011     var c3 = {
1012         version: "0.6.7",
1013         chart: {
1014             fn: Chart.prototype,
1015             internal: {
1016                 fn: ChartInternal.prototype,
1017                 axis: {
1018                     fn: Axis.prototype,
1019                     internal: {
1020                         fn: AxisInternal.prototype
1021                     }
1022                 }
1023             }
1024         },
1025         generate: function generate(config) {
1026             return new Chart(config);
1027         }
1028     };
1029
1030     ChartInternal.prototype.beforeInit = function () {
1031         // can do something
1032     };
1033     ChartInternal.prototype.afterInit = function () {
1034         // can do something
1035     };
1036     ChartInternal.prototype.init = function () {
1037         var $$ = this,
1038             config = $$.config;
1039
1040         $$.initParams();
1041
1042         if (config.data_url) {
1043             $$.convertUrlToData(config.data_url, config.data_mimeType, config.data_headers, config.data_keys, $$.initWithData);
1044         } else if (config.data_json) {
1045             $$.initWithData($$.convertJsonToData(config.data_json, config.data_keys));
1046         } else if (config.data_rows) {
1047             $$.initWithData($$.convertRowsToData(config.data_rows));
1048         } else if (config.data_columns) {
1049             $$.initWithData($$.convertColumnsToData(config.data_columns));
1050         } else {
1051             throw Error('url or json or rows or columns is required.');
1052         }
1053     };
1054
1055     ChartInternal.prototype.initParams = function () {
1056         var $$ = this,
1057             d3 = $$.d3,
1058             config = $$.config;
1059
1060         // MEMO: clipId needs to be unique because it conflicts when multiple charts exist
1061         $$.clipId = "c3-" + +new Date() + '-clip';
1062         $$.clipIdForXAxis = $$.clipId + '-xaxis';
1063         $$.clipIdForYAxis = $$.clipId + '-yaxis';
1064         $$.clipIdForGrid = $$.clipId + '-grid';
1065         $$.clipIdForSubchart = $$.clipId + '-subchart';
1066         $$.clipPath = $$.getClipPath($$.clipId);
1067         $$.clipPathForXAxis = $$.getClipPath($$.clipIdForXAxis);
1068         $$.clipPathForYAxis = $$.getClipPath($$.clipIdForYAxis);
1069         $$.clipPathForGrid = $$.getClipPath($$.clipIdForGrid);
1070         $$.clipPathForSubchart = $$.getClipPath($$.clipIdForSubchart);
1071
1072         $$.dragStart = null;
1073         $$.dragging = false;
1074         $$.flowing = false;
1075         $$.cancelClick = false;
1076         $$.mouseover = false;
1077         $$.transiting = false;
1078
1079         $$.color = $$.generateColor();
1080         $$.levelColor = $$.generateLevelColor();
1081
1082         $$.dataTimeParse = (config.data_xLocaltime ? d3.timeParse : d3.utcParse)($$.config.data_xFormat);
1083         $$.axisTimeFormat = config.axis_x_localtime ? d3.timeFormat : d3.utcFormat;
1084         $$.defaultAxisTimeFormat = function (date) {
1085             if (date.getMilliseconds()) {
1086                 return d3.timeFormat(".%L")(date);
1087             }
1088             if (date.getSeconds()) {
1089                 return d3.timeFormat(":%S")(date);
1090             }
1091             if (date.getMinutes()) {
1092                 return d3.timeFormat("%I:%M")(date);
1093             }
1094             if (date.getHours()) {
1095                 return d3.timeFormat("%I %p")(date);
1096             }
1097             if (date.getDay() && date.getDate() !== 1) {
1098                 return d3.timeFormat("%-m/%-d")(date);
1099             }
1100             if (date.getDate() !== 1) {
1101                 return d3.timeFormat("%-m/%-d")(date);
1102             }
1103             if (date.getMonth()) {
1104                 return d3.timeFormat("%-m/%-d")(date);
1105             }
1106             return d3.timeFormat("%Y/%-m/%-d")(date);
1107         };
1108         $$.hiddenTargetIds = [];
1109         $$.hiddenLegendIds = [];
1110         $$.focusedTargetIds = [];
1111         $$.defocusedTargetIds = [];
1112
1113         $$.xOrient = config.axis_rotated ? config.axis_x_inner ? "right" : "left" : config.axis_x_inner ? "top" : "bottom";
1114         $$.yOrient = config.axis_rotated ? config.axis_y_inner ? "top" : "bottom" : config.axis_y_inner ? "right" : "left";
1115         $$.y2Orient = config.axis_rotated ? config.axis_y2_inner ? "bottom" : "top" : config.axis_y2_inner ? "left" : "right";
1116         $$.subXOrient = config.axis_rotated ? "left" : "bottom";
1117
1118         $$.isLegendRight = config.legend_position === 'right';
1119         $$.isLegendInset = config.legend_position === 'inset';
1120         $$.isLegendTop = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'top-right';
1121         $$.isLegendLeft = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'bottom-left';
1122         $$.legendStep = 0;
1123         $$.legendItemWidth = 0;
1124         $$.legendItemHeight = 0;
1125
1126         $$.currentMaxTickWidths = {
1127             x: 0,
1128             y: 0,
1129             y2: 0
1130         };
1131
1132         $$.rotated_padding_left = 30;
1133         $$.rotated_padding_right = config.axis_rotated && !config.axis_x_show ? 0 : 30;
1134         $$.rotated_padding_top = 5;
1135
1136         $$.withoutFadeIn = {};
1137
1138         $$.intervalForObserveInserted = undefined;
1139
1140         $$.axes.subx = d3.selectAll([]); // needs when excluding subchart.js
1141     };
1142
1143     ChartInternal.prototype.initChartElements = function () {
1144         if (this.initBar) {
1145             this.initBar();
1146         }
1147         if (this.initLine) {
1148             this.initLine();
1149         }
1150         if (this.initArc) {
1151             this.initArc();
1152         }
1153         if (this.initGauge) {
1154             this.initGauge();
1155         }
1156         if (this.initText) {
1157             this.initText();
1158         }
1159     };
1160
1161     ChartInternal.prototype.initWithData = function (data) {
1162         var $$ = this,
1163             d3 = $$.d3,
1164             config = $$.config;
1165         var defs,
1166             main,
1167             binding = true;
1168
1169         $$.axis = new Axis($$);
1170
1171         if (!config.bindto) {
1172             $$.selectChart = d3.selectAll([]);
1173         } else if (typeof config.bindto.node === 'function') {
1174             $$.selectChart = config.bindto;
1175         } else {
1176             $$.selectChart = d3.select(config.bindto);
1177         }
1178         if ($$.selectChart.empty()) {
1179             $$.selectChart = d3.select(document.createElement('div')).style('opacity', 0);
1180             $$.observeInserted($$.selectChart);
1181             binding = false;
1182         }
1183         $$.selectChart.html("").classed("c3", true);
1184
1185         // Init data as targets
1186         $$.data.xs = {};
1187         $$.data.targets = $$.convertDataToTargets(data);
1188
1189         if (config.data_filter) {
1190             $$.data.targets = $$.data.targets.filter(config.data_filter);
1191         }
1192
1193         // Set targets to hide if needed
1194         if (config.data_hide) {
1195             $$.addHiddenTargetIds(config.data_hide === true ? $$.mapToIds($$.data.targets) : config.data_hide);
1196         }
1197         if (config.legend_hide) {
1198             $$.addHiddenLegendIds(config.legend_hide === true ? $$.mapToIds($$.data.targets) : config.legend_hide);
1199         }
1200
1201         // Init sizes and scales
1202         $$.updateSizes();
1203         $$.updateScales();
1204
1205         // Set domains for each scale
1206         $$.x.domain(d3.extent($$.getXDomain($$.data.targets)));
1207         $$.y.domain($$.getYDomain($$.data.targets, 'y'));
1208         $$.y2.domain($$.getYDomain($$.data.targets, 'y2'));
1209         $$.subX.domain($$.x.domain());
1210         $$.subY.domain($$.y.domain());
1211         $$.subY2.domain($$.y2.domain());
1212
1213         // Save original x domain for zoom update
1214         $$.orgXDomain = $$.x.domain();
1215
1216         /*-- Basic Elements --*/
1217
1218         // Define svgs
1219         $$.svg = $$.selectChart.append("svg").style("overflow", "hidden").on('mouseenter', function () {
1220             return config.onmouseover.call($$);
1221         }).on('mouseleave', function () {
1222             return config.onmouseout.call($$);
1223         });
1224
1225         if ($$.config.svg_classname) {
1226             $$.svg.attr('class', $$.config.svg_classname);
1227         }
1228
1229         // Define defs
1230         defs = $$.svg.append("defs");
1231         $$.clipChart = $$.appendClip(defs, $$.clipId);
1232         $$.clipXAxis = $$.appendClip(defs, $$.clipIdForXAxis);
1233         $$.clipYAxis = $$.appendClip(defs, $$.clipIdForYAxis);
1234         $$.clipGrid = $$.appendClip(defs, $$.clipIdForGrid);
1235         $$.clipSubchart = $$.appendClip(defs, $$.clipIdForSubchart);
1236         $$.updateSvgSize();
1237
1238         // Define regions
1239         main = $$.main = $$.svg.append("g").attr("transform", $$.getTranslate('main'));
1240
1241         if ($$.initPie) {
1242             $$.initPie();
1243         }
1244         if ($$.initDragZoom) {
1245             $$.initDragZoom();
1246         }
1247         if ($$.initSubchart) {
1248             $$.initSubchart();
1249         }
1250         if ($$.initTooltip) {
1251             $$.initTooltip();
1252         }
1253         if ($$.initLegend) {
1254             $$.initLegend();
1255         }
1256         if ($$.initTitle) {
1257             $$.initTitle();
1258         }
1259         if ($$.initZoom) {
1260             $$.initZoom();
1261         }
1262
1263         // Update selection based on size and scale
1264         // TODO: currently this must be called after initLegend because of update of sizes, but it should be done in initSubchart.
1265         if ($$.initSubchartBrush) {
1266             $$.initSubchartBrush();
1267         }
1268
1269         /*-- Main Region --*/
1270
1271         // text when empty
1272         main.append("text").attr("class", CLASS.text + ' ' + CLASS.empty).attr("text-anchor", "middle") // horizontal centering of text at x position in all browsers.
1273         .attr("dominant-baseline", "middle"); // vertical centering of text at y position in all browsers, except IE.
1274
1275         // Regions
1276         $$.initRegion();
1277
1278         // Grids
1279         $$.initGrid();
1280
1281         // Define g for chart area
1282         main.append('g').attr("clip-path", $$.clipPath).attr('class', CLASS.chart);
1283
1284         // Grid lines
1285         if (config.grid_lines_front) {
1286             $$.initGridLines();
1287         }
1288
1289         // Cover whole with rects for events
1290         $$.initEventRect();
1291
1292         // Define g for chart
1293         $$.initChartElements();
1294
1295         // Add Axis
1296         $$.axis.init();
1297
1298         // Set targets
1299         $$.updateTargets($$.data.targets);
1300
1301         // Set default extent if defined
1302         if (config.axis_x_selection) {
1303             $$.brush.selectionAsValue($$.getDefaultSelection());
1304         }
1305
1306         // Draw with targets
1307         if (binding) {
1308             $$.updateDimension();
1309             $$.config.oninit.call($$);
1310             $$.redraw({
1311                 withTransition: false,
1312                 withTransform: true,
1313                 withUpdateXDomain: true,
1314                 withUpdateOrgXDomain: true,
1315                 withTransitionForAxis: false
1316             });
1317         }
1318
1319         // Bind resize event
1320         $$.bindResize();
1321
1322         // export element of the chart
1323         $$.api.element = $$.selectChart.node();
1324     };
1325
1326     ChartInternal.prototype.smoothLines = function (el, type) {
1327         var $$ = this;
1328         if (type === 'grid') {
1329             el.each(function () {
1330                 var g = $$.d3.select(this),
1331                     x1 = g.attr('x1'),
1332                     x2 = g.attr('x2'),
1333                     y1 = g.attr('y1'),
1334                     y2 = g.attr('y2');
1335                 g.attr({
1336                     'x1': Math.ceil(x1),
1337                     'x2': Math.ceil(x2),
1338                     'y1': Math.ceil(y1),
1339                     'y2': Math.ceil(y2)
1340                 });
1341             });
1342         }
1343     };
1344
1345     ChartInternal.prototype.updateSizes = function () {
1346         var $$ = this,
1347             config = $$.config;
1348         var legendHeight = $$.legend ? $$.getLegendHeight() : 0,
1349             legendWidth = $$.legend ? $$.getLegendWidth() : 0,
1350             legendHeightForBottom = $$.isLegendRight || $$.isLegendInset ? 0 : legendHeight,
1351             hasArc = $$.hasArcType(),
1352             xAxisHeight = config.axis_rotated || hasArc ? 0 : $$.getHorizontalAxisHeight('x'),
1353             subchartHeight = config.subchart_show && !hasArc ? config.subchart_size_height + xAxisHeight : 0;
1354
1355         $$.currentWidth = $$.getCurrentWidth();
1356         $$.currentHeight = $$.getCurrentHeight();
1357
1358         // for main
1359         $$.margin = config.axis_rotated ? {
1360             top: $$.getHorizontalAxisHeight('y2') + $$.getCurrentPaddingTop(),
1361             right: hasArc ? 0 : $$.getCurrentPaddingRight(),
1362             bottom: $$.getHorizontalAxisHeight('y') + legendHeightForBottom + $$.getCurrentPaddingBottom(),
1363             left: subchartHeight + (hasArc ? 0 : $$.getCurrentPaddingLeft())
1364         } : {
1365             top: 4 + $$.getCurrentPaddingTop(), // for top tick text
1366             right: hasArc ? 0 : $$.getCurrentPaddingRight(),
1367             bottom: xAxisHeight + subchartHeight + legendHeightForBottom + $$.getCurrentPaddingBottom(),
1368             left: hasArc ? 0 : $$.getCurrentPaddingLeft()
1369         };
1370
1371         // for subchart
1372         $$.margin2 = config.axis_rotated ? {
1373             top: $$.margin.top,
1374             right: NaN,
1375             bottom: 20 + legendHeightForBottom,
1376             left: $$.rotated_padding_left
1377         } : {
1378             top: $$.currentHeight - subchartHeight - legendHeightForBottom,
1379             right: NaN,
1380             bottom: xAxisHeight + legendHeightForBottom,
1381             left: $$.margin.left
1382         };
1383
1384         // for legend
1385         $$.margin3 = {
1386             top: 0,
1387             right: NaN,
1388             bottom: 0,
1389             left: 0
1390         };
1391         if ($$.updateSizeForLegend) {
1392             $$.updateSizeForLegend(legendHeight, legendWidth);
1393         }
1394
1395         $$.width = $$.currentWidth - $$.margin.left - $$.margin.right;
1396         $$.height = $$.currentHeight - $$.margin.top - $$.margin.bottom;
1397         if ($$.width < 0) {
1398             $$.width = 0;
1399         }
1400         if ($$.height < 0) {
1401             $$.height = 0;
1402         }
1403
1404         $$.width2 = config.axis_rotated ? $$.margin.left - $$.rotated_padding_left - $$.rotated_padding_right : $$.width;
1405         $$.height2 = config.axis_rotated ? $$.height : $$.currentHeight - $$.margin2.top - $$.margin2.bottom;
1406         if ($$.width2 < 0) {
1407             $$.width2 = 0;
1408         }
1409         if ($$.height2 < 0) {
1410             $$.height2 = 0;
1411         }
1412
1413         // for arc
1414         $$.arcWidth = $$.width - ($$.isLegendRight ? legendWidth + 10 : 0);
1415         $$.arcHeight = $$.height - ($$.isLegendRight ? 0 : 10);
1416         if ($$.hasType('gauge') && !config.gauge_fullCircle) {
1417             $$.arcHeight += $$.height - $$.getGaugeLabelHeight();
1418         }
1419         if ($$.updateRadius) {
1420             $$.updateRadius();
1421         }
1422
1423         if ($$.isLegendRight && hasArc) {
1424             $$.margin3.left = $$.arcWidth / 2 + $$.radiusExpanded * 1.1;
1425         }
1426     };
1427
1428     ChartInternal.prototype.updateTargets = function (targets) {
1429         var $$ = this;
1430
1431         /*-- Main --*/
1432
1433         //-- Text --//
1434         $$.updateTargetsForText(targets);
1435
1436         //-- Bar --//
1437         $$.updateTargetsForBar(targets);
1438
1439         //-- Line --//
1440         $$.updateTargetsForLine(targets);
1441
1442         //-- Arc --//
1443         if ($$.hasArcType() && $$.updateTargetsForArc) {
1444             $$.updateTargetsForArc(targets);
1445         }
1446
1447         /*-- Sub --*/
1448
1449         if ($$.updateTargetsForSubchart) {
1450             $$.updateTargetsForSubchart(targets);
1451         }
1452
1453         // Fade-in each chart
1454         $$.showTargets();
1455     };
1456     ChartInternal.prototype.showTargets = function () {
1457         var $$ = this;
1458         $$.svg.selectAll('.' + CLASS.target).filter(function (d) {
1459             return $$.isTargetToShow(d.id);
1460         }).transition().duration($$.config.transition_duration).style("opacity", 1);
1461     };
1462
1463     ChartInternal.prototype.redraw = function (options, transitions) {
1464         var $$ = this,
1465             main = $$.main,
1466             d3 = $$.d3,
1467             config = $$.config;
1468         var areaIndices = $$.getShapeIndices($$.isAreaType),
1469             barIndices = $$.getShapeIndices($$.isBarType),
1470             lineIndices = $$.getShapeIndices($$.isLineType);
1471         var withY, withSubchart, withTransition, withTransitionForExit, withTransitionForAxis, withTransform, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain, withLegend, withEventRect, withDimension, withUpdateXAxis;
1472         var hideAxis = $$.hasArcType();
1473         var drawArea, drawBar, drawLine, xForText, yForText;
1474         var duration, durationForExit, durationForAxis;
1475         var transitionsToWait, waitForDraw, flow, transition;
1476         var targetsToShow = $$.filterTargetsToShow($$.data.targets),
1477             tickValues,
1478             i,
1479             intervalForCulling,
1480             xDomainForZoom;
1481         var xv = $$.xv.bind($$),
1482             cx,
1483             cy;
1484
1485         options = options || {};
1486         withY = getOption(options, "withY", true);
1487         withSubchart = getOption(options, "withSubchart", true);
1488         withTransition = getOption(options, "withTransition", true);
1489         withTransform = getOption(options, "withTransform", false);
1490         withUpdateXDomain = getOption(options, "withUpdateXDomain", false);
1491         withUpdateOrgXDomain = getOption(options, "withUpdateOrgXDomain", false);
1492         withTrimXDomain = getOption(options, "withTrimXDomain", true);
1493         withUpdateXAxis = getOption(options, "withUpdateXAxis", withUpdateXDomain);
1494         withLegend = getOption(options, "withLegend", false);
1495         withEventRect = getOption(options, "withEventRect", true);
1496         withDimension = getOption(options, "withDimension", true);
1497         withTransitionForExit = getOption(options, "withTransitionForExit", withTransition);
1498         withTransitionForAxis = getOption(options, "withTransitionForAxis", withTransition);
1499
1500         duration = withTransition ? config.transition_duration : 0;
1501         durationForExit = withTransitionForExit ? duration : 0;
1502         durationForAxis = withTransitionForAxis ? duration : 0;
1503
1504         transitions = transitions || $$.axis.generateTransitions(durationForAxis);
1505
1506         // update legend and transform each g
1507         if (withLegend && config.legend_show) {
1508             $$.updateLegend($$.mapToIds($$.data.targets), options, transitions);
1509         } else if (withDimension) {
1510             // need to update dimension (e.g. axis.y.tick.values) because y tick values should change
1511             // no need to update axis in it because they will be updated in redraw()
1512             $$.updateDimension(true);
1513         }
1514
1515         // MEMO: needed for grids calculation
1516         if ($$.isCategorized() && targetsToShow.length === 0) {
1517             $$.x.domain([0, $$.axes.x.selectAll('.tick').size()]);
1518         }
1519
1520         if (targetsToShow.length) {
1521             $$.updateXDomain(targetsToShow, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain);
1522             if (!config.axis_x_tick_values) {
1523                 tickValues = $$.axis.updateXAxisTickValues(targetsToShow);
1524             }
1525         } else {
1526             $$.xAxis.tickValues([]);
1527             $$.subXAxis.tickValues([]);
1528         }
1529
1530         if (config.zoom_rescale && !options.flow) {
1531             xDomainForZoom = $$.x.orgDomain();
1532         }
1533
1534         $$.y.domain($$.getYDomain(targetsToShow, 'y', xDomainForZoom));
1535         $$.y2.domain($$.getYDomain(targetsToShow, 'y2', xDomainForZoom));
1536
1537         if (!config.axis_y_tick_values && config.axis_y_tick_count) {
1538             $$.yAxis.tickValues($$.axis.generateTickValues($$.y.domain(), config.axis_y_tick_count));
1539         }
1540         if (!config.axis_y2_tick_values && config.axis_y2_tick_count) {
1541             $$.y2Axis.tickValues($$.axis.generateTickValues($$.y2.domain(), config.axis_y2_tick_count));
1542         }
1543
1544         // axes
1545         $$.axis.redraw(durationForAxis, hideAxis);
1546
1547         // Update axis label
1548         $$.axis.updateLabels(withTransition);
1549
1550         // show/hide if manual culling needed
1551         if ((withUpdateXDomain || withUpdateXAxis) && targetsToShow.length) {
1552             if (config.axis_x_tick_culling && tickValues) {
1553                 for (i = 1; i < tickValues.length; i++) {
1554                     if (tickValues.length / i < config.axis_x_tick_culling_max) {
1555                         intervalForCulling = i;
1556                         break;
1557                     }
1558                 }
1559                 $$.svg.selectAll('.' + CLASS.axisX + ' .tick text').each(function (e) {
1560                     var index = tickValues.indexOf(e);
1561                     if (index >= 0) {
1562                         d3.select(this).style('display', index % intervalForCulling ? 'none' : 'block');
1563                     }
1564                 });
1565             } else {
1566                 $$.svg.selectAll('.' + CLASS.axisX + ' .tick text').style('display', 'block');
1567             }
1568         }
1569
1570         // setup drawer - MEMO: these must be called after axis updated
1571         drawArea = $$.generateDrawArea ? $$.generateDrawArea(areaIndices, false) : undefined;
1572         drawBar = $$.generateDrawBar ? $$.generateDrawBar(barIndices) : undefined;
1573         drawLine = $$.generateDrawLine ? $$.generateDrawLine(lineIndices, false) : undefined;
1574         xForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, true);
1575         yForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, false);
1576
1577         // update circleY based on updated parameters
1578         $$.updateCircleY();
1579         // generate circle x/y functions depending on updated params
1580         cx = ($$.config.axis_rotated ? $$.circleY : $$.circleX).bind($$);
1581         cy = ($$.config.axis_rotated ? $$.circleX : $$.circleY).bind($$);
1582
1583         // Update sub domain
1584         if (withY) {
1585             $$.subY.domain($$.getYDomain(targetsToShow, 'y'));
1586             $$.subY2.domain($$.getYDomain(targetsToShow, 'y2'));
1587         }
1588
1589         // xgrid focus
1590         $$.updateXgridFocus();
1591
1592         // Data empty label positioning and text.
1593         main.select("text." + CLASS.text + '.' + CLASS.empty).attr("x", $$.width / 2).attr("y", $$.height / 2).text(config.data_empty_label_text).transition().style('opacity', targetsToShow.length ? 0 : 1);
1594
1595         // event rect
1596         if (withEventRect) {
1597             $$.redrawEventRect();
1598         }
1599
1600         // grid
1601         $$.updateGrid(duration);
1602
1603         // rect for regions
1604         $$.updateRegion(duration);
1605
1606         // bars
1607         $$.updateBar(durationForExit);
1608
1609         // lines, areas and cricles
1610         $$.updateLine(durationForExit);
1611         $$.updateArea(durationForExit);
1612         $$.updateCircle(cx, cy);
1613
1614         // text
1615         if ($$.hasDataLabel()) {
1616             $$.updateText(xForText, yForText, durationForExit);
1617         }
1618
1619         // title
1620         if ($$.redrawTitle) {
1621             $$.redrawTitle();
1622         }
1623
1624         // arc
1625         if ($$.redrawArc) {
1626             $$.redrawArc(duration, durationForExit, withTransform);
1627         }
1628
1629         // subchart
1630         if ($$.redrawSubchart) {
1631             $$.redrawSubchart(withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices);
1632         }
1633
1634         // circles for select
1635         main.selectAll('.' + CLASS.selectedCircles).filter($$.isBarType.bind($$)).selectAll('circle').remove();
1636
1637         if (options.flow) {
1638             flow = $$.generateFlow({
1639                 targets: targetsToShow,
1640                 flow: options.flow,
1641                 duration: options.flow.duration,
1642                 drawBar: drawBar,
1643                 drawLine: drawLine,
1644                 drawArea: drawArea,
1645                 cx: cx,
1646                 cy: cy,
1647                 xv: xv,
1648                 xForText: xForText,
1649                 yForText: yForText
1650             });
1651         }
1652
1653         if ($$.isTabVisible()) {
1654             // Only use transition if tab visible. See #938.
1655             if (duration) {
1656                 // transition should be derived from one transition
1657                 transition = d3.transition().duration(duration);
1658                 transitionsToWait = [];
1659                 [$$.redrawBar(drawBar, true, transition), $$.redrawLine(drawLine, true, transition), $$.redrawArea(drawArea, true, transition), $$.redrawCircle(cx, cy, true, transition), $$.redrawText(xForText, yForText, options.flow, true, transition), $$.redrawRegion(true, transition), $$.redrawGrid(true, transition)].forEach(function (transitions) {
1660                     transitions.forEach(function (transition) {
1661                         transitionsToWait.push(transition);
1662                     });
1663                 });
1664                 // Wait for end of transitions to call flow and onrendered callback
1665                 waitForDraw = $$.generateWait();
1666                 transitionsToWait.forEach(function (t) {
1667                     waitForDraw.add(t);
1668                 });
1669                 waitForDraw(function () {
1670                     if (flow) {
1671                         flow();
1672                     }
1673                     if (config.onrendered) {
1674                         config.onrendered.call($$);
1675                     }
1676                 });
1677             } else {
1678                 $$.redrawBar(drawBar);
1679                 $$.redrawLine(drawLine);
1680                 $$.redrawArea(drawArea);
1681                 $$.redrawCircle(cx, cy);
1682                 $$.redrawText(xForText, yForText, options.flow);
1683                 $$.redrawRegion();
1684                 $$.redrawGrid();
1685                 if (flow) {
1686                     flow();
1687                 }
1688                 if (config.onrendered) {
1689                     config.onrendered.call($$);
1690                 }
1691             }
1692         }
1693
1694         // update fadein condition
1695         $$.mapToIds($$.data.targets).forEach(function (id) {
1696             $$.withoutFadeIn[id] = true;
1697         });
1698     };
1699
1700     ChartInternal.prototype.updateAndRedraw = function (options) {
1701         var $$ = this,
1702             config = $$.config,
1703             transitions;
1704         options = options || {};
1705         // same with redraw
1706         options.withTransition = getOption(options, "withTransition", true);
1707         options.withTransform = getOption(options, "withTransform", false);
1708         options.withLegend = getOption(options, "withLegend", false);
1709         // NOT same with redraw
1710         options.withUpdateXDomain = getOption(options, "withUpdateXDomain", true);
1711         options.withUpdateOrgXDomain = getOption(options, "withUpdateOrgXDomain", true);
1712         options.withTransitionForExit = false;
1713         options.withTransitionForTransform = getOption(options, "withTransitionForTransform", options.withTransition);
1714         // MEMO: this needs to be called before updateLegend and it means this ALWAYS needs to be called)
1715         $$.updateSizes();
1716         // MEMO: called in updateLegend in redraw if withLegend
1717         if (!(options.withLegend && config.legend_show)) {
1718             transitions = $$.axis.generateTransitions(options.withTransitionForAxis ? config.transition_duration : 0);
1719             // Update scales
1720             $$.updateScales();
1721             $$.updateSvgSize();
1722             // Update g positions
1723             $$.transformAll(options.withTransitionForTransform, transitions);
1724         }
1725         // Draw with new sizes & scales
1726         $$.redraw(options, transitions);
1727     };
1728     ChartInternal.prototype.redrawWithoutRescale = function () {
1729         this.redraw({
1730             withY: false,
1731             withSubchart: false,
1732             withEventRect: false,
1733             withTransitionForAxis: false
1734         });
1735     };
1736
1737     ChartInternal.prototype.isTimeSeries = function () {
1738         return this.config.axis_x_type === 'timeseries';
1739     };
1740     ChartInternal.prototype.isCategorized = function () {
1741         return this.config.axis_x_type.indexOf('categor') >= 0;
1742     };
1743     ChartInternal.prototype.isCustomX = function () {
1744         var $$ = this,
1745             config = $$.config;
1746         return !$$.isTimeSeries() && (config.data_x || notEmpty(config.data_xs));
1747     };
1748
1749     ChartInternal.prototype.isTimeSeriesY = function () {
1750         return this.config.axis_y_type === 'timeseries';
1751     };
1752
1753     ChartInternal.prototype.getTranslate = function (target) {
1754         var $$ = this,
1755             config = $$.config,
1756             x,
1757             y;
1758         if (target === 'main') {
1759             x = asHalfPixel($$.margin.left);
1760             y = asHalfPixel($$.margin.top);
1761         } else if (target === 'context') {
1762             x = asHalfPixel($$.margin2.left);
1763             y = asHalfPixel($$.margin2.top);
1764         } else if (target === 'legend') {
1765             x = $$.margin3.left;
1766             y = $$.margin3.top;
1767         } else if (target === 'x') {
1768             x = 0;
1769             y = config.axis_rotated ? 0 : $$.height;
1770         } else if (target === 'y') {
1771             x = 0;
1772             y = config.axis_rotated ? $$.height : 0;
1773         } else if (target === 'y2') {
1774             x = config.axis_rotated ? 0 : $$.width;
1775             y = config.axis_rotated ? 1 : 0;
1776         } else if (target === 'subx') {
1777             x = 0;
1778             y = config.axis_rotated ? 0 : $$.height2;
1779         } else if (target === 'arc') {
1780             x = $$.arcWidth / 2;
1781             y = $$.arcHeight / 2 - ($$.hasType('gauge') ? 6 : 0); // to prevent wrong display of min and max label
1782         }
1783         return "translate(" + x + "," + y + ")";
1784     };
1785     ChartInternal.prototype.initialOpacity = function (d) {
1786         return d.value !== null && this.withoutFadeIn[d.id] ? 1 : 0;
1787     };
1788     ChartInternal.prototype.initialOpacityForCircle = function (d) {
1789         return d.value !== null && this.withoutFadeIn[d.id] ? this.opacityForCircle(d) : 0;
1790     };
1791     ChartInternal.prototype.opacityForCircle = function (d) {
1792         var isPointShouldBeShown = isFunction(this.config.point_show) ? this.config.point_show(d) : this.config.point_show;
1793         var opacity = isPointShouldBeShown ? 1 : 0;
1794         return isValue(d.value) ? this.isScatterType(d) ? 0.5 : opacity : 0;
1795     };
1796     ChartInternal.prototype.opacityForText = function () {
1797         return this.hasDataLabel() ? 1 : 0;
1798     };
1799     ChartInternal.prototype.xx = function (d) {
1800         return d ? this.x(d.x) : null;
1801     };
1802     ChartInternal.prototype.xv = function (d) {
1803         var $$ = this,
1804             value = d.value;
1805         if ($$.isTimeSeries()) {
1806             value = $$.parseDate(d.value);
1807         } else if ($$.isCategorized() && typeof d.value === 'string') {
1808             value = $$.config.axis_x_categories.indexOf(d.value);
1809         }
1810         return Math.ceil($$.x(value));
1811     };
1812     ChartInternal.prototype.yv = function (d) {
1813         var $$ = this,
1814             yScale = d.axis && d.axis === 'y2' ? $$.y2 : $$.y;
1815         return Math.ceil(yScale(d.value));
1816     };
1817     ChartInternal.prototype.subxx = function (d) {
1818         return d ? this.subX(d.x) : null;
1819     };
1820
1821     ChartInternal.prototype.transformMain = function (withTransition, transitions) {
1822         var $$ = this,
1823             xAxis,
1824             yAxis,
1825             y2Axis;
1826         if (transitions && transitions.axisX) {
1827             xAxis = transitions.axisX;
1828         } else {
1829             xAxis = $$.main.select('.' + CLASS.axisX);
1830             if (withTransition) {
1831                 xAxis = xAxis.transition();
1832             }
1833         }
1834         if (transitions && transitions.axisY) {
1835             yAxis = transitions.axisY;
1836         } else {
1837             yAxis = $$.main.select('.' + CLASS.axisY);
1838             if (withTransition) {
1839                 yAxis = yAxis.transition();
1840             }
1841         }
1842         if (transitions && transitions.axisY2) {
1843             y2Axis = transitions.axisY2;
1844         } else {
1845             y2Axis = $$.main.select('.' + CLASS.axisY2);
1846             if (withTransition) {
1847                 y2Axis = y2Axis.transition();
1848             }
1849         }
1850         (withTransition ? $$.main.transition() : $$.main).attr("transform", $$.getTranslate('main'));
1851         xAxis.attr("transform", $$.getTranslate('x'));
1852         yAxis.attr("transform", $$.getTranslate('y'));
1853         y2Axis.attr("transform", $$.getTranslate('y2'));
1854         $$.main.select('.' + CLASS.chartArcs).attr("transform", $$.getTranslate('arc'));
1855     };
1856     ChartInternal.prototype.transformAll = function (withTransition, transitions) {
1857         var $$ = this;
1858         $$.transformMain(withTransition, transitions);
1859         if ($$.config.subchart_show) {
1860             $$.transformContext(withTransition, transitions);
1861         }
1862         if ($$.legend) {
1863             $$.transformLegend(withTransition);
1864         }
1865     };
1866
1867     ChartInternal.prototype.updateSvgSize = function () {
1868         var $$ = this,
1869             brush = $$.svg.select(".c3-brush .overlay");
1870         $$.svg.attr('width', $$.currentWidth).attr('height', $$.currentHeight);
1871         $$.svg.selectAll(['#' + $$.clipId, '#' + $$.clipIdForGrid]).select('rect').attr('width', $$.width).attr('height', $$.height);
1872         $$.svg.select('#' + $$.clipIdForXAxis).select('rect').attr('x', $$.getXAxisClipX.bind($$)).attr('y', $$.getXAxisClipY.bind($$)).attr('width', $$.getXAxisClipWidth.bind($$)).attr('height', $$.getXAxisClipHeight.bind($$));
1873         $$.svg.select('#' + $$.clipIdForYAxis).select('rect').attr('x', $$.getYAxisClipX.bind($$)).attr('y', $$.getYAxisClipY.bind($$)).attr('width', $$.getYAxisClipWidth.bind($$)).attr('height', $$.getYAxisClipHeight.bind($$));
1874         $$.svg.select('#' + $$.clipIdForSubchart).select('rect').attr('width', $$.width).attr('height', brush.size() ? brush.attr('height') : 0);
1875         // MEMO: parent div's height will be bigger than svg when <!DOCTYPE html>
1876         $$.selectChart.style('max-height', $$.currentHeight + "px");
1877     };
1878
1879     ChartInternal.prototype.updateDimension = function (withoutAxis) {
1880         var $$ = this;
1881         if (!withoutAxis) {
1882             if ($$.config.axis_rotated) {
1883                 $$.axes.x.call($$.xAxis);
1884                 $$.axes.subx.call($$.subXAxis);
1885             } else {
1886                 $$.axes.y.call($$.yAxis);
1887                 $$.axes.y2.call($$.y2Axis);
1888             }
1889         }
1890         $$.updateSizes();
1891         $$.updateScales();
1892         $$.updateSvgSize();
1893         $$.transformAll(false);
1894     };
1895
1896     ChartInternal.prototype.observeInserted = function (selection) {
1897         var $$ = this,
1898             observer;
1899         if (typeof MutationObserver === 'undefined') {
1900             window.console.error("MutationObserver not defined.");
1901             return;
1902         }
1903         observer = new MutationObserver(function (mutations) {
1904             mutations.forEach(function (mutation) {
1905                 if (mutation.type === 'childList' && mutation.previousSibling) {
1906                     observer.disconnect();
1907                     // need to wait for completion of load because size calculation requires the actual sizes determined after that completion
1908                     $$.intervalForObserveInserted = window.setInterval(function () {
1909                         // parentNode will NOT be null when completed
1910                         if (selection.node().parentNode) {
1911                             window.clearInterval($$.intervalForObserveInserted);
1912                             $$.updateDimension();
1913                             if ($$.brush) {
1914                                 $$.brush.update();
1915                             }
1916                             $$.config.oninit.call($$);
1917                             $$.redraw({
1918                                 withTransform: true,
1919                                 withUpdateXDomain: true,
1920                                 withUpdateOrgXDomain: true,
1921                                 withTransition: false,
1922                                 withTransitionForTransform: false,
1923                                 withLegend: true
1924                             });
1925                             selection.transition().style('opacity', 1);
1926                         }
1927                     }, 10);
1928                 }
1929             });
1930         });
1931         observer.observe(selection.node(), {
1932             attributes: true,
1933             childList: true,
1934             characterData: true
1935         });
1936     };
1937
1938     ChartInternal.prototype.bindResize = function () {
1939         var $$ = this,
1940             config = $$.config;
1941
1942         $$.resizeFunction = $$.generateResize(); // need to call .remove
1943
1944         $$.resizeFunction.add(function () {
1945             config.onresize.call($$);
1946         });
1947         if (config.resize_auto) {
1948             $$.resizeFunction.add(function () {
1949                 if ($$.resizeTimeout !== undefined) {
1950                     window.clearTimeout($$.resizeTimeout);
1951                 }
1952                 $$.resizeTimeout = window.setTimeout(function () {
1953                     delete $$.resizeTimeout;
1954                     $$.updateAndRedraw({
1955                         withUpdateXDomain: false,
1956                         withUpdateOrgXDomain: false,
1957                         withTransition: false,
1958                         withTransitionForTransform: false,
1959                         withLegend: true
1960                     });
1961                     if ($$.brush) {
1962                         $$.brush.update();
1963                     }
1964                 }, 100);
1965             });
1966         }
1967         $$.resizeFunction.add(function () {
1968             config.onresized.call($$);
1969         });
1970
1971         $$.resizeIfElementDisplayed = function () {
1972             // if element not displayed skip it
1973             if ($$.api == null || !$$.api.element.offsetParent) {
1974                 return;
1975             }
1976
1977             $$.resizeFunction();
1978         };
1979
1980         if (window.attachEvent) {
1981             window.attachEvent('onresize', $$.resizeIfElementDisplayed);
1982         } else if (window.addEventListener) {
1983             window.addEventListener('resize', $$.resizeIfElementDisplayed, false);
1984         } else {
1985             // fallback to this, if this is a very old browser
1986             var wrapper = window.onresize;
1987             if (!wrapper) {
1988                 // create a wrapper that will call all charts
1989                 wrapper = $$.generateResize();
1990             } else if (!wrapper.add || !wrapper.remove) {
1991                 // there is already a handler registered, make sure we call it too
1992                 wrapper = $$.generateResize();
1993                 wrapper.add(window.onresize);
1994             }
1995             // add this graph to the wrapper, we will be removed if the user calls destroy
1996             wrapper.add($$.resizeFunction);
1997             window.onresize = function () {
1998                 // if element not displayed skip it
1999                 if (!$$.api.element.offsetParent) {
2000                     return;
2001                 }
2002
2003                 wrapper();
2004             };
2005         }
2006     };
2007
2008     ChartInternal.prototype.generateResize = function () {
2009         var resizeFunctions = [];
2010
2011         function callResizeFunctions() {
2012             resizeFunctions.forEach(function (f) {
2013                 f();
2014             });
2015         }
2016         callResizeFunctions.add = function (f) {
2017             resizeFunctions.push(f);
2018         };
2019         callResizeFunctions.remove = function (f) {
2020             for (var i = 0; i < resizeFunctions.length; i++) {
2021                 if (resizeFunctions[i] === f) {
2022                     resizeFunctions.splice(i, 1);
2023                     break;
2024                 }
2025             }
2026         };
2027         return callResizeFunctions;
2028     };
2029
2030     ChartInternal.prototype.endall = function (transition, callback) {
2031         var n = 0;
2032         transition.each(function () {
2033             ++n;
2034         }).on("end", function () {
2035             if (! --n) {
2036                 callback.apply(this, arguments);
2037             }
2038         });
2039     };
2040     ChartInternal.prototype.generateWait = function () {
2041         var transitionsToWait = [],
2042             f = function f(callback) {
2043             var timer = setInterval(function () {
2044                 var done = 0;
2045                 transitionsToWait.forEach(function (t) {
2046                     if (t.empty()) {
2047                         done += 1;
2048                         return;
2049                     }
2050                     try {
2051                         t.transition();
2052                     } catch (e) {
2053                         done += 1;
2054                     }
2055                 });
2056                 if (done === transitionsToWait.length) {
2057                     clearInterval(timer);
2058                     if (callback) {
2059                         callback();
2060                     }
2061                 }
2062             }, 50);
2063         };
2064         f.add = function (transition) {
2065             transitionsToWait.push(transition);
2066         };
2067         return f;
2068     };
2069
2070     ChartInternal.prototype.parseDate = function (date) {
2071         var $$ = this,
2072             parsedDate;
2073         if (date instanceof Date) {
2074             parsedDate = date;
2075         } else if (typeof date === 'string') {
2076             parsedDate = $$.dataTimeParse(date);
2077         } else if ((typeof date === 'undefined' ? 'undefined' : _typeof(date)) === 'object') {
2078             parsedDate = new Date(+date);
2079         } else if (typeof date === 'number' && !isNaN(date)) {
2080             parsedDate = new Date(+date);
2081         }
2082         if (!parsedDate || isNaN(+parsedDate)) {
2083             window.console.error("Failed to parse x '" + date + "' to Date object");
2084         }
2085         return parsedDate;
2086     };
2087
2088     ChartInternal.prototype.isTabVisible = function () {
2089         var hidden;
2090         if (typeof document.hidden !== "undefined") {
2091             // Opera 12.10 and Firefox 18 and later support
2092             hidden = "hidden";
2093         } else if (typeof document.mozHidden !== "undefined") {
2094             hidden = "mozHidden";
2095         } else if (typeof document.msHidden !== "undefined") {
2096             hidden = "msHidden";
2097         } else if (typeof document.webkitHidden !== "undefined") {
2098             hidden = "webkitHidden";
2099         }
2100
2101         return document[hidden] ? false : true;
2102     };
2103
2104     ChartInternal.prototype.getPathBox = getPathBox;
2105     ChartInternal.prototype.CLASS = CLASS;
2106
2107     /* jshint ignore:start */
2108
2109     // PhantomJS doesn't have support for Function.prototype.bind, which has caused confusion. Use
2110     // this polyfill to avoid the confusion.
2111     // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Polyfill
2112
2113     if (!Function.prototype.bind) {
2114         Function.prototype.bind = function (oThis) {
2115             if (typeof this !== 'function') {
2116                 // closest thing possible to the ECMAScript 5
2117                 // internal IsCallable function
2118                 throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
2119             }
2120
2121             var aArgs = Array.prototype.slice.call(arguments, 1),
2122                 fToBind = this,
2123                 fNOP = function fNOP() {},
2124                 fBound = function fBound() {
2125                 return fToBind.apply(this instanceof fNOP ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
2126             };
2127
2128             fNOP.prototype = this.prototype;
2129             fBound.prototype = new fNOP();
2130
2131             return fBound;
2132         };
2133     }
2134
2135     // SVGPathSeg API polyfill
2136     // https://github.com/progers/pathseg
2137     //
2138     // This is a drop-in replacement for the SVGPathSeg and SVGPathSegList APIs that were removed from
2139     // SVG2 (https://lists.w3.org/Archives/Public/www-svg/2015Jun/0044.html), including the latest spec
2140     // changes which were implemented in Firefox 43 and Chrome 46.
2141
2142     (function () {
2143
2144         if (!("SVGPathSeg" in window)) {
2145             // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSeg
2146             window.SVGPathSeg = function (type, typeAsLetter, owningPathSegList) {
2147                 this.pathSegType = type;
2148                 this.pathSegTypeAsLetter = typeAsLetter;
2149                 this._owningPathSegList = owningPathSegList;
2150             };
2151
2152             window.SVGPathSeg.prototype.classname = "SVGPathSeg";
2153
2154             window.SVGPathSeg.PATHSEG_UNKNOWN = 0;
2155             window.SVGPathSeg.PATHSEG_CLOSEPATH = 1;
2156             window.SVGPathSeg.PATHSEG_MOVETO_ABS = 2;
2157             window.SVGPathSeg.PATHSEG_MOVETO_REL = 3;
2158             window.SVGPathSeg.PATHSEG_LINETO_ABS = 4;
2159             window.SVGPathSeg.PATHSEG_LINETO_REL = 5;
2160             window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS = 6;
2161             window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL = 7;
2162             window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS = 8;
2163             window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL = 9;
2164             window.SVGPathSeg.PATHSEG_ARC_ABS = 10;
2165             window.SVGPathSeg.PATHSEG_ARC_REL = 11;
2166             window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS = 12;
2167             window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL = 13;
2168             window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS = 14;
2169             window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL = 15;
2170             window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS = 16;
2171             window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL = 17;
2172             window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS = 18;
2173             window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL = 19;
2174
2175             // Notify owning PathSegList on any changes so they can be synchronized back to the path element.
2176             window.SVGPathSeg.prototype._segmentChanged = function () {
2177                 if (this._owningPathSegList) this._owningPathSegList.segmentChanged(this);
2178             };
2179
2180             window.SVGPathSegClosePath = function (owningPathSegList) {
2181                 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CLOSEPATH, "z", owningPathSegList);
2182             };
2183             window.SVGPathSegClosePath.prototype = Object.create(window.SVGPathSeg.prototype);
2184             window.SVGPathSegClosePath.prototype.toString = function () {
2185                 return "[object SVGPathSegClosePath]";
2186             };
2187             window.SVGPathSegClosePath.prototype._asPathString = function () {
2188                 return this.pathSegTypeAsLetter;
2189             };
2190             window.SVGPathSegClosePath.prototype.clone = function () {
2191                 return new window.SVGPathSegClosePath(undefined);
2192             };
2193
2194             window.SVGPathSegMovetoAbs = function (owningPathSegList, x, y) {
2195                 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_ABS, "M", owningPathSegList);
2196                 this._x = x;
2197                 this._y = y;
2198             };
2199             window.SVGPathSegMovetoAbs.prototype = Object.create(window.SVGPathSeg.prototype);
2200             window.SVGPathSegMovetoAbs.prototype.toString = function () {
2201                 return "[object SVGPathSegMovetoAbs]";
2202             };
2203             window.SVGPathSegMovetoAbs.prototype._asPathString = function () {
2204                 return this.pathSegTypeAsLetter + " " + this._x + " " + this._y;
2205             };
2206             window.SVGPathSegMovetoAbs.prototype.clone = function () {
2207                 return new window.SVGPathSegMovetoAbs(undefined, this._x, this._y);
2208             };
2209             Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, "x", {
2210                 get: function get() {
2211                     return this._x;
2212                 },
2213                 set: function set(x) {
2214                     this._x = x;
2215                     this._segmentChanged();
2216                 },
2217                 enumerable: true
2218             });
2219             Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, "y", {
2220                 get: function get() {
2221                     return this._y;
2222                 },
2223                 set: function set(y) {
2224                     this._y = y;
2225                     this._segmentChanged();
2226                 },
2227                 enumerable: true
2228             });
2229
2230             window.SVGPathSegMovetoRel = function (owningPathSegList, x, y) {
2231                 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_REL, "m", owningPathSegList);
2232                 this._x = x;
2233                 this._y = y;
2234             };
2235             window.SVGPathSegMovetoRel.prototype = Object.create(window.SVGPathSeg.prototype);
2236             window.SVGPathSegMovetoRel.prototype.toString = function () {
2237                 return "[object SVGPathSegMovetoRel]";
2238             };
2239             window.SVGPathSegMovetoRel.prototype._asPathString = function () {
2240                 return this.pathSegTypeAsLetter + " " + this._x + " " + this._y;
2241             };
2242             window.SVGPathSegMovetoRel.prototype.clone = function () {
2243                 return new window.SVGPathSegMovetoRel(undefined, this._x, this._y);
2244             };
2245             Object.defineProperty(window.SVGPathSegMovetoRel.prototype, "x", {
2246                 get: function get() {
2247                     return this._x;
2248                 },
2249                 set: function set(x) {
2250                     this._x = x;
2251                     this._segmentChanged();
2252                 },
2253                 enumerable: true
2254             });
2255             Object.defineProperty(window.SVGPathSegMovetoRel.prototype, "y", {
2256                 get: function get() {
2257                     return this._y;
2258                 },
2259                 set: function set(y) {
2260                     this._y = y;
2261                     this._segmentChanged();
2262                 },
2263                 enumerable: true
2264             });
2265
2266             window.SVGPathSegLinetoAbs = function (owningPathSegList, x, y) {
2267                 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_ABS, "L", owningPathSegList);
2268                 this._x = x;
2269                 this._y = y;
2270             };
2271             window.SVGPathSegLinetoAbs.prototype = Object.create(window.SVGPathSeg.prototype);
2272             window.SVGPathSegLinetoAbs.prototype.toString = function () {
2273                 return "[object SVGPathSegLinetoAbs]";
2274             };
2275             window.SVGPathSegLinetoAbs.prototype._asPathString = function () {
2276                 return this.pathSegTypeAsLetter + " " + this._x + " " + this._y;
2277             };
2278             window.SVGPathSegLinetoAbs.prototype.clone = function () {
2279                 return new window.SVGPathSegLinetoAbs(undefined, this._x, this._y);
2280             };
2281             Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, "x", {
2282                 get: function get() {
2283                     return this._x;
2284                 },
2285                 set: function set(x) {
2286                     this._x = x;
2287                     this._segmentChanged();
2288                 },
2289                 enumerable: true
2290             });
2291             Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, "y", {
2292                 get: function get() {
2293                     return this._y;
2294                 },
2295                 set: function set(y) {
2296                     this._y = y;
2297                     this._segmentChanged();
2298                 },
2299                 enumerable: true
2300             });
2301
2302             window.SVGPathSegLinetoRel = function (owningPathSegList, x, y) {
2303                 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_REL, "l", owningPathSegList);
2304                 this._x = x;
2305                 this._y = y;
2306             };
2307             window.SVGPathSegLinetoRel.prototype = Object.create(window.SVGPathSeg.prototype);
2308             window.SVGPathSegLinetoRel.prototype.toString = function () {
2309                 return "[object SVGPathSegLinetoRel]";
2310             };
2311             window.SVGPathSegLinetoRel.prototype._asPathString = function () {
2312                 return this.pathSegTypeAsLetter + " " + this._x + " " + this._y;
2313             };
2314             window.SVGPathSegLinetoRel.prototype.clone = function () {
2315                 return new window.SVGPathSegLinetoRel(undefined, this._x, this._y);
2316             };
2317             Object.defineProperty(window.SVGPathSegLinetoRel.prototype, "x", {
2318                 get: function get() {
2319                     return this._x;
2320                 },
2321                 set: function set(x) {
2322                     this._x = x;
2323                     this._segmentChanged();
2324                 },
2325                 enumerable: true
2326             });
2327             Object.defineProperty(window.SVGPathSegLinetoRel.prototype, "y", {
2328                 get: function get() {
2329                     return this._y;
2330                 },
2331                 set: function set(y) {
2332                     this._y = y;
2333                     this._segmentChanged();
2334                 },
2335                 enumerable: true
2336             });
2337
2338             window.SVGPathSegCurvetoCubicAbs = function (owningPathSegList, x, y, x1, y1, x2, y2) {
2339                 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS, "C", owningPathSegList);
2340                 this._x = x;
2341                 this._y = y;
2342                 this._x1 = x1;
2343                 this._y1 = y1;
2344                 this._x2 = x2;
2345                 this._y2 = y2;
2346             };
2347             window.SVGPathSegCurvetoCubicAbs.prototype = Object.create(window.SVGPathSeg.prototype);
2348             window.SVGPathSegCurvetoCubicAbs.prototype.toString = function () {
2349                 return "[object SVGPathSegCurvetoCubicAbs]";
2350             };
2351             window.SVGPathSegCurvetoCubicAbs.prototype._asPathString = function () {
2352                 return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y;
2353             };
2354             window.SVGPathSegCurvetoCubicAbs.prototype.clone = function () {
2355                 return new window.SVGPathSegCurvetoCubicAbs(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2);
2356             };
2357             Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "x", {
2358                 get: function get() {
2359                     return this._x;
2360                 },
2361                 set: function set(x) {
2362                     this._x = x;
2363                     this._segmentChanged();
2364                 },
2365                 enumerable: true
2366             });
2367             Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "y", {
2368                 get: function get() {
2369                     return this._y;
2370                 },
2371                 set: function set(y) {
2372                     this._y = y;
2373                     this._segmentChanged();
2374                 },
2375                 enumerable: true
2376             });
2377             Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "x1", {
2378                 get: function get() {
2379                     return this._x1;
2380                 },
2381                 set: function set(x1) {
2382                     this._x1 = x1;
2383                     this._segmentChanged();
2384                 },
2385                 enumerable: true
2386             });
2387             Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "y1", {
2388                 get: function get() {
2389                     return this._y1;
2390                 },
2391                 set: function set(y1) {
2392                     this._y1 = y1;
2393                     this._segmentChanged();
2394                 },
2395                 enumerable: true
2396             });
2397             Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "x2", {
2398                 get: function get() {
2399                     return this._x2;
2400                 },
2401                 set: function set(x2) {
2402                     this._x2 = x2;
2403                     this._segmentChanged();
2404                 },
2405                 enumerable: true
2406             });
2407             Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "y2", {
2408                 get: function get() {
2409                     return this._y2;
2410                 },
2411                 set: function set(y2) {
2412                     this._y2 = y2;
2413                     this._segmentChanged();
2414                 },
2415                 enumerable: true
2416             });
2417
2418             window.SVGPathSegCurvetoCubicRel = function (owningPathSegList, x, y, x1, y1, x2, y2) {
2419                 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL, "c", owningPathSegList);
2420                 this._x = x;
2421                 this._y = y;
2422                 this._x1 = x1;
2423                 this._y1 = y1;
2424                 this._x2 = x2;
2425                 this._y2 = y2;
2426             };
2427             window.SVGPathSegCurvetoCubicRel.prototype = Object.create(window.SVGPathSeg.prototype);
2428             window.SVGPathSegCurvetoCubicRel.prototype.toString = function () {
2429                 return "[object SVGPathSegCurvetoCubicRel]";
2430             };
2431             window.SVGPathSegCurvetoCubicRel.prototype._asPathString = function () {
2432                 return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y;
2433             };
2434             window.SVGPathSegCurvetoCubicRel.prototype.clone = function () {
2435                 return new window.SVGPathSegCurvetoCubicRel(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2);
2436             };
2437             Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "x", {
2438                 get: function get() {
2439                     return this._x;
2440                 },
2441                 set: function set(x) {
2442                     this._x = x;
2443                     this._segmentChanged();
2444                 },
2445                 enumerable: true
2446             });
2447             Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "y", {
2448                 get: function get() {
2449                     return this._y;
2450                 },
2451                 set: function set(y) {
2452                     this._y = y;
2453                     this._segmentChanged();
2454                 },
2455                 enumerable: true
2456             });
2457             Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "x1", {
2458                 get: function get() {
2459                     return this._x1;
2460                 },
2461                 set: function set(x1) {
2462                     this._x1 = x1;
2463                     this._segmentChanged();
2464                 },
2465                 enumerable: true
2466             });
2467             Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "y1", {
2468                 get: function get() {
2469                     return this._y1;
2470                 },
2471                 set: function set(y1) {
2472                     this._y1 = y1;
2473                     this._segmentChanged();
2474                 },
2475                 enumerable: true
2476             });
2477             Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "x2", {
2478                 get: function get() {
2479                     return this._x2;
2480                 },
2481                 set: function set(x2) {
2482                     this._x2 = x2;
2483                     this._segmentChanged();
2484                 },
2485                 enumerable: true
2486             });
2487             Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "y2", {
2488                 get: function get() {
2489                     return this._y2;
2490                 },
2491                 set: function set(y2) {
2492                     this._y2 = y2;
2493                     this._segmentChanged();
2494                 },
2495                 enumerable: true
2496             });
2497
2498             window.SVGPathSegCurvetoQuadraticAbs = function (owningPathSegList, x, y, x1, y1) {
2499                 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS, "Q", owningPathSegList);
2500                 this._x = x;
2501                 this._y = y;
2502                 this._x1 = x1;
2503                 this._y1 = y1;
2504             };
2505             window.SVGPathSegCurvetoQuadraticAbs.prototype = Object.create(window.SVGPathSeg.prototype);
2506             window.SVGPathSegCurvetoQuadraticAbs.prototype.toString = function () {
2507                 return "[object SVGPathSegCurvetoQuadraticAbs]";
2508             };
2509             window.SVGPathSegCurvetoQuadraticAbs.prototype._asPathString = function () {
2510                 return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x + " " + this._y;
2511             };
2512             window.SVGPathSegCurvetoQuadraticAbs.prototype.clone = function () {
2513                 return new window.SVGPathSegCurvetoQuadraticAbs(undefined, this._x, this._y, this._x1, this._y1);
2514             };
2515             Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "x", {
2516                 get: function get() {
2517                     return this._x;
2518                 },
2519                 set: function set(x) {
2520                     this._x = x;
2521                     this._segmentChanged();
2522                 },
2523                 enumerable: true
2524             });
2525             Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "y", {
2526                 get: function get() {
2527                     return this._y;
2528                 },
2529                 set: function set(y) {
2530                     this._y = y;
2531                     this._segmentChanged();
2532                 },
2533                 enumerable: true
2534             });
2535             Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "x1", {
2536                 get: function get() {
2537                     return this._x1;
2538                 },
2539                 set: function set(x1) {
2540                     this._x1 = x1;
2541                     this._segmentChanged();
2542                 },
2543                 enumerable: true
2544             });
2545             Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "y1", {
2546                 get: function get() {
2547                     return this._y1;
2548                 },
2549                 set: function set(y1) {
2550                     this._y1 = y1;
2551                     this._segmentChanged();
2552                 },
2553                 enumerable: true
2554             });
2555
2556             window.SVGPathSegCurvetoQuadraticRel = function (owningPathSegList, x, y, x1, y1) {
2557                 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL, "q", owningPathSegList);
2558                 this._x = x;
2559                 this._y = y;
2560                 this._x1 = x1;
2561                 this._y1 = y1;
2562             };
2563             window.SVGPathSegCurvetoQuadraticRel.prototype = Object.create(window.SVGPathSeg.prototype);
2564             window.SVGPathSegCurvetoQuadraticRel.prototype.toString = function () {
2565                 return "[object SVGPathSegCurvetoQuadraticRel]";
2566             };
2567             window.SVGPathSegCurvetoQuadraticRel.prototype._asPathString = function () {
2568                 return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x + " " + this._y;
2569             };
2570             window.SVGPathSegCurvetoQuadraticRel.prototype.clone = function () {
2571                 return new window.SVGPathSegCurvetoQuadraticRel(undefined, this._x, this._y, this._x1, this._y1);
2572             };
2573             Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "x", {
2574                 get: function get() {
2575                     return this._x;
2576                 },
2577                 set: function set(x) {
2578                     this._x = x;
2579                     this._segmentChanged();
2580                 },
2581                 enumerable: true
2582             });
2583             Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "y", {
2584                 get: function get() {
2585                     return this._y;
2586                 },
2587                 set: function set(y) {
2588                     this._y = y;
2589                     this._segmentChanged();
2590                 },
2591                 enumerable: true
2592             });
2593             Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "x1", {
2594                 get: function get() {
2595                     return this._x1;
2596                 },
2597                 set: function set(x1) {
2598                     this._x1 = x1;
2599                     this._segmentChanged();
2600                 },
2601                 enumerable: true
2602             });
2603             Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "y1", {
2604                 get: function get() {
2605                     return this._y1;
2606                 },
2607                 set: function set(y1) {
2608                     this._y1 = y1;
2609                     this._segmentChanged();
2610                 },
2611                 enumerable: true
2612             });
2613
2614             window.SVGPathSegArcAbs = function (owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) {
2615                 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_ABS, "A", owningPathSegList);
2616                 this._x = x;
2617                 this._y = y;
2618                 this._r1 = r1;
2619                 this._r2 = r2;
2620                 this._angle = angle;
2621                 this._largeArcFlag = largeArcFlag;
2622                 this._sweepFlag = sweepFlag;
2623             };
2624             window.SVGPathSegArcAbs.prototype = Object.create(window.SVGPathSeg.prototype);
2625             window.SVGPathSegArcAbs.prototype.toString = function () {
2626                 return "[object SVGPathSegArcAbs]";
2627             };
2628             window.SVGPathSegArcAbs.prototype._asPathString = function () {
2629                 return this.pathSegTypeAsLetter + " " + this._r1 + " " + this._r2 + " " + this._angle + " " + (this._largeArcFlag ? "1" : "0") + " " + (this._sweepFlag ? "1" : "0") + " " + this._x + " " + this._y;
2630             };
2631             window.SVGPathSegArcAbs.prototype.clone = function () {
2632                 return new window.SVGPathSegArcAbs(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag);
2633             };
2634             Object.defineProperty(window.SVGPathSegArcAbs.prototype, "x", {
2635                 get: function get() {
2636                     return this._x;
2637                 },
2638                 set: function set(x) {
2639                     this._x = x;
2640                     this._segmentChanged();
2641                 },
2642                 enumerable: true
2643             });
2644             Object.defineProperty(window.SVGPathSegArcAbs.prototype, "y", {
2645                 get: function get() {
2646                     return this._y;
2647                 },
2648                 set: function set(y) {
2649                     this._y = y;
2650                     this._segmentChanged();
2651                 },
2652                 enumerable: true
2653             });
2654             Object.defineProperty(window.SVGPathSegArcAbs.prototype, "r1", {
2655                 get: function get() {
2656                     return this._r1;
2657                 },
2658                 set: function set(r1) {
2659                     this._r1 = r1;
2660                     this._segmentChanged();
2661                 },
2662                 enumerable: true
2663             });
2664             Object.defineProperty(window.SVGPathSegArcAbs.prototype, "r2", {
2665                 get: function get() {
2666                     return this._r2;
2667                 },
2668                 set: function set(r2) {
2669                     this._r2 = r2;
2670                     this._segmentChanged();
2671                 },
2672                 enumerable: true
2673             });
2674             Object.defineProperty(window.SVGPathSegArcAbs.prototype, "angle", {
2675                 get: function get() {
2676                     return this._angle;
2677                 },
2678                 set: function set(angle) {
2679                     this._angle = angle;
2680                     this._segmentChanged();
2681                 },
2682                 enumerable: true
2683             });
2684             Object.defineProperty(window.SVGPathSegArcAbs.prototype, "largeArcFlag", {
2685                 get: function get() {
2686                     return this._largeArcFlag;
2687                 },
2688                 set: function set(largeArcFlag) {
2689                     this._largeArcFlag = largeArcFlag;
2690                     this._segmentChanged();
2691                 },
2692                 enumerable: true
2693             });
2694             Object.defineProperty(window.SVGPathSegArcAbs.prototype, "sweepFlag", {
2695                 get: function get() {
2696                     return this._sweepFlag;
2697                 },
2698                 set: function set(sweepFlag) {
2699                     this._sweepFlag = sweepFlag;
2700                     this._segmentChanged();
2701                 },
2702                 enumerable: true
2703             });
2704
2705             window.SVGPathSegArcRel = function (owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) {
2706                 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_REL, "a", owningPathSegList);
2707                 this._x = x;
2708                 this._y = y;
2709                 this._r1 = r1;
2710                 this._r2 = r2;
2711                 this._angle = angle;
2712                 this._largeArcFlag = largeArcFlag;
2713                 this._sweepFlag = sweepFlag;
2714             };
2715             window.SVGPathSegArcRel.prototype = Object.create(window.SVGPathSeg.prototype);
2716             window.SVGPathSegArcRel.prototype.toString = function () {
2717                 return "[object SVGPathSegArcRel]";
2718             };
2719             window.SVGPathSegArcRel.prototype._asPathString = function () {
2720                 return this.pathSegTypeAsLetter + " " + this._r1 + " " + this._r2 + " " + this._angle + " " + (this._largeArcFlag ? "1" : "0") + " " + (this._sweepFlag ? "1" : "0") + " " + this._x + " " + this._y;
2721             };
2722             window.SVGPathSegArcRel.prototype.clone = function () {
2723                 return new window.SVGPathSegArcRel(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag);
2724             };
2725             Object.defineProperty(window.SVGPathSegArcRel.prototype, "x", {
2726                 get: function get() {
2727                     return this._x;
2728                 },
2729                 set: function set(x) {
2730                     this._x = x;
2731                     this._segmentChanged();
2732                 },
2733                 enumerable: true
2734             });
2735             Object.defineProperty(window.SVGPathSegArcRel.prototype, "y", {
2736                 get: function get() {
2737                     return this._y;
2738                 },
2739                 set: function set(y) {
2740                     this._y = y;
2741                     this._segmentChanged();
2742                 },
2743                 enumerable: true
2744             });
2745             Object.defineProperty(window.SVGPathSegArcRel.prototype, "r1", {
2746                 get: function get() {
2747                     return this._r1;
2748                 },
2749                 set: function set(r1) {
2750                     this._r1 = r1;
2751                     this._segmentChanged();
2752                 },
2753                 enumerable: true
2754             });
2755             Object.defineProperty(window.SVGPathSegArcRel.prototype, "r2", {
2756                 get: function get() {
2757                     return this._r2;
2758                 },
2759                 set: function set(r2) {
2760                     this._r2 = r2;
2761                     this._segmentChanged();
2762                 },
2763                 enumerable: true
2764             });
2765             Object.defineProperty(window.SVGPathSegArcRel.prototype, "angle", {
2766                 get: function get() {
2767                     return this._angle;
2768                 },
2769                 set: function set(angle) {
2770                     this._angle = angle;
2771                     this._segmentChanged();
2772                 },
2773                 enumerable: true
2774             });
2775             Object.defineProperty(window.SVGPathSegArcRel.prototype, "largeArcFlag", {
2776                 get: function get() {
2777                     return this._largeArcFlag;
2778                 },
2779                 set: function set(largeArcFlag) {
2780                     this._largeArcFlag = largeArcFlag;
2781                     this._segmentChanged();
2782                 },
2783                 enumerable: true
2784             });
2785             Object.defineProperty(window.SVGPathSegArcRel.prototype, "sweepFlag", {
2786                 get: function get() {
2787                     return this._sweepFlag;
2788                 },
2789                 set: function set(sweepFlag) {
2790                     this._sweepFlag = sweepFlag;
2791                     this._segmentChanged();
2792                 },
2793                 enumerable: true
2794             });
2795
2796             window.SVGPathSegLinetoHorizontalAbs = function (owningPathSegList, x) {
2797                 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS, "H", owningPathSegList);
2798                 this._x = x;
2799             };
2800             window.SVGPathSegLinetoHorizontalAbs.prototype = Object.create(window.SVGPathSeg.prototype);
2801             window.SVGPathSegLinetoHorizontalAbs.prototype.toString = function () {
2802                 return "[object SVGPathSegLinetoHorizontalAbs]";
2803             };
2804             window.SVGPathSegLinetoHorizontalAbs.prototype._asPathString = function () {
2805                 return this.pathSegTypeAsLetter + " " + this._x;
2806             };
2807             window.SVGPathSegLinetoHorizontalAbs.prototype.clone = function () {
2808                 return new window.SVGPathSegLinetoHorizontalAbs(undefined, this._x);
2809             };
2810             Object.defineProperty(window.SVGPathSegLinetoHorizontalAbs.prototype, "x", {
2811                 get: function get() {
2812                     return this._x;
2813                 },
2814                 set: function set(x) {
2815                     this._x = x;
2816                     this._segmentChanged();
2817                 },
2818                 enumerable: true
2819             });
2820
2821             window.SVGPathSegLinetoHorizontalRel = function (owningPathSegList, x) {
2822                 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL, "h", owningPathSegList);
2823                 this._x = x;
2824             };
2825             window.SVGPathSegLinetoHorizontalRel.prototype = Object.create(window.SVGPathSeg.prototype);
2826             window.SVGPathSegLinetoHorizontalRel.prototype.toString = function () {
2827                 return "[object SVGPathSegLinetoHorizontalRel]";
2828             };
2829             window.SVGPathSegLinetoHorizontalRel.prototype._asPathString = function () {
2830                 return this.pathSegTypeAsLetter + " " + this._x;
2831             };
2832             window.SVGPathSegLinetoHorizontalRel.prototype.clone = function () {
2833                 return new window.SVGPathSegLinetoHorizontalRel(undefined, this._x);
2834             };
2835             Object.defineProperty(window.SVGPathSegLinetoHorizontalRel.prototype, "x", {
2836                 get: function get() {
2837                     return this._x;
2838                 },
2839                 set: function set(x) {
2840                     this._x = x;
2841                     this._segmentChanged();
2842                 },
2843                 enumerable: true
2844             });
2845
2846             window.SVGPathSegLinetoVerticalAbs = function (owningPathSegList, y) {
2847                 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS, "V", owningPathSegList);
2848                 this._y = y;
2849             };
2850             window.SVGPathSegLinetoVerticalAbs.prototype = Object.create(window.SVGPathSeg.prototype);
2851             window.SVGPathSegLinetoVerticalAbs.prototype.toString = function () {
2852                 return "[object SVGPathSegLinetoVerticalAbs]";
2853             };
2854             window.SVGPathSegLinetoVerticalAbs.prototype._asPathString = function () {
2855                 return this.pathSegTypeAsLetter + " " + this._y;
2856             };
2857             window.SVGPathSegLinetoVerticalAbs.prototype.clone = function () {
2858                 return new window.SVGPathSegLinetoVerticalAbs(undefined, this._y);
2859             };
2860             Object.defineProperty(window.SVGPathSegLinetoVerticalAbs.prototype, "y", {
2861                 get: function get() {
2862                     return this._y;
2863                 },
2864                 set: function set(y) {
2865                     this._y = y;
2866                     this._segmentChanged();
2867                 },
2868                 enumerable: true
2869             });
2870
2871             window.SVGPathSegLinetoVerticalRel = function (owningPathSegList, y) {
2872                 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL, "v", owningPathSegList);
2873                 this._y = y;
2874             };
2875             window.SVGPathSegLinetoVerticalRel.prototype = Object.create(window.SVGPathSeg.prototype);
2876             window.SVGPathSegLinetoVerticalRel.prototype.toString = function () {
2877                 return "[object SVGPathSegLinetoVerticalRel]";
2878             };
2879             window.SVGPathSegLinetoVerticalRel.prototype._asPathString = function () {
2880                 return this.pathSegTypeAsLetter + " " + this._y;
2881             };
2882             window.SVGPathSegLinetoVerticalRel.prototype.clone = function () {
2883                 return new window.SVGPathSegLinetoVerticalRel(undefined, this._y);
2884             };
2885             Object.defineProperty(window.SVGPathSegLinetoVerticalRel.prototype, "y", {
2886                 get: function get() {
2887                     return this._y;
2888                 },
2889                 set: function set(y) {
2890                     this._y = y;
2891                     this._segmentChanged();
2892                 },
2893                 enumerable: true
2894             });
2895
2896             window.SVGPathSegCurvetoCubicSmoothAbs = function (owningPathSegList, x, y, x2, y2) {
2897                 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS, "S", owningPathSegList);
2898                 this._x = x;
2899                 this._y = y;
2900                 this._x2 = x2;
2901                 this._y2 = y2;
2902             };
2903             window.SVGPathSegCurvetoCubicSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype);
2904             window.SVGPathSegCurvetoCubicSmoothAbs.prototype.toString = function () {
2905                 return "[object SVGPathSegCurvetoCubicSmoothAbs]";
2906             };
2907             window.SVGPathSegCurvetoCubicSmoothAbs.prototype._asPathString = function () {
2908                 return this.pathSegTypeAsLetter + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y;
2909             };
2910             window.SVGPathSegCurvetoCubicSmoothAbs.prototype.clone = function () {
2911                 return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, this._x, this._y, this._x2, this._y2);
2912             };
2913             Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "x", {
2914                 get: function get() {
2915                     return this._x;
2916                 },
2917                 set: function set(x) {
2918                     this._x = x;
2919                     this._segmentChanged();
2920                 },
2921                 enumerable: true
2922             });
2923             Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "y", {
2924                 get: function get() {
2925                     return this._y;
2926                 },
2927                 set: function set(y) {
2928                     this._y = y;
2929                     this._segmentChanged();
2930                 },
2931                 enumerable: true
2932             });
2933             Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "x2", {
2934                 get: function get() {
2935                     return this._x2;
2936                 },
2937                 set: function set(x2) {
2938                     this._x2 = x2;
2939                     this._segmentChanged();
2940                 },
2941                 enumerable: true
2942             });
2943             Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "y2", {
2944                 get: function get() {
2945                     return this._y2;
2946                 },
2947                 set: function set(y2) {
2948                     this._y2 = y2;
2949                     this._segmentChanged();
2950                 },
2951                 enumerable: true
2952             });
2953
2954             window.SVGPathSegCurvetoCubicSmoothRel = function (owningPathSegList, x, y, x2, y2) {
2955                 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL, "s", owningPathSegList);
2956                 this._x = x;
2957                 this._y = y;
2958                 this._x2 = x2;
2959                 this._y2 = y2;
2960             };
2961             window.SVGPathSegCurvetoCubicSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype);
2962             window.SVGPathSegCurvetoCubicSmoothRel.prototype.toString = function () {
2963                 return "[object SVGPathSegCurvetoCubicSmoothRel]";
2964             };
2965             window.SVGPathSegCurvetoCubicSmoothRel.prototype._asPathString = function () {
2966                 return this.pathSegTypeAsLetter + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y;
2967             };
2968             window.SVGPathSegCurvetoCubicSmoothRel.prototype.clone = function () {
2969                 return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, this._x, this._y, this._x2, this._y2);
2970             };
2971             Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "x", {
2972                 get: function get() {
2973                     return this._x;
2974                 },
2975                 set: function set(x) {
2976                     this._x = x;
2977                     this._segmentChanged();
2978                 },
2979                 enumerable: true
2980             });
2981             Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "y", {
2982                 get: function get() {
2983                     return this._y;
2984                 },
2985                 set: function set(y) {
2986                     this._y = y;
2987                     this._segmentChanged();
2988                 },
2989                 enumerable: true
2990             });
2991             Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "x2", {
2992                 get: function get() {
2993                     return this._x2;
2994                 },
2995                 set: function set(x2) {
2996                     this._x2 = x2;
2997                     this._segmentChanged();
2998                 },
2999                 enumerable: true
3000             });
3001             Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "y2", {
3002                 get: function get() {
3003                     return this._y2;
3004                 },
3005                 set: function set(y2) {
3006                     this._y2 = y2;
3007                     this._segmentChanged();
3008                 },
3009                 enumerable: true
3010             });
3011
3012             window.SVGPathSegCurvetoQuadraticSmoothAbs = function (owningPathSegList, x, y) {
3013                 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS, "T", owningPathSegList);
3014                 this._x = x;
3015                 this._y = y;
3016             };
3017             window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype);
3018             window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.toString = function () {
3019                 return "[object SVGPathSegCurvetoQuadraticSmoothAbs]";
3020             };
3021             window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype._asPathString = function () {
3022                 return this.pathSegTypeAsLetter + " " + this._x + " " + this._y;
3023             };
3024             window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.clone = function () {
3025                 return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, this._x, this._y);
3026             };
3027             Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, "x", {
3028                 get: function get() {
3029                     return this._x;
3030                 },
3031                 set: function set(x) {
3032                     this._x = x;
3033                     this._segmentChanged();
3034                 },
3035                 enumerable: true
3036             });
3037             Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, "y", {
3038                 get: function get() {
3039                     return this._y;
3040                 },
3041                 set: function set(y) {
3042                     this._y = y;
3043                     this._segmentChanged();
3044                 },
3045                 enumerable: true
3046             });
3047
3048             window.SVGPathSegCurvetoQuadraticSmoothRel = function (owningPathSegList, x, y) {
3049                 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, "t", owningPathSegList);
3050                 this._x = x;
3051                 this._y = y;
3052             };
3053             window.SVGPathSegCurvetoQuadraticSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype);
3054             window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.toString = function () {
3055                 return "[object SVGPathSegCurvetoQuadraticSmoothRel]";
3056             };
3057             window.SVGPathSegCurvetoQuadraticSmoothRel.prototype._asPathString = function () {
3058                 return this.pathSegTypeAsLetter + " " + this._x + " " + this._y;
3059             };
3060             window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.clone = function () {
3061                 return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, this._x, this._y);
3062             };
3063             Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, "x", {
3064                 get: function get() {
3065                     return this._x;
3066                 },
3067                 set: function set(x) {
3068                     this._x = x;
3069                     this._segmentChanged();
3070                 },
3071                 enumerable: true
3072             });
3073             Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, "y", {
3074                 get: function get() {
3075                     return this._y;
3076                 },
3077                 set: function set(y) {
3078                     this._y = y;
3079                     this._segmentChanged();
3080                 },
3081                 enumerable: true
3082             });
3083
3084             // Add createSVGPathSeg* functions to window.SVGPathElement.
3085             // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-Interfacewindow.SVGPathElement.
3086             window.SVGPathElement.prototype.createSVGPathSegClosePath = function () {
3087                 return new window.SVGPathSegClosePath(undefined);
3088             };
3089             window.SVGPathElement.prototype.createSVGPathSegMovetoAbs = function (x, y) {
3090                 return new window.SVGPathSegMovetoAbs(undefined, x, y);
3091             };
3092             window.SVGPathElement.prototype.createSVGPathSegMovetoRel = function (x, y) {
3093                 return new window.SVGPathSegMovetoRel(undefined, x, y);
3094             };
3095             window.SVGPathElement.prototype.createSVGPathSegLinetoAbs = function (x, y) {
3096                 return new window.SVGPathSegLinetoAbs(undefined, x, y);
3097             };
3098             window.SVGPathElement.prototype.createSVGPathSegLinetoRel = function (x, y) {
3099                 return new window.SVGPathSegLinetoRel(undefined, x, y);
3100             };
3101             window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicAbs = function (x, y, x1, y1, x2, y2) {
3102                 return new window.SVGPathSegCurvetoCubicAbs(undefined, x, y, x1, y1, x2, y2);
3103             };
3104             window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicRel = function (x, y, x1, y1, x2, y2) {
3105                 return new window.SVGPathSegCurvetoCubicRel(undefined, x, y, x1, y1, x2, y2);
3106             };
3107             window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticAbs = function (x, y, x1, y1) {
3108                 return new window.SVGPathSegCurvetoQuadraticAbs(undefined, x, y, x1, y1);
3109             };
3110             window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticRel = function (x, y, x1, y1) {
3111                 return new window.SVGPathSegCurvetoQuadraticRel(undefined, x, y, x1, y1);
3112             };
3113             window.SVGPathElement.prototype.createSVGPathSegArcAbs = function (x, y, r1, r2, angle, largeArcFlag, sweepFlag) {
3114                 return new window.SVGPathSegArcAbs(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
3115             };
3116             window.SVGPathElement.prototype.createSVGPathSegArcRel = function (x, y, r1, r2, angle, largeArcFlag, sweepFlag) {
3117                 return new window.SVGPathSegArcRel(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
3118             };
3119             window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs = function (x) {
3120                 return new window.SVGPathSegLinetoHorizontalAbs(undefined, x);
3121             };
3122             window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel = function (x) {
3123                 return new window.SVGPathSegLinetoHorizontalRel(undefined, x);
3124             };
3125             window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs = function (y) {
3126                 return new window.SVGPathSegLinetoVerticalAbs(undefined, y);
3127             };
3128             window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel = function (y) {
3129                 return new window.SVGPathSegLinetoVerticalRel(undefined, y);
3130             };
3131             window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothAbs = function (x, y, x2, y2) {
3132                 return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, x, y, x2, y2);
3133             };
3134             window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothRel = function (x, y, x2, y2) {
3135                 return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, x, y, x2, y2);
3136             };
3137             window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothAbs = function (x, y) {
3138                 return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, x, y);
3139             };
3140             window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothRel = function (x, y) {
3141                 return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, x, y);
3142             };
3143
3144             if (!("getPathSegAtLength" in window.SVGPathElement.prototype)) {
3145                 // Add getPathSegAtLength to SVGPathElement.
3146                 // Spec: https://www.w3.org/TR/SVG11/single-page.html#paths-__svg__SVGPathElement__getPathSegAtLength
3147                 // This polyfill requires SVGPathElement.getTotalLength to implement the distance-along-a-path algorithm.
3148                 window.SVGPathElement.prototype.getPathSegAtLength = function (distance) {
3149                     if (distance === undefined || !isFinite(distance)) throw "Invalid arguments.";
3150
3151                     var measurementElement = document.createElementNS("http://www.w3.org/2000/svg", "path");
3152                     measurementElement.setAttribute("d", this.getAttribute("d"));
3153                     var lastPathSegment = measurementElement.pathSegList.numberOfItems - 1;
3154
3155                     // If the path is empty, return 0.
3156                     if (lastPathSegment <= 0) return 0;
3157
3158                     do {
3159                         measurementElement.pathSegList.removeItem(lastPathSegment);
3160                         if (distance > measurementElement.getTotalLength()) break;
3161                         lastPathSegment--;
3162                     } while (lastPathSegment > 0);
3163                     return lastPathSegment;
3164                 };
3165             }
3166         }
3167
3168         if (!("SVGPathSegList" in window)) {
3169             // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSegList
3170             window.SVGPathSegList = function (pathElement) {
3171                 this._pathElement = pathElement;
3172                 this._list = this._parsePath(this._pathElement.getAttribute("d"));
3173
3174                 // Use a MutationObserver to catch changes to the path's "d" attribute.
3175                 this._mutationObserverConfig = {
3176                     "attributes": true,
3177                     "attributeFilter": ["d"]
3178                 };
3179                 this._pathElementMutationObserver = new MutationObserver(this._updateListFromPathMutations.bind(this));
3180                 this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig);
3181             };
3182
3183             window.SVGPathSegList.prototype.classname = "SVGPathSegList";
3184
3185             Object.defineProperty(window.SVGPathSegList.prototype, "numberOfItems", {
3186                 get: function get() {
3187                     this._checkPathSynchronizedToList();
3188                     return this._list.length;
3189                 },
3190                 enumerable: true
3191             });
3192
3193             // Add the pathSegList accessors to window.SVGPathElement.
3194             // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGAnimatedPathData
3195             Object.defineProperty(window.SVGPathElement.prototype, "pathSegList", {
3196                 get: function get() {
3197                     if (!this._pathSegList) this._pathSegList = new window.SVGPathSegList(this);
3198                     return this._pathSegList;
3199                 },
3200                 enumerable: true
3201             });
3202             // FIXME: The following are not implemented and simply return window.SVGPathElement.pathSegList.
3203             Object.defineProperty(window.SVGPathElement.prototype, "normalizedPathSegList", {
3204                 get: function get() {
3205                     return this.pathSegList;
3206                 },
3207                 enumerable: true
3208             });
3209             Object.defineProperty(window.SVGPathElement.prototype, "animatedPathSegList", {
3210                 get: function get() {
3211                     return this.pathSegList;
3212                 },
3213                 enumerable: true
3214             });
3215             Object.defineProperty(window.SVGPathElement.prototype, "animatedNormalizedPathSegList", {
3216                 get: function get() {
3217                     return this.pathSegList;
3218                 },
3219                 enumerable: true
3220             });
3221
3222             // Process any pending mutations to the path element and update the list as needed.
3223             // This should be the first call of all public functions and is needed because
3224             // MutationObservers are not synchronous so we can have pending asynchronous mutations.
3225             window.SVGPathSegList.prototype._checkPathSynchronizedToList = function () {
3226                 this._updateListFromPathMutations(this._pathElementMutationObserver.takeRecords());
3227             };
3228
3229             window.SVGPathSegList.prototype._updateListFromPathMutations = function (mutationRecords) {
3230                 if (!this._pathElement) return;
3231                 var hasPathMutations = false;
3232                 mutationRecords.forEach(function (record) {
3233                     if (record.attributeName == "d") hasPathMutations = true;
3234                 });
3235                 if (hasPathMutations) this._list = this._parsePath(this._pathElement.getAttribute("d"));
3236             };
3237
3238             // Serialize the list and update the path's 'd' attribute.
3239             window.SVGPathSegList.prototype._writeListToPath = function () {
3240                 this._pathElementMutationObserver.disconnect();
3241                 this._pathElement.setAttribute("d", window.SVGPathSegList._pathSegArrayAsString(this._list));
3242                 this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig);
3243             };
3244
3245             // When a path segment changes the list needs to be synchronized back to the path element.
3246             window.SVGPathSegList.prototype.segmentChanged = function (pathSeg) {
3247                 this._writeListToPath();
3248             };
3249
3250             window.SVGPathSegList.prototype.clear = function () {
3251                 this._checkPathSynchronizedToList();
3252
3253                 this._list.forEach(function (pathSeg) {
3254                     pathSeg._owningPathSegList = null;
3255                 });
3256                 this._list = [];
3257                 this._writeListToPath();
3258             };
3259
3260             window.SVGPathSegList.prototype.initialize = function (newItem) {
3261                 this._checkPathSynchronizedToList();
3262
3263                 this._list = [newItem];
3264                 newItem._owningPathSegList = this;
3265                 this._writeListToPath();
3266                 return newItem;
3267             };
3268
3269             window.SVGPathSegList.prototype._checkValidIndex = function (index) {
3270                 if (isNaN(index) || index < 0 || index >= this.numberOfItems) throw "INDEX_SIZE_ERR";
3271             };
3272
3273             window.SVGPathSegList.prototype.getItem = function (index) {
3274                 this._checkPathSynchronizedToList();
3275
3276                 this._checkValidIndex(index);
3277                 return this._list[index];
3278             };
3279
3280             window.SVGPathSegList.prototype.insertItemBefore = function (newItem, index) {
3281                 this._checkPathSynchronizedToList();
3282
3283                 // Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list.
3284                 if (index > this.numberOfItems) index = this.numberOfItems;
3285                 if (newItem._owningPathSegList) {
3286                     // SVG2 spec says to make a copy.
3287                     newItem = newItem.clone();
3288                 }
3289                 this._list.splice(index, 0, newItem);
3290                 newItem._owningPathSegList = this;
3291                 this._writeListToPath();
3292                 return newItem;
3293             };
3294
3295             window.SVGPathSegList.prototype.replaceItem = function (newItem, index) {
3296                 this._checkPathSynchronizedToList();
3297
3298                 if (newItem._owningPathSegList) {
3299                     // SVG2 spec says to make a copy.
3300                     newItem = newItem.clone();
3301                 }
3302                 this._checkValidIndex(index);
3303                 this._list[index] = newItem;
3304                 newItem._owningPathSegList = this;
3305                 this._writeListToPath();
3306                 return newItem;
3307             };
3308
3309             window.SVGPathSegList.prototype.removeItem = function (index) {
3310                 this._checkPathSynchronizedToList();
3311
3312                 this._checkValidIndex(index);
3313                 var item = this._list[index];
3314                 this._list.splice(index, 1);
3315                 this._writeListToPath();
3316                 return item;
3317             };
3318
3319             window.SVGPathSegList.prototype.appendItem = function (newItem) {
3320                 this._checkPathSynchronizedToList();
3321
3322                 if (newItem._owningPathSegList) {
3323                     // SVG2 spec says to make a copy.
3324                     newItem = newItem.clone();
3325                 }
3326                 this._list.push(newItem);
3327                 newItem._owningPathSegList = this;
3328                 // TODO: Optimize this to just append to the existing attribute.
3329                 this._writeListToPath();
3330                 return newItem;
3331             };
3332
3333             window.SVGPathSegList._pathSegArrayAsString = function (pathSegArray) {
3334                 var string = "";
3335                 var first = true;
3336                 pathSegArray.forEach(function (pathSeg) {
3337                     if (first) {
3338                         first = false;
3339                         string += pathSeg._asPathString();
3340                     } else {
3341                         string += " " + pathSeg._asPathString();
3342                     }
3343                 });
3344                 return string;
3345             };
3346
3347             // This closely follows SVGPathParser::parsePath from Source/core/svg/SVGPathParser.cpp.
3348             window.SVGPathSegList.prototype._parsePath = function (string) {
3349                 if (!string || string.length == 0) return [];
3350
3351                 var owningPathSegList = this;
3352
3353                 var Builder = function Builder() {
3354                     this.pathSegList = [];
3355                 };
3356
3357                 Builder.prototype.appendSegment = function (pathSeg) {
3358                     this.pathSegList.push(pathSeg);
3359                 };
3360
3361                 var Source = function Source(string) {
3362                     this._string = string;
3363                     this._currentIndex = 0;
3364                     this._endIndex = this._string.length;
3365                     this._previousCommand = window.SVGPathSeg.PATHSEG_UNKNOWN;
3366
3367                     this._skipOptionalSpaces();
3368                 };
3369
3370                 Source.prototype._isCurrentSpace = function () {
3371                     var character = this._string[this._currentIndex];
3372                     return character <= " " && (character == " " || character == "\n" || character == "\t" || character == "\r" || character == "\f");
3373                 };
3374
3375                 Source.prototype._skipOptionalSpaces = function () {
3376                     while (this._currentIndex < this._endIndex && this._isCurrentSpace()) {
3377                         this._currentIndex++;
3378                     }return this._currentIndex < this._endIndex;
3379                 };
3380
3381                 Source.prototype._skipOptionalSpacesOrDelimiter = function () {
3382                     if (this._currentIndex < this._endIndex && !this._isCurrentSpace() && this._string.charAt(this._currentIndex) != ",") return false;
3383                     if (this._skipOptionalSpaces()) {
3384                         if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ",") {
3385                             this._currentIndex++;
3386                             this._skipOptionalSpaces();
3387                         }
3388                     }
3389                     return this._currentIndex < this._endIndex;
3390                 };
3391
3392                 Source.prototype.hasMoreData = function () {
3393                     return this._currentIndex < this._endIndex;
3394                 };
3395
3396                 Source.prototype.peekSegmentType = function () {
3397                     var lookahead = this._string[this._currentIndex];
3398                     return this._pathSegTypeFromChar(lookahead);
3399                 };
3400
3401                 Source.prototype._pathSegTypeFromChar = function (lookahead) {
3402                     switch (lookahead) {
3403                         case "Z":
3404                         case "z":
3405                             return window.SVGPathSeg.PATHSEG_CLOSEPATH;
3406                         case "M":
3407                             return window.SVGPathSeg.PATHSEG_MOVETO_ABS;
3408                         case "m":
3409                             return window.SVGPathSeg.PATHSEG_MOVETO_REL;
3410                         case "L":
3411                             return window.SVGPathSeg.PATHSEG_LINETO_ABS;
3412                         case "l":
3413                             return window.SVGPathSeg.PATHSEG_LINETO_REL;
3414                         case "C":
3415                             return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS;
3416                         case "c":
3417                             return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL;
3418                         case "Q":
3419                             return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS;
3420                         case "q":
3421                             return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL;
3422                         case "A":
3423                             return window.SVGPathSeg.PATHSEG_ARC_ABS;
3424                         case "a":
3425                             return window.SVGPathSeg.PATHSEG_ARC_REL;
3426                         case "H":
3427                             return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS;
3428                         case "h":
3429                             return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL;
3430                         case "V":
3431                             return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS;
3432                         case "v":
3433                             return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL;
3434                         case "S":
3435                             return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS;
3436                         case "s":
3437                             return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL;
3438                         case "T":
3439                             return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS;
3440                         case "t":
3441                             return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL;
3442                         default:
3443                             return window.SVGPathSeg.PATHSEG_UNKNOWN;
3444                     }
3445                 };
3446
3447                 Source.prototype._nextCommandHelper = function (lookahead, previousCommand) {
3448                     // Check for remaining coordinates in the current command.
3449                     if ((lookahead == "+" || lookahead == "-" || lookahead == "." || lookahead >= "0" && lookahead <= "9") && previousCommand != window.SVGPathSeg.PATHSEG_CLOSEPATH) {
3450                         if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_ABS) return window.SVGPathSeg.PATHSEG_LINETO_ABS;
3451                         if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_REL) return window.SVGPathSeg.PATHSEG_LINETO_REL;
3452                         return previousCommand;
3453                     }
3454                     return window.SVGPathSeg.PATHSEG_UNKNOWN;
3455                 };
3456
3457                 Source.prototype.initialCommandIsMoveTo = function () {
3458                     // If the path is empty it is still valid, so return true.
3459                     if (!this.hasMoreData()) return true;
3460                     var command = this.peekSegmentType();
3461                     // Path must start with moveTo.
3462                     return command == window.SVGPathSeg.PATHSEG_MOVETO_ABS || command == window.SVGPathSeg.PATHSEG_MOVETO_REL;
3463                 };
3464
3465                 // Parse a number from an SVG path. This very closely follows genericParseNumber(...) from Source/core/svg/SVGParserUtilities.cpp.
3466                 // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-PathDataBNF
3467                 Source.prototype._parseNumber = function () {
3468                     var exponent = 0;
3469                     var integer = 0;
3470                     var frac = 1;
3471                     var decimal = 0;
3472                     var sign = 1;
3473                     var expsign = 1;
3474
3475                     var startIndex = this._currentIndex;
3476
3477                     this._skipOptionalSpaces();
3478
3479                     // Read the sign.
3480                     if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == "+") this._currentIndex++;else if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == "-") {
3481                         this._currentIndex++;
3482                         sign = -1;
3483                     }
3484
3485                     if (this._currentIndex == this._endIndex || (this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9") && this._string.charAt(this._currentIndex) != ".")
3486                         // The first character of a number must be one of [0-9+-.].
3487                         return undefined;
3488
3489                     // Read the integer part, build right-to-left.
3490                     var startIntPartIndex = this._currentIndex;
3491                     while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9") {
3492                         this._currentIndex++;
3493                     } // Advance to first non-digit.
3494
3495                     if (this._currentIndex != startIntPartIndex) {
3496                         var scanIntPartIndex = this._currentIndex - 1;
3497                         var multiplier = 1;
3498                         while (scanIntPartIndex >= startIntPartIndex) {
3499                             integer += multiplier * (this._string.charAt(scanIntPartIndex--) - "0");
3500                             multiplier *= 10;
3501                         }
3502                     }
3503
3504                     // Read the decimals.
3505                     if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ".") {
3506                         this._currentIndex++;
3507
3508                         // There must be a least one digit following the .
3509                         if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9") return undefined;
3510                         while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9") {
3511                             frac *= 10;
3512                             decimal += (this._string.charAt(this._currentIndex) - "0") / frac;
3513                             this._currentIndex += 1;
3514                         }
3515                     }
3516
3517                     // Read the exponent part.
3518                     if (this._currentIndex != startIndex && this._currentIndex + 1 < this._endIndex && (this._string.charAt(this._currentIndex) == "e" || this._string.charAt(this._currentIndex) == "E") && this._string.charAt(this._currentIndex + 1) != "x" && this._string.charAt(this._currentIndex + 1) != "m") {
3519                         this._currentIndex++;
3520
3521                         // Read the sign of the exponent.
3522                         if (this._string.charAt(this._currentIndex) == "+") {
3523                             this._currentIndex++;
3524                         } else if (this._string.charAt(this._currentIndex) == "-") {
3525                             this._currentIndex++;
3526                             expsign = -1;
3527                         }
3528
3529                         // There must be an exponent.
3530                         if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9") return undefined;
3531
3532                         while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9") {
3533                             exponent *= 10;
3534                             exponent += this._string.charAt(this._currentIndex) - "0";
3535                             this._currentIndex++;
3536                         }
3537                     }
3538
3539                     var number = integer + decimal;
3540                     number *= sign;
3541
3542                     if (exponent) number *= Math.pow(10, expsign * exponent);
3543
3544                     if (startIndex == this._currentIndex) return undefined;
3545
3546                     this._skipOptionalSpacesOrDelimiter();
3547
3548                     return number;
3549                 };
3550
3551                 Source.prototype._parseArcFlag = function () {
3552                     if (this._currentIndex >= this._endIndex) return undefined;
3553                     var flag = false;
3554                     var flagChar = this._string.charAt(this._currentIndex++);
3555                     if (flagChar == "0") flag = false;else if (flagChar == "1") flag = true;else return undefined;
3556
3557                     this._skipOptionalSpacesOrDelimiter();
3558                     return flag;
3559                 };
3560
3561                 Source.prototype.parseSegment = function () {
3562                     var lookahead = this._string[this._currentIndex];
3563                     var command = this._pathSegTypeFromChar(lookahead);
3564                     if (command == window.SVGPathSeg.PATHSEG_UNKNOWN) {
3565                         // Possibly an implicit command. Not allowed if this is the first command.
3566                         if (this._previousCommand == window.SVGPathSeg.PATHSEG_UNKNOWN) return null;
3567                         command = this._nextCommandHelper(lookahead, this._previousCommand);
3568                         if (command == window.SVGPathSeg.PATHSEG_UNKNOWN) return null;
3569                     } else {
3570                         this._currentIndex++;
3571                     }
3572
3573                     this._previousCommand = command;
3574
3575                     switch (command) {
3576                         case window.SVGPathSeg.PATHSEG_MOVETO_REL:
3577                             return new window.SVGPathSegMovetoRel(owningPathSegList, this._parseNumber(), this._parseNumber());
3578                         case window.SVGPathSeg.PATHSEG_MOVETO_ABS:
3579                             return new window.SVGPathSegMovetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber());
3580                         case window.SVGPathSeg.PATHSEG_LINETO_REL:
3581                             return new window.SVGPathSegLinetoRel(owningPathSegList, this._parseNumber(), this._parseNumber());
3582                         case window.SVGPathSeg.PATHSEG_LINETO_ABS:
3583                             return new window.SVGPathSegLinetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber());
3584                         case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:
3585                             return new window.SVGPathSegLinetoHorizontalRel(owningPathSegList, this._parseNumber());
3586                         case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:
3587                             return new window.SVGPathSegLinetoHorizontalAbs(owningPathSegList, this._parseNumber());
3588                         case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL:
3589                             return new window.SVGPathSegLinetoVerticalRel(owningPathSegList, this._parseNumber());
3590                         case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS:
3591                             return new window.SVGPathSegLinetoVerticalAbs(owningPathSegList, this._parseNumber());
3592                         case window.SVGPathSeg.PATHSEG_CLOSEPATH:
3593                             this._skipOptionalSpaces();
3594                             return new window.SVGPathSegClosePath(owningPathSegList);
3595                         case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL:
3596                             var points = {
3597                                 x1: this._parseNumber(),
3598                                 y1: this._parseNumber(),
3599                                 x2: this._parseNumber(),
3600                                 y2: this._parseNumber(),
3601                                 x: this._parseNumber(),
3602                                 y: this._parseNumber()
3603                             };
3604                             return new window.SVGPathSegCurvetoCubicRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2);
3605                         case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:
3606                             var points = {
3607                                 x1: this._parseNumber(),
3608                                 y1: this._parseNumber(),
3609                                 x2: this._parseNumber(),
3610                                 y2: this._parseNumber(),
3611                                 x: this._parseNumber(),
3612                                 y: this._parseNumber()
3613                             };
3614                             return new window.SVGPathSegCurvetoCubicAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2);
3615                         case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
3616                             var points = {
3617                                 x2: this._parseNumber(),
3618                                 y2: this._parseNumber(),
3619                                 x: this._parseNumber(),
3620                                 y: this._parseNumber()
3621                             };
3622                             return new window.SVGPathSegCurvetoCubicSmoothRel(owningPathSegList, points.x, points.y, points.x2, points.y2);
3623                         case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
3624                             var points = {
3625                                 x2: this._parseNumber(),
3626                                 y2: this._parseNumber(),
3627                                 x: this._parseNumber(),
3628                                 y: this._parseNumber()
3629                             };
3630                             return new window.SVGPathSegCurvetoCubicSmoothAbs(owningPathSegList, points.x, points.y, points.x2, points.y2);
3631                         case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:
3632                             var points = {
3633                                 x1: this._parseNumber(),
3634                                 y1: this._parseNumber(),
3635                                 x: this._parseNumber(),
3636                                 y: this._parseNumber()
3637                             };
3638                             return new window.SVGPathSegCurvetoQuadraticRel(owningPathSegList, points.x, points.y, points.x1, points.y1);
3639                         case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:
3640                             var points = {
3641                                 x1: this._parseNumber(),
3642                                 y1: this._parseNumber(),
3643                                 x: this._parseNumber(),
3644                                 y: this._parseNumber()
3645                             };
3646                             return new window.SVGPathSegCurvetoQuadraticAbs(owningPathSegList, points.x, points.y, points.x1, points.y1);
3647                         case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
3648                             return new window.SVGPathSegCurvetoQuadraticSmoothRel(owningPathSegList, this._parseNumber(), this._parseNumber());
3649                         case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
3650                             return new window.SVGPathSegCurvetoQuadraticSmoothAbs(owningPathSegList, this._parseNumber(), this._parseNumber());
3651                         case window.SVGPathSeg.PATHSEG_ARC_REL:
3652                             var points = {
3653                                 x1: this._parseNumber(),
3654                                 y1: this._parseNumber(),
3655                                 arcAngle: this._parseNumber(),
3656                                 arcLarge: this._parseArcFlag(),
3657                                 arcSweep: this._parseArcFlag(),
3658                                 x: this._parseNumber(),
3659                                 y: this._parseNumber()
3660                             };
3661                             return new window.SVGPathSegArcRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep);
3662                         case window.SVGPathSeg.PATHSEG_ARC_ABS:
3663                             var points = {
3664                                 x1: this._parseNumber(),
3665                                 y1: this._parseNumber(),
3666                                 arcAngle: this._parseNumber(),
3667                                 arcLarge: this._parseArcFlag(),
3668                                 arcSweep: this._parseArcFlag(),
3669                                 x: this._parseNumber(),
3670                                 y: this._parseNumber()
3671                             };
3672                             return new window.SVGPathSegArcAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep);
3673                         default:
3674                             throw "Unknown path seg type.";
3675                     }
3676                 };
3677
3678                 var builder = new Builder();
3679                 var source = new Source(string);
3680
3681                 if (!source.initialCommandIsMoveTo()) return [];
3682                 while (source.hasMoreData()) {
3683                     var pathSeg = source.parseSegment();
3684                     if (!pathSeg) return [];
3685                     builder.appendSegment(pathSeg);
3686                 }
3687
3688                 return builder.pathSegList;
3689             };
3690         }
3691     })();
3692
3693     // String.padEnd polyfill for IE11
3694     //
3695     // https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
3696     // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd
3697     if (!String.prototype.padEnd) {
3698         String.prototype.padEnd = function padEnd(targetLength, padString) {
3699             targetLength = targetLength >> 0; //floor if number or convert non-number to 0;
3700             padString = String(typeof padString !== 'undefined' ? padString : ' ');
3701             if (this.length > targetLength) {
3702                 return String(this);
3703             } else {
3704                 targetLength = targetLength - this.length;
3705                 if (targetLength > padString.length) {
3706                     padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed
3707                 }
3708                 return String(this) + padString.slice(0, targetLength);
3709             }
3710         };
3711     }
3712
3713     /* jshint ignore:end */
3714
3715     Chart.prototype.axis = function () {};
3716     Chart.prototype.axis.labels = function (labels) {
3717         var $$ = this.internal;
3718         if (arguments.length) {
3719             Object.keys(labels).forEach(function (axisId) {
3720                 $$.axis.setLabelText(axisId, labels[axisId]);
3721             });
3722             $$.axis.updateLabels();
3723         }
3724         // TODO: return some values?
3725     };
3726     Chart.prototype.axis.max = function (max) {
3727         var $$ = this.internal,
3728             config = $$.config;
3729         if (arguments.length) {
3730             if ((typeof max === 'undefined' ? 'undefined' : _typeof(max)) === 'object') {
3731                 if (isValue(max.x)) {
3732                     config.axis_x_max = max.x;
3733                 }
3734                 if (isValue(max.y)) {
3735                     config.axis_y_max = max.y;
3736                 }
3737                 if (isValue(max.y2)) {
3738                     config.axis_y2_max = max.y2;
3739                 }
3740             } else {
3741                 config.axis_y_max = config.axis_y2_max = max;
3742             }
3743             $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true });
3744         } else {
3745             return {
3746                 x: config.axis_x_max,
3747                 y: config.axis_y_max,
3748                 y2: config.axis_y2_max
3749             };
3750         }
3751     };
3752     Chart.prototype.axis.min = function (min) {
3753         var $$ = this.internal,
3754             config = $$.config;
3755         if (arguments.length) {
3756             if ((typeof min === 'undefined' ? 'undefined' : _typeof(min)) === 'object') {
3757                 if (isValue(min.x)) {
3758                     config.axis_x_min = min.x;
3759                 }
3760                 if (isValue(min.y)) {
3761                     config.axis_y_min = min.y;
3762                 }
3763                 if (isValue(min.y2)) {
3764                     config.axis_y2_min = min.y2;
3765                 }
3766             } else {
3767                 config.axis_y_min = config.axis_y2_min = min;
3768             }
3769             $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true });
3770         } else {
3771             return {
3772                 x: config.axis_x_min,
3773                 y: config.axis_y_min,
3774                 y2: config.axis_y2_min
3775             };
3776         }
3777     };
3778     Chart.prototype.axis.range = function (range) {
3779         if (arguments.length) {
3780             if (isDefined(range.max)) {
3781                 this.axis.max(range.max);
3782             }
3783             if (isDefined(range.min)) {
3784                 this.axis.min(range.min);
3785             }
3786         } else {
3787             return {
3788                 max: this.axis.max(),
3789                 min: this.axis.min()
3790             };
3791         }
3792     };
3793
3794     Chart.prototype.category = function (i, category) {
3795         var $$ = this.internal,
3796             config = $$.config;
3797         if (arguments.length > 1) {
3798             config.axis_x_categories[i] = category;
3799             $$.redraw();
3800         }
3801         return config.axis_x_categories[i];
3802     };
3803     Chart.prototype.categories = function (categories) {
3804         var $$ = this.internal,
3805             config = $$.config;
3806         if (!arguments.length) {
3807             return config.axis_x_categories;
3808         }
3809         config.axis_x_categories = categories;
3810         $$.redraw();
3811         return config.axis_x_categories;
3812     };
3813
3814     Chart.prototype.resize = function (size) {
3815         var $$ = this.internal,
3816             config = $$.config;
3817         config.size_width = size ? size.width : null;
3818         config.size_height = size ? size.height : null;
3819         this.flush();
3820     };
3821
3822     Chart.prototype.flush = function () {
3823         var $$ = this.internal;
3824         $$.updateAndRedraw({ withLegend: true, withTransition: false, withTransitionForTransform: false });
3825     };
3826
3827     Chart.prototype.destroy = function () {
3828         var $$ = this.internal;
3829
3830         window.clearInterval($$.intervalForObserveInserted);
3831
3832         if ($$.resizeTimeout !== undefined) {
3833             window.clearTimeout($$.resizeTimeout);
3834         }
3835
3836         if (window.detachEvent) {
3837             window.detachEvent('onresize', $$.resizeIfElementDisplayed);
3838         } else if (window.removeEventListener) {
3839             window.removeEventListener('resize', $$.resizeIfElementDisplayed);
3840         } else {
3841             var wrapper = window.onresize;
3842             // check if no one else removed our wrapper and remove our resizeFunction from it
3843             if (wrapper && wrapper.add && wrapper.remove) {
3844                 wrapper.remove($$.resizeFunction);
3845             }
3846         }
3847
3848         // remove the inner resize functions
3849         $$.resizeFunction.remove();
3850
3851         $$.selectChart.classed('c3', false).html("");
3852
3853         // MEMO: this is needed because the reference of some elements will not be released, then memory leak will happen.
3854         Object.keys($$).forEach(function (key) {
3855             $$[key] = null;
3856         });
3857
3858         return null;
3859     };
3860
3861     // TODO: fix
3862     Chart.prototype.color = function (id) {
3863         var $$ = this.internal;
3864         return $$.color(id); // more patterns
3865     };
3866
3867     Chart.prototype.data = function (targetIds) {
3868         var targets = this.internal.data.targets;
3869         return typeof targetIds === 'undefined' ? targets : targets.filter(function (t) {
3870             return [].concat(targetIds).indexOf(t.id) >= 0;
3871         });
3872     };
3873     Chart.prototype.data.shown = function (targetIds) {
3874         return this.internal.filterTargetsToShow(this.data(targetIds));
3875     };
3876     Chart.prototype.data.values = function (targetId) {
3877         var targets,
3878             values = null;
3879         if (targetId) {
3880             targets = this.data(targetId);
3881             values = targets[0] ? targets[0].values.map(function (d) {
3882                 return d.value;
3883             }) : null;
3884         }
3885         return values;
3886     };
3887     Chart.prototype.data.names = function (names) {
3888         this.internal.clearLegendItemTextBoxCache();
3889         return this.internal.updateDataAttributes('names', names);
3890     };
3891     Chart.prototype.data.colors = function (colors) {
3892         return this.internal.updateDataAttributes('colors', colors);
3893     };
3894     Chart.prototype.data.axes = function (axes) {
3895         return this.internal.updateDataAttributes('axes', axes);
3896     };
3897
3898     Chart.prototype.flow = function (args) {
3899         var $$ = this.internal,
3900             targets,
3901             data,
3902             notfoundIds = [],
3903             orgDataCount = $$.getMaxDataCount(),
3904             dataCount,
3905             domain,
3906             baseTarget,
3907             baseValue,
3908             length = 0,
3909             tail = 0,
3910             diff,
3911             to;
3912
3913         if (args.json) {
3914             data = $$.convertJsonToData(args.json, args.keys);
3915         } else if (args.rows) {
3916             data = $$.convertRowsToData(args.rows);
3917         } else if (args.columns) {
3918             data = $$.convertColumnsToData(args.columns);
3919         } else {
3920             return;
3921         }
3922         targets = $$.convertDataToTargets(data, true);
3923
3924         // Update/Add data
3925         $$.data.targets.forEach(function (t) {
3926             var found = false,
3927                 i,
3928                 j;
3929             for (i = 0; i < targets.length; i++) {
3930                 if (t.id === targets[i].id) {
3931                     found = true;
3932
3933                     if (t.values[t.values.length - 1]) {
3934                         tail = t.values[t.values.length - 1].index + 1;
3935                     }
3936                     length = targets[i].values.length;
3937
3938                     for (j = 0; j < length; j++) {
3939                         targets[i].values[j].index = tail + j;
3940                         if (!$$.isTimeSeries()) {
3941                             targets[i].values[j].x = tail + j;
3942                         }
3943                     }
3944                     t.values = t.values.concat(targets[i].values);
3945
3946                     targets.splice(i, 1);
3947                     break;
3948                 }
3949             }
3950             if (!found) {
3951                 notfoundIds.push(t.id);
3952             }
3953         });
3954
3955         // Append null for not found targets
3956         $$.data.targets.forEach(function (t) {
3957             var i, j;
3958             for (i = 0; i < notfoundIds.length; i++) {
3959                 if (t.id === notfoundIds[i]) {
3960                     tail = t.values[t.values.length - 1].index + 1;
3961                     for (j = 0; j < length; j++) {
3962                         t.values.push({
3963                             id: t.id,
3964                             index: tail + j,
3965                             x: $$.isTimeSeries() ? $$.getOtherTargetX(tail + j) : tail + j,
3966                             value: null
3967                         });
3968                     }
3969                 }
3970             }
3971         });
3972
3973         // Generate null values for new target
3974         if ($$.data.targets.length) {
3975             targets.forEach(function (t) {
3976                 var i,
3977                     missing = [];
3978                 for (i = $$.data.targets[0].values[0].index; i < tail; i++) {
3979                     missing.push({
3980                         id: t.id,
3981                         index: i,
3982                         x: $$.isTimeSeries() ? $$.getOtherTargetX(i) : i,
3983                         value: null
3984                     });
3985                 }
3986                 t.values.forEach(function (v) {
3987                     v.index += tail;
3988                     if (!$$.isTimeSeries()) {
3989                         v.x += tail;
3990                     }
3991                 });
3992                 t.values = missing.concat(t.values);
3993             });
3994         }
3995         $$.data.targets = $$.data.targets.concat(targets); // add remained
3996
3997         // check data count because behavior needs to change when it's only one
3998         dataCount = $$.getMaxDataCount();
3999         baseTarget = $$.data.targets[0];
4000         baseValue = baseTarget.values[0];
4001
4002         // Update length to flow if needed
4003         if (isDefined(args.to)) {
4004             length = 0;
4005             to = $$.isTimeSeries() ? $$.parseDate(args.to) : args.to;
4006             baseTarget.values.forEach(function (v) {
4007                 if (v.x < to) {
4008                     length++;
4009                 }
4010             });
4011         } else if (isDefined(args.length)) {
4012             length = args.length;
4013         }
4014
4015         // If only one data, update the domain to flow from left edge of the chart
4016         if (!orgDataCount) {
4017             if ($$.isTimeSeries()) {
4018                 if (baseTarget.values.length > 1) {
4019                     diff = baseTarget.values[baseTarget.values.length - 1].x - baseValue.x;
4020                 } else {
4021                     diff = baseValue.x - $$.getXDomain($$.data.targets)[0];
4022                 }
4023             } else {
4024                 diff = 1;
4025             }
4026             domain = [baseValue.x - diff, baseValue.x];
4027             $$.updateXDomain(null, true, true, false, domain);
4028         } else if (orgDataCount === 1) {
4029             if ($$.isTimeSeries()) {
4030                 diff = (baseTarget.values[baseTarget.values.length - 1].x - baseValue.x) / 2;
4031                 domain = [new Date(+baseValue.x - diff), new Date(+baseValue.x + diff)];
4032                 $$.updateXDomain(null, true, true, false, domain);
4033             }
4034         }
4035
4036         // Set targets
4037         $$.updateTargets($$.data.targets);
4038
4039         // Redraw with new targets
4040         $$.redraw({
4041             flow: {
4042                 index: baseValue.index,
4043                 length: length,
4044                 duration: isValue(args.duration) ? args.duration : $$.config.transition_duration,
4045                 done: args.done,
4046                 orgDataCount: orgDataCount
4047             },
4048             withLegend: true,
4049             withTransition: orgDataCount > 1,
4050             withTrimXDomain: false,
4051             withUpdateXAxis: true
4052         });
4053     };
4054
4055     ChartInternal.prototype.generateFlow = function (args) {
4056         var $$ = this,
4057             config = $$.config,
4058             d3 = $$.d3;
4059
4060         return function () {
4061             var targets = args.targets,
4062                 flow = args.flow,
4063                 drawBar = args.drawBar,
4064                 drawLine = args.drawLine,
4065                 drawArea = args.drawArea,
4066                 cx = args.cx,
4067                 cy = args.cy,
4068                 xv = args.xv,
4069                 xForText = args.xForText,
4070                 yForText = args.yForText,
4071                 duration = args.duration;
4072
4073             var translateX,
4074                 scaleX = 1,
4075                 transform,
4076                 flowIndex = flow.index,
4077                 flowLength = flow.length,
4078                 flowStart = $$.getValueOnIndex($$.data.targets[0].values, flowIndex),
4079                 flowEnd = $$.getValueOnIndex($$.data.targets[0].values, flowIndex + flowLength),
4080                 orgDomain = $$.x.domain(),
4081                 domain,
4082                 durationForFlow = flow.duration || duration,
4083                 done = flow.done || function () {},
4084                 wait = $$.generateWait();
4085
4086             var xgrid, xgridLines, mainRegion, mainText, mainBar, mainLine, mainArea, mainCircle;
4087
4088             // set flag
4089             $$.flowing = true;
4090
4091             // remove head data after rendered
4092             $$.data.targets.forEach(function (d) {
4093                 d.values.splice(0, flowLength);
4094             });
4095
4096             // update x domain to generate axis elements for flow
4097             domain = $$.updateXDomain(targets, true, true);
4098             // update elements related to x scale
4099             if ($$.updateXGrid) {
4100                 $$.updateXGrid(true);
4101             }
4102
4103             xgrid = $$.xgrid || d3.selectAll([]); // xgrid needs to be obtained after updateXGrid
4104             xgridLines = $$.xgridLines || d3.selectAll([]);
4105             mainRegion = $$.mainRegion || d3.selectAll([]);
4106             mainText = $$.mainText || d3.selectAll([]);
4107             mainBar = $$.mainBar || d3.selectAll([]);
4108             mainLine = $$.mainLine || d3.selectAll([]);
4109             mainArea = $$.mainArea || d3.selectAll([]);
4110             mainCircle = $$.mainCircle || d3.selectAll([]);
4111
4112             // generate transform to flow
4113             if (!flow.orgDataCount) {
4114                 // if empty
4115                 if ($$.data.targets[0].values.length !== 1) {
4116                     translateX = $$.x(orgDomain[0]) - $$.x(domain[0]);
4117                 } else {
4118                     if ($$.isTimeSeries()) {
4119                         flowStart = $$.getValueOnIndex($$.data.targets[0].values, 0);
4120                         flowEnd = $$.getValueOnIndex($$.data.targets[0].values, $$.data.targets[0].values.length - 1);
4121                         translateX = $$.x(flowStart.x) - $$.x(flowEnd.x);
4122                     } else {
4123                         translateX = diffDomain(domain) / 2;
4124                     }
4125                 }
4126             } else if (flow.orgDataCount === 1 || (flowStart && flowStart.x) === (flowEnd && flowEnd.x)) {
4127                 translateX = $$.x(orgDomain[0]) - $$.x(domain[0]);
4128             } else {
4129                 if ($$.isTimeSeries()) {
4130                     translateX = $$.x(orgDomain[0]) - $$.x(domain[0]);
4131                 } else {
4132                     translateX = $$.x(flowStart.x) - $$.x(flowEnd.x);
4133                 }
4134             }
4135             scaleX = diffDomain(orgDomain) / diffDomain(domain);
4136             transform = 'translate(' + translateX + ',0) scale(' + scaleX + ',1)';
4137
4138             $$.hideXGridFocus();
4139
4140             var flowTransition = d3.transition().ease(d3.easeLinear).duration(durationForFlow);
4141             wait.add($$.xAxis($$.axes.x, flowTransition));
4142             wait.add(mainBar.transition(flowTransition).attr('transform', transform));
4143             wait.add(mainLine.transition(flowTransition).attr('transform', transform));
4144             wait.add(mainArea.transition(flowTransition).attr('transform', transform));
4145             wait.add(mainCircle.transition(flowTransition).attr('transform', transform));
4146             wait.add(mainText.transition(flowTransition).attr('transform', transform));
4147             wait.add(mainRegion.filter($$.isRegionOnX).transition(flowTransition).attr('transform', transform));
4148             wait.add(xgrid.transition(flowTransition).attr('transform', transform));
4149             wait.add(xgridLines.transition(flowTransition).attr('transform', transform));
4150             wait(function () {
4151                 var i,
4152                     shapes = [],
4153                     texts = [];
4154
4155                 // remove flowed elements
4156                 if (flowLength) {
4157                     for (i = 0; i < flowLength; i++) {
4158                         shapes.push('.' + CLASS.shape + '-' + (flowIndex + i));
4159                         texts.push('.' + CLASS.text + '-' + (flowIndex + i));
4160                     }
4161                     $$.svg.selectAll('.' + CLASS.shapes).selectAll(shapes).remove();
4162                     $$.svg.selectAll('.' + CLASS.texts).selectAll(texts).remove();
4163                     $$.svg.select('.' + CLASS.xgrid).remove();
4164                 }
4165
4166                 // draw again for removing flowed elements and reverting attr
4167                 xgrid.attr('transform', null).attr('x1', $$.xgridAttr.x1).attr('x2', $$.xgridAttr.x2).attr('y1', $$.xgridAttr.y1).attr('y2', $$.xgridAttr.y2).style("opacity", $$.xgridAttr.opacity);
4168                 xgridLines.attr('transform', null);
4169                 xgridLines.select('line').attr("x1", config.axis_rotated ? 0 : xv).attr("x2", config.axis_rotated ? $$.width : xv);
4170                 xgridLines.select('text').attr("x", config.axis_rotated ? $$.width : 0).attr("y", xv);
4171                 mainBar.attr('transform', null).attr("d", drawBar);
4172                 mainLine.attr('transform', null).attr("d", drawLine);
4173                 mainArea.attr('transform', null).attr("d", drawArea);
4174                 mainCircle.attr('transform', null).attr("cx", cx).attr("cy", cy);
4175                 mainText.attr('transform', null).attr('x', xForText).attr('y', yForText).style('fill-opacity', $$.opacityForText.bind($$));
4176                 mainRegion.attr('transform', null);
4177                 mainRegion.filter($$.isRegionOnX).attr("x", $$.regionX.bind($$)).attr("width", $$.regionWidth.bind($$));
4178
4179                 // callback for end of flow
4180                 done();
4181
4182                 $$.flowing = false;
4183             });
4184         };
4185     };
4186
4187     Chart.prototype.focus = function (targetIds) {
4188         var $$ = this.internal,
4189             candidates;
4190
4191         targetIds = $$.mapToTargetIds(targetIds);
4192         candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$))), this.revert();
4193         this.defocus();
4194         candidates.classed(CLASS.focused, true).classed(CLASS.defocused, false);
4195         if ($$.hasArcType()) {
4196             $$.expandArc(targetIds);
4197         }
4198         $$.toggleFocusLegend(targetIds, true);
4199
4200         $$.focusedTargetIds = targetIds;
4201         $$.defocusedTargetIds = $$.defocusedTargetIds.filter(function (id) {
4202             return targetIds.indexOf(id) < 0;
4203         });
4204     };
4205
4206     Chart.prototype.defocus = function (targetIds) {
4207         var $$ = this.internal,
4208             candidates;
4209
4210         targetIds = $$.mapToTargetIds(targetIds);
4211         candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$))), candidates.classed(CLASS.focused, false).classed(CLASS.defocused, true);
4212         if ($$.hasArcType()) {
4213             $$.unexpandArc(targetIds);
4214         }
4215         $$.toggleFocusLegend(targetIds, false);
4216
4217         $$.focusedTargetIds = $$.focusedTargetIds.filter(function (id) {
4218             return targetIds.indexOf(id) < 0;
4219         });
4220         $$.defocusedTargetIds = targetIds;
4221     };
4222
4223     Chart.prototype.revert = function (targetIds) {
4224         var $$ = this.internal,
4225             candidates;
4226
4227         targetIds = $$.mapToTargetIds(targetIds);
4228         candidates = $$.svg.selectAll($$.selectorTargets(targetIds)); // should be for all targets
4229
4230         candidates.classed(CLASS.focused, false).classed(CLASS.defocused, false);
4231         if ($$.hasArcType()) {
4232             $$.unexpandArc(targetIds);
4233         }
4234         if ($$.config.legend_show) {
4235             $$.showLegend(targetIds.filter($$.isLegendToShow.bind($$)));
4236             $$.legend.selectAll($$.selectorLegends(targetIds)).filter(function () {
4237                 return $$.d3.select(this).classed(CLASS.legendItemFocused);
4238             }).classed(CLASS.legendItemFocused, false);
4239         }
4240
4241         $$.focusedTargetIds = [];
4242         $$.defocusedTargetIds = [];
4243     };
4244
4245     Chart.prototype.xgrids = function (grids) {
4246         var $$ = this.internal,
4247             config = $$.config;
4248         if (!grids) {
4249             return config.grid_x_lines;
4250         }
4251         config.grid_x_lines = grids;
4252         $$.redrawWithoutRescale();
4253         return config.grid_x_lines;
4254     };
4255     Chart.prototype.xgrids.add = function (grids) {
4256         var $$ = this.internal;
4257         return this.xgrids($$.config.grid_x_lines.concat(grids ? grids : []));
4258     };
4259     Chart.prototype.xgrids.remove = function (params) {
4260         // TODO: multiple
4261         var $$ = this.internal;
4262         $$.removeGridLines(params, true);
4263     };
4264
4265     Chart.prototype.ygrids = function (grids) {
4266         var $$ = this.internal,
4267             config = $$.config;
4268         if (!grids) {
4269             return config.grid_y_lines;
4270         }
4271         config.grid_y_lines = grids;
4272         $$.redrawWithoutRescale();
4273         return config.grid_y_lines;
4274     };
4275     Chart.prototype.ygrids.add = function (grids) {
4276         var $$ = this.internal;
4277         return this.ygrids($$.config.grid_y_lines.concat(grids ? grids : []));
4278     };
4279     Chart.prototype.ygrids.remove = function (params) {
4280         // TODO: multiple
4281         var $$ = this.internal;
4282         $$.removeGridLines(params, false);
4283     };
4284
4285     Chart.prototype.groups = function (groups) {
4286         var $$ = this.internal,
4287             config = $$.config;
4288         if (isUndefined(groups)) {
4289             return config.data_groups;
4290         }
4291         config.data_groups = groups;
4292         $$.redraw();
4293         return config.data_groups;
4294     };
4295
4296     Chart.prototype.legend = function () {};
4297     Chart.prototype.legend.show = function (targetIds) {
4298         var $$ = this.internal;
4299         $$.showLegend($$.mapToTargetIds(targetIds));
4300         $$.updateAndRedraw({ withLegend: true });
4301     };
4302     Chart.prototype.legend.hide = function (targetIds) {
4303         var $$ = this.internal;
4304         $$.hideLegend($$.mapToTargetIds(targetIds));
4305         $$.updateAndRedraw({ withLegend: false });
4306     };
4307
4308     Chart.prototype.load = function (args) {
4309         var $$ = this.internal,
4310             config = $$.config;
4311         // update xs if specified
4312         if (args.xs) {
4313             $$.addXs(args.xs);
4314         }
4315         // update names if exists
4316         if ('names' in args) {
4317             Chart.prototype.data.names.bind(this)(args.names);
4318         }
4319         // update classes if exists
4320         if ('classes' in args) {
4321             Object.keys(args.classes).forEach(function (id) {
4322                 config.data_classes[id] = args.classes[id];
4323             });
4324         }
4325         // update categories if exists
4326         if ('categories' in args && $$.isCategorized()) {
4327             config.axis_x_categories = args.categories;
4328         }
4329         // update axes if exists
4330         if ('axes' in args) {
4331             Object.keys(args.axes).forEach(function (id) {
4332                 config.data_axes[id] = args.axes[id];
4333             });
4334         }
4335         // update colors if exists
4336         if ('colors' in args) {
4337             Object.keys(args.colors).forEach(function (id) {
4338                 config.data_colors[id] = args.colors[id];
4339             });
4340         }
4341         // use cache if exists
4342         if ('cacheIds' in args && $$.hasCaches(args.cacheIds)) {
4343             $$.load($$.getCaches(args.cacheIds), args.done);
4344             return;
4345         }
4346         // unload if needed
4347         if ('unload' in args) {
4348             // TODO: do not unload if target will load (included in url/rows/columns)
4349             $$.unload($$.mapToTargetIds(typeof args.unload === 'boolean' && args.unload ? null : args.unload), function () {
4350                 $$.loadFromArgs(args);
4351             });
4352         } else {
4353             $$.loadFromArgs(args);
4354         }
4355     };
4356
4357     Chart.prototype.unload = function (args) {
4358         var $$ = this.internal;
4359         args = args || {};
4360         if (args instanceof Array) {
4361             args = { ids: args };
4362         } else if (typeof args === 'string') {
4363             args = { ids: [args] };
4364         }
4365         $$.unload($$.mapToTargetIds(args.ids), function () {
4366             $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true });
4367             if (args.done) {
4368                 args.done();
4369             }
4370         });
4371     };
4372
4373     Chart.prototype.regions = function (regions) {
4374         var $$ = this.internal,
4375             config = $$.config;
4376         if (!regions) {
4377             return config.regions;
4378         }
4379         config.regions = regions;
4380         $$.redrawWithoutRescale();
4381         return config.regions;
4382     };
4383     Chart.prototype.regions.add = function (regions) {
4384         var $$ = this.internal,
4385             config = $$.config;
4386         if (!regions) {
4387             return config.regions;
4388         }
4389         config.regions = config.regions.concat(regions);
4390         $$.redrawWithoutRescale();
4391         return config.regions;
4392     };
4393     Chart.prototype.regions.remove = function (options) {
4394         var $$ = this.internal,
4395             config = $$.config,
4396             duration,
4397             classes,
4398             regions;
4399
4400         options = options || {};
4401         duration = $$.getOption(options, "duration", config.transition_duration);
4402         classes = $$.getOption(options, "classes", [CLASS.region]);
4403
4404         regions = $$.main.select('.' + CLASS.regions).selectAll(classes.map(function (c) {
4405             return '.' + c;
4406         }));
4407         (duration ? regions.transition().duration(duration) : regions).style('opacity', 0).remove();
4408
4409         config.regions = config.regions.filter(function (region) {
4410             var found = false;
4411             if (!region['class']) {
4412                 return true;
4413             }
4414             region['class'].split(' ').forEach(function (c) {
4415                 if (classes.indexOf(c) >= 0) {
4416                     found = true;
4417                 }
4418             });
4419             return !found;
4420         });
4421
4422         return config.regions;
4423     };
4424
4425     Chart.prototype.selected = function (targetId) {
4426         var $$ = this.internal,
4427             d3 = $$.d3;
4428         return d3.merge($$.main.selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(targetId)).selectAll('.' + CLASS.shape).filter(function () {
4429             return d3.select(this).classed(CLASS.SELECTED);
4430         }).map(function (d) {
4431             return d.map(function (d) {
4432                 var data = d.__data__;return data.data ? data.data : data;
4433             });
4434         }));
4435     };
4436     Chart.prototype.select = function (ids, indices, resetOther) {
4437         var $$ = this.internal,
4438             d3 = $$.d3,
4439             config = $$.config;
4440         if (!config.data_selection_enabled) {
4441             return;
4442         }
4443         $$.main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape).each(function (d, i) {
4444             var shape = d3.select(this),
4445                 id = d.data ? d.data.id : d.id,
4446                 toggle = $$.getToggle(this, d).bind($$),
4447                 isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0,
4448                 isTargetIndex = !indices || indices.indexOf(i) >= 0,
4449                 isSelected = shape.classed(CLASS.SELECTED);
4450             // line/area selection not supported yet
4451             if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) {
4452                 return;
4453             }
4454             if (isTargetId && isTargetIndex) {
4455                 if (config.data_selection_isselectable(d) && !isSelected) {
4456                     toggle(true, shape.classed(CLASS.SELECTED, true), d, i);
4457                 }
4458             } else if (isDefined(resetOther) && resetOther) {
4459                 if (isSelected) {
4460                     toggle(false, shape.classed(CLASS.SELECTED, false), d, i);
4461                 }
4462             }
4463         });
4464     };
4465     Chart.prototype.unselect = function (ids, indices) {
4466         var $$ = this.internal,
4467             d3 = $$.d3,
4468             config = $$.config;
4469         if (!config.data_selection_enabled) {
4470             return;
4471         }
4472         $$.main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape).each(function (d, i) {
4473             var shape = d3.select(this),
4474                 id = d.data ? d.data.id : d.id,
4475                 toggle = $$.getToggle(this, d).bind($$),
4476                 isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0,
4477                 isTargetIndex = !indices || indices.indexOf(i) >= 0,
4478                 isSelected = shape.classed(CLASS.SELECTED);
4479             // line/area selection not supported yet
4480             if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) {
4481                 return;
4482             }
4483             if (isTargetId && isTargetIndex) {
4484                 if (config.data_selection_isselectable(d)) {
4485                     if (isSelected) {
4486                         toggle(false, shape.classed(CLASS.SELECTED, false), d, i);
4487                     }
4488                 }
4489             }
4490         });
4491     };
4492
4493     Chart.prototype.show = function (targetIds, options) {
4494         var $$ = this.internal,
4495             targets;
4496
4497         targetIds = $$.mapToTargetIds(targetIds);
4498         options = options || {};
4499
4500         $$.removeHiddenTargetIds(targetIds);
4501         targets = $$.svg.selectAll($$.selectorTargets(targetIds));
4502
4503         targets.transition().style('display', 'initial', 'important').style('opacity', 1, 'important').call($$.endall, function () {
4504             targets.style('opacity', null).style('opacity', 1);
4505         });
4506
4507         if (options.withLegend) {
4508             $$.showLegend(targetIds);
4509         }
4510
4511         $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true });
4512     };
4513
4514     Chart.prototype.hide = function (targetIds, options) {
4515         var $$ = this.internal,
4516             targets;
4517
4518         targetIds = $$.mapToTargetIds(targetIds);
4519         options = options || {};
4520
4521         $$.addHiddenTargetIds(targetIds);
4522         targets = $$.svg.selectAll($$.selectorTargets(targetIds));
4523
4524         targets.transition().style('opacity', 0, 'important').call($$.endall, function () {
4525             targets.style('opacity', null).style('opacity', 0);
4526             targets.style('display', 'none');
4527         });
4528
4529         if (options.withLegend) {
4530             $$.hideLegend(targetIds);
4531         }
4532
4533         $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true });
4534     };
4535
4536     Chart.prototype.toggle = function (targetIds, options) {
4537         var that = this,
4538             $$ = this.internal;
4539         $$.mapToTargetIds(targetIds).forEach(function (targetId) {
4540             $$.isTargetToShow(targetId) ? that.hide(targetId, options) : that.show(targetId, options);
4541         });
4542     };
4543
4544     Chart.prototype.tooltip = function () {};
4545     Chart.prototype.tooltip.show = function (args) {
4546         var $$ = this.internal,
4547             targets,
4548             data,
4549             mouse = {};
4550
4551         // determine mouse position on the chart
4552         if (args.mouse) {
4553             mouse = args.mouse;
4554         } else {
4555             // determine focus data
4556             if (args.data) {
4557                 data = args.data;
4558             } else if (typeof args.x !== 'undefined') {
4559                 if (args.id) {
4560                     targets = $$.data.targets.filter(function (t) {
4561                         return t.id === args.id;
4562                     });
4563                 } else {
4564                     targets = $$.data.targets;
4565                 }
4566                 data = $$.filterByX(targets, args.x).slice(0, 1)[0];
4567             }
4568             mouse = data ? $$.getMousePosition(data) : null;
4569         }
4570
4571         // emulate mouse events to show
4572         $$.dispatchEvent('mousemove', mouse);
4573
4574         $$.config.tooltip_onshow.call($$, data);
4575     };
4576     Chart.prototype.tooltip.hide = function () {
4577         // TODO: get target data by checking the state of focus
4578         this.internal.dispatchEvent('mouseout', 0);
4579
4580         this.internal.config.tooltip_onhide.call(this);
4581     };
4582
4583     Chart.prototype.transform = function (type, targetIds) {
4584         var $$ = this.internal,
4585             options = ['pie', 'donut'].indexOf(type) >= 0 ? { withTransform: true } : null;
4586         $$.transformTo(targetIds, type, options);
4587     };
4588
4589     ChartInternal.prototype.transformTo = function (targetIds, type, optionsForRedraw) {
4590         var $$ = this,
4591             withTransitionForAxis = !$$.hasArcType(),
4592             options = optionsForRedraw || { withTransitionForAxis: withTransitionForAxis };
4593         options.withTransitionForTransform = false;
4594         $$.transiting = false;
4595         $$.setTargetType(targetIds, type);
4596         $$.updateTargets($$.data.targets); // this is needed when transforming to arc
4597         $$.updateAndRedraw(options);
4598     };
4599
4600     Chart.prototype.x = function (x) {
4601         var $$ = this.internal;
4602         if (arguments.length) {
4603             $$.updateTargetX($$.data.targets, x);
4604             $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true });
4605         }
4606         return $$.data.xs;
4607     };
4608     Chart.prototype.xs = function (xs) {
4609         var $$ = this.internal;
4610         if (arguments.length) {
4611             $$.updateTargetXs($$.data.targets, xs);
4612             $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true });
4613         }
4614         return $$.data.xs;
4615     };
4616
4617     Chart.prototype.zoom = function (domain) {
4618         var $$ = this.internal;
4619         if (domain) {
4620             if ($$.isTimeSeries()) {
4621                 domain = domain.map(function (x) {
4622                     return $$.parseDate(x);
4623                 });
4624             }
4625             if ($$.config.subchart_show) {
4626                 $$.brush.selectionAsValue(domain, true);
4627             } else {
4628                 $$.updateXDomain(null, true, false, false, domain);
4629                 $$.redraw({ withY: $$.config.zoom_rescale, withSubchart: false });
4630             }
4631             $$.config.zoom_onzoom.call(this, $$.x.orgDomain());
4632             return domain;
4633         } else {
4634             return $$.x.domain();
4635         }
4636     };
4637     Chart.prototype.zoom.enable = function (enabled) {
4638         var $$ = this.internal;
4639         $$.config.zoom_enabled = enabled;
4640         $$.updateAndRedraw();
4641     };
4642     Chart.prototype.unzoom = function () {
4643         var $$ = this.internal;
4644         if ($$.config.subchart_show) {
4645             $$.brush.clear();
4646         } else {
4647             $$.updateXDomain(null, true, false, false, $$.subX.domain());
4648             $$.redraw({ withY: $$.config.zoom_rescale, withSubchart: false });
4649         }
4650     };
4651
4652     Chart.prototype.zoom.max = function (max) {
4653         var $$ = this.internal,
4654             config = $$.config,
4655             d3 = $$.d3;
4656         if (max === 0 || max) {
4657             config.zoom_x_max = d3.max([$$.orgXDomain[1], max]);
4658         } else {
4659             return config.zoom_x_max;
4660         }
4661     };
4662
4663     Chart.prototype.zoom.min = function (min) {
4664         var $$ = this.internal,
4665             config = $$.config,
4666             d3 = $$.d3;
4667         if (min === 0 || min) {
4668             config.zoom_x_min = d3.min([$$.orgXDomain[0], min]);
4669         } else {
4670             return config.zoom_x_min;
4671         }
4672     };
4673
4674     Chart.prototype.zoom.range = function (range) {
4675         if (arguments.length) {
4676             if (isDefined(range.max)) {
4677                 this.domain.max(range.max);
4678             }
4679             if (isDefined(range.min)) {
4680                 this.domain.min(range.min);
4681             }
4682         } else {
4683             return {
4684                 max: this.domain.max(),
4685                 min: this.domain.min()
4686             };
4687         }
4688     };
4689
4690     ChartInternal.prototype.initPie = function () {
4691         var $$ = this,
4692             d3 = $$.d3;
4693         $$.pie = d3.pie().value(function (d) {
4694             return d.values.reduce(function (a, b) {
4695                 return a + b.value;
4696             }, 0);
4697         });
4698
4699         var orderFct = $$.getOrderFunction();
4700
4701         // we need to reverse the returned order if asc or desc to have the slice in expected order.
4702         if (orderFct && ($$.isOrderAsc() || $$.isOrderDesc())) {
4703             var defaultSort = orderFct;
4704             orderFct = function orderFct(t1, t2) {
4705                 return defaultSort(t1, t2) * -1;
4706             };
4707         }
4708
4709         $$.pie.sort(orderFct || null);
4710     };
4711
4712     ChartInternal.prototype.updateRadius = function () {
4713         var $$ = this,
4714             config = $$.config,
4715             w = config.gauge_width || config.donut_width,
4716             gaugeArcWidth = $$.filterTargetsToShow($$.data.targets).length * $$.config.gauge_arcs_minWidth;
4717         $$.radiusExpanded = Math.min($$.arcWidth, $$.arcHeight) / 2 * ($$.hasType('gauge') ? 0.85 : 1);
4718         $$.radius = $$.radiusExpanded * 0.95;
4719         $$.innerRadiusRatio = w ? ($$.radius - w) / $$.radius : 0.6;
4720         $$.innerRadius = $$.hasType('donut') || $$.hasType('gauge') ? $$.radius * $$.innerRadiusRatio : 0;
4721         $$.gaugeArcWidth = w ? w : gaugeArcWidth <= $$.radius - $$.innerRadius ? $$.radius - $$.innerRadius : gaugeArcWidth <= $$.radius ? gaugeArcWidth : $$.radius;
4722     };
4723
4724     ChartInternal.prototype.updateArc = function () {
4725         var $$ = this;
4726         $$.svgArc = $$.getSvgArc();
4727         $$.svgArcExpanded = $$.getSvgArcExpanded();
4728         $$.svgArcExpandedSub = $$.getSvgArcExpanded(0.98);
4729     };
4730
4731     ChartInternal.prototype.updateAngle = function (d) {
4732         var $$ = this,
4733             config = $$.config,
4734             found = false,
4735             index = 0,
4736             gMin,
4737             gMax,
4738             gTic,
4739             gValue;
4740
4741         if (!config) {
4742             return null;
4743         }
4744
4745         $$.pie($$.filterTargetsToShow($$.data.targets)).forEach(function (t) {
4746             if (!found && t.data.id === d.data.id) {
4747                 found = true;
4748                 d = t;
4749                 d.index = index;
4750             }
4751             index++;
4752         });
4753         if (isNaN(d.startAngle)) {
4754             d.startAngle = 0;
4755         }
4756         if (isNaN(d.endAngle)) {
4757             d.endAngle = d.startAngle;
4758         }
4759         if ($$.isGaugeType(d.data)) {
4760             gMin = config.gauge_min;
4761             gMax = config.gauge_max;
4762             gTic = Math.PI * (config.gauge_fullCircle ? 2 : 1) / (gMax - gMin);
4763             gValue = d.value < gMin ? 0 : d.value < gMax ? d.value - gMin : gMax - gMin;
4764             d.startAngle = config.gauge_startingAngle;
4765             d.endAngle = d.startAngle + gTic * gValue;
4766         }
4767         return found ? d : null;
4768     };
4769
4770     ChartInternal.prototype.getSvgArc = function () {
4771         var $$ = this,
4772             hasGaugeType = $$.hasType('gauge'),
4773             singleArcWidth = $$.gaugeArcWidth / $$.filterTargetsToShow($$.data.targets).length,
4774             arc = $$.d3.arc().outerRadius(function (d) {
4775             return hasGaugeType ? $$.radius - singleArcWidth * d.index : $$.radius;
4776         }).innerRadius(function (d) {
4777             return hasGaugeType ? $$.radius - singleArcWidth * (d.index + 1) : $$.innerRadius;
4778         }),
4779             newArc = function newArc(d, withoutUpdate) {
4780             var updated;
4781             if (withoutUpdate) {
4782                 return arc(d);
4783             } // for interpolate
4784             updated = $$.updateAngle(d);
4785             return updated ? arc(updated) : "M 0 0";
4786         };
4787         // TODO: extends all function
4788         newArc.centroid = arc.centroid;
4789         return newArc;
4790     };
4791
4792     ChartInternal.prototype.getSvgArcExpanded = function (rate) {
4793         rate = rate || 1;
4794         var $$ = this,
4795             hasGaugeType = $$.hasType('gauge'),
4796             singleArcWidth = $$.gaugeArcWidth / $$.filterTargetsToShow($$.data.targets).length,
4797             expandWidth = Math.min($$.radiusExpanded * rate - $$.radius, singleArcWidth * 0.8 - (1 - rate) * 100),
4798             arc = $$.d3.arc().outerRadius(function (d) {
4799             return hasGaugeType ? $$.radius - singleArcWidth * d.index + expandWidth : $$.radiusExpanded * rate;
4800         }).innerRadius(function (d) {
4801             return hasGaugeType ? $$.radius - singleArcWidth * (d.index + 1) : $$.innerRadius;
4802         });
4803         return function (d) {
4804             var updated = $$.updateAngle(d);
4805             return updated ? arc(updated) : "M 0 0";
4806         };
4807     };
4808
4809     ChartInternal.prototype.getArc = function (d, withoutUpdate, force) {
4810         return force || this.isArcType(d.data) ? this.svgArc(d, withoutUpdate) : "M 0 0";
4811     };
4812
4813     ChartInternal.prototype.transformForArcLabel = function (d) {
4814         var $$ = this,
4815             config = $$.config,
4816             updated = $$.updateAngle(d),
4817             c,
4818             x,
4819             y,
4820             h,
4821             ratio,
4822             translate = "",
4823             hasGauge = $$.hasType('gauge');
4824         if (updated && !hasGauge) {
4825             c = this.svgArc.centroid(updated);
4826             x = isNaN(c[0]) ? 0 : c[0];
4827             y = isNaN(c[1]) ? 0 : c[1];
4828             h = Math.sqrt(x * x + y * y);
4829             if ($$.hasType('donut') && config.donut_label_ratio) {
4830                 ratio = isFunction(config.donut_label_ratio) ? config.donut_label_ratio(d, $$.radius, h) : config.donut_label_ratio;
4831             } else if ($$.hasType('pie') && config.pie_label_ratio) {
4832                 ratio = isFunction(config.pie_label_ratio) ? config.pie_label_ratio(d, $$.radius, h) : config.pie_label_ratio;
4833             } else {
4834                 ratio = $$.radius && h ? (36 / $$.radius > 0.375 ? 1.175 - 36 / $$.radius : 0.8) * $$.radius / h : 0;
4835             }
4836             translate = "translate(" + x * ratio + ',' + y * ratio + ")";
4837         } else if (updated && hasGauge && $$.filterTargetsToShow($$.data.targets).length > 1) {
4838             var y1 = Math.sin(updated.endAngle - Math.PI / 2);
4839             x = Math.cos(updated.endAngle - Math.PI / 2) * ($$.radiusExpanded + 25);
4840             y = y1 * ($$.radiusExpanded + 15 - Math.abs(y1 * 10)) + 3;
4841             translate = "translate(" + x + ',' + y + ")";
4842         }
4843         return translate;
4844     };
4845
4846     ChartInternal.prototype.getArcRatio = function (d) {
4847         var $$ = this,
4848             config = $$.config,
4849             whole = Math.PI * ($$.hasType('gauge') && !config.gauge_fullCircle ? 1 : 2);
4850         return d ? (d.endAngle - d.startAngle) / whole : null;
4851     };
4852
4853     ChartInternal.prototype.convertToArcData = function (d) {
4854         return this.addName({
4855             id: d.data.id,
4856             value: d.value,
4857             ratio: this.getArcRatio(d),
4858             index: d.index
4859         });
4860     };
4861
4862     ChartInternal.prototype.textForArcLabel = function (d) {
4863         var $$ = this,
4864             updated,
4865             value,
4866             ratio,
4867             id,
4868             format;
4869         if (!$$.shouldShowArcLabel()) {
4870             return "";
4871         }
4872         updated = $$.updateAngle(d);
4873         value = updated ? updated.value : null;
4874         ratio = $$.getArcRatio(updated);
4875         id = d.data.id;
4876         if (!$$.hasType('gauge') && !$$.meetsArcLabelThreshold(ratio)) {
4877             return "";
4878         }
4879         format = $$.getArcLabelFormat();
4880         return format ? format(value, ratio, id) : $$.defaultArcValueFormat(value, ratio);
4881     };
4882
4883     ChartInternal.prototype.textForGaugeMinMax = function (value, isMax) {
4884         var $$ = this,
4885             format = $$.getGaugeLabelExtents();
4886
4887         return format ? format(value, isMax) : value;
4888     };
4889
4890     ChartInternal.prototype.expandArc = function (targetIds) {
4891         var $$ = this,
4892             interval;
4893
4894         // MEMO: avoid to cancel transition
4895         if ($$.transiting) {
4896             interval = window.setInterval(function () {
4897                 if (!$$.transiting) {
4898                     window.clearInterval(interval);
4899                     if ($$.legend.selectAll('.c3-legend-item-focused').size() > 0) {
4900                         $$.expandArc(targetIds);
4901                     }
4902                 }
4903             }, 10);
4904             return;
4905         }
4906
4907         targetIds = $$.mapToTargetIds(targetIds);
4908
4909         $$.svg.selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)).each(function (d) {
4910             if (!$$.shouldExpand(d.data.id)) {
4911                 return;
4912             }
4913             $$.d3.select(this).selectAll('path').transition().duration($$.expandDuration(d.data.id)).attr("d", $$.svgArcExpanded).transition().duration($$.expandDuration(d.data.id) * 2).attr("d", $$.svgArcExpandedSub).each(function (d) {
4914                 if ($$.isDonutType(d.data)) ;
4915             });
4916         });
4917     };
4918
4919     ChartInternal.prototype.unexpandArc = function (targetIds) {
4920         var $$ = this;
4921
4922         if ($$.transiting) {
4923             return;
4924         }
4925
4926         targetIds = $$.mapToTargetIds(targetIds);
4927
4928         $$.svg.selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)).selectAll('path').transition().duration(function (d) {
4929             return $$.expandDuration(d.data.id);
4930         }).attr("d", $$.svgArc);
4931         $$.svg.selectAll('.' + CLASS.arc);
4932     };
4933
4934     ChartInternal.prototype.expandDuration = function (id) {
4935         var $$ = this,
4936             config = $$.config;
4937
4938         if ($$.isDonutType(id)) {
4939             return config.donut_expand_duration;
4940         } else if ($$.isGaugeType(id)) {
4941             return config.gauge_expand_duration;
4942         } else if ($$.isPieType(id)) {
4943             return config.pie_expand_duration;
4944         } else {
4945             return 50;
4946         }
4947     };
4948
4949     ChartInternal.prototype.shouldExpand = function (id) {
4950         var $$ = this,
4951             config = $$.config;
4952         return $$.isDonutType(id) && config.donut_expand || $$.isGaugeType(id) && config.gauge_expand || $$.isPieType(id) && config.pie_expand;
4953     };
4954
4955     ChartInternal.prototype.shouldShowArcLabel = function () {
4956         var $$ = this,
4957             config = $$.config,
4958             shouldShow = true;
4959         if ($$.hasType('donut')) {
4960             shouldShow = config.donut_label_show;
4961         } else if ($$.hasType('pie')) {
4962             shouldShow = config.pie_label_show;
4963         }
4964         // when gauge, always true
4965         return shouldShow;
4966     };
4967
4968     ChartInternal.prototype.meetsArcLabelThreshold = function (ratio) {
4969         var $$ = this,
4970             config = $$.config,
4971             threshold = $$.hasType('donut') ? config.donut_label_threshold : config.pie_label_threshold;
4972         return ratio >= threshold;
4973     };
4974
4975     ChartInternal.prototype.getArcLabelFormat = function () {
4976         var $$ = this,
4977             config = $$.config,
4978             format = config.pie_label_format;
4979         if ($$.hasType('gauge')) {
4980             format = config.gauge_label_format;
4981         } else if ($$.hasType('donut')) {
4982             format = config.donut_label_format;
4983         }
4984         return format;
4985     };
4986
4987     ChartInternal.prototype.getGaugeLabelExtents = function () {
4988         var $$ = this,
4989             config = $$.config;
4990         return config.gauge_label_extents;
4991     };
4992
4993     ChartInternal.prototype.getArcTitle = function () {
4994         var $$ = this;
4995         return $$.hasType('donut') ? $$.config.donut_title : "";
4996     };
4997
4998     ChartInternal.prototype.updateTargetsForArc = function (targets) {
4999         var $$ = this,
5000             main = $$.main,
5001             mainPies,
5002             mainPieEnter,
5003             classChartArc = $$.classChartArc.bind($$),
5004             classArcs = $$.classArcs.bind($$),
5005             classFocus = $$.classFocus.bind($$);
5006         mainPies = main.select('.' + CLASS.chartArcs).selectAll('.' + CLASS.chartArc).data($$.pie(targets)).attr("class", function (d) {
5007             return classChartArc(d) + classFocus(d.data);
5008         });
5009         mainPieEnter = mainPies.enter().append("g").attr("class", classChartArc);
5010         mainPieEnter.append('g').attr('class', classArcs);
5011         mainPieEnter.append("text").attr("dy", $$.hasType('gauge') ? "-.1em" : ".35em").style("opacity", 0).style("text-anchor", "middle").style("pointer-events", "none");
5012         // MEMO: can not keep same color..., but not bad to update color in redraw
5013         //mainPieUpdate.exit().remove();
5014     };
5015
5016     ChartInternal.prototype.initArc = function () {
5017         var $$ = this;
5018         $$.arcs = $$.main.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartArcs).attr("transform", $$.getTranslate('arc'));
5019         $$.arcs.append('text').attr('class', CLASS.chartArcsTitle).style("text-anchor", "middle").text($$.getArcTitle());
5020     };
5021
5022     ChartInternal.prototype.redrawArc = function (duration, durationForExit, withTransform) {
5023         var $$ = this,
5024             d3 = $$.d3,
5025             config = $$.config,
5026             main = $$.main,
5027             arcs,
5028             mainArc,
5029             arcLabelLines,
5030             mainArcLabelLine,
5031             hasGaugeType = $$.hasType('gauge');
5032         arcs = main.selectAll('.' + CLASS.arcs).selectAll('.' + CLASS.arc).data($$.arcData.bind($$));
5033         mainArc = arcs.enter().append('path').attr("class", $$.classArc.bind($$)).style("fill", function (d) {
5034             return $$.color(d.data);
5035         }).style("cursor", function (d) {
5036             return config.interaction_enabled && config.data_selection_isselectable(d) ? "pointer" : null;
5037         }).each(function (d) {
5038             if ($$.isGaugeType(d.data)) {
5039                 d.startAngle = d.endAngle = config.gauge_startingAngle;
5040             }
5041             this._current = d;
5042         }).merge(arcs);
5043         if (hasGaugeType) {
5044             arcLabelLines = main.selectAll('.' + CLASS.arcs).selectAll('.' + CLASS.arcLabelLine).data($$.arcData.bind($$));
5045             mainArcLabelLine = arcLabelLines.enter().append('rect').attr("class", function (d) {
5046                 return CLASS.arcLabelLine + ' ' + CLASS.target + ' ' + CLASS.target + '-' + d.data.id;
5047             }).merge(arcLabelLines);
5048
5049             if ($$.filterTargetsToShow($$.data.targets).length === 1) {
5050                 mainArcLabelLine.style("display", "none");
5051             } else {
5052                 mainArcLabelLine.style("fill", function (d) {
5053                     return config.color_pattern.length > 0 ? $$.levelColor(d.data.values[0].value) : $$.color(d.data);
5054                 }).style("display", config.gauge_labelLine_show ? "" : "none").each(function (d) {
5055                     var lineLength = 0,
5056                         lineThickness = 2,
5057                         x = 0,
5058                         y = 0,
5059                         transform = "";
5060                     if ($$.hiddenTargetIds.indexOf(d.data.id) < 0) {
5061                         var updated = $$.updateAngle(d),
5062                             innerLineLength = $$.gaugeArcWidth / $$.filterTargetsToShow($$.data.targets).length * (updated.index + 1),
5063                             lineAngle = updated.endAngle - Math.PI / 2,
5064                             arcInnerRadius = $$.radius - innerLineLength,
5065                             linePositioningAngle = lineAngle - (arcInnerRadius === 0 ? 0 : 1 / arcInnerRadius);
5066                         lineLength = $$.radiusExpanded - $$.radius + innerLineLength;
5067                         x = Math.cos(linePositioningAngle) * arcInnerRadius;
5068                         y = Math.sin(linePositioningAngle) * arcInnerRadius;
5069                         transform = "rotate(" + lineAngle * 180 / Math.PI + ", " + x + ", " + y + ")";
5070                     }
5071                     d3.select(this).attr('x', x).attr('y', y).attr('width', lineLength).attr('height', lineThickness).attr('transform', transform).style("stroke-dasharray", "0, " + (lineLength + lineThickness) + ", 0");
5072                 });
5073             }
5074         }
5075         mainArc.attr("transform", function (d) {
5076             return !$$.isGaugeType(d.data) && withTransform ? "scale(0)" : "";
5077         }).on('mouseover', config.interaction_enabled ? function (d) {
5078             var updated, arcData;
5079             if ($$.transiting) {
5080                 // skip while transiting
5081                 return;
5082             }
5083             updated = $$.updateAngle(d);
5084             if (updated) {
5085                 arcData = $$.convertToArcData(updated);
5086                 // transitions
5087                 $$.expandArc(updated.data.id);
5088                 $$.api.focus(updated.data.id);
5089                 $$.toggleFocusLegend(updated.data.id, true);
5090                 $$.config.data_onmouseover(arcData, this);
5091             }
5092         } : null).on('mousemove', config.interaction_enabled ? function (d) {
5093             var updated = $$.updateAngle(d),
5094                 arcData,
5095                 selectedData;
5096             if (updated) {
5097                 arcData = $$.convertToArcData(updated), selectedData = [arcData];
5098                 $$.showTooltip(selectedData, this);
5099             }
5100         } : null).on('mouseout', config.interaction_enabled ? function (d) {
5101             var updated, arcData;
5102             if ($$.transiting) {
5103                 // skip while transiting
5104                 return;
5105             }
5106             updated = $$.updateAngle(d);
5107             if (updated) {
5108                 arcData = $$.convertToArcData(updated);
5109                 // transitions
5110                 $$.unexpandArc(updated.data.id);
5111                 $$.api.revert();
5112                 $$.revertLegend();
5113                 $$.hideTooltip();
5114                 $$.config.data_onmouseout(arcData, this);
5115             }
5116         } : null).on('click', config.interaction_enabled ? function (d, i) {
5117             var updated = $$.updateAngle(d),
5118                 arcData;
5119             if (updated) {
5120                 arcData = $$.convertToArcData(updated);
5121                 if ($$.toggleShape) {
5122                     $$.toggleShape(this, arcData, i);
5123                 }
5124                 $$.config.data_onclick.call($$.api, arcData, this);
5125             }
5126         } : null).each(function () {
5127             $$.transiting = true;
5128         }).transition().duration(duration).attrTween("d", function (d) {
5129             var updated = $$.updateAngle(d),
5130                 interpolate;
5131             if (!updated) {
5132                 return function () {
5133                     return "M 0 0";
5134                 };
5135             }
5136             //                if (this._current === d) {
5137             //                    this._current = {
5138             //                        startAngle: Math.PI*2,
5139             //                        endAngle: Math.PI*2,
5140             //                    };
5141             //                }
5142             if (isNaN(this._current.startAngle)) {
5143                 this._current.startAngle = 0;
5144             }
5145             if (isNaN(this._current.endAngle)) {
5146                 this._current.endAngle = this._current.startAngle;
5147             }
5148             interpolate = d3.interpolate(this._current, updated);
5149             this._current = interpolate(0);
5150             return function (t) {
5151                 var interpolated = interpolate(t);
5152                 interpolated.data = d.data; // data.id will be updated by interporator
5153                 return $$.getArc(interpolated, true);
5154             };
5155         }).attr("transform", withTransform ? "scale(1)" : "").style("fill", function (d) {
5156             return $$.levelColor ? $$.levelColor(d.data.values[0].value) : $$.color(d.data.id);
5157         }) // Where gauge reading color would receive customization.
5158         .call($$.endall, function () {
5159             $$.transiting = false;
5160         });
5161         arcs.exit().transition().duration(durationForExit).style('opacity', 0).remove();
5162         main.selectAll('.' + CLASS.chartArc).select('text').style("opacity", 0).attr('class', function (d) {
5163             return $$.isGaugeType(d.data) ? CLASS.gaugeValue : '';
5164         }).text($$.textForArcLabel.bind($$)).attr("transform", $$.transformForArcLabel.bind($$)).style('font-size', function (d) {
5165             return $$.isGaugeType(d.data) && $$.filterTargetsToShow($$.data.targets).length === 1 ? Math.round($$.radius / 5) + 'px' : '';
5166         }).transition().duration(duration).style("opacity", function (d) {
5167             return $$.isTargetToShow(d.data.id) && $$.isArcType(d.data) ? 1 : 0;
5168         });
5169         main.select('.' + CLASS.chartArcsTitle).style("opacity", $$.hasType('donut') || hasGaugeType ? 1 : 0);
5170
5171         if (hasGaugeType) {
5172             var index = 0;
5173             var backgroundArc = $$.arcs.select('g.' + CLASS.chartArcsBackground).selectAll('path.' + CLASS.chartArcsBackground).data($$.data.targets);
5174
5175             backgroundArc.enter().append("path").attr("class", function (d, i) {
5176                 return CLASS.chartArcsBackground + ' ' + CLASS.chartArcsBackground + '-' + i;
5177             }).merge(backgroundArc).attr("d", function (d1) {
5178                 if ($$.hiddenTargetIds.indexOf(d1.id) >= 0) {
5179                     return "M 0 0";
5180                 }
5181
5182                 var d = {
5183                     data: [{ value: config.gauge_max }],
5184                     startAngle: config.gauge_startingAngle,
5185                     endAngle: -1 * config.gauge_startingAngle * (config.gauge_fullCircle ? Math.PI : 1),
5186                     index: index++
5187                 };
5188                 return $$.getArc(d, true, true);
5189             });
5190
5191             backgroundArc.exit().remove();
5192
5193             $$.arcs.select('.' + CLASS.chartArcsGaugeUnit).attr("dy", ".75em").text(config.gauge_label_show ? config.gauge_units : '');
5194             $$.arcs.select('.' + CLASS.chartArcsGaugeMin).attr("dx", -1 * ($$.innerRadius + ($$.radius - $$.innerRadius) / (config.gauge_fullCircle ? 1 : 2)) + "px").attr("dy", "1.2em").text(config.gauge_label_show ? $$.textForGaugeMinMax(config.gauge_min, false) : '');
5195             $$.arcs.select('.' + CLASS.chartArcsGaugeMax).attr("dx", $$.innerRadius + ($$.radius - $$.innerRadius) / (config.gauge_fullCircle ? 1 : 2) + "px").attr("dy", "1.2em").text(config.gauge_label_show ? $$.textForGaugeMinMax(config.gauge_max, true) : '');
5196         }
5197     };
5198     ChartInternal.prototype.initGauge = function () {
5199         var arcs = this.arcs;
5200         if (this.hasType('gauge')) {
5201             arcs.append('g').attr("class", CLASS.chartArcsBackground);
5202             arcs.append("text").attr("class", CLASS.chartArcsGaugeUnit).style("text-anchor", "middle").style("pointer-events", "none");
5203             arcs.append("text").attr("class", CLASS.chartArcsGaugeMin).style("text-anchor", "middle").style("pointer-events", "none");
5204             arcs.append("text").attr("class", CLASS.chartArcsGaugeMax).style("text-anchor", "middle").style("pointer-events", "none");
5205         }
5206     };
5207     ChartInternal.prototype.getGaugeLabelHeight = function () {
5208         return this.config.gauge_label_show ? 20 : 0;
5209     };
5210
5211     ChartInternal.prototype.hasCaches = function (ids) {
5212         for (var i = 0; i < ids.length; i++) {
5213             if (!(ids[i] in this.cache)) {
5214                 return false;
5215             }
5216         }
5217         return true;
5218     };
5219     ChartInternal.prototype.addCache = function (id, target) {
5220         this.cache[id] = this.cloneTarget(target);
5221     };
5222     ChartInternal.prototype.getCaches = function (ids) {
5223         var targets = [],
5224             i;
5225         for (i = 0; i < ids.length; i++) {
5226             if (ids[i] in this.cache) {
5227                 targets.push(this.cloneTarget(this.cache[ids[i]]));
5228             }
5229         }
5230         return targets;
5231     };
5232
5233     ChartInternal.prototype.categoryName = function (i) {
5234         var config = this.config;
5235         return i < config.axis_x_categories.length ? config.axis_x_categories[i] : i;
5236     };
5237
5238     ChartInternal.prototype.generateTargetClass = function (targetId) {
5239         return targetId || targetId === 0 ? ('-' + targetId).replace(/\s/g, '-') : '';
5240     };
5241     ChartInternal.prototype.generateClass = function (prefix, targetId) {
5242         return " " + prefix + " " + prefix + this.generateTargetClass(targetId);
5243     };
5244     ChartInternal.prototype.classText = function (d) {
5245         return this.generateClass(CLASS.text, d.index);
5246     };
5247     ChartInternal.prototype.classTexts = function (d) {
5248         return this.generateClass(CLASS.texts, d.id);
5249     };
5250     ChartInternal.prototype.classShape = function (d) {
5251         return this.generateClass(CLASS.shape, d.index);
5252     };
5253     ChartInternal.prototype.classShapes = function (d) {
5254         return this.generateClass(CLASS.shapes, d.id);
5255     };
5256     ChartInternal.prototype.classLine = function (d) {
5257         return this.classShape(d) + this.generateClass(CLASS.line, d.id);
5258     };
5259     ChartInternal.prototype.classLines = function (d) {
5260         return this.classShapes(d) + this.generateClass(CLASS.lines, d.id);
5261     };
5262     ChartInternal.prototype.classCircle = function (d) {
5263         return this.classShape(d) + this.generateClass(CLASS.circle, d.index);
5264     };
5265     ChartInternal.prototype.classCircles = function (d) {
5266         return this.classShapes(d) + this.generateClass(CLASS.circles, d.id);
5267     };
5268     ChartInternal.prototype.classBar = function (d) {
5269         return this.classShape(d) + this.generateClass(CLASS.bar, d.index);
5270     };
5271     ChartInternal.prototype.classBars = function (d) {
5272         return this.classShapes(d) + this.generateClass(CLASS.bars, d.id);
5273     };
5274     ChartInternal.prototype.classArc = function (d) {
5275         return this.classShape(d.data) + this.generateClass(CLASS.arc, d.data.id);
5276     };
5277     ChartInternal.prototype.classArcs = function (d) {
5278         return this.classShapes(d.data) + this.generateClass(CLASS.arcs, d.data.id);
5279     };
5280     ChartInternal.prototype.classArea = function (d) {
5281         return this.classShape(d) + this.generateClass(CLASS.area, d.id);
5282     };
5283     ChartInternal.prototype.classAreas = function (d) {
5284         return this.classShapes(d) + this.generateClass(CLASS.areas, d.id);
5285     };
5286     ChartInternal.prototype.classRegion = function (d, i) {
5287         return this.generateClass(CLASS.region, i) + ' ' + ('class' in d ? d['class'] : '');
5288     };
5289     ChartInternal.prototype.classEvent = function (d) {
5290         return this.generateClass(CLASS.eventRect, d.index);
5291     };
5292     ChartInternal.prototype.classTarget = function (id) {
5293         var $$ = this;
5294         var additionalClassSuffix = $$.config.data_classes[id],
5295             additionalClass = '';
5296         if (additionalClassSuffix) {
5297             additionalClass = ' ' + CLASS.target + '-' + additionalClassSuffix;
5298         }
5299         return $$.generateClass(CLASS.target, id) + additionalClass;
5300     };
5301     ChartInternal.prototype.classFocus = function (d) {
5302         return this.classFocused(d) + this.classDefocused(d);
5303     };
5304     ChartInternal.prototype.classFocused = function (d) {
5305         return ' ' + (this.focusedTargetIds.indexOf(d.id) >= 0 ? CLASS.focused : '');
5306     };
5307     ChartInternal.prototype.classDefocused = function (d) {
5308         return ' ' + (this.defocusedTargetIds.indexOf(d.id) >= 0 ? CLASS.defocused : '');
5309     };
5310     ChartInternal.prototype.classChartText = function (d) {
5311         return CLASS.chartText + this.classTarget(d.id);
5312     };
5313     ChartInternal.prototype.classChartLine = function (d) {
5314         return CLASS.chartLine + this.classTarget(d.id);
5315     };
5316     ChartInternal.prototype.classChartBar = function (d) {
5317         return CLASS.chartBar + this.classTarget(d.id);
5318     };
5319     ChartInternal.prototype.classChartArc = function (d) {
5320         return CLASS.chartArc + this.classTarget(d.data.id);
5321     };
5322     ChartInternal.prototype.getTargetSelectorSuffix = function (targetId) {
5323         return this.generateTargetClass(targetId).replace(/([?!@#$%^&*()_=+,.<>'":;\[\]\/|~`{}\\])/g, '\\$1');
5324     };
5325     ChartInternal.prototype.selectorTarget = function (id, prefix) {
5326         return (prefix || '') + '.' + CLASS.target + this.getTargetSelectorSuffix(id);
5327     };
5328     ChartInternal.prototype.selectorTargets = function (ids, prefix) {
5329         var $$ = this;
5330         ids = ids || [];
5331         return ids.length ? ids.map(function (id) {
5332             return $$.selectorTarget(id, prefix);
5333         }) : null;
5334     };
5335     ChartInternal.prototype.selectorLegend = function (id) {
5336         return '.' + CLASS.legendItem + this.getTargetSelectorSuffix(id);
5337     };
5338     ChartInternal.prototype.selectorLegends = function (ids) {
5339         var $$ = this;
5340         return ids && ids.length ? ids.map(function (id) {
5341             return $$.selectorLegend(id);
5342         }) : null;
5343     };
5344
5345     ChartInternal.prototype.getClipPath = function (id) {
5346         var isIE9 = window.navigator.appVersion.toLowerCase().indexOf("msie 9.") >= 0;
5347         return "url(" + (isIE9 ? "" : document.URL.split('#')[0]) + "#" + id + ")";
5348     };
5349     ChartInternal.prototype.appendClip = function (parent, id) {
5350         return parent.append("clipPath").attr("id", id).append("rect");
5351     };
5352     ChartInternal.prototype.getAxisClipX = function (forHorizontal) {
5353         // axis line width + padding for left
5354         var left = Math.max(30, this.margin.left);
5355         return forHorizontal ? -(1 + left) : -(left - 1);
5356     };
5357     ChartInternal.prototype.getAxisClipY = function (forHorizontal) {
5358         return forHorizontal ? -20 : -this.margin.top;
5359     };
5360     ChartInternal.prototype.getXAxisClipX = function () {
5361         var $$ = this;
5362         return $$.getAxisClipX(!$$.config.axis_rotated);
5363     };
5364     ChartInternal.prototype.getXAxisClipY = function () {
5365         var $$ = this;
5366         return $$.getAxisClipY(!$$.config.axis_rotated);
5367     };
5368     ChartInternal.prototype.getYAxisClipX = function () {
5369         var $$ = this;
5370         return $$.config.axis_y_inner ? -1 : $$.getAxisClipX($$.config.axis_rotated);
5371     };
5372     ChartInternal.prototype.getYAxisClipY = function () {
5373         var $$ = this;
5374         return $$.getAxisClipY($$.config.axis_rotated);
5375     };
5376     ChartInternal.prototype.getAxisClipWidth = function (forHorizontal) {
5377         var $$ = this,
5378             left = Math.max(30, $$.margin.left),
5379             right = Math.max(30, $$.margin.right);
5380         // width + axis line width + padding for left/right
5381         return forHorizontal ? $$.width + 2 + left + right : $$.margin.left + 20;
5382     };
5383     ChartInternal.prototype.getAxisClipHeight = function (forHorizontal) {
5384         // less than 20 is not enough to show the axis label 'outer' without legend
5385         return (forHorizontal ? this.margin.bottom : this.margin.top + this.height) + 20;
5386     };
5387     ChartInternal.prototype.getXAxisClipWidth = function () {
5388         var $$ = this;
5389         return $$.getAxisClipWidth(!$$.config.axis_rotated);
5390     };
5391     ChartInternal.prototype.getXAxisClipHeight = function () {
5392         var $$ = this;
5393         return $$.getAxisClipHeight(!$$.config.axis_rotated);
5394     };
5395     ChartInternal.prototype.getYAxisClipWidth = function () {
5396         var $$ = this;
5397         return $$.getAxisClipWidth($$.config.axis_rotated) + ($$.config.axis_y_inner ? 20 : 0);
5398     };
5399     ChartInternal.prototype.getYAxisClipHeight = function () {
5400         var $$ = this;
5401         return $$.getAxisClipHeight($$.config.axis_rotated);
5402     };
5403
5404     ChartInternal.prototype.generateColor = function () {
5405         var $$ = this,
5406             config = $$.config,
5407             d3 = $$.d3,
5408             colors = config.data_colors,
5409             pattern = notEmpty(config.color_pattern) ? config.color_pattern : d3.schemeCategory10,
5410             callback = config.data_color,
5411             ids = [];
5412
5413         return function (d) {
5414             var id = d.id || d.data && d.data.id || d,
5415                 color;
5416
5417             // if callback function is provided
5418             if (colors[id] instanceof Function) {
5419                 color = colors[id](d);
5420             }
5421             // if specified, choose that color
5422             else if (colors[id]) {
5423                     color = colors[id];
5424                 }
5425                 // if not specified, choose from pattern
5426                 else {
5427                         if (ids.indexOf(id) < 0) {
5428                             ids.push(id);
5429                         }
5430                         color = pattern[ids.indexOf(id) % pattern.length];
5431                         colors[id] = color;
5432                     }
5433             return callback instanceof Function ? callback(color, d) : color;
5434         };
5435     };
5436     ChartInternal.prototype.generateLevelColor = function () {
5437         var $$ = this,
5438             config = $$.config,
5439             colors = config.color_pattern,
5440             threshold = config.color_threshold,
5441             asValue = threshold.unit === 'value',
5442             values = threshold.values && threshold.values.length ? threshold.values : [],
5443             max = threshold.max || 100;
5444         return notEmpty(config.color_threshold) ? function (value) {
5445             var i,
5446                 v,
5447                 color = colors[colors.length - 1];
5448             for (i = 0; i < values.length; i++) {
5449                 v = asValue ? value : value * 100 / max;
5450                 if (v < values[i]) {
5451                     color = colors[i];
5452                     break;
5453                 }
5454             }
5455             return color;
5456         } : null;
5457     };
5458
5459     ChartInternal.prototype.getDefaultConfig = function () {
5460         var config = {
5461             bindto: '#chart',
5462             svg_classname: undefined,
5463             size_width: undefined,
5464             size_height: undefined,
5465             padding_left: undefined,
5466             padding_right: undefined,
5467             padding_top: undefined,
5468             padding_bottom: undefined,
5469             resize_auto: true,
5470             zoom_enabled: false,
5471             zoom_initialRange: undefined,
5472             zoom_type: 'scroll',
5473             zoom_disableDefaultBehavior: false,
5474             zoom_privileged: false,
5475             zoom_rescale: false,
5476             zoom_onzoom: function zoom_onzoom() {},
5477             zoom_onzoomstart: function zoom_onzoomstart() {},
5478             zoom_onzoomend: function zoom_onzoomend() {},
5479             zoom_x_min: undefined,
5480             zoom_x_max: undefined,
5481             interaction_brighten: true,
5482             interaction_enabled: true,
5483             onmouseover: function onmouseover() {},
5484             onmouseout: function onmouseout() {},
5485             onresize: function onresize() {},
5486             onresized: function onresized() {},
5487             oninit: function oninit() {},
5488             onrendered: function onrendered() {},
5489             transition_duration: 350,
5490             data_x: undefined,
5491             data_xs: {},
5492             data_xFormat: '%Y-%m-%d',
5493             data_xLocaltime: true,
5494             data_xSort: true,
5495             data_idConverter: function data_idConverter(id) {
5496                 return id;
5497             },
5498             data_names: {},
5499             data_classes: {},
5500             data_groups: [],
5501             data_axes: {},
5502             data_type: undefined,
5503             data_types: {},
5504             data_labels: {},
5505             data_order: 'desc',
5506             data_regions: {},
5507             data_color: undefined,
5508             data_colors: {},
5509             data_hide: false,
5510             data_filter: undefined,
5511             data_selection_enabled: false,
5512             data_selection_grouped: false,
5513             data_selection_isselectable: function data_selection_isselectable() {
5514                 return true;
5515             },
5516             data_selection_multiple: true,
5517             data_selection_draggable: false,
5518             data_onclick: function data_onclick() {},
5519             data_onmouseover: function data_onmouseover() {},
5520             data_onmouseout: function data_onmouseout() {},
5521             data_onselected: function data_onselected() {},
5522             data_onunselected: function data_onunselected() {},
5523             data_url: undefined,
5524             data_headers: undefined,
5525             data_json: undefined,
5526             data_rows: undefined,
5527             data_columns: undefined,
5528             data_mimeType: undefined,
5529             data_keys: undefined,
5530             // configuration for no plot-able data supplied.
5531             data_empty_label_text: "",
5532             // subchart
5533             subchart_show: false,
5534             subchart_size_height: 60,
5535             subchart_axis_x_show: true,
5536             subchart_onbrush: function subchart_onbrush() {},
5537             // color
5538             color_pattern: [],
5539             color_threshold: {},
5540             // legend
5541             legend_show: true,
5542             legend_hide: false,
5543             legend_position: 'bottom',
5544             legend_inset_anchor: 'top-left',
5545             legend_inset_x: 10,
5546             legend_inset_y: 0,
5547             legend_inset_step: undefined,
5548             legend_item_onclick: undefined,
5549             legend_item_onmouseover: undefined,
5550             legend_item_onmouseout: undefined,
5551             legend_equally: false,
5552             legend_padding: 0,
5553             legend_item_tile_width: 10,
5554             legend_item_tile_height: 10,
5555             // axis
5556             axis_rotated: false,
5557             axis_x_show: true,
5558             axis_x_type: 'indexed',
5559             axis_x_localtime: true,
5560             axis_x_categories: [],
5561             axis_x_tick_centered: false,
5562             axis_x_tick_format: undefined,
5563             axis_x_tick_culling: {},
5564             axis_x_tick_culling_max: 10,
5565             axis_x_tick_count: undefined,
5566             axis_x_tick_fit: true,
5567             axis_x_tick_values: null,
5568             axis_x_tick_rotate: 0,
5569             axis_x_tick_outer: true,
5570             axis_x_tick_multiline: true,
5571             axis_x_tick_multilineMax: 0,
5572             axis_x_tick_width: null,
5573             axis_x_max: undefined,
5574             axis_x_min: undefined,
5575             axis_x_padding: {},
5576             axis_x_height: undefined,
5577             axis_x_selection: undefined,
5578             axis_x_label: {},
5579             axis_x_inner: undefined,
5580             axis_y_show: true,
5581             axis_y_type: undefined,
5582             axis_y_max: undefined,
5583             axis_y_min: undefined,
5584             axis_y_inverted: false,
5585             axis_y_center: undefined,
5586             axis_y_inner: undefined,
5587             axis_y_label: {},
5588             axis_y_tick_format: undefined,
5589             axis_y_tick_outer: true,
5590             axis_y_tick_values: null,
5591             axis_y_tick_rotate: 0,
5592             axis_y_tick_count: undefined,
5593             axis_y_tick_time_type: undefined,
5594             axis_y_tick_time_interval: undefined,
5595             axis_y_padding: {},
5596             axis_y_default: undefined,
5597             axis_y2_show: false,
5598             axis_y2_max: undefined,
5599             axis_y2_min: undefined,
5600             axis_y2_inverted: false,
5601             axis_y2_center: undefined,
5602             axis_y2_inner: undefined,
5603             axis_y2_label: {},
5604             axis_y2_tick_format: undefined,
5605             axis_y2_tick_outer: true,
5606             axis_y2_tick_values: null,
5607             axis_y2_tick_count: undefined,
5608             axis_y2_padding: {},
5609             axis_y2_default: undefined,
5610             // grid
5611             grid_x_show: false,
5612             grid_x_type: 'tick',
5613             grid_x_lines: [],
5614             grid_y_show: false,
5615             // not used
5616             // grid_y_type: 'tick',
5617             grid_y_lines: [],
5618             grid_y_ticks: 10,
5619             grid_focus_show: true,
5620             grid_lines_front: true,
5621             // point - point of each data
5622             point_show: true,
5623             point_r: 2.5,
5624             point_sensitivity: 10,
5625             point_focus_expand_enabled: true,
5626             point_focus_expand_r: undefined,
5627             point_select_r: undefined,
5628             // line
5629             line_connectNull: false,
5630             line_step_type: 'step',
5631             // bar
5632             bar_width: undefined,
5633             bar_width_ratio: 0.6,
5634             bar_width_max: undefined,
5635             bar_zerobased: true,
5636             bar_space: 0,
5637             // area
5638             area_zerobased: true,
5639             area_above: false,
5640             // pie
5641             pie_label_show: true,
5642             pie_label_format: undefined,
5643             pie_label_threshold: 0.05,
5644             pie_label_ratio: undefined,
5645             pie_expand: {},
5646             pie_expand_duration: 50,
5647             // gauge
5648             gauge_fullCircle: false,
5649             gauge_label_show: true,
5650             gauge_labelLine_show: true,
5651             gauge_label_format: undefined,
5652             gauge_min: 0,
5653             gauge_max: 100,
5654             gauge_startingAngle: -1 * Math.PI / 2,
5655             gauge_label_extents: undefined,
5656             gauge_units: undefined,
5657             gauge_width: undefined,
5658             gauge_arcs_minWidth: 5,
5659             gauge_expand: {},
5660             gauge_expand_duration: 50,
5661             // donut
5662             donut_label_show: true,
5663             donut_label_format: undefined,
5664             donut_label_threshold: 0.05,
5665             donut_label_ratio: undefined,
5666             donut_width: undefined,
5667             donut_title: "",
5668             donut_expand: {},
5669             donut_expand_duration: 50,
5670             // spline
5671             spline_interpolation_type: 'cardinal',
5672             // region - region to change style
5673             regions: [],
5674             // tooltip - show when mouseover on each data
5675             tooltip_show: true,
5676             tooltip_grouped: true,
5677             tooltip_order: undefined,
5678             tooltip_format_title: undefined,
5679             tooltip_format_name: undefined,
5680             tooltip_format_value: undefined,
5681             tooltip_position: undefined,
5682             tooltip_contents: function tooltip_contents(d, defaultTitleFormat, defaultValueFormat, color) {
5683                 return this.getTooltipContent ? this.getTooltipContent(d, defaultTitleFormat, defaultValueFormat, color) : '';
5684             },
5685             tooltip_init_show: false,
5686             tooltip_init_x: 0,
5687             tooltip_init_position: { top: '0px', left: '50px' },
5688             tooltip_onshow: function tooltip_onshow() {},
5689             tooltip_onhide: function tooltip_onhide() {},
5690             // title
5691             title_text: undefined,
5692             title_padding: {
5693                 top: 0,
5694                 right: 0,
5695                 bottom: 0,
5696                 left: 0
5697             },
5698             title_position: 'top-center'
5699         };
5700
5701         Object.keys(this.additionalConfig).forEach(function (key) {
5702             config[key] = this.additionalConfig[key];
5703         }, this);
5704
5705         return config;
5706     };
5707     ChartInternal.prototype.additionalConfig = {};
5708
5709     ChartInternal.prototype.loadConfig = function (config) {
5710         var this_config = this.config,
5711             target,
5712             keys,
5713             read;
5714         function find() {
5715             var key = keys.shift();
5716             //        console.log("key =>", key, ", target =>", target);
5717             if (key && target && (typeof target === 'undefined' ? 'undefined' : _typeof(target)) === 'object' && key in target) {
5718                 target = target[key];
5719                 return find();
5720             } else if (!key) {
5721                 return target;
5722             } else {
5723                 return undefined;
5724             }
5725         }
5726         Object.keys(this_config).forEach(function (key) {
5727             target = config;
5728             keys = key.split('_');
5729             read = find();
5730             //        console.log("CONFIG : ", key, read);
5731             if (isDefined(read)) {
5732                 this_config[key] = read;
5733             }
5734         });
5735     };
5736
5737     ChartInternal.prototype.convertUrlToData = function (url, mimeType, headers, keys, done) {
5738         var $$ = this,
5739             type = mimeType ? mimeType : 'csv',
5740             f,
5741             converter;
5742
5743         if (type === 'json') {
5744             f = $$.d3.json;
5745             converter = $$.convertJsonToData;
5746         } else if (type === 'tsv') {
5747             f = $$.d3.tsv;
5748             converter = $$.convertXsvToData;
5749         } else {
5750             f = $$.d3.csv;
5751             converter = $$.convertXsvToData;
5752         }
5753
5754         f(url, headers).then(function (data) {
5755             done.call($$, converter.call($$, data, keys));
5756         }).catch(function (error) {
5757             throw error;
5758         });
5759     };
5760     ChartInternal.prototype.convertXsvToData = function (xsv) {
5761         var keys = xsv.columns,
5762             rows = xsv;
5763         if (rows.length === 0) {
5764             return { keys: keys, rows: [keys.reduce(function (row, key) {
5765                     return Object.assign(row, defineProperty({}, key, null));
5766                 }, {})] };
5767         } else {
5768             // [].concat() is to convert result into a plain array otherwise
5769             // test is not happy because rows have properties.
5770             return { keys: keys, rows: [].concat(xsv) };
5771         }
5772     };
5773     ChartInternal.prototype.convertJsonToData = function (json, keys) {
5774         var $$ = this,
5775             new_rows = [],
5776             targetKeys,
5777             data;
5778         if (keys) {
5779             // when keys specified, json would be an array that includes objects
5780             if (keys.x) {
5781                 targetKeys = keys.value.concat(keys.x);
5782                 $$.config.data_x = keys.x;
5783             } else {
5784                 targetKeys = keys.value;
5785             }
5786             new_rows.push(targetKeys);
5787             json.forEach(function (o) {
5788                 var new_row = [];
5789                 targetKeys.forEach(function (key) {
5790                     // convert undefined to null because undefined data will be removed in convertDataToTargets()
5791                     var v = $$.findValueInJson(o, key);
5792                     if (isUndefined(v)) {
5793                         v = null;
5794                     }
5795                     new_row.push(v);
5796                 });
5797                 new_rows.push(new_row);
5798             });
5799             data = $$.convertRowsToData(new_rows);
5800         } else {
5801             Object.keys(json).forEach(function (key) {
5802                 new_rows.push([key].concat(json[key]));
5803             });
5804             data = $$.convertColumnsToData(new_rows);
5805         }
5806         return data;
5807     };
5808     ChartInternal.prototype.findValueInJson = function (object, path) {
5809         path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties (replace [] with .)
5810         path = path.replace(/^\./, ''); // strip a leading dot
5811         var pathArray = path.split('.');
5812         for (var i = 0; i < pathArray.length; ++i) {
5813             var k = pathArray[i];
5814             if (k in object) {
5815                 object = object[k];
5816             } else {
5817                 return;
5818             }
5819         }
5820         return object;
5821     };
5822
5823     /**
5824      * Converts the rows to normalized data.
5825      * @param {any[][]} rows The row data
5826      * @return {Object}
5827      */
5828     ChartInternal.prototype.convertRowsToData = function (rows) {
5829         var newRows = [];
5830         var keys = rows[0];
5831
5832         for (var i = 1; i < rows.length; i++) {
5833             var newRow = {};
5834             for (var j = 0; j < rows[i].length; j++) {
5835                 if (isUndefined(rows[i][j])) {
5836                     throw new Error("Source data is missing a component at (" + i + "," + j + ")!");
5837                 }
5838                 newRow[keys[j]] = rows[i][j];
5839             }
5840             newRows.push(newRow);
5841         }
5842         return { keys: keys, rows: newRows };
5843     };
5844
5845     /**
5846      * Converts the columns to normalized data.
5847      * @param {any[][]} columns The column data
5848      * @return {Object}
5849      */
5850     ChartInternal.prototype.convertColumnsToData = function (columns) {
5851         var newRows = [];
5852         var keys = [];
5853
5854         for (var i = 0; i < columns.length; i++) {
5855             var key = columns[i][0];
5856             for (var j = 1; j < columns[i].length; j++) {
5857                 if (isUndefined(newRows[j - 1])) {
5858                     newRows[j - 1] = {};
5859                 }
5860                 if (isUndefined(columns[i][j])) {
5861                     throw new Error("Source data is missing a component at (" + i + "," + j + ")!");
5862                 }
5863                 newRows[j - 1][key] = columns[i][j];
5864             }
5865             keys.push(key);
5866         }
5867
5868         return { keys: keys, rows: newRows };
5869     };
5870
5871     /**
5872      * Converts the data format into the target format.
5873      * @param {!Object} data
5874      * @param {!Array} data.keys Ordered list of target IDs.
5875      * @param {!Array} data.rows Rows of data to convert.
5876      * @param {boolean} appendXs True to append to $$.data.xs, False to replace.
5877      * @return {!Array}
5878      */
5879     ChartInternal.prototype.convertDataToTargets = function (data, appendXs) {
5880         var $$ = this,
5881             config = $$.config,
5882             targets,
5883             ids,
5884             xs,
5885             keys;
5886
5887         // handles format where keys are not orderly provided
5888         if (isArray(data)) {
5889             keys = Object.keys(data[0]);
5890         } else {
5891             keys = data.keys;
5892             data = data.rows;
5893         }
5894
5895         ids = keys.filter($$.isNotX, $$);
5896         xs = keys.filter($$.isX, $$);
5897
5898         // save x for update data by load when custom x and c3.x API
5899         ids.forEach(function (id) {
5900             var xKey = $$.getXKey(id);
5901
5902             if ($$.isCustomX() || $$.isTimeSeries()) {
5903                 // if included in input data
5904                 if (xs.indexOf(xKey) >= 0) {
5905                     $$.data.xs[id] = (appendXs && $$.data.xs[id] ? $$.data.xs[id] : []).concat(data.map(function (d) {
5906                         return d[xKey];
5907                     }).filter(isValue).map(function (rawX, i) {
5908                         return $$.generateTargetX(rawX, id, i);
5909                     }));
5910                 }
5911                 // if not included in input data, find from preloaded data of other id's x
5912                 else if (config.data_x) {
5913                         $$.data.xs[id] = $$.getOtherTargetXs();
5914                     }
5915                     // if not included in input data, find from preloaded data
5916                     else if (notEmpty(config.data_xs)) {
5917                             $$.data.xs[id] = $$.getXValuesOfXKey(xKey, $$.data.targets);
5918                         }
5919                 // MEMO: if no x included, use same x of current will be used
5920             } else {
5921                 $$.data.xs[id] = data.map(function (d, i) {
5922                     return i;
5923                 });
5924             }
5925         });
5926
5927         // check x is defined
5928         ids.forEach(function (id) {
5929             if (!$$.data.xs[id]) {
5930                 throw new Error('x is not defined for id = "' + id + '".');
5931             }
5932         });
5933
5934         // convert to target
5935         targets = ids.map(function (id, index) {
5936             var convertedId = config.data_idConverter(id);
5937             return {
5938                 id: convertedId,
5939                 id_org: id,
5940                 values: data.map(function (d, i) {
5941                     var xKey = $$.getXKey(id),
5942                         rawX = d[xKey],
5943                         value = d[id] !== null && !isNaN(d[id]) ? +d[id] : null,
5944                         x;
5945                     // use x as categories if custom x and categorized
5946                     if ($$.isCustomX() && $$.isCategorized() && !isUndefined(rawX)) {
5947                         if (index === 0 && i === 0) {
5948                             config.axis_x_categories = [];
5949                         }
5950                         x = config.axis_x_categories.indexOf(rawX);
5951                         if (x === -1) {
5952                             x = config.axis_x_categories.length;
5953                             config.axis_x_categories.push(rawX);
5954                         }
5955                     } else {
5956                         x = $$.generateTargetX(rawX, id, i);
5957                     }
5958                     // mark as x = undefined if value is undefined and filter to remove after mapped
5959                     if (isUndefined(d[id]) || $$.data.xs[id].length <= i) {
5960                         x = undefined;
5961                     }
5962                     return { x: x, value: value, id: convertedId };
5963                 }).filter(function (v) {
5964                     return isDefined(v.x);
5965                 })
5966             };
5967         });
5968
5969         // finish targets
5970         targets.forEach(function (t) {
5971             var i;
5972             // sort values by its x
5973             if (config.data_xSort) {
5974                 t.values = t.values.sort(function (v1, v2) {
5975                     var x1 = v1.x || v1.x === 0 ? v1.x : Infinity,
5976                         x2 = v2.x || v2.x === 0 ? v2.x : Infinity;
5977                     return x1 - x2;
5978                 });
5979             }
5980             // indexing each value
5981             i = 0;
5982             t.values.forEach(function (v) {
5983                 v.index = i++;
5984             });
5985             // this needs to be sorted because its index and value.index is identical
5986             $$.data.xs[t.id].sort(function (v1, v2) {
5987                 return v1 - v2;
5988             });
5989         });
5990
5991         // cache information about values
5992         $$.hasNegativeValue = $$.hasNegativeValueInTargets(targets);
5993         $$.hasPositiveValue = $$.hasPositiveValueInTargets(targets);
5994
5995         // set target types
5996         if (config.data_type) {
5997             $$.setTargetType($$.mapToIds(targets).filter(function (id) {
5998                 return !(id in config.data_types);
5999             }), config.data_type);
6000         }
6001
6002         // cache as original id keyed
6003         targets.forEach(function (d) {
6004             $$.addCache(d.id_org, d);
6005         });
6006
6007         return targets;
6008     };
6009
6010     ChartInternal.prototype.isX = function (key) {
6011         var $$ = this,
6012             config = $$.config;
6013         return config.data_x && key === config.data_x || notEmpty(config.data_xs) && hasValue(config.data_xs, key);
6014     };
6015     ChartInternal.prototype.isNotX = function (key) {
6016         return !this.isX(key);
6017     };
6018     ChartInternal.prototype.getXKey = function (id) {
6019         var $$ = this,
6020             config = $$.config;
6021         return config.data_x ? config.data_x : notEmpty(config.data_xs) ? config.data_xs[id] : null;
6022     };
6023     ChartInternal.prototype.getXValuesOfXKey = function (key, targets) {
6024         var $$ = this,
6025             xValues,
6026             ids = targets && notEmpty(targets) ? $$.mapToIds(targets) : [];
6027         ids.forEach(function (id) {
6028             if ($$.getXKey(id) === key) {
6029                 xValues = $$.data.xs[id];
6030             }
6031         });
6032         return xValues;
6033     };
6034     ChartInternal.prototype.getXValue = function (id, i) {
6035         var $$ = this;
6036         return id in $$.data.xs && $$.data.xs[id] && isValue($$.data.xs[id][i]) ? $$.data.xs[id][i] : i;
6037     };
6038     ChartInternal.prototype.getOtherTargetXs = function () {
6039         var $$ = this,
6040             idsForX = Object.keys($$.data.xs);
6041         return idsForX.length ? $$.data.xs[idsForX[0]] : null;
6042     };
6043     ChartInternal.prototype.getOtherTargetX = function (index) {
6044         var xs = this.getOtherTargetXs();
6045         return xs && index < xs.length ? xs[index] : null;
6046     };
6047     ChartInternal.prototype.addXs = function (xs) {
6048         var $$ = this;
6049         Object.keys(xs).forEach(function (id) {
6050             $$.config.data_xs[id] = xs[id];
6051         });
6052     };
6053     ChartInternal.prototype.addName = function (data) {
6054         var $$ = this,
6055             name;
6056         if (data) {
6057             name = $$.config.data_names[data.id];
6058             data.name = name !== undefined ? name : data.id;
6059         }
6060         return data;
6061     };
6062     ChartInternal.prototype.getValueOnIndex = function (values, index) {
6063         var valueOnIndex = values.filter(function (v) {
6064             return v.index === index;
6065         });
6066         return valueOnIndex.length ? valueOnIndex[0] : null;
6067     };
6068     ChartInternal.prototype.updateTargetX = function (targets, x) {
6069         var $$ = this;
6070         targets.forEach(function (t) {
6071             t.values.forEach(function (v, i) {
6072                 v.x = $$.generateTargetX(x[i], t.id, i);
6073             });
6074             $$.data.xs[t.id] = x;
6075         });
6076     };
6077     ChartInternal.prototype.updateTargetXs = function (targets, xs) {
6078         var $$ = this;
6079         targets.forEach(function (t) {
6080             if (xs[t.id]) {
6081                 $$.updateTargetX([t], xs[t.id]);
6082             }
6083         });
6084     };
6085     ChartInternal.prototype.generateTargetX = function (rawX, id, index) {
6086         var $$ = this,
6087             x;
6088         if ($$.isTimeSeries()) {
6089             x = rawX ? $$.parseDate(rawX) : $$.parseDate($$.getXValue(id, index));
6090         } else if ($$.isCustomX() && !$$.isCategorized()) {
6091             x = isValue(rawX) ? +rawX : $$.getXValue(id, index);
6092         } else {
6093             x = index;
6094         }
6095         return x;
6096     };
6097     ChartInternal.prototype.cloneTarget = function (target) {
6098         return {
6099             id: target.id,
6100             id_org: target.id_org,
6101             values: target.values.map(function (d) {
6102                 return {
6103                     x: d.x,
6104                     value: d.value,
6105                     id: d.id
6106                 };
6107             })
6108         };
6109     };
6110     ChartInternal.prototype.getMaxDataCount = function () {
6111         var $$ = this;
6112         return $$.d3.max($$.data.targets, function (t) {
6113             return t.values.length;
6114         });
6115     };
6116     ChartInternal.prototype.mapToIds = function (targets) {
6117         return targets.map(function (d) {
6118             return d.id;
6119         });
6120     };
6121     ChartInternal.prototype.mapToTargetIds = function (ids) {
6122         var $$ = this;
6123         return ids ? [].concat(ids) : $$.mapToIds($$.data.targets);
6124     };
6125     ChartInternal.prototype.hasTarget = function (targets, id) {
6126         var ids = this.mapToIds(targets),
6127             i;
6128         for (i = 0; i < ids.length; i++) {
6129             if (ids[i] === id) {
6130                 return true;
6131             }
6132         }
6133         return false;
6134     };
6135     ChartInternal.prototype.isTargetToShow = function (targetId) {
6136         return this.hiddenTargetIds.indexOf(targetId) < 0;
6137     };
6138     ChartInternal.prototype.isLegendToShow = function (targetId) {
6139         return this.hiddenLegendIds.indexOf(targetId) < 0;
6140     };
6141     ChartInternal.prototype.filterTargetsToShow = function (targets) {
6142         var $$ = this;
6143         return targets.filter(function (t) {
6144             return $$.isTargetToShow(t.id);
6145         });
6146     };
6147     ChartInternal.prototype.mapTargetsToUniqueXs = function (targets) {
6148         var $$ = this;
6149         var xs = $$.d3.set($$.d3.merge(targets.map(function (t) {
6150             return t.values.map(function (v) {
6151                 return +v.x;
6152             });
6153         }))).values();
6154         xs = $$.isTimeSeries() ? xs.map(function (x) {
6155             return new Date(+x);
6156         }) : xs.map(function (x) {
6157             return +x;
6158         });
6159         return xs.sort(function (a, b) {
6160             return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
6161         });
6162     };
6163     ChartInternal.prototype.addHiddenTargetIds = function (targetIds) {
6164         targetIds = targetIds instanceof Array ? targetIds : new Array(targetIds);
6165         for (var i = 0; i < targetIds.length; i++) {
6166             if (this.hiddenTargetIds.indexOf(targetIds[i]) < 0) {
6167                 this.hiddenTargetIds = this.hiddenTargetIds.concat(targetIds[i]);
6168             }
6169         }
6170     };
6171     ChartInternal.prototype.removeHiddenTargetIds = function (targetIds) {
6172         this.hiddenTargetIds = this.hiddenTargetIds.filter(function (id) {
6173             return targetIds.indexOf(id) < 0;
6174         });
6175     };
6176     ChartInternal.prototype.addHiddenLegendIds = function (targetIds) {
6177         targetIds = targetIds instanceof Array ? targetIds : new Array(targetIds);
6178         for (var i = 0; i < targetIds.length; i++) {
6179             if (this.hiddenLegendIds.indexOf(targetIds[i]) < 0) {
6180                 this.hiddenLegendIds = this.hiddenLegendIds.concat(targetIds[i]);
6181             }
6182         }
6183     };
6184     ChartInternal.prototype.removeHiddenLegendIds = function (targetIds) {
6185         this.hiddenLegendIds = this.hiddenLegendIds.filter(function (id) {
6186             return targetIds.indexOf(id) < 0;
6187         });
6188     };
6189     ChartInternal.prototype.getValuesAsIdKeyed = function (targets) {
6190         var ys = {};
6191         targets.forEach(function (t) {
6192             ys[t.id] = [];
6193             t.values.forEach(function (v) {
6194                 ys[t.id].push(v.value);
6195             });
6196         });
6197         return ys;
6198     };
6199     ChartInternal.prototype.checkValueInTargets = function (targets, checker) {
6200         var ids = Object.keys(targets),
6201             i,
6202             j,
6203             values;
6204         for (i = 0; i < ids.length; i++) {
6205             values = targets[ids[i]].values;
6206             for (j = 0; j < values.length; j++) {
6207                 if (checker(values[j].value)) {
6208                     return true;
6209                 }
6210             }
6211         }
6212         return false;
6213     };
6214     ChartInternal.prototype.hasNegativeValueInTargets = function (targets) {
6215         return this.checkValueInTargets(targets, function (v) {
6216             return v < 0;
6217         });
6218     };
6219     ChartInternal.prototype.hasPositiveValueInTargets = function (targets) {
6220         return this.checkValueInTargets(targets, function (v) {
6221             return v > 0;
6222         });
6223     };
6224     ChartInternal.prototype.isOrderDesc = function () {
6225         var config = this.config;
6226         return typeof config.data_order === 'string' && config.data_order.toLowerCase() === 'desc';
6227     };
6228     ChartInternal.prototype.isOrderAsc = function () {
6229         var config = this.config;
6230         return typeof config.data_order === 'string' && config.data_order.toLowerCase() === 'asc';
6231     };
6232     ChartInternal.prototype.getOrderFunction = function () {
6233         var $$ = this,
6234             config = $$.config,
6235             orderAsc = $$.isOrderAsc(),
6236             orderDesc = $$.isOrderDesc();
6237         if (orderAsc || orderDesc) {
6238             var reducer = function reducer(p, c) {
6239                 return p + Math.abs(c.value);
6240             };
6241             return function (t1, t2) {
6242                 var t1Sum = t1.values.reduce(reducer, 0),
6243                     t2Sum = t2.values.reduce(reducer, 0);
6244                 return orderAsc ? t2Sum - t1Sum : t1Sum - t2Sum;
6245             };
6246         } else if (isFunction(config.data_order)) {
6247             return config.data_order;
6248         } else if (isArray(config.data_order)) {
6249             var order = config.data_order;
6250             return function (t1, t2) {
6251                 return order.indexOf(t1.id) - order.indexOf(t2.id);
6252             };
6253         }
6254     };
6255     ChartInternal.prototype.orderTargets = function (targets) {
6256         var fct = this.getOrderFunction();
6257         if (fct) {
6258             targets.sort(fct);
6259         }
6260         return targets;
6261     };
6262     ChartInternal.prototype.filterByX = function (targets, x) {
6263         return this.d3.merge(targets.map(function (t) {
6264             return t.values;
6265         })).filter(function (v) {
6266             return v.x - x === 0;
6267         });
6268     };
6269     ChartInternal.prototype.filterRemoveNull = function (data) {
6270         return data.filter(function (d) {
6271             return isValue(d.value);
6272         });
6273     };
6274     ChartInternal.prototype.filterByXDomain = function (targets, xDomain) {
6275         return targets.map(function (t) {
6276             return {
6277                 id: t.id,
6278                 id_org: t.id_org,
6279                 values: t.values.filter(function (v) {
6280                     return xDomain[0] <= v.x && v.x <= xDomain[1];
6281                 })
6282             };
6283         });
6284     };
6285     ChartInternal.prototype.hasDataLabel = function () {
6286         var config = this.config;
6287         if (typeof config.data_labels === 'boolean' && config.data_labels) {
6288             return true;
6289         } else if (_typeof(config.data_labels) === 'object' && notEmpty(config.data_labels)) {
6290             return true;
6291         }
6292         return false;
6293     };
6294     ChartInternal.prototype.getDataLabelLength = function (min, max, key) {
6295         var $$ = this,
6296             lengths = [0, 0],
6297             paddingCoef = 1.3;
6298         $$.selectChart.select('svg').selectAll('.dummy').data([min, max]).enter().append('text').text(function (d) {
6299             return $$.dataLabelFormat(d.id)(d);
6300         }).each(function (d, i) {
6301             lengths[i] = this.getBoundingClientRect()[key] * paddingCoef;
6302         }).remove();
6303         return lengths;
6304     };
6305     ChartInternal.prototype.isNoneArc = function (d) {
6306         return this.hasTarget(this.data.targets, d.id);
6307     }, ChartInternal.prototype.isArc = function (d) {
6308         return 'data' in d && this.hasTarget(this.data.targets, d.data.id);
6309     };
6310     ChartInternal.prototype.findClosestFromTargets = function (targets, pos) {
6311         var $$ = this,
6312             candidates;
6313
6314         // map to array of closest points of each target
6315         candidates = targets.map(function (target) {
6316             return $$.findClosest(target.values, pos);
6317         });
6318
6319         // decide closest point and return
6320         return $$.findClosest(candidates, pos);
6321     };
6322     ChartInternal.prototype.findClosest = function (values, pos) {
6323         var $$ = this,
6324             minDist = $$.config.point_sensitivity,
6325             closest;
6326
6327         // find mouseovering bar
6328         values.filter(function (v) {
6329             return v && $$.isBarType(v.id);
6330         }).forEach(function (v) {
6331             var shape = $$.main.select('.' + CLASS.bars + $$.getTargetSelectorSuffix(v.id) + ' .' + CLASS.bar + '-' + v.index).node();
6332             if (!closest && $$.isWithinBar($$.d3.mouse(shape), shape)) {
6333                 closest = v;
6334             }
6335         });
6336
6337         // find closest point from non-bar
6338         values.filter(function (v) {
6339             return v && !$$.isBarType(v.id);
6340         }).forEach(function (v) {
6341             var d = $$.dist(v, pos);
6342             if (d < minDist) {
6343                 minDist = d;
6344                 closest = v;
6345             }
6346         });
6347
6348         return closest;
6349     };
6350     ChartInternal.prototype.dist = function (data, pos) {
6351         var $$ = this,
6352             config = $$.config,
6353             xIndex = config.axis_rotated ? 1 : 0,
6354             yIndex = config.axis_rotated ? 0 : 1,
6355             y = $$.circleY(data, data.index),
6356             x = $$.x(data.x);
6357         return Math.sqrt(Math.pow(x - pos[xIndex], 2) + Math.pow(y - pos[yIndex], 2));
6358     };
6359     ChartInternal.prototype.convertValuesToStep = function (values) {
6360         var converted = [].concat(values),
6361             i;
6362
6363         if (!this.isCategorized()) {
6364             return values;
6365         }
6366
6367         for (i = values.length + 1; 0 < i; i--) {
6368             converted[i] = converted[i - 1];
6369         }
6370
6371         converted[0] = {
6372             x: converted[0].x - 1,
6373             value: converted[0].value,
6374             id: converted[0].id
6375         };
6376         converted[values.length + 1] = {
6377             x: converted[values.length].x + 1,
6378             value: converted[values.length].value,
6379             id: converted[values.length].id
6380         };
6381
6382         return converted;
6383     };
6384     ChartInternal.prototype.updateDataAttributes = function (name, attrs) {
6385         var $$ = this,
6386             config = $$.config,
6387             current = config['data_' + name];
6388         if (typeof attrs === 'undefined') {
6389             return current;
6390         }
6391         Object.keys(attrs).forEach(function (id) {
6392             current[id] = attrs[id];
6393         });
6394         $$.redraw({
6395             withLegend: true
6396         });
6397         return current;
6398     };
6399
6400     ChartInternal.prototype.load = function (targets, args) {
6401         var $$ = this;
6402         if (targets) {
6403             // filter loading targets if needed
6404             if (args.filter) {
6405                 targets = targets.filter(args.filter);
6406             }
6407             // set type if args.types || args.type specified
6408             if (args.type || args.types) {
6409                 targets.forEach(function (t) {
6410                     var type = args.types && args.types[t.id] ? args.types[t.id] : args.type;
6411                     $$.setTargetType(t.id, type);
6412                 });
6413             }
6414             // Update/Add data
6415             $$.data.targets.forEach(function (d) {
6416                 for (var i = 0; i < targets.length; i++) {
6417                     if (d.id === targets[i].id) {
6418                         d.values = targets[i].values;
6419                         targets.splice(i, 1);
6420                         break;
6421                     }
6422                 }
6423             });
6424             $$.data.targets = $$.data.targets.concat(targets); // add remained
6425         }
6426
6427         // Set targets
6428         $$.updateTargets($$.data.targets);
6429
6430         // Redraw with new targets
6431         $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true });
6432
6433         if (args.done) {
6434             args.done();
6435         }
6436     };
6437     ChartInternal.prototype.loadFromArgs = function (args) {
6438         var $$ = this;
6439         if (args.data) {
6440             $$.load($$.convertDataToTargets(args.data), args);
6441         } else if (args.url) {
6442             $$.convertUrlToData(args.url, args.mimeType, args.headers, args.keys, function (data) {
6443                 $$.load($$.convertDataToTargets(data), args);
6444             });
6445         } else if (args.json) {
6446             $$.load($$.convertDataToTargets($$.convertJsonToData(args.json, args.keys)), args);
6447         } else if (args.rows) {
6448             $$.load($$.convertDataToTargets($$.convertRowsToData(args.rows)), args);
6449         } else if (args.columns) {
6450             $$.load($$.convertDataToTargets($$.convertColumnsToData(args.columns)), args);
6451         } else {
6452             $$.load(null, args);
6453         }
6454     };
6455     ChartInternal.prototype.unload = function (targetIds, done) {
6456         var $$ = this;
6457         if (!done) {
6458             done = function done() {};
6459         }
6460         // filter existing target
6461         targetIds = targetIds.filter(function (id) {
6462             return $$.hasTarget($$.data.targets, id);
6463         });
6464         // If no target, call done and return
6465         if (!targetIds || targetIds.length === 0) {
6466             done();
6467             return;
6468         }
6469         $$.svg.selectAll(targetIds.map(function (id) {
6470             return $$.selectorTarget(id);
6471         })).transition().style('opacity', 0).remove().call($$.endall, done);
6472         targetIds.forEach(function (id) {
6473             // Reset fadein for future load
6474             $$.withoutFadeIn[id] = false;
6475             // Remove target's elements
6476             if ($$.legend) {
6477                 $$.legend.selectAll('.' + CLASS.legendItem + $$.getTargetSelectorSuffix(id)).remove();
6478             }
6479             // Remove target
6480             $$.data.targets = $$.data.targets.filter(function (t) {
6481                 return t.id !== id;
6482             });
6483         });
6484     };
6485
6486     ChartInternal.prototype.getYDomainMin = function (targets) {
6487         var $$ = this,
6488             config = $$.config,
6489             ids = $$.mapToIds(targets),
6490             ys = $$.getValuesAsIdKeyed(targets),
6491             j,
6492             k,
6493             baseId,
6494             idsInGroup,
6495             id,
6496             hasNegativeValue;
6497         if (config.data_groups.length > 0) {
6498             hasNegativeValue = $$.hasNegativeValueInTargets(targets);
6499             for (j = 0; j < config.data_groups.length; j++) {
6500                 // Determine baseId
6501                 idsInGroup = config.data_groups[j].filter(function (id) {
6502                     return ids.indexOf(id) >= 0;
6503                 });
6504                 if (idsInGroup.length === 0) {
6505                     continue;
6506                 }
6507                 baseId = idsInGroup[0];
6508                 // Consider negative values
6509                 if (hasNegativeValue && ys[baseId]) {
6510                     ys[baseId].forEach(function (v, i) {
6511                         ys[baseId][i] = v < 0 ? v : 0;
6512                     });
6513                 }
6514                 // Compute min
6515                 for (k = 1; k < idsInGroup.length; k++) {
6516                     id = idsInGroup[k];
6517                     if (!ys[id]) {
6518                         continue;
6519                     }
6520                     ys[id].forEach(function (v, i) {
6521                         if ($$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasNegativeValue && +v > 0)) {
6522                             ys[baseId][i] += +v;
6523                         }
6524                     });
6525                 }
6526             }
6527         }
6528         return $$.d3.min(Object.keys(ys).map(function (key) {
6529             return $$.d3.min(ys[key]);
6530         }));
6531     };
6532     ChartInternal.prototype.getYDomainMax = function (targets) {
6533         var $$ = this,
6534             config = $$.config,
6535             ids = $$.mapToIds(targets),
6536             ys = $$.getValuesAsIdKeyed(targets),
6537             j,
6538             k,
6539             baseId,
6540             idsInGroup,
6541             id,
6542             hasPositiveValue;
6543         if (config.data_groups.length > 0) {
6544             hasPositiveValue = $$.hasPositiveValueInTargets(targets);
6545             for (j = 0; j < config.data_groups.length; j++) {
6546                 // Determine baseId
6547                 idsInGroup = config.data_groups[j].filter(function (id) {
6548                     return ids.indexOf(id) >= 0;
6549                 });
6550                 if (idsInGroup.length === 0) {
6551                     continue;
6552                 }
6553                 baseId = idsInGroup[0];
6554                 // Consider positive values
6555                 if (hasPositiveValue && ys[baseId]) {
6556                     ys[baseId].forEach(function (v, i) {
6557                         ys[baseId][i] = v > 0 ? v : 0;
6558                     });
6559                 }
6560                 // Compute max
6561                 for (k = 1; k < idsInGroup.length; k++) {
6562                     id = idsInGroup[k];
6563                     if (!ys[id]) {
6564                         continue;
6565                     }
6566                     ys[id].forEach(function (v, i) {
6567                         if ($$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasPositiveValue && +v < 0)) {
6568                             ys[baseId][i] += +v;
6569                         }
6570                     });
6571                 }
6572             }
6573         }
6574         return $$.d3.max(Object.keys(ys).map(function (key) {
6575             return $$.d3.max(ys[key]);
6576         }));
6577     };
6578     ChartInternal.prototype.getYDomain = function (targets, axisId, xDomain) {
6579         var $$ = this,
6580             config = $$.config,
6581             targetsByAxisId = targets.filter(function (t) {
6582             return $$.axis.getId(t.id) === axisId;
6583         }),
6584             yTargets = xDomain ? $$.filterByXDomain(targetsByAxisId, xDomain) : targetsByAxisId,
6585             yMin = axisId === 'y2' ? config.axis_y2_min : config.axis_y_min,
6586             yMax = axisId === 'y2' ? config.axis_y2_max : config.axis_y_max,
6587             yDomainMin = $$.getYDomainMin(yTargets),
6588             yDomainMax = $$.getYDomainMax(yTargets),
6589             domain,
6590             domainLength,
6591             padding,
6592             padding_top,
6593             padding_bottom,
6594             center = axisId === 'y2' ? config.axis_y2_center : config.axis_y_center,
6595             yDomainAbs,
6596             lengths,
6597             diff,
6598             ratio,
6599             isAllPositive,
6600             isAllNegative,
6601             isZeroBased = $$.hasType('bar', yTargets) && config.bar_zerobased || $$.hasType('area', yTargets) && config.area_zerobased,
6602             isInverted = axisId === 'y2' ? config.axis_y2_inverted : config.axis_y_inverted,
6603             showHorizontalDataLabel = $$.hasDataLabel() && config.axis_rotated,
6604             showVerticalDataLabel = $$.hasDataLabel() && !config.axis_rotated;
6605
6606         // MEMO: avoid inverting domain unexpectedly
6607         yDomainMin = isValue(yMin) ? yMin : isValue(yMax) ? yDomainMin < yMax ? yDomainMin : yMax - 10 : yDomainMin;
6608         yDomainMax = isValue(yMax) ? yMax : isValue(yMin) ? yMin < yDomainMax ? yDomainMax : yMin + 10 : yDomainMax;
6609
6610         if (yTargets.length === 0) {
6611             // use current domain if target of axisId is none
6612             return axisId === 'y2' ? $$.y2.domain() : $$.y.domain();
6613         }
6614         if (isNaN(yDomainMin)) {
6615             // set minimum to zero when not number
6616             yDomainMin = 0;
6617         }
6618         if (isNaN(yDomainMax)) {
6619             // set maximum to have same value as yDomainMin
6620             yDomainMax = yDomainMin;
6621         }
6622         if (yDomainMin === yDomainMax) {
6623             yDomainMin < 0 ? yDomainMax = 0 : yDomainMin = 0;
6624         }
6625         isAllPositive = yDomainMin >= 0 && yDomainMax >= 0;
6626         isAllNegative = yDomainMin <= 0 && yDomainMax <= 0;
6627
6628         // Cancel zerobased if axis_*_min / axis_*_max specified
6629         if (isValue(yMin) && isAllPositive || isValue(yMax) && isAllNegative) {
6630             isZeroBased = false;
6631         }
6632
6633         // Bar/Area chart should be 0-based if all positive|negative
6634         if (isZeroBased) {
6635             if (isAllPositive) {
6636                 yDomainMin = 0;
6637             }
6638             if (isAllNegative) {
6639                 yDomainMax = 0;
6640             }
6641         }
6642
6643         domainLength = Math.abs(yDomainMax - yDomainMin);
6644         padding = padding_top = padding_bottom = domainLength * 0.1;
6645
6646         if (typeof center !== 'undefined') {
6647             yDomainAbs = Math.max(Math.abs(yDomainMin), Math.abs(yDomainMax));
6648             yDomainMax = center + yDomainAbs;
6649             yDomainMin = center - yDomainAbs;
6650         }
6651         // add padding for data label
6652         if (showHorizontalDataLabel) {
6653             lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'width');
6654             diff = diffDomain($$.y.range());
6655             ratio = [lengths[0] / diff, lengths[1] / diff];
6656             padding_top += domainLength * (ratio[1] / (1 - ratio[0] - ratio[1]));
6657             padding_bottom += domainLength * (ratio[0] / (1 - ratio[0] - ratio[1]));
6658         } else if (showVerticalDataLabel) {
6659             lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'height');
6660             padding_top += $$.axis.convertPixelsToAxisPadding(lengths[1], domainLength);
6661             padding_bottom += $$.axis.convertPixelsToAxisPadding(lengths[0], domainLength);
6662         }
6663         if (axisId === 'y' && notEmpty(config.axis_y_padding)) {
6664             padding_top = $$.axis.getPadding(config.axis_y_padding, 'top', padding_top, domainLength);
6665             padding_bottom = $$.axis.getPadding(config.axis_y_padding, 'bottom', padding_bottom, domainLength);
6666         }
6667         if (axisId === 'y2' && notEmpty(config.axis_y2_padding)) {
6668             padding_top = $$.axis.getPadding(config.axis_y2_padding, 'top', padding_top, domainLength);
6669             padding_bottom = $$.axis.getPadding(config.axis_y2_padding, 'bottom', padding_bottom, domainLength);
6670         }
6671         // Bar/Area chart should be 0-based if all positive|negative
6672         if (isZeroBased) {
6673             if (isAllPositive) {
6674                 padding_bottom = yDomainMin;
6675             }
6676             if (isAllNegative) {
6677                 padding_top = -yDomainMax;
6678             }
6679         }
6680         domain = [yDomainMin - padding_bottom, yDomainMax + padding_top];
6681         return isInverted ? domain.reverse() : domain;
6682     };
6683     ChartInternal.prototype.getXDomainMin = function (targets) {
6684         var $$ = this,
6685             config = $$.config;
6686         return isDefined(config.axis_x_min) ? $$.isTimeSeries() ? this.parseDate(config.axis_x_min) : config.axis_x_min : $$.d3.min(targets, function (t) {
6687             return $$.d3.min(t.values, function (v) {
6688                 return v.x;
6689             });
6690         });
6691     };
6692     ChartInternal.prototype.getXDomainMax = function (targets) {
6693         var $$ = this,
6694             config = $$.config;
6695         return isDefined(config.axis_x_max) ? $$.isTimeSeries() ? this.parseDate(config.axis_x_max) : config.axis_x_max : $$.d3.max(targets, function (t) {
6696             return $$.d3.max(t.values, function (v) {
6697                 return v.x;
6698             });
6699         });
6700     };
6701     ChartInternal.prototype.getXDomainPadding = function (domain) {
6702         var $$ = this,
6703             config = $$.config,
6704             diff = domain[1] - domain[0],
6705             maxDataCount,
6706             padding,
6707             paddingLeft,
6708             paddingRight;
6709         if ($$.isCategorized()) {
6710             padding = 0;
6711         } else if ($$.hasType('bar')) {
6712             maxDataCount = $$.getMaxDataCount();
6713             padding = maxDataCount > 1 ? diff / (maxDataCount - 1) / 2 : 0.5;
6714         } else {
6715             padding = diff * 0.01;
6716         }
6717         if (_typeof(config.axis_x_padding) === 'object' && notEmpty(config.axis_x_padding)) {
6718             paddingLeft = isValue(config.axis_x_padding.left) ? config.axis_x_padding.left : padding;
6719             paddingRight = isValue(config.axis_x_padding.right) ? config.axis_x_padding.right : padding;
6720         } else if (typeof config.axis_x_padding === 'number') {
6721             paddingLeft = paddingRight = config.axis_x_padding;
6722         } else {
6723             paddingLeft = paddingRight = padding;
6724         }
6725         return { left: paddingLeft, right: paddingRight };
6726     };
6727     ChartInternal.prototype.getXDomain = function (targets) {
6728         var $$ = this,
6729             xDomain = [$$.getXDomainMin(targets), $$.getXDomainMax(targets)],
6730             firstX = xDomain[0],
6731             lastX = xDomain[1],
6732             padding = $$.getXDomainPadding(xDomain),
6733             min = 0,
6734             max = 0;
6735         // show center of x domain if min and max are the same
6736         if (firstX - lastX === 0 && !$$.isCategorized()) {
6737             if ($$.isTimeSeries()) {
6738                 firstX = new Date(firstX.getTime() * 0.5);
6739                 lastX = new Date(lastX.getTime() * 1.5);
6740             } else {
6741                 firstX = firstX === 0 ? 1 : firstX * 0.5;
6742                 lastX = lastX === 0 ? -1 : lastX * 1.5;
6743             }
6744         }
6745         if (firstX || firstX === 0) {
6746             min = $$.isTimeSeries() ? new Date(firstX.getTime() - padding.left) : firstX - padding.left;
6747         }
6748         if (lastX || lastX === 0) {
6749             max = $$.isTimeSeries() ? new Date(lastX.getTime() + padding.right) : lastX + padding.right;
6750         }
6751         return [min, max];
6752     };
6753     ChartInternal.prototype.updateXDomain = function (targets, withUpdateXDomain, withUpdateOrgXDomain, withTrim, domain) {
6754         var $$ = this,
6755             config = $$.config;
6756
6757         if (withUpdateOrgXDomain) {
6758             $$.x.domain(domain ? domain : $$.d3.extent($$.getXDomain(targets)));
6759             $$.orgXDomain = $$.x.domain();
6760             if (config.zoom_enabled) {
6761                 $$.zoom.update();
6762             }
6763             $$.subX.domain($$.x.domain());
6764             if ($$.brush) {
6765                 $$.brush.updateScale($$.subX);
6766             }
6767         }
6768         if (withUpdateXDomain) {
6769             $$.x.domain(domain ? domain : !$$.brush || $$.brush.empty() ? $$.orgXDomain : $$.brush.selectionAsValue());
6770         }
6771
6772         // Trim domain when too big by zoom mousemove event
6773         if (withTrim) {
6774             $$.x.domain($$.trimXDomain($$.x.orgDomain()));
6775         }
6776
6777         return $$.x.domain();
6778     };
6779     ChartInternal.prototype.trimXDomain = function (domain) {
6780         var zoomDomain = this.getZoomDomain(),
6781             min = zoomDomain[0],
6782             max = zoomDomain[1];
6783         if (domain[0] <= min) {
6784             domain[1] = +domain[1] + (min - domain[0]);
6785             domain[0] = min;
6786         }
6787         if (max <= domain[1]) {
6788             domain[0] = +domain[0] - (domain[1] - max);
6789             domain[1] = max;
6790         }
6791         return domain;
6792     };
6793
6794     ChartInternal.prototype.drag = function (mouse) {
6795         var $$ = this,
6796             config = $$.config,
6797             main = $$.main,
6798             d3 = $$.d3;
6799         var sx, sy, mx, my, minX, maxX, minY, maxY;
6800
6801         if ($$.hasArcType()) {
6802             return;
6803         }
6804         if (!config.data_selection_enabled) {
6805             return;
6806         } // do nothing if not selectable
6807         if (!config.data_selection_multiple) {
6808             return;
6809         } // skip when single selection because drag is used for multiple selection
6810
6811         sx = $$.dragStart[0];
6812         sy = $$.dragStart[1];
6813         mx = mouse[0];
6814         my = mouse[1];
6815         minX = Math.min(sx, mx);
6816         maxX = Math.max(sx, mx);
6817         minY = config.data_selection_grouped ? $$.margin.top : Math.min(sy, my);
6818         maxY = config.data_selection_grouped ? $$.height : Math.max(sy, my);
6819
6820         main.select('.' + CLASS.dragarea).attr('x', minX).attr('y', minY).attr('width', maxX - minX).attr('height', maxY - minY);
6821         // TODO: binary search when multiple xs
6822         main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape).filter(function (d) {
6823             return config.data_selection_isselectable(d);
6824         }).each(function (d, i) {
6825             var shape = d3.select(this),
6826                 isSelected = shape.classed(CLASS.SELECTED),
6827                 isIncluded = shape.classed(CLASS.INCLUDED),
6828                 _x,
6829                 _y,
6830                 _w,
6831                 _h,
6832                 toggle,
6833                 isWithin = false,
6834                 box;
6835             if (shape.classed(CLASS.circle)) {
6836                 _x = shape.attr("cx") * 1;
6837                 _y = shape.attr("cy") * 1;
6838                 toggle = $$.togglePoint;
6839                 isWithin = minX < _x && _x < maxX && minY < _y && _y < maxY;
6840             } else if (shape.classed(CLASS.bar)) {
6841                 box = getPathBox(this);
6842                 _x = box.x;
6843                 _y = box.y;
6844                 _w = box.width;
6845                 _h = box.height;
6846                 toggle = $$.togglePath;
6847                 isWithin = !(maxX < _x || _x + _w < minX) && !(maxY < _y || _y + _h < minY);
6848             } else {
6849                 // line/area selection not supported yet
6850                 return;
6851             }
6852             if (isWithin ^ isIncluded) {
6853                 shape.classed(CLASS.INCLUDED, !isIncluded);
6854                 // TODO: included/unincluded callback here
6855                 shape.classed(CLASS.SELECTED, !isSelected);
6856                 toggle.call($$, !isSelected, shape, d, i);
6857             }
6858         });
6859     };
6860
6861     ChartInternal.prototype.dragstart = function (mouse) {
6862         var $$ = this,
6863             config = $$.config;
6864         if ($$.hasArcType()) {
6865             return;
6866         }
6867         if (!config.data_selection_enabled) {
6868             return;
6869         } // do nothing if not selectable
6870         $$.dragStart = mouse;
6871         $$.main.select('.' + CLASS.chart).append('rect').attr('class', CLASS.dragarea).style('opacity', 0.1);
6872         $$.dragging = true;
6873     };
6874
6875     ChartInternal.prototype.dragend = function () {
6876         var $$ = this,
6877             config = $$.config;
6878         if ($$.hasArcType()) {
6879             return;
6880         }
6881         if (!config.data_selection_enabled) {
6882             return;
6883         } // do nothing if not selectable
6884         $$.main.select('.' + CLASS.dragarea).transition().duration(100).style('opacity', 0).remove();
6885         $$.main.selectAll('.' + CLASS.shape).classed(CLASS.INCLUDED, false);
6886         $$.dragging = false;
6887     };
6888
6889     ChartInternal.prototype.getYFormat = function (forArc) {
6890         var $$ = this,
6891             formatForY = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.yFormat,
6892             formatForY2 = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.y2Format;
6893         return function (v, ratio, id) {
6894             var format = $$.axis.getId(id) === 'y2' ? formatForY2 : formatForY;
6895             return format.call($$, v, ratio);
6896         };
6897     };
6898     ChartInternal.prototype.yFormat = function (v) {
6899         var $$ = this,
6900             config = $$.config,
6901             format = config.axis_y_tick_format ? config.axis_y_tick_format : $$.defaultValueFormat;
6902         return format(v);
6903     };
6904     ChartInternal.prototype.y2Format = function (v) {
6905         var $$ = this,
6906             config = $$.config,
6907             format = config.axis_y2_tick_format ? config.axis_y2_tick_format : $$.defaultValueFormat;
6908         return format(v);
6909     };
6910     ChartInternal.prototype.defaultValueFormat = function (v) {
6911         return isValue(v) ? +v : "";
6912     };
6913     ChartInternal.prototype.defaultArcValueFormat = function (v, ratio) {
6914         return (ratio * 100).toFixed(1) + '%';
6915     };
6916     ChartInternal.prototype.dataLabelFormat = function (targetId) {
6917         var $$ = this,
6918             data_labels = $$.config.data_labels,
6919             format,
6920             defaultFormat = function defaultFormat(v) {
6921             return isValue(v) ? +v : "";
6922         };
6923         // find format according to axis id
6924         if (typeof data_labels.format === 'function') {
6925             format = data_labels.format;
6926         } else if (_typeof(data_labels.format) === 'object') {
6927             if (data_labels.format[targetId]) {
6928                 format = data_labels.format[targetId] === true ? defaultFormat : data_labels.format[targetId];
6929             } else {
6930                 format = function format() {
6931                     return '';
6932                 };
6933             }
6934         } else {
6935             format = defaultFormat;
6936         }
6937         return format;
6938     };
6939
6940     ChartInternal.prototype.initGrid = function () {
6941         var $$ = this,
6942             config = $$.config,
6943             d3 = $$.d3;
6944         $$.grid = $$.main.append('g').attr("clip-path", $$.clipPathForGrid).attr('class', CLASS.grid);
6945         if (config.grid_x_show) {
6946             $$.grid.append("g").attr("class", CLASS.xgrids);
6947         }
6948         if (config.grid_y_show) {
6949             $$.grid.append('g').attr('class', CLASS.ygrids);
6950         }
6951         if (config.grid_focus_show) {
6952             $$.grid.append('g').attr("class", CLASS.xgridFocus).append('line').attr('class', CLASS.xgridFocus);
6953         }
6954         $$.xgrid = d3.selectAll([]);
6955         if (!config.grid_lines_front) {
6956             $$.initGridLines();
6957         }
6958     };
6959     ChartInternal.prototype.initGridLines = function () {
6960         var $$ = this,
6961             d3 = $$.d3;
6962         $$.gridLines = $$.main.append('g').attr("clip-path", $$.clipPathForGrid).attr('class', CLASS.grid + ' ' + CLASS.gridLines);
6963         $$.gridLines.append('g').attr("class", CLASS.xgridLines);
6964         $$.gridLines.append('g').attr('class', CLASS.ygridLines);
6965         $$.xgridLines = d3.selectAll([]);
6966     };
6967     ChartInternal.prototype.updateXGrid = function (withoutUpdate) {
6968         var $$ = this,
6969             config = $$.config,
6970             d3 = $$.d3,
6971             xgridData = $$.generateGridData(config.grid_x_type, $$.x),
6972             tickOffset = $$.isCategorized() ? $$.xAxis.tickOffset() : 0;
6973
6974         $$.xgridAttr = config.axis_rotated ? {
6975             'x1': 0,
6976             'x2': $$.width,
6977             'y1': function y1(d) {
6978                 return $$.x(d) - tickOffset;
6979             },
6980             'y2': function y2(d) {
6981                 return $$.x(d) - tickOffset;
6982             }
6983         } : {
6984             'x1': function x1(d) {
6985                 return $$.x(d) + tickOffset;
6986             },
6987             'x2': function x2(d) {
6988                 return $$.x(d) + tickOffset;
6989             },
6990             'y1': 0,
6991             'y2': $$.height
6992         };
6993         $$.xgridAttr.opacity = function () {
6994             var pos = +d3.select(this).attr(config.axis_rotated ? 'y1' : 'x1');
6995             return pos === (config.axis_rotated ? $$.height : 0) ? 0 : 1;
6996         };
6997
6998         var xgrid = $$.main.select('.' + CLASS.xgrids).selectAll('.' + CLASS.xgrid).data(xgridData);
6999         var xgridEnter = xgrid.enter().append('line').attr("class", CLASS.xgrid).attr('x1', $$.xgridAttr.x1).attr('x2', $$.xgridAttr.x2).attr('y1', $$.xgridAttr.y1).attr('y2', $$.xgridAttr.y2).style("opacity", 0);
7000         $$.xgrid = xgridEnter.merge(xgrid);
7001         if (!withoutUpdate) {
7002             $$.xgrid.attr('x1', $$.xgridAttr.x1).attr('x2', $$.xgridAttr.x2).attr('y1', $$.xgridAttr.y1).attr('y2', $$.xgridAttr.y2).style("opacity", $$.xgridAttr.opacity);
7003         }
7004         xgrid.exit().remove();
7005     };
7006
7007     ChartInternal.prototype.updateYGrid = function () {
7008         var $$ = this,
7009             config = $$.config,
7010             gridValues = $$.yAxis.tickValues() || $$.y.ticks(config.grid_y_ticks);
7011         var ygrid = $$.main.select('.' + CLASS.ygrids).selectAll('.' + CLASS.ygrid).data(gridValues);
7012         var ygridEnter = ygrid.enter().append('line')
7013         // TODO: x1, x2, y1, y2, opacity need to be set here maybe
7014         .attr('class', CLASS.ygrid);
7015         $$.ygrid = ygridEnter.merge(ygrid);
7016         $$.ygrid.attr("x1", config.axis_rotated ? $$.y : 0).attr("x2", config.axis_rotated ? $$.y : $$.width).attr("y1", config.axis_rotated ? 0 : $$.y).attr("y2", config.axis_rotated ? $$.height : $$.y);
7017         ygrid.exit().remove();
7018         $$.smoothLines($$.ygrid, 'grid');
7019     };
7020
7021     ChartInternal.prototype.gridTextAnchor = function (d) {
7022         return d.position ? d.position : "end";
7023     };
7024     ChartInternal.prototype.gridTextDx = function (d) {
7025         return d.position === 'start' ? 4 : d.position === 'middle' ? 0 : -4;
7026     };
7027     ChartInternal.prototype.xGridTextX = function (d) {
7028         return d.position === 'start' ? -this.height : d.position === 'middle' ? -this.height / 2 : 0;
7029     };
7030     ChartInternal.prototype.yGridTextX = function (d) {
7031         return d.position === 'start' ? 0 : d.position === 'middle' ? this.width / 2 : this.width;
7032     };
7033     ChartInternal.prototype.updateGrid = function (duration) {
7034         var $$ = this,
7035             main = $$.main,
7036             config = $$.config,
7037             xgridLine,
7038             xgridLineEnter,
7039             ygridLine,
7040             ygridLineEnter,
7041             xv = $$.xv.bind($$),
7042             yv = $$.yv.bind($$),
7043             xGridTextX = $$.xGridTextX.bind($$),
7044             yGridTextX = $$.yGridTextX.bind($$);
7045
7046         // hide if arc type
7047         $$.grid.style('visibility', $$.hasArcType() ? 'hidden' : 'visible');
7048
7049         main.select('line.' + CLASS.xgridFocus).style("visibility", "hidden");
7050         if (config.grid_x_show) {
7051             $$.updateXGrid();
7052         }
7053         xgridLine = main.select('.' + CLASS.xgridLines).selectAll('.' + CLASS.xgridLine).data(config.grid_x_lines);
7054         // enter
7055         xgridLineEnter = xgridLine.enter().append('g').attr("class", function (d) {
7056             return CLASS.xgridLine + (d['class'] ? ' ' + d['class'] : '');
7057         });
7058         xgridLineEnter.append('line').attr("x1", config.axis_rotated ? 0 : xv).attr("x2", config.axis_rotated ? $$.width : xv).attr("y1", config.axis_rotated ? xv : 0).attr("y2", config.axis_rotated ? xv : $$.height).style("opacity", 0);
7059         xgridLineEnter.append('text').attr("text-anchor", $$.gridTextAnchor).attr("transform", config.axis_rotated ? "" : "rotate(-90)").attr("x", config.axis_rotated ? yGridTextX : xGridTextX).attr("y", xv).attr('dx', $$.gridTextDx).attr('dy', -5).style("opacity", 0);
7060         // udpate
7061         $$.xgridLines = xgridLineEnter.merge(xgridLine);
7062         // done in d3.transition() of the end of this function
7063         // exit
7064         xgridLine.exit().transition().duration(duration).style("opacity", 0).remove();
7065
7066         // Y-Grid
7067         if (config.grid_y_show) {
7068             $$.updateYGrid();
7069         }
7070         ygridLine = main.select('.' + CLASS.ygridLines).selectAll('.' + CLASS.ygridLine).data(config.grid_y_lines);
7071         // enter
7072         ygridLineEnter = ygridLine.enter().append('g').attr("class", function (d) {
7073             return CLASS.ygridLine + (d['class'] ? ' ' + d['class'] : '');
7074         });
7075         ygridLineEnter.append('line').attr("x1", config.axis_rotated ? yv : 0).attr("x2", config.axis_rotated ? yv : $$.width).attr("y1", config.axis_rotated ? 0 : yv).attr("y2", config.axis_rotated ? $$.height : yv).style("opacity", 0);
7076         ygridLineEnter.append('text').attr("text-anchor", $$.gridTextAnchor).attr("transform", config.axis_rotated ? "rotate(-90)" : "").attr("x", config.axis_rotated ? xGridTextX : yGridTextX).attr("y", yv).attr('dx', $$.gridTextDx).attr('dy', -5).style("opacity", 0);
7077         // update
7078         $$.ygridLines = ygridLineEnter.merge(ygridLine);
7079         $$.ygridLines.select('line').transition().duration(duration).attr("x1", config.axis_rotated ? yv : 0).attr("x2", config.axis_rotated ? yv : $$.width).attr("y1", config.axis_rotated ? 0 : yv).attr("y2", config.axis_rotated ? $$.height : yv).style("opacity", 1);
7080         $$.ygridLines.select('text').transition().duration(duration).attr("x", config.axis_rotated ? $$.xGridTextX.bind($$) : $$.yGridTextX.bind($$)).attr("y", yv).text(function (d) {
7081             return d.text;
7082         }).style("opacity", 1);
7083         // exit
7084         ygridLine.exit().transition().duration(duration).style("opacity", 0).remove();
7085     };
7086     ChartInternal.prototype.redrawGrid = function (withTransition, transition) {
7087         var $$ = this,
7088             config = $$.config,
7089             xv = $$.xv.bind($$),
7090             lines = $$.xgridLines.select('line'),
7091             texts = $$.xgridLines.select('text');
7092         return [(withTransition ? lines.transition(transition) : lines).attr("x1", config.axis_rotated ? 0 : xv).attr("x2", config.axis_rotated ? $$.width : xv).attr("y1", config.axis_rotated ? xv : 0).attr("y2", config.axis_rotated ? xv : $$.height).style("opacity", 1), (withTransition ? texts.transition(transition) : texts).attr("x", config.axis_rotated ? $$.yGridTextX.bind($$) : $$.xGridTextX.bind($$)).attr("y", xv).text(function (d) {
7093             return d.text;
7094         }).style("opacity", 1)];
7095     };
7096     ChartInternal.prototype.showXGridFocus = function (selectedData) {
7097         var $$ = this,
7098             config = $$.config,
7099             dataToShow = selectedData.filter(function (d) {
7100             return d && isValue(d.value);
7101         }),
7102             focusEl = $$.main.selectAll('line.' + CLASS.xgridFocus),
7103             xx = $$.xx.bind($$);
7104         if (!config.tooltip_show) {
7105             return;
7106         }
7107         // Hide when scatter plot exists
7108         if ($$.hasType('scatter') || $$.hasArcType()) {
7109             return;
7110         }
7111         focusEl.style("visibility", "visible").data([dataToShow[0]]).attr(config.axis_rotated ? 'y1' : 'x1', xx).attr(config.axis_rotated ? 'y2' : 'x2', xx);
7112         $$.smoothLines(focusEl, 'grid');
7113     };
7114     ChartInternal.prototype.hideXGridFocus = function () {
7115         this.main.select('line.' + CLASS.xgridFocus).style("visibility", "hidden");
7116     };
7117     ChartInternal.prototype.updateXgridFocus = function () {
7118         var $$ = this,
7119             config = $$.config;
7120         $$.main.select('line.' + CLASS.xgridFocus).attr("x1", config.axis_rotated ? 0 : -10).attr("x2", config.axis_rotated ? $$.width : -10).attr("y1", config.axis_rotated ? -10 : 0).attr("y2", config.axis_rotated ? -10 : $$.height);
7121     };
7122     ChartInternal.prototype.generateGridData = function (type, scale) {
7123         var $$ = this,
7124             gridData = [],
7125             xDomain,
7126             firstYear,
7127             lastYear,
7128             i,
7129             tickNum = $$.main.select("." + CLASS.axisX).selectAll('.tick').size();
7130         if (type === 'year') {
7131             xDomain = $$.getXDomain();
7132             firstYear = xDomain[0].getFullYear();
7133             lastYear = xDomain[1].getFullYear();
7134             for (i = firstYear; i <= lastYear; i++) {
7135                 gridData.push(new Date(i + '-01-01 00:00:00'));
7136             }
7137         } else {
7138             gridData = scale.ticks(10);
7139             if (gridData.length > tickNum) {
7140                 // use only int
7141                 gridData = gridData.filter(function (d) {
7142                     return ("" + d).indexOf('.') < 0;
7143                 });
7144             }
7145         }
7146         return gridData;
7147     };
7148     ChartInternal.prototype.getGridFilterToRemove = function (params) {
7149         return params ? function (line) {
7150             var found = false;
7151             [].concat(params).forEach(function (param) {
7152                 if ('value' in param && line.value === param.value || 'class' in param && line['class'] === param['class']) {
7153                     found = true;
7154                 }
7155             });
7156             return found;
7157         } : function () {
7158             return true;
7159         };
7160     };
7161     ChartInternal.prototype.removeGridLines = function (params, forX) {
7162         var $$ = this,
7163             config = $$.config,
7164             toRemove = $$.getGridFilterToRemove(params),
7165             toShow = function toShow(line) {
7166             return !toRemove(line);
7167         },
7168             classLines = forX ? CLASS.xgridLines : CLASS.ygridLines,
7169             classLine = forX ? CLASS.xgridLine : CLASS.ygridLine;
7170         $$.main.select('.' + classLines).selectAll('.' + classLine).filter(toRemove).transition().duration(config.transition_duration).style('opacity', 0).remove();
7171         if (forX) {
7172             config.grid_x_lines = config.grid_x_lines.filter(toShow);
7173         } else {
7174             config.grid_y_lines = config.grid_y_lines.filter(toShow);
7175         }
7176     };
7177
7178     ChartInternal.prototype.initEventRect = function () {
7179         var $$ = this,
7180             config = $$.config;
7181
7182         $$.main.select('.' + CLASS.chart).append("g").attr("class", CLASS.eventRects).style('fill-opacity', 0);
7183         $$.eventRect = $$.main.select('.' + CLASS.eventRects).append('rect').attr('class', CLASS.eventRect);
7184
7185         // event rect handle zoom event as well
7186         if (config.zoom_enabled && $$.zoom) {
7187             $$.eventRect.call($$.zoom).on("dblclick.zoom", null);
7188             if (config.zoom_initialRange) {
7189                 // WORKAROUND: Add transition to apply transform immediately when no subchart
7190                 $$.eventRect.transition().duration(0).call($$.zoom.transform, $$.zoomTransform(config.zoom_initialRange));
7191             }
7192         }
7193     };
7194     ChartInternal.prototype.redrawEventRect = function () {
7195         var $$ = this,
7196             d3 = $$.d3,
7197             config = $$.config,
7198             x,
7199             y,
7200             w,
7201             h;
7202
7203         // TODO: rotated not supported yet
7204         x = 0;
7205         y = 0;
7206         w = $$.width;
7207         h = $$.height;
7208
7209         function mouseout() {
7210             $$.svg.select('.' + CLASS.eventRect).style('cursor', null);
7211             $$.hideXGridFocus();
7212             $$.hideTooltip();
7213             $$.unexpandCircles();
7214             $$.unexpandBars();
7215         }
7216
7217         // rects for mouseover
7218         $$.main.select('.' + CLASS.eventRects).style('cursor', config.zoom_enabled ? config.axis_rotated ? 'ns-resize' : 'ew-resize' : null);
7219
7220         $$.eventRect.attr('x', x).attr('y', y).attr('width', w).attr('height', h).on('mouseout', config.interaction_enabled ? function () {
7221             if (!config) {
7222                 return;
7223             } // chart is destroyed
7224             if ($$.hasArcType()) {
7225                 return;
7226             }
7227             mouseout();
7228         } : null).on('mousemove', config.interaction_enabled ? function () {
7229             var targetsToShow, mouse, closest, sameXData, selectedData;
7230
7231             if ($$.dragging) {
7232                 return;
7233             } // do nothing when dragging
7234             if ($$.hasArcType(targetsToShow)) {
7235                 return;
7236             }
7237
7238             targetsToShow = $$.filterTargetsToShow($$.data.targets);
7239             mouse = d3.mouse(this);
7240             closest = $$.findClosestFromTargets(targetsToShow, mouse);
7241
7242             if ($$.mouseover && (!closest || closest.id !== $$.mouseover.id)) {
7243                 config.data_onmouseout.call($$.api, $$.mouseover);
7244                 $$.mouseover = undefined;
7245             }
7246
7247             if (!closest) {
7248                 mouseout();
7249                 return;
7250             }
7251
7252             if ($$.isScatterType(closest) || !config.tooltip_grouped) {
7253                 sameXData = [closest];
7254             } else {
7255                 sameXData = $$.filterByX(targetsToShow, closest.x);
7256             }
7257
7258             // show tooltip when cursor is close to some point
7259             selectedData = sameXData.map(function (d) {
7260                 return $$.addName(d);
7261             });
7262             $$.showTooltip(selectedData, this);
7263
7264             // expand points
7265             if (config.point_focus_expand_enabled) {
7266                 $$.unexpandCircles();
7267                 selectedData.forEach(function (d) {
7268                     $$.expandCircles(d.index, d.id, false);
7269                 });
7270             }
7271             $$.expandBars(closest.index, closest.id, true);
7272
7273             // Show xgrid focus line
7274             $$.showXGridFocus(selectedData);
7275
7276             // Show cursor as pointer if point is close to mouse position
7277             if ($$.isBarType(closest.id) || $$.dist(closest, mouse) < config.point_sensitivity) {
7278                 $$.svg.select('.' + CLASS.eventRect).style('cursor', 'pointer');
7279                 if (!$$.mouseover) {
7280                     config.data_onmouseover.call($$.api, closest);
7281                     $$.mouseover = closest;
7282                 }
7283             }
7284         } : null).on('click', config.interaction_enabled ? function () {
7285             var targetsToShow, mouse, closest, sameXData;
7286             if ($$.hasArcType(targetsToShow)) {
7287                 return;
7288             }
7289
7290             targetsToShow = $$.filterTargetsToShow($$.data.targets);
7291             mouse = d3.mouse(this);
7292             closest = $$.findClosestFromTargets(targetsToShow, mouse);
7293             if (!closest) {
7294                 return;
7295             }
7296             // select if selection enabled
7297             if ($$.isBarType(closest.id) || $$.dist(closest, mouse) < config.point_sensitivity) {
7298                 if ($$.isScatterType(closest) || !config.data_selection_grouped) {
7299                     sameXData = [closest];
7300                 } else {
7301                     sameXData = $$.filterByX(targetsToShow, closest.x);
7302                 }
7303                 sameXData.forEach(function (d) {
7304                     $$.main.selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(d.id)).selectAll('.' + CLASS.shape + '-' + d.index).each(function () {
7305                         if (config.data_selection_grouped || $$.isWithinShape(this, d)) {
7306                             $$.toggleShape(this, d, d.index);
7307                             config.data_onclick.call($$.api, d, this);
7308                         }
7309                     });
7310                 });
7311             }
7312         } : null).call(config.interaction_enabled && config.data_selection_draggable && $$.drag ? d3.drag().on('drag', function () {
7313             $$.drag(d3.mouse(this));
7314         }).on('start', function () {
7315             $$.dragstart(d3.mouse(this));
7316         }).on('end', function () {
7317             $$.dragend();
7318         }) : function () {});
7319     };
7320     ChartInternal.prototype.getMousePosition = function (data) {
7321         var $$ = this;
7322         return [$$.x(data.x), $$.getYScale(data.id)(data.value)];
7323     };
7324     ChartInternal.prototype.dispatchEvent = function (type, mouse) {
7325         var $$ = this,
7326             selector = '.' + CLASS.eventRect,
7327             eventRect = $$.main.select(selector).node(),
7328             box = eventRect.getBoundingClientRect(),
7329             x = box.left + (mouse ? mouse[0] : 0),
7330             y = box.top + (mouse ? mouse[1] : 0),
7331             event = document.createEvent("MouseEvents");
7332
7333         event.initMouseEvent(type, true, true, window, 0, x, y, x, y, false, false, false, false, 0, null);
7334         eventRect.dispatchEvent(event);
7335     };
7336
7337     ChartInternal.prototype.initLegend = function () {
7338         var $$ = this;
7339         $$.legendItemTextBox = {};
7340         $$.legendHasRendered = false;
7341         $$.legend = $$.svg.append("g").attr("transform", $$.getTranslate('legend'));
7342         if (!$$.config.legend_show) {
7343             $$.legend.style('visibility', 'hidden');
7344             $$.hiddenLegendIds = $$.mapToIds($$.data.targets);
7345             return;
7346         }
7347         // MEMO: call here to update legend box and tranlate for all
7348         // MEMO: translate will be upated by this, so transform not needed in updateLegend()
7349         $$.updateLegendWithDefaults();
7350     };
7351     ChartInternal.prototype.updateLegendWithDefaults = function () {
7352         var $$ = this;
7353         $$.updateLegend($$.mapToIds($$.data.targets), { withTransform: false, withTransitionForTransform: false, withTransition: false });
7354     };
7355     ChartInternal.prototype.updateSizeForLegend = function (legendHeight, legendWidth) {
7356         var $$ = this,
7357             config = $$.config,
7358             insetLegendPosition = {
7359             top: $$.isLegendTop ? $$.getCurrentPaddingTop() + config.legend_inset_y + 5.5 : $$.currentHeight - legendHeight - $$.getCurrentPaddingBottom() - config.legend_inset_y,
7360             left: $$.isLegendLeft ? $$.getCurrentPaddingLeft() + config.legend_inset_x + 0.5 : $$.currentWidth - legendWidth - $$.getCurrentPaddingRight() - config.legend_inset_x + 0.5
7361         };
7362
7363         $$.margin3 = {
7364             top: $$.isLegendRight ? 0 : $$.isLegendInset ? insetLegendPosition.top : $$.currentHeight - legendHeight,
7365             right: NaN,
7366             bottom: 0,
7367             left: $$.isLegendRight ? $$.currentWidth - legendWidth : $$.isLegendInset ? insetLegendPosition.left : 0
7368         };
7369     };
7370     ChartInternal.prototype.transformLegend = function (withTransition) {
7371         var $$ = this;
7372         (withTransition ? $$.legend.transition() : $$.legend).attr("transform", $$.getTranslate('legend'));
7373     };
7374     ChartInternal.prototype.updateLegendStep = function (step) {
7375         this.legendStep = step;
7376     };
7377     ChartInternal.prototype.updateLegendItemWidth = function (w) {
7378         this.legendItemWidth = w;
7379     };
7380     ChartInternal.prototype.updateLegendItemHeight = function (h) {
7381         this.legendItemHeight = h;
7382     };
7383     ChartInternal.prototype.getLegendWidth = function () {
7384         var $$ = this;
7385         return $$.config.legend_show ? $$.isLegendRight || $$.isLegendInset ? $$.legendItemWidth * ($$.legendStep + 1) : $$.currentWidth : 0;
7386     };
7387     ChartInternal.prototype.getLegendHeight = function () {
7388         var $$ = this,
7389             h = 0;
7390         if ($$.config.legend_show) {
7391             if ($$.isLegendRight) {
7392                 h = $$.currentHeight;
7393             } else {
7394                 h = Math.max(20, $$.legendItemHeight) * ($$.legendStep + 1);
7395             }
7396         }
7397         return h;
7398     };
7399     ChartInternal.prototype.opacityForLegend = function (legendItem) {
7400         return legendItem.classed(CLASS.legendItemHidden) ? null : 1;
7401     };
7402     ChartInternal.prototype.opacityForUnfocusedLegend = function (legendItem) {
7403         return legendItem.classed(CLASS.legendItemHidden) ? null : 0.3;
7404     };
7405     ChartInternal.prototype.toggleFocusLegend = function (targetIds, focus) {
7406         var $$ = this;
7407         targetIds = $$.mapToTargetIds(targetIds);
7408         $$.legend.selectAll('.' + CLASS.legendItem).filter(function (id) {
7409             return targetIds.indexOf(id) >= 0;
7410         }).classed(CLASS.legendItemFocused, focus).transition().duration(100).style('opacity', function () {
7411             var opacity = focus ? $$.opacityForLegend : $$.opacityForUnfocusedLegend;
7412             return opacity.call($$, $$.d3.select(this));
7413         });
7414     };
7415     ChartInternal.prototype.revertLegend = function () {
7416         var $$ = this,
7417             d3 = $$.d3;
7418         $$.legend.selectAll('.' + CLASS.legendItem).classed(CLASS.legendItemFocused, false).transition().duration(100).style('opacity', function () {
7419             return $$.opacityForLegend(d3.select(this));
7420         });
7421     };
7422     ChartInternal.prototype.showLegend = function (targetIds) {
7423         var $$ = this,
7424             config = $$.config;
7425         if (!config.legend_show) {
7426             config.legend_show = true;
7427             $$.legend.style('visibility', 'visible');
7428             if (!$$.legendHasRendered) {
7429                 $$.updateLegendWithDefaults();
7430             }
7431         }
7432         $$.removeHiddenLegendIds(targetIds);
7433         $$.legend.selectAll($$.selectorLegends(targetIds)).style('visibility', 'visible').transition().style('opacity', function () {
7434             return $$.opacityForLegend($$.d3.select(this));
7435         });
7436     };
7437     ChartInternal.prototype.hideLegend = function (targetIds) {
7438         var $$ = this,
7439             config = $$.config;
7440         if (config.legend_show && isEmpty(targetIds)) {
7441             config.legend_show = false;
7442             $$.legend.style('visibility', 'hidden');
7443         }
7444         $$.addHiddenLegendIds(targetIds);
7445         $$.legend.selectAll($$.selectorLegends(targetIds)).style('opacity', 0).style('visibility', 'hidden');
7446     };
7447     ChartInternal.prototype.clearLegendItemTextBoxCache = function () {
7448         this.legendItemTextBox = {};
7449     };
7450     ChartInternal.prototype.updateLegend = function (targetIds, options, transitions) {
7451         var $$ = this,
7452             config = $$.config;
7453         var xForLegend, xForLegendText, xForLegendRect, yForLegend, yForLegendText, yForLegendRect, x1ForLegendTile, x2ForLegendTile, yForLegendTile;
7454         var paddingTop = 4,
7455             paddingRight = 10,
7456             maxWidth = 0,
7457             maxHeight = 0,
7458             posMin = 10,
7459             tileWidth = config.legend_item_tile_width + 5;
7460         var l,
7461             totalLength = 0,
7462             offsets = {},
7463             widths = {},
7464             heights = {},
7465             margins = [0],
7466             steps = {},
7467             step = 0;
7468         var withTransition, withTransitionForTransform;
7469         var texts, rects, tiles, background;
7470
7471         // Skip elements when their name is set to null
7472         targetIds = targetIds.filter(function (id) {
7473             return !isDefined(config.data_names[id]) || config.data_names[id] !== null;
7474         });
7475
7476         options = options || {};
7477         withTransition = getOption(options, "withTransition", true);
7478         withTransitionForTransform = getOption(options, "withTransitionForTransform", true);
7479
7480         function getTextBox(textElement, id) {
7481             if (!$$.legendItemTextBox[id]) {
7482                 $$.legendItemTextBox[id] = $$.getTextRect(textElement.textContent, CLASS.legendItem, textElement);
7483             }
7484             return $$.legendItemTextBox[id];
7485         }
7486
7487         function updatePositions(textElement, id, index) {
7488             var reset = index === 0,
7489                 isLast = index === targetIds.length - 1,
7490                 box = getTextBox(textElement, id),
7491                 itemWidth = box.width + tileWidth + (isLast && !($$.isLegendRight || $$.isLegendInset) ? 0 : paddingRight) + config.legend_padding,
7492                 itemHeight = box.height + paddingTop,
7493                 itemLength = $$.isLegendRight || $$.isLegendInset ? itemHeight : itemWidth,
7494                 areaLength = $$.isLegendRight || $$.isLegendInset ? $$.getLegendHeight() : $$.getLegendWidth(),
7495                 margin,
7496                 maxLength;
7497
7498             // MEMO: care about condifion of step, totalLength
7499             function updateValues(id, withoutStep) {
7500                 if (!withoutStep) {
7501                     margin = (areaLength - totalLength - itemLength) / 2;
7502                     if (margin < posMin) {
7503                         margin = (areaLength - itemLength) / 2;
7504                         totalLength = 0;
7505                         step++;
7506                     }
7507                 }
7508                 steps[id] = step;
7509                 margins[step] = $$.isLegendInset ? 10 : margin;
7510                 offsets[id] = totalLength;
7511                 totalLength += itemLength;
7512             }
7513
7514             if (reset) {
7515                 totalLength = 0;
7516                 step = 0;
7517                 maxWidth = 0;
7518                 maxHeight = 0;
7519             }
7520
7521             if (config.legend_show && !$$.isLegendToShow(id)) {
7522                 widths[id] = heights[id] = steps[id] = offsets[id] = 0;
7523                 return;
7524             }
7525
7526             widths[id] = itemWidth;
7527             heights[id] = itemHeight;
7528
7529             if (!maxWidth || itemWidth >= maxWidth) {
7530                 maxWidth = itemWidth;
7531             }
7532             if (!maxHeight || itemHeight >= maxHeight) {
7533                 maxHeight = itemHeight;
7534             }
7535             maxLength = $$.isLegendRight || $$.isLegendInset ? maxHeight : maxWidth;
7536
7537             if (config.legend_equally) {
7538                 Object.keys(widths).forEach(function (id) {
7539                     widths[id] = maxWidth;
7540                 });
7541                 Object.keys(heights).forEach(function (id) {
7542                     heights[id] = maxHeight;
7543                 });
7544                 margin = (areaLength - maxLength * targetIds.length) / 2;
7545                 if (margin < posMin) {
7546                     totalLength = 0;
7547                     step = 0;
7548                     targetIds.forEach(function (id) {
7549                         updateValues(id);
7550                     });
7551                 } else {
7552                     updateValues(id, true);
7553                 }
7554             } else {
7555                 updateValues(id);
7556             }
7557         }
7558
7559         if ($$.isLegendInset) {
7560             step = config.legend_inset_step ? config.legend_inset_step : targetIds.length;
7561             $$.updateLegendStep(step);
7562         }
7563
7564         if ($$.isLegendRight) {
7565             xForLegend = function xForLegend(id) {
7566                 return maxWidth * steps[id];
7567             };
7568             yForLegend = function yForLegend(id) {
7569                 return margins[steps[id]] + offsets[id];
7570             };
7571         } else if ($$.isLegendInset) {
7572             xForLegend = function xForLegend(id) {
7573                 return maxWidth * steps[id] + 10;
7574             };
7575             yForLegend = function yForLegend(id) {
7576                 return margins[steps[id]] + offsets[id];
7577             };
7578         } else {
7579             xForLegend = function xForLegend(id) {
7580                 return margins[steps[id]] + offsets[id];
7581             };
7582             yForLegend = function yForLegend(id) {
7583                 return maxHeight * steps[id];
7584             };
7585         }
7586         xForLegendText = function xForLegendText(id, i) {
7587             return xForLegend(id, i) + 4 + config.legend_item_tile_width;
7588         };
7589         yForLegendText = function yForLegendText(id, i) {
7590             return yForLegend(id, i) + 9;
7591         };
7592         xForLegendRect = function xForLegendRect(id, i) {
7593             return xForLegend(id, i);
7594         };
7595         yForLegendRect = function yForLegendRect(id, i) {
7596             return yForLegend(id, i) - 5;
7597         };
7598         x1ForLegendTile = function x1ForLegendTile(id, i) {
7599             return xForLegend(id, i) - 2;
7600         };
7601         x2ForLegendTile = function x2ForLegendTile(id, i) {
7602             return xForLegend(id, i) - 2 + config.legend_item_tile_width;
7603         };
7604         yForLegendTile = function yForLegendTile(id, i) {
7605             return yForLegend(id, i) + 4;
7606         };
7607
7608         // Define g for legend area
7609         l = $$.legend.selectAll('.' + CLASS.legendItem).data(targetIds).enter().append('g').attr('class', function (id) {
7610             return $$.generateClass(CLASS.legendItem, id);
7611         }).style('visibility', function (id) {
7612             return $$.isLegendToShow(id) ? 'visible' : 'hidden';
7613         }).style('cursor', 'pointer').on('click', function (id) {
7614             if (config.legend_item_onclick) {
7615                 config.legend_item_onclick.call($$, id);
7616             } else {
7617                 if ($$.d3.event.altKey) {
7618                     $$.api.hide();
7619                     $$.api.show(id);
7620                 } else {
7621                     $$.api.toggle(id);
7622                     $$.isTargetToShow(id) ? $$.api.focus(id) : $$.api.revert();
7623                 }
7624             }
7625         }).on('mouseover', function (id) {
7626             if (config.legend_item_onmouseover) {
7627                 config.legend_item_onmouseover.call($$, id);
7628             } else {
7629                 $$.d3.select(this).classed(CLASS.legendItemFocused, true);
7630                 if (!$$.transiting && $$.isTargetToShow(id)) {
7631                     $$.api.focus(id);
7632                 }
7633             }
7634         }).on('mouseout', function (id) {
7635             if (config.legend_item_onmouseout) {
7636                 config.legend_item_onmouseout.call($$, id);
7637             } else {
7638                 $$.d3.select(this).classed(CLASS.legendItemFocused, false);
7639                 $$.api.revert();
7640             }
7641         });
7642         l.append('text').text(function (id) {
7643             return isDefined(config.data_names[id]) ? config.data_names[id] : id;
7644         }).each(function (id, i) {
7645             updatePositions(this, id, i);
7646         }).style("pointer-events", "none").attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendText : -200).attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendText);
7647         l.append('rect').attr("class", CLASS.legendItemEvent).style('fill-opacity', 0).attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendRect : -200).attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendRect);
7648         l.append('line').attr('class', CLASS.legendItemTile).style('stroke', $$.color).style("pointer-events", "none").attr('x1', $$.isLegendRight || $$.isLegendInset ? x1ForLegendTile : -200).attr('y1', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendTile).attr('x2', $$.isLegendRight || $$.isLegendInset ? x2ForLegendTile : -200).attr('y2', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendTile).attr('stroke-width', config.legend_item_tile_height);
7649
7650         // Set background for inset legend
7651         background = $$.legend.select('.' + CLASS.legendBackground + ' rect');
7652         if ($$.isLegendInset && maxWidth > 0 && background.size() === 0) {
7653             background = $$.legend.insert('g', '.' + CLASS.legendItem).attr("class", CLASS.legendBackground).append('rect');
7654         }
7655
7656         texts = $$.legend.selectAll('text').data(targetIds).text(function (id) {
7657             return isDefined(config.data_names[id]) ? config.data_names[id] : id;
7658         }) // MEMO: needed for update
7659         .each(function (id, i) {
7660             updatePositions(this, id, i);
7661         });
7662         (withTransition ? texts.transition() : texts).attr('x', xForLegendText).attr('y', yForLegendText);
7663
7664         rects = $$.legend.selectAll('rect.' + CLASS.legendItemEvent).data(targetIds);
7665         (withTransition ? rects.transition() : rects).attr('width', function (id) {
7666             return widths[id];
7667         }).attr('height', function (id) {
7668             return heights[id];
7669         }).attr('x', xForLegendRect).attr('y', yForLegendRect);
7670
7671         tiles = $$.legend.selectAll('line.' + CLASS.legendItemTile).data(targetIds);
7672         (withTransition ? tiles.transition() : tiles).style('stroke', $$.levelColor ? function (id) {
7673             return $$.levelColor($$.cache[id].values[0].value);
7674         } : $$.color).attr('x1', x1ForLegendTile).attr('y1', yForLegendTile).attr('x2', x2ForLegendTile).attr('y2', yForLegendTile);
7675
7676         if (background) {
7677             (withTransition ? background.transition() : background).attr('height', $$.getLegendHeight() - 12).attr('width', maxWidth * (step + 1) + 10);
7678         }
7679
7680         // toggle legend state
7681         $$.legend.selectAll('.' + CLASS.legendItem).classed(CLASS.legendItemHidden, function (id) {
7682             return !$$.isTargetToShow(id);
7683         });
7684
7685         // Update all to reflect change of legend
7686         $$.updateLegendItemWidth(maxWidth);
7687         $$.updateLegendItemHeight(maxHeight);
7688         $$.updateLegendStep(step);
7689         // Update size and scale
7690         $$.updateSizes();
7691         $$.updateScales();
7692         $$.updateSvgSize();
7693         // Update g positions
7694         $$.transformAll(withTransitionForTransform, transitions);
7695         $$.legendHasRendered = true;
7696     };
7697
7698     ChartInternal.prototype.initRegion = function () {
7699         var $$ = this;
7700         $$.region = $$.main.append('g').attr("clip-path", $$.clipPath).attr("class", CLASS.regions);
7701     };
7702     ChartInternal.prototype.updateRegion = function (duration) {
7703         var $$ = this,
7704             config = $$.config;
7705
7706         // hide if arc type
7707         $$.region.style('visibility', $$.hasArcType() ? 'hidden' : 'visible');
7708
7709         var mainRegion = $$.main.select('.' + CLASS.regions).selectAll('.' + CLASS.region).data(config.regions);
7710         var mainRegionEnter = mainRegion.enter().append('rect').attr("x", $$.regionX.bind($$)).attr("y", $$.regionY.bind($$)).attr("width", $$.regionWidth.bind($$)).attr("height", $$.regionHeight.bind($$)).style("fill-opacity", 0);
7711         $$.mainRegion = mainRegionEnter.merge(mainRegion).attr('class', $$.classRegion.bind($$));
7712         mainRegion.exit().transition().duration(duration).style("opacity", 0).remove();
7713     };
7714     ChartInternal.prototype.redrawRegion = function (withTransition, transition) {
7715         var $$ = this,
7716             regions = $$.mainRegion;
7717         return [(withTransition ? regions.transition(transition) : regions).attr("x", $$.regionX.bind($$)).attr("y", $$.regionY.bind($$)).attr("width", $$.regionWidth.bind($$)).attr("height", $$.regionHeight.bind($$)).style("fill-opacity", function (d) {
7718             return isValue(d.opacity) ? d.opacity : 0.1;
7719         })];
7720     };
7721     ChartInternal.prototype.regionX = function (d) {
7722         var $$ = this,
7723             config = $$.config,
7724             xPos,
7725             yScale = d.axis === 'y' ? $$.y : $$.y2;
7726         if (d.axis === 'y' || d.axis === 'y2') {
7727             xPos = config.axis_rotated ? 'start' in d ? yScale(d.start) : 0 : 0;
7728         } else {
7729             xPos = config.axis_rotated ? 0 : 'start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0;
7730         }
7731         return xPos;
7732     };
7733     ChartInternal.prototype.regionY = function (d) {
7734         var $$ = this,
7735             config = $$.config,
7736             yPos,
7737             yScale = d.axis === 'y' ? $$.y : $$.y2;
7738         if (d.axis === 'y' || d.axis === 'y2') {
7739             yPos = config.axis_rotated ? 0 : 'end' in d ? yScale(d.end) : 0;
7740         } else {
7741             yPos = config.axis_rotated ? 'start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0 : 0;
7742         }
7743         return yPos;
7744     };
7745     ChartInternal.prototype.regionWidth = function (d) {
7746         var $$ = this,
7747             config = $$.config,
7748             start = $$.regionX(d),
7749             end,
7750             yScale = d.axis === 'y' ? $$.y : $$.y2;
7751         if (d.axis === 'y' || d.axis === 'y2') {
7752             end = config.axis_rotated ? 'end' in d ? yScale(d.end) : $$.width : $$.width;
7753         } else {
7754             end = config.axis_rotated ? $$.width : 'end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.width;
7755         }
7756         return end < start ? 0 : end - start;
7757     };
7758     ChartInternal.prototype.regionHeight = function (d) {
7759         var $$ = this,
7760             config = $$.config,
7761             start = this.regionY(d),
7762             end,
7763             yScale = d.axis === 'y' ? $$.y : $$.y2;
7764         if (d.axis === 'y' || d.axis === 'y2') {
7765             end = config.axis_rotated ? $$.height : 'start' in d ? yScale(d.start) : $$.height;
7766         } else {
7767             end = config.axis_rotated ? 'end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.height : $$.height;
7768         }
7769         return end < start ? 0 : end - start;
7770     };
7771     ChartInternal.prototype.isRegionOnX = function (d) {
7772         return !d.axis || d.axis === 'x';
7773     };
7774
7775     ChartInternal.prototype.getScale = function (min, max, forTimeseries) {
7776         return (forTimeseries ? this.d3.scaleTime() : this.d3.scaleLinear()).range([min, max]);
7777     };
7778     ChartInternal.prototype.getX = function (min, max, domain, offset) {
7779         var $$ = this,
7780             scale = $$.getScale(min, max, $$.isTimeSeries()),
7781             _scale = domain ? scale.domain(domain) : scale,
7782             key;
7783         // Define customized scale if categorized axis
7784         if ($$.isCategorized()) {
7785             offset = offset || function () {
7786                 return 0;
7787             };
7788             scale = function scale(d, raw) {
7789                 var v = _scale(d) + offset(d);
7790                 return raw ? v : Math.ceil(v);
7791             };
7792         } else {
7793             scale = function scale(d, raw) {
7794                 var v = _scale(d);
7795                 return raw ? v : Math.ceil(v);
7796             };
7797         }
7798         // define functions
7799         for (key in _scale) {
7800             scale[key] = _scale[key];
7801         }
7802         scale.orgDomain = function () {
7803             return _scale.domain();
7804         };
7805         // define custom domain() for categorized axis
7806         if ($$.isCategorized()) {
7807             scale.domain = function (domain) {
7808                 if (!arguments.length) {
7809                     domain = this.orgDomain();
7810                     return [domain[0], domain[1] + 1];
7811                 }
7812                 _scale.domain(domain);
7813                 return scale;
7814             };
7815         }
7816         return scale;
7817     };
7818     ChartInternal.prototype.getY = function (min, max, domain) {
7819         var scale = this.getScale(min, max, this.isTimeSeriesY());
7820         if (domain) {
7821             scale.domain(domain);
7822         }
7823         return scale;
7824     };
7825     ChartInternal.prototype.getYScale = function (id) {
7826         return this.axis.getId(id) === 'y2' ? this.y2 : this.y;
7827     };
7828     ChartInternal.prototype.getSubYScale = function (id) {
7829         return this.axis.getId(id) === 'y2' ? this.subY2 : this.subY;
7830     };
7831     ChartInternal.prototype.updateScales = function () {
7832         var $$ = this,
7833             config = $$.config,
7834             forInit = !$$.x;
7835         // update edges
7836         $$.xMin = config.axis_rotated ? 1 : 0;
7837         $$.xMax = config.axis_rotated ? $$.height : $$.width;
7838         $$.yMin = config.axis_rotated ? 0 : $$.height;
7839         $$.yMax = config.axis_rotated ? $$.width : 1;
7840         $$.subXMin = $$.xMin;
7841         $$.subXMax = $$.xMax;
7842         $$.subYMin = config.axis_rotated ? 0 : $$.height2;
7843         $$.subYMax = config.axis_rotated ? $$.width2 : 1;
7844         // update scales
7845         $$.x = $$.getX($$.xMin, $$.xMax, forInit ? undefined : $$.x.orgDomain(), function () {
7846             return $$.xAxis.tickOffset();
7847         });
7848         $$.y = $$.getY($$.yMin, $$.yMax, forInit ? config.axis_y_default : $$.y.domain());
7849         $$.y2 = $$.getY($$.yMin, $$.yMax, forInit ? config.axis_y2_default : $$.y2.domain());
7850         $$.subX = $$.getX($$.xMin, $$.xMax, $$.orgXDomain, function (d) {
7851             return d % 1 ? 0 : $$.subXAxis.tickOffset();
7852         });
7853         $$.subY = $$.getY($$.subYMin, $$.subYMax, forInit ? config.axis_y_default : $$.subY.domain());
7854         $$.subY2 = $$.getY($$.subYMin, $$.subYMax, forInit ? config.axis_y2_default : $$.subY2.domain());
7855         // update axes
7856         $$.xAxisTickFormat = $$.axis.getXAxisTickFormat();
7857         $$.xAxisTickValues = $$.axis.getXAxisTickValues();
7858         $$.yAxisTickValues = $$.axis.getYAxisTickValues();
7859         $$.y2AxisTickValues = $$.axis.getY2AxisTickValues();
7860
7861         $$.xAxis = $$.axis.getXAxis($$.x, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer);
7862         $$.subXAxis = $$.axis.getXAxis($$.subX, $$.subXOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer);
7863         $$.yAxis = $$.axis.getYAxis($$.y, $$.yOrient, config.axis_y_tick_format, $$.yAxisTickValues, config.axis_y_tick_outer);
7864         $$.y2Axis = $$.axis.getYAxis($$.y2, $$.y2Orient, config.axis_y2_tick_format, $$.y2AxisTickValues, config.axis_y2_tick_outer);
7865
7866         // Set initialized scales to brush and zoom
7867         if (!forInit) {
7868             if ($$.brush) {
7869                 $$.brush.updateScale($$.subX);
7870             }
7871         }
7872         // update for arc
7873         if ($$.updateArc) {
7874             $$.updateArc();
7875         }
7876     };
7877
7878     ChartInternal.prototype.selectPoint = function (target, d, i) {
7879         var $$ = this,
7880             config = $$.config,
7881             cx = (config.axis_rotated ? $$.circleY : $$.circleX).bind($$),
7882             cy = (config.axis_rotated ? $$.circleX : $$.circleY).bind($$),
7883             r = $$.pointSelectR.bind($$);
7884         config.data_onselected.call($$.api, d, target.node());
7885         // add selected-circle on low layer g
7886         $$.main.select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)).selectAll('.' + CLASS.selectedCircle + '-' + i).data([d]).enter().append('circle').attr("class", function () {
7887             return $$.generateClass(CLASS.selectedCircle, i);
7888         }).attr("cx", cx).attr("cy", cy).attr("stroke", function () {
7889             return $$.color(d);
7890         }).attr("r", function (d) {
7891             return $$.pointSelectR(d) * 1.4;
7892         }).transition().duration(100).attr("r", r);
7893     };
7894     ChartInternal.prototype.unselectPoint = function (target, d, i) {
7895         var $$ = this;
7896         $$.config.data_onunselected.call($$.api, d, target.node());
7897         // remove selected-circle from low layer g
7898         $$.main.select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)).selectAll('.' + CLASS.selectedCircle + '-' + i).transition().duration(100).attr('r', 0).remove();
7899     };
7900     ChartInternal.prototype.togglePoint = function (selected, target, d, i) {
7901         selected ? this.selectPoint(target, d, i) : this.unselectPoint(target, d, i);
7902     };
7903     ChartInternal.prototype.selectPath = function (target, d) {
7904         var $$ = this;
7905         $$.config.data_onselected.call($$, d, target.node());
7906         if ($$.config.interaction_brighten) {
7907             target.transition().duration(100).style("fill", function () {
7908                 return $$.d3.rgb($$.color(d)).brighter(0.75);
7909             });
7910         }
7911     };
7912     ChartInternal.prototype.unselectPath = function (target, d) {
7913         var $$ = this;
7914         $$.config.data_onunselected.call($$, d, target.node());
7915         if ($$.config.interaction_brighten) {
7916             target.transition().duration(100).style("fill", function () {
7917                 return $$.color(d);
7918             });
7919         }
7920     };
7921     ChartInternal.prototype.togglePath = function (selected, target, d, i) {
7922         selected ? this.selectPath(target, d, i) : this.unselectPath(target, d, i);
7923     };
7924     ChartInternal.prototype.getToggle = function (that, d) {
7925         var $$ = this,
7926             toggle;
7927         if (that.nodeName === 'circle') {
7928             if ($$.isStepType(d)) {
7929                 // circle is hidden in step chart, so treat as within the click area
7930                 toggle = function toggle() {}; // TODO: how to select step chart?
7931             } else {
7932                 toggle = $$.togglePoint;
7933             }
7934         } else if (that.nodeName === 'path') {
7935             toggle = $$.togglePath;
7936         }
7937         return toggle;
7938     };
7939     ChartInternal.prototype.toggleShape = function (that, d, i) {
7940         var $$ = this,
7941             d3 = $$.d3,
7942             config = $$.config,
7943             shape = d3.select(that),
7944             isSelected = shape.classed(CLASS.SELECTED),
7945             toggle = $$.getToggle(that, d).bind($$);
7946
7947         if (config.data_selection_enabled && config.data_selection_isselectable(d)) {
7948             if (!config.data_selection_multiple) {
7949                 $$.main.selectAll('.' + CLASS.shapes + (config.data_selection_grouped ? $$.getTargetSelectorSuffix(d.id) : "")).selectAll('.' + CLASS.shape).each(function (d, i) {
7950                     var shape = d3.select(this);
7951                     if (shape.classed(CLASS.SELECTED)) {
7952                         toggle(false, shape.classed(CLASS.SELECTED, false), d, i);
7953                     }
7954                 });
7955             }
7956             shape.classed(CLASS.SELECTED, !isSelected);
7957             toggle(!isSelected, shape, d, i);
7958         }
7959     };
7960
7961     ChartInternal.prototype.initBar = function () {
7962         var $$ = this;
7963         $$.main.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartBars);
7964     };
7965     ChartInternal.prototype.updateTargetsForBar = function (targets) {
7966         var $$ = this,
7967             config = $$.config,
7968             mainBars,
7969             mainBarEnter,
7970             classChartBar = $$.classChartBar.bind($$),
7971             classBars = $$.classBars.bind($$),
7972             classFocus = $$.classFocus.bind($$);
7973         mainBars = $$.main.select('.' + CLASS.chartBars).selectAll('.' + CLASS.chartBar).data(targets).attr('class', function (d) {
7974             return classChartBar(d) + classFocus(d);
7975         });
7976         mainBarEnter = mainBars.enter().append('g').attr('class', classChartBar).style("pointer-events", "none");
7977         // Bars for each data
7978         mainBarEnter.append('g').attr("class", classBars).style("cursor", function (d) {
7979             return config.data_selection_isselectable(d) ? "pointer" : null;
7980         });
7981     };
7982     ChartInternal.prototype.updateBar = function (durationForExit) {
7983         var $$ = this,
7984             barData = $$.barData.bind($$),
7985             classBar = $$.classBar.bind($$),
7986             initialOpacity = $$.initialOpacity.bind($$),
7987             color = function color(d) {
7988             return $$.color(d.id);
7989         };
7990         var mainBar = $$.main.selectAll('.' + CLASS.bars).selectAll('.' + CLASS.bar).data(barData);
7991         var mainBarEnter = mainBar.enter().append('path').attr("class", classBar).style("stroke", color).style("fill", color);
7992         $$.mainBar = mainBarEnter.merge(mainBar).style("opacity", initialOpacity);
7993         mainBar.exit().transition().duration(durationForExit).style("opacity", 0);
7994     };
7995     ChartInternal.prototype.redrawBar = function (drawBar, withTransition, transition) {
7996         return [(withTransition ? this.mainBar.transition(transition) : this.mainBar).attr('d', drawBar).style("stroke", this.color).style("fill", this.color).style("opacity", 1)];
7997     };
7998     ChartInternal.prototype.getBarW = function (axis, barTargetsNum) {
7999         var $$ = this,
8000             config = $$.config,
8001             w = typeof config.bar_width === 'number' ? config.bar_width : barTargetsNum ? axis.tickInterval() * config.bar_width_ratio / barTargetsNum : 0;
8002         return config.bar_width_max && w > config.bar_width_max ? config.bar_width_max : w;
8003     };
8004     ChartInternal.prototype.getBars = function (i, id) {
8005         var $$ = this;
8006         return (id ? $$.main.selectAll('.' + CLASS.bars + $$.getTargetSelectorSuffix(id)) : $$.main).selectAll('.' + CLASS.bar + (isValue(i) ? '-' + i : ''));
8007     };
8008     ChartInternal.prototype.expandBars = function (i, id, reset) {
8009         var $$ = this;
8010         if (reset) {
8011             $$.unexpandBars();
8012         }
8013         $$.getBars(i, id).classed(CLASS.EXPANDED, true);
8014     };
8015     ChartInternal.prototype.unexpandBars = function (i) {
8016         var $$ = this;
8017         $$.getBars(i).classed(CLASS.EXPANDED, false);
8018     };
8019     ChartInternal.prototype.generateDrawBar = function (barIndices, isSub) {
8020         var $$ = this,
8021             config = $$.config,
8022             getPoints = $$.generateGetBarPoints(barIndices, isSub);
8023         return function (d, i) {
8024             // 4 points that make a bar
8025             var points = getPoints(d, i);
8026
8027             // switch points if axis is rotated, not applicable for sub chart
8028             var indexX = config.axis_rotated ? 1 : 0;
8029             var indexY = config.axis_rotated ? 0 : 1;
8030
8031             var path = 'M ' + points[0][indexX] + ',' + points[0][indexY] + ' ' + 'L' + points[1][indexX] + ',' + points[1][indexY] + ' ' + 'L' + points[2][indexX] + ',' + points[2][indexY] + ' ' + 'L' + points[3][indexX] + ',' + points[3][indexY] + ' ' + 'z';
8032
8033             return path;
8034         };
8035     };
8036     ChartInternal.prototype.generateGetBarPoints = function (barIndices, isSub) {
8037         var $$ = this,
8038             axis = isSub ? $$.subXAxis : $$.xAxis,
8039             barTargetsNum = barIndices.__max__ + 1,
8040             barW = $$.getBarW(axis, barTargetsNum),
8041             barX = $$.getShapeX(barW, barTargetsNum, barIndices, !!isSub),
8042             barY = $$.getShapeY(!!isSub),
8043             barOffset = $$.getShapeOffset($$.isBarType, barIndices, !!isSub),
8044             barSpaceOffset = barW * ($$.config.bar_space / 2),
8045             yScale = isSub ? $$.getSubYScale : $$.getYScale;
8046         return function (d, i) {
8047             var y0 = yScale.call($$, d.id)(0),
8048                 offset = barOffset(d, i) || y0,
8049                 // offset is for stacked bar chart
8050             posX = barX(d),
8051                 posY = barY(d);
8052             // fix posY not to overflow opposite quadrant
8053             if ($$.config.axis_rotated) {
8054                 if (0 < d.value && posY < y0 || d.value < 0 && y0 < posY) {
8055                     posY = y0;
8056                 }
8057             }
8058             // 4 points that make a bar
8059             return [[posX + barSpaceOffset, offset], [posX + barSpaceOffset, posY - (y0 - offset)], [posX + barW - barSpaceOffset, posY - (y0 - offset)], [posX + barW - barSpaceOffset, offset]];
8060         };
8061     };
8062     ChartInternal.prototype.isWithinBar = function (mouse, that) {
8063         var box = that.getBoundingClientRect(),
8064             seg0 = that.pathSegList.getItem(0),
8065             seg1 = that.pathSegList.getItem(1),
8066             x = Math.min(seg0.x, seg1.x),
8067             y = Math.min(seg0.y, seg1.y),
8068             w = box.width,
8069             h = box.height,
8070             offset = 2,
8071             sx = x - offset,
8072             ex = x + w + offset,
8073             sy = y + h + offset,
8074             ey = y - offset;
8075         return sx < mouse[0] && mouse[0] < ex && ey < mouse[1] && mouse[1] < sy;
8076     };
8077
8078     ChartInternal.prototype.getShapeIndices = function (typeFilter) {
8079         var $$ = this,
8080             config = $$.config,
8081             indices = {},
8082             i = 0,
8083             j,
8084             k;
8085         $$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$)).forEach(function (d) {
8086             for (j = 0; j < config.data_groups.length; j++) {
8087                 if (config.data_groups[j].indexOf(d.id) < 0) {
8088                     continue;
8089                 }
8090                 for (k = 0; k < config.data_groups[j].length; k++) {
8091                     if (config.data_groups[j][k] in indices) {
8092                         indices[d.id] = indices[config.data_groups[j][k]];
8093                         break;
8094                     }
8095                 }
8096             }
8097             if (isUndefined(indices[d.id])) {
8098                 indices[d.id] = i++;
8099             }
8100         });
8101         indices.__max__ = i - 1;
8102         return indices;
8103     };
8104     ChartInternal.prototype.getShapeX = function (offset, targetsNum, indices, isSub) {
8105         var $$ = this,
8106             scale = isSub ? $$.subX : $$.x;
8107         return function (d) {
8108             var index = d.id in indices ? indices[d.id] : 0;
8109             return d.x || d.x === 0 ? scale(d.x) - offset * (targetsNum / 2 - index) : 0;
8110         };
8111     };
8112     ChartInternal.prototype.getShapeY = function (isSub) {
8113         var $$ = this;
8114         return function (d) {
8115             var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id);
8116             return scale(d.value);
8117         };
8118     };
8119     ChartInternal.prototype.getShapeOffset = function (typeFilter, indices, isSub) {
8120         var $$ = this,
8121             targets = $$.orderTargets($$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$))),
8122             targetIds = targets.map(function (t) {
8123             return t.id;
8124         });
8125         return function (d, i) {
8126             var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id),
8127                 y0 = scale(0),
8128                 offset = y0;
8129             targets.forEach(function (t) {
8130                 var values = $$.isStepType(d) ? $$.convertValuesToStep(t.values) : t.values;
8131                 if (t.id === d.id || indices[t.id] !== indices[d.id]) {
8132                     return;
8133                 }
8134                 if (targetIds.indexOf(t.id) < targetIds.indexOf(d.id)) {
8135                     // check if the x values line up
8136                     if (typeof values[i] === 'undefined' || +values[i].x !== +d.x) {
8137                         // "+" for timeseries
8138                         // if not, try to find the value that does line up
8139                         i = -1;
8140                         values.forEach(function (v, j) {
8141                             if (v.x === d.x) {
8142                                 i = j;
8143                             }
8144                         });
8145                     }
8146                     if (i in values && values[i].value * d.value >= 0) {
8147                         offset += scale(values[i].value) - y0;
8148                     }
8149                 }
8150             });
8151             return offset;
8152         };
8153     };
8154     ChartInternal.prototype.isWithinShape = function (that, d) {
8155         var $$ = this,
8156             shape = $$.d3.select(that),
8157             isWithin;
8158         if (!$$.isTargetToShow(d.id)) {
8159             isWithin = false;
8160         } else if (that.nodeName === 'circle') {
8161             isWithin = $$.isStepType(d) ? $$.isWithinStep(that, $$.getYScale(d.id)(d.value)) : $$.isWithinCircle(that, $$.pointSelectR(d) * 1.5);
8162         } else if (that.nodeName === 'path') {
8163             isWithin = shape.classed(CLASS.bar) ? $$.isWithinBar($$.d3.mouse(that), that) : true;
8164         }
8165         return isWithin;
8166     };
8167
8168     ChartInternal.prototype.getInterpolate = function (d) {
8169         var $$ = this,
8170             d3 = $$.d3,
8171             types = {
8172             'linear': d3.curveLinear,
8173             'linear-closed': d3.curveLinearClosed,
8174             'basis': d3.curveBasis,
8175             'basis-open': d3.curveBasisOpen,
8176             'basis-closed': d3.curveBasisClosed,
8177             'bundle': d3.curveBundle,
8178             'cardinal': d3.curveCardinal,
8179             'cardinal-open': d3.curveCardinalOpen,
8180             'cardinal-closed': d3.curveCardinalClosed,
8181             'monotone': d3.curveMonotoneX,
8182             'step': d3.curveStep,
8183             'step-before': d3.curveStepBefore,
8184             'step-after': d3.curveStepAfter
8185         },
8186             type;
8187
8188         if ($$.isSplineType(d)) {
8189             type = types[$$.config.spline_interpolation_type] || types.cardinal;
8190         } else if ($$.isStepType(d)) {
8191             type = types[$$.config.line_step_type];
8192         } else {
8193             type = types.linear;
8194         }
8195         return type;
8196     };
8197
8198     ChartInternal.prototype.initLine = function () {
8199         var $$ = this;
8200         $$.main.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartLines);
8201     };
8202     ChartInternal.prototype.updateTargetsForLine = function (targets) {
8203         var $$ = this,
8204             config = $$.config,
8205             mainLines,
8206             mainLineEnter,
8207             classChartLine = $$.classChartLine.bind($$),
8208             classLines = $$.classLines.bind($$),
8209             classAreas = $$.classAreas.bind($$),
8210             classCircles = $$.classCircles.bind($$),
8211             classFocus = $$.classFocus.bind($$);
8212         mainLines = $$.main.select('.' + CLASS.chartLines).selectAll('.' + CLASS.chartLine).data(targets).attr('class', function (d) {
8213             return classChartLine(d) + classFocus(d);
8214         });
8215         mainLineEnter = mainLines.enter().append('g').attr('class', classChartLine).style('opacity', 0).style("pointer-events", "none");
8216         // Lines for each data
8217         mainLineEnter.append('g').attr("class", classLines);
8218         // Areas
8219         mainLineEnter.append('g').attr('class', classAreas);
8220         // Circles for each data point on lines
8221         mainLineEnter.append('g').attr("class", function (d) {
8222             return $$.generateClass(CLASS.selectedCircles, d.id);
8223         });
8224         mainLineEnter.append('g').attr("class", classCircles).style("cursor", function (d) {
8225             return config.data_selection_isselectable(d) ? "pointer" : null;
8226         });
8227         // Update date for selected circles
8228         targets.forEach(function (t) {
8229             $$.main.selectAll('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(t.id)).selectAll('.' + CLASS.selectedCircle).each(function (d) {
8230                 d.value = t.values[d.index].value;
8231             });
8232         });
8233         // MEMO: can not keep same color...
8234         //mainLineUpdate.exit().remove();
8235     };
8236     ChartInternal.prototype.updateLine = function (durationForExit) {
8237         var $$ = this;
8238         var mainLine = $$.main.selectAll('.' + CLASS.lines).selectAll('.' + CLASS.line).data($$.lineData.bind($$));
8239         var mainLineEnter = mainLine.enter().append('path').attr('class', $$.classLine.bind($$)).style("stroke", $$.color);
8240         $$.mainLine = mainLineEnter.merge(mainLine).style("opacity", $$.initialOpacity.bind($$)).style('shape-rendering', function (d) {
8241             return $$.isStepType(d) ? 'crispEdges' : '';
8242         }).attr('transform', null);
8243         mainLine.exit().transition().duration(durationForExit).style('opacity', 0);
8244     };
8245     ChartInternal.prototype.redrawLine = function (drawLine, withTransition, transition) {
8246         return [(withTransition ? this.mainLine.transition(transition) : this.mainLine).attr("d", drawLine).style("stroke", this.color).style("opacity", 1)];
8247     };
8248     ChartInternal.prototype.generateDrawLine = function (lineIndices, isSub) {
8249         var $$ = this,
8250             config = $$.config,
8251             line = $$.d3.line(),
8252             getPoints = $$.generateGetLinePoints(lineIndices, isSub),
8253             yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale,
8254             xValue = function xValue(d) {
8255             return (isSub ? $$.subxx : $$.xx).call($$, d);
8256         },
8257             yValue = function yValue(d, i) {
8258             return config.data_groups.length > 0 ? getPoints(d, i)[0][1] : yScaleGetter.call($$, d.id)(d.value);
8259         };
8260
8261         line = config.axis_rotated ? line.x(yValue).y(xValue) : line.x(xValue).y(yValue);
8262         if (!config.line_connectNull) {
8263             line = line.defined(function (d) {
8264                 return d.value != null;
8265             });
8266         }
8267         return function (d) {
8268             var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values,
8269                 x = isSub ? $$.subX : $$.x,
8270                 y = yScaleGetter.call($$, d.id),
8271                 x0 = 0,
8272                 y0 = 0,
8273                 path;
8274             if ($$.isLineType(d)) {
8275                 if (config.data_regions[d.id]) {
8276                     path = $$.lineWithRegions(values, x, y, config.data_regions[d.id]);
8277                 } else {
8278                     if ($$.isStepType(d)) {
8279                         values = $$.convertValuesToStep(values);
8280                     }
8281                     path = line.curve($$.getInterpolate(d))(values);
8282                 }
8283             } else {
8284                 if (values[0]) {
8285                     x0 = x(values[0].x);
8286                     y0 = y(values[0].value);
8287                 }
8288                 path = config.axis_rotated ? "M " + y0 + " " + x0 : "M " + x0 + " " + y0;
8289             }
8290             return path ? path : "M 0 0";
8291         };
8292     };
8293     ChartInternal.prototype.generateGetLinePoints = function (lineIndices, isSub) {
8294         // partial duplication of generateGetBarPoints
8295         var $$ = this,
8296             config = $$.config,
8297             lineTargetsNum = lineIndices.__max__ + 1,
8298             x = $$.getShapeX(0, lineTargetsNum, lineIndices, !!isSub),
8299             y = $$.getShapeY(!!isSub),
8300             lineOffset = $$.getShapeOffset($$.isLineType, lineIndices, !!isSub),
8301             yScale = isSub ? $$.getSubYScale : $$.getYScale;
8302         return function (d, i) {
8303             var y0 = yScale.call($$, d.id)(0),
8304                 offset = lineOffset(d, i) || y0,
8305                 // offset is for stacked area chart
8306             posX = x(d),
8307                 posY = y(d);
8308             // fix posY not to overflow opposite quadrant
8309             if (config.axis_rotated) {
8310                 if (0 < d.value && posY < y0 || d.value < 0 && y0 < posY) {
8311                     posY = y0;
8312                 }
8313             }
8314             // 1 point that marks the line position
8315             return [[posX, posY - (y0 - offset)], [posX, posY - (y0 - offset)], // needed for compatibility
8316             [posX, posY - (y0 - offset)], // needed for compatibility
8317             [posX, posY - (y0 - offset)] // needed for compatibility
8318             ];
8319         };
8320     };
8321
8322     ChartInternal.prototype.lineWithRegions = function (d, x, y, _regions) {
8323         var $$ = this,
8324             config = $$.config,
8325             prev = -1,
8326             i,
8327             j,
8328             s = "M",
8329             sWithRegion,
8330             xp,
8331             yp,
8332             dx,
8333             dy,
8334             dd,
8335             diff,
8336             diffx2,
8337             xOffset = $$.isCategorized() ? 0.5 : 0,
8338             xValue,
8339             yValue,
8340             regions = [];
8341
8342         function isWithinRegions(x, regions) {
8343             var i;
8344             for (i = 0; i < regions.length; i++) {
8345                 if (regions[i].start < x && x <= regions[i].end) {
8346                     return true;
8347                 }
8348             }
8349             return false;
8350         }
8351
8352         // Check start/end of regions
8353         if (isDefined(_regions)) {
8354             for (i = 0; i < _regions.length; i++) {
8355                 regions[i] = {};
8356                 if (isUndefined(_regions[i].start)) {
8357                     regions[i].start = d[0].x;
8358                 } else {
8359                     regions[i].start = $$.isTimeSeries() ? $$.parseDate(_regions[i].start) : _regions[i].start;
8360                 }
8361                 if (isUndefined(_regions[i].end)) {
8362                     regions[i].end = d[d.length - 1].x;
8363                 } else {
8364                     regions[i].end = $$.isTimeSeries() ? $$.parseDate(_regions[i].end) : _regions[i].end;
8365                 }
8366             }
8367         }
8368
8369         // Set scales
8370         xValue = config.axis_rotated ? function (d) {
8371             return y(d.value);
8372         } : function (d) {
8373             return x(d.x);
8374         };
8375         yValue = config.axis_rotated ? function (d) {
8376             return x(d.x);
8377         } : function (d) {
8378             return y(d.value);
8379         };
8380
8381         // Define svg generator function for region
8382         function generateM(points) {
8383             return 'M' + points[0][0] + ' ' + points[0][1] + ' ' + points[1][0] + ' ' + points[1][1];
8384         }
8385         if ($$.isTimeSeries()) {
8386             sWithRegion = function sWithRegion(d0, d1, j, diff) {
8387                 var x0 = d0.x.getTime(),
8388                     x_diff = d1.x - d0.x,
8389                     xv0 = new Date(x0 + x_diff * j),
8390                     xv1 = new Date(x0 + x_diff * (j + diff)),
8391                     points;
8392                 if (config.axis_rotated) {
8393                     points = [[y(yp(j)), x(xv0)], [y(yp(j + diff)), x(xv1)]];
8394                 } else {
8395                     points = [[x(xv0), y(yp(j))], [x(xv1), y(yp(j + diff))]];
8396                 }
8397                 return generateM(points);
8398             };
8399         } else {
8400             sWithRegion = function sWithRegion(d0, d1, j, diff) {
8401                 var points;
8402                 if (config.axis_rotated) {
8403                     points = [[y(yp(j), true), x(xp(j))], [y(yp(j + diff), true), x(xp(j + diff))]];
8404                 } else {
8405                     points = [[x(xp(j), true), y(yp(j))], [x(xp(j + diff), true), y(yp(j + diff))]];
8406                 }
8407                 return generateM(points);
8408             };
8409         }
8410
8411         // Generate
8412         for (i = 0; i < d.length; i++) {
8413
8414             // Draw as normal
8415             if (isUndefined(regions) || !isWithinRegions(d[i].x, regions)) {
8416                 s += " " + xValue(d[i]) + " " + yValue(d[i]);
8417             }
8418             // Draw with region // TODO: Fix for horizotal charts
8419             else {
8420                     xp = $$.getScale(d[i - 1].x + xOffset, d[i].x + xOffset, $$.isTimeSeries());
8421                     yp = $$.getScale(d[i - 1].value, d[i].value);
8422
8423                     dx = x(d[i].x) - x(d[i - 1].x);
8424                     dy = y(d[i].value) - y(d[i - 1].value);
8425                     dd = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
8426                     diff = 2 / dd;
8427                     diffx2 = diff * 2;
8428
8429                     for (j = diff; j <= 1; j += diffx2) {
8430                         s += sWithRegion(d[i - 1], d[i], j, diff);
8431                     }
8432                 }
8433             prev = d[i].x;
8434         }
8435
8436         return s;
8437     };
8438
8439     ChartInternal.prototype.updateArea = function (durationForExit) {
8440         var $$ = this,
8441             d3 = $$.d3;
8442         var mainArea = $$.main.selectAll('.' + CLASS.areas).selectAll('.' + CLASS.area).data($$.lineData.bind($$));
8443         var mainAreaEnter = mainArea.enter().append('path').attr("class", $$.classArea.bind($$)).style("fill", $$.color).style("opacity", function () {
8444             $$.orgAreaOpacity = +d3.select(this).style('opacity');return 0;
8445         });
8446         $$.mainArea = mainAreaEnter.merge(mainArea).style("opacity", $$.orgAreaOpacity);
8447         mainArea.exit().transition().duration(durationForExit).style('opacity', 0);
8448     };
8449     ChartInternal.prototype.redrawArea = function (drawArea, withTransition, transition) {
8450         return [(withTransition ? this.mainArea.transition(transition) : this.mainArea).attr("d", drawArea).style("fill", this.color).style("opacity", this.orgAreaOpacity)];
8451     };
8452     ChartInternal.prototype.generateDrawArea = function (areaIndices, isSub) {
8453         var $$ = this,
8454             config = $$.config,
8455             area = $$.d3.area(),
8456             getPoints = $$.generateGetAreaPoints(areaIndices, isSub),
8457             yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale,
8458             xValue = function xValue(d) {
8459             return (isSub ? $$.subxx : $$.xx).call($$, d);
8460         },
8461             value0 = function value0(d, i) {
8462             return config.data_groups.length > 0 ? getPoints(d, i)[0][1] : yScaleGetter.call($$, d.id)($$.getAreaBaseValue(d.id));
8463         },
8464             value1 = function value1(d, i) {
8465             return config.data_groups.length > 0 ? getPoints(d, i)[1][1] : yScaleGetter.call($$, d.id)(d.value);
8466         };
8467
8468         area = config.axis_rotated ? area.x0(value0).x1(value1).y(xValue) : area.x(xValue).y0(config.area_above ? 0 : value0).y1(value1);
8469         if (!config.line_connectNull) {
8470             area = area.defined(function (d) {
8471                 return d.value !== null;
8472             });
8473         }
8474
8475         return function (d) {
8476             var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values,
8477                 x0 = 0,
8478                 y0 = 0,
8479                 path;
8480             if ($$.isAreaType(d)) {
8481                 if ($$.isStepType(d)) {
8482                     values = $$.convertValuesToStep(values);
8483                 }
8484                 path = area.curve($$.getInterpolate(d))(values);
8485             } else {
8486                 if (values[0]) {
8487                     x0 = $$.x(values[0].x);
8488                     y0 = $$.getYScale(d.id)(values[0].value);
8489                 }
8490                 path = config.axis_rotated ? "M " + y0 + " " + x0 : "M " + x0 + " " + y0;
8491             }
8492             return path ? path : "M 0 0";
8493         };
8494     };
8495     ChartInternal.prototype.getAreaBaseValue = function () {
8496         return 0;
8497     };
8498     ChartInternal.prototype.generateGetAreaPoints = function (areaIndices, isSub) {
8499         // partial duplication of generateGetBarPoints
8500         var $$ = this,
8501             config = $$.config,
8502             areaTargetsNum = areaIndices.__max__ + 1,
8503             x = $$.getShapeX(0, areaTargetsNum, areaIndices, !!isSub),
8504             y = $$.getShapeY(!!isSub),
8505             areaOffset = $$.getShapeOffset($$.isAreaType, areaIndices, !!isSub),
8506             yScale = isSub ? $$.getSubYScale : $$.getYScale;
8507         return function (d, i) {
8508             var y0 = yScale.call($$, d.id)(0),
8509                 offset = areaOffset(d, i) || y0,
8510                 // offset is for stacked area chart
8511             posX = x(d),
8512                 posY = y(d);
8513             // fix posY not to overflow opposite quadrant
8514             if (config.axis_rotated) {
8515                 if (0 < d.value && posY < y0 || d.value < 0 && y0 < posY) {
8516                     posY = y0;
8517                 }
8518             }
8519             // 1 point that marks the area position
8520             return [[posX, offset], [posX, posY - (y0 - offset)], [posX, posY - (y0 - offset)], // needed for compatibility
8521             [posX, offset] // needed for compatibility
8522             ];
8523         };
8524     };
8525
8526     ChartInternal.prototype.updateCircle = function (cx, cy) {
8527         var $$ = this;
8528         var mainCircle = $$.main.selectAll('.' + CLASS.circles).selectAll('.' + CLASS.circle).data($$.lineOrScatterData.bind($$));
8529         var mainCircleEnter = mainCircle.enter().append("circle").attr("class", $$.classCircle.bind($$)).attr("cx", cx).attr("cy", cy).attr("r", $$.pointR.bind($$)).style("fill", $$.color);
8530         $$.mainCircle = mainCircleEnter.merge(mainCircle).style("opacity", $$.initialOpacityForCircle.bind($$));
8531         mainCircle.exit().style("opacity", 0);
8532     };
8533     ChartInternal.prototype.redrawCircle = function (cx, cy, withTransition, transition) {
8534         var $$ = this,
8535             selectedCircles = $$.main.selectAll('.' + CLASS.selectedCircle);
8536         return [(withTransition ? $$.mainCircle.transition(transition) : $$.mainCircle).style('opacity', this.opacityForCircle.bind($$)).style("fill", $$.color).attr("cx", cx).attr("cy", cy), (withTransition ? selectedCircles.transition(transition) : selectedCircles).attr("cx", cx).attr("cy", cy)];
8537     };
8538     ChartInternal.prototype.circleX = function (d) {
8539         return d.x || d.x === 0 ? this.x(d.x) : null;
8540     };
8541     ChartInternal.prototype.updateCircleY = function () {
8542         var $$ = this,
8543             lineIndices,
8544             getPoints;
8545         if ($$.config.data_groups.length > 0) {
8546             lineIndices = $$.getShapeIndices($$.isLineType), getPoints = $$.generateGetLinePoints(lineIndices);
8547             $$.circleY = function (d, i) {
8548                 return getPoints(d, i)[0][1];
8549             };
8550         } else {
8551             $$.circleY = function (d) {
8552                 return $$.getYScale(d.id)(d.value);
8553             };
8554         }
8555     };
8556     ChartInternal.prototype.getCircles = function (i, id) {
8557         var $$ = this;
8558         return (id ? $$.main.selectAll('.' + CLASS.circles + $$.getTargetSelectorSuffix(id)) : $$.main).selectAll('.' + CLASS.circle + (isValue(i) ? '-' + i : ''));
8559     };
8560     ChartInternal.prototype.expandCircles = function (i, id, reset) {
8561         var $$ = this,
8562             r = $$.pointExpandedR.bind($$);
8563         if (reset) {
8564             $$.unexpandCircles();
8565         }
8566         $$.getCircles(i, id).classed(CLASS.EXPANDED, true).attr('r', r);
8567     };
8568     ChartInternal.prototype.unexpandCircles = function (i) {
8569         var $$ = this,
8570             r = $$.pointR.bind($$);
8571         $$.getCircles(i).filter(function () {
8572             return $$.d3.select(this).classed(CLASS.EXPANDED);
8573         }).classed(CLASS.EXPANDED, false).attr('r', r);
8574     };
8575     ChartInternal.prototype.pointR = function (d) {
8576         var $$ = this,
8577             config = $$.config;
8578         return $$.isStepType(d) ? 0 : isFunction(config.point_r) ? config.point_r(d) : config.point_r;
8579     };
8580     ChartInternal.prototype.pointExpandedR = function (d) {
8581         var $$ = this,
8582             config = $$.config;
8583         if (config.point_focus_expand_enabled) {
8584             return isFunction(config.point_focus_expand_r) ? config.point_focus_expand_r(d) : config.point_focus_expand_r ? config.point_focus_expand_r : $$.pointR(d) * 1.75;
8585         } else {
8586             return $$.pointR(d);
8587         }
8588     };
8589     ChartInternal.prototype.pointSelectR = function (d) {
8590         var $$ = this,
8591             config = $$.config;
8592         return isFunction(config.point_select_r) ? config.point_select_r(d) : config.point_select_r ? config.point_select_r : $$.pointR(d) * 4;
8593     };
8594     ChartInternal.prototype.isWithinCircle = function (that, r) {
8595         var d3 = this.d3,
8596             mouse = d3.mouse(that),
8597             d3_this = d3.select(that),
8598             cx = +d3_this.attr("cx"),
8599             cy = +d3_this.attr("cy");
8600         return Math.sqrt(Math.pow(cx - mouse[0], 2) + Math.pow(cy - mouse[1], 2)) < r;
8601     };
8602     ChartInternal.prototype.isWithinStep = function (that, y) {
8603         return Math.abs(y - this.d3.mouse(that)[1]) < 30;
8604     };
8605
8606     ChartInternal.prototype.getCurrentWidth = function () {
8607         var $$ = this,
8608             config = $$.config;
8609         return config.size_width ? config.size_width : $$.getParentWidth();
8610     };
8611     ChartInternal.prototype.getCurrentHeight = function () {
8612         var $$ = this,
8613             config = $$.config,
8614             h = config.size_height ? config.size_height : $$.getParentHeight();
8615         return h > 0 ? h : 320 / ($$.hasType('gauge') && !config.gauge_fullCircle ? 2 : 1);
8616     };
8617     ChartInternal.prototype.getCurrentPaddingTop = function () {
8618         var $$ = this,
8619             config = $$.config,
8620             padding = isValue(config.padding_top) ? config.padding_top : 0;
8621         if ($$.title && $$.title.node()) {
8622             padding += $$.getTitlePadding();
8623         }
8624         return padding;
8625     };
8626     ChartInternal.prototype.getCurrentPaddingBottom = function () {
8627         var config = this.config;
8628         return isValue(config.padding_bottom) ? config.padding_bottom : 0;
8629     };
8630     ChartInternal.prototype.getCurrentPaddingLeft = function (withoutRecompute) {
8631         var $$ = this,
8632             config = $$.config;
8633         if (isValue(config.padding_left)) {
8634             return config.padding_left;
8635         } else if (config.axis_rotated) {
8636             return !config.axis_x_show || config.axis_x_inner ? 1 : Math.max(ceil10($$.getAxisWidthByAxisId('x', withoutRecompute)), 40);
8637         } else if (!config.axis_y_show || config.axis_y_inner) {
8638             // && !config.axis_rotated
8639             return $$.axis.getYAxisLabelPosition().isOuter ? 30 : 1;
8640         } else {
8641             return ceil10($$.getAxisWidthByAxisId('y', withoutRecompute));
8642         }
8643     };
8644     ChartInternal.prototype.getCurrentPaddingRight = function () {
8645         var $$ = this,
8646             config = $$.config,
8647             defaultPadding = 10,
8648             legendWidthOnRight = $$.isLegendRight ? $$.getLegendWidth() + 20 : 0;
8649         if (isValue(config.padding_right)) {
8650             return config.padding_right + 1; // 1 is needed not to hide tick line
8651         } else if (config.axis_rotated) {
8652             return defaultPadding + legendWidthOnRight;
8653         } else if (!config.axis_y2_show || config.axis_y2_inner) {
8654             // && !config.axis_rotated
8655             return 2 + legendWidthOnRight + ($$.axis.getY2AxisLabelPosition().isOuter ? 20 : 0);
8656         } else {
8657             return ceil10($$.getAxisWidthByAxisId('y2')) + legendWidthOnRight;
8658         }
8659     };
8660
8661     ChartInternal.prototype.getParentRectValue = function (key) {
8662         var parent = this.selectChart.node(),
8663             v;
8664         while (parent && parent.tagName !== 'BODY') {
8665             try {
8666                 v = parent.getBoundingClientRect()[key];
8667             } catch (e) {
8668                 if (key === 'width') {
8669                     // In IE in certain cases getBoundingClientRect
8670                     // will cause an "unspecified error"
8671                     v = parent.offsetWidth;
8672                 }
8673             }
8674             if (v) {
8675                 break;
8676             }
8677             parent = parent.parentNode;
8678         }
8679         return v;
8680     };
8681     ChartInternal.prototype.getParentWidth = function () {
8682         return this.getParentRectValue('width');
8683     };
8684     ChartInternal.prototype.getParentHeight = function () {
8685         var h = this.selectChart.style('height');
8686         return h.indexOf('px') > 0 ? +h.replace('px', '') : 0;
8687     };
8688
8689     ChartInternal.prototype.getSvgLeft = function (withoutRecompute) {
8690         var $$ = this,
8691             config = $$.config,
8692             hasLeftAxisRect = config.axis_rotated || !config.axis_rotated && !config.axis_y_inner,
8693             leftAxisClass = config.axis_rotated ? CLASS.axisX : CLASS.axisY,
8694             leftAxis = $$.main.select('.' + leftAxisClass).node(),
8695             svgRect = leftAxis && hasLeftAxisRect ? leftAxis.getBoundingClientRect() : { right: 0 },
8696             chartRect = $$.selectChart.node().getBoundingClientRect(),
8697             hasArc = $$.hasArcType(),
8698             svgLeft = svgRect.right - chartRect.left - (hasArc ? 0 : $$.getCurrentPaddingLeft(withoutRecompute));
8699         return svgLeft > 0 ? svgLeft : 0;
8700     };
8701
8702     ChartInternal.prototype.getAxisWidthByAxisId = function (id, withoutRecompute) {
8703         var $$ = this,
8704             position = $$.axis.getLabelPositionById(id);
8705         return $$.axis.getMaxTickWidth(id, withoutRecompute) + (position.isInner ? 20 : 40);
8706     };
8707     ChartInternal.prototype.getHorizontalAxisHeight = function (axisId) {
8708         var $$ = this,
8709             config = $$.config,
8710             h = 30;
8711         if (axisId === 'x' && !config.axis_x_show) {
8712             return 8;
8713         }
8714         if (axisId === 'x' && config.axis_x_height) {
8715             return config.axis_x_height;
8716         }
8717         if (axisId === 'y' && !config.axis_y_show) {
8718             return config.legend_show && !$$.isLegendRight && !$$.isLegendInset ? 10 : 1;
8719         }
8720         if (axisId === 'y2' && !config.axis_y2_show) {
8721             return $$.rotated_padding_top;
8722         }
8723         // Calculate x axis height when tick rotated
8724         if (axisId === 'x' && !config.axis_rotated && config.axis_x_tick_rotate) {
8725             h = 30 + $$.axis.getMaxTickWidth(axisId) * Math.cos(Math.PI * (90 - Math.abs(config.axis_x_tick_rotate)) / 180);
8726         }
8727         // Calculate y axis height when tick rotated
8728         if (axisId === 'y' && config.axis_rotated && config.axis_y_tick_rotate) {
8729             h = 30 + $$.axis.getMaxTickWidth(axisId) * Math.cos(Math.PI * (90 - Math.abs(config.axis_y_tick_rotate)) / 180);
8730         }
8731         return h + ($$.axis.getLabelPositionById(axisId).isInner ? 0 : 10) + (axisId === 'y2' ? -10 : 0);
8732     };
8733
8734     ChartInternal.prototype.initBrush = function (scale) {
8735         var $$ = this,
8736             d3 = $$.d3;
8737         // TODO: dynamically change brushY/brushX according to axis_rotated.
8738         $$.brush = ($$.config.axis_rotated ? d3.brushY() : d3.brushX()).on("brush", function () {
8739             var event = d3.event.sourceEvent;
8740             if (event && event.type === "zoom") {
8741                 return;
8742             }
8743             $$.redrawForBrush();
8744         }).on("end", function () {
8745             var event = d3.event.sourceEvent;
8746             if (event && event.type === "zoom") {
8747                 return;
8748             }
8749             if ($$.brush.empty() && event && event.type !== 'end') {
8750                 $$.brush.clear();
8751             }
8752         });
8753         $$.brush.updateExtent = function () {
8754             var range = this.scale.range(),
8755                 extent;
8756             if ($$.config.axis_rotated) {
8757                 extent = [[0, range[0]], [$$.width2, range[1]]];
8758             } else {
8759                 extent = [[range[0], 0], [range[1], $$.height2]];
8760             }
8761             this.extent(extent);
8762             return this;
8763         };
8764         $$.brush.updateScale = function (scale) {
8765             this.scale = scale;
8766             return this;
8767         };
8768         $$.brush.update = function (scale) {
8769             this.updateScale(scale || $$.subX).updateExtent();
8770             $$.context.select('.' + CLASS.brush).call(this);
8771         };
8772         $$.brush.clear = function () {
8773             $$.context.select('.' + CLASS.brush).call($$.brush.move, null);
8774         };
8775         $$.brush.selection = function () {
8776             return d3.brushSelection($$.context.select('.' + CLASS.brush).node());
8777         };
8778         $$.brush.selectionAsValue = function (selectionAsValue, withTransition) {
8779             var selection, brush;
8780             if (selectionAsValue) {
8781                 if ($$.context) {
8782                     selection = [this.scale(selectionAsValue[0]), this.scale(selectionAsValue[1])];
8783                     brush = $$.context.select('.' + CLASS.brush);
8784                     if (withTransition) {
8785                         brush = brush.transition();
8786                     }
8787                     $$.brush.move(brush, selection);
8788                 }
8789                 return [];
8790             }
8791             selection = $$.brush.selection() || [0, 0];
8792             return [this.scale.invert(selection[0]), this.scale.invert(selection[1])];
8793         };
8794         $$.brush.empty = function () {
8795             var selection = $$.brush.selection();
8796             return !selection || selection[0] === selection[1];
8797         };
8798         return $$.brush.updateScale(scale);
8799     };
8800     ChartInternal.prototype.initSubchart = function () {
8801         var $$ = this,
8802             config = $$.config,
8803             context = $$.context = $$.svg.append("g").attr("transform", $$.getTranslate('context')),
8804             visibility = config.subchart_show ? 'visible' : 'hidden';
8805
8806         // set style
8807         context.style('visibility', visibility);
8808
8809         // Define g for chart area
8810         context.append('g').attr("clip-path", $$.clipPathForSubchart).attr('class', CLASS.chart);
8811
8812         // Define g for bar chart area
8813         context.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartBars);
8814
8815         // Define g for line chart area
8816         context.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartLines);
8817
8818         // Add extent rect for Brush
8819         context.append("g").attr("clip-path", $$.clipPath).attr("class", CLASS.brush);
8820
8821         // ATTENTION: This must be called AFTER chart added
8822         // Add Axis
8823         $$.axes.subx = context.append("g").attr("class", CLASS.axisX).attr("transform", $$.getTranslate('subx')).attr("clip-path", config.axis_rotated ? "" : $$.clipPathForXAxis);
8824     };
8825     ChartInternal.prototype.initSubchartBrush = function () {
8826         var $$ = this;
8827         // Add extent rect for Brush
8828         $$.initBrush($$.subX).updateExtent();
8829         $$.context.select('.' + CLASS.brush).call($$.brush);
8830     };
8831     ChartInternal.prototype.updateTargetsForSubchart = function (targets) {
8832         var $$ = this,
8833             context = $$.context,
8834             config = $$.config,
8835             contextLineEnter,
8836             contextLine,
8837             contextBarEnter,
8838             contextBar,
8839             classChartBar = $$.classChartBar.bind($$),
8840             classBars = $$.classBars.bind($$),
8841             classChartLine = $$.classChartLine.bind($$),
8842             classLines = $$.classLines.bind($$),
8843             classAreas = $$.classAreas.bind($$);
8844
8845         if (config.subchart_show) {
8846             //-- Bar --//
8847             contextBar = context.select('.' + CLASS.chartBars).selectAll('.' + CLASS.chartBar).data(targets);
8848             contextBarEnter = contextBar.enter().append('g').style('opacity', 0);
8849             contextBarEnter.merge(contextBar).attr('class', classChartBar);
8850             // Bars for each data
8851             contextBarEnter.append('g').attr("class", classBars);
8852
8853             //-- Line --//
8854             contextLine = context.select('.' + CLASS.chartLines).selectAll('.' + CLASS.chartLine).data(targets);
8855             contextLineEnter = contextLine.enter().append('g').style('opacity', 0);
8856             contextLineEnter.merge(contextLine).attr('class', classChartLine);
8857             // Lines for each data
8858             contextLineEnter.append("g").attr("class", classLines);
8859             // Area
8860             contextLineEnter.append("g").attr("class", classAreas);
8861
8862             //-- Brush --//
8863             context.selectAll('.' + CLASS.brush + ' rect').attr(config.axis_rotated ? "width" : "height", config.axis_rotated ? $$.width2 : $$.height2);
8864         }
8865     };
8866     ChartInternal.prototype.updateBarForSubchart = function (durationForExit) {
8867         var $$ = this;
8868         var contextBar = $$.context.selectAll('.' + CLASS.bars).selectAll('.' + CLASS.bar).data($$.barData.bind($$));
8869         var contextBarEnter = contextBar.enter().append('path').attr("class", $$.classBar.bind($$)).style("stroke", 'none').style("fill", $$.color);
8870         contextBar.exit().transition().duration(durationForExit).style('opacity', 0).remove();
8871         $$.contextBar = contextBarEnter.merge(contextBar).style("opacity", $$.initialOpacity.bind($$));
8872     };
8873     ChartInternal.prototype.redrawBarForSubchart = function (drawBarOnSub, withTransition, duration) {
8874         (withTransition ? this.contextBar.transition(Math.random().toString()).duration(duration) : this.contextBar).attr('d', drawBarOnSub).style('opacity', 1);
8875     };
8876     ChartInternal.prototype.updateLineForSubchart = function (durationForExit) {
8877         var $$ = this;
8878         var contextLine = $$.context.selectAll('.' + CLASS.lines).selectAll('.' + CLASS.line).data($$.lineData.bind($$));
8879         var contextLineEnter = contextLine.enter().append('path').attr('class', $$.classLine.bind($$)).style('stroke', $$.color);
8880         contextLine.exit().transition().duration(durationForExit).style('opacity', 0).remove();
8881         $$.contextLine = contextLineEnter.merge(contextLine).style("opacity", $$.initialOpacity.bind($$));
8882     };
8883     ChartInternal.prototype.redrawLineForSubchart = function (drawLineOnSub, withTransition, duration) {
8884         (withTransition ? this.contextLine.transition(Math.random().toString()).duration(duration) : this.contextLine).attr("d", drawLineOnSub).style('opacity', 1);
8885     };
8886     ChartInternal.prototype.updateAreaForSubchart = function (durationForExit) {
8887         var $$ = this,
8888             d3 = $$.d3;
8889         var contextArea = $$.context.selectAll('.' + CLASS.areas).selectAll('.' + CLASS.area).data($$.lineData.bind($$));
8890         var contextAreaEnter = contextArea.enter().append('path').attr("class", $$.classArea.bind($$)).style("fill", $$.color).style("opacity", function () {
8891             $$.orgAreaOpacity = +d3.select(this).style('opacity');return 0;
8892         });
8893         contextArea.exit().transition().duration(durationForExit).style('opacity', 0).remove();
8894         $$.contextArea = contextAreaEnter.merge(contextArea).style("opacity", 0);
8895     };
8896     ChartInternal.prototype.redrawAreaForSubchart = function (drawAreaOnSub, withTransition, duration) {
8897         (withTransition ? this.contextArea.transition(Math.random().toString()).duration(duration) : this.contextArea).attr("d", drawAreaOnSub).style("fill", this.color).style("opacity", this.orgAreaOpacity);
8898     };
8899     ChartInternal.prototype.redrawSubchart = function (withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices) {
8900         var $$ = this,
8901             d3 = $$.d3,
8902             config = $$.config,
8903             drawAreaOnSub,
8904             drawBarOnSub,
8905             drawLineOnSub;
8906
8907         $$.context.style('visibility', config.subchart_show ? 'visible' : 'hidden');
8908
8909         // subchart
8910         if (config.subchart_show) {
8911             // reflect main chart to extent on subchart if zoomed
8912             if (d3.event && d3.event.type === 'zoom') {
8913                 $$.brush.selectionAsValue($$.x.orgDomain());
8914             }
8915             // update subchart elements if needed
8916             if (withSubchart) {
8917                 // extent rect
8918                 if (!$$.brush.empty()) {
8919                     $$.brush.selectionAsValue($$.x.orgDomain());
8920                 }
8921                 // setup drawer - MEMO: this must be called after axis updated
8922                 drawAreaOnSub = $$.generateDrawArea(areaIndices, true);
8923                 drawBarOnSub = $$.generateDrawBar(barIndices, true);
8924                 drawLineOnSub = $$.generateDrawLine(lineIndices, true);
8925
8926                 $$.updateBarForSubchart(duration);
8927                 $$.updateLineForSubchart(duration);
8928                 $$.updateAreaForSubchart(duration);
8929
8930                 $$.redrawBarForSubchart(drawBarOnSub, duration, duration);
8931                 $$.redrawLineForSubchart(drawLineOnSub, duration, duration);
8932                 $$.redrawAreaForSubchart(drawAreaOnSub, duration, duration);
8933             }
8934         }
8935     };
8936     ChartInternal.prototype.redrawForBrush = function () {
8937         var $$ = this,
8938             x = $$.x,
8939             d3 = $$.d3,
8940             s;
8941         $$.redraw({
8942             withTransition: false,
8943             withY: $$.config.zoom_rescale,
8944             withSubchart: false,
8945             withUpdateXDomain: true,
8946             withEventRect: false,
8947             withDimension: false
8948         });
8949         // update zoom transation binded to event rect
8950         s = d3.event.selection || $$.brush.scale.range();
8951         $$.main.select('.' + CLASS.eventRect).call($$.zoom.transform, d3.zoomIdentity.scale($$.width / (s[1] - s[0])).translate(-s[0], 0));
8952         $$.config.subchart_onbrush.call($$.api, x.orgDomain());
8953     };
8954     ChartInternal.prototype.transformContext = function (withTransition, transitions) {
8955         var $$ = this,
8956             subXAxis;
8957         if (transitions && transitions.axisSubX) {
8958             subXAxis = transitions.axisSubX;
8959         } else {
8960             subXAxis = $$.context.select('.' + CLASS.axisX);
8961             if (withTransition) {
8962                 subXAxis = subXAxis.transition();
8963             }
8964         }
8965         $$.context.attr("transform", $$.getTranslate('context'));
8966         subXAxis.attr("transform", $$.getTranslate('subx'));
8967     };
8968     ChartInternal.prototype.getDefaultSelection = function () {
8969         var $$ = this,
8970             config = $$.config,
8971             selection = isFunction(config.axis_x_selection) ? config.axis_x_selection($$.getXDomain($$.data.targets)) : config.axis_x_selection;
8972         if ($$.isTimeSeries()) {
8973             selection = [$$.parseDate(selection[0]), $$.parseDate(selection[1])];
8974         }
8975         return selection;
8976     };
8977
8978     ChartInternal.prototype.initText = function () {
8979         var $$ = this;
8980         $$.main.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartTexts);
8981         $$.mainText = $$.d3.selectAll([]);
8982     };
8983     ChartInternal.prototype.updateTargetsForText = function (targets) {
8984         var $$ = this,
8985             classChartText = $$.classChartText.bind($$),
8986             classTexts = $$.classTexts.bind($$),
8987             classFocus = $$.classFocus.bind($$);
8988         var mainText = $$.main.select('.' + CLASS.chartTexts).selectAll('.' + CLASS.chartText).data(targets);
8989         var mainTextEnter = mainText.enter().append('g').attr('class', classChartText).style('opacity', 0).style("pointer-events", "none");
8990         mainTextEnter.append('g').attr('class', classTexts);
8991         mainTextEnter.merge(mainText).attr('class', function (d) {
8992             return classChartText(d) + classFocus(d);
8993         });
8994     };
8995     ChartInternal.prototype.updateText = function (xForText, yForText, durationForExit) {
8996         var $$ = this,
8997             config = $$.config,
8998             barOrLineData = $$.barOrLineData.bind($$),
8999             classText = $$.classText.bind($$);
9000         var mainText = $$.main.selectAll('.' + CLASS.texts).selectAll('.' + CLASS.text).data(barOrLineData);
9001         var mainTextEnter = mainText.enter().append('text').attr("class", classText).attr('text-anchor', function (d) {
9002             return config.axis_rotated ? d.value < 0 ? 'end' : 'start' : 'middle';
9003         }).style("stroke", 'none').attr('x', xForText).attr('y', yForText).style("fill", function (d) {
9004             return $$.color(d);
9005         }).style("fill-opacity", 0);
9006         $$.mainText = mainTextEnter.merge(mainText).text(function (d, i, j) {
9007             return $$.dataLabelFormat(d.id)(d.value, d.id, i, j);
9008         });
9009         mainText.exit().transition().duration(durationForExit).style('fill-opacity', 0).remove();
9010     };
9011     ChartInternal.prototype.redrawText = function (xForText, yForText, forFlow, withTransition, transition) {
9012         return [(withTransition ? this.mainText.transition(transition) : this.mainText).attr('x', xForText).attr('y', yForText).style("fill", this.color).style("fill-opacity", forFlow ? 0 : this.opacityForText.bind(this))];
9013     };
9014     ChartInternal.prototype.getTextRect = function (text, cls, element) {
9015         var dummy = this.d3.select('body').append('div').classed('c3', true),
9016             svg = dummy.append("svg").style('visibility', 'hidden').style('position', 'fixed').style('top', 0).style('left', 0),
9017             font = this.d3.select(element).style('font'),
9018             rect;
9019         svg.selectAll('.dummy').data([text]).enter().append('text').classed(cls ? cls : "", true).style('font', font).text(text).each(function () {
9020             rect = this.getBoundingClientRect();
9021         });
9022         dummy.remove();
9023         return rect;
9024     };
9025     ChartInternal.prototype.generateXYForText = function (areaIndices, barIndices, lineIndices, forX) {
9026         var $$ = this,
9027             getAreaPoints = $$.generateGetAreaPoints(areaIndices, false),
9028             getBarPoints = $$.generateGetBarPoints(barIndices, false),
9029             getLinePoints = $$.generateGetLinePoints(lineIndices, false),
9030             getter = forX ? $$.getXForText : $$.getYForText;
9031         return function (d, i) {
9032             var getPoints = $$.isAreaType(d) ? getAreaPoints : $$.isBarType(d) ? getBarPoints : getLinePoints;
9033             return getter.call($$, getPoints(d, i), d, this);
9034         };
9035     };
9036     ChartInternal.prototype.getXForText = function (points, d, textElement) {
9037         var $$ = this,
9038             box = textElement.getBoundingClientRect(),
9039             xPos,
9040             padding;
9041         if ($$.config.axis_rotated) {
9042             padding = $$.isBarType(d) ? 4 : 6;
9043             xPos = points[2][1] + padding * (d.value < 0 ? -1 : 1);
9044         } else {
9045             xPos = $$.hasType('bar') ? (points[2][0] + points[0][0]) / 2 : points[0][0];
9046         }
9047         // show labels regardless of the domain if value is null
9048         if (d.value === null) {
9049             if (xPos > $$.width) {
9050                 xPos = $$.width - box.width;
9051             } else if (xPos < 0) {
9052                 xPos = 4;
9053             }
9054         }
9055         return xPos;
9056     };
9057     ChartInternal.prototype.getYForText = function (points, d, textElement) {
9058         var $$ = this,
9059             box = textElement.getBoundingClientRect(),
9060             yPos;
9061         if ($$.config.axis_rotated) {
9062             yPos = (points[0][0] + points[2][0] + box.height * 0.6) / 2;
9063         } else {
9064             yPos = points[2][1];
9065             if (d.value < 0 || d.value === 0 && !$$.hasPositiveValue) {
9066                 yPos += box.height;
9067                 if ($$.isBarType(d) && $$.isSafari()) {
9068                     yPos -= 3;
9069                 } else if (!$$.isBarType(d) && $$.isChrome()) {
9070                     yPos += 3;
9071                 }
9072             } else {
9073                 yPos += $$.isBarType(d) ? -3 : -6;
9074             }
9075         }
9076         // show labels regardless of the domain if value is null
9077         if (d.value === null && !$$.config.axis_rotated) {
9078             if (yPos < box.height) {
9079                 yPos = box.height;
9080             } else if (yPos > this.height) {
9081                 yPos = this.height - 4;
9082             }
9083         }
9084         return yPos;
9085     };
9086
9087     ChartInternal.prototype.initTitle = function () {
9088         var $$ = this;
9089         $$.title = $$.svg.append("text").text($$.config.title_text).attr("class", $$.CLASS.title);
9090     };
9091     ChartInternal.prototype.redrawTitle = function () {
9092         var $$ = this;
9093         $$.title.attr("x", $$.xForTitle.bind($$)).attr("y", $$.yForTitle.bind($$));
9094     };
9095     ChartInternal.prototype.xForTitle = function () {
9096         var $$ = this,
9097             config = $$.config,
9098             position = config.title_position || 'left',
9099             x;
9100         if (position.indexOf('right') >= 0) {
9101             x = $$.currentWidth - $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()).width - config.title_padding.right;
9102         } else if (position.indexOf('center') >= 0) {
9103             x = ($$.currentWidth - $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()).width) / 2;
9104         } else {
9105             // left
9106             x = config.title_padding.left;
9107         }
9108         return x;
9109     };
9110     ChartInternal.prototype.yForTitle = function () {
9111         var $$ = this;
9112         return $$.config.title_padding.top + $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()).height;
9113     };
9114     ChartInternal.prototype.getTitlePadding = function () {
9115         var $$ = this;
9116         return $$.yForTitle() + $$.config.title_padding.bottom;
9117     };
9118
9119     ChartInternal.prototype.initTooltip = function () {
9120         var $$ = this,
9121             config = $$.config,
9122             i;
9123         $$.tooltip = $$.selectChart.style("position", "relative").append("div").attr('class', CLASS.tooltipContainer).style("position", "absolute").style("pointer-events", "none").style("display", "none");
9124         // Show tooltip if needed
9125         if (config.tooltip_init_show) {
9126             if ($$.isTimeSeries() && isString(config.tooltip_init_x)) {
9127                 config.tooltip_init_x = $$.parseDate(config.tooltip_init_x);
9128                 for (i = 0; i < $$.data.targets[0].values.length; i++) {
9129                     if ($$.data.targets[0].values[i].x - config.tooltip_init_x === 0) {
9130                         break;
9131                     }
9132                 }
9133                 config.tooltip_init_x = i;
9134             }
9135             $$.tooltip.html(config.tooltip_contents.call($$, $$.data.targets.map(function (d) {
9136                 return $$.addName(d.values[config.tooltip_init_x]);
9137             }), $$.axis.getXAxisTickFormat(), $$.getYFormat($$.hasArcType()), $$.color));
9138             $$.tooltip.style("top", config.tooltip_init_position.top).style("left", config.tooltip_init_position.left).style("display", "block");
9139         }
9140     };
9141     ChartInternal.prototype.getTooltipSortFunction = function () {
9142         var $$ = this,
9143             config = $$.config;
9144
9145         if (config.data_groups.length === 0 || config.tooltip_order !== undefined) {
9146             // if data are not grouped or if an order is specified
9147             // for the tooltip values we sort them by their values
9148
9149             var order = config.tooltip_order;
9150             if (order === undefined) {
9151                 order = config.data_order;
9152             }
9153
9154             var valueOf = function valueOf(obj) {
9155                 return obj ? obj.value : null;
9156             };
9157
9158             // if data are not grouped, we sort them by their value
9159             if (isString(order) && order.toLowerCase() === 'asc') {
9160                 return function (a, b) {
9161                     return valueOf(a) - valueOf(b);
9162                 };
9163             } else if (isString(order) && order.toLowerCase() === 'desc') {
9164                 return function (a, b) {
9165                     return valueOf(b) - valueOf(a);
9166                 };
9167             } else if (isFunction(order)) {
9168
9169                 // if the function is from data_order we need
9170                 // to wrap the returned function in order to format
9171                 // the sorted value to the expected format
9172
9173                 var sortFunction = order;
9174
9175                 if (config.tooltip_order === undefined) {
9176                     sortFunction = function sortFunction(a, b) {
9177                         return order(a ? {
9178                             id: a.id,
9179                             values: [a]
9180                         } : null, b ? {
9181                             id: b.id,
9182                             values: [b]
9183                         } : null);
9184                     };
9185                 }
9186
9187                 return sortFunction;
9188             } else if (isArray(order)) {
9189                 return function (a, b) {
9190                     return order.indexOf(a.id) - order.indexOf(b.id);
9191                 };
9192             }
9193         } else {
9194             // if data are grouped, we follow the order of grouped targets
9195             var ids = $$.orderTargets($$.data.targets).map(function (i) {
9196                 return i.id;
9197             });
9198
9199             // if it was either asc or desc we need to invert the order
9200             // returned by orderTargets
9201             if ($$.isOrderAsc() || $$.isOrderDesc()) {
9202                 ids = ids.reverse();
9203             }
9204
9205             return function (a, b) {
9206                 return ids.indexOf(a.id) - ids.indexOf(b.id);
9207             };
9208         }
9209     };
9210     ChartInternal.prototype.getTooltipContent = function (d, defaultTitleFormat, defaultValueFormat, color) {
9211         var $$ = this,
9212             config = $$.config,
9213             titleFormat = config.tooltip_format_title || defaultTitleFormat,
9214             nameFormat = config.tooltip_format_name || function (name) {
9215             return name;
9216         },
9217             valueFormat = config.tooltip_format_value || defaultValueFormat,
9218             text,
9219             i,
9220             title,
9221             value,
9222             name,
9223             bgcolor;
9224
9225         var tooltipSortFunction = this.getTooltipSortFunction();
9226         if (tooltipSortFunction) {
9227             d.sort(tooltipSortFunction);
9228         }
9229
9230         for (i = 0; i < d.length; i++) {
9231             if (!(d[i] && (d[i].value || d[i].value === 0))) {
9232                 continue;
9233             }
9234
9235             if (!text) {
9236                 title = sanitise(titleFormat ? titleFormat(d[i].x) : d[i].x);
9237                 text = "<table class='" + $$.CLASS.tooltip + "'>" + (title || title === 0 ? "<tr><th colspan='2'>" + title + "</th></tr>" : "");
9238             }
9239
9240             value = sanitise(valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index, d));
9241             if (value !== undefined) {
9242                 // Skip elements when their name is set to null
9243                 if (d[i].name === null) {
9244                     continue;
9245                 }
9246                 name = sanitise(nameFormat(d[i].name, d[i].ratio, d[i].id, d[i].index));
9247                 bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id);
9248
9249                 text += "<tr class='" + $$.CLASS.tooltipName + "-" + $$.getTargetSelectorSuffix(d[i].id) + "'>";
9250                 text += "<td class='name'><span style='background-color:" + bgcolor + "'></span>" + name + "</td>";
9251                 text += "<td class='value'>" + value + "</td>";
9252                 text += "</tr>";
9253             }
9254         }
9255         return text + "</table>";
9256     };
9257     ChartInternal.prototype.tooltipPosition = function (dataToShow, tWidth, tHeight, element) {
9258         var $$ = this,
9259             config = $$.config,
9260             d3 = $$.d3;
9261         var svgLeft, tooltipLeft, tooltipRight, tooltipTop, chartRight;
9262         var forArc = $$.hasArcType(),
9263             mouse = d3.mouse(element);
9264         // Determin tooltip position
9265         if (forArc) {
9266             tooltipLeft = ($$.width - ($$.isLegendRight ? $$.getLegendWidth() : 0)) / 2 + mouse[0];
9267             tooltipTop = ($$.hasType('gauge') ? $$.height : $$.height / 2) + mouse[1] + 20;
9268         } else {
9269             svgLeft = $$.getSvgLeft(true);
9270             if (config.axis_rotated) {
9271                 tooltipLeft = svgLeft + mouse[0] + 100;
9272                 tooltipRight = tooltipLeft + tWidth;
9273                 chartRight = $$.currentWidth - $$.getCurrentPaddingRight();
9274                 tooltipTop = $$.x(dataToShow[0].x) + 20;
9275             } else {
9276                 tooltipLeft = svgLeft + $$.getCurrentPaddingLeft(true) + $$.x(dataToShow[0].x) + 20;
9277                 tooltipRight = tooltipLeft + tWidth;
9278                 chartRight = svgLeft + $$.currentWidth - $$.getCurrentPaddingRight();
9279                 tooltipTop = mouse[1] + 15;
9280             }
9281
9282             if (tooltipRight > chartRight) {
9283                 // 20 is needed for Firefox to keep tooltip width
9284                 tooltipLeft -= tooltipRight - chartRight + 20;
9285             }
9286             if (tooltipTop + tHeight > $$.currentHeight) {
9287                 tooltipTop -= tHeight + 30;
9288             }
9289         }
9290         if (tooltipTop < 0) {
9291             tooltipTop = 0;
9292         }
9293         return {
9294             top: tooltipTop,
9295             left: tooltipLeft
9296         };
9297     };
9298     ChartInternal.prototype.showTooltip = function (selectedData, element) {
9299         var $$ = this,
9300             config = $$.config;
9301         var tWidth, tHeight, position;
9302         var forArc = $$.hasArcType(),
9303             dataToShow = selectedData.filter(function (d) {
9304             return d && isValue(d.value);
9305         }),
9306             positionFunction = config.tooltip_position || ChartInternal.prototype.tooltipPosition;
9307         if (dataToShow.length === 0 || !config.tooltip_show) {
9308             return;
9309         }
9310         $$.tooltip.html(config.tooltip_contents.call($$, selectedData, $$.axis.getXAxisTickFormat(), $$.getYFormat(forArc), $$.color)).style("display", "block");
9311
9312         // Get tooltip dimensions
9313         tWidth = $$.tooltip.property('offsetWidth');
9314         tHeight = $$.tooltip.property('offsetHeight');
9315
9316         position = positionFunction.call(this, dataToShow, tWidth, tHeight, element);
9317         // Set tooltip
9318         $$.tooltip.style("top", position.top + "px").style("left", position.left + 'px');
9319     };
9320     ChartInternal.prototype.hideTooltip = function () {
9321         this.tooltip.style("display", "none");
9322     };
9323
9324     ChartInternal.prototype.setTargetType = function (targetIds, type) {
9325         var $$ = this,
9326             config = $$.config;
9327         $$.mapToTargetIds(targetIds).forEach(function (id) {
9328             $$.withoutFadeIn[id] = type === config.data_types[id];
9329             config.data_types[id] = type;
9330         });
9331         if (!targetIds) {
9332             config.data_type = type;
9333         }
9334     };
9335     ChartInternal.prototype.hasType = function (type, targets) {
9336         var $$ = this,
9337             types = $$.config.data_types,
9338             has = false;
9339         targets = targets || $$.data.targets;
9340         if (targets && targets.length) {
9341             targets.forEach(function (target) {
9342                 var t = types[target.id];
9343                 if (t && t.indexOf(type) >= 0 || !t && type === 'line') {
9344                     has = true;
9345                 }
9346             });
9347         } else if (Object.keys(types).length) {
9348             Object.keys(types).forEach(function (id) {
9349                 if (types[id] === type) {
9350                     has = true;
9351                 }
9352             });
9353         } else {
9354             has = $$.config.data_type === type;
9355         }
9356         return has;
9357     };
9358     ChartInternal.prototype.hasArcType = function (targets) {
9359         return this.hasType('pie', targets) || this.hasType('donut', targets) || this.hasType('gauge', targets);
9360     };
9361     ChartInternal.prototype.isLineType = function (d) {
9362         var config = this.config,
9363             id = isString(d) ? d : d.id;
9364         return !config.data_types[id] || ['line', 'spline', 'area', 'area-spline', 'step', 'area-step'].indexOf(config.data_types[id]) >= 0;
9365     };
9366     ChartInternal.prototype.isStepType = function (d) {
9367         var id = isString(d) ? d : d.id;
9368         return ['step', 'area-step'].indexOf(this.config.data_types[id]) >= 0;
9369     };
9370     ChartInternal.prototype.isSplineType = function (d) {
9371         var id = isString(d) ? d : d.id;
9372         return ['spline', 'area-spline'].indexOf(this.config.data_types[id]) >= 0;
9373     };
9374     ChartInternal.prototype.isAreaType = function (d) {
9375         var id = isString(d) ? d : d.id;
9376         return ['area', 'area-spline', 'area-step'].indexOf(this.config.data_types[id]) >= 0;
9377     };
9378     ChartInternal.prototype.isBarType = function (d) {
9379         var id = isString(d) ? d : d.id;
9380         return this.config.data_types[id] === 'bar';
9381     };
9382     ChartInternal.prototype.isScatterType = function (d) {
9383         var id = isString(d) ? d : d.id;
9384         return this.config.data_types[id] === 'scatter';
9385     };
9386     ChartInternal.prototype.isPieType = function (d) {
9387         var id = isString(d) ? d : d.id;
9388         return this.config.data_types[id] === 'pie';
9389     };
9390     ChartInternal.prototype.isGaugeType = function (d) {
9391         var id = isString(d) ? d : d.id;
9392         return this.config.data_types[id] === 'gauge';
9393     };
9394     ChartInternal.prototype.isDonutType = function (d) {
9395         var id = isString(d) ? d : d.id;
9396         return this.config.data_types[id] === 'donut';
9397     };
9398     ChartInternal.prototype.isArcType = function (d) {
9399         return this.isPieType(d) || this.isDonutType(d) || this.isGaugeType(d);
9400     };
9401     ChartInternal.prototype.lineData = function (d) {
9402         return this.isLineType(d) ? [d] : [];
9403     };
9404     ChartInternal.prototype.arcData = function (d) {
9405         return this.isArcType(d.data) ? [d] : [];
9406     };
9407     /* not used
9408      function scatterData(d) {
9409      return isScatterType(d) ? d.values : [];
9410      }
9411      */
9412     ChartInternal.prototype.barData = function (d) {
9413         return this.isBarType(d) ? d.values : [];
9414     };
9415     ChartInternal.prototype.lineOrScatterData = function (d) {
9416         return this.isLineType(d) || this.isScatterType(d) ? d.values : [];
9417     };
9418     ChartInternal.prototype.barOrLineData = function (d) {
9419         return this.isBarType(d) || this.isLineType(d) ? d.values : [];
9420     };
9421
9422     ChartInternal.prototype.isSafari = function () {
9423         var ua = window.navigator.userAgent;
9424         return ua.indexOf('Safari') >= 0 && ua.indexOf('Chrome') < 0;
9425     };
9426     ChartInternal.prototype.isChrome = function () {
9427         var ua = window.navigator.userAgent;
9428         return ua.indexOf('Chrome') >= 0;
9429     };
9430
9431     ChartInternal.prototype.initZoom = function () {
9432         var $$ = this,
9433             d3 = $$.d3,
9434             config = $$.config,
9435             startEvent;
9436
9437         $$.zoom = d3.zoom().on("start", function () {
9438             if (config.zoom_type !== 'scroll') {
9439                 return;
9440             }
9441
9442             var e = d3.event.sourceEvent;
9443             if (e && e.type === "brush") {
9444                 return;
9445             }
9446             startEvent = e;
9447             config.zoom_onzoomstart.call($$.api, e);
9448         }).on("zoom", function () {
9449             if (config.zoom_type !== 'scroll') {
9450                 return;
9451             }
9452
9453             var e = d3.event.sourceEvent;
9454             if (e && e.type === "brush") {
9455                 return;
9456             }
9457
9458             $$.redrawForZoom();
9459
9460             config.zoom_onzoom.call($$.api, $$.x.orgDomain());
9461         }).on('end', function () {
9462             if (config.zoom_type !== 'scroll') {
9463                 return;
9464             }
9465
9466             var e = d3.event.sourceEvent;
9467             if (e && e.type === "brush") {
9468                 return;
9469             }
9470             // if click, do nothing. otherwise, click interaction will be canceled.
9471             if (e && startEvent.clientX === e.clientX && startEvent.clientY === e.clientY) {
9472                 return;
9473             }
9474             config.zoom_onzoomend.call($$.api, $$.x.orgDomain());
9475         });
9476
9477         $$.zoom.updateDomain = function () {
9478             if (d3.event && d3.event.transform) {
9479                 $$.x.domain(d3.event.transform.rescaleX($$.subX).domain());
9480             }
9481             return this;
9482         };
9483         $$.zoom.updateExtent = function () {
9484             this.scaleExtent([1, Infinity]).translateExtent([[0, 0], [$$.width, $$.height]]).extent([[0, 0], [$$.width, $$.height]]);
9485             return this;
9486         };
9487         $$.zoom.update = function () {
9488             return this.updateExtent().updateDomain();
9489         };
9490
9491         return $$.zoom.updateExtent();
9492     };
9493     ChartInternal.prototype.zoomTransform = function (range) {
9494         var $$ = this,
9495             s = [$$.x(range[0]), $$.x(range[1])];
9496         return $$.d3.zoomIdentity.scale($$.width / (s[1] - s[0])).translate(-s[0], 0);
9497     };
9498
9499     ChartInternal.prototype.initDragZoom = function () {
9500         var $$ = this;
9501         var d3 = $$.d3;
9502         var config = $$.config;
9503         var context = $$.context = $$.svg;
9504         var brushXPos = $$.margin.left + 20.5;
9505         var brushYPos = $$.margin.top + 0.5;
9506
9507         if (!(config.zoom_type === 'drag' && config.zoom_enabled)) {
9508             return;
9509         }
9510
9511         var getZoomedDomain = function getZoomedDomain(selection) {
9512             return selection && selection.map(function (x) {
9513                 return $$.x.invert(x);
9514             });
9515         };
9516
9517         var brush = $$.dragZoomBrush = d3.brushX().on("start", function () {
9518             $$.api.unzoom();
9519
9520             $$.svg.select("." + CLASS.dragZoom).classed("disabled", false);
9521
9522             config.zoom_onzoomstart.call($$.api, d3.event.sourceEvent);
9523         }).on("brush", function () {
9524             config.zoom_onzoom.call($$.api, getZoomedDomain(d3.event.selection));
9525         }).on("end", function () {
9526             if (d3.event.selection == null) {
9527                 return;
9528             }
9529
9530             var zoomedDomain = getZoomedDomain(d3.event.selection);
9531
9532             if (!config.zoom_disableDefaultBehavior) {
9533                 $$.api.zoom(zoomedDomain);
9534             }
9535
9536             $$.svg.select("." + CLASS.dragZoom).classed("disabled", true);
9537
9538             config.zoom_onzoomend.call($$.api, zoomedDomain);
9539         });
9540
9541         context.append("g").classed(CLASS.dragZoom, true).attr("clip-path", $$.clipPath).attr("transform", "translate(" + brushXPos + "," + brushYPos + ")").call(brush);
9542     };
9543
9544     ChartInternal.prototype.getZoomDomain = function () {
9545         var $$ = this,
9546             config = $$.config,
9547             d3 = $$.d3,
9548             min = d3.min([$$.orgXDomain[0], config.zoom_x_min]),
9549             max = d3.max([$$.orgXDomain[1], config.zoom_x_max]);
9550         return [min, max];
9551     };
9552     ChartInternal.prototype.redrawForZoom = function () {
9553         var $$ = this,
9554             d3 = $$.d3,
9555             config = $$.config,
9556             zoom = $$.zoom,
9557             x = $$.x;
9558         if (!config.zoom_enabled) {
9559             return;
9560         }
9561         if ($$.filterTargetsToShow($$.data.targets).length === 0) {
9562             return;
9563         }
9564
9565         zoom.update();
9566
9567         if (config.zoom_disableDefaultBehavior) {
9568             return;
9569         }
9570
9571         if ($$.isCategorized() && x.orgDomain()[0] === $$.orgXDomain[0]) {
9572             x.domain([$$.orgXDomain[0] - 1e-10, x.orgDomain()[1]]);
9573         }
9574
9575         $$.redraw({
9576             withTransition: false,
9577             withY: config.zoom_rescale,
9578             withSubchart: false,
9579             withEventRect: false,
9580             withDimension: false
9581         });
9582
9583         if (d3.event.sourceEvent && d3.event.sourceEvent.type === 'mousemove') {
9584             $$.cancelClick = true;
9585         }
9586     };
9587
9588     return c3;
9589
9590 })));