Edit in JSFiddle

var $node = function($parent, c, p) {
    var $content = $('<p>').addClass('content');
    var $ret = $('<div>').addClass('node').append($content).appendTo($parent);
    $ret.data('connections', []);

    $ret.content = function(c) {
      if(c === undefined) {
          return $(this).data('content');
      }
      else {
          $(this).data('content', c);
          $('.content', this).text(c);
      }
    };

    content($ret, c);
    xy($ret, p);
  
    return $ret;
};

var content = function($e, c) {
    if(c === undefined) {
        return $e.data('content');
    }
    else {
        $e.data('content', c);
        $('.content', $e).text(c);
    }
};

var xy = function($e, p) {
    if(p === undefined) {
        var pos = $e.position();
    
        return {
            x: pos.left + parseInt($e.css('width'), 10) / 2,
            y: pos.top + parseInt($e.css('height'), 10) / 2
        };
    }
    else {
        $e.css('left', p.x - parseInt($e.css('width'), 10) / 2 + 'px');
        $e.css('top', p.y - parseInt($e.css('height'), 10) / 2 + 'px');
    }
};

var angle = function(x, y) {
    var ret = Math.atan2(y, x);

    ret += ret < 0? 2 * Math.PI: 0;

    ret = Math.min(Math.PI * 2, ret);

    return ret + Math.PI;
};

var dist = function(xOffset, yOffset) {
    return Math.sqrt(xOffset * xOffset + yOffset * yOffset);
};

var $connection = function($parent, $p1, $p2) {
    var $ret = $('<div>').addClass('line').appendTo($parent);
    $ret.data('p1', $p1);
    $ret.data('p2', $p2);
  
    $ret.render = function() {
      var $elem = $(this);
      var $p1 = xy($elem.data('p1'));
      var $p2 = xy($elem.data('p2'));
      
      $elem.css('left', $p1.x + 'px');
      $elem.css('top', $p1.y + 'px');
      
      var xOffset = $p1.x - $p2.x;
      var yOffset = $p1.y - $p2.y;
      var rot = angle(xOffset, yOffset);
      var d = dist(xOffset, yOffset);
      
      $elem.css('-webkit-transform', 'rotate3d(0, 0, 1, ' + rot + 'rad)');
      $elem.css('width', d + 'px');
    };  

    return $ret;
};

var connect = function($parent, $a, $b) {
    var $con = $connection($parent, $a, $b);
    $a.data('connections').push($con);
    $b.data('connections').push($con);

    $con.render();
};

var render = function(e, ui) {
  // e.shiftKey (bind to start) -> create new tmp node, if released on top of an existing one, create connection, else new node
  $(this).data('connections').forEach(function(e) {
      e.render();
  });
};

var dragging = false;
var $parent = $('body');
var $a = $node($parent, 'foo', {x: 100, y: 100});
var $b = $node($parent, 'bar', {x: 400, y: 400});
var $c = $node($parent, 'baz', {x: 200, y: 300});

connect($parent, $a, $b);
connect($parent, $a, $c);

// apparently Draggable doesn't support live properly yet!
$('.node').live('mouseover', function() {
    $(this).draggable().bind('drag', render).bind('dragstop', function(e, ui) {
        render.apply(this, [e, ui]);
        dragging = false;
    }).bind('dragstart', function(e, ui) {
        // XXX: this gets triggered for the newly created node too so we
        // need to stash dragging state somewhere to avoid creating extra ones
        if(!dragging && e.shiftKey) {
            var $new = $(this);
            var $old = $node($parent, content($new), xy($new));

            connect($parent, $old, $new);

            dragging = true;
        }
    });
});

$('.node').live('dblclick', function() {
    var $input;
    var $elem = $(this).children('.content');

    if($elem[0].tagName == 'P') {
        $input = $('<input>').attr('type', 'text').attr('value', $elem.text()).attr('size', 10).addClass('content').bind('keypress', function(e) {
            if(e.keyCode == 13) {
            var newText = $input.attr('value');
            $input.replaceWith($elem);
            content($elem.parent(), $input.attr('value'));
            }
        });
        
        $elem.replaceWith($input);
    }
});
<!DOCTYPE html>
<html>
<head>
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1/themes/base/jquery-ui.css" rel="stylesheet" type="text/css" />
<script class="jsbin" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.js"></script>
<script class="jsbin" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/jquery-ui.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
<!--[if IE]>
  <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
  <body>
    <p>Drag nodes around, shift-drag to create new nodes, double click a node to edit its content (enter to confirm)</p>
  </body>
</html>
  article, aside, figure, footer, header, hgroup, 
  menu, nav, section { display: block; }
  
  .node {
    position: absolute;
    -moz-border-radius: 100px;
    -webkit-border-radius: 100px;
    border-radius: 100px;
    border: 3px solid black;
    width: 100px;
    height: 100px;
    text-align: center;
    background-color: white;
  }
  
  .node .content {
    position: relative;
    top: 40px;
  }
  
  .node input.content {
    top: 40px;
  }
  
  .line {
    position: absolute;
    border: 2px solid black;
    width: 400px;
    z-index: -1;
    -webkit-transform-origin: 0 0;
  }