c3_chart_internal_fn.initGrid = function () {
    var $$ = this, config = $$.config, d3 = $$.d3;
    $$.grid = $$.main.append('g')
        .attr("clip-path", $$.clipPath)
        .attr('class', CLASS.grid);
    if (config.grid_x_show) {
        $$.grid.append("g").attr("class", CLASS.xgrids);
    }
    if (config.grid_y_show) {
        $$.grid.append('g').attr('class', CLASS.ygrids);
    }
    $$.grid.append('g').attr("class", CLASS.xgridLines);
    $$.grid.append('g').attr('class', CLASS.ygridLines);
    if (config.grid_focus_show) {
        $$.grid.append('g')
            .attr("class", CLASS.xgridFocus)
            .append('line')
            .attr('class', CLASS.xgridFocus);
    }
    $$.xgrid = d3.selectAll([]);
    $$.xgridLines = d3.selectAll([]);
};

c3_chart_internal_fn.updateXGrid = function (withoutUpdate) {
    var $$ = this, config = $$.config, d3 = $$.d3,
        xgridData = $$.generateGridData(config.grid_x_type, $$.x),
        tickOffset = $$.isCategorized() ? $$.xAxis.tickOffset() : 0;

    $$.xgridAttr = config.axis_rotated ? {
        'x1': 0,
        'x2': $$.width,
        'y1': function (d) { return $$.x(d) - tickOffset; },
        'y2': function (d) { return $$.x(d) - tickOffset; }
    } : {
        'x1': function (d) { return $$.x(d) + tickOffset; },
        'x2': function (d) { return $$.x(d) + tickOffset; },
        'y1': 0,
        'y2': $$.height
    };

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

c3_chart_internal_fn.updateYGrid = function () {
    var $$ = this, config = $$.config;
    $$.ygrid = $$.main.select('.' + CLASS.ygrids).selectAll('.' + CLASS.ygrid)
        .data($$.y.ticks(config.grid_y_ticks));
    $$.ygrid.enter().append('line')
        .attr('class', CLASS.ygrid);
    $$.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);
    $$.ygrid.exit().remove();
    $$.smoothLines($$.ygrid, 'grid');
};


