ElementFactory.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. import {
  2. assign,
  3. forEach,
  4. isDefined,
  5. isObject,
  6. omit
  7. } from 'min-dash';
  8. import inherits from 'inherits-browser';
  9. import {
  10. getBusinessObject,
  11. getDi,
  12. is
  13. } from '../../util/ModelUtil';
  14. import {
  15. isAny
  16. } from '../modeling/util/ModelingUtil';
  17. import {
  18. isExpanded
  19. } from '../../util/DiUtil';
  20. import BaseElementFactory from 'diagram-js/lib/core/ElementFactory';
  21. import {
  22. DEFAULT_LABEL_SIZE
  23. } from '../../util/LabelUtil';
  24. import {
  25. ensureCompatDiRef
  26. } from '../../util/CompatibilityUtil';
  27. /**
  28. * @typedef {import('diagram-js/lib/i18n/translate/translate').default} Translate
  29. *
  30. * @typedef {import('diagram-js/lib/util/Types').Dimensions} Dimensions
  31. *
  32. * @typedef {import('./BpmnFactory').default} BpmnFactory
  33. *
  34. * @typedef {import('../../model/Types').BpmnAttributes} BpmnAttributes
  35. * @typedef {import('../../model/Types').Connection} Connection
  36. * @typedef {import('../../model/Types').Element} Element
  37. * @typedef {import('../../model/Types').Label} Label
  38. * @typedef {import('../../model/Types').Root} Root
  39. * @typedef {import('../../model/Types').Shape} Shape
  40. * @typedef {import('../../model/Types').Moddle} Moddle
  41. * @typedef {import('../../model/Types').ModdleElement} ModdleElement
  42. */
  43. /**
  44. * A BPMN-specific element factory.
  45. *
  46. * @template {Connection} [T=Connection]
  47. * @template {Label} [U=Label]
  48. * @template {Root} [V=Root]
  49. * @template {Shape} [W=Shape]
  50. *
  51. * @extends {BaseElementFactory<T, U, V, W>}
  52. *
  53. * @param {BpmnFactory} bpmnFactory
  54. * @param {Moddle} moddle
  55. * @param {Translate} translate
  56. */
  57. export default function ElementFactory(bpmnFactory, moddle, translate) {
  58. BaseElementFactory.call(this);
  59. this._bpmnFactory = bpmnFactory;
  60. this._moddle = moddle;
  61. this._translate = translate;
  62. }
  63. inherits(ElementFactory, BaseElementFactory);
  64. ElementFactory.$inject = [
  65. 'bpmnFactory',
  66. 'moddle',
  67. 'translate'
  68. ];
  69. ElementFactory.prototype._baseCreate = BaseElementFactory.prototype.create;
  70. /**
  71. * Create a root element.
  72. *
  73. * @overlord
  74. * @param {'root'} elementType
  75. * @param {Partial<Root> & Partial<BpmnAttributes>} [attrs]
  76. * @return {V}
  77. */
  78. /**
  79. * Create a shape.
  80. *
  81. * @overlord
  82. * @param {'shape'} elementType
  83. * @param {Partial<Shape> & Partial<BpmnAttributes>} [attrs]
  84. * @return {W}
  85. */
  86. /**
  87. * Create a connection.
  88. *
  89. * @overlord
  90. * @param {'connection'} elementType
  91. * @param {Partial<Connection> & Partial<BpmnAttributes>} [attrs]
  92. * @return {T}
  93. */
  94. /**
  95. * Create a label.
  96. *
  97. * @param {'label'} elementType
  98. * @param {Partial<Label> & Partial<BpmnAttributes>} [attrs]
  99. * @return {U}
  100. */
  101. ElementFactory.prototype.create = function(elementType, attrs) {
  102. // no special magic for labels,
  103. // we assume their businessObjects have already been created
  104. // and wired via attrs
  105. if (elementType === 'label') {
  106. var di = attrs.di || this._bpmnFactory.createDiLabel();
  107. return this._baseCreate(elementType, assign({ type: 'label', di: di }, DEFAULT_LABEL_SIZE, attrs));
  108. }
  109. return this.createElement(elementType, attrs);
  110. };
  111. /**
  112. * Create a BPMN root element.
  113. *
  114. * @overlord
  115. * @param {'root'} elementType
  116. * @param {Partial<Root> & Partial<BpmnAttributes>} [attrs]
  117. * @return {V}
  118. */
  119. /**
  120. * Create a BPMN shape.
  121. *
  122. * @overlord
  123. * @param {'shape'} elementType
  124. * @param {Partial<Shape> & Partial<BpmnAttributes>} [attrs]
  125. * @return {W}
  126. */
  127. /**
  128. * Create a BPMN connection.
  129. *
  130. * @param {'connection'} elementType
  131. * @param {Partial<Connection> & Partial<BpmnAttributes>} [attrs]
  132. * @return {T}
  133. */
  134. ElementFactory.prototype.createElement = function(elementType, attrs) {
  135. var size,
  136. translate = this._translate;
  137. attrs = assign({}, attrs || {});
  138. var businessObject = attrs.businessObject,
  139. di = attrs.di;
  140. if (!businessObject) {
  141. if (!attrs.type) {
  142. throw new Error(translate('no shape type specified'));
  143. }
  144. businessObject = this._bpmnFactory.create(attrs.type);
  145. ensureCompatDiRef(businessObject);
  146. }
  147. if (!isModdleDi(di)) {
  148. var diAttrs = assign(
  149. {},
  150. di || {},
  151. { id: businessObject.id + '_di' }
  152. );
  153. if (elementType === 'root') {
  154. di = this._bpmnFactory.createDiPlane(businessObject, diAttrs);
  155. } else
  156. if (elementType === 'connection') {
  157. di = this._bpmnFactory.createDiEdge(businessObject, diAttrs);
  158. } else {
  159. di = this._bpmnFactory.createDiShape(businessObject, diAttrs);
  160. }
  161. }
  162. if (is(businessObject, 'bpmn:Group')) {
  163. attrs = assign({
  164. isFrame: true
  165. }, attrs);
  166. }
  167. attrs = applyAttributes(businessObject, attrs, [
  168. 'processRef',
  169. 'isInterrupting',
  170. 'associationDirection',
  171. 'isForCompensation'
  172. ]);
  173. if (attrs.isExpanded) {
  174. attrs = applyAttribute(di, attrs, 'isExpanded');
  175. }
  176. if (is(businessObject, 'bpmn:SubProcess')) {
  177. attrs.collapsed = !isExpanded(businessObject, di);
  178. }
  179. if (is(businessObject, 'bpmn:ExclusiveGateway')) {
  180. di.isMarkerVisible = true;
  181. }
  182. if (isDefined(attrs.triggeredByEvent)) {
  183. businessObject.triggeredByEvent = attrs.triggeredByEvent;
  184. delete attrs.triggeredByEvent;
  185. }
  186. if (isDefined(attrs.cancelActivity)) {
  187. businessObject.cancelActivity = attrs.cancelActivity;
  188. delete attrs.cancelActivity;
  189. }
  190. var eventDefinitions,
  191. newEventDefinition;
  192. if (attrs.eventDefinitionType) {
  193. eventDefinitions = businessObject.get('eventDefinitions') || [];
  194. newEventDefinition = this._bpmnFactory.create(attrs.eventDefinitionType, attrs.eventDefinitionAttrs);
  195. if (attrs.eventDefinitionType === 'bpmn:ConditionalEventDefinition') {
  196. newEventDefinition.condition = this._bpmnFactory.create('bpmn:FormalExpression');
  197. }
  198. eventDefinitions.push(newEventDefinition);
  199. newEventDefinition.$parent = businessObject;
  200. businessObject.eventDefinitions = eventDefinitions;
  201. delete attrs.eventDefinitionType;
  202. }
  203. size = this.getDefaultSize(businessObject, di);
  204. attrs = assign({
  205. id: businessObject.id
  206. }, size, attrs, {
  207. businessObject: businessObject,
  208. di: di
  209. });
  210. return this._baseCreate(elementType, attrs);
  211. };
  212. /**
  213. * Get the default size of a diagram element.
  214. *
  215. * @param {Element} element The element.
  216. * @param {ModdleElement} di The DI.
  217. *
  218. * @return {Dimensions} Default width and height of the element.
  219. */
  220. ElementFactory.prototype.getDefaultSize = function(element, di) {
  221. var bo = getBusinessObject(element);
  222. di = di || getDi(element);
  223. if (is(bo, 'bpmn:SubProcess')) {
  224. if (isExpanded(bo, di)) {
  225. return { width: 350, height: 200 };
  226. } else {
  227. return { width: 100, height: 80 };
  228. }
  229. }
  230. if (is(bo, 'bpmn:Task')) {
  231. return { width: 100, height: 80 };
  232. }
  233. if (is(bo, 'bpmn:Gateway')) {
  234. return { width: 50, height: 50 };
  235. }
  236. if (is(bo, 'bpmn:Event')) {
  237. return { width: 36, height: 36 };
  238. }
  239. if (is(bo, 'bpmn:Participant')) {
  240. if (isExpanded(bo, di)) {
  241. return { width: 600, height: 250 };
  242. } else {
  243. return { width: 400, height: 60 };
  244. }
  245. }
  246. if (is(bo, 'bpmn:Lane')) {
  247. return { width: 400, height: 100 };
  248. }
  249. if (is(bo, 'bpmn:DataObjectReference')) {
  250. return { width: 36, height: 50 };
  251. }
  252. if (is(bo, 'bpmn:DataStoreReference')) {
  253. return { width: 50, height: 50 };
  254. }
  255. if (is(bo, 'bpmn:TextAnnotation')) {
  256. return { width: 100, height: 30 };
  257. }
  258. if (is(bo, 'bpmn:Group')) {
  259. return { width: 300, height: 300 };
  260. }
  261. return { width: 100, height: 80 };
  262. };
  263. /**
  264. * Create participant.
  265. *
  266. * @param {boolean|Partial<Shape> & Partial<BpmnAttributes>} [attrs]
  267. * Attributes or whether the participant is expanded.
  268. *
  269. * @return {W} The created participant.
  270. */
  271. ElementFactory.prototype.createParticipantShape = function(attrs) {
  272. if (!isObject(attrs)) {
  273. attrs = { isExpanded: attrs };
  274. }
  275. attrs = assign({ type: 'bpmn:Participant' }, attrs || {});
  276. // participants are expanded by default
  277. if (attrs.isExpanded !== false) {
  278. attrs.processRef = this._bpmnFactory.create('bpmn:Process');
  279. }
  280. return this.createShape(attrs);
  281. };
  282. // helpers //////////////////////
  283. /**
  284. * Apply attributes from a map to the given element, remove attribute from the
  285. * map on application.
  286. *
  287. * @param {Element} element
  288. * @param {Object} attrs (in/out map of attributes)
  289. * @param {string[]} attributeNames name of attributes to apply
  290. *
  291. * @return {Object} changed attrs
  292. */
  293. function applyAttributes(element, attrs, attributeNames) {
  294. forEach(attributeNames, function(property) {
  295. attrs = applyAttribute(element, attrs, property);
  296. });
  297. return attrs;
  298. }
  299. /**
  300. * Apply named property to element and drain it from the attrs collection.
  301. *
  302. * @param {Element} element
  303. * @param {Object} attrs (in/out map of attributes)
  304. * @param {string} attributeName to apply
  305. *
  306. * @return {Object} changed attrs
  307. */
  308. function applyAttribute(element, attrs, attributeName) {
  309. if (attrs[attributeName] === undefined) {
  310. return attrs;
  311. }
  312. element[attributeName] = attrs[attributeName];
  313. return omit(attrs, [ attributeName ]);
  314. }
  315. /**
  316. * @param {Element} element
  317. *
  318. * @return {boolean}
  319. */
  320. function isModdleDi(element) {
  321. return isAny(element, [
  322. 'bpmndi:BPMNShape',
  323. 'bpmndi:BPMNEdge',
  324. 'bpmndi:BPMNDiagram',
  325. 'bpmndi:BPMNPlane',
  326. ]);
  327. }