title: raphael: how to start "drag" event on object via code instead of via "mouse down"?
body: <p>I need the ability to start the raphael "drag" event on a circle object that is created when left mouse button is clicked.</p>
  
  <p><strong>Details:</strong><br>
  I'm working on a piece of code that manipulates a closed path. I need these features:<br>
  1. moving existing points around by mouse drag<br>
  2. remove points via right-click<br>
  3. add points by left-click on the path, resulting in the path splitting at that location<br>
  4. if point is created by left-click, allow user to drag it to another location before releasing the mouse and dropping it</p>
  
  <p>Here's my function that creates a new circle on a given raphael "path" object:</p>
  
  <pre><code>// create a new circle wrapped around the given point reference
  function make_circle(point, path) {
      var c = paper.circle(point[1], point[2], 6)
                   .attr({fill: "#DDD", stroke: "black"});
  
      // record the point and path reference in the circle to allow updates
      c.data("point", point).data("path", path);
  
      // set event handlers
      c.drag(dragmove,dragstart);
      c.update = update_coordinates_circle;
      c.mousemove(handle_mousemove_circle);
      c.mouseup(handle_mouseup_circle);
      c.mousedown(handle_mousedown_circle);
  
      return c;
  }
  </code></pre>
  
  <p>Then I can do this:  </p>
  
  <pre><code>var pt1 = ["M", 0, 0],
      pt2 = ["L", 10, 0],
      pt3 = ["L", 10, 10],
      pt4 = ["L", 0, 10],
      point_set = [pt1, pt2, pt3, pt4, ["Z"]],
      path = paper.path(point_set),
      c1 = make_circle(pt1, path),
      c2 = make_circle(pt2, path),
      c3 = make_circle(pt3, path),
      c4 = make_circle(pt4, path);  
  </code></pre>
  
  <p>When creating new point via clicking on path, I do this:  </p>
  
  <pre><code>make_circle(new_point, this).data("just_created", true);  
  </code></pre>
  
  <p>... and the mousemove handler of this circle checks:</p>
  
  <pre><code>if (this.data("just_created")) { ... // follow mouse  
  </code></pre>
  
  <p><strong>My full code here</strong>: <a href="http://jsfiddle.net/T7XS3/" rel="nofollow">http://jsfiddle.net/T7XS3/</a> </p>
  
  <p>The problem is that because the circle radius is small, moving the mouse to quickly before releasing a new point breaks the mousemove handler since it's attached to the circle.  </p>
  
  <p>When moving an existing circle via its "drag" event, everything works fine. No matter how quickly the mouse moves the circle stays with it.  </p>
  
  <p>So again, is it possible to start a raphael "drag" event on an object that is created when the left mouse button is pressed, but has not yet been released?  </p>
  
  <hr>
  
  <p><strong>Solution</strong> (<a href="http://jsfiddle.net/A27NZ/3/" rel="nofollow">http://jsfiddle.net/A27NZ/3/</a>):  </p>
  
  <pre><code>var width = 300,
      height = 300,
      paper_offset = 50,
      maxX = width,
      maxY = height,
      paper = Raphael(paper_offset, paper_offset, 300, 300),
      dragging;
  
  make_path([100, 100], [200, 100], [200, 200], [100, 200], "green");
  
  // reset 'dragging' to avoid initial drag
  dragging = null;
  
  
  // some math to determine if a point is between two other points, within some threshold
  // based on: http://stackoverflow.com/questions/328107/how-can-you-determine-a-point-is-between-two-other-points-on-a-line-segment
  function isBetween(a, b, c) {
      var x1 = a[1],
          x2 = b[1],
          x3 = c[1],
          y1 = a[2],
          y2 = b[2],
          y3 = c[2],
          THRESHOLD = 1000;
  
      var dotproduct = (x3 - x1) * (x2 - x1) + (y3 - y1) * (y2 - y1);
      if (dotproduct &lt; 0) return false; // early return if possible
  
      var squaredlengthba = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
      if (dotproduct &gt; squaredlengthba) return false; // early return if possible
  
      var crossproduct = (y3 - y1) * (x2 - x1) - (x3 - x1) * (y2 - y1);
      if (Math.abs(crossproduct) &lt;= THRESHOLD) return true;
      else return false;
  }
  
  
  function global_mousemove(e) {
      if (dragging) {
          handle_mousemove_circle.call(dragging, e);
      }
  }
  
  
  function global_mouseup(e) {
      dragging = null;
  }
  
  
  if (document.addEventListener) {
      document.addEventListener("mousemove", global_mousemove, false);
      document.addEventListener("mouseup", global_mouseup, false);
  } else {
      document.attachEvent('onmousemove', global_mousemove);
      document.attachEvent('onmouseup', global_mouseup);
  }
  
  
  // move circle to given coordinates
  function update_circle_xy(new_x, new_y) {
      var point = this.data("point"),
          path = this.data("path");
  
      // don't follow the mouse outside the canvas, we don't want to lose the point
      if (new_x &lt;= 0) {
          new_x = 0;
      } else if (new_x &gt;= maxX) {
          new_x = maxX;
      }
  
      if (new_y &lt; 0) {
          new_y = 0;
      } else if (new_y &gt; maxY) {
          new_y = maxY;
      }
  
      // update circle coords
      this.attr({
          cx: new_x,
          cy: new_y
      });
  
      // update the referenced point
      point[1] = new_x;
      point[2] = new_y;
  
      // redraw the path
      path.attr({
          path: path.data("point_set")
      });
  }
  
  
  // move circle based on mouse event
  function handle_mousemove_circle(e) {
      var new_x = e.clientX - paper_offset,
          new_y = e.clientY - paper_offset;
  
      update_circle_xy.call(this, new_x, new_y);
  }
  
  
  // handle mouse down on circle
  // e.which 1 = left click
  // e.which 3 = right click
  function handle_mousedown_circle(e) {
      // remove the target point on right-click
      if (e.which === 3) {
          var path = this.data("path"),
              point_set = path.data("point_set"),
              point = this.data("point"),
              index = point_set.indexOf(point);
  
          // don't do anything if we only have 2 points left
          // (checking if &lt; 4 because last element is not a point ("Z"))
          if (point_set.length &lt; 4) return false;
  
          // remove the target point
          point_set.splice(index, 1);
  
          // if removed point was head of set, make the following point the new head
          if (index === 0) point_set[0][0] = "M";
  
          // redraw the path
          path.attr({
              path: point_set
          });
  
          // finally, remove the circle
          this.remove();
      } else if (e.which === 1) {
          dragging = this;
      }
  }
  
  
  // handle mouse click on path
  function handle_mousedown_path(e) {
  
      // split on left-click
      if (e.which === 1) {
          var X = e.clientX - paper_offset,
              Y = e.clientY - paper_offset,
              new_point = ["L", X, Y],
              point_set = this.data("point_set"),
              index;
  
          // "open" the path by removing the end ("Z")
          point_set.pop();
  
          for (var i = 0; i &lt; point_set.length; i += 1) {
              // cur point
              var pt1 = point_set[i], // cur point
                  pt2; // next point
  
              // circular wrap for next point
              if (i === point_set.length - 1) {
                  pt2 = point_set[0];
              } else {
                  pt2 = point_set[i + 1];
              }
  
              // check if these are the two points we want to split between
              if (isBetween(pt1, pt2, new_point)) {
                  index = i + 1;
                  break;
              }
          }
  
          // we should have found a place to insert the point, put it there
          if (index) {
              point_set.splice(index, 0, new_point);
          } else {
              return; // we didn't find a place to put the new point
          }
  
          // "close" the path with a ("Z")
          point_set.push("Z");
  
          // redraw the path
          this.attr({
              path: point_set
          });
  
          // create new circle to represent the new point
          c = make_circle(new_point, this);
      }
  }
  
  
  // create a new circle wrapped around the given point reference
  function make_circle(point, path) {
      var c = paper.circle(point[1], point[2], 6).attr({
          fill: "#DDD",
          stroke: "black"
      });
  
      // record the point and path reference in the circle to allow updates
      c.data("point", point).data("path", path);
  
      // set event handlers
      c.mousedown(handle_mousedown_circle);
  
      // start dragging the new circle
      dragging = c;
      return c;
  }
  
  
  // create a new colored path from four point coordinate pairs
  function make_path(p1, p2, p3, p4, color) {
  
      // starting points
      var pt1 = ["M", p1[0], p1[1]],
          pt2 = ["L", p2[0], p2[1]],
          pt3 = ["L", p3[0], p3[1]],
          pt4 = ["L", p4[0], p4[1]],
          point_set = [pt1, pt2, pt3, pt4, ["Z"]],
          path = paper.path(point_set).attr({
              stroke: color,
                  "stroke-width": 5,
                  "stroke-linecap": "round"
          });
  
      // keep a reference to the set of points
      path.data("point_set", point_set);
  
      // add listener to the path to allow path-splitting
      path.mousedown(handle_mousedown_path);
  
      // create the circles that represent the points
      make_circle(pt1, path);
      make_circle(pt2, path);
      make_circle(pt3, path);
      make_circle(pt4, path);
  }
  </code></pre>