text: //copy codes from d3.js, add 4 functions: tickAttr, tickTextAttr, minorTickAttr and domainAttr;
  //axis() changes, need a raphael paper object param, return raphael set object.
  //examples in ../examples/axis/ to know the usage.
  //a basic part for other data visualization format
  /*global d3*/
  /*!
   * Axis兼容定义
   */
  ;(function (name, definition) {
      if (typeof define === 'function') { // Module
          define(definition);
      } else { // Assign to common namespaces or simply the global object (window)
          this[name] = definition(function (id) { return this[id];});
      }
  })('Axis', function (require) {
      /**
       * function from d3, get scaleRange of an ordinal scale
       * @param {Array} domain ordinal scale's range
       */
      function d3_scaleExtent(domain) {
          var start = domain[0], stop = domain[domain.length - 1];
          return start < stop ? [start, stop] : [stop, start];
      }
  
      /**
       * function from d3, get scaleRange of a scale
       */
      function d3_scaleRange(scale) {
          return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range());
      }
  
      /**
       * function from d3, get subticks
       * @param scale, scale
       * @param ticks, major ticks of scale
       * @param m, number of subdivide
       */
      function d3_svg_axisSubdivide(scale, ticks, m) {
          var subticks = [];
          if (m && ticks.length > 1) {
              var extent = d3_scaleExtent(scale.domain()),
                  i = -1,
                  n = ticks.length,
                  d = (ticks[1] - ticks[0]) / ++m,
                  j,
                  v;
              while (++i < n) {
                  for (j = m; --j > 0;) {
                      if ((v = +ticks[i] - j * d) >= extent[0]) {
                          subticks.push(v);
                      }
                  }
              }
              for (--i, j = 0; ++j < m && (v = +ticks[i] + j * d) < extent[1];) {
                  subticks.push(v);
              }
          }
          return subticks;
      }
  
      var Axis = function () {
          var scale = d3.scale.linear(),
              orient = "bottom",
              tickMajorSize = 6,
              tickMinorSize = 6,
              tickEndSize = 6,
              tickPadding = 3,
              tickArguments_ = [10],
              tickFormat_,
              tickSubdivide = 0,
  
              tickAttr_ = {},
              tickTextAttr_ = {},
              minorTickAttr_ = {},
              domainAttr_ = {};
        
          /**
           * @param paper: raphael's paper object.
           * @return axisSet: raphael's set object.
           */
          function axis(paper) {
              // Ticks for quantitative scale, or domain values for ordinal scale.
              var ticks = scale.ticks ? scale.ticks.apply(scale, tickArguments_) : scale.domain(),
                  tickFormat = tickFormat_ === undefined ?
                      (scale.tickFormat ?
                          scale.tickFormat.apply(scale, tickArguments_)
                          : String)
                      : tickFormat_;
  
              var subticks = d3_svg_axisSubdivide(scale, ticks, tickSubdivide);
              var range = d3_scaleRange(scale);
          
              var axisSet = paper.set();
  
              switch (orient) {
              case "bottom":
                  subticks.forEach(function (d, i, arr) {
                      var tickX = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
                      axisSet.push(paper
                          .path("M" + tickX + "," + tickMinorSize + "V0")
                          .attr(minorTickAttr_));
                  });
                  ticks.forEach(function (d, i, arr) {
                      var tickX = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
                      axisSet.push(paper
                          .path("M" + tickX + "," + tickMajorSize + "V0")
                          .attr(tickAttr_));
                      axisSet.push(paper
                          .text(tickX,  Math.max(tickMajorSize, 0) + tickPadding + 2,
                              typeof tickFormat === "function" ? tickFormat(d) : tickFormat)
                          .attr({"text-anchor": "middle"})
                          .attr(tickTextAttr_));
                  });
                  axisSet.push(paper
                      .path("M" + range[0] + "," + tickEndSize + "V0H" + range[1] + "V" + tickEndSize)
                      .attr(domainAttr_));
                  break;
  
              case "top":
                  subticks.forEach(function (d, i, arr) {
                      var tickX = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
                      axisSet.push(paper
                          .path("M" + tickX + "," + -tickMinorSize + "V0")
                          .attr(minorTickAttr_));
                  });
                  ticks.forEach(function (d, i, arr) {
                      var tickX = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
                      axisSet.push(paper
                          .path("M" + tickX + "," + -tickMajorSize + "V0")
                          .attr(tickAttr_));
                      axisSet.push(paper
                          .text(tickX,  -(Math.max(tickMajorSize, 0) + tickPadding + 2),
                              typeof tickFormat === "function" ? tickFormat(d) : tickFormat)
                          .attr({"text-anchor": "middle"})
                          .attr(tickTextAttr_));
                  });
                  axisSet.push(paper
                      .path("M" + range[0] + "," + -tickEndSize + "V0H" + range[1] + "V" + -tickEndSize)
                      .attr(domainAttr_));
                  break;
  
              case "left":
                  subticks.forEach(function (d, i, arr) {
                      var tickY = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
                      axisSet.push(paper
                          .path("M" + -tickMinorSize + "," + tickY + "H0")
                          .attr(minorTickAttr_));
                  });
                  ticks.forEach(function (d, i, arr) {
                      var tickY = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
                      axisSet.push(paper
                          .path("M" + -tickMajorSize + "," + tickY + "H0")
                          .attr(tickAttr_));
                      axisSet.push(paper
                          .text(-(Math.max(tickMajorSize, 0) + tickPadding),  tickY,
                              typeof tickFormat === "function" ? tickFormat(d) : tickFormat)
                          .attr({"text-anchor": "end"})
                          .attr(tickTextAttr_));
                  });
                  axisSet.push(paper
                      .path("M" + -tickEndSize + "," + range[0] + "H0V" + range[1] + "H" + -tickEndSize)
                      .attr(domainAttr_));
                  break;
  
              case "right":
                  subticks.forEach(function (d, i, arr) {
                      var tickY = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
                      axisSet.push(paper
                          .path("M" + tickMinorSize + "," + tickY + "H0")
                          .attr(minorTickAttr_));
                  });
                  ticks.forEach(function (d, i, arr) {
                      var tickY = scale.ticks ? scale(d) : scale(d) + scale.rangeBand() / 2;
                      axisSet.push(paper
                          .path("M" + tickMajorSize + "," + tickY + "H0")
                          .attr(tickAttr_));
                      axisSet.push(paper
                          .text(Math.max(tickMajorSize, 0) + tickPadding,  tickY,
                              typeof tickFormat === "function" ? tickFormat(d) : tickFormat)
                          .attr({"text-anchor": "start"})
                          .attr(tickTextAttr_));
                  });
                  axisSet.push(paper
                      .path("M" + tickEndSize + "," + range[0] + "H0V" + range[1] + "H" + tickEndSize)
                      .attr(domainAttr_));
                  break;
              }
  
              return axisSet;
          }
        
          /**
           * get or set axis' scale.
           */
          axis.scale = function (x) {
              if (!arguments.length) {
                  return scale;
              }
              scale = x;
              return axis;
          };
        
          /**
           * get or set axis' orinet: "bottom", "top", "left", "right", default orient is bottom.
           */
          axis.orient = function (x) {
              if (!arguments.length) {
                  return orient;
              }
              orient = x;
              return axis;
          };
        
          /**
           * get or set axis' ticks number.
           */
          axis.ticks = function () {
              if (!arguments.length) {
                  return tickArguments_;
              }
              tickArguments_ = arguments;
              return axis;
          };
        
          /**
           * get or set axis' ticks format function, it's a function change format style.
           * from one string format to another string format.
           */
          axis.tickFormat = function (x) {
              if (!arguments.length) {
                  return tickFormat_;
              }
              tickFormat_ = x;
              return axis;
          };
        
          /**
           * get or set axis' tick size(length of tick line, unit: px).
           * @param arguments.length === 0, get axis' major tick size.
           * @param arguments.length === 1, set axis' all tick sizes as x.
           * @param arguments.length === 2, get axis' major tick size as x, minor and end size as y.
           * @param arguments.length === 3, get axis' major tick size as x, minor size as y, end size as z.
           */
          axis.tickSize = function (x, y, z) {
              if (!arguments.length) {
                  return tickMajorSize;
              }
              var n = arguments.length - 1;
              tickMajorSize = +x;
              tickMinorSize = n > 1 ? +y : tickMajorSize;
              tickEndSize = n > 0 ? +arguments[n] : tickMajorSize;
              return axis;
          };
        
          /**
           * get or set axis' tick padding(the distance between tick text and axis).
           * @param x is a number, unit is px;
           */
          axis.tickPadding = function (x) {
              if (!arguments.length) {
                  return tickPadding;
              }
              tickPadding = +x;
              return axis;
          };
  
          /**
           * get or set axis' sub tick divide number(divide number between two major ticks).
           */
          axis.tickSubdivide = function (x) {
              if (!arguments.length) {
                  return tickSubdivide;
              }
              tickSubdivide = +x;
              return axis;
          };
  
          /**
           * get or set axis' tick attribute(Raphael format).
           */
          axis.tickAttr = function (x) {
              if (!arguments.length) {
                  return tickAttr_;
              }
              tickAttr_ = x;
              return axis;
          };
        
          /**
           * get or set axis' tick text attribute(Raphael format).
           */
          axis.tickTextAttr = function (x) {
              if (!arguments.length) {
                  return tickTextAttr_;
              }
              tickTextAttr_ = x;
              return axis;
          };
  
          /**
           * get or set axis' minor tick attribute(Raphael format).
           */
          axis.minorTickAttr = function (x) {
              if (!arguments.length) {
                  return minorTickAttr_;
              }
              minorTickAttr_ = x;
              return axis;
          };
        
          /**
           * get or set axis' domain(axis line) attribute(Raphael format).
           */
          axis.domainAttr = function (x) {
              if (!arguments.length) {
                  return domainAttr_;
              }
              domainAttr_ = x;
              return axis;
          };
        
          return axis;
      };
  
      return Axis;
  });
  