// custom axis (hacked axis) d3.svg.haxis = function() { var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, innerTickSize = 6, outerTickSize = 6, tickPadding = 3, tickArguments_ = [10], tickValues = null, tickFormat_, endTickFormat_, tickMultiFormat_, endTickMultiFormat_; function axis(g) { g.each(function() { var g = d3.select(this); // Stash a snapshot of the new scale, and retrieve the old snapshot. var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = scale.copy(); // Ticks, or domain values for ordinal scales. if (endTickMultiFormat_ != null) endTickMultiFormat_ = (endTickMultiFormat_ == null && tickMultiFormat_ != null) ? tickMultiFormat_ : null; var ticks = tickValues == null ? (scale1.ticks ? scale1.ticks.apply(scale1, tickArguments_) : scale1.domain()) : tickValues, tickLength = ticks.length, tickFormat = (tickMultiFormat_ === null)?(tickFormat_ == null ? (scale1.tickFormat ? scale1.tickFormat.apply(scale1, tickArguments_) : d3_identity) : tickFormat_):(d3_time_formatMulti(tickMultiFormat_)), endTickFormat = (endTickMultiFormat_ === null)?(tickFormat):(d3_time_formatMulti(endTickMultiFormat_)), tick = g.selectAll(".tick").data(ticks, scale1), tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", ε), tickExit = d3.transition(tick.exit()).style("opacity", ε).remove(), tickUpdate = d3.transition(tick).style("opacity", 1).attr("class", function(d, i) { return ((!i || i === tickLength - 1)?"end":"") + " tick"; }), tickTransform; function d3_time_formatMulti(formats) { var n = formats.length, i = -1; // convert to formats while (++i < n) formats[i][0] = d3.time.format(formats[i][0]); return function(date, i) { var j = 0, k = 0, f = formats[j], n = formats[k], neighbor = ticks[(j > 0)?(j - 1):(j + 1)]; while (!f[1](date)) f = formats[++j]; while (!n[1](neighbor)) n = formats[++k]; if (j - k > 1) { f = formats[++k]; } return f[0](date); }; } // Domain. var range = d3_scaleRange(scale1), path = g.selectAll(".domain").data([0]), pathUpdate = (path.enter().append("path").attr("class", "domain"), d3.transition(path)); tickEnter.append("line"); tickEnter.append("text"); var lineEnter = tickEnter.select("line"), lineUpdate = tickUpdate.select("line"), text = tick.select("text").text(function(d, i) { var render = (endTickFormat && (!i || i === tickLength - 1))?endTickFormat(d, i):tickFormat(d, i); if (!endTickFormat || render.indexOf('s') === -1) return render.toUpperCase(); return render; }), textEnter = tickEnter.select("text"), textUpdate = tickUpdate.select("text"); switch (orient) { case "bottom": { tickTransform = d3_svg_axisX; lineEnter.attr("y2", innerTickSize); textEnter.attr("y", Math.max(innerTickSize, 0) + tickPadding); lineUpdate.attr("x2", 0).attr("y2", innerTickSize); textUpdate.attr("x", 0).attr("y", Math.max(innerTickSize, 0) + tickPadding); text.attr("dy", ".71em").style("text-anchor", "middle"); pathUpdate.attr("d", "M" + range[0] + "," + outerTickSize + "V0H" + range[1] + "V" + outerTickSize); break; } case "top": { tickTransform = d3_svg_axisX; lineEnter.attr("y2", -innerTickSize); textEnter.attr("y", -(Math.max(innerTickSize, 0) + tickPadding)); lineUpdate.attr("x2", 0).attr("y2", -innerTickSize); textUpdate.attr("x", 0).attr("y", -(Math.max(innerTickSize, 0) + tickPadding)); text.attr("dy", "0em").style("text-anchor", "middle"); pathUpdate.attr("d", "M" + range[0] + "," + -outerTickSize + "V0H" + range[1] + "V" + -outerTickSize); break; } case "left": { tickTransform = d3_svg_axisY; lineEnter.attr("x2", -innerTickSize); textEnter.attr("x", -(Math.max(innerTickSize, 0) + tickPadding)); lineUpdate.attr("x2", -innerTickSize).attr("y2", 0); textUpdate.attr("x", -(Math.max(innerTickSize, 0) + tickPadding)).attr("y", 0); text.attr("dy", ".32em").style("text-anchor", "end"); pathUpdate.attr("d", "M" + -outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + -outerTickSize); break; } case "right": { tickTransform = d3_svg_axisY; lineEnter.attr("x2", innerTickSize); textEnter.attr("x", Math.max(innerTickSize, 0) + tickPadding); lineUpdate.attr("x2", innerTickSize).attr("y2", 0); textUpdate.attr("x", Math.max(innerTickSize, 0) + tickPadding).attr("y", 0); text.attr("dy", ".32em").style("text-anchor", "start"); pathUpdate.attr("d", "M" + outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + outerTickSize); break; } } // If either the new or old scale is ordinal, // entering ticks are undefined in the old scale, // and so can fade-in in the new scale’s position. // Exiting ticks are likewise undefined in the new scale, // and so can fade-out in the old scale’s position. if (scale1.rangeBand) { var x = scale1, dx = x.rangeBand() / 2; scale0 = scale1 = function(d) { return x(d) + dx; }; } else if (scale0.rangeBand) { scale0 = scale1; } else { tickExit.call(tickTransform, scale1); } tickEnter.call(tickTransform, scale0); tickUpdate.call(tickTransform, scale1); }); } axis.scale = function(x) { if (!arguments.length) return scale; scale = x; return axis; }; axis.orient = function(x) { if (!arguments.length) return orient; orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient; return axis; }; axis.ticks = function() { if (!arguments.length) return tickArguments_; tickArguments_ = arguments; return axis; }; axis.tickValues = function(x) { if (!arguments.length) return tickValues; tickValues = x; return axis; }; axis.tickFormat = function(x) { if (!arguments.length) return tickFormat_; tickFormat_ = x; return axis; }; axis.tickMultiFormat = function(x) { if (!arguments.length) return tickMultiFormat_; tickMultiFormat_ = x; return axis; }; axis.endTickMultiFormat = function(x) { if (!arguments.length) return endTickMultiFormat_; endTickMultiFormat_ = x; return axis; }; axis.tickSize = function(x) { var n = arguments.length; if (!n) return innerTickSize; innerTickSize = +x; outerTickSize = +arguments[n - 1]; return axis; }; axis.innerTickSize = function(x) { if (!arguments.length) return innerTickSize; innerTickSize = +x; return axis; }; axis.outerTickSize = function(x) { if (!arguments.length) return outerTickSize; outerTickSize = +x; return axis; }; axis.tickPadding = function(x) { if (!arguments.length) return tickPadding; tickPadding = +x; return axis; }; axis.tickSubdivide = function() { return arguments.length && axis; }; return axis; }; // necessary variables from the d3 namespace var ε = 1e-6; var d3_identity = function(d) { return d; }; var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = {top: 1, right: 1, bottom: 1, left: 1}; function d3_svg_axisX(selection, x) { selection.attr("transform", function(d) { return "translate(" + x(d) + ",0)"; }); } function d3_svg_axisY(selection, y) { selection.attr("transform", function(d) { return "translate(0," + y(d) + ")"; }); } function d3_scaleExtent(domain) { var start = domain[0], stop = domain[domain.length - 1]; return start < stop ? [start, stop] : [stop, start]; } function d3_scaleRange(scale) { return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range()); }