reservation plugin - unbound request (unclean
[unfold.git] / portal / static / unbound_reservation_static / src / jsPlumb.js
diff --git a/portal/static/unbound_reservation_static/src/jsPlumb.js b/portal/static/unbound_reservation_static/src/jsPlumb.js
new file mode 100644 (file)
index 0000000..c464b47
--- /dev/null
@@ -0,0 +1,2997 @@
+/**
+ * @module jsPlumb
+ * @description Provides a way to visually connect elements on an HTML page, using either SVG, Canvas
+ * elements, or VML.   
+ * 
+ * - [Demo Site](http://jsplumb.org)
+ * - [GitHub](http://github.com/sporritt/jsplumb)
+ * 
+ * Dual licensed under the MIT and GPL2 licenses.
+ *
+ * Copyright (c) 2010 - 2013 Simon Porritt (simon.porritt@gmail.com)
+ */
+;(function() {
+                       
+    var _ju = jsPlumbUtil,
+       _addClass = function(el, clazz) { jsPlumb.CurrentLibrary.addClass(_gel(el), clazz); },
+               _hasClass = function(el, clazz) { return jsPlumb.CurrentLibrary.hasClass(_gel(el), clazz); },
+               _removeClass = function(el, clazz) { jsPlumb.CurrentLibrary.removeClass(_gel(el), clazz); },
+               _gel = function(el) { return jsPlumb.CurrentLibrary.getElementObject(el); },
+               _dom = function(el) { return jsPlumb.CurrentLibrary.getDOMElement(el); },               
+               _getOffset = function(el, _instance) {
+            var o = jsPlumb.CurrentLibrary.getOffset(_gel(el));
+                       if (_instance != null) {
+                var z = _instance.getZoom();
+                return {left:o.left / z, top:o.top / z };    
+            }
+            else
+                return o;
+        },             
+               _getSize = function(el) {
+            return jsPlumb.CurrentLibrary.getSize(_gel(el));
+        },
+               
+               /**
+                * creates a timestamp, using milliseconds since 1970, but as a string.
+                */
+               _timestamp = function() { return "" + (new Date()).getTime(); },
+
+               // helper method to update the hover style whenever it, or paintStyle, changes.
+               // we use paintStyle as the foundation and merge hoverPaintStyle over the
+               // top.
+               _updateHoverStyle = function(component) {
+                       if (component._jsPlumb.paintStyle && component._jsPlumb.hoverPaintStyle) {
+                               var mergedHoverStyle = {};
+                               jsPlumb.extend(mergedHoverStyle, component._jsPlumb.paintStyle);
+                               jsPlumb.extend(mergedHoverStyle, component._jsPlumb.hoverPaintStyle);
+                               delete component._jsPlumb.hoverPaintStyle;
+                               // we want the fillStyle of paintStyle to override a gradient, if possible.
+                               if (mergedHoverStyle.gradient && component._jsPlumb.paintStyle.fillStyle)
+                                       delete mergedHoverStyle.gradient;
+                               component._jsPlumb.hoverPaintStyle = mergedHoverStyle;
+                       }
+               },              
+               events = [ "click", "dblclick", "mouseenter", "mouseout", "mousemove", "mousedown", "mouseup", "contextmenu" ],
+               eventFilters = { "mouseout":"mouseexit" },
+               _updateAttachedElements = function(component, state, timestamp, sourceElement) {
+                       var affectedElements = component.getAttachedElements();
+                       if (affectedElements) {
+                               for (var i = 0, j = affectedElements.length; i < j; i++) {
+                                       if (!sourceElement || sourceElement != affectedElements[i])
+                                               affectedElements[i].setHover(state, true, timestamp);                   // tell the attached elements not to inform their own attached elements.
+                               }
+                       }
+               },
+               _splitType = function(t) { return t == null ? null : t.split(" "); },           
+               _applyTypes = function(component, params, doNotRepaint) {
+                       if (component.getDefaultType) {
+                               var td = component.getTypeDescriptor();
+                                       
+                               var o = _ju.merge({}, component.getDefaultType());
+                               for (var i = 0, j = component._jsPlumb.types.length; i < j; i++)
+                                       o = _ju.merge(o, component._jsPlumb.instance.getType(component._jsPlumb.types[i], td));                                         
+                                       
+                               if (params) {
+                                       o = _ju.populate(o, params);
+                               }
+                       
+                               component.applyType(o, doNotRepaint);                                   
+                               if (!doNotRepaint) component.repaint();
+                       }
+               },              
+
+// ------------------------------ BEGIN jsPlumbUIComponent --------------------------------------------
+
+               jsPlumbUIComponent = window.jsPlumbUIComponent = function(params) {
+
+                       jsPlumbUtil.EventGenerator.apply(this, arguments);
+
+                       var self = this, 
+                               a = arguments,                                                          
+                               idPrefix = self.idPrefix,
+                               id = idPrefix + (new Date()).getTime(),
+                               jpcl = jsPlumb.CurrentLibrary;
+
+                       this._jsPlumb = { 
+                               instance: params._jsPlumb,
+                               parameters:params.parameters || {},
+                               paintStyle:null,
+                               hoverPaintStyle:null,
+                               paintStyleInUse:null,
+                               hover:false,
+                               beforeDetach:params.beforeDetach,
+                               beforeDrop:params.beforeDrop,
+                               overlayPlacements : [],
+                               hoverClass: params.hoverClass || params._jsPlumb.Defaults.HoverClass || jsPlumb.Defaults.HoverClass,
+                               types:[]
+                       };
+
+                       this.getId = function() { return id; }; 
+                       
+                       // all components can generate events
+                       
+                       if (params.events) {
+                               for (var i in params.events)
+                                       self.bind(i, params.events[i]);
+                       }
+
+                       // all components get this clone function.
+                       // TODO issue 116 showed a problem with this - it seems 'a' that is in
+                       // the clone function's scope is shared by all invocations of it, the classic
+                       // JS closure problem.  for now, jsPlumb does a version of this inline where 
+                       // it used to call clone.  but it would be nice to find some time to look
+                       // further at this.
+                       this.clone = function() {
+                               var o = {};//new Object();
+                               this.constructor.apply(o, a);
+                               return o;
+                       }.bind(this);                           
+                                               
+                       // user can supply a beforeDetach callback, which will be executed before a detach
+                       // is performed; returning false prevents the detach.                   
+                       this.isDetachAllowed = function(connection) {
+                               var r = true;
+                               if (this._jsPlumb.beforeDetach) {
+                                       try { 
+                                               r = this._jsPlumb.beforeDetach(connection); 
+                                       }
+                                       catch (e) { _ju.log("jsPlumb: beforeDetach callback failed", e); }
+                               }
+                               return r;
+                       };
+                       
+                       // user can supply a beforeDrop callback, which will be executed before a dropped
+                       // connection is confirmed. user can return false to reject connection.                 
+                       this.isDropAllowed = function(sourceId, targetId, scope, connection, dropEndpoint) {
+                               var r = this._jsPlumb.instance.checkCondition("beforeDrop", { 
+                                       sourceId:sourceId, 
+                                       targetId:targetId, 
+                                       scope:scope,
+                                       connection:connection,
+                                       dropEndpoint:dropEndpoint 
+                               });
+                               if (this._jsPlumb.beforeDrop) {
+                                       try { 
+                                               r = this._jsPlumb.beforeDrop({ 
+                                                       sourceId:sourceId, 
+                                                       targetId:targetId, 
+                                                       scope:scope, 
+                                                       connection:connection,
+                                                       dropEndpoint:dropEndpoint
+                                               }); 
+                                       }
+                                       catch (e) { _ju.log("jsPlumb: beforeDrop callback failed", e); }
+                               }
+                               return r;
+                       };                                                                                                      
+
+                   var boundListeners = [],
+                       bindAListener = function(obj, type, fn) {
+                               boundListeners.push([obj, type, fn]);
+                               obj.bind(type, fn);
+                           },
+                       domListeners = [],
+               bindOne = function(o, c, evt) {
+                                       var filteredEvent = eventFilters[evt] || evt,
+                                               fn = function(ee) {
+                                                       c.fire(filteredEvent, c, ee);
+                                               };
+                                       domListeners.push([o, evt, fn]);
+                                       jpcl.bind(o, evt, fn);
+                               },
+                               unbindOne = function(o, evt, fn) {
+                                       var filteredEvent = eventFilters[evt] || evt;
+                                       jpcl.unbind(o, evt, fn);
+                               };
+
+            this.bindListeners = function(obj, _self, _hoverFunction) {
+                bindAListener(obj, "click", function(ep, e) { _self.fire("click", _self, e); });             
+               bindAListener(obj, "dblclick", function(ep, e) { _self.fire("dblclick", _self, e); });
+                bindAListener(obj, "contextmenu", function(ep, e) { _self.fire("contextmenu", _self, e); });
+                bindAListener(obj, "mouseenter", function(ep, e) {
+                    if (!_self.isHover()) {
+                        _hoverFunction(true);
+                        _self.fire("mouseenter", _self, e);
+                    }
+                });
+                bindAListener(obj, "mouseexit", function(ep, e) {
+                    if (_self.isHover()) {
+                        _hoverFunction(false);
+                        _self.fire("mouseexit", _self, e);
+                    }
+                });      
+                bindAListener(obj, "mousedown", function(ep, e) { _self.fire("mousedown", _self, e); });
+                bindAListener(obj, "mouseup", function(ep, e) { _self.fire("mouseup", _self, e); });
+            };
+
+            this.unbindListeners = function() {
+               for (var i = 0; i < boundListeners.length; i++) {
+                       var o = boundListeners[i];
+                       o[0].unbind(o[1], o[2]);
+               }               
+               boundListeners = null;
+            };            
+                   
+                   this.attachListeners = function(o, c) {
+                               for (var i = 0, j = events.length; i < j; i++) {
+                                       bindOne(o, c, events[i]);                       
+                               }
+                       };      
+                       this.detachListeners = function() {
+                               for (var i = 0; i < domListeners.length; i++) {
+                                       unbindOne(domListeners[i][0], domListeners[i][1], domListeners[i][2]);
+                               }
+                               domListeners = null;
+                       };                          
+                   
+                   this.reattachListenersForElement = function(o) {
+                           if (arguments.length > 1) {
+                               for (var i = 0, j = events.length; i < j; i++)
+                                       unbindOne(o, events[i]);
+                               for (i = 1, j = arguments.length; i < j; i++)
+                                       this.attachListeners(o, arguments[i]);
+                       }
+                   };                                                                
+               };
+
+               jsPlumbUtil.extend(jsPlumbUIComponent, jsPlumbUtil.EventGenerator, {
+                       
+                       getParameter : function(name) { 
+                               return this._jsPlumb.parameters[name]; 
+                       },
+                       
+                       setParameter : function(name, value) { 
+                               this._jsPlumb.parameters[name] = value; 
+                       },
+                       
+                       getParameters : function() { 
+                               return this._jsPlumb.parameters; 
+                       },                      
+                       
+                       setParameters : function(p) { 
+                               this._jsPlumb.parameters = p; 
+                       },                      
+                       
+                       addClass : function(clazz) {
+                           if (this.canvas != null)
+                               _addClass(this.canvas, clazz);
+                       },
+                                               
+                       removeClass : function(clazz) {
+                           if (this.canvas != null)
+                               _removeClass(this.canvas, clazz);
+                       },
+                       
+                       setType : function(typeId, params, doNotRepaint) {                              
+                               this._jsPlumb.types = _splitType(typeId) || [];
+                               _applyTypes(this, params, doNotRepaint);                                                                        
+                       },
+                       
+                       getType : function() {
+                               return this._jsPlumb.types;
+                       },
+                       
+                       reapplyTypes : function(params, doNotRepaint) {
+                               _applyTypes(this, params, doNotRepaint);
+                       },
+                       
+                       hasType : function(typeId) {
+                               return jsPlumbUtil.indexOf(this._jsPlumb.types, typeId) != -1;
+                       },
+                       
+                       addType : function(typeId, params, doNotRepaint) {
+                               var t = _splitType(typeId), _cont = false;
+                               if (t != null) {
+                                       for (var i = 0, j = t.length; i < j; i++) {
+                                               if (!this.hasType(t[i])) {
+                                                       this._jsPlumb.types.push(t[i]);
+                                                       _cont = true;                                           
+                                               }
+                                       }
+                                       if (_cont) _applyTypes(this, params, doNotRepaint);
+                               }
+                       },
+                       
+                       removeType : function(typeId, doNotRepaint) {
+                               var t = _splitType(typeId), _cont = false, _one = function(tt) {
+                                               var idx = _ju.indexOf(this._jsPlumb.types, tt);
+                                               if (idx != -1) {
+                                                       this._jsPlumb.types.splice(idx, 1);
+                                                       return true;
+                                               }
+                                               return false;
+                                       }.bind(this);
+                               
+                               if (t != null) {
+                                       for (var i = 0,j = t.length; i < j; i++) {
+                                               _cont = _one(t[i]) || _cont;
+                                       }
+                                       if (_cont) _applyTypes(this, null, doNotRepaint);
+                               }
+                       },
+                       
+                       toggleType : function(typeId, params, doNotRepaint) {
+                               var t = _splitType(typeId);
+                               if (t != null) {
+                                       for (var i = 0, j = t.length; i < j; i++) {
+                                               var idx = jsPlumbUtil.indexOf(this._jsPlumb.types, t[i]);
+                                               if (idx != -1)
+                                                       this._jsPlumb.types.splice(idx, 1);
+                                               else
+                                                       this._jsPlumb.types.push(t[i]);
+                                       }
+                                               
+                                       _applyTypes(this, params, doNotRepaint);
+                               }
+                       },
+                       applyType : function(t, doNotRepaint) {
+                               this.setPaintStyle(t.paintStyle, doNotRepaint);                         
+                               this.setHoverPaintStyle(t.hoverPaintStyle, doNotRepaint);
+                               if (t.parameters){
+                                       for (var i in t.parameters)
+                                               this.setParameter(i, t.parameters[i]);
+                               }
+                       },
+                       setPaintStyle : function(style, doNotRepaint) {
+//                     this._jsPlumb.paintStyle = jsPlumb.extend({}, style);
+// TODO figure out if we want components to clone paintStyle so as not to share it.
+                               this._jsPlumb.paintStyle = style;
+                       this._jsPlumb.paintStyleInUse = this._jsPlumb.paintStyle;
+                       _updateHoverStyle(this);
+                       if (!doNotRepaint) this.repaint();
+                   },
+                   getPaintStyle : function() {
+                       return this._jsPlumb.paintStyle;
+                   },
+                   setHoverPaintStyle : function(style, doNotRepaint) {                        
+                       //this._jsPlumb.hoverPaintStyle = jsPlumb.extend({}, style);
+// TODO figure out if we want components to clone paintStyle so as not to share it.                    
+                       this._jsPlumb.hoverPaintStyle = style;
+                       _updateHoverStyle(this);
+                       if (!doNotRepaint) this.repaint();
+                   },
+                   getHoverPaintStyle : function() {
+                       return this._jsPlumb.hoverPaintStyle;
+                   },
+                       cleanup:function() {            
+                               this.unbindListeners();
+                               this.detachListeners();
+                       },
+                       destroy:function() {
+                               this.cleanupListeners();
+                               this.clone = null;                              
+                               this._jsPlumb = null;
+                       },
+                       
+                       isHover : function() { return this._jsPlumb.hover; },
+                       
+                       setHover : function(hover, ignoreAttachedElements, timestamp) {
+                               var jpcl = jsPlumb.CurrentLibrary;
+                       // while dragging, we ignore these events.  this keeps the UI from flashing and
+                       // swishing and whatevering.
+                               if (this._jsPlumb && !this._jsPlumb.instance.currentlyDragging && !this._jsPlumb.instance.isHoverSuspended()) {
+                   
+                               this._jsPlumb.hover = hover;
+                        
+                    if (this.canvas != null) {
+                        if (this._jsPlumb.instance.hoverClass != null) {                            
+                            jpcl[hover ? "addClass" : "removeClass"](this.canvas, this._jsPlumb.instance.hoverClass);                                                                      
+                        }                                              
+                    }
+                                       if (this._jsPlumb.hoverPaintStyle != null) {
+                                               this._jsPlumb.paintStyleInUse = hover ? this._jsPlumb.hoverPaintStyle : this._jsPlumb.paintStyle;
+                                               if (!this._jsPlumb.instance.isSuspendDrawing()) {
+                                                       timestamp = timestamp || _timestamp();
+                                                       this.repaint({timestamp:timestamp, recalc:false});
+                                               }
+                                       }
+                                       // get the list of other affected elements, if supported by this component.
+                                       // for a connection, its the endpoints.  for an endpoint, its the connections! surprise.
+                                       if (this.getAttachedElements && !ignoreAttachedElements)
+                                               _updateAttachedElements(this, hover, _timestamp(), this);
+                               }
+                   }
+               });
+
+// ------------------------------ END jsPlumbUIComponent --------------------------------------------
+
+// ------------------------------ BEGIN OverlayCapablejsPlumbUIComponent --------------------------------------------
+
+               var _internalLabelOverlayId = "__label",
+                       // helper to get the index of some overlay
+                       _getOverlayIndex = function(component, id) {
+                               var idx = -1;
+                               for (var i = 0, j = component._jsPlumb.overlays.length; i < j; i++) {
+                                       if (id === component._jsPlumb.overlays[i].id) {
+                                               idx = i;
+                                               break;
+                                       }
+                               }
+                               return idx;
+                       },
+                       // this is a shortcut helper method to let people add a label as
+                       // overlay.                                             
+                       _makeLabelOverlay = function(component, params) {
+
+                               var _params = {
+                                       cssClass:params.cssClass,
+                                       labelStyle : component.labelStyle,                                      
+                                       id:_internalLabelOverlayId,
+                                       component:component,
+                                       _jsPlumb:component._jsPlumb.instance  // TODO not necessary, since the instance can be accessed through the component.
+                               },
+                               mergedParams = jsPlumb.extend(_params, params);
+
+                               return new jsPlumb.Overlays[component._jsPlumb.instance.getRenderMode()].Label( mergedParams );
+                       },
+                       _processOverlay = function(component, o) {
+                               var _newOverlay = null;
+                               if (_ju.isArray(o)) {   // this is for the shorthand ["Arrow", { width:50 }] syntax
+                                       // there's also a three arg version:
+                                       // ["Arrow", { width:50 }, {location:0.7}] 
+                                       // which merges the 3rd arg into the 2nd.
+                                       var type = o[0],
+                                               // make a copy of the object so as not to mess up anyone else's reference...
+                                               p = jsPlumb.extend({component:component, _jsPlumb:component._jsPlumb.instance}, o[1]);
+                                       if (o.length == 3) jsPlumb.extend(p, o[2]);
+                                       _newOverlay = new jsPlumb.Overlays[component._jsPlumb.instance.getRenderMode()][type](p);                                       
+                               } else if (o.constructor == String) {
+                                       _newOverlay = new jsPlumb.Overlays[component._jsPlumb.instance.getRenderMode()][o]({component:component, _jsPlumb:component._jsPlumb.instance});
+                               } else {
+                                       _newOverlay = o;
+                               }                                                                               
+                                       
+                               component._jsPlumb.overlays.push(_newOverlay);
+                       },
+                       _calculateOverlaysToAdd = function(component, params) {
+                               var defaultKeys = component.defaultOverlayKeys || [], o = params.overlays,
+                                       checkKey = function(k) {
+                                               return component._jsPlumb.instance.Defaults[k] || jsPlumb.Defaults[k] || [];
+                                       };
+                               
+                               if (!o) o = [];
+
+                               for (var i = 0, j = defaultKeys.length; i < j; i++)
+                                       o.unshift.apply(o, checkKey(defaultKeys[i]));
+                               
+                               return o;
+                       },              
+                       OverlayCapableJsPlumbUIComponent = window.OverlayCapableJsPlumbUIComponent = function(params) {
+
+                               jsPlumbUIComponent.apply(this, arguments);
+                               this._jsPlumb.overlays = [];                    
+
+                               var _overlays = _calculateOverlaysToAdd(this, params);
+                               if (_overlays) {
+                                       for (var i = 0, j = _overlays.length; i < j; i++) {
+                                               _processOverlay(this, _overlays[i]);
+                                       }
+                               }
+                               
+                               if (params.label) {
+                                       var loc = params.labelLocation || this.defaultLabelLocation || 0.5,
+                                               labelStyle = params.labelStyle || this._jsPlumb.instance.Defaults.LabelStyle || jsPlumb.Defaults.LabelStyle;
+
+                                       this._jsPlumb.overlays.push(_makeLabelOverlay(this, {
+                                               label:params.label,
+                                               location:loc,
+                                               labelStyle:labelStyle
+                                       }));
+                               }                                                         
+                       };
+
+               jsPlumbUtil.extend(OverlayCapableJsPlumbUIComponent, jsPlumbUIComponent, {
+                       applyType : function(t, doNotRepaint) {                 
+                               this.removeAllOverlays(doNotRepaint);
+                               if (t.overlays) {
+                                       for (var i = 0, j = t.overlays.length; i < j; i++)
+                                               this.addOverlay(t.overlays[i], true);
+                               }
+                       },
+                       setHover : function(hover, ignoreAttachedElements, timestamp) {            
+                               if (this._jsPlumb && !this._jsPlumb.instance.isConnectionBeingDragged()) {
+                       for (var i = 0, j = this._jsPlumb.overlays.length; i < j; i++) {
+                                               this._jsPlumb.overlays[i][hover ? "addClass":"removeClass"](this._jsPlumb.instance.hoverClass);
+                                       }
+                               }
+            },
+            addOverlay : function(overlay, doNotRepaint) { 
+                               _processOverlay(this, overlay); 
+                               if (!doNotRepaint) this.repaint();
+                       },
+                       getOverlay : function(id) {
+                               var idx = _getOverlayIndex(this, id);
+                               return idx >= 0 ? this._jsPlumb.overlays[idx] : null;
+                       },                      
+                       getOverlays : function() {
+                               return this._jsPlumb.overlays;
+                       },                      
+                       hideOverlay : function(id) {
+                               var o = this.getOverlay(id);
+                               if (o) o.hide();
+                       },
+                       hideOverlays : function() {
+                               for (var i = 0, j = this._jsPlumb.overlays.length; i < j; i++)
+                                       this._jsPlumb.overlays[i].hide();
+                       },
+                       showOverlay : function(id) {
+                               var o = this.getOverlay(id);
+                               if (o) o.show();
+                       },
+                       showOverlays : function() {
+                               for (var i = 0, j = this._jsPlumb.overlays.length; i < j; i++)
+                                       this._jsPlumb.overlays[i].show();
+                       },
+                       removeAllOverlays : function(doNotRepaint) {
+                               for (var i = 0, j = this._jsPlumb.overlays.length; i < j; i++) {
+                                       if (this._jsPlumb.overlays[i].cleanup) this._jsPlumb.overlays[i].cleanup();
+                               }
+
+                               this._jsPlumb.overlays.splice(0, this._jsPlumb.overlays.length);
+                               this._jsPlumb.overlayPositions = null;
+                               if (!doNotRepaint)
+                                       this.repaint();
+                       },
+                       removeOverlay : function(overlayId) {
+                               var idx = _getOverlayIndex(this, overlayId);
+                               if (idx != -1) {
+                                       var o = this._jsPlumb.overlays[idx];
+                                       if (o.cleanup) o.cleanup();
+                                       this._jsPlumb.overlays.splice(idx, 1);
+                                       this._jsPlumb.overlayPositions && delete this._jsPlumb.overlayPositions[overlayId];
+                               }
+                       },
+                       removeOverlays : function() {
+                               for (var i = 0, j = arguments.length; i < j; i++)
+                                       this.removeOverlay(arguments[i]);
+                       },
+                       getLabel : function() {
+                               var lo = this.getOverlay(_internalLabelOverlayId);
+                               return lo != null ? lo.getLabel() : null;
+                       },              
+                       getLabelOverlay : function() {
+                               return this.getOverlay(_internalLabelOverlayId);
+                       },
+                       setLabel : function(l) {
+                               var lo = this.getOverlay(_internalLabelOverlayId);
+                               if (!lo) {
+                                       var params = l.constructor == String || l.constructor == Function ? { label:l } : l;
+                                       lo = _makeLabelOverlay(this, params);   
+                                       this._jsPlumb.overlays.push(lo);
+                               }
+                               else {
+                                       if (l.constructor == String || l.constructor == Function) lo.setLabel(l);
+                                       else {
+                                               if (l.label) lo.setLabel(l.label);
+                                               if (l.location) lo.setLocation(l.location);
+                                       }
+                               }
+                               
+                               if (!this._jsPlumb.instance.isSuspendDrawing()) 
+                                       this.repaint();
+                       },
+                       cleanup:function() {
+                               for (var i = 0; i < this._jsPlumb.overlays.length; i++) {
+                                       this._jsPlumb.overlays[i].cleanup();
+                                       this._jsPlumb.overlays[i].destroy();
+                               }
+                               this._jsPlumb.overlays.splice(0);
+                               this._jsPlumb.overlayPositions = null;
+                       },
+                       setVisible:function(v) {
+                               this[v ? "showOverlays" : "hideOverlays"]();
+                       },
+                       setAbsoluteOverlayPosition:function(overlay, xy) {
+                               this._jsPlumb.overlayPositions = this._jsPlumb.overlayPositions || {};
+                               this._jsPlumb.overlayPositions[overlay.id] = xy;
+                       },
+                       getAbsoluteOverlayPosition:function(overlay) {
+                               return this._jsPlumb.overlayPositions ? this._jsPlumb.overlayPositions[overlay.id] : null;
+                       }
+               });             
+
+// ------------------------------ END OverlayCapablejsPlumbUIComponent --------------------------------------------
+               
+               var _jsPlumbInstanceIndex = 0,
+                       getInstanceIndex = function() {
+                               var i = _jsPlumbInstanceIndex + 1;
+                               _jsPlumbInstanceIndex++;
+                               return i;
+                       };
+
+               var jsPlumbInstance = window.jsPlumbInstance = function(_defaults) {
+                               
+                       this.Defaults = {
+                               Anchor : "BottomCenter",
+                               Anchors : [ null, null ],
+                   ConnectionsDetachable : true,
+                   ConnectionOverlays : [ ],
+                   Connector : "Bezier",
+                               Container : null,
+                               DoNotThrowErrors:false,
+                               DragOptions : { },
+                               DropOptions : { },
+                               Endpoint : "Dot",
+                               EndpointOverlays : [ ],
+                               Endpoints : [ null, null ],
+                               EndpointStyle : { fillStyle : "#456" },
+                               EndpointStyles : [ null, null ],
+                               EndpointHoverStyle : null,
+                               EndpointHoverStyles : [ null, null ],
+                               HoverPaintStyle : null,
+                               LabelStyle : { color : "black" },
+                               LogEnabled : false,
+                               Overlays : [ ],
+                               MaxConnections : 1, 
+                               PaintStyle : { lineWidth : 8, strokeStyle : "#456" },            
+                               ReattachConnections:false,
+                               RenderMode : "svg",
+                               Scope : "jsPlumb_DefaultScope"
+                       };
+                       if (_defaults) jsPlumb.extend(this.Defaults, _defaults);
+               
+                       this.logEnabled = this.Defaults.LogEnabled;
+                       this._connectionTypes = {};
+                       this._endpointTypes = {};               
+
+                       jsPlumbUtil.EventGenerator.apply(this);
+
+                       var _currentInstance = this,
+                               _instanceIndex = getInstanceIndex(),
+                               _bb = _currentInstance.bind,
+                               _initialDefaults = {},
+                   _zoom = 1,
+                   _info = function(el) {
+                       var _el = _dom(el);     
+                       return { el:_el, id:(jsPlumbUtil.isString(el) && _el == null) ? el : _getId(_el) };
+                   };
+            
+               this.getInstanceIndex = function() { return _instanceIndex; };
+
+               this.setZoom = function(z, repaintEverything) {
+               _zoom = z;
+               if (repaintEverything) _currentInstance.repaintEverything();
+               };
+               this.getZoom = function() { return _zoom; };
+                        
+                       for (var i in this.Defaults)
+                               _initialDefaults[i] = this.Defaults[i];
+                       
+                       this.bind = function(event, fn) {               
+                               if ("ready" === event && initialized) fn();
+                               else _bb.apply(_currentInstance,[event, fn]);
+                       };
+
+                       _currentInstance.importDefaults = function(d) {
+                               for (var i in d) {
+                                       _currentInstance.Defaults[i] = d[i];
+                               }       
+                               return _currentInstance;
+                       };              
+                       
+                       _currentInstance.restoreDefaults = function() {
+                               _currentInstance.Defaults = jsPlumb.extend({}, _initialDefaults);
+                               return _currentInstance;
+                       };
+               
+                   var log = null,
+                       resizeTimer = null,
+                       initialized = false,
+                       // TODO remove from window scope       
+                       connections = [],
+                       // map of element id -> endpoint lists. an element can have an arbitrary
+                       // number of endpoints on it, and not all of them have to be connected
+                       // to anything.         
+                       endpointsByElement = {},
+                       endpointsByUUID = {},
+                       offsets = {},
+                       offsetTimestamps = {},
+                       floatingConnections = {},
+                       draggableStates = {},           
+                       connectionBeingDragged = false,
+                       sizes = [],
+                       _suspendDrawing = false,
+                       _suspendedAt = null,
+                       DEFAULT_SCOPE = this.Defaults.Scope,
+                       renderMode = null,  // will be set in init()            
+                       _curIdStamp = 1,
+                       _idstamp = function() { return "" + _curIdStamp++; },                                                   
+               
+                               //
+                               // appends an element to some other element, which is calculated as follows:
+                               // 
+                               // 1. if _currentInstance.Defaults.Container exists, use that element.
+                               // 2. if the 'parent' parameter exists, use that.
+                               // 3. otherwise just use the root element (for DOM usage, the document body).
+                               // 
+                               //
+                               _appendElement = function(el, parent) {
+                                       if (_currentInstance.Defaults.Container)
+                                               jsPlumb.CurrentLibrary.appendElement(el, _currentInstance.Defaults.Container);
+                                       else if (!parent)
+                                               jsPlumbAdapter.appendToRoot(el);
+                                       else
+                                               jsPlumb.CurrentLibrary.appendElement(el, parent);
+                               },              
+                               
+                               //
+                               // YUI, for some reason, put the result of a Y.all call into an object that contains
+                               // a '_nodes' array, instead of handing back an array-like object like the other
+                               // libraries do.
+                               //
+                               _convertYUICollection = function(c) {
+                                       return c._nodes ? c._nodes : c;
+                               },                
+
+                       //
+                       // Draws an endpoint and its connections. this is the main entry point into drawing connections as well
+                       // as endpoints, since jsPlumb is endpoint-centric under the hood.
+                       // 
+                       // @param element element to draw (of type library specific element object)
+                       // @param ui UI object from current library's event system. optional.
+                       // @param timestamp timestamp for this paint cycle. used to speed things up a little by cutting down the amount of offset calculations we do.
+                       // @param clearEdits defaults to false; indicates that mouse edits for connectors should be cleared
+                       ///
+                       _draw = function(element, ui, timestamp, clearEdits) {
+
+                               // TODO is it correct to filter by headless at this top level? how would a headless adapter ever repaint?
+                   if (!jsPlumbAdapter.headless && !_suspendDrawing) {
+                                   var id = _getId(element),
+                                       repaintEls = _currentInstance.dragManager.getElementsForDraggable(id);                      
+
+                                   if (timestamp == null) timestamp = _timestamp();
+
+                                   // update the offset of everything _before_ we try to draw anything.
+                                   var o = _updateOffset( { elId : id, offset : ui, recalc : false, timestamp : timestamp });
+
+                               if (repaintEls) {
+                                   for (var i in repaintEls) {                                                                                                                         
+                                       // TODO this seems to cause a lag, but we provide the offset, so in theory it 
+                                       // should not.  is the timestamp failing?
+                                               _updateOffset( { 
+                                                       elId : repaintEls[i].id, 
+                                                       offset : {
+                                                                       left:o.o.left + repaintEls[i].offset.left,
+                                                               top:o.o.top + repaintEls[i].offset.top
+                                                       }, 
+                                                       recalc : false, 
+                                                       timestamp : timestamp 
+                                               });
+                                       }
+                                   }   
+                                                         
+
+                                   _currentInstance.anchorManager.redraw(id, ui, timestamp, null, clearEdits);
+                                   
+                                   if (repaintEls) {
+                                           for (var j in repaintEls) {
+                                                       _currentInstance.anchorManager.redraw(repaintEls[j].id, ui, timestamp, repaintEls[j].offset, clearEdits, true);                         
+                                           }
+                                       }               
+                   }
+                       },
+
+                       //
+                       // executes the given function against the given element if the first
+                       // argument is an object, or the list of elements, if the first argument
+                       // is a list. the function passed in takes (element, elementId) as
+                       // arguments.
+                       //
+                       _elementProxy = function(element, fn) {
+                               var retVal = null, el, id;
+                               if (_ju.isArray(element)) {
+                                       retVal = [];
+                                       for ( var i = 0, j = element.length; i < j; i++) {
+                                               el = _gel(element[i]);
+                                               id = _currentInstance.getAttribute(el, "id");
+                                               retVal.push(fn(el, id)); // append return values to what we will return
+                                       }
+                               } else {
+                                       el = _gel(element);
+                                       id = _currentInstance.getAttribute(el, "id");
+                                       retVal = fn(el, id);
+                               }
+                               return retVal;
+                       },                              
+
+                       //
+                       // gets an Endpoint by uuid.
+                       //
+                       _getEndpoint = function(uuid) { return endpointsByUUID[uuid]; },
+
+               /**
+                * inits a draggable if it's not already initialised.
+                * TODO: somehow abstract this to the adapter, because the concept of "draggable" has no
+                * place on the server.
+                */
+               _initDraggableIfNecessary = function(element, isDraggable, dragOptions) {
+                       // TODO move to DragManager?
+                       if (!jsPlumbAdapter.headless) {
+                               var _draggable = isDraggable == null ? false : isDraggable, jpcl = jsPlumb.CurrentLibrary;
+                               if (_draggable) {
+                                       if (jpcl.isDragSupported(element) && !jpcl.isAlreadyDraggable(element)) {
+                                               var options = dragOptions || _currentInstance.Defaults.DragOptions || jsPlumb.Defaults.DragOptions;
+                                               options = jsPlumb.extend( {}, options); // make a copy.
+                                               var dragEvent = jpcl.dragEvents.drag,
+                                                       stopEvent = jpcl.dragEvents.stop,
+                                                       startEvent = jpcl.dragEvents.start;
+       
+                                               options[startEvent] = _ju.wrap(options[startEvent], function() {
+                                                       _currentInstance.setHoverSuspended(true);                                                       
+                                                       _currentInstance.select({source:element}).addClass(_currentInstance.elementDraggingClass + " " + _currentInstance.sourceElementDraggingClass, true);
+                                                       _currentInstance.select({target:element}).addClass(_currentInstance.elementDraggingClass + " " + _currentInstance.targetElementDraggingClass, true);
+                                                       _currentInstance.setConnectionBeingDragged(true);
+                                               });
+       
+                                               options[dragEvent] = _ju.wrap(options[dragEvent], function() {                            
+                                                       var ui = jpcl.getUIPosition(arguments, _currentInstance.getZoom());
+                                                       _draw(element, ui, null, true);
+                                                       _addClass(element, "jsPlumb_dragged");
+                                               });
+                                               options[stopEvent] = _ju.wrap(options[stopEvent], function() {
+                                                       var ui = jpcl.getUIPosition(arguments, _currentInstance.getZoom());
+                                                       _draw(element, ui);
+                                                       _removeClass(element, "jsPlumb_dragged");
+                                                       _currentInstance.setHoverSuspended(false);                                                      
+                                                       _currentInstance.select({source:element}).removeClass(_currentInstance.elementDraggingClass + " " + _currentInstance.sourceElementDraggingClass, true);
+                                                       _currentInstance.select({target:element}).removeClass(_currentInstance.elementDraggingClass + " " + _currentInstance.targetElementDraggingClass, true);
+                                                       _currentInstance.setConnectionBeingDragged(false);
+                                                       _currentInstance.dragManager.dragEnded(element);
+                                               });
+                                               var elId = _getId(element); // need ID
+                                               draggableStates[elId] = true;  
+                                               var draggable = draggableStates[elId];
+                                               options.disabled = draggable == null ? false : !draggable;
+                                               jpcl.initDraggable(element, options, false, _currentInstance);
+                                               _currentInstance.dragManager.register(element);
+                                       }
+                               }
+                       }
+               },
+               
+               /*
+               * prepares a final params object that can be passed to _newConnection, taking into account defaults, events, etc.
+               */
+               _prepareConnectionParams = function(params, referenceParams) {
+                       var _p = jsPlumb.extend( { }, params);
+                       if (referenceParams) jsPlumb.extend(_p, referenceParams);
+                       
+                       // hotwire endpoints passed as source or target to sourceEndpoint/targetEndpoint, respectively.
+                       if (_p.source) {
+                               if (_p.source.endpoint) 
+                                       _p.sourceEndpoint = _p.source;
+                               else
+                                       _p.source = _dom(_p.source);
+                       }
+                       if (_p.target) {
+                               if (_p.target.endpoint) 
+                                       _p.targetEndpoint = _p.target;
+                               else
+                                       _p.target = _dom(_p.target);
+                       }
+                       
+                       // test for endpoint uuids to connect
+                       if (params.uuids) {
+                               _p.sourceEndpoint = _getEndpoint(params.uuids[0]);
+                               _p.targetEndpoint = _getEndpoint(params.uuids[1]);
+                       }                                               
+
+                       // now ensure that if we do have Endpoints already, they're not full.
+                       // source:
+                       if (_p.sourceEndpoint && _p.sourceEndpoint.isFull()) {
+                               _ju.log(_currentInstance, "could not add connection; source endpoint is full");
+                               return;
+                       }
+
+                       // target:
+                       if (_p.targetEndpoint && _p.targetEndpoint.isFull()) {
+                               _ju.log(_currentInstance, "could not add connection; target endpoint is full");
+                               return;
+                       }
+                       
+                       // if source endpoint mandates connection type and nothing specified in our params, use it.
+                       if (!_p.type && _p.sourceEndpoint)
+                               _p.type = _p.sourceEndpoint.connectionType;
+                       
+                       // copy in any connectorOverlays that were specified on the source endpoint.
+                       // it doesnt copy target endpoint overlays.  i'm not sure if we want it to or not.
+                       if (_p.sourceEndpoint && _p.sourceEndpoint.connectorOverlays) {
+                               _p.overlays = _p.overlays || [];
+                               for (var i = 0, j = _p.sourceEndpoint.connectorOverlays.length; i < j; i++) {
+                                       _p.overlays.push(_p.sourceEndpoint.connectorOverlays[i]);
+                               }
+                       }               
+            
+            // pointer events
+            if (!_p["pointer-events"] && _p.sourceEndpoint && _p.sourceEndpoint.connectorPointerEvents)
+                _p["pointer-events"] = _p.sourceEndpoint.connectorPointerEvents;
+                                                                       
+                       // if there's a target specified (which of course there should be), and there is no
+                       // target endpoint specified, and 'newConnection' was not set to true, then we check to
+                       // see if a prior call to makeTarget has provided us with the specs for the target endpoint, and
+                       // we use those if so.  additionally, if the makeTarget call was specified with 'uniqueEndpoint' set
+                       // to true, then if that target endpoint has already been created, we re-use it.
+
+                       var tid, tep, existingUniqueEndpoint, newEndpoint;
+
+                       // TODO: this code can be refactored to be a little dry.
+                       if (_p.target && !_p.target.endpoint && !_p.targetEndpoint && !_p.newConnection) {
+                               tid = _getId(_p.target);
+                               tep =_targetEndpointDefinitions[tid];
+                               existingUniqueEndpoint = _targetEndpoints[tid];                 
+
+                               if (tep) {                      
+                                       // if target not enabled, return.
+                                       if (!_targetsEnabled[tid]) return;
+
+                                       // TODO this is dubious. i think it is there so that the endpoint can subsequently
+                                       // be dragged (ie it kicks off the draggable registration). but it is dubious.
+                                       tep.isTarget = true;
+
+                                       // check for max connections??                                          
+                                       newEndpoint = existingUniqueEndpoint != null ? existingUniqueEndpoint : _currentInstance.addEndpoint(_p.target, tep);
+                                       if (_targetEndpointsUnique[tid]) _targetEndpoints[tid] = newEndpoint;
+                                        _p.targetEndpoint = newEndpoint;
+                                        // TODO test options to makeTarget to see if we should do this?
+                                        newEndpoint._doNotDeleteOnDetach = false; // reset.
+                                        newEndpoint._deleteOnDetach = true;                                     
+                               }
+                       }
+
+                       // same thing, but for source.
+                       if (_p.source && !_p.source.endpoint && !_p.sourceEndpoint && !_p.newConnection) {
+                               tid = _getId(_p.source);
+                               tep = _sourceEndpointDefinitions[tid];
+                               existingUniqueEndpoint = _sourceEndpoints[tid];                         
+
+                               if (tep) {
+                                       // if source not enabled, return.                                       
+                                       if (!_sourcesEnabled[tid]) return;
+
+                                       // TODO this is dubious. i think it is there so that the endpoint can subsequently
+                                       // be dragged (ie it kicks off the draggable registration). but it is dubious.
+                                       //tep.isSource = true;
+                               
+                                       newEndpoint = existingUniqueEndpoint != null ? existingUniqueEndpoint : _currentInstance.addEndpoint(_p.source, tep);
+                                       if (_sourceEndpointsUnique[tid]) _sourceEndpoints[tid] = newEndpoint;
+                                        _p.sourceEndpoint = newEndpoint;
+                                        // TODO test options to makeSource to see if we should do this?
+                                        newEndpoint._doNotDeleteOnDetach = false; // reset.
+                                        newEndpoint._deleteOnDetach = true;
+                               }
+                       }
+                       
+                       return _p;
+               },
+               
+               _newConnection = function(params) {
+                       var connectionFunc = _currentInstance.Defaults.ConnectionType || _currentInstance.getDefaultConnectionType(),
+                           endpointFunc = _currentInstance.Defaults.EndpointType || jsPlumb.Endpoint,
+                           parent = jsPlumb.CurrentLibrary.getParent;
+                       
+                       if (params.container)
+                               params.parent = params.container;
+                       else {
+                               if (params.sourceEndpoint)
+                                       params.parent = params.sourceEndpoint.parent;
+                               else if (params.source.constructor == endpointFunc)
+                                       params.parent = params.source.parent;
+                               else params.parent = parent(params.source);
+                       }
+                       
+                       params._jsPlumb = _currentInstance;
+            params.newConnection = _newConnection;
+            params.newEndpoint = _newEndpoint;
+            params.endpointsByUUID = endpointsByUUID;             
+            params.endpointsByElement = endpointsByElement;  
+            params.finaliseConnection = _finaliseConnection;
+                       var con = new connectionFunc(params);
+                       con.id = "con_" + _idstamp();
+                       _eventFireProxy("click", "click", con);
+                       _eventFireProxy("dblclick", "dblclick", con);
+            _eventFireProxy("contextmenu", "contextmenu", con);
+
+            // if the connection is draggable, then maybe we need to tell the target endpoint to init the
+            // dragging code. it won't run again if it already configured to be draggable.
+            if (con.isDetachable()) {
+               con.endpoints[0].initDraggable();
+               con.endpoints[1].initDraggable();
+            }
+
+                       return con;
+               },
+               
+               //
+               // adds the connection to the backing model, fires an event if necessary and then redraws
+               //
+               _finaliseConnection = function(jpc, params, originalEvent, doInformAnchorManager) {
+            params = params || {};
+                       // add to list of connections (by scope).
+            if (!jpc.suspendedEndpoint)
+                           connections.push(jpc);
+                       
+            // always inform the anchor manager
+            // except that if jpc has a suspended endpoint it's not true to say the
+            // connection is new; it has just (possibly) moved. the question is whether
+            // to make that call here or in the anchor manager.  i think perhaps here.
+            if (jpc.suspendedEndpoint == null || doInformAnchorManager)
+               _currentInstance.anchorManager.newConnection(jpc);
+
+                       // force a paint
+                       _draw(jpc.source);
+                       
+                       // fire an event
+                       if (!params.doNotFireConnectionEvent && params.fireEvent !== false) {
+                       
+                               var eventArgs = {
+                                       connection:jpc,
+                                       source : jpc.source, target : jpc.target,
+                                       sourceId : jpc.sourceId, targetId : jpc.targetId,
+                                       sourceEndpoint : jpc.endpoints[0], targetEndpoint : jpc.endpoints[1]
+                               };
+                       
+                               _currentInstance.fire("connection", eventArgs, originalEvent);
+                       }
+               },
+               
+               _eventFireProxy = function(event, proxyEvent, obj) {
+                       obj.bind(event, function(originalObject, originalEvent) {
+                               _currentInstance.fire(proxyEvent, obj, originalEvent);
+                       });
+               },
+               
+               /*
+                * for the given endpoint params, returns an appropriate parent element for the UI elements that will be added.
+                * this function is used by _newEndpoint (directly below), and also in the makeSource function in jsPlumb.
+                * 
+                *   the logic is to first look for a "container" member of params, and pass that back if found.  otherwise we
+                *   handoff to the 'getParent' function in the current library.
+                */
+               _getParentFromParams = function(params) {
+                       if (params.container)
+                               return params.container;
+                       else {
+                var tag = jsPlumb.CurrentLibrary.getTagName(params.source),
+                    p = jsPlumb.CurrentLibrary.getParent(params.source);
+                if (tag && tag.toLowerCase() === "td")
+                    return jsPlumb.CurrentLibrary.getParent(p);
+                else return p;
+            }
+               },
+               
+               /*
+                       factory method to prepare a new endpoint.  this should always be used instead of creating Endpoints
+                       manually, since this method attaches event listeners and an id.
+               */
+               _newEndpoint = function(params) {
+                               var endpointFunc = _currentInstance.Defaults.EndpointType || jsPlumb.Endpoint;
+                               var _p = jsPlumb.extend({}, params);                            
+                               _p.parent = _getParentFromParams(_p);
+                               _p._jsPlumb = _currentInstance;
+                _p.newConnection = _newConnection;
+                _p.newEndpoint = _newEndpoint;                
+                _p.endpointsByUUID = endpointsByUUID;             
+                _p.endpointsByElement = endpointsByElement;  
+                _p.finaliseConnection = _finaliseConnection;
+                _p.fireDetachEvent = fireDetachEvent;
+                _p.fireMoveEvent = fireMoveEvent;
+                _p.floatingConnections = floatingConnections;
+                _p.getParentFromParams = _getParentFromParams;
+                _p.elementId = _getId(_p.source);                
+                               var ep = new endpointFunc(_p);                  
+                               ep.id = "ep_" + _idstamp();
+                               _eventFireProxy("click", "endpointClick", ep);
+                               _eventFireProxy("dblclick", "endpointDblClick", ep);
+                               _eventFireProxy("contextmenu", "contextmenu", ep);
+                               if (!jsPlumbAdapter.headless)
+                                       _currentInstance.dragManager.endpointAdded(_p.source);
+                       return ep;
+               },
+               
+               /*
+                * performs the given function operation on all the connections found
+                * for the given element id; this means we find all the endpoints for
+                * the given element, and then for each endpoint find the connectors
+                * connected to it. then we pass each connection in to the given
+                * function.             
+                */
+               _operation = function(elId, func, endpointFunc) {
+                       var endpoints = endpointsByElement[elId];
+                       if (endpoints && endpoints.length) {
+                               for ( var i = 0, ii = endpoints.length; i < ii; i++) {
+                                       for ( var j = 0, jj = endpoints[i].connections.length; j < jj; j++) {
+                                               var retVal = func(endpoints[i].connections[j]);
+                                               // if the function passed in returns true, we exit.
+                                               // most functions return false.
+                                               if (retVal) return;
+                                       }
+                                       if (endpointFunc) endpointFunc(endpoints[i]);
+                               }
+                       }
+               },      
+               
+               _setDraggable = function(element, draggable) {
+                       return _elementProxy(element, function(el, id) {
+                               draggableStates[id] = draggable;
+                               if (jsPlumb.CurrentLibrary.isDragSupported(el)) {
+                                       jsPlumb.CurrentLibrary.setDraggable(el, draggable);
+                               }
+                       });
+               },
+               /*
+                * private method to do the business of hiding/showing.
+                * 
+                * @param el
+                *            either Id of the element in question or a library specific
+                *            object for the element.
+                * @param state
+                *            String specifying a value for the css 'display' property
+                *            ('block' or 'none').
+                */
+               _setVisible = function(el, state, alsoChangeEndpoints) {
+                       state = state === "block";
+                       var endpointFunc = null;
+                       if (alsoChangeEndpoints) {
+                               if (state) endpointFunc = function(ep) {
+                                       ep.setVisible(true, true, true);
+                               };
+                               else endpointFunc = function(ep) {
+                                       ep.setVisible(false, true, true);
+                               };
+                       }
+                       var info = _info(el);
+                       _operation(info.id, function(jpc) {
+                               if (state && alsoChangeEndpoints) {             
+                                       // this test is necessary because this functionality is new, and i wanted to maintain backwards compatibility.
+                                       // this block will only set a connection to be visible if the other endpoint in the connection is also visible.
+                                       var oidx = jpc.sourceId === info.id ? 1 : 0;
+                                       if (jpc.endpoints[oidx].isVisible()) jpc.setVisible(true);
+                               }
+                               else  // the default behaviour for show, and what always happens for hide, is to just set the visibility without getting clever.
+                                       jpc.setVisible(state);
+                       }, endpointFunc);
+               },
+               /*
+                * toggles the draggable state of the given element(s).
+                * el is either an id, or an element object, or a list of ids/element objects.
+                */
+               _toggleDraggable = function(el) {
+                       return _elementProxy(el, function(el, elId) {
+                               var state = draggableStates[elId] == null ? false : draggableStates[elId];
+                               state = !state;
+                               draggableStates[elId] = state;
+                               jsPlumb.CurrentLibrary.setDraggable(el, state);
+                               return state;
+                       });
+               },
+               /**
+                * private method to do the business of toggling hiding/showing.
+                */
+               _toggleVisible = function(elId, changeEndpoints) {
+                       var endpointFunc = null;
+                       if (changeEndpoints) {
+                               endpointFunc = function(ep) {
+                                       var state = ep.isVisible();
+                                       ep.setVisible(!state);
+                               };
+                       }
+                       _operation(elId, function(jpc) {
+                               var state = jpc.isVisible();
+                               jpc.setVisible(!state);                         
+                       }, endpointFunc);
+                       // todo this should call _elementProxy, and pass in the
+                       // _operation(elId, f) call as a function. cos _toggleDraggable does
+                       // that.
+               },
+               /**
+                * updates the offset and size for a given element, and stores the
+                * values. if 'offset' is not null we use that (it would have been
+                * passed in from a drag call) because it's faster; but if it is null,
+                * or if 'recalc' is true in order to force a recalculation, we get the current values.
+                */
+               _updateOffset = function(params) {
+                       var timestamp = params.timestamp, recalc = params.recalc, offset = params.offset, elId = params.elId, s;
+                       if (_suspendDrawing && !timestamp) timestamp = _suspendedAt;
+                       if (!recalc) {
+                               if (timestamp && timestamp === offsetTimestamps[elId]) {                        
+                                       return {o:params.offset || offsets[elId], s:sizes[elId]};
+                               }
+                       }                       
+                       if (recalc || !offset) { // if forced repaint or no offset available, we recalculate.
+                               // get the current size and offset, and store them
+                               s = _gel(elId);
+                               if (s != null) {                                                
+                                       sizes[elId] = _getSize(s);
+                                       offsets[elId] = _getOffset(s, _currentInstance);
+                                       offsetTimestamps[elId] = timestamp;
+                               }
+                       } else {
+                               offsets[elId] = offset;
+                if (sizes[elId] == null) {
+                    s = _gel(elId);
+                    if (s != null) sizes[elId] = _getSize(s);
+                }
+                offsetTimestamps[elId] = timestamp;
+            }
+                       
+                       if(offsets[elId] && !offsets[elId].right) {
+                               offsets[elId].right = offsets[elId].left + sizes[elId][0];
+                               offsets[elId].bottom = offsets[elId].top + sizes[elId][1];      
+                               offsets[elId].width = sizes[elId][0];
+                               offsets[elId].height = sizes[elId][1];  
+                               offsets[elId].centerx = offsets[elId].left + (offsets[elId].width / 2);
+                               offsets[elId].centery = offsets[elId].top + (offsets[elId].height / 2);                         
+                       }
+                       return {o:offsets[elId], s:sizes[elId]};
+               },
+
+               // TODO comparison performance
+               _getCachedData = function(elId) {
+                       var o = offsets[elId];
+                       if (!o) 
+                return _updateOffset({elId:elId});
+                       else
+                return {o:o, s:sizes[elId]};
+               },
+
+               /**
+                * gets an id for the given element, creating and setting one if
+                * necessary.  the id is of the form
+                *
+                *      jsPlumb_<instance index>_<index in instance>
+                *
+                * where "index in instance" is a monotonically increasing integer that starts at 0,
+                * for each instance.  this method is used not only to assign ids to elements that do not
+                * have them but also to connections and endpoints.
+                */
+               _getId = function(element, uuid, doNotCreateIfNotFound) {
+                       if (jsPlumbUtil.isString(element)) return element;                      
+                       if (element == null) return null;                       
+                       var id = jsPlumbAdapter.getAttribute(element, "id");
+                       if (!id || id === "undefined") {
+                               // check if fixed uuid parameter is given
+                               if (arguments.length == 2 && arguments[1] !== undefined)
+                                       id = uuid;
+                               else if (arguments.length == 1 || (arguments.length == 3 && !arguments[2]))
+                                       id = "jsPlumb_" + _instanceIndex + "_" + _idstamp();
+                               
+                if (!doNotCreateIfNotFound) jsPlumbAdapter.setAttribute(element, "id", id);
+                       }
+                       return id;
+               };
+
+               this.setConnectionBeingDragged = function(v) {
+                       connectionBeingDragged = v;
+               };
+               this.isConnectionBeingDragged = function() {
+                       return connectionBeingDragged;
+               };
+    
+               this.connectorClass = "_jsPlumb_connector";                     
+               this.hoverClass = "_jsPlumb_hover";                     
+               this.endpointClass = "_jsPlumb_endpoint";               
+               this.endpointConnectedClass = "_jsPlumb_endpoint_connected";            
+               this.endpointFullClass = "_jsPlumb_endpoint_full";              
+               this.endpointDropAllowedClass = "_jsPlumb_endpoint_drop_allowed";               
+               this.endpointDropForbiddenClass = "_jsPlumb_endpoint_drop_forbidden";           
+               this.overlayClass = "_jsPlumb_overlay";                         
+               this.draggingClass = "_jsPlumb_dragging";               
+               this.elementDraggingClass = "_jsPlumb_element_dragging";                        
+               this.sourceElementDraggingClass = "_jsPlumb_source_element_dragging";
+               this.targetElementDraggingClass = "_jsPlumb_target_element_dragging";
+               this.endpointAnchorClassPrefix = "_jsPlumb_endpoint_anchor";
+               this.hoverSourceClass = "_jsPlumb_source_hover";        
+               this.hoverTargetClass = "_jsPlumb_target_hover";
+               this.dragSelectClass = "_jsPlumb_drag_select";
+
+               this.Anchors = {};              
+               this.Connectors = {  "canvas":{}, "svg":{}, "vml":{} };                         
+               this.Endpoints = { "canvas":{}, "svg":{}, "vml":{} };
+               this.Overlays = { "canvas":{}, "svg":{}, "vml":{}};             
+               this.ConnectorRenderers = {};                           
+               this.SVG = "svg";
+               this.CANVAS = "canvas";         
+               this.VML = "vml";
+                               
+
+// --------------------------- jsPLumbInstance public API ---------------------------------------------------------
+                                       
+               
+               this.addEndpoint = function(el, params, referenceParams) {
+                       referenceParams = referenceParams || {};
+                       var p = jsPlumb.extend({}, referenceParams);
+                       jsPlumb.extend(p, params);
+                       p.endpoint = p.endpoint || _currentInstance.Defaults.Endpoint || jsPlumb.Defaults.Endpoint;
+                       p.paintStyle = p.paintStyle || _currentInstance.Defaults.EndpointStyle || jsPlumb.Defaults.EndpointStyle;
+            // YUI wrapper
+                       el = _convertYUICollection(el);                                                 
+
+                       var results = [], 
+                               inputs = (_ju.isArray(el) || (el.length != null && !_ju.isString(el))) ? el : [ el ];
+                                               
+                       for (var i = 0, j = inputs.length; i < j; i++) {
+                               var _el = _dom(inputs[i]), id = _getId(_el);
+                               p.source = _el;
+
+                _updateOffset({ elId : id, timestamp:_suspendedAt });
+                               var e = _newEndpoint(p);
+                               if (p.parentAnchor) e.parentAnchor = p.parentAnchor;
+                               _ju.addToList(endpointsByElement, id, e);
+                               var myOffset = offsets[id], 
+                                       myWH = sizes[id],
+                                       anchorLoc = e.anchor.compute( { xy : [ myOffset.left, myOffset.top ], wh : myWH, element : e, timestamp:_suspendedAt }),
+                                       endpointPaintParams = { anchorLoc : anchorLoc, timestamp:_suspendedAt };
+                               
+                               if (_suspendDrawing) endpointPaintParams.recalc = false;
+                               if (!_suspendDrawing) e.paint(endpointPaintParams);
+                               
+                               results.push(e);
+                               e._doNotDeleteOnDetach = true; // mark this as being added via addEndpoint.                             
+                       }
+                       
+                       return results.length == 1 ? results[0] : results;
+               };
+               
+               
+               this.addEndpoints = function(el, endpoints, referenceParams) {
+                       var results = [];
+                       for ( var i = 0, j = endpoints.length; i < j; i++) {
+                               var e = _currentInstance.addEndpoint(el, endpoints[i], referenceParams);
+                               if (_ju.isArray(e))
+                                       Array.prototype.push.apply(results, e);
+                               else results.push(e);
+                       }
+                       return results;
+               };
+               
+               this.animate = function(el, properties, options) {
+                       options = options || {};
+                       var ele = _gel(el), 
+                               id = _getId(el),
+                               stepFunction = jsPlumb.CurrentLibrary.dragEvents.step,
+                               completeFunction = jsPlumb.CurrentLibrary.dragEvents.complete;
+
+                       options[stepFunction] = _ju.wrap(options[stepFunction], function() {
+                               _currentInstance.repaint(id);
+                       });
+
+                       // onComplete repaints, just to make sure everything looks good at the end of the animation.
+                       options[completeFunction] = _ju.wrap(options[completeFunction], function() {
+                               _currentInstance.repaint(id);
+                       });
+
+                       jsPlumb.CurrentLibrary.animate(ele, properties, options);
+               };              
+               
+               /**
+               * checks for a listener for the given condition, executing it if found, passing in the given value.
+               * condition listeners would have been attached using "bind" (which is, you could argue, now overloaded, since
+               * firing click events etc is a bit different to what this does).  i thought about adding a "bindCondition"
+               * or something, but decided against it, for the sake of simplicity. jsPlumb will never fire one of these
+               * condition events anyway.
+               */
+               this.checkCondition = function(conditionName, value) {
+                       var l = _currentInstance.getListener(conditionName),
+                               r = true;
+                               
+                       if (l && l.length > 0) {
+                               try {
+                                       for (var i = 0, j = l.length; i < j; i++) {
+                                               r = r && l[i](value); 
+                                       }
+                               }
+                               catch (e) { 
+                                       _ju.log(_currentInstance, "cannot check condition [" + conditionName + "]" + e); 
+                               }
+                       }
+                       return r;
+               };
+               
+               /**
+                * checks a condition asynchronously: fires the event handler and passes the handler
+                * a 'proceed' function and a 'stop' function. The handler MUST execute one or other
+                * of these once it has made up its mind.
+                *
+                * Note that although this reads the listener list for the given condition, it
+                * does not loop through and hit each listener, because that, with asynchronous
+                * callbacks, would be messy. so it uses only the first listener registered.
+                */ 
+               this.checkASyncCondition = function(conditionName, value, proceed, stop) {
+                       var l = _currentInstance.getListener(conditionName);
+                               
+                       if (l && l.length > 0) {
+                               try {
+                                       l[0](value, proceed, stop);                                     
+                               }
+                               catch (e) { 
+                                       _ju.log(_currentInstance, "cannot asynchronously check condition [" + conditionName + "]" + e); 
+                               }
+                       }       
+               };
+
+               
+               this.connect = function(params, referenceParams) {
+                       // prepare a final set of parameters to create connection with
+                       var _p = _prepareConnectionParams(params, referenceParams), jpc;
+                       // TODO probably a nicer return value if the connection was not made.  _prepareConnectionParams
+                       // will return null (and log something) if either endpoint was full.  what would be nicer is to 
+                       // create a dedicated 'error' object.
+                       if (_p) {
+                               // create the connection.  it is not yet registered 
+                               jpc = _newConnection(_p);
+                               // now add it the model, fire an event, and redraw
+                               _finaliseConnection(jpc, _p);                                                                           
+                       }
+                       return jpc;
+               };              
+               
+               this.deleteEndpoint = function(object, doNotRepaintAfterwards) {
+                       var _is = _currentInstance.setSuspendDrawing(true);
+                       var endpoint = (typeof object == "string") ? endpointsByUUID[object] : object;                  
+                       if (endpoint) {         
+                               _currentInstance.deleteObject({
+                                       endpoint:endpoint
+                               });
+                       }
+                       if(!_is) _currentInstance.setSuspendDrawing(false, doNotRepaintAfterwards);
+                       return _currentInstance;                                                                        
+               };              
+               
+               this.deleteEveryEndpoint = function() {
+                       var _is = _currentInstance.setSuspendDrawing(true);
+                       for ( var id in endpointsByElement) {
+                               var endpoints = endpointsByElement[id];
+                               if (endpoints && endpoints.length) {
+                                       for ( var i = 0, j = endpoints.length; i < j; i++) {
+                                               _currentInstance.deleteEndpoint(endpoints[i], true);
+                                       }
+                               }
+                       }                       
+                       endpointsByElement = {};                        
+                       endpointsByUUID = {};
+                       _currentInstance.anchorManager.reset();
+                       _currentInstance.dragManager.reset();                                                   
+                       if(!_is) _currentInstance.setSuspendDrawing(false);
+                       return _currentInstance;
+               };
+
+               var fireDetachEvent = function(jpc, doFireEvent, originalEvent) {
+            // may have been given a connection, or in special cases, an object
+            var connType =  _currentInstance.Defaults.ConnectionType || _currentInstance.getDefaultConnectionType(),
+                argIsConnection = jpc.constructor == connType,
+                params = argIsConnection ? {
+                    connection:jpc,
+                                   source : jpc.source, target : jpc.target,
+                                   sourceId : jpc.sourceId, targetId : jpc.targetId,
+                                   sourceEndpoint : jpc.endpoints[0], targetEndpoint : jpc.endpoints[1]
+                } : jpc;
+
+                       if (doFireEvent)
+                               _currentInstance.fire("connectionDetached", params, originalEvent);
+                       
+            _currentInstance.anchorManager.connectionDetached(params);
+               };      
+
+               var fireMoveEvent = function(params, evt) {
+                       _currentInstance.fire("connectionMoved", params, evt);
+               };
+
+               this.unregisterEndpoint = function(endpoint) {
+                       if (endpoint._jsPlumb.uuid) endpointsByUUID[endpoint._jsPlumb.uuid] = null;                             
+                       _currentInstance.anchorManager.deleteEndpoint(endpoint);                        
+                       // TODO at least replace this with a removeWithFunction call.                   
+                       for (var e in endpointsByElement) {
+                               var endpoints = endpointsByElement[e];
+                               if (endpoints) {
+                                       var newEndpoints = [];
+                                       for (var i = 0, j = endpoints.length; i < j; i++)
+                                               if (endpoints[i] != endpoint) newEndpoints.push(endpoints[i]);
+                                       
+                                       endpointsByElement[e] = newEndpoints;
+                               }
+                               if(endpointsByElement[e].length <1){
+                                       delete endpointsByElement[e];
+                               }
+                       }
+               };
+                               
+               this.detach = function() {
+
+            if (arguments.length === 0) return;
+            var connType =  _currentInstance.Defaults.ConnectionType || _currentInstance.getDefaultConnectionType(),
+                firstArgIsConnection = arguments[0].constructor == connType,
+                params = arguments.length == 2 ? firstArgIsConnection ? (arguments[1] || {}) : arguments[0] : arguments[0],
+                fireEvent = (params.fireEvent !== false),
+                forceDetach = params.forceDetach,
+                conn = firstArgIsConnection ? arguments[0] : params.connection;
+                                                    
+                               if (conn) {             
+                    if (forceDetach || jsPlumbUtil.functionChain(true, false, [
+                            [ conn.endpoints[0], "isDetachAllowed", [ conn ] ],    
+                            [ conn.endpoints[1], "isDetachAllowed", [ conn ] ],
+                            [ conn, "isDetachAllowed", [ conn ] ],
+                            [ _currentInstance, "checkCondition", [ "beforeDetach", conn ] ] ])) {
+                        
+                        conn.endpoints[0].detach(conn, false, true, fireEvent); 
+                    }
+                }
+                else {
+                                       var _p = jsPlumb.extend( {}, params); // a backwards compatibility hack: source should be thought of as 'params' in this case.
+                                       // test for endpoint uuids to detach
+                                       if (_p.uuids) {
+                                               _getEndpoint(_p.uuids[0]).detachFrom(_getEndpoint(_p.uuids[1]), fireEvent);
+                                       } else if (_p.sourceEndpoint && _p.targetEndpoint) {
+                                               _p.sourceEndpoint.detachFrom(_p.targetEndpoint);
+                                       } else {
+                                               var sourceId = _getId(_dom(_p.source)),
+                                                   targetId = _getId(_dom(_p.target));
+                                               _operation(sourceId, function(jpc) {
+                                                   if ((jpc.sourceId == sourceId && jpc.targetId == targetId) || (jpc.targetId == sourceId && jpc.sourceId == targetId)) {
+                                                           if (_currentInstance.checkCondition("beforeDetach", jpc)) {
+                                    jpc.endpoints[0].detach(jpc, false, true, fireEvent);
+                                                               }
+                                                       }
+                                               });
+                                       }
+                               }
+               };
+
+               this.detachAllConnections = function(el, params) {
+            params = params || {};
+            el = _dom(el);
+                       var id = _getId(el),
+                endpoints = endpointsByElement[id];
+                       if (endpoints && endpoints.length) {
+                               for ( var i = 0, j = endpoints.length; i < j; i++) {
+                                       endpoints[i].detachAll(params.fireEvent !== false);
+                               }
+                       }
+                       return _currentInstance;
+               };
+
+               this.detachEveryConnection = function(params) {
+            params = params || {};
+            _currentInstance.doWhileSuspended(function() {
+                               for ( var id in endpointsByElement) {
+                                       var endpoints = endpointsByElement[id];
+                                       if (endpoints && endpoints.length) {
+                                               for ( var i = 0, j = endpoints.length; i < j; i++) {
+                                                       endpoints[i].detachAll(params.fireEvent !== false);
+                                               }
+                                       }
+                               }
+                               connections.splice(0);
+                       });
+                       return _currentInstance;
+               };
+
+               /// not public.  but of course its exposed. how to change this.
+               this.deleteObject = function(params) {
+                       var result = {
+                                       endpoints : {}, 
+                                       connections : {},
+                                       endpointCount:0,
+                                       connectionCount:0
+                               },
+                               fireEvent = params.fireEvent !== false,
+                               deleteAttachedObjects = params.deleteAttachedObjects !== false;
+
+                       var unravelConnection = function(connection) {
+                               if(connection != null && result.connections[connection.id] == null) {
+                                       if (connection._jsPlumb != null) connection.setHover(false);
+                                       result.connections[connection.id] = connection;
+                                       result.connectionCount++;
+                                       if (deleteAttachedObjects) {
+                                               for (var j = 0; j < connection.endpoints.length; j++) {
+                                                       if (connection.endpoints[j]._deleteOnDetach)
+                                                               unravelEndpoint(connection.endpoints[j]);
+                                               }
+                                       }                                       
+                               }
+                       };
+                       var unravelEndpoint = function(endpoint) {
+                               if(endpoint != null && result.endpoints[endpoint.id] == null) {
+                                       if (endpoint._jsPlumb != null) endpoint.setHover(false);
+                                       result.endpoints[endpoint.id] = endpoint;
+                                       result.endpointCount++;
+
+                                       if (deleteAttachedObjects) {
+                                               for (var i = 0; i < endpoint.connections.length; i++) {
+                                                       var c = endpoint.connections[i];
+                                                       unravelConnection(c);                                           
+                                               }
+                                       }
+                               }
+                       };
+
+                       if (params.connection) 
+                               unravelConnection(params.connection);
+                       else unravelEndpoint(params.endpoint);
+
+                       // loop through connections
+                       for (var i in result.connections) {
+                               var c = result.connections[i];
+                               c.endpoints[0].detachFromConnection(c);
+                               c.endpoints[1].detachFromConnection(c);
+                               //_currentInstance.unregisterConnection(c);
+                               jsPlumbUtil.removeWithFunction(connections, function(_c) {
+                                   return c.id == _c.id;
+                               });
+                               fireDetachEvent(c, fireEvent, params.originalEvent);
+                               c.cleanup();
+                               c.destroy();
+                       }
+
+                       // loop through endpoints
+                       for (var j in result.endpoints) {
+                               var e = result.endpoints[j];    
+                               _currentInstance.unregisterEndpoint(e);
+                               // FIRE some endpoint deleted event?
+                               e.cleanup();
+                               e.destroy();
+                       }       
+
+                       return result;
+               };
+               this.draggable = function(el, options) {
+                       var i,j,ele;
+                       // allows for array or jquery/mootools selector
+                       if (typeof el == 'object' && el.length) {
+                               for (i = 0, j = el.length; i < j; i++) {
+                                       ele = _dom(el[i]);
+                                       if (ele) _initDraggableIfNecessary(ele, true, options);
+                               }
+                       } 
+                       // allows for YUI selector
+                       else if (el._nodes) {   // TODO this is YUI specific; really the logic should be forced
+                               // into the library adapters (for jquery and mootools aswell)
+                               for (i = 0, j = el._nodes.length; i < j; i++) {
+                                       ele = _dom(el._nodes[i]);
+                                       if (ele) _initDraggableIfNecessary(ele, true, options);
+                               }
+                       }
+                       else {                          
+                               ele = _dom(el);
+                               if (ele) _initDraggableIfNecessary(ele, true, options);
+                       }
+                       return _currentInstance;
+               };
+
+
+               // just a library-agnostic wrapper.
+               this.extend = function(o1, o2) {
+                       return jsPlumb.CurrentLibrary.extend(o1, o2);
+               };
+
+               // helpers for select/selectEndpoints
+               var _setOperation = function(list, func, args, selector) {
+                               for (var i = 0, j = list.length; i < j; i++) {
+                                       list[i][func].apply(list[i], args);
+                               }       
+                               return selector(list);
+                       },
+                       _getOperation = function(list, func, args) {
+                               var out = [];
+                               for (var i = 0, j = list.length; i < j; i++) {                                  
+                                       out.push([ list[i][func].apply(list[i], args), list[i] ]);
+                               }       
+                               return out;
+                       },
+                       setter = function(list, func, selector) {
+                               return function() {
+                                       return _setOperation(list, func, arguments, selector);
+                               };
+                       },
+                       getter = function(list, func) {
+                               return function() {
+                                       return _getOperation(list, func, arguments);
+                               };      
+                       },
+                       prepareList = function(input, doNotGetIds) {
+                               var r = [];
+                               if (input) {
+                                       if (typeof input == 'string') {
+                                               if (input === "*") return input;
+                                               r.push(input);
+                                       }
+                                       else {
+                                               input = _gel(input);
+                                               if (doNotGetIds) r = input;
+                                               else { 
+                                                       for (var i = 0, j = input.length; i < j; i++) 
+                                                               r.push(_info(input[i]).id);
+                                               }       
+                                       }
+                               }
+                               return r;
+                       },
+                       filterList = function(list, value, missingIsFalse) {
+                               if (list === "*") return true;
+                               return list.length > 0 ? jsPlumbUtil.indexOf(list, value) != -1 : !missingIsFalse;
+                       };
+
+               // get some connections, specifying source/target/scope
+               this.getConnections = function(options, flat) {
+                       if (!options) {
+                               options = {};
+                       } else if (options.constructor == String) {
+                               options = { "scope": options };
+                       }
+                       var scope = options.scope || _currentInstance.getDefaultScope(),
+                               scopes = prepareList(scope, true),
+                               sources = prepareList(options.source),
+                               targets = prepareList(options.target),                  
+                               results = (!flat && scopes.length > 1) ? {} : [],
+                               _addOne = function(scope, obj) {
+                                       if (!flat && scopes.length > 1) {
+                                               var ss = results[scope];
+                                               if (ss == null) {
+                                                       ss = results[scope] = [];
+                                               }
+                                               ss.push(obj);
+                                       } else results.push(obj);
+                               };
+                       
+                       for ( var j = 0, jj = connections.length; j < jj; j++) {
+                               var c = connections[j];
+                               if (filterList(scopes, c.scope) && filterList(sources, c.sourceId) && filterList(targets, c.targetId))
+                                       _addOne(c.scope, c);
+                       }
+                       
+                       return results;
+               };
+               
+               var _curryEach = function(list, executor) {
+                               return function(f) {
+                                       for (var i = 0, ii = list.length; i < ii; i++) {
+                                               f(list[i]);
+                                       }
+                                       return executor(list);
+                               };              
+                       },
+                       _curryGet = function(list) {
+                               return function(idx) {
+                                       return list[idx];
+                               };
+                       };
+                       
+               var _makeCommonSelectHandler = function(list, executor) {
+            var out = {
+                    length:list.length,
+                                   each:_curryEach(list, executor),
+                                   get:_curryGet(list)
+                },
+                setters = ["setHover", "removeAllOverlays", "setLabel", "addClass", "addOverlay", "removeOverlay", 
+                           "removeOverlays", "showOverlay", "hideOverlay", "showOverlays", "hideOverlays", "setPaintStyle",
+                           "setHoverPaintStyle", "setSuspendEvents", "setParameter", "setParameters", "setVisible", 
+                           "repaint", "addType", "toggleType", "removeType", "removeClass", "setType", "bind", "unbind" ],
+                
+                getters = ["getLabel", "getOverlay", "isHover", "getParameter", "getParameters", "getPaintStyle",
+                           "getHoverPaintStyle", "isVisible", "hasType", "getType", "isSuspendEvents" ],
+                i, ii;
+            
+            for (i = 0, ii = setters.length; i < ii; i++)
+                out[setters[i]] = setter(list, setters[i], executor);
+            
+            for (i = 0, ii = getters.length; i < ii; i++)
+                out[getters[i]] = getter(list, getters[i]);       
+            
+            return out;
+               };
+               
+               var     _makeConnectionSelectHandler = function(list) {
+                       var common = _makeCommonSelectHandler(list, _makeConnectionSelectHandler);
+                       return jsPlumb.CurrentLibrary.extend(common, {
+                               // setters                                                                      
+                               setDetachable:setter(list, "setDetachable", _makeConnectionSelectHandler),
+                               setReattach:setter(list, "setReattach", _makeConnectionSelectHandler),
+                               setConnector:setter(list, "setConnector", _makeConnectionSelectHandler),                        
+                               detach:function() {
+                                       for (var i = 0, ii = list.length; i < ii; i++)
+                                               _currentInstance.detach(list[i]);
+                               },                              
+                               // getters
+                               isDetachable:getter(list, "isDetachable"),
+                               isReattach:getter(list, "isReattach")
+                       });
+               };
+               
+               var     _makeEndpointSelectHandler = function(list) {
+                       var common = _makeCommonSelectHandler(list, _makeEndpointSelectHandler);
+                       return jsPlumb.CurrentLibrary.extend(common, {
+                               setEnabled:setter(list, "setEnabled", _makeEndpointSelectHandler),                              
+                               setAnchor:setter(list, "setAnchor", _makeEndpointSelectHandler),
+                               isEnabled:getter(list, "isEnabled"),
+                               detachAll:function() {
+                                       for (var i = 0, ii = list.length; i < ii; i++)
+                                               list[i].detachAll();
+                               },
+                               "remove":function() {
+                                       for (var i = 0, ii = list.length; i < ii; i++)
+                                               _currentInstance.deleteObject({endpoint:list[i]});
+                               }
+                       });
+               };
+                       
+
+               this.select = function(params) {
+                       params = params || {};
+                       params.scope = params.scope || "*";
+                       return _makeConnectionSelectHandler(params.connections || _currentInstance.getConnections(params, true));                                                       
+               };              
+
+               this.selectEndpoints = function(params) {
+                       params = params || {};
+                       params.scope = params.scope || "*";
+                       var noElementFilters = !params.element && !params.source && !params.target,                     
+                               elements = noElementFilters ? "*" : prepareList(params.element),
+                               sources = noElementFilters ? "*" : prepareList(params.source),
+                               targets = noElementFilters ? "*" : prepareList(params.target),
+                               scopes = prepareList(params.scope, true);
+                       
+                       var ep = [];
+                       
+                       for (var el in endpointsByElement) {
+                               var either = filterList(elements, el, true),
+                                       source = filterList(sources, el, true),
+                                       sourceMatchExact = sources != "*",
+                                       target = filterList(targets, el, true),
+                                       targetMatchExact = targets != "*"; 
+                                       
+                               // if they requested 'either' then just match scope. otherwise if they requested 'source' (not as a wildcard) then we have to match only endpoints that have isSource set to to true, and the same thing with isTarget.  
+                               if ( either || source  || target ) {
+                                       inner:
+                                       for (var i = 0, ii = endpointsByElement[el].length; i < ii; i++) {
+                                               var _ep = endpointsByElement[el][i];
+                                               if (filterList(scopes, _ep.scope, true)) {
+                                               
+                                                       var noMatchSource = (sourceMatchExact && sources.length > 0 && !_ep.isSource),
+                                                               noMatchTarget = (targetMatchExact && targets.length > 0 && !_ep.isTarget);
+                                               
+                                                       if (noMatchSource || noMatchTarget)                                                               
+                                                                 continue inner; 
+                                                                                                               
+                                                       ep.push(_ep);           
+                                               }
+                                       }
+                               }                                       
+                       }
+                       
+                       return _makeEndpointSelectHandler(ep);
+               };
+
+               // get all connections managed by the instance of jsplumb.
+               this.getAllConnections = function() { return connections; };
+               this.getDefaultScope = function() { return DEFAULT_SCOPE; };
+               // get an endpoint by uuid.
+               this.getEndpoint = _getEndpoint;                                
+               // get endpoints for some element.
+               this.getEndpoints = function(el) { return endpointsByElement[_info(el).id]; };          
+               // gets the default endpoint type. used when subclassing. see wiki.
+               this.getDefaultEndpointType = function() { return jsPlumb.Endpoint; };          
+               // gets the default connection type. used when subclassing.  see wiki.
+               this.getDefaultConnectionType = function() { return jsPlumb.Connection; };
+               /*
+                * Gets an element's id, creating one if necessary. really only exposed
+                * for the lib-specific functionality to access; would be better to pass
+                * the current instance into the lib-specific code (even though this is
+                * a static call. i just don't want to expose it to the public API).
+                */
+               this.getId = _getId;
+               this.getOffset = function(id) { 
+                       var o = offsets[id]; 
+                       return _updateOffset({elId:id});
+               };
+
+               this.getSelector = function() {
+                       return jsPlumb.CurrentLibrary.getSelector.apply(null, arguments);
+               };
+               
+               // get the size of the element with the given id, perhaps from cache.
+               this.getSize = function(id) { 
+                       var s = sizes[id]; 
+                       if (!s) _updateOffset({elId:id});
+                       return sizes[id];
+               };              
+               
+               this.appendElement = _appendElement;
+               
+               var _hoverSuspended = false;
+               this.isHoverSuspended = function() { return _hoverSuspended; };
+               this.setHoverSuspended = function(s) { _hoverSuspended = s; };
+
+               var _isAvailable = function(m) {
+                       return function() {
+                               return jsPlumbAdapter.isRenderModeAvailable(m);
+                       };
+               };
+               this.isCanvasAvailable = _isAvailable("canvas");
+               this.isSVGAvailable = _isAvailable("svg");
+               this.isVMLAvailable = _isAvailable("vml");
+
+               // set an element's connections to be hidden
+               this.hide = function(el, changeEndpoints) {
+                       _setVisible(el, "none", changeEndpoints);
+                       return _currentInstance;
+               };
+               
+               // exposed for other objects to use to get a unique id.
+               this.idstamp = _idstamp;
+
+               this.connectorsInitialized = false;
+               var connectorTypes = [], rendererTypes = ["canvas", "svg", "vml"];
+               this.registerConnectorType = function(connector, name) {
+                       connectorTypes.push([connector, name]);
+               };
+               
+               /**
+                * callback from the current library to tell us to prepare ourselves (attach
+                * mouse listeners etc; can't do that until the library has provided a bind method)              
+                */
+               this.init = function() {
+                       var _oneType = function(renderer, name, fn) {
+                               jsPlumb.Connectors[renderer][name] = function() {
+                                       fn.apply(this, arguments);
+                                       jsPlumb.ConnectorRenderers[renderer].apply(this, arguments);            
+                               };
+                               jsPlumbUtil.extend(jsPlumb.Connectors[renderer][name], [ fn, jsPlumb.ConnectorRenderers[renderer]]);
+                       };
+
+                       if (!jsPlumb.connectorsInitialized) {
+                               for (var i = 0; i < connectorTypes.length; i++) {
+                                       for (var j = 0; j < rendererTypes.length; j++) {
+                                               _oneType(rendererTypes[j], connectorTypes[i][1], connectorTypes[i][0]);                                                                                         
+                                       }
+
+                               }
+                               jsPlumb.connectorsInitialized = true;
+                       }
+                       
+                       if (!initialized) {                
+                _currentInstance.anchorManager = new jsPlumb.AnchorManager({jsPlumbInstance:_currentInstance});                
+                               _currentInstance.setRenderMode(_currentInstance.Defaults.RenderMode);  // calling the method forces the capability logic to be run.                                                                                                             
+                               initialized = true;
+                               _currentInstance.fire("ready", _currentInstance);
+                       }
+               }.bind(this);           
+               
+               this.log = log;
+               this.jsPlumbUIComponent = jsPlumbUIComponent;           
+
+               /*
+                * Creates an anchor with the given params.
+                * 
+                * 
+                * Returns: The newly created Anchor.
+                * Throws: an error if a named anchor was not found.
+                */
+               this.makeAnchor = function() {
+                       var pp, _a = function(t, p) {
+                               if (jsPlumb.Anchors[t]) return new jsPlumb.Anchors[t](p);
+                               if (!_currentInstance.Defaults.DoNotThrowErrors)
+                                       throw { msg:"jsPlumb: unknown anchor type '" + t + "'" };
+                       };
+                       if (arguments.length === 0) return null;
+                       var specimen = arguments[0], elementId = arguments[1], jsPlumbInstance = arguments[2], newAnchor = null;                        
+                       // if it appears to be an anchor already...
+                       if (specimen.compute && specimen.getOrientation) return specimen;  //TODO hazy here about whether it should be added or is already added somehow.
+                       // is it the name of an anchor type?
+                       else if (typeof specimen == "string") {
+                               newAnchor = _a(arguments[0], {elementId:elementId, jsPlumbInstance:_currentInstance});
+                       }
+                       // is it an array? it will be one of:
+                       //              an array of [spec, params] - this defines a single anchor, which may be dynamic, but has parameters.
+                       //              an array of arrays - this defines some dynamic anchors
+                       //              an array of numbers - this defines a single anchor.                             
+                       else if (_ju.isArray(specimen)) {
+                               if (_ju.isArray(specimen[0]) || _ju.isString(specimen[0])) {
+                                       // if [spec, params] format
+                                       if (specimen.length == 2 && _ju.isObject(specimen[1])) {
+                                               // if first arg is a string, its a named anchor with params
+                                               if (_ju.isString(specimen[0])) {
+                                                       pp = jsPlumb.extend({elementId:elementId, jsPlumbInstance:_currentInstance}, specimen[1]);
+                                                       newAnchor = _a(specimen[0], pp);
+                                               }
+                                               // otherwise first arg is array, second is params. we treat as a dynamic anchor, which is fine
+                                               // even if the first arg has only one entry. you could argue all anchors should be implicitly dynamic in fact.
+                                               else {
+                                                       pp = jsPlumb.extend({elementId:elementId, jsPlumbInstance:_currentInstance, anchors:specimen[0]}, specimen[1]);
+                                                       newAnchor = new jsPlumb.DynamicAnchor(pp);
+                                               }
+                                       }
+                                       else
+                                               newAnchor = new jsPlumb.DynamicAnchor({anchors:specimen, selector:null, elementId:elementId, jsPlumbInstance:jsPlumbInstance});
+
+                               }
+                               else {
+                                       var anchorParams = {
+                                               x:specimen[0], y:specimen[1],
+                                               orientation : (specimen.length >= 4) ? [ specimen[2], specimen[3] ] : [0,0],
+                                               offsets : (specimen.length >= 6) ? [ specimen[4], specimen[5] ] : [ 0, 0 ],
+                                               elementId:elementId,
+                        jsPlumbInstance:jsPlumbInstance,
+                        cssClass:specimen.length == 7 ? specimen[6] : null
+                                       };                                              
+                                       newAnchor = new jsPlumb.Anchor(anchorParams);
+                                       newAnchor.clone = function() { return new jsPlumb.Anchor(anchorParams); };                                                                                      
+                               }
+                       }
+                       
+                       if (!newAnchor.id) newAnchor.id = "anchor_" + _idstamp();
+                       return newAnchor;
+               };
+
+               /**
+                * makes a list of anchors from the given list of types or coords, eg
+                * ["TopCenter", "RightMiddle", "BottomCenter", [0, 1, -1, -1] ]
+                */
+               this.makeAnchors = function(types, elementId, jsPlumbInstance) {
+                       var r = [];
+                       for ( var i = 0, ii = types.length; i < ii; i++) {
+                               if (typeof types[i] == "string")
+                                       r.push(jsPlumb.Anchors[types[i]]({elementId:elementId, jsPlumbInstance:jsPlumbInstance}));
+                               else if (_ju.isArray(types[i]))
+                                       r.push(_currentInstance.makeAnchor(types[i], elementId, jsPlumbInstance));
+                       }
+                       return r;
+               };
+
+               /**
+                * Makes a dynamic anchor from the given list of anchors (which may be in shorthand notation as strings or dimension arrays, or Anchor
+                * objects themselves) and the given, optional, anchorSelector function (jsPlumb uses a default if this is not provided; most people will
+                * not need to provide this - i think). 
+                */
+               this.makeDynamicAnchor = function(anchors, anchorSelector) {
+                       return new jsPlumb.DynamicAnchor({anchors:anchors, selector:anchorSelector, elementId:null, jsPlumbInstance:_currentInstance});
+               };
+               
+// --------------------- makeSource/makeTarget ---------------------------------------------- 
+               
+               var _targetEndpointDefinitions = {},
+                       _targetEndpoints = {},
+                       _targetEndpointsUnique = {},
+                       _targetMaxConnections = {},
+                       _setEndpointPaintStylesAndAnchor = function(ep, epIndex) {
+                               ep.paintStyle = ep.paintStyle ||
+                                                               _currentInstance.Defaults.EndpointStyles[epIndex] ||
+                                   _currentInstance.Defaults.EndpointStyle ||
+                                   jsPlumb.Defaults.EndpointStyles[epIndex] ||
+                                   jsPlumb.Defaults.EndpointStyle;
+                               ep.hoverPaintStyle = ep.hoverPaintStyle ||
+                                  _currentInstance.Defaults.EndpointHoverStyles[epIndex] ||
+                                  _currentInstance.Defaults.EndpointHoverStyle ||
+                                  jsPlumb.Defaults.EndpointHoverStyles[epIndex] ||
+                                  jsPlumb.Defaults.EndpointHoverStyle;                            
+
+                               ep.anchor = ep.anchor ||
+                               _currentInstance.Defaults.Anchors[epIndex] ||
+                               _currentInstance.Defaults.Anchor ||
+                               jsPlumb.Defaults.Anchors[epIndex] ||
+                               jsPlumb.Defaults.Anchor;                           
+                                       
+                               ep.endpoint = ep.endpoint ||
+                                                         _currentInstance.Defaults.Endpoints[epIndex] ||
+                                                         _currentInstance.Defaults.Endpoint ||
+                                                         jsPlumb.Defaults.Endpoints[epIndex] ||
+                                                         jsPlumb.Defaults.Endpoint;
+                       },
+                       // TODO put all the source stuff inside one parent, keyed by id.
+                       _sourceEndpointDefinitions = {},
+                       _sourceEndpoints = {},
+                       _sourceEndpointsUnique = {},
+                       _sourcesEnabled = {},
+                       _sourceTriggers = {},
+                       _sourceMaxConnections = {},
+                       _targetsEnabled = {},
+                       selectorFilter = function(evt, _el, selector) {             
+                var t = evt.target || evt.srcElement, ok = false, 
+                    sel = _currentInstance.getSelector(_el, selector);
+                for (var j = 0; j < sel.length; j++) {
+                    if (sel[j] == t) {
+                        ok = true;
+                        break;
+                    }
+                }
+                return ok;                 
+               };
+
+               // see API docs
+               this.makeTarget = function(el, params, referenceParams) {                                               
+                       
+                       // put jsplumb ref into params without altering the params passed in
+                       var p = jsPlumb.extend({_jsPlumb:_currentInstance}, referenceParams);
+                       jsPlumb.extend(p, params);
+
+                       // calculate appropriate paint styles and anchor from the params given                  
+                       _setEndpointPaintStylesAndAnchor(p, 1);                               
+
+                       var jpcl = jsPlumb.CurrentLibrary,
+                           targetScope = p.scope || _currentInstance.Defaults.Scope,
+                           deleteEndpointsOnDetach = !(p.deleteEndpointsOnDetach === false),
+                           maxConnections = p.maxConnections || -1,
+                               onMaxConnections = p.onMaxConnections,
+
+                               _doOne = function(el) {
+                                       
+                                       // get the element's id and store the endpoint definition for it.  jsPlumb.connect calls will look for one of these,
+                                       // and use the endpoint definition if found.
+                                       // decode the info for this element (id and element)
+                                       var elInfo = _info(el), 
+                                               elid = elInfo.id,
+                                               proxyComponent = new jsPlumbUIComponent(p),
+                                               dropOptions = jsPlumb.extend({}, p.dropOptions || {});
+
+                                       // store the definitions keyed against the element id.
+                                       _targetEndpointDefinitions[elid] = p;
+                                       _targetEndpointsUnique[elid] = p.uniqueEndpoint;
+                                       _targetMaxConnections[elid] = maxConnections;
+                                       _targetsEnabled[elid] = true;                           
+
+                                       var _drop = function() {
+                                               _currentInstance.currentlyDragging = false;
+                                               var originalEvent = jsPlumb.CurrentLibrary.getDropEvent(arguments),
+                                                       targetCount = _currentInstance.select({target:elid}).length,
+                                                       draggable = _gel(jpcl.getDragObject(arguments)),
+                                                       id = _currentInstance.getAttribute(draggable, "dragId"),                                                                                
+                                                       scope = _currentInstance.getAttribute(draggable, "originalScope"),
+                                                       jpc = floatingConnections[id],
+                                                       idx = jpc.endpoints[0].isFloating() ? 0 : 1,
+                                                       // this is not necessarily correct. if the source is being dragged,
+                                                       // then the source endpoint is actually the currently suspended endpoint.
+                                                       source = jpc.endpoints[0],
+                                                       _endpoint = p.endpoint ? jsPlumb.extend({}, p.endpoint) : {};                                   
+                                                       
+                                               if (!_targetsEnabled[elid] || _targetMaxConnections[elid] > 0 && targetCount >= _targetMaxConnections[elid]){
+                                                       if (onMaxConnections) {
+                                                               // TODO here we still have the id of the floating element, not the
+                                                               // actual target.
+                                                               onMaxConnections({
+                                                                       element:elInfo.el,
+                                                                       connection:jpc
+                                                               }, originalEvent);
+                                                       }
+                                                       return false;
+                                               }
+
+                                               // unlock the source anchor to allow it to refresh its position if necessary
+                                               source.anchor.locked = false;                                   
+                                                                                       
+                                               // restore the original scope if necessary (issue 57)
+                                               if (scope) jpcl.setDragScope(draggable, scope);         
+
+                                               // if no suspendedEndpoint and not pending, it is likely there was a drop on two 
+                                               // elements that are on top of each other. abort.
+                                               if (jpc.suspendedEndpoint == null && !jpc.pending)
+                                                       return false;           
+                                               
+                                               // check if drop is allowed here.                                       
+                                               // if the source is being dragged then in fact
+                                               // the source and target ids to pass into the drop interceptor are
+                                               // source - elid
+                                               // target - jpc's targetId
+                                               // 
+                                               // otherwise the ids are
+                                               // source - jpc.sourceId
+                                               // target - elid
+                                               //
+                                               var _continue = proxyComponent.isDropAllowed(idx === 0 ? elid : jpc.sourceId, idx === 0 ? jpc.targetId : elid, jpc.scope, jpc, null);                                                   
+
+                                               // reinstate any suspended endpoint; this just puts the connection back into
+                                               // a state in which it will report sensible values if someone asks it about
+                                               // its target.  we're going to throw this connection away shortly so it doesnt matter
+                                               // if we manipulate it a bit.
+                                               if (jpc.suspendedEndpoint) {
+                                                       jpc[idx ? "targetId" : "sourceId"] = jpc.suspendedEndpoint.elementId;
+                                                       jpc[idx ? "target" : "source"] = jpc.suspendedEndpoint.element;
+                                                       jpc.endpoints[idx] = jpc.suspendedEndpoint;
+                                               }                                                                                                                                                                                                               
+                                               
+                                               if (_continue) {
+                                                                                                                                       
+                                                       // make a new Endpoint for the target, or get it from the cache if uniqueEndpoint
+                            // is set.
+                                                       var _el = jpcl.getElementObject(elInfo.el),
+                                                               newEndpoint = _targetEndpoints[elid];
+
+                            // if no cached endpoint, or there was one but it has been cleaned up
+                            // (ie. detached), then create a new one.
+                            if (newEndpoint == null || newEndpoint._jsPlumb == null)
+                                newEndpoint = _currentInstance.addEndpoint(_el, p);
+
+                                                       if (p.uniqueEndpoint) _targetEndpoints[elid] = 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 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 = jpcl.getUIPosition(arguments, _currentInstance.getZoom()),
+                                                               elPosition = _getOffset(_el, _currentInstance),
+                                                               elSize = _getSize(_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.
+                                                       }
+                                                       
+                                                       // change the target endpoint and target element information. really this should be 
+                                                       // done on a method on connection
+                                                       jpc[idx ? "target" : "source"] = newEndpoint.element;
+                                                       jpc[idx ? "targetId" : "sourceId"] = newEndpoint.elementId;
+                                                       jpc.endpoints[idx].detachFromConnection(jpc);
+                                                       if (jpc.endpoints[idx]._deleteOnDetach)
+                                                               jpc.endpoints[idx].deleteAfterDragStop = true; // tell this endpoint to delet itself after drag stop.
+                                                       // set new endpoint, and configure the settings for endpoints to delete on detach
+                                                       newEndpoint.addConnection(jpc);
+                                                       jpc.endpoints[idx] = newEndpoint;
+                                                       jpc.deleteEndpointsOnDetach = deleteEndpointsOnDetach;                                          
+
+                                                       // inform the anchor manager to update its target endpoint for this connection.
+                                                       // TODO refactor to make this a single method.
+                                                       if (idx == 1)
+                                                               _currentInstance.anchorManager.updateOtherEndpoint(jpc.sourceId, jpc.suspendedElementId, jpc.targetId, jpc);
+                                                       else
+                                                               _currentInstance.anchorManager.sourceChanged(jpc.suspendedEndpoint.elementId, jpc.sourceId, jpc);
+
+                                                       _finaliseConnection(jpc, null, originalEvent);
+                                                       jpc.pending = false;
+
+                                               }                               
+                                               // if not allowed to drop...
+                                               else {
+                                                       // TODO this code is identical (pretty much) to what happens when a connection
+                                                       // dragged from a normal endpoint is in this situation. refactor.
+                                                       // is this an existing connection, and will we reattach?
+                                                       // TODO also this assumes the source needs to detach - is that always valid?
+                                                       if (jpc.suspendedEndpoint) {                                                    
+                                                               if (jpc.isReattach()) {
+                                                                       jpc.setHover(false);
+                                                                       jpc.floatingAnchorIndex = null;
+                                                                       jpc.suspendedEndpoint.addConnection(jpc);
+                                                                       _currentInstance.repaint(source.elementId);
+                                                               }
+                                                               else
+                                                                       source.detach(jpc, false, true, true, originalEvent);  // otherwise, detach the connection and tell everyone about it.
+                                                       }
+                                                       
+                                               }                                                                                                               
+                                       };
+                                       
+                                       // wrap drop events as needed and initialise droppable
+                                       var dropEvent = jpcl.dragEvents.drop;
+                                       dropOptions.scope = dropOptions.scope || targetScope;
+                                       dropOptions[dropEvent] = _ju.wrap(dropOptions[dropEvent], _drop);                               
+                                       jpcl.initDroppable(_gel(elInfo.el), dropOptions, true);
+                               };
+                       
+                       // YUI collection fix
+                       el = _convertYUICollection(el);                 
+                       // make an array if only given one element
+                       var inputs = el.length && el.constructor != String ? el : [ el ];
+                                               
+                       // register each one in the list.
+                       for (var i = 0, ii = inputs.length; i < ii; i++) {                                                      
+                               _doOne(inputs[i]);
+                       }
+
+                       return _currentInstance;
+               };
+
+               // see api docs
+               this.unmakeTarget = function(el, doNotClearArrays) {
+                       var info = _info(el);
+
+                       jsPlumb.CurrentLibrary.destroyDroppable(info.el);
+                       // TODO this is not an exhaustive unmake of a target, since it does not remove the droppable stuff from
+                       // the element.  the effect will be to prevent it from behaving as a target, but it's not completely purged.
+                       if (!doNotClearArrays) {
+                               delete _targetEndpointDefinitions[info.id];
+                               delete _targetEndpointsUnique[info.id];
+                               delete _targetMaxConnections[info.id];
+                               delete _targetsEnabled[info.id];                
+                       }
+
+                       return _currentInstance;
+               };                                              
+
+           // see api docs
+               this.makeSource = function(el, params, referenceParams) {
+                       var p = jsPlumb.extend({}, referenceParams);
+                       jsPlumb.extend(p, params);
+                       _setEndpointPaintStylesAndAnchor(p, 0);   
+                       var jpcl = jsPlumb.CurrentLibrary,
+                               maxConnections = p.maxConnections || -1,
+                               onMaxConnections = p.onMaxConnections,
+                               _doOne = function(elInfo) {
+                                       // get the element's id and store the endpoint definition for it.  jsPlumb.connect calls will look for one of these,
+                                       // and use the endpoint definition if found.
+                                       var elid = elInfo.id,
+                                               _el = _gel(elInfo.el),
+                                               parentElement = function() {
+                                                       return p.parent == null ? null : p.parent === "parent" ? elInfo.el.parentNode : _dom(p.parent);
+                                               },
+                                               idToRegisterAgainst = p.parent != null ? _currentInstance.getId(parentElement()) : elid;
+                                       
+                                       _sourceEndpointDefinitions[idToRegisterAgainst] = p;
+                                       _sourceEndpointsUnique[idToRegisterAgainst] = p.uniqueEndpoint;
+                                       _sourcesEnabled[idToRegisterAgainst] = true;
+
+                                       var stopEvent = jpcl.dragEvents.stop,
+                                               dragEvent = jpcl.dragEvents.drag,
+                                               dragOptions = jsPlumb.extend({ }, p.dragOptions || {}),
+                                               existingDrag = dragOptions.drag,
+                                               existingStop = dragOptions.stop,
+                                               ep = null,
+                                               endpointAddedButNoDragYet = false;
+                               
+                                       _sourceMaxConnections[idToRegisterAgainst] = maxConnections;    
+
+                                       // set scope if its not set in dragOptions but was passed in in params
+                                       dragOptions.scope = dragOptions.scope || p.scope;
+
+                                       dragOptions[dragEvent] = _ju.wrap(dragOptions[dragEvent], function() {
+                                               if (existingDrag) existingDrag.apply(this, arguments);
+                                               endpointAddedButNoDragYet = false;
+                                       });
+                                       
+                                       dragOptions[stopEvent] = _ju.wrap(dragOptions[stopEvent], function() { 
+
+                                               if (existingStop) existingStop.apply(this, arguments);                                                          
+                           _currentInstance.currentlyDragging = false;                                         
+                                               if (ep._jsPlumb != null) { // if not cleaned up...
+
+                                                       jpcl.unbind(ep.canvas, "mousedown"); 
+                                                                       
+                                                       // reset the anchor to the anchor that was initially provided. the one we were using to drag
+                                                       // the connection was just a placeholder that was located at the place the user pressed the
+                                                       // mouse button to initiate the drag.
+                                                       var anchorDef = p.anchor || _currentInstance.Defaults.Anchor,
+                                                               oldAnchor = ep.anchor,
+                                                               oldConnection = ep.connections[0],
+                                                               newAnchor = _currentInstance.makeAnchor(anchorDef, elid, _currentInstance),
+                                                               _el = ep.element;
+
+                                                       // if the anchor has a 'positionFinder' set, then delegate to that function to find
+                                                       // out where to locate the anchor. issue 117.
+                                                       if (newAnchor.positionFinder != null) {
+                                                               var elPosition = _getOffset(_el, _currentInstance),
+                                                                       elSize = _getSize(_el),
+                                                                       dropPosition = { left:elPosition.left + (oldAnchor.x * elSize[0]), top:elPosition.top + (oldAnchor.y * elSize[1]) },
+                                                                       ap = newAnchor.positionFinder(dropPosition, elPosition, elSize, newAnchor.constructorParams);
+
+                                                               newAnchor.x = ap[0];
+                                                               newAnchor.y = ap[1];
+                                                       }
+
+                                                       ep.setAnchor(newAnchor, true);                                                                                                                                                                                  
+                                                       
+                                                       if (p.parent) {                                         
+                                                               var parent = parentElement();
+                                                               if (parent) {   
+                                                                       var potentialParent = p.container || _currentInstance.Defaults.Container || jsPlumb.Defaults.Container;
+                                                                       ep.setElement(parent, potentialParent);
+                                                               }
+                                                       }                                               
+                                                       
+                                                       ep.repaint();                   
+                                                       _currentInstance.repaint(ep.elementId);                                                                                                                                         
+                                                       _currentInstance.repaint(oldConnection.targetId);
+                                               }                               
+                                       });
+                                       // when the user presses the mouse, add an Endpoint, if we are enabled.
+                                       var mouseDownListener = function(e) {
+
+                                               // if disabled, return.
+                                               if (!_sourcesEnabled[idToRegisterAgainst]) return;
+                           
+                           // if a filter was given, run it, and return if it says no.
+                                               if (p.filter) {
+                                                       var evt = jpcl.getOriginalEvent(e),
+                                                               r = jsPlumbUtil.isString(p.filter) ? selectorFilter(evt, _el, p.filter) : p.filter(evt, _el);
+                                                       
+                                                       if (r === false) return;
+                                               }
+                                               
+                                               // if maxConnections reached
+                                               var sourceCount = _currentInstance.select({source:idToRegisterAgainst}).length;
+                                               if (_sourceMaxConnections[idToRegisterAgainst] >= 0 && sourceCount >= _sourceMaxConnections[idToRegisterAgainst]) {
+                                                       if (onMaxConnections) {
+                                                               onMaxConnections({
+                                                                       element:_el,
+                                                                       maxConnections:maxConnections
+                                                               }, e);
+                                                       }
+                                                       return false;
+                                               }                                       
+
+                                               // make sure we have the latest offset for this div 
+                                               var myOffsetInfo = _updateOffset({elId:elid}).o,
+                                                       z = _currentInstance.getZoom(),         
+                                                       x = ( ((e.pageX || e.page.x) / z) - myOffsetInfo.left) / myOffsetInfo.width, 
+                                                   y = ( ((e.pageY || e.page.y) / z) - myOffsetInfo.top) / myOffsetInfo.height,
+                                                   parentX = x, 
+                                                   parentY = y;                                        
+                                                               
+                                               // if there is a parent, the endpoint will actually be added to it now, rather than the div
+                                               // that was the source.  in that case, we have to adjust the anchor position so it refers to
+                                               // the parent.
+                                               if (p.parent) {
+                                                       var pEl = parentElement(), pId = _getId(pEl);
+                                                       myOffsetInfo = _updateOffset({elId:pId}).o;
+                                                       parentX = ((e.pageX || e.page.x) - myOffsetInfo.left) / myOffsetInfo.width; 
+                                                   parentY = ((e.pageY || e.page.y) - myOffsetInfo.top) / myOffsetInfo.height;
+                                               }                                                                                       
+                                               
+                                               // we need to override the anchor in here, and force 'isSource', but we don't want to mess with
+                                               // the params passed in, because after a connection is established we're going to reset the endpoint
+                                               // to have the anchor we were given.
+                                               var tempEndpointParams = {};
+                                               jsPlumb.extend(tempEndpointParams, p);
+                                               tempEndpointParams.isSource = true;
+                                               tempEndpointParams.anchor = [x,y,0,0];
+                                               tempEndpointParams.parentAnchor = [ parentX, parentY, 0, 0 ];
+                                               tempEndpointParams.dragOptions = dragOptions;
+                                               // if a parent was given we need to turn that into a "container" argument.  this is, by default,
+                                               // the parent of the element we will move to, so parent of p.parent in this case.  however, if
+                                               // the user has specified a 'container' on the endpoint definition or on 
+                                               // the defaults, we should use that.
+                                               if (p.parent) {
+                                                       var potentialParent = tempEndpointParams.container || _currentInstance.Defaults.Container || jsPlumb.Defaults.Container;
+                                                       if (potentialParent)
+                                                               tempEndpointParams.container = potentialParent;
+                                                       else
+                                                               tempEndpointParams.container = jsPlumb.CurrentLibrary.getParent(parentElement());
+                                               }
+                                               
+                                               ep = _currentInstance.addEndpoint(elid, tempEndpointParams);
+
+                                               endpointAddedButNoDragYet = true;
+                                               // we set this to prevent connections from firing attach events before this function has had a chance
+                                               // to move the endpoint.
+                                               ep.endpointWillMoveAfterConnection = p.parent != null;
+                                               ep.endpointWillMoveTo = p.parent ? parentElement() : null;
+
+                                               // TODO test options to makeSource to see if we should do this?
+                                               ep._doNotDeleteOnDetach = false; // reset.
+                                               ep._deleteOnDetach = true;
+
+                           var _delTempEndpoint = function() {
+                                                       // this mouseup event is fired only if no dragging occurred, by jquery and yui, but for mootools
+                                                       // it is fired even if dragging has occurred, in which case we would blow away a perfectly
+                                                       // legitimate endpoint, were it not for this check.  the flag is set after adding an
+                                                       // endpoint and cleared in a drag listener we set in the dragOptions above.
+                                                       if(endpointAddedButNoDragYet) {
+                                                                endpointAddedButNoDragYet = false;
+                                                               _currentInstance.deleteEndpoint(ep);
+                               }
+                                               };
+
+                                               _currentInstance.registerListener(ep.canvas, "mouseup", _delTempEndpoint);
+                           _currentInstance.registerListener(_el, "mouseup", _delTempEndpoint);
+                                               
+                                               // and then trigger its mousedown event, which will kick off a drag, which will start dragging
+                                               // a new connection from this endpoint.
+                                               jpcl.trigger(ep.canvas, "mousedown", e);
+                                               
+                                       };
+                      
+                       // register this on jsPlumb so that it can be cleared by a reset.
+                       _currentInstance.registerListener(_el, "mousedown", mouseDownListener);
+                       _sourceTriggers[elid] = mouseDownListener;
+
+                       // lastly, if a filter was provided, set it as a dragFilter on the element,
+                       // to prevent the element drag function from kicking in when we want to
+                       // drag a new connection
+                       if (p.filter && jsPlumbUtil.isString(p.filter)) {
+                               jpcl.setDragFilter(_el, p.filter);
+                       }
+                               };
+                       
+                       el = _convertYUICollection(el);                 
+                       
+                       var inputs = el.length && el.constructor != String ? el : [ el ];
+                                               
+                       for (var i = 0, ii = inputs.length; i < ii; i++) {                      
+                               _doOne(_info(inputs[i]));
+                       }
+
+                       return _currentInstance;
+               };
+       
+               // see api docs         
+               this.unmakeSource = function(el, doNotClearArrays) {
+                       var info = _info(el),
+                               mouseDownListener = _sourceTriggers[info.id];
+                       
+                       if (mouseDownListener) 
+                               _currentInstance.unregisterListener(info.el, "mousedown", mouseDownListener);
+
+                       if (!doNotClearArrays) {
+                               delete _sourceEndpointDefinitions[info.id];
+                               delete _sourceEndpointsUnique[info.id];
+                               delete _sourcesEnabled[info.id];
+                               delete _sourceTriggers[info.id];
+                               delete _sourceMaxConnections[info.id];
+                       }
+
+                       return _currentInstance;
+               };
+
+               // see api docs
+               this.unmakeEverySource = function() {
+                       for (var i in _sourcesEnabled)
+                               _currentInstance.unmakeSource(i, true);
+
+                       _sourceEndpointDefinitions = {};
+                       _sourceEndpointsUnique = {};
+                       _sourcesEnabled = {};
+                       _sourceTriggers = {};
+               };
+               
+               // see api docs
+               this.unmakeEveryTarget = function() {
+                       for (var i in _targetsEnabled)
+                               _currentInstance.unmakeTarget(i, true);
+                       
+                       _targetEndpointDefinitions = {};
+                       _targetEndpointsUnique = {};
+                       _targetMaxConnections = {};
+                       _targetsEnabled = {};
+
+                       return _currentInstance;
+               };                      
+
+               // does the work of setting a source enabled or disabled.
+               var _setEnabled = function(type, el, state, toggle) {
+                       var a = type == "source" ? _sourcesEnabled : _targetsEnabled;                                                                   
+                       el = _convertYUICollection(el);
+
+                       if (_ju.isString(el)) a[el] = toggle ? !a[el] : state;
+                       else if (el.length) {                           
+                               for (var i = 0, ii = el.length; i < ii; i++) {
+                                       var info = _info(el[i]);
+                                       a[info.id] = toggle ? !a[info.id] : state;
+                               }
+                       }       
+                       return _currentInstance;
+               };
+
+               this.toggleSourceEnabled = function(el) {
+                       _setEnabled("source", el, null, true);  
+                       return _currentInstance.isSourceEnabled(el);
+               };
+
+               this.setSourceEnabled = function(el, state) { return _setEnabled("source", el, state); };
+               this.isSource = function(el) { return _sourcesEnabled[_info(el).id] != null; };         
+               this.isSourceEnabled = function(el) { return _sourcesEnabled[_info(el).id] === true; };
+
+               this.toggleTargetEnabled = function(el) {
+                       _setEnabled("target", el, null, true);  
+                       return _currentInstance.isTargetEnabled(el);
+               };
+               
+               this.isTarget = function(el) { return _targetsEnabled[_info(el).id] != null; };         
+               this.isTargetEnabled = function(el) { return _targetsEnabled[_info(el).id] === true; };
+               this.setTargetEnabled = function(el, state) { return _setEnabled("target", el, state); };
+
+// --------------------- end makeSource/makeTarget ----------------------------------------------                              
+                               
+               this.ready = function(fn) {
+                       _currentInstance.bind("ready", fn);
+               };
+
+               // repaint some element's endpoints and connections
+               this.repaint = function(el, ui, timestamp) {
+                       // support both lists...
+                       if (typeof el == 'object' && el.length)
+                               for ( var i = 0, ii = el.length; i < ii; i++) {                 
+                                       _draw(el[i], ui, timestamp);
+                               }
+                       else // ...and single strings.                                                          
+                               _draw(el, ui, timestamp);
+                               
+                       return _currentInstance;
+               };
+
+               // repaint every endpoint and connection.
+               this.repaintEverything = function(clearEdits) { 
+                       // TODO this timestamp causes continuous anchors to not repaint properly.
+                       // fix this. do not just take out the timestamp. it runs a lot faster with 
+                       // the timestamp included.
+                       //var timestamp = null;
+                       var timestamp = _timestamp();
+                       for ( var elId in endpointsByElement) {
+                               _draw(elId, null, timestamp, clearEdits);                               
+                       }
+                       return _currentInstance;
+               };
+
+               
+               this.removeAllEndpoints = function(el, recurse) {
+            var _one = function(_el) {                                 
+                var info = _info(_el),
+                    ebe = endpointsByElement[info.id],
+                    i, ii;
+
+                if (ebe) {
+                    for ( i = 0, ii = ebe.length; i < ii; i++) 
+                        _currentInstance.deleteEndpoint(ebe[i]);
+                }
+                delete endpointsByElement[info.id];
+                
+                if (recurse) {
+                    if (info.el && info.el.nodeType != 3 && info.el.nodeType != 8 ) {
+                        for ( i = 0, ii = info.el.childNodes.length; i < ii; i++) {
+                            _one(info.el.childNodes[i]);
+                        }
+                    }
+                }
+                
+            };
+            _one(el);
+                       return _currentInstance;
+               };
+                    
+        /**
+        * Remove the given element, including cleaning up all endpoints registered for it.
+        * This is exposed in the public API but also used internally by jsPlumb when removing the
+        * element associated with a connection drag.
+        */
+        this.remove = function(el, doNotRepaint) {
+               var info = _info(el);           
+            _currentInstance.doWhileSuspended(function() {
+               _currentInstance.removeAllEndpoints(info.id, true);
+               _currentInstance.dragManager.elementRemoved(info.id);
+               delete floatingConnections[info.id];     
+               _currentInstance.anchorManager.clearFor(info.id);                                               
+               _currentInstance.anchorManager.removeFloatingConnection(info.id);
+            }, doNotRepaint === false);
+            if(info.el) jsPlumb.CurrentLibrary.removeElement(info.el);
+        };
+
+               var _registeredListeners = {},
+                       _unbindRegisteredListeners = function() {
+                               for (var i in _registeredListeners) {
+                                       for (var j = 0, jj = _registeredListeners[i].length; j < jj; j++) {
+                                               var info = _registeredListeners[i][j];
+                                               jsPlumb.CurrentLibrary.unbind(info.el, info.event, info.listener);
+                                       }
+                               }
+                               _registeredListeners = {};
+                       };
+
+        // internal register listener method.  gives us a hook to clean things up
+        // with if the user calls jsPlumb.reset.
+        this.registerListener = function(el, type, listener) {
+            jsPlumb.CurrentLibrary.bind(el, type, listener);
+            jsPlumbUtil.addToList(_registeredListeners, type, {el:el, event:type, listener:listener});
+        };
+
+        this.unregisterListener = function(el, type, listener) {
+               jsPlumb.CurrentLibrary.unbind(el, type, listener);
+               jsPlumbUtil.removeWithFunction(_registeredListeners, function(rl) {
+                       return rl.type == type && rl.listener == listener;
+               });
+        };
+               
+               this.reset = function() {                       
+                       _currentInstance.deleteEveryEndpoint();
+                       _currentInstance.unbind();
+                       _targetEndpointDefinitions = {};
+                       _targetEndpoints = {};
+                       _targetEndpointsUnique = {};
+                       _targetMaxConnections = {};
+                       _sourceEndpointDefinitions = {};
+                       _sourceEndpoints = {};
+                       _sourceEndpointsUnique = {};
+                       _sourceMaxConnections = {};
+                       connections.splice(0);
+                       _unbindRegisteredListeners();
+                       _currentInstance.anchorManager.reset();
+                       if (!jsPlumbAdapter.headless)
+                               _currentInstance.dragManager.reset();
+               };
+               
+
+               this.setDefaultScope = function(scope) {
+                       DEFAULT_SCOPE = scope;
+                       return _currentInstance;
+               };
+
+               // sets whether or not some element should be currently draggable.
+               this.setDraggable = _setDraggable;
+
+               // sets the id of some element, changing whatever we need to to keep track.
+               this.setId = function(el, newId, doNotSetAttribute) {
+                       // 
+                       var id;
+
+                       if (jsPlumbUtil.isString(el)) {
+                               id = el;                                
+                       }
+                       else {
+                               el = _dom(el);
+                               id = _currentInstance.getId(el);
+                       }
+
+                       var sConns = _currentInstance.getConnections({source:id, scope:'*'}, true),
+                               tConns = _currentInstance.getConnections({target:id, scope:'*'}, true);
+
+                       newId = "" + newId;
+
+                       if (!doNotSetAttribute) {
+                               el = _dom(id);
+                               jsPlumbAdapter.setAttribute(el, "id", newId);
+                       }
+                       else
+                               el = _dom(newId);
+
+                       endpointsByElement[newId] = endpointsByElement[id] || [];
+                       for (var i = 0, ii = endpointsByElement[newId].length; i < ii; i++) {
+                               endpointsByElement[newId][i].setElementId(newId);
+                               endpointsByElement[newId][i].setReferenceElement(el);
+                       }
+                       delete endpointsByElement[id];
+
+                       _currentInstance.anchorManager.changeId(id, newId);
+                       if (!jsPlumbAdapter.headless)           
+                               _currentInstance.dragManager.changeId(id, newId);
+
+                       var _conns = function(list, epIdx, type) {
+                               for (var i = 0, ii = list.length; i < ii; i++) {
+                                       list[i].endpoints[epIdx].setElementId(newId);
+                                       list[i].endpoints[epIdx].setReferenceElement(el);
+                                       list[i][type + "Id"] = newId;
+                                       list[i][type] = el;
+                               }
+                       };
+                       _conns(sConns, 0, "source");
+                       _conns(tConns, 1, "target");
+
+                       _currentInstance.repaint(newId);
+               };              
+
+               this.setDebugLog = function(debugLog) {
+                       log = debugLog;
+               };
+                                       
+               this.setSuspendDrawing = function(val, repaintAfterwards) {
+                       var curVal = _suspendDrawing;
+                   _suspendDrawing = val;
+                               if (val) _suspendedAt = new Date().getTime(); else _suspendedAt = null;
+                   if (repaintAfterwards) _currentInstance.repaintEverything();
+                   return curVal;
+               };
+               
+        // returns whether or not drawing is currently suspended.              
+               this.isSuspendDrawing = function() {
+                       return _suspendDrawing;
+               };
+            
+        // return timestamp for when drawing was suspended.
+        this.getSuspendedAt = function() { return _suspendedAt; };
+
+        /**
+        * @doc function
+        * @name jsPlumb.class:doWhileSuspended
+        * @param {function} fn Function to run while suspended.
+        * @param {boolean} doNotRepaintAfterwards If true, jsPlumb won't run a full repaint. Otherwise it will.
+        * @description Suspends drawing, runs the given function, then re-enables drawing (and repaints, unless you tell it not to)
+        */
+        this.doWhileSuspended = function(fn, doNotRepaintAfterwards) {     
+               var _wasSuspended = _currentInstance.isSuspendDrawing();                
+               if (!_wasSuspended)
+                               _currentInstance.setSuspendDrawing(true);
+                       try {
+                               fn();
+                       }
+                       catch (e) {
+                               _ju.log("Function run while suspended failed", e);
+                       }                       
+                       if (!_wasSuspended)
+                               _currentInstance.setSuspendDrawing(false, !doNotRepaintAfterwards);
+        };
+            
+        this.updateOffset = _updateOffset;
+        this.getOffset = function(elId) { return offsets[elId]; };
+        this.getSize = function(elId) { return sizes[elId]; };            
+        this.getCachedData = _getCachedData;
+        this.timestamp = _timestamp;
+               
+               
+               
+               /**
+                * @doc function
+                * @name jsPlumb.class:setRenderMode
+                * @param {string} mode One of `jsPlumb.SVG, `jsPlumb.VML` or `jsPlumb.CANVAS`.
+                * @description Sets render mode.  jsPlumb will fall back to VML if it determines that
+                * what you asked for is not supported (and that VML is).  If you asked for VML but the browser does
+                * not support it, jsPlumb uses SVG.
+                * @return {string} The render mode that jsPlumb set, which of course may be different from that requested.
+                */
+               this.setRenderMode = function(mode) {                   
+                       renderMode = jsPlumbAdapter.setRenderMode(mode);
+                       var i, ii;
+                       // only add this if the renderer is canvas; we dont want these listeners registered on te
+                       // entire document otherwise.
+                       if (renderMode == jsPlumb.CANVAS) {
+                               var bindOne = function(event) {
+                       jsPlumb.CurrentLibrary.bind(document, event, function(e) {
+                           if (!_currentInstance.currentlyDragging && renderMode == jsPlumb.CANVAS) {
+                               // try connections first
+                               for (i = 0, ii = connections.length; i < ii; i++ ) {
+                                var t = connections[i].getConnector()[event](e);
+                                if (t) return; 
+                            }
+                               for (var el in endpointsByElement) {
+                                   var ee = endpointsByElement[el];
+                                   for ( i = 0, ii = ee.length; i < ii; i++ ) {
+                                       if (ee[i].endpoint[event] && ee[i].endpoint[event](e)) return;
+                                   }
+                               }
+                           }
+                       });                                     
+                               };
+                               bindOne("click");bindOne("dblclick");bindOne("mousemove");bindOne("mousedown");bindOne("mouseup");bindOne("contextmenu");                               
+                       }
+
+                       return renderMode;
+               };
+               
+               /**
+                * @doc function
+                * @name jsPlumb.class:getRenderMode
+                * @description Gets the current render mode for this instance of jsPlumb.
+                * @return {string} The current render mode - "canvas", "svg" or "vml".
+                */
+               this.getRenderMode = function() { return renderMode; };
+               
+               this.show = function(el, changeEndpoints) {
+                       _setVisible(el, "block", changeEndpoints);
+                       return _currentInstance;
+               };              
+
+               /**
+                * gets some test hooks. nothing writable.
+                */
+               this.getTestHarness = function() {
+                       return {
+                               endpointsByElement : endpointsByElement,  
+                               endpointCount : function(elId) {
+                                       var e = endpointsByElement[elId];
+                                       return e ? e.length : 0;
+                               },
+                               connectionCount : function(scope) {
+                                       scope = scope || DEFAULT_SCOPE;
+                                       var c = _currentInstance.getConnections({scope:scope});
+                                       return c ? c.length : 0;
+                               },
+                               getId : _getId,
+                               makeAnchor:self.makeAnchor,
+                               makeDynamicAnchor:self.makeDynamicAnchor
+                       };
+               };
+               
+               
+               // TODO: update this method to return the current state.
+               this.toggleVisible = _toggleVisible;
+               this.toggleDraggable = _toggleDraggable;                                                
+               this.addListener = this.bind;
+               
+        /*
+            helper method to take an xy location and adjust it for the parent's offset and scroll.
+        */
+               this.adjustForParentOffsetAndScroll = function(xy, el) {
+
+                       var offsetParent = null, result = xy;
+                       if (el.tagName.toLowerCase() === "svg" && el.parentNode) {
+                               offsetParent = el.parentNode;
+                       }
+                       else if (el.offsetParent) {
+                               offsetParent = el.offsetParent;                                 
+                       }
+                       if (offsetParent != null) {
+                               var po = offsetParent.tagName.toLowerCase() === "body" ? {left:0,top:0} : _getOffset(offsetParent, _currentInstance),
+                                       so = offsetParent.tagName.toLowerCase() === "body" ? {left:0,top:0} : {left:offsetParent.scrollLeft, top:offsetParent.scrollTop};                                       
+
+                               // i thought it might be cool to do this:
+                               //      lastReturnValue[0] = lastReturnValue[0] - offsetParent.offsetLeft + offsetParent.scrollLeft;
+                               //      lastReturnValue[1] = lastReturnValue[1] - offsetParent.offsetTop + offsetParent.scrollTop;                                      
+                               // but i think it ignores margins.  my reasoning was that it's quicker to not hand off to some underlying                                       
+                               // library.
+                               
+                               result[0] = xy[0] - po.left + so.left;
+                               result[1] = xy[1] - po.top + so.top;
+                       }
+               
+                       return result;
+                       
+               };
+
+               if (!jsPlumbAdapter.headless) {
+                       _currentInstance.dragManager = jsPlumbAdapter.getDragManager(_currentInstance);
+                       _currentInstance.recalculateOffsets = _currentInstance.dragManager.updateOffsets;
+           }       
+                                   
+    };
+
+    jsPlumbUtil.extend(jsPlumbInstance, jsPlumbUtil.EventGenerator, {
+       setAttribute : function(el, a, v) {
+               jsPlumbAdapter.setAttribute(el, a, v);
+       },
+       getAttribute : function(el, a) {
+               return jsPlumbAdapter.getAttribute(jsPlumb.CurrentLibrary.getDOMElement(el), a);
+       },      
+       registerConnectionType : function(id, type) {
+               this._connectionTypes[id] = jsPlumb.extend({}, type);
+       },      
+       registerConnectionTypes : function(types) {
+               for (var i in types)
+                       this._connectionTypes[i] = jsPlumb.extend({}, types[i]);
+       },              
+       registerEndpointType : function(id, type) {
+               this._endpointTypes[id] = jsPlumb.extend({}, type);
+       },      
+       registerEndpointTypes : function(types) {
+               for (var i in types)
+                       this._endpointTypes[i] = jsPlumb.extend({}, types[i]);
+       },      
+       getType : function(id, typeDescriptor) {
+               return typeDescriptor ===  "connection" ? this._connectionTypes[id] : this._endpointTypes[id];
+       },
+       setIdChanged : function(oldId, newId) {
+               this.setId(oldId, newId, true);
+       },
+       // set parent: change the parent for some node and update all the registrations we need to.
+       setParent : function(el, newParent) {
+               var jpcl = jsPlumb.CurrentLibrary,
+                       _el = jpcl.getElementObject(el),
+                       _dom = jpcl.getDOMElement(_el),
+                       _id = this.getId(_dom),
+                       _pel = jpcl.getElementObject(newParent),
+                       _pdom = jpcl.getDOMElement(_pel),
+                       _pid = this.getId(_pdom);
+
+               _dom.parentNode.removeChild(_dom);
+               _pdom.appendChild(_dom);
+               this.dragManager.setParent(_el, _id, _pel, _pid);
+       }
+    });
+
+// --------------------- static instance + AMD registration -------------------------------------------        
+       
+// create static instance and assign to window if window exists.       
+       var jsPlumb = new jsPlumbInstance();
+       // register on window if defined (lets us run on server)
+       if (typeof window != 'undefined') window.jsPlumb = jsPlumb;     
+       // add 'getInstance' method to static instance
+       /**
+       * @name jsPlumb.getInstance
+       * @param {object} [_defaults] Optional default settings for the new instance.
+       * @desc Gets a new instance of jsPlumb.
+       */
+       jsPlumb.getInstance = function(_defaults) {
+               var j = new jsPlumbInstance(_defaults);
+               j.init();
+               return j;
+       };
+// maybe register static instance as an AMD module, and getInstance method too.
+       if ( typeof define === "function") {
+               define( "jsplumb", [], function () { return jsPlumb; } );
+               define( "jsplumbinstance", [], function () { return jsPlumb.getInstance(); } );
+       }
+ // CommonJS 
+       if (typeof exports !== 'undefined') {
+      exports.jsPlumb = jsPlumb;
+       }
+       
+       
+// --------------------- end static instance + AMD registration -------------------------------------------            
+       
+})();