1 /** 2 * Constructs a Raphael axis renderer function. 3 * 4 * @return {D3RaphaelAxis} 5 * 6 * @see <a href="https://github.com/mbostock/d3/wiki/SVG-Axes">d3.svg.axis</a> 7 */ 8 d3.raphael.axis = function() { 9 var scale = d3.scale.linear(), 10 orient = "bottom", 11 tickMajorSize = 6, 12 tickMinorSize = 6, 13 tickEndSize = 6, 14 tickPadding = 3, 15 tickArguments_ = [10], 16 tickValues = null, 17 tickFormat_, 18 tickSubdivide = 0; 19 20 // todo: work-around because we don't have groups 21 var top = 0, 22 left = 0; 23 24 // todo: work-around because we don't have stylesheet 25 var classPrefix = ""; 26 27 // todo: figure out if we can refactor to reuse code 28 29 function axis(selection) { 30 31 selection.each(function() { 32 var g = selection.root.select(""); 33 34 // Ticks, or domain values for ordinal scales. 35 var ticks = tickValues == null ? (scale.ticks ? scale.ticks.apply(scale, tickArguments_) : scale.domain()) : tickValues, 36 tickFormat = tickFormat_ == null ? (scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments_) : String) : tickFormat_; 37 38 // Major ticks. 39 var tick = g.selectAll("g").data(ticks, String), 40 tickEnter = tick.enter().append("path") 41 .classed(classPrefix + "path", true) 42 // tickEnter = tick.enter().insert("g", "path").style("opacity", 1e-6), 43 // tickExit = d3.transition(tick.exit()).style("opacity", 1e-6).remove(), 44 // tickUpdate = d3.transition(tick).style("opacity", 1), 45 // tickTransform; 46 47 var text = tick.append("text") 48 .attr("text", tickFormat ); 49 50 // Domain. 51 var range = d3_scaleRange(scale), 52 path = g.selectAll(".domain").data([0]), 53 pathEnter = path.enter().append("path") 54 .classed(classPrefix + "pathdomain", true) 55 // pathEnter = path.enter().append("path").attr("class", "domain") 56 // pathUpdate = d3.transition(path); 57 58 // Stash a snapshot of the new scale, and retrieve the old snapshot. 59 var scale1 = scale.copy(), 60 scale0 = this.__chart__ || scale1; 61 this.__chart__ = scale1; 62 63 switch (orient) { 64 case "top": { 65 tick.attr("path", function(d) { return d3_raphael_pathArrayToString([["M", [left + scale1(d), top]],["l", [0, -tickMajorSize]]]); }); 66 text.attr("x", function(d) { return scale1(d) + left + (scale1.rangeBand? scale1.rangeBand() / 2.0 : 0); }) 67 .attr("y", top- 7 ) // todo add dy support to raphael 68 .attr("text-anchor", "middle") 69 // path.attr("path", "M" + (-tickEndSize + left) + "," + (range[0] + top) + "h" + tickEndSize + "v" + (range[1] + top) + "h" + -tickEndSize) 70 path.attr("path", "M" + (range[0] + left) + "," + (-tickEndSize + top) + "v" + tickEndSize + "H" + (range[1] + left) + "v" + -tickEndSize) 71 72 break; 73 } 74 75 case "bottom": { 76 tick.attr("path", function(d) { return d3_raphael_pathArrayToString([["M", [left + scale1(d), top]],["l", [0, tickMajorSize]]]); }); 77 text.attr("x", function(d) { return scale1(d) + left + (scale1.rangeBand? scale1.rangeBand() / 2.0 : 0); }) 78 .attr("y", top + tickMajorSize + 7 ) // todo add dy support to raphael 79 .attr("text-anchor", "middle") 80 // path.attr("path", "M" + (-tickEndSize + left) + "," + (range[0] + top) + "h" + tickEndSize + "v" + (range[1] + top) + "h" + -tickEndSize) 81 path.attr("path", "M" + (range[0] + left) + "," + (tickEndSize + top) + "v" + -tickEndSize + "H" + (range[1] + left) + "v" + tickEndSize) 82 83 break; 84 } 85 86 87 case "left": { 88 tick.attr("path", function(d) { return d3_raphael_pathArrayToString([["M", [left, scale1(d) + top]],["l", [-tickMajorSize,0]]]); }); 89 path.attr("path", "M" + (-tickEndSize + left) + "," + (range[0] + top) + "h" + tickEndSize + "V" + (range[1] + top) + "h" + -tickEndSize) 90 text.attr("x", left - 5) 91 .attr("y", function(d) { return scale1(d) + top + (scale1.rangeBand? scale1.rangeBand() / 2.0 : 0); }) 92 .attr("text-anchor", "end") 93 94 break; 95 } 96 97 default: { 98 throw "Unsupported " + orient; 99 } 100 } 101 102 // // For quantitative scales: 103 // // - enter new ticks from the old scale 104 // // - exit old ticks to the new scale 105 // if (scale.ticks) { 106 // tickEnter.call(tickTransform, scale0); 107 // tickUpdate.call(tickTransform, scale1); 108 // tickExit.call(tickTransform, scale1); 109 // subtickEnter.call(tickTransform, scale0); 110 // subtickUpdate.call(tickTransform, scale1); 111 // subtickExit.call(tickTransform, scale1); 112 // } 113 // 114 // // For ordinal scales: 115 // // - any entering ticks are undefined in the old scale 116 // // - any exiting ticks are undefined in the new scale 117 // // Therefore, we only need to transition updating ticks. 118 // else { 119 // var dx = scale1.rangeBand() / 2, x = function(d) { return scale1(d) + dx; }; 120 // tickEnter.call(tickTransform, x); 121 // tickUpdate.call(tickTransform, x); 122 // } 123 124 125 126 127 // tick.attr("path", function(d) { return d3_raphael_pathArrayToString( 128 // [["M", [left, scale(d) + top]],["l", [-6,0]]] 129 // ); }); 130 // 131 // tick.append("text") 132 // .attr("x", left - 2) 133 // .attr("y", function(d) { return scale(d) + top + scale.rangeBand() / 2; }) 134 // .attr("text-anchor", "end") 135 // .attr("text", function(d) { return d;} ) 136 // 137 // var range = d3_scaleRange(scale); 138 // console.log(range); 139 // g.select("rect") 140 // .append("path") 141 // .attr("path", d3_raphael_pathArrayToString([["M", [left, range[0] + top]],["L",[left, range[1] + top]]])) 142 }) 143 } 144 145 /** 146 * Get or set the associated scale. If scale is specified, sets the scale and returns the axis. If scale is not specified, returns the current scale which defaults to a linear scale. 147 * 148 * @param {d3.Scale} x scale 149 * @return {D3RaphaelAxis} this 150 * 151 * @see <code><a href="https://github.com/mbostock/d3/wiki/SVG-Axes#wiki-axis_scale">d3.svg.axis().scale()</a></code> 152 * 153 * @function 154 * @name D3RaphaelAxis#scale 155 */ 156 axis.scale = function(x) { 157 if (!arguments.length) return scale; 158 scale = x; 159 return axis; 160 }; 161 162 /** 163 * Get or set the axis orientation. If orientation is not specified, returns the current orientation, which defaults to "bottom". 164 * 165 * @param {String} x orientation, one of top, bottom, or left. NOTE: right currently unsupported. top/bottom for horizontal axis, and left for vertical. 166 * @return {D3RaphaelAxis} this 167 * 168 * @see <code><a href="https://github.com/mbostock/d3/wiki/SVG-Axes#wiki-axis_orient">d3.svg.axis().orient()</a></code> 169 * 170 * @function 171 * @name D3RaphaelAxis#orient 172 */ 173 axis.orient = function(x) { 174 if (!arguments.length) return orient; 175 orient = x; 176 return axis; 177 }; 178 179 /** 180 * Get or set the size of major, minor and end ticks. 181 * 182 * @param {Number} x major tick size 183 * @param {Number} y minor tick size (optional) 184 * @param {Number} z end tick size (optional) 185 * @return {D3RaphaelAxis} this 186 * 187 * @see <code><a href="https://github.com/mbostock/d3/wiki/SVG-Axes#wiki-axis_tickSize">d3.svg.axis().tickSize()</a></code> 188 * 189 * @function 190 * @name D3RaphaelAxis#tickSize 191 */ 192 axis.tickSize = function(x, y, z) { 193 if (!arguments.length) return tickMajorSize; 194 var n = arguments.length - 1; 195 tickMajorSize = +x; 196 tickMinorSize = n > 1 ? +y : tickMajorSize; 197 tickEndSize = n > 0 ? +arguments[n] : tickMajorSize; 198 return axis; 199 }; 200 201 /** 202 * Get or set the top offset for axis rendering. This is a work-around for the fact Raphael doesn't have a group element. 203 * 204 * @param {Number} val top offset, in pixels 205 * @return {D3RaphaelAxis} this 206 * 207 * @function 208 * @name D3RaphaelAxis#top 209 */ 210 axis.top = function(val) { 211 if(typeof val === "undefined") 212 return top; 213 else 214 top = val; 215 216 return this; 217 } 218 219 /** 220 * Get or set the left offset for the axis rendering. This is a work-around for the fact Raphael doesn't have a group element. 221 * 222 * @param {Number} val left offset, in pixels 223 * @return {D3RaphaelAxis} this 224 * 225 * @function 226 * @name D3RaphaelAxis#left 227 */ 228 axis.left = function(val) { 229 if(typeof val === "undefined") 230 return left; 231 else 232 left = val; 233 234 return this; 235 } 236 237 /** 238 * Get or set the class name prefix appended to the class names used to differentiate parts of the rendered axis.<br /> 239 * <br /> 240 * Class name suffixes used internally are: 241 * <dl> 242 * <dt>path</dt> 243 * <dd>axis ticks</dd> 244 * <dt>pathdomain</dt> 245 * <dd>axis domain line (and end-ticks)</dd> 246 * </dl> 247 * 248 * So, for example, if you specified a class name prefix <code>xaxis_</code>, you would want to specify CSS selectors, <code>.xaxis_path</code> and <code>.xaxis_pathdomain</code> 249 * 250 * @param {String} val 251 * @return {D3RaphaelAxis} this 252 * 253 * @function 254 * @name D3RaphaelAxis#classPrefix 255 */ 256 axis.classPrefix = function(val) { 257 if(typeof val === "undefined") 258 return classPrefix; 259 else 260 classPrefix = val; 261 262 return this; 263 } 264 265 return axis; 266 };