Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge branch 'release'

  • Loading branch information...
commit 3ea08db753a76f0a7ac7ac9b22a069cbfb059631 2 parents c421f90 + 2af6fbf
Mike Bostock authored August 23, 2011

Showing 91 changed files with 7,920 additions and 3,140 deletions. Show diff stats Hide diff stats

  1. 47  Makefile
  2. 213  d3.behavior.js
  3. 1  d3.behavior.min.js
  4. 2,097  d3.js
  5. 98  d3.layout.js
  6. 2  d3.layout.min.js
  7. 4  d3.min.js
  8. 11  d3.time.js
  9. 2  d3.time.min.js
  10. 117  examples/axis/axis-multiples.html
  11. 153  examples/axis/axis-transition.html
  12. 807  examples/data/stocks.csv
  13. 1,709  examples/data/unemployment.csv
  14. 6  examples/force/force-bounds.html
  15. 105  src/behavior/drag.js
  16. 12  src/core/array.js
  17. 2  src/core/core.js
  18. 6  src/core/select.js
  19. 5  src/core/selectAll.js
  20. 15  src/core/selection-append.js
  21. 44  src/core/selection-attr.js
  22. 4  src/core/{call.js → selection-call.js}
  23. 46  src/core/selection-classed.js
  24. 110  src/core/selection-data.js
  25. 9  src/core/selection-each.js
  26. 3  src/core/selection-empty.js
  27. 24  src/core/selection-enter-select.js
  28. 10  src/core/selection-enter.js
  29. 19  src/core/selection-filter.js
  30. 6  src/core/selection-html.js
  31. 20  src/core/selection-insert.js
  32. 5  src/core/selection-map.js
  33. 9  src/core/selection-node.js
  34. 34  src/core/selection-on.js
  35. 23  src/core/selection-property.js
  36. 9  src/core/selection-remove.js
  37. 30  src/core/selection-select.js
  38. 24  src/core/selection-selectAll.js
  39. 20  src/core/selection-sort.js
  40. 26  src/core/selection-style.js
  41. 6  src/core/selection-text.js
  42. 14  src/core/selection-transition.js
  43. 639  src/core/selection.js
  44. 23  src/core/transition-attr.js
  45. 6  src/core/transition-delay.js
  46. 6  src/core/transition-duration.js
  47. 9  src/core/transition-each.js
  48. 6  src/core/transition-remove.js
  49. 22  src/core/transition-select.js
  50. 20  src/core/transition-selectAll.js
  51. 14  src/core/transition-style.js
  52. 7  src/core/transition-text.js
  53. 294  src/core/transition.js
  54. 4  src/core/uninterpolate.js
  55. 98  src/layout/force.js
  56. 14  src/scale/linear.js
  57. 18  src/scale/log.js
  58. 84  src/scale/ordinal.js
  59. 19  src/scale/pow.js
  60. 23  src/scale/quantile.js
  61. 29  src/scale/quantize.js
  62. 182  src/svg/axis.js
  63. 2  src/time/scale-utc.js
  64. 9  src/time/scale.js
  65. 8  test/core/selection-on-test.js
  66. 35  test/core/selection-test.js
  67. 8  test/core/timer-test.js
  68. 33  test/core/transition-test-attr.js
  69. 67  test/core/transition-test-attrTween.js
  70. 33  test/core/transition-test-call.js
  71. 41  test/core/transition-test-delay.js
  72. 41  test/core/transition-test-duration.js
  73. 166  test/core/transition-test-each.js
  74. 20  test/core/transition-test-id.js
  75. 46  test/core/transition-test-remove.js
  76. 63  test/core/transition-test-select.js
  77. 55  test/core/transition-test-selectAll.js
  78. 38  test/core/transition-test-style.js
  79. 73  test/core/transition-test-styleTween.js
  80. 30  test/core/transition-test-text.js
  81. 71  test/core/transition-test-tween.js
  82. 72  test/core/transition-test.js
  83. 31  test/env-assert.js
  84. 23  test/scale/category-test.js
  85. 319  test/scale/linear-test.js
  86. 299  test/scale/log-test.js
  87. 185  test/scale/ordinal-test.js
  88. 533  test/scale/pow-test.js
  89. 256  test/scale/sqrt-test.js
  90. 329  test/svg/axis-test.js
  91. 750  test/time/scale-test.js
47  Makefile
@@ -6,8 +6,6 @@ JS_TESTER = ./node_modules/vows/bin/vows
6 6
 all: \
7 7
 	d3.js \
8 8
 	d3.min.js \
9  
-	d3.behavior.js \
10  
-	d3.behavior.min.js \
11 9
 	d3.chart.js \
12 10
 	d3.chart.min.js \
13 11
 	d3.layout.js \
@@ -26,6 +24,7 @@ all: \
26 24
 	d3.core.js \
27 25
 	d3.scale.js \
28 26
 	d3.svg.js \
  27
+	d3.behavior.js \
29 28
 	src/end.js
30 29
 
31 30
 d3.core.js: \
@@ -53,7 +52,6 @@ d3.core.js: \
53 52
 	src/core/merge.js \
54 53
 	src/core/split.js \
55 54
 	src/core/collapse.js \
56  
-	src/core/call.js \
57 55
 	src/core/range.js \
58 56
 	src/core/requote.js \
59 57
 	src/core/round.js \
@@ -71,11 +69,46 @@ d3.core.js: \
71 69
 	src/core/uninterpolate.js \
72 70
 	src/core/rgb.js \
73 71
 	src/core/hsl.js \
  72
+	src/core/select.js \
  73
+	src/core/selectAll.js \
74 74
 	src/core/selection.js \
  75
+	src/core/selection-select.js \
  76
+	src/core/selection-selectAll.js \
  77
+	src/core/selection-attr.js \
  78
+	src/core/selection-classed.js \
  79
+	src/core/selection-style.js \
  80
+	src/core/selection-property.js \
  81
+	src/core/selection-text.js \
  82
+	src/core/selection-html.js \
  83
+	src/core/selection-append.js \
  84
+	src/core/selection-insert.js \
  85
+	src/core/selection-remove.js \
  86
+	src/core/selection-data.js \
  87
+	src/core/selection-enter.js \
  88
+	src/core/selection-enter-select.js \
  89
+	src/core/selection-filter.js \
  90
+	src/core/selection-map.js \
  91
+	src/core/selection-sort.js \
  92
+	src/core/selection-on.js \
  93
+	src/core/selection-each.js \
  94
+	src/core/selection-call.js \
  95
+	src/core/selection-empty.js \
  96
+	src/core/selection-node.js \
  97
+	src/core/selection-transition.js \
75 98
 	src/core/transition.js \
  99
+	src/core/transition-select.js \
  100
+	src/core/transition-selectAll.js \
  101
+	src/core/transition-attr.js \
  102
+	src/core/transition-style.js \
  103
+	src/core/transition-text.js \
  104
+	src/core/transition-remove.js \
  105
+	src/core/transition-delay.js \
  106
+	src/core/transition-duration.js \
  107
+	src/core/transition-each.js \
76 108
 	src/core/timer.js \
77 109
 	src/core/noop.js
78 110
 
  111
+
79 112
 d3.scale.js: \
80 113
 	src/scale/scale.js \
81 114
 	src/scale/nice.js \