c3_chart_internal_fn.redrawGrid = function (duration, withY) {
    var $$ = this, main = $$.main, config = $$.config,
        xgridLine, ygridLine, yv;
    main.select('line.' + CLASS.xgridFocus).style("visibility", "hidden");
    if (config.grid_x_show) {
        $$.updateXGrid();
    }
    $$.xgridLines = main.select('.' + CLASS.xgridLines).selectAll('.' + CLASS.xgridLine)
        .data(config.grid_x_lines);
    // enter
    xgridLine = $$.xgridLines.enter().append('g')
        .attr("class", function (d) { return CLASS.xgridLine + (d.class ? ' ' + d.class : ''); });
    xgridLine.append('line')
        .style("opacity", 0);
    xgridLine.append('text')
        .attr("text-anchor", "end")
        .attr("transform", config.axis_rotated ? "" : "rotate(-90)")
        .attr('dx', config.axis_rotated ? 0 : -$$.margin.top)
        .attr('dy', -5)
        .style("opacity", 0);
    // udpate
    // done in d3.transition() of the end of this function
    // exit
    $$.xgridLines.exit().transition().duration(duration)
        .style("opacity", 0)
        .remove();

    // Y-Grid
    if (withY && config.grid_y_show) {
        $$.updateYGrid();
    }
    if (withY) {
        $$.ygridLines = main.select('.' + CLASS.ygridLines).selectAll('.' + CLASS.ygridLine)
            .data(config.grid_y_lines);
        // enter
        ygridLine = $$.ygridLines.enter().append('g')
            .attr("class", function (d) { return CLASS.ygridLine + (d.class ? ' ' + d.class : ''); });
        ygridLine.append('line')
            .style("opacity", 0);
        ygridLine.append('text')
            .attr("text-anchor", "end")
            .attr("transform", config.axis_rotated ? "rotate(-90)" : "")
            .attr('dx', config.axis_rotated ? 0 : -$$.margin.top)
            .attr('dy', -5)
            .style("opacity", 0);
        // update
        yv = $$.yv.bind($$);
        $$.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);
        $$.ygridLines.select('text')
          .transition().duration(duration)
            .attr("x", config.axis_rotated ? 0 : $$.width)
            .attr("y", yv)
            .text(function (d) { return d.text; })
            .style("opacity", 1);
        // exit
        $$.ygridLines.exit().transition().duration(duration)
            .style("opacity", 0)
            .remove();
    }
};
c3_chart_internal_fn.addTransitionForGrid = function (transitions) {
    var $$ = this, config = $$.config, xv = $$.xv.bind($$);
    transitions.push($$.xgridLines.select('line').transition()
                     .attr("x1", config.axis_rotated ? 0 : xv)
                     .attr("x2", config.axis_rotated ? $$.width : xv)
                     .attr("y1", config.axis_rotated ? xv : $$.margin.top)
                     .attr("y2", config.axis_rotated ? xv : $$.height)
                     .style("opacity", 1));
    transitions.push($$.xgridLines.select('text').transition()
                     .attr("x", config.axis_rotated ? $$.width : 0)
                     .attr("y", xv)
                     .text(function (d) { return d.text; })
                     .style("opacity", 1));
};
c3_chart_internal_fn.showXGridFocus = function (selectedData) {
    var $$ = this, config = $$.config,
        dataToShow = selectedData.filter(function (d) { return d && isValue(d.value); }),
        focusEl = $$.main.selectAll('line.' + CLASS.xgridFocus),
        xx = $$.xx.bind($$);
    if (! config.tooltip_show) { return; }
    // Hide when scatter plot exists
    if ($$.hasType('scatter') || $$.hasArcType()) { return; }
    focusEl
        .style("visibility", "visible")
        .data([dataToShow[0]])
        .attr(config.axis_rotated ? 'y1' : 'x1', xx)
        .attr(config.axis_rotated ? 'y2' : 'x2', xx);
    $$.smoothLines(focusEl, 'grid');
};
c3_chart_internal_fn.hideXGridFocus = function () {
    this.main.select('line.' + CLASS.xgridFocus).style("visibility", "hidden");
};
c3_chart_internal_fn.updateXgridFocus = function () {
    var $$ = this, config = $$.config;
    $$.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);
};
c3_chart_internal_fn.generateGridData = function (type, scale) {
    var $$ = this,
        gridData = [], xDomain, firstYear, lastYear, i,
        tickNum = $$.main.select("." + CLASS.axisX).selectAll('.tick').size();
    if (type === 'year') {
        xDomain = $$.getXDomain();
        firstYear = xDomain[0].getFullYear();
        lastYear = xDomain[1].getFullYear();
        for (i = firstYear; i <= lastYear; i++) {
            gridData.push(new Date(i + '-01-01 00:00:00'));
        }
    } else {
        gridData = scale.ticks(10);
        if (gridData.length > tickNum) { // use only int
            gridData = gridData.filter(function (d) { return ("" + d).indexOf('.') < 0; });
        }
    }
    return gridData;
};
c3_chart_internal_fn.getGridFilterToRemove = function (params) {
    return params ? function (line) {
        var found = false;
        [].concat(params).forEach(function (param) {
            if ((('value' in param && line.value === params.value) || ('class' in param && line.class === params.class))) {
                found = true;
            }
        });
        return found;
    } : function () { return true; };
};
c3_chart_internal_fn.removeGridLines = function (params, forX) {
    var $$ = this, config = $$.config,
        toRemove = $$.getGridFilterToRemove(params),
        toShow = function (line) { return !toRemove(line); },
        classLines = forX ? CLASS.xgridLines : CLASS.ygridLines,
        classLine = forX ? CLASS.xgridLine : CLASS.ygridLine;
    $$.main.select('.' + classLines).selectAll('.' + classLine).filter(toRemove)
        .transition().duration(config.transition_duration)
        .style('opacity', 0).remove();
    if (forX) {
        config.grid_x_lines = config.grid_x_lines.filter(toShow);
    } else {
        config.grid_y_lines = config.grid_y_lines.filter(toShow);
    }
};
