~bcsaller/juju-gui/update-reductions

« back to all changes in this revision

Viewing changes to lib/d3/src/svg/brush.js

  • Committer: kapil.foss at gmail
  • Date: 2012-07-13 18:45:59 UTC
  • Revision ID: kapil.foss@gmail.com-20120713184559-2xl7be17egsrz0c9
reshape

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
d3.svg.brush = function() {
2
 
  var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"),
3
 
      x = null, // x-scale, optional
4
 
      y = null, // y-scale, optional
5
 
      resizes = d3_svg_brushResizes[0],
6
 
      extent = [[0, 0], [0, 0]], // [x0, y0], [x1, y1], in pixels (integers)
7
 
      extentDomain; // the extent in data space, lazily created
8
 
 
9
 
  function brush(g) {
10
 
    g.each(function() {
11
 
      var g = d3.select(this),
12
 
          bg = g.selectAll(".background").data([0]),
13
 
          fg = g.selectAll(".extent").data([0]),
14
 
          tz = g.selectAll(".resize").data(resizes, String),
15
 
          e;
16
 
 
17
 
      // Prepare the brush container for events.
18
 
      g
19
 
          .style("pointer-events", "all")
20
 
          .on("mousedown.brush", brushstart)
21
 
          .on("touchstart.brush", brushstart);
22
 
 
23
 
      // An invisible, mouseable area for starting a new brush.
24
 
      bg.enter().append("rect")
25
 
          .attr("class", "background")
26
 
          .style("visibility", "hidden")
27
 
          .style("cursor", "crosshair");
28
 
 
29
 
      // The visible brush extent; style this as you like!
30
 
      fg.enter().append("rect")
31
 
          .attr("class", "extent")
32
 
          .style("cursor", "move");
33
 
 
34
 
      // More invisible rects for resizing the extent.
35
 
      tz.enter().append("g")
36
 
          .attr("class", function(d) { return "resize " + d; })
37
 
          .style("cursor", function(d) { return d3_svg_brushCursor[d]; })
38
 
        .append("rect")
39
 
          .attr("x", function(d) { return /[ew]$/.test(d) ? -3 : null; })
40
 
          .attr("y", function(d) { return /^[ns]/.test(d) ? -3 : null; })
41
 
          .attr("width", 6)
42
 
          .attr("height", 6)
43
 
          .style("visibility", "hidden");
44
 
 
45
 
      // Show or hide the resizers.
46
 
      tz.style("display", brush.empty() ? "none" : null);
47
 
 
48
 
      // Remove any superfluous resizers.
49
 
      tz.exit().remove();
50
 
 
51
 
      // Initialize the background to fill the defined range.
52
 
      // If the range isn't defined, you can post-process.
53
 
      if (x) {
54
 
        e = d3_scaleRange(x);
55
 
        bg.attr("x", e[0]).attr("width", e[1] - e[0]);
56
 
        redrawX(g);
57
 
      }
58
 
      if (y) {
59
 
        e = d3_scaleRange(y);
60
 
        bg.attr("y", e[0]).attr("height", e[1] - e[0]);
61
 
        redrawY(g);
62
 
      }
63
 
      redraw(g);
64
 
    });
65
 
  }
66
 
 
67
 
  function redraw(g) {
68
 
    g.selectAll(".resize").attr("transform", function(d) {
69
 
      return "translate(" + extent[+/e$/.test(d)][0] + "," + extent[+/^s/.test(d)][1] + ")";
70
 
    });
71
 
  }
72
 
 
73
 
  function redrawX(g) {
74
 
    g.select(".extent").attr("x", extent[0][0]);
75
 
    g.selectAll(".extent,.n>rect,.s>rect").attr("width", extent[1][0] - extent[0][0]);
76
 
  }
77
 
 
78
 
  function redrawY(g) {
79
 
    g.select(".extent").attr("y", extent[0][1]);
80
 
    g.selectAll(".extent,.e>rect,.w>rect").attr("height", extent[1][1] - extent[0][1]);
81
 
  }
82
 
 
83
 
  function brushstart() {
84
 
    var target = this,
85
 
        eventTarget = d3.select(d3.event.target),
86
 
        event_ = event.of(target, arguments),
87
 
        g = d3.select(target),
88
 
        resizing = eventTarget.datum(),
89
 
        resizingX = !/^(n|s)$/.test(resizing) && x,
90
 
        resizingY = !/^(e|w)$/.test(resizing) && y,
91
 
        dragging = eventTarget.classed("extent"),
92
 
        center,
93
 
        origin = mouse(),
94
 
        offset;
95
 
 
96
 
    var w = d3.select(window)
97
 
        .on("mousemove.brush", brushmove)
98
 
        .on("mouseup.brush", brushend)
99
 
        .on("touchmove.brush", brushmove)
100
 
        .on("touchend.brush", brushend)
101
 
        .on("keydown.brush", keydown)
102
 
        .on("keyup.brush", keyup);
103
 
 
104
 
    // If the extent was clicked on, drag rather than brush;
105
 
    // store the point between the mouse and extent origin instead.
106
 
    if (dragging) {
107
 
      origin[0] = extent[0][0] - origin[0];
108
 
      origin[1] = extent[0][1] - origin[1];
109
 
    }
110
 
 
111
 
    // If a resizer was clicked on, record which side is to be resized.
112
 
    // Also, set the origin to the opposite side.
113
 
    else if (resizing) {
114
 
      var ex = +/w$/.test(resizing),
115
 
          ey = +/^n/.test(resizing);
116
 
      offset = [extent[1 - ex][0] - origin[0], extent[1 - ey][1] - origin[1]];
117
 
      origin[0] = extent[ex][0];
118
 
      origin[1] = extent[ey][1];
119
 
    }
120
 
 
121
 
    // If the ALT key is down when starting a brush, the center is at the mouse.
122
 
    else if (d3.event.altKey) center = origin.slice();
123
 
 
124
 
    // Propagate the active cursor to the body for the drag duration.
125
 
    g.style("pointer-events", "none").selectAll(".resize").style("display", null);
126
 
    d3.select("body").style("cursor", eventTarget.style("cursor"));
127
 
 
128
 
    // Notify listeners.
129
 
    event_({type: "brushstart"});
130
 
    brushmove();
131
 
    d3_eventCancel();
132
 
 
133
 
    function mouse() {
134
 
      var touches = d3.event.changedTouches;
135
 
      return touches ? d3.touches(target, touches)[0] : d3.mouse(target);
136
 
    }
137
 
 
138
 
    function keydown() {
139
 
      if (d3.event.keyCode == 32) {
140
 
        if (!dragging) {
141
 
          center = null;
142
 
          origin[0] -= extent[1][0];
143
 
          origin[1] -= extent[1][1];
144
 
          dragging = 2;
145
 
        }
146
 
        d3_eventCancel();
147
 
      }
148
 
    }
149
 
 
150
 
    function keyup() {
151
 
      if (d3.event.keyCode == 32 && dragging == 2) {
152
 
        origin[0] += extent[1][0];
153
 
        origin[1] += extent[1][1];
154
 
        dragging = 0;
155
 
        d3_eventCancel();
156
 
      }
157
 
    }
158
 
 
159
 
    function brushmove() {
160
 
      var point = mouse(),
161
 
          moved = false;
162
 
 
163
 
      // Preserve the offset for thick resizers.
164
 
      if (offset) {
165
 
        point[0] += offset[0];
166
 
        point[1] += offset[1];
167
 
      }
168
 
 
169
 
      if (!dragging) {
170
 
 
171
 
        // If needed, determine the center from the current extent.
172
 
        if (d3.event.altKey) {
173
 
          if (!center) center = [(extent[0][0] + extent[1][0]) / 2, (extent[0][1] + extent[1][1]) / 2];
174
 
 
175
 
          // Update the origin, for when the ALT key is released.
176
 
          origin[0] = extent[+(point[0] < center[0])][0];
177
 
          origin[1] = extent[+(point[1] < center[1])][1];
178
 
        }
179
 
 
180
 
        // When the ALT key is released, we clear the center.
181
 
        else center = null;
182
 
      }
183
 
 
184
 
      // Update the brush extent for each dimension.
185
 
      if (resizingX && move1(point, x, 0)) {
186
 
        redrawX(g);
187
 
        moved = true;
188
 
      }
189
 
      if (resizingY && move1(point, y, 1)) {
190
 
        redrawY(g);
191
 
        moved = true;
192
 
      }
193
 
 
194
 
      // Final redraw and notify listeners.
195
 
      if (moved) {
196
 
        redraw(g);
197
 
        event_({type: "brush", mode: dragging ? "move" : "resize"});
198
 
      }
199
 
    }
200
 
 
201
 
    function move1(point, scale, i) {
202
 
      var range = d3_scaleRange(scale),
203
 
          r0 = range[0],
204
 
          r1 = range[1],
205
 
          position = origin[i],
206
 
          size = extent[1][i] - extent[0][i],
207
 
          min,
208
 
          max;
209
 
 
210
 
      // When dragging, reduce the range by the extent size and position.
211
 
      if (dragging) {
212
 
        r0 -= position;
213
 
        r1 -= size + position;
214
 
      }
215
 
 
216
 
      // Clamp the point so that the extent fits within the range extent.
217
 
      min = Math.max(r0, Math.min(r1, point[i]));
218
 
 
219
 
      // Compute the new extent bounds.
220
 
      if (dragging) {
221
 
        max = (min += position) + size;
222
 
      } else {
223
 
 
224
 
        // If the ALT key is pressed, then preserve the center of the extent.
225
 
        if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min));
226
 
 
227
 
        // Compute the min and max of the position and point.
228
 
        if (position < min) {
229
 
          max = min;
230
 
          min = position;
231
 
        } else {
232
 
          max = position;
233
 
        }
234
 
      }
235
 
 
236
 
      // Update the stored bounds.
237
 
      if (extent[0][i] !== min || extent[1][i] !== max) {
238
 
        extentDomain = null;
239
 
        extent[0][i] = min;
240
 
        extent[1][i] = max;
241
 
        return true;
242
 
      }
243
 
    }
