text: c3_chart_internal_fn.initPie = function () {
      var $$ = this, d3 = $$.d3, config = $$.config;
      $$.pie = d3.layout.pie().value(function (d) {
          return d.values.reduce(function (a, b) { return a + b.value; }, 0);
      });
      if (!config.data_order || !config.pie_sort || !config.donut_sort) {
          $$.pie.sort(null);
      }
  };
  
  c3_chart_internal_fn.updateRadius = function () {
      var $$ = this, config = $$.config,
          w = config.gauge_width || config.donut_width;
      $$.radiusExpanded = Math.min($$.arcWidth, $$.arcHeight) / 2;
      $$.radius = $$.radiusExpanded * 0.95;
      $$.innerRadiusRatio = w ? ($$.radius - w) / $$.radius : 0.6;
      $$.innerRadius = $$.hasType('donut') || $$.hasType('gauge') ? $$.radius * $$.innerRadiusRatio : 0;
  };
  
  c3_chart_internal_fn.updateArc = function () {
      var $$ = this;
      $$.svgArc = $$.getSvgArc();
      $$.svgArcExpanded = $$.getSvgArcExpanded();
      $$.svgArcExpandedSub = $$.getSvgArcExpanded(0.98);
  };
  
  c3_chart_internal_fn.updateAngle = function (d) {
      var $$ = this, config = $$.config,
          found = false, index = 0;
      $$.pie($$.filterTargetsToShow($$.data.targets)).sort($$.descByStartAngle).forEach(function (t) {
          if (! found && t.data.id === d.data.id) {
              found = true;
              d = t;
              d.index = index;
          }
          index++;
      });
      if (isNaN(d.endAngle)) {
          d.endAngle = d.startAngle;
      }
      if ($$.isGaugeType(d.data)) {
          var gMin = config.gauge_min, gMax = config.gauge_max,
              gF = Math.abs(gMin) + gMax,
              aTic = (Math.PI) / gF;
          d.startAngle = (-1 * (Math.PI / 2)) + (aTic * Math.abs(gMin));
          d.endAngle = d.startAngle + (aTic * ((d.value > gMax) ? gMax : d.value));
      }
      return found ? d : null;
  };
  
  c3_chart_internal_fn.getSvgArc = function () {
      var $$ = this,
          arc = $$.d3.svg.arc().outerRadius($$.radius).innerRadius($$.innerRadius),
          newArc = function (d, withoutUpdate) {
              var updated;
              if (withoutUpdate) { return arc(d); } // for interpolate
              updated = $$.updateAngle(d);
              return updated ? arc(updated) : "M 0 0";
          };
      // TODO: extends all function
      newArc.centroid = arc.centroid;
      return newArc;
  };
  
  c3_chart_internal_fn.getSvgArcExpanded = function (rate) {
      var $$ = this,
          arc = $$.d3.svg.arc().outerRadius($$.radiusExpanded * (rate ? rate : 1)).innerRadius($$.innerRadius);
      return function (d) {
          var updated = $$.updateAngle(d);
          return updated ? arc(updated) : "M 0 0";
      };
  };
  
  c3_chart_internal_fn.getArc = function (d, withoutUpdate, force) {
      return force || this.isArcType(d.data) ? this.svgArc(d, withoutUpdate) : "M 0 0";
  };
  
  
  c3_chart_internal_fn.transformForArcLabel = function (d) {
      var $$ = this,
          updated = $$.updateAngle(d), c, x, y, h, ratio, translate = "";
      if (updated && !$$.hasType('gauge')) {
          c = this.svgArc.centroid(updated);
          x = isNaN(c[0]) ? 0 : c[0];
          y = isNaN(c[1]) ? 0 : c[1];
          h = Math.sqrt(x * x + y * y);
          // TODO: ratio should be an option?
          ratio = $$.radius && h ? (36 / $$.radius > 0.375 ? 1.175 - 36 / $$.radius : 0.8) * $$.radius / h : 0;
          translate = "translate(" + (x * ratio) +  ',' + (y * ratio) +  ")";
      }
      return translate;
  };
  
  c3_chart_internal_fn.getArcRatio = function (d) {
      var $$ = this,
          whole = $$.hasType('gauge') ? Math.PI : (Math.PI * 2);
      return d ? (d.endAngle - d.startAngle) / whole : null;
  };
  
  c3_chart_internal_fn.convertToArcData = function (d) {
      return this.addName({
          id: d.data.id,
          value: d.value,
          ratio: this.getArcRatio(d),
          index: d.index
      });
  };
  
  c3_chart_internal_fn.textForArcLabel = function (d) {
      var $$ = this,
          updated, value, ratio, format;
      if (! $$.shouldShowArcLabel()) { return ""; }
      updated = $$.updateAngle(d);
      value = updated ? updated.value : null;
      ratio = $$.getArcRatio(updated);
      if (! $$.hasType('gauge') && ! $$.meetsArcLabelThreshold(ratio)) { return ""; }
      format = $$.getArcLabelFormat();
      return format ? format(value, ratio) : $$.defaultArcValueFormat(value, ratio);
  };
  
  c3_chart_internal_fn.expandArc = function (id, withoutFadeOut) {
      var $$ = this,
          target = $$.svg.selectAll('.' + CLASS.chartArc + $$.selectorTarget(id)),
          noneTargets = $$.svg.selectAll('.' + CLASS.arc).filter(function (data) { return data.data.id !== id; });
  
      if ($$.shouldExpand(id)) {
          target.selectAll('path')
              .transition().duration(50)
              .attr("d", $$.svgArcExpanded)
              .transition().duration(100)
              .attr("d", $$.svgArcExpandedSub)
              .each(function (d) {
                  if ($$.isDonutType(d.data)) {
                      // callback here
                  }
              });
      }
      if (!withoutFadeOut) {
          noneTargets.style("opacity", 0.3);
      }
  };
  
  c3_chart_internal_fn.unexpandArc = function (id) {
      var $$ = this,
          target = $$.svg.selectAll('.' + CLASS.chartArc + $$.selectorTarget(id));
      target.selectAll('path.' + CLASS.arc)
          .transition().duration(50)
          .attr("d", $$.svgArc);
      $$.svg.selectAll('.' + CLASS.arc)
          .style("opacity", 1);
  };
  
  c3_chart_internal_fn.shouldExpand = function (id) {
      var $$ = this, config = $$.config;
      return ($$.isDonutType(id) && config.donut_expand) || ($$.isGaugeType(id) && config.gauge_expand) || ($$.isPieType(id) && config.pie_expand);
  };
  
  c3_chart_internal_fn.shouldShowArcLabel = function () {
      var $$ = this, config = $$.config, shouldShow = true;
      if ($$.hasType('donut')) {
          shouldShow = config.donut_label_show;
      } else if ($$.hasType('pie')) {
          shouldShow = config.pie_label_show;
      }
      // when gauge, always true
      return shouldShow;
  };
  
  c3_chart_internal_fn.meetsArcLabelThreshold = function (ratio) {
      var $$ = this, config = $$.config,
          threshold = $$.hasType('donut') ? config.donut_label_threshold : config.pie_label_threshold;
      return ratio >= threshold;
  };
  
  c3_chart_internal_fn.getArcLabelFormat = function () {
      var $$ = this, config = $$.config,
          format = config.pie_label_format;
      if ($$.hasType('gauge')) {
          format = config.gauge_label_format;
      } else if ($$.hasType('donut')) {
          format = config.donut_label_format;
      }
      return format;
  };
  
  c3_chart_internal_fn.getArcTitle = function () {
      var $$ = this;
      return $$.hasType('donut') ? $$.config.donut_title : "";
  };
  
  c3_chart_internal_fn.descByStartAngle = function (a, b) {
      return a.startAngle - b.startAngle;
  };
  
  c3_chart_internal_fn.updateTargetsForArc = function (targets) {
      var $$ = this, main = $$.main,
          mainPieUpdate, mainPieEnter,
          classChartArc = $$.classChartArc.bind($$),
          classArcs = $$.classArcs.bind($$);
      mainPieUpdate = main.select('.' + CLASS.chartArcs).selectAll('.' + CLASS.chartArc)
          .data($$.pie(targets))
          .attr("class", classChartArc);
      mainPieEnter = mainPieUpdate.enter().append("g")
          .attr("class", classChartArc);
      mainPieEnter.append('g')
          .attr('class', classArcs);
      mainPieEnter.append("text")
          .attr("dy", $$.hasType('gauge') ? "-0.35em" : ".35em")
          .style("opacity", 0)
          .style("text-anchor", "middle")
          .style("pointer-events", "none");
      // MEMO: can not keep same color..., but not bad to update color in redraw
      //mainPieUpdate.exit().remove();
  };
  
  c3_chart_internal_fn.initArc = function () {
      var $$ = this;
      $$.arcs = $$.main.select('.' + CLASS.chart).append("g")
          .attr("class", CLASS.chartArcs)
          .attr("transform", $$.getTranslate('arc'));
      $$.arcs.append('text')
          .attr('class', CLASS.chartArcsTitle)
          .style("text-anchor", "middle")
          .text($$.getArcTitle());
  };
  
  c3_chart_internal_fn.redrawArc = function (duration, durationForExit, withTransform) {
      var $$ = this, d3 = $$.d3, config = $$.config, main = $$.main,
          mainArc;
      mainArc = main.selectAll('.' + CLASS.arcs).selectAll('.' + CLASS.arc)
          .data($$.arcData.bind($$));
      mainArc.enter().append('path')
          .attr("class", $$.classArc.bind($$))
          .style("fill", function (d) { return $$.color(d.data); })
          .style("cursor", function (d) { return config.data_selection_isselectable(d) ? "pointer" : null; })
          .style("opacity", 0)
          .each(function (d) {
              if ($$.isGaugeType(d.data)) {
                  d.startAngle = d.endAngle = -1 * (Math.PI / 2);
              }
              this._current = d;
          })
          .on('mouseover', function (d) {
              var updated, arcData;
              if ($$.transiting) { // skip while transiting
                  return;
              }
              updated = $$.updateAngle(d);
              arcData = $$.convertToArcData(updated);
              // transitions
              $$.expandArc(updated.data.id);
              $$.toggleFocusLegend(updated.data.id, true);
              $$.config.data_onmouseover(arcData, this);
          })
          .on('mousemove', function (d) {
              var updated = $$.updateAngle(d),
                  arcData = $$.convertToArcData(updated),
                  selectedData = [arcData];
              $$.showTooltip(selectedData, d3.mouse(this));
          })
          .on('mouseout', function (d) {
              var updated, arcData;
              if ($$.transiting) { // skip while transiting
                  return;
              }
              updated = $$.updateAngle(d);
              arcData = $$.convertToArcData(updated);
              // transitions
              $$.unexpandArc(updated.data.id);
              $$.revertLegend();
              $$.hideTooltip();
              $$.config.data_onmouseout(arcData, this);
          })
          .on('click', function (d, i) {
              var updated, arcData;
              if (!$$.toggleShape) {
                  return;
              }
              updated = $$.updateAngle(d);
              arcData = $$.convertToArcData(updated);
              $$.toggleShape(this, arcData, i); // onclick called in toogleShape()
          });
      mainArc
          .attr("transform", function (d) { return !$$.isGaugeType(d.data) && withTransform ? "scale(0)" : ""; })
          .style("opacity", function (d) { return d === this._current ? 0 : 1; })
          .each(function () { $$.transiting = true; })
          .transition().duration(duration)
          .attrTween("d", function (d) {
              var updated = $$.updateAngle(d), interpolate;
              if (! updated) {
                  return function () { return "M 0 0"; };
              }
              //                if (this._current === d) {
              //                    this._current = {
              //                        startAngle: Math.PI*2,
              //                        endAngle: Math.PI*2,
              //                    };
              //                }
              if (isNaN(this._current.endAngle)) {
                  this._current.endAngle = this._current.startAngle;
              }
              interpolate = d3.interpolate(this._current, updated);
              this._current = interpolate(0);
              return function (t) { return $$.getArc(interpolate(t), true); };
          })
          .attr("transform", withTransform ? "scale(1)" : "")
          .style("fill", function (d) {
              return $$.levelColor ? $$.levelColor(d.data.values[0].value) : $$.color(d.data.id);
          }) // Where gauge reading color would receive customization.
          .style("opacity", 1)
          .call($$.endall, function () {
              $$.transiting = false;
          });
      mainArc.exit().transition().duration(durationForExit)
          .style('opacity', 0)
          .remove();
      main.selectAll('.' + CLASS.chartArc).select('text')
          .style("opacity", 0)
          .attr('class', function (d) { return $$.isGaugeType(d.data) ? CLASS.gaugeValue : ''; })
          .text($$.textForArcLabel.bind($$))
          .attr("transform", $$.transformForArcLabel.bind($$))
          .transition().duration(duration)
          .style("opacity", function (d) { return $$.isTargetToShow(d.data.id) && $$.isArcType(d.data) ? 1 : 0; });
      main.select('.' + CLASS.chartArcsTitle)
          .style("opacity", $$.hasType('donut') || $$.hasType('gauge') ? 1 : 0);
  
  };
  c3_chart_internal_fn.initGauge = function () {
      var $$ = this, config = $$.config, arcs = $$.arcs;
      if ($$.hasType('gauge')) {
          arcs.append('path')
              .attr("class", CLASS.chartArcsBackground)
              .attr("d", function () {
                  var d = {
                      data: [{value: config.gauge_max}],
                      startAngle: -1 * (Math.PI / 2),
                      endAngle: Math.PI / 2
                  };
                  return $$.getArc(d, true, true);
              });
          arcs.append("text")
              .attr("dy", ".75em")
              .attr("class", CLASS.chartArcsGaugeUnit)
              .style("text-anchor", "middle")
              .style("pointer-events", "none")
              .text(config.gauge_label_show ? config.gauge_units : '');
          arcs.append("text")
              .attr("dx", -1 * ($$.innerRadius + (($$.radius - $$.innerRadius) / 2)) + "px")
              .attr("dy", "1.2em")
              .attr("class", CLASS.chartArcsGaugeMin)
              .style("text-anchor", "middle")
              .style("pointer-events", "none")
              .text(config.gauge_label_show ? config.gauge_min : '');
          arcs.append("text")
              .attr("dx", $$.innerRadius + (($$.radius - $$.innerRadius) / 2) + "px")
              .attr("dy", "1.2em")
              .attr("class", CLASS.chartArcsGaugeMax)
              .style("text-anchor", "middle")
              .style("pointer-events", "none")
              .text(config.gauge_label_show ? config.gauge_max : '');
      }
  };
  