in mc/Scripts/nv.d3.js [1582:1874]
function chart(selection) {
renderWatch.reset();
selection.each(function(data) {
var container = d3.select(this);
nv.utils.initSVG(container);
// Setup containers and skeleton of chart
var wrap = container.selectAll('g.nv-wrap.nv-axis').data([data]);
var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-axis');
var gEnter = wrapEnter.append('g');
var g = wrap.select('g');
if (ticks !== null)
axis.ticks(ticks);
else if (axis.orient() == 'top' || axis.orient() == 'bottom')
axis.ticks(Math.abs(scale.range()[1] - scale.range()[0]) / 100);
//TODO: consider calculating width/height based on whether or not label is added, for reference in charts using this component
g.watchTransition(renderWatch, 'axis').call(axis);
scale0 = scale0 || axis.scale();
var fmt = axis.tickFormat();
if (fmt == null) {
fmt = scale0.tickFormat();
}
var axisLabel = g.selectAll('text.nv-axislabel')
.data([axisLabelText || null]);
axisLabel.exit().remove();
var xLabelMargin;
var axisMaxMin;
var w;
switch (axis.orient()) {
case 'top':
axisLabel.enter().append('text').attr('class', 'nv-axislabel');
if (scale.range().length < 2) {
w = 0;
} else if (scale.range().length === 2) {
w = scale.range()[1];
} else {
w = scale.range()[scale.range().length-1]+(scale.range()[1]-scale.range()[0]);
}
axisLabel
.attr('text-anchor', 'middle')
.attr('y', 0)
.attr('x', w/2);
if (showMaxMin) {
axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
.data(scale.domain());
axisMaxMin.enter().append('g').attr('class',function(d,i){
return ['nv-axisMaxMin','nv-axisMaxMin-x',(i == 0 ? 'nv-axisMin-x':'nv-axisMax-x')].join(' ')
}).append('text');
axisMaxMin.exit().remove();
axisMaxMin
.attr('transform', function(d,i) {
return 'translate(' + nv.utils.NaNtoZero(scale(d)) + ',0)'
})
.select('text')
.attr('dy', '-0.5em')
.attr('y', -axis.tickPadding())
.attr('text-anchor', 'middle')
.text(function(d,i) {
var v = fmt(d);
return ('' + v).match('NaN') ? '' : v;
});
axisMaxMin.watchTransition(renderWatch, 'min-max top')
.attr('transform', function(d,i) {
return 'translate(' + nv.utils.NaNtoZero(scale.range()[i]) + ',0)'
});
}
break;
case 'bottom':
xLabelMargin = axisLabelDistance + 36;
var maxTextWidth = 30;
var textHeight = 0;
var xTicks = g.selectAll('g').select("text");
var rotateLabelsRule = '';
if (rotateLabels%360) {
//Calculate the longest xTick width
xTicks.each(function(d,i){
var box = this.getBoundingClientRect();
var width = box.width;
textHeight = box.height;
if(width > maxTextWidth) maxTextWidth = width;
});
rotateLabelsRule = 'rotate(' + rotateLabels + ' 0,' + (textHeight/2 + axis.tickPadding()) + ')';
//Convert to radians before calculating sin. Add 30 to margin for healthy padding.
var sin = Math.abs(Math.sin(rotateLabels*Math.PI/180));
xLabelMargin = (sin ? sin*maxTextWidth : maxTextWidth)+30;
//Rotate all xTicks
xTicks
.attr('transform', rotateLabelsRule)
.style('text-anchor', rotateLabels%360 > 0 ? 'start' : 'end');
}
axisLabel.enter().append('text').attr('class', 'nv-axislabel');
if (scale.range().length < 2) {
w = 0;
} else if (scale.range().length === 2) {
w = scale.range()[1];
} else {
w = scale.range()[scale.range().length-1]+(scale.range()[1]-scale.range()[0]);
}
axisLabel
.attr('text-anchor', 'middle')
.attr('y', xLabelMargin)
.attr('x', w/2);
if (showMaxMin) {
//if (showMaxMin && !isOrdinal) {
axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
//.data(scale.domain())
.data([scale.domain()[0], scale.domain()[scale.domain().length - 1]]);
axisMaxMin.enter().append('g').attr('class',function(d,i){
return ['nv-axisMaxMin','nv-axisMaxMin-x',(i == 0 ? 'nv-axisMin-x':'nv-axisMax-x')].join(' ')
}).append('text');
axisMaxMin.exit().remove();
axisMaxMin
.attr('transform', function(d,i) {
return 'translate(' + nv.utils.NaNtoZero((scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0))) + ',0)'
})
.select('text')
.attr('dy', '.71em')
.attr('y', axis.tickPadding())
.attr('transform', rotateLabelsRule)
.style('text-anchor', rotateLabels ? (rotateLabels%360 > 0 ? 'start' : 'end') : 'middle')
.text(function(d,i) {
var v = fmt(d);
return ('' + v).match('NaN') ? '' : v;
});
axisMaxMin.watchTransition(renderWatch, 'min-max bottom')
.attr('transform', function(d,i) {
return 'translate(' + nv.utils.NaNtoZero((scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0))) + ',0)'
});
}
if (staggerLabels)
xTicks
.attr('transform', function(d,i) {
return 'translate(0,' + (i % 2 == 0 ? '0' : '12') + ')'
});
break;
case 'right':
axisLabel.enter().append('text').attr('class', 'nv-axislabel');
axisLabel
.style('text-anchor', rotateYLabel ? 'middle' : 'begin')
.attr('transform', rotateYLabel ? 'rotate(90)' : '')
.attr('y', rotateYLabel ? (-Math.max(margin.right, width) + 12) : -10) //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
.attr('x', rotateYLabel ? (d3.max(scale.range()) / 2) : axis.tickPadding());
if (showMaxMin) {
axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
.data(scale.domain());
axisMaxMin.enter().append('g').attr('class',function(d,i){
return ['nv-axisMaxMin','nv-axisMaxMin-y',(i == 0 ? 'nv-axisMin-y':'nv-axisMax-y')].join(' ')
}).append('text')
.style('opacity', 0);
axisMaxMin.exit().remove();
axisMaxMin
.attr('transform', function(d,i) {
return 'translate(0,' + nv.utils.NaNtoZero(scale(d)) + ')'
})
.select('text')
.attr('dy', '.32em')
.attr('y', 0)
.attr('x', axis.tickPadding())
.style('text-anchor', 'start')
.text(function(d, i) {
var v = fmt(d);
return ('' + v).match('NaN') ? '' : v;
});
axisMaxMin.watchTransition(renderWatch, 'min-max right')
.attr('transform', function(d,i) {
return 'translate(0,' + nv.utils.NaNtoZero(scale.range()[i]) + ')'
})
.select('text')
.style('opacity', 1);
}
break;
case 'left':
/*
//For dynamically placing the label. Can be used with dynamically-sized chart axis margins
var yTicks = g.selectAll('g').select("text");
yTicks.each(function(d,i){
var labelPadding = this.getBoundingClientRect().width + axis.tickPadding() + 16;
if(labelPadding > width) width = labelPadding;
});
*/
axisLabel.enter().append('text').attr('class', 'nv-axislabel');
axisLabel
.style('text-anchor', rotateYLabel ? 'middle' : 'end')
.attr('transform', rotateYLabel ? 'rotate(-90)' : '')
.attr('y', rotateYLabel ? (-Math.max(margin.left, width) + 25 - (axisLabelDistance || 0)) : -10)
.attr('x', rotateYLabel ? (-d3.max(scale.range()) / 2) : -axis.tickPadding());
if (showMaxMin) {
axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
.data(scale.domain());
axisMaxMin.enter().append('g').attr('class',function(d,i){
return ['nv-axisMaxMin','nv-axisMaxMin-y',(i == 0 ? 'nv-axisMin-y':'nv-axisMax-y')].join(' ')
}).append('text')
.style('opacity', 0);
axisMaxMin.exit().remove();
axisMaxMin
.attr('transform', function(d,i) {
return 'translate(0,' + nv.utils.NaNtoZero(scale0(d)) + ')'
})
.select('text')
.attr('dy', '.32em')
.attr('y', 0)
.attr('x', -axis.tickPadding())
.attr('text-anchor', 'end')
.text(function(d,i) {
var v = fmt(d);
return ('' + v).match('NaN') ? '' : v;
});
axisMaxMin.watchTransition(renderWatch, 'min-max right')
.attr('transform', function(d,i) {
return 'translate(0,' + nv.utils.NaNtoZero(scale.range()[i]) + ')'
})
.select('text')
.style('opacity', 1);
}
break;
}
axisLabel.text(function(d) { return d });
if (showMaxMin && (axis.orient() === 'left' || axis.orient() === 'right')) {
//check if max and min overlap other values, if so, hide the values that overlap
g.selectAll('g') // the g's wrapping each tick
.each(function(d,i) {
d3.select(this).select('text').attr('opacity', 1);
if (scale(d) < scale.range()[1] + 10 || scale(d) > scale.range()[0] - 10) { // 10 is assuming text height is 16... if d is 0, leave it!
if (d > 1e-10 || d < -1e-10) // accounts for minor floating point errors... though could be problematic if the scale is EXTREMELY SMALL
d3.select(this).attr('opacity', 0);
d3.select(this).select('text').attr('opacity', 0); // Don't remove the ZERO line!!
}
});
//if Max and Min = 0 only show min, Issue #281
if (scale.domain()[0] == scale.domain()[1] && scale.domain()[0] == 0) {
wrap.selectAll('g.nv-axisMaxMin').style('opacity', function (d, i) {
return !i ? 1 : 0
});
}
}
if (showMaxMin && (axis.orient() === 'top' || axis.orient() === 'bottom')) {
var maxMinRange = [];
wrap.selectAll('g.nv-axisMaxMin')
.each(function(d,i) {
try {
if (i) // i== 1, max position
maxMinRange.push(scale(d) - this.getBoundingClientRect().width - 4); //assuming the max and min labels are as wide as the next tick (with an extra 4 pixels just in case)
else // i==0, min position
maxMinRange.push(scale(d) + this.getBoundingClientRect().width + 4)
}catch (err) {
if (i) // i== 1, max position
maxMinRange.push(scale(d) - 4); //assuming the max and min labels are as wide as the next tick (with an extra 4 pixels just in case)
else // i==0, min position
maxMinRange.push(scale(d) + 4);
}
});
// the g's wrapping each tick
g.selectAll('g').each(function(d, i) {
if (scale(d) < maxMinRange[0] || scale(d) > maxMinRange[1]) {
if (d > 1e-10 || d < -1e-10) // accounts for minor floating point errors... though could be problematic if the scale is EXTREMELY SMALL
d3.select(this).remove();
else
d3.select(this).select('text').remove(); // Don't remove the ZERO line!!
}
});
}
//Highlight zero tick line
g.selectAll('.tick')
.filter(function (d) {
/*
The filter needs to return only ticks at or near zero.
Numbers like 0.00001 need to count as zero as well,
and the arithmetic trick below solves that.
*/
return !parseFloat(Math.round(d * 100000) / 1000000) && (d !== undefined)
})
.classed('zero', true);
//store old scales for use in transitions on update
scale0 = scale.copy();
});
renderWatch.renderEnd('axis immediate');
return chart;
}