You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tajo.apache.org by hy...@apache.org on 2013/10/10 14:40:41 UTC

[2/6] TAJO-239: Improving web UI. (Keuntae Park via hyunsik)

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/85b83940/tajo-core/tajo-core-backend/src/main/resources/webapps/static/js/jquery.jsPlumb-1.3.16-all.js
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/resources/webapps/static/js/jquery.jsPlumb-1.3.16-all.js b/tajo-core/tajo-core-backend/src/main/resources/webapps/static/js/jquery.jsPlumb-1.3.16-all.js
new file mode 100644
index 0000000..2e35a49
--- /dev/null
+++ b/tajo-core/tajo-core-backend/src/main/resources/webapps/static/js/jquery.jsPlumb-1.3.16-all.js
@@ -0,0 +1,10561 @@
+/*
+ * jsPlumb
+ * 
+ * Title:jsPlumb 1.3.16
+ * 
+ * Provides a way to visually connect elements on an HTML page, using either SVG, Canvas
+ * elements, or VML.  
+ * 
+ * This file contains the util functions
+ *
+ * Copyright (c) 2010 - 2012 Simon Porritt (http://jsplumb.org)
+ * 
+ * http://jsplumb.org
+ * http://github.com/sporritt/jsplumb
+ * http://code.google.com/p/jsplumb
+ * 
+ * Dual licensed under the MIT and GPL2 licenses.
+ */
+jsPlumbUtil = {
+    isArray : function(a) {
+        return Object.prototype.toString.call(a) === "[object Array]";
+    },
+    isString : function(s) {
+        return typeof s === "string";
+    },
+    isBoolean: function(s) {
+        return typeof s === "boolean";
+    },
+    isObject : function(o) {
+        return Object.prototype.toString.call(o) === "[object Object]";
+    },
+    isDate : function(o) {
+        return Object.prototype.toString.call(o) === "[object Date]";
+    },
+    isFunction: function(o) {
+        return Object.prototype.toString.call(o) === "[object Function]";
+    },
+    clone : function(a) {
+        if (this.isString(a)) return new String(a);
+        else if (this.isBoolean(a)) return new Boolean(a);
+        else if (this.isDate(a)) return new Date(a.getTime());
+        else if (this.isFunction(a)) return a;
+        else if (this.isArray(a)) {
+            var b = [];
+            for (var i = 0; i < a.length; i++)
+                b.push(this.clone(a[i]));
+            return b;
+        }
+        else if (this.isObject(a)) {
+            var b = {};
+            for (var i in a)
+                b[i] = this.clone(a[i]);
+            return b;
+        }
+        else return a;
+    },
+    merge : function(a, b) {
+        var c = this.clone(a);
+        for (var i in b) {
+            if (c[i] == null || this.isString(b[i]) || this.isBoolean(b[i]))
+                c[i] = b[i];
+            else {
+                if (this.isArray(b[i]) && this.isArray(c[i])) {
+                    var ar = [];
+                    ar.push.apply(ar, c[i]);
+                    ar.push.apply(ar, b[i]);
+                    c[i] = ar;
+                }
+                else if(this.isObject(c[i]) && this.isObject(b[i])) {
+                    for (var j in b[i])
+                        c[i][j] = b[i][j];
+                }
+            }
+        }
+        return c;
+    },
+    convertStyle : function(s, ignoreAlpha) {
+        // TODO: jsPlumb should support a separate 'opacity' style member.
+        if ("transparent" === s) return s;
+        var o = s,
+            pad = function(n) { return n.length == 1 ? "0" + n : n; },
+            hex = function(k) { return pad(Number(k).toString(16)); },
+            pattern = /(rgb[a]?\()(.*)(\))/;
+        if (s.match(pattern)) {
+            var parts = s.match(pattern)[2].split(",");
+            o = "#" + hex(parts[0]) + hex(parts[1]) + hex(parts[2]);
+            if (!ignoreAlpha && parts.length == 4)
+                o = o + hex(parts[3]);
+        }
+        return o;
+    },
+    gradient : function(p1, p2) {
+        p1 = jsPlumbUtil.isArray(p1) ? p1 : [p1.x, p1.y];
+        p2 = jsPlumbUtil.isArray(p2) ? p2 : [p2.x, p2.y];
+        return (p2[1] - p1[1]) / (p2[0] - p1[0]);
+    },
+    normal : function(p1, p2) {
+        return -1 / jsPlumbUtil.gradient(p1,p2);
+    },
+    lineLength : function(p1, p2) {
+        p1 = jsPlumbUtil.isArray(p1) ? p1 : [p1.x, p1.y];
+        p2 = jsPlumbUtil.isArray(p2) ? p2 : [p2.x, p2.y];
+        return Math.sqrt(Math.pow(p2[1] - p1[1], 2) + Math.pow(p2[0] - p1[0], 2));
+    },
+    segment : function(p1, p2) {
+        p1 = jsPlumbUtil.isArray(p1) ? p1 : [p1.x, p1.y];
+        p2 = jsPlumbUtil.isArray(p2) ? p2 : [p2.x, p2.y];
+        if (p2[0] > p1[0]) {
+            return (p2[1] > p1[1]) ? 2 : 1;
+        }
+        else {
+            return (p2[1] > p1[1]) ? 3 : 4;
+        }
+    },
+    intersects : function(r1, r2) {
+        var x1 = r1.x, x2 = r1.x + r1.w, y1 = r1.y, y2 = r1.y + r1.h,
+            a1 = r2.x, a2 = r2.x + r2.w, b1 = r2.y, b2 = r2.y + r2.h;
+
+        return  ( (x1 <= a1 && a1 <= x2) && (y1 <= b1 && b1 <= y2) ) ||
+            ( (x1 <= a2 && a2 <= x2) && (y1 <= b1 && b1 <= y2) ) ||
+            ( (x1 <= a1 && a1 <= x2) && (y1 <= b2 && b2 <= y2) ) ||
+            ( (x1 <= a2 && a1 <= x2) && (y1 <= b2 && b2 <= y2) ) ||
+
+            ( (a1 <= x1 && x1 <= a2) && (b1 <= y1 && y1 <= b2) ) ||
+            ( (a1 <= x2 && x2 <= a2) && (b1 <= y1 && y1 <= b2) ) ||
+            ( (a1 <= x1 && x1 <= a2) && (b1 <= y2 && y2 <= b2) ) ||
+            ( (a1 <= x2 && x1 <= a2) && (b1 <= y2 && y2 <= b2) );
+    },
+    segmentMultipliers : [null, [1, -1], [1, 1], [-1, 1], [-1, -1] ],
+    inverseSegmentMultipliers : [null, [-1, -1], [-1, 1], [1, 1], [1, -1] ],
+    pointOnLine : function(fromPoint, toPoint, distance) {
+        var m = jsPlumbUtil.gradient(fromPoint, toPoint),
+            s = jsPlumbUtil.segment(fromPoint, toPoint),
+            segmentMultiplier = distance > 0 ? jsPlumbUtil.segmentMultipliers[s] : jsPlumbUtil.inverseSegmentMultipliers[s],
+            theta = Math.atan(m),
+            y = Math.abs(distance * Math.sin(theta)) * segmentMultiplier[1],
+            x =  Math.abs(distance * Math.cos(theta)) * segmentMultiplier[0];
+        return { x:fromPoint.x + x, y:fromPoint.y + y };
+    },
+    /**
+     * calculates a perpendicular to the line fromPoint->toPoint, that passes through toPoint and is 'length' long.
+     * @param fromPoint
+     * @param toPoint
+     * @param length
+     */
+    perpendicularLineTo : function(fromPoint, toPoint, length) {
+        var m = jsPlumbUtil.gradient(fromPoint, toPoint),
+            theta2 = Math.atan(-1 / m),
+            y =  length / 2 * Math.sin(theta2),
+            x =  length / 2 * Math.cos(theta2);
+        return [{x:toPoint.x + x, y:toPoint.y + y}, {x:toPoint.x - x, y:toPoint.y - y}];
+    },
+    findWithFunction : function(a, f) {
+        if (a)
+            for (var i = 0; i < a.length; i++) if (f(a[i])) return i;
+        return -1;
+    },
+    indexOf : function(l, v) {
+        return jsPlumbUtil.findWithFunction(l, function(_v) { return _v == v; });
+    },
+    removeWithFunction : function(a, f) {
+        var idx = jsPlumbUtil.findWithFunction(a, f);
+        if (idx > -1) a.splice(idx, 1);
+        return idx != -1;
+    },
+    remove : function(l, v) {
+        var idx = jsPlumbUtil.indexOf(l, v);
+        if (idx > -1) l.splice(idx, 1);
+        return idx != -1;
+    },
+    // TODO support insert index
+    addWithFunction : function(list, item, hashFunction) {
+        if (jsPlumbUtil.findWithFunction(list, hashFunction) == -1) list.push(item);
+    },
+    addToList : function(map, key, value) {
+        var l = map[key];
+        if (l == null) {
+            l = [], map[key] = l;
+        }
+        l.push(value);
+        return l;
+    },
+    /**
+     * EventGenerator
+     * Superclass for objects that generate events - jsPlumb extends this, as does jsPlumbUIComponent, which all the UI elements extend.
+     */
+    EventGenerator : function() {
+        var _listeners = {}, self = this;
+
+        // this is a list of events that should re-throw any errors that occur during their dispatch. as of 1.3.0 this is private to
+        // jsPlumb, but it seems feasible that people might want to manipulate this list.  the thinking is that we don't want event
+        // listeners to bring down jsPlumb - or do we.  i can't make up my mind about this, but i know i want to hear about it if the "ready"
+        // event fails, because then my page has most likely not initialised.  so i have this halfway-house solution.  it will be interesting
+        // to hear what other people think.
+        var eventsToDieOn = [ "ready" ];
+
+        /*
+         * Binds a listener to an event.  
+         * 
+         * Parameters:
+         * 	event		-	name of the event to bind to.
+         * 	listener	-	function to execute.
+         */
+        this.bind = function(event, listener) {
+            jsPlumbUtil.addToList(_listeners, event, listener);
+            return self;
+        };
+        /*
+         * Fires an update for the given event.
+         * 
+         * Parameters:
+         * 	event				-	event to fire
+         * 	value				-	value to pass to the event listener(s).
+         *  originalEvent	 	- 	the original event from the browser
+         */
+        this.fire = function(event, value, originalEvent) {
+            if (_listeners[event]) {
+                for ( var i = 0; i < _listeners[event].length; i++) {
+                    // doing it this way rather than catching and then possibly re-throwing means that an error propagated by this
+                    // method will have the whole call stack available in the debugger.
+                    if (jsPlumbUtil.findWithFunction(eventsToDieOn, function(e) { return e === event}) != -1)
+                        _listeners[event][i](value, originalEvent);
+                    else {
+                        // for events we don't want to die on, catch and log.
+                        try {
+                            _listeners[event][i](value, originalEvent);
+                        } catch (e) {
+                            jsPlumbUtil.log("jsPlumb: fire failed for event " + event + " : " + e);
+                        }
+                    }
+                }
+            }
+            return self;
+        };
+        /*
+         * Clears either all listeners, or listeners for some specific event.
+         * 
+         * Parameters:
+         * 	event	-	optional. constrains the clear to just listeners for this event.
+         */
+        this.unbind = function(event) {
+            if (event)
+                delete _listeners[event];
+            else {
+                _listeners = {};
+            }
+            return self;
+        };
+
+        this.getListener = function(forEvent) {
+            return _listeners[forEvent];
+        };
+    },
+    logEnabled : true,
+    log : function() {
+        if (jsPlumbUtil.logEnabled && typeof console != "undefined") {
+            try {
+                var msg = arguments[arguments.length - 1];
+                console.log(msg);
+            }
+            catch (e) {}
+        }
+    },
+    group : function(g) { if (jsPlumbUtil.logEnabled && typeof console != "undefined") console.group(g); },
+    groupEnd : function(g) { if (jsPlumbUtil.logEnabled && typeof console != "undefined") console.groupEnd(g); },
+    time : function(t) { if (jsPlumbUtil.logEnabled && typeof console != "undefined") console.time(t); },
+    timeEnd : function(t) { if (jsPlumbUtil.logEnabled && typeof console != "undefined") console.timeEnd(t); }
+};/*
+ * jsPlumb
+ * 
+ * Title:jsPlumb 1.3.16
+ * 
+ * Provides a way to visually connect elements on an HTML page, using either SVG, Canvas
+ * elements, or VML.  
+ * 
+ * This file contains the base functionality for DOM type adapters. 
+ *
+ * Copyright (c) 2010 - 2012 Simon Porritt (http://jsplumb.org)
+ * 
+ * http://jsplumb.org
+ * http://github.com/sporritt/jsplumb
+ * http://code.google.com/p/jsplumb
+ * 
+ * Dual licensed under the MIT and GPL2 licenses.
+ */
+;(function() {
+
+    var canvasAvailable = !!document.createElement('canvas').getContext,
+        svgAvailable = !!window.SVGAngle || document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1"),
+    // http://stackoverflow.com/questions/654112/how-do-you-detect-support-for-vml-or-svg-in-a-browser
+        vmlAvailable = function() {
+            if (vmlAvailable.vml == undefined) {
+                var a = document.body.appendChild(document.createElement('div'));
+                a.innerHTML = '<v:shape id="vml_flag1" adj="1" />';
+                var b = a.firstChild;
+                b.style.behavior = "url(#default#VML)";
+                vmlAvailable.vml = b ? typeof b.adj == "object": true;
+                a.parentNode.removeChild(a);
+            }
+            return vmlAvailable.vml;
+        };
+
+    /**
+     Manages dragging for some instance of jsPlumb.
+     */
+    var DragManager = function(_currentInstance) {
+        var _draggables = {}, _dlist = [], _delements = {}, _elementsWithEndpoints = {};
+
+        /**
+         register some element as draggable.  right now the drag init stuff is done elsewhere, and it is
+         possible that will continue to be the case.
+         */
+        this.register = function(el) {
+            var jpcl = jsPlumb.CurrentLibrary;
+            el = jpcl.getElementObject(el);
+            var id = _currentInstance.getId(el),
+                domEl = jpcl.getDOMElement(el),
+                parentOffset = jpcl.getOffset(el);
+
+            if (!_draggables[id]) {
+                _draggables[id] = el;
+                _dlist.push(el);
+                _delements[id] = {};
+            }
+
+            // look for child elements that have endpoints and register them against this draggable.
+            var _oneLevel = function(p, startOffset) {
+                if (p) {
+                    for (var i = 0; i < p.childNodes.length; i++) {
+                        if (p.childNodes[i].nodeType != 3) {
+                            var cEl = jpcl.getElementObject(p.childNodes[i]),
+                                cid = _currentInstance.getId(cEl, null, true);
+                            if (cid && _elementsWithEndpoints[cid] && _elementsWithEndpoints[cid] > 0) {
+                                var cOff = jpcl.getOffset(cEl);
+                                _delements[id][cid] = {
+                                    id:cid,
+                                    offset:{
+                                        left:cOff.left - parentOffset.left,
+                                        top:cOff.top - parentOffset.top
+                                    }
+                                };
+                            }
+                            _oneLevel(p.childNodes[i]);
+                        }
+                    }
+                }
+            };
+
+            _oneLevel(domEl);
+        };
+
+        // refresh the offsets for child elements of this element. 
+        this.updateOffsets = function(elId) {
+            var jpcl = jsPlumb.CurrentLibrary,
+                el = jpcl.getElementObject(elId),
+                id = _currentInstance.getId(el),
+                children = _delements[id],
+                parentOffset = jpcl.getOffset(el);
+
+            if (children) {
+                for (var i in children) {
+                    var cel = jpcl.getElementObject(i),
+                        cOff = jpcl.getOffset(cel);
+
+                    _delements[id][i] = {
+                        id:i,
+                        offset:{
+                            left:cOff.left - parentOffset.left,
+                            top:cOff.top - parentOffset.top
+                        }
+                    };
+                }
+            }
+        };
+
+        /**
+         notification that an endpoint was added to the given el.  we go up from that el's parent
+         node, looking for a parent that has been registered as a draggable. if we find one, we add this
+         el to that parent's list of elements to update on drag (if it is not there already)
+         */
+        this.endpointAdded = function(el) {
+            var jpcl = jsPlumb.CurrentLibrary, b = document.body, id = _currentInstance.getId(el), c = jpcl.getDOMElement(el),
+                p = c.parentNode, done = p == b;
+
+            _elementsWithEndpoints[id] = _elementsWithEndpoints[id] ? _elementsWithEndpoints[id] + 1 : 1;
+
+            while (p != b) {
+                var pid = _currentInstance.getId(p, null, true);
+                if (pid && _draggables[pid]) {
+                    var idx = -1, pEl = jpcl.getElementObject(p), pLoc = jpcl.getOffset(pEl);
+
+                    if (_delements[pid][id] == null) {
+                        var cLoc = jsPlumb.CurrentLibrary.getOffset(el);
+                        _delements[pid][id] = {
+                            id:id,
+                            offset:{
+                                left:cLoc.left - pLoc.left,
+                                top:cLoc.top - pLoc.top
+                            }
+                        };
+                    }
+                    break;
+                }
+                p = p.parentNode;
+            }
+        };
+
+        this.endpointDeleted = function(endpoint) {
+            if (_elementsWithEndpoints[endpoint.elementId]) {
+                _elementsWithEndpoints[endpoint.elementId]--;
+                if (_elementsWithEndpoints[endpoint.elementId] <= 0) {
+                    for (var i in _delements) {
+                        delete _delements[i][endpoint.elementId];
+                    }
+                }
+            }
+        };
+
+        this.getElementsForDraggable = function(id) {
+            return _delements[id];
+        };
+
+        this.reset = function() {
+            _draggables = {};
+            _dlist = [];
+            _delements = {};
+            _elementsWithEndpoints = {};
+        };
+
+    };
+
+    // for those browsers that dont have it.  they still don't have it! but at least they won't crash.
+    if (!window.console)
+        window.console = { time:function(){}, timeEnd:function(){}, group:function(){}, groupEnd:function(){}, log:function(){} };
+
+    window.jsPlumbAdapter = {
+
+        headless:false,
+
+        appendToRoot : function(node) {
+            document.body.appendChild(node);
+        },
+        getRenderModes : function() {
+            return [ "canvas", "svg", "vml" ]
+        },
+        isRenderModeAvailable : function(m) {
+            return {
+                "canvas":canvasAvailable,
+                "svg":svgAvailable,
+                "vml":vmlAvailable()
+            }[m];
+        },
+        getDragManager : function(_jsPlumb) {
+            return new DragManager(_jsPlumb);
+        },
+        setRenderMode : function(mode) {
+            var renderMode;
+
+            if (mode) {
+                mode = mode.toLowerCase();
+
+                var canvasAvailable = this.isRenderModeAvailable("canvas"),
+                    svgAvailable = this.isRenderModeAvailable("svg"),
+                    vmlAvailable = this.isRenderModeAvailable("vml");
+
+                //if (mode !== jsPlumb.CANVAS && mode !== jsPlumb.SVG && mode !== jsPlumb.VML) throw new Error("render mode must be one of jsPlumb.CANVAS, jsPlumb.SVG or jsPlumb.VML");
+                // now test we actually have the capability to do this.						
+                if (mode === "svg") {
+                    if (svgAvailable) renderMode = "svg"
+                    else if (canvasAvailable) renderMode = "canvas"
+                    else if (vmlAvailable) renderMode = "vml"
+                }
+                else if (mode === "canvas" && canvasAvailable) renderMode = "canvas";
+                else if (vmlAvailable) renderMode = "vml";
+            }
+
+            return renderMode;
+        }
+    };
+
+})();/*
+ * jsPlumb
+ * 
+ * Title:jsPlumb 1.3.16
+ * 
+ * Provides a way to visually connect elements on an HTML page, using either SVG, Canvas
+ * elements, or VML.  
+ * 
+ * This file contains the jsPlumb core code.
+ *
+ * Copyright (c) 2010 - 2012 Simon Porritt (simon.porritt@gmail.com)
+ * 
+ * http://jsplumb.org
+ * http://github.com/sporritt/jsplumb
+ * http://code.google.com/p/jsplumb
+ * 
+ * Dual licensed under the MIT and GPL2 licenses.
+ */
+
+;(function() {
+
+    /**
+     * Class:jsPlumb
+     * The jsPlumb engine, registered as a static object in the window.  This object contains all of the methods you will use to
+     * create and maintain Connections and Endpoints.
+     */
+
+    var _findWithFunction = jsPlumbUtil.findWithFunction,
+        _indexOf = jsPlumbUtil.indexOf,
+        _removeWithFunction = jsPlumbUtil.removeWithFunction,
+        _remove = jsPlumbUtil.remove,
+    // TODO support insert index
+        _addWithFunction = jsPlumbUtil.addWithFunction,
+        _addToList = jsPlumbUtil.addToList,
+        /**
+         an isArray function that even works across iframes...see here:
+
+         http://tobyho.com/2011/01/28/checking-types-in-javascript/
+
+         i was originally using "a.constructor == Array" as a test.
+         */
+            _isArray = jsPlumbUtil.isArray,
+        _isString = jsPlumbUtil.isString,
+        _isObject = jsPlumbUtil.isObject;
+
+    var _connectionBeingDragged = null,
+        _getAttribute = function(el, attName) { return jsPlumb.CurrentLibrary.getAttribute(_getElementObject(el), attName); },
+        _setAttribute = function(el, attName, attValue) { jsPlumb.CurrentLibrary.setAttribute(_getElementObject(el), attName, attValue); },
+        _addClass = function(el, clazz) { jsPlumb.CurrentLibrary.addClass(_getElementObject(el), clazz); },
+        _hasClass = function(el, clazz) { return jsPlumb.CurrentLibrary.hasClass(_getElementObject(el), clazz); },
+        _removeClass = function(el, clazz) { jsPlumb.CurrentLibrary.removeClass(_getElementObject(el), clazz); },
+        _getElementObject = function(el) { return jsPlumb.CurrentLibrary.getElementObject(el); },
+        _getOffset = function(el, _instance) {
+            var o = jsPlumb.CurrentLibrary.getOffset(_getElementObject(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(_getElementObject(el));
+        },
+        _log = jsPlumbUtil.log,
+        _group = jsPlumbUtil.group,
+        _groupEnd = jsPlumbUtil.groupEnd,
+        _time = jsPlumbUtil.time,
+        _timeEnd = jsPlumbUtil.timeEnd,
+
+        /**
+         * creates a timestamp, using milliseconds since 1970, but as a string.
+         */
+            _timestamp = function() { return "" + (new Date()).getTime(); },
+
+    /*
+     * Class:jsPlumbUIComponent
+     * Abstract superclass for UI components Endpoint and Connection.  Provides the abstraction of paintStyle/hoverPaintStyle,
+     * and also extends jsPlumbUtil.EventGenerator to provide the bind and fire methods.
+     */
+        jsPlumbUIComponent = function(params) {
+            var self = this,
+                a = arguments,
+                _hover = false,
+                parameters = params.parameters || {},
+                idPrefix = self.idPrefix,
+                id = idPrefix + (new Date()).getTime(),
+                paintStyle = null,
+                hoverPaintStyle = null;
+
+            self._jsPlumb = params["_jsPlumb"];
+            self.getId = function() { return id; };
+            self.tooltip = params.tooltip;
+            self.hoverClass = params.hoverClass || self._jsPlumb.Defaults.HoverClass || jsPlumb.Defaults.HoverClass;
+
+            // all components can generate events
+            jsPlumbUtil.EventGenerator.apply(this);
+            // 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();
+                self.constructor.apply(o, a);
+                return o;
+            };
+
+            this.getParameter = function(name) { return parameters[name]; },
+                this.getParameters = function() {
+                    return parameters;
+                },
+                this.setParameter = function(name, value) { parameters[name] = value; },
+                this.setParameters = function(p) { parameters = p; },
+                this.overlayPlacements = [];
+
+            // user can supply a beforeDetach callback, which will be executed before a detach
+            // is performed; returning false prevents the detach.
+            var beforeDetach = params.beforeDetach;
+            this.isDetachAllowed = function(connection) {
+                var r = self._jsPlumb.checkCondition("beforeDetach", connection );
+                if (beforeDetach) {
+                    try {
+                        r = beforeDetach(connection);
+                    }
+                    catch (e) { _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.
+            var beforeDrop = params.beforeDrop;
+            this.isDropAllowed = function(sourceId, targetId, scope, connection, dropEndpoint) {
+                var r = self._jsPlumb.checkCondition("beforeDrop", {
+                    sourceId:sourceId,
+                    targetId:targetId,
+                    scope:scope,
+                    connection:connection,
+                    dropEndpoint:dropEndpoint
+                });
+                if (beforeDrop) {
+                    try {
+                        r = beforeDrop({
+                            sourceId:sourceId,
+                            targetId:targetId,
+                            scope:scope,
+                            connection:connection,
+                            dropEndpoint:dropEndpoint
+                        });
+                    }
+                    catch (e) { _log("jsPlumb: beforeDrop callback failed", e); }
+                }
+                return r;
+            };
+
+            // helper method to update the hover style whenever it, or paintStyle, changes.
+            // we use paintStyle as the foundation and merge hoverPaintStyle over the
+            // top.
+            var _updateHoverStyle = function() {
+                if (paintStyle && hoverPaintStyle) {
+                    var mergedHoverStyle = {};
+                    jsPlumb.extend(mergedHoverStyle, paintStyle);
+                    jsPlumb.extend(mergedHoverStyle, hoverPaintStyle);
+                    delete self["hoverPaintStyle"];
+                    // we want the fillStyle of paintStyle to override a gradient, if possible.
+                    if (mergedHoverStyle.gradient && paintStyle.fillStyle)
+                        delete mergedHoverStyle["gradient"];
+                    hoverPaintStyle = mergedHoverStyle;
+                }
+            };
+
+            /*
+             * Sets the paint style and then repaints the element.
+             * 
+             * Parameters:
+             * 	style - Style to use.
+             */
+            this.setPaintStyle = function(style, doNotRepaint) {
+                paintStyle = style;
+                self.paintStyleInUse = paintStyle;
+                _updateHoverStyle();
+                if (!doNotRepaint) self.repaint();
+            };
+
+            /**
+             * Gets the component's paint style.
+             *
+             * Returns:
+             * the component's paint style. if there is no hoverPaintStyle set then this will be the paint style used all the time, otherwise this is the style used when the mouse is not hovering.
+             */
+            this.getPaintStyle = function() {
+                return paintStyle;
+            };
+
+            /*
+             * Sets the paint style to use when the mouse is hovering over the element. This is null by default.
+             * The hover paint style is applied as extensions to the paintStyle; it does not entirely replace
+             * it.  This is because people will most likely want to change just one thing when hovering, say the
+             * color for example, but leave the rest of the appearance the same.
+             * 
+             * Parameters:
+             * 	style - Style to use when the mouse is hovering.
+             *  doNotRepaint - if true, the component will not be repainted.  useful when setting things up initially.
+             */
+            this.setHoverPaintStyle = function(style, doNotRepaint) {
+                hoverPaintStyle = style;
+                _updateHoverStyle();
+                if (!doNotRepaint) self.repaint();
+            };
+
+            /**
+             * Gets the component's hover paint style.
+             *
+             * Returns:
+             * the component's hover paint style. may be null.
+             */
+            this.getHoverPaintStyle = function() {
+                return hoverPaintStyle;
+            };
+
+            /*
+             * sets/unsets the hover state of this element.
+             * 
+             * Parameters:
+             * 	hover - hover state boolean
+             * 	ignoreAttachedElements - if true, does not notify any attached elements of the change in hover state.  used mostly to avoid infinite loops.
+             */
+            this.setHover = function(hover, ignoreAttachedElements, timestamp) {
+                // while dragging, we ignore these events.  this keeps the UI from flashing and
+                // swishing and whatevering.
+                if (!self._jsPlumb.currentlyDragging && !self._jsPlumb.isHoverSuspended()) {
+
+                    _hover = hover;
+                    if (self.hoverClass != null && self.canvas != null) {
+                        if (hover)
+                            jpcl.addClass(self.canvas, self.hoverClass);
+                        else
+                            jpcl.removeClass(self.canvas, self.hoverClass);
+                    }
+                    if (hoverPaintStyle != null) {
+                        self.paintStyleInUse = hover ? hoverPaintStyle : paintStyle;
+                        timestamp = timestamp || _timestamp();
+                        self.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 (self.getAttachedElements && !ignoreAttachedElements)
+                        _updateAttachedElements(hover, _timestamp(), self);
+                }
+            };
+
+            this.isHover = function() { return _hover; };
+
+            var zIndex = null;
+            this.setZIndex = function(v) { zIndex = v; };
+            this.getZIndex = function() { return zIndex; };
+
+            var jpcl = jsPlumb.CurrentLibrary,
+                events = [ "click", "dblclick", "mouseenter", "mouseout", "mousemove", "mousedown", "mouseup", "contextmenu" ],
+                eventFilters = { "mouseout":"mouseexit" },
+                bindOne = function(o, c, evt) {
+                    var filteredEvent = eventFilters[evt] || evt;
+                    jpcl.bind(o, evt, function(ee) {
+                        c.fire(filteredEvent, c, ee);
+                    });
+                },
+                unbindOne = function(o, evt) {
+                    var filteredEvent = eventFilters[evt] || evt;
+                    jpcl.unbind(o, evt);
+                };
+
+            this.attachListeners = function(o, c) {
+                for (var i = 0; i < events.length; i++) {
+                    bindOne(o, c, events[i]);
+                }
+            };
+
+            var _updateAttachedElements = function(state, timestamp, sourceElement) {
+                var affectedElements = self.getAttachedElements();		// implemented in subclasses
+                if (affectedElements) {
+                    for (var i = 0; i < affectedElements.length; i++) {
+                        if (!sourceElement || sourceElement != affectedElements[i])
+                            affectedElements[i].setHover(state, true, timestamp);			// tell the attached elements not to inform their own attached elements.
+                    }
+                }
+            };
+
+            this.reattachListenersForElement = function(o) {
+                if (arguments.length > 1) {
+                    for (var i = 0; i < events.length; i++)
+                        unbindOne(o, events[i]);
+                    for (var i = 1; i < arguments.length; i++)
+                        self.attachListeners(o, arguments[i]);
+                }
+            };
+
+            /*
+             * TYPES
+             */
+            var _types = [],
+                _splitType = function(t) { return t == null ? null : t.split(" ")},
+                _applyTypes = function(doNotRepaint) {
+                    if (self.getDefaultType) {
+                        var td = self.getTypeDescriptor();
+
+                        var o = jsPlumbUtil.merge({}, self.getDefaultType());
+                        for (var i = 0; i < _types.length; i++)
+                            o = jsPlumbUtil.merge(o, self._jsPlumb.getType(_types[i], td));
+
+                        self.applyType(o);
+                        if (!doNotRepaint) self.repaint();
+                    }
+                };
+
+            self.setType = function(typeId, doNotRepaint) {
+                _types = _splitType(typeId) || [];
+                _applyTypes(doNotRepaint);
+            };
+
+            /*
+             * Function : getType
+             * Gets the 'types' of this component.
+             */
+            self.getType = function() {
+                return _types;
+            };
+
+            self.hasType = function(typeId) {
+                return jsPlumbUtil.indexOf(_types, typeId) != -1;
+            };
+
+            self.addType = function(typeId, doNotRepaint) {
+                var t = _splitType(typeId), _cont = false;
+                if (t != null) {
+                    for (var i = 0; i < t.length; i++) {
+                        if (!self.hasType(t[i])) {
+                            _types.push(t[i]);
+                            _cont = true;
+                        }
+                    }
+                    if (_cont) _applyTypes(doNotRepaint);
+                }
+            };
+
+            self.removeType = function(typeId, doNotRepaint) {
+                var t = _splitType(typeId), _cont = false, _one = function(tt) {
+                    var idx = jsPlumbUtil.indexOf(_types, tt);
+                    if (idx != -1) {
+                        _types.splice(idx, 1);
+                        return true;
+                    }
+                    return false;
+                };
+
+                if (t != null) {
+                    for (var i = 0; i < t.length; i++) {
+                        _cont = _one(t[i]) || _cont;
+                    }
+                    if (_cont) _applyTypes(doNotRepaint);
+                }
+            };
+
+            self.toggleType = function(typeId, doNotRepaint) {
+                var t = _splitType(typeId);
+                if (t != null) {
+                    for (var i = 0; i < t.length; i++) {
+                        var idx = jsPlumbUtil.indexOf(_types, t[i]);
+                        if (idx != -1)
+                            _types.splice(idx, 1);
+                        else
+                            _types.push(t[i]);
+                    }
+
+                    _applyTypes(doNotRepaint);
+                }
+            };
+
+            this.applyType = function(t) {
+                self.setPaintStyle(t.paintStyle);
+                self.setHoverPaintStyle(t.hoverPaintStyle);
+                if (t.parameters){
+                    for (var i in t.parameters)
+                        self.setParameter(i, t.parameters[i]);
+                }
+            };
+
+        },
+
+        overlayCapableJsPlumbUIComponent = function(params) {
+            jsPlumbUIComponent.apply(this, arguments);
+            var self = this;
+            this.overlays = [];
+
+            var processOverlay = function(o) {
+                    var _newOverlay = null;
+                    if (_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:self, _jsPlumb:self._jsPlumb}, o[1]);
+                        if (o.length == 3) jsPlumb.extend(p, o[2]);
+                        _newOverlay = new jsPlumb.Overlays[self._jsPlumb.getRenderMode()][type](p);
+                        if (p.events) {
+                            for (var evt in p.events) {
+                                _newOverlay.bind(evt, p.events[evt]);
+                            }
+                        }
+                    } else if (o.constructor == String) {
+                        _newOverlay = new jsPlumb.Overlays[self._jsPlumb.getRenderMode()][o]({component:self, _jsPlumb:self._jsPlumb});
+                    } else {
+                        _newOverlay = o;
+                    }
+
+                    self.overlays.push(_newOverlay);
+                },
+                calculateOverlaysToAdd = function(params) {
+                    var defaultKeys = self.defaultOverlayKeys || [],
+                        o = params.overlays,
+                        checkKey = function(k) {
+                            return self._jsPlumb.Defaults[k] || jsPlumb.Defaults[k] || [];
+                        };
+
+                    if (!o) o = [];
+
+                    for (var i = 0; i < defaultKeys.length; i++)
+                        o.unshift.apply(o, checkKey(defaultKeys[i]));
+
+                    return o;
+                }
+
+            var _overlays = calculateOverlaysToAdd(params);//params.overlays || self._jsPlumb.Defaults.Overlays;
+            if (_overlays) {
+                for (var i = 0; i < _overlays.length; i++) {
+                    processOverlay(_overlays[i]);
+                }
+            }
+
+            // overlay finder helper method
+            var _getOverlayIndex = function(id) {
+                var idx = -1;
+                for (var i = 0; i < self.overlays.length; i++) {
+                    if (id === self.overlays[i].id) {
+                        idx = i;
+                        break;
+                    }
+                }
+                return idx;
+            };
+
+            this.addOverlay = function(overlay, doNotRepaint) {
+                processOverlay(overlay);
+                if (!doNotRepaint) self.repaint();
+            };
+
+            this.getOverlay = function(id) {
+                var idx = _getOverlayIndex(id);
+                return idx >= 0 ? self.overlays[idx] : null;
+            };
+
+            this.getOverlays = function() {
+                return self.overlays;
+            };
+
+            this.hideOverlay = function(id) {
+                var o = self.getOverlay(id);
+                if (o) o.hide();
+            };
+
+            this.hideOverlays = function() {
+                for (var i = 0; i < self.overlays.length; i++)
+                    self.overlays[i].hide();
+            };
+
+            this.showOverlay = function(id) {
+                var o = self.getOverlay(id);
+                if (o) o.show();
+            };
+
+            this.showOverlays = function() {
+                for (var i = 0; i < self.overlays.length; i++)
+                    self.overlays[i].show();
+            };
+
+            this.removeAllOverlays = function() {
+                for (var i = 0; i < self.overlays.length; i++) {
+                    if (self.overlays[i].cleanup) self.overlays[i].cleanup();
+                }
+
+                self.overlays.splice(0, self.overlays.length);
+                self.repaint();
+            };
+
+            this.removeOverlay = function(overlayId) {
+                var idx = _getOverlayIndex(overlayId);
+                if (idx != -1) {
+                    var o = self.overlays[idx];
+                    if (o.cleanup) o.cleanup();
+                    self.overlays.splice(idx, 1);
+                }
+            };
+
+            this.removeOverlays = function() {
+                for (var i = 0; i < arguments.length; i++)
+                    self.removeOverlay(arguments[i]);
+            };
+
+            // this is a shortcut helper method to let people add a label as
+            // overlay.			
+            var _internalLabelOverlayId = "__label",
+                _makeLabelOverlay = function(params) {
+
+                    var _params = {
+                            cssClass:params.cssClass,
+                            labelStyle : this.labelStyle,
+                            id:_internalLabelOverlayId,
+                            component:self,
+                            _jsPlumb:self._jsPlumb
+                        },
+                        mergedParams = jsPlumb.extend(_params, params);
+
+                    return new jsPlumb.Overlays[self._jsPlumb.getRenderMode()].Label( mergedParams );
+                };
+            if (params.label) {
+                var loc = params.labelLocation || self.defaultLabelLocation || 0.5,
+                    labelStyle = params.labelStyle || self._jsPlumb.Defaults.LabelStyle || jsPlumb.Defaults.LabelStyle;
+                this.overlays.push(_makeLabelOverlay({
+                    label:params.label,
+                    location:loc,
+                    labelStyle:labelStyle
+                }));
+            }
+
+
+            this.setLabel = function(l) {
+                var lo = self.getOverlay(_internalLabelOverlayId);
+                if (!lo) {
+                    var params = l.constructor == String || l.constructor == Function ? { label:l } : l;
+                    lo = _makeLabelOverlay(params);
+                    this.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 (!self._jsPlumb.isSuspendDrawing())
+                    self.repaint();
+            };
+
+
+            this.getLabel = function() {
+                var lo = self.getOverlay(_internalLabelOverlayId);
+                return lo != null ? lo.getLabel() : null;
+            };
+
+
+            this.getLabelOverlay = function() {
+                return self.getOverlay(_internalLabelOverlayId);
+            };
+
+            var superAt = this.applyType;
+            this.applyType = function(t) {
+                superAt(t);
+                self.removeAllOverlays();
+                if (t.overlays) {
+                    for (var i = 0; i < t.overlays.length; i++)
+                        self.addOverlay(t.overlays[i], true);
+                }
+            };
+        },
+
+        _bindListeners = function(obj, _self, _hoverFunction) {
+            obj.bind("click", function(ep, e) { _self.fire("click", _self, e); });
+            obj.bind("dblclick", function(ep, e) { _self.fire("dblclick", _self, e); });
+            obj.bind("contextmenu", function(ep, e) { _self.fire("contextmenu", _self, e); });
+            obj.bind("mouseenter", function(ep, e) {
+                if (!_self.isHover()) {
+                    _hoverFunction(true);
+                    _self.fire("mouseenter", _self, e);
+                }
+            });
+            obj.bind("mouseexit", function(ep, e) {
+                if (_self.isHover()) {
+                    _hoverFunction(false);
+                    _self.fire("mouseexit", _self, e);
+                }
+            });
+        };
+
+    var _jsPlumbInstanceIndex = 0,
+        getInstanceIndex = function() {
+            var i = _jsPlumbInstanceIndex + 1;
+            _jsPlumbInstanceIndex++;
+            return i;
+        };
+
+    var jsPlumbInstance = function(_defaults) {
+
+        /*
+         * Property: Defaults 
+         * 
+         * These are the default settings for jsPlumb.  They are what will be used if you do not supply specific pieces of information 
+         * to the various API calls. A convenient way to implement your own look and feel can be to override these defaults 
+         * by including a script somewhere after the jsPlumb include, but before you make any calls to jsPlumb.
+         * 
+         * Properties:
+         * 	-	*Anchor*				    The default anchor to use for all connections (both source and target). Default is "BottomCenter".
+         * 	-	*Anchors*				    The default anchors to use ([source, target]) for all connections. Defaults are ["BottomCenter", "BottomCenter"].
+         *  -   *ConnectionsDetachable*		Whether or not connections are detachable by default (using the mouse). Defaults to true.
+         *  -   *ConnectionOverlays*		The default overlay definitions for Connections. Defaults to an empty list.
+         * 	-	*Connector*				The default connector definition to use for all connections.  Default is "Bezier".
+         * 	-   *ConnectorZIndex*       Optional value for the z-index of Connections that are not in the hover state. If you set this, jsPlumb will set the z-index of all created Connections to be this value, and the z-index of any Connections in the hover state to be this value plus one. This brings hovered connections up on top of others, which is a nice effect in busy UIs.
+         *  -   *Container*				Optional selector or element id that instructs jsPlumb to append elements it creates to a specific element.
+         * 	-	*DragOptions*			The default drag options to pass in to connect, makeTarget and addEndpoint calls. Default is empty.
+         * 	-	*DropOptions*			The default drop options to pass in to connect, makeTarget and addEndpoint calls. Default is empty.
+         * 	-	*Endpoint*				The default endpoint definition to use for all connections (both source and target).  Default is "Dot".
+         *  -   *EndpointOverlays*		The default overlay definitions for Endpoints. Defaults to an empty list.
+         * 	-	*Endpoints*				The default endpoint definitions ([ source, target ]) to use for all connections.  Defaults are ["Dot", "Dot"].
+         * 	-	*EndpointStyle*			The default style definition to use for all endpoints. Default is fillStyle:"#456".
+         * 	-	*EndpointStyles*		The default style definitions ([ source, target ]) to use for all endpoints.  Defaults are empty.
+         * 	-	*EndpointHoverStyle*	The default hover style definition to use for all endpoints. Default is null.
+         * 	-	*EndpointHoverStyles*	The default hover style definitions ([ source, target ]) to use for all endpoints. Defaults are null.
+         * 	-	*HoverPaintStyle*		The default hover style definition to use for all connections. Defaults are null.
+         * 	-	*LabelStyle*			The default style to use for label overlays on connections.
+         * 	-	*LogEnabled*			Whether or not the jsPlumb log is enabled. defaults to false.
+         * 	-	*Overlays*				The default overlay definitions (for both Connections and Endpoint). Defaults to an empty list.
+         * 	-	*MaxConnections*		The default maximum number of connections for an Endpoint.  Defaults to 1.		 
+         * 	-	*PaintStyle*			The default paint style for a connection. Default is line width of 8 pixels, with color "#456".
+         * 	-	*ReattachConnections*	Whether or not to reattach Connections that a user has detached with the mouse and then dropped. Default is false.
+         * 	-	*RenderMode*			What mode to use to paint with.  If you're on IE<9, you don't really get to choose this.  You'll just get VML.  Otherwise, the jsPlumb default is to use SVG.
+         * 	-	*Scope*				The default "scope" to use for connections. Scope lets you assign connections to different categories. 
+         */
+        this.Defaults = {
+            Anchor : "BottomCenter",
+            Anchors : [ null, null ],
+            ConnectionsDetachable : true,
+            ConnectionOverlays : [ ],
+            Connector : "Bezier",
+            ConnectorZIndex : null,
+            Container : null,
+            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;
+
+        var _connectionTypes = { }, _endpointTypes = {};
+        this.registerConnectionType = function(id, type) {
+            _connectionTypes[id] = jsPlumb.extend({}, type);
+        };
+        this.registerConnectionTypes = function(types) {
+            for (var i in types)
+                _connectionTypes[i] = jsPlumb.extend({}, types[i]);
+        };
+        this.registerEndpointType = function(id, type) {
+            _endpointTypes[id] = jsPlumb.extend({}, type);
+        };
+        this.registerEndpointTypes = function(types) {
+            for (var i in types)
+                _endpointTypes[i] = jsPlumb.extend({}, types[i]);
+        };
+        this.getType = function(id, typeDescriptor) {
+            return typeDescriptor ===  "connection" ? _connectionTypes[id] : _endpointTypes[id];
+        };
+
+        jsPlumbUtil.EventGenerator.apply(this);
+        var _currentInstance = this,
+            _instanceIndex = getInstanceIndex(),
+            _bb = _currentInstance.bind,
+            _initialDefaults = {},
+            _zoom = 1;
+
+        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];
+            }
+        };
+
+        _currentInstance.restoreDefaults = function() {
+            _currentInstance.Defaults = jsPlumb.extend({}, _initialDefaults);
+        };
+
+        var log = null,
+            resizeTimer = null,
+            initialized = false,
+            connectionsByScope = {},
+            /**
+             * 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 = {},
+            canvasList = [],
+            sizes = [],
+        //listeners = {}, // a map: keys are event types, values are lists of listeners.
+            DEFAULT_SCOPE = this.Defaults.Scope,
+            renderMode = null,  // will be set in init()							
+
+            /**
+             * helper method to add an item to a list, creating the list if it does
+             * not yet exist.
+             */
+                _addToList = function(map, key, value) {
+                var l = map[key];
+                if (l == null) {
+                    l = [];
+                    map[key] = l;
+                }
+                l.push(value);
+                return l;
+            },
+
+            /**
+             * 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)
+                //document.body.appendChild(el);
+                    jsPlumbAdapter.appendToRoot(el);
+                else
+                    jsPlumb.CurrentLibrary.appendElement(el, parent);
+            },
+
+            _curIdStamp = 1,
+            _idstamp = function() { return "" + _curIdStamp++; },
+
+            /**
+             * 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.
+             */
+                _draw = function(element, ui, timestamp) {
+
+                // TOD is it correct to filter by headless at this top level? how would a headless adapter ever repaint?
+                if (!jsPlumbAdapter.headless && !_suspendDrawing) {
+                    var id = _getAttribute(element, "id"),
+                        repaintEls = _currentInstance.dragManager.getElementsForDraggable(id);
+
+                    if (timestamp == null) timestamp = _timestamp();
+
+                    _currentInstance.anchorManager.redraw(id, ui, timestamp);
+
+                    if (repaintEls) {
+                        for (var i in repaintEls) {
+                            _currentInstance.anchorManager.redraw(repaintEls[i].id, ui, timestamp, repaintEls[i].offset);
+                        }
+                    }
+                }
+            },
+
+            /**
+             * 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;
+                if (_isArray(element)) {
+                    retVal = [];
+                    for ( var i = 0; i < element.length; i++) {
+                        var el = _getElementObject(element[i]), id = _getAttribute(el, "id");
+                        retVal.push(fn(el, id)); // append return values to what we will return
+                    }
+                } else {
+                    var el = _getElementObject(element), id = _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.
+             */
+                _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] = _wrap(options[startEvent], function() {
+                                _currentInstance.setHoverSuspended(true);
+                            });
+
+                            options[dragEvent] = _wrap(options[dragEvent], function() {
+                                var ui = jpcl.getUIPosition(arguments, _currentInstance.getZoom());
+                                _draw(element, ui);
+                                _addClass(element, "jsPlumb_dragged");
+                            });
+                            options[stopEvent] = _wrap(options[stopEvent], function() {
+                                var ui = jpcl.getUIPosition(arguments, _currentInstance.getZoom());
+                                _draw(element, ui);
+                                _removeClass(element, "jsPlumb_dragged");
+                                _currentInstance.setHoverSuspended(false);
+                            });
+                            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.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( {
+                    sourceIsNew:true,
+                    targetIsNew:true
+                }, params);
+                if (referenceParams) jsPlumb.extend(_p, referenceParams);
+
+                // hotwire endpoints passed as source or target to sourceEndpoint/targetEndpoint, respectively.
+                if (_p.source && _p.source.endpoint) _p.sourceEndpoint = _p.source;
+                if (_p.source && _p.target.endpoint) _p.targetEndpoint = _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()) {
+                    _log(_currentInstance, "could not add connection; source endpoint is full");
+                    return;
+                }
+
+                // target:
+                if (_p.targetEndpoint && _p.targetEndpoint.isFull()) {
+                    _log(_currentInstance, "could not add connection; target endpoint is full");
+                    return;
+                }
+
+                // at this point, if we have source or target Endpoints, they were not new and we should mark the
+                // flag to reflect that.  this is for use later with the deleteEndpointsOnDetach flag.
+                if (_p.sourceEndpoint) _p.sourceIsNew = false;
+                if (_p.targetEndpoint) _p.targetIsNew = false;
+
+                // 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; i < _p.sourceEndpoint.connectorOverlays.length; i++) {
+                        _p.overlays.push(_p.sourceEndpoint.connectorOverlays[i]);
+                    }
+                }
+
+                // tooltip.  params.tooltip takes precedence, then sourceEndpoint.connectorTooltip.
+                _p.tooltip = params.tooltip;
+                if (!_p.tooltip && _p.sourceEndpoint && _p.sourceEndpoint.connectorTooltip)
+                    _p.tooltip = _p.sourceEndpoint.connectorTooltip;
+
+                // 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.
+                if (_p.target && !_p.target.endpoint && !_p.targetEndpoint && !_p.newConnection) {
+                    var tid = _getId(_p.target),
+                        tep =_targetEndpointDefinitions[tid],
+                        existingUniqueEndpoint = _targetEndpoints[tid];
+
+                    if (tep) {
+                        // if target not enabled, return.
+                        if (!_targetsEnabled[tid]) return;
+
+                        // check for max connections??						
+                        var newEndpoint = existingUniqueEndpoint != null ? existingUniqueEndpoint : _currentInstance.addEndpoint(_p.target, tep);
+                        if (_targetEndpointsUnique[tid]) _targetEndpoints[tid] = newEndpoint;
+                        _p.targetEndpoint = newEndpoint;
+                        newEndpoint._makeTargetCreator = true;
+                        _p.targetIsNew = true;
+                    }
+                }
+
+                // same thing, but for source.
+                if (_p.source && !_p.source.endpoint && !_p.sourceEndpoint && !_p.newConnection) {
+                    var tid = _getId(_p.source),
+                        tep = _sourceEndpointDefinitions[tid],
+                        existingUniqueEndpoint = _sourceEndpoints[tid];
+
+                    if (tep) {
+                        // if source not enabled, return.					
+                        if (!_sourcesEnabled[tid]) return;
+
+                        var newEndpoint = existingUniqueEndpoint != null ? existingUniqueEndpoint : _currentInstance.addEndpoint(_p.source, tep);
+                        if (_sourceEndpointsUnique[tid]) _sourceEndpoints[tid] = newEndpoint;
+                        _p.sourceEndpoint = newEndpoint;
+                        _p.sourceIsNew = true;
+                    }
+                }
+
+                return _p;
+            },
+
+            _newConnection = function(params) {
+                var connectionFunc = _currentInstance.Defaults.ConnectionType || _currentInstance.getDefaultConnectionType(),
+                    endpointFunc = _currentInstance.Defaults.EndpointType || 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;
+                var con = new connectionFunc(params);
+                con.id = "con_" + _idstamp();
+                _eventFireProxy("click", "click", con);
+                _eventFireProxy("dblclick", "dblclick", con);
+                _eventFireProxy("contextmenu", "contextmenu", con);
+                return con;
+            },
+
+            /**
+             * adds the connection to the backing model, fires an event if necessary and then redraws
+             */
+                _finaliseConnection = function(jpc, params, originalEvent) {
+                params = params || {};
+                // add to list of connections (by scope).
+                if (!jpc.suspendedEndpoint)
+                    _addToList(connectionsByScope, jpc.scope, jpc);
+                // 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("jsPlumbConnection", eventArgs, originalEvent);
+                    // this is from 1.3.11 onwards. "jsPlumbConnection" always felt so unnecessary, so
+                    // I've added this alias in 1.3.11, with a view to removing "jsPlumbConnection" completely in a future version. be aware, of course, you should only register listeners for one or the other of these events.
+                    _currentInstance.fire("connection", eventArgs, originalEvent);
+                }
+
+                // 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.
+                _currentInstance.anchorManager.newConnection(jpc);
+                // force a paint
+                _draw(jpc.source);
+            },
+
+            _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 || Endpoint;
+                params.parent = _getParentFromParams(params);
+                params["_jsPlumb"] = _currentInstance;
+                var ep = new endpointFunc(params);
+                ep.id = "ep_" + _idstamp();
+                _eventFireProxy("click", "endpointClick", ep);
+                _eventFireProxy("dblclick", "endpointDblClick", ep);
+                _eventFireProxy("contextmenu", "contextmenu", ep);
+                if (!jsPlumbAdapter.headless)
+                    _currentInstance.dragManager.endpointAdded(params.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; i < endpoints.length; i++) {
+                        for ( var j = 0; j < endpoints[i].connections.length; 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]);
+                    }
+                }
+            },
+            /**
+             * perform an operation on all elements.
+             */
+                _operationOnAll = function(func) {
+                for ( var elId in endpointsByElement) {
+                    _operation(elId, func);
+                }
+            },
+
+            /**
+             * helper to remove an element from the DOM.
+             */
+                _removeElement = function(element, parent) {
+                if (element != null && element.parentNode != null) {
+                    element.parentNode.removeChild(element);
+                }
+            },
+            /**
+             * helper to remove a list of elements from the DOM.
+             */
+                _removeElements = function(elements, parent) {
+                for ( var i = 0; i < elements.length; i++)
+                    _removeElement(elements[i], parent);
+            },
+            /**
+             * Sets whether or not the given element(s) should be draggable,
+             * regardless of what a particular plumb command may request.
+             *
+             * @param element
+             *            May be a string, a element objects, or a list of
+             *            strings/elements.
+             * @param draggable
+             *            Whether or not the given element(s) should be draggable.
+             */
+                _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 id = _getAttribute(el, "id");
+                _operation(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 === 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).
+             *
+             * @param el
+             *            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.
+             *
+             * @param elId
+             *            Id of the element in question
+             */
+                _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;
+                if (_suspendDrawing && !timestamp) timestamp = _suspendedAt;
+                if (!recalc) {
+                    if (timestamp && timestamp === offsetTimestamps[elId])
+                        return offsets[elId];
+                }
+                if (recalc || !offset) { // if forced repaint or no offset available, we recalculate.
+                    // get the current size and offset, and store them
+                    var s = _getElementObject(elId);
+                    if (s != null) {
+                        sizes[elId] = _getSize(s);
+                        offsets[elId] = _getOffset(s, _currentInstance);
+                        offsetTimestamps[elId] = timestamp;
+                    }
+                } else {
+                    offsets[elId] = offset;
+                    if (sizes[elId] == null) {
+                        var s = _getElementObject(elId);
+                        if (s != null) sizes[elId] = _getSize(s);
+                    }
+                }
+
+                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 offsets[elId];
+            },
+
+        // TODO comparison performance
+            _getCachedData = function(elId) {
+                var o = offsets[elId];
+                if (!o) o = _updateOffset({elId:elId});
+                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) {
+                var ele = _getElementObject(element);
+                var id = _getAttribute(ele, "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) _setAttribute(ele, "id", id);
+                }
+                return id;
+            },
+
+            /**
+             * wraps one function with another, creating a placeholder for the
+             * wrapped function if it was null. this is used to wrap the various
+             * drag/drop event functions - to allow jsPlumb to be notified of
+             * important lifecycle events without imposing itself on the user's
+             * drag/drop functionality. TODO: determine whether or not we should
+             * support an error handler concept, if one of the functions fails.
+             *
+             * @param wrappedFunction original function to wrap; may be null.
+             * @param newFunction function to wrap the original with.
+             * @param returnOnThisValue Optional. Indicates that the wrappedFunction should
+             * not be executed if the newFunction returns a value matching 'returnOnThisValue'.
+             * note that this is a simple comparison and only works for primitives right now.
+             */
+                _wrap = function(wrappedFunction, newFunction, returnOnThisValue) {
+                wrappedFunction = wrappedFunction || function() { };
+                newFunction = newFunction || function() { };
+                return function() {
+                    var r = null;
+                    try {
+                        r = newFunction.apply(this, arguments);
+                    } catch (e) {
+                        _log(_currentInstance, "jsPlumb function failed : " + e);
+                    }
+                    if (returnOnThisValue == null || (r !== returnOnThisValue)) {
+                        try {
+                            wrappedFunction.apply(this, arguments);
+                        } catch (e) {
+                            _log(_currentInstance, "wrapped function failed : " + e);
+                        }
+                    }
+                    return r;
+                };
+            };
+
+        /*
+         * Property: connectorClass 
+         *   The CSS class to set on Connection elements. This value is a String and can have multiple classes; the entire String is appended as-is.
+         */
+        this.connectorClass = "_jsPlumb_connector";
+
+        /*
+         * Property: endpointClass 
+         *   The CSS class to set on Endpoint elements. This value is a String and can have multiple classes; the entire String is appended as-is.
+         */
+        this.endpointClass = "_jsPlumb_endpoint";
+
+        /*
+         * Property: overlayClass 
+         * The CSS class to set on an Overlay that is an HTML element. This value is a String and can have multiple classes; the entire String is appended as-is.
+         */
+        this.overlayClass = "_jsPlumb_overlay";
+
+        this.Anchors = {};
+
+        this.Connectors = {
+            "canvas":{},
+            "svg":{},
+            "vml":{}
+        };
+
+        this.Endpoints = {
+            "canvas":{},
+            "svg":{},
+            "vml":{}
+        };
+
+        this.Overlays = {
+            "canvas":{},
+            "svg":{},
+            "vml":{}
+        };
+
+// ************************ PLACEHOLDER DOC ENTRIES FOR NATURAL DOCS *****************************************
+
+        /*
+         * Function: importDefaults
+         * Imports all the given defaults into this instance of jsPlumb.		
+         */
+
+        /*
+         * Function: restoreDefaults
+         * Restores the default settings to "factory" values.
+         */
+
+        /*
+         * Function: bind
+         * Bind to an event on jsPlumb.  
+         * 
+         * Parameters:
+         * 	event - the event to bind.  Available events on jsPlumb are:
+         *         - *jsPlumbConnection* 			: 	notification that a new Connection was established.  jsPlumb passes the new Connection to the callback.
+         *         - *jsPlumbConnectionDetached* 	: 	notification that a Connection was detached.  jsPlumb passes the detached Connection to the callback.
+         *         - *click*						:	notification that a Connection was clicked.  jsPlumb passes the Connection that was clicked to the callback.
+         *         - *dblclick*						:	notification that a Connection was double clicked.  jsPlumb passes the Connection that was double clicked to the callback.
+         *         - *endpointClick*				:	notification that an Endpoint was clicked.  jsPlumb passes the Endpoint that was clicked to the callback.
+         *         - *endpointDblClick*				:	notification that an Endpoint was double clicked.  jsPlumb passes the Endpoint that was double clicked to the callback.
+         *         - *beforeDrop*					: 	notification that a Connection is about to be dropped. Returning false from this method cancels the drop. jsPlumb passes { sourceId, targetId, scope, connection, dropEndpoint } to your callback. For more information, refer to the jsPlumb documentation.
+         *         - *beforeDetach*					: 	notification that a Connection is about to be detached. Returning false from this method cancels the detach. jsPlumb passes the Connection to your callback. For more information, refer to the jsPlumb documentation.
+         *		   - *connectionDrag* 				:   notification that an existing Connection is being dragged. jsPlumb passes the Connection to your callback function.
+         *         - *connectionDragEnd*            :   notification that the drag of an existing Connection has ended.  jsPlumb passes the Connection to your callback function.
+         *         
+         *  callback - function to callback. This function will be passed the Connection/Endpoint that caused the event, and also the original event.    
+         */
+
+        /*
+         * Function: unbind
+         * Clears either all listeners, or listeners for some specific event.
+         * 
+         * Parameters:
+         * 	event	-	optional. constrains the clear to just listeners for this event.
+         */
+
+        /*
+         * Function: addClass
+         * Add class(es) to some element(s).
+         *
+         * Parameters:
+         * 	el			-	element id, dom element, or selector representing the element(s) to add class(es) to.
+         * 	clazz		-	string representing the class(es) to add. may contain multiple classes separated by spaces.
+         */
+
+        /*
+         * Function: removeClass
+         * Remove class from some selement(s).
+         *
+         * Parameters:
+         * 	el			-	element id, dom element, or selector representing the element(s) to remove a class from.
+         * 	clazz		-	string representing the class to remove. 
+         */
+
+        /*
+         * Function: hasClass
+         * Checks if an element has some class.
+         *
+         * Parameters:
+         * 	el			-	element id, dom element, or selector representing the element to test. If the selector matches multiple elements, we return the test for the first element in the selector only.
+         * 	clazz		-	string representing the class to test for. 
+         */
+
+// *************** END OF PLACEHOLDER DOC ENTRIES FOR NATURAL DOCS ***********************************************************		
+
+
+// --------------------------- jsPLumbInstance public API ---------------------------------------------------------
+
+        this.addClass = function(el, clazz) { return jsPlumb.CurrentLibrary.addClass(el, clazz); };
+        this.removeClass = function(el, clazz) { return jsPlumb.CurrentLibrary.removeClass(el, clazz); };
+        this.hasClass = function(el, clazz) { return jsPlumb.CurrentLibrary.hasClass(el, clazz); };
+
+        /*
+         Function: addEndpoint 
+
+         Adds an <Endpoint> to a given element or elements.
+
+         Parameters:
+
+         el - Element to add the endpoint to. Either an element id, a selector representing some element(s), or an array of either of these. 
+         params - Object containing Endpoint constructor arguments.  For more information, see <Endpoint>.
+         referenceParams - Object containing more Endpoint constructor arguments; it will be merged with params by jsPlumb.  You would use this if you had some 
+         shared parameters that you wanted to reuse when you added Endpoints to a number of elements. The allowed values in
+         this object are anything that 'params' can contain.  See <Endpoint>.
+
+         Returns: 
+         The newly created <Endpoint>, if el referred to a single element.  Otherwise, an array of newly created <Endpoint>s. 
+
+         See Also: 
+         <addEndpoints>
+         */
+        this.addEndpoint = function(el, params, referenceParams) {
+            referenceParams = referenceParams || {};
+            var p = jsPlumb.extend({}, re

<TRUNCATED>