Modeling.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. import {
  2. assign,
  3. forEach,
  4. isArray
  5. } from 'min-dash';
  6. import {
  7. Base
  8. } from '../../model';
  9. import AlignElementsHandler from './cmd/AlignElementsHandler';
  10. import AppendShapeHandler from './cmd/AppendShapeHandler';
  11. import CreateConnectionHandler from './cmd/CreateConnectionHandler';
  12. import CreateElementsHandler from './cmd/CreateElementsHandler';
  13. import CreateLabelHandler from './cmd/CreateLabelHandler';
  14. import CreateShapeHandler from './cmd/CreateShapeHandler';
  15. import DeleteConnectionHandler from './cmd/DeleteConnectionHandler';
  16. import DeleteElementsHandler from './cmd/DeleteElementsHandler';
  17. import DeleteShapeHandler from './cmd/DeleteShapeHandler';
  18. import DistributeElementsHandler from './cmd/DistributeElementsHandler';
  19. import LayoutConnectionHandler from './cmd/LayoutConnectionHandler';
  20. import MoveConnectionHandler from './cmd/MoveConnectionHandler';
  21. import MoveElementsHandler from './cmd/MoveElementsHandler';
  22. import MoveShapeHandler from './cmd/MoveShapeHandler';
  23. import ReconnectConnectionHandler from './cmd/ReconnectConnectionHandler';
  24. import ReplaceShapeHandler from './cmd/ReplaceShapeHandler';
  25. import ResizeShapeHandler from './cmd/ResizeShapeHandler';
  26. import SpaceToolHandler from './cmd/SpaceToolHandler';
  27. import ToggleShapeCollapseHandler from './cmd/ToggleShapeCollapseHandler';
  28. import UpdateAttachmentHandler from './cmd/UpdateAttachmentHandler';
  29. import UpdateWaypointsHandler from './cmd/UpdateWaypointsHandler';
  30. /**
  31. * The basic modeling entry point.
  32. *
  33. * @param {EventBus} eventBus
  34. * @param {ElementFactory} elementFactory
  35. * @param {CommandStack} commandStack
  36. */
  37. export default function Modeling(eventBus, elementFactory, commandStack) {
  38. this._eventBus = eventBus;
  39. this._elementFactory = elementFactory;
  40. this._commandStack = commandStack;
  41. var self = this;
  42. eventBus.on('diagram.init', function() {
  43. // register modeling handlers
  44. self.registerHandlers(commandStack);
  45. });
  46. }
  47. Modeling.$inject = [ 'eventBus', 'elementFactory', 'commandStack' ];
  48. Modeling.prototype.getHandlers = function() {
  49. return {
  50. 'shape.append': AppendShapeHandler,
  51. 'shape.create': CreateShapeHandler,
  52. 'shape.delete': DeleteShapeHandler,
  53. 'shape.move': MoveShapeHandler,
  54. 'shape.resize': ResizeShapeHandler,
  55. 'shape.replace': ReplaceShapeHandler,
  56. 'shape.toggleCollapse': ToggleShapeCollapseHandler,
  57. 'spaceTool': SpaceToolHandler,
  58. 'label.create': CreateLabelHandler,
  59. 'connection.create': CreateConnectionHandler,
  60. 'connection.delete': DeleteConnectionHandler,
  61. 'connection.move': MoveConnectionHandler,
  62. 'connection.layout': LayoutConnectionHandler,
  63. 'connection.updateWaypoints': UpdateWaypointsHandler,
  64. 'connection.reconnect': ReconnectConnectionHandler,
  65. 'elements.create': CreateElementsHandler,
  66. 'elements.move': MoveElementsHandler,
  67. 'elements.delete': DeleteElementsHandler,
  68. 'elements.distribute': DistributeElementsHandler,
  69. 'elements.align': AlignElementsHandler,
  70. 'element.updateAttachment': UpdateAttachmentHandler
  71. };
  72. };
  73. /**
  74. * Register handlers with the command stack
  75. *
  76. * @param {CommandStack} commandStack
  77. */
  78. Modeling.prototype.registerHandlers = function(commandStack) {
  79. forEach(this.getHandlers(), function(handler, id) {
  80. commandStack.registerHandler(id, handler);
  81. });
  82. };
  83. // modeling helpers //////////////////////
  84. Modeling.prototype.moveShape = function(shape, delta, newParent, newParentIndex, hints) {
  85. if (typeof newParentIndex === 'object') {
  86. hints = newParentIndex;
  87. newParentIndex = null;
  88. }
  89. var context = {
  90. shape: shape,
  91. delta: delta,
  92. newParent: newParent,
  93. newParentIndex: newParentIndex,
  94. hints: hints || {}
  95. };
  96. this._commandStack.execute('shape.move', context);
  97. };
  98. /**
  99. * Update the attachment of the given shape.
  100. *
  101. * @param {djs.mode.Base} shape
  102. * @param {djs.model.Base} [newHost]
  103. */
  104. Modeling.prototype.updateAttachment = function(shape, newHost) {
  105. var context = {
  106. shape: shape,
  107. newHost: newHost
  108. };
  109. this._commandStack.execute('element.updateAttachment', context);
  110. };
  111. /**
  112. * Move a number of shapes to a new target, either setting it as
  113. * the new parent or attaching it.
  114. *
  115. * @param {Array<djs.mode.Base>} shapes
  116. * @param {Point} delta
  117. * @param {djs.model.Base} [target]
  118. * @param {Object} [hints]
  119. * @param {boolean} [hints.attach=false]
  120. */
  121. Modeling.prototype.moveElements = function(shapes, delta, target, hints) {
  122. hints = hints || {};
  123. var attach = hints.attach;
  124. var newParent = target,
  125. newHost;
  126. if (attach === true) {
  127. newHost = target;
  128. newParent = target.parent;
  129. } else
  130. if (attach === false) {
  131. newHost = null;
  132. }
  133. var context = {
  134. shapes: shapes,
  135. delta: delta,
  136. newParent: newParent,
  137. newHost: newHost,
  138. hints: hints
  139. };
  140. this._commandStack.execute('elements.move', context);
  141. };
  142. Modeling.prototype.moveConnection = function(connection, delta, newParent, newParentIndex, hints) {
  143. if (typeof newParentIndex === 'object') {
  144. hints = newParentIndex;
  145. newParentIndex = undefined;
  146. }
  147. var context = {
  148. connection: connection,
  149. delta: delta,
  150. newParent: newParent,
  151. newParentIndex: newParentIndex,
  152. hints: hints || {}
  153. };
  154. this._commandStack.execute('connection.move', context);
  155. };
  156. Modeling.prototype.layoutConnection = function(connection, hints) {
  157. var context = {
  158. connection: connection,
  159. hints: hints || {}
  160. };
  161. this._commandStack.execute('connection.layout', context);
  162. };
  163. /**
  164. * Create connection.
  165. *
  166. * @param {djs.model.Base} source
  167. * @param {djs.model.Base} target
  168. * @param {number} [parentIndex]
  169. * @param {Object|djs.model.Connection} connection
  170. * @param {djs.model.Base} parent
  171. * @param {Object} hints
  172. *
  173. * @return {djs.model.Connection} the created connection.
  174. */
  175. Modeling.prototype.createConnection = function(source, target, parentIndex, connection, parent, hints) {
  176. if (typeof parentIndex === 'object') {
  177. hints = parent;
  178. parent = connection;
  179. connection = parentIndex;
  180. parentIndex = undefined;
  181. }
  182. connection = this._create('connection', connection);
  183. var context = {
  184. source: source,
  185. target: target,
  186. parent: parent,
  187. parentIndex: parentIndex,
  188. connection: connection,
  189. hints: hints
  190. };
  191. this._commandStack.execute('connection.create', context);
  192. return context.connection;
  193. };
  194. /**
  195. * Create a shape at the specified position.
  196. *
  197. * @param {djs.model.Shape|Object} shape
  198. * @param {Point} position
  199. * @param {djs.model.Shape|djs.model.Root} target
  200. * @param {number} [parentIndex] position in parents children list
  201. * @param {Object} [hints]
  202. * @param {boolean} [hints.attach] whether to attach to target or become a child
  203. *
  204. * @return {djs.model.Shape} the created shape
  205. */
  206. Modeling.prototype.createShape = function(shape, position, target, parentIndex, hints) {
  207. if (typeof parentIndex !== 'number') {
  208. hints = parentIndex;
  209. parentIndex = undefined;
  210. }
  211. hints = hints || {};
  212. var attach = hints.attach,
  213. parent,
  214. host;
  215. shape = this._create('shape', shape);
  216. if (attach) {
  217. parent = target.parent;
  218. host = target;
  219. } else {
  220. parent = target;
  221. }
  222. var context = {
  223. position: position,
  224. shape: shape,
  225. parent: parent,
  226. parentIndex: parentIndex,
  227. host: host,
  228. hints: hints
  229. };
  230. this._commandStack.execute('shape.create', context);
  231. return context.shape;
  232. };
  233. Modeling.prototype.createElements = function(elements, position, parent, parentIndex, hints) {
  234. if (!isArray(elements)) {
  235. elements = [ elements ];
  236. }
  237. if (typeof parentIndex !== 'number') {
  238. hints = parentIndex;
  239. parentIndex = undefined;
  240. }
  241. hints = hints || {};
  242. var context = {
  243. position: position,
  244. elements: elements,
  245. parent: parent,
  246. parentIndex: parentIndex,
  247. hints: hints
  248. };
  249. this._commandStack.execute('elements.create', context);
  250. return context.elements;
  251. };
  252. Modeling.prototype.createLabel = function(labelTarget, position, label, parent) {
  253. label = this._create('label', label);
  254. var context = {
  255. labelTarget: labelTarget,
  256. position: position,
  257. parent: parent || labelTarget.parent,
  258. shape: label
  259. };
  260. this._commandStack.execute('label.create', context);
  261. return context.shape;
  262. };
  263. /**
  264. * Append shape to given source, drawing a connection
  265. * between source and the newly created shape.
  266. *
  267. * @param {djs.model.Shape} source
  268. * @param {djs.model.Shape|Object} shape
  269. * @param {Point} position
  270. * @param {djs.model.Shape} target
  271. * @param {Object} [hints]
  272. * @param {boolean} [hints.attach]
  273. * @param {djs.model.Connection|Object} [hints.connection]
  274. * @param {djs.model.Base} [hints.connectionParent]
  275. *
  276. * @return {djs.model.Shape} the newly created shape
  277. */
  278. Modeling.prototype.appendShape = function(source, shape, position, target, hints) {
  279. hints = hints || {};
  280. shape = this._create('shape', shape);
  281. var context = {
  282. source: source,
  283. position: position,
  284. target: target,
  285. shape: shape,
  286. connection: hints.connection,
  287. connectionParent: hints.connectionParent,
  288. hints: hints
  289. };
  290. this._commandStack.execute('shape.append', context);
  291. return context.shape;
  292. };
  293. Modeling.prototype.removeElements = function(elements) {
  294. var context = {
  295. elements: elements
  296. };
  297. this._commandStack.execute('elements.delete', context);
  298. };
  299. Modeling.prototype.distributeElements = function(groups, axis, dimension) {
  300. var context = {
  301. groups: groups,
  302. axis: axis,
  303. dimension: dimension
  304. };
  305. this._commandStack.execute('elements.distribute', context);
  306. };
  307. Modeling.prototype.removeShape = function(shape, hints) {
  308. var context = {
  309. shape: shape,
  310. hints: hints || {}
  311. };
  312. this._commandStack.execute('shape.delete', context);
  313. };
  314. Modeling.prototype.removeConnection = function(connection, hints) {
  315. var context = {
  316. connection: connection,
  317. hints: hints || {}
  318. };
  319. this._commandStack.execute('connection.delete', context);
  320. };
  321. Modeling.prototype.replaceShape = function(oldShape, newShape, hints) {
  322. var context = {
  323. oldShape: oldShape,
  324. newData: newShape,
  325. hints: hints || {}
  326. };
  327. this._commandStack.execute('shape.replace', context);
  328. return context.newShape;
  329. };
  330. Modeling.prototype.alignElements = function(elements, alignment) {
  331. var context = {
  332. elements: elements,
  333. alignment: alignment
  334. };
  335. this._commandStack.execute('elements.align', context);
  336. };
  337. Modeling.prototype.resizeShape = function(shape, newBounds, minBounds, hints) {
  338. var context = {
  339. shape: shape,
  340. newBounds: newBounds,
  341. minBounds: minBounds,
  342. hints: hints
  343. };
  344. this._commandStack.execute('shape.resize', context);
  345. };
  346. Modeling.prototype.createSpace = function(movingShapes, resizingShapes, delta, direction, start) {
  347. var context = {
  348. delta: delta,
  349. direction: direction,
  350. movingShapes: movingShapes,
  351. resizingShapes: resizingShapes,
  352. start: start
  353. };
  354. this._commandStack.execute('spaceTool', context);
  355. };
  356. Modeling.prototype.updateWaypoints = function(connection, newWaypoints, hints) {
  357. var context = {
  358. connection: connection,
  359. newWaypoints: newWaypoints,
  360. hints: hints || {}
  361. };
  362. this._commandStack.execute('connection.updateWaypoints', context);
  363. };
  364. Modeling.prototype.reconnect = function(connection, source, target, dockingOrPoints, hints) {
  365. var context = {
  366. connection: connection,
  367. newSource: source,
  368. newTarget: target,
  369. dockingOrPoints: dockingOrPoints,
  370. hints: hints || {}
  371. };
  372. this._commandStack.execute('connection.reconnect', context);
  373. };
  374. Modeling.prototype.reconnectStart = function(connection, newSource, dockingOrPoints, hints) {
  375. if (!hints) {
  376. hints = {};
  377. }
  378. this.reconnect(connection, newSource, connection.target, dockingOrPoints, assign(hints, {
  379. docking: 'source'
  380. }));
  381. };
  382. Modeling.prototype.reconnectEnd = function(connection, newTarget, dockingOrPoints, hints) {
  383. if (!hints) {
  384. hints = {};
  385. }
  386. this.reconnect(connection, connection.source, newTarget, dockingOrPoints, assign(hints, {
  387. docking: 'target'
  388. }));
  389. };
  390. Modeling.prototype.connect = function(source, target, attrs, hints) {
  391. return this.createConnection(source, target, attrs || {}, source.parent, hints);
  392. };
  393. Modeling.prototype._create = function(type, attrs) {
  394. if (attrs instanceof Base) {
  395. return attrs;
  396. } else {
  397. return this._elementFactory.create(type, attrs);
  398. }
  399. };
  400. Modeling.prototype.toggleCollapse = function(shape, hints) {
  401. var context = {
  402. shape: shape,
  403. hints: hints || {}
  404. };
  405. this._commandStack.execute('shape.toggleCollapse', context);
  406. };