import “../core/document”; import “../core/rebind”; import “../event/dispatch”; import “../event/drag”; import “../event/event”; import “../event/mouse”; import “../event/touches”; import “../scale/scale”; import “../selection/selection”; import “svg”;

d3.svg.brush = function() {

var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"),
    x = null, // x-scale, optional
    y = null, // y-scale, optional
    resizes = d3_svg_brushResizes[0],
    extent = [[0, 0], [0, 0]], // [x0, y0], [x1, y1], in pixels (integers)
    clamp = [true, true], // whether or not to clamp the extent to the range
    extentDomain; // the extent in data space, lazily created

function brush(g) {
  g.each(function() {
    var g = d3.select(this),
        bg = g.selectAll(".background").data([0]),
        fg = g.selectAll(".extent").data([0]),
        tz = g.selectAll(".resize").data(resizes, String),
        e;

    // Prepare the brush container for events.
    g
        .style("pointer-events", "all")
        .on("mousedown.brush", brushstart)
        .on("touchstart.brush", brushstart);

    // An invisible, mouseable area for starting a new brush.
    bg.enter().append("rect")
        .attr("class", "background")
        .style("visibility", "hidden")
        .style("cursor", "crosshair");

    // The visible brush extent; style this as you like!
    fg.enter().append("rect")
        .attr("class", "extent")
        .style("cursor", "move");

    // More invisible rects for resizing the extent.
    tz.enter().append("g")
        .attr("class", function(d) { return "resize " + d; })
        .style("cursor", function(d) { return d3_svg_brushCursor[d]; })
      .append("rect")
        .attr("x", function(d) { return /[ew]$/.test(d) ? -3 : null; })
        .attr("y", function(d) { return /^[ns]/.test(d) ? -3 : null; })
        .attr("width", 6)
        .attr("height", 6)
        .style("visibility", "hidden");

    // Show or hide the resizers.
    tz.style("display", brush.empty() ? "none" : null);

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

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

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

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

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

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

  var w = d3.select(d3_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);
  }

  // If the extent was clicked on, drag rather than brush;
  // store the point between the mouse and extent origin instead.
  if (dragging) {
    origin[0] = extent[0][0] - origin[0];
    origin[1] = extent[0][1] - 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);
    offset = [extent[1 - ex][0] - origin[0], extent[1 - ey][1] - origin[1]];
    origin[0] = extent[ex][0];
    origin[1] = extent[ey][1];
  }

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

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

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

  function mouse() {
    var touches = d3.event.changedTouches;
    return touches ? d3.touches(target, touches)[0] : d3.mouse(target);
  }

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

  function keyup() {
    if (d3.event.keyCode == 32 && dragging == 2) {
      origin[0] += extent[1][0];
      origin[1] += extent[1][1];
      dragging = 0;
      d3_eventPreventDefault();
    }
  }

  function brushmove() {
    var point = mouse(),
        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 = [(extent[0][0] + extent[1][0]) / 2, (extent[0][1] + extent[1][1]) / 2];

        // Update the origin, for when the ALT key is released.
        origin[0] = extent[+(point[0] < center[0])][0];
        origin[1] = extent[+(point[1] < center[1])][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);
      moved = true;
    }
    if (resizingY && move1(point, y, 1)) {
      redrawY(g);
      moved = true;
    }

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

  function move1(point, scale, i) {
    var range = d3_scaleRange(scale),
        r0 = range[0],
        r1 = range[1],
        position = origin[i],
        size = extent[1][i] - extent[0][i],
        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 = clamp[i] ? Math.max(r0, Math.min(r1, point[i])) : point[i];

    // 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[i] - 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][i] !== min || extent[1][i] !== max) {
      extentDomain = null;
      extent[0][i] = min;
      extent[1][i] = max;
      return true;
    }
  }

  function brushend() {
    brushmove();

    // reset the cursor styles
    g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "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);

    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.clamp = function(z) {
  if (!arguments.length) return x && y ? clamp : x || y ? clamp[+!x] : null;
  if (x && y) clamp = [!!z[0], !!z[1]];
  else if (x || y) clamp[+!x] = !!z;
  return brush;
};

brush.extent = function(z) {
  var x0, x1, y0, y1, t;

  // Invert the pixel extent to data-space.
  if (!arguments.length) {
    z = extentDomain || extent;
    if (x) {
      x0 = z[0][0], x1 = z[1][0];
      if (!extentDomain) {
        x0 = extent[0][0], x1 = extent[1][0];
        if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1);
        if (x1 < x0) t = x0, x0 = x1, x1 = t;
      }
    }
    if (y) {
      y0 = z[0][1], y1 = z[1][1];
      if (!extentDomain) {
        y0 = extent[0][1], y1 = extent[1][1];
        if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1);
        if (y1 < y0) t = y0, y0 = y1, y1 = t;
      }
    }
    return x && y ? [[x0, y0], [x1, y1]] : x ? [x0, x1] : y && [y0, y1];
  }

  // Scale the data-space extent to pixels.
  extentDomain = [[0, 0], [0, 0]];
  if (x) {
    x0 = z[0], x1 = z[1];
    if (y) x0 = x0[0], x1 = x1[0];
    extentDomain[0][0] = x0, extentDomain[1][0] = x1;
    if (x.invert) x0 = x(x0), x1 = x(x1);
    if (x1 < x0) t = x0, x0 = x1, x1 = t;
    extent[0][0] = x0 | 0, extent[1][0] = x1 | 0;
  }
  if (y) {
    y0 = z[0], y1 = z[1];
    if (x) y0 = y0[1], y1 = y1[1];
    extentDomain[0][1] = y0, extentDomain[1][1] = y1;
    if (y.invert) y0 = y(y0), y1 = y(y1);
    if (y1 < y0) t = y0, y0 = y1, y1 = t;
    extent[0][1] = y0 | 0, extent[1][1] = y1 | 0;
  }

  return brush;
};

brush.clear = function() {
  extentDomain = null;
  extent[0][0] =
  extent[0][1] =
  extent[1][0] =
  extent[1][1] = 0;
  return brush;
};

brush.empty = function() {
  return (x && extent[0][0] === extent[1][0])
      || (y && extent[0][1] === extent[1][1]);
};

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"],
[]

];