mootools.jsPlumb-1.3.6-RC1.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. /*
  2. * jsPlumb
  3. *
  4. * Title:jsPlumb 1.3.6
  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 MooTools adapter.
  10. *
  11. * Copyright (c) 2010 - 2012 Simon Porritt (http://jsplumb.org)
  12. *
  13. * http://jsplumb.org
  14. * http://github.com/sporritt/jsplumb
  15. * http://code.google.com/p/jsplumb
  16. *
  17. * Dual licensed under the MIT and GPL2 licenses.
  18. */
  19. ;(function() {
  20. /*
  21. * overrides the FX class to inject 'step' functionality, which MooTools does not
  22. * offer, and which makes me sad. they don't seem keen to add it, either, despite
  23. * the fact that it could be useful:
  24. *
  25. * https://mootools.lighthouseapp.com/projects/2706/tickets/668
  26. *
  27. */
  28. var jsPlumbMorph = new Class({
  29. Extends:Fx.Morph,
  30. onStep : null,
  31. initialize : function(el, options) {
  32. this.parent(el, options);
  33. if (options['onStep']) {
  34. this.onStep = options['onStep'];
  35. }
  36. },
  37. step : function(now) {
  38. this.parent(now);
  39. if (this.onStep) {
  40. try { this.onStep(); }
  41. catch(e) { }
  42. }
  43. }
  44. });
  45. var _droppables = {},
  46. _droppableOptions = {},
  47. _draggablesByScope = {},
  48. _draggablesById = {},
  49. _droppableScopesById = {};
  50. /*
  51. *
  52. */
  53. var _executeDroppableOption = function(el, dr, event) {
  54. if (dr) {
  55. var id = dr.get("id");
  56. if (id) {
  57. var options = _droppableOptions[id];
  58. if (options) {
  59. if (options[event]) {
  60. options[event](el, dr);
  61. }
  62. }
  63. }
  64. }
  65. };
  66. var _checkHover = function(el, entering) {
  67. if (el) {
  68. var id = el.get("id");
  69. if (id) {
  70. var options = _droppableOptions[id];
  71. if (options) {
  72. if (options['hoverClass']) {
  73. if (entering) el.addClass(options['hoverClass']);
  74. else el.removeClass(options['hoverClass']);
  75. }
  76. }
  77. }
  78. }
  79. };
  80. /**
  81. * adds the given value to the given list, with the given scope. creates the scoped list
  82. * if necessary.
  83. * used by initDraggable and initDroppable.
  84. */
  85. var _add = function(list, _scope, value) {
  86. var l = list[_scope];
  87. if (!l) {
  88. l = [];
  89. list[_scope] = l;
  90. }
  91. l.push(value);
  92. };
  93. /*
  94. * gets an "element object" from the given input. this means an object that is used by the
  95. * underlying library on which jsPlumb is running. 'el' may already be one of these objects,
  96. * in which case it is returned as-is. otherwise, 'el' is a String, the library's lookup
  97. * function is used to find the element, using the given String as the element's id.
  98. */
  99. var _getElementObject = function(el) {
  100. return $(el);
  101. },
  102. _removeNonPermanentDroppables = function(drag) {
  103. /*
  104. // remove non-permanent droppables from all arrays
  105. var dbs = _droppables[drag.scope], d = [];
  106. if (dbs) {
  107. var d = [];
  108. for (var i=0; i < dbs.length; i++) {
  109. var isPermanent = dbs[i].getAttribute("_isPermanentDroppable");
  110. if (isPermanent === "true") d.push(dbs[i]);
  111. }
  112. dbs.splice(0, dbs.length);
  113. _droppables[drag.scope] = d;
  114. }
  115. d = [];
  116. // clear out transient droppables from the drag itself
  117. for(var i = 0; i < drag.droppables.length; i++) {
  118. var isPermanent = drag.droppables[i].getAttribute("_isPermanentDroppable");
  119. if (isPermanent === "true") d.push(drag.droppables[i]);
  120. }
  121. drag.droppables.splice(0, drag.droppables.length); // release old ones
  122. drag.droppables = d;
  123. //*/
  124. };
  125. jsPlumb.CurrentLibrary = {
  126. /**
  127. * adds the given class to the element object.
  128. */
  129. addClass : function(el, clazz) {
  130. el = jsPlumb.CurrentLibrary.getElementObject(el)
  131. try {
  132. if (el.className.constructor == SVGAnimatedString) {
  133. jsPlumb.util.svg.addClass(el, clazz);
  134. }
  135. else el.addClass(clazz);
  136. }
  137. catch (e) {
  138. // SVGAnimatedString not supported; no problem.
  139. el.addClass(clazz);
  140. }
  141. },
  142. animate : function(el, properties, options) {
  143. var m = new jsPlumbMorph(el, options);
  144. m.start(properties);
  145. },
  146. appendElement : function(child, parent) {
  147. _getElementObject(parent).grab(child);
  148. },
  149. bind : function(el, event, callback) {
  150. el = _getElementObject(el);
  151. el.addEvent(event, callback);
  152. },
  153. dragEvents : {
  154. 'start':'onStart', 'stop':'onComplete', 'drag':'onDrag', 'step':'onStep',
  155. 'over':'onEnter', 'out':'onLeave','drop':'onDrop', 'complete':'onComplete'
  156. },
  157. /*
  158. * wrapper around the library's 'extend' functionality (which it hopefully has.
  159. * otherwise you'll have to do it yourself). perhaps jsPlumb could do this for you
  160. * instead. it's not like its hard.
  161. */
  162. extend : function(o1, o2) {
  163. return $extend(o1, o2);
  164. },
  165. /**
  166. * gets the named attribute from the given element object.
  167. */
  168. getAttribute : function(el, attName) {
  169. return el.get(attName);
  170. },
  171. getClientXY : function(eventObject) {
  172. return [eventObject.event.clientX, eventObject.event.clientY];
  173. },
  174. getDragObject : function(eventArgs) {
  175. return eventArgs[0];
  176. },
  177. getDragScope : function(el) {
  178. var id = jsPlumb.getId(el),
  179. drags = _draggablesById[id];
  180. return drags[0].scope;
  181. },
  182. getDropScope : function(el) {
  183. var id = jsPlumb.getId(el);
  184. return _droppableScopesById[id];
  185. },
  186. getDOMElement : function(el) {
  187. // MooTools just decorates the DOM elements. so we have either an ID or an Element here.
  188. return typeof(el) == "String" ? document.getElementById(el) : el;
  189. },
  190. getElementObject : _getElementObject,
  191. /*
  192. gets the offset for the element object. this should return a js object like this:
  193. { left:xxx, top: xxx}
  194. */
  195. getOffset : function(el) {
  196. var p = el.getPosition();
  197. return { left:p.x, top:p.y };
  198. },
  199. getPageXY : function(eventObject) {
  200. return [eventObject.event.pageX, eventObject.event.pageY];
  201. },
  202. getParent : function(el) {
  203. return jsPlumb.CurrentLibrary.getElementObject(el).getParent();
  204. },
  205. getScrollLeft : function(el) {
  206. return null;
  207. },
  208. getScrollTop : function(el) {
  209. return null;
  210. },
  211. getSelector : function(spec) {
  212. return $$(spec);
  213. },
  214. getSize : function(el) {
  215. var s = el.getSize();
  216. return [s.x, s.y];
  217. },
  218. getTagName : function(el) {
  219. var e = jsPlumb.CurrentLibrary.getElementObject(el);
  220. return e != null ? e.tagName : null;
  221. },
  222. /*
  223. * takes the args passed to an event function and returns you an object that gives the
  224. * position of the object being moved, as a js object with the same params as the result of
  225. * getOffset, ie: { left: xxx, top: xxx }.
  226. */
  227. getUIPosition : function(eventArgs) {
  228. var ui = eventArgs[0],
  229. p = jsPlumb.CurrentLibrary.getElementObject(ui).getPosition();
  230. return { left:p.x, top:p.y };
  231. },
  232. hasClass : function(el, clazz) {
  233. return el.hasClass(clazz);
  234. },
  235. initDraggable : function(el, options, isPlumbedComponent) {
  236. var id = jsPlumb.getId(el);
  237. var drag = _draggablesById[id];
  238. if (!drag) {
  239. var originalZIndex = 0,
  240. originalCursor = null,
  241. dragZIndex = jsPlumb.Defaults.DragOptions.zIndex || 2000;
  242. options['onStart'] = jsPlumb.wrap(options['onStart'], function() {
  243. originalZIndex = this.element.getStyle('z-index');
  244. this.element.setStyle('z-index', dragZIndex);
  245. drag.originalZIndex = originalZIndex;
  246. if (jsPlumb.Defaults.DragOptions.cursor) {
  247. originalCursor = this.element.getStyle('cursor');
  248. this.element.setStyle('cursor', jsPlumb.Defaults.DragOptions.cursor);
  249. }
  250. });
  251. options['onComplete'] = jsPlumb.wrap(options['onComplete'], function() {
  252. this.element.setStyle('z-index', originalZIndex);
  253. if (originalCursor) {
  254. this.element.setStyle('cursor', originalCursor);
  255. }
  256. _removeNonPermanentDroppables(drag);
  257. });
  258. // DROPPABLES - only relevant if this is a plumbed component, ie. not just the result of the user making some DOM element
  259. // draggable. this is the only library adapter that has to care about this parameter.
  260. var scope = "" + (options["scope"] || jsPlumb.Defaults.Scope),
  261. filterFunc = function(entry) {
  262. return entry.get("id") != el.get("id");
  263. },
  264. droppables = _droppables[scope] ? _droppables[scope].filter(filterFunc) : [];
  265. if (isPlumbedComponent) {
  266. options['droppables'] = droppables;
  267. options['onLeave'] = jsPlumb.wrap(options['onLeave'], function(el, dr) {
  268. if (dr) {
  269. _checkHover(dr, false);
  270. _executeDroppableOption(el, dr, 'onLeave');
  271. }
  272. });
  273. options['onEnter'] = jsPlumb.wrap(options['onEnter'], function(el, dr) {
  274. if (dr) {
  275. _checkHover(dr, true);
  276. _executeDroppableOption(el, dr, 'onEnter');
  277. }
  278. });
  279. options['onDrop'] = function(el, dr) {
  280. if (dr) {
  281. _checkHover(dr, false);
  282. _executeDroppableOption(el, dr, 'onDrop');
  283. }
  284. };
  285. }
  286. else
  287. options["droppables"] = [];
  288. drag = new Drag.Move(el, options);
  289. drag.scope = scope;
  290. drag.originalZIndex = originalZIndex;
  291. _add(_draggablesById, el.get("id"), drag);
  292. // again, only keep a record of this for scope stuff if this is a plumbed component (an endpoint)
  293. if (isPlumbedComponent) {
  294. _add(_draggablesByScope, scope, drag);
  295. }
  296. // test for disabled.
  297. if (options.disabled) drag.detach();
  298. }
  299. return drag;
  300. },
  301. initDroppable : function(el, options, isPlumbedComponent, isPermanent) {
  302. var scope = options["scope"];
  303. _add(_droppables, scope, el);
  304. var id = jsPlumb.getId(el);
  305. el.setAttribute("_isPermanentDroppable", isPermanent); // we use this to clear out droppables on drag complete.
  306. _droppableOptions[id] = options;
  307. _droppableScopesById[id] = scope;
  308. var filterFunc = function(entry) { return entry.element != el; },
  309. draggables = _draggablesByScope[scope] ? _draggablesByScope[scope].filter(filterFunc) : [];
  310. for (var i = 0; i < draggables.length; i++) {
  311. draggables[i].droppables.push(el);
  312. }
  313. },
  314. isAlreadyDraggable : function(el) {
  315. return _draggablesById[jsPlumb.getId(el)] != null;
  316. },
  317. isDragSupported : function(el, options) {
  318. return typeof Drag != 'undefined' ;
  319. },
  320. /*
  321. * you need Drag.Move imported to make drop work.
  322. */
  323. isDropSupported : function(el, options) {
  324. return (typeof Drag != undefined && typeof Drag.Move != undefined);
  325. },
  326. /**
  327. * removes the given class from the element object.
  328. */
  329. removeClass : function(el, clazz) {
  330. el = jsPlumb.CurrentLibrary.getElementObject(el);
  331. try {
  332. if (el.className.constructor == SVGAnimatedString) {
  333. jsPlumb.util.svg.removeClass(el, clazz);
  334. }
  335. else el.removeClass(clazz);
  336. }
  337. catch (e) {
  338. // SVGAnimatedString not supported; no problem.
  339. el.removeClass(clazz);
  340. }
  341. },
  342. removeElement : function(element, parent) {
  343. var el = _getElementObject(element);
  344. if (el) el.dispose(); // ??
  345. },
  346. /**
  347. * sets the named attribute on the given element object.
  348. */
  349. setAttribute : function(el, attName, attValue) {
  350. el.set(attName, attValue);
  351. },
  352. setDraggable : function(el, draggable) {
  353. var draggables = _draggablesById[el.get("id")];
  354. if (draggables) {
  355. draggables.each(function(d) {
  356. if (draggable) d.attach(); else d.detach();
  357. });
  358. }
  359. },
  360. setDragScope : function(el, scope) {
  361. var drag = _draggablesById[el.get("id")];
  362. var filterFunc = function(entry) {
  363. return entry.get("id") != el.get("id");
  364. };
  365. var droppables = _droppables[scope] ? _droppables[scope].filter(filterFunc) : [];
  366. drag[0].droppables = droppables;
  367. },
  368. setOffset : function(el, o) {
  369. _getElementObject(el).setPosition({x:o.left, y:o.top});
  370. },
  371. stopDrag : function() {
  372. for (var i in _draggablesById) {
  373. for (var j = 0; j < _draggablesById[i].length; j++) {
  374. var d = _draggablesById[i][j];
  375. d.stop();
  376. if (d.originalZIndex != 0)
  377. d.element.setStyle("z-index", d.originalZIndex);
  378. }
  379. }
  380. },
  381. /**
  382. * note that jquery ignores the name of the event you wanted to trigger, and figures it out for itself.
  383. * the other libraries do not. yui, in fact, cannot even pass an original event. we have to pull out stuff
  384. * from the originalEvent to put in an options object for YUI.
  385. * @param el
  386. * @param event
  387. * @param originalEvent
  388. */
  389. trigger : function(el, event, originalEvent) {
  390. originalEvent.stopPropagation();
  391. _getElementObject(el).fireEvent(event, originalEvent);
  392. },
  393. unbind : function(el, event, callback) {
  394. el = _getElementObject(el);
  395. el.removeEvent(event, callback);
  396. }
  397. };
  398. window.addEvent('domready', jsPlumb.init);
  399. })();