UpdatePropertiesHandler.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. import {
  2. reduce,
  3. keys,
  4. forEach,
  5. assign
  6. } from 'min-dash';
  7. import {
  8. getBusinessObject
  9. } from '../../../util/ModelUtil';
  10. var DEFAULT_FLOW = 'default',
  11. ID = 'id',
  12. DI = 'di';
  13. var NULL_DIMENSIONS = {
  14. width: 0,
  15. height: 0
  16. };
  17. /**
  18. * A handler that implements a BPMN 2.0 property update.
  19. *
  20. * This should be used to set simple properties on elements with
  21. * an underlying BPMN business object.
  22. *
  23. * Use respective diagram-js provided handlers if you would
  24. * like to perform automated modeling.
  25. */
  26. export default function UpdatePropertiesHandler(
  27. elementRegistry, moddle, translate,
  28. modeling, textRenderer) {
  29. this._elementRegistry = elementRegistry;
  30. this._moddle = moddle;
  31. this._translate = translate;
  32. this._modeling = modeling;
  33. this._textRenderer = textRenderer;
  34. }
  35. UpdatePropertiesHandler.$inject = [
  36. 'elementRegistry',
  37. 'moddle',
  38. 'translate',
  39. 'modeling',
  40. 'textRenderer'
  41. ];
  42. // api //////////////////////
  43. /**
  44. * Updates a BPMN element with a list of new properties
  45. *
  46. * @param {Object} context
  47. * @param {djs.model.Base} context.element the element to update
  48. * @param {Object} context.properties a list of properties to set on the element's
  49. * businessObject (the BPMN model element)
  50. *
  51. * @return {Array<djs.model.Base>} the updated element
  52. */
  53. UpdatePropertiesHandler.prototype.execute = function(context) {
  54. var element = context.element,
  55. changed = [ element ],
  56. translate = this._translate;
  57. if (!element) {
  58. throw new Error(translate('element required'));
  59. }
  60. var elementRegistry = this._elementRegistry,
  61. ids = this._moddle.ids;
  62. var businessObject = element.businessObject,
  63. properties = unwrapBusinessObjects(context.properties),
  64. oldProperties = context.oldProperties || getProperties(businessObject, properties);
  65. if (isIdChange(properties, businessObject)) {
  66. ids.unclaim(businessObject[ID]);
  67. elementRegistry.updateId(element, properties[ID]);
  68. ids.claim(properties[ID], businessObject);
  69. }
  70. // correctly indicate visual changes on default flow updates
  71. if (DEFAULT_FLOW in properties) {
  72. if (properties[DEFAULT_FLOW]) {
  73. changed.push(elementRegistry.get(properties[DEFAULT_FLOW].id));
  74. }
  75. if (businessObject[DEFAULT_FLOW]) {
  76. changed.push(elementRegistry.get(businessObject[DEFAULT_FLOW].id));
  77. }
  78. }
  79. // update properties
  80. setProperties(businessObject, properties);
  81. // store old values
  82. context.oldProperties = oldProperties;
  83. context.changed = changed;
  84. // indicate changed on objects affected by the update
  85. return changed;
  86. };
  87. UpdatePropertiesHandler.prototype.postExecute = function(context) {
  88. var element = context.element,
  89. label = element.label;
  90. var text = label && getBusinessObject(label).name;
  91. if (!text) {
  92. return;
  93. }
  94. // get layouted text bounds and resize external
  95. // external label accordingly
  96. var newLabelBounds = this._textRenderer.getExternalLabelBounds(label, text);
  97. this._modeling.resizeShape(label, newLabelBounds, NULL_DIMENSIONS);
  98. };
  99. /**
  100. * Reverts the update on a BPMN elements properties.
  101. *
  102. * @param {Object} context
  103. *
  104. * @return {djs.model.Base} the updated element
  105. */
  106. UpdatePropertiesHandler.prototype.revert = function(context) {
  107. var element = context.element,
  108. properties = context.properties,
  109. oldProperties = context.oldProperties,
  110. businessObject = element.businessObject,
  111. elementRegistry = this._elementRegistry,
  112. ids = this._moddle.ids;
  113. // update properties
  114. setProperties(businessObject, oldProperties);
  115. if (isIdChange(properties, businessObject)) {
  116. ids.unclaim(properties[ID]);
  117. elementRegistry.updateId(element, oldProperties[ID]);
  118. ids.claim(oldProperties[ID], businessObject);
  119. }
  120. return context.changed;
  121. };
  122. function isIdChange(properties, businessObject) {
  123. return ID in properties && properties[ID] !== businessObject[ID];
  124. }
  125. function getProperties(businessObject, properties) {
  126. var propertyNames = keys(properties);
  127. return reduce(propertyNames, function(result, key) {
  128. // handle DI separately
  129. if (key !== DI) {
  130. result[key] = businessObject.get(key);
  131. } else {
  132. result[key] = getDiProperties(businessObject.di, keys(properties.di));
  133. }
  134. return result;
  135. }, {});
  136. }
  137. function getDiProperties(di, propertyNames) {
  138. return reduce(propertyNames, function(result, key) {
  139. result[key] = di.get(key);
  140. return result;
  141. }, {});
  142. }
  143. function setProperties(businessObject, properties) {
  144. forEach(properties, function(value, key) {
  145. if (key !== DI) {
  146. businessObject.set(key, value);
  147. } else {
  148. // only update, if businessObject.di exists
  149. if (businessObject.di) {
  150. setDiProperties(businessObject.di, value);
  151. }
  152. }
  153. });
  154. }
  155. function setDiProperties(di, properties) {
  156. forEach(properties, function(value, key) {
  157. di.set(key, value);
  158. });
  159. }
  160. var referencePropertyNames = [ 'default' ];
  161. /**
  162. * Make sure we unwrap the actual business object
  163. * behind diagram element that may have been
  164. * passed as arguments.
  165. *
  166. * @param {Object} properties
  167. *
  168. * @return {Object} unwrappedProps
  169. */
  170. function unwrapBusinessObjects(properties) {
  171. var unwrappedProps = assign({}, properties);
  172. referencePropertyNames.forEach(function(name) {
  173. if (name in properties) {
  174. unwrappedProps[name] = getBusinessObject(unwrappedProps[name]);
  175. }
  176. });
  177. return unwrappedProps;
  178. }