@@ -102,13 +135,13 @@ d3.svg.js: \
102 135
 	src/svg/diagonal-radial.js \
103 136
 	src/svg/mouse.js \
104 137
 	src/svg/touches.js \
105  
-	src/svg/symbol.js
  138
+	src/svg/symbol.js \
  139
+	src/svg/axis.js
106 140
 
107 141
 d3.behavior.js: \
108  
-	src/start.js \
109 142
 	src/behavior/behavior.js \
110  
-	src/behavior/zoom.js \
111  
-	src/end.js
  143
+	src/behavior/drag.js \
  144
+	src/behavior/zoom.js
112 145
 
113 146
 d3.chart.js: \
114 147
 	src/start.js \
213  d3.behavior.js
... ...
@@ -1,213 +0,0 @@
1  
-(function(){d3.behavior = {};
2  
-// TODO unbind zoom behavior?
3  
-// TODO unbind listener?
4  
-d3.behavior.zoom = function() {
5  
-  var xyz = [0, 0, 0],
6  
-      event = d3.dispatch("zoom");
7  
-
8  
-  function zoom() {
9  
-    this
10  
-        .on("mousedown.zoom", mousedown)
11  
-        .on("mousewheel.zoom", mousewheel)
12  
-        .on("DOMMouseScroll.zoom", dblclick)
13  
-        .on("dblclick.zoom", dblclick)
14  
-        .on("touchstart.zoom", touchstart);
15  
-
16  
-    d3.select(window)
17  
-        .on("mousemove.zoom", d3_behavior_zoomMousemove)
18  
-        .on("mouseup.zoom", d3_behavior_zoomMouseup)
19  
-        .on("touchmove.zoom", d3_behavior_zoomTouchmove)
20  
-        .on("touchend.zoom", d3_behavior_zoomTouchup)
21  
-        .on("click.zoom", d3_behavior_zoomClick, true);
22  
-  }
23  
-
24  
-  // snapshot the local context for subsequent dispatch
25  
-  function start() {
26  
-    d3_behavior_zoomXyz = xyz;
27  
-    d3_behavior_zoomDispatch = event.zoom.dispatch;
28  
-    d3_behavior_zoomTarget = this;
29  
-    d3_behavior_zoomArguments = arguments;
30  
-  }
31  
-
32  
-  function mousedown() {
33  
-    start.apply(this, arguments);
34  
-    d3_behavior_zoomPanning = d3_behavior_zoomLocation(d3.svg.mouse(d3_behavior_zoomTarget));
35  
-    d3_behavior_zoomMoved = false;
36  
-    d3.event.preventDefault();
37  
-    window.focus();
38  
-  }
39  
-
40  
-  // store starting mouse location
41  
-  function mousewheel() {
42  
-    start.apply(this, arguments);
43  
-    if (!d3_behavior_zoomZooming) d3_behavior_zoomZooming = d3_behavior_zoomLocation(d3.svg.mouse(d3_behavior_zoomTarget));
44  
-    d3_behavior_zoomTo(d3_behavior_zoomDelta() + xyz[2], d3.svg.mouse(d3_behavior_zoomTarget), d3_behavior_zoomZooming);
45  
-  }
46  
-
47  
-  function dblclick() {
48  
-    start.apply(this, arguments);
49  
-    var mouse = d3.svg.mouse(d3_behavior_zoomTarget);
50  
-    d3_behavior_zoomTo(d3.event.shiftKey ? Math.ceil(xyz[2] - 1) : Math.floor(xyz[2] + 1), mouse, d3_behavior_zoomLocation(mouse));
51  
-  }
52  
-
53  
-  // doubletap detection
54  
-  function touchstart() {
55  
-    start.apply(this, arguments);
56  
-    var touches = d3_behavior_zoomTouchup(),
57  
-        touch,
58  
-        now = Date.now();
59  
-    if ((touches.length === 1) && (now - d3_behavior_zoomLast < 300)) {
60  
-      d3_behavior_zoomTo(1 + Math.floor(xyz[2]), touch = touches[0], d3_behavior_zoomLocations[touch.identifier]);
61  
-    }
62  
-    d3_behavior_zoomLast = now;
63  
-  }
64  
-
65  
-  zoom.on = function(type, listener) {
66  
-    event[type].add(listener);
67  
-    return zoom;
68  
-  };
69  
-
70  
-  return zoom;
71  
-};
72  
-
73  
-var d3_behavior_zoomDiv,
74  
-    d3_behavior_zoomPanning,
75  
-    d3_behavior_zoomZooming,
76  
-    d3_behavior_zoomLocations = {}, // identifier -> location
77  
-    d3_behavior_zoomLast = 0,
78  
-    d3_behavior_zoomXyz,
79  
-    d3_behavior_zoomDispatch,
80  
-    d3_behavior_zoomTarget,
81  
-    d3_behavior_zoomArguments,
82  
-    d3_behavior_zoomStopClick;
83  
-
84  
-function d3_behavior_zoomLocation(point) {
85  
-  return [
86  
-    point[0] - d3_behavior_zoomXyz[0],
87  
-    point[1] - d3_behavior_zoomXyz[1],
88  
-    d3_behavior_zoomXyz[2]
89  
-  ];
90  
-}
91  
-
92  
-// detect the pixels that would be scrolled by this wheel event
93  
-function d3_behavior_zoomDelta() {
94  
-
95  
-  // mousewheel events are totally broken!
96  
-  // https://bugs.webkit.org/show_bug.cgi?id=40441
97  
-  // not only that, but Chrome and Safari differ in re. to acceleration!
98  
-  if (!d3_behavior_zoomDiv) {
99  
-    d3_behavior_zoomDiv = d3.select("body").append("div")
100  
-        .style("visibility", "hidden")
101  
-        .style("top", 0)
102  
-        .style("height", 0)
103  
-        .style("width", 0)
104  
-        .style("overflow-y", "scroll")
105  
-      .append("div")
106  
-        .style("height", "2000px")
107  
-      .node().parentNode;
108  
-  }
109  
-
110  
-  var e = d3.event, delta;
111  
-  try {
112  
-    d3_behavior_zoomDiv.scrollTop = 1000;
113  
-    d3_behavior_zoomDiv.dispatchEvent(e);
114  
-    delta = 1000 - d3_behavior_zoomDiv.scrollTop;
115  
-  } catch (error) {
116  
-    delta = e.wheelDelta || -e.detail;
117  
-  }
118  
-
119  
-  return delta * .005;
120  
-}
121  
-
122  
-// Note: Since we don't rotate, it's possible for the touches to become
123  
-// slightly detached from their original positions. Thus, we recompute the
124  
-// touch points on touchend as well as touchstart!
125  
-function d3_behavior_zoomTouchup() {
126  
-  var touches = d3.svg.touches(d3_behavior_zoomTarget),
127  
-      i = -1,
128  
-      n = touches.length,
129  
-      touch;
130  
-  while (++i < n) d3_behavior_zoomLocations[(touch = touches[i]).identifier] = d3_behavior_zoomLocation(touch);
131  
-  return touches;
132  
-}
133  
-
134  
-function d3_behavior_zoomTouchmove() {
135  
-  var touches = d3.svg.touches(d3_behavior_zoomTarget);
136  
-  switch (touches.length) {
137  
-
138  
-    // single-touch pan
139  
-    case 1: {
140  
-      var touch = touches[0];
141  
-      d3_behavior_zoomTo(d3_behavior_zoomXyz[2], touch, d3_behavior_zoomLocations[touch.identifier]);
142  
-      break;
143  
-    }
144  
-
145  
-    // double-touch pan + zoom
146  
-    case 2: {
147  
-      var p0 = touches[0],
148  
-          p1 = touches[1],
149  
-          p2 = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2],
150  
-          l0 = d3_behavior_zoomLocations[p0.identifier],
151  
-          l1 = d3_behavior_zoomLocations[p1.identifier],
152  
-          l2 = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2, l0[2]];
153  
-      d3_behavior_zoomTo(Math.log(d3.event.scale) / Math.LN2 + l0[2], p2, l2);
154  
-      break;
155  
-    }
156  
-  }
157  
-}
158  
-
159  
-function d3_behavior_zoomMousemove() {
160  
-  d3_behavior_zoomZooming = null;
161  
-  if (d3_behavior_zoomPanning) {
162  
-    d3_behavior_zoomMoved = true;
163  
-    d3_behavior_zoomTo(d3_behavior_zoomXyz[2], d3.svg.mouse(d3_behavior_zoomTarget), d3_behavior_zoomPanning);
164  
-  }
165  
-}
166  
-
167  
-function d3_behavior_zoomMouseup() {
168  
-  if (d3_behavior_zoomPanning) {
169  
-    if (d3_behavior_zoomMoved) d3_behavior_zoomStopClick = true;
170  
-    d3_behavior_zoomMousemove();
171  
-    d3_behavior_zoomPanning = null;
172  
-  }
173  
-}
174  
-
175  
-function d3_behavior_zoomClick() {
176  
-  if (d3_behavior_zoomStopClick) {
177  
-    d3.event.stopPropagation();
178  
-    d3.event.preventDefault();
179  
-    d3_behavior_zoomStopClick = false;
180  
-  }
181  
-}
182  
-
183  
-function d3_behavior_zoomTo(z, x0, x1) {
184  
-  var K = Math.pow(2, (d3_behavior_zoomXyz[2] = z) - x1[2]),
185  
-      x = d3_behavior_zoomXyz[0] = x0[0] - K * x1[0],
186  
-      y = d3_behavior_zoomXyz[1] = x0[1] - K * x1[1],
187  
-      o = d3.event, // Events can be reentrant (e.g., focus).
188  
-      k = Math.pow(2, z);
189  
-
190  
-  d3.event = {
191  
-    scale: k,
192  
-    translate: [x, y],
193  
-    transform: function(sx, sy) {
194  
-      if (sx) transform(sx, x);
195  
-      if (sy) transform(sy, y);
196  
-    }
197  
-  };
198  
-
199  
-  function transform(scale, o) {
200  
-    var domain = scale.__domain || (scale.__domain = scale.domain()),
201  
-        range = scale.range().map(function(v) { return (v - o) / k; });
202  
-    scale.domain(domain).domain(range.map(scale.invert));
203  
-  }
204  
-
205  
-  try {
206  
-    d3_behavior_zoomDispatch.apply(d3_behavior_zoomTarget, d3_behavior_zoomArguments);
207  
-  } finally {
208  
-    d3.event = o;
209  
-  }
210  
-
211  
-  o.preventDefault();
212  
-}
213  
-})();
1  d3.behavior.min.js
... ...
@@ -1 +0,0 @@
1  
-(function(){function r(a,b,c){function m(a,b){var c=a.__domain||(a.__domain=a.domain()),d=a.range().map(function(a){return(a-b)/l});a.domain(c).domain(d.map(a.invert))}var d=Math.pow(2,(f[2]=a)-c[2]),e=f[0]=b[0]-d*c[0],j=f[1]=b[1]-d*c[1],k=d3.event,l=Math.pow(2,a);d3.event={scale:l,translate:[e,j],transform:function(a,b){a&&m(a,e),b&&m(b,j)}};try{g.apply(h,i)}finally{d3.event=k}k.preventDefault()}function q(){j&&(d3.event.stopPropagation(),d3.event.preventDefault(),j=!1)}function p(){b&&(d3_behavior_zoomMoved&&(j=!0),o(),b=null)}function o(){c=null,b&&(d3_behavior_zoomMoved=!0,r(f[2],d3.svg.mouse(h),b))}function n(){var a=d3.svg.touches(h);switch(a.length){case 1:var b=a[0];r(f[2],b,d[b.identifier]);break;case 2:var c=a[0],e=a[1],g=[(c[0]+e[0])/2,(c[1]+e[1])/2],i=d[c.identifier],j=d[e.identifier],k=[(i[0]+j[0])/2,(i[1]+j[1])/2,i[2]];r(Math.log(d3.event.scale)/Math.LN2+i[2],g,k)}}function m(){var a=d3.svg.touches(h),b=-1,c=a.length,e;while(++b<c)d[(e=a[b]).identifier]=k(e);return a}function l(){a||(a=d3.select("body").append("div").style("visibility","hidden").style("top",0).style("height",0).style("width",0).style("overflow-y","scroll").append("div").style("height","2000px").node().parentNode);var b=d3.event,c;try{a.scrollTop=1e3,a.dispatchEvent(b),c=1e3-a.scrollTop}catch(d){c=b.wheelDelta||-b.detail}return c*.005}function k(a){return[a[0]-f[0],a[1]-f[1],f[2]]}d3.behavior={},d3.behavior.zoom=function(){function x(){t.apply(this,arguments);var b=m(),c,f=Date.now();b.length===1&&f-e<300&&r(1+Math.floor(a[2]),c=b[0],d[c.identifier]),e=f}function w(){t.apply(this,arguments);var b=d3.svg.mouse(h);r(d3.event.shiftKey?Math.ceil(a[2]-1):Math.floor(a[2]+1),b,k(b))}function v(){t.apply(this,arguments),c||(c=k(d3.svg.mouse(h))),r(l()+a[2],d3.svg.mouse(h),c)}function u(){t.apply(this,arguments),b=k(d3.svg.mouse(h)),d3_behavior_zoomMoved=!1,d3.event.preventDefault(),window.focus()}function t(){f=a,g=j.zoom.dispatch,h=this,i=arguments}function s(){this.on("mousedown.zoom",u).on("mousewheel.zoom",v).on("DOMMouseScroll.zoom",w).on("dblclick.zoom",w).on("touchstart.zoom",x),d3.select(window).on("mousemove.zoom",o).on("mouseup.zoom",p).on("touchmove.zoom",n).on("touchend.zoom",m).on("click.zoom",q,!0)}var a=[0,0,0],j=d3.dispatch("zoom");s.on=function(a,b){j[a].add(b);return s};return s};var a,b,c,d={},e=0,f,g,h,i,j})()
2,097  d3.js
... ...
@@ -1,4 +1,4 @@
1  
-(function(){d3 = {version: "1.29.7"}; // semver
  1
+(function(){d3 = {version: "2.0.0"}; // semver
2 2
 if (!Date.now) Date.now = function() {
3 3
   return +new Date;
4 4
 };
@@ -24,6 +24,18 @@ try {
24 24
 } catch(e) {
25 25
   d3_array = d3_arrayCopy;
26 26
 }
  27
+
  28
+var d3_arraySubclass = [].__proto__?
  29
+
  30
+// Until ECMAScript supports array subclassing, prototype injection works well.
  31
+function(array, prototype) {
  32
+  array.__proto__ = prototype;
  33
+}:
  34
+
  35
+// And if your browser doesn't support __proto__, we'll use direct extension.
  36
+function(array, prototype) {
  37
+  for (var property in prototype) array[property] = prototype[property];
  38
+};
27 39
 d3.functor = function(v) {
28 40
   return typeof v === "function" ? v : function() { return v; };
29 41
 };
@@ -303,19 +315,6 @@ function d3_splitter(d) {
303 315
 function d3_collapse(s) {
304 316
   return s.replace(/(^\s+)|(\s+$)/g, "").replace(/\s+/g, " ");
305 317
 }
306  
-//
307  
-// Note: assigning to the arguments array simultaneously changes the value of
308  
-// the corresponding argument!
309  
-//
310  
-// TODO The `this` argument probably shouldn't be the first argument to the
311  
-// callback, anyway, since it's redundant. However, that will require a major
312  
-// version bump due to backwards compatibility, so I'm not changing it right
313  
-// away.
314  
-//
315  
-function d3_call(callback) {
316  
-  callback.apply(this, (arguments[0] = this, arguments));
317  
-  return this;
318  
-}
319 318
 /**
320 319
  * @param {number} start
321 320
  * @param {number=} stop
@@ -845,12 +844,12 @@ d3.interpolators = [
845 844
   function(a, b) { return (typeof b === "number") && d3.interpolateNumber(+a, b); }
846 845
 ];
847 846
 function d3_uninterpolateNumber(a, b) {
848  
-  b = 1 / (b - (a = +a));
  847
+  b = b - (a = +a) ? 1 / (b - a) : 0;
849 848
   return function(x) { return (x - a) * b; };
850 849
 }
851 850
 
852 851
 function d3_uninterpolateClamp(a, b) {
853  
-  b = 1 / (b - (a = +a));
  852
+  b = b - (a = +a) ? 1 / (b - a) : 0;
854 853
   return function(x) { return Math.max(0, Math.min(1, (x - a) * b)); };
855 854
 }
856 855
 d3.rgb = function(r, g, b) {
@@ -1199,884 +1198,774 @@ function d3_hsl_rgb(h, s, l) {
1199 1198
 
1200 1199
   return d3_rgb(vv(h + 120), vv(h), vv(h - 120));
1201 1200
 }
1202  
-var d3_select = function(s, n) { return n.querySelector(s); },
1203  
-    d3_selectAll = function(s, n) { return d3_array(n.querySelectorAll(s)); };
1204  
-
1205  
-// Use Sizzle, if available.
1206  
-if (typeof Sizzle === "function") {
1207  
-  d3_select = function(s, n) { return Sizzle(s, n)[0]; };
1208  
-  d3_selectAll = function(s, n) { return Sizzle.uniqueSort(Sizzle(s, n)); };
1209  
-}
1210  
-
1211  
-var d3_root = d3_selection([[document]]);
1212  
-d3_root[0].parentNode = document.documentElement;
1213  
-
1214 1201
 // TODO fast singleton implementation!
1215 1202
 d3.select = function(selector) {
1216 1203
   return typeof selector === "string"
1217  
-      ? d3_root.select(selector)
  1204
+      ? d3_selectionRoot.select(selector)
1218 1205
       : d3_selection([[selector]]); // assume node
1219 1206
 };
1220  
-
1221 1207
 d3.selectAll = function(selector) {
1222 1208
   return typeof selector === "string"
1223  
-      ? d3_root.selectAll(selector)
  1209
+      ? d3_selectionRoot.selectAll(selector)
1224 1210
       : d3_selection([d3_array(selector)]); // assume node[]
1225 1211
 };
1226  
-
1227 1212
 function d3_selection(groups) {
  1213
+  d3_arraySubclass(groups, d3_selectionPrototype);
  1214
+  return groups;
  1215
+}
1228 1216
 
1229  
-  function select(select) {
1230  
-    var subgroups = [],
1231  
-        subgroup,
1232  
-        subnode,
1233  
-        group,
1234  
-        node;
1235  
-    for (var j = 0, m = groups.length; j < m; j++) {
1236  
-      group = groups[j];
1237  
-      subgroups.push(subgroup = []);
1238  
-      subgroup.parentNode = group.parentNode;
1239  
-      for (var i = 0, n = group.length; i < n; i++) {
1240  
-        if (node = group[i]) {
1241  
-          subgroup.push(subnode = select(node));
1242  
-          if (subnode && "__data__" in node) subnode.__data__ = node.__data__;
1243  
-        } else {
1244  
-          subgroup.push(null);
1245  
-        }
1246  
-      }
1247  
-    }
1248  
-    return d3_selection(subgroups);
1249  
-  }
1250  
-
1251  
-  function selectAll(selectAll) {
1252  
-    var subgroups = [],
1253  
-        subgroup,
1254  
-        group,
1255  
-        node;
1256  
-    for (var j = 0, m = groups.length; j < m; j++) {
1257  
-      group = groups[j];
1258  
-      for (var i = 0, n = group.length; i < n; i++) {
1259  
-        if (node = group[i]) {
1260  
-          subgroups.push(subgroup = selectAll(node));
1261  
-          subgroup.parentNode = node;
1262  
-        }
1263  
-      }
1264  
-    }
1265  
-    return d3_selection(subgroups);
1266  
-  }
  1217
+var d3_select = function(s, n) { return n.querySelector(s); },
  1218
+    d3_selectAll = function(s, n) { return d3_array(n.querySelectorAll(s)); };
1267 1219
 
1268  
-  // TODO select(function)?
1269  
-  groups.select = function(selector) {
1270  
-    return select(function(node) {
1271  
-      return d3_select(selector, node);
1272  
-    });
1273  
-  };
  1220
+// Prefer Sizzle, if available.
  1221
+if (typeof Sizzle === "function") {
  1222
+  d3_select = function(s, n) { return Sizzle(s, n)[0]; };
  1223
+  d3_selectAll = function(s, n) { return Sizzle.uniqueSort(Sizzle(s, n)); };
  1224
+}
1274 1225
 
1275  
-  // TODO selectAll(function)?
1276  
-  groups.selectAll = function(selector) {
1277  
-    return selectAll(function(node) {
1278  
-      return d3_selectAll(selector, node);
1279  
-    });
1280  
-  };
  1226
+var d3_selectionPrototype = [],
  1227
+    d3_selectionRoot = d3_selection([[document]]);
1281 1228
 
1282  
-  // TODO preserve null elements to maintain index?
1283  
-  groups.filter = function(filter) {
1284  
-    var subgroups = [],
1285  
-        subgroup,
1286  
-        group,
1287  
-        node;
1288  
-    for (var j = 0, m = groups.length; j < m; j++) {
1289  
-      group = groups[j];
1290  
-      subgroups.push(subgroup = []);
1291  
-      subgroup.parentNode = group.parentNode;
1292  
-      for (var i = 0, n = group.length; i < n; i++) {
1293  
-        if ((node = group[i]) && filter.call(node, node.__data__, i)) {
1294  
-          subgroup.push(node);
1295  
-        }
1296  
-      }
1297  
-    }
1298  
-    return d3_selection(subgroups);
1299  
-  };
  1229
+d3_selectionRoot[0].parentNode = document.documentElement;
1300 1230
 
1301  
-  groups.map = function(map) {
1302  
-    var group,
1303  
-        node;
1304  
-    for (var j = 0, m = groups.length; j < m; j++) {
1305  
-      group = groups[j];
1306  
-      for (var i = 0, n = group.length; i < n; i++) {
1307  
-        if (node = group[i]) node.__data__ = map.call(node, node.__data__, i);
1308  
-      }
1309  
-    }
1310  
-    return groups;
1311  
-  };
  1231
+d3.selection = function() {
  1232
+  return d3_selectionRoot;
  1233
+};
1312 1234
 
1313  
-  // TODO data(null) for clearing data?
1314  
-  groups.data = function(data, join) {
1315  
-    var enter = [],
1316  
-        update = [],
1317  
-        exit = [];
1318  
-
1319  
-    function bind(group, groupData) {
1320  
-      var i = 0,
1321  
-          n = group.length,
1322  
-          m = groupData.length,
1323  
-          n0 = Math.min(n, m),
1324  
-          n1 = Math.max(n, m),
1325  
-          updateNodes = [],
1326  
-          enterNodes = [],
1327  
-          exitNodes = [],
1328  
-          node,
1329  
-          nodeData;
1330  
-
1331  
-      if (join) {
1332  
-        var nodeByKey = {},
1333  
-            keys = [],
1334  
-            key,
1335  
-            j = groupData.length;
1336  
-
1337  
-        for (i = 0; i < n; i++) {
1338  
-          key = join.call(node = group[i], node.__data__, i);
1339  
-          if (key in nodeByKey) {
1340  
-            exitNodes[j++] = node; // duplicate key
1341  
-          } else {
1342  
-            nodeByKey[key] = node;
1343  
-          }
1344  
-          keys.push(key);
1345  
-        }
  1235
+d3.selection.prototype = d3_selectionPrototype;
  1236
+d3_selectionPrototype.select = function(selector) {
  1237
+  var subgroups = [],
  1238
+      subgroup,
  1239
+      subnode,
  1240
+      group,
  1241
+      node;
1346 1242
 
1347  
-        for (i = 0; i < m; i++) {
1348  
-          node = nodeByKey[key = join.call(groupData, nodeData = groupData[i], i)];
1349  
-          if (node) {
1350  
-            node.__data__ = nodeData;
1351  
-            updateNodes[i] = node;
1352  
-            enterNodes[i] = exitNodes[i] = null;
1353  
-          } else {
1354  
-            enterNodes[i] = d3_selection_enterNode(nodeData);
1355  
-            updateNodes[i] = exitNodes[i] = null;
1356  
-          }
1357  
-          delete nodeByKey[key];
1358  
-        }
  1243
+  if (typeof selector !== "function") selector = d3_selection_selector(selector);
1359 1244
 
1360  
-        for (i = 0; i < n; i++) {
1361  
-          if (keys[i] in nodeByKey) {
1362  
-            exitNodes[i] = group[i];
1363  
-          }
1364  
-        }
  1245
+  for (var j = -1, m = this.length; ++j < m;) {
  1246
+    subgroups.push(subgroup = []);
  1247
+    subgroup.parentNode = (group = this[j]).parentNode;
  1248
+    for (var i = -1, n = group.length; ++i < n;) {
  1249
+      if (node = group[i]) {
  1250
+        subgroup.push(subnode = selector(node));
  1251
+        if (subnode && "__data__" in node) subnode.__data__ = node.__data__;
1365 1252
       } else {
1366  
-        for (; i < n0; i++) {
1367  
-          node = group[i];
1368  
-          nodeData = groupData[i];
1369  
-          if (node) {
1370  
-            node.__data__ = nodeData;
1371  
-            updateNodes[i] = node;
1372  
-            enterNodes[i] = exitNodes[i] = null;
1373  
-          } else {
1374  
-            enterNodes[i] = d3_selection_enterNode(nodeData);
1375  
-            updateNodes[i] = exitNodes[i] = null;
1376  
-          }
1377  
-        }
1378  
-        for (; i < m; i++) {
1379  
-          enterNodes[i] = d3_selection_enterNode(groupData[i]);
1380  
-          updateNodes[i] = exitNodes[i] = null;
1381  
-        }
1382  
-        for (; i < n1; i++) {
1383  
-          exitNodes[i] = group[i];
1384  
-          enterNodes[i] = updateNodes[i] = null;
1385  
-        }
  1253
+        subgroup.push(null);
1386 1254
       }
1387  
-
1388  
-      enterNodes.parentNode
1389  
-          = updateNodes.parentNode
1390  
-          = exitNodes.parentNode
1391  
-          = group.parentNode;
1392  
-
1393  
-      enter.push(enterNodes);
1394  
-      update.push(updateNodes);
1395  
-      exit.push(exitNodes);
1396 1255
     }
  1256
+  }
1397 1257
 
1398  
-    var i = -1,
1399  
-        n = groups.length,
1400  
-        group;
1401  
-    if (typeof data === "function") {
1402  
-      while (++i < n) {
1403  
-        bind(group = groups[i], data.call(group, group.parentNode.__data__, i));
1404  
-      }
1405  
-    } else {
1406  
-      while (++i < n) {
1407  
-        bind(group = groups[i], data);
1408  
-      }
1409  
-    }
  1258
+  return d3_selection(subgroups);
  1259
+};
1410 1260
 
1411  
-    var selection = d3_selection(update);
1412  
-    selection.enter = function() {
1413  
-      return d3_selectionEnter(enter);
1414  
-    };
1415  
-    selection.exit = function() {
1416  
-      return d3_selection(exit);
1417  
-    };
1418  
-    return selection;
  1261
+function d3_selection_selector(selector) {
  1262
+  return function(node) {
  1263
+    return d3_select(selector, node);
1419 1264
   };
  1265
+}
  1266
+d3_selectionPrototype.selectAll = function(selector) {
  1267
+  var subgroups = [],
  1268
+      subgroup,
  1269
+      node;
1420 1270
 
1421  
-  // TODO mask forEach? or rename for eachData?
1422  
-  // TODO offer the same semantics for map, reduce, etc.?
1423  
-  groups.each = function(callback) {
1424  
-    for (var j = 0, m = groups.length; j < m; j++) {
1425  
-      var group = groups[j];
1426  
-      for (var i = 0, n = group.length; i < n; i++) {
1427  
-        var node = group[i];
1428  
-        if (node) callback.call(node, node.__data__, i);
1429  
-      }
1430  
-    }
1431  
-    return groups;
1432  
-  };
  1271
+  if (typeof selector !== "function") selector = d3_selection_selectorAll(selector);
1433 1272
 
1434  
-  function first(callback) {
1435  
-    for (var j = 0, m = groups.length; j < m; j++) {
1436  
-      var group = groups[j];
1437  
-      for (var i = 0, n = group.length; i < n; i++) {
1438  
-        var node = group[i];
1439  
-        if (node) return callback.call(node, node.__data__, i);
  1273
+  for (var j = -1, m = this.length; ++j < m;) {
  1274
+    for (var group = this[j], i = -1, n = group.length; ++i < n;) {
  1275
+      if (node = group[i]) {
  1276
+        subgroups.push(subgroup = selector(node));
  1277
+        subgroup.parentNode = node;
1440 1278
       }
1441 1279
     }
1442  
-    return null;
1443 1280
   }
1444 1281
 
1445  
-  groups.empty = function() {
1446  
-    return !first(function() { return true; });
1447  
-  };
  1282
+  return d3_selection(subgroups);
  1283
+};
1448 1284
 
1449  
-  groups.node = function() {
1450  
-    return first(function() { return this; });
  1285
+function d3_selection_selectorAll(selector) {
  1286
+  return function(node) {
  1287
+    return d3_selectAll(selector, node);
1451 1288
   };
  1289
+}
  1290
+d3_selectionPrototype.attr = function(name, value) {
  1291
+  name = d3.ns.qualify(name);
1452 1292
 
1453  
-  groups.attr = function(name, value) {
1454  
-    name = d3.ns.qualify(name);
1455  
-
1456  
-    // If no value is specified, return the first value.
1457  
-    if (arguments.length < 2) {
1458  
-      return first(name.local
1459  
-          ? function() { return this.getAttributeNS(name.space, name.local); }
1460  
-          : function() { return this.getAttribute(name); });
1461  
-    }
1462  
-
1463  
-    /** @this {Element} */
1464  
-    function attrNull() {
1465  
-      this.removeAttribute(name);
1466  
-    }
1467  
-
1468  
-    /** @this {Element} */
1469  
-    function attrNullNS() {
1470  
-      this.removeAttributeNS(name.space, name.local);
1471  
-    }
  1293
+  // If no value is specified, return the first value.
  1294
+  if (arguments.length < 2) {
  1295
+    var node = this.node();
  1296
+    return name.local
  1297
+        ? node.getAttributeNS(name.space, name.local)
  1298
+        : node.getAttribute(name);
  1299
+  }
1472 1300
 
1473  
-    /** @this {Element} */
1474  
-    function attrConstant() {
1475  
-      this.setAttribute(name, value);
1476  
-    }
  1301
+  function attrNull() {
  1302
+    this.removeAttribute(name);
  1303
+  }
1477 1304
 
1478  
-    /** @this {Element} */
1479  
-    function attrConstantNS() {
1480  
-      this.setAttributeNS(name.space, name.local, value);
1481  
-    }
  1305
+  function attrNullNS() {
  1306
+    this.removeAttributeNS(name.space, name.local);
  1307
+  }
1482 1308
 
1483  
-    /** @this {Element} */
1484  
-    function attrFunction() {
1485  
-      var x = value.apply(this, arguments);
1486  
-      if (x == null) this.removeAttribute(name);
1487  
-      else this.setAttribute(name, x);
1488  
-    }
  1309
+  function attrConstant() {
  1310
+    this.setAttribute(name, value);
  1311
+  }
1489 1312
 
1490  
-    /** @this {Element} */
1491  
-    function attrFunctionNS() {
1492  
-      var x = value.apply(this, arguments);
1493  
-      if (x == null) this.removeAttributeNS(name.space, name.local);
1494  
-      else this.setAttributeNS(name.space, name.local, x);
1495  
-    }
  1313
+  function attrConstantNS() {
  1314
+    this.setAttributeNS(name.space, name.local, value);
  1315
+  }
1496 1316
 
1497  
-    return groups.each(value == null
1498  
-        ? (name.local ? attrNullNS : attrNull) : (typeof value === "function"
1499  
-        ? (name.local ? attrFunctionNS : attrFunction)
1500  
-        : (name.local ? attrConstantNS : attrConstant)));
1501  
-  };
  1317
+  function attrFunction() {
  1318
+    var x = value.apply(this, arguments);
  1319
+    if (x == null) this.removeAttribute(name);
  1320
+    else this.setAttribute(name, x);
  1321
+  }
1502 1322
 
1503  
-  groups.classed = function(name, value) {
1504  
-    var re = new RegExp("(^|\\s+)" + d3.requote(name) + "(\\s+|$)", "g");
  1323
+  function attrFunctionNS() {
  1324
+    var x = value.apply(this, arguments);
  1325
+    if (x == null) this.removeAttributeNS(name.space, name.local);
  1326
+    else this.setAttributeNS(name.space, name.local, x);
  1327
+  }
1505 1328
 
1506  
-    // If no value is specified, return the first value.
1507  
-    if (arguments.length < 2) {
1508  
-      return first(function() {
1509  
-        if (c = this.classList) return c.contains(name);
1510  
-        var c = this.className;
1511  
-        re.lastIndex = 0;
1512  
-        return re.test(c.baseVal != null ? c.baseVal : c);
1513  
-      });
1514  
-    }
  1329
+  return this.each(value == null
  1330
+      ? (name.local ? attrNullNS : attrNull) : (typeof value === "function"
  1331
+      ? (name.local ? attrFunctionNS : attrFunction)
  1332
+      : (name.local ? attrConstantNS : attrConstant)));
  1333
+};
  1334
+d3_selectionPrototype.classed = function(name, value) {
  1335
+  var re = new RegExp("(^|\\s+)" + d3.requote(name) + "(\\s+|$)", "g");
1515 1336
 
1516  
-    /** @this {Element} */
1517  
-    function classedAdd() {
1518  
-      if (c = this.classList) return c.add(name);
1519  
-      var c = this.className,
1520  
-          cb = c.baseVal != null,
1521  
-          cv = cb ? c.baseVal : c;
1522  
-      re.lastIndex = 0;
1523  
-      if (!re.test(cv)) {
1524  
-        cv = d3_collapse(cv + " " + name);
1525  
-        if (cb) c.baseVal = cv;
1526  
-        else this.className = cv;
1527  
-      }
1528  
-    }
  1337
+  // If no value is specified, return the first value.
  1338
+  if (arguments.length < 2) {
  1339
+    var node = this.node();
  1340
+    if (c = node.classList) return c.contains(name);
  1341
+    var c = node.className;
  1342
+    re.lastIndex = 0;
  1343
+    return re.test(c.baseVal != null ? c.baseVal : c);
  1344
+  }
1529 1345
 
1530  
-    /** @this {Element} */
1531  
-    function classedRemove() {
1532  
-      if (c = this.classList) return c.remove(name);
1533  
-      var c = this.className,
1534  
-          cb = c.baseVal != null,
1535  
-          cv = cb ? c.baseVal : c;
1536  
-      cv = d3_collapse(cv.replace(re, " "));
  1346
+  function classedAdd() {
  1347
+    if (c = this.classList) return c.add(name);
  1348
+    var c = this.className,
  1349
+        cb = c.baseVal != null,
  1350
+        cv = cb ? c.baseVal : c;
  1351
+    re.lastIndex = 0;
  1352
+    if (!re.test(cv)) {
  1353
+      cv = d3_collapse(cv + " " + name);
1537 1354
       if (cb) c.baseVal = cv;
1538 1355
       else this.className = cv;
1539 1356
     }
  1357
+  }
1540 1358
 
1541  
-    /** @this {Element} */
1542  
-    function classedFunction() {
1543  
-      (value.apply(this, arguments)
1544  
-          ? classedAdd
1545  
-          : classedRemove).call(this);
1546  
-    }
  1359
+  function classedRemove() {
  1360
+    if (c = this.classList) return c.remove(name);
  1361
+    var c = this.className,
  1362
+        cb = c.baseVal != null,
  1363
+        cv = cb ? c.baseVal : c;
  1364
+    cv = d3_collapse(cv.replace(re, " "));
  1365
+    if (cb) c.baseVal = cv;
  1366
+    else this.className = cv;
  1367
+  }
1547 1368
 
1548  
-    return groups.each(typeof value === "function"
1549  
-        ? classedFunction : value
  1369
+  function classedFunction() {
  1370
+    (value.apply(this, arguments)
1550 1371
         ? classedAdd
1551  
-        : classedRemove);
1552  
-  };
1553  
-
1554  
-  groups.style = function(name, value, priority) {
1555  
-    if (arguments.length < 3) priority = "";
  1372
+        : classedRemove).call(this);
  1373
+  }
1556 1374
 
1557  
-    // If no value is specified, return the first value.
1558  
-    if (arguments.length < 2) {
1559  
-      return first(function() {
1560  
-        return window.getComputedStyle(this, null).getPropertyValue(name);
1561  
-      });
1562  
-    }
  1375
+  return this.each(typeof value === "function"
  1376
+      ? classedFunction : value
  1377
+      ? classedAdd
  1378
+      : classedRemove);
  1379
+};
  1380
+d3_selectionPrototype.style = function(name, value, priority) {
  1381
+  if (arguments.length < 3) priority = "";
1563 1382
 
1564  
-    /** @this {Element} */
1565  
-    function styleNull() {
1566  
-      this.style.removeProperty(name);
1567  
-    }
  1383
+  // If no value is specified, return the first value.
  1384
+  if (arguments.length < 2) return window
  1385
+      .getComputedStyle(this.node(), null)
  1386
+      .getPropertyValue(name);
1568 1387
 
1569  
-    /** @this {Element} */
1570  
-    function styleConstant() {
1571  
-      this.style.setProperty(name, value, priority);
1572  
-    }
  1388
+  function styleNull() {
  1389
+    this.style.removeProperty(name);
  1390
+  }
1573 1391
 
1574  
-    /** @this {Element} */
1575  
-    function styleFunction() {
1576  
-      var x = value.apply(this, arguments);
1577  
-      if (x == null) this.style.removeProperty(name);
1578  
-      else this.style.setProperty(name, x, priority);
1579  
-    }
  1392
+  function styleConstant() {
  1393
+    this.style.setProperty(name, value, priority);
  1394
+  }
1580 1395
 
1581  
-    return groups.each(value == null
1582  
-        ? styleNull : (typeof value === "function"
1583  
-        ? styleFunction : styleConstant));
1584  
-  };
  1396
+  function styleFunction() {
  1397
+    var x = value.apply(this, arguments);
  1398
+    if (x == null) this.style.removeProperty(name);
  1399
+    else this.style.setProperty(name, x, priority);
  1400
+  }
1585 1401
 
1586  
-  groups.property = function(name, value) {
1587  
-    name = d3.ns.qualify(name);
  1402
+  return this.each(value == null
  1403
+      ? styleNull : (typeof value === "function"
  1404
+      ? styleFunction : styleConstant));
  1405
+};
  1406
+d3_selectionPrototype.property = function(name, value) {
1588 1407
 
1589  
-    // If no value is specified, return the first value.
1590  
-    if (arguments.length < 2) {
1591  
-      return first(function() {
1592  
-        return this[name];
1593  
-      });
1594  
-    }
  1408
+  // If no value is specified, return the first value.
  1409
+  if (arguments.length < 2) return this.node()[name];
1595 1410
 
1596  
-    /** @this {Element} */
1597  
-    function propertyNull() {
1598  
-      delete this[name];
1599  
-    }
  1411
+  function propertyNull() {
  1412
+    delete this[name];
  1413
+  }
1600 1414
 
1601  
-    /** @this {Element} */
1602  
-    function propertyConstant() {
1603  
-      this[name] = value;
1604  
-    }
  1415
+  function propertyConstant() {
  1416
+    this[name] = value;
  1417
+  }
1605 1418
 
1606  
-    /** @this {Element} */
1607  
-    function propertyFunction() {
1608  
-      var x = value.apply(this, arguments);
1609  
-      if (x == null) delete this[name];
1610  
-      else this[name] = x;
1611  
-    }
  1419
+  function propertyFunction() {
  1420
+    var x = value.apply(this, arguments);
  1421
+    if (x == null) delete this[name];
  1422
+    else this[name] = x;
  1423
+  }
1612 1424
 
1613  
-    return groups.each(value == null
1614  
-        ? propertyNull : (typeof value === "function"
1615  
-        ? propertyFunction : propertyConstant));
1616  
-  };
  1425
+  return this.each(value == null
  1426
+      ? propertyNull : (typeof value === "function"
  1427
+      ? propertyFunction : propertyConstant));
  1428
+};
  1429
+d3_selectionPrototype.text = function(value) {
  1430
+  return arguments.length < 1 ? this.node().textContent
  1431
+      : (this.each(typeof value === "function"
  1432
+      ? function() { this.textContent = value.apply(this, arguments); }
  1433
+      : function() { this.textContent = value; }));
  1434
+};
  1435
+d3_selectionPrototype.html = function(value) {
  1436
+  return arguments.length < 1 ? this.node().innerHTML
  1437
+      : (this.each(typeof value === "function"
  1438
+      ? function() { this.innerHTML = value.apply(this, arguments); }
  1439
+      : function() { this.innerHTML = value; }));
  1440
+};
  1441
+// TODO append(node)?
  1442
+// TODO append(function)?
  1443
+d3_selectionPrototype.append = function(name) {
  1444
+  name = d3.ns.qualify(name);
1617 1445
 
1618  
-  groups.text = function(value) {
  1446
+  function append(node) {
  1447
+    return node.appendChild(document.createElement(name));
  1448
+  }
1619 1449
 
1620  
-    // If no value is specified, return the first value.
1621  
-    if (arguments.length < 1) {
1622  
-      return first(function() {
1623  
-        return this.textContent;
1624  
-      });
1625  
-    }
  1450
+  function appendNS(node) {
  1451
+    return node.appendChild(document.createElementNS(name.space, name.local));
  1452
+  }
1626 1453
 
1627  
-    /** @this {Element} */
1628  
-    function textConstant() {
1629  
-      this.textContent = value;
1630  
-    }
  1454
+  return this.select(name.local ? appendNS : append);
  1455
+};
  1456
+// TODO insert(node, function)?
  1457
+// TODO insert(function, string)?
  1458
+// TODO insert(function, function)?
  1459
+d3_selectionPrototype.insert = function(name, before) {
  1460
+  name = d3.ns.qualify(name);
1631 1461
 
1632  
-    /** @this {Element} */
1633  
-    function textFunction() {
1634  
-      this.textContent = value.apply(this, arguments);
1635  
-    }
  1462
+  function insert(node) {
  1463
+    return node.insertBefore(
  1464
+        document.createElement(name),
  1465
+        d3_select(before, node));
  1466
+  }
1636 1467
 
1637  
-    return groups.each(typeof value === "function"
1638  
-        ? textFunction : textConstant);
1639  
-  };
  1468
+  function insertNS(node) {
  1469
+    return node.insertBefore(
  1470
+        document.createElementNS(name.space, name.local),
  1471
+        d3_select(before, node));
  1472
+  }
1640 1473
 
1641  
-  groups.html = function(value) {
  1474
+  return this.select(name.local ? insertNS : insert);
  1475
+};
  1476
+// TODO remove(selector)?
  1477
+// TODO remove(node)?
  1478
+// TODO remove(function)?
  1479
+d3_selectionPrototype.remove = function() {
  1480
+  return this.each(function() {
  1481
+    var parent = this.parentNode;
  1482
+    if (parent) parent.removeChild(this);
  1483
+  });
  1484
+};
  1485
+// TODO data(null) for clearing data?
  1486
+d3_selectionPrototype.data = function(data, join) {
  1487
+  var enter = [],
  1488
+      update = [],
  1489
+      exit = [];
  1490
+
  1491
+  function bind(group, groupData) {
  1492
+    var i,
  1493
+        n = group.length,
  1494
+        m = groupData.length,
  1495
+        n0 = Math.min(n, m),
  1496
+        n1 = Math.max(n, m),
  1497
+        updateNodes = [],
  1498
+        enterNodes = [],
  1499
+        exitNodes = [],
  1500
+        node,
  1501
+        nodeData;
  1502
+
  1503
+    if (join) {
  1504
+      var nodeByKey = {},
  1505
+          keys = [],
  1506
+          key,
  1507
+          j = groupData.length;
  1508
+
  1509
+      for (i = -1; ++i < n;) {
  1510
+        key = join.call(node = group[i], node.__data__, i);
  1511
+        if (key in nodeByKey) {
  1512
+          exitNodes[j++] = node; // duplicate key
  1513
+        } else {
  1514
+          nodeByKey[key] = node;
  1515
+        }
  1516
+        keys.push(key);
  1517
+      }
1642 1518
 
1643  
-    // If no value is specified, return the first value.
1644  
-    if (arguments.length < 1) {
1645  
-      return first(function() {
1646  
-        return this.innerHTML;
1647  
-      });
1648  
-    }
  1519
+      for (i = -1; ++i < m;) {
  1520
+        node = nodeByKey[key = join.call(groupData, nodeData = groupData[i], i)];
  1521
+        if (node) {
  1522
+          node.__data__ = nodeData;
  1523
+          updateNodes[i] = node;
  1524
+          enterNodes[i] = exitNodes[i] = null;
  1525
+        } else {
  1526
+          enterNodes[i] = d3_selection_dataNode(nodeData);
  1527
+          updateNodes[i] = exitNodes[i] = null;
  1528
+        }
  1529
+        delete nodeByKey[key];
  1530
+      }
1649 1531
 
1650  
-    /** @this {Element} */
1651  
-    function htmlConstant() {
1652  
-      this.innerHTML = value;
  1532
+      for (i = -1; ++i < n;) {
  1533
+        if (keys[i] in nodeByKey) {
  1534
+          exitNodes[i] = group[i];
  1535
+        }
  1536
+      }
  1537
+    } else {
  1538
+      for (i = -1; ++i < n0;) {
  1539
+        node = group[i];
  1540
+        nodeData = groupData[i];
  1541
+        if (node) {
  1542
+          node.__data__ = nodeData;
  1543
+          updateNodes[i] = node;
  1544
+          enterNodes[i] = exitNodes[i] = null;
  1545
+        } else {
  1546
+          enterNodes[i] = d3_selection_dataNode(nodeData);
  1547
+          updateNodes[i] = exitNodes[i] = null;
  1548
+        }
  1549
+      }
  1550
+      for (; i < m; ++i) {
  1551
+        enterNodes[i] = d3_selection_dataNode(groupData[i]);
  1552
+        updateNodes[i] = exitNodes[i] = null;
  1553
+      }
  1554
+      for (; i < n1; ++i) {
  1555
+        exitNodes[i] = group[i];
  1556
+        enterNodes[i] = updateNodes[i] = null;
  1557
+      }
1653 1558
     }
1654 1559
 
1655  
-    /** @this {Element} */
1656  
-    function htmlFunction() {
1657  
-      this.innerHTML = value.apply(this, arguments);
1658  
-    }
  1560
+    enterNodes.update
  1561
+        = updateNodes;
1659 1562
 
1660  
-    return groups.each(typeof value === "function"
1661  
-        ? htmlFunction : htmlConstant);
1662  
-  };
  1563
+    enterNodes.parentNode
  1564
+        = updateNodes.parentNode
  1565
+        = exitNodes.parentNode
  1566
+        = group.parentNode;
1663 1567
 
1664  
-  // TODO append(node)?
1665  
-  // TODO append(function)?
1666  
-  groups.append = function(name) {
1667  
-    name = d3.ns.qualify(name);
  1568
+    enter.push(enterNodes);
  1569
+    update.push(updateNodes);
  1570
+    exit.push(exitNodes);
  1571
+  }
1668 1572
 
1669  
-    function append(node) {
1670  
-      return node.appendChild(document.createElement(name));
  1573
+  var i = -1,
  1574
+      n = this.length,
  1575
+      group;
  1576
+  if (typeof data === "function") {
  1577
+    while (++i < n) {
  1578
+      bind(group = this[i], data.call(group, group.parentNode.__data__, i));
1671 1579
     }
1672  
-
1673  
-    function appendNS(node) {
1674  
-      return node.appendChild(document.createElementNS(name.space, name.local));
  1580
+  } else {
  1581
+    while (++i < n) {
  1582
+      bind(group = this[i], data);
1675 1583
     }
  1584
+  }
1676 1585
 
1677  
-    return select(name.local ? appendNS : append);
1678  
-  };
1679  
-
1680  
-  // TODO insert(node, function)?
1681  
-  // TODO insert(function, string)?
1682  
-  // TODO insert(function, function)?
1683  
-  groups.insert = function(name, before) {
1684  
-    name = d3.ns.qualify(name);
1685  
-
1686  
-    function insert(node) {
1687  
-      return node.insertBefore(
1688  
-          document.createElement(name),
1689  
-          d3_select(before, node));
1690  
-    }
  1586
+  var selection = d3_selection(update);
  1587
+  selection.enter = function() { return d3_selection_enter(enter); };
  1588
+  selection.exit = function() { return d3_selection(exit); };
  1589
+  return selection;
  1590
+};
1691 1591
 
1692  
-    function insertNS(node) {
1693  
-      return node.insertBefore(
1694  
-          document.createElementNS(name.space, name.local),
1695  
-          d3_select(before, node));
  1592
+function d3_selection_dataNode(data) {
  1593
+  return {__data__: data};
  1594
+}
  1595
+function d3_selection_enter(selection) {
  1596
+  d3_arraySubclass(selection, d3_selection_enterPrototype);
  1597
+  return selection;
  1598
+}
  1599
+
  1600
+var d3_selection_enterPrototype = [];
  1601
+
  1602
+d3_selection_enterPrototype.append = d3_selectionPrototype.append;
  1603
+d3_selection_enterPrototype.insert = d3_selectionPrototype.insert;
  1604
+d3_selection_enterPrototype.empty = d3_selectionPrototype.empty;