docs: function (elInfo, p, dropOptions, isSource, isTarget) {
  			var proxyComponent = new jsPlumbUIComponent(p);
  			var _drop = p._jsPlumb.EndpointDropHandler({
  				jsPlumb: _currentInstance,
  				enabled: function () {
  					return elInfo.def.enabled;
  				},
  				isFull: function () {
  					var targetCount = _currentInstance.select({target: elInfo.id}).length;
  					return elInfo.def.maxConnections > 0 && targetCount >= elInfo.def.maxConnections;
  				},
  				element: elInfo.el,
  				elementId: elInfo.id,
  				isSource: isSource,
  				isTarget: isTarget,
  				addClass: function (clazz) {
  					_currentInstance.addClass(elInfo.el, clazz);
  				},
  				removeClass: function (clazz) {
  					_currentInstance.removeClass(elInfo.el, clazz);
  				},
  				onDrop: function (jpc) {
  					var source = jpc.endpoints[0];
  					source.anchor.locked = false;
  				},
  				isDropAllowed: function () {
  					return proxyComponent.isDropAllowed.apply(proxyComponent, arguments);
  				},
  				isRedrop:function(jpc) {
  					return (jpc.suspendedElement != null && jpc.suspendedEndpoint != null && jpc.suspendedEndpoint.element === elInfo.el);
  				},
  				getEndpoint: function (jpc) {
  
  					// make a new Endpoint for the target, or get it from the cache if uniqueEndpoint
  					// is set. if its a redrop the new endpoint will be immediately cleaned up.
  
  					var newEndpoint = elInfo.def.endpoint;
  
  					// if no cached endpoint, or there was one but it has been cleaned up
  					// (ie. detached), create a new one
  					if (newEndpoint == null || newEndpoint._jsPlumb == null) {
  						var eps = _currentInstance.deriveEndpointAndAnchorSpec(jpc.getType().join(" "), true);
  						var pp = eps.endpoints ? jsPlumb.extend(p, {
  							endpoint:elInfo.def.def.endpoint || eps.endpoints[1]
  						}) :p;
  						if (eps.anchors) {
  							pp = jsPlumb.extend(pp, {
  								anchor:elInfo.def.def.anchor || eps.anchors[1]
  							});
  						}
  						newEndpoint = _currentInstance.addEndpoint(elInfo.el, pp);
  						newEndpoint._mtNew = true;
  					}
  
  					if (p.uniqueEndpoint) elInfo.def.endpoint = newEndpoint;  // may of course just store what it just pulled out. that's ok.
  					// TODO test options to makeTarget to see if we should do this?
  					newEndpoint._doNotDeleteOnDetach = false; // reset.
  					newEndpoint._deleteOnDetach = true;
  
  					// if connection is detachable, init the new endpoint to be draggable, to support that happening.
  					if (jpc.isDetachable())
  						newEndpoint.initDraggable();
  
  					// if the anchor has a 'positionFinder' set, then delegate to that function to find
  					// out where to locate the anchor.
  					if (newEndpoint.anchor.positionFinder != null) {
  						var dropPosition = _currentInstance.getUIPosition(arguments, _currentInstance.getZoom()),
  							elPosition = _currentInstance.getOffset(elInfo.el),
  							elSize = _currentInstance.getSize(elInfo.el),
  							ap = newEndpoint.anchor.positionFinder(dropPosition, elPosition, elSize, newEndpoint.anchor.constructorParams);
  
  						newEndpoint.anchor.x = ap[0];
  						newEndpoint.anchor.y = ap[1];
  						// now figure an orientation for it..kind of hard to know what to do actually. probably the best thing i can do is to
  						// support specifying an orientation in the anchor's spec. if one is not supplied then i will make the orientation
  						// be what will cause the most natural link to the source: it will be pointing at the source, but it needs to be
  						// specified in one axis only, and so how to make that choice? i think i will use whichever axis is the one in which
  						// the target is furthest away from the source.
  					}
  
  					return newEndpoint;
  				},
  				maybeCleanup: function (ep) {
  					if (ep._mtNew && ep.connections.length === 0) {
  						_currentInstance.deleteObject({endpoint: ep});
  					}
  					else
  						delete ep._mtNew;
  				}
  			});
  
  			// wrap drop events as needed and initialise droppable
  			var dropEvent = jsPlumb.dragEvents.drop;
  			dropOptions.scope = dropOptions.scope || (p.scope || _currentInstance.Defaults.Scope);
  			dropOptions[dropEvent] = _ju.wrap(dropOptions[dropEvent], _drop, true);
  
  			// if target, return true from the over event. this will cause katavorio to stop setting drops to hover
  			// if multipleDrop is set to false.
  			if (isTarget) {
  				dropOptions[jsPlumb.dragEvents.over] = function () { return true; };
  			}
  
  			// vanilla jsplumb only
  			if (p.allowLoopback === false) {
  				dropOptions.canDrop = function (_drag) {
  					var de = _drag.getDragElement()._jsPlumbRelatedElement;
  					return de != elInfo.el;
  				};
  			}
  			_currentInstance.initDroppable(elInfo.el, dropOptions, "internal");
  
  			return _drop;
  
  		}