1 /**
  2  * Returns a new select behavior to be registered on mousedown events.
  3  *
  4  * @class Implements interactive selecting starting with mousedown events.
  5  * Register this behavior on panels that should be selectable by the user, such
  6  * for brushing and linking. This behavior can be used in tandom with
  7  * {@link pv.Behavior.drag} to allow the selected region to be dragged
  8  * interactively.
  9  *
 10  * <p>After the initial mousedown event is triggered, this behavior listens for
 11  * mousemove and mouseup events on the window. This allows selecting to continue
 12  * even if the mouse temporarily leaves the assigned panel, or even the root
 13  * panel.
 14  *
 15  * <p>This behavior requires that the data associated with the mark being
 16  * dragged have <tt>x</tt>, <tt>y</tt>, <tt>dx</tt> and <tt>dy</tt> attributes
 17  * that correspond to the mark's location and dimensions in pixels. The mark's
 18  * positional properties are not set directly by this behavior; instead, the
 19  * positional properties should be defined as:
 20  *
 21  * <pre>    .left(function(d) d.x)
 22  *     .top(function(d) d.y)
 23  *     .width(function(d) d.dx)
 24  *     .height(function(d) d.dy)</pre>
 25  *
 26  * Thus, the behavior does not resize the mark directly, but instead updates the
 27  * selection by updating the assigned panel's underlying data. Note that if the
 28  * positional properties are defined with bottom and right (rather than top and
 29  * left), the drag behavior will be inverted, which will confuse users!
 30  *
 31  * <p>The select behavior is bounded by the assigned panel; the positional
 32  * attributes are clamped such that the selection does not extend outside the
 33  * panel's bounds.
 34  *
 35  * <p>The panel being selected is automatically re-rendered for each mouse event
 36  * as part of the drag operation. This behavior may be enhanced in the future to
 37  * allow more flexible configuration of select behavior. In some cases, such as
 38  * with parallel coordinates, making a selection may cause related marks to
 39  * change, in which case additional marks may also need to be rendered. This can
 40  * be accomplished by listening for the select psuedo-events:<ul>
 41  *
 42  * <li>selectstart (on mousedown)
 43  * <li>select (on mousemove)
 44  * <li>selectend (on mouseup)
 45  *
 46  * </ul>For example, to render the parent panel while selecting, thus
 47  * re-rendering all sibling marks:
 48  *
 49  * <pre>    .event("mousedown", pv.Behavior.drag())
 50  *     .event("select", function() this.parent)</pre>
 51  *
 52  * This behavior may be enhanced in the future to allow more flexible
 53  * configuration of the selection behavior.
 54  *
 55  * @extends pv.Behavior
 56  * @see pv.Behavior.drag
 57  */
 58 pv.Behavior.select = function() {
 59   var scene, // scene context
 60       index, // scene context
 61       r, // region being selected
 62       m1; // initial mouse position
 63 
 64   /** @private */
 65   function mousedown(d) {
 66     index = this.index;
 67     scene = this.scene;
 68     m1 = this.mouse();
 69     r = d;
 70     r.x = m1.x;
 71     r.y = m1.y;
 72     r.dx = r.dy = 0;
 73     pv.Mark.dispatch("selectstart", scene, index);
 74   }
 75 
 76   /** @private */
 77   function mousemove() {
 78     if (!scene) return;
 79     scene.mark.context(scene, index, function() {
 80         var m2 = this.mouse();
 81         r.x = Math.max(0, Math.min(m1.x, m2.x));
 82         r.y = Math.max(0, Math.min(m1.y, m2.y));
 83         r.dx = Math.min(this.width(), Math.max(m2.x, m1.x)) - r.x;
 84         r.dy = Math.min(this.height(), Math.max(m2.y, m1.y)) - r.y;
 85         this.render();
 86       });
 87     pv.Mark.dispatch("select", scene, index);
 88   }
 89 
 90   /** @private */
 91   function mouseup() {
 92     if (!scene) return;
 93     pv.Mark.dispatch("selectend", scene, index);
 94     scene = null;
 95   }
 96 
 97   pv.listen(window, "mousemove", mousemove);
 98   pv.listen(window, "mouseup", mouseup);
 99   return mousedown;
100 };
101