244
 
 
245
 
    function brushend() {
246
 
      brushmove();
247
 
 
248
 
      // reset the cursor styles
249
 
      g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null);
250
 
      d3.select("body").style("cursor", null);
251
 
 
252
 
      w .on("mousemove.brush", null)
253
 
        .on("mouseup.brush", null)
254
 
        .on("touchmove.brush", null)
255
 
        .on("touchend.brush", null)
256
 
        .on("keydown.brush", null)
257
 
        .on("keyup.brush", null);
258
 
 
259
 
      event_({type: "brushend"});
260
 
      d3_eventCancel();
261
 
    }
262
 
  }
263
 
 
264
 
  brush.x = function(z) {
265
 
    if (!arguments.length) return x;
266
 
    x = z;
267
 
    resizes = d3_svg_brushResizes[!x << 1 | !y]; // fore!
268
 
    return brush;
269
 
  };
270
 
 
271
 
  brush.y = function(z) {
272
 
    if (!arguments.length) return y;
273
 
    y = z;
274
 
    resizes = d3_svg_brushResizes[!x << 1 | !y]; // fore!
275
 
    return brush;
276
 
  };
277
 
 
278
 
  brush.extent = function(z) {
279
 
    var x0, x1, y0, y1, t;
280
 
 
281
 
    // Invert the pixel extent to data-space.
282
 
    if (!arguments.length) {
283
 
      z = extentDomain || extent;
284
 
      if (x) {
285
 
        x0 = z[0][0], x1 = z[1][0];
286
 
        if (!extentDomain) {
287
 
          x0 = extent[0][0], x1 = extent[1][0];
288
 
          if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1);
289
 
          if (x1 < x0) t = x0, x0 = x1, x1 = t;
290
 
        }
291
 
      }
292
 
      if (y) {
293
 
        y0 = z[0][1], y1 = z[1][1];
294
 
        if (!extentDomain) {
295
 
          y0 = extent[0][1], y1 = extent[1][1];
296
 
          if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1);
297
 
          if (y1 < y0) t = y0, y0 = y1, y1 = t;
298
 
        }
299
 
      }
