github.com/matm/etcd@v0.3.1-0.20140328024009-5b4a473f1453/mod/dashboard/app/scripts/vega.js (about)

     1  vg = (function(d3, topojson) { // take d3 & topojson as imports
     2    var vg = {
     3      version:  "1.3.2", // semantic versioning
     4      d3:       d3,      // stash d3 for use in property functions
     5      topojson: topojson // stash topojson similarly
     6    };
     7  // type checking functions
     8  var toString = Object.prototype.toString;
     9  
    10  vg.isObject = function(obj) {
    11    return obj === Object(obj);
    12  };
    13  
    14  vg.isFunction = function(obj) {
    15    return toString.call(obj) == '[object Function]';
    16  };
    17  
    18  vg.isString = function(obj) {
    19    return toString.call(obj) == '[object String]';
    20  };
    21    
    22  vg.isArray = Array.isArray || function(obj) {
    23    return toString.call(obj) == '[object Array]';
    24  };
    25  
    26  vg.isNumber = function(obj) {
    27    return toString.call(obj) == '[object Number]';
    28  };
    29  
    30  vg.isBoolean = function(obj) {
    31    return toString.call(obj) == '[object Boolean]';
    32  };
    33  
    34  vg.isTree = function(obj) {
    35    return vg.isArray(obj) && obj.__vgtree__;
    36  };
    37  
    38  vg.number = function(s) { return +s; };
    39  
    40  vg.boolean = function(s) { return !!s; };
    41  
    42  // utility functions
    43  
    44  vg.identity = function(x) { return x; };
    45  
    46  vg.extend = function(obj) {
    47    for (var x, name, i=1, len=arguments.length; i<len; ++i) {
    48      x = arguments[i];
    49      for (name in x) { obj[name] = x[name]; }
    50    }
    51    return obj;
    52  };
    53  
    54  vg.duplicate = function(obj) {
    55    return JSON.parse(JSON.stringify(obj));
    56  };
    57  
    58  vg.field = function(f) {
    59    return f.split("\\.")
    60      .map(function(d) { return d.split("."); })
    61      .reduce(function(a, b) {
    62        if (a.length) { a[a.length-1] += "." + b.shift(); }
    63        a.push.apply(a, b);
    64        return a;
    65      }, []);
    66  };
    67  
    68  vg.accessor = function(f) {
    69    var s;
    70    return (vg.isFunction(f) || f==null)
    71      ? f : vg.isString(f) && (s=vg.field(f)).length > 1
    72      ? function(x) { return s.reduce(function(x,f) { return x[f]; }, x); }
    73      : function(x) { return x[f]; };
    74  };
    75  
    76  vg.comparator = function(sort) {
    77    var sign = [];
    78    if (sort === undefined) sort = [];
    79    sort = vg.array(sort).map(function(f) {
    80      var s = 1;
    81      if      (f[0] === "-") { s = -1; f = f.slice(1); }
    82      else if (f[0] === "+") { s = +1; f = f.slice(1); }
    83      sign.push(s);
    84      return vg.accessor(f);
    85    });
    86    return function(a,b) {
    87      var i, n, f, x, y;
    88      for (i=0, n=sort.length; i<n; ++i) {
    89        f = sort[i]; x = f(a); y = f(b);
    90        if (x < y) return -1 * sign[i];
    91        if (x > y) return sign[i];
    92      }
    93      return 0;
    94    };
    95  };
    96  
    97  vg.cmp = function(a, b) { return a<b ? -1 : a>b ? 1 : 0; };
    98  
    99  vg.numcmp = function(a, b) { return a - b; };
   100  
   101  vg.array = function(x) {
   102    return x != null ? (vg.isArray(x) ? x : [x]) : [];
   103  };
   104  
   105  vg.values = function(x) {
   106    return (vg.isObject(x) && !vg.isArray(x) && x.values) ? x.values : x;
   107  };
   108  
   109  vg.str = function(x) {
   110    return vg.isArray(x) ? "[" + x.map(vg.str) + "]"
   111      : vg.isObject(x) ? JSON.stringify(x)
   112      : vg.isString(x) ? ("'"+vg_escape_str(x)+"'") : x;
   113  };
   114  
   115  var escape_str_re = /(^|[^\\])'/g;
   116  
   117  function vg_escape_str(x) {
   118    return x.replace(escape_str_re, "$1\\'");
   119  }
   120  
   121  vg.keys = function(x) {
   122    var keys = [];
   123    for (var key in x) keys.push(key);
   124    return keys;
   125  };
   126  
   127  vg.unique = function(data, f, results) {
   128    if (!vg.isArray(data) || data.length==0) return [];
   129    f = f || vg.identity;
   130    results = results || [];
   131    for (var v, i=0, n=data.length; i<n; ++i) {
   132      v = f(data[i]);
   133      if (results.indexOf(v) < 0) results.push(v);
   134    }
   135    return results;
   136  };
   137  
   138  vg.minIndex = function(data, f) {
   139    if (!vg.isArray(data) || data.length==0) return -1;
   140    f = f || vg.identity;
   141    var idx = 0, min = f(data[0]), v = min;
   142    for (var i=1, n=data.length; i<n; ++i) {
   143      v = f(data[i]);
   144      if (v < min) { min = v; idx = i; }
   145    }
   146    return idx;
   147  };
   148  
   149  vg.maxIndex = function(data, f) {
   150    if (!vg.isArray(data) || data.length==0) return -1;
   151    f = f || vg.identity;
   152    var idx = 0, max = f(data[0]), v = max;
   153    for (var i=1, n=data.length; i<n; ++i) {
   154      v = f(data[i]);
   155      if (v > max) { max = v; idx = i; }
   156    }
   157    return idx;
   158  };
   159  
   160  vg.truncate = function(s, length, pos, word, ellipsis) {
   161    var len = s.length;
   162    if (len <= length) return s;
   163    ellipsis = ellipsis || "...";
   164    var l = Math.max(0, length - ellipsis.length);
   165  
   166    switch (pos) {
   167      case "left":
   168        return ellipsis + (word ? vg_truncateOnWord(s,l,1) : s.slice(len-l));
   169      case "middle":
   170      case "center":
   171        var l1 = Math.ceil(l/2), l2 = Math.floor(l/2);
   172        return (word ? vg_truncateOnWord(s,l1) : s.slice(0,l1)) + ellipsis
   173          + (word ? vg_truncateOnWord(s,l2,1) : s.slice(len-l2));
   174      default:
   175        return (word ? vg_truncateOnWord(s,l) : s.slice(0,l)) + ellipsis;
   176    }
   177  }
   178  
   179  function vg_truncateOnWord(s, len, rev) {
   180    var cnt = 0, tok = s.split(vg_truncate_word_re);
   181    if (rev) {
   182      s = (tok = tok.reverse())
   183        .filter(function(w) { cnt += w.length; return cnt <= len; })
   184        .reverse();
   185    } else {
   186      s = tok.filter(function(w) { cnt += w.length; return cnt <= len; });
   187    }
   188    return s.length ? s.join("").trim() : tok[0].slice(0, len);
   189  }
   190  
   191  var vg_truncate_word_re = /([\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u2028\u2029\u3000\uFEFF])/;
   192  
   193  // Logging
   194  
   195  function vg_write(msg) {
   196    vg.config.isNode
   197      ? process.stderr.write(msg + "\n")
   198      : console.log(msg);
   199  }
   200  
   201  vg.log = function(msg) {
   202    vg_write("[Vega Log] " + msg);
   203  };
   204  
   205  vg.error = function(msg) {
   206    msg = "[Vega Err] " + msg;
   207    vg_write(msg);
   208    if (typeof alert !== "undefined") alert(msg);
   209  };vg.config = {};
   210  
   211  // are we running in node.js?
   212  // via timetler.com/2012/10/13/environment-detection-in-javascript/
   213  vg.config.isNode = typeof exports !== 'undefined' && this.exports !== exports;
   214  
   215  // base url for loading external data files
   216  // used only for server-side operation
   217  vg.config.baseURL = "";
   218  
   219  // version and namepsaces for exported svg
   220  vg.config.svgNamespace =
   221    'version="1.1" xmlns="http://www.w3.org/2000/svg" ' +
   222    'xmlns:xlink="http://www.w3.org/1999/xlink"';
   223  
   224  // inset padding for automatic padding calculation
   225  vg.config.autopadInset = 5;
   226  
   227  // extensible scale lookup table
   228  // all d3.scale.* instances also supported
   229  vg.config.scale = {
   230    time: d3.time.scale,
   231    utc:  d3.time.scale.utc
   232  };
   233  
   234  // default rendering settings
   235  vg.config.render = {
   236    lineWidth: 1,
   237    lineCap:   "butt",
   238    font:      "sans-serif",
   239    fontSize:  11
   240  };
   241  
   242  // default axis properties
   243  vg.config.axis = {
   244    orient: "bottom",
   245    ticks: 10,
   246    padding: 3,
   247    axisColor: "#000",
   248    gridColor: "#d8d8d8",
   249    tickColor: "#000",
   250    tickLabelColor: "#000",
   251    axisWidth: 1,
   252    tickWidth: 1,
   253    tickSize: 6,
   254    tickLabelFontSize: 11,
   255    tickLabelFont: "sans-serif",
   256    titleColor: "#000",
   257    titleFont: "sans-serif",
   258    titleFontSize: 11,
   259    titleFontWeight: "bold",
   260    titleOffset: 35
   261  };
   262  
   263  // default legend properties
   264  vg.config.legend = {
   265    orient: "right",
   266    offset: 10,
   267    padding: 3,
   268    gradientStrokeColor: "#888",
   269    gradientStrokeWidth: 1,
   270    gradientHeight: 16,
   271    gradientWidth: 100,
   272    labelColor: "#000",
   273    labelFontSize: 10,
   274    labelFont: "sans-serif",
   275    labelAlign: "left",
   276    labelBaseline: "middle",
   277    labelOffset: 8,
   278    symbolShape: "circle",
   279    symbolSize: 50,
   280    symbolColor: "#888",
   281    symbolStrokeWidth: 1,
   282    titleColor: "#000",
   283    titleFont: "sans-serif",
   284    titleFontSize: 11,
   285    titleFontWeight: "bold"
   286  };
   287  
   288  // default color values
   289  vg.config.color = {
   290    rgb: [128, 128, 128],
   291    lab: [50, 0, 0],
   292    hcl: [0, 0, 50],
   293    hsl: [0, 0, 0.5]
   294  };
   295  
   296  // default scale ranges
   297  vg.config.range = {
   298    category10: [
   299      "#1f77b4",
   300      "#ff7f0e",
   301      "#2ca02c",
   302      "#d62728",
   303      "#9467bd",
   304      "#8c564b",
   305      "#e377c2",
   306      "#7f7f7f",
   307      "#bcbd22",
   308      "#17becf"
   309    ],
   310    category20: [
   311      "#1f77b4",
   312      "#aec7e8",
   313      "#ff7f0e",
   314      "#ffbb78",
   315      "#2ca02c",
   316      "#98df8a",
   317      "#d62728",
   318      "#ff9896",
   319      "#9467bd",
   320      "#c5b0d5",
   321      "#8c564b",
   322      "#c49c94",
   323      "#e377c2",
   324      "#f7b6d2",
   325      "#7f7f7f",
   326      "#c7c7c7",
   327      "#bcbd22",
   328      "#dbdb8d",
   329      "#17becf",
   330      "#9edae5"
   331    ],
   332    shapes: [
   333      "circle",
   334      "cross",
   335      "diamond",
   336      "square",
   337      "triangle-down",
   338      "triangle-up"
   339    ]
   340  };vg.Bounds = (function() {
   341    var bounds = function(b) {
   342      this.clear();
   343      if (b) this.union(b);
   344    };
   345    
   346    var prototype = bounds.prototype;
   347    
   348    prototype.clear = function() {
   349      this.x1 = +Number.MAX_VALUE;
   350      this.y1 = +Number.MAX_VALUE;
   351      this.x2 = -Number.MAX_VALUE;
   352      this.y2 = -Number.MAX_VALUE;
   353      return this;
   354    };
   355    
   356    prototype.set = function(x1, y1, x2, y2) {
   357      this.x1 = x1;
   358      this.y1 = y1;
   359      this.x2 = x2;
   360      this.y2 = y2;
   361      return this;
   362    };
   363  
   364    prototype.add = function(x, y) {
   365      if (x < this.x1) this.x1 = x;
   366      if (y < this.y1) this.y1 = y;
   367      if (x > this.x2) this.x2 = x;
   368      if (y > this.y2) this.y2 = y;
   369      return this;
   370    };
   371  
   372    prototype.expand = function(d) {
   373      this.x1 -= d;
   374      this.y1 -= d;
   375      this.x2 += d;
   376      this.y2 += d;
   377      return this;
   378    };
   379    
   380    prototype.round = function() {
   381      this.x1 = Math.floor(this.x1);
   382      this.y1 = Math.floor(this.y1);
   383      this.x2 = Math.ceil(this.x2);
   384      this.y2 = Math.ceil(this.y2);
   385      return this;
   386    };
   387  
   388    prototype.translate = function(dx, dy) {
   389      this.x1 += dx;
   390      this.x2 += dx;
   391      this.y1 += dy;
   392      this.y2 += dy;
   393      return this;
   394    };
   395    
   396    prototype.rotate = function(angle, x, y) {
   397      var cos = Math.cos(angle),
   398          sin = Math.sin(angle),
   399          cx = x - x*cos + y*sin,
   400          cy = y - x*sin - y*cos,
   401          x1 = this.x1, x2 = this.x2,
   402          y1 = this.y1, y2 = this.y2;
   403  
   404      return this.clear()
   405        .add(cos*x1 - sin*y1 + cx,  sin*x1 + cos*y1 + cy)
   406        .add(cos*x1 - sin*y2 + cx,  sin*x1 + cos*y2 + cy)
   407        .add(cos*x2 - sin*y1 + cx,  sin*x2 + cos*y1 + cy)
   408        .add(cos*x2 - sin*y2 + cx,  sin*x2 + cos*y2 + cy);
   409    }
   410  
   411    prototype.union = function(b) {
   412      if (b.x1 < this.x1) this.x1 = b.x1;
   413      if (b.y1 < this.y1) this.y1 = b.y1;
   414      if (b.x2 > this.x2) this.x2 = b.x2;
   415      if (b.y2 > this.y2) this.y2 = b.y2;
   416      return this;
   417    };
   418  
   419    prototype.encloses = function(b) {
   420      return b && (
   421        this.x1 <= b.x1 &&
   422        this.x2 >= b.x2 &&
   423        this.y1 <= b.y1 &&
   424        this.y2 >= b.y2
   425      );
   426    };
   427  
   428    prototype.intersects = function(b) {
   429      return b && !(
   430        this.x2 < b.x1 ||
   431        this.x1 > b.x2 ||
   432        this.y2 < b.y1 ||
   433        this.y1 > b.y2
   434      );
   435    };
   436  
   437    prototype.contains = function(x, y) {
   438      return !(
   439        x < this.x1 ||
   440        x > this.x2 ||
   441        y < this.y1 ||
   442        y > this.y2
   443      );
   444    };
   445  
   446    prototype.width = function() {
   447      return this.x2 - this.x1;
   448    };
   449  
   450    prototype.height = function() {
   451      return this.y2 - this.y1;
   452    };
   453  
   454    return bounds;
   455  })();vg.Gradient = (function() {
   456  
   457    function gradient(type) {
   458      this.id = "grad_" + (vg_gradient_id++);
   459      this.type = type || "linear";
   460      this.stops = [];
   461      this.x1 = 0;
   462      this.x2 = 1;
   463      this.y1 = 0;
   464      this.y2 = 0;
   465    };
   466  
   467    var prototype = gradient.prototype;
   468  
   469    prototype.stop = function(offset, color) {
   470      this.stops.push({
   471        offset: offset,
   472        color: color
   473      });
   474      return this;
   475    };
   476    
   477    return gradient;
   478  })();
   479  
   480  var vg_gradient_id = 0;vg.canvas = {};vg.canvas.path = (function() {
   481  
   482    // Path parsing and rendering code taken from fabric.js -- Thanks!
   483    var cmdLength = { m:2, l:2, h:1, v:1, c:6, s:4, q:4, t:2, a:7 },
   484        re = [/([MLHVCSQTAZmlhvcsqtaz])/g, /###/, /(\d)-/g, /\s|,|###/];
   485  
   486    function parse(path) {
   487      var result = [],
   488          currentPath,
   489          chunks,
   490          parsed;
   491  
   492      // First, break path into command sequence
   493      path = path.slice().replace(re[0], '###$1').split(re[1]).slice(1);
   494  
   495      // Next, parse each command in turn
   496      for (var i=0, j, chunksParsed, len=path.length; i<len; i++) {
   497        currentPath = path[i];
   498        chunks = currentPath.slice(1).trim().replace(re[2],'$1###-').split(re[3]);
   499        chunksParsed = [currentPath.charAt(0)];
   500  
   501        for (var j = 0, jlen = chunks.length; j < jlen; j++) {
   502          parsed = parseFloat(chunks[j]);
   503          if (!isNaN(parsed)) {
   504            chunksParsed.push(parsed);
   505          }
   506        }
   507  
   508        var command = chunksParsed[0].toLowerCase(),
   509            commandLength = cmdLength[command];
   510  
   511        if (chunksParsed.length - 1 > commandLength) {
   512          for (var k = 1, klen = chunksParsed.length; k < klen; k += commandLength) {
   513            result.push([ chunksParsed[0] ].concat(chunksParsed.slice(k, k + commandLength)));
   514          }
   515        }
   516        else {
   517          result.push(chunksParsed);
   518        }
   519      }
   520  
   521      return result;
   522    }
   523  
   524    function drawArc(g, x, y, coords, bounds, l, t) {
   525      var rx = coords[0];
   526      var ry = coords[1];
   527      var rot = coords[2];
   528      var large = coords[3];
   529      var sweep = coords[4];
   530      var ex = coords[5];
   531      var ey = coords[6];
   532      var segs = arcToSegments(ex, ey, rx, ry, large, sweep, rot, x, y);
   533      for (var i=0; i<segs.length; i++) {
   534        var bez = segmentToBezier.apply(null, segs[i]);
   535        g.bezierCurveTo.apply(g, bez);
   536        bounds.add(bez[0]-l, bez[1]-t);
   537        bounds.add(bez[2]-l, bez[3]-t);
   538        bounds.add(bez[4]-l, bez[5]-t);
   539      }
   540    }
   541  
   542    function boundArc(x, y, coords, bounds) {
   543      var rx = coords[0];
   544      var ry = coords[1];
   545      var rot = coords[2];
   546      var large = coords[3];
   547      var sweep = coords[4];
   548      var ex = coords[5];
   549      var ey = coords[6];
   550      var segs = arcToSegments(ex, ey, rx, ry, large, sweep, rot, x, y);
   551      for (var i=0; i<segs.length; i++) {
   552        var bez = segmentToBezier.apply(null, segs[i]);
   553        bounds.add(bez[0]-l, bez[1]-t);
   554        bounds.add(bez[2]-l, bez[3]-t);
   555        bounds.add(bez[4]-l, bez[5]-t);
   556      }
   557    }
   558  
   559    var arcToSegmentsCache = { },
   560        segmentToBezierCache = { },
   561        join = Array.prototype.join,
   562        argsStr;
   563  
   564    // Copied from Inkscape svgtopdf, thanks!
   565    function arcToSegments(x, y, rx, ry, large, sweep, rotateX, ox, oy) {
   566      argsStr = join.call(arguments);
   567      if (arcToSegmentsCache[argsStr]) {
   568        return arcToSegmentsCache[argsStr];
   569      }
   570  
   571      var th = rotateX * (Math.PI/180);
   572      var sin_th = Math.sin(th);
   573      var cos_th = Math.cos(th);
   574      rx = Math.abs(rx);
   575      ry = Math.abs(ry);
   576      var px = cos_th * (ox - x) * 0.5 + sin_th * (oy - y) * 0.5;
   577      var py = cos_th * (oy - y) * 0.5 - sin_th * (ox - x) * 0.5;
   578      var pl = (px*px) / (rx*rx) + (py*py) / (ry*ry);
   579      if (pl > 1) {
   580        pl = Math.sqrt(pl);
   581        rx *= pl;
   582        ry *= pl;
   583      }
   584  
   585      var a00 = cos_th / rx;
   586      var a01 = sin_th / rx;
   587      var a10 = (-sin_th) / ry;
   588      var a11 = (cos_th) / ry;
   589      var x0 = a00 * ox + a01 * oy;
   590      var y0 = a10 * ox + a11 * oy;
   591      var x1 = a00 * x + a01 * y;
   592      var y1 = a10 * x + a11 * y;
   593  
   594      var d = (x1-x0) * (x1-x0) + (y1-y0) * (y1-y0);
   595      var sfactor_sq = 1 / d - 0.25;
   596      if (sfactor_sq < 0) sfactor_sq = 0;
   597      var sfactor = Math.sqrt(sfactor_sq);
   598      if (sweep == large) sfactor = -sfactor;
   599      var xc = 0.5 * (x0 + x1) - sfactor * (y1-y0);
   600      var yc = 0.5 * (y0 + y1) + sfactor * (x1-x0);
   601  
   602      var th0 = Math.atan2(y0-yc, x0-xc);
   603      var th1 = Math.atan2(y1-yc, x1-xc);
   604  
   605      var th_arc = th1-th0;
   606      if (th_arc < 0 && sweep == 1){
   607        th_arc += 2*Math.PI;
   608      } else if (th_arc > 0 && sweep == 0) {
   609        th_arc -= 2 * Math.PI;
   610      }
   611  
   612      var segments = Math.ceil(Math.abs(th_arc / (Math.PI * 0.5 + 0.001)));
   613      var result = [];
   614      for (var i=0; i<segments; i++) {
   615        var th2 = th0 + i * th_arc / segments;
   616        var th3 = th0 + (i+1) * th_arc / segments;
   617        result[i] = [xc, yc, th2, th3, rx, ry, sin_th, cos_th];
   618      }
   619  
   620      return (arcToSegmentsCache[argsStr] = result);
   621    }
   622  
   623    function segmentToBezier(cx, cy, th0, th1, rx, ry, sin_th, cos_th) {
   624      argsStr = join.call(arguments);
   625      if (segmentToBezierCache[argsStr]) {
   626        return segmentToBezierCache[argsStr];
   627      }
   628  
   629      var a00 = cos_th * rx;
   630      var a01 = -sin_th * ry;
   631      var a10 = sin_th * rx;
   632      var a11 = cos_th * ry;
   633  
   634      var cos_th0 = Math.cos(th0);
   635      var sin_th0 = Math.sin(th0);
   636      var cos_th1 = Math.cos(th1);
   637      var sin_th1 = Math.sin(th1);
   638  
   639      var th_half = 0.5 * (th1 - th0);
   640      var sin_th_h2 = Math.sin(th_half * 0.5);
   641      var t = (8/3) * sin_th_h2 * sin_th_h2 / Math.sin(th_half);
   642      var x1 = cx + cos_th0 - t * sin_th0;
   643      var y1 = cy + sin_th0 + t * cos_th0;
   644      var x3 = cx + cos_th1;
   645      var y3 = cy + sin_th1;
   646      var x2 = x3 + t * sin_th1;
   647      var y2 = y3 - t * cos_th1;
   648  
   649      return (segmentToBezierCache[argsStr] = [
   650        a00 * x1 + a01 * y1,  a10 * x1 + a11 * y1,
   651        a00 * x2 + a01 * y2,  a10 * x2 + a11 * y2,
   652        a00 * x3 + a01 * y3,  a10 * x3 + a11 * y3
   653      ]);
   654    }
   655  
   656    function render(g, path, l, t) {
   657      var current, // current instruction
   658          previous = null,
   659          x = 0, // current x
   660          y = 0, // current y
   661          controlX = 0, // current control point x
   662          controlY = 0, // current control point y
   663          tempX,
   664          tempY,
   665          tempControlX,
   666          tempControlY,
   667          bounds = new vg.Bounds();
   668      if (l == undefined) l = 0;
   669      if (t == undefined) t = 0;
   670  
   671      g.beginPath();
   672    
   673      for (var i=0, len=path.length; i<len; ++i) {
   674        current = path[i];
   675  
   676        switch (current[0]) { // first letter
   677  
   678          case 'l': // lineto, relative
   679            x += current[1];
   680            y += current[2];
   681            g.lineTo(x + l, y + t);
   682            bounds.add(x, y);
   683            break;
   684  
   685          case 'L': // lineto, absolute
   686            x = current[1];
   687            y = current[2];
   688            g.lineTo(x + l, y + t);
   689            bounds.add(x, y);
   690            break;
   691  
   692          case 'h': // horizontal lineto, relative
   693            x += current[1];
   694            g.lineTo(x + l, y + t);
   695            bounds.add(x, y);
   696            break;
   697  
   698          case 'H': // horizontal lineto, absolute
   699            x = current[1];
   700            g.lineTo(x + l, y + t);
   701            bounds.add(x, y);
   702            break;
   703  
   704          case 'v': // vertical lineto, relative
   705            y += current[1];
   706            g.lineTo(x + l, y + t);
   707            bounds.add(x, y);
   708            break;
   709  
   710          case 'V': // verical lineto, absolute
   711            y = current[1];
   712            g.lineTo(x + l, y + t);
   713            bounds.add(x, y);
   714            break;
   715  
   716          case 'm': // moveTo, relative
   717            x += current[1];
   718            y += current[2];
   719            g.moveTo(x + l, y + t);
   720            bounds.add(x, y);
   721            break;
   722  
   723          case 'M': // moveTo, absolute
   724            x = current[1];
   725            y = current[2];
   726            g.moveTo(x + l, y + t);
   727            bounds.add(x, y);
   728            break;
   729  
   730          case 'c': // bezierCurveTo, relative
   731            tempX = x + current[5];
   732            tempY = y + current[6];
   733            controlX = x + current[3];
   734            controlY = y + current[4];
   735            g.bezierCurveTo(
   736              x + current[1] + l, // x1
   737              y + current[2] + t, // y1
   738              controlX + l, // x2
   739              controlY + t, // y2
   740              tempX + l,
   741              tempY + t
   742            );
   743            bounds.add(x + current[1], y + current[2]);
   744            bounds.add(controlX, controlY);
   745            bounds.add(tempX, tempY);
   746            x = tempX;
   747            y = tempY;
   748            break;
   749  
   750          case 'C': // bezierCurveTo, absolute
   751            x = current[5];
   752            y = current[6];
   753            controlX = current[3];
   754            controlY = current[4];
   755            g.bezierCurveTo(
   756              current[1] + l,
   757              current[2] + t,
   758              controlX + l,
   759              controlY + t,
   760              x + l,
   761              y + t
   762            );
   763            bounds.add(current[1], current[2]);
   764            bounds.add(controlX, controlY);
   765            bounds.add(x, y);
   766            break;
   767  
   768          case 's': // shorthand cubic bezierCurveTo, relative
   769            // transform to absolute x,y
   770            tempX = x + current[3];
   771            tempY = y + current[4];
   772            // calculate reflection of previous control points
   773            controlX = 2 * x - controlX;
   774            controlY = 2 * y - controlY;
   775            g.bezierCurveTo(
   776              controlX + l,
   777              controlY + t,
   778              x + current[1] + l,
   779              y + current[2] + t,
   780              tempX + l,
   781              tempY + t
   782            );
   783            bounds.add(controlX, controlY);
   784            bounds.add(x + current[1], y + current[2]);
   785            bounds.add(tempX, tempY);
   786  
   787            // set control point to 2nd one of this command
   788            // "... the first control point is assumed to be the reflection of the second control point on the previous command relative to the current point."
   789            controlX = x + current[1];
   790            controlY = y + current[2];
   791  
   792            x = tempX;
   793            y = tempY;
   794            break;
   795  
   796          case 'S': // shorthand cubic bezierCurveTo, absolute
   797            tempX = current[3];
   798            tempY = current[4];
   799            // calculate reflection of previous control points
   800            controlX = 2*x - controlX;
   801            controlY = 2*y - controlY;
   802            g.bezierCurveTo(
   803              controlX + l,
   804              controlY + t,
   805              current[1] + l,
   806              current[2] + t,
   807              tempX + l,
   808              tempY + t
   809            );
   810            x = tempX;
   811            y = tempY;
   812            bounds.add(current[1], current[2]);
   813            bounds.add(controlX, controlY);
   814            bounds.add(tempX, tempY);
   815            // set control point to 2nd one of this command
   816            // "... the first control point is assumed to be the reflection of the second control point on the previous command relative to the current point."
   817            controlX = current[1];
   818            controlY = current[2];
   819  
   820            break;
   821  
   822          case 'q': // quadraticCurveTo, relative
   823            // transform to absolute x,y
   824            tempX = x + current[3];
   825            tempY = y + current[4];
   826  
   827            controlX = x + current[1];
   828            controlY = y + current[2];
   829  
   830            g.quadraticCurveTo(
   831              controlX + l,
   832              controlY + t,
   833              tempX + l,
   834              tempY + t
   835            );
   836            x = tempX;
   837            y = tempY;
   838            bounds.add(controlX, controlY);
   839            bounds.add(tempX, tempY);
   840            break;
   841  
   842          case 'Q': // quadraticCurveTo, absolute
   843            tempX = current[3];
   844            tempY = current[4];
   845  
   846            g.quadraticCurveTo(
   847              current[1] + l,
   848              current[2] + t,
   849              tempX + l,
   850              tempY + t
   851            );
   852            x = tempX;
   853            y = tempY;
   854            controlX = current[1];
   855            controlY = current[2];
   856            bounds.add(controlX, controlY);
   857            bounds.add(tempX, tempY);
   858            break;
   859  
   860          case 't': // shorthand quadraticCurveTo, relative
   861  
   862            // transform to absolute x,y
   863            tempX = x + current[1];
   864            tempY = y + current[2];
   865  
   866            if (previous[0].match(/[QqTt]/) === null) {
   867              // If there is no previous command or if the previous command was not a Q, q, T or t,
   868              // assume the control point is coincident with the current point
   869              controlX = x;
   870              controlY = y;
   871            }
   872            else if (previous[0] === 't') {
   873              // calculate reflection of previous control points for t
   874              controlX = 2 * x - tempControlX;
   875              controlY = 2 * y - tempControlY;
   876            }
   877            else if (previous[0] === 'q') {
   878              // calculate reflection of previous control points for q
   879              controlX = 2 * x - controlX;
   880              controlY = 2 * y - controlY;
   881            }
   882  
   883            tempControlX = controlX;
   884            tempControlY = controlY;
   885  
   886            g.quadraticCurveTo(
   887              controlX + l,
   888              controlY + t,
   889              tempX + l,
   890              tempY + t
   891            );
   892            x = tempX;
   893            y = tempY;
   894            controlX = x + current[1];
   895            controlY = y + current[2];
   896            bounds.add(controlX, controlY);
   897            bounds.add(tempX, tempY);
   898            break;
   899  
   900          case 'T':
   901            tempX = current[1];
   902            tempY = current[2];
   903  
   904            // calculate reflection of previous control points
   905            controlX = 2 * x - controlX;
   906            controlY = 2 * y - controlY;
   907            g.quadraticCurveTo(
   908              controlX + l,
   909              controlY + t,
   910              tempX + l,
   911              tempY + t
   912            );
   913            x = tempX;
   914            y = tempY;
   915            bounds.add(controlX, controlY);
   916            bounds.add(tempX, tempY);
   917            break;
   918  
   919          case 'a':
   920            drawArc(g, x + l, y + t, [
   921              current[1],
   922              current[2],
   923              current[3],
   924              current[4],
   925              current[5],
   926              current[6] + x + l,
   927              current[7] + y + t
   928            ], bounds, l, t);
   929            x += current[6];
   930            y += current[7];
   931            break;
   932  
   933          case 'A':
   934            drawArc(g, x + l, y + t, [
   935              current[1],
   936              current[2],
   937              current[3],
   938              current[4],
   939              current[5],
   940              current[6] + l,
   941              current[7] + t
   942            ], bounds, l, t);
   943            x = current[6];
   944            y = current[7];
   945            break;
   946  
   947          case 'z':
   948          case 'Z':
   949            g.closePath();
   950            break;
   951        }
   952        previous = current;
   953      }
   954      return bounds.translate(l, t);
   955    }
   956  
   957    function bounds(path, bounds) {
   958      var current, // current instruction
   959          previous = null,
   960          x = 0, // current x
   961          y = 0, // current y
   962          controlX = 0, // current control point x
   963          controlY = 0, // current control point y
   964          tempX,
   965          tempY,
   966          tempControlX,
   967          tempControlY;
   968  
   969      for (var i=0, len=path.length; i<len; ++i) {
   970        current = path[i];
   971  
   972        switch (current[0]) { // first letter
   973  
   974          case 'l': // lineto, relative
   975            x += current[1];
   976            y += current[2];
   977            bounds.add(x, y);
   978            break;
   979  
   980          case 'L': // lineto, absolute
   981            x = current[1];
   982            y = current[2];
   983            bounds.add(x, y);
   984            break;
   985  
   986          case 'h': // horizontal lineto, relative
   987            x += current[1];
   988            bounds.add(x, y);
   989            break;
   990  
   991          case 'H': // horizontal lineto, absolute
   992            x = current[1];
   993            bounds.add(x, y);
   994            break;
   995  
   996          case 'v': // vertical lineto, relative
   997            y += current[1];
   998            bounds.add(x, y);
   999            break;
  1000  
  1001          case 'V': // verical lineto, absolute
  1002            y = current[1];
  1003            bounds.add(x, y);
  1004            break;
  1005  
  1006          case 'm': // moveTo, relative
  1007            x += current[1];
  1008            y += current[2];
  1009            bounds.add(x, y);
  1010            break;
  1011  
  1012          case 'M': // moveTo, absolute
  1013            x = current[1];
  1014            y = current[2];
  1015            bounds.add(x, y);
  1016            break;
  1017  
  1018          case 'c': // bezierCurveTo, relative
  1019            tempX = x + current[5];
  1020            tempY = y + current[6];
  1021            controlX = x + current[3];
  1022            controlY = y + current[4];
  1023            bounds.add(x + current[1], y + current[2]);
  1024            bounds.add(controlX, controlY);
  1025            bounds.add(tempX, tempY);
  1026            x = tempX;
  1027            y = tempY;
  1028            break;
  1029  
  1030          case 'C': // bezierCurveTo, absolute
  1031            x = current[5];
  1032            y = current[6];
  1033            controlX = current[3];
  1034            controlY = current[4];
  1035            bounds.add(current[1], current[2]);
  1036            bounds.add(controlX, controlY);
  1037            bounds.add(x, y);
  1038            break;
  1039  
  1040          case 's': // shorthand cubic bezierCurveTo, relative
  1041            // transform to absolute x,y
  1042            tempX = x + current[3];
  1043            tempY = y + current[4];
  1044            // calculate reflection of previous control points
  1045            controlX = 2 * x - controlX;
  1046            controlY = 2 * y - controlY;
  1047            bounds.add(controlX, controlY);
  1048            bounds.add(x + current[1], y + current[2]);
  1049            bounds.add(tempX, tempY);
  1050  
  1051            // set control point to 2nd one of this command
  1052            // "... the first control point is assumed to be the reflection of the second control point on the previous command relative to the current point."
  1053            controlX = x + current[1];
  1054            controlY = y + current[2];
  1055  
  1056            x = tempX;
  1057            y = tempY;
  1058            break;
  1059  
  1060          case 'S': // shorthand cubic bezierCurveTo, absolute
  1061            tempX = current[3];
  1062            tempY = current[4];
  1063            // calculate reflection of previous control points
  1064            controlX = 2*x - controlX;
  1065            controlY = 2*y - controlY;
  1066            x = tempX;
  1067            y = tempY;
  1068            bounds.add(current[1], current[2]);
  1069            bounds.add(controlX, controlY);
  1070            bounds.add(tempX, tempY);
  1071            // set control point to 2nd one of this command
  1072            // "... the first control point is assumed to be the reflection of the second control point on the previous command relative to the current point."
  1073            controlX = current[1];
  1074            controlY = current[2];
  1075  
  1076            break;
  1077  
  1078          case 'q': // quadraticCurveTo, relative
  1079            // transform to absolute x,y
  1080            tempX = x + current[3];
  1081            tempY = y + current[4];
  1082  
  1083            controlX = x + current[1];
  1084            controlY = y + current[2];
  1085  
  1086            x = tempX;
  1087            y = tempY;
  1088            bounds.add(controlX, controlY);
  1089            bounds.add(tempX, tempY);
  1090            break;
  1091  
  1092          case 'Q': // quadraticCurveTo, absolute
  1093            tempX = current[3];
  1094            tempY = current[4];
  1095  
  1096            x = tempX;
  1097            y = tempY;
  1098            controlX = current[1];
  1099            controlY = current[2];
  1100            bounds.add(controlX, controlY);
  1101            bounds.add(tempX, tempY);
  1102            break;
  1103  
  1104          case 't': // shorthand quadraticCurveTo, relative
  1105  
  1106            // transform to absolute x,y
  1107            tempX = x + current[1];
  1108            tempY = y + current[2];
  1109  
  1110            if (previous[0].match(/[QqTt]/) === null) {
  1111              // If there is no previous command or if the previous command was not a Q, q, T or t,
  1112              // assume the control point is coincident with the current point
  1113              controlX = x;
  1114              controlY = y;
  1115            }
  1116            else if (previous[0] === 't') {
  1117              // calculate reflection of previous control points for t
  1118              controlX = 2 * x - tempControlX;
  1119              controlY = 2 * y - tempControlY;
  1120            }
  1121            else if (previous[0] === 'q') {
  1122              // calculate reflection of previous control points for q
  1123              controlX = 2 * x - controlX;
  1124              controlY = 2 * y - controlY;
  1125            }
  1126  
  1127            tempControlX = controlX;
  1128            tempControlY = controlY;
  1129  
  1130            x = tempX;
  1131            y = tempY;
  1132            controlX = x + current[1];
  1133            controlY = y + current[2];
  1134            bounds.add(controlX, controlY);
  1135            bounds.add(tempX, tempY);
  1136            break;
  1137  
  1138          case 'T':
  1139            tempX = current[1];
  1140            tempY = current[2];
  1141  
  1142            // calculate reflection of previous control points
  1143            controlX = 2 * x - controlX;
  1144            controlY = 2 * y - controlY;
  1145  
  1146            x = tempX;
  1147            y = tempY;
  1148            bounds.add(controlX, controlY);
  1149            bounds.add(tempX, tempY);
  1150            break;
  1151  
  1152          case 'a':
  1153            boundArc(x, y, [
  1154              current[1],
  1155              current[2],
  1156              current[3],
  1157              current[4],
  1158              current[5],
  1159              current[6] + x,
  1160              current[7] + y
  1161            ], bounds);
  1162            x += current[6];
  1163            y += current[7];
  1164            break;
  1165  
  1166          case 'A':
  1167            boundArc(x, y, [
  1168              current[1],
  1169              current[2],
  1170              current[3],
  1171              current[4],
  1172              current[5],
  1173              current[6],
  1174              current[7]
  1175            ], bounds);
  1176            x = current[6];
  1177            y = current[7];
  1178            break;
  1179  
  1180          case 'z':
  1181          case 'Z':
  1182            break;
  1183        }
  1184        previous = current;
  1185      }
  1186      return bounds;
  1187    }
  1188    
  1189    function area(items) {
  1190      var o = items[0];
  1191      var area = d3.svg.area()
  1192        .x(function(d) { return d.x; })
  1193        .y1(function(d) { return d.y; })
  1194        .y0(function(d) { return d.y + d.height; });
  1195      if (o.interpolate) area.interpolate(o.interpolate);
  1196      if (o.tension != null) area.tension(o.tension);
  1197      return area(items);
  1198    }
  1199  
  1200    function line(items) {
  1201      var o = items[0];
  1202      var line = d3.svg.line()
  1203       .x(function(d) { return d.x; })
  1204       .y(function(d) { return d.y; });
  1205      if (o.interpolate) line.interpolate(o.interpolate);
  1206      if (o.tension != null) line.tension(o.tension);
  1207      return line(items);
  1208    }
  1209    
  1210    return {
  1211      parse:  parse,
  1212      render: render,
  1213      bounds: bounds,
  1214      area:   area,
  1215      line:   line
  1216    };
  1217    
  1218  })();vg.canvas.marks = (function() {
  1219  
  1220    var parsePath = vg.canvas.path.parse,
  1221        renderPath = vg.canvas.path.render,
  1222        halfpi = Math.PI / 2,
  1223        sqrt3 = Math.sqrt(3),
  1224        tan30 = Math.tan(30 * Math.PI / 180),
  1225        tmpBounds = new vg.Bounds();
  1226  
  1227    // path generators
  1228  
  1229    function arcPath(g, o) {
  1230      var x = o.x || 0,
  1231          y = o.y || 0,
  1232          ir = o.innerRadius || 0,
  1233          or = o.outerRadius || 0,
  1234          sa = (o.startAngle || 0) - Math.PI/2,
  1235          ea = (o.endAngle || 0) - Math.PI/2;
  1236      g.beginPath();
  1237      if (ir === 0) g.moveTo(x, y);
  1238      else g.arc(x, y, ir, sa, ea, 0);
  1239      g.arc(x, y, or, ea, sa, 1);
  1240      g.closePath();
  1241    }
  1242  
  1243    function pathPath(g, o) {
  1244      if (o.path == null) return;
  1245      if (!o["path:parsed"]) {
  1246        o["path:parsed"] = parsePath(o.path);
  1247      }
  1248      return renderPath(g, o["path:parsed"], o.x, o.y);
  1249    }
  1250  
  1251    function symbolPath(g, o) {
  1252      g.beginPath();
  1253      var size = o.size != null ? o.size : 100,
  1254          x = o.x, y = o.y, r, t, rx, ry;
  1255  
  1256      if (o.shape == null || o.shape === "circle") {
  1257        r = Math.sqrt(size/Math.PI);
  1258        g.arc(x, y, r, 0, 2*Math.PI, 0);
  1259        g.closePath();
  1260        return;
  1261      }
  1262  
  1263      switch (o.shape) {
  1264        case "cross":
  1265          r = Math.sqrt(size / 5) / 2;
  1266          t = 3*r;
  1267          g.moveTo(x-t, y-r);
  1268          g.lineTo(x-r, y-r);
  1269          g.lineTo(x-r, y-t);
  1270          g.lineTo(x+r, y-t);
  1271          g.lineTo(x+r, y-r);
  1272          g.lineTo(x+t, y-r);
  1273          g.lineTo(x+t, y+r);
  1274          g.lineTo(x+r, y+r);
  1275          g.lineTo(x+r, y+t);
  1276          g.lineTo(x-r, y+t);
  1277          g.lineTo(x-r, y+r);
  1278          g.lineTo(x-t, y+r);
  1279          break;
  1280  
  1281        case "diamond":
  1282          ry = Math.sqrt(size / (2 * tan30));
  1283          rx = ry * tan30;
  1284          g.moveTo(x, y-ry);
  1285          g.lineTo(x+rx, y);
  1286          g.lineTo(x, y+ry);
  1287          g.lineTo(x-rx, y);
  1288          break;
  1289  
  1290        case "square":
  1291          t = Math.sqrt(size);
  1292          r = t / 2;
  1293          g.rect(x-r, y-r, t, t);
  1294          break;
  1295  
  1296        case "triangle-down":
  1297          rx = Math.sqrt(size / sqrt3);
  1298          ry = rx * sqrt3 / 2;
  1299          g.moveTo(x, y+ry);
  1300          g.lineTo(x+rx, y-ry);
  1301          g.lineTo(x-rx, y-ry);
  1302          break;
  1303  
  1304        case "triangle-up":
  1305          rx = Math.sqrt(size / sqrt3);
  1306          ry = rx * sqrt3 / 2;
  1307          g.moveTo(x, y-ry);
  1308          g.lineTo(x+rx, y+ry);
  1309          g.lineTo(x-rx, y+ry);
  1310      }
  1311      g.closePath();
  1312    }
  1313  
  1314    function areaPath(g, items) {
  1315      var o = items[0],
  1316          p = o["path:parsed"] ||
  1317             (o["path:parsed"] = parsePath(vg.canvas.path.area(items)));
  1318      renderPath(g, p);
  1319    }
  1320  
  1321    function linePath(g, items) {
  1322      var o = items[0],
  1323          p = o["path:parsed"] ||
  1324             (o["path:parsed"] = parsePath(vg.canvas.path.line(items)));
  1325      renderPath(g, p);
  1326    }
  1327  
  1328    function lineStroke(g, items) {
  1329      var o = items[0],
  1330          lw = o.strokeWidth,
  1331          lc = o.strokeCap;
  1332      g.lineWidth = lw != null ? lw : vg.config.render.lineWidth;
  1333      g.lineCap   = lc != null ? lc : vg.config.render.lineCap;
  1334      linePath(g, items);
  1335    }
  1336  
  1337    function ruleStroke(g, o) {
  1338      var x1 = o.x || 0,
  1339          y1 = o.y || 0,
  1340          x2 = o.x2 != null ? o.x2 : x1,
  1341          y2 = o.y2 != null ? o.y2 : y1,
  1342          lw = o.strokeWidth,
  1343          lc = o.strokeCap;
  1344  
  1345      g.lineWidth = lw != null ? lw : vg.config.render.lineWidth;
  1346      g.lineCap   = lc != null ? lc : vg.config.render.lineCap;
  1347      g.beginPath();
  1348      g.moveTo(x1, y1);
  1349      g.lineTo(x2, y2);
  1350    }
  1351  
  1352    // drawing functions
  1353  
  1354    function drawPathOne(path, g, o, items) {
  1355      var fill = o.fill, stroke = o.stroke, opac, lc, lw;
  1356  
  1357      path(g, items);
  1358  
  1359      opac = o.opacity == null ? 1 : o.opacity;
  1360      if (opac == 0 || !fill && !stroke) return;
  1361  
  1362      if (fill) {
  1363        g.globalAlpha = opac * (o.fillOpacity==null ? 1 : o.fillOpacity);
  1364        g.fillStyle = color(g, o, fill);
  1365        g.fill();
  1366      }
  1367  
  1368      if (stroke) {
  1369        lw = (lw = o.strokeWidth) != null ? lw : vg.config.render.lineWidth;
  1370        if (lw > 0) {
  1371          g.globalAlpha = opac * (o.strokeOpacity==null ? 1 : o.strokeOpacity);
  1372          g.strokeStyle = color(g, o, stroke);
  1373          g.lineWidth = lw;
  1374          g.lineCap = (lc = o.strokeCap) != null ? lc : vg.config.render.lineCap;
  1375          g.vgLineDash(o.strokeDash || null);
  1376          g.vgLineDashOffset(o.strokeDashOffset || 0);
  1377          g.stroke();
  1378        }
  1379      }
  1380    }
  1381  
  1382    function drawPathAll(path, g, scene, bounds) {
  1383      var i, len, item;
  1384      for (i=0, len=scene.items.length; i<len; ++i) {
  1385        item = scene.items[i];
  1386        if (bounds && !bounds.intersects(item.bounds))
  1387          continue; // bounds check
  1388        drawPathOne(path, g, item, item);
  1389      }
  1390    }
  1391  
  1392    function drawRect(g, scene, bounds) {
  1393      if (!scene.items.length) return;
  1394      var items = scene.items,
  1395          o, fill, stroke, opac, lc, lw, x, y, w, h;
  1396  
  1397      for (var i=0, len=items.length; i<len; ++i) {
  1398        o = items[i];
  1399        if (bounds && !bounds.intersects(o.bounds))
  1400          continue; // bounds check
  1401  
  1402        x = o.x || 0;
  1403        y = o.y || 0;
  1404        w = o.width || 0;
  1405        h = o.height || 0;
  1406  
  1407        opac = o.opacity == null ? 1 : o.opacity;
  1408        if (opac == 0) return;
  1409  
  1410        if (fill = o.fill) {
  1411          g.globalAlpha = opac * (o.fillOpacity==null ? 1 : o.fillOpacity);
  1412          g.fillStyle = color(g, o, fill);
  1413          g.fillRect(x, y, w, h);
  1414        }
  1415  
  1416        if (stroke = o.stroke) {
  1417          lw = (lw = o.strokeWidth) != null ? lw : vg.config.render.lineWidth;
  1418          if (lw > 0) {
  1419            g.globalAlpha = opac * (o.strokeOpacity==null ? 1 : o.strokeOpacity);
  1420            g.strokeStyle = color(g, o, stroke);
  1421            g.lineWidth = lw;
  1422            g.lineCap = (lc = o.strokeCap) != null ? lc : vg.config.render.lineCap;
  1423            g.vgLineDash(o.strokeDash || null);
  1424            g.vgLineDashOffset(o.strokeDashOffset || 0);
  1425            g.strokeRect(x, y, w, h);
  1426          }
  1427        }
  1428      }
  1429    }
  1430  
  1431    function drawRule(g, scene, bounds) {
  1432      if (!scene.items.length) return;
  1433      var items = scene.items,
  1434          o, stroke, opac, lc, lw, x1, y1, x2, y2;
  1435  
  1436      for (var i=0, len=items.length; i<len; ++i) {
  1437        o = items[i];
  1438        if (bounds && !bounds.intersects(o.bounds))
  1439          continue; // bounds check
  1440  
  1441        x1 = o.x || 0;
  1442        y1 = o.y || 0;
  1443        x2 = o.x2 != null ? o.x2 : x1;
  1444        y2 = o.y2 != null ? o.y2 : y1;
  1445  
  1446        opac = o.opacity == null ? 1 : o.opacity;
  1447        if (opac == 0) return;
  1448        
  1449        if (stroke = o.stroke) {
  1450          lw = (lw = o.strokeWidth) != null ? lw : vg.config.render.lineWidth;
  1451          if (lw > 0) {
  1452            g.globalAlpha = opac * (o.strokeOpacity==null ? 1 : o.strokeOpacity);
  1453            g.strokeStyle = color(g, o, stroke);
  1454            g.lineWidth = lw;
  1455            g.lineCap = (lc = o.strokeCap) != null ? lc : vg.config.render.lineCap;
  1456            g.vgLineDash(o.strokeDash || null);
  1457            g.vgLineDashOffset(o.strokeDashOffset || 0);
  1458            g.beginPath();
  1459            g.moveTo(x1, y1);
  1460            g.lineTo(x2, y2);
  1461            g.stroke();
  1462          }
  1463        }
  1464      }
  1465    }
  1466  
  1467    function drawImage(g, scene, bounds) {
  1468      if (!scene.items.length) return;
  1469      var renderer = this,
  1470          items = scene.items, o;
  1471  
  1472      for (var i=0, len=items.length; i<len; ++i) {
  1473        o = items[i];
  1474        if (bounds && !bounds.intersects(o.bounds))
  1475          continue; // bounds check
  1476  
  1477        if (!(o.image && o.image.url === o.url)) {
  1478          o.image = renderer.loadImage(o.url);
  1479          o.image.url = o.url;
  1480        }
  1481  
  1482        var x, y, w, h, opac;
  1483        w = o.width || (o.image && o.image.width) || 0;
  1484        h = o.height || (o.image && o.image.height) || 0;
  1485        x = (o.x||0) - (o.align === "center"
  1486          ? w/2 : (o.align === "right" ? w : 0));
  1487        y = (o.y||0) - (o.baseline === "middle"
  1488          ? h/2 : (o.baseline === "bottom" ? h : 0));
  1489  
  1490        if (o.image.loaded) {
  1491          g.globalAlpha = (opac = o.opacity) != null ? opac : 1;
  1492          g.drawImage(o.image, x, y, w, h);
  1493        }
  1494      }
  1495    }
  1496  
  1497    function drawText(g, scene, bounds) {
  1498      if (!scene.items.length) return;
  1499      var items = scene.items,
  1500          o, fill, stroke, opac, lw, text, ta, tb;
  1501  
  1502      for (var i=0, len=items.length; i<len; ++i) {
  1503        o = items[i];
  1504        if (bounds && !bounds.intersects(o.bounds))
  1505          continue; // bounds check
  1506  
  1507        g.font = vg.scene.fontString(o);
  1508        g.textAlign = o.align || "left";
  1509        g.textBaseline = o.baseline || "alphabetic";
  1510  
  1511        opac = o.opacity == null ? 1 : o.opacity;
  1512        if (opac == 0) return;
  1513  
  1514        if (o.angle) {
  1515          g.save();
  1516          g.translate(o.x || 0, o.y || 0);
  1517          g.rotate(o.angle * Math.PI/180);
  1518          x = o.dx || 0;
  1519          y = o.dy || 0;
  1520        } else {
  1521          x = (o.x || 0) + (o.dx || 0);
  1522          y = (o.y || 0) + (o.dy || 0);
  1523        }
  1524  
  1525        if (fill = o.fill) {
  1526          g.globalAlpha = opac * (o.fillOpacity==null ? 1 : o.fillOpacity);
  1527          g.fillStyle = color(g, o, fill);
  1528          g.fillText(o.text, x, y);
  1529        }
  1530  
  1531        if (stroke = o.stroke) {
  1532          lw = (lw = o.strokeWidth) != null ? lw : 1;
  1533          if (lw > 0) {
  1534            g.globalAlpha = opac * (o.strokeOpacity==null ? 1 : o.strokeOpacity);
  1535            g.strokeStyle = color(o, stroke);
  1536            g.lineWidth = lw;
  1537            g.strokeText(o.text, x, y);
  1538          }
  1539        }
  1540  
  1541        if (o.angle) g.restore();
  1542      }
  1543    }
  1544  
  1545    function drawAll(pathFunc) {
  1546      return function(g, scene, bounds) {
  1547        drawPathAll(pathFunc, g, scene, bounds);
  1548      }
  1549    }
  1550  
  1551    function drawOne(pathFunc) {
  1552      return function(g, scene, bounds) {
  1553        if (!scene.items.length) return;
  1554        if (bounds && !bounds.intersects(scene.items[0].bounds))
  1555          return; // bounds check
  1556        drawPathOne(pathFunc, g, scene.items[0], scene.items);
  1557      }
  1558    }
  1559  
  1560    function drawGroup(g, scene, bounds) {
  1561      if (!scene.items.length) return;
  1562      var items = scene.items, group, axes, legends,
  1563          renderer = this, gx, gy, gb, i, n, j, m;
  1564  
  1565      drawRect(g, scene, bounds);
  1566  
  1567      for (i=0, n=items.length; i<n; ++i) {
  1568        group = items[i];
  1569        axes = group.axisItems || [];
  1570        legends = group.legendItems || [];
  1571        gx = group.x || 0;
  1572        gy = group.y || 0;
  1573  
  1574        // render group contents
  1575        g.save();
  1576        g.translate(gx, gy);
  1577        if (bounds) bounds.translate(-gx, -gy);
  1578        for (j=0, m=axes.length; j<m; ++j) {
  1579          if (axes[j].def.layer === "back") {
  1580            renderer.draw(g, axes[j], bounds);
  1581          }
  1582        }
  1583        for (j=0, m=group.items.length; j<m; ++j) {
  1584          renderer.draw(g, group.items[j], bounds);
  1585        }
  1586        for (j=0, m=axes.length; j<m; ++j) {
  1587          if (axes[j].def.layer !== "back") {
  1588            renderer.draw(g, axes[j], bounds);
  1589          }
  1590        }
  1591        for (j=0, m=legends.length; j<m; ++j) {
  1592          renderer.draw(g, legends[j], bounds);
  1593        }
  1594        if (bounds) bounds.translate(gx, gy);
  1595        g.restore();
  1596      }    
  1597    }
  1598  
  1599    function color(g, o, value) {
  1600      return (value.id)
  1601        ? gradient(g, value, o.bounds)
  1602        : value;
  1603    }
  1604  
  1605    function gradient(g, p, b) {
  1606      var w = b.width(),
  1607          h = b.height(),
  1608          x1 = b.x1 + p.x1 * w,
  1609          y1 = b.y1 + p.y1 * h,
  1610          x2 = b.x1 + p.x2 * w,
  1611          y2 = b.y1 + p.y2 * h,
  1612          grad = g.createLinearGradient(x1, y1, x2, y2),
  1613          stop = p.stops,
  1614          i, n;
  1615  
  1616      for (i=0, n=stop.length; i<n; ++i) {
  1617        grad.addColorStop(stop[i].offset, stop[i].color);
  1618      }
  1619      return grad;
  1620    }
  1621  
  1622    // hit testing
  1623  
  1624    function pickGroup(g, scene, x, y, gx, gy) {
  1625      if (scene.items.length === 0 ||
  1626          scene.bounds && !scene.bounds.contains(gx, gy)) {
  1627        return false;
  1628      }
  1629      var items = scene.items, subscene, group, hit, dx, dy,
  1630          handler = this, i, j;
  1631  
  1632      for (i=items.length; --i>=0;) {
  1633        group = items[i];
  1634        dx = group.x || 0;
  1635        dy = group.y || 0;
  1636  
  1637        g.save();
  1638        g.translate(dx, dy);
  1639        for (j=group.items.length; --j >= 0;) {
  1640          subscene = group.items[j];
  1641          if (subscene.interactive === false) continue;
  1642          hit = handler.pick(subscene, x, y, gx-dx, gy-dy);
  1643          if (hit) {
  1644            g.restore();
  1645            return hit;
  1646          }
  1647        }
  1648        g.restore();
  1649      }
  1650  
  1651      return scene.interactive
  1652        ? pickAll(hitTests.rect, g, scene, x, y, gx, gy)
  1653        : false;
  1654    }
  1655  
  1656    function pickAll(test, g, scene, x, y, gx, gy) {
  1657      if (!scene.items.length) return false;
  1658      var o, b, i;
  1659  
  1660      if (g._ratio !== 1) {
  1661        x *= g._ratio;
  1662        y *= g._ratio;
  1663      }
  1664  
  1665      for (i=scene.items.length; --i >= 0;) {
  1666        o = scene.items[i]; b = o.bounds;
  1667        // first hit test against bounding box
  1668        if ((b && !b.contains(gx, gy)) || !b) continue;
  1669        // if in bounding box, perform more careful test
  1670        if (test(g, o, x, y, gx, gy)) return o;
  1671      }
  1672      return false;
  1673    }
  1674  
  1675    function pickArea(g, scene, x, y, gx, gy) {
  1676      if (!scene.items.length) return false;
  1677      var items = scene.items,
  1678          o, b, i, di, dd, od, dx, dy;
  1679  
  1680      b = items[0].bounds;
  1681      if (b && !b.contains(gx, gy)) return false;
  1682      if (g._ratio !== 1) {
  1683        x *= g._ratio;
  1684        y *= g._ratio;
  1685      }
  1686      if (!hitTests.area(g, items, x, y)) return false;
  1687      return items[0];
  1688    }
  1689  
  1690    function pickLine(g, scene, x, y, gx, gy) {
  1691      if (!scene.items.length) return false;
  1692      var items = scene.items,
  1693          o, b, i, di, dd, od, dx, dy;
  1694  
  1695      b = items[0].bounds;
  1696      if (b && !b.contains(gx, gy)) return false;
  1697      if (g._ratio !== 1) {
  1698        x *= g._ratio;
  1699        y *= g._ratio;
  1700      }
  1701      if (!hitTests.line(g, items, x, y)) return false;
  1702      return items[0];
  1703    }
  1704  
  1705    function pick(test) {
  1706      return function (g, scene, x, y, gx, gy) {
  1707        return pickAll(test, g, scene, x, y, gx, gy);
  1708      };
  1709    }
  1710  
  1711    function textHit(g, o, x, y, gx, gy) {
  1712      if (!o.fontSize) return false;
  1713      if (!o.angle) return true; // bounds sufficient if no rotation
  1714  
  1715      var b = vg.scene.bounds.text(o, tmpBounds, true),
  1716          a = -o.angle * Math.PI / 180,
  1717          cos = Math.cos(a),
  1718          sin = Math.sin(a),
  1719          x = o.x,
  1720          y = o.y,
  1721          px = cos*gx - sin*gy + (x - x*cos + y*sin),
  1722          py = sin*gx + cos*gy + (y - x*sin - y*cos);
  1723  
  1724      return b.contains(px, py);
  1725    }
  1726  
  1727    var hitTests = {
  1728      text:   textHit,
  1729      rect:   function(g,o,x,y) { return true; }, // bounds test is sufficient
  1730      image:  function(g,o,x,y) { return true; }, // bounds test is sufficient
  1731      rule:   function(g,o,x,y) {
  1732                if (!g.isPointInStroke) return false;
  1733                ruleStroke(g,o); return g.isPointInStroke(x,y);
  1734              },
  1735      line:   function(g,s,x,y) {
  1736                if (!g.isPointInStroke) return false;
  1737                lineStroke(g,s); return g.isPointInStroke(x,y);
  1738              },
  1739      arc:    function(g,o,x,y) { arcPath(g,o);  return g.isPointInPath(x,y); },
  1740      area:   function(g,s,x,y) { areaPath(g,s); return g.isPointInPath(x,y); },
  1741      path:   function(g,o,x,y) { pathPath(g,o); return g.isPointInPath(x,y); },
  1742      symbol: function(g,o,x,y) { symbolPath(g,o); return g.isPointInPath(x,y); }
  1743    };
  1744  
  1745    return {
  1746      draw: {
  1747        group:   drawGroup,
  1748        area:    drawOne(areaPath),
  1749        line:    drawOne(linePath),
  1750        arc:     drawAll(arcPath),
  1751        path:    drawAll(pathPath),
  1752        symbol:  drawAll(symbolPath),
  1753        rect:    drawRect,
  1754        rule:    drawRule,
  1755        text:    drawText,
  1756        image:   drawImage,
  1757        drawOne: drawOne, // expose for extensibility
  1758        drawAll: drawAll  // expose for extensibility
  1759      },
  1760      pick: {
  1761        group:   pickGroup,
  1762        area:    pickArea,
  1763        line:    pickLine,
  1764        arc:     pick(hitTests.arc),
  1765        path:    pick(hitTests.path),
  1766        symbol:  pick(hitTests.symbol),
  1767        rect:    pick(hitTests.rect),
  1768        rule:    pick(hitTests.rule),
  1769        text:    pick(hitTests.text),
  1770        image:   pick(hitTests.image),
  1771        pickAll: pickAll  // expose for extensibility
  1772      }
  1773    };
  1774  
  1775  })();vg.canvas.Renderer = (function() {  
  1776    var renderer = function() {
  1777      this._ctx = null;
  1778      this._el = null;
  1779      this._imgload = 0;
  1780    };
  1781    
  1782    var prototype = renderer.prototype;
  1783    
  1784    prototype.initialize = function(el, width, height, pad) {
  1785      this._el = el;
  1786      
  1787      if (!el) return this; // early exit if no DOM element
  1788  
  1789      // select canvas element
  1790      var canvas = d3.select(el)
  1791        .selectAll("canvas.marks")
  1792        .data([1]);
  1793      
  1794      // create new canvas element if needed
  1795      canvas.enter()
  1796        .append("canvas")
  1797        .attr("class", "marks");
  1798      
  1799      // remove extraneous canvas if needed
  1800      canvas.exit().remove();
  1801      
  1802      return this.resize(width, height, pad);
  1803    };
  1804    
  1805    prototype.resize = function(width, height, pad) {
  1806      this._width = width;
  1807      this._height = height;
  1808      this._padding = pad;
  1809      
  1810      if (this._el) {
  1811        var canvas = d3.select(this._el).select("canvas.marks");
  1812  
  1813        // initialize canvas attributes
  1814        canvas
  1815          .attr("width", width + pad.left + pad.right)
  1816          .attr("height", height + pad.top + pad.bottom);
  1817  
  1818        // get the canvas graphics context
  1819        var s;
  1820        this._ctx = canvas.node().getContext("2d");
  1821        this._ctx._ratio = (s = scaleCanvas(canvas.node(), this._ctx) || 1);
  1822        this._ctx.setTransform(s, 0, 0, s, s*pad.left, s*pad.top);
  1823      }
  1824      
  1825      initializeLineDash(this._ctx);
  1826      return this;
  1827    };
  1828    
  1829    function scaleCanvas(canvas, ctx) {
  1830      // get canvas pixel data
  1831      var devicePixelRatio = window.devicePixelRatio || 1,
  1832          backingStoreRatio = (
  1833            ctx.webkitBackingStorePixelRatio ||
  1834            ctx.mozBackingStorePixelRatio ||
  1835            ctx.msBackingStorePixelRatio ||
  1836            ctx.oBackingStorePixelRatio ||
  1837            ctx.backingStorePixelRatio) || 1,
  1838          ratio = devicePixelRatio / backingStoreRatio;
  1839  
  1840      if (devicePixelRatio !== backingStoreRatio) {
  1841        var w = canvas.width, h = canvas.height;
  1842        // set actual and visible canvas size
  1843        canvas.setAttribute("width", w * ratio);
  1844        canvas.setAttribute("height", h * ratio);
  1845        canvas.style.width = w + 'px';
  1846        canvas.style.height = h + 'px';
  1847      }
  1848      return ratio;
  1849    }
  1850  
  1851    function initializeLineDash(ctx) {
  1852      if (ctx.vgLineDash) return; // already set
  1853  
  1854      var NODASH = [];
  1855      if (ctx.setLineDash) {
  1856        ctx.vgLineDash = function(dash) { this.setLineDash(dash || NODASH); };
  1857        ctx.vgLineDashOffset = function(off) { this.lineDashOffset = off; };
  1858      } else if (ctx.webkitLineDash !== undefined) {
  1859      	ctx.vgLineDash = function(dash) { this.webkitLineDash = dash || NODASH; };
  1860        ctx.vgLineDashOffset = function(off) { this.webkitLineDashOffset = off; };
  1861      } else if (ctx.mozDash !== undefined) {
  1862        ctx.vgLineDash = function(dash) { this.mozDash = dash; };
  1863        ctx.vgLineDashOffset = function(off) { /* unsupported */ };
  1864      } else {
  1865        ctx.vgLineDash = function(dash) { /* unsupported */ };
  1866        ctx.vgLineDashOffset = function(off) { /* unsupported */ };
  1867      }
  1868    }
  1869    
  1870    prototype.context = function(ctx) {
  1871      if (ctx) { this._ctx = ctx; return this; }
  1872      else return this._ctx;
  1873    };
  1874    
  1875    prototype.element = function() {
  1876      return this._el;
  1877    };
  1878    
  1879    prototype.pendingImages = function() {
  1880      return this._imgload;
  1881    };
  1882  
  1883    function translatedBounds(item, bounds) {
  1884      var b = new vg.Bounds(bounds);
  1885      while ((item = item.mark.group) != null) {
  1886        b.translate(item.x || 0, item.y || 0);
  1887      }
  1888      return b;
  1889    }
  1890      
  1891    function getBounds(items) {
  1892      return !items ? null :
  1893        vg.array(items).reduce(function(b, item) {
  1894          return b.union(translatedBounds(item, item.bounds))
  1895                  .union(translatedBounds(item, item['bounds:prev']));
  1896        }, new vg.Bounds());  
  1897    }
  1898    
  1899    function setBounds(g, bounds) {
  1900      var bbox = null;
  1901      if (bounds) {
  1902        bbox = (new vg.Bounds(bounds)).round();
  1903        g.beginPath();
  1904        g.rect(bbox.x1, bbox.y1, bbox.width(), bbox.height());
  1905        g.clip();
  1906      }
  1907      return bbox;
  1908    }
  1909    
  1910    prototype.render = function(scene, items) {
  1911      var g = this._ctx,
  1912          pad = this._padding,
  1913          w = this._width + pad.left + pad.right,
  1914          h = this._height + pad.top + pad.bottom,
  1915          bb = null, bb2;
  1916  
  1917      // setup
  1918      this._scene = scene;
  1919      g.save();
  1920      bb = setBounds(g, getBounds(items));
  1921      g.clearRect(-pad.left, -pad.top, w, h);
  1922  
  1923      // render
  1924      this.draw(g, scene, bb);
  1925  
  1926      // render again to handle possible bounds change
  1927      if (items) {
  1928        g.restore();
  1929        g.save();
  1930        bb2 = setBounds(g, getBounds(items));
  1931        if (!bb.encloses(bb2)) {
  1932          g.clearRect(-pad.left, -pad.top, w, h);
  1933          this.draw(g, scene, bb2);
  1934        }
  1935      }
  1936      
  1937      // takedown
  1938      g.restore();
  1939      this._scene = null;
  1940    };
  1941    
  1942    prototype.draw = function(ctx, scene, bounds) {
  1943      var marktype = scene.marktype,
  1944          renderer = vg.canvas.marks.draw[marktype];
  1945      renderer.call(this, ctx, scene, bounds);
  1946  
  1947      // compute mark-level bounds
  1948      scene.bounds = scene.items.reduce(function(b, item) {
  1949        return item.bounds ? b.union(item.bounds) : b;
  1950      }, scene.bounds || new vg.Bounds());
  1951    };
  1952    
  1953    prototype.renderAsync = function(scene) {
  1954      // TODO make safe for multiple scene rendering?
  1955      var renderer = this;
  1956      if (renderer._async_id) {
  1957        clearTimeout(renderer._async_id);
  1958      }
  1959      renderer._async_id = setTimeout(function() {
  1960        renderer.render(scene);
  1961        delete renderer._async_id;
  1962      }, 50);
  1963    };
  1964    
  1965    prototype.loadImage = function(uri) {
  1966      var renderer = this,
  1967          scene = renderer._scene,
  1968          image = null, url;
  1969  
  1970      renderer._imgload += 1;
  1971      if (vg.config.isNode) {
  1972        image = new (require("canvas").Image)();
  1973        vg.data.load(uri, function(err, data) {
  1974          if (err) { vg.error(err); return; }
  1975          image.src = data;
  1976          image.loaded = true;
  1977          renderer._imgload -= 1;
  1978        });
  1979      } else {
  1980        image = new Image();
  1981        url = vg.config.baseURL + uri;
  1982        image.onload = function() {
  1983          vg.log("LOAD IMAGE: "+url);
  1984          image.loaded = true;
  1985          renderer._imgload -= 1;
  1986          renderer.renderAsync(scene);
  1987        };
  1988        image.src = url;
  1989      }
  1990  
  1991      return image;
  1992    };
  1993    
  1994    return renderer;
  1995  })();vg.canvas.Handler = (function() {
  1996    var handler = function(el, model) {
  1997      this._active = null;
  1998      this._handlers = {};
  1999      if (el) this.initialize(el);
  2000      if (model) this.model(model);
  2001    };
  2002    
  2003    var prototype = handler.prototype;
  2004  
  2005    prototype.initialize = function(el, pad, obj) {
  2006      this._el = d3.select(el).node();
  2007      this._canvas = d3.select(el).select("canvas.marks").node();
  2008      this._padding = pad;
  2009      this._obj = obj || null;
  2010      
  2011      // add event listeners
  2012      var canvas = this._canvas, that = this;
  2013      events.forEach(function(type) {
  2014        canvas.addEventListener(type, function(evt) {
  2015          prototype[type].call(that, evt);
  2016        });
  2017      });
  2018      
  2019      return this;
  2020    };
  2021    
  2022    prototype.padding = function(pad) {
  2023      this._padding = pad;
  2024      return this;
  2025    };
  2026    
  2027    prototype.model = function(model) {
  2028      if (!arguments.length) return this._model;
  2029      this._model = model;
  2030      return this;
  2031    };
  2032  
  2033    prototype.handlers = function() {
  2034      var h = this._handlers;
  2035      return vg.keys(h).reduce(function(a, k) {
  2036        return h[k].reduce(function(a, x) { return (a.push(x), a); }, a);
  2037      }, []);
  2038    };
  2039  
  2040    // setup events
  2041    var events = [
  2042      "mousedown",
  2043      "mouseup",
  2044      "click",
  2045      "dblclick",
  2046      "wheel",
  2047      "keydown",
  2048      "keypress",
  2049      "keyup",
  2050      "mousewheel"
  2051    ];
  2052    events.forEach(function(type) {
  2053      prototype[type] = function(evt) {
  2054        this.fire(type, evt);
  2055      };
  2056    });
  2057    events.push("mousemove");
  2058    events.push("mouseout");
  2059  
  2060    function eventName(name) {
  2061      var i = name.indexOf(".");
  2062      return i < 0 ? name : name.slice(0,i);
  2063    }
  2064  
  2065    prototype.mousemove = function(evt) {
  2066      var pad = this._padding,
  2067          b = evt.target.getBoundingClientRect(),
  2068          x = evt.clientX - b.left,
  2069          y = evt.clientY - b.top,
  2070          a = this._active,
  2071          p = this.pick(this._model.scene(), x, y, x-pad.left, y-pad.top);
  2072  
  2073      if (p === a) {
  2074        this.fire("mousemove", evt);
  2075        return;
  2076      } else if (a) {
  2077        this.fire("mouseout", evt);
  2078      }
  2079      this._active = p;
  2080      if (p) {
  2081        this.fire("mouseover", evt);
  2082      }
  2083    };
  2084    
  2085    prototype.mouseout = function(evt) {
  2086      if (this._active) {
  2087        this.fire("mouseout", evt);
  2088      }
  2089      this._active = null;
  2090    };
  2091  
  2092    // to keep firefox happy
  2093    prototype.DOMMouseScroll = function(evt) {
  2094      this.fire("mousewheel", evt);
  2095    };
  2096  
  2097    // fire an event
  2098    prototype.fire = function(type, evt) {
  2099      var a = this._active,
  2100          h = this._handlers[type];
  2101      if (a && h) {
  2102        for (var i=0, len=h.length; i<len; ++i) {
  2103          h[i].handler.call(this._obj, evt, a);
  2104        }
  2105      }
  2106    };
  2107  
  2108    // add an event handler
  2109    prototype.on = function(type, handler) {
  2110      var name = eventName(type),
  2111          h = this._handlers;
  2112      h = h[name] || (h[name] = []);
  2113      h.push({
  2114        type: type,
  2115        handler: handler
  2116      });
  2117      return this;
  2118    };
  2119  
  2120    // remove an event handler
  2121    prototype.off = function(type, handler) {
  2122      var name = eventName(type),
  2123          h = this._handlers[name];
  2124      if (!h) return;
  2125      for (var i=h.length; --i>=0;) {
  2126        if (h[i].type !== type) continue;
  2127        if (!handler || h[i].handler === handler) h.splice(i, 1);
  2128      }
  2129      return this;
  2130    };
  2131    
  2132    // retrieve the current canvas context
  2133    prototype.context = function() {
  2134      return this._canvas.getContext("2d");
  2135    };
  2136    
  2137    // find the scenegraph item at the current mouse position
  2138    // returns an array of scenegraph items, from leaf node up to the root
  2139    // x, y -- the absolute x, y mouse coordinates on the canvas element
  2140    // gx, gy -- the relative coordinates within the current group
  2141    prototype.pick = function(scene, x, y, gx, gy) {
  2142      var g = this.context(),
  2143          marktype = scene.marktype,
  2144          picker = vg.canvas.marks.pick[marktype];
  2145      return picker.call(this, g, scene, x, y, gx, gy);
  2146    };
  2147  
  2148    return handler;
  2149  })();vg.svg = {};vg.svg.marks = (function() {
  2150  
  2151    function x(o)     { return o.x || 0; }
  2152    function y(o)     { return o.y || 0; }
  2153    function yh(o)    { return o.y + o.height || 0; }
  2154    function key(o)   { return o.key; }
  2155    function size(o)  { return o.size==null ? 100 : o.size; }
  2156    function shape(o) { return o.shape || "circle"; }
  2157        
  2158    var arc_path    = d3.svg.arc(),
  2159        area_path   = d3.svg.area().x(x).y1(y).y0(yh),
  2160        line_path   = d3.svg.line().x(x).y(y),
  2161        symbol_path = d3.svg.symbol().type(shape).size(size);
  2162    
  2163    var mark_id = 0;
  2164    
  2165    var textAlign = {
  2166      "left":   "start",
  2167      "center": "middle",
  2168      "right":  "end"
  2169    };
  2170    
  2171    var styles = {
  2172      "fill":             "fill",
  2173      "fillOpacity":      "fill-opacity",
  2174      "stroke":           "stroke",
  2175      "strokeWidth":      "stroke-width",
  2176      "strokeOpacity":    "stroke-opacity",
  2177      "strokeCap":        "stroke-linecap",
  2178      "strokeDash":       "stroke-dasharray",
  2179      "strokeDashOffset": "stroke-dashoffset",
  2180      "opacity":          "opacity"
  2181    };
  2182    var styleProps = vg.keys(styles);
  2183  
  2184    function style(d) {
  2185      var i, n, prop, name, value,
  2186          o = d.mark ? d : d.length ? d[0] : null;
  2187      if (o === null) return;
  2188  
  2189      for (i=0, n=styleProps.length; i<n; ++i) {
  2190        prop = styleProps[i];
  2191        name = styles[prop];
  2192        value = o[prop];
  2193  
  2194        if (value == null) {
  2195          if (name === "fill") this.style.setProperty(name, "none", null);
  2196          else this.style.removeProperty(name);
  2197        } else {
  2198          if (value.id) {
  2199            // ensure definition is included
  2200            vg.svg._cur._defs[value.id] = value;
  2201            value = "url(#" + value.id + ")";
  2202          }
  2203          this.style.setProperty(name, value+"", null);
  2204        }
  2205      }
  2206    }
  2207    
  2208    function arc(o) {
  2209      var x = o.x || 0,
  2210          y = o.y || 0;
  2211      this.setAttribute("transform", "translate("+x+","+y+")");
  2212      this.setAttribute("d", arc_path(o));
  2213    }
  2214    
  2215    function area(items) {
  2216      if (!items.length) return;
  2217      var o = items[0];
  2218      area_path
  2219        .interpolate(o.interpolate || "linear")
  2220        .tension(o.tension == null ? 0.7 : o.tension);
  2221      this.setAttribute("d", area_path(items));
  2222    }
  2223    
  2224    function line(items) {
  2225      if (!items.length) return;
  2226      var o = items[0];
  2227      line_path
  2228        .interpolate(o.interpolate || "linear")
  2229        .tension(o.tension == null ? 0.7 : o.tension);
  2230      this.setAttribute("d", line_path(items));
  2231    }
  2232    
  2233    function path(o) {
  2234      var x = o.x || 0,
  2235          y = o.y || 0;
  2236      this.setAttribute("transform", "translate("+x+","+y+")");
  2237      if (o.path != null) this.setAttribute("d", o.path);
  2238    }
  2239  
  2240    function rect(o) {
  2241      this.setAttribute("x", o.x || 0);
  2242      this.setAttribute("y", o.y || 0);
  2243      this.setAttribute("width", o.width || 0);
  2244      this.setAttribute("height", o.height || 0);
  2245    }
  2246  
  2247    function rule(o) {
  2248      var x1 = o.x || 0,
  2249          y1 = o.y || 0;
  2250      this.setAttribute("x1", x1);
  2251      this.setAttribute("y1", y1);
  2252      this.setAttribute("x2", o.x2 != null ? o.x2 : x1);
  2253      this.setAttribute("y2", o.y2 != null ? o.y2 : y1);
  2254    }
  2255    
  2256    function symbol(o) {
  2257      var x = o.x || 0,
  2258          y = o.y || 0;
  2259      this.setAttribute("transform", "translate("+x+","+y+")");
  2260      this.setAttribute("d", symbol_path(o));
  2261    }
  2262    
  2263    function image(o) {
  2264      var w = o.width || (o.image && o.image.width) || 0,
  2265          h = o.height || (o.image && o.image.height) || 0,
  2266          x = o.x - (o.align === "center"
  2267            ? w/2 : (o.align === "right" ? w : 0)),
  2268          y = o.y - (o.baseline === "middle"
  2269            ? h/2 : (o.baseline === "bottom" ? h : 0)),
  2270          url = vg.config.baseURL + o.url;
  2271      
  2272      this.setAttributeNS("http://www.w3.org/1999/xlink", "href", url);
  2273      this.setAttribute("x", x);
  2274      this.setAttribute("y", y);
  2275      this.setAttribute("width", w);
  2276      this.setAttribute("height", h);
  2277    }
  2278      
  2279    function fontString(o) {
  2280      return (o.fontStyle ? o.fontStyle + " " : "")
  2281        + (o.fontVariant ? o.fontVariant + " " : "")
  2282        + (o.fontWeight ? o.fontWeight + " " : "")
  2283        + (o.fontSize != null ? o.fontSize : vg.config.render.fontSize) + "px "
  2284        + (o.font || vg.config.render.font);
  2285    }
  2286    
  2287    function text(o) {
  2288      var x = o.x || 0,
  2289          y = o.y || 0,
  2290          dx = o.dx || 0,
  2291          dy = o.dy || 0,
  2292          a = o.angle || 0,
  2293          align = textAlign[o.align || "left"],
  2294          base = o.baseline==="top" ? ".9em"
  2295               : o.baseline==="middle" ? ".35em" : 0;
  2296    
  2297      this.setAttribute("x", x + dx);
  2298      this.setAttribute("y", y + dy);
  2299      this.setAttribute("dy", dy);
  2300      this.setAttribute("text-anchor", align);
  2301      
  2302      if (a) this.setAttribute("transform", "rotate("+a+" "+x+","+y+")");
  2303      else this.removeAttribute("transform");
  2304      
  2305      if (base) this.setAttribute("dy", base);
  2306      else this.removeAttribute("dy");
  2307      
  2308      this.textContent = o.text;
  2309      this.style.setProperty("font", fontString(o), null);
  2310    }
  2311    
  2312    function group(o) {
  2313      var x = o.x || 0,
  2314          y = o.y || 0;
  2315      this.setAttribute("transform", "translate("+x+","+y+")");
  2316    }
  2317  
  2318    function group_bg(o) {
  2319      var w = o.width || 0,
  2320          h = o.height || 0;
  2321      this.setAttribute("width", w);
  2322      this.setAttribute("height", h);
  2323    }
  2324  
  2325    function draw(tag, attr, nest) {
  2326      return function(g, scene, index) {
  2327        drawMark(g, scene, index, "mark_", tag, attr, nest);
  2328      };
  2329    }
  2330    
  2331    function drawMark(g, scene, index, prefix, tag, attr, nest) {
  2332      var data = nest ? [scene.items] : scene.items,
  2333          evts = scene.interactive===false ? "none" : null,
  2334          grps = g.node().childNodes,
  2335          notG = (tag !== "g"),
  2336          p = (p = grps[index+1]) // +1 to skip group background rect
  2337            ? d3.select(p)
  2338            : g.append("g").attr("id", "g"+(++mark_id));
  2339  
  2340      var id = "#" + p.attr("id"),
  2341          s = id + " > " + tag,
  2342          m = p.selectAll(s).data(data),
  2343          e = m.enter().append(tag);
  2344  
  2345      if (notG) {
  2346        p.style("pointer-events", evts);
  2347        e.each(function(d) {
  2348          if (d.mark) d._svg = this;
  2349          else if (d.length) d[0]._svg = this;
  2350        });
  2351      } else {
  2352        e.append("rect").attr("class","background").style("pointer-events",evts);
  2353      }
  2354      
  2355      m.exit().remove();
  2356      m.each(attr);
  2357      if (notG) m.each(style);
  2358      else p.selectAll(s+" > rect.background").each(group_bg).each(style);
  2359      
  2360      return p;
  2361    }
  2362  
  2363    function drawGroup(g, scene, index, prefix) {    
  2364      var p = drawMark(g, scene, index, prefix || "group_", "g", group),
  2365          c = p.node().childNodes, n = c.length, i, j, m;
  2366      
  2367      for (i=0; i<n; ++i) {
  2368        var items = c[i].__data__.items,
  2369            legends = c[i].__data__.legendItems || [],
  2370            axes = c[i].__data__.axisItems || [],
  2371            sel = d3.select(c[i]),
  2372            idx = 0;
  2373  
  2374        for (j=0, m=axes.length; j<m; ++j) {
  2375          if (axes[j].def.layer === "back") {
  2376            drawGroup.call(this, sel, axes[j], idx++, "axis_");
  2377          }
  2378        }
  2379        for (j=0, m=items.length; j<m; ++j) {
  2380          this.draw(sel, items[j], idx++);
  2381        }
  2382        for (j=0, m=axes.length; j<m; ++j) {
  2383          if (axes[j].def.layer !== "back") {
  2384            drawGroup.call(this, sel, axes[j], idx++, "axis_");
  2385          }
  2386        }
  2387        for (j=0, m=legends.length; j<m; ++j) {
  2388          drawGroup.call(this, sel, legends[j], idx++, "legend_");
  2389        }
  2390      }
  2391    }
  2392  
  2393    return {
  2394      update: {
  2395        group:   rect,
  2396        area:    area,
  2397        line:    line,
  2398        arc:     arc,
  2399        path:    path,
  2400        symbol:  symbol,
  2401        rect:    rect,
  2402        rule:    rule,
  2403        text:    text,
  2404        image:   image
  2405      },
  2406      nested: {
  2407        "area": true,
  2408        "line": true
  2409      },
  2410      style: style,
  2411      draw: {
  2412        group:   drawGroup,
  2413        area:    draw("path", area, true),
  2414        line:    draw("path", line, true),
  2415        arc:     draw("path", arc),
  2416        path:    draw("path", path),
  2417        symbol:  draw("path", symbol),
  2418        rect:    draw("rect", rect),
  2419        rule:    draw("line", rule),
  2420        text:    draw("text", text),
  2421        image:   draw("image", image),
  2422        draw:    draw // expose for extensibility
  2423      }
  2424    };
  2425    
  2426  })();vg.svg.Renderer = (function() {  
  2427    var renderer = function() {
  2428      this._svg = null;
  2429      this._ctx = null;
  2430      this._el = null;
  2431      this._defs = {};
  2432    };
  2433    
  2434    var prototype = renderer.prototype;
  2435    
  2436    prototype.initialize = function(el, width, height, pad) {
  2437      this._el = el;
  2438  
  2439      // remove any existing svg element
  2440      d3.select(el).select("svg.marks").remove();
  2441  
  2442      // create svg element and initialize attributes
  2443      this._svg = d3.select(el)
  2444        .append("svg")
  2445        .attr("class", "marks");
  2446      
  2447      // set the svg root group
  2448      this._ctx = this._svg.append("g");
  2449      
  2450      return this.resize(width, height, pad);
  2451    };
  2452    
  2453    prototype.resize = function(width, height, pad) {
  2454      this._width = width;
  2455      this._height = height;
  2456      this._padding = pad;
  2457      
  2458      this._svg
  2459        .attr("width", width + pad.left + pad.right)
  2460        .attr("height", height + pad.top + pad.bottom);
  2461        
  2462      this._ctx
  2463        .attr("transform", "translate("+pad.left+","+pad.top+")");
  2464  
  2465      return this;
  2466    };
  2467    
  2468    prototype.context = function() {
  2469      return this._ctx;
  2470    };
  2471    
  2472    prototype.element = function() {
  2473      return this._el;
  2474    };
  2475  
  2476    prototype.updateDefs = function() {
  2477      var svg = this._svg,
  2478          all = this._defs,
  2479          ids = vg.keys(all),
  2480          defs = svg.select("defs"), grds;
  2481    
  2482      // get or create svg defs block
  2483      if (ids.length===0) { defs.remove(); return; }
  2484      if (defs.empty()) defs = svg.insert("defs", ":first-child");
  2485      
  2486      grds = defs.selectAll("linearGradient").data(ids, vg.identity);
  2487      grds.enter().append("linearGradient").attr("id", vg.identity);
  2488      grds.exit().remove();
  2489      grds.each(function(id) {
  2490        var def = all[id],
  2491            grd = d3.select(this);
  2492    
  2493        // set gradient coordinates
  2494        grd.attr({x1: def.x1, x2: def.x2, y1: def.y1, y2: def.y2});
  2495    
  2496        // set gradient stops
  2497        stop = grd.selectAll("stop").data(def.stops);
  2498        stop.enter().append("stop");
  2499        stop.exit().remove();
  2500        stop.attr("offset", function(d) { return d.offset; })
  2501            .attr("stop-color", function(d) { return d.color; });
  2502      });
  2503    };
  2504    
  2505    prototype.render = function(scene, items) {
  2506      vg.svg._cur = this;
  2507  
  2508      if (items) this.renderItems(vg.array(items));
  2509      else this.draw(this._ctx, scene, -1);
  2510      this.updateDefs();
  2511  
  2512     delete vg.svg._cur;
  2513    };
  2514    
  2515    prototype.renderItems = function(items) {
  2516      var item, node, type, nest, i, n,
  2517          marks = vg.svg.marks;
  2518  
  2519      for (i=0, n=items.length; i<n; ++i) {
  2520        item = items[i];
  2521        node = item._svg;
  2522        type = item.mark.marktype;
  2523  
  2524        item = marks.nested[type] ? item.mark.items : item;
  2525        marks.update[type].call(node, item);
  2526        marks.style.call(node, item);
  2527      }
  2528    }
  2529    
  2530    prototype.draw = function(ctx, scene, index) {
  2531      var marktype = scene.marktype,
  2532          renderer = vg.svg.marks.draw[marktype];
  2533      renderer.call(this, ctx, scene, index);
  2534    };
  2535    
  2536    return renderer;
  2537  })();vg.svg.Handler = (function() {
  2538    var handler = function(el, model) {
  2539      this._active = null;
  2540      this._handlers = {};
  2541      if (el) this.initialize(el);
  2542      if (model) this.model(model);
  2543    };
  2544    
  2545    function svgHandler(handler) {
  2546      var that = this;
  2547      return function(evt) {
  2548        var target = evt.target,
  2549            item = target.__data__;
  2550        if (item) {
  2551          item = item.mark ? item : item[0];
  2552          handler.call(that._obj, evt, item);
  2553        }
  2554      };
  2555    }
  2556    
  2557    function eventName(name) {
  2558      var i = name.indexOf(".");
  2559      return i < 0 ? name : name.slice(0,i);
  2560    }
  2561    
  2562    var prototype = handler.prototype;
  2563  
  2564    prototype.initialize = function(el, pad, obj) {
  2565      this._el = d3.select(el).node();
  2566      this._svg = d3.select(el).select("svg.marks").node();
  2567      this._padding = pad;
  2568      this._obj = obj || null;
  2569      return this;
  2570    };
  2571    
  2572    prototype.padding = function(pad) {
  2573      this._padding = pad;
  2574      return this;
  2575    };
  2576    
  2577    prototype.model = function(model) {
  2578      if (!arguments.length) return this._model;
  2579      this._model = model;
  2580      return this;
  2581    };
  2582    
  2583    prototype.handlers = function() {
  2584      var h = this._handlers;
  2585      return vg.keys(h).reduce(function(a, k) {
  2586        return h[k].reduce(function(a, x) { return (a.push(x), a); }, a);
  2587      }, []);
  2588    };
  2589  
  2590    // add an event handler
  2591    prototype.on = function(type, handler) {
  2592      var name = eventName(type),
  2593          h = this._handlers,
  2594          dom = d3.select(this._svg).node();
  2595          
  2596      var x = {
  2597        type: type,
  2598        handler: handler,
  2599        svg: svgHandler.call(this, handler)
  2600      };
  2601      h = h[name] || (h[name] = []);
  2602      h.push(x);
  2603  
  2604      dom.addEventListener(name, x.svg);
  2605      return this;
  2606    };
  2607  
  2608    // remove an event handler
  2609    prototype.off = function(type, handler) {
  2610      var name = eventName(type),
  2611          h = this._handlers[name],
  2612          dom = d3.select(this._svg).node();
  2613      if (!h) return;
  2614      for (var i=h.length; --i>=0;) {
  2615        if (h[i].type !== type) continue;
  2616        if (!handler || h[i].handler === handler) {
  2617          dom.removeEventListener(name, h[i].svg);
  2618          h.splice(i, 1);
  2619        }
  2620      }
  2621      return this;
  2622    };
  2623  
  2624    return handler;
  2625  })();vg.data = {};
  2626  
  2627  vg.data.ingestAll = function(data) {
  2628    return vg.isTree(data)
  2629      ? vg_make_tree(vg.data.ingestTree(data[0], data.children))
  2630      : data.map(vg.data.ingest);
  2631  };
  2632  
  2633  vg.data.ingest = function(datum, index) {
  2634    return {
  2635      data: datum,
  2636      index: index
  2637    };
  2638  };
  2639  
  2640  vg.data.ingestTree = function(node, children) {
  2641    var d = vg.data.ingest(node),
  2642        c = node[children], n, i;
  2643    if (c && (n = c.length)) {
  2644      d.values = Array(n);
  2645      for (i=0; i<n; ++i) {
  2646        d.values[i] = vg.data.ingestTree(c[i], children);
  2647      }
  2648    }
  2649    return d;
  2650  };
  2651  
  2652  
  2653  function vg_make_tree(d) {
  2654    d.__vgtree__ = true;
  2655    d.nodes = function() { return vg_tree_nodes(this, []); };
  2656    return d;
  2657  }
  2658  
  2659  function vg_tree_nodes(root, nodes) {
  2660    var c = root.values,
  2661        n = c ? c.length : 0, i;
  2662    nodes.push(root);
  2663    for (i=0; i<n; ++i) { vg_tree_nodes(c[i], nodes); }
  2664    return nodes;
  2665  }
  2666  
  2667  function vg_data_duplicate(d) {
  2668    var x=d, i, n;
  2669    if (vg.isArray(d)) {
  2670      x = [];
  2671      for (i=0, n=d.length; i<n; ++i) {
  2672        x.push(vg_data_duplicate(d[i]));
  2673      }
  2674    } else if (vg.isObject(d)) {
  2675      x = {};
  2676      for (i in d) {
  2677        x[i] = vg_data_duplicate(d[i]);
  2678      }
  2679    }
  2680    return x;
  2681  }
  2682  
  2683  vg.data.mapper = function(func) {
  2684    return function(data) {
  2685      data.forEach(func);
  2686      return data;
  2687    }
  2688  };
  2689  
  2690  vg.data.size = function(size, group) {
  2691    size = vg.isArray(size) ? size : [0, size];
  2692    size = size.map(function(d) {
  2693      return (typeof d === 'string') ? group[d] : d;
  2694    });
  2695    return size;
  2696  };vg.data.load = function(uri, callback) {
  2697    var url = vg_load_hasProtocol(uri) ? uri : vg.config.baseURL + uri;
  2698    if (vg.config.isNode) {
  2699      // in node.js, consult url and select file or http
  2700      var get = vg_load_isFile(url) ? vg_load_file : vg_load_http;
  2701      get(url, callback);
  2702    } else {
  2703      // in browser, use xhr
  2704      vg_load_xhr(url, callback);
  2705    }  
  2706  };
  2707  
  2708  var vg_load_protocolRE = /^[A-Za-z]+\:\/\//;
  2709  var vg_load_fileProtocol = "file://";
  2710  
  2711  function vg_load_hasProtocol(url) {
  2712    return vg_load_protocolRE.test(url);
  2713  }
  2714  
  2715  function vg_load_isFile(url) {
  2716    return url.indexOf(vg_load_fileProtocol) === 0;
  2717  }
  2718  
  2719  function vg_load_xhr(url, callback) {
  2720    vg.log("LOAD: " + url);
  2721    d3.xhr(url, function(err, resp) {
  2722      if (resp) resp = resp.responseText;
  2723      callback(err, resp);
  2724    });
  2725  }
  2726  
  2727  function vg_load_file(file, callback) {
  2728    vg.log("LOAD FILE: " + file);
  2729    var idx = file.indexOf(vg_load_fileProtocol);
  2730    if (idx >= 0) file = file.slice(vg_load_fileProtocol.length);
  2731    require("fs").readFile(file, callback);
  2732  }
  2733  
  2734  function vg_load_http(url, callback) {
  2735    vg.log("LOAD HTTP: " + url);
  2736  	var req = require("http").request(url, function(res) {
  2737      var pos=0, data = new Buffer(parseInt(res.headers['content-length'],10));
  2738  		res.on("error", function(err) { callback(err, null); });
  2739  		res.on("data", function(x) { x.copy(data, pos); pos += x.length; });
  2740  		res.on("end", function() { callback(null, data); });
  2741  	});
  2742  	req.on("error", function(err) { callback(err); });
  2743  	req.end();
  2744  }vg.data.read = (function() {
  2745    var formats = {},
  2746        parsers = {
  2747          "number": vg.number,
  2748          "boolean": vg.boolean,
  2749          "date": Date.parse
  2750        };
  2751  
  2752    function read(data, format) {
  2753      var type = (format && format.type) || "json";
  2754      data = formats[type](data, format);
  2755      if (format && format.parse) parseValues(data, format.parse);
  2756      return data;
  2757    }
  2758  
  2759    formats.json = function(data, format) {
  2760      var d = JSON.parse(data);
  2761      if (format && format.property) {
  2762        d = vg.accessor(format.property)(d);
  2763      }
  2764      return d;
  2765    };
  2766  
  2767    formats.csv = function(data, format) {
  2768      var d = d3.csv.parse(data);
  2769      return d;
  2770    };
  2771  
  2772    formats.tsv = function(data, format) {
  2773      var d = d3.tsv.parse(data);
  2774      return d;
  2775    };
  2776    
  2777    formats.topojson = function(data, format) {
  2778      if (topojson == null) {
  2779        vg.error("TopoJSON library not loaded.");
  2780        return [];
  2781      }    
  2782      var t = JSON.parse(data), obj = [];
  2783  
  2784      if (format && format.feature) {
  2785        obj = (obj = t.objects[format.feature])
  2786          ? topojson.feature(t, obj).features
  2787          : (vg.error("Invalid TopoJSON object: "+format.feature), []);
  2788      } else if (format && format.mesh) {
  2789        obj = (obj = t.objects[format.mesh])
  2790          ? [topojson.mesh(t, t.objects[format.mesh])]
  2791          : (vg.error("Invalid TopoJSON object: " + format.mesh), []);
  2792      }
  2793      else { vg.error("Missing TopoJSON feature or mesh parameter."); }
  2794  
  2795      return obj;
  2796    };
  2797    
  2798    formats.treejson = function(data, format) {
  2799      var d = [JSON.parse(data)];
  2800      d.__vgtree__ = true;
  2801      d.children = format.children || "children";
  2802      return d;
  2803    };
  2804    
  2805    function parseValues(data, types) {
  2806      var cols = vg.keys(types),
  2807          p = cols.map(function(col) { return parsers[types[col]]; }),
  2808          tree = vg.isTree(data);
  2809      vg_parseArray(tree ? [data] : data, cols, p, tree);
  2810    }
  2811    
  2812    function vg_parseArray(data, cols, p, tree) {
  2813      var d, i, j, len, clen;
  2814      for (i=0, len=data.length; i<len; ++i) {
  2815        d = data[i];
  2816        for (j=0, clen=cols.length; j<clen; ++j) {
  2817          d[cols[j]] = p[j](d[cols[j]]);
  2818        }
  2819        if (tree && d.values) parseCollection(d, cols, p, true);
  2820      }
  2821    }
  2822  
  2823    read.formats = formats;
  2824    read.parse = parseValues;
  2825    return read;
  2826  })();vg.data.array = function() {
  2827    var fields = [];
  2828     
  2829    function array(data) {
  2830      return data.map(function(d) {      
  2831        var list = [];
  2832        for (var i=0, len=fields.length; i<len; ++i) {
  2833          list.push(fields[i](d));
  2834        }
  2835        return list;
  2836      });
  2837    }
  2838    
  2839    array.fields = function(fieldList) {
  2840      fields = vg.array(fieldList).map(vg.accessor);
  2841      return array;
  2842    };
  2843    
  2844    return array;
  2845  };vg.data.copy = function() {
  2846    var from = vg.accessor("data"),
  2847        fields = [],
  2848        as = null;
  2849    
  2850    var copy = vg.data.mapper(function(d) {
  2851      var src = from(d), i, len,
  2852          source = fields,
  2853          target = as || fields;
  2854      for (i=0, len=fields.length; i<len; ++i) {
  2855        d[target[i]] = src[fields[i]];
  2856      }
  2857      return d;
  2858    });
  2859  
  2860    copy.from = function(field) {
  2861      from = vg.accessor(field);
  2862      return copy;
  2863    };
  2864    
  2865    copy.fields = function(fieldList) {
  2866      fields = vg.array(fieldList);
  2867      return copy;
  2868    };
  2869    
  2870    copy.as = function(fieldList) {
  2871      as = vg.array(fieldList);
  2872      return copy;
  2873    };
  2874  
  2875    return copy;
  2876  };vg.data.cross = function() {
  2877    var other = null,
  2878        nodiag = false,
  2879        output = {left:"a", right:"b"};
  2880  
  2881    function cross(data) {
  2882      var result = [],
  2883          data2 = other || data,
  2884          o, i, j, n = data.length;
  2885  
  2886      for (i=0; i<n; ++i) {
  2887        for (j=0; j<n; ++j) {
  2888          if (nodiag && i===j) continue;
  2889          o = {};
  2890          o[output.left] = data[i];
  2891          o[output.right] = data2[j];
  2892          result.push(o);
  2893        }
  2894      }
  2895      return result;
  2896    }
  2897  
  2898    cross["with"] = function(d) {
  2899      other = d;
  2900      return cross;
  2901    };
  2902    
  2903    cross.diagonal = function(x) {
  2904      nodiag = !x;
  2905      return cross;
  2906    };
  2907  
  2908    cross.output = function(map) {
  2909      vg.keys(output).forEach(function(k) {
  2910        if (map[k] !== undefined) { output[k] = map[k]; }
  2911      });
  2912      return cross;
  2913    };
  2914  
  2915    return cross;
  2916  };
  2917  vg.data.facet = function() {
  2918  
  2919    var keys = [],
  2920        sort = null;
  2921  
  2922    function facet(data) {    
  2923      var result = {
  2924            key: "",
  2925            keys: [],
  2926            values: []
  2927          },
  2928          map = {}, 
  2929          vals = result.values,
  2930          obj, klist, kstr, len, i, j, k, kv, cmp;
  2931  
  2932      if (keys.length === 0) {
  2933        // if no keys, skip collation step
  2934        vals.push(obj = {
  2935          key: "", keys: [], index: 0,
  2936          values: sort ? data.slice() : data
  2937        });
  2938        if (sort) sort(obj.values);
  2939        return result;
  2940      }
  2941  
  2942      for (i=0, len=data.length; i<len; ++i) {
  2943        for (k=0, klist=[], kstr=""; k<keys.length; ++k) {
  2944          kv = keys[k](data[i]);
  2945          klist.push(kv);
  2946          kstr += (k>0 ? "|" : "") + String(kv);
  2947        }
  2948        obj = map[kstr];
  2949        if (obj === undefined) {
  2950          vals.push(obj = map[kstr] = {
  2951            key: kstr,
  2952            keys: klist,
  2953            index: vals.length,
  2954            values: []
  2955          });
  2956        }
  2957        obj.values.push(data[i]);
  2958      }
  2959  
  2960      if (sort) {
  2961        for (i=0, len=vals.length; i<len; ++i) {
  2962          sort(vals[i].values);
  2963        }
  2964      }
  2965  
  2966      return result;
  2967    }
  2968    
  2969    facet.keys = function(k) {
  2970      keys = vg.array(k).map(vg.accessor);
  2971      return facet;
  2972    };
  2973    
  2974    facet.sort = function(s) {
  2975      sort = vg.data.sort().by(s);
  2976      return facet;
  2977    };
  2978  
  2979    return facet;
  2980  };vg.data.filter = function() {
  2981  
  2982    var test = null;
  2983  
  2984    function filter(data) {
  2985      return test ? data.filter(test) : data;
  2986    }
  2987    
  2988    filter.test = function(func) {
  2989      test = vg.isFunction(func) ? func : vg.parse.expr(func);
  2990      return filter;
  2991    };
  2992  
  2993    return filter;
  2994  };vg.data.flatten = function() {
  2995      
  2996    function flatten(data) {
  2997      return flat(data, []);
  2998    }
  2999    
  3000    function flat(data, list) {
  3001      if (data.values) {
  3002        for (var i=0, n=data.values.length; i<n; ++i) {
  3003          flat(data.values[i], list);
  3004        }
  3005      } else {
  3006        list.push(data);
  3007      }
  3008      return list;
  3009    }
  3010    
  3011    return flatten;
  3012  };vg.data.fold = function() {
  3013    var fields = [],
  3014        accessors = [],
  3015        output = {
  3016          key: "key",
  3017          value: "value"
  3018        };
  3019  
  3020    function fold(data) {
  3021      var values = [],
  3022          item, i, j, n, m = fields.length;
  3023  
  3024      for (i=0, n=data.length; i<n; ++i) {
  3025        item = data[i];
  3026        for (j=0; j<m; ++j) {
  3027          var o = {
  3028            index: values.length,
  3029            data: item.data
  3030          };
  3031          o[output.key] = fields[j];
  3032          o[output.value] = accessors[j](item);
  3033          values.push(o);
  3034        }
  3035      }
  3036  
  3037      return values;
  3038    }  
  3039  
  3040    fold.fields = function(f) {
  3041      fields = vg.array(f);
  3042      accessors = fields.map(vg.accessor);
  3043      return fold;
  3044    };
  3045  
  3046    fold.output = function(map) {
  3047      vg.keys(output).forEach(function(k) {
  3048        if (map[k] !== undefined) {
  3049          output[k] = map[k];
  3050        }
  3051      });
  3052      return fold;
  3053    };
  3054  
  3055    return fold;
  3056  };vg.data.force = function() {
  3057    var layout = d3.layout.force(),
  3058        links = null,
  3059        linkDistance = 20,
  3060        linkStrength = 1,
  3061        charge = -30,
  3062        iterations = 500,
  3063        size = ["width", "height"],
  3064        params = [
  3065          "friction",
  3066          "theta",
  3067          "gravity",
  3068          "alpha"
  3069        ];
  3070  
  3071    function force(data, db, group) {    
  3072      layout
  3073        .size(vg.data.size(size, group))
  3074        .nodes(data);
  3075        
  3076      if (links && db[links]) {
  3077        layout.links(db[links]);
  3078      }
  3079  
  3080      layout.start();      
  3081      for (var i=0; i<iterations; ++i) {
  3082        layout.tick();
  3083      }
  3084      layout.stop();
  3085      
  3086      return data;
  3087    }
  3088  
  3089    force.links = function(dataSetName) {
  3090      links = dataSetName;
  3091      return force;
  3092    };
  3093    
  3094    force.size = function(sz) {
  3095      size = sz;
  3096      return force;
  3097    };
  3098         
  3099    force.linkDistance = function(field) {
  3100      linkDistance = typeof field === 'number'
  3101        ? field
  3102        : vg.accessor(field);
  3103      layout.linkDistance(linkDistance);
  3104      return force;
  3105    };
  3106  
  3107    force.linkStrength = function(field) {
  3108      linkStrength = typeof field === 'number'
  3109        ? field
  3110        : vg.accessor(field);
  3111      layout.linkStrength(linkStrength);
  3112      return force;
  3113    };
  3114    
  3115    force.charge = function(field) {
  3116      charge = typeof field === 'number'
  3117        ? field
  3118        : vg.accessor(field);
  3119      layout.charge(charge);
  3120      return force;
  3121    };
  3122    
  3123    force.iterations = function(iter) {
  3124      iterations = iter;
  3125      return force;
  3126    };
  3127  
  3128    params.forEach(function(name) {
  3129      force[name] = function(x) {
  3130        layout[name](x);
  3131        return force;
  3132      }
  3133    });
  3134  
  3135    return force;
  3136  };vg.data.formula = (function() {
  3137    
  3138    return function() {
  3139      var field = null,
  3140          expr = vg.identity;
  3141    
  3142      var formula = vg.data.mapper(function(d, i, list) {
  3143        if (field) d[field] = expr.call(null, d, i, list);
  3144        return d;
  3145      });
  3146  
  3147      formula.field = function(name) {
  3148        field = name;
  3149        return formula;
  3150      };
  3151    
  3152      formula.expr = function(func) {
  3153        expr = vg.isFunction(func) ? func : vg.parse.expr(func);
  3154        return formula;
  3155      };
  3156  
  3157      return formula;
  3158    };
  3159  })();vg.data.geo = (function() {
  3160    var params = [
  3161      "center",
  3162      "scale",
  3163      "translate",
  3164      "rotate",
  3165      "precision",
  3166      "clipAngle"
  3167    ];
  3168  
  3169    function geo() {
  3170      var opt = {},
  3171          projection = "mercator",
  3172          func = d3.geo[projection](),
  3173          lat = vg.identity,
  3174          lon = vg.identity,
  3175          output = {
  3176            "x": "x",
  3177            "y": "y"
  3178          };
  3179      
  3180      var map = vg.data.mapper(function(d) {
  3181        var ll = [lon(d), lat(d)],
  3182            xy = func(ll);
  3183        d[output.x] = xy[0];
  3184        d[output.y] = xy[1];
  3185        return d;
  3186      });
  3187  
  3188      map.func = function() {
  3189        return func;
  3190      };
  3191          
  3192      map.projection = function(p) {
  3193        if (projection !== p) {
  3194          projection = p;
  3195          func = d3.geo[projection]();
  3196          for (var name in opt) {
  3197            func[name](opt[name]);
  3198          }
  3199        }
  3200        return map;
  3201      };
  3202  
  3203      params.forEach(function(name) {
  3204        map[name] = function(x) {
  3205          opt[name] = x;
  3206          func[name](x);
  3207          return map;
  3208        }
  3209      });
  3210      
  3211      map.lon = function(field) {
  3212        lon = vg.accessor(field);
  3213        return map;
  3214      };
  3215  
  3216      map.lat = function(field) {
  3217        lat = vg.accessor(field);
  3218        return map;
  3219      };
  3220      
  3221      map.output = function(map) {
  3222        vg.keys(output).forEach(function(k) {
  3223          if (map[k] !== undefined) {
  3224            output[k] = map[k];
  3225          }
  3226        });
  3227        return map;
  3228      };
  3229      
  3230      
  3231      return map;
  3232    };
  3233    
  3234    geo.params = params;
  3235    return geo;
  3236  })();vg.data.geopath = function() {
  3237    var geopath = d3.geo.path().projection(d3.geo.mercator()),
  3238        projection = "mercator",
  3239        geojson = vg.identity,
  3240        opt = {},
  3241        output = {"path": "path"};
  3242  
  3243    var map = vg.data.mapper(function(d) {
  3244      d[output.path] = geopath(geojson(d));
  3245      return d;
  3246    });
  3247    
  3248    map.projection = function(proj) {
  3249      if (projection !== proj) {
  3250        projection = proj;
  3251        var p = d3.geo[projection]();
  3252        for (var name in opt) {
  3253          p[name](opt[name]);
  3254        }
  3255        geopath.projection(p);
  3256      }
  3257      return map;
  3258    };
  3259    
  3260    vg.data.geo.params.forEach(function(name) {
  3261      map[name] = function(x) {
  3262        opt[name] = x;
  3263        (geopath.projection())[name](x);
  3264        return map;
  3265      }
  3266    });
  3267     
  3268    map.value = function(field) {
  3269      geojson = vg.accessor(field);
  3270      return map;
  3271    };
  3272  
  3273    map.output = function(map) {
  3274      vg.keys(output).forEach(function(k) {
  3275        if (map[k] !== undefined) {
  3276          output[k] = map[k];
  3277        }
  3278      });
  3279      return map;
  3280    };
  3281  
  3282    return map;
  3283  };vg.data.link = function() {
  3284    var shape = "line",
  3285        source = vg.accessor("source"),
  3286        target = vg.accessor("target"),
  3287        tension = 0.2,
  3288        output = {"path": "path"};
  3289    
  3290    function line(d) {
  3291      var s = source(d),
  3292          t = target(d);
  3293      return "M" + s.x + "," + s.y 
  3294           + "L" + t.x + "," + t.y;
  3295    }
  3296  
  3297    function curve(d) {
  3298      var s = source(d),
  3299          t = target(d),
  3300          dx = t.x - s.x,
  3301          dy = t.y - s.y,
  3302          ix = tension * (dx + dy),
  3303          iy = tension * (dy - dx);
  3304      return "M" + s.x + "," + s.y
  3305           + "C" + (s.x+ix) + "," + (s.y+iy)
  3306           + " " + (t.x+iy) + "," + (t.y-ix)
  3307           + " " + t.x + "," + t.y;
  3308    }
  3309    
  3310    function diagonalX(d) {
  3311      var s = source(d),
  3312          t = target(d),
  3313          m = (s.x + t.x) / 2;
  3314      return "M" + s.x + "," + s.y
  3315           + "C" + m   + "," + s.y
  3316           + " " + m   + "," + t.y
  3317           + " " + t.x + "," + t.y;
  3318    }
  3319  
  3320    function diagonalY(d) {
  3321      var s = source(d),
  3322          t = target(d),
  3323          m = (s.y + t.y) / 2;
  3324      return "M" + s.x + "," + s.y
  3325           + "C" + s.x + "," + m
  3326           + " " + t.x + "," + m
  3327           + " " + t.x + "," + t.y;
  3328    }
  3329  
  3330    var shapes = {
  3331      line:      line,
  3332      curve:     curve,
  3333      diagonal:  diagonalX,
  3334      diagonalX: diagonalX,
  3335      diagonalY: diagonalY
  3336    };
  3337    
  3338    function link(data) {
  3339      var path = shapes[shape];
  3340          
  3341      data.forEach(function(d) {
  3342        d[output.path] = path(d);
  3343      });
  3344      
  3345      return data;
  3346    }
  3347  
  3348    link.shape = function(val) {
  3349      shape = val;
  3350      return link;
  3351    };
  3352  
  3353    link.tension = function(val) {
  3354      tension = val;
  3355      return link;
  3356    };
  3357    
  3358    link.source = function(field) {
  3359      source = vg.accessor(field);
  3360      return link;
  3361    };
  3362    
  3363    link.target = function(field) {
  3364      target = vg.accessor(field);
  3365      return link;
  3366    };
  3367    
  3368    link.output = function(map) {
  3369      vg.keys(output).forEach(function(k) {
  3370        if (map[k] !== undefined) {
  3371          output[k] = map[k];
  3372        }
  3373      });
  3374      return link;
  3375    };
  3376    
  3377    return link;
  3378  };vg.data.pie = function() {
  3379    var one = function() { return 1; },
  3380        value = one,
  3381        start = 0,
  3382        end = 2 * Math.PI,
  3383        sort = false,
  3384        output = {
  3385          "startAngle": "startAngle",
  3386          "endAngle": "endAngle"
  3387        };
  3388  
  3389    function pie(data) {
  3390      var values = data.map(function(d, i) { return +value(d); }),
  3391          a = start,
  3392          k = (end - start) / d3.sum(values),
  3393          index = d3.range(data.length);
  3394      
  3395      if (sort) {
  3396        index.sort(function(a, b) {
  3397          return values[a] - values[b];
  3398        });
  3399      }
  3400      
  3401      index.forEach(function(i) {
  3402        var d;
  3403        data[i].value = (d = values[i]);
  3404        data[i][output.startAngle] = a;
  3405        data[i][output.endAngle] = (a += d * k);
  3406      });
  3407      
  3408      return data;
  3409    }
  3410  
  3411    pie.sort = function(b) {
  3412      sort = b;
  3413      return pie;
  3414    };
  3415         
  3416    pie.value = function(field) {
  3417      value = field ? vg.accessor(field) : one;
  3418      return pie;
  3419    };
  3420    
  3421    pie.startAngle = function(startAngle) {
  3422      start = Math.PI * startAngle / 180;
  3423      return pie;
  3424    };
  3425    
  3426    pie.endAngle = function(endAngle) {
  3427      end = Math.PI * endAngle / 180;
  3428      return pie;
  3429    };
  3430  
  3431    pie.output = function(map) {
  3432      vg.keys(output).forEach(function(k) {
  3433        if (map[k] !== undefined) {
  3434          output[k] = map[k];
  3435        }
  3436      });
  3437      return pie;
  3438    };
  3439  
  3440    return pie;
  3441  };vg.data.slice = function() {
  3442    var by = null,
  3443        field = vg.accessor("data");
  3444  
  3445    function slice(data) {
  3446      data = vg.values(data);
  3447      
  3448      if (by === "min") {
  3449        data = [data[vg.minIndex(data, field)]];
  3450      } else if (by === "max") {
  3451        data = [data[vg.maxIndex(data, field)]];
  3452      } else if (by === "median") {
  3453        var list = data.slice().sort(function(a,b) {
  3454          a = field(a); b = field(b);
  3455          return a < b ? -1 : a > b ? 1 : 0;
  3456        });
  3457        data = [data[~~(list.length/2)]];
  3458      } else {
  3459        var idx = vg.array(by);
  3460        data = data.slice(idx[0], idx[1]);
  3461      }
  3462      return data;
  3463    }
  3464    
  3465    slice.by = function(x) {
  3466      by = x;
  3467      return slice;
  3468    };
  3469    
  3470    slice.field = function(f) {
  3471      field = vg.accessor(f);
  3472      return slice;
  3473    };
  3474  
  3475    return slice;
  3476  };vg.data.sort = function() {
  3477    var by = null;
  3478  
  3479    function sort(data) {
  3480      data = (vg.isArray(data) ? data : data.values || []);
  3481      data.sort(by);
  3482      for (var i=0, n=data.length; i<n; ++i) data[i].index = i; // re-index
  3483      return data;
  3484    }
  3485    
  3486    sort.by = function(s) {
  3487      by = vg.comparator(s);
  3488      return sort;
  3489    };
  3490  
  3491    return sort;
  3492  };vg.data.stack = function() {
  3493    var layout = d3.layout.stack(),
  3494        point = vg.accessor("index"),
  3495        height = vg.accessor("data"),
  3496        params = ["offset", "order"],
  3497        output = {
  3498          "y0": "y2",
  3499          "y1": "y",
  3500          "cy": "cy"
  3501        };
  3502  
  3503    function stack(data) {
  3504      var out_y0 = output["y0"],
  3505          out_y1 = output["y1"],
  3506          out_cy = output["cy"];
  3507      
  3508      var series = stacks(data);
  3509      if (series.length === 0) return data;
  3510      
  3511      layout.out(function(d, y0, y) {
  3512        if (d.datum) {
  3513          d.datum[out_y0] = y0;
  3514          d.datum[out_y1] = y + y0;
  3515          d.datum[out_cy] = y0 + y/2;
  3516        }
  3517      })(series);
  3518      
  3519      return data;
  3520    }
  3521    
  3522    function stacks(data) {
  3523      var values = vg.values(data),
  3524          points = [], series = [],
  3525          a, i, n, j, m, k, p, v, x;
  3526  
  3527      // exit early if no data
  3528      if (values.length === 0) return series;
  3529  
  3530      // collect and sort data points
  3531      for (i=0, n=values.length; i<n; ++i) {
  3532        a = vg.values(values[i]);
  3533        for (j=0, m=a.length; j<m; ++j) {
  3534          points.push({x:point(a[j]), y:height(a[j]), z:i, datum:a[j]});
  3535        }
  3536        series.push([]);
  3537      }
  3538      points.sort(function(a,b) {
  3539        return a.x<b.x ? -1 : a.x>b.x ? 1 : (a.z<b.z ? -1 : a.z>b.z ? 1 : 0);
  3540      });
  3541  
  3542      // emit data series for stack layout
  3543      for (x=points[0].x, i=0, j=0, k=0, n=points.length; k<n; ++k) {
  3544        p = points[k];    
  3545        if (p.x !== x) {
  3546          while (i < series.length) series[i++].push({x:j, y:0});
  3547          x = p.x; i = 0; j += 1;
  3548        }
  3549        while (p.z > i) series[i++].push({x:j, y:0});
  3550        p.x = j;
  3551        series[i++].push(p);
  3552      }
  3553      while (i < series.length) series[i++].push({x:j, y:0});
  3554  
  3555      return series;
  3556    }
  3557         
  3558    stack.point = function(field) {
  3559      point = vg.accessor(field);
  3560      return stack;
  3561    };
  3562    
  3563    stack.height = function(field) {
  3564      height = vg.accessor(field);
  3565      return stack;
  3566    };
  3567  
  3568    params.forEach(function(name) {
  3569      stack[name] = function(x) {
  3570        layout[name](x);
  3571        return stack;
  3572      }
  3573    });
  3574  
  3575    stack.output = function(map) {
  3576      d3.keys(output).forEach(function(k) {
  3577        if (map[k] !== undefined) {
  3578          output[k] = map[k];
  3579        }
  3580      });
  3581      return stack;
  3582    };
  3583  
  3584    return stack;
  3585  };vg.data.stats = function() {
  3586    var value = vg.accessor("data"),
  3587        assign = false,
  3588        median = false,
  3589        output = {
  3590          "count":    "count",
  3591          "min":      "min",
  3592          "max":      "max",
  3593          "sum":      "sum",
  3594          "mean":     "mean",
  3595          "variance": "variance",
  3596          "stdev":    "stdev",
  3597          "median":   "median"
  3598        };
  3599    
  3600    function reduce(data) {
  3601      var min = +Infinity,
  3602          max = -Infinity,
  3603          sum = 0,
  3604          mean = 0,
  3605          M2 = 0,
  3606          i, len, v, delta;
  3607  
  3608      var list = (vg.isArray(data) ? data : data.values || []).map(value);
  3609      
  3610      // compute aggregates
  3611      for (i=0, len=list.length; i<len; ++i) {
  3612        v = list[i];
  3613        if (v < min) min = v;
  3614        if (v > max) max = v;
  3615        sum += v;
  3616        delta = v - mean;
  3617        mean = mean + delta / (i+1);
  3618        M2 = M2 + delta * (v - mean);
  3619      }
  3620      M2 = M2 / (len - 1);
  3621      
  3622      var o = vg.isArray(data) ? {} : data;
  3623      if (median) {
  3624        list.sort(vg.numcmp);
  3625        i = list.length >> 1;
  3626        o[output.median] = list.length % 2
  3627          ? list[i]
  3628          : (list[i-1] + list[i])/2;
  3629      }
  3630      o[output.count] = len;
  3631      o[output.min] = min;
  3632      o[output.max] = max;
  3633      o[output.sum] = sum;
  3634      o[output.mean] = mean;
  3635      o[output.variance] = M2;
  3636      o[output.stdev] = Math.sqrt(M2);
  3637      
  3638      if (assign) {
  3639        list = (vg.isArray(data) ? data : data.values);
  3640        v = {};
  3641        v[output.count] = len;
  3642        v[output.min] = min;
  3643        v[output.max] = max;
  3644        v[output.sum] = sum;
  3645        v[output.mean] = mean;
  3646        v[output.variance] = M2;
  3647        v[output.stdev] = Math.sqrt(M2);
  3648        if (median) v[output.median] = o[output.median];
  3649        for (i=0, len=list.length; i<len; ++i) {
  3650          list[i].stats = v;
  3651        }
  3652      }
  3653      
  3654      return o;
  3655    }
  3656    
  3657    function stats(data) {
  3658      return (vg.isArray(data) ? [data] : data.values || [])
  3659        .map(reduce); // no pun intended
  3660    }
  3661    
  3662    stats.median = function(bool) {
  3663      median = bool || false;
  3664      return stats;
  3665    };
  3666    
  3667    stats.value = function(field) {
  3668      value = vg.accessor(field);
  3669      return stats;
  3670    };
  3671  
  3672    stats.assign = function(b) {
  3673      assign = b;
  3674      return stats;
  3675    };
  3676    
  3677    stats.output = function(map) {
  3678      vg.keys(output).forEach(function(k) {
  3679        if (map[k] !== undefined) {
  3680          output[k] = map[k];
  3681        }
  3682      });
  3683      return stats;
  3684    };
  3685    
  3686    return stats;
  3687  };vg.data.treemap = function() {
  3688    var layout = d3.layout.treemap()
  3689                   .children(function(d) { return d.values; }),
  3690        value = vg.accessor("data"),
  3691        size = ["width", "height"],
  3692        params = ["round", "sticky", "ratio", "padding"],
  3693        output = {
  3694          "x": "x",
  3695          "y": "y",
  3696          "dx": "width",
  3697          "dy": "height"
  3698        };
  3699  
  3700    function treemap(data, db, group) {
  3701      data = layout
  3702        .size(vg.data.size(size, group))
  3703        .value(value)
  3704        .nodes(vg.isTree(data) ? data.nodes() : data);
  3705      
  3706      var keys = vg.keys(output),
  3707          len = keys.length;
  3708      data.forEach(function(d) {
  3709        var key, val;
  3710        for (var i=0; i<len; ++i) {
  3711          key = keys[i];
  3712          if (key !== output[key]) {
  3713            val = d[key];
  3714            delete d[key];
  3715            d[output[key]] = val;
  3716          }
  3717        }
  3718      });
  3719      
  3720      return data;
  3721    }
  3722  
  3723    treemap.size = function(sz) {
  3724      size = sz;
  3725      return treemap;
  3726    };
  3727  
  3728    treemap.value = function(field) {
  3729      value = vg.accessor(field);
  3730      return treemap;
  3731    };
  3732  
  3733    params.forEach(function(name) {
  3734      treemap[name] = function(x) {
  3735        layout[name](x);
  3736        return treemap;
  3737      }
  3738    });
  3739  
  3740    treemap.output = function(map) {
  3741      vg.keys(output).forEach(function(k) {
  3742        if (map[k] !== undefined) {
  3743          output[k] = map[k];
  3744        }
  3745      });
  3746      return treemap;
  3747    };
  3748  
  3749    return treemap;
  3750  };vg.data.truncate = function() {
  3751    var value = vg.accessor("data"),
  3752        as = "truncate",
  3753        position = "right",
  3754        ellipsis = "...",
  3755        wordBreak = true,
  3756        limit = 100;
  3757    
  3758    var truncate = vg.data.mapper(function(d) {
  3759      var text = vg.truncate(value(d), limit, position, wordBreak, ellipsis);
  3760      return (d[as] = text, d);
  3761    });
  3762  
  3763    truncate.value = function(field) {
  3764      value = vg.accessor(field);
  3765      return truncate;
  3766    };
  3767    
  3768    truncate.output = function(field) {
  3769      as = field;
  3770      return truncate;
  3771    };
  3772  
  3773    truncate.limit = function(len) {
  3774      limit = +len;
  3775      return truncate;
  3776    };
  3777    
  3778    truncate.position = function(pos) {
  3779      position = pos;
  3780      return truncate;
  3781    };
  3782  
  3783    truncate.ellipsis = function(str) {
  3784      ellipsis = str+"";
  3785      return truncate;
  3786    };
  3787  
  3788    truncate.wordbreak = function(b) {
  3789      wordBreak = !!b;
  3790      return truncate;
  3791    };
  3792  
  3793    return truncate;
  3794  };vg.data.unique = function() {
  3795  
  3796    var field = null,
  3797        as = "field";
  3798  
  3799    function unique(data) {
  3800      return vg.unique(data, field)
  3801        .map(function(x) {
  3802          var o = {};
  3803          o[as] = x;
  3804          return o;
  3805        });
  3806    }
  3807    
  3808    unique.field = function(f) {
  3809      field = vg.accessor(f);
  3810      return unique;
  3811    };
  3812    
  3813    unique.as = function(x) {
  3814      as = x;
  3815      return unique;
  3816    };
  3817  
  3818    return unique;
  3819  };vg.data.window = function() {
  3820  
  3821    var size = 2,
  3822        step = 1;
  3823    
  3824    function win(data) {
  3825      data = vg.isArray(data) ? data : data.values || [];
  3826      var runs = [], i, j, n=data.length-size, curr;
  3827      for (i=0; i<=n; i+=step) {
  3828        for (j=0, curr=[]; j<size; ++j) curr.push(data[i+j]);
  3829        runs.push({key: i, values: curr});
  3830      }
  3831      return {values: runs};
  3832    }
  3833    
  3834    win.size = function(n) {
  3835      size = n;
  3836      return win;
  3837    };
  3838    
  3839    win.step = function(n) {
  3840      step = n;
  3841      return win;
  3842    };
  3843  
  3844    return win;
  3845  };vg.data.wordcloud = function() {
  3846    var layout = d3.layout.cloud().size([900, 500]),
  3847        text = vg.accessor("data"),
  3848        size = ["width", "height"],
  3849        fontSize = function() { return 14; },
  3850        rotate = function() { return 0; },
  3851        params = ["font", "fontStyle", "fontWeight", "padding"];
  3852    
  3853    var output = {
  3854      "x": "x",
  3855      "y": "y",
  3856      "size": "fontSize",
  3857      "font": "font",
  3858      "rotate": "angle"
  3859    };
  3860    
  3861    function cloud(data, db, group) {
  3862      function finish(tags, bounds) {
  3863        var size = layout.size(),
  3864            dx = size[0] / 2,
  3865            dy = size[1] / 2,
  3866            keys = vg.keys(output),
  3867            key, d, i, n, k, m = keys.length;
  3868  
  3869        // sort data to match wordcloud order
  3870        data.sort(function(a,b) {
  3871          return fontSize(b) - fontSize(a);
  3872        });
  3873  
  3874        for (i=0, n=tags.length; i<n; ++i) {
  3875          d = data[i];
  3876          for (k=0; k<m; ++k) {
  3877            key = keys[k];
  3878            d[output[key]] = tags[i][key];
  3879            if (key === "x") d[output.x] += dx;
  3880            if (key === "y") d[output.y] += dy;
  3881          }
  3882        }
  3883      }
  3884      
  3885      layout
  3886        .size(vg.data.size(size, group))
  3887        .text(text)
  3888        .fontSize(fontSize)
  3889        .rotate(rotate)
  3890        .words(data)
  3891        .on("end", finish)
  3892        .start();
  3893      return data;
  3894    }
  3895  
  3896    cloud.text = function(field) {
  3897      text = vg.accessor(field);
  3898      return cloud;
  3899    };
  3900    
  3901    cloud.size = function(sz) {
  3902      size = sz;
  3903      return cloud;
  3904    };
  3905           
  3906    cloud.fontSize = function(field) {
  3907      fontSize = vg.accessor(field);
  3908      return cloud;
  3909    };
  3910    
  3911    cloud.rotate = function(x) {
  3912      var v;
  3913      if (vg.isObject(x) && !Array.isArray(x)) {
  3914        if (x.random !== undefined) {
  3915          v = (v = x.random) ? vg.array(v) : [0];
  3916          rotate = function() {
  3917            return v[~~(Math.random()*v.length-0.00001)];
  3918          };
  3919        } else if (x.alternate !== undefined) {
  3920          v = (v = x.alternate) ? vg.array(v) : [0];
  3921          rotate = function(d, i) {
  3922            return v[i % v.length];
  3923          };
  3924        }
  3925      } else {
  3926        rotate = vg.accessor(field);
  3927      }
  3928      return cloud;
  3929    };
  3930  
  3931    params.forEach(function(name) {
  3932      cloud[name] = function(x) {
  3933        layout[name](x);
  3934        return cloud;
  3935      }
  3936    });
  3937  
  3938    cloud.output = function(map) {
  3939      vg.keys(output).forEach(function(k) {
  3940        if (map[k] !== undefined) {
  3941          output[k] = map[k];
  3942        }
  3943      });
  3944      return cloud;
  3945    };
  3946    
  3947    return cloud;
  3948  };vg.data.zip = function() {
  3949    var z = null,
  3950        as = "zip",
  3951        key = vg.accessor("data"),
  3952        defaultValue = undefined,
  3953        withKey = null;
  3954  
  3955    function zip(data, db) {
  3956      var zdata = db[z], zlen = zdata.length, v, d, i, len, map;
  3957      
  3958      if (withKey) {
  3959        map = {};
  3960        zdata.forEach(function(s) { map[withKey(s)] = s; });
  3961      }
  3962      
  3963      for (i=0, len=data.length; i<len; ++i) {
  3964        d = data[i];
  3965        d[as] = map
  3966          ? ((v=map[key(d)]) != null ? v : defaultValue)
  3967          : zdata[i % zlen];
  3968      }
  3969      
  3970      return data;
  3971    }
  3972  
  3973    zip["with"] = function(d) {
  3974      z = d;
  3975      return zip;
  3976    };
  3977    
  3978    zip["default"] = function(d) {
  3979      defaultValue = d;
  3980      return zip;
  3981    };
  3982  
  3983    zip.as = function(name) {
  3984      as = name;
  3985      return zip;
  3986    };
  3987  
  3988    zip.key = function(k) {
  3989      key = vg.accessor(k);
  3990      return zip;
  3991    };
  3992  
  3993    zip.withKey = function(k) {
  3994      withKey = vg.accessor(k);
  3995      return zip;
  3996    };
  3997  
  3998    return zip;
  3999  };vg.parse = {};vg.parse.axes = (function() {
  4000    var ORIENT = {
  4001      "x":      "bottom",
  4002      "y":      "left",
  4003      "top":    "top",
  4004      "bottom": "bottom",
  4005      "left":   "left",
  4006      "right":  "right"
  4007    };
  4008  
  4009    function axes(spec, axes, scales) {
  4010      (spec || []).forEach(function(def, index) {
  4011        axes[index] = axes[index] || vg.scene.axis();
  4012        axis(def, index, axes[index], scales);
  4013      });
  4014    };
  4015  
  4016    function axis(def, index, axis, scales) {
  4017      // axis scale
  4018      if (def.scale !== undefined) {
  4019        axis.scale(scales[def.scale]);
  4020      }
  4021  
  4022      // axis orientation
  4023      axis.orient(def.orient || ORIENT[def.type]);
  4024      // axis offset
  4025      axis.offset(def.offset || 0);
  4026      // axis layer
  4027      axis.layer(def.layer || "front");
  4028      // axis grid lines
  4029      axis.grid(def.grid || false);
  4030      // axis title
  4031      axis.title(def.title || null);
  4032      // axis title offset
  4033      axis.titleOffset(def.titleOffset != null
  4034        ? def.titleOffset : vg.config.axis.titleOffset);
  4035      // axis values
  4036      axis.tickValues(def.values || null);
  4037      // axis label formatting
  4038      axis.tickFormat(def.format ? d3.format(def.format) : null);
  4039      // axis tick subdivision
  4040      axis.tickSubdivide(def.subdivide || 0);
  4041      // axis tick padding
  4042      axis.tickPadding(def.tickPadding || vg.config.axis.padding);
  4043  
  4044      // axis tick size(s)
  4045      var size = [];
  4046      if (def.tickSize !== undefined) {
  4047        for (var i=0; i<3; ++i) size.push(def.tickSize);
  4048      } else {
  4049        var ts = vg.config.axis.tickSize;
  4050        size = [ts, ts, ts];
  4051      }
  4052      if (def.tickSizeMajor != null) size[0] = def.tickSizeMajor;
  4053      if (def.tickSizeMinor != null) size[1] = def.tickSizeMinor;
  4054      if (def.tickSizeEnd   != null) size[2] = def.tickSizeEnd;
  4055      if (size.length) {
  4056        axis.tickSize.apply(axis, size);
  4057      }
  4058  
  4059      // tick arguments
  4060      if (def.ticks != null) {
  4061        var ticks = vg.isArray(def.ticks) ? def.ticks : [def.ticks];
  4062        axis.ticks.apply(axis, ticks);
  4063      } else {
  4064        axis.ticks(vg.config.axis.ticks);
  4065      }
  4066      
  4067      // style properties
  4068      var p = def.properties;
  4069      if (p && p.ticks) {
  4070        axis.majorTickProperties(p.majorTicks
  4071          ? vg.extend({}, p.ticks, p.majorTicks) : p.ticks);
  4072        axis.minorTickProperties(p.minorTicks
  4073          ? vg.extend({}, p.ticks, p.minorTicks) : p.ticks);
  4074      } else {
  4075        axis.majorTickProperties(p && p.majorTicks || {});
  4076        axis.minorTickProperties(p && p.minorTicks || {});
  4077      }
  4078      axis.tickLabelProperties(p && p.labels || {});
  4079      axis.titleProperties(p && p.title || {});
  4080      axis.gridLineProperties(p && p.grid || {});
  4081      axis.domainProperties(p && p.axis || {});
  4082    }
  4083    
  4084    return axes;
  4085  })();vg.parse.data = function(spec, callback) {
  4086    var model = {
  4087      defs: spec,
  4088      load: {},
  4089      flow: {},
  4090      source: {}
  4091    };
  4092  
  4093    var count = 0;
  4094    
  4095    function load(d) {
  4096      return function(error, data) {
  4097        if (error) {
  4098          vg.error("LOADING FAILED: " + d.url);
  4099        } else {
  4100          model.load[d.name] = vg.data.read(data.toString(), d.format);
  4101        }
  4102        if (--count === 0) callback();
  4103      }
  4104    }
  4105    
  4106    (spec || []).forEach(function(d) {
  4107      if (d.url) {
  4108        count += 1;
  4109        vg.data.load(d.url, load(d)); 
  4110      }
  4111       
  4112      if (d.values) {
  4113        if (d.format && d.format.parse) {
  4114          // run specified value parsers
  4115          vg.data.read.parse(d.values, d.format.parse);
  4116        }
  4117        model.load[d.name] = d.values;
  4118      }
  4119      
  4120      if (d.source) {
  4121        var list = model.source[d.source] || (model.source[d.source] = []);
  4122        list.push(d.name);
  4123      }
  4124      
  4125      if (d.transform) {
  4126        model.flow[d.name] = vg.parse.dataflow(d);
  4127      }
  4128    });
  4129    
  4130    if (count === 0) setTimeout(callback, 1);
  4131    return model;
  4132  };vg.parse.dataflow = function(def) {
  4133    var tx = (def.transform || []).map(vg.parse.transform);
  4134    return !tx.length ? vg.identity :
  4135      function(data, db, group) {
  4136        return tx.reduce(function(d,t) { return t(d, db, group); }, data);
  4137      };
  4138  };vg.parse.expr = (function() {
  4139    
  4140    var CONSTANT = {
  4141    	"E":       "Math.E",
  4142    	"LN2":     "Math.LN2",
  4143    	"LN10":    "Math.LN10",
  4144    	"LOG2E":   "Math.LOG2E",
  4145    	"LOG10E":  "Math.LOG10E",
  4146    	"PI":      "Math.PI",
  4147    	"SQRT1_2": "Math.SQRT1_2",
  4148    	"SQRT2":   "Math.SQRT2"
  4149    };
  4150  
  4151    var FUNCTION = {
  4152    	"abs":    "Math.abs",
  4153    	"acos":   "Math.acos",
  4154    	"asin":   "Math.asin",
  4155    	"atan":   "Math.atan",
  4156    	"atan2":  "Math.atan2",
  4157    	"ceil":   "Math.ceil",
  4158    	"cos":    "Math.cos",
  4159    	"exp":    "Math.exp",
  4160    	"floor":  "Math.floor",
  4161    	"log":    "Math.log",
  4162    	"max":    "Math.max",
  4163    	"min":    "Math.min",
  4164    	"pow":    "Math.pow",
  4165    	"random": "Math.random",
  4166    	"round":  "Math.round",
  4167    	"sin":    "Math.sin",
  4168    	"sqrt":   "Math.sqrt",
  4169    	"tan":    "Math.tan"
  4170    };
  4171    
  4172    var lexer = /([\"\']|[\=\<\>\~\&\|\?\:\+\-\/\*\%\!\^\,\;\[\]\{\}\(\) ]+)/;
  4173        
  4174    return function(x) {
  4175      var tokens = x.split(lexer),
  4176          t, v, i, n, sq, dq;
  4177  
  4178      for (sq=0, dq=0, i=0, n=tokens.length; i<n; ++i) {
  4179        var t = tokens[i];
  4180        if (t==="'") { if (!dq) sq = !sq; continue; }
  4181        if (t==='"') { if (!sq) dq = !dq; continue; }
  4182        if (dq || sq) continue;
  4183        if (CONSTANT[t]) {
  4184          tokens[i] = CONSTANT[t];
  4185        }
  4186        if (FUNCTION[t] && (v=tokens[i+1]) && v[0]==="(") {
  4187          tokens[i] = FUNCTION[t];
  4188        }
  4189      }
  4190      
  4191      return Function("d", "index", "data", "return ("+tokens.join("")+");");
  4192    };
  4193    
  4194  })();vg.parse.legends = (function() {
  4195  
  4196    function legends(spec, legends, scales) {
  4197      (spec || []).forEach(function(def, index) {
  4198        legends[index] = legends[index] || vg.scene.legend();
  4199        legend(def, index, legends[index], scales);
  4200      });
  4201    };
  4202  
  4203    function legend(def, index, legend, scales) {
  4204      // legend scales
  4205      legend.size  (def.size   ? scales[def.size]   : null);
  4206      legend.shape (def.shape  ? scales[def.shape]  : null);
  4207      legend.fill  (def.fill   ? scales[def.fill]   : null);
  4208      legend.stroke(def.stroke ? scales[def.stroke] : null);
  4209  
  4210      // legend orientation
  4211      if (def.orient) legend.orient(def.orient);
  4212  
  4213      // legend offset
  4214      if (def.offset != null) legend.offset(def.offset);
  4215  
  4216      // legend title
  4217      legend.title(def.title || null);
  4218  
  4219      // legend values
  4220      legend.values(def.values || null);
  4221  
  4222      // legend label formatting
  4223      legend.format(def.format !== undefined ? d3.format(def.format) : null);
  4224  
  4225      // style properties
  4226      var p = def.properties;
  4227      legend.titleProperties(p && p.title || {});
  4228      legend.labelProperties(p && p.labels || {});
  4229      legend.legendProperties(p && p.legend || {});
  4230      legend.symbolProperties(p && p.symbols || {});
  4231      legend.gradientProperties(p && p.gradient || {});
  4232    }
  4233    
  4234    return legends;
  4235  })();vg.parse.mark = function(mark) {
  4236    var props = mark.properties,
  4237        group = mark.marks;
  4238    
  4239    // parse mark property definitions
  4240    vg.keys(props).forEach(function(k) {
  4241      props[k] = vg.parse.properties(mark.type, props[k]);
  4242    });
  4243    // parse delay function
  4244    if (mark.delay) {
  4245      mark.delay = vg.parse.properties(mark.type, {delay: mark.delay});
  4246    }
  4247        
  4248    // parse mark data definition
  4249    if (mark.from) {
  4250      var name = mark.from.data,
  4251          tx = vg.parse.dataflow(mark.from);
  4252      mark.from = function(db, group, parentData) {
  4253        var data = vg.scene.data(name ? db[name] : null, parentData);
  4254        return tx(data, db, group);
  4255      };
  4256    }
  4257    
  4258    // recurse if group type
  4259    if (group) {
  4260      mark.marks = group.map(vg.parse.mark);
  4261    }
  4262        
  4263    return mark;
  4264  };vg.parse.marks = function(spec, width, height) {
  4265    return {
  4266      type: "group",
  4267      width: width,
  4268      height: height,
  4269      scales: spec.scales || [],
  4270      axes: spec.axes || [],
  4271      legends: spec.legends || [],
  4272      marks: (spec.marks || []).map(vg.parse.mark)
  4273    };
  4274  };vg.parse.padding = function(pad) {
  4275    if (pad == null) return "auto";
  4276    else if (vg.isString(pad)) return pad==="strict" ? "strict" : "auto";
  4277    else if (vg.isObject(pad)) return pad;
  4278    var p = vg.isNumber(pad) ? pad : 20;
  4279    return {top:p, left:p, right:p, bottom:p};
  4280  };
  4281  vg.parse.properties = (function() {
  4282    function compile(mark, spec) {
  4283      var code = "",
  4284          names = vg.keys(spec),
  4285          i, len, name, ref, vars = {};
  4286          
  4287      code += "var o = trans ? {} : item;\n"
  4288      
  4289      for (i=0, len=names.length; i<len; ++i) {
  4290        ref = spec[name = names[i]];
  4291        code += (i > 0) ? "\n  " : "  ";
  4292        code += "o."+name+" = "+valueRef(name, ref)+";";
  4293        vars[name] = true;
  4294      }
  4295      
  4296      if (vars.x2) {
  4297        if (vars.x) {
  4298          code += "\n  if (o.x > o.x2) { "
  4299                + "var t = o.x; o.x = o.x2; o.x2 = t; };";
  4300          code += "\n  o.width = (o.x2 - o.x);";
  4301        } else if (vars.width && !vars.x1) {
  4302          code += "\n  o.x = (o.x2 - o.width);";
  4303        } 
  4304      }
  4305  
  4306      if (vars.y2) {
  4307        if (vars.y) {
  4308          code += "\n  if (o.y > o.y2) { "
  4309                + "var t = o.y; o.y = o.y2; o.y2 = t; };";
  4310          code += "\n  o.height = (o.y2 - o.y);";
  4311        } else if (vars.height && !vars.y1) {
  4312          code += "\n  o.y = (o.y2 - o.height);";
  4313        }
  4314      }
  4315      
  4316      if (hasPath(mark, vars)) {
  4317        code += "\n  if (o['path:parsed']) o['path:parsed'] = null;"
  4318      }
  4319      code += "\n  if (trans) trans.interpolate(item, o);";
  4320  
  4321      try {
  4322        return Function("item", "group", "trans", code);
  4323      } catch (e) {
  4324        vg.error(e);
  4325        vg.log(code);
  4326      }
  4327    }
  4328    
  4329    function hasPath(mark, vars) {
  4330      return vars.path ||
  4331        ((mark==="area" || mark==="line") &&
  4332          (vars.x || vars.x2 || vars.width ||
  4333           vars.y || vars.y2 || vars.height ||
  4334           vars.tension || vars.interpolate));
  4335    }
  4336    
  4337    var GROUP_VARS = {
  4338      "width": 1,
  4339      "height": 1,
  4340      "mark.group.width": 1,
  4341      "mark.group.height": 1
  4342    };
  4343  
  4344    function valueRef(name, ref) {
  4345      if (ref == null) return null;
  4346      var isColor = name==="fill" || name==="stroke";
  4347  
  4348      if (isColor) {
  4349        if (ref.c) {
  4350          return colorRef("hcl", ref.h, ref.c, ref.l);
  4351        } else if (ref.h || ref.s) {
  4352          return colorRef("hsl", ref.h, ref.s, ref.l);
  4353        } else if (ref.l || ref.a) {
  4354          return colorRef("lab", ref.l, ref.a, ref.b);
  4355        } else if (ref.r || ref.g || ref.b) {
  4356          return colorRef("rgb", ref.r, ref.g, ref.b);
  4357        }
  4358      }
  4359  
  4360      // initialize value
  4361      var val = "item.datum.data";
  4362      if (ref.value !== undefined) {
  4363        val = vg.str(ref.value);
  4364      }
  4365  
  4366      // get field reference for enclosing group
  4367      if (ref.group != null) {
  4368        var grp = "";
  4369        if (vg.isString(ref.group)) {
  4370          grp = GROUP_VARS[ref.group]
  4371            ? "group." + ref.group
  4372            : "group.datum["+vg.field(ref.group).map(vg.str).join("][")+"]";
  4373        }
  4374      }
  4375  
  4376      // get data field value
  4377      if (ref.field != null) {
  4378        if (vg.isString(ref.field)) {
  4379          val = "item.datum["+vg.field(ref.field).map(vg.str).join("][")+"]";
  4380          if (ref.group != null) { val = grp+"["+val+"]"; }
  4381        } else {
  4382          val = "this.accessor(group.datum["
  4383              + vg.field(ref.field.group).map(vg.str).join("][")
  4384              + "])(item.datum.data)";
  4385        }
  4386      } else if (ref.group != null) {
  4387        val = grp;
  4388      }
  4389  
  4390      // run through scale function
  4391      if (ref.scale != null) {
  4392        var scale = vg.isString(ref.scale)
  4393          ? vg.str(ref.scale)
  4394          : (ref.scale.group ? "group" : "item")
  4395            + ".datum[" + vg.str(ref.scale.group || ref.scale.field) + "]";
  4396        scale = "group.scales[" + scale + "]";
  4397        val = scale + (ref.band ? ".rangeBand()" : "("+val+")");
  4398      }
  4399      
  4400      // multiply, offset, return value
  4401      val = "(" + (ref.mult?(vg.number(ref.mult)+" * "):"") + val + ")"
  4402        + (ref.offset ? " + " + vg.number(ref.offset) : "");
  4403      if (isColor) val = '('+val+')+""';
  4404      return val;
  4405    }
  4406    
  4407    function colorRef(type, x, y, z) {
  4408      var xx = x ? valueRef("", x) : vg.config.color[type][0],
  4409          yy = y ? valueRef("", y) : vg.config.color[type][1],
  4410          zz = z ? valueRef("", z) : vg.config.color[type][2];
  4411      return "(this.d3." + type + "(" + [xx,yy,zz].join(",") + ') + "")';
  4412    }
  4413    
  4414    return compile;
  4415  })();vg.parse.scales = (function() {
  4416    var LINEAR = "linear",
  4417        ORDINAL = "ordinal",
  4418        LOG = "log",
  4419        POWER = "pow",
  4420        TIME = "time",
  4421        GROUP_PROPERTY = {width: 1, height: 1};
  4422  
  4423    function scales(spec, scales, db, group) {
  4424      return (spec || []).reduce(function(o, def) {
  4425        var name = def.name, prev = name + ":prev";
  4426        o[name] = scale(def, o[name], db, group);
  4427        o[prev] = o[prev] || o[name];
  4428        return o;
  4429      }, scales || {});
  4430    }
  4431  
  4432    function scale(def, scale, db, group) {
  4433      var s = instance(def, scale),
  4434          m = s.type===ORDINAL ? ordinal : quantitative,
  4435          rng = range(def, group),
  4436          data = vg.values(group.datum);
  4437  
  4438      m(def, s, rng, db, data);
  4439      return s;
  4440    }
  4441  
  4442    function instance(def, scale) {
  4443      var type = def.type || LINEAR;
  4444      if (!scale || type !== scale.type) {
  4445        var ctor = vg.config.scale[type] || d3.scale[type];
  4446        if (!ctor) vg.error("Unrecognized scale type: " + type);
  4447        (scale = ctor()).type = scale.type || type;
  4448        scale.scaleName = def.name;
  4449      }
  4450      return scale;
  4451    }
  4452  
  4453    function ordinal(def, scale, rng, db, data) {
  4454      var domain, refs, values, str;
  4455      
  4456      // domain
  4457      domain = def.domain;
  4458      if (vg.isArray(domain)) {
  4459        scale.domain(domain);
  4460      } else if (vg.isObject(domain)) {
  4461        refs = def.domain.fields || vg.array(def.domain);
  4462        values = refs.reduce(function(values, r) {        
  4463          var dat = vg.values(db[r.data] || data),
  4464              get = vg.accessor(vg.isString(r.field)
  4465                ? r.field : "data." + vg.accessor(r.field.group)(data));
  4466          return vg.unique(dat, get, values);
  4467        }, []);
  4468        if (def.sort) values.sort(vg.cmp);
  4469        scale.domain(values);
  4470      }
  4471  
  4472      // range
  4473      str = typeof rng[0] === 'string';
  4474      if (str || rng.length > 2) {
  4475        scale.range(rng); // color or shape values
  4476      } else if (def.points) {
  4477        scale.rangePoints(rng, def.padding||0);
  4478      } else if (def.round || def.round===undefined) {
  4479        scale.rangeRoundBands(rng, def.padding||0);
  4480      } else {
  4481        scale.rangeBands(rng, def.padding||0);
  4482      }
  4483    }
  4484  
  4485    function quantitative(def, scale, rng, db, data) {
  4486      var domain, refs, interval, z;
  4487  
  4488      // domain
  4489      domain = [null, null];
  4490      function extract(ref, min, max, z) {
  4491        var dat = vg.values(db[ref.data] || data);
  4492        var fields = vg.array(ref.field).map(function(f) {
  4493          return vg.isString(f) ? f
  4494            : "data." + vg.accessor(f.group)(data);
  4495        });
  4496        
  4497        fields.forEach(function(f,i) {
  4498          f = vg.accessor(f);
  4499          if (min) domain[0] = d3.min([domain[0], d3.min(dat, f)]);
  4500          if (max) domain[z] = d3.max([domain[z], d3.max(dat, f)]);
  4501        });
  4502      }
  4503      if (def.domain !== undefined) {
  4504        if (vg.isArray(def.domain)) {
  4505          domain = def.domain.slice();
  4506        } else if (vg.isObject(def.domain)) {
  4507          refs = def.domain.fields || vg.array(def.domain);
  4508          refs.forEach(function(r) { extract(r,1,1,1); });
  4509        } else {
  4510          domain = def.domain;
  4511        }
  4512      }
  4513      z = domain.length - 1;
  4514      if (def.domainMin !== undefined) {
  4515        if (vg.isObject(def.domainMin)) {
  4516          domain[0] = null;
  4517          refs = def.domainMin.fields || vg.array(def.domainMin);
  4518          refs.forEach(function(r) { extract(r,1,0,z); });
  4519        } else {
  4520          domain[0] = def.domainMin;
  4521        }
  4522      }
  4523      if (def.domainMax !== undefined) {
  4524        if (vg.isObject(def.domainMax)) {
  4525          domain[z] = null;
  4526          refs = def.domainMax.fields || vg.array(def.domainMax);
  4527          refs.forEach(function(r) { extract(r,0,1,z); });
  4528        } else {
  4529          domain[z] = def.domainMax;
  4530        }
  4531      }
  4532      if (def.type !== LOG && def.type !== TIME && (def.zero || def.zero===undefined)) {
  4533        domain[0] = Math.min(0, domain[0]);
  4534        domain[z] = Math.max(0, domain[z]);
  4535      }
  4536      scale.domain(domain);
  4537  
  4538      // range
  4539      // vertical scales should flip by default, so use XOR here
  4540      if (def.range === "height") rng = rng.reverse();
  4541      scale[def.round && scale.rangeRound ? "rangeRound" : "range"](rng);
  4542  
  4543      if (def.exponent && def.type===POWER) scale.exponent(def.exponent);
  4544      if (def.clamp) scale.clamp(true);
  4545      if (def.nice) {
  4546        if (def.type === TIME) {
  4547          interval = d3.time[def.nice];
  4548          if (!interval) vg.error("Unrecognized interval: " + interval);
  4549          scale.nice(interval);
  4550        } else {
  4551          scale.nice();
  4552        }
  4553      }
  4554    }
  4555  
  4556    function range(def, group) {
  4557      var rng = [null, null];
  4558  
  4559      if (def.range !== undefined) {
  4560        if (typeof def.range === 'string') {
  4561          if (GROUP_PROPERTY[def.range]) {
  4562            rng = [0, group[def.range]];
  4563          } else if (vg.config.range[def.range]) {
  4564            rng = vg.config.range[def.range];
  4565          } else {
  4566            vg.error("Unrecogized range: "+def.range);
  4567            return rng;
  4568          }
  4569        } else if (vg.isArray(def.range)) {
  4570          rng = def.range;
  4571        } else {
  4572          rng = [0, def.range];
  4573        }
  4574      }
  4575      if (def.rangeMin !== undefined) {
  4576        rng[0] = def.rangeMin;
  4577      }
  4578      if (def.rangeMax !== undefined) {
  4579        rng[rng.length-1] = def.rangeMax;
  4580      }
  4581      
  4582      if (def.reverse !== undefined) {
  4583        var rev = def.reverse;
  4584        if (vg.isObject(rev)) {
  4585          rev = vg.accessor(rev.field)(group.datum);
  4586        }
  4587        if (rev) rng = rng.reverse();
  4588      }
  4589      
  4590      return rng;
  4591    }
  4592  
  4593    return scales;
  4594  })();
  4595  vg.parse.spec = function(spec, callback, viewFactory) {
  4596    
  4597    viewFactory = viewFactory || vg.ViewFactory;
  4598    
  4599    function parse(spec) {
  4600      // protect against subsequent spec modification
  4601      spec = vg.duplicate(spec);
  4602      
  4603      var width = spec.width || 500,
  4604          height = spec.height || 500,
  4605          viewport = spec.viewport || null;
  4606      
  4607      var defs = {
  4608        width: width,
  4609        height: height,
  4610        viewport: viewport,
  4611        padding: vg.parse.padding(spec.padding),
  4612        marks: vg.parse.marks(spec, width, height),
  4613        data: vg.parse.data(spec.data, function() { callback(viewConstructor); })
  4614      };
  4615      
  4616      var viewConstructor = viewFactory(defs);
  4617    }
  4618    
  4619    vg.isObject(spec) ? parse(spec) :
  4620      d3.json(spec, function(error, json) {
  4621        error ? vg.error(error) : parse(json);
  4622      });
  4623  };vg.parse.transform = function(def) {
  4624    var tx = vg.data[def.type]();
  4625        
  4626    vg.keys(def).forEach(function(k) {
  4627      if (k === 'type') return;
  4628      (tx[k])(def[k]);
  4629    });
  4630    
  4631    return tx;
  4632  };vg.scene = {};
  4633  
  4634  vg.scene.GROUP  = "group",
  4635  vg.scene.ENTER  = 0,
  4636  vg.scene.UPDATE = 1,
  4637  vg.scene.EXIT   = 2;
  4638  
  4639  vg.scene.DEFAULT_DATA = {"sentinel":1}
  4640  
  4641  vg.scene.data = function(data, parentData) {
  4642    var DEFAULT = vg.scene.DEFAULT_DATA;
  4643  
  4644    // if data is undefined, inherit or use default
  4645    data = vg.values(data || parentData || [DEFAULT]);
  4646  
  4647    // if inheriting default data, ensure its in an array
  4648    if (data === DEFAULT) data = [DEFAULT];
  4649    
  4650    return data;
  4651  };
  4652  
  4653  vg.scene.fontString = function(o) {
  4654    return (o.fontStyle ? o.fontStyle + " " : "")
  4655      + (o.fontVariant ? o.fontVariant + " " : "")
  4656      + (o.fontWeight ? o.fontWeight + " " : "")
  4657      + (o.fontSize != null ? o.fontSize : vg.config.render.fontSize) + "px "
  4658      + (o.font || vg.config.render.font);
  4659  };vg.scene.Item = (function() {
  4660    function item(mark) {
  4661      this.mark = mark;
  4662    }
  4663    
  4664    var prototype = item.prototype;
  4665  
  4666    prototype.hasPropertySet = function(name) {
  4667      var props = this.mark.def.properties;
  4668      return props && props[name] != null;
  4669    };
  4670  
  4671    prototype.cousin = function(offset, index) {
  4672      if (offset === 0) return this;
  4673      offset = offset || -1;
  4674      var mark = this.mark,
  4675          group = mark.group,
  4676          iidx = index==null ? mark.items.indexOf(this) : index,
  4677          midx = group.items.indexOf(mark) + offset;
  4678      return group.items[midx].items[iidx];
  4679    };
  4680    
  4681    prototype.sibling = function(offset) {
  4682      if (offset === 0) return this;
  4683      offset = offset || -1;
  4684      var mark = this.mark,
  4685          iidx = mark.items.indexOf(this) + offset;
  4686      return mark.items[iidx];
  4687    };
  4688    
  4689    prototype.remove = function() {
  4690      var item = this,
  4691          list = item.mark.items,
  4692          i = list.indexOf(item);
  4693      if (i >= 0) (i===list.length-1) ? list.pop() : list.splice(i, 1);
  4694      return item;
  4695    };
  4696    
  4697    return item;
  4698  })();
  4699  
  4700  vg.scene.item = function(mark) {
  4701    return new vg.scene.Item(mark);
  4702  };vg.scene.visit = function(node, func) {
  4703    var i, n, items;
  4704    if (func(node)) return true;
  4705    if (items = node.items) {
  4706      for (i=0, n=items.length; i<n; ++i) {
  4707        if (vg.scene.visit(items[i], func)) return true;
  4708      }
  4709    }
  4710  };vg.scene.build = (function() {
  4711    var GROUP  = vg.scene.GROUP,
  4712        ENTER  = vg.scene.ENTER,
  4713        UPDATE = vg.scene.UPDATE,
  4714        EXIT   = vg.scene.EXIT,
  4715        DEFAULT= {"sentinel":1};
  4716    
  4717    function build(def, db, node, parentData) {
  4718      var data = vg.scene.data(
  4719        def.from ? def.from(db, node, parentData) : null,
  4720        parentData);
  4721      
  4722      // build node and items
  4723      node = buildNode(def, node);
  4724      node.items = buildItems(def, data, node);
  4725      buildTrans(def, node);
  4726      
  4727      // recurse if group
  4728      if (def.type === GROUP) {
  4729        buildGroup(def, db, node);
  4730      }
  4731      
  4732      return node;
  4733    };
  4734    
  4735    function buildNode(def, node) {
  4736      node = node || {};
  4737      node.def = def;
  4738      node.marktype = def.type;
  4739      node.interactive = !(def.interactive === false);
  4740      return node;
  4741    }
  4742    
  4743    function buildItems(def, data, node) {
  4744      var keyf = keyFunction(def.key),
  4745          prev = node.items || [],
  4746          next = [],
  4747          map = {},
  4748          i, key, len, item, datum, enter;
  4749  
  4750      for (i=0, len=prev.length; i<len; ++i) {
  4751        item = prev[i];
  4752        item.status = EXIT;
  4753        if (keyf) map[item.key] = item;
  4754      }
  4755      
  4756      for (i=0, len=data.length; i<len; ++i) {
  4757        datum = data[i];
  4758        key = i;
  4759        item = keyf ? map[key = keyf(datum)] : prev[i];
  4760        enter = item ? false : (item = vg.scene.item(node), true);
  4761        item.status = enter ? ENTER : UPDATE;
  4762        item.datum = datum;
  4763        item.key = key;
  4764        next.push(item);
  4765      }
  4766  
  4767      for (i=0, len=prev.length; i<len; ++i) {
  4768        item = prev[i];
  4769        if (item.status === EXIT) {
  4770          item.key = keyf ? item.key : next.length;
  4771          next.push(item);
  4772        }
  4773      }
  4774      
  4775      return next;
  4776    }
  4777    
  4778    function buildGroup(def, db, node) {
  4779      var groups = node.items,
  4780          marks = def.marks,
  4781          i, len, m, mlen, name, group;
  4782  
  4783      for (i=0, len=groups.length; i<len; ++i) {
  4784        group = groups[i];
  4785        
  4786        // update scales
  4787        if (group.scales) for (name in group.scales) {
  4788          if (name.indexOf(":prev") < 0) {
  4789            group.scales[name+":prev"] = group.scales[name].copy();
  4790          }
  4791        }
  4792  
  4793        // build items
  4794        group.items = group.items || [];
  4795        for (m=0, mlen=marks.length; m<mlen; ++m) {
  4796          group.items[m] = build(marks[m], db, group.items[m], group.datum);
  4797          group.items[m].group = group;
  4798        }
  4799      }
  4800    }
  4801  
  4802    function buildTrans(def, node) {
  4803      if (def.duration) node.duration = def.duration;
  4804      if (def.ease) node.ease = d3.ease(def.ease)
  4805      if (def.delay) {
  4806        var items = node.items, group = node.group, n = items.length, i;
  4807        for (i=0; i<n; ++i) def.delay.call(this, items[i], group);
  4808      }
  4809    }
  4810    
  4811    function keyFunction(key) {
  4812      if (key == null) return null;
  4813      var f = vg.array(key).map(vg.accessor);
  4814      return function(d) {
  4815        for (var s="", i=0, n=f.length; i<n; ++i) {
  4816          if (i>0) s += "|";
  4817          s += String(f[i](d));
  4818        }
  4819        return s;
  4820      }
  4821    }
  4822    
  4823    return build;
  4824  })();vg.scene.bounds = (function() {
  4825  
  4826    var parse = vg.canvas.path.parse,
  4827        boundPath = vg.canvas.path.bounds,
  4828        areaPath = vg.canvas.path.area,
  4829        linePath = vg.canvas.path.line,
  4830        halfpi = Math.PI / 2,
  4831        sqrt3 = Math.sqrt(3),
  4832        tan30 = Math.tan(30 * Math.PI / 180),
  4833        gfx = null;
  4834  
  4835    function context() {
  4836      return gfx || (gfx = (vg.config.isNode
  4837        ? new (require("canvas"))(1,1)
  4838        : d3.select("body").append("canvas")
  4839            .attr("class", "vega_hidden")
  4840            .attr("width", 1)
  4841            .attr("height", 1)
  4842            .style("display", "none")
  4843            .node())
  4844        .getContext("2d"));
  4845    }
  4846  
  4847    function pathBounds(o, path, bounds) {
  4848      if (path == null) {
  4849        bounds.set(0, 0, 0, 0);
  4850      } else {
  4851        boundPath(path, bounds);
  4852        if (o.stroke && o.opacity !== 0 && o.strokeWidth > 0) {
  4853          bounds.expand(o.strokeWidth);
  4854        }
  4855      }
  4856      return bounds;
  4857    }
  4858  
  4859    function path(o, bounds) {
  4860      var p = o.path
  4861        ? o["path:parsed"] || (o["path:parsed"] = parse(o.path))
  4862        : null;
  4863      return pathBounds(o, p, bounds);
  4864    }
  4865    
  4866    function area(o, bounds) {
  4867      var items = o.mark.items, o = items[0];
  4868      var p = o["path:parsed"] || (o["path:parsed"]=parse(areaPath(items)));
  4869      return pathBounds(items[0], p, bounds);
  4870    }
  4871  
  4872    function line(o, bounds) {
  4873      var items = o.mark.items, o = items[0];
  4874      var p = o["path:parsed"] || (o["path:parsed"]=parse(linePath(items)));
  4875      return pathBounds(items[0], p, bounds);
  4876    }
  4877  
  4878    function rect(o, bounds) {
  4879      var x = o.x || 0,
  4880          y = o.y || 0,
  4881          w = (x + o.width) || 0,
  4882          h = (y + o.height) || 0;
  4883      bounds.set(x, y, w, h);
  4884      if (o.stroke && o.opacity !== 0 && o.strokeWidth > 0) {
  4885        bounds.expand(o.strokeWidth);
  4886      }
  4887      return bounds;
  4888    }
  4889  
  4890    function image(o, bounds) {
  4891      var w = o.width || 0,
  4892          h = o.height || 0,
  4893          x = (o.x||0) - (o.align === "center"
  4894              ? w/2 : (o.align === "right" ? w : 0)),
  4895          y = (o.y||0) - (o.baseline === "middle"
  4896              ? h/2 : (o.baseline === "bottom" ? h : 0));
  4897      return bounds.set(x, y, x+w, y+h);
  4898    }
  4899  
  4900    function rule(o, bounds) {
  4901      var x1, y1;
  4902      bounds.set(
  4903        x1 = o.x || 0,
  4904        y1 = o.y || 0,
  4905        o.x2 != null ? o.x2 : x1,
  4906        o.y2 != null ? o.y2 : y1
  4907      );
  4908      if (o.stroke && o.opacity !== 0 && o.strokeWidth > 0) {
  4909        bounds.expand(o.strokeWidth);
  4910      }
  4911      return bounds;
  4912    }
  4913    
  4914    function arc(o, bounds) {
  4915      var cx = o.x || 0,
  4916          cy = o.y || 0,
  4917          ir = o.innerRadius || 0,
  4918          or = o.outerRadius || 0,
  4919          sa = (o.startAngle || 0) - halfpi,
  4920          ea = (o.endAngle || 0) - halfpi,
  4921          xmin = Infinity, xmax = -Infinity,
  4922          ymin = Infinity, ymax = -Infinity,
  4923          a, i, n, x, y, ix, iy, ox, oy;
  4924  
  4925      var angles = [sa, ea],
  4926          s = sa - (sa%halfpi);
  4927      for (i=0; i<4 && s<ea; ++i, s+=halfpi) {
  4928        angles.push(s);
  4929      }
  4930  
  4931      for (i=0, n=angles.length; i<n; ++i) {
  4932        a = angles[i];
  4933        x = Math.cos(a); ix = ir*x; ox = or*x;
  4934        y = Math.sin(a); iy = ir*y; oy = or*y;
  4935        xmin = Math.min(xmin, ix, ox);
  4936        xmax = Math.max(xmax, ix, ox);
  4937        ymin = Math.min(ymin, iy, oy);
  4938        ymax = Math.max(ymax, iy, oy);
  4939      }
  4940  
  4941      bounds.set(cx+xmin, cy+ymin, cx+xmax, cy+ymax);
  4942      if (o.stroke && o.opacity !== 0 && o.strokeWidth > 0) {
  4943        bounds.expand(o.strokeWidth);
  4944      }
  4945      return bounds;
  4946    }
  4947  
  4948    function symbol(o, bounds) {
  4949      var size = o.size != null ? o.size : 100,
  4950          x = o.x || 0,
  4951          y = o.y || 0,
  4952          r, t, rx, ry;
  4953  
  4954      switch (o.shape) {
  4955        case "cross":
  4956          r = Math.sqrt(size / 5) / 2;
  4957          t = 3*r;
  4958          bounds.set(x-t, y-t, x+y, y+t);
  4959          break;
  4960  
  4961        case "diamond":
  4962          ry = Math.sqrt(size / (2 * tan30));
  4963          rx = ry * tan30;
  4964          bounds.set(x-rx, y-ry, x+rx, y+ry);
  4965          break;
  4966  
  4967        case "square":
  4968          t = Math.sqrt(size);
  4969          r = t / 2;
  4970          bounds.set(x-r, y-r, x+r, y+r);
  4971          break;
  4972  
  4973        case "triangle-down":
  4974          rx = Math.sqrt(size / sqrt3);
  4975          ry = rx * sqrt3 / 2;
  4976          bounds.set(x-rx, y-ry, x+rx, y+ry);
  4977          break;
  4978  
  4979        case "triangle-up":
  4980          rx = Math.sqrt(size / sqrt3);
  4981          ry = rx * sqrt3 / 2;
  4982          bounds.set(x-rx, y-ry, x+rx, y+ry);
  4983          break;
  4984  
  4985        default:
  4986          r = Math.sqrt(size/Math.PI);
  4987          bounds.set(x-r, y-r, x+r, y+r);
  4988      }
  4989      if (o.stroke && o.opacity !== 0 && o.strokeWidth > 0) {
  4990        bounds.expand(o.strokeWidth);
  4991      }
  4992      return bounds;
  4993    }
  4994  
  4995    function text(o, bounds, noRotate) {
  4996      var x = (o.x || 0) + (o.dx || 0),
  4997          y = (o.y || 0) + (o.dy || 0),
  4998          h = o.fontSize || vg.config.render.fontSize,
  4999          a = o.align,
  5000          b = o.baseline,
  5001          g = context(), w;
  5002  
  5003      g.font = vg.scene.fontString(o);
  5004      g.textAlign = a || "left";
  5005      g.textBaseline = b || "alphabetic";
  5006      w = g.measureText(o.text || "").width;
  5007  
  5008      // horizontal
  5009      if (a === "center") {
  5010        x = x - (w / 2);
  5011      } else if (a === "right") {
  5012        x = x - w;
  5013      } else {
  5014        // left by default, do nothing
  5015      }
  5016  
  5017      /// TODO find a robust solution for heights.
  5018      /// These offsets work for some but not all fonts.
  5019  
  5020      // vertical
  5021      if (b === "top") {
  5022        y = y + (h/5);
  5023      } else if (b === "bottom") {
  5024        y = y - h;
  5025      } else if (b === "middle") {
  5026        y = y - (h/2) + (h/10);
  5027      } else {
  5028        y = y - 4*h/5; // alphabetic by default
  5029      }
  5030      
  5031      bounds.set(x, y, x+w, y+h);
  5032      if (o.angle && !noRotate) {
  5033        bounds.rotate(o.angle*Math.PI/180, o.x||0, o.y||0);
  5034      }
  5035      return bounds.expand(noRotate ? 0 : 1);
  5036    }
  5037  
  5038    function group(g, bounds, includeLegends) {
  5039      var axes = g.axisItems || [],
  5040          legends = g.legendItems || [], j, m;
  5041  
  5042      for (j=0, m=axes.length; j<m; ++j) {
  5043        bounds.union(axes[j].bounds);
  5044      }
  5045      for (j=0, m=g.items.length; j<m; ++j) {
  5046        bounds.union(g.items[j].bounds);
  5047      }
  5048      if (includeLegends) {
  5049        for (j=0, m=legends.length; j<m; ++j) {
  5050          bounds.union(legends[j].bounds);
  5051        }
  5052        if (g.width != null && g.height != null) {
  5053          bounds.add(g.width, g.height);
  5054        }
  5055        if (g.x != null && g.y != null) {
  5056          bounds.add(0, 0);
  5057        }
  5058      }
  5059      bounds.translate(g.x||0, g.y||0);
  5060      return bounds;
  5061    }
  5062  
  5063    var methods = {
  5064      group:  group,
  5065      symbol: symbol,
  5066      image:  image,
  5067      rect:   rect,
  5068      rule:   rule,
  5069      arc:    arc,
  5070      text:   text,
  5071      path:   path,
  5072      area:   area,
  5073      line:   line
  5074    };
  5075  
  5076    function itemBounds(item, func, opt) {
  5077      func = func || methods[item.mark.marktype];
  5078      if (!item.bounds_prev) item['bounds:prev'] = new vg.Bounds();
  5079      var b = item.bounds, pb = item['bounds:prev'];
  5080      if (b) pb.clear().union(b);
  5081      item.bounds = func(item, b ? b.clear() : new vg.Bounds(), opt);
  5082      if (!b) pb.clear().union(item.bounds);
  5083      return item.bounds;
  5084    }
  5085  
  5086    function markBounds(mark, bounds, opt) {
  5087      bounds = bounds || mark.bounds && mark.bounds.clear() || new vg.Bounds();
  5088      var type  = mark.marktype,
  5089          func  = methods[type],
  5090          items = mark.items,
  5091          item, i, len;
  5092          
  5093      if (type==="area" || type==="line") {
  5094        items[0].bounds = func(items[0], bounds);
  5095      } else {
  5096        for (i=0, len=items.length; i<len; ++i) {
  5097          bounds.union(itemBounds(items[i], func, opt));
  5098        }
  5099      }
  5100      mark.bounds = bounds;
  5101    }
  5102    
  5103    return {
  5104      mark:  markBounds,
  5105      item:  itemBounds,
  5106      text:  text,
  5107      group: group
  5108    };
  5109  
  5110  })();vg.scene.encode = (function() {
  5111    var GROUP  = vg.scene.GROUP,
  5112        ENTER  = vg.scene.ENTER,
  5113        UPDATE = vg.scene.UPDATE,
  5114        EXIT   = vg.scene.EXIT,
  5115        EMPTY  = {};
  5116  
  5117    function main(scene, def, trans, request, items) {
  5118      (request && items)
  5119        ? update.call(this, scene, def, trans, request, items)
  5120        : encode.call(this, scene, scene, def, trans, request);
  5121      return scene;
  5122    }
  5123    
  5124    function update(scene, def, trans, request, items) {
  5125      items = vg.array(items);
  5126      var i, len, item, group, props, prop;
  5127      for (i=0, len=items.length; i<len; ++i) {
  5128        item = items[i];
  5129        group = item.mark.group || null;
  5130        props = item.mark.def.properties;
  5131        prop = props && props[request];
  5132        if (prop) {
  5133          prop.call(vg, item, group, trans);
  5134          vg.scene.bounds.item(item);
  5135        }
  5136      }
  5137    }
  5138    
  5139    function encode(group, scene, def, trans, request) {
  5140      encodeItems.call(this, group, scene.items, def, trans, request);
  5141      if (scene.marktype === GROUP) {
  5142        encodeGroup.call(this, scene, def, group, trans, request);
  5143      } else {
  5144        vg.scene.bounds.mark(scene);
  5145      }
  5146    }
  5147    
  5148    function encodeLegend(group, scene, def, trans, request) {
  5149      encodeGroup.call(this, scene, def, group, trans, request);
  5150      encodeItems.call(this, group, scene.items, def, trans, request);
  5151      vg.scene.bounds.mark(scene, null, true);
  5152    }
  5153    
  5154    function encodeGroup(scene, def, parent, trans, request) {
  5155      var i, len, m, mlen, group, scales,
  5156          axes, axisItems, axisDef, leg, legItems, legDef;
  5157  
  5158      for (i=0, len=scene.items.length; i<len; ++i) {
  5159        group = scene.items[i];
  5160  
  5161        // cascade scales recursively
  5162        // use parent scales if there are no group-level scale defs
  5163        scales = group.scales || (group.scales =
  5164          def.scales ? vg.extend({}, parent.scales) : parent.scales);
  5165        
  5166        // update group-level scales
  5167        if (def.scales) {
  5168          vg.parse.scales(def.scales, scales, this._data, group);
  5169        }
  5170        
  5171        // update group-level axes
  5172        if (def.axes) {
  5173          axes = group.axes || (group.axes = []);
  5174          axisItems = group.axisItems || (group.axisItems = []);
  5175          vg.parse.axes(def.axes, axes, group.scales);
  5176          axes.forEach(function(a, i) {
  5177            axisDef = a.def();
  5178            axisItems[i] = vg.scene.build(axisDef, this._data, axisItems[i]);
  5179            axisItems[i].group = group;
  5180            encode.call(this, group, group.axisItems[i], axisDef, trans);
  5181          });
  5182        }
  5183        
  5184        // encode children marks
  5185        for (m=0, mlen=group.items.length; m<mlen; ++m) {
  5186          encode.call(this, group, group.items[m], def.marks[m], trans, request);
  5187        }
  5188      }
  5189      
  5190      // compute bounds (without legend)
  5191      vg.scene.bounds.mark(scene, null, !def.legends);
  5192      
  5193      // update legends
  5194      if (def.legends) {
  5195        for (i=0, len=scene.items.length; i<len; ++i) {
  5196          group = scene.items[i];
  5197          leg = group.legends || (group.legends = []);
  5198          legItems = group.legendItems || (group.legendItems = []);
  5199          vg.parse.legends(def.legends, leg, group.scales);
  5200          leg.forEach(function(l, i) {
  5201            legDef = l.def();
  5202            legItems[i] = vg.scene.build(legDef, this._data, legItems[i]);
  5203            legItems[i].group = group;
  5204            encodeLegend.call(this, group, group.legendItems[i], legDef, trans);
  5205          });
  5206        }
  5207        vg.scene.bounds.mark(scene, null, true);
  5208      }
  5209    }
  5210    
  5211    function encodeItems(group, items, def, trans, request) {    
  5212      var props  = def.properties || EMPTY,
  5213          enter  = props.enter,
  5214          update = props.update,
  5215          exit   = props.exit,
  5216          i, len, item, prop;
  5217  
  5218      if (request) {
  5219        if (prop = props[request]) {
  5220          for (i=0, len=items.length; i<len; ++i) {
  5221            prop.call(vg, items[i], group, trans);
  5222          }
  5223        }
  5224        return; // exit early if given request
  5225      }
  5226  
  5227      for (i=0; i<items.length; ++i) {
  5228        item = items[i];
  5229  
  5230        // enter set
  5231        if (item.status === ENTER) {
  5232          if (enter) enter.call(vg, item, group);
  5233          item.status = UPDATE;
  5234        }
  5235  
  5236        // update set      
  5237        if (item.status !== EXIT && update) {
  5238          update.call(vg, item, group, trans);
  5239        }
  5240        
  5241        // exit set
  5242        if (item.status === EXIT) {
  5243          if (exit) exit.call(vg, item, group, trans);
  5244          if (trans && !exit) trans.interpolate(item, EMPTY);
  5245          else if (!trans) items[i--].remove();
  5246        }
  5247      }
  5248    }
  5249    
  5250    return main;
  5251  })();vg.scene.Transition = (function() {
  5252    function trans(duration, ease) {
  5253      this.duration = duration || 500;
  5254      this.ease = ease && d3.ease(ease) || d3.ease("cubic-in-out");
  5255      this.updates = {next: null};
  5256    }
  5257    
  5258    var prototype = trans.prototype;
  5259    
  5260    prototype.interpolate = function(item, values) {
  5261      var key, curr, next, interp, list = null;
  5262  
  5263      for (key in values) {
  5264        curr = item[key];
  5265        next = values[key];      
  5266        if (curr !== next) {
  5267          if (key === "text") {
  5268            // skip interpolation for text labels
  5269            item[key] = next;
  5270          } else {
  5271            // otherwise lookup interpolator
  5272            interp = d3.interpolate(curr, next);
  5273            interp.property = key;
  5274            (list || (list=[])).push(interp);
  5275          }
  5276        }
  5277      }
  5278  
  5279      if (list === null && item.status === vg.scene.EXIT) {
  5280        list = []; // ensure exiting items are included
  5281      }
  5282  
  5283      if (list != null) {
  5284        list.item = item;
  5285        list.ease = item.mark.ease || this.ease;
  5286        list.next = this.updates.next;
  5287        this.updates.next = list;
  5288      }
  5289      return this;
  5290    };
  5291    
  5292    prototype.start = function(callback) {
  5293      var t = this, prev = t.updates, curr = prev.next;
  5294      for (; curr!=null; prev=curr, curr=prev.next) {
  5295        if (curr.item.status === vg.scene.EXIT) curr.remove = true;
  5296      }
  5297      t.callback = callback;
  5298      d3.timer(function(elapsed) { return step.call(t, elapsed); });
  5299    };
  5300  
  5301    function step(elapsed) {
  5302      var list = this.updates, prev = list, curr = prev.next,
  5303          duration = this.duration,
  5304          item, delay, f, e, i, n, stop = true;
  5305  
  5306      for (; curr!=null; prev=curr, curr=prev.next) {
  5307        item = curr.item;
  5308        delay = item.delay || 0;
  5309  
  5310        f = (elapsed - delay) / duration;
  5311        if (f < 0) { stop = false; continue; }
  5312        if (f > 1) f = 1;
  5313        e = curr.ease(f);
  5314  
  5315        for (i=0, n=curr.length; i<n; ++i) {
  5316          item[curr[i].property] = curr[i](e);
  5317          vg.scene.bounds.item(item);
  5318        }
  5319  
  5320        if (f === 1) {
  5321          if (curr.remove) item.remove();
  5322          prev.next = curr.next;
  5323          curr = prev;
  5324        } else {
  5325          stop = false;
  5326        }
  5327      }
  5328  
  5329      this.callback();
  5330      return stop;
  5331    };
  5332    
  5333    return trans;
  5334    
  5335  })();
  5336  
  5337  vg.scene.transition = function(dur, ease) {
  5338    return new vg.scene.Transition(dur, ease);
  5339  };vg.scene.axis = function() {
  5340    var scale,
  5341        orient = vg.config.axis.orient,
  5342        offset = 0,
  5343        titleOffset = vg.config.axis.titleOffset,
  5344        axisDef = null,
  5345        layer = "front",
  5346        grid = false,
  5347        title = null,
  5348        tickMajorSize = vg.config.axis.tickSize,
  5349        tickMinorSize = vg.config.axis.tickSize,
  5350        tickEndSize = vg.config.axis.tickSize,
  5351        tickPadding = vg.config.axis.padding,
  5352        tickValues = null,
  5353        tickFormat = null,
  5354        tickSubdivide = 0,
  5355        tickArguments = [vg.config.axis.ticks],
  5356        gridLineStyle = {},
  5357        tickLabelStyle = {},
  5358        majorTickStyle = {},
  5359        minorTickStyle = {},
  5360        titleStyle = {},
  5361        domainStyle = {};
  5362  
  5363    var axis = {};
  5364  
  5365    function reset() { axisDef = null; }
  5366  
  5367    axis.def = function() {
  5368      var def = axisDef ? axisDef : (axisDef = axis_def(scale));
  5369      
  5370      // generate data
  5371      var major = tickValues == null
  5372        ? (scale.ticks ? scale.ticks.apply(scale, tickArguments) : scale.domain())
  5373        : tickValues;
  5374      var minor = vg_axisSubdivide(scale, major, tickSubdivide).map(vg.data.ingest);
  5375      major = major.map(vg.data.ingest);
  5376      var fmt = tickFormat==null ? (scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments) : String) : tickFormat;
  5377      major.forEach(function(d) { d.label = fmt(d.data); });
  5378      var tdata = title ? [title].map(vg.data.ingest) : [];
  5379      
  5380      // update axis def
  5381      def.marks[0].from = function() { return grid ? major : []; };
  5382      def.marks[1].from = function() { return major; };
  5383      def.marks[2].from = function() { return minor; };
  5384      def.marks[3].from = def.marks[1].from;
  5385      def.marks[4].from = function() { return [1]; };
  5386      def.marks[5].from = function() { return tdata; };
  5387      def.offset = offset;
  5388      def.orient = orient;
  5389      def.layer = layer;
  5390      return def;
  5391    };
  5392  
  5393    function axis_def(scale) {
  5394      // setup scale mapping
  5395      var newScale, oldScale, range;
  5396      if (scale.type === "ordinal") {
  5397        newScale = {scale: scale.scaleName, offset: 0.5 + scale.rangeBand()/2};
  5398        oldScale = newScale;
  5399      } else {
  5400        newScale = {scale: scale.scaleName, offset: 0.5};
  5401        oldScale = {scale: scale.scaleName+":prev", offset: 0.5};
  5402      }
  5403      range = vg_axisScaleRange(scale);
  5404  
  5405      // setup axis marks
  5406      var gridLines = vg_axisTicks();
  5407      var majorTicks = vg_axisTicks();
  5408      var minorTicks = vg_axisTicks();
  5409      var tickLabels = vg_axisTickLabels();
  5410      var domain = vg_axisDomain();
  5411      var title = vg_axisTitle();
  5412      gridLines.properties.enter.stroke = {value: vg.config.axis.gridColor};
  5413  
  5414      // extend axis marks based on axis orientation
  5415      vg_axisTicksExtend(orient, gridLines, oldScale, newScale, Infinity);
  5416      vg_axisTicksExtend(orient, majorTicks, oldScale, newScale, tickMajorSize);
  5417      vg_axisTicksExtend(orient, minorTicks, oldScale, newScale, tickMinorSize);
  5418      vg_axisLabelExtend(orient, tickLabels, oldScale, newScale, tickMajorSize, tickPadding);
  5419  
  5420      vg_axisDomainExtend(orient, domain, range, tickEndSize);
  5421      vg_axisTitleExtend(orient, title, range, titleOffset); // TODO get offset
  5422      
  5423      // add / override custom style properties
  5424      vg.extend(gridLines.properties.update, gridLineStyle);
  5425      vg.extend(majorTicks.properties.update, majorTickStyle);
  5426      vg.extend(minorTicks.properties.update, minorTickStyle);
  5427      vg.extend(tickLabels.properties.update, tickLabelStyle);
  5428      vg.extend(domain.properties.update, domainStyle);
  5429      vg.extend(title.properties.update, titleStyle);
  5430  
  5431      var marks = [gridLines, majorTicks, minorTicks, tickLabels, domain, title];
  5432      return {
  5433        type: "group",
  5434        interactive: false,
  5435        properties: { enter: vg_axisUpdate, update: vg_axisUpdate },
  5436        marks: marks.map(vg.parse.mark)
  5437      };
  5438    }
  5439  
  5440    axis.scale = function(x) {
  5441      if (!arguments.length) return scale;
  5442      if (scale !== x) { scale = x; reset(); }
  5443      return axis;
  5444    };
  5445  
  5446    axis.orient = function(x) {
  5447      if (!arguments.length) return orient;
  5448      if (orient !== x) {
  5449        orient = x in vg_axisOrients ? x + "" : vg.config.axis.orient;
  5450        reset();
  5451      }
  5452      return axis;
  5453    };
  5454  
  5455    axis.title = function(x) {
  5456      if (!arguments.length) return title;
  5457      if (title !== x) { title = x; reset(); }
  5458      return axis;
  5459    };
  5460  
  5461    axis.ticks = function() {
  5462      if (!arguments.length) return tickArguments;
  5463      tickArguments = arguments;
  5464      return axis;
  5465    };
  5466  
  5467    axis.tickValues = function(x) {
  5468      if (!arguments.length) return tickValues;
  5469      tickValues = x;
  5470      return axis;
  5471    };
  5472  
  5473    axis.tickFormat = function(x) {
  5474      if (!arguments.length) return tickFormat;
  5475      tickFormat = x;
  5476      return axis;
  5477    };
  5478    
  5479    axis.tickSize = function(x, y) {
  5480      if (!arguments.length) return tickMajorSize;
  5481      var n = arguments.length - 1,
  5482          major = +x,
  5483          minor = n > 1 ? +y : tickMajorSize,
  5484          end   = n > 0 ? +arguments[n] : tickMajorSize;
  5485  
  5486      if (tickMajorSize !== major ||
  5487          tickMinorSize !== minor ||
  5488          tickEndSize !== end) {
  5489        reset();
  5490      }
  5491  
  5492      tickMajorSize = major;
  5493      tickMinorSize = minor;
  5494      tickEndSize = end;
  5495      return axis;
  5496    };
  5497  
  5498    axis.tickSubdivide = function(x) {
  5499      if (!arguments.length) return tickSubdivide;
  5500      tickSubdivide = +x;
  5501      return axis;
  5502    };
  5503    
  5504    axis.offset = function(x) {
  5505      if (!arguments.length) return offset;
  5506      offset = vg.isObject(x) ? x : +x;
  5507      return axis;
  5508    };
  5509  
  5510    axis.tickPadding = function(x) {
  5511      if (!arguments.length) return tickPadding;
  5512      if (tickPadding !== +x) { tickPadding = +x; reset(); }
  5513      return axis;
  5514    };
  5515  
  5516    axis.titleOffset = function(x) {
  5517      if (!arguments.length) return titleOffset;
  5518      if (titleOffset !== +x) { titleOffset = +x; reset(); }
  5519      return axis;
  5520    };
  5521  
  5522    axis.layer = function(x) {
  5523      if (!arguments.length) return layer;
  5524      if (layer !== x) { layer = x; reset(); }
  5525      return axis;
  5526    };
  5527  
  5528    axis.grid = function(x) {
  5529      if (!arguments.length) return grid;
  5530      if (grid !== x) { grid = x; reset(); }
  5531      return axis;
  5532    };
  5533  
  5534    axis.gridLineProperties = function(x) {
  5535      if (!arguments.length) return gridLineStyle;
  5536      if (gridLineStyle !== x) { gridLineStyle = x; }
  5537      return axis;
  5538    };
  5539  
  5540    axis.majorTickProperties = function(x) {
  5541      if (!arguments.length) return majorTickStyle;
  5542      if (majorTickStyle !== x) { majorTickStyle = x; }
  5543      return axis;
  5544    };
  5545  
  5546    axis.minorTickProperties = function(x) {
  5547      if (!arguments.length) return minorTickStyle;
  5548      if (minorTickStyle !== x) { minorTickStyle = x; }
  5549      return axis;
  5550    };
  5551  
  5552    axis.tickLabelProperties = function(x) {
  5553      if (!arguments.length) return tickLabelStyle;
  5554      if (tickLabelStyle !== x) { tickLabelStyle = x; }
  5555      return axis;
  5556    };
  5557  
  5558    axis.titleProperties = function(x) {
  5559      if (!arguments.length) return titleStyle;
  5560      if (titleStyle !== x) { titleStyle = x; }
  5561      return axis;
  5562    };
  5563  
  5564    axis.domainProperties = function(x) {
  5565      if (!arguments.length) return domainStyle;
  5566      if (domainStyle !== x) { domainStyle = x; }
  5567      return axis;
  5568    };
  5569    
  5570    axis.reset = function() { reset(); };
  5571  
  5572    return axis;
  5573  };
  5574  
  5575  var vg_axisOrients = {top: 1, right: 1, bottom: 1, left: 1};
  5576  
  5577  function vg_axisSubdivide(scale, ticks, m) {
  5578    subticks = [];
  5579    if (m && ticks.length > 1) {
  5580      var extent = vg_axisScaleExtent(scale.domain()),
  5581          subticks,
  5582          i = -1,
  5583          n = ticks.length,
  5584          d = (ticks[1] - ticks[0]) / ++m,
  5585          j,
  5586          v;
  5587      while (++i < n) {
  5588        for (j = m; --j > 0;) {
  5589          if ((v = +ticks[i] - j * d) >= extent[0]) {
  5590            subticks.push(v);
  5591          }
  5592        }
  5593      }
  5594      for (--i, j = 0; ++j < m && (v = +ticks[i] + j * d) < extent[1];) {
  5595        subticks.push(v);
  5596      }
  5597    }
  5598    return subticks;
  5599  }
  5600  
  5601  function vg_axisScaleExtent(domain) {
  5602    var start = domain[0], stop = domain[domain.length - 1];
  5603    return start < stop ? [start, stop] : [stop, start];
  5604  }
  5605  
  5606  function vg_axisScaleRange(scale) {
  5607    return scale.rangeExtent
  5608      ? scale.rangeExtent()
  5609      : vg_axisScaleExtent(scale.range());
  5610  }
  5611  
  5612  var vg_axisAlign = {
  5613    bottom: "center",
  5614    top: "center",
  5615    left: "right",
  5616    right: "left"
  5617  };
  5618  
  5619  var vg_axisBaseline = {
  5620    bottom: "top",
  5621    top: "bottom",
  5622    left: "middle",
  5623    right: "middle"
  5624  };
  5625  
  5626  function vg_axisLabelExtend(orient, labels, oldScale, newScale, size, pad) {
  5627    size = Math.max(size, 0) + pad;
  5628    if (orient === "left" || orient === "top") {
  5629      size *= -1;
  5630    }  
  5631    if (orient === "top" || orient === "bottom") {
  5632      vg.extend(labels.properties.enter, {
  5633        x: oldScale,
  5634        y: {value: size},
  5635      });
  5636      vg.extend(labels.properties.update, {
  5637        x: newScale,
  5638        y: {value: size},
  5639        align: {value: "center"},
  5640        baseline: {value: vg_axisBaseline[orient]}
  5641      });
  5642    } else {
  5643      vg.extend(labels.properties.enter, {
  5644        x: {value: size},
  5645        y: oldScale,
  5646      });
  5647      vg.extend(labels.properties.update, {
  5648        x: {value: size},
  5649        y: newScale,
  5650        align: {value: vg_axisAlign[orient]},
  5651        baseline: {value: "middle"}
  5652      });
  5653    }
  5654  }
  5655  
  5656  function vg_axisTicksExtend(orient, ticks, oldScale, newScale, size) {
  5657    var sign = (orient === "left" || orient === "top") ? -1 : 1;
  5658    if (size === Infinity) {
  5659      size = (orient === "top" || orient === "bottom")
  5660        ? {group: "mark.group.height", mult: -sign}
  5661        : {group: "mark.group.width", mult: -sign};
  5662    } else {
  5663      size = {value: sign * size};
  5664    }
  5665    if (orient === "top" || orient === "bottom") {
  5666      vg.extend(ticks.properties.enter, {
  5667        x:  oldScale,
  5668        y:  {value: 0},
  5669        y2: size
  5670      });
  5671      vg.extend(ticks.properties.update, {
  5672        x:  newScale,
  5673        y:  {value: 0},
  5674        y2: size
  5675      });
  5676      vg.extend(ticks.properties.exit, {
  5677        x:  newScale,
  5678      });        
  5679    } else {
  5680      vg.extend(ticks.properties.enter, {
  5681        x:  {value: 0},
  5682        x2: size,
  5683        y:  oldScale
  5684      });
  5685      vg.extend(ticks.properties.update, {
  5686        x:  {value: 0},
  5687        x2: size,
  5688        y:  newScale
  5689      });
  5690      vg.extend(ticks.properties.exit, {
  5691        y:  newScale,
  5692      });
  5693    }
  5694  }
  5695  
  5696  function vg_axisTitleExtend(orient, title, range, offset) {
  5697    var mid = ~~((range[1] - range[0]) / 2),
  5698        sign = (orient === "top" || orient === "left") ? -1 : 1;
  5699    
  5700    if (orient === "bottom" || orient === "top") {
  5701      vg.extend(title.properties.update, {
  5702        x: {value: mid},
  5703        y: {value: sign*offset},
  5704        angle: {value: 0}
  5705      });
  5706    } else {
  5707      vg.extend(title.properties.update, {
  5708        x: {value: sign*offset},
  5709        y: {value: mid},
  5710        angle: {value: -90}
  5711      });
  5712    }
  5713  }
  5714  
  5715  function vg_axisDomainExtend(orient, domain, range, size) {
  5716    var path;
  5717    if (orient === "top" || orient === "left") {
  5718      size = -1 * size;
  5719    }
  5720    if (orient === "bottom" || orient === "top") {
  5721      path = "M" + range[0] + "," + size + "V0H" + range[1] + "V" + size;
  5722    } else {
  5723      path = "M" + size + "," + range[0] + "H0V" + range[1] + "H" + size;
  5724    }
  5725    domain.properties.update.path = {value: path};
  5726  }
  5727  
  5728  function vg_axisUpdate(item, group, trans) {
  5729    var o = trans ? {} : item,
  5730        offset = item.mark.def.offset,
  5731        orient = item.mark.def.orient,
  5732        width  = group.width,
  5733        height = group.height; // TODO fallback to global w,h?
  5734  
  5735    if (vg.isObject(offset)) {
  5736      offset = -group.scales[offset.scale](offset.value);
  5737    }
  5738  
  5739    switch (orient) {
  5740      case "left":   { o.x = -offset; o.y = 0; break; }
  5741      case "right":  { o.x = width + offset; o.y = 0; break; }
  5742      case "bottom": { o.x = 0; o.y = height + offset; break; }
  5743      case "top":    { o.x = 0; o.y = -offset; break; }
  5744      default:       { o.x = 0; o.y = 0; }
  5745    }
  5746  
  5747    if (trans) trans.interpolate(item, o);
  5748  }
  5749  
  5750  function vg_axisTicks() {
  5751    return {
  5752      type: "rule",
  5753      interactive: false,
  5754      key: "data",
  5755      properties: {
  5756        enter: {
  5757          stroke: {value: vg.config.axis.tickColor},
  5758          strokeWidth: {value: vg.config.axis.tickWidth},
  5759          opacity: {value: 1e-6}
  5760        },
  5761        exit: { opacity: {value: 1e-6} },
  5762        update: { opacity: {value: 1} }
  5763      }
  5764    };
  5765  }
  5766  
  5767  function vg_axisTickLabels() {
  5768    return {
  5769      type: "text",
  5770      interactive: true,
  5771      key: "data",
  5772      properties: {
  5773        enter: {
  5774          fill: {value: vg.config.axis.tickLabelColor},
  5775          font: {value: vg.config.axis.tickLabelFont},
  5776          fontSize: {value: vg.config.axis.tickLabelFontSize},
  5777          opacity: {value: 1e-6},
  5778          text: {field: "label"}
  5779        },
  5780        exit: { opacity: {value: 1e-6} },
  5781        update: { opacity: {value: 1} }
  5782      }
  5783    };
  5784  }
  5785  
  5786  function vg_axisTitle() {
  5787    return {
  5788      type: "text",
  5789      interactive: true,
  5790      properties: {
  5791        enter: {
  5792          font: {value: vg.config.axis.titleFont},
  5793          fontSize: {value: vg.config.axis.titleFontSize},
  5794          fontWeight: {value: vg.config.axis.titleFontWeight},
  5795          fill: {value: vg.config.axis.titleColor},
  5796          align: {value: "center"},
  5797          baseline: {value: "middle"},
  5798          text: {field: "data"}
  5799        },
  5800        update: {}
  5801      }
  5802    };
  5803  }
  5804  
  5805  function vg_axisDomain() {
  5806    return {
  5807      type: "path",
  5808      interactive: false,
  5809      properties: {
  5810        enter: {
  5811          x: {value: 0.5},
  5812          y: {value: 0.5},
  5813          stroke: {value: vg.config.axis.axisColor},
  5814          strokeWidth: {value: vg.config.axis.axisWidth}
  5815        },
  5816        update: {}
  5817      }
  5818    };
  5819  }
  5820  vg.scene.legend = function() {
  5821    var size = null,
  5822        shape = null,
  5823        fill = null,
  5824        stroke = null,
  5825        spacing = null,
  5826        values = null,
  5827        format = null,
  5828        title = undefined,
  5829        orient = "right",
  5830        offset = vg.config.legend.offset,
  5831        padding = vg.config.legend.padding,
  5832        legendDef,
  5833        tickArguments = [5],
  5834        legendStyle = {},
  5835        symbolStyle = {},
  5836        gradientStyle = {},
  5837        titleStyle = {},
  5838        labelStyle = {};
  5839  
  5840    var legend = {},
  5841        legendDef = null;
  5842  
  5843    function reset() { legendDef = null; }
  5844  
  5845    legend.def = function() {
  5846      var scale = size || shape || fill || stroke; 
  5847      if (!legendDef) {
  5848        legendDef = (scale===fill || scale===stroke) && !discrete(scale.type)
  5849          ? quantDef(scale)
  5850          : ordinalDef(scale);      
  5851      }
  5852      legendDef.orient = orient;
  5853      legendDef.offset = offset;
  5854      legendDef.padding = padding;
  5855      return legendDef;
  5856    };
  5857  
  5858    function discrete(type) {
  5859      return type==="ordinal" || type==="quantize"
  5860        || type==="quantile" || type==="threshold";
  5861    }
  5862  
  5863    function ordinalDef(scale) {
  5864      var def = o_legend_def(size, shape, fill, stroke);
  5865  
  5866      // generate data
  5867      var data = (values == null
  5868        ? (scale.ticks ? scale.ticks.apply(scale, tickArguments) : scale.domain())
  5869        : values).map(vg.data.ingest);
  5870      var fmt = format==null ? (scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments) : String) : format;
  5871      
  5872      // determine spacing between legend entries
  5873      var fs, range, offset, pad=5, domain = d3.range(data.length);
  5874      if (size) {
  5875        range = data.map(function(x) { return Math.sqrt(size(x.data)); });
  5876        offset = d3.max(range);
  5877        range = range.reduce(function(a,b,i,z) {
  5878            if (i > 0) a[i] = a[i-1] + z[i-1]/2 + pad;
  5879            return (a[i] += b/2, a); }, [0]).map(Math.round);
  5880      } else {
  5881        offset = Math.round(Math.sqrt(vg.config.legend.symbolSize));
  5882        range = spacing
  5883          || (fs = labelStyle.fontSize) && (fs.value + pad)
  5884          || (vg.config.legend.labelFontSize + pad);
  5885        range = domain.map(function(d,i) {
  5886          return Math.round(offset/2 + i*range);
  5887        });
  5888      }
  5889  
  5890      // account for padding and title size
  5891      var sz = padding, ts;
  5892      if (title) {
  5893        ts = titleStyle.fontSize;
  5894        sz += 5 + ((ts && ts.value) || vg.config.legend.titleFontSize);
  5895      }
  5896      for (var i=0, n=range.length; i<n; ++i) range[i] += sz;
  5897      
  5898      // build scale for label layout
  5899      var scale = {
  5900        name: "legend",
  5901        type: "ordinal",
  5902        points: true,
  5903        domain: domain,
  5904        range: range
  5905      };
  5906      
  5907      // update legend def
  5908      var tdata = (title ? [title] : []).map(vg.data.ingest);
  5909      data.forEach(function(d) {
  5910        d.label = fmt(d.data);
  5911        d.offset = offset;
  5912      });
  5913      def.scales = [ scale ];
  5914      def.marks[0].from = function() { return tdata; };
  5915      def.marks[1].from = function() { return data; };
  5916      def.marks[2].from = def.marks[1].from;
  5917      return def;
  5918    }
  5919  
  5920    function o_legend_def(size, shape, fill, stroke) {
  5921      // setup legend marks
  5922      var titles = vg_legendTitle(),
  5923          symbols = vg_legendSymbols(),
  5924          labels = vg_vLegendLabels();
  5925  
  5926      // extend legend marks
  5927      vg_legendSymbolExtend(symbols, size, shape, fill, stroke);
  5928      
  5929      // add / override custom style properties
  5930      vg.extend(titles.properties.update, titleStyle);
  5931      vg.extend(symbols.properties.update, symbolStyle);
  5932      vg.extend(labels.properties.update, labelStyle);
  5933  
  5934      // padding from legend border
  5935      titles.properties.enter.x.value += padding;
  5936      titles.properties.enter.y.value += padding;
  5937      labels.properties.enter.x.offset += padding + 1;
  5938      symbols.properties.enter.x.offset = padding + 1;
  5939  
  5940      return {
  5941        type: "group",
  5942        interactive: false,
  5943        properties: {
  5944          enter: vg.parse.properties("group", legendStyle),
  5945          update: vg_legendUpdate
  5946        },
  5947        marks: [titles, symbols, labels].map(vg.parse.mark)
  5948      };
  5949    }
  5950  
  5951    function quantDef(scale) {
  5952      var def = q_legend_def(scale),
  5953          dom = scale.domain(),
  5954          data = dom.map(vg.data.ingest),
  5955          width = (gradientStyle.width && gradientStyle.width.value) || vg.config.legend.gradientWidth,
  5956          fmt = format==null ? (scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments) : String) : format;
  5957  
  5958      // build scale for label layout
  5959      var layout = {
  5960        name: "legend",
  5961        type: scale.type,
  5962        round: true,
  5963        zero: false,
  5964        domain: [dom[0], dom[dom.length-1]],
  5965        range: [padding, width+padding]
  5966      };
  5967      if (scale.type==="pow") layout.exponent = scale.exponent();
  5968      
  5969      // update legend def
  5970      var tdata = (title ? [title] : []).map(vg.data.ingest);
  5971      data.forEach(function(d,i) {
  5972        d.label = fmt(d.data);
  5973        d.align = i==(data.length-1) ? "right" : i==0 ? "left" : "center";
  5974      });
  5975      def.scales = [ layout ];
  5976      def.marks[0].from = function() { return tdata; };
  5977      def.marks[1].from = function() { return [1]; };
  5978      def.marks[2].from = function() { return data; };
  5979      return def;
  5980    }
  5981    
  5982    function q_legend_def(scale) {
  5983      // setup legend marks
  5984      var titles = vg_legendTitle(),
  5985          gradient = vg_legendGradient(),
  5986          labels = vg_hLegendLabels(),
  5987          grad = new vg.Gradient();
  5988  
  5989      // setup color gradient
  5990      var dom = scale.domain(),
  5991          min = dom[0],
  5992          max = dom[dom.length-1],
  5993          f = scale.copy().domain([min, max]).range([0,1]);
  5994          
  5995      var stops = (scale.type !== "linear" && scale.ticks)
  5996        ? scale.ticks.call(scale, 15) : dom;
  5997      if (min !== stops[0]) stops.unshift(min);
  5998      if (max !== stops[stops.length-1]) stops.push(max);
  5999  
  6000      for (var i=0, n=stops.length; i<n; ++i) {
  6001        grad.stop(f(stops[i]), scale(stops[i]));
  6002      }
  6003      gradient.properties.enter.fill = {value: grad};
  6004  
  6005      // add / override custom style properties
  6006      vg.extend(titles.properties.update, titleStyle);
  6007      vg.extend(gradient.properties.update, gradientStyle);
  6008      vg.extend(labels.properties.update, labelStyle);
  6009  
  6010      // account for gradient size
  6011      var gp = gradient.properties, gh = gradientStyle.height,
  6012          hh = (gh && gh.value) || gp.enter.height.value;
  6013      labels.properties.enter.y.value = hh;
  6014  
  6015      // account for title size as needed
  6016      if (title) {
  6017        var tp = titles.properties, fs = titleStyle.fontSize,
  6018            sz = 4 + ((fs && fs.value) || tp.enter.fontSize.value);
  6019        gradient.properties.enter.y.value += sz;
  6020        labels.properties.enter.y.value += sz;
  6021      }
  6022      
  6023      // padding from legend border
  6024      titles.properties.enter.x.value += padding;
  6025      titles.properties.enter.y.value += padding;
  6026      gradient.properties.enter.x.value += padding;
  6027      gradient.properties.enter.y.value += padding;
  6028      labels.properties.enter.y.value += padding;
  6029  
  6030      return {
  6031        type: "group",
  6032        interactive: false,
  6033        properties: {
  6034          enter: vg.parse.properties("group", legendStyle),
  6035          update: vg_legendUpdate
  6036        },
  6037        marks: [titles, gradient, labels].map(vg.parse.mark)
  6038      };
  6039    }
  6040  
  6041    legend.size = function(x) {
  6042      if (!arguments.length) return size;
  6043      if (size !== x) { size = x; reset(); }
  6044      return legend;
  6045    };
  6046  
  6047    legend.shape = function(x) {
  6048      if (!arguments.length) return shape;
  6049      if (shape !== x) { shape = x; reset(); }
  6050      return legend;
  6051    };
  6052  
  6053    legend.fill = function(x) {
  6054      if (!arguments.length) return fill;
  6055      if (fill !== x) { fill = x; reset(); }
  6056      return legend;
  6057    };
  6058    
  6059    legend.stroke = function(x) {
  6060      if (!arguments.length) return stroke;
  6061      if (stroke !== x) { stroke = x; reset(); }
  6062      return legend;
  6063    };
  6064  
  6065    legend.title = function(x) {
  6066      if (!arguments.length) return title;
  6067      if (title !== x) { title = x; reset(); }
  6068      return legend;
  6069    };
  6070  
  6071    legend.format = function(x) {
  6072      if (!arguments.length) return format;
  6073      if (format !== x) { format = x; reset(); }
  6074      return legend;
  6075    };
  6076  
  6077    legend.spacing = function(x) {
  6078      if (!arguments.length) return spacing;
  6079      if (spacing !== +x) { spacing = +x; reset(); }
  6080      return legend;
  6081    };
  6082  
  6083    legend.orient = function(x) {
  6084      if (!arguments.length) return orient;
  6085      orient = x in vg_legendOrients ? x + "" : vg.config.legend.orient;
  6086      return legend;
  6087    };
  6088  
  6089    legend.offset = function(x) {
  6090      if (!arguments.length) return offset;
  6091      offset = +x;
  6092      return legend;
  6093    };
  6094  
  6095    legend.values = function(x) {
  6096      if (!arguments.length) return values;
  6097      values = x;
  6098      return legend;
  6099    };
  6100  
  6101    legend.legendProperties = function(x) {
  6102      if (!arguments.length) return legendStyle;
  6103      legendStyle = x;
  6104      return legend;
  6105    };
  6106  
  6107    legend.symbolProperties = function(x) {
  6108      if (!arguments.length) return symbolStyle;
  6109      symbolStyle = x;
  6110      return legend;
  6111    };
  6112  
  6113    legend.gradientProperties = function(x) {
  6114      if (!arguments.length) return gradientStyle;
  6115      gradientStyle = x;
  6116      return legend;
  6117    };
  6118  
  6119    legend.labelProperties = function(x) {
  6120      if (!arguments.length) return labelStyle;
  6121      labelStyle = x;
  6122      return legend;
  6123    };
  6124    
  6125    legend.titleProperties = function(x) {
  6126      if (!arguments.length) return titleStyle;
  6127      titleStyle = x;
  6128      return legend;
  6129    };
  6130  
  6131    legend.reset = function() { reset(); };
  6132  
  6133    return legend;
  6134  };
  6135  
  6136  var vg_legendOrients = {right: 1, left: 1};
  6137  
  6138  function vg_legendUpdate(item, group, trans) {
  6139    var o = trans ? {} : item,
  6140        offset = item.mark.def.offset,
  6141        orient = item.mark.def.orient,
  6142        pad    = item.mark.def.padding * 2,
  6143        gx1    = group.bounds ? group.bounds.x1 : 0,
  6144        gx2    = group.bounds ? group.bounds.x2 : group.width,
  6145        lw     = ~~item.bounds.width() + (o.width ? 0 : pad),
  6146        lh     = ~~item.bounds.height() + (o.height ? 0 : pad);
  6147  
  6148    o.x = 0.5;
  6149    o.y = 0.5;
  6150    o.width = lw;
  6151    o.height = lh;
  6152  
  6153    switch (orient) {
  6154      case "left":  { o.x += gx1 - offset - lw; break; };
  6155      case "right": { o.x += gx2 + offset; break; };
  6156    }
  6157    
  6158    item.mark.def.properties.enter(item, group, trans);
  6159  }
  6160  
  6161  function vg_legendSymbolExtend(mark, size, shape, fill, stroke) {
  6162    var props = mark.properties.enter;
  6163    if (size)   props.size   = {scale: size.scaleName,   field: "data"};
  6164    if (shape)  props.shape  = {scale: shape.scaleName,  field: "data"};
  6165    if (fill)   props.fill   = {scale: fill.scaleName,   field: "data"};
  6166    if (stroke) props.stroke = {scale: stroke.scaleName, field: "data"};
  6167  }
  6168  
  6169  function vg_legendTitle() {
  6170    var cfg = vg.config.legend;
  6171    return {
  6172      type: "text",
  6173      interactive: false,
  6174      key: "data",
  6175      properties: {
  6176        enter: {
  6177          x: {value: 0},
  6178          y: {value: 0},
  6179          fill: {value: cfg.titleColor},
  6180          font: {value: cfg.titleFont},
  6181          fontSize: {value: cfg.titleFontSize},
  6182          fontWeight: {value: cfg.titleFontWeight},
  6183          baseline: {value: "top"},
  6184          text: {field: "data"},
  6185          opacity: {value: 1e-6}
  6186        },
  6187        exit: { opacity: {value: 1e-6} },
  6188        update: { opacity: {value: 1} }
  6189      }
  6190    };
  6191  }
  6192  
  6193  function vg_legendSymbols() {
  6194    var cfg = vg.config.legend;
  6195    return {
  6196      type: "symbol",
  6197      interactive: false,
  6198      key: "data",
  6199      properties: {
  6200        enter: {
  6201          x: {field: "offset", mult: 0.5},
  6202          y: {scale: "legend", field: "index"},
  6203          shape: {value: cfg.symbolShape},
  6204          size: {value: cfg.symbolSize},
  6205          stroke: {value: cfg.symbolColor},
  6206          strokeWidth: {value: cfg.symbolStrokeWidth},
  6207          opacity: {value: 1e-6}
  6208        },
  6209        exit: { opacity: {value: 1e-6} },
  6210        update: { opacity: {value: 1} }
  6211      }
  6212    };
  6213  }
  6214  
  6215  function vg_vLegendLabels() {
  6216    var cfg = vg.config.legend;
  6217    return {
  6218      type: "text",
  6219      interactive: false,
  6220      key: "data",
  6221      properties: {
  6222        enter: {
  6223          x: {field: "offset", offset: 5},
  6224          y: {scale: "legend", field: "index"},
  6225          fill: {value: cfg.labelColor},
  6226          font: {value: cfg.labelFont},
  6227          fontSize: {value: cfg.labelFontSize},
  6228          align: {value: cfg.labelAlign},
  6229          baseline: {value: cfg.labelBaseline},
  6230          text: {field: "label"},
  6231          opacity: {value: 1e-6}
  6232        },
  6233        exit: { opacity: {value: 1e-6} },
  6234        update: { opacity: {value: 1} }
  6235      }
  6236    };
  6237  }
  6238  
  6239  function vg_legendGradient() {
  6240    var cfg = vg.config.legend;
  6241    return {
  6242      type: "rect",
  6243      interactive: false,
  6244      properties: {
  6245        enter: {
  6246          x: {value: 0},
  6247          y: {value: 0},
  6248          width: {value: cfg.gradientWidth},
  6249          height: {value: cfg.gradientHeight},
  6250          stroke: {value: cfg.gradientStrokeColor},
  6251          strokeWidth: {value: cfg.gradientStrokeWidth},
  6252          opacity: {value: 1e-6}
  6253        },
  6254        exit: { opacity: {value: 1e-6} },
  6255        update: { opacity: {value: 1} }
  6256      }
  6257    };
  6258  }
  6259  
  6260  function vg_hLegendLabels() {
  6261    var cfg = vg.config.legend;
  6262    return {
  6263      type: "text",
  6264      interactive: false,
  6265      key: "data",
  6266      properties: {
  6267        enter: {
  6268          x: {scale: "legend", field: "data"},
  6269          y: {value: 20},
  6270          dy: {value: 2},
  6271          fill: {value: cfg.labelColor},
  6272          font: {value: cfg.labelFont},
  6273          fontSize: {value: cfg.labelFontSize},
  6274          align: {field: "align"},
  6275          baseline: {value: "top"},
  6276          text: {field: "label"},
  6277          opacity: {value: 1e-6}
  6278        },
  6279        exit: { opacity: {value: 1e-6} },
  6280        update: { opacity: {value: 1} }
  6281      }
  6282    };
  6283  }vg.Model = (function() {
  6284    function model() {
  6285      this._defs = null;
  6286      this._data = {};
  6287      this._scene = null;
  6288      this._reset = false;
  6289    }
  6290    
  6291    var prototype = model.prototype;
  6292    
  6293    prototype.defs = function(defs) {
  6294      if (!arguments.length) return this._defs;
  6295      this._defs = defs;
  6296      return this;
  6297    };
  6298    
  6299    prototype.data = function(data) {
  6300      if (!arguments.length) return this._data;
  6301  
  6302      var tx = this._defs.data.flow || {},
  6303          keys = this._defs.data.defs.map(vg.accessor("name")),
  6304          len = keys.length, i, k;
  6305  
  6306      for (i=0; i<len; ++i) {
  6307        if (!data[k=keys[i]]) continue;
  6308        this.ingest(k, tx, data[k]);
  6309      }
  6310  
  6311      return this;
  6312    };
  6313    
  6314    prototype.ingest = function(name, tx, input) {
  6315      this._data[name] = tx[name]
  6316        ? tx[name](input, this._data, this._defs.marks)
  6317        : input;
  6318      this.dependencies(name, tx);
  6319    };
  6320    
  6321    prototype.dependencies = function(name, tx) {
  6322      var source = this._defs.data.source[name],
  6323          data = this._data[name],
  6324          n = source ? source.length : 0, i, x;
  6325      for (i=0; i<n; ++i) {
  6326        x = vg_data_duplicate(data);
  6327        if (vg.isTree(data)) vg_make_tree(x);
  6328        this.ingest(source[i], tx, x);
  6329      }
  6330    };
  6331    
  6332    prototype.width = function(width) {
  6333      if (this._defs) this._defs.width = width;
  6334      if (this._defs && this._defs.marks) this._defs.marks.width = width;
  6335      if (this._scene) this._scene.items[0].width = width;
  6336      this._reset = true;
  6337      return this;
  6338    };
  6339    
  6340    prototype.height = function(height) {
  6341      if (this._defs) this._defs.height = height;
  6342      if (this._defs && this._defs.marks) this._defs.marks.height = height;
  6343      if (this._scene) this._scene.items[0].height = height;
  6344      this._reset = true;
  6345      return this;
  6346    };
  6347    
  6348    prototype.scene = function(node) {
  6349      if (!arguments.length) return this._scene;
  6350      this._scene = node;
  6351      return this;
  6352    };
  6353    
  6354    prototype.build = function() {
  6355      var m = this, data = m._data, marks = m._defs.marks;
  6356      m._scene = vg.scene.build.call(m, marks, data, m._scene);
  6357      m._scene.items[0].width = marks.width;
  6358      m._scene.items[0].height = marks.height;
  6359      m._scene.interactive = false;
  6360      return this;
  6361    };
  6362    
  6363    prototype.encode = function(trans, request, item) {
  6364      if (this._reset) { this.reset(); this._reset = false; }
  6365      var m = this, scene = m._scene, defs = m._defs;
  6366      vg.scene.encode.call(m, scene, defs.marks, trans, request, item);
  6367      return this;
  6368    };
  6369  
  6370    prototype.reset = function() {
  6371      if (this._scene) {
  6372        vg.scene.visit(this._scene, function(item) {
  6373          if (item.axes) item.axes.forEach(function(axis) { axis.reset(); });
  6374        });
  6375      }
  6376      return this;
  6377    };
  6378  
  6379    return model;
  6380  })();vg.View = (function() {
  6381    var view = function(el, width, height) {
  6382      this._el = null;
  6383      this._build = false;
  6384      this._model = new vg.Model();
  6385      this._width = this.__width = width || 500;
  6386      this._height = this.__height = height || 500;
  6387      this._autopad = 1;
  6388      this._padding = {top:0, left:0, bottom:0, right:0};
  6389      this._viewport = null;
  6390      this._renderer = null;
  6391      this._handler = null;
  6392      this._io = vg.canvas;
  6393      if (el) this.initialize(el);
  6394    };
  6395    
  6396    var prototype = view.prototype;
  6397    
  6398    prototype.width = function(width) {
  6399      if (!arguments.length) return this.__width;
  6400      if (this.__width !== width) {
  6401        this._width = this.__width = width;
  6402        if (this._el) this.initialize(this._el.parentNode);
  6403        this._model.width(width);
  6404        if (this._strict) this._autopad = 1;
  6405      }
  6406      return this;
  6407    };
  6408  
  6409    prototype.height = function(height) {
  6410      if (!arguments.length) return this.__height;
  6411      if (this.__height !== height) {
  6412        this._height = this.__height = height;
  6413        if (this._el) this.initialize(this._el.parentNode);
  6414        this._model.height(this._height);
  6415        if (this._strict) this._autopad = 1;
  6416      }
  6417      return this;
  6418    };
  6419  
  6420    prototype.padding = function(pad) {
  6421      if (!arguments.length) return this._padding;
  6422      if (this._padding !== pad) {
  6423        if (vg.isString(pad)) {
  6424          this._autopad = 1;
  6425          this._padding = {top:0, left:0, bottom:0, right:0};
  6426          this._strict = (pad === "strict");
  6427        } else {
  6428          this._autopad = 0;
  6429          this._padding = pad;
  6430          this._strict = false;
  6431        }
  6432        if (this._el) {
  6433          this._renderer.resize(this._width, this._height, pad);
  6434          this._handler.padding(pad);
  6435        }
  6436      }
  6437      return this;
  6438    };
  6439    
  6440    prototype.autopad = function(opt) {
  6441      if (this._autopad < 1) return this;
  6442      else this._autopad = 0;
  6443  
  6444      var pad = this._padding,
  6445          b = this.model().scene().bounds,
  6446          inset = vg.config.autopadInset,
  6447          l = b.x1 < 0 ? Math.ceil(-b.x1) + inset : 0,
  6448          t = b.y1 < 0 ? Math.ceil(-b.y1) + inset : 0,
  6449          r = b.x2 > this._width  ? Math.ceil(+b.x2 - this._width) + inset : 0,
  6450          b = b.y2 > this._height ? Math.ceil(+b.y2 - this._height) + inset : 0;
  6451      pad = {left:l, top:t, right:r, bottom:b};
  6452  
  6453      if (this._strict) {
  6454        this._autopad = 0;
  6455        this._padding = pad;
  6456        this._width = Math.max(0, this.__width - (l+r));
  6457        this._height = Math.max(0, this.__height - (t+b));
  6458        this._model.width(this._width);
  6459        this._model.height(this._height);
  6460        if (this._el) this.initialize(this._el.parentNode);
  6461        this.update({props:"enter"}).update({props:"update"});
  6462      } else {
  6463        this.padding(pad).update(opt);
  6464      }
  6465      return this;
  6466    };
  6467  
  6468    prototype.viewport = function(size) {
  6469      if (!arguments.length) return this._viewport;
  6470      if (this._viewport !== size) {
  6471        this._viewport = size;
  6472        if (this._el) this.initialize(this._el.parentNode);
  6473      }
  6474      return this;
  6475    };
  6476    
  6477    prototype.renderer = function(type) {
  6478      if (!arguments.length) return this._io;
  6479      if (type === "canvas") type = vg.canvas;
  6480      if (type === "svg") type = vg.svg;
  6481      if (this._io !== type) {
  6482        this._io = type;
  6483        this._renderer = null;
  6484        if (this._el) this.initialize(this._el.parentNode);
  6485        if (this._build) this.render();
  6486      }
  6487      return this;
  6488    };
  6489  
  6490    prototype.defs = function(defs) {
  6491      if (!arguments.length) return this._model.defs();
  6492      this._model.defs(defs);
  6493      return this;
  6494    };
  6495  
  6496    prototype.data = function(data) {
  6497      if (!arguments.length) return this._model.data();
  6498      var ingest = vg.keys(data).reduce(function(d, k) {
  6499        return (d[k] = vg.data.ingestAll(data[k]), d);
  6500      }, {});
  6501      this._model.data(ingest);
  6502      this._build = false;
  6503      return this;
  6504    };
  6505  
  6506    prototype.model = function(model) {
  6507      if (!arguments.length) return this._model;
  6508      if (this._model !== model) {
  6509        this._model = model;
  6510        if (this._handler) this._handler.model(model);
  6511      }
  6512      return this;
  6513    };
  6514  
  6515    prototype.initialize = function(el) {
  6516      var v = this, prevHandler,
  6517          w = v._width, h = v._height, pad = v._padding;
  6518      
  6519      // clear pre-existing container
  6520      d3.select(el).select("div.vega").remove();
  6521      
  6522      // add div container
  6523      this._el = el = d3.select(el)
  6524        .append("div")
  6525        .attr("class", "vega")
  6526        .style("position", "relative")
  6527        .node();
  6528      if (v._viewport) {
  6529        d3.select(el)
  6530          .style("width",  (v._viewport[0] || w)+"px")
  6531          .style("height", (v._viewport[1] || h)+"px")
  6532          .style("overflow", "auto");
  6533      }
  6534      
  6535      // renderer
  6536      v._renderer = (v._renderer || new this._io.Renderer())
  6537        .initialize(el, w, h, pad);
  6538      
  6539      // input handler
  6540      prevHandler = v._handler;
  6541      v._handler = new this._io.Handler()
  6542        .initialize(el, pad, v)
  6543        .model(v._model);
  6544  
  6545      if (prevHandler) {
  6546        prevHandler.handlers().forEach(function(h) {
  6547          v._handler.on(h.type, h.handler);
  6548        });
  6549      }
  6550      
  6551      return this;
  6552    };
  6553    
  6554    prototype.render = function(items) {
  6555      this._renderer.render(this._model.scene(), items);
  6556      return this;
  6557    };
  6558    
  6559    prototype.on = function() {
  6560      this._handler.on.apply(this._handler, arguments);
  6561      return this;
  6562    };
  6563    
  6564    prototype.off = function() {
  6565      this._handler.off.apply(this._handler, arguments);
  6566      return this;
  6567    };
  6568    
  6569    prototype.update = function(opt) {    
  6570      opt = opt || {};
  6571      var view = this,
  6572          trans = opt.duration
  6573            ? vg.scene.transition(opt.duration, opt.ease)
  6574            : null;
  6575  
  6576      view._build = view._build || (view._model.build(), true);
  6577      view._model.encode(trans, opt.props, opt.items);
  6578      
  6579      if (trans) {
  6580        trans.start(function(items) {
  6581          view._renderer.render(view._model.scene(), items);
  6582        });
  6583      }
  6584      else view.render(opt.items);
  6585  
  6586      return view.autopad(opt);
  6587    };
  6588        
  6589    return view;
  6590  })();
  6591  
  6592  // view constructor factory
  6593  // takes definitions from parsed specification as input
  6594  // returns a view constructor
  6595  vg.ViewFactory = function(defs) {
  6596    return function(opt) {
  6597      opt = opt || {};
  6598      var v = new vg.View()
  6599        .width(defs.width)
  6600        .height(defs.height)
  6601        .padding(defs.padding)
  6602        .viewport(defs.viewport)
  6603        .renderer(opt.renderer || "canvas")
  6604        .defs(defs);
  6605  
  6606      if (defs.data.load) v.data(defs.data.load);
  6607      if (opt.data) v.data(opt.data);
  6608      if (opt.el) v.initialize(opt.el);
  6609  
  6610      if (opt.hover !== false) {
  6611        v.on("mouseover", function(evt, item) {
  6612          if (item.hasPropertySet("hover")) {
  6613            this.update({props:"hover", items:item});
  6614          }
  6615        })
  6616        .on("mouseout", function(evt, item) {
  6617          if (item.hasPropertySet("hover")) {
  6618            this.update({props:"update", items:item});
  6619          }
  6620        });
  6621      }
  6622    
  6623      return v;
  6624    };
  6625  };
  6626  vg.Spec = (function() {
  6627    var spec = function(s) {
  6628      this.spec = {
  6629        width: 500,
  6630        height: 500,
  6631        padding: 0,
  6632        data: [],
  6633        scales: [],
  6634        axes: [],
  6635        marks: []
  6636      };
  6637      if (s) vg.extend(this.spec, s);
  6638    };
  6639    
  6640    var prototype = spec.prototype;
  6641  
  6642    prototype.width = function(w) {
  6643      this.spec.width = w;
  6644      return this;
  6645    };
  6646    
  6647    prototype.height = function(h) {
  6648      this.spec.height = h;
  6649      return this;
  6650    };
  6651    
  6652    prototype.padding = function(p) {
  6653      this.spec.padding = p;
  6654      return this;
  6655    };
  6656    
  6657    prototype.viewport = function(v) {
  6658      this.spec.viewport = v;
  6659      return this;
  6660    };
  6661  
  6662    prototype.data = function(name, params) {
  6663      if (!params) params = vg.isString(name) ? {name: name} : name;
  6664      else params.name = name;
  6665      this.spec.data.push(params);
  6666      return this;
  6667    };
  6668    
  6669    prototype.scale = function(name, params) {
  6670      if (!params) params = vg.isString(name) ? {name: name} : name;
  6671      else params.name = name;
  6672      this.spec.scales.push(params);
  6673      return this;
  6674    };
  6675    
  6676    prototype.axis = function(params) {
  6677      this.spec.axes.push(params);
  6678      return this;
  6679    };
  6680    
  6681    prototype.mark = function(type, mark) {
  6682      if (!mark) mark = {type: type};
  6683      else mark.type = type;
  6684      mark.properties = {};
  6685      this.spec.marks.push(mark);
  6686      
  6687      var that = this;
  6688      return {
  6689        from: function(name, obj) {
  6690                mark.from = obj
  6691                  ? (obj.data = name, obj)
  6692                  : vg.isString(name) ? {data: name} : name;
  6693                return this;
  6694              },
  6695        prop: function(name, obj) {
  6696                mark.properties[name] = vg.keys(obj).reduce(function(o,k) {
  6697                  var v = obj[k];
  6698                  return (o[k] = vg.isObject(v) ? v : {value: v}, o);
  6699                }, {});
  6700                return this;
  6701              },
  6702        done: function() { return that; }
  6703      };
  6704    };
  6705  
  6706    prototype.parse = function(callback) {
  6707      vg.parse.spec(this.spec, callback);
  6708    };
  6709  
  6710    prototype.json = function() {
  6711      return this.spec;
  6712    };
  6713  
  6714    return spec;
  6715  })();
  6716  
  6717  vg.spec = function(s) {
  6718    return new vg.Spec(s);
  6719  };
  6720  vg.headless = {};vg.headless.View = (function() {
  6721    
  6722    var view = function(width, height, pad, type) {
  6723      this._canvas = null;
  6724      this._type = type;
  6725      this._el = "body";
  6726      this._build = false;
  6727      this._model = new vg.Model();
  6728      this._width = this.__width = width || 500;
  6729      this._height = this.__height = height || 500;
  6730      this._autopad = 1;
  6731      this._padding = pad || {top:0, left:0, bottom:0, right:0};
  6732      this._renderer = new vg[type].Renderer();
  6733      this.initialize();
  6734    };
  6735    
  6736    var prototype = view.prototype;
  6737  
  6738    prototype.el = function(el) {
  6739      if (!arguments.length) return this._el;
  6740      if (this._el !== el) {
  6741        this._el = el;
  6742        this.initialize();
  6743      }
  6744      return this;
  6745    };
  6746  
  6747    prototype.width = function(width) {
  6748      if (!arguments.length) return this._width;
  6749      if (this._width !== width) {
  6750        this._width = width;
  6751        this.initialize();
  6752        this._model.width(width);
  6753      }
  6754      return this;
  6755    };
  6756  
  6757    prototype.height = function(height) {
  6758      if (!arguments.length) return this._height;
  6759      if (this._height !== height) {
  6760        this._height = height;
  6761        this.initialize();
  6762        this._model.height(this._height);
  6763      }
  6764      return this;
  6765    };
  6766  
  6767    prototype.padding = function(pad) {
  6768      if (!arguments.length) return this._padding;
  6769      if (this._padding !== pad) {
  6770        if (vg.isString(pad)) {
  6771          this._autopad = 1;
  6772          this._padding = {top:0, left:0, bottom:0, right:0};
  6773          this._strict = (pad === "strict");
  6774        } else {
  6775          this._autopad = 0;
  6776          this._padding = pad;
  6777          this._strict = false;
  6778        }
  6779        this.initialize();
  6780      }
  6781      return this;
  6782    };
  6783  
  6784    prototype.autopad = function(opt) {
  6785      if (this._autopad < 1) return this;
  6786      else this._autopad = 0;
  6787  
  6788      var pad = this._padding,
  6789          b = this._model.scene().bounds,
  6790          inset = vg.config.autopadInset,
  6791          l = b.x1 < 0 ? Math.ceil(-b.x1) + inset : 0,
  6792          t = b.y1 < 0 ? Math.ceil(-b.y1) + inset : 0,
  6793          r = b.x2 > this._width  ? Math.ceil(+b.x2 - this._width) + inset : 0,
  6794          b = b.y2 > this._height ? Math.ceil(+b.y2 - this._height) + inset : 0;
  6795      pad = {left:l, top:t, right:r, bottom:b};
  6796  
  6797      if (this._strict) {
  6798        this._autopad = 0;
  6799        this._padding = pad;
  6800        this._width = Math.max(0, this.__width - (l+r));
  6801        this._height = Math.max(0, this.__height - (t+b));
  6802        this._model.width(this._width);
  6803        this._model.height(this._height);
  6804        if (this._el) this.initialize();
  6805        this.update({props:"enter"}).update({props:"update"});
  6806      } else {
  6807        this.padding(pad).update(opt);
  6808      }
  6809      return this;
  6810    };
  6811  
  6812    prototype.viewport = function() {
  6813      if (!arguments.length) return null;
  6814      return this;
  6815    };
  6816  
  6817    prototype.defs = function(defs) {
  6818      if (!arguments.length) return this._model.defs();
  6819      this._model.defs(defs);
  6820      return this;
  6821    };
  6822  
  6823    prototype.data = function(data) {
  6824      if (!arguments.length) return this._model.data();
  6825      var ingest = vg.keys(data).reduce(function(d, k) {
  6826        return (d[k] = vg.data.ingestAll(data[k]), d);
  6827      }, {});
  6828      this._model.data(ingest);
  6829      this._build = false;
  6830      return this;
  6831    };
  6832  
  6833    prototype.renderer = function() {
  6834      return this._renderer;
  6835    };
  6836  
  6837    prototype.canvas = function() {
  6838      return this._canvas;
  6839    };
  6840    
  6841    prototype.canvasAsync = function(callback) {
  6842      var r = this._renderer, view = this;
  6843      
  6844      function wait() {
  6845        if (r.pendingImages() === 0) {
  6846          view.render(); // re-render with all images
  6847          callback(view._canvas);
  6848        } else {
  6849          setTimeout(wait, 10);
  6850        }
  6851      }
  6852  
  6853      // if images loading, poll until ready
  6854      (r.pendingImages() > 0) ? wait() : callback(this._canvas);
  6855    };
  6856    
  6857    prototype.svg = function() {
  6858      if (this._type !== "svg") return null;
  6859  
  6860      var p = this._padding,
  6861          w = this._width  + (p ? p.left + p.right : 0),
  6862          h = this._height + (p ? p.top + p.bottom : 0);
  6863  
  6864        // build svg text
  6865      var svg = d3.select(this._el)
  6866        .select("svg").node().innerHTML
  6867        .replace(/ href=/g, " xlink:href="); // ns hack. sigh.
  6868  
  6869      return '<svg '
  6870        + 'width="' + w + '" '
  6871        + 'height="' + h + '" '
  6872        + vg.config.svgNamespace + '>' + svg + '</svg>'
  6873    };
  6874  
  6875    prototype.initialize = function() {    
  6876      var w = this._width,
  6877          h = this._height,
  6878          pad = this._padding;
  6879      
  6880      if (this._type === "svg") {
  6881        this.initSVG(w, h, pad);
  6882      } else {
  6883        this.initCanvas(w, h, pad);
  6884      }
  6885      
  6886      return this;
  6887    };
  6888    
  6889    prototype.initCanvas = function(w, h, pad) {
  6890      var Canvas = require("canvas"),
  6891          tw = w + pad.left + pad.right,
  6892          th = h + pad.top + pad.bottom,
  6893          canvas = this._canvas = new Canvas(tw, th),
  6894          ctx = canvas.getContext("2d");
  6895      
  6896      // setup canvas context
  6897      ctx.setTransform(1, 0, 0, 1, pad.left, pad.top);
  6898  
  6899      // configure renderer
  6900      this._renderer.context(ctx);
  6901      this._renderer.resize(w, h, pad);
  6902    };
  6903    
  6904    prototype.initSVG = function(w, h, pad) {
  6905      var tw = w + pad.left + pad.right,
  6906          th = h + pad.top + pad.bottom;
  6907  
  6908      // configure renderer
  6909      this._renderer.initialize(this._el, w, h, pad);
  6910    }
  6911    
  6912    prototype.render = function(items) {
  6913      this._renderer.render(this._model.scene(), items);
  6914      return this;
  6915    };
  6916    
  6917    prototype.update = function(opt) {
  6918      opt = opt || {};
  6919      var view = this;
  6920      view._build = view._build || (view._model.build(), true);
  6921      view._model.encode(null, opt.props, opt.items);
  6922      view.render(opt.items);
  6923      return view.autopad(opt);
  6924    };
  6925      
  6926    return view;
  6927  })();
  6928  
  6929  // headless view constructor factory
  6930  // takes definitions from parsed specification as input
  6931  // returns a view constructor
  6932  vg.headless.View.Factory = function(defs) {
  6933    return function(opt) {
  6934      opt = opt || {};
  6935      var w = defs.width,
  6936          h = defs.height,
  6937          p = defs.padding,
  6938          r = opt.renderer || "canvas",
  6939          v = new vg.headless.View(w, h, p, r).defs(defs);
  6940      if (defs.data.load) v.data(defs.data.load);
  6941      if (opt.data) v.data(opt.data);
  6942      return v;
  6943    };
  6944  };vg.headless.render = function(opt, callback) {
  6945    function draw(chart) {
  6946      try {
  6947        // create and render view
  6948        var view = chart({
  6949          data: opt.data,
  6950          renderer: opt.renderer
  6951        }).update();
  6952  
  6953        if (opt.renderer === "svg") {
  6954          // extract rendered svg
  6955          callback(null, {svg: view.svg()});
  6956        } else {
  6957          // extract rendered canvas, waiting for any images to load
  6958          view.canvasAsync(function(canvas) {
  6959            callback(null, {canvas: canvas});
  6960          });
  6961        }
  6962      } catch (err) {
  6963        callback(err, null);
  6964      }
  6965    }
  6966  
  6967    vg.parse.spec(opt.spec, draw, vg.headless.View.Factory);
  6968  };  return vg;
  6969  })(d3, typeof topojson === "undefined" ? null : topojson);
  6970  // assumes D3 and topojson in global namespace