1 /** 2 * Returns a new resize behavior to be registered on mousedown events. 3 * 4 * @class Implements interactive resizing of a selection starting with mousedown 5 * events. Register this behavior on selection handles that should be resizeable 6 * by the user, such for brushing and linking. This behavior can be used in 7 * tandom with {@link pv.Behavior.select} and {@link pv.Behavior.drag} to allow 8 * the selected region to be selected and dragged 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 resizing 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 * resized 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 * size 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 resize behavior will be inverted, which will confuse users! 30 * 31 * <p>The resize behavior is bounded by the assigned mark's enclosing panel; the 32 * positional attributes are clamped such that the selection does not extend 33 * outside the panel's bounds. 34 * 35 * <p>The mark being resized is automatically re-rendered for each mouse event 36 * as part of the resize operation. This behavior may be enhanced in the future 37 * to allow more flexible configuration. In some cases, such as with parallel 38 * coordinates, resizing the selection may cause related marks to change, in 39 * which case additional marks may also need to be rendered. This can be 40 * accomplished by listening for the select psuedo-events:<ul> 41 * 42 * <li>resizestart (on mousedown) 43 * <li>resize (on mousemove) 44 * <li>resizeend (on mouseup) 45 * 46 * </ul>For example, to render the parent panel while resizing, thus 47 * re-rendering all sibling marks: 48 * 49 * <pre> .event("mousedown", pv.Behavior.resize("left")) 50 * .event("resize", 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.select 57 * @see pv.Behavior.drag 58 */ 59 pv.Behavior.resize = function(side) { 60 var scene, // scene context 61 index, // scene context 62 r, // region being selected 63 m1; // initial mouse position 64 65 /** @private */ 66 function mousedown(d) { 67 index = this.index; 68 scene = this.scene; 69 m1 = this.mouse(); 70 r = d; 71 switch (side) { 72 case "left": m1.x = r.x + r.dx; break; 73 case "right": m1.x = r.x; break; 74 case "top": m1.y = r.y + r.dy; break; 75 case "bottom": m1.y = r.y; break; 76 } 77 pv.Mark.dispatch("resizestart", scene, index); 78 } 79 80 /** @private */ 81 function mousemove() { 82 if (!scene) return; 83 scene.mark.context(scene, index, function() { 84 var m2 = this.mouse(); 85 r.x = Math.max(0, Math.min(m1.x, m2.x)); 86 r.y = Math.max(0, Math.min(m1.y, m2.y)); 87 r.dx = Math.min(this.parent.width(), Math.max(m2.x, m1.x)) - r.x; 88 r.dy = Math.min(this.parent.height(), Math.max(m2.y, m1.y)) - r.y; 89 this.render(); 90 }); 91 pv.Mark.dispatch("resize", scene, index); 92 } 93 94 /** @private */ 95 function mouseup() { 96 if (!scene) return; 97 pv.Mark.dispatch("resizeend", scene, index); 98 scene = null; 99 } 100 101 pv.listen(window, "mousemove", mousemove); 102 pv.listen(window, "mouseup", mouseup); 103 return mousedown; 104 }; 105