block by bollwyvl 8463137

8463137

Full Screen

This is based on Jason Davies’ parallel coordinates visualization of cars from the ‘70s and ‘80s which demonstrated one of D3 2.5.0’s new interactive features: the brush component. By clicking and dragging along any axis, you can specify a filter for that dimension.

This version demonstrates the multibrush component by Humanities + Design. After creating a filter, click and drag again on the same axis, and you can create a second filter. You will see all cars that match either filter.

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
    <title>Multibrush Parallel Coordinates</title>
    <style type="text/css">

svg {
  font: 10px sans-serif;
}

.background path {
  fill: none;
  stroke: #ccc;
  stroke-opacity: .4;
  shape-rendering: crispEdges;
}

.foreground path {
  fill: none;
  stroke: steelblue;
  stroke-opacity: .7;
}

.brush .extent {
  fill-opacity: .3;
  stroke: #fff;
  shape-rendering: crispEdges;
}

.axis line, .axis path {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.axis text {
  text-shadow: 0 1px 0 #fff;
  cursor: move;
}

    </style>
  </head>
  <body>
    <script type="text/javascript" src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
    <script type="text/javascript" src="./d3.svg.multibrush.js" charset="utf-8"></script>
    <script type="text/javascript">

var m = [30, 10, 10, 10],
    w = 960 - m[1] - m[3],
    h = 500 - m[0] - m[2];

var x = d3.scale.ordinal().rangePoints([0, w], 1),
    y = {},
    dragging = {};

var line = d3.svg.line(),
    axis = d3.svg.axis().orient("left"),
    background,
    foreground;

var svg = d3.select("body").append("svg:svg")
    .attr("width", w + m[1] + m[3])
    .attr("height", h + m[0] + m[2])
  .append("svg:g")
    .attr("transform", "translate(" + m[3] + "," + m[0] + ")");

d3.csv("cars.csv", function(cars) {

  // Extract the list of dimensions and create a scale for each.
  x.domain(dimensions = d3.keys(cars[0]).filter(function(d) {
    return d != "name" && (y[d] = d3.scale.linear()
        .domain(d3.extent(cars, function(p) { return +p[d]; }))
        .range([h, 0]));
  }));

  // Add grey background lines for context.
  background = svg.append("svg:g")
      .attr("class", "background")
    .selectAll("path")
      .data(cars)
    .enter().append("svg:path")
      .attr("d", path);

  // Add blue foreground lines for focus.
  foreground = svg.append("svg:g")
      .attr("class", "foreground")
    .selectAll("path")
      .data(cars)
    .enter().append("svg:path")
      .attr("d", path);

  // Add a group element for each dimension.
  var g = svg.selectAll(".dimension")
      .data(dimensions)
    .enter().append("svg:g")
      .attr("class", "dimension")
      .attr("transform", function(d) { return "translate(" + x(d) + ")"; })
      .call(d3.behavior.drag()
        .on("dragstart", function(d) {
          dragging[d] = this.__origin__ = x(d);
          background.attr("visibility", "hidden");
        })
        .on("drag", function(d) {
          dragging[d] = Math.min(w, Math.max(0, this.__origin__ += d3.event.dx));
          foreground.attr("d", path);
          dimensions.sort(function(a, b) { return position(a) - position(b); });
          x.domain(dimensions);
          g.attr("transform", function(d) { return "translate(" + position(d) + ")"; })
        })
        .on("dragend", function(d) {
          delete this.__origin__;
          delete dragging[d];
          transition(d3.select(this)).attr("transform", "translate(" + x(d) + ")");
          transition(foreground)
              .attr("d", path);
          background
              .attr("d", path)
              .transition()
              .delay(500)
              .duration(0)
              .attr("visibility", null);
        }));

  // Add an axis and title.
  g.append("svg:g")
      .attr("class", "axis")
      .each(function(d) { d3.select(this).call(axis.scale(y[d])); })
    .append("svg:text")
      .attr("text-anchor", "middle")
      .attr("y", -9)
      .text(String);

  // Add and store a brush for each axis.
  g.append("svg:g")
      .attr("class", "brush")
      .each(function(d) {
				d3.select(this).call(y[d].brush = d3.svg.multibrush()
					.extentAdaption(resizeExtent)
					.y(y[d]).on("brush", brush));
			})
			.selectAll("rect").call(resizeExtent);
});

function resizeExtent(selection){
	selection
		.attr("x", -8)
		.attr("width", 16);
}

function position(d) {
  var v = dragging[d];
  return v == null ? x(d) : v;
}

function transition(g) {
  return g.transition().duration(500);
}

// Returns the path for a given data point.
function path(d) {
  return line(dimensions.map(function(p) { return [position(p), y[p](d[p])]; }));
}

// Handles a brush event, toggling the display of foreground lines.
function brush() {
  var actives = dimensions.filter(function(p) { return !y[p].brush.empty(); }),
      extents = actives.map(function(p) { return y[p].brush.extent(); });
  foreground.style("display", function(d) {
    return actives.every(function(p, i) {
			return extents[i].some(function(e){
				return e[0] <= d[p] && d[p] <= e[1];
			});
    }) ? null : "none";
  });
}

    </script>
  </body>
</html>

cars.csv

name,economy (mpg),cylinders,displacement (cc),power (hp),weight (lb),0-60 mph (s),year
AMC Ambassador Brougham,13,8,360,175,3821,11,73
AMC Ambassador DPL,15,8,390,190,3850,8.5,70
AMC Ambassador SST,17,8,304,150,3672,11.5,72
AMC Concord DL 6,20.2,6,232,90,3265,18.2,79
AMC Concord DL,18.1,6,258,120,3410,15.1,78
AMC Concord DL,23,4,151,,3035,20.5,82
AMC Concord,19.4,6,232,90,3210,17.2,78
AMC Concord,24.3,4,151,90,3003,20.1,80
AMC Gremlin,18,6,232,100,2789,15,73
AMC Gremlin,19,6,232,100,2634,13,71
AMC Gremlin,20,6,232,100,2914,16,75
AMC Gremlin,21,6,199,90,2648,15,70
AMC Hornet Sportabout (Wagon),18,6,258,110,2962,13.5,71
AMC Hornet,18,6,199,97,2774,15.5,70
AMC Hornet,18,6,232,100,2945,16,73
AMC Hornet,19,6,232,100,2901,16,74
AMC Hornet,22.5,6,232,90,3085,17.6,76
AMC Matador (Wagon),14,8,304,150,4257,15.5,74
AMC Matador (Wagon),15,8,304,150,3892,12.5,72
AMC Matador,14,8,304,150,3672,11.5,73
AMC Matador,15,6,258,110,3730,19,75
AMC Matador,15.5,8,304,120,3962,13.9,76
AMC Matador,16,6,258,110,3632,18,74
AMC Matador,18,6,232,100,3288,15.5,71
AMC Pacer D/L,17.5,6,258,95,3193,17.8,76
AMC Pacer,19,6,232,90,3211,17,75
AMC Rebel SST (Wagon),,8,360,175,3850,11,70
AMC Rebel SST,16,8,304,150,3433,12,70
AMC Spirit DL,27.4,4,121,80,2670,15,79
Audi 100 LS,20,4,114,91,2582,14,73
Audi 100 LS,23,4,115,95,2694,15,75
Audi 100 LS,24,4,107,90,2430,14.5,70
Audi 4000,34.3,4,97,78,2188,15.8,80
Audi 5000,20.3,5,131,103,2830,15.9,78
Audi 5000S (Diesel),36.4,5,121,67,2950,19.9,80
Audi Fox,29,4,98,83,2219,16.5,74
BMW 2002,26,4,121,113,2234,12.5,70
BMW 320i,21.5,4,121,110,2600,12.8,77
Buick Century 350,13,8,350,175,4100,13,73
Buick Century Limited,25,6,181,110,2945,16.4,82
Buick Century Luxus (Wagon),13,8,350,150,4699,14.5,74
Buick Century Special,20.6,6,231,105,3380,15.8,78
Buick Century,17,6,231,110,3907,21,75
Buick Century,22.4,6,231,110,3415,15.8,81
Buick Electra 225 Custom,12,8,455,225,4951,11,73
Buick Estate Wagon (Wagon),14,8,455,225,3086,10,70
Buick Estate Wagon (Wagon),16.9,8,350,155,4360,14.9,79
Buick Lesabre Custom,13,8,350,155,4502,13.5,72
Buick Opel Isuzu Deluxe,30,4,111,80,2155,14.8,77
Buick Regal Sport Coupe (Turbo),17.7,6,231,165,3445,13.4,78
Buick Skyhawk,21,6,231,110,3039,15,75
Buick Skylark 320,15,8,350,165,3693,11.5,70
Buick Skylark Limited,28.4,4,151,90,2670,16,79
Buick Skylark,20.5,6,231,105,3425,16.9,77
Buick Skylark,26.6,4,151,84,2635,16.4,81
Cadillac Eldorado,23,8,350,125,3900,17.4,79
Cadillac Seville,16.5,8,350,180,4380,12.1,76
Chevroelt Chevelle Malibu,16,6,250,105,3897,18.5,75
Chevrolet Bel Air,15,8,350,145,4440,14,75
Chevrolet Camaro,27,4,151,90,2950,17.3,82
Chevrolet Caprice Classic,13,8,400,150,4464,12,73
Chevrolet Caprice Classic,17,8,305,130,3840,15.4,79
Chevrolet Caprice Classic,17.5,8,305,145,3880,12.5,77
Chevrolet Cavalier 2-Door,34,4,112,88,2395,18,82
Chevrolet Cavalier Wagon,27,4,112,88,2640,18.6,82
Chevrolet Cavalier,28,4,112,88,2605,19.6,82
Chevrolet Chevelle Concours (Wagon),,8,350,165,4142,11.5,70
Chevrolet Chevelle Concours (Wagon),13,8,307,130,4098,14,72
Chevrolet Chevelle Malibu Classic,16,6,250,100,3781,17,74
Chevrolet Chevelle Malibu Classic,17.5,8,305,140,4215,13,76
Chevrolet Chevelle Malibu,17,6,250,100,3329,15.5,71
Chevrolet Chevelle Malibu,18,8,307,130,3504,12,70
Chevrolet Chevette,29,4,85,52,2035,22.2,76
Chevrolet Chevette,30,4,98,68,2155,16.5,78
Chevrolet Chevette,30.5,4,98,63,2051,17,77
Chevrolet Chevette,32.1,4,98,70,2120,15.5,80
Chevrolet Citation,23.5,6,173,110,2725,12.6,81
Chevrolet Citation,28,4,151,90,2678,16.5,80
Chevrolet Citation,28.8,6,173,115,2595,11.3,79
Chevrolet Concours,17.5,6,250,110,3520,16.4,77
Chevrolet Impala,11,8,400,150,4997,14,73
Chevrolet Impala,13,8,350,165,4274,12,72
Chevrolet Impala,14,8,350,165,4209,12,71
Chevrolet Impala,14,8,454,220,4354,9,70
Chevrolet Malibu Classic (Wagon),19.2,8,267,125,3605,15,79
Chevrolet Malibu,13,8,350,145,3988,13,73
Chevrolet Malibu,20.5,6,200,95,3155,18.2,78
Chevrolet Monte Carlo Landau,15.5,8,350,170,4165,11.4,77
Chevrolet Monte Carlo Landau,19.2,8,305,145,3425,13.2,78
Chevrolet Monte Carlo S,15,8,350,145,4082,13,73
Chevrolet Monte Carlo,15,8,400,150,3761,9.5,70
Chevrolet Monza 2+2,20,8,262,110,3221,13.5,75
Chevrolet Nova Custom,16,6,250,100,3278,18,73
Chevrolet Nova,15,6,250,100,3336,17,74
Chevrolet Nova,18,6,250,105,3459,16,75
Chevrolet Nova,22,6,250,105,3353,14.5,76
Chevrolet Vega (Wagon),22,4,140,72,2408,19,71
Chevrolet Vega 2300,28,4,140,90,2264,15.5,71
Chevrolet Vega,20,4,140,90,2408,19.5,72
Chevrolet Vega,21,4,140,72,2401,19.5,73
Chevrolet Vega,25,4,140,75,2542,17,74
Chevrolet Woody,24.5,4,98,60,2164,22.1,76
Chevy C10,13,8,350,145,4055,12,76
Chevy C20,10,8,307,200,4376,15,70
Chevy S-10,31,4,119,82,2720,19.4,82
Chrysler Cordoba,15.5,8,400,190,4325,12.2,77
Chrysler Lebaron Medallion,26,4,156,92,2585,14.5,82
Chrysler Lebaron Salon,17.6,6,225,85,3465,16.6,81
Chrysler Lebaron Town & Country (Wagon),18.5,8,360,150,3940,13,79
Chrysler New Yorker Brougham,13,8,440,215,4735,11,73
Chrysler Newport Royal,13,8,400,190,4422,12.5,72
Citroen DS-21 Pallas,,4,133,115,3090,17.5,70
Datsun 1200,35,4,72,69,1613,18,71
Datsun 200SX,23.9,4,119,97,2405,14.9,78
Datsun 200SX,32.9,4,119,100,2615,14.8,81
Datsun 210,31.8,4,85,65,2020,19.2,79
Datsun 210,37,4,85,65,1975,19.4,81
Datsun 210,40.8,4,85,65,2110,19.2,80
Datsun 280ZX,32.7,6,168,132,2910,11.4,80
Datsun 310 GX,38,4,91,67,1995,16.2,82
Datsun 310,37.2,4,86,65,2019,16.4,80
Datsun 510 (Wagon),28,4,97,92,2288,17,72
Datsun 510 Hatchback,37,4,119,92,2434,15,80
Datsun 510,27.2,4,119,97,2300,14.7,78
Datsun 610,22,4,108,94,2379,16.5,73
Datsun 710,24,4,119,97,2545,17,75
Datsun 710,32,4,83,61,2003,19,74
Datsun 810 Maxima,24.2,6,146,120,2930,13.8,81
Datsun 810,22,6,146,97,2815,14.5,77
Datsun B-210,32,4,85,70,1990,17,76
Datsun B210 GX,39.4,4,85,70,2070,18.6,78
Datsun B210,31,4,79,67,1950,19,74
Datsun F-10 Hatchback,33.5,4,85,70,1945,16.8,77
Datsun PL510,27,4,97,88,2130,14.5,70
Datsun PL510,27,4,97,88,2130,14.5,71
Dodge Aries SE,29,4,135,84,2525,16,82
Dodge Aries Wagon (Wagon),25.8,4,156,92,2620,14.4,81
Dodge Aspen 6,20.6,6,225,110,3360,16.6,79
Dodge Aspen SE,20,6,225,100,3651,17.7,76
Dodge Aspen,18.6,6,225,110,3620,18.7,78
Dodge Aspen,19.1,6,225,90,3381,18.7,80
Dodge Challenger SE,15,8,383,170,3563,10,70
Dodge Charger 2.2,36,4,135,84,2370,13,82
Dodge Colt (Wagon),28,4,98,80,2164,15,72
Dodge Colt Hardtop,25,4,97.5,80,2126,17,72
Dodge Colt Hatchback Custom,35.7,4,98,80,1915,14.4,79
Dodge Colt M/M,33.5,4,98,83,2075,15.9,77
Dodge Colt,26,4,98,79,2255,17.7,76
Dodge Colt,27.9,4,156,105,2800,14.4,80
Dodge Colt,28,4,90,75,2125,14.5,74
Dodge Coronet Brougham,16,8,318,150,4190,13,76
Dodge Coronet Custom (Wagon),14,8,318,150,4457,13.5,74
Dodge Coronet Custom,15,8,318,150,3777,12.5,73
Dodge D100,13,8,318,150,3755,14,76
Dodge D200,11,8,318,210,4382,13.5,70
Dodge Dart Custom,15,8,318,150,3399,11,73
Dodge Diplomat,19.4,8,318,140,3735,13.2,78
Dodge Magnum XE,17.5,8,318,140,4080,13.7,78
Dodge Monaco (Wagon),12,8,383,180,4955,11.5,71
Dodge Monaco Brougham,15.5,8,318,145,4140,13.7,77
Dodge Omni,30.9,4,105,75,2230,14.5,78
Dodge Rampage,32,4,135,84,2295,11.6,82
Dodge St. Regis,18.2,8,318,135,3830,15.2,79
Fiat 124 Sport Coupe,26,4,98,90,2265,15.5,73
Fiat 124 TC,26,4,116,75,2246,14,74
Fiat 124B,30,4,88,76,2065,14.5,71
Fiat 128,24,4,90,75,2108,15.5,74
Fiat 128,29,4,68,49,1867,19.5,73
Fiat 131,28,4,107,86,2464,15.5,76
Fiat Strada Custom,37.3,4,91,69,2130,14.7,79
Fiat X1.9,31,4,79,67,2000,16,74
Ford Capri II,25,4,140,92,2572,14.9,76
Ford Country Squire (Wagon),13,8,400,170,4746,12,71
Ford Country Squire (Wagon),15.5,8,351,142,4054,14.3,79
Ford Country,12,8,400,167,4906,12.5,73
Ford Escort 2H,29.9,4,98,65,2380,20.7,81
Ford Escort 4W,34.4,4,98,65,2045,16.2,81
Ford F108,13,8,302,130,3870,15,76
Ford F250,10,8,360,215,4615,14,70
Ford Fairmont (Auto),20.2,6,200,85,2965,15.8,78
Ford Fairmont (Man),25.1,4,140,88,2720,15.4,78
Ford Fairmont 4,22.3,4,140,88,2890,17.3,79
Ford Fairmont Futura,24,4,140,92,2865,16.4,82
Ford Fairmont,26.4,4,140,88,2870,18.1,80
Ford Fiesta,36.1,4,98,66,1800,14.4,78
Ford Futura,18.1,8,302,139,3205,11.2,78
Ford Galaxie 500,14,8,351,153,4129,13,72
Ford Galaxie 500,14,8,351,153,4154,13.5,71
Ford Galaxie 500,15,8,429,198,4341,10,70
Ford Gran Torino (Wagon),13,8,302,140,4294,16,72
Ford Gran Torino (Wagon),14,8,302,140,4638,16,74
Ford Gran Torino,14,8,302,137,4042,14.5,73
Ford Gran Torino,14.5,8,351,152,4215,12.8,76
Ford Gran Torino,16,8,302,140,4141,14,74
Ford Granada Ghia,18,6,250,78,3574,21,76
Ford Granada GL,20.2,6,200,88,3060,17.1,81
Ford Granada L,22,6,232,112,2835,14.7,82
Ford Granada,18.5,6,250,98,3525,19,77
Ford LTD Landau,17.6,8,302,129,3725,13.4,79
Ford LTD,13,8,351,158,4363,13,73
Ford LTD,14,8,351,148,4657,13.5,75
Ford Maverick,15,6,250,72,3158,19.5,75
Ford Maverick,18,6,250,88,3021,16.5,73
Ford Maverick,21,6,200,,2875,17,74
Ford Maverick,21,6,200,85,2587,16,70
Ford Maverick,24,6,200,81,3012,17.6,76
Ford Mustang Boss 302,,8,302,140,3353,8,70
Ford Mustang Cobra,23.6,4,140,,2905,14.3,80
Ford Mustang GL,27,4,140,86,2790,15.6,82
Ford Mustang II 2+2,25.5,4,140,89,2755,15.8,77
Ford Mustang II,13,8,302,129,3169,12,75
Ford Mustang,18,6,250,88,3139,14.5,71
Ford Pinto (Wagon),22,4,122,86,2395,16,72
Ford Pinto Runabout,21,4,122,86,2226,16.5,72
Ford Pinto,18,6,171,97,2984,14.5,75
Ford Pinto,19,4,122,85,2310,18.5,73
Ford Pinto,23,4,140,83,2639,17,75
Ford Pinto,25,4,98,,2046,19,71
Ford Pinto,26,4,122,80,2451,16.5,74
Ford Pinto,26.5,4,140,72,2565,13.6,76
Ford Ranger,28,4,120,79,2625,18.6,82
Ford Thunderbird,16,8,351,149,4335,14.5,77
Ford Torino (Wagon),,8,351,153,4034,11,70
Ford Torino 500,19,6,250,88,3302,15.5,71
Ford Torino,17,8,302,140,3449,10.5,70
Hi 1200D,9,8,304,193,4732,18.5,70
Honda Accord CVCC,31.5,4,98,68,2045,18.5,77
Honda Accord LX,29.5,4,98,68,2135,16.6,78
Honda Accord,32.4,4,107,72,2290,17,80
Honda Accord,36,4,107,75,2205,14.5,82
Honda Civic (Auto),32,4,91,67,1965,15.7,82
Honda Civic 1300,35.1,4,81,60,1760,16.1,81
Honda Civic 1500 GL,44.6,4,91,67,1850,13.8,80
Honda Civic CVCC,33,4,91,53,1795,17.5,75
Honda Civic CVCC,36.1,4,91,60,1800,16.4,78
Honda Civic,24,4,120,97,2489,15,74
Honda Civic,33,4,91,53,1795,17.4,76
Honda Civic,38,4,91,67,1965,15,82
Honda Prelude,33.7,4,107,75,2210,14.4,81
Maxda GLC Deluxe,34.1,4,86,65,1975,15.2,79
Maxda RX-3,18,3,70,90,2124,13.5,73
Mazda 626,31.3,4,120,75,2542,17.5,80
Mazda 626,31.6,4,120,74,2635,18.3,81
Mazda GLC 4,34.1,4,91,68,1985,16,81
Mazda GLC Custom L,37,4,91,68,2025,18.2,82
Mazda GLC Custom,31,4,91,68,1970,17.6,82
Mazda GLC Deluxe,32.8,4,78,52,1985,19.4,78
Mazda GLC,46.6,4,86,65,2110,17.9,80
Mazda RX-2 Coupe,19,3,70,97,2330,13.5,72
Mazda RX-4,21.5,3,80,110,2720,13.5,77
Mazda RX-7 Gs,23.7,3,70,100,2420,12.5,80
Mercedes-Benz 240D,30,4,146,67,3250,21.8,80
Mercedes-Benz 280S,16.5,6,168,120,3820,16.7,76
Mercedes-Benz 300D,25.4,5,183,77,3530,20.1,79
Mercury Capri 2000,23,4,122,86,2220,14,71
Mercury Capri V6,21,6,155,107,2472,14,73
Mercury Cougar Brougham,15,8,302,130,4295,14.9,77
Mercury Grand Marquis,16.5,8,351,138,3955,13.2,79
Mercury Lynx L,36,4,98,70,2125,17.3,82
Mercury Marquis Brougham,12,8,429,198,4952,11.5,73
Mercury Marquis,11,8,429,208,4633,11,72
Mercury Monarch Ghia,20.2,8,302,139,3570,12.8,78
Mercury Monarch,15,6,250,72,3432,21,75
Mercury Zephyr 6,19.8,6,200,85,2990,18.2,79
Mercury Zephyr,20.8,6,200,85,3070,16.7,78
Nissan Stanza XE,36,4,120,88,2160,14.5,82
Oldsmobile Cutlass Ciera (Diesel),38,6,262,85,3015,17,82
Oldsmobile Cutlass LS,26.6,8,350,105,3725,19,81
Oldsmobile Cutlass Salon Brougham,19.9,8,260,110,3365,15.5,78
Oldsmobile Cutlass Salon Brougham,23.9,8,260,90,3420,22.2,79
Oldsmobile Cutlass Supreme,17,8,260,110,4060,19,77
Oldsmobile Delta 88 Royale,12,8,350,160,4456,13.5,72
Oldsmobile Omega Brougham,26.8,6,173,115,2700,12.9,79
Oldsmobile Omega,11,8,350,180,3664,11,73
Oldsmobile Starfire SX,23.8,4,151,85,2855,17.6,78
Oldsmobile Vista Cruiser,12,8,350,180,4499,12.5,73
Opel 1900,25,4,116,81,2220,16.9,76
Opel 1900,28,4,116,90,2123,14,71
Opel Manta,24,4,116,75,2158,15.5,73
Opel Manta,26,4,97,78,2300,14.5,74
Peugeot 304,30,4,79,70,2074,19.5,71
Peugeot 504 (Wagon),21,4,120,87,2979,19.5,72
Peugeot 504,19,4,120,88,3270,21.9,76
Peugeot 504,23,4,120,88,2957,17,75
Peugeot 504,25,4,110,87,2672,17.5,70
Peugeot 504,27.2,4,141,71,3190,24.8,79
Peugeot 505S Turbo Diesel,28.1,4,141,80,3230,20.4,81
Peugeot 604SL,16.2,6,163,133,3410,15.8,78
Plymouth Arrow GS,25.5,4,122,96,2300,15.5,77
Plymouth Barracuda 340,14,8,340,160,3609,8,70
Plymouth Champ,39,4,86,64,1875,16.4,81
Plymouth Cricket,26,4,91,70,1955,20.5,71
Plymouth Custom Suburb,13,8,360,170,4654,13,73
Plymouth Duster,20,6,198,95,3102,16.5,74
Plymouth Duster,22,6,198,95,2833,15.5,70
Plymouth Duster,23,6,198,95,2904,16,73
Plymouth Fury Gran Sedan,14,8,318,150,4237,14.5,73
Plymouth Fury III,14,8,318,150,4096,13,71
Plymouth Fury III,14,8,440,215,4312,8.5,70
Plymouth Fury III,15,8,318,150,4135,13.5,72
Plymouth Fury,18,6,225,95,3785,19,75
Plymouth Grand Fury,16,8,318,150,4498,14.5,75
Plymouth Horizon 4,34.7,4,105,63,2215,14.9,81
Plymouth Horizon Miser,38,4,105,63,2125,14.7,82
Plymouth Horizon TC3,34.5,4,105,70,2150,14.9,79
Plymouth Horizon,34.2,4,105,70,2200,13.2,79
Plymouth Reliant,27.2,4,135,84,2490,15.7,81
Plymouth Reliant,30,4,135,84,2385,12.9,81
Plymouth Sapporo,23.2,4,156,105,2745,16.7,78
Plymouth Satellite (Wagon),,8,383,175,4166,10.5,70
Plymouth Satellite Custom (Wagon),14,8,318,150,4077,14,72
Plymouth Satellite Custom,16,6,225,105,3439,15.5,71
Plymouth Satellite Sebring,18,6,225,105,3613,16.5,74
Plymouth Satellite,18,8,318,150,3436,11,70
Plymouth Valiant Custom,19,6,225,95,3264,16,75
Plymouth Valiant,18,6,225,105,3121,16.5,73
Plymouth Valiant,22,6,225,100,3233,15.4,76
Plymouth Volare Custom,19,6,225,100,3630,17.7,77
Plymouth Volare Premier V8,13,8,318,150,3940,13.2,76
Plymouth Volare,20.5,6,225,100,3430,17.2,78
Pontiac Astro,23,4,140,78,2592,18.5,75
Pontiac Catalina Brougham,14,8,400,175,4464,11.5,71
Pontiac Catalina,14,8,400,175,4385,12,72
Pontiac Catalina,14,8,455,225,4425,10,70
Pontiac Catalina,16,8,400,170,4668,11.5,75
Pontiac Firebird,19,6,250,100,3282,15,71
Pontiac Grand Prix Lj,16,8,400,180,4220,11.1,77
Pontiac Grand Prix,16,8,400,230,4278,9.5,73
Pontiac J2000 Se Hatchback,31,4,112,85,2575,16.2,82
Pontiac Lemans V6,21.5,6,231,115,3245,15.4,79
Pontiac Phoenix LJ,19.2,6,231,105,3535,19.2,78
Pontiac Phoenix,27,4,151,90,2735,18,82
Pontiac Phoenix,33.5,4,151,90,2556,13.2,79
Pontiac Safari (Wagon),13,8,400,175,5140,12,71
Pontiac Sunbird Coupe,24.5,4,151,88,2740,16,77
Pontiac Ventura Sj,18.5,6,250,110,3645,16.2,76
Renault 12 (Wagon),26,4,96,69,2189,18,72
Renault 12TL,27,4,101,83,2202,15.3,76
Renault 18I,34.5,4,100,,2320,15.8,81
Renault 5 Gtl,36,4,79,58,1825,18.6,77
Renault Lecar Deluxe,40.9,4,85,,1835,17.3,80
Saab 900S,,4,121,110,2800,15.4,81
Saab 99E,25,4,104,95,2375,17.5,70
Saab 99GLE,21.6,4,121,115,2795,15.7,78
Saab 99LE,24,4,121,110,2660,14,73
Saab 99LE,25,4,121,115,2671,13.5,75
Subaru DL,30,4,97,67,1985,16.4,77
Subaru DL,33.8,4,97,67,2145,18,80
Subaru,26,4,108,93,2391,15.5,74
Subaru,32.3,4,97,67,2065,17.8,81
Toyota Carina,20,4,97,88,2279,19,73
Toyota Celica GT Liftback,21.1,4,134,95,2515,14.8,78
Toyota Celica GT,32,4,144,96,2665,13.9,82
Toyota Corolla 1200,31,4,71,65,1773,19,71
Toyota Corolla 1200,32,4,71,65,1836,21,74
Toyota Corolla 1600 (Wagon),27,4,97,88,2100,16.5,72
Toyota Corolla Liftback,26,4,97,75,2265,18.2,77
Toyota Corolla Tercel,38.1,4,89,60,1968,18.8,80
Toyota Corolla,28,4,97,75,2155,16.4,76
Toyota Corolla,29,4,97,75,2171,16,75
Toyota Corolla,32.2,4,108,75,2265,15.2,80
Toyota Corolla,32.4,4,108,75,2350,16.8,81
Toyota Corolla,34,4,108,70,2245,16.9,82
Toyota Corona Hardtop,24,4,113,95,2278,15.5,72
Toyota Corona Liftback,29.8,4,134,90,2711,15.5,80
Toyota Corona Mark II,24,4,113,95,2372,15,70
Toyota Corona,24,4,134,96,2702,13.5,75
Toyota Corona,25,4,113,95,2228,14,71
Toyota Corona,27.5,4,134,95,2560,14.2,78
Toyota Corona,31,4,76,52,1649,16.5,74
Toyota Cressida,25.4,6,168,116,2900,12.6,81
Toyota Mark II,19,6,156,108,2930,15.5,76
Toyota Mark II,20,6,156,122,2807,13.5,73
Toyota Starlet,39.1,4,79,58,1755,16.9,81
Toyota Tercel,37.7,4,89,62,2050,17.3,81
Toyouta Corona Mark II (Wagon),23,4,120,97,2506,14.5,72
Triumph TR7 Coupe,35,4,122,88,2500,15.1,80
Vokswagen Rabbit,29.8,4,89,62,1845,15.3,80
Volkswagen 1131 Deluxe Sedan,26,4,97,46,1835,20.5,70
Volkswagen 411 (Wagon),22,4,121,76,2511,18,72
Volkswagen Dasher (Diesel),43.4,4,90,48,2335,23.7,80
Volkswagen Dasher,25,4,90,71,2223,16.5,75
Volkswagen Dasher,26,4,79,67,1963,15.5,74
Volkswagen Dasher,30.5,4,97,78,2190,14.1,77
Volkswagen Jetta,33,4,105,74,2190,14.2,81
Volkswagen Model 111,27,4,97,60,1834,19,71
Volkswagen Pickup,44,4,97,52,2130,24.6,82
Volkswagen Rabbit C (Diesel),44.3,4,90,48,2085,21.7,80
Volkswagen Rabbit Custom Diesel,43.1,4,90,48,1985,21.5,78
Volkswagen Rabbit Custom,29,4,97,78,1940,14.5,77
Volkswagen Rabbit Custom,31.9,4,89,71,1925,14,79
Volkswagen Rabbit L,36,4,105,74,1980,15.3,82
Volkswagen Rabbit,29,4,90,70,1937,14,75
Volkswagen Rabbit,29,4,90,70,1937,14.2,76
Volkswagen Rabbit,29.5,4,97,71,1825,12.2,76
Volkswagen Rabbit,41.5,4,98,76,2144,14.7,80
Volkswagen Scirocco,31.5,4,89,71,1990,14.9,78
Volkswagen Super Beetle 117,,4,97,48,1978,20,71
Volkswagen Super Beetle,26,4,97,46,1950,21,73
Volkswagen Type 3,23,4,97,54,2254,23.5,72
Volvo 144EA,19,4,121,112,2868,15.5,73
Volvo 145E (Wagon),18,4,121,112,2933,14.5,72
Volvo 244DL,22,4,121,98,2945,14.5,75
Volvo 245,20,4,130,102,3150,15.7,76
Volvo 264GL,17,6,163,125,3140,13.6,78
Volvo Diesel,30.7,6,145,76,3160,19.6,81

d3.svg.multibrush.js

d3.svg.multibrush = function() {

  // From d3/scale/scale.js
  function d3_scaleExtent(domain) {
    var start = domain[0], stop = domain[domain.length - 1];
    return start < stop ? [ start, stop ] : [ stop, start ];
  }
  function d3_scaleRange(scale) {
    return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range());
  }

  // From d3
  var d3_event_dragSelect = "onselectstart" in document ? null : d3_vendorSymbol(document.documentElement.style, "userSelect"), d3_event_dragId = 0;
  function d3_event_dragSuppress() {
    var name = ".dragsuppress-" + ++d3_event_dragId, click = "click" + name, w = d3.select(window).on("touchmove" + name, d3.event.preventDefault()).on("dragstart" + name, d3.event.preventDefault()).on("selectstart" + name, d3.event.preventDefault());
    if (d3_event_dragSelect) {
      var style = d3_documentElement.style, select = style[d3_event_dragSelect];
      style[d3_event_dragSelect] = "none";
    }
    return function(suppressClick) {
      w.on(name, null);
      if (d3_event_dragSelect) style[d3_event_dragSelect] = select;
      if (suppressClick) {
        function off() {
          w.on(click, null);
        }
        w.on(click, function() {
          d3.event.preventDefault();
          off();
        }, true);
        setTimeout(off, 0);
      }
    };
  }

  var event = d3.dispatch("brushstart", "brush", "brushend"),
      brushElement,
      x = null, // x-scale, optional
      y = null, // y-scale, optional
      xExtent = [[0, 0]], // [x0, x1] in integer pixels
      yExtent = [[0, 0]], // [y0, y1] in integer pixels
      xExtentDomain = [], // x-extent in data space
      yExtentDomain = [], // y-extent in data space
      xClamp = true, // whether to clamp the x-extent to the range
      yClamp = true, // whether to clamp the y-extent to the range
      resizes = d3_svg_brushResizes[0],
      resizeAdaption = function () {}, // Function to 'call' on new resize selection
      extentAdaption = function () {}; // Function to 'call' on new extent selection

  event.of = function(thiz, argumentz) {
    return function(e1) {
      try {
        var e0 =
        e1.sourceEvent = d3.event;
        e1.target = brush;
        d3.event = e1;
        event[e1.type].apply(thiz, argumentz);
      } finally {
        d3.event = e0;
      }
    };
  };

  function brush(g) {
    g.each(function() {

      // Prepare the brush container for events.
      var g = d3.select(this)
          .style("pointer-events", "all")
          .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)")
          .on("mousedown.brush", brushstart)
          .on("touchstart.brush", brushstart);

      brushElement = g;

      // An invisible, mouseable area for starting a new brush.
      var background = g.selectAll(".background")
          .data([0]);

      background.enter().append("rect")
          .attr("class", "background")
          .style("visibility", "hidden")
          .style("cursor", "crosshair");

      drawExtents(g);

      // When called on a transition, use a transition to update.
      var gUpdate = d3.transition(g),
          backgroundUpdate = d3.transition(background),
          range;

      // Initialize the background to fill the defined range.
      // If the range isn't defined, you can post-process.
      if (x) {
        range = d3_scaleRange(x);
        backgroundUpdate.attr("x", range[0]).attr("width", range[1] - range[0]);
        redrawX(gUpdate);
      }
      if (y) {
        range = d3_scaleRange(y);
        backgroundUpdate.attr("y", range[0]).attr("height", range[1] - range[0]);
        redrawY(gUpdate);
      }
      redraw(gUpdate);
    });
  }

  function drawExtents(g) {
    var ex = xExtent.length > yExtent.length ? xExtent : yExtent,
        i = ex.length
        extentArr = ex.map(function(d,i) { return i; }),
        extentResizes = d3.merge(ex.map(function(d,i) { return resizes.map(function(r) { return [r, i]; }); }));

    if(!g) g = brushElement;

    // The visible brush extent; style this as you like!
    var extent = g.selectAll(".extent")
        .data(extentArr, function (d) { return d; });

    extent.exit().remove();

    extent.enter().append("rect")
        .attr("class", "extent")
        .style("cursor", "move")
        .call(extentAdaption);

    // More invisible rects for resizing the extent.
    var resize = g.selectAll(".resize")
        .data(extentResizes, function (d) { return d[0] + d[1]; });

    // Remove any superfluous resizers.
    resize.exit().remove();

    var newResize = resize.enter().append("g")
        .attr("class", function(d) { return "resize " + d[0]; })
        .style("cursor", function(d) { return d3_svg_brushCursor[d[0]]; });
    
    newResize.append("rect")
        .attr("x", function(d) { return /[ew]$/.test(d[0]) ? -3 : null; })
        .attr("y", function(d) { return /^[ns]/.test(d[0]) ? -3 : null; })
        .attr("width", 6)
        .attr("height", 6)
        .style("visibility", "hidden");

    newResize.call(resizeAdaption);

    // Show or hide the resizers.
    resize.style("display", function (d) { return brush.empty(d[1]) ? "none" : null; });
  }

  brush.event = function(g) {
    g.each(function() {
      var event_ = event.of(this, arguments),
          extent1 = {x: xExtent, y: yExtent, i: xExtentDomain, j: yExtentDomain},
          extent0 = this.__chart__ || extent1;
      this.__chart__ = extent1;
      if (d3_transitionInheritId) {
        d3.select(this).transition()
            .each("start.brush", function() {
              xExtentDomain = extent0.i; // pre-transition state
              yExtentDomain = extent0.j;
              xExtent = extent0.x;
              yExtent = extent0.y;
              event_({type: "brushstart"});
            })
            .tween("brush:brush", function() {
              // TODO: transitions for all extents
              var xi = d3_interpolateArray(xExtent[0], extent1.x[0]),
                  yi = d3_interpolateArray(yExtent[0], extent1.y[0]);
              xExtentDomain[0] = yExtentDomain[0] = null; // transition state
              return function(t) {
                xExtent[0] = extent1.x[0] = xi(t);
                yExtent[0] = extent1.y[0] = yi(t);
                event_({type: "brush", mode: "resize"});
              };
            })
            .each("end.brush", function() {
              xExtentDomain = extent1.i; // post-transition state
              yExtentDomain = extent1.j;
              event_({type: "brush", mode: "resize"});
              event_({type: "brushend"});
            });
      } else {
        event_({type: "brushstart"});
        event_({type: "brush", mode: "resize"});
        event_({type: "brushend"});
      }
    });
  };

  function redraw(g) {
    g.selectAll(".resize").attr("transform", function(d) {
      return "translate(" + xExtent[d[1]][+/e$/.test(d[0])] + "," + yExtent[d[1]][+/^s/.test(d[0])] + ")";
    });
  }

  function redrawX(g) {
    g.selectAll(".extent").attr("x", function (d) { return xExtent[d][0]; });
    g.selectAll(".extent,.n>rect,.s>rect").attr("width", function(d) { return xExtent[d][1] - xExtent[d][0]; });
  }

  function redrawY(g) {
    g.selectAll(".extent").attr("y", function (d) { return yExtent[d][0]; });
    g.selectAll(".extent,.e>rect,.w>rect").attr("height", function (d) { return yExtent[d][1] - yExtent[d][0]; });
  }

  function brushstart() {
    var target = this,
        eventTarget = d3.select(d3.event.target),
        event_ = event.of(target, arguments),
        g = d3.select(target),
        resizing = eventTarget.datum()[0],
        resizingX = !/^(n|s)$/.test(resizing) && x,
        resizingY = !/^(e|w)$/.test(resizing) && y,
        dragging = eventTarget.classed("extent"),
        dragRestore = d3_event_dragSuppress(),
        center,
        origin = d3.mouse(target),
        offset,
        i;

    var w = d3.select(window)
        .on("keydown.brush", keydown)
        .on("keyup.brush", keyup);

    if (d3.event.changedTouches) {
      w.on("touchmove.brush", brushmove).on("touchend.brush", brushend);
    } else {
      w.on("mousemove.brush", brushmove).on("mouseup.brush", brushend);
    }

    // Interrupt the transition, if any.
    g.interrupt().selectAll("*").interrupt();

    // If the extent was clicked on, drag rather than brush;
    // store the point between the mouse and extent origin instead.
    if (dragging) {
      i = eventTarget.datum();
      origin[0] = xExtent[i][0] - origin[0];
      origin[1] = yExtent[i][0] - origin[1];
    }

    // If a resizer was clicked on, record which side is to be resized.
    // Also, set the origin to the opposite side.
    else if (resizing) {
      var ex = +/w$/.test(resizing),
          ey = +/^n/.test(resizing);

      i = eventTarget.datum()[1];
      offset = [xExtent[i][1 - ex] - origin[0], yExtent[i][1 - ey] - origin[1]];
      origin[0] = xExtent[i][ex];
      origin[1] = yExtent[i][ey];
    }

    else {
      i = xExtent.length - 1; // Figure out the count of the new extent.
      xExtent.push([0,0]);
      yExtent.push([0,0]);

      // If the ALT key is down when starting a brush, the center is at the mouse.
      if (d3.event.altKey) center = origin.slice();
    }

    // Propagate the active cursor to the body for the drag duration.
    g.style("pointer-events", "none");
    d3.select("body").style("cursor", eventTarget.style("cursor"));

    // Show resizers as long as we're not dragging or resizing.
    if(!dragging && !resizing) g.selectAll(".resize").style("display", null)

    // Notify listeners.
    event_({type: "brushstart"});
    brushmove();

    function keydown() {
      if (d3.event.keyCode == 32) {
        if (!dragging) {
          center = null;
          origin[0] -= xExtent[i][1];
          origin[1] -= yExtent[i][1];
          dragging = 2;
        }
        d3.event.preventDefault();
      }
    }

    function keyup() {
      if (d3.event.keyCode == 32 && dragging == 2) {
        origin[0] += xExtent[i][1];
        origin[1] += yExtent[i][1];
        dragging = 0;
        d3.event.preventDefault();
      }
    }

    function brushmove() {
      var point = d3.mouse(target),
          moved = false;

      // Preserve the offset for thick resizers.
      if (offset) {
        point[0] += offset[0];
        point[1] += offset[1];
      }

      if (!dragging) {

        // If needed, determine the center from the current extent.
        if (d3.event.altKey) {
          if (!center) center = [(xExtent[i][0] + xExtent[i][1]) / 2, (yExtent[i][0] + yExtent[i][1]) / 2];

          // Update the origin, for when the ALT key is released.
          origin[0] = xExtent[i][+(point[0] < center[0])];
          origin[1] = yExtent[i][+(point[1] < center[1])];
        }

        // When the ALT key is released, we clear the center.
        else center = null;
      }

      // Update the brush extent for each dimension.
      if (resizingX && move1(point, x, 0)) {
        redrawX(g, i);
        moved = true;
      }
      if (resizingY && move1(point, y, 1)) {
        redrawY(g, i);
        moved = true;
      }

      // Final redraw and notify listeners.
      if (moved) {
        redraw(g);
        event_({type: "brush", mode: dragging ? "move" : "resize"});
      }
    }

    function move1(point, scale, j) {
      var range = d3_scaleRange(scale),
          r0 = range[0],
          r1 = range[1],
          position = origin[j],
          extent = j ? yExtent[i] : xExtent[i],
          size = extent[1] - extent[0],
          min,
          max;

      // When dragging, reduce the range by the extent size and position.
      if (dragging) {
        r0 -= position;
        r1 -= size + position;
      }

      // Clamp the point (unless clamp set to false) so that the extent fits within the range extent.
      min = (j ? yClamp : xClamp) ? Math.max(r0, Math.min(r1, point[j])) : point[j];

      // Compute the new extent bounds.
      if (dragging) {
        max = (min += position) + size;
      } else {

        // If the ALT key is pressed, then preserve the center of the extent.
        if (center) position = Math.max(r0, Math.min(r1, 2 * center[j] - min));

        // Compute the min and max of the position and point.
        if (position < min) {
          max = min;
          min = position;
        } else {
          max = position;
        }
      }

      // Update the stored bounds.
      if (extent[0] != min || extent[1] != max) {
        if (j) yExtentDomain[i] = null;
        else xExtentDomain[i] = null;
        extent[0] = min;
        extent[1] = max;
        return true;
      }
    }

    function brushend() {
      brushmove();

      // If the current extent is empty, clear everything.
      if(x && xExtent[i][0] == xExtent[i][1] ||
         y && yExtent[i][0] == yExtent[i][1]) {
        brush.clear();
      }

      // reset the cursor styles
      g.style("pointer-events", "all").selectAll(".resize").style("display", function(d) { return brush.empty(d[1]) ? "none" : null; });
      d3.select("body").style("cursor", null);

      w .on("mousemove.brush", null)
        .on("mouseup.brush", null)
        .on("touchmove.brush", null)
        .on("touchend.brush", null)
        .on("keydown.brush", null)
        .on("keyup.brush", null);

      drawExtents();

      dragRestore();
      event_({type: "brushend"});
    }
  }

  brush.x = function(z) {
    if (!arguments.length) return x;
    x = z;
    resizes = d3_svg_brushResizes[!x << 1 | !y]; // fore!
    return brush;
  };

  brush.y = function(z) {
    if (!arguments.length) return y;
    y = z;
    resizes = d3_svg_brushResizes[!x << 1 | !y]; // fore!
    return brush;
  };

  brush.resizeAdaption = function(z) {
    if (!arguments.length) return resizeAdaption;
    resizeAdaption = z;
    return brush;
  }

  brush.extentAdaption = function(z) {
    if (!arguments.length) return extentAdaption;
    extentAdaption = z;
    return brush;
  }  

  brush.clamp = function(z) {
    if (!arguments.length) return x && y ? [xClamp, yClamp] : x ? xClamp : y ? yClamp : null;
    if (x && y) xClamp = !!z[0], yClamp = !!z[1];
    else if (x) xClamp = !!z;
    else if (y) yClamp = !!z;
    return brush;
  };

  brush.extent = function(z) {
    var x0, x1, y0, y1, t;
    var xOutput, yOutput, xyOutput = [];

    // Invert the pixel extent to data-space.
    if (!arguments.length) {
      if (x) {
        if (xExtentDomain[0]) {
          xOutput = xExtentDomain;
        } else {
          xOutput = xExtent.map(function (d) {
            if (x.invert) return [ x.invert(d[0]), x.invert(d[1]) ];
            return d;
          }).map(function (d) {
            if (d[1] < d[0]) return [ d[1], d[0] ];
            return d;
          }).filter(function (d) { return d[1] - d[0] != 0; });
        }
      }
      if (y) {
        if (yExtentDomain[0]) {
          yOutput = yExtentDomain;
        } else {
          yOutput = yExtent.map(function (d) {
            if(y.invert) return [ y.invert(d[0]), y.invert(d[1]) ];
            return d;
          }).map(function (d) {
            if (d[1] < d[0]) return [ d[1], d[0] ];
            return d;
          }).filter(function (d) { return d[1] - d[0] != 0; });
        }
      }
      if(x && y) {
        xOutput.forEach(function (d, i) {
          xyOutput.push([[d[0], yOutput[i][0]], [d[1], yOutput[i][1]]]);
        });
      }
      return x && y ? xyOutput : x ? xOutput : y && yOutput;
    }

    // Scale the data-space extent to pixels.
    if (x) {
      xOutput = z;
      if (y) xOutput = xOutput.map(function (d) {
        return [d[0][0], d[1][0]];
      });
      xExtentDomain = xOutput;
      xOutput = xOutput.map(function (d) {
        if (x.invert) return [x(d[0]), x(d[1])];
        return d;
      }).map(function (d) {
        if(d[1] < d[0]) return [d[1], d[0]];
        return d;
      });
      xExtent = xOutput;
      if(!y) yExtent = xOutput.map(function() { return [0,0]; });
    }
    if (y) {
      yOutput = z;
      if (x) yOutput = yOutput.map(function (d) {
        return [d[0][1], d[1][1]];
      });
      yExtentDomain = yOutput;
      yOutput = yOutput.map(function (d) {
        if (y.invert) return [y(d[0]), y(d[1])];
        return d;
      }).map(function (d) {
        if(d[1] < d[0]) return [d[1], d[0]];
        return d;
      });
      yExtent = yOutput;
      if(!x) xExtent = yOutput.map(function () { return [0,0]; });
    }

    // Handle the case where the extents are set to empty arrays.
    if(xExtent.length === 0) xExtent = [[0,0]];
    if(yExtent.length === 0) yExtent = [[0,0]];

    return brush;
  };

  brush.clear = function() {
    xExtent = [[0, 0]], yExtent = [[0, 0]];
    xExtentDomain = yExtentDomain = [];
    drawExtents();
    if(x) redrawX(brushElement);
    if(y) redrawY(brushElement);
    return brush;
  };

  brush.empty = function(i) {
    if(xExtent.length == 1 && yExtent.length == 1) i = 0;
    if(i !== undefined) {
      return !!x && xExtent[i][0] == xExtent[i][1]
        || !!y && yExtent[i][0] == yExtent[i][1];
    } else {
      return false;
    }
  };

  return d3.rebind(brush, event, "on");
};

var d3_svg_brushCursor = {
  n: "ns-resize",
  e: "ew-resize",
  s: "ns-resize",
  w: "ew-resize",
  nw: "nwse-resize",
  ne: "nesw-resize",
  se: "nwse-resize",
  sw: "nesw-resize"
};

var d3_svg_brushResizes = [
  ["n", "e", "s", "w", "nw", "ne", "se", "sw"],
  ["e", "w"],
  ["n", "s"],
  []
];