/demo/js/1.3.3/jquery.jsPlumb-1.3.3-all.js

http://jsplumb.googlecode.com/ · JavaScript · 5929 lines · 3395 code · 556 blank · 1978 comment · 895 complexity · 31ab9d70fbcb2c6a22c6ae14a849597c MD5 · raw file

Large files are truncated click here to view the full file

  1. /*
  2. * jsPlumb
  3. *
  4. * Title:jsPlumb 1.3.3
  5. *
  6. * Provides a way to visually connect elements on an HTML page, using either SVG, Canvas
  7. * elements, or VML.
  8. *
  9. * This file contains the jsPlumb core code.
  10. *
  11. * Copyright (c) 2010 - 2011 Simon Porritt (simon.porritt@gmail.com)
  12. *
  13. * http://jsplumb.org
  14. * http://code.google.com/p/jsplumb
  15. *
  16. * Triple licensed under the MIT, GPL2 and Beer licenses.
  17. */
  18. ;(function() {
  19. /**
  20. * Class:jsPlumb
  21. * The jsPlumb engine, registered as a static object in the window. This object contains all of the methods you will use to
  22. * create and maintain Connections and Endpoints.
  23. */
  24. var canvasAvailable = !!document.createElement('canvas').getContext;
  25. var svgAvailable = !!window.SVGAngle || document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1");
  26. // TODO what is a good test for VML availability? aside from just assuming its there because nothing else is.
  27. var vmlAvailable = !(canvasAvailable | svgAvailable);
  28. var _findIndex = function(a, v, b, s) {
  29. var _eq = function(o1, o2) {
  30. if (o1 === o2)
  31. return true;
  32. else if (typeof o1 == "object" && typeof o2 == "object") {
  33. var same = true;
  34. for ( var propertyName in o1) {
  35. if (!_eq(o1[propertyName], o2[propertyName])) {
  36. same = false;
  37. break;
  38. }
  39. }
  40. for ( var propertyName in o2) {
  41. if (!_eq(o2[propertyName], o1[propertyName])) {
  42. same = false;
  43. break;
  44. }
  45. }
  46. return same;
  47. }
  48. };
  49. for ( var i = +b || 0, l = a.length; i < l; i++) {
  50. if (_eq(a[i], v))
  51. return i;
  52. }
  53. return -1;
  54. };
  55. /**
  56. * helper method to add an item to a list, creating the list if it does
  57. * not yet exist.
  58. */
  59. var _addToList = function(map, key, value) {
  60. var l = map[key];
  61. if (l == null) {
  62. l = [];
  63. map[key] = l;
  64. }
  65. l.push(value);
  66. return l;
  67. };
  68. var _connectionBeingDragged = null;
  69. var _getAttribute = function(el, attName) { return jsPlumb.CurrentLibrary.getAttribute(_getElementObject(el), attName); },
  70. _setAttribute = function(el, attName, attValue) { jsPlumb.CurrentLibrary.setAttribute(_getElementObject(el), attName, attValue); },
  71. _addClass = function(el, clazz) { jsPlumb.CurrentLibrary.addClass(_getElementObject(el), clazz); },
  72. _hasClass = function(el, clazz) { return jsPlumb.CurrentLibrary.hasClass(_getElementObject(el), clazz); },
  73. _removeClass = function(el, clazz) { jsPlumb.CurrentLibrary.removeClass(_getElementObject(el), clazz); },
  74. _getElementObject = function(el) { return jsPlumb.CurrentLibrary.getElementObject(el); },
  75. _getOffset = function(el) { return jsPlumb.CurrentLibrary.getOffset(_getElementObject(el)); },
  76. _getSize = function(el) { return jsPlumb.CurrentLibrary.getSize(_getElementObject(el)); },
  77. _log = function(jsp, msg) {
  78. if (jsp.logEnabled && typeof console != "undefined")
  79. console.log(msg);
  80. };
  81. /**
  82. * EventGenerator
  83. * Superclass for objects that generate events - jsPlumb extends this, as does jsPlumbUIComponent, which all the UI elements extend.
  84. */
  85. var EventGenerator = function() {
  86. var _listeners = {}, self = this;
  87. // 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
  88. // jsPlumb, but it seems feasible that people might want to manipulate this list. the thinking is that we don't want event
  89. // 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"
  90. // event fails, because then my page has most likely not initialised. so i have this halfway-house solution. it will be interesting
  91. // to hear what other people think.
  92. var eventsToDieOn = [ "ready" ];
  93. /*
  94. * Binds a listener to an event.
  95. *
  96. * Parameters:
  97. * event - name of the event to bind to.
  98. * listener - function to execute.
  99. */
  100. this.bind = function(event, listener) {
  101. _addToList(_listeners, event, listener);
  102. };
  103. /*
  104. * Fires an update for the given event.
  105. *
  106. * Parameters:
  107. * event - event to fire
  108. * value - value to pass to the event listener(s).
  109. * o riginalEvent - the original event from the browser
  110. */
  111. this.fire = function(event, value, originalEvent) {
  112. if (_listeners[event]) {
  113. for ( var i = 0; i < _listeners[event].length; i++) {
  114. // doing it this way rather than catching and then possibly re-throwing means that an error propagated by this
  115. // method will have the whole call stack available in the debugger.
  116. if (_findIndex(eventsToDieOn, event) != -1)
  117. _listeners[event][i](value, originalEvent);
  118. else {
  119. // for events we don't want to die on, catch and log.
  120. try {
  121. _listeners[event][i](value, originalEvent);
  122. } catch (e) {
  123. _log("jsPlumb: fire failed for event " + event + " : " + e);
  124. }
  125. }
  126. }
  127. }
  128. };
  129. /*
  130. * Clears either all listeners, or listeners for some specific event.
  131. *
  132. * Parameters:
  133. * event - optional. constrains the clear to just listeners for this event.
  134. */
  135. this.clearListeners = function(event) {
  136. if (event) {
  137. delete _listeners[event];
  138. } else {
  139. delete _listeners;
  140. _listeners = {};
  141. }
  142. };
  143. };
  144. /*
  145. * Class:jsPlumbUIComponent
  146. * Abstract superclass for UI components Endpoint and Connection. Provides the abstraction of paintStyle/hoverPaintStyle,
  147. * and also extends EventGenerator to provide the bind and fire methods.
  148. */
  149. var jsPlumbUIComponent = function(params) {
  150. var self = this, a = arguments, _hover = false;
  151. self._jsPlumb = params["_jsPlumb"];
  152. // all components can generate events
  153. EventGenerator.apply(this);
  154. // all components get this clone function.
  155. // TODO issue 116 showed a problem with this - it seems 'a' that is in
  156. // the clone function's scope is shared by all invocations of it, the classic
  157. // JS closure problem. for now, jsPlumb does a version of this inline where
  158. // it used to call clone. but it would be nice to find some time to look
  159. // further at this.
  160. this.clone = function() {
  161. var o = new Object();
  162. self.constructor.apply(o, a);
  163. return o;
  164. };
  165. this.overlayPlacements = [],
  166. this.paintStyle = null,
  167. this.hoverPaintStyle = null;
  168. // helper method to update the hover style whenever it, or paintStyle, changes.
  169. // we use paintStyle as the foundation and merge hoverPaintStyle over the
  170. // top.
  171. var _updateHoverStyle = function() {
  172. if (self.paintStyle && self.hoverPaintStyle) {
  173. var mergedHoverStyle = {};
  174. jsPlumb.extend(mergedHoverStyle, self.paintStyle);
  175. jsPlumb.extend(mergedHoverStyle, self.hoverPaintStyle);
  176. delete self.hoverPaintStyle;
  177. // we want the fillStyle of paintStyle to override a gradient, if possible.
  178. if (mergedHoverStyle.gradient && self.paintStyle.fillStyle)
  179. delete mergedHoverStyle.gradient;
  180. self.hoverPaintStyle = mergedHoverStyle;
  181. }
  182. };
  183. /*
  184. * Sets the paint style and then repaints the element.
  185. *
  186. * Parameters:
  187. * style - Style to use.
  188. */
  189. this.setPaintStyle = function(style, doNotRepaint) {
  190. self.paintStyle = style;
  191. self.paintStyleInUse = self.paintStyle;
  192. _updateHoverStyle();
  193. if (!doNotRepaint) self.repaint();
  194. };
  195. /*
  196. * Sets the paint style to use when the mouse is hovering over the element. This is null by default.
  197. * The hover paint style is applied as extensions to the paintStyle; it does not entirely replace
  198. * it. This is because people will most likely want to change just one thing when hovering, say the
  199. * color for example, but leave the rest of the appearance the same.
  200. *
  201. * Parameters:
  202. * style - Style to use when the mouse is hovering.
  203. * doNotRepaint - if true, the component will not be repainted. useful when setting things up initially.
  204. */
  205. this.setHoverPaintStyle = function(style, doNotRepaint) {
  206. self.hoverPaintStyle = style;
  207. _updateHoverStyle();
  208. if (!doNotRepaint) self.repaint();
  209. };
  210. /*
  211. * sets/unsets the hover state of this element.
  212. *
  213. * Parameters:
  214. * hover - hover state boolean
  215. * ignoreAttachedElements - if true, does not notify any attached elements of the change in hover state. used mostly to avoid infinite loops.
  216. */
  217. this.setHover = function(hover, ignoreAttachedElements) {
  218. _hover = hover;
  219. if (self.hoverPaintStyle != null) {
  220. self.paintStyleInUse = hover ? self.hoverPaintStyle : self.paintStyle;
  221. self.repaint();
  222. // get the list of other affected elements. for a connection, its the endpoints. for an endpoint, its the connections! surprise.
  223. if (!ignoreAttachedElements)
  224. _updateAttachedElements(hover);
  225. }
  226. };
  227. this.isHover = function() {
  228. return _hover;
  229. };
  230. this.attachListeners = function(o, c) {
  231. var jpcl = jsPlumb.CurrentLibrary,
  232. events = [ "click", "dblclick", "mouseenter", "mouseout", "mousemove", "mousedown", "mouseup" ],
  233. eventFilters = { "mouseout":"mouseexit" },
  234. bindOne = function(evt) {
  235. var filteredEvent = eventFilters[evt] || evt;
  236. jpcl.bind(o, evt, function(ee) {
  237. c.fire(filteredEvent, c, ee);
  238. });
  239. };
  240. for (var i = 0; i < events.length; i++) {
  241. bindOne(events[i]);
  242. }
  243. };
  244. var _updateAttachedElements = function(state) {
  245. var affectedElements = self.getAttachedElements(); // implemented in subclasses
  246. if (affectedElements) {
  247. for (var i = 0; i < affectedElements.length; i++) {
  248. affectedElements[i].setHover(state, true); // tell the attached elements not to inform their own attached elements.
  249. }
  250. }
  251. };
  252. };
  253. var jsPlumbInstance = function(_defaults) {
  254. /*
  255. * Property: Defaults
  256. *
  257. * These are the default settings for jsPlumb. They are what will be used if you do not supply specific pieces of information
  258. * to the various API calls. A convenient way to implement your own look and feel can be to override these defaults
  259. * by including a script somewhere after the jsPlumb include, but before you make any calls to jsPlumb.
  260. *
  261. * Properties:
  262. * - *Anchor* The default anchor to use for all connections (both source and target). Default is "BottomCenter".
  263. * - *Anchors* The default anchors to use ([source, target]) for all connections. Defaults are ["BottomCenter", "BottomCenter"].
  264. * - *Connector* The default connector definition to use for all connections. Default is "Bezier".
  265. * - *Container* Optional selector or element id that instructs jsPlumb to append elements it creates to a specific element.
  266. * - *DragOptions* The default drag options to pass in to connect, makeTarget and addEndpoint calls. Default is empty.
  267. * - *DropOptions* The default drop options to pass in to connect, makeTarget and addEndpoint calls. Default is empty.
  268. * - *Endpoint* The default endpoint definition to use for all connections (both source and target). Default is "Dot".
  269. * - *Endpoints* The default endpoint definitions ([ source, target ]) to use for all connections. Defaults are ["Dot", "Dot"].
  270. * - *EndpointStyle* The default style definition to use for all endpoints. Default is fillStyle:"#456".
  271. * - *EndpointStyles* The default style definitions ([ source, target ]) to use for all endpoints. Defaults are empty.
  272. * - *EndpointHoverStyle* The default hover style definition to use for all endpoints. Default is null.
  273. * - *EndpointHoverStyles* The default hover style definitions ([ source, target ]) to use for all endpoints. Defaults are null.
  274. * - *HoverPaintStyle* The default hover style definition to use for all connections. Defaults are null.
  275. * - *LabelStyle* The default style to use for label overlays on connections.
  276. * - *LogEnabled* Whether or not the jsPlumb log is enabled. defaults to false.
  277. * - *Overlays* The default overlay definitions. Defaults to an empty list.
  278. * - *MaxConnections* The default maximum number of connections for an Endpoint. Defaults to 1.
  279. * - *MouseEventsEnabled* Whether or not mouse events are enabled when using the canvas renderer. Defaults to true.
  280. * The idea of this is just to give people a way to prevent all the mouse listeners from activating if they know they won't need mouse events.
  281. * - *PaintStyle* The default paint style for a connection. Default is line width of 8 pixels, with color "#456".
  282. * - *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 Canvas elements.
  283. * - *Scope* The default "scope" to use for connections. Scope lets you assign connections to different categories.
  284. */
  285. this.Defaults = {
  286. Anchor : "BottomCenter",
  287. Anchors : [ null, null ],
  288. Connector : "Bezier",
  289. DragOptions : { },
  290. DropOptions : { },
  291. Endpoint : "Dot",
  292. Endpoints : [ null, null ],
  293. EndpointStyle : { fillStyle : "#456" },
  294. EndpointStyles : [ null, null ],
  295. EndpointHoverStyle : null,
  296. EndpointHoverStyles : [ null, null ],
  297. HoverPaintStyle : null,
  298. LabelStyle : { color : "black" },
  299. LogEnabled : false,
  300. Overlays : [ ],
  301. MaxConnections : 1,
  302. MouseEventsEnabled : true,
  303. PaintStyle : { lineWidth : 8, strokeStyle : "#456" },
  304. RenderMode : "canvas",
  305. Scope : "_jsPlumb_DefaultScope"
  306. };
  307. if (_defaults) jsPlumb.extend(this.Defaults, _defaults);
  308. this.logEnabled = this.Defaults.LogEnabled;
  309. EventGenerator.apply(this);
  310. var _bb = this.bind;
  311. this.bind = function(event, fn) {
  312. if ("ready" === event && initialized) fn();
  313. else _bb(event, fn);
  314. };
  315. var _currentInstance = this,
  316. log = null,
  317. repaintFunction = function() {
  318. jsPlumb.repaintEverything();
  319. },
  320. automaticRepaint = true,
  321. repaintEverything = function() {
  322. if (automaticRepaint)
  323. repaintFunction();
  324. },
  325. resizeTimer = null,
  326. initialized = false,
  327. connectionsByScope = {},
  328. /**
  329. * map of element id -> endpoint lists. an element can have an arbitrary
  330. * number of endpoints on it, and not all of them have to be connected
  331. * to anything.
  332. */
  333. endpointsByElement = {},
  334. endpointsByUUID = {},
  335. offsets = {},
  336. offsetTimestamps = {},
  337. floatingConnections = {},
  338. draggableStates = {},
  339. _mouseEventsEnabled = this.Defaults.MouseEventsEnabled,
  340. _draggableByDefault = true,
  341. canvasList = [],
  342. sizes = [],
  343. listeners = {}, // a map: keys are event types, values are lists of listeners.
  344. DEFAULT_SCOPE = this.Defaults.Scope,
  345. renderMode = null, // will be set in init()
  346. /**
  347. * helper method to add an item to a list, creating the list if it does
  348. * not yet exist.
  349. */
  350. _addToList = function(map, key, value) {
  351. var l = map[key];
  352. if (l == null) {
  353. l = [];
  354. map[key] = l;
  355. }
  356. l.push(value);
  357. return l;
  358. },
  359. /**
  360. * appends an element to some other element, which is calculated as follows:
  361. *
  362. * 1. if jsPlumb.Defaults.Container exists, use that element.
  363. * 2. if the 'parent' parameter exists, use that.
  364. * 3. otherwise just use the document body.
  365. *
  366. */
  367. _appendElement = function(el, parent) {
  368. if (_currentInstance.Defaults.Container)
  369. jsPlumb.CurrentLibrary.appendElement(el, _currentInstance.Defaults.Container);
  370. else if (!parent)
  371. document.body.appendChild(el);
  372. else
  373. jsPlumb.CurrentLibrary.appendElement(el, parent);
  374. },
  375. /**
  376. * creates a timestamp, using milliseconds since 1970, but as a string.
  377. */
  378. _timestamp = function() { return "" + (new Date()).getTime(); },
  379. /**
  380. * YUI, for some reason, put the result of a Y.all call into an object that contains
  381. * a '_nodes' array, instead of handing back an array-like object like the other
  382. * libraries do.
  383. */
  384. _convertYUICollection = function(c) {
  385. return c._nodes ? c._nodes : c;
  386. },
  387. /**
  388. * Draws an endpoint and its connections.
  389. *
  390. * @param element element to draw (of type library specific element object)
  391. * @param ui UI object from current library's event system. optional.
  392. * @param timestamp timestamp for this paint cycle. used to speed things up a little by cutting down the amount of offset calculations we do.
  393. */
  394. _draw = function(element, ui, timestamp) {
  395. var id = _getAttribute(element, "id");
  396. var endpoints = endpointsByElement[id];
  397. if (!timestamp) timestamp = _timestamp();
  398. if (endpoints) {
  399. _updateOffset( { elId : id, offset : ui, recalc : false, timestamp : timestamp }); // timestamp is checked against last update cache; it is
  400. // valid for one paint cycle.
  401. var myOffset = offsets[id], myWH = sizes[id];
  402. for ( var i = 0; i < endpoints.length; i++) {
  403. endpoints[i].paint( { timestamp : timestamp, offset : myOffset, dimensions : myWH });
  404. var l = endpoints[i].connections;
  405. for ( var j = 0; j < l.length; j++) {
  406. l[j].paint( { elId : id, ui : ui, recalc : false, timestamp : timestamp }); // ...paint each connection.
  407. // then, check for dynamic endpoint; need to repaint it.
  408. var oIdx = l[j].endpoints[0] == endpoints[i] ? 1 : 0,
  409. otherEndpoint = l[j].endpoints[oIdx];
  410. if (otherEndpoint.anchor.isDynamic && !otherEndpoint.isFloating()) {
  411. _updateOffset( { elId : otherEndpoint.elementId, timestamp : timestamp });
  412. otherEndpoint.paint({ elementWithPrecedence:id });
  413. // all the connections for the other endpoint now need to be repainted
  414. for (var k = 0; k < otherEndpoint.connections.length; k++) {
  415. if (otherEndpoint.connections[k] !== l)
  416. otherEndpoint.connections[k].paint( { elId : id, ui : ui, recalc : false, timestamp : timestamp });
  417. }
  418. }
  419. }
  420. }
  421. }
  422. },
  423. /**
  424. * executes the given function against the given element if the first
  425. * argument is an object, or the list of elements, if the first argument
  426. * is a list. the function passed in takes (element, elementId) as
  427. * arguments.
  428. */
  429. _elementProxy = function(element, fn) {
  430. var retVal = null;
  431. if (element.constructor == Array) {
  432. retVal = [];
  433. for ( var i = 0; i < element.length; i++) {
  434. var el = _getElementObject(element[i]), id = _getAttribute(el, "id");
  435. retVal.push(fn(el, id)); // append return values to what we will return
  436. }
  437. } else {
  438. var el = _getElementObject(element), id = _getAttribute(el, "id");
  439. retVal = fn(el, id);
  440. }
  441. return retVal;
  442. },
  443. /**
  444. * gets an Endpoint by uuid.
  445. */
  446. _getEndpoint = function(uuid) { return endpointsByUUID[uuid]; },
  447. /**
  448. * inits a draggable if it's not already initialised.
  449. */
  450. _initDraggableIfNecessary = function(element, isDraggable, dragOptions) {
  451. var draggable = isDraggable == null ? _draggableByDefault : isDraggable;
  452. if (draggable) {
  453. if (jsPlumb.CurrentLibrary.isDragSupported(element) && !jsPlumb.CurrentLibrary.isAlreadyDraggable(element)) {
  454. var options = dragOptions || _currentInstance.Defaults.DragOptions || jsPlumb.Defaults.DragOptions;
  455. options = jsPlumb.extend( {}, options); // make a copy.
  456. var dragEvent = jsPlumb.CurrentLibrary.dragEvents['drag'];
  457. var stopEvent = jsPlumb.CurrentLibrary.dragEvents['stop'];
  458. options[dragEvent] = _wrap(options[dragEvent], function() {
  459. var ui = jsPlumb.CurrentLibrary.getUIPosition(arguments);
  460. _draw(element, ui);
  461. _addClass(element, "jsPlumb_dragged");
  462. });
  463. options[stopEvent] = _wrap(options[stopEvent], function() {
  464. var ui = jsPlumb.CurrentLibrary.getUIPosition(arguments);
  465. _draw(element, ui);
  466. _removeClass(element, "jsPlumb_dragged");
  467. });
  468. var draggable = draggableStates[_getId(element)];
  469. options.disabled = draggable == null ? false : !draggable;
  470. jsPlumb.CurrentLibrary.initDraggable(element, options);
  471. }
  472. }
  473. },
  474. _newConnection = function(params) {
  475. var connectionFunc = jsPlumb.Defaults.ConnectionType || Connection,
  476. endpointFunc = jsPlumb.Defaults.EndpointType || Endpoint,
  477. parent = jsPlumb.CurrentLibrary.getParent;
  478. if (params.container)
  479. params["parent"] = params.container;
  480. else {
  481. if (params.sourceEndpoint)
  482. params["parent"] = params.sourceEndpoint.parent;
  483. else if (params.source.constructor == endpointFunc)
  484. params["parent"] = params.source.parent;
  485. else params["parent"] = parent(params.source);
  486. }
  487. params["_jsPlumb"] = _currentInstance;
  488. var con = new connectionFunc(params);
  489. _eventFireProxy("click", "click", con);
  490. _eventFireProxy("dblclick", "dblclick", con);
  491. return con;
  492. },
  493. _eventFireProxy = function(event, proxyEvent, obj) {
  494. obj.bind(event, function(originalObject, originalEvent) {
  495. _currentInstance.fire(proxyEvent, obj, originalEvent);
  496. });
  497. },
  498. _newEndpoint = function(params) {
  499. var endpointFunc = jsPlumb.Defaults.EndpointType || Endpoint;
  500. if (params.container)
  501. params.parent = params.container;
  502. else
  503. params["parent"] = jsPlumb.CurrentLibrary.getParent(params.source);
  504. params["_jsPlumb"] = _currentInstance,
  505. ep = new endpointFunc(params);
  506. _eventFireProxy("click", "endpointClick", ep);
  507. _eventFireProxy("dblclick", "endpointDblClick", ep);
  508. return ep;
  509. },
  510. /**
  511. * performs the given function operation on all the connections found
  512. * for the given element id; this means we find all the endpoints for
  513. * the given element, and then for each endpoint find the connectors
  514. * connected to it. then we pass each connection in to the given
  515. * function.
  516. */
  517. _operation = function(elId, func, endpointFunc) {
  518. var endpoints = endpointsByElement[elId];
  519. if (endpoints && endpoints.length) {
  520. for ( var i = 0; i < endpoints.length; i++) {
  521. for ( var j = 0; j < endpoints[i].connections.length; j++) {
  522. var retVal = func(endpoints[i].connections[j]);
  523. // if the function passed in returns true, we exit.
  524. // most functions return false.
  525. if (retVal) return;
  526. }
  527. if (endpointFunc) endpointFunc(endpoints[i]);
  528. }
  529. }
  530. },
  531. /**
  532. * perform an operation on all elements.
  533. */
  534. _operationOnAll = function(func) {
  535. for ( var elId in endpointsByElement) {
  536. _operation(elId, func);
  537. }
  538. },
  539. /**
  540. * helper to remove an element from the DOM.
  541. */
  542. _removeElement = function(element, parent) {
  543. if (element != null && element.parentNode != null) {
  544. element.parentNode.removeChild(element);
  545. }
  546. },
  547. /**
  548. * helper to remove a list of elements from the DOM.
  549. */
  550. _removeElements = function(elements, parent) {
  551. for ( var i = 0; i < elements.length; i++)
  552. _removeElement(elements[i], parent);
  553. },
  554. /**
  555. * helper method to remove an item from a list.
  556. */
  557. _removeFromList = function(map, key, value) {
  558. if (key != null) {
  559. var l = map[key];
  560. if (l != null) {
  561. var i = _findIndex(l, value);
  562. if (i >= 0) {
  563. delete (l[i]);
  564. l.splice(i, 1);
  565. return true;
  566. }
  567. }
  568. }
  569. return false;
  570. },
  571. /**
  572. * Sets whether or not the given element(s) should be draggable,
  573. * regardless of what a particular plumb command may request.
  574. *
  575. * @param element
  576. * May be a string, a element objects, or a list of
  577. * strings/elements.
  578. * @param draggable
  579. * Whether or not the given element(s) should be draggable.
  580. */
  581. _setDraggable = function(element, draggable) {
  582. return _elementProxy(element, function(el, id) {
  583. draggableStates[id] = draggable;
  584. if (jsPlumb.CurrentLibrary.isDragSupported(el)) {
  585. jsPlumb.CurrentLibrary.setDraggable(el, draggable);
  586. }
  587. });
  588. },
  589. /**
  590. * private method to do the business of hiding/showing.
  591. *
  592. * @param el
  593. * either Id of the element in question or a library specific
  594. * object for the element.
  595. * @param state
  596. * String specifying a value for the css 'display' property
  597. * ('block' or 'none').
  598. */
  599. _setVisible = function(el, state, alsoChangeEndpoints) {
  600. state = state === "block";
  601. var endpointFunc = null;
  602. if (alsoChangeEndpoints) {
  603. if (state) endpointFunc = function(ep) {
  604. ep.setVisible(true, true, true);
  605. };
  606. else endpointFunc = function(ep) {
  607. ep.setVisible(false, true, true);
  608. };
  609. }
  610. var id = _getAttribute(el, "id");
  611. _operation(id, function(jpc) {
  612. if (state && alsoChangeEndpoints) {
  613. // this test is necessary because this functionality is new, and i wanted to maintain backwards compatibility.
  614. // this block will only set a connection to be visible if the other endpoint in the connection is also visible.
  615. var oidx = jpc.sourceId === id ? 1 : 0;
  616. if (jpc.endpoints[oidx].isVisible()) jpc.setVisible(true);
  617. }
  618. else // the default behaviour for show, and what always happens for hide, is to just set the visibility without getting clever.
  619. jpc.setVisible(state);
  620. }, endpointFunc);
  621. },
  622. /**
  623. * toggles the draggable state of the given element(s).
  624. *
  625. * @param el
  626. * either an id, or an element object, or a list of
  627. * ids/element objects.
  628. */
  629. _toggleDraggable = function(el) {
  630. return _elementProxy(el, function(el, elId) {
  631. var state = draggableStates[elId] == null ? _draggableByDefault : draggableStates[elId];
  632. state = !state;
  633. draggableStates[elId] = state;
  634. jsPlumb.CurrentLibrary.setDraggable(el, state);
  635. return state;
  636. });
  637. },
  638. /**
  639. * private method to do the business of toggling hiding/showing.
  640. *
  641. * @param elId
  642. * Id of the element in question
  643. */
  644. _toggleVisible = function(elId, changeEndpoints) {
  645. var endpointFunc = null;
  646. if (changeEndpoints) {
  647. endpointFunc = function(ep) {
  648. var state = ep.isVisible();
  649. ep.setVisible(!state);
  650. };
  651. }
  652. _operation(elId, function(jpc) {
  653. var state = jpc.isVisible();
  654. jpc.setVisible(!state);
  655. }, endpointFunc);
  656. // todo this should call _elementProxy, and pass in the
  657. // _operation(elId, f) call as a function. cos _toggleDraggable does
  658. // that.
  659. },
  660. /**
  661. * updates the offset and size for a given element, and stores the
  662. * values. if 'offset' is not null we use that (it would have been
  663. * passed in from a drag call) because it's faster; but if it is null,
  664. * or if 'recalc' is true in order to force a recalculation, we get the current values.
  665. */
  666. _updateOffset = function(params) {
  667. var timestamp = params.timestamp, recalc = params.recalc, offset = params.offset, elId = params.elId;
  668. if (!recalc) {
  669. if (timestamp && timestamp === offsetTimestamps[elId])
  670. return offsets[elId];
  671. }
  672. if (recalc || offset == null) { // if forced repaint or no offset
  673. // available, we recalculate.
  674. // get the current size and offset, and store them
  675. var s = _getElementObject(elId);
  676. if (s != null) {
  677. sizes[elId] = _getSize(s);
  678. offsets[elId] = _getOffset(s);
  679. offsetTimestamps[elId] = timestamp;
  680. }
  681. } else {
  682. offsets[elId] = offset;
  683. }
  684. return offsets[elId];
  685. },
  686. /**
  687. * gets an id for the given element, creating and setting one if
  688. * necessary.
  689. */
  690. _getId = function(element, uuid) {
  691. var ele = _getElementObject(element);
  692. var id = _getAttribute(ele, "id");
  693. if (!id || id == "undefined") {
  694. // check if fixed uuid parameter is given
  695. if (arguments.length == 2 && arguments[1] != undefined)
  696. id = uuid;
  697. else
  698. id = "jsPlumb_" + _timestamp();
  699. _setAttribute(ele, "id", id);
  700. }
  701. return id;
  702. },
  703. /**
  704. * wraps one function with another, creating a placeholder for the
  705. * wrapped function if it was null. this is used to wrap the various
  706. * drag/drop event functions - to allow jsPlumb to be notified of
  707. * important lifecycle events without imposing itself on the user's
  708. * drag/drop functionality. TODO: determine whether or not we should
  709. * support an error handler concept, if one of the functions fails.
  710. *
  711. * @param wrappedFunction original function to wrap; may be null.
  712. * @param newFunction function to wrap the original with.
  713. * @param returnOnThisValue Optional. Indicates that the wrappedFunction should
  714. * not be executed if the newFunction returns a value matching 'returnOnThisValue'.
  715. * note that this is a simple comparison and only works for primitives right now.
  716. */
  717. _wrap = function(wrappedFunction, newFunction, returnOnThisValue) {
  718. wrappedFunction = wrappedFunction || function() { };
  719. newFunction = newFunction || function() { };
  720. return function() {
  721. var r = null;
  722. try {
  723. r = newFunction.apply(this, arguments);
  724. } catch (e) {
  725. _log(_currentInstance, 'jsPlumb function failed : ' + e);
  726. }
  727. if (returnOnThisValue == null || (r !== returnOnThisValue)) {
  728. try {
  729. wrappedFunction.apply(this, arguments);
  730. } catch (e) {
  731. _log(_currentInstance, 'wrapped function failed : ' + e);
  732. }
  733. }
  734. return r;
  735. };
  736. };
  737. /*
  738. * Property: connectorClass
  739. * 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.
  740. */
  741. this.connectorClass = "_jsPlumb_connector";
  742. /*
  743. * Property: endpointClass
  744. * 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.
  745. */
  746. this.endpointClass = "_jsPlumb_endpoint";
  747. /*
  748. * Property: overlayClass
  749. * 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.
  750. */
  751. this.overlayClass = "_jsPlumb_overlay";
  752. this.Anchors = {};
  753. this.Connectors = {
  754. "canvas":{},
  755. "svg":{},
  756. "vml":{}
  757. };
  758. this.Endpoints = {
  759. "canvas":{},
  760. "svg":{},
  761. "vml":{}
  762. };
  763. this.Overlays = {
  764. "canvas":{},
  765. "svg":{},
  766. "vml":{}
  767. };
  768. // ************************ PLACEHOLDER DOC ENTRIES FOR NATURAL DOCS *****************************************
  769. /*
  770. * Function: bind
  771. * Bind to an event on jsPlumb.
  772. *
  773. * Parameters:
  774. * event - the event to bind. Available events on jsPlumb are:
  775. * - *jsPlumbConnection* : notification that a new Connection was established. jsPlumb passes the new Connection to the callback.
  776. * - *jsPlumbConnectionDetached* : notification that a Connection was detached. jsPlumb passes the detached Connection to the callback.
  777. * - *click* : notification that a Connection was clicked. jsPlumb passes the Connection that was clicked to the callback.
  778. * - *dblclick* : notification that a Connection was double clicked. jsPlumb passes the Connection that was double clicked to the callback.
  779. * - *endpointClick* : notification that an Endpoint was clicked. jsPlumb passes the Endpoint that was clicked to the callback.
  780. * - *endpointDblClick* : notification that an Endpoint was double clicked. jsPlumb passes the Endpoint that was double clicked to the callback.
  781. *
  782. * callback - function to callback. This function will be passed the Connection/Endpoint that caused the event, and also the original event.
  783. */
  784. /*
  785. * Function: clearListeners
  786. * Clears either all listeners, or listeners for some specific event.
  787. *
  788. * Parameters:
  789. * event - optional. constrains the clear to just listeners for this event.
  790. */
  791. // *************** END OF PLACEHOLDER DOC ENTRIES FOR NATURAL DOCS ***********************************************************
  792. /*
  793. Function: addEndpoint
  794. Adds an <Endpoint> to a given element or elements.
  795. Parameters:
  796. el - Element to add the endpoint to. Either an element id, a selector representing some element(s), or an array of either of these.
  797. params - Object containing Endpoint constructor arguments. For more information, see <Endpoint>.
  798. referenceParams - Object containing more Endpoint constructor arguments; it will be merged with params by jsPlumb. You would use this if you had some
  799. shared parameters that you wanted to reuse when you added Endpoints to a number of elements. The allowed values in
  800. this object are anything that 'params' can contain. See <Endpoint>.
  801. Returns:
  802. The newly created <Endpoint>, if el referred to a single element. Otherwise, an array of newly created <Endpoint>s.
  803. See Also:
  804. <addEndpoints>
  805. */
  806. this.addEndpoint = function(el, params, referenceParams) {
  807. referenceParams = referenceParams || {};
  808. var p = jsPlumb.extend({}, referenceParams);
  809. jsPlumb.extend(p, params);
  810. p.endpoint = p.endpoint || _currentInstance.Defaults.Endpoint || jsPlumb.Defaults.Endpoint;
  811. p.paintStyle = p.paintStyle || _currentInstance.Defaults.EndpointStyle || jsPlumb.Defaults.EndpointStyle;
  812. // YUI wrapper
  813. el = _convertYUICollection(el);
  814. var results = [], inputs = el.length && el.constructor != String ? el : [ el ];
  815. for (var i = 0; i < inputs.length; i++) {
  816. var _el = _getElementObject(inputs[i]), id = _getId(_el);
  817. p.source = _el;
  818. _updateOffset({ elId : id });
  819. var e = _newEndpoint(p);
  820. _addToList(endpointsByElement, id, e);
  821. var myOffset = offsets[id], myWH = sizes[id];
  822. var anchorLoc = e.anchor.compute( { xy : [ myOffset.left, myOffset.top ], wh : myWH, element : e });
  823. e.paint({ anchorLoc : anchorLoc });
  824. results.push(e);
  825. }
  826. return results.length == 1 ? results[0] : results;
  827. };
  828. /*
  829. Function: addEndpoints
  830. Adds a list of <Endpoint>s to a given element or elements.
  831. Parameters:
  832. target - element to add the Endpoint to. Either an element id, a selector representing some element(s), or an array of either of these.
  833. endpoints - List of objects containing Endpoint constructor arguments. one Endpoint is created for each entry in this list. See <Endpoint>'s constructor documentation.
  834. 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.
  835. Returns:
  836. List of newly created <Endpoint>s, one for each entry in the 'endpoints' argument.
  837. See Also:
  838. <addEndpoint>
  839. */
  840. this.addEndpoints = function(el, endpoints, referenceParams) {
  841. var results = [];
  842. for ( var i = 0; i < endpoints.length; i++) {
  843. var e = _currentInstance.addEndpoint(el, endpoints[i], referenceParams);
  844. if (e.constructor == Array)
  845. Array.prototype.push.apply(results, e);
  846. else results.push(e);
  847. }
  848. return results;
  849. };
  850. /*
  851. Function: animate
  852. This is a wrapper around the supporting library's animate function; it injects a call to jsPlumb in the 'step' function (creating
  853. the 'step' function if necessary). This only supports the two-arg version of the animate call in jQuery, the one that takes an 'options' object as
  854. the second arg. MooTools has only one method, a two arg one. Which is handy. YUI has a one-arg method, so jsPlumb merges 'properties' and 'options' together for YUI.
  855. Parameters:
  856. el - Element to animate. Either an id, or a selector representing the element.
  857. properties - The 'properties' argument you want passed to the library's animate call.
  858. options - The 'options' argument you want passed to the library's animate call.
  859. Returns:
  860. void
  861. */
  862. this.animate = function(el, properties, options) {
  863. var ele = _getElementObject(el), id = _getAttribute(el, "id");
  864. options = options || {};
  865. var stepFunction = jsPlumb.CurrentLibrary.dragEvents['step'];
  866. var completeFunction = jsPlumb.CurrentLibrary.dragEvents['complete'];
  867. options[stepFunction] = _wrap(options[stepFunction], function() {
  868. _currentInstance.repaint(id);
  869. });
  870. // onComplete repaints, just to make sure everything looks good at the end of the animation.
  871. options[completeFunction] = _wrap(options[completeFunction],
  872. function() {
  873. _currentInstance.repaint(id);
  874. });
  875. jsPlumb.CurrentLibrary.animate(ele, properties, options);
  876. };
  877. /*
  878. Function: connect
  879. Establishes a <Connection> between two elements (or <Endpoint>s, which are themselves registered to elements).
  880. Parameters:
  881. params - Object containing constructor arguments for the Connection. See <Connection>'s constructor documentation.
  882. referenceParams - Optional object containing more constructor arguments for the Connection. Typically you would pass in data that a lot of
  883. Connections are sharing here, such as connector style etc, and then use the main params for data specific to this Connection.
  884. Returns:
  885. The newly created <Connection>.
  886. */
  887. this.connect = function(params, referenceParams) {
  888. var _p = jsPlumb.extend( {}, params);
  889. if (referenceParams) jsPlumb.extend(_p, referenceParams);
  890. if (_p.source && _p.source.endpoint) _p.sourceEndpoint = _p.source;
  891. if (_p.source && _p.target.endpoint) _p.targetEndpoint = _p.target;
  892. // test for endpoint uuids to connect
  893. if (params.uuids) {
  894. _p.sourceEndpoint = _getEndpoint(params.uuids[0]);
  895. _p.targetEndpoint = _getEndpoint(params.uuids[1]);
  896. }
  897. // now ensure that if we do have Endpoints already, they're not full.
  898. if (_p.sourceEndpoint && _p.sourceEndpoint.isFull()) {
  899. _log(_currentInstance, "could not add connection; source endpoint is full");
  900. return;
  901. }
  902. if (_p.targetEndpoint && _p.targetEndpoint.isFull()) {
  903. _log(_currentInstance, "could not add connection; target endpoint is full");
  904. return;
  905. }
  906. if (_p.target && !_p.target.endpoint) {
  907. var tid = _getId(_p.target),
  908. tep =_targetEndpointDefinitions[tid];
  909. var overrideOne = function(singlePropertyName, pluralPropertyName, tepProperty, tep) {
  910. if (tep[tepProperty]) {
  911. if (_p[pluralPropertyName]) _p[pluralPropertyName][1] = tep[tepProperty];
  912. else if (_p[singlePropertyName]) {
  913. _p[pluralPropertyName] = [ _p[singlePropertyName], tep[tepProperty] ];
  914. _p[singlePropertyName] = null;
  915. }
  916. else _p[pluralPropertyName] = [ null, tep[tepProperty] ];
  917. }
  918. };
  919. if (tep) {
  920. overrideOne("endpoint", "endpoints", "endpoint", tep);
  921. overrideOne("endpointStyle", "endpointStyles", "paintStyle", tep);
  922. overrideOne("endpointHoverStyle", "endpointHoverStyles", "hoverPaintStyle", tep);
  923. }
  924. }
  925. // dynamic anchors. backwards compatibility here: from 1.2.6 onwards you don't need to specify "dynamicAnchors". the fact that some anchor consists
  926. // of multiple definitions is enough to tell jsPlumb you want it to be dynamic.
  927. if (_p.dynamicAnchors) {
  928. // these can either be an array of anchor coords, which we will use for both source and target, or an object with {source:[anchors], target:[anchors]}, in which
  929. // case we will use a different set for each element.
  930. var a = _p.dynamicAnchors.constructor == Array;
  931. var sa = a ? new DynamicAnchor(jsPlumb.makeAnchors(_p.dynamicAnchors)) : new DynamicAnchor(jsPlumb.makeAnchors(_p.dynamicAnchors.source));
  932. var ta = a ? new DynamicAnchor(jsPlumb.makeAnchors(_p.dynamicAnchors)) : new DynamicAnchor(jsPlumb.makeAnchors(_p.dynamicAnchors.target));
  933. _p.anchors = [sa,ta];
  934. }
  935. var jpc = _newConnection(_p);
  936. // add to list of connections (by scope).
  937. _addToList(connectionsByScope, jpc.scope, jpc);
  938. // fire an event
  939. _currentInstance.fire("jsPlumbConnection", {
  940. connection:jpc,
  941. source : jpc.source, target : jpc.target,
  942. sourceId : jpc.sourceId, targetId : jpc.targetId,
  943. sourceEndpoint : jpc.endpoints[0], targetEndpoint : jpc.endpoints[1]
  944. });
  945. // force a paint
  946. _draw(jpc.source);
  947. return jpc;
  948. };
  949. /*
  950. Function: deleteEndpoint
  951. Deletes an Endpoint and removes all Connections it has (which removes the Connections from the other Endpoints involved too)
  952. Parameters:
  953. object - either an <Endpoint> object (such as from an addEndpoint call), or a String UUID.
  954. Returns:
  955. void
  956. */
  957. this.deleteEndpoint = function(object) {
  958. var endpoint = (typeof object == "string") ? endpointsByUUID[object] : object;
  959. if (endpoint) {
  960. var uuid = endpoint.getUuid();
  961. if (uuid) endpointsByUUID[uuid] = null;
  962. endpoint.detachAll();
  963. _removeElement(endpoint.canvas, endpoint.parent);
  964. // remove from endpointsbyElement
  965. for (var e in endpointsByElement) {
  966. var endpoints = endpointsByElement[e];
  967. if (endpoints) {
  968. var newEndpoints = [];
  969. for (var i = 0; i < endpoints.length; i++)
  970. if (endpoints[i] != endpoint) newEndpoints.push(endpoints[i]);
  971. endpointsByElement[e] = newEndpoints;
  972. }
  973. }
  974. delete endpoint;
  975. }
  976. };
  977. /*
  978. Function: deleteEveryEndpoint
  979. Deletes every <Endpoint>, and their associated <Connection>s, in this instance of jsPlumb. Does not unregister any event listeners (this is the only difference
  980. between this method and jsPlumb.reset).
  981. Returns:
  982. void
  983. */
  984. this.deleteEveryEndpoint = function() {
  985. for ( var id in endpointsByElement) {
  986. var endpoints = endpointsByElement[id];
  987. if (endpoints && endpoints.length) {
  988. for ( var i = 0; i < endpoints.length; i++) {
  989. _currentInstance.deleteEndpoint(endpoints[i]);
  990. }
  991. }
  992. }
  993. delete endpointsByElement;
  994. endpointsByElement = {};
  995. delete endpointsByUUID;
  996. endpointsByUUID = {};
  997. };
  998. var fireDetachEvent = function(jpc) {
  999. _currentInstance.fire("jsPlumbConnectionDetached", {
  1000. connection:jpc,
  1001. source : jpc.source, target : jpc.target,
  1002. sourceId : jpc.sourceId, targetId : jpc.targetId,
  1003. sourceEndpoint : jpc.endpoints[0], targetEndpoint : jpc.endpoints[1]
  1004. });
  1005. };
  1006. /*
  1007. Function: detach
  1008. Detaches and then removes a <Connection>. Takes either (source, target) (the old way, maintained for backwards compatibility), or a params
  1009. object with various possible values.
  1010. Parameters:
  1011. source - id or element object of the first element in the Connection.
  1012. target - id or element object of the second element in the Connection.
  1013. params - a JS object containing the same parameters as you pass to jsPlumb.connect. If this is present then neither source nor
  1014. target should be present; it should be the only argument to the method. See the docs for <Connection>'s constructor for information
  1015. about the parameters allowed in the params object.
  1016. Returns:
  1017. true if successful, false if not.
  1018. */
  1019. this.detach = function(source, target) {
  1020. if (arguments.length == 2) {
  1021. var s = _getElementObject(source), sId = _getId(s);
  1022. var t = _getElementObject(target), tId = _getId(t);
  1023. _operation(sId, function(jpc) {
  1024. if ((jpc.sourceId == sId && jpc.targetId == tId) || (jpc.targetId == sId && jpc.sourceId == tId)) {
  1025. _removeElements(jpc.connector.getDisplayElements(), jpc.parent);
  1026. jpc.endpoints[0].removeConnection(jpc);
  1027. jpc.endpoints[1].removeConnection(jpc);
  1028. _removeFromList(connectionsByScope, jpc.scope, jpc);
  1029. }
  1030. });
  1031. }
  1032. // this is the new version of the method, taking a JS object like
  1033. // the connect method does.
  1034. else if (arguments.length == 1) {
  1035. // TODO investigate whether or not this code still works when a user has supplied their own subclass of Connection. i suspect it may not.
  1036. if (arguments[0].constructor == Connection) {
  1037. arguments[0].endpoints[0].detachFrom(arguments[0].endpoints[1]);
  1038. }
  1039. else if (arguments[0].connection) {
  1040. arguments[0].connection.endpoints[0].detachFrom(arguments[0].connection.endpoints[1]);
  1041. }
  1042. else {
  1043. var _p = jsPlumb.extend( {}, source); // a backwards compatibility hack: source should be thought of as 'params' in this case.
  1044. // test for endpoint uuids to detach
  1045. if (_p.uuids) {
  1046. _getEndpoint(_p.uuids[0]).detachFrom(_getEndpoint(_p.uuids[1]));
  1047. } else if (_p.sourceEndpoint && _p.targetEndpoint) {
  1048. _p.sourceEndpoint.detachFrom(_p.targetEndpoint);
  1049. } else {
  1050. var sourceId = _getId(_p.source);
  1051. var targetId = _getId(_p.target);
  1052. _operation(sourceId, function(jpc) {
  1053. if ((jpc.sourceId == sourceId && jpc.targetId == targetId) || (jpc.targetId == sourceId && jpc.sourceId == targetId)) {
  1054. _removeElements(jpc.connector.getDisplayElements(), jpc.parent);
  1055. jpc.endpoints[0].removeConnection(jpc);
  1056. jpc.endpoints[1].removeConnection(jpc);
  1057. _removeFromList(connectionsByScope, jpc.scope, jpc);
  1058. }
  1059. });
  1060. }
  1061. }
  1062. }
  1063. };
  1064. /*
  1065. Function: detachAll
  1066. Removes all an element's Connections.
  1067. Parameters:
  1068. el - either the id of the element, or a selector for the element.
  1069. Returns:
  1070. void
  1071. */
  1072. this.detachAllConnections = function(el) {
  1073. var id = _getAttribute(el, "id");
  1074. var endpoints = endpointsByElement[id];
  1075. if (endpoints && endpoints.length) {
  1076. for ( var i = 0; i < endpoints.length; i++) {
  1077. endpoints[i].detachAll();
  1078. }
  1079. }
  1080. };
  1081. /**
  1082. * @deprecated Use detachAllConnections instead. this will be removed in jsPlumb 1.3.
  1083. */
  1084. this.detachAll = this.detachAllConnections;
  1085. /*
  1086. Function: detachEveryConnection
  1087. Remove all Connections from all elements, but leaves Endpoints in place.
  1088. Returns:
  1089. void
  1090. See Also:
  1091. <removeEveryEndpoint>
  1092. */
  1093. this.detachEveryConnection = function() {
  1094. for ( var id in endpointsByElement) {
  1095. var endpoints = endpointsByElement[id];
  1096. if (endpoints && endpoints.length) {
  1097. for ( var i = 0; i < endpoints.length; i++) {
  1098. endpoints[i].detachAll();
  1099. }
  1100. }
  1101. }
  1102. delete connectionsByScope;
  1103. connectionsByScope = {};
  1104. };
  1105. /**
  1106. * @deprecated use detachEveryConnection instead. this will be removed in jsPlumb 1.3.
  1107. */
  1108. this.detachEverything = this.detachEveryConnection;
  1109. /*
  1110. Function: draggable
  1111. Initialises the draggability of some element or elements. You should use this instead of you library's draggable method so that jsPlumb can setup the appropriate callbacks. Your underlying library's drag method is always called from this method.
  1112. Parameters:
  1113. el - either an element id, a list of element ids, or a selector.
  1114. options - options to pass through to the underlying library
  1115. Returns:
  1116. void
  1117. */
  1118. this.draggable = function(el, options) {
  1119. if (typeof el == 'object' && el.length) {
  1120. for ( var i = 0; i < el.length; i++) {
  1121. var ele = _getElementObject(el[i]);
  1122. if (ele) _initDraggableIfNecessary(ele, true, options);
  1123. }
  1124. }
  1125. else if (el._nodes) { // TODO this is YUI specific; really the logic should be forced
  1126. // into the library adapters (for jquery and mootools aswell)
  1127. for ( var i = 0; i < el._nodes.length; i++) {
  1128. var ele = _getElementObject(el._nodes[i]);
  1129. if (ele) _initDraggableIfNecessary(ele, true, options);
  1130. }
  1131. }
  1132. else {
  1133. var ele = _getElementObject(el);
  1134. if (ele) _initDraggableIfNecessary(ele, true, options);
  1135. }
  1136. };
  1137. /*
  1138. Function: extend
  1139. Wraps the underlying library's extend functionality.
  1140. Parameters:
  1141. o1 - object to extend
  1142. o2 - object to extend o1 with
  1143. Returns:
  1144. o1, extended with all properties from o2.
  1145. */
  1146. this.extend = function(o1, o2) {
  1147. return jsPlumb.CurrentLibrary.extend(o1, o2);
  1148. };
  1149. /*
  1150. * Function: getDefaultEndpointType
  1151. * Returns the default Endpoint type. Used when someone wants to subclass Endpoint and have jsPlumb return instances of their subclass.
  1152. * you would make a call like this in your class's constructor:
  1153. * jsPlumb.getDefaultEndpointType().apply(this, arguments);
  1154. *
  1155. * Returns:
  1156. * the default Endpoint function used by jsPlumb.
  1157. */
  1158. this.getDefaultEndpointType = function() {
  1159. return Endpoint;
  1160. };
  1161. /*
  1162. * Function: getDefaultConnectionType
  1163. * Returns the default Connection type. Used when someone wants to subclass Connection and have jsPlumb return instances of their subclass.
  1164. * you would make a call like this in your class's constructor:
  1165. * jsPlumb.getDefaultConnectionType().apply(this, arguments);
  1166. *
  1167. * Returns:
  1168. * the default Connection function used by jsPlumb.
  1169. */
  1170. this.getDefaultConnectionType = function() {
  1171. return Connection;
  1172. };
  1173. /*
  1174. * Function: getConnections
  1175. * Gets all or a subset of connections currently managed by this jsPlumb instance. If only one scope is passed in to this method,
  1176. * the result will be a list of connections having that scope (passing in no scope at all will result in jsPlumb assuming you want the
  1177. * default scope). If multiple scopes are passed in, the return value will be a map of { scope -> [ connection... ] }.
  1178. *
  1179. * Parameters
  1180. * scope - if the only argument to getConnections is a string, jsPlumb will treat that string as a scope filter, and return a list
  1181. * of connections that are in the given scope.
  1182. * options - if the argument is a JS object, you can specify a finer-grained filter:
  1183. *
  1184. * - *scope* may be a string specifying a single scope, or an array of strings, specifying multiple scopes.
  1185. * - *source* either a string representing an element id, or a selector. constrains the result to connections having this source.
  1186. * - *target* either a string representing an element id, or a selector. constrains the result to connections having this target.
  1187. *
  1188. */
  1189. this.getConnections = function(options) {
  1190. if (!options) {
  1191. options = {};
  1192. } else if (options.constructor == St