300
 
      return x && y ? [[x0, y0], [x1, y1]] : x ? [x0, x1] : y && [y0, y1];
301
 
    }
302
 
 
303
 
    // Scale the data-space extent to pixels.
304
 
    extentDomain = [[0, 0], [0, 0]];
305
 
    if (x) {
306
 
      x0 = z[0], x1 = z[1];
307
 
      if (y) x0 = x0[0], x1 = x1[0];
308
 
      extentDomain[0][0] = x0, extentDomain[1][0] = x1;
309
 
      if (x.invert) x0 = x(x0), x1 = x(x1);
310
 
      if (x1 < x0) t = x0, x0 = x1, x1 = t;
311
 
      extent[0][0] = x0 | 0, extent[1][0] = x1 | 0;
312
 
    }
313
 
    if (y) {
314
 
      y0 = z[0], y1 = z[1];
315
 
      if (x) y0 = y0[1], y1 = y1[1];
316
 
      extentDomain[0][1] = y0, extentDomain[1][1] = y1;
317
 
      if (y.invert) y0 = y(y0), y1 = y(y1);
318
 
      if (y1 < y0) t = y0, y0 = y1, y1 = t;
319
 
      extent[0][1] = y0 | 0, extent[1][1] = y1 | 0;
320
 
    }
321
 
 
322
 
    return brush;
323
 
  };
324
 
 
325
 
  brush.clear = function() {
326
 
    extentDomain = null;
327
 
    extent[0][0] =
328
 
    extent[0][1] =
329
 
    extent[1][0] =
330
 
    extent[1][1] = 0;
331
 
    return brush;
332
 
  };
333
 
 
334
 
  brush.empty = function() {
335
 
    return (x && extent[0][0] === extent[1][0])
336
 
        || (y && extent[0][1] === extent[1][1]);
337
 
  };
338
 
 
339
 
  return d3.rebind(brush, event, "on");
340
 
};
341
 
 
342
 
var d3_svg_brushCursor = {
343
 
  n: "ns-resize",
344
 
  e: "ew-resize",
345
 
  s: "ns-resize",
346
 
  w: "ew-resize",
347
 
  nw: "nwse-resize",
348
 
  ne: "nesw-resize",
349
 
  se: "nwse-resize",
350
 
  sw: "nesw-resize"
351
 
};
352
 
 
353
 
var d3_svg_brushResizes = [
354
 
  ["n", "e", "s", "w", "nw", "ne", "se", "sw"],
355
 
  ["e", "w"],
356
 
  ["n", "s"],
357
 
  []
358
 
];