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';
8 function ChartInternal(api) {
10 $$.d3 = window.d3 ? window.d3 : typeof require !== 'undefined' ? require("d3") : undefined;
12 $$.config = $$.getDefaultConfig();
18 function Chart(config) {
19 var $$ = this.internal = new ChartInternal(this);
20 $$.loadConfig(config);
22 $$.beforeInit(config);
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);
34 })(Chart.prototype, this, this);
37 function AxisInternal(component, params) {
39 internal.component = component;
40 internal.params = params || {};
42 internal.d3 = component.d3;
43 internal.scale = internal.d3.scaleLinear();
45 internal.orient = "bottom";
46 internal.innerTickSize = 6;
47 internal.outerTickSize = this.params.withOuterTick ? 6 : 0;
48 internal.tickPadding = 3;
49 internal.tickValues = null;
51 internal.tickArguments;
53 internal.tickOffset = 0;
54 internal.tickCulling = true;
55 internal.tickCentered;
56 internal.tickTextCharSize;
57 internal.tickTextRotate = internal.params.tickTextRotate;
60 internal.axis = internal.generateAxis();
63 AxisInternal.prototype.axisX = function (selection, x, tickOffset) {
64 selection.attr("transform", function (d) {
65 return "translate(" + Math.ceil(x(d) + tickOffset) + ", 0)";
68 AxisInternal.prototype.axisY = function (selection, y) {
69 selection.attr("transform", function (d) {
70 return "translate(0," + Math.ceil(y(d)) + ")";
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];
78 AxisInternal.prototype.generateTicks = function (scale) {
84 return scale.ticks.apply(scale, internal.tickArguments);
86 domain = scale.domain();
87 for (i = Math.ceil(domain[0]); i < domain[1]; i++) {
90 if (ticks.length > 0 && ticks[0] > 0) {
91 ticks.unshift(ticks[0] - (ticks[1] - ticks[0]));
95 AxisInternal.prototype.copyScale = function () {
97 var newScale = internal.scale.copy(),
99 if (internal.params.isCategory) {
100 domain = internal.scale.domain();
101 newScale.domain([domain[0], domain[1] - 1]);
105 AxisInternal.prototype.textFormatted = function (v) {
107 formatted = internal.tickFormat ? internal.tickFormat(v) : v;
108 return typeof formatted !== 'undefined' ? formatted : '';
110 AxisInternal.prototype.updateRange = function () {
112 internal.range = internal.scale.rangeExtent ? internal.scale.rangeExtent() : internal.scaleExtent(internal.scale.range());
113 return internal.range;
115 AxisInternal.prototype.updateTickTextCharSize = function (tick) {
117 if (internal.tickTextCharSize) {
118 return internal.tickTextCharSize;
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),
130 w = text ? box.width / text.length : undefined;
136 internal.tickTextCharSize = size;
139 AxisInternal.prototype.isVertical = function () {
140 return this.orient === 'left' || this.orient === 'right';
142 AxisInternal.prototype.tspanData = function (d, i, scale) {
144 var splitted = internal.params.tickMultiline ? internal.splitTickText(d, scale) : [].concat(internal.textFormatted(d));
146 if (internal.params.tickMultiline && internal.params.tickMultilineMax > 0) {
147 splitted = internal.ellipsify(splitted, internal.params.tickMultilineMax);
150 return splitted.map(function (s) {
151 return { index: i, splitted: s, length: splitted.length };
154 AxisInternal.prototype.splitTickText = function (d, scale) {
156 tickText = internal.textFormatted(d),
157 maxWidth = internal.params.tickWidth,
163 if (Object.prototype.toString.call(tickText) === "[object Array]") {
167 if (!maxWidth || maxWidth <= 0) {
168 maxWidth = internal.isVertical() ? 95 : internal.params.isCategory ? Math.ceil(scale(1) - scale(0)) - 12 : 110;
171 function split(splitted, text) {
172 spaceIndex = undefined;
173 for (var i = 1; i < text.length; i++) {
174 if (text.charAt(i) === ' ') {
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));
184 return splitted.concat(text);
187 return split(splitted, tickText + "");
189 AxisInternal.prototype.ellipsify = function (splitted, max) {
190 if (splitted.length <= max) {
194 var ellipsified = splitted.slice(0, max);
196 for (var i = max - 1; i >= 0; i--) {
197 var available = ellipsified[i].length;
199 ellipsified[i] = ellipsified[i].substr(0, available - remaining).padEnd(available, '.');
201 remaining -= available;
203 if (remaining <= 0) {
210 AxisInternal.prototype.updateTickLength = function () {
212 internal.tickLength = Math.max(internal.innerTickSize, 0) + internal.tickPadding;
214 AxisInternal.prototype.lineY2 = function (d) {
216 tickPosition = internal.scale(d) + (internal.tickCentered ? 0 : internal.tickOffset);
217 return internal.range[0] < tickPosition && tickPosition < internal.range[1] ? internal.innerTickSize : 0;
219 AxisInternal.prototype.textY = function () {
221 rotate = internal.tickTextRotate;
222 return rotate ? 11.5 - 2.5 * (rotate / 15) * (rotate > 0 ? 1 : -1) : internal.tickLength;
224 AxisInternal.prototype.textTransform = function () {
226 rotate = internal.tickTextRotate;
227 return rotate ? "rotate(" + rotate + ")" : "";
229 AxisInternal.prototype.textTextAnchor = function () {
231 rotate = internal.tickTextRotate;
232 return rotate ? rotate > 0 ? "start" : "end" : "middle";
234 AxisInternal.prototype.tspanDx = function () {
236 rotate = internal.tickTextRotate;
237 return rotate ? 8 * Math.sin(Math.PI * (rotate / 180)) : 0;
239 AxisInternal.prototype.tspanDy = function (d, i) {
241 dy = internal.tickTextCharSize.h;
243 if (internal.isVertical()) {
244 dy = -((d.length - 1) * (internal.tickTextCharSize.h / 2) - 3);
252 AxisInternal.prototype.generateAxis = function () {
255 params = internal.params;
256 function axis(g, transition) {
259 var g = axis.g = d3.select(this);
261 var scale0 = this.__chart__ || internal.scale,
262 scale1 = this.__chart__ = internal.copyScale();
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),
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),
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;
280 internal.tickOffset = tickX = 0;
283 internal.updateRange();
284 internal.updateTickLength();
285 internal.updateTickTextCharSize(g.select('.tick'));
287 var lineUpdate = tickUpdate.select("line").merge(tickEnter.append("line")),
288 textUpdate = tickUpdate.select("text").merge(tickEnter.append("text"));
290 var tspans = tickUpdate.selectAll('text').selectAll('tspan').data(function (d, i) {
291 return internal.tspanData(d, i, scale1);
293 tspanEnter = tspans.enter().append('tspan'),
294 tspanUpdate = tspanEnter.merge(tspans).text(function (d) {
297 tspans.exit().remove();
299 var path = g.selectAll(".domain").data([0]),
300 pathUpdate = path.enter().append("path").merge(path).attr("class", "domain");
302 // TODO: each attr should be one function and change its behavior by internal.orient, probably
303 switch (internal.orient) {
306 tickTransform = internal.axisX;
307 lineUpdate.attr("x1", tickX).attr("x2", tickX).attr("y2", function (d, i) {
308 return internal.lineY2(d, i);
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);
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);
322 pathUpdate.attr("d", "M" + internal.range[0] + "," + internal.outerTickSize + "V0H" + internal.range[1] + "V" + internal.outerTickSize);
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);
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);
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);
344 pathUpdate.attr("d", "M" + internal.range[0] + "," + -internal.outerTickSize + "V0H" + internal.range[1] + "V" + -internal.outerTickSize);
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);
355 pathUpdate.attr("d", "M" + -internal.outerTickSize + "," + internal.range[0] + "H0V" + internal.range[1] + "H" + -internal.outerTickSize);
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);
366 pathUpdate.attr("d", "M" + internal.outerTickSize + "," + internal.range[0] + "H0V" + internal.range[1] + "H" + internal.outerTickSize);
370 if (scale1.rangeBand) {
372 dx = x.rangeBand() / 2;
373 scale0 = scale1 = function scale1(d) {
376 } else if (scale0.rangeBand) {
379 tickExit.call(tickTransform, scale1, internal.tickOffset);
381 tickEnter.call(tickTransform, scale0, internal.tickOffset);
382 self = (transition ? tickUpdate.transition(transition) : tickUpdate).style('opacity', 1).call(tickTransform, scale1, internal.tickOffset);
386 axis.scale = function (x) {
387 if (!arguments.length) {
388 return internal.scale;
393 axis.orient = function (x) {
394 if (!arguments.length) {
395 return internal.orient;
397 internal.orient = x in { top: 1, right: 1, bottom: 1, left: 1 } ? x + "" : "bottom";
400 axis.tickFormat = function (format) {
401 if (!arguments.length) {
402 return internal.tickFormat;
404 internal.tickFormat = format;
407 axis.tickCentered = function (isCentered) {
408 if (!arguments.length) {
409 return internal.tickCentered;
411 internal.tickCentered = isCentered;
414 axis.tickOffset = function () {
415 return internal.tickOffset;
417 axis.tickInterval = function () {
418 var interval, length;
419 if (params.isCategory) {
420 interval = internal.tickOffset * 2;
422 length = axis.g.select('path.domain').node().getTotalLength() - internal.outerTickSize * 2;
423 interval = length / axis.g.selectAll('line').size();
425 return interval === Infinity ? 0 : interval;
427 axis.ticks = function () {
428 if (!arguments.length) {
429 return internal.tickArguments;
431 internal.tickArguments = arguments;
434 axis.tickCulling = function (culling) {
435 if (!arguments.length) {
436 return internal.tickCulling;
438 internal.tickCulling = culling;
441 axis.tickValues = function (x) {
442 if (typeof x === 'function') {
443 internal.tickValues = function () {
444 return x(internal.scale.domain());
447 if (!arguments.length) {
448 return internal.tickValues;
450 internal.tickValues = x;
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',
481 dragZoom: 'c3-drag-zoom',
482 focused: 'c3-focused',
483 defocused: 'c3-defocused',
485 regions: 'c3-regions',
487 tooltipContainer: 'c3-tooltip-container',
488 tooltip: 'c3-tooltip',
489 tooltipName: 'c3-tooltip-name',
497 circles: 'c3-circles',
499 arcLabelLine: 'c3-arc-label-line',
506 gaugeValue: 'c3-gauge-value',
508 gridLines: 'c3-grid-lines',
511 xgridLine: 'c3-xgrid-line',
512 xgridLines: 'c3-xgrid-lines',
513 xgridFocus: 'c3-xgrid-focus',
516 ygridLine: 'c3-ygrid-line',
517 ygridLines: 'c3-ygrid-lines',
520 axisXLabel: 'c3-axis-x-label',
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_'
537 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
540 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
543 var classCallCheck = function (instance, Constructor) {
544 if (!(instance instanceof Constructor)) {
545 throw new TypeError("Cannot call a class as a function");
549 var defineProperty = function (obj, key, value) {
551 Object.defineProperty(obj, key, {
564 var asHalfPixel = function asHalfPixel(n) {
565 return Math.ceil(n) + 0.5;
567 var ceil10 = function ceil10(v) {
568 return Math.ceil(v / 10) * 10;
570 var diffDomain = function diffDomain(d) {
573 var getOption = function getOption(options, key, defaultValue) {
574 return isDefined(options[key]) ? options[key] : defaultValue;
576 var getPathBox = function getPathBox(path) {
577 var box = path.getBoundingClientRect(),
578 items = [path.pathSegList.getItem(0), path.pathSegList.getItem(1)],
580 minY = Math.min(items[0].y, items[1].y);
581 return { x: minX, y: minY, width: box.width, height: box.height };
583 var hasValue = function hasValue(dict, value) {
585 Object.keys(dict).forEach(function (key) {
586 if (dict[key] === value) {
592 var isArray = function isArray(o) {
593 return Array.isArray(o);
595 var isDefined = function isDefined(v) {
596 return typeof v !== 'undefined';
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;
601 var isFunction = function isFunction(o) {
602 return typeof o === 'function';
604 var isString = function isString(o) {
605 return typeof o === 'string';
607 var isUndefined = function isUndefined(v) {
608 return typeof v === 'undefined';
610 var isValue = function isValue(v) {
613 var notEmpty = function notEmpty(o) {
616 var sanitise = function sanitise(str) {
617 return typeof str === 'string' ? str.replace(/</g, '<').replace(/>/g, '>') : str;
620 var Axis = function Axis(owner) {
621 classCallCheck(this, Axis);
625 this.internal = AxisInternal;
628 Axis.prototype.init = function init() {
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));
637 $$.axes.y2 = main.append("g").attr("class", CLASS.axis + ' ' + CLASS.axisY2)
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));
642 Axis.prototype.getXAxis = function getXAxis(scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition, withoutRotateTickText) {
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
654 axis = new this.internal(this, axisParams).axis.scale(scale).orient(orient);
656 if ($$.isTimeSeries() && tickValues && typeof tickValues !== "function") {
657 tickValues = tickValues.map(function (v) {
658 return $$.parseDate(v);
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;
673 Axis.prototype.updateXAxisTickValues = function updateXAxisTickValues(targets, axis) {
677 if (config.axis_x_tick_fit || config.axis_x_tick_count) {
678 tickValues = this.generateTickValues($$.mapTargetsToUniqueXs(targets), config.axis_x_tick_count, $$.isTimeSeries());
681 axis.tickValues(tickValues);
683 $$.xAxis.tickValues(tickValues);
684 $$.subXAxis.tickValues(tickValues);
688 Axis.prototype.getYAxis = function getYAxis(scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition, withoutRotateTickText) {
692 withOuterTick: withOuterTick,
693 withoutTransition: withoutTransition,
694 tickTextRotate: withoutRotateTickText ? 0 : config.axis_y_tick_rotate
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);
700 axis.tickValues(tickValues);
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';
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
713 var format = $$.isTimeSeries() ? $$.defaultAxisTimeFormat : $$.isCategorized() ? $$.categoryName : function (v) {
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) : "";
726 return isFunction(format) ? function (v) {
727 return format.call($$, v);
730 Axis.prototype.getTickValues = function getTickValues(tickValues, axis) {
731 return tickValues ? tickValues : axis ? axis.tickValues() : undefined;
733 Axis.prototype.getXAxisTickValues = function getXAxisTickValues() {
734 return this.getTickValues(this.owner.config.axis_x_tick_values, this.owner.xAxis);
736 Axis.prototype.getYAxisTickValues = function getYAxisTickValues() {
737 return this.getTickValues(this.owner.config.axis_y_tick_values, this.owner.yAxis);
739 Axis.prototype.getY2AxisTickValues = function getY2AxisTickValues() {
740 return this.getTickValues(this.owner.config.axis_y2_tick_values, this.owner.y2Axis);
742 Axis.prototype.getLabelOptionByAxisId = function getLabelOptionByAxisId(axisId) {
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;
755 Axis.prototype.getLabelText = function getLabelText(axisId) {
756 var option = this.getLabelOptionByAxisId(axisId);
757 return isString(option) ? option : option ? option.text : null;
759 Axis.prototype.setLabelText = function setLabelText(axisId, text) {
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;
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;
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
789 Axis.prototype.getXAxisLabelPosition = function getXAxisLabelPosition() {
790 return this.getLabelPosition('x', this.owner.config.axis_rotated ? 'inner-top' : 'inner-right');
792 Axis.prototype.getYAxisLabelPosition = function getYAxisLabelPosition() {
793 return this.getLabelPosition('y', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top');
795 Axis.prototype.getY2AxisLabelPosition = function getY2AxisLabelPosition() {
796 return this.getLabelPosition('y2', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top');
798 Axis.prototype.getLabelPositionById = function getLabelPositionById(id) {
799 return id === 'y2' ? this.getY2AxisLabelPosition() : id === 'y' ? this.getYAxisLabelPosition() : this.getXAxisLabelPosition();
801 Axis.prototype.textForXAxisLabel = function textForXAxisLabel() {
802 return this.getLabelText('x');
804 Axis.prototype.textForYAxisLabel = function textForYAxisLabel() {
805 return this.getLabelText('y');
807 Axis.prototype.textForY2AxisLabel = function textForY2AxisLabel() {
808 return this.getLabelText('y2');
810 Axis.prototype.xForAxisLabel = function xForAxisLabel(forHorizontal, position) {
813 return position.isLeft ? 0 : position.isCenter ? $$.width / 2 : $$.width;
815 return position.isBottom ? -$$.height : position.isMiddle ? -$$.height / 2 : 0;
818 Axis.prototype.dxForAxisLabel = function dxForAxisLabel(forHorizontal, position) {
820 return position.isLeft ? "0.5em" : position.isRight ? "-0.5em" : "0";
822 return position.isTop ? "-0.5em" : position.isBottom ? "0.5em" : "0";
825 Axis.prototype.textAnchorForAxisLabel = function textAnchorForAxisLabel(forHorizontal, position) {
827 return position.isLeft ? 'start' : position.isCenter ? 'middle' : 'end';
829 return position.isBottom ? 'start' : position.isMiddle ? 'middle' : 'end';
832 Axis.prototype.xForXAxisLabel = function xForXAxisLabel() {
833 return this.xForAxisLabel(!this.owner.config.axis_rotated, this.getXAxisLabelPosition());
835 Axis.prototype.xForYAxisLabel = function xForYAxisLabel() {
836 return this.xForAxisLabel(this.owner.config.axis_rotated, this.getYAxisLabelPosition());
838 Axis.prototype.xForY2AxisLabel = function xForY2AxisLabel() {
839 return this.xForAxisLabel(this.owner.config.axis_rotated, this.getY2AxisLabelPosition());
841 Axis.prototype.dxForXAxisLabel = function dxForXAxisLabel() {
842 return this.dxForAxisLabel(!this.owner.config.axis_rotated, this.getXAxisLabelPosition());
844 Axis.prototype.dxForYAxisLabel = function dxForYAxisLabel() {
845 return this.dxForAxisLabel(this.owner.config.axis_rotated, this.getYAxisLabelPosition());
847 Axis.prototype.dxForY2AxisLabel = function dxForY2AxisLabel() {
848 return this.dxForAxisLabel(this.owner.config.axis_rotated, this.getY2AxisLabelPosition());
850 Axis.prototype.dyForXAxisLabel = function dyForXAxisLabel() {
853 position = this.getXAxisLabelPosition();
854 if (config.axis_rotated) {
855 return position.isInner ? "1.2em" : -25 - ($$.config.axis_x_inner ? 0 : this.getMaxTickWidth('x'));
857 return position.isInner ? "-0.5em" : config.axis_x_height ? config.axis_x_height - 10 : "3em";
860 Axis.prototype.dyForYAxisLabel = function dyForYAxisLabel() {
862 position = this.getYAxisLabelPosition();
863 if ($$.config.axis_rotated) {
864 return position.isInner ? "-0.5em" : "3em";
866 return position.isInner ? "1.2em" : -10 - ($$.config.axis_y_inner ? 0 : this.getMaxTickWidth('y') + 10);
869 Axis.prototype.dyForY2AxisLabel = function dyForY2AxisLabel() {
871 position = this.getY2AxisLabelPosition();
872 if ($$.config.axis_rotated) {
873 return position.isInner ? "1.2em" : "-2.2em";
875 return position.isInner ? "-0.5em" : 15 + ($$.config.axis_y2_inner ? 0 : this.getMaxTickWidth('y2') + 15);
878 Axis.prototype.textAnchorForXAxisLabel = function textAnchorForXAxisLabel() {
880 return this.textAnchorForAxisLabel(!$$.config.axis_rotated, this.getXAxisLabelPosition());
882 Axis.prototype.textAnchorForYAxisLabel = function textAnchorForYAxisLabel() {
884 return this.textAnchorForAxisLabel($$.config.axis_rotated, this.getYAxisLabelPosition());
886 Axis.prototype.textAnchorForY2AxisLabel = function textAnchorForY2AxisLabel() {
888 return this.textAnchorForAxisLabel($$.config.axis_rotated, this.getY2AxisLabelPosition());
890 Axis.prototype.getMaxTickWidth = function getMaxTickWidth(id, withoutRecompute) {
899 if (withoutRecompute && $$.currentMaxTickWidths[id]) {
900 return $$.currentMaxTickWidths[id];
903 targetsToShow = $$.filterTargetsToShow($$.data.targets);
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);
911 scale = $$.x.copy().domain($$.getXDomain(targetsToShow));
912 axis = this.getXAxis(scale, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, false, true, true);
913 this.updateXAxisTickValues(targetsToShow, axis);
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;
926 $$.currentMaxTickWidths[id] = maxWidth <= 0 ? $$.currentMaxTickWidths[id] : maxWidth;
927 return $$.currentMaxTickWidths[id];
930 Axis.prototype.updateLabels = function updateLabels(withTransition) {
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));
939 Axis.prototype.getPadding = function getPadding(padding, key, defaultValue, domainLength) {
940 var p = typeof padding === 'number' ? padding : padding[key];
944 if (padding.unit === 'ratio') {
945 return padding[key] * domainLength;
947 // assume padding is pixels if unit is not specified
948 return this.convertPixelsToAxisPadding(p, domainLength);
950 Axis.prototype.convertPixelsToAxisPadding = function convertPixelsToAxisPadding(pixels, domainLength) {
952 length = $$.config.axis_rotated ? $$.width : $$.height;
953 return domainLength * (pixels / length);
955 Axis.prototype.generateTickValues = function generateTickValues(values, tickCount, forTimeSeries) {
956 var tickValues = values,
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;
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);
982 tickValues.push(end);
985 if (!forTimeSeries) {
986 tickValues = tickValues.sort(function (a, b) {
992 Axis.prototype.generateTransitions = function generateTransitions(duration) {
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
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);
1014 fn: Chart.prototype,
1016 fn: ChartInternal.prototype,
1020 fn: AxisInternal.prototype
1025 generate: function generate(config) {
1026 return new Chart(config);
1030 ChartInternal.prototype.beforeInit = function () {
1033 ChartInternal.prototype.afterInit = function () {
1036 ChartInternal.prototype.init = function () {
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));
1051 throw Error('url or json or rows or columns is required.');
1055 ChartInternal.prototype.initParams = function () {
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);
1072 $$.dragStart = null;
1073 $$.dragging = false;
1075 $$.cancelClick = false;
1076 $$.mouseover = false;
1077 $$.transiting = false;
1079 $$.color = $$.generateColor();
1080 $$.levelColor = $$.generateLevelColor();
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);
1088 if (date.getSeconds()) {
1089 return d3.timeFormat(":%S")(date);
1091 if (date.getMinutes()) {
1092 return d3.timeFormat("%I:%M")(date);
1094 if (date.getHours()) {
1095 return d3.timeFormat("%I %p")(date);
1097 if (date.getDay() && date.getDate() !== 1) {
1098 return d3.timeFormat("%-m/%-d")(date);
1100 if (date.getDate() !== 1) {
1101 return d3.timeFormat("%-m/%-d")(date);
1103 if (date.getMonth()) {
1104 return d3.timeFormat("%-m/%-d")(date);
1106 return d3.timeFormat("%Y/%-m/%-d")(date);
1108 $$.hiddenTargetIds = [];
1109 $$.hiddenLegendIds = [];
1110 $$.focusedTargetIds = [];
1111 $$.defocusedTargetIds = [];
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";
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';
1123 $$.legendItemWidth = 0;
1124 $$.legendItemHeight = 0;
1126 $$.currentMaxTickWidths = {
1132 $$.rotated_padding_left = 30;
1133 $$.rotated_padding_right = config.axis_rotated && !config.axis_x_show ? 0 : 30;
1134 $$.rotated_padding_top = 5;
1136 $$.withoutFadeIn = {};
1138 $$.intervalForObserveInserted = undefined;
1140 $$.axes.subx = d3.selectAll([]); // needs when excluding subchart.js
1143 ChartInternal.prototype.initChartElements = function () {
1147 if (this.initLine) {
1153 if (this.initGauge) {
1156 if (this.initText) {
1161 ChartInternal.prototype.initWithData = function (data) {
1169 $$.axis = new Axis($$);
1171 if (!config.bindto) {
1172 $$.selectChart = d3.selectAll([]);
1173 } else if (typeof config.bindto.node === 'function') {
1174 $$.selectChart = config.bindto;
1176 $$.selectChart = d3.select(config.bindto);
1178 if ($$.selectChart.empty()) {
1179 $$.selectChart = d3.select(document.createElement('div')).style('opacity', 0);
1180 $$.observeInserted($$.selectChart);
1183 $$.selectChart.html("").classed("c3", true);
1185 // Init data as targets
1187 $$.data.targets = $$.convertDataToTargets(data);
1189 if (config.data_filter) {
1190 $$.data.targets = $$.data.targets.filter(config.data_filter);
1193 // Set targets to hide if needed
1194 if (config.data_hide) {
1195 $$.addHiddenTargetIds(config.data_hide === true ? $$.mapToIds($$.data.targets) : config.data_hide);
1197 if (config.legend_hide) {
1198 $$.addHiddenLegendIds(config.legend_hide === true ? $$.mapToIds($$.data.targets) : config.legend_hide);
1201 // Init sizes and scales
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());
1213 // Save original x domain for zoom update
1214 $$.orgXDomain = $$.x.domain();
1216 /*-- Basic Elements --*/
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($$);
1225 if ($$.config.svg_classname) {
1226 $$.svg.attr('class', $$.config.svg_classname);
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);
1239 main = $$.main = $$.svg.append("g").attr("transform", $$.getTranslate('main'));
1244 if ($$.initDragZoom) {
1247 if ($$.initSubchart) {
1250 if ($$.initTooltip) {
1253 if ($$.initLegend) {
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();
1269 /*-- Main Region --*/
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.
1281 // Define g for chart area
1282 main.append('g').attr("clip-path", $$.clipPath).attr('class', CLASS.chart);
1285 if (config.grid_lines_front) {
1289 // Cover whole with rects for events
1292 // Define g for chart
1293 $$.initChartElements();
1299 $$.updateTargets($$.data.targets);
1301 // Set default extent if defined
1302 if (config.axis_x_selection) {
1303 $$.brush.selectionAsValue($$.getDefaultSelection());
1306 // Draw with targets
1308 $$.updateDimension();
1309 $$.config.oninit.call($$);
1311 withTransition: false,
1312 withTransform: true,
1313 withUpdateXDomain: true,
1314 withUpdateOrgXDomain: true,
1315 withTransitionForAxis: false
1319 // Bind resize event
1322 // export element of the chart
1323 $$.api.element = $$.selectChart.node();
1326 ChartInternal.prototype.smoothLines = function (el, type) {
1328 if (type === 'grid') {
1329 el.each(function () {
1330 var g = $$.d3.select(this),
1336 'x1': Math.ceil(x1),
1337 'x2': Math.ceil(x2),
1338 'y1': Math.ceil(y1),
1345 ChartInternal.prototype.updateSizes = function () {
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;
1355 $$.currentWidth = $$.getCurrentWidth();
1356 $$.currentHeight = $$.getCurrentHeight();
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())
1365 top: 4 + $$.getCurrentPaddingTop(), // for top tick text
1366 right: hasArc ? 0 : $$.getCurrentPaddingRight(),
1367 bottom: xAxisHeight + subchartHeight + legendHeightForBottom + $$.getCurrentPaddingBottom(),
1368 left: hasArc ? 0 : $$.getCurrentPaddingLeft()
1372 $$.margin2 = config.axis_rotated ? {
1375 bottom: 20 + legendHeightForBottom,
1376 left: $$.rotated_padding_left
1378 top: $$.currentHeight - subchartHeight - legendHeightForBottom,
1380 bottom: xAxisHeight + legendHeightForBottom,
1381 left: $$.margin.left
1391 if ($$.updateSizeForLegend) {
1392 $$.updateSizeForLegend(legendHeight, legendWidth);
1395 $$.width = $$.currentWidth - $$.margin.left - $$.margin.right;
1396 $$.height = $$.currentHeight - $$.margin.top - $$.margin.bottom;
1400 if ($$.height < 0) {
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) {
1409 if ($$.height2 < 0) {
1414 $$.arcWidth = $$.width - ($$.isLegendRight ? legendWidth + 10 : 0);
1415 $$.arcHeight = $$.height - ($$.isLegendRight ? 0 : 10);
1416 if ($$.hasType('gauge') && !config.gauge_fullCircle) {
1417 $$.arcHeight += $$.height - $$.getGaugeLabelHeight();
1419 if ($$.updateRadius) {
1423 if ($$.isLegendRight && hasArc) {
1424 $$.margin3.left = $$.arcWidth / 2 + $$.radiusExpanded * 1.1;
1428 ChartInternal.prototype.updateTargets = function (targets) {
1434 $$.updateTargetsForText(targets);
1437 $$.updateTargetsForBar(targets);
1440 $$.updateTargetsForLine(targets);
1443 if ($$.hasArcType() && $$.updateTargetsForArc) {
1444 $$.updateTargetsForArc(targets);
1449 if ($$.updateTargetsForSubchart) {
1450 $$.updateTargetsForSubchart(targets);
1453 // Fade-in each chart
1456 ChartInternal.prototype.showTargets = function () {
1458 $$.svg.selectAll('.' + CLASS.target).filter(function (d) {
1459 return $$.isTargetToShow(d.id);
1460 }).transition().duration($$.config.transition_duration).style("opacity", 1);
1463 ChartInternal.prototype.redraw = function (options, transitions) {
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),
1481 var xv = $$.xv.bind($$),
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);
1500 duration = withTransition ? config.transition_duration : 0;
1501 durationForExit = withTransitionForExit ? duration : 0;
1502 durationForAxis = withTransitionForAxis ? duration : 0;
1504 transitions = transitions || $$.axis.generateTransitions(durationForAxis);
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);
1515 // MEMO: needed for grids calculation
1516 if ($$.isCategorized() && targetsToShow.length === 0) {
1517 $$.x.domain([0, $$.axes.x.selectAll('.tick').size()]);
1520 if (targetsToShow.length) {
1521 $$.updateXDomain(targetsToShow, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain);
1522 if (!config.axis_x_tick_values) {
1523 tickValues = $$.axis.updateXAxisTickValues(targetsToShow);
1526 $$.xAxis.tickValues([]);
1527 $$.subXAxis.tickValues([]);
1530 if (config.zoom_rescale && !options.flow) {
1531 xDomainForZoom = $$.x.orgDomain();
1534 $$.y.domain($$.getYDomain(targetsToShow, 'y', xDomainForZoom));
1535 $$.y2.domain($$.getYDomain(targetsToShow, 'y2', xDomainForZoom));
1537 if (!config.axis_y_tick_values && config.axis_y_tick_count) {
1538 $$.yAxis.tickValues($$.axis.generateTickValues($$.y.domain(), config.axis_y_tick_count));
1540 if (!config.axis_y2_tick_values && config.axis_y2_tick_count) {
1541 $$.y2Axis.tickValues($$.axis.generateTickValues($$.y2.domain(), config.axis_y2_tick_count));
1545 $$.axis.redraw(durationForAxis, hideAxis);
1547 // Update axis label
1548 $$.axis.updateLabels(withTransition);
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;
1559 $$.svg.selectAll('.' + CLASS.axisX + ' .tick text').each(function (e) {
1560 var index = tickValues.indexOf(e);
1562 d3.select(this).style('display', index % intervalForCulling ? 'none' : 'block');
1566 $$.svg.selectAll('.' + CLASS.axisX + ' .tick text').style('display', 'block');
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);
1577 // update circleY based on updated parameters
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($$);
1583 // Update sub domain
1585 $$.subY.domain($$.getYDomain(targetsToShow, 'y'));
1586 $$.subY2.domain($$.getYDomain(targetsToShow, 'y2'));
1590 $$.updateXgridFocus();
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);
1596 if (withEventRect) {
1597 $$.redrawEventRect();
1601 $$.updateGrid(duration);
1604 $$.updateRegion(duration);
1607 $$.updateBar(durationForExit);
1609 // lines, areas and cricles
1610 $$.updateLine(durationForExit);
1611 $$.updateArea(durationForExit);
1612 $$.updateCircle(cx, cy);
1615 if ($$.hasDataLabel()) {
1616 $$.updateText(xForText, yForText, durationForExit);
1620 if ($$.redrawTitle) {
1626 $$.redrawArc(duration, durationForExit, withTransform);
1630 if ($$.redrawSubchart) {
1631 $$.redrawSubchart(withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices);
1634 // circles for select
1635 main.selectAll('.' + CLASS.selectedCircles).filter($$.isBarType.bind($$)).selectAll('circle').remove();
1638 flow = $$.generateFlow({
1639 targets: targetsToShow,
1641 duration: options.flow.duration,
1653 if ($$.isTabVisible()) {
1654 // Only use transition if tab visible. See #938.
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);
1664 // Wait for end of transitions to call flow and onrendered callback
1665 waitForDraw = $$.generateWait();
1666 transitionsToWait.forEach(function (t) {
1669 waitForDraw(function () {
1673 if (config.onrendered) {
1674 config.onrendered.call($$);
1678 $$.redrawBar(drawBar);
1679 $$.redrawLine(drawLine);
1680 $$.redrawArea(drawArea);
1681 $$.redrawCircle(cx, cy);
1682 $$.redrawText(xForText, yForText, options.flow);
1688 if (config.onrendered) {
1689 config.onrendered.call($$);
1694 // update fadein condition
1695 $$.mapToIds($$.data.targets).forEach(function (id) {
1696 $$.withoutFadeIn[id] = true;
1700 ChartInternal.prototype.updateAndRedraw = function (options) {
1704 options = options || {};
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)
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);
1722 // Update g positions
1723 $$.transformAll(options.withTransitionForTransform, transitions);
1725 // Draw with new sizes & scales
1726 $$.redraw(options, transitions);
1728 ChartInternal.prototype.redrawWithoutRescale = function () {
1731 withSubchart: false,
1732 withEventRect: false,
1733 withTransitionForAxis: false
1737 ChartInternal.prototype.isTimeSeries = function () {
1738 return this.config.axis_x_type === 'timeseries';
1740 ChartInternal.prototype.isCategorized = function () {
1741 return this.config.axis_x_type.indexOf('categor') >= 0;
1743 ChartInternal.prototype.isCustomX = function () {
1746 return !$$.isTimeSeries() && (config.data_x || notEmpty(config.data_xs));
1749 ChartInternal.prototype.isTimeSeriesY = function () {
1750 return this.config.axis_y_type === 'timeseries';
1753 ChartInternal.prototype.getTranslate = function (target) {
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;
1767 } else if (target === 'x') {
1769 y = config.axis_rotated ? 0 : $$.height;
1770 } else if (target === 'y') {
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') {
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
1783 return "translate(" + x + "," + y + ")";
1785 ChartInternal.prototype.initialOpacity = function (d) {
1786 return d.value !== null && this.withoutFadeIn[d.id] ? 1 : 0;
1788 ChartInternal.prototype.initialOpacityForCircle = function (d) {
1789 return d.value !== null && this.withoutFadeIn[d.id] ? this.opacityForCircle(d) : 0;
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;
1796 ChartInternal.prototype.opacityForText = function () {
1797 return this.hasDataLabel() ? 1 : 0;
1799 ChartInternal.prototype.xx = function (d) {
1800 return d ? this.x(d.x) : null;
1802 ChartInternal.prototype.xv = function (d) {
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);
1810 return Math.ceil($$.x(value));
1812 ChartInternal.prototype.yv = function (d) {
1814 yScale = d.axis && d.axis === 'y2' ? $$.y2 : $$.y;
1815 return Math.ceil(yScale(d.value));
1817 ChartInternal.prototype.subxx = function (d) {
1818 return d ? this.subX(d.x) : null;
1821 ChartInternal.prototype.transformMain = function (withTransition, transitions) {
1826 if (transitions && transitions.axisX) {
1827 xAxis = transitions.axisX;
1829 xAxis = $$.main.select('.' + CLASS.axisX);
1830 if (withTransition) {
1831 xAxis = xAxis.transition();
1834 if (transitions && transitions.axisY) {
1835 yAxis = transitions.axisY;
1837 yAxis = $$.main.select('.' + CLASS.axisY);
1838 if (withTransition) {
1839 yAxis = yAxis.transition();
1842 if (transitions && transitions.axisY2) {
1843 y2Axis = transitions.axisY2;
1845 y2Axis = $$.main.select('.' + CLASS.axisY2);
1846 if (withTransition) {
1847 y2Axis = y2Axis.transition();
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'));
1856 ChartInternal.prototype.transformAll = function (withTransition, transitions) {
1858 $$.transformMain(withTransition, transitions);
1859 if ($$.config.subchart_show) {
1860 $$.transformContext(withTransition, transitions);
1863 $$.transformLegend(withTransition);
1867 ChartInternal.prototype.updateSvgSize = function () {
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");
1879 ChartInternal.prototype.updateDimension = function (withoutAxis) {
1882 if ($$.config.axis_rotated) {
1883 $$.axes.x.call($$.xAxis);
1884 $$.axes.subx.call($$.subXAxis);
1886 $$.axes.y.call($$.yAxis);
1887 $$.axes.y2.call($$.y2Axis);
1893 $$.transformAll(false);
1896 ChartInternal.prototype.observeInserted = function (selection) {
1899 if (typeof MutationObserver === 'undefined') {
1900 window.console.error("MutationObserver not defined.");
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();
1916 $$.config.oninit.call($$);
1918 withTransform: true,
1919 withUpdateXDomain: true,
1920 withUpdateOrgXDomain: true,
1921 withTransition: false,
1922 withTransitionForTransform: false,
1925 selection.transition().style('opacity', 1);
1931 observer.observe(selection.node(), {
1938 ChartInternal.prototype.bindResize = function () {
1942 $$.resizeFunction = $$.generateResize(); // need to call .remove
1944 $$.resizeFunction.add(function () {
1945 config.onresize.call($$);
1947 if (config.resize_auto) {
1948 $$.resizeFunction.add(function () {
1949 if ($$.resizeTimeout !== undefined) {
1950 window.clearTimeout($$.resizeTimeout);
1952 $$.resizeTimeout = window.setTimeout(function () {
1953 delete $$.resizeTimeout;
1954 $$.updateAndRedraw({
1955 withUpdateXDomain: false,
1956 withUpdateOrgXDomain: false,
1957 withTransition: false,
1958 withTransitionForTransform: false,
1967 $$.resizeFunction.add(function () {
1968 config.onresized.call($$);
1971 $$.resizeIfElementDisplayed = function () {
1972 // if element not displayed skip it
1973 if ($$.api == null || !$$.api.element.offsetParent) {
1977 $$.resizeFunction();
1980 if (window.attachEvent) {
1981 window.attachEvent('onresize', $$.resizeIfElementDisplayed);
1982 } else if (window.addEventListener) {
1983 window.addEventListener('resize', $$.resizeIfElementDisplayed, false);
1985 // fallback to this, if this is a very old browser
1986 var wrapper = window.onresize;
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);
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) {
2008 ChartInternal.prototype.generateResize = function () {
2009 var resizeFunctions = [];
2011 function callResizeFunctions() {
2012 resizeFunctions.forEach(function (f) {
2016 callResizeFunctions.add = function (f) {
2017 resizeFunctions.push(f);
2019 callResizeFunctions.remove = function (f) {
2020 for (var i = 0; i < resizeFunctions.length; i++) {
2021 if (resizeFunctions[i] === f) {
2022 resizeFunctions.splice(i, 1);
2027 return callResizeFunctions;
2030 ChartInternal.prototype.endall = function (transition, callback) {
2032 transition.each(function () {
2034 }).on("end", function () {
2036 callback.apply(this, arguments);
2040 ChartInternal.prototype.generateWait = function () {
2041 var transitionsToWait = [],
2042 f = function f(callback) {
2043 var timer = setInterval(function () {
2045 transitionsToWait.forEach(function (t) {
2056 if (done === transitionsToWait.length) {
2057 clearInterval(timer);
2064 f.add = function (transition) {
2065 transitionsToWait.push(transition);
2070 ChartInternal.prototype.parseDate = function (date) {
2073 if (date instanceof 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);
2082 if (!parsedDate || isNaN(+parsedDate)) {
2083 window.console.error("Failed to parse x '" + date + "' to Date object");
2088 ChartInternal.prototype.isTabVisible = function () {
2090 if (typeof document.hidden !== "undefined") {
2091 // Opera 12.10 and Firefox 18 and later support
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";
2101 return document[hidden] ? false : true;
2104 ChartInternal.prototype.getPathBox = getPathBox;
2105 ChartInternal.prototype.CLASS = CLASS;
2107 /* jshint ignore:start */
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
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');
2121 var aArgs = Array.prototype.slice.call(arguments, 1),
2123 fNOP = function fNOP() {},
2124 fBound = function fBound() {
2125 return fToBind.apply(this instanceof fNOP ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
2128 fNOP.prototype = this.prototype;
2129 fBound.prototype = new fNOP();
2135 // SVGPathSeg API polyfill
2136 // https://github.com/progers/pathseg
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.
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;
2152 window.SVGPathSeg.prototype.classname = "SVGPathSeg";
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;
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);
2180 window.SVGPathSegClosePath = function (owningPathSegList) {
2181 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CLOSEPATH, "z", owningPathSegList);
2183 window.SVGPathSegClosePath.prototype = Object.create(window.SVGPathSeg.prototype);
2184 window.SVGPathSegClosePath.prototype.toString = function () {
2185 return "[object SVGPathSegClosePath]";
2187 window.SVGPathSegClosePath.prototype._asPathString = function () {
2188 return this.pathSegTypeAsLetter;
2190 window.SVGPathSegClosePath.prototype.clone = function () {
2191 return new window.SVGPathSegClosePath(undefined);
2194 window.SVGPathSegMovetoAbs = function (owningPathSegList, x, y) {
2195 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_ABS, "M", owningPathSegList);
2199 window.SVGPathSegMovetoAbs.prototype = Object.create(window.SVGPathSeg.prototype);
2200 window.SVGPathSegMovetoAbs.prototype.toString = function () {
2201 return "[object SVGPathSegMovetoAbs]";
2203 window.SVGPathSegMovetoAbs.prototype._asPathString = function () {
2204 return this.pathSegTypeAsLetter + " " + this._x + " " + this._y;
2206 window.SVGPathSegMovetoAbs.prototype.clone = function () {
2207 return new window.SVGPathSegMovetoAbs(undefined, this._x, this._y);
2209 Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, "x", {
2210 get: function get() {
2213 set: function set(x) {
2215 this._segmentChanged();
2219 Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, "y", {
2220 get: function get() {
2223 set: function set(y) {
2225 this._segmentChanged();
2230 window.SVGPathSegMovetoRel = function (owningPathSegList, x, y) {
2231 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_REL, "m", owningPathSegList);
2235 window.SVGPathSegMovetoRel.prototype = Object.create(window.SVGPathSeg.prototype);
2236 window.SVGPathSegMovetoRel.prototype.toString = function () {
2237 return "[object SVGPathSegMovetoRel]";
2239 window.SVGPathSegMovetoRel.prototype._asPathString = function () {
2240 return this.pathSegTypeAsLetter + " " + this._x + " " + this._y;
2242 window.SVGPathSegMovetoRel.prototype.clone = function () {
2243 return new window.SVGPathSegMovetoRel(undefined, this._x, this._y);
2245 Object.defineProperty(window.SVGPathSegMovetoRel.prototype, "x", {
2246 get: function get() {
2249 set: function set(x) {
2251 this._segmentChanged();
2255 Object.defineProperty(window.SVGPathSegMovetoRel.prototype, "y", {
2256 get: function get() {
2259 set: function set(y) {
2261 this._segmentChanged();
2266 window.SVGPathSegLinetoAbs = function (owningPathSegList, x, y) {
2267 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_ABS, "L", owningPathSegList);
2271 window.SVGPathSegLinetoAbs.prototype = Object.create(window.SVGPathSeg.prototype);
2272 window.SVGPathSegLinetoAbs.prototype.toString = function () {
2273 return "[object SVGPathSegLinetoAbs]";
2275 window.SVGPathSegLinetoAbs.prototype._asPathString = function () {
2276 return this.pathSegTypeAsLetter + " " + this._x + " " + this._y;
2278 window.SVGPathSegLinetoAbs.prototype.clone = function () {
2279 return new window.SVGPathSegLinetoAbs(undefined, this._x, this._y);
2281 Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, "x", {
2282 get: function get() {
2285 set: function set(x) {
2287 this._segmentChanged();
2291 Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, "y", {
2292 get: function get() {
2295 set: function set(y) {
2297 this._segmentChanged();
2302 window.SVGPathSegLinetoRel = function (owningPathSegList, x, y) {
2303 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_REL, "l", owningPathSegList);
2307 window.SVGPathSegLinetoRel.prototype = Object.create(window.SVGPathSeg.prototype);
2308 window.SVGPathSegLinetoRel.prototype.toString = function () {
2309 return "[object SVGPathSegLinetoRel]";
2311 window.SVGPathSegLinetoRel.prototype._asPathString = function () {
2312 return this.pathSegTypeAsLetter + " " + this._x + " " + this._y;
2314 window.SVGPathSegLinetoRel.prototype.clone = function () {
2315 return new window.SVGPathSegLinetoRel(undefined, this._x, this._y);
2317 Object.defineProperty(window.SVGPathSegLinetoRel.prototype, "x", {
2318 get: function get() {
2321 set: function set(x) {
2323 this._segmentChanged();
2327 Object.defineProperty(window.SVGPathSegLinetoRel.prototype, "y", {
2328 get: function get() {
2331 set: function set(y) {
2333 this._segmentChanged();
2338 window.SVGPathSegCurvetoCubicAbs = function (owningPathSegList, x, y, x1, y1, x2, y2) {
2339 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS, "C", owningPathSegList);
2347 window.SVGPathSegCurvetoCubicAbs.prototype = Object.create(window.SVGPathSeg.prototype);
2348 window.SVGPathSegCurvetoCubicAbs.prototype.toString = function () {
2349 return "[object SVGPathSegCurvetoCubicAbs]";
2351 window.SVGPathSegCurvetoCubicAbs.prototype._asPathString = function () {
2352 return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y;
2354 window.SVGPathSegCurvetoCubicAbs.prototype.clone = function () {
2355 return new window.SVGPathSegCurvetoCubicAbs(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2);
2357 Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "x", {
2358 get: function get() {
2361 set: function set(x) {
2363 this._segmentChanged();
2367 Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "y", {
2368 get: function get() {
2371 set: function set(y) {
2373 this._segmentChanged();
2377 Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "x1", {
2378 get: function get() {
2381 set: function set(x1) {
2383 this._segmentChanged();
2387 Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "y1", {
2388 get: function get() {
2391 set: function set(y1) {
2393 this._segmentChanged();
2397 Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "x2", {
2398 get: function get() {
2401 set: function set(x2) {
2403 this._segmentChanged();
2407 Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "y2", {
2408 get: function get() {
2411 set: function set(y2) {
2413 this._segmentChanged();
2418 window.SVGPathSegCurvetoCubicRel = function (owningPathSegList, x, y, x1, y1, x2, y2) {
2419 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL, "c", owningPathSegList);
2427 window.SVGPathSegCurvetoCubicRel.prototype = Object.create(window.SVGPathSeg.prototype);
2428 window.SVGPathSegCurvetoCubicRel.prototype.toString = function () {
2429 return "[object SVGPathSegCurvetoCubicRel]";
2431 window.SVGPathSegCurvetoCubicRel.prototype._asPathString = function () {
2432 return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y;
2434 window.SVGPathSegCurvetoCubicRel.prototype.clone = function () {
2435 return new window.SVGPathSegCurvetoCubicRel(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2);
2437 Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "x", {
2438 get: function get() {
2441 set: function set(x) {
2443 this._segmentChanged();
2447 Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "y", {
2448 get: function get() {
2451 set: function set(y) {
2453 this._segmentChanged();
2457 Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "x1", {
2458 get: function get() {
2461 set: function set(x1) {
2463 this._segmentChanged();
2467 Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "y1", {
2468 get: function get() {
2471 set: function set(y1) {
2473 this._segmentChanged();
2477 Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "x2", {
2478 get: function get() {
2481 set: function set(x2) {
2483 this._segmentChanged();
2487 Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "y2", {
2488 get: function get() {
2491 set: function set(y2) {
2493 this._segmentChanged();
2498 window.SVGPathSegCurvetoQuadraticAbs = function (owningPathSegList, x, y, x1, y1) {
2499 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS, "Q", owningPathSegList);
2505 window.SVGPathSegCurvetoQuadraticAbs.prototype = Object.create(window.SVGPathSeg.prototype);
2506 window.SVGPathSegCurvetoQuadraticAbs.prototype.toString = function () {
2507 return "[object SVGPathSegCurvetoQuadraticAbs]";
2509 window.SVGPathSegCurvetoQuadraticAbs.prototype._asPathString = function () {
2510 return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x + " " + this._y;
2512 window.SVGPathSegCurvetoQuadraticAbs.prototype.clone = function () {
2513 return new window.SVGPathSegCurvetoQuadraticAbs(undefined, this._x, this._y, this._x1, this._y1);
2515 Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "x", {
2516 get: function get() {
2519 set: function set(x) {
2521 this._segmentChanged();
2525 Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "y", {
2526 get: function get() {
2529 set: function set(y) {
2531 this._segmentChanged();
2535 Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "x1", {
2536 get: function get() {
2539 set: function set(x1) {
2541 this._segmentChanged();
2545 Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "y1", {
2546 get: function get() {
2549 set: function set(y1) {
2551 this._segmentChanged();
2556 window.SVGPathSegCurvetoQuadraticRel = function (owningPathSegList, x, y, x1, y1) {
2557 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL, "q", owningPathSegList);
2563 window.SVGPathSegCurvetoQuadraticRel.prototype = Object.create(window.SVGPathSeg.prototype);
2564 window.SVGPathSegCurvetoQuadraticRel.prototype.toString = function () {
2565 return "[object SVGPathSegCurvetoQuadraticRel]";
2567 window.SVGPathSegCurvetoQuadraticRel.prototype._asPathString = function () {
2568 return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x + " " + this._y;
2570 window.SVGPathSegCurvetoQuadraticRel.prototype.clone = function () {
2571 return new window.SVGPathSegCurvetoQuadraticRel(undefined, this._x, this._y, this._x1, this._y1);
2573 Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "x", {
2574 get: function get() {
2577 set: function set(x) {
2579 this._segmentChanged();
2583 Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "y", {
2584 get: function get() {
2587 set: function set(y) {
2589 this._segmentChanged();
2593 Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "x1", {
2594 get: function get() {
2597 set: function set(x1) {
2599 this._segmentChanged();
2603 Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "y1", {
2604 get: function get() {
2607 set: function set(y1) {
2609 this._segmentChanged();
2614 window.SVGPathSegArcAbs = function (owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) {
2615 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_ABS, "A", owningPathSegList);
2620 this._angle = angle;
2621 this._largeArcFlag = largeArcFlag;
2622 this._sweepFlag = sweepFlag;
2624 window.SVGPathSegArcAbs.prototype = Object.create(window.SVGPathSeg.prototype);
2625 window.SVGPathSegArcAbs.prototype.toString = function () {
2626 return "[object SVGPathSegArcAbs]";
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;
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);
2634 Object.defineProperty(window.SVGPathSegArcAbs.prototype, "x", {
2635 get: function get() {
2638 set: function set(x) {
2640 this._segmentChanged();
2644 Object.defineProperty(window.SVGPathSegArcAbs.prototype, "y", {
2645 get: function get() {
2648 set: function set(y) {
2650 this._segmentChanged();
2654 Object.defineProperty(window.SVGPathSegArcAbs.prototype, "r1", {
2655 get: function get() {
2658 set: function set(r1) {
2660 this._segmentChanged();
2664 Object.defineProperty(window.SVGPathSegArcAbs.prototype, "r2", {
2665 get: function get() {
2668 set: function set(r2) {
2670 this._segmentChanged();
2674 Object.defineProperty(window.SVGPathSegArcAbs.prototype, "angle", {
2675 get: function get() {
2678 set: function set(angle) {
2679 this._angle = angle;
2680 this._segmentChanged();
2684 Object.defineProperty(window.SVGPathSegArcAbs.prototype, "largeArcFlag", {
2685 get: function get() {
2686 return this._largeArcFlag;
2688 set: function set(largeArcFlag) {
2689 this._largeArcFlag = largeArcFlag;
2690 this._segmentChanged();
2694 Object.defineProperty(window.SVGPathSegArcAbs.prototype, "sweepFlag", {
2695 get: function get() {
2696 return this._sweepFlag;
2698 set: function set(sweepFlag) {
2699 this._sweepFlag = sweepFlag;
2700 this._segmentChanged();
2705 window.SVGPathSegArcRel = function (owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) {
2706 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_REL, "a", owningPathSegList);
2711 this._angle = angle;
2712 this._largeArcFlag = largeArcFlag;
2713 this._sweepFlag = sweepFlag;
2715 window.SVGPathSegArcRel.prototype = Object.create(window.SVGPathSeg.prototype);
2716 window.SVGPathSegArcRel.prototype.toString = function () {
2717 return "[object SVGPathSegArcRel]";
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;
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);
2725 Object.defineProperty(window.SVGPathSegArcRel.prototype, "x", {
2726 get: function get() {
2729 set: function set(x) {
2731 this._segmentChanged();
2735 Object.defineProperty(window.SVGPathSegArcRel.prototype, "y", {
2736 get: function get() {
2739 set: function set(y) {
2741 this._segmentChanged();
2745 Object.defineProperty(window.SVGPathSegArcRel.prototype, "r1", {
2746 get: function get() {
2749 set: function set(r1) {
2751 this._segmentChanged();
2755 Object.defineProperty(window.SVGPathSegArcRel.prototype, "r2", {
2756 get: function get() {
2759 set: function set(r2) {
2761 this._segmentChanged();
2765 Object.defineProperty(window.SVGPathSegArcRel.prototype, "angle", {
2766 get: function get() {
2769 set: function set(angle) {
2770 this._angle = angle;
2771 this._segmentChanged();
2775 Object.defineProperty(window.SVGPathSegArcRel.prototype, "largeArcFlag", {
2776 get: function get() {
2777 return this._largeArcFlag;
2779 set: function set(largeArcFlag) {
2780 this._largeArcFlag = largeArcFlag;
2781 this._segmentChanged();
2785 Object.defineProperty(window.SVGPathSegArcRel.prototype, "sweepFlag", {
2786 get: function get() {
2787 return this._sweepFlag;
2789 set: function set(sweepFlag) {
2790 this._sweepFlag = sweepFlag;
2791 this._segmentChanged();
2796 window.SVGPathSegLinetoHorizontalAbs = function (owningPathSegList, x) {
2797 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS, "H", owningPathSegList);
2800 window.SVGPathSegLinetoHorizontalAbs.prototype = Object.create(window.SVGPathSeg.prototype);
2801 window.SVGPathSegLinetoHorizontalAbs.prototype.toString = function () {
2802 return "[object SVGPathSegLinetoHorizontalAbs]";
2804 window.SVGPathSegLinetoHorizontalAbs.prototype._asPathString = function () {
2805 return this.pathSegTypeAsLetter + " " + this._x;
2807 window.SVGPathSegLinetoHorizontalAbs.prototype.clone = function () {
2808 return new window.SVGPathSegLinetoHorizontalAbs(undefined, this._x);
2810 Object.defineProperty(window.SVGPathSegLinetoHorizontalAbs.prototype, "x", {
2811 get: function get() {
2814 set: function set(x) {
2816 this._segmentChanged();
2821 window.SVGPathSegLinetoHorizontalRel = function (owningPathSegList, x) {
2822 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL, "h", owningPathSegList);
2825 window.SVGPathSegLinetoHorizontalRel.prototype = Object.create(window.SVGPathSeg.prototype);
2826 window.SVGPathSegLinetoHorizontalRel.prototype.toString = function () {
2827 return "[object SVGPathSegLinetoHorizontalRel]";
2829 window.SVGPathSegLinetoHorizontalRel.prototype._asPathString = function () {
2830 return this.pathSegTypeAsLetter + " " + this._x;
2832 window.SVGPathSegLinetoHorizontalRel.prototype.clone = function () {
2833 return new window.SVGPathSegLinetoHorizontalRel(undefined, this._x);
2835 Object.defineProperty(window.SVGPathSegLinetoHorizontalRel.prototype, "x", {
2836 get: function get() {
2839 set: function set(x) {
2841 this._segmentChanged();
2846 window.SVGPathSegLinetoVerticalAbs = function (owningPathSegList, y) {
2847 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS, "V", owningPathSegList);
2850 window.SVGPathSegLinetoVerticalAbs.prototype = Object.create(window.SVGPathSeg.prototype);
2851 window.SVGPathSegLinetoVerticalAbs.prototype.toString = function () {
2852 return "[object SVGPathSegLinetoVerticalAbs]";
2854 window.SVGPathSegLinetoVerticalAbs.prototype._asPathString = function () {
2855 return this.pathSegTypeAsLetter + " " + this._y;
2857 window.SVGPathSegLinetoVerticalAbs.prototype.clone = function () {
2858 return new window.SVGPathSegLinetoVerticalAbs(undefined, this._y);
2860 Object.defineProperty(window.SVGPathSegLinetoVerticalAbs.prototype, "y", {
2861 get: function get() {
2864 set: function set(y) {
2866 this._segmentChanged();
2871 window.SVGPathSegLinetoVerticalRel = function (owningPathSegList, y) {
2872 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL, "v", owningPathSegList);
2875 window.SVGPathSegLinetoVerticalRel.prototype = Object.create(window.SVGPathSeg.prototype);
2876 window.SVGPathSegLinetoVerticalRel.prototype.toString = function () {
2877 return "[object SVGPathSegLinetoVerticalRel]";
2879 window.SVGPathSegLinetoVerticalRel.prototype._asPathString = function () {
2880 return this.pathSegTypeAsLetter + " " + this._y;
2882 window.SVGPathSegLinetoVerticalRel.prototype.clone = function () {
2883 return new window.SVGPathSegLinetoVerticalRel(undefined, this._y);
2885 Object.defineProperty(window.SVGPathSegLinetoVerticalRel.prototype, "y", {
2886 get: function get() {
2889 set: function set(y) {
2891 this._segmentChanged();
2896 window.SVGPathSegCurvetoCubicSmoothAbs = function (owningPathSegList, x, y, x2, y2) {
2897 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS, "S", owningPathSegList);
2903 window.SVGPathSegCurvetoCubicSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype);
2904 window.SVGPathSegCurvetoCubicSmoothAbs.prototype.toString = function () {
2905 return "[object SVGPathSegCurvetoCubicSmoothAbs]";
2907 window.SVGPathSegCurvetoCubicSmoothAbs.prototype._asPathString = function () {
2908 return this.pathSegTypeAsLetter + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y;
2910 window.SVGPathSegCurvetoCubicSmoothAbs.prototype.clone = function () {
2911 return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, this._x, this._y, this._x2, this._y2);
2913 Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "x", {
2914 get: function get() {
2917 set: function set(x) {
2919 this._segmentChanged();
2923 Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "y", {
2924 get: function get() {
2927 set: function set(y) {
2929 this._segmentChanged();
2933 Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "x2", {
2934 get: function get() {
2937 set: function set(x2) {
2939 this._segmentChanged();
2943 Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "y2", {
2944 get: function get() {
2947 set: function set(y2) {
2949 this._segmentChanged();
2954 window.SVGPathSegCurvetoCubicSmoothRel = function (owningPathSegList, x, y, x2, y2) {
2955 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL, "s", owningPathSegList);
2961 window.SVGPathSegCurvetoCubicSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype);
2962 window.SVGPathSegCurvetoCubicSmoothRel.prototype.toString = function () {
2963 return "[object SVGPathSegCurvetoCubicSmoothRel]";
2965 window.SVGPathSegCurvetoCubicSmoothRel.prototype._asPathString = function () {
2966 return this.pathSegTypeAsLetter + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y;
2968 window.SVGPathSegCurvetoCubicSmoothRel.prototype.clone = function () {
2969 return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, this._x, this._y, this._x2, this._y2);
2971 Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "x", {
2972 get: function get() {
2975 set: function set(x) {
2977 this._segmentChanged();
2981 Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "y", {
2982 get: function get() {
2985 set: function set(y) {
2987 this._segmentChanged();
2991 Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "x2", {
2992 get: function get() {
2995 set: function set(x2) {
2997 this._segmentChanged();
3001 Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "y2", {
3002 get: function get() {
3005 set: function set(y2) {
3007 this._segmentChanged();
3012 window.SVGPathSegCurvetoQuadraticSmoothAbs = function (owningPathSegList, x, y) {
3013 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS, "T", owningPathSegList);
3017 window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype);
3018 window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.toString = function () {
3019 return "[object SVGPathSegCurvetoQuadraticSmoothAbs]";
3021 window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype._asPathString = function () {
3022 return this.pathSegTypeAsLetter + " " + this._x + " " + this._y;
3024 window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.clone = function () {
3025 return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, this._x, this._y);
3027 Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, "x", {
3028 get: function get() {
3031 set: function set(x) {
3033 this._segmentChanged();
3037 Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, "y", {
3038 get: function get() {
3041 set: function set(y) {
3043 this._segmentChanged();
3048 window.SVGPathSegCurvetoQuadraticSmoothRel = function (owningPathSegList, x, y) {
3049 window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, "t", owningPathSegList);
3053 window.SVGPathSegCurvetoQuadraticSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype);
3054 window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.toString = function () {
3055 return "[object SVGPathSegCurvetoQuadraticSmoothRel]";
3057 window.SVGPathSegCurvetoQuadraticSmoothRel.prototype._asPathString = function () {
3058 return this.pathSegTypeAsLetter + " " + this._x + " " + this._y;
3060 window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.clone = function () {
3061 return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, this._x, this._y);
3063 Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, "x", {
3064 get: function get() {
3067 set: function set(x) {
3069 this._segmentChanged();
3073 Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, "y", {
3074 get: function get() {
3077 set: function set(y) {
3079 this._segmentChanged();
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);
3089 window.SVGPathElement.prototype.createSVGPathSegMovetoAbs = function (x, y) {
3090 return new window.SVGPathSegMovetoAbs(undefined, x, y);
3092 window.SVGPathElement.prototype.createSVGPathSegMovetoRel = function (x, y) {
3093 return new window.SVGPathSegMovetoRel(undefined, x, y);
3095 window.SVGPathElement.prototype.createSVGPathSegLinetoAbs = function (x, y) {
3096 return new window.SVGPathSegLinetoAbs(undefined, x, y);
3098 window.SVGPathElement.prototype.createSVGPathSegLinetoRel = function (x, y) {
3099 return new window.SVGPathSegLinetoRel(undefined, x, y);
3101 window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicAbs = function (x, y, x1, y1, x2, y2) {
3102 return new window.SVGPathSegCurvetoCubicAbs(undefined, x, y, x1, y1, x2, y2);
3104 window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicRel = function (x, y, x1, y1, x2, y2) {
3105 return new window.SVGPathSegCurvetoCubicRel(undefined, x, y, x1, y1, x2, y2);
3107 window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticAbs = function (x, y, x1, y1) {
3108 return new window.SVGPathSegCurvetoQuadraticAbs(undefined, x, y, x1, y1);
3110 window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticRel = function (x, y, x1, y1) {
3111 return new window.SVGPathSegCurvetoQuadraticRel(undefined, x, y, x1, y1);
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);
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);
3119 window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs = function (x) {
3120 return new window.SVGPathSegLinetoHorizontalAbs(undefined, x);
3122 window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel = function (x) {
3123 return new window.SVGPathSegLinetoHorizontalRel(undefined, x);
3125 window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs = function (y) {
3126 return new window.SVGPathSegLinetoVerticalAbs(undefined, y);
3128 window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel = function (y) {
3129 return new window.SVGPathSegLinetoVerticalRel(undefined, y);
3131 window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothAbs = function (x, y, x2, y2) {
3132 return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, x, y, x2, y2);
3134 window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothRel = function (x, y, x2, y2) {
3135 return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, x, y, x2, y2);
3137 window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothAbs = function (x, y) {
3138 return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, x, y);
3140 window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothRel = function (x, y) {
3141 return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, x, y);
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.";
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;
3155 // If the path is empty, return 0.
3156 if (lastPathSegment <= 0) return 0;
3159 measurementElement.pathSegList.removeItem(lastPathSegment);
3160 if (distance > measurementElement.getTotalLength()) break;
3162 } while (lastPathSegment > 0);
3163 return lastPathSegment;
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"));
3174 // Use a MutationObserver to catch changes to the path's "d" attribute.
3175 this._mutationObserverConfig = {
3177 "attributeFilter": ["d"]
3179 this._pathElementMutationObserver = new MutationObserver(this._updateListFromPathMutations.bind(this));
3180 this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig);
3183 window.SVGPathSegList.prototype.classname = "SVGPathSegList";
3185 Object.defineProperty(window.SVGPathSegList.prototype, "numberOfItems", {
3186 get: function get() {
3187 this._checkPathSynchronizedToList();
3188 return this._list.length;
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;
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;
3209 Object.defineProperty(window.SVGPathElement.prototype, "animatedPathSegList", {
3210 get: function get() {
3211 return this.pathSegList;
3215 Object.defineProperty(window.SVGPathElement.prototype, "animatedNormalizedPathSegList", {
3216 get: function get() {
3217 return this.pathSegList;
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());
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;
3235 if (hasPathMutations) this._list = this._parsePath(this._pathElement.getAttribute("d"));
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);
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();
3250 window.SVGPathSegList.prototype.clear = function () {
3251 this._checkPathSynchronizedToList();
3253 this._list.forEach(function (pathSeg) {
3254 pathSeg._owningPathSegList = null;
3257 this._writeListToPath();
3260 window.SVGPathSegList.prototype.initialize = function (newItem) {
3261 this._checkPathSynchronizedToList();
3263 this._list = [newItem];
3264 newItem._owningPathSegList = this;
3265 this._writeListToPath();
3269 window.SVGPathSegList.prototype._checkValidIndex = function (index) {
3270 if (isNaN(index) || index < 0 || index >= this.numberOfItems) throw "INDEX_SIZE_ERR";
3273 window.SVGPathSegList.prototype.getItem = function (index) {
3274 this._checkPathSynchronizedToList();
3276 this._checkValidIndex(index);
3277 return this._list[index];
3280 window.SVGPathSegList.prototype.insertItemBefore = function (newItem, index) {
3281 this._checkPathSynchronizedToList();
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();
3289 this._list.splice(index, 0, newItem);
3290 newItem._owningPathSegList = this;
3291 this._writeListToPath();
3295 window.SVGPathSegList.prototype.replaceItem = function (newItem, index) {
3296 this._checkPathSynchronizedToList();
3298 if (newItem._owningPathSegList) {
3299 // SVG2 spec says to make a copy.
3300 newItem = newItem.clone();
3302 this._checkValidIndex(index);
3303 this._list[index] = newItem;
3304 newItem._owningPathSegList = this;
3305 this._writeListToPath();
3309 window.SVGPathSegList.prototype.removeItem = function (index) {
3310 this._checkPathSynchronizedToList();
3312 this._checkValidIndex(index);
3313 var item = this._list[index];
3314 this._list.splice(index, 1);
3315 this._writeListToPath();
3319 window.SVGPathSegList.prototype.appendItem = function (newItem) {
3320 this._checkPathSynchronizedToList();
3322 if (newItem._owningPathSegList) {
3323 // SVG2 spec says to make a copy.
3324 newItem = newItem.clone();
3326 this._list.push(newItem);
3327 newItem._owningPathSegList = this;
3328 // TODO: Optimize this to just append to the existing attribute.
3329 this._writeListToPath();
3333 window.SVGPathSegList._pathSegArrayAsString = function (pathSegArray) {
3336 pathSegArray.forEach(function (pathSeg) {
3339 string += pathSeg._asPathString();
3341 string += " " + pathSeg._asPathString();
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 [];
3351 var owningPathSegList = this;
3353 var Builder = function Builder() {
3354 this.pathSegList = [];
3357 Builder.prototype.appendSegment = function (pathSeg) {
3358 this.pathSegList.push(pathSeg);
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;
3367 this._skipOptionalSpaces();
3370 Source.prototype._isCurrentSpace = function () {
3371 var character = this._string[this._currentIndex];
3372 return character <= " " && (character == " " || character == "\n" || character == "\t" || character == "\r" || character == "\f");
3375 Source.prototype._skipOptionalSpaces = function () {
3376 while (this._currentIndex < this._endIndex && this._isCurrentSpace()) {
3377 this._currentIndex++;
3378 }return this._currentIndex < this._endIndex;
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();
3389 return this._currentIndex < this._endIndex;
3392 Source.prototype.hasMoreData = function () {
3393 return this._currentIndex < this._endIndex;
3396 Source.prototype.peekSegmentType = function () {
3397 var lookahead = this._string[this._currentIndex];
3398 return this._pathSegTypeFromChar(lookahead);
3401 Source.prototype._pathSegTypeFromChar = function (lookahead) {
3402 switch (lookahead) {
3405 return window.SVGPathSeg.PATHSEG_CLOSEPATH;
3407 return window.SVGPathSeg.PATHSEG_MOVETO_ABS;
3409 return window.SVGPathSeg.PATHSEG_MOVETO_REL;
3411 return window.SVGPathSeg.PATHSEG_LINETO_ABS;
3413 return window.SVGPathSeg.PATHSEG_LINETO_REL;
3415 return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS;
3417 return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL;
3419 return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS;
3421 return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL;
3423 return window.SVGPathSeg.PATHSEG_ARC_ABS;
3425 return window.SVGPathSeg.PATHSEG_ARC_REL;
3427 return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS;
3429 return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL;
3431 return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS;
3433 return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL;
3435 return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS;
3437 return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL;
3439 return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS;
3441 return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL;
3443 return window.SVGPathSeg.PATHSEG_UNKNOWN;
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;
3454 return window.SVGPathSeg.PATHSEG_UNKNOWN;
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;
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 () {
3475 var startIndex = this._currentIndex;
3477 this._skipOptionalSpaces();
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++;
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+-.].
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.
3495 if (this._currentIndex != startIntPartIndex) {
3496 var scanIntPartIndex = this._currentIndex - 1;
3498 while (scanIntPartIndex >= startIntPartIndex) {
3499 integer += multiplier * (this._string.charAt(scanIntPartIndex--) - "0");
3504 // Read the decimals.
3505 if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ".") {
3506 this._currentIndex++;
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") {
3512 decimal += (this._string.charAt(this._currentIndex) - "0") / frac;
3513 this._currentIndex += 1;
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++;
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++;
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;
3532 while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9") {
3534 exponent += this._string.charAt(this._currentIndex) - "0";
3535 this._currentIndex++;
3539 var number = integer + decimal;
3542 if (exponent) number *= Math.pow(10, expsign * exponent);
3544 if (startIndex == this._currentIndex) return undefined;
3546 this._skipOptionalSpacesOrDelimiter();
3551 Source.prototype._parseArcFlag = function () {
3552 if (this._currentIndex >= this._endIndex) return undefined;
3554 var flagChar = this._string.charAt(this._currentIndex++);
3555 if (flagChar == "0") flag = false;else if (flagChar == "1") flag = true;else return undefined;
3557 this._skipOptionalSpacesOrDelimiter();
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;
3570 this._currentIndex++;
3573 this._previousCommand = 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:
3597 x1: this._parseNumber(),
3598 y1: this._parseNumber(),
3599 x2: this._parseNumber(),
3600 y2: this._parseNumber(),
3601 x: this._parseNumber(),
3602 y: this._parseNumber()
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:
3607 x1: this._parseNumber(),
3608 y1: this._parseNumber(),
3609 x2: this._parseNumber(),
3610 y2: this._parseNumber(),
3611 x: this._parseNumber(),
3612 y: this._parseNumber()
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:
3617 x2: this._parseNumber(),
3618 y2: this._parseNumber(),
3619 x: this._parseNumber(),
3620 y: this._parseNumber()
3622 return new window.SVGPathSegCurvetoCubicSmoothRel(owningPathSegList, points.x, points.y, points.x2, points.y2);
3623 case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
3625 x2: this._parseNumber(),
3626 y2: this._parseNumber(),
3627 x: this._parseNumber(),
3628 y: this._parseNumber()
3630 return new window.SVGPathSegCurvetoCubicSmoothAbs(owningPathSegList, points.x, points.y, points.x2, points.y2);
3631 case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:
3633 x1: this._parseNumber(),
3634 y1: this._parseNumber(),
3635 x: this._parseNumber(),
3636 y: this._parseNumber()
3638 return new window.SVGPathSegCurvetoQuadraticRel(owningPathSegList, points.x, points.y, points.x1, points.y1);
3639 case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:
3641 x1: this._parseNumber(),
3642 y1: this._parseNumber(),
3643 x: this._parseNumber(),
3644 y: this._parseNumber()
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:
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()
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:
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()
3672 return new window.SVGPathSegArcAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep);
3674 throw "Unknown path seg type.";
3678 var builder = new Builder();
3679 var source = new Source(string);
3681 if (!source.initialCommandIsMoveTo()) return [];
3682 while (source.hasMoreData()) {
3683 var pathSeg = source.parseSegment();
3684 if (!pathSeg) return [];
3685 builder.appendSegment(pathSeg);
3688 return builder.pathSegList;
3693 // String.padEnd polyfill for IE11
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);
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
3708 return String(this) + padString.slice(0, targetLength);
3713 /* jshint ignore:end */
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]);
3722 $$.axis.updateLabels();
3724 // TODO: return some values?
3726 Chart.prototype.axis.max = function (max) {
3727 var $$ = this.internal,
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;
3734 if (isValue(max.y)) {
3735 config.axis_y_max = max.y;
3737 if (isValue(max.y2)) {
3738 config.axis_y2_max = max.y2;
3741 config.axis_y_max = config.axis_y2_max = max;
3743 $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true });
3746 x: config.axis_x_max,
3747 y: config.axis_y_max,
3748 y2: config.axis_y2_max
3752 Chart.prototype.axis.min = function (min) {
3753 var $$ = this.internal,
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;
3760 if (isValue(min.y)) {
3761 config.axis_y_min = min.y;
3763 if (isValue(min.y2)) {
3764 config.axis_y2_min = min.y2;
3767 config.axis_y_min = config.axis_y2_min = min;
3769 $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true });
3772 x: config.axis_x_min,
3773 y: config.axis_y_min,
3774 y2: config.axis_y2_min
3778 Chart.prototype.axis.range = function (range) {
3779 if (arguments.length) {
3780 if (isDefined(range.max)) {
3781 this.axis.max(range.max);
3783 if (isDefined(range.min)) {
3784 this.axis.min(range.min);
3788 max: this.axis.max(),
3789 min: this.axis.min()
3794 Chart.prototype.category = function (i, category) {
3795 var $$ = this.internal,
3797 if (arguments.length > 1) {
3798 config.axis_x_categories[i] = category;
3801 return config.axis_x_categories[i];
3803 Chart.prototype.categories = function (categories) {
3804 var $$ = this.internal,
3806 if (!arguments.length) {
3807 return config.axis_x_categories;
3809 config.axis_x_categories = categories;
3811 return config.axis_x_categories;
3814 Chart.prototype.resize = function (size) {
3815 var $$ = this.internal,
3817 config.size_width = size ? size.width : null;
3818 config.size_height = size ? size.height : null;
3822 Chart.prototype.flush = function () {
3823 var $$ = this.internal;
3824 $$.updateAndRedraw({ withLegend: true, withTransition: false, withTransitionForTransform: false });
3827 Chart.prototype.destroy = function () {
3828 var $$ = this.internal;
3830 window.clearInterval($$.intervalForObserveInserted);
3832 if ($$.resizeTimeout !== undefined) {
3833 window.clearTimeout($$.resizeTimeout);
3836 if (window.detachEvent) {
3837 window.detachEvent('onresize', $$.resizeIfElementDisplayed);
3838 } else if (window.removeEventListener) {
3839 window.removeEventListener('resize', $$.resizeIfElementDisplayed);
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);
3848 // remove the inner resize functions
3849 $$.resizeFunction.remove();
3851 $$.selectChart.classed('c3', false).html("");
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) {
3862 Chart.prototype.color = function (id) {
3863 var $$ = this.internal;
3864 return $$.color(id); // more patterns
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;
3873 Chart.prototype.data.shown = function (targetIds) {
3874 return this.internal.filterTargetsToShow(this.data(targetIds));
3876 Chart.prototype.data.values = function (targetId) {
3880 targets = this.data(targetId);
3881 values = targets[0] ? targets[0].values.map(function (d) {
3887 Chart.prototype.data.names = function (names) {
3888 this.internal.clearLegendItemTextBoxCache();
3889 return this.internal.updateDataAttributes('names', names);
3891 Chart.prototype.data.colors = function (colors) {
3892 return this.internal.updateDataAttributes('colors', colors);
3894 Chart.prototype.data.axes = function (axes) {
3895 return this.internal.updateDataAttributes('axes', axes);
3898 Chart.prototype.flow = function (args) {
3899 var $$ = this.internal,
3903 orgDataCount = $$.getMaxDataCount(),
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);
3922 targets = $$.convertDataToTargets(data, true);
3925 $$.data.targets.forEach(function (t) {
3929 for (i = 0; i < targets.length; i++) {
3930 if (t.id === targets[i].id) {
3933 if (t.values[t.values.length - 1]) {
3934 tail = t.values[t.values.length - 1].index + 1;
3936 length = targets[i].values.length;
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;
3944 t.values = t.values.concat(targets[i].values);
3946 targets.splice(i, 1);
3951 notfoundIds.push(t.id);
3955 // Append null for not found targets
3956 $$.data.targets.forEach(function (t) {
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++) {
3965 x: $$.isTimeSeries() ? $$.getOtherTargetX(tail + j) : tail + j,
3973 // Generate null values for new target
3974 if ($$.data.targets.length) {
3975 targets.forEach(function (t) {
3978 for (i = $$.data.targets[0].values[0].index; i < tail; i++) {
3982 x: $$.isTimeSeries() ? $$.getOtherTargetX(i) : i,
3986 t.values.forEach(function (v) {
3988 if (!$$.isTimeSeries()) {
3992 t.values = missing.concat(t.values);
3995 $$.data.targets = $$.data.targets.concat(targets); // add remained
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];
4002 // Update length to flow if needed
4003 if (isDefined(args.to)) {
4005 to = $$.isTimeSeries() ? $$.parseDate(args.to) : args.to;
4006 baseTarget.values.forEach(function (v) {
4011 } else if (isDefined(args.length)) {
4012 length = args.length;
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;
4021 diff = baseValue.x - $$.getXDomain($$.data.targets)[0];
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);
4037 $$.updateTargets($$.data.targets);
4039 // Redraw with new targets
4042 index: baseValue.index,
4044 duration: isValue(args.duration) ? args.duration : $$.config.transition_duration,
4046 orgDataCount: orgDataCount
4049 withTransition: orgDataCount > 1,
4050 withTrimXDomain: false,
4051 withUpdateXAxis: true
4055 ChartInternal.prototype.generateFlow = function (args) {
4060 return function () {
4061 var targets = args.targets,
4063 drawBar = args.drawBar,
4064 drawLine = args.drawLine,
4065 drawArea = args.drawArea,
4069 xForText = args.xForText,
4070 yForText = args.yForText,
4071 duration = args.duration;
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(),
4082 durationForFlow = flow.duration || duration,
4083 done = flow.done || function () {},
4084 wait = $$.generateWait();
4086 var xgrid, xgridLines, mainRegion, mainText, mainBar, mainLine, mainArea, mainCircle;
4091 // remove head data after rendered
4092 $$.data.targets.forEach(function (d) {
4093 d.values.splice(0, flowLength);
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);
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([]);
4112 // generate transform to flow
4113 if (!flow.orgDataCount) {
4115 if ($$.data.targets[0].values.length !== 1) {
4116 translateX = $$.x(orgDomain[0]) - $$.x(domain[0]);
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);
4123 translateX = diffDomain(domain) / 2;
4126 } else if (flow.orgDataCount === 1 || (flowStart && flowStart.x) === (flowEnd && flowEnd.x)) {
4127 translateX = $$.x(orgDomain[0]) - $$.x(domain[0]);
4129 if ($$.isTimeSeries()) {
4130 translateX = $$.x(orgDomain[0]) - $$.x(domain[0]);
4132 translateX = $$.x(flowStart.x) - $$.x(flowEnd.x);
4135 scaleX = diffDomain(orgDomain) / diffDomain(domain);
4136 transform = 'translate(' + translateX + ',0) scale(' + scaleX + ',1)';
4138 $$.hideXGridFocus();
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));
4155 // remove flowed elements
4157 for (i = 0; i < flowLength; i++) {
4158 shapes.push('.' + CLASS.shape + '-' + (flowIndex + i));
4159 texts.push('.' + CLASS.text + '-' + (flowIndex + i));
4161 $$.svg.selectAll('.' + CLASS.shapes).selectAll(shapes).remove();
4162 $$.svg.selectAll('.' + CLASS.texts).selectAll(texts).remove();
4163 $$.svg.select('.' + CLASS.xgrid).remove();
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($$));
4179 // callback for end of flow
4187 Chart.prototype.focus = function (targetIds) {
4188 var $$ = this.internal,
4191 targetIds = $$.mapToTargetIds(targetIds);
4192 candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$))), this.revert();
4194 candidates.classed(CLASS.focused, true).classed(CLASS.defocused, false);
4195 if ($$.hasArcType()) {
4196 $$.expandArc(targetIds);
4198 $$.toggleFocusLegend(targetIds, true);
4200 $$.focusedTargetIds = targetIds;
4201 $$.defocusedTargetIds = $$.defocusedTargetIds.filter(function (id) {
4202 return targetIds.indexOf(id) < 0;
4206 Chart.prototype.defocus = function (targetIds) {
4207 var $$ = this.internal,
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);
4215 $$.toggleFocusLegend(targetIds, false);
4217 $$.focusedTargetIds = $$.focusedTargetIds.filter(function (id) {
4218 return targetIds.indexOf(id) < 0;
4220 $$.defocusedTargetIds = targetIds;
4223 Chart.prototype.revert = function (targetIds) {
4224 var $$ = this.internal,
4227 targetIds = $$.mapToTargetIds(targetIds);
4228 candidates = $$.svg.selectAll($$.selectorTargets(targetIds)); // should be for all targets
4230 candidates.classed(CLASS.focused, false).classed(CLASS.defocused, false);
4231 if ($$.hasArcType()) {
4232 $$.unexpandArc(targetIds);
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);
4241 $$.focusedTargetIds = [];
4242 $$.defocusedTargetIds = [];
4245 Chart.prototype.xgrids = function (grids) {
4246 var $$ = this.internal,
4249 return config.grid_x_lines;
4251 config.grid_x_lines = grids;
4252 $$.redrawWithoutRescale();
4253 return config.grid_x_lines;
4255 Chart.prototype.xgrids.add = function (grids) {
4256 var $$ = this.internal;
4257 return this.xgrids($$.config.grid_x_lines.concat(grids ? grids : []));
4259 Chart.prototype.xgrids.remove = function (params) {
4261 var $$ = this.internal;
4262 $$.removeGridLines(params, true);
4265 Chart.prototype.ygrids = function (grids) {
4266 var $$ = this.internal,
4269 return config.grid_y_lines;
4271 config.grid_y_lines = grids;
4272 $$.redrawWithoutRescale();
4273 return config.grid_y_lines;
4275 Chart.prototype.ygrids.add = function (grids) {
4276 var $$ = this.internal;
4277 return this.ygrids($$.config.grid_y_lines.concat(grids ? grids : []));
4279 Chart.prototype.ygrids.remove = function (params) {
4281 var $$ = this.internal;
4282 $$.removeGridLines(params, false);
4285 Chart.prototype.groups = function (groups) {
4286 var $$ = this.internal,
4288 if (isUndefined(groups)) {
4289 return config.data_groups;
4291 config.data_groups = groups;
4293 return config.data_groups;
4296 Chart.prototype.legend = function () {};
4297 Chart.prototype.legend.show = function (targetIds) {
4298 var $$ = this.internal;
4299 $$.showLegend($$.mapToTargetIds(targetIds));
4300 $$.updateAndRedraw({ withLegend: true });
4302 Chart.prototype.legend.hide = function (targetIds) {
4303 var $$ = this.internal;
4304 $$.hideLegend($$.mapToTargetIds(targetIds));
4305 $$.updateAndRedraw({ withLegend: false });
4308 Chart.prototype.load = function (args) {
4309 var $$ = this.internal,
4311 // update xs if specified
4315 // update names if exists
4316 if ('names' in args) {
4317 Chart.prototype.data.names.bind(this)(args.names);
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];
4325 // update categories if exists
4326 if ('categories' in args && $$.isCategorized()) {
4327 config.axis_x_categories = args.categories;
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];
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];
4341 // use cache if exists
4342 if ('cacheIds' in args && $$.hasCaches(args.cacheIds)) {
4343 $$.load($$.getCaches(args.cacheIds), args.done);
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);
4353 $$.loadFromArgs(args);
4357 Chart.prototype.unload = function (args) {
4358 var $$ = this.internal;
4360 if (args instanceof Array) {
4361 args = { ids: args };
4362 } else if (typeof args === 'string') {
4363 args = { ids: [args] };
4365 $$.unload($$.mapToTargetIds(args.ids), function () {
4366 $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true });
4373 Chart.prototype.regions = function (regions) {
4374 var $$ = this.internal,
4377 return config.regions;
4379 config.regions = regions;
4380 $$.redrawWithoutRescale();
4381 return config.regions;
4383 Chart.prototype.regions.add = function (regions) {
4384 var $$ = this.internal,
4387 return config.regions;
4389 config.regions = config.regions.concat(regions);
4390 $$.redrawWithoutRescale();
4391 return config.regions;
4393 Chart.prototype.regions.remove = function (options) {
4394 var $$ = this.internal,
4400 options = options || {};
4401 duration = $$.getOption(options, "duration", config.transition_duration);
4402 classes = $$.getOption(options, "classes", [CLASS.region]);
4404 regions = $$.main.select('.' + CLASS.regions).selectAll(classes.map(function (c) {
4407 (duration ? regions.transition().duration(duration) : regions).style('opacity', 0).remove();
4409 config.regions = config.regions.filter(function (region) {
4411 if (!region['class']) {
4414 region['class'].split(' ').forEach(function (c) {
4415 if (classes.indexOf(c) >= 0) {
4422 return config.regions;
4425 Chart.prototype.selected = function (targetId) {
4426 var $$ = this.internal,
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;
4436 Chart.prototype.select = function (ids, indices, resetOther) {
4437 var $$ = this.internal,
4440 if (!config.data_selection_enabled) {
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)) {
4454 if (isTargetId && isTargetIndex) {
4455 if (config.data_selection_isselectable(d) && !isSelected) {
4456 toggle(true, shape.classed(CLASS.SELECTED, true), d, i);
4458 } else if (isDefined(resetOther) && resetOther) {
4460 toggle(false, shape.classed(CLASS.SELECTED, false), d, i);
4465 Chart.prototype.unselect = function (ids, indices) {
4466 var $$ = this.internal,
4469 if (!config.data_selection_enabled) {
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)) {
4483 if (isTargetId && isTargetIndex) {
4484 if (config.data_selection_isselectable(d)) {
4486 toggle(false, shape.classed(CLASS.SELECTED, false), d, i);
4493 Chart.prototype.show = function (targetIds, options) {
4494 var $$ = this.internal,
4497 targetIds = $$.mapToTargetIds(targetIds);
4498 options = options || {};
4500 $$.removeHiddenTargetIds(targetIds);
4501 targets = $$.svg.selectAll($$.selectorTargets(targetIds));
4503 targets.transition().style('display', 'initial', 'important').style('opacity', 1, 'important').call($$.endall, function () {
4504 targets.style('opacity', null).style('opacity', 1);
4507 if (options.withLegend) {
4508 $$.showLegend(targetIds);
4511 $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true });
4514 Chart.prototype.hide = function (targetIds, options) {
4515 var $$ = this.internal,
4518 targetIds = $$.mapToTargetIds(targetIds);
4519 options = options || {};
4521 $$.addHiddenTargetIds(targetIds);
4522 targets = $$.svg.selectAll($$.selectorTargets(targetIds));
4524 targets.transition().style('opacity', 0, 'important').call($$.endall, function () {
4525 targets.style('opacity', null).style('opacity', 0);
4526 targets.style('display', 'none');
4529 if (options.withLegend) {
4530 $$.hideLegend(targetIds);
4533 $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true });
4536 Chart.prototype.toggle = function (targetIds, options) {
4539 $$.mapToTargetIds(targetIds).forEach(function (targetId) {
4540 $$.isTargetToShow(targetId) ? that.hide(targetId, options) : that.show(targetId, options);
4544 Chart.prototype.tooltip = function () {};
4545 Chart.prototype.tooltip.show = function (args) {
4546 var $$ = this.internal,
4551 // determine mouse position on the chart
4555 // determine focus data
4558 } else if (typeof args.x !== 'undefined') {
4560 targets = $$.data.targets.filter(function (t) {
4561 return t.id === args.id;
4564 targets = $$.data.targets;
4566 data = $$.filterByX(targets, args.x).slice(0, 1)[0];
4568 mouse = data ? $$.getMousePosition(data) : null;
4571 // emulate mouse events to show
4572 $$.dispatchEvent('mousemove', mouse);
4574 $$.config.tooltip_onshow.call($$, data);
4576 Chart.prototype.tooltip.hide = function () {
4577 // TODO: get target data by checking the state of focus
4578 this.internal.dispatchEvent('mouseout', 0);
4580 this.internal.config.tooltip_onhide.call(this);
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);
4589 ChartInternal.prototype.transformTo = function (targetIds, type, optionsForRedraw) {
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);
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 });
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 });
4617 Chart.prototype.zoom = function (domain) {
4618 var $$ = this.internal;
4620 if ($$.isTimeSeries()) {
4621 domain = domain.map(function (x) {
4622 return $$.parseDate(x);
4625 if ($$.config.subchart_show) {
4626 $$.brush.selectionAsValue(domain, true);
4628 $$.updateXDomain(null, true, false, false, domain);
4629 $$.redraw({ withY: $$.config.zoom_rescale, withSubchart: false });
4631 $$.config.zoom_onzoom.call(this, $$.x.orgDomain());
4634 return $$.x.domain();
4637 Chart.prototype.zoom.enable = function (enabled) {
4638 var $$ = this.internal;
4639 $$.config.zoom_enabled = enabled;
4640 $$.updateAndRedraw();
4642 Chart.prototype.unzoom = function () {
4643 var $$ = this.internal;
4644 if ($$.config.subchart_show) {
4647 $$.updateXDomain(null, true, false, false, $$.subX.domain());
4648 $$.redraw({ withY: $$.config.zoom_rescale, withSubchart: false });
4652 Chart.prototype.zoom.max = function (max) {
4653 var $$ = this.internal,
4656 if (max === 0 || max) {
4657 config.zoom_x_max = d3.max([$$.orgXDomain[1], max]);
4659 return config.zoom_x_max;
4663 Chart.prototype.zoom.min = function (min) {
4664 var $$ = this.internal,
4667 if (min === 0 || min) {
4668 config.zoom_x_min = d3.min([$$.orgXDomain[0], min]);
4670 return config.zoom_x_min;
4674 Chart.prototype.zoom.range = function (range) {
4675 if (arguments.length) {
4676 if (isDefined(range.max)) {
4677 this.domain.max(range.max);
4679 if (isDefined(range.min)) {
4680 this.domain.min(range.min);
4684 max: this.domain.max(),
4685 min: this.domain.min()
4690 ChartInternal.prototype.initPie = function () {
4693 $$.pie = d3.pie().value(function (d) {
4694 return d.values.reduce(function (a, b) {
4699 var orderFct = $$.getOrderFunction();
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;
4709 $$.pie.sort(orderFct || null);
4712 ChartInternal.prototype.updateRadius = function () {
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;
4724 ChartInternal.prototype.updateArc = function () {
4726 $$.svgArc = $$.getSvgArc();
4727 $$.svgArcExpanded = $$.getSvgArcExpanded();
4728 $$.svgArcExpandedSub = $$.getSvgArcExpanded(0.98);
4731 ChartInternal.prototype.updateAngle = function (d) {
4745 $$.pie($$.filterTargetsToShow($$.data.targets)).forEach(function (t) {
4746 if (!found && t.data.id === d.data.id) {
4753 if (isNaN(d.startAngle)) {
4756 if (isNaN(d.endAngle)) {
4757 d.endAngle = d.startAngle;
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;
4767 return found ? d : null;
4770 ChartInternal.prototype.getSvgArc = function () {
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;
4779 newArc = function newArc(d, withoutUpdate) {
4781 if (withoutUpdate) {
4783 } // for interpolate
4784 updated = $$.updateAngle(d);
4785 return updated ? arc(updated) : "M 0 0";
4787 // TODO: extends all function
4788 newArc.centroid = arc.centroid;
4792 ChartInternal.prototype.getSvgArcExpanded = function (rate) {
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;
4803 return function (d) {
4804 var updated = $$.updateAngle(d);
4805 return updated ? arc(updated) : "M 0 0";
4809 ChartInternal.prototype.getArc = function (d, withoutUpdate, force) {
4810 return force || this.isArcType(d.data) ? this.svgArc(d, withoutUpdate) : "M 0 0";
4813 ChartInternal.prototype.transformForArcLabel = function (d) {
4816 updated = $$.updateAngle(d),
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;
4834 ratio = $$.radius && h ? (36 / $$.radius > 0.375 ? 1.175 - 36 / $$.radius : 0.8) * $$.radius / h : 0;
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 + ")";
4846 ChartInternal.prototype.getArcRatio = function (d) {
4849 whole = Math.PI * ($$.hasType('gauge') && !config.gauge_fullCircle ? 1 : 2);
4850 return d ? (d.endAngle - d.startAngle) / whole : null;
4853 ChartInternal.prototype.convertToArcData = function (d) {
4854 return this.addName({
4857 ratio: this.getArcRatio(d),
4862 ChartInternal.prototype.textForArcLabel = function (d) {
4869 if (!$$.shouldShowArcLabel()) {
4872 updated = $$.updateAngle(d);
4873 value = updated ? updated.value : null;
4874 ratio = $$.getArcRatio(updated);
4876 if (!$$.hasType('gauge') && !$$.meetsArcLabelThreshold(ratio)) {
4879 format = $$.getArcLabelFormat();
4880 return format ? format(value, ratio, id) : $$.defaultArcValueFormat(value, ratio);
4883 ChartInternal.prototype.textForGaugeMinMax = function (value, isMax) {
4885 format = $$.getGaugeLabelExtents();
4887 return format ? format(value, isMax) : value;
4890 ChartInternal.prototype.expandArc = function (targetIds) {
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);
4907 targetIds = $$.mapToTargetIds(targetIds);
4909 $$.svg.selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)).each(function (d) {
4910 if (!$$.shouldExpand(d.data.id)) {
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)) ;
4919 ChartInternal.prototype.unexpandArc = function (targetIds) {
4922 if ($$.transiting) {
4926 targetIds = $$.mapToTargetIds(targetIds);
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);
4934 ChartInternal.prototype.expandDuration = function (id) {
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;
4949 ChartInternal.prototype.shouldExpand = function (id) {
4952 return $$.isDonutType(id) && config.donut_expand || $$.isGaugeType(id) && config.gauge_expand || $$.isPieType(id) && config.pie_expand;
4955 ChartInternal.prototype.shouldShowArcLabel = function () {
4959 if ($$.hasType('donut')) {
4960 shouldShow = config.donut_label_show;
4961 } else if ($$.hasType('pie')) {
4962 shouldShow = config.pie_label_show;
4964 // when gauge, always true
4968 ChartInternal.prototype.meetsArcLabelThreshold = function (ratio) {
4971 threshold = $$.hasType('donut') ? config.donut_label_threshold : config.pie_label_threshold;
4972 return ratio >= threshold;
4975 ChartInternal.prototype.getArcLabelFormat = function () {
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;
4987 ChartInternal.prototype.getGaugeLabelExtents = function () {
4990 return config.gauge_label_extents;
4993 ChartInternal.prototype.getArcTitle = function () {
4995 return $$.hasType('donut') ? $$.config.donut_title : "";
4998 ChartInternal.prototype.updateTargetsForArc = function (targets) {
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);
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();
5016 ChartInternal.prototype.initArc = function () {
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());
5022 ChartInternal.prototype.redrawArc = function (duration, durationForExit, withTransform) {
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;
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);
5049 if ($$.filterTargetsToShow($$.data.targets).length === 1) {
5050 mainArcLabelLine.style("display", "none");
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) {
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 + ")";
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");
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
5083 updated = $$.updateAngle(d);
5085 arcData = $$.convertToArcData(updated);
5087 $$.expandArc(updated.data.id);
5088 $$.api.focus(updated.data.id);
5089 $$.toggleFocusLegend(updated.data.id, true);
5090 $$.config.data_onmouseover(arcData, this);
5092 } : null).on('mousemove', config.interaction_enabled ? function (d) {
5093 var updated = $$.updateAngle(d),
5097 arcData = $$.convertToArcData(updated), selectedData = [arcData];
5098 $$.showTooltip(selectedData, this);
5100 } : null).on('mouseout', config.interaction_enabled ? function (d) {
5101 var updated, arcData;
5102 if ($$.transiting) {
5103 // skip while transiting
5106 updated = $$.updateAngle(d);
5108 arcData = $$.convertToArcData(updated);
5110 $$.unexpandArc(updated.data.id);
5114 $$.config.data_onmouseout(arcData, this);
5116 } : null).on('click', config.interaction_enabled ? function (d, i) {
5117 var updated = $$.updateAngle(d),
5120 arcData = $$.convertToArcData(updated);
5121 if ($$.toggleShape) {
5122 $$.toggleShape(this, arcData, i);
5124 $$.config.data_onclick.call($$.api, arcData, this);
5126 } : null).each(function () {
5127 $$.transiting = true;
5128 }).transition().duration(duration).attrTween("d", function (d) {
5129 var updated = $$.updateAngle(d),
5132 return function () {
5136 // if (this._current === d) {
5137 // this._current = {
5138 // startAngle: Math.PI*2,
5139 // endAngle: Math.PI*2,
5142 if (isNaN(this._current.startAngle)) {
5143 this._current.startAngle = 0;
5145 if (isNaN(this._current.endAngle)) {
5146 this._current.endAngle = this._current.startAngle;
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);
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;
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;
5169 main.select('.' + CLASS.chartArcsTitle).style("opacity", $$.hasType('donut') || hasGaugeType ? 1 : 0);
5173 var backgroundArc = $$.arcs.select('g.' + CLASS.chartArcsBackground).selectAll('path.' + CLASS.chartArcsBackground).data($$.data.targets);
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) {
5183 data: [{ value: config.gauge_max }],
5184 startAngle: config.gauge_startingAngle,
5185 endAngle: -1 * config.gauge_startingAngle * (config.gauge_fullCircle ? Math.PI : 1),
5188 return $$.getArc(d, true, true);
5191 backgroundArc.exit().remove();
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) : '');
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");
5207 ChartInternal.prototype.getGaugeLabelHeight = function () {
5208 return this.config.gauge_label_show ? 20 : 0;
5211 ChartInternal.prototype.hasCaches = function (ids) {
5212 for (var i = 0; i < ids.length; i++) {
5213 if (!(ids[i] in this.cache)) {
5219 ChartInternal.prototype.addCache = function (id, target) {
5220 this.cache[id] = this.cloneTarget(target);
5222 ChartInternal.prototype.getCaches = function (ids) {
5225 for (i = 0; i < ids.length; i++) {
5226 if (ids[i] in this.cache) {
5227 targets.push(this.cloneTarget(this.cache[ids[i]]));
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;
5238 ChartInternal.prototype.generateTargetClass = function (targetId) {
5239 return targetId || targetId === 0 ? ('-' + targetId).replace(/\s/g, '-') : '';
5241 ChartInternal.prototype.generateClass = function (prefix, targetId) {
5242 return " " + prefix + " " + prefix + this.generateTargetClass(targetId);
5244 ChartInternal.prototype.classText = function (d) {
5245 return this.generateClass(CLASS.text, d.index);
5247 ChartInternal.prototype.classTexts = function (d) {
5248 return this.generateClass(CLASS.texts, d.id);
5250 ChartInternal.prototype.classShape = function (d) {
5251 return this.generateClass(CLASS.shape, d.index);
5253 ChartInternal.prototype.classShapes = function (d) {
5254 return this.generateClass(CLASS.shapes, d.id);
5256 ChartInternal.prototype.classLine = function (d) {
5257 return this.classShape(d) + this.generateClass(CLASS.line, d.id);
5259 ChartInternal.prototype.classLines = function (d) {
5260 return this.classShapes(d) + this.generateClass(CLASS.lines, d.id);
5262 ChartInternal.prototype.classCircle = function (d) {
5263 return this.classShape(d) + this.generateClass(CLASS.circle, d.index);
5265 ChartInternal.prototype.classCircles = function (d) {
5266 return this.classShapes(d) + this.generateClass(CLASS.circles, d.id);
5268 ChartInternal.prototype.classBar = function (d) {
5269 return this.classShape(d) + this.generateClass(CLASS.bar, d.index);
5271 ChartInternal.prototype.classBars = function (d) {
5272 return this.classShapes(d) + this.generateClass(CLASS.bars, d.id);
5274 ChartInternal.prototype.classArc = function (d) {
5275 return this.classShape(d.data) + this.generateClass(CLASS.arc, d.data.id);
5277 ChartInternal.prototype.classArcs = function (d) {
5278 return this.classShapes(d.data) + this.generateClass(CLASS.arcs, d.data.id);
5280 ChartInternal.prototype.classArea = function (d) {
5281 return this.classShape(d) + this.generateClass(CLASS.area, d.id);
5283 ChartInternal.prototype.classAreas = function (d) {
5284 return this.classShapes(d) + this.generateClass(CLASS.areas, d.id);
5286 ChartInternal.prototype.classRegion = function (d, i) {
5287 return this.generateClass(CLASS.region, i) + ' ' + ('class' in d ? d['class'] : '');
5289 ChartInternal.prototype.classEvent = function (d) {
5290 return this.generateClass(CLASS.eventRect, d.index);
5292 ChartInternal.prototype.classTarget = function (id) {
5294 var additionalClassSuffix = $$.config.data_classes[id],
5295 additionalClass = '';
5296 if (additionalClassSuffix) {
5297 additionalClass = ' ' + CLASS.target + '-' + additionalClassSuffix;
5299 return $$.generateClass(CLASS.target, id) + additionalClass;
5301 ChartInternal.prototype.classFocus = function (d) {
5302 return this.classFocused(d) + this.classDefocused(d);
5304 ChartInternal.prototype.classFocused = function (d) {
5305 return ' ' + (this.focusedTargetIds.indexOf(d.id) >= 0 ? CLASS.focused : '');
5307 ChartInternal.prototype.classDefocused = function (d) {
5308 return ' ' + (this.defocusedTargetIds.indexOf(d.id) >= 0 ? CLASS.defocused : '');
5310 ChartInternal.prototype.classChartText = function (d) {
5311 return CLASS.chartText + this.classTarget(d.id);
5313 ChartInternal.prototype.classChartLine = function (d) {
5314 return CLASS.chartLine + this.classTarget(d.id);
5316 ChartInternal.prototype.classChartBar = function (d) {
5317 return CLASS.chartBar + this.classTarget(d.id);
5319 ChartInternal.prototype.classChartArc = function (d) {
5320 return CLASS.chartArc + this.classTarget(d.data.id);
5322 ChartInternal.prototype.getTargetSelectorSuffix = function (targetId) {
5323 return this.generateTargetClass(targetId).replace(/([?!@#$%^&*()_=+,.<>'":;\[\]\/|~`{}\\])/g, '\\$1');
5325 ChartInternal.prototype.selectorTarget = function (id, prefix) {
5326 return (prefix || '') + '.' + CLASS.target + this.getTargetSelectorSuffix(id);
5328 ChartInternal.prototype.selectorTargets = function (ids, prefix) {
5331 return ids.length ? ids.map(function (id) {
5332 return $$.selectorTarget(id, prefix);
5335 ChartInternal.prototype.selectorLegend = function (id) {
5336 return '.' + CLASS.legendItem + this.getTargetSelectorSuffix(id);
5338 ChartInternal.prototype.selectorLegends = function (ids) {
5340 return ids && ids.length ? ids.map(function (id) {
5341 return $$.selectorLegend(id);
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 + ")";
5349 ChartInternal.prototype.appendClip = function (parent, id) {
5350 return parent.append("clipPath").attr("id", id).append("rect");
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);
5357 ChartInternal.prototype.getAxisClipY = function (forHorizontal) {
5358 return forHorizontal ? -20 : -this.margin.top;
5360 ChartInternal.prototype.getXAxisClipX = function () {
5362 return $$.getAxisClipX(!$$.config.axis_rotated);
5364 ChartInternal.prototype.getXAxisClipY = function () {
5366 return $$.getAxisClipY(!$$.config.axis_rotated);
5368 ChartInternal.prototype.getYAxisClipX = function () {
5370 return $$.config.axis_y_inner ? -1 : $$.getAxisClipX($$.config.axis_rotated);
5372 ChartInternal.prototype.getYAxisClipY = function () {
5374 return $$.getAxisClipY($$.config.axis_rotated);
5376 ChartInternal.prototype.getAxisClipWidth = function (forHorizontal) {
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;
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;
5387 ChartInternal.prototype.getXAxisClipWidth = function () {
5389 return $$.getAxisClipWidth(!$$.config.axis_rotated);
5391 ChartInternal.prototype.getXAxisClipHeight = function () {
5393 return $$.getAxisClipHeight(!$$.config.axis_rotated);
5395 ChartInternal.prototype.getYAxisClipWidth = function () {
5397 return $$.getAxisClipWidth($$.config.axis_rotated) + ($$.config.axis_y_inner ? 20 : 0);
5399 ChartInternal.prototype.getYAxisClipHeight = function () {
5401 return $$.getAxisClipHeight($$.config.axis_rotated);
5404 ChartInternal.prototype.generateColor = function () {
5408 colors = config.data_colors,
5409 pattern = notEmpty(config.color_pattern) ? config.color_pattern : d3.schemeCategory10,
5410 callback = config.data_color,
5413 return function (d) {
5414 var id = d.id || d.data && d.data.id || d,
5417 // if callback function is provided
5418 if (colors[id] instanceof Function) {
5419 color = colors[id](d);
5421 // if specified, choose that color
5422 else if (colors[id]) {
5425 // if not specified, choose from pattern
5427 if (ids.indexOf(id) < 0) {
5430 color = pattern[ids.indexOf(id) % pattern.length];
5433 return callback instanceof Function ? callback(color, d) : color;
5436 ChartInternal.prototype.generateLevelColor = function () {
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) {
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]) {
5459 ChartInternal.prototype.getDefaultConfig = function () {
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,
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,
5492 data_xFormat: '%Y-%m-%d',
5493 data_xLocaltime: true,
5495 data_idConverter: function data_idConverter(id) {
5502 data_type: undefined,
5507 data_color: undefined,
5510 data_filter: undefined,
5511 data_selection_enabled: false,
5512 data_selection_grouped: false,
5513 data_selection_isselectable: function data_selection_isselectable() {
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: "",
5533 subchart_show: false,
5534 subchart_size_height: 60,
5535 subchart_axis_x_show: true,
5536 subchart_onbrush: function subchart_onbrush() {},
5539 color_threshold: {},
5543 legend_position: 'bottom',
5544 legend_inset_anchor: 'top-left',
5547 legend_inset_step: undefined,
5548 legend_item_onclick: undefined,
5549 legend_item_onmouseover: undefined,
5550 legend_item_onmouseout: undefined,
5551 legend_equally: false,
5553 legend_item_tile_width: 10,
5554 legend_item_tile_height: 10,
5556 axis_rotated: false,
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,
5576 axis_x_height: undefined,
5577 axis_x_selection: undefined,
5579 axis_x_inner: undefined,
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,
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,
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,
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,
5612 grid_x_type: 'tick',
5616 // grid_y_type: 'tick',
5619 grid_focus_show: true,
5620 grid_lines_front: true,
5621 // point - point of each data
5624 point_sensitivity: 10,
5625 point_focus_expand_enabled: true,
5626 point_focus_expand_r: undefined,
5627 point_select_r: undefined,
5629 line_connectNull: false,
5630 line_step_type: 'step',
5632 bar_width: undefined,
5633 bar_width_ratio: 0.6,
5634 bar_width_max: undefined,
5635 bar_zerobased: true,
5638 area_zerobased: true,
5641 pie_label_show: true,
5642 pie_label_format: undefined,
5643 pie_label_threshold: 0.05,
5644 pie_label_ratio: undefined,
5646 pie_expand_duration: 50,
5648 gauge_fullCircle: false,
5649 gauge_label_show: true,
5650 gauge_labelLine_show: true,
5651 gauge_label_format: undefined,
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,
5660 gauge_expand_duration: 50,
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,
5669 donut_expand_duration: 50,
5671 spline_interpolation_type: 'cardinal',
5672 // region - region to change style
5674 // tooltip - show when mouseover on each data
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) : '';
5685 tooltip_init_show: false,
5687 tooltip_init_position: { top: '0px', left: '50px' },
5688 tooltip_onshow: function tooltip_onshow() {},
5689 tooltip_onhide: function tooltip_onhide() {},
5691 title_text: undefined,
5698 title_position: 'top-center'
5701 Object.keys(this.additionalConfig).forEach(function (key) {
5702 config[key] = this.additionalConfig[key];
5707 ChartInternal.prototype.additionalConfig = {};
5709 ChartInternal.prototype.loadConfig = function (config) {
5710 var this_config = this.config,
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];
5726 Object.keys(this_config).forEach(function (key) {
5728 keys = key.split('_');
5730 // console.log("CONFIG : ", key, read);
5731 if (isDefined(read)) {
5732 this_config[key] = read;
5737 ChartInternal.prototype.convertUrlToData = function (url, mimeType, headers, keys, done) {
5739 type = mimeType ? mimeType : 'csv',
5743 if (type === 'json') {
5745 converter = $$.convertJsonToData;
5746 } else if (type === 'tsv') {
5748 converter = $$.convertXsvToData;
5751 converter = $$.convertXsvToData;
5754 f(url, headers).then(function (data) {
5755 done.call($$, converter.call($$, data, keys));
5756 }).catch(function (error) {
5760 ChartInternal.prototype.convertXsvToData = function (xsv) {
5761 var keys = xsv.columns,
5763 if (rows.length === 0) {
5764 return { keys: keys, rows: [keys.reduce(function (row, key) {
5765 return Object.assign(row, defineProperty({}, key, null));
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) };
5773 ChartInternal.prototype.convertJsonToData = function (json, keys) {
5779 // when keys specified, json would be an array that includes objects
5781 targetKeys = keys.value.concat(keys.x);
5782 $$.config.data_x = keys.x;
5784 targetKeys = keys.value;
5786 new_rows.push(targetKeys);
5787 json.forEach(function (o) {
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)) {
5797 new_rows.push(new_row);
5799 data = $$.convertRowsToData(new_rows);
5801 Object.keys(json).forEach(function (key) {
5802 new_rows.push([key].concat(json[key]));
5804 data = $$.convertColumnsToData(new_rows);
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];
5824 * Converts the rows to normalized data.
5825 * @param {any[][]} rows The row data
5828 ChartInternal.prototype.convertRowsToData = function (rows) {
5832 for (var i = 1; i < rows.length; i++) {
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 + ")!");
5838 newRow[keys[j]] = rows[i][j];
5840 newRows.push(newRow);
5842 return { keys: keys, rows: newRows };
5846 * Converts the columns to normalized data.
5847 * @param {any[][]} columns The column data
5850 ChartInternal.prototype.convertColumnsToData = function (columns) {
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] = {};
5860 if (isUndefined(columns[i][j])) {
5861 throw new Error("Source data is missing a component at (" + i + "," + j + ")!");
5863 newRows[j - 1][key] = columns[i][j];
5868 return { keys: keys, rows: newRows };
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.
5879 ChartInternal.prototype.convertDataToTargets = function (data, appendXs) {
5887 // handles format where keys are not orderly provided
5888 if (isArray(data)) {
5889 keys = Object.keys(data[0]);
5895 ids = keys.filter($$.isNotX, $$);
5896 xs = keys.filter($$.isX, $$);
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);
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) {
5907 }).filter(isValue).map(function (rawX, i) {
5908 return $$.generateTargetX(rawX, id, i);
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();
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);
5919 // MEMO: if no x included, use same x of current will be used
5921 $$.data.xs[id] = data.map(function (d, i) {
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 + '".');
5934 // convert to target
5935 targets = ids.map(function (id, index) {
5936 var convertedId = config.data_idConverter(id);
5940 values: data.map(function (d, i) {
5941 var xKey = $$.getXKey(id),
5943 value = d[id] !== null && !isNaN(d[id]) ? +d[id] : null,
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 = [];
5950 x = config.axis_x_categories.indexOf(rawX);
5952 x = config.axis_x_categories.length;
5953 config.axis_x_categories.push(rawX);
5956 x = $$.generateTargetX(rawX, id, i);
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) {
5962 return { x: x, value: value, id: convertedId };
5963 }).filter(function (v) {
5964 return isDefined(v.x);
5970 targets.forEach(function (t) {
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;
5980 // indexing each value
5982 t.values.forEach(function (v) {
5985 // this needs to be sorted because its index and value.index is identical
5986 $$.data.xs[t.id].sort(function (v1, v2) {
5991 // cache information about values
5992 $$.hasNegativeValue = $$.hasNegativeValueInTargets(targets);
5993 $$.hasPositiveValue = $$.hasPositiveValueInTargets(targets);
5996 if (config.data_type) {
5997 $$.setTargetType($$.mapToIds(targets).filter(function (id) {
5998 return !(id in config.data_types);
5999 }), config.data_type);
6002 // cache as original id keyed
6003 targets.forEach(function (d) {
6004 $$.addCache(d.id_org, d);
6010 ChartInternal.prototype.isX = function (key) {
6013 return config.data_x && key === config.data_x || notEmpty(config.data_xs) && hasValue(config.data_xs, key);
6015 ChartInternal.prototype.isNotX = function (key) {
6016 return !this.isX(key);
6018 ChartInternal.prototype.getXKey = function (id) {
6021 return config.data_x ? config.data_x : notEmpty(config.data_xs) ? config.data_xs[id] : null;
6023 ChartInternal.prototype.getXValuesOfXKey = function (key, targets) {
6026 ids = targets && notEmpty(targets) ? $$.mapToIds(targets) : [];
6027 ids.forEach(function (id) {
6028 if ($$.getXKey(id) === key) {
6029 xValues = $$.data.xs[id];
6034 ChartInternal.prototype.getXValue = function (id, i) {
6036 return id in $$.data.xs && $$.data.xs[id] && isValue($$.data.xs[id][i]) ? $$.data.xs[id][i] : i;
6038 ChartInternal.prototype.getOtherTargetXs = function () {
6040 idsForX = Object.keys($$.data.xs);
6041 return idsForX.length ? $$.data.xs[idsForX[0]] : null;
6043 ChartInternal.prototype.getOtherTargetX = function (index) {
6044 var xs = this.getOtherTargetXs();
6045 return xs && index < xs.length ? xs[index] : null;
6047 ChartInternal.prototype.addXs = function (xs) {
6049 Object.keys(xs).forEach(function (id) {
6050 $$.config.data_xs[id] = xs[id];
6053 ChartInternal.prototype.addName = function (data) {
6057 name = $$.config.data_names[data.id];
6058 data.name = name !== undefined ? name : data.id;
6062 ChartInternal.prototype.getValueOnIndex = function (values, index) {
6063 var valueOnIndex = values.filter(function (v) {
6064 return v.index === index;
6066 return valueOnIndex.length ? valueOnIndex[0] : null;
6068 ChartInternal.prototype.updateTargetX = function (targets, x) {
6070 targets.forEach(function (t) {
6071 t.values.forEach(function (v, i) {
6072 v.x = $$.generateTargetX(x[i], t.id, i);
6074 $$.data.xs[t.id] = x;
6077 ChartInternal.prototype.updateTargetXs = function (targets, xs) {
6079 targets.forEach(function (t) {
6081 $$.updateTargetX([t], xs[t.id]);
6085 ChartInternal.prototype.generateTargetX = function (rawX, id, index) {
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);
6097 ChartInternal.prototype.cloneTarget = function (target) {
6100 id_org: target.id_org,
6101 values: target.values.map(function (d) {
6110 ChartInternal.prototype.getMaxDataCount = function () {
6112 return $$.d3.max($$.data.targets, function (t) {
6113 return t.values.length;
6116 ChartInternal.prototype.mapToIds = function (targets) {
6117 return targets.map(function (d) {
6121 ChartInternal.prototype.mapToTargetIds = function (ids) {
6123 return ids ? [].concat(ids) : $$.mapToIds($$.data.targets);
6125 ChartInternal.prototype.hasTarget = function (targets, id) {
6126 var ids = this.mapToIds(targets),
6128 for (i = 0; i < ids.length; i++) {
6129 if (ids[i] === id) {
6135 ChartInternal.prototype.isTargetToShow = function (targetId) {
6136 return this.hiddenTargetIds.indexOf(targetId) < 0;
6138 ChartInternal.prototype.isLegendToShow = function (targetId) {
6139 return this.hiddenLegendIds.indexOf(targetId) < 0;
6141 ChartInternal.prototype.filterTargetsToShow = function (targets) {
6143 return targets.filter(function (t) {
6144 return $$.isTargetToShow(t.id);
6147 ChartInternal.prototype.mapTargetsToUniqueXs = function (targets) {
6149 var xs = $$.d3.set($$.d3.merge(targets.map(function (t) {
6150 return t.values.map(function (v) {
6154 xs = $$.isTimeSeries() ? xs.map(function (x) {
6155 return new Date(+x);
6156 }) : xs.map(function (x) {
6159 return xs.sort(function (a, b) {
6160 return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
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]);
6171 ChartInternal.prototype.removeHiddenTargetIds = function (targetIds) {
6172 this.hiddenTargetIds = this.hiddenTargetIds.filter(function (id) {
6173 return targetIds.indexOf(id) < 0;
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]);
6184 ChartInternal.prototype.removeHiddenLegendIds = function (targetIds) {
6185 this.hiddenLegendIds = this.hiddenLegendIds.filter(function (id) {
6186 return targetIds.indexOf(id) < 0;
6189 ChartInternal.prototype.getValuesAsIdKeyed = function (targets) {
6191 targets.forEach(function (t) {
6193 t.values.forEach(function (v) {
6194 ys[t.id].push(v.value);
6199 ChartInternal.prototype.checkValueInTargets = function (targets, checker) {
6200 var ids = Object.keys(targets),
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)) {
6214 ChartInternal.prototype.hasNegativeValueInTargets = function (targets) {
6215 return this.checkValueInTargets(targets, function (v) {
6219 ChartInternal.prototype.hasPositiveValueInTargets = function (targets) {
6220 return this.checkValueInTargets(targets, function (v) {
6224 ChartInternal.prototype.isOrderDesc = function () {
6225 var config = this.config;
6226 return typeof config.data_order === 'string' && config.data_order.toLowerCase() === 'desc';
6228 ChartInternal.prototype.isOrderAsc = function () {
6229 var config = this.config;
6230 return typeof config.data_order === 'string' && config.data_order.toLowerCase() === 'asc';
6232 ChartInternal.prototype.getOrderFunction = function () {
6235 orderAsc = $$.isOrderAsc(),
6236 orderDesc = $$.isOrderDesc();
6237 if (orderAsc || orderDesc) {
6238 var reducer = function reducer(p, c) {
6239 return p + Math.abs(c.value);
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;
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);
6255 ChartInternal.prototype.orderTargets = function (targets) {
6256 var fct = this.getOrderFunction();
6262 ChartInternal.prototype.filterByX = function (targets, x) {
6263 return this.d3.merge(targets.map(function (t) {
6265 })).filter(function (v) {
6266 return v.x - x === 0;
6269 ChartInternal.prototype.filterRemoveNull = function (data) {
6270 return data.filter(function (d) {
6271 return isValue(d.value);
6274 ChartInternal.prototype.filterByXDomain = function (targets, xDomain) {
6275 return targets.map(function (t) {
6279 values: t.values.filter(function (v) {
6280 return xDomain[0] <= v.x && v.x <= xDomain[1];
6285 ChartInternal.prototype.hasDataLabel = function () {
6286 var config = this.config;
6287 if (typeof config.data_labels === 'boolean' && config.data_labels) {
6289 } else if (_typeof(config.data_labels) === 'object' && notEmpty(config.data_labels)) {
6294 ChartInternal.prototype.getDataLabelLength = function (min, max, key) {
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;
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);
6310 ChartInternal.prototype.findClosestFromTargets = function (targets, pos) {
6314 // map to array of closest points of each target
6315 candidates = targets.map(function (target) {
6316 return $$.findClosest(target.values, pos);
6319 // decide closest point and return
6320 return $$.findClosest(candidates, pos);
6322 ChartInternal.prototype.findClosest = function (values, pos) {
6324 minDist = $$.config.point_sensitivity,
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)) {
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);
6350 ChartInternal.prototype.dist = function (data, pos) {
6353 xIndex = config.axis_rotated ? 1 : 0,
6354 yIndex = config.axis_rotated ? 0 : 1,
6355 y = $$.circleY(data, data.index),
6357 return Math.sqrt(Math.pow(x - pos[xIndex], 2) + Math.pow(y - pos[yIndex], 2));
6359 ChartInternal.prototype.convertValuesToStep = function (values) {
6360 var converted = [].concat(values),
6363 if (!this.isCategorized()) {
6367 for (i = values.length + 1; 0 < i; i--) {
6368 converted[i] = converted[i - 1];
6372 x: converted[0].x - 1,
6373 value: converted[0].value,
6376 converted[values.length + 1] = {
6377 x: converted[values.length].x + 1,
6378 value: converted[values.length].value,
6379 id: converted[values.length].id
6384 ChartInternal.prototype.updateDataAttributes = function (name, attrs) {
6387 current = config['data_' + name];
6388 if (typeof attrs === 'undefined') {
6391 Object.keys(attrs).forEach(function (id) {
6392 current[id] = attrs[id];
6400 ChartInternal.prototype.load = function (targets, args) {
6403 // filter loading targets if needed
6405 targets = targets.filter(args.filter);
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);
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);
6424 $$.data.targets = $$.data.targets.concat(targets); // add remained
6428 $$.updateTargets($$.data.targets);
6430 // Redraw with new targets
6431 $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true });
6437 ChartInternal.prototype.loadFromArgs = function (args) {
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);
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);
6452 $$.load(null, args);
6455 ChartInternal.prototype.unload = function (targetIds, done) {
6458 done = function done() {};
6460 // filter existing target
6461 targetIds = targetIds.filter(function (id) {
6462 return $$.hasTarget($$.data.targets, id);
6464 // If no target, call done and return
6465 if (!targetIds || targetIds.length === 0) {
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
6477 $$.legend.selectAll('.' + CLASS.legendItem + $$.getTargetSelectorSuffix(id)).remove();
6480 $$.data.targets = $$.data.targets.filter(function (t) {
6486 ChartInternal.prototype.getYDomainMin = function (targets) {
6489 ids = $$.mapToIds(targets),
6490 ys = $$.getValuesAsIdKeyed(targets),
6497 if (config.data_groups.length > 0) {
6498 hasNegativeValue = $$.hasNegativeValueInTargets(targets);
6499 for (j = 0; j < config.data_groups.length; j++) {
6501 idsInGroup = config.data_groups[j].filter(function (id) {
6502 return ids.indexOf(id) >= 0;
6504 if (idsInGroup.length === 0) {
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;
6515 for (k = 1; k < idsInGroup.length; k++) {
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;
6528 return $$.d3.min(Object.keys(ys).map(function (key) {
6529 return $$.d3.min(ys[key]);
6532 ChartInternal.prototype.getYDomainMax = function (targets) {
6535 ids = $$.mapToIds(targets),
6536 ys = $$.getValuesAsIdKeyed(targets),
6543 if (config.data_groups.length > 0) {
6544 hasPositiveValue = $$.hasPositiveValueInTargets(targets);
6545 for (j = 0; j < config.data_groups.length; j++) {
6547 idsInGroup = config.data_groups[j].filter(function (id) {
6548 return ids.indexOf(id) >= 0;
6550 if (idsInGroup.length === 0) {
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;
6561 for (k = 1; k < idsInGroup.length; k++) {
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;
6574 return $$.d3.max(Object.keys(ys).map(function (key) {
6575 return $$.d3.max(ys[key]);
6578 ChartInternal.prototype.getYDomain = function (targets, axisId, xDomain) {
6581 targetsByAxisId = targets.filter(function (t) {
6582 return $$.axis.getId(t.id) === axisId;
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),
6594 center = axisId === 'y2' ? config.axis_y2_center : config.axis_y_center,
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;
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;
6610 if (yTargets.length === 0) {
6611 // use current domain if target of axisId is none
6612 return axisId === 'y2' ? $$.y2.domain() : $$.y.domain();
6614 if (isNaN(yDomainMin)) {
6615 // set minimum to zero when not number
6618 if (isNaN(yDomainMax)) {
6619 // set maximum to have same value as yDomainMin
6620 yDomainMax = yDomainMin;
6622 if (yDomainMin === yDomainMax) {
6623 yDomainMin < 0 ? yDomainMax = 0 : yDomainMin = 0;
6625 isAllPositive = yDomainMin >= 0 && yDomainMax >= 0;
6626 isAllNegative = yDomainMin <= 0 && yDomainMax <= 0;
6628 // Cancel zerobased if axis_*_min / axis_*_max specified
6629 if (isValue(yMin) && isAllPositive || isValue(yMax) && isAllNegative) {
6630 isZeroBased = false;
6633 // Bar/Area chart should be 0-based if all positive|negative
6635 if (isAllPositive) {
6638 if (isAllNegative) {
6643 domainLength = Math.abs(yDomainMax - yDomainMin);
6644 padding = padding_top = padding_bottom = domainLength * 0.1;
6646 if (typeof center !== 'undefined') {
6647 yDomainAbs = Math.max(Math.abs(yDomainMin), Math.abs(yDomainMax));
6648 yDomainMax = center + yDomainAbs;
6649 yDomainMin = center - yDomainAbs;
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);
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);
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);
6671 // Bar/Area chart should be 0-based if all positive|negative
6673 if (isAllPositive) {
6674 padding_bottom = yDomainMin;
6676 if (isAllNegative) {
6677 padding_top = -yDomainMax;
6680 domain = [yDomainMin - padding_bottom, yDomainMax + padding_top];
6681 return isInverted ? domain.reverse() : domain;
6683 ChartInternal.prototype.getXDomainMin = function (targets) {
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) {
6692 ChartInternal.prototype.getXDomainMax = function (targets) {
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) {
6701 ChartInternal.prototype.getXDomainPadding = function (domain) {
6704 diff = domain[1] - domain[0],
6709 if ($$.isCategorized()) {
6711 } else if ($$.hasType('bar')) {
6712 maxDataCount = $$.getMaxDataCount();
6713 padding = maxDataCount > 1 ? diff / (maxDataCount - 1) / 2 : 0.5;
6715 padding = diff * 0.01;
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;
6723 paddingLeft = paddingRight = padding;
6725 return { left: paddingLeft, right: paddingRight };
6727 ChartInternal.prototype.getXDomain = function (targets) {
6729 xDomain = [$$.getXDomainMin(targets), $$.getXDomainMax(targets)],
6730 firstX = xDomain[0],
6732 padding = $$.getXDomainPadding(xDomain),
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);
6741 firstX = firstX === 0 ? 1 : firstX * 0.5;
6742 lastX = lastX === 0 ? -1 : lastX * 1.5;
6745 if (firstX || firstX === 0) {
6746 min = $$.isTimeSeries() ? new Date(firstX.getTime() - padding.left) : firstX - padding.left;
6748 if (lastX || lastX === 0) {
6749 max = $$.isTimeSeries() ? new Date(lastX.getTime() + padding.right) : lastX + padding.right;
6753 ChartInternal.prototype.updateXDomain = function (targets, withUpdateXDomain, withUpdateOrgXDomain, withTrim, domain) {
6757 if (withUpdateOrgXDomain) {
6758 $$.x.domain(domain ? domain : $$.d3.extent($$.getXDomain(targets)));
6759 $$.orgXDomain = $$.x.domain();
6760 if (config.zoom_enabled) {
6763 $$.subX.domain($$.x.domain());
6765 $$.brush.updateScale($$.subX);
6768 if (withUpdateXDomain) {
6769 $$.x.domain(domain ? domain : !$$.brush || $$.brush.empty() ? $$.orgXDomain : $$.brush.selectionAsValue());
6772 // Trim domain when too big by zoom mousemove event
6774 $$.x.domain($$.trimXDomain($$.x.orgDomain()));
6777 return $$.x.domain();
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]);
6787 if (max <= domain[1]) {
6788 domain[0] = +domain[0] - (domain[1] - max);
6794 ChartInternal.prototype.drag = function (mouse) {
6799 var sx, sy, mx, my, minX, maxX, minY, maxY;
6801 if ($$.hasArcType()) {
6804 if (!config.data_selection_enabled) {
6806 } // do nothing if not selectable
6807 if (!config.data_selection_multiple) {
6809 } // skip when single selection because drag is used for multiple selection
6811 sx = $$.dragStart[0];
6812 sy = $$.dragStart[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);
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),
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);
6846 toggle = $$.togglePath;
6847 isWithin = !(maxX < _x || _x + _w < minX) && !(maxY < _y || _y + _h < minY);
6849 // line/area selection not supported yet
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);
6861 ChartInternal.prototype.dragstart = function (mouse) {
6864 if ($$.hasArcType()) {
6867 if (!config.data_selection_enabled) {
6869 } // do nothing if not selectable
6870 $$.dragStart = mouse;
6871 $$.main.select('.' + CLASS.chart).append('rect').attr('class', CLASS.dragarea).style('opacity', 0.1);
6875 ChartInternal.prototype.dragend = function () {
6878 if ($$.hasArcType()) {
6881 if (!config.data_selection_enabled) {
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;
6889 ChartInternal.prototype.getYFormat = function (forArc) {
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);
6898 ChartInternal.prototype.yFormat = function (v) {
6901 format = config.axis_y_tick_format ? config.axis_y_tick_format : $$.defaultValueFormat;
6904 ChartInternal.prototype.y2Format = function (v) {
6907 format = config.axis_y2_tick_format ? config.axis_y2_tick_format : $$.defaultValueFormat;
6910 ChartInternal.prototype.defaultValueFormat = function (v) {
6911 return isValue(v) ? +v : "";
6913 ChartInternal.prototype.defaultArcValueFormat = function (v, ratio) {
6914 return (ratio * 100).toFixed(1) + '%';
6916 ChartInternal.prototype.dataLabelFormat = function (targetId) {
6918 data_labels = $$.config.data_labels,
6920 defaultFormat = function defaultFormat(v) {
6921 return isValue(v) ? +v : "";
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];
6930 format = function format() {
6935 format = defaultFormat;
6940 ChartInternal.prototype.initGrid = function () {
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);
6948 if (config.grid_y_show) {
6949 $$.grid.append('g').attr('class', CLASS.ygrids);
6951 if (config.grid_focus_show) {
6952 $$.grid.append('g').attr("class", CLASS.xgridFocus).append('line').attr('class', CLASS.xgridFocus);
6954 $$.xgrid = d3.selectAll([]);
6955 if (!config.grid_lines_front) {
6959 ChartInternal.prototype.initGridLines = function () {
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([]);
6967 ChartInternal.prototype.updateXGrid = function (withoutUpdate) {
6971 xgridData = $$.generateGridData(config.grid_x_type, $$.x),
6972 tickOffset = $$.isCategorized() ? $$.xAxis.tickOffset() : 0;
6974 $$.xgridAttr = config.axis_rotated ? {
6977 'y1': function y1(d) {
6978 return $$.x(d) - tickOffset;
6980 'y2': function y2(d) {
6981 return $$.x(d) - tickOffset;
6984 'x1': function x1(d) {
6985 return $$.x(d) + tickOffset;
6987 'x2': function x2(d) {
6988 return $$.x(d) + tickOffset;
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;
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);
7004 xgrid.exit().remove();
7007 ChartInternal.prototype.updateYGrid = function () {
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');
7021 ChartInternal.prototype.gridTextAnchor = function (d) {
7022 return d.position ? d.position : "end";
7024 ChartInternal.prototype.gridTextDx = function (d) {
7025 return d.position === 'start' ? 4 : d.position === 'middle' ? 0 : -4;
7027 ChartInternal.prototype.xGridTextX = function (d) {
7028 return d.position === 'start' ? -this.height : d.position === 'middle' ? -this.height / 2 : 0;
7030 ChartInternal.prototype.yGridTextX = function (d) {
7031 return d.position === 'start' ? 0 : d.position === 'middle' ? this.width / 2 : this.width;
7033 ChartInternal.prototype.updateGrid = function (duration) {
7041 xv = $$.xv.bind($$),
7042 yv = $$.yv.bind($$),
7043 xGridTextX = $$.xGridTextX.bind($$),
7044 yGridTextX = $$.yGridTextX.bind($$);
7047 $$.grid.style('visibility', $$.hasArcType() ? 'hidden' : 'visible');
7049 main.select('line.' + CLASS.xgridFocus).style("visibility", "hidden");
7050 if (config.grid_x_show) {
7053 xgridLine = main.select('.' + CLASS.xgridLines).selectAll('.' + CLASS.xgridLine).data(config.grid_x_lines);
7055 xgridLineEnter = xgridLine.enter().append('g').attr("class", function (d) {
7056 return CLASS.xgridLine + (d['class'] ? ' ' + d['class'] : '');
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);
7061 $$.xgridLines = xgridLineEnter.merge(xgridLine);
7062 // done in d3.transition() of the end of this function
7064 xgridLine.exit().transition().duration(duration).style("opacity", 0).remove();
7067 if (config.grid_y_show) {
7070 ygridLine = main.select('.' + CLASS.ygridLines).selectAll('.' + CLASS.ygridLine).data(config.grid_y_lines);
7072 ygridLineEnter = ygridLine.enter().append('g').attr("class", function (d) {
7073 return CLASS.ygridLine + (d['class'] ? ' ' + d['class'] : '');
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);
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) {
7082 }).style("opacity", 1);
7084 ygridLine.exit().transition().duration(duration).style("opacity", 0).remove();
7086 ChartInternal.prototype.redrawGrid = function (withTransition, transition) {
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) {
7094 }).style("opacity", 1)];
7096 ChartInternal.prototype.showXGridFocus = function (selectedData) {
7099 dataToShow = selectedData.filter(function (d) {
7100 return d && isValue(d.value);
7102 focusEl = $$.main.selectAll('line.' + CLASS.xgridFocus),
7103 xx = $$.xx.bind($$);
7104 if (!config.tooltip_show) {
7107 // Hide when scatter plot exists
7108 if ($$.hasType('scatter') || $$.hasArcType()) {
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');
7114 ChartInternal.prototype.hideXGridFocus = function () {
7115 this.main.select('line.' + CLASS.xgridFocus).style("visibility", "hidden");
7117 ChartInternal.prototype.updateXgridFocus = function () {
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);
7122 ChartInternal.prototype.generateGridData = function (type, scale) {
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'));
7138 gridData = scale.ticks(10);
7139 if (gridData.length > tickNum) {
7141 gridData = gridData.filter(function (d) {
7142 return ("" + d).indexOf('.') < 0;
7148 ChartInternal.prototype.getGridFilterToRemove = function (params) {
7149 return params ? function (line) {
7151 [].concat(params).forEach(function (param) {
7152 if ('value' in param && line.value === param.value || 'class' in param && line['class'] === param['class']) {
7161 ChartInternal.prototype.removeGridLines = function (params, forX) {
7164 toRemove = $$.getGridFilterToRemove(params),
7165 toShow = function toShow(line) {
7166 return !toRemove(line);
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();
7172 config.grid_x_lines = config.grid_x_lines.filter(toShow);
7174 config.grid_y_lines = config.grid_y_lines.filter(toShow);
7178 ChartInternal.prototype.initEventRect = function () {
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);
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));
7194 ChartInternal.prototype.redrawEventRect = function () {
7203 // TODO: rotated not supported yet
7209 function mouseout() {
7210 $$.svg.select('.' + CLASS.eventRect).style('cursor', null);
7211 $$.hideXGridFocus();
7213 $$.unexpandCircles();
7217 // rects for mouseover
7218 $$.main.select('.' + CLASS.eventRects).style('cursor', config.zoom_enabled ? config.axis_rotated ? 'ns-resize' : 'ew-resize' : null);
7220 $$.eventRect.attr('x', x).attr('y', y).attr('width', w).attr('height', h).on('mouseout', config.interaction_enabled ? function () {
7223 } // chart is destroyed
7224 if ($$.hasArcType()) {
7228 } : null).on('mousemove', config.interaction_enabled ? function () {
7229 var targetsToShow, mouse, closest, sameXData, selectedData;
7233 } // do nothing when dragging
7234 if ($$.hasArcType(targetsToShow)) {
7238 targetsToShow = $$.filterTargetsToShow($$.data.targets);
7239 mouse = d3.mouse(this);
7240 closest = $$.findClosestFromTargets(targetsToShow, mouse);
7242 if ($$.mouseover && (!closest || closest.id !== $$.mouseover.id)) {
7243 config.data_onmouseout.call($$.api, $$.mouseover);
7244 $$.mouseover = undefined;
7252 if ($$.isScatterType(closest) || !config.tooltip_grouped) {
7253 sameXData = [closest];
7255 sameXData = $$.filterByX(targetsToShow, closest.x);
7258 // show tooltip when cursor is close to some point
7259 selectedData = sameXData.map(function (d) {
7260 return $$.addName(d);
7262 $$.showTooltip(selectedData, this);
7265 if (config.point_focus_expand_enabled) {
7266 $$.unexpandCircles();
7267 selectedData.forEach(function (d) {
7268 $$.expandCircles(d.index, d.id, false);
7271 $$.expandBars(closest.index, closest.id, true);
7273 // Show xgrid focus line
7274 $$.showXGridFocus(selectedData);
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;
7284 } : null).on('click', config.interaction_enabled ? function () {
7285 var targetsToShow, mouse, closest, sameXData;
7286 if ($$.hasArcType(targetsToShow)) {
7290 targetsToShow = $$.filterTargetsToShow($$.data.targets);
7291 mouse = d3.mouse(this);
7292 closest = $$.findClosestFromTargets(targetsToShow, mouse);
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];
7301 sameXData = $$.filterByX(targetsToShow, closest.x);
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);
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 () {
7318 }) : function () {});
7320 ChartInternal.prototype.getMousePosition = function (data) {
7322 return [$$.x(data.x), $$.getYScale(data.id)(data.value)];
7324 ChartInternal.prototype.dispatchEvent = function (type, mouse) {
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");
7333 event.initMouseEvent(type, true, true, window, 0, x, y, x, y, false, false, false, false, 0, null);
7334 eventRect.dispatchEvent(event);
7337 ChartInternal.prototype.initLegend = function () {
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);
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();
7351 ChartInternal.prototype.updateLegendWithDefaults = function () {
7353 $$.updateLegend($$.mapToIds($$.data.targets), { withTransform: false, withTransitionForTransform: false, withTransition: false });
7355 ChartInternal.prototype.updateSizeForLegend = function (legendHeight, legendWidth) {
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
7364 top: $$.isLegendRight ? 0 : $$.isLegendInset ? insetLegendPosition.top : $$.currentHeight - legendHeight,
7367 left: $$.isLegendRight ? $$.currentWidth - legendWidth : $$.isLegendInset ? insetLegendPosition.left : 0
7370 ChartInternal.prototype.transformLegend = function (withTransition) {
7372 (withTransition ? $$.legend.transition() : $$.legend).attr("transform", $$.getTranslate('legend'));
7374 ChartInternal.prototype.updateLegendStep = function (step) {
7375 this.legendStep = step;
7377 ChartInternal.prototype.updateLegendItemWidth = function (w) {
7378 this.legendItemWidth = w;
7380 ChartInternal.prototype.updateLegendItemHeight = function (h) {
7381 this.legendItemHeight = h;
7383 ChartInternal.prototype.getLegendWidth = function () {
7385 return $$.config.legend_show ? $$.isLegendRight || $$.isLegendInset ? $$.legendItemWidth * ($$.legendStep + 1) : $$.currentWidth : 0;
7387 ChartInternal.prototype.getLegendHeight = function () {
7390 if ($$.config.legend_show) {
7391 if ($$.isLegendRight) {
7392 h = $$.currentHeight;
7394 h = Math.max(20, $$.legendItemHeight) * ($$.legendStep + 1);
7399 ChartInternal.prototype.opacityForLegend = function (legendItem) {
7400 return legendItem.classed(CLASS.legendItemHidden) ? null : 1;
7402 ChartInternal.prototype.opacityForUnfocusedLegend = function (legendItem) {
7403 return legendItem.classed(CLASS.legendItemHidden) ? null : 0.3;
7405 ChartInternal.prototype.toggleFocusLegend = function (targetIds, focus) {
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));
7415 ChartInternal.prototype.revertLegend = function () {
7418 $$.legend.selectAll('.' + CLASS.legendItem).classed(CLASS.legendItemFocused, false).transition().duration(100).style('opacity', function () {
7419 return $$.opacityForLegend(d3.select(this));
7422 ChartInternal.prototype.showLegend = function (targetIds) {
7425 if (!config.legend_show) {
7426 config.legend_show = true;
7427 $$.legend.style('visibility', 'visible');
7428 if (!$$.legendHasRendered) {
7429 $$.updateLegendWithDefaults();
7432 $$.removeHiddenLegendIds(targetIds);
7433 $$.legend.selectAll($$.selectorLegends(targetIds)).style('visibility', 'visible').transition().style('opacity', function () {
7434 return $$.opacityForLegend($$.d3.select(this));
7437 ChartInternal.prototype.hideLegend = function (targetIds) {
7440 if (config.legend_show && isEmpty(targetIds)) {
7441 config.legend_show = false;
7442 $$.legend.style('visibility', 'hidden');
7444 $$.addHiddenLegendIds(targetIds);
7445 $$.legend.selectAll($$.selectorLegends(targetIds)).style('opacity', 0).style('visibility', 'hidden');
7447 ChartInternal.prototype.clearLegendItemTextBoxCache = function () {
7448 this.legendItemTextBox = {};
7450 ChartInternal.prototype.updateLegend = function (targetIds, options, transitions) {
7453 var xForLegend, xForLegendText, xForLegendRect, yForLegend, yForLegendText, yForLegendRect, x1ForLegendTile, x2ForLegendTile, yForLegendTile;
7459 tileWidth = config.legend_item_tile_width + 5;
7468 var withTransition, withTransitionForTransform;
7469 var texts, rects, tiles, background;
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;
7476 options = options || {};
7477 withTransition = getOption(options, "withTransition", true);
7478 withTransitionForTransform = getOption(options, "withTransitionForTransform", true);
7480 function getTextBox(textElement, id) {
7481 if (!$$.legendItemTextBox[id]) {
7482 $$.legendItemTextBox[id] = $$.getTextRect(textElement.textContent, CLASS.legendItem, textElement);
7484 return $$.legendItemTextBox[id];
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(),
7498 // MEMO: care about condifion of step, totalLength
7499 function updateValues(id, withoutStep) {
7501 margin = (areaLength - totalLength - itemLength) / 2;
7502 if (margin < posMin) {
7503 margin = (areaLength - itemLength) / 2;
7509 margins[step] = $$.isLegendInset ? 10 : margin;
7510 offsets[id] = totalLength;
7511 totalLength += itemLength;
7521 if (config.legend_show && !$$.isLegendToShow(id)) {
7522 widths[id] = heights[id] = steps[id] = offsets[id] = 0;
7526 widths[id] = itemWidth;
7527 heights[id] = itemHeight;
7529 if (!maxWidth || itemWidth >= maxWidth) {
7530 maxWidth = itemWidth;
7532 if (!maxHeight || itemHeight >= maxHeight) {
7533 maxHeight = itemHeight;
7535 maxLength = $$.isLegendRight || $$.isLegendInset ? maxHeight : maxWidth;
7537 if (config.legend_equally) {
7538 Object.keys(widths).forEach(function (id) {
7539 widths[id] = maxWidth;
7541 Object.keys(heights).forEach(function (id) {
7542 heights[id] = maxHeight;
7544 margin = (areaLength - maxLength * targetIds.length) / 2;
7545 if (margin < posMin) {
7548 targetIds.forEach(function (id) {
7552 updateValues(id, true);
7559 if ($$.isLegendInset) {
7560 step = config.legend_inset_step ? config.legend_inset_step : targetIds.length;
7561 $$.updateLegendStep(step);
7564 if ($$.isLegendRight) {
7565 xForLegend = function xForLegend(id) {
7566 return maxWidth * steps[id];
7568 yForLegend = function yForLegend(id) {
7569 return margins[steps[id]] + offsets[id];
7571 } else if ($$.isLegendInset) {
7572 xForLegend = function xForLegend(id) {
7573 return maxWidth * steps[id] + 10;
7575 yForLegend = function yForLegend(id) {
7576 return margins[steps[id]] + offsets[id];
7579 xForLegend = function xForLegend(id) {
7580 return margins[steps[id]] + offsets[id];
7582 yForLegend = function yForLegend(id) {
7583 return maxHeight * steps[id];
7586 xForLegendText = function xForLegendText(id, i) {
7587 return xForLegend(id, i) + 4 + config.legend_item_tile_width;
7589 yForLegendText = function yForLegendText(id, i) {
7590 return yForLegend(id, i) + 9;
7592 xForLegendRect = function xForLegendRect(id, i) {
7593 return xForLegend(id, i);
7595 yForLegendRect = function yForLegendRect(id, i) {
7596 return yForLegend(id, i) - 5;
7598 x1ForLegendTile = function x1ForLegendTile(id, i) {
7599 return xForLegend(id, i) - 2;
7601 x2ForLegendTile = function x2ForLegendTile(id, i) {
7602 return xForLegend(id, i) - 2 + config.legend_item_tile_width;
7604 yForLegendTile = function yForLegendTile(id, i) {
7605 return yForLegend(id, i) + 4;
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);
7617 if ($$.d3.event.altKey) {
7622 $$.isTargetToShow(id) ? $$.api.focus(id) : $$.api.revert();
7625 }).on('mouseover', function (id) {
7626 if (config.legend_item_onmouseover) {
7627 config.legend_item_onmouseover.call($$, id);
7629 $$.d3.select(this).classed(CLASS.legendItemFocused, true);
7630 if (!$$.transiting && $$.isTargetToShow(id)) {
7634 }).on('mouseout', function (id) {
7635 if (config.legend_item_onmouseout) {
7636 config.legend_item_onmouseout.call($$, id);
7638 $$.d3.select(this).classed(CLASS.legendItemFocused, false);
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);
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');
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);
7662 (withTransition ? texts.transition() : texts).attr('x', xForLegendText).attr('y', yForLegendText);
7664 rects = $$.legend.selectAll('rect.' + CLASS.legendItemEvent).data(targetIds);
7665 (withTransition ? rects.transition() : rects).attr('width', function (id) {
7667 }).attr('height', function (id) {
7669 }).attr('x', xForLegendRect).attr('y', yForLegendRect);
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);
7677 (withTransition ? background.transition() : background).attr('height', $$.getLegendHeight() - 12).attr('width', maxWidth * (step + 1) + 10);
7680 // toggle legend state
7681 $$.legend.selectAll('.' + CLASS.legendItem).classed(CLASS.legendItemHidden, function (id) {
7682 return !$$.isTargetToShow(id);
7685 // Update all to reflect change of legend
7686 $$.updateLegendItemWidth(maxWidth);
7687 $$.updateLegendItemHeight(maxHeight);
7688 $$.updateLegendStep(step);
7689 // Update size and scale
7693 // Update g positions
7694 $$.transformAll(withTransitionForTransform, transitions);
7695 $$.legendHasRendered = true;
7698 ChartInternal.prototype.initRegion = function () {
7700 $$.region = $$.main.append('g').attr("clip-path", $$.clipPath).attr("class", CLASS.regions);
7702 ChartInternal.prototype.updateRegion = function (duration) {
7707 $$.region.style('visibility', $$.hasArcType() ? 'hidden' : 'visible');
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();
7714 ChartInternal.prototype.redrawRegion = function (withTransition, transition) {
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;
7721 ChartInternal.prototype.regionX = function (d) {
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;
7729 xPos = config.axis_rotated ? 0 : 'start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0;
7733 ChartInternal.prototype.regionY = function (d) {
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;
7741 yPos = config.axis_rotated ? 'start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0 : 0;
7745 ChartInternal.prototype.regionWidth = function (d) {
7748 start = $$.regionX(d),
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;
7754 end = config.axis_rotated ? $$.width : 'end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.width;
7756 return end < start ? 0 : end - start;
7758 ChartInternal.prototype.regionHeight = function (d) {
7761 start = this.regionY(d),
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;
7767 end = config.axis_rotated ? 'end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.height : $$.height;
7769 return end < start ? 0 : end - start;
7771 ChartInternal.prototype.isRegionOnX = function (d) {
7772 return !d.axis || d.axis === 'x';
7775 ChartInternal.prototype.getScale = function (min, max, forTimeseries) {
7776 return (forTimeseries ? this.d3.scaleTime() : this.d3.scaleLinear()).range([min, max]);
7778 ChartInternal.prototype.getX = function (min, max, domain, offset) {
7780 scale = $$.getScale(min, max, $$.isTimeSeries()),
7781 _scale = domain ? scale.domain(domain) : scale,
7783 // Define customized scale if categorized axis
7784 if ($$.isCategorized()) {
7785 offset = offset || function () {
7788 scale = function scale(d, raw) {
7789 var v = _scale(d) + offset(d);
7790 return raw ? v : Math.ceil(v);
7793 scale = function scale(d, raw) {
7795 return raw ? v : Math.ceil(v);
7799 for (key in _scale) {
7800 scale[key] = _scale[key];
7802 scale.orgDomain = function () {
7803 return _scale.domain();
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];
7812 _scale.domain(domain);
7818 ChartInternal.prototype.getY = function (min, max, domain) {
7819 var scale = this.getScale(min, max, this.isTimeSeriesY());
7821 scale.domain(domain);
7825 ChartInternal.prototype.getYScale = function (id) {
7826 return this.axis.getId(id) === 'y2' ? this.y2 : this.y;
7828 ChartInternal.prototype.getSubYScale = function (id) {
7829 return this.axis.getId(id) === 'y2' ? this.subY2 : this.subY;
7831 ChartInternal.prototype.updateScales = function () {
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;
7845 $$.x = $$.getX($$.xMin, $$.xMax, forInit ? undefined : $$.x.orgDomain(), function () {
7846 return $$.xAxis.tickOffset();
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();
7853 $$.subY = $$.getY($$.subYMin, $$.subYMax, forInit ? config.axis_y_default : $$.subY.domain());
7854 $$.subY2 = $$.getY($$.subYMin, $$.subYMax, forInit ? config.axis_y2_default : $$.subY2.domain());
7856 $$.xAxisTickFormat = $$.axis.getXAxisTickFormat();
7857 $$.xAxisTickValues = $$.axis.getXAxisTickValues();
7858 $$.yAxisTickValues = $$.axis.getYAxisTickValues();
7859 $$.y2AxisTickValues = $$.axis.getY2AxisTickValues();
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);
7866 // Set initialized scales to brush and zoom
7869 $$.brush.updateScale($$.subX);
7878 ChartInternal.prototype.selectPoint = function (target, d, i) {
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 () {
7890 }).attr("r", function (d) {
7891 return $$.pointSelectR(d) * 1.4;
7892 }).transition().duration(100).attr("r", r);
7894 ChartInternal.prototype.unselectPoint = function (target, d, i) {
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();
7900 ChartInternal.prototype.togglePoint = function (selected, target, d, i) {
7901 selected ? this.selectPoint(target, d, i) : this.unselectPoint(target, d, i);
7903 ChartInternal.prototype.selectPath = function (target, d) {
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);
7912 ChartInternal.prototype.unselectPath = function (target, d) {
7914 $$.config.data_onunselected.call($$, d, target.node());
7915 if ($$.config.interaction_brighten) {
7916 target.transition().duration(100).style("fill", function () {
7921 ChartInternal.prototype.togglePath = function (selected, target, d, i) {
7922 selected ? this.selectPath(target, d, i) : this.unselectPath(target, d, i);
7924 ChartInternal.prototype.getToggle = function (that, d) {
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?
7932 toggle = $$.togglePoint;
7934 } else if (that.nodeName === 'path') {
7935 toggle = $$.togglePath;
7939 ChartInternal.prototype.toggleShape = function (that, d, i) {
7943 shape = d3.select(that),
7944 isSelected = shape.classed(CLASS.SELECTED),
7945 toggle = $$.getToggle(that, d).bind($$);
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);
7956 shape.classed(CLASS.SELECTED, !isSelected);
7957 toggle(!isSelected, shape, d, i);
7961 ChartInternal.prototype.initBar = function () {
7963 $$.main.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartBars);
7965 ChartInternal.prototype.updateTargetsForBar = function (targets) {
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);
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;
7982 ChartInternal.prototype.updateBar = function (durationForExit) {
7984 barData = $$.barData.bind($$),
7985 classBar = $$.classBar.bind($$),
7986 initialOpacity = $$.initialOpacity.bind($$),
7987 color = function color(d) {
7988 return $$.color(d.id);
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);
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)];
7998 ChartInternal.prototype.getBarW = function (axis, barTargetsNum) {
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;
8004 ChartInternal.prototype.getBars = function (i, id) {
8006 return (id ? $$.main.selectAll('.' + CLASS.bars + $$.getTargetSelectorSuffix(id)) : $$.main).selectAll('.' + CLASS.bar + (isValue(i) ? '-' + i : ''));
8008 ChartInternal.prototype.expandBars = function (i, id, reset) {
8013 $$.getBars(i, id).classed(CLASS.EXPANDED, true);
8015 ChartInternal.prototype.unexpandBars = function (i) {
8017 $$.getBars(i).classed(CLASS.EXPANDED, false);
8019 ChartInternal.prototype.generateDrawBar = function (barIndices, isSub) {
8022 getPoints = $$.generateGetBarPoints(barIndices, isSub);
8023 return function (d, i) {
8024 // 4 points that make a bar
8025 var points = getPoints(d, i);
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;
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';
8036 ChartInternal.prototype.generateGetBarPoints = function (barIndices, isSub) {
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
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) {
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]];
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),
8072 ex = x + w + offset,
8073 sy = y + h + offset,
8075 return sx < mouse[0] && mouse[0] < ex && ey < mouse[1] && mouse[1] < sy;
8078 ChartInternal.prototype.getShapeIndices = function (typeFilter) {
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) {
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]];
8097 if (isUndefined(indices[d.id])) {
8098 indices[d.id] = i++;
8101 indices.__max__ = i - 1;
8104 ChartInternal.prototype.getShapeX = function (offset, targetsNum, indices, isSub) {
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;
8112 ChartInternal.prototype.getShapeY = function (isSub) {
8114 return function (d) {
8115 var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id);
8116 return scale(d.value);
8119 ChartInternal.prototype.getShapeOffset = function (typeFilter, indices, isSub) {
8121 targets = $$.orderTargets($$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$))),
8122 targetIds = targets.map(function (t) {
8125 return function (d, i) {
8126 var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id),
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]) {
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
8140 values.forEach(function (v, j) {
8146 if (i in values && values[i].value * d.value >= 0) {
8147 offset += scale(values[i].value) - y0;
8154 ChartInternal.prototype.isWithinShape = function (that, d) {
8156 shape = $$.d3.select(that),
8158 if (!$$.isTargetToShow(d.id)) {
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;
8168 ChartInternal.prototype.getInterpolate = function (d) {
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
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];
8193 type = types.linear;
8198 ChartInternal.prototype.initLine = function () {
8200 $$.main.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartLines);
8202 ChartInternal.prototype.updateTargetsForLine = function (targets) {
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);
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);
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);
8224 mainLineEnter.append('g').attr("class", classCircles).style("cursor", function (d) {
8225 return config.data_selection_isselectable(d) ? "pointer" : null;
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;
8233 // MEMO: can not keep same color...
8234 //mainLineUpdate.exit().remove();
8236 ChartInternal.prototype.updateLine = function (durationForExit) {
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);
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)];
8248 ChartInternal.prototype.generateDrawLine = function (lineIndices, isSub) {
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);
8257 yValue = function yValue(d, i) {
8258 return config.data_groups.length > 0 ? getPoints(d, i)[0][1] : yScaleGetter.call($$, d.id)(d.value);
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;
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),
8274 if ($$.isLineType(d)) {
8275 if (config.data_regions[d.id]) {
8276 path = $$.lineWithRegions(values, x, y, config.data_regions[d.id]);
8278 if ($$.isStepType(d)) {
8279 values = $$.convertValuesToStep(values);
8281 path = line.curve($$.getInterpolate(d))(values);
8285 x0 = x(values[0].x);
8286 y0 = y(values[0].value);
8288 path = config.axis_rotated ? "M " + y0 + " " + x0 : "M " + x0 + " " + y0;
8290 return path ? path : "M 0 0";
8293 ChartInternal.prototype.generateGetLinePoints = function (lineIndices, isSub) {
8294 // partial duplication of generateGetBarPoints
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
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) {
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
8322 ChartInternal.prototype.lineWithRegions = function (d, x, y, _regions) {
8337 xOffset = $$.isCategorized() ? 0.5 : 0,
8342 function isWithinRegions(x, regions) {
8344 for (i = 0; i < regions.length; i++) {
8345 if (regions[i].start < x && x <= regions[i].end) {
8352 // Check start/end of regions
8353 if (isDefined(_regions)) {
8354 for (i = 0; i < _regions.length; i++) {
8356 if (isUndefined(_regions[i].start)) {
8357 regions[i].start = d[0].x;
8359 regions[i].start = $$.isTimeSeries() ? $$.parseDate(_regions[i].start) : _regions[i].start;
8361 if (isUndefined(_regions[i].end)) {
8362 regions[i].end = d[d.length - 1].x;
8364 regions[i].end = $$.isTimeSeries() ? $$.parseDate(_regions[i].end) : _regions[i].end;
8370 xValue = config.axis_rotated ? function (d) {
8375 yValue = config.axis_rotated ? function (d) {
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];
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)),
8392 if (config.axis_rotated) {
8393 points = [[y(yp(j)), x(xv0)], [y(yp(j + diff)), x(xv1)]];
8395 points = [[x(xv0), y(yp(j))], [x(xv1), y(yp(j + diff))]];
8397 return generateM(points);
8400 sWithRegion = function sWithRegion(d0, d1, j, diff) {
8402 if (config.axis_rotated) {
8403 points = [[y(yp(j), true), x(xp(j))], [y(yp(j + diff), true), x(xp(j + diff))]];
8405 points = [[x(xp(j), true), y(yp(j))], [x(xp(j + diff), true), y(yp(j + diff))]];
8407 return generateM(points);
8412 for (i = 0; i < d.length; i++) {
8415 if (isUndefined(regions) || !isWithinRegions(d[i].x, regions)) {
8416 s += " " + xValue(d[i]) + " " + yValue(d[i]);
8418 // Draw with region // TODO: Fix for horizotal charts
8420 xp = $$.getScale(d[i - 1].x + xOffset, d[i].x + xOffset, $$.isTimeSeries());
8421 yp = $$.getScale(d[i - 1].value, d[i].value);
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));
8429 for (j = diff; j <= 1; j += diffx2) {
8430 s += sWithRegion(d[i - 1], d[i], j, diff);
8439 ChartInternal.prototype.updateArea = function (durationForExit) {
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;
8446 $$.mainArea = mainAreaEnter.merge(mainArea).style("opacity", $$.orgAreaOpacity);
8447 mainArea.exit().transition().duration(durationForExit).style('opacity', 0);
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)];
8452 ChartInternal.prototype.generateDrawArea = function (areaIndices, isSub) {
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);
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));
8464 value1 = function value1(d, i) {
8465 return config.data_groups.length > 0 ? getPoints(d, i)[1][1] : yScaleGetter.call($$, d.id)(d.value);
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;
8475 return function (d) {
8476 var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values,
8480 if ($$.isAreaType(d)) {
8481 if ($$.isStepType(d)) {
8482 values = $$.convertValuesToStep(values);
8484 path = area.curve($$.getInterpolate(d))(values);
8487 x0 = $$.x(values[0].x);
8488 y0 = $$.getYScale(d.id)(values[0].value);
8490 path = config.axis_rotated ? "M " + y0 + " " + x0 : "M " + x0 + " " + y0;
8492 return path ? path : "M 0 0";
8495 ChartInternal.prototype.getAreaBaseValue = function () {
8498 ChartInternal.prototype.generateGetAreaPoints = function (areaIndices, isSub) {
8499 // partial duplication of generateGetBarPoints
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
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) {
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
8526 ChartInternal.prototype.updateCircle = function (cx, cy) {
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);
8533 ChartInternal.prototype.redrawCircle = function (cx, cy, withTransition, transition) {
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)];
8538 ChartInternal.prototype.circleX = function (d) {
8539 return d.x || d.x === 0 ? this.x(d.x) : null;
8541 ChartInternal.prototype.updateCircleY = function () {
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];
8551 $$.circleY = function (d) {
8552 return $$.getYScale(d.id)(d.value);
8556 ChartInternal.prototype.getCircles = function (i, id) {
8558 return (id ? $$.main.selectAll('.' + CLASS.circles + $$.getTargetSelectorSuffix(id)) : $$.main).selectAll('.' + CLASS.circle + (isValue(i) ? '-' + i : ''));
8560 ChartInternal.prototype.expandCircles = function (i, id, reset) {
8562 r = $$.pointExpandedR.bind($$);
8564 $$.unexpandCircles();
8566 $$.getCircles(i, id).classed(CLASS.EXPANDED, true).attr('r', r);
8568 ChartInternal.prototype.unexpandCircles = function (i) {
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);
8575 ChartInternal.prototype.pointR = function (d) {
8578 return $$.isStepType(d) ? 0 : isFunction(config.point_r) ? config.point_r(d) : config.point_r;
8580 ChartInternal.prototype.pointExpandedR = function (d) {
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;
8586 return $$.pointR(d);
8589 ChartInternal.prototype.pointSelectR = function (d) {
8592 return isFunction(config.point_select_r) ? config.point_select_r(d) : config.point_select_r ? config.point_select_r : $$.pointR(d) * 4;
8594 ChartInternal.prototype.isWithinCircle = function (that, r) {
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;
8602 ChartInternal.prototype.isWithinStep = function (that, y) {
8603 return Math.abs(y - this.d3.mouse(that)[1]) < 30;
8606 ChartInternal.prototype.getCurrentWidth = function () {
8609 return config.size_width ? config.size_width : $$.getParentWidth();
8611 ChartInternal.prototype.getCurrentHeight = function () {
8614 h = config.size_height ? config.size_height : $$.getParentHeight();
8615 return h > 0 ? h : 320 / ($$.hasType('gauge') && !config.gauge_fullCircle ? 2 : 1);
8617 ChartInternal.prototype.getCurrentPaddingTop = function () {
8620 padding = isValue(config.padding_top) ? config.padding_top : 0;
8621 if ($$.title && $$.title.node()) {
8622 padding += $$.getTitlePadding();
8626 ChartInternal.prototype.getCurrentPaddingBottom = function () {
8627 var config = this.config;
8628 return isValue(config.padding_bottom) ? config.padding_bottom : 0;
8630 ChartInternal.prototype.getCurrentPaddingLeft = function (withoutRecompute) {
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;
8641 return ceil10($$.getAxisWidthByAxisId('y', withoutRecompute));
8644 ChartInternal.prototype.getCurrentPaddingRight = function () {
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);
8657 return ceil10($$.getAxisWidthByAxisId('y2')) + legendWidthOnRight;
8661 ChartInternal.prototype.getParentRectValue = function (key) {
8662 var parent = this.selectChart.node(),
8664 while (parent && parent.tagName !== 'BODY') {
8666 v = parent.getBoundingClientRect()[key];
8668 if (key === 'width') {
8669 // In IE in certain cases getBoundingClientRect
8670 // will cause an "unspecified error"
8671 v = parent.offsetWidth;
8677 parent = parent.parentNode;
8681 ChartInternal.prototype.getParentWidth = function () {
8682 return this.getParentRectValue('width');
8684 ChartInternal.prototype.getParentHeight = function () {
8685 var h = this.selectChart.style('height');
8686 return h.indexOf('px') > 0 ? +h.replace('px', '') : 0;
8689 ChartInternal.prototype.getSvgLeft = function (withoutRecompute) {
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;
8702 ChartInternal.prototype.getAxisWidthByAxisId = function (id, withoutRecompute) {
8704 position = $$.axis.getLabelPositionById(id);
8705 return $$.axis.getMaxTickWidth(id, withoutRecompute) + (position.isInner ? 20 : 40);
8707 ChartInternal.prototype.getHorizontalAxisHeight = function (axisId) {
8711 if (axisId === 'x' && !config.axis_x_show) {
8714 if (axisId === 'x' && config.axis_x_height) {
8715 return config.axis_x_height;
8717 if (axisId === 'y' && !config.axis_y_show) {
8718 return config.legend_show && !$$.isLegendRight && !$$.isLegendInset ? 10 : 1;
8720 if (axisId === 'y2' && !config.axis_y2_show) {
8721 return $$.rotated_padding_top;
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);
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);
8731 return h + ($$.axis.getLabelPositionById(axisId).isInner ? 0 : 10) + (axisId === 'y2' ? -10 : 0);
8734 ChartInternal.prototype.initBrush = function (scale) {
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") {
8743 $$.redrawForBrush();
8744 }).on("end", function () {
8745 var event = d3.event.sourceEvent;
8746 if (event && event.type === "zoom") {
8749 if ($$.brush.empty() && event && event.type !== 'end') {
8753 $$.brush.updateExtent = function () {
8754 var range = this.scale.range(),
8756 if ($$.config.axis_rotated) {
8757 extent = [[0, range[0]], [$$.width2, range[1]]];
8759 extent = [[range[0], 0], [range[1], $$.height2]];
8761 this.extent(extent);
8764 $$.brush.updateScale = function (scale) {
8768 $$.brush.update = function (scale) {
8769 this.updateScale(scale || $$.subX).updateExtent();
8770 $$.context.select('.' + CLASS.brush).call(this);
8772 $$.brush.clear = function () {
8773 $$.context.select('.' + CLASS.brush).call($$.brush.move, null);
8775 $$.brush.selection = function () {
8776 return d3.brushSelection($$.context.select('.' + CLASS.brush).node());
8778 $$.brush.selectionAsValue = function (selectionAsValue, withTransition) {
8779 var selection, brush;
8780 if (selectionAsValue) {
8782 selection = [this.scale(selectionAsValue[0]), this.scale(selectionAsValue[1])];
8783 brush = $$.context.select('.' + CLASS.brush);
8784 if (withTransition) {
8785 brush = brush.transition();
8787 $$.brush.move(brush, selection);
8791 selection = $$.brush.selection() || [0, 0];
8792 return [this.scale.invert(selection[0]), this.scale.invert(selection[1])];
8794 $$.brush.empty = function () {
8795 var selection = $$.brush.selection();
8796 return !selection || selection[0] === selection[1];
8798 return $$.brush.updateScale(scale);
8800 ChartInternal.prototype.initSubchart = function () {
8803 context = $$.context = $$.svg.append("g").attr("transform", $$.getTranslate('context')),
8804 visibility = config.subchart_show ? 'visible' : 'hidden';
8807 context.style('visibility', visibility);
8809 // Define g for chart area
8810 context.append('g').attr("clip-path", $$.clipPathForSubchart).attr('class', CLASS.chart);
8812 // Define g for bar chart area
8813 context.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartBars);
8815 // Define g for line chart area
8816 context.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartLines);
8818 // Add extent rect for Brush
8819 context.append("g").attr("clip-path", $$.clipPath).attr("class", CLASS.brush);
8821 // ATTENTION: This must be called AFTER chart added
8823 $$.axes.subx = context.append("g").attr("class", CLASS.axisX).attr("transform", $$.getTranslate('subx')).attr("clip-path", config.axis_rotated ? "" : $$.clipPathForXAxis);
8825 ChartInternal.prototype.initSubchartBrush = function () {
8827 // Add extent rect for Brush
8828 $$.initBrush($$.subX).updateExtent();
8829 $$.context.select('.' + CLASS.brush).call($$.brush);
8831 ChartInternal.prototype.updateTargetsForSubchart = function (targets) {
8833 context = $$.context,
8839 classChartBar = $$.classChartBar.bind($$),
8840 classBars = $$.classBars.bind($$),
8841 classChartLine = $$.classChartLine.bind($$),
8842 classLines = $$.classLines.bind($$),
8843 classAreas = $$.classAreas.bind($$);
8845 if (config.subchart_show) {
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);
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);
8860 contextLineEnter.append("g").attr("class", classAreas);
8863 context.selectAll('.' + CLASS.brush + ' rect').attr(config.axis_rotated ? "width" : "height", config.axis_rotated ? $$.width2 : $$.height2);
8866 ChartInternal.prototype.updateBarForSubchart = function (durationForExit) {
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($$));
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);
8876 ChartInternal.prototype.updateLineForSubchart = function (durationForExit) {
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($$));
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);
8886 ChartInternal.prototype.updateAreaForSubchart = function (durationForExit) {
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;
8893 contextArea.exit().transition().duration(durationForExit).style('opacity', 0).remove();
8894 $$.contextArea = contextAreaEnter.merge(contextArea).style("opacity", 0);
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);
8899 ChartInternal.prototype.redrawSubchart = function (withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices) {
8907 $$.context.style('visibility', config.subchart_show ? 'visible' : 'hidden');
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());
8915 // update subchart elements if needed
8918 if (!$$.brush.empty()) {
8919 $$.brush.selectionAsValue($$.x.orgDomain());
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);
8926 $$.updateBarForSubchart(duration);
8927 $$.updateLineForSubchart(duration);
8928 $$.updateAreaForSubchart(duration);
8930 $$.redrawBarForSubchart(drawBarOnSub, duration, duration);
8931 $$.redrawLineForSubchart(drawLineOnSub, duration, duration);
8932 $$.redrawAreaForSubchart(drawAreaOnSub, duration, duration);
8936 ChartInternal.prototype.redrawForBrush = function () {
8942 withTransition: false,
8943 withY: $$.config.zoom_rescale,
8944 withSubchart: false,
8945 withUpdateXDomain: true,
8946 withEventRect: false,
8947 withDimension: false
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());
8954 ChartInternal.prototype.transformContext = function (withTransition, transitions) {
8957 if (transitions && transitions.axisSubX) {
8958 subXAxis = transitions.axisSubX;
8960 subXAxis = $$.context.select('.' + CLASS.axisX);
8961 if (withTransition) {
8962 subXAxis = subXAxis.transition();
8965 $$.context.attr("transform", $$.getTranslate('context'));
8966 subXAxis.attr("transform", $$.getTranslate('subx'));
8968 ChartInternal.prototype.getDefaultSelection = function () {
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])];
8978 ChartInternal.prototype.initText = function () {
8980 $$.main.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartTexts);
8981 $$.mainText = $$.d3.selectAll([]);
8983 ChartInternal.prototype.updateTargetsForText = function (targets) {
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);
8995 ChartInternal.prototype.updateText = function (xForText, yForText, durationForExit) {
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) {
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);
9009 mainText.exit().transition().duration(durationForExit).style('fill-opacity', 0).remove();
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))];
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'),
9019 svg.selectAll('.dummy').data([text]).enter().append('text').classed(cls ? cls : "", true).style('font', font).text(text).each(function () {
9020 rect = this.getBoundingClientRect();
9025 ChartInternal.prototype.generateXYForText = function (areaIndices, barIndices, lineIndices, forX) {
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);
9036 ChartInternal.prototype.getXForText = function (points, d, textElement) {
9038 box = textElement.getBoundingClientRect(),
9041 if ($$.config.axis_rotated) {
9042 padding = $$.isBarType(d) ? 4 : 6;
9043 xPos = points[2][1] + padding * (d.value < 0 ? -1 : 1);
9045 xPos = $$.hasType('bar') ? (points[2][0] + points[0][0]) / 2 : points[0][0];
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) {
9057 ChartInternal.prototype.getYForText = function (points, d, textElement) {
9059 box = textElement.getBoundingClientRect(),
9061 if ($$.config.axis_rotated) {
9062 yPos = (points[0][0] + points[2][0] + box.height * 0.6) / 2;
9064 yPos = points[2][1];
9065 if (d.value < 0 || d.value === 0 && !$$.hasPositiveValue) {
9067 if ($$.isBarType(d) && $$.isSafari()) {
9069 } else if (!$$.isBarType(d) && $$.isChrome()) {
9073 yPos += $$.isBarType(d) ? -3 : -6;
9076 // show labels regardless of the domain if value is null
9077 if (d.value === null && !$$.config.axis_rotated) {
9078 if (yPos < box.height) {
9080 } else if (yPos > this.height) {
9081 yPos = this.height - 4;
9087 ChartInternal.prototype.initTitle = function () {
9089 $$.title = $$.svg.append("text").text($$.config.title_text).attr("class", $$.CLASS.title);
9091 ChartInternal.prototype.redrawTitle = function () {
9093 $$.title.attr("x", $$.xForTitle.bind($$)).attr("y", $$.yForTitle.bind($$));
9095 ChartInternal.prototype.xForTitle = function () {
9098 position = config.title_position || 'left',
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;
9106 x = config.title_padding.left;
9110 ChartInternal.prototype.yForTitle = function () {
9112 return $$.config.title_padding.top + $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()).height;
9114 ChartInternal.prototype.getTitlePadding = function () {
9116 return $$.yForTitle() + $$.config.title_padding.bottom;
9119 ChartInternal.prototype.initTooltip = function () {
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) {
9133 config.tooltip_init_x = i;
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");
9141 ChartInternal.prototype.getTooltipSortFunction = function () {
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
9149 var order = config.tooltip_order;
9150 if (order === undefined) {
9151 order = config.data_order;
9154 var valueOf = function valueOf(obj) {
9155 return obj ? obj.value : null;
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);
9163 } else if (isString(order) && order.toLowerCase() === 'desc') {
9164 return function (a, b) {
9165 return valueOf(b) - valueOf(a);
9167 } else if (isFunction(order)) {
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
9173 var sortFunction = order;
9175 if (config.tooltip_order === undefined) {
9176 sortFunction = function sortFunction(a, b) {
9187 return sortFunction;
9188 } else if (isArray(order)) {
9189 return function (a, b) {
9190 return order.indexOf(a.id) - order.indexOf(b.id);
9194 // if data are grouped, we follow the order of grouped targets
9195 var ids = $$.orderTargets($$.data.targets).map(function (i) {
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();
9205 return function (a, b) {
9206 return ids.indexOf(a.id) - ids.indexOf(b.id);
9210 ChartInternal.prototype.getTooltipContent = function (d, defaultTitleFormat, defaultValueFormat, color) {
9213 titleFormat = config.tooltip_format_title || defaultTitleFormat,
9214 nameFormat = config.tooltip_format_name || function (name) {
9217 valueFormat = config.tooltip_format_value || defaultValueFormat,
9225 var tooltipSortFunction = this.getTooltipSortFunction();
9226 if (tooltipSortFunction) {
9227 d.sort(tooltipSortFunction);
9230 for (i = 0; i < d.length; i++) {
9231 if (!(d[i] && (d[i].value || d[i].value === 0))) {
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>" : "");
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) {
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);
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>";
9255 return text + "</table>";
9257 ChartInternal.prototype.tooltipPosition = function (dataToShow, tWidth, tHeight, element) {
9261 var svgLeft, tooltipLeft, tooltipRight, tooltipTop, chartRight;
9262 var forArc = $$.hasArcType(),
9263 mouse = d3.mouse(element);
9264 // Determin tooltip position
9266 tooltipLeft = ($$.width - ($$.isLegendRight ? $$.getLegendWidth() : 0)) / 2 + mouse[0];
9267 tooltipTop = ($$.hasType('gauge') ? $$.height : $$.height / 2) + mouse[1] + 20;
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;
9276 tooltipLeft = svgLeft + $$.getCurrentPaddingLeft(true) + $$.x(dataToShow[0].x) + 20;
9277 tooltipRight = tooltipLeft + tWidth;
9278 chartRight = svgLeft + $$.currentWidth - $$.getCurrentPaddingRight();
9279 tooltipTop = mouse[1] + 15;
9282 if (tooltipRight > chartRight) {
9283 // 20 is needed for Firefox to keep tooltip width
9284 tooltipLeft -= tooltipRight - chartRight + 20;
9286 if (tooltipTop + tHeight > $$.currentHeight) {
9287 tooltipTop -= tHeight + 30;
9290 if (tooltipTop < 0) {
9298 ChartInternal.prototype.showTooltip = function (selectedData, element) {
9301 var tWidth, tHeight, position;
9302 var forArc = $$.hasArcType(),
9303 dataToShow = selectedData.filter(function (d) {
9304 return d && isValue(d.value);
9306 positionFunction = config.tooltip_position || ChartInternal.prototype.tooltipPosition;
9307 if (dataToShow.length === 0 || !config.tooltip_show) {
9310 $$.tooltip.html(config.tooltip_contents.call($$, selectedData, $$.axis.getXAxisTickFormat(), $$.getYFormat(forArc), $$.color)).style("display", "block");
9312 // Get tooltip dimensions
9313 tWidth = $$.tooltip.property('offsetWidth');
9314 tHeight = $$.tooltip.property('offsetHeight');
9316 position = positionFunction.call(this, dataToShow, tWidth, tHeight, element);
9318 $$.tooltip.style("top", position.top + "px").style("left", position.left + 'px');
9320 ChartInternal.prototype.hideTooltip = function () {
9321 this.tooltip.style("display", "none");
9324 ChartInternal.prototype.setTargetType = function (targetIds, type) {
9327 $$.mapToTargetIds(targetIds).forEach(function (id) {
9328 $$.withoutFadeIn[id] = type === config.data_types[id];
9329 config.data_types[id] = type;
9332 config.data_type = type;
9335 ChartInternal.prototype.hasType = function (type, targets) {
9337 types = $$.config.data_types,
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') {
9347 } else if (Object.keys(types).length) {
9348 Object.keys(types).forEach(function (id) {
9349 if (types[id] === type) {
9354 has = $$.config.data_type === type;
9358 ChartInternal.prototype.hasArcType = function (targets) {
9359 return this.hasType('pie', targets) || this.hasType('donut', targets) || this.hasType('gauge', targets);
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;
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;
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;
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;
9378 ChartInternal.prototype.isBarType = function (d) {
9379 var id = isString(d) ? d : d.id;
9380 return this.config.data_types[id] === 'bar';
9382 ChartInternal.prototype.isScatterType = function (d) {
9383 var id = isString(d) ? d : d.id;
9384 return this.config.data_types[id] === 'scatter';
9386 ChartInternal.prototype.isPieType = function (d) {
9387 var id = isString(d) ? d : d.id;
9388 return this.config.data_types[id] === 'pie';
9390 ChartInternal.prototype.isGaugeType = function (d) {
9391 var id = isString(d) ? d : d.id;
9392 return this.config.data_types[id] === 'gauge';
9394 ChartInternal.prototype.isDonutType = function (d) {
9395 var id = isString(d) ? d : d.id;
9396 return this.config.data_types[id] === 'donut';
9398 ChartInternal.prototype.isArcType = function (d) {
9399 return this.isPieType(d) || this.isDonutType(d) || this.isGaugeType(d);
9401 ChartInternal.prototype.lineData = function (d) {
9402 return this.isLineType(d) ? [d] : [];
9404 ChartInternal.prototype.arcData = function (d) {
9405 return this.isArcType(d.data) ? [d] : [];
9408 function scatterData(d) {
9409 return isScatterType(d) ? d.values : [];
9412 ChartInternal.prototype.barData = function (d) {
9413 return this.isBarType(d) ? d.values : [];
9415 ChartInternal.prototype.lineOrScatterData = function (d) {
9416 return this.isLineType(d) || this.isScatterType(d) ? d.values : [];
9418 ChartInternal.prototype.barOrLineData = function (d) {
9419 return this.isBarType(d) || this.isLineType(d) ? d.values : [];
9422 ChartInternal.prototype.isSafari = function () {
9423 var ua = window.navigator.userAgent;
9424 return ua.indexOf('Safari') >= 0 && ua.indexOf('Chrome') < 0;
9426 ChartInternal.prototype.isChrome = function () {
9427 var ua = window.navigator.userAgent;
9428 return ua.indexOf('Chrome') >= 0;
9431 ChartInternal.prototype.initZoom = function () {
9437 $$.zoom = d3.zoom().on("start", function () {
9438 if (config.zoom_type !== 'scroll') {
9442 var e = d3.event.sourceEvent;
9443 if (e && e.type === "brush") {
9447 config.zoom_onzoomstart.call($$.api, e);
9448 }).on("zoom", function () {
9449 if (config.zoom_type !== 'scroll') {
9453 var e = d3.event.sourceEvent;
9454 if (e && e.type === "brush") {
9460 config.zoom_onzoom.call($$.api, $$.x.orgDomain());
9461 }).on('end', function () {
9462 if (config.zoom_type !== 'scroll') {
9466 var e = d3.event.sourceEvent;
9467 if (e && e.type === "brush") {
9470 // if click, do nothing. otherwise, click interaction will be canceled.
9471 if (e && startEvent.clientX === e.clientX && startEvent.clientY === e.clientY) {
9474 config.zoom_onzoomend.call($$.api, $$.x.orgDomain());
9477 $$.zoom.updateDomain = function () {
9478 if (d3.event && d3.event.transform) {
9479 $$.x.domain(d3.event.transform.rescaleX($$.subX).domain());
9483 $$.zoom.updateExtent = function () {
9484 this.scaleExtent([1, Infinity]).translateExtent([[0, 0], [$$.width, $$.height]]).extent([[0, 0], [$$.width, $$.height]]);
9487 $$.zoom.update = function () {
9488 return this.updateExtent().updateDomain();
9491 return $$.zoom.updateExtent();
9493 ChartInternal.prototype.zoomTransform = function (range) {
9495 s = [$$.x(range[0]), $$.x(range[1])];
9496 return $$.d3.zoomIdentity.scale($$.width / (s[1] - s[0])).translate(-s[0], 0);
9499 ChartInternal.prototype.initDragZoom = function () {
9502 var config = $$.config;
9503 var context = $$.context = $$.svg;
9504 var brushXPos = $$.margin.left + 20.5;
9505 var brushYPos = $$.margin.top + 0.5;
9507 if (!(config.zoom_type === 'drag' && config.zoom_enabled)) {
9511 var getZoomedDomain = function getZoomedDomain(selection) {
9512 return selection && selection.map(function (x) {
9513 return $$.x.invert(x);
9517 var brush = $$.dragZoomBrush = d3.brushX().on("start", function () {
9520 $$.svg.select("." + CLASS.dragZoom).classed("disabled", false);
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) {
9530 var zoomedDomain = getZoomedDomain(d3.event.selection);
9532 if (!config.zoom_disableDefaultBehavior) {
9533 $$.api.zoom(zoomedDomain);
9536 $$.svg.select("." + CLASS.dragZoom).classed("disabled", true);
9538 config.zoom_onzoomend.call($$.api, zoomedDomain);
9541 context.append("g").classed(CLASS.dragZoom, true).attr("clip-path", $$.clipPath).attr("transform", "translate(" + brushXPos + "," + brushYPos + ")").call(brush);
9544 ChartInternal.prototype.getZoomDomain = function () {
9548 min = d3.min([$$.orgXDomain[0], config.zoom_x_min]),
9549 max = d3.max([$$.orgXDomain[1], config.zoom_x_max]);
9552 ChartInternal.prototype.redrawForZoom = function () {
9558 if (!config.zoom_enabled) {
9561 if ($$.filterTargetsToShow($$.data.targets).length === 0) {
9567 if (config.zoom_disableDefaultBehavior) {
9571 if ($$.isCategorized() && x.orgDomain()[0] === $$.orgXDomain[0]) {
9572 x.domain([$$.orgXDomain[0] - 1e-10, x.orgDomain()[1]]);
9576 withTransition: false,
9577 withY: config.zoom_rescale,
9578 withSubchart: false,
9579 withEventRect: false,
9580 withDimension: false
9583 if (d3.event.sourceEvent && d3.event.sourceEvent.type === 'mousemove') {
9584 $$.cancelClick = true;