docs: function (_currentInstance) {
  		var _draggables = {}, _dlist = [], _delements = {}, _elementsWithEndpoints = {},
  		// elementids mapped to the draggable to which they belong.
  			_draggablesForElements = {};
  
  		/**
  		 register some element as draggable.  right now the drag init stuff is done elsewhere, and it is
  		 possible that will continue to be the case.
  		 */
  		this.register = function (el) {
  			var id = _currentInstance.getId(el),
  				parentOffset = _currentInstance.getOffset(el);
  
  			if (!_draggables[id]) {
  				_draggables[id] = el;
  				_dlist.push(el);
  				_delements[id] = {};
  			}
  
  			// look for child elements that have endpoints and register them against this draggable.
  			var _oneLevel = function (p) {
  				if (p) {
  					for (var i = 0; i < p.childNodes.length; i++) {
  						if (p.childNodes[i].nodeType != 3 && p.childNodes[i].nodeType != 8) {
  							var cEl = jsPlumb.getElement(p.childNodes[i]),
  								cid = _currentInstance.getId(p.childNodes[i], null, true);
  							if (cid && _elementsWithEndpoints[cid] && _elementsWithEndpoints[cid] > 0) {
  								var cOff = _currentInstance.getOffset(cEl);
  								_delements[id][cid] = {
  									id: cid,
  									offset: {
  										left: cOff.left - parentOffset.left,
  										top: cOff.top - parentOffset.top
  									}
  								};
  								_draggablesForElements[cid] = id;
  							}
  							_oneLevel(p.childNodes[i]);
  						}
  					}
  				}
  			};
  
  			_oneLevel(el);
  		};
  
  		// refresh the offsets for child elements of this element.
  		this.updateOffsets = function (elId) {
  			if (elId != null) {
  				var domEl = jsPlumb.getElement(elId),
  					id = _currentInstance.getId(domEl),
  					children = _delements[id],
  					parentOffset = _currentInstance.getOffset(domEl);
  
  				if (children) {
  					for (var i in children) {
  						if (children.hasOwnProperty(i)) {
  							var cel = jsPlumb.getElement(i),
  								cOff = _currentInstance.getOffset(cel);
  
  							_delements[id][i] = {
  								id: i,
  								offset: {
  									left: cOff.left - parentOffset.left,
  									top: cOff.top - parentOffset.top
  								}
  							};
  							_draggablesForElements[i] = id;
  						}
  					}
  				}
  			}
  		};
  
  		/**
  		 notification that an endpoint was added to the given el.  we go up from that el's parent
  		 node, looking for a parent that has been registered as a draggable. if we find one, we add this
  		 el to that parent's list of elements to update on drag (if it is not there already)
  		 */
  		this.endpointAdded = function (el, id) {
  
  			id = id || _currentInstance.getId(el);
  
  			var b = document.body,
  				p = el.parentNode;
  
  			_elementsWithEndpoints[id] = _elementsWithEndpoints[id] ? _elementsWithEndpoints[id] + 1 : 1;
  
  			while (p != null && p != b) {
  				var pid = _currentInstance.getId(p, null, true);
  				if (pid && _draggables[pid]) {
  					var pLoc = _currentInstance.getOffset(p);
  
  					if (_delements[pid][id] == null) {
  						var cLoc = _currentInstance.getOffset(el);
  						_delements[pid][id] = {
  							id: id,
  							offset: {
  								left: cLoc.left - pLoc.left,
  								top: cLoc.top - pLoc.top
  							}
  						};
  						_draggablesForElements[id] = pid;
  					}
  					break;
  				}
  				p = p.parentNode;
  			}
  		};
  
  		this.endpointDeleted = function (endpoint) {
  			if (_elementsWithEndpoints[endpoint.elementId]) {
  				_elementsWithEndpoints[endpoint.elementId]--;
  				if (_elementsWithEndpoints[endpoint.elementId] <= 0) {
  					for (var i in _delements) {
  						if (_delements.hasOwnProperty(i) && _delements[i]) {
  							delete _delements[i][endpoint.elementId];
  							delete _draggablesForElements[endpoint.elementId];
  						}
  					}
  				}
  			}
  		};
  
  		this.changeId = function (oldId, newId) {
  			_delements[newId] = _delements[oldId];
  			_delements[oldId] = {};
  			_draggablesForElements[newId] = _draggablesForElements[oldId];
  			_draggablesForElements[oldId] = null;
  		};
  
  		this.getElementsForDraggable = function (id) {
  			return _delements[id];
  		};
  
  		this.elementRemoved = function (elementId) {
  			var elId = _draggablesForElements[elementId];
  			if (elId) {
  				delete _delements[elId][elementId];
  				delete _draggablesForElements[elementId];
  			}
  		};
  
  		this.reset = function () {
  			_draggables = {};
  			_dlist = [];
  			_delements = {};
  			_elementsWithEndpoints = {};
  		};
  
  		//
  		// notification drag ended. We check automatically if need to update some
  		// ancestor's offsets.
  		//
  		this.dragEnded = function (el) {
  			var id = _currentInstance.getId(el),
  				ancestor = _draggablesForElements[id];
  
  			if (ancestor) this.updateOffsets(ancestor);
  		};
  
  		this.setParent = function (el, elId, p, pId) {
  			var current = _draggablesForElements[elId];
  			if (current) {
  				if (!_delements[pId])
  					_delements[pId] = {};
  				_delements[pId][elId] = _delements[current][elId];
  				delete _delements[current][elId];
  				var pLoc = _currentInstance.getOffset(p),
  					cLoc = _currentInstance.getOffset(el);
  				_delements[pId][elId].offset = {
  					left: cLoc.left - pLoc.left,
  					top: cLoc.top - pLoc.top
  				};
  				_draggablesForElements[elId] = pId;
  			}
  		};
  
  		this.getDragAncestor = function (el) {
  			var de = jsPlumb.getElement(el),
  				id = _currentInstance.getId(de),
  				aid = _draggablesForElements[id];
  
  			if (aid)
  				return jsPlumb.getElement(aid);
  			else
  				return null;
  		};
  
  	}