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