mootools.jsPlumb-1.3.7-RC1.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. /*
  2. * jsPlumb
  3. *
  4. * Title:jsPlumb 1.3.7
  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, originalEvent) {
  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, originalEvent);
  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. getDropEvent : function(args) {
  183. return args[2];
  184. },
  185. getDropScope : function(el) {
  186. var id = jsPlumb.getId(el);
  187. return _droppableScopesById[id];
  188. },
  189. getDOMElement : function(el) {
  190. // MooTools just decorates the DOM elements. so we have either an ID or an Element here.
  191. return typeof(el) == "String" ? document.getElementById(el) : el;
  192. },
  193. getElementObject : _getElementObject,
  194. /*
  195. gets the offset for the element object. this should return a js object like this:
  196. { left:xxx, top: xxx}
  197. */
  198. getOffset : function(el) {
  199. var p = el.getPosition();
  200. return { left:p.x, top:p.y };
  201. },
  202. getPageXY : function(eventObject) {
  203. return [eventObject.event.pageX, eventObject.event.pageY];
  204. },
  205. getParent : function(el) {
  206. return jsPlumb.CurrentLibrary.getElementObject(el).getParent();
  207. },
  208. getScrollLeft : function(el) {
  209. return null;
  210. },
  211. getScrollTop : function(el) {
  212. return null;
  213. },
  214. getSelector : function(spec) {
  215. return $$(spec);
  216. },
  217. getSize : function(el) {
  218. var s = el.getSize();
  219. return [s.x, s.y];
  220. },
  221. getTagName : function(el) {
  222. var e = jsPlumb.CurrentLibrary.getElementObject(el);
  223. return e != null ? e.tagName : null;
  224. },
  225. /*
  226. * takes the args passed to an event function and returns you an object that gives the
  227. * position of the object being moved, as a js object with the same params as the result of
  228. * getOffset, ie: { left: xxx, top: xxx }.
  229. */
  230. getUIPosition : function(eventArgs) {
  231. var ui = eventArgs[0],
  232. p = jsPlumb.CurrentLibrary.getElementObject(ui).getPosition();
  233. return { left:p.x, top:p.y };
  234. },
  235. hasClass : function(el, clazz) {
  236. return el.hasClass(clazz);
  237. },
  238. initDraggable : function(el, options, isPlumbedComponent) {
  239. var id = jsPlumb.getId(el);
  240. var drag = _draggablesById[id];
  241. if (!drag) {
  242. var originalZIndex = 0,
  243. originalCursor = null,
  244. dragZIndex = jsPlumb.Defaults.DragOptions.zIndex || 2000;
  245. options['onStart'] = jsPlumb.wrap(options['onStart'], function() {
  246. originalZIndex = this.element.getStyle('z-index');
  247. this.element.setStyle('z-index', dragZIndex);
  248. drag.originalZIndex = originalZIndex;
  249. if (jsPlumb.Defaults.DragOptions.cursor) {
  250. originalCursor = this.element.getStyle('cursor');
  251. this.element.setStyle('cursor', jsPlumb.Defaults.DragOptions.cursor);
  252. }
  253. });
  254. options['onComplete'] = jsPlumb.wrap(options['onComplete'], function() {
  255. this.element.setStyle('z-index', originalZIndex);
  256. if (originalCursor) {
  257. this.element.setStyle('cursor', originalCursor);
  258. }
  259. _removeNonPermanentDroppables(drag);
  260. });
  261. // DROPPABLES - only relevant if this is a plumbed component, ie. not just the result of the user making some DOM element
  262. // draggable. this is the only library adapter that has to care about this parameter.
  263. var scope = "" + (options["scope"] || jsPlumb.Defaults.Scope),
  264. filterFunc = function(entry) {
  265. return entry.get("id") != el.get("id");
  266. },
  267. droppables = _droppables[scope] ? _droppables[scope].filter(filterFunc) : [];
  268. if (isPlumbedComponent) {
  269. options['droppables'] = droppables;
  270. options['onLeave'] = jsPlumb.wrap(options['onLeave'], function(el, dr) {
  271. if (dr) {
  272. _checkHover(dr, false);
  273. _executeDroppableOption(el, dr, 'onLeave');
  274. }
  275. });
  276. options['onEnter'] = jsPlumb.wrap(options['onEnter'], function(el, dr) {
  277. if (dr) {
  278. _checkHover(dr, true);
  279. _executeDroppableOption(el, dr, 'onEnter');
  280. }
  281. });
  282. options['onDrop'] = function(el, dr, event) {
  283. if (dr) {
  284. _checkHover(dr, false);
  285. _executeDroppableOption(el, dr, 'onDrop', event);
  286. }
  287. };
  288. }
  289. else
  290. options["droppables"] = [];
  291. drag = new Drag.Move(el, options);
  292. drag.scope = scope;
  293. drag.originalZIndex = originalZIndex;
  294. _add(_draggablesById, el.get("id"), drag);
  295. // again, only keep a record of this for scope stuff if this is a plumbed component (an endpoint)
  296. if (isPlumbedComponent) {
  297. _add(_draggablesByScope, scope, drag);
  298. }
  299. // test for disabled.
  300. if (options.disabled) drag.detach();
  301. }
  302. return drag;
  303. },
  304. initDroppable : function(el, options, isPlumbedComponent, isPermanent) {
  305. var scope = options["scope"];
  306. _add(_droppables, scope, el);
  307. var id = jsPlumb.getId(el);
  308. el.setAttribute("_isPermanentDroppable", isPermanent); // we use this to clear out droppables on drag complete.
  309. _droppableOptions[id] = options;
  310. _droppableScopesById[id] = scope;
  311. var filterFunc = function(entry) { return entry.element != el; },
  312. draggables = _draggablesByScope[scope] ? _draggablesByScope[scope].filter(filterFunc) : [];
  313. for (var i = 0; i < draggables.length; i++) {
  314. draggables[i].droppables.push(el);
  315. }
  316. },
  317. isAlreadyDraggable : function(el) {
  318. return _draggablesById[jsPlumb.getId(el)] != null;
  319. },
  320. isDragSupported : function(el, options) {
  321. return typeof Drag != 'undefined' ;
  322. },
  323. /*
  324. * you need Drag.Move imported to make drop work.
  325. */
  326. isDropSupported : function(el, options) {
  327. return (typeof Drag != undefined && typeof Drag.Move != undefined);
  328. },
  329. /**
  330. * removes the given class from the element object.
  331. */
  332. removeClass : function(el, clazz) {
  333. el = jsPlumb.CurrentLibrary.getElementObject(el);
  334. try {
  335. if (el.className.constructor == SVGAnimatedString) {
  336. jsPlumb.util.svg.removeClass(el, clazz);
  337. }
  338. else el.removeClass(clazz);
  339. }
  340. catch (e) {
  341. // SVGAnimatedString not supported; no problem.
  342. el.removeClass(clazz);
  343. }
  344. },
  345. removeElement : function(element, parent) {
  346. var el = _getElementObject(element);
  347. if (el) el.dispose(); // ??
  348. },
  349. /**
  350. * sets the named attribute on the given element object.
  351. */
  352. setAttribute : function(el, attName, attValue) {
  353. el.set(attName, attValue);
  354. },
  355. setDraggable : function(el, draggable) {
  356. var draggables = _draggablesById[el.get("id")];
  357. if (draggables) {
  358. draggables.each(function(d) {
  359. if (draggable) d.attach(); else d.detach();
  360. });
  361. }
  362. },
  363. setDragScope : function(el, scope) {
  364. var drag = _draggablesById[el.get("id")];
  365. var filterFunc = function(entry) {
  366. return entry.get("id") != el.get("id");
  367. };
  368. var droppables = _droppables[scope] ? _droppables[scope].filter(filterFunc) : [];
  369. drag[0].droppables = droppables;
  370. },
  371. setOffset : function(el, o) {
  372. _getElementObject(el).setPosition({x:o.left, y:o.top});
  373. },
  374. stopDrag : function() {
  375. for (var i in _draggablesById) {
  376. for (var j = 0; j < _draggablesById[i].length; j++) {
  377. var d = _draggablesById[i][j];
  378. d.stop();
  379. if (d.originalZIndex != 0)
  380. d.element.setStyle("z-index", d.originalZIndex);
  381. }
  382. }
  383. },
  384. /**
  385. * note that jquery ignores the name of the event you wanted to trigger, and figures it out for itself.
  386. * the other libraries do not. yui, in fact, cannot even pass an original event. we have to pull out stuff
  387. * from the originalEvent to put in an options object for YUI.
  388. * @param el
  389. * @param event
  390. * @param originalEvent
  391. */
  392. trigger : function(el, event, originalEvent) {
  393. originalEvent.stopPropagation();
  394. _getElementObject(el).fireEvent(event, originalEvent);
  395. },
  396. unbind : function(el, event, callback) {
  397. el = _getElementObject(el);
  398. el.removeEvent(event, callback);
  399. }
  400. };
  401. window.addEvent('domready', jsPlumb.init);
  402. })();