From 39f6ce9b11b4a0d85bc8d288e0b1f140171fa8db Mon Sep 17 00:00:00 2001 From: zhangxu Date: Fri, 2 Sep 2022 17:17:09 +0800 Subject: [PATCH] init --- CHANGELOG.md | 813 + README.md | 110 + dist/assets/bpmn-font/css/bpmn-codes.css | 108 + dist/assets/bpmn-font/css/bpmn-embedded.css | 161 + dist/assets/bpmn-font/css/bpmn.css | 164 + dist/assets/bpmn-font/font/bpmn.eot | Bin 0 -> 47728 bytes dist/assets/bpmn-font/font/bpmn.svg | 224 + dist/assets/bpmn-font/font/bpmn.ttf | Bin 0 -> 47576 bytes dist/assets/bpmn-font/font/bpmn.woff | Bin 0 -> 15916 bytes dist/assets/bpmn-font/font/bpmn.woff2 | Bin 0 -> 12932 bytes dist/assets/bpmn-js.css | 144 + dist/assets/diagram-js.css | 823 + dist/bpmn-modeler.development.js | 60829 ++++++++++++++++ dist/bpmn-modeler.production.min.js | 34 + dist/bpmn-navigated-viewer.development.js | 22665 ++++++ dist/bpmn-navigated-viewer.production.min.js | 24 + dist/bpmn-viewer.development.js | 21654 ++++++ dist/bpmn-viewer.production.min.js | 24 + index.js | 3 + lib/BaseModeler.js | 74 + lib/BaseViewer.js | 786 + lib/Modeler.js | 220 + lib/NavigatedViewer.js | 31 + lib/Viewer.js | 75 + lib/core/index.js | 9 + lib/draw/BpmnRenderUtil.js | 157 + lib/draw/BpmnRenderer.js | 1928 + lib/draw/PathMap.js | 471 + lib/draw/TextRenderer.js | 116 + lib/draw/index.js | 11 + .../AlignElementsContextPadProvider.js | 87 + .../align-elements/AlignElementsIcons.js | 15 + .../AlignElementsMenuProvider.js | 72 + .../align-elements/BpmnAlignElements.js | 39 + lib/features/align-elements/index.js | 23 + .../resources/align-bottom-tool.svg | 5 + .../align-horizontal-center-tool.svg | 5 + .../resources/align-left-tool.svg | 5 + .../resources/align-right-tool.svg | 5 + .../align-elements/resources/align-tool.svg | 5 + .../resources/align-top-tool.svg | 5 + .../resources/align-vertical-center-tool.svg | 5 + lib/features/auto-place/BpmnAutoPlace.js | 18 + lib/features/auto-place/BpmnAutoPlaceUtil.js | 148 + lib/features/auto-place/index.js | 9 + lib/features/auto-resize/BpmnAutoResize.js | 38 + .../auto-resize/BpmnAutoResizeProvider.js | 57 + lib/features/auto-resize/index.js | 12 + .../context-pad/ContextPadProvider.js | 526 + lib/features/context-pad/index.js | 21 + lib/features/copy-paste/BpmnCopyPaste.js | 185 + lib/features/copy-paste/ModdleCopy.js | 304 + lib/features/copy-paste/index.js | 13 + lib/features/di-ordering/BpmnDiOrdering.js | 40 + lib/features/di-ordering/index.js | 8 + .../BpmnDistributeElements.js | 55 + .../DistributeElementsIcons.js | 10 + .../DistributeElementsMenuProvider.js | 69 + lib/features/distribute-elements/index.js | 19 + .../distribute-horizontally-tool.svg | 5 + .../resources/distribute-vertically-tool.svg | 5 + .../drilldown/DrilldownBreadcrumbs.js | 122 + lib/features/drilldown/DrilldownCentering.js | 118 + .../drilldown/DrilldownOverlayBehavior.js | 181 + .../drilldown/SubprocessCompatibility.js | 218 + lib/features/drilldown/index.js | 17 + .../editor-actions/BpmnEditorActions.js | 177 + lib/features/editor-actions/index.js | 10 + .../grid-snapping/BpmnGridSnapping.js | 25 + .../behavior/GridSnappingAutoPlaceBehavior.js | 59 + .../GridSnappingLayoutConnectionBehavior.js | 144 + .../GridSnappingParticipantBehavior.js | 36 + lib/features/grid-snapping/behavior/index.js | 14 + lib/features/grid-snapping/index.js | 13 + .../BpmnInteractionEvents.js | 118 + lib/features/interaction-events/index.js | 6 + lib/features/keyboard/BpmnKeyboardBindings.js | 158 + lib/features/keyboard/index.js | 11 + .../label-editing/LabelEditingPreview.js | 138 + .../label-editing/LabelEditingProvider.js | 426 + lib/features/label-editing/LabelUtil.js | 67 + .../label-editing/cmd/UpdateLabelHandler.js | 165 + lib/features/label-editing/index.js | 21 + lib/features/modeling/BpmnFactory.js | 127 + lib/features/modeling/BpmnLayouter.js | 399 + lib/features/modeling/BpmnUpdater.js | 762 + lib/features/modeling/ElementFactory.js | 292 + lib/features/modeling/Modeling.js | 202 + .../AdaptiveLabelPositioningBehavior.js | 274 + .../modeling/behavior/AppendBehavior.js | 42 + .../modeling/behavior/AssociationBehavior.js | 35 + .../modeling/behavior/AttachEventBehavior.js | 98 + .../behavior/BoundaryEventBehavior.js | 68 + .../modeling/behavior/CreateBehavior.js | 28 + .../behavior/CreateDataObjectBehavior.js | 38 + .../behavior/CreateParticipantBehavior.js | 230 + .../behavior/DataInputAssociationBehavior.js | 158 + .../modeling/behavior/DataStoreBehavior.js | 212 + .../modeling/behavior/DeleteLaneBehavior.js | 112 + .../modeling/behavior/DetachEventBehavior.js | 94 + .../modeling/behavior/DropOnFlowBehavior.js | 211 + .../behavior/EventBasedGatewayBehavior.js | 85 + .../modeling/behavior/FixHoverBehavior.js | 117 + .../modeling/behavior/GroupBehavior.js | 311 + .../modeling/behavior/ImportDockingFix.js | 81 + .../modeling/behavior/IsHorizontalFix.js | 45 + .../modeling/behavior/LabelBehavior.js | 403 + .../behavior/LayoutConnectionBehavior.js | 117 + .../modeling/behavior/MessageFlowBehavior.js | 89 + .../modeling/behavior/ModelingFeedback.js | 35 + .../behavior/RemoveElementBehavior.js | 82 + .../RemoveEmbeddedLabelBoundsBehavior.js | 34 + .../behavior/RemoveParticipantBehavior.js | 48 + .../behavior/ReplaceConnectionBehavior.js | 184 + .../behavior/ReplaceElementBehaviour.js | 129 + .../modeling/behavior/ResizeBehavior.js | 176 + .../modeling/behavior/ResizeLaneBehavior.js | 63 + .../behavior/RootElementReferenceBehavior.js | 184 + .../modeling/behavior/SpaceToolBehavior.js | 129 + .../behavior/SubProcessPlaneBehavior.js | 566 + .../behavior/SubProcessStartEventBehavior.js | 49 + .../ToggleCollapseConnectionBehaviour.js | 74 + .../ToggleElementCollapseBehaviour.js | 149 + .../modeling/behavior/UnclaimIdBehavior.js | 58 + .../behavior/UnsetDefaultFlowBehavior.js | 57 + .../behavior/UpdateFlowNodeRefsBehavior.js | 158 + lib/features/modeling/behavior/index.js | 119 + .../modeling/behavior/util/CategoryUtil.js | 83 + .../behavior/util/ConnectionLayoutUtil.js | 16 + .../modeling/behavior/util/GeometricUtil.js | 1 + .../modeling/behavior/util/LabelLayoutUtil.js | 33 + .../modeling/behavior/util/LayoutUtil.js | 230 + .../behavior/util/LineAttachmentUtil.js | 229 + .../modeling/behavior/util/LineIntersect.js | 36 + .../modeling/behavior/util/ResizeUtil.js | 1 + lib/features/modeling/cmd/AddLaneHandler.js | 101 + lib/features/modeling/cmd/IdClaimHandler.js | 36 + .../modeling/cmd/ResizeLaneHandler.js | 134 + lib/features/modeling/cmd/SetColorHandler.js | 144 + lib/features/modeling/cmd/SplitLaneHandler.js | 85 + .../modeling/cmd/UpdateCanvasRootHandler.js | 85 + .../modeling/cmd/UpdateFlowNodeRefsHandler.js | 193 + .../cmd/UpdateModdlePropertiesHandler.js | 93 + .../modeling/cmd/UpdatePropertiesHandler.js | 242 + .../cmd/UpdateSemanticParentHandler.js | 36 + lib/features/modeling/index.js | 48 + lib/features/modeling/util/LaneUtil.js | 154 + lib/features/modeling/util/ModelingUtil.js | 26 + lib/features/ordering/BpmnOrderingProvider.js | 181 + lib/features/ordering/index.js | 11 + lib/features/palette/PaletteProvider.js | 200 + lib/features/palette/index.js | 23 + .../popup-menu/ReplaceMenuProvider.js | 607 + lib/features/popup-menu/index.js | 14 + lib/features/popup-menu/util/TypeUtil.js | 44 + .../replace-preview/BpmnReplacePreview.js | 126 + lib/features/replace-preview/index.js | 11 + lib/features/replace/BpmnReplace.js | 331 + lib/features/replace/ReplaceOptions.js | 850 + lib/features/replace/index.js | 14 + lib/features/rules/BpmnRules.js | 954 + lib/features/rules/index.js | 11 + lib/features/search/BpmnSearchProvider.js | 129 + lib/features/search/index.js | 12 + lib/features/snapping/BpmnConnectSnapping.js | 212 + .../snapping/BpmnCreateMoveSnapping.js | 243 + lib/features/snapping/BpmnSnappingUtil.js | 12 + lib/features/snapping/index.js | 13 + lib/import/BpmnImporter.js | 347 + lib/import/BpmnTreeWalker.js | 465 + lib/import/Importer.js | 225 + lib/import/Util.js | 7 + lib/import/index.js | 10 + lib/util/CompatibilityUtil.js | 74 + lib/util/DiUtil.js | 68 + lib/util/DrilldownUtil.js | 66 + lib/util/LabelUtil.js | 156 + lib/util/ModelUtil.js | 55 + lib/util/PoweredByUtil.js | 99 + package.json | 109 + 180 files changed, 130324 insertions(+) create mode 100644 CHANGELOG.md create mode 100644 README.md create mode 100644 dist/assets/bpmn-font/css/bpmn-codes.css create mode 100644 dist/assets/bpmn-font/css/bpmn-embedded.css create mode 100644 dist/assets/bpmn-font/css/bpmn.css create mode 100644 dist/assets/bpmn-font/font/bpmn.eot create mode 100644 dist/assets/bpmn-font/font/bpmn.svg create mode 100644 dist/assets/bpmn-font/font/bpmn.ttf create mode 100644 dist/assets/bpmn-font/font/bpmn.woff create mode 100644 dist/assets/bpmn-font/font/bpmn.woff2 create mode 100644 dist/assets/bpmn-js.css create mode 100644 dist/assets/diagram-js.css create mode 100644 dist/bpmn-modeler.development.js create mode 100644 dist/bpmn-modeler.production.min.js create mode 100644 dist/bpmn-navigated-viewer.development.js create mode 100644 dist/bpmn-navigated-viewer.production.min.js create mode 100644 dist/bpmn-viewer.development.js create mode 100644 dist/bpmn-viewer.production.min.js create mode 100644 index.js create mode 100644 lib/BaseModeler.js create mode 100644 lib/BaseViewer.js create mode 100644 lib/Modeler.js create mode 100644 lib/NavigatedViewer.js create mode 100644 lib/Viewer.js create mode 100644 lib/core/index.js create mode 100644 lib/draw/BpmnRenderUtil.js create mode 100644 lib/draw/BpmnRenderer.js create mode 100644 lib/draw/PathMap.js create mode 100644 lib/draw/TextRenderer.js create mode 100644 lib/draw/index.js create mode 100644 lib/features/align-elements/AlignElementsContextPadProvider.js create mode 100644 lib/features/align-elements/AlignElementsIcons.js create mode 100644 lib/features/align-elements/AlignElementsMenuProvider.js create mode 100644 lib/features/align-elements/BpmnAlignElements.js create mode 100644 lib/features/align-elements/index.js create mode 100644 lib/features/align-elements/resources/align-bottom-tool.svg create mode 100644 lib/features/align-elements/resources/align-horizontal-center-tool.svg create mode 100644 lib/features/align-elements/resources/align-left-tool.svg create mode 100644 lib/features/align-elements/resources/align-right-tool.svg create mode 100644 lib/features/align-elements/resources/align-tool.svg create mode 100644 lib/features/align-elements/resources/align-top-tool.svg create mode 100644 lib/features/align-elements/resources/align-vertical-center-tool.svg create mode 100644 lib/features/auto-place/BpmnAutoPlace.js create mode 100644 lib/features/auto-place/BpmnAutoPlaceUtil.js create mode 100644 lib/features/auto-place/index.js create mode 100644 lib/features/auto-resize/BpmnAutoResize.js create mode 100644 lib/features/auto-resize/BpmnAutoResizeProvider.js create mode 100644 lib/features/auto-resize/index.js create mode 100644 lib/features/context-pad/ContextPadProvider.js create mode 100644 lib/features/context-pad/index.js create mode 100644 lib/features/copy-paste/BpmnCopyPaste.js create mode 100644 lib/features/copy-paste/ModdleCopy.js create mode 100644 lib/features/copy-paste/index.js create mode 100644 lib/features/di-ordering/BpmnDiOrdering.js create mode 100644 lib/features/di-ordering/index.js create mode 100644 lib/features/distribute-elements/BpmnDistributeElements.js create mode 100644 lib/features/distribute-elements/DistributeElementsIcons.js create mode 100644 lib/features/distribute-elements/DistributeElementsMenuProvider.js create mode 100644 lib/features/distribute-elements/index.js create mode 100644 lib/features/distribute-elements/resources/distribute-horizontally-tool.svg create mode 100644 lib/features/distribute-elements/resources/distribute-vertically-tool.svg create mode 100644 lib/features/drilldown/DrilldownBreadcrumbs.js create mode 100644 lib/features/drilldown/DrilldownCentering.js create mode 100644 lib/features/drilldown/DrilldownOverlayBehavior.js create mode 100644 lib/features/drilldown/SubprocessCompatibility.js create mode 100644 lib/features/drilldown/index.js create mode 100644 lib/features/editor-actions/BpmnEditorActions.js create mode 100644 lib/features/editor-actions/index.js create mode 100644 lib/features/grid-snapping/BpmnGridSnapping.js create mode 100644 lib/features/grid-snapping/behavior/GridSnappingAutoPlaceBehavior.js create mode 100644 lib/features/grid-snapping/behavior/GridSnappingLayoutConnectionBehavior.js create mode 100644 lib/features/grid-snapping/behavior/GridSnappingParticipantBehavior.js create mode 100644 lib/features/grid-snapping/behavior/index.js create mode 100644 lib/features/grid-snapping/index.js create mode 100644 lib/features/interaction-events/BpmnInteractionEvents.js create mode 100644 lib/features/interaction-events/index.js create mode 100644 lib/features/keyboard/BpmnKeyboardBindings.js create mode 100644 lib/features/keyboard/index.js create mode 100644 lib/features/label-editing/LabelEditingPreview.js create mode 100644 lib/features/label-editing/LabelEditingProvider.js create mode 100644 lib/features/label-editing/LabelUtil.js create mode 100644 lib/features/label-editing/cmd/UpdateLabelHandler.js create mode 100644 lib/features/label-editing/index.js create mode 100644 lib/features/modeling/BpmnFactory.js create mode 100644 lib/features/modeling/BpmnLayouter.js create mode 100644 lib/features/modeling/BpmnUpdater.js create mode 100644 lib/features/modeling/ElementFactory.js create mode 100644 lib/features/modeling/Modeling.js create mode 100644 lib/features/modeling/behavior/AdaptiveLabelPositioningBehavior.js create mode 100644 lib/features/modeling/behavior/AppendBehavior.js create mode 100644 lib/features/modeling/behavior/AssociationBehavior.js create mode 100644 lib/features/modeling/behavior/AttachEventBehavior.js create mode 100644 lib/features/modeling/behavior/BoundaryEventBehavior.js create mode 100644 lib/features/modeling/behavior/CreateBehavior.js create mode 100644 lib/features/modeling/behavior/CreateDataObjectBehavior.js create mode 100644 lib/features/modeling/behavior/CreateParticipantBehavior.js create mode 100644 lib/features/modeling/behavior/DataInputAssociationBehavior.js create mode 100644 lib/features/modeling/behavior/DataStoreBehavior.js create mode 100644 lib/features/modeling/behavior/DeleteLaneBehavior.js create mode 100644 lib/features/modeling/behavior/DetachEventBehavior.js create mode 100644 lib/features/modeling/behavior/DropOnFlowBehavior.js create mode 100644 lib/features/modeling/behavior/EventBasedGatewayBehavior.js create mode 100644 lib/features/modeling/behavior/FixHoverBehavior.js create mode 100644 lib/features/modeling/behavior/GroupBehavior.js create mode 100644 lib/features/modeling/behavior/ImportDockingFix.js create mode 100644 lib/features/modeling/behavior/IsHorizontalFix.js create mode 100644 lib/features/modeling/behavior/LabelBehavior.js create mode 100644 lib/features/modeling/behavior/LayoutConnectionBehavior.js create mode 100644 lib/features/modeling/behavior/MessageFlowBehavior.js create mode 100644 lib/features/modeling/behavior/ModelingFeedback.js create mode 100644 lib/features/modeling/behavior/RemoveElementBehavior.js create mode 100644 lib/features/modeling/behavior/RemoveEmbeddedLabelBoundsBehavior.js create mode 100644 lib/features/modeling/behavior/RemoveParticipantBehavior.js create mode 100644 lib/features/modeling/behavior/ReplaceConnectionBehavior.js create mode 100644 lib/features/modeling/behavior/ReplaceElementBehaviour.js create mode 100644 lib/features/modeling/behavior/ResizeBehavior.js create mode 100644 lib/features/modeling/behavior/ResizeLaneBehavior.js create mode 100644 lib/features/modeling/behavior/RootElementReferenceBehavior.js create mode 100644 lib/features/modeling/behavior/SpaceToolBehavior.js create mode 100644 lib/features/modeling/behavior/SubProcessPlaneBehavior.js create mode 100644 lib/features/modeling/behavior/SubProcessStartEventBehavior.js create mode 100644 lib/features/modeling/behavior/ToggleCollapseConnectionBehaviour.js create mode 100644 lib/features/modeling/behavior/ToggleElementCollapseBehaviour.js create mode 100644 lib/features/modeling/behavior/UnclaimIdBehavior.js create mode 100644 lib/features/modeling/behavior/UnsetDefaultFlowBehavior.js create mode 100644 lib/features/modeling/behavior/UpdateFlowNodeRefsBehavior.js create mode 100644 lib/features/modeling/behavior/index.js create mode 100644 lib/features/modeling/behavior/util/CategoryUtil.js create mode 100644 lib/features/modeling/behavior/util/ConnectionLayoutUtil.js create mode 100644 lib/features/modeling/behavior/util/GeometricUtil.js create mode 100644 lib/features/modeling/behavior/util/LabelLayoutUtil.js create mode 100644 lib/features/modeling/behavior/util/LayoutUtil.js create mode 100644 lib/features/modeling/behavior/util/LineAttachmentUtil.js create mode 100644 lib/features/modeling/behavior/util/LineIntersect.js create mode 100644 lib/features/modeling/behavior/util/ResizeUtil.js create mode 100644 lib/features/modeling/cmd/AddLaneHandler.js create mode 100644 lib/features/modeling/cmd/IdClaimHandler.js create mode 100644 lib/features/modeling/cmd/ResizeLaneHandler.js create mode 100644 lib/features/modeling/cmd/SetColorHandler.js create mode 100644 lib/features/modeling/cmd/SplitLaneHandler.js create mode 100644 lib/features/modeling/cmd/UpdateCanvasRootHandler.js create mode 100644 lib/features/modeling/cmd/UpdateFlowNodeRefsHandler.js create mode 100644 lib/features/modeling/cmd/UpdateModdlePropertiesHandler.js create mode 100644 lib/features/modeling/cmd/UpdatePropertiesHandler.js create mode 100644 lib/features/modeling/cmd/UpdateSemanticParentHandler.js create mode 100644 lib/features/modeling/index.js create mode 100644 lib/features/modeling/util/LaneUtil.js create mode 100644 lib/features/modeling/util/ModelingUtil.js create mode 100644 lib/features/ordering/BpmnOrderingProvider.js create mode 100644 lib/features/ordering/index.js create mode 100644 lib/features/palette/PaletteProvider.js create mode 100644 lib/features/palette/index.js create mode 100644 lib/features/popup-menu/ReplaceMenuProvider.js create mode 100644 lib/features/popup-menu/index.js create mode 100644 lib/features/popup-menu/util/TypeUtil.js create mode 100644 lib/features/replace-preview/BpmnReplacePreview.js create mode 100644 lib/features/replace-preview/index.js create mode 100644 lib/features/replace/BpmnReplace.js create mode 100644 lib/features/replace/ReplaceOptions.js create mode 100644 lib/features/replace/index.js create mode 100644 lib/features/rules/BpmnRules.js create mode 100644 lib/features/rules/index.js create mode 100644 lib/features/search/BpmnSearchProvider.js create mode 100644 lib/features/search/index.js create mode 100644 lib/features/snapping/BpmnConnectSnapping.js create mode 100644 lib/features/snapping/BpmnCreateMoveSnapping.js create mode 100644 lib/features/snapping/BpmnSnappingUtil.js create mode 100644 lib/features/snapping/index.js create mode 100644 lib/import/BpmnImporter.js create mode 100644 lib/import/BpmnTreeWalker.js create mode 100644 lib/import/Importer.js create mode 100644 lib/import/Util.js create mode 100644 lib/import/index.js create mode 100644 lib/util/CompatibilityUtil.js create mode 100644 lib/util/DiUtil.js create mode 100644 lib/util/DrilldownUtil.js create mode 100644 lib/util/LabelUtil.js create mode 100644 lib/util/ModelUtil.js create mode 100644 lib/util/PoweredByUtil.js create mode 100644 package.json diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..74d5bf4 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,813 @@ +# Changelog + +All notable changes to [bpmn-js](https://github.com/bpmn-io/bpmn-js) are documented here. We use [semantic versioning](http://semver.org/) for releases. + + +## Unreleased + +___Note:__ Yet to be released changes appear here._ + +## 9.4.0 + +* `FEAT`: allow clipboard to be serialized ([#1707](https://github.com/bpmn-io/bpmn-js/pull/1707)) +* `FEAT`: allow cloning of elements ([#1707](https://github.com/bpmn-io/bpmn-js/pull/1707)) +* `FEAT`: copy groups in a safe manner ([#1707](https://github.com/bpmn-io/bpmn-js/pull/1707)) +* `FIX`: make clipboard contents immutable ([#1707](https://github.com/bpmn-io/bpmn-js/pull/1707)) +* `FIX`: do not alter inputs passed to `ElementFactory#create` ([#1711](https://github.com/bpmn-io/bpmn-js/pull/1711)) +* `FIX`: prevent bogus meta-data to be attached on paste ([#1707](https://github.com/bpmn-io/bpmn-js/pull/1707)) +* `FIX`: only claim existing IDs ([#1707](https://github.com/bpmn-io/bpmn-js/pull/1707)) +* `FIX`: prevent double paste on label creation ([#1707](https://github.com/bpmn-io/bpmn-js/pull/1707)) +* `FIX`: move labels when collapsing sub-process ([#1695](https://github.com/bpmn-io/bpmn-js/issues/1695)) +* `FIX`: assign default size when expanding element ([#1687](https://github.com/bpmn-io/bpmn-js/issues/1687)) +* `FIX`: render sequence flows always on top ([#1716](https://github.com/bpmn-io/bpmn-js/issues/1716)) +* `DEPS`: update to `diagram-js@8.9.0` +* `DEPS`: update to `bpmn-moddle@7.1.3` + +## 9.3.2 + +* `FIX`: prevent unnecessary scrollbar ([#1692](https://github.com/bpmn-io/bpmn-js/issues/1692)) +* `FIX`: check for replacement using actual target ([#1699](https://github.com/bpmn-io/bpmn-js/pull/1699)) +* `DEPS`: update to `diagram-js@8.7.1` + +## 9.3.1 + +* `FIX`: properly size icons for distribute/align menu ([#1694](https://github.com/bpmn-io/bpmn-js/pull/1694)) + +## 9.3.0 + +* `FEAT`: add aligment and distribution menu ([#1680](https://github.com/bpmn-io/bpmn-js/issues/1680), [#1691](https://github.com/bpmn-io/bpmn-js/issues/1691)) +* `DEPS`: update to `diagram-js@8.7.0` + +## 9.2.2 + +* `FIX`: correctly toggle loop characteristics ([#1673](https://github.com/bpmn-io/bpmn-js/issues/1673)) + +## 9.2.1 + +* `FIX`: cancel direct editing before shape deletion ([#1677](https://github.com/bpmn-io/bpmn-js/issues/1677)) + +## 9.2.0 + +* `FEAT`: rework select and hover interaction on the diagram ([#1616](https://github.com/bpmn-io/bpmn-js/issues/1616), [#640](https://github.com/bpmn-io/diagram-js/pull/640), [#643](https://github.com/bpmn-io/diagram-js/pull/643)) +* `FEAT`: rework diagram interaction handles ([#640](https://github.com/bpmn-io/diagram-js/pull/640)) +* `FEAT`: clearly distinguish select and hover states ([#1616](https://github.com/bpmn-io/bpmn-js/issues/1616)) +* `FEAT`: allow text annotation on sequence flows ([#1652](https://github.com/bpmn-io/bpmn-js/pull/1652)) +* `FEAT`: add multi-element context pad ([#1525](https://github.com/bpmn-io/bpmn-js/pull/1525)) +* `FEAT`: change default color to off black ([#1656](https://github.com/bpmn-io/bpmn-js/pull/1656)) +* `FEAT`: select connection after connect ([#644](https://github.com/bpmn-io/diagram-js/pull/644)) +* `FIX`: copy elements with `string` extension properties ([#1518](https://github.com/bpmn-io/bpmn-js/issues/1518)) +* `FIX`: cancel direct editing before shape deletion ([#1664](https://github.com/bpmn-io/bpmn-js/issues/1664)) +* `FIX`: remove connection on source connection deletion ([#1663](https://github.com/bpmn-io/bpmn-js/issues/1663)) +* `FIX`: set correct label color when batch coloring elements ([#1653](https://github.com/bpmn-io/bpmn-js/issues/1653)) +* `FIX`: always reconnect labels and associations ([#1659](https://github.com/bpmn-io/bpmn-js/pull/1659)) +* `FIX`: correct connection drop highlighting +* `DEPS`: replace `inherits` with `inherits-browser` +* `DEPS`: bump to `diagram-js@8.5.0` + +## 9.1.0 + +* `FEAT`: allow to select participant and subprocess via click on body ([#1646](https://github.com/bpmn-io/bpmn-js/pull/1646)) +* `FIX`: comply with strict style-src CSP ([#1625](https://github.com/bpmn-io/bpmn-js/issues/1625)) +* `FIX`: complete direct editing when selection changes ([#1648](https://github.com/bpmn-io/bpmn-js/pull/1648)) +* `DEPS`: update to `diagram-js@8.3.0` +* `DEPS`: update to `min-dom@3.2.0` + +## 9.0.4 + +* `FIX`: remove `label` property on empty label ([#1637](https://github.com/bpmn-io/bpmn-js/issues/1637)) +* `FIX`: create drilldown overlays on `viewer.open` ([`574a67438`](https://github.com/bpmn-io/bpmn-js/commit/574a674381d6449b509396b6d17c4ca94674ea1c)) +* `FIX`: render data association inside collapsed sub-processes ([#1619](https://github.com/bpmn-io/bpmn-js/issues/1619)) +* `FIX`: preserve multi-instance properties when toggling between parallel and sequential ([#1581](https://github.com/bpmn-io/bpmn-js/issues/1581)) +* `FIX`: correct hanging sequence flow label after collapsing sub-process ([#1617](https://github.com/bpmn-io/bpmn-js/issues/1617)) +* `FIX`: correct start event not added to newly created sub-process ([#1631](https://github.com/bpmn-io/bpmn-js/issues/1631)) + +## 9.0.3 + +* `FIX`: submit direct editing result on drilldown ([#1609](https://github.com/bpmn-io/bpmn-js/issues/1609)) +* `DEPS`: bump to `diagram-js@8.2.0` ([2bac149](https://github.com/bpmn-io/bpmn-js/commit/2bac1495058601fec203c134b41efe5600e5fc97)) + +## 9.0.2 + +* `FIX`: support modeling of groups in collapsed subporcesses ([#1606](https://github.com/bpmn-io/bpmn-js/issues/1606)) +* `FIX`: override default padding of breadcrumb element ([#1608](https://github.com/bpmn-io/bpmn-js/pull/1608)) + +## 9.0.1 + +* `FIX`: use ES5 everywhere ([#1605](https://github.com/bpmn-io/bpmn-js/pull/1605)) +* `FIX`: support DIs without associated business object ([#1605](https://github.com/bpmn-io/bpmn-js/pull/1605)) +* `DEPS`: bump to `diagram-js@8.1.2` ([bdf9cf3](https://github.com/bpmn-io/bpmn-js/commit/bdf9cf3e752254a4c8172031d8a493955a9fca9c)) + +## 9.0.0 + +* `FEAT`: support drilldown and modeling of collapsed subprocesses ([#1443](https://github.com/bpmn-io/bpmn-js/issues/1443)) +* `FEAT`: update embedded label bounds when shape is moved ([#1586](https://github.com/bpmn-io/bpmn-js/pull/1586)) +* `FIX`: create di for embedded labels ([#1579](https://github.com/bpmn-io/bpmn-js/pull/1579)) +* `CHORE`: expose `BpmnRenderer` extension points ([#1585](https://github.com/bpmn-io/bpmn-js/pull/1585)) +* `DEPS`: bump to `diagram-js@8.1.1` + +### Breaking Changes + +* Reworked the link of elements to bpmn DIs. You must access the `di` directly from the diagram element instead of the `businessObject` [#1472](https://github.com/bpmn-io/bpmn-js/issues/1472). +* Reworked `viewer.open` behavior for single planes ([#1576](https://github.com/bpmn-io/bpmn-js/pull/1576)). +* Reworked import and `BpmnFactory` APIs [#1472](https://github.com/bpmn-io/bpmn-js/issues/1472). +* Added `bpmn-js.css`, which is required to display drilldown overlays correctly. + +## 8.10.0 + +* `CHORE`: provide `ModelUtil#isAny` utility ([#1604](https://github.com/bpmn-io/bpmn-js/pull/1604)) +* `CHORE`: provide `ModelUtil#getDi` utility ([#1604](https://github.com/bpmn-io/bpmn-js/pull/1604)) + +## 8.9.1 + +* `FIX`: re-use process for redo of first participant ([#1439](https://github.com/bpmn-io/bpmn-js/issues/1439)) +* `FIX`: ensure IDs are claimed when used ([#1555](https://github.com/bpmn-io/bpmn-js/issues/1555)) +* `FIX`: prevent morphing data stores outside participants ([#1508](https://github.com/bpmn-io/bpmn-js/issues/1508)) + +## 8.9.0 + +* `FEAT`: select newly created sub-process ([`6214772b`](https://github.com/bpmn-io/bpmn-js/commit/6214772b8519cb82f61c4867b16c112bc6344922)) +* `FEAT`: select newly created group for immediate resizing ([`56eb34cc`](https://github.com/bpmn-io/bpmn-js/commit/56eb34cc826ca0dc8ee788575a504d5fda301292)) +* `FEAT`: simplify color scheme +* `FIX`: set label color on `bpmndi:BPMNLabel#color` ([#1543](https://github.com/bpmn-io/bpmn-js/pull/1543)) +* `FIX`: don't create illegal `bpmndi:BPMNEdge#waypoints` property ([#1544](https://github.com/bpmn-io/bpmn-js/issues/1544)) +* `FIX`: correct direct editing on touch devices +* `DEPS`: update to `diagram-js@7.8.2` + +## 8.8.3 + +* `FIX`: correct resize handles hidden behind element ([#1520](https://github.com/bpmn-io/bpmn-js/issues/1520)) +* `FIX`: handle close to source or target drop on flow ([#1541](https://github.com/bpmn-io/bpmn-js/issues/1541)) +* `CHORE`: bump to `diagram-js@7.6.3` + +## 8.8.2 + +* `FIX`: properly re-use ID of a copied element if available ([#1503](https://github.com/bpmn-io/bpmn-js/pull/1509)) + +## 8.8.1 + +* `FIX`: re-use ID of a copied element if available ([#1503](https://github.com/bpmn-io/bpmn-js/pull/1503)) +* `CHORE`: unbuild circular dependency with `ResizeUtil` ([#1500](https://github.com/bpmn-io/bpmn-js/pull/1500)) + +## 8.8.0 + +* `FEAT`: give `keyboard` fine-grained control over which events to handle ([#1493](https://github.com/bpmn-io/bpmn-js/issues/1493)) +* `FIX`: correct keyboard shortcuts not working in direct editing mode ([#1493](https://github.com/bpmn-io/bpmn-js/issues/1493)) +* `DEPS`: update to `diagram-js@7.15` + +## 8.7.3 + +* `FIX`: convert file to `ES6` module ([#1478](https://github.com/bpmn-io/bpmn-js/pull/1478)) + +## 8.7.2 + +* `CHORE`: improve error recovery in ordering provider +* `DEPS`: update build dependencies + +## 8.7.1 + +* `FIX`: allow connecting `bpmn:MessageFlow` to `bpmn:CallActivity` ([#1467](https://github.com/bpmn-io/bpmn-js/issues/1467)) +* `DEPS`: update to `bpmn-moddle@7.1.2` + +## 8.7.0 + +* `FEAT`: support BPMN in Color ([#1453](https://github.com/bpmn-io/bpmn-js/pull/1453)) +* `DEPS`: update to `bpmn-moddle@7.1.1` + +## 8.6.2 + +* `DEPS`: update diagram-js-direct-editing to v1.6.3 + +## 8.6.1 + +* `FIX`: serialize `bpmn:DataStoreReference` correctly in case if first participant is an empty pool ([#1456](https://github.com/bpmn-io/bpmn-js/issues/1456)) + +## 8.6.0 + +* `FEAT`: support Promise in `inject` test helper ([#1450](https://github.com/bpmn-io/bpmn-js/pull/1450)) +* `DEPS`: update to `hosted-git@2.8.9` ([#1447](https://github.com/bpmn-io/bpmn-js/pull/1447)) + +## 8.5.0 + +* `FEAT`: reconnect message flows when participant is collapsed ([#1432](https://github.com/bpmn-io/bpmn-js/pull/1432)) +* `FEAT`: replace elements on create ([#1340](https://github.com/bpmn-io/bpmn-js/issues/1340)) +* `FEAT`: show message name on message flow ([#777](https://github.com/bpmn-io/bpmn-js/issues/777)) +* `FEAT`: ensure auto-placed elements are visible +* `FIX`: fix reversed connection preview ([#1431](https://github.com/bpmn-io/bpmn-js/issues/1431)) +* `FIX`: copy root element references on replace ([#1430](https://github.com/bpmn-io/bpmn-js/issues/1431)) +* `DEPS`: update to `diagram-js@7.3.0` + +## 8.4.0 + +* `FIX`: disallow inserting multiple elements on a sequence flow ([#1440](https://github.com/bpmn-io/bpmn-js/issues/1440)) + +## 8.3.1 + +* `FIX`: correctly serialize `xml` attributes on `Any` elements +* `DEPS`: update bump to `bpmn-moddle@7.0.5` + +## 8.3.0 + +* `FEAT`: enable connection tool for text annotations ([#1428](https://github.com/bpmn-io/bpmn-js/pull/1428)) + +## 8.2.2 + +* `FIX`: always emit `saveXML.done` +* `FIX`: correct path intersections not being detected in certain cases +* `CHORE`: bump to `diagram-js@7.2.3` + +## 8.2.1 + +* `FIX`: prevent bendpoint hover error ([#1387](https://github.com/bpmn-io/bpmn-js/issues/1387)) + +## 8.2.0 + +* `FIX`: correct label colors on connect / hover ([#1380](https://github.com/bpmn-io/bpmn-js/issues/1380)) +* `FIX`: correct new parent indicator when leaving lane ([#1413](https://github.com/bpmn-io/bpmn-js/issues/1413)) +* `CHORE`: update to `diagram-js@7.2.0` + +## 8.1.0 + +* `TEST`: simplify markup created by built-in test helpers + +## 8.0.1 + +* `FIX`: activate, not toggle global connect tool on palette click +* `FIX`: only allow cancel boundary events on transactions +* `CHORE`: add `npm start` script for demo purposes + +## 8.0.0 + +* `FEAT`: improve replace label for collapsed pools ([`8faee2bd`](https://github.com/bpmn-io/bpmn-js/commit/8faee2bde9a74b75b4b6bb9b003507652e75c9c5)) +* `FEAT`: allow participant multiplicity marker to be toggled ([#533](https://github.com/bpmn-io/bpmn-js/issues/533)) +* `FEAT`: support soft breaks / discretionary hyphens in labels ([#1383](https://github.com/bpmn-io/bpmn-js/issues/1383)) +* `FEAT`: improve tool activation via keyboard shortcuts or editor actions +* `FEAT`: allow components to react to auxiliary mouse button interactions +* `FEAT`: move canvas on auxiliary button mouse down +* `CHORE`: bump to `diagram-js@7` + +### Breaking Changes + +* Auxiliary mouse button events will now be passed as `element.*` mouse events to components. You must filter your event listeners to prevent reactions to these events ([`1063f7c1`](https://github.com/bpmn-io/diagram-js/commit/1063f7c18474096d3d7c9e400ce82a1bf762a157)). + +## 7.5.0 + +* `FEAT`: update translatable strings ([#1364](https://github.com/bpmn-io/bpmn-js/pull/1364)) +* `FEAT`: add collection marker to DataObjectReference ([#381](https://github.com/bpmn-io/bpmn-js/issues/381)) +* `FEAT`: provide generic command for updating moddle properties ([#1376](https://github.com/bpmn-io/bpmn-js/pull/1376)) +* `FEAT`: add switch between DataStoreReference and DataObjectReference in replace menu ([#1372](https://github.com/bpmn-io/bpmn-js/issues/1372)) +* `FIX`: align collection and parallel instance markers style ([#1371](https://github.com/bpmn-io/bpmn-js/issues/1371)) + +## 7.4.2 + +* `FIX`: correctly emit out `element.event` after drop-on-flow ([#1366](https://github.com/bpmn-io/bpmn-js/issues/1366)) + +## 7.4.1 + +* `FIX`: correct keyboard zoom in key on international keyboard shortcuts ([#1362](https://github.com/bpmn-io/bpmn-js/issues/1362)) + +## 7.4.0 + +* `CHORE`: bump to `diagram-js@6.8.0` +* `CHORE`: migrate to `travis-ci.com` + +## 7.3.1 + +* `CHORE`: bump to `diagram-js@6.7.1` + +## 7.3.0 + +* `FEAT`: disallow typed start events inside non-event based sub processes ([#831](https://github.com/bpmn-io/bpmn-js/issues/831)) +* `CHORE`: bump to `diagram-js@6.7.0` + +## 7.2.1 + +* `FIX`: disallow boundary events as message flow targets ([#1300](https://github.com/bpmn-io/bpmn-js/issues/1300)) + +## 7.2.0 + +_Republish of `v7.1.0`._ + +## 7.1.0 + +* `FEAT`: allow annotating groups ([#1327](https://github.com/bpmn-io/bpmn-js/issues/1327)) + +## 7.0.1 + +* `FIX`: roundtrip default `xml` namespace ([#1319](https://github.com/bpmn-io/bpmn-js/issues/1319)) +* `CHORE`: bump to `bpmn-moddle@7.0.3` + +## 7.0.0 + +* `FEAT`: make import and export APIs awaitable ([#812](https://github.com/bpmn-io/bpmn-js/issues/812)) +* `FEAT`: update watermark ([#1281](https://github.com/bpmn-io/bpmn-js/pull/1281)) +* `CHORE`: deprecated `import.parse.complete` context payload ([`157aec6e`](https://github.com/bpmn-io/bpmn-js/commit/157aec6e)) +* `CHORE`: clarify license terms ([`bc98a637`](https://github.com/bpmn-io/bpmn-js/commit/bc98a63712f6ac5c66d39f59bf93e296e59ad1e0)) +* `CHORE`: bump to `bpmn-moddle@7.0.1` + +### Breaking Changes + +* The toolkit now requires the ES6 `Promise` to be present. To support IE11 you must polyfill it. + +## 6.5.1 + +* `FIX`: correct namespaces being removed on diagram export ([#1310](https://github.com/bpmn-io/bpmn-js/issues/1310)) +* `CHORE`: bump to `bpmn-moddle@6.0.6` + +## 6.5.0 + +* `FEAT`: prefer straight layout for sub-process connections ([#1309](https://github.com/bpmn-io/bpmn-js/pull/1309)) +* `FEAT`: move common auto-place feature to diagram-js, add BPMN-specific auto-place feature ([#1284](https://github.com/bpmn-io/bpmn-js/pull/1284)) +* `CHORE`: make bpmn-font a development dependency ([`63045bdf`](https://github.com/bpmn-io/bpmn-js/commit/63045bdfa87b9f1989a2a7a509facbeb4616acda)) +* `CHORE`: bump to `diagram-js@6.6.1` + +## 6.4.2 + +* `CHORE`: bump to `bpmn-moddle@6.0.5` + +## 6.4.1 + +* `FIX`: parse `>` in attribute names and body tag +* `CHORE`: bump to `bpmn-moddle@6.0.4` + +## 6.4.0 + +* `FEAT`: serialize link events with an empty name ([#1296](https://github.com/bpmn-io/bpmn-js/issues/1296)) + +## 6.3.5 + +* `FIX`: correct accidental resizing of label target ([#1294](https://github.com/bpmn-io/bpmn-js/issues/1294)) + +## 6.3.4 + +* `FIX`: export BPMNDI in correct order ([#985](https://github.com/bpmn-io/bpmn-js/issues/985)) + +## 6.3.3 + +* `FIX`: resize empty text annotations +* `CHORE`: bump `min-dom` version +* `CHORE`: bump to `diagram-js@6.4.1` + +## 6.3.2 + +* `FIX`: correctly move flows when adding lane ([#1287](https://github.com/bpmn-io/bpmn-js/pull/1287)) +* `FIX`: restore semantic IDs for non flow nodes ([#1285](https://github.com/bpmn-io/bpmn-js/issues/1285)) + +## 6.3.1 + +* `FIX`: prevent editor crash in some strict execution environments ([#1283](https://github.com/bpmn-io/bpmn-js/pull/1283)) + +## 6.3.0 + +* `FEAT`: generate more generic IDs for new elements ([`035bb0c1`](https://github.com/bpmn-io/bpmn-js/commit/035bb0c1fd01adbaab8a340cb1075aa57736540d)) +* `FEAT`: copy referenced root elements (message, signal, ...) ([`dc5a566e`](https://github.com/bpmn-io/bpmn-js/commit/dc5a566e107bc156505a40de3331b3832afc4b8d)) +* `FEAT`: ensure minimum size when resizing elements with space tool ([`7ee304f4`](https://github.com/bpmn-io/bpmn-js/commit/7ee304f424d1c9db46633523165d25ca1fabba1b)) +* `FIX`: correct interaction events inside `bpmn:Group` elements ([#1278](https://github.com/bpmn-io/bpmn-js/issues/1278)) +* `FIX`: correct copy and paste of collapsed sub-processes ([#1270](https://github.com/bpmn-io/bpmn-js/issues/1270)) +* `FIX`: correct various space tool related issues ([#1019](https://github.com/bpmn-io/bpmn-js/issues/1019), [#878](https://github.com/bpmn-io/bpmn-js/issues/878)) +* `CHORE`: rework space tool +* `CHORE`: update to `diagram-js@6.4.0` + +## 6.2.1 + +* `FIX`: correct serialization of `DataAssociation#assignment` +* `CHORE`: update to `bpmn-moddle@6.0.2` + +## 6.2.0 + +* `FIX`: keep non-duplicate outgoing connection when dropping on flows ([#1263](https://github.com/bpmn-io/bpmn-js/issues/1263)) +* `FIX`: properly reconnect message flows when collapsing participant +* `CHORE`: update to `diagram-js@6.3.0` +* `CHORE`: update to `bpmn-moddle@6.0.1` + +## 6.1.2 + +* `FIX`: translate _Append ReceiveTask_ +* `FIX`: allow associations where data associations are allowed, too ([`4a675b37`](https://github.com/bpmn-io/bpmn-js/commit/4a675b378027532db413186ea292daeac087285b)) +* `FIX`: correct origin snapping on multi-element create ([`27fec8bd`](https://github.com/bpmn-io/bpmn-js/commit/27fec8bdf1c6236e7ca09b5721b74b1b45b45d39)) +* `CHORE`: update to `diagram-js@6.2.2` + +## 6.1.1 + +_Republish of `v6.1.0`._ + +## 6.1.0 + +* `FEAT`: copy signals, escalations and errors ([#1245](https://github.com/bpmn-io/bpmn-js/pull/1245)) +* `FEAT`: provide base viewer / modeler distributions ([`bb94b206`](https://github.com/bpmn-io/bpmn-js/commit/bb94b206a7c9ab3b80e283d6513600a9591c437d)) +* `FEAT`: add horizontal and vertical resize handles +* `FEAT`: improve connection cropping (bump to `path-intersection@2`) +* `FIX`: correctly mark elements as changed on `{shape|connection}.create` undo +* `FIX`: do not open replace menu after multi create ([#1255](https://github.com/bpmn-io/bpmn-js/pull/1255)) +* `CHORE`: update to `diagram-js@6.2.0` + +## 6.0.7 + +* `FIX`: disable waypoints-cropping after pasting connections ([`9f8a724e`](https://github.com/bpmn-io/bpmn-js/commit/9f8a724e9a3ff66bfce14e06ab38066189111a95)) + +## 6.0.6 + +* `FIX`: create nested lanes in the correct parent again ([#1256](https://github.com/bpmn-io/bpmn-js/issues/1256), [#1253](https://github.com/bpmn-io/bpmn-js/issues/1253), [#1254](https://github.com/bpmn-io/bpmn-js/issues/1254)) + +## 6.0.5 + +* `FIX`: only update `Lane#flownNodeRefs` once during paste ([`4455c3fc`](https://github.com/bpmn-io/bpmn-js/commit/4455c3fc35290e51220566fb6539a1efc4d3612f)) +* `FIX`: do not adjust labels on paste ([`b2b607f5`](https://github.com/bpmn-io/bpmn-js/commit/b2b607f5582d3409c789d831a0896aaa55949899)) +* `FIX`: do not snap connection waypoints on paste ([`d769e6dd`](https://github.com/bpmn-io/bpmn-js/commit/d769e6ddb0cb2dc8befb2e7b31682925089ba8f1)) + +## 6.0.4 + +* `FIX`: correctly fix hover on cleanup ([#1247](https://github.com/bpmn-io/bpmn-js/pull/1247)) + +## 6.0.3 + +* `FIX`: render colored BPMN groups ([#1246](https://github.com/bpmn-io/bpmn-js/pull/1246)) +* `CHORE`: bump to `diagram-js@6.0.2` + +## 6.0.2 + +* `CHORE`: bump `diagram-js-direct-editing` dependency + +## 6.0.1 + +* `CHORE`: bump to `diagram-js@6.0.1` + +## 6.0.0 + +* `FEAT`: rework (re-)connecting of shapes ([#427](https://github.com/bpmn-io/bpmn-js/pull/1230)) + +### Breaking Changes + +Connecting and re-connecting shapes got reworked via [#427](https://github.com/bpmn-io/bpmn-js/pull/1230): + +* The rules `connection.reconnectStart` and `connection.reconnectEnd` got replaced with `connection.reconnect` rule +* `BpmnLayouter#layoutConnection` waypoints can be specified via hint + +## 5.1.2 + +* `FIX`: account for label pasting in label behavior ([#1227](https://github.com/bpmn-io/bpmn-js/issues/1227)) + +## 5.1.1 + +* `FIX`: re-select only existing elements when dragging is finished ([#1225](https://github.com/bpmn-io/bpmn-js/issues/1225)) +* `FIX`: correctly hide nested children of a collapsed shape +* `CHORE`: bump to [`diagram-js@5.1.1`](https://github.com/bpmn-io/diagram-js/blob/develop/CHANGELOG.md#511) + +## 5.1.0 + +* `FEAT`: adjust label position post creation ([`41c6af18`](https://github.com/bpmn-io/bpmn-js/commit/41c6af183014626a0f84e0bda0f8e39018f9151e)) +* `FEAT`: copy and paste boundary events ([`2e27d743`](https://github.com/bpmn-io/bpmn-js/commit/2e27d7430642439e30806941d0df43018ca729eb)) +* `FIX`: ordering after moving boundary events between hosts ([#1207](https://github.com/bpmn-io/bpmn-js/issues/1207)) +* `FIX`: do not remove sequence flow condition on type change ([`b2900786`](https://github.com/bpmn-io/bpmn-js/commit/b290078600ae4e45e7c72bd37919732e3f8fcbea)) +* `FIX`: do not remove default sequence flow on type change ([`37bcd070`](https://github.com/bpmn-io/bpmn-js/commit/37bcd070e8406a43a7316893c6b68debeaae5e26)) +* `FIX`: do not duplicate flow node references ([`168a1493`](https://github.com/bpmn-io/bpmn-js/commit/168a1493b26c3059d2440a70f7aa5991745b51e5)) +* `FIX`: ignore labels that are being created in adaptive label positioning ([`44cceb5d`](https://github.com/bpmn-io/bpmn-js/commit/44cceb5da287a0ad01d9389f475284c88eda7f7b)) + +## 5.0.5 + +* `FIX`: snap connections to task mid ([`86c61b0`](https://github.com/bpmn-io/bpmn-js/commit/86c61b0c0d6dcf776adda94b6d72b621644c2abe)) +* `FIX`: snap connections to sub process mid ([`83e9f05`](https://github.com/bpmn-io/bpmn-js/commit/83e9f05efab6fbe57100e11d0443291a561bdfe4)) +* `FIX`: complete direct editing when auto place starts ([`dcf440b`](https://github.com/bpmn-io/bpmn-js/commit/dcf440b07684339bdb52ba97cd1c83f9eb234044)) +* `FIX`: do not clear diagram if no diagram to clear ([#1181](https://github.com/bpmn-io/bpmn-js/issues/1181)) +* `FIX`: copy boundary events attachments ([#1190](https://github.com/bpmn-io/bpmn-js/issues/1190)) +* `FIX`: do not copy generic properties ([`a74d83`](https://github.com/bpmn-io/bpmn-js/commit/a74d838dc78aceddf88e07231cf85a4cf9e0dd95)) + +## 5.0.4 + +* `FIX`: correct sequence flow layout after drop on flow ([#1178](https://github.com/bpmn-io/bpmn-js/issues/1178)) + +## 5.0.3 + +_Republish of `v5.0.2`._ + +## 5.0.2 + +* `FIX`: allow reconnecting to loops ([#1121](https://github.com/bpmn-io/bpmn-js/issues/1121)) +* `CHORE`: bump to `diagram-js@5.0.1` + +## 5.0.1 + +* `FIX`: import boundary event associations ([#1170](https://github.com/bpmn-io/bpmn-js/issues/1170)) + +## 5.0.0 + +* `FEAT`: add two-step copy and paste ([#1137](https://github.com/bpmn-io/bpmn-js/pull/1137)) +* `FEAT` add `elements.create` rule for creating multiple elements ([#1137](https://github.com/bpmn-io/bpmn-js/pull/1137)) +* `FEAT`: make containers draggable via their borders / labels only ([#1097](https://github.com/bpmn-io/bpmn-js/pull/1097), [#957](https://github.com/bpmn-io/bpmn-js/issues/957)) +* `FEAT`: allow copied elements to be filtered ([#888](https://github.com/bpmn-io/bpmn-js/issues/888)) +* `FIX`: prevent accidental dragging of participants and sub-processes ([#1097](https://github.com/bpmn-io/bpmn-js/pull/1097), [#957](https://github.com/bpmn-io/bpmn-js/issues/957)) +* `FIX`: keep labels during pool extraction ([#921](https://github.com/bpmn-io/bpmn-js/issues/921)) +* `FIX`: duplicate `bpmn:CategoryValue` when copying groups ([#1055](https://github.com/bpmn-io/bpmn-js/issues/1055)) +* `FIX`: translate group creation entry in palette ([#1146](https://github.com/bpmn-io/bpmn-js/issues/1146)) +* `CHORE`: use `element.copyProperty` event to copy category value when copying group ([`12bedca5`](https://github.com/bpmn-io/bpmn-js/pull/1137/commits/12bedca5ba2a05791591e53f554dc2310f6c1a6f)) +* `CHORE`: bump to `diagram-js@5` + +### Breaking Changes + +Copy and paste as well as create is completely reworked: + +* `CopyPaste`: remove `ModelCloneHelper` in favor of `ModdleCopy` service, remove `property.clone` event, add `moddleCopy.canCopyProperties`, `moddleCopy.canCopyProperty` and `moddleCopy.canSetCopiedProperty` event +* `BpmnRules`: removed `elements.paste` rule in favor of `elements.create` rule +* `BpmnRules`: removed `element.paste` rule +* `ElementFactory`: use `attrs.di` property instead of `attrs.colors` for fill and stroke when creating element through `ElementFactory#createBpmnElement` +* To prevent additional behavior on create after paste you should check for the `createElementsBehavior` hint, cf. [`bf180321`](https://github.com/bpmn-io/bpmn-js/commit/bf180321a3a40428c3f87b639b87cc3fc578066e#diff-2f0de25761fb7459e88071f83fd845c5R22) + +## 4.0.4 + +* `FIX`: creating `bpmn:Participant` on single `bpmn:Group` throwing error ([#1133](https://github.com/bpmn-io/bpmn-js/issues/1133)) +* `CHORE`: bump to `diagram-js@4.0.3` + +## 4.0.3 + +* `FIX`: prevent dropping on labels and `bpmn:Group` elements ([#1131](https://github.com/bpmn-io/bpmn-js/pull/1131)) + +## 4.0.2 + +* `FIX`: correct element positioning update ([#1129](https://github.com/bpmn-io/bpmn-js/issues/1129)) +* `CHORE`: bump to `diagram-js@4.0.2` + +## 4.0.1 + +* `FIX`: prevent adding lane from crashing IE ([#746](https://github.com/bpmn-io/bpmn-js/issues/746)) +* `FIX`: correct inverse space tool visuals ([#1105](https://github.com/bpmn-io/bpmn-js/issues/1105)) +* `CHORE`: update `diagram-js-direct-editing` to prevent install warning +* `CHORE`: update to `diagram-js@4.0.1` + +## 4.0.0 + +* `FEAT`: add top, right, bottom, left snapping with container elements ([#1108](https://github.com/bpmn-io/bpmn-js/pull/1108)) +* `FEAT`: add grid snapping ([#987](https://github.com/bpmn-io/bpmn-js/pull/987)) +* `FEAT`: allow modeling of groups ([#343](https://github.com/bpmn-io/bpmn-js/issues/343)) +* `FEAT`: improve modeling rules behind event-based gateways ([#1006](https://github.com/bpmn-io/bpmn-js/pull/1006)) +* `FEAT`: adjust default collapsed pool to standard height ([`5affe2570`](https://github.com/bpmn-io/bpmn-js/commit/5affe25705082937beace6b4a568f176a0527baf)) +* `FEAT`: add connection previews ([#743](https://github.com/bpmn-io/bpmn-js/issues/743)) +* `FEAT`: create expanded sub-process with start event included ([#1039](https://github.com/bpmn-io/bpmn-js/pull/1039)) +* `FEAT`: improve automatic label adjustment for boundary events ([#1064](https://github.com/bpmn-io/bpmn-js/pull/1064)) +* `FEAT`: improve creation of initial participant ([#1046](https://github.com/bpmn-io/bpmn-js/pull/1046)) +* `FEAT`: improve boundary to host loop layout ([#1070](https://github.com/bpmn-io/bpmn-js/pull/1070)) +* `FEAT`: make connection segment move the primary connection drag behavior +* `FEAT`: allow label and group movement everywhere ([#1080](https://github.com/bpmn-io/bpmn-js/pull/1080)) +* `FEAT`: improve message flow to participant connection in the presence of lanes ([#950](https://github.com/bpmn-io/bpmn-js/issues/950)) +* `FEAT`: allow detaching of boundary and attaching of intermediate events ([#1045](https://github.com/bpmn-io/bpmn-js/issues/1045)) +* `FEAT`: simplify requested palette and context pad translations ([#1027](https://github.com/bpmn-io/bpmn-js/pull/1027)) +* `FEAT`: simplify participant dragging in the presence of nested lanes ([`fdb299dc`](https://github.com/bpmn-io/bpmn-js/commit/fdb299dc888a7dcdb3f7674b6ed2a857864df457)) +* `FEAT`: correctly render all kinds of multiple events ([#1091](https://github.com/bpmn-io/bpmn-js/pull/1091)) +* `CHORE`: validate BPMN 2.0 XML ids as QNames ([`92c03679a`](https://github.com/bpmn-io/bpmn-js/commit/92c03679a4fd3c92a1c5ce3c97f7d366e2a5753a)) +* `FIX`: correctly handle flow reconnection + type replacement ([#896](https://github.com/bpmn-io/bpmn-js/issues/896), [#1008](https://github.com/bpmn-io/bpmn-js/issues/1008)) + +### Breaking Changes + +* `CHORE`: bump to [`diagram-js@4.0.0`](https://github.com/bpmn-io/diagram-js/blob/master/CHANGELOG.md#400) + +## 3.5.0 + +* `FEAT`: restore `Viewer#importDefinitions` and make it public API ([#1112](https://github.com/bpmn-io/bpmn-js/pull/1112)) + +## 3.4.3 + +* `FIX`: prevent HTML injection in search ([diagram-js#362](https://github.com/bpmn-io/diagram-js/pull/362)) + +## 2.5.4 + +* `FIX`: prevent HTML injection in search ([diagram-js#362](https://github.com/bpmn-io/diagram-js/pull/362)) +* `CHORE`: bump to `diagram-js@2.6.2` + +## 3.4.2 + +* `FIX`: do not evaluate pasted text as HTML ([#1073](https://github.com/bpmn-io/bpmn-js/issues/1073)) + +## 2.5.3 + +* `FIX`: do not evaluate pasted text as HTML ([#1073](https://github.com/bpmn-io/bpmn-js/issues/1073)) + +## 3.4.1 + +_Republish of `v3.4.0` without `.git` folder._ + +## 3.4.0 + +* `FIX`: properly render colored connection markers ([#981](https://github.com/bpmn-io/bpmn-js/issues/981)) +* `FEAT`: add ability to open different DI diagrams ([#87](https://github.com/bpmn-io/bpmn-js/issues/87)) +* `FIX`: correctly layout straight boundary to target connections ([#891](https://github.com/bpmn-io/bpmn-js/issues/891)) +* `FEAT`: resize participant to standard size on collapse ([#975](https://github.com/bpmn-io/bpmn-js/pull/975)) +* `FEAT`: consistently layout connection on reconnect start and end ([#971](https://github.com/bpmn-io/bpmn-js/pull/971)) +* `FEAT`: layout connection on element removal ([#989](https://github.com/bpmn-io/bpmn-js/issues/989)) +* `FIX`: properly crop sequence flow ends on undo/redo ([#940](https://github.com/bpmn-io/bpmn-js/issues/940)) +* `CHORE`: bump to [`diagram-js@3.3.0`](https://github.com/bpmn-io/diagram-js/blob/master/CHANGELOG.md#330) + +## 3.3.1 + +* `FIX`: ignore unchanged direct editing completion +* `CHORE`: update to `diagram-js-direct-editing@1.4.2` + +## 3.3.0 + +* `FEAT`: display `DataInput` / `DataOutput` labels ([`89719de3b`](https://github.com/bpmn-io/bpmn-js/commit/89719de3be50d9270227fd04216f7f19f0d018a2)) +* `FEAT`: support basic `DataInput` / `DataOutput` move ([#962](https://github.com/bpmn-io/bpmn-js/pull/962)) +* `FIX`: properly handle `DataInput` / `DataOutput` move ([#961](https://github.com/bpmn-io/bpmn-js/issues/961)) + +## 3.2.3 + +* `FIX`: update to `diagram-js-direct-editing@1.4.1` to trim trailing/leading whitespace in task names ([#763](https://github.com/bpmn-io/bpmn-js/issues/763)) + +## 3.2.2 + +* `FIX`: gracefully handle missing waypoints ([`45486f2`](https://github.com/bpmn-io/bpmn-js/commit/45486f2afe7f42fcac31be9ca477a7c94babe7d8)) + +## 3.2.1 + +* `FIX`: bump to `diagram-js@3.1.3` / `tiny-svg@2.2.1` to work around MS Edge bug ([`ed798a15`](https://github.com/bpmn-io/bpmn-js/commit/ed798a152539a613dbc9de9d61231ebbfb50987a)) + +## 3.2.0 + +* `FEAT`: set isHorizontal=true for new and updated participant/lane DIs ([#934](https://github.com/bpmn-io/bpmn-js/issues/934)) + +## 3.1.1 + +* `CHORE`: update to `diagram-js@3.1.1` + +## 3.1.0 + +* `CHORE`: update to `diagram-js@3.1` + +## 3.0.4 + +* `FIX`: render labels always on top ([#920](https://github.com/bpmn-io/bpmn-js/pull/920)) + +## 3.0.3 + +* `FIX`: do not join incoming/outgoing flows other than sequence flows on element deletion ([#917](https://github.com/bpmn-io/bpmn-js/issues/917)) + +## 3.0.2 + +* `FIX`: correct IE 11 delete keybinding ([#904](https://github.com/bpmn-io/bpmn-js/issues/904)) + +## 3.0.1 + +* `FIX`: restore copy-paste behavior + +## 3.0.0 + +* `FEAT`: improve context pad tooltip titles for `EventBasedGateway` ([`350a5ab`](https://github.com/bpmn-io/bpmn-js/commit/350a5ab75ed675991599faff9615e4bbe184d491)) +* `FEAT`: display group names ([#844](https://github.com/bpmn-io/bpmn-js/issues/844)) +* `FEAT`: add ability to move selection with keyboard arrows ([#376](https://github.com/bpmn-io/bpmn-js/issues/376)) +* `FEAT`: support `SHIFT` modifier to move elements / canvas with keyboard arrows at accelerated speed +* `FEAT`: require `Ctrl/Cmd` to be pressed as a modifier key to move the canvas via keyboard errors +* `FEAT`: auto-expand elements when children resize ([#786](https://github.com/bpmn-io/bpmn-js/issues/786)) +* `CHORE`: bind editor actions and keyboard shortcuts for explicitly added features only ([#887](https://github.com/bpmn-io/bpmn-js/pull/887)) +* `CHORE`: update to [`diagram-js@3.0.0`](https://github.com/bpmn-io/diagram-js/blob/master/CHANGELOG.md#300) +* `FIX`: disallow attaching of `BoundaryEvent` to a `ReceiveTask` following an `EventBasedGateway` ([#874](https://github.com/bpmn-io/bpmn-js/issues/874)) +* `FIX`: fix date in license ([#882](https://github.com/bpmn-io/bpmn-js/pull/882)) + +### Breaking Changes + +* `BpmnGlobalConnect` provider got removed. Use `connection.start` rule to customize whether connection should allowed to be started ([#565](https://github.com/bpmn-io/bpmn-js/issues/565), [#870](https://github.com/bpmn-io/bpmn-js/issues/870)) +* `EditorActions` / `Keyboard` do not pull in features implicitly anymore. If you roll your own editor, include features you would like to ship with manually to provide the respective actions / keyboard bindings ([`645265ad`](https://github.com/bpmn-io/bpmn-js/commit/645265ad7e4a47e80657c671068a027752d7504f)) +* Moving the canvas with keyboard arrows now requires the `Ctrl/Cmd` modifiers to be pressed. + +## 2.5.2 + +* `FIX`: correct horizontal embedded label padding + +## 2.5.1 + +* `FIX`: prevent error to be thrown on lane move ([#855](https://github.com/bpmn-io/bpmn-js/issues/855)) + +## 2.5.0 + +* `FEAT`: snap message flows to `bpmn:Event` center during connect ([#850](https://github.com/bpmn-io/bpmn-js/issues/850)) +* `CHORE`: bump to `diagram-js@2.6.0` +* `FIX`: allow label movement over message flow ([#849](https://github.com/bpmn-io/bpmn-js/issues/849)) + +## 2.4.1 + +* `FIX`: make viewer IE 9 compatible +* `FIX`: prevent duplicate connections after drop on flow ([#774](https://github.com/bpmn-io/bpmn-js/issues/774)) +* `FIX`: fix rules not preventing redundant loop ([#836](https://github.com/bpmn-io/bpmn-js/issues/836)) + +## 2.4.0 + +* `FEAT`: improve layouting of boundary event to host loops ([#467](https://github.com/bpmn-io/bpmn-js/issues/467)) +* `FEAT`: allow circular activity to activity loops ([#824](https://github.com/bpmn-io/bpmn-js/issues/824)) +* `FEAT`: create label on appropriate free position ([#825](https://github.com/bpmn-io/bpmn-js/issues/825)) +* `CHORE`: bump to `diagram-js@2.5.0` +* `FIX`: repair label position not being adapted on host move + +## 2.3.1 + +* `FIX`: revert to `Arial` as the default rendering font ([#819](https://github.com/bpmn-io/bpmn-js/issues/819)) +* `FIX`: keep event definitions when switching from interrupting to non-interrupting boundary event ([#799](https://github.com/bpmn-io/bpmn-js/issues/799)) + +## 2.3.0 + +* `CHORE`: update to `diagram-js@2.4.0` + +## 2.2.1 + +* `FIX`: correct updating of multiple data stores ([`300e7010`](https://github.com/bpmn-io/bpmn-js/commit/300e7010c4e1862394d147988dc4c4bcc09b07bc)) + +## 2.2.0 + +* `FEAT`: emit export events ([#813](https://github.com/bpmn-io/bpmn-js/issues/813)) +* `FEAT`: unset businessObject name if empty ([`6c081d85`](https://github.com/bpmn-io/bpmn-js/commit/6c081d854fa8a4e87eb7cdd1744be37c78652667)) +* `FEAT`: resize text annotation on text change ([`100f3fb2`](https://github.com/bpmn-io/bpmn-js/commit/100f3fb2ee6373cd4b7ad0b76e520a1afb70887e)) +* `FIX`: apply data store behavior in collaboration only ([`5cc28d5d`](https://github.com/bpmn-io/bpmn-js/commit/5cc28d5d5571287a798b189aed75095f1fd0189e)) +* `FIX`: create/update labels when updating element name via `Modeling#updateProperties` ([`4a0f6da8`](https://github.com/bpmn-io/bpmn-js/commit/4a0f6da814c45268e8a324e73a53479bd2435bbe)) + +## 2.1.0 + +* `FEAT`: support specifying `lineHeight` for text rendering ([#256](https://github.com/bpmn-io/diagram-js/pull/256)) +* `FEAT`: `bpmn:LaneSet` elements get an ID assigned on creation +* `FEAT`: external labels can be deleted, clearing the elements name ([#791](https://github.com/bpmn-io/bpmn-js/pull/791)) +* `FEAT`: add ability to override default element colors ([#713](https://github.com/bpmn-io/bpmn-js/issues/713)) +* `FEAT`: add ability to override font family and size of rendered labels ([`4bb270f1`](https://github.com/bpmn-io/bpmn-js/commit/4bb270f19279db40f9cc3c179e09ee3a9a114e7c)) + +## 2.0.1 + +_Republish of `v2.0.0` due to registry error._ + +## 2.0.0 + +* `FEAT`: allow data store to be modeled between participants ([#483](https://github.com/bpmn-io/bpmn-js/issues/483)) +* `CHORE`: update to [`diagram-js@2.0.0`](https://github.com/bpmn-io/diagram-js/blob/master/CHANGELOG.md#200) +* `FIX`: correctly handle missing `bpmndi:Label` bounds during model updating ([#794](https://github.com/bpmn-io/bpmn-js/issues/794)) + +### Breaking Changes + +* The `PopupMenu` API got rewritten, cf. [`b1852e1d`](https://github.com/bpmn-io/diagram-js/pull/254/commits/b1852e1d71f67bd36ae1eb02748d2d0cbf124625) + +## 1.3.3 + +* `CHORE`: update to [`bpmn-moddle@5.1.5`](https://github.com/bpmn-io/bpmn-moddle/blob/master/CHANGELOG.md#515) + +## 1.3.2 + +* `FIX`: correctly serialize extension attributes on `bpmn:Expression` + +## 1.3.1 + +* `FIX`: correctly auto-place from boundary events attached to host edges ([#788](https://github.com/bpmn-io/bpmn-js/issues/788)) + +## 1.3.0 + +* `FEAT`: expose additional `BpmnTreeWalker` APIs for advanced import use-cases +* `CHORE`: bump diagram-js and object-refs version + +## 1.2.1 + +* `FIX`: correct side-effects config to not include `*.css` files + +## 1.2.0 + +* `FEAT`: add initial snapping when creating associations +* `CHORE`: update to `diagram-js@1.3.0` +* `FIX`: allow message flows between collapsed pools +* `FIX`: complete direct editing on popup menu use +* `FIX`: focus label editing box on element creation + +## 1.1.1 + +* `FIX`: escape `data-element-id` in CSS selectors + +## 1.1.0 + +* `FEAT`: show gateway icon on context pad without marker ([`15dfab6b`](https://github.com/bpmn-io/bpmn-js/commit/15dfab6b5b12dd184acf070f2ab3ad205d1b245c)) + +## 1.0.4 + +* `FIX`: properly wire `$parent` on copy + paste +* `FIX`: improve boundary event rendering to correct SVG to image conversion + +## 1.0.3 + +* `FIX`: re-expose `TestHelper#bootstrapBpmnJS` util + +## 1.0.2 + +* `FIX`: correct library default export + +## 1.0.1 + +_Republished 1.0.0 with CHANGELOG entries._ + +## 1.0.0 + +* `CHORE`: convert code base to ES modules +* `CHORE`: update utility toolbelt + +### Breaking Changes + +* You must now configure a module transpiler such as Babel or Webpack to handle ES module imports and exports. + +## 0.31.0 + +* `FEAT`: encode entities in body properties during XML export +* `CHORE`: bump to [`bpmn-moddle@4.0.0`](https://github.com/bpmn-io/bpmn-moddle/releases/tag/v4.0.0) +* `CHORE`: bump utility version + +## 0.30.0 + +* `CHORE`: bump to [`diagram-js@0.31.0`](https://github.com/bpmn-io/diagram-js/releases/tag/v0.31.0) + +## ... + +Check `git log` for earlier history. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1ee6a37 --- /dev/null +++ b/README.md @@ -0,0 +1,110 @@ +# bpmn-js - BPMN 2.0 for the web + +[![Build Status](https://github.com/bpmn-io/bpmn-js/workflows/CI/badge.svg)](https://github.com/bpmn-io/bpmn-js/actions?query=workflow%3ACI) + +View and edit BPMN 2.0 diagrams in the browser. + +[![bpmn-js screencast](./resources/screencast.gif "bpmn-js in action")](http://demo.bpmn.io/s/start) + + +## Installation + +Use the library [pre-packaged](https://github.com/bpmn-io/bpmn-js-examples/tree/master/pre-packaged) +or include it [via npm](https://github.com/bpmn-io/bpmn-js-examples/tree/master/bundling) +into your node-style web-application. + +## Usage + +To get started, create a [bpmn-js](https://github.com/bpmn-io/bpmn-js) instance +and render [BPMN 2.0 diagrams](https://www.omg.org/spec/BPMN/2.0.2/) in the browser: + +```javascript +const xml = '...'; // my BPMN 2.0 xml +const viewer = new BpmnJS({ + container: 'body' +}); + +try { + const { warnings } = await viewer.importXML(xml); + + console.log('rendered'); +} catch (err) { + console.log('error rendering', err); +} +``` + +Checkout our [examples](https://github.com/bpmn-io/bpmn-js-examples) for many +more supported usage scenarios. + + +### Dynamic Attach/Detach + +You may attach or detach the viewer dynamically to any element on the page, too: + +```javascript +const viewer = new BpmnJS(); + +// attach it to some element +viewer.attachTo('#container'); + +// detach the panel +viewer.detach(); +``` + + +## Resources + +* [Demo](http://demo.bpmn.io) +* [Issues](https://github.com/bpmn-io/bpmn-js/issues) +* [Examples](https://github.com/bpmn-io/bpmn-js-examples) +* [Forum](https://forum.bpmn.io) +* [Changelog](./CHANGELOG.md) + + +## Build and Run + +Prepare the project by installing all dependencies: + +```sh +npm install +``` + +Then, depending on your use-case you may run any of the following commands: + +```sh +# build the library and run all tests +npm run all + +# spin up a single local modeler instance +npm start + +# run the full development setup +npm run dev +``` + +You may need to perform [additional project setup](./docs/project/SETUP.md) when +building the latest development snapshot. + + +## Related + +bpmn-js builds on top of a few powerful tools: + +* [bpmn-moddle](https://github.com/bpmn-io/bpmn-moddle): Read / write support for BPMN 2.0 XML in the browsers +* [diagram-js](https://github.com/bpmn-io/diagram-js): Diagram rendering and editing toolkit + + +## Contributing + +Please checkout our [contributing guidelines](./.github/CONTRIBUTING.md) if you plan to +file an issue or pull request. + + +## Code of Conduct + +By participating to this project, please uphold to our [Code of Conduct](https://github.com/bpmn-io/.github/blob/master/.github/CODE_OF_CONDUCT.md). + + +## License + +Use under the terms of the [bpmn.io license](http://bpmn.io/license). diff --git a/dist/assets/bpmn-font/css/bpmn-codes.css b/dist/assets/bpmn-font/css/bpmn-codes.css new file mode 100644 index 0000000..a285dc9 --- /dev/null +++ b/dist/assets/bpmn-font/css/bpmn-codes.css @@ -0,0 +1,108 @@ + +.bpmn-icon-screw-wrench:before { content: '\e800'; } /* '' */ +.bpmn-icon-trash:before { content: '\e801'; } /* '' */ +.bpmn-icon-conditional-flow:before { content: '\e802'; } /* '' */ +.bpmn-icon-default-flow:before { content: '\e803'; } /* '' */ +.bpmn-icon-gateway-parallel:before { content: '\e804'; } /* '' */ +.bpmn-icon-intermediate-event-catch-cancel:before { content: '\e805'; } /* '' */ +.bpmn-icon-intermediate-event-catch-non-interrupting-message:before { content: '\e806'; } /* '' */ +.bpmn-icon-start-event-compensation:before { content: '\e807'; } /* '' */ +.bpmn-icon-start-event-non-interrupting-parallel-multiple:before { content: '\e808'; } /* '' */ +.bpmn-icon-loop-marker:before { content: '\e809'; } /* '' */ +.bpmn-icon-parallel-mi-marker:before { content: '\e80a'; } /* '' */ +.bpmn-icon-start-event-non-interrupting-signal:before { content: '\e80b'; } /* '' */ +.bpmn-icon-intermediate-event-catch-non-interrupting-timer:before { content: '\e80c'; } /* '' */ +.bpmn-icon-intermediate-event-catch-parallel-multiple:before { content: '\e80d'; } /* '' */ +.bpmn-icon-intermediate-event-catch-compensation:before { content: '\e80e'; } /* '' */ +.bpmn-icon-gateway-xor:before { content: '\e80f'; } /* '' */ +.bpmn-icon-connection:before { content: '\e810'; } /* '' */ +.bpmn-icon-end-event-cancel:before { content: '\e811'; } /* '' */ +.bpmn-icon-intermediate-event-catch-condition:before { content: '\e812'; } /* '' */ +.bpmn-icon-intermediate-event-catch-non-interrupting-parallel-multiple:before { content: '\e813'; } /* '' */ +.bpmn-icon-start-event-condition:before { content: '\e814'; } /* '' */ +.bpmn-icon-start-event-non-interrupting-timer:before { content: '\e815'; } /* '' */ +.bpmn-icon-sequential-mi-marker:before { content: '\e816'; } /* '' */ +.bpmn-icon-user-task:before { content: '\e817'; } /* '' */ +.bpmn-icon-business-rule:before { content: '\e818'; } /* '' */ +.bpmn-icon-sub-process-marker:before { content: '\e819'; } /* '' */ +.bpmn-icon-start-event-parallel-multiple:before { content: '\e81a'; } /* '' */ +.bpmn-icon-start-event-error:before { content: '\e81b'; } /* '' */ +.bpmn-icon-intermediate-event-catch-signal:before { content: '\e81c'; } /* '' */ +.bpmn-icon-intermediate-event-catch-error:before { content: '\e81d'; } /* '' */ +.bpmn-icon-end-event-compensation:before { content: '\e81e'; } /* '' */ +.bpmn-icon-subprocess-collapsed:before { content: '\e81f'; } /* '' */ +.bpmn-icon-subprocess-expanded:before { content: '\e820'; } /* '' */ +.bpmn-icon-task:before { content: '\e821'; } /* '' */ +.bpmn-icon-end-event-error:before { content: '\e822'; } /* '' */ +.bpmn-icon-intermediate-event-catch-escalation:before { content: '\e823'; } /* '' */ +.bpmn-icon-intermediate-event-catch-timer:before { content: '\e824'; } /* '' */ +.bpmn-icon-start-event-escalation:before { content: '\e825'; } /* '' */ +.bpmn-icon-start-event-signal:before { content: '\e826'; } /* '' */ +.bpmn-icon-business-rule-task:before { content: '\e827'; } /* '' */ +.bpmn-icon-manual:before { content: '\e828'; } /* '' */ +.bpmn-icon-receive:before { content: '\e829'; } /* '' */ +.bpmn-icon-call-activity:before { content: '\e82a'; } /* '' */ +.bpmn-icon-start-event-timer:before { content: '\e82b'; } /* '' */ +.bpmn-icon-start-event-message:before { content: '\e82c'; } /* '' */ +.bpmn-icon-intermediate-event-none:before { content: '\e82d'; } /* '' */ +.bpmn-icon-intermediate-event-catch-link:before { content: '\e82e'; } /* '' */ +.bpmn-icon-end-event-escalation:before { content: '\e82f'; } /* '' */ +.bpmn-icon-text-annotation:before { content: '\e830'; } /* '' */ +.bpmn-icon-bpmn-io:before { content: '\e831'; } /* '' */ +.bpmn-icon-gateway-complex:before { content: '\e832'; } /* '' */ +.bpmn-icon-gateway-eventbased:before { content: '\e833'; } /* '' */ +.bpmn-icon-gateway-none:before { content: '\e834'; } /* '' */ +.bpmn-icon-gateway-or:before { content: '\e835'; } /* '' */ +.bpmn-icon-end-event-terminate:before { content: '\e836'; } /* '' */ +.bpmn-icon-end-event-signal:before { content: '\e837'; } /* '' */ +.bpmn-icon-end-event-none:before { content: '\e838'; } /* '' */ +.bpmn-icon-end-event-multiple:before { content: '\e839'; } /* '' */ +.bpmn-icon-end-event-message:before { content: '\e83a'; } /* '' */ +.bpmn-icon-end-event-link:before { content: '\e83b'; } /* '' */ +.bpmn-icon-intermediate-event-catch-message:before { content: '\e83c'; } /* '' */ +.bpmn-icon-intermediate-event-throw-compensation:before { content: '\e83d'; } /* '' */ +.bpmn-icon-start-event-multiple:before { content: '\e83e'; } /* '' */ +.bpmn-icon-script:before { content: '\e83f'; } /* '' */ +.bpmn-icon-manual-task:before { content: '\e840'; } /* '' */ +.bpmn-icon-send:before { content: '\e841'; } /* '' */ +.bpmn-icon-service:before { content: '\e842'; } /* '' */ +.bpmn-icon-receive-task:before { content: '\e843'; } /* '' */ +.bpmn-icon-user:before { content: '\e844'; } /* '' */ +.bpmn-icon-start-event-none:before { content: '\e845'; } /* '' */ +.bpmn-icon-intermediate-event-throw-escalation:before { content: '\e846'; } /* '' */ +.bpmn-icon-intermediate-event-catch-multiple:before { content: '\e847'; } /* '' */ +.bpmn-icon-intermediate-event-catch-non-interrupting-escalation:before { content: '\e848'; } /* '' */ +.bpmn-icon-intermediate-event-throw-link:before { content: '\e849'; } /* '' */ +.bpmn-icon-start-event-non-interrupting-condition:before { content: '\e84a'; } /* '' */ +.bpmn-icon-data-object:before { content: '\e84b'; } /* '' */ +.bpmn-icon-script-task:before { content: '\e84c'; } /* '' */ +.bpmn-icon-send-task:before { content: '\e84d'; } /* '' */ +.bpmn-icon-data-store:before { content: '\e84e'; } /* '' */ +.bpmn-icon-start-event-non-interrupting-escalation:before { content: '\e84f'; } /* '' */ +.bpmn-icon-intermediate-event-throw-message:before { content: '\e850'; } /* '' */ +.bpmn-icon-intermediate-event-catch-non-interrupting-multiple:before { content: '\e851'; } /* '' */ +.bpmn-icon-intermediate-event-catch-non-interrupting-signal:before { content: '\e852'; } /* '' */ +.bpmn-icon-intermediate-event-throw-multiple:before { content: '\e853'; } /* '' */ +.bpmn-icon-start-event-non-interrupting-message:before { content: '\e854'; } /* '' */ +.bpmn-icon-ad-hoc-marker:before { content: '\e855'; } /* '' */ +.bpmn-icon-service-task:before { content: '\e856'; } /* '' */ +.bpmn-icon-task-none:before { content: '\e857'; } /* '' */ +.bpmn-icon-compensation-marker:before { content: '\e858'; } /* '' */ +.bpmn-icon-start-event-non-interrupting-multiple:before { content: '\e859'; } /* '' */ +.bpmn-icon-intermediate-event-throw-signal:before { content: '\e85a'; } /* '' */ +.bpmn-icon-intermediate-event-catch-non-interrupting-condition:before { content: '\e85b'; } /* '' */ +.bpmn-icon-participant:before { content: '\e85c'; } /* '' */ +.bpmn-icon-event-subprocess-expanded:before { content: '\e85d'; } /* '' */ +.bpmn-icon-lane-insert-below:before { content: '\e85e'; } /* '' */ +.bpmn-icon-space-tool:before { content: '\e85f'; } /* '' */ +.bpmn-icon-connection-multi:before { content: '\e860'; } /* '' */ +.bpmn-icon-lane:before { content: '\e861'; } /* '' */ +.bpmn-icon-lasso-tool:before { content: '\e862'; } /* '' */ +.bpmn-icon-lane-insert-above:before { content: '\e863'; } /* '' */ +.bpmn-icon-lane-divide-three:before { content: '\e864'; } /* '' */ +.bpmn-icon-lane-divide-two:before { content: '\e865'; } /* '' */ +.bpmn-icon-data-input:before { content: '\e866'; } /* '' */ +.bpmn-icon-data-output:before { content: '\e867'; } /* '' */ +.bpmn-icon-hand-tool:before { content: '\e868'; } /* '' */ +.bpmn-icon-group:before { content: '\e869'; } /* '' */ +.bpmn-icon-transaction:before { content: '\e8c4'; } /* '' */ \ No newline at end of file diff --git a/dist/assets/bpmn-font/css/bpmn-embedded.css b/dist/assets/bpmn-font/css/bpmn-embedded.css new file mode 100644 index 0000000..df390be --- /dev/null +++ b/dist/assets/bpmn-font/css/bpmn-embedded.css @@ -0,0 +1,161 @@ +@font-face { + font-family: 'bpmn'; + src: url('../font/bpmn.eot?68866489'); + src: url('../font/bpmn.eot?68866489#iefix') format('embedded-opentype'), + url('../font/bpmn.svg?68866489#bpmn') format('svg'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'bpmn'; + src: url('data:application/octet-stream;base64,d09GRgABAAAAAD4sAAsAAAAAudgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADsAAABUIIslek9TLzIAAAFEAAAAQwAAAFY+IEq7Y21hcAAAAYgAAAI5AAAHRN+uG6xnbHlmAAADxAAANBYAAKG8CQmG52hlYWQAADfcAAAAMAAAADYY7KABaGhlYQAAOAwAAAAbAAAAJAc8A79obXR4AAA4KAAAABEAAAGwpeAAAGxvY2EAADg8AAAA2gAAANpyjUcSbWF4cAAAORgAAAAfAAAAIAGWBHduYW1lAAA5OAAAAVIAAAI9ejh1lXBvc3QAADqMAAADnwAACihWzTZJeJxjYGRgYOBiMGCwY2BycfMJYeDLSSzJY5BiYGGAAJA8MpsxJzM9kYEDxgPKsYBpDiBmg4gCACY7BUgAeJxjYGR+wTiBgZWBgamKaQ8DA0MPhGZ8wGDIyAQUZWBlZsAKAtJcUxgOvGB4cYQ56H8WQxRzEMM0oDAjSA4AJHkNWQB4nO3VBXLVBxhF8ZMQAsXdXUuwIMXdXYoUb3HJNrsIlnBXAZx/cpdBZn55yWMek0zudx4wG5ilfRqD0UlG/IqRCZ8dmX5+FvOmnx/jh9+P+zjCaL7m/58/Icw8Di/x375PP476mjH/53HmMJc/fP18FrCQRSxmCUtZxnJWsJJVrGYNa1nHejawkU1sZgtb2cZ2drCTXezmT/YwwV5/jv0c4CCTHOIwRzjKXxzjOCc4ySlOc4aznOM8F7jIJS5zhatc4zo3uMktbnOHu9zjPg94yN884jFPeMo/POM5L3jJK17zhn/5j7e84z0f+MgnPvOFr3xjyl9rnN8fC4ZPs1f0u6nh7z9j2ErKvz+pYVupYV+pYXcpd0LKxZByO6RcEalhjymXRWr46VKujZS7I+UCSblFUq6SlPsk5VJJuVlSrpeUOyblokm5bVKunJR7J+XySXkDpLwGUt4FKS+ElLdCyqsh5f2Q8pJIeVOkvC5S3hkpL46Ut0fKKyTlPZLyMkkNDUl5raS8W1JeMClvmZRXTcr7JuWlk/LmSXn9pOwAKYtAyjaQshKk7AUpy0HKhpCyJqTsCikLQ8rWkLI6pOwPKUtEyiaRsk6k7BQpi0XKdpGyYqTsGSnLRsrGkbJ2pOweKQtIyhaSsoqk7CMpS0nKZpKynqTsKCmLSsq2krKypOwtKctLygaTssak7DIpC03KVpOy2qTsNylLTsqmk7LupOw8KYtPyvaT8l2AlO8HvuvNYOoXV5IpPQAAAHic7X0JnCRFlXdG5H1UZl2ZWVXdXdVV2V1VfdZd1cdMd8/d0zM9933BHAww4CC3HIOsAsoheIEuDAyHoKzgqrCuCyyysOuyLup6rLrKt7K66uK1qOO338c3XX4vIqv6mp5hpmGGn/6msvKOjOPFixf/9+JlJIMZ5g9H2Kfwc4yfaWAYlNBxMIoL+XJFTA2ibiQjIYpsOMCH20b7mpv7RtsW7g/eednAvU2rqv9eLYhmh41eaGjBz8GtNRBi9bz06LXtzU7159W/D5ZTCJXtAYZheEjnObYf/wxS6WRyzBAzCqkJCVFHTQg2ViGeLyczqFRMppJOIuXwCcEORBG5XAmUk06cb0NsPJBwdESuD6AU241aE4LIQgZpEPS42ioo4mOKKCr4nbIoyp/iZcH0a0uqP1IMlEceBf2T4sFDSFerP6qOIRax/dVdyxDWDNyJ/ZYHth6kK14Ee9sHZwb+GcKsGZZ7sOHxGLiiRIIsRi8Z6ti/yh5Nwe2qN1cV0WeRX6s+pwLtVDSokDiqf6cGsa6iIc3PEBp/j70D/4oJM02k1KIgWjGyM/AgLlfKFTgrV5CBKuwdq1Y/sdVqjyxtae77Rtv/sLGeuDMaTEezW6rphvsj+Fdt6S0dwY7ISDLRn/iftPF8r9M6GmiLnv149dL7Gx5gEKT1Q5pWw+SUHLMgWnalUHLqqY2ntPWLj1x7nWod/Msb11xYoSmNJ7L9qY7r1OC9HWv3V2gSUI8sxH83+0U2wXiZIDOPcIyIhKCNrHwFlYsplBRl3JowWNOBNcYWyDrIlsiaYR1Y0WFDre5UFHRIjZENnKgGuql6dfVqdA3GHIue52CDWI57nmws7qhLbILEEJsSzQCN4KafjAcy+eNExQhQjjvZZ9kWoFOOWcqczVzF3AKlSYqEZoJlW3YUVQZwpZzqRqlkykmKZZtctQTg1mSqG+5UBpBdC+/egisYwnYjN6xo0XvA0KkkqeUynNkCqQe7XIGLcCmZEgwEbE+uibaYEsQUvYV+d+E5O+fNV7XKnLlrV/71hk03rBSEaOOVK1dtZE3v7u2b++ZI8tyBoUeeHF21p9m0e3sePGvr+tX5EuJaWuYtXDN63+C8RVy0eU722vmrRpZ0dCI0PDC0bMlthXLQDKZSc0camvre2VNu79x/cV80mQ+35kLtneFsazif7O3ZX7zo4tLFLSPFkVa4xSZwubRl+4XbvrB0JJ1WG5uu/MTmrWvX3pRu43GOzWXXbti96bGhBf3ejs47nli/ef7CFR1sNrNyzda1q1aUe7ze1mz+tgeXLs93ZLO4s33xyKplt3ZmRDlXKC2a/8H+uYmOi6J9F5d7Otv3v7OviWQlG+5sD+UgK2xP70XF/ZCVVshKC9xiGB/U20OU/1ioQ5VZwTzCHEGD6KPoN3gOvg4/z/LsNvZR9vfcINSmL+7jC2LFjJdsX84RYTFhNUQzxhUq44s9vgzypQzvpJwSrCnbMfhYeJAjfJsqweJkEPxhnyrVjuCYnJJjuOIG4R0Dm5AIMskeFgPVTugBOXavYTPGFwbDpQwXzzluc+Gm5Scm0CxhJyOTHJGcQcyYxkHiguhpqSBC1jFpKnCZRNsEDS7D8lACi0QLpYBMS2TDObUCkDJBxseLhKGgkHnedGi2ydY2C7CSJSaTOCBManypjC8ZjifZF236IC2zRNu9ADcHRdgUaLiUmwJbixNiJUnEsCskIHp1PHIaJU9iMoBMg7g0iEpAm9IgLgwqpQrchqJhEgkhEy7EUC2n5AiukXgRRE1iJyRshOgkRxxfDI5Qv84BgyrZIJpIiWSERE8CQBw0AYgKoiWUHK8UK+4YcAVOJJJ3GlGJxkGLwqEUurv6d/95ycHqiwdVQeU1nheSgiJxGCQSFjiZ6+BlHn4KH4WVHHAhGYQWxyEEWzHJCzw8KBzmOR4uYixhTZREuAULiBIDy5j1wYksg0jjoKPC7upHMieAvIP+TSIbWCQOYuUkxCljv+E5iYcTjtNglVhE4+NkZJDHWR5D4iw5kjiZF3neK2ABG5CeG06EqEQIiVkSnd+j8KIAJ5LEQSJJiA8iFWEXYSVRZKE8NG+QYcSSYw7KneTIZd7zOCcJcBmyYEIuWMAaGpJllhYDUkdQZHrAczIEgp4B1xaWrvQnsA6mxIFSoRt4KC9EJLDjQUULQURwBXoWyAgvu/mBmBFdYI+QBCvcR1isk1BgIR8sN/YCRAuRkaD0h5FEjlhSSoiLJVHx7PhNknEZ/xoeEtxrUGc0ZotUqQZPCFDBCpbcpEUaBvIjHfkkDxQkzwCxITnIMVYgclaoZxkjfz3HIo0bMsvj14A45CZcgzPCAxo8hl0O4mSJF1moVoEnJRTqz8tu7Y0B/YGmQDpBE4D0UMOkFqDWYCVcA7cpj3AkB8AXwDOsyXKKGIYi1OgPlKKEltBByAkSlUZV9HMqsADhAs5LU0CU9BrvgdzyEp+kEVKOpNwJXAhXMCU7x1cXQigBu8wIQTwy8iCOJ4ekrYSBNkEBWouqyHyN74ErAzwJAv/WKORa1lgP9pN2g4KEDFYnvQlrAydwtJpIsVhoYHBVCCLaAgmjcHyjh2zZOou/Xn0R9eS2DDz11AH0W0J3kWAUShQeyi7X655H47zKsrRi15KaF2VZQfKk1iMGOQ/HNROmotwtQyuflLJEW6aEXMK4rRMJ8Bxcdpu0NrlFk9VjuxHIUAaNq9GEF7UooWpwksSAlaeVSRsAT9iRrfMpMBBmsUt1iZclIgfq7EKCpGr8ZRDCyJqMCPMTdmFVANpyXWppLCeMt0480Qw51iJllYEM/ZT6Hl6dCKU4rMiKnK7CjjQ3aGCYk4AlaozCu9RArJfKNBYpqJY4oY2MSByPUiYjdQ9V4OEaeI9Myoq4BMeTW/UKhduT+DMKKz3k3FbWhUgUHFeTyDKfAp6RFS4PdJUIVwrSZBYU6jLaEoBkLOeF+5Is+OAqSPw4rLwCAI9DYV5BnFAvmITaXG6V+US9D7B5EKu4l9JfhAxFWFWlVRY0gJ0k0nBcnoGmaQLVkeittU4qUnkQfZD/+4nwhg5Bpg1aE3hSbsIcIB+pqOOoIKjJKmAHKt+ghgXWywrj1SW4dSOOQDhW0KAhUhlH43YlImUauS5GUU2QkthBwJHMgsCGHpAIMNoHgJSEihLkuoBlJ8Sg29lArRHZzdXYDnmh32F5mVVI1AYcClqwdgtEpCBztGdh62mDYAbem9wD0u7GldE1/fMu9m9Zh7EA7/cxA8xCgvZ1FnA+BiBP4DlF+hMwny6ppLsAsg9ATdoAJMVU0Ey1wgow/fCmR/5sIRqa2//AZ1evuynVBspo9IoVOy7YsGZVX79t2x2dCxdv2bRh04IFqSTLOtFLPjpYvjDzja98JXQH2ty+IMEm0OLrHnjikYHBpXK06erVazdtXL/2fclMDiVbFyxct/KTwyOZotewrN7yqpUbt6xY+s6BPzDJ0fhH77gDpe84e885e84555xo1mZkKN+DgIubARPbzBrmabQQPYD+C+fxfVBOAHMFswAoDPAjHMApQNxSIdCBauiRgp1BdgogBsRDlDauhoYNwYwpAH4Ioop5KMYhwDEjOBREqhQdi44LMXkCcDkT/nVwSqCqe14Dw5gAQYIaTUMxCSIfpDB1MEl0RMjQBJiculD0Pgi5RS5io7irjjAnL6kajKvBQhfM0cWFhxwpgqeONyFN280DO6EalNwVQCeozpQYFBeyBA0SMmB64ILDQg2JEvBZiIkkDSC17YLBGnrPKA4cg4qBPnPgwG0HDly+48AB+BdEj+DxiCmP7iIKaJEc6ZRtkCzwayMbgY9BF0pEgMAqoCcKHvFVIutZwv4EgRFkISEdEIdABDVtCSyBk6wCHZXsyn0MwLCGowCbIJ5cga6ASH/k4gKRreFIACD82O94EeEJKU4CYNLtkERps8PkedLsSe9B2yoRNrWug8hdnYAMcnsCc9FYatAIwmAKdkixQAIRQcMJEo8/D1K/1jlRcQV3eNLABSIuBBfzUnlKyIURj6BfHA9OuiPpyCMUN9MwVAoqbtFpXkjH7BIEroicC44ISkIguvDnaPeucAbIeIOABBDlAGV42vV7ZEEI8sgviDxyl0akwg7+KpH1VOSSDIz9OxFRtCY4T13usSqIKhCodVKQCiQLdMWsh23CrIdjPTKgJDcSggGC2AdA2odBmkNXgQGo85je9RFYFieMdNsBGxjpuu3oIyAdESkU7elqUJdzCcNDZwkRQfk40gWsnMCxpPSAvbCkQ+8quPVMMQPpj/2KRCoZZLQCuFCT3D6Hgx6IJaoBVAvhGZDshIuwK5BB3msUv7s8QquZRup2RnUeI6KcoD4WE7hBsQWp3Tq4R9B3SHQPGYXSU5BVUwHgCRZrdXRBqAfsAxwoYCL3oQPkUPXLkCTNbgGyJ+BavAIF6VBSBCCEFxSRUsCFY3AXWhriRSMJ8UHBWK72F1md7QRgRPgl4GK9BkEMiJ6AAK035qnzAlkILqPRybIE0QlelUACgc/AMTRrkahM1c8TJZAjeiB0c6QMGqIklwmSIFwj1LA1awgU63CiRiAgwbMUthMswdF+EABRrT0COuWJfjLeLyO3/mVECc9Bj0+LgAAOEkqQI0xQrAsTPAhDX4kYYugbwjcxBWoRRsQelkyJlk0MVmI3IpYv0m+KOk4liX04XykXiK2MdKDYN3D5Br9ppzu3aA+uajZa7MaEHU4kzr0o0zVoaeqiXUWPH5m65NOEuRcskuT48uICfGNpx5x5t1Ukf3+zcf8qkftQ+sIL0u1dbIO97txcYV0WoWAD5A7LwxcM9F9+dmOLa4tkGPxrfAscidDvMQFfYXx5fQ/54VvGPoU31VcI7vaTz7BxeEZiEoAFljDbmIuZG5iPUHtpHABBvBwQHGo6tS2CC2wCCcoEBYhJu2bqs60ywQU8MeIiagCsUBPfIBCn7F4wXUviACoNIIAWvHuRPlsqkthK3Qi5MCNB7tHnSTgaP0EjTjKD0OGzz0bXP7enUP1m4Tytsc0fVaSg1uiTdDlcaNItVWkMmdLPm1UhqvPtiaDJ6X2Lxn7T1OBt0D0qYKImj+VVDK8nzEv4z4NEcgB6bMbeUMAOCNhz5BM2BwJQlTyBhFcC1vbgzwRaokbE9rWynF0pFTtF7Bn7t9aIz1m50ObYVt+qHgHnH3scJZ/csgX9K8heyQ5iBNKQ9ESgzOsa0bFWKqYnovGZAU8grZ7VoWhNvmTAE4oYAcHb0uixDM3SgtVv8EG1ySt55HlzfDoX0PPvKIuynw/rCVvuCiUjfNhoDYqDcyW1+oRs+2WPYHoMVYmEkmJAR3rU9HfKqqGZoRw7yTZoMM1Mnrmc+WfUhi5Dfw34cRs+hP+HHWXvYL/NNXP7CPYDmtegH6nZMrAxte12Y2rYhWrVgcmh+voB3hDG70AnbKQzuBM20Tkna6OjEOTkTXQ8f1xzmnOS9rQmfgZzWumY5jSHdaHmyZhfZ2N9xfxJG18ddKKmS6ARd0KmSyfg0uM41kObP67x0EGHhy6yw6EN1/XNXY24hb0d+eRwQTa6cvFVy3oishVatVsONgGGkYi2u3mzP6kPcvqbscZ9DvDWW2mN49ENVEN7C81x8tg/nrw5jsMfmL2NjB/7jIvh3hojmY4efgMjme+EbGTcJwFIvAmT8n+/lRZlaezwW2ZR5vBfnrTtlAOgz83CeMp99C2xM3pw5C2wM3K4iJb1916/OWqa7xgauDhiplBibmtlf5MpRnpGV8dbO/N+p4wMH2lQ4cRC6Vcnb8ODlvIWGfHm0Eb3lhvx8KffEiseuvJNmtEeIKLurTOjsaNvzo6Gv/b2GZ5BpXpLLc/Vf3iTJlYg/JuzsYLm9eatkq7d5c3bJY/2Q3gHc/Wp9kOwg5MNgvlJFsFT73cQOnTo6kOHdra2HDq09P77T7lvwfYVK649ePDag6F7R+69d4QBPXE6vc8Hil96yik+g+34lJN60TRT8ykm9UMz2aWn+gvNn9lfqJIwuBg0phg3iGN4kMvgQZzhDAxo+jjeQodByhnEu+fmm8nWgFbF3XwzaX8zXD2Oy9Djb/TsxFVqHpnkY+Wb8LFiRAsKNcWzqvqDu1QLPT3Vp6r63Y+pQfTcZH+qu9gvAX0aQGNdRukzmQ/LVEF1OXHq2AThtjK9fyy3ohT6xYW7zx4cUtTeuQPrVjy5duPQglWS2NRw1eq1mz3GymHCNOxQ75zhxR+s9Oa9yfSQs2bJsfyC2ESlvHX7Bdv+eslIKgns09/7wKata9ff2tbOodtxW3rRkpVLb+7OaXpbNve+jw3Mc0xr/7EcexhGgXJ/mP071mEamQqzmtnHXASt8ErmGubdzHsJFZyZ6FAjQ74wiRA6qo3Q0FY55TnLpY/bMPOF/CTqdROTjEs9k3oJ+ZwACERx0op+h/DuHVuH5ltmtDszPPz5zduWjZxnWg07hpdu6Onn+W0b1vX2Gd72Ymlk+ZNr1/fPbWqUle7SF/fz/LqVy/JFWS339C0fvnfR4q5sNGiWC7evZNllixZ0dApSpdSzaOEdVzQncCzel969B3VXf16tRr7i/thEd9eKVTs3PDG6olBq141s94c2bHz8nnIlqOdQV8fy0a1rPrVwcapdaY4PDW5ct3nryMbzOzrah5euW3lv/5yGBk9rcnDooZVr+vsWZFPJoXnLltyaL/qDDesHhu6av6gjlrhKq34QXRuqPolS4fBIKLQ+FBoNhYAfveM2FDKOtIX5PGhjy9D9aAwP41vw11gfex77V9DnLzvOqNIZT6sznlan1dNqymBa5oyz1RlnqzPOVn+MzlbTxjLvPONudcbd6oy71R+juxUjjet9QaaN6Qbdpky9rubXNZwggeJ5ohsXXUxPzoPkToKcF+n7EseE5oev3rd3zsDAnL37DtcPrt6/Y2upUilt3fFy/aBtOqqe8gA9yE15gB7867GwsTLuY+WOL76D+Q46Cz0DJBjFfzOLEUX2jPvV2+V+dfKja/1nfLTO+GjN1kfrZMd20PAZN64zblxv0o3L9Y9im/Gmcf8oM17ia+uLE25ReNOePS/Sv+sn/S322/hVJsKsYy5j3s8cJD22IPYi0mNTdyd4GrrocqkXFYktjnTcMdKxFXL5QWSZpBMn10w4JF1eIplByVKxUiTeUaluBIf5SqEsZnMCte3lqYU8lc1ZNjEg1dZyIKEj0xekrwAPoJKv2I0cPp6Pkos6cuKJbnJxAKHh1jxSBDlhRw2ElqPIMju3lJiGQQQADBIUTQGmE/xYUAgl5WhLVCb0VABgKzqgWywnCPdqyfakRvg4788GD5qZgF7aXII/7ittKRa3lMa+jPtaB1paBlrHvkz3eHfO8Vu6qhnBRnzjjTgXsdudXMbVbqmqqhmWx1D9ICyBP6AuFfgRHzZyjoKslNB9XsS+RxNAW4MfDwdW84XL4XdhAi0rbi5V95c2F2GPPgz7OS1zW6v7W+e2wB59GPaM69cOdXwH/kitjg0mQPzgCGwynUrcjIu1/Wa2fORXhw49j7vHvvl8bY8/MvYxfP43Hn/8cefhhx+u8csa/AzDAX4LEn4hEcV98cmmvrvwM2PvxHvGDiL+nnu23HMPfiY8th0/0wkXXifnW+4h0XA1f/sEYzMFZtC1bB/tbe/Yk42yU9ztbfEY9kX02saH3z2T0720ftWK3j5rJq/7lYsnm4jYxOIDMzndp5LzF66Z5HNfWl3zuUf/MlkxnVw+932C/pN/m4DAwRQLAW2WhKiIKXEWbxMc8SUK119mDzs33SzqCxLRpmvzs3il4FvOovMfqf5FdGv33aAz+Ne3L7jsQ6MzjU3uYc4/9SNlNR/MUz8+tn7Cs/KUD47trvlLMjOO+e5jLj/VdOWP5jdBPPU0RvgoDtXD6ikn9xUzcLTVEpg+3tY/q/G28cHc2Yyvhd1B2VmMpj04Pr5akz+vsZ/GL4G8TzEdTA9I65k7TAuuCR3kVXgnkYSr5QAI9EoTmhCu32tf0g5/3Nu+pK1tSfvYP+LezoGBzsuqn7m0a+7cL6Fg9ZeXjo52j47il+B2dZMbDH0K9tmBzuqmzgFUrL400Ik+1TlQ/fPqL39ll7tJeGaGfGZOMp+Qxe+66eG+WvagH65l77LOWvZa//nEsvaLT2YgA3hSnpzj5wdgBs3LMfIw9mXIwTGTdpOdznNzZs1zbvOdDdN9Z7wRzoLvLp9oSzPJrguYq059n4BFcmCTmTJiKEVvkxunoY8Ijdp2eyp4IJzLaxaoZmG/oCA1aJ36WTHmV383z852mO9bcd/qlqX7R8SmoKgK8QXLSbsSjxo/v4b5AHPotI6c24kpBi/AclMtXjF0lMnrdI6pZwYvnGrj4VoX56cYeTor0608p3zgfefIVEOAM6dlsh0A5aYaAqbhzDnMyMnjzGO1nZNHmyuP3RhOGnO+dGz2nlrmEtN38mWuY8hZlHEyKDzpUj1Xh3niuB7t9nsFaKX9oAcdq7c5Sq21qeoFqrZD1SCqw7Fw+t030EoRer3agL5efdTatWvXu9DG6qNXoK/iV99QmRyu3oCufe95555r7t27F6248ELaT0LndQB0yxHmCdpr9SJhfBWaUNAmSrtVsCq9oMWXiqkCMejnyinodaGKOhC1EZAXmXj3zSj6IlQG5cRs3szbvciiUREhYQbtHJEe/ciqFPLuUEAHMuPk0MyRjtg9hvCESiYNY/aiIEgbR0j1IjJqUErORXTgAPIA/0KxgF7y+Od+MROQMJK8Xel5L/8g0sR7I7pHELSGBl0K6aZgwLb6Ai9wekAwOI/gV3iEiBFPlJo0S8GYF1msLbvrYkHiJJEzfosG4EhUjOrzyBBlMj6jqtXffJUXWEHAgvCDH/RG4x4RYcHX8kJO5BD+iJK+vnkw1Bxo8eQS86IRhJxGw6eroajNxvuDikf3NQVUDUFCuikGuv2KhpGDJDEiK02a6YGIeYHaTHktJgsOxyv+gOYIgsfUZIflJJ9Bxn9Q0CqiaKfS4m8yC+H3NXOobgO6Be+rzZfk2gbifCvf6oufxXaMpfEN1Wert6NL0KVjDXjf2KvYdr79lcOHq70UJ9V5+ARx0s96d/bCH0fc/dhPcaSjr69j7Kewxa/2nNVbvaX3rB7Yoytgv7+3rXpLW29vG7qirde1PU2MGfUxy5kdJzNaNJvu6MTGkfbNqic5ocGmvz3J/mDqPAZlZohZfLLSUQyawAL5gnsnWQmS6bFmISmvI/MbXXKJ0RzymN6hH1dfPDgLgfl/Dh5EPTm50ZINXOS3b3/qgKszEIzpAMYsgOzZeYL4soYZx9EJuWDW4UwdYVLOEYPTECZ67cI9OwePApKXr1i5gW3gd2/d1NMnSPOG5q9b/bnlK7KFZsvq6/skgIFVuTxePn/RmuX3DAwt5GLNc/LXl1YvXdLWzi6dOzRSx5HpuaNAGgIXL5gKF9cAXBRwDhXy6zbu3vTpoXkNjYH2ziVLntiwef7iFd357Kq1W9feU6roRiqbo3CxM5Pr6hgeWT3yvs6ucbQ4EO+a2a5wHnPxafIlr6QAXIinx4X8fT/60cM3g0az94c/POUo/LJ8/tk9L1wdTM+9qnzjUf66c2epy9Uh2aw8dNsmUNhsfHIn4a7avCiPsV9gGxiJ0aiPdoh4dvoAd/gKJSTaFQNNrPhh5Hvllep9y+Ys+xas31xO1+VsQ/jI53Isn69eN2fZnM45y/o7+5fTPW3L+/CD7BqgXJCJglxnWqErR75CvgydNSAFsk06InToACigzxeJtuEe4gern0Dbqp/47PoNq53mZHpdb28s0pzI8qVWr7+xaUHbl9j/GiuvW9N3TcK/uNK4Kt2+pyWdbHP2+4PZgWS8bn+Y8D8nXsYzz1iZM1gzg4kbem3eStcbvTZ7peuUXhjExEPU9U2HIzHbi7LHm87yAMdhtJuM9XiIA8ju2+hwKGa523aTvYcMPe5GmOP8RwckzhnTgt16HCf2L59gtNPT53ZBwMjRqWePol2EiR2DdsAZ8UrKFp3jeeujm8R/WYOUNd99evinw8cpyCv8x8bGrnvfyy+7dpq7a+9zk1GNKQknRJntn5ISie+j6CaSHHrXtMjV6hI3G0fzRJzpPtYspjoCtANYp0j6jSgCfA7onAw5Ha+YvTt7enZeQja9hQ35/Ia9ZHOcAn+zFho2di00bJijZM3I7GRNUiTzN9QfGFfqZyV4yvnMfFn16+nW80s9nd2RMGdZndFls5BCq3EkvLnYUyimW1sFsbkpn7koW2wORaaVuTxL+SrGZ1U+ovrNojC7Hnuc8qprj4lA3zuea0CgR2U6P7nPHYcr6LU9O7bOm28Gm89euuQvN21ZOnK+ZeueXSPLNxdTIwvndUB3ny0Ub/rgnIFEUgSk0b6XTWS6Vq3eteGJe/KFtG5kuj60ftOGLfeVe/zq7a0tAMIW31oodRD4MXT3ouHO5vgUnoowGab3uPSdhg7QxJtMcdDmkhMknjOwbuVUEjfWKdzXfzshsJFKDw1vaFlyIYgAu8O7hE30lLfsADTkUrmxv2+cyh+oEzmTjRAa3zU4z+nYECmuGFv4/lxxYuytziOVWfbCdDoTnmDhWdnxr9uwvueS6o3o7n+aBcfcvWhR97bm225D/oeOaud9s+P5GgybTVG2Ap6aRSEupdCImemdunOgnz3lI1oz6zGnHH2+awa15xSD0HuOVpL4Gd5jPF3vMJ62NxdP4yuLM43pz5/NmL4rG1MWgUOzsj9OCMkPXjsIWGlwFlr1k3VxObKl6+WXu1zdEATNInwnk2ZWMGuYDcwWal2hkxeJxLySo4Ui/j3xSoGiBOK3kyKeO1AiPk6V6UnhYIkTktTDEf8ee7qf7rUcXteWxA3edIvXZrHVMDyCvsKj0UU8ZxuOJ+ENNrWMLq0uRJGUmUK1YAiAaHhR9Scxr8NzIaNFSxgmaowrL//kJwf+17M/wXdilM92d6fTobZWM2pGGp2ODrZrY1c8EWzRE3Y03BRv7+7obopMhPFouhFvs63OzkQi2Ko5FgmUSpjmpZZ1Gd3W6n/CZnsB8+WTsNYmXEPmuGk0R2yj8Ngk02i5kqWm0Uq5mJwwjXagOigTs7nJNlEBKB0jERB9KCgKTiJlWqAlmTli8+xAxARKraHkMJd3jaFQJznCjb0oWcrRiiP/whsZjf9Ckezzw00eYvhUi2033+zzsYrXx7GipHNSIKDzCueVviwHeRmLCvH8ZjGHzTDoEyLGuT0LZczzrLS3sOBGkbycwL/4aVElb7Z8+j0CcVCr/joRsEReUK15wwbxfH1je3RaMIZ9ZsoIarbYGOpox9jyKrLPALLLgtbaqGl+VRDloKA1qTqyeN4QWDMsiPRdEJ63eQFZyKOqEstaXl0ULVETiV+qN9BsRvJms7czryBUs3HfhM+GI+jHAnwrTwyku9GH0IeO/JBNVPfjs3/843XVIfTcKxP+Vp34SiYJ7egm5g7m49CKgqYgpshLuNAHW3ZJLBWor1vFJrtSwSyQOoEWMR6qAvLBhg20kTKESpXJpxmSqUpKhI1YEIlHHfkiAOEA0yDsYBOHDQOZTom0NxGCkt2UhOyEELTyZeKUZwWFRLJYRvdaVnokrQk9PVIwl2MFn5QIJ7RIs96yQ7B8utDgS4+kVKG3V1TTy9oC4fPDgbaRNhpeTS1LWRZa0oKMaKOaCMclv8C6cbREHK0hpjtGrEFtibSQG/k8ueGEHS2U0P3NmcxQJtMcCIdbIxF8ZWOjoKVG0oHQvnB0K2rRYxHNCSckr8Rms6IscZVzKhKknw7a+5Bp0wz39gkaXLGs88P+1EhKEypOtp79hqjuIBqJm3YB0vZLLWGaKYg+2qgl3OhXZeZ1d8/LOJFWkpNa/U0Zl+kmc9ydaCsXqe1cTNkVX/wNx2GwXk2jB7/x2mu/qcZOgN/XVh9GW+2b33vffdV/G/fd9OF7mWZmEx1hJ59KMY/ywqz7Ydb1CuqIaefoaIvrihnDlimM+2GmioNsknZjpSKEEbNmrffPu/PXpUo1L8y464WJXutoSaS8qrc/XYpgPB+Hi+m+dCrRcp7MR7yazEmC4g3rIA0UWYtyjcTNHHkrfRUvHa9oIK6wXsFjCFqRxVi1Es3DzQlLxSy3oXmtU33NWduMD5qBhclEa8QXjDg57mc/47JOJNg2klwYMNeSF1yoezJCEsYoGElZDf5mOSgZHIe9fr8Xc5whBeUEh3nbE4vEwpj/D6T5g6qlqapmKabHh5K9T98Av6d7XZvChF/iSfskzsr38ETdDce7b8TMgO8uOH1eFqdhbor1k42Yp9GT4lj+d6feTn4UNjz1NJ4BSp5iUm+cDjzJfAL3s09De1OYEPSZJWYJyLPzmSvpjJsPMJ9hnmG+xrzM/DeZSeMYngoBQnYLSEjn3LST7pAOHUtOJflaK4V6y7sTaBYSIpGVRHGGJ4opWk0U57omsGKSr0WRp+iVhwBQ4WXiCENuEXBL6p9O2pkUamklK+RMrMcCJ4na0BKtY55Y2UR3gJFyR3La/Xo2yX3XIQcdPpY7hTaS0VBA27FX03kt9xXNYNXlXWLY4EJLu7mx36uKoEr8vDjnC4ltqwXk+QOn+EWPLAflFUsAFs3/uUeXvcrcMO8LC22rJB29W9OxUgyLYS8XWciNvRDm1QYh5McRL9Lxq4aHRVnOGxKbGjisH/mEobM4B8nxGYw8+JcR7MkLRliaqyHP2KdUDUuD6zQfUldWBHRI61ADYSPq03hD27lKQp7qXNXDKpvPJ1nfs1LAIVnmFV0qblaxz1M+p03EHpw8pu8Hugx7hbCPC1soEOa0tSgQ4nu2KkCM/KZmrCCRVyVuYBUUcvsQmQAUc6ZPYoUYByWNpbDEa2I2qwWQOtQtBox+5LeF0jo1oG2fxz6KOzaMqqxPW7VO0btQRD9nh+ZHSvmqAUnxwukFWxQc0LbuldQe3Da4QAuy6sqtsn6/zxKWNrH+kBCdL4yyJitS8BsKIpwVTSMVsPjhAAqGOHM+yyKelVXWsYSwn1VzDuSwjjmmj8udplG50zMYd5pG4QgtPePvYHKTRqzWMs+gRehB9Cou4EPu+NWM742eeePybXvjcvvUd3F3nnmd8szrlLN9nbJxxje0cdeZlybPvDT5Jl+aJGMx32Qd/HPotTU6v1vCgc4kFTQLpE8BXOrgS77zXbzjJ18cO4KuMtsHDuCfH/kvvOvm7dvHjuBnNbNjXn3+gwk7wwrmEuYK5mrmOuY9J2FTFB0dTbfMTra4upZZkQyAmMG66k8ss870UNPtsm9kvHg3AMJOQ7JNGajiCbS2AVm7vUEWe6SgFJA0ry/T9n/vs1Rbly0cpIH01H0BMYCxLplCQFa9Qe6a669ffO2+60/A9rENN5Yzlidkqj7NMCwzjButcMgfUE0pqPk83oDZFIr49EjYsjw2hksQKmB7FTsSgDBiwEPC2EFNG9K0efQ/zZbbfpz3To628tgVYgMU34hI/OuvVz3o8P87gQKe9YEPFKuvIvu2mv1wGD9aG9PfRWf7TLi6g49WYDO1xPiKrtWhNlwsjPs6ptzBrzyt7jzhAHPSTfP415unX0eHD+zIxoj3Q0tzZiOSNmaaHRGJiVh2x4FrNndEfILgC3due21bZ5gcRjo2fwEaZENxx+s7ig0iF6zdC3LHuUqirV1eX11PLuNHg6rXF+GqrZGgTw4GZV+gAf2ARRGfVw06wFCyosi6HAjABo48cvAC4CwtGNR0rk1V3Duqkj3+xd9NXKTffTjE/i3gRlcPLQLttwAOv5q5fTaaZ02jc0dPhNrbEkS1RERhFGZUIFPjn3uoxUq/9kC1RKibQr5cb7HHVgy9qzIq8ms7d3t0Qev4B83g1Pk9csgj9Kzlxl5TVEFVM20g0MJhrrWLszjJgx8H7Y4NhTkjLMYiIvneA6h3fIZok11EvfsMdBeKRypv0CDi/evptx6IBrfgbMEvGR6fvrMfsIdwPJ3tEmyAwkNUIlCD1JVEZ8vsVXhTvaCfVZHEqWr3MkM1fIaxMMP7eCmgV19EDUZ+Txdoakr5wIAo+1HEOG8b0fO2nks+9QA9sqxwLTYoqWx4mGh6fpvvjWA+HEKhHJkbaUY7zjmn3ko22W3glCtXayY5GZzqMeYpLgmT7DYiYzBNTCczwKwECu9nrmFuYT7OPMw8wfwD8y3mp4TeKdGOlyvQbBwQThb7p2muwf+85IPfpeY0LZT96fDgtdde+6dtpkEPFs87OLbw/emRD7/ctWUL2vanZpqZ4HHyLSCLaWUqzDCzmTmXuYx5L/Mh5j7mMeZZ5iXmh1O+DPQnydyTvzK050+brSc+XfTdPzWGntHWeDpmbKg5BJ76EY0PLhl3IDzVIxnnFesOh8o4duQYnQkyCSbLLGY2MHtAp7uFjv4f7Yf2NiHH1FHOadzbBhnR60e5raHX3iaoWPum3VocIN7eAZdvrfpS+2xbxZw83ts9TvK7Ig1OV6IrmQw1eJv7400t8ZAZ89hqY6SlI9mRdhrzcX9TeY7ZEDYavBIaGUgVG4KiEDJMvaFo+hS5IZTwR5seriS6gl5JDAe6Y3qoQQyGWuzuRp2Z9q7oKHM58zjzBeZLJ2EpmOZ/4hzlfwKtfbL7SWG6+wmEEmvuJ2SunUnuJxXTEY/lfuK8gfvJG2nQSz16uDsksU4Lq8dimFV507Akw5Tsfi4icroeyoQltqWFFyOZiGbM9xjhTETiEg4rhTIhxd9pyUBQ02vyKoejUcxqvO21JG9QtuSAT7K8tqDwGGLmFd7yWqIvIKvBpqa2piZT83ptr/cEVPe8z89J4UzIr8z3WL2W7PdDghav8igW5VTsDCXI7bBHn29okDuRb2llyQXdg+YjxR/qDgl8wowhjhZOhLwhG3ItQn4gV7i5GXLNWSTXAblWHIvXWJxvSkej6SaLZNP2TnkXM3XinPFGNXACBKj7ouzAK0HyJUETIGhIdz0HLZsnEqrkCipfMZkQ+TlEeonudy6SqTL63zJQn19822IPMFBJsXX83o8rlo4DIan6S4T9qZ/gzn3XXINX0FlRMzsyiiN+XRCC6qEte8gOPagZ0lNNFzeq+sa76W/p6tVM7VuOrlx2dZQOZh6zmjmLuZi58SS0klMqkI9WGt719snjaXh+y9sli4/GKHtOx6xSp2lCqdMxlRRDZmerfadh8jjoVuYJJKPl6AFUxUvxrfjrrJ89n/0Ch7nlxxsVPfPBhjMfbDitH2yYNhh8zplPNpz5ZMOZTzb8UX6y4Rhj8Zkz32448+2GM99u+KP8doOr7/0Nvn183mgyHE4mZgTwmGFvyY3tum3HDnz72B344uzYL7A59gumGfDor9nH8NcBj8qMDlpqiIkzXUwZdLIVzHpmC+hlZPbY/cylzLuYA8z7mduZjzEPMI8wn2Y+y/wV8xTzJebvmX9ivgY4NV8oOfbMm7JdSXageEK0y8RgXi4leTIeR754x5NP4Fn0E3i8Tib+sfKFfKUI181CspLiSQwnspkUjWXX4iWf9UuRU5IQ6BzHyWHJEYuluC+QL/jilSB5Bw6Un9wuWHfW9vSYE0PQGMzg2K88mszqAnWJ2uX38IqPD4bGCqEAr3pFr64o+DcqrWrF4xeVIw+JOSR0syWI48gLsGHnHveon5cF1ScZvNc68iXBl1Y5/LIPdDm2wY6OZRvCPI8DXlHfPTl7oVB2B+zPgbVU/Uzsllvo/Jr35sZ/CGcxmyXN3+TEubIXuhPR9rCGRaSJX/T6NyBFBeUSRSNSqClgGnfmSrnbc2VYS7k7YN1sQUNV2XADy3saww3+wDyN18KyGEi1+0V/UBY02WybSC+XeOzT1UdRT/UXn7RDGYZw5B++Bvz5W9AkZcbD+BiTCTMxxqnPMGoTkxeZ3HSmFbatsMbJnnj2wNqd8+Z25na5a9aTO3J5jm3LHbkCWP3Id/Kb05OykktvvmUb/G7FxthvsuiJ6qj73uBB9gW2mbYXHXLDIF+BLki0yRSrrYDWiTfu/uQLyeqqT3xi+3a8dseOh9jmI9eyN5C1+h8PPXTR+fsvRPsueoiOa9/HfpSNwtFE+RjE2wiWVt5OTZuWhf1ode9QNRJGPx1AbPWcj905GB782EB44D6yT4aTbPQdZ1X/JomGt/HveGVTR2t7pj3ZUepo7chGyFtRuNbe99L809ZOpmGFln7kYbYd7x17DXurTW5Zic3lk+znIW+ubKjTvpXM7IKAvnyNzjatAb620rm86ZFD7pRQ5tzqA99/4AfvuSY9duBcdDidvvLQ2H2HrnxlDRv9/vf/97nwi6Yvv/e873//0Ojos5elD6J705dfnk6/cd0HanU841rLQ5zsazLtuHWPf7u5LTvp17b5Zlr5hCLZ6ih6AjRv5g+vQ36eBy4IMY0gB1uYNNPJZJki08PMYYbGZ1F3p/EzKecRC23JmSmT/AzXahWhwe/Id3zwm7+rnuWxdeOH+PnvVb34xc7Hcl/IPfoobCZ+P5s4rM8dWs+zazdw880EwojO9+5QZ7dj5HA8NyQv8yclX32yegOkX30lW/0xappEtbrNp+5Xp0IbsZimcd8617XOdlLupMRkKhlCoMl+dgcO/Hjfvg9ctW3bvn01d7unqLfdn10YWrRq2w0bNmyYn6btcHIa5tEpuF7xR8c8LdLtEKf7rth32EvwfzMXMf/pfgUBFlB4kxmUq78VRqzqNSNUrDafqkjf4J0YXogRvz33Tdzxl8wEd4S2VCyVqd3KjdvAmdqcIGJBzOfIJIdRFCM+SYPYNUz2o7zr6CeaMTe6StnuRWSmu/GhZQPFk6nxZQC7eahMvMeGKmU3a+QNY8gVJOrOQfJbPQRymSWf2EYg1FXVF7CbwgA4ea/GSx7B45VTLVbA9jbxOKy3dNmNvSkvj5aD0qMFG9SQ4hUVgQv59EbFbwgy8caVBDNsiR6SOVlgeVEkfQXBQYDq4DJBgfEIcQsVAeQRX1tEdBvUrPi9ku5RNFkCaCt4pGAgFAawXf2hHu+LhbodLQAhfZYRSzQ22U0ery54DADUiCeTIqqaZChBX3NN5RQBT/5a14LxxsH0Ao/N2s1G2BuWVRWp0YBj5ZKBhrytGVJCB5ilN0RDzb7W7sb4pn5T1DzdlR1LGvsbu2KdEcMmUz36HCXkXeZrLdsJn2qosq/ByrVkCs3zo45X01GcE7TeJeGenk50p5ML2jKoB/BYY3godb9oZtr9UY8CqpEccEJJq1CYs99OtfqLm1ubuluNkC5pXsR7onZzg1EezFttXf6IARqnAOSI+tORbIeQ72+PLm4M68L4d7Z+wL4Dv0LbcTv90tZywFq76/watIiJj1S6TV4x5glKKRP2KRRLxVQ3BnZ1EME9ZA3WhsNIiEK5Uko4ApmfBu5Y9D3zfMEihlngWhoe4Y4RlWjleajocvP3RvYgJZO5tb8xxsGVlt6YHJDHnszn8bP5/Nj8LifY4I2HNU4FRIq9sUpDLIXgHsotWuIE2+bOLScgpupd3d34h70dfluv6Iuzq9KJLpxD+VyOa4oOfbyhZ2lrc28L1Gt1SW5Z7tupJT4F133gQSVhxVJ842anBx7hW3ub9SZT/WpuYh7yx/BX6fhJhVlyMu/2C3DFglBlOE8eNeflN9sWptML2/Ci9KI0/MeexotixRj8x56OlWKx0pxkpZLEarJcTo79HquhRCI09vtwIoFfgqeqK9yn0OchlgSEr66gD8XQ52GTKyer7yEPouuSZXiu+h7yNLoulIAi/X8HUxgaAAB4nGNgZGBgAOKdog0R8fw2Xxm4mV8ARRhufVpngKD/ZzG/YA4CcjkYmECiAFedDEV4nGNgZGBgDvqfBSRfMDCASUYGVJADAF1gBAMAeJxjfsHAwDyKhwwGABFkYyUAAAAAAAAAADQAsgDoARgBfAJYB3QH5AsKC14LeAxmEcASeBMwE5oTwBQ8FQgaLBqWHe4eCB7gHwYfLB+cIA4guiF4IdwiMCJ6IrIjGCPcJNAlSiWsJg4m8CcOJ0Yn2ChKKOYpmCoAKjAqcCsOK04reivOLEgspCz4LVotwi4iLtwvhi/0MIYxgDGYMngyzDN4M8w0ejUwNrI3WDqIOqw7bDu4PHQ9Xj4MP35A6EGSQnRCwkPURBJEXkU6RdxLCkskTFpMokzKTQJNGE1YTaBN+k40TmZOjk/gUHRQ3gAAeJxjYGRgYMhhyWZQYAABJiDmAkIGhv9gPgMAIWkCEgB4nF2PvU7DMBSFT9q0QCsxgEBi84AQAin9YUD0AZrOrdQ9P07aKrGjxK3Up2HkCRgZeQokFl6Ek9R0IJbj7373XEcBcIFvODg8V9wHduCyOnALJ7ix3KYXll2uW8sd9HFvuUv/ZLmHRzxb7uMSIW9w3DNWD9hadnCKV8stnOPNcpv+3bJL/rDcwTU+LXfpvyz3sMSP5T7unJcoyLcqDsRClrt1JCvh5+EsLHI1l+k2C8oa672UZbXWSoy8YV36UskyMDIW4V5Uu3RsTCKSUudiqpWRWaZFUeqNjIy3MqaYDAaJ9V6kc0QIkPPnFGKSwAISJXZYsyNR0fjsh5hxFySFOX3KiYz58mj/zmUzX3FesxIYwcPw2PXZVU0igOEZMxFiz3fFb6YY0xokrBNmNGcEps1NdTrj0jRF09vQRPQeVs1UgQkGXMm/vMcUb/oFZv1ifgAAeJyVVvd72zYQ1Us9akmOZMlxkrbOsOOkTcvszrRNR7r33gMizxJiCGABUHL++4IEJVKRqM/hD/qIm+/eHY6qnar5p15b/AicwjNYwSrWsI5nsYE6GmhiE6fRQhtb6KCLbZzBDs7iHM7jOTyPF7CLC7iIS7iMPezjCg5wFdfwIl7CdbyMVxDgBm7iFm7jDu7iHl7Fa3gdb+BNvIX7eBvv4F08wHt4Hx/gQzzER/gYn+BTfIbP8QW+xFf4Gt/gW3yH7/EDfsRP+Bm/4Ff8ht/xB/7EX/gb/+BfMPQQIgLhEH0MwPEIRxAY1pom1DQOxppkOFi1mplBO1Qy4pYryURwKNS4GdEhS4TNDu0+szRmj4OYaSYEiYtcWtJDirhTBDQiaYOQ2XDgfmVI4nalXioZZEqdxJbLfjAkY1ifzhnLtJ2YqmFM0rAUz42yYs57AigYOqw8FtQQSsXBkOkj0p1Cy3PR/tJohvdd/TdPDt7yIenrlfZz6A6qeSuV3JjQfax03TVGUpiK2ySjqU/K8t6SaHk375+8ljmwZ2ZbkkfcW8pgRsi2of8Sp+asxPxGYkgHlpmjzV5iuHRtD3QiqGOSXhBrFaYCb7pbTjEHa6usdZmVrp5G39ALlfrMfafEa6kL2w7YBFeohGCxoahbEtJxzGRE0UpaVKsIkgXdr85pQiayDNW4Mhp3ZgqdenXKYl9gZ4bRjOS1IZMJE+uaQuIj2nTeImBujkbcPp6hMMvVLUvyK3l2ATzXcNqthC24PNouETHF3LJ0bAMmpbLZeb0XD93kqNZk0lPiBR13JucsQI85xpsTUZq6Pjko3S3ypGi4dJrSDfHEnC4EqXunOE6nqSTyZZd80nouVVab2y+603ag1fiJaSoTnCdfc4uYx7bhe5X1bcW49Ovuqox4SM28e16TXqD2E7ePFs2Zz16wf7m6hBzIvZOviSLsojnwqVPeri7dE9N10oiYZYHqPXI7ruHpyKrdSHnI3uqZhbFK07WlMQtki3rmkeU9u/MU36ecolsnd/Gzt4j1HEQe8srScnKomywKBirMV2MznwzPUfqTTUG3PGq56cHy6DmGRbvTo/RV3D152UVP3c62PORuPdrz+XWcX5tbgklyEVxFNuiR+5tRNzFLS1NKtIvPnoe6klrXBTNGZQYz3qynRuQlkVtwEaUlELVmJGPlJ4nLOLH52CXWvW8MWDpqLuhqX6skbrh/RI7KLHmt9j8cpp0SAA==') format('woff'), + url('data:application/octet-stream;base64,AAEAAAALAIAAAwAwR1NVQiCLJXoAAAE4AAAAVE9TLzI+IEq7AAABjAAAAFZjbWFw364brAAAA5QAAAdEZ2x5ZgkJhucAAAu0AAChvGhlYWQY7KABAAAA4AAAADZoaGVhBzwDvwAAALwAAAAkaG10eKXgAAAAAAHkAAABsGxvY2FyjUcSAAAK2AAAANptYXhwAZYEdwAAARgAAAAgbmFtZXo4dZUAAK1wAAACPXBvc3RWzTZJAACvsAAACigAAQAAA1L/agAAA+gAAAAAA+gAAQAAAAAAAAAAAAAAAAAAAGwAAQAAAAEAALkVgFhfDzz1AAsD6AAAAADa8q4wAAAAANryrjAAAP9qA+gDUgAAAAgAAgAAAAAAAAABAAAAbARrACAAAAAAAAIAAAAKAAoAAAD/AAAAAAAAAAEAAAAKADAAPgACREZMVAAObGF0bgAaAAQAAAAAAAAAAQAAAAQAAAAAAAAAAQAAAAFsaWdhAAgAAAABAAAAAQAEAAQAAAABAAgAAQAGAAAAAQAAAAED6AGQAAUAAAJ6ArwAAACMAnoCvAAAAeAAMQECAAACAAUDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBmRWQAwOgA6MQDUv9qAFoDUgCWAAAAAQAAAAAAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAPoAAAD6AAAA+gAAAAAAAUAAAADAAAALAAAAAQAAAIwAAEAAAAAASoAAwABAAAALAADAAoAAAIwAAQA/gAAAAYABAABAALoaejE//8AAOgA6MT//wAAAAAAAQAGANgAAAABAAIAAwAEAAUABgAHAAgACQAKAAsADAANAA4ADwAQABEAEgATABQAFQAWABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgArACwALQAuAC8AMAAxADIAMwA0ADUANgA3ADgAOQA6ADsAPAA9AD4APwBAAEEAQgBDAEQARQBGAEcASABJAEoASwBMAE0ATgBPAFAAUQBSAFMAVABVAFYAVwBYAFkAWgBbAFwAXQBeAF8AYABhAGIAYwBkAGUAZgBnAGgAaQBqAGsAAAEGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAABRQAAAAAAAAAawAA6AAAAOgAAAAAAQAA6AEAAOgBAAAAAgAA6AIAAOgCAAAAAwAA6AMAAOgDAAAABAAA6AQAAOgEAAAABQAA6AUAAOgFAAAABgAA6AYAAOgGAAAABwAA6AcAAOgHAAAACAAA6AgAAOgIAAAACQAA6AkAAOgJAAAACgAA6AoAAOgKAAAACwAA6AsAAOgLAAAADAAA6AwAAOgMAAAADQAA6A0AAOgNAAAADgAA6A4AAOgOAAAADwAA6A8AAOgPAAAAEAAA6BAAAOgQAAAAEQAA6BEAAOgRAAAAEgAA6BIAAOgSAAAAEwAA6BMAAOgTAAAAFAAA6BQAAOgUAAAAFQAA6BUAAOgVAAAAFgAA6BYAAOgWAAAAFwAA6BcAAOgXAAAAGAAA6BgAAOgYAAAAGQAA6BkAAOgZAAAAGgAA6BoAAOgaAAAAGwAA6BsAAOgbAAAAHAAA6BwAAOgcAAAAHQAA6B0AAOgdAAAAHgAA6B4AAOgeAAAAHwAA6B8AAOgfAAAAIAAA6CAAAOggAAAAIQAA6CEAAOghAAAAIgAA6CIAAOgiAAAAIwAA6CMAAOgjAAAAJAAA6CQAAOgkAAAAJQAA6CUAAOglAAAAJgAA6CYAAOgmAAAAJwAA6CcAAOgnAAAAKAAA6CgAAOgoAAAAKQAA6CkAAOgpAAAAKgAA6CoAAOgqAAAAKwAA6CsAAOgrAAAALAAA6CwAAOgsAAAALQAA6C0AAOgtAAAALgAA6C4AAOguAAAALwAA6C8AAOgvAAAAMAAA6DAAAOgwAAAAMQAA6DEAAOgxAAAAMgAA6DIAAOgyAAAAMwAA6DMAAOgzAAAANAAA6DQAAOg0AAAANQAA6DUAAOg1AAAANgAA6DYAAOg2AAAANwAA6DcAAOg3AAAAOAAA6DgAAOg4AAAAOQAA6DkAAOg5AAAAOgAA6DoAAOg6AAAAOwAA6DsAAOg7AAAAPAAA6DwAAOg8AAAAPQAA6D0AAOg9AAAAPgAA6D4AAOg+AAAAPwAA6D8AAOg/AAAAQAAA6EAAAOhAAAAAQQAA6EEAAOhBAAAAQgAA6EIAAOhCAAAAQwAA6EMAAOhDAAAARAAA6EQAAOhEAAAARQAA6EUAAOhFAAAARgAA6EYAAOhGAAAARwAA6EcAAOhHAAAASAAA6EgAAOhIAAAASQAA6EkAAOhJAAAASgAA6EoAAOhKAAAASwAA6EsAAOhLAAAATAAA6EwAAOhMAAAATQAA6E0AAOhNAAAATgAA6E4AAOhOAAAATwAA6E8AAOhPAAAAUAAA6FAAAOhQAAAAUQAA6FEAAOhRAAAAUgAA6FIAAOhSAAAAUwAA6FMAAOhTAAAAVAAA6FQAAOhUAAAAVQAA6FUAAOhVAAAAVgAA6FYAAOhWAAAAVwAA6FcAAOhXAAAAWAAA6FgAAOhYAAAAWQAA6FkAAOhZAAAAWgAA6FoAAOhaAAAAWwAA6FsAAOhbAAAAXAAA6FwAAOhcAAAAXQAA6F0AAOhdAAAAXgAA6F4AAOheAAAAXwAA6F8AAOhfAAAAYAAA6GAAAOhgAAAAYQAA6GEAAOhhAAAAYgAA6GIAAOhiAAAAYwAA6GMAAOhjAAAAZAAA6GQAAOhkAAAAZQAA6GUAAOhlAAAAZgAA6GYAAOhmAAAAZwAA6GcAAOhnAAAAaAAA6GgAAOhoAAAAaQAA6GkAAOhpAAAAagAA6MQAAOjEAAAAawAAAAAANACyAOgBGAF8AlgHdAfkCwoLXgt4DGYRwBJ4EzATmhPAFDwVCBosGpYd7h4IHuAfBh8sH5wgDiC6IXgh3CIwInoisiMYI9wk0CVKJawmDibwJw4nRifYKEoo5imYKgAqMCpwKw4rTit6K84sSCykLPgtWi3CLiIu3C+GL/QwhjGAMZgyeDLMM3gzzDR6NTA2sjdYOog6rDtsO7g8dD1ePgw/fkDoQZJCdELCQ9REEkReRTpF3EsKSyRMWkyiTMpNAk0YTVhNoE36TjROZk6OT+BQdFDeAAAAAgAA//wDugLAABIAGwAAASIOAhQeAjMyNjcHJz8BLgEJAQYeARcBLgEC9ClNOiAgOk0pRG4UlXQ+nR1Q/t7+MwcVKxcBwxskAsAgOk1STTogUUEoTXwqICP+6v7EFDYnAQE2Fz4AAAAFAAD/wAM7AucAGwAsADEAQABNAAABBiIHDgEdAQcOARYzITI2Ji8BNTQmJyYjIicjBSIGFxMeATMhMjY3EzYmIyEFKQEDIRMiIw4BFxMeAT4BJwMuASUiBgcDBh4BNjcTNiYBrQslBgoHrAoHBwoCcAkHBwmoBQkGFRIMR/7iCg8BMgENCgHJCg0CQAEOC/7i/v0BAwEDO/5iSwECDA8CLAISFg0CLAINAQ4KEAEsAg0XEQIsAg8C5wECAxUZCTgCDw0NDwI3ChoUAwIBzA8L/dUJDQwKAioLEDH+BwGwARIM/sALDgIUCwE/Cg0BDgr+wQsUAg4LAUAMEgACAAD/2AOPAu0AGQAdAAABBgcGBxYfAQYHBg8CPwI2NzY3Fh8BNjcBDwE3A49QUbNbFioaSSQgOtEp9wMfOCEjTRQoHjBa/igboBoC7SkoWisUKxpKJiI7IvcoD8I5IyVNEykeYK3+c6AboQABAAD/3wOPAu0AGwAAAQYHBgcWHwEGByMVMwcWFzczNSM3NjcWHwE2NwOPUFGzWxYqGlu4pnx+Cxacr4VSbDcUKB4wWgLtKShaKxQrGl26K34LFJ0rU243EykeYK0AAAADAAD/mgO4AyIAEAAUAEEAAAEiBwEGFBcBFjI3ATY0JwEmBwkCJSIPAxUjDwMVHwMzFR8DMz8DNTM/AzUvAyM1LwMB9A8L/mEKCgGfCx8KAZ8LC/5hCw8Bhv56/noBewICBAMBwgQEAwEBAwQEwgEDBAQWBAQDAcIEBAMBAQMEBMIBAwQEAyIL/mEKHwv+YQoKAZ8LHwoBnws+/nr+egGG5QEDBATCAQMEBBUFBAMBwgQEAwEBAwQEwgEDBAQWBAQDAcIEBAMBAAYAAP+VA74DJAAbADEASQBgAHkAigAAASYHBgcGBwYWFxYXHgE3PgI3NicuAScmJyMmBzYXFhcWFxYGBw4BJicuAjc2Nz4BFyYHBgcGBwYXFhcWFxY2Nz4CJyYnLgEHNhcWFxYHFgYHBgcGJicmJyY2NzY3NgcGBxcGBxYXNxc2NycmJzc2NyYnBg8BJi8BFhc3FwcXBycGByc2NycmJwHzbGVhQUILDDc8PVNPt1ZYhE8GBh4cd09QVwMVEGNdWTo8CAk9PkCmtE1QZCAVFzk4ol9bVVEyNQEEJCRBRFJNnj9BRQQeIDwwfEJQSkcrLAEBSD5AS0eMMzYUFRQnJz1KGx06cDg2KixucToeJjIZJTEYKiwZMCUZMiY5OG40b3E1cSRKNEolJjIZAyICNjVaXWxctklKKCgLHB13pFlbU1OGKCkFAjEDMTBTVmNYrEBDOxArLI+zVVlCRE4rAzAvT1JbU1BONjgQECUwMoyiSUwyKzAwAiwqRkpQS4ssLwcJMTM1RUKQOz0iK28eOnE2OCwqbnA6HSYyGSUwGSwqGDElGTIDODlvNG5xNXElSjRKJCYyGQAAEQAA/6MDuAMiAAMABgALAE4ApgD8AT8BlAHxAjwCfgLCAwUDXAOpA/UEPwAAAREhEQUzBzcVITUXETEjByMHIxUjByMPBxUfBDM3MzczNzM3MzczFzMXMxczFzMXMz8FNS8FIycjNSMnIycXIw8FHxk/BDUvAyMnNSc1JyMvASMvATUnIyc1LwEjLwE1JzUnIzUnIycjLwE1LwEjJzUnIy8BBSMPAhUHIw8BFQ8CFQcVBxUPASMPARUPAhUPASMPASMPARUHFQ8BIw8CFR8FMz8ZNS8EITEjFSMPAxUfBDM3MzczFzMXMxczFzMXMx8GMz8FNS8CIy8JIycjJyMnIzUHIw8CIw8BFQcjDwEjBxUHIwcjByMHFQ8DIxUPARUPAiMPAR8FMz8dMz8DLwMFDwUfFhUfBDM/BDUnNS8INSc1LwQjJyMnNS8BIyc1JzUvATUjLwE1JyMnNS8CNS8DBSMPBRUjFQcVBxUjFQcVFxUzFRcVFxUXFR8JMz8ENS8CNSc1JzUnNSc1JzU3NTc1NzU3NTc1LwQFIw8DFQcXFQcVBxUHFQcVDwgVHwMzPwY1NzU/BzU3NTM1NzU3NSc1LwMFIw8DFRcVFxUXFRcVHwcVFxUfAjMVHwMzPwQ1Lws1JzUnNSc1JzUvBAUjDwUVBxUPDxUfBT8CNT8BNTczNzU/AjM/CjU3LwQFDwUfAhUXFRczFzMfAjMfATMVFxUXFRczHwEzFzMXFRcVHwEVHwIVHwIzPwU1LxwFIw8IIwcjByMHIwcjByMPBBUfBTM3MzczNzM3Mz8LMzczPwE1PwE1NzU/BDUvBAUPBRUfBTMfARUXMxczHwEVHwEzFxUfBDMXMx8GMz8FNS8WISMPFxUfBjM/CDM/BDU3MzczNzU/ATU/ATM/BDUvBAEnAZr+weRynP7InAsGCwUMBQUGJgYKCAQCAwEBAgYECQQrBQkFBQUFCgUeBQoFBQUFCgQYCQUEAwQEAQEEAwQHJgUGBQsGCwb0BQQFAwQEAgIIAgwHCAcEAwQDBAMHBgcPAgkCAxEEAwQJCQQEAwQBAgMCAQIDAgECEgEJBAYBAwQDAQMIBAMBAwEDAQgEBAQBBAgBBAr98QUECAUEAQQEBAwEBAQIAwEDBAMEAwQJAQ8CAQIDAwUCAQQDAwECAwMIBAkFBwUFEAYCBgIPBwYHAwQDBAMEBwgEBAcCAQICAwQIAQQSDQoFBwYCAgMICAQBCAQmBAgDBAQEBwQIAxoDCAcHAwkFBAQEAwQBBAMHAQMFBAQEBAQJBCYECQUEBAUNrQQIBgcBAwgDARUDAQMDAQkBDAEJCQMDAgECAwMIBAEEAgICAwMIBAUECQMIAwIEAwIDAgMCAwIDAwMCAwMDAwMDAwMGAyMCAwQEAgIFBAgBhAUIBAMDAwEGAwMCAwIDAgMCAwcWAQQBBAEGAwICBAQDBAUJBAQEAwQBAgICAQIBAgECAgIBAQEIAQEBAgIEAQIHAwIBAgMCAQIGAwkDAwME/cMFBAgDAwICAQIBAQEBAQECAQgCAQEBAwMEBAQFCQQDAwQBAgUDAgEBAQEBAQIBAQIDAwkC7gUECAYCAQEBAQECCAIBAgIEARYBAQQDDAUJBAYFBAMKAggBAgECAgIHAQEBAQEEAwQI/KcFCAgEAgEBAQIGAgICAQQBBgIKAwQBAwYEBQkEBAQDBAIBEgECAQIBAgICBwIBAQEBAgcDBQLwBAUEBAMEAgIHAgMFAgQBAgMMCgMEAgQBAQQDBAQJCAUHAw8CAQYFCAEBAQYBAgECAQICAgkBAgIDBAj9rQQIBAMCAgIFBgwGAQwBBgcDAQMDAQcHAwEHBwEDAQMECAQEDAQEBAQJBAQEBQIBAQQEAxUDBAoHGQMKAgMDAwMDAwMCBgMCAwIDAggBnAUEBAEHChwLBxIECwQHBAQHBAgEEAgEAwICAQQEAwQFDAUNBAUEBQgFJgQJBAQEBAQEAQQDAQMBAwQECAQJBAICAQIGAwQF/kQEBQQGAgIBAgMDBQQBBA0JAQ0BBAUFBAEEBQUKBRkBBAEUBgUFBQsKCQUEBAMEAQIDAwQHEwUEBQUEBQUEJR4IBAQJDAMNAhIFBAUDARQEBAkIFiwFBQQFBQUEBRsEBgQBAQIDAwQEBAkDBgULBQUFBhQBHgUKBQUEAQQBBAUcDQQBBAUDAgECAgMECAH6/sgBODFaPrq6fQHyAQEBAQcCAgQDBAQECQQFBgMCCQIBAQEBAQECBQECAwMIBAUECQMDAwcBAQEBUwECAgQHCQkKAQkHBgcDBAMEAwQHCAcUBA0EBCADAwICAgIDAwgJBQYFBQQBBAEEBRwNBAEIBAEEBAQIAQMBAwEDBAgDAQMEAwEGBAcIAQQEAQMEAwEDDAMBAwEDAQgEBAQBBAQEAQQNFwUFBAEEAQkFCgUMBAUEBAMEAQIFBwweCAQJBBQHCAcEAwQDBAMHBgQDBwUEBAkEBAMEAQICBQwFCQQDBgIBAQEBAQICBwICAwICAgECAwMIBQkIAwUCAQIBAgECAgIJAgEBAScCAwUCBAECDwMCAQIJDAkBCQQDAwEDAwEDCwcHCQkEAwQEAQEEAwwDBAYDAwIDAwMDAwMCAwMCAwIDAgMCAwQDFgMDCAkJBwMEOwECAwMEDQULAgMDAwMDAwIDAwojAwcDBwQOCwMHAwgDAwIBAQMCBAgJBAIECQQEBAQEBQMBAwEDBAEDEAQDAQMIAwEKAQMDAQMDAQMDAQYECQEDAgMDqQEEBAMECAQECQUECQ0EGwUNCQQECQQBBCIEBQEEBAMDAgECAgMECAkFBBIECwQHBAQHBAgEHgQIBAcEBAcEBAkEBAQDBC0BBAgEBAQEHgUKBQUFBQkFJwUEBQkKBDIEBQQIBAYCAgYICgUZAQQBFAYFBQULBiYFBgULBgsGFgYJCAMDBBACBggICQYRBQYFBgsFIQYLBQUKBg8BBAEZBQoBBAYCAQEDAgQICQgBKQQFBQQFBQkFIgUJBQUFBQoFFwUEBwICOQECAwMIBwQHBBoDCwsHBgQDBxQPAwcBCAUEBQgDAwICAgIHAQMVAQMLAQcQBAQMBAQEBAQECQQmBQUIBAQDBKABBAMDBAkJCQYBDAEGDAYFAwMCAQQBBAECBQQCAgEBAQQBAQEGAQEBAgEBAgMHBQQFBAgDAwkCAQYDEAMGAwIDAgMCAwIDBgIDAwMDAwdKAQIBAwYMAwMFAwIBAQEEAwMECQUECAMDAgEBAQECCQICAgECAQIBAQECAgIBAQEEAQEBBQQDBQQFCAgDAgEnAQEDBgQJBQQEBAIFAwQJAQYJAwIBAgMCAQIDBAMKAggBAgECAgMBAgMDCAUECQQDAwMEAgECAQIBAgEQEAYCAwUJAwoBAgICDwIDBQYMFAECAQIBAgECBgIGCQQFBAQEAwMCAQEBAgICAQIBCAwDBAMCAQIDAgECEgEJBAMFBAQEBQkEAwMEAAAABQAA/5YDvQMjABYAMQA6AD4ARAAAASYOAxYXHgI3PgE3Njc2Jy4BJyYHNhcWFxYXFgYHBgcGBwYnJicmJyYnJjc+AhMGDwEXNRcRBycUFSclFBUnJicB9FimgUQBQD07obBRVIYnKQMGHh52Tl5rVlJQOjsXFxcrLERGWlhWWENDJyYDAyMecpQ/Nmwv0crKGI8BWSpDIgMiAUZ+obOmPj9JCR4delFTWFdVU4cmLzEBJiVDRFRPp0hKLzQQDxYWOTZQT1daTklwPv8AJk0hlI+PASiPYGRlZGVlZR4wFwAJAAD/ogO4AyAACwAXAFIAuwFEAaEB6AIyAp4AAAEVIxUzFTM1MzUjNQczFTMVIxUjNSM1MxMrAQcjByMHFQ8FFR8FMz8DMzczNzM3MxczFzMXMx8BMz8DNS8EIzUjJyMnFw8GFR8KMx8GFR8CMx8NMz8ENS8BNScjLwYjLwE1Iy8LIy8BIy8HIzUnNS8EIwUPASMPBBUPBBUHIwcVDwMjFQcVByMVDwQVByMPARUPAhUPAiMPAxUPAxUHFQ8KFQcVHwQ/BDM/BDU/JjUvAwEPBRUXFQcVBxUHFQcVBxUHFQcVBxUHFQcVBxUPBxUfBD8FMz8BMz8BNT8BMzczNzU3NTM1NzU3NTc1NzU3NTc1NzU3NSc1LwQFDwUVFxUXFRczHwUzHwEVFxUXFRcVMxcVFxUfBD8ENS8NNSc1JzUnNS8DAQ8XHwQ/BDM/AzM3MzczNzM3Mzc1NzM3NTczPwIzNzM1PwM1LwQFDwUVHwMVHwEzHwIzHwIVHwEzHwEVHwEzFzMXMxczHwIzFzMXFTMfBzMXFRczFTMXMz8ENS8EIycjJyMnIy8KIy8EIy8FIwGufX2MfX11Xn19Xn19MwcNBg0NBycNDgQDAwQBAgMDCQQFBgwGFwYLBgYGBikGBgYGBgUfBAUMAwMCAgYDCicGBwYNB+kFBAQDAwECAgIGAgIDBgUEAwgBDgIIAgYCBAECAgECAwIDBQQDBAMKBQYDCQQJBAQDBAIEBgEEAwIDAgMCAQIIAQUDBAIEAgIFAgcCAgECCQECAgMHAwIDAgECAwIGAgoF/fMFBwECAwIDAgMCAwIDBAECAgMCBAEEBAECAgICAgIBAgICBAICBAEBAQIDAgIKAQICAgEEAQIBAgECAQICAQQEAw4ECAQDAQECAwECAgIBAgECAQIBAgIDAgECCAIBAgICAQQCBAEGAgoCBgMKAwYDBQMCAQQGCAUCsgQFAwYCAQEBAQEBAQECAQMFAwUDBAMCBgMBAgMGBAkJBAQDAgEBAwIBAgMDAgEFAQwDAQIBAQEBAQEBAQQDBAQI/KYEBQMEBAEBAwIBAwIBBAEKAQQDAgMCAQIDAQIEBwkJBAQDBAIEAgMCBwQBBgECAQIIAQEBAQUICAKxBAQJBAUKBA8FBQsPBgULBSEGCxAIBAUCAgQDBA0JBgYUBQESBgcFAQUBBQEFARwBCwUBBQUBBQsPAQQBAwMCAQQDBAQI/d4EBAQDAgIBAgMEDQIBAgMCAQIDAwsCAQgDAwUBAgECAQIBAgMDAQIBAgECBAMGAw0DHQIDDQQDDQkFCAUCAQQDBAQDBAgDFAIRAwkFEQIGBQMFBQUCAQ8CBQICAQQDBBEEBAUCIX2MfX2MfRd9Xn1+XQGTAQIGAQIEAgMECAkFBAQDBAECAgIEAgEBAQEBBQEGBAMJBQgIAgQGAQEBTwEBAwMEBAQFCQQGAQIBBgMEAggOAwgDBgMEAQICAwQDBAMHBwMIAxIKCAIEAgIDAwgKBwcBDAgEBAQEBAQECwEHAwYCBgIDBQMHAwIDCQMCAgcCAgICAQEBAgIEAgUMAQQCAgICAgECAgICAgEEAgECAgMEAQQBBAECAwIDAgECAwIBAgYCAQIGAwMCBQIBAg8DAgECAQIDBgMDAwMDAwMDAQQJBQgDAwMBBAMDBAYFAwIFAQIDAgMCAwIDAwIFAgMCDAIDAgMCAwQDBAMGAwoDBgIKAQYCBQQEBAkJBgQB/sYBAgIIBAQEBDMDCQMGAgYDAwMDAwMGAgMDCQIOAwgBDQULBQYKBwQFCQQGAgICAgMDAwMGBgYFAQUHDyYDCgMDBwMDBAMDBAMDBAMHAw4DLAUJCAQCAwITAQIDAwgEGwYHEwcNEwYHDQYfDQUBBQEFAQUBBQEFAQEEBAQCAgIDAwkJCAYGBQYQCwYRBQYGBS8GBQYGDAYHBQcGAv6yAQIGBAQGBAkEAwUJAgMEAwwBBAMEAwcJCQgDAwMCAgEGAgYDAgMCAwMPBgEDAwEDBAcMBAEDBAQECQgEAwICAgEDAgQECQQFBAQDAQoCAgICAgIBAQcCBQEBAQMCAgECAQIBAQEBAQICAgQCCQEBBAEEAQQHBQQJCAQCAwEDBwYDAwYCAgMBAwIDAgkCAwIBBAEEDQECAAAAAQAAAAADQAKGADMAAAEiDgEHBhYXJicHFhc2NyYnBy4BNjc+ARYXHgIHDgInJgYeARcyNzYzPgI3Ni4BJyYCET51VhIVFygsWgyiUCAPJBccIhcZIiJnby8tPxYMC0ViNA0SARUOCBEMBj1rRQgJIUw0QwKFNV48QYw3CBI7IA+gUAcEkShsaygqLQMbF1RnMTNUMAEBFBsNAQICCUhrPjt1YBwkAAAAAwAAAAAC7gKKAAMABwALAAATETMRMxEzETMRMxH6ZGRkZGQCiv2oAlj9qAJY/agCWAAAAAkAAP+iA7wDIQADAAgAIgAxAEcAXABxAIQAkwAAAQYHIQMWFyE2EwYjBgcGBxYXFjc2NzYXNhcWFzYnJicmByYXBhcWFxYXFhcWNiYnJicFBgcGDwEGBwYXFjc2NzY3Nj8BNjc2AQYHBhcWFQYHBgcGFj4BNT4BJy4BBQYHBhcWFRYXFhcWNTQnJicmNS4BAQYHBgcGByIGBwYXNjc2NzYuAQUGFxYXFhc2Jy4BIyYvAQH0YGABgMBkM/7SM2gMHCkSHgoIFAwcEQgOCRkzHQ4WCwocGBUI6iALBh4OBSoiFBUEDjpF/fEdGxAbDg0LDAMEHQ0WEAoPEA0ZBQgCmRQFAgIBAQMLIAIQGBMXEwYCDfykFwQCBwQLCA0TIhAIAhMBDQKuEyQeDxoXESUDBBc3NTQsBwIN/dklGhEjT0QXBAMlEVA4BgIyrK0BJrRaWgHVAgEFCBcUAgEHBQECAgIGAwEVDgwGBAEBTwoVDRoMBS8+DRMoC18rCgwdESYTDRgaDxMGECQcDRYPDBYMFP7RBRQLHRAIDQlBPBEOBBMOMm02BwkSBRkOIhcJLRgmGgUZDyUUBz89CAv+swkXEgkNBhUNDwsKGhgmBxMOAQ4eFRIsCQsPDBUYMQMAEQAA/6MDuAMiAA8AIAAyAHUAywEpAXQBtwIPAlwCnwL3A00DjwPUBCAEagAAASYGBwYeAjc+ATc2JicmBzYeARcWBgcOAS4CNjc+ARcGDwEOARYXFj4BOwE1IzY3JicrARUjDwMVHwQzNzM3MxczFzMXMxczFzMfBjM/BTUvAiMvCSMnIycjJyM1DwQjDwEVByMPASMHFQcjByMHIwcVDwMjFQ8BFQ8CIw8BHwUzPx0zPwMvAyMFDwUfFhUfBDM/BDUnNS8INSc1LwQjJyMnNS8BIyc1JzUvATUjLwE1JyMnNS8CNS8EBQ8FFSMVBxUHFSMVBxUXFTMVFxUXFRcVHwkzPwQ1LwI1JzUnNSc1JzUnNTc1NzU3NTc1NzUvBQUPBRUHFQ8PFR8FPwI1PwE1NzM3NT8CMz8KNTcvBCMFDwUfAhUXFRczFzMfAjMfATMVFxUXFRczHwEzFzMXFRcVHwEVHwIVHwIzPwU1Lx0FDwgjByMHIwcjByMHIw8EFR8FMzUzNzM3MzczPwszNzM/ATU/ATU3NT8ENS8EIwMrAQcjByMVIwcjDwcVHwQzNzM3MzczNzM3MxczFzMXMxczFzM/BTUvBSMnIzUjJyMnFw8FHxk/BDUvAyMnNSc1JyMvASMvATUnIyc1LwEjLwE1JzUnIzUnIycjLwE1LwEjJzUnIy8CBQ8CFQcjDwEVDwIVBxUHFQ8BIw8BFQ8CFQ8BIw8BIw8BFQcVDwEjDwIVHwUzPxk1LwQjAQ8DFQcXFQcVBxUHFQcVDwgVHwMzPwY1NzU/BzU3NTM1NzU3NSc1LwMjBQ8EFRcVFxUXFRcVHwcVFxUfAjMVHwMzPwQ1Lws1JzUnNSc1JzUvBCMTDwQVHwUzHwEVFzMXMx8BFR8BMxcVHwQzFzMfBjM/BTUvFwUPFxUfBjM/CDM/BDU3MzczNzU/ATU/ATM/BDUvBCMB9EBvFxkYVn46PVEBBEQ5KzImSDMJDy0xIVBLOBoJFhhQYwkUHQkJBAgECQ0EWVkSJg4/BA4NCgUHBgICAwgIBAEIBCYECAMEBAQHBAgDGgMIBwcDCQUEBAQDBAEEAwcBAwUEBAQEBAkEJgQJBQQEBQ2xBQMGBwEDCAMBFQMBAwMBCQEMAQkJAwMCAQIDAwgEAQQCAgIDAwgEBQQJAwgDAgQDAgMCAwIDAgMDAwIDAwMDAwMDAwYDIwIDBAQCAgUECAUBhAQEBAMDAwEGAwMCAwIDAgMCAwcWAQQBBAEGAwICBAQDBAUJBAQEAwQBAgICAQIBAgECAgIBAQEIAQEBAgIEAQIHAwIBAgMCAQIGAwkDAwMECf3HBAgDAwICAQIBAQEBAQECAQgCAQEBAwMEBAQFCQQDAwQBAgUDAgEBAQEBAQIBAQIDAwkEAo0FBAQDBAICBwIDBQIEAQIDDAoDBAIEAQEEAwQECQgFBwMPAgEGBQgBAQEGAQIBAgECAgIJAQICAwQIBf2uBAQEAwICAgUGDAYBDAEGBwMBAwMBBwcDAQcHAQMBAwQIBAQMBAQEBAkEBAQFAgEBBAQDFQMECgcZAwoCAwMDAwMDAwIGAwIDAgMCCA4BpQQEAQcKHAsHEgQLBAcEBAcECAQQCAQDAgIBBAQDBAURDQQFBAUIBSYECQQEBAQEBAEEAwEDAQMEBAgECQQCAgECBgMEBQSnBQYGCwUMBQUGJgYKCAQCAwEBAgYECQQrBQkFBQUFCgUeBQoFBQUFCgQYCQUEAwQEAQEEAwQHJgUGBQsGCwbvBAUDBAQCAggCDAcIBwQDBAMEAwcGBw8CCQIDEQQDBAkJBAQDBAECAwIBAgMCAQISAQkEBgEDBAMBAwgEAwEDAQMBCAQEBAEECAEECgj99AQIBQQBBAQEDAQEBAgDAQMEAwQDBAkBDwIBAgMDBQIBBAMDAQIDAwgECQUHBQUQBgIGAg8HBgcDBAMEAwQHCAQEBwIBAgIDBAgEAq8ECAYCAQEBAQECCAIBAgIEARYBAQQDDAUJBAYFBAMKAggBAgECAgIHAQEBAQEEAwQIBPymBAQIBAIBAQECBgICAgEEAQYCCgMEAQMGBAUJBAQEAwQCARIBAgECAQICAgcCAQEBAQIHAwUElAUEBgICAQIDAwUEAQQNCQENAQQFBQQBBAUFCgUZAQQBFAYFBQULCgkFBAQDBAECAwMEBxMFBAUFBAUFBCUeCAQECQwDDQ0CGgQFAwEUBAQJCBYsBQUEBQUFBAUbBAYEAQECAwMEBAQJAwYFCwUFBQYUAR4FCgUFBAEEAQQFHA0EAQQFAwIBAgIDBAgEAjQBSzs5gFkeFRVtQD5xGhUnASI9JTduHRUHGjhNUSElLDISIzYBDxEDAwEGGSJECO0BAgIFDAUJBAMGAgEBAQEBAgIHAgIDAgICAQIDAwgFCQgDBQIBAgECAQICAgkCAQEBJwEBAwUCBAECDwMCAQIJDAkBCQQDAwEDAwEDCwcHCQkEAwQEAQEEAwwDBAYDAwIDAwMDAwMCAwMCAwIDAgMCAwQDFgMDCAkJBwMEPAEBAwMEDQULAgMDAwMDAwIDAwojAwcDBwQOCwMHAwgDAwIBAQMCBAgJBAIECQQEBAQEBQMBAwEDBAEDEAQDAQMIAwEKAQMDAQMDAQMDAQYECQEDAgMDAqsBBAQDBAgEBAkFBAkNBBsFDQkEBAkEAQQiBAUBBAQDAwIBAgIDBAgJBQQSBAsEBwQEBwQIBB4ECAQHBAQHBAQJBAQEAwQBdwECAwMIBwQHBBoDCwsHBgQDBxQPAwcBCAUEBQgDAwICAgIHAQMVAQMLAQcQBAQMBAQEBAQECQQmBQUIBAQDBKECAgMDBAkJCQYBDAEGDAYFAwMCAQQBBAECBQQCAgEBAQQBAQEGAQEBAgEBAgMHBQQFBAgDAwkCAQYDEAMGAwIDAgMCAwIDBgIDAwMDAwcDTQECAQMGDAMDBQMCAQEBBAMDBAkFBAgDAwIBAQEBAgkCAgIBAgECAQEBAgICAQEBBAEBAQUEAwUEBQgIAwIBAs4BAQEBBwICBAMEBAQJBAUGAwIJAgEBAQEBAQIFAQIDAwgEBQQJAwMDBwEBAQFTAQICBAcJCQoBCQcGBwMEAwQDBAcIBxQEDQQEIAMDAgICAgMDCAkFBgUFBAEEAQQFHA0EAQgEAQQEBAgBAwEDAQMECAMBAwQDAQYEBwIKAQQEAQMEAwEDDAMBAwEDAQgEBAQBBAQEAQQNFwUFBAEEAQkFCgUMBAUEBAMEAQIFBwweCAQJBBQHCAcEAwQDBAMHBgQDBwUEBAkEBAME/sUBBAgEBAQEHgUKBQUFBQkFJwUEBQkKBDIEBQQIBAYCAgYICgUZAQQBFAYFBQULBiYFBgULBgsGFgYJCAMDBBABAQYICAkGEQUGBQYLBSEGCwUFCgYPAQQBGQUKAQQGAgEBAwIECAkIASkEBQUEBQUJBSIFCQUFBQUKBRcFBAcCAv6yAQMGBAkFBAQEAgUDBAkBBgkDAgECAwIBAgMEAwoCCAECAQICAwECAwMIBQQJBAMDAwQCAQIBAgECARAQBgIDBQkDCgMDAQICAg8CAwUGDBQBAgECAQIBAgYCBgkEBQQEBAMDAgEBAQICAgECAQgMAwQDAgECAwIBAhIBCQQDBQQEBAUJBAMDBAAAAAYAAP+VA74DJAAbADEASQBgAG0AegAAASYHBgcGBwYWFxYXHgE3PgI3NicuAScmJyMmBzYXFhcWFxYGBw4BJicuAjc2Nz4BFyYHBgcGBwYXFhcWFxY2Nz4CJyYnLgEHNhcWFxYHFgYHBgcGJicmJyY2NzY3NhcUFSMVMxUzNTM1IzUHMjMVMxUjFSM1IzUzAfNsZWFBQgsMNzw9U0+3VliETwYGHhx3T1BXAxUQY11ZOjwICT0+QKa0TVBkIBUXOTiiX1tVUTI1AQQkJEFEUk2eP0FFBB4gPDB8QlBKRyssAQFIPkBLR4wzNhQVFCcnPUoYn596n59hJSSfn0mgoAMiAjY1Wl1sXLZJSigoCxwdd6RZW1NThigpBQIxAzEwU1ZjWKxAQzsQKyyPs1VZQkROKwMwL09SW1NQTjY4EBAlMDKMoklMMiswMAIsKkZKUEuLLC8HCTEzNUVCkDs9IitdTk58nJx8nBidSp2dSgAABwAA/5UDvgMkABsAMQBJAGAAaQBtAHMAAAEmBwYHBgcGFhcWFx4BNz4CNzYnLgEnJicjJgc2FxYXFhcWBgcOASYnLgI3Njc+ARcmBwYHBgcGFxYXFhcWNjc+AicmJy4BBzYXFhcWBxYGBwYHBiYnJicmNjc2NzYXBg8BFzUXEQcnFBUnJRQVJyYnAfNsZWFBQgsMNzw9U0+3VliETwYGHhx3T1BXAxUQY11ZOjwICT0+QKa0TVBkIBUXOTiiX1tVUTI1AQQkJEFEUk2eP0FFBB4gPDB8QlBKRyssAQFIPkBLR4wzNhQVFCcnPUpFNmwv0crKGI8BWSpDIgMiAjY1Wl1sXLZJSigoCxwdd6RZW1NThigpBQIxAzEwU1ZjWKxAQzsQKyyPs1VZQkROKwMwL09SW1NQTjY4EBAlMDKMoklMMiswMAIsKkZKUEuLLC8HCTEzNUVCkDs9IiujJk0hlI+PASiPYGRlZGVlZR4wFwADAAD/mgO4AyIAEAAUAEIAAAEiBwEGFBcBFjI3ATY0JwEmBwkCNyIPBB8CDwIfBD8CHwI/BC8CPwIvBA8CJzUnAfQPC/5hCgoBnwsfCgGfCwv+YQsPAYb+ev569AICBA8DAQEDiYkDAQEDDwQEBQSJiQQFBAQPAwEBA4mJAwEBAw8EBAUEiYkEAyIL/mEKHwv+YQoKAZ8LHwoBnws+/nr+egGGrQEDDwQEBQSJiQQFBAQPAwEBA4mJAwEBAw8EBAUEiYkEBQQEDwMBAQOJiQECAAAAAAEAAP/fA48C7QARAAABBgcGBxYfAQAHFhcBFh8BNjcDj1BRs1sWKhr+25YLFgG7FCgeMFoC7SkoWisUKxr+15cLFAHAEykeYK0AAAADAAD/lgO/AyIAGwAyAEsAAAEiBwYHBgcGFhcWFx4BNzY3PgE3NicuAScmJyYHNhcWFxYXFgYHBgcGJicuATY3Njc+AQcGBxcGBxYXNxc2NycmJzc2NyYnBg8BJicB62xjYD9ACgs5PT5UTrRTV0BDUAgHHRt5UVNZDQ9PSEcrLAEDQDk8SEaQNzkyECYoQCNSRx06cDg2KixucToeJjIZJTEYKiwZMCUZMgMiNzZbXWtct0dKJyYLHB07OaFYW1NViykqBAGOAikoRUdPSYkuMQwOKTAxh5c+QSMVFm4eOnE2OCwqbnA6HSYyGSUwGSwqGDElGTIAAAoAAP+SA8EDIwAcADcAUQBqAG8AcwB3AHsAfwCDAAABIiMiBwYHBgcGFhcWFx4BNzY3PgE3Ni4BJyYnJgcyMzYXFhcWFxYGBwYHDgEnJicmJyY3Njc+ARciIyIHBgcGBwYWFxYXFjY3Njc+AScmJy4BBzIzMhcWFxYXFgYHBgcGLgEnLgE3Njc+AQcVESERBTMRIxMVMzUHFTM1BxUzNQcVMzUB8wECY15bQEIWFR4uL0hIsllcS0poFRYbXkhJVjg7BQVcVlQ5Og8QKjQ1Sky0U1U7PR0cCQouNbhuBQVUT0syNAkLNjg6TEidRUYtMB4UFTYzjk8DA0tFQyssBgg3NThFRI92ICICHyE6KGNkAS7+6v7+GsrKysrKysoDIi4tTlBhVrNNTjM1Kg4PMC6RVletmzY3FA4xAS0rTE1bUqhERicqCiAhQD9XVFlbSldpKysqSElUT507PBsbDSUmP0CjT1I7OkMwJyZAQUtHizI0EhQbVT5AlkJFKx8ieQz+kAF8GP60AScZGUoYGFUYGE0YGAAAABAAAP+jA7gDIgALABcAWgCyAQgBSwGgAf0CSAKKAs4DEQNoA7UEAQRLAAABFSMVMxUzNTM1IzUHMxUzFSMVIzUjNTMTMSMHIwcjFSMHIw8HFR8EMzczNzM3MzczNzMXMxczFzMXMxczPwU1LwUjJyM1IycjJxcjDwUfGT8ENS8DIyc1JzUnIy8BIy8BNScjJzUvASMvATUnNScjNScjJyMvATUvASMnNScjLwEFIw8CFQcjDwEVDwIVBxUHFQ8BIw8BFQ8CFQ8BIw8BIw8BFQcVDwEjDwIVHwUzPxk1LwQhMSMVIw8DFR8EMzczNzMXMxczFzMXMxczHwYzPwU1LwIjLwkjJyMnIycjNQcjDwIjDwEVByMPASMHFQcjByMHIwcVDwMjFQ8BFQ8CIw8BHwUzPx0zPwMvAwUPBR8WFR8EMz8ENSc1Lwg1JzUvBCMnIyc1LwEjJzUnNS8BNSMvATUnIyc1LwI1LwMFIw8FFSMVBxUHFSMVBxUXFTMVFxUXFRcVHwkzPwQ1LwI1JzUnNSc1JzUnNTc1NzU3NTc1NzUvBAUjDwMVBxcVBxUHFQcVBxUPCBUfAzM/BjU3NT8HNTc1MzU3NTc1JzUvAwUjDwMVFxUXFRcVFxUfBxUXFR8CMxUfAzM/BDUvCzUnNSc1JzUnNS8EBSMPBRUHFQ8PFR8FPwI1PwE1NzM3NT8CMz8KNTcvBAUPBR8CFRcVFzMXMx8CMx8BMxUXFRcVFzMfATMXMxcVFxUfARUfAhUfAjM/BTUvHAUjDwgjByMHIwcjByMHIw8EFR8FMzczNzM3MzczPwszNzM/ATU/ATU3NT8ENS8EBQ8FFR8FMx8BFRczFzMfARUfATMXFR8EMxczHwYzPwU1LxYhIw8XFR8GMz8IMz8ENTczNzM3NT8BNT8BMz8ENS8EAa59fYx9fXVefX1efX0vCwYLBQwFBQYmBgoIBAIDAQECBgQJBCsFCQUFBQUKBR4FCgUFBQUKBBgJBQQDBAQBAQQDBAcmBQYFCwYLBvQFBAUDBAQCAggCDAcIBwQDBAMEAwcGBw8CCQIDEQQDBAkJBAQDBAECAwIBAgMCAQISAQkEBgEDBAMBAwgEAwEDAQMBCAQEBAEECAEECv3xBQQIBQQBBAQEDAQEBAgDAQMEAwQDBAkBDwIBAgMDBQIBBAMDAQIDAwgECQUHBQUQBgIGAg8HBgcDBAMEAwQHCAQEBwIBAgIDBAgBBBINCgUHBgICAwgIBAEIBCYECAMEBAQHBAgDGgMIBwcDCQUEBAQDBAEEAwcBAwUEBAQEBAkEJgQJBQQEBQ2tBAgGBwEDCAMBFQMBAwMBCQEMAQkJAwMCAQIDAwgEAQQCAgIDAwgEBQQJAwgDAgQDAgMCAwIDAgMDAwIDAwMDAwMDAwYDIwIDBAQCAgUECAGEBQgEAwMDAQYDAwIDAgMCAwIDBxYBBAEEAQYDAgIEBAMEBQkEBAQDBAECAgIBAgECAQICAgEBAQgBAQECAgQBAgcDAgECAwIBAgYDCQMDAwT9wwUECAMDAgIBAgEBAQEBAQIBCAIBAQEDAwQEBAUJBAMDBAECBQMCAQEBAQEBAgEBAgMDCQLuBQQIBgIBAQEBAQIIAgECAgQBFgEBBAMMBQkEBgUEAwoCCAECAQICAgcBAQEBAQQDBAj8pwUICAQCAQEBAgYCAgIBBAEGAgoDBAEDBgQFCQQEBAMEAgESAQIBAgECAgIHAgEBAQECBwMFAvAEBQQEAwQCAgcCAwUCBAECAwwKAwQCBAEBBAMEBAkIBQcDDwIBBgUIAQEBBgECAQIBAgICCQECAgMECP2tBAgEAwICAgUGDAYBDAEGBwMBAwMBBwcDAQcHAQMBAwQIBAQMBAQEBAkEBAQFAgEBBAQDFQMECgcZAwoCAwMDAwMDAwIGAwIDAgMCCAGcBQQEAQcKHAsHEgQLBAcEBAcECAQQCAQDAgIBBAQDBAUMBQ0EBQQFCAUmBAkEBAQEBAQBBAMBAwEDBAQIBAkEAgIBAgYDBAX+RAQFBAYCAgECAwMFBAEEDQkBDQEEBQUEAQQFBQoFGQEEARQGBQUFCwoJBQQEAwQBAgMDBAcTBQQFBQQFBQQlHggEBAkMAw0CEgUEBQMBFAQECQgWLAUFBAUFBQQFGwQGBAEBAgMDBAQECQMGBQsFBQUGFAEeBQoFBQQBBAEEBRwNBAEEBQMCAQICAwQIAiF9jH19jH0XfV59fl0BlQEBAQEHAgIEAwQEBAkEBQYDAgkCAQEBAQEBAgUBAgMDCAQFBAkDAwMHAQEBAVMBAgIEBwkJCgEJBwYHAwQDBAMEBwgHFAQNBAQgAwMCAgICAwMICQUGBQUEAQQBBAUcDQQBCAQBBAQECAEDAQMBAwQIAwEDBAMBBgQHCAEEBAEDBAMBAwwDAQMBAwEIBAQEAQQEBAEEDRcFBQQBBAEJBQoFDAQFBAQDBAECBQcMHggECQQUBwgHBAMEAwQDBwYEAwcFBAQJBAQDBAECAgUMBQkEAwYCAQEBAQECAgcCAgMCAgIBAgMDCAUJCAMFAgECAQIBAgICCQIBAQEnAgMFAgQBAg8DAgECCQwJAQkEAwMBAwMBAwsHBwkJBAMEBAEBBAMMAwQGAwMCAwMDAwMDAgMDAgMCAwIDAgMEAxYDAwgJCQcDBDsBAgMDBA0FCwIDAwMDAwMCAwMKIwMHAwcEDgsDBwMIAwMCAQEDAgQICQQCBAkEBAQEBAUDAQMBAwQBAxAEAwEDCAMBCgEDAwEDAwEDAwEGBAkBAwIDA6kBBAQDBAgEBAkFBAkNBBsFDQkEBAkEAQQiBAUBBAQDAwIBAgIDBAgJBQQSBAsEBwQEBwQIBB4ECAQHBAQHBAQJBAQEAwQtAQQIBAQEBB4FCgUFBQUJBScFBAUJCgQyBAUECAQGAgIGCAoFGQEEARQGBQUFCwYmBQYFCwYLBhYGCQgDAwQQAgYICAkGEQUGBQYLBSEGCwUFCgYPAQQBGQUKAQQGAgEBAwIECAkIASkEBQUEBQUJBSIFCQUFBQUKBRcFBAcCAjkBAgMDCAcEBwQaAwsLBwYEAwcUDwMHAQgFBAUIAwMCAgICBwEDFQEDCwEHEAQEDAQEBAQEBAkEJgUFCAQEAwSgAQQDAwQJCQkGAQwBBgwGBQMDAgEEAQQBAgUEAgIBAQEEAQEBBgEBAQIBAQIDBwUEBQQIAwMJAgEGAxADBgMCAwIDAgMCAwYCAwMDAwMHSgECAQMGDAMDBQMCAQEBBAMDBAkFBAgDAwIBAQEBAgkCAgIBAgECAQEBAgICAQEBBAEBAQUEAwUEBQgIAwIBJwEBAwYECQUEBAQCBQMECQEGCQMCAQIDAgECAwQDCgIIAQIBAgIDAQIDAwgFBAkEAwMDBAIBAgECAQIBEBAGAgMFCQMKAQICAg8CAwUGDBQBAgECAQIBAgYCBgkEBQQEBAMDAgEBAQICAgECAQgMAwQDAgECAwIBAhIBCQQDBQQEBAUJBAMDBAAAAAAIAAD/mgO4AyIAFAApAC4AMgA2ADoAPgBCAAABIgcGBwYUFxYXFjI3Njc2NCcmJyYHMhcWFxYUBwYHBiInJicmNDc2NzYHFREhEQUzESMTFTM1BxUzNQcVMzUHFTM1AfR6amY8Pj48Zmr0amY8Pj48Zmp6bl5bNTc3NVte3F5bNTc3NVteKQEu/ur+/hrKysrKysrKAyI+PGZq9GpmPD4+PGZq9GpmPD4xNzVbXtxeWzU3NzVbXtxeWzU31Qz+kAF8GP60AScZGUoYGFUYGE0YGAAAAAoAAP+iA7gDIAAPACAAMgBtANYBXwG8AgMCTQK5AAABJgYHBh4CNz4BNzYmJyYHNh4BFxYGBw4BLgI2Nz4BFwYPAQ4BFhcWPgE7ATUjNjcmAysBByMHIwcVDwUVHwUzPwMzNzM3MzczFzMXMxczHwEzPwM1LwQjNSMnIycXDwYVHwozHwYVHwIzHw0zPwQ1LwE1JyMvBiMvATUjLwsjLwEjLwcjNSc1LwQjBQ8BIw8EFQ8EFQcjBxUPAyMVBxUHIxUPBBUHIw8BFQ8CFQ8CIw8DFQ8DFQcVDwoVBxUfBD8EMz8ENT8mNS8DAQ8FFRcVBxUHFQcVBxUHFQcVBxUHFQcVBxUHFQ8HFR8EPwUzPwEzPwE1PwEzNzM3NTc1MzU3NTc1NzU3NTc1NzU3NTc1JzUvBAUPBRUXFRcVFzMfBTMfARUXFRcVFxUzFxUXFR8EPwQ1Lw01JzUnNSc1LwMBDxcfBD8EMz8DMzczNzM3MzczNzU3Mzc1NzM/AjM3MzU/AzUvBAUPBRUfAxUfATMfAjMfAhUfATMfARUfATMXMxczFzMfAjMXMxcVMx8HMxcVFzMVMxczPwQ1LwQjJyMnIycjLwojLwQjLwUjAfRAbxcZGFZ+Oj1RAQREOSsyJkgzCQ8tMSFQSzgaCRYYUGMJFB0JCQQIBAkNBFlZEiYOOwcNBg0NBycNDgQDAwQBAgMDCQQFBgwGFwYLBgYGBikGBgYGBgUfBAUMAwMCAgYDCicGBwYNB+kFBAQDAwECAgIGAgIDBgUEAwgBDgIIAgYCBAECAgECAwIDBQQDBAMKBQYDCQQJBAQDBAIEBgEEAwIDAgMCAQIIAQUDBAIEAgIFAgcCAgECCQECAgMHAwIDAgECAwIGAgoF/fMFBwECAwIDAgMCAwIDBAECAgMCBAEEBAECAgICAgIBAgICBAICBAEBAQIDAgIKAQICAgEEAQIBAgECAQICAQQEAw4ECAQDAQECAwECAgIBAgECAQIBAgIDAgECCAIBAgICAQQCBAEGAgoCBgMKAwYDBQMCAQQGCAUCsgQFAwYCAQEBAQEBAQECAQMFAwUDBAMCBgMBAgMGBAkJBAQDAgEBAwIBAgMDAgEFAQwDAQIBAQEBAQEBAQQDBAQI/KYEBQMEBAEBAwIBAwIBBAEKAQQDAgMCAQIDAQIEBwkJBAQDBAIEAgMCBwQBBgECAQIIAQEBAQUICAKxBAQJBAUKBA8FBQsPBgULBSEGCxAIBAUCAgQDBA0JBgYUBQESBgcFAQUBBQEFARwBCwUBBQUBBQsPAQQBAwMCAQQDBAQI/d4EBAQDAgIBAgMEDQIBAgMCAQIDAwsCAQgDAwUBAgECAQIBAgMDAQIBAgECBAMGAw0DHQIDDQQDDQkFCAUCAQQDBAQDBAgDFAIRAwkFEQIGBQMFBQUCAQ8CBQICAQQDBBEEBAUCNAFLOzmAWR4VFW1APnEaFScBIj0lN24dFQcaOE1RISUsMhIjNgEPEQMDAQYZIkQIAUgBAgYBAgQCAwQICQUEBAMEAQICAgQCAQEBAQEFAQYEAwkFCAgCBAYBAQFPAQEDAwQEBAUJBAYBAgEGAwQCCA4DCAMGAwQBAgIDBAMEAwcHAwgDEgoIAgQCAgMDCAoHBwEMCAQEBAQEBAQLAQcDBgIGAgMFAwcDAgMJAwICBwICAgIBAQECAgQCBQwBBAICAgICAQICAgICAQQCAQICAwQBBAEEAQIDAgMCAQIDAgECBgIBAgYDAwIFAgECDwMCAQIBAgMGAwMDAwMDAwMBBAkFCAMDAwEEAwMEBgUDAgUBAgMCAwIDAgMDAgUCAwIMAgMCAwIDBAMEAwYDCgMGAgoBBgIFBAQECQkGBAH+xgECAggEBAQEMwMJAwYCBgMDAwMDAwYCAwMJAg4DCAENBQsFBgoHBAUJBAYCAgICAwMDAwYGBgUBBQcPJgMKAwMHAwMEAwMEAwMEAwcDDgMsBQkIBAIDAhMBAgMDCAQbBgcTBw0TBgcNBh8NBQEFAQUBBQEFAQUBAQQEBAICAgMDCQkIBgYFBhALBhEFBgYFLwYFBgYMBgcFBwYC/rIBAgYEBAYECQQDBQkCAwQDDAEEAwQDBwkJCAMDAwICAQYCBgMCAwIDAw8GAQMDAQMEBwwEAQMEBAQJCAQDAgICAQMCBAQJBAUEBAMBCgICAgICAgEBBwIFAQEBAwICAQIBAgEBAQEBAgICBAIJAQEEAQQBBAcFBAkIBAIDAQMHBgMDBgICAwEDAgMCCQIDAgEEAQQNAQIAAAAAAwAAAAADIAJYAAMABwALAAATFSE1BRUhNQUVITXIAlj9qAJY/agCWAJYZGTIZGTIZGQAAAUAAP/TA9QC6QAaAFQAdACIAJwAAAEiBgc5ARQXFhcGBwYPARUhNScmJzY1OQE0JgcyMxcWFxYfARYXFjMxMj8BFhUUBwYHFxYXFhUWFRQPAQ4BIiYvASY1NDc0NzY/AScuATU0NzI3MzYHMDEGFxYXHgEyNjc2NzYnMDEWFxUjNSMVIzUjFSM1NhMiDgEVERQeATMhMj4BNRE0LgEjBSEyHgEVERQOASMhIi4BNRE0PgEBSCUyAQoGCSIXHg8BAUwBGksXMUkCAgQPCQYGAgUDBgoMCgQGBQYSAgYKAgEBAQkeJB4JAQEBAgoFAgQKDggDAQIJIgIBAgYMJiomDAYCAQIyEjAUnBUvEw41WTU1WTUCOjVaNDRaNf3GAjolPiQkPiX9xiU+JCQ+AmMxIxIWDgsMDxQcAoWFAjEaFyojMS8BAQICBAEEAQIFAwwPFg0PCxIBBAYGBAkCAgEKCgoKAQICCQQGBgQBFAMIIg4REAEDggwGDAUNDQ0NBQwGDBYgbExMTExsIgFLNFk1/m41WTQ0WTUBkjVZNDwkPSX+biU9JCQ9JQGSJT0kAAAAAAUAAAAAA48CkwADAAcACwAPABMAABMRIREFMxUjNyEVIQczFSM3IRUhWQM2/O2fn8ICLv3Swp+fwgIu/dICk/2XAmnRra2tI6WlpQADAAAAAANSArwABAAIABQAABMVESERBSERIQEVIxUzFTM1MzUjNZYCvP1wAmT9nAEFm5tam5sCvBn9XQK8LP2cAfqbWpubWpsAAAAABAAA/5YDvQMiABcAMwA/AEsAAAEiDgMWFx4CNz4BNzY3NicuAScmIxcyFxYXFhcWBgcGBwYHBicmJyYnJicmNz4CFwcVIxUzFTM1MzUjNQczFTMVIxUjNSM1MwHwV6V/RAFAPTuhsFFUhicpAwYeHnZOXmsIVVBOOToWFxcrLERGWlhWWENDJyYDAyMecpRPRn19jH19dV59fV59fQMiRn2hs6Y+P0kJHh16UVNYV1VThyYvMScmQkRST6dISi80EA8WFjk1UU9XWk5JcD4B0H2MfX2MfRd9Xn1+XQAAAAAEAAD/lgO9AyMAFgAxADsARAAAASYOAxYXHgI3PgE3Njc2Jy4BJyYHNhcWFxYXFgYHBgcGBwYnJicmJyYnJjc+AhcGDwEnAzc2NxcDFhcWFzcHJwcB9FimgUQBQD07obBRVIYnKQMGHh52Tl5rVlJQOjsXFxcrLERGWlhWWENDJyYDAyMecpT8ESIzgHQXSCOGiQcOQyIeHXwyAyIBRn6hs6Y+P0kJHh16UVNYV1VThyYvMQEmJUNEVE+nSEovNBAPFhY5NlBPV1pOSXA+0yNFaab+qh5bLpoBBgkSVSpDdJFNAAAGAAD/lQO+AyQAGwAxAEkAYABkAGkAAAEmBwYHBgcGFhcWFx4BNz4CNzYnLgEnJicjJgc2FxYXFhcWBgcOASYnLgI3Njc+ARcmBwYHBgcGFxYXFhcWNjc+AicmJy4BBzYXFhcWBxYGBwYHBiYnJicmNjc2NzYXBgchAxYXITYB82xlYUFCCww3PD1TT7dWWIRPBgYeHHdPUFcDFRBjXVk6PAgJPT5AprRNUGQgFRc5OKJfW1VRMjUBBCQkQURSTZ4/QUUEHiA8MHxCUEpHKywBAUg+QEtHjDM2FBUUJyc9SlVgYAGAwGQz/tIzAyICNjVaXWxctklKKCgLHB13pFlbU1OGKCkFAjEDMTBTVmNYrEBDOxArLI+zVVlCRE4rAzAvT1JbU1BONjgQECUwMoyiSUwyKzAwAiwqRkpQS4ssLwcJMTM1RUKQOz0iK2OsrQEmtFpaAAAAAAYAAP+VA74DJAAbADEASQBgAGoAdQAAASYHBgcGBwYWFxYXHgE3PgI3NicuAScmJyMmBzYXFhcWFxYGBw4BJicuAjc2Nz4BFyYHBgcGBwYXFhcWFxY2Nz4CJyYnLgEHNhcWFxYHFgYHBgcGJicmJyY2NzY3NgUGDwEnAzc2NxcDFhcWFzcHJwcGBwHzbGVhQUILDDc8PVNPt1ZYhE8GBh4cd09QVwMVEGNdWTo8CAk9PkCmtE1QZCAVFzk4ol9bVVEyNQEEJCRBRFJNnj9BRQQeIDwwfEJQSkcrLAEBSD5AS0eMMzYUFRQnJz1KAQIRIjOAdBdII4aJBw5DIh4dfA4ZCwMiAjY1Wl1sXLZJSigoCxwdd6RZW1NThigpBQIxAzEwU1ZjWKxAQzsQKyyPs1VZQkROKwMwL09SW1NQTjY4EBAlMDKMoklMMiswMAIsKkZKUEuLLC8HCTEzNUVCkDs9Iit2I0Vppv6qHlsumgEGCRJVKkN0kRYkEwAAAwAA/5YDvwMiABsAMgA7AAABIgcGBwYHBhYXFhceATc2Nz4BNzYnLgEnJicmBzYXFhcWFxYGBwYHBiYnLgE2NzY3PgEXBg8BFzUXEQcB62xjYD9ACgs5PT5UTrRTV0BDUAgHHRt5UVNZDQ9PSEcrLAEDQDk8SEaQNzkyECYoQCNSGTZsL9HKygMiNzZbXWtct0dKJyYLHB07OaFYW1NViykqBAGOAikoRUdPSYkuMQwOKTAxh5c+QSMVFqImTSGUj48BKI8AAAAEAAD/8AOrAswAEwAnACsAOAAAEyIOARURFB4BMyEyPgE1ETQuASMFITIWFREUBisBESERIyImNRE0NhMhESE3HQEjFTMVMzUzNSM12CpHKipHKgI5KkcpKUcq/ccCOSw+Pix0/q5zLT09vwEU/uxzTU0uTU0CzClHKv5YKkcpKUcqAagqRykwPiz+WCw+ATT+zD4sAagsPv6Z/uztFzYuTU0uTQAAAAAEAAD/8AOrAswAEwAnACsALwAAEyIOARURFB4BMyEyPgE1ETQuASMFITIWFREUBisBESERIyImNRE0NhMhESE3FTM11ypHKSlHKgI6KkcpKUcq/cYCOiw+Pix0/q50LD09vwEU/uwlywLMKUcq/lgqRykpRyoBqCpHKTA+LP5YLD4BNP7MPiwBqCw+/pn+66cvLwAAAgAA//ADqwLMABMAIwAAEyIOARURFB4BMyEyPgE1ETQuASMFITIWFREUBiMhIiY1ETQ21ypHKSlHKgI6KkcpKUcq/cYCOiw+Piz9xiw9PQLMKUcq/lgqRykpRyoBqCpHKTA+LP5YLD4+LAGoLD4AAAMAAP+WA78DIgAbADIAPAAAASIHBgcGBwYWFxYXHgE3Njc+ATc2Jy4BJyYnJgc2FxYXFhcWBgcGBwYmJy4BNjc2Nz4BFwYPAScDNzY3FwHrbGNgP0AKCzk9PlROtFNXQENQCAcdG3lRU1kND09IRyssAQNAOTxIRpA3OTIQJihAI1LWESIzgHQXSCOGAyI3Nltda1y3R0onJgscHTs5oVhbU1WLKSoEAY4CKShFR09JiS4xDA4pMDGHlz5BIxUWdSNFaab+qh5bLpoAAAYAAP+VA74DJAAbADEASQBgAGsAeQAAASYHBgcGBwYWFxYXHgE3PgI3NicuAScmJyMmBzYXFhcWFxYGBw4BJicuAjc2Nz4BFyYHBgcGBwYXFhcWFxY2Nz4CJyYnLgEHNhcWFxYHFgYHBgcGJicmJyY2NzY3NhcGAgc2NzY3FyYvARYfAScGBwYHBjc2NzYB82xlYUFCCww3PD1TT7dWWIRPBgYeHHdPUFcDFRBjXVk6PAgJPT5AprRNUGQgFRc5OKJfW1VRMjUBBCQkQURSTZ4/QUUEHiA8MHxCUEpHKywBAUg+QEtHjDM2FBUUJyc9SlUYTRcXKicUfRkxMgwWIkQIGRIGCgELFBYDIgI2NVpdbFy2SUooKAscHXekWVtTU4YoKQUCMQMxMFNWY1isQEM7ECssj7NVWUJETisDMC9PUltTUE42OBAQJTAyjKJJTDIrMDACLCpGSlBLiywvBwkxMzVFQpA7PSIrb0L+80EXMCsVh06eUSRJbkoHHRQHCwYhQ0wAAAAABwAA/5IDwQMjABwANwBRAGoAewCNAJ8AAAEiIyIHBgcGBwYWFxYXHgE3Njc+ATc2LgEnJicmBzIzNhcWFxYXFgYHBgcOAScmJyYnJjc2Nz4BFyIjIgcGBwYHBhYXFhcWNjc2Nz4BJyYnLgEHMjMyFxYXFhcWBgcGBwYuAScuATc2Nz4BFyIGBwYeAjc+ATc2JicmIxcyHgEXFgYHDgEuAjY3PgEfAQYPAQ4BFhcWPgE7ATUjNjcmAfMBAmNeW0BCFhUeLi9ISLJZXEtKaBUWG15ISVY4OwUFXFZUOToPECo0NUpMtFNVOz0dHAkKLjW4bgUFVE9LMjQJCzY4OkxInUVGLTAeFBU2M45PAwNLRUMrLAYINzU4RUSPdiAiAh8hOihjLz9sFxkYVn46PVEBBEQ5KzIEJUYyCQ8tMSFQSzgaCRYYUCw3CRQdCQkECAQJDQRZWRImDgMiLi1OUGFWs01OMzUqDg8wLpFWV62bNjcUDjEBLStMTVtSqERGJyoKICFAP1dUWVtKV2krKypISVRPnTs8GxsNJSY/QKNPUjs6QzAnJkBBS0eLMjQSFBtVPkCWQkUrHyJhSjs5gFkeFRVtQD5xGhUnIzwkN24dFQcaOE1RISUsATESIzYBDxEDAwEGGSJECAAEAAD/lgO9AyMAFgAxADwASgAAASYOAxYXHgI3PgE3Njc2Jy4BJyYHNhcWFxYXFgYHBgcGBwYnJicmJyYnJjc+AhcGAgc2NzY3FyYvARYfAScGBwYHBjc2NzYB9FimgUQBQD07obBRVIYnKQMGHh52Tl5rVlJQOjsXFxcrLERGWlhWWENDJyYDAyMecpRPGE0XFyonFH0ZMTIMFiJECBkSBgoBCxQWAyIBRn6hs6Y+P0kJHh16UVNYV1VThyYvMQEmJUNEVE+nSEovNBAPFhY5NlBPV1pOSXA+zEL+80EXMCsVh06eUSRJbkoHHRQHCwYhQ0wABAAA/5YDvQMjABYAMQA1ADoAAAEmDgMWFx4CNz4BNzY3NicuAScmBzYXFhcWFxYGBwYHBgcGJyYnJicmJyY3PgIXBgchAxYXITYB9FimgUQBQD07obBRVIYnKQMGHh52Tl5rVlJQOjsXFxcrLERGWlhWWENDJyYDAyMecpRPYGABgMBkM/7SMwMiAUZ+obOmPj9JCR4delFTWFdVU4cmLzEBJiVDRFRPp0hKLzQQDxYWOTZQT1daTklwPsCsrQEmtFpaAAcAAP/TA9QC6QATACcAKwAzADcAOwA/AAATIg4BFREUHgEzITI+ATURNC4BIwUhMh4BFREUDgEjISIuATURND4BFxEhEQUhFSE1IxUjFTMVIzchFSEDFSE11zVZNTVZNQI6NVo0NFo1/cYCOiU+JCQ+Jf3GJT4kJD4BAfr+GwHP/qkWYmJieAFX/ql2Ac0C6TRZNf5uNVk0NFk1AZI1WTQ8JD0l/m4lPSQkPSUBkiU9JEj+hAF8g2hnZxVmZmYBTmxsAAACAAAAAAN9ApMASgCzAAABIgc5AQYHOQEGBzkBBgcGHQEUFzAxFhcWMxY3OQEyNjU0JzMyNzY3MTYnPgE0Jy4BKwE2NTkBNCcuASMFNzY/ATY3PgEnLgEvATEHMDIVMhc5ARYGBzkBBg8BDgEVFBcxHgEXFjsBFjczMhYXFhQHBisBFSEyFhcWFTEUBiMhFSEyFhcWBzkBDgEjIRUzMhYXFhU5ARQHDgEjBic5ASInJic1Jj0BNDc2NzY3MTY3MTYzNDMBzA0SPbgvEwgCAQgQLShB3NsaHQUQGg4NBgYMGxsOCBgOFQYPCBgO/sMFBgQOEwYPBA0GEgoFAQEFBQUCBwgdDBYKAgIFBwMCDEuWcQYIBAgHBA/yAT4GCAQHCg/+wgEPBwkECAMDCwv+8c0FBgMGBgIGBtvbOR4hDQcBAgYRJMMxBwQBApMKKIAgPxggEyQNMSJBHhoBASMcDxEOCxgeFwMhOxQKDQ4RHRMLDAEFBwMOFQcTLhIKDAIBIwEIBxoJCh0MFQ0GAwYFBgEBAQEEBQwfCQYjBAUKEhMMIwYGDRUMCSMDBAgRDwgDAgEBFBY0AR4sCiQSHRUzGYcgBAEAAAMAAAAAA4oCagADAAYACwAAExEhEQUhBSUFJREhXwMr/SgChP6+/o4BcgFz/RsCav3pAhcj1Mr09P45AAIAAP/TA9QC6QATACMAABMiDgEVERQeATMhMj4BNRE0LgEjBSEyFhURFAYjISImNRE0Nuc5YTk5YTkCGjlhOTlhOf3mAhorOjor/eYrOjoC6ThfOf6KOV84OF85AXY5XzhuOSn+iik5OSkBdik5AAAFAAD/mgO4AyIAFAApADoATABeAAABIgcGBwYUFxYXFjI3Njc2NCcmJyYHMhcWFxYUBwYHBiInJicmNDc2NzYXIgYHBh4CNz4BNzYmJyYjFzIeARcWBgcOAS4CNjc+AR8BBg8BDgEWFxY+ATsBNSM2NyYB9HpqZjw+PjxmavRqZjw+PjxmanpuXls1Nzc1W17cXls1Nzc1W15qP2wXGRhWfjo9UQEERDkrMgQlRjIJDy0xIVBLOBoJFhhQLDcJFB0JCQQIBAkNBFlZEiYOAyI+PGZq9GpmPD4+PGZq9GpmPD4xNzVbXtxeWzU3NzVbXtxeWzU3vUo7OYBZHhUVbUA+cRoVJyM8JDduHRUHGjhNUSElLAExEiM2AQ8RAwMBBhkiRAgABQAA/5YDvQMjABYAMQA2AEAARgAAASYOAxYXHgI3PgE3Njc2Jy4BJyYHNhcWFxYXFgYHBgcGBwYnJicmJyYnJjc+AgcUFSERBTIzBgcGBwYHJjcUFSE1FwH0WKaBRAFAPTuhsFFUhicpAwYeHnZOXmtWUlA6OxcXFyssREZaWFZYQ0MnJgMDIx5ylH4Bmv7BcnIPIBgNFRBA4/7InAMiAUZ+obOmPj9JCR4delFTWFdVU4cmLzEBJiVDRFRPp0hKLzQQDxYWOTZQT1daTklwPvicnAE4MQkcFgkPAjQFXV26fQAABAAA/5UDvgMjABsAMwBKAGEAAAEmBwYHBgcGFhcWFx4BNz4CNzYnLgEnJicjJgc2FxYXFhcWBgcGBwYmJyYnLgE3Njc+ARciBwYHBhUGFhcWFxY2Nz4CJyYnLgEHMhcWFxYHFAYHBgcGJicmJyY2NzY3NgHwbGRhP0ILDDc8PVNPt1ZYhE8GBh4cdU5PVgMbBWNbWDg6BghBQEJUUbFMTjAzIBYWOjqnTVtSUDEyAkxCRVJMmz5ARAQfIDwygDVRSUcpKgNJPUBKR4wzNhQVFCcoPU0DIgE2NVpda1y2SUooKAscHXekWVtTUoYoKQYCMQEzMlRXY1irQEEbHBMqLEdHs1ZZQkZOLjIwUFNbU5s1Nw4PJzAxjKJJTDIsLzEtK0hKUUqHLC0HCTEzNUVCkDs+IS0AAAAABgAA/5UDvgMkABsAMQBJAGAAaABxAAABJgcGBwYHBhYXFhceATc+Ajc2Jy4BJyYnIyYHNhcWFxYXFgYHDgEmJy4CNzY3PgEXJgcGBwYHBhcWFxYXFjY3PgInJicuAQc2FxYXFgcWBgcGBwYmJyYnJjY3Njc2FxQVIxUzFTcnFh8BBzUjNTMB82xlYUFCCww3PD1TT7dWWIRPBgYeHHdPUFcDFRBjXVk6PAgJPT5AprRNUGQgFRc5OKJfW1VRMjUBBCQkQURSTZ4/QUUEHiA8MHxCUEpHKywBAUg+QEtHjDM2FBUUJyc9Sofi4qWJESIzZt/fAyICNjVaXWxctklKKCgLHB13pFlbU1OGKCkFAjEDMTBTVmNYrEBDOxArLI+zVVlCRE4rAzAvT1JbU1BONjgQECUwMoyiSUwyKzAwAiwqRkpQS4ssLwcJMTM1RUKQOz0iK3QyMr5kw3oUKD15NoUAAAADAAD/lgO/AyIAGwAyAD0AAAEiBwYHBgcGFhcWFx4BNzY3PgE3NicuAScmJyYHNhcWFxYXFgYHBgcGJicuATY3Njc+ARcGAgc2NzY3FyYnAetsY2A/QAoLOT0+VE60U1dAQ1AIBx0beVFTWQ0PT0hHKywBA0A5PEhGkDc5MhAmKEAjUikYTRcXKicUfRkxAyI3Nltda1y3R0onJgscHTs5oVhbU1WLKSoEAY4CKShFR09JiS4xDA4pMDGHlz5BIxUWbkL+80EXMCsVh06eAAAABQAA/6wDtgMbAAgADAAQABQAGAAAARURITUjETM1AQcXNw8BFzcPARc3DwEXNwKlARHg4P6eSzxL00s8S9JMPEvSTDxMAxsZ/LExAwUy/n48SzwsPEs7LDtMPCw8SzsABAAA/2oCogNSAAMAFAAeACkAACUhFSEBETMyNj0BNCc+AT0BNCcmIwczMhYdARQGKwEHMzIXFh0BFAYrAQKi/qQBXP6ksFVWUSMgJihUOTkfGiAiMAU1JRASHB1DKb8D6P02VFI6eyISRjccUCgqZCQoJikjbhIUMD4mIQAAAAAEAAD/mgO4AyIAEAAUAGoAbwAAASIHAQYUFwEWMjcBNjQnASYHCQIlMQ8DFS8CDwQfAiMPAxUfAzMPAh8EPwIVHwMzPwM1HwI/BC8CMz8CNS8CIz8CLwQPAjUvAgcwOQEwAfQPC/5hCgoBnwsfCgGfCwv+YQsPAYb+ev56AX0EBAIBYwMEBAQNAgEBAmOMBAMCAQECAwSMYwIBAQINBAQEA2MBAgQEEgQEAgFjAwQEBA0CAQECY4wEAwMDAwSMYwIBAQINBAQEA2MBAgSLAyIL/mEKHwv+YQoKAZ8LHwoBnws+/nr+egGGxgECAwSMYwIBAQINBAQEA2MBAgQEEgQEAgFjAwQEBA0CAQECY4wEAwIBAQIDBIxjAgEBAg0EBAQEYgECBBoEAgFjAwQEBA0CAQECY4wEAwMwAAAABAAA/5oDuAMiABAAFAAaAB8AAAEiBwEGFBcBFjI3ATY0JwEmBwkCJQ8BFyE3JxcHIycB9A8L/mEKCgGfCx8KAZ8LC/5hCw8Bhv56/noBhgfQUgEKUte7SOZIAyIL/mEKHwv+YQoKAZ8LHwoBnws+/nr+egGG4AWX/f1+h9zcAAACAAD/mgO4AyIADwATAAATBhQXARYyNwE2NCcBJiIHCQM7CgoBnwsfCgGfCwv+YQofC/6UAYYBhv56AXgKHwv+YQoKAZ8LHwoBnwsL/kcBhv56/noAAAAABAAA/5oDuAMiABAAFAAhAC4AAAEiBwEGFBcBFjI3ATY0JwEmBwkCJSIOARQeATI+ATQuAQcyHgEUDgEiLgE0PgEB9A8L/mEKCgGfCx8KAZ8LC/5hCw8Bhv56/noBhjlhODhhcmE4OGE5M1YyMlZmVjIyVgMiC/5hCh8L/mEKCgGfCx8KAZ8LPv56/noBhtI4YXJhODhhcmE4FzJWZlYyMlZmVjIAAAAAAwAA/5YDvwMiABsAMgBKAAABIgcGBwYHBhYXFhceATc2Nz4BNzYnLgEnJicmBzYXFhcWFxYGBwYHBiYnLgE2NzY3PgEXJgcOAQcGFxYXHgE3Njc2Nz4BJyYnLgEB62xjYD9ACgs5PT5UTrRTV0BDUAgHHRt5UVNZDQ9PSEcrLAEDQDk8SEaQNzkyECYoQCNSKTYyL0IJCxIOKCVpNTgsLhoZBBYWLB5LAyI3Nltda1y3R0onJgscHTs5oVhbU1WLKSoEAY4CKShFR09JiS4xDA4pMDGHlz5BIxUWUQIaGVk0ODM0KCUlBgcgHTIvbzA0IBgaAAMAAP+WA78DIgAbADIANgAAASIHBgcGBwYWFxYXHgE3Njc+ATc2Jy4BJyYnJgc2FxYXFhcWBgcGBwYmJy4BNjc2Nz4BFwYHIQHrbGNgP0AKCzk9PlROtFNXQENQCAcdG3lRU1kND09IRyssAQNAOTxIRpA3OTIQJihAI1IpYGABgAMiNzZbXWtct0dKJyYLHB07OaFYW1NViykqBAGOAikoRUdPSYkuMQwOKTAxh5c+QSMVFmKsrQACAAD/kgPBAyMAGgAxAAABIgcGBwYHBh4BFx4BNzY3PgE3NicuAScmJyYHMhcWFxYXFgYHDgEmJyYnLgE3Njc+AQHwZF5bQUIVFCBgSUevWFpJSmkWFw4NYkpMWTQnSkRBKy0HCTAzNIaQPD4iJgcfIDwqZgMiLy1QUWJWs5syMygODy8tkVVYVlqeNjgSC44lJD4/SUaLMzUrFCcoPUCaRUgsICEAAAADAAD/lgO/AyIAGgAvADkAAAEiBwYHBgcGFhcWFx4BNzY3PgE3NicuAScmJwc2FxYXFhcWBgcOASYnLgE2NzY3NhcGDwEXITY/ASYB62xjYD9ACgs5PD5UT7RTV0BDUAgHHRt5UVNZHE9IRyssAQNAOjuOkDc5Mg8nKEBIViRHbFIBChcrEEcDIjg2Wl5rXLZHSicmCxwcOzqhWFtTVYspKgSNAikoRUdPSYkvMBopMDGHlj9BIytWGjRO/USIMTQAAAAABAAA/5YDvwMiABsAMgA3AD0AAAEiBwYHBgcGFhcWFx4BNzY3PgE3NicuAScmJyYHNhcWFxYXFgYHBgcGJicuATY3Njc+AQcWFzY3BRQVIREHAetsY2A/QAoLOT0+VE60U1dAQ1AIBx0beVFTWQ0PT0hHKywBA0A5PEhGkDc5MhAmKEAjUn5WVThy/oUBmskDIjc2W11rXLdHSicmCxwdOzmhWFtTVYspKgQBjgIpKEVHT0mJLjEMDikwMYeXPkEjFRaaRUUuXCCMjAESowAAAAMAAP+WA78DIgAbADIAOgAAASIHBgcGBwYWFxYXHgE3Njc+ATc2Jy4BJyYnJgc2FxYXFhcWBgcGBwYmJy4BNjc2Nz4BFxQVIxUzFTcB62xjYD9ACgs5PT5UTrRTV0BDUAgHHRt5UVNZDQ9PSEcrLAEDQDk8SEaQNzkyECYoQCNSW+LipQMiNzZbXWtct0dKJyYLHB07OaFYW1NViykqBAGOAikoRUdPSYkuMQwOKTAxh5c+QSMVFnMyMr5kwwAAAAcAAP+VA74DJAAbADEASQBgAGUAbwB1AAABJgcGBwYHBhYXFhceATc+Ajc2Jy4BJyYnIyYHNhcWFxYXFgYHDgEmJy4CNzY3PgEXJgcGBwYHBhcWFxYXFjY3PgInJicuAQc2FxYXFgcWBgcGBwYmJyYnJjY3Njc2BxQVIREFMjMGBwYHBgcmNxQVITUXAfNsZWFBQgsMNzw9U0+3VliETwYGHhx3T1BXAxUQY11ZOjwICT0+QKa0TVBkIBUXOTiiX1tVUTI1AQQkJEFEUk2eP0FFBB4gPDB8QlBKRyssAQFIPkBLR4wzNhQVFCcnPUp4AZr+wXJyDyAYDRUQQOP+yJwDIgI2NVpdbFy2SUooKAscHXekWVtTU4YoKQUCMQMxMFNWY1isQEM7ECssj7NVWUJETisDMC9PUltTUE42OBAQJTAyjKJJTDIrMDACLCpGSlBLiywvBwkxMzVFQpA7PSIrm5ycATgxCRwWCQ8CNAVdXbp9AAUAAP+VA74DJAAbADEASQBgAGkAAAEmBwYHBgcGFhcWFx4BNz4CNzYnLgEnJicjJgc2FxYXFhcWBgcOASYnLgI3Njc+ARcmBwYHBgcGFxYXFhcWNjc+AicmJy4BBzYXFhcWBxYGBwYHBiYnJicmNjc2NzYXBg8BFzUXEQcB82xlYUFCCww3PD1TT7dWWIRPBgYeHHdPUFcDFRBjXVk6PAgJPT5AprRNUGQgFRc5OKJfW1VRMjUBBCQkQURSTZ4/QUUEHiA8MHxCUEpHKywBAUg+QEtHjDM2FBUUJyc9SkU2bC/RysoDIgI2NVpdbFy2SUooKAscHXekWVtTU4YoKQUCMQMxMFNWY1isQEM7ECssj7NVWUJETisDMC9PUltTUE42OBAQJTAyjKJJTDIrMDACLCpGSlBLiywvBwkxMzVFQpA7PSIroyZNIZSPjwEojwAAAAAEAAD/lgO9AyMAFgAxADsAQgAAASYOAxYXHgI3PgE3Njc2Jy4BJyYHNhcWFxYXFgYHBgcGBwYnJicmJyYnJjc+AhcGDwEXITY/ASYnFhcHIyc2AfRYpoFEAUA9O6GwUVSGJykDBh4edk5ea1ZSUDo7FxcXKyxERlpYVlhDQycmAwMjHnKUTyRHbFIBChcrEEeQfD9I5kg/AyIBRn6hs6Y+P0kJHh16UVNYV1VThyYvMQEmJUNEVE+nSEovNBAPFhY5NlBPV1pOSXA+tBo0Tv1EiDE0Slot3NwtAAAABgAAAAADRQKVACgATgBSAFYAWgBeAAABDwEGBwYHFBcWFzEWFxYXFgcGDwEhNzM+AScmJyYvASYnJjU0NzY/AQUhBgcGFQYXFhcxFhcWFxYHBgcGByE2NzYnJicmLwEmJyY3NDc2FxUzNQcVMzUHFTM1BxUzNQF8BAJUKSYCGxAoJBAXAwIWG0hKAcoFAU1FBQQXDyMNIhAUHSRNSf5EARonFScBGxAoJBAXAwEEBA0ZRf7lHxAjBQQYDyQMIg8VARwhCtzl5X3dvuUClQIBMjAuLigoGCklFR4VGhwjKysDLVctISIUJA4iFx4ZHSEqLisuHRouLigoGCklFR4VDQwODyEpFxYsLCIiFCUMIxYeGR0hJyIVFXMWFnQVFXMWFgAAAAQAAP/TA9QC6QATACcAawDGAAATIg4BFREUHgEzITI+ATURNC4BIwUhMh4BFREUDgEjISIuATURND4BFyIHOQEGDwEGBzkBBgcGHQExFBcwMR4BMxY3OQEyNjU0JzMyNjcwMTYnPgE3NjQmKwE2NTkBNCcuASsBNzY3PgEnJicHMDEyFzkBFgYHOQEGDwEGFzEWHwEzFjczMhcWFAcGIyInFRYzMhYVMRQHDgErARUzMhYHOQEOASsBFTMxMhYVOQEUBwYHITEmJyYnOQEmNTE0NzY/ATY/ATYz1zVZNTVZNQI6NVo0NFo1/cYCOiU+JCQ+Jf3GJT4kJD6qCggXaRkdDQUBAQULNCmJiRERAwoQEQQDBwgOBAgTEw4FCgQQCMYJFAUJAgcKDAMEAwMCBAIVGQQDAgcCAjFkRAkCBQUDCGYzQ4UHCQQDBAXIqwcLAwEHB6uCBgYDAwb+7iITFgcFBgsWQUgPAgQBAuk0WTX+bjVZNDRZNQGSNVk0PCQ9Jf5uJT0kJD0lAZIlPSQoBg9IERUnDxQMFwccGCsqAgIWEAoJEQ8UDgEJBgwlHAwMEgsGBwkUBgwdCw4BFgUFDwYDFRkGBwcBAQEBBQUXBQYBFgENCwsIAwMWEA4HBxYHDAcGAwECEBMgFRoyFSAQLDIKAQEAAgAAAAADhgJgAAIABwAAEwUlBREhEQVjAZEBkfzfAyL+bgJg4+NU/kABwOAAAAAABQAAAAADLAJ3ACYATgCGAI8AmAAAARQVBgcnBxcGByMVMxYXNQc1Mzc2PwEnNxc3Nj8BNTMVMyYnJic1BxUGBycHFwYHIxU3FhcHFzcWFxUzNTY3FzcnNjczNSMmJzcnByYnNwczBxcWHwE3FwcXFh8BMxUPAQYPARcHJwcGDwEVIzUnJi8BByc3JyYvAQc1Mzc2PwEnNxc3Nj8BFyIGFBYyNjQmBzIWFAYiJjQ2AZ0WFihKKAwGODgIFDExAwYRCCIZIgwaIA4kXgYWEQ4GGxEoSicLBjk5BwsoSykTGWkZEylKKQwGODgICydLJxYWAUckAQ8eHAsiGSEIEgYDMTEDBhEIJBojDBsfDiMPHxsLJBokCBIGAzIyAwYRCCMZIwwYIg4SIC8vQC8vIBMZGSUaGgJ3HBwGDCdKKBMYahkeWwEkDh8aDCMZIggQCAMwMAcJCAQ3ZTcICyhLKBQXagEVFyhKKAwGOToGDChLKBYWaRkSJ0onDAY3IzADBhEIIhkiDBseDiMBDh8aDCMaJAgSBgMzMgMGEggkGSMMGx8OASQOHhwMIhkiCBAIA1AvQS4uQS8jGiUZGSUaAAAAAAUAAP/TA9QC6QATACcAKwAuADMAABMiDgEVERQeATMhMj4BNRE0LgEjBSEyHgEVERQOASMhIi4BNRE0PgEHESERBSEHJxc3ESHXNVk1NVk1Ajo1WjQ0WjX9xgI6JT4kJD4l/cYlPiQkPgECDv4oAaLR8PDx/h8C6TRZNf5uNVk0NFk1AZI1WTQ8JD0l/m4lPSQkPSUBkiU9JFP+pQFbF4mDnp7+2QAAAAMAAAAAAxECnQAgAFgAeQAAASIHDgEVOQEUFxYXBgcGDwEVITUnJicmJzY1OQE0JicmBzIXFhcWHwEWFxYXMTY/ATY3FhUUBwYHHwIWFQYPAQ4BIiYvASYnND8DJicuAScmNTQ/ATYHMBUGFxYXFhcWMjc2NzY3Nic1FhcVIzUjFSE1IxUjNTYB8CskIicQCxA7KDUaAgJCAhk0KDooJyIkaAkFGhAMCQQIBgoQGQ4CBAIKCQweBBwEAQEBARA3OjcQAQEBAQQbAwQDBxAGDQ8GDDQDAgILFiIgSCAiFgsCAwRWIFMj/vAjUyACnBUTRCYiJRoRFBojMQTn5wQwIxoUKUomRBMVUwEBBQQGAgYCAwEBCAICARQaJxYbEiAJFAgPBAQCEBISEAIEBA8IFAkiBAIFFw0fGh8ZAgXhAQwSFAsWDAsLDBYKFQ0RASY5u4SEhIS7OQAAAgAA/5YDvQMiABcAMwAAASIOAxYXHgI3PgE3Njc2Jy4BJyYjFzIXFhcWFxYGBwYHBgcGJyYnJicmJyY3PgIXAfBXpX9EAUA9O6GwUVSGJykDBh4edk5eawhVUE45OhYXFyssREZaWFZYQ0MnJgMDIx5ylE8DIkZ9obOmPj9JCR4delFTWFdVU4cmLzEnJkJEUk+nSEovNBAPFhY5NlBPV1pOSXA+AQAABQAA/5UDvgMkABsAMQBJAGAAawAAASYHBgcGBwYWFxYXHgE3PgI3NicuAScmJyMmBzYXFhcWFxYGBw4BJicuAjc2Nz4BFyYHBgcGBwYXFhcWFxY2Nz4CJyYnLgEHNhcWFxYHFgYHBgcGJicmJyY2NzY3NhcGAgc2NzY3FyYnAfNsZWFBQgsMNzw9U0+3VliETwYGHhx3T1BXAxUQY11ZOjwICT0+QKa0TVBkIBUXOTiiX1tVUTI1AQQkJEFEUk2eP0FFBB4gPDB8QlBKRyssAQFIPkBLR4wzNhQVFCcnPUpVGE0XFyonFH0ZMQMiAjY1Wl1sXLZJSigoCxwdd6RZW1NThigpBQIxAzEwU1ZjWKxAQzsQKyyPs1VZQkROKwMwL09SW1NQTjY4EBAlMDKMoklMMiswMAIsKkZKUEuLLC8HCTEzNUVCkDs9IitvQv7zQRcwKxWHTp4AAAAABgAA/5UDvgMkABsAMQBJAGAAagBxAAABJgcGBwYHBhYXFhceATc+Ajc2Jy4BJyYnIyYHNhcWFxYXFgYHDgEmJy4CNzY3PgEXJgcGBwYHBhcWFxYXFjY3PgInJicuAQc2FxYXFgcWBgcGBwYmJyYnJjY3Njc2FwYPARchNj8BJicWFwcjJzYB82xlYUFCCww3PD1TT7dWWIRPBgYeHHdPUFcDFRBjXVk6PAgJPT5AprRNUGQgFRc5OKJfW1VRMjUBBCQkQURSTZ4/QUUEHiA8MHxCUEpHKywBAUg+QEtHjDM2FBUUJyc9SlUkR2xSAQoXKxBHkHw/SOZIPwMiAjY1Wl1sXLZJSigoCxwdd6RZW1NThigpBQIxAzEwU1ZjWKxAQzsQKyyPs1VZQkROKwMwL09SW1NQTjY4EBAlMDKMoklMMiswMAIsKkZKUEuLLC8HCTEzNUVCkDs9IitXGjRO/USIMTRKWi3c3C0AABAAAP+gA7sDIgAKABgAJgA1AEcAWABpAHcAhACTAKEArgC8AM4A3ADvAAABBgIHNjc2NxcmLwEWHwEnBgcGBwY3Njc2EwYHDgEWNzYXFjYmJyYXJgYWFxYXFj4BJy4BJyYFBgcGBwYHBgcGFjI3Njc2LgEzIgciBw4BHgE3NhcWNjQnJgcGBwYPAQYHBh4BNzY3NjQmBSYGFhcWFxYyNicmJyYFJgcGFx4BNicmNzYmBSYGFxYHBh4BNz4BJy4BBSYGFxYXFj4BJyY3LgEFJgcGBwYeATc2Ny4BBSIGFhcWFxY2JicmJyYFDgEHBgcGDwEOARY3Njc+ASYFIgYWFxYXFjYmJyYnJgUGBwYHBgcGBw4BFjc+ATc2LgEB9BhNFxcqJxR9GTEyDBYiRAgZEgYKAQsUFgxKLwwBEwxeZgwOBQwxygwPAwtMLQcZDwQYSS4E/fULCgYLCAVBIQQRGAcpUQYBDf8EChIHDQkJFAlORwwSC0LqDQ4JEAo9GQURGQYpUAgOAX8MDgIKNBkHGRAEGkQE/cMZBQsbBhgSAhoQAQ4C6Q8NAwEwBBAYBx0bBAIO/KQPDgMCMQcZDwUvAgENAuwaAg0yBg8ZCD0MAQ39qAsMAgg/VAwRAQtPNwYBnwwrCxMZDx4RDAUPDGFQCAEN/j0LDQMKWWkMDgUMZE8GAhgJCQUKDgg0WQsCEQ02ZSkHAg0CJkL+80EXMCsVh06eUSRJbkoHHRQHCwYhQ0wBdAIQBhkRBBkWARMZBAxTARMYBThbCgETDDJYIAIKAQcFCwgEPlAMEgtdQAcTDgECBBURCAMGHwQRGQYfJwIIBQwHMDAMEwELQC4HEw87ARIXBjVUCxMMXUEDqQIrVk0LAxEMUFQKDi0BGg5lXgwSAQo2eT4IChABGg5rWgoCEwxbZggLOAIpP0MMFAMLT1sJDqARFgZJHQMSGAYeQgZNAxUDBwQDAwIEGBQBAjAHFQ8nExYFSBMBFBgEFUIDAwEFAwkLAyMWBhkSAwsxIwcTDgAAAAAFAAD/lQO+AyQAGwAxAEkAYABoAAABJgcGBwYHBhYXFhceATc+Ajc2Jy4BJyYnIyYHNhcWFxYXFgYHDgEmJy4CNzY3PgEXJgcGBwYHBhcWFxYXFjY3PgInJicuAQc2FxYXFgcWBgcGBwYmJyYnJjY3Njc2FxQVIxUzFTcB82xlYUFCCww3PD1TT7dWWIRPBgYeHHdPUFcDFRBjXVk6PAgJPT5AprRNUGQgFRc5OKJfW1VRMjUBBCQkQURSTZ4/QUUEHiA8MHxCUEpHKywBAUg+QEtHjDM2FBUUJyc9Sofi4qUDIgI2NVpdbFy2SUooKAscHXekWVtTU4YoKQUCMQMxMFNWY1isQEM7ECssj7NVWUJETisDMC9PUltTUE42OBAQJTAyjKJJTDIrMDACLCpGSlBLiywvBwkxMzVFQpA7PSIrdDIyvmTDAAAAAA0AAP+iA7gDIAAEAAgADAAQABQAGABTALwBRQGiAekCMwKfAAABFREhEQUzESMTFTM1BxUzNQcVMzUHFTM1AysBByMHIwcVDwUVHwUzPwMzNzM3MzczFzMXMxczHwEzPwM1LwQjNSMnIycXDwYVHwozHwYVHwIzHw0zPwQ1LwE1JyMvBiMvATUjLwsjLwEjLwcjNSc1LwQjBQ8BIw8EFQ8EFQcjBxUPAyMVBxUHIxUPBBUHIw8BFQ8CFQ8CIw8DFQ8DFQcVDwoVBxUfBD8EMz8ENT8mNS8DAQ8FFRcVBxUHFQcVBxUHFQcVBxUHFQcVBxUHFQ8HFR8EPwUzPwEzPwE1PwEzNzM3NTc1MzU3NTc1NzU3NTc1NzU3NTc1JzUvBAUPBRUXFRcVFzMfBTMfARUXFRcVFxUzFxUXFR8EPwQ1Lw01JzUnNSc1LwMBDxcfBD8EMz8DMzczNzM3MzczNzU3Mzc1NzM/AjM3MzU/AzUvBAUPBRUfAxUfATMfAjMfAhUfATMfARUfATMXMxczFzMfAjMXMxcVMx8HMxcVFzMVMxczPwQ1LwQjJyMnIycjLwojLwQjLwUjAV0BLv7q/v4aysrKysrKymEHDQYNDQcnDQ4EAwMEAQIDAwkEBQYMBhcGCwYGBgYpBgYGBgYFHwQFDAMDAgIGAwonBgcGDQfpBQQEAwMBAgICBgICAwYFBAMIAQ4CCAIGAgQBAgIBAgMCAwUEAwQDCgUGAwkECQQEAwQCBAYBBAMCAwIDAgECCAEFAwQCBAICBQIHAgIBAgkBAgIDBwMCAwIBAgMCBgIKBf3zBQcBAgMCAwIDAgMCAwQBAgIDAgQBBAQBAgICAgICAQICAgQCAgQBAQECAwICCgECAgIBBAECAQIBAgECAgEEBAMOBAgEAwEBAgMBAgICAQIBAgECAQICAwIBAggCAQICAgEEAgQBBgIKAgYDCgMGAwUDAgEEBggFArIEBQMGAgEBAQEBAQEBAgEDBQMFAwQDAgYDAQIDBgQJCQQEAwIBAQMCAQIDAwIBBQEMAwECAQEBAQEBAQEEAwQECPymBAUDBAQBAQMCAQMCAQQBCgEEAwIDAgECAwECBAcJCQQEAwQCBAIDAgcEAQYBAgECCAEBAQEFCAgCsQQECQQFCgQPBQULDwYFCwUhBgsQCAQFAgIEAwQNCQYGFAUBEgYHBQEFAQUBBQEcAQsFAQUFAQULDwEEAQMDAgEEAwQECP3eBAQEAwICAQIDBA0CAQIDAgECAwMLAgEIAwMFAQIBAgECAQIDAwECAQIBAgQDBgMNAx0CAw0EAw0JBQgFAgEEAwQEAwQIAxQCEQMJBRECBgUDBQUFAgEPAgUCAgEEAwQRBAQFAhwM/pABfBj+tAEnGRlKGBhVGBhNGBgCLQECBgECBAIDBAgJBQQEAwQBAgICBAIBAQEBAQUBBgQDCQUICAIEBgEBAU8BAQMDBAQEBQkEBgECAQYDBAIIDgMIAwYDBAECAgMEAwQDBwcDCAMSCggCBAICAwMICgcHAQwIBAQEBAQEBAsBBwMGAgYCAwUDBwMCAwkDAgIHAgICAgEBAQICBAIFDAEEAgICAgIBAgICAgIBBAIBAgIDBAEEAQQBAgMCAwIBAgMCAQIGAgECBgMDAgUCAQIPAwIBAgECAwYDAwMDAwMDAwEECQUIAwMDAQQDAwQGBQMCBQECAwIDAgMCAwMCBQIDAgwCAwIDAgMEAwQDBgMKAwYCCgEGAgUEBAQJCQYEAf7GAQICCAQEBAQzAwkDBgIGAwMDAwMDBgIDAwkCDgMIAQ0FCwUGCgcEBQkEBgICAgIDAwMDBgYGBQEFBw8mAwoDAwcDAwQDAwQDAwQDBwMOAywFCQgEAgMCEwECAwMIBBsGBxMHDRMGBw0GHw0FAQUBBQEFAQUBBQEBBAQEAgICAwMJCQgGBgUGEAsGEQUGBgUvBgUGBgwGBwUHBgL+sgECBgQEBgQJBAMFCQIDBAMMAQQDBAMHCQkIAwMDAgIBBgIGAwIDAgMDDwYBAwMBAwQHDAQBAwQEBAkIBAMCAgIBAwIEBAkEBQQEAwEKAgICAgICAQEHAgUBAQEDAgIBAgECAQEBAQECAgIEAgkBAQQBBAEEBwUECQgEAgMBAwcGAwMGAgIDAQMCAwIJAgMCAQQBBA0BAgAAAAMAAP/SAyMC6gAFAAwAEQAAASIjESERJxQVMxEhEQUWHwEjAnLW1wJe5bj9/AF5FSo+fQLq/OgCYoldXf38Ar4MFStBAAAAAAgAAP/TA9QC6QATACcATgByAHYAegB+AIIAABMiDgEVERQeATMhMj4BNRE0LgEjBSEyHgEVERQOASMhIi4BNRE0PgEXByMOARQXFhcxFhcWFxYHBg8BITc2NzYnJicmLwEmJyY1NDc2PwEHMwYHBhUUFxYfARYXFhcWBwYHIzY3NicmJyYvASYnJjU0NzYXFTM1BxUzNQcVMzUHFTM11zVZNTVZNQI6NVo0NFo1/cYCOiU+JCQ+Jf3GJT4kJD5/AwEwLA8IFxUJDQECDRMlKQEBAy4QFAMCDQgUCBMIDBARLyn5nhYLFw4JFgIUCQ0BAg0OJ54TBxMCAg4IFQYTCQsQFAR7gIBGfGqAAuk0WTX+bjVZNDRZNQGSNVk0PCQ9Jf5uJT0kJD0lAZIlPSRcAhw2LxYNGBULEQwPDxYVGQIcFhkYEhMLFQgUDBENEBMVHRgaEQ4aGRYWDRcCFAwRDA8PExcQChcaExMLFQcTDRENEBMXFAwMQAwMQQwMQQwMAAQAAP/TA9QC6QATACcAKgAvAAATIg4BFREUHgEzITI+ATURNC4BIwUhMh4BFREUDgEjISIuATURND4BBxc3BREhEQfXNVk1NVk1Ajo1WjQ0WjX9xgI6JT4kJD4l/cYlPiQkPgX6+v4NAfT7Auk0WTX+bjVZNDRZNQGSNVk0PCQ9Jf5uJT0kJD0lAZIlPSRfjY00/ukBF4wABQAAAAADSAKpABsAMgBKAGIAegAAASIHBgcGDwERFhcWFxYgNzY3NjcRNCcmJyYnJgcyFxYXFhcGBwYHBiInJicmJzY3Njc2BzIfARYXFjI3Nj8BFQYHBgcGIicmJyYnFTIfARYXFjI3Nj8BFQYHBgcGIicmJyYnFTIfARYXFiA3Nj8BFQYHBgcGIicmJyYnAfR9XjAfIgcBBiQgL1cBCFcvICMHAQciHzBefXtZKxoRBgYRGSxc8FwsGREGBhEaK1m2AQMDGzRe+l40GwcEFBksXPBcLBkUBAEDAxs0XvpeNBsHBBQZLFzwXCwZFAQBAwMbNFcBCFc0GwcEFBksVf5VLBkUBAKpFAsQERoE/iUaFBEJFBQJERMbAdsDARoREAsUIxQJDQkKCgkOCRMTCQ4JCgoJDQkUawICDgwUFAwOBCkLCg4JExMJDgsKMAICDgwUFAwOBCkLCg4JExMJDgsKMAICDgwUFAwOBPMLCg4JExMJDgsKAAkAAP+fA70DIAAKABgAJgA0AEoAWgBoAHoAjgAAAQYCBzY3NjcXJi8BFh8BJwYHBgcGNzY3NhMGBw4BFjc2FxY2JicmFyYGFhcWFxY+AScmJyYFBgcGDwEGBwYHBhY2NzY3PgE3NjQmASYGFxYGBwYeATc+AScuAQUmBhcWFxY+AScmJy4BAQYHBgcGBwYHDgEWNzY3Ni4BBSIGFhcWHwEWMzI2JicmLwEmJyYB9BhNFxcqJxR9GTEyDBYiRAgZEgYKAQsUFhBQLwsBEgxhYw0OBgwrxQwPBAtCOAkYDQY4UwT98AoLBgsLLykJAgMZGQQlLQQWBAgNAq0PDQMDGBkEDxkHHxoHAg38pA8OAwUvBxkPBC0CAQ0CrgkJBgoNCDZWDAESDG5VBwIN/dkLDQMKQ2AGEggPDREOYTsDBgQGAiZC/vNBFzArFYdOnlEkSW5KBx0UBwsGIUNMAXICDwYZEgQYFAEUGAQLTwETGAUvZgoFFQtrOwMLAQgECwsuSw8LDxEPD0QvBREFCBMO/sgBGw8yZC0MEwEKNn0+BwkSARoPaFwKARMMW2cIC/6zAQYDCQoEJBcGGBIDGUgHEw4BEhcFORoCBRkYARgxAwUCBAAAAAYAAP+VA74DJAAbADEASQBgAGUAawAAASYHBgcGBwYWFxYXHgE3PgI3NicuAScmJyMmBzYXFhcWFxYGBw4BJicuAjc2Nz4BFyYHBgcGBwYXFhcWFxY2Nz4CJyYnLgEHNhcWFxYHFgYHBgcGJicmJyY2NzY3NgcWFzY3BRQVIREHAfNsZWFBQgsMNzw9U0+3VliETwYGHhx3T1BXAxUQY11ZOjwICT0+QKa0TVBkIBUXOTiiX1tVUTI1AQQkJEFEUk2eP0FFBB4gPDB8QlBKRyssAQFIPkBLR4wzNhQVFCcnPUpSVlU4cv6FAZrJAyICNjVaXWxctklKKCgLHB13pFlbU1OGKCkFAjEDMTBTVmNYrEBDOxArLI+zVVlCRE4rAzAvT1JbU1BONjgQECUwMoyiSUwyKzAwAiwqRkpQS4ssLwcJMTM1RUKQOz0iK5tFRS5cIIyMARKjAAAAABAAAP+gA7sDIgAHAA8AHQAsAD4ATwBgAG4AewCKAJgApQCzAMUA0wDmAAABJicHFyE2NycGByMnNjcWAwYHDgEWNzYXFjYmJyYXJgYWFxYXFj4BJy4BJyYFBgcGBwYHBgcGFjI3Njc2LgEzIgciBw4BHgE3NhcWNjQnJgcGBwYPAQYHBh4BNzY3NjQmBSYGFhcWFxYyNicmJyYFJgcGFx4BNicmNzYmBSYGFxYHBh4BNz4BJy4BBSYGFxYXFj4BJyY3LgEFJgcGBwYeATc2Ny4BBSIGFhcWFxY2JicmJyYFDgEHBgcGDwEOARY3Njc+ASYFIgYWFxYXFjYmJyYnJgUGBwYHBgcGBw4BFjc+ATc2LgECy0eQ11IBChcrDBgw5kg/fHx8Si8MARMMXmYMDgUMMcoMDwMLTC0HGQ8EGEkuBP31CwoGCwgFQSEEERgHKVEGAQ3/BAoSBw0JCRQJTkcMEgtC6g0OCRAKPRkFERkGKVAIDgF/DA4CCjQZBxkQBBpEBP3DGQULGwYYEgIaEAEOAukPDQMBMAQQGAcdGwQCDvykDw4DAjEHGQ8FLwIBDQLsGgINMgYPGQg9DAEN/agLDAIIP1QMEQELTzcGAZ8MKwsTGQ8eEQwFDwxhUAgBDf49Cw0DCllpDA4FDGRPBgIYCQkFCg4INFkLAhENNmUpBwINAaI0aJz9RIgoSpLcLVpaAVwCEAYZEQQZFgETGQQMUwETGAU4WwoBEwwyWCACCgEHBQsIBD5QDBILXUAHEw4BAgQVEQgDBh8EERkGHycCCAUMBzAwDBMBC0AuBxMPOwESFwY1VAsTDF1BA6kCK1ZNCwMRDFBUCg4tARoOZV4MEgEKNnk+CAoQARoOa1oKAhMMW2YICzgCKT9DDBQDC09bCQ6gERYGSR0DEhgGHkIGTQMVAwcEAwMCBBgUAQIwBxUPJxMWBUgTARQYBBVCAwMBBQMJCwMjFgYZEgMLMSMHEw4AEAAA/6ADuwMiAAMACAAWACUANwBIAFkAZwB0AIMAkQCeAKwAvgDMAN8AAAEGByEDFhchNhMGBw4BFjc2FxY2JicmFyYGFhcWFxY+AScuAScmBQYHBgcGBwYHBhYyNzY3Ni4BMyIHIgcOAR4BNzYXFjY0JyYHBgcGDwEGBwYeATc2NzY0JgUmBhYXFhcWMjYnJicmBSYHBhceATYnJjc2JgUmBhcWBwYeATc+AScuAQUmBhcWFxY+AScmNy4BBSYHBgcGHgE3NjcuAQUiBhYXFhcWNiYnJicmBQ4BBwYHBg8BDgEWNzY3PgEmBSIGFhcWFxY2JicmJyYFBgcGBwYHBgcOARY3PgE3Ni4BAfRgYAGAwGQz/tIzZEovDAETDF5mDA4FDDHKDA8DC0wtBxkPBBhJLgT99QsKBgsIBUEhBBEYBylRBgEN/wQKEgcNCQkUCU5HDBILQuoNDgkQCj0ZBREZBilQCA4BfwwOAgo0GQcZEAQaRAT9wxkFCxsGGBICGhABDgLpDw0DATAEEBgHHRsEAg78pA8OAwIxBxkPBS8CAQ0C7BoCDTIGDxkIPQwBDf2oCwwCCD9UDBEBC083BgGfDCsLExkPHhEMBQ8MYVAIAQ3+PQsNAwpZaQwOBQxkTwYCGAkJBQoOCDRZCwIRDTZlKQcCDQIyrK0BJrRaWgHXAhAGGREEGRYBExkEDFMBExgFOFsKARMMMlggAgoBBwULCAQ+UAwSC11ABxMOAQIEFREIAwYfBBEZBh8nAggFDAcwMAwTAQtALgcTDzsBEhcGNVQLEwxdQQOpAitWTQsDEQxQVAoOLQEaDmVeDBIBCjZ5PggKEAEaDmtaCgITDFtmCAs4Aik/QwwUAwtPWwkOoBEWBkkdAxIYBh5CBk0DFQMHBAMDAgQYFAECMAcVDycTFgVIEwEUGAQVQgMDAQUDCQsDIxYGGRIDCzEjBxMOAAAAAAUAAP+VA74DJAAbADEASQBgAGkAAAEmBwYHBgcGFhcWFx4BNz4CNzYnLgEnJicjJgc2FxYXFhcWBgcOASYnLgI3Njc+ARcmBwYHBgcGFxYXFhcWNjc+AicmJy4BBzYXFhcWBxYGBwYHBiYnJicmNjc2NzYXBgcXITY/ASYB82xlYUFCCww3PD1TT7dWWIRPBgYeHHdPUFcDFRBjXVk6PAgJPT5AprRNUGQgFRc5OKJfW1VRMjUBBCQkQURSTZ4/QUUEHiA8MHxCUEpHKywBAUg+QEtHjDM2FBUUJyc9SlWQR1IBChcrEEcDIgI2NVpdbFy2SUooKAscHXekWVtTU4YoKQUCMQMxMFNWY1isQEM7ECssj7NVWUJETisDMC9PUltTUE42OBAQJTAyjKJJTDIrMDACLCpGSlBLiywvBwkxMzVFQpA7PSIrV2g0/USIMTQAAAAKAAD/nwO9AyAABAAOABQAIgAwAEYAVgBkAHYAigAAARQVIREFMjMGBwYHBgcmNxQVITUXEwYHDgEWNzYXFjYmJyYXJgYWFxYXFj4BJyYnJgUGBwYPAQYHBgcGFjY3Njc+ATc2NCYBJgYXFgYHBh4BNz4BJy4BBSYGFxYXFj4BJyYnLgEBBgcGBwYHBgcOARY3Njc2LgEFIgYWFxYfARYzMjYmJyYvASYnJgEnAZr+wXJyDyAYDRUQQOP+yJwEUC8LARIMYWMNDgYMK8UMDwQLQjgJGA0GOFME/fAKCwYLCy8pCQIDGRkEJS0EFgQIDQKtDw0DAxgZBA8ZBx8aBwIN/KQPDgMFLwcZDwQtAgENAq4JCQYKDQg2VgwBEgxuVQcCDf3ZCw0DCkNgBhIIDw0RDmE7AwYEBgH6nJwBODEJHBYJDwI0BV1dun0B8AIPBhkSBBgUARQYBAtPARMYBS9mCgUVC2s7AwsBCAQLCy5LDwsPEQ8PRC8FEQUIEw7+yAEbDzJkLQwTAQo2fT4HCRIBGg9oXAoBEwxbZwgL/rMBBgMJCgQkFwYYEgMZSAcTDgESFwU5GgIFGRgBGDEDBQIEAAAAAAEAAAAAA1MCEwAuAAATNjc2NzYXFhcWFxYXFhcWNzY/ATY3NjcVBgcGBwYnJicmJyYnLgEGBwYHBgcGB5YaGyMtIi0mJhgbECA7IR0kIRgVHw0XCxwaJCsmKygjHDIhEh02PBUbGQ8bEAgBSj4nNBsUBwYYDxUOGzQVEQoJGxgiEh4dpTciLRQQCAcZEy4fDhgbBxQYJBcuHA4AAAcAAP/TA9QC6QATACcATQB1AK0AtgC/AAATIg4BFREUHgEzITI+ATURNC4BIwUhMh4BFREUDgEjISIuATURND4BFxQVBgcnBxcGByMVMxYXNSM1Mzc2PwEnNxc3Nj8BNTMVMy4BJzUHFQYHJwcXBgcjFTMWFwcXNxYXFTM1NjcXNyc2PwE1ByYnNycHJic1BzMVFxYfATcXBxcWHwE3FSMHBg8BFwcnBwYPARUjNScmLwEHJzcnJi8BIzUzNzY/ASc3Fzc2PwEXIgYUFjI2NCYHMhYUBiImNDbXNVk1NVk1Ajo1WjQ0WjX9xgI6JT4kJD4l/cYlPiQkPkkNDhkuGAgDIyQDDh8fAgMLBRUPFggPFQgXOwQaBwQODhgvGQgDJCQFBxovGgwPQg0PGS8aCAQiIwMIGC8YChIsFgkUEAgVEBUFCwQCHh4CAwwFFxAWCBAUCRYJExEIFhAXBgoFAh8fAgUKBRYQFgcREwkLFB0dKR0dFQwQEBcQEALpNFk1/m41WTQ0WTUBkjVZNDwkPSX+biU9JCQ9JQGSJT0kMhESBAgZLxgSCkINFjkWCRISCBUQFgULBQEfHgQLAiNAIgQIGS8ZDQ5CDwwZLxoHBSQlAwgZLxkODQFCAQoSGC4YBgUiFR8BBAsFFQ8WBxAUCQEXCRQQBxYQFgUKBQIgIAIDDAQWEBYIEBMJFgkUEAgVEBYFDAMCMh0oHh4oHRYQFxAQFxAAAgAA/9MD1ALpABMAJwAAEyIOARURFB4BMyEyPgE1ETQuASMFITIeARURFA4BIyEiLgE1ETQ+Adc1WTU1WTUCOjVaNDRaNf3GAjolPiQkPiX9xiU+JCQ+Auk0WTX+bjVZNDRZNQGSNVk0PCQ9Jf5uJT0kJD0lAZIlPSQAAAMAAAAAA14CTwAiACYALAAAAQYHDgEPAQYHBhYXBRY+ASc1FhcWPgEnETQmIgcFPAEnLgEHFBUnJRQVJic2AfYJCgUWBUaMRg0GDwE1ChcOAoOYChYOAhMYCP7sAQISJ+UCLGp7ewJOAQYDEAMvXi8KIwfPBgYUC59aZAYGFAsBogwPCLodcRwLDleampqamppJUVEAAAAACQAA/58DvQMgAAcADwAdACsAQQBRAF8AcQCFAAABJicHFyE2NycGByMnNjcWAwYHDgEWNzYXFjYmJyYXJgYWFxYXFj4BJyYnJgUGBwYPAQYHBgcGFjY3Njc+ATc2NCYBJgYXFgYHBh4BNz4BJy4BBSYGFxYXFj4BJyYnLgEBBgcGBwYHBgcOARY3Njc2LgEFIgYWFxYfARYzMjYmJyYvASYnJgLLR5DXUgEKFysMGDDmSD98fHhQLwsBEgxhYw0OBgwrxQwPBAtCOAkYDQY4UwT98AoLBgsLLykJAgMZGQQlLQQWBAgNAq0PDQMDGBkEDxkHHxoHAg38pA8OAwUvBxkPBC0CAQ0CrgkJBgoNCDZWDAESDG5VBwIN/dkLDQMKQ2AGEggPDREOYTsDBgQGAaI0aJz9RIgoSpLcLVpaAVoCDwYZEgQYFAEUGAQLTwETGAUvZgoFFQtrOwMLAQgECwsuSw8LDxEPD0QvBREFCBMO/sgBGw8yZC0MEwEKNn0+BwkSARoPaFwKARMMW2cIC/6zAQYDCQoEJBcGGBIDGUgHEw4BEhcFORoCBRkYARgxAwUCBAAAAAAFAAD/lQO+AyQAGwAxAEkAYABkAAABJgcGBwYHBhYXFhceATc+Ajc2Jy4BJyYnIyYHNhcWFxYXFgYHDgEmJy4CNzY3PgEXJgcGBwYHBhcWFxYXFjY3PgInJicuAQc2FxYXFgcWBgcGBwYmJyYnJjY3Njc2FwYHIQHzbGVhQUILDDc8PVNPt1ZYhE8GBh4cd09QVwMVEGNdWTo8CAk9PkCmtE1QZCAVFzk4ol9bVVEyNQEEJCRBRFJNnj9BRQQeIDwwfEJQSkcrLAEBSD5AS0eMMzYUFRQnJz1KVWBgAYADIgI2NVpdbFy2SUooKAscHXekWVtTU4YoKQUCMQMxMFNWY1isQEM7ECssj7NVWUJETisDMC9PUltTUE42OBAQJTAyjKJJTDIrMDACLCpGSlBLiywvBwkxMzVFQpA7PSIrY6ytAAAUAAD/owO4AyIABAAIAAwAEAAUABgAWwCzAQkBTAGhAf4CSQKLAs8DEgNpA7YEAgRMAAABFREhEQUzESMTFTM1BxUzNQcVMzUHFTM1AzEjByMHIxUjByMPBxUfBDM3MzczNzM3MzczFzMXMxczFzMXMz8FNS8FIycjNSMnIycXIw8FHxk/BDUvAyMnNSc1JyMvASMvATUnIyc1LwEjLwE1JzUnIzUnIycjLwE1LwEjJzUnIy8BBSMPAhUHIw8BFQ8CFQcVBxUPASMPARUPAhUPASMPASMPARUHFQ8BIw8CFR8FMz8ZNS8EITEjFSMPAxUfBDM3MzczFzMXMxczFzMXMx8GMz8FNS8CIy8JIycjJyMnIzUHIw8CIw8BFQcjDwEjBxUHIwcjByMHFQ8DIxUPARUPAiMPAR8FMz8dMz8DLwMFDwUfFhUfBDM/BDUnNS8INSc1LwQjJyMnNS8BIyc1JzUvATUjLwE1JyMnNS8CNS8DBSMPBRUjFQcVBxUjFQcVFxUzFRcVFxUXFR8JMz8ENS8CNSc1JzUnNSc1JzU3NTc1NzU3NTc1LwQFIw8DFQcXFQcVBxUHFQcVDwgVHwMzPwY1NzU/BzU3NTM1NzU3NSc1LwMFIw8DFRcVFxUXFRcVHwcVFxUfAjMVHwMzPwQ1Lws1JzUnNSc1JzUvBAUjDwUVBxUPDxUfBT8CNT8BNTczNzU/AjM/CjU3LwQFDwUfAhUXFRczFzMfAjMfATMVFxUXFRczHwEzFzMXFRcVHwEVHwIVHwIzPwU1LxwFIw8IIwcjByMHIwcjByMPBBUfBTM3MzczNzM3Mz8LMzczPwE1PwE1NzU/BDUvBAUPBRUfBTMfARUXMxczHwEVHwEzFxUfBDMXMx8GMz8FNS8WISMPFxUfBjM/CDM/BDU3MzczNzU/ATU/ATM/BDUvBAFdAS7+6v7+GsrKysrKysplCwYLBQwFBQYmBgoIBAIDAQECBgQJBCsFCQUFBQUKBR4FCgUFBQUKBBgJBQQDBAQBAQQDBAcmBQYFCwYLBvQFBAUDBAQCAggCDAcIBwQDBAMEAwcGBw8CCQIDEQQDBAkJBAQDBAECAwIBAgMCAQISAQkEBgEDBAMBAwgEAwEDAQMBCAQEBAEECAEECv3xBQQIBQQBBAQEDAQEBAgDAQMEAwQDBAkBDwIBAgMDBQIBBAMDAQIDAwgECQUHBQUQBgIGAg8HBgcDBAMEAwQHCAQEBwIBAgIDBAgBBBINCgUHBgICAwgIBAEIBCYECAMEBAQHBAgDGgMIBwcDCQUEBAQDBAEEAwcBAwUEBAQEBAkEJgQJBQQEBQ2tBAgGBwEDCAMBFQMBAwMBCQEMAQkJAwMCAQIDAwgEAQQCAgIDAwgEBQQJAwgDAgQDAgMCAwIDAgMDAwIDAwMDAwMDAwYDIwIDBAQCAgUECAGEBQgEAwMDAQYDAwIDAgMCAwIDBxYBBAEEAQYDAgIEBAMEBQkEBAQDBAECAgIBAgECAQICAgEBAQgBAQECAgQBAgcDAgECAwIBAgYDCQMDAwT9wwUECAMDAgIBAgEBAQEBAQIBCAIBAQEDAwQEBAUJBAMDBAECBQMCAQEBAQEBAgEBAgMDCQLuBQQIBgIBAQEBAQIIAgECAgQBFgEBBAMMBQkEBgUEAwoCCAECAQICAgcBAQEBAQQDBAj8pwUICAQCAQEBAgYCAgIBBAEGAgoDBAEDBgQFCQQEBAMEAgESAQIBAgECAgIHAgEBAQECBwMFAvAEBQQEAwQCAgcCAwUCBAECAwwKAwQCBAEBBAMEBAkIBQcDDwIBBgUIAQEBBgECAQIBAgICCQECAgMECP2tBAgEAwICAgUGDAYBDAEGBwMBAwMBBwcDAQcHAQMBAwQIBAQMBAQEBAkEBAQFAgEBBAQDFQMECgcZAwoCAwMDAwMDAwIGAwIDAgMCCAGcBQQEAQcKHAsHEgQLBAcEBAcECAQQCAQDAgIBBAQDBAUMBQ0EBQQFCAUmBAkEBAQEBAQBBAMBAwEDBAQIBAkEAgIBAgYDBAX+RAQFBAYCAgECAwMFBAEEDQkBDQEEBQUEAQQFBQoFGQEEARQGBQUFCwoJBQQEAwQBAgMDBAcTBQQFBQQFBQQlHggEBAkMAw0CEgUEBQMBFAQECQgWLAUFBAUFBQQFGwQGBAEBAgMDBAQECQMGBQsFBQUGFAEeBQoFBQQBBAEEBRwNBAEEBQMCAQICAwQIAhwM/pABfBj+tAEnGRlKGBhVGBhNGBgCLwEBAQEHAgIEAwQEBAkEBQYDAgkCAQEBAQEBAgUBAgMDCAQFBAkDAwMHAQEBAVMBAgIEBwkJCgEJBwYHAwQDBAMEBwgHFAQNBAQgAwMCAgICAwMICQUGBQUEAQQBBAUcDQQBCAQBBAQECAEDAQMBAwQIAwEDBAMBBgQHCAEEBAEDBAMBAwwDAQMBAwEIBAQEAQQEBAEEDRcFBQQBBAEJBQoFDAQFBAQDBAECBQcMHggECQQUBwgHBAMEAwQDBwYEAwcFBAQJBAQDBAECAgUMBQkEAwYCAQEBAQECAgcCAgMCAgIBAgMDCAUJCAMFAgECAQIBAgICCQIBAQEnAgMFAgQBAg8DAgECCQwJAQkEAwMBAwMBAwsHBwkJBAMEBAEBBAMMAwQGAwMCAwMDAwMDAgMDAgMCAwIDAgMEAxYDAwgJCQcDBDsBAgMDBA0FCwIDAwMDAwMCAwMKIwMHAwcEDgsDBwMIAwMCAQEDAgQICQQCBAkEBAQEBAUDAQMBAwQBAxAEAwEDCAMBCgEDAwEDAwEDAwEGBAkBAwIDA6kBBAQDBAgEBAkFBAkNBBsFDQkEBAkEAQQiBAUBBAQDAwIBAgIDBAgJBQQSBAsEBwQEBwQIBB4ECAQHBAQHBAQJBAQEAwQtAQQIBAQEBB4FCgUFBQUJBScFBAUJCgQyBAUECAQGAgIGCAoFGQEEARQGBQUFCwYmBQYFCwYLBhYGCQgDAwQQAgYICAkGEQUGBQYLBSEGCwUFCgYPAQQBGQUKAQQGAgEBAwIECAkIASkEBQUEBQUJBSIFCQUFBQUKBRcFBAcCAjkBAgMDCAcEBwQaAwsLBwYEAwcUDwMHAQgFBAUIAwMCAgICBwEDFQEDCwEHEAQEDAQEBAQEBAkEJgUFCAQEAwSgAQQDAwQJCQkGAQwBBgwGBQMDAgEEAQQBAgUEAgIBAQEEAQEBBgEBAQIBAQIDBwUEBQQIAwMJAgEGAxADBgMCAwIDAgMCAwYCAwMDAwMHSgECAQMGDAMDBQMCAQEBBAMDBAkFBAgDAwIBAQEBAgkCAgIBAgECAQEBAgICAQEBBAEBAQUEAwUEBQgIAwIBJwEBAwYECQUEBAQCBQMECQEGCQMCAQIDAgECAwQDCgIIAQIBAgIDAQIDAwgFBAkEAwMDBAIBAgECAQIBEBAGAgMFCQMKAQICAg8CAwUGDBQBAgECAQIBAgYCBgkEBQQEBAMDAgEBAQICAgECAQgMAwQDAgECAwIBAhIBCQQDBQQEBAUJBAMDBAAAAAADAAAAAAO5Ao4AAwAHAAsAABMRIREHESERIzMRIy8DijH9YoxeXgKO/Y8CcTD96wIV/esAIAAA/+4DrALPAAQACQAOABMAGAAhAC0ANgBBAE4AVQBaAF8AZABpAG4AcwB4AH0AiACOAJcAoQCmAKsAsAC1ALoAvwDEAMkAzgAAATIzNSMXMjM1IxcyMzUjFzIzNSMXMjM1IxcyMzYXNyYrASEiBxc2MjYnJjY1JgUWFzY3NjcmJwUGBxYXFhc2NycmBQ4BFxYGFjIzMjc0JwUGFTMmNycFMjM1IwUyMzUjBTIzNSMFMjM1IwUyMzUjBTIzNSMFMjM1IwUyMzUjBQYHFhcWFzY3JyYFFhc3JicFBgcWHwE2NycFFhc3NjcmJwcGFzIzNSMXMjM1IxcyMzUjFzIzNSMXMjM1IxcyMzUjBzQ1IRETMjMRITcUFTM1AQUZGDFiGRgxYRkYMWIZGDFiGRgxYQQHGAsGCxUU/e0NDAkDDgYDAQIBAmISDQUKEQUUGP0zGBMFCxAHEA4KCgLxCwMEAgECCg0SBwr8owcxAQYuAzUZGDH8wxkYMQM9GRgx/MMZGDEDPRkYMfzDGRgxAz0ZGDH8wxkYMQM7BQkGCxEIDwUQFvy/BhEoCwQC3BERBQgDGxce/TAbGQUFAhMQBw5jGRgxYRkYMWIZGDFiGBgwXhkYMWUZGDE1/q4fior+7CXLAp0xMTExMTExMTExAQIwAgMwAQQIBBUEBz0JEAQJDAcXDQMPFgQJDQQSBxASVgEKCwIPBgEeGggYHRMVD5UxNTGOMTYxjjE1MY8xNTFZFg8DBwsDGRsDBQ0cGRsSE0EMBQwZCQcTJyoSBxIUCQYMCRUpMTExMTExMTExMTEirKv+qQE4/uunFxgvAAALAAD/zgO5AvIABQAJAA0AEQAVABkAHwAjACcAKwAvAAAXNTMVMxUzNTMVMzUzFTM1MxUzNTMVMzUzFTM1MzUzFSU1MxUhNTMVJREhEScRIREuMRAxYTFiMWExYjFhMA0x/HUxAykx/HYDijH81jJZKDExMTExMTExMTExMShZilxcXFyLAg/98TABs/5NAAAEAAD/nAPDAyAAAwAHAA4AFQAAAREzETMRMxEBBxc1MzUjJRUHFTMVNwFuJsMm/lCkpF1dAlNeXqMDIPx8A4T8fAOE/uGjo29pbmwBam+jAAYAAP+eA5QDHgAGAAkADQARABUAGQAAAQUXARcBFyUFFycHFzcPARc3DwEXNw8BFzcDlP5mQP4aGQHmPgED/mWXlT8ZP5c+GT6ePxk/lyYZJgMebV/+uSYBSFwFbeBYKyUqLyomKzUrJSswGiUaAAACAAAAAAO5AmYAAwAHAAATESERAREhES8DivylAyoCZv3wAhD+HQGz/k0AAAkAAP+nA7IDHgADAAcACwARABUAGQAfACUAKQAAARUzNQUVMzUzFTM1FxUzFTM1BRUzNQUVMzUHFSMVMzUFFTM1IzUXFTM1AS9n/qHaoduCeyj9fWcB9Cgod5/9np934FIDHtra9mdnZ2ceKHWdaNran01NvnQonAGdKHV1KCgACwAA/84DuQLyAAUACQANABEAFQAZAB8AIwAnACsALwAAExUzNTM1MxUzNTMVMzUzFTM1MxUzNTMVMzUzFTMVMzUFFTM1IRUzNQURIREHESERLjEQMWExYjFhMWIxYTANMfx1MQMpMfx2A4ox/NYC8lkpMDAwMDAwMDAwMDAwKVmJXFxcXIv98AIQMP5NAbMADAAA//oDuQLCAAQAGAAcACAAJAAoACwAMAA0ADgAPABAAAATFREhEQUhFSMVMxUjFTMVITUzNSM1MzUjMxUzNTMVMzUzFTM1MxUzNTMVMzUFFTM1MxUzNTMVMzUzFTM1MxUzNS8DivylAyoMDAwM/NYRERERQmIxYTFiMWExYv1UYjFhMWIxYTFiAsLY/hACyCysMbYxqakxtjExMTExMTExMTEx5zExMTExMTExMTEAAAAABwAA//oDuQLCAAQAEAAUABgAHAAgACQAABMZASERBSERIxUzESERMzUjMxUzNTMVMzUzFTM1MxUzNTMVMzUvA4r8pQMqDAz81hERQmIxYTFiMWExYgLC/rT+hALILP7gMP7jAR0wMDAwMDAwMDAwMAAAAAAFAAD/0gMjAuoABQALAA4AFgAdAAABIiMRIREnFTMRIREFFyMnHQEjFTMVNycXBzUjNTMCctbXAl7luP38AXl9feNqao15XFxqagLq/OgCYom6/fwCvgyBbBhFUFyEVlZWQigABAAA/9IDIwLqAAUACwAOABUAAAEiIxEhEScVMxEhEQUXIycVIxUzFTcCctbXAl7luP38AXl9feNqao0C6vzoAmKJuv38Ar4MgWxdUFyEAAACAAD/1gNyAu8AbwDkAAABIgYHBgcGHwEVJi8BMScmJyYnJicmBxUGBwYXFhcWFxYfAScmJyYnJgcGBzkBBhcWFxYXFhcWFxYfASE3Nj8BNj8BNjc2NzYnLgEnJgYHBgcGDwE1NDU2JyYnLgEiBgcGBwYPAi8BJicmJy4BBzMHMjEzMhYXHgEfARYfAT8CNjc2Nz4BOwEyFh8BFhcWBxUfATY3Njc2NzYXOQEeARcWBwYHBg8BBgcGDwEhJicmJyYnJicmJyY+AhcWFxYfATcnJicmJyYnJjc+ATc2FhcWFxYXMRYfAT8BNi8BJjc2Nz4BAfIOGAcLAwMBAQYIAQkMBwsLERMXHRkHAwcFEAwFCA0GDRAJJyQWExcQHQUCGQ4kLRccOScQBQFMAwQKDBQbCxgKEAcKBgQYEQ4cChIPBgkGAQIECAYVGRYHDQcGBwYJBgMFBwcMBxcNAQIBAQUFBAcNBwIGCAghGgYHBQcHBAUFAgQCAgEGAgIBASAKEhAIDg0KDAkIAgQJBg0IFBMYGQUJCP7fDiE6HxguIwwTAgEGERYPHyIcHRcdDRAOBg0PBAYDAQUGDRUMCwwIDwoUESABAgEBAgMDBwQGAu4ODBQhHD8oQw0XAxcgDxkQGQkLCwELHhMjFjEmExsyFwwPCCIOCAECDhseGCARJS4cIVg7FQcMDS43XkccOxwtHywaDxcDAwsLESMKGBBLESU2FyIRCw8LCREbFjEkLzMgQh4jEAwOASEEBgw5Rxk4OCwBlSMxFBcJBQIBAwMLHBlAJ6AHFS8qEh4NCgEBBwkTIxgmFjMzPG4XJyUSNFklHS4lDxgOCAwQAQUNHhcgGw82PzIWKS0SGg8HBwMGChIQHhIoGjArBjI7Kh5GHBkOBgQAAAAACAAA/9sDbQLgABQAGAAqAC4AMgBMAF8AYwAAASIGBwYHFBYHFTM1Jjc+ARczNSMmBTM1Ixc2FxYHFTM0NTQnLgInJgcjATM1IwUzNSMFFBYXFhcWNzYXNhcWMzY3NSIjBicuATc1IwUWDgErARUyMxY+Ajc2NTQ1IwUzNSMBAitKCwQCAQEyAQIENiDYSmQBCi8vizscHwQyAQIkOR8JEwn9tDIyAr4yMv1CLSMUGxAhGQwECwkEBQIQHzcbHycBMgK+ATFFRyMUKT09NiIBATL+li4uAt85KxIXDjcORjBQKCItAjEBMjExBB0eQJgbOEklIDkkAQIB/kcxSzHUJ0cRCgICAQEBAQEBAQQtAQMHNSFXWSM4AjEBBSU5IA4dFQvNMQAAAAAEAAD/8AOsAs0AEwAnADcARwAAEyIOARURFB4BMyEyPgE1ETQuASMFITIeARURFA4BIyEiLgE1ETQ+ARciBhURFBYzITI2NRE0JiMFITIWFREUBiMhIiY1ETQ20ilEKChEKQJFKEUoKEUo/bsCRR80Hx80H/27HzUfHzU8Jjc3JgILJjY2Jv31AgsYIiIY/fUZIiICzChEKf5OKEUoKEUoAbIpRCgiHzUf/k4fNR8fNR8Bsh81HzE2Jv6CJjY2JgF+JjYiIhj+ghgiIhgBfhgiAAAAAAAAEgDeAAEAAAAAAAAAFQAAAAEAAAAAAAEABAAVAAEAAAAAAAIABwAZAAEAAAAAAAMABAAgAAEAAAAAAAQABAAkAAEAAAAAAAUACwAoAAEAAAAAAAYABAAzAAEAAAAAAAoAKwA3AAEAAAAAAAsAEwBiAAMAAQQJAAAAKgB1AAMAAQQJAAEACACfAAMAAQQJAAIADgCnAAMAAQQJAAMACAC1AAMAAQQJAAQACAC9AAMAAQQJAAUAFgDFAAMAAQQJAAYACADbAAMAAQQJAAoAVgDjAAMAAQQJAAsAJgE5Y2FtdW5kYSBTZXJ2aWNlcyBHbWJIYnBtblJlZ3VsYXJicG1uYnBtblZlcnNpb24gMS4wYnBtbkdlbmVyYXRlZCBieSBzdmcydHRmIGZyb20gRm9udGVsbG8gcHJvamVjdC5odHRwOi8vZm9udGVsbG8uY29tAGMAYQBtAHUAbgBkAGEAIABTAGUAcgB2AGkAYwBlAHMAIABHAG0AYgBIAGIAcABtAG4AUgBlAGcAdQBsAGEAcgBiAHAAbQBuAGIAcABtAG4AVgBlAHIAcwBpAG8AbgAgADEALgAwAGIAcABtAG4ARwBlAG4AZQByAGEAdABlAGQAIABiAHkAIABzAHYAZwAyAHQAdABmACAAZgByAG8AbQAgAEYAbwBuAHQAZQBsAGwAbwAgAHAAcgBvAGoAZQBjAHQALgBoAHQAdABwADoALwAvAGYAbwBuAHQAZQBsAGwAbwAuAGMAbwBtAAAAAAIAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbAECAQMBBAEFAQYBBwEIAQkBCgELAQwBDQEOAQ8BEAERARIBEwEUARUBFgEXARgBGQEaARsBHAEdAR4BHwEgASEBIgEjASQBJQEmAScBKAEpASoBKwEsAS0BLgEvATABMQEyATMBNAE1ATYBNwE4ATkBOgE7ATwBPQE+AT8BQAFBAUIBQwFEAUUBRgFHAUgBSQFKAUsBTAFNAU4BTwFQAVEBUgFTAVQBVQFWAVcBWAFZAVoBWwFcAV0BXgFfAWABYQFiAWMBZAFlAWYBZwFoAWkBagFrAWwBbQAMc2NyZXctd3JlbmNoBXRyYXNoEGNvbmRpdGlvbmFsLWZsb3cMZGVmYXVsdC1mbG93EGdhdGV3YXktcGFyYWxsZWwfaW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLWNhbmNlbDFpbnRlcm1lZGlhdGUtZXZlbnQtY2F0Y2gtbm9uLWludGVycnVwdGluZy1tZXNzYWdlGHN0YXJ0LWV2ZW50LWNvbXBlbnNhdGlvbi5zdGFydC1ldmVudC1ub24taW50ZXJydXB0aW5nLXBhcmFsbGVsLW11bHRpcGxlC2xvb3AtbWFya2VyEnBhcmFsbGVsLW1pLW1hcmtlciNzdGFydC1ldmVudC1ub24taW50ZXJydXB0aW5nLXNpZ25hbC9pbnRlcm1lZGlhdGUtZXZlbnQtY2F0Y2gtbm9uLWludGVycnVwdGluZy10aW1lcippbnRlcm1lZGlhdGUtZXZlbnQtY2F0Y2gtcGFyYWxsZWwtbXVsdGlwbGUlaW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLWNvbXBlbnNhdGlvbgtnYXRld2F5LXhvcgpjb25uZWN0aW9uEGVuZC1ldmVudC1jYW5jZWwiaW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLWNvbmRpdGlvbjtpbnRlcm1lZGlhdGUtZXZlbnQtY2F0Y2gtbm9uLWludGVycnVwdGluZy1wYXJhbGxlbC1tdWx0aXBsZRVzdGFydC1ldmVudC1jb25kaXRpb24ic3RhcnQtZXZlbnQtbm9uLWludGVycnVwdGluZy10aW1lchRzZXF1ZW50aWFsLW1pLW1hcmtlcgl1c2VyLXRhc2sNYnVzaW5lc3MtcnVsZRJzdWItcHJvY2Vzcy1tYXJrZXIdc3RhcnQtZXZlbnQtcGFyYWxsZWwtbXVsdGlwbGURc3RhcnQtZXZlbnQtZXJyb3IfaW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLXNpZ25hbB5pbnRlcm1lZGlhdGUtZXZlbnQtY2F0Y2gtZXJyb3IWZW5kLWV2ZW50LWNvbXBlbnNhdGlvbhRzdWJwcm9jZXNzLWNvbGxhcHNlZBNzdWJwcm9jZXNzLWV4cGFuZGVkBHRhc2sPZW5kLWV2ZW50LWVycm9yI2ludGVybWVkaWF0ZS1ldmVudC1jYXRjaC1lc2NhbGF0aW9uHmludGVybWVkaWF0ZS1ldmVudC1jYXRjaC10aW1lchZzdGFydC1ldmVudC1lc2NhbGF0aW9uEnN0YXJ0LWV2ZW50LXNpZ25hbBJidXNpbmVzcy1ydWxlLXRhc2sGbWFudWFsB3JlY2VpdmUNY2FsbC1hY3Rpdml0eRFzdGFydC1ldmVudC10aW1lchNzdGFydC1ldmVudC1tZXNzYWdlF2ludGVybWVkaWF0ZS1ldmVudC1ub25lHWludGVybWVkaWF0ZS1ldmVudC1jYXRjaC1saW5rFGVuZC1ldmVudC1lc2NhbGF0aW9uD3RleHQtYW5ub3RhdGlvbgdicG1uLWlvD2dhdGV3YXktY29tcGxleBJnYXRld2F5LWV2ZW50YmFzZWQMZ2F0ZXdheS1ub25lCmdhdGV3YXktb3ITZW5kLWV2ZW50LXRlcm1pbmF0ZRBlbmQtZXZlbnQtc2lnbmFsDmVuZC1ldmVudC1ub25lEmVuZC1ldmVudC1tdWx0aXBsZRFlbmQtZXZlbnQtbWVzc2FnZQ5lbmQtZXZlbnQtbGluayBpbnRlcm1lZGlhdGUtZXZlbnQtY2F0Y2gtbWVzc2FnZSVpbnRlcm1lZGlhdGUtZXZlbnQtdGhyb3ctY29tcGVuc2F0aW9uFHN0YXJ0LWV2ZW50LW11bHRpcGxlBnNjcmlwdAttYW51YWwtdGFzawRzZW5kB3NlcnZpY2UMcmVjZWl2ZS10YXNrBHVzZXIQc3RhcnQtZXZlbnQtbm9uZSNpbnRlcm1lZGlhdGUtZXZlbnQtdGhyb3ctZXNjYWxhdGlvbiFpbnRlcm1lZGlhdGUtZXZlbnQtY2F0Y2gtbXVsdGlwbGU0aW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLW5vbi1pbnRlcnJ1cHRpbmctZXNjYWxhdGlvbh1pbnRlcm1lZGlhdGUtZXZlbnQtdGhyb3ctbGluayZzdGFydC1ldmVudC1ub24taW50ZXJydXB0aW5nLWNvbmRpdGlvbgtkYXRhLW9iamVjdAtzY3JpcHQtdGFzawlzZW5kLXRhc2sKZGF0YS1zdG9yZSdzdGFydC1ldmVudC1ub24taW50ZXJydXB0aW5nLWVzY2FsYXRpb24gaW50ZXJtZWRpYXRlLWV2ZW50LXRocm93LW1lc3NhZ2UyaW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLW5vbi1pbnRlcnJ1cHRpbmctbXVsdGlwbGUwaW50ZXJtZWRpYXRlLWV2ZW50LWNhdGNoLW5vbi1pbnRlcnJ1cHRpbmctc2lnbmFsIWludGVybWVkaWF0ZS1ldmVudC10aHJvdy1tdWx0aXBsZSRzdGFydC1ldmVudC1ub24taW50ZXJydXB0aW5nLW1lc3NhZ2UNYWQtaG9jLW1hcmtlcgxzZXJ2aWNlLXRhc2sJdGFzay1ub25lE2NvbXBlbnNhdGlvbi1tYXJrZXIlc3RhcnQtZXZlbnQtbm9uLWludGVycnVwdGluZy1tdWx0aXBsZR9pbnRlcm1lZGlhdGUtZXZlbnQtdGhyb3ctc2lnbmFsM2ludGVybWVkaWF0ZS1ldmVudC1jYXRjaC1ub24taW50ZXJydXB0aW5nLWNvbmRpdGlvbgtwYXJ0aWNpcGFudBlldmVudC1zdWJwcm9jZXNzLWV4cGFuZGVkEWxhbmUtaW5zZXJ0LWJlbG93CnNwYWNlLXRvb2wQY29ubmVjdGlvbi1tdWx0aQRsYW5lCmxhc3NvLXRvb2wRbGFuZS1pbnNlcnQtYWJvdmURbGFuZS1kaXZpZGUtdGhyZWUPbGFuZS1kaXZpZGUtdHdvCmRhdGEtaW5wdXQLZGF0YS1vdXRwdXQJaGFuZC10b29sBWdyb3VwC3RyYW5zYWN0aW9uAAA=') format('truetype'); +} +/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ +/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ +/* +@media screen and (-webkit-min-device-pixel-ratio:0) { + @font-face { + font-family: 'bpmn'; + src: url('../font/bpmn.svg?68866489#bpmn') format('svg'); + } +} +*/ + + [class^="bpmn-icon-"]:before, [class*=" bpmn-icon-"]:before { + font-family: "bpmn"; + font-style: normal; + font-weight: normal; + speak: never; + + display: inline-block; + text-decoration: inherit; + width: 1em; + /* margin-right: .2em; */ + text-align: center; + /* opacity: .8; */ + + /* For safety - reset parent styles, that can break glyph codes*/ + font-variant: normal; + text-transform: none; + + /* fix buttons height, for twitter bootstrap */ + line-height: 1em; + + /* Animation center compensation - margins should be symmetric */ + /* remove if not needed */ + /* margin-left: .2em; */ + + /* you can be more comfortable with increased icons size */ + /* font-size: 120%; */ + + /* Uncomment for 3D effect */ + /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ +} +.bpmn-icon-screw-wrench:before { content: '\e800'; } /* '' */ +.bpmn-icon-trash:before { content: '\e801'; } /* '' */ +.bpmn-icon-conditional-flow:before { content: '\e802'; } /* '' */ +.bpmn-icon-default-flow:before { content: '\e803'; } /* '' */ +.bpmn-icon-gateway-parallel:before { content: '\e804'; } /* '' */ +.bpmn-icon-intermediate-event-catch-cancel:before { content: '\e805'; } /* '' */ +.bpmn-icon-intermediate-event-catch-non-interrupting-message:before { content: '\e806'; } /* '' */ +.bpmn-icon-start-event-compensation:before { content: '\e807'; } /* '' */ +.bpmn-icon-start-event-non-interrupting-parallel-multiple:before { content: '\e808'; } /* '' */ +.bpmn-icon-loop-marker:before { content: '\e809'; } /* '' */ +.bpmn-icon-parallel-mi-marker:before { content: '\e80a'; } /* '' */ +.bpmn-icon-start-event-non-interrupting-signal:before { content: '\e80b'; } /* '' */ +.bpmn-icon-intermediate-event-catch-non-interrupting-timer:before { content: '\e80c'; } /* '' */ +.bpmn-icon-intermediate-event-catch-parallel-multiple:before { content: '\e80d'; } /* '' */ +.bpmn-icon-intermediate-event-catch-compensation:before { content: '\e80e'; } /* '' */ +.bpmn-icon-gateway-xor:before { content: '\e80f'; } /* '' */ +.bpmn-icon-connection:before { content: '\e810'; } /* '' */ +.bpmn-icon-end-event-cancel:before { content: '\e811'; } /* '' */ +.bpmn-icon-intermediate-event-catch-condition:before { content: '\e812'; } /* '' */ +.bpmn-icon-intermediate-event-catch-non-interrupting-parallel-multiple:before { content: '\e813'; } /* '' */ +.bpmn-icon-start-event-condition:before { content: '\e814'; } /* '' */ +.bpmn-icon-start-event-non-interrupting-timer:before { content: '\e815'; } /* '' */ +.bpmn-icon-sequential-mi-marker:before { content: '\e816'; } /* '' */ +.bpmn-icon-user-task:before { content: '\e817'; } /* '' */ +.bpmn-icon-business-rule:before { content: '\e818'; } /* '' */ +.bpmn-icon-sub-process-marker:before { content: '\e819'; } /* '' */ +.bpmn-icon-start-event-parallel-multiple:before { content: '\e81a'; } /* '' */ +.bpmn-icon-start-event-error:before { content: '\e81b'; } /* '' */ +.bpmn-icon-intermediate-event-catch-signal:before { content: '\e81c'; } /* '' */ +.bpmn-icon-intermediate-event-catch-error:before { content: '\e81d'; } /* '' */ +.bpmn-icon-end-event-compensation:before { content: '\e81e'; } /* '' */ +.bpmn-icon-subprocess-collapsed:before { content: '\e81f'; } /* '' */ +.bpmn-icon-subprocess-expanded:before { content: '\e820'; } /* '' */ +.bpmn-icon-task:before { content: '\e821'; } /* '' */ +.bpmn-icon-end-event-error:before { content: '\e822'; } /* '' */ +.bpmn-icon-intermediate-event-catch-escalation:before { content: '\e823'; } /* '' */ +.bpmn-icon-intermediate-event-catch-timer:before { content: '\e824'; } /* '' */ +.bpmn-icon-start-event-escalation:before { content: '\e825'; } /* '' */ +.bpmn-icon-start-event-signal:before { content: '\e826'; } /* '' */ +.bpmn-icon-business-rule-task:before { content: '\e827'; } /* '' */ +.bpmn-icon-manual:before { content: '\e828'; } /* '' */ +.bpmn-icon-receive:before { content: '\e829'; } /* '' */ +.bpmn-icon-call-activity:before { content: '\e82a'; } /* '' */ +.bpmn-icon-start-event-timer:before { content: '\e82b'; } /* '' */ +.bpmn-icon-start-event-message:before { content: '\e82c'; } /* '' */ +.bpmn-icon-intermediate-event-none:before { content: '\e82d'; } /* '' */ +.bpmn-icon-intermediate-event-catch-link:before { content: '\e82e'; } /* '' */ +.bpmn-icon-end-event-escalation:before { content: '\e82f'; } /* '' */ +.bpmn-icon-text-annotation:before { content: '\e830'; } /* '' */ +.bpmn-icon-bpmn-io:before { content: '\e831'; } /* '' */ +.bpmn-icon-gateway-complex:before { content: '\e832'; } /* '' */ +.bpmn-icon-gateway-eventbased:before { content: '\e833'; } /* '' */ +.bpmn-icon-gateway-none:before { content: '\e834'; } /* '' */ +.bpmn-icon-gateway-or:before { content: '\e835'; } /* '' */ +.bpmn-icon-end-event-terminate:before { content: '\e836'; } /* '' */ +.bpmn-icon-end-event-signal:before { content: '\e837'; } /* '' */ +.bpmn-icon-end-event-none:before { content: '\e838'; } /* '' */ +.bpmn-icon-end-event-multiple:before { content: '\e839'; } /* '' */ +.bpmn-icon-end-event-message:before { content: '\e83a'; } /* '' */ +.bpmn-icon-end-event-link:before { content: '\e83b'; } /* '' */ +.bpmn-icon-intermediate-event-catch-message:before { content: '\e83c'; } /* '' */ +.bpmn-icon-intermediate-event-throw-compensation:before { content: '\e83d'; } /* '' */ +.bpmn-icon-start-event-multiple:before { content: '\e83e'; } /* '' */ +.bpmn-icon-script:before { content: '\e83f'; } /* '' */ +.bpmn-icon-manual-task:before { content: '\e840'; } /* '' */ +.bpmn-icon-send:before { content: '\e841'; } /* '' */ +.bpmn-icon-service:before { content: '\e842'; } /* '' */ +.bpmn-icon-receive-task:before { content: '\e843'; } /* '' */ +.bpmn-icon-user:before { content: '\e844'; } /* '' */ +.bpmn-icon-start-event-none:before { content: '\e845'; } /* '' */ +.bpmn-icon-intermediate-event-throw-escalation:before { content: '\e846'; } /* '' */ +.bpmn-icon-intermediate-event-catch-multiple:before { content: '\e847'; } /* '' */ +.bpmn-icon-intermediate-event-catch-non-interrupting-escalation:before { content: '\e848'; } /* '' */ +.bpmn-icon-intermediate-event-throw-link:before { content: '\e849'; } /* '' */ +.bpmn-icon-start-event-non-interrupting-condition:before { content: '\e84a'; } /* '' */ +.bpmn-icon-data-object:before { content: '\e84b'; } /* '' */ +.bpmn-icon-script-task:before { content: '\e84c'; } /* '' */ +.bpmn-icon-send-task:before { content: '\e84d'; } /* '' */ +.bpmn-icon-data-store:before { content: '\e84e'; } /* '' */ +.bpmn-icon-start-event-non-interrupting-escalation:before { content: '\e84f'; } /* '' */ +.bpmn-icon-intermediate-event-throw-message:before { content: '\e850'; } /* '' */ +.bpmn-icon-intermediate-event-catch-non-interrupting-multiple:before { content: '\e851'; } /* '' */ +.bpmn-icon-intermediate-event-catch-non-interrupting-signal:before { content: '\e852'; } /* '' */ +.bpmn-icon-intermediate-event-throw-multiple:before { content: '\e853'; } /* '' */ +.bpmn-icon-start-event-non-interrupting-message:before { content: '\e854'; } /* '' */ +.bpmn-icon-ad-hoc-marker:before { content: '\e855'; } /* '' */ +.bpmn-icon-service-task:before { content: '\e856'; } /* '' */ +.bpmn-icon-task-none:before { content: '\e857'; } /* '' */ +.bpmn-icon-compensation-marker:before { content: '\e858'; } /* '' */ +.bpmn-icon-start-event-non-interrupting-multiple:before { content: '\e859'; } /* '' */ +.bpmn-icon-intermediate-event-throw-signal:before { content: '\e85a'; } /* '' */ +.bpmn-icon-intermediate-event-catch-non-interrupting-condition:before { content: '\e85b'; } /* '' */ +.bpmn-icon-participant:before { content: '\e85c'; } /* '' */ +.bpmn-icon-event-subprocess-expanded:before { content: '\e85d'; } /* '' */ +.bpmn-icon-lane-insert-below:before { content: '\e85e'; } /* '' */ +.bpmn-icon-space-tool:before { content: '\e85f'; } /* '' */ +.bpmn-icon-connection-multi:before { content: '\e860'; } /* '' */ +.bpmn-icon-lane:before { content: '\e861'; } /* '' */ +.bpmn-icon-lasso-tool:before { content: '\e862'; } /* '' */ +.bpmn-icon-lane-insert-above:before { content: '\e863'; } /* '' */ +.bpmn-icon-lane-divide-three:before { content: '\e864'; } /* '' */ +.bpmn-icon-lane-divide-two:before { content: '\e865'; } /* '' */ +.bpmn-icon-data-input:before { content: '\e866'; } /* '' */ +.bpmn-icon-data-output:before { content: '\e867'; } /* '' */ +.bpmn-icon-hand-tool:before { content: '\e868'; } /* '' */ +.bpmn-icon-group:before { content: '\e869'; } /* '' */ +.bpmn-icon-transaction:before { content: '\e8c4'; } /* '' */ \ No newline at end of file diff --git a/dist/assets/bpmn-font/css/bpmn.css b/dist/assets/bpmn-font/css/bpmn.css new file mode 100644 index 0000000..59234d4 --- /dev/null +++ b/dist/assets/bpmn-font/css/bpmn.css @@ -0,0 +1,164 @@ +@font-face { + font-family: 'bpmn'; + src: url('../font/bpmn.eot?26374340'); + src: url('../font/bpmn.eot?26374340#iefix') format('embedded-opentype'), + url('../font/bpmn.woff2?26374340') format('woff2'), + url('../font/bpmn.woff?26374340') format('woff'), + url('../font/bpmn.ttf?26374340') format('truetype'), + url('../font/bpmn.svg?26374340#bpmn') format('svg'); + font-weight: normal; + font-style: normal; +} +/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ +/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ +/* +@media screen and (-webkit-min-device-pixel-ratio:0) { + @font-face { + font-family: 'bpmn'; + src: url('../font/bpmn.svg?26374340#bpmn') format('svg'); + } +} +*/ + + [class^="bpmn-icon-"]:before, [class*=" bpmn-icon-"]:before { + font-family: "bpmn"; + font-style: normal; + font-weight: normal; + speak: never; + + display: inline-block; + text-decoration: inherit; + width: 1em; + /* margin-right: .2em; */ + text-align: center; + /* opacity: .8; */ + + /* For safety - reset parent styles, that can break glyph codes*/ + font-variant: normal; + text-transform: none; + + /* fix buttons height, for twitter bootstrap */ + line-height: 1em; + + /* Animation center compensation - margins should be symmetric */ + /* remove if not needed */ + /* margin-left: .2em; */ + + /* you can be more comfortable with increased icons size */ + /* font-size: 120%; */ + + /* Font smoothing. That was taken from TWBS */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + /* Uncomment for 3D effect */ + /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ +} + +.bpmn-icon-screw-wrench:before { content: '\e800'; } /* '' */ +.bpmn-icon-trash:before { content: '\e801'; } /* '' */ +.bpmn-icon-conditional-flow:before { content: '\e802'; } /* '' */ +.bpmn-icon-default-flow:before { content: '\e803'; } /* '' */ +.bpmn-icon-gateway-parallel:before { content: '\e804'; } /* '' */ +.bpmn-icon-intermediate-event-catch-cancel:before { content: '\e805'; } /* '' */ +.bpmn-icon-intermediate-event-catch-non-interrupting-message:before { content: '\e806'; } /* '' */ +.bpmn-icon-start-event-compensation:before { content: '\e807'; } /* '' */ +.bpmn-icon-start-event-non-interrupting-parallel-multiple:before { content: '\e808'; } /* '' */ +.bpmn-icon-loop-marker:before { content: '\e809'; } /* '' */ +.bpmn-icon-parallel-mi-marker:before { content: '\e80a'; } /* '' */ +.bpmn-icon-start-event-non-interrupting-signal:before { content: '\e80b'; } /* '' */ +.bpmn-icon-intermediate-event-catch-non-interrupting-timer:before { content: '\e80c'; } /* '' */ +.bpmn-icon-intermediate-event-catch-parallel-multiple:before { content: '\e80d'; } /* '' */ +.bpmn-icon-intermediate-event-catch-compensation:before { content: '\e80e'; } /* '' */ +.bpmn-icon-gateway-xor:before { content: '\e80f'; } /* '' */ +.bpmn-icon-connection:before { content: '\e810'; } /* '' */ +.bpmn-icon-end-event-cancel:before { content: '\e811'; } /* '' */ +.bpmn-icon-intermediate-event-catch-condition:before { content: '\e812'; } /* '' */ +.bpmn-icon-intermediate-event-catch-non-interrupting-parallel-multiple:before { content: '\e813'; } /* '' */ +.bpmn-icon-start-event-condition:before { content: '\e814'; } /* '' */ +.bpmn-icon-start-event-non-interrupting-timer:before { content: '\e815'; } /* '' */ +.bpmn-icon-sequential-mi-marker:before { content: '\e816'; } /* '' */ +.bpmn-icon-user-task:before { content: '\e817'; } /* '' */ +.bpmn-icon-business-rule:before { content: '\e818'; } /* '' */ +.bpmn-icon-sub-process-marker:before { content: '\e819'; } /* '' */ +.bpmn-icon-start-event-parallel-multiple:before { content: '\e81a'; } /* '' */ +.bpmn-icon-start-event-error:before { content: '\e81b'; } /* '' */ +.bpmn-icon-intermediate-event-catch-signal:before { content: '\e81c'; } /* '' */ +.bpmn-icon-intermediate-event-catch-error:before { content: '\e81d'; } /* '' */ +.bpmn-icon-end-event-compensation:before { content: '\e81e'; } /* '' */ +.bpmn-icon-subprocess-collapsed:before { content: '\e81f'; } /* '' */ +.bpmn-icon-subprocess-expanded:before { content: '\e820'; } /* '' */ +.bpmn-icon-task:before { content: '\e821'; } /* '' */ +.bpmn-icon-end-event-error:before { content: '\e822'; } /* '' */ +.bpmn-icon-intermediate-event-catch-escalation:before { content: '\e823'; } /* '' */ +.bpmn-icon-intermediate-event-catch-timer:before { content: '\e824'; } /* '' */ +.bpmn-icon-start-event-escalation:before { content: '\e825'; } /* '' */ +.bpmn-icon-start-event-signal:before { content: '\e826'; } /* '' */ +.bpmn-icon-business-rule-task:before { content: '\e827'; } /* '' */ +.bpmn-icon-manual:before { content: '\e828'; } /* '' */ +.bpmn-icon-receive:before { content: '\e829'; } /* '' */ +.bpmn-icon-call-activity:before { content: '\e82a'; } /* '' */ +.bpmn-icon-start-event-timer:before { content: '\e82b'; } /* '' */ +.bpmn-icon-start-event-message:before { content: '\e82c'; } /* '' */ +.bpmn-icon-intermediate-event-none:before { content: '\e82d'; } /* '' */ +.bpmn-icon-intermediate-event-catch-link:before { content: '\e82e'; } /* '' */ +.bpmn-icon-end-event-escalation:before { content: '\e82f'; } /* '' */ +.bpmn-icon-text-annotation:before { content: '\e830'; } /* '' */ +.bpmn-icon-bpmn-io:before { content: '\e831'; } /* '' */ +.bpmn-icon-gateway-complex:before { content: '\e832'; } /* '' */ +.bpmn-icon-gateway-eventbased:before { content: '\e833'; } /* '' */ +.bpmn-icon-gateway-none:before { content: '\e834'; } /* '' */ +.bpmn-icon-gateway-or:before { content: '\e835'; } /* '' */ +.bpmn-icon-end-event-terminate:before { content: '\e836'; } /* '' */ +.bpmn-icon-end-event-signal:before { content: '\e837'; } /* '' */ +.bpmn-icon-end-event-none:before { content: '\e838'; } /* '' */ +.bpmn-icon-end-event-multiple:before { content: '\e839'; } /* '' */ +.bpmn-icon-end-event-message:before { content: '\e83a'; } /* '' */ +.bpmn-icon-end-event-link:before { content: '\e83b'; } /* '' */ +.bpmn-icon-intermediate-event-catch-message:before { content: '\e83c'; } /* '' */ +.bpmn-icon-intermediate-event-throw-compensation:before { content: '\e83d'; } /* '' */ +.bpmn-icon-start-event-multiple:before { content: '\e83e'; } /* '' */ +.bpmn-icon-script:before { content: '\e83f'; } /* '' */ +.bpmn-icon-manual-task:before { content: '\e840'; } /* '' */ +.bpmn-icon-send:before { content: '\e841'; } /* '' */ +.bpmn-icon-service:before { content: '\e842'; } /* '' */ +.bpmn-icon-receive-task:before { content: '\e843'; } /* '' */ +.bpmn-icon-user:before { content: '\e844'; } /* '' */ +.bpmn-icon-start-event-none:before { content: '\e845'; } /* '' */ +.bpmn-icon-intermediate-event-throw-escalation:before { content: '\e846'; } /* '' */ +.bpmn-icon-intermediate-event-catch-multiple:before { content: '\e847'; } /* '' */ +.bpmn-icon-intermediate-event-catch-non-interrupting-escalation:before { content: '\e848'; } /* '' */ +.bpmn-icon-intermediate-event-throw-link:before { content: '\e849'; } /* '' */ +.bpmn-icon-start-event-non-interrupting-condition:before { content: '\e84a'; } /* '' */ +.bpmn-icon-data-object:before { content: '\e84b'; } /* '' */ +.bpmn-icon-script-task:before { content: '\e84c'; } /* '' */ +.bpmn-icon-send-task:before { content: '\e84d'; } /* '' */ +.bpmn-icon-data-store:before { content: '\e84e'; } /* '' */ +.bpmn-icon-start-event-non-interrupting-escalation:before { content: '\e84f'; } /* '' */ +.bpmn-icon-intermediate-event-throw-message:before { content: '\e850'; } /* '' */ +.bpmn-icon-intermediate-event-catch-non-interrupting-multiple:before { content: '\e851'; } /* '' */ +.bpmn-icon-intermediate-event-catch-non-interrupting-signal:before { content: '\e852'; } /* '' */ +.bpmn-icon-intermediate-event-throw-multiple:before { content: '\e853'; } /* '' */ +.bpmn-icon-start-event-non-interrupting-message:before { content: '\e854'; } /* '' */ +.bpmn-icon-ad-hoc-marker:before { content: '\e855'; } /* '' */ +.bpmn-icon-service-task:before { content: '\e856'; } /* '' */ +.bpmn-icon-task-none:before { content: '\e857'; } /* '' */ +.bpmn-icon-compensation-marker:before { content: '\e858'; } /* '' */ +.bpmn-icon-start-event-non-interrupting-multiple:before { content: '\e859'; } /* '' */ +.bpmn-icon-intermediate-event-throw-signal:before { content: '\e85a'; } /* '' */ +.bpmn-icon-intermediate-event-catch-non-interrupting-condition:before { content: '\e85b'; } /* '' */ +.bpmn-icon-participant:before { content: '\e85c'; } /* '' */ +.bpmn-icon-event-subprocess-expanded:before { content: '\e85d'; } /* '' */ +.bpmn-icon-lane-insert-below:before { content: '\e85e'; } /* '' */ +.bpmn-icon-space-tool:before { content: '\e85f'; } /* '' */ +.bpmn-icon-connection-multi:before { content: '\e860'; } /* '' */ +.bpmn-icon-lane:before { content: '\e861'; } /* '' */ +.bpmn-icon-lasso-tool:before { content: '\e862'; } /* '' */ +.bpmn-icon-lane-insert-above:before { content: '\e863'; } /* '' */ +.bpmn-icon-lane-divide-three:before { content: '\e864'; } /* '' */ +.bpmn-icon-lane-divide-two:before { content: '\e865'; } /* '' */ +.bpmn-icon-data-input:before { content: '\e866'; } /* '' */ +.bpmn-icon-data-output:before { content: '\e867'; } /* '' */ +.bpmn-icon-hand-tool:before { content: '\e868'; } /* '' */ +.bpmn-icon-group:before { content: '\e869'; } /* '' */ +.bpmn-icon-transaction:before { content: '\e8c4'; } /* '' */ \ No newline at end of file diff --git a/dist/assets/bpmn-font/font/bpmn.eot b/dist/assets/bpmn-font/font/bpmn.eot new file mode 100644 index 0000000000000000000000000000000000000000..8f3a3ddc4f21358048f197c9851272034da2c4cb GIT binary patch literal 47728 zcmeIb34B~fl`mX%*WUMCTdP~@*52xFb+;Ba%4FhaV8H!n1qn9 z1|HcH0_2e}Ou{e_7{~;M$1s5z7zTKdH{=ZiH0uP&ym?b4Nx7heN-3m|}bhE`J0b>LT$Axc?kn-H~HcH#|;n82?wePmE0r4Nksem4`9Q zcj5WHV}myw=l4q2<6Q{P(ec4!BR6%Pd>>;^9S6h~AD=iewc~5~HH`h)C-6-JP6~pYgA}fALgU23}jy5dLJ zMXienkBCq*o>gF-8Q)u(joCpwoXo}C%)`9Q$NV52K^9_R;Bp14WL2!1)u0vBvKWiA zI#$mTEXf*JBWq&K$VV&4N(${D%`z;@I#`b7S%G!3F4oO@*dn$VEu@e2vn6aPTgH~N z6>KG2#a6R5Y=Et0>)3j>fo)`)*k+Kht!x|H4#KsQ?P9yx9=4b5WBb_wb``rCWbGgu zV#6S6hd}6#fUF&5*PvL`cK%=g*$4o#&j16ULgY{*iNKYj;fRJ64M(_DG#ueq(Qt%! zMZ*zQDjE(L!-|F@8dfwMu!t26NBCGY959O&4F?=!MZ*ExSkZ96J61FtFpw1u2V7)D z!vQN<(Qv>|Rx}(il@$#KoMlDB0ee}|aKK|$G#oIR6%7a6W<|pR%URKIz;{+O95A01 z4TmI!2dcx0h6DX!MZhf4s@Os z4F`(PiiQL2XGO!I7hpxhp+7*cr#tEy@atetgZzc~&B88qO8r->#d^ScgYA&}InNE= zcJBwh&-oVlO_7$!y;Z-eHdVh=qtvw2+#hvBpRK*2_QhCx?55b$@lgE5x__u2sDCoy zNW7eMBv&TC+c41ZmOeK>V}RDhc?{3@uf{uoBkQ)62OL@7M~TK z15K)Bj|)+blwp)s-%( z{1e<`D?Mig?5w=cLUEM-+UgS=R^D&(XfXb+c#rT)^bu85YE@AKHFR+beL}vFFW@O( z;7(o;@7c8ZnZ1Fg$eOxn_ct2wt5q{a&u&3GA92rQ&dSd_4 z;C#L-UccVkSbgDDpIIX2Yi-kXAt)UYhtWJJ=>88?6{7r%&iTrs9CoW3T z^K=T((|j*6MlsaPi>6nL;PxNQeCL^%p8V9HnpD)J_KN>@Y-DiBQmd`7Xz|vKf4O7VZ5tJ(y7Kys zn|6wR*U{^a^i!%=^*tMk#T_HN&t$#JQ!Zprd3>mTe}vO=nkE^5Dd z>862I%`Kd-?(JW@>aJ|w=l3O(iw7#Ix{r6}n_9-N?XFH_!u9D;Q%ktLKAcH(b&hvT zT${VLZlGhJ9 z%`Nvlvwio{f1ASJ-TLHrn$XcXlYtGuxahvTT-eyoz1OS z`p%xkvF3^D?rZa%EluOcyQ?TG?ctWDP#RegJG&-2#*vkJWTg(T7;$m0J*MHGh&r^Q z=ld`_&HMO!`LBgV!Y#t{qAczcpAi3DLLbiEwQf197W}ojpgSE`aroh!s=r3c7G{oM zwtV!-xs)7F#&dAVVB9I!g!?4I+hh($JjLgAwn^Z zgpee>DcUMDxRfZP1ObW`r9h@lbdo3@BfGfdW{M=Bpvb)Jk89bcbI_lKqoc-5aij!- zgK-pc7~(lzO@(#}qapkKy; zJ}YjK0wt28a#HjZ0C8vuASy&lkesQMKyBO!AJmYEK%+%P5%h5lQW8({50?J=UnlP` zedT_uVwG*OtRxhRNfJQZ1Vu7S&9Yg>&mvdD(Tx-`gJ?+-=QyhgS&{Lf@`^0U@Dv1- zU{g&hUf}>KaSCQZbmPHn28oo=Q3<;9aI>U<4x)cE(Fuo1LadUBOP1+h%aTdPgCyDD zOn9rsXy#7(T$BYQF4CPzGRvwgyA(kYoJda(ry?p9AqA15^Vlu2s^Gz7l8{IO@gY(b zmxyRmRT1UU@`em^k?tjwJAsHLNwz;FnG|><3x4E61g~M^X0xaj1F3TqS<8kjnGpy* z14^hJ2qGFkMT{dS2#Heh+hmj%5h)ZvKhyw6MDRh+f^y0Ll?E#TMW6#$&P{N5&jqy% zLPbPAL}~hqh!&AiQ0*sh6W!qz3SdTJ_(;Y_#!CX5S@;z`QglDmnHF<^s?>%r6jZQ9 zFzHFF+Gog*>GVfrK#;xxu#p^cELae^sFbrTa8EhM06zs(QCBZZfTW1xs{)<~2WC+&YUJ8%6YYWr&m#8` z7y>QYOL$aB3V?&$BYCrk28NgvAJ>X>S?H|r6fwNAd-}#L6OaJ5;$YF zNEyJ@L|Cbq=AxidrqKZfAQL4Q-kZ#d8+8hbUkfK&z~D(dEL%7}HwtFrjXJiP@4CTW%MNJ05NUBMnKUoi#Nb*;9efYmD=lzR&9{I%PZMP>I zF&M4BZo`3Vc5Kjq^WomUHRkH7n>KIVwR8K{w+bMhI_6a9vL1P8L4g$lJxYoN5L0I!4n4|-}x+G&L81LAtOA9a)PDH`m^A| zz(3*!k6>|gS#LAf_(#9a?DUBSL!>jH;CN`zCK=qYQ}NeWz>yM1Rb$t99O8vj3NeuJ zlogkFN(DbmpUA}PN`Cy2fX18>tExZioH22#BHj>-XThmP>eJZNz66*=jttE*lf+QY z9vb)F2NoCE)3_~S3(Fj4nIokuWrWx`je*S?%$)Y5vxi79m$DliC#4zG^H7F@7B9t} z1=ES4vjz-}F9fH~HKr5nC2n-)P#Z)5HpF~pYg9@DpbqNHAo1KdQkFP?gCPe0_^DHO zojP^$z^POCXH~mmx2s9JgIX_<0P0+9N)X&1evNce#Mdg^4@BaVW$HXzdJb0FA=2~jyMAatyrVV9DOz#r9iwBQGYaNd<6=uw@<`k zk*9wvtJGeNgQ5jxrmhHy;zt*JPecVT1g)(SvC$7u9!Ye7ZW6<*<#Xl@HB^l?LKu9G zw5V91KtyOH#Uu+)gX$Zd6U7eh5eKra5V-(Rq2hq#Xe}NmnWJAM5~Tg8J2IXAFruJt z39s>&&}(Q#*YZLgvknksTtyBjYs4jSkS^iRiKc=dcY@7u5|aQ@4pv6ix_-M^QG7D@ zD5{KP>8RvZT=0h=1HBX_Mj4p?XChHrO-Ob`4}Vmx6{HFTwG0ZXKswMBi*~U}5bcs^ zH-k&kBOxBZC%C~#y9H2la4P~Bb!3E+a1&crONeyWsUQ&Pmi_!aV4@Hiu`Wb+37e>j zG^ohvED;gP2Q1)xBOrrp5`#&l2k#}A9OzwfL-mYrMeu?q(PO~}@DNRqtZ|#xKjJg= zK&T)h(i+ZyvJ(~|W7HXd-rF?(UB?m1F3N~V0UyL(#xde{iR%+lA}(mW1zzG=|8evk zfR%`{g0BE-5R;`J^o1zc^askShCaYR1w9>V&kQ!N^kpQeWj70U9(mPcrOwSPprjm3 zhzyCL1_8=Cy{DGOW!0GgI-+2RS^uk|Lu>(K1kegzsz;Mlp{QQf?p4rwYwUyvVhIfZ*PYn6sfrKeGF3EU|4R2ayCccLm4ftOTdK#Vd>i z+K2^2;dHhgNUfnMYMEOo`viLxFd~cxIC0Pq-V&O&26zcQzE%W|0VK+bJR&S{v04L> z?4%n&GDHaa`i0wRZUz!zPV%rs5`rrdSgCE0RrW$+RV}Cvh`UHWoGIk9Br79Pnc(g{ zxx?cRHni-qJ-R9CtP57gg5g-~@Ik;i_=ht>B5O>;ad>>9U7qlki`C zL^;4cMg>>M8}urI{q%=|5(uExWcS8gCZM)m__(*O+8GJD>qRM8$aS=+f_?gb)JNR$ zjmv|QSnuA{sR)@TpW=y6?%Bit3AEP~^a&i)5U2+7?048`jJ46?w?}Mps@LvquwK<{ zu~oSfUVA9w^eV2pN_)U*3)p<6Z^%AtmCIx|FInVvNM1+gSY9=IKb|d4i;Fs|c`4%8 z%aOhc@+AvjR|d|)=(NFIz9Dg(dHTO!x;Rb}-k~uUr$0`G0Ac%`2+17$aWJG85|X(u zN=7Dq1dPNR5Gntg#u@*PB+91gSAJ&_Wl8w6OO~BVVD!-pG{zGg4Oq>K6P`-%{hi1e z?LveERBuquCsvCOc%D1m)S$w# z<)&YvyS+pi9(sOFNc_fyc#E{L!|z;(Cwz>CjHt{vCdK2|YYh73i_sn-=KXgkMibYA z7k>kCG~w&S$YXQ`*k~;O?@{oKAr(j#1}48F$#dy%-k^vTJo6iqv0~))#-);Ig&;L= zTrPRG9>Otp6LF{7xB%m>h0sF?`sNA*Gjk+@3$J$&dg!5>9(ri7zV4xi);#>MxX`)< z`!{U3`TqNFzCZN9zyl9JrA|Fd?_(4TA5JVd#Lpo+UT_tU6=#T=7g&wsv2)3#=j#hC z1)R^JFI0#*r5ZFQ$X|sTp-)OdEh;5B1!x-Z^J@#e0($5qMWH+HptBQ_Ea{FrXkOqP zz86p#dg}GQosCfwkId1rf;Ngaf`|SVgS3EadR#M_Ri*FWYYp(v&ey3bedqmFAAfGH zKGnVA7obm7fe|b8JOLk)2$WksjYZ2kNOM$>5P^;@i6 zi+i_i_~h1|{mV9))T)XbH*ei-cWzuwdO>1;*P_)c-&yF&xDpNh@hz*)rxzr~3i-YJ zui5vPs|Jz@XaV(fJ+f=>*6nX^Y?AohLSw^D~L* z4jl0ZDh{k(v!k;|miO)0*46EFHFe|$)_rp8_MXL6m1av@?$hJ4ylvy!Oo!Q;@9bW; z`hgWITidIB{(ScCjiR`A#j@rW#Z<_3u2_E0bwj$HmY(iq?a4&{lC`Vep6T%TDz^9b-@9~0b4~0QExFrOAy3wH`%7v16!@h>35B}|>$iOlIk zyjL`>u4q~rdTDW}t1Y~y)x62H)D>l`E6P?^l&$_VDqEc~m-)Uw+^F}8x)r8suc%vL zh<`=h3UlQ|ROyVi3>N9&4? z7PO2mOGoQc=XUt&D{5Sjx?NG@dLwFFx_*=x6?V<&M`0nHY`Cw?sznLAhKIkR{q=Ra z|0sS-_$h1{Y-3aGPucw>jZ@SvPCYidXQ=Pi=Fmmwb6vcHWO3Ay*O01AHl69?0Y7!( zzcshVSfFOFn;IuInaWW(jXP(1UyecBRW z=uo*kpbsV2W~#~4!Q-_t#N_Vi#lpt=47VueSg_j3`8pn18%(bu(J<6Bp>?iUY!>Ja zDjq?x&=j$`x~>}9a2(pLf@E=EF@a!?0nCam(Uh=}I%3At?z`Wg@;Y+6bNCD0xjh{n zdveoX7P{+u>*{*zr@yRSg`srZ6L455H%n6}`Dx6rqQf|O4*#T;%I7vF4Curvlg88?CG*gE{iVtj4K?p$d+x4Q#Z{%%|s z)h(_sjn^-(!#v2rw|3nv=1>3fp@*Iq z+NQtxym1xYGyQ&H^czn-^;G=v#~(NFWee;DN#Ngnh({BR*1Buwiu&#qJ~w?_7@oeL z%lF;4=f3-d&xNP=3!iI&$E)qoU3mw##Wv>m(qPQv2^*CIXd^b&4$iB z|A)CswsXs;uP5la^wk8UV3rht`9WwLV)?I{R4-#S!D)9ad+Stib^P`_RL8Pdb=A!o z@zR$R{9}B@=!Z-Ht$J_U2eCxMv%P8A)Vr`e|LnQRVX~62z;lyj9n6KFirhY{S-Ifz zkVA&PB;n54a6JlL#RZ0|d^Wy8-D|<;Ah~cBhC0I51qb(a=fLPd9Y`b1N6yZ-^-$kN zW6n#OZ=3obz;;0rZzjGcARW z#}eo<^Ij12LKUA~67=1sRZaK{T}`VR8&@?=|97FQrMI_bs`T*_t&10bf%{6oII(_x z+xqpw3wT}Hr3d35#kIY+rL?Q1mv@w2=xyO2ZRsuj*U~S38O$Sa8z_k056sOcECXFq zJ~1En9X&mv+sGdJVIzA}Ek^e0|L)?l_VbUVkOAnpzgEsu{KEMGo1ww%Jb9S@GIDU< zq;-g#iF2?&beTA(cNdp~bl;w7gqMMLCuiHzxoFolu$-~Lv@56xDlv&cvXoK7lUjo% zrg7ov*Y?o*V6Z9aI~7i6Yyqg(-19s8v4I zs?;un0VrLMW&Zs6o7g+pLxfwux0&-GEnApXp$9KFUxywhZq8Jq|9#G>r~1a`Yfww| zD>L&{s9OqW>QFEE#QWgDd?nBLqPn>{o;*EY$#aH}*7?juWEt$zR;`}D?OgiW)s5$G z441r&^@a1JcX{lfmpMn)+%C1u#yXzM;HWpwWeP93RL_;!Jkm4M$I*KKoQ0N4580MG zPw#iuur^3zIE>$DKujxkX)Gt=@jI_GD&_puQU(9}(i4G$2M^xBcb1;Gj(<(~>FW+A zSC?+%H^1e`;lutzhYs-#V`ExBiO%a3#+C!@8KNu@0+1j;cS;o`0yJn3WCMjRjN-AV zDN6$VbUq1&00aGIuE_{+7nk*wQQAVN6i=(|8Gk0&#i781=!i)8eZe$Qj~*Tm`t{ea$`%p_Mfv{syQ*vLSU0P< z>%N%A+EU>?mWErSeW9qg&Yq4fsg7_SuXMT{)=+g&tnKkx><)L8*J{JEL($<^y=@+g zP2h2EQX^(dmCbKQ#4@eegpCMWjai9fX{yI-iz|xVZ!^cCFYR{Ha(nIzbnxmHOP#06 zpAEk)Dq$fX_+6at6prGK>?P^4fm*pid zzWT~5r7n$6D&z1)@<~7G8tlSfi0Iep9}AJ@?(XL4ALA_iwDYR2(w$vbb>hme!*#r? zv2=|0J;_B)HvQEl_6KA~ zZ`O`Mp7oP$$xEY-s?QJ7lF4dZl$t2uF&DfHN_h*}H=3MuMniVLtN({Eb95P^hyQc` z{V=a$t_+x+LWjJ6|Ffs`F$39Vi$f7sXndnny5*WKJu5xh6nr3vbT0}!^RzAg*>!(S7*0kTGGFC+vY!C zx1l{74FtNoKLRbhP3erVZt03G>+b9AUoO=|7iDhEZC|VNZLtCbO)7HIP@5>b&PKZY} zBWf+FbZhhKfz1PNYiXUaq1Id5O3nOixxx{wY`r+xUA9d-Lzlde1Y@p}QHh1;$=~)b z|MK`97||U1!4DRk3!lnlK0o}$n|uw6Z_K}yNa;NJ!QwJ~xb3kFUKqQA=%tc*$9Nm84}%J<8b@E7Mp6ni5D2U0dTT zW_4|J!+NEzlny%}WQ$MNO~;+u-o@K5prFoA!3+Q&qBT1({{Us;yEKy(SnGe+5Iw^V zlq3$pS_3Q+ofQxZC(jJ__6sPn!_>i9;m;K@%VGFlx`Oaro|9JHU3i6Wt{^{vk-uB{)OyfWFy3=Ph=9w9dsb;Zf zo)u9t2>M=rJDC#YH=JP|)LL4ln;1PCCu+%h^*PIh2`EfIWx&jm`gk?Rv=b(sXv*pQ zixT+lU4xySgOhaX%I?Tyb{wM91q%F4BQ!n<8lmwC<#Rr*p^@KZ$SMnKPxX z4lFozk(VPyYRqR+OU+h~qoIB@*V)n*2}^-MOZD2zphBC4NO*TgXSSoEzFtwIRhiU8 zdq*@BIit+5`F%NM#uUP3m5+vgmr?b z3V(U{z}_WG{l4hcYgYZ)u03l8Mgu{I{oug5-5tq+5@-B~AEUi<#WSEQlBNZreD2bxCD;aY=X2-L$sEnQZ7^ zy`yf`7)U^{*|kcHb>{aR05_yBCaLV{o>@%tj`CWPRC|P$lHA+3B;LFu(y?KB`Jbj? z9A2kOvoibwS#rNXDbBlUhRm7B#0#slCAs)@GPms5-Z@!%EC1l%UIxZ|aK(zYebKw_ z;-1Im)d{TuB5B8Ev~zt)U*U3!vKRc+WfbCs#u_32XRoOnK|j0TGg{{!$1M1a)(z(v z_AKzqy8F%^BoW^>-+uM)VP>{G?X}=AHdZb!ICnq0WO0_Rlx+m|&@K;`HnN_jjmZG9 zL;1@XRd1XnlJC5^54=*}WsIsnStgeQds<(7kp_nt8-n<+5Z;G9KQ_QV;0~F1TTmKT5Yqf`I$^FI>aC-6*rTU$dzsIlH(?T=K(o14YfovpPoUmffQ zRfnr;o7$S&sv@&t>^6t9wlNrJX^F*r^|p9`0wrU9|A|0gN;?BQG*&3fyOP-V;memG zt<_|s1WXc-LNuBt*(h|E=7>h~g?3Fe3XSK)tZ1}ZUxf%TLLq9dOjH4+VO@~Kp_()l zn%m^|Zx%NzAaGXAKmC1c^n7JB0C!qwp5(2sc`7$w2`5Puhc*J+3sO5?)SKm6gg zQa^w0C2B&TyJ$x(!u8lwX9K&P-NQbBR^)@NuOuzPg{VFd%&9PhM7nN;AYF1;8%cG7 zmN65m0QwNb3GD^bngtmBhi%S8vXE49QnS#XtKqaG0AT>^x|6~I62Jx8an#;a1f|Qo zgdhO*1v0=cH52eDu|!9nKM)9DixHdB*=h2n)1u-w#lkULB~yD54%mT_kR+sBy%dH5JynNFBY% zWGM1@IBpBY9G++@)t^d5z2R_uBqCg2S*h5P0}bBL(Qx%%UgxNZ*y7=s$z>AT+f~@3 zD~uFONWZ}sJPLd3T86v26&rj4fzhxhIgqp|g?M{8`xVuWIM*W4)6ODokEsr6BU6aJ zy3&SHQuLcrOWN9&q~ei!%85~bbJ}<_#(vq0Z{?~sC!;2V1$XW1H7PDQN)7zcZ@m2S zuS+!-f$v*OkMq63JKpl(gQfpLD5l|=TX=v)vC9t4u+vs4)W(DJv@pwhnB^4|nj&T} zO|8D5t76s&0nBlf^)QnieIoJlMAs2UZP#cz2*0)z1LQnsXkls-Pq{pQxw$TubXi?J z4Y`OQEEU2X4c!gNSltn`9C6t&`=MA|VF##z#cZpVD#;WmcNMw|F1nE_$fBW3u{#x8 zhbRcvKrFgC8VkVov$P|+HC}o-zBMY`@AocG#9&~~7m25(pZr8>k4Jos1BvBc|5mhR z*iDAXMp$VV1n!F@0~MaA*=NFXf5GMPxF9`ontbLMEKUaPHIbUIApb9J^Z2YVHE6~3 zjo z%Obh(GTb?O6${Sg&DEr!W_7ll{OIpNPEIsj8#kAE!v*J}x1WQRT;P$)&NCTLvM2Dc z_*v)yTd-SR0=v4c0zDgrr3bRO`3UCQDijkRYm|vX>ec2(<@?v z5mJj^dn6-}cnQ;-T2xvrc!(KOOVqa~WRkK`GFndYapH{kc&Q!qz86`82=0|xLH>N* zBHKX9#=W)!hinenmj0^EDO%UHs$r)TTGJ*?|GU+qSWWVhTFD(!8#gQ5{+eX*sCKj2 zXWp<1Y|+x6*&Sw=WpP+`hn2=nCI|l$n?ta4gw?Q1iY&*j7Gc?1p@cj_#07)VKXuwg z-Y&U9unZyzj?*7&Ak@AF?%D zy*xZs8rc%l6p6xko)_|0H`4O-LP0(BcY@xB z)b@6pms|VWRIjs#dxA=Co7HREzeIdOXx_2jD!Of(wpkplJmMHRVDoTG{>EMtcKnJs zuGwP|ytciEOx8}Jv2U5pCt5e|H9H=52b48cq9>$OFICoK{|juu0_HaaWAyD%%=kOkl*UIIh_H2Sf~tyLmscy4^#JUyUXjZ3Ps$GNH`F%2L*T`lsD+I z1S4LAQN4Bw6ZF|^{WjYY?SFbR2l~Ku3it`HXiy+Vuy8m$)njn&eAVb9;rhSc_b zcnIrJqsBP21A2vfu>bQ->~6sSl1H92;sea}*%Jlog``+Pf_+ZoN4z=50FtyWw37zv2?LiwwqAsBig-Ep-m-7z{U7J!?Y&ASMWOpdG=D)EyCF{~obI7iAZk48Aw!re4 zHPwjCJi=kAz7;#?n(V?;7~P4Xu;dJ@H4zNHwBencqS0h44evgVtx_y@Q+|gH5sz=z z_IjWZ+_I}7Q+3+ij=>&S%T+EkW}6h881T@L2*V)jMjErF4q0Ts^_m{+Q)ZH^*0!}y ztJCdtE>FpB+2nO#PrV9fX1En2HB0_fFLr$35$BP8G|t<5n6`R=&3Np(Ru{w|RSd7D zk)9_gcSQs_9OBsO0ZX?*W6zf3jL>e~3p=t>=co-V_~2^G93_JVmfYMoPbERK1F6ex zROZC4)h*aDZ6mvyjl)9cotX1@94i9;hW#V^F~J*CAD9YEqT>N^3Sb+7KSptl@OP`; z`5jGt#TIJEwCv3{-~7idrs0ov9JwD#Hw^>tez6rhz3lsA6V1%ZCTTCA0ISCuoz-kN zI}E!QZ(;9Z53(oO=h+MF2h>B%>XZW zW|;MRBNg%1SZg8?s&GYnYOCsML;f0j&{`R(Yfdyb#49tko~ryJe?{0?;WF`o-egAw zRB=L1zoVkV@3xpLLNQNu)#HU&tIuUp!``+UN2o&eh3bNBm6#!zzgBcTG$)^8pJHFQ z_z^4C;LK>2kp2l);ZTw0=$~N1f3Ero0%4RpNBsm^DypG=LOaCHQ9mhQ%PyL_RL`M) zGC!TO)lXh;1ii-Y2)BhyV!Tdt)YJ%~RrWgrCa2#N?2#ha584q*g-v2zovcPu5u0lQ#%wD%C;0h|R@`RYNGj_lg zP~8wr_^PTJtE&7qmn-OUUBu8m_6#1D1e8@Pp4TNv`_$*kaZC(4Jws@JDS<)3a6AR$3i9KdAlIY~D;VHuDT!*2gH# zYzcM}xC(RmZ@q+3$$}E(vj+_~{Nd>F8T|a7KNKxKn@1U@`XV9L%sRSrukd^Wx-D1| zvheJQzJO$bITM(YVq~4|wa2jEcMOuw*?G{t>=|x`4ZKHqNmwJiUHE&^BaVun!WPRg z8gl+w(JLD^T-mVU_qt)j8I!>ySGH`pvSq`SEgSw*ZrO02xpLA0 zeT;pA{RMj#@{TXDzlAO^;~A`l37-3u4;HX?sy3zu^Q63+&n0APJ6*Z_8XD|Emg%Wn!v7`(~FFXl8SngCMM;;n&rD2bG`grF^a?#vmvRy%UFZf^$q zA~3B38yc9iOdo_BG_Kmcq=vxs_nqgqZsrlz^lCgu>==`Z3WF23Mhk8D<~Zj)1X1x|lKaW_Ct@}e74uwq59 zdb+(L3@cY&m+BZoBs19yh1w6`c?2$3`gqNqcb0wu`#Wi_I>{ zY*T}F(HWqv$UUmdvx8f#7+mt|h$&R%^*i5}&ZX~8=izecd*F5loFdkTg)2naUKy_N zc$e5@TiC36lT9AgSlfxhINX#W(gGmg0$ocpg*UBZ#`TsT}iVCt}X3K52g?5uHBwKeKIXJrcYl7R6qUg z%%WPxa|AilSBsJ|2m^B?zeu{84j_w|MQ-rpPUeUPq+a6+s;c2(&!3BG!t zeC(xN&Gk*GrbKhDxxTqQLaU;%CI&*D&j^RK{A(7%$XqO1`sv5TCgITZ%Yv&^rI(dt zE*}w}M&5MVP=UXn6g)_fq<( z+jBbjQf^>AE~lyZyk6Z4zuDc`KIhlC`wp%02_o&K_52y=h_TmR1?-*&-3wta%P6bE z3fmUU5OlyM?;_R@DXwlsT{q(gr~vawLYs@9E0bmL&3$K7gblyb-*)4-)Iinr!Rc)? zx5D$^ExClRv^<&qRQic0aGL%7WcHS-n&i#pJi8!CDCe7b!<>u`y0MB8kU38dXL3V% znazpteCd;=+mL?grS{SfdDR@KQJN(4DeKi)(MAH$DJbjK>8f?Xcv3e?Or~TAKy&r% zPM!MU(WCFUao@h9M-6qmXEklR|1uU@v1#9JJ9g|?+Mwxt%}s;ap{~6*lY&+Wg|lNm zBhvkdQ)?HmeOsIq{tea){*~g=1XMNL4>g^%Hb(&2AuZ~pWzjfbnO2)RfEWv_fOS9J zA9{P3VbCWTbi-v@R1k-Dl*;eCRxW7NYlx}u*7 znX)#G0qwpnEOyZp|8#{U=m~21&~pPz`dZ%5FY1S;sib9CTVae`AyM>fuxs-hM+kHo zYwZ*h2T`|L-QHkTSQS;-Ws^;I#qKgE>jF@{tCEGVqpmer*_Cw3e4Qv+Y`zL>$l_8h ziWG7?DlHzTVpgy|!=(7b0o6{TJ+mUpDwg2}p;Sm~bR~ET*vz{&qNsQwp)-=O#!rFc zQH#fAa@Z|4vq_N53iJ-Wp|ET=m44u;?XC&6#cf`MbO)R@vC67omEGmQB0~w?s7yu? ztu~X>;&VqS9qi<;LVxL3P#>$U>}yzN4~oI4GwceRtyXTW_QnJ0gtsCSgpfJrFk#td zMRh3Zu5YWX-PPk)ZT7apfmM|~m8~@`5oeH0J-XwTkZY~GJ|B#^txl`iT@gsvrLxhb z)p3{2!D}VO*0n0!+1bM17soPivy8ppMQdfaKl!lgPc?a}?O1rMn!WK*B9P548V@Gx zJsrF2tJ>Q zeCoPt%wF^KCo>u0^O?-_($=`I!c`l#NmjEY3$B_%MNN|99Z#=V756nRUYw61rqaD_ zZNd+_nms{B!LhP^Q$ws32J$lLv{Y5y|AC6mHTBW1I@D`vReEjuTgg>!80e(2CVo;Y z7u8(t&fW1&d?MF(MIBXs>(^k8NAIIw27mfB&0cx|tI#fb4h8dCFrgmE*22UoW;kdQ z63wQ1**5q$8<#gUEN>K6G^}XAfBLh+ikglZ{HH%#ldGx8ElLy$3Bj7k=M&TaE?7gc zSZMm+!?Bp~0=_D3(7)nOBbFG#mo{i0)$pfrP3IG(H`9mwmP8)kmEKHW^IJkODidQK z_RspvChkG)jC&4B=r`_ZReRXD7vUc@?j_vU8TT@J)du5Ufq&Mxx4^D^!ML{qZU@ol za|s*(Yr-6*e$Rn{4;l9Y==w*Dd(x=+3*(+7)PH5%%b@&!W85q7|Gsf=!F>7;je9Fg z@UEf3V<*Rl2cug@Ca)VE8aWYNb?o5kgU64JZy7m!a%^ytZprN!nLIH%F&<5~wbSFO zk@1nq!Ksnq=)oJKC$2l3nVLEjJv2FSEV^=Hd}?HDY$AGma^mR7&{W%zsj1`Lsnj9E zzinvZ7@Ex>wu3*3VahP>qHHT0(Nwob@n!@=mnabD7yz*v?l|toVM=2J3PUH^7{X7^ z_?F-9(7r!`fzJfOMqwJN4Xi==?JB$-*Fp?J!9*(7N+z(3&K_22^9^n`|@4Q z2l+uh#E1C^Kg197BYcz}<=5~revH{p3{8$)-+KMz$oS9^d1`X-#1Yrf#Q5;&6i{h! zto6{?#Pzn}kwb$g$ELIg*J0q)^@BII9*1eSv9Xb{no(fWGQrHpBQh|UQV7oJ~cXixb@h`i4%i|M?xp21}CS=QB52>J~Dn{kn+dTem=nvqG*?AuYpD}KS)PK+K#`BN94`Ki%kBa=<% z4L_T{k-Pfy21WCmDYCT;?i(g1EvT4rv{yjFH8MV229O5j*ac!UYP09OL0%t}{<*N4 z!H?Kn-?Jjo0OC6_a_vc=#3*p-SQ+x>lP5+dTc-w3Tw_0Y^2F#ks=amcMb#@!^qSi3;VMjYNwye*QF13=NKHsD1u?X*X85$YAZp4l>$65!0N7s!`-8c_~T3X(@ks3`hc#a~V!bhsk;jcITvC;8s zd~*OXlV|7D$PH7igX7~9Q(DPY>a1EvC!A$WC-fg1xxrI@)=0|1K|tJA_M)UM<%fw$ z?`%3miblsliRQEr9WajBK=iq1_OVPa+;hBiN;KyiDo^yhr8U5Gt~Nb&WOCyA*12uk zHxIxDIZ!}|M~_cg^}5pPO*(-Lsc0(H?c0pn)89}7a?Oo~igYeK(^DUrfqw1zi(q7= zU9?^j*(US;Bf1ai6MhD+6>Il zVMK(dm9W4&7uAfQ^%sua$ium4r=Cl_6xoX}_H3PkW>1X{je~lO9AwiyHaI>4 ztU!sTS`UtZ=dheOJ~%YeIyEsdM*N7;?de^+M4>HXgC|Z*XaVMZJ$P{9x)I%H7zAz@ zaU7W(8F6YUlzpzB&@pLr{P@W!1MN;u;of`%WL%3>K0G;b^0*Zo5tt9H&t>fY0;>#8 AegFUf literal 0 HcmV?d00001 diff --git a/dist/assets/bpmn-font/font/bpmn.svg b/dist/assets/bpmn-font/font/bpmn.svg new file mode 100644 index 0000000..00a3162 --- /dev/null +++ b/dist/assets/bpmn-font/font/bpmn.svg @@ -0,0 +1,224 @@ + + + +camunda Services GmbH + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dist/assets/bpmn-font/font/bpmn.ttf b/dist/assets/bpmn-font/font/bpmn.ttf new file mode 100644 index 0000000000000000000000000000000000000000..f5f0b9992f598fb7e0b752cfdde562c129835b80 GIT binary patch literal 47576 zcmeHw34B~fnP=5I`@U~ot!}AXcdNVA-8yVZzHRxwZzr-P+j3;dk>y;(nGA$52_fMO zEV&Z`WJwq%VHmD~Okh}s3CzGSz(RJ&4g)mj1PD93S*x@E@4eSLY}v6L1ixY3uj?K4 z>ecbpS6_Yg)mL9J&KR?@Tban(S8d(CH2T*1n;7Gr@ZGj?Ybw(l9rz5M@51wrL&pbC zeE*{rPckOHn=y6yk(g3*k9BHh6sarp{CEVeF|B zj0uZRjGvs`@zwkq#{T?c_@;p&C2`BE$8auEtTeTkvRNg7G=UG%8)gqS`b0T5vG-s`iX5YCi3(mkG;PuG_{Oql1%U zEW)H&?`Plh(UBvAh+qGbBOJZu@Zn4;`-(;Wb|#~&Hwgr>yYwr6k)=6uATU{k?>xUv zhgS@-XN#=(MGYQ%P?mcUjPi&dmH3ML&jSNg8E^nClCc&z;EB+#*ClU)FV$)buE0k4 zORR)v1y|05;z;p}uL45}6X=Q`VHdS7yu~*XVpf<6tTW?#E3+{>b1)}!F*oxtFY_@! z3$P#yu`qDCf>p9AR?TYA3Tj!5#aSJzX9<>M4Xlwhv1a6>m9?=HYiDVeVOiF}axBjZ ztdn)IZq~yVvBhX1eXO4?VN2OEww$eCE7>Zxnyq02Y%N>I*0T+4BiqC_vn^~Z+s3xD z9c(At#dfnjY%kl#_Ok=*Dt0v+WCz(HHpGV6VRnQaWh3kuy9ULgw)6k{&qe@{eFhi+ z6(WZkNd&GG4M&u%XgI>HqTvX?iiRV+D;kcdQqge07*;eK(XgW7fJLlmIKs!G;ec7J zXgJ^)D;f^i#)^gm-m#+LfPt)NIN%~H8V*>=iiQJzvZCRDsjO%?;4CW|4%o|zh65h6 zqTztitY|pkHY*wqSk8)u1HQAO;eh$9XgIV1Rx})111lO1?Sd5zhZe$$hC^FnMZ=-h zu%hA6epu0PXi2PSIJ7BNG#pwND;f^%j1>)s7RQQ)L)&9T!=V+jqT$dUSUh64>@MZh69~vMZ*3AsqLft>s57a-Ia3o$zI+81s-)R_V_)+8gnpjhN(}`wB^M>Y| zn!nbvy5$2c|GRZh>vL_fwii;jr(SNqJ$-BXeVH3F&u4GQKHqUuu08*BVORH`bwAlN z+VjaplZy}Z+WKzkFD`lK(#fUIE&Jwj&+-E+x>vlg*0Q#4-JW%iuKUV*VSQ-*uJsSE ze`Q0*hQk}~-uU9C$xZ))atUBV&xoHEo&`;;Kpz-$2)=3|o5>f{WFK$iX0BB8AZ~=0 z8`pP7quuKpmyh}0Guit<)uz(Fl(MS7Imo|IQ71f$S6dKb^OA=3H#bG&rJt6*=*uTL z&j-=6s5f}^S+Pg>F;Zy(E$AowFC+~tc zpNQAWja;nt#^Mf6{=GaYw(+i;LqLbmPe>{Fn=(KWy$4 zoOZiYC|DvsQQ*%ztyBMOw%aU1lhu_jsr+NyV=Fyt1?;T6&q8sO{>JJP99G_M^Jp;s zj(Cso3-l3HRBBaG12uGU3VlMpkT2jVU*Jw&5bxQv`I)_erpTJQX!q9}|3j?ltc|bt zHB`6nDK%6)91(ue*s!PB*Blv0#Cl@?(cpZpD_+0e+gN?|Q>Bv+S3E)--K*c%(xTd& zPATrss)1l3n~RrIn@(x(CqH=eE!M#OfBxnzqXjLc=|uK_y7?BX?}6s6V+Ab<)T4;_ z-!FbrjG=G!5l!SVl`Fm=4`d2F-;v}A)hyJwIoA?_9K@$0M5>8x{r03`qpr^TBVvJ&_nG;Q~7QyX5oc_))pYz>J zRK$_I`X2FfVjb`?4cdD(yOG_A(j-tfI0C^yu$mWog+e~r#*>L;JfY^{heH9jCX#J< zQ|RSEBfS0!J_5qFaXqYxusDFw$pqDM9#273eXtPB7vKr6L{f2bpgsAiK{ctUN$nN? z&FJvplBHH#VbS8P8~yAUap6p-N<7#fX=b7!hmoDGXEVidMZrQta(}sMf%T?c= zx$Dt2>oU#l?Lte_%7IO5-`bK=&FO4z#nQL;ERHpgS9f2V?`&xrJJDT5S!oZqG=fyC4+INTodk-2yc@)9Pt## zAJ=5gxWhd?k}rJpAadL(_>lxyXHjR8ybJ25E&P^~| zqhu*dLOf-r9O1|zW<{VQvk%FC4i_9EkSa+QUbMc zCwx#tCIXEX6-Cg;HAqQ3$=_f4n}3_Qzx3t%t%_B)$+D7AEG9_+aT65DEH%q!89$3$ z4M#Up$PA(-Nu1-XCS*m%hsw*cB*Rk>OoB}{sd$A0q{Jzh1<{QMvl%2(LPsU&&cn@; z0y>EP%|s_0CJC`hCN5c~ekDsL84r?VgEQf+7NeOv>2pyQkhn;9Cdn+Tvg}d>MQ|cL zJ)DZDRD={niq2!V$f|+|lSx7%3B-p;Ra_#XNmWIZN6Q;B%tgAFQ0@dGmL%E!lw?xi zjV$<)3lY4AjhoG)Rt%)hQDiL}vSdae^b9DWb|8po{1h>coFF7h#cz{QUPPo&0R2z{ z91+0>JqyYy15_HU1QdY|Tsb$v;XN1BG6)qB`4FY4FCbb(MnScoz)f_AS15oPiQyv| z9~mzRY-Zt?_(;+HP-j}q0jg3PzEDuX7Qv(^t!kekKc+Juk^w>b2Eayg$gyBS=zC_93cmLn8^pdllI*v;I|C7Do`+DaI(@hNC6 zRtsT~hWBVJUc{{Z>#LD#vrV)M9z2WOM_>rFXfNSWAt?Y3a*yQAA{rQCQhZ!%frKcM zTxq8>A%ceDuav&bJJWl5KmF;`{MQ6`m3S2TT9Q$AvtG}@0*cgxtP|pB6}y#cUNxI7 z+&ruGs6NRqNl`*74ZF=~%&ytF^25caB*)*6LEt+6x59$$^ z0Ueu;56F&c10c+jZx+QsBLu_@Vj-YDfWT!u1p=WKb*P(Uvx(?{b_jYAlLme{2{2}x z86*a{LVqh5Z9E_+gyN#4H9ieH01&+q0+3<=&@r1qM0zyn+hwbPLbzDsqAIGA!-@+v zH#9IoG65whv-qJJaiIJz0sw$w(Ne}e!N!cxqWFY{WK{R40=rZp+wrX#;E72xy((8I zUeChMYM`Ems#;2-)CwX=DH;^nEGL08W{Z>oTup?PifJ|qDrFiSPyjMfa^bznthiC9 zp!l_LvIPvD#KW?M<8z~6Cf=xHt67ef`+%SfvMO|GSfnCP5z%T@6;zqe33LL*mNi0z zmd-^#pw3m7(K@M^2^pjhmGCg?fI0-T)}U>QOyvd;fDqKcP(>OcKntKo0Cdpk8<1EL zU82#H0Zm1(8+8ETM8$?)fht9hTc?P40v>1xLvaDs%dvyNQD#A~(E}0zqh>0BN}{3^ zGgX7;X2_u4*`U^tbTuRtOdv_8bD;fj_v2s3c696R-Z zT6d$rH#T(e`#rtY3#FxcF(eC3{9J9aHw zmQ09Zyn5o@efiPU*T3?W&^>&2)3TTt<124@#m*Kx4tEjO7leh zvgO-0erWYTs>9_B1iJE@HtyWBVaH%u_;UUTFA5pqL6j3LUDlrk7Y6Gb!MkeG#DbC0R_iH zgEqXVatP#TCbEHMZ0tF&MBPk|Xcp6mS=$t5aaE~~Ub%n?U zhzb=4Bu8uUILRFSB9S2NN8OR>%m)z#bxU}Szl2^xE4r2!>X>zaAmb`>Kv^R$iGy?r ze?c@A{J0ZrhLe~CkaDmxvexz6&5GiaxkpiDBuhsnx8j071R3b1C^5>w)V~mk(rQAo zBYOCwYONquAgEi ziW{nDd@F(%G>IMyK7fa4f@F=`wEhvFp$9?*5s}t#29%w!2pOZ!0QBCb@$Wj0P~sIJSZQ)djO1*F`E#b;<6G;pvWoQqgksoWGX$4f=l2= zsi(p~4!jdpsR+EJA_HQSVJe^K#9)s2AJs-IAPT3m?LcY`O;O9-LfI$StAG(=<&58a10<(R^$<3iHp@5h-4?-0Fog>(AO{APIEJm2y>E$C6W+ak-$oA zgRHU_602%KbwJ!j^5IM&pCwrtiOK|b@2MRgf3TrtkL}S-QDNaJ!B!@2jJv&4?jhr1bTT6!s<3oL4yImCPU11P;9w249X~s3Nf|<;NqZgS_;IC z#332vFJFB%zxCOnZ0Q@>qqfRMPqoG5vsJoH4s$qLod?lCI3Lf)WP5$tC^5R^awttPuS<}v}b z?ZQXBb=A&D&|NP|!9uR1MHTE*|Ghrqj&EEZl*D@XrcOo3Joyw)e0U%V z8f&uMHb~?l*hRHsRw9V%>j8U1h~Er03jE5&2CMT_XbDqWh4PUC4VT|_4n=W>2= znwE`Akf&A2^N7=O2LE`jI4v&ftmdVNUoS`cD#({Ce2sHrLT9*5=Xph!fRUtglQ>^U zbQ$;R;>8O{4}i;Ap^VUYzh$diU={&S)1RB%peoaz;t$;A?xjZrxq&_aE=?y*A=c z^4Q|~!dR7GjdZTxTwC9g@x=4o>81u1jx9I+0^RK;%J9(hYeM4JFT`7K^04jCrIeH%>>^M8Q z;1HijcD&#!9xKigH7~Fl$7A!!rRVAkEd`wC(HAPjoKg)M6XdT#jnF5hpca*qoB}ir z`1!SkUIsmMlA_QZchK1hNtSfS9W*a6kM9LkhMsz@Z)alE#3QqGte}mejo_ib#UL%< znjY7*W>x8X_gVw|GjnySO5c93)yJQmtxt8Y_<874Rba#lJx{=gBm(7@Ph-)t4$>?Y zBm^KmBN7?(LOe6ST2Ye!Z1m98ef<_|*W%u78$P~uXaBNICbg>K#?4!I+npO%lU|V6 z-?eD<%C{G~GOk2Je|*cT^XUbNu|j_D{%iLA)vAGH0$M;lU61VAyLJ0p8=EA4x6s(I zV%5eqceJH#j>h)%Ti)BdB<>H4olmicR=}`V-zokL=3^?M(YqNMm*Z%K*RwaUKVfeo zD8%Q3gQkj=P%~MQ$lxGIIY>PVhX#t^obLiUeDD@3S($8R<|73$wb7&xpy$`Lgxqm2 zW=Paoj{k-WhYsxRUmEaNx20CEetP%5wF5`}fr!0mwsmzoT}>Uifps6> zy1i#{Ri)X|miy$GEN|PmHq&9Y<~zIBt$tv|%GUO3pFf|yd!s0>U9qgWMKKj}ohz2# zb6qqh)YNu292(+nP?#)5zM}ucSX=9cO@lk0S-&BhYjQZ-+upHb=TrCP3qD7hw>Ga^ zzjw=rm#<7VS)#T5eLJ`9-aD{!q`A3i^_p!PALvJ#RR#!Bw4862C)YTSV(`wFSTI!0j)fHu{ zE6P^?8I`Thn#+98A8yopMcoQhwO7=wFvP#2ZiPAeOIEj{m7T^q5g%3-wqb0O*R-zm z5g_<>A2H*YH!fh&2+hA~%(;(7J~4Xkpf~4d&0oIh*x^OJy^9VXd%1jb)7XK%xk4eg z_rMF~o5r)b>Ny{1U#91Lpndtz=i`lO%%ba9>GO-n*|+#r{Ii%lUoU(L{P3mDC>nEO zn3U6W&9A6mX)0j97xk-4pIz&@qN8<1M+;g;m!+e1sdGDg^%XTPNZqcealIZjE?qxL zj0(G^^`o#5PBz?^XVjvET|+}(*8ciB-G36lDf|RB47RaJ_Gj#VlEx`&7pEQ@-80m8 zYjfzL^SLhGL9#gN$ZJSdCY#Rm@qnK?aq{!ymXv5>&|*sDItu90`$+1B+e{&wSKHI1 z=~T_Jz!yidJsk*=TC(BtUMQaV-9BvzFm$Ng9ngo8Yctj4>EQ9&7-DjF^kQLSeTG{U zb1Ycxt2NjQ?SZIpaTwPZUZ8#3?Rzb2ju$VwF#{g!< zmS{@YNF6cbY4_dlPk9}=-8uY)?%bY^jy<`lFA3fCy>)fH^;2KcuEL>o+!JtEZBAdM z@a8uQ=}53Co`%L25+@}`S+qF=P*(Clp%rR=n3%TUhqutTgo2b$G{qcl7Z=}XQ?N7! zKN&ZH=;%89Mq_+!$L?HdEVsJ@SN=|17u7AUFOAhNuEUkT6IW7y!@6oH!rddh%cx@~ z*|YF!5>0=+Q0uRSzPEPWE#}Ys;-QD06WXS}@tkoL-Zk}JVdU#iJ@r)l@y8!G@MR0^ z1xeuFe27OAjn=wrXN&so6+Sz4LKvF5pUd~%x97h5gwKYj_6whFfyXQKyyrg3k_29f z^zCDiB4%MzL6dBst8E_-&KAn53bB54K;1s5o=3jOU)uTjpUkIpZ`!_TLsxfTKCSzW zD`)Fli?Nlb=To{*CYCPWGE3(^x7pCS=l?ic$#!=6^z{Tim%f^S6wHz$Fh2;5LoEMQ zlj>!xCOG4cWpAAfu8!Y+hw4}stFF2^BVPKFf`5vy82Mo7zgF*Udq0+Fc(yk!n|ueB z=bt+_IYd?x7I<#5tb@7mQ<2+eG%FW;9`cZ(FG;v_E?kd6S8;*iDxZsQQ1@E!IY=&? zgQ1SFb-}@X-8>i_r~_%F`N*01wjS!+Xv}#@^KI00wS_>JGrtzrCuJ{VHto^r`Bj6y zz9fD`cpmgV2^&4If^(h@Gl2e5X{M#{@mK;qX5I^eUZ~%Z(F}!cpk4yyYyiE!??Ehwv=|Y^zx3<^Sv$n z!!5m~|5Ez-FM@dlZUY6;`+?c{gk_*h$|vUIzOAPxbQ{@2KWt=gvcu0UEO&8#&F5YSf4*XdY8uzdYN-%&FxalY^>wC432u^ zY^Ly%OZ9A-%_BWCeH^X#&sk`>^pI`2^Ynh_3~PflhQs)c2E?>tm&S4;9>4t>qf*Xa zDOK=)C_NE4c<|s2d}ry2>-blNpSnPa-xo|1_2}V&LNbNki)My zaV~v9RwM^jcuRK0W5E(ST4<@7s%!zQtb-8^!M66^YZa3O^DE9@^IqJk7H8=>?o_b? z18X|1rC)s&t85{0P?Ya|udBM&j&-w&yY35VtSuGZWofuI+82s?>+I>+lIjTO@k*!L zVGUIW#o8X9#qMxdd95}qI}{y$)!XK=*aRNuCN*NVRN4G?L@d*aP1uOA)tHqymZo~V zwz#6${WfzP`qFMEEw|^sKnJgGvDA60{MqnZq7oMJf#1dHPT?5t$X=2z8>p4*<$8DR zRbum0gK%5vbEUia1V1@dAsn0fi4cr`^D8gETl(Xs*^_QtjOL8GVeJq$0d-#K zVrGjjr#rg1d8A{P)stM*WYb??Vt+t(^k(cB_eeWQs9XEbE@yZV0sGe?&pdidY>-w*RD=E{KCDRjvD_ka4dK4u`>Y;ovBWU&tdI}w-PPHxn3nV}-M0BJ)@^9dMgxKF?hio=Z&NxWtXsNb%ewn|`^;t@q^$4kyH;nh~{@RJyf!^}yzVx3siQ+feJRZKY;@u3X_L zR<>Ro>@M4;ouNx!NP;m}$*9D_^W<;&*MEKd4vc6HfB*Xn&V^5AGM^jz!cD%0#W&{P zOr&&<{9tjJK3$^xpgf$pEDhbp87}NHMss7c_%4)(=*g4fC&UU!d~KRdM`(dU#16j0 zoy~DISb(f!+6j+y_lqx<9$dR|(=YyT&M#k#Vm2)>tjH0s!r$FaKTt$Zth5~&)?t;dx@M!4+d|&AUAKSiT zb3B@8*w)om6N$#!OQy-LzDrXJ;7i!#yQZSUgk7f?{= zr(p(wAEGroF#iB$;=44H6ESx+$*jq24#12yj=Y*dx zT1t0zKn&klI2rsKzg(ti4*h7lT{ z1dY)6gz`C;*3iiBGGvtrFvw(hfXtavR|giHy2#6sA~ohSsikJC$I(zflIv_~i-e^> zprv~4Wl*8bLL|JqqchvlP+za8(W*>pyuBkDikwwu*!;enGGhwivdTw8zso4h!6%>6 za8;MbL|{{5I@cJ|XmFpsh+}%ji~8JYc7?w*bYSn2rG8)Z>NTtWeAk{e10#W;!+vmJ z-R_R$!15){=HWZ@yvahY=gs@YJJD{T|4$XnD6vh z@2;xlUMWB3YoRTK|uEk7l zVg|&Edbe$yySk*Zytt&h=WbeC;!HO5uijC&Y7`_O*z8&*#yazR4uBid7n4->bWblP zd0TlcNvb_UOG)nSTM}>H5$V`4wfxW0Fb=QNr5PE1fh@USpcLm_HACi1Wa5R@nUY-m zI+Xy_vuN?=Ayl-oIi++rH>scX7{SbLxcF0FkugGTOPmq_1!}McE5}>M{y( zQe%yf|8v*W4Wpl3@ENW7$1w{&qjkeP!=43RS$E&LgCyeH=Gw3RJUFU z1?TQ(mMqTDm9mY%9@^#M(ni)Zv@sbVb|`-tqw0+_MDp!7_kmaHyNprw$IIk$U{C7{ zFVNr+V?z-C6~cS4=f?)v2i$?RT_j!6j2EdSULi>y zs`!bID2KyG#0$-P|Q~H%r2{#)MGeYN&GsMIlhJdVqgL=Id9;QqUQ< z$6UUuy7g;H%XuUTg9k=1EI7A^SCoEO`v`OloIU%QHVy#Gm}MkqwhmWe8WG^`7fI8>8{Vp9Zv0HY*GL&4HPHi~;q8cMfm_>(Xcb&@8g zq6->VX|3YOB31UagvS5XVhWCgt6;`jXiP zOJ%Bs3~j>pnBWg%JOir)>7nIjL6$|+;q0>LTRP4T^KM+n;p$Y(cd$)Z8QpFk@wQJHdpT6bQj@UgzK@V&IWcnyNA6Gt;h#k zUrAbo3sHR_m{Va0iFDlxLAvCyHj?TDEn_-V0rVk=6WR-=H48BM58IrHWFe{Iq-LQ% zSHo#X0Kx#+bti=bB!COF&AZb;)x>Re!+D319BnE90wZBhav*6_ z3i0-G_A9C#ajr$8r=3OG9#b9CMy3#bb)^lZr06%LmbA4kNyQ`eloO-=X0`EVjQz3~ z-^x{OPDV`z3+~$2YEoQqlp6S>Uw`SPUzKVu0^hfm9_M?5cf9Gr2TT8*P)x%!x9|Xq zVwWA7VW+K9sEr5bXknK1Fv}|_G)2r{np%B9SH-Lm0+{0{>tQB4`b6U8iLN7z+OE-b z5Poeb2FQ8N(8AOvo^pBqQgdA_>9V?d8gdaqSSo}&8oC>jvAUyXIpVTm_Cv9_!VXXa zi`iB!Rgx)C?kaQ_Ty!H|FA`5nKmM`Q9*_7M2NKJ@{;g=su$v5%jj+-#2;3J*1}Z#Jv(JR({({TnaY1_G zH2KUiSey*nYa%saLH?iI=J8o!YS4=58^7Jn6J4LV4ZqJ|xI?-hGkS@D>ZR!CQDijkRYm|vX>ec2(<@?v5mJj^dn6-}cnQ;-T2xvrc!(KOOVqa~WRkK` zGFndYapJ7^c&Q!qz86`82=3(>LH=CbBHKX9#=W)!hiwkomi~&(DO%UHs$r)TTGJ*? z{nlzxtR{I$t>g}=jhhv2e^s)0RJ+;iGjCW0wrJ^3?GCfcvN$Ze!%E{OlY{?>%^_Gi z!fMzhMV4b%i?D31P(mId;)22GpE&IzZ<$C(c}9ios%a&k(5HPD|&1baqt zhE0oY+&=YTt4%QVZL_(#bz?!{580Zn-mtUUZIhk0!A&r!TUu zNxhp;$o>6f0926tZdfd?kxu9uPmB;owo03e(W1!T=*$^Z5vq> zhdwn%{n+v)OM`#NW8%WZx4tbL_~9p~&hQ)kO}(dupPnfS2k+Rwf9j0zIh((E3H4KC z|6oRzxPeWu>tNUM7WT%Amo`9xj0tEA_sbGjlFZJQxayKwl@z(4K8dVh7*EWr3UlQ% za#;#>wp{kLgcpAThOotH3i{2kxo`K@H^RPNn+sc?+D$%_*JN|KQ;q-U!GJaBFb4#m z7RZr&(5rd{!C~?%UbEHZliqOatt)RncI!pRKK2Qf`BcCj@>|_Dr!(LW3zdOz$m6y8 zVd~y(cX|C)p@`cN2?ql9pa3s~@&;X&V8n|ss@G0ofz zc86kX{#%<+yT;)Pzwh6(B0S^s{ zFbuM8q%m9SutoM;uj#=)WhTjLZCmTKI^9m^@|5hBO3M>3S45D*A&#vcuyh+V_FOs6Fzwd8up=vV zmfFCA53aV%QZiUz$<2LpR1!2hkhZ$US?P zp`kx+w4T|z=-Yp62#r4RTv_AqK`)5NKPdz*cz?M4?W?902p70N+4c!-d=48MT2Gipg|FN=>sX6&cV?(&8*)Wsff46S`&#-g)7=qTUA#Z^4Hjd*2+j- zbE3H+UYV)&ROJ`>E5gnSmx&MbCOayiiW74B9TgpZx5Zo$ig~K59xudNeJ+z4_O{hH zLKUhnR2OWk#0L<`rQ4RGI+97t9`bhy>cG1+OI*n0vKmQ6Y|f>2XE+rxNwK(S3Z+68PfNfIfcjm2*(wRu)!6)8 z4!Qy+m(Lt9d)=mhE2zNA6Jo~B*a25Sbwe=WtEy_Os`A@huAs|x5kvQk+asC6sgTFA z)E?*xm^~gO7{JDnyrx>R3i19Jy$;(QOPx04PL=EGMe=ai`BHB2gxW%i9K$xTl2$ks zdE-IkT19@4D!KVGHiGgcFcKhc$f0Wv27)q8jzVdKF7A#*OqCaLv>hnt(jLiBPvQT|4Cu=%?^xKfl`m4hVNo-6LGvKJ_!fKlL-LQedzCQhZYQ2k@h2NXfj=_N`^D*!5uv*2!$g zT&cI0Z^-VF3bU|I(@G%#nGJ_t8xT(x^i4T0(L`=)+jx0yu; ztxV#=L62RwxMg2xDjV|3R+s8>SS-S?tRn0)2o}3XwVZiOO>?D9%ptBbU%*LRe8IaO z*{oRICa3HQocX-sZh)NR1vjQ(#fo6{RC`4jR<67*)o}=sOlL0?YCnMIVYpoBqcwNl zS^7EbYd(;k{z3Cq5Zh@RSHGk#HoGLVO%2*bXMnaM_oyz<4sNkxaLKD9rcjmF?|e@> zm%cllhs&k!f!iH$idY{Ot`KE=Ww^rQU1F1MVYBK@HhEN!59_JI#(&^ZfA1OV- zJ4-+NP%s2V4p^~z^=sm%gkMA2ZH7juoB3H7)+yqeB~%0m(vmBHeqIOPdbnD;l4cKF zTiTT#Odr%;yFGp8R9b9IpScdGe&$=5-3_y{l5W_2=RW-2DmbTp)y|(Ot=HEL-YogV)V?-;%($g|MRiO z#z)3R`LXfGw0Z3Z#dnL2?KkDUTY52YG z=?nM0w>RATAYBvTgjjw2s?w(teDyy0_=~%m>zh(diRN5$eRF$+Rz+b=41_$N5)Nzm z*DQpQxmdLHGmnc+!r`fx1Xrm_FDuDhJ|sSkyy>)|4EuVr?1F`eurIH}9V$o06ZvSi zytQ}n94RC5)REF7-+knJZ+t_;)afJq<%WjqADVjbq3d7VB36I*yZ`G5e$@@99yt2l z?>@AC{pThd?&l9QoI2Ie@Y;3nrSws^^E&uaZeTtxr>Xe7R^1D~-rd+f>({vZ4z2PD zBJHL1{2Azou~%OK?4ASN3t=zID67K?+ZN0abigL>BGwNnu5LwLH{%DW0P{#fn~Tqv z$ujt6zcVVrhToZQx$#?SplbTy)VAqc;koaWT*8-Io=krt{lpVE&HR2mb4yiC^5$}$ zU63S{^Ub_rPDTgaSj7m)oF|9VxuLww zBLV0Xly&QL)w*CjsT(CGQ!)gg*?M-TPygW9vA5m0Z{M+FhPvISHEp{;9SyD6wC}bZ zJ9aE>&~(0Lr$Oyd*WQ~>L92wqxiOy=>3+niwToB3B~A$c4r>PgMsaBZsv7Qxnoe4q zBY^CX7Io6HXdJLit4$q1jD=Oex}WY3y*TCK0((p>fsq((NBd;S)0ayc3&43yJ(7kx=v8ZBuHijdWYUnST>tV-*?n@*M!>Q zHZMZD1J0UQWmT}s?s8y}p@eQ!CZmW}o5^YMxucX0c5+vtzw}F}kJVQ8H7v6S#bDGK zc7@GWE4NmAhY^Kdt2eas>+_q)|!@xGf1W$ z-Em9Ewbosq560Y9r`7DP2&C&$+33>hxXb3?wUT1%S{3f>Y~k;TV;Q(v#@_FuwKCkF zd|36TnmpBZEId}t-gqbx$YvLf1(Wrjj@|WDZS~HO1G<%5wpRzE70!HLCeYaGi8xgi zyI6Q!)t-h(d$W@1X{ugX8Fnb70;TDheos6uyr{L~CT*v!b>OuQmGRXVpgnD|p#l%0 zK__IKf_Wf|KidI=ooxazIL_foFJzyF7QiTk$mR>V7))!zoF3i;v?b@6Y=8u_z*;Rl z7n%paQE;9?Mdzd685rW0RO+ogl{FH4>bh#oUh~w)Ga2D?natGE*0`_2RU5WRRNCQMnvNR$ zr#@4YtEtH?N)!qS!J5eD6H~txtf5#eH1*qXEG9gUuSy&AulUo5C5G^&4cbRF{Apa% z`9$fB^dY|`k;iwXH`3SqmQak!#F&Tui$1f7dr&*$o`VwljeA(JowQ)5Ge(XGQ1*Nq$+J{etg{NUE{s!5gC|uRD^NoID&oJTZPex^jGM za(HxfJbGec{MhiJ$+n}DlP9`Usl$eU+oAE}Xf}h`4*nE|DMPr6vaM`bQ{5iHn_&!H zqClME0K{sz6Sy0LDUD$$44q=52tP6HTYkGk`~D;bKH~@*g=wfZum-FzYR*Q^wkL% z5=!AXd{+22grb=6)A&AIq2Dm?Zxqc;#7;IcSGbCOpv@eX&pB2BVv(bhySSTsIF`O~ zKP)W-d5DL3gjeuNUWM+chDUiVkHLy<9j}LKNRl`3M&1Ow6)n7#xA7D#=b>5iEbrhs zp63PL$-8(r@8OGJ5wMr{@qWGpGpNh>a;)rH$yZ@ZtTlW9dr`0B>-h%0k#FLg`4+yF zZ{yqf4!)D`;=B1CzL)Rg`}qOv%Xc*&y_`68Vsd2cNbB+8 zlP3p{42Mom4o*y#qZ&VcVtDN2AmyiR_KS1AE@!CqIPyAjVszL#IzE1)_4we#HNz90 znYSZ`SNwvpog6uW@~197^OGaThbNlO8-6BzBX{-Z4T|PBU1Vz++&7F*SWq!zXs>{T zYj|v^3?L24u?xgz)Mn3lgS<8<{j*^;jUTbuzUM@u0mOH5_}WuIi4ow^@iOGir%nz} zv`!A5yvBa;)X9-CRD0{hsnKE2$x{bgL6kt`P8yX|H9LuOVCbIxc6ef9eB%7rs8?w9 zd4ZsP9+(B!S+(Fp#>)9RG(I{yc;e*nkaw2n@C_#h$A*T7Br241CK4^u`1#X3d1!D{ zL+$hDO9NwIPC=)?^33+u3+R~xQ4Pq-@xifEgQM!i@S)+6>xS(}bF_63cy!&!UE{nn{*NxQqfeX+qW6D zr@x^FpeIdmOPrO{xv@->6YJ36 zMV2d2gini6)4eU>%n309F~(O1`iFlPL7X{5|-2b3V7)9nOr^LYgj}!i93;lyC zAE9KIk&S`(AD_q)Av{IMUIOn*-uP8iepy}!ND>*GaeO`)VTMjch_6IH5B;jA>q zJlX0KWUNhLwAM#mUF(x{u1@4$7zcFyV!v_s2$OIR7{xdy^>7b4C^#mlWSv5rvJZJM z+Q-T0okFU#54qUeC+KXQ!n(E&`4HU4cXb#96^{ldY31C9yn$(`UD)w*rjn*V5`}iviv?>hrDh%AH+yo~_;vKn*wUH_e z;;7O@EZ@!*Yh#kTNRELeIz$v}6O%tlj};|4G!$#olDWx`3ne;i6l=4Sy(o?!Bs@Th zyTKKAz^E94liLI*tB8)j;~x^mJ(P;O=@oads2F0C+r%bmNRN3FADAWJ;3Pb1io3NG zbs4GbGLqF~CO^rKOOhT=Bs{*0uK-k0zbDz^8(GwdZi?2wiq$DOy z6B~1juPCXaBqv)F8ykzS=%}WoCc%>$M~knRsbFL#Rg)Swim%wIq+};clNxV|uXw1U zE#b-rl zc}&`EZhMJ^_tj?r7nX64unjk;8hDb(J%An-HY~q-ud==thPJW(vXTD6v$}J#t+Dgs zpr!nLWAPpwu8V@29l!AvXGOBnsp2>wyVr5Y`ET*jm}0Q*@q%K4$W~ z17jr2yQ7FST%j!~2~ngqM}OF+=x1iur|2g74Z@DcdVh~hRpnn@x^UC57+rJofw_U2 zUxD3$#|6PH5TDvNs3{MO zA}OigavCWo^A1b~o~)!Q`4wT4#qhztlMV)i#MP?!D`Sb!8@`uyrVv+Pw<5k%AzJZ^ zX>>uoHX+Qwf3OCtu~NP2_wO;=3xxw|MH#Rz*7_##Wd^P*+11b%%o6mf_{xByvHjwDK>kKTvB(tvDJ0ZnYGk=1q*t3n=25xd?$3*# zIQaUA#m_>SszXP~xJ`tbt|ju07ZovO&CgMB+Fm)9cwbXEtJ>-XR528d-xaZ!fA)It ztX|d3k>%rPhyB<;Mc4-1GYDDY?4!ej^JA*L!BV2Al2J{zG8xsE0)CC6k8_Quj%OO+ zsV^7^)!ZnfI*Z37z-5<%cUE}1JX<=$jp9b-p6Umw$9m;j)83RzSqBayW+Bf2V)jb& zq9K^y4~0h{O|)E5Rlw~_vM)p{j3*@`O|rCG zK=)RRD{*d=!Q*vcZg&D{Fb7>p*sIMKy!z)WNGpz+v)6$uAiA^zqU@4%@0#()NG7pZf~x$K2O9SwgL*oa&dK9 z%GR5`yzqIfECLpFQs zok??4ak6T^hEHm&R(FzZ4;^A>Ojd2zdd?dPz`l24XSs9L`#gRI(eSLhjGNk6iWW9UM`fPF~Bp3D-d*6bjI!u-wV$__i)Ab zqVV#%Mfj(b;pPy=a3ng-T5lG~fXU#OFv}}_#2ay8W^)0?_`Jc)M} z^}vBK;+H`l^>XZ9dUm2w`vLeq9!OR^B=ZD5I>@r@84#yyodL@z2KZ13EXEwCt;IJgFJAQFieTRw3P7>XA5vV zv%u(BRB%ClyvL5SdF#Xq8%%v78F>;1p|quUMKl9eEX6v8%?BY}lkjE_fE_|)4b{9o zISW`T3dN92Vz{1$l+e?K#}S)M&XCz(I;fpBT=I9;A|X92bqVxVXrTcz6cGyjusmJa z5oB$GJCd-BAqA;*@ENHXt{!ZxeBUfk5cN7?rg(&Lu&^BPZ(LTOaOgs^WZXJbnj1`e zFj>+>H*AH7QGuwz0@Ti~54XP_69X4f5Gn%4zj84W1Bo3lNs-YxGA{Q_@O_~PLQ-(; ziLipv$B00+m?mI&cqnmCIPvKXX$@qOh1ZC*oh{o=pc$>^2VP1PRyWzxj?JvskKiO_3)*`0g44U?vxC|r1ggaA}|8N_F$gq zeWf|`a}e@>@UE=FbN6yPhkndvFeJF;ZbA?_g8N+i4ItQ zvGI|&tmMsDO{0`6z~vz22cT28ibb%&A4u@tNfA&<=Ku{voa0QYgpR7^W>_SCyko>M zILuRrtNt8EgmL#i>7agV8C2vP!dO+yOsFh3B+QiK)P1P8DQ0BJ^5j(f-42GD@; zKmq?eKZ> zdOm7}<$3-C^rR)|mY|O+iHbi-zj~P~cGx}H*lf6(n2BMcA@ESVLITc-F!l?jv~Y_~ zh$b(&QCO=qSbMO&1_kO4SKBqKnXN-4Y1ya?XO_pZ?mUD&J zBqad$JNW}&HwUtWqk23tyLK>^mEBxFJ;|TUGJ3G<&+qEANmg{K@>Gt3m}qg_I7M<2 z7X+2PeQftQ>B-1;(6J<~XXud4q&F658s4KV)s+x&R9v;BappYW+ypdw1m_e;HRwfS zbkqyx4P<{~+S8EFMB2AKYVj|Q>TX~_oRER8K(;(Xdhmv}ZOXfF4??{MVpV(EqFykp zh`LIN3OcKQ4(X!3c5Xna1@LmhSxt*9wW0^2PyFA{h`UgDfl@e46n912L0m$Dit@v9 z!tx1feF*B^{7|r!Dd@vxvUV4e*xwwg^Z7)ISYn5+WW*-e9rU6KGVzIP0{scof&HQQ z@sNxg5dO)*k;q4#{w5T|`J3y%YmnAwme=4t3U&=VGE~I>fa5}I*1N(87ZW-)n;BeM z1nGnCWNm>g-i)R!{Q7>Rf3edDYXIE7GJ#AB2EMqU+w*}F%I$HAe4sGq(N`3A0U8m0 z%WMEhN0jHKTLz&Szwb)LeyoYGGdKVCo2k`xQ7wZ{nmn@CaV<#kajEw*KE~8}lQ~0dZ1oZjkk21n;=3Ot1PqioHlJTCTd~_{_r3T` zNW+t9VbE%~JtPg0WE8H}SMNMjrr@+ytAES}@H0*UO1 zY#AXYC}M(QMi)j2XW{XA(Ddw1`6oWe4iS38qV}p-Mhb=s?C7ciL5%5zk&rN4s=%^X zb|RV084&IQ?Bp%9-xTGu$(X1Zji7MI%=(^tfkT3D5y*EM;Sr>8ge}*B1K6H(l<>Gg zkpX;8d;K;L@Y=nH{XlOR!f|`|;Z$)q#L(smxaY|>@w9hVBsc+a0vM9Q{{7VX6wvth zD#Vu>gFm1FNXA4&c!*)YL4Vmm(FP<{1LLJ!P5Qw0*M}AXvIujv18V)n{f-WR6KeLw z0fb*`x9&*$P*k$O`is}`!WKX!46gZj{} z;tjunkpzaIkmL9f6#G^<6&%3a+!y=7i0MHw9u++#$3u|f^7U{LWIIXD$4OAxml&0y zscjc5bj8Q6kU?w1vteQ(B?SpP?@rBMYwf|u-Glr=5sv{T?i0v&n@NCoS=q;%%G42-Z>V* zsWhVLsoS5WnLy6tz9WUHv5cF6+vStC_`-tl!^tXvlwtO<;;(|E>S^Ky zcC1kQx_AVuGDI(2MP2Yo3bXV1&{5h28o(;Hx!T3%-Nc;{utFA<;#HJ@_Mp(MT$}~U zA+A7hDfNk83qX$KpQqwUXKaBQDfTqGfx*t@6+sDuF;Ei}`$LTEF*LCcL`b0S1qRe> zUobRsu0i#M-^lh1d%y)?!*l%sHS8x9kH8v`^hFC3ji3uqx+%Yb1LpdfSgjJurI+VW z$l^&n37wsaVX+HI)1VRwwMqWe|FVKay|gbBB_teZLTyE%9a!*11Sr(-0&^Wl_%iP> zkhJ}n2t&;Y#K}eZ1K^P*<4L6Dt6@-hAcgcIFb&Ue2HBawERjTq@l&NEQe|LO4cUoP z85Zp#xn#*Ad^q%|Fv4r1o%7e&?cc%sEXugs%n@oW*)*?4gYjfZXL~4@ewkZb6zYh? z5oB~8Ed_UA&?X+eJsbdg8(EBHd$g&L`uR0E{H0F#l>ZonS-jS<4(hqt(l*dRFVF`$ zf}a|~fPK>Qbv25ziZaIhvhs$(jhMe>0@5?^_V7V?-`5)D3t?YYgS>f3nyYtk7d1ro z2TNt#OpnUINsp8FvcLrhbmJo8T5`3}1;SVZLrsE$g-djqUNqG(4}kG79S~ars^w93 z2@%V#bQyJ|4gl0}g#PH=UI~0!)QM}7+mIcMBmy@JrxG04`~oDh%t7<}O>?5F{B33O z-=_OKH4QTaD#iILXGzYS+UARNDi*LAi*s2jM3s-%5&0A6K2f8-OMmuk=6R|03(4b- zRbcBVrlsEIf`lCbp4Ou~^8N6TYfeWfY!JZmDLeteYQQTmdvlC%jpyH1tfDdpP*u$xt^)z+SSN%EyQv;V4~pT184_v^!7J} zbumjj3flrFg;h}z;?a?eGmoFPQ>IhmOEbAIxRd62`Uz{8OTo!Rx}KSl&&Zbqjvk(K zVX0I;lAB$(>$q^K~*v{@l!<`=rkk zT?31!{SjR~i(`9u>hr5^AE#yJEhct5hheF_p{NSP7WboXSu`wxh5Y`o z;JF%w-B3{K13K3I$-L-mD$e~~*)B2-e&>Ts<^n!p;Y|%oQA3g4Y;0)X%L`VnK#8Xm z-Q2RDdlI=S;a65uL*U8tNljwy_VLUnS=@_|JA~77#fwmVP!e3=y9D}x(ybdJm-E?$ zP?&3LD=t^_)6b_|huy#n8ci*)``t!RftT1#k6SPhuLD=l97Lp^=Oh(h(Dhq#lIH@e z(jyi5vk=RAN!bU?Oy0U)c_L#wCTl4sd$^H%{M4R6_C74E`3J|2V3@!p)^OM9N!G-Q zzv{@gH;aWIi#BiU)cm2{)77wwC4R<{(Q(#YgP|}C^skYo%8#HfWf)%G<+93;?YIho zr!8Z@%C0pAARp}CMmnPBEExz>$^tH&4i{^n`>o79eza|cs2?LCU32)jihp)GKrU5z zI$RIu162~&{@5>Hv}Ey)P8tldUdi8P?~f2vKb`bUJjFMePBeT4t_hcPyL^Ie{&-G^ zQ$t-X?SA%`{=3fZZmi4}Y&vLpE+nQ%+1<=qcxth|_Qt_YJ4svpZNT-y6b*3nOaJCx zF~H7%ZpvIA(*~`%lRUvb*`D@-Ld(uLlP3}CWsE@Ns*IVRSLHnBoGOXiWPA+yBE4`y zNs5-@P4a|#&jvsWJ1j=}hTYxZX@#9=nKpB?@O-_`Y^KI!n`y#vho;${ou9tj;H*5q zVC}pZS{T14S*^a#*;pp0+$>_eK7MtkOg)Hb(7is<5%jt(9MoE~ny13F)fumwM4>x7 zLo1g2X)@o(Q)8vDr`<{0>49)j^V6*n?LGKe6LwpTRN-dlVd^ZDw_J^}ehp)@_WsIs zBr89aR~vg+sIlT}v3ID{v4};0v(;|%xEvpS;4ziDmC5iC1a}D$dXw2#PLkR#S>=nUgHy>Dp}2o1PT9x{=_61Ya}K^;D&13ivid8VFyO$W5C=m?T5dMz zNH#DVZl}#{w!c5S=M#Pvp?0Uw$f`n-%4YlC@jKs!t1|Ziz&kGE;gA1y z*-+oWd}DMR%g`J24$lR{3qp!H;>Qw&B=Qpdxx9Jo>+(=!^=;hPD6Mfbq!G7803!F?&0mtnE*lH!~0W%Esie6ZSx-e_kI zmz1}a@P&BMNXd=D9lP_)u>u1vf-cs1i)pa|&GB8pIHMSaVpW=hR7aOMLtsL&L6LMv zp~4n4>|L^e*3=2O<@&fHgePT2gN=!BdBu^ifMf}yxDJ4jpIEmRgUgp2ml(z6co1Rh z=m03#U*S;%XXb88P7f6T1;8Ss$^ez3e5uPrk{z2%DU&}b1N3RQ^0jCw zAYUyfUW!HEIBw^_5i&FUbvLvF`EO`!sL3 zOzpcF0MpUf(yP3tv0)g*@xgv_(m82WeQKSy_T&3tzC>TH!n=4w{D{}Y!jb>}+Jm%a z&XL@Tj%xWfZS_7<3xns4>l*8Q_qK`SXMSPw{$a=W`*Jwl4k-f-Eu+)9kw(xx;5gUiX<2MUfT0HhciW!ns|R_=r)P zapa_@sq@$&_Lo_MDMy>{)hF;L$uG;le*hH*$R;(oJe^>>aoeaWKV0h@R6Y6N5t9cbvvKsZMBV|{Yt2aB}V4PV3z-x#N-#jv=TpGtM6db zLo!0_b?v(pGfV9IjS)Tnvh#7=3A+H#GppzuCH(Tx1r^0B8jF4NGIBcK&I*6kee3x+xW-1}bB+ICXEq>UcB(S3quJ_ai=jaOIV~+V)-^8nAZ^dpB?$-C z2l{0_VD_SP7D&0emvugZZ<$m6`|*Oo&m^r&!re5~+f5P-@V-DPsk6Aa; zY~U?@p1BmuL@i8um8?&mzlfuLQU4KH)F( zoDU7A6E#2fxmnlrTP=s(i`0@&vEkDEa_D=oDTyxRIBfpD(072-S#xamW&{;DL%*XE zoi3Ae^2DWcY=s#(L$>ANz9zA13xKTDZOBOc(m6dSm7f0}c8 zeByB0s3j*!EQ=;?8&ekBsn^;1C0FwVdCBd;q}FI$BiVtOlIL8JS1rurpdJ7E#S6^5 z~xaq%%vcb3$X<$9wWp3b%yeTSQ-3y*m`GrvuC_e0g1=}{2Dg_|9iX=_2EYT zHw5$H(fo8m>>6+AU9coMoY6}mp*W71_%kuHrM%>Y@I7(m=!?g3L=$GdqRQo6?-UGmf&Rd|Ac7~+LwqEIfz<~=*(1cC@WkF; z55_;Z=J!X)kA+CV*`h;bRR!fUqn|;MVz0lzl;H#fJE_1szav8vf=HQ&X=F0b1HwH5 zHl#i1$YJ2eXny=pAOpw#7>6B`<_V1Y$7b(11!jZ!jgIsv~*C z(Q%Aez4UEB%4vn!2 z&_=a~M|-$ryV-drOqm{Z(vEfyuifQ z;C<+wt#xSTK)lUdueHZ_y0>^Bt1AX*hnJ0oeEINyEDW&+i8j54@$(#0el>dktn)Hx z<4Mo;RC*sO{B$~*RZJ}ttyl9nZ<5QeS$jKD?ALB|Q^qyu>EmEVER@H&hP%6d0Nmc7 z$w)WQKxC@l434~xQ!ad_PG~>Tt9P|!SC6fQ-o{TIY4hrI+0E9oXE~=nxIfdDcx|RW z?}Dzd^R?q60=3OF3uwf81$$w0ei4E-%5V`op8+WI=P)8#_wf%Ps`7qsR_483eZW=a zoEUrR*xAu8W{*FVsMhs0SO{#Ut^LKOsnc3=lNs!5Uu>t|?kywBv37d-3|bE`?zZ(y zzvHLS9c7tQ&tOq!35vk+wAqnd#pz-5D!EM7#T*_M`tvLkM#8ii)i{*M)WHZ(>xPV+T3I$Gy)Gr zLj^@4oN=9(h#YZSbVi$%!IzI=hqjwALcoPEx>LR-oO+zg^#CjJpa3G5AjmJQ2^ zSC`(y?d9pJE6COp0{8wE+GFgL4eZy1j?Lyu$7-EkWIQu|Wh|pe`pEu=08{P4jLd!V zecD^&`SN__QHnQbJAu(mBWJ;+DPFM3fwa40O^LEpOAD-_gDS6sQp*}%q%@_2N9vt= z)J%o1@METsu?ycT`=Qb*F5>qtcdpg%d@&O){*F1kFq*R#UhdY}EIaV5Io-}926h__ zF2`Pz%Um6jDRvAc5|~q$-)?zysCFNF*#W|epv4QlX)?y0sx><;`w?CbAShxwC46w+Ll zdfQ`VWcC%RtyMp6qWT0$1UNf$4M(DIK-W-Lu*_3(+ZBNINU@`#&n|nCpd5uWDFzXH z_ZP?9`5GcsEvDAnkG6y1Rn06(ipI;JGZ#v({=UZgecfh#?`h&i%-M}!CfL|kkGaBY z>&d3A&`(&b<*858;X5{LmW)CxaBiju5q-&gc*c27r&E3&?=guc%Oz78u1~KOc*`kj z(7acyYT_H#juDT}En{vgJBHjLO%zMa`CQ&idM^!*5_*78QzO4DA3SJ-X&| zl8XapW69-lT9@`VBIpdUH${@EDx+08+%&qSPC*hz#Bg_FQ3&x5@4aL|A?QI{5^*lK zJTaea%DKe8egsP5pCP2b_+FiPH!ZljtEt0qJI+z7rVY!CJb}G~MGGQ4W;n4F`*3V6 z(eyR+!U@S4f-U-x{^srq&lF5`Lrz3RB2t*-meDi}e~&02epZ|K2iy#k=Um;&mI_)` zSu91db7J9Jt&G%okgOL_Tv@kJ?QU<*Y$K(E@69`r?3!w3XN8H^Dml@ik|onmN>W|0 zwpT9v22H(y9*>jiSID)6PTDP4#jK0&)<~DkI}%Gu7bH8a1O@wP-;vrM3Vd+#KDUBE0}Qg zI3$B__@*3X!@hg<*buoo)=*gNq)3?m2z{E;OAFzF6$O3&Wy%yb!+3^5JleqvV>10% z-P~5cCz{Zm-QV|Sp+IKW>ixkB>>ywD!-Z5YtSevRF$8|^?PKKStjQSaV>$_G*}8^# zVLrC@K;1Y8Xq*aM*(kU0RY5$?&;M1y)#$#1sLnzg{-lGL+vWkJF z5}{YgK5s4hVA|BiQb(xV7#_ffn>-=G_xZGCT=GOUj-vJKbRaO zn}LN0H>!)iYN~pY)`O& zv?j1&>&nncX`pK^21c6~WkQaT_4Ri;Gz;U_5S4O^RtwlfG{@t-^K|S7~Yd7?t(kT~#Fz)w<5w_1g7W z_QQGIe%klSFf~QNGoZ+mWQuzjto}4ud4|3Sw>UjBkGhP+8cL;#{xm3!46;siH7X$v zMrtyxd7bNeTrH9O#W3A|84{s;X)aORUD?L36RFQb5Egab>rB1im^&@jojENLcmQ`v z2#2Z>o_I6q8=IkaVE_<5spK>x;_v?+TO)B$;cdXqOGoGvN*V+9W6BrIe2N zltE?+HQw}?L319Rg}|6+kZaI8+CBZno*{9FnF|dh=n5k)e!A71bQ2GcCnZbvu1OQ( zIn{+?j*d*6kRt35N)bt=DT8|2NnUT5SSONDc9s$bkl!~sfE2PtvX{EZlikQbwX`6k zIg+j(1Ez@Bm`AQL%2i(%cJohg6m#78_GFvzStWwN<4ffZVM|<)|Gn9uw&X$giZ-La ze5_-*wkIgn)zPq96`3#j)y;7V6Jb*YcJvNX_9hZX3vrAl1wfzq+aZ?LGr7fh3jwbY;p8)d+Np)nrQgLtwGPINSe#ISf@6&fa@F^hwoh0Re-@E%;y!~rpJj}^`H zmWFgS0VArOSw%FrR;COm6?Dl;ps1z_>MVXK4`M)avE(ka*&!*Id!fBNM4dH9p|RVf z*iWQ!f~G@ltJ*N9#8)OWjGxU@dJEAB>lIQT2ct!ac+%WLiFn}EO8q(f-Wj?tPp&t|V)x8`H5uvp`~*R94?&+GpFp2jo^YM;QWz+q;^E1( zP0O~nccyA2!1|{TbY95OHqAcCFI_j<8F#z00|sS<8fCD`51^V`fi${$1kk;T^HJkj z>ll_^{BE0+wbJNtivyni_y95aH_&GY{#NY~xXHY%)APRWy_D;JX@Fx-Wl5zluH>6x zq-BZ5sj(j?ke8t_9@-o z85F0g&(bpd7FoMoz@|+b{mE6jHBiS>bHlR_a9Wgkd|S;b(-R9rxEdb~e-)B)G9^i5 zGtI<9iil8m9Sg8xa})Ls+=Fqm^|H}2OUD)EV)FySHWqRiyX209BgUxShH)J0kSwgx z(bdzDqdyc2s$9j`udGSPmxBj4(aQ(=_7_3QQ-kB`mv1Z_@5F`d2an}(yaV2&rNjK= zTq~op#a)@m`!ljDqg}k~!!i6rwb4$thet*hOg<%dmoQ22MTq(Y10B>WpJslfmg|&OS zxuj@G;H^8|#xtrioQjh8ah%#1yU2Rgzi3lr*T#-{>}>MNK6DYo$*L97WWfG%&-y%+p}UXAdR((K zDlQ>ePjJP4bnsL1h+Riy!|q1L#u7)v2`ofpmhLGq|3K{^O1=}z!mlOm4Ahl?h3GW$ zEEj+c)x5e9&B=JFJ)?BueB^ipJCUL=ph`dSJHS2m1FrFD{FE^0{DcZdwz{D(uFa9S zp+H+giaYi_=nnXtz_FN@;`MCjCT);~sTwm(SMgY?Cml+@@}(zs?8Ki8CTlGT!63;9 zH=TaP+?N(UK!&FYVI~^cwV3!Vzhw159af|;ZP|U@LTV@9dgL$H*xj0chiQ0%w)3D_ z$Us;%P+HnrO0M+iX;b$Ks{GnLHz%<#Jzh8cs@{`&^(qvIUiUzWeyn^2OE!L8FP3L# z9?zX%XJIk{wjFdrGD2_x59K`=O%#hR8{AEV<47myOQ_b~f#o=(LAnamp^6)|s-A5p zDOPk&9%a1rBI|*{;&PCEFZ#sNzE^_`4!+Z`Nm3J4kI_LxF{x zIg-Rz2Ql!XfX-_ip0^xpQAq}az4BwU>)h^N{fP9nR&1@dt4lJxDSlHXXH-Xe$31Ct zq7rp{oGSLt^UK7cg}d+6CRtS6D5aO|ShZrAgc+ zkn)Jsea+itbslftH*i?T@fZ#o@8duy3xPF94S{t!8>aVch8$b=yYbLJ^`+q3xQ}Uc zpTV+g%kg*}`cJ(TeYplz-G0OM`gre{GvmsVn}Xh@8`w?6TzA@x!FzwM;k_MPtufSd zzQB}X-JR8~kKL^T#Z5-TYg&d!u;@r*IX9wBdz!+WnHayUeNUH6ccp_&e`aiE#4#Z- z#xdrZ@=blAJ)%C6o!n;l8TNv6{L-W3e3{N_)RAz_cNc7)8rIjPC-Y#&7wdH&OOo5u zctUaSmL>mbcnI+>fn1#tY)m|mfTd{6K#eh3Js`__mP$}cL_EVCn=qI_iYjSmh-Y{^ zNFnrqfKSfbqE~lYd3j1l5t_%G-dn5V$(+6bc8Svy)aFRMVk%b?Q!_*s`;=h*XrGYI|V0Eu@J&+Bgsd9cbA~}e5e@kDbYMp&$zMhQwL7FjTku*~N2&t^wgG_m055fKP^TezUHe!mme2VzV zqqDACXNfbUxnC_Uiz&!GW8^gpVTIZs5%hw8y=%>rnv4#;VJ1PbFpXBsFq)+RoyQorzzSr(s zzrXWmXH97(2!&otX?nS`eGc0YmM%Ppno~mAS60l|?JjYCT^)4X=c}#nrkd};#OoK7 zp1`bjCkS;yzPS+ekW4Xz5L)T1OB2Q!#7jfd2BD*73T|chEL5jRFg6nzR7qd3tCna5 z1GnRD-gjAjRk_CvE{9=n@3$&FZzHo~j(X#~AC%VQzWiAn)pK~7b4^z4_K!25gW z#?)AX`4hgb5b$5_yS)h7EjF9IKEK?$%F5fe^K>vZn=LotaM-NV^GdJn9kEXqsM>Y! z#m0E(7OxS@K5N(9)}-$ISTLCWp8J9Uq074`VI^1fp@~*s{321VX3MJ--`&f>)D1`K z<9(Icx=))1bqj7)rcJ5Xo{ZV-&)CQ&G!&RV9VZP_KqcXm2wM9k{ZiC@c}9`R#NH{n zej{RB+I3%G0y2_Rac=_X@+g`=qEqdEVY}z3ZgXCb>7inXOAT5q0>k+ee}IM-0Q3GNI${UOd_S53 z%3>XXpUA19@1a2I12!Pwf3e3b5O5Fa0D}f}gruh>?2PlL`mHDNMuAlW*qp|_Sf3CT zyk6(K0RJ{Bq!W)0K;)B(B*n_ME`wi3?+y$26HbvtT$yQ1DWK;hbBqck_oKNQct1Mu z1ROWBF(bN$ld6JNvEXlXoEQ+=U4x}42$5dGE-WM-NY(Ff7H7(kHm-!2P|r;@Gd*b@_$e93By;+$p2WbOI$w zh4Mm$Dm9|a4({rLIKXQRw-P!e9xm9SD9Hhwkq3UG{>1y}JwP0)v8RcRzaNW~Vt(vV ze&+c2D*8f5mekSG$22;SlDdLXM6iCtOw6mtL`kSscJETK3nKrJJu=^-lx03%)-ja4(5-Qi-fJl z&1MA|AK0qTo&cG_h@$tQX&D6mzLr1OI`wlRnv>_}dRc2^0Ij zzM%i@X#*VO6v83hf3fLdwP)x7L@;fAG|l|;|C%xN>B9p%a$=_cE(ECMz>56e`S2fk zV5EACjQ$d$;L7nJ03Cf$kpE)&4)pi)M25kF1CWf3$o};m{jabB8UbfOm_XjZ>JZKl zM=;PZx-jmrrbzwB?kJ2XnJE3JJZO;EwAiV5H~5hFdj#MFv;^6N@PwU2?nGO}jKu%S zj7Ts^wn*p6B+07D;mPkP;3-5Y)+i+@PpHzV0o07t_B8M`vNWDFv$W#0#k6m9nskHo z#PnMXp$t!qp-lcv>CEoT(=6^R)2yDq7}*-x)w#mCs=2MX+j*RMb@^cVeSe<`BnmnU z4hsDdMi$l;;S$-Bf|eqc)|4)ko|FNW!IV*#$(Q+%WtBCRjg{Mzcb4Dzf4Lhsm@t*s z2LO>1o&XDihyK;)0|5|O03-j)hfZ9N><}Yrh}=f=ZyF~5--eI=h`?0e2EU(xGG}Z4 z@bb%UP&X6}*Ze6nID*2&@_;5q*9}hk<4l-(cu0-B>Zg8d z;1?gA+j$@DYyEKCG2cyg8s&H!HTWRsQ^$>?qa zoe#wcW=3Z8eO=`)XKQKVM8#9X%9I3+QW1rVnCR#>=F)Un@Q7#KVkcq9_{URJ!^D1I zWHMLvCK82(x2v#p2Mp2>PM^t$==TTnzq2MRdJeix$e05|cWck;d@M1Kim|PM$c7 z`Y4$8aH;la-m61sczd+fJ7V2Im+oja#657^19aO%_?rWK-4QEyJi9%a?vPJ+oZCGe zo_;TP0)jmuo?)>!%)C7%@6fq>u-;*{H|*U#E$^_~dl27Yxi_r7J+-gU-Ft|iVZAq; zzk7O&Pl&|8`-uNai0>iA5HVLW$^Wpzk7gQksJCQh2yiPiAEeSu2-{QsX~$Y1k_w)I zzvgRVHH`~Ow;%_Nh|<vl-gX;-FcXl zk7=K>VXF$5P53oXwen#7hJtb}0_Dy=_Uae zypG(p`{(18+u|PMI~N%t4U$VOB}_2JQJyCW$GlySsJms^g<2GD_f2i#&$z<)50i_I zj^$aj(BRb5YDO%1uyYC@NiqU$JeD2j0}p4Mp(0&NMNcYQydLW!a@(`5JV&;?8;|cU z*?L)8ff9~L+9GUc37=UC>`YQpN|Mb{bGc!iiJGfmETSghEIZX@qDN#2TJ}gKWQZ-M tq(;XS5mOuu?`vc$J$A6_T$c1_mU>mBw(Wkxv6HkOH81?Qr8&re{{bUxYBT@< literal 0 HcmV?d00001 diff --git a/dist/assets/bpmn-font/font/bpmn.woff2 b/dist/assets/bpmn-font/font/bpmn.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..79929b236ad9f2ef354c8675ab785d4d6c11245b GIT binary patch literal 12932 zcmV-~GJDN;Pew8T0RR9105XIC3jhEB0J+!z05UNE0RR9100000000000000000000 z0000SR0dW6jzkKA!#skJZVQ2000A}vBm;vm1Rw>3JqMI18|t1F(`_CKoChF6P5%~+ zL^zYX$s@l3QD2&sMFlqeGAHBM!kBJGTQQ#l&BTG7A*MP z-S23er7y{zVVj2lB)Mp=yzZ{nG>QC-Pk}j13uUk! ze=kE$w8AkQ^jOY1n!zJLh*;N@b^o2buKN{J!rbhA2_EecjtR9Gn17w=D%l|?1-ry9 z!mx$50#Ezbx1FyqDR;8Y*UIOB&MwG7*{X8?+OT0mNi?;>)O|;uTx*gPGg6q>s|ulRLxqH0RMSi_rhL57f<%0Tm9Z?NlS-! z00)8V5Nl^f{5Z`voZw1P2wk|3sRl&QY-08}d;Hz^+kD}WnHRC{ z$V#Xq`Zf7X3LD0@P;h#>_aE@P2P6R$8n-H1yR8uq=T_qJA^`vagO0;_q>Eg2d=d$e z`8kuG zw7bv`7NOI{3VRd(A(Q-y{;l5Ua)>&BpX=}Yz477izu?JNpiq%wB}$bkSD{jsYBg%r zsn?)UlV&YiwQ1L(QP*hS@ zQB_md(A3h_(bdy8Ff=kYF*P%{u(Yzav9%+T$P_A#&S0|G2!*Gz*ePugJUPlE0z?E6 zLnII>LSu?n#Uu@12Tu?evSu??{Uu?w*Wu@7+w;xNP!h@%k4AdW+vfH(

yAbyv?n69)cnI+b z7Cde|D0w2x15Z1|_wu3#7B=ips%7IFcyy*rU=YhFr6(gNIXpqK-u*@eg%KF5#RCL! z^w^deOYd3QVOtm$Y604%X$GQbR>CvXCGnhJ=RvqFQ1cl*;7Mr(pxsW2raYDpCb;`^&trFB(XE&>3+ zWt1jK94T#fDLX-tk#wJz$325z7lPoOdLCE9gsHQlsmcO(h5^1$>LMh%rb-)P>Ncrm zwbE_vD;nSC^V)l}N^r*nz0X@6Wf+HU+PgAhLO=a45G>xnlSd#9$AMZ+EeUYL;W@Z7V9i8 zYss)>szPI390p;`hEf3AxF^SgnAl^|+Ckq4ePB&x4+owWllU-!;d-;&=Q5ypeMz{$ zwF%TAK+vT;z(jymYvp6B2(P3MRoxz=^QkUt`%kp?iZ${7`c zl#Ug}>HdeMA30U_v1^M@J&S)L$3OmP(|Suw3&IU1#5l6Y0yqGe1B-$8A2Ed3q)=2m zMBpWgHDOD9eSC< zRQi>>RH&5x4i=veEIk>#TM_G7!YX0E@^)% z9cpnH~SNgGD3yzer*0c-bupqf;s z2H+NtD%F|w5kDMmhD{f9>a*`rQ-S%?AdwaJPv_XwUt#8a>{Yz%Ni&D@6kIExu4hTs z!d%fzpX$I78Ea&DM&+L^1ETO;wj(^&`FV>CxXiRuveZ;v!)2F#+#2lW zF)d%s`Rf!%@mY}dHa0{*uI_v<8K9cF%QyvQw9b!1 zq#pgixh`#{YG#lj8);^*q8z4=S>ZYszVBm`p21geCzHc(_bzsxSf?4eSkkVf+p? zA}0_km^ko8@waWDFN{|k(3{7vw+>KDCjd-0?&&;()wV;|p^0{PU_)kb*Kj74ynLc$ zGP+}xQUO+Us9b4i{Q#wA8Jl{Z_x=m6@o9gvPbfM?1+RcWh7hcKfsm zK&vi0nd&MOjbPK`@&FLcbFfKcj-ecrpcBGHnmQ?U&Y%mh37WMw=Ms#tgeP>yyD`KT z#4|0(S`bg!XLf7%aemu21+9c)lyy!nJBn9WG39)6e#_;9ZecRtJk64F`E)vk4*gme$X_N{=T8N z^hQpNmMdnW8jy{~2D00ep6^9$RJR;d+Lfru+Z(Gh9zUZlpIYP#kL+_f>=$bzo=72s zG&I!*Y?KR)X&wp`8`+-0ihmd^UCF=Er*f@SO0u~wb;KU1EN$^9SgYfPg*zRJKK*B; zZ3&7noEqDMszu+}p)m}y_YesJ2?zi+iGv_2z1hd|MTa_e+G0% zz-Yu4o(>JIa|F7QZA=9~6zDZaw!t^NC!Pt;Az^If7(mXa0NEVvI!H5c$*Q0KGjTnY zc`<=phzPZH9X59d!x0f&YMU5@|f+1{J4jn_{;Bc`Uz-Pfo5M*|i-;rGf{Use`T@y+Uz5-9=5{!uxt z>l>6J=M+V0laikol!i+e6V>%W0WPUMHOL(_P1(#t)3SR*y~rOD*LJhfqKGg_knSIt~E0gzQ31gKRDYL99!VnA^7KvYu6v~Cj0Mg*XU}hXcl}0BQ6>6GvwwK#va@IMp6+Q7jRT|T+fVvHm8Sul)!u|FgAp|;XL=)?a{QhcqT8tR+bI2jMd)@K z{CK<|mmP{hqU-c*F=VeUiKPH;3qcFD7+5?8Rgpd%vgVe!QDDjU%@fZEn~#aab|Rc= zwFcmrmn+1x=r;Az{_YjtLT{)OLV`!^-YFi=T+6i}2!px}oxSgzrOlL+s=xvCL(_(Q zPs`ZgR{SVDgh;sW7;jQJnlQcf<(SwTKt>0+?!$a%@wg~RG~!Gcti4qzVRk7~eD!e}Uc2H|L(O`z|Ko}075#$h|7mnS`yN?1ka>zgMw-dMb_ z{nUKRaE)j4z1cJQ{1JViqoeCC&Yhe&z0m)|`Aq-*eP4H-uYHHo!<1E&rPDhMpJK#0 z6>yWeLtRV@LwId-inwtdMn__~LcXSuE+D@&jR|I9TVBf-b20;SjBvw@eKUpUlhQZoc+7kfW3If&KZ zm#lV;zGscr$z~NLUx#7WlVR0=4nplPLWC{27utVO0pB~^0)CeX1L6#AcLXWk&EyR> zCNYJUCOzBZvH|&(7V|_$^fSxN+<-{t4Z-Uxj>BBXwTTm!w(27;>ob3|%pBoLa(*Z- zQkS(@L+9<)z*wwnE6tRvvQB=(axbknM4MCJX9#R)u>s)?#qva|5#tPG&|ablIg;+1 z(L&7NdHw6t{OnsQ(S3S5S3dC&HDszh?3_A%eA2er|KqwN-m~<&`jMOO^Orrtc<3bC z@r=ABkDC4u-7!Kj9}heYyM;XY$><|Y5yd?9AYLzg9tUh&Mg<9iu;zPp`g&Y$FS2ip;f)v&|FCH>Mish{vCl1OSCv@%!yex{v5O~p?J6T4JVs9f^P zGi$j?0dBf!GlIe;<51~ZJsS~)v8f^JC-gmr*W{bjvPruMN1YaB+s<~T_~VJh zq!zCT-jb4pgU@KO_m$Lr6Uo`i*a5vE{#L;kz=o>9I4Y$Z8~1>f7(h-8623~CHXSwF znNF;&|0Evi7ccWZmtLpCTJt@_$WHE`tdH*2>S6#jZA*34APK?x_1UQdqgw26hF{jg zCSR%UqljR=b;Zm3%yWn@ z5xz+HGU;6T{}tn@C(^s=iMopJ?*C0=D=Sj*vXy~#iBXNc>~kr8zJ${e1#ug4kBK)# zfo+cl8EMN4DNb9%mQ|KJS38wgwr&h{rYh#75pW}F+9~H(&M?REwDZ5trzkOO=Q}Rm z5Fd#Q|J<+Lfq+Ktd2(Z@w6xjM&--Wt6p)xGXt))UW8QXU5p`gIN*UCB@+?_<_HE!cH8*e3uj$>v zZA;UYRTPVQfUfXEZ|U#vruWq5&Gc@|7W(GAmu>49Gao=0fj{!tF@7TxQ2qk`h>@^6 zk*}~c5&0bl(1m_wh(d9$NM?@BaxQbp4BZtGAF0s=CI{|zHhP-8=1xyuYnNeXq3Jwz z)7z2*Q&{OJWb9-q zSEpKd%w%>`cj+PC-2vk6jZO~Kpno;(G9zE=ka0HR0=BN=4UU(YH2y1a6}v&rarO_t z)ZX3J9!}J5wZDrm6W)-Pez)s4F+!6H6&nH`2ihm#vH-1#4*XWVz0i<=wC>f=L5$r9 zZWzpQm5wtGcJQl#0N#5S2Ql?3;4=JZ!(Pr37TOrC%pww=OqCgLuP^zZ#cJ_gyVVYc zq}#iLAlr-_fqE%r3aZK!&ekn%mn;9qATyNP;YPmF z!mhuXj=H310FOWFKGH!8u1C@8d;ohEk{1tA=@JcUUrcp0boZo;M1|+ce5ILg1## zDBv|iI+LZ0!)y{{JN6dtaN^WUB@%5n{%eV%R9eVID$-7C=HgV(ZN<|^f4Ir#dYHyzS{5lO2{Y8x{b+S#HF4@0HgG@mX?h62pfZah}FU0rY%Nu!<-#@jDz-X?m}=8O&6c4l>!L(+o7-wE0O4E6>I^f;2`-fK(o?AnJ+9#0?xZSEJy? z3C(DeyOCfb$R%)BFtct&ZXoPfu*i-kohIo*@8a z{=MSQAD+n$FSXojcVv-k3~c`SU!=73nBb0R4(H3wByjiQ3u~{{v;rV+K}aF%H5tmQ z7q#4TiMoaM7He3C&=cI2Ar1g_SFGO|1Z}K13x`0M6l(cNL6#T_l`CwMoyXRF;PAmf zSh|4~7)|SDtLffcj$k%sY>qV3=;=`vyDhbYmHthNYFUN@2}Z z#vQLo&u7LmFr|P(1(hO}<9RlNffboS24joVIb+b6O=jr%x>LPNsJQ0HK{6tbt;v|os^GSXfK{@DGKF_G0`}3lwii}oI4;gXc^gU+m{vmpbJAJ@CCA7eE zDT!D{wvi~sL6y+6zoPYo;*nvcJ}-nnj(1Kx?}+%r{vVrKX>0EAnVOIjA@>Jge#

5fC>Rr%c>;6E|Nro6@wl?84j$Q!s=m4o&r@v)b*p(I1=S1f=*n`IeZ%~y z^K;L`l^cowh{th-5?WrU)3*8(Y!3?heM;m-Ih@>{$O^siq_+sG42rguYQlCEfhE8Q`Npf^Bx6NxS~?* zwUl~FqGM|6)D&($+$uQHvV>nNXgPs0Q_NU~;!5-jlzZ*{H5*~dd?t&i-P{O1AALMp zC0iz=m%nIwt+~kAr47O`+Eo|VC2luZ#G#7XaK_HQh5I&qrGp>88W*R@VEsM3b#|;p zi3c%!KKhuTRwOlAJ{HF)5PB6xb@}3uvXYYrng)7j_U?ZN&LuGd`UbQEeVV>O?O!W!)(M_EPpoA^l%vf=qBTA_5s-F&Y9MWY|vtt83L3{r)543eWa> zkNK77hvtZ*EiqY6Hs?&q?nqUXM$b%Qj5+H()4jgRB`JdrCVPvQj%sM=NM-5R8OU+> z#mNXYOBKjba#bK1ijQeuJfe1qbL(VPwZQYytE_>tw*hGcRYjK_rrr+?1XN%Rwgkl_ z#ikH)J&d%`;6z@MTA&vF!On2Ybk~Mxf=iZq7zydrZPp&OI6l;fZyvO>%LK+CJx7ut zO?0=Q)7Tq@3Q?SfnZO=%($Nh*JI;Egd+HrCoGif_QTm=K6&yP|Lo!FphSkD&N4;~h zxOe871ge_a>#7WhW1Rif4C9kM(k$%7^5hUCGC|3dGj?!gc3KBjXh&?F605*3p-waW zpd-4xV||o8v+{il8eFS+O$5@<#Ge!<%P5zKr3Ue=w#ue63*L?TP5^=1WHFoJo{Z^~t7{hx?+^WMt`NW#^_5*>du z6=oEg{@z_)fI6RnfdJsTtR0?}m!2cq659|N( zGyp)Lm-7=n5_=N~i6P~_Zd!4MP?RBFE(Q<>iNnQaq3{kMz>^kBBf=S%A{omWk(U@~ z+Cawg($G?ve_>$zz|n2I?YyIY1M8y%>v-#Vpz8o-?ZJbc+`_UA>dUQbEaJolbflkY zUEHju@k+U@+L*%Zj}l3&R6$6a?u3Y$Izh=G?p#wR>xWDKHvH`(^iGa+VPF|pfsznu(5;|NA#kclC#nlRlS*d}7kiW)*1jNxCh?NQp~2--R(cG- z8&Vn)?!Z!0gN`W#kAMv!r$S6};ij@UVWRyvHD#*c;`I;NhDuUwqAO8WJYW+w%_k@E z`{2|uLcDK2X@MOXK`0~6&vP!?nvvYBr@{lBd@2*N zKM*}TLOBQR$sG4h6WuH2|PxXJ*9S0M`Kk)MH*~8HRf6qzPDe4-ZCMn$hG=uwntAP0Va`j|9qe z!L5MwiKwGy&t)t<05Ie90Yk4nk^ns7a(y6IE$9L@_I090`V=wxtrtC> z*Gq8#zXl!ck9KGiHw6H|-Q!ln{a9N*THoFfb^2xaYRLsa{$8207<~H z`Dm~ng+PGOQWBSR#cy8hrJ8?h-qpVQ`yqJrM4B5W$(Bx+O0s!CDRp%MlnN?$HG4XH zHFugtU66Od(u29%+x)4w_ji)`wY5*aP5USL&ASPd)%sltY+(hLfdX-^D4J?EyzP9N zHnlA&FqvhvLk`LEfkj|~!}QX0yL<CZRj^Gm5N!$>XKt&%V7zr8DSZUmb zq{QING&m)Mp91SL5xRt)SdlU`%`L&bhk0jGK?d);hUaXaEG<4r#bK%08Yop8KmXKs zjr1+tJf0Xt=l>Xinp$&N#xkqLA1#UukM8ZS?wFLNB1PrmVj${#zKRG#KH0pKXjj6uA^tdpuRMQND zq(Dkv!D6!C`teZ}V#XUhKbkp0Q;Y%YHk|-`7>8VxpYO}h<uQ&Zq^SpEo>zs=weBbcBF)q~1fIyi<5?RoZuF+3@A&{qxl~9t{hG z*XwN6L*G)iS1gfT$_vumu&~@(1KQkLSvg;5gkfVb0?aMuvN@aoQcg{yJc-SiyAgM( zUO6QoZu*g%%a$N3bM!~9XesWpd9@WJA{Wgw@# zr!0=8-^|V3Mx{a7T$4GIzGzVo?w{_%ZkAeVQw3AYvefsmtEx0C;oqKRe>1XYV|lw} zr>4>Tg5I5zFF@l;7^8V;U?ihE%70-sg7`q zaOkXc`}K$J=0N9Z08~yEd~ImY!@>DF&6`Y^+4(5p(H68j>SGi zIcRfOU8(($@^iKEkGG2V(*R!B&?>-*(?Uvq=}R zDL$`aU&>y_zIu!QjnU3n&FJn+ELL=x?wMDJ!{^&y&w1T9*%x>Zyk5f<;g||00DkW` zPp+LIUy6S$e~j-G%vjN&1%Sy^;6x=lC)v{XV{P!XwB?gPU%|qd71Y4C{TE{P3LVf$ zB;wc6Lf$WgM4}ypn|y`7g&(udQt409b_(wT1Q(0nV)XnP|JBpD*&~RLFfu+L(9iRq zhY24e{ejQpg5gWft1Tcg;l~rI;wbcPI^i1VcZ{z3|IXK?PJ?*8%h6}dN-iRdT#O%c z`ByhG5x6cPEXY#YG7(V|Z@L3JI}NTfY^hhsqI^;!5M&!GD>Wt^I`a#b;_{BqYYCf^ zWT)~T!%)`8dOu|zy)y6Hs&kzQP>ZEAC3rgi_N?%c3&buFS#P7fji6*HWP->|ty*Uz zHPKRw@uZ}n^-i~*1l|2N7!-zBBi>Y(o}e~Fk*d~bY^1Y(cP6xI7Tj<{joqW-twGo?RudntwZ-=cJ^aG|bb7mNEtN|_Yg#hJ=< zzeR(bJXGk~=sG*bwZ%0~=Gx?1DjT%+kA%wpvPjR^w?fi;($|IH-KJ(M{_AT@De{I{ zIQb+U@0gB%GU;c#fdpGIufJOHO@56T_)WczX0zksy_vYlOx)hZH+=7=J>@PN!q<0u z#0|=K60yC29WI@NvCsI*&?N{A?^ZS7ej2o357UIMNkP0??}#1CCFSGCFPX-+{Fxk` zd132fd0|DrP5sjnX48cW(9TOY>bGop$-Te2Pn>pQbqOnGuy)Mp5*zG3Km67 zw3I%iG*$d^9enp14^_g;tX;f87DVwfeM_5e^}ls`BXO4lcC>X*Ob~(yiDVO9> z(mWi||2Uk_B@Ox+U_FJ7n%2{=-Pg$>m9Vqq@|xPa#U7F-WEtajHk$P{WM-{g{C@%O zAqy_YPE}eWQ2UkdEU_wKL;&;4aK^g~@-+>5w7pByx557;{x73x*18{G*P_Oyjx*Kw zj#g}2zk6Ba+MJEkS)(*}+TWJlij8w0ZU4s9uDJ0q7I*wg`|bJ~w!b_e=szi)NGJOa zsg*}+ zS>NU1x06V35q_Pj{28a(AtawCd6WH*sEU4dF29IceTY7-elB&!UuQfZCwn0&2GoY* z^oK5ftwSECS!dX1Sf}G$e`Q$fXy}3uNiALS9C;jXp*5RN|LPuz&UO`Dq7KY4|F59S zc!@gf$|Kj~_t9!xs5EAJPtSvHZnsFK@iS9RYa#%|lHGkJ$~bo^P&Ng~^T3uc&wfuB*K$9xr=54*8ovt*Pgn7-wO#KM4XK{33NgIBGbEE|TiHh(~aH!t0r z%hvjE-5nHJ(;;x-Nady-jx5_rL`Q{Re%$0P`OKr~r+zNTi!%A-{Qz(>iPU*sbf#=X zVzRwgC2}MCcH^y7)z+D7zE`d>M^A???(e}&m>1+EU>SBqxZ09Tu}m4a8FTeZbRnqN ziu@yG72^mD0-&8jT=tK>ifpQH1eBG!Tt)E|QLG(=!r&l_Cm9_G20pIz{!(-6boU4V zEz`+@Ii?fS1b&U=3f26nz88?vj0a7(0uBe#95T_V4_?rApR=7k@$-W5pBJ#1XRp$o z(EPFEP|ah|9pn4~nv)wMj?1bL>jC06S0mX16W#6IR02LoA;DR1uEN>^-~@-jAszg+ z2Vcgl7I;cDD7s|s-Bur`guMb-a7g=h9=H@3r1Fsty#acJKiGnj;e#>s+~G%sz$7@7 z0XZlUYJb>+J^rVGjCP|DTm+@3c`y%LIK@^31}fO2tUzVI7CVlm3|xyf$_`Wjujd;_ zkA6TOr9X(KvoSd!d4yTSoE1bDrZJ^pqt3oAWGs1Bo(lzr1haoVnI;qP>wC{b<6s?+ zfF4_~c0wVpw)Q zlsj|~RIh7_h58^Y)sTY+8fX=5?$A*V6K4^*c)i&rHKD#8}dU}szTlcR-dSl4qf{mjM1o*4TJ*H4h2&|WDq1P>MR6ut( zp}9Q>omn;rAm9@_`nOZI{NajY0r>gvcLi{D*ACkwUj(#G_zw#bzDEh3n5Ww3%^pnz z-_!hr&DXYyaiK;*j!UJ#5Y&tYf<%o-o(8c*dzhr%Qbqb@5>pBdnY%7h<+f8PbHRDg zI}qgiAX4h1Sia9;l75w{^-U(AX}zAn?hrY4%Gx4x9|KD(~*SznH%l&$XT`fQ|-n8-w~lt&q23C>VOGG>-s z6%*`nNophkxIF`d^~c!ZoJfNdJ253=>?26qP8c=bCabJhyHQa>rNqANNbp8R_hl>z z4d2hO|J)d<&4^ivR3=v_Rcej4F5PeFw)pdrIXj2bh}nJ{U}v>CJJ z%v-Q%$+8uz)~wsGX-i%~QAt@vRZU$(Q%hS%S5M!-(8$=t)Xdz%(#qP#){aObQ>ZjL zgUMoZxDor`^Vx^nM`!mZmpJ*4UM`jFtAe9jCw=RF?+d)*A)}anef@Ft<<3Ak>k`dn zonS2MhTITkY3`^kUyW%_+biI@jniYFDyC&mpT<)}G?^j;0l8=n@MmXzRxJmG$%pev zQkbVf!XPu#B)HsUlN0QStTj(rt2M#Lg+nK%DmMSzmh7%^$f'); + padding: 0 8px; + color: var(--breadcrumbs-arrow-color); + height: 1em; +} + +.bjs-breadcrumbs .bjs-crumb { + display: inline-block; + max-width: 200px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.bjs-drilldown { + width: 20px; + height: 20px; + + padding: 0px; + margin-left: -20px; + + cursor: pointer; + border: none; + border-radius: 2px; + outline: none; + + fill: var(--drilldown-fill-color); + background-color: var(--drilldown-background-color); +} + +.bjs-drilldown-empty { + display: none; +} + +.selected .bjs-drilldown-empty { + display: inherit; +} + +[data-popup="align-elements"] .djs-popup-body { + display: flex; +} + +[data-popup="align-elements"] .djs-popup-body [data-group] + [data-group] { + border-left: 1px solid var(--popup-border-color); +} + +[data-popup="align-elements"] [data-group="align"] { + display: grid; + grid-template-columns: repeat(3, 1fr); +} + +[data-popup="align-elements"] .djs-popup-body .entry { + padding: 6px 8px; +} + +[data-popup="align-elements"] .djs-popup-body .entry img { + display: block; + + height: 20px; + width: 20px; +} + +[data-popup="align-elements"] .bjs-align-elements-menu-entry { + display: inline-block; +} diff --git a/dist/assets/diagram-js.css b/dist/assets/diagram-js.css new file mode 100644 index 0000000..d882133 --- /dev/null +++ b/dist/assets/diagram-js.css @@ -0,0 +1,823 @@ +/** + * color definitions + */ +.djs-container { + --color-grey-225-10-15: hsl(225, 10%, 15%); + --color-grey-225-10-35: hsl(225, 10%, 35%); + --color-grey-225-10-55: hsl(225, 10%, 55%); + --color-grey-225-10-75: hsl(225, 10%, 75%); + --color-grey-225-10-80: hsl(225, 10%, 80%); + --color-grey-225-10-85: hsl(225, 10%, 85%); + --color-grey-225-10-90: hsl(225, 10%, 90%); + --color-grey-225-10-95: hsl(225, 10%, 95%); + --color-grey-225-10-97: hsl(225, 10%, 97%); + + --color-blue-205-100-45: hsl(205, 100%, 45%); + --color-blue-205-100-45-opacity-30: hsla(205, 100%, 45%, 30%); + --color-blue-205-100-50: hsl(205, 100%, 50%); + --color-blue-205-100-50-opacity-15: hsla(205, 100%, 50%, 15%); + --color-blue-205-100-70: hsl(205, 100%, 75%); + --color-blue-205-100-95: hsl(205, 100%, 95%); + + --color-green-150-86-44: hsl(150, 86%, 44%); + + --color-red-360-100-40: hsl(360, 100%, 40%); + --color-red-360-100-45: hsl(360, 100%, 45%); + --color-red-360-100-92: hsl(360, 100%, 92%); + --color-red-360-100-97: hsl(360, 100%, 97%); + + --color-white: hsl(0, 0%, 100%); + --color-black: hsl(0, 0%, 0%); + --color-black-opacity-10: hsla(0, 0%, 0%, 10%); + + --canvas-fill-color: var(--color-white); + + --bendpoint-fill-color: var(--color-blue-205-100-45); + --bendpoint-stroke-color: var(--canvas-fill-color); + + --context-pad-entry-background-color: var(--color-white); + --context-pad-entry-hover-background-color: var(--color-grey-225-10-95); + + --element-dragger-color: var(--color-blue-205-100-50); + --element-hover-outline-fill-color: var(--color-blue-205-100-45); + --element-selected-outline-stroke-color: var(--color-blue-205-100-50); + --element-selected-outline-secondary-stroke-color: var(--color-blue-205-100-70); + + --lasso-fill-color: var(--color-blue-205-100-50-opacity-15); + --lasso-stroke-color: var(--element-selected-outline-stroke-color); + + --palette-entry-color: var(--color-grey-225-10-15); + --palette-entry-hover-color: var(--color-blue-205-100-45); + --palette-entry-selected-color: var(--color-blue-205-100-50); + --palette-separator-color: var(--color-grey-225-10-75); + --palette-toggle-hover-background-color: var(--color-grey-225-10-55); + --palette-background-color: var(--color-grey-225-10-97); + --palette-border-color: var(--color-grey-225-10-75); + + --popup-body-background-color: var(--color-white); + --popup-header-entry-selected-color: var(--color-blue-205-100-50); + --popup-header-entry-selected-background-color: var(--color-black-opacity-10); + --popup-header-separator-color: var(--color-grey-225-10-75); + --popup-background-color: var(--color-grey-225-10-97); + --popup-border-color: var(--color-grey-225-10-75); + + --resizer-fill-color: var(--color-blue-205-100-45); + --resizer-stroke-color: var(--canvas-fill-color); + + --search-container-background-color: var(--color-grey-225-10-97); + --search-container-border-color: var(--color-blue-205-100-50); + --search-container-box-shadow-color: var(--color-blue-205-100-95); + --search-container-box-shadow-inset-color: var(--color-grey-225-10-80); + --search-input-border-color: var(--color-grey-225-10-75); + --search-result-border-color: var(--color-grey-225-10-75); + --search-result-highlight-color: var(--color-black); + --search-result-selected-color: var(--color-blue-205-100-45-opacity-30); + + --shape-attach-allowed-stroke-color: var(--color-blue-205-100-50); + --shape-connect-allowed-fill-color: var(--color-grey-225-10-97); + --shape-drop-allowed-fill-color: var(--color-grey-225-10-97); + --shape-drop-not-allowed-fill-color: var(--color-red-360-100-97); + --shape-resize-preview-stroke-color: var(--color-blue-205-100-50); + + --snap-line-stroke-color: var(--color-blue-205-100-45-opacity-30); + + --space-tool-crosshair-stroke-color: var(--color-black); + + --tooltip-error-background-color: var(--color-red-360-100-97); + --tooltip-error-border-color: var(--color-red-360-100-45); + --tooltip-error-color: var(--color-red-360-100-45); +} + +/** + * outline styles + */ + +.djs-outline, +.djs-selection-outline { + fill: none; + shape-rendering: geometricPrecision; + stroke-width: 2px; +} + +.djs-outline { + visibility: hidden; +} + +.djs-selection-outline { + stroke: var(--element-selected-outline-stroke-color); +} + +.djs-element.selected .djs-outline { + visibility: visible; + + stroke: var(--element-selected-outline-stroke-color); +} + +.djs-multi-select .djs-element.selected .djs-outline { + stroke: var(--element-selected-outline-secondary-stroke-color); +} + +.djs-shape.connect-ok .djs-visual > :nth-child(1) { + fill: var(--shape-connect-allowed-fill-color) !important; +} + +.djs-shape.connect-not-ok .djs-visual > :nth-child(1), +.djs-shape.drop-not-ok .djs-visual > :nth-child(1) { + fill: var(--shape-drop-not-allowed-fill-color) !important; +} + +.djs-shape.new-parent .djs-visual > :nth-child(1) { + fill: var(--shape-drop-allowed-fill-color) !important; +} + +svg.drop-not-ok { + background: var(--shape-drop-not-allowed-fill-color) !important; +} + +svg.new-parent { + background: var(--shape-drop-allowed-fill-color) !important; +} + + +/* Override move cursor during drop and connect */ +.drop-not-ok, +.connect-not-ok, +.drop-not-ok *, +.connect-not-ok * { + cursor: not-allowed !important; +} + +.drop-ok, +.connect-ok, +.drop-ok *, +.connect-ok * { + cursor: default !important; +} + +.djs-element.attach-ok .djs-visual > :nth-child(1) { + stroke-width: 5px !important; + stroke: var(--shape-attach-allowed-stroke-color) !important; +} + +.djs-frame.connect-not-ok .djs-visual > :nth-child(1), +.djs-frame.drop-not-ok .djs-visual > :nth-child(1) { + stroke-width: 3px !important; + stroke: var(--shape-drop-not-allowed-fill-color) !important; + fill: none !important; +} + +/** +* Selection box style +* +*/ +.djs-lasso-overlay { + fill: var(--lasso-fill-color); + stroke: var(--lasso-stroke-color); + stroke-width: 2px; + shape-rendering: geometricPrecision; + pointer-events: none; +} + +/** + * Resize styles + */ +.djs-resize-overlay { + fill: none; + + stroke-dasharray: 5 1 3 1; + stroke: var(--shape-resize-preview-stroke-color); + + pointer-events: none; +} + +.djs-resizer-hit { + fill: none; + pointer-events: all; +} + +.djs-resizer-visual { + fill: var(--resizer-fill-color); + stroke-width: 1px; + stroke: var(--resizer-stroke-color); + shape-rendering: geometricPrecision; +} + +.djs-resizer:hover .djs-resizer-visual { + stroke: var(--resizer-stroke-color); + stroke-opacity: 1; +} + +.djs-cursor-resize-ns, +.djs-resizer-n, +.djs-resizer-s { + cursor: ns-resize; +} + +.djs-cursor-resize-ew, +.djs-resizer-e, +.djs-resizer-w { + cursor: ew-resize; +} + +.djs-cursor-resize-nwse, +.djs-resizer-nw, +.djs-resizer-se { + cursor: nwse-resize; +} + +.djs-cursor-resize-nesw, +.djs-resizer-ne, +.djs-resizer-sw { + cursor: nesw-resize; +} + +.djs-shape.djs-resizing > .djs-outline { + visibility: hidden !important; +} + +.djs-shape.djs-resizing > .djs-resizer { + visibility: hidden; +} + +.djs-dragger > .djs-resizer { + visibility: hidden; +} + +/** + * drag styles + */ +.djs-dragger * { + fill: none !important; + stroke: var(--element-dragger-color) !important; +} + +.djs-dragger tspan, +.djs-dragger text { + fill: var(--element-dragger-color) !important; + stroke: none !important; +} + +marker.djs-dragger circle, +marker.djs-dragger path, +marker.djs-dragger polygon, +marker.djs-dragger polyline, +marker.djs-dragger rect { + fill: var(--element-dragger-color) !important; + stroke: none !important; +} + +marker.djs-dragger text, +marker.djs-dragger tspan { + fill: none !important; + stroke: var(--element-dragger-color) !important; +} + +.djs-dragging { + opacity: 0.3; +} + +.djs-dragging, +.djs-dragging > * { + pointer-events: none !important; +} + +.djs-dragging .djs-context-pad, +.djs-dragging .djs-outline { + display: none !important; +} + +/** + * no pointer events for visual + */ +.djs-visual, +.djs-outline { + pointer-events: none; +} + +.djs-element.attach-ok .djs-hit { + stroke-width: 60px !important; +} + +/** + * all pointer events for hit shape + */ +.djs-element > .djs-hit-all, +.djs-element > .djs-hit-no-move { + pointer-events: all; +} + +.djs-element > .djs-hit-stroke, +.djs-element > .djs-hit-click-stroke { + pointer-events: stroke; +} + +/** + * shape / connection basic styles + */ +.djs-connection .djs-visual { + stroke-width: 2px; + fill: none; +} + +.djs-cursor-grab { + cursor: -webkit-grab; + cursor: -moz-grab; + cursor: grab; +} + +.djs-cursor-grabbing { + cursor: -webkit-grabbing; + cursor: -moz-grabbing; + cursor: grabbing; +} + +.djs-cursor-crosshair { + cursor: crosshair; +} + +.djs-cursor-move { + cursor: move; +} + +.djs-cursor-resize-ns { + cursor: ns-resize; +} + +.djs-cursor-resize-ew { + cursor: ew-resize; +} + + +/** + * snapping + */ +.djs-snap-line { + stroke: var(--snap-line-stroke-color); + stroke-linecap: round; + stroke-width: 2px; + pointer-events: none; +} + +/** + * snapping + */ +.djs-crosshair { + stroke: var(--space-tool-crosshair-stroke-color); + stroke-linecap: round; + stroke-width: 1px; + pointer-events: none; + shape-rendering: geometricPrecision; + stroke-dasharray: 5, 5; +} + +/** + * palette + */ + +.djs-palette { + position: absolute; + left: 20px; + top: 20px; + + box-sizing: border-box; + width: 48px; +} + +.djs-palette .separator { + margin: 5px; + padding-top: 5px; + + border: none; + border-bottom: solid 1px var(--palette-separator-color); + + clear: both; +} + +.djs-palette .entry:before { + vertical-align: initial; +} + +.djs-palette .djs-palette-toggle { + cursor: pointer; +} + +.djs-palette .entry, +.djs-palette .djs-palette-toggle { + color: var(--palette-entry-color); + font-size: 30px; + + text-align: center; +} + +.djs-palette .entry { + float: left; +} + +.djs-palette .entry img { + max-width: 100%; +} + +.djs-palette .djs-palette-entries:after { + content: ''; + display: table; + clear: both; +} + +.djs-palette .djs-palette-toggle:hover { + background: var(--palette-toggle-hover-background-color); +} + +.djs-palette .entry:hover { + color: var(--palette-entry-hover-color); +} + +.djs-palette .highlighted-entry { + color: var(--palette-entry-selected-color) !important; +} + +.djs-palette .entry, +.djs-palette .djs-palette-toggle { + width: 46px; + height: 46px; + line-height: 46px; + cursor: default; +} + +/** + * Palette open / two-column layout is controlled via + * classes on the palette. Events to hook into palette + * changed life-cycle are available in addition. + */ +.djs-palette.two-column.open { + width: 94px; +} + +.djs-palette:not(.open) .djs-palette-entries { + display: none; +} + +.djs-palette:not(.open) { + overflow: hidden; +} + +.djs-palette.open .djs-palette-toggle { + display: none; +} + +/** + * context-pad + */ +.djs-overlay-context-pad { + width: 72px; + z-index: 100; +} + +.djs-context-pad { + position: absolute; + display: none; + pointer-events: none; + line-height: 1; +} + +.djs-context-pad .entry { + width: 22px; + height: 22px; + text-align: center; + display: inline-block; + font-size: 22px; + margin: 0 2px 2px 0; + + border-radius: 3px; + + cursor: default; + + background-color: var(--context-pad-entry-background-color); + box-shadow: 0 0 2px 1px var(--context-pad-entry-background-color); + pointer-events: all; + vertical-align: middle; +} + +.djs-context-pad .entry:hover { + background: var(--context-pad-entry-hover-background-color); +} + +.djs-context-pad.open { + display: block; +} + +/** + * popup styles + */ +.djs-popup .entry { + line-height: 20px; + white-space: nowrap; + cursor: default; +} + +/* larger font for prefixed icons */ +.djs-popup .entry:before { + vertical-align: middle; + font-size: 20px; +} + +.djs-popup .entry > span { + vertical-align: middle; + font-size: 14px; +} + +.djs-popup .entry:hover, +.djs-popup .entry.active:hover { + background: var(--popup-header-entry-selected-background-color); +} + +.djs-popup .entry.disabled { + background: inherit; +} + +.djs-popup .djs-popup-header .entry { + display: inline-block; + padding: 2px 3px 2px 3px; + + border: solid 1px transparent; + border-radius: 3px; +} + +.djs-popup .djs-popup-header .entry.active { + color: var(--popup-header-entry-selected-color); + border: solid 1px var(--popup-header-entry-selected-color); + background-color: var(--popup-header-entry-selected-background-color); +} + +.djs-popup-body .entry { + padding: 4px 5px; +} + +.djs-popup-body .entry > span { + margin-left: 5px; +} + +.djs-popup-body { + background-color: var(--popup-body-background-color); +} + +.djs-popup-header { + border-bottom: 1px solid var(--popup-header-separator-color); +} + +.djs-popup-header .entry { + margin: 1px; + margin-left: 3px; +} + +.djs-popup-header .entry:last-child { + margin-right: 3px; +} + +/** + * popup / palette styles + */ +.djs-palette { + background: var(--palette-background-color); + border: solid 1px var(--palette-border-color); + border-radius: 2px; +} + +.djs-popup { + background: var(--popup-background-color); + border: solid 1px var(--popup-border-color); + border-radius: 2px; +} + +/** + * touch + */ + +.djs-shape, +.djs-connection { + touch-action: none; +} + +.djs-segment-dragger, +.djs-bendpoint { + display: none; +} + +/** + * bendpoints + */ +.djs-segment-dragger .djs-visual { + display: none; + + fill: var(--bendpoint-fill-color); + stroke: var(--bendpoint-stroke-color); + stroke-width: 1px; + stroke-opacity: 1; +} + +.djs-segment-dragger:hover .djs-visual { + display: block; +} + +.djs-bendpoint .djs-visual { + fill: var(--bendpoint-fill-color); + stroke: var(--bendpoint-stroke-color); + stroke-width: 1px; +} + +.djs-segment-dragger:hover, +.djs-bendpoints.hover .djs-segment-dragger, +.djs-bendpoints.selected .djs-segment-dragger, +.djs-bendpoint:hover, +.djs-bendpoints.hover .djs-bendpoint, +.djs-bendpoints.selected .djs-bendpoint { + display: block; +} + +.djs-drag-active .djs-bendpoints * { + display: none; +} + +.djs-bendpoints:not(.hover) .floating { + display: none; +} + +.djs-segment-dragger:hover .djs-visual, +.djs-segment-dragger.djs-dragging .djs-visual, +.djs-bendpoint:hover .djs-visual, +.djs-bendpoint.floating .djs-visual { + fill: var(--bendpoint-fill-color); + stroke: var(--bendpoint-stroke-color); + stroke-opacity: 1; +} + +.djs-bendpoint.floating .djs-hit { + pointer-events: none; +} + +.djs-segment-dragger .djs-hit, +.djs-bendpoint .djs-hit { + fill: none; + pointer-events: all; +} + +.djs-segment-dragger.horizontal .djs-hit { + cursor: ns-resize; +} + +.djs-segment-dragger.vertical .djs-hit { + cursor: ew-resize; +} + +.djs-segment-dragger.djs-dragging .djs-hit { + pointer-events: none; +} + +.djs-updating, +.djs-updating > * { + pointer-events: none !important; +} + +.djs-updating .djs-context-pad, +.djs-updating .djs-outline, +.djs-updating .djs-bendpoint, +.djs-multi-select .djs-bendpoint, +.djs-multi-select .djs-segment-dragger, +.connect-ok .djs-bendpoint, +.connect-not-ok .djs-bendpoint, +.drop-ok .djs-bendpoint, +.drop-not-ok .djs-bendpoint { + display: none !important; +} + +.djs-segment-dragger.djs-dragging, +.djs-bendpoint.djs-dragging { + display: block; + opacity: 1.0; +} + + +/** + * tooltips + */ +.djs-tooltip-error { + width: 160px; + padding: 6px; + + background: var(--tooltip-error-background-color); + border: solid 1px var(--tooltip-error-border-color); + border-radius: 2px; + color: var(--tooltip-error-color); + font-size: 12px; + line-height: 16px; + + opacity: 0.75; +} + +.djs-tooltip-error:hover { + opacity: 1; +} + + +/** + * search pad + */ +.djs-search-container { + position: absolute; + top: 20px; + left: 0; + right: 0; + margin-left: auto; + margin-right: auto; + + width: 25%; + min-width: 300px; + max-width: 400px; + z-index: 10; + + font-size: 1.05em; + opacity: 0.9; + background: var(--search-container-background-color); + border: solid 1px var(--search-container-border-color); + border-radius: 2px; + box-shadow: 0 0 0 2px var(--search-container-box-shadow-color), 0 0 0 1px var(--search-container-box-shadow-inset-color) inset; +} + +.djs-search-container:not(.open) { + display: none; +} + +.djs-search-input input { + font-size: 1.05em; + width: 100%; + padding: 6px 10px; + border: 1px solid var(--search-input-border-color); + box-sizing: border-box; +} + +.djs-search-input input:focus { + outline: none; + border-color: var(--search-input-border-color); +} + +.djs-search-results { + position: relative; + overflow-y: auto; + max-height: 200px; +} + +.djs-search-results:hover { + cursor: pointer; +} + +.djs-search-result { + width: 100%; + padding: 6px 10px; + background: white; + border-bottom: solid 1px var(--search-result-border-color); + border-radius: 1px; +} + +.djs-search-highlight { + color: var(--search-result-highlight-color); +} + +.djs-search-result-primary { + margin: 0 0 10px; +} + +.djs-search-result-secondary { + font-family: monospace; + margin: 0; +} + +.djs-search-result:hover { + background: var(--search-result-selected-color); +} + +.djs-search-result-selected { + background: var(--search-result-selected-color); +} + +.djs-search-result-selected:hover { + background: var(--search-result-selected-color); +} + +.djs-search-overlay { + background: var(--search-result-selected-color); +} + +/** + * hidden styles + */ +.djs-element-hidden, +.djs-element-hidden .djs-hit, +.djs-element-hidden .djs-outline, +.djs-label-hidden .djs-label { + display: none !important; +} + +.djs-element .djs-hit-stroke, +.djs-element .djs-hit-click-stroke, +.djs-element .djs-hit-all { + cursor: move; +} \ No newline at end of file diff --git a/dist/bpmn-modeler.development.js b/dist/bpmn-modeler.development.js new file mode 100644 index 0000000..1072eab --- /dev/null +++ b/dist/bpmn-modeler.development.js @@ -0,0 +1,60829 @@ +/*! + * bpmn-js - bpmn-modeler v9.4.0 + * + * Copyright (c) 2014-present, camunda Services GmbH + * + * Released under the bpmn.io license + * http://bpmn.io/license + * + * Source Code: https://github.com/bpmn-io/bpmn-js + * + * Date: 2022-08-22 + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.BpmnJS = factory()); +})(this, (function () { 'use strict'; + + function e(e,t){t&&(e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}));} + + function createCommonjsModule(fn, module) { + return module = { exports: {} }, fn(module, module.exports), module.exports; + } + + var hat_1 = createCommonjsModule(function (module) { + var hat = module.exports = function (bits, base) { + if (!base) base = 16; + if (bits === undefined) bits = 128; + if (bits <= 0) return '0'; + + var digits = Math.log(Math.pow(2, bits)) / Math.log(base); + for (var i = 2; digits === Infinity; i *= 2) { + digits = Math.log(Math.pow(2, bits / i)) / Math.log(base) * i; + } + + var rem = digits - Math.floor(digits); + + var res = ''; + + for (var i = 0; i < Math.floor(digits); i++) { + var x = Math.floor(Math.random() * base).toString(base); + res = x + res; + } + + if (rem) { + var b = Math.pow(base, rem); + var x = Math.floor(Math.random() * b).toString(base); + res = x + res; + } + + var parsed = parseInt(res, base); + if (parsed !== Infinity && parsed >= Math.pow(2, bits)) { + return hat(bits, base) + } + else return res; + }; + + hat.rack = function (bits, base, expandBy) { + var fn = function (data) { + var iters = 0; + do { + if (iters ++ > 10) { + if (expandBy) bits += expandBy; + else throw new Error('too many ID collisions, use more bits') + } + + var id = hat(bits, base); + } while (Object.hasOwnProperty.call(hats, id)); + + hats[id] = data; + return id; + }; + var hats = fn.hats = {}; + + fn.get = function (id) { + return fn.hats[id]; + }; + + fn.set = function (id, value) { + fn.hats[id] = value; + return fn; + }; + + fn.bits = bits || 128; + fn.base = base || 16; + return fn; + }; + }); + + /** + * Create a new id generator / cache instance. + * + * You may optionally provide a seed that is used internally. + * + * @param {Seed} seed + */ + + function Ids(seed) { + if (!(this instanceof Ids)) { + return new Ids(seed); + } + + seed = seed || [128, 36, 1]; + this._seed = seed.length ? hat_1.rack(seed[0], seed[1], seed[2]) : seed; + } + /** + * Generate a next id. + * + * @param {Object} [element] element to bind the id to + * + * @return {String} id + */ + + Ids.prototype.next = function (element) { + return this._seed(element || true); + }; + /** + * Generate a next id with a given prefix. + * + * @param {Object} [element] element to bind the id to + * + * @return {String} id + */ + + + Ids.prototype.nextPrefixed = function (prefix, element) { + var id; + + do { + id = prefix + this.next(true); + } while (this.assigned(id)); // claim {prefix}{random} + + + this.claim(id, element); // return + + return id; + }; + /** + * Manually claim an existing id. + * + * @param {String} id + * @param {String} [element] element the id is claimed by + */ + + + Ids.prototype.claim = function (id, element) { + this._seed.set(id, element || true); + }; + /** + * Returns true if the given id has already been assigned. + * + * @param {String} id + * @return {Boolean} + */ + + + Ids.prototype.assigned = function (id) { + return this._seed.get(id) || false; + }; + /** + * Unclaim an id. + * + * @param {String} id the id to unclaim + */ + + + Ids.prototype.unclaim = function (id) { + delete this._seed.hats[id]; + }; + /** + * Clear all claimed ids. + */ + + + Ids.prototype.clear = function () { + var hats = this._seed.hats, + id; + + for (id in hats) { + this.unclaim(id); + } + }; + + /** + * Flatten array, one level deep. + * + * @param {Array} arr + * + * @return {Array} + */ + function flatten(arr) { + return Array.prototype.concat.apply([], arr); + } + + var nativeToString$1 = Object.prototype.toString; + var nativeHasOwnProperty$1 = Object.prototype.hasOwnProperty; + function isUndefined$2(obj) { + return obj === undefined; + } + function isDefined(obj) { + return obj !== undefined; + } + function isNil(obj) { + return obj == null; + } + function isArray$3(obj) { + return nativeToString$1.call(obj) === '[object Array]'; + } + function isObject(obj) { + return nativeToString$1.call(obj) === '[object Object]'; + } + function isNumber(obj) { + return nativeToString$1.call(obj) === '[object Number]'; + } + function isFunction(obj) { + var tag = nativeToString$1.call(obj); + return tag === '[object Function]' || tag === '[object AsyncFunction]' || tag === '[object GeneratorFunction]' || tag === '[object AsyncGeneratorFunction]' || tag === '[object Proxy]'; + } + function isString(obj) { + return nativeToString$1.call(obj) === '[object String]'; + } + /** + * Ensure collection is an array. + * + * @param {Object} obj + */ + + function ensureArray(obj) { + if (isArray$3(obj)) { + return; + } + + throw new Error('must supply array'); + } + /** + * Return true, if target owns a property with the given key. + * + * @param {Object} target + * @param {String} key + * + * @return {Boolean} + */ + + function has$1(target, key) { + return nativeHasOwnProperty$1.call(target, key); + } + + /** + * Find element in collection. + * + * @param {Array|Object} collection + * @param {Function|Object} matcher + * + * @return {Object} + */ + + function find(collection, matcher) { + matcher = toMatcher(matcher); + var match; + forEach$1(collection, function (val, key) { + if (matcher(val, key)) { + match = val; + return false; + } + }); + return match; + } + /** + * Find element index in collection. + * + * @param {Array|Object} collection + * @param {Function} matcher + * + * @return {Object} + */ + + function findIndex(collection, matcher) { + matcher = toMatcher(matcher); + var idx = isArray$3(collection) ? -1 : undefined; + forEach$1(collection, function (val, key) { + if (matcher(val, key)) { + idx = key; + return false; + } + }); + return idx; + } + /** + * Find element in collection. + * + * @param {Array|Object} collection + * @param {Function} matcher + * + * @return {Array} result + */ + + function filter(collection, matcher) { + var result = []; + forEach$1(collection, function (val, key) { + if (matcher(val, key)) { + result.push(val); + } + }); + return result; + } + /** + * Iterate over collection; returning something + * (non-undefined) will stop iteration. + * + * @param {Array|Object} collection + * @param {Function} iterator + * + * @return {Object} return result that stopped the iteration + */ + + function forEach$1(collection, iterator) { + var val, result; + + if (isUndefined$2(collection)) { + return; + } + + var convertKey = isArray$3(collection) ? toNum$1 : identity$1; + + for (var key in collection) { + if (has$1(collection, key)) { + val = collection[key]; + result = iterator(val, convertKey(key)); + + if (result === false) { + return val; + } + } + } + } + /** + * Return collection without element. + * + * @param {Array} arr + * @param {Function} matcher + * + * @return {Array} + */ + + function without(arr, matcher) { + if (isUndefined$2(arr)) { + return []; + } + + ensureArray(arr); + matcher = toMatcher(matcher); + return arr.filter(function (el, idx) { + return !matcher(el, idx); + }); + } + /** + * Reduce collection, returning a single result. + * + * @param {Object|Array} collection + * @param {Function} iterator + * @param {Any} result + * + * @return {Any} result returned from last iterator + */ + + function reduce(collection, iterator, result) { + forEach$1(collection, function (value, idx) { + result = iterator(result, value, idx); + }); + return result; + } + /** + * Return true if every element in the collection + * matches the criteria. + * + * @param {Object|Array} collection + * @param {Function} matcher + * + * @return {Boolean} + */ + + function every(collection, matcher) { + return !!reduce(collection, function (matches, val, key) { + return matches && matcher(val, key); + }, true); + } + /** + * Return true if some elements in the collection + * match the criteria. + * + * @param {Object|Array} collection + * @param {Function} matcher + * + * @return {Boolean} + */ + + function some(collection, matcher) { + return !!find(collection, matcher); + } + /** + * Transform a collection into another collection + * by piping each member through the given fn. + * + * @param {Object|Array} collection + * @param {Function} fn + * + * @return {Array} transformed collection + */ + + function map(collection, fn) { + var result = []; + forEach$1(collection, function (val, key) { + result.push(fn(val, key)); + }); + return result; + } + /** + * Get the collections keys. + * + * @param {Object|Array} collection + * + * @return {Array} + */ + + function keys(collection) { + return collection && Object.keys(collection) || []; + } + /** + * Shorthand for `keys(o).length`. + * + * @param {Object|Array} collection + * + * @return {Number} + */ + + function size(collection) { + return keys(collection).length; + } + /** + * Get the values in the collection. + * + * @param {Object|Array} collection + * + * @return {Array} + */ + + function values(collection) { + return map(collection, function (val) { + return val; + }); + } + /** + * Group collection members by attribute. + * + * @param {Object|Array} collection + * @param {Function} extractor + * + * @return {Object} map with { attrValue => [ a, b, c ] } + */ + + function groupBy(collection, extractor) { + var grouped = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + extractor = toExtractor(extractor); + forEach$1(collection, function (val) { + var discriminator = extractor(val) || '_'; + var group = grouped[discriminator]; + + if (!group) { + group = grouped[discriminator] = []; + } + + group.push(val); + }); + return grouped; + } + function uniqueBy(extractor) { + extractor = toExtractor(extractor); + var grouped = {}; + + for (var _len = arguments.length, collections = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + collections[_key - 1] = arguments[_key]; + } + + forEach$1(collections, function (c) { + return groupBy(c, extractor, grouped); + }); + var result = map(grouped, function (val, key) { + return val[0]; + }); + return result; + } + var unionBy = uniqueBy; + /** + * Sort collection by criteria. + * + * @param {Object|Array} collection + * @param {String|Function} extractor + * + * @return {Array} + */ + + function sortBy(collection, extractor) { + extractor = toExtractor(extractor); + var sorted = []; + forEach$1(collection, function (value, key) { + var disc = extractor(value, key); + var entry = { + d: disc, + v: value + }; + + for (var idx = 0; idx < sorted.length; idx++) { + var d = sorted[idx].d; + + if (disc < d) { + sorted.splice(idx, 0, entry); + return; + } + } // not inserted, append (!) + + + sorted.push(entry); + }); + return map(sorted, function (e) { + return e.v; + }); + } + /** + * Create an object pattern matcher. + * + * @example + * + * const matcher = matchPattern({ id: 1 }); + * + * let element = find(elements, matcher); + * + * @param {Object} pattern + * + * @return {Function} matcherFn + */ + + function matchPattern(pattern) { + return function (el) { + return every(pattern, function (val, key) { + return el[key] === val; + }); + }; + } + + function toExtractor(extractor) { + return isFunction(extractor) ? extractor : function (e) { + return e[extractor]; + }; + } + + function toMatcher(matcher) { + return isFunction(matcher) ? matcher : function (e) { + return e === matcher; + }; + } + + function identity$1(arg) { + return arg; + } + + function toNum$1(arg) { + return Number(arg); + } + + /** + * Debounce fn, calling it only once if the given time + * elapsed between calls. + * + * Lodash-style the function exposes methods to `#clear` + * and `#flush` to control internal behavior. + * + * @param {Function} fn + * @param {Number} timeout + * + * @return {Function} debounced function + */ + function debounce(fn, timeout) { + var timer; + var lastArgs; + var lastThis; + var lastNow; + + function fire(force) { + var now = Date.now(); + var scheduledDiff = force ? 0 : lastNow + timeout - now; + + if (scheduledDiff > 0) { + return schedule(scheduledDiff); + } + + fn.apply(lastThis, lastArgs); + clear(); + } + + function schedule(timeout) { + timer = setTimeout(fire, timeout); + } + + function clear() { + if (timer) { + clearTimeout(timer); + } + + timer = lastNow = lastArgs = lastThis = undefined; + } + + function flush() { + if (timer) { + fire(true); + } + + clear(); + } + + function callback() { + lastNow = Date.now(); + + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + lastArgs = args; + lastThis = this; // ensure an execution is scheduled + + if (!timer) { + schedule(timeout); + } + } + + callback.flush = flush; + callback.cancel = clear; + return callback; + } + /** + * Bind function against target . + * + * @param {Function} fn + * @param {Object} target + * + * @return {Function} bound function + */ + + function bind(fn, target) { + return fn.bind(target); + } + + function _extends() { + _extends = Object.assign || function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; + }; + + return _extends.apply(this, arguments); + } + + /** + * Convenience wrapper for `Object.assign`. + * + * @param {Object} target + * @param {...Object} others + * + * @return {Object} the target + */ + + function assign(target) { + for (var _len = arguments.length, others = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + others[_key - 1] = arguments[_key]; + } + + return _extends.apply(void 0, [target].concat(others)); + } + /** + * Pick given properties from the target object. + * + * @param {Object} target + * @param {Array} properties + * + * @return {Object} target + */ + + function pick(target, properties) { + var result = {}; + var obj = Object(target); + forEach$1(properties, function (prop) { + if (prop in obj) { + result[prop] = target[prop]; + } + }); + return result; + } + /** + * Pick all target properties, excluding the given ones. + * + * @param {Object} target + * @param {Array} properties + * + * @return {Object} target + */ + + function omit(target, properties) { + var result = {}; + var obj = Object(target); + forEach$1(obj, function (prop, key) { + if (properties.indexOf(key) === -1) { + result[key] = prop; + } + }); + return result; + } + + /** + * Flatten array, one level deep. + * + * @param {Array} arr + * + * @return {Array} + */ + + var nativeToString = Object.prototype.toString; + var nativeHasOwnProperty = Object.prototype.hasOwnProperty; + function isUndefined$1(obj) { + return obj === undefined; + } + function isArray$2(obj) { + return nativeToString.call(obj) === '[object Array]'; + } + /** + * Return true, if target owns a property with the given key. + * + * @param {Object} target + * @param {String} key + * + * @return {Boolean} + */ + + function has(target, key) { + return nativeHasOwnProperty.call(target, key); + } + /** + * Iterate over collection; returning something + * (non-undefined) will stop iteration. + * + * @param {Array|Object} collection + * @param {Function} iterator + * + * @return {Object} return result that stopped the iteration + */ + + function forEach(collection, iterator) { + var val, result; + + if (isUndefined$1(collection)) { + return; + } + + var convertKey = isArray$2(collection) ? toNum : identity; + + for (var key in collection) { + if (has(collection, key)) { + val = collection[key]; + result = iterator(val, convertKey(key)); + + if (result === false) { + return val; + } + } + } + } + + function identity(arg) { + return arg; + } + + function toNum(arg) { + return Number(arg); + } + + /** + * Assigns style attributes in a style-src compliant way. + * + * @param {Element} element + * @param {...Object} styleSources + * + * @return {Element} the element + */ + function assign$1(element) { + var target = element.style; + + for (var _len = arguments.length, styleSources = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + styleSources[_key - 1] = arguments[_key]; + } + + forEach(styleSources, function (style) { + if (!style) { + return; + } + + forEach(style, function (value, key) { + target[key] = value; + }); + }); + + return element; + } + + /** + * Set attribute `name` to `val`, or get attr `name`. + * + * @param {Element} el + * @param {String} name + * @param {String} [val] + * @api public + */ + function attr$1(el, name, val) { + // get + if (arguments.length == 2) { + return el.getAttribute(name); + } + + // remove + if (val === null) { + return el.removeAttribute(name); + } + + // set + el.setAttribute(name, val); + + return el; + } + + var indexOf$1 = [].indexOf; + + var indexof = function(arr, obj){ + if (indexOf$1) return arr.indexOf(obj); + for (var i = 0; i < arr.length; ++i) { + if (arr[i] === obj) return i; + } + return -1; + }; + + /** + * Taken from https://github.com/component/classes + * + * Without the component bits. + */ + + /** + * Whitespace regexp. + */ + + var re$1 = /\s+/; + + /** + * toString reference. + */ + + var toString$1 = Object.prototype.toString; + + /** + * Wrap `el` in a `ClassList`. + * + * @param {Element} el + * @return {ClassList} + * @api public + */ + + function classes$1(el) { + return new ClassList$1(el); + } + + /** + * Initialize a new ClassList for `el`. + * + * @param {Element} el + * @api private + */ + + function ClassList$1(el) { + if (!el || !el.nodeType) { + throw new Error('A DOM element reference is required'); + } + this.el = el; + this.list = el.classList; + } + + /** + * Add class `name` if not already present. + * + * @param {String} name + * @return {ClassList} + * @api public + */ + + ClassList$1.prototype.add = function (name) { + // classList + if (this.list) { + this.list.add(name); + return this; + } + + // fallback + var arr = this.array(); + var i = indexof(arr, name); + if (!~i) arr.push(name); + this.el.className = arr.join(' '); + return this; + }; + + /** + * Remove class `name` when present, or + * pass a regular expression to remove + * any which match. + * + * @param {String|RegExp} name + * @return {ClassList} + * @api public + */ + + ClassList$1.prototype.remove = function (name) { + if ('[object RegExp]' == toString$1.call(name)) { + return this.removeMatching(name); + } + + // classList + if (this.list) { + this.list.remove(name); + return this; + } + + // fallback + var arr = this.array(); + var i = indexof(arr, name); + if (~i) arr.splice(i, 1); + this.el.className = arr.join(' '); + return this; + }; + + /** + * Remove all classes matching `re`. + * + * @param {RegExp} re + * @return {ClassList} + * @api private + */ + + ClassList$1.prototype.removeMatching = function (re) { + var arr = this.array(); + for (var i = 0; i < arr.length; i++) { + if (re.test(arr[i])) { + this.remove(arr[i]); + } + } + return this; + }; + + /** + * Toggle class `name`, can force state via `force`. + * + * For browsers that support classList, but do not support `force` yet, + * the mistake will be detected and corrected. + * + * @param {String} name + * @param {Boolean} force + * @return {ClassList} + * @api public + */ + + ClassList$1.prototype.toggle = function (name, force) { + // classList + if (this.list) { + if ('undefined' !== typeof force) { + if (force !== this.list.toggle(name, force)) { + this.list.toggle(name); // toggle again to correct + } + } else { + this.list.toggle(name); + } + return this; + } + + // fallback + if ('undefined' !== typeof force) { + if (!force) { + this.remove(name); + } else { + this.add(name); + } + } else { + if (this.has(name)) { + this.remove(name); + } else { + this.add(name); + } + } + + return this; + }; + + /** + * Return an array of classes. + * + * @return {Array} + * @api public + */ + + ClassList$1.prototype.array = function () { + var className = this.el.getAttribute('class') || ''; + var str = className.replace(/^\s+|\s+$/g, ''); + var arr = str.split(re$1); + if ('' === arr[0]) arr.shift(); + return arr; + }; + + /** + * Check if class `name` is present. + * + * @param {String} name + * @return {ClassList} + * @api public + */ + + ClassList$1.prototype.has = ClassList$1.prototype.contains = function (name) { + return this.list ? this.list.contains(name) : !!~indexof(this.array(), name); + }; + + /** + * Remove all children from the given element. + */ + function clear$1(el) { + + var c; + + while (el.childNodes.length) { + c = el.childNodes[0]; + el.removeChild(c); + } + + return el; + } + + var proto = typeof Element !== 'undefined' ? Element.prototype : {}; + var vendor = proto.matches + || proto.matchesSelector + || proto.webkitMatchesSelector + || proto.mozMatchesSelector + || proto.msMatchesSelector + || proto.oMatchesSelector; + + var matchesSelector = match; + + /** + * Match `el` to `selector`. + * + * @param {Element} el + * @param {String} selector + * @return {Boolean} + * @api public + */ + + function match(el, selector) { + if (!el || el.nodeType !== 1) return false; + if (vendor) return vendor.call(el, selector); + var nodes = el.parentNode.querySelectorAll(selector); + for (var i = 0; i < nodes.length; i++) { + if (nodes[i] == el) return true; + } + return false; + } + + /** + * Closest + * + * @param {Element} el + * @param {String} selector + * @param {Boolean} checkYourSelf (optional) + */ + function closest (element, selector, checkYourSelf) { + var currentElem = checkYourSelf ? element : element.parentNode; + + while (currentElem && currentElem.nodeType !== document.DOCUMENT_NODE && currentElem.nodeType !== document.DOCUMENT_FRAGMENT_NODE) { + + if (matchesSelector(currentElem, selector)) { + return currentElem; + } + + currentElem = currentElem.parentNode; + } + + return matchesSelector(currentElem, selector) ? currentElem : null; + } + + var bind$1 = window.addEventListener ? 'addEventListener' : 'attachEvent', + unbind = window.removeEventListener ? 'removeEventListener' : 'detachEvent', + prefix$6 = bind$1 !== 'addEventListener' ? 'on' : ''; + + /** + * Bind `el` event `type` to `fn`. + * + * @param {Element} el + * @param {String} type + * @param {Function} fn + * @param {Boolean} capture + * @return {Function} + * @api public + */ + + var bind_1 = function(el, type, fn, capture){ + el[bind$1](prefix$6 + type, fn, capture || false); + return fn; + }; + + /** + * Unbind `el` event `type`'s callback `fn`. + * + * @param {Element} el + * @param {String} type + * @param {Function} fn + * @param {Boolean} capture + * @return {Function} + * @api public + */ + + var unbind_1 = function(el, type, fn, capture){ + el[unbind](prefix$6 + type, fn, capture || false); + return fn; + }; + + var componentEvent = { + bind: bind_1, + unbind: unbind_1 + }; + + /** + * Module dependencies. + */ + + /** + * Delegate event `type` to `selector` + * and invoke `fn(e)`. A callback function + * is returned which may be passed to `.unbind()`. + * + * @param {Element} el + * @param {String} selector + * @param {String} type + * @param {Function} fn + * @param {Boolean} capture + * @return {Function} + * @api public + */ + + // Some events don't bubble, so we want to bind to the capture phase instead + // when delegating. + var forceCaptureEvents = ['focus', 'blur']; + + function bind$2(el, selector, type, fn, capture) { + if (forceCaptureEvents.indexOf(type) !== -1) { + capture = true; + } + + return componentEvent.bind(el, type, function (e) { + var target = e.target || e.srcElement; + e.delegateTarget = closest(target, selector, true); + if (e.delegateTarget) { + fn.call(el, e); + } + }, capture); + } + + /** + * Unbind event `type`'s callback `fn`. + * + * @param {Element} el + * @param {String} type + * @param {Function} fn + * @param {Boolean} capture + * @api public + */ + function unbind$1(el, type, fn, capture) { + if (forceCaptureEvents.indexOf(type) !== -1) { + capture = true; + } + + return componentEvent.unbind(el, type, fn, capture); + } + + var delegate = { + bind: bind$2, + unbind: unbind$1 + }; + + /** + * Expose `parse`. + */ + + var domify = parse$1; + + /** + * Tests for browser support. + */ + + var innerHTMLBug = false; + var bugTestDiv; + if (typeof document !== 'undefined') { + bugTestDiv = document.createElement('div'); + // Setup + bugTestDiv.innerHTML = '
a'; + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + innerHTMLBug = !bugTestDiv.getElementsByTagName('link').length; + bugTestDiv = undefined; + } + + /** + * Wrap map from jquery. + */ + + var map$1 = { + legend: [1, '
', '
'], + tr: [2, '', '
'], + col: [2, '', '
'], + // for script/link/style tags to work in IE6-8, you have to wrap + // in a div with a non-whitespace character in front, ha! + _default: innerHTMLBug ? [1, 'X
', '
'] : [0, '', ''] + }; + + map$1.td = + map$1.th = [3, '', '
']; + + map$1.option = + map$1.optgroup = [1, '']; + + map$1.thead = + map$1.tbody = + map$1.colgroup = + map$1.caption = + map$1.tfoot = [1, '', '
']; + + map$1.polyline = + map$1.ellipse = + map$1.polygon = + map$1.circle = + map$1.text = + map$1.line = + map$1.path = + map$1.rect = + map$1.g = [1, '','']; + + /** + * Parse `html` and return a DOM Node instance, which could be a TextNode, + * HTML DOM Node of some kind (
for example), or a DocumentFragment + * instance, depending on the contents of the `html` string. + * + * @param {String} html - HTML string to "domify" + * @param {Document} doc - The `document` instance to create the Node for + * @return {DOMNode} the TextNode, DOM Node, or DocumentFragment instance + * @api private + */ + + function parse$1(html, doc) { + if ('string' != typeof html) throw new TypeError('String expected'); + + // default to the global `document` object + if (!doc) doc = document; + + // tag name + var m = /<([\w:]+)/.exec(html); + if (!m) return doc.createTextNode(html); + + html = html.replace(/^\s+|\s+$/g, ''); // Remove leading/trailing whitespace + + var tag = m[1]; + + // body support + if (tag == 'body') { + var el = doc.createElement('html'); + el.innerHTML = html; + return el.removeChild(el.lastChild); + } + + // wrap map + var wrap = map$1[tag] || map$1._default; + var depth = wrap[0]; + var prefix = wrap[1]; + var suffix = wrap[2]; + var el = doc.createElement('div'); + el.innerHTML = prefix + html + suffix; + while (depth--) el = el.lastChild; + + // one element + if (el.firstChild == el.lastChild) { + return el.removeChild(el.firstChild); + } + + // several elements + var fragment = doc.createDocumentFragment(); + while (el.firstChild) { + fragment.appendChild(el.removeChild(el.firstChild)); + } + + return fragment; + } + + function query(selector, el) { + el = el || document; + + return el.querySelector(selector); + } + + function all(selector, el) { + el = el || document; + + return el.querySelectorAll(selector); + } + + function remove$2(el) { + el.parentNode && el.parentNode.removeChild(el); + } + + function ensureImported(element, target) { + + if (element.ownerDocument !== target.ownerDocument) { + try { + // may fail on webkit + return target.ownerDocument.importNode(element, true); + } catch (e) { + // ignore + } + } + + return element; + } + + /** + * appendTo utility + */ + + /** + * Append a node to a target element and return the appended node. + * + * @param {SVGElement} element + * @param {SVGElement} target + * + * @return {SVGElement} the appended node + */ + function appendTo(element, target) { + return target.appendChild(ensureImported(element, target)); + } + + /** + * append utility + */ + + /** + * Append a node to an element + * + * @param {SVGElement} element + * @param {SVGElement} node + * + * @return {SVGElement} the element + */ + function append(target, node) { + appendTo(node, target); + return target; + } + + /** + * attribute accessor utility + */ + + var LENGTH_ATTR = 2; + + var CSS_PROPERTIES = { + 'alignment-baseline': 1, + 'baseline-shift': 1, + 'clip': 1, + 'clip-path': 1, + 'clip-rule': 1, + 'color': 1, + 'color-interpolation': 1, + 'color-interpolation-filters': 1, + 'color-profile': 1, + 'color-rendering': 1, + 'cursor': 1, + 'direction': 1, + 'display': 1, + 'dominant-baseline': 1, + 'enable-background': 1, + 'fill': 1, + 'fill-opacity': 1, + 'fill-rule': 1, + 'filter': 1, + 'flood-color': 1, + 'flood-opacity': 1, + 'font': 1, + 'font-family': 1, + 'font-size': LENGTH_ATTR, + 'font-size-adjust': 1, + 'font-stretch': 1, + 'font-style': 1, + 'font-variant': 1, + 'font-weight': 1, + 'glyph-orientation-horizontal': 1, + 'glyph-orientation-vertical': 1, + 'image-rendering': 1, + 'kerning': 1, + 'letter-spacing': 1, + 'lighting-color': 1, + 'marker': 1, + 'marker-end': 1, + 'marker-mid': 1, + 'marker-start': 1, + 'mask': 1, + 'opacity': 1, + 'overflow': 1, + 'pointer-events': 1, + 'shape-rendering': 1, + 'stop-color': 1, + 'stop-opacity': 1, + 'stroke': 1, + 'stroke-dasharray': 1, + 'stroke-dashoffset': 1, + 'stroke-linecap': 1, + 'stroke-linejoin': 1, + 'stroke-miterlimit': 1, + 'stroke-opacity': 1, + 'stroke-width': LENGTH_ATTR, + 'text-anchor': 1, + 'text-decoration': 1, + 'text-rendering': 1, + 'unicode-bidi': 1, + 'visibility': 1, + 'word-spacing': 1, + 'writing-mode': 1 + }; + + + function getAttribute(node, name) { + if (CSS_PROPERTIES[name]) { + return node.style[name]; + } else { + return node.getAttributeNS(null, name); + } + } + + function setAttribute(node, name, value) { + var hyphenated = name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); + + var type = CSS_PROPERTIES[hyphenated]; + + if (type) { + // append pixel unit, unless present + if (type === LENGTH_ATTR && typeof value === 'number') { + value = String(value) + 'px'; + } + + node.style[hyphenated] = value; + } else { + node.setAttributeNS(null, name, value); + } + } + + function setAttributes(node, attrs) { + + var names = Object.keys(attrs), i, name; + + for (i = 0, name; (name = names[i]); i++) { + setAttribute(node, name, attrs[name]); + } + } + + /** + * Gets or sets raw attributes on a node. + * + * @param {SVGElement} node + * @param {Object} [attrs] + * @param {String} [name] + * @param {String} [value] + * + * @return {String} + */ + function attr(node, name, value) { + if (typeof name === 'string') { + if (value !== undefined) { + setAttribute(node, name, value); + } else { + return getAttribute(node, name); + } + } else { + setAttributes(node, name); + } + + return node; + } + + /** + * Clear utility + */ + function index(arr, obj) { + if (arr.indexOf) { + return arr.indexOf(obj); + } + + + for (var i = 0; i < arr.length; ++i) { + if (arr[i] === obj) { + return i; + } + } + + return -1; + } + + var re = /\s+/; + + var toString = Object.prototype.toString; + + function defined(o) { + return typeof o !== 'undefined'; + } + + /** + * Wrap `el` in a `ClassList`. + * + * @param {Element} el + * @return {ClassList} + * @api public + */ + + function classes(el) { + return new ClassList(el); + } + + function ClassList(el) { + if (!el || !el.nodeType) { + throw new Error('A DOM element reference is required'); + } + this.el = el; + this.list = el.classList; + } + + /** + * Add class `name` if not already present. + * + * @param {String} name + * @return {ClassList} + * @api public + */ + + ClassList.prototype.add = function(name) { + + // classList + if (this.list) { + this.list.add(name); + return this; + } + + // fallback + var arr = this.array(); + var i = index(arr, name); + if (!~i) { + arr.push(name); + } + + if (defined(this.el.className.baseVal)) { + this.el.className.baseVal = arr.join(' '); + } else { + this.el.className = arr.join(' '); + } + + return this; + }; + + /** + * Remove class `name` when present, or + * pass a regular expression to remove + * any which match. + * + * @param {String|RegExp} name + * @return {ClassList} + * @api public + */ + + ClassList.prototype.remove = function(name) { + if ('[object RegExp]' === toString.call(name)) { + return this.removeMatching(name); + } + + // classList + if (this.list) { + this.list.remove(name); + return this; + } + + // fallback + var arr = this.array(); + var i = index(arr, name); + if (~i) { + arr.splice(i, 1); + } + this.el.className.baseVal = arr.join(' '); + return this; + }; + + /** + * Remove all classes matching `re`. + * + * @param {RegExp} re + * @return {ClassList} + * @api private + */ + + ClassList.prototype.removeMatching = function(re) { + var arr = this.array(); + for (var i = 0; i < arr.length; i++) { + if (re.test(arr[i])) { + this.remove(arr[i]); + } + } + return this; + }; + + /** + * Toggle class `name`, can force state via `force`. + * + * For browsers that support classList, but do not support `force` yet, + * the mistake will be detected and corrected. + * + * @param {String} name + * @param {Boolean} force + * @return {ClassList} + * @api public + */ + + ClassList.prototype.toggle = function(name, force) { + // classList + if (this.list) { + if (defined(force)) { + if (force !== this.list.toggle(name, force)) { + this.list.toggle(name); // toggle again to correct + } + } else { + this.list.toggle(name); + } + return this; + } + + // fallback + if (defined(force)) { + if (!force) { + this.remove(name); + } else { + this.add(name); + } + } else { + if (this.has(name)) { + this.remove(name); + } else { + this.add(name); + } + } + + return this; + }; + + /** + * Return an array of classes. + * + * @return {Array} + * @api public + */ + + ClassList.prototype.array = function() { + var className = this.el.getAttribute('class') || ''; + var str = className.replace(/^\s+|\s+$/g, ''); + var arr = str.split(re); + if ('' === arr[0]) { + arr.shift(); + } + return arr; + }; + + /** + * Check if class `name` is present. + * + * @param {String} name + * @return {ClassList} + * @api public + */ + + ClassList.prototype.has = + ClassList.prototype.contains = function(name) { + return ( + this.list ? + this.list.contains(name) : + !! ~index(this.array(), name) + ); + }; + + function remove$1(element) { + var parent = element.parentNode; + + if (parent) { + parent.removeChild(element); + } + + return element; + } + + /** + * Clear utility + */ + + /** + * Removes all children from the given element + * + * @param {DOMElement} element + * @return {DOMElement} the element (for chaining) + */ + function clear(element) { + var child; + + while ((child = element.firstChild)) { + remove$1(child); + } + + return element; + } + + function clone$1(element) { + return element.cloneNode(true); + } + + var ns = { + svg: 'http://www.w3.org/2000/svg' + }; + + /** + * DOM parsing utility + */ + + var SVG_START = '' + svg + ''; + unwrap = true; + } + + var parsed = parseDocument(svg); + + if (!unwrap) { + return parsed; + } + + var fragment = document.createDocumentFragment(); + + var parent = parsed.firstChild; + + while (parent.firstChild) { + fragment.appendChild(parent.firstChild); + } + + return fragment; + } + + function parseDocument(svg) { + + var parser; + + // parse + parser = new DOMParser(); + parser.async = false; + + return parser.parseFromString(svg, 'text/xml'); + } + + /** + * Create utility for SVG elements + */ + + + /** + * Create a specific type from name or SVG markup. + * + * @param {String} name the name or markup of the element + * @param {Object} [attrs] attributes to set on the element + * + * @returns {SVGElement} + */ + function create$1(name, attrs) { + var element; + + if (name.charAt(0) === '<') { + element = parse(name).firstChild; + element = document.importNode(element, true); + } else { + element = document.createElementNS(ns.svg, name); + } + + if (attrs) { + attr(element, attrs); + } + + return element; + } + + /** + * Geometry helpers + */ + + // fake node used to instantiate svg geometry elements + var node = null; + + function getNode() { + if (node === null) { + node = create$1('svg'); + } + + return node; + } + + function extend$1(object, props) { + var i, k, keys = Object.keys(props); + + for (i = 0; (k = keys[i]); i++) { + object[k] = props[k]; + } + + return object; + } + + /** + * Create matrix via args. + * + * @example + * + * createMatrix({ a: 1, b: 1 }); + * createMatrix(); + * createMatrix(1, 2, 0, 0, 30, 20); + * + * @return {SVGMatrix} + */ + function createMatrix(a, b, c, d, e, f) { + var matrix = getNode().createSVGMatrix(); + + switch (arguments.length) { + case 0: + return matrix; + case 1: + return extend$1(matrix, a); + case 6: + return extend$1(matrix, { + a: a, + b: b, + c: c, + d: d, + e: e, + f: f + }); + } + } + + function createTransform(matrix) { + if (matrix) { + return getNode().createSVGTransformFromMatrix(matrix); + } else { + return getNode().createSVGTransform(); + } + } + + /** + * Serialization util + */ + + var TEXT_ENTITIES = /([&<>]{1})/g; + var ATTR_ENTITIES = /([\n\r"]{1})/g; + + var ENTITY_REPLACEMENT = { + '&': '&', + '<': '<', + '>': '>', + '"': '\'' + }; + + function escape$1(str, pattern) { + + function replaceFn(match, entity) { + return ENTITY_REPLACEMENT[entity] || entity; + } + + return str.replace(pattern, replaceFn); + } + + function serialize(node, output) { + + var i, len, attrMap, attrNode, childNodes; + + switch (node.nodeType) { + // TEXT + case 3: + // replace special XML characters + output.push(escape$1(node.textContent, TEXT_ENTITIES)); + break; + + // ELEMENT + case 1: + output.push('<', node.tagName); + + if (node.hasAttributes()) { + attrMap = node.attributes; + for (i = 0, len = attrMap.length; i < len; ++i) { + attrNode = attrMap.item(i); + output.push(' ', attrNode.name, '="', escape$1(attrNode.value, ATTR_ENTITIES), '"'); + } + } + + if (node.hasChildNodes()) { + output.push('>'); + childNodes = node.childNodes; + for (i = 0, len = childNodes.length; i < len; ++i) { + serialize(childNodes.item(i), output); + } + output.push(''); + } else { + output.push('/>'); + } + break; + + // COMMENT + case 8: + output.push(''); + break; + + // CDATA + case 4: + output.push(''); + break; + + default: + throw new Error('unable to handle node ' + node.nodeType); + } + + return output; + } + + /** + * innerHTML like functionality for SVG elements. + * based on innerSVG (https://code.google.com/p/innersvg) + */ + + + function set$1(element, svg) { + + var parsed = parse(svg); + + // clear element contents + clear(element); + + if (!svg) { + return; + } + + if (!isFragment(parsed)) { + // extract from parsed document + parsed = parsed.documentElement; + } + + var nodes = slice$1(parsed.childNodes); + + // import + append each node + for (var i = 0; i < nodes.length; i++) { + appendTo(nodes[i], element); + } + + } + + function get$1(element) { + var child = element.firstChild, + output = []; + + while (child) { + serialize(child, output); + child = child.nextSibling; + } + + return output.join(''); + } + + function isFragment(node) { + return node.nodeName === '#document-fragment'; + } + + function innerSVG(element, svg) { + + if (svg !== undefined) { + + try { + set$1(element, svg); + } catch (e) { + throw new Error('error parsing SVG: ' + e.message); + } + + return element; + } else { + return get$1(element); + } + } + + + function slice$1(arr) { + return Array.prototype.slice.call(arr); + } + + /** + * transform accessor utility + */ + + function wrapMatrix(transformList, transform) { + if (transform instanceof SVGMatrix) { + return transformList.createSVGTransformFromMatrix(transform); + } + + return transform; + } + + + function setTransforms(transformList, transforms) { + var i, t; + + transformList.clear(); + + for (i = 0; (t = transforms[i]); i++) { + transformList.appendItem(wrapMatrix(transformList, t)); + } + } + + /** + * Get or set the transforms on the given node. + * + * @param {SVGElement} node + * @param {SVGTransform|SVGMatrix|Array} [transforms] + * + * @return {SVGTransform} the consolidated transform + */ + function transform$1(node, transforms) { + var transformList = node.transform.baseVal; + + if (transforms) { + + if (!Array.isArray(transforms)) { + transforms = [ transforms ]; + } + + setTransforms(transformList, transforms); + } + + return transformList.consolidate(); + } + + var CLASS_PATTERN = /^class /; + + + /** + * @param {function} fn + * + * @return {boolean} + */ + function isClass(fn) { + return CLASS_PATTERN.test(fn.toString()); + } + + /** + * @param {any} obj + * + * @return {boolean} + */ + function isArray$1(obj) { + return Object.prototype.toString.call(obj) === '[object Array]'; + } + + /** + * @param {any} obj + * @param {string} prop + * + * @return {boolean} + */ + function hasOwnProp(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); + } + + /** + * @typedef {import('./index').InjectAnnotated } InjectAnnotated + */ + + /** + * @template T + * + * @params {[...string[], T] | ...string[], T} args + * + * @return {T & InjectAnnotated} + */ + function annotate() { + var args = Array.prototype.slice.call(arguments); + + if (args.length === 1 && isArray$1(args[0])) { + args = args[0]; + } + + var fn = args.pop(); + + fn.$inject = args; + + return fn; + } + + + // Current limitations: + // - can't put into "function arg" comments + // function /* (no parenthesis like this) */ (){} + // function abc( /* xx (no parenthesis like this) */ a, b) {} + // + // Just put the comment before function or inside: + // /* (((this is fine))) */ function(a, b) {} + // function abc(a) { /* (((this is fine))) */} + // + // - can't reliably auto-annotate constructor; we'll match the + // first constructor(...) pattern found which may be the one + // of a nested class, too. + + var CONSTRUCTOR_ARGS = /constructor\s*[^(]*\(\s*([^)]*)\)/m; + var FN_ARGS = /^(?:async\s+)?(?:function\s*[^(]*)?(?:\(\s*([^)]*)\)|(\w+))/m; + var FN_ARG = /\/\*([^*]*)\*\//m; + + /** + * @param {unknown} fn + * + * @return {string[]} + */ + function parseAnnotations(fn) { + + if (typeof fn !== 'function') { + throw new Error('Cannot annotate "' + fn + '". Expected a function!'); + } + + var match = fn.toString().match(isClass(fn) ? CONSTRUCTOR_ARGS : FN_ARGS); + + // may parse class without constructor + if (!match) { + return []; + } + + var args = match[1] || match[2]; + + return args && args.split(',').map(function(arg) { + var argMatch = arg.match(FN_ARG); + return (argMatch && argMatch[1] || arg).trim(); + }) || []; + } + + /** + * @typedef { import('./index').ModuleDeclaration } ModuleDeclaration + * @typedef { import('./index').ModuleDefinition } ModuleDefinition + * @typedef { import('./index').InjectorContext } InjectorContext + */ + + /** + * Create a new injector with the given modules. + * + * @param {ModuleDefinition[]} modules + * @param {InjectorContext} [parent] + */ + function Injector(modules, parent) { + parent = parent || { + get: function(name, strict) { + currentlyResolving.push(name); + + if (strict === false) { + return null; + } else { + throw error('No provider for "' + name + '"!'); + } + } + }; + + var currentlyResolving = []; + var providers = this._providers = Object.create(parent._providers || null); + var instances = this._instances = Object.create(null); + + var self = instances.injector = this; + + var error = function(msg) { + var stack = currentlyResolving.join(' -> '); + currentlyResolving.length = 0; + return new Error(stack ? msg + ' (Resolving: ' + stack + ')' : msg); + }; + + /** + * Return a named service. + * + * @param {string} name + * @param {boolean} [strict=true] if false, resolve missing services to null + * + * @return {any} + */ + function get(name, strict) { + if (!providers[name] && name.indexOf('.') !== -1) { + var parts = name.split('.'); + var pivot = get(parts.shift()); + + while (parts.length) { + pivot = pivot[parts.shift()]; + } + + return pivot; + } + + if (hasOwnProp(instances, name)) { + return instances[name]; + } + + if (hasOwnProp(providers, name)) { + if (currentlyResolving.indexOf(name) !== -1) { + currentlyResolving.push(name); + throw error('Cannot resolve circular dependency!'); + } + + currentlyResolving.push(name); + instances[name] = providers[name][0](providers[name][1]); + currentlyResolving.pop(); + + return instances[name]; + } + + return parent.get(name, strict); + } + + function fnDef(fn, locals) { + + if (typeof locals === 'undefined') { + locals = {}; + } + + if (typeof fn !== 'function') { + if (isArray$1(fn)) { + fn = annotate(fn.slice()); + } else { + throw new Error('Cannot invoke "' + fn + '". Expected a function!'); + } + } + + var inject = fn.$inject || parseAnnotations(fn); + var dependencies = inject.map(function(dep) { + if (hasOwnProp(locals, dep)) { + return locals[dep]; + } else { + return get(dep); + } + }); + + return { + fn: fn, + dependencies: dependencies + }; + } + + function instantiate(Type) { + var def = fnDef(Type); + + var fn = def.fn, + dependencies = def.dependencies; + + // instantiate var args constructor + var Constructor = Function.prototype.bind.apply(fn, [ null ].concat(dependencies)); + + return new Constructor(); + } + + function invoke(func, context, locals) { + var def = fnDef(func, locals); + + var fn = def.fn, + dependencies = def.dependencies; + + return fn.apply(context, dependencies); + } + + /** + * @param {Injector} childInjector + * + * @return {Function} + */ + function createPrivateInjectorFactory(childInjector) { + return annotate(function(key) { + return childInjector.get(key); + }); + } + + /** + * @param {ModuleDefinition[]} modules + * @param {string[]} [forceNewInstances] + * + * @return {Injector} + */ + function createChild(modules, forceNewInstances) { + if (forceNewInstances && forceNewInstances.length) { + var fromParentModule = Object.create(null); + var matchedScopes = Object.create(null); + + var privateInjectorsCache = []; + var privateChildInjectors = []; + var privateChildFactories = []; + + var provider; + var cacheIdx; + var privateChildInjector; + var privateChildInjectorFactory; + for (var name in providers) { + provider = providers[name]; + + if (forceNewInstances.indexOf(name) !== -1) { + if (provider[2] === 'private') { + cacheIdx = privateInjectorsCache.indexOf(provider[3]); + if (cacheIdx === -1) { + privateChildInjector = provider[3].createChild([], forceNewInstances); + privateChildInjectorFactory = createPrivateInjectorFactory(privateChildInjector); + privateInjectorsCache.push(provider[3]); + privateChildInjectors.push(privateChildInjector); + privateChildFactories.push(privateChildInjectorFactory); + fromParentModule[name] = [ privateChildInjectorFactory, name, 'private', privateChildInjector ]; + } else { + fromParentModule[name] = [ privateChildFactories[cacheIdx], name, 'private', privateChildInjectors[cacheIdx] ]; + } + } else { + fromParentModule[name] = [ provider[2], provider[1] ]; + } + matchedScopes[name] = true; + } + + if ((provider[2] === 'factory' || provider[2] === 'type') && provider[1].$scope) { + /* jshint -W083 */ + forceNewInstances.forEach(function(scope) { + if (provider[1].$scope.indexOf(scope) !== -1) { + fromParentModule[name] = [ provider[2], provider[1] ]; + matchedScopes[scope] = true; + } + }); + } + } + + forceNewInstances.forEach(function(scope) { + if (!matchedScopes[scope]) { + throw new Error('No provider for "' + scope + '". Cannot use provider from the parent!'); + } + }); + + modules.unshift(fromParentModule); + } + + return new Injector(modules, self); + } + + var factoryMap = { + factory: invoke, + type: instantiate, + value: function(value) { + return value; + } + }; + + /** + * @param {ModuleDefinition} moduleDefinition + * @param {Injector} injector + */ + function createInitializer(moduleDefinition, injector) { + + var initializers = moduleDefinition.__init__ || []; + + return function() { + initializers.forEach(function(initializer) { + + // eagerly resolve component (fn or string) + if (typeof initializer === 'string') { + injector.get(initializer); + } else { + injector.invoke(initializer); + } + }); + }; + } + + /** + * @param {ModuleDefinition} moduleDefinition + */ + function loadModule(moduleDefinition) { + + var moduleExports = moduleDefinition.__exports__; + + // private module + if (moduleExports) { + var nestedModules = moduleDefinition.__modules__; + + var clonedModule = Object.keys(moduleDefinition).reduce(function(clonedModule, key) { + + if (key !== '__exports__' && key !== '__modules__' && key !== '__init__' && key !== '__depends__') { + clonedModule[key] = moduleDefinition[key]; + } + + return clonedModule; + }, Object.create(null)); + + var childModules = (nestedModules || []).concat(clonedModule); + + var privateInjector = createChild(childModules); + var getFromPrivateInjector = annotate(function(key) { + return privateInjector.get(key); + }); + + moduleExports.forEach(function(key) { + providers[key] = [ getFromPrivateInjector, key, 'private', privateInjector ]; + }); + + // ensure child injector initializes + var initializers = (moduleDefinition.__init__ || []).slice(); + + initializers.unshift(function() { + privateInjector.init(); + }); + + moduleDefinition = Object.assign({}, moduleDefinition, { + __init__: initializers + }); + + return createInitializer(moduleDefinition, privateInjector); + } + + // normal module + Object.keys(moduleDefinition).forEach(function(key) { + + if (key === '__init__' || key === '__depends__') { + return; + } + + if (moduleDefinition[key][2] === 'private') { + providers[key] = moduleDefinition[key]; + return; + } + + var type = moduleDefinition[key][0]; + var value = moduleDefinition[key][1]; + + providers[key] = [ factoryMap[type], arrayUnwrap(type, value), type ]; + }); + + return createInitializer(moduleDefinition, self); + } + + /** + * @param {ModuleDefinition[]} moduleDefinitions + * @param {ModuleDefinition} moduleDefinition + * + * @return {ModuleDefinition[]} + */ + function resolveDependencies(moduleDefinitions, moduleDefinition) { + + if (moduleDefinitions.indexOf(moduleDefinition) !== -1) { + return moduleDefinitions; + } + + moduleDefinitions = (moduleDefinition.__depends__ || []).reduce(resolveDependencies, moduleDefinitions); + + if (moduleDefinitions.indexOf(moduleDefinition) !== -1) { + return moduleDefinitions; + } + + return moduleDefinitions.concat(moduleDefinition); + } + + /** + * @param {ModuleDefinition[]} moduleDefinitions + * + * @return { () => void } initializerFn + */ + function bootstrap(moduleDefinitions) { + + var initializers = moduleDefinitions + .reduce(resolveDependencies, []) + .map(loadModule); + + var initialized = false; + + return function() { + + if (initialized) { + return; + } + + initialized = true; + + initializers.forEach(function(initializer) { + return initializer(); + }); + }; + } + + // public API + this.get = get; + this.invoke = invoke; + this.instantiate = instantiate; + this.createChild = createChild; + + // setup + this.init = bootstrap(modules); + } + + + // helpers /////////////// + + function arrayUnwrap(type, value) { + if (type !== 'value' && isArray$1(value)) { + value = annotate(value.slice()); + } + + return value; + } + + var DEFAULT_RENDER_PRIORITY$1 = 1000; + + /** + * The base implementation of shape and connection renderers. + * + * @param {EventBus} eventBus + * @param {number} [renderPriority=1000] + */ + function BaseRenderer(eventBus, renderPriority) { + var self = this; + + renderPriority = renderPriority || DEFAULT_RENDER_PRIORITY$1; + + eventBus.on([ 'render.shape', 'render.connection' ], renderPriority, function(evt, context) { + var type = evt.type, + element = context.element, + visuals = context.gfx, + attrs = context.attrs; + + if (self.canRender(element)) { + if (type === 'render.shape') { + return self.drawShape(visuals, element, attrs); + } else { + return self.drawConnection(visuals, element, attrs); + } + } + }); + + eventBus.on([ 'render.getShapePath', 'render.getConnectionPath' ], renderPriority, function(evt, element) { + if (self.canRender(element)) { + if (evt.type === 'render.getShapePath') { + return self.getShapePath(element); + } else { + return self.getConnectionPath(element); + } + } + }); + } + + /** + * Should check whether *this* renderer can render + * the element/connection. + * + * @param {element} element + * + * @returns {boolean} + */ + BaseRenderer.prototype.canRender = function() {}; + + /** + * Provides the shape's snap svg element to be drawn on the `canvas`. + * + * @param {djs.Graphics} visuals + * @param {Shape} shape + * + * @returns {Snap.svg} [returns a Snap.svg paper element ] + */ + BaseRenderer.prototype.drawShape = function() {}; + + /** + * Provides the shape's snap svg element to be drawn on the `canvas`. + * + * @param {djs.Graphics} visuals + * @param {Connection} connection + * + * @returns {Snap.svg} [returns a Snap.svg paper element ] + */ + BaseRenderer.prototype.drawConnection = function() {}; + + /** + * Gets the SVG path of a shape that represents it's visual bounds. + * + * @param {Shape} shape + * + * @return {string} svg path + */ + BaseRenderer.prototype.getShapePath = function() {}; + + /** + * Gets the SVG path of a connection that represents it's visual bounds. + * + * @param {Connection} connection + * + * @return {string} svg path + */ + BaseRenderer.prototype.getConnectionPath = function() {}; + + function componentsToPath(elements) { + return elements.join(',').replace(/,?([A-z]),?/g, '$1'); + } + + function toSVGPoints(points) { + var result = ''; + + for (var i = 0, p; (p = points[i]); i++) { + result += p.x + ',' + p.y + ' '; + } + + return result; + } + + function createLine(points, attrs) { + + var line = create$1('polyline'); + attr(line, { points: toSVGPoints(points) }); + + if (attrs) { + attr(line, attrs); + } + + return line; + } + + function updateLine(gfx, points) { + attr(gfx, { points: toSVGPoints(points) }); + + return gfx; + } + + /** + * @typedef { {x:number, y: number, width: number, height: number} } Bounds + */ + + /** + * Get parent elements. + * + * @param {Array} elements + * + * @returns {Array} + */ + function getParents$1(elements) { + + // find elements that are not children of any other elements + return filter(elements, function(element) { + return !find(elements, function(e) { + return e !== element && getParent$1(element, e); + }); + }); + } + + + function getParent$1(element, parent) { + if (!parent) { + return; + } + + if (element === parent) { + return parent; + } + + if (!element.parent) { + return; + } + + return getParent$1(element.parent, parent); + } + + + /** + * Adds an element to a collection and returns true if the + * element was added. + * + * @param {Array} elements + * @param {Object} e + * @param {boolean} unique + */ + function add$1(elements, e, unique) { + var canAdd = !unique || elements.indexOf(e) === -1; + + if (canAdd) { + elements.push(e); + } + + return canAdd; + } + + + /** + * Iterate over each element in a collection, calling the iterator function `fn` + * with (element, index, recursionDepth). + * + * Recurse into all elements that are returned by `fn`. + * + * @param {Object|Array} elements + * @param {Function} fn iterator function called with (element, index, recursionDepth) + * @param {number} [depth] maximum recursion depth + */ + function eachElement(elements, fn, depth) { + + depth = depth || 0; + + if (!isArray$3(elements)) { + elements = [ elements ]; + } + + forEach$1(elements, function(s, i) { + var filter = fn(s, i, depth); + + if (isArray$3(filter) && filter.length) { + eachElement(filter, fn, depth + 1); + } + }); + } + + + /** + * Collects self + child elements up to a given depth from a list of elements. + * + * @param {djs.model.Base|Array} elements the elements to select the children from + * @param {boolean} unique whether to return a unique result set (no duplicates) + * @param {number} maxDepth the depth to search through or -1 for infinite + * + * @return {Array} found elements + */ + function selfAndChildren(elements, unique, maxDepth) { + var result = [], + processedChildren = []; + + eachElement(elements, function(element, i, depth) { + add$1(result, element, unique); + + var children = element.children; + + // max traversal depth not reached yet + if (maxDepth === -1 || depth < maxDepth) { + + // children exist && children not yet processed + if (children && add$1(processedChildren, children, unique)) { + return children; + } + } + }); + + return result; + } + + + /** + * Return self + ALL children for a number of elements + * + * @param {Array} elements to query + * @param {boolean} allowDuplicates to allow duplicates in the result set + * + * @return {Array} the collected elements + */ + function selfAndAllChildren(elements, allowDuplicates) { + return selfAndChildren(elements, !allowDuplicates, -1); + } + + + /** + * Gets the the closure for all selected elements, + * their enclosed children and connections. + * + * @param {Array} elements + * @param {boolean} [isTopLevel=true] + * @param {Object} [existingClosure] + * + * @return {Object} newClosure + */ + function getClosure(elements, isTopLevel, closure) { + + if (isUndefined$2(isTopLevel)) { + isTopLevel = true; + } + + if (isObject(isTopLevel)) { + closure = isTopLevel; + isTopLevel = true; + } + + + closure = closure || {}; + + var allShapes = copyObject(closure.allShapes), + allConnections = copyObject(closure.allConnections), + enclosedElements = copyObject(closure.enclosedElements), + enclosedConnections = copyObject(closure.enclosedConnections); + + var topLevel = copyObject( + closure.topLevel, + isTopLevel && groupBy(elements, function(e) { return e.id; }) + ); + + + function handleConnection(c) { + if (topLevel[c.source.id] && topLevel[c.target.id]) { + topLevel[c.id] = [ c ]; + } + + // not enclosed as a child, but maybe logically + // (connecting two moved elements?) + if (allShapes[c.source.id] && allShapes[c.target.id]) { + enclosedConnections[c.id] = enclosedElements[c.id] = c; + } + + allConnections[c.id] = c; + } + + function handleElement(element) { + + enclosedElements[element.id] = element; + + if (element.waypoints) { + + // remember connection + enclosedConnections[element.id] = allConnections[element.id] = element; + } else { + + // remember shape + allShapes[element.id] = element; + + // remember all connections + forEach$1(element.incoming, handleConnection); + + forEach$1(element.outgoing, handleConnection); + + // recurse into children + return element.children; + } + } + + eachElement(elements, handleElement); + + return { + allShapes: allShapes, + allConnections: allConnections, + topLevel: topLevel, + enclosedConnections: enclosedConnections, + enclosedElements: enclosedElements + }; + } + + /** + * Returns the surrounding bbox for all elements in + * the array or the element primitive. + * + * @param {Array|djs.model.Shape} elements + * @param {boolean} [stopRecursion=false] + * + * @return {Bounds} + */ + function getBBox(elements, stopRecursion) { + + stopRecursion = !!stopRecursion; + if (!isArray$3(elements)) { + elements = [ elements ]; + } + + var minX, + minY, + maxX, + maxY; + + forEach$1(elements, function(element) { + + // If element is a connection the bbox must be computed first + var bbox = element; + if (element.waypoints && !stopRecursion) { + bbox = getBBox(element.waypoints, true); + } + + var x = bbox.x, + y = bbox.y, + height = bbox.height || 0, + width = bbox.width || 0; + + if (x < minX || minX === undefined) { + minX = x; + } + if (y < minY || minY === undefined) { + minY = y; + } + + if ((x + width) > maxX || maxX === undefined) { + maxX = x + width; + } + if ((y + height) > maxY || maxY === undefined) { + maxY = y + height; + } + }); + + return { + x: minX, + y: minY, + height: maxY - minY, + width: maxX - minX + }; + } + + + /** + * Returns all elements that are enclosed from the bounding box. + * + * * If bbox.(width|height) is not specified the method returns + * all elements with element.x/y > bbox.x/y + * * If only bbox.x or bbox.y is specified, method return all elements with + * e.x > bbox.x or e.y > bbox.y + * + * @param {Array} elements List of Elements to search through + * @param {djs.model.Shape} bbox the enclosing bbox. + * + * @return {Array} enclosed elements + */ + function getEnclosedElements(elements, bbox) { + + var filteredElements = {}; + + forEach$1(elements, function(element) { + + var e = element; + + if (e.waypoints) { + e = getBBox(e); + } + + if (!isNumber(bbox.y) && (e.x > bbox.x)) { + filteredElements[element.id] = element; + } + if (!isNumber(bbox.x) && (e.y > bbox.y)) { + filteredElements[element.id] = element; + } + if (e.x > bbox.x && e.y > bbox.y) { + if (isNumber(bbox.width) && isNumber(bbox.height) && + e.width + e.x < bbox.width + bbox.x && + e.height + e.y < bbox.height + bbox.y) { + + filteredElements[element.id] = element; + } else if (!isNumber(bbox.width) || !isNumber(bbox.height)) { + filteredElements[element.id] = element; + } + } + }); + + return filteredElements; + } + + + function getType(element) { + + if ('waypoints' in element) { + return 'connection'; + } + + if ('x' in element) { + return 'shape'; + } + + return 'root'; + } + + function isFrameElement$1(element) { + + return !!(element && element.isFrame); + } + + // helpers /////////////////////////////// + + function copyObject(src1, src2) { + return assign({}, src1 || {}, src2 || {}); + } + + // apply default renderer with lowest possible priority + // so that it only kicks in if noone else could render + var DEFAULT_RENDER_PRIORITY = 1; + + /** + * The default renderer used for shapes and connections. + * + * @param {EventBus} eventBus + * @param {Styles} styles + */ + function DefaultRenderer(eventBus, styles) { + + // + BaseRenderer.call(this, eventBus, DEFAULT_RENDER_PRIORITY); + + this.CONNECTION_STYLE = styles.style([ 'no-fill' ], { strokeWidth: 5, stroke: 'fuchsia' }); + this.SHAPE_STYLE = styles.style({ fill: 'white', stroke: 'fuchsia', strokeWidth: 2 }); + this.FRAME_STYLE = styles.style([ 'no-fill' ], { stroke: 'fuchsia', strokeDasharray: 4, strokeWidth: 2 }); + } + + e(DefaultRenderer, BaseRenderer); + + + DefaultRenderer.prototype.canRender = function() { + return true; + }; + + DefaultRenderer.prototype.drawShape = function drawShape(visuals, element, attrs) { + var rect = create$1('rect'); + + attr(rect, { + x: 0, + y: 0, + width: element.width || 0, + height: element.height || 0 + }); + + if (isFrameElement$1(element)) { + attr(rect, assign({}, this.FRAME_STYLE, attrs || {})); + } else { + attr(rect, assign({}, this.SHAPE_STYLE, attrs || {})); + } + + append(visuals, rect); + + return rect; + }; + + DefaultRenderer.prototype.drawConnection = function drawConnection(visuals, connection, attrs) { + + var line = createLine(connection.waypoints, assign({}, this.CONNECTION_STYLE, attrs || {})); + append(visuals, line); + + return line; + }; + + DefaultRenderer.prototype.getShapePath = function getShapePath(shape) { + + var x = shape.x, + y = shape.y, + width = shape.width, + height = shape.height; + + var shapePath = [ + [ 'M', x, y ], + [ 'l', width, 0 ], + [ 'l', 0, height ], + [ 'l', -width, 0 ], + [ 'z' ] + ]; + + return componentsToPath(shapePath); + }; + + DefaultRenderer.prototype.getConnectionPath = function getConnectionPath(connection) { + var waypoints = connection.waypoints; + + var idx, point, connectionPath = []; + + for (idx = 0; (point = waypoints[idx]); idx++) { + + // take invisible docking into account + // when creating the path + point = point.original || point; + + connectionPath.push([ idx === 0 ? 'M' : 'L', point.x, point.y ]); + } + + return componentsToPath(connectionPath); + }; + + + DefaultRenderer.$inject = [ 'eventBus', 'styles' ]; + + /** + * A component that manages shape styles + */ + function Styles() { + + var defaultTraits = { + + 'no-fill': { + fill: 'none' + }, + 'no-border': { + strokeOpacity: 0.0 + }, + 'no-events': { + pointerEvents: 'none' + } + }; + + var self = this; + + /** + * Builds a style definition from a className, a list of traits and an object of additional attributes. + * + * @param {string} className + * @param {Array} traits + * @param {Object} additionalAttrs + * + * @return {Object} the style defintion + */ + this.cls = function(className, traits, additionalAttrs) { + var attrs = this.style(traits, additionalAttrs); + + return assign(attrs, { 'class': className }); + }; + + /** + * Builds a style definition from a list of traits and an object of additional attributes. + * + * @param {Array} traits + * @param {Object} additionalAttrs + * + * @return {Object} the style defintion + */ + this.style = function(traits, additionalAttrs) { + + if (!isArray$3(traits) && !additionalAttrs) { + additionalAttrs = traits; + traits = []; + } + + var attrs = reduce(traits, function(attrs, t) { + return assign(attrs, defaultTraits[t] || {}); + }, {}); + + return additionalAttrs ? assign(attrs, additionalAttrs) : attrs; + }; + + this.computeStyle = function(custom, traits, defaultStyles) { + if (!isArray$3(traits)) { + defaultStyles = traits; + traits = []; + } + + return self.style(traits || [], assign({}, defaultStyles, custom || {})); + }; + } + + var DrawModule$1 = { + __init__: [ 'defaultRenderer' ], + defaultRenderer: [ 'type', DefaultRenderer ], + styles: [ 'type', Styles ] + }; + + /** + * Failsafe remove an element from a collection + * + * @param {Array} [collection] + * @param {Object} [element] + * + * @return {number} the previous index of the element + */ + function remove(collection, element) { + + if (!collection || !element) { + return -1; + } + + var idx = collection.indexOf(element); + + if (idx !== -1) { + collection.splice(idx, 1); + } + + return idx; + } + + /** + * Fail save add an element to the given connection, ensuring + * it does not yet exist. + * + * @param {Array} collection + * @param {Object} element + * @param {number} idx + */ + function add(collection, element, idx) { + + if (!collection || !element) { + return; + } + + if (typeof idx !== 'number') { + idx = -1; + } + + var currentIdx = collection.indexOf(element); + + if (currentIdx !== -1) { + + if (currentIdx === idx) { + + // nothing to do, position has not changed + return; + } else { + + if (idx !== -1) { + + // remove from current position + collection.splice(currentIdx, 1); + } else { + + // already exists in collection + return; + } + } + } + + if (idx !== -1) { + + // insert at specified position + collection.splice(idx, 0, element); + } else { + + // push to end + collection.push(element); + } + } + + + /** + * Fail save get the index of an element in a collection. + * + * @param {Array} collection + * @param {Object} element + * + * @return {number} the index or -1 if collection or element do + * not exist or the element is not contained. + */ + function indexOf(collection, element) { + + if (!collection || !element) { + return -1; + } + + return collection.indexOf(element); + } + + /** + * Computes the distance between two points + * + * @param {Point} p + * @param {Point} q + * + * @return {number} distance + */ + function pointDistance(a, b) { + if (!a || !b) { + return -1; + } + + return Math.sqrt( + Math.pow(a.x - b.x, 2) + + Math.pow(a.y - b.y, 2) + ); + } + + + /** + * Returns true if the point r is on the line between p and q + * + * @param {Point} p + * @param {Point} q + * @param {Point} r + * @param {number} [accuracy=5] accuracy for points on line check (lower is better) + * + * @return {boolean} + */ + function pointsOnLine(p, q, r, accuracy) { + + if (typeof accuracy === 'undefined') { + accuracy = 5; + } + + if (!p || !q || !r) { + return false; + } + + var val = (q.x - p.x) * (r.y - p.y) - (q.y - p.y) * (r.x - p.x), + dist = pointDistance(p, q); + + // @see http://stackoverflow.com/a/907491/412190 + return Math.abs(val / dist) <= accuracy; + } + + + var ALIGNED_THRESHOLD = 2; + + /** + * Check whether two points are horizontally or vertically aligned. + * + * @param {Array|Point} + * @param {Point} + * + * @return {string|boolean} + */ + function pointsAligned(a, b) { + var points; + + if (isArray$3(a)) { + points = a; + } else { + points = [ a, b ]; + } + + if (pointsAlignedHorizontally(points)) { + return 'h'; + } + + if (pointsAlignedVertically(points)) { + return 'v'; + } + + return false; + } + + function pointsAlignedHorizontally(a, b) { + var points; + + if (isArray$3(a)) { + points = a; + } else { + points = [ a, b ]; + } + + var firstPoint = points.slice().shift(); + + return every(points, function(point) { + return Math.abs(firstPoint.y - point.y) <= ALIGNED_THRESHOLD; + }); + } + + function pointsAlignedVertically(a, b) { + var points; + + if (isArray$3(a)) { + points = a; + } else { + points = [ a, b ]; + } + + var firstPoint = points.slice().shift(); + + return every(points, function(point) { + return Math.abs(firstPoint.x - point.x) <= ALIGNED_THRESHOLD; + }); + } + + + + /** + * Returns true if the point p is inside the rectangle rect + * + * @param {Point} p + * @param {Rect} rect + * @param {number} tolerance + * + * @return {boolean} + */ + function pointInRect(p, rect, tolerance) { + tolerance = tolerance || 0; + + return p.x > rect.x - tolerance && + p.y > rect.y - tolerance && + p.x < rect.x + rect.width + tolerance && + p.y < rect.y + rect.height + tolerance; + } + + /** + * Returns a point in the middle of points p and q + * + * @param {Point} p + * @param {Point} q + * + * @return {Point} middle point + */ + function getMidPoint(p, q) { + return { + x: Math.round(p.x + ((q.x - p.x) / 2.0)), + y: Math.round(p.y + ((q.y - p.y) / 2.0)) + }; + } + + var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + + function getDefaultExportFromCjs (x) { + return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; + } + + /** + * This file contains source code adapted from Snap.svg (licensed Apache-2.0). + * + * @see https://github.com/adobe-webplatform/Snap.svg/blob/master/src/path.js + */ + + /* eslint no-fallthrough: "off" */ + + var p2s = /,?([a-z]),?/gi, + toFloat = parseFloat, + math = Math, + PI = math.PI, + mmin = math.min, + mmax = math.max, + pow = math.pow, + abs$7 = math.abs, + pathCommand = /([a-z])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?[\s]*,?[\s]*)+)/ig, + pathValues = /(-?\d*\.?\d*(?:e[-+]?\d+)?)[\s]*,?[\s]*/ig; + + var isArray = Array.isArray || function(o) { return o instanceof Array; }; + + function hasProperty(obj, property) { + return Object.prototype.hasOwnProperty.call(obj, property); + } + + function clone(obj) { + + if (typeof obj == 'function' || Object(obj) !== obj) { + return obj; + } + + var res = new obj.constructor; + + for (var key in obj) { + if (hasProperty(obj, key)) { + res[key] = clone(obj[key]); + } + } + + return res; + } + + function repush(array, item) { + for (var i = 0, ii = array.length; i < ii; i++) if (array[i] === item) { + return array.push(array.splice(i, 1)[0]); + } + } + + function cacher(f) { + + function newf() { + + var arg = Array.prototype.slice.call(arguments, 0), + args = arg.join('\u2400'), + cache = newf.cache = newf.cache || {}, + count = newf.count = newf.count || []; + + if (hasProperty(cache, args)) { + repush(count, args); + return cache[args]; + } + + count.length >= 1e3 && delete cache[count.shift()]; + count.push(args); + cache[args] = f.apply(0, arg); + + return cache[args]; + } + return newf; + } + + function parsePathString(pathString) { + + if (!pathString) { + return null; + } + + var pth = paths(pathString); + + if (pth.arr) { + return clone(pth.arr); + } + + var paramCounts = { a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0 }, + data = []; + + if (isArray(pathString) && isArray(pathString[0])) { // rough assumption + data = clone(pathString); + } + + if (!data.length) { + + String(pathString).replace(pathCommand, function(a, b, c) { + var params = [], + name = b.toLowerCase(); + + c.replace(pathValues, function(a, b) { + b && params.push(+b); + }); + + if (name == 'm' && params.length > 2) { + data.push([b].concat(params.splice(0, 2))); + name = 'l'; + b = b == 'm' ? 'l' : 'L'; + } + + while (params.length >= paramCounts[name]) { + data.push([b].concat(params.splice(0, paramCounts[name]))); + if (!paramCounts[name]) { + break; + } + } + }); + } + + data.toString = paths.toString; + pth.arr = clone(data); + + return data; + } + + function paths(ps) { + var p = paths.ps = paths.ps || {}; + + if (p[ps]) { + p[ps].sleep = 100; + } else { + p[ps] = { + sleep: 100 + }; + } + + setTimeout(function() { + for (var key in p) { + if (hasProperty(p, key) && key != ps) { + p[key].sleep--; + !p[key].sleep && delete p[key]; + } + } + }); + + return p[ps]; + } + + function rectBBox(x, y, width, height) { + + if (arguments.length === 1) { + y = x.y; + width = x.width; + height = x.height; + x = x.x; + } + + return { + x: x, + y: y, + width: width, + height: height, + x2: x + width, + y2: y + height + }; + } + + function pathToString() { + return this.join(',').replace(p2s, '$1'); + } + + function pathClone(pathArray) { + var res = clone(pathArray); + res.toString = pathToString; + return res; + } + + function findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { + var t1 = 1 - t, + t13 = pow(t1, 3), + t12 = pow(t1, 2), + t2 = t * t, + t3 = t2 * t, + x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x, + y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y; + + return { + x: fixError(x), + y: fixError(y) + }; + } + + function bezierBBox(points) { + + var bbox = curveBBox.apply(null, points); + + return rectBBox( + bbox.x0, + bbox.y0, + bbox.x1 - bbox.x0, + bbox.y1 - bbox.y0 + ); + } + + function isPointInsideBBox$2(bbox, x, y) { + return x >= bbox.x && + x <= bbox.x + bbox.width && + y >= bbox.y && + y <= bbox.y + bbox.height; + } + + function isBBoxIntersect(bbox1, bbox2) { + bbox1 = rectBBox(bbox1); + bbox2 = rectBBox(bbox2); + return isPointInsideBBox$2(bbox2, bbox1.x, bbox1.y) + || isPointInsideBBox$2(bbox2, bbox1.x2, bbox1.y) + || isPointInsideBBox$2(bbox2, bbox1.x, bbox1.y2) + || isPointInsideBBox$2(bbox2, bbox1.x2, bbox1.y2) + || isPointInsideBBox$2(bbox1, bbox2.x, bbox2.y) + || isPointInsideBBox$2(bbox1, bbox2.x2, bbox2.y) + || isPointInsideBBox$2(bbox1, bbox2.x, bbox2.y2) + || isPointInsideBBox$2(bbox1, bbox2.x2, bbox2.y2) + || (bbox1.x < bbox2.x2 && bbox1.x > bbox2.x + || bbox2.x < bbox1.x2 && bbox2.x > bbox1.x) + && (bbox1.y < bbox2.y2 && bbox1.y > bbox2.y + || bbox2.y < bbox1.y2 && bbox2.y > bbox1.y); + } + + function base3(t, p1, p2, p3, p4) { + var t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4, + t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3; + return t * t2 - 3 * p1 + 3 * p2; + } + + function bezlen(x1, y1, x2, y2, x3, y3, x4, y4, z) { + + if (z == null) { + z = 1; + } + + z = z > 1 ? 1 : z < 0 ? 0 : z; + + var z2 = z / 2, + n = 12, + Tvalues = [-.1252,.1252,-.3678,.3678,-.5873,.5873,-.7699,.7699,-.9041,.9041,-.9816,.9816], + Cvalues = [0.2491,0.2491,0.2335,0.2335,0.2032,0.2032,0.1601,0.1601,0.1069,0.1069,0.0472,0.0472], + sum = 0; + + for (var i = 0; i < n; i++) { + var ct = z2 * Tvalues[i] + z2, + xbase = base3(ct, x1, x2, x3, x4), + ybase = base3(ct, y1, y2, y3, y4), + comb = xbase * xbase + ybase * ybase; + + sum += Cvalues[i] * math.sqrt(comb); + } + + return z2 * sum; + } + + + function intersectLines(x1, y1, x2, y2, x3, y3, x4, y4) { + + if ( + mmax(x1, x2) < mmin(x3, x4) || + mmin(x1, x2) > mmax(x3, x4) || + mmax(y1, y2) < mmin(y3, y4) || + mmin(y1, y2) > mmax(y3, y4) + ) { + return; + } + + var nx = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4), + ny = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4), + denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); + + if (!denominator) { + return; + } + + var px = fixError(nx / denominator), + py = fixError(ny / denominator), + px2 = +px.toFixed(2), + py2 = +py.toFixed(2); + + if ( + px2 < +mmin(x1, x2).toFixed(2) || + px2 > +mmax(x1, x2).toFixed(2) || + px2 < +mmin(x3, x4).toFixed(2) || + px2 > +mmax(x3, x4).toFixed(2) || + py2 < +mmin(y1, y2).toFixed(2) || + py2 > +mmax(y1, y2).toFixed(2) || + py2 < +mmin(y3, y4).toFixed(2) || + py2 > +mmax(y3, y4).toFixed(2) + ) { + return; + } + + return { x: px, y: py }; + } + + function fixError(number) { + return Math.round(number * 100000000000) / 100000000000; + } + + function findBezierIntersections(bez1, bez2, justCount) { + var bbox1 = bezierBBox(bez1), + bbox2 = bezierBBox(bez2); + + if (!isBBoxIntersect(bbox1, bbox2)) { + return justCount ? 0 : []; + } + + // As an optimization, lines will have only 1 segment + + var l1 = bezlen.apply(0, bez1), + l2 = bezlen.apply(0, bez2), + n1 = isLine(bez1) ? 1 : ~~(l1 / 5) || 1, + n2 = isLine(bez2) ? 1 : ~~(l2 / 5) || 1, + dots1 = [], + dots2 = [], + xy = {}, + res = justCount ? 0 : []; + + for (var i = 0; i < n1 + 1; i++) { + var p = findDotsAtSegment.apply(0, bez1.concat(i / n1)); + dots1.push({ x: p.x, y: p.y, t: i / n1 }); + } + + for (i = 0; i < n2 + 1; i++) { + p = findDotsAtSegment.apply(0, bez2.concat(i / n2)); + dots2.push({ x: p.x, y: p.y, t: i / n2 }); + } + + for (i = 0; i < n1; i++) { + + for (var j = 0; j < n2; j++) { + var di = dots1[i], + di1 = dots1[i + 1], + dj = dots2[j], + dj1 = dots2[j + 1], + ci = abs$7(di1.x - di.x) < .01 ? 'y' : 'x', + cj = abs$7(dj1.x - dj.x) < .01 ? 'y' : 'x', + is = intersectLines(di.x, di.y, di1.x, di1.y, dj.x, dj.y, dj1.x, dj1.y), + key; + + if (is) { + key = is.x.toFixed(9) + '#' + is.y.toFixed(9); + + if (xy[key]) { + continue; + } + + xy[key] = true; + + var t1 = di.t + abs$7((is[ci] - di[ci]) / (di1[ci] - di[ci])) * (di1.t - di.t), + t2 = dj.t + abs$7((is[cj] - dj[cj]) / (dj1[cj] - dj[cj])) * (dj1.t - dj.t); + + if (t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1) { + + if (justCount) { + res++; + } else { + res.push({ + x: is.x, + y: is.y, + t1: t1, + t2: t2 + }); + } + } + } + } + } + + return res; + } + + + /** + * Find or counts the intersections between two SVG paths. + * + * Returns a number in counting mode and a list of intersections otherwise. + * + * A single intersection entry contains the intersection coordinates (x, y) + * as well as additional information regarding the intersecting segments + * on each path (segment1, segment2) and the relative location of the + * intersection on these segments (t1, t2). + * + * The path may be an SVG path string or a list of path components + * such as `[ [ 'M', 0, 10 ], [ 'L', 20, 0 ] ]`. + * + * @example + * + * var intersections = findPathIntersections( + * 'M0,0L100,100', + * [ [ 'M', 0, 100 ], [ 'L', 100, 0 ] ] + * ); + * + * // intersections = [ + * // { x: 50, y: 50, segment1: 1, segment2: 1, t1: 0.5, t2: 0.5 } + * // ] + * + * @param {String|Array} path1 + * @param {String|Array} path2 + * @param {Boolean} [justCount=false] + * + * @return {Array|Number} + */ + function findPathIntersections(path1, path2, justCount) { + path1 = pathToCurve(path1); + path2 = pathToCurve(path2); + + var x1, y1, x2, y2, x1m, y1m, x2m, y2m, bez1, bez2, + res = justCount ? 0 : []; + + for (var i = 0, ii = path1.length; i < ii; i++) { + var pi = path1[i]; + + if (pi[0] == 'M') { + x1 = x1m = pi[1]; + y1 = y1m = pi[2]; + } else { + + if (pi[0] == 'C') { + bez1 = [x1, y1].concat(pi.slice(1)); + x1 = bez1[6]; + y1 = bez1[7]; + } else { + bez1 = [x1, y1, x1, y1, x1m, y1m, x1m, y1m]; + x1 = x1m; + y1 = y1m; + } + + for (var j = 0, jj = path2.length; j < jj; j++) { + var pj = path2[j]; + + if (pj[0] == 'M') { + x2 = x2m = pj[1]; + y2 = y2m = pj[2]; + } else { + + if (pj[0] == 'C') { + bez2 = [x2, y2].concat(pj.slice(1)); + x2 = bez2[6]; + y2 = bez2[7]; + } else { + bez2 = [x2, y2, x2, y2, x2m, y2m, x2m, y2m]; + x2 = x2m; + y2 = y2m; + } + + var intr = findBezierIntersections(bez1, bez2, justCount); + + if (justCount) { + res += intr; + } else { + + for (var k = 0, kk = intr.length; k < kk; k++) { + intr[k].segment1 = i; + intr[k].segment2 = j; + intr[k].bez1 = bez1; + intr[k].bez2 = bez2; + } + + res = res.concat(intr); + } + } + } + } + } + + return res; + } + + + function pathToAbsolute(pathArray) { + var pth = paths(pathArray); + + if (pth.abs) { + return pathClone(pth.abs); + } + + if (!isArray(pathArray) || !isArray(pathArray && pathArray[0])) { // rough assumption + pathArray = parsePathString(pathArray); + } + + if (!pathArray || !pathArray.length) { + return [['M', 0, 0]]; + } + + var res = [], + x = 0, + y = 0, + mx = 0, + my = 0, + start = 0, + pa0; + + if (pathArray[0][0] == 'M') { + x = +pathArray[0][1]; + y = +pathArray[0][2]; + mx = x; + my = y; + start++; + res[0] = ['M', x, y]; + } + + for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) { + res.push(r = []); + pa = pathArray[i]; + pa0 = pa[0]; + + if (pa0 != pa0.toUpperCase()) { + r[0] = pa0.toUpperCase(); + + switch (r[0]) { + case 'A': + r[1] = pa[1]; + r[2] = pa[2]; + r[3] = pa[3]; + r[4] = pa[4]; + r[5] = pa[5]; + r[6] = +pa[6] + x; + r[7] = +pa[7] + y; + break; + case 'V': + r[1] = +pa[1] + y; + break; + case 'H': + r[1] = +pa[1] + x; + break; + case 'M': + mx = +pa[1] + x; + my = +pa[2] + y; + default: + for (var j = 1, jj = pa.length; j < jj; j++) { + r[j] = +pa[j] + ((j % 2) ? x : y); + } + } + } else { + for (var k = 0, kk = pa.length; k < kk; k++) { + r[k] = pa[k]; + } + } + pa0 = pa0.toUpperCase(); + + switch (r[0]) { + case 'Z': + x = +mx; + y = +my; + break; + case 'H': + x = r[1]; + break; + case 'V': + y = r[1]; + break; + case 'M': + mx = r[r.length - 2]; + my = r[r.length - 1]; + default: + x = r[r.length - 2]; + y = r[r.length - 1]; + } + } + + res.toString = pathToString; + pth.abs = pathClone(res); + + return res; + } + + function isLine(bez) { + return ( + bez[0] === bez[2] && + bez[1] === bez[3] && + bez[4] === bez[6] && + bez[5] === bez[7] + ); + } + + function lineToCurve(x1, y1, x2, y2) { + return [ + x1, y1, x2, + y2, x2, y2 + ]; + } + + function qubicToCurve(x1, y1, ax, ay, x2, y2) { + var _13 = 1 / 3, + _23 = 2 / 3; + + return [ + _13 * x1 + _23 * ax, + _13 * y1 + _23 * ay, + _13 * x2 + _23 * ax, + _13 * y2 + _23 * ay, + x2, + y2 + ]; + } + + function arcToCurve(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) { + + // for more information of where this math came from visit: + // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes + var _120 = PI * 120 / 180, + rad = PI / 180 * (+angle || 0), + res = [], + xy, + rotate = cacher(function(x, y, rad) { + var X = x * math.cos(rad) - y * math.sin(rad), + Y = x * math.sin(rad) + y * math.cos(rad); + + return { x: X, y: Y }; + }); + + if (!recursive) { + xy = rotate(x1, y1, -rad); + x1 = xy.x; + y1 = xy.y; + xy = rotate(x2, y2, -rad); + x2 = xy.x; + y2 = xy.y; + + var x = (x1 - x2) / 2, + y = (y1 - y2) / 2; + + var h = (x * x) / (rx * rx) + (y * y) / (ry * ry); + + if (h > 1) { + h = math.sqrt(h); + rx = h * rx; + ry = h * ry; + } + + var rx2 = rx * rx, + ry2 = ry * ry, + k = (large_arc_flag == sweep_flag ? -1 : 1) * + math.sqrt(abs$7((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))), + cx = k * rx * y / ry + (x1 + x2) / 2, + cy = k * -ry * x / rx + (y1 + y2) / 2, + f1 = math.asin(((y1 - cy) / ry).toFixed(9)), + f2 = math.asin(((y2 - cy) / ry).toFixed(9)); + + f1 = x1 < cx ? PI - f1 : f1; + f2 = x2 < cx ? PI - f2 : f2; + f1 < 0 && (f1 = PI * 2 + f1); + f2 < 0 && (f2 = PI * 2 + f2); + + if (sweep_flag && f1 > f2) { + f1 = f1 - PI * 2; + } + if (!sweep_flag && f2 > f1) { + f2 = f2 - PI * 2; + } + } else { + f1 = recursive[0]; + f2 = recursive[1]; + cx = recursive[2]; + cy = recursive[3]; + } + + var df = f2 - f1; + + if (abs$7(df) > _120) { + var f2old = f2, + x2old = x2, + y2old = y2; + + f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1); + x2 = cx + rx * math.cos(f2); + y2 = cy + ry * math.sin(f2); + res = arcToCurve(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]); + } + + df = f2 - f1; + + var c1 = math.cos(f1), + s1 = math.sin(f1), + c2 = math.cos(f2), + s2 = math.sin(f2), + t = math.tan(df / 4), + hx = 4 / 3 * rx * t, + hy = 4 / 3 * ry * t, + m1 = [x1, y1], + m2 = [x1 + hx * s1, y1 - hy * c1], + m3 = [x2 + hx * s2, y2 - hy * c2], + m4 = [x2, y2]; + + m2[0] = 2 * m1[0] - m2[0]; + m2[1] = 2 * m1[1] - m2[1]; + + if (recursive) { + return [m2, m3, m4].concat(res); + } else { + res = [m2, m3, m4].concat(res).join().split(','); + var newres = []; + + for (var i = 0, ii = res.length; i < ii; i++) { + newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x; + } + + return newres; + } + } + + // Returns bounding box of cubic bezier curve. + // Source: http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html + // Original version: NISHIO Hirokazu + // Modifications: https://github.com/timo22345 + function curveBBox(x0, y0, x1, y1, x2, y2, x3, y3) { + var tvalues = [], + bounds = [[], []], + a, b, c, t, t1, t2, b2ac, sqrtb2ac; + + for (var i = 0; i < 2; ++i) { + + if (i == 0) { + b = 6 * x0 - 12 * x1 + 6 * x2; + a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3; + c = 3 * x1 - 3 * x0; + } else { + b = 6 * y0 - 12 * y1 + 6 * y2; + a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3; + c = 3 * y1 - 3 * y0; + } + + if (abs$7(a) < 1e-12) { + + if (abs$7(b) < 1e-12) { + continue; + } + + t = -c / b; + + if (0 < t && t < 1) { + tvalues.push(t); + } + + continue; + } + + b2ac = b * b - 4 * c * a; + sqrtb2ac = math.sqrt(b2ac); + + if (b2ac < 0) { + continue; + } + + t1 = (-b + sqrtb2ac) / (2 * a); + + if (0 < t1 && t1 < 1) { + tvalues.push(t1); + } + + t2 = (-b - sqrtb2ac) / (2 * a); + + if (0 < t2 && t2 < 1) { + tvalues.push(t2); + } + } + + var j = tvalues.length, + jlen = j, + mt; + + while (j--) { + t = tvalues[j]; + mt = 1 - t; + bounds[0][j] = (mt * mt * mt * x0) + (3 * mt * mt * t * x1) + (3 * mt * t * t * x2) + (t * t * t * x3); + bounds[1][j] = (mt * mt * mt * y0) + (3 * mt * mt * t * y1) + (3 * mt * t * t * y2) + (t * t * t * y3); + } + + bounds[0][jlen] = x0; + bounds[1][jlen] = y0; + bounds[0][jlen + 1] = x3; + bounds[1][jlen + 1] = y3; + bounds[0].length = bounds[1].length = jlen + 2; + + return { + x0: mmin.apply(0, bounds[0]), + y0: mmin.apply(0, bounds[1]), + x1: mmax.apply(0, bounds[0]), + y1: mmax.apply(0, bounds[1]) + }; + } + + function pathToCurve(path) { + + var pth = paths(path); + + // return cached curve, if existing + if (pth.curve) { + return pathClone(pth.curve); + } + + var curvedPath = pathToAbsolute(path), + attrs = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null }, + processPath = function(path, d, pathCommand) { + var nx, ny; + + if (!path) { + return ['C', d.x, d.y, d.x, d.y, d.x, d.y]; + } + + !(path[0] in { T: 1, Q: 1 }) && (d.qx = d.qy = null); + + switch (path[0]) { + case 'M': + d.X = path[1]; + d.Y = path[2]; + break; + case 'A': + path = ['C'].concat(arcToCurve.apply(0, [d.x, d.y].concat(path.slice(1)))); + break; + case 'S': + if (pathCommand == 'C' || pathCommand == 'S') { + + // In 'S' case we have to take into account, if the previous command is C/S. + nx = d.x * 2 - d.bx; + + // And reflect the previous + ny = d.y * 2 - d.by; + + // command's control point relative to the current point. + } + else { + + // or some else or nothing + nx = d.x; + ny = d.y; + } + path = ['C', nx, ny].concat(path.slice(1)); + break; + case 'T': + if (pathCommand == 'Q' || pathCommand == 'T') { + + // In 'T' case we have to take into account, if the previous command is Q/T. + d.qx = d.x * 2 - d.qx; + + // And make a reflection similar + d.qy = d.y * 2 - d.qy; + + // to case 'S'. + } + else { + + // or something else or nothing + d.qx = d.x; + d.qy = d.y; + } + path = ['C'].concat(qubicToCurve(d.x, d.y, d.qx, d.qy, path[1], path[2])); + break; + case 'Q': + d.qx = path[1]; + d.qy = path[2]; + path = ['C'].concat(qubicToCurve(d.x, d.y, path[1], path[2], path[3], path[4])); + break; + case 'L': + path = ['C'].concat(lineToCurve(d.x, d.y, path[1], path[2])); + break; + case 'H': + path = ['C'].concat(lineToCurve(d.x, d.y, path[1], d.y)); + break; + case 'V': + path = ['C'].concat(lineToCurve(d.x, d.y, d.x, path[1])); + break; + case 'Z': + path = ['C'].concat(lineToCurve(d.x, d.y, d.X, d.Y)); + break; + } + + return path; + }, + + fixArc = function(pp, i) { + + if (pp[i].length > 7) { + pp[i].shift(); + var pi = pp[i]; + + while (pi.length) { + pathCommands[i] = 'A'; // if created multiple C:s, their original seg is saved + pp.splice(i++, 0, ['C'].concat(pi.splice(0, 6))); + } + + pp.splice(i, 1); + ii = curvedPath.length; + } + }, + + pathCommands = [], // path commands of original path p + pfirst = '', // temporary holder for original path command + pathCommand = ''; // holder for previous path command of original path + + for (var i = 0, ii = curvedPath.length; i < ii; i++) { + curvedPath[i] && (pfirst = curvedPath[i][0]); // save current path command + + if (pfirst != 'C') // C is not saved yet, because it may be result of conversion + { + pathCommands[i] = pfirst; // Save current path command + i && (pathCommand = pathCommands[i - 1]); // Get previous path command pathCommand + } + curvedPath[i] = processPath(curvedPath[i], attrs, pathCommand); // Previous path command is inputted to processPath + + if (pathCommands[i] != 'A' && pfirst == 'C') pathCommands[i] = 'C'; // A is the only command + // which may produce multiple C:s + // so we have to make sure that C is also C in original path + + fixArc(curvedPath, i); // fixArc adds also the right amount of A:s to pathCommands + + var seg = curvedPath[i], + seglen = seg.length; + + attrs.x = seg[seglen - 2]; + attrs.y = seg[seglen - 1]; + attrs.bx = toFloat(seg[seglen - 4]) || attrs.x; + attrs.by = toFloat(seg[seglen - 3]) || attrs.y; + } + + // cache curve + pth.curve = pathClone(curvedPath); + + return curvedPath; + } + + var intersect = findPathIntersections; + + function roundBounds(bounds) { + return { + x: Math.round(bounds.x), + y: Math.round(bounds.y), + width: Math.round(bounds.width), + height: Math.round(bounds.height) + }; + } + + + function roundPoint(point) { + + return { + x: Math.round(point.x), + y: Math.round(point.y) + }; + } + + + /** + * Convert the given bounds to a { top, left, bottom, right } descriptor. + * + * @param {Bounds|Point} bounds + * + * @return {Object} + */ + function asTRBL(bounds) { + return { + top: bounds.y, + right: bounds.x + (bounds.width || 0), + bottom: bounds.y + (bounds.height || 0), + left: bounds.x + }; + } + + + /** + * Convert a { top, left, bottom, right } to an objects bounds. + * + * @param {Object} trbl + * + * @return {Bounds} + */ + function asBounds(trbl) { + return { + x: trbl.left, + y: trbl.top, + width: trbl.right - trbl.left, + height: trbl.bottom - trbl.top + }; + } + + + /** + * Get the mid of the given bounds or point. + * + * @param {Bounds|Point} bounds + * + * @return {Point} + */ + function getBoundsMid(bounds) { + return roundPoint({ + x: bounds.x + (bounds.width || 0) / 2, + y: bounds.y + (bounds.height || 0) / 2 + }); + } + + + /** + * Get the mid of the given Connection. + * + * @param {djs.Base.Connection} connection + * + * @return {Point} + */ + function getConnectionMid(connection) { + var waypoints = connection.waypoints; + + // calculate total length and length of each segment + var parts = waypoints.reduce(function(parts, point, index) { + + var lastPoint = waypoints[index - 1]; + + if (lastPoint) { + var lastPart = parts[parts.length - 1]; + + var startLength = lastPart && lastPart.endLength || 0; + var length = distance(lastPoint, point); + + parts.push({ + start: lastPoint, + end: point, + startLength: startLength, + endLength: startLength + length, + length: length + }); + } + + return parts; + }, []); + + var totalLength = parts.reduce(function(length, part) { + return length + part.length; + }, 0); + + // find which segement contains middle point + var midLength = totalLength / 2; + + var i = 0; + var midSegment = parts[i]; + + while (midSegment.endLength < midLength) { + midSegment = parts[++i]; + } + + // calculate relative position on mid segment + var segmentProgress = (midLength - midSegment.startLength) / midSegment.length; + + var midPoint = { + x: midSegment.start.x + (midSegment.end.x - midSegment.start.x) * segmentProgress, + y: midSegment.start.y + (midSegment.end.y - midSegment.start.y) * segmentProgress + }; + + return midPoint; + } + + + /** + * Get the mid of the given Element. + * + * @param {djs.Base.Connection} connection + * + * @return {Point} + */ + function getMid(element) { + if (isConnection$f(element)) { + return getConnectionMid(element); + } + + return getBoundsMid(element); + } + + // orientation utils ////////////////////// + + /** + * Get orientation of the given rectangle with respect to + * the reference rectangle. + * + * A padding (positive or negative) may be passed to influence + * horizontal / vertical orientation and intersection. + * + * @param {Bounds} rect + * @param {Bounds} reference + * @param {Point|number} padding + * + * @return {string} the orientation; one of top, top-left, left, ..., bottom, right or intersect. + */ + function getOrientation(rect, reference, padding) { + + padding = padding || 0; + + // make sure we can use an object, too + // for individual { x, y } padding + if (!isObject(padding)) { + padding = { x: padding, y: padding }; + } + + + var rectOrientation = asTRBL(rect), + referenceOrientation = asTRBL(reference); + + var top = rectOrientation.bottom + padding.y <= referenceOrientation.top, + right = rectOrientation.left - padding.x >= referenceOrientation.right, + bottom = rectOrientation.top - padding.y >= referenceOrientation.bottom, + left = rectOrientation.right + padding.x <= referenceOrientation.left; + + var vertical = top ? 'top' : (bottom ? 'bottom' : null), + horizontal = left ? 'left' : (right ? 'right' : null); + + if (horizontal && vertical) { + return vertical + '-' + horizontal; + } else { + return horizontal || vertical || 'intersect'; + } + } + + + // intersection utils ////////////////////// + + /** + * Get intersection between an element and a line path. + * + * @param {PathDef} elementPath + * @param {PathDef} linePath + * @param {boolean} cropStart crop from start or end + * + * @return {Point} + */ + function getElementLineIntersection(elementPath, linePath, cropStart) { + + var intersections = getIntersections(elementPath, linePath); + + // recognize intersections + // only one -> choose + // two close together -> choose first + // two or more distinct -> pull out appropriate one + // none -> ok (fallback to point itself) + if (intersections.length === 1) { + return roundPoint(intersections[0]); + } else if (intersections.length === 2 && pointDistance(intersections[0], intersections[1]) < 1) { + return roundPoint(intersections[0]); + } else if (intersections.length > 1) { + + // sort by intersections based on connection segment + + // distance from start + intersections = sortBy(intersections, function(i) { + var distance = Math.floor(i.t2 * 100) || 1; + + distance = 100 - distance; + + distance = (distance < 10 ? '0' : '') + distance; + + // create a sort string that makes sure we sort + // line segment ASC + line segment position DESC (for cropStart) + // line segment ASC + line segment position ASC (for cropEnd) + return i.segment2 + '#' + distance; + }); + + return roundPoint(intersections[cropStart ? 0 : intersections.length - 1]); + } + + return null; + } + + + function getIntersections(a, b) { + return intersect(a, b); + } + + + function filterRedundantWaypoints(waypoints) { + + // alter copy of waypoints, not original + waypoints = waypoints.slice(); + + var idx = 0, + point, + previousPoint, + nextPoint; + + while (waypoints[idx]) { + point = waypoints[idx]; + previousPoint = waypoints[idx - 1]; + nextPoint = waypoints[idx + 1]; + + if (pointDistance(point, nextPoint) === 0 || + pointsOnLine(previousPoint, nextPoint, point)) { + + // remove point, if overlapping with {nextPoint} + // or on line with {previousPoint} -> {point} -> {nextPoint} + waypoints.splice(idx, 1); + } else { + idx++; + } + } + + return waypoints; + } + + // helpers ////////////////////// + + function distance(a, b) { + return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2)); + } + + function isConnection$f(element) { + return !!element.waypoints; + } + + function round$b(number, resolution) { + return Math.round(number * resolution) / resolution; + } + + function ensurePx(number) { + return isNumber(number) ? number + 'px' : number; + } + + function findRoot(element) { + while (element.parent) { + element = element.parent; + } + + return element; + } + + /** + * Creates a HTML container element for a SVG element with + * the given configuration + * + * @param {Object} options + * @return {HTMLElement} the container element + */ + function createContainer(options) { + + options = assign({}, { width: '100%', height: '100%' }, options); + + var container = options.container || document.body; + + // create a
around the svg element with the respective size + // this way we can always get the correct container size + // (this is impossible for elements at the moment) + var parent = document.createElement('div'); + parent.setAttribute('class', 'djs-container'); + + assign$1(parent, { + position: 'relative', + overflow: 'hidden', + width: ensurePx(options.width), + height: ensurePx(options.height) + }); + + container.appendChild(parent); + + return parent; + } + + function createGroup(parent, cls, childIndex) { + var group = create$1('g'); + classes(group).add(cls); + + var index = childIndex !== undefined ? childIndex : parent.childNodes.length - 1; + + // must ensure second argument is node or _null_ + // cf. https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore + parent.insertBefore(group, parent.childNodes[index] || null); + + return group; + } + + var BASE_LAYER = 'base'; + + // render plane contents behind utility layers + var PLANE_LAYER_INDEX = 0; + var UTILITY_LAYER_INDEX = 1; + + + var REQUIRED_MODEL_ATTRS = { + shape: [ 'x', 'y', 'width', 'height' ], + connection: [ 'waypoints' ] + }; + + /** + * The main drawing canvas. + * + * @class + * @constructor + * + * @emits Canvas#canvas.init + * + * @param {Object} config + * @param {EventBus} eventBus + * @param {GraphicsFactory} graphicsFactory + * @param {ElementRegistry} elementRegistry + */ + function Canvas(config, eventBus, graphicsFactory, elementRegistry) { + + this._eventBus = eventBus; + this._elementRegistry = elementRegistry; + this._graphicsFactory = graphicsFactory; + + this._rootsIdx = 0; + + this._layers = {}; + this._planes = []; + this._rootElement = null; + + this._init(config || {}); + } + + Canvas.$inject = [ + 'config.canvas', + 'eventBus', + 'graphicsFactory', + 'elementRegistry' + ]; + + /** + * Creates a element that is wrapped into a
. + * This way we are always able to correctly figure out the size of the svg element + * by querying the parent node. + + * (It is not possible to get the size of a svg element cross browser @ 2014-04-01) + + *
+ * + * ... + * + *
+ */ + Canvas.prototype._init = function(config) { + + var eventBus = this._eventBus; + + // html container + var container = this._container = createContainer(config); + + var svg = this._svg = create$1('svg'); + attr(svg, { width: '100%', height: '100%' }); + + append(container, svg); + + var viewport = this._viewport = createGroup(svg, 'viewport'); + + // debounce canvas.viewbox.changed events + // for smoother diagram interaction + if (config.deferUpdate !== false) { + this._viewboxChanged = debounce(bind(this._viewboxChanged, this), 300); + } + + eventBus.on('diagram.init', function() { + + /** + * An event indicating that the canvas is ready to be drawn on. + * + * @memberOf Canvas + * + * @event canvas.init + * + * @type {Object} + * @property {SVGElement} svg the created svg element + * @property {SVGElement} viewport the direct parent of diagram elements and shapes + */ + eventBus.fire('canvas.init', { + svg: svg, + viewport: viewport + }); + + }, this); + + // reset viewbox on shape changes to + // recompute the viewbox + eventBus.on([ + 'shape.added', + 'connection.added', + 'shape.removed', + 'connection.removed', + 'elements.changed', + 'root.set' + ], function() { + delete this._cachedViewbox; + }, this); + + eventBus.on('diagram.destroy', 500, this._destroy, this); + eventBus.on('diagram.clear', 500, this._clear, this); + }; + + Canvas.prototype._destroy = function(emit) { + this._eventBus.fire('canvas.destroy', { + svg: this._svg, + viewport: this._viewport + }); + + var parent = this._container.parentNode; + + if (parent) { + parent.removeChild(this._container); + } + + delete this._svg; + delete this._container; + delete this._layers; + delete this._planes; + delete this._rootElement; + delete this._viewport; + }; + + Canvas.prototype._clear = function() { + + var self = this; + + var allElements = this._elementRegistry.getAll(); + + // remove all elements + allElements.forEach(function(element) { + var type = getType(element); + + if (type === 'root') { + self.removeRootElement(element); + } else { + self._removeElement(element, type); + } + }); + + // remove all planes + this._planes = []; + this._rootElement = null; + + // force recomputation of view box + delete this._cachedViewbox; + }; + + /** + * Returns the default layer on which + * all elements are drawn. + * + * @returns {SVGElement} + */ + Canvas.prototype.getDefaultLayer = function() { + return this.getLayer(BASE_LAYER, PLANE_LAYER_INDEX); + }; + + /** + * Returns a layer that is used to draw elements + * or annotations on it. + * + * Non-existing layers retrieved through this method + * will be created. During creation, the optional index + * may be used to create layers below or above existing layers. + * A layer with a certain index is always created above all + * existing layers with the same index. + * + * @param {string} name + * @param {number} index + * + * @returns {SVGElement} + */ + Canvas.prototype.getLayer = function(name, index) { + + if (!name) { + throw new Error('must specify a name'); + } + + var layer = this._layers[name]; + + if (!layer) { + layer = this._layers[name] = this._createLayer(name, index); + } + + // throw an error if layer creation / retrival is + // requested on different index + if (typeof index !== 'undefined' && layer.index !== index) { + throw new Error('layer <' + name + '> already created at index <' + index + '>'); + } + + return layer.group; + }; + + /** + * For a given index, return the number of layers that have a higher index and + * are visible. + * + * This is used to determine the node a layer should be inserted at. + * + * @param {Number} index + * @returns {Number} + */ + Canvas.prototype._getChildIndex = function(index) { + return reduce(this._layers, function(childIndex, layer) { + if (layer.visible && index >= layer.index) { + childIndex++; + } + + return childIndex; + }, 0); + }; + + /** + * Creates a given layer and returns it. + * + * @param {string} name + * @param {number} [index=0] + * + * @return {Object} layer descriptor with { index, group: SVGGroup } + */ + Canvas.prototype._createLayer = function(name, index) { + + if (typeof index === 'undefined') { + index = UTILITY_LAYER_INDEX; + } + + var childIndex = this._getChildIndex(index); + + return { + group: createGroup(this._viewport, 'layer-' + name, childIndex), + index: index, + visible: true + }; + }; + + + /** + * Shows a given layer. + * + * @param {String} layer + * @returns {SVGElement} + */ + Canvas.prototype.showLayer = function(name) { + + if (!name) { + throw new Error('must specify a name'); + } + + var layer = this._layers[name]; + + if (!layer) { + throw new Error('layer <' + name + '> does not exist'); + } + + var viewport = this._viewport; + var group = layer.group; + var index = layer.index; + + if (layer.visible) { + return group; + } + + var childIndex = this._getChildIndex(index); + + viewport.insertBefore(group, viewport.childNodes[childIndex] || null); + + layer.visible = true; + + return group; + }; + + /** + * Hides a given layer. + * + * @param {String} layer + * @returns {SVGElement} + */ + Canvas.prototype.hideLayer = function(name) { + + if (!name) { + throw new Error('must specify a name'); + } + + var layer = this._layers[name]; + + if (!layer) { + throw new Error('layer <' + name + '> does not exist'); + } + + var group = layer.group; + + if (!layer.visible) { + return group; + } + + remove$1(group); + + layer.visible = false; + + return group; + }; + + + Canvas.prototype._removeLayer = function(name) { + + var layer = this._layers[name]; + + if (layer) { + delete this._layers[name]; + + remove$1(layer.group); + } + }; + + /** + * Returns the currently active layer. Can be null. + * + * @returns {SVGElement|null} + */ + Canvas.prototype.getActiveLayer = function() { + var plane = this._findPlaneForRoot(this.getRootElement()); + + if (!plane) { + return null; + } + + return plane.layer; + }; + + + /** + * Returns the plane which contains the given element. + * + * @param {string|djs.model.Base} element + * + * @return {djs.model.Base} root for element + */ + Canvas.prototype.findRoot = function(element) { + if (typeof element === 'string') { + element = this._elementRegistry.get(element); + } + + if (!element) { + return; + } + + var plane = this._findPlaneForRoot( + findRoot(element) + ) || {}; + + return plane.rootElement; + }; + + /** + * Return a list of all root elements on the diagram. + * + * @return {djs.model.Root[]} + */ + Canvas.prototype.getRootElements = function() { + return this._planes.map(function(plane) { + return plane.rootElement; + }); + }; + + Canvas.prototype._findPlaneForRoot = function(rootElement) { + return find(this._planes, function(plane) { + return plane.rootElement === rootElement; + }); + }; + + + /** + * Returns the html element that encloses the + * drawing canvas. + * + * @return {DOMNode} + */ + Canvas.prototype.getContainer = function() { + return this._container; + }; + + + // markers ////////////////////// + + Canvas.prototype._updateMarker = function(element, marker, add) { + var container; + + if (!element.id) { + element = this._elementRegistry.get(element); + } + + // we need to access all + container = this._elementRegistry._elements[element.id]; + + if (!container) { + return; + } + + forEach$1([ container.gfx, container.secondaryGfx ], function(gfx) { + if (gfx) { + + // invoke either addClass or removeClass based on mode + if (add) { + classes(gfx).add(marker); + } else { + classes(gfx).remove(marker); + } + } + }); + + /** + * An event indicating that a marker has been updated for an element + * + * @event element.marker.update + * @type {Object} + * @property {djs.model.Element} element the shape + * @property {Object} gfx the graphical representation of the shape + * @property {string} marker + * @property {boolean} add true if the marker was added, false if it got removed + */ + this._eventBus.fire('element.marker.update', { element: element, gfx: container.gfx, marker: marker, add: !!add }); + }; + + + /** + * Adds a marker to an element (basically a css class). + * + * Fires the element.marker.update event, making it possible to + * integrate extension into the marker life-cycle, too. + * + * @example + * canvas.addMarker('foo', 'some-marker'); + * + * var fooGfx = canvas.getGraphics('foo'); + * + * fooGfx; // ... + * + * @param {string|djs.model.Base} element + * @param {string} marker + */ + Canvas.prototype.addMarker = function(element, marker) { + this._updateMarker(element, marker, true); + }; + + + /** + * Remove a marker from an element. + * + * Fires the element.marker.update event, making it possible to + * integrate extension into the marker life-cycle, too. + * + * @param {string|djs.model.Base} element + * @param {string} marker + */ + Canvas.prototype.removeMarker = function(element, marker) { + this._updateMarker(element, marker, false); + }; + + /** + * Check the existence of a marker on element. + * + * @param {string|djs.model.Base} element + * @param {string} marker + */ + Canvas.prototype.hasMarker = function(element, marker) { + if (!element.id) { + element = this._elementRegistry.get(element); + } + + var gfx = this.getGraphics(element); + + return classes(gfx).has(marker); + }; + + /** + * Toggles a marker on an element. + * + * Fires the element.marker.update event, making it possible to + * integrate extension into the marker life-cycle, too. + * + * @param {string|djs.model.Base} element + * @param {string} marker + */ + Canvas.prototype.toggleMarker = function(element, marker) { + if (this.hasMarker(element, marker)) { + this.removeMarker(element, marker); + } else { + this.addMarker(element, marker); + } + }; + + /** + * Returns the current root element. + * + * Supports two different modes for handling root elements: + * + * 1. if no root element has been added before, an implicit root will be added + * and returned. This is used in applications that don't require explicit + * root elements. + * + * 2. when root elements have been added before calling `getRootElement`, + * root elements can be null. This is used for applications that want to manage + * root elements themselves. + * + * @returns {Object|djs.model.Root|null} rootElement. + */ + Canvas.prototype.getRootElement = function() { + var rootElement = this._rootElement; + + // can return null if root elements are present but none was set yet + if (rootElement || this._planes.length) { + return rootElement; + } + + return this.setRootElement(this.addRootElement(null)); + }; + + /** + * Adds a given root element and returns it. + * + * @param {Object|djs.model.Root} rootElement + * + * @return {Object|djs.model.Root} rootElement + */ + + Canvas.prototype.addRootElement = function(rootElement) { + var idx = this._rootsIdx++; + + if (!rootElement) { + rootElement = { + id: '__implicitroot_' + idx, + children: [], + isImplicit: true + }; + } + + var layerName = rootElement.layer = 'root-' + idx; + + this._ensureValid('root', rootElement); + + var layer = this.getLayer(layerName, PLANE_LAYER_INDEX); + + this.hideLayer(layerName); + + this._addRoot(rootElement, layer); + + this._planes.push({ + rootElement: rootElement, + layer: layer + }); + + return rootElement; + }; + + /** + * Removes a given rootElement and returns it. + * + * @param {djs.model.Root|String} rootElement + * + * @return {Object|djs.model.Root} rootElement + */ + Canvas.prototype.removeRootElement = function(rootElement) { + + if (typeof rootElement === 'string') { + rootElement = this._elementRegistry.get(rootElement); + } + + var plane = this._findPlaneForRoot(rootElement); + + if (!plane) { + return; + } + + // hook up life-cycle events + this._removeRoot(rootElement); + + // clean up layer + this._removeLayer(rootElement.layer); + + // clean up plane + this._planes = this._planes.filter(function(plane) { + return plane.rootElement !== rootElement; + }); + + // clean up active root + if (this._rootElement === rootElement) { + this._rootElement = null; + } + + return rootElement; + }; + + + // root element handling ////////////////////// + + /** + * Sets a given element as the new root element for the canvas + * and returns the new root element. + * + * @param {Object|djs.model.Root} rootElement + * + * @return {Object|djs.model.Root} new root element + */ + Canvas.prototype.setRootElement = function(rootElement, override) { + + if (isDefined(override)) { + throw new Error('override not supported'); + } + + if (rootElement === this._rootElement) { + return; + } + + var plane; + + if (!rootElement) { + throw new Error('rootElement required'); + } + + plane = this._findPlaneForRoot(rootElement); + + // give set add semantics for backwards compatibility + if (!plane) { + rootElement = this.addRootElement(rootElement); + } + + this._setRoot(rootElement); + + return rootElement; + }; + + + Canvas.prototype._removeRoot = function(element) { + var elementRegistry = this._elementRegistry, + eventBus = this._eventBus; + + // simulate element remove event sequence + eventBus.fire('root.remove', { element: element }); + eventBus.fire('root.removed', { element: element }); + + elementRegistry.remove(element); + }; + + + Canvas.prototype._addRoot = function(element, gfx) { + var elementRegistry = this._elementRegistry, + eventBus = this._eventBus; + + // resemble element add event sequence + eventBus.fire('root.add', { element: element }); + + elementRegistry.add(element, gfx); + + eventBus.fire('root.added', { element: element, gfx: gfx }); + }; + + + Canvas.prototype._setRoot = function(rootElement, layer) { + + var currentRoot = this._rootElement; + + if (currentRoot) { + + // un-associate previous root element + this._elementRegistry.updateGraphics(currentRoot, null, true); + + // hide previous layer + this.hideLayer(currentRoot.layer); + } + + if (rootElement) { + + if (!layer) { + layer = this._findPlaneForRoot(rootElement).layer; + } + + // associate element with + this._elementRegistry.updateGraphics(rootElement, this._svg, true); + + // show root layer + this.showLayer(rootElement.layer); + } + + this._rootElement = rootElement; + + this._eventBus.fire('root.set', { element: rootElement }); + }; + + // add functionality ////////////////////// + + Canvas.prototype._ensureValid = function(type, element) { + if (!element.id) { + throw new Error('element must have an id'); + } + + if (this._elementRegistry.get(element.id)) { + throw new Error('element <' + element.id + '> already exists'); + } + + var requiredAttrs = REQUIRED_MODEL_ATTRS[type]; + + var valid = every(requiredAttrs, function(attr) { + return typeof element[attr] !== 'undefined'; + }); + + if (!valid) { + throw new Error( + 'must supply { ' + requiredAttrs.join(', ') + ' } with ' + type); + } + }; + + Canvas.prototype._setParent = function(element, parent, parentIndex) { + add(parent.children, element, parentIndex); + element.parent = parent; + }; + + /** + * Adds an element to the canvas. + * + * This wires the parent <-> child relationship between the element and + * a explicitly specified parent or an implicit root element. + * + * During add it emits the events + * + * * <{type}.add> (element, parent) + * * <{type}.added> (element, gfx) + * + * Extensions may hook into these events to perform their magic. + * + * @param {string} type + * @param {Object|djs.model.Base} element + * @param {Object|djs.model.Base} [parent] + * @param {number} [parentIndex] + * + * @return {Object|djs.model.Base} the added element + */ + Canvas.prototype._addElement = function(type, element, parent, parentIndex) { + + parent = parent || this.getRootElement(); + + var eventBus = this._eventBus, + graphicsFactory = this._graphicsFactory; + + this._ensureValid(type, element); + + eventBus.fire(type + '.add', { element: element, parent: parent }); + + this._setParent(element, parent, parentIndex); + + // create graphics + var gfx = graphicsFactory.create(type, element, parentIndex); + + this._elementRegistry.add(element, gfx); + + // update its visual + graphicsFactory.update(type, element, gfx); + + eventBus.fire(type + '.added', { element: element, gfx: gfx }); + + return element; + }; + + /** + * Adds a shape to the canvas + * + * @param {Object|djs.model.Shape} shape to add to the diagram + * @param {djs.model.Base} [parent] + * @param {number} [parentIndex] + * + * @return {djs.model.Shape} the added shape + */ + Canvas.prototype.addShape = function(shape, parent, parentIndex) { + return this._addElement('shape', shape, parent, parentIndex); + }; + + /** + * Adds a connection to the canvas + * + * @param {Object|djs.model.Connection} connection to add to the diagram + * @param {djs.model.Base} [parent] + * @param {number} [parentIndex] + * + * @return {djs.model.Connection} the added connection + */ + Canvas.prototype.addConnection = function(connection, parent, parentIndex) { + return this._addElement('connection', connection, parent, parentIndex); + }; + + + /** + * Internal remove element + */ + Canvas.prototype._removeElement = function(element, type) { + + var elementRegistry = this._elementRegistry, + graphicsFactory = this._graphicsFactory, + eventBus = this._eventBus; + + element = elementRegistry.get(element.id || element); + + if (!element) { + + // element was removed already + return; + } + + eventBus.fire(type + '.remove', { element: element }); + + graphicsFactory.remove(element); + + // unset parent <-> child relationship + remove(element.parent && element.parent.children, element); + element.parent = null; + + eventBus.fire(type + '.removed', { element: element }); + + elementRegistry.remove(element); + + return element; + }; + + + /** + * Removes a shape from the canvas + * + * @param {string|djs.model.Shape} shape or shape id to be removed + * + * @return {djs.model.Shape} the removed shape + */ + Canvas.prototype.removeShape = function(shape) { + + /** + * An event indicating that a shape is about to be removed from the canvas. + * + * @memberOf Canvas + * + * @event shape.remove + * @type {Object} + * @property {djs.model.Shape} element the shape descriptor + * @property {Object} gfx the graphical representation of the shape + */ + + /** + * An event indicating that a shape has been removed from the canvas. + * + * @memberOf Canvas + * + * @event shape.removed + * @type {Object} + * @property {djs.model.Shape} element the shape descriptor + * @property {Object} gfx the graphical representation of the shape + */ + return this._removeElement(shape, 'shape'); + }; + + + /** + * Removes a connection from the canvas + * + * @param {string|djs.model.Connection} connection or connection id to be removed + * + * @return {djs.model.Connection} the removed connection + */ + Canvas.prototype.removeConnection = function(connection) { + + /** + * An event indicating that a connection is about to be removed from the canvas. + * + * @memberOf Canvas + * + * @event connection.remove + * @type {Object} + * @property {djs.model.Connection} element the connection descriptor + * @property {Object} gfx the graphical representation of the connection + */ + + /** + * An event indicating that a connection has been removed from the canvas. + * + * @memberOf Canvas + * + * @event connection.removed + * @type {Object} + * @property {djs.model.Connection} element the connection descriptor + * @property {Object} gfx the graphical representation of the connection + */ + return this._removeElement(connection, 'connection'); + }; + + + /** + * Return the graphical object underlaying a certain diagram element + * + * @param {string|djs.model.Base} element descriptor of the element + * @param {boolean} [secondary=false] whether to return the secondary connected element + * + * @return {SVGElement} + */ + Canvas.prototype.getGraphics = function(element, secondary) { + return this._elementRegistry.getGraphics(element, secondary); + }; + + + /** + * Perform a viewbox update via a given change function. + * + * @param {Function} changeFn + */ + Canvas.prototype._changeViewbox = function(changeFn) { + + // notify others of the upcoming viewbox change + this._eventBus.fire('canvas.viewbox.changing'); + + // perform actual change + changeFn.apply(this); + + // reset the cached viewbox so that + // a new get operation on viewbox or zoom + // triggers a viewbox re-computation + this._cachedViewbox = null; + + // notify others of the change; this step + // may or may not be debounced + this._viewboxChanged(); + }; + + Canvas.prototype._viewboxChanged = function() { + this._eventBus.fire('canvas.viewbox.changed', { viewbox: this.viewbox() }); + }; + + + /** + * Gets or sets the view box of the canvas, i.e. the + * area that is currently displayed. + * + * The getter may return a cached viewbox (if it is currently + * changing). To force a recomputation, pass `false` as the first argument. + * + * @example + * + * canvas.viewbox({ x: 100, y: 100, width: 500, height: 500 }) + * + * // sets the visible area of the diagram to (100|100) -> (600|100) + * // and and scales it according to the diagram width + * + * var viewbox = canvas.viewbox(); // pass `false` to force recomputing the box. + * + * console.log(viewbox); + * // { + * // inner: Dimensions, + * // outer: Dimensions, + * // scale, + * // x, y, + * // width, height + * // } + * + * // if the current diagram is zoomed and scrolled, you may reset it to the + * // default zoom via this method, too: + * + * var zoomedAndScrolledViewbox = canvas.viewbox(); + * + * canvas.viewbox({ + * x: 0, + * y: 0, + * width: zoomedAndScrolledViewbox.outer.width, + * height: zoomedAndScrolledViewbox.outer.height + * }); + * + * @param {Object} [box] the new view box to set + * @param {number} box.x the top left X coordinate of the canvas visible in view box + * @param {number} box.y the top left Y coordinate of the canvas visible in view box + * @param {number} box.width the visible width + * @param {number} box.height + * + * @return {Object} the current view box + */ + Canvas.prototype.viewbox = function(box) { + + if (box === undefined && this._cachedViewbox) { + return this._cachedViewbox; + } + + var viewport = this._viewport, + innerBox, + outerBox = this.getSize(), + matrix, + activeLayer, + transform, + scale, + x, y; + + if (!box) { + + // compute the inner box based on the + // diagrams active layer. This allows us to exclude + // external components, such as overlays + + activeLayer = this._rootElement ? this.getActiveLayer() : null; + innerBox = activeLayer && activeLayer.getBBox() || {}; + + transform = transform$1(viewport); + matrix = transform ? transform.matrix : createMatrix(); + scale = round$b(matrix.a, 1000); + + x = round$b(-matrix.e || 0, 1000); + y = round$b(-matrix.f || 0, 1000); + + box = this._cachedViewbox = { + x: x ? x / scale : 0, + y: y ? y / scale : 0, + width: outerBox.width / scale, + height: outerBox.height / scale, + scale: scale, + inner: { + width: innerBox.width || 0, + height: innerBox.height || 0, + x: innerBox.x || 0, + y: innerBox.y || 0 + }, + outer: outerBox + }; + + return box; + } else { + + this._changeViewbox(function() { + scale = Math.min(outerBox.width / box.width, outerBox.height / box.height); + + var matrix = this._svg.createSVGMatrix() + .scale(scale) + .translate(-box.x, -box.y); + + transform$1(viewport, matrix); + }); + } + + return box; + }; + + + /** + * Gets or sets the scroll of the canvas. + * + * @param {Object} [delta] the new scroll to apply. + * + * @param {number} [delta.dx] + * @param {number} [delta.dy] + */ + Canvas.prototype.scroll = function(delta) { + + var node = this._viewport; + var matrix = node.getCTM(); + + if (delta) { + this._changeViewbox(function() { + delta = assign({ dx: 0, dy: 0 }, delta || {}); + + matrix = this._svg.createSVGMatrix().translate(delta.dx, delta.dy).multiply(matrix); + + setCTM(node, matrix); + }); + } + + return { x: matrix.e, y: matrix.f }; + }; + + /** + * Scrolls the viewbox to contain the given element. + * Optionally specify a padding to be applied to the edges. + * + * @param {Object|String} [element] the element to scroll to. + * @param {Object|Number} [padding=100] the padding to be applied. Can also specify top, bottom, left and right. + * + */ + Canvas.prototype.scrollToElement = function(element, padding) { + var defaultPadding = 100; + + if (typeof element === 'string') { + element = this._elementRegistry.get(element); + } + + // set to correct rootElement + var rootElement = this.findRoot(element); + + if (rootElement !== this.getRootElement()) { + this.setRootElement(rootElement); + } + + if (!padding) { + padding = {}; + } + if (typeof padding === 'number') { + defaultPadding = padding; + } + + padding = { + top: padding.top || defaultPadding, + right: padding.right || defaultPadding, + bottom: padding.bottom || defaultPadding, + left: padding.left || defaultPadding + }; + + var elementBounds = getBBox(element), + elementTrbl = asTRBL(elementBounds), + viewboxBounds = this.viewbox(), + zoom = this.zoom(), + dx, dy; + + // shrink viewboxBounds with padding + viewboxBounds.y += padding.top / zoom; + viewboxBounds.x += padding.left / zoom; + viewboxBounds.width -= (padding.right + padding.left) / zoom; + viewboxBounds.height -= (padding.bottom + padding.top) / zoom; + + var viewboxTrbl = asTRBL(viewboxBounds); + + var canFit = elementBounds.width < viewboxBounds.width && elementBounds.height < viewboxBounds.height; + + if (!canFit) { + + // top-left when element can't fit + dx = elementBounds.x - viewboxBounds.x; + dy = elementBounds.y - viewboxBounds.y; + + } else { + + var dRight = Math.max(0, elementTrbl.right - viewboxTrbl.right), + dLeft = Math.min(0, elementTrbl.left - viewboxTrbl.left), + dBottom = Math.max(0, elementTrbl.bottom - viewboxTrbl.bottom), + dTop = Math.min(0, elementTrbl.top - viewboxTrbl.top); + + dx = dRight || dLeft; + dy = dBottom || dTop; + + } + + this.scroll({ dx: -dx * zoom, dy: -dy * zoom }); + }; + + /** + * Gets or sets the current zoom of the canvas, optionally zooming + * to the specified position. + * + * The getter may return a cached zoom level. Call it with `false` as + * the first argument to force recomputation of the current level. + * + * @param {string|number} [newScale] the new zoom level, either a number, i.e. 0.9, + * or `fit-viewport` to adjust the size to fit the current viewport + * @param {string|Point} [center] the reference point { x: .., y: ..} to zoom to, 'auto' to zoom into mid or null + * + * @return {number} the current scale + */ + Canvas.prototype.zoom = function(newScale, center) { + + if (!newScale) { + return this.viewbox(newScale).scale; + } + + if (newScale === 'fit-viewport') { + return this._fitViewport(center); + } + + var outer, + matrix; + + this._changeViewbox(function() { + + if (typeof center !== 'object') { + outer = this.viewbox().outer; + + center = { + x: outer.width / 2, + y: outer.height / 2 + }; + } + + matrix = this._setZoom(newScale, center); + }); + + return round$b(matrix.a, 1000); + }; + + function setCTM(node, m) { + var mstr = 'matrix(' + m.a + ',' + m.b + ',' + m.c + ',' + m.d + ',' + m.e + ',' + m.f + ')'; + node.setAttribute('transform', mstr); + } + + Canvas.prototype._fitViewport = function(center) { + + var vbox = this.viewbox(), + outer = vbox.outer, + inner = vbox.inner, + newScale, + newViewbox; + + // display the complete diagram without zooming in. + // instead of relying on internal zoom, we perform a + // hard reset on the canvas viewbox to realize this + // + // if diagram does not need to be zoomed in, we focus it around + // the diagram origin instead + + if (inner.x >= 0 && + inner.y >= 0 && + inner.x + inner.width <= outer.width && + inner.y + inner.height <= outer.height && + !center) { + + newViewbox = { + x: 0, + y: 0, + width: Math.max(inner.width + inner.x, outer.width), + height: Math.max(inner.height + inner.y, outer.height) + }; + } else { + + newScale = Math.min(1, outer.width / inner.width, outer.height / inner.height); + newViewbox = { + x: inner.x + (center ? inner.width / 2 - outer.width / newScale / 2 : 0), + y: inner.y + (center ? inner.height / 2 - outer.height / newScale / 2 : 0), + width: outer.width / newScale, + height: outer.height / newScale + }; + } + + this.viewbox(newViewbox); + + return this.viewbox(false).scale; + }; + + + Canvas.prototype._setZoom = function(scale, center) { + + var svg = this._svg, + viewport = this._viewport; + + var matrix = svg.createSVGMatrix(); + var point = svg.createSVGPoint(); + + var centerPoint, + originalPoint, + currentMatrix, + scaleMatrix, + newMatrix; + + currentMatrix = viewport.getCTM(); + + var currentScale = currentMatrix.a; + + if (center) { + centerPoint = assign(point, center); + + // revert applied viewport transformations + originalPoint = centerPoint.matrixTransform(currentMatrix.inverse()); + + // create scale matrix + scaleMatrix = matrix + .translate(originalPoint.x, originalPoint.y) + .scale(1 / currentScale * scale) + .translate(-originalPoint.x, -originalPoint.y); + + newMatrix = currentMatrix.multiply(scaleMatrix); + } else { + newMatrix = matrix.scale(scale); + } + + setCTM(this._viewport, newMatrix); + + return newMatrix; + }; + + + /** + * Returns the size of the canvas + * + * @return {Dimensions} + */ + Canvas.prototype.getSize = function() { + return { + width: this._container.clientWidth, + height: this._container.clientHeight + }; + }; + + + /** + * Return the absolute bounding box for the given element + * + * The absolute bounding box may be used to display overlays in the + * callers (browser) coordinate system rather than the zoomed in/out + * canvas coordinates. + * + * @param {ElementDescriptor} element + * @return {Bounds} the absolute bounding box + */ + Canvas.prototype.getAbsoluteBBox = function(element) { + var vbox = this.viewbox(); + var bbox; + + // connection + // use svg bbox + if (element.waypoints) { + var gfx = this.getGraphics(element); + + bbox = gfx.getBBox(); + } + + // shapes + // use data + else { + bbox = element; + } + + var x = bbox.x * vbox.scale - vbox.x * vbox.scale; + var y = bbox.y * vbox.scale - vbox.y * vbox.scale; + + var width = bbox.width * vbox.scale; + var height = bbox.height * vbox.scale; + + return { + x: x, + y: y, + width: width, + height: height + }; + }; + + /** + * Fires an event in order other modules can react to the + * canvas resizing + */ + Canvas.prototype.resized = function() { + + // force recomputation of view box + delete this._cachedViewbox; + + this._eventBus.fire('canvas.resized'); + }; + + var ELEMENT_ID = 'data-element-id'; + + + /** + * @class + * + * A registry that keeps track of all shapes in the diagram. + */ + function ElementRegistry(eventBus) { + this._elements = {}; + + this._eventBus = eventBus; + } + + ElementRegistry.$inject = [ 'eventBus' ]; + + /** + * Register a pair of (element, gfx, (secondaryGfx)). + * + * @param {djs.model.Base} element + * @param {SVGElement} gfx + * @param {SVGElement} [secondaryGfx] optional other element to register, too + */ + ElementRegistry.prototype.add = function(element, gfx, secondaryGfx) { + + var id = element.id; + + this._validateId(id); + + // associate dom node with element + attr(gfx, ELEMENT_ID, id); + + if (secondaryGfx) { + attr(secondaryGfx, ELEMENT_ID, id); + } + + this._elements[id] = { element: element, gfx: gfx, secondaryGfx: secondaryGfx }; + }; + + /** + * Removes an element from the registry. + * + * @param {djs.model.Base} element + */ + ElementRegistry.prototype.remove = function(element) { + var elements = this._elements, + id = element.id || element, + container = id && elements[id]; + + if (container) { + + // unset element id on gfx + attr(container.gfx, ELEMENT_ID, ''); + + if (container.secondaryGfx) { + attr(container.secondaryGfx, ELEMENT_ID, ''); + } + + delete elements[id]; + } + }; + + /** + * Update the id of an element + * + * @param {djs.model.Base} element + * @param {string} newId + */ + ElementRegistry.prototype.updateId = function(element, newId) { + + this._validateId(newId); + + if (typeof element === 'string') { + element = this.get(element); + } + + this._eventBus.fire('element.updateId', { + element: element, + newId: newId + }); + + var gfx = this.getGraphics(element), + secondaryGfx = this.getGraphics(element, true); + + this.remove(element); + + element.id = newId; + + this.add(element, gfx, secondaryGfx); + }; + + /** + * Update the graphics of an element + * + * @param {djs.model.Base} element + * @param {SVGElement} gfx + * @param {boolean} [secondary=false] whether to update the secondary connected element + */ + ElementRegistry.prototype.updateGraphics = function(filter, gfx, secondary) { + var id = filter.id || filter; + + var container = this._elements[id]; + + if (secondary) { + container.secondaryGfx = gfx; + } else { + container.gfx = gfx; + } + + if (gfx) { + attr(gfx, ELEMENT_ID, id); + } + + return gfx; + }; + + /** + * Return the model element for a given id or graphics. + * + * @example + * + * elementRegistry.get('SomeElementId_1'); + * elementRegistry.get(gfx); + * + * + * @param {string|SVGElement} filter for selecting the element + * + * @return {djs.model.Base} + */ + ElementRegistry.prototype.get = function(filter) { + var id; + + if (typeof filter === 'string') { + id = filter; + } else { + id = filter && attr(filter, ELEMENT_ID); + } + + var container = this._elements[id]; + return container && container.element; + }; + + /** + * Return all elements that match a given filter function. + * + * @param {Function} fn + * + * @return {Array} + */ + ElementRegistry.prototype.filter = function(fn) { + + var filtered = []; + + this.forEach(function(element, gfx) { + if (fn(element, gfx)) { + filtered.push(element); + } + }); + + return filtered; + }; + + /** + * Return the first element that satisfies the provided testing function. + * + * @param {Function} fn + * + * @return {djs.model.Base} + */ + ElementRegistry.prototype.find = function(fn) { + var map = this._elements, + keys = Object.keys(map); + + for (var i = 0; i < keys.length; i++) { + var id = keys[i], + container = map[id], + element = container.element, + gfx = container.gfx; + + if (fn(element, gfx)) { + return element; + } + } + }; + + /** + * Return all rendered model elements. + * + * @return {Array} + */ + ElementRegistry.prototype.getAll = function() { + return this.filter(function(e) { return e; }); + }; + + /** + * Iterate over all diagram elements. + * + * @param {Function} fn + */ + ElementRegistry.prototype.forEach = function(fn) { + + var map = this._elements; + + Object.keys(map).forEach(function(id) { + var container = map[id], + element = container.element, + gfx = container.gfx; + + return fn(element, gfx); + }); + }; + + /** + * Return the graphical representation of an element or its id. + * + * @example + * elementRegistry.getGraphics('SomeElementId_1'); + * elementRegistry.getGraphics(rootElement); // + * + * elementRegistry.getGraphics(rootElement, true); // + * + * + * @param {string|djs.model.Base} filter + * @param {boolean} [secondary=false] whether to return the secondary connected element + * + * @return {SVGElement} + */ + ElementRegistry.prototype.getGraphics = function(filter, secondary) { + var id = filter.id || filter; + + var container = this._elements[id]; + return container && (secondary ? container.secondaryGfx : container.gfx); + }; + + /** + * Validate the suitability of the given id and signals a problem + * with an exception. + * + * @param {string} id + * + * @throws {Error} if id is empty or already assigned + */ + ElementRegistry.prototype._validateId = function(id) { + if (!id) { + throw new Error('element must have an id'); + } + + if (this._elements[id]) { + throw new Error('element with id ' + id + ' already added'); + } + }; + + var objectRefs = {exports: {}}; + + var collection = {}; + + /** + * An empty collection stub. Use {@link RefsCollection.extend} to extend a + * collection with ref semantics. + * + * @class RefsCollection + */ + + /** + * Extends a collection with {@link Refs} aware methods + * + * @memberof RefsCollection + * @static + * + * @param {Array} collection + * @param {Refs} refs instance + * @param {Object} property represented by the collection + * @param {Object} target object the collection is attached to + * + * @return {RefsCollection} the extended array + */ + function extend(collection, refs, property, target) { + + var inverseProperty = property.inverse; + + /** + * Removes the given element from the array and returns it. + * + * @method RefsCollection#remove + * + * @param {Object} element the element to remove + */ + Object.defineProperty(collection, 'remove', { + value: function(element) { + var idx = this.indexOf(element); + if (idx !== -1) { + this.splice(idx, 1); + + // unset inverse + refs.unset(element, inverseProperty, target); + } + + return element; + } + }); + + /** + * Returns true if the collection contains the given element + * + * @method RefsCollection#contains + * + * @param {Object} element the element to check for + */ + Object.defineProperty(collection, 'contains', { + value: function(element) { + return this.indexOf(element) !== -1; + } + }); + + /** + * Adds an element to the array, unless it exists already (set semantics). + * + * @method RefsCollection#add + * + * @param {Object} element the element to add + * @param {Number} optional index to add element to + * (possibly moving other elements around) + */ + Object.defineProperty(collection, 'add', { + value: function(element, idx) { + + var currentIdx = this.indexOf(element); + + if (typeof idx === 'undefined') { + + if (currentIdx !== -1) { + // element already in collection (!) + return; + } + + // add to end of array, as no idx is specified + idx = this.length; + } + + // handle already in collection + if (currentIdx !== -1) { + + // remove element from currentIdx + this.splice(currentIdx, 1); + } + + // add element at idx + this.splice(idx, 0, element); + + if (currentIdx === -1) { + // set inverse, unless element was + // in collection already + refs.set(element, inverseProperty, target); + } + } + }); + + // a simple marker, identifying this element + // as being a refs collection + Object.defineProperty(collection, '__refs_collection', { + value: true + }); + + return collection; + } + + + function isExtended(collection) { + return collection.__refs_collection === true; + } + + collection.extend = extend; + + collection.isExtended = isExtended; + + var Collection = collection; + + function hasOwnProperty$1(e, property) { + return Object.prototype.hasOwnProperty.call(e, property.name || property); + } + + function defineCollectionProperty(ref, property, target) { + + var collection = Collection.extend(target[property.name] || [], ref, property, target); + + Object.defineProperty(target, property.name, { + enumerable: property.enumerable, + value: collection + }); + + if (collection.length) { + + collection.forEach(function(o) { + ref.set(o, property.inverse, target); + }); + } + } + + + function defineProperty$1(ref, property, target) { + + var inverseProperty = property.inverse; + + var _value = target[property.name]; + + Object.defineProperty(target, property.name, { + configurable: property.configurable, + enumerable: property.enumerable, + + get: function() { + return _value; + }, + + set: function(value) { + + // return if we already performed all changes + if (value === _value) { + return; + } + + var old = _value; + + // temporary set null + _value = null; + + if (old) { + ref.unset(old, inverseProperty, target); + } + + // set new value + _value = value; + + // set inverse value + ref.set(_value, inverseProperty, target); + } + }); + + } + + /** + * Creates a new references object defining two inversly related + * attribute descriptors a and b. + * + *

+ * When bound to an object using {@link Refs#bind} the references + * get activated and ensure that add and remove operations are applied + * reversely, too. + *

+ * + *

+ * For attributes represented as collections {@link Refs} provides the + * {@link RefsCollection#add}, {@link RefsCollection#remove} and {@link RefsCollection#contains} extensions + * that must be used to properly hook into the inverse change mechanism. + *

+ * + * @class Refs + * + * @classdesc A bi-directional reference between two attributes. + * + * @param {Refs.AttributeDescriptor} a property descriptor + * @param {Refs.AttributeDescriptor} b property descriptor + * + * @example + * + * var refs = Refs({ name: 'wheels', collection: true, enumerable: true }, { name: 'car' }); + * + * var car = { name: 'toyota' }; + * var wheels = [{ pos: 'front-left' }, { pos: 'front-right' }]; + * + * refs.bind(car, 'wheels'); + * + * car.wheels // [] + * car.wheels.add(wheels[0]); + * car.wheels.add(wheels[1]); + * + * car.wheels // [{ pos: 'front-left' }, { pos: 'front-right' }] + * + * wheels[0].car // { name: 'toyota' }; + * car.wheels.remove(wheels[0]); + * + * wheels[0].car // undefined + */ + function Refs$1(a, b) { + + if (!(this instanceof Refs$1)) { + return new Refs$1(a, b); + } + + // link + a.inverse = b; + b.inverse = a; + + this.props = {}; + this.props[a.name] = a; + this.props[b.name] = b; + } + + /** + * Binds one side of a bi-directional reference to a + * target object. + * + * @memberOf Refs + * + * @param {Object} target + * @param {String} property + */ + Refs$1.prototype.bind = function(target, property) { + if (typeof property === 'string') { + if (!this.props[property]) { + throw new Error('no property <' + property + '> in ref'); + } + property = this.props[property]; + } + + if (property.collection) { + defineCollectionProperty(this, property, target); + } else { + defineProperty$1(this, property, target); + } + }; + + Refs$1.prototype.ensureRefsCollection = function(target, property) { + + var collection = target[property.name]; + + if (!Collection.isExtended(collection)) { + defineCollectionProperty(this, property, target); + } + + return collection; + }; + + Refs$1.prototype.ensureBound = function(target, property) { + if (!hasOwnProperty$1(target, property)) { + this.bind(target, property); + } + }; + + Refs$1.prototype.unset = function(target, property, value) { + + if (target) { + this.ensureBound(target, property); + + if (property.collection) { + this.ensureRefsCollection(target, property).remove(value); + } else { + target[property.name] = undefined; + } + } + }; + + Refs$1.prototype.set = function(target, property, value) { + + if (target) { + this.ensureBound(target, property); + + if (property.collection) { + this.ensureRefsCollection(target, property).add(value); + } else { + target[property.name] = value; + } + } + }; + + var refs = Refs$1; + + (function (module) { + module.exports = refs; + + module.exports.Collection = collection; + } (objectRefs)); + + var Refs = /*@__PURE__*/getDefaultExportFromCjs(objectRefs.exports); + + var parentRefs = new Refs({ name: 'children', enumerable: true, collection: true }, { name: 'parent' }), + labelRefs = new Refs({ name: 'labels', enumerable: true, collection: true }, { name: 'labelTarget' }), + attacherRefs = new Refs({ name: 'attachers', collection: true }, { name: 'host' }), + outgoingRefs = new Refs({ name: 'outgoing', collection: true }, { name: 'source' }), + incomingRefs = new Refs({ name: 'incoming', collection: true }, { name: 'target' }); + + /** + * @namespace djs.model + */ + + /** + * @memberOf djs.model + */ + + /** + * The basic graphical representation + * + * @class + * + * @abstract + */ + function Base$1() { + + /** + * The object that backs up the shape + * + * @name Base#businessObject + * @type Object + */ + Object.defineProperty(this, 'businessObject', { + writable: true + }); + + + /** + * Single label support, will mapped to multi label array + * + * @name Base#label + * @type Object + */ + Object.defineProperty(this, 'label', { + get: function() { + return this.labels[0]; + }, + set: function(newLabel) { + + var label = this.label, + labels = this.labels; + + if (!newLabel && label) { + labels.remove(label); + } else { + labels.add(newLabel, 0); + } + } + }); + + /** + * The parent shape + * + * @name Base#parent + * @type Shape + */ + parentRefs.bind(this, 'parent'); + + /** + * The list of labels + * + * @name Base#labels + * @type Label + */ + labelRefs.bind(this, 'labels'); + + /** + * The list of outgoing connections + * + * @name Base#outgoing + * @type Array + */ + outgoingRefs.bind(this, 'outgoing'); + + /** + * The list of incoming connections + * + * @name Base#incoming + * @type Array + */ + incomingRefs.bind(this, 'incoming'); + } + + + /** + * A graphical object + * + * @class + * @constructor + * + * @extends Base + */ + function Shape() { + Base$1.call(this); + + /** + * Indicates frame shapes + * + * @name Shape#isFrame + * @type boolean + */ + + /** + * The list of children + * + * @name Shape#children + * @type Array + */ + parentRefs.bind(this, 'children'); + + /** + * @name Shape#host + * @type Shape + */ + attacherRefs.bind(this, 'host'); + + /** + * @name Shape#attachers + * @type Shape + */ + attacherRefs.bind(this, 'attachers'); + } + + e(Shape, Base$1); + + + /** + * A root graphical object + * + * @class + * @constructor + * + * @extends Shape + */ + function Root() { + Shape.call(this); + } + + e(Root, Shape); + + + /** + * A label for an element + * + * @class + * @constructor + * + * @extends Shape + */ + function Label() { + Shape.call(this); + + /** + * The labeled element + * + * @name Label#labelTarget + * @type Base + */ + labelRefs.bind(this, 'labelTarget'); + } + + e(Label, Shape); + + + /** + * A connection between two elements + * + * @class + * @constructor + * + * @extends Base + */ + function Connection() { + Base$1.call(this); + + /** + * The element this connection originates from + * + * @name Connection#source + * @type Base + */ + outgoingRefs.bind(this, 'source'); + + /** + * The element this connection points to + * + * @name Connection#target + * @type Base + */ + incomingRefs.bind(this, 'target'); + } + + e(Connection, Base$1); + + + var types$6 = { + connection: Connection, + shape: Shape, + label: Label, + root: Root + }; + + /** + * Creates a new model element of the specified type + * + * @method create + * + * @example + * + * var shape1 = Model.create('shape', { x: 10, y: 10, width: 100, height: 100 }); + * var shape2 = Model.create('shape', { x: 210, y: 210, width: 100, height: 100 }); + * + * var connection = Model.create('connection', { waypoints: [ { x: 110, y: 55 }, {x: 210, y: 55 } ] }); + * + * @param {string} type lower-cased model name + * @param {Object} attrs attributes to initialize the new model instance with + * + * @return {Base} the new model instance + */ + function create(type, attrs) { + var Type = types$6[type]; + if (!Type) { + throw new Error('unknown type: <' + type + '>'); + } + return assign(new Type(), attrs); + } + + /** + * A factory for diagram-js shapes + */ + function ElementFactory$1() { + this._uid = 12; + } + + + ElementFactory$1.prototype.createRoot = function(attrs) { + return this.create('root', attrs); + }; + + ElementFactory$1.prototype.createLabel = function(attrs) { + return this.create('label', attrs); + }; + + ElementFactory$1.prototype.createShape = function(attrs) { + return this.create('shape', attrs); + }; + + ElementFactory$1.prototype.createConnection = function(attrs) { + return this.create('connection', attrs); + }; + + /** + * Create a model element with the given type and + * a number of pre-set attributes. + * + * @param {string} type + * @param {Object} attrs + * @return {djs.model.Base} the newly created model instance + */ + ElementFactory$1.prototype.create = function(type, attrs) { + + attrs = assign({}, attrs || {}); + + if (!attrs.id) { + attrs.id = type + '_' + (this._uid++); + } + + return create(type, attrs); + }; + + var FN_REF = '__fn'; + + var DEFAULT_PRIORITY$5 = 1000; + + var slice = Array.prototype.slice; + + /** + * A general purpose event bus. + * + * This component is used to communicate across a diagram instance. + * Other parts of a diagram can use it to listen to and broadcast events. + * + * + * ## Registering for Events + * + * The event bus provides the {@link EventBus#on} and {@link EventBus#once} + * methods to register for events. {@link EventBus#off} can be used to + * remove event registrations. Listeners receive an instance of {@link Event} + * as the first argument. It allows them to hook into the event execution. + * + * ```javascript + * + * // listen for event + * eventBus.on('foo', function(event) { + * + * // access event type + * event.type; // 'foo' + * + * // stop propagation to other listeners + * event.stopPropagation(); + * + * // prevent event default + * event.preventDefault(); + * }); + * + * // listen for event with custom payload + * eventBus.on('bar', function(event, payload) { + * console.log(payload); + * }); + * + * // listen for event returning value + * eventBus.on('foobar', function(event) { + * + * // stop event propagation + prevent default + * return false; + * + * // stop event propagation + return custom result + * return { + * complex: 'listening result' + * }; + * }); + * + * + * // listen with custom priority (default=1000, higher is better) + * eventBus.on('priorityfoo', 1500, function(event) { + * console.log('invoked first!'); + * }); + * + * + * // listen for event and pass the context (`this`) + * eventBus.on('foobar', function(event) { + * this.foo(); + * }, this); + * ``` + * + * + * ## Emitting Events + * + * Events can be emitted via the event bus using {@link EventBus#fire}. + * + * ```javascript + * + * // false indicates that the default action + * // was prevented by listeners + * if (eventBus.fire('foo') === false) { + * console.log('default has been prevented!'); + * }; + * + * + * // custom args + return value listener + * eventBus.on('sum', function(event, a, b) { + * return a + b; + * }); + * + * // you can pass custom arguments + retrieve result values. + * var sum = eventBus.fire('sum', 1, 2); + * console.log(sum); // 3 + * ``` + */ + function EventBus() { + this._listeners = {}; + + // cleanup on destroy on lowest priority to allow + // message passing until the bitter end + this.on('diagram.destroy', 1, this._destroy, this); + } + + + /** + * Register an event listener for events with the given name. + * + * The callback will be invoked with `event, ...additionalArguments` + * that have been passed to {@link EventBus#fire}. + * + * Returning false from a listener will prevent the events default action + * (if any is specified). To stop an event from being processed further in + * other listeners execute {@link Event#stopPropagation}. + * + * Returning anything but `undefined` from a listener will stop the listener propagation. + * + * @param {string|Array} events + * @param {number} [priority=1000] the priority in which this listener is called, larger is higher + * @param {Function} callback + * @param {Object} [that] Pass context (`this`) to the callback + */ + EventBus.prototype.on = function(events, priority, callback, that) { + + events = isArray$3(events) ? events : [ events ]; + + if (isFunction(priority)) { + that = callback; + callback = priority; + priority = DEFAULT_PRIORITY$5; + } + + if (!isNumber(priority)) { + throw new Error('priority must be a number'); + } + + var actualCallback = callback; + + if (that) { + actualCallback = bind(callback, that); + + // make sure we remember and are able to remove + // bound callbacks via {@link #off} using the original + // callback + actualCallback[FN_REF] = callback[FN_REF] || callback; + } + + var self = this; + + events.forEach(function(e) { + self._addListener(e, { + priority: priority, + callback: actualCallback, + next: null + }); + }); + }; + + + /** + * Register an event listener that is executed only once. + * + * @param {string} event the event name to register for + * @param {number} [priority=1000] the priority in which this listener is called, larger is higher + * @param {Function} callback the callback to execute + * @param {Object} [that] Pass context (`this`) to the callback + */ + EventBus.prototype.once = function(event, priority, callback, that) { + var self = this; + + if (isFunction(priority)) { + that = callback; + callback = priority; + priority = DEFAULT_PRIORITY$5; + } + + if (!isNumber(priority)) { + throw new Error('priority must be a number'); + } + + function wrappedCallback() { + wrappedCallback.__isTomb = true; + + var result = callback.apply(that, arguments); + + self.off(event, wrappedCallback); + + return result; + } + + // make sure we remember and are able to remove + // bound callbacks via {@link #off} using the original + // callback + wrappedCallback[FN_REF] = callback; + + this.on(event, priority, wrappedCallback); + }; + + + /** + * Removes event listeners by event and callback. + * + * If no callback is given, all listeners for a given event name are being removed. + * + * @param {string|Array} events + * @param {Function} [callback] + */ + EventBus.prototype.off = function(events, callback) { + + events = isArray$3(events) ? events : [ events ]; + + var self = this; + + events.forEach(function(event) { + self._removeListener(event, callback); + }); + + }; + + + /** + * Create an EventBus event. + * + * @param {Object} data + * + * @return {Object} event, recognized by the eventBus + */ + EventBus.prototype.createEvent = function(data) { + var event = new InternalEvent(); + + event.init(data); + + return event; + }; + + + /** + * Fires a named event. + * + * @example + * + * // fire event by name + * events.fire('foo'); + * + * // fire event object with nested type + * var event = { type: 'foo' }; + * events.fire(event); + * + * // fire event with explicit type + * var event = { x: 10, y: 20 }; + * events.fire('element.moved', event); + * + * // pass additional arguments to the event + * events.on('foo', function(event, bar) { + * alert(bar); + * }); + * + * events.fire({ type: 'foo' }, 'I am bar!'); + * + * @param {string} [name] the optional event name + * @param {Object} [event] the event object + * @param {...Object} additional arguments to be passed to the callback functions + * + * @return {boolean} the events return value, if specified or false if the + * default action was prevented by listeners + */ + EventBus.prototype.fire = function(type, data) { + var event, + firstListener, + returnValue, + args; + + args = slice.call(arguments); + + if (typeof type === 'object') { + data = type; + type = data.type; + } + + if (!type) { + throw new Error('no event type specified'); + } + + firstListener = this._listeners[type]; + + if (!firstListener) { + return; + } + + // we make sure we fire instances of our home made + // events here. We wrap them only once, though + if (data instanceof InternalEvent) { + + // we are fine, we alread have an event + event = data; + } else { + event = this.createEvent(data); + } + + // ensure we pass the event as the first parameter + args[0] = event; + + // original event type (in case we delegate) + var originalType = event.type; + + // update event type before delegation + if (type !== originalType) { + event.type = type; + } + + try { + returnValue = this._invokeListeners(event, args, firstListener); + } finally { + + // reset event type after delegation + if (type !== originalType) { + event.type = originalType; + } + } + + // set the return value to false if the event default + // got prevented and no other return value exists + if (returnValue === undefined && event.defaultPrevented) { + returnValue = false; + } + + return returnValue; + }; + + + EventBus.prototype.handleError = function(error) { + return this.fire('error', { error: error }) === false; + }; + + + EventBus.prototype._destroy = function() { + this._listeners = {}; + }; + + EventBus.prototype._invokeListeners = function(event, args, listener) { + + var returnValue; + + while (listener) { + + // handle stopped propagation + if (event.cancelBubble) { + break; + } + + returnValue = this._invokeListener(event, args, listener); + + listener = listener.next; + } + + return returnValue; + }; + + EventBus.prototype._invokeListener = function(event, args, listener) { + + var returnValue; + + if (listener.callback.__isTomb) { + return returnValue; + } + + try { + + // returning false prevents the default action + returnValue = invokeFunction(listener.callback, args); + + // stop propagation on return value + if (returnValue !== undefined) { + event.returnValue = returnValue; + event.stopPropagation(); + } + + // prevent default on return false + if (returnValue === false) { + event.preventDefault(); + } + } catch (error) { + if (!this.handleError(error)) { + console.error('unhandled error in event listener', error); + + throw error; + } + } + + return returnValue; + }; + + /* + * Add new listener with a certain priority to the list + * of listeners (for the given event). + * + * The semantics of listener registration / listener execution are + * first register, first serve: New listeners will always be inserted + * after existing listeners with the same priority. + * + * Example: Inserting two listeners with priority 1000 and 1300 + * + * * before: [ 1500, 1500, 1000, 1000 ] + * * after: [ 1500, 1500, (new=1300), 1000, 1000, (new=1000) ] + * + * @param {string} event + * @param {Object} listener { priority, callback } + */ + EventBus.prototype._addListener = function(event, newListener) { + + var listener = this._getListeners(event), + previousListener; + + // no prior listeners + if (!listener) { + this._setListeners(event, newListener); + + return; + } + + // ensure we order listeners by priority from + // 0 (high) to n > 0 (low) + while (listener) { + + if (listener.priority < newListener.priority) { + + newListener.next = listener; + + if (previousListener) { + previousListener.next = newListener; + } else { + this._setListeners(event, newListener); + } + + return; + } + + previousListener = listener; + listener = listener.next; + } + + // add new listener to back + previousListener.next = newListener; + }; + + + EventBus.prototype._getListeners = function(name) { + return this._listeners[name]; + }; + + EventBus.prototype._setListeners = function(name, listener) { + this._listeners[name] = listener; + }; + + EventBus.prototype._removeListener = function(event, callback) { + + var listener = this._getListeners(event), + nextListener, + previousListener, + listenerCallback; + + if (!callback) { + + // clear listeners + this._setListeners(event, null); + + return; + } + + while (listener) { + + nextListener = listener.next; + + listenerCallback = listener.callback; + + if (listenerCallback === callback || listenerCallback[FN_REF] === callback) { + if (previousListener) { + previousListener.next = nextListener; + } else { + + // new first listener + this._setListeners(event, nextListener); + } + } + + previousListener = listener; + listener = nextListener; + } + }; + + /** + * A event that is emitted via the event bus. + */ + function InternalEvent() { } + + InternalEvent.prototype.stopPropagation = function() { + this.cancelBubble = true; + }; + + InternalEvent.prototype.preventDefault = function() { + this.defaultPrevented = true; + }; + + InternalEvent.prototype.init = function(data) { + assign(this, data || {}); + }; + + + /** + * Invoke function. Be fast... + * + * @param {Function} fn + * @param {Array} args + * + * @return {Any} + */ + function invokeFunction(fn, args) { + return fn.apply(null, args); + } + + /** + * SVGs for elements are generated by the {@link GraphicsFactory}. + * + * This utility gives quick access to the important semantic + * parts of an element. + */ + + /** + * Returns the visual part of a diagram element + * + * @param {Snap} gfx + * + * @return {Snap} + */ + function getVisual(gfx) { + return gfx.childNodes[0]; + } + + /** + * Returns the children for a given diagram element. + * + * @param {Snap} gfx + * @return {Snap} + */ + function getChildren$1(gfx) { + return gfx.parentNode.childNodes[1]; + } + + /** + * @param {} element + * @param {number} x + * @param {number} y + * @param {number} angle + * @param {number} amount + */ + function transform(gfx, x, y, angle, amount) { + var translate = createTransform(); + translate.setTranslate(x, y); + + var rotate = createTransform(); + rotate.setRotate(angle || 0, 0, 0); + + var scale = createTransform(); + scale.setScale(amount || 1, amount || 1); + + transform$1(gfx, [ translate, rotate, scale ]); + } + + + /** + * @param {SVGElement} element + * @param {number} x + * @param {number} y + */ + function translate$2(gfx, x, y) { + var translate = createTransform(); + translate.setTranslate(x, y); + + transform$1(gfx, translate); + } + + + /** + * @param {SVGElement} element + * @param {number} angle + */ + function rotate(gfx, angle) { + var rotate = createTransform(); + rotate.setRotate(angle, 0, 0); + + transform$1(gfx, rotate); + } + + /** + * A factory that creates graphical elements + * + * @param {EventBus} eventBus + * @param {ElementRegistry} elementRegistry + */ + function GraphicsFactory(eventBus, elementRegistry) { + this._eventBus = eventBus; + this._elementRegistry = elementRegistry; + } + + GraphicsFactory.$inject = [ 'eventBus' , 'elementRegistry' ]; + + + GraphicsFactory.prototype._getChildrenContainer = function(element) { + + var gfx = this._elementRegistry.getGraphics(element); + + var childrenGfx; + + // root element + if (!element.parent) { + childrenGfx = gfx; + } else { + childrenGfx = getChildren$1(gfx); + if (!childrenGfx) { + childrenGfx = create$1('g'); + classes(childrenGfx).add('djs-children'); + + append(gfx.parentNode, childrenGfx); + } + } + + return childrenGfx; + }; + + /** + * Clears the graphical representation of the element and returns the + * cleared visual (the element). + */ + GraphicsFactory.prototype._clear = function(gfx) { + var visual = getVisual(gfx); + + clear$1(visual); + + return visual; + }; + + /** + * Creates a gfx container for shapes and connections + * + * The layout is as follows: + * + * + * + * + * + * + * + * + * + * + * + * + * + * @param {string} type the type of the element, i.e. shape | connection + * @param {SVGElement} [childrenGfx] + * @param {number} [parentIndex] position to create container in parent + * @param {boolean} [isFrame] is frame element + * + * @return {SVGElement} + */ + GraphicsFactory.prototype._createContainer = function( + type, childrenGfx, parentIndex, isFrame + ) { + var outerGfx = create$1('g'); + classes(outerGfx).add('djs-group'); + + // insert node at position + if (typeof parentIndex !== 'undefined') { + prependTo(outerGfx, childrenGfx, childrenGfx.childNodes[parentIndex]); + } else { + append(childrenGfx, outerGfx); + } + + var gfx = create$1('g'); + classes(gfx).add('djs-element'); + classes(gfx).add('djs-' + type); + + if (isFrame) { + classes(gfx).add('djs-frame'); + } + + append(outerGfx, gfx); + + // create visual + var visual = create$1('g'); + classes(visual).add('djs-visual'); + + append(gfx, visual); + + return gfx; + }; + + GraphicsFactory.prototype.create = function(type, element, parentIndex) { + var childrenGfx = this._getChildrenContainer(element.parent); + return this._createContainer(type, childrenGfx, parentIndex, isFrameElement$1(element)); + }; + + GraphicsFactory.prototype.updateContainments = function(elements) { + + var self = this, + elementRegistry = this._elementRegistry, + parents; + + parents = reduce(elements, function(map, e) { + + if (e.parent) { + map[e.parent.id] = e.parent; + } + + return map; + }, {}); + + // update all parents of changed and reorganized their children + // in the correct order (as indicated in our model) + forEach$1(parents, function(parent) { + + var children = parent.children; + + if (!children) { + return; + } + + var childrenGfx = self._getChildrenContainer(parent); + + forEach$1(children.slice().reverse(), function(child) { + var childGfx = elementRegistry.getGraphics(child); + + prependTo(childGfx.parentNode, childrenGfx); + }); + }); + }; + + GraphicsFactory.prototype.drawShape = function(visual, element) { + var eventBus = this._eventBus; + + return eventBus.fire('render.shape', { gfx: visual, element: element }); + }; + + GraphicsFactory.prototype.getShapePath = function(element) { + var eventBus = this._eventBus; + + return eventBus.fire('render.getShapePath', element); + }; + + GraphicsFactory.prototype.drawConnection = function(visual, element) { + var eventBus = this._eventBus; + + return eventBus.fire('render.connection', { gfx: visual, element: element }); + }; + + GraphicsFactory.prototype.getConnectionPath = function(waypoints) { + var eventBus = this._eventBus; + + return eventBus.fire('render.getConnectionPath', waypoints); + }; + + GraphicsFactory.prototype.update = function(type, element, gfx) { + + // do NOT update root element + if (!element.parent) { + return; + } + + var visual = this._clear(gfx); + + // redraw + if (type === 'shape') { + this.drawShape(visual, element); + + // update positioning + translate$2(gfx, element.x, element.y); + } else + if (type === 'connection') { + this.drawConnection(visual, element); + } else { + throw new Error('unknown type: ' + type); + } + + if (element.hidden) { + attr(gfx, 'display', 'none'); + } else { + attr(gfx, 'display', 'block'); + } + }; + + GraphicsFactory.prototype.remove = function(element) { + var gfx = this._elementRegistry.getGraphics(element); + + // remove + remove$1(gfx.parentNode); + }; + + + // helpers ////////// + + function prependTo(newNode, parentNode, siblingNode) { + var node = siblingNode || parentNode.firstChild; + + // do not prepend node to itself to prevent IE from crashing + // https://github.com/bpmn-io/bpmn-js/issues/746 + if (newNode === node) { + return; + } + + parentNode.insertBefore(newNode, node); + } + + var CoreModule$1 = { + __depends__: [ DrawModule$1 ], + __init__: [ 'canvas' ], + canvas: [ 'type', Canvas ], + elementRegistry: [ 'type', ElementRegistry ], + elementFactory: [ 'type', ElementFactory$1 ], + eventBus: [ 'type', EventBus ], + graphicsFactory: [ 'type', GraphicsFactory ] + }; + + /** + * @typedef { import('didi').ModuleDeclaration } Module + */ + + /** + * Bootstrap an injector from a list of modules, instantiating a number of default components + * + * @param {Array} modules + * + * @return {Injector} a injector to use to access the components + */ + function bootstrap(modules) { + var injector = new Injector(modules); + + injector.init(); + + return injector; + } + + /** + * Creates an injector from passed options. + * + * @param {Object} options + * @return {Injector} + */ + function createInjector(options) { + + options = options || {}; + + var configModule = { + 'config': [ 'value', options ] + }; + + var modules = [ configModule, CoreModule$1 ].concat(options.modules || []); + + return bootstrap(modules); + } + + + /** + * The main diagram-js entry point that bootstraps the diagram with the given + * configuration. + * + * To register extensions with the diagram, pass them as Array to the constructor. + * + * @class djs.Diagram + * @memberOf djs + * @constructor + * + * @example + * + * Creating a plug-in that logs whenever a shape is added to the canvas. + * + * // plug-in implemenentation + * function MyLoggingPlugin(eventBus) { + * eventBus.on('shape.added', function(event) { + * console.log('shape ', event.shape, ' was added to the diagram'); + * }); + * } + * + * // export as module + * export default { + * __init__: [ 'myLoggingPlugin' ], + * myLoggingPlugin: [ 'type', MyLoggingPlugin ] + * }; + * + * + * // instantiate the diagram with the new plug-in + * + * import MyLoggingModule from 'path-to-my-logging-plugin'; + * + * var diagram = new Diagram({ + * modules: [ + * MyLoggingModule + * ] + * }); + * + * diagram.invoke([ 'canvas', function(canvas) { + * // add shape to drawing canvas + * canvas.addShape({ x: 10, y: 10 }); + * }); + * + * // 'shape ... was added to the diagram' logged to console + * + * @param {Object} options + * @param {Array} [options.modules] external modules to instantiate with the diagram + * @param {Injector} [injector] an (optional) injector to bootstrap the diagram with + */ + function Diagram(options, injector) { + + // create injector unless explicitly specified + this.injector = injector = injector || createInjector(options); + + // API + + /** + * Resolves a diagram service + * + * @method Diagram#get + * + * @param {string} name the name of the diagram service to be retrieved + * @param {boolean} [strict=true] if false, resolve missing services to null + */ + this.get = injector.get; + + /** + * Executes a function into which diagram services are injected + * + * @method Diagram#invoke + * + * @param {Function|Object[]} fn the function to resolve + * @param {Object} locals a number of locals to use to resolve certain dependencies + */ + this.invoke = injector.invoke; + + // init + + // indicate via event + + + /** + * An event indicating that all plug-ins are loaded. + * + * Use this event to fire other events to interested plug-ins + * + * @memberOf Diagram + * + * @event diagram.init + * + * @example + * + * eventBus.on('diagram.init', function() { + * eventBus.fire('my-custom-event', { foo: 'BAR' }); + * }); + * + * @type {Object} + */ + this.get('eventBus').fire('diagram.init'); + } + + + /** + * Destroys the diagram + * + * @method Diagram#destroy + */ + Diagram.prototype.destroy = function() { + this.get('eventBus').fire('diagram.destroy'); + }; + + /** + * Clear the diagram, removing all contents. + */ + Diagram.prototype.clear = function() { + this.get('eventBus').fire('diagram.clear'); + }; + + /** + * Moddle base element. + */ + function Base() { } + + Base.prototype.get = function(name) { + return this.$model.properties.get(this, name); + }; + + Base.prototype.set = function(name, value) { + this.$model.properties.set(this, name, value); + }; + + /** + * A model element factory. + * + * @param {Moddle} model + * @param {Properties} properties + */ + function Factory(model, properties) { + this.model = model; + this.properties = properties; + } + + + Factory.prototype.createType = function(descriptor) { + + var model = this.model; + + var props = this.properties, + prototype = Object.create(Base.prototype); + + // initialize default values + forEach$1(descriptor.properties, function(p) { + if (!p.isMany && p.default !== undefined) { + prototype[p.name] = p.default; + } + }); + + props.defineModel(prototype, model); + props.defineDescriptor(prototype, descriptor); + + var name = descriptor.ns.name; + + /** + * The new type constructor + */ + function ModdleElement(attrs) { + props.define(this, '$type', { value: name, enumerable: true }); + props.define(this, '$attrs', { value: {} }); + props.define(this, '$parent', { writable: true }); + + forEach$1(attrs, bind(function(val, key) { + this.set(key, val); + }, this)); + } + + ModdleElement.prototype = prototype; + + ModdleElement.hasType = prototype.$instanceOf = this.model.hasType; + + // static links + props.defineModel(ModdleElement, model); + props.defineDescriptor(ModdleElement, descriptor); + + return ModdleElement; + }; + + /** + * Built-in moddle types + */ + var BUILTINS = { + String: true, + Boolean: true, + Integer: true, + Real: true, + Element: true + }; + + /** + * Converters for built in types from string representations + */ + var TYPE_CONVERTERS = { + String: function(s) { return s; }, + Boolean: function(s) { return s === 'true'; }, + Integer: function(s) { return parseInt(s, 10); }, + Real: function(s) { return parseFloat(s); } + }; + + /** + * Convert a type to its real representation + */ + function coerceType(type, value) { + + var converter = TYPE_CONVERTERS[type]; + + if (converter) { + return converter(value); + } else { + return value; + } + } + + /** + * Return whether the given type is built-in + */ + function isBuiltIn(type) { + return !!BUILTINS[type]; + } + + /** + * Return whether the given type is simple + */ + function isSimple(type) { + return !!TYPE_CONVERTERS[type]; + } + + /** + * Parses a namespaced attribute name of the form (ns:)localName to an object, + * given a default prefix to assume in case no explicit namespace is given. + * + * @param {String} name + * @param {String} [defaultPrefix] the default prefix to take, if none is present. + * + * @return {Object} the parsed name + */ + function parseName(name, defaultPrefix) { + var parts = name.split(/:/), + localName, prefix; + + // no prefix (i.e. only local name) + if (parts.length === 1) { + localName = name; + prefix = defaultPrefix; + } else + // prefix + local name + if (parts.length === 2) { + localName = parts[1]; + prefix = parts[0]; + } else { + throw new Error('expected or , got ' + name); + } + + name = (prefix ? prefix + ':' : '') + localName; + + return { + name: name, + prefix: prefix, + localName: localName + }; + } + + /** + * A utility to build element descriptors. + */ + function DescriptorBuilder(nameNs) { + this.ns = nameNs; + this.name = nameNs.name; + this.allTypes = []; + this.allTypesByName = {}; + this.properties = []; + this.propertiesByName = {}; + } + + + DescriptorBuilder.prototype.build = function() { + return pick(this, [ + 'ns', + 'name', + 'allTypes', + 'allTypesByName', + 'properties', + 'propertiesByName', + 'bodyProperty', + 'idProperty' + ]); + }; + + /** + * Add property at given index. + * + * @param {Object} p + * @param {Number} [idx] + * @param {Boolean} [validate=true] + */ + DescriptorBuilder.prototype.addProperty = function(p, idx, validate) { + + if (typeof idx === 'boolean') { + validate = idx; + idx = undefined; + } + + this.addNamedProperty(p, validate !== false); + + var properties = this.properties; + + if (idx !== undefined) { + properties.splice(idx, 0, p); + } else { + properties.push(p); + } + }; + + + DescriptorBuilder.prototype.replaceProperty = function(oldProperty, newProperty, replace) { + var oldNameNs = oldProperty.ns; + + var props = this.properties, + propertiesByName = this.propertiesByName, + rename = oldProperty.name !== newProperty.name; + + if (oldProperty.isId) { + if (!newProperty.isId) { + throw new Error( + 'property <' + newProperty.ns.name + '> must be id property ' + + 'to refine <' + oldProperty.ns.name + '>'); + } + + this.setIdProperty(newProperty, false); + } + + if (oldProperty.isBody) { + + if (!newProperty.isBody) { + throw new Error( + 'property <' + newProperty.ns.name + '> must be body property ' + + 'to refine <' + oldProperty.ns.name + '>'); + } + + // TODO: Check compatibility + this.setBodyProperty(newProperty, false); + } + + // validate existence and get location of old property + var idx = props.indexOf(oldProperty); + if (idx === -1) { + throw new Error('property <' + oldNameNs.name + '> not found in property list'); + } + + // remove old property + props.splice(idx, 1); + + // replacing the named property is intentional + // + // * validate only if this is a "rename" operation + // * add at specific index unless we "replace" + // + this.addProperty(newProperty, replace ? undefined : idx, rename); + + // make new property available under old name + propertiesByName[oldNameNs.name] = propertiesByName[oldNameNs.localName] = newProperty; + }; + + + DescriptorBuilder.prototype.redefineProperty = function(p, targetPropertyName, replace) { + + var nsPrefix = p.ns.prefix; + var parts = targetPropertyName.split('#'); + + var name = parseName(parts[0], nsPrefix); + var attrName = parseName(parts[1], name.prefix).name; + + var redefinedProperty = this.propertiesByName[attrName]; + if (!redefinedProperty) { + throw new Error('refined property <' + attrName + '> not found'); + } else { + this.replaceProperty(redefinedProperty, p, replace); + } + + delete p.redefines; + }; + + DescriptorBuilder.prototype.addNamedProperty = function(p, validate) { + var ns = p.ns, + propsByName = this.propertiesByName; + + if (validate) { + this.assertNotDefined(p, ns.name); + this.assertNotDefined(p, ns.localName); + } + + propsByName[ns.name] = propsByName[ns.localName] = p; + }; + + DescriptorBuilder.prototype.removeNamedProperty = function(p) { + var ns = p.ns, + propsByName = this.propertiesByName; + + delete propsByName[ns.name]; + delete propsByName[ns.localName]; + }; + + DescriptorBuilder.prototype.setBodyProperty = function(p, validate) { + + if (validate && this.bodyProperty) { + throw new Error( + 'body property defined multiple times ' + + '(<' + this.bodyProperty.ns.name + '>, <' + p.ns.name + '>)'); + } + + this.bodyProperty = p; + }; + + DescriptorBuilder.prototype.setIdProperty = function(p, validate) { + + if (validate && this.idProperty) { + throw new Error( + 'id property defined multiple times ' + + '(<' + this.idProperty.ns.name + '>, <' + p.ns.name + '>)'); + } + + this.idProperty = p; + }; + + DescriptorBuilder.prototype.assertNotDefined = function(p, name) { + var propertyName = p.name, + definedProperty = this.propertiesByName[propertyName]; + + if (definedProperty) { + throw new Error( + 'property <' + propertyName + '> already defined; ' + + 'override of <' + definedProperty.definedBy.ns.name + '#' + definedProperty.ns.name + '> by ' + + '<' + p.definedBy.ns.name + '#' + p.ns.name + '> not allowed without redefines'); + } + }; + + DescriptorBuilder.prototype.hasProperty = function(name) { + return this.propertiesByName[name]; + }; + + DescriptorBuilder.prototype.addTrait = function(t, inherited) { + + var typesByName = this.allTypesByName, + types = this.allTypes; + + var typeName = t.name; + + if (typeName in typesByName) { + return; + } + + forEach$1(t.properties, bind(function(p) { + + // clone property to allow extensions + p = assign({}, p, { + name: p.ns.localName, + inherited: inherited + }); + + Object.defineProperty(p, 'definedBy', { + value: t + }); + + var replaces = p.replaces, + redefines = p.redefines; + + // add replace/redefine support + if (replaces || redefines) { + this.redefineProperty(p, replaces || redefines, replaces); + } else { + if (p.isBody) { + this.setBodyProperty(p); + } + if (p.isId) { + this.setIdProperty(p); + } + this.addProperty(p); + } + }, this)); + + types.push(t); + typesByName[typeName] = t; + }; + + /** + * A registry of Moddle packages. + * + * @param {Array} packages + * @param {Properties} properties + */ + function Registry(packages, properties) { + this.packageMap = {}; + this.typeMap = {}; + + this.packages = []; + + this.properties = properties; + + forEach$1(packages, bind(this.registerPackage, this)); + } + + + Registry.prototype.getPackage = function(uriOrPrefix) { + return this.packageMap[uriOrPrefix]; + }; + + Registry.prototype.getPackages = function() { + return this.packages; + }; + + + Registry.prototype.registerPackage = function(pkg) { + + // copy package + pkg = assign({}, pkg); + + var pkgMap = this.packageMap; + + ensureAvailable(pkgMap, pkg, 'prefix'); + ensureAvailable(pkgMap, pkg, 'uri'); + + // register types + forEach$1(pkg.types, bind(function(descriptor) { + this.registerType(descriptor, pkg); + }, this)); + + pkgMap[pkg.uri] = pkgMap[pkg.prefix] = pkg; + this.packages.push(pkg); + }; + + + /** + * Register a type from a specific package with us + */ + Registry.prototype.registerType = function(type, pkg) { + + type = assign({}, type, { + superClass: (type.superClass || []).slice(), + extends: (type.extends || []).slice(), + properties: (type.properties || []).slice(), + meta: assign((type.meta || {})) + }); + + var ns = parseName(type.name, pkg.prefix), + name = ns.name, + propertiesByName = {}; + + // parse properties + forEach$1(type.properties, bind(function(p) { + + // namespace property names + var propertyNs = parseName(p.name, ns.prefix), + propertyName = propertyNs.name; + + // namespace property types + if (!isBuiltIn(p.type)) { + p.type = parseName(p.type, propertyNs.prefix).name; + } + + assign(p, { + ns: propertyNs, + name: propertyName + }); + + propertiesByName[propertyName] = p; + }, this)); + + // update ns + name + assign(type, { + ns: ns, + name: name, + propertiesByName: propertiesByName + }); + + forEach$1(type.extends, bind(function(extendsName) { + var extended = this.typeMap[extendsName]; + + extended.traits = extended.traits || []; + extended.traits.push(name); + }, this)); + + // link to package + this.definePackage(type, pkg); + + // register + this.typeMap[name] = type; + }; + + + /** + * Traverse the type hierarchy from bottom to top, + * calling iterator with (type, inherited) for all elements in + * the inheritance chain. + * + * @param {Object} nsName + * @param {Function} iterator + * @param {Boolean} [trait=false] + */ + Registry.prototype.mapTypes = function(nsName, iterator, trait) { + + var type = isBuiltIn(nsName.name) ? { name: nsName.name } : this.typeMap[nsName.name]; + + var self = this; + + /** + * Traverse the selected trait. + * + * @param {String} cls + */ + function traverseTrait(cls) { + return traverseSuper(cls, true); + } + + /** + * Traverse the selected super type or trait + * + * @param {String} cls + * @param {Boolean} [trait=false] + */ + function traverseSuper(cls, trait) { + var parentNs = parseName(cls, isBuiltIn(cls) ? '' : nsName.prefix); + self.mapTypes(parentNs, iterator, trait); + } + + if (!type) { + throw new Error('unknown type <' + nsName.name + '>'); + } + + forEach$1(type.superClass, trait ? traverseTrait : traverseSuper); + + // call iterator with (type, inherited=!trait) + iterator(type, !trait); + + forEach$1(type.traits, traverseTrait); + }; + + + /** + * Returns the effective descriptor for a type. + * + * @param {String} type the namespaced name (ns:localName) of the type + * + * @return {Descriptor} the resulting effective descriptor + */ + Registry.prototype.getEffectiveDescriptor = function(name) { + + var nsName = parseName(name); + + var builder = new DescriptorBuilder(nsName); + + this.mapTypes(nsName, function(type, inherited) { + builder.addTrait(type, inherited); + }); + + var descriptor = builder.build(); + + // define package link + this.definePackage(descriptor, descriptor.allTypes[descriptor.allTypes.length - 1].$pkg); + + return descriptor; + }; + + + Registry.prototype.definePackage = function(target, pkg) { + this.properties.define(target, '$pkg', { value: pkg }); + }; + + + + ///////// helpers //////////////////////////// + + function ensureAvailable(packageMap, pkg, identifierKey) { + + var value = pkg[identifierKey]; + + if (value in packageMap) { + throw new Error('package with ' + identifierKey + ' <' + value + '> already defined'); + } + } + + /** + * A utility that gets and sets properties of model elements. + * + * @param {Model} model + */ + function Properties(model) { + this.model = model; + } + + + /** + * Sets a named property on the target element. + * If the value is undefined, the property gets deleted. + * + * @param {Object} target + * @param {String} name + * @param {Object} value + */ + Properties.prototype.set = function(target, name, value) { + + var property = this.model.getPropertyDescriptor(target, name); + + var propertyName = property && property.name; + + if (isUndefined(value)) { + // unset the property, if the specified value is undefined; + // delete from $attrs (for extensions) or the target itself + if (property) { + delete target[propertyName]; + } else { + delete target.$attrs[name]; + } + } else { + // set the property, defining well defined properties on the fly + // or simply updating them in target.$attrs (for extensions) + if (property) { + if (propertyName in target) { + target[propertyName] = value; + } else { + defineProperty(target, property, value); + } + } else { + target.$attrs[name] = value; + } + } + }; + + /** + * Returns the named property of the given element + * + * @param {Object} target + * @param {String} name + * + * @return {Object} + */ + Properties.prototype.get = function(target, name) { + + var property = this.model.getPropertyDescriptor(target, name); + + if (!property) { + return target.$attrs[name]; + } + + var propertyName = property.name; + + // check if access to collection property and lazily initialize it + if (!target[propertyName] && property.isMany) { + defineProperty(target, property, []); + } + + return target[propertyName]; + }; + + + /** + * Define a property on the target element + * + * @param {Object} target + * @param {String} name + * @param {Object} options + */ + Properties.prototype.define = function(target, name, options) { + + if (!options.writable) { + + var value = options.value; + + // use getters for read-only variables to support ES6 proxies + // cf. https://github.com/bpmn-io/internal-docs/issues/386 + options = assign({}, options, { + get: function() { return value; } + }); + + delete options.value; + } + + Object.defineProperty(target, name, options); + }; + + + /** + * Define the descriptor for an element + */ + Properties.prototype.defineDescriptor = function(target, descriptor) { + this.define(target, '$descriptor', { value: descriptor }); + }; + + /** + * Define the model for an element + */ + Properties.prototype.defineModel = function(target, model) { + this.define(target, '$model', { value: model }); + }; + + + function isUndefined(val) { + return typeof val === 'undefined'; + } + + function defineProperty(target, property, value) { + Object.defineProperty(target, property.name, { + enumerable: !property.isReference, + writable: true, + value: value, + configurable: true + }); + } + + //// Moddle implementation ///////////////////////////////////////////////// + + /** + * @class Moddle + * + * A model that can be used to create elements of a specific type. + * + * @example + * + * var Moddle = require('moddle'); + * + * var pkg = { + * name: 'mypackage', + * prefix: 'my', + * types: [ + * { name: 'Root' } + * ] + * }; + * + * var moddle = new Moddle([pkg]); + * + * @param {Array} packages the packages to contain + */ + function Moddle(packages) { + + this.properties = new Properties(this); + + this.factory = new Factory(this, this.properties); + this.registry = new Registry(packages, this.properties); + + this.typeCache = {}; + } + + + /** + * Create an instance of the specified type. + * + * @method Moddle#create + * + * @example + * + * var foo = moddle.create('my:Foo'); + * var bar = moddle.create('my:Bar', { id: 'BAR_1' }); + * + * @param {String|Object} descriptor the type descriptor or name know to the model + * @param {Object} attrs a number of attributes to initialize the model instance with + * @return {Object} model instance + */ + Moddle.prototype.create = function(descriptor, attrs) { + var Type = this.getType(descriptor); + + if (!Type) { + throw new Error('unknown type <' + descriptor + '>'); + } + + return new Type(attrs); + }; + + + /** + * Returns the type representing a given descriptor + * + * @method Moddle#getType + * + * @example + * + * var Foo = moddle.getType('my:Foo'); + * var foo = new Foo({ 'id' : 'FOO_1' }); + * + * @param {String|Object} descriptor the type descriptor or name know to the model + * @return {Object} the type representing the descriptor + */ + Moddle.prototype.getType = function(descriptor) { + + var cache = this.typeCache; + + var name = isString(descriptor) ? descriptor : descriptor.ns.name; + + var type = cache[name]; + + if (!type) { + descriptor = this.registry.getEffectiveDescriptor(name); + type = cache[name] = this.factory.createType(descriptor); + } + + return type; + }; + + + /** + * Creates an any-element type to be used within model instances. + * + * This can be used to create custom elements that lie outside the meta-model. + * The created element contains all the meta-data required to serialize it + * as part of meta-model elements. + * + * @method Moddle#createAny + * + * @example + * + * var foo = moddle.createAny('vendor:Foo', 'http://vendor', { + * value: 'bar' + * }); + * + * var container = moddle.create('my:Container', 'http://my', { + * any: [ foo ] + * }); + * + * // go ahead and serialize the stuff + * + * + * @param {String} name the name of the element + * @param {String} nsUri the namespace uri of the element + * @param {Object} [properties] a map of properties to initialize the instance with + * @return {Object} the any type instance + */ + Moddle.prototype.createAny = function(name, nsUri, properties) { + + var nameNs = parseName(name); + + var element = { + $type: name, + $instanceOf: function(type) { + return type === this.$type; + } + }; + + var descriptor = { + name: name, + isGeneric: true, + ns: { + prefix: nameNs.prefix, + localName: nameNs.localName, + uri: nsUri + } + }; + + this.properties.defineDescriptor(element, descriptor); + this.properties.defineModel(element, this); + this.properties.define(element, '$parent', { enumerable: false, writable: true }); + this.properties.define(element, '$instanceOf', { enumerable: false, writable: true }); + + forEach$1(properties, function(a, key) { + if (isObject(a) && a.value !== undefined) { + element[a.name] = a.value; + } else { + element[key] = a; + } + }); + + return element; + }; + + /** + * Returns a registered package by uri or prefix + * + * @return {Object} the package + */ + Moddle.prototype.getPackage = function(uriOrPrefix) { + return this.registry.getPackage(uriOrPrefix); + }; + + /** + * Returns a snapshot of all known packages + * + * @return {Object} the package + */ + Moddle.prototype.getPackages = function() { + return this.registry.getPackages(); + }; + + /** + * Returns the descriptor for an element + */ + Moddle.prototype.getElementDescriptor = function(element) { + return element.$descriptor; + }; + + /** + * Returns true if the given descriptor or instance + * represents the given type. + * + * May be applied to this, if element is omitted. + */ + Moddle.prototype.hasType = function(element, type) { + if (type === undefined) { + type = element; + element = this; + } + + var descriptor = element.$model.getElementDescriptor(element); + + return (type in descriptor.allTypesByName); + }; + + /** + * Returns the descriptor of an elements named property + */ + Moddle.prototype.getPropertyDescriptor = function(element, property) { + return this.getElementDescriptor(element).propertiesByName[property]; + }; + + /** + * Returns a mapped type's descriptor + */ + Moddle.prototype.getTypeDescriptor = function(type) { + return this.registry.typeMap[type]; + }; + + var fromCharCode = String.fromCharCode; + + var hasOwnProperty = Object.prototype.hasOwnProperty; + + var ENTITY_PATTERN = /&#(\d+);|&#x([0-9a-f]+);|&(\w+);/ig; + + var ENTITY_MAPPING = { + 'amp': '&', + 'apos': '\'', + 'gt': '>', + 'lt': '<', + 'quot': '"' + }; + + // map UPPERCASE variants of supported special chars + Object.keys(ENTITY_MAPPING).forEach(function(k) { + ENTITY_MAPPING[k.toUpperCase()] = ENTITY_MAPPING[k]; + }); + + + function replaceEntities(_, d, x, z) { + + // reserved names, i.e.   + if (z) { + if (hasOwnProperty.call(ENTITY_MAPPING, z)) { + return ENTITY_MAPPING[z]; + } else { + + // fall back to original value + return '&' + z + ';'; + } + } + + // decimal encoded char + if (d) { + return fromCharCode(d); + } + + // hex encoded char + return fromCharCode(parseInt(x, 16)); + } + + + /** + * A basic entity decoder that can decode a minimal + * sub-set of reserved names (&) as well as + * hex (ય) and decimal (ӏ) encoded characters. + * + * @param {string} str + * + * @return {string} decoded string + */ + function decodeEntities(s) { + if (s.length > 3 && s.indexOf('&') !== -1) { + return s.replace(ENTITY_PATTERN, replaceEntities); + } + + return s; + } + + var XSI_URI = 'http://www.w3.org/2001/XMLSchema-instance'; + var XSI_PREFIX = 'xsi'; + var XSI_TYPE$1 = 'xsi:type'; + + var NON_WHITESPACE_OUTSIDE_ROOT_NODE = 'non-whitespace outside of root node'; + + function error$2(msg) { + return new Error(msg); + } + + function missingNamespaceForPrefix(prefix) { + return 'missing namespace for prefix <' + prefix + '>'; + } + + function getter(getFn) { + return { + 'get': getFn, + 'enumerable': true + }; + } + + function cloneNsMatrix(nsMatrix) { + var clone = {}, key; + for (key in nsMatrix) { + clone[key] = nsMatrix[key]; + } + return clone; + } + + function uriPrefix(prefix) { + return prefix + '$uri'; + } + + function buildNsMatrix(nsUriToPrefix) { + var nsMatrix = {}, + uri, + prefix; + + for (uri in nsUriToPrefix) { + prefix = nsUriToPrefix[uri]; + nsMatrix[prefix] = prefix; + nsMatrix[uriPrefix(prefix)] = uri; + } + + return nsMatrix; + } + + function noopGetContext() { + return { 'line': 0, 'column': 0 }; + } + + function throwFunc(err) { + throw err; + } + + /** + * Creates a new parser with the given options. + * + * @constructor + * + * @param {!Object=} options + */ + function Parser(options) { + + if (!this) { + return new Parser(options); + } + + var proxy = options && options['proxy']; + + var onText, + onOpenTag, + onCloseTag, + onCDATA, + onError = throwFunc, + onWarning, + onComment, + onQuestion, + onAttention; + + var getContext = noopGetContext; + + /** + * Do we need to parse the current elements attributes for namespaces? + * + * @type {boolean} + */ + var maybeNS = false; + + /** + * Do we process namespaces at all? + * + * @type {boolean} + */ + var isNamespace = false; + + /** + * The caught error returned on parse end + * + * @type {Error} + */ + var returnError = null; + + /** + * Should we stop parsing? + * + * @type {boolean} + */ + var parseStop = false; + + /** + * A map of { uri: prefix } used by the parser. + * + * This map will ensure we can normalize prefixes during processing; + * for each uri, only one prefix will be exposed to the handlers. + * + * @type {!Object}} + */ + var nsUriToPrefix; + + /** + * Handle parse error. + * + * @param {string|Error} err + */ + function handleError(err) { + if (!(err instanceof Error)) { + err = error$2(err); + } + + returnError = err; + + onError(err, getContext); + } + + /** + * Handle parse error. + * + * @param {string|Error} err + */ + function handleWarning(err) { + + if (!onWarning) { + return; + } + + if (!(err instanceof Error)) { + err = error$2(err); + } + + onWarning(err, getContext); + } + + /** + * Register parse listener. + * + * @param {string} name + * @param {Function} cb + * + * @return {Parser} + */ + this['on'] = function(name, cb) { + + if (typeof cb !== 'function') { + throw error$2('required args '); + } + + switch (name) { + case 'openTag': onOpenTag = cb; break; + case 'text': onText = cb; break; + case 'closeTag': onCloseTag = cb; break; + case 'error': onError = cb; break; + case 'warn': onWarning = cb; break; + case 'cdata': onCDATA = cb; break; + case 'attention': onAttention = cb; break; // + case 'question': onQuestion = cb; break; // + case 'comment': onComment = cb; break; + default: + throw error$2('unsupported event: ' + name); + } + + return this; + }; + + /** + * Set the namespace to prefix mapping. + * + * @example + * + * parser.ns({ + * 'http://foo': 'foo', + * 'http://bar': 'bar' + * }); + * + * @param {!Object} nsMap + * + * @return {Parser} + */ + this['ns'] = function(nsMap) { + + if (typeof nsMap === 'undefined') { + nsMap = {}; + } + + if (typeof nsMap !== 'object') { + throw error$2('required args '); + } + + var _nsUriToPrefix = {}, k; + + for (k in nsMap) { + _nsUriToPrefix[k] = nsMap[k]; + } + + // FORCE default mapping for schema instance + _nsUriToPrefix[XSI_URI] = XSI_PREFIX; + + isNamespace = true; + nsUriToPrefix = _nsUriToPrefix; + + return this; + }; + + /** + * Parse xml string. + * + * @param {string} xml + * + * @return {Error} returnError, if not thrown + */ + this['parse'] = function(xml) { + if (typeof xml !== 'string') { + throw error$2('required args '); + } + + returnError = null; + + parse(xml); + + getContext = noopGetContext; + parseStop = false; + + return returnError; + }; + + /** + * Stop parsing. + */ + this['stop'] = function() { + parseStop = true; + }; + + /** + * Parse string, invoking configured listeners on element. + * + * @param {string} xml + */ + function parse(xml) { + var nsMatrixStack = isNamespace ? [] : null, + nsMatrix = isNamespace ? buildNsMatrix(nsUriToPrefix) : null, + _nsMatrix, + nodeStack = [], + anonymousNsCount = 0, + tagStart = false, + tagEnd = false, + i = 0, j = 0, + x, y, q, w, v, + xmlns, + elementName, + _elementName, + elementProxy + ; + + var attrsString = '', + attrsStart = 0, + cachedAttrs // false = parsed with errors, null = needs parsing + ; + + /** + * Parse attributes on demand and returns the parsed attributes. + * + * Return semantics: (1) `false` on attribute parse error, + * (2) object hash on extracted attrs. + * + * @return {boolean|Object} + */ + function getAttrs() { + if (cachedAttrs !== null) { + return cachedAttrs; + } + + var nsUri, + nsUriPrefix, + nsName, + defaultAlias = isNamespace && nsMatrix['xmlns'], + attrList = isNamespace && maybeNS ? [] : null, + i = attrsStart, + s = attrsString, + l = s.length, + hasNewMatrix, + newalias, + value, + alias, + name, + attrs = {}, + seenAttrs = {}, + skipAttr, + w, + j; + + parseAttr: + for (; i < l; i++) { + skipAttr = false; + w = s.charCodeAt(i); + + if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE={ \f\n\r\t\v} + continue; + } + + // wait for non whitespace character + if (w < 65 || w > 122 || (w > 90 && w < 97)) { + if (w !== 95 && w !== 58) { // char 95"_" 58":" + handleWarning('illegal first char attribute name'); + skipAttr = true; + } + } + + // parse attribute name + for (j = i + 1; j < l; j++) { + w = s.charCodeAt(j); + + if ( + w > 96 && w < 123 || + w > 64 && w < 91 || + w > 47 && w < 59 || + w === 46 || // '.' + w === 45 || // '-' + w === 95 // '_' + ) { + continue; + } + + // unexpected whitespace + if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE + handleWarning('missing attribute value'); + i = j; + + continue parseAttr; + } + + // expected "=" + if (w === 61) { // "=" == 61 + break; + } + + handleWarning('illegal attribute name char'); + skipAttr = true; + } + + name = s.substring(i, j); + + if (name === 'xmlns:xmlns') { + handleWarning('illegal declaration of xmlns'); + skipAttr = true; + } + + w = s.charCodeAt(j + 1); + + if (w === 34) { // '"' + j = s.indexOf('"', i = j + 2); + + if (j === -1) { + j = s.indexOf('\'', i); + + if (j !== -1) { + handleWarning('attribute value quote missmatch'); + skipAttr = true; + } + } + + } else if (w === 39) { // "'" + j = s.indexOf('\'', i = j + 2); + + if (j === -1) { + j = s.indexOf('"', i); + + if (j !== -1) { + handleWarning('attribute value quote missmatch'); + skipAttr = true; + } + } + + } else { + handleWarning('missing attribute value quotes'); + skipAttr = true; + + // skip to next space + for (j = j + 1; j < l; j++) { + w = s.charCodeAt(j + 1); + + if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE + break; + } + } + + } + + if (j === -1) { + handleWarning('missing closing quotes'); + + j = l; + skipAttr = true; + } + + if (!skipAttr) { + value = s.substring(i, j); + } + + i = j; + + // ensure SPACE follows attribute + // skip illegal content otherwise + // example a="b"c + for (; j + 1 < l; j++) { + w = s.charCodeAt(j + 1); + + if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE + break; + } + + // FIRST ILLEGAL CHAR + if (i === j) { + handleWarning('illegal character after attribute end'); + skipAttr = true; + } + } + + // advance cursor to next attribute + i = j + 1; + + if (skipAttr) { + continue parseAttr; + } + + // check attribute re-declaration + if (name in seenAttrs) { + handleWarning('attribute <' + name + '> already defined'); + continue; + } + + seenAttrs[name] = true; + + if (!isNamespace) { + attrs[name] = value; + continue; + } + + // try to extract namespace information + if (maybeNS) { + newalias = ( + name === 'xmlns' + ? 'xmlns' + : (name.charCodeAt(0) === 120 && name.substr(0, 6) === 'xmlns:') + ? name.substr(6) + : null + ); + + // handle xmlns(:alias) assignment + if (newalias !== null) { + nsUri = decodeEntities(value); + nsUriPrefix = uriPrefix(newalias); + + alias = nsUriToPrefix[nsUri]; + + if (!alias) { + + // no prefix defined or prefix collision + if ( + (newalias === 'xmlns') || + (nsUriPrefix in nsMatrix && nsMatrix[nsUriPrefix] !== nsUri) + ) { + + // alocate free ns prefix + do { + alias = 'ns' + (anonymousNsCount++); + } while (typeof nsMatrix[alias] !== 'undefined'); + } else { + alias = newalias; + } + + nsUriToPrefix[nsUri] = alias; + } + + if (nsMatrix[newalias] !== alias) { + if (!hasNewMatrix) { + nsMatrix = cloneNsMatrix(nsMatrix); + hasNewMatrix = true; + } + + nsMatrix[newalias] = alias; + if (newalias === 'xmlns') { + nsMatrix[uriPrefix(alias)] = nsUri; + defaultAlias = alias; + } + + nsMatrix[nsUriPrefix] = nsUri; + } + + // expose xmlns(:asd)="..." in attributes + attrs[name] = value; + continue; + } + + // collect attributes until all namespace + // declarations are processed + attrList.push(name, value); + continue; + + } /** end if (maybeNs) */ + + // handle attributes on element without + // namespace declarations + w = name.indexOf(':'); + if (w === -1) { + attrs[name] = value; + continue; + } + + // normalize ns attribute name + if (!(nsName = nsMatrix[name.substring(0, w)])) { + handleWarning(missingNamespaceForPrefix(name.substring(0, w))); + continue; + } + + name = defaultAlias === nsName + ? name.substr(w + 1) + : nsName + name.substr(w); + + // end: normalize ns attribute name + + // normalize xsi:type ns attribute value + if (name === XSI_TYPE$1) { + w = value.indexOf(':'); + + if (w !== -1) { + nsName = value.substring(0, w); + + // handle default prefixes, i.e. xs:String gracefully + nsName = nsMatrix[nsName] || nsName; + value = nsName + value.substring(w); + } else { + value = defaultAlias + ':' + value; + } + } + + // end: normalize xsi:type ns attribute value + + attrs[name] = value; + } + + + // handle deferred, possibly namespaced attributes + if (maybeNS) { + + // normalize captured attributes + for (i = 0, l = attrList.length; i < l; i++) { + + name = attrList[i++]; + value = attrList[i]; + + w = name.indexOf(':'); + + if (w !== -1) { + + // normalize ns attribute name + if (!(nsName = nsMatrix[name.substring(0, w)])) { + handleWarning(missingNamespaceForPrefix(name.substring(0, w))); + continue; + } + + name = defaultAlias === nsName + ? name.substr(w + 1) + : nsName + name.substr(w); + + // end: normalize ns attribute name + + // normalize xsi:type ns attribute value + if (name === XSI_TYPE$1) { + w = value.indexOf(':'); + + if (w !== -1) { + nsName = value.substring(0, w); + + // handle default prefixes, i.e. xs:String gracefully + nsName = nsMatrix[nsName] || nsName; + value = nsName + value.substring(w); + } else { + value = defaultAlias + ':' + value; + } + } + + // end: normalize xsi:type ns attribute value + } + + attrs[name] = value; + } + + // end: normalize captured attributes + } + + return cachedAttrs = attrs; + } + + /** + * Extract the parse context { line, column, part } + * from the current parser position. + * + * @return {Object} parse context + */ + function getParseContext() { + var splitsRe = /(\r\n|\r|\n)/g; + + var line = 0; + var column = 0; + var startOfLine = 0; + var endOfLine = j; + var match; + var data; + + while (i >= startOfLine) { + + match = splitsRe.exec(xml); + + if (!match) { + break; + } + + // end of line = (break idx + break chars) + endOfLine = match[0].length + match.index; + + if (endOfLine > i) { + break; + } + + // advance to next line + line += 1; + + startOfLine = endOfLine; + } + + // EOF errors + if (i == -1) { + column = endOfLine; + data = xml.substring(j); + } else + + // start errors + if (j === 0) { + data = xml.substring(j, i); + } + + // other errors + else { + column = i - startOfLine; + data = (j == -1 ? xml.substring(i) : xml.substring(i, j + 1)); + } + + return { + 'data': data, + 'line': line, + 'column': column + }; + } + + getContext = getParseContext; + + + if (proxy) { + elementProxy = Object.create({}, { + 'name': getter(function() { + return elementName; + }), + 'originalName': getter(function() { + return _elementName; + }), + 'attrs': getter(getAttrs), + 'ns': getter(function() { + return nsMatrix; + }) + }); + } + + // actual parse logic + while (j !== -1) { + + if (xml.charCodeAt(j) === 60) { // "<" + i = j; + } else { + i = xml.indexOf('<', j); + } + + // parse end + if (i === -1) { + if (nodeStack.length) { + return handleError('unexpected end of file'); + } + + if (j === 0) { + return handleError('missing start tag'); + } + + if (j < xml.length) { + if (xml.substring(j).trim()) { + handleWarning(NON_WHITESPACE_OUTSIDE_ROOT_NODE); + } + } + + return; + } + + // parse text + if (j !== i) { + + if (nodeStack.length) { + if (onText) { + onText(xml.substring(j, i), decodeEntities, getContext); + + if (parseStop) { + return; + } + } + } else { + if (xml.substring(j, i).trim()) { + handleWarning(NON_WHITESPACE_OUTSIDE_ROOT_NODE); + + if (parseStop) { + return; + } + } + } + } + + w = xml.charCodeAt(i+1); + + // parse comments + CDATA + if (w === 33) { // "!" + q = xml.charCodeAt(i+2); + + // CDATA section + if (q === 91 && xml.substr(i + 3, 6) === 'CDATA[') { // 91 == "[" + j = xml.indexOf(']]>', i); + if (j === -1) { + return handleError('unclosed cdata'); + } + + if (onCDATA) { + onCDATA(xml.substring(i + 9, j), getContext); + if (parseStop) { + return; + } + } + + j += 3; + continue; + } + + // comment + if (q === 45 && xml.charCodeAt(i + 3) === 45) { // 45 == "-" + j = xml.indexOf('-->', i); + if (j === -1) { + return handleError('unclosed comment'); + } + + + if (onComment) { + onComment(xml.substring(i + 4, j), decodeEntities, getContext); + if (parseStop) { + return; + } + } + + j += 3; + continue; + } + } + + // parse question + if (w === 63) { // "?" + j = xml.indexOf('?>', i); + if (j === -1) { + return handleError('unclosed question'); + } + + if (onQuestion) { + onQuestion(xml.substring(i, j + 2), getContext); + if (parseStop) { + return; + } + } + + j += 2; + continue; + } + + // find matching closing tag for attention or standard tags + // for that we must skip through attribute values + // (enclosed in single or double quotes) + for (x = i + 1; ; x++) { + v = xml.charCodeAt(x); + if (isNaN(v)) { + j = -1; + return handleError('unclosed tag'); + } + + // [10] AttValue ::= '"' ([^<&"] | Reference)* '"' | "'" ([^<&'] | Reference)* "'" + // skips the quoted string + // (double quotes) does not appear in a literal enclosed by (double quotes) + // (single quote) does not appear in a literal enclosed by (single quote) + if (v === 34) { // '"' + q = xml.indexOf('"', x + 1); + x = q !== -1 ? q : x; + } else if (v === 39) { // "'" + q = xml.indexOf("'", x + 1); + x = q !== -1 ? q : x; + } else if (v === 62) { // '>' + j = x; + break; + } + } + + + // parse attention + // previously comment and CDATA have already been parsed + if (w === 33) { // "!" + + if (onAttention) { + onAttention(xml.substring(i, j + 1), decodeEntities, getContext); + if (parseStop) { + return; + } + } + + j += 1; + continue; + } + + // don't process attributes; + // there are none + cachedAttrs = {}; + + // if (xml.charCodeAt(i+1) === 47) { // close tag match + x = elementName = nodeStack.pop(); + q = i + 2 + x.length; + + if (xml.substring(i + 2, q) !== x) { + return handleError('closing tag mismatch'); + } + + // verify chars in close tag + for (; q < j; q++) { + w = xml.charCodeAt(q); + + if (w === 32 || (w > 8 && w < 14)) { // \f\n\r\t\v space + continue; + } + + return handleError('close tag'); + } + + } else { + if (xml.charCodeAt(j - 1) === 47) { // .../> + x = elementName = xml.substring(i + 1, j - 1); + + tagStart = true; + tagEnd = true; + + } else { + x = elementName = xml.substring(i + 1, j); + + tagStart = true; + tagEnd = false; + } + + if (!(w > 96 && w < 123 || w > 64 && w < 91 || w === 95 || w === 58)) { // char 95"_" 58":" + return handleError('illegal first char nodeName'); + } + + for (q = 1, y = x.length; q < y; q++) { + w = x.charCodeAt(q); + + if (w > 96 && w < 123 || w > 64 && w < 91 || w > 47 && w < 59 || w === 45 || w === 95 || w == 46) { + continue; + } + + if (w === 32 || (w < 14 && w > 8)) { // \f\n\r\t\v space + elementName = x.substring(0, q); + + // maybe there are attributes + cachedAttrs = null; + break; + } + + return handleError('invalid nodeName'); + } + + if (!tagEnd) { + nodeStack.push(elementName); + } + } + + if (isNamespace) { + + _nsMatrix = nsMatrix; + + if (tagStart) { + + // remember old namespace + // unless we're self-closing + if (!tagEnd) { + nsMatrixStack.push(_nsMatrix); + } + + if (cachedAttrs === null) { + + // quick check, whether there may be namespace + // declarations on the node; if that is the case + // we need to eagerly parse the node attributes + if ((maybeNS = x.indexOf('xmlns', q) !== -1)) { + attrsStart = q; + attrsString = x; + + getAttrs(); + + maybeNS = false; + } + } + } + + _elementName = elementName; + + w = elementName.indexOf(':'); + if (w !== -1) { + xmlns = nsMatrix[elementName.substring(0, w)]; + + // prefix given; namespace must exist + if (!xmlns) { + return handleError('missing namespace on <' + _elementName + '>'); + } + + elementName = elementName.substr(w + 1); + } else { + xmlns = nsMatrix['xmlns']; + + // if no default namespace is defined, + // we'll import the element as anonymous. + // + // it is up to users to correct that to the document defined + // targetNamespace, or whatever their undersanding of the + // XML spec mandates. + } + + // adjust namespace prefixs as configured + if (xmlns) { + elementName = xmlns + ':' + elementName; + } + + } + + if (tagStart) { + attrsStart = q; + attrsString = x; + + if (onOpenTag) { + if (proxy) { + onOpenTag(elementProxy, decodeEntities, tagEnd, getContext); + } else { + onOpenTag(elementName, getAttrs, decodeEntities, tagEnd, getContext); + } + + if (parseStop) { + return; + } + } + + } + + if (tagEnd) { + + if (onCloseTag) { + onCloseTag(proxy ? elementProxy : elementName, decodeEntities, tagStart, getContext); + + if (parseStop) { + return; + } + } + + // restore old namespace + if (isNamespace) { + if (!tagStart) { + nsMatrix = nsMatrixStack.pop(); + } else { + nsMatrix = _nsMatrix; + } + } + } + + j += 1; + } + } /** end parse */ + + } + + function hasLowerCaseAlias(pkg) { + return pkg.xml && pkg.xml.tagAlias === 'lowerCase'; + } + + var DEFAULT_NS_MAP = { + 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', + 'xml': 'http://www.w3.org/XML/1998/namespace' + }; + + var XSI_TYPE = 'xsi:type'; + + function serializeFormat(element) { + return element.xml && element.xml.serialize; + } + + function serializeAsType(element) { + return serializeFormat(element) === XSI_TYPE; + } + + function serializeAsProperty(element) { + return serializeFormat(element) === 'property'; + } + + function capitalize(str) { + return str.charAt(0).toUpperCase() + str.slice(1); + } + + function aliasToName(aliasNs, pkg) { + + if (!hasLowerCaseAlias(pkg)) { + return aliasNs.name; + } + + return aliasNs.prefix + ':' + capitalize(aliasNs.localName); + } + + function prefixedToName(nameNs, pkg) { + + var name = nameNs.name, + localName = nameNs.localName; + + var typePrefix = pkg.xml && pkg.xml.typePrefix; + + if (typePrefix && localName.indexOf(typePrefix) === 0) { + return nameNs.prefix + ':' + localName.slice(typePrefix.length); + } else { + return name; + } + } + + function normalizeXsiTypeName(name, model) { + + var nameNs = parseName(name); + var pkg = model.getPackage(nameNs.prefix); + + return prefixedToName(nameNs, pkg); + } + + function error$1(message) { + return new Error(message); + } + + /** + * Get the moddle descriptor for a given instance or type. + * + * @param {ModdleElement|Function} element + * + * @return {Object} the moddle descriptor + */ + function getModdleDescriptor(element) { + return element.$descriptor; + } + + + /** + * A parse context. + * + * @class + * + * @param {Object} options + * @param {ElementHandler} options.rootHandler the root handler for parsing a document + * @param {boolean} [options.lax=false] whether or not to ignore invalid elements + */ + function Context(options) { + + /** + * @property {ElementHandler} rootHandler + */ + + /** + * @property {Boolean} lax + */ + + assign(this, options); + + this.elementsById = {}; + this.references = []; + this.warnings = []; + + /** + * Add an unresolved reference. + * + * @param {Object} reference + */ + this.addReference = function(reference) { + this.references.push(reference); + }; + + /** + * Add a processed element. + * + * @param {ModdleElement} element + */ + this.addElement = function(element) { + + if (!element) { + throw error$1('expected element'); + } + + var elementsById = this.elementsById; + + var descriptor = getModdleDescriptor(element); + + var idProperty = descriptor.idProperty, + id; + + if (idProperty) { + id = element.get(idProperty.name); + + if (id) { + + // for QName validation as per http://www.w3.org/TR/REC-xml/#NT-NameChar + if (!/^([a-z][\w-.]*:)?[a-z_][\w-.]*$/i.test(id)) { + throw new Error('illegal ID <' + id + '>'); + } + + if (elementsById[id]) { + throw error$1('duplicate ID <' + id + '>'); + } + + elementsById[id] = element; + } + } + }; + + /** + * Add an import warning. + * + * @param {Object} warning + * @param {String} warning.message + * @param {Error} [warning.error] + */ + this.addWarning = function(warning) { + this.warnings.push(warning); + }; + } + + function BaseHandler() {} + + BaseHandler.prototype.handleEnd = function() {}; + BaseHandler.prototype.handleText = function() {}; + BaseHandler.prototype.handleNode = function() {}; + + + /** + * A simple pass through handler that does nothing except for + * ignoring all input it receives. + * + * This is used to ignore unknown elements and + * attributes. + */ + function NoopHandler() { } + + NoopHandler.prototype = Object.create(BaseHandler.prototype); + + NoopHandler.prototype.handleNode = function() { + return this; + }; + + function BodyHandler() {} + + BodyHandler.prototype = Object.create(BaseHandler.prototype); + + BodyHandler.prototype.handleText = function(text) { + this.body = (this.body || '') + text; + }; + + function ReferenceHandler(property, context) { + this.property = property; + this.context = context; + } + + ReferenceHandler.prototype = Object.create(BodyHandler.prototype); + + ReferenceHandler.prototype.handleNode = function(node) { + + if (this.element) { + throw error$1('expected no sub nodes'); + } else { + this.element = this.createReference(node); + } + + return this; + }; + + ReferenceHandler.prototype.handleEnd = function() { + this.element.id = this.body; + }; + + ReferenceHandler.prototype.createReference = function(node) { + return { + property: this.property.ns.name, + id: '' + }; + }; + + function ValueHandler(propertyDesc, element) { + this.element = element; + this.propertyDesc = propertyDesc; + } + + ValueHandler.prototype = Object.create(BodyHandler.prototype); + + ValueHandler.prototype.handleEnd = function() { + + var value = this.body || '', + element = this.element, + propertyDesc = this.propertyDesc; + + value = coerceType(propertyDesc.type, value); + + if (propertyDesc.isMany) { + element.get(propertyDesc.name).push(value); + } else { + element.set(propertyDesc.name, value); + } + }; + + + function BaseElementHandler() {} + + BaseElementHandler.prototype = Object.create(BodyHandler.prototype); + + BaseElementHandler.prototype.handleNode = function(node) { + var parser = this, + element = this.element; + + if (!element) { + element = this.element = this.createElement(node); + + this.context.addElement(element); + } else { + parser = this.handleChild(node); + } + + return parser; + }; + + /** + * @class Reader.ElementHandler + * + */ + function ElementHandler(model, typeName, context) { + this.model = model; + this.type = model.getType(typeName); + this.context = context; + } + + ElementHandler.prototype = Object.create(BaseElementHandler.prototype); + + ElementHandler.prototype.addReference = function(reference) { + this.context.addReference(reference); + }; + + ElementHandler.prototype.handleText = function(text) { + + var element = this.element, + descriptor = getModdleDescriptor(element), + bodyProperty = descriptor.bodyProperty; + + if (!bodyProperty) { + throw error$1('unexpected body text <' + text + '>'); + } + + BodyHandler.prototype.handleText.call(this, text); + }; + + ElementHandler.prototype.handleEnd = function() { + + var value = this.body, + element = this.element, + descriptor = getModdleDescriptor(element), + bodyProperty = descriptor.bodyProperty; + + if (bodyProperty && value !== undefined) { + value = coerceType(bodyProperty.type, value); + element.set(bodyProperty.name, value); + } + }; + + /** + * Create an instance of the model from the given node. + * + * @param {Element} node the xml node + */ + ElementHandler.prototype.createElement = function(node) { + var attributes = node.attributes, + Type = this.type, + descriptor = getModdleDescriptor(Type), + context = this.context, + instance = new Type({}), + model = this.model, + propNameNs; + + forEach$1(attributes, function(value, name) { + + var prop = descriptor.propertiesByName[name], + values; + + if (prop && prop.isReference) { + + if (!prop.isMany) { + context.addReference({ + element: instance, + property: prop.ns.name, + id: value + }); + } else { + + // IDREFS: parse references as whitespace-separated list + values = value.split(' '); + + forEach$1(values, function(v) { + context.addReference({ + element: instance, + property: prop.ns.name, + id: v + }); + }); + } + + } else { + if (prop) { + value = coerceType(prop.type, value); + } else + if (name !== 'xmlns') { + propNameNs = parseName(name, descriptor.ns.prefix); + + // check whether attribute is defined in a well-known namespace + // if that is the case we emit a warning to indicate potential misuse + if (model.getPackage(propNameNs.prefix)) { + + context.addWarning({ + message: 'unknown attribute <' + name + '>', + element: instance, + property: name, + value: value + }); + } + } + + instance.set(name, value); + } + }); + + return instance; + }; + + ElementHandler.prototype.getPropertyForNode = function(node) { + + var name = node.name; + var nameNs = parseName(name); + + var type = this.type, + model = this.model, + descriptor = getModdleDescriptor(type); + + var propertyName = nameNs.name, + property = descriptor.propertiesByName[propertyName], + elementTypeName, + elementType; + + // search for properties by name first + + if (property && !property.isAttr) { + + if (serializeAsType(property)) { + elementTypeName = node.attributes[XSI_TYPE]; + + // xsi type is optional, if it does not exists the + // default type is assumed + if (elementTypeName) { + + // take possible type prefixes from XML + // into account, i.e.: xsi:type="t{ActualType}" + elementTypeName = normalizeXsiTypeName(elementTypeName, model); + + elementType = model.getType(elementTypeName); + + return assign({}, property, { + effectiveType: getModdleDescriptor(elementType).name + }); + } + } + + // search for properties by name first + return property; + } + + var pkg = model.getPackage(nameNs.prefix); + + if (pkg) { + elementTypeName = aliasToName(nameNs, pkg); + elementType = model.getType(elementTypeName); + + // search for collection members later + property = find(descriptor.properties, function(p) { + return !p.isVirtual && !p.isReference && !p.isAttribute && elementType.hasType(p.type); + }); + + if (property) { + return assign({}, property, { + effectiveType: getModdleDescriptor(elementType).name + }); + } + } else { + + // parse unknown element (maybe extension) + property = find(descriptor.properties, function(p) { + return !p.isReference && !p.isAttribute && p.type === 'Element'; + }); + + if (property) { + return property; + } + } + + throw error$1('unrecognized element <' + nameNs.name + '>'); + }; + + ElementHandler.prototype.toString = function() { + return 'ElementDescriptor[' + getModdleDescriptor(this.type).name + ']'; + }; + + ElementHandler.prototype.valueHandler = function(propertyDesc, element) { + return new ValueHandler(propertyDesc, element); + }; + + ElementHandler.prototype.referenceHandler = function(propertyDesc) { + return new ReferenceHandler(propertyDesc, this.context); + }; + + ElementHandler.prototype.handler = function(type) { + if (type === 'Element') { + return new GenericElementHandler(this.model, type, this.context); + } else { + return new ElementHandler(this.model, type, this.context); + } + }; + + /** + * Handle the child element parsing + * + * @param {Element} node the xml node + */ + ElementHandler.prototype.handleChild = function(node) { + var propertyDesc, type, element, childHandler; + + propertyDesc = this.getPropertyForNode(node); + element = this.element; + + type = propertyDesc.effectiveType || propertyDesc.type; + + if (isSimple(type)) { + return this.valueHandler(propertyDesc, element); + } + + if (propertyDesc.isReference) { + childHandler = this.referenceHandler(propertyDesc).handleNode(node); + } else { + childHandler = this.handler(type).handleNode(node); + } + + var newElement = childHandler.element; + + // child handles may decide to skip elements + // by not returning anything + if (newElement !== undefined) { + + if (propertyDesc.isMany) { + element.get(propertyDesc.name).push(newElement); + } else { + element.set(propertyDesc.name, newElement); + } + + if (propertyDesc.isReference) { + assign(newElement, { + element: element + }); + + this.context.addReference(newElement); + } else { + + // establish child -> parent relationship + newElement.$parent = element; + } + } + + return childHandler; + }; + + /** + * An element handler that performs special validation + * to ensure the node it gets initialized with matches + * the handlers type (namespace wise). + * + * @param {Moddle} model + * @param {String} typeName + * @param {Context} context + */ + function RootElementHandler(model, typeName, context) { + ElementHandler.call(this, model, typeName, context); + } + + RootElementHandler.prototype = Object.create(ElementHandler.prototype); + + RootElementHandler.prototype.createElement = function(node) { + + var name = node.name, + nameNs = parseName(name), + model = this.model, + type = this.type, + pkg = model.getPackage(nameNs.prefix), + typeName = pkg && aliasToName(nameNs, pkg) || name; + + // verify the correct namespace if we parse + // the first element in the handler tree + // + // this ensures we don't mistakenly import wrong namespace elements + if (!type.hasType(typeName)) { + throw error$1('unexpected element <' + node.originalName + '>'); + } + + return ElementHandler.prototype.createElement.call(this, node); + }; + + + function GenericElementHandler(model, typeName, context) { + this.model = model; + this.context = context; + } + + GenericElementHandler.prototype = Object.create(BaseElementHandler.prototype); + + GenericElementHandler.prototype.createElement = function(node) { + + var name = node.name, + ns = parseName(name), + prefix = ns.prefix, + uri = node.ns[prefix + '$uri'], + attributes = node.attributes; + + return this.model.createAny(name, uri, attributes); + }; + + GenericElementHandler.prototype.handleChild = function(node) { + + var handler = new GenericElementHandler(this.model, 'Element', this.context).handleNode(node), + element = this.element; + + var newElement = handler.element, + children; + + if (newElement !== undefined) { + children = element.$children = element.$children || []; + children.push(newElement); + + // establish child -> parent relationship + newElement.$parent = element; + } + + return handler; + }; + + GenericElementHandler.prototype.handleEnd = function() { + if (this.body) { + this.element.$body = this.body; + } + }; + + /** + * A reader for a meta-model + * + * @param {Object} options + * @param {Model} options.model used to read xml files + * @param {Boolean} options.lax whether to make parse errors warnings + */ + function Reader(options) { + + if (options instanceof Moddle) { + options = { + model: options + }; + } + + assign(this, { lax: false }, options); + } + + /** + * The fromXML result. + * + * @typedef {Object} ParseResult + * + * @property {ModdleElement} rootElement + * @property {Array} references + * @property {Array} warnings + * @property {Object} elementsById - a mapping containing each ID -> ModdleElement + */ + + /** + * The fromXML result. + * + * @typedef {Error} ParseError + * + * @property {Array} warnings + */ + + /** + * Parse the given XML into a moddle document tree. + * + * @param {String} xml + * @param {ElementHandler|Object} options or rootHandler + * + * @returns {Promise} + */ + Reader.prototype.fromXML = function(xml, options, done) { + + var rootHandler = options.rootHandler; + + if (options instanceof ElementHandler) { + + // root handler passed via (xml, { rootHandler: ElementHandler }, ...) + rootHandler = options; + options = {}; + } else { + if (typeof options === 'string') { + + // rootHandler passed via (xml, 'someString', ...) + rootHandler = this.handler(options); + options = {}; + } else if (typeof rootHandler === 'string') { + + // rootHandler passed via (xml, { rootHandler: 'someString' }, ...) + rootHandler = this.handler(rootHandler); + } + } + + var model = this.model, + lax = this.lax; + + var context = new Context(assign({}, options, { rootHandler: rootHandler })), + parser = new Parser({ proxy: true }), + stack = createStack(); + + rootHandler.context = context; + + // push root handler + stack.push(rootHandler); + + + /** + * Handle error. + * + * @param {Error} err + * @param {Function} getContext + * @param {boolean} lax + * + * @return {boolean} true if handled + */ + function handleError(err, getContext, lax) { + + var ctx = getContext(); + + var line = ctx.line, + column = ctx.column, + data = ctx.data; + + // we receive the full context data here, + // for elements trim down the information + // to the tag name, only + if (data.charAt(0) === '<' && data.indexOf(' ') !== -1) { + data = data.slice(0, data.indexOf(' ')) + '>'; + } + + var message = + 'unparsable content ' + (data ? data + ' ' : '') + 'detected\n\t' + + 'line: ' + line + '\n\t' + + 'column: ' + column + '\n\t' + + 'nested error: ' + err.message; + + if (lax) { + context.addWarning({ + message: message, + error: err + }); + + return true; + } else { + throw error$1(message); + } + } + + function handleWarning(err, getContext) { + + // just like handling errors in mode + return handleError(err, getContext, true); + } + + /** + * Resolve collected references on parse end. + */ + function resolveReferences() { + + var elementsById = context.elementsById; + var references = context.references; + + var i, r; + + for (i = 0; (r = references[i]); i++) { + var element = r.element; + var reference = elementsById[r.id]; + var property = getModdleDescriptor(element).propertiesByName[r.property]; + + if (!reference) { + context.addWarning({ + message: 'unresolved reference <' + r.id + '>', + element: r.element, + property: r.property, + value: r.id + }); + } + + if (property.isMany) { + var collection = element.get(property.name), + idx = collection.indexOf(r); + + // we replace an existing place holder (idx != -1) or + // append to the collection instead + if (idx === -1) { + idx = collection.length; + } + + if (!reference) { + + // remove unresolvable reference + collection.splice(idx, 1); + } else { + + // add or update reference in collection + collection[idx] = reference; + } + } else { + element.set(property.name, reference); + } + } + } + + function handleClose() { + stack.pop().handleEnd(); + } + + var PREAMBLE_START_PATTERN = /^<\?xml /i; + + var ENCODING_PATTERN = / encoding="([^"]+)"/i; + + var UTF_8_PATTERN = /^utf-8$/i; + + function handleQuestion(question) { + + if (!PREAMBLE_START_PATTERN.test(question)) { + return; + } + + var match = ENCODING_PATTERN.exec(question); + var encoding = match && match[1]; + + if (!encoding || UTF_8_PATTERN.test(encoding)) { + return; + } + + context.addWarning({ + message: + 'unsupported document encoding <' + encoding + '>, ' + + 'falling back to UTF-8' + }); + } + + function handleOpen(node, getContext) { + var handler = stack.peek(); + + try { + stack.push(handler.handleNode(node)); + } catch (err) { + + if (handleError(err, getContext, lax)) { + stack.push(new NoopHandler()); + } + } + } + + function handleCData(text, getContext) { + + try { + stack.peek().handleText(text); + } catch (err) { + handleWarning(err, getContext); + } + } + + function handleText(text, getContext) { + + // strip whitespace only nodes, i.e. before + // sections and in between tags + + if (!text.trim()) { + return; + } + + handleCData(text, getContext); + } + + var uriMap = model.getPackages().reduce(function(uriMap, p) { + uriMap[p.uri] = p.prefix; + + return uriMap; + }, { + 'http://www.w3.org/XML/1998/namespace': 'xml' // add default xml ns + }); + parser + .ns(uriMap) + .on('openTag', function(obj, decodeStr, selfClosing, getContext) { + + // gracefully handle unparsable attributes (attrs=false) + var attrs = obj.attrs || {}; + + var decodedAttrs = Object.keys(attrs).reduce(function(d, key) { + var value = decodeStr(attrs[key]); + + d[key] = value; + + return d; + }, {}); + + var node = { + name: obj.name, + originalName: obj.originalName, + attributes: decodedAttrs, + ns: obj.ns + }; + + handleOpen(node, getContext); + }) + .on('question', handleQuestion) + .on('closeTag', handleClose) + .on('cdata', handleCData) + .on('text', function(text, decodeEntities, getContext) { + handleText(decodeEntities(text), getContext); + }) + .on('error', handleError) + .on('warn', handleWarning); + + // async XML parsing to make sure the execution environment + // (node or brower) is kept responsive and that certain optimization + // strategies can kick in. + return new Promise(function(resolve, reject) { + + var err; + + try { + parser.parse(xml); + + resolveReferences(); + } catch (e) { + err = e; + } + + var rootElement = rootHandler.element; + + if (!err && !rootElement) { + err = error$1('failed to parse document as <' + rootHandler.type.$descriptor.name + '>'); + } + + var warnings = context.warnings; + var references = context.references; + var elementsById = context.elementsById; + + if (err) { + err.warnings = warnings; + + return reject(err); + } else { + return resolve({ + rootElement: rootElement, + elementsById: elementsById, + references: references, + warnings: warnings + }); + } + }); + }; + + Reader.prototype.handler = function(name) { + return new RootElementHandler(this.model, name); + }; + + + // helpers ////////////////////////// + + function createStack() { + var stack = []; + + Object.defineProperty(stack, 'peek', { + value: function() { + return this[this.length - 1]; + } + }); + + return stack; + } + + var XML_PREAMBLE = '\n'; + + var ESCAPE_ATTR_CHARS = /<|>|'|"|&|\n\r|\n/g; + var ESCAPE_CHARS = /<|>|&/g; + + + function Namespaces(parent) { + + var prefixMap = {}; + var uriMap = {}; + var used = {}; + + var wellknown = []; + var custom = []; + + // API + + this.byUri = function(uri) { + return uriMap[uri] || ( + parent && parent.byUri(uri) + ); + }; + + this.add = function(ns, isWellknown) { + + uriMap[ns.uri] = ns; + + if (isWellknown) { + wellknown.push(ns); + } else { + custom.push(ns); + } + + this.mapPrefix(ns.prefix, ns.uri); + }; + + this.uriByPrefix = function(prefix) { + return prefixMap[prefix || 'xmlns']; + }; + + this.mapPrefix = function(prefix, uri) { + prefixMap[prefix || 'xmlns'] = uri; + }; + + this.getNSKey = function(ns) { + return (ns.prefix !== undefined) ? (ns.uri + '|' + ns.prefix) : ns.uri; + }; + + this.logUsed = function(ns) { + + var uri = ns.uri; + var nsKey = this.getNSKey(ns); + + used[nsKey] = this.byUri(uri); + + // Inform parent recursively about the usage of this NS + if (parent) { + parent.logUsed(ns); + } + }; + + this.getUsed = function(ns) { + + function isUsed(ns) { + var nsKey = self.getNSKey(ns); + + return used[nsKey]; + } + + var self = this; + + var allNs = [].concat(wellknown, custom); + + return allNs.filter(isUsed); + }; + + } + + function lower(string) { + return string.charAt(0).toLowerCase() + string.slice(1); + } + + function nameToAlias(name, pkg) { + if (hasLowerCaseAlias(pkg)) { + return lower(name); + } else { + return name; + } + } + + function inherits(ctor, superCtor) { + ctor.super_ = superCtor; + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + } + + function nsName(ns) { + if (isString(ns)) { + return ns; + } else { + return (ns.prefix ? ns.prefix + ':' : '') + ns.localName; + } + } + + function getNsAttrs(namespaces) { + + return namespaces.getUsed().filter(function(ns) { + + // do not serialize built in namespace + return ns.prefix !== 'xml'; + }).map(function(ns) { + var name = 'xmlns' + (ns.prefix ? ':' + ns.prefix : ''); + return { name: name, value: ns.uri }; + }); + + } + + function getElementNs(ns, descriptor) { + if (descriptor.isGeneric) { + return assign({ localName: descriptor.ns.localName }, ns); + } else { + return assign({ localName: nameToAlias(descriptor.ns.localName, descriptor.$pkg) }, ns); + } + } + + function getPropertyNs(ns, descriptor) { + return assign({ localName: descriptor.ns.localName }, ns); + } + + function getSerializableProperties(element) { + var descriptor = element.$descriptor; + + return filter(descriptor.properties, function(p) { + var name = p.name; + + if (p.isVirtual) { + return false; + } + + // do not serialize defaults + if (!has$1(element, name)) { + return false; + } + + var value = element[name]; + + // do not serialize default equals + if (value === p.default) { + return false; + } + + // do not serialize null properties + if (value === null) { + return false; + } + + return p.isMany ? value.length : true; + }); + } + + var ESCAPE_ATTR_MAP = { + '\n': '#10', + '\n\r': '#10', + '"': '#34', + '\'': '#39', + '<': '#60', + '>': '#62', + '&': '#38' + }; + + var ESCAPE_MAP = { + '<': 'lt', + '>': 'gt', + '&': 'amp' + }; + + function escape(str, charPattern, replaceMap) { + + // ensure we are handling strings here + str = isString(str) ? str : '' + str; + + return str.replace(charPattern, function(s) { + return '&' + replaceMap[s] + ';'; + }); + } + + /** + * Escape a string attribute to not contain any bad values (line breaks, '"', ...) + * + * @param {String} str the string to escape + * @return {String} the escaped string + */ + function escapeAttr(str) { + return escape(str, ESCAPE_ATTR_CHARS, ESCAPE_ATTR_MAP); + } + + function escapeBody(str) { + return escape(str, ESCAPE_CHARS, ESCAPE_MAP); + } + + function filterAttributes(props) { + return filter(props, function(p) { return p.isAttr; }); + } + + function filterContained(props) { + return filter(props, function(p) { return !p.isAttr; }); + } + + + function ReferenceSerializer(tagName) { + this.tagName = tagName; + } + + ReferenceSerializer.prototype.build = function(element) { + this.element = element; + return this; + }; + + ReferenceSerializer.prototype.serializeTo = function(writer) { + writer + .appendIndent() + .append('<' + this.tagName + '>' + this.element.id + '') + .appendNewLine(); + }; + + function BodySerializer() {} + + BodySerializer.prototype.serializeValue = + BodySerializer.prototype.serializeTo = function(writer) { + writer.append( + this.escape + ? escapeBody(this.value) + : this.value + ); + }; + + BodySerializer.prototype.build = function(prop, value) { + this.value = value; + + if (prop.type === 'String' && value.search(ESCAPE_CHARS) !== -1) { + this.escape = true; + } + + return this; + }; + + function ValueSerializer(tagName) { + this.tagName = tagName; + } + + inherits(ValueSerializer, BodySerializer); + + ValueSerializer.prototype.serializeTo = function(writer) { + + writer + .appendIndent() + .append('<' + this.tagName + '>'); + + this.serializeValue(writer); + + writer + .append('') + .appendNewLine(); + }; + + function ElementSerializer(parent, propertyDescriptor) { + this.body = []; + this.attrs = []; + + this.parent = parent; + this.propertyDescriptor = propertyDescriptor; + } + + ElementSerializer.prototype.build = function(element) { + this.element = element; + + var elementDescriptor = element.$descriptor, + propertyDescriptor = this.propertyDescriptor; + + var otherAttrs, + properties; + + var isGeneric = elementDescriptor.isGeneric; + + if (isGeneric) { + otherAttrs = this.parseGeneric(element); + } else { + otherAttrs = this.parseNsAttributes(element); + } + + if (propertyDescriptor) { + this.ns = this.nsPropertyTagName(propertyDescriptor); + } else { + this.ns = this.nsTagName(elementDescriptor); + } + + // compute tag name + this.tagName = this.addTagName(this.ns); + + if (!isGeneric) { + properties = getSerializableProperties(element); + + this.parseAttributes(filterAttributes(properties)); + this.parseContainments(filterContained(properties)); + } + + this.parseGenericAttributes(element, otherAttrs); + + return this; + }; + + ElementSerializer.prototype.nsTagName = function(descriptor) { + var effectiveNs = this.logNamespaceUsed(descriptor.ns); + return getElementNs(effectiveNs, descriptor); + }; + + ElementSerializer.prototype.nsPropertyTagName = function(descriptor) { + var effectiveNs = this.logNamespaceUsed(descriptor.ns); + return getPropertyNs(effectiveNs, descriptor); + }; + + ElementSerializer.prototype.isLocalNs = function(ns) { + return ns.uri === this.ns.uri; + }; + + /** + * Get the actual ns attribute name for the given element. + * + * @param {Object} element + * @param {Boolean} [element.inherited=false] + * + * @return {Object} nsName + */ + ElementSerializer.prototype.nsAttributeName = function(element) { + + var ns; + + if (isString(element)) { + ns = parseName(element); + } else { + ns = element.ns; + } + + // return just local name for inherited attributes + if (element.inherited) { + return { localName: ns.localName }; + } + + // parse + log effective ns + var effectiveNs = this.logNamespaceUsed(ns); + + // LOG ACTUAL namespace use + this.getNamespaces().logUsed(effectiveNs); + + // strip prefix if same namespace like parent + if (this.isLocalNs(effectiveNs)) { + return { localName: ns.localName }; + } else { + return assign({ localName: ns.localName }, effectiveNs); + } + }; + + ElementSerializer.prototype.parseGeneric = function(element) { + + var self = this, + body = this.body; + + var attributes = []; + + forEach$1(element, function(val, key) { + + var nonNsAttr; + + if (key === '$body') { + body.push(new BodySerializer().build({ type: 'String' }, val)); + } else + if (key === '$children') { + forEach$1(val, function(child) { + body.push(new ElementSerializer(self).build(child)); + }); + } else + if (key.indexOf('$') !== 0) { + nonNsAttr = self.parseNsAttribute(element, key, val); + + if (nonNsAttr) { + attributes.push({ name: key, value: val }); + } + } + }); + + return attributes; + }; + + ElementSerializer.prototype.parseNsAttribute = function(element, name, value) { + var model = element.$model; + + var nameNs = parseName(name); + + var ns; + + // parse xmlns:foo="http://foo.bar" + if (nameNs.prefix === 'xmlns') { + ns = { prefix: nameNs.localName, uri: value }; + } + + // parse xmlns="http://foo.bar" + if (!nameNs.prefix && nameNs.localName === 'xmlns') { + ns = { uri: value }; + } + + if (!ns) { + return { + name: name, + value: value + }; + } + + if (model && model.getPackage(value)) { + + // register well known namespace + this.logNamespace(ns, true, true); + } else { + + // log custom namespace directly as used + var actualNs = this.logNamespaceUsed(ns, true); + + this.getNamespaces().logUsed(actualNs); + } + }; + + + /** + * Parse namespaces and return a list of left over generic attributes + * + * @param {Object} element + * @return {Array} + */ + ElementSerializer.prototype.parseNsAttributes = function(element, attrs) { + var self = this; + + var genericAttrs = element.$attrs; + + var attributes = []; + + // parse namespace attributes first + // and log them. push non namespace attributes to a list + // and process them later + forEach$1(genericAttrs, function(value, name) { + + var nonNsAttr = self.parseNsAttribute(element, name, value); + + if (nonNsAttr) { + attributes.push(nonNsAttr); + } + }); + + return attributes; + }; + + ElementSerializer.prototype.parseGenericAttributes = function(element, attributes) { + + var self = this; + + forEach$1(attributes, function(attr) { + + // do not serialize xsi:type attribute + // it is set manually based on the actual implementation type + if (attr.name === XSI_TYPE) { + return; + } + + try { + self.addAttribute(self.nsAttributeName(attr.name), attr.value); + } catch (e) { + console.warn( + 'missing namespace information for ', + attr.name, '=', attr.value, 'on', element, + e); + } + }); + }; + + ElementSerializer.prototype.parseContainments = function(properties) { + + var self = this, + body = this.body, + element = this.element; + + forEach$1(properties, function(p) { + var value = element.get(p.name), + isReference = p.isReference, + isMany = p.isMany; + + if (!isMany) { + value = [ value ]; + } + + if (p.isBody) { + body.push(new BodySerializer().build(p, value[0])); + } else + if (isSimple(p.type)) { + forEach$1(value, function(v) { + body.push(new ValueSerializer(self.addTagName(self.nsPropertyTagName(p))).build(p, v)); + }); + } else + if (isReference) { + forEach$1(value, function(v) { + body.push(new ReferenceSerializer(self.addTagName(self.nsPropertyTagName(p))).build(v)); + }); + } else { + + // allow serialization via type + // rather than element name + var asType = serializeAsType(p), + asProperty = serializeAsProperty(p); + + forEach$1(value, function(v) { + var serializer; + + if (asType) { + serializer = new TypeSerializer(self, p); + } else + if (asProperty) { + serializer = new ElementSerializer(self, p); + } else { + serializer = new ElementSerializer(self); + } + + body.push(serializer.build(v)); + }); + } + }); + }; + + ElementSerializer.prototype.getNamespaces = function(local) { + + var namespaces = this.namespaces, + parent = this.parent, + parentNamespaces; + + if (!namespaces) { + parentNamespaces = parent && parent.getNamespaces(); + + if (local || !parentNamespaces) { + this.namespaces = namespaces = new Namespaces(parentNamespaces); + } else { + namespaces = parentNamespaces; + } + } + + return namespaces; + }; + + ElementSerializer.prototype.logNamespace = function(ns, wellknown, local) { + var namespaces = this.getNamespaces(local); + + var nsUri = ns.uri, + nsPrefix = ns.prefix; + + var existing = namespaces.byUri(nsUri); + + if (!existing || local) { + namespaces.add(ns, wellknown); + } + + namespaces.mapPrefix(nsPrefix, nsUri); + + return ns; + }; + + ElementSerializer.prototype.logNamespaceUsed = function(ns, local) { + var element = this.element, + model = element.$model, + namespaces = this.getNamespaces(local); + + // ns may be + // + // * prefix only + // * prefix:uri + // * localName only + + var prefix = ns.prefix, + uri = ns.uri, + newPrefix, idx, + wellknownUri; + + // handle anonymous namespaces (elementForm=unqualified), cf. #23 + if (!prefix && !uri) { + return { localName: ns.localName }; + } + + wellknownUri = DEFAULT_NS_MAP[prefix] || model && (model.getPackage(prefix) || {}).uri; + + uri = uri || wellknownUri || namespaces.uriByPrefix(prefix); + + if (!uri) { + throw new Error('no namespace uri given for prefix <' + prefix + '>'); + } + + ns = namespaces.byUri(uri); + + if (!ns) { + newPrefix = prefix; + idx = 1; + + // find a prefix that is not mapped yet + while (namespaces.uriByPrefix(newPrefix)) { + newPrefix = prefix + '_' + idx++; + } + + ns = this.logNamespace({ prefix: newPrefix, uri: uri }, wellknownUri === uri); + } + + if (prefix) { + namespaces.mapPrefix(prefix, uri); + } + + return ns; + }; + + ElementSerializer.prototype.parseAttributes = function(properties) { + var self = this, + element = this.element; + + forEach$1(properties, function(p) { + + var value = element.get(p.name); + + if (p.isReference) { + + if (!p.isMany) { + value = value.id; + } + else { + var values = []; + forEach$1(value, function(v) { + values.push(v.id); + }); + + // IDREFS is a whitespace-separated list of references. + value = values.join(' '); + } + + } + + self.addAttribute(self.nsAttributeName(p), value); + }); + }; + + ElementSerializer.prototype.addTagName = function(nsTagName) { + var actualNs = this.logNamespaceUsed(nsTagName); + + this.getNamespaces().logUsed(actualNs); + + return nsName(nsTagName); + }; + + ElementSerializer.prototype.addAttribute = function(name, value) { + var attrs = this.attrs; + + if (isString(value)) { + value = escapeAttr(value); + } + + // de-duplicate attributes + // https://github.com/bpmn-io/moddle-xml/issues/66 + var idx = findIndex(attrs, function(element) { + return ( + element.name.localName === name.localName && + element.name.uri === name.uri && + element.name.prefix === name.prefix + ); + }); + + var attr = { name: name, value: value }; + + if (idx !== -1) { + attrs.splice(idx, 1, attr); + } else { + attrs.push(attr); + } + }; + + ElementSerializer.prototype.serializeAttributes = function(writer) { + var attrs = this.attrs, + namespaces = this.namespaces; + + if (namespaces) { + attrs = getNsAttrs(namespaces).concat(attrs); + } + + forEach$1(attrs, function(a) { + writer + .append(' ') + .append(nsName(a.name)).append('="').append(a.value).append('"'); + }); + }; + + ElementSerializer.prototype.serializeTo = function(writer) { + var firstBody = this.body[0], + indent = firstBody && firstBody.constructor !== BodySerializer; + + writer + .appendIndent() + .append('<' + this.tagName); + + this.serializeAttributes(writer); + + writer.append(firstBody ? '>' : ' />'); + + if (firstBody) { + + if (indent) { + writer + .appendNewLine() + .indent(); + } + + forEach$1(this.body, function(b) { + b.serializeTo(writer); + }); + + if (indent) { + writer + .unindent() + .appendIndent(); + } + + writer.append(''); + } + + writer.appendNewLine(); + }; + + /** + * A serializer for types that handles serialization of data types + */ + function TypeSerializer(parent, propertyDescriptor) { + ElementSerializer.call(this, parent, propertyDescriptor); + } + + inherits(TypeSerializer, ElementSerializer); + + TypeSerializer.prototype.parseNsAttributes = function(element) { + + // extracted attributes + var attributes = ElementSerializer.prototype.parseNsAttributes.call(this, element); + + var descriptor = element.$descriptor; + + // only serialize xsi:type if necessary + if (descriptor.name === this.propertyDescriptor.type) { + return attributes; + } + + var typeNs = this.typeNs = this.nsTagName(descriptor); + this.getNamespaces().logUsed(this.typeNs); + + // add xsi:type attribute to represent the elements + // actual type + + var pkg = element.$model.getPackage(typeNs.uri), + typePrefix = (pkg.xml && pkg.xml.typePrefix) || ''; + + this.addAttribute( + this.nsAttributeName(XSI_TYPE), + (typeNs.prefix ? typeNs.prefix + ':' : '') + typePrefix + descriptor.ns.localName + ); + + return attributes; + }; + + TypeSerializer.prototype.isLocalNs = function(ns) { + return ns.uri === (this.typeNs || this.ns).uri; + }; + + function SavingWriter() { + this.value = ''; + + this.write = function(str) { + this.value += str; + }; + } + + function FormatingWriter(out, format) { + + var indent = ['']; + + this.append = function(str) { + out.write(str); + + return this; + }; + + this.appendNewLine = function() { + if (format) { + out.write('\n'); + } + + return this; + }; + + this.appendIndent = function() { + if (format) { + out.write(indent.join(' ')); + } + + return this; + }; + + this.indent = function() { + indent.push(''); + return this; + }; + + this.unindent = function() { + indent.pop(); + return this; + }; + } + + /** + * A writer for meta-model backed document trees + * + * @param {Object} options output options to pass into the writer + */ + function Writer(options) { + + options = assign({ format: false, preamble: true }, options || {}); + + function toXML(tree, writer) { + var internalWriter = writer || new SavingWriter(); + var formatingWriter = new FormatingWriter(internalWriter, options.format); + + if (options.preamble) { + formatingWriter.append(XML_PREAMBLE); + } + + new ElementSerializer().build(tree).serializeTo(formatingWriter); + + if (!writer) { + return internalWriter.value; + } + } + + return { + toXML: toXML + }; + } + + /** + * A sub class of {@link Moddle} with support for import and export of BPMN 2.0 xml files. + * + * @class BpmnModdle + * @extends Moddle + * + * @param {Object|Array} packages to use for instantiating the model + * @param {Object} [options] additional options to pass over + */ + function BpmnModdle(packages, options) { + Moddle.call(this, packages, options); + } + + BpmnModdle.prototype = Object.create(Moddle.prototype); + + /** + * The fromXML result. + * + * @typedef {Object} ParseResult + * + * @property {ModdleElement} rootElement + * @property {Array} references + * @property {Array} warnings + * @property {Object} elementsById - a mapping containing each ID -> ModdleElement + */ + + /** + * The fromXML error. + * + * @typedef {Error} ParseError + * + * @property {Array} warnings + */ + + /** + * Instantiates a BPMN model tree from a given xml string. + * + * @param {String} xmlStr + * @param {String} [typeName='bpmn:Definitions'] name of the root element + * @param {Object} [options] options to pass to the underlying reader + * + * @returns {Promise} + */ + BpmnModdle.prototype.fromXML = function(xmlStr, typeName, options) { + + if (!isString(typeName)) { + options = typeName; + typeName = 'bpmn:Definitions'; + } + + var reader = new Reader(assign({ model: this, lax: true }, options)); + var rootHandler = reader.handler(typeName); + + return reader.fromXML(xmlStr, rootHandler); + }; + + + /** + * The toXML result. + * + * @typedef {Object} SerializationResult + * + * @property {String} xml + */ + + /** + * Serializes a BPMN 2.0 object tree to XML. + * + * @param {String} element the root element, typically an instance of `bpmn:Definitions` + * @param {Object} [options] to pass to the underlying writer + * + * @returns {Promise} + */ + BpmnModdle.prototype.toXML = function(element, options) { + + var writer = new Writer(options); + + return new Promise(function(resolve, reject) { + try { + var result = writer.toXML(element); + + return resolve({ + xml: result + }); + } catch (err) { + return reject(err); + } + }); + }; + + var name$5 = "BPMN20"; + var uri$5 = "http://www.omg.org/spec/BPMN/20100524/MODEL"; + var prefix$5 = "bpmn"; + var associations$5 = [ + ]; + var types$5 = [ + { + name: "Interface", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "operations", + type: "Operation", + isMany: true + }, + { + name: "implementationRef", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Operation", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "inMessageRef", + type: "Message", + isReference: true + }, + { + name: "outMessageRef", + type: "Message", + isReference: true + }, + { + name: "errorRef", + type: "Error", + isMany: true, + isReference: true + }, + { + name: "implementationRef", + isAttr: true, + type: "String" + } + ] + }, + { + name: "EndPoint", + superClass: [ + "RootElement" + ] + }, + { + name: "Auditing", + superClass: [ + "BaseElement" + ] + }, + { + name: "GlobalTask", + superClass: [ + "CallableElement" + ], + properties: [ + { + name: "resources", + type: "ResourceRole", + isMany: true + } + ] + }, + { + name: "Monitoring", + superClass: [ + "BaseElement" + ] + }, + { + name: "Performer", + superClass: [ + "ResourceRole" + ] + }, + { + name: "Process", + superClass: [ + "FlowElementsContainer", + "CallableElement" + ], + properties: [ + { + name: "processType", + type: "ProcessType", + isAttr: true + }, + { + name: "isClosed", + isAttr: true, + type: "Boolean" + }, + { + name: "auditing", + type: "Auditing" + }, + { + name: "monitoring", + type: "Monitoring" + }, + { + name: "properties", + type: "Property", + isMany: true + }, + { + name: "laneSets", + isMany: true, + replaces: "FlowElementsContainer#laneSets", + type: "LaneSet" + }, + { + name: "flowElements", + isMany: true, + replaces: "FlowElementsContainer#flowElements", + type: "FlowElement" + }, + { + name: "artifacts", + type: "Artifact", + isMany: true + }, + { + name: "resources", + type: "ResourceRole", + isMany: true + }, + { + name: "correlationSubscriptions", + type: "CorrelationSubscription", + isMany: true + }, + { + name: "supports", + type: "Process", + isMany: true, + isReference: true + }, + { + name: "definitionalCollaborationRef", + type: "Collaboration", + isAttr: true, + isReference: true + }, + { + name: "isExecutable", + isAttr: true, + type: "Boolean" + } + ] + }, + { + name: "LaneSet", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "lanes", + type: "Lane", + isMany: true + }, + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Lane", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "partitionElementRef", + type: "BaseElement", + isAttr: true, + isReference: true + }, + { + name: "partitionElement", + type: "BaseElement" + }, + { + name: "flowNodeRef", + type: "FlowNode", + isMany: true, + isReference: true + }, + { + name: "childLaneSet", + type: "LaneSet", + xml: { + serialize: "xsi:type" + } + } + ] + }, + { + name: "GlobalManualTask", + superClass: [ + "GlobalTask" + ] + }, + { + name: "ManualTask", + superClass: [ + "Task" + ] + }, + { + name: "UserTask", + superClass: [ + "Task" + ], + properties: [ + { + name: "renderings", + type: "Rendering", + isMany: true + }, + { + name: "implementation", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Rendering", + superClass: [ + "BaseElement" + ] + }, + { + name: "HumanPerformer", + superClass: [ + "Performer" + ] + }, + { + name: "PotentialOwner", + superClass: [ + "HumanPerformer" + ] + }, + { + name: "GlobalUserTask", + superClass: [ + "GlobalTask" + ], + properties: [ + { + name: "implementation", + isAttr: true, + type: "String" + }, + { + name: "renderings", + type: "Rendering", + isMany: true + } + ] + }, + { + name: "Gateway", + isAbstract: true, + superClass: [ + "FlowNode" + ], + properties: [ + { + name: "gatewayDirection", + type: "GatewayDirection", + "default": "Unspecified", + isAttr: true + } + ] + }, + { + name: "EventBasedGateway", + superClass: [ + "Gateway" + ], + properties: [ + { + name: "instantiate", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "eventGatewayType", + type: "EventBasedGatewayType", + isAttr: true, + "default": "Exclusive" + } + ] + }, + { + name: "ComplexGateway", + superClass: [ + "Gateway" + ], + properties: [ + { + name: "activationCondition", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "default", + type: "SequenceFlow", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ExclusiveGateway", + superClass: [ + "Gateway" + ], + properties: [ + { + name: "default", + type: "SequenceFlow", + isAttr: true, + isReference: true + } + ] + }, + { + name: "InclusiveGateway", + superClass: [ + "Gateway" + ], + properties: [ + { + name: "default", + type: "SequenceFlow", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ParallelGateway", + superClass: [ + "Gateway" + ] + }, + { + name: "RootElement", + isAbstract: true, + superClass: [ + "BaseElement" + ] + }, + { + name: "Relationship", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "type", + isAttr: true, + type: "String" + }, + { + name: "direction", + type: "RelationshipDirection", + isAttr: true + }, + { + name: "source", + isMany: true, + isReference: true, + type: "Element" + }, + { + name: "target", + isMany: true, + isReference: true, + type: "Element" + } + ] + }, + { + name: "BaseElement", + isAbstract: true, + properties: [ + { + name: "id", + isAttr: true, + type: "String", + isId: true + }, + { + name: "documentation", + type: "Documentation", + isMany: true + }, + { + name: "extensionDefinitions", + type: "ExtensionDefinition", + isMany: true, + isReference: true + }, + { + name: "extensionElements", + type: "ExtensionElements" + } + ] + }, + { + name: "Extension", + properties: [ + { + name: "mustUnderstand", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "definition", + type: "ExtensionDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ExtensionDefinition", + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "extensionAttributeDefinitions", + type: "ExtensionAttributeDefinition", + isMany: true + } + ] + }, + { + name: "ExtensionAttributeDefinition", + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "type", + isAttr: true, + type: "String" + }, + { + name: "isReference", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "extensionDefinition", + type: "ExtensionDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ExtensionElements", + properties: [ + { + name: "valueRef", + isAttr: true, + isReference: true, + type: "Element" + }, + { + name: "values", + type: "Element", + isMany: true + }, + { + name: "extensionAttributeDefinition", + type: "ExtensionAttributeDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Documentation", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "text", + type: "String", + isBody: true + }, + { + name: "textFormat", + "default": "text/plain", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Event", + isAbstract: true, + superClass: [ + "FlowNode", + "InteractionNode" + ], + properties: [ + { + name: "properties", + type: "Property", + isMany: true + } + ] + }, + { + name: "IntermediateCatchEvent", + superClass: [ + "CatchEvent" + ] + }, + { + name: "IntermediateThrowEvent", + superClass: [ + "ThrowEvent" + ] + }, + { + name: "EndEvent", + superClass: [ + "ThrowEvent" + ] + }, + { + name: "StartEvent", + superClass: [ + "CatchEvent" + ], + properties: [ + { + name: "isInterrupting", + "default": true, + isAttr: true, + type: "Boolean" + } + ] + }, + { + name: "ThrowEvent", + isAbstract: true, + superClass: [ + "Event" + ], + properties: [ + { + name: "dataInputs", + type: "DataInput", + isMany: true + }, + { + name: "dataInputAssociations", + type: "DataInputAssociation", + isMany: true + }, + { + name: "inputSet", + type: "InputSet" + }, + { + name: "eventDefinitions", + type: "EventDefinition", + isMany: true + }, + { + name: "eventDefinitionRef", + type: "EventDefinition", + isMany: true, + isReference: true + } + ] + }, + { + name: "CatchEvent", + isAbstract: true, + superClass: [ + "Event" + ], + properties: [ + { + name: "parallelMultiple", + isAttr: true, + type: "Boolean", + "default": false + }, + { + name: "dataOutputs", + type: "DataOutput", + isMany: true + }, + { + name: "dataOutputAssociations", + type: "DataOutputAssociation", + isMany: true + }, + { + name: "outputSet", + type: "OutputSet" + }, + { + name: "eventDefinitions", + type: "EventDefinition", + isMany: true + }, + { + name: "eventDefinitionRef", + type: "EventDefinition", + isMany: true, + isReference: true + } + ] + }, + { + name: "BoundaryEvent", + superClass: [ + "CatchEvent" + ], + properties: [ + { + name: "cancelActivity", + "default": true, + isAttr: true, + type: "Boolean" + }, + { + name: "attachedToRef", + type: "Activity", + isAttr: true, + isReference: true + } + ] + }, + { + name: "EventDefinition", + isAbstract: true, + superClass: [ + "RootElement" + ] + }, + { + name: "CancelEventDefinition", + superClass: [ + "EventDefinition" + ] + }, + { + name: "ErrorEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "errorRef", + type: "Error", + isAttr: true, + isReference: true + } + ] + }, + { + name: "TerminateEventDefinition", + superClass: [ + "EventDefinition" + ] + }, + { + name: "EscalationEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "escalationRef", + type: "Escalation", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Escalation", + properties: [ + { + name: "structureRef", + type: "ItemDefinition", + isAttr: true, + isReference: true + }, + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "escalationCode", + isAttr: true, + type: "String" + } + ], + superClass: [ + "RootElement" + ] + }, + { + name: "CompensateEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "waitForCompletion", + isAttr: true, + type: "Boolean", + "default": true + }, + { + name: "activityRef", + type: "Activity", + isAttr: true, + isReference: true + } + ] + }, + { + name: "TimerEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "timeDate", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "timeCycle", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "timeDuration", + type: "Expression", + xml: { + serialize: "xsi:type" + } + } + ] + }, + { + name: "LinkEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "target", + type: "LinkEventDefinition", + isAttr: true, + isReference: true + }, + { + name: "source", + type: "LinkEventDefinition", + isMany: true, + isReference: true + } + ] + }, + { + name: "MessageEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "messageRef", + type: "Message", + isAttr: true, + isReference: true + }, + { + name: "operationRef", + type: "Operation", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ConditionalEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "condition", + type: "Expression", + xml: { + serialize: "xsi:type" + } + } + ] + }, + { + name: "SignalEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "signalRef", + type: "Signal", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Signal", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "structureRef", + type: "ItemDefinition", + isAttr: true, + isReference: true + }, + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ImplicitThrowEvent", + superClass: [ + "ThrowEvent" + ] + }, + { + name: "DataState", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ItemAwareElement", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "itemSubjectRef", + type: "ItemDefinition", + isAttr: true, + isReference: true + }, + { + name: "dataState", + type: "DataState" + } + ] + }, + { + name: "DataAssociation", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "sourceRef", + type: "ItemAwareElement", + isMany: true, + isReference: true + }, + { + name: "targetRef", + type: "ItemAwareElement", + isReference: true + }, + { + name: "transformation", + type: "FormalExpression", + xml: { + serialize: "property" + } + }, + { + name: "assignment", + type: "Assignment", + isMany: true + } + ] + }, + { + name: "DataInput", + superClass: [ + "ItemAwareElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "isCollection", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "inputSetRef", + type: "InputSet", + isMany: true, + isVirtual: true, + isReference: true + }, + { + name: "inputSetWithOptional", + type: "InputSet", + isMany: true, + isVirtual: true, + isReference: true + }, + { + name: "inputSetWithWhileExecuting", + type: "InputSet", + isMany: true, + isVirtual: true, + isReference: true + } + ] + }, + { + name: "DataOutput", + superClass: [ + "ItemAwareElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "isCollection", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "outputSetRef", + type: "OutputSet", + isMany: true, + isVirtual: true, + isReference: true + }, + { + name: "outputSetWithOptional", + type: "OutputSet", + isMany: true, + isVirtual: true, + isReference: true + }, + { + name: "outputSetWithWhileExecuting", + type: "OutputSet", + isMany: true, + isVirtual: true, + isReference: true + } + ] + }, + { + name: "InputSet", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "dataInputRefs", + type: "DataInput", + isMany: true, + isReference: true + }, + { + name: "optionalInputRefs", + type: "DataInput", + isMany: true, + isReference: true + }, + { + name: "whileExecutingInputRefs", + type: "DataInput", + isMany: true, + isReference: true + }, + { + name: "outputSetRefs", + type: "OutputSet", + isMany: true, + isReference: true + } + ] + }, + { + name: "OutputSet", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "dataOutputRefs", + type: "DataOutput", + isMany: true, + isReference: true + }, + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "inputSetRefs", + type: "InputSet", + isMany: true, + isReference: true + }, + { + name: "optionalOutputRefs", + type: "DataOutput", + isMany: true, + isReference: true + }, + { + name: "whileExecutingOutputRefs", + type: "DataOutput", + isMany: true, + isReference: true + } + ] + }, + { + name: "Property", + superClass: [ + "ItemAwareElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "DataInputAssociation", + superClass: [ + "DataAssociation" + ] + }, + { + name: "DataOutputAssociation", + superClass: [ + "DataAssociation" + ] + }, + { + name: "InputOutputSpecification", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "dataInputs", + type: "DataInput", + isMany: true + }, + { + name: "dataOutputs", + type: "DataOutput", + isMany: true + }, + { + name: "inputSets", + type: "InputSet", + isMany: true + }, + { + name: "outputSets", + type: "OutputSet", + isMany: true + } + ] + }, + { + name: "DataObject", + superClass: [ + "FlowElement", + "ItemAwareElement" + ], + properties: [ + { + name: "isCollection", + "default": false, + isAttr: true, + type: "Boolean" + } + ] + }, + { + name: "InputOutputBinding", + properties: [ + { + name: "inputDataRef", + type: "InputSet", + isAttr: true, + isReference: true + }, + { + name: "outputDataRef", + type: "OutputSet", + isAttr: true, + isReference: true + }, + { + name: "operationRef", + type: "Operation", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Assignment", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "from", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "to", + type: "Expression", + xml: { + serialize: "xsi:type" + } + } + ] + }, + { + name: "DataStore", + superClass: [ + "RootElement", + "ItemAwareElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "capacity", + isAttr: true, + type: "Integer" + }, + { + name: "isUnlimited", + "default": true, + isAttr: true, + type: "Boolean" + } + ] + }, + { + name: "DataStoreReference", + superClass: [ + "ItemAwareElement", + "FlowElement" + ], + properties: [ + { + name: "dataStoreRef", + type: "DataStore", + isAttr: true, + isReference: true + } + ] + }, + { + name: "DataObjectReference", + superClass: [ + "ItemAwareElement", + "FlowElement" + ], + properties: [ + { + name: "dataObjectRef", + type: "DataObject", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ConversationLink", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "sourceRef", + type: "InteractionNode", + isAttr: true, + isReference: true + }, + { + name: "targetRef", + type: "InteractionNode", + isAttr: true, + isReference: true + }, + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ConversationAssociation", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "innerConversationNodeRef", + type: "ConversationNode", + isAttr: true, + isReference: true + }, + { + name: "outerConversationNodeRef", + type: "ConversationNode", + isAttr: true, + isReference: true + } + ] + }, + { + name: "CallConversation", + superClass: [ + "ConversationNode" + ], + properties: [ + { + name: "calledCollaborationRef", + type: "Collaboration", + isAttr: true, + isReference: true + }, + { + name: "participantAssociations", + type: "ParticipantAssociation", + isMany: true + } + ] + }, + { + name: "Conversation", + superClass: [ + "ConversationNode" + ] + }, + { + name: "SubConversation", + superClass: [ + "ConversationNode" + ], + properties: [ + { + name: "conversationNodes", + type: "ConversationNode", + isMany: true + } + ] + }, + { + name: "ConversationNode", + isAbstract: true, + superClass: [ + "InteractionNode", + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "participantRef", + type: "Participant", + isMany: true, + isReference: true + }, + { + name: "messageFlowRefs", + type: "MessageFlow", + isMany: true, + isReference: true + }, + { + name: "correlationKeys", + type: "CorrelationKey", + isMany: true + } + ] + }, + { + name: "GlobalConversation", + superClass: [ + "Collaboration" + ] + }, + { + name: "PartnerEntity", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "participantRef", + type: "Participant", + isMany: true, + isReference: true + } + ] + }, + { + name: "PartnerRole", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "participantRef", + type: "Participant", + isMany: true, + isReference: true + } + ] + }, + { + name: "CorrelationProperty", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "correlationPropertyRetrievalExpression", + type: "CorrelationPropertyRetrievalExpression", + isMany: true + }, + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "type", + type: "ItemDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Error", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "structureRef", + type: "ItemDefinition", + isAttr: true, + isReference: true + }, + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "errorCode", + isAttr: true, + type: "String" + } + ] + }, + { + name: "CorrelationKey", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "correlationPropertyRef", + type: "CorrelationProperty", + isMany: true, + isReference: true + }, + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Expression", + superClass: [ + "BaseElement" + ], + isAbstract: false, + properties: [ + { + name: "body", + isBody: true, + type: "String" + } + ] + }, + { + name: "FormalExpression", + superClass: [ + "Expression" + ], + properties: [ + { + name: "language", + isAttr: true, + type: "String" + }, + { + name: "evaluatesToTypeRef", + type: "ItemDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Message", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "itemRef", + type: "ItemDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ItemDefinition", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "itemKind", + type: "ItemKind", + isAttr: true + }, + { + name: "structureRef", + isAttr: true, + type: "String" + }, + { + name: "isCollection", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "import", + type: "Import", + isAttr: true, + isReference: true + } + ] + }, + { + name: "FlowElement", + isAbstract: true, + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "auditing", + type: "Auditing" + }, + { + name: "monitoring", + type: "Monitoring" + }, + { + name: "categoryValueRef", + type: "CategoryValue", + isMany: true, + isReference: true + } + ] + }, + { + name: "SequenceFlow", + superClass: [ + "FlowElement" + ], + properties: [ + { + name: "isImmediate", + isAttr: true, + type: "Boolean" + }, + { + name: "conditionExpression", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "sourceRef", + type: "FlowNode", + isAttr: true, + isReference: true + }, + { + name: "targetRef", + type: "FlowNode", + isAttr: true, + isReference: true + } + ] + }, + { + name: "FlowElementsContainer", + isAbstract: true, + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "laneSets", + type: "LaneSet", + isMany: true + }, + { + name: "flowElements", + type: "FlowElement", + isMany: true + } + ] + }, + { + name: "CallableElement", + isAbstract: true, + superClass: [ + "RootElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "ioSpecification", + type: "InputOutputSpecification", + xml: { + serialize: "property" + } + }, + { + name: "supportedInterfaceRef", + type: "Interface", + isMany: true, + isReference: true + }, + { + name: "ioBinding", + type: "InputOutputBinding", + isMany: true, + xml: { + serialize: "property" + } + } + ] + }, + { + name: "FlowNode", + isAbstract: true, + superClass: [ + "FlowElement" + ], + properties: [ + { + name: "incoming", + type: "SequenceFlow", + isMany: true, + isReference: true + }, + { + name: "outgoing", + type: "SequenceFlow", + isMany: true, + isReference: true + }, + { + name: "lanes", + type: "Lane", + isMany: true, + isVirtual: true, + isReference: true + } + ] + }, + { + name: "CorrelationPropertyRetrievalExpression", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "messagePath", + type: "FormalExpression" + }, + { + name: "messageRef", + type: "Message", + isAttr: true, + isReference: true + } + ] + }, + { + name: "CorrelationPropertyBinding", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "dataPath", + type: "FormalExpression" + }, + { + name: "correlationPropertyRef", + type: "CorrelationProperty", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Resource", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "resourceParameters", + type: "ResourceParameter", + isMany: true + } + ] + }, + { + name: "ResourceParameter", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "isRequired", + isAttr: true, + type: "Boolean" + }, + { + name: "type", + type: "ItemDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "CorrelationSubscription", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "correlationKeyRef", + type: "CorrelationKey", + isAttr: true, + isReference: true + }, + { + name: "correlationPropertyBinding", + type: "CorrelationPropertyBinding", + isMany: true + } + ] + }, + { + name: "MessageFlow", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "sourceRef", + type: "InteractionNode", + isAttr: true, + isReference: true + }, + { + name: "targetRef", + type: "InteractionNode", + isAttr: true, + isReference: true + }, + { + name: "messageRef", + type: "Message", + isAttr: true, + isReference: true + } + ] + }, + { + name: "MessageFlowAssociation", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "innerMessageFlowRef", + type: "MessageFlow", + isAttr: true, + isReference: true + }, + { + name: "outerMessageFlowRef", + type: "MessageFlow", + isAttr: true, + isReference: true + } + ] + }, + { + name: "InteractionNode", + isAbstract: true, + properties: [ + { + name: "incomingConversationLinks", + type: "ConversationLink", + isMany: true, + isVirtual: true, + isReference: true + }, + { + name: "outgoingConversationLinks", + type: "ConversationLink", + isMany: true, + isVirtual: true, + isReference: true + } + ] + }, + { + name: "Participant", + superClass: [ + "InteractionNode", + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "interfaceRef", + type: "Interface", + isMany: true, + isReference: true + }, + { + name: "participantMultiplicity", + type: "ParticipantMultiplicity" + }, + { + name: "endPointRefs", + type: "EndPoint", + isMany: true, + isReference: true + }, + { + name: "processRef", + type: "Process", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ParticipantAssociation", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "innerParticipantRef", + type: "Participant", + isAttr: true, + isReference: true + }, + { + name: "outerParticipantRef", + type: "Participant", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ParticipantMultiplicity", + properties: [ + { + name: "minimum", + "default": 0, + isAttr: true, + type: "Integer" + }, + { + name: "maximum", + "default": 1, + isAttr: true, + type: "Integer" + } + ], + superClass: [ + "BaseElement" + ] + }, + { + name: "Collaboration", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "isClosed", + isAttr: true, + type: "Boolean" + }, + { + name: "participants", + type: "Participant", + isMany: true + }, + { + name: "messageFlows", + type: "MessageFlow", + isMany: true + }, + { + name: "artifacts", + type: "Artifact", + isMany: true + }, + { + name: "conversations", + type: "ConversationNode", + isMany: true + }, + { + name: "conversationAssociations", + type: "ConversationAssociation" + }, + { + name: "participantAssociations", + type: "ParticipantAssociation", + isMany: true + }, + { + name: "messageFlowAssociations", + type: "MessageFlowAssociation", + isMany: true + }, + { + name: "correlationKeys", + type: "CorrelationKey", + isMany: true + }, + { + name: "choreographyRef", + type: "Choreography", + isMany: true, + isReference: true + }, + { + name: "conversationLinks", + type: "ConversationLink", + isMany: true + } + ] + }, + { + name: "ChoreographyActivity", + isAbstract: true, + superClass: [ + "FlowNode" + ], + properties: [ + { + name: "participantRef", + type: "Participant", + isMany: true, + isReference: true + }, + { + name: "initiatingParticipantRef", + type: "Participant", + isAttr: true, + isReference: true + }, + { + name: "correlationKeys", + type: "CorrelationKey", + isMany: true + }, + { + name: "loopType", + type: "ChoreographyLoopType", + "default": "None", + isAttr: true + } + ] + }, + { + name: "CallChoreography", + superClass: [ + "ChoreographyActivity" + ], + properties: [ + { + name: "calledChoreographyRef", + type: "Choreography", + isAttr: true, + isReference: true + }, + { + name: "participantAssociations", + type: "ParticipantAssociation", + isMany: true + } + ] + }, + { + name: "SubChoreography", + superClass: [ + "ChoreographyActivity", + "FlowElementsContainer" + ], + properties: [ + { + name: "artifacts", + type: "Artifact", + isMany: true + } + ] + }, + { + name: "ChoreographyTask", + superClass: [ + "ChoreographyActivity" + ], + properties: [ + { + name: "messageFlowRef", + type: "MessageFlow", + isMany: true, + isReference: true + } + ] + }, + { + name: "Choreography", + superClass: [ + "Collaboration", + "FlowElementsContainer" + ] + }, + { + name: "GlobalChoreographyTask", + superClass: [ + "Choreography" + ], + properties: [ + { + name: "initiatingParticipantRef", + type: "Participant", + isAttr: true, + isReference: true + } + ] + }, + { + name: "TextAnnotation", + superClass: [ + "Artifact" + ], + properties: [ + { + name: "text", + type: "String" + }, + { + name: "textFormat", + "default": "text/plain", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Group", + superClass: [ + "Artifact" + ], + properties: [ + { + name: "categoryValueRef", + type: "CategoryValue", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Association", + superClass: [ + "Artifact" + ], + properties: [ + { + name: "associationDirection", + type: "AssociationDirection", + isAttr: true + }, + { + name: "sourceRef", + type: "BaseElement", + isAttr: true, + isReference: true + }, + { + name: "targetRef", + type: "BaseElement", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Category", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "categoryValue", + type: "CategoryValue", + isMany: true + }, + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Artifact", + isAbstract: true, + superClass: [ + "BaseElement" + ] + }, + { + name: "CategoryValue", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "categorizedFlowElements", + type: "FlowElement", + isMany: true, + isVirtual: true, + isReference: true + }, + { + name: "value", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Activity", + isAbstract: true, + superClass: [ + "FlowNode" + ], + properties: [ + { + name: "isForCompensation", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "default", + type: "SequenceFlow", + isAttr: true, + isReference: true + }, + { + name: "ioSpecification", + type: "InputOutputSpecification", + xml: { + serialize: "property" + } + }, + { + name: "boundaryEventRefs", + type: "BoundaryEvent", + isMany: true, + isReference: true + }, + { + name: "properties", + type: "Property", + isMany: true + }, + { + name: "dataInputAssociations", + type: "DataInputAssociation", + isMany: true + }, + { + name: "dataOutputAssociations", + type: "DataOutputAssociation", + isMany: true + }, + { + name: "startQuantity", + "default": 1, + isAttr: true, + type: "Integer" + }, + { + name: "resources", + type: "ResourceRole", + isMany: true + }, + { + name: "completionQuantity", + "default": 1, + isAttr: true, + type: "Integer" + }, + { + name: "loopCharacteristics", + type: "LoopCharacteristics" + } + ] + }, + { + name: "ServiceTask", + superClass: [ + "Task" + ], + properties: [ + { + name: "implementation", + isAttr: true, + type: "String" + }, + { + name: "operationRef", + type: "Operation", + isAttr: true, + isReference: true + } + ] + }, + { + name: "SubProcess", + superClass: [ + "Activity", + "FlowElementsContainer", + "InteractionNode" + ], + properties: [ + { + name: "triggeredByEvent", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "artifacts", + type: "Artifact", + isMany: true + } + ] + }, + { + name: "LoopCharacteristics", + isAbstract: true, + superClass: [ + "BaseElement" + ] + }, + { + name: "MultiInstanceLoopCharacteristics", + superClass: [ + "LoopCharacteristics" + ], + properties: [ + { + name: "isSequential", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "behavior", + type: "MultiInstanceBehavior", + "default": "All", + isAttr: true + }, + { + name: "loopCardinality", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "loopDataInputRef", + type: "ItemAwareElement", + isReference: true + }, + { + name: "loopDataOutputRef", + type: "ItemAwareElement", + isReference: true + }, + { + name: "inputDataItem", + type: "DataInput", + xml: { + serialize: "property" + } + }, + { + name: "outputDataItem", + type: "DataOutput", + xml: { + serialize: "property" + } + }, + { + name: "complexBehaviorDefinition", + type: "ComplexBehaviorDefinition", + isMany: true + }, + { + name: "completionCondition", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "oneBehaviorEventRef", + type: "EventDefinition", + isAttr: true, + isReference: true + }, + { + name: "noneBehaviorEventRef", + type: "EventDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "StandardLoopCharacteristics", + superClass: [ + "LoopCharacteristics" + ], + properties: [ + { + name: "testBefore", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "loopCondition", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "loopMaximum", + type: "Integer", + isAttr: true + } + ] + }, + { + name: "CallActivity", + superClass: [ + "Activity", + "InteractionNode" + ], + properties: [ + { + name: "calledElement", + type: "String", + isAttr: true + } + ] + }, + { + name: "Task", + superClass: [ + "Activity", + "InteractionNode" + ] + }, + { + name: "SendTask", + superClass: [ + "Task" + ], + properties: [ + { + name: "implementation", + isAttr: true, + type: "String" + }, + { + name: "operationRef", + type: "Operation", + isAttr: true, + isReference: true + }, + { + name: "messageRef", + type: "Message", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ReceiveTask", + superClass: [ + "Task" + ], + properties: [ + { + name: "implementation", + isAttr: true, + type: "String" + }, + { + name: "instantiate", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "operationRef", + type: "Operation", + isAttr: true, + isReference: true + }, + { + name: "messageRef", + type: "Message", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ScriptTask", + superClass: [ + "Task" + ], + properties: [ + { + name: "scriptFormat", + isAttr: true, + type: "String" + }, + { + name: "script", + type: "String" + } + ] + }, + { + name: "BusinessRuleTask", + superClass: [ + "Task" + ], + properties: [ + { + name: "implementation", + isAttr: true, + type: "String" + } + ] + }, + { + name: "AdHocSubProcess", + superClass: [ + "SubProcess" + ], + properties: [ + { + name: "completionCondition", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "ordering", + type: "AdHocOrdering", + isAttr: true + }, + { + name: "cancelRemainingInstances", + "default": true, + isAttr: true, + type: "Boolean" + } + ] + }, + { + name: "Transaction", + superClass: [ + "SubProcess" + ], + properties: [ + { + name: "protocol", + isAttr: true, + type: "String" + }, + { + name: "method", + isAttr: true, + type: "String" + } + ] + }, + { + name: "GlobalScriptTask", + superClass: [ + "GlobalTask" + ], + properties: [ + { + name: "scriptLanguage", + isAttr: true, + type: "String" + }, + { + name: "script", + isAttr: true, + type: "String" + } + ] + }, + { + name: "GlobalBusinessRuleTask", + superClass: [ + "GlobalTask" + ], + properties: [ + { + name: "implementation", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ComplexBehaviorDefinition", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "condition", + type: "FormalExpression" + }, + { + name: "event", + type: "ImplicitThrowEvent" + } + ] + }, + { + name: "ResourceRole", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "resourceRef", + type: "Resource", + isReference: true + }, + { + name: "resourceParameterBindings", + type: "ResourceParameterBinding", + isMany: true + }, + { + name: "resourceAssignmentExpression", + type: "ResourceAssignmentExpression" + }, + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ResourceParameterBinding", + properties: [ + { + name: "expression", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "parameterRef", + type: "ResourceParameter", + isAttr: true, + isReference: true + } + ], + superClass: [ + "BaseElement" + ] + }, + { + name: "ResourceAssignmentExpression", + properties: [ + { + name: "expression", + type: "Expression", + xml: { + serialize: "xsi:type" + } + } + ], + superClass: [ + "BaseElement" + ] + }, + { + name: "Import", + properties: [ + { + name: "importType", + isAttr: true, + type: "String" + }, + { + name: "location", + isAttr: true, + type: "String" + }, + { + name: "namespace", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Definitions", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "targetNamespace", + isAttr: true, + type: "String" + }, + { + name: "expressionLanguage", + "default": "http://www.w3.org/1999/XPath", + isAttr: true, + type: "String" + }, + { + name: "typeLanguage", + "default": "http://www.w3.org/2001/XMLSchema", + isAttr: true, + type: "String" + }, + { + name: "imports", + type: "Import", + isMany: true + }, + { + name: "extensions", + type: "Extension", + isMany: true + }, + { + name: "rootElements", + type: "RootElement", + isMany: true + }, + { + name: "diagrams", + isMany: true, + type: "bpmndi:BPMNDiagram" + }, + { + name: "exporter", + isAttr: true, + type: "String" + }, + { + name: "relationships", + type: "Relationship", + isMany: true + }, + { + name: "exporterVersion", + isAttr: true, + type: "String" + } + ] + } + ]; + var enumerations$3 = [ + { + name: "ProcessType", + literalValues: [ + { + name: "None" + }, + { + name: "Public" + }, + { + name: "Private" + } + ] + }, + { + name: "GatewayDirection", + literalValues: [ + { + name: "Unspecified" + }, + { + name: "Converging" + }, + { + name: "Diverging" + }, + { + name: "Mixed" + } + ] + }, + { + name: "EventBasedGatewayType", + literalValues: [ + { + name: "Parallel" + }, + { + name: "Exclusive" + } + ] + }, + { + name: "RelationshipDirection", + literalValues: [ + { + name: "None" + }, + { + name: "Forward" + }, + { + name: "Backward" + }, + { + name: "Both" + } + ] + }, + { + name: "ItemKind", + literalValues: [ + { + name: "Physical" + }, + { + name: "Information" + } + ] + }, + { + name: "ChoreographyLoopType", + literalValues: [ + { + name: "None" + }, + { + name: "Standard" + }, + { + name: "MultiInstanceSequential" + }, + { + name: "MultiInstanceParallel" + } + ] + }, + { + name: "AssociationDirection", + literalValues: [ + { + name: "None" + }, + { + name: "One" + }, + { + name: "Both" + } + ] + }, + { + name: "MultiInstanceBehavior", + literalValues: [ + { + name: "None" + }, + { + name: "One" + }, + { + name: "All" + }, + { + name: "Complex" + } + ] + }, + { + name: "AdHocOrdering", + literalValues: [ + { + name: "Parallel" + }, + { + name: "Sequential" + } + ] + } + ]; + var xml$1 = { + tagAlias: "lowerCase", + typePrefix: "t" + }; + var BpmnPackage = { + name: name$5, + uri: uri$5, + prefix: prefix$5, + associations: associations$5, + types: types$5, + enumerations: enumerations$3, + xml: xml$1 + }; + + var name$4 = "BPMNDI"; + var uri$4 = "http://www.omg.org/spec/BPMN/20100524/DI"; + var prefix$4 = "bpmndi"; + var types$4 = [ + { + name: "BPMNDiagram", + properties: [ + { + name: "plane", + type: "BPMNPlane", + redefines: "di:Diagram#rootElement" + }, + { + name: "labelStyle", + type: "BPMNLabelStyle", + isMany: true + } + ], + superClass: [ + "di:Diagram" + ] + }, + { + name: "BPMNPlane", + properties: [ + { + name: "bpmnElement", + isAttr: true, + isReference: true, + type: "bpmn:BaseElement", + redefines: "di:DiagramElement#modelElement" + } + ], + superClass: [ + "di:Plane" + ] + }, + { + name: "BPMNShape", + properties: [ + { + name: "bpmnElement", + isAttr: true, + isReference: true, + type: "bpmn:BaseElement", + redefines: "di:DiagramElement#modelElement" + }, + { + name: "isHorizontal", + isAttr: true, + type: "Boolean" + }, + { + name: "isExpanded", + isAttr: true, + type: "Boolean" + }, + { + name: "isMarkerVisible", + isAttr: true, + type: "Boolean" + }, + { + name: "label", + type: "BPMNLabel" + }, + { + name: "isMessageVisible", + isAttr: true, + type: "Boolean" + }, + { + name: "participantBandKind", + type: "ParticipantBandKind", + isAttr: true + }, + { + name: "choreographyActivityShape", + type: "BPMNShape", + isAttr: true, + isReference: true + } + ], + superClass: [ + "di:LabeledShape" + ] + }, + { + name: "BPMNEdge", + properties: [ + { + name: "label", + type: "BPMNLabel" + }, + { + name: "bpmnElement", + isAttr: true, + isReference: true, + type: "bpmn:BaseElement", + redefines: "di:DiagramElement#modelElement" + }, + { + name: "sourceElement", + isAttr: true, + isReference: true, + type: "di:DiagramElement", + redefines: "di:Edge#source" + }, + { + name: "targetElement", + isAttr: true, + isReference: true, + type: "di:DiagramElement", + redefines: "di:Edge#target" + }, + { + name: "messageVisibleKind", + type: "MessageVisibleKind", + isAttr: true, + "default": "initiating" + } + ], + superClass: [ + "di:LabeledEdge" + ] + }, + { + name: "BPMNLabel", + properties: [ + { + name: "labelStyle", + type: "BPMNLabelStyle", + isAttr: true, + isReference: true, + redefines: "di:DiagramElement#style" + } + ], + superClass: [ + "di:Label" + ] + }, + { + name: "BPMNLabelStyle", + properties: [ + { + name: "font", + type: "dc:Font" + } + ], + superClass: [ + "di:Style" + ] + } + ]; + var enumerations$2 = [ + { + name: "ParticipantBandKind", + literalValues: [ + { + name: "top_initiating" + }, + { + name: "middle_initiating" + }, + { + name: "bottom_initiating" + }, + { + name: "top_non_initiating" + }, + { + name: "middle_non_initiating" + }, + { + name: "bottom_non_initiating" + } + ] + }, + { + name: "MessageVisibleKind", + literalValues: [ + { + name: "initiating" + }, + { + name: "non_initiating" + } + ] + } + ]; + var associations$4 = [ + ]; + var BpmnDiPackage = { + name: name$4, + uri: uri$4, + prefix: prefix$4, + types: types$4, + enumerations: enumerations$2, + associations: associations$4 + }; + + var name$3 = "DC"; + var uri$3 = "http://www.omg.org/spec/DD/20100524/DC"; + var prefix$3 = "dc"; + var types$3 = [ + { + name: "Boolean" + }, + { + name: "Integer" + }, + { + name: "Real" + }, + { + name: "String" + }, + { + name: "Font", + properties: [ + { + name: "name", + type: "String", + isAttr: true + }, + { + name: "size", + type: "Real", + isAttr: true + }, + { + name: "isBold", + type: "Boolean", + isAttr: true + }, + { + name: "isItalic", + type: "Boolean", + isAttr: true + }, + { + name: "isUnderline", + type: "Boolean", + isAttr: true + }, + { + name: "isStrikeThrough", + type: "Boolean", + isAttr: true + } + ] + }, + { + name: "Point", + properties: [ + { + name: "x", + type: "Real", + "default": "0", + isAttr: true + }, + { + name: "y", + type: "Real", + "default": "0", + isAttr: true + } + ] + }, + { + name: "Bounds", + properties: [ + { + name: "x", + type: "Real", + "default": "0", + isAttr: true + }, + { + name: "y", + type: "Real", + "default": "0", + isAttr: true + }, + { + name: "width", + type: "Real", + isAttr: true + }, + { + name: "height", + type: "Real", + isAttr: true + } + ] + } + ]; + var associations$3 = [ + ]; + var DcPackage = { + name: name$3, + uri: uri$3, + prefix: prefix$3, + types: types$3, + associations: associations$3 + }; + + var name$2 = "DI"; + var uri$2 = "http://www.omg.org/spec/DD/20100524/DI"; + var prefix$2 = "di"; + var types$2 = [ + { + name: "DiagramElement", + isAbstract: true, + properties: [ + { + name: "id", + isAttr: true, + isId: true, + type: "String" + }, + { + name: "extension", + type: "Extension" + }, + { + name: "owningDiagram", + type: "Diagram", + isReadOnly: true, + isVirtual: true, + isReference: true + }, + { + name: "owningElement", + type: "DiagramElement", + isReadOnly: true, + isVirtual: true, + isReference: true + }, + { + name: "modelElement", + isReadOnly: true, + isVirtual: true, + isReference: true, + type: "Element" + }, + { + name: "style", + type: "Style", + isReadOnly: true, + isVirtual: true, + isReference: true + }, + { + name: "ownedElement", + type: "DiagramElement", + isReadOnly: true, + isMany: true, + isVirtual: true + } + ] + }, + { + name: "Node", + isAbstract: true, + superClass: [ + "DiagramElement" + ] + }, + { + name: "Edge", + isAbstract: true, + superClass: [ + "DiagramElement" + ], + properties: [ + { + name: "source", + type: "DiagramElement", + isReadOnly: true, + isVirtual: true, + isReference: true + }, + { + name: "target", + type: "DiagramElement", + isReadOnly: true, + isVirtual: true, + isReference: true + }, + { + name: "waypoint", + isUnique: false, + isMany: true, + type: "dc:Point", + xml: { + serialize: "xsi:type" + } + } + ] + }, + { + name: "Diagram", + isAbstract: true, + properties: [ + { + name: "id", + isAttr: true, + isId: true, + type: "String" + }, + { + name: "rootElement", + type: "DiagramElement", + isReadOnly: true, + isVirtual: true + }, + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "documentation", + isAttr: true, + type: "String" + }, + { + name: "resolution", + isAttr: true, + type: "Real" + }, + { + name: "ownedStyle", + type: "Style", + isReadOnly: true, + isMany: true, + isVirtual: true + } + ] + }, + { + name: "Shape", + isAbstract: true, + superClass: [ + "Node" + ], + properties: [ + { + name: "bounds", + type: "dc:Bounds" + } + ] + }, + { + name: "Plane", + isAbstract: true, + superClass: [ + "Node" + ], + properties: [ + { + name: "planeElement", + type: "DiagramElement", + subsettedProperty: "DiagramElement-ownedElement", + isMany: true + } + ] + }, + { + name: "LabeledEdge", + isAbstract: true, + superClass: [ + "Edge" + ], + properties: [ + { + name: "ownedLabel", + type: "Label", + isReadOnly: true, + subsettedProperty: "DiagramElement-ownedElement", + isMany: true, + isVirtual: true + } + ] + }, + { + name: "LabeledShape", + isAbstract: true, + superClass: [ + "Shape" + ], + properties: [ + { + name: "ownedLabel", + type: "Label", + isReadOnly: true, + subsettedProperty: "DiagramElement-ownedElement", + isMany: true, + isVirtual: true + } + ] + }, + { + name: "Label", + isAbstract: true, + superClass: [ + "Node" + ], + properties: [ + { + name: "bounds", + type: "dc:Bounds" + } + ] + }, + { + name: "Style", + isAbstract: true, + properties: [ + { + name: "id", + isAttr: true, + isId: true, + type: "String" + } + ] + }, + { + name: "Extension", + properties: [ + { + name: "values", + isMany: true, + type: "Element" + } + ] + } + ]; + var associations$2 = [ + ]; + var xml = { + tagAlias: "lowerCase" + }; + var DiPackage = { + name: name$2, + uri: uri$2, + prefix: prefix$2, + types: types$2, + associations: associations$2, + xml: xml + }; + + var name$1 = "bpmn.io colors for BPMN"; + var uri$1 = "http://bpmn.io/schema/bpmn/biocolor/1.0"; + var prefix$1 = "bioc"; + var types$1 = [ + { + name: "ColoredShape", + "extends": [ + "bpmndi:BPMNShape" + ], + properties: [ + { + name: "stroke", + isAttr: true, + type: "String" + }, + { + name: "fill", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ColoredEdge", + "extends": [ + "bpmndi:BPMNEdge" + ], + properties: [ + { + name: "stroke", + isAttr: true, + type: "String" + }, + { + name: "fill", + isAttr: true, + type: "String" + } + ] + } + ]; + var enumerations$1 = [ + ]; + var associations$1 = [ + ]; + var BiocPackage = { + name: name$1, + uri: uri$1, + prefix: prefix$1, + types: types$1, + enumerations: enumerations$1, + associations: associations$1 + }; + + var name = "BPMN in Color"; + var uri = "http://www.omg.org/spec/BPMN/non-normative/color/1.0"; + var prefix = "color"; + var types = [ + { + name: "ColoredLabel", + "extends": [ + "bpmndi:BPMNLabel" + ], + properties: [ + { + name: "color", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ColoredShape", + "extends": [ + "bpmndi:BPMNShape" + ], + properties: [ + { + name: "background-color", + isAttr: true, + type: "String" + }, + { + name: "border-color", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ColoredEdge", + "extends": [ + "bpmndi:BPMNEdge" + ], + properties: [ + { + name: "border-color", + isAttr: true, + type: "String" + } + ] + } + ]; + var enumerations = [ + ]; + var associations = [ + ]; + var BpmnInColorPackage = { + name: name, + uri: uri, + prefix: prefix, + types: types, + enumerations: enumerations, + associations: associations + }; + + var packages = { + bpmn: BpmnPackage, + bpmndi: BpmnDiPackage, + dc: DcPackage, + di: DiPackage, + bioc: BiocPackage, + color: BpmnInColorPackage + }; + + function simple(additionalPackages, options) { + var pks = assign({}, packages, additionalPackages); + + return new BpmnModdle(pks, options); + } + + function elementToString(e) { + if (!e) { + return ''; + } + + return '<' + e.$type + (e.id ? ' id="' + e.id : '') + '" />'; + } + + // TODO(nikku): remove with future bpmn-js version + + /** + * Wraps APIs to check: + * + * 1) If a callback is passed -> Warn users about callback deprecation. + * 2) If Promise class is implemented in current environment. + * + * @private + */ + function wrapForCompatibility(api) { + + return function() { + + if (!window.Promise) { + throw new Error('Promises is not supported in this environment. Please polyfill Promise.'); + } + + var argLen = arguments.length; + if (argLen >= 1 && isFunction(arguments[argLen - 1])) { + + var callback = arguments[argLen - 1]; + + console.warn(new Error( + 'Passing callbacks to ' + api.name + ' is deprecated and will be removed in a future major release. ' + + 'Please switch to promises: https://bpmn.io/l/moving-to-promises.html' + )); + + var argsWithoutCallback = Array.prototype.slice.call(arguments, 0, -1); + + api.apply(this, argsWithoutCallback).then(function(result) { + + var firstKey = Object.keys(result)[0]; + + // The APIs we are wrapping all resolve a single item depending on the API. + // For instance, importXML resolves { warnings } and saveXML returns { xml }. + // That's why we can call the callback with the first item of result. + return callback(null, result[firstKey]); + + // Passing a second paramter instead of catch because we don't want to + // catch errors thrown by callback(). + }, function(err) { + + return callback(err, err.warnings); + }); + } else { + + return api.apply(this, arguments); + } + }; + } + + + // TODO(nikku): remove with future bpmn-js version + + var DI_ERROR_MESSAGE = 'Tried to access di from the businessObject. The di is available through the diagram element only. For more information, see https://github.com/bpmn-io/bpmn-js/issues/1472'; + + function ensureCompatDiRef(businessObject) { + + // bpmnElement can have multiple independent DIs + if (!has$1(businessObject, 'di')) { + Object.defineProperty(businessObject, 'di', { + enumerable: false, + get: function() { + throw new Error(DI_ERROR_MESSAGE); + } + }); + } + } + + /** + * Returns true if an element has the given meta-model type + * + * @param {ModdleElement} element + * @param {string} type + * + * @return {boolean} + */ + function is$2(element, type) { + return element.$instanceOf(type); + } + + + /** + * Find a suitable display candidate for definitions where the DI does not + * correctly specify one. + */ + function findDisplayCandidate(definitions) { + return find(definitions.rootElements, function(e) { + return is$2(e, 'bpmn:Process') || is$2(e, 'bpmn:Collaboration'); + }); + } + + + function BpmnTreeWalker(handler, translate) { + + // list of containers already walked + var handledElements = {}; + + // list of elements to handle deferred to ensure + // prerequisites are drawn + var deferred = []; + + var diMap = {}; + + // Helpers ////////////////////// + + function contextual(fn, ctx) { + return function(e) { + fn(e, ctx); + }; + } + + function handled(element) { + handledElements[element.id] = element; + } + + function isHandled(element) { + return handledElements[element.id]; + } + + function visit(element, ctx) { + + var gfx = element.gfx; + + // avoid multiple rendering of elements + if (gfx) { + throw new Error( + translate('already rendered {element}', { element: elementToString(element) }) + ); + } + + // call handler + return handler.element(element, diMap[element.id], ctx); + } + + function visitRoot(element, diagram) { + return handler.root(element, diMap[element.id], diagram); + } + + function visitIfDi(element, ctx) { + + try { + var gfx = diMap[element.id] && visit(element, ctx); + + handled(element); + + return gfx; + } catch (e) { + logError(e.message, { element: element, error: e }); + + console.error(translate('failed to import {element}', { element: elementToString(element) })); + console.error(e); + } + } + + function logError(message, context) { + handler.error(message, context); + } + + // DI handling ////////////////////// + + function registerDi(di) { + var bpmnElement = di.bpmnElement; + + if (bpmnElement) { + if (diMap[bpmnElement.id]) { + logError( + translate('multiple DI elements defined for {element}', { + element: elementToString(bpmnElement) + }), + { element: bpmnElement } + ); + } else { + diMap[bpmnElement.id] = di; + + ensureCompatDiRef(bpmnElement); + } + } else { + logError( + translate('no bpmnElement referenced in {element}', { + element: elementToString(di) + }), + { element: di } + ); + } + } + + function handleDiagram(diagram) { + handlePlane(diagram.plane); + } + + function handlePlane(plane) { + registerDi(plane); + + forEach$1(plane.planeElement, handlePlaneElement); + } + + function handlePlaneElement(planeElement) { + registerDi(planeElement); + } + + + // Semantic handling ////////////////////// + + /** + * Handle definitions and return the rendered diagram (if any) + * + * @param {ModdleElement} definitions to walk and import + * @param {ModdleElement} [diagram] specific diagram to import and display + * + * @throws {Error} if no diagram to display could be found + */ + function handleDefinitions(definitions, diagram) { + + // make sure we walk the correct bpmnElement + + var diagrams = definitions.diagrams; + + if (diagram && diagrams.indexOf(diagram) === -1) { + throw new Error(translate('diagram not part of bpmn:Definitions')); + } + + if (!diagram && diagrams && diagrams.length) { + diagram = diagrams[0]; + } + + // no diagram -> nothing to import + if (!diagram) { + throw new Error(translate('no diagram to display')); + } + + // load DI from selected diagram only + diMap = {}; + handleDiagram(diagram); + + + var plane = diagram.plane; + + if (!plane) { + throw new Error(translate( + 'no plane for {element}', + { element: elementToString(diagram) } + )); + } + + var rootElement = plane.bpmnElement; + + // ensure we default to a suitable display candidate (process or collaboration), + // even if non is specified in DI + if (!rootElement) { + rootElement = findDisplayCandidate(definitions); + + if (!rootElement) { + throw new Error(translate('no process or collaboration to display')); + } else { + + logError( + translate('correcting missing bpmnElement on {plane} to {rootElement}', { + plane: elementToString(plane), + rootElement: elementToString(rootElement) + }) + ); + + // correct DI on the fly + plane.bpmnElement = rootElement; + registerDi(plane); + } + } + + + var ctx = visitRoot(rootElement, plane); + + if (is$2(rootElement, 'bpmn:Process') || is$2(rootElement, 'bpmn:SubProcess')) { + handleProcess(rootElement, ctx); + } else if (is$2(rootElement, 'bpmn:Collaboration')) { + handleCollaboration(rootElement, ctx); + + // force drawing of everything not yet drawn that is part of the target DI + handleUnhandledProcesses(definitions.rootElements, ctx); + } else { + throw new Error( + translate('unsupported bpmnElement for {plane}: {rootElement}', { + plane: elementToString(plane), + rootElement: elementToString(rootElement) + }) + ); + } + + // handle all deferred elements + handleDeferred(); + } + + function handleDeferred() { + + var fn; + + // drain deferred until empty + while (deferred.length) { + fn = deferred.shift(); + + fn(); + } + } + + function handleProcess(process, context) { + handleFlowElementsContainer(process, context); + handleIoSpecification(process.ioSpecification, context); + + handleArtifacts(process.artifacts, context); + + // log process handled + handled(process); + } + + function handleUnhandledProcesses(rootElements, ctx) { + + // walk through all processes that have not yet been drawn and draw them + // if they contain lanes with DI information. + // we do this to pass the free-floating lane test cases in the MIWG test suite + var processes = filter(rootElements, function(e) { + return !isHandled(e) && is$2(e, 'bpmn:Process') && e.laneSets; + }); + + processes.forEach(contextual(handleProcess, ctx)); + } + + function handleMessageFlow(messageFlow, context) { + visitIfDi(messageFlow, context); + } + + function handleMessageFlows(messageFlows, context) { + forEach$1(messageFlows, contextual(handleMessageFlow, context)); + } + + function handleDataAssociation(association, context) { + visitIfDi(association, context); + } + + function handleDataInput(dataInput, context) { + visitIfDi(dataInput, context); + } + + function handleDataOutput(dataOutput, context) { + visitIfDi(dataOutput, context); + } + + function handleArtifact(artifact, context) { + + // bpmn:TextAnnotation + // bpmn:Group + // bpmn:Association + + visitIfDi(artifact, context); + } + + function handleArtifacts(artifacts, context) { + + forEach$1(artifacts, function(e) { + if (is$2(e, 'bpmn:Association')) { + deferred.push(function() { + handleArtifact(e, context); + }); + } else { + handleArtifact(e, context); + } + }); + } + + function handleIoSpecification(ioSpecification, context) { + + if (!ioSpecification) { + return; + } + + forEach$1(ioSpecification.dataInputs, contextual(handleDataInput, context)); + forEach$1(ioSpecification.dataOutputs, contextual(handleDataOutput, context)); + } + + function handleSubProcess(subProcess, context) { + handleFlowElementsContainer(subProcess, context); + handleArtifacts(subProcess.artifacts, context); + } + + function handleFlowNode(flowNode, context) { + var childCtx = visitIfDi(flowNode, context); + + if (is$2(flowNode, 'bpmn:SubProcess')) { + handleSubProcess(flowNode, childCtx || context); + } + + if (is$2(flowNode, 'bpmn:Activity')) { + handleIoSpecification(flowNode.ioSpecification, context); + } + + // defer handling of associations + // affected types: + // + // * bpmn:Activity + // * bpmn:ThrowEvent + // * bpmn:CatchEvent + // + deferred.push(function() { + forEach$1(flowNode.dataInputAssociations, contextual(handleDataAssociation, context)); + forEach$1(flowNode.dataOutputAssociations, contextual(handleDataAssociation, context)); + }); + } + + function handleSequenceFlow(sequenceFlow, context) { + visitIfDi(sequenceFlow, context); + } + + function handleDataElement(dataObject, context) { + visitIfDi(dataObject, context); + } + + function handleLane(lane, context) { + + deferred.push(function() { + + var newContext = visitIfDi(lane, context); + + if (lane.childLaneSet) { + handleLaneSet(lane.childLaneSet, newContext || context); + } + + wireFlowNodeRefs(lane); + }); + } + + function handleLaneSet(laneSet, context) { + forEach$1(laneSet.lanes, contextual(handleLane, context)); + } + + function handleLaneSets(laneSets, context) { + forEach$1(laneSets, contextual(handleLaneSet, context)); + } + + function handleFlowElementsContainer(container, context) { + handleFlowElements(container.flowElements, context); + + if (container.laneSets) { + handleLaneSets(container.laneSets, context); + } + } + + function handleFlowElements(flowElements, context) { + forEach$1(flowElements, function(e) { + if (is$2(e, 'bpmn:SequenceFlow')) { + deferred.push(function() { + handleSequenceFlow(e, context); + }); + } else if (is$2(e, 'bpmn:BoundaryEvent')) { + deferred.unshift(function() { + handleFlowNode(e, context); + }); + } else if (is$2(e, 'bpmn:FlowNode')) { + handleFlowNode(e, context); + } else if (is$2(e, 'bpmn:DataObject')) ; else if (is$2(e, 'bpmn:DataStoreReference')) { + handleDataElement(e, context); + } else if (is$2(e, 'bpmn:DataObjectReference')) { + handleDataElement(e, context); + } else { + logError( + translate('unrecognized flowElement {element} in context {context}', { + element: elementToString(e), + context: (context ? elementToString(context.businessObject) : 'null') + }), + { element: e, context: context } + ); + } + }); + } + + function handleParticipant(participant, context) { + var newCtx = visitIfDi(participant, context); + + var process = participant.processRef; + if (process) { + handleProcess(process, newCtx || context); + } + } + + function handleCollaboration(collaboration, context) { + + forEach$1(collaboration.participants, contextual(handleParticipant, context)); + + handleArtifacts(collaboration.artifacts, context); + + // handle message flows latest in the process + deferred.push(function() { + handleMessageFlows(collaboration.messageFlows, context); + }); + } + + + function wireFlowNodeRefs(lane) { + + // wire the virtual flowNodeRefs <-> relationship + forEach$1(lane.flowNodeRef, function(flowNode) { + var lanes = flowNode.get('lanes'); + + if (lanes) { + lanes.push(lane); + } + }); + } + + // API ////////////////////// + + return { + handleDeferred: handleDeferred, + handleDefinitions: handleDefinitions, + handleSubProcess: handleSubProcess, + registerDi: registerDi + }; + } + + /** + * Is an element of the given BPMN type? + * + * @param {djs.model.Base|ModdleElement} element + * @param {string} type + * + * @return {boolean} + */ + function is$1(element, type) { + var bo = getBusinessObject(element); + + return bo && (typeof bo.$instanceOf === 'function') && bo.$instanceOf(type); + } + + + /** + * Return true if element has any of the given types. + * + * @param {djs.model.Base} element + * @param {Array} types + * + * @return {boolean} + */ + function isAny(element, types) { + return some(types, function(t) { + return is$1(element, t); + }); + } + + /** + * Return the business object for a given element. + * + * @param {djs.model.Base|ModdleElement} element + * + * @return {ModdleElement} + */ + function getBusinessObject(element) { + return (element && element.businessObject) || element; + } + + /** + * Return the di object for a given element. + * + * @param {djs.model.Base} element + * + * @return {ModdleElement} + */ + function getDi(element) { + return element && element.di; + } + + /** + * The importBpmnDiagram result. + * + * @typedef {Object} ImportBPMNDiagramResult + * + * @property {Array} warnings + */ + + /** + * The importBpmnDiagram error. + * + * @typedef {Error} ImportBPMNDiagramError + * + * @property {Array} warnings + */ + + /** + * Import the definitions into a diagram. + * + * Errors and warnings are reported through the specified callback. + * + * @param {djs.Diagram} diagram + * @param {ModdleElement} definitions + * @param {ModdleElement} [bpmnDiagram] the diagram to be rendered + * (if not provided, the first one will be rendered) + * + * Returns {Promise} + */ + function importBpmnDiagram(diagram, definitions, bpmnDiagram) { + + var importer, + eventBus, + translate, + canvas; + + var error, + warnings = []; + + /** + * Walk the diagram semantically, importing (=drawing) + * all elements you encounter. + * + * @param {ModdleElement} definitions + * @param {ModdleElement} bpmnDiagram + */ + function render(definitions, bpmnDiagram) { + + var visitor = { + + root: function(element, di) { + return importer.add(element, di); + }, + + element: function(element, di, parentShape) { + return importer.add(element, di, parentShape); + }, + + error: function(message, context) { + warnings.push({ message: message, context: context }); + } + }; + + var walker = new BpmnTreeWalker(visitor, translate); + + + bpmnDiagram = bpmnDiagram || (definitions.diagrams && definitions.diagrams[0]); + + var diagramsToImport = getDiagramsToImport(definitions, bpmnDiagram); + + if (!diagramsToImport) { + throw new Error(translate('no diagram to display')); + } + + // traverse BPMN 2.0 document model, + // starting at definitions + forEach$1(diagramsToImport, function(diagram) { + walker.handleDefinitions(definitions, diagram); + }); + + var rootId = bpmnDiagram.plane.bpmnElement.id; + + // we do need to account for different ways we create root elements + // each nested imported do have the `_plane` suffix, while + // the root is found under the business object ID + canvas.setRootElement( + canvas.findRoot(rootId + '_plane') || canvas.findRoot(rootId) + ); + } + + return new Promise(function(resolve, reject) { + try { + importer = diagram.get('bpmnImporter'); + eventBus = diagram.get('eventBus'); + translate = diagram.get('translate'); + canvas = diagram.get('canvas'); + + eventBus.fire('import.render.start', { definitions: definitions }); + + render(definitions, bpmnDiagram); + + eventBus.fire('import.render.complete', { + error: error, + warnings: warnings + }); + + return resolve({ warnings: warnings }); + } catch (e) { + + e.warnings = warnings; + return reject(e); + } + }); + } + + /** + * Returns all diagrams in the same hierarchy as the requested diagram. + * Includes all parent and sub process diagrams. + * + * @param {Array} definitions + * @param {Object} bpmnDiagram + * + * @returns {Array} + */ + function getDiagramsToImport(definitions, bpmnDiagram) { + if (!bpmnDiagram) { + return; + } + + var bpmnElement = bpmnDiagram.plane.bpmnElement, + rootElement = bpmnElement; + + if (!is$1(bpmnElement, 'bpmn:Process') && !is$1(bpmnElement, 'bpmn:Collaboration')) { + rootElement = findRootProcess(bpmnElement); + } + + // in case the process is part of a collaboration, the plane references the + // collaboration, not the process + var collaboration; + + if (is$1(rootElement, 'bpmn:Collaboration')) { + collaboration = rootElement; + } else { + collaboration = find(definitions.rootElements, function(element) { + if (!is$1(element, 'bpmn:Collaboration')) { + return; + } + + return find(element.participants, function(participant) { + return participant.processRef === rootElement; + }); + }); + } + + var rootElements = [ rootElement ]; + + // all collaboration processes can contain sub-diagrams + if (collaboration) { + rootElements = map(collaboration.participants, function(participant) { + return participant.processRef; + }); + + rootElements.push(collaboration); + } + + var allChildren = selfAndAllFlowElements(rootElements); + + // if we have multiple diagrams referencing the same element, we + // use the first in the file + var diagramsToImport = [ bpmnDiagram ]; + var handledElements = [ bpmnElement ]; + + forEach$1(definitions.diagrams, function(diagram) { + var businessObject = diagram.plane.bpmnElement; + + if ( + allChildren.indexOf(businessObject) !== -1 && + handledElements.indexOf(businessObject) === -1 + ) { + diagramsToImport.push(diagram); + handledElements.push(businessObject); + } + }); + + + return diagramsToImport; + } + + function selfAndAllFlowElements(elements) { + var result = []; + + forEach$1(elements, function(element) { + if (!element) { + return; + } + + result.push(element); + + result = result.concat(selfAndAllFlowElements(element.flowElements)); + }); + + return result; + } + + function findRootProcess(element) { + var parent = element; + + while (parent) { + if (is$1(parent, 'bpmn:Process')) { + return parent; + } + + parent = parent.$parent; + } + } + + /** + * This file must not be changed or exchanged. + * + * @see http://bpmn.io/license for more information. + */ + + + // inlined ../../resources/logo.svg + var BPMNIO_LOGO_SVG = ''; + + var BPMNIO_IMG = BPMNIO_LOGO_SVG; + + var LOGO_STYLES = { + verticalAlign: 'middle' + }; + + var LINK_STYLES = { + 'color': '#404040' + }; + + var LIGHTBOX_STYLES = { + 'zIndex': '1001', + 'position': 'fixed', + 'top': '0', + 'left': '0', + 'right': '0', + 'bottom': '0' + }; + + var BACKDROP_STYLES = { + 'width': '100%', + 'height': '100%', + 'background': 'rgba(40,40,40,0.2)' + }; + + var NOTICE_STYLES = { + 'position': 'absolute', + 'left': '50%', + 'top': '40%', + 'transform': 'translate(-50%)', + 'width': '260px', + 'padding': '10px', + 'background': 'white', + 'boxShadow': '0 1px 4px rgba(0,0,0,0.3)', + 'fontFamily': 'Helvetica, Arial, sans-serif', + 'fontSize': '14px', + 'display': 'flex', + 'lineHeight': '1.3' + }; + + var LIGHTBOX_MARKUP = + '
' + + '
' + + '
' + + '' + + BPMNIO_IMG + + '' + + '' + + 'Web-based tooling for BPMN, DMN and CMMN diagrams ' + + 'powered by bpmn.io.' + + '' + + '
' + + '
'; + + + var lightbox; + + function createLightbox() { + lightbox = domify(LIGHTBOX_MARKUP); + + assign$1(lightbox, LIGHTBOX_STYLES); + assign$1(query('svg', lightbox), LOGO_STYLES); + assign$1(query('.backdrop', lightbox), BACKDROP_STYLES); + assign$1(query('.notice', lightbox), NOTICE_STYLES); + assign$1(query('.link', lightbox), LINK_STYLES, { + 'margin': '15px 20px 15px 10px', + 'alignSelf': 'center' + }); + } + + function open() { + + if (!lightbox) { + createLightbox(); + + delegate.bind(lightbox, '.backdrop', 'click', function(event) { + document.body.removeChild(lightbox); + }); + } + + document.body.appendChild(lightbox); + } + + /** + * The code in the area + * must not be changed. + * + * @see http://bpmn.io/license for more information. + */ + + /** + * A base viewer for BPMN 2.0 diagrams. + * + * Have a look at {@link Viewer}, {@link NavigatedViewer} or {@link Modeler} for + * bundles that include actual features. + * + * @param {Object} [options] configuration options to pass to the viewer + * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body. + * @param {string|number} [options.width] the width of the viewer + * @param {string|number} [options.height] the height of the viewer + * @param {Object} [options.moddleExtensions] extension packages to provide + * @param {Array} [options.modules] a list of modules to override the default modules + * @param {Array} [options.additionalModules] a list of modules to use with the default modules + */ + function BaseViewer(options) { + + options = assign({}, DEFAULT_OPTIONS, options); + + this._moddle = this._createModdle(options); + + this._container = this._createContainer(options); + + /* */ + + addProjectLogo(this._container); + + /* */ + + this._init(this._container, this._moddle, options); + } + + e(BaseViewer, Diagram); + + /** + * The importXML result. + * + * @typedef {Object} ImportXMLResult + * + * @property {Array} warnings + */ + + /** + * The importXML error. + * + * @typedef {Error} ImportXMLError + * + * @property {Array} warnings + */ + + /** + * Parse and render a BPMN 2.0 diagram. + * + * Once finished the viewer reports back the result to the + * provided callback function with (err, warnings). + * + * ## Life-Cycle Events + * + * During import the viewer will fire life-cycle events: + * + * * import.parse.start (about to read model from xml) + * * import.parse.complete (model read; may have worked or not) + * * import.render.start (graphical import start) + * * import.render.complete (graphical import finished) + * * import.done (everything done) + * + * You can use these events to hook into the life-cycle. + * + * @param {string} xml the BPMN 2.0 xml + * @param {ModdleElement|string} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered) + * + * Returns {Promise} + */ + BaseViewer.prototype.importXML = wrapForCompatibility(function importXML(xml, bpmnDiagram) { + + var self = this; + + function ParseCompleteEvent(data) { + + var event = self.get('eventBus').createEvent(data); + + // TODO(nikku): remove with future bpmn-js version + Object.defineProperty(event, 'context', { + enumerable: true, + get: function() { + + console.warn(new Error( + 'import.parse.complete is deprecated ' + + 'and will be removed in future library versions' + )); + + return { + warnings: data.warnings, + references: data.references, + elementsById: data.elementsById + }; + } + }); + + return event; + } + + return new Promise(function(resolve, reject) { + + // hook in pre-parse listeners + + // allow xml manipulation + xml = self._emit('import.parse.start', { xml: xml }) || xml; + + self._moddle.fromXML(xml, 'bpmn:Definitions').then(function(result) { + var definitions = result.rootElement; + var references = result.references; + var parseWarnings = result.warnings; + var elementsById = result.elementsById; + + // hook in post parse listeners + + // allow definitions manipulation + definitions = self._emit('import.parse.complete', ParseCompleteEvent({ + error: null, + definitions: definitions, + elementsById: elementsById, + references: references, + warnings: parseWarnings + })) || definitions; + + self.importDefinitions(definitions, bpmnDiagram).then(function(result) { + var allWarnings = [].concat(parseWarnings, result.warnings || []); + + self._emit('import.done', { error: null, warnings: allWarnings }); + + return resolve({ warnings: allWarnings }); + }).catch(function(err) { + var allWarnings = [].concat(parseWarnings, err.warnings || []); + + self._emit('import.done', { error: err, warnings: allWarnings }); + + return reject(addWarningsToError(err, allWarnings)); + }); + }).catch(function(err) { + + self._emit('import.parse.complete', { + error: err + }); + + err = checkValidationError(err); + + self._emit('import.done', { error: err, warnings: err.warnings }); + + return reject(err); + }); + }); + }); + + /** + * The importDefinitions result. + * + * @typedef {Object} ImportDefinitionsResult + * + * @property {Array} warnings + */ + + /** + * The importDefinitions error. + * + * @typedef {Error} ImportDefinitionsError + * + * @property {Array} warnings + */ + + /** + * Import parsed definitions and render a BPMN 2.0 diagram. + * + * Once finished the viewer reports back the result to the + * provided callback function with (err, warnings). + * + * ## Life-Cycle Events + * + * During import the viewer will fire life-cycle events: + * + * * import.render.start (graphical import start) + * * import.render.complete (graphical import finished) + * + * You can use these events to hook into the life-cycle. + * + * @param {ModdleElement} definitions parsed BPMN 2.0 definitions + * @param {ModdleElement|string} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered) + * + * Returns {Promise} + */ + BaseViewer.prototype.importDefinitions = wrapForCompatibility(function importDefinitions(definitions, bpmnDiagram) { + + var self = this; + + return new Promise(function(resolve, reject) { + + self._setDefinitions(definitions); + + self.open(bpmnDiagram).then(function(result) { + + var warnings = result.warnings; + + return resolve({ warnings: warnings }); + }).catch(function(err) { + + return reject(err); + }); + }); + }); + + /** + * The open result. + * + * @typedef {Object} OpenResult + * + * @property {Array} warnings + */ + + /** + * The open error. + * + * @typedef {Error} OpenError + * + * @property {Array} warnings + */ + + /** + * Open diagram of previously imported XML. + * + * Once finished the viewer reports back the result to the + * provided callback function with (err, warnings). + * + * ## Life-Cycle Events + * + * During switch the viewer will fire life-cycle events: + * + * * import.render.start (graphical import start) + * * import.render.complete (graphical import finished) + * + * You can use these events to hook into the life-cycle. + * + * @param {string|ModdleElement} [bpmnDiagramOrId] id or the diagram to open + * + * Returns {Promise} + */ + BaseViewer.prototype.open = wrapForCompatibility(function open(bpmnDiagramOrId) { + + var definitions = this._definitions; + var bpmnDiagram = bpmnDiagramOrId; + + var self = this; + + return new Promise(function(resolve, reject) { + if (!definitions) { + var err1 = new Error('no XML imported'); + + return reject(addWarningsToError(err1, [])); + } + + if (typeof bpmnDiagramOrId === 'string') { + bpmnDiagram = findBPMNDiagram(definitions, bpmnDiagramOrId); + + if (!bpmnDiagram) { + var err2 = new Error('BPMNDiagram <' + bpmnDiagramOrId + '> not found'); + + return reject(addWarningsToError(err2, [])); + } + } + + // clear existing rendered diagram + // catch synchronous exceptions during #clear() + try { + self.clear(); + } catch (error) { + + return reject(addWarningsToError(error, [])); + } + + // perform graphical import + importBpmnDiagram(self, definitions, bpmnDiagram).then(function(result) { + + var warnings = result.warnings; + + return resolve({ warnings: warnings }); + }).catch(function(err) { + + return reject(err); + }); + }); + }); + + /** + * The saveXML result. + * + * @typedef {Object} SaveXMLResult + * + * @property {string} xml + */ + + /** + * Export the currently displayed BPMN 2.0 diagram as + * a BPMN 2.0 XML document. + * + * ## Life-Cycle Events + * + * During XML saving the viewer will fire life-cycle events: + * + * * saveXML.start (before serialization) + * * saveXML.serialized (after xml generation) + * * saveXML.done (everything done) + * + * You can use these events to hook into the life-cycle. + * + * @param {Object} [options] export options + * @param {boolean} [options.format=false] output formatted XML + * @param {boolean} [options.preamble=true] output preamble + * + * Returns {Promise} + */ + BaseViewer.prototype.saveXML = wrapForCompatibility(function saveXML(options) { + + options = options || {}; + + var self = this; + + var definitions = this._definitions; + + return new Promise(function(resolve) { + + if (!definitions) { + return resolve({ + error: new Error('no definitions loaded') + }); + } + + // allow to fiddle around with definitions + definitions = self._emit('saveXML.start', { + definitions: definitions + }) || definitions; + + self._moddle.toXML(definitions, options).then(function(result) { + + var xml = result.xml; + + xml = self._emit('saveXML.serialized', { + xml: xml + }) || xml; + + return resolve({ + xml: xml + }); + }); + }).catch(function(error) { + return { error: error }; + }).then(function(result) { + + self._emit('saveXML.done', result); + + var error = result.error; + + if (error) { + return Promise.reject(error); + } + + return result; + }); + }); + + /** + * The saveSVG result. + * + * @typedef {Object} SaveSVGResult + * + * @property {string} svg + */ + + /** + * Export the currently displayed BPMN 2.0 diagram as + * an SVG image. + * + * ## Life-Cycle Events + * + * During SVG saving the viewer will fire life-cycle events: + * + * * saveSVG.start (before serialization) + * * saveSVG.done (everything done) + * + * You can use these events to hook into the life-cycle. + * + * @param {Object} [options] + * + * Returns {Promise} + */ + BaseViewer.prototype.saveSVG = wrapForCompatibility(function saveSVG(options) { + + var self = this; + + return new Promise(function(resolve, reject) { + + self._emit('saveSVG.start'); + + var svg, err; + + try { + var canvas = self.get('canvas'); + + var contentNode = canvas.getActiveLayer(), + defsNode = query('defs', canvas._svg); + + var contents = innerSVG(contentNode), + defs = defsNode ? '' + innerSVG(defsNode) + '' : ''; + + var bbox = contentNode.getBBox(); + + svg = + '\n' + + '\n' + + '\n' + + '' + + defs + contents + + ''; + } catch (e) { + err = e; + } + + self._emit('saveSVG.done', { + error: err, + svg: svg + }); + + if (!err) { + return resolve({ svg: svg }); + } + + return reject(err); + }); + }); + + /** + * Get a named diagram service. + * + * @example + * + * var elementRegistry = viewer.get('elementRegistry'); + * var startEventShape = elementRegistry.get('StartEvent_1'); + * + * @param {string} name + * + * @return {Object} diagram service instance + * + * @method BaseViewer#get + */ + + /** + * Invoke a function in the context of this viewer. + * + * @example + * + * viewer.invoke(function(elementRegistry) { + * var startEventShape = elementRegistry.get('StartEvent_1'); + * }); + * + * @param {Function} fn to be invoked + * + * @return {Object} the functions return value + * + * @method BaseViewer#invoke + */ + + + BaseViewer.prototype._setDefinitions = function(definitions) { + this._definitions = definitions; + }; + + BaseViewer.prototype.getModules = function() { + return this._modules; + }; + + /** + * Remove all drawn elements from the viewer. + * + * After calling this method the viewer can still + * be reused for opening another diagram. + * + * @method BaseViewer#clear + */ + BaseViewer.prototype.clear = function() { + if (!this.getDefinitions()) { + + // no diagram to clear + return; + } + + // remove drawn elements + Diagram.prototype.clear.call(this); + }; + + /** + * Destroy the viewer instance and remove all its + * remainders from the document tree. + */ + BaseViewer.prototype.destroy = function() { + + // diagram destroy + Diagram.prototype.destroy.call(this); + + // dom detach + remove$2(this._container); + }; + + /** + * Register an event listener + * + * Remove a previously added listener via {@link #off(event, callback)}. + * + * @param {string} event + * @param {number} [priority] + * @param {Function} callback + * @param {Object} [that] + */ + BaseViewer.prototype.on = function(event, priority, callback, target) { + return this.get('eventBus').on(event, priority, callback, target); + }; + + /** + * De-register an event listener + * + * @param {string} event + * @param {Function} callback + */ + BaseViewer.prototype.off = function(event, callback) { + this.get('eventBus').off(event, callback); + }; + + BaseViewer.prototype.attachTo = function(parentNode) { + + if (!parentNode) { + throw new Error('parentNode required'); + } + + // ensure we detach from the + // previous, old parent + this.detach(); + + // unwrap jQuery if provided + if (parentNode.get && parentNode.constructor.prototype.jquery) { + parentNode = parentNode.get(0); + } + + if (typeof parentNode === 'string') { + parentNode = query(parentNode); + } + + parentNode.appendChild(this._container); + + this._emit('attach', {}); + + this.get('canvas').resized(); + }; + + BaseViewer.prototype.getDefinitions = function() { + return this._definitions; + }; + + BaseViewer.prototype.detach = function() { + + var container = this._container, + parentNode = container.parentNode; + + if (!parentNode) { + return; + } + + this._emit('detach', {}); + + parentNode.removeChild(container); + }; + + BaseViewer.prototype._init = function(container, moddle, options) { + + var baseModules = options.modules || this.getModules(), + additionalModules = options.additionalModules || [], + staticModules = [ + { + bpmnjs: [ 'value', this ], + moddle: [ 'value', moddle ] + } + ]; + + var diagramModules = [].concat(staticModules, baseModules, additionalModules); + + var diagramOptions = assign(omit(options, [ 'additionalModules' ]), { + canvas: assign({}, options.canvas, { container: container }), + modules: diagramModules + }); + + // invoke diagram constructor + Diagram.call(this, diagramOptions); + + if (options && options.container) { + this.attachTo(options.container); + } + }; + + /** + * Emit an event on the underlying {@link EventBus} + * + * @param {string} type + * @param {Object} event + * + * @return {Object} event processing result (if any) + */ + BaseViewer.prototype._emit = function(type, event) { + return this.get('eventBus').fire(type, event); + }; + + BaseViewer.prototype._createContainer = function(options) { + + var container = domify('
'); + + assign$1(container, { + width: ensureUnit(options.width), + height: ensureUnit(options.height), + position: options.position + }); + + return container; + }; + + BaseViewer.prototype._createModdle = function(options) { + var moddleOptions = assign({}, this._moddleExtensions, options.moddleExtensions); + + return new simple(moddleOptions); + }; + + BaseViewer.prototype._modules = []; + + // helpers /////////////// + + function addWarningsToError(err, warningsAry) { + err.warnings = warningsAry; + return err; + } + + function checkValidationError(err) { + + // check if we can help the user by indicating wrong BPMN 2.0 xml + // (in case he or the exporting tool did not get that right) + + var pattern = /unparsable content <([^>]+)> detected([\s\S]*)$/; + var match = pattern.exec(err.message); + + if (match) { + err.message = + 'unparsable content <' + match[1] + '> detected; ' + + 'this may indicate an invalid BPMN 2.0 diagram file' + match[2]; + } + + return err; + } + + var DEFAULT_OPTIONS = { + width: '100%', + height: '100%', + position: 'relative' + }; + + + /** + * Ensure the passed argument is a proper unit (defaulting to px) + */ + function ensureUnit(val) { + return val + (isNumber(val) ? 'px' : ''); + } + + + /** + * Find BPMNDiagram in definitions by ID + * + * @param {ModdleElement} definitions + * @param {string} diagramId + * + * @return {ModdleElement|null} + */ + function findBPMNDiagram(definitions, diagramId) { + if (!diagramId) { + return null; + } + + return find(definitions.diagrams, function(element) { + return element.id === diagramId; + }) || null; + } + + /** + * Adds the project logo to the diagram container as + * required by the bpmn.io license. + * + * @see http://bpmn.io/license + * + * @param {Element} container + */ + function addProjectLogo(container) { + var img = BPMNIO_IMG; + + var linkMarkup = + '' + + img + + ''; + + var linkElement = domify(linkMarkup); + + assign$1(query('svg', linkElement), LOGO_STYLES); + assign$1(linkElement, LINK_STYLES, { + position: 'absolute', + bottom: '15px', + right: '15px', + zIndex: '100' + }); + + container.appendChild(linkElement); + + componentEvent.bind(linkElement, 'click', function(event) { + open(); + + event.preventDefault(); + }); + } + + /* */ + + /** + * A base modeler for BPMN 2.0 diagrams. + * + * Have a look at {@link Modeler} for a bundle that includes actual features. + * + * @param {Object} [options] configuration options to pass to the viewer + * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body. + * @param {string|number} [options.width] the width of the viewer + * @param {string|number} [options.height] the height of the viewer + * @param {Object} [options.moddleExtensions] extension packages to provide + * @param {Array} [options.modules] a list of modules to override the default modules + * @param {Array} [options.additionalModules] a list of modules to use with the default modules + */ + function BaseModeler(options) { + BaseViewer.call(this, options); + + // hook ID collection into the modeler + this.on('import.parse.complete', function(event) { + if (!event.error) { + this._collectIds(event.definitions, event.elementsById); + } + }, this); + + this.on('diagram.destroy', function() { + this.get('moddle').ids.clear(); + }, this); + } + + e(BaseModeler, BaseViewer); + + + /** + * Create a moddle instance, attaching ids to it. + * + * @param {Object} options + */ + BaseModeler.prototype._createModdle = function(options) { + var moddle = BaseViewer.prototype._createModdle.call(this, options); + + // attach ids to moddle to be able to track + // and validated ids in the BPMN 2.0 XML document + // tree + moddle.ids = new Ids([ 32, 36, 1 ]); + + return moddle; + }; + + /** + * Collect ids processed during parsing of the + * definitions object. + * + * @param {ModdleElement} definitions + * @param {Context} context + */ + BaseModeler.prototype._collectIds = function(definitions, elementsById) { + + var moddle = definitions.$model, + ids = moddle.ids, + id; + + // remove references from previous import + ids.clear(); + + for (id in elementsById) { + ids.claim(id, elementsById[id]); + } + }; + + function isExpanded(element, di) { + + if (is$1(element, 'bpmn:CallActivity')) { + return false; + } + + if (is$1(element, 'bpmn:SubProcess')) { + di = di || getDi(element); + + if (di && is$1(di, 'bpmndi:BPMNPlane')) { + return true; + } + + return di && !!di.isExpanded; + } + + if (is$1(element, 'bpmn:Participant')) { + return !!getBusinessObject(element).processRef; + } + + return true; + } + + function isInterrupting(element) { + return element && getBusinessObject(element).isInterrupting !== false; + } + + function isEventSubProcess(element) { + return element && !!getBusinessObject(element).triggeredByEvent; + } + + function hasEventDefinition$2(element, eventType) { + var bo = getBusinessObject(element), + hasEventDefinition = false; + + if (bo.eventDefinitions) { + forEach$1(bo.eventDefinitions, function(event) { + if (is$1(event, eventType)) { + hasEventDefinition = true; + } + }); + } + + return hasEventDefinition; + } + + function hasErrorEventDefinition(element) { + return hasEventDefinition$2(element, 'bpmn:ErrorEventDefinition'); + } + + function hasEscalationEventDefinition(element) { + return hasEventDefinition$2(element, 'bpmn:EscalationEventDefinition'); + } + + function hasCompensateEventDefinition(element) { + return hasEventDefinition$2(element, 'bpmn:CompensateEventDefinition'); + } + + function getLabelAttr(semantic) { + if ( + is$1(semantic, 'bpmn:FlowElement') || + is$1(semantic, 'bpmn:Participant') || + is$1(semantic, 'bpmn:Lane') || + is$1(semantic, 'bpmn:SequenceFlow') || + is$1(semantic, 'bpmn:MessageFlow') || + is$1(semantic, 'bpmn:DataInput') || + is$1(semantic, 'bpmn:DataOutput') + ) { + return 'name'; + } + + if (is$1(semantic, 'bpmn:TextAnnotation')) { + return 'text'; + } + + if (is$1(semantic, 'bpmn:Group')) { + return 'categoryValueRef'; + } + } + + function getCategoryValue(semantic) { + var categoryValueRef = semantic['categoryValueRef']; + + if (!categoryValueRef) { + return ''; + } + + + return categoryValueRef.value || ''; + } + + function getLabel(element) { + var semantic = element.businessObject, + attr = getLabelAttr(semantic); + + if (attr) { + + if (attr === 'categoryValueRef') { + + return getCategoryValue(semantic); + } + + return semantic[attr] || ''; + } + } + + + function setLabel(element, text, isExternal) { + var semantic = element.businessObject, + attr = getLabelAttr(semantic); + + if (attr) { + + if (attr === 'categoryValueRef') { + semantic['categoryValueRef'].value = text; + } else { + semantic[attr] = text; + } + + } + + return element; + } + + var black = 'hsl(225, 10%, 15%)'; + + // element utils ////////////////////// + + /** + * Checks if eventDefinition of the given element matches with semantic type. + * + * @return {boolean} true if element is of the given semantic type + */ + function isTypedEvent(event, eventDefinitionType, filter) { + + function matches(definition, filter) { + return every(filter, function(val, key) { + + // we want a == conversion here, to be able to catch + // undefined == false and friends + /* jshint -W116 */ + return definition[key] == val; + }); + } + + return some(event.eventDefinitions, function(definition) { + return definition.$type === eventDefinitionType && matches(event, filter); + }); + } + + function isThrowEvent(event) { + return (event.$type === 'bpmn:IntermediateThrowEvent') || (event.$type === 'bpmn:EndEvent'); + } + + function isCollection(element) { + var dataObject = element.dataObjectRef; + + return element.isCollection || (dataObject && dataObject.isCollection); + } + + function getSemantic(element) { + return element.businessObject; + } + + + // color access ////////////////////// + + function getFillColor(element, defaultColor) { + var di = getDi(element); + + return di.get('color:background-color') || di.get('bioc:fill') || defaultColor || 'white'; + } + + function getStrokeColor$1(element, defaultColor) { + var di = getDi(element); + + return di.get('color:border-color') || di.get('bioc:stroke') || defaultColor || black; + } + + function getLabelColor(element, defaultColor, defaultStrokeColor) { + var di = getDi(element), + label = di.get('label'); + + return label && label.get('color:color') || defaultColor || + getStrokeColor$1(element, defaultStrokeColor); + } + + // cropping path customizations ////////////////////// + + function getCirclePath(shape) { + + var cx = shape.x + shape.width / 2, + cy = shape.y + shape.height / 2, + radius = shape.width / 2; + + var circlePath = [ + [ 'M', cx, cy ], + [ 'm', 0, -radius ], + [ 'a', radius, radius, 0, 1, 1, 0, 2 * radius ], + [ 'a', radius, radius, 0, 1, 1, 0, -2 * radius ], + [ 'z' ] + ]; + + return componentsToPath(circlePath); + } + + function getRoundRectPath(shape, borderRadius) { + + var x = shape.x, + y = shape.y, + width = shape.width, + height = shape.height; + + var roundRectPath = [ + [ 'M', x + borderRadius, y ], + [ 'l', width - borderRadius * 2, 0 ], + [ 'a', borderRadius, borderRadius, 0, 0, 1, borderRadius, borderRadius ], + [ 'l', 0, height - borderRadius * 2 ], + [ 'a', borderRadius, borderRadius, 0, 0, 1, -borderRadius, borderRadius ], + [ 'l', borderRadius * 2 - width, 0 ], + [ 'a', borderRadius, borderRadius, 0, 0, 1, -borderRadius, -borderRadius ], + [ 'l', 0, borderRadius * 2 - height ], + [ 'a', borderRadius, borderRadius, 0, 0, 1, borderRadius, -borderRadius ], + [ 'z' ] + ]; + + return componentsToPath(roundRectPath); + } + + function getDiamondPath(shape) { + + var width = shape.width, + height = shape.height, + x = shape.x, + y = shape.y, + halfWidth = width / 2, + halfHeight = height / 2; + + var diamondPath = [ + [ 'M', x + halfWidth, y ], + [ 'l', halfWidth, halfHeight ], + [ 'l', -halfWidth, halfHeight ], + [ 'l', -halfWidth, -halfHeight ], + [ 'z' ] + ]; + + return componentsToPath(diamondPath); + } + + function getRectPath(shape) { + var x = shape.x, + y = shape.y, + width = shape.width, + height = shape.height; + + var rectPath = [ + [ 'M', x, y ], + [ 'l', width, 0 ], + [ 'l', 0, height ], + [ 'l', -width, 0 ], + [ 'z' ] + ]; + + return componentsToPath(rectPath); + } + + var RENDERER_IDS = new Ids(); + + var TASK_BORDER_RADIUS = 10; + var INNER_OUTER_DIST = 3; + + var DEFAULT_FILL_OPACITY = .95, + HIGH_FILL_OPACITY = .35; + + var ELEMENT_LABEL_DISTANCE$1 = 10; + + function BpmnRenderer( + config, eventBus, styles, pathMap, + canvas, textRenderer, priority) { + + BaseRenderer.call(this, eventBus, priority); + + var defaultFillColor = config && config.defaultFillColor, + defaultStrokeColor = config && config.defaultStrokeColor, + defaultLabelColor = config && config.defaultLabelColor; + + var rendererId = RENDERER_IDS.next(); + + var markers = {}; + + var computeStyle = styles.computeStyle; + + function addMarker(id, options) { + var attrs = assign({ + fill: black, + strokeWidth: 1, + strokeLinecap: 'round', + strokeDasharray: 'none' + }, options.attrs); + + var ref = options.ref || { x: 0, y: 0 }; + + var scale = options.scale || 1; + + // fix for safari / chrome / firefox bug not correctly + // resetting stroke dash array + if (attrs.strokeDasharray === 'none') { + attrs.strokeDasharray = [ 10000, 1 ]; + } + + var marker = create$1('marker'); + + attr(options.element, attrs); + + append(marker, options.element); + + attr(marker, { + id: id, + viewBox: '0 0 20 20', + refX: ref.x, + refY: ref.y, + markerWidth: 20 * scale, + markerHeight: 20 * scale, + orient: 'auto' + }); + + var defs = query('defs', canvas._svg); + + if (!defs) { + defs = create$1('defs'); + + append(canvas._svg, defs); + } + + append(defs, marker); + + markers[id] = marker; + } + + function colorEscape(str) { + + // only allow characters and numbers + return str.replace(/[^0-9a-zA-z]+/g, '_'); + } + + function marker(type, fill, stroke) { + var id = type + '-' + colorEscape(fill) + '-' + colorEscape(stroke) + '-' + rendererId; + + if (!markers[id]) { + createMarker(id, type, fill, stroke); + } + + return 'url(#' + id + ')'; + } + + function createMarker(id, type, fill, stroke) { + + if (type === 'sequenceflow-end') { + var sequenceflowEnd = create$1('path'); + attr(sequenceflowEnd, { d: 'M 1 5 L 11 10 L 1 15 Z' }); + + addMarker(id, { + element: sequenceflowEnd, + ref: { x: 11, y: 10 }, + scale: 0.5, + attrs: { + fill: stroke, + stroke: stroke + } + }); + } + + if (type === 'messageflow-start') { + var messageflowStart = create$1('circle'); + attr(messageflowStart, { cx: 6, cy: 6, r: 3.5 }); + + addMarker(id, { + element: messageflowStart, + attrs: { + fill: fill, + stroke: stroke + }, + ref: { x: 6, y: 6 } + }); + } + + if (type === 'messageflow-end') { + var messageflowEnd = create$1('path'); + attr(messageflowEnd, { d: 'm 1 5 l 0 -3 l 7 3 l -7 3 z' }); + + addMarker(id, { + element: messageflowEnd, + attrs: { + fill: fill, + stroke: stroke, + strokeLinecap: 'butt' + }, + ref: { x: 8.5, y: 5 } + }); + } + + if (type === 'association-start') { + var associationStart = create$1('path'); + attr(associationStart, { d: 'M 11 5 L 1 10 L 11 15' }); + + addMarker(id, { + element: associationStart, + attrs: { + fill: 'none', + stroke: stroke, + strokeWidth: 1.5 + }, + ref: { x: 1, y: 10 }, + scale: 0.5 + }); + } + + if (type === 'association-end') { + var associationEnd = create$1('path'); + attr(associationEnd, { d: 'M 1 5 L 11 10 L 1 15' }); + + addMarker(id, { + element: associationEnd, + attrs: { + fill: 'none', + stroke: stroke, + strokeWidth: 1.5 + }, + ref: { x: 12, y: 10 }, + scale: 0.5 + }); + } + + if (type === 'conditional-flow-marker') { + var conditionalflowMarker = create$1('path'); + attr(conditionalflowMarker, { d: 'M 0 10 L 8 6 L 16 10 L 8 14 Z' }); + + addMarker(id, { + element: conditionalflowMarker, + attrs: { + fill: fill, + stroke: stroke + }, + ref: { x: -1, y: 10 }, + scale: 0.5 + }); + } + + if (type === 'conditional-default-flow-marker') { + var conditionaldefaultflowMarker = create$1('path'); + attr(conditionaldefaultflowMarker, { d: 'M 6 4 L 10 16' }); + + addMarker(id, { + element: conditionaldefaultflowMarker, + attrs: { + stroke: stroke + }, + ref: { x: 0, y: 10 }, + scale: 0.5 + }); + } + } + + function drawCircle(parentGfx, width, height, offset, attrs) { + + if (isObject(offset)) { + attrs = offset; + offset = 0; + } + + offset = offset || 0; + + attrs = computeStyle(attrs, { + stroke: black, + strokeWidth: 2, + fill: 'white' + }); + + if (attrs.fill === 'none') { + delete attrs.fillOpacity; + } + + var cx = width / 2, + cy = height / 2; + + var circle = create$1('circle'); + attr(circle, { + cx: cx, + cy: cy, + r: Math.round((width + height) / 4 - offset) + }); + attr(circle, attrs); + + append(parentGfx, circle); + + return circle; + } + + function drawRect(parentGfx, width, height, r, offset, attrs) { + + if (isObject(offset)) { + attrs = offset; + offset = 0; + } + + offset = offset || 0; + + attrs = computeStyle(attrs, { + stroke: black, + strokeWidth: 2, + fill: 'white' + }); + + var rect = create$1('rect'); + attr(rect, { + x: offset, + y: offset, + width: width - offset * 2, + height: height - offset * 2, + rx: r, + ry: r + }); + attr(rect, attrs); + + append(parentGfx, rect); + + return rect; + } + + function drawDiamond(parentGfx, width, height, attrs) { + + var x_2 = width / 2; + var y_2 = height / 2; + + var points = [ { x: x_2, y: 0 }, { x: width, y: y_2 }, { x: x_2, y: height }, { x: 0, y: y_2 } ]; + + var pointsString = points.map(function(point) { + return point.x + ',' + point.y; + }).join(' '); + + attrs = computeStyle(attrs, { + stroke: black, + strokeWidth: 2, + fill: 'white' + }); + + var polygon = create$1('polygon'); + attr(polygon, { + points: pointsString + }); + attr(polygon, attrs); + + append(parentGfx, polygon); + + return polygon; + } + + function drawLine(parentGfx, waypoints, attrs) { + attrs = computeStyle(attrs, [ 'no-fill' ], { + stroke: black, + strokeWidth: 2, + fill: 'none' + }); + + var line = createLine(waypoints, attrs); + + append(parentGfx, line); + + return line; + } + + function drawPath(parentGfx, d, attrs) { + + attrs = computeStyle(attrs, [ 'no-fill' ], { + strokeWidth: 2, + stroke: black + }); + + var path = create$1('path'); + attr(path, { d: d }); + attr(path, attrs); + + append(parentGfx, path); + + return path; + } + + function drawMarker(type, parentGfx, path, attrs) { + return drawPath(parentGfx, path, assign({ 'data-marker': type }, attrs)); + } + + function renderer(type) { + return handlers[type]; + } + + function as(type) { + return function(parentGfx, element) { + return renderer(type)(parentGfx, element); + }; + } + + function renderEventContent(element, parentGfx) { + + var event = getSemantic(element); + var isThrowing = isThrowEvent(event); + + if (event.eventDefinitions && event.eventDefinitions.length > 1) { + if (event.parallelMultiple) { + return renderer('bpmn:ParallelMultipleEventDefinition')(parentGfx, element, isThrowing); + } + else { + return renderer('bpmn:MultipleEventDefinition')(parentGfx, element, isThrowing); + } + } + + if (isTypedEvent(event, 'bpmn:MessageEventDefinition')) { + return renderer('bpmn:MessageEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:TimerEventDefinition')) { + return renderer('bpmn:TimerEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:ConditionalEventDefinition')) { + return renderer('bpmn:ConditionalEventDefinition')(parentGfx, element); + } + + if (isTypedEvent(event, 'bpmn:SignalEventDefinition')) { + return renderer('bpmn:SignalEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:EscalationEventDefinition')) { + return renderer('bpmn:EscalationEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:LinkEventDefinition')) { + return renderer('bpmn:LinkEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:ErrorEventDefinition')) { + return renderer('bpmn:ErrorEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:CancelEventDefinition')) { + return renderer('bpmn:CancelEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:CompensateEventDefinition')) { + return renderer('bpmn:CompensateEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:TerminateEventDefinition')) { + return renderer('bpmn:TerminateEventDefinition')(parentGfx, element, isThrowing); + } + + return null; + } + + function renderLabel(parentGfx, label, options) { + + options = assign({ + size: { + width: 100 + } + }, options); + + var text = textRenderer.createText(label || '', options); + + classes(text).add('djs-label'); + + append(parentGfx, text); + + return text; + } + + function renderEmbeddedLabel(parentGfx, element, align) { + var semantic = getSemantic(element); + + return renderLabel(parentGfx, semantic.name, { + box: element, + align: align, + padding: 5, + style: { + fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor) + } + }); + } + + function renderExternalLabel(parentGfx, element) { + + var box = { + width: 90, + height: 30, + x: element.width / 2 + element.x, + y: element.height / 2 + element.y + }; + + return renderLabel(parentGfx, getLabel(element), { + box: box, + fitBox: true, + style: assign( + {}, + textRenderer.getExternalStyle(), + { + fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor) + } + ) + }); + } + + function renderLaneLabel(parentGfx, text, element) { + var textBox = renderLabel(parentGfx, text, { + box: { + height: 30, + width: element.height + }, + align: 'center-middle', + style: { + fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor) + } + }); + + var top = -1 * element.height; + + transform(textBox, 0, -top, 270); + } + + function createPathFromConnection(connection) { + var waypoints = connection.waypoints; + + var pathData = 'm ' + waypoints[0].x + ',' + waypoints[0].y; + for (var i = 1; i < waypoints.length; i++) { + pathData += 'L' + waypoints[i].x + ',' + waypoints[i].y + ' '; + } + return pathData; + } + + var handlers = this.handlers = { + 'bpmn:Event': function(parentGfx, element, attrs) { + + if (!('fillOpacity' in attrs)) { + attrs.fillOpacity = DEFAULT_FILL_OPACITY; + } + + return drawCircle(parentGfx, element.width, element.height, attrs); + }, + 'bpmn:StartEvent': function(parentGfx, element) { + var attrs = { + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }; + + var semantic = getSemantic(element); + + if (!semantic.isInterrupting) { + attrs = { + strokeDasharray: '6', + strokeLinecap: 'round', + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }; + } + + var circle = renderer('bpmn:Event')(parentGfx, element, attrs); + + renderEventContent(element, parentGfx); + + return circle; + }, + 'bpmn:MessageEventDefinition': function(parentGfx, element, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_MESSAGE', { + xScaleFactor: 0.9, + yScaleFactor: 0.9, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.235, + my: 0.315 + } + }); + + var fill = isThrowing ? getStrokeColor$1(element, defaultStrokeColor) : getFillColor(element, defaultFillColor); + var stroke = isThrowing ? getFillColor(element, defaultFillColor) : getStrokeColor$1(element, defaultStrokeColor); + + var messagePath = drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: stroke + }); + + return messagePath; + }, + 'bpmn:TimerEventDefinition': function(parentGfx, element) { + var circle = drawCircle(parentGfx, element.width, element.height, 0.2 * element.height, { + strokeWidth: 2, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + var pathData = pathMap.getScaledPath('EVENT_TIMER_WH', { + xScaleFactor: 0.75, + yScaleFactor: 0.75, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.5, + my: 0.5 + } + }); + + drawPath(parentGfx, pathData, { + strokeWidth: 2, + strokeLinecap: 'square', + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + for (var i = 0;i < 12; i++) { + + var linePathData = pathMap.getScaledPath('EVENT_TIMER_LINE', { + xScaleFactor: 0.75, + yScaleFactor: 0.75, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.5, + my: 0.5 + } + }); + + var width = element.width / 2; + var height = element.height / 2; + + drawPath(parentGfx, linePathData, { + strokeWidth: 1, + strokeLinecap: 'square', + transform: 'rotate(' + (i * 30) + ',' + height + ',' + width + ')', + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + } + + return circle; + }, + 'bpmn:EscalationEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_ESCALATION', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.5, + my: 0.2 + } + }); + + var fill = isThrowing ? getStrokeColor$1(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor$1(event, defaultStrokeColor) + }); + }, + 'bpmn:ConditionalEventDefinition': function(parentGfx, event) { + var pathData = pathMap.getScaledPath('EVENT_CONDITIONAL', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.5, + my: 0.222 + } + }); + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + stroke: getStrokeColor$1(event, defaultStrokeColor) + }); + }, + 'bpmn:LinkEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_LINK', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.57, + my: 0.263 + } + }); + + var fill = isThrowing ? getStrokeColor$1(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor$1(event, defaultStrokeColor) + }); + }, + 'bpmn:ErrorEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_ERROR', { + xScaleFactor: 1.1, + yScaleFactor: 1.1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.2, + my: 0.722 + } + }); + + var fill = isThrowing ? getStrokeColor$1(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor$1(event, defaultStrokeColor) + }); + }, + 'bpmn:CancelEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_CANCEL_45', { + xScaleFactor: 1.0, + yScaleFactor: 1.0, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.638, + my: -0.055 + } + }); + + var fill = isThrowing ? getStrokeColor$1(event, defaultStrokeColor) : 'none'; + + var path = drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor$1(event, defaultStrokeColor) + }); + + rotate(path, 45); + + return path; + }, + 'bpmn:CompensateEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_COMPENSATION', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.22, + my: 0.5 + } + }); + + var fill = isThrowing ? getStrokeColor$1(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor$1(event, defaultStrokeColor) + }); + }, + 'bpmn:SignalEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_SIGNAL', { + xScaleFactor: 0.9, + yScaleFactor: 0.9, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.5, + my: 0.2 + } + }); + + var fill = isThrowing ? getStrokeColor$1(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor$1(event, defaultStrokeColor) + }); + }, + 'bpmn:MultipleEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_MULTIPLE', { + xScaleFactor: 1.1, + yScaleFactor: 1.1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.222, + my: 0.36 + } + }); + + var fill = isThrowing ? getStrokeColor$1(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill + }); + }, + 'bpmn:ParallelMultipleEventDefinition': function(parentGfx, event) { + var pathData = pathMap.getScaledPath('EVENT_PARALLEL_MULTIPLE', { + xScaleFactor: 1.2, + yScaleFactor: 1.2, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.458, + my: 0.194 + } + }); + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getStrokeColor$1(event, defaultStrokeColor), + stroke: getStrokeColor$1(event, defaultStrokeColor) + }); + }, + 'bpmn:EndEvent': function(parentGfx, element) { + var circle = renderer('bpmn:Event')(parentGfx, element, { + strokeWidth: 4, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + renderEventContent(element, parentGfx); + + return circle; + }, + 'bpmn:TerminateEventDefinition': function(parentGfx, element) { + var circle = drawCircle(parentGfx, element.width, element.height, 8, { + strokeWidth: 4, + fill: getStrokeColor$1(element, defaultStrokeColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + return circle; + }, + 'bpmn:IntermediateEvent': function(parentGfx, element) { + var outer = renderer('bpmn:Event')(parentGfx, element, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + /* inner */ + drawCircle(parentGfx, element.width, element.height, INNER_OUTER_DIST, { + strokeWidth: 1, + fill: getFillColor(element, 'none'), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + renderEventContent(element, parentGfx); + + return outer; + }, + 'bpmn:IntermediateCatchEvent': as('bpmn:IntermediateEvent'), + 'bpmn:IntermediateThrowEvent': as('bpmn:IntermediateEvent'), + + 'bpmn:Activity': function(parentGfx, element, attrs) { + + attrs = attrs || {}; + + if (!('fillOpacity' in attrs)) { + attrs.fillOpacity = DEFAULT_FILL_OPACITY; + } + + return drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS, attrs); + }, + + 'bpmn:Task': function(parentGfx, element) { + var attrs = { + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }; + + var rect = renderer('bpmn:Activity')(parentGfx, element, attrs); + + renderEmbeddedLabel(parentGfx, element, 'center-middle'); + attachTaskMarkers(parentGfx, element); + + return rect; + }, + 'bpmn:ServiceTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var pathDataBG = pathMap.getScaledPath('TASK_TYPE_SERVICE', { + abspos: { + x: 12, + y: 18 + } + }); + + /* service bg */ drawPath(parentGfx, pathDataBG, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + var fillPathData = pathMap.getScaledPath('TASK_TYPE_SERVICE_FILL', { + abspos: { + x: 17.2, + y: 18 + } + }); + + /* service fill */ drawPath(parentGfx, fillPathData, { + strokeWidth: 0, + fill: getFillColor(element, defaultFillColor) + }); + + var pathData = pathMap.getScaledPath('TASK_TYPE_SERVICE', { + abspos: { + x: 17, + y: 22 + } + }); + + /* service */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:UserTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var x = 15; + var y = 12; + + var pathData = pathMap.getScaledPath('TASK_TYPE_USER_1', { + abspos: { + x: x, + y: y + } + }); + + /* user path */ drawPath(parentGfx, pathData, { + strokeWidth: 0.5, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + var pathData2 = pathMap.getScaledPath('TASK_TYPE_USER_2', { + abspos: { + x: x, + y: y + } + }); + + /* user2 path */ drawPath(parentGfx, pathData2, { + strokeWidth: 0.5, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + var pathData3 = pathMap.getScaledPath('TASK_TYPE_USER_3', { + abspos: { + x: x, + y: y + } + }); + + /* user3 path */ drawPath(parentGfx, pathData3, { + strokeWidth: 0.5, + fill: getStrokeColor$1(element, defaultStrokeColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:ManualTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var pathData = pathMap.getScaledPath('TASK_TYPE_MANUAL', { + abspos: { + x: 17, + y: 15 + } + }); + + /* manual path */ drawPath(parentGfx, pathData, { + strokeWidth: 0.5, // 0.25, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:SendTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var pathData = pathMap.getScaledPath('TASK_TYPE_SEND', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: 21, + containerHeight: 14, + position: { + mx: 0.285, + my: 0.357 + } + }); + + /* send path */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getStrokeColor$1(element, defaultStrokeColor), + stroke: getFillColor(element, defaultFillColor) + }); + + return task; + }, + 'bpmn:ReceiveTask' : function(parentGfx, element) { + var semantic = getSemantic(element); + + var task = renderer('bpmn:Task')(parentGfx, element); + var pathData; + + if (semantic.instantiate) { + drawCircle(parentGfx, 28, 28, 20 * 0.22, { strokeWidth: 1 }); + + pathData = pathMap.getScaledPath('TASK_TYPE_INSTANTIATING_SEND', { + abspos: { + x: 7.77, + y: 9.52 + } + }); + } else { + + pathData = pathMap.getScaledPath('TASK_TYPE_SEND', { + xScaleFactor: 0.9, + yScaleFactor: 0.9, + containerWidth: 21, + containerHeight: 14, + position: { + mx: 0.3, + my: 0.4 + } + }); + } + + /* receive path */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:ScriptTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var pathData = pathMap.getScaledPath('TASK_TYPE_SCRIPT', { + abspos: { + x: 15, + y: 20 + } + }); + + /* script path */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:BusinessRuleTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var headerPathData = pathMap.getScaledPath('TASK_TYPE_BUSINESS_RULE_HEADER', { + abspos: { + x: 8, + y: 8 + } + }); + + var businessHeaderPath = drawPath(parentGfx, headerPathData); + attr(businessHeaderPath, { + strokeWidth: 1, + fill: getFillColor(element, '#aaaaaa'), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + var headerData = pathMap.getScaledPath('TASK_TYPE_BUSINESS_RULE_MAIN', { + abspos: { + x: 8, + y: 8 + } + }); + + var businessPath = drawPath(parentGfx, headerData); + attr(businessPath, { + strokeWidth: 1, + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:SubProcess': function(parentGfx, element, attrs) { + attrs = assign({ + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }, attrs); + + var rect = renderer('bpmn:Activity')(parentGfx, element, attrs); + + var expanded = isExpanded(element); + + if (isEventSubProcess(element)) { + attr(rect, { + strokeDasharray: '1,2' + }); + } + + renderEmbeddedLabel(parentGfx, element, expanded ? 'center-top' : 'center-middle'); + + if (expanded) { + attachTaskMarkers(parentGfx, element); + } else { + attachTaskMarkers(parentGfx, element, [ 'SubProcessMarker' ]); + } + + return rect; + }, + 'bpmn:AdHocSubProcess': function(parentGfx, element) { + return renderer('bpmn:SubProcess')(parentGfx, element); + }, + 'bpmn:Transaction': function(parentGfx, element) { + var outer = renderer('bpmn:SubProcess')(parentGfx, element); + + var innerAttrs = styles.style([ 'no-fill', 'no-events' ], { + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + /* inner path */ drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS - 2, INNER_OUTER_DIST, innerAttrs); + + return outer; + }, + 'bpmn:CallActivity': function(parentGfx, element) { + return renderer('bpmn:SubProcess')(parentGfx, element, { + strokeWidth: 5 + }); + }, + 'bpmn:Participant': function(parentGfx, element) { + + var attrs = { + fillOpacity: DEFAULT_FILL_OPACITY, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }; + + var lane = renderer('bpmn:Lane')(parentGfx, element, attrs); + + var expandedPool = isExpanded(element); + + if (expandedPool) { + drawLine(parentGfx, [ + { x: 30, y: 0 }, + { x: 30, y: element.height } + ], { + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + var text = getSemantic(element).name; + renderLaneLabel(parentGfx, text, element); + } else { + + // Collapsed pool draw text inline + var text2 = getSemantic(element).name; + renderLabel(parentGfx, text2, { + box: element, align: 'center-middle', + style: { + fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor) + } + }); + } + + var participantMultiplicity = !!(getSemantic(element).participantMultiplicity); + + if (participantMultiplicity) { + renderer('ParticipantMultiplicityMarker')(parentGfx, element); + } + + return lane; + }, + 'bpmn:Lane': function(parentGfx, element, attrs) { + var rect = drawRect(parentGfx, element.width, element.height, 0, assign({ + fill: getFillColor(element, defaultFillColor), + fillOpacity: HIGH_FILL_OPACITY, + stroke: getStrokeColor$1(element, defaultStrokeColor) + }, attrs)); + + var semantic = getSemantic(element); + + if (semantic.$type === 'bpmn:Lane') { + var text = semantic.name; + renderLaneLabel(parentGfx, text, element); + } + + return rect; + }, + 'bpmn:InclusiveGateway': function(parentGfx, element) { + var diamond = renderer('bpmn:Gateway')(parentGfx, element); + + /* circle path */ + drawCircle(parentGfx, element.width, element.height, element.height * 0.24, { + strokeWidth: 2.5, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + return diamond; + }, + 'bpmn:ExclusiveGateway': function(parentGfx, element) { + var diamond = renderer('bpmn:Gateway')(parentGfx, element); + + var pathData = pathMap.getScaledPath('GATEWAY_EXCLUSIVE', { + xScaleFactor: 0.4, + yScaleFactor: 0.4, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.32, + my: 0.3 + } + }); + + if ((getDi(element).isMarkerVisible)) { + drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getStrokeColor$1(element, defaultStrokeColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + } + + return diamond; + }, + 'bpmn:ComplexGateway': function(parentGfx, element) { + var diamond = renderer('bpmn:Gateway')(parentGfx, element); + + var pathData = pathMap.getScaledPath('GATEWAY_COMPLEX', { + xScaleFactor: 0.5, + yScaleFactor:0.5, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.46, + my: 0.26 + } + }); + + /* complex path */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getStrokeColor$1(element, defaultStrokeColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + return diamond; + }, + 'bpmn:ParallelGateway': function(parentGfx, element) { + var diamond = renderer('bpmn:Gateway')(parentGfx, element); + + var pathData = pathMap.getScaledPath('GATEWAY_PARALLEL', { + xScaleFactor: 0.6, + yScaleFactor:0.6, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.46, + my: 0.2 + } + }); + + /* parallel path */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getStrokeColor$1(element, defaultStrokeColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + return diamond; + }, + 'bpmn:EventBasedGateway': function(parentGfx, element) { + + var semantic = getSemantic(element); + + var diamond = renderer('bpmn:Gateway')(parentGfx, element); + + /* outer circle path */ drawCircle(parentGfx, element.width, element.height, element.height * 0.20, { + strokeWidth: 1, + fill: 'none', + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + var type = semantic.eventGatewayType; + var instantiate = !!semantic.instantiate; + + function drawEvent() { + + var pathData = pathMap.getScaledPath('GATEWAY_EVENT_BASED', { + xScaleFactor: 0.18, + yScaleFactor: 0.18, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.36, + my: 0.44 + } + }); + + var attrs = { + strokeWidth: 2, + fill: getFillColor(element, 'none'), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }; + + /* event path */ drawPath(parentGfx, pathData, attrs); + } + + if (type === 'Parallel') { + + var pathData = pathMap.getScaledPath('GATEWAY_PARALLEL', { + xScaleFactor: 0.4, + yScaleFactor:0.4, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.474, + my: 0.296 + } + }); + + var parallelPath = drawPath(parentGfx, pathData); + attr(parallelPath, { + strokeWidth: 1, + fill: 'none' + }); + } else if (type === 'Exclusive') { + + if (!instantiate) { + var innerCircle = drawCircle(parentGfx, element.width, element.height, element.height * 0.26); + attr(innerCircle, { + strokeWidth: 1, + fill: 'none', + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + } + + drawEvent(); + } + + + return diamond; + }, + 'bpmn:Gateway': function(parentGfx, element) { + var attrs = { + fill: getFillColor(element, defaultFillColor), + fillOpacity: DEFAULT_FILL_OPACITY, + stroke: getStrokeColor$1(element, defaultStrokeColor) + }; + + return drawDiamond(parentGfx, element.width, element.height, attrs); + }, + 'bpmn:SequenceFlow': function(parentGfx, element) { + var pathData = createPathFromConnection(element); + + var fill = getFillColor(element, defaultFillColor), + stroke = getStrokeColor$1(element, defaultStrokeColor); + + var attrs = { + strokeLinejoin: 'round', + markerEnd: marker('sequenceflow-end', fill, stroke), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }; + + var path = drawPath(parentGfx, pathData, attrs); + + var sequenceFlow = getSemantic(element); + + var source; + + if (element.source) { + source = element.source.businessObject; + + // conditional flow marker + if (sequenceFlow.conditionExpression && source.$instanceOf('bpmn:Activity')) { + attr(path, { + markerStart: marker('conditional-flow-marker', fill, stroke) + }); + } + + // default marker + if (source.default && (source.$instanceOf('bpmn:Gateway') || source.$instanceOf('bpmn:Activity')) && + source.default === sequenceFlow) { + attr(path, { + markerStart: marker('conditional-default-flow-marker', fill, stroke) + }); + } + } + + return path; + }, + 'bpmn:Association': function(parentGfx, element, attrs) { + + var semantic = getSemantic(element); + + var fill = getFillColor(element, defaultFillColor), + stroke = getStrokeColor$1(element, defaultStrokeColor); + + attrs = assign({ + strokeDasharray: '0.5, 5', + strokeLinecap: 'round', + strokeLinejoin: 'round', + stroke: getStrokeColor$1(element, defaultStrokeColor) + }, attrs || {}); + + if (semantic.associationDirection === 'One' || + semantic.associationDirection === 'Both') { + attrs.markerEnd = marker('association-end', fill, stroke); + } + + if (semantic.associationDirection === 'Both') { + attrs.markerStart = marker('association-start', fill, stroke); + } + + return drawLine(parentGfx, element.waypoints, attrs); + }, + 'bpmn:DataInputAssociation': function(parentGfx, element) { + var fill = getFillColor(element, defaultFillColor), + stroke = getStrokeColor$1(element, defaultStrokeColor); + + return renderer('bpmn:Association')(parentGfx, element, { + markerEnd: marker('association-end', fill, stroke) + }); + }, + 'bpmn:DataOutputAssociation': function(parentGfx, element) { + var fill = getFillColor(element, defaultFillColor), + stroke = getStrokeColor$1(element, defaultStrokeColor); + + return renderer('bpmn:Association')(parentGfx, element, { + markerEnd: marker('association-end', fill, stroke) + }); + }, + 'bpmn:MessageFlow': function(parentGfx, element) { + + var semantic = getSemantic(element), + di = getDi(element); + + var fill = getFillColor(element, defaultFillColor), + stroke = getStrokeColor$1(element, defaultStrokeColor); + + var pathData = createPathFromConnection(element); + + var attrs = { + markerEnd: marker('messageflow-end', fill, stroke), + markerStart: marker('messageflow-start', fill, stroke), + strokeDasharray: '10, 12', + strokeLinecap: 'round', + strokeLinejoin: 'round', + strokeWidth: '1.5px', + stroke: getStrokeColor$1(element, defaultStrokeColor) + }; + + var path = drawPath(parentGfx, pathData, attrs); + + if (semantic.messageRef) { + var midPoint = path.getPointAtLength(path.getTotalLength() / 2); + + var markerPathData = pathMap.getScaledPath('MESSAGE_FLOW_MARKER', { + abspos: { + x: midPoint.x, + y: midPoint.y + } + }); + + var messageAttrs = { strokeWidth: 1 }; + + if (di.messageVisibleKind === 'initiating') { + messageAttrs.fill = 'white'; + messageAttrs.stroke = black; + } else { + messageAttrs.fill = '#888'; + messageAttrs.stroke = 'white'; + } + + var message = drawPath(parentGfx, markerPathData, messageAttrs); + + var labelText = semantic.messageRef.name; + var label = renderLabel(parentGfx, labelText, { + align: 'center-top', + fitBox: true, + style: { + fill: getStrokeColor$1(element, defaultLabelColor) + } + }); + + var messageBounds = message.getBBox(), + labelBounds = label.getBBox(); + + var translateX = midPoint.x - labelBounds.width / 2, + translateY = midPoint.y + messageBounds.height / 2 + ELEMENT_LABEL_DISTANCE$1; + + transform(label, translateX, translateY, 0); + + } + + return path; + }, + 'bpmn:DataObject': function(parentGfx, element) { + var pathData = pathMap.getScaledPath('DATA_OBJECT_PATH', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.474, + my: 0.296 + } + }); + + var elementObject = drawPath(parentGfx, pathData, { + fill: getFillColor(element, defaultFillColor), + fillOpacity: DEFAULT_FILL_OPACITY, + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + var semantic = getSemantic(element); + + if (isCollection(semantic)) { + renderDataItemCollection(parentGfx, element); + } + + return elementObject; + }, + 'bpmn:DataObjectReference': as('bpmn:DataObject'), + 'bpmn:DataInput': function(parentGfx, element) { + + var arrowPathData = pathMap.getRawPath('DATA_ARROW'); + + // page + var elementObject = renderer('bpmn:DataObject')(parentGfx, element); + + /* input arrow path */ drawPath(parentGfx, arrowPathData, { strokeWidth: 1 }); + + return elementObject; + }, + 'bpmn:DataOutput': function(parentGfx, element) { + var arrowPathData = pathMap.getRawPath('DATA_ARROW'); + + // page + var elementObject = renderer('bpmn:DataObject')(parentGfx, element); + + /* output arrow path */ drawPath(parentGfx, arrowPathData, { + strokeWidth: 1, + fill: black + }); + + return elementObject; + }, + 'bpmn:DataStoreReference': function(parentGfx, element) { + var DATA_STORE_PATH = pathMap.getScaledPath('DATA_STORE', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0, + my: 0.133 + } + }); + + var elementStore = drawPath(parentGfx, DATA_STORE_PATH, { + strokeWidth: 2, + fill: getFillColor(element, defaultFillColor), + fillOpacity: DEFAULT_FILL_OPACITY, + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + return elementStore; + }, + 'bpmn:BoundaryEvent': function(parentGfx, element) { + + var semantic = getSemantic(element), + cancel = semantic.cancelActivity; + + var attrs = { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }; + + if (!cancel) { + attrs.strokeDasharray = '6'; + attrs.strokeLinecap = 'round'; + } + + // apply fillOpacity + var outerAttrs = assign({}, attrs, { + fillOpacity: 1 + }); + + // apply no-fill + var innerAttrs = assign({}, attrs, { + fill: 'none' + }); + + var outer = renderer('bpmn:Event')(parentGfx, element, outerAttrs); + + /* inner path */ drawCircle(parentGfx, element.width, element.height, INNER_OUTER_DIST, innerAttrs); + + renderEventContent(element, parentGfx); + + return outer; + }, + 'bpmn:Group': function(parentGfx, element) { + + var group = drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS, { + stroke: getStrokeColor$1(element, defaultStrokeColor), + strokeWidth: 1, + strokeDasharray: '8,3,1,3', + fill: 'none', + pointerEvents: 'none' + }); + + return group; + }, + 'label': function(parentGfx, element) { + return renderExternalLabel(parentGfx, element); + }, + 'bpmn:TextAnnotation': function(parentGfx, element) { + var style = { + 'fill': 'none', + 'stroke': 'none' + }; + + var textElement = drawRect(parentGfx, element.width, element.height, 0, 0, style); + + var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.0, + my: 0.0 + } + }); + + drawPath(parentGfx, textPathData, { + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + var text = getSemantic(element).text || ''; + renderLabel(parentGfx, text, { + box: element, + align: 'left-top', + padding: 5, + style: { + fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor) + } + }); + + return textElement; + }, + 'ParticipantMultiplicityMarker': function(parentGfx, element) { + var markerPath = pathMap.getScaledPath('MARKER_PARALLEL', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2) / element.width), + my: (element.height - 15) / element.height + } + }); + + drawMarker('participant-multiplicity', parentGfx, markerPath, { + strokeWidth: 2, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + }, + 'SubProcessMarker': function(parentGfx, element) { + var markerRect = drawRect(parentGfx, 14, 14, 0, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + + // Process marker is placed in the middle of the box + // therefore fixed values can be used here + translate$2(markerRect, element.width / 2 - 7.5, element.height - 20); + + var markerPath = pathMap.getScaledPath('MARKER_SUB_PROCESS', { + xScaleFactor: 1.5, + yScaleFactor: 1.5, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: (element.width / 2 - 7.5) / element.width, + my: (element.height - 20) / element.height + } + }); + + drawMarker('sub-process', parentGfx, markerPath, { + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + }, + 'ParallelMarker': function(parentGfx, element, position) { + var markerPath = pathMap.getScaledPath('MARKER_PARALLEL', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2 + position.parallel) / element.width), + my: (element.height - 20) / element.height + } + }); + + drawMarker('parallel', parentGfx, markerPath, { + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + }, + 'SequentialMarker': function(parentGfx, element, position) { + var markerPath = pathMap.getScaledPath('MARKER_SEQUENTIAL', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2 + position.seq) / element.width), + my: (element.height - 19) / element.height + } + }); + + drawMarker('sequential', parentGfx, markerPath, { + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + }, + 'CompensationMarker': function(parentGfx, element, position) { + var markerMath = pathMap.getScaledPath('MARKER_COMPENSATION', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2 + position.compensation) / element.width), + my: (element.height - 13) / element.height + } + }); + + drawMarker('compensation', parentGfx, markerMath, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + }, + 'LoopMarker': function(parentGfx, element, position) { + var markerPath = pathMap.getScaledPath('MARKER_LOOP', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2 + position.loop) / element.width), + my: (element.height - 7) / element.height + } + }); + + drawMarker('loop', parentGfx, markerPath, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor$1(element, defaultStrokeColor), + strokeLinecap: 'round', + strokeMiterlimit: 0.5 + }); + }, + 'AdhocMarker': function(parentGfx, element, position) { + var markerPath = pathMap.getScaledPath('MARKER_ADHOC', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2 + position.adhoc) / element.width), + my: (element.height - 15) / element.height + } + }); + + drawMarker('adhoc', parentGfx, markerPath, { + strokeWidth: 1, + fill: getStrokeColor$1(element, defaultStrokeColor), + stroke: getStrokeColor$1(element, defaultStrokeColor) + }); + } + }; + + function attachTaskMarkers(parentGfx, element, taskMarkers) { + var obj = getSemantic(element); + + var subprocess = taskMarkers && taskMarkers.indexOf('SubProcessMarker') !== -1; + var position; + + if (subprocess) { + position = { + seq: -21, + parallel: -22, + compensation: -42, + loop: -18, + adhoc: 10 + }; + } else { + position = { + seq: -3, + parallel: -6, + compensation: -27, + loop: 0, + adhoc: 10 + }; + } + + forEach$1(taskMarkers, function(marker) { + renderer(marker)(parentGfx, element, position); + }); + + if (obj.isForCompensation) { + renderer('CompensationMarker')(parentGfx, element, position); + } + + if (obj.$type === 'bpmn:AdHocSubProcess') { + renderer('AdhocMarker')(parentGfx, element, position); + } + + var loopCharacteristics = obj.loopCharacteristics, + isSequential = loopCharacteristics && loopCharacteristics.isSequential; + + if (loopCharacteristics) { + + if (isSequential === undefined) { + renderer('LoopMarker')(parentGfx, element, position); + } + + if (isSequential === false) { + renderer('ParallelMarker')(parentGfx, element, position); + } + + if (isSequential === true) { + renderer('SequentialMarker')(parentGfx, element, position); + } + } + } + + function renderDataItemCollection(parentGfx, element) { + + var yPosition = (element.height - 18) / element.height; + + var pathData = pathMap.getScaledPath('DATA_OBJECT_COLLECTION_PATH', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.33, + my: yPosition + } + }); + + /* collection path */ drawPath(parentGfx, pathData, { + strokeWidth: 2 + }); + } + + + // extension API, use at your own risk + this._drawPath = drawPath; + + this._renderer = renderer; + } + + + e(BpmnRenderer, BaseRenderer); + + BpmnRenderer.$inject = [ + 'config.bpmnRenderer', + 'eventBus', + 'styles', + 'pathMap', + 'canvas', + 'textRenderer' + ]; + + + BpmnRenderer.prototype.canRender = function(element) { + return is$1(element, 'bpmn:BaseElement'); + }; + + BpmnRenderer.prototype.drawShape = function(parentGfx, element) { + var type = element.type; + var h = this._renderer(type); + + /* jshint -W040 */ + return h(parentGfx, element); + }; + + BpmnRenderer.prototype.drawConnection = function(parentGfx, element) { + var type = element.type; + var h = this._renderer(type); + + /* jshint -W040 */ + return h(parentGfx, element); + }; + + BpmnRenderer.prototype.getShapePath = function(element) { + + if (is$1(element, 'bpmn:Event')) { + return getCirclePath(element); + } + + if (is$1(element, 'bpmn:Activity')) { + return getRoundRectPath(element, TASK_BORDER_RADIUS); + } + + if (is$1(element, 'bpmn:Gateway')) { + return getDiamondPath(element); + } + + return getRectPath(element); + }; + + var DEFAULT_BOX_PADDING = 0; + + var DEFAULT_LABEL_SIZE$1 = { + width: 150, + height: 50 + }; + + + function parseAlign(align) { + + var parts = align.split('-'); + + return { + horizontal: parts[0] || 'center', + vertical: parts[1] || 'top' + }; + } + + function parsePadding(padding) { + + if (isObject(padding)) { + return assign({ top: 0, left: 0, right: 0, bottom: 0 }, padding); + } else { + return { + top: padding, + left: padding, + right: padding, + bottom: padding + }; + } + } + + function getTextBBox(text, fakeText) { + + fakeText.textContent = text; + + var textBBox; + + try { + var bbox, + emptyLine = text === ''; + + // add dummy text, when line is empty to + // determine correct height + fakeText.textContent = emptyLine ? 'dummy' : text; + + textBBox = fakeText.getBBox(); + + // take text rendering related horizontal + // padding into account + bbox = { + width: textBBox.width + textBBox.x * 2, + height: textBBox.height + }; + + if (emptyLine) { + + // correct width + bbox.width = 0; + } + + return bbox; + } catch (e) { + return { width: 0, height: 0 }; + } + } + + + /** + * Layout the next line and return the layouted element. + * + * Alters the lines passed. + * + * @param {Array} lines + * @return {Object} the line descriptor, an object { width, height, text } + */ + function layoutNext(lines, maxWidth, fakeText) { + + var originalLine = lines.shift(), + fitLine = originalLine; + + var textBBox; + + for (;;) { + textBBox = getTextBBox(fitLine, fakeText); + + textBBox.width = fitLine ? textBBox.width : 0; + + // try to fit + if (fitLine === ' ' || fitLine === '' || textBBox.width < Math.round(maxWidth) || fitLine.length < 2) { + return fit(lines, fitLine, originalLine, textBBox); + } + + fitLine = shortenLine(fitLine, textBBox.width, maxWidth); + } + } + + function fit(lines, fitLine, originalLine, textBBox) { + if (fitLine.length < originalLine.length) { + var remainder = originalLine.slice(fitLine.length).trim(); + + lines.unshift(remainder); + } + + return { + width: textBBox.width, + height: textBBox.height, + text: fitLine + }; + } + + var SOFT_BREAK = '\u00AD'; + + + /** + * Shortens a line based on spacing and hyphens. + * Returns the shortened result on success. + * + * @param {string} line + * @param {number} maxLength the maximum characters of the string + * @return {string} the shortened string + */ + function semanticShorten(line, maxLength) { + + var parts = line.split(/(\s|-|\u00AD)/g), + part, + shortenedParts = [], + length = 0; + + // try to shorten via break chars + if (parts.length > 1) { + + while ((part = parts.shift())) { + if (part.length + length < maxLength) { + shortenedParts.push(part); + length += part.length; + } else { + + // remove previous part, too if hyphen does not fit anymore + if (part === '-' || part === SOFT_BREAK) { + shortenedParts.pop(); + } + + break; + } + } + } + + var last = shortenedParts[shortenedParts.length - 1]; + + // translate trailing soft break to actual hyphen + if (last && last === SOFT_BREAK) { + shortenedParts[shortenedParts.length - 1] = '-'; + } + + return shortenedParts.join(''); + } + + + function shortenLine(line, width, maxWidth) { + var length = Math.max(line.length * (maxWidth / width), 1); + + // try to shorten semantically (i.e. based on spaces and hyphens) + var shortenedLine = semanticShorten(line, length); + + if (!shortenedLine) { + + // force shorten by cutting the long word + shortenedLine = line.slice(0, Math.max(Math.round(length - 1), 1)); + } + + return shortenedLine; + } + + + function getHelperSvg() { + var helperSvg = document.getElementById('helper-svg'); + + if (!helperSvg) { + helperSvg = create$1('svg'); + + attr(helperSvg, { + id: 'helper-svg' + }); + + assign$1(helperSvg, { + visibility: 'hidden', + position: 'fixed', + width: 0, + height: 0 + }); + + document.body.appendChild(helperSvg); + } + + return helperSvg; + } + + + /** + * Creates a new label utility + * + * @param {Object} config + * @param {Dimensions} config.size + * @param {number} config.padding + * @param {Object} config.style + * @param {string} config.align + */ + function Text(config) { + + this._config = assign({}, { + size: DEFAULT_LABEL_SIZE$1, + padding: DEFAULT_BOX_PADDING, + style: {}, + align: 'center-top' + }, config || {}); + } + + /** + * Returns the layouted text as an SVG element. + * + * @param {string} text + * @param {Object} options + * + * @return {SVGElement} + */ + Text.prototype.createText = function(text, options) { + return this.layoutText(text, options).element; + }; + + /** + * Returns a labels layouted dimensions. + * + * @param {string} text to layout + * @param {Object} options + * + * @return {Dimensions} + */ + Text.prototype.getDimensions = function(text, options) { + return this.layoutText(text, options).dimensions; + }; + + /** + * Creates and returns a label and its bounding box. + * + * @method Text#createText + * + * @param {string} text the text to render on the label + * @param {Object} options + * @param {string} options.align how to align in the bounding box. + * Any of { 'center-middle', 'center-top' }, + * defaults to 'center-top'. + * @param {string} options.style style to be applied to the text + * @param {boolean} options.fitBox indicates if box will be recalculated to + * fit text + * + * @return {Object} { element, dimensions } + */ + Text.prototype.layoutText = function(text, options) { + var box = assign({}, this._config.size, options.box), + style = assign({}, this._config.style, options.style), + align = parseAlign(options.align || this._config.align), + padding = parsePadding(options.padding !== undefined ? options.padding : this._config.padding), + fitBox = options.fitBox || false; + + var lineHeight = getLineHeight(style); + + // we split text by lines and normalize + // {soft break} + {line break} => { line break } + var lines = text.split(/\u00AD?\r?\n/), + layouted = []; + + var maxWidth = box.width - padding.left - padding.right; + + // ensure correct rendering by attaching helper text node to invisible SVG + var helperText = create$1('text'); + attr(helperText, { x: 0, y: 0 }); + attr(helperText, style); + + var helperSvg = getHelperSvg(); + + append(helperSvg, helperText); + + while (lines.length) { + layouted.push(layoutNext(lines, maxWidth, helperText)); + } + + if (align.vertical === 'middle') { + padding.top = padding.bottom = 0; + } + + var totalHeight = reduce(layouted, function(sum, line, idx) { + return sum + (lineHeight || line.height); + }, 0) + padding.top + padding.bottom; + + var maxLineWidth = reduce(layouted, function(sum, line, idx) { + return line.width > sum ? line.width : sum; + }, 0); + + // the y position of the next line + var y = padding.top; + + if (align.vertical === 'middle') { + y += (box.height - totalHeight) / 2; + } + + // magic number initial offset + y -= (lineHeight || layouted[0].height) / 4; + + + var textElement = create$1('text'); + + attr(textElement, style); + + // layout each line taking into account that parent + // shape might resize to fit text size + forEach$1(layouted, function(line) { + + var x; + + y += (lineHeight || line.height); + + switch (align.horizontal) { + case 'left': + x = padding.left; + break; + + case 'right': + x = ((fitBox ? maxLineWidth : maxWidth) + - padding.right - line.width); + break; + + default: + + // aka center + x = Math.max((((fitBox ? maxLineWidth : maxWidth) + - line.width) / 2 + padding.left), 0); + } + + var tspan = create$1('tspan'); + attr(tspan, { x: x, y: y }); + + tspan.textContent = line.text; + + append(textElement, tspan); + }); + + remove$1(helperText); + + var dimensions = { + width: maxLineWidth, + height: totalHeight + }; + + return { + dimensions: dimensions, + element: textElement + }; + }; + + + function getLineHeight(style) { + if ('fontSize' in style && 'lineHeight' in style) { + return style.lineHeight * parseInt(style.fontSize, 10); + } + } + + var DEFAULT_FONT_SIZE = 12; + var LINE_HEIGHT_RATIO = 1.2; + + var MIN_TEXT_ANNOTATION_HEIGHT = 30; + + + function TextRenderer(config) { + + var defaultStyle = assign({ + fontFamily: 'Arial, sans-serif', + fontSize: DEFAULT_FONT_SIZE, + fontWeight: 'normal', + lineHeight: LINE_HEIGHT_RATIO + }, config && config.defaultStyle || {}); + + var fontSize = parseInt(defaultStyle.fontSize, 10) - 1; + + var externalStyle = assign({}, defaultStyle, { + fontSize: fontSize + }, config && config.externalStyle || {}); + + var textUtil = new Text({ + style: defaultStyle + }); + + /** + * Get the new bounds of an externally rendered, + * layouted label. + * + * @param {Bounds} bounds + * @param {string} text + * + * @return {Bounds} + */ + this.getExternalLabelBounds = function(bounds, text) { + + var layoutedDimensions = textUtil.getDimensions(text, { + box: { + width: 90, + height: 30, + x: bounds.width / 2 + bounds.x, + y: bounds.height / 2 + bounds.y + }, + style: externalStyle + }); + + // resize label shape to fit label text + return { + x: Math.round(bounds.x + bounds.width / 2 - layoutedDimensions.width / 2), + y: Math.round(bounds.y), + width: Math.ceil(layoutedDimensions.width), + height: Math.ceil(layoutedDimensions.height) + }; + + }; + + /** + * Get the new bounds of text annotation. + * + * @param {Bounds} bounds + * @param {string} text + * + * @return {Bounds} + */ + this.getTextAnnotationBounds = function(bounds, text) { + + var layoutedDimensions = textUtil.getDimensions(text, { + box: bounds, + style: defaultStyle, + align: 'left-top', + padding: 5 + }); + + return { + x: bounds.x, + y: bounds.y, + width: bounds.width, + height: Math.max(MIN_TEXT_ANNOTATION_HEIGHT, Math.round(layoutedDimensions.height)) + }; + }; + + /** + * Create a layouted text element. + * + * @param {string} text + * @param {Object} [options] + * + * @return {SVGElement} rendered text + */ + this.createText = function(text, options) { + return textUtil.createText(text, options || {}); + }; + + /** + * Get default text style. + */ + this.getDefaultStyle = function() { + return defaultStyle; + }; + + /** + * Get the external text style. + */ + this.getExternalStyle = function() { + return externalStyle; + }; + + } + + TextRenderer.$inject = [ + 'config.textRenderer' + ]; + + /** + * Map containing SVG paths needed by BpmnRenderer. + */ + + function PathMap() { + + /** + * Contains a map of path elements + * + *

Path definition

+ * A parameterized path is defined like this: + *
+     * 'GATEWAY_PARALLEL': {
+     *   d: 'm {mx},{my} {e.x0},0 0,{e.x1} {e.x1},0 0,{e.y0} -{e.x1},0 0,{e.y1} ' +
+            '-{e.x0},0 0,-{e.y1} -{e.x1},0 0,-{e.y0} {e.x1},0 z',
+     *   height: 17.5,
+     *   width:  17.5,
+     *   heightElements: [2.5, 7.5],
+     *   widthElements: [2.5, 7.5]
+     * }
+     * 
+ *

It's important to specify a correct height and width for the path as the scaling + * is based on the ratio between the specified height and width in this object and the + * height and width that is set as scale target (Note x,y coordinates will be scaled with + * individual ratios).

+ *

The 'heightElements' and 'widthElements' array must contain the values that will be scaled. + * The scaling is based on the computed ratios. + * Coordinates on the y axis should be in the heightElement's array, they will be scaled using + * the computed ratio coefficient. + * In the parameterized path the scaled values can be accessed through the 'e' object in {} brackets. + *

    + *
  • The values for the y axis can be accessed in the path string using {e.y0}, {e.y1}, ....
  • + *
  • The values for the x axis can be accessed in the path string using {e.x0}, {e.x1}, ....
  • + *
+ * The numbers x0, x1 respectively y0, y1, ... map to the corresponding array index. + *

+ */ + this.pathMap = { + 'EVENT_MESSAGE': { + d: 'm {mx},{my} l 0,{e.y1} l {e.x1},0 l 0,-{e.y1} z l {e.x0},{e.y0} l {e.x0},-{e.y0}', + height: 36, + width: 36, + heightElements: [ 6, 14 ], + widthElements: [ 10.5, 21 ] + }, + 'EVENT_SIGNAL': { + d: 'M {mx},{my} l {e.x0},{e.y0} l -{e.x1},0 Z', + height: 36, + width: 36, + heightElements: [ 18 ], + widthElements: [ 10, 20 ] + }, + 'EVENT_ESCALATION': { + d: 'M {mx},{my} l {e.x0},{e.y0} l -{e.x0},-{e.y1} l -{e.x0},{e.y1} Z', + height: 36, + width: 36, + heightElements: [ 20, 7 ], + widthElements: [ 8 ] + }, + 'EVENT_CONDITIONAL': { + d: 'M {e.x0},{e.y0} l {e.x1},0 l 0,{e.y2} l -{e.x1},0 Z ' + + 'M {e.x2},{e.y3} l {e.x0},0 ' + + 'M {e.x2},{e.y4} l {e.x0},0 ' + + 'M {e.x2},{e.y5} l {e.x0},0 ' + + 'M {e.x2},{e.y6} l {e.x0},0 ' + + 'M {e.x2},{e.y7} l {e.x0},0 ' + + 'M {e.x2},{e.y8} l {e.x0},0 ', + height: 36, + width: 36, + heightElements: [ 8.5, 14.5, 18, 11.5, 14.5, 17.5, 20.5, 23.5, 26.5 ], + widthElements: [ 10.5, 14.5, 12.5 ] + }, + 'EVENT_LINK': { + d: 'm {mx},{my} 0,{e.y0} -{e.x1},0 0,{e.y1} {e.x1},0 0,{e.y0} {e.x0},-{e.y2} -{e.x0},-{e.y2} z', + height: 36, + width: 36, + heightElements: [ 4.4375, 6.75, 7.8125 ], + widthElements: [ 9.84375, 13.5 ] + }, + 'EVENT_ERROR': { + d: 'm {mx},{my} {e.x0},-{e.y0} {e.x1},-{e.y1} {e.x2},{e.y2} {e.x3},-{e.y3} -{e.x4},{e.y4} -{e.x5},-{e.y5} z', + height: 36, + width: 36, + heightElements: [ 0.023, 8.737, 8.151, 16.564, 10.591, 8.714 ], + widthElements: [ 0.085, 6.672, 6.97, 4.273, 5.337, 6.636 ] + }, + 'EVENT_CANCEL_45': { + d: 'm {mx},{my} -{e.x1},0 0,{e.x0} {e.x1},0 0,{e.y1} {e.x0},0 ' + + '0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z', + height: 36, + width: 36, + heightElements: [ 4.75, 8.5 ], + widthElements: [ 4.75, 8.5 ] + }, + 'EVENT_COMPENSATION': { + d: 'm {mx},{my} {e.x0},-{e.y0} 0,{e.y1} z m {e.x1},-{e.y2} {e.x2},-{e.y3} 0,{e.y1} -{e.x2},-{e.y3} z', + height: 36, + width: 36, + heightElements: [ 6.5, 13, 0.4, 6.1 ], + widthElements: [ 9, 9.3, 8.7 ] + }, + 'EVENT_TIMER_WH': { + d: 'M {mx},{my} l {e.x0},-{e.y0} m -{e.x0},{e.y0} l {e.x1},{e.y1} ', + height: 36, + width: 36, + heightElements: [ 10, 2 ], + widthElements: [ 3, 7 ] + }, + 'EVENT_TIMER_LINE': { + d: 'M {mx},{my} ' + + 'm {e.x0},{e.y0} l -{e.x1},{e.y1} ', + height: 36, + width: 36, + heightElements: [ 10, 3 ], + widthElements: [ 0, 0 ] + }, + 'EVENT_MULTIPLE': { + d:'m {mx},{my} {e.x1},-{e.y0} {e.x1},{e.y0} -{e.x0},{e.y1} -{e.x2},0 z', + height: 36, + width: 36, + heightElements: [ 6.28099, 12.56199 ], + widthElements: [ 3.1405, 9.42149, 12.56198 ] + }, + 'EVENT_PARALLEL_MULTIPLE': { + d:'m {mx},{my} {e.x0},0 0,{e.y1} {e.x1},0 0,{e.y0} -{e.x1},0 0,{e.y1} ' + + '-{e.x0},0 0,-{e.y1} -{e.x1},0 0,-{e.y0} {e.x1},0 z', + height: 36, + width: 36, + heightElements: [ 2.56228, 7.68683 ], + widthElements: [ 2.56228, 7.68683 ] + }, + 'GATEWAY_EXCLUSIVE': { + d:'m {mx},{my} {e.x0},{e.y0} {e.x1},{e.y0} {e.x2},0 {e.x4},{e.y2} ' + + '{e.x4},{e.y1} {e.x2},0 {e.x1},{e.y3} {e.x0},{e.y3} ' + + '{e.x3},0 {e.x5},{e.y1} {e.x5},{e.y2} {e.x3},0 z', + height: 17.5, + width: 17.5, + heightElements: [ 8.5, 6.5312, -6.5312, -8.5 ], + widthElements: [ 6.5, -6.5, 3, -3, 5, -5 ] + }, + 'GATEWAY_PARALLEL': { + d:'m {mx},{my} 0,{e.y1} -{e.x1},0 0,{e.y0} {e.x1},0 0,{e.y1} {e.x0},0 ' + + '0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z', + height: 30, + width: 30, + heightElements: [ 5, 12.5 ], + widthElements: [ 5, 12.5 ] + }, + 'GATEWAY_EVENT_BASED': { + d:'m {mx},{my} {e.x0},{e.y0} {e.x0},{e.y1} {e.x1},{e.y2} {e.x2},0 z', + height: 11, + width: 11, + heightElements: [ -6, 6, 12, -12 ], + widthElements: [ 9, -3, -12 ] + }, + 'GATEWAY_COMPLEX': { + d:'m {mx},{my} 0,{e.y0} -{e.x0},-{e.y1} -{e.x1},{e.y2} {e.x0},{e.y1} -{e.x2},0 0,{e.y3} ' + + '{e.x2},0 -{e.x0},{e.y1} l {e.x1},{e.y2} {e.x0},-{e.y1} 0,{e.y0} {e.x3},0 0,-{e.y0} {e.x0},{e.y1} ' + + '{e.x1},-{e.y2} -{e.x0},-{e.y1} {e.x2},0 0,-{e.y3} -{e.x2},0 {e.x0},-{e.y1} -{e.x1},-{e.y2} ' + + '-{e.x0},{e.y1} 0,-{e.y0} -{e.x3},0 z', + height: 17.125, + width: 17.125, + heightElements: [ 4.875, 3.4375, 2.125, 3 ], + widthElements: [ 3.4375, 2.125, 4.875, 3 ] + }, + 'DATA_OBJECT_PATH': { + d:'m 0,0 {e.x1},0 {e.x0},{e.y0} 0,{e.y1} -{e.x2},0 0,-{e.y2} {e.x1},0 0,{e.y0} {e.x0},0', + height: 61, + width: 51, + heightElements: [ 10, 50, 60 ], + widthElements: [ 10, 40, 50, 60 ] + }, + 'DATA_OBJECT_COLLECTION_PATH': { + d: 'm{mx},{my} m 3,2 l 0,10 m 3,-10 l 0,10 m 3,-10 l 0,10', + height: 10, + width: 10, + heightElements: [], + widthElements: [] + }, + 'DATA_ARROW': { + d:'m 5,9 9,0 0,-3 5,5 -5,5 0,-3 -9,0 z', + height: 61, + width: 51, + heightElements: [], + widthElements: [] + }, + 'DATA_STORE': { + d:'m {mx},{my} ' + + 'l 0,{e.y2} ' + + 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 ' + + 'l 0,-{e.y2} ' + + 'c -{e.x0},-{e.y1} -{e.x1},-{e.y1} -{e.x2},0' + + 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 ' + + 'm -{e.x2},{e.y0}' + + 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0' + + 'm -{e.x2},{e.y0}' + + 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0', + height: 61, + width: 61, + heightElements: [ 7, 10, 45 ], + widthElements: [ 2, 58, 60 ] + }, + 'TEXT_ANNOTATION': { + d: 'm {mx}, {my} m 10,0 l -10,0 l 0,{e.y0} l 10,0', + height: 30, + width: 10, + heightElements: [ 30 ], + widthElements: [ 10 ] + }, + 'MARKER_SUB_PROCESS': { + d: 'm{mx},{my} m 7,2 l 0,10 m -5,-5 l 10,0', + height: 10, + width: 10, + heightElements: [], + widthElements: [] + }, + 'MARKER_PARALLEL': { + d: 'm{mx},{my} m 3,2 l 0,10 m 3,-10 l 0,10 m 3,-10 l 0,10', + height: 10, + width: 10, + heightElements: [], + widthElements: [] + }, + 'MARKER_SEQUENTIAL': { + d: 'm{mx},{my} m 0,3 l 10,0 m -10,3 l 10,0 m -10,3 l 10,0', + height: 10, + width: 10, + heightElements: [], + widthElements: [] + }, + 'MARKER_COMPENSATION': { + d: 'm {mx},{my} 7,-5 0,10 z m 7.1,-0.3 6.9,-4.7 0,10 -6.9,-4.7 z', + height: 10, + width: 21, + heightElements: [], + widthElements: [] + }, + 'MARKER_LOOP': { + d: 'm {mx},{my} c 3.526979,0 6.386161,-2.829858 6.386161,-6.320661 0,-3.490806 -2.859182,-6.320661 ' + + '-6.386161,-6.320661 -3.526978,0 -6.38616,2.829855 -6.38616,6.320661 0,1.745402 ' + + '0.714797,3.325567 1.870463,4.469381 0.577834,0.571908 1.265885,1.034728 2.029916,1.35457 ' + + 'l -0.718163,-3.909793 m 0.718163,3.909793 -3.885211,0.802902', + height: 13.9, + width: 13.7, + heightElements: [], + widthElements: [] + }, + 'MARKER_ADHOC': { + d: 'm {mx},{my} m 0.84461,2.64411 c 1.05533,-1.23780996 2.64337,-2.07882 4.29653,-1.97997996 2.05163,0.0805 ' + + '3.85579,1.15803 5.76082,1.79107 1.06385,0.34139996 2.24454,0.1438 3.18759,-0.43767 0.61743,-0.33642 ' + + '1.2775,-0.64078 1.7542,-1.17511 0,0.56023 0,1.12046 0,1.6807 -0.98706,0.96237996 -2.29792,1.62393996 ' + + '-3.6918,1.66181996 -1.24459,0.0927 -2.46671,-0.2491 -3.59505,-0.74812 -1.35789,-0.55965 ' + + '-2.75133,-1.33436996 -4.27027,-1.18121996 -1.37741,0.14601 -2.41842,1.13685996 -3.44288,1.96782996 z', + height: 4, + width: 15, + heightElements: [], + widthElements: [] + }, + 'TASK_TYPE_SEND': { + d: 'm {mx},{my} l 0,{e.y1} l {e.x1},0 l 0,-{e.y1} z l {e.x0},{e.y0} l {e.x0},-{e.y0}', + height: 14, + width: 21, + heightElements: [ 6, 14 ], + widthElements: [ 10.5, 21 ] + }, + 'TASK_TYPE_SCRIPT': { + d: 'm {mx},{my} c 9.966553,-6.27276 -8.000926,-7.91932 2.968968,-14.938 l -8.802728,0 ' + + 'c -10.969894,7.01868 6.997585,8.66524 -2.968967,14.938 z ' + + 'm -7,-12 l 5,0 ' + + 'm -4.5,3 l 4.5,0 ' + + 'm -3,3 l 5,0' + + 'm -4,3 l 5,0', + height: 15, + width: 12.6, + heightElements: [ 6, 14 ], + widthElements: [ 10.5, 21 ] + }, + 'TASK_TYPE_USER_1': { + d: 'm {mx},{my} c 0.909,-0.845 1.594,-2.049 1.594,-3.385 0,-2.554 -1.805,-4.62199999 ' + + '-4.357,-4.62199999 -2.55199998,0 -4.28799998,2.06799999 -4.28799998,4.62199999 0,1.348 ' + + '0.974,2.562 1.89599998,3.405 -0.52899998,0.187 -5.669,2.097 -5.794,4.7560005 v 6.718 ' + + 'h 17 v -6.718 c 0,-2.2980005 -5.5279996,-4.5950005 -6.0509996,-4.7760005 z' + + 'm -8,6 l 0,5.5 m 11,0 l 0,-5' + }, + 'TASK_TYPE_USER_2': { + d: 'm {mx},{my} m 2.162,1.009 c 0,2.4470005 -2.158,4.4310005 -4.821,4.4310005 ' + + '-2.66499998,0 -4.822,-1.981 -4.822,-4.4310005 ' + }, + 'TASK_TYPE_USER_3': { + d: 'm {mx},{my} m -6.9,-3.80 c 0,0 2.25099998,-2.358 4.27399998,-1.177 2.024,1.181 4.221,1.537 ' + + '4.124,0.965 -0.098,-0.57 -0.117,-3.79099999 -4.191,-4.13599999 -3.57499998,0.001 ' + + '-4.20799998,3.36699999 -4.20699998,4.34799999 z' + }, + 'TASK_TYPE_MANUAL': { + d: 'm {mx},{my} c 0.234,-0.01 5.604,0.008 8.029,0.004 0.808,0 1.271,-0.172 1.417,-0.752 0.227,-0.898 ' + + '-0.334,-1.314 -1.338,-1.316 -2.467,-0.01 -7.886,-0.004 -8.108,-0.004 -0.014,-0.079 0.016,-0.533 0,-0.61 ' + + '0.195,-0.042 8.507,0.006 9.616,0.002 0.877,-0.007 1.35,-0.438 1.353,-1.208 0.003,-0.768 -0.479,-1.09 ' + + '-1.35,-1.091 -2.968,-0.002 -9.619,-0.013 -9.619,-0.013 v -0.591 c 0,0 5.052,-0.016 7.225,-0.016 ' + + '0.888,-0.002 1.354,-0.416 1.351,-1.193 -0.006,-0.761 -0.492,-1.196 -1.361,-1.196 -3.473,-0.005 ' + + '-10.86,-0.003 -11.0829995,-0.003 -0.022,-0.047 -0.045,-0.094 -0.069,-0.139 0.3939995,-0.319 ' + + '2.0409995,-1.626 2.4149995,-2.017 0.469,-0.4870005 0.519,-1.1650005 0.162,-1.6040005 -0.414,-0.511 ' + + '-0.973,-0.5 -1.48,-0.236 -1.4609995,0.764 -6.5999995,3.6430005 -7.7329995,4.2710005 -0.9,0.499 ' + + '-1.516,1.253 -1.882,2.19 -0.37000002,0.95 -0.17,2.01 -0.166,2.979 0.004,0.718 -0.27300002,1.345 ' + + '-0.055,2.063 0.629,2.087 2.425,3.312 4.859,3.318 4.6179995,0.014 9.2379995,-0.139 13.8569995,-0.158 ' + + '0.755,-0.004 1.171,-0.301 1.182,-1.033 0.012,-0.754 -0.423,-0.969 -1.183,-0.973 -1.778,-0.01 ' + + '-5.824,-0.004 -6.04,-0.004 10e-4,-0.084 0.003,-0.586 10e-4,-0.67 z' + }, + 'TASK_TYPE_INSTANTIATING_SEND': { + d: 'm {mx},{my} l 0,8.4 l 12.6,0 l 0,-8.4 z l 6.3,3.6 l 6.3,-3.6' + }, + 'TASK_TYPE_SERVICE': { + d: 'm {mx},{my} v -1.71335 c 0.352326,-0.0705 0.703932,-0.17838 1.047628,-0.32133 ' + + '0.344416,-0.14465 0.665822,-0.32133 0.966377,-0.52145 l 1.19431,1.18005 1.567487,-1.57688 ' + + '-1.195028,-1.18014 c 0.403376,-0.61394 0.683079,-1.29908 0.825447,-2.01824 l 1.622133,-0.01 ' + + 'v -2.2196 l -1.636514,0.01 c -0.07333,-0.35153 -0.178319,-0.70024 -0.323564,-1.04372 ' + + '-0.145244,-0.34406 -0.321407,-0.6644 -0.522735,-0.96217 l 1.131035,-1.13631 -1.583305,-1.56293 ' + + '-1.129598,1.13589 c -0.614052,-0.40108 -1.302883,-0.68093 -2.022633,-0.82247 l 0.0093,-1.61852 ' + + 'h -2.241173 l 0.0042,1.63124 c -0.353763,0.0736 -0.705369,0.17977 -1.049785,0.32371 -0.344415,0.14437 ' + + '-0.665102,0.32092 -0.9635006,0.52046 l -1.1698628,-1.15823 -1.5667691,1.5792 1.1684265,1.15669 ' + + 'c -0.4026573,0.61283 -0.68308,1.29797 -0.8247287,2.01713 l -1.6588041,0.003 v 2.22174 ' + + 'l 1.6724648,-0.006 c 0.073327,0.35077 0.1797598,0.70243 0.3242851,1.04472 0.1452428,0.34448 ' + + '0.3214064,0.6644 0.5227339,0.96066 l -1.1993431,1.19723 1.5840256,1.56011 1.1964668,-1.19348 ' + + 'c 0.6140517,0.40346 1.3028827,0.68232 2.0233517,0.82331 l 7.19e-4,1.69892 h 2.226848 z ' + + 'm 0.221462,-3.9957 c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 ' + + '0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 ' + + '0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z' + }, + 'TASK_TYPE_SERVICE_FILL': { + d: 'm {mx},{my} c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 ' + + '0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 ' + + '0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z' + }, + 'TASK_TYPE_BUSINESS_RULE_HEADER': { + d: 'm {mx},{my} 0,4 20,0 0,-4 z' + }, + 'TASK_TYPE_BUSINESS_RULE_MAIN': { + d: 'm {mx},{my} 0,12 20,0 0,-12 z' + + 'm 0,8 l 20,0 ' + + 'm -13,-4 l 0,8' + }, + 'MESSAGE_FLOW_MARKER': { + d: 'm {mx},{my} m -10.5 ,-7 l 0,14 l 21,0 l 0,-14 z l 10.5,6 l 10.5,-6' + } + }; + + this.getRawPath = function getRawPath(pathId) { + return this.pathMap[pathId].d; + }; + + /** + * Scales the path to the given height and width. + *

Use case

+ *

Use case is to scale the content of elements (event, gateways) based + * on the element bounding box's size. + *

+ *

Why not transform

+ *

Scaling a path with transform() will also scale the stroke and IE does not support + * the option 'non-scaling-stroke' to prevent this. + * Also there are use cases where only some parts of a path should be + * scaled.

+ * + * @param {string} pathId The ID of the path. + * @param {Object} param

+ * Example param object scales the path to 60% size of the container (data.width, data.height). + *

+     *   {
+     *     xScaleFactor: 0.6,
+     *     yScaleFactor:0.6,
+     *     containerWidth: data.width,
+     *     containerHeight: data.height,
+     *     position: {
+     *       mx: 0.46,
+     *       my: 0.2,
+     *     }
+     *   }
+     *   
+ *
    + *
  • targetpathwidth = xScaleFactor * containerWidth
  • + *
  • targetpathheight = yScaleFactor * containerHeight
  • + *
  • Position is used to set the starting coordinate of the path. M is computed: + *
      + *
    • position.x * containerWidth
    • + *
    • position.y * containerHeight
    • + *
    + * Center of the container
     position: {
    +     *       mx: 0.5,
    +     *       my: 0.5,
    +     *     }
    + * Upper left corner of the container + *
     position: {
    +     *       mx: 0.0,
    +     *       my: 0.0,
    +     *     }
    + *
  • + *
+ *

+ * + */ + this.getScaledPath = function getScaledPath(pathId, param) { + var rawPath = this.pathMap[pathId]; + + // positioning + // compute the start point of the path + var mx, my; + + if (param.abspos) { + mx = param.abspos.x; + my = param.abspos.y; + } else { + mx = param.containerWidth * param.position.mx; + my = param.containerHeight * param.position.my; + } + + var coordinates = {}; // map for the scaled coordinates + if (param.position) { + + // path + var heightRatio = (param.containerHeight / rawPath.height) * param.yScaleFactor; + var widthRatio = (param.containerWidth / rawPath.width) * param.xScaleFactor; + + + // Apply height ratio + for (var heightIndex = 0; heightIndex < rawPath.heightElements.length; heightIndex++) { + coordinates['y' + heightIndex] = rawPath.heightElements[heightIndex] * heightRatio; + } + + // Apply width ratio + for (var widthIndex = 0; widthIndex < rawPath.widthElements.length; widthIndex++) { + coordinates['x' + widthIndex] = rawPath.widthElements[widthIndex] * widthRatio; + } + } + + // Apply value to raw path + var path = format( + rawPath.d, { + mx: mx, + my: my, + e: coordinates + } + ); + return path; + }; + } + + // helpers ////////////////////// + + // copied and adjusted from https://github.com/adobe-webplatform/Snap.svg/blob/master/src/svg.js + var tokenRegex = /\{([^{}]+)\}/g, + objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g; // matches .xxxxx or ["xxxxx"] to run over object properties + + function replacer(all, key, obj) { + var res = obj; + key.replace(objNotationRegex, function(all, name, quote, quotedName, isFunc) { + name = name || quotedName; + if (res) { + if (name in res) { + res = res[name]; + } + typeof res == 'function' && isFunc && (res = res()); + } + }); + res = (res == null || res == obj ? all : res) + ''; + + return res; + } + + function format(str, obj) { + return String(str).replace(tokenRegex, function(all, key) { + return replacer(all, key, obj); + }); + } + + var DrawModule = { + __init__: [ 'bpmnRenderer' ], + bpmnRenderer: [ 'type', BpmnRenderer ], + textRenderer: [ 'type', TextRenderer ], + pathMap: [ 'type', PathMap ] + }; + + /** + * A simple translation stub to be used for multi-language support + * in diagrams. Can be easily replaced with a more sophisticated + * solution. + * + * @example + * + * // use it inside any diagram component by injecting `translate`. + * + * function MyService(translate) { + * alert(translate('HELLO {you}', { you: 'You!' })); + * } + * + * @param {string} template to interpolate + * @param {Object} [replacements] a map with substitutes + * + * @return {string} the translated string + */ + function translate$1(template, replacements) { + + replacements = replacements || {}; + + return template.replace(/{([^}]+)}/g, function(_, key) { + return replacements[key] || '{' + key + '}'; + }); + } + + var translate = { + translate: [ 'value', translate$1 ] + }; + + var DEFAULT_LABEL_SIZE = { + width: 90, + height: 20 + }; + + var FLOW_LABEL_INDENT = 15; + + + /** + * Returns true if the given semantic has an external label + * + * @param {BpmnElement} semantic + * @return {boolean} true if has label + */ + function isLabelExternal(semantic) { + return is$1(semantic, 'bpmn:Event') || + is$1(semantic, 'bpmn:Gateway') || + is$1(semantic, 'bpmn:DataStoreReference') || + is$1(semantic, 'bpmn:DataObjectReference') || + is$1(semantic, 'bpmn:DataInput') || + is$1(semantic, 'bpmn:DataOutput') || + is$1(semantic, 'bpmn:SequenceFlow') || + is$1(semantic, 'bpmn:MessageFlow') || + is$1(semantic, 'bpmn:Group'); + } + + /** + * Returns true if the given element has an external label + * + * @param {djs.model.shape} element + * @return {boolean} true if has label + */ + function hasExternalLabel(element) { + return isLabel$6(element.label); + } + + /** + * Get the position for sequence flow labels + * + * @param {Array} waypoints + * @return {Point} the label position + */ + function getFlowLabelPosition(waypoints) { + + // get the waypoints mid + var mid = waypoints.length / 2 - 1; + + var first = waypoints[Math.floor(mid)]; + var second = waypoints[Math.ceil(mid + 0.01)]; + + // get position + var position = getWaypointsMid(waypoints); + + // calculate angle + var angle = Math.atan((second.y - first.y) / (second.x - first.x)); + + var x = position.x, + y = position.y; + + if (Math.abs(angle) < Math.PI / 2) { + y -= FLOW_LABEL_INDENT; + } else { + x += FLOW_LABEL_INDENT; + } + + return { x: x, y: y }; + } + + + /** + * Get the middle of a number of waypoints + * + * @param {Array} waypoints + * @return {Point} the mid point + */ + function getWaypointsMid(waypoints) { + + var mid = waypoints.length / 2 - 1; + + var first = waypoints[Math.floor(mid)]; + var second = waypoints[Math.ceil(mid + 0.01)]; + + return { + x: first.x + (second.x - first.x) / 2, + y: first.y + (second.y - first.y) / 2 + }; + } + + + function getExternalLabelMid(element) { + + if (element.waypoints) { + return getFlowLabelPosition(element.waypoints); + } else if (is$1(element, 'bpmn:Group')) { + return { + x: element.x + element.width / 2, + y: element.y + DEFAULT_LABEL_SIZE.height / 2 + }; + } else { + return { + x: element.x + element.width / 2, + y: element.y + element.height + DEFAULT_LABEL_SIZE.height / 2 + }; + } + } + + + /** + * Returns the bounds of an elements label, parsed from the elements DI or + * generated from its bounds. + * + * @param {BpmndDi} di + * @param {djs.model.Base} element + */ + function getExternalLabelBounds(di, element) { + + var mid, + size, + bounds, + label = di.label; + + if (label && label.bounds) { + bounds = label.bounds; + + size = { + width: Math.max(DEFAULT_LABEL_SIZE.width, bounds.width), + height: bounds.height + }; + + mid = { + x: bounds.x + bounds.width / 2, + y: bounds.y + bounds.height / 2 + }; + } else { + + mid = getExternalLabelMid(element); + + size = DEFAULT_LABEL_SIZE; + } + + return assign({ + x: mid.x - size.width / 2, + y: mid.y - size.height / 2 + }, size); + } + + function isLabel$6(element) { + return element && !!element.labelTarget; + } + + /** + * @param {ModdleElement} semantic + * @param {ModdleElement} di + * @param {Object} [attrs=null] + * + * @return {Object} + */ + function elementData(semantic, di, attrs) { + return assign({ + id: semantic.id, + type: semantic.$type, + businessObject: semantic, + di: di + }, attrs); + } + + function getWaypoints(di, source, target) { + + var waypoints = di.waypoint; + + if (!waypoints || waypoints.length < 2) { + return [ getMid(source), getMid(target) ]; + } + + return waypoints.map(function(p) { + return { x: p.x, y: p.y }; + }); + } + + function notYetDrawn(translate, semantic, refSemantic, property) { + return new Error(translate('element {element} referenced by {referenced}#{property} not yet drawn', { + element: elementToString(refSemantic), + referenced: elementToString(semantic), + property: property + })); + } + + + /** + * An importer that adds bpmn elements to the canvas + * + * @param {EventBus} eventBus + * @param {Canvas} canvas + * @param {ElementFactory} elementFactory + * @param {ElementRegistry} elementRegistry + * @param {Function} translate + * @param {TextRenderer} textRenderer + */ + function BpmnImporter( + eventBus, canvas, elementFactory, + elementRegistry, translate, textRenderer) { + + this._eventBus = eventBus; + this._canvas = canvas; + this._elementFactory = elementFactory; + this._elementRegistry = elementRegistry; + this._translate = translate; + this._textRenderer = textRenderer; + } + + BpmnImporter.$inject = [ + 'eventBus', + 'canvas', + 'elementFactory', + 'elementRegistry', + 'translate', + 'textRenderer' + ]; + + + /** + * Add bpmn element (semantic) to the canvas onto the + * specified parent shape. + */ + BpmnImporter.prototype.add = function(semantic, di, parentElement) { + var element, + translate = this._translate, + hidden; + + var parentIndex; + + // ROOT ELEMENT + // handle the special case that we deal with a + // invisible root element (process, subprocess or collaboration) + if (is$1(di, 'bpmndi:BPMNPlane')) { + + var attrs = is$1(semantic, 'bpmn:SubProcess') + ? { id: semantic.id + '_plane' } + : {}; + + // add a virtual element (not being drawn) + element = this._elementFactory.createRoot(elementData(semantic, di, attrs)); + + this._canvas.addRootElement(element); + } + + // SHAPE + else if (is$1(di, 'bpmndi:BPMNShape')) { + + var collapsed = !isExpanded(semantic, di), + isFrame = isFrameElement(semantic); + + hidden = parentElement && (parentElement.hidden || parentElement.collapsed); + + var bounds = di.bounds; + + element = this._elementFactory.createShape(elementData(semantic, di, { + collapsed: collapsed, + hidden: hidden, + x: Math.round(bounds.x), + y: Math.round(bounds.y), + width: Math.round(bounds.width), + height: Math.round(bounds.height), + isFrame: isFrame + })); + + if (is$1(semantic, 'bpmn:BoundaryEvent')) { + this._attachBoundary(semantic, element); + } + + // insert lanes behind other flow nodes (cf. #727) + if (is$1(semantic, 'bpmn:Lane')) { + parentIndex = 0; + } + + if (is$1(semantic, 'bpmn:DataStoreReference')) { + + // check whether data store is inside our outside of its semantic parent + if (!isPointInsideBBox$1(parentElement, getMid(bounds))) { + parentElement = this._canvas.findRoot(parentElement); + } + } + + this._canvas.addShape(element, parentElement, parentIndex); + } + + // CONNECTION + else if (is$1(di, 'bpmndi:BPMNEdge')) { + + var source = this._getSource(semantic), + target = this._getTarget(semantic); + + hidden = parentElement && (parentElement.hidden || parentElement.collapsed); + + element = this._elementFactory.createConnection(elementData(semantic, di, { + hidden: hidden, + source: source, + target: target, + waypoints: getWaypoints(di, source, target) + })); + + if (is$1(semantic, 'bpmn:DataAssociation')) { + + // render always on top; this ensures DataAssociations + // are rendered correctly across different "hacks" people + // love to model such as cross participant / sub process + // associations + parentElement = this._canvas.findRoot(parentElement); + } + + this._canvas.addConnection(element, parentElement, parentIndex); + } else { + throw new Error(translate('unknown di {di} for element {semantic}', { + di: elementToString(di), + semantic: elementToString(semantic) + })); + } + + // (optional) LABEL + if (isLabelExternal(semantic) && getLabel(element)) { + this.addLabel(semantic, di, element); + } + + + this._eventBus.fire('bpmnElement.added', { element: element }); + + return element; + }; + + + /** + * Attach the boundary element to the given host + * + * @param {ModdleElement} boundarySemantic + * @param {djs.model.Base} boundaryElement + */ + BpmnImporter.prototype._attachBoundary = function(boundarySemantic, boundaryElement) { + var translate = this._translate; + var hostSemantic = boundarySemantic.attachedToRef; + + if (!hostSemantic) { + throw new Error(translate('missing {semantic}#attachedToRef', { + semantic: elementToString(boundarySemantic) + })); + } + + var host = this._elementRegistry.get(hostSemantic.id), + attachers = host && host.attachers; + + if (!host) { + throw notYetDrawn(translate, boundarySemantic, hostSemantic, 'attachedToRef'); + } + + // wire element.host <> host.attachers + boundaryElement.host = host; + + if (!attachers) { + host.attachers = attachers = []; + } + + if (attachers.indexOf(boundaryElement) === -1) { + attachers.push(boundaryElement); + } + }; + + + /** + * add label for an element + */ + BpmnImporter.prototype.addLabel = function(semantic, di, element) { + var bounds, + text, + label; + + bounds = getExternalLabelBounds(di, element); + + text = getLabel(element); + + if (text) { + + // get corrected bounds from actual layouted text + bounds = this._textRenderer.getExternalLabelBounds(bounds, text); + } + + label = this._elementFactory.createLabel(elementData(semantic, di, { + id: semantic.id + '_label', + labelTarget: element, + type: 'label', + hidden: element.hidden || !getLabel(element), + x: Math.round(bounds.x), + y: Math.round(bounds.y), + width: Math.round(bounds.width), + height: Math.round(bounds.height) + })); + + return this._canvas.addShape(label, element.parent); + }; + + /** + * Return the drawn connection end based on the given side. + * + * @throws {Error} if the end is not yet drawn + */ + BpmnImporter.prototype._getEnd = function(semantic, side) { + + var element, + refSemantic, + type = semantic.$type, + translate = this._translate; + + refSemantic = semantic[side + 'Ref']; + + // handle mysterious isMany DataAssociation#sourceRef + if (side === 'source' && type === 'bpmn:DataInputAssociation') { + refSemantic = refSemantic && refSemantic[0]; + } + + // fix source / target for DataInputAssociation / DataOutputAssociation + if (side === 'source' && type === 'bpmn:DataOutputAssociation' || + side === 'target' && type === 'bpmn:DataInputAssociation') { + + refSemantic = semantic.$parent; + } + + element = refSemantic && this._getElement(refSemantic); + + if (element) { + return element; + } + + if (refSemantic) { + throw notYetDrawn(translate, semantic, refSemantic, side + 'Ref'); + } else { + throw new Error(translate('{semantic}#{side} Ref not specified', { + semantic: elementToString(semantic), + side: side + })); + } + }; + + BpmnImporter.prototype._getSource = function(semantic) { + return this._getEnd(semantic, 'source'); + }; + + BpmnImporter.prototype._getTarget = function(semantic) { + return this._getEnd(semantic, 'target'); + }; + + + BpmnImporter.prototype._getElement = function(semantic) { + return this._elementRegistry.get(semantic.id); + }; + + + // helpers //////////////////// + + function isPointInsideBBox$1(bbox, point) { + var x = point.x, + y = point.y; + + return x >= bbox.x && + x <= bbox.x + bbox.width && + y >= bbox.y && + y <= bbox.y + bbox.height; + } + + function isFrameElement(semantic) { + return is$1(semantic, 'bpmn:Group'); + } + + var ImportModule = { + __depends__: [ + translate + ], + bpmnImporter: [ 'type', BpmnImporter ] + }; + + var CoreModule = { + __depends__: [ + DrawModule, + ImportModule + ] + }; + + function __stopPropagation(event) { + if (!event || typeof event.stopPropagation !== 'function') { + return; + } + + event.stopPropagation(); + } + + + function getOriginal$1(event) { + return event.originalEvent || event.srcEvent; + } + + + function stopPropagation$1(event, immediate) { + __stopPropagation(event); + __stopPropagation(getOriginal$1(event)); + } + + + function toPoint(event) { + + if (event.pointers && event.pointers.length) { + event = event.pointers[0]; + } + + if (event.touches && event.touches.length) { + event = event.touches[0]; + } + + return event ? { + x: event.clientX, + y: event.clientY + } : null; + } + + function isMac() { + return (/mac/i).test(navigator.platform); + } + + function isButton(event, button) { + return (getOriginal$1(event) || event).button === button; + } + + function isPrimaryButton(event) { + + // button === 0 -> left áka primary mouse button + return isButton(event, 0); + } + + function isAuxiliaryButton(event) { + + // button === 1 -> auxiliary áka wheel button + return isButton(event, 1); + } + + function hasPrimaryModifier(event) { + var originalEvent = getOriginal$1(event) || event; + + if (!isPrimaryButton(event)) { + return false; + } + + // Use cmd as primary modifier key for mac OS + if (isMac()) { + return originalEvent.metaKey; + } else { + return originalEvent.ctrlKey; + } + } + + + function hasSecondaryModifier(event) { + var originalEvent = getOriginal$1(event) || event; + + return isPrimaryButton(event) && originalEvent.shiftKey; + } + + function allowAll(event) { return true; } + + function allowPrimaryAndAuxiliary(event) { + return isPrimaryButton(event) || isAuxiliaryButton(event); + } + + var LOW_PRIORITY$q = 500; + + + /** + * A plugin that provides interaction events for diagram elements. + * + * It emits the following events: + * + * * element.click + * * element.contextmenu + * * element.dblclick + * * element.hover + * * element.mousedown + * * element.mousemove + * * element.mouseup + * * element.out + * + * Each event is a tuple { element, gfx, originalEvent }. + * + * Canceling the event via Event#preventDefault() + * prevents the original DOM operation. + * + * @param {EventBus} eventBus + */ + function InteractionEvents(eventBus, elementRegistry, styles) { + + var self = this; + + /** + * Fire an interaction event. + * + * @param {string} type local event name, e.g. element.click. + * @param {DOMEvent} event native event + * @param {djs.model.Base} [element] the diagram element to emit the event on; + * defaults to the event target + */ + function fire(type, event, element) { + + if (isIgnored(type, event)) { + return; + } + + var target, gfx, returnValue; + + if (!element) { + target = event.delegateTarget || event.target; + + if (target) { + gfx = target; + element = elementRegistry.get(gfx); + } + } else { + gfx = elementRegistry.getGraphics(element); + } + + if (!gfx || !element) { + return; + } + + returnValue = eventBus.fire(type, { + element: element, + gfx: gfx, + originalEvent: event + }); + + if (returnValue === false) { + event.stopPropagation(); + event.preventDefault(); + } + } + + // TODO(nikku): document this + var handlers = {}; + + function mouseHandler(localEventName) { + return handlers[localEventName]; + } + + function isIgnored(localEventName, event) { + + var filter = ignoredFilters[localEventName] || isPrimaryButton; + + // only react on left mouse button interactions + // except for interaction events that are enabled + // for secundary mouse button + return !filter(event); + } + + var bindings = { + click: 'element.click', + contextmenu: 'element.contextmenu', + dblclick: 'element.dblclick', + mousedown: 'element.mousedown', + mousemove: 'element.mousemove', + mouseover: 'element.hover', + mouseout: 'element.out', + mouseup: 'element.mouseup', + }; + + var ignoredFilters = { + 'element.contextmenu': allowAll, + 'element.mousedown': allowPrimaryAndAuxiliary, + 'element.mouseup': allowPrimaryAndAuxiliary, + 'element.click': allowPrimaryAndAuxiliary, + 'element.dblclick': allowPrimaryAndAuxiliary + }; + + + // manual event trigger ////////// + + /** + * Trigger an interaction event (based on a native dom event) + * on the target shape or connection. + * + * @param {string} eventName the name of the triggered DOM event + * @param {MouseEvent} event + * @param {djs.model.Base} targetElement + */ + function triggerMouseEvent(eventName, event, targetElement) { + + // i.e. element.mousedown... + var localEventName = bindings[eventName]; + + if (!localEventName) { + throw new Error('unmapped DOM event name <' + eventName + '>'); + } + + return fire(localEventName, event, targetElement); + } + + + var ELEMENT_SELECTOR = 'svg, .djs-element'; + + // event handling /////// + + function registerEvent(node, event, localEvent, ignoredFilter) { + + var handler = handlers[localEvent] = function(event) { + fire(localEvent, event); + }; + + if (ignoredFilter) { + ignoredFilters[localEvent] = ignoredFilter; + } + + handler.$delegate = delegate.bind(node, ELEMENT_SELECTOR, event, handler); + } + + function unregisterEvent(node, event, localEvent) { + + var handler = mouseHandler(localEvent); + + if (!handler) { + return; + } + + delegate.unbind(node, event, handler.$delegate); + } + + function registerEvents(svg) { + forEach$1(bindings, function(val, key) { + registerEvent(svg, key, val); + }); + } + + function unregisterEvents(svg) { + forEach$1(bindings, function(val, key) { + unregisterEvent(svg, key, val); + }); + } + + eventBus.on('canvas.destroy', function(event) { + unregisterEvents(event.svg); + }); + + eventBus.on('canvas.init', function(event) { + registerEvents(event.svg); + }); + + + // hit box updating //////////////// + + eventBus.on([ 'shape.added', 'connection.added' ], function(event) { + var element = event.element, + gfx = event.gfx; + + eventBus.fire('interactionEvents.createHit', { element: element, gfx: gfx }); + }); + + // Update djs-hit on change. + // A low priortity is necessary, because djs-hit of labels has to be updated + // after the label bounds have been updated in the renderer. + eventBus.on([ + 'shape.changed', + 'connection.changed' + ], LOW_PRIORITY$q, function(event) { + + var element = event.element, + gfx = event.gfx; + + eventBus.fire('interactionEvents.updateHit', { element: element, gfx: gfx }); + }); + + eventBus.on('interactionEvents.createHit', LOW_PRIORITY$q, function(event) { + var element = event.element, + gfx = event.gfx; + + self.createDefaultHit(element, gfx); + }); + + eventBus.on('interactionEvents.updateHit', function(event) { + var element = event.element, + gfx = event.gfx; + + self.updateDefaultHit(element, gfx); + }); + + + // hit styles //////////// + + var STROKE_HIT_STYLE = createHitStyle('djs-hit djs-hit-stroke'); + + var CLICK_STROKE_HIT_STYLE = createHitStyle('djs-hit djs-hit-click-stroke'); + + var ALL_HIT_STYLE = createHitStyle('djs-hit djs-hit-all'); + + var NO_MOVE_HIT_STYLE = createHitStyle('djs-hit djs-hit-no-move'); + + var HIT_TYPES = { + 'all': ALL_HIT_STYLE, + 'click-stroke': CLICK_STROKE_HIT_STYLE, + 'stroke': STROKE_HIT_STYLE, + 'no-move': NO_MOVE_HIT_STYLE + }; + + function createHitStyle(classNames, attrs) { + + attrs = assign({ + stroke: 'white', + strokeWidth: 15 + }, attrs || {}); + + return styles.cls(classNames, [ 'no-fill', 'no-border' ], attrs); + } + + + // style helpers /////////////// + + function applyStyle(hit, type) { + + var attrs = HIT_TYPES[type]; + + if (!attrs) { + throw new Error('invalid hit type <' + type + '>'); + } + + attr(hit, attrs); + + return hit; + } + + function appendHit(gfx, hit) { + append(gfx, hit); + } + + + // API + + /** + * Remove hints on the given graphics. + * + * @param {SVGElement} gfx + */ + this.removeHits = function(gfx) { + var hits = all('.djs-hit', gfx); + + forEach$1(hits, remove$1); + }; + + /** + * Create default hit for the given element. + * + * @param {djs.model.Base} element + * @param {SVGElement} gfx + * + * @return {SVGElement} created hit + */ + this.createDefaultHit = function(element, gfx) { + var waypoints = element.waypoints, + isFrame = element.isFrame, + boxType; + + if (waypoints) { + return this.createWaypointsHit(gfx, waypoints); + } else { + + boxType = isFrame ? 'stroke' : 'all'; + + return this.createBoxHit(gfx, boxType, { + width: element.width, + height: element.height + }); + } + }; + + /** + * Create hits for the given waypoints. + * + * @param {SVGElement} gfx + * @param {Array} waypoints + * + * @return {SVGElement} + */ + this.createWaypointsHit = function(gfx, waypoints) { + + var hit = createLine(waypoints); + + applyStyle(hit, 'stroke'); + + appendHit(gfx, hit); + + return hit; + }; + + /** + * Create hits for a box. + * + * @param {SVGElement} gfx + * @param {string} hitType + * @param {Object} attrs + * + * @return {SVGElement} + */ + this.createBoxHit = function(gfx, type, attrs) { + + attrs = assign({ + x: 0, + y: 0 + }, attrs); + + var hit = create$1('rect'); + + applyStyle(hit, type); + + attr(hit, attrs); + + appendHit(gfx, hit); + + return hit; + }; + + /** + * Update default hit of the element. + * + * @param {djs.model.Base} element + * @param {SVGElement} gfx + * + * @return {SVGElement} updated hit + */ + this.updateDefaultHit = function(element, gfx) { + + var hit = query('.djs-hit', gfx); + + if (!hit) { + return; + } + + if (element.waypoints) { + updateLine(hit, element.waypoints); + } else { + attr(hit, { + width: element.width, + height: element.height + }); + } + + return hit; + }; + + this.fire = fire; + + this.triggerMouseEvent = triggerMouseEvent; + + this.mouseHandler = mouseHandler; + + this.registerEvent = registerEvent; + this.unregisterEvent = unregisterEvent; + } + + + InteractionEvents.$inject = [ + 'eventBus', + 'elementRegistry', + 'styles' + ]; + + + /** + * An event indicating that the mouse hovered over an element + * + * @event element.hover + * + * @type {Object} + * @property {djs.model.Base} element + * @property {SVGElement} gfx + * @property {Event} originalEvent + */ + + /** + * An event indicating that the mouse has left an element + * + * @event element.out + * + * @type {Object} + * @property {djs.model.Base} element + * @property {SVGElement} gfx + * @property {Event} originalEvent + */ + + /** + * An event indicating that the mouse has clicked an element + * + * @event element.click + * + * @type {Object} + * @property {djs.model.Base} element + * @property {SVGElement} gfx + * @property {Event} originalEvent + */ + + /** + * An event indicating that the mouse has double clicked an element + * + * @event element.dblclick + * + * @type {Object} + * @property {djs.model.Base} element + * @property {SVGElement} gfx + * @property {Event} originalEvent + */ + + /** + * An event indicating that the mouse has gone down on an element. + * + * @event element.mousedown + * + * @type {Object} + * @property {djs.model.Base} element + * @property {SVGElement} gfx + * @property {Event} originalEvent + */ + + /** + * An event indicating that the mouse has gone up on an element. + * + * @event element.mouseup + * + * @type {Object} + * @property {djs.model.Base} element + * @property {SVGElement} gfx + * @property {Event} originalEvent + */ + + /** + * An event indicating that the context menu action is triggered + * via mouse or touch controls. + * + * @event element.contextmenu + * + * @type {Object} + * @property {djs.model.Base} element + * @property {SVGElement} gfx + * @property {Event} originalEvent + */ + + var InteractionEventsModule$1 = { + __init__: [ 'interactionEvents' ], + interactionEvents: [ 'type', InteractionEvents ] + }; + + var LOW_PRIORITY$p = 500; + + + /** + * @class + * + * A plugin that adds an outline to shapes and connections that may be activated and styled + * via CSS classes. + * + * @param {EventBus} eventBus + * @param {Styles} styles + * @param {ElementRegistry} elementRegistry + */ + function Outline(eventBus, styles, elementRegistry) { + + this.offset = 6; + + var OUTLINE_STYLE = styles.cls('djs-outline', [ 'no-fill' ]); + + var self = this; + + function createOutline(gfx, bounds) { + var outline = create$1('rect'); + + attr(outline, assign({ + x: 10, + y: 10, + rx: 3, + width: 100, + height: 100 + }, OUTLINE_STYLE)); + + append(gfx, outline); + + return outline; + } + + // A low priortity is necessary, because outlines of labels have to be updated + // after the label bounds have been updated in the renderer. + eventBus.on([ 'shape.added', 'shape.changed' ], LOW_PRIORITY$p, function(event) { + var element = event.element, + gfx = event.gfx; + + var outline = query('.djs-outline', gfx); + + if (!outline) { + outline = createOutline(gfx); + } + + self.updateShapeOutline(outline, element); + }); + + eventBus.on([ 'connection.added', 'connection.changed' ], function(event) { + var element = event.element, + gfx = event.gfx; + + var outline = query('.djs-outline', gfx); + + if (!outline) { + outline = createOutline(gfx); + } + + self.updateConnectionOutline(outline, element); + }); + } + + + /** + * Updates the outline of a shape respecting the dimension of the + * element and an outline offset. + * + * @param {SVGElement} outline + * @param {djs.model.Base} element + */ + Outline.prototype.updateShapeOutline = function(outline, element) { + + attr(outline, { + x: -this.offset, + y: -this.offset, + width: element.width + this.offset * 2, + height: element.height + this.offset * 2 + }); + + }; + + + /** + * Updates the outline of a connection respecting the bounding box of + * the connection and an outline offset. + * + * @param {SVGElement} outline + * @param {djs.model.Base} element + */ + Outline.prototype.updateConnectionOutline = function(outline, connection) { + + var bbox = getBBox(connection); + + attr(outline, { + x: bbox.x - this.offset, + y: bbox.y - this.offset, + width: bbox.width + this.offset * 2, + height: bbox.height + this.offset * 2 + }); + + }; + + + Outline.$inject = [ 'eventBus', 'styles', 'elementRegistry' ]; + + var OutlineModule = { + __init__: [ 'outline' ], + outline: [ 'type', Outline ] + }; + + /** + * A service that offers the current selection in a diagram. + * Offers the api to control the selection, too. + * + * @class + * + * @param {EventBus} eventBus the event bus + */ + function Selection(eventBus, canvas) { + + this._eventBus = eventBus; + this._canvas = canvas; + + this._selectedElements = []; + + var self = this; + + eventBus.on([ 'shape.remove', 'connection.remove' ], function(e) { + var element = e.element; + self.deselect(element); + }); + + eventBus.on([ 'diagram.clear', 'root.set' ], function(e) { + self.select(null); + }); + } + + Selection.$inject = [ 'eventBus', 'canvas' ]; + + + Selection.prototype.deselect = function(element) { + var selectedElements = this._selectedElements; + + var idx = selectedElements.indexOf(element); + + if (idx !== -1) { + var oldSelection = selectedElements.slice(); + + selectedElements.splice(idx, 1); + + this._eventBus.fire('selection.changed', { oldSelection: oldSelection, newSelection: selectedElements }); + } + }; + + + Selection.prototype.get = function() { + return this._selectedElements; + }; + + Selection.prototype.isSelected = function(element) { + return this._selectedElements.indexOf(element) !== -1; + }; + + + /** + * This method selects one or more elements on the diagram. + * + * By passing an additional add parameter you can decide whether or not the element(s) + * should be added to the already existing selection or not. + * + * @method Selection#select + * + * @param {Object|Object[]} elements element or array of elements to be selected + * @param {boolean} [add] whether the element(s) should be appended to the current selection, defaults to false + */ + Selection.prototype.select = function(elements, add) { + var selectedElements = this._selectedElements, + oldSelection = selectedElements.slice(); + + if (!isArray$3(elements)) { + elements = elements ? [ elements ] : []; + } + + var canvas = this._canvas; + + var rootElement = canvas.getRootElement(); + + elements = elements.filter(function(element) { + var elementRoot = canvas.findRoot(element); + + return rootElement === elementRoot; + }); + + // selection may be cleared by passing an empty array or null + // to the method + if (add) { + forEach$1(elements, function(element) { + if (selectedElements.indexOf(element) !== -1) { + + // already selected + return; + } else { + selectedElements.push(element); + } + }); + } else { + this._selectedElements = selectedElements = elements.slice(); + } + + this._eventBus.fire('selection.changed', { oldSelection: oldSelection, newSelection: selectedElements }); + }; + + var MARKER_HOVER = 'hover', + MARKER_SELECTED = 'selected'; + + var SELECTION_OUTLINE_PADDING = 6; + + + /** + * A plugin that adds a visible selection UI to shapes and connections + * by appending the hover and selected classes to them. + * + * @class + * + * Makes elements selectable, too. + * + * @param {Canvas} canvas + * @param {EventBus} eventBus + */ + function SelectionVisuals(canvas, eventBus, selection) { + this._canvas = canvas; + + var self = this; + + this._multiSelectionBox = null; + + function addMarker(e, cls) { + canvas.addMarker(e, cls); + } + + function removeMarker(e, cls) { + canvas.removeMarker(e, cls); + } + + eventBus.on('element.hover', function(event) { + addMarker(event.element, MARKER_HOVER); + }); + + eventBus.on('element.out', function(event) { + removeMarker(event.element, MARKER_HOVER); + }); + + eventBus.on('selection.changed', function(event) { + + function deselect(s) { + removeMarker(s, MARKER_SELECTED); + } + + function select(s) { + addMarker(s, MARKER_SELECTED); + } + + var oldSelection = event.oldSelection, + newSelection = event.newSelection; + + forEach$1(oldSelection, function(e) { + if (newSelection.indexOf(e) === -1) { + deselect(e); + } + }); + + forEach$1(newSelection, function(e) { + if (oldSelection.indexOf(e) === -1) { + select(e); + } + }); + + self._updateSelectionOutline(newSelection); + }); + + + eventBus.on('element.changed', function(event) { + if (selection.isSelected(event.element)) { + self._updateSelectionOutline(selection.get()); + } + }); + } + + SelectionVisuals.$inject = [ + 'canvas', + 'eventBus', + 'selection' + ]; + + SelectionVisuals.prototype._updateSelectionOutline = function(selection) { + var layer = this._canvas.getLayer('selectionOutline'); + + clear(layer); + + var enabled = selection.length > 1; + + var container = this._canvas.getContainer(); + + classes(container)[enabled ? 'add' : 'remove']('djs-multi-select'); + + if (!enabled) { + return; + } + + var bBox = addSelectionOutlinePadding(getBBox(selection)); + + var rect = create$1('rect'); + + attr(rect, assign({ + rx: 3 + }, bBox)); + + classes(rect).add('djs-selection-outline'); + + append(layer, rect); + }; + + // helpers ////////// + + function addSelectionOutlinePadding(bBox) { + return { + x: bBox.x - SELECTION_OUTLINE_PADDING, + y: bBox.y - SELECTION_OUTLINE_PADDING, + width: bBox.width + SELECTION_OUTLINE_PADDING * 2, + height: bBox.height + SELECTION_OUTLINE_PADDING * 2 + }; + } + + function SelectionBehavior(eventBus, selection, canvas, elementRegistry) { + + // Select elements on create + eventBus.on('create.end', 500, function(event) { + var context = event.context, + canExecute = context.canExecute, + elements = context.elements, + hints = context.hints || {}, + autoSelect = hints.autoSelect; + + if (canExecute) { + if (autoSelect === false) { + + // Select no elements + return; + } + + if (isArray$3(autoSelect)) { + selection.select(autoSelect); + } else { + + // Select all elements by default + selection.select(elements.filter(isShown)); + } + } + }); + + // Select connection targets on connect + eventBus.on('connect.end', 500, function(event) { + var context = event.context, + connection = context.connection; + + if (connection) { + selection.select(connection); + } + }); + + // Select shapes on move + eventBus.on('shape.move.end', 500, function(event) { + var previousSelection = event.previousSelection || []; + + var shape = elementRegistry.get(event.context.shape.id); + + // Always select main shape on move + var isSelected = find(previousSelection, function(selectedShape) { + return shape.id === selectedShape.id; + }); + + if (!isSelected) { + selection.select(shape); + } + }); + + // Select elements on click + eventBus.on('element.click', function(event) { + + if (!isPrimaryButton(event)) { + return; + } + + var element = event.element; + + if (element === canvas.getRootElement()) { + element = null; + } + + var isSelected = selection.isSelected(element), + isMultiSelect = selection.get().length > 1; + + // Add to selection if CTRL or SHIFT pressed + var add = hasPrimaryModifier(event) || hasSecondaryModifier(event); + + if (isSelected && isMultiSelect) { + if (add) { + + // Deselect element + return selection.deselect(element); + } else { + + // Select element only + return selection.select(element); + } + } else if (!isSelected) { + + // Select element + selection.select(element, add); + } else { + + // Deselect element + selection.deselect(element); + } + }); + } + + SelectionBehavior.$inject = [ + 'eventBus', + 'selection', + 'canvas', + 'elementRegistry' + ]; + + + function isShown(element) { + return !element.hidden; + } + + var SelectionModule = { + __init__: [ 'selectionVisuals', 'selectionBehavior' ], + __depends__: [ + InteractionEventsModule$1, + OutlineModule + ], + selection: [ 'type', Selection ], + selectionVisuals: [ 'type', SelectionVisuals ], + selectionBehavior: [ 'type', SelectionBehavior ] + }; + + /** + * Util that provides unique IDs. + * + * @class djs.util.IdGenerator + * @constructor + * @memberOf djs.util + * + * The ids can be customized via a given prefix and contain a random value to avoid collisions. + * + * @param {string} prefix a prefix to prepend to generated ids (for better readability) + */ + function IdGenerator(prefix) { + + this._counter = 0; + this._prefix = (prefix ? prefix + '-' : '') + Math.floor(Math.random() * 1000000000) + '-'; + } + + /** + * Returns a next unique ID. + * + * @method djs.util.IdGenerator#next + * + * @returns {string} the id + */ + IdGenerator.prototype.next = function() { + return this._prefix + (++this._counter); + }; + + // document wide unique overlay ids + var ids$1 = new IdGenerator('ov'); + + var LOW_PRIORITY$o = 500; + + + /** + * A service that allows users to attach overlays to diagram elements. + * + * The overlay service will take care of overlay positioning during updates. + * + * @example + * + * // add a pink badge on the top left of the shape + * overlays.add(someShape, { + * position: { + * top: -5, + * left: -5 + * }, + * html: '
0
' + * }); + * + * // or add via shape id + * + * overlays.add('some-element-id', { + * position: { + * top: -5, + * left: -5 + * } + * html: '
0
' + * }); + * + * // or add with optional type + * + * overlays.add(someShape, 'badge', { + * position: { + * top: -5, + * left: -5 + * } + * html: '
0
' + * }); + * + * + * // remove an overlay + * + * var id = overlays.add(...); + * overlays.remove(id); + * + * + * You may configure overlay defaults during tool by providing a `config` module + * with `overlays.defaults` as an entry: + * + * { + * overlays: { + * defaults: { + * show: { + * minZoom: 0.7, + * maxZoom: 5.0 + * }, + * scale: { + * min: 1 + * } + * } + * } + * + * @param {Object} config + * @param {EventBus} eventBus + * @param {Canvas} canvas + * @param {ElementRegistry} elementRegistry + */ + function Overlays(config, eventBus, canvas, elementRegistry) { + + this._eventBus = eventBus; + this._canvas = canvas; + this._elementRegistry = elementRegistry; + + this._ids = ids$1; + + this._overlayDefaults = assign({ + + // no show constraints + show: null, + + // always scale + scale: true + }, config && config.defaults); + + /** + * Mapping overlayId -> overlay + */ + this._overlays = {}; + + /** + * Mapping elementId -> overlay container + */ + this._overlayContainers = []; + + // root html element for all overlays + this._overlayRoot = createRoot$1(canvas.getContainer()); + + this._init(); + } + + + Overlays.$inject = [ + 'config.overlays', + 'eventBus', + 'canvas', + 'elementRegistry' + ]; + + + /** + * Returns the overlay with the specified id or a list of overlays + * for an element with a given type. + * + * @example + * + * // return the single overlay with the given id + * overlays.get('some-id'); + * + * // return all overlays for the shape + * overlays.get({ element: someShape }); + * + * // return all overlays on shape with type 'badge' + * overlays.get({ element: someShape, type: 'badge' }); + * + * // shape can also be specified as id + * overlays.get({ element: 'element-id', type: 'badge' }); + * + * + * @param {Object} search + * @param {string} [search.id] + * @param {string|djs.model.Base} [search.element] + * @param {string} [search.type] + * + * @return {Object|Array} the overlay(s) + */ + Overlays.prototype.get = function(search) { + + if (isString(search)) { + search = { id: search }; + } + + if (isString(search.element)) { + search.element = this._elementRegistry.get(search.element); + } + + if (search.element) { + var container = this._getOverlayContainer(search.element, true); + + // return a list of overlays when searching by element (+type) + if (container) { + return search.type ? filter(container.overlays, matchPattern({ type: search.type })) : container.overlays.slice(); + } else { + return []; + } + } else + if (search.type) { + return filter(this._overlays, matchPattern({ type: search.type })); + } else { + + // return single element when searching by id + return search.id ? this._overlays[search.id] : null; + } + }; + + /** + * Adds a HTML overlay to an element. + * + * @param {string|djs.model.Base} element attach overlay to this shape + * @param {string} [type] optional type to assign to the overlay + * @param {Object} overlay the overlay configuration + * + * @param {string|DOMElement} overlay.html html element to use as an overlay + * @param {Object} [overlay.show] show configuration + * @param {number} [overlay.show.minZoom] minimal zoom level to show the overlay + * @param {number} [overlay.show.maxZoom] maximum zoom level to show the overlay + * @param {Object} overlay.position where to attach the overlay + * @param {number} [overlay.position.left] relative to element bbox left attachment + * @param {number} [overlay.position.top] relative to element bbox top attachment + * @param {number} [overlay.position.bottom] relative to element bbox bottom attachment + * @param {number} [overlay.position.right] relative to element bbox right attachment + * @param {boolean|Object} [overlay.scale=true] false to preserve the same size regardless of + * diagram zoom + * @param {number} [overlay.scale.min] + * @param {number} [overlay.scale.max] + * + * @return {string} id that may be used to reference the overlay for update or removal + */ + Overlays.prototype.add = function(element, type, overlay) { + + if (isObject(type)) { + overlay = type; + type = null; + } + + if (!element.id) { + element = this._elementRegistry.get(element); + } + + if (!overlay.position) { + throw new Error('must specifiy overlay position'); + } + + if (!overlay.html) { + throw new Error('must specifiy overlay html'); + } + + if (!element) { + throw new Error('invalid element specified'); + } + + var id = this._ids.next(); + + overlay = assign({}, this._overlayDefaults, overlay, { + id: id, + type: type, + element: element, + html: overlay.html + }); + + this._addOverlay(overlay); + + return id; + }; + + + /** + * Remove an overlay with the given id or all overlays matching the given filter. + * + * @see Overlays#get for filter options. + * + * @param {string|object} [filter] + */ + Overlays.prototype.remove = function(filter) { + + var overlays = this.get(filter) || []; + + if (!isArray$3(overlays)) { + overlays = [ overlays ]; + } + + var self = this; + + forEach$1(overlays, function(overlay) { + + var container = self._getOverlayContainer(overlay.element, true); + + if (overlay) { + remove$2(overlay.html); + remove$2(overlay.htmlContainer); + + delete overlay.htmlContainer; + delete overlay.element; + + delete self._overlays[overlay.id]; + } + + if (container) { + var idx = container.overlays.indexOf(overlay); + if (idx !== -1) { + container.overlays.splice(idx, 1); + } + } + }); + + }; + + + Overlays.prototype.show = function() { + setVisible$1(this._overlayRoot); + }; + + + Overlays.prototype.hide = function() { + setVisible$1(this._overlayRoot, false); + }; + + Overlays.prototype.clear = function() { + this._overlays = {}; + + this._overlayContainers = []; + + clear$1(this._overlayRoot); + }; + + Overlays.prototype._updateOverlayContainer = function(container) { + var element = container.element, + html = container.html; + + // update container left,top according to the elements x,y coordinates + // this ensures we can attach child elements relative to this container + + var x = element.x, + y = element.y; + + if (element.waypoints) { + var bbox = getBBox(element); + x = bbox.x; + y = bbox.y; + } + + setPosition$1(html, x, y); + + attr$1(container.html, 'data-container-id', element.id); + }; + + + Overlays.prototype._updateOverlay = function(overlay) { + + var position = overlay.position, + htmlContainer = overlay.htmlContainer, + element = overlay.element; + + // update overlay html relative to shape because + // it is already positioned on the element + + // update relative + var left = position.left, + top = position.top; + + if (position.right !== undefined) { + + var width; + + if (element.waypoints) { + width = getBBox(element).width; + } else { + width = element.width; + } + + left = position.right * -1 + width; + } + + if (position.bottom !== undefined) { + + var height; + + if (element.waypoints) { + height = getBBox(element).height; + } else { + height = element.height; + } + + top = position.bottom * -1 + height; + } + + setPosition$1(htmlContainer, left || 0, top || 0); + this._updateOverlayVisibilty(overlay, this._canvas.viewbox()); + }; + + + Overlays.prototype._createOverlayContainer = function(element) { + var html = domify('
'); + assign$1(html, { position: 'absolute' }); + + this._overlayRoot.appendChild(html); + + var container = { + html: html, + element: element, + overlays: [] + }; + + this._updateOverlayContainer(container); + + this._overlayContainers.push(container); + + return container; + }; + + + Overlays.prototype._updateRoot = function(viewbox) { + var scale = viewbox.scale || 1; + + var matrix = 'matrix(' + + [ + scale, + 0, + 0, + scale, + -1 * viewbox.x * scale, + -1 * viewbox.y * scale + ].join(',') + + ')'; + + setTransform$1(this._overlayRoot, matrix); + }; + + + Overlays.prototype._getOverlayContainer = function(element, raw) { + var container = find(this._overlayContainers, function(c) { + return c.element === element; + }); + + + if (!container && !raw) { + return this._createOverlayContainer(element); + } + + return container; + }; + + + Overlays.prototype._addOverlay = function(overlay) { + + var id = overlay.id, + element = overlay.element, + html = overlay.html, + htmlContainer, + overlayContainer; + + // unwrap jquery (for those who need it) + if (html.get && html.constructor.prototype.jquery) { + html = html.get(0); + } + + // create proper html elements from + // overlay HTML strings + if (isString(html)) { + html = domify(html); + } + + overlayContainer = this._getOverlayContainer(element); + + htmlContainer = domify('
'); + assign$1(htmlContainer, { position: 'absolute' }); + + htmlContainer.appendChild(html); + + if (overlay.type) { + classes$1(htmlContainer).add('djs-overlay-' + overlay.type); + } + + var elementRoot = this._canvas.findRoot(element); + var activeRoot = this._canvas.getRootElement(); + + setVisible$1(htmlContainer, elementRoot === activeRoot); + + overlay.htmlContainer = htmlContainer; + + overlayContainer.overlays.push(overlay); + overlayContainer.html.appendChild(htmlContainer); + + this._overlays[id] = overlay; + + this._updateOverlay(overlay); + this._updateOverlayVisibilty(overlay, this._canvas.viewbox()); + }; + + + Overlays.prototype._updateOverlayVisibilty = function(overlay, viewbox) { + var show = overlay.show, + rootElement = this._canvas.findRoot(overlay.element), + minZoom = show && show.minZoom, + maxZoom = show && show.maxZoom, + htmlContainer = overlay.htmlContainer, + activeRootElement = this._canvas.getRootElement(), + visible = true; + + if (rootElement !== activeRootElement) { + visible = false; + } else if (show) { + if ( + (isDefined(minZoom) && minZoom > viewbox.scale) || + (isDefined(maxZoom) && maxZoom < viewbox.scale) + ) { + visible = false; + } + } + + setVisible$1(htmlContainer, visible); + + this._updateOverlayScale(overlay, viewbox); + }; + + + Overlays.prototype._updateOverlayScale = function(overlay, viewbox) { + var shouldScale = overlay.scale, + minScale, + maxScale, + htmlContainer = overlay.htmlContainer; + + var scale, transform = ''; + + if (shouldScale !== true) { + + if (shouldScale === false) { + minScale = 1; + maxScale = 1; + } else { + minScale = shouldScale.min; + maxScale = shouldScale.max; + } + + if (isDefined(minScale) && viewbox.scale < minScale) { + scale = (1 / viewbox.scale || 1) * minScale; + } + + if (isDefined(maxScale) && viewbox.scale > maxScale) { + scale = (1 / viewbox.scale || 1) * maxScale; + } + } + + if (isDefined(scale)) { + transform = 'scale(' + scale + ',' + scale + ')'; + } + + setTransform$1(htmlContainer, transform); + }; + + + Overlays.prototype._updateOverlaysVisibilty = function(viewbox) { + + var self = this; + + forEach$1(this._overlays, function(overlay) { + self._updateOverlayVisibilty(overlay, viewbox); + }); + }; + + + Overlays.prototype._init = function() { + + var eventBus = this._eventBus; + + var self = this; + + + // scroll/zoom integration + + function updateViewbox(viewbox) { + self._updateRoot(viewbox); + self._updateOverlaysVisibilty(viewbox); + + self.show(); + } + + eventBus.on('canvas.viewbox.changing', function(event) { + self.hide(); + }); + + eventBus.on('canvas.viewbox.changed', function(event) { + updateViewbox(event.viewbox); + }); + + + // remove integration + + eventBus.on([ 'shape.remove', 'connection.remove' ], function(e) { + var element = e.element; + var overlays = self.get({ element: element }); + + forEach$1(overlays, function(o) { + self.remove(o.id); + }); + + var container = self._getOverlayContainer(element); + + if (container) { + remove$2(container.html); + var i = self._overlayContainers.indexOf(container); + if (i !== -1) { + self._overlayContainers.splice(i, 1); + } + } + }); + + + // move integration + + eventBus.on('element.changed', LOW_PRIORITY$o, function(e) { + var element = e.element; + + var container = self._getOverlayContainer(element, true); + + if (container) { + forEach$1(container.overlays, function(overlay) { + self._updateOverlay(overlay); + }); + + self._updateOverlayContainer(container); + } + }); + + + // marker integration, simply add them on the overlays as classes, too. + + eventBus.on('element.marker.update', function(e) { + var container = self._getOverlayContainer(e.element, true); + if (container) { + classes$1(container.html)[e.add ? 'add' : 'remove'](e.marker); + } + }); + + + eventBus.on('root.set', function() { + self._updateOverlaysVisibilty(self._canvas.viewbox()); + }); + + // clear overlays with diagram + + eventBus.on('diagram.clear', this.clear, this); + }; + + + + // helpers ///////////////////////////// + + function createRoot$1(parentNode) { + var root = domify( + '
' + ); + + assign$1(root, { + position: 'absolute', + width: 0, + height: 0 + }); + + parentNode.insertBefore(root, parentNode.firstChild); + + return root; + } + + function setPosition$1(el, x, y) { + assign$1(el, { left: x + 'px', top: y + 'px' }); + } + + /** + * Set element visible + * + * @param {DOMElement} el + * @param {boolean} [visible=true] + */ + function setVisible$1(el, visible) { + el.style.display = visible === false ? 'none' : ''; + } + + function setTransform$1(el, transform) { + + el.style['transform-origin'] = 'top left'; + + [ '', '-ms-', '-webkit-' ].forEach(function(prefix) { + el.style[prefix + 'transform'] = transform; + }); + } + + var OverlaysModule = { + __init__: [ 'overlays' ], + overlays: [ 'type', Overlays ] + }; + + /** + * Adds change support to the diagram, including + * + *
    + *
  • redrawing shapes and connections on change
  • + *
+ * + * @param {EventBus} eventBus + * @param {Canvas} canvas + * @param {ElementRegistry} elementRegistry + * @param {GraphicsFactory} graphicsFactory + */ + function ChangeSupport( + eventBus, canvas, elementRegistry, + graphicsFactory) { + + + // redraw shapes / connections on change + + eventBus.on('element.changed', function(event) { + + var element = event.element; + + // element might have been deleted and replaced by new element with same ID + // thus check for parent of element except for root element + if (element.parent || element === canvas.getRootElement()) { + event.gfx = elementRegistry.getGraphics(element); + } + + // shape + gfx may have been deleted + if (!event.gfx) { + return; + } + + eventBus.fire(getType(element) + '.changed', event); + }); + + eventBus.on('elements.changed', function(event) { + + var elements = event.elements; + + elements.forEach(function(e) { + eventBus.fire('element.changed', { element: e }); + }); + + graphicsFactory.updateContainments(elements); + }); + + eventBus.on('shape.changed', function(event) { + graphicsFactory.update('shape', event.element, event.gfx); + }); + + eventBus.on('connection.changed', function(event) { + graphicsFactory.update('connection', event.element, event.gfx); + }); + } + + ChangeSupport.$inject = [ + 'eventBus', + 'canvas', + 'elementRegistry', + 'graphicsFactory' + ]; + + var ChangeSupportModule = { + __init__: [ 'changeSupport' ], + changeSupport: [ 'type', ChangeSupport ] + }; + + var DEFAULT_PRIORITY$4 = 1000; + + /** + * A utility that can be used to plug-in into the command execution for + * extension and/or validation. + * + * @param {EventBus} eventBus + * + * @example + * + * import inherits from 'inherits-browser'; + * + * import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + * + * function CommandLogger(eventBus) { + * CommandInterceptor.call(this, eventBus); + * + * this.preExecute(function(event) { + * console.log('command pre-execute', event); + * }); + * } + * + * inherits(CommandLogger, CommandInterceptor); + * + */ + function CommandInterceptor(eventBus) { + this._eventBus = eventBus; + } + + CommandInterceptor.$inject = [ 'eventBus' ]; + + function unwrapEvent(fn, that) { + return function(event) { + return fn.call(that || null, event.context, event.command, event); + }; + } + + /** + * Register an interceptor for a command execution + * + * @param {string|Array} [events] list of commands to register on + * @param {string} [hook] command hook, i.e. preExecute, executed to listen on + * @param {number} [priority] the priority on which to hook into the execution + * @param {Function} handlerFn interceptor to be invoked with (event) + * @param {boolean} unwrap if true, unwrap the event and pass (context, command, event) to the + * listener instead + * @param {Object} [that] Pass context (`this`) to the handler function + */ + CommandInterceptor.prototype.on = function(events, hook, priority, handlerFn, unwrap, that) { + + if (isFunction(hook) || isNumber(hook)) { + that = unwrap; + unwrap = handlerFn; + handlerFn = priority; + priority = hook; + hook = null; + } + + if (isFunction(priority)) { + that = unwrap; + unwrap = handlerFn; + handlerFn = priority; + priority = DEFAULT_PRIORITY$4; + } + + if (isObject(unwrap)) { + that = unwrap; + unwrap = false; + } + + if (!isFunction(handlerFn)) { + throw new Error('handlerFn must be a function'); + } + + if (!isArray$3(events)) { + events = [ events ]; + } + + var eventBus = this._eventBus; + + forEach$1(events, function(event) { + + // concat commandStack(.event)?(.hook)? + var fullEvent = [ 'commandStack', event, hook ].filter(function(e) { return e; }).join('.'); + + eventBus.on(fullEvent, priority, unwrap ? unwrapEvent(handlerFn, that) : handlerFn, that); + }); + }; + + + var hooks = [ + 'canExecute', + 'preExecute', + 'preExecuted', + 'execute', + 'executed', + 'postExecute', + 'postExecuted', + 'revert', + 'reverted' + ]; + + /* + * Install hook shortcuts + * + * This will generate the CommandInterceptor#(preExecute|...|reverted) methods + * which will in term forward to CommandInterceptor#on. + */ + forEach$1(hooks, function(hook) { + + /** + * {canExecute|preExecute|preExecuted|execute|executed|postExecute|postExecuted|revert|reverted} + * + * A named hook for plugging into the command execution + * + * @param {string|Array} [events] list of commands to register on + * @param {number} [priority] the priority on which to hook into the execution + * @param {Function} handlerFn interceptor to be invoked with (event) + * @param {boolean} [unwrap=false] if true, unwrap the event and pass (context, command, event) to the + * listener instead + * @param {Object} [that] Pass context (`this`) to the handler function + */ + CommandInterceptor.prototype[hook] = function(events, priority, handlerFn, unwrap, that) { + + if (isFunction(events) || isNumber(events)) { + that = unwrap; + unwrap = handlerFn; + handlerFn = priority; + priority = events; + events = null; + } + + this.on(events, hook, priority, handlerFn, unwrap, that); + }; + }); + + /** + * A modeling behavior that ensures we set the correct root element + * as we undo and redo commands. + * + * @param {Canvas} canvas + * @param {didi.Injector} injector + */ + function RootElementsBehavior(canvas, injector) { + + injector.invoke(CommandInterceptor, this); + + this.executed(function(event) { + var context = event.context; + + if (context.rootElement) { + canvas.setRootElement(context.rootElement); + } else { + context.rootElement = canvas.getRootElement(); + } + }); + + this.revert(function(event) { + var context = event.context; + + if (context.rootElement) { + canvas.setRootElement(context.rootElement); + } + }); + } + + e(RootElementsBehavior, CommandInterceptor); + + RootElementsBehavior.$inject = [ 'canvas', 'injector' ]; + + var RootElementsModule = { + __init__: [ 'rootElementsBehavior' ], + rootElementsBehavior: [ 'type', RootElementsBehavior ] + }; + + var css_escape = {exports: {}}; + + /*! https://mths.be/cssescape v1.5.1 by @mathias | MIT license */ + + (function (module, exports) { + (function(root, factory) { + // https://github.com/umdjs/umd/blob/master/returnExports.js + { + // For Node.js. + module.exports = factory(root); + } + }(typeof commonjsGlobal != 'undefined' ? commonjsGlobal : commonjsGlobal, function(root) { + + if (root.CSS && root.CSS.escape) { + return root.CSS.escape; + } + + // https://drafts.csswg.org/cssom/#serialize-an-identifier + var cssEscape = function(value) { + if (arguments.length == 0) { + throw new TypeError('`CSS.escape` requires an argument.'); + } + var string = String(value); + var length = string.length; + var index = -1; + var codeUnit; + var result = ''; + var firstCodeUnit = string.charCodeAt(0); + while (++index < length) { + codeUnit = string.charCodeAt(index); + // Note: there’s no need to special-case astral symbols, surrogate + // pairs, or lone surrogates. + + // If the character is NULL (U+0000), then the REPLACEMENT CHARACTER + // (U+FFFD). + if (codeUnit == 0x0000) { + result += '\uFFFD'; + continue; + } + + if ( + // If the character is in the range [\1-\1F] (U+0001 to U+001F) or is + // U+007F, […] + (codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F || + // If the character is the first character and is in the range [0-9] + // (U+0030 to U+0039), […] + (index == 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) || + // If the character is the second character and is in the range [0-9] + // (U+0030 to U+0039) and the first character is a `-` (U+002D), […] + ( + index == 1 && + codeUnit >= 0x0030 && codeUnit <= 0x0039 && + firstCodeUnit == 0x002D + ) + ) { + // https://drafts.csswg.org/cssom/#escape-a-character-as-code-point + result += '\\' + codeUnit.toString(16) + ' '; + continue; + } + + if ( + // If the character is the first character and is a `-` (U+002D), and + // there is no second character, […] + index == 0 && + length == 1 && + codeUnit == 0x002D + ) { + result += '\\' + string.charAt(index); + continue; + } + + // If the character is not handled by one of the above rules and is + // greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or + // is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to + // U+005A), or [a-z] (U+0061 to U+007A), […] + if ( + codeUnit >= 0x0080 || + codeUnit == 0x002D || + codeUnit == 0x005F || + codeUnit >= 0x0030 && codeUnit <= 0x0039 || + codeUnit >= 0x0041 && codeUnit <= 0x005A || + codeUnit >= 0x0061 && codeUnit <= 0x007A + ) { + // the character itself + result += string.charAt(index); + continue; + } + + // Otherwise, the escaped character. + // https://drafts.csswg.org/cssom/#escape-a-character + result += '\\' + string.charAt(index); + + } + return result; + }; + + if (!root.CSS) { + root.CSS = {}; + } + + root.CSS.escape = cssEscape; + return cssEscape; + + })); + } (css_escape)); + + var cssEscape = css_escape.exports; + + var HTML_ESCAPE_MAP = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + '\'': ''' + }; + + function escapeHTML(str) { + str = '' + str; + + return str && str.replace(/[&<>"']/g, function(match) { + return HTML_ESCAPE_MAP[match]; + }); + } + + var planeSuffix = '_plane'; + + /** + * Get primary shape ID for a plane. + * + * @param {djs.model.Base|ModdleElement} element + * + * @returns {String} + */ + function getShapeIdFromPlane(element) { + var id = element.id; + + return removePlaneSuffix(id); + } + + /** + * Get plane ID for a primary shape. + * + * @param {djs.model.Base|ModdleElement} element + * + * @returns {String} + */ + function getPlaneIdFromShape(element) { + var id = element.id; + + if (is$1(element, 'bpmn:SubProcess')) { + return addPlaneSuffix(id); + } + + return id; + } + + /** + * Get plane ID for primary shape ID. + * + * @param {String} id + * + * @returns {String} + */ + function toPlaneId(id) { + return addPlaneSuffix(id); + } + + /** + * Check wether element is plane. + * + * @param {djs.model.Base|ModdleElement} element + * + * @returns {Boolean} + */ + function isPlane(element) { + var di = getDi(element); + + return is$1(di, 'bpmndi:BPMNPlane'); + } + + function addPlaneSuffix(id) { + return id + planeSuffix; + } + + function removePlaneSuffix(id) { + return id.replace(new RegExp(planeSuffix + '$'), ''); + } + + var OPEN_CLASS = 'bjs-breadcrumbs-shown'; + + + /** + * Adds overlays that allow switching planes on collapsed subprocesses. + * + * @param {eventBus} eventBus + * @param {elementRegistry} elementRegistry + * @param {overlays} overlays + * @param {canvas} canvas + */ + function DrilldownBreadcrumbs(eventBus, elementRegistry, overlays, canvas) { + var breadcrumbs = domify('
    '); + var container = canvas.getContainer(); + var containerClasses = classes$1(container); + container.appendChild(breadcrumbs); + + var boParents = []; + + // update breadcrumbs if name or ID of the primary shape changes + eventBus.on('element.changed', function(e) { + var shape = e.element, + bo = getBusinessObject(shape); + + var isPresent = find(boParents, function(el) { + return el === bo; + }); + + if (!isPresent) { + return; + } + + updateBreadcrumbs(); + }); + + /** + * Updates the displayed breadcrumbs. If no element is provided, only the + * labels are updated. + * + * @param {djs.model.Base} [element] + */ + function updateBreadcrumbs(element) { + if (element) { + boParents = getBoParentChain(element); + } + + var path = boParents.map(function(parent) { + var title = escapeHTML(parent.name || parent.id); + var link = domify('
  • ' + title + '
  • '); + + var parentPlane = canvas.findRoot(getPlaneIdFromShape(parent)) || canvas.findRoot(parent.id); + + // when the root is a collaboration, the process does not have a corresponding + // element in the elementRegisty. Instead, we search for the corresponding participant + if (!parentPlane && is$1(parent, 'bpmn:Process')) { + var participant = elementRegistry.find(function(element) { + var bo = getBusinessObject(element); + return bo && bo.processRef && bo.processRef === parent; + }); + + parentPlane = canvas.findRoot(participant.id); + } + + link.addEventListener('click', function() { + canvas.setRootElement(parentPlane); + }); + + return link; + }); + + breadcrumbs.innerHTML = ''; + + // show breadcrumbs and expose state to .djs-container + var visible = path.length > 1; + containerClasses.toggle(OPEN_CLASS, visible); + + path.forEach(function(el) { + breadcrumbs.appendChild(el); + }); + } + + eventBus.on('root.set', function(event) { + updateBreadcrumbs(event.element); + }); + + } + + DrilldownBreadcrumbs.$inject = [ 'eventBus', 'elementRegistry', 'overlays', 'canvas' ]; + + + // helpers ////////// + + /** + * Returns the parents for the element using the business object chain, + * starting with the root element. + * + * @param {djs.model.Shape} child + * + * @returns {Array} parents + */ + function getBoParentChain(child) { + var bo = getBusinessObject(child); + + var parents = []; + + for (var element = bo; element; element = element.$parent) { + if (is$1(element, 'bpmn:SubProcess') || is$1(element, 'bpmn:Process')) { + parents.push(element); + } + } + + return parents.reverse(); + } + + /** + * Move collapsed subprocesses into view when drilling down. + * + * Zoom and scroll are saved in a session. + * + * @param {eventBus} eventBus + * @param {canvas} canvas + */ + function DrilldownCentering(eventBus, canvas) { + + var currentRoot = null; + var positionMap = new Map(); + + eventBus.on('root.set', function(event) { + var newRoot = event.element; + var currentViewbox = canvas.viewbox(); + var storedViewbox = positionMap.get(newRoot); + + positionMap.set(currentRoot, { + x: currentViewbox.x, + y: currentViewbox.y, + zoom: currentViewbox.scale + }); + + currentRoot = newRoot; + + // current root was replaced with a collaboration, we don't update the viewbox + if (is$1(newRoot, 'bpmn:Collaboration') && !storedViewbox) { + return; + } + + storedViewbox = storedViewbox || { x: 0, y: 0, zoom: 1 }; + + var dx = (currentViewbox.x - storedViewbox.x) * currentViewbox.scale, + dy = (currentViewbox.y - storedViewbox.y) * currentViewbox.scale; + + if (dx !== 0 || dy !== 0) { + canvas.scroll({ + dx: dx, + dy: dy + }); + } + + if (storedViewbox.zoom !== currentViewbox.scale) { + canvas.zoom(storedViewbox.zoom, { x: 0, y: 0 }); + } + }); + + eventBus.on('diagram.clear', function() { + positionMap.clear(); + currentRoot = null; + }); + + } + + DrilldownCentering.$inject = [ 'eventBus', 'canvas' ]; + + + /** + * ES5 Map implementation. Works. + */ + function Map() { + + this._entries = []; + + this.set = function(key, value) { + + var found = false; + + for (var k in this._entries) { + if (this._entries[k][0] === key) { + this._entries[k][1] = value; + + found = true; + + break; + } + } + + if (!found) { + this._entries.push([ key, value ]); + } + }; + + this.get = function(key) { + + for (var k in this._entries) { + if (this._entries[k][0] === key) { + return this._entries[k][1]; + } + } + + return null; + }; + + this.clear = function() { + this._entries.length = 0; + }; + + this.remove = function(key) { + + var idx = -1; + + for (var k in this._entries) { + if (this._entries[k][0] === key) { + idx = k; + + break; + } + } + + if (idx !== -1) { + this._entries.splice(idx, 1); + } + }; + } + + var DEFAULT_POSITION$1 = { + x: 180, + y: 160 + }; + + /** + * Hook into `import.render.start` and create new planes for diagrams with + * collapsed subprocesses and all dis on the same plane. + * + * @param {eventBus} eventBus + * @param {moddle} moddle + */ + function SubprocessCompatibility(eventBus, moddle) { + this._eventBus = eventBus; + this._moddle = moddle; + + var self = this; + + eventBus.on('import.render.start', 1500, function(e, context) { + self.handleImport(context.definitions); + }); + } + + SubprocessCompatibility.prototype.handleImport = function(definitions) { + if (!definitions.diagrams) { + return; + } + + var self = this; + this._definitions = definitions; + this._processToDiagramMap = {}; + + definitions.diagrams.forEach(function(diagram) { + if (!diagram.plane || !diagram.plane.bpmnElement) { + return; + } + + self._processToDiagramMap[diagram.plane.bpmnElement.id] = diagram; + }); + + var newDiagrams = []; + definitions.diagrams.forEach(function(diagram) { + var createdDiagrams = self.createNewDiagrams(diagram.plane); + Array.prototype.push.apply(newDiagrams, createdDiagrams); + }); + + newDiagrams.forEach(function(diagram) { + self.movePlaneElementsToOrigin(diagram.plane); + }); + }; + + + /** + * Moves all DI elements from collapsed subprocesses to a new plane. + * + * @param {Object} plane + * @return {Array} new diagrams created for the collapsed subprocesses + */ + SubprocessCompatibility.prototype.createNewDiagrams = function(plane) { + var self = this; + + var collapsedElements = []; + var elementsToMove = []; + + plane.get('planeElement').forEach(function(diElement) { + var bo = diElement.bpmnElement; + + if (!bo) { + return; + } + + var parent = bo.$parent; + + if (is$1(bo, 'bpmn:SubProcess') && !diElement.isExpanded) { + collapsedElements.push(bo); + } + + if (shouldMoveToPlane(bo, plane)) { + + // don't change the array while we iterate over it + elementsToMove.push({ diElement: diElement, parent: parent }); + } + }); + + var newDiagrams = []; + + // create new planes for all collapsed subprocesses, even when they are empty + collapsedElements.forEach(function(element) { + if (!self._processToDiagramMap[element.id]) { + var diagram = self.createDiagram(element); + self._processToDiagramMap[element.id] = diagram; + newDiagrams.push(diagram); + } + }); + + elementsToMove.forEach(function(element) { + var diElement = element.diElement; + var parent = element.parent; + + // parent is expanded, get nearest collapsed parent + while (parent && collapsedElements.indexOf(parent) === -1) { + parent = parent.$parent; + } + + // false positive, all parents are expanded + if (!parent) { + return; + } + + var diagram = self._processToDiagramMap[parent.id]; + self.moveToDiPlane(diElement, diagram.plane); + }); + + return newDiagrams; + }; + + SubprocessCompatibility.prototype.movePlaneElementsToOrigin = function(plane) { + var elements = plane.get('planeElement'); + + // get bounding box of all elements + var planeBounds = getPlaneBounds(plane); + + var offset = { + x: planeBounds.x - DEFAULT_POSITION$1.x, + y: planeBounds.y - DEFAULT_POSITION$1.y + }; + + elements.forEach(function(diElement) { + if (diElement.waypoint) { + diElement.waypoint.forEach(function(waypoint) { + waypoint.x = waypoint.x - offset.x; + waypoint.y = waypoint.y - offset.y; + }); + } else if (diElement.bounds) { + diElement.bounds.x = diElement.bounds.x - offset.x; + diElement.bounds.y = diElement.bounds.y - offset.y; + } + }); + }; + + + SubprocessCompatibility.prototype.moveToDiPlane = function(diElement, newPlane) { + var containingDiagram = findRootDiagram(diElement); + + // remove DI from old Plane and add it to the new one + var parentPlaneElement = containingDiagram.plane.get('planeElement'); + parentPlaneElement.splice(parentPlaneElement.indexOf(diElement), 1); + newPlane.get('planeElement').push(diElement); + }; + + + SubprocessCompatibility.prototype.createDiagram = function(bo) { + var plane = this._moddle.create('bpmndi:BPMNPlane', { bpmnElement: bo }); + var diagram = this._moddle.create('bpmndi:BPMNDiagram', { + plane: plane + }); + plane.$parent = diagram; + plane.bpmnElement = bo; + diagram.$parent = this._definitions; + this._definitions.diagrams.push(diagram); + return diagram; + }; + + SubprocessCompatibility.$inject = [ 'eventBus', 'moddle' ]; + + + // helpers ////////////////////////// + + function findRootDiagram(element) { + if (is$1(element, 'bpmndi:BPMNDiagram')) { + return element; + } else { + return findRootDiagram(element.$parent); + } + } + + function getPlaneBounds(plane) { + var planeTrbl = { + top: Infinity, + right: -Infinity, + bottom: -Infinity, + left: Infinity + }; + + plane.planeElement.forEach(function(element) { + if (!element.bounds) { + return; + } + + var trbl = asTRBL(element.bounds); + + planeTrbl.top = Math.min(trbl.top, planeTrbl.top); + planeTrbl.left = Math.min(trbl.left, planeTrbl.left); + }); + + return asBounds(planeTrbl); + } + + function shouldMoveToPlane(bo, plane) { + var parent = bo.$parent; + + // don't move elements that are already on the plane + if (!is$1(parent, 'bpmn:SubProcess') || parent === plane.bpmnElement) { + return false; + } + + // dataAssociations are children of the subprocess but rendered on process level + // cf. https://github.com/bpmn-io/bpmn-js/issues/1619 + if (isAny(bo, [ 'bpmn:DataInputAssociation', 'bpmn:DataOutputAssociation' ])) { + return false; + } + + return true; + } + + var LOW_PRIORITY$n = 250; + var ARROW_DOWN_SVG = ''; + + var EMPTY_MARKER = 'bjs-drilldown-empty'; + + function DrilldownOverlayBehavior( + canvas, eventBus, elementRegistry, overlays + ) { + CommandInterceptor.call(this, eventBus); + + this._canvas = canvas; + this._eventBus = eventBus; + this._elementRegistry = elementRegistry; + this._overlays = overlays; + + var self = this; + + this.executed('shape.toggleCollapse', LOW_PRIORITY$n, function(context) { + var shape = context.shape; + + // Add overlay to the collapsed shape + if (self.canDrillDown(shape)) { + self.addOverlay(shape); + } else { + self.removeOverlay(shape); + } + }, true); + + + this.reverted('shape.toggleCollapse', LOW_PRIORITY$n, function(context) { + var shape = context.shape; + + // Add overlay to the collapsed shape + if (self.canDrillDown(shape)) { + self.addOverlay(shape); + } else { + self.removeOverlay(shape); + } + }, true); + + + this.executed([ 'shape.create', 'shape.move', 'shape.delete' ], LOW_PRIORITY$n, + function(context) { + var oldParent = context.oldParent, + newParent = context.newParent || context.parent, + shape = context.shape; + + // Add overlay to the collapsed shape + if (self.canDrillDown(shape)) { + self.addOverlay(shape); + } + + self.updateDrilldownOverlay(oldParent); + self.updateDrilldownOverlay(newParent); + self.updateDrilldownOverlay(shape); + }, true); + + + this.reverted([ 'shape.create', 'shape.move', 'shape.delete' ], LOW_PRIORITY$n, + function(context) { + var oldParent = context.oldParent, + newParent = context.newParent || context.parent, + shape = context.shape; + + // Add overlay to the collapsed shape + if (self.canDrillDown(shape)) { + self.addOverlay(shape); + } + + self.updateDrilldownOverlay(oldParent); + self.updateDrilldownOverlay(newParent); + self.updateDrilldownOverlay(shape); + }, true); + + + eventBus.on('import.render.complete', function() { + elementRegistry.filter(function(e) { + return self.canDrillDown(e); + }).map(function(el) { + self.addOverlay(el); + }); + }); + + } + + e(DrilldownOverlayBehavior, CommandInterceptor); + + DrilldownOverlayBehavior.prototype.updateDrilldownOverlay = function(shape) { + var canvas = this._canvas; + + if (!shape) { + return; + } + + var root = canvas.findRoot(shape); + if (root) { + this.updateOverlayVisibility(root); + } + }; + + + DrilldownOverlayBehavior.prototype.canDrillDown = function(element) { + var canvas = this._canvas; + return is$1(element, 'bpmn:SubProcess') && canvas.findRoot(getPlaneIdFromShape(element)); + }; + + /** + * Updates visibility of the drilldown overlay. If the plane has no elements, + * the drilldown will be only shown when the element is selected. + * + * @param {djs.model.Shape|djs.model.Root} element collapsed shape or root element + */ + DrilldownOverlayBehavior.prototype.updateOverlayVisibility = function(element) { + var overlays = this._overlays; + + var bo = element.businessObject; + + var overlay = overlays.get({ element: bo.id, type: 'drilldown' })[0]; + + if (!overlay) { + return; + } + + var hasContent = bo && bo.flowElements && bo.flowElements.length; + classes$1(overlay.html).toggle(EMPTY_MARKER, !hasContent); + }; + + /** + * Attaches a drilldown button to the given element. We assume that the plane has + * the same id as the element. + * + * @param {djs.model.Shape} element collapsed shape + */ + DrilldownOverlayBehavior.prototype.addOverlay = function(element) { + var canvas = this._canvas; + var overlays = this._overlays; + + var existingOverlays = overlays.get({ element: element, type: 'drilldown' }); + if (existingOverlays.length) { + this.removeOverlay(element); + } + + var button = domify(''); + + button.addEventListener('click', function() { + canvas.setRootElement(canvas.findRoot(getPlaneIdFromShape(element))); + }); + + overlays.add(element, 'drilldown', { + position: { + bottom: -7, + right: -8 + }, + html: button + }); + + this.updateOverlayVisibility(element); + }; + + DrilldownOverlayBehavior.prototype.removeOverlay = function(element) { + var overlays = this._overlays; + + overlays.remove({ + element: element, + type: 'drilldown' + }); + }; + + DrilldownOverlayBehavior.$inject = [ + 'canvas', + 'eventBus', + 'elementRegistry', + 'overlays' + ]; + + var DrilldownModdule = { + __depends__: [ OverlaysModule, ChangeSupportModule, RootElementsModule ], + __init__: [ 'drilldownBreadcrumbs', 'drilldownOverlayBehavior', 'drilldownCentering', 'subprocessCompatibility' ], + drilldownBreadcrumbs: [ 'type', DrilldownBreadcrumbs ], + drilldownCentering: [ 'type', DrilldownCentering ], + drilldownOverlayBehavior: [ 'type', DrilldownOverlayBehavior ], + subprocessCompatibility: [ 'type', SubprocessCompatibility ] + }; + + /** + * A viewer for BPMN 2.0 diagrams. + * + * Have a look at {@link NavigatedViewer} or {@link Modeler} for bundles that include + * additional features. + * + * + * ## Extending the Viewer + * + * In order to extend the viewer pass extension modules to bootstrap via the + * `additionalModules` option. An extension module is an object that exposes + * named services. + * + * The following example depicts the integration of a simple + * logging component that integrates with interaction events: + * + * + * ```javascript + * + * // logging component + * function InteractionLogger(eventBus) { + * eventBus.on('element.hover', function(event) { + * console.log() + * }) + * } + * + * InteractionLogger.$inject = [ 'eventBus' ]; // minification save + * + * // extension module + * var extensionModule = { + * __init__: [ 'interactionLogger' ], + * interactionLogger: [ 'type', InteractionLogger ] + * }; + * + * // extend the viewer + * var bpmnViewer = new Viewer({ additionalModules: [ extensionModule ] }); + * bpmnViewer.importXML(...); + * ``` + * + * @param {Object} [options] configuration options to pass to the viewer + * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body. + * @param {string|number} [options.width] the width of the viewer + * @param {string|number} [options.height] the height of the viewer + * @param {Object} [options.moddleExtensions] extension packages to provide + * @param {Array} [options.modules] a list of modules to override the default modules + * @param {Array} [options.additionalModules] a list of modules to use with the default modules + */ + function Viewer(options) { + BaseViewer.call(this, options); + } + + e(Viewer, BaseViewer); + + // modules the viewer is composed of + Viewer.prototype._modules = [ + CoreModule, + translate, + SelectionModule, + OverlaysModule, + DrilldownModdule + ]; + + // default moddle extensions the viewer is composed of + Viewer.prototype._moddleExtensions = {}; + + var KEYCODE_C = 67; + var KEYCODE_V = 86; + var KEYCODE_Y = 89; + var KEYCODE_Z = 90; + + var KEYS_COPY = [ 'c', 'C', KEYCODE_C ]; + var KEYS_PASTE = [ 'v', 'V', KEYCODE_V ]; + var KEYS_REDO = [ 'y', 'Y', KEYCODE_Y ]; + var KEYS_UNDO = [ 'z', 'Z', KEYCODE_Z ]; + + /** + * Returns true if event was triggered with any modifier + * @param {KeyboardEvent} event + */ + function hasModifier(event) { + return (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey); + } + + /** + * @param {KeyboardEvent} event + */ + function isCmd(event) { + + // ensure we don't react to AltGr + // (mapped to CTRL + ALT) + if (event.altKey) { + return false; + } + + return event.ctrlKey || event.metaKey; + } + + /** + * Checks if key pressed is one of provided keys. + * + * @param {string|Array} keys + * @param {KeyboardEvent} event + */ + function isKey(keys, event) { + keys = isArray$3(keys) ? keys : [ keys ]; + + return keys.indexOf(event.key) !== -1 || keys.indexOf(event.keyCode) !== -1; + } + + /** + * @param {KeyboardEvent} event + */ + function isShift(event) { + return event.shiftKey; + } + + function isCopy(event) { + return isCmd(event) && isKey(KEYS_COPY, event); + } + + function isPaste(event) { + return isCmd(event) && isKey(KEYS_PASTE, event); + } + + function isUndo(event) { + return isCmd(event) && !isShift(event) && isKey(KEYS_UNDO, event); + } + + function isRedo(event) { + return isCmd(event) && ( + isKey(KEYS_REDO, event) || ( + isKey(KEYS_UNDO, event) && isShift(event) + ) + ); + } + + var KEYDOWN_EVENT = 'keyboard.keydown', + KEYUP_EVENT = 'keyboard.keyup'; + + var HANDLE_MODIFIER_ATTRIBUTE = 'input-handle-modified-keys'; + + var DEFAULT_PRIORITY$3 = 1000; + + /** + * A keyboard abstraction that may be activated and + * deactivated by users at will, consuming key events + * and triggering diagram actions. + * + * For keys pressed down, keyboard fires `keyboard.keydown` event. + * The event context contains one field which is `KeyboardEvent` event. + * + * The implementation fires the following key events that allow + * other components to hook into key handling: + * + * - keyboard.bind + * - keyboard.unbind + * - keyboard.init + * - keyboard.destroy + * + * All events contain one field which is node. + * + * A default binding for the keyboard may be specified via the + * `keyboard.bindTo` configuration option. + * + * @param {Config} config + * @param {EventBus} eventBus + */ + function Keyboard(config, eventBus) { + var self = this; + + this._config = config || {}; + this._eventBus = eventBus; + + this._keydownHandler = this._keydownHandler.bind(this); + this._keyupHandler = this._keyupHandler.bind(this); + + // properly clean dom registrations + eventBus.on('diagram.destroy', function() { + self._fire('destroy'); + + self.unbind(); + }); + + eventBus.on('diagram.init', function() { + self._fire('init'); + }); + + eventBus.on('attach', function() { + if (config && config.bindTo) { + self.bind(config.bindTo); + } + }); + + eventBus.on('detach', function() { + self.unbind(); + }); + } + + Keyboard.$inject = [ + 'config.keyboard', + 'eventBus' + ]; + + Keyboard.prototype._keydownHandler = function(event) { + this._keyHandler(event, KEYDOWN_EVENT); + }; + + Keyboard.prototype._keyupHandler = function(event) { + this._keyHandler(event, KEYUP_EVENT); + }; + + Keyboard.prototype._keyHandler = function(event, type) { + var eventBusResult; + + if (this._isEventIgnored(event)) { + return; + } + + var context = { + keyEvent: event + }; + + eventBusResult = this._eventBus.fire(type || KEYDOWN_EVENT, context); + + if (eventBusResult) { + event.preventDefault(); + } + }; + + Keyboard.prototype._isEventIgnored = function(event) { + return isInput(event.target) && this._isModifiedKeyIgnored(event); + }; + + Keyboard.prototype._isModifiedKeyIgnored = function(event) { + if (!isCmd(event)) { + return true; + } + + var allowedModifiers = this._getAllowedModifiers(event.target); + return allowedModifiers.indexOf(event.key) === -1; + }; + + Keyboard.prototype._getAllowedModifiers = function(element) { + var modifierContainer = closest(element, '[' + HANDLE_MODIFIER_ATTRIBUTE + ']', true); + + if (!modifierContainer || (this._node && !this._node.contains(modifierContainer))) { + return []; + } + + return modifierContainer.getAttribute(HANDLE_MODIFIER_ATTRIBUTE).split(','); + }; + + Keyboard.prototype.bind = function(node) { + + // make sure that the keyboard is only bound once to the DOM + this.unbind(); + + this._node = node; + + // bind key events + componentEvent.bind(node, 'keydown', this._keydownHandler, true); + componentEvent.bind(node, 'keyup', this._keyupHandler, true); + + this._fire('bind'); + }; + + Keyboard.prototype.getBinding = function() { + return this._node; + }; + + Keyboard.prototype.unbind = function() { + var node = this._node; + + if (node) { + this._fire('unbind'); + + // unbind key events + componentEvent.unbind(node, 'keydown', this._keydownHandler, true); + componentEvent.unbind(node, 'keyup', this._keyupHandler, true); + } + + this._node = null; + }; + + Keyboard.prototype._fire = function(event) { + this._eventBus.fire('keyboard.' + event, { node: this._node }); + }; + + /** + * Add a listener function that is notified with `KeyboardEvent` whenever + * the keyboard is bound and the user presses a key. If no priority is + * provided, the default value of 1000 is used. + * + * @param {number} [priority] + * @param {Function} listener + * @param {string} type + */ + Keyboard.prototype.addListener = function(priority, listener, type) { + if (isFunction(priority)) { + type = listener; + listener = priority; + priority = DEFAULT_PRIORITY$3; + } + + this._eventBus.on(type || KEYDOWN_EVENT, priority, listener); + }; + + Keyboard.prototype.removeListener = function(listener, type) { + this._eventBus.off(type || KEYDOWN_EVENT, listener); + }; + + Keyboard.prototype.hasModifier = hasModifier; + Keyboard.prototype.isCmd = isCmd; + Keyboard.prototype.isShift = isShift; + Keyboard.prototype.isKey = isKey; + + + + // helpers /////// + + function isInput(target) { + return target && (matchesSelector(target, 'input, textarea') || target.contentEditable === 'true'); + } + + var LOW_PRIORITY$m = 500; + + + /** + * Adds default keyboard bindings. + * + * This does not pull in any features will bind only actions that + * have previously been registered against the editorActions component. + * + * @param {EventBus} eventBus + * @param {Keyboard} keyboard + */ + function KeyboardBindings(eventBus, keyboard) { + + var self = this; + + eventBus.on('editorActions.init', LOW_PRIORITY$m, function(event) { + + var editorActions = event.editorActions; + + self.registerBindings(keyboard, editorActions); + }); + } + + KeyboardBindings.$inject = [ + 'eventBus', + 'keyboard' + ]; + + + /** + * Register available keyboard bindings. + * + * @param {Keyboard} keyboard + * @param {EditorActions} editorActions + */ + KeyboardBindings.prototype.registerBindings = function(keyboard, editorActions) { + + /** + * Add keyboard binding if respective editor action + * is registered. + * + * @param {string} action name + * @param {Function} fn that implements the key binding + */ + function addListener(action, fn) { + + if (editorActions.isRegistered(action)) { + keyboard.addListener(fn); + } + } + + + // undo + // (CTRL|CMD) + Z + addListener('undo', function(context) { + + var event = context.keyEvent; + + if (isUndo(event)) { + editorActions.trigger('undo'); + + return true; + } + }); + + // redo + // CTRL + Y + // CMD + SHIFT + Z + addListener('redo', function(context) { + + var event = context.keyEvent; + + if (isRedo(event)) { + editorActions.trigger('redo'); + + return true; + } + }); + + // copy + // CTRL/CMD + C + addListener('copy', function(context) { + + var event = context.keyEvent; + + if (isCopy(event)) { + editorActions.trigger('copy'); + + return true; + } + }); + + // paste + // CTRL/CMD + V + addListener('paste', function(context) { + + var event = context.keyEvent; + + if (isPaste(event)) { + editorActions.trigger('paste'); + + return true; + } + }); + + // zoom in one step + // CTRL/CMD + + + addListener('stepZoom', function(context) { + + var event = context.keyEvent; + + // quirk: it has to be triggered by `=` as well to work on international keyboard layout + // cf: https://github.com/bpmn-io/bpmn-js/issues/1362#issuecomment-722989754 + if (isKey([ '+', 'Add', '=' ], event) && isCmd(event)) { + editorActions.trigger('stepZoom', { value: 1 }); + + return true; + } + }); + + // zoom out one step + // CTRL + - + addListener('stepZoom', function(context) { + + var event = context.keyEvent; + + if (isKey([ '-', 'Subtract' ], event) && isCmd(event)) { + editorActions.trigger('stepZoom', { value: -1 }); + + return true; + } + }); + + // zoom to the default level + // CTRL + 0 + addListener('zoom', function(context) { + + var event = context.keyEvent; + + if (isKey('0', event) && isCmd(event)) { + editorActions.trigger('zoom', { value: 1 }); + + return true; + } + }); + + // delete selected element + // DEL + addListener('removeSelection', function(context) { + + var event = context.keyEvent; + + if (isKey([ 'Backspace', 'Delete', 'Del' ], event)) { + editorActions.trigger('removeSelection'); + + return true; + } + }); + }; + + var KeyboardModule$1 = { + __init__: [ 'keyboard', 'keyboardBindings' ], + keyboard: [ 'type', Keyboard ], + keyboardBindings: [ 'type', KeyboardBindings ] + }; + + var DEFAULT_CONFIG$1 = { + moveSpeed: 50, + moveSpeedAccelerated: 200 + }; + + + /** + * A feature that allows users to move the canvas using the keyboard. + * + * @param {Object} config + * @param {number} [config.moveSpeed=50] + * @param {number} [config.moveSpeedAccelerated=200] + * @param {Keyboard} keyboard + * @param {Canvas} canvas + */ + function KeyboardMove( + config, + keyboard, + canvas + ) { + + var self = this; + + this._config = assign({}, DEFAULT_CONFIG$1, config || {}); + + keyboard.addListener(arrowsListener); + + + function arrowsListener(context) { + + var event = context.keyEvent, + config = self._config; + + if (!keyboard.isCmd(event)) { + return; + } + + if (keyboard.isKey([ + 'ArrowLeft', 'Left', + 'ArrowUp', 'Up', + 'ArrowDown', 'Down', + 'ArrowRight', 'Right' + ], event)) { + + var speed = ( + keyboard.isShift(event) ? + config.moveSpeedAccelerated : + config.moveSpeed + ); + + var direction; + + switch (event.key) { + case 'ArrowLeft': + case 'Left': + direction = 'left'; + break; + case 'ArrowUp': + case 'Up': + direction = 'up'; + break; + case 'ArrowRight': + case 'Right': + direction = 'right'; + break; + case 'ArrowDown': + case 'Down': + direction = 'down'; + break; + } + + self.moveCanvas({ + speed: speed, + direction: direction + }); + + return true; + } + } + + this.moveCanvas = function(opts) { + + var dx = 0, + dy = 0, + speed = opts.speed; + + var actualSpeed = speed / Math.min(Math.sqrt(canvas.viewbox().scale), 1); + + switch (opts.direction) { + case 'left': // Left + dx = actualSpeed; + break; + case 'up': // Up + dy = actualSpeed; + break; + case 'right': // Right + dx = -actualSpeed; + break; + case 'down': // Down + dy = -actualSpeed; + break; + } + + canvas.scroll({ + dx: dx, + dy: dy + }); + }; + + } + + + KeyboardMove.$inject = [ + 'config.keyboardMove', + 'keyboard', + 'canvas' + ]; + + var KeyboardMoveModule = { + __depends__: [ + KeyboardModule$1 + ], + __init__: [ 'keyboardMove' ], + keyboardMove: [ 'type', KeyboardMove ] + }; + + var CURSOR_CLS_PATTERN = /^djs-cursor-.*$/; + + + function set(mode) { + var classes = classes$1(document.body); + + classes.removeMatching(CURSOR_CLS_PATTERN); + + if (mode) { + classes.add('djs-cursor-' + mode); + } + } + + function unset() { + set(null); + } + + var TRAP_PRIORITY = 5000; + + /** + * Installs a click trap that prevents a ghost click following a dragging operation. + * + * @return {Function} a function to immediately remove the installed trap. + */ + function install(eventBus, eventName) { + + eventName = eventName || 'element.click'; + + function trap() { + return false; + } + + eventBus.once(eventName, TRAP_PRIORITY, trap); + + return function() { + eventBus.off(eventName, trap); + }; + } + + function center(bounds) { + return { + x: bounds.x + (bounds.width / 2), + y: bounds.y + (bounds.height / 2) + }; + } + + + function delta(a, b) { + return { + x: a.x - b.x, + y: a.y - b.y + }; + } + + var THRESHOLD$1 = 15; + + + /** + * Move the canvas via mouse. + * + * @param {EventBus} eventBus + * @param {Canvas} canvas + */ + function MoveCanvas(eventBus, canvas) { + + var context; + + + // listen for move on element mouse down; + // allow others to hook into the event before us though + // (dragging / element moving will do this) + eventBus.on('element.mousedown', 500, function(e) { + return handleStart(e.originalEvent); + }); + + + function handleMove(event) { + + var start = context.start, + button = context.button, + position = toPoint(event), + delta$1 = delta(position, start); + + if (!context.dragging && length(delta$1) > THRESHOLD$1) { + context.dragging = true; + + if (button === 0) { + install(eventBus); + } + + set('grab'); + } + + if (context.dragging) { + + var lastPosition = context.last || context.start; + + delta$1 = delta(position, lastPosition); + + canvas.scroll({ + dx: delta$1.x, + dy: delta$1.y + }); + + context.last = position; + } + + // prevent select + event.preventDefault(); + } + + + function handleEnd(event) { + componentEvent.unbind(document, 'mousemove', handleMove); + componentEvent.unbind(document, 'mouseup', handleEnd); + + context = null; + + unset(); + } + + function handleStart(event) { + + // event is already handled by '.djs-draggable' + if (closest(event.target, '.djs-draggable')) { + return; + } + + var button = event.button; + + // reject right mouse button or modifier key + if (button >= 2 || event.ctrlKey || event.shiftKey || event.altKey) { + return; + } + + context = { + button: button, + start: toPoint(event) + }; + + componentEvent.bind(document, 'mousemove', handleMove); + componentEvent.bind(document, 'mouseup', handleEnd); + + // we've handled the event + return true; + } + + this.isActive = function() { + return !!context; + }; + + } + + + MoveCanvas.$inject = [ + 'eventBus', + 'canvas' + ]; + + + + // helpers /////// + + function length(point) { + return Math.sqrt(Math.pow(point.x, 2) + Math.pow(point.y, 2)); + } + + var MoveCanvasModule = { + __init__: [ 'moveCanvas' ], + moveCanvas: [ 'type', MoveCanvas ] + }; + + /** + * Get the logarithm of x with base 10 + * @param {Integer} value + */ + function log10(x) { + return Math.log(x) / Math.log(10); + } + + /** + * Get step size for given range and number of steps. + * + * @param {Object} range + * @param {number} range.min + * @param {number} range.max + */ + function getStepSize(range, steps) { + + var minLinearRange = log10(range.min), + maxLinearRange = log10(range.max); + + var absoluteLinearRange = Math.abs(minLinearRange) + Math.abs(maxLinearRange); + + return absoluteLinearRange / steps; + } + + function cap(range, scale) { + return Math.max(range.min, Math.min(range.max, scale)); + } + + var sign = Math.sign || function(n) { + return n >= 0 ? 1 : -1; + }; + + var RANGE = { min: 0.2, max: 4 }, + NUM_STEPS = 10; + + var DELTA_THRESHOLD = 0.1; + + var DEFAULT_SCALE = 0.75; + + /** + * An implementation of zooming and scrolling within the + * {@link Canvas} via the mouse wheel. + * + * Mouse wheel zooming / scrolling may be disabled using + * the {@link toggle(enabled)} method. + * + * @param {Object} [config] + * @param {boolean} [config.enabled=true] default enabled state + * @param {number} [config.scale=.75] scroll sensivity + * @param {EventBus} eventBus + * @param {Canvas} canvas + */ + function ZoomScroll(config, eventBus, canvas) { + + config = config || {}; + + this._enabled = false; + + this._canvas = canvas; + this._container = canvas._container; + + this._handleWheel = bind(this._handleWheel, this); + + this._totalDelta = 0; + this._scale = config.scale || DEFAULT_SCALE; + + var self = this; + + eventBus.on('canvas.init', function(e) { + self._init(config.enabled !== false); + }); + } + + ZoomScroll.$inject = [ + 'config.zoomScroll', + 'eventBus', + 'canvas' + ]; + + ZoomScroll.prototype.scroll = function scroll(delta) { + this._canvas.scroll(delta); + }; + + + ZoomScroll.prototype.reset = function reset() { + this._canvas.zoom('fit-viewport'); + }; + + /** + * Zoom depending on delta. + * + * @param {number} delta + * @param {Object} position + */ + ZoomScroll.prototype.zoom = function zoom(delta, position) { + + // zoom with half the step size of stepZoom + var stepSize = getStepSize(RANGE, NUM_STEPS * 2); + + // add until threshold reached + this._totalDelta += delta; + + if (Math.abs(this._totalDelta) > DELTA_THRESHOLD) { + this._zoom(delta, position, stepSize); + + // reset + this._totalDelta = 0; + } + }; + + + ZoomScroll.prototype._handleWheel = function handleWheel(event) { + + // event is already handled by '.djs-scrollable' + if (closest(event.target, '.djs-scrollable', true)) { + return; + } + + var element = this._container; + + event.preventDefault(); + + // pinch to zoom is mapped to wheel + ctrlKey = true + // in modern browsers (!) + + var isZoom = event.ctrlKey; + + var isHorizontalScroll = event.shiftKey; + + var factor = -1 * this._scale, + delta; + + if (isZoom) { + factor *= event.deltaMode === 0 ? 0.020 : 0.32; + } else { + factor *= event.deltaMode === 0 ? 1.0 : 16.0; + } + + if (isZoom) { + var elementRect = element.getBoundingClientRect(); + + var offset = { + x: event.clientX - elementRect.left, + y: event.clientY - elementRect.top + }; + + delta = ( + Math.sqrt( + Math.pow(event.deltaY, 2) + + Math.pow(event.deltaX, 2) + ) * sign(event.deltaY) * factor + ); + + // zoom in relative to diagram {x,y} coordinates + this.zoom(delta, offset); + } else { + + if (isHorizontalScroll) { + delta = { + dx: factor * event.deltaY, + dy: 0 + }; + } else { + delta = { + dx: factor * event.deltaX, + dy: factor * event.deltaY + }; + } + + this.scroll(delta); + } + }; + + /** + * Zoom with fixed step size. + * + * @param {number} delta - Zoom delta (1 for zooming in, -1 for out). + * @param {Object} position + */ + ZoomScroll.prototype.stepZoom = function stepZoom(delta, position) { + + var stepSize = getStepSize(RANGE, NUM_STEPS); + + this._zoom(delta, position, stepSize); + }; + + + /** + * Zoom in/out given a step size. + * + * @param {number} delta + * @param {Object} position + * @param {number} stepSize + */ + ZoomScroll.prototype._zoom = function(delta, position, stepSize) { + var canvas = this._canvas; + + var direction = delta > 0 ? 1 : -1; + + var currentLinearZoomLevel = log10(canvas.zoom()); + + // snap to a proximate zoom step + var newLinearZoomLevel = Math.round(currentLinearZoomLevel / stepSize) * stepSize; + + // increase or decrease one zoom step in the given direction + newLinearZoomLevel += stepSize * direction; + + // calculate the absolute logarithmic zoom level based on the linear zoom level + // (e.g. 2 for an absolute x2 zoom) + var newLogZoomLevel = Math.pow(10, newLinearZoomLevel); + + canvas.zoom(cap(RANGE, newLogZoomLevel), position); + }; + + + /** + * Toggle the zoom scroll ability via mouse wheel. + * + * @param {boolean} [newEnabled] new enabled state + */ + ZoomScroll.prototype.toggle = function toggle(newEnabled) { + + var element = this._container; + var handleWheel = this._handleWheel; + + var oldEnabled = this._enabled; + + if (typeof newEnabled === 'undefined') { + newEnabled = !oldEnabled; + } + + // only react on actual changes + if (oldEnabled !== newEnabled) { + + // add or remove wheel listener based on + // changed enabled state + componentEvent[newEnabled ? 'bind' : 'unbind'](element, 'wheel', handleWheel, false); + } + + this._enabled = newEnabled; + + return newEnabled; + }; + + + ZoomScroll.prototype._init = function(newEnabled) { + this.toggle(newEnabled); + }; + + var ZoomScrollModule = { + __init__: [ 'zoomScroll' ], + zoomScroll: [ 'type', ZoomScroll ] + }; + + /** + * A viewer that includes mouse navigation facilities + * + * @param {Object} options + */ + function NavigatedViewer(options) { + Viewer.call(this, options); + } + + e(NavigatedViewer, Viewer); + + + NavigatedViewer.prototype._navigationModules = [ + KeyboardMoveModule, + MoveCanvasModule, + ZoomScrollModule + ]; + + NavigatedViewer.prototype._modules = [].concat( + Viewer.prototype._modules, + NavigatedViewer.prototype._navigationModules + ); + + var hammer = {exports: {}}; + + /*! Hammer.JS - v2.0.7 - 2016-04-22 + * http://hammerjs.github.io/ + * + * Copyright (c) 2016 Jorik Tangelder; + * Licensed under the MIT license */ + + (function (module) { + (function(window, document, exportName, undefined$1) { + + var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o']; + var TEST_ELEMENT = document.createElement('div'); + + var TYPE_FUNCTION = 'function'; + + var round = Math.round; + var abs = Math.abs; + var now = Date.now; + + /** + * set a timeout with a given scope + * @param {Function} fn + * @param {Number} timeout + * @param {Object} context + * @returns {number} + */ + function setTimeoutContext(fn, timeout, context) { + return setTimeout(bindFn(fn, context), timeout); + } + + /** + * if the argument is an array, we want to execute the fn on each entry + * if it aint an array we don't want to do a thing. + * this is used by all the methods that accept a single and array argument. + * @param {*|Array} arg + * @param {String} fn + * @param {Object} [context] + * @returns {Boolean} + */ + function invokeArrayArg(arg, fn, context) { + if (Array.isArray(arg)) { + each(arg, context[fn], context); + return true; + } + return false; + } + + /** + * walk objects and arrays + * @param {Object} obj + * @param {Function} iterator + * @param {Object} context + */ + function each(obj, iterator, context) { + var i; + + if (!obj) { + return; + } + + if (obj.forEach) { + obj.forEach(iterator, context); + } else if (obj.length !== undefined$1) { + i = 0; + while (i < obj.length) { + iterator.call(context, obj[i], i, obj); + i++; + } + } else { + for (i in obj) { + obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj); + } + } + } + + /** + * wrap a method with a deprecation warning and stack trace + * @param {Function} method + * @param {String} name + * @param {String} message + * @returns {Function} A new function wrapping the supplied method. + */ + function deprecate(method, name, message) { + var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n'; + return function() { + var e = new Error('get-stack-trace'); + var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '') + .replace(/^\s+at\s+/gm, '') + .replace(/^Object.\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace'; + + var log = window.console && (window.console.warn || window.console.log); + if (log) { + log.call(window.console, deprecationMessage, stack); + } + return method.apply(this, arguments); + }; + } + + /** + * extend object. + * means that properties in dest will be overwritten by the ones in src. + * @param {Object} target + * @param {...Object} objects_to_assign + * @returns {Object} target + */ + var assign; + if (typeof Object.assign !== 'function') { + assign = function assign(target) { + if (target === undefined$1 || target === null) { + throw new TypeError('Cannot convert undefined or null to object'); + } + + var output = Object(target); + for (var index = 1; index < arguments.length; index++) { + var source = arguments[index]; + if (source !== undefined$1 && source !== null) { + for (var nextKey in source) { + if (source.hasOwnProperty(nextKey)) { + output[nextKey] = source[nextKey]; + } + } + } + } + return output; + }; + } else { + assign = Object.assign; + } + + /** + * extend object. + * means that properties in dest will be overwritten by the ones in src. + * @param {Object} dest + * @param {Object} src + * @param {Boolean} [merge=false] + * @returns {Object} dest + */ + var extend = deprecate(function extend(dest, src, merge) { + var keys = Object.keys(src); + var i = 0; + while (i < keys.length) { + if (!merge || (merge && dest[keys[i]] === undefined$1)) { + dest[keys[i]] = src[keys[i]]; + } + i++; + } + return dest; + }, 'extend', 'Use `assign`.'); + + /** + * merge the values from src in the dest. + * means that properties that exist in dest will not be overwritten by src + * @param {Object} dest + * @param {Object} src + * @returns {Object} dest + */ + var merge = deprecate(function merge(dest, src) { + return extend(dest, src, true); + }, 'merge', 'Use `assign`.'); + + /** + * simple class inheritance + * @param {Function} child + * @param {Function} base + * @param {Object} [properties] + */ + function inherit(child, base, properties) { + var baseP = base.prototype, + childP; + + childP = child.prototype = Object.create(baseP); + childP.constructor = child; + childP._super = baseP; + + if (properties) { + assign(childP, properties); + } + } + + /** + * simple function bind + * @param {Function} fn + * @param {Object} context + * @returns {Function} + */ + function bindFn(fn, context) { + return function boundFn() { + return fn.apply(context, arguments); + }; + } + + /** + * let a boolean value also be a function that must return a boolean + * this first item in args will be used as the context + * @param {Boolean|Function} val + * @param {Array} [args] + * @returns {Boolean} + */ + function boolOrFn(val, args) { + if (typeof val == TYPE_FUNCTION) { + return val.apply(args ? args[0] || undefined$1 : undefined$1, args); + } + return val; + } + + /** + * use the val2 when val1 is undefined + * @param {*} val1 + * @param {*} val2 + * @returns {*} + */ + function ifUndefined(val1, val2) { + return (val1 === undefined$1) ? val2 : val1; + } + + /** + * addEventListener with multiple events at once + * @param {EventTarget} target + * @param {String} types + * @param {Function} handler + */ + function addEventListeners(target, types, handler) { + each(splitStr(types), function(type) { + target.addEventListener(type, handler, false); + }); + } + + /** + * removeEventListener with multiple events at once + * @param {EventTarget} target + * @param {String} types + * @param {Function} handler + */ + function removeEventListeners(target, types, handler) { + each(splitStr(types), function(type) { + target.removeEventListener(type, handler, false); + }); + } + + /** + * find if a node is in the given parent + * @method hasParent + * @param {HTMLElement} node + * @param {HTMLElement} parent + * @return {Boolean} found + */ + function hasParent(node, parent) { + while (node) { + if (node == parent) { + return true; + } + node = node.parentNode; + } + return false; + } + + /** + * small indexOf wrapper + * @param {String} str + * @param {String} find + * @returns {Boolean} found + */ + function inStr(str, find) { + return str.indexOf(find) > -1; + } + + /** + * split string on whitespace + * @param {String} str + * @returns {Array} words + */ + function splitStr(str) { + return str.trim().split(/\s+/g); + } + + /** + * find if a array contains the object using indexOf or a simple polyFill + * @param {Array} src + * @param {String} find + * @param {String} [findByKey] + * @return {Boolean|Number} false when not found, or the index + */ + function inArray(src, find, findByKey) { + if (src.indexOf && !findByKey) { + return src.indexOf(find); + } else { + var i = 0; + while (i < src.length) { + if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) { + return i; + } + i++; + } + return -1; + } + } + + /** + * convert array-like objects to real arrays + * @param {Object} obj + * @returns {Array} + */ + function toArray(obj) { + return Array.prototype.slice.call(obj, 0); + } + + /** + * unique array with objects based on a key (like 'id') or just by the array's value + * @param {Array} src [{id:1},{id:2},{id:1}] + * @param {String} [key] + * @param {Boolean} [sort=False] + * @returns {Array} [{id:1},{id:2}] + */ + function uniqueArray(src, key, sort) { + var results = []; + var values = []; + var i = 0; + + while (i < src.length) { + var val = key ? src[i][key] : src[i]; + if (inArray(values, val) < 0) { + results.push(src[i]); + } + values[i] = val; + i++; + } + + if (sort) { + if (!key) { + results = results.sort(); + } else { + results = results.sort(function sortUniqueArray(a, b) { + return a[key] > b[key]; + }); + } + } + + return results; + } + + /** + * get the prefixed property + * @param {Object} obj + * @param {String} property + * @returns {String|Undefined} prefixed + */ + function prefixed(obj, property) { + var prefix, prop; + var camelProp = property[0].toUpperCase() + property.slice(1); + + var i = 0; + while (i < VENDOR_PREFIXES.length) { + prefix = VENDOR_PREFIXES[i]; + prop = (prefix) ? prefix + camelProp : property; + + if (prop in obj) { + return prop; + } + i++; + } + return undefined$1; + } + + /** + * get a unique id + * @returns {number} uniqueId + */ + var _uniqueId = 1; + function uniqueId() { + return _uniqueId++; + } + + /** + * get the window object of an element + * @param {HTMLElement} element + * @returns {DocumentView|Window} + */ + function getWindowForElement(element) { + var doc = element.ownerDocument || element; + return (doc.defaultView || doc.parentWindow || window); + } + + var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i; + + var SUPPORT_TOUCH = ('ontouchstart' in window); + var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined$1; + var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent); + + var INPUT_TYPE_TOUCH = 'touch'; + var INPUT_TYPE_PEN = 'pen'; + var INPUT_TYPE_MOUSE = 'mouse'; + var INPUT_TYPE_KINECT = 'kinect'; + + var COMPUTE_INTERVAL = 25; + + var INPUT_START = 1; + var INPUT_MOVE = 2; + var INPUT_END = 4; + var INPUT_CANCEL = 8; + + var DIRECTION_NONE = 1; + var DIRECTION_LEFT = 2; + var DIRECTION_RIGHT = 4; + var DIRECTION_UP = 8; + var DIRECTION_DOWN = 16; + + var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT; + var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN; + var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL; + + var PROPS_XY = ['x', 'y']; + var PROPS_CLIENT_XY = ['clientX', 'clientY']; + + /** + * create new input type manager + * @param {Manager} manager + * @param {Function} callback + * @returns {Input} + * @constructor + */ + function Input(manager, callback) { + var self = this; + this.manager = manager; + this.callback = callback; + this.element = manager.element; + this.target = manager.options.inputTarget; + + // smaller wrapper around the handler, for the scope and the enabled state of the manager, + // so when disabled the input events are completely bypassed. + this.domHandler = function(ev) { + if (boolOrFn(manager.options.enable, [manager])) { + self.handler(ev); + } + }; + + this.init(); + + } + + Input.prototype = { + /** + * should handle the inputEvent data and trigger the callback + * @virtual + */ + handler: function() { }, + + /** + * bind the events + */ + init: function() { + this.evEl && addEventListeners(this.element, this.evEl, this.domHandler); + this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler); + this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); + }, + + /** + * unbind the events + */ + destroy: function() { + this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler); + this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler); + this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); + } + }; + + /** + * create new input type manager + * called by the Manager constructor + * @param {Hammer} manager + * @returns {Input} + */ + function createInputInstance(manager) { + var Type; + var inputClass = manager.options.inputClass; + + if (inputClass) { + Type = inputClass; + } else if (SUPPORT_POINTER_EVENTS) { + Type = PointerEventInput; + } else if (SUPPORT_ONLY_TOUCH) { + Type = TouchInput; + } else if (!SUPPORT_TOUCH) { + Type = MouseInput; + } else { + Type = TouchMouseInput; + } + return new (Type)(manager, inputHandler); + } + + /** + * handle input events + * @param {Manager} manager + * @param {String} eventType + * @param {Object} input + */ + function inputHandler(manager, eventType, input) { + var pointersLen = input.pointers.length; + var changedPointersLen = input.changedPointers.length; + var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0)); + var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0)); + + input.isFirst = !!isFirst; + input.isFinal = !!isFinal; + + if (isFirst) { + manager.session = {}; + } + + // source event is the normalized value of the domEvents + // like 'touchstart, mouseup, pointerdown' + input.eventType = eventType; + + // compute scale, rotation etc + computeInputData(manager, input); + + // emit secret event + manager.emit('hammer.input', input); + + manager.recognize(input); + manager.session.prevInput = input; + } + + /** + * extend the data with some usable properties like scale, rotate, velocity etc + * @param {Object} manager + * @param {Object} input + */ + function computeInputData(manager, input) { + var session = manager.session; + var pointers = input.pointers; + var pointersLength = pointers.length; + + // store the first input to calculate the distance and direction + if (!session.firstInput) { + session.firstInput = simpleCloneInputData(input); + } + + // to compute scale and rotation we need to store the multiple touches + if (pointersLength > 1 && !session.firstMultiple) { + session.firstMultiple = simpleCloneInputData(input); + } else if (pointersLength === 1) { + session.firstMultiple = false; + } + + var firstInput = session.firstInput; + var firstMultiple = session.firstMultiple; + var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center; + + var center = input.center = getCenter(pointers); + input.timeStamp = now(); + input.deltaTime = input.timeStamp - firstInput.timeStamp; + + input.angle = getAngle(offsetCenter, center); + input.distance = getDistance(offsetCenter, center); + + computeDeltaXY(session, input); + input.offsetDirection = getDirection(input.deltaX, input.deltaY); + + var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY); + input.overallVelocityX = overallVelocity.x; + input.overallVelocityY = overallVelocity.y; + input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y; + + input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1; + input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0; + + input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length > + session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers); + + computeIntervalInputData(session, input); + + // find the correct target + var target = manager.element; + if (hasParent(input.srcEvent.target, target)) { + target = input.srcEvent.target; + } + input.target = target; + } + + function computeDeltaXY(session, input) { + var center = input.center; + var offset = session.offsetDelta || {}; + var prevDelta = session.prevDelta || {}; + var prevInput = session.prevInput || {}; + + if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) { + prevDelta = session.prevDelta = { + x: prevInput.deltaX || 0, + y: prevInput.deltaY || 0 + }; + + offset = session.offsetDelta = { + x: center.x, + y: center.y + }; + } + + input.deltaX = prevDelta.x + (center.x - offset.x); + input.deltaY = prevDelta.y + (center.y - offset.y); + } + + /** + * velocity is calculated every x ms + * @param {Object} session + * @param {Object} input + */ + function computeIntervalInputData(session, input) { + var last = session.lastInterval || input, + deltaTime = input.timeStamp - last.timeStamp, + velocity, velocityX, velocityY, direction; + + if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined$1)) { + var deltaX = input.deltaX - last.deltaX; + var deltaY = input.deltaY - last.deltaY; + + var v = getVelocity(deltaTime, deltaX, deltaY); + velocityX = v.x; + velocityY = v.y; + velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y; + direction = getDirection(deltaX, deltaY); + + session.lastInterval = input; + } else { + // use latest velocity info if it doesn't overtake a minimum period + velocity = last.velocity; + velocityX = last.velocityX; + velocityY = last.velocityY; + direction = last.direction; + } + + input.velocity = velocity; + input.velocityX = velocityX; + input.velocityY = velocityY; + input.direction = direction; + } + + /** + * create a simple clone from the input used for storage of firstInput and firstMultiple + * @param {Object} input + * @returns {Object} clonedInputData + */ + function simpleCloneInputData(input) { + // make a simple copy of the pointers because we will get a reference if we don't + // we only need clientXY for the calculations + var pointers = []; + var i = 0; + while (i < input.pointers.length) { + pointers[i] = { + clientX: round(input.pointers[i].clientX), + clientY: round(input.pointers[i].clientY) + }; + i++; + } + + return { + timeStamp: now(), + pointers: pointers, + center: getCenter(pointers), + deltaX: input.deltaX, + deltaY: input.deltaY + }; + } + + /** + * get the center of all the pointers + * @param {Array} pointers + * @return {Object} center contains `x` and `y` properties + */ + function getCenter(pointers) { + var pointersLength = pointers.length; + + // no need to loop when only one touch + if (pointersLength === 1) { + return { + x: round(pointers[0].clientX), + y: round(pointers[0].clientY) + }; + } + + var x = 0, y = 0, i = 0; + while (i < pointersLength) { + x += pointers[i].clientX; + y += pointers[i].clientY; + i++; + } + + return { + x: round(x / pointersLength), + y: round(y / pointersLength) + }; + } + + /** + * calculate the velocity between two points. unit is in px per ms. + * @param {Number} deltaTime + * @param {Number} x + * @param {Number} y + * @return {Object} velocity `x` and `y` + */ + function getVelocity(deltaTime, x, y) { + return { + x: x / deltaTime || 0, + y: y / deltaTime || 0 + }; + } + + /** + * get the direction between two points + * @param {Number} x + * @param {Number} y + * @return {Number} direction + */ + function getDirection(x, y) { + if (x === y) { + return DIRECTION_NONE; + } + + if (abs(x) >= abs(y)) { + return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; + } + return y < 0 ? DIRECTION_UP : DIRECTION_DOWN; + } + + /** + * calculate the absolute distance between two points + * @param {Object} p1 {x, y} + * @param {Object} p2 {x, y} + * @param {Array} [props] containing x and y keys + * @return {Number} distance + */ + function getDistance(p1, p2, props) { + if (!props) { + props = PROPS_XY; + } + var x = p2[props[0]] - p1[props[0]], + y = p2[props[1]] - p1[props[1]]; + + return Math.sqrt((x * x) + (y * y)); + } + + /** + * calculate the angle between two coordinates + * @param {Object} p1 + * @param {Object} p2 + * @param {Array} [props] containing x and y keys + * @return {Number} angle + */ + function getAngle(p1, p2, props) { + if (!props) { + props = PROPS_XY; + } + var x = p2[props[0]] - p1[props[0]], + y = p2[props[1]] - p1[props[1]]; + return Math.atan2(y, x) * 180 / Math.PI; + } + + /** + * calculate the rotation degrees between two pointersets + * @param {Array} start array of pointers + * @param {Array} end array of pointers + * @return {Number} rotation + */ + function getRotation(start, end) { + return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY); + } + + /** + * calculate the scale factor between two pointersets + * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out + * @param {Array} start array of pointers + * @param {Array} end array of pointers + * @return {Number} scale + */ + function getScale(start, end) { + return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY); + } + + var MOUSE_INPUT_MAP = { + mousedown: INPUT_START, + mousemove: INPUT_MOVE, + mouseup: INPUT_END + }; + + var MOUSE_ELEMENT_EVENTS = 'mousedown'; + var MOUSE_WINDOW_EVENTS = 'mousemove mouseup'; + + /** + * Mouse events input + * @constructor + * @extends Input + */ + function MouseInput() { + this.evEl = MOUSE_ELEMENT_EVENTS; + this.evWin = MOUSE_WINDOW_EVENTS; + + this.pressed = false; // mousedown state + + Input.apply(this, arguments); + } + + inherit(MouseInput, Input, { + /** + * handle mouse events + * @param {Object} ev + */ + handler: function MEhandler(ev) { + var eventType = MOUSE_INPUT_MAP[ev.type]; + + // on start we want to have the left mouse button down + if (eventType & INPUT_START && ev.button === 0) { + this.pressed = true; + } + + if (eventType & INPUT_MOVE && ev.which !== 1) { + eventType = INPUT_END; + } + + // mouse must be down + if (!this.pressed) { + return; + } + + if (eventType & INPUT_END) { + this.pressed = false; + } + + this.callback(this.manager, eventType, { + pointers: [ev], + changedPointers: [ev], + pointerType: INPUT_TYPE_MOUSE, + srcEvent: ev + }); + } + }); + + var POINTER_INPUT_MAP = { + pointerdown: INPUT_START, + pointermove: INPUT_MOVE, + pointerup: INPUT_END, + pointercancel: INPUT_CANCEL, + pointerout: INPUT_CANCEL + }; + + // in IE10 the pointer types is defined as an enum + var IE10_POINTER_TYPE_ENUM = { + 2: INPUT_TYPE_TOUCH, + 3: INPUT_TYPE_PEN, + 4: INPUT_TYPE_MOUSE, + 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816 + }; + + var POINTER_ELEMENT_EVENTS = 'pointerdown'; + var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel'; + + // IE10 has prefixed support, and case-sensitive + if (window.MSPointerEvent && !window.PointerEvent) { + POINTER_ELEMENT_EVENTS = 'MSPointerDown'; + POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel'; + } + + /** + * Pointer events input + * @constructor + * @extends Input + */ + function PointerEventInput() { + this.evEl = POINTER_ELEMENT_EVENTS; + this.evWin = POINTER_WINDOW_EVENTS; + + Input.apply(this, arguments); + + this.store = (this.manager.session.pointerEvents = []); + } + + inherit(PointerEventInput, Input, { + /** + * handle mouse events + * @param {Object} ev + */ + handler: function PEhandler(ev) { + var store = this.store; + var removePointer = false; + + var eventTypeNormalized = ev.type.toLowerCase().replace('ms', ''); + var eventType = POINTER_INPUT_MAP[eventTypeNormalized]; + var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType; + + var isTouch = (pointerType == INPUT_TYPE_TOUCH); + + // get index of the event in the store + var storeIndex = inArray(store, ev.pointerId, 'pointerId'); + + // start and mouse must be down + if (eventType & INPUT_START && (ev.button === 0 || isTouch)) { + if (storeIndex < 0) { + store.push(ev); + storeIndex = store.length - 1; + } + } else if (eventType & (INPUT_END | INPUT_CANCEL)) { + removePointer = true; + } + + // it not found, so the pointer hasn't been down (so it's probably a hover) + if (storeIndex < 0) { + return; + } + + // update the event in the store + store[storeIndex] = ev; + + this.callback(this.manager, eventType, { + pointers: store, + changedPointers: [ev], + pointerType: pointerType, + srcEvent: ev + }); + + if (removePointer) { + // remove from the store + store.splice(storeIndex, 1); + } + } + }); + + var SINGLE_TOUCH_INPUT_MAP = { + touchstart: INPUT_START, + touchmove: INPUT_MOVE, + touchend: INPUT_END, + touchcancel: INPUT_CANCEL + }; + + var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart'; + var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel'; + + /** + * Touch events input + * @constructor + * @extends Input + */ + function SingleTouchInput() { + this.evTarget = SINGLE_TOUCH_TARGET_EVENTS; + this.evWin = SINGLE_TOUCH_WINDOW_EVENTS; + this.started = false; + + Input.apply(this, arguments); + } + + inherit(SingleTouchInput, Input, { + handler: function TEhandler(ev) { + var type = SINGLE_TOUCH_INPUT_MAP[ev.type]; + + // should we handle the touch events? + if (type === INPUT_START) { + this.started = true; + } + + if (!this.started) { + return; + } + + var touches = normalizeSingleTouches.call(this, ev, type); + + // when done, reset the started state + if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) { + this.started = false; + } + + this.callback(this.manager, type, { + pointers: touches[0], + changedPointers: touches[1], + pointerType: INPUT_TYPE_TOUCH, + srcEvent: ev + }); + } + }); + + /** + * @this {TouchInput} + * @param {Object} ev + * @param {Number} type flag + * @returns {undefined|Array} [all, changed] + */ + function normalizeSingleTouches(ev, type) { + var all = toArray(ev.touches); + var changed = toArray(ev.changedTouches); + + if (type & (INPUT_END | INPUT_CANCEL)) { + all = uniqueArray(all.concat(changed), 'identifier', true); + } + + return [all, changed]; + } + + var TOUCH_INPUT_MAP = { + touchstart: INPUT_START, + touchmove: INPUT_MOVE, + touchend: INPUT_END, + touchcancel: INPUT_CANCEL + }; + + var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel'; + + /** + * Multi-user touch events input + * @constructor + * @extends Input + */ + function TouchInput() { + this.evTarget = TOUCH_TARGET_EVENTS; + this.targetIds = {}; + + Input.apply(this, arguments); + } + + inherit(TouchInput, Input, { + handler: function MTEhandler(ev) { + var type = TOUCH_INPUT_MAP[ev.type]; + var touches = getTouches.call(this, ev, type); + if (!touches) { + return; + } + + this.callback(this.manager, type, { + pointers: touches[0], + changedPointers: touches[1], + pointerType: INPUT_TYPE_TOUCH, + srcEvent: ev + }); + } + }); + + /** + * @this {TouchInput} + * @param {Object} ev + * @param {Number} type flag + * @returns {undefined|Array} [all, changed] + */ + function getTouches(ev, type) { + var allTouches = toArray(ev.touches); + var targetIds = this.targetIds; + + // when there is only one touch, the process can be simplified + if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) { + targetIds[allTouches[0].identifier] = true; + return [allTouches, allTouches]; + } + + var i, + targetTouches, + changedTouches = toArray(ev.changedTouches), + changedTargetTouches = [], + target = this.target; + + // get target touches from touches + targetTouches = allTouches.filter(function(touch) { + return hasParent(touch.target, target); + }); + + // collect touches + if (type === INPUT_START) { + i = 0; + while (i < targetTouches.length) { + targetIds[targetTouches[i].identifier] = true; + i++; + } + } + + // filter changed touches to only contain touches that exist in the collected target ids + i = 0; + while (i < changedTouches.length) { + if (targetIds[changedTouches[i].identifier]) { + changedTargetTouches.push(changedTouches[i]); + } + + // cleanup removed touches + if (type & (INPUT_END | INPUT_CANCEL)) { + delete targetIds[changedTouches[i].identifier]; + } + i++; + } + + if (!changedTargetTouches.length) { + return; + } + + return [ + // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel' + uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true), + changedTargetTouches + ]; + } + + /** + * Combined touch and mouse input + * + * Touch has a higher priority then mouse, and while touching no mouse events are allowed. + * This because touch devices also emit mouse events while doing a touch. + * + * @constructor + * @extends Input + */ + + var DEDUP_TIMEOUT = 2500; + var DEDUP_DISTANCE = 25; + + function TouchMouseInput() { + Input.apply(this, arguments); + + var handler = bindFn(this.handler, this); + this.touch = new TouchInput(this.manager, handler); + this.mouse = new MouseInput(this.manager, handler); + + this.primaryTouch = null; + this.lastTouches = []; + } + + inherit(TouchMouseInput, Input, { + /** + * handle mouse and touch events + * @param {Hammer} manager + * @param {String} inputEvent + * @param {Object} inputData + */ + handler: function TMEhandler(manager, inputEvent, inputData) { + var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH), + isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE); + + if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) { + return; + } + + // when we're in a touch event, record touches to de-dupe synthetic mouse event + if (isTouch) { + recordTouches.call(this, inputEvent, inputData); + } else if (isMouse && isSyntheticEvent.call(this, inputData)) { + return; + } + + this.callback(manager, inputEvent, inputData); + }, + + /** + * remove the event listeners + */ + destroy: function destroy() { + this.touch.destroy(); + this.mouse.destroy(); + } + }); + + function recordTouches(eventType, eventData) { + if (eventType & INPUT_START) { + this.primaryTouch = eventData.changedPointers[0].identifier; + setLastTouch.call(this, eventData); + } else if (eventType & (INPUT_END | INPUT_CANCEL)) { + setLastTouch.call(this, eventData); + } + } + + function setLastTouch(eventData) { + var touch = eventData.changedPointers[0]; + + if (touch.identifier === this.primaryTouch) { + var lastTouch = {x: touch.clientX, y: touch.clientY}; + this.lastTouches.push(lastTouch); + var lts = this.lastTouches; + var removeLastTouch = function() { + var i = lts.indexOf(lastTouch); + if (i > -1) { + lts.splice(i, 1); + } + }; + setTimeout(removeLastTouch, DEDUP_TIMEOUT); + } + } + + function isSyntheticEvent(eventData) { + var x = eventData.srcEvent.clientX, y = eventData.srcEvent.clientY; + for (var i = 0; i < this.lastTouches.length; i++) { + var t = this.lastTouches[i]; + var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y); + if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) { + return true; + } + } + return false; + } + + var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction'); + var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined$1; + + // magical touchAction value + var TOUCH_ACTION_COMPUTE = 'compute'; + var TOUCH_ACTION_AUTO = 'auto'; + var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented + var TOUCH_ACTION_NONE = 'none'; + var TOUCH_ACTION_PAN_X = 'pan-x'; + var TOUCH_ACTION_PAN_Y = 'pan-y'; + var TOUCH_ACTION_MAP = getTouchActionProps(); + + /** + * Touch Action + * sets the touchAction property or uses the js alternative + * @param {Manager} manager + * @param {String} value + * @constructor + */ + function TouchAction(manager, value) { + this.manager = manager; + this.set(value); + } + + TouchAction.prototype = { + /** + * set the touchAction value on the element or enable the polyfill + * @param {String} value + */ + set: function(value) { + // find out the touch-action by the event handlers + if (value == TOUCH_ACTION_COMPUTE) { + value = this.compute(); + } + + if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) { + this.manager.element.style[PREFIXED_TOUCH_ACTION] = value; + } + this.actions = value.toLowerCase().trim(); + }, + + /** + * just re-set the touchAction value + */ + update: function() { + this.set(this.manager.options.touchAction); + }, + + /** + * compute the value for the touchAction property based on the recognizer's settings + * @returns {String} value + */ + compute: function() { + var actions = []; + each(this.manager.recognizers, function(recognizer) { + if (boolOrFn(recognizer.options.enable, [recognizer])) { + actions = actions.concat(recognizer.getTouchAction()); + } + }); + return cleanTouchActions(actions.join(' ')); + }, + + /** + * this method is called on each input cycle and provides the preventing of the browser behavior + * @param {Object} input + */ + preventDefaults: function(input) { + var srcEvent = input.srcEvent; + var direction = input.offsetDirection; + + // if the touch action did prevented once this session + if (this.manager.session.prevented) { + srcEvent.preventDefault(); + return; + } + + var actions = this.actions; + var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE]; + var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y]; + var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X]; + + if (hasNone) { + //do not prevent defaults if this is a tap gesture + + var isTapPointer = input.pointers.length === 1; + var isTapMovement = input.distance < 2; + var isTapTouchTime = input.deltaTime < 250; + + if (isTapPointer && isTapMovement && isTapTouchTime) { + return; + } + } + + if (hasPanX && hasPanY) { + // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent + return; + } + + if (hasNone || + (hasPanY && direction & DIRECTION_HORIZONTAL) || + (hasPanX && direction & DIRECTION_VERTICAL)) { + return this.preventSrc(srcEvent); + } + }, + + /** + * call preventDefault to prevent the browser's default behavior (scrolling in most cases) + * @param {Object} srcEvent + */ + preventSrc: function(srcEvent) { + this.manager.session.prevented = true; + srcEvent.preventDefault(); + } + }; + + /** + * when the touchActions are collected they are not a valid value, so we need to clean things up. * + * @param {String} actions + * @returns {*} + */ + function cleanTouchActions(actions) { + // none + if (inStr(actions, TOUCH_ACTION_NONE)) { + return TOUCH_ACTION_NONE; + } + + var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); + var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); + + // if both pan-x and pan-y are set (different recognizers + // for different directions, e.g. horizontal pan but vertical swipe?) + // we need none (as otherwise with pan-x pan-y combined none of these + // recognizers will work, since the browser would handle all panning + if (hasPanX && hasPanY) { + return TOUCH_ACTION_NONE; + } + + // pan-x OR pan-y + if (hasPanX || hasPanY) { + return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y; + } + + // manipulation + if (inStr(actions, TOUCH_ACTION_MANIPULATION)) { + return TOUCH_ACTION_MANIPULATION; + } + + return TOUCH_ACTION_AUTO; + } + + function getTouchActionProps() { + if (!NATIVE_TOUCH_ACTION) { + return false; + } + var touchMap = {}; + var cssSupports = window.CSS && window.CSS.supports; + ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function(val) { + + // If css.supports is not supported but there is native touch-action assume it supports + // all values. This is the case for IE 10 and 11. + touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true; + }); + return touchMap; + } + + /** + * Recognizer flow explained; * + * All recognizers have the initial state of POSSIBLE when a input session starts. + * The definition of a input session is from the first input until the last input, with all it's movement in it. * + * Example session for mouse-input: mousedown -> mousemove -> mouseup + * + * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed + * which determines with state it should be. + * + * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to + * POSSIBLE to give it another change on the next cycle. + * + * Possible + * | + * +-----+---------------+ + * | | + * +-----+-----+ | + * | | | + * Failed Cancelled | + * +-------+------+ + * | | + * Recognized Began + * | + * Changed + * | + * Ended/Recognized + */ + var STATE_POSSIBLE = 1; + var STATE_BEGAN = 2; + var STATE_CHANGED = 4; + var STATE_ENDED = 8; + var STATE_RECOGNIZED = STATE_ENDED; + var STATE_CANCELLED = 16; + var STATE_FAILED = 32; + + /** + * Recognizer + * Every recognizer needs to extend from this class. + * @constructor + * @param {Object} options + */ + function Recognizer(options) { + this.options = assign({}, this.defaults, options || {}); + + this.id = uniqueId(); + + this.manager = null; + + // default is enable true + this.options.enable = ifUndefined(this.options.enable, true); + + this.state = STATE_POSSIBLE; + + this.simultaneous = {}; + this.requireFail = []; + } + + Recognizer.prototype = { + /** + * @virtual + * @type {Object} + */ + defaults: {}, + + /** + * set options + * @param {Object} options + * @return {Recognizer} + */ + set: function(options) { + assign(this.options, options); + + // also update the touchAction, in case something changed about the directions/enabled state + this.manager && this.manager.touchAction.update(); + return this; + }, + + /** + * recognize simultaneous with an other recognizer. + * @param {Recognizer} otherRecognizer + * @returns {Recognizer} this + */ + recognizeWith: function(otherRecognizer) { + if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) { + return this; + } + + var simultaneous = this.simultaneous; + otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); + if (!simultaneous[otherRecognizer.id]) { + simultaneous[otherRecognizer.id] = otherRecognizer; + otherRecognizer.recognizeWith(this); + } + return this; + }, + + /** + * drop the simultaneous link. it doesnt remove the link on the other recognizer. + * @param {Recognizer} otherRecognizer + * @returns {Recognizer} this + */ + dropRecognizeWith: function(otherRecognizer) { + if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) { + return this; + } + + otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); + delete this.simultaneous[otherRecognizer.id]; + return this; + }, + + /** + * recognizer can only run when an other is failing + * @param {Recognizer} otherRecognizer + * @returns {Recognizer} this + */ + requireFailure: function(otherRecognizer) { + if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) { + return this; + } + + var requireFail = this.requireFail; + otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); + if (inArray(requireFail, otherRecognizer) === -1) { + requireFail.push(otherRecognizer); + otherRecognizer.requireFailure(this); + } + return this; + }, + + /** + * drop the requireFailure link. it does not remove the link on the other recognizer. + * @param {Recognizer} otherRecognizer + * @returns {Recognizer} this + */ + dropRequireFailure: function(otherRecognizer) { + if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) { + return this; + } + + otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); + var index = inArray(this.requireFail, otherRecognizer); + if (index > -1) { + this.requireFail.splice(index, 1); + } + return this; + }, + + /** + * has require failures boolean + * @returns {boolean} + */ + hasRequireFailures: function() { + return this.requireFail.length > 0; + }, + + /** + * if the recognizer can recognize simultaneous with an other recognizer + * @param {Recognizer} otherRecognizer + * @returns {Boolean} + */ + canRecognizeWith: function(otherRecognizer) { + return !!this.simultaneous[otherRecognizer.id]; + }, + + /** + * You should use `tryEmit` instead of `emit` directly to check + * that all the needed recognizers has failed before emitting. + * @param {Object} input + */ + emit: function(input) { + var self = this; + var state = this.state; + + function emit(event) { + self.manager.emit(event, input); + } + + // 'panstart' and 'panmove' + if (state < STATE_ENDED) { + emit(self.options.event + stateStr(state)); + } + + emit(self.options.event); // simple 'eventName' events + + if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...) + emit(input.additionalEvent); + } + + // panend and pancancel + if (state >= STATE_ENDED) { + emit(self.options.event + stateStr(state)); + } + }, + + /** + * Check that all the require failure recognizers has failed, + * if true, it emits a gesture event, + * otherwise, setup the state to FAILED. + * @param {Object} input + */ + tryEmit: function(input) { + if (this.canEmit()) { + return this.emit(input); + } + // it's failing anyway + this.state = STATE_FAILED; + }, + + /** + * can we emit? + * @returns {boolean} + */ + canEmit: function() { + var i = 0; + while (i < this.requireFail.length) { + if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) { + return false; + } + i++; + } + return true; + }, + + /** + * update the recognizer + * @param {Object} inputData + */ + recognize: function(inputData) { + // make a new copy of the inputData + // so we can change the inputData without messing up the other recognizers + var inputDataClone = assign({}, inputData); + + // is is enabled and allow recognizing? + if (!boolOrFn(this.options.enable, [this, inputDataClone])) { + this.reset(); + this.state = STATE_FAILED; + return; + } + + // reset when we've reached the end + if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) { + this.state = STATE_POSSIBLE; + } + + this.state = this.process(inputDataClone); + + // the recognizer has recognized a gesture + // so trigger an event + if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) { + this.tryEmit(inputDataClone); + } + }, + + /** + * return the state of the recognizer + * the actual recognizing happens in this method + * @virtual + * @param {Object} inputData + * @returns {Const} STATE + */ + process: function(inputData) { }, // jshint ignore:line + + /** + * return the preferred touch-action + * @virtual + * @returns {Array} + */ + getTouchAction: function() { }, + + /** + * called when the gesture isn't allowed to recognize + * like when another is being recognized or it is disabled + * @virtual + */ + reset: function() { } + }; + + /** + * get a usable string, used as event postfix + * @param {Const} state + * @returns {String} state + */ + function stateStr(state) { + if (state & STATE_CANCELLED) { + return 'cancel'; + } else if (state & STATE_ENDED) { + return 'end'; + } else if (state & STATE_CHANGED) { + return 'move'; + } else if (state & STATE_BEGAN) { + return 'start'; + } + return ''; + } + + /** + * direction cons to string + * @param {Const} direction + * @returns {String} + */ + function directionStr(direction) { + if (direction == DIRECTION_DOWN) { + return 'down'; + } else if (direction == DIRECTION_UP) { + return 'up'; + } else if (direction == DIRECTION_LEFT) { + return 'left'; + } else if (direction == DIRECTION_RIGHT) { + return 'right'; + } + return ''; + } + + /** + * get a recognizer by name if it is bound to a manager + * @param {Recognizer|String} otherRecognizer + * @param {Recognizer} recognizer + * @returns {Recognizer} + */ + function getRecognizerByNameIfManager(otherRecognizer, recognizer) { + var manager = recognizer.manager; + if (manager) { + return manager.get(otherRecognizer); + } + return otherRecognizer; + } + + /** + * This recognizer is just used as a base for the simple attribute recognizers. + * @constructor + * @extends Recognizer + */ + function AttrRecognizer() { + Recognizer.apply(this, arguments); + } + + inherit(AttrRecognizer, Recognizer, { + /** + * @namespace + * @memberof AttrRecognizer + */ + defaults: { + /** + * @type {Number} + * @default 1 + */ + pointers: 1 + }, + + /** + * Used to check if it the recognizer receives valid input, like input.distance > 10. + * @memberof AttrRecognizer + * @param {Object} input + * @returns {Boolean} recognized + */ + attrTest: function(input) { + var optionPointers = this.options.pointers; + return optionPointers === 0 || input.pointers.length === optionPointers; + }, + + /** + * Process the input and return the state for the recognizer + * @memberof AttrRecognizer + * @param {Object} input + * @returns {*} State + */ + process: function(input) { + var state = this.state; + var eventType = input.eventType; + + var isRecognized = state & (STATE_BEGAN | STATE_CHANGED); + var isValid = this.attrTest(input); + + // on cancel input and we've recognized before, return STATE_CANCELLED + if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) { + return state | STATE_CANCELLED; + } else if (isRecognized || isValid) { + if (eventType & INPUT_END) { + return state | STATE_ENDED; + } else if (!(state & STATE_BEGAN)) { + return STATE_BEGAN; + } + return state | STATE_CHANGED; + } + return STATE_FAILED; + } + }); + + /** + * Pan + * Recognized when the pointer is down and moved in the allowed direction. + * @constructor + * @extends AttrRecognizer + */ + function PanRecognizer() { + AttrRecognizer.apply(this, arguments); + + this.pX = null; + this.pY = null; + } + + inherit(PanRecognizer, AttrRecognizer, { + /** + * @namespace + * @memberof PanRecognizer + */ + defaults: { + event: 'pan', + threshold: 10, + pointers: 1, + direction: DIRECTION_ALL + }, + + getTouchAction: function() { + var direction = this.options.direction; + var actions = []; + if (direction & DIRECTION_HORIZONTAL) { + actions.push(TOUCH_ACTION_PAN_Y); + } + if (direction & DIRECTION_VERTICAL) { + actions.push(TOUCH_ACTION_PAN_X); + } + return actions; + }, + + directionTest: function(input) { + var options = this.options; + var hasMoved = true; + var distance = input.distance; + var direction = input.direction; + var x = input.deltaX; + var y = input.deltaY; + + // lock to axis? + if (!(direction & options.direction)) { + if (options.direction & DIRECTION_HORIZONTAL) { + direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; + hasMoved = x != this.pX; + distance = Math.abs(input.deltaX); + } else { + direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN; + hasMoved = y != this.pY; + distance = Math.abs(input.deltaY); + } + } + input.direction = direction; + return hasMoved && distance > options.threshold && direction & options.direction; + }, + + attrTest: function(input) { + return AttrRecognizer.prototype.attrTest.call(this, input) && + (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input))); + }, + + emit: function(input) { + + this.pX = input.deltaX; + this.pY = input.deltaY; + + var direction = directionStr(input.direction); + + if (direction) { + input.additionalEvent = this.options.event + direction; + } + this._super.emit.call(this, input); + } + }); + + /** + * Pinch + * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out). + * @constructor + * @extends AttrRecognizer + */ + function PinchRecognizer() { + AttrRecognizer.apply(this, arguments); + } + + inherit(PinchRecognizer, AttrRecognizer, { + /** + * @namespace + * @memberof PinchRecognizer + */ + defaults: { + event: 'pinch', + threshold: 0, + pointers: 2 + }, + + getTouchAction: function() { + return [TOUCH_ACTION_NONE]; + }, + + attrTest: function(input) { + return this._super.attrTest.call(this, input) && + (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN); + }, + + emit: function(input) { + if (input.scale !== 1) { + var inOut = input.scale < 1 ? 'in' : 'out'; + input.additionalEvent = this.options.event + inOut; + } + this._super.emit.call(this, input); + } + }); + + /** + * Press + * Recognized when the pointer is down for x ms without any movement. + * @constructor + * @extends Recognizer + */ + function PressRecognizer() { + Recognizer.apply(this, arguments); + + this._timer = null; + this._input = null; + } + + inherit(PressRecognizer, Recognizer, { + /** + * @namespace + * @memberof PressRecognizer + */ + defaults: { + event: 'press', + pointers: 1, + time: 251, // minimal time of the pointer to be pressed + threshold: 9 // a minimal movement is ok, but keep it low + }, + + getTouchAction: function() { + return [TOUCH_ACTION_AUTO]; + }, + + process: function(input) { + var options = this.options; + var validPointers = input.pointers.length === options.pointers; + var validMovement = input.distance < options.threshold; + var validTime = input.deltaTime > options.time; + + this._input = input; + + // we only allow little movement + // and we've reached an end event, so a tap is possible + if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) { + this.reset(); + } else if (input.eventType & INPUT_START) { + this.reset(); + this._timer = setTimeoutContext(function() { + this.state = STATE_RECOGNIZED; + this.tryEmit(); + }, options.time, this); + } else if (input.eventType & INPUT_END) { + return STATE_RECOGNIZED; + } + return STATE_FAILED; + }, + + reset: function() { + clearTimeout(this._timer); + }, + + emit: function(input) { + if (this.state !== STATE_RECOGNIZED) { + return; + } + + if (input && (input.eventType & INPUT_END)) { + this.manager.emit(this.options.event + 'up', input); + } else { + this._input.timeStamp = now(); + this.manager.emit(this.options.event, this._input); + } + } + }); + + /** + * Rotate + * Recognized when two or more pointer are moving in a circular motion. + * @constructor + * @extends AttrRecognizer + */ + function RotateRecognizer() { + AttrRecognizer.apply(this, arguments); + } + + inherit(RotateRecognizer, AttrRecognizer, { + /** + * @namespace + * @memberof RotateRecognizer + */ + defaults: { + event: 'rotate', + threshold: 0, + pointers: 2 + }, + + getTouchAction: function() { + return [TOUCH_ACTION_NONE]; + }, + + attrTest: function(input) { + return this._super.attrTest.call(this, input) && + (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN); + } + }); + + /** + * Swipe + * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction. + * @constructor + * @extends AttrRecognizer + */ + function SwipeRecognizer() { + AttrRecognizer.apply(this, arguments); + } + + inherit(SwipeRecognizer, AttrRecognizer, { + /** + * @namespace + * @memberof SwipeRecognizer + */ + defaults: { + event: 'swipe', + threshold: 10, + velocity: 0.3, + direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL, + pointers: 1 + }, + + getTouchAction: function() { + return PanRecognizer.prototype.getTouchAction.call(this); + }, + + attrTest: function(input) { + var direction = this.options.direction; + var velocity; + + if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) { + velocity = input.overallVelocity; + } else if (direction & DIRECTION_HORIZONTAL) { + velocity = input.overallVelocityX; + } else if (direction & DIRECTION_VERTICAL) { + velocity = input.overallVelocityY; + } + + return this._super.attrTest.call(this, input) && + direction & input.offsetDirection && + input.distance > this.options.threshold && + input.maxPointers == this.options.pointers && + abs(velocity) > this.options.velocity && input.eventType & INPUT_END; + }, + + emit: function(input) { + var direction = directionStr(input.offsetDirection); + if (direction) { + this.manager.emit(this.options.event + direction, input); + } + + this.manager.emit(this.options.event, input); + } + }); + + /** + * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur + * between the given interval and position. The delay option can be used to recognize multi-taps without firing + * a single tap. + * + * The eventData from the emitted event contains the property `tapCount`, which contains the amount of + * multi-taps being recognized. + * @constructor + * @extends Recognizer + */ + function TapRecognizer() { + Recognizer.apply(this, arguments); + + // previous time and center, + // used for tap counting + this.pTime = false; + this.pCenter = false; + + this._timer = null; + this._input = null; + this.count = 0; + } + + inherit(TapRecognizer, Recognizer, { + /** + * @namespace + * @memberof PinchRecognizer + */ + defaults: { + event: 'tap', + pointers: 1, + taps: 1, + interval: 300, // max time between the multi-tap taps + time: 250, // max time of the pointer to be down (like finger on the screen) + threshold: 9, // a minimal movement is ok, but keep it low + posThreshold: 10 // a multi-tap can be a bit off the initial position + }, + + getTouchAction: function() { + return [TOUCH_ACTION_MANIPULATION]; + }, + + process: function(input) { + var options = this.options; + + var validPointers = input.pointers.length === options.pointers; + var validMovement = input.distance < options.threshold; + var validTouchTime = input.deltaTime < options.time; + + this.reset(); + + if ((input.eventType & INPUT_START) && (this.count === 0)) { + return this.failTimeout(); + } + + // we only allow little movement + // and we've reached an end event, so a tap is possible + if (validMovement && validTouchTime && validPointers) { + if (input.eventType != INPUT_END) { + return this.failTimeout(); + } + + var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true; + var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold; + + this.pTime = input.timeStamp; + this.pCenter = input.center; + + if (!validMultiTap || !validInterval) { + this.count = 1; + } else { + this.count += 1; + } + + this._input = input; + + // if tap count matches we have recognized it, + // else it has began recognizing... + var tapCount = this.count % options.taps; + if (tapCount === 0) { + // no failing requirements, immediately trigger the tap event + // or wait as long as the multitap interval to trigger + if (!this.hasRequireFailures()) { + return STATE_RECOGNIZED; + } else { + this._timer = setTimeoutContext(function() { + this.state = STATE_RECOGNIZED; + this.tryEmit(); + }, options.interval, this); + return STATE_BEGAN; + } + } + } + return STATE_FAILED; + }, + + failTimeout: function() { + this._timer = setTimeoutContext(function() { + this.state = STATE_FAILED; + }, this.options.interval, this); + return STATE_FAILED; + }, + + reset: function() { + clearTimeout(this._timer); + }, + + emit: function() { + if (this.state == STATE_RECOGNIZED) { + this._input.tapCount = this.count; + this.manager.emit(this.options.event, this._input); + } + } + }); + + /** + * Simple way to create a manager with a default set of recognizers. + * @param {HTMLElement} element + * @param {Object} [options] + * @constructor + */ + function Hammer(element, options) { + options = options || {}; + options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset); + return new Manager(element, options); + } + + /** + * @const {string} + */ + Hammer.VERSION = '2.0.7'; + + /** + * default settings + * @namespace + */ + Hammer.defaults = { + /** + * set if DOM events are being triggered. + * But this is slower and unused by simple implementations, so disabled by default. + * @type {Boolean} + * @default false + */ + domEvents: false, + + /** + * The value for the touchAction property/fallback. + * When set to `compute` it will magically set the correct value based on the added recognizers. + * @type {String} + * @default compute + */ + touchAction: TOUCH_ACTION_COMPUTE, + + /** + * @type {Boolean} + * @default true + */ + enable: true, + + /** + * EXPERIMENTAL FEATURE -- can be removed/changed + * Change the parent input target element. + * If Null, then it is being set the to main element. + * @type {Null|EventTarget} + * @default null + */ + inputTarget: null, + + /** + * force an input class + * @type {Null|Function} + * @default null + */ + inputClass: null, + + /** + * Default recognizer setup when calling `Hammer()` + * When creating a new Manager these will be skipped. + * @type {Array} + */ + preset: [ + // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...] + [RotateRecognizer, {enable: false}], + [PinchRecognizer, {enable: false}, ['rotate']], + [SwipeRecognizer, {direction: DIRECTION_HORIZONTAL}], + [PanRecognizer, {direction: DIRECTION_HORIZONTAL}, ['swipe']], + [TapRecognizer], + [TapRecognizer, {event: 'doubletap', taps: 2}, ['tap']], + [PressRecognizer] + ], + + /** + * Some CSS properties can be used to improve the working of Hammer. + * Add them to this method and they will be set when creating a new Manager. + * @namespace + */ + cssProps: { + /** + * Disables text selection to improve the dragging gesture. Mainly for desktop browsers. + * @type {String} + * @default 'none' + */ + userSelect: 'none', + + /** + * Disable the Windows Phone grippers when pressing an element. + * @type {String} + * @default 'none' + */ + touchSelect: 'none', + + /** + * Disables the default callout shown when you touch and hold a touch target. + * On iOS, when you touch and hold a touch target such as a link, Safari displays + * a callout containing information about the link. This property allows you to disable that callout. + * @type {String} + * @default 'none' + */ + touchCallout: 'none', + + /** + * Specifies whether zooming is enabled. Used by IE10> + * @type {String} + * @default 'none' + */ + contentZooming: 'none', + + /** + * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers. + * @type {String} + * @default 'none' + */ + userDrag: 'none', + + /** + * Overrides the highlight color shown when the user taps a link or a JavaScript + * clickable element in iOS. This property obeys the alpha value, if specified. + * @type {String} + * @default 'rgba(0,0,0,0)' + */ + tapHighlightColor: 'rgba(0,0,0,0)' + } + }; + + var STOP = 1; + var FORCED_STOP = 2; + + /** + * Manager + * @param {HTMLElement} element + * @param {Object} [options] + * @constructor + */ + function Manager(element, options) { + this.options = assign({}, Hammer.defaults, options || {}); + + this.options.inputTarget = this.options.inputTarget || element; + + this.handlers = {}; + this.session = {}; + this.recognizers = []; + this.oldCssProps = {}; + + this.element = element; + this.input = createInputInstance(this); + this.touchAction = new TouchAction(this, this.options.touchAction); + + toggleCssProps(this, true); + + each(this.options.recognizers, function(item) { + var recognizer = this.add(new (item[0])(item[1])); + item[2] && recognizer.recognizeWith(item[2]); + item[3] && recognizer.requireFailure(item[3]); + }, this); + } + + Manager.prototype = { + /** + * set options + * @param {Object} options + * @returns {Manager} + */ + set: function(options) { + assign(this.options, options); + + // Options that need a little more setup + if (options.touchAction) { + this.touchAction.update(); + } + if (options.inputTarget) { + // Clean up existing event listeners and reinitialize + this.input.destroy(); + this.input.target = options.inputTarget; + this.input.init(); + } + return this; + }, + + /** + * stop recognizing for this session. + * This session will be discarded, when a new [input]start event is fired. + * When forced, the recognizer cycle is stopped immediately. + * @param {Boolean} [force] + */ + stop: function(force) { + this.session.stopped = force ? FORCED_STOP : STOP; + }, + + /** + * run the recognizers! + * called by the inputHandler function on every movement of the pointers (touches) + * it walks through all the recognizers and tries to detect the gesture that is being made + * @param {Object} inputData + */ + recognize: function(inputData) { + var session = this.session; + if (session.stopped) { + return; + } + + // run the touch-action polyfill + this.touchAction.preventDefaults(inputData); + + var recognizer; + var recognizers = this.recognizers; + + // this holds the recognizer that is being recognized. + // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED + // if no recognizer is detecting a thing, it is set to `null` + var curRecognizer = session.curRecognizer; + + // reset when the last recognizer is recognized + // or when we're in a new session + if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) { + curRecognizer = session.curRecognizer = null; + } + + var i = 0; + while (i < recognizers.length) { + recognizer = recognizers[i]; + + // find out if we are allowed try to recognize the input for this one. + // 1. allow if the session is NOT forced stopped (see the .stop() method) + // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one + // that is being recognized. + // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer. + // this can be setup with the `recognizeWith()` method on the recognizer. + if (session.stopped !== FORCED_STOP && ( // 1 + !curRecognizer || recognizer == curRecognizer || // 2 + recognizer.canRecognizeWith(curRecognizer))) { // 3 + recognizer.recognize(inputData); + } else { + recognizer.reset(); + } + + // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the + // current active recognizer. but only if we don't already have an active recognizer + if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) { + curRecognizer = session.curRecognizer = recognizer; + } + i++; + } + }, + + /** + * get a recognizer by its event name. + * @param {Recognizer|String} recognizer + * @returns {Recognizer|Null} + */ + get: function(recognizer) { + if (recognizer instanceof Recognizer) { + return recognizer; + } + + var recognizers = this.recognizers; + for (var i = 0; i < recognizers.length; i++) { + if (recognizers[i].options.event == recognizer) { + return recognizers[i]; + } + } + return null; + }, + + /** + * add a recognizer to the manager + * existing recognizers with the same event name will be removed + * @param {Recognizer} recognizer + * @returns {Recognizer|Manager} + */ + add: function(recognizer) { + if (invokeArrayArg(recognizer, 'add', this)) { + return this; + } + + // remove existing + var existing = this.get(recognizer.options.event); + if (existing) { + this.remove(existing); + } + + this.recognizers.push(recognizer); + recognizer.manager = this; + + this.touchAction.update(); + return recognizer; + }, + + /** + * remove a recognizer by name or instance + * @param {Recognizer|String} recognizer + * @returns {Manager} + */ + remove: function(recognizer) { + if (invokeArrayArg(recognizer, 'remove', this)) { + return this; + } + + recognizer = this.get(recognizer); + + // let's make sure this recognizer exists + if (recognizer) { + var recognizers = this.recognizers; + var index = inArray(recognizers, recognizer); + + if (index !== -1) { + recognizers.splice(index, 1); + this.touchAction.update(); + } + } + + return this; + }, + + /** + * bind event + * @param {String} events + * @param {Function} handler + * @returns {EventEmitter} this + */ + on: function(events, handler) { + if (events === undefined$1) { + return; + } + if (handler === undefined$1) { + return; + } + + var handlers = this.handlers; + each(splitStr(events), function(event) { + handlers[event] = handlers[event] || []; + handlers[event].push(handler); + }); + return this; + }, + + /** + * unbind event, leave emit blank to remove all handlers + * @param {String} events + * @param {Function} [handler] + * @returns {EventEmitter} this + */ + off: function(events, handler) { + if (events === undefined$1) { + return; + } + + var handlers = this.handlers; + each(splitStr(events), function(event) { + if (!handler) { + delete handlers[event]; + } else { + handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1); + } + }); + return this; + }, + + /** + * emit event to the listeners + * @param {String} event + * @param {Object} data + */ + emit: function(event, data) { + // we also want to trigger dom events + if (this.options.domEvents) { + triggerDomEvent(event, data); + } + + // no handlers, so skip it all + var handlers = this.handlers[event] && this.handlers[event].slice(); + if (!handlers || !handlers.length) { + return; + } + + data.type = event; + data.preventDefault = function() { + data.srcEvent.preventDefault(); + }; + + var i = 0; + while (i < handlers.length) { + handlers[i](data); + i++; + } + }, + + /** + * destroy the manager and unbinds all events + * it doesn't unbind dom events, that is the user own responsibility + */ + destroy: function() { + this.element && toggleCssProps(this, false); + + this.handlers = {}; + this.session = {}; + this.input.destroy(); + this.element = null; + } + }; + + /** + * add/remove the css properties as defined in manager.options.cssProps + * @param {Manager} manager + * @param {Boolean} add + */ + function toggleCssProps(manager, add) { + var element = manager.element; + if (!element.style) { + return; + } + var prop; + each(manager.options.cssProps, function(value, name) { + prop = prefixed(element.style, name); + if (add) { + manager.oldCssProps[prop] = element.style[prop]; + element.style[prop] = value; + } else { + element.style[prop] = manager.oldCssProps[prop] || ''; + } + }); + if (!add) { + manager.oldCssProps = {}; + } + } + + /** + * trigger dom event + * @param {String} event + * @param {Object} data + */ + function triggerDomEvent(event, data) { + var gestureEvent = document.createEvent('Event'); + gestureEvent.initEvent(event, true, true); + gestureEvent.gesture = data; + data.target.dispatchEvent(gestureEvent); + } + + assign(Hammer, { + INPUT_START: INPUT_START, + INPUT_MOVE: INPUT_MOVE, + INPUT_END: INPUT_END, + INPUT_CANCEL: INPUT_CANCEL, + + STATE_POSSIBLE: STATE_POSSIBLE, + STATE_BEGAN: STATE_BEGAN, + STATE_CHANGED: STATE_CHANGED, + STATE_ENDED: STATE_ENDED, + STATE_RECOGNIZED: STATE_RECOGNIZED, + STATE_CANCELLED: STATE_CANCELLED, + STATE_FAILED: STATE_FAILED, + + DIRECTION_NONE: DIRECTION_NONE, + DIRECTION_LEFT: DIRECTION_LEFT, + DIRECTION_RIGHT: DIRECTION_RIGHT, + DIRECTION_UP: DIRECTION_UP, + DIRECTION_DOWN: DIRECTION_DOWN, + DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL, + DIRECTION_VERTICAL: DIRECTION_VERTICAL, + DIRECTION_ALL: DIRECTION_ALL, + + Manager: Manager, + Input: Input, + TouchAction: TouchAction, + + TouchInput: TouchInput, + MouseInput: MouseInput, + PointerEventInput: PointerEventInput, + TouchMouseInput: TouchMouseInput, + SingleTouchInput: SingleTouchInput, + + Recognizer: Recognizer, + AttrRecognizer: AttrRecognizer, + Tap: TapRecognizer, + Pan: PanRecognizer, + Swipe: SwipeRecognizer, + Pinch: PinchRecognizer, + Rotate: RotateRecognizer, + Press: PressRecognizer, + + on: addEventListeners, + off: removeEventListeners, + each: each, + merge: merge, + extend: extend, + assign: assign, + inherit: inherit, + bindFn: bindFn, + prefixed: prefixed + }); + + // this prevents errors when Hammer is loaded in the presence of an AMD + // style loader but by script tag, not by the loader. + var freeGlobal = (typeof window !== 'undefined' ? window : (typeof self !== 'undefined' ? self : {})); // jshint ignore:line + freeGlobal.Hammer = Hammer; + + if (typeof undefined$1 === 'function' && undefined$1.amd) { + undefined$1(function() { + return Hammer; + }); + } else if (module.exports) { + module.exports = Hammer; + } else { + window[exportName] = Hammer; + } + + })(window, document, 'Hammer'); + } (hammer)); + + var Hammer = hammer.exports; + + var MIN_ZOOM = 0.2, + MAX_ZOOM = 4; + + var mouseEvents = [ + 'mousedown', + 'mouseup', + 'mouseover', + 'mouseout', + 'click', + 'dblclick' + ]; + + function get(service, injector) { + return injector.get(service, false); + } + + function stopEvent(event) { + + event.preventDefault(); + + if (typeof event.stopPropagation === 'function') { + event.stopPropagation(); + } else if (event.srcEvent && typeof event.srcEvent.stopPropagation === 'function') { + + // iPhone & iPad + event.srcEvent.stopPropagation(); + } + + if (typeof event.stopImmediatePropagation === 'function') { + event.stopImmediatePropagation(); + } + } + + + function createTouchRecognizer(node) { + + function stopMouse(event) { + + forEach$1(mouseEvents, function(e) { + componentEvent.bind(node, e, stopEvent, true); + }); + } + + function allowMouse(event) { + setTimeout(function() { + forEach$1(mouseEvents, function(e) { + componentEvent.unbind(node, e, stopEvent, true); + }); + }, 500); + } + + componentEvent.bind(node, 'touchstart', stopMouse, true); + componentEvent.bind(node, 'touchend', allowMouse, true); + componentEvent.bind(node, 'touchcancel', allowMouse, true); + + // A touch event recognizer that handles + // touch events only (we know, we can already handle + // mouse events out of the box) + + var recognizer = new Hammer.Manager(node, { + inputClass: Hammer.TouchInput, + recognizers: [], + domEvents: true + }); + + + var tap = new Hammer.Tap(); + var pan = new Hammer.Pan({ threshold: 10 }); + var press = new Hammer.Press(); + var pinch = new Hammer.Pinch(); + + var doubleTap = new Hammer.Tap({ event: 'doubletap', taps: 2 }); + + pinch.requireFailure(pan); + pinch.requireFailure(press); + + recognizer.add([ pan, press, pinch, doubleTap, tap ]); + + recognizer.reset = function(force) { + var recognizers = this.recognizers, + session = this.session; + + if (session.stopped) { + return; + } + + recognizer.stop(force); + + setTimeout(function() { + var i, r; + for (i = 0; (r = recognizers[i]); i++) { + r.reset(); + r.state = 8; // FAILED STATE + } + + session.curRecognizer = null; + }, 0); + }; + + recognizer.on('hammer.input', function(event) { + if (event.srcEvent.defaultPrevented) { + recognizer.reset(true); + } + }); + + return recognizer; + } + + /** + * A plugin that provides touch events for elements. + * + * @param {EventBus} eventBus + * @param {InteractionEvents} interactionEvents + */ + function TouchInteractionEvents( + injector, canvas, eventBus, + elementRegistry, interactionEvents) { + + // optional integrations + var dragging = get('dragging', injector), + move = get('move', injector), + contextPad = get('contextPad', injector), + palette = get('palette', injector); + + // the touch recognizer + var recognizer; + + function handler(type, buttonType) { + + return function(event) { + + var gfx = getGfx(event.target), + element = gfx && elementRegistry.get(gfx); + + // translate into an actual mouse click event + if (buttonType) { + event.srcEvent.button = buttonType; + } + + return interactionEvents.fire(type, event, element); + }; + } + + + function getGfx(target) { + var node = closest(target, 'svg, .djs-element', true); + return node; + } + + function initEvents(svg) { + + // touch recognizer + recognizer = createTouchRecognizer(svg); + + function startGrabCanvas(event) { + + var lx = 0, ly = 0; + + function update(e) { + + var dx = e.deltaX - lx, + dy = e.deltaY - ly; + + canvas.scroll({ dx: dx, dy: dy }); + + lx = e.deltaX; + ly = e.deltaY; + } + + function end(e) { + recognizer.off('panmove', update); + recognizer.off('panend', end); + recognizer.off('pancancel', end); + } + + recognizer.on('panmove', update); + recognizer.on('panend', end); + recognizer.on('pancancel', end); + } + + function startGrab(event) { + + var gfx = getGfx(event.target), + element = gfx && elementRegistry.get(gfx); + + // recognizer + if (move && canvas.getRootElement() !== element) { + return move.start(event, element, true); + } else { + startGrabCanvas(); + } + } + + function startZoom(e) { + + var zoom = canvas.zoom(), + mid = e.center; + + function update(e) { + + var ratio = 1 - (1 - e.scale) / 1.50, + newZoom = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, ratio * zoom)); + + canvas.zoom(newZoom, mid); + + stopEvent(e); + } + + function end(e) { + recognizer.off('pinchmove', update); + recognizer.off('pinchend', end); + recognizer.off('pinchcancel', end); + + recognizer.reset(true); + } + + recognizer.on('pinchmove', update); + recognizer.on('pinchend', end); + recognizer.on('pinchcancel', end); + } + + recognizer.on('tap', handler('element.click')); + recognizer.on('doubletap', handler('element.dblclick', 1)); + + recognizer.on('panstart', startGrab); + recognizer.on('press', startGrab); + + recognizer.on('pinchstart', startZoom); + } + + if (dragging) { + + // simulate hover during dragging + eventBus.on('drag.move', function(event) { + + var originalEvent = event.originalEvent; + + if (!originalEvent || originalEvent instanceof MouseEvent) { + return; + } + + var position = toPoint(originalEvent); + + // this gets really expensive ... + var node = document.elementFromPoint(position.x, position.y), + gfx = getGfx(node), + element = gfx && elementRegistry.get(gfx); + + if (element !== event.hover) { + if (event.hover) { + dragging.out(event); + } + + if (element) { + dragging.hover({ element: element, gfx: gfx }); + + event.hover = element; + event.hoverGfx = gfx; + } + } + }); + } + + if (contextPad) { + + eventBus.on('contextPad.create', function(event) { + var node = event.pad.html; + + // touch recognizer + var padRecognizer = createTouchRecognizer(node); + + padRecognizer.on('panstart', function(event) { + contextPad.trigger('dragstart', event, true); + }); + + padRecognizer.on('press', function(event) { + contextPad.trigger('dragstart', event, true); + }); + + padRecognizer.on('tap', function(event) { + contextPad.trigger('click', event); + }); + }); + } + + if (palette) { + eventBus.on('palette.create', function(event) { + var node = event.container; + + // touch recognizer + var padRecognizer = createTouchRecognizer(node); + + padRecognizer.on('panstart', function(event) { + palette.trigger('dragstart', event, true); + }); + + padRecognizer.on('press', function(event) { + palette.trigger('dragstart', event, true); + }); + + padRecognizer.on('tap', function(event) { + palette.trigger('click', event); + }); + }); + } + + eventBus.on('canvas.init', function(event) { + initEvents(event.svg); + }); + } + + + TouchInteractionEvents.$inject = [ + 'injector', + 'canvas', + 'eventBus', + 'elementRegistry', + 'interactionEvents', + 'touchFix' + ]; + + function TouchFix(canvas, eventBus) { + + var self = this; + + eventBus.on('canvas.init', function(e) { + self.addBBoxMarker(e.svg); + }); + } + + TouchFix.$inject = [ 'canvas', 'eventBus' ]; + + + /** + * Safari mobile (iOS 7) does not fire touchstart event in element + * if there is no shape between 0,0 and viewport elements origin. + * + * So touchstart event is only fired when the element was hit. + * Putting an element over and below the 'viewport' fixes that behavior. + */ + TouchFix.prototype.addBBoxMarker = function(svg) { + + var markerStyle = { + fill: 'none', + class: 'outer-bound-marker' + }; + + var rect1 = create$1('rect'); + attr(rect1, { + x: -10000, + y: 10000, + width: 10, + height: 10 + }); + attr(rect1, markerStyle); + + append(svg, rect1); + + var rect2 = create$1('rect'); + attr(rect2, { + x: 10000, + y: 10000, + width: 10, + height: 10 + }); + attr(rect2, markerStyle); + + append(svg, rect2); + }; + + var TouchModule$1 = { + __depends__: [ InteractionEventsModule$1 ], + __init__: [ 'touchInteractionEvents' ], + touchInteractionEvents: [ 'type', TouchInteractionEvents ], + touchFix: [ 'type', TouchFix ] + }; + + var TouchModule = { + __depends__: [ + TouchModule$1 + ] + }; + + function last(arr) { + return arr && arr[arr.length - 1]; + } + + function sortTopOrMiddle(element) { + return element.y; + } + + function sortLeftOrCenter(element) { + return element.x; + } + + /** + * Sorting functions for different types of alignment + * + * @type {Object} + * + * @return {Function} + */ + var ALIGNMENT_SORTING = { + left: sortLeftOrCenter, + center: sortLeftOrCenter, + right: function(element) { + return element.x + element.width; + }, + top: sortTopOrMiddle, + middle: sortTopOrMiddle, + bottom: function(element) { + return element.y + element.height; + } + }; + + + function AlignElements$1(modeling, rules) { + this._modeling = modeling; + this._rules = rules; + } + + AlignElements$1.$inject = [ 'modeling', 'rules' ]; + + + /** + * Get the relevant "axis" and "dimension" related to the current type of alignment + * + * @param {string} type left|right|center|top|bottom|middle + * + * @return {Object} { axis, dimension } + */ + AlignElements$1.prototype._getOrientationDetails = function(type) { + var vertical = [ 'top', 'bottom', 'middle' ], + axis = 'x', + dimension = 'width'; + + if (vertical.indexOf(type) !== -1) { + axis = 'y'; + dimension = 'height'; + } + + return { + axis: axis, + dimension: dimension + }; + }; + + AlignElements$1.prototype._isType = function(type, types) { + return types.indexOf(type) !== -1; + }; + + /** + * Get a point on the relevant axis where elements should align to + * + * @param {string} type left|right|center|top|bottom|middle + * @param {Array} sortedElements + * + * @return {Object} + */ + AlignElements$1.prototype._alignmentPosition = function(type, sortedElements) { + var orientation = this._getOrientationDetails(type), + axis = orientation.axis, + dimension = orientation.dimension, + alignment = {}, + centers = {}, + hasSharedCenters = false, + centeredElements, + firstElement, + lastElement; + + function getMiddleOrTop(first, last) { + return Math.round((first[axis] + last[axis] + last[dimension]) / 2); + } + + if (this._isType(type, [ 'left', 'top' ])) { + alignment[type] = sortedElements[0][axis]; + + } else if (this._isType(type, [ 'right', 'bottom' ])) { + lastElement = last(sortedElements); + + alignment[type] = lastElement[axis] + lastElement[dimension]; + + } else if (this._isType(type, [ 'center', 'middle' ])) { + + // check if there is a center shared by more than one shape + // if not, just take the middle of the range + forEach$1(sortedElements, function(element) { + var center = element[axis] + Math.round(element[dimension] / 2); + + if (centers[center]) { + centers[center].elements.push(element); + } else { + centers[center] = { + elements: [ element ], + center: center + }; + } + }); + + centeredElements = sortBy(centers, function(center) { + if (center.elements.length > 1) { + hasSharedCenters = true; + } + + return center.elements.length; + }); + + if (hasSharedCenters) { + alignment[type] = last(centeredElements).center; + + return alignment; + } + + firstElement = sortedElements[0]; + + sortedElements = sortBy(sortedElements, function(element) { + return element[axis] + element[dimension]; + }); + + lastElement = last(sortedElements); + + alignment[type] = getMiddleOrTop(firstElement, lastElement); + } + + return alignment; + }; + + /** + * Executes the alignment of a selection of elements + * + * @param {Array} elements + * @param {string} type left|right|center|top|bottom|middle + */ + AlignElements$1.prototype.trigger = function(elements, type) { + var modeling = this._modeling, + allowed; + + // filter out elements which cannot be aligned + var filteredElements = filter(elements, function(element) { + return !(element.waypoints || element.host || element.labelTarget); + }); + + // filter out elements via rules + allowed = this._rules.allowed('elements.align', { elements: filteredElements }); + if (isArray$3(allowed)) { + filteredElements = allowed; + } + + if (filteredElements.length < 2 || !allowed) { + return; + } + + var sortFn = ALIGNMENT_SORTING[type]; + + var sortedElements = sortBy(filteredElements, sortFn); + + var alignment = this._alignmentPosition(type, sortedElements); + + modeling.alignElements(sortedElements, alignment); + }; + + var AlignElementsModule$1 = { + __init__: [ 'alignElements' ], + alignElements: [ 'type', AlignElements$1 ] + }; + + var entrySelector = '.entry'; + + var DEFAULT_PRIORITY$2 = 1000; + var CONTEXT_PAD_PADDING = 12; + + + /** + * @typedef {djs.model.Base|djs.model.Base[]} ContextPadTarget + */ + + /** + * A context pad that displays element specific, contextual actions next + * to a diagram element. + * + * @param {Canvas} canvas + * @param {Object} config + * @param {boolean|Object} [config.scale={ min: 1.0, max: 1.5 }] + * @param {number} [config.scale.min] + * @param {number} [config.scale.max] + * @param {EventBus} eventBus + * @param {Overlays} overlays + */ + function ContextPad(canvas, config, eventBus, overlays) { + + this._canvas = canvas; + this._eventBus = eventBus; + this._overlays = overlays; + + var scale = isDefined(config && config.scale) ? config.scale : { + min: 1, + max: 1.5 + }; + + this._overlaysConfig = { + scale: scale + }; + + this._current = null; + + this._init(); + } + + ContextPad.$inject = [ + 'canvas', + 'config.contextPad', + 'eventBus', + 'overlays' + ]; + + + /** + * Registers events needed for interaction with other components. + */ + ContextPad.prototype._init = function() { + var self = this; + + this._eventBus.on('selection.changed', function(event) { + + var selection = event.newSelection; + + var target = selection.length + ? selection.length === 1 + ? selection[0] + : selection + : null; + + if (target) { + self.open(target, true); + } else { + self.close(); + } + }); + + this._eventBus.on('elements.changed', function(event) { + var elements = event.elements, + current = self._current; + + if (!current) { + return; + } + + var currentTarget = current.target; + + var currentChanged = some( + isArray$3(currentTarget) ? currentTarget : [ currentTarget ], + function(element) { + return includes$8(elements, element); + } + ); + + // re-open if elements in current selection changed + if (currentChanged) { + self.open(currentTarget, true); + } + }); + }; + + + /** + * Register context pad provider. + * + * @param {number} [priority=1000] + * @param {ContextPadProvider} provider + * + * @example + * const contextPadProvider = { + * getContextPadEntries: function(element) { + * return function(entries) { + * return { + * ...entries, + * 'entry-1': { + * label: 'My Entry', + * action: function() { alert("I have been clicked!"); } + * } + * }; + * } + * }, + * + * getMultiElementContextPadEntries: function(elements) { + * // ... + * } + * }; + * + * contextPad.registerProvider(800, contextPadProvider); + */ + ContextPad.prototype.registerProvider = function(priority, provider) { + if (!provider) { + provider = priority; + priority = DEFAULT_PRIORITY$2; + } + + this._eventBus.on('contextPad.getProviders', priority, function(event) { + event.providers.push(provider); + }); + }; + + + /** + * Get context pad entries for given elements. + * + * @param {ContextPadTarget} target + * + * @return {ContextPadEntryDescriptor[]} list of entries + */ + ContextPad.prototype.getEntries = function(target) { + var providers = this._getProviders(); + + var provideFn = isArray$3(target) + ? 'getMultiElementContextPadEntries' + : 'getContextPadEntries'; + + var entries = {}; + + // loop through all providers and their entries. + // group entries by id so that overriding an entry is possible + forEach$1(providers, function(provider) { + + if (!isFunction(provider[provideFn])) { + return; + } + + var entriesOrUpdater = provider[provideFn](target); + + if (isFunction(entriesOrUpdater)) { + entries = entriesOrUpdater(entries); + } else { + forEach$1(entriesOrUpdater, function(entry, id) { + entries[id] = entry; + }); + } + }); + + return entries; + }; + + + /** + * Trigger context pad action. + * + * @param {string} action + * @param {Event} event + * @param {boolean} [autoActivate=false] + */ + ContextPad.prototype.trigger = function(action, event, autoActivate) { + + var target = this._current.target, + entries = this._current.entries, + entry, + handler, + originalEvent, + button = event.delegateTarget || event.target; + + if (!button) { + return event.preventDefault(); + } + + entry = entries[attr$1(button, 'data-action')]; + handler = entry.action; + + originalEvent = event.originalEvent || event; + + // simple action (via callback function) + if (isFunction(handler)) { + if (action === 'click') { + return handler(originalEvent, target, autoActivate); + } + } else { + if (handler[action]) { + return handler[action](originalEvent, target, autoActivate); + } + } + + // silence other actions + event.preventDefault(); + }; + + + /** + * Open the context pad for given elements. + * + * @param {ContextPadTarget} target + * @param {boolean} [force=false] - Force re-opening context pad. + */ + ContextPad.prototype.open = function(target, force) { + if (!force && this.isOpen(target)) { + return; + } + + this.close(); + + this._updateAndOpen(target); + }; + + ContextPad.prototype._getProviders = function() { + + var event = this._eventBus.createEvent({ + type: 'contextPad.getProviders', + providers: [] + }); + + this._eventBus.fire(event); + + return event.providers; + }; + + + /** + * @param {ContextPadTarget} target + */ + ContextPad.prototype._updateAndOpen = function(target) { + var entries = this.getEntries(target), + pad = this.getPad(target), + html = pad.html, + image; + + forEach$1(entries, function(entry, id) { + var grouping = entry.group || 'default', + control = domify(entry.html || '
    '), + container; + + attr$1(control, 'data-action', id); + + container = query('[data-group=' + cssEscape(grouping) + ']', html); + if (!container) { + container = domify('
    '); + attr$1(container, 'data-group', grouping); + + html.appendChild(container); + } + + container.appendChild(control); + + if (entry.className) { + addClasses$1(control, entry.className); + } + + if (entry.title) { + attr$1(control, 'title', entry.title); + } + + if (entry.imageUrl) { + image = domify(''); + attr$1(image, 'src', entry.imageUrl); + image.style.width = '100%'; + image.style.height = '100%'; + + control.appendChild(image); + } + }); + + classes$1(html).add('open'); + + this._current = { + target: target, + entries: entries, + pad: pad + }; + + this._eventBus.fire('contextPad.open', { current: this._current }); + }; + + /** + * @param {ContextPadTarget} target + * + * @return {Overlay} + */ + ContextPad.prototype.getPad = function(target) { + if (this.isOpen()) { + return this._current.pad; + } + + var self = this; + + var overlays = this._overlays; + + var html = domify('
    '); + + var position = this._getPosition(target); + + var overlaysConfig = assign({ + html: html + }, this._overlaysConfig, position); + + delegate.bind(html, entrySelector, 'click', function(event) { + self.trigger('click', event); + }); + + delegate.bind(html, entrySelector, 'dragstart', function(event) { + self.trigger('dragstart', event); + }); + + // stop propagation of mouse events + componentEvent.bind(html, 'mousedown', function(event) { + event.stopPropagation(); + }); + + var activeRootElement = this._canvas.getRootElement(); + + this._overlayId = overlays.add(activeRootElement, 'context-pad', overlaysConfig); + + var pad = overlays.get(this._overlayId); + + this._eventBus.fire('contextPad.create', { + target: target, + pad: pad + }); + + return pad; + }; + + + /** + * Close the context pad + */ + ContextPad.prototype.close = function() { + if (!this.isOpen()) { + return; + } + + this._overlays.remove(this._overlayId); + + this._overlayId = null; + + this._eventBus.fire('contextPad.close', { current: this._current }); + + this._current = null; + }; + + /** + * Check if pad is open. + * + * If target is provided, check if it is opened + * for the given target (single or multiple elements). + * + * @param {ContextPadTarget} [target] + * @return {boolean} + */ + ContextPad.prototype.isOpen = function(target) { + var current = this._current; + + if (!current) { + return false; + } + + // basic no-args is open check + if (!target) { + return true; + } + + var currentTarget = current.target; + + // strict handling of single vs. multi-selection + if (isArray$3(target) !== isArray$3(currentTarget)) { + return false; + } + + if (isArray$3(target)) { + return ( + target.length === currentTarget.length && + every(target, function(element) { + return includes$8(currentTarget, element); + }) + ); + } else { + return currentTarget === target; + } + }; + + + /** + * Get contex pad position. + * + * @param {ContextPadTarget} target + * @return {Bounds} + */ + ContextPad.prototype._getPosition = function(target) { + + var elements = isArray$3(target) ? target : [ target ]; + var bBox = getBBox(elements); + + return { + position: { + left: bBox.x + bBox.width + CONTEXT_PAD_PADDING, + top: bBox.y - CONTEXT_PAD_PADDING / 2 + } + }; + }; + + + // helpers ////////// + + function addClasses$1(element, classNames) { + var classes = classes$1(element); + + classNames = isArray$3(classNames) ? classNames : classNames.split(/\s+/g); + + classNames.forEach(function(cls) { + classes.add(cls); + }); + } + + /** + * @param {any[]} array + * @param {any} item + * + * @return {boolean} + */ + function includes$8(array, item) { + return array.indexOf(item) !== -1; + } + + var ContextPadModule$1 = { + __depends__: [ + InteractionEventsModule$1, + OverlaysModule + ], + contextPad: [ 'type', ContextPad ] + }; + + var DATA_REF = 'data-id'; + + var CLOSE_EVENTS = [ + 'contextPad.close', + 'canvas.viewbox.changing', + 'commandStack.changed' + ]; + + var DEFAULT_PRIORITY$1 = 1000; + + + /** + * A popup menu that can be used to display a list of actions anywhere in the canvas. + * + * @param {Object} config + * @param {boolean|Object} [config.scale={ min: 1.0, max: 1.5 }] + * @param {number} [config.scale.min] + * @param {number} [config.scale.max] + * @param {EventBus} eventBus + * @param {Canvas} canvas + * + * @class + * @constructor + */ + function PopupMenu(config, eventBus, canvas) { + + var scale = isDefined(config && config.scale) ? config.scale : { + min: 1, + max: 1.5 + }; + + this._config = { + scale: scale + }; + + this._eventBus = eventBus; + this._canvas = canvas; + this._providers = {}; + this._current = {}; + } + + PopupMenu.$inject = [ + 'config.popupMenu', + 'eventBus', + 'canvas' + ]; + + /** + * Registers a popup menu provider + * + * @param {string} id + * @param {number} [priority=1000] + * @param {Object} provider + * + * @example + * const popupMenuProvider = { + * getPopupMenuEntries: function(element) { + * return { + * 'entry-1': { + * label: 'My Entry', + * action: function() { alert("I have been clicked!"); } + * } + * } + * } + * }; + * + * popupMenu.registerProvider('myMenuID', popupMenuProvider); + */ + PopupMenu.prototype.registerProvider = function(id, priority, provider) { + if (!provider) { + provider = priority; + priority = DEFAULT_PRIORITY$1; + } + + this._eventBus.on('popupMenu.getProviders.' + id, priority, function(event) { + event.providers.push(provider); + }); + }; + + /** + * Determine if the popup menu has entries. + * + * @return {boolean} true if empty + */ + PopupMenu.prototype.isEmpty = function(element, providerId) { + if (!element) { + throw new Error('element parameter is missing'); + } + + if (!providerId) { + throw new Error('providerId parameter is missing'); + } + + var providers = this._getProviders(providerId); + + if (!providers) { + return true; + } + + var entries = this._getEntries(element, providers), + headerEntries = this._getHeaderEntries(element, providers); + + var hasEntries = size(entries) > 0, + hasHeaderEntries = headerEntries && size(headerEntries) > 0; + + return !hasEntries && !hasHeaderEntries; + }; + + + /** + * Create entries and open popup menu at given position + * + * @param {Object} element + * @param {string} id provider id + * @param {Object} position + * + * @return {Object} popup menu instance + */ + PopupMenu.prototype.open = function(element, id, position) { + + var providers = this._getProviders(id); + + if (!element) { + throw new Error('Element is missing'); + } + + if (!providers || !providers.length) { + throw new Error('No registered providers for: ' + id); + } + + if (!position) { + throw new Error('the position argument is missing'); + } + + if (this.isOpen()) { + this.close(); + } + + this._emit('open'); + + var current = this._current = { + className: id, + element: element, + position: position + }; + + var entries = this._getEntries(element, providers), + headerEntries = this._getHeaderEntries(element, providers); + + current.entries = assign({}, entries, headerEntries); + + current.container = this._createContainer(id); + + if (size(headerEntries)) { + current.container.appendChild( + this._createEntries(headerEntries, 'djs-popup-header') + ); + } + + if (size(entries)) { + current.container.appendChild( + this._createEntries(entries, 'djs-popup-body') + ); + } + + var canvas = this._canvas, + parent = canvas.getContainer(); + + this._attachContainer(current.container, parent, position.cursor); + this._bindAutoClose(); + }; + + + /** + * Removes the popup menu and unbinds the event handlers. + */ + PopupMenu.prototype.close = function() { + + if (!this.isOpen()) { + return; + } + + this._emit('close'); + + this._unbindAutoClose(); + remove$2(this._current.container); + this._current.container = null; + }; + + + /** + * Determine if an open popup menu exist. + * + * @return {boolean} true if open + */ + PopupMenu.prototype.isOpen = function() { + return !!this._current.container; + }; + + + /** + * Trigger an action associated with an entry. + * + * @param {Object} event + * + * @return the result of the action callback, if any + */ + PopupMenu.prototype.trigger = function(event) { + + // silence other actions + event.preventDefault(); + + var element = event.delegateTarget || event.target, + entryId = attr$1(element, DATA_REF); + + var entry = this._getEntry(entryId); + + if (entry.action) { + return entry.action.call(null, event, entry); + } + }; + + PopupMenu.prototype._getProviders = function(id) { + + var event = this._eventBus.createEvent({ + type: 'popupMenu.getProviders.' + id, + providers: [] + }); + + this._eventBus.fire(event); + + return event.providers; + }; + + PopupMenu.prototype._getEntries = function(element, providers) { + + var entries = {}; + + forEach$1(providers, function(provider) { + + // handle legacy method + if (!provider.getPopupMenuEntries) { + forEach$1(provider.getEntries(element), function(entry) { + var id = entry.id; + + if (!id) { + throw new Error('every entry must have the id property set'); + } + + entries[id] = omit(entry, [ 'id' ]); + }); + + return; + } + + var entriesOrUpdater = provider.getPopupMenuEntries(element); + + if (isFunction(entriesOrUpdater)) { + entries = entriesOrUpdater(entries); + } else { + forEach$1(entriesOrUpdater, function(entry, id) { + entries[id] = entry; + }); + } + }); + + return entries; + }; + + PopupMenu.prototype._getHeaderEntries = function(element, providers) { + + var entries = {}; + + forEach$1(providers, function(provider) { + + // handle legacy method + if (!provider.getPopupMenuHeaderEntries) { + if (!provider.getHeaderEntries) { + return; + } + + forEach$1(provider.getHeaderEntries(element), function(entry) { + var id = entry.id; + + if (!id) { + throw new Error('every entry must have the id property set'); + } + + entries[id] = omit(entry, [ 'id' ]); + }); + + return; + } + + var entriesOrUpdater = provider.getPopupMenuHeaderEntries(element); + + if (isFunction(entriesOrUpdater)) { + entries = entriesOrUpdater(entries); + } else { + forEach$1(entriesOrUpdater, function(entry, id) { + entries[id] = entry; + }); + } + }); + + return entries; + + + }; + + /** + * Gets an entry instance (either entry or headerEntry) by id. + * + * @param {string} entryId + * + * @return {Object} entry instance + */ + PopupMenu.prototype._getEntry = function(entryId) { + + var entry = this._current.entries[entryId]; + + if (!entry) { + throw new Error('entry not found'); + } + + return entry; + }; + + PopupMenu.prototype._emit = function(eventName) { + this._eventBus.fire('popupMenu.' + eventName); + }; + + /** + * Creates the popup menu container. + * + * @return {Object} a DOM container + */ + PopupMenu.prototype._createContainer = function(id) { + var container = domify('
    '), + position = this._current.position, + className = this._current.className; + + assign$1(container, { + position: 'absolute', + left: position.x + 'px', + top: position.y + 'px', + visibility: 'hidden' + }); + + classes$1(container).add(className); + + attr$1(container, 'data-popup', id); + + return container; + }; + + + /** + * Attaches the container to the DOM. + * + * @param {Object} container + * @param {Object} parent + */ + PopupMenu.prototype._attachContainer = function(container, parent, cursor) { + var self = this; + + // Event handler + delegate.bind(container, '.entry' ,'click', function(event) { + self.trigger(event); + }); + + this._updateScale(container); + + // Attach to DOM + parent.appendChild(container); + + if (cursor) { + this._assureIsInbounds(container, cursor); + } + + // display after position adjustment to avoid flickering + assign$1(container, { visibility: 'visible' }); + }; + + + /** + * Updates popup style.transform with respect to the config and zoom level. + * + * @method _updateScale + * + * @param {Object} container + */ + PopupMenu.prototype._updateScale = function(container) { + var zoom = this._canvas.zoom(); + + var scaleConfig = this._config.scale, + minScale, + maxScale, + scale = zoom; + + if (scaleConfig !== true) { + + if (scaleConfig === false) { + minScale = 1; + maxScale = 1; + } else { + minScale = scaleConfig.min; + maxScale = scaleConfig.max; + } + + if (isDefined(minScale) && zoom < minScale) { + scale = minScale; + } + + if (isDefined(maxScale) && zoom > maxScale) { + scale = maxScale; + } + + } + + setTransform(container, 'scale(' + scale + ')'); + }; + + + /** + * Make sure that the menu is always fully shown + * + * @method function + * + * @param {Object} container + * @param {Position} cursor {x, y} + */ + PopupMenu.prototype._assureIsInbounds = function(container, cursor) { + var canvas = this._canvas, + clientRect = canvas._container.getBoundingClientRect(); + + var containerX = container.offsetLeft, + containerY = container.offsetTop, + containerWidth = container.scrollWidth, + containerHeight = container.scrollHeight, + overAxis = {}, + left, top; + + var cursorPosition = { + x: cursor.x - clientRect.left, + y: cursor.y - clientRect.top + }; + + if (containerX + containerWidth > clientRect.width) { + overAxis.x = true; + } + + if (containerY + containerHeight > clientRect.height) { + overAxis.y = true; + } + + if (overAxis.x && overAxis.y) { + left = cursorPosition.x - containerWidth + 'px'; + top = cursorPosition.y - containerHeight + 'px'; + } else if (overAxis.x) { + left = cursorPosition.x - containerWidth + 'px'; + top = cursorPosition.y + 'px'; + } else if (overAxis.y && cursorPosition.y < containerHeight) { + left = cursorPosition.x + 'px'; + top = 10 + 'px'; + } else if (overAxis.y) { + left = cursorPosition.x + 'px'; + top = cursorPosition.y - containerHeight + 'px'; + } + + assign$1(container, { left: left, top: top }, { 'zIndex': 1000 }); + }; + + + /** + * Creates a list of entries and returns them as a DOM container. + * + * @param {Array} entries an array of entry objects + * @param {string} className the class name of the entry container + * + * @return {Object} a DOM container + */ + PopupMenu.prototype._createEntries = function(entries, className) { + + var entriesContainer = domify('
    '), + self = this; + + classes$1(entriesContainer).add(className); + + forEach$1(entries, function(entry, id) { + var entryContainer = self._createEntry(entry, id), + grouping = entry.group || 'default', + groupContainer = query('[data-group=' + cssEscape(grouping) + ']', entriesContainer); + + if (!groupContainer) { + groupContainer = domify('
    '); + attr$1(groupContainer, 'data-group', grouping); + + entriesContainer.appendChild(groupContainer); + } + + groupContainer.appendChild(entryContainer); + }); + + return entriesContainer; + }; + + + /** + * Creates a single entry and returns it as a DOM container. + * + * @param {Object} entry + * + * @return {Object} a DOM container + */ + PopupMenu.prototype._createEntry = function(entry, id) { + + var entryContainer = domify('
    '), + entryClasses = classes$1(entryContainer); + + entryClasses.add('entry'); + + if (entry.className) { + entry.className.split(' ').forEach(function(className) { + entryClasses.add(className); + }); + } + + attr$1(entryContainer, DATA_REF, id); + + if (entry.label) { + var label = domify(''); + label.textContent = entry.label; + entryContainer.appendChild(label); + } + + if (entry.imageUrl) { + var image = domify(''); + attr$1(image, 'src', entry.imageUrl); + + entryContainer.appendChild(image); + } + + if (entry.active === true) { + entryClasses.add('active'); + } + + if (entry.disabled === true) { + entryClasses.add('disabled'); + } + + if (entry.title) { + entryContainer.title = entry.title; + } + + return entryContainer; + }; + + + /** + * Set up listener to close popup automatically on certain events. + */ + PopupMenu.prototype._bindAutoClose = function() { + this._eventBus.once(CLOSE_EVENTS, this.close, this); + }; + + + /** + * Remove the auto-closing listener. + */ + PopupMenu.prototype._unbindAutoClose = function() { + this._eventBus.off(CLOSE_EVENTS, this.close, this); + }; + + + + // helpers ///////////////////////////// + + function setTransform(element, transform) { + element.style['transform-origin'] = 'top left'; + + [ '', '-ms-', '-webkit-' ].forEach(function(prefix) { + element.style[prefix + 'transform'] = transform; + }); + } + + var PopupMenuModule$1 = { + __init__: [ 'popupMenu' ], + popupMenu: [ 'type', PopupMenu ] + }; + + /** + * To change the icons, modify the SVGs in `./resources`, execute `npx svgo -f resources --datauri enc -o dist`, + * and then replace respective icons with the optimized data URIs in `./dist`. + */ + var icons$1 = { + align: 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%202000%202000%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M200%20150v1700%22%2F%3E%3Crect%20x%3D%22500%22%20y%3D%22150%22%20width%3D%221300%22%20height%3D%22700%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%22500%22%20y%3D%221150%22%20width%3D%22700%22%20height%3D%22700%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E', + bottom: 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M150%201650h1500%22%2F%3E%3Crect%20x%3D%22150%22%20y%3D%22350%22%20width%3D%22600%22%20height%3D%221300%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%221050%22%20y%3D%22850%22%20width%3D%22600%22%20height%3D%22800%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E', + center: 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M900%20150v1500%22%2F%3E%3Crect%20x%3D%22250%22%20y%3D%22150%22%20width%3D%221300%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%22500%22%20y%3D%221050%22%20width%3D%22800%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E', + left: 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M100%20150v1500%22%2F%3E%3Crect%20x%3D%22100%22%20y%3D%22150%22%20width%3D%221300%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%22100%22%20y%3D%221050%22%20width%3D%22800%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E', + right: 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M1650%20150v1500%22%2F%3E%3Crect%20x%3D%22350%22%20y%3D%22150%22%20width%3D%221300%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%22850%22%20y%3D%221050%22%20width%3D%22800%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E', + top: 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M150%20150h1500%22%2F%3E%3Crect%20x%3D%22150%22%20y%3D%22150%22%20width%3D%22600%22%20height%3D%221300%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%221050%22%20y%3D%22150%22%20width%3D%22600%22%20height%3D%22800%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E', + middle: 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M150%20900h1500%22%2F%3E%3Crect%20x%3D%22150%22%20y%3D%22250%22%20width%3D%22600%22%20height%3D%221300%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%221050%22%20y%3D%22500%22%20width%3D%22600%22%20height%3D%22800%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E' + }; + + var LOW_PRIORITY$l = 900; + + /** + * A provider for align elements context pad button + */ + function AlignElementsContextPadProvider(contextPad, popupMenu, translate, canvas) { + + contextPad.registerProvider(LOW_PRIORITY$l, this); + + this._contextPad = contextPad; + this._popupMenu = popupMenu; + this._translate = translate; + this._canvas = canvas; + } + + AlignElementsContextPadProvider.$inject = [ + 'contextPad', + 'popupMenu', + 'translate', + 'canvas' + ]; + + AlignElementsContextPadProvider.prototype.getMultiElementContextPadEntries = function(elements) { + var actions = {}; + + if (this._isAllowed(elements)) { + assign(actions, this._getEntries(elements)); + } + + return actions; + }; + + AlignElementsContextPadProvider.prototype._isAllowed = function(elements) { + return !this._popupMenu.isEmpty(elements, 'align-elements'); + }; + + AlignElementsContextPadProvider.prototype._getEntries = function(elements) { + var self = this; + + return { + 'align-elements': { + group: 'align-elements', + title: self._translate('Align elements'), + imageUrl: icons$1['align'], + action: { + click: function(event, elements) { + var position = self._getMenuPosition(elements); + + assign(position, { + cursor: { + x: event.x, + y: event.y + } + }); + + self._popupMenu.open(elements, 'align-elements', position); + } + } + } + }; + }; + + AlignElementsContextPadProvider.prototype._getMenuPosition = function(elements) { + var Y_OFFSET = 5; + + var diagramContainer = this._canvas.getContainer(), + pad = this._contextPad.getPad(elements).html; + + var diagramRect = diagramContainer.getBoundingClientRect(), + padRect = pad.getBoundingClientRect(); + + var top = padRect.top - diagramRect.top; + var left = padRect.left - diagramRect.left; + + var pos = { + x: left, + y: top + padRect.height + Y_OFFSET + }; + + return pos; + }; + + var ALIGNMENT_OPTIONS = [ + 'left', + 'center', + 'right', + 'top', + 'middle', + 'bottom' + ]; + + /** + * A provider for align elements popup menu. + */ + function AlignElementsMenuProvider(popupMenu, alignElements, translate, rules) { + + this._alignElements = alignElements; + this._translate = translate; + this._popupMenu = popupMenu; + this._rules = rules; + + popupMenu.registerProvider('align-elements', this); + } + + AlignElementsMenuProvider.$inject = [ + 'popupMenu', + 'alignElements', + 'translate', + 'rules' + ]; + + AlignElementsMenuProvider.prototype.getPopupMenuEntries = function(elements) { + var entries = {}; + + if (this._isAllowed(elements)) { + assign(entries, this._getEntries(elements)); + } + + return entries; + }; + + AlignElementsMenuProvider.prototype._isAllowed = function(elements) { + return this._rules.allowed('elements.align', { elements: elements }); + }; + + AlignElementsMenuProvider.prototype._getEntries = function(elements) { + var alignElements = this._alignElements, + translate = this._translate, + popupMenu = this._popupMenu; + + var entries = {}; + + forEach$1(ALIGNMENT_OPTIONS, function(alignment) { + entries[ 'align-elements-' + alignment ] = { + group: 'align', + title: translate('Align elements ' + alignment), + className: 'bjs-align-elements-menu-entry', + imageUrl: icons$1[alignment], + action: function(event, entry) { + alignElements.trigger(elements, alignment); + popupMenu.close(); + } + }; + }); + + return entries; + }; + + /** + * A basic provider that may be extended to implement modeling rules. + * + * Extensions should implement the init method to actually add their custom + * modeling checks. Checks may be added via the #addRule(action, fn) method. + * + * @param {EventBus} eventBus + */ + function RuleProvider(eventBus) { + CommandInterceptor.call(this, eventBus); + + this.init(); + } + + RuleProvider.$inject = [ 'eventBus' ]; + + e(RuleProvider, CommandInterceptor); + + + /** + * Adds a modeling rule for the given action, implemented through + * a callback function. + * + * The function will receive the modeling specific action context + * to perform its check. It must return `false` to disallow the + * action from happening or `true` to allow the action. + * + * A rule provider may pass over the evaluation to lower priority + * rules by returning return nothing (or undefined). + * + * @example + * + * ResizableRules.prototype.init = function() { + * + * \/** + * * Return `true`, `false` or nothing to denote + * * _allowed_, _not allowed_ and _continue evaluating_. + * *\/ + * this.addRule('shape.resize', function(context) { + * + * var shape = context.shape; + * + * if (!context.newBounds) { + * // check general resizability + * if (!shape.resizable) { + * return false; + * } + * + * // not returning anything (read: undefined) + * // will continue the evaluation of other rules + * // (with lower priority) + * return; + * } else { + * // element must have minimum size of 10*10 points + * return context.newBounds.width > 10 && context.newBounds.height > 10; + * } + * }); + * }; + * + * @param {string|Array} actions the identifier for the modeling action to check + * @param {number} [priority] the priority at which this rule is being applied + * @param {Function} fn the callback function that performs the actual check + */ + RuleProvider.prototype.addRule = function(actions, priority, fn) { + + var self = this; + + if (typeof actions === 'string') { + actions = [ actions ]; + } + + actions.forEach(function(action) { + + self.canExecute(action, priority, function(context, action, event) { + return fn(context); + }, true); + }); + }; + + /** + * Implement this method to add new rules during provider initialization. + */ + RuleProvider.prototype.init = function() {}; + + /** + * Rule provider for alignment of BPMN elements. + */ + function BpmnAlignElements(eventBus) { + RuleProvider.call(this, eventBus); + } + + BpmnAlignElements.$inject = [ 'eventBus' ]; + + e(BpmnAlignElements, RuleProvider); + + BpmnAlignElements.prototype.init = function() { + this.addRule('elements.align', function(context) { + var elements = context.elements; + + // filter out elements which cannot be aligned + var filteredElements = filter(elements, function(element) { + return !(element.waypoints || element.host || element.labelTarget); + }); + + // filter out elements which are children of any of the selected elements + filteredElements = getParents$1(filteredElements); + + if (filteredElements.length < 2) { + return false; + } + + return filteredElements; + }); + }; + + var AlignElementsModule = { + __depends__: [ + AlignElementsModule$1, + ContextPadModule$1, + PopupMenuModule$1 + ], + __init__: [ + 'alignElementsContextPadProvider', + 'alignElementsMenuProvider', + 'bpmnAlignElements' + ], + alignElementsContextPadProvider: [ 'type', AlignElementsContextPadProvider ], + alignElementsMenuProvider: [ 'type', AlignElementsMenuProvider ], + bpmnAlignElements: [ 'type', BpmnAlignElements ] + }; + + // padding to detect element placement + var PLACEMENT_DETECTION_PAD = 10; + + var DEFAULT_DISTANCE = 50; + + var DEFAULT_MAX_DISTANCE = 250; + + + /** + * Get free position starting from given position. + * + * @param {djs.model.Shape} source + * @param {djs.model.Shape} element + * @param {Point} position + * @param {Function} getNextPosition + * + * @return {Point} + */ + function findFreePosition(source, element, position, getNextPosition) { + var connectedAtPosition; + + while ((connectedAtPosition = getConnectedAtPosition(source, position, element))) { + position = getNextPosition(element, position, connectedAtPosition); + } + + return position; + } + + /** + * Returns function that returns next position. + * + * @param {Object} nextPositionDirection + * @param {Object} [nextPositionDirection.x] + * @param {Object} [nextPositionDirection.y] + * + * @returns {Function} + */ + function generateGetNextPosition(nextPositionDirection) { + return function(element, previousPosition, connectedAtPosition) { + var nextPosition = { + x: previousPosition.x, + y: previousPosition.y + }; + + [ 'x', 'y' ].forEach(function(axis) { + + var nextPositionDirectionForAxis = nextPositionDirection[ axis ]; + + if (!nextPositionDirectionForAxis) { + return; + } + + var dimension = axis === 'x' ? 'width' : 'height'; + + var margin = nextPositionDirectionForAxis.margin, + minDistance = nextPositionDirectionForAxis.minDistance; + + if (margin < 0) { + nextPosition[ axis ] = Math.min( + connectedAtPosition[ axis ] + margin - element[ dimension ] / 2, + previousPosition[ axis ] - minDistance + margin + ); + } else { + nextPosition[ axis ] = Math.max( + connectedAtPosition[ axis ] + connectedAtPosition[ dimension ] + margin + element[ dimension ] / 2, + previousPosition[ axis ] + minDistance + margin + ); + } + }); + + return nextPosition; + }; + } + + /** + * Return target at given position, if defined. + * + * This takes connected elements from host and attachers + * into account, too. + */ + function getConnectedAtPosition(source, position, element) { + + var bounds = { + x: position.x - (element.width / 2), + y: position.y - (element.height / 2), + width: element.width, + height: element.height + }; + + var closure = getAutoPlaceClosure(source); + + return find(closure, function(target) { + + if (target === element) { + return false; + } + + var orientation = getOrientation(target, bounds, PLACEMENT_DETECTION_PAD); + + return orientation === 'intersect'; + }); + } + + /** + * Compute optimal distance between source and target based on existing connections to and from source. + * Assumes left-to-right and top-to-down modeling. + * + * @param {djs.model.Shape} source + * @param {Object} [hints] + * @param {number} [hints.defaultDistance] + * @param {string} [hints.direction] + * @param {Function} [hints.filter] + * @param {Function} [hints.getWeight] + * @param {number} [hints.maxDistance] + * @param {string} [hints.reference] + * + * @return {number} + */ + function getConnectedDistance(source, hints) { + if (!hints) { + hints = {}; + } + + // targets > sources by default + function getDefaultWeight(connection) { + return connection.source === source ? 1 : -1; + } + + var defaultDistance = hints.defaultDistance || DEFAULT_DISTANCE, + direction = hints.direction || 'e', + filter = hints.filter, + getWeight = hints.getWeight || getDefaultWeight, + maxDistance = hints.maxDistance || DEFAULT_MAX_DISTANCE, + reference = hints.reference || 'start'; + + if (!filter) { + filter = noneFilter; + } + + function getDistance(a, b) { + if (direction === 'n') { + if (reference === 'start') { + return asTRBL(a).top - asTRBL(b).bottom; + } else if (reference === 'center') { + return asTRBL(a).top - getMid(b).y; + } else { + return asTRBL(a).top - asTRBL(b).top; + } + } else if (direction === 'w') { + if (reference === 'start') { + return asTRBL(a).left - asTRBL(b).right; + } else if (reference === 'center') { + return asTRBL(a).left - getMid(b).x; + } else { + return asTRBL(a).left - asTRBL(b).left; + } + } else if (direction === 's') { + if (reference === 'start') { + return asTRBL(b).top - asTRBL(a).bottom; + } else if (reference === 'center') { + return getMid(b).y - asTRBL(a).bottom; + } else { + return asTRBL(b).bottom - asTRBL(a).bottom; + } + } else { + if (reference === 'start') { + return asTRBL(b).left - asTRBL(a).right; + } else if (reference === 'center') { + return getMid(b).x - asTRBL(a).right; + } else { + return asTRBL(b).right - asTRBL(a).right; + } + } + } + + var sourcesDistances = source.incoming + .filter(filter) + .map(function(connection) { + var weight = getWeight(connection); + + var distance = weight < 0 + ? getDistance(connection.source, source) + : getDistance(source, connection.source); + + return { + id: connection.source.id, + distance: distance, + weight: weight + }; + }); + + var targetsDistances = source.outgoing + .filter(filter) + .map(function(connection) { + var weight = getWeight(connection); + + var distance = weight > 0 + ? getDistance(source, connection.target) + : getDistance(connection.target, source); + + return { + id: connection.target.id, + distance: distance, + weight: weight + }; + }); + + var distances = sourcesDistances.concat(targetsDistances).reduce(function(accumulator, currentValue) { + accumulator[ currentValue.id + '__weight_' + currentValue.weight ] = currentValue; + + return accumulator; + }, {}); + + var distancesGrouped = reduce(distances, function(accumulator, currentValue) { + var distance = currentValue.distance, + weight = currentValue.weight; + + if (distance < 0 || distance > maxDistance) { + return accumulator; + } + + if (!accumulator[ String(distance) ]) { + accumulator[ String(distance) ] = 0; + } + + accumulator[ String(distance) ] += 1 * weight; + + if (!accumulator.distance || accumulator[ accumulator.distance ] < accumulator[ String(distance) ]) { + accumulator.distance = distance; + } + + return accumulator; + }, {}); + + return distancesGrouped.distance || defaultDistance; + } + + /** + * Returns all connected elements around the given source. + * + * This includes: + * + * - connected elements + * - host connected elements + * - attachers connected elements + * + * @param {djs.model.Shape} source + * + * @return {Array} + */ + function getAutoPlaceClosure(source) { + + var allConnected = getConnected(source); + + if (source.host) { + allConnected = allConnected.concat(getConnected(source.host)); + } + + if (source.attachers) { + allConnected = allConnected.concat(source.attachers.reduce(function(shapes, attacher) { + return shapes.concat(getConnected(attacher)); + }, [])); + } + + return allConnected; + } + + function getConnected(element) { + return getTargets(element).concat(getSources(element)); + } + + function getSources(shape) { + return shape.incoming.map(function(connection) { + return connection.source; + }); + } + + function getTargets(shape) { + return shape.outgoing.map(function(connection) { + return connection.target; + }); + } + + function noneFilter() { + return true; + } + + var LOW_PRIORITY$k = 100; + + + /** + * A service that places elements connected to existing ones + * to an appropriate position in an _automated_ fashion. + * + * @param {EventBus} eventBus + * @param {Modeling} modeling + */ + function AutoPlace$1(eventBus, modeling, canvas) { + + eventBus.on('autoPlace', LOW_PRIORITY$k, function(context) { + var shape = context.shape, + source = context.source; + + return getNewShapePosition$1(source, shape); + }); + + eventBus.on('autoPlace.end', function(event) { + canvas.scrollToElement(event.shape); + }); + + /** + * Append shape to source at appropriate position. + * + * @param {djs.model.Shape} source + * @param {djs.model.Shape} shape + * + * @return {djs.model.Shape} appended shape + */ + this.append = function(source, shape, hints) { + + eventBus.fire('autoPlace.start', { + source: source, + shape: shape + }); + + // allow others to provide the position + var position = eventBus.fire('autoPlace', { + source: source, + shape: shape + }); + + var newShape = modeling.appendShape(source, shape, position, source.parent, hints); + + eventBus.fire('autoPlace.end', { + source: source, + shape: newShape + }); + + return newShape; + }; + + } + + AutoPlace$1.$inject = [ + 'eventBus', + 'modeling', + 'canvas' + ]; + + // helpers ////////// + + /** + * Find the new position for the target element to + * connect to source. + * + * @param {djs.model.Shape} source + * @param {djs.model.Shape} element + * @param {Object} [hints] + * @param {Object} [hints.defaultDistance] + * + * @returns {Point} + */ + function getNewShapePosition$1(source, element, hints) { + if (!hints) { + hints = {}; + } + + var distance = hints.defaultDistance || DEFAULT_DISTANCE; + + var sourceMid = getMid(source), + sourceTrbl = asTRBL(source); + + // simply put element right next to source + return { + x: sourceTrbl.right + distance + element.width / 2, + y: sourceMid.y + }; + } + + /** + * Select element after auto placement. + * + * @param {EventBus} eventBus + * @param {Selection} selection + */ + function AutoPlaceSelectionBehavior(eventBus, selection) { + + eventBus.on('autoPlace.end', 500, function(e) { + selection.select(e.shape); + }); + + } + + AutoPlaceSelectionBehavior.$inject = [ + 'eventBus', + 'selection' + ]; + + var AutoPlaceModule$1 = { + __init__: [ 'autoPlaceSelectionBehavior' ], + autoPlace: [ 'type', AutoPlace$1 ], + autoPlaceSelectionBehavior: [ 'type', AutoPlaceSelectionBehavior ] + }; + + /** + * Return the parent of the element with any of the given types. + * + * @param {djs.model.Base} element + * @param {string|Array} anyType + * + * @return {djs.model.Base} + */ + function getParent(element, anyType) { + + if (typeof anyType === 'string') { + anyType = [ anyType ]; + } + + while ((element = element.parent)) { + if (isAny(element, anyType)) { + return element; + } + } + + return null; + } + + /** + * Find the new position for the target element to + * connect to source. + * + * @param {djs.model.Shape} source + * @param {djs.model.Shape} element + * + * @return {Point} + */ + function getNewShapePosition(source, element) { + + if (is$1(element, 'bpmn:TextAnnotation')) { + return getTextAnnotationPosition(source, element); + } + + if (isAny(element, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ])) { + return getDataElementPosition(source, element); + } + + if (is$1(element, 'bpmn:FlowNode')) { + return getFlowNodePosition(source, element); + } + } + + /** + * Always try to place element right of source; + * compute actual distance from previous nodes in flow. + */ + function getFlowNodePosition(source, element) { + + var sourceTrbl = asTRBL(source); + var sourceMid = getMid(source); + + var horizontalDistance = getConnectedDistance(source, { + filter: function(connection) { + return is$1(connection, 'bpmn:SequenceFlow'); + } + }); + + var margin = 30, + minDistance = 80, + orientation = 'left'; + + if (is$1(source, 'bpmn:BoundaryEvent')) { + orientation = getOrientation(source, source.host, -25); + + if (orientation.indexOf('top') !== -1) { + margin *= -1; + } + } + + var position = { + x: sourceTrbl.right + horizontalDistance + element.width / 2, + y: sourceMid.y + getVerticalDistance(orientation, minDistance) + }; + + var nextPositionDirection = { + y: { + margin: margin, + minDistance: minDistance + } + }; + + return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection)); + } + + + function getVerticalDistance(orientation, minDistance) { + if (orientation.indexOf('top') != -1) { + return -1 * minDistance; + } else if (orientation.indexOf('bottom') != -1) { + return minDistance; + } else { + return 0; + } + } + + + /** + * Always try to place text annotations top right of source. + */ + function getTextAnnotationPosition(source, element) { + + var sourceTrbl = asTRBL(source); + + var position = { + x: sourceTrbl.right + element.width / 2, + y: sourceTrbl.top - 50 - element.height / 2 + }; + + if (isConnection$e(source)) { + position = getMid(source); + position.x += 100; + position.y -= 50; + } + + var nextPositionDirection = { + y: { + margin: -30, + minDistance: 20 + } + }; + + return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection)); + } + + + /** + * Always put element bottom right of source. + */ + function getDataElementPosition(source, element) { + + var sourceTrbl = asTRBL(source); + + var position = { + x: sourceTrbl.right - 10 + element.width / 2, + y: sourceTrbl.bottom + 40 + element.width / 2 + }; + + var nextPositionDirection = { + x: { + margin: 30, + minDistance: 30 + } + }; + + return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection)); + } + + function isConnection$e(element) { + return !!element.waypoints; + } + + /** + * BPMN auto-place behavior. + * + * @param {EventBus} eventBus + */ + function AutoPlace(eventBus) { + eventBus.on('autoPlace', function(context) { + var shape = context.shape, + source = context.source; + + return getNewShapePosition(source, shape); + }); + } + + AutoPlace.$inject = [ 'eventBus' ]; + + var AutoPlaceModule = { + __depends__: [ AutoPlaceModule$1 ], + __init__: [ 'bpmnAutoPlace' ], + bpmnAutoPlace: [ 'type', AutoPlace ] + }; + + /** + * An auto resize component that takes care of expanding a parent element + * if child elements are created or moved close the parents edge. + * + * @param {EventBus} eventBus + * @param {ElementRegistry} elementRegistry + * @param {Modeling} modeling + * @param {Rules} rules + */ + function AutoResize(eventBus, elementRegistry, modeling, rules) { + + CommandInterceptor.call(this, eventBus); + + this._elementRegistry = elementRegistry; + this._modeling = modeling; + this._rules = rules; + + var self = this; + + this.postExecuted([ 'shape.create' ], function(event) { + var context = event.context, + hints = context.hints || {}, + shape = context.shape, + parent = context.parent || context.newParent; + + if (hints.autoResize === false) { + return; + } + + self._expand([ shape ], parent); + }); + + this.postExecuted([ 'elements.move' ], function(event) { + var context = event.context, + elements = flatten(values(context.closure.topLevel)), + hints = context.hints; + + var autoResize = hints ? hints.autoResize : true; + + if (autoResize === false) { + return; + } + + var expandings = groupBy(elements, function(element) { + return element.parent.id; + }); + + forEach$1(expandings, function(elements, parentId) { + + // optionally filter elements to be considered when resizing + if (isArray$3(autoResize)) { + elements = elements.filter(function(element) { + return find(autoResize, matchPattern({ id: element.id })); + }); + } + + self._expand(elements, parentId); + }); + }); + + this.postExecuted([ 'shape.toggleCollapse' ], function(event) { + var context = event.context, + hints = context.hints, + shape = context.shape; + + if (hints && hints.autoResize === false) { + return; + } + + if (shape.collapsed) { + return; + } + + self._expand(shape.children || [], shape); + }); + + this.postExecuted([ 'shape.resize' ], function(event) { + var context = event.context, + hints = context.hints, + shape = context.shape, + parent = shape.parent; + + if (hints && hints.autoResize === false) { + return; + } + + if (parent) { + self._expand([ shape ], parent); + } + }); + + } + + AutoResize.$inject = [ + 'eventBus', + 'elementRegistry', + 'modeling', + 'rules' + ]; + + e(AutoResize, CommandInterceptor); + + + /** + * Calculate the new bounds of the target shape, given + * a number of elements have been moved or added into the parent. + * + * This method considers the current size, the added elements as well as + * the provided padding for the new bounds. + * + * @param {Array} elements + * @param {djs.model.Shape} target + */ + AutoResize.prototype._getOptimalBounds = function(elements, target) { + + var offset = this.getOffset(target), + padding = this.getPadding(target); + + var elementsTrbl = asTRBL(getBBox(elements)), + targetTrbl = asTRBL(target); + + var newTrbl = {}; + + if (elementsTrbl.top - targetTrbl.top < padding.top) { + newTrbl.top = elementsTrbl.top - offset.top; + } + + if (elementsTrbl.left - targetTrbl.left < padding.left) { + newTrbl.left = elementsTrbl.left - offset.left; + } + + if (targetTrbl.right - elementsTrbl.right < padding.right) { + newTrbl.right = elementsTrbl.right + offset.right; + } + + if (targetTrbl.bottom - elementsTrbl.bottom < padding.bottom) { + newTrbl.bottom = elementsTrbl.bottom + offset.bottom; + } + + return asBounds(assign({}, targetTrbl, newTrbl)); + }; + + + /** + * Expand the target shape respecting rules, offset and padding + * + * @param {Array} elements + * @param {djs.model.Shape|string} target|targetId + */ + AutoResize.prototype._expand = function(elements, target) { + + if (typeof target === 'string') { + target = this._elementRegistry.get(target); + } + + var allowed = this._rules.allowed('element.autoResize', { + elements: elements, + target: target + }); + + if (!allowed) { + return; + } + + // calculate the new bounds + var newBounds = this._getOptimalBounds(elements, target); + + if (!boundsChanged$1(newBounds, target)) { + return; + } + + var resizeDirections = getResizeDirections(pick(target, [ 'x', 'y', 'width', 'height' ]), newBounds); + + // resize the parent shape + this.resize(target, newBounds, { + autoResize: resizeDirections + }); + + var parent = target.parent; + + // recursively expand parent elements + if (parent) { + this._expand([ target ], parent); + } + }; + + + /** + * Get the amount to expand the given shape in each direction. + * + * @param {djs.model.Shape} shape + * + * @return {TRBL} + */ + AutoResize.prototype.getOffset = function(shape) { + return { top: 60, bottom: 60, left: 100, right: 100 }; + }; + + + /** + * Get the activation threshold for each side for which + * resize triggers. + * + * @param {djs.model.Shape} shape + * + * @return {TRBL} + */ + AutoResize.prototype.getPadding = function(shape) { + return { top: 2, bottom: 2, left: 15, right: 15 }; + }; + + + /** + * Perform the actual resize operation. + * + * @param {djs.model.Shape} shape + * @param {Bounds} newBounds + * @param {Object} [hints] + * @param {string} [hints.autoResize] + */ + AutoResize.prototype.resize = function(shape, newBounds, hints) { + this._modeling.resizeShape(shape, newBounds, null, hints); + }; + + + function boundsChanged$1(newBounds, oldBounds) { + return ( + newBounds.x !== oldBounds.x || + newBounds.y !== oldBounds.y || + newBounds.width !== oldBounds.width || + newBounds.height !== oldBounds.height + ); + } + + /** + * Get directions of resize as {n|w|s|e} e.g. "nw". + * + * @param {Bounds} oldBounds + * @param {Bounds} newBounds + * + * @returns {string} Resize directions as {n|w|s|e}. + */ + function getResizeDirections(oldBounds, newBounds) { + var directions = ''; + + oldBounds = asTRBL(oldBounds); + newBounds = asTRBL(newBounds); + + if (oldBounds.top > newBounds.top) { + directions = directions.concat('n'); + } + + if (oldBounds.right < newBounds.right) { + directions = directions.concat('w'); + } + + if (oldBounds.bottom < newBounds.bottom) { + directions = directions.concat('s'); + } + + if (oldBounds.left > newBounds.left) { + directions = directions.concat('e'); + } + + return directions; + } + + /** + * Sub class of the AutoResize module which implements a BPMN + * specific resize function. + */ + function BpmnAutoResize(injector) { + + injector.invoke(AutoResize, this); + } + + BpmnAutoResize.$inject = [ + 'injector' + ]; + + e(BpmnAutoResize, AutoResize); + + + /** + * Resize shapes and lanes. + * + * @param {djs.model.Shape} target + * @param {Bounds} newBounds + * @param {Object} hints + */ + BpmnAutoResize.prototype.resize = function(target, newBounds, hints) { + + if (is$1(target, 'bpmn:Participant')) { + this._modeling.resizeLane(target, newBounds, null, hints); + } else { + this._modeling.resizeShape(target, newBounds, null, hints); + } + }; + + /** + * This is a base rule provider for the element.autoResize rule. + */ + function AutoResizeProvider(eventBus) { + + RuleProvider.call(this, eventBus); + + var self = this; + + this.addRule('element.autoResize', function(context) { + return self.canResize(context.elements, context.target); + }); + } + + AutoResizeProvider.$inject = [ 'eventBus' ]; + + e(AutoResizeProvider, RuleProvider); + + /** + * Needs to be implemented by sub classes to allow actual auto resize + * + * @param {Array} elements + * @param {djs.model.Shape} target + * + * @return {boolean} + */ + AutoResizeProvider.prototype.canResize = function(elements, target) { + return false; + }; + + /** + * This module is a provider for automatically resizing parent BPMN elements + */ + function BpmnAutoResizeProvider(eventBus, modeling) { + AutoResizeProvider.call(this, eventBus); + + this._modeling = modeling; + } + + e(BpmnAutoResizeProvider, AutoResizeProvider); + + BpmnAutoResizeProvider.$inject = [ + 'eventBus', + 'modeling' + ]; + + + /** + * Check if the given target can be expanded + * + * @param {djs.model.Shape} target + * + * @return {boolean} + */ + BpmnAutoResizeProvider.prototype.canResize = function(elements, target) { + + // do not resize plane elements: + // root elements, collapsed sub-processes + if (is$1(target.di, 'bpmndi:BPMNPlane')) { + return false; + } + + if (!is$1(target, 'bpmn:Participant') && !is$1(target, 'bpmn:Lane') && !(is$1(target, 'bpmn:SubProcess'))) { + return false; + } + + var canResize = true; + + forEach$1(elements, function(element) { + + if (is$1(element, 'bpmn:Lane') || element.labelTarget) { + canResize = false; + return; + } + }); + + return canResize; + }; + + var AutoResizeModule = { + __init__: [ + 'bpmnAutoResize', + 'bpmnAutoResizeProvider' + ], + bpmnAutoResize: [ 'type', BpmnAutoResize ], + bpmnAutoResizeProvider: [ 'type', BpmnAutoResizeProvider ] + }; + + var HIGH_PRIORITY$j = 1500; + + + /** + * Browsers may swallow certain events (hover, out ...) if users are to + * fast with the mouse. + * + * @see http://stackoverflow.com/questions/7448468/why-cant-i-reliably-capture-a-mouseout-event + * + * The fix implemented in this component ensure that we + * + * 1) have a hover state after a successful drag.move event + * 2) have an out event when dragging leaves an element + * + * @param {ElementRegistry} elementRegistry + * @param {EventBus} eventBus + * @param {Injector} injector + */ + function HoverFix(elementRegistry, eventBus, injector) { + + var self = this; + + var dragging = injector.get('dragging', false); + + /** + * Make sure we are god damn hovering! + * + * @param {Event} dragging event + */ + function ensureHover(event) { + + if (event.hover) { + return; + } + + var originalEvent = event.originalEvent; + + var gfx = self._findTargetGfx(originalEvent); + + var element = gfx && elementRegistry.get(gfx); + + if (gfx && element) { + + // 1) cancel current mousemove + event.stopPropagation(); + + // 2) emit fake hover for new target + dragging.hover({ element: element, gfx: gfx }); + + // 3) re-trigger move event + dragging.move(originalEvent); + } + } + + + if (dragging) { + + /** + * We wait for a specific sequence of events before + * emitting a fake drag.hover event. + * + * Event Sequence: + * + * drag.start + * drag.move >> ensure we are hovering + */ + eventBus.on('drag.start', function(event) { + + eventBus.once('drag.move', HIGH_PRIORITY$j, function(event) { + + ensureHover(event); + + }); + + }); + } + + + /** + * We make sure that element.out is always fired, even if the + * browser swallows an element.out event. + * + * Event sequence: + * + * element.hover + * (element.out >> sometimes swallowed) + * element.hover >> ensure we fired element.out + */ + (function() { + var hoverGfx; + var hover; + + eventBus.on('element.hover', function(event) { + + // (1) remember current hover element + hoverGfx = event.gfx; + hover = event.element; + }); + + eventBus.on('element.hover', HIGH_PRIORITY$j, function(event) { + + // (3) am I on an element still? + if (hover) { + + // (4) that is a problem, gotta "simulate the out" + eventBus.fire('element.out', { + element: hover, + gfx: hoverGfx + }); + } + + }); + + eventBus.on('element.out', function() { + + // (2) unset hover state if we correctly outed us *GG* + hoverGfx = null; + hover = null; + }); + + })(); + + this._findTargetGfx = function(event) { + var position, + target; + + if (!(event instanceof MouseEvent)) { + return; + } + + position = toPoint(event); + + // damn expensive operation, ouch! + target = document.elementFromPoint(position.x, position.y); + + return getGfx(target); + }; + + } + + HoverFix.$inject = [ + 'elementRegistry', + 'eventBus', + 'injector' + ]; + + + // helpers ///////////////////// + + function getGfx(target) { + return closest(target, 'svg, .djs-element', true); + } + + var HoverFixModule = { + __init__: [ + 'hoverFix' + ], + hoverFix: [ 'type', HoverFix ], + }; + + var round$a = Math.round; + + var DRAG_ACTIVE_CLS = 'djs-drag-active'; + + + function preventDefault$1(event) { + event.preventDefault(); + } + + function isTouchEvent(event) { + + // check for TouchEvent being available first + // (i.e. not available on desktop Firefox) + return typeof TouchEvent !== 'undefined' && event instanceof TouchEvent; + } + + function getLength(point) { + return Math.sqrt(Math.pow(point.x, 2) + Math.pow(point.y, 2)); + } + + /** + * A helper that fires canvas localized drag events and realizes + * the general "drag-and-drop" look and feel. + * + * Calling {@link Dragging#activate} activates dragging on a canvas. + * + * It provides the following: + * + * * emits life cycle events, namespaced with a prefix assigned + * during dragging activation + * * sets and restores the cursor + * * sets and restores the selection if elements still exist + * * ensures there can be only one drag operation active at a time + * + * Dragging may be canceled manually by calling {@link Dragging#cancel} + * or by pressing ESC. + * + * + * ## Life-cycle events + * + * Dragging can be in three different states, off, initialized + * and active. + * + * (1) off: no dragging operation is in progress + * (2) initialized: a new drag operation got initialized but not yet + * started (i.e. because of no initial move) + * (3) started: dragging is in progress + * + * Eventually dragging will be off again after a drag operation has + * been ended or canceled via user click or ESC key press. + * + * To indicate transitions between these states dragging emits generic + * life-cycle events with the `drag.` prefix _and_ events namespaced + * to a prefix choosen by a user during drag initialization. + * + * The following events are emitted (appropriately prefixed) via + * the {@link EventBus}. + * + * * `init` + * * `start` + * * `move` + * * `end` + * * `ended` (dragging already in off state) + * * `cancel` (only if previously started) + * * `canceled` (dragging already in off state, only if previously started) + * * `cleanup` + * + * + * @example + * + * function MyDragComponent(eventBus, dragging) { + * + * eventBus.on('mydrag.start', function(event) { + * console.log('yes, we start dragging'); + * }); + * + * eventBus.on('mydrag.move', function(event) { + * console.log('canvas local coordinates', event.x, event.y, event.dx, event.dy); + * + * // local drag data is passed with the event + * event.context.foo; // "BAR" + * + * // the original mouse event, too + * event.originalEvent; // MouseEvent(...) + * }); + * + * eventBus.on('element.click', function(event) { + * dragging.init(event, 'mydrag', { + * cursor: 'grabbing', + * data: { + * context: { + * foo: "BAR" + * } + * } + * }); + * }); + * } + */ + function Dragging(eventBus, canvas, selection, elementRegistry) { + + var defaultOptions = { + threshold: 5, + trapClick: true + }; + + // the currently active drag operation + // dragging is active as soon as this context exists. + // + // it is visually _active_ only when a context.active flag is set to true. + var context; + + /* convert a global event into local coordinates */ + function toLocalPoint(globalPosition) { + + var viewbox = canvas.viewbox(); + + var clientRect = canvas._container.getBoundingClientRect(); + + return { + x: viewbox.x + (globalPosition.x - clientRect.left) / viewbox.scale, + y: viewbox.y + (globalPosition.y - clientRect.top) / viewbox.scale + }; + } + + // helpers + + function fire(type, dragContext) { + dragContext = dragContext || context; + + var event = eventBus.createEvent( + assign( + {}, + dragContext.payload, + dragContext.data, + { isTouch: dragContext.isTouch } + ) + ); + + // default integration + if (eventBus.fire('drag.' + type, event) === false) { + return false; + } + + return eventBus.fire(dragContext.prefix + '.' + type, event); + } + + function restoreSelection(previousSelection) { + var existingSelection = previousSelection.filter(function(element) { + return elementRegistry.get(element.id); + }); + + existingSelection.length && selection.select(existingSelection); + } + + // event listeners + + function move(event, activate) { + var payload = context.payload, + displacement = context.displacement; + + var globalStart = context.globalStart, + globalCurrent = toPoint(event), + globalDelta = delta(globalCurrent, globalStart); + + var localStart = context.localStart, + localCurrent = toLocalPoint(globalCurrent), + localDelta = delta(localCurrent, localStart); + + + // activate context explicitly or once threshold is reached + if (!context.active && (activate || getLength(globalDelta) > context.threshold)) { + + // fire start event with original + // starting coordinates + + assign(payload, { + x: round$a(localStart.x + displacement.x), + y: round$a(localStart.y + displacement.y), + dx: 0, + dy: 0 + }, { originalEvent: event }); + + if (false === fire('start')) { + return cancel(); + } + + context.active = true; + + // unset selection and remember old selection + // the previous (old) selection will always passed + // with the event via the event.previousSelection property + if (!context.keepSelection) { + payload.previousSelection = selection.get(); + selection.select(null); + } + + // allow custom cursor + if (context.cursor) { + set(context.cursor); + } + + // indicate dragging via marker on root element + canvas.addMarker(canvas.getRootElement(), DRAG_ACTIVE_CLS); + } + + stopPropagation$1(event); + + if (context.active) { + + // update payload with actual coordinates + assign(payload, { + x: round$a(localCurrent.x + displacement.x), + y: round$a(localCurrent.y + displacement.y), + dx: round$a(localDelta.x), + dy: round$a(localDelta.y) + }, { originalEvent: event }); + + // emit move event + fire('move'); + } + } + + function end(event) { + var previousContext, + returnValue = true; + + if (context.active) { + + if (event) { + context.payload.originalEvent = event; + + // suppress original event (click, ...) + // because we just ended a drag operation + stopPropagation$1(event); + } + + // implementations may stop restoring the + // original state (selections, ...) by preventing the + // end events default action + returnValue = fire('end'); + } + + if (returnValue === false) { + fire('rejected'); + } + + previousContext = cleanup(returnValue !== true); + + // last event to be fired when all drag operations are done + // at this point in time no drag operation is in progress anymore + fire('ended', previousContext); + } + + + // cancel active drag operation if the user presses + // the ESC key on the keyboard + + function checkCancel(event) { + + if (event.which === 27) { + preventDefault$1(event); + + cancel(); + } + } + + + // prevent ghost click that might occur after a finished + // drag and drop session + + function trapClickAndEnd(event) { + + var untrap; + + // trap the click in case we are part of an active + // drag operation. This will effectively prevent + // the ghost click that cannot be canceled otherwise. + if (context.active) { + + untrap = install(eventBus); + + // remove trap after minimal delay + setTimeout(untrap, 400); + + // prevent default action (click) + preventDefault$1(event); + } + + end(event); + } + + function trapTouch(event) { + move(event); + } + + // update the drag events hover (djs.model.Base) and hoverGfx (Snap) + // properties during hover and out and fire {prefix}.hover and {prefix}.out properties + // respectively + + function hover(event) { + var payload = context.payload; + + payload.hoverGfx = event.gfx; + payload.hover = event.element; + + fire('hover'); + } + + function out(event) { + fire('out'); + + var payload = context.payload; + + payload.hoverGfx = null; + payload.hover = null; + } + + + // life-cycle methods + + function cancel(restore) { + var previousContext; + + if (!context) { + return; + } + + var wasActive = context.active; + + if (wasActive) { + fire('cancel'); + } + + previousContext = cleanup(restore); + + if (wasActive) { + + // last event to be fired when all drag operations are done + // at this point in time no drag operation is in progress anymore + fire('canceled', previousContext); + } + } + + function cleanup(restore) { + var previousContext, + endDrag; + + fire('cleanup'); + + // reset cursor + unset(); + + if (context.trapClick) { + endDrag = trapClickAndEnd; + } else { + endDrag = end; + } + + // reset dom listeners + componentEvent.unbind(document, 'mousemove', move); + + componentEvent.unbind(document, 'dragstart', preventDefault$1); + componentEvent.unbind(document, 'selectstart', preventDefault$1); + + componentEvent.unbind(document, 'mousedown', endDrag, true); + componentEvent.unbind(document, 'mouseup', endDrag, true); + + componentEvent.unbind(document, 'keyup', checkCancel); + + componentEvent.unbind(document, 'touchstart', trapTouch, true); + componentEvent.unbind(document, 'touchcancel', cancel, true); + componentEvent.unbind(document, 'touchmove', move, true); + componentEvent.unbind(document, 'touchend', end, true); + + eventBus.off('element.hover', hover); + eventBus.off('element.out', out); + + // remove drag marker on root element + canvas.removeMarker(canvas.getRootElement(), DRAG_ACTIVE_CLS); + + // restore selection, unless it has changed + var previousSelection = context.payload.previousSelection; + + if (restore !== false && previousSelection && !selection.get().length) { + restoreSelection(previousSelection); + } + + previousContext = context; + + context = null; + + return previousContext; + } + + /** + * Initialize a drag operation. + * + * If `localPosition` is given, drag events will be emitted + * relative to it. + * + * @param {MouseEvent|TouchEvent} [event] + * @param {Point} [localPosition] actual diagram local position this drag operation should start at + * @param {string} prefix + * @param {Object} [options] + */ + function init(event, relativeTo, prefix, options) { + + // only one drag operation may be active, at a time + if (context) { + cancel(false); + } + + if (typeof relativeTo === 'string') { + options = prefix; + prefix = relativeTo; + relativeTo = null; + } + + options = assign({}, defaultOptions, options || {}); + + var data = options.data || {}, + originalEvent, + globalStart, + localStart, + endDrag, + isTouch; + + if (options.trapClick) { + endDrag = trapClickAndEnd; + } else { + endDrag = end; + } + + if (event) { + originalEvent = getOriginal$1(event) || event; + globalStart = toPoint(event); + + stopPropagation$1(event); + + // prevent default browser dragging behavior + if (originalEvent.type === 'dragstart') { + preventDefault$1(originalEvent); + } + } else { + originalEvent = null; + globalStart = { x: 0, y: 0 }; + } + + localStart = toLocalPoint(globalStart); + + if (!relativeTo) { + relativeTo = localStart; + } + + isTouch = isTouchEvent(originalEvent); + + context = assign({ + prefix: prefix, + data: data, + payload: {}, + globalStart: globalStart, + displacement: delta(relativeTo, localStart), + localStart: localStart, + isTouch: isTouch + }, options); + + // skip dom registration if trigger + // is set to manual (during testing) + if (!options.manual) { + + // add dom listeners + + if (isTouch) { + componentEvent.bind(document, 'touchstart', trapTouch, true); + componentEvent.bind(document, 'touchcancel', cancel, true); + componentEvent.bind(document, 'touchmove', move, true); + componentEvent.bind(document, 'touchend', end, true); + } else { + + // assume we use the mouse to interact per default + componentEvent.bind(document, 'mousemove', move); + + // prevent default browser drag and text selection behavior + componentEvent.bind(document, 'dragstart', preventDefault$1); + componentEvent.bind(document, 'selectstart', preventDefault$1); + + componentEvent.bind(document, 'mousedown', endDrag, true); + componentEvent.bind(document, 'mouseup', endDrag, true); + } + + componentEvent.bind(document, 'keyup', checkCancel); + + eventBus.on('element.hover', hover); + eventBus.on('element.out', out); + } + + fire('init'); + + if (options.autoActivate) { + move(event, true); + } + } + + // cancel on diagram destruction + eventBus.on('diagram.destroy', cancel); + + + // API + + this.init = init; + this.move = move; + this.hover = hover; + this.out = out; + this.end = end; + + this.cancel = cancel; + + // for introspection + + this.context = function() { + return context; + }; + + this.setOptions = function(options) { + assign(defaultOptions, options); + }; + } + + Dragging.$inject = [ + 'eventBus', + 'canvas', + 'selection', + 'elementRegistry' + ]; + + var DraggingModule = { + __depends__: [ + HoverFixModule, + SelectionModule, + ], + dragging: [ 'type', Dragging ], + }; + + /** + * Initiates canvas scrolling if current cursor point is close to a border. + * Cancelled when current point moves back inside the scrolling borders + * or cancelled manually. + * + * Default options : + * scrollThresholdIn: [ 20, 20, 20, 20 ], + * scrollThresholdOut: [ 0, 0, 0, 0 ], + * scrollRepeatTimeout: 15, + * scrollStep: 10 + * + * Threshold order: + * [ left, top, right, bottom ] + */ + function AutoScroll(config, eventBus, canvas) { + + this._canvas = canvas; + + this._opts = assign({ + scrollThresholdIn: [ 20, 20, 20, 20 ], + scrollThresholdOut: [ 0, 0, 0, 0 ], + scrollRepeatTimeout: 15, + scrollStep: 10 + }, config); + + var self = this; + + eventBus.on('drag.move', function(e) { + var point = self._toBorderPoint(e); + + self.startScroll(point); + }); + + eventBus.on([ 'drag.cleanup' ], function() { + self.stopScroll(); + }); + } + + AutoScroll.$inject = [ + 'config.autoScroll', + 'eventBus', + 'canvas' + ]; + + + /** + * Starts scrolling loop. + * Point is given in global scale in canvas container box plane. + * + * @param {Object} point { x: X, y: Y } + */ + AutoScroll.prototype.startScroll = function(point) { + + var canvas = this._canvas; + var opts = this._opts; + var self = this; + + var clientRect = canvas.getContainer().getBoundingClientRect(); + + var diff = [ + point.x, + point.y, + clientRect.width - point.x, + clientRect.height - point.y + ]; + + this.stopScroll(); + + var dx = 0, + dy = 0; + + for (var i = 0; i < 4; i++) { + if (between(diff[i], opts.scrollThresholdOut[i], opts.scrollThresholdIn[i])) { + if (i === 0) { + dx = opts.scrollStep; + } else if (i == 1) { + dy = opts.scrollStep; + } else if (i == 2) { + dx = -opts.scrollStep; + } else if (i == 3) { + dy = -opts.scrollStep; + } + } + } + + if (dx !== 0 || dy !== 0) { + canvas.scroll({ dx: dx, dy: dy }); + + this._scrolling = setTimeout(function() { + self.startScroll(point); + }, opts.scrollRepeatTimeout); + } + }; + + function between(val, start, end) { + if (start < val && val < end) { + return true; + } + + return false; + } + + + /** + * Stops scrolling loop. + */ + AutoScroll.prototype.stopScroll = function() { + clearTimeout(this._scrolling); + }; + + + /** + * Overrides defaults options. + * + * @param {Object} options + */ + AutoScroll.prototype.setOptions = function(options) { + this._opts = assign({}, this._opts, options); + }; + + + /** + * Converts event to a point in canvas container plane in global scale. + * + * @param {Event} event + * @return {Point} + */ + AutoScroll.prototype._toBorderPoint = function(event) { + var clientRect = this._canvas._container.getBoundingClientRect(); + + var globalPosition = toPoint(event.originalEvent); + + return { + x: globalPosition.x - clientRect.left, + y: globalPosition.y - clientRect.top + }; + }; + + var AutoScrollModule = { + __depends__: [ + DraggingModule, + ], + __init__: [ 'autoScroll' ], + autoScroll: [ 'type', AutoScroll ] + }; + + /** + * A service that provides rules for certain diagram actions. + * + * The default implementation will hook into the {@link CommandStack} + * to perform the actual rule evaluation. Make sure to provide the + * `commandStack` service with this module if you plan to use it. + * + * Together with this implementation you may use the {@link RuleProvider} + * to implement your own rule checkers. + * + * This module is ment to be easily replaced, thus the tiny foot print. + * + * @param {Injector} injector + */ + function Rules(injector) { + this._commandStack = injector.get('commandStack', false); + } + + Rules.$inject = [ 'injector' ]; + + + /** + * Returns whether or not a given modeling action can be executed + * in the specified context. + * + * This implementation will respond with allow unless anyone + * objects. + * + * @param {string} action the action to be checked + * @param {Object} [context] the context to check the action in + * + * @return {boolean} returns true, false or null depending on whether the + * operation is allowed, not allowed or should be ignored. + */ + Rules.prototype.allowed = function(action, context) { + var allowed = true; + + var commandStack = this._commandStack; + + if (commandStack) { + allowed = commandStack.canExecute(action, context); + } + + // map undefined to true, i.e. no rules + return allowed === undefined ? true : allowed; + }; + + var RulesModule$1 = { + __init__: [ 'rules' ], + rules: [ 'type', Rules ] + }; + + var round$9 = Math.round, + max$6 = Math.max; + + + function circlePath(center, r) { + var x = center.x, + y = center.y; + + return [ + [ 'M', x, y ], + [ 'm', 0, -r ], + [ 'a', r, r, 0, 1, 1, 0, 2 * r ], + [ 'a', r, r, 0, 1, 1, 0, -2 * r ], + [ 'z' ] + ]; + } + + function linePath(points) { + var segments = []; + + points.forEach(function(p, idx) { + segments.push([ idx === 0 ? 'M' : 'L', p.x, p.y ]); + }); + + return segments; + } + + + var INTERSECTION_THRESHOLD$1 = 10; + + function getBendpointIntersection(waypoints, reference) { + + var i, w; + + for (i = 0; (w = waypoints[i]); i++) { + + if (pointDistance(w, reference) <= INTERSECTION_THRESHOLD$1) { + return { + point: waypoints[i], + bendpoint: true, + index: i + }; + } + } + + return null; + } + + function getPathIntersection(waypoints, reference) { + + var intersections = intersect(circlePath(reference, INTERSECTION_THRESHOLD$1), linePath(waypoints)); + + var a = intersections[0], + b = intersections[intersections.length - 1], + idx; + + if (!a) { + + // no intersection + return null; + } + + if (a !== b) { + + if (a.segment2 !== b.segment2) { + + // we use the bendpoint in between both segments + // as the intersection point + + idx = max$6(a.segment2, b.segment2) - 1; + + return { + point: waypoints[idx], + bendpoint: true, + index: idx + }; + } + + return { + point: { + x: (round$9(a.x + b.x) / 2), + y: (round$9(a.y + b.y) / 2) + }, + index: a.segment2 + }; + } + + return { + point: { + x: round$9(a.x), + y: round$9(a.y) + }, + index: a.segment2 + }; + } + + /** + * Returns the closest point on the connection towards a given reference point. + * + * @param {Array} waypoints + * @param {Point} reference + * + * @return {Object} intersection data (segment, point) + */ + function getApproxIntersection(waypoints, reference) { + return getBendpointIntersection(waypoints, reference) || getPathIntersection(waypoints, reference); + } + + /** + * Returns the length of a vector + * + * @param {Vector} + * @return {Float} + */ + function vectorLength(v) { + return Math.sqrt(Math.pow(v.x, 2) + Math.pow(v.y, 2)); + } + + + /** + * Calculates the angle between a line a the yAxis + * + * @param {Array} + * @return {Float} + */ + function getAngle(line) { + + // return value is between 0, 180 and -180, -0 + // @janstuemmel: maybe replace return a/b with b/a + return Math.atan((line[1].y - line[0].y) / (line[1].x - line[0].x)); + } + + + /** + * Rotates a vector by a given angle + * + * @param {Vector} + * @param {Float} Angle in radians + * @return {Vector} + */ + function rotateVector(vector, angle) { + return (!angle) ? vector : { + x: Math.cos(angle) * vector.x - Math.sin(angle) * vector.y, + y: Math.sin(angle) * vector.x + Math.cos(angle) * vector.y + }; + } + + + /** + * Solves a 2D equation system + * a + r*b = c, where a,b,c are 2D vectors + * + * @param {Vector} + * @param {Vector} + * @param {Vector} + * @return {Float} + */ + function solveLambaSystem(a, b, c) { + + // the 2d system + var system = [ + { n: a[0] - c[0], lambda: b[0] }, + { n: a[1] - c[1], lambda: b[1] } + ]; + + // solve + var n = system[0].n * b[0] + system[1].n * b[1], + l = system[0].lambda * b[0] + system[1].lambda * b[1]; + + return -n / l; + } + + + /** + * Position of perpendicular foot + * + * @param {Point} + * @param [ {Point}, {Point} ] line defined through two points + * @return {Point} the perpendicular foot position + */ + function perpendicularFoot(point, line) { + + var a = line[0], b = line[1]; + + // relative position of b from a + var bd = { x: b.x - a.x, y: b.y - a.y }; + + // solve equation system to the parametrized vectors param real value + var r = solveLambaSystem([ a.x, a.y ], [ bd.x, bd.y ], [ point.x, point.y ]); + + return { x: a.x + r * bd.x, y: a.y + r * bd.y }; + } + + + /** + * Calculates the distance between a point and a line + * + * @param {Point} + * @param [ {Point}, {Point} ] line defined through two points + * @return {Float} distance + */ + function getDistancePointLine(point, line) { + + var pfPoint = perpendicularFoot(point, line); + + // distance vector + var connectionVector = { + x: pfPoint.x - point.x, + y: pfPoint.y - point.y + }; + + return vectorLength(connectionVector); + } + + + /** + * Calculates the distance between two points + * + * @param {Point} + * @param {Point} + * @return {Float} distance + */ + function getDistancePointPoint(point1, point2) { + + return vectorLength({ + x: point1.x - point2.x, + y: point1.y - point2.y + }); + } + + var BENDPOINT_CLS = 'djs-bendpoint'; + var SEGMENT_DRAGGER_CLS = 'djs-segment-dragger'; + + function toCanvasCoordinates(canvas, event) { + + var position = toPoint(event), + clientRect = canvas._container.getBoundingClientRect(), + offset; + + // canvas relative position + + offset = { + x: clientRect.left, + y: clientRect.top + }; + + // update actual event payload with canvas relative measures + + var viewbox = canvas.viewbox(); + + return { + x: viewbox.x + (position.x - offset.x) / viewbox.scale, + y: viewbox.y + (position.y - offset.y) / viewbox.scale + }; + } + + function getConnectionIntersection(canvas, waypoints, event) { + var localPosition = toCanvasCoordinates(canvas, event), + intersection = getApproxIntersection(waypoints, localPosition); + + return intersection; + } + + function addBendpoint(parentGfx, cls) { + var groupGfx = create$1('g'); + classes(groupGfx).add(BENDPOINT_CLS); + + append(parentGfx, groupGfx); + + var visual = create$1('circle'); + attr(visual, { + cx: 0, + cy: 0, + r: 4 + }); + classes(visual).add('djs-visual'); + + append(groupGfx, visual); + + var hit = create$1('circle'); + attr(hit, { + cx: 0, + cy: 0, + r: 10 + }); + classes(hit).add('djs-hit'); + + append(groupGfx, hit); + + if (cls) { + classes(groupGfx).add(cls); + } + + return groupGfx; + } + + function createParallelDragger(parentGfx, segmentStart, segmentEnd, alignment) { + var draggerGfx = create$1('g'); + + append(parentGfx, draggerGfx); + + var width = 18, + height = 6, + padding = 11, + hitWidth = calculateHitWidth(segmentStart, segmentEnd, alignment), + hitHeight = height + padding; + + var visual = create$1('rect'); + attr(visual, { + x: -width / 2, + y: -height / 2, + width: width, + height: height + }); + classes(visual).add('djs-visual'); + + append(draggerGfx, visual); + + var hit = create$1('rect'); + attr(hit, { + x: -hitWidth / 2, + y: -hitHeight / 2, + width: hitWidth, + height: hitHeight + }); + classes(hit).add('djs-hit'); + + append(draggerGfx, hit); + + rotate(draggerGfx, alignment === 'v' ? 90 : 0); + + return draggerGfx; + } + + + function addSegmentDragger(parentGfx, segmentStart, segmentEnd) { + + var groupGfx = create$1('g'), + mid = getMidPoint(segmentStart, segmentEnd), + alignment = pointsAligned(segmentStart, segmentEnd); + + append(parentGfx, groupGfx); + + createParallelDragger(groupGfx, segmentStart, segmentEnd, alignment); + + classes(groupGfx).add(SEGMENT_DRAGGER_CLS); + classes(groupGfx).add(alignment === 'h' ? 'horizontal' : 'vertical'); + + translate$2(groupGfx, mid.x, mid.y); + + return groupGfx; + } + + /** + * Calculates region for segment move which is 2/3 of the full segment length + * @param {number} segmentLength + * + * @return {number} + */ + function calculateSegmentMoveRegion(segmentLength) { + return Math.abs(Math.round(segmentLength * 2 / 3)); + } + + /** + * Returns the point with the closest distance that is on the connection path. + * + * @param {Point} position + * @param {djs.Base.Connection} connection + * @returns {Point} + */ + function getClosestPointOnConnection(position, connection) { + var segment = getClosestSegment(position, connection); + + return perpendicularFoot(position, segment); + } + + + // helper ////////// + + function calculateHitWidth(segmentStart, segmentEnd, alignment) { + var segmentLengthXAxis = segmentEnd.x - segmentStart.x, + segmentLengthYAxis = segmentEnd.y - segmentStart.y; + + return alignment === 'h' ? + calculateSegmentMoveRegion(segmentLengthXAxis) : + calculateSegmentMoveRegion(segmentLengthYAxis); + } + + function getClosestSegment(position, connection) { + var waypoints = connection.waypoints; + + var minDistance = Infinity, + segmentIndex; + + for (var i = 0; i < waypoints.length - 1; i++) { + var start = waypoints[i], + end = waypoints[i + 1], + distance = getDistancePointLine(position, [ start, end ]); + + if (distance < minDistance) { + minDistance = distance; + segmentIndex = i; + } + } + + return [ waypoints[segmentIndex], waypoints[segmentIndex + 1] ]; + } + + /** + * A service that adds editable bendpoints to connections. + */ + function Bendpoints( + eventBus, canvas, interactionEvents, + bendpointMove, connectionSegmentMove) { + + /** + * Returns true if intersection point is inside middle region of segment, adjusted by + * optional threshold + */ + function isIntersectionMiddle(intersection, waypoints, treshold) { + var idx = intersection.index, + p = intersection.point, + p0, p1, mid, aligned, xDelta, yDelta; + + if (idx <= 0 || intersection.bendpoint) { + return false; + } + + p0 = waypoints[idx - 1]; + p1 = waypoints[idx]; + mid = getMidPoint(p0, p1), + aligned = pointsAligned(p0, p1); + xDelta = Math.abs(p.x - mid.x); + yDelta = Math.abs(p.y - mid.y); + + return aligned && xDelta <= treshold && yDelta <= treshold; + } + + /** + * Calculates the threshold from a connection's middle which fits the two-third-region + */ + function calculateIntersectionThreshold(connection, intersection) { + var waypoints = connection.waypoints, + relevantSegment, alignment, segmentLength, threshold; + + if (intersection.index <= 0 || intersection.bendpoint) { + return null; + } + + // segment relative to connection intersection + relevantSegment = { + start: waypoints[intersection.index - 1], + end: waypoints[intersection.index] + }; + + alignment = pointsAligned(relevantSegment.start, relevantSegment.end); + + if (!alignment) { + return null; + } + + if (alignment === 'h') { + segmentLength = relevantSegment.end.x - relevantSegment.start.x; + } else { + segmentLength = relevantSegment.end.y - relevantSegment.start.y; + } + + // calculate threshold relative to 2/3 of segment length + threshold = calculateSegmentMoveRegion(segmentLength) / 2; + + return threshold; + } + + function activateBendpointMove(event, connection) { + var waypoints = connection.waypoints, + intersection = getConnectionIntersection(canvas, waypoints, event), + threshold; + + if (!intersection) { + return; + } + + threshold = calculateIntersectionThreshold(connection, intersection); + + if (isIntersectionMiddle(intersection, waypoints, threshold)) { + connectionSegmentMove.start(event, connection, intersection.index); + } else { + bendpointMove.start(event, connection, intersection.index, !intersection.bendpoint); + } + + // we've handled the event + return true; + } + + function bindInteractionEvents(node, eventName, element) { + + componentEvent.bind(node, eventName, function(event) { + interactionEvents.triggerMouseEvent(eventName, event, element); + event.stopPropagation(); + }); + } + + function getBendpointsContainer(element, create) { + + var layer = canvas.getLayer('overlays'), + gfx = query('.djs-bendpoints[data-element-id="' + cssEscape(element.id) + '"]', layer); + + if (!gfx && create) { + gfx = create$1('g'); + attr(gfx, { 'data-element-id': element.id }); + classes(gfx).add('djs-bendpoints'); + + append(layer, gfx); + + bindInteractionEvents(gfx, 'mousedown', element); + bindInteractionEvents(gfx, 'click', element); + bindInteractionEvents(gfx, 'dblclick', element); + } + + return gfx; + } + + function getSegmentDragger(idx, parentGfx) { + return query( + '.djs-segment-dragger[data-segment-idx="' + idx + '"]', + parentGfx + ); + } + + function createBendpoints(gfx, connection) { + connection.waypoints.forEach(function(p, idx) { + var bendpoint = addBendpoint(gfx); + + append(gfx, bendpoint); + + translate$2(bendpoint, p.x, p.y); + }); + + // add floating bendpoint + addBendpoint(gfx, 'floating'); + } + + function createSegmentDraggers(gfx, connection) { + + var waypoints = connection.waypoints; + + var segmentStart, + segmentEnd, + segmentDraggerGfx; + + for (var i = 1; i < waypoints.length; i++) { + + segmentStart = waypoints[i - 1]; + segmentEnd = waypoints[i]; + + if (pointsAligned(segmentStart, segmentEnd)) { + segmentDraggerGfx = addSegmentDragger(gfx, segmentStart, segmentEnd); + + attr(segmentDraggerGfx, { 'data-segment-idx': i }); + + bindInteractionEvents(segmentDraggerGfx, 'mousemove', connection); + } + } + } + + function clearBendpoints(gfx) { + forEach$1(all('.' + BENDPOINT_CLS, gfx), function(node) { + remove$1(node); + }); + } + + function clearSegmentDraggers(gfx) { + forEach$1(all('.' + SEGMENT_DRAGGER_CLS, gfx), function(node) { + remove$1(node); + }); + } + + function addHandles(connection) { + + var gfx = getBendpointsContainer(connection); + + if (!gfx) { + gfx = getBendpointsContainer(connection, true); + + createBendpoints(gfx, connection); + createSegmentDraggers(gfx, connection); + } + + return gfx; + } + + function updateHandles(connection) { + + var gfx = getBendpointsContainer(connection); + + if (gfx) { + clearSegmentDraggers(gfx); + clearBendpoints(gfx); + createSegmentDraggers(gfx, connection); + createBendpoints(gfx, connection); + } + } + + function updateFloatingBendpointPosition(parentGfx, intersection) { + var floating = query('.floating', parentGfx), + point = intersection.point; + + if (!floating) { + return; + } + + translate$2(floating, point.x, point.y); + + } + + function updateSegmentDraggerPosition(parentGfx, intersection, waypoints) { + + var draggerGfx = getSegmentDragger(intersection.index, parentGfx), + segmentStart = waypoints[intersection.index - 1], + segmentEnd = waypoints[intersection.index], + point = intersection.point, + mid = getMidPoint(segmentStart, segmentEnd), + alignment = pointsAligned(segmentStart, segmentEnd), + draggerVisual, relativePosition; + + if (!draggerGfx) { + return; + } + + draggerVisual = getDraggerVisual(draggerGfx); + + relativePosition = { + x: point.x - mid.x, + y: point.y - mid.y + }; + + if (alignment === 'v') { + + // rotate position + relativePosition = { + x: relativePosition.y, + y: relativePosition.x + }; + } + + translate$2(draggerVisual, relativePosition.x, relativePosition.y); + } + + eventBus.on('connection.changed', function(event) { + updateHandles(event.element); + }); + + eventBus.on('connection.remove', function(event) { + var gfx = getBendpointsContainer(event.element); + + if (gfx) { + remove$1(gfx); + } + }); + + eventBus.on('element.marker.update', function(event) { + + var element = event.element, + bendpointsGfx; + + if (!element.waypoints) { + return; + } + + bendpointsGfx = addHandles(element); + + if (event.add) { + classes(bendpointsGfx).add(event.marker); + } else { + classes(bendpointsGfx).remove(event.marker); + } + }); + + eventBus.on('element.mousemove', function(event) { + + var element = event.element, + waypoints = element.waypoints, + bendpointsGfx, + intersection; + + if (waypoints) { + bendpointsGfx = getBendpointsContainer(element, true); + + intersection = getConnectionIntersection(canvas, waypoints, event.originalEvent); + + if (!intersection) { + return; + } + + updateFloatingBendpointPosition(bendpointsGfx, intersection); + + if (!intersection.bendpoint) { + updateSegmentDraggerPosition(bendpointsGfx, intersection, waypoints); + } + + } + }); + + eventBus.on('element.mousedown', function(event) { + + if (!isPrimaryButton(event)) { + return; + } + + var originalEvent = event.originalEvent, + element = event.element; + + if (!element.waypoints) { + return; + } + + return activateBendpointMove(originalEvent, element); + }); + + eventBus.on('selection.changed', function(event) { + var newSelection = event.newSelection, + primary = newSelection[0]; + + if (primary && primary.waypoints) { + addHandles(primary); + } + }); + + eventBus.on('element.hover', function(event) { + var element = event.element; + + if (element.waypoints) { + addHandles(element); + interactionEvents.registerEvent(event.gfx, 'mousemove', 'element.mousemove'); + } + }); + + eventBus.on('element.out', function(event) { + interactionEvents.unregisterEvent(event.gfx, 'mousemove', 'element.mousemove'); + }); + + // update bendpoint container data attribute on element ID change + eventBus.on('element.updateId', function(context) { + var element = context.element, + newId = context.newId; + + if (element.waypoints) { + var bendpointContainer = getBendpointsContainer(element); + + if (bendpointContainer) { + attr(bendpointContainer, { 'data-element-id': newId }); + } + } + }); + + // API + + this.addHandles = addHandles; + this.updateHandles = updateHandles; + this.getBendpointsContainer = getBendpointsContainer; + this.getSegmentDragger = getSegmentDragger; + } + + Bendpoints.$inject = [ + 'eventBus', + 'canvas', + 'interactionEvents', + 'bendpointMove', + 'connectionSegmentMove' + ]; + + + + // helper ///////////// + + function getDraggerVisual(draggerGfx) { + return query('.djs-visual', draggerGfx); + } + + var round$8 = Math.round; + + var RECONNECT_START$1 = 'reconnectStart', + RECONNECT_END$1 = 'reconnectEnd', + UPDATE_WAYPOINTS$1 = 'updateWaypoints'; + + + /** + * Move bendpoints through drag and drop to add/remove bendpoints or reconnect connection. + */ + function BendpointMove(injector, eventBus, canvas, dragging, rules, modeling) { + this._injector = injector; + + this.start = function(event, connection, bendpointIndex, insert) { + var gfx = canvas.getGraphics(connection), + source = connection.source, + target = connection.target, + waypoints = connection.waypoints, + type; + + if (!insert && bendpointIndex === 0) { + type = RECONNECT_START$1; + } else + if (!insert && bendpointIndex === waypoints.length - 1) { + type = RECONNECT_END$1; + } else { + type = UPDATE_WAYPOINTS$1; + } + + var command = type === UPDATE_WAYPOINTS$1 ? 'connection.updateWaypoints' : 'connection.reconnect'; + + var allowed = rules.allowed(command, { + connection: connection, + source: source, + target: target + }); + + if (allowed === false) { + allowed = rules.allowed(command, { + connection: connection, + source: target, + target: source + }); + } + + if (allowed === false) { + return; + } + + dragging.init(event, 'bendpoint.move', { + data: { + connection: connection, + connectionGfx: gfx, + context: { + allowed: allowed, + bendpointIndex: bendpointIndex, + connection: connection, + source: source, + target: target, + insert: insert, + type: type + } + } + }); + }; + + eventBus.on('bendpoint.move.hover', function(event) { + var context = event.context, + connection = context.connection, + source = connection.source, + target = connection.target, + hover = event.hover, + type = context.type; + + // cache hover state + context.hover = hover; + + var allowed; + + if (!hover) { + return; + } + + var command = type === UPDATE_WAYPOINTS$1 ? 'connection.updateWaypoints' : 'connection.reconnect'; + + allowed = context.allowed = rules.allowed(command, { + connection: connection, + source: type === RECONNECT_START$1 ? hover : source, + target: type === RECONNECT_END$1 ? hover : target + }); + + if (allowed) { + context.source = type === RECONNECT_START$1 ? hover : source; + context.target = type === RECONNECT_END$1 ? hover : target; + + return; + } + + if (allowed === false) { + allowed = context.allowed = rules.allowed(command, { + connection: connection, + source: type === RECONNECT_END$1 ? hover : target, + target: type === RECONNECT_START$1 ? hover : source + }); + } + + if (allowed) { + context.source = type === RECONNECT_END$1 ? hover : target; + context.target = type === RECONNECT_START$1 ? hover : source; + } + }); + + eventBus.on([ 'bendpoint.move.out', 'bendpoint.move.cleanup' ], function(event) { + var context = event.context, + type = context.type; + + context.hover = null; + context.source = null; + context.target = null; + + if (type !== UPDATE_WAYPOINTS$1) { + context.allowed = false; + } + }); + + eventBus.on('bendpoint.move.end', function(event) { + var context = event.context, + allowed = context.allowed, + bendpointIndex = context.bendpointIndex, + connection = context.connection, + insert = context.insert, + newWaypoints = connection.waypoints.slice(), + source = context.source, + target = context.target, + type = context.type, + hints = context.hints || {}; + + // ensure integer values (important if zoom level was > 1 during move) + var docking = { + x: round$8(event.x), + y: round$8(event.y) + }; + + if (!allowed) { + return false; + } + + if (type === UPDATE_WAYPOINTS$1) { + if (insert) { + + // insert new bendpoint + newWaypoints.splice(bendpointIndex, 0, docking); + } else { + + // swap previous waypoint with moved one + newWaypoints[bendpointIndex] = docking; + } + + // pass hints about actual moved bendpoint + // useful for connection/label layout + hints.bendpointMove = { + insert: insert, + bendpointIndex: bendpointIndex + }; + + newWaypoints = this.cropWaypoints(connection, newWaypoints); + + modeling.updateWaypoints(connection, filterRedundantWaypoints(newWaypoints), hints); + } else { + if (type === RECONNECT_START$1) { + hints.docking = 'source'; + + if (isReverse$2(context)) { + hints.docking = 'target'; + + hints.newWaypoints = newWaypoints.reverse(); + } + } else if (type === RECONNECT_END$1) { + hints.docking = 'target'; + + if (isReverse$2(context)) { + hints.docking = 'source'; + + hints.newWaypoints = newWaypoints.reverse(); + } + } + + modeling.reconnect(connection, source, target, docking, hints); + } + }, this); + } + + BendpointMove.$inject = [ + 'injector', + 'eventBus', + 'canvas', + 'dragging', + 'rules', + 'modeling' + ]; + + BendpointMove.prototype.cropWaypoints = function(connection, newWaypoints) { + var connectionDocking = this._injector.get('connectionDocking', false); + + if (!connectionDocking) { + return newWaypoints; + } + + var waypoints = connection.waypoints; + + connection.waypoints = newWaypoints; + + connection.waypoints = connectionDocking.getCroppedWaypoints(connection); + + newWaypoints = connection.waypoints; + + connection.waypoints = waypoints; + + return newWaypoints; + }; + + + // helpers ////////// + + function isReverse$2(context) { + var hover = context.hover, + source = context.source, + target = context.target, + type = context.type; + + if (type === RECONNECT_START$1) { + return hover && target && hover === target && source !== target; + } + + if (type === RECONNECT_END$1) { + return hover && source && hover === source && source !== target; + } + } + + var RECONNECT_START = 'reconnectStart', + RECONNECT_END = 'reconnectEnd', + UPDATE_WAYPOINTS = 'updateWaypoints'; + + var MARKER_OK$4 = 'connect-ok', + MARKER_NOT_OK$4 = 'connect-not-ok', + MARKER_CONNECT_HOVER$1 = 'connect-hover', + MARKER_CONNECT_UPDATING$1 = 'djs-updating', + MARKER_ELEMENT_HIDDEN = 'djs-element-hidden'; + + var HIGH_PRIORITY$i = 1100; + + /** + * Preview connection while moving bendpoints. + */ + function BendpointMovePreview(bendpointMove, injector, eventBus, canvas) { + this._injector = injector; + + var connectionPreview = injector.get('connectionPreview', false); + + eventBus.on('bendpoint.move.start', function(event) { + var context = event.context, + bendpointIndex = context.bendpointIndex, + connection = context.connection, + insert = context.insert, + waypoints = connection.waypoints, + newWaypoints = waypoints.slice(); + + context.waypoints = waypoints; + + if (insert) { + + // insert placeholder for new bendpoint + newWaypoints.splice(bendpointIndex, 0, { x: event.x, y: event.y }); + } + + connection.waypoints = newWaypoints; + + // add dragger gfx + var draggerGfx = context.draggerGfx = addBendpoint(canvas.getLayer('overlays')); + + classes(draggerGfx).add('djs-dragging'); + + canvas.addMarker(connection, MARKER_ELEMENT_HIDDEN); + canvas.addMarker(connection, MARKER_CONNECT_UPDATING$1); + }); + + eventBus.on('bendpoint.move.hover', function(event) { + var context = event.context, + allowed = context.allowed, + hover = context.hover, + type = context.type; + + if (hover) { + canvas.addMarker(hover, MARKER_CONNECT_HOVER$1); + + if (type === UPDATE_WAYPOINTS) { + return; + } + + if (allowed) { + canvas.removeMarker(hover, MARKER_NOT_OK$4); + canvas.addMarker(hover, MARKER_OK$4); + } else if (allowed === false) { + canvas.removeMarker(hover, MARKER_OK$4); + canvas.addMarker(hover, MARKER_NOT_OK$4); + } + } + }); + + eventBus.on([ + 'bendpoint.move.out', + 'bendpoint.move.cleanup' + ], HIGH_PRIORITY$i, function(event) { + var context = event.context, + hover = context.hover, + target = context.target; + + if (hover) { + canvas.removeMarker(hover, MARKER_CONNECT_HOVER$1); + canvas.removeMarker(hover, target ? MARKER_OK$4 : MARKER_NOT_OK$4); + } + }); + + eventBus.on('bendpoint.move.move', function(event) { + var context = event.context, + allowed = context.allowed, + bendpointIndex = context.bendpointIndex, + draggerGfx = context.draggerGfx, + hover = context.hover, + type = context.type, + connection = context.connection, + source = connection.source, + target = connection.target, + newWaypoints = connection.waypoints.slice(), + bendpoint = { x: event.x, y: event.y }, + hints = context.hints || {}, + drawPreviewHints = {}; + + if (connectionPreview) { + if (hints.connectionStart) { + drawPreviewHints.connectionStart = hints.connectionStart; + } + + if (hints.connectionEnd) { + drawPreviewHints.connectionEnd = hints.connectionEnd; + } + + + if (type === RECONNECT_START) { + if (isReverse$2(context)) { + drawPreviewHints.connectionEnd = drawPreviewHints.connectionEnd || bendpoint; + + drawPreviewHints.source = target; + drawPreviewHints.target = hover || source; + + newWaypoints = newWaypoints.reverse(); + } else { + drawPreviewHints.connectionStart = drawPreviewHints.connectionStart || bendpoint; + + drawPreviewHints.source = hover || source; + drawPreviewHints.target = target; + } + } else if (type === RECONNECT_END) { + if (isReverse$2(context)) { + drawPreviewHints.connectionStart = drawPreviewHints.connectionStart || bendpoint; + + drawPreviewHints.source = hover || target; + drawPreviewHints.target = source; + + newWaypoints = newWaypoints.reverse(); + } else { + drawPreviewHints.connectionEnd = drawPreviewHints.connectionEnd || bendpoint; + + drawPreviewHints.source = source; + drawPreviewHints.target = hover || target; + } + + } else { + drawPreviewHints.noCropping = true; + drawPreviewHints.noLayout = true; + newWaypoints[ bendpointIndex ] = bendpoint; + } + + if (type === UPDATE_WAYPOINTS) { + newWaypoints = bendpointMove.cropWaypoints(connection, newWaypoints); + } + + drawPreviewHints.waypoints = newWaypoints; + + connectionPreview.drawPreview(context, allowed, drawPreviewHints); + } + + translate$2(draggerGfx, event.x, event.y); + }, this); + + eventBus.on([ + 'bendpoint.move.end', + 'bendpoint.move.cancel' + ], HIGH_PRIORITY$i, function(event) { + var context = event.context, + connection = context.connection, + draggerGfx = context.draggerGfx, + hover = context.hover, + target = context.target, + waypoints = context.waypoints; + + connection.waypoints = waypoints; + + // remove dragger gfx + remove$1(draggerGfx); + + canvas.removeMarker(connection, MARKER_CONNECT_UPDATING$1); + canvas.removeMarker(connection, MARKER_ELEMENT_HIDDEN); + + if (hover) { + canvas.removeMarker(hover, MARKER_OK$4); + canvas.removeMarker(hover, target ? MARKER_OK$4 : MARKER_NOT_OK$4); + } + + if (connectionPreview) { + connectionPreview.cleanUp(context); + } + }); + } + + BendpointMovePreview.$inject = [ + 'bendpointMove', + 'injector', + 'eventBus', + 'canvas' + ]; + + var MARKER_CONNECT_HOVER = 'connect-hover', + MARKER_CONNECT_UPDATING = 'djs-updating'; + + + function axisAdd(point, axis, delta) { + return axisSet(point, axis, point[axis] + delta); + } + + function axisSet(point, axis, value) { + return { + x: (axis === 'x' ? value : point.x), + y: (axis === 'y' ? value : point.y) + }; + } + + function axisFenced(position, segmentStart, segmentEnd, axis) { + + var maxValue = Math.max(segmentStart[axis], segmentEnd[axis]), + minValue = Math.min(segmentStart[axis], segmentEnd[axis]); + + var padding = 20; + + var fencedValue = Math.min(Math.max(minValue + padding, position[axis]), maxValue - padding); + + return axisSet(segmentStart, axis, fencedValue); + } + + function flipAxis(axis) { + return axis === 'x' ? 'y' : 'x'; + } + + /** + * Get the docking point on the given element. + * + * Compute a reasonable docking, if non exists. + * + * @param {Point} point + * @param {djs.model.Shape} referenceElement + * @param {string} moveAxis (x|y) + * + * @return {Point} + */ + function getDocking$2(point, referenceElement, moveAxis) { + + var referenceMid, + inverseAxis; + + if (point.original) { + return point.original; + } else { + referenceMid = getMid(referenceElement); + inverseAxis = flipAxis(moveAxis); + + return axisSet(point, inverseAxis, referenceMid[inverseAxis]); + } + } + + /** + * A component that implements moving of bendpoints + */ + function ConnectionSegmentMove( + injector, eventBus, canvas, + dragging, graphicsFactory, modeling) { + + // optional connection docking integration + var connectionDocking = injector.get('connectionDocking', false); + + + // API + + this.start = function(event, connection, idx) { + + var context, + gfx = canvas.getGraphics(connection), + segmentStartIndex = idx - 1, + segmentEndIndex = idx, + waypoints = connection.waypoints, + segmentStart = waypoints[segmentStartIndex], + segmentEnd = waypoints[segmentEndIndex], + intersection = getConnectionIntersection(canvas, waypoints, event), + direction, axis, dragPosition; + + direction = pointsAligned(segmentStart, segmentEnd); + + // do not move diagonal connection + if (!direction) { + return; + } + + // the axis where we are going to move things + axis = direction === 'v' ? 'x' : 'y'; + + if (segmentStartIndex === 0) { + segmentStart = getDocking$2(segmentStart, connection.source, axis); + } + + if (segmentEndIndex === waypoints.length - 1) { + segmentEnd = getDocking$2(segmentEnd, connection.target, axis); + } + + if (intersection) { + dragPosition = intersection.point; + } else { + + // set to segment center as default + dragPosition = { + x: (segmentStart.x + segmentEnd.x) / 2, + y: (segmentStart.y + segmentEnd.y) / 2 + }; + } + + context = { + connection: connection, + segmentStartIndex: segmentStartIndex, + segmentEndIndex: segmentEndIndex, + segmentStart: segmentStart, + segmentEnd: segmentEnd, + axis: axis, + dragPosition: dragPosition + }; + + dragging.init(event, dragPosition, 'connectionSegment.move', { + cursor: axis === 'x' ? 'resize-ew' : 'resize-ns', + data: { + connection: connection, + connectionGfx: gfx, + context: context + } + }); + }; + + /** + * Crop connection if connection cropping is provided. + * + * @param {Connection} connection + * @param {Array} newWaypoints + * + * @return {Array} cropped connection waypoints + */ + function cropConnection(connection, newWaypoints) { + + // crop connection, if docking service is provided only + if (!connectionDocking) { + return newWaypoints; + } + + var oldWaypoints = connection.waypoints, + croppedWaypoints; + + // temporary set new waypoints + connection.waypoints = newWaypoints; + + croppedWaypoints = connectionDocking.getCroppedWaypoints(connection); + + // restore old waypoints + connection.waypoints = oldWaypoints; + + return croppedWaypoints; + } + + // DRAGGING IMPLEMENTATION + + function redrawConnection(data) { + graphicsFactory.update('connection', data.connection, data.connectionGfx); + } + + function updateDragger(context, segmentOffset, event) { + + var newWaypoints = context.newWaypoints, + segmentStartIndex = context.segmentStartIndex + segmentOffset, + segmentStart = newWaypoints[segmentStartIndex], + segmentEndIndex = context.segmentEndIndex + segmentOffset, + segmentEnd = newWaypoints[segmentEndIndex], + axis = flipAxis(context.axis); + + // make sure the dragger does not move + // outside the connection + var draggerPosition = axisFenced(event, segmentStart, segmentEnd, axis); + + // update dragger + translate$2(context.draggerGfx, draggerPosition.x, draggerPosition.y); + } + + /** + * Filter waypoints for redundant ones (i.e. on the same axis). + * Returns the filtered waypoints and the offset related to the segment move. + * + * @param {Array} waypoints + * @param {Integer} segmentStartIndex of moved segment start + * + * @return {Object} { filteredWaypoints, segmentOffset } + */ + function filterRedundantWaypoints(waypoints, segmentStartIndex) { + + var segmentOffset = 0; + + var filteredWaypoints = waypoints.filter(function(r, idx) { + if (pointsOnLine(waypoints[idx - 1], waypoints[idx + 1], r)) { + + // remove point and increment offset + segmentOffset = idx <= segmentStartIndex ? segmentOffset - 1 : segmentOffset; + return false; + } + + // dont remove point + return true; + }); + + return { + waypoints: filteredWaypoints, + segmentOffset: segmentOffset + }; + } + + eventBus.on('connectionSegment.move.start', function(event) { + + var context = event.context, + connection = event.connection, + layer = canvas.getLayer('overlays'); + + context.originalWaypoints = connection.waypoints.slice(); + + // add dragger gfx + context.draggerGfx = addSegmentDragger(layer, context.segmentStart, context.segmentEnd); + classes(context.draggerGfx).add('djs-dragging'); + + canvas.addMarker(connection, MARKER_CONNECT_UPDATING); + }); + + eventBus.on('connectionSegment.move.move', function(event) { + + var context = event.context, + connection = context.connection, + segmentStartIndex = context.segmentStartIndex, + segmentEndIndex = context.segmentEndIndex, + segmentStart = context.segmentStart, + segmentEnd = context.segmentEnd, + axis = context.axis; + + var newWaypoints = context.originalWaypoints.slice(), + newSegmentStart = axisAdd(segmentStart, axis, event['d' + axis]), + newSegmentEnd = axisAdd(segmentEnd, axis, event['d' + axis]); + + // original waypoint count and added / removed + // from start waypoint delta. We use the later + // to retrieve the updated segmentStartIndex / segmentEndIndex + var waypointCount = newWaypoints.length, + segmentOffset = 0; + + // move segment start / end by axis delta + newWaypoints[segmentStartIndex] = newSegmentStart; + newWaypoints[segmentEndIndex] = newSegmentEnd; + + var sourceToSegmentOrientation, + targetToSegmentOrientation; + + // handle first segment + if (segmentStartIndex < 2) { + sourceToSegmentOrientation = getOrientation(connection.source, newSegmentStart); + + // first bendpoint, remove first segment if intersecting + if (segmentStartIndex === 1) { + + if (sourceToSegmentOrientation === 'intersect') { + newWaypoints.shift(); + newWaypoints[0] = newSegmentStart; + segmentOffset--; + } + } + + // docking point, add segment if not intersecting anymore + else { + if (sourceToSegmentOrientation !== 'intersect') { + newWaypoints.unshift(segmentStart); + segmentOffset++; + } + } + } + + // handle last segment + if (segmentEndIndex > waypointCount - 3) { + targetToSegmentOrientation = getOrientation(connection.target, newSegmentEnd); + + // last bendpoint, remove last segment if intersecting + if (segmentEndIndex === waypointCount - 2) { + + if (targetToSegmentOrientation === 'intersect') { + newWaypoints.pop(); + newWaypoints[newWaypoints.length - 1] = newSegmentEnd; + } + } + + // last bendpoint, remove last segment if intersecting + else { + if (targetToSegmentOrientation !== 'intersect') { + newWaypoints.push(segmentEnd); + } + } + } + + // update connection waypoints + context.newWaypoints = connection.waypoints = cropConnection(connection, newWaypoints); + + // update dragger position + updateDragger(context, segmentOffset, event); + + // save segmentOffset in context + context.newSegmentStartIndex = segmentStartIndex + segmentOffset; + + // redraw connection + redrawConnection(event); + }); + + eventBus.on('connectionSegment.move.hover', function(event) { + + event.context.hover = event.hover; + canvas.addMarker(event.hover, MARKER_CONNECT_HOVER); + }); + + eventBus.on([ + 'connectionSegment.move.out', + 'connectionSegment.move.cleanup' + ], function(event) { + + // remove connect marker + // if it was added + var hover = event.context.hover; + + if (hover) { + canvas.removeMarker(hover, MARKER_CONNECT_HOVER); + } + }); + + eventBus.on('connectionSegment.move.cleanup', function(event) { + + var context = event.context, + connection = context.connection; + + // remove dragger gfx + if (context.draggerGfx) { + remove$1(context.draggerGfx); + } + + canvas.removeMarker(connection, MARKER_CONNECT_UPDATING); + }); + + eventBus.on([ + 'connectionSegment.move.cancel', + 'connectionSegment.move.end' + ], function(event) { + var context = event.context, + connection = context.connection; + + connection.waypoints = context.originalWaypoints; + + redrawConnection(event); + }); + + eventBus.on('connectionSegment.move.end', function(event) { + + var context = event.context, + connection = context.connection, + newWaypoints = context.newWaypoints, + newSegmentStartIndex = context.newSegmentStartIndex; + + // ensure we have actual pixel values bendpoint + // coordinates (important when zoom level was > 1 during move) + newWaypoints = newWaypoints.map(function(p) { + return { + original: p.original, + x: Math.round(p.x), + y: Math.round(p.y) + }; + }); + + // apply filter redunant waypoints + var filtered = filterRedundantWaypoints(newWaypoints, newSegmentStartIndex); + + // get filtered waypoints + var filteredWaypoints = filtered.waypoints, + croppedWaypoints = cropConnection(connection, filteredWaypoints), + segmentOffset = filtered.segmentOffset; + + var hints = { + segmentMove: { + segmentStartIndex: context.segmentStartIndex, + newSegmentStartIndex: newSegmentStartIndex + segmentOffset + } + }; + + modeling.updateWaypoints(connection, croppedWaypoints, hints); + }); + } + + ConnectionSegmentMove.$inject = [ + 'injector', + 'eventBus', + 'canvas', + 'dragging', + 'graphicsFactory', + 'modeling' + ]; + + var abs$6 = Math.abs, + round$7 = Math.round; + + + /** + * Snap value to a collection of reference values. + * + * @param {number} value + * @param {Array} values + * @param {number} [tolerance=10] + * + * @return {number} the value we snapped to or null, if none snapped + */ + function snapTo(value, values, tolerance) { + tolerance = tolerance === undefined ? 10 : tolerance; + + var idx, snapValue; + + for (idx = 0; idx < values.length; idx++) { + snapValue = values[idx]; + + if (abs$6(snapValue - value) <= tolerance) { + return snapValue; + } + } + } + + + function topLeft(bounds) { + return { + x: bounds.x, + y: bounds.y + }; + } + + function bottomRight(bounds) { + return { + x: bounds.x + bounds.width, + y: bounds.y + bounds.height + }; + } + + function mid$2(bounds, defaultValue) { + + if (!bounds || isNaN(bounds.x) || isNaN(bounds.y)) { + return defaultValue; + } + + return { + x: round$7(bounds.x + bounds.width / 2), + y: round$7(bounds.y + bounds.height / 2) + }; + } + + + /** + * Retrieve the snap state of the given event. + * + * @param {Event} event + * @param {string} axis + * + * @return {boolean} the snapped state + * + */ + function isSnapped(event, axis) { + var snapped = event.snapped; + + if (!snapped) { + return false; + } + + if (typeof axis === 'string') { + return snapped[axis]; + } + + return snapped.x && snapped.y; + } + + + /** + * Set the given event as snapped. + * + * This method may change the x and/or y position of the shape + * from the given event! + * + * @param {Event} event + * @param {string} axis + * @param {number|boolean} value + * + * @return {number} old value + */ + function setSnapped(event, axis, value) { + if (typeof axis !== 'string') { + throw new Error('axis must be in [x, y]'); + } + + if (typeof value !== 'number' && value !== false) { + throw new Error('value must be Number or false'); + } + + var delta, + previousValue = event[axis]; + + var snapped = event.snapped = (event.snapped || {}); + + + if (value === false) { + snapped[axis] = false; + } else { + snapped[axis] = true; + + delta = value - previousValue; + + event[axis] += delta; + event['d' + axis] += delta; + } + + return previousValue; + } + + /** + * Get children of a shape. + * + * @param {djs.model.Shape} parent + * + * @returns {Array} + */ + function getChildren(parent) { + return parent.children || []; + } + + var abs$5 = Math.abs, + round$6 = Math.round; + + var TOLERANCE = 10; + + + function BendpointSnapping(eventBus) { + + function snapTo(values, value) { + + if (isArray$3(values)) { + var i = values.length; + + while (i--) if (abs$5(values[i] - value) <= TOLERANCE) { + return values[i]; + } + } else { + values = +values; + var rem = value % values; + + if (rem < TOLERANCE) { + return value - rem; + } + + if (rem > values - TOLERANCE) { + return value - rem + values; + } + } + + return value; + } + + function getSnapPoint(element, event) { + + if (element.waypoints) { + return getClosestPointOnConnection(event, element); + } + + if (element.width) { + return { + x: round$6(element.width / 2 + element.x), + y: round$6(element.height / 2 + element.y) + }; + } + } + + // connection segment snapping ////////////////////// + + function getConnectionSegmentSnaps(event) { + + var context = event.context, + snapPoints = context.snapPoints, + connection = context.connection, + waypoints = connection.waypoints, + segmentStart = context.segmentStart, + segmentStartIndex = context.segmentStartIndex, + segmentEnd = context.segmentEnd, + segmentEndIndex = context.segmentEndIndex, + axis = context.axis; + + if (snapPoints) { + return snapPoints; + } + + var referenceWaypoints = [ + waypoints[segmentStartIndex - 1], + segmentStart, + segmentEnd, + waypoints[segmentEndIndex + 1] + ]; + + if (segmentStartIndex < 2) { + referenceWaypoints.unshift(getSnapPoint(connection.source, event)); + } + + if (segmentEndIndex > waypoints.length - 3) { + referenceWaypoints.unshift(getSnapPoint(connection.target, event)); + } + + context.snapPoints = snapPoints = { horizontal: [] , vertical: [] }; + + forEach$1(referenceWaypoints, function(p) { + + // we snap on existing bendpoints only, + // not placeholders that are inserted during add + if (p) { + p = p.original || p; + + if (axis === 'y') { + snapPoints.horizontal.push(p.y); + } + + if (axis === 'x') { + snapPoints.vertical.push(p.x); + } + } + }); + + return snapPoints; + } + + eventBus.on('connectionSegment.move.move', 1500, function(event) { + var snapPoints = getConnectionSegmentSnaps(event), + x = event.x, + y = event.y, + sx, sy; + + if (!snapPoints) { + return; + } + + // snap + sx = snapTo(snapPoints.vertical, x); + sy = snapTo(snapPoints.horizontal, y); + + + // correction x/y + var cx = (x - sx), + cy = (y - sy); + + // update delta + assign(event, { + dx: event.dx - cx, + dy: event.dy - cy, + x: sx, + y: sy + }); + + // only set snapped if actually snapped + if (cx || snapPoints.vertical.indexOf(x) !== -1) { + setSnapped(event, 'x', sx); + } + + if (cy || snapPoints.horizontal.indexOf(y) !== -1) { + setSnapped(event, 'y', sy); + } + }); + + + // bendpoint snapping ////////////////////// + + function getBendpointSnaps(context) { + + var snapPoints = context.snapPoints, + waypoints = context.connection.waypoints, + bendpointIndex = context.bendpointIndex; + + if (snapPoints) { + return snapPoints; + } + + var referenceWaypoints = [ waypoints[bendpointIndex - 1], waypoints[bendpointIndex + 1] ]; + + context.snapPoints = snapPoints = { horizontal: [] , vertical: [] }; + + forEach$1(referenceWaypoints, function(p) { + + // we snap on existing bendpoints only, + // not placeholders that are inserted during add + if (p) { + p = p.original || p; + + snapPoints.horizontal.push(p.y); + snapPoints.vertical.push(p.x); + } + }); + + return snapPoints; + } + + // Snap Endpoint of new connection + eventBus.on([ + 'connect.hover', + 'connect.move', + 'connect.end' + ], 1500, function(event) { + var context = event.context, + hover = context.hover, + hoverMid = hover && getSnapPoint(hover, event); + + // only snap on connections, elements can have multiple connect endpoints + if (!isConnection$d(hover) || !hoverMid || !hoverMid.x || !hoverMid.y) { + return; + } + + setSnapped(event, 'x', hoverMid.x); + setSnapped(event, 'y', hoverMid.y); + }); + + eventBus.on([ 'bendpoint.move.move', 'bendpoint.move.end' ], 1500, function(event) { + + var context = event.context, + snapPoints = getBendpointSnaps(context), + hover = context.hover, + hoverMid = hover && getSnapPoint(hover, event), + x = event.x, + y = event.y, + sx, sy; + + if (!snapPoints) { + return; + } + + // snap to hover mid + sx = snapTo(hoverMid ? snapPoints.vertical.concat([ hoverMid.x ]) : snapPoints.vertical, x); + sy = snapTo(hoverMid ? snapPoints.horizontal.concat([ hoverMid.y ]) : snapPoints.horizontal, y); + + // correction x/y + var cx = (x - sx), + cy = (y - sy); + + // update delta + assign(event, { + dx: event.dx - cx, + dy: event.dy - cy, + x: event.x - cx, + y: event.y - cy + }); + + // only set snapped if actually snapped + if (cx || snapPoints.vertical.indexOf(x) !== -1) { + setSnapped(event, 'x', sx); + } + + if (cy || snapPoints.horizontal.indexOf(y) !== -1) { + setSnapped(event, 'y', sy); + } + }); + } + + + BendpointSnapping.$inject = [ 'eventBus' ]; + + + // helpers ////////////////////// + + function isConnection$d(element) { + return element && !!element.waypoints; + } + + var BendpointsModule = { + __depends__: [ + DraggingModule, + RulesModule$1 + ], + __init__: [ 'bendpoints', 'bendpointSnapping', 'bendpointMovePreview' ], + bendpoints: [ 'type', Bendpoints ], + bendpointMove: [ 'type', BendpointMove ], + bendpointMovePreview: [ 'type', BendpointMovePreview ], + connectionSegmentMove: [ 'type', ConnectionSegmentMove ], + bendpointSnapping: [ 'type', BendpointSnapping ] + }; + + function Connect(eventBus, dragging, modeling, rules) { + + // rules + + function canConnect(source, target) { + return rules.allowed('connection.create', { + source: source, + target: target + }); + } + + function canConnectReverse(source, target) { + return canConnect(target, source); + } + + + // event handlers + + eventBus.on('connect.hover', function(event) { + var context = event.context, + start = context.start, + hover = event.hover, + canExecute; + + // cache hover state + context.hover = hover; + + canExecute = context.canExecute = canConnect(start, hover); + + // ignore hover + if (isNil(canExecute)) { + return; + } + + if (canExecute !== false) { + context.source = start; + context.target = hover; + + return; + } + + canExecute = context.canExecute = canConnectReverse(start, hover); + + // ignore hover + if (isNil(canExecute)) { + return; + } + + if (canExecute !== false) { + context.source = hover; + context.target = start; + } + }); + + eventBus.on([ 'connect.out', 'connect.cleanup' ], function(event) { + var context = event.context; + + context.hover = null; + context.source = null; + context.target = null; + + context.canExecute = false; + }); + + eventBus.on('connect.end', function(event) { + var context = event.context, + canExecute = context.canExecute, + connectionStart = context.connectionStart, + connectionEnd = { + x: event.x, + y: event.y + }, + source = context.source, + target = context.target; + + if (!canExecute) { + return false; + } + + var attrs = null, + hints = { + connectionStart: isReverse$1(context) ? connectionEnd : connectionStart, + connectionEnd: isReverse$1(context) ? connectionStart : connectionEnd + }; + + if (isObject(canExecute)) { + attrs = canExecute; + } + + context.connection = modeling.connect(source, target, attrs, hints); + }); + + + // API + + /** + * Start connect operation. + * + * @param {DOMEvent} event + * @param {djs.model.Base} start + * @param {Point} [connectionStart] + * @param {boolean} [autoActivate=false] + */ + this.start = function(event, start, connectionStart, autoActivate) { + if (!isObject(connectionStart)) { + autoActivate = connectionStart; + connectionStart = getMid(start); + } + + dragging.init(event, 'connect', { + autoActivate: autoActivate, + data: { + shape: start, + context: { + start: start, + connectionStart: connectionStart + } + } + }); + }; + } + + Connect.$inject = [ + 'eventBus', + 'dragging', + 'modeling', + 'rules' + ]; + + + // helpers ////////// + + function isReverse$1(context) { + var hover = context.hover, + source = context.source, + target = context.target; + + return hover && source && hover === source && source !== target; + } + + var HIGH_PRIORITY$h = 1100, + LOW_PRIORITY$j = 900; + + var MARKER_OK$3 = 'connect-ok', + MARKER_NOT_OK$3 = 'connect-not-ok'; + + /** + * Shows connection preview during connect. + * + * @param {didi.Injector} injector + * @param {EventBus} eventBus + * @param {Canvas} canvas + */ + function ConnectPreview(injector, eventBus, canvas) { + var connectionPreview = injector.get('connectionPreview', false); + + connectionPreview && eventBus.on('connect.move', function(event) { + var context = event.context, + canConnect = context.canExecute, + hover = context.hover, + source = context.source, + start = context.start, + startPosition = context.startPosition, + target = context.target, + connectionStart = context.connectionStart || startPosition, + connectionEnd = context.connectionEnd || { + x: event.x, + y: event.y + }, + previewStart = connectionStart, + previewEnd = connectionEnd; + + if (isReverse$1(context)) { + previewStart = connectionEnd; + previewEnd = connectionStart; + } + + connectionPreview.drawPreview(context, canConnect, { + source: source || start, + target: target || hover, + connectionStart: previewStart, + connectionEnd: previewEnd + }); + }); + + eventBus.on('connect.hover', LOW_PRIORITY$j, function(event) { + var context = event.context, + hover = event.hover, + canExecute = context.canExecute; + + // ignore hover + if (canExecute === null) { + return; + } + + canvas.addMarker(hover, canExecute ? MARKER_OK$3 : MARKER_NOT_OK$3); + }); + + eventBus.on([ + 'connect.out', + 'connect.cleanup' + ], HIGH_PRIORITY$h, function(event) { + var hover = event.hover; + + if (hover) { + canvas.removeMarker(hover, MARKER_OK$3); + canvas.removeMarker(hover, MARKER_NOT_OK$3); + } + }); + + connectionPreview && eventBus.on('connect.cleanup', function(event) { + connectionPreview.cleanUp(event.context); + }); + } + + ConnectPreview.$inject = [ + 'injector', + 'eventBus', + 'canvas' + ]; + + var ConnectModule = { + __depends__: [ + SelectionModule, + RulesModule$1, + DraggingModule + ], + __init__: [ + 'connectPreview' + ], + connect: [ 'type', Connect ], + connectPreview: [ 'type', ConnectPreview ] + }; + + var MARKER_CONNECTION_PREVIEW = 'djs-connection-preview'; + + /** + * Draws connection preview. Optionally, this can use layouter and connection docking to draw + * better looking previews. + * + * @param {didi.Injector} injector + * @param {Canvas} canvas + * @param {GraphicsFactory} graphicsFactory + * @param {ElementFactory} elementFactory + */ + function ConnectionPreview( + injector, + canvas, + graphicsFactory, + elementFactory + ) { + this._canvas = canvas; + this._graphicsFactory = graphicsFactory; + this._elementFactory = elementFactory; + + // optional components + this._connectionDocking = injector.get('connectionDocking', false); + this._layouter = injector.get('layouter', false); + } + + ConnectionPreview.$inject = [ + 'injector', + 'canvas', + 'graphicsFactory', + 'elementFactory' + ]; + + /** + * Draw connection preview. + * + * Provide at least one of and to create a preview. + * In the clean up stage, call `connectionPreview#cleanUp` with the context to remove preview. + * + * @param {Object} context + * @param {Object|boolean} canConnect + * @param {Object} hints + * @param {djs.model.shape} [hints.source] source element + * @param {djs.model.shape} [hints.target] target element + * @param {Point} [hints.connectionStart] connection preview start + * @param {Point} [hints.connectionEnd] connection preview end + * @param {Array} [hints.waypoints] provided waypoints for preview + * @param {boolean} [hints.noLayout] true if preview should not be laid out + * @param {boolean} [hints.noCropping] true if preview should not be cropped + * @param {boolean} [hints.noNoop] true if simple connection should not be drawn + */ + ConnectionPreview.prototype.drawPreview = function(context, canConnect, hints) { + + hints = hints || {}; + + var connectionPreviewGfx = context.connectionPreviewGfx, + getConnection = context.getConnection, + source = hints.source, + target = hints.target, + waypoints = hints.waypoints, + connectionStart = hints.connectionStart, + connectionEnd = hints.connectionEnd, + noLayout = hints.noLayout, + noCropping = hints.noCropping, + noNoop = hints.noNoop, + connection; + + var self = this; + + if (!connectionPreviewGfx) { + connectionPreviewGfx = context.connectionPreviewGfx = this.createConnectionPreviewGfx(); + } + + clear(connectionPreviewGfx); + + if (!getConnection) { + getConnection = context.getConnection = cacheReturnValues(function(canConnect, source, target) { + return self.getConnection(canConnect, source, target); + }); + } + + if (canConnect) { + connection = getConnection(canConnect, source, target); + } + + if (!connection) { + !noNoop && this.drawNoopPreview(connectionPreviewGfx, hints); + return; + } + + connection.waypoints = waypoints || []; + + // optional layout + if (this._layouter && !noLayout) { + connection.waypoints = this._layouter.layoutConnection(connection, { + source: source, + target: target, + connectionStart: connectionStart, + connectionEnd: connectionEnd, + waypoints: hints.waypoints || connection.waypoints + }); + } + + // fallback if no waypoints were provided nor created with layouter + if (!connection.waypoints || !connection.waypoints.length) { + connection.waypoints = [ + source ? getMid(source) : connectionStart, + target ? getMid(target) : connectionEnd + ]; + } + + // optional cropping + if (this._connectionDocking && (source || target) && !noCropping) { + connection.waypoints = this._connectionDocking.getCroppedWaypoints(connection, source, target); + } + + this._graphicsFactory.drawConnection(connectionPreviewGfx, connection); + }; + + /** + * Draw simple connection between source and target or provided points. + * + * @param {SVGElement} connectionPreviewGfx container for the connection + * @param {Object} hints + * @param {djs.model.shape} [hints.source] source element + * @param {djs.model.shape} [hints.target] target element + * @param {Point} [hints.connectionStart] required if source is not provided + * @param {Point} [hints.connectionEnd] required if target is not provided + */ + ConnectionPreview.prototype.drawNoopPreview = function(connectionPreviewGfx, hints) { + var source = hints.source, + target = hints.target, + start = hints.connectionStart || getMid(source), + end = hints.connectionEnd || getMid(target); + + var waypoints = this.cropWaypoints(start, end, source, target); + + var connection = this.createNoopConnection(waypoints[0], waypoints[1]); + + append(connectionPreviewGfx, connection); + }; + + /** + * Return cropped waypoints. + * + * @param {Point} start + * @param {Point} end + * @param {djs.model.shape} source + * @param {djs.model.shape} target + * + * @returns {Array} + */ + ConnectionPreview.prototype.cropWaypoints = function(start, end, source, target) { + var graphicsFactory = this._graphicsFactory, + sourcePath = source && graphicsFactory.getShapePath(source), + targetPath = target && graphicsFactory.getShapePath(target), + connectionPath = graphicsFactory.getConnectionPath({ waypoints: [ start, end ] }); + + start = (source && getElementLineIntersection(sourcePath, connectionPath, true)) || start; + end = (target && getElementLineIntersection(targetPath, connectionPath, false)) || end; + + return [ start, end ]; + }; + + /** + * Remove connection preview container if it exists. + * + * @param {Object} [context] + * @param {SVGElement} [context.connectionPreviewGfx] preview container + */ + ConnectionPreview.prototype.cleanUp = function(context) { + if (context && context.connectionPreviewGfx) { + remove$1(context.connectionPreviewGfx); + } + }; + + /** + * Get connection that connects source and target. + * + * @param {Object|boolean} canConnect + * + * @returns {djs.model.connection} + */ + ConnectionPreview.prototype.getConnection = function(canConnect) { + var attrs = ensureConnectionAttrs(canConnect); + + return this._elementFactory.createConnection(attrs); + }; + + + /** + * Add and return preview graphics. + * + * @returns {SVGElement} + */ + ConnectionPreview.prototype.createConnectionPreviewGfx = function() { + var gfx = create$1('g'); + + attr(gfx, { + pointerEvents: 'none' + }); + + classes(gfx).add(MARKER_CONNECTION_PREVIEW); + + append(this._canvas.getActiveLayer(), gfx); + + return gfx; + }; + + /** + * Create and return simple connection. + * + * @param {Point} start + * @param {Point} end + * + * @returns {SVGElement} + */ + ConnectionPreview.prototype.createNoopConnection = function(start, end) { + var connection = create$1('polyline'); + + attr(connection, { + 'stroke': '#333', + 'strokeDasharray': [ 1 ], + 'strokeWidth': 2, + 'pointer-events': 'none' + }); + + attr(connection, { 'points': [ start.x, start.y, end.x, end.y ] }); + + return connection; + }; + + // helpers ////////// + + /** + * Returns function that returns cached return values referenced by stringified first argument. + * + * @param {Function} fn + * + * @return {Function} + */ + function cacheReturnValues(fn) { + var returnValues = {}; + + /** + * Return cached return value referenced by stringified first argument. + * + * @returns {*} + */ + return function(firstArgument) { + var key = JSON.stringify(firstArgument); + + var returnValue = returnValues[key]; + + if (!returnValue) { + returnValue = returnValues[key] = fn.apply(null, arguments); + } + + return returnValue; + }; + } + + /** + * Ensure connection attributes is object. + * + * @param {Object|boolean} canConnect + * + * @returns {Object} + */ + function ensureConnectionAttrs(canConnect) { + if (isObject(canConnect)) { + return canConnect; + } else { + return {}; + } + } + + var ConnectionPreviewModule = { + __init__: [ 'connectionPreview' ], + connectionPreview: [ 'type', ConnectionPreview ] + }; + + var min$3 = Math.min, + max$5 = Math.max; + + function preventDefault(e) { + e.preventDefault(); + } + + function stopPropagation(e) { + e.stopPropagation(); + } + + function isTextNode(node) { + return node.nodeType === Node.TEXT_NODE; + } + + function toArray(nodeList) { + return [].slice.call(nodeList); + } + + /** + * Initializes a container for a content editable div. + * + * Structure: + * + * container + * parent + * content + * resize-handle + * + * @param {object} options + * @param {DOMElement} options.container The DOM element to append the contentContainer to + * @param {Function} options.keyHandler Handler for key events + * @param {Function} options.resizeHandler Handler for resize events + */ + function TextBox(options) { + this.container = options.container; + + this.parent = domify( + '
    ' + + '
    ' + + '
    ' + ); + + this.content = query('[contenteditable]', this.parent); + + this.keyHandler = options.keyHandler || function() {}; + this.resizeHandler = options.resizeHandler || function() {}; + + this.autoResize = bind(this.autoResize, this); + this.handlePaste = bind(this.handlePaste, this); + } + + + /** + * Create a text box with the given position, size, style and text content + * + * @param {Object} bounds + * @param {Number} bounds.x absolute x position + * @param {Number} bounds.y absolute y position + * @param {Number} [bounds.width] fixed width value + * @param {Number} [bounds.height] fixed height value + * @param {Number} [bounds.maxWidth] maximum width value + * @param {Number} [bounds.maxHeight] maximum height value + * @param {Number} [bounds.minWidth] minimum width value + * @param {Number} [bounds.minHeight] minimum height value + * @param {Object} [style] + * @param {String} value text content + * + * @return {DOMElement} The created content DOM element + */ + TextBox.prototype.create = function(bounds, style, value, options) { + var self = this; + + var parent = this.parent, + content = this.content, + container = this.container; + + options = this.options = options || {}; + + style = this.style = style || {}; + + var parentStyle = pick(style, [ + 'width', + 'height', + 'maxWidth', + 'maxHeight', + 'minWidth', + 'minHeight', + 'left', + 'top', + 'backgroundColor', + 'position', + 'overflow', + 'border', + 'wordWrap', + 'textAlign', + 'outline', + 'transform' + ]); + + assign(parent.style, { + width: bounds.width + 'px', + height: bounds.height + 'px', + maxWidth: bounds.maxWidth + 'px', + maxHeight: bounds.maxHeight + 'px', + minWidth: bounds.minWidth + 'px', + minHeight: bounds.minHeight + 'px', + left: bounds.x + 'px', + top: bounds.y + 'px', + backgroundColor: '#ffffff', + position: 'absolute', + overflow: 'visible', + border: '1px solid #ccc', + boxSizing: 'border-box', + wordWrap: 'normal', + textAlign: 'center', + outline: 'none' + }, parentStyle); + + var contentStyle = pick(style, [ + 'fontFamily', + 'fontSize', + 'fontWeight', + 'lineHeight', + 'padding', + 'paddingTop', + 'paddingRight', + 'paddingBottom', + 'paddingLeft' + ]); + + assign(content.style, { + boxSizing: 'border-box', + width: '100%', + outline: 'none', + wordWrap: 'break-word' + }, contentStyle); + + if (options.centerVertically) { + assign(content.style, { + position: 'absolute', + top: '50%', + transform: 'translate(0, -50%)' + }, contentStyle); + } + + content.innerText = value; + + componentEvent.bind(content, 'keydown', this.keyHandler); + componentEvent.bind(content, 'mousedown', stopPropagation); + componentEvent.bind(content, 'paste', self.handlePaste); + + if (options.autoResize) { + componentEvent.bind(content, 'input', this.autoResize); + } + + if (options.resizable) { + this.resizable(style); + } + + container.appendChild(parent); + + // set selection to end of text + this.setSelection(content.lastChild, content.lastChild && content.lastChild.length); + + return parent; + }; + + /** + * Intercept paste events to remove formatting from pasted text. + */ + TextBox.prototype.handlePaste = function(e) { + var options = this.options, + style = this.style; + + e.preventDefault(); + + var text; + + if (e.clipboardData) { + + // Chrome, Firefox, Safari + text = e.clipboardData.getData('text/plain'); + } else { + + // Internet Explorer + text = window.clipboardData.getData('Text'); + } + + this.insertText(text); + + if (options.autoResize) { + var hasResized = this.autoResize(style); + + if (hasResized) { + this.resizeHandler(hasResized); + } + } + }; + + TextBox.prototype.insertText = function(text) { + text = normalizeEndOfLineSequences(text); + + // insertText command not supported by Internet Explorer + var success = document.execCommand('insertText', false, text); + + if (success) { + return; + } + + this._insertTextIE(text); + }; + + TextBox.prototype._insertTextIE = function(text) { + + // Internet Explorer + var range = this.getSelection(), + startContainer = range.startContainer, + endContainer = range.endContainer, + startOffset = range.startOffset, + endOffset = range.endOffset, + commonAncestorContainer = range.commonAncestorContainer; + + var childNodesArray = toArray(commonAncestorContainer.childNodes); + + var container, + offset; + + if (isTextNode(commonAncestorContainer)) { + var containerTextContent = startContainer.textContent; + + startContainer.textContent = + containerTextContent.substring(0, startOffset) + + text + + containerTextContent.substring(endOffset); + + container = startContainer; + offset = startOffset + text.length; + + } else if (startContainer === this.content && endContainer === this.content) { + var textNode = document.createTextNode(text); + + this.content.insertBefore(textNode, childNodesArray[startOffset]); + + container = textNode; + offset = textNode.textContent.length; + } else { + var startContainerChildIndex = childNodesArray.indexOf(startContainer), + endContainerChildIndex = childNodesArray.indexOf(endContainer); + + childNodesArray.forEach(function(childNode, index) { + + if (index === startContainerChildIndex) { + childNode.textContent = + startContainer.textContent.substring(0, startOffset) + + text + + endContainer.textContent.substring(endOffset); + } else if (index > startContainerChildIndex && index <= endContainerChildIndex) { + remove$2(childNode); + } + }); + + container = startContainer; + offset = startOffset + text.length; + } + + if (container && offset !== undefined) { + + // is necessary in Internet Explorer + setTimeout(function() { + self.setSelection(container, offset); + }); + } + }; + + /** + * Automatically resize element vertically to fit its content. + */ + TextBox.prototype.autoResize = function() { + var parent = this.parent, + content = this.content; + + var fontSize = parseInt(this.style.fontSize) || 12; + + if (content.scrollHeight > parent.offsetHeight || + content.scrollHeight < parent.offsetHeight - fontSize) { + var bounds = parent.getBoundingClientRect(); + + var height = content.scrollHeight; + parent.style.height = height + 'px'; + + this.resizeHandler({ + width: bounds.width, + height: bounds.height, + dx: 0, + dy: height - bounds.height + }); + } + }; + + /** + * Make an element resizable by adding a resize handle. + */ + TextBox.prototype.resizable = function() { + var self = this; + + var parent = this.parent, + resizeHandle = this.resizeHandle; + + var minWidth = parseInt(this.style.minWidth) || 0, + minHeight = parseInt(this.style.minHeight) || 0, + maxWidth = parseInt(this.style.maxWidth) || Infinity, + maxHeight = parseInt(this.style.maxHeight) || Infinity; + + if (!resizeHandle) { + resizeHandle = this.resizeHandle = domify( + '
    ' + ); + + var startX, startY, startWidth, startHeight; + + var onMouseDown = function(e) { + preventDefault(e); + stopPropagation(e); + + startX = e.clientX; + startY = e.clientY; + + var bounds = parent.getBoundingClientRect(); + + startWidth = bounds.width; + startHeight = bounds.height; + + componentEvent.bind(document, 'mousemove', onMouseMove); + componentEvent.bind(document, 'mouseup', onMouseUp); + }; + + var onMouseMove = function(e) { + preventDefault(e); + stopPropagation(e); + + var newWidth = min$3(max$5(startWidth + e.clientX - startX, minWidth), maxWidth); + var newHeight = min$3(max$5(startHeight + e.clientY - startY, minHeight), maxHeight); + + parent.style.width = newWidth + 'px'; + parent.style.height = newHeight + 'px'; + + self.resizeHandler({ + width: startWidth, + height: startHeight, + dx: e.clientX - startX, + dy: e.clientY - startY + }); + }; + + var onMouseUp = function(e) { + preventDefault(e); + stopPropagation(e); + + componentEvent.unbind(document,'mousemove', onMouseMove, false); + componentEvent.unbind(document, 'mouseup', onMouseUp, false); + }; + + componentEvent.bind(resizeHandle, 'mousedown', onMouseDown); + } + + assign(resizeHandle.style, { + position: 'absolute', + bottom: '0px', + right: '0px', + cursor: 'nwse-resize', + width: '0', + height: '0', + borderTop: (parseInt(this.style.fontSize) / 4 || 3) + 'px solid transparent', + borderRight: (parseInt(this.style.fontSize) / 4 || 3) + 'px solid #ccc', + borderBottom: (parseInt(this.style.fontSize) / 4 || 3) + 'px solid #ccc', + borderLeft: (parseInt(this.style.fontSize) / 4 || 3) + 'px solid transparent' + }); + + parent.appendChild(resizeHandle); + }; + + + /** + * Clear content and style of the textbox, unbind listeners and + * reset CSS style. + */ + TextBox.prototype.destroy = function() { + var parent = this.parent, + content = this.content, + resizeHandle = this.resizeHandle; + + // clear content + content.innerText = ''; + + // clear styles + parent.removeAttribute('style'); + content.removeAttribute('style'); + + componentEvent.unbind(content, 'keydown', this.keyHandler); + componentEvent.unbind(content, 'mousedown', stopPropagation); + componentEvent.unbind(content, 'input', this.autoResize); + componentEvent.unbind(content, 'paste', this.handlePaste); + + if (resizeHandle) { + resizeHandle.removeAttribute('style'); + + remove$2(resizeHandle); + } + + remove$2(parent); + }; + + + TextBox.prototype.getValue = function() { + return this.content.innerText.trim(); + }; + + + TextBox.prototype.getSelection = function() { + var selection = window.getSelection(), + range = selection.getRangeAt(0); + + return range; + }; + + + TextBox.prototype.setSelection = function(container, offset) { + var range = document.createRange(); + + if (container === null) { + range.selectNodeContents(this.content); + } else { + range.setStart(container, offset); + range.setEnd(container, offset); + } + + var selection = window.getSelection(); + + selection.removeAllRanges(); + selection.addRange(range); + }; + + // helpers ////////// + + function normalizeEndOfLineSequences(string) { + return string.replace(/\r\n|\r|\n/g, '\n'); + } + + /** + * A direct editing component that allows users + * to edit an elements text directly in the diagram + * + * @param {EventBus} eventBus the event bus + */ + function DirectEditing(eventBus, canvas) { + + this._eventBus = eventBus; + + this._providers = []; + this._textbox = new TextBox({ + container: canvas.getContainer(), + keyHandler: bind(this._handleKey, this), + resizeHandler: bind(this._handleResize, this) + }); + } + + DirectEditing.$inject = [ 'eventBus', 'canvas' ]; + + + /** + * Register a direct editing provider + + * @param {Object} provider the provider, must expose an #activate(element) method that returns + * an activation context ({ bounds: {x, y, width, height }, text }) if + * direct editing is available for the given element. + * Additionally the provider must expose a #update(element, value) method + * to receive direct editing updates. + */ + DirectEditing.prototype.registerProvider = function(provider) { + this._providers.push(provider); + }; + + + /** + * Returns true if direct editing is currently active + * + * @param {djs.model.Base} [element] + * + * @return {boolean} + */ + DirectEditing.prototype.isActive = function(element) { + return !!(this._active && (!element || this._active.element === element)); + }; + + + /** + * Cancel direct editing, if it is currently active + */ + DirectEditing.prototype.cancel = function() { + if (!this._active) { + return; + } + + this._fire('cancel'); + this.close(); + }; + + + DirectEditing.prototype._fire = function(event, context) { + this._eventBus.fire('directEditing.' + event, context || { active: this._active }); + }; + + DirectEditing.prototype.close = function() { + this._textbox.destroy(); + + this._fire('deactivate'); + + this._active = null; + + this.resizable = undefined; + }; + + + DirectEditing.prototype.complete = function() { + + var active = this._active; + + if (!active) { + return; + } + + var containerBounds, + previousBounds = active.context.bounds, + newBounds = this.$textbox.getBoundingClientRect(), + newText = this.getValue(), + previousText = active.context.text; + + if ( + newText !== previousText || + newBounds.height !== previousBounds.height || + newBounds.width !== previousBounds.width + ) { + containerBounds = this._textbox.container.getBoundingClientRect(); + + active.provider.update(active.element, newText, active.context.text, { + x: newBounds.left - containerBounds.left, + y: newBounds.top - containerBounds.top, + width: newBounds.width, + height: newBounds.height + }); + } + + this._fire('complete'); + + this.close(); + }; + + + DirectEditing.prototype.getValue = function() { + return this._textbox.getValue(); + }; + + + DirectEditing.prototype._handleKey = function(e) { + + // stop bubble + e.stopPropagation(); + + var key = e.keyCode || e.charCode; + + // ESC + if (key === 27) { + e.preventDefault(); + return this.cancel(); + } + + // Enter + if (key === 13 && !e.shiftKey) { + e.preventDefault(); + return this.complete(); + } + }; + + + DirectEditing.prototype._handleResize = function(event) { + this._fire('resize', event); + }; + + + /** + * Activate direct editing on the given element + * + * @param {Object} ElementDescriptor the descriptor for a shape or connection + * @return {Boolean} true if the activation was possible + */ + DirectEditing.prototype.activate = function(element) { + if (this.isActive()) { + this.cancel(); + } + + // the direct editing context + var context; + + var provider = find(this._providers, function(p) { + return (context = p.activate(element)) ? p : null; + }); + + // check if activation took place + if (context) { + this.$textbox = this._textbox.create( + context.bounds, + context.style, + context.text, + context.options + ); + + this._active = { + element: element, + context: context, + provider: provider + }; + + if (context.options && context.options.resizable) { + this.resizable = true; + } + + this._fire('activate'); + } + + return !!context; + }; + + var DirectEditingModule = { + __depends__: [ + InteractionEventsModule$1 + ], + __init__: [ 'directEditing' ], + directEditing: [ 'type', DirectEditing ] + }; + + var MARKER_TYPES = [ + 'marker-start', + 'marker-mid', + 'marker-end' + ]; + + var NODES_CAN_HAVE_MARKER = [ + 'circle', + 'ellipse', + 'line', + 'path', + 'polygon', + 'polyline', + 'rect' + ]; + + + /** + * Adds support for previews of moving/resizing elements. + */ + function PreviewSupport(elementRegistry, eventBus, canvas, styles) { + this._elementRegistry = elementRegistry; + this._canvas = canvas; + this._styles = styles; + + this._clonedMarkers = {}; + + var self = this; + + eventBus.on('drag.cleanup', function() { + forEach$1(self._clonedMarkers, function(clonedMarker) { + remove$1(clonedMarker); + }); + + self._clonedMarkers = {}; + }); + } + + PreviewSupport.$inject = [ + 'elementRegistry', + 'eventBus', + 'canvas', + 'styles' + ]; + + + /** + * Returns graphics of an element. + * + * @param {djs.model.Base} element + * + * @return {SVGElement} + */ + PreviewSupport.prototype.getGfx = function(element) { + return this._elementRegistry.getGraphics(element); + }; + + /** + * Adds a move preview of a given shape to a given svg group. + * + * @param {djs.model.Base} element + * @param {SVGElement} group + * @param {SVGElement} [gfx] + * + * @return {SVGElement} dragger + */ + PreviewSupport.prototype.addDragger = function(element, group, gfx) { + gfx = gfx || this.getGfx(element); + + var dragger = clone$1(gfx); + var bbox = gfx.getBoundingClientRect(); + + this._cloneMarkers(getVisual(dragger)); + + attr(dragger, this._styles.cls('djs-dragger', [], { + x: bbox.top, + y: bbox.left + })); + + append(group, dragger); + + return dragger; + }; + + /** + * Adds a resize preview of a given shape to a given svg group. + * + * @param {djs.model.Base} element + * @param {SVGElement} group + * + * @return {SVGElement} frame + */ + PreviewSupport.prototype.addFrame = function(shape, group) { + + var frame = create$1('rect', { + class: 'djs-resize-overlay', + width: shape.width, + height: shape.height, + x: shape.x, + y: shape.y + }); + + append(group, frame); + + return frame; + }; + + /** + * Clone all markers referenced by a node and its child nodes. + * + * @param {SVGElement} gfx + */ + PreviewSupport.prototype._cloneMarkers = function(gfx) { + var self = this; + + if (gfx.childNodes) { + + // TODO: use forEach once we drop PhantomJS + for (var i = 0; i < gfx.childNodes.length; i++) { + + // recursively clone markers of child nodes + self._cloneMarkers(gfx.childNodes[ i ]); + } + } + + if (!canHaveMarker(gfx)) { + return; + } + + MARKER_TYPES.forEach(function(markerType) { + if (attr(gfx, markerType)) { + var marker = getMarker(gfx, markerType, self._canvas.getContainer()); + + self._cloneMarker(gfx, marker, markerType); + } + }); + }; + + /** + * Clone marker referenced by an element. + * + * @param {SVGElement} gfx + * @param {SVGElement} marker + * @param {string} markerType + */ + PreviewSupport.prototype._cloneMarker = function(gfx, marker, markerType) { + var markerId = marker.id; + + var clonedMarker = this._clonedMarkers[ markerId ]; + + if (!clonedMarker) { + clonedMarker = clone$1(marker); + + var clonedMarkerId = markerId + '-clone'; + + clonedMarker.id = clonedMarkerId; + + classes(clonedMarker) + .add('djs-dragger') + .add('djs-dragger-marker'); + + this._clonedMarkers[ markerId ] = clonedMarker; + + var defs = query('defs', this._canvas._svg); + + if (!defs) { + defs = create$1('defs'); + + append(this._canvas._svg, defs); + } + + append(defs, clonedMarker); + } + + var reference = idToReference(this._clonedMarkers[ markerId ].id); + + attr(gfx, markerType, reference); + }; + + // helpers ////////// + + /** + * Get marker of given type referenced by node. + * + * @param {Node} node + * @param {string} markerType + * @param {Node} [parentNode] + * + * @param {Node} + */ + function getMarker(node, markerType, parentNode) { + var id = referenceToId(attr(node, markerType)); + + return query('marker#' + id, parentNode || document); + } + + /** + * Get ID of fragment within current document from its functional IRI reference. + * References may use single or double quotes. + * + * @param {string} reference + * + * @returns {string} + */ + function referenceToId(reference) { + return reference.match(/url\(['"]?#([^'"]*)['"]?\)/)[1]; + } + + /** + * Get functional IRI reference for given ID of fragment within current document. + * + * @param {string} id + * + * @returns {string} + */ + function idToReference(id) { + return 'url(#' + id + ')'; + } + + /** + * Check wether node type can have marker attributes. + * + * @param {Node} node + * + * @returns {boolean} + */ + function canHaveMarker(node) { + return NODES_CAN_HAVE_MARKER.indexOf(node.nodeName) !== -1; + } + + var PreviewSupportModule = { + __init__: [ 'previewSupport' ], + previewSupport: [ 'type', PreviewSupport ] + }; + + var MARKER_OK$2 = 'drop-ok', + MARKER_NOT_OK$2 = 'drop-not-ok', + MARKER_ATTACH$2 = 'attach-ok', + MARKER_NEW_PARENT$1 = 'new-parent'; + + var PREFIX = 'create'; + + var HIGH_PRIORITY$g = 2000; + + + /** + * Create new elements through drag and drop. + * + * @param {Canvas} canvas + * @param {Dragging} dragging + * @param {EventBus} eventBus + * @param {Modeling} modeling + * @param {Rules} rules + */ + function Create( + canvas, + dragging, + eventBus, + modeling, + rules + ) { + + // rules ////////// + + /** + * Check wether elements can be created. + * + * @param {Array} elements + * @param {djs.model.Base} target + * @param {Point} position + * @param {djs.model.Base} [source] + * + * @returns {boolean|null|Object} + */ + function canCreate(elements, target, position, source, hints) { + if (!target) { + return false; + } + + // ignore child elements and external labels + elements = filter(elements, function(element) { + var labelTarget = element.labelTarget; + + return !element.parent && !(isLabel$5(element) && elements.indexOf(labelTarget) !== -1); + }); + + var shape = find(elements, function(element) { + return !isConnection$c(element); + }); + + var attach = false, + connect = false, + create = false; + + // (1) attaching single shapes + if (isSingleShape(elements)) { + attach = rules.allowed('shape.attach', { + position: position, + shape: shape, + target: target + }); + } + + if (!attach) { + + // (2) creating elements + if (isSingleShape(elements)) { + create = rules.allowed('shape.create', { + position: position, + shape: shape, + source: source, + target: target + }); + } else { + create = rules.allowed('elements.create', { + elements: elements, + position: position, + target: target + }); + } + + } + + var connectionTarget = hints.connectionTarget; + + // (3) appending single shapes + if (create || attach) { + if (shape && source) { + connect = rules.allowed('connection.create', { + source: connectionTarget === source ? shape : source, + target: connectionTarget === source ? source : shape, + hints: { + targetParent: target, + targetAttach: attach + } + }); + } + + return { + attach: attach, + connect: connect + }; + } + + // ignore wether or not elements can be created + if (create === null || attach === null) { + return null; + } + + return false; + } + + function setMarker(element, marker) { + [ MARKER_ATTACH$2, MARKER_OK$2, MARKER_NOT_OK$2, MARKER_NEW_PARENT$1 ].forEach(function(m) { + + if (m === marker) { + canvas.addMarker(element, m); + } else { + canvas.removeMarker(element, m); + } + }); + } + + // event handling ////////// + + eventBus.on([ 'create.move', 'create.hover' ], function(event) { + var context = event.context, + elements = context.elements, + hover = event.hover, + source = context.source, + hints = context.hints || {}; + + if (!hover) { + context.canExecute = false; + context.target = null; + + return; + } + + ensureConstraints$2(event); + + var position = { + x: event.x, + y: event.y + }; + + var canExecute = context.canExecute = hover && canCreate(elements, hover, position, source, hints); + + if (hover && canExecute !== null) { + context.target = hover; + + if (canExecute && canExecute.attach) { + setMarker(hover, MARKER_ATTACH$2); + } else { + setMarker(hover, canExecute ? MARKER_NEW_PARENT$1 : MARKER_NOT_OK$2); + } + } + }); + + eventBus.on([ 'create.end', 'create.out', 'create.cleanup' ], function(event) { + var hover = event.hover; + + if (hover) { + setMarker(hover, null); + } + }); + + eventBus.on('create.end', function(event) { + var context = event.context, + source = context.source, + shape = context.shape, + elements = context.elements, + target = context.target, + canExecute = context.canExecute, + attach = canExecute && canExecute.attach, + connect = canExecute && canExecute.connect, + hints = context.hints || {}; + + if (canExecute === false || !target) { + return false; + } + + ensureConstraints$2(event); + + var position = { + x: event.x, + y: event.y + }; + + if (connect) { + shape = modeling.appendShape(source, shape, position, target, { + attach: attach, + connection: connect === true ? {} : connect, + connectionTarget: hints.connectionTarget + }); + } else { + elements = modeling.createElements(elements, position, target, assign({}, hints, { + attach: attach + })); + + // update shape + shape = find(elements, function(element) { + return !isConnection$c(element); + }); + } + + // update elements and shape + assign(context, { + elements: elements, + shape: shape + }); + + assign(event, { + elements: elements, + shape: shape + }); + }); + + function cancel() { + var context = dragging.context(); + + if (context && context.prefix === PREFIX) { + dragging.cancel(); + } + } + + // cancel on that is not result of + eventBus.on('create.init', function() { + eventBus.on('elements.changed', cancel); + + eventBus.once([ 'create.cancel', 'create.end' ], HIGH_PRIORITY$g, function() { + eventBus.off('elements.changed', cancel); + }); + }); + + // API ////////// + + this.start = function(event, elements, context) { + if (!isArray$3(elements)) { + elements = [ elements ]; + } + + var shape = find(elements, function(element) { + return !isConnection$c(element); + }); + + if (!shape) { + + // at least one shape is required + return; + } + + context = assign({ + elements: elements, + hints: {}, + shape: shape + }, context || {}); + + // make sure each element has x and y + forEach$1(elements, function(element) { + if (!isNumber(element.x)) { + element.x = 0; + } + + if (!isNumber(element.y)) { + element.y = 0; + } + }); + + var visibleElements = filter(elements, function(element) { + return !element.hidden; + }); + + var bbox = getBBox(visibleElements); + + // center elements around cursor + forEach$1(elements, function(element) { + if (isConnection$c(element)) { + element.waypoints = map(element.waypoints, function(waypoint) { + return { + x: waypoint.x - bbox.x - bbox.width / 2, + y: waypoint.y - bbox.y - bbox.height / 2 + }; + }); + } + + assign(element, { + x: element.x - bbox.x - bbox.width / 2, + y: element.y - bbox.y - bbox.height / 2 + }); + }); + + dragging.init(event, PREFIX, { + cursor: 'grabbing', + autoActivate: true, + data: { + shape: shape, + elements: elements, + context: context + } + }); + }; + } + + Create.$inject = [ + 'canvas', + 'dragging', + 'eventBus', + 'modeling', + 'rules' + ]; + + // helpers ////////// + + function ensureConstraints$2(event) { + var context = event.context, + createConstraints = context.createConstraints; + + if (!createConstraints) { + return; + } + + if (createConstraints.left) { + event.x = Math.max(event.x, createConstraints.left); + } + + if (createConstraints.right) { + event.x = Math.min(event.x, createConstraints.right); + } + + if (createConstraints.top) { + event.y = Math.max(event.y, createConstraints.top); + } + + if (createConstraints.bottom) { + event.y = Math.min(event.y, createConstraints.bottom); + } + } + + function isConnection$c(element) { + return !!element.waypoints; + } + + function isSingleShape(elements) { + return elements && elements.length === 1 && !isConnection$c(elements[0]); + } + + function isLabel$5(element) { + return !!element.labelTarget; + } + + var LOW_PRIORITY$i = 750; + + + function CreatePreview( + canvas, + eventBus, + graphicsFactory, + previewSupport, + styles + ) { + function createDragGroup(elements) { + var dragGroup = create$1('g'); + + attr(dragGroup, styles.cls('djs-drag-group', [ 'no-events' ])); + + var childrenGfx = create$1('g'); + + elements.forEach(function(element) { + + // create graphics + var gfx; + + if (element.hidden) { + return; + } + + if (element.waypoints) { + gfx = graphicsFactory._createContainer('connection', childrenGfx); + + graphicsFactory.drawConnection(getVisual(gfx), element); + } else { + gfx = graphicsFactory._createContainer('shape', childrenGfx); + + graphicsFactory.drawShape(getVisual(gfx), element); + + translate$2(gfx, element.x, element.y); + } + + // add preview + previewSupport.addDragger(element, dragGroup, gfx); + }); + + return dragGroup; + } + + eventBus.on('create.move', LOW_PRIORITY$i, function(event) { + + var hover = event.hover, + context = event.context, + elements = context.elements, + dragGroup = context.dragGroup; + + // lazily create previews + if (!dragGroup) { + dragGroup = context.dragGroup = createDragGroup(elements); + } + + var activeLayer; + + if (hover) { + if (!dragGroup.parentNode) { + activeLayer = canvas.getActiveLayer(); + + append(activeLayer, dragGroup); + } + + translate$2(dragGroup, event.x, event.y); + } else { + remove$1(dragGroup); + } + }); + + eventBus.on('create.cleanup', function(event) { + var context = event.context, + dragGroup = context.dragGroup; + + if (dragGroup) { + remove$1(dragGroup); + } + }); + } + + CreatePreview.$inject = [ + 'canvas', + 'eventBus', + 'graphicsFactory', + 'previewSupport', + 'styles' + ]; + + var CreateModule = { + __depends__: [ + DraggingModule, + PreviewSupportModule, + RulesModule$1, + SelectionModule + ], + __init__: [ + 'create', + 'createPreview' + ], + create: [ 'type', Create ], + createPreview: [ 'type', CreatePreview ] + }; + + /** + * A clip board stub + */ + function Clipboard() {} + + + Clipboard.prototype.get = function() { + return this._data; + }; + + Clipboard.prototype.set = function(data) { + this._data = data; + }; + + Clipboard.prototype.clear = function() { + var data = this._data; + + delete this._data; + + return data; + }; + + Clipboard.prototype.isEmpty = function() { + return !this._data; + }; + + var ClipboardModule = { + clipboard: [ 'type', Clipboard ] + }; + + function Mouse(eventBus) { + var self = this; + + this._lastMoveEvent = null; + + function setLastMoveEvent(mousemoveEvent) { + self._lastMoveEvent = mousemoveEvent; + } + + eventBus.on('canvas.init', function(context) { + var svg = self._svg = context.svg; + + svg.addEventListener('mousemove', setLastMoveEvent); + }); + + eventBus.on('canvas.destroy', function() { + self._lastMouseEvent = null; + + self._svg.removeEventListener('mousemove', setLastMoveEvent); + }); + } + + Mouse.$inject = [ 'eventBus' ]; + + Mouse.prototype.getLastMoveEvent = function() { + return this._lastMoveEvent || createMoveEvent(0, 0); + }; + + // helpers ////////// + + function createMoveEvent(x, y) { + var event = document.createEvent('MouseEvent'); + + var screenX = x, + screenY = y, + clientX = x, + clientY = y; + + if (event.initMouseEvent) { + event.initMouseEvent( + 'mousemove', + true, + true, + window, + 0, + screenX, + screenY, + clientX, + clientY, + false, + false, + false, + false, + 0, + null + ); + } + + return event; + } + + var MouseModule = { + __init__: [ 'mouse' ], + mouse: [ 'type', Mouse ] + }; + + /** + * @typedef {Function} listener + * + * @param {Object} context + * @param {Array} context.elements + * + * @returns {Array|boolean} - Return elements to be copied or false to disallow + * copying. + */ + + /** + * @typedef {Function} listener + * + * @param {Object} context + * @param {Object} context.descriptor + * @param {djs.model.Base} context.element + * @param {Array} context.elements + */ + + /** + * @typedef {Function} listener + * + * @param {Object} context + * @param {djs.model.Base} context.element + * @param {Array} context.children - Add children to be added to tree. + */ + + /** + * @typedef {Function} listener + * + * @param {Object} context + * @param {Object} context.elements + * @param {Object} context.tree + */ + + /** + * @typedef {Function} listener + * + * @param {Object} context + * @param {Object} context.cache - Already created elements. + * @param {Object} context.descriptor + */ + + /** + * @typedef {Function} listener + * + * @param {Object} context + * @param {Object} context.hints - Add hints before pasting. + */ + + /** + * Copy and paste elements. + * + * @param {Canvas} canvas + * @param {Create} create + * @param {Clipboard} clipboard + * @param {ElementFactory} elementFactory + * @param {EventBus} eventBus + * @param {Modeling} modeling + * @param {Mouse} mouse + * @param {Rules} rules + */ + function CopyPaste( + canvas, + create, + clipboard, + elementFactory, + eventBus, + modeling, + mouse, + rules + ) { + + this._canvas = canvas; + this._create = create; + this._clipboard = clipboard; + this._elementFactory = elementFactory; + this._eventBus = eventBus; + this._modeling = modeling; + this._mouse = mouse; + this._rules = rules; + + eventBus.on('copyPaste.copyElement', function(context) { + var descriptor = context.descriptor, + element = context.element, + elements = context.elements; + + // default priority (priority = 1) + descriptor.priority = 1; + + descriptor.id = element.id; + + var parentCopied = find(elements, function(e) { + return e === element.parent; + }); + + // do NOT reference parent if parent wasn't copied + if (parentCopied) { + descriptor.parent = element.parent.id; + } + + // attachers (priority = 2) + if (isAttacher$1(element)) { + descriptor.priority = 2; + + descriptor.host = element.host.id; + } + + // connections (priority = 3) + if (isConnection$b(element)) { + descriptor.priority = 3; + + descriptor.source = element.source.id; + descriptor.target = element.target.id; + + descriptor.waypoints = copyWaypoints$1(element); + } + + // labels (priority = 4) + if (isLabel$4(element)) { + descriptor.priority = 4; + + descriptor.labelTarget = element.labelTarget.id; + } + + forEach$1([ 'x', 'y', 'width', 'height' ], function(property) { + if (isNumber(element[ property ])) { + descriptor[ property ] = element[ property ]; + } + }); + + descriptor.hidden = element.hidden; + descriptor.collapsed = element.collapsed; + + }); + + eventBus.on('copyPaste.pasteElements', function(context) { + var hints = context.hints; + + assign(hints, { + createElementsBehavior: false + }); + }); + } + + CopyPaste.$inject = [ + 'canvas', + 'create', + 'clipboard', + 'elementFactory', + 'eventBus', + 'modeling', + 'mouse', + 'rules' + ]; + + + /** + * Copy elements. + * + * @param {Array} elements + * + * @returns {Object} + */ + CopyPaste.prototype.copy = function(elements) { + var allowed, + tree; + + if (!isArray$3(elements)) { + elements = elements ? [ elements ] : []; + } + + allowed = this._eventBus.fire('copyPaste.canCopyElements', { + elements: elements + }); + + if (allowed === false) { + tree = {}; + } else { + tree = this.createTree(isArray$3(allowed) ? allowed : elements); + } + + // we set an empty tree, selection of elements + // to copy was empty. + this._clipboard.set(tree); + + this._eventBus.fire('copyPaste.elementsCopied', { + elements: elements, + tree: tree + }); + + return tree; + }; + + /** + * Paste elements. + * + * @param {Object} [context] + * @param {djs.model.base} [context.element] - Parent. + * @param {Point} [context.point] - Position. + * @param {Object} [context.hints] - Hints. + */ + CopyPaste.prototype.paste = function(context) { + var tree = this._clipboard.get(); + + if (this._clipboard.isEmpty()) { + return; + } + + var hints = context && context.hints || {}; + + this._eventBus.fire('copyPaste.pasteElements', { + hints: hints + }); + + var elements = this._createElements(tree); + + // paste directly + if (context && context.element && context.point) { + return this._paste(elements, context.element, context.point, hints); + } + + this._create.start(this._mouse.getLastMoveEvent(), elements, { + hints: hints || {} + }); + }; + + /** + * Paste elements directly. + * + * @param {Array} elements + * @param {djs.model.base} target + * @param {Point} position + * @param {Object} [hints] + */ + CopyPaste.prototype._paste = function(elements, target, position, hints) { + + // make sure each element has x and y + forEach$1(elements, function(element) { + if (!isNumber(element.x)) { + element.x = 0; + } + + if (!isNumber(element.y)) { + element.y = 0; + } + }); + + var bbox = getBBox(elements); + + // center elements around cursor + forEach$1(elements, function(element) { + if (isConnection$b(element)) { + element.waypoints = map(element.waypoints, function(waypoint) { + return { + x: waypoint.x - bbox.x - bbox.width / 2, + y: waypoint.y - bbox.y - bbox.height / 2 + }; + }); + } + + assign(element, { + x: element.x - bbox.x - bbox.width / 2, + y: element.y - bbox.y - bbox.height / 2 + }); + }); + + return this._modeling.createElements(elements, position, target, assign({}, hints)); + }; + + /** + * Create elements from tree. + */ + CopyPaste.prototype._createElements = function(tree) { + var self = this; + + var eventBus = this._eventBus; + + var cache = {}; + + var elements = []; + + forEach$1(tree, function(branch, depth) { + + // sort by priority + branch = sortBy(branch, 'priority'); + + forEach$1(branch, function(descriptor) { + + // remove priority + var attrs = assign({}, omit(descriptor, [ 'priority' ])); + + if (cache[ descriptor.parent ]) { + attrs.parent = cache[ descriptor.parent ]; + } else { + delete attrs.parent; + } + + eventBus.fire('copyPaste.pasteElement', { + cache: cache, + descriptor: attrs + }); + + var element; + + if (isConnection$b(attrs)) { + attrs.source = cache[ descriptor.source ]; + attrs.target = cache[ descriptor.target ]; + + element = cache[ descriptor.id ] = self.createConnection(attrs); + + elements.push(element); + + return; + } + + if (isLabel$4(attrs)) { + attrs.labelTarget = cache[ attrs.labelTarget ]; + + element = cache[ descriptor.id ] = self.createLabel(attrs); + + elements.push(element); + + return; + } + + if (attrs.host) { + attrs.host = cache[ attrs.host ]; + } + + element = cache[ descriptor.id ] = self.createShape(attrs); + + elements.push(element); + }); + + }); + + return elements; + }; + + CopyPaste.prototype.createConnection = function(attrs) { + var connection = this._elementFactory.createConnection(omit(attrs, [ 'id' ])); + + return connection; + }; + + CopyPaste.prototype.createLabel = function(attrs) { + var label = this._elementFactory.createLabel(omit(attrs, [ 'id' ])); + + return label; + }; + + CopyPaste.prototype.createShape = function(attrs) { + var shape = this._elementFactory.createShape(omit(attrs, [ 'id' ])); + + return shape; + }; + + /** + * Check wether element has relations to other elements e.g. attachers, labels and connections. + * + * @param {Object} element + * @param {Array} elements + * + * @returns {boolean} + */ + CopyPaste.prototype.hasRelations = function(element, elements) { + var labelTarget, + source, + target; + + if (isConnection$b(element)) { + source = find(elements, matchPattern({ id: element.source.id })); + target = find(elements, matchPattern({ id: element.target.id })); + + if (!source || !target) { + return false; + } + } + + if (isLabel$4(element)) { + labelTarget = find(elements, matchPattern({ id: element.labelTarget.id })); + + if (!labelTarget) { + return false; + } + } + + return true; + }; + + /** + * Create a tree-like structure from elements. + * + * @example + * tree: { + * 0: [ + * { id: 'Shape_1', priority: 1, ... }, + * { id: 'Shape_2', priority: 1, ... }, + * { id: 'Connection_1', source: 'Shape_1', target: 'Shape_2', priority: 3, ... }, + * ... + * ], + * 1: [ + * { id: 'Shape_3', parent: 'Shape1', priority: 1, ... }, + * ... + * ] + * }; + * + * @param {Array} elements + * + * @return {Object} + */ + CopyPaste.prototype.createTree = function(elements) { + var rules = this._rules, + self = this; + + var tree = {}, + elementsData = []; + + var parents = getParents$1(elements); + + function canCopy(element, elements) { + return rules.allowed('element.copy', { + element: element, + elements: elements + }); + } + + function addElementData(element, depth) { + + // (1) check wether element has already been added + var foundElementData = find(elementsData, function(elementsData) { + return element === elementsData.element; + }); + + // (2) add element if not already added + if (!foundElementData) { + elementsData.push({ + element: element, + depth: depth + }); + + return; + } + + // (3) update depth + if (foundElementData.depth < depth) { + elementsData = removeElementData(foundElementData, elementsData); + + elementsData.push({ + element: foundElementData.element, + depth: depth + }); + } + } + + function removeElementData(elementData, elementsData) { + var index = elementsData.indexOf(elementData); + + if (index !== -1) { + elementsData.splice(index, 1); + } + + return elementsData; + } + + // (1) add elements + eachElement(parents, function(element, _index, depth) { + + // do NOT add external labels directly + if (isLabel$4(element)) { + return; + } + + // always copy external labels + forEach$1(element.labels, function(label) { + addElementData(label, depth); + }); + + function addRelatedElements(elements) { + elements && elements.length && forEach$1(elements, function(element) { + + // add external labels + forEach$1(element.labels, function(label) { + addElementData(label, depth); + }); + + addElementData(element, depth); + }); + } + + forEach$1([ element.attachers, element.incoming, element.outgoing ], addRelatedElements); + + addElementData(element, depth); + + var children = []; + + if (element.children) { + children = element.children.slice(); + } + + // allow others to add children to tree + self._eventBus.fire('copyPaste.createTree', { + element: element, + children: children + }); + + return children; + }); + + elements = map(elementsData, function(elementData) { + return elementData.element; + }); + + // (2) copy elements + elementsData = map(elementsData, function(elementData) { + elementData.descriptor = {}; + + self._eventBus.fire('copyPaste.copyElement', { + descriptor: elementData.descriptor, + element: elementData.element, + elements: elements + }); + + return elementData; + }); + + // (3) sort elements by priority + elementsData = sortBy(elementsData, function(elementData) { + return elementData.descriptor.priority; + }); + + elements = map(elementsData, function(elementData) { + return elementData.element; + }); + + // (4) create tree + forEach$1(elementsData, function(elementData) { + var depth = elementData.depth; + + if (!self.hasRelations(elementData.element, elements)) { + removeElement(elementData.element, elements); + + return; + } + + if (!canCopy(elementData.element, elements)) { + removeElement(elementData.element, elements); + + return; + } + + if (!tree[depth]) { + tree[depth] = []; + } + + tree[depth].push(elementData.descriptor); + }); + + return tree; + }; + + // helpers ////////// + + function isAttacher$1(element) { + return !!element.host; + } + + function isConnection$b(element) { + return !!element.waypoints; + } + + function isLabel$4(element) { + return !!element.labelTarget; + } + + function copyWaypoints$1(element) { + return map(element.waypoints, function(waypoint) { + + waypoint = copyWaypoint$1(waypoint); + + if (waypoint.original) { + waypoint.original = copyWaypoint$1(waypoint.original); + } + + return waypoint; + }); + } + + function copyWaypoint$1(waypoint) { + return assign({}, waypoint); + } + + function removeElement(element, elements) { + var index = elements.indexOf(element); + + if (index === -1) { + return elements; + } + + return elements.splice(index, 1); + } + + var CopyPasteModule$1 = { + __depends__: [ + ClipboardModule, + CreateModule, + MouseModule, + RulesModule$1 + ], + __init__: [ 'copyPaste' ], + copyPaste: [ 'type', CopyPaste ] + }; + + function copyProperties$1(source, target, properties) { + if (!isArray$3(properties)) { + properties = [ properties ]; + } + + forEach$1(properties, function(property) { + if (!isUndefined$2(source[property])) { + target[property] = source[property]; + } + }); + } + + var LOW_PRIORITY$h = 750; + + + function BpmnCopyPaste(bpmnFactory, eventBus, moddleCopy) { + + function copy(bo, clone) { + var targetBo = bpmnFactory.create(bo.$type); + + return moddleCopy.copyElement(bo, targetBo, null, clone); + } + + eventBus.on('copyPaste.copyElement', LOW_PRIORITY$h, function(context) { + var descriptor = context.descriptor, + element = context.element, + businessObject = getBusinessObject(element); + + // do not copy business object + di for labels; + // will be pulled from the referenced label target + if (isLabel$3(element)) { + return descriptor; + } + + var businessObjectCopy = descriptor.businessObject = copy(businessObject, true); + var diCopy = descriptor.di = copy(getDi(element), true); + diCopy.bpmnElement = businessObjectCopy; + + copyProperties$1(businessObjectCopy, descriptor, 'name'); + copyProperties$1(diCopy, descriptor, 'isExpanded'); + + // default sequence flow + if (businessObject.default) { + descriptor.default = businessObject.default.id; + } + }); + + var referencesKey = '-bpmn-js-refs'; + + function getReferences(cache) { + return (cache[referencesKey] = cache[referencesKey] || {}); + } + + function setReferences(cache, references) { + cache[referencesKey] = references; + } + + function resolveReferences(descriptor, cache, references) { + var businessObject = getBusinessObject(descriptor); + + // default sequence flows + if (descriptor.default) { + + // relationship cannot be resolved immediately + references[ descriptor.default ] = { + element: businessObject, + property: 'default' + }; + } + + // boundary events + if (descriptor.host) { + + // relationship can be resolved immediately + getBusinessObject(descriptor).attachedToRef = getBusinessObject(cache[ descriptor.host ]); + } + + return omit(references, reduce(references, function(array, reference, key) { + var element = reference.element, + property = reference.property; + + if (key === descriptor.id) { + element[ property ] = businessObject; + + array.push(descriptor.id); + } + + return array; + }, [])); + } + + eventBus.on('copyPaste.pasteElement', function(context) { + var cache = context.cache, + descriptor = context.descriptor, + businessObject = descriptor.businessObject, + di = descriptor.di; + + // wire existing di + businessObject for external label + if (isLabel$3(descriptor)) { + descriptor.businessObject = getBusinessObject(cache[ descriptor.labelTarget ]); + descriptor.di = getDi(cache[ descriptor.labelTarget ]); + + return; + } + + businessObject = descriptor.businessObject = copy(businessObject); + + di = descriptor.di = copy(di); + di.bpmnElement = businessObject; + + copyProperties$1(descriptor, businessObject, [ + 'isExpanded', + 'name' + ]); + + descriptor.type = businessObject.$type; + }); + + // copy + paste processRef with participant + + eventBus.on('copyPaste.copyElement', LOW_PRIORITY$h, function(context) { + var descriptor = context.descriptor, + element = context.element; + + if (!is$1(element, 'bpmn:Participant')) { + return; + } + + var participantBo = getBusinessObject(element); + + if (participantBo.processRef) { + descriptor.processRef = copy(participantBo.processRef, true); + } + }); + + eventBus.on('copyPaste.pasteElement', function(context) { + var descriptor = context.descriptor, + processRef = descriptor.processRef; + + if (processRef) { + descriptor.processRef = copy(processRef); + } + }); + + // resolve references + + eventBus.on('copyPaste.pasteElement', LOW_PRIORITY$h, function(context) { + var cache = context.cache, + descriptor = context.descriptor; + + // resolve references e.g. default sequence flow + setReferences( + cache, + resolveReferences(descriptor, cache, getReferences(cache)) + ); + }); + + } + + + BpmnCopyPaste.$inject = [ + 'bpmnFactory', + 'eventBus', + 'moddleCopy' + ]; + + // helpers ////////// + + function isLabel$3(element) { + return !!element.labelTarget; + } + + var DISALLOWED_PROPERTIES = [ + 'artifacts', + 'dataInputAssociations', + 'dataOutputAssociations', + 'default', + 'flowElements', + 'lanes', + 'incoming', + 'outgoing', + 'categoryValue' + ]; + + /** + * @typedef {Function} listener + * + * @param {Object} context + * @param {Array} context.propertyNames + * @param {ModdleElement} context.sourceElement + * @param {ModdleElement} context.targetElement + * + * @returns {Array|boolean} - Return properties to be copied or false to disallow + * copying. + */ + + /** + * @typedef {Function} listener + * + * @param {Object} context + * @param {ModdleElement} context.parent + * @param {*} context.property + * @param {string} context.propertyName + * + * @returns {*|boolean} - Return copied property or false to disallow + * copying. + */ + + /** + * @typedef {Function} listener + * + * @param {Object} context + * @param {ModdleElement} context.parent + * @param {*} context.property + * @param {string} context.propertyName + * + * @returns {boolean} - Return false to disallow + * setting copied property. + */ + + /** + * Utility for copying model properties from source element to target element. + * + * @param {EventBus} eventBus + * @param {BpmnFactory} bpmnFactory + * @param {BpmnModdle} moddle + */ + function ModdleCopy(eventBus, bpmnFactory, moddle) { + this._bpmnFactory = bpmnFactory; + this._eventBus = eventBus; + this._moddle = moddle; + + // copy extension elements last + eventBus.on('moddleCopy.canCopyProperties', function(context) { + var propertyNames = context.propertyNames; + + if (!propertyNames || !propertyNames.length) { + return; + } + + return sortBy(propertyNames, function(propertyName) { + return propertyName === 'extensionElements'; + }); + }); + + // default check whether property can be copied + eventBus.on('moddleCopy.canCopyProperty', function(context) { + var parent = context.parent, + parentDescriptor = isObject(parent) && parent.$descriptor, + propertyName = context.propertyName; + + if (propertyName && DISALLOWED_PROPERTIES.indexOf(propertyName) !== -1) { + + // disallow copying property + return false; + } + + if (propertyName && + parentDescriptor && + !find(parentDescriptor.properties, matchPattern({ name: propertyName }))) { + + // disallow copying property + return false; + } + }); + + // do NOT allow to copy empty extension elements + eventBus.on('moddleCopy.canSetCopiedProperty', function(context) { + var property = context.property; + + if (is(property, 'bpmn:ExtensionElements') && (!property.values || !property.values.length)) { + + // disallow setting copied property + return false; + } + }); + } + + ModdleCopy.$inject = [ + 'eventBus', + 'bpmnFactory', + 'moddle' + ]; + + /** + * Copy model properties of source element to target element. + * + * @param {ModdleElement} sourceElement + * @param {ModdleElement} targetElement + * @param {Array} [propertyNames] + * @param {boolean} clone + * + * @param {ModdleElement} + */ + ModdleCopy.prototype.copyElement = function(sourceElement, targetElement, propertyNames, clone) { + var self = this; + + if (propertyNames && !isArray$3(propertyNames)) { + propertyNames = [ propertyNames ]; + } + + propertyNames = propertyNames || getPropertyNames(sourceElement.$descriptor); + + var canCopyProperties = this._eventBus.fire('moddleCopy.canCopyProperties', { + propertyNames: propertyNames, + sourceElement: sourceElement, + targetElement: targetElement, + clone: clone + }); + + if (canCopyProperties === false) { + return targetElement; + } + + if (isArray$3(canCopyProperties)) { + propertyNames = canCopyProperties; + } + + // copy properties + forEach$1(propertyNames, function(propertyName) { + var sourceProperty; + + if (has$1(sourceElement, propertyName)) { + sourceProperty = sourceElement.get(propertyName); + } + + var copiedProperty = self.copyProperty(sourceProperty, targetElement, propertyName, clone); + + if (!isDefined(copiedProperty)) { + return; + } + + var canSetProperty = self._eventBus.fire('moddleCopy.canSetCopiedProperty', { + parent: targetElement, + property: copiedProperty, + propertyName: propertyName + }); + + if (canSetProperty === false) { + return; + } + + // TODO(nikku): unclaim old IDs if ID property is copied over + // this._moddle.getPropertyDescriptor(parent, propertyName) + targetElement.set(propertyName, copiedProperty); + }); + + return targetElement; + }; + + /** + * Copy model property. + * + * @param {*} property + * @param {ModdleElement} parent + * @param {string} propertyName + * @param {boolean} clone + * + * @returns {*} + */ + ModdleCopy.prototype.copyProperty = function(property, parent, propertyName, clone) { + var self = this; + + // allow others to copy property + var copiedProperty = this._eventBus.fire('moddleCopy.canCopyProperty', { + parent: parent, + property: property, + propertyName: propertyName, + clone: clone + }); + + // return if copying is NOT allowed + if (copiedProperty === false) { + return; + } + + if (copiedProperty) { + if (isObject(copiedProperty) && copiedProperty.$type && !copiedProperty.$parent) { + copiedProperty.$parent = parent; + } + + return copiedProperty; + } + + var propertyDescriptor = this._moddle.getPropertyDescriptor(parent, propertyName); + + // do NOT copy references + if (propertyDescriptor.isReference) { + return; + } + + // copy id + if (propertyDescriptor.isId) { + return property && this._copyId(property, parent, clone); + } + + // copy arrays + if (isArray$3(property)) { + return reduce(property, function(childProperties, childProperty) { + + // recursion + copiedProperty = self.copyProperty(childProperty, parent, propertyName, clone); + + // copying might NOT be allowed + if (copiedProperty) { + return childProperties.concat(copiedProperty); + } + + return childProperties; + }, []); + } + + // copy model elements + if (isObject(property) && property.$type) { + if (this._moddle.getElementDescriptor(property).isGeneric) { + return; + } + + copiedProperty = self._bpmnFactory.create(property.$type); + + copiedProperty.$parent = parent; + + // recursion + copiedProperty = self.copyElement(property, copiedProperty, null, clone); + + return copiedProperty; + } + + // copy primitive properties + return property; + }; + + ModdleCopy.prototype._copyId = function(id, element, clone) { + + if (clone) { + return id; + } + + // disallow if already taken + if (this._moddle.ids.assigned(id)) { + return; + } else { + + this._moddle.ids.claim(id, element); + return id; + } + }; + + // helpers ////////// + + function getPropertyNames(descriptor, keepDefaultProperties) { + return reduce(descriptor.properties, function(properties, property) { + + if (keepDefaultProperties && property.default) { + return properties; + } + + return properties.concat(property.name); + }, []); + } + + function is(element, type) { + return element && (typeof element.$instanceOf === 'function') && element.$instanceOf(type); + } + + var CopyPasteModule = { + __depends__: [ + CopyPasteModule$1 + ], + __init__: [ 'bpmnCopyPaste', 'moddleCopy' ], + bpmnCopyPaste: [ 'type', BpmnCopyPaste ], + moddleCopy: [ 'type', ModdleCopy ] + }; + + var round$5 = Math.round; + + /** + * Service that allow replacing of elements. + */ + function Replace(modeling) { + + this._modeling = modeling; + } + + Replace.$inject = [ 'modeling' ]; + + /** + * @param {Element} oldElement - Element to be replaced + * @param {Object} newElementData - Containing information about the new element, + * for example the new bounds and type. + * @param {Object} options - Custom options that will be attached to the context. It can be used to inject data + * that is needed in the command chain. For example it could be used in + * eventbus.on('commandStack.shape.replace.postExecute') to change shape attributes after + * shape creation. + */ + Replace.prototype.replaceElement = function(oldElement, newElementData, options) { + + if (oldElement.waypoints) { + + // TODO(nikku): we do not replace connections, yet + return null; + } + + var modeling = this._modeling; + + var width = newElementData.width || oldElement.width, + height = newElementData.height || oldElement.height, + x = newElementData.x || oldElement.x, + y = newElementData.y || oldElement.y, + centerX = round$5(x + width / 2), + centerY = round$5(y + height / 2); + + // modeling API requires center coordinates, + // account for that when handling shape bounds + + return modeling.replaceShape( + oldElement, + assign( + {}, + newElementData, + { + x: centerX, + y: centerY, + width: width, + height: height + } + ), + options + ); + }; + + var ReplaceModule$1 = { + __init__: [ 'replace' ], + replace: [ 'type', Replace ] + }; + + function copyProperties(source, target, properties) { + if (!isArray$3(properties)) { + properties = [ properties ]; + } + + forEach$1(properties, function(property) { + if (!isUndefined$2(source[property])) { + target[property] = source[property]; + } + }); + } + + + var CUSTOM_PROPERTIES = [ + 'cancelActivity', + 'instantiate', + 'eventGatewayType', + 'triggeredByEvent', + 'isInterrupting' + ]; + + /** + * Check if element should be collapsed or expanded. + */ + function shouldToggleCollapsed(element, targetElement) { + + var oldCollapsed = ( + element && has$1(element, 'collapsed') ? element.collapsed : !isExpanded(element) + ); + + var targetCollapsed; + + if (targetElement && (has$1(targetElement, 'collapsed') || has$1(targetElement, 'isExpanded'))) { + + // property is explicitly set so use it + targetCollapsed = ( + has$1(targetElement, 'collapsed') ? targetElement.collapsed : !targetElement.isExpanded + ); + } else { + + // keep old state + targetCollapsed = oldCollapsed; + } + + if (oldCollapsed !== targetCollapsed) { + return true; + } + + return false; + } + + + /** + * This module takes care of replacing BPMN elements + */ + function BpmnReplace( + bpmnFactory, + elementFactory, + moddleCopy, + modeling, + replace, + rules, + selection + ) { + + /** + * Prepares a new business object for the replacement element + * and triggers the replace operation. + * + * @param {djs.model.Base} element + * @param {Object} target + * @param {Object} [hints] + * + * @return {djs.model.Base} the newly created element + */ + function replaceElement(element, target, hints) { + + hints = hints || {}; + + var type = target.type, + oldBusinessObject = element.businessObject; + + if (isSubProcess(oldBusinessObject) && type === 'bpmn:SubProcess') { + if (shouldToggleCollapsed(element, target)) { + + // expanding or collapsing process + modeling.toggleCollapse(element); + + return element; + } + } + + var newBusinessObject = bpmnFactory.create(type); + + var newElement = { + type: type, + businessObject: newBusinessObject, + }; + + newElement.di = {}; + + // colors will be set to DI + copyProperties(element.di, newElement.di, [ + 'fill', + 'stroke', + 'background-color', + 'border-color', + 'color' + ]); + + var elementProps = getPropertyNames(oldBusinessObject.$descriptor), + newElementProps = getPropertyNames(newBusinessObject.$descriptor, true), + copyProps = intersection(elementProps, newElementProps); + + // initialize special properties defined in target definition + assign(newBusinessObject, pick(target, CUSTOM_PROPERTIES)); + + var properties = filter(copyProps, function(propertyName) { + + // copying event definitions, unless we replace + if (propertyName === 'eventDefinitions') { + return hasEventDefinition$1(element, target.eventDefinitionType); + } + + // retain loop characteristics if the target element + // is not an event sub process + if (propertyName === 'loopCharacteristics') { + return !isEventSubProcess(newBusinessObject); + } + + // so the applied properties from 'target' don't get lost + if (has$1(newBusinessObject, propertyName)) { + return false; + } + + if (propertyName === 'processRef' && target.isExpanded === false) { + return false; + } + + if (propertyName === 'triggeredByEvent') { + return false; + } + + return true; + }); + + newBusinessObject = moddleCopy.copyElement( + oldBusinessObject, + newBusinessObject, + properties + ); + + // initialize custom BPMN extensions + if (target.eventDefinitionType) { + + // only initialize with new eventDefinition + // if we did not set an event definition yet, + // i.e. because we copied it + if (!hasEventDefinition$1(newBusinessObject, target.eventDefinitionType)) { + newElement.eventDefinitionType = target.eventDefinitionType; + newElement.eventDefinitionAttrs = target.eventDefinitionAttrs; + } + } + + if (is$1(oldBusinessObject, 'bpmn:Activity')) { + + if (isSubProcess(oldBusinessObject)) { + + // no toggeling, so keep old state + newElement.isExpanded = isExpanded(element); + } + + // else if property is explicitly set, use it + else if (target && has$1(target, 'isExpanded')) { + newElement.isExpanded = target.isExpanded; + + // assign default size of new expanded element + var defaultSize = elementFactory.getDefaultSize(newBusinessObject, { + isExpanded: newElement.isExpanded + }); + + newElement.width = defaultSize.width; + newElement.height = defaultSize.height; + + // keep element centered + newElement.x = element.x - (newElement.width - element.width) / 2; + newElement.y = element.y - (newElement.height - element.height) / 2; + } + + // TODO: need also to respect min/max Size + // copy size, from an expanded subprocess to an expanded alternative subprocess + // except bpmn:Task, because Task is always expanded + if ((isExpanded(element) && !is$1(oldBusinessObject, 'bpmn:Task')) && newElement.isExpanded) { + newElement.width = element.width; + newElement.height = element.height; + } + } + + // remove children if not expanding sub process + if (isSubProcess(oldBusinessObject) && !isSubProcess(newBusinessObject)) { + hints.moveChildren = false; + } + + // transform collapsed/expanded pools + if (is$1(oldBusinessObject, 'bpmn:Participant')) { + + // create expanded pool + if (target.isExpanded === true) { + newBusinessObject.processRef = bpmnFactory.create('bpmn:Process'); + } else { + + // remove children when transforming to collapsed pool + hints.moveChildren = false; + } + + // apply same width and default height + newElement.width = element.width; + newElement.height = elementFactory.getDefaultSize(newElement).height; + } + + if (!rules.allowed('shape.resize', { shape: newBusinessObject })) { + newElement.height = elementFactory.getDefaultSize(newElement).height; + newElement.width = elementFactory.getDefaultSize(newElement).width; + } + + newBusinessObject.name = oldBusinessObject.name; + + // retain default flow's reference between inclusive <-> exclusive gateways and activities + if ( + isAny(oldBusinessObject, [ + 'bpmn:ExclusiveGateway', + 'bpmn:InclusiveGateway', + 'bpmn:Activity' + ]) && + isAny(newBusinessObject, [ + 'bpmn:ExclusiveGateway', + 'bpmn:InclusiveGateway', + 'bpmn:Activity' + ]) + ) { + newBusinessObject.default = oldBusinessObject.default; + } + + if ( + target.host && + !is$1(oldBusinessObject, 'bpmn:BoundaryEvent') && + is$1(newBusinessObject, 'bpmn:BoundaryEvent') + ) { + newElement.host = target.host; + } + + // The DataStoreReference element is 14px wider than the DataObjectReference element + // This ensures that they stay centered on the x axis when replaced + if ( + newElement.type === 'bpmn:DataStoreReference' || + newElement.type === 'bpmn:DataObjectReference' + ) { + newElement.x = element.x + (element.width - newElement.width) / 2; + } + + + newElement = replace.replaceElement(element, newElement, hints); + + if (hints.select !== false) { + selection.select(newElement); + } + + return newElement; + } + + this.replaceElement = replaceElement; + } + + BpmnReplace.$inject = [ + 'bpmnFactory', + 'elementFactory', + 'moddleCopy', + 'modeling', + 'replace', + 'rules', + 'selection' + ]; + + + function isSubProcess(bo) { + return is$1(bo, 'bpmn:SubProcess'); + } + + function hasEventDefinition$1(element, type) { + + var bo = getBusinessObject(element); + + return type && bo.get('eventDefinitions').some(function(definition) { + return is$1(definition, type); + }); + } + + /** + * Compute intersection between two arrays. + */ + function intersection(a1, a2) { + return a1.filter(function(el) { + return a2.indexOf(el) !== -1; + }); + } + + var ReplaceModule = { + __depends__: [ + CopyPasteModule, + ReplaceModule$1, + SelectionModule + ], + bpmnReplace: [ 'type', BpmnReplace ] + }; + + /** + * Returns true, if an element is from a different type + * than a target definition. Takes into account the type, + * event definition type and triggeredByEvent property. + * + * @param {djs.model.Base} element + * + * @return {boolean} + */ + function isDifferentType(element) { + + return function(entry) { + var target = entry.target; + + var businessObject = getBusinessObject(element), + eventDefinition = businessObject.eventDefinitions && businessObject.eventDefinitions[0]; + + var isTypeEqual = businessObject.$type === target.type; + + var isEventDefinitionEqual = ( + (eventDefinition && eventDefinition.$type) === target.eventDefinitionType + ); + + var isTriggeredByEventEqual = ( + businessObject.triggeredByEvent === target.triggeredByEvent + ); + + var isExpandedEqual = ( + target.isExpanded === undefined || + target.isExpanded === isExpanded(element) + ); + + return !isTypeEqual || !isEventDefinitionEqual || !isTriggeredByEventEqual || !isExpandedEqual; + }; + } + + var START_EVENT = [ + { + label: 'Start Event', + actionName: 'replace-with-none-start', + className: 'bpmn-icon-start-event-none', + target: { + type: 'bpmn:StartEvent' + } + }, + { + label: 'Intermediate Throw Event', + actionName: 'replace-with-none-intermediate-throwing', + className: 'bpmn-icon-intermediate-event-none', + target: { + type: 'bpmn:IntermediateThrowEvent' + } + }, + { + label: 'End Event', + actionName: 'replace-with-none-end', + className: 'bpmn-icon-end-event-none', + target: { + type: 'bpmn:EndEvent' + } + }, + { + label: 'Message Start Event', + actionName: 'replace-with-message-start', + className: 'bpmn-icon-start-event-message', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:MessageEventDefinition' + } + }, + { + label: 'Timer Start Event', + actionName: 'replace-with-timer-start', + className: 'bpmn-icon-start-event-timer', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:TimerEventDefinition' + } + }, + { + label: 'Conditional Start Event', + actionName: 'replace-with-conditional-start', + className: 'bpmn-icon-start-event-condition', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:ConditionalEventDefinition' + } + }, + { + label: 'Signal Start Event', + actionName: 'replace-with-signal-start', + className: 'bpmn-icon-start-event-signal', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:SignalEventDefinition' + } + } + ]; + + var START_EVENT_SUB_PROCESS = [ + { + label: 'Start Event', + actionName: 'replace-with-none-start', + className: 'bpmn-icon-start-event-none', + target: { + type: 'bpmn:StartEvent' + } + }, + { + label: 'Intermediate Throw Event', + actionName: 'replace-with-none-intermediate-throwing', + className: 'bpmn-icon-intermediate-event-none', + target: { + type: 'bpmn:IntermediateThrowEvent' + } + }, + { + label: 'End Event', + actionName: 'replace-with-none-end', + className: 'bpmn-icon-end-event-none', + target: { + type: 'bpmn:EndEvent' + } + } + ]; + + var INTERMEDIATE_EVENT = [ + { + label: 'Start Event', + actionName: 'replace-with-none-start', + className: 'bpmn-icon-start-event-none', + target: { + type: 'bpmn:StartEvent' + } + }, + { + label: 'Intermediate Throw Event', + actionName: 'replace-with-none-intermediate-throw', + className: 'bpmn-icon-intermediate-event-none', + target: { + type: 'bpmn:IntermediateThrowEvent' + } + }, + { + label: 'End Event', + actionName: 'replace-with-none-end', + className: 'bpmn-icon-end-event-none', + target: { + type: 'bpmn:EndEvent' + } + }, + { + label: 'Message Intermediate Catch Event', + actionName: 'replace-with-message-intermediate-catch', + className: 'bpmn-icon-intermediate-event-catch-message', + target: { + type: 'bpmn:IntermediateCatchEvent', + eventDefinitionType: 'bpmn:MessageEventDefinition' + } + }, + { + label: 'Message Intermediate Throw Event', + actionName: 'replace-with-message-intermediate-throw', + className: 'bpmn-icon-intermediate-event-throw-message', + target: { + type: 'bpmn:IntermediateThrowEvent', + eventDefinitionType: 'bpmn:MessageEventDefinition' + } + }, + { + label: 'Timer Intermediate Catch Event', + actionName: 'replace-with-timer-intermediate-catch', + className: 'bpmn-icon-intermediate-event-catch-timer', + target: { + type: 'bpmn:IntermediateCatchEvent', + eventDefinitionType: 'bpmn:TimerEventDefinition' + } + }, + { + label: 'Escalation Intermediate Throw Event', + actionName: 'replace-with-escalation-intermediate-throw', + className: 'bpmn-icon-intermediate-event-throw-escalation', + target: { + type: 'bpmn:IntermediateThrowEvent', + eventDefinitionType: 'bpmn:EscalationEventDefinition' + } + }, + { + label: 'Conditional Intermediate Catch Event', + actionName: 'replace-with-conditional-intermediate-catch', + className: 'bpmn-icon-intermediate-event-catch-condition', + target: { + type: 'bpmn:IntermediateCatchEvent', + eventDefinitionType: 'bpmn:ConditionalEventDefinition' + } + }, + { + label: 'Link Intermediate Catch Event', + actionName: 'replace-with-link-intermediate-catch', + className: 'bpmn-icon-intermediate-event-catch-link', + target: { + type: 'bpmn:IntermediateCatchEvent', + eventDefinitionType: 'bpmn:LinkEventDefinition', + eventDefinitionAttrs: { + name: '' + } + } + }, + { + label: 'Link Intermediate Throw Event', + actionName: 'replace-with-link-intermediate-throw', + className: 'bpmn-icon-intermediate-event-throw-link', + target: { + type: 'bpmn:IntermediateThrowEvent', + eventDefinitionType: 'bpmn:LinkEventDefinition', + eventDefinitionAttrs: { + name: '' + } + } + }, + { + label: 'Compensation Intermediate Throw Event', + actionName: 'replace-with-compensation-intermediate-throw', + className: 'bpmn-icon-intermediate-event-throw-compensation', + target: { + type: 'bpmn:IntermediateThrowEvent', + eventDefinitionType: 'bpmn:CompensateEventDefinition' + } + }, + { + label: 'Signal Intermediate Catch Event', + actionName: 'replace-with-signal-intermediate-catch', + className: 'bpmn-icon-intermediate-event-catch-signal', + target: { + type: 'bpmn:IntermediateCatchEvent', + eventDefinitionType: 'bpmn:SignalEventDefinition' + } + }, + { + label: 'Signal Intermediate Throw Event', + actionName: 'replace-with-signal-intermediate-throw', + className: 'bpmn-icon-intermediate-event-throw-signal', + target: { + type: 'bpmn:IntermediateThrowEvent', + eventDefinitionType: 'bpmn:SignalEventDefinition' + } + } + ]; + + var END_EVENT = [ + { + label: 'Start Event', + actionName: 'replace-with-none-start', + className: 'bpmn-icon-start-event-none', + target: { + type: 'bpmn:StartEvent' + } + }, + { + label: 'Intermediate Throw Event', + actionName: 'replace-with-none-intermediate-throw', + className: 'bpmn-icon-intermediate-event-none', + target: { + type: 'bpmn:IntermediateThrowEvent' + } + }, + { + label: 'End Event', + actionName: 'replace-with-none-end', + className: 'bpmn-icon-end-event-none', + target: { + type: 'bpmn:EndEvent' + } + }, + { + label: 'Message End Event', + actionName: 'replace-with-message-end', + className: 'bpmn-icon-end-event-message', + target: { + type: 'bpmn:EndEvent', + eventDefinitionType: 'bpmn:MessageEventDefinition' + } + }, + { + label: 'Escalation End Event', + actionName: 'replace-with-escalation-end', + className: 'bpmn-icon-end-event-escalation', + target: { + type: 'bpmn:EndEvent', + eventDefinitionType: 'bpmn:EscalationEventDefinition' + } + }, + { + label: 'Error End Event', + actionName: 'replace-with-error-end', + className: 'bpmn-icon-end-event-error', + target: { + type: 'bpmn:EndEvent', + eventDefinitionType: 'bpmn:ErrorEventDefinition' + } + }, + { + label: 'Cancel End Event', + actionName: 'replace-with-cancel-end', + className: 'bpmn-icon-end-event-cancel', + target: { + type: 'bpmn:EndEvent', + eventDefinitionType: 'bpmn:CancelEventDefinition' + } + }, + { + label: 'Compensation End Event', + actionName: 'replace-with-compensation-end', + className: 'bpmn-icon-end-event-compensation', + target: { + type: 'bpmn:EndEvent', + eventDefinitionType: 'bpmn:CompensateEventDefinition' + } + }, + { + label: 'Signal End Event', + actionName: 'replace-with-signal-end', + className: 'bpmn-icon-end-event-signal', + target: { + type: 'bpmn:EndEvent', + eventDefinitionType: 'bpmn:SignalEventDefinition' + } + }, + { + label: 'Terminate End Event', + actionName: 'replace-with-terminate-end', + className: 'bpmn-icon-end-event-terminate', + target: { + type: 'bpmn:EndEvent', + eventDefinitionType: 'bpmn:TerminateEventDefinition' + } + } + ]; + + var GATEWAY = [ + { + label: 'Exclusive Gateway', + actionName: 'replace-with-exclusive-gateway', + className: 'bpmn-icon-gateway-xor', + target: { + type: 'bpmn:ExclusiveGateway' + } + }, + { + label: 'Parallel Gateway', + actionName: 'replace-with-parallel-gateway', + className: 'bpmn-icon-gateway-parallel', + target: { + type: 'bpmn:ParallelGateway' + } + }, + { + label: 'Inclusive Gateway', + actionName: 'replace-with-inclusive-gateway', + className: 'bpmn-icon-gateway-or', + target: { + type: 'bpmn:InclusiveGateway' + } + }, + { + label: 'Complex Gateway', + actionName: 'replace-with-complex-gateway', + className: 'bpmn-icon-gateway-complex', + target: { + type: 'bpmn:ComplexGateway' + } + }, + { + label: 'Event based Gateway', + actionName: 'replace-with-event-based-gateway', + className: 'bpmn-icon-gateway-eventbased', + target: { + type: 'bpmn:EventBasedGateway', + instantiate: false, + eventGatewayType: 'Exclusive' + } + } + + // Gateways deactivated until https://github.com/bpmn-io/bpmn-js/issues/194 + // { + // label: 'Event based instantiating Gateway', + // actionName: 'replace-with-exclusive-event-based-gateway', + // className: 'bpmn-icon-exclusive-event-based', + // target: { + // type: 'bpmn:EventBasedGateway' + // }, + // options: { + // businessObject: { instantiate: true, eventGatewayType: 'Exclusive' } + // } + // }, + // { + // label: 'Parallel Event based instantiating Gateway', + // actionName: 'replace-with-parallel-event-based-instantiate-gateway', + // className: 'bpmn-icon-parallel-event-based-instantiate-gateway', + // target: { + // type: 'bpmn:EventBasedGateway' + // }, + // options: { + // businessObject: { instantiate: true, eventGatewayType: 'Parallel' } + // } + // } + ]; + + var SUBPROCESS_EXPANDED = [ + { + label: 'Transaction', + actionName: 'replace-with-transaction', + className: 'bpmn-icon-transaction', + target: { + type: 'bpmn:Transaction', + isExpanded: true + } + }, + { + label: 'Event Sub Process', + actionName: 'replace-with-event-subprocess', + className: 'bpmn-icon-event-subprocess-expanded', + target: { + type: 'bpmn:SubProcess', + triggeredByEvent: true, + isExpanded: true + } + }, + { + label: 'Sub Process (collapsed)', + actionName: 'replace-with-collapsed-subprocess', + className: 'bpmn-icon-subprocess-collapsed', + target: { + type: 'bpmn:SubProcess', + isExpanded: false + } + } + ]; + + var TRANSACTION = [ + { + label: 'Sub Process', + actionName: 'replace-with-subprocess', + className: 'bpmn-icon-subprocess-expanded', + target: { + type: 'bpmn:SubProcess', + isExpanded: true + } + }, + { + label: 'Event Sub Process', + actionName: 'replace-with-event-subprocess', + className: 'bpmn-icon-event-subprocess-expanded', + target: { + type: 'bpmn:SubProcess', + triggeredByEvent: true, + isExpanded: true + } + } + ]; + + var EVENT_SUB_PROCESS = [ + { + label: 'Sub Process', + actionName: 'replace-with-subprocess', + className: 'bpmn-icon-subprocess-expanded', + target: { + type: 'bpmn:SubProcess', + isExpanded: true + } + }, + { + label: 'Transaction', + actionName: 'replace-with-transaction', + className: 'bpmn-icon-transaction', + target: { + type: 'bpmn:Transaction', + isExpanded: true + } + } + ]; + + var TASK = [ + { + label: 'Task', + actionName: 'replace-with-task', + className: 'bpmn-icon-task', + target: { + type: 'bpmn:Task' + } + }, + { + label: 'Send Task', + actionName: 'replace-with-send-task', + className: 'bpmn-icon-send', + target: { + type: 'bpmn:SendTask' + } + }, + { + label: 'Receive Task', + actionName: 'replace-with-receive-task', + className: 'bpmn-icon-receive', + target: { + type: 'bpmn:ReceiveTask' + } + }, + { + label: 'User Task', + actionName: 'replace-with-user-task', + className: 'bpmn-icon-user', + target: { + type: 'bpmn:UserTask' + } + }, + { + label: 'Manual Task', + actionName: 'replace-with-manual-task', + className: 'bpmn-icon-manual', + target: { + type: 'bpmn:ManualTask' + } + }, + { + label: 'Business Rule Task', + actionName: 'replace-with-rule-task', + className: 'bpmn-icon-business-rule', + target: { + type: 'bpmn:BusinessRuleTask' + } + }, + { + label: 'Service Task', + actionName: 'replace-with-service-task', + className: 'bpmn-icon-service', + target: { + type: 'bpmn:ServiceTask' + } + }, + { + label: 'Script Task', + actionName: 'replace-with-script-task', + className: 'bpmn-icon-script', + target: { + type: 'bpmn:ScriptTask' + } + }, + { + label: 'Call Activity', + actionName: 'replace-with-call-activity', + className: 'bpmn-icon-call-activity', + target: { + type: 'bpmn:CallActivity' + } + }, + { + label: 'Sub Process (collapsed)', + actionName: 'replace-with-collapsed-subprocess', + className: 'bpmn-icon-subprocess-collapsed', + target: { + type: 'bpmn:SubProcess', + isExpanded: false + } + }, + { + label: 'Sub Process (expanded)', + actionName: 'replace-with-expanded-subprocess', + className: 'bpmn-icon-subprocess-expanded', + target: { + type: 'bpmn:SubProcess', + isExpanded: true + } + } + ]; + + var DATA_OBJECT_REFERENCE = [ + { + label: 'Data Store Reference', + actionName: 'replace-with-data-store-reference', + className: 'bpmn-icon-data-store', + target: { + type: 'bpmn:DataStoreReference' + } + } + ]; + + var DATA_STORE_REFERENCE = [ + { + label: 'Data Object Reference', + actionName: 'replace-with-data-object-reference', + className: 'bpmn-icon-data-object', + target: { + type: 'bpmn:DataObjectReference' + } + } + ]; + + var BOUNDARY_EVENT = [ + { + label: 'Message Boundary Event', + actionName: 'replace-with-message-boundary', + className: 'bpmn-icon-intermediate-event-catch-message', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:MessageEventDefinition' + } + }, + { + label: 'Timer Boundary Event', + actionName: 'replace-with-timer-boundary', + className: 'bpmn-icon-intermediate-event-catch-timer', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:TimerEventDefinition' + } + }, + { + label: 'Escalation Boundary Event', + actionName: 'replace-with-escalation-boundary', + className: 'bpmn-icon-intermediate-event-catch-escalation', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:EscalationEventDefinition' + } + }, + { + label: 'Conditional Boundary Event', + actionName: 'replace-with-conditional-boundary', + className: 'bpmn-icon-intermediate-event-catch-condition', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:ConditionalEventDefinition' + } + }, + { + label: 'Error Boundary Event', + actionName: 'replace-with-error-boundary', + className: 'bpmn-icon-intermediate-event-catch-error', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:ErrorEventDefinition' + } + }, + { + label: 'Cancel Boundary Event', + actionName: 'replace-with-cancel-boundary', + className: 'bpmn-icon-intermediate-event-catch-cancel', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:CancelEventDefinition' + } + }, + { + label: 'Signal Boundary Event', + actionName: 'replace-with-signal-boundary', + className: 'bpmn-icon-intermediate-event-catch-signal', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:SignalEventDefinition' + } + }, + { + label: 'Compensation Boundary Event', + actionName: 'replace-with-compensation-boundary', + className: 'bpmn-icon-intermediate-event-catch-compensation', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:CompensateEventDefinition' + } + }, + { + label: 'Message Boundary Event (non-interrupting)', + actionName: 'replace-with-non-interrupting-message-boundary', + className: 'bpmn-icon-intermediate-event-catch-non-interrupting-message', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:MessageEventDefinition', + cancelActivity: false + } + }, + { + label: 'Timer Boundary Event (non-interrupting)', + actionName: 'replace-with-non-interrupting-timer-boundary', + className: 'bpmn-icon-intermediate-event-catch-non-interrupting-timer', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:TimerEventDefinition', + cancelActivity: false + } + }, + { + label: 'Escalation Boundary Event (non-interrupting)', + actionName: 'replace-with-non-interrupting-escalation-boundary', + className: 'bpmn-icon-intermediate-event-catch-non-interrupting-escalation', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:EscalationEventDefinition', + cancelActivity: false + } + }, + { + label: 'Conditional Boundary Event (non-interrupting)', + actionName: 'replace-with-non-interrupting-conditional-boundary', + className: 'bpmn-icon-intermediate-event-catch-non-interrupting-condition', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:ConditionalEventDefinition', + cancelActivity: false + } + }, + { + label: 'Signal Boundary Event (non-interrupting)', + actionName: 'replace-with-non-interrupting-signal-boundary', + className: 'bpmn-icon-intermediate-event-catch-non-interrupting-signal', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:SignalEventDefinition', + cancelActivity: false + } + } + ]; + + var EVENT_SUB_PROCESS_START_EVENT = [ + { + label: 'Message Start Event', + actionName: 'replace-with-message-start', + className: 'bpmn-icon-start-event-message', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:MessageEventDefinition' + } + }, + { + label: 'Timer Start Event', + actionName: 'replace-with-timer-start', + className: 'bpmn-icon-start-event-timer', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:TimerEventDefinition' + } + }, + { + label: 'Conditional Start Event', + actionName: 'replace-with-conditional-start', + className: 'bpmn-icon-start-event-condition', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:ConditionalEventDefinition' + } + }, + { + label: 'Signal Start Event', + actionName: 'replace-with-signal-start', + className: 'bpmn-icon-start-event-signal', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:SignalEventDefinition' + } + }, + { + label: 'Error Start Event', + actionName: 'replace-with-error-start', + className: 'bpmn-icon-start-event-error', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:ErrorEventDefinition' + } + }, + { + label: 'Escalation Start Event', + actionName: 'replace-with-escalation-start', + className: 'bpmn-icon-start-event-escalation', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:EscalationEventDefinition' + } + }, + { + label: 'Compensation Start Event', + actionName: 'replace-with-compensation-start', + className: 'bpmn-icon-start-event-compensation', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:CompensateEventDefinition' + } + }, + { + label: 'Message Start Event (non-interrupting)', + actionName: 'replace-with-non-interrupting-message-start', + className: 'bpmn-icon-start-event-non-interrupting-message', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:MessageEventDefinition', + isInterrupting: false + } + }, + { + label: 'Timer Start Event (non-interrupting)', + actionName: 'replace-with-non-interrupting-timer-start', + className: 'bpmn-icon-start-event-non-interrupting-timer', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:TimerEventDefinition', + isInterrupting: false + } + }, + { + label: 'Conditional Start Event (non-interrupting)', + actionName: 'replace-with-non-interrupting-conditional-start', + className: 'bpmn-icon-start-event-non-interrupting-condition', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:ConditionalEventDefinition', + isInterrupting: false + } + }, + { + label: 'Signal Start Event (non-interrupting)', + actionName: 'replace-with-non-interrupting-signal-start', + className: 'bpmn-icon-start-event-non-interrupting-signal', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:SignalEventDefinition', + isInterrupting: false + } + }, + { + label: 'Escalation Start Event (non-interrupting)', + actionName: 'replace-with-non-interrupting-escalation-start', + className: 'bpmn-icon-start-event-non-interrupting-escalation', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:EscalationEventDefinition', + isInterrupting: false + } + } + ]; + + var SEQUENCE_FLOW = [ + { + label: 'Sequence Flow', + actionName: 'replace-with-sequence-flow', + className: 'bpmn-icon-connection' + }, + { + label: 'Default Flow', + actionName: 'replace-with-default-flow', + className: 'bpmn-icon-default-flow' + }, + { + label: 'Conditional Flow', + actionName: 'replace-with-conditional-flow', + className: 'bpmn-icon-conditional-flow' + } + ]; + + var PARTICIPANT = [ + { + label: 'Expanded Pool', + actionName: 'replace-with-expanded-pool', + className: 'bpmn-icon-participant', + target: { + type: 'bpmn:Participant', + isExpanded: true + } + }, + { + label: function(element) { + var label = 'Empty Pool'; + + if (element.children && element.children.length) { + label += ' (removes content)'; + } + + return label; + }, + actionName: 'replace-with-collapsed-pool', + + // TODO(@janstuemmel): maybe design new icon + className: 'bpmn-icon-lane', + target: { + type: 'bpmn:Participant', + isExpanded: false + } + } + ]; + + /** + * This module is an element agnostic replace menu provider for the popup menu. + */ + function ReplaceMenuProvider( + bpmnFactory, popupMenu, modeling, moddle, + bpmnReplace, rules, translate) { + + this._bpmnFactory = bpmnFactory; + this._popupMenu = popupMenu; + this._modeling = modeling; + this._moddle = moddle; + this._bpmnReplace = bpmnReplace; + this._rules = rules; + this._translate = translate; + + this.register(); + } + + ReplaceMenuProvider.$inject = [ + 'bpmnFactory', + 'popupMenu', + 'modeling', + 'moddle', + 'bpmnReplace', + 'rules', + 'translate' + ]; + + + /** + * Register replace menu provider in the popup menu + */ + ReplaceMenuProvider.prototype.register = function() { + this._popupMenu.registerProvider('bpmn-replace', this); + }; + + + /** + * Get all entries from replaceOptions for the given element and apply filters + * on them. Get for example only elements, which are different from the current one. + * + * @param {djs.model.Base} element + * + * @return {Array} a list of menu entry items + */ + ReplaceMenuProvider.prototype.getEntries = function(element) { + + var businessObject = element.businessObject; + + var rules = this._rules; + + var entries; + + if (!rules.allowed('shape.replace', { element: element })) { + return []; + } + + var differentType = isDifferentType(element); + + if (is$1(businessObject, 'bpmn:DataObjectReference')) { + return this._createEntries(element, DATA_OBJECT_REFERENCE); + } + + if (is$1(businessObject, 'bpmn:DataStoreReference') && !is$1(element.parent, 'bpmn:Collaboration')) { + return this._createEntries(element, DATA_STORE_REFERENCE); + } + + // start events outside sub processes + if (is$1(businessObject, 'bpmn:StartEvent') && !is$1(businessObject.$parent, 'bpmn:SubProcess')) { + + entries = filter(START_EVENT, differentType); + + return this._createEntries(element, entries); + } + + // expanded/collapsed pools + if (is$1(businessObject, 'bpmn:Participant')) { + + entries = filter(PARTICIPANT, function(entry) { + return isExpanded(element) !== entry.target.isExpanded; + }); + + return this._createEntries(element, entries); + } + + // start events inside event sub processes + if (is$1(businessObject, 'bpmn:StartEvent') && isEventSubProcess(businessObject.$parent)) { + entries = filter(EVENT_SUB_PROCESS_START_EVENT, function(entry) { + + var target = entry.target; + + var isInterrupting = target.isInterrupting !== false; + + var isInterruptingEqual = getBusinessObject(element).isInterrupting === isInterrupting; + + // filters elements which types and event definition are equal but have have different interrupting types + return differentType(entry) || !differentType(entry) && !isInterruptingEqual; + + }); + + return this._createEntries(element, entries); + } + + // start events inside sub processes + if (is$1(businessObject, 'bpmn:StartEvent') && !isEventSubProcess(businessObject.$parent) + && is$1(businessObject.$parent, 'bpmn:SubProcess')) { + entries = filter(START_EVENT_SUB_PROCESS, differentType); + + return this._createEntries(element, entries); + } + + // end events + if (is$1(businessObject, 'bpmn:EndEvent')) { + + entries = filter(END_EVENT, function(entry) { + var target = entry.target; + + // hide cancel end events outside transactions + if (target.eventDefinitionType == 'bpmn:CancelEventDefinition' && !is$1(businessObject.$parent, 'bpmn:Transaction')) { + return false; + } + + return differentType(entry); + }); + + return this._createEntries(element, entries); + } + + // boundary events + if (is$1(businessObject, 'bpmn:BoundaryEvent')) { + + entries = filter(BOUNDARY_EVENT, function(entry) { + + var target = entry.target; + + if (target.eventDefinitionType == 'bpmn:CancelEventDefinition' && + !is$1(businessObject.attachedToRef, 'bpmn:Transaction')) { + return false; + } + var cancelActivity = target.cancelActivity !== false; + + var isCancelActivityEqual = businessObject.cancelActivity == cancelActivity; + + return differentType(entry) || !differentType(entry) && !isCancelActivityEqual; + }); + + return this._createEntries(element, entries); + } + + // intermediate events + if (is$1(businessObject, 'bpmn:IntermediateCatchEvent') || + is$1(businessObject, 'bpmn:IntermediateThrowEvent')) { + + entries = filter(INTERMEDIATE_EVENT, differentType); + + return this._createEntries(element, entries); + } + + // gateways + if (is$1(businessObject, 'bpmn:Gateway')) { + + entries = filter(GATEWAY, differentType); + + return this._createEntries(element, entries); + } + + // transactions + if (is$1(businessObject, 'bpmn:Transaction')) { + + entries = filter(TRANSACTION, differentType); + + return this._createEntries(element, entries); + } + + // expanded event sub processes + if (isEventSubProcess(businessObject) && isExpanded(element)) { + + entries = filter(EVENT_SUB_PROCESS, differentType); + + return this._createEntries(element, entries); + } + + // expanded sub processes + if (is$1(businessObject, 'bpmn:SubProcess') && isExpanded(element)) { + + entries = filter(SUBPROCESS_EXPANDED, differentType); + + return this._createEntries(element, entries); + } + + // collapsed ad hoc sub processes + if (is$1(businessObject, 'bpmn:AdHocSubProcess') && !isExpanded(element)) { + + entries = filter(TASK, function(entry) { + + var target = entry.target; + + var isTargetSubProcess = target.type === 'bpmn:SubProcess'; + + var isTargetExpanded = target.isExpanded === true; + + return isDifferentType(element) && (!isTargetSubProcess || isTargetExpanded); + }); + + return this._createEntries(element, entries); + } + + // sequence flows + if (is$1(businessObject, 'bpmn:SequenceFlow')) { + return this._createSequenceFlowEntries(element, SEQUENCE_FLOW); + } + + // flow nodes + if (is$1(businessObject, 'bpmn:FlowNode')) { + entries = filter(TASK, differentType); + + // collapsed SubProcess can not be replaced with itself + if (is$1(businessObject, 'bpmn:SubProcess') && !isExpanded(element)) { + entries = filter(entries, function(entry) { + return entry.label !== 'Sub Process (collapsed)'; + }); + } + + return this._createEntries(element, entries); + } + + return []; + }; + + + /** + * Get a list of header items for the given element. This includes buttons + * for multi instance markers and for the ad hoc marker. + * + * @param {djs.model.Base} element + * + * @return {Array} a list of menu entry items + */ + ReplaceMenuProvider.prototype.getHeaderEntries = function(element) { + + var headerEntries = []; + + if (is$1(element, 'bpmn:Activity') && !isEventSubProcess(element)) { + headerEntries = headerEntries.concat(this._getLoopEntries(element)); + } + + if (is$1(element, 'bpmn:DataObjectReference')) { + headerEntries = headerEntries.concat(this._getDataObjectIsCollection(element)); + } + + if (is$1(element, 'bpmn:Participant')) { + headerEntries = headerEntries.concat(this._getParticipantMultiplicity(element)); + } + + if (is$1(element, 'bpmn:SubProcess') && + !is$1(element, 'bpmn:Transaction') && + !isEventSubProcess(element)) { + headerEntries.push(this._getAdHocEntry(element)); + } + + return headerEntries; + }; + + + /** + * Creates an array of menu entry objects for a given element and filters the replaceOptions + * according to a filter function. + * + * @param {djs.model.Base} element + * @param {Object} replaceOptions + * + * @return {Array} a list of menu items + */ + ReplaceMenuProvider.prototype._createEntries = function(element, replaceOptions) { + var menuEntries = []; + + var self = this; + + forEach$1(replaceOptions, function(definition) { + var entry = self._createMenuEntry(definition, element); + + menuEntries.push(entry); + }); + + return menuEntries; + }; + + /** + * Creates an array of menu entry objects for a given sequence flow. + * + * @param {djs.model.Base} element + * @param {Object} replaceOptions + + * @return {Array} a list of menu items + */ + ReplaceMenuProvider.prototype._createSequenceFlowEntries = function(element, replaceOptions) { + + var businessObject = getBusinessObject(element); + + var menuEntries = []; + + var modeling = this._modeling, + moddle = this._moddle; + + var self = this; + + forEach$1(replaceOptions, function(entry) { + + switch (entry.actionName) { + case 'replace-with-default-flow': + if (businessObject.sourceRef.default !== businessObject && + (is$1(businessObject.sourceRef, 'bpmn:ExclusiveGateway') || + is$1(businessObject.sourceRef, 'bpmn:InclusiveGateway') || + is$1(businessObject.sourceRef, 'bpmn:ComplexGateway') || + is$1(businessObject.sourceRef, 'bpmn:Activity'))) { + + menuEntries.push(self._createMenuEntry(entry, element, function() { + modeling.updateProperties(element.source, { default: businessObject }); + })); + } + break; + case 'replace-with-conditional-flow': + if (!businessObject.conditionExpression && is$1(businessObject.sourceRef, 'bpmn:Activity')) { + + menuEntries.push(self._createMenuEntry(entry, element, function() { + var conditionExpression = moddle.create('bpmn:FormalExpression', { body: '' }); + + modeling.updateProperties(element, { conditionExpression: conditionExpression }); + })); + } + break; + default: + + // default flows + if (is$1(businessObject.sourceRef, 'bpmn:Activity') && businessObject.conditionExpression) { + return menuEntries.push(self._createMenuEntry(entry, element, function() { + modeling.updateProperties(element, { conditionExpression: undefined }); + })); + } + + // conditional flows + if ((is$1(businessObject.sourceRef, 'bpmn:ExclusiveGateway') || + is$1(businessObject.sourceRef, 'bpmn:InclusiveGateway') || + is$1(businessObject.sourceRef, 'bpmn:ComplexGateway') || + is$1(businessObject.sourceRef, 'bpmn:Activity')) && + businessObject.sourceRef.default === businessObject) { + + return menuEntries.push(self._createMenuEntry(entry, element, function() { + modeling.updateProperties(element.source, { default: undefined }); + })); + } + } + }); + + return menuEntries; + }; + + + /** + * Creates and returns a single menu entry item. + * + * @param {Object} definition a single replace options definition object + * @param {djs.model.Base} element + * @param {Function} [action] an action callback function which gets called when + * the menu entry is being triggered. + * + * @return {Object} menu entry item + */ + ReplaceMenuProvider.prototype._createMenuEntry = function(definition, element, action) { + var translate = this._translate; + var replaceElement = this._bpmnReplace.replaceElement; + + var replaceAction = function() { + return replaceElement(element, definition.target); + }; + + var label = definition.label; + if (label && typeof label === 'function') { + label = label(element); + } + + action = action || replaceAction; + + var menuEntry = { + label: translate(label), + className: definition.className, + id: definition.actionName, + action: action + }; + + return menuEntry; + }; + + /** + * Get a list of menu items containing buttons for multi instance markers + * + * @param {djs.model.Base} element + * + * @return {Array} a list of menu items + */ + ReplaceMenuProvider.prototype._getLoopEntries = function(element) { + + var self = this; + var translate = this._translate; + + function toggleLoopEntry(event, entry) { + var newLoopCharacteristics = getBusinessObject(element).loopCharacteristics; + + if (entry.active) { + newLoopCharacteristics = undefined; + } else { + if (isUndefined$2(entry.options.isSequential) || !newLoopCharacteristics + || !is$1(newLoopCharacteristics, entry.options.loopCharacteristics)) { + newLoopCharacteristics = self._moddle.create(entry.options.loopCharacteristics); + } + + newLoopCharacteristics.isSequential = entry.options.isSequential; + } + self._modeling.updateProperties(element, { loopCharacteristics: newLoopCharacteristics }); + } + + var businessObject = getBusinessObject(element), + loopCharacteristics = businessObject.loopCharacteristics; + + var isSequential, + isLoop, + isParallel; + + if (loopCharacteristics) { + isSequential = loopCharacteristics.isSequential; + isLoop = loopCharacteristics.isSequential === undefined; + isParallel = loopCharacteristics.isSequential !== undefined && !loopCharacteristics.isSequential; + } + + + var loopEntries = [ + { + id: 'toggle-parallel-mi', + className: 'bpmn-icon-parallel-mi-marker', + title: translate('Parallel Multi Instance'), + active: isParallel, + action: toggleLoopEntry, + options: { + loopCharacteristics: 'bpmn:MultiInstanceLoopCharacteristics', + isSequential: false + } + }, + { + id: 'toggle-sequential-mi', + className: 'bpmn-icon-sequential-mi-marker', + title: translate('Sequential Multi Instance'), + active: isSequential, + action: toggleLoopEntry, + options: { + loopCharacteristics: 'bpmn:MultiInstanceLoopCharacteristics', + isSequential: true + } + }, + { + id: 'toggle-loop', + className: 'bpmn-icon-loop-marker', + title: translate('Loop'), + active: isLoop, + action: toggleLoopEntry, + options: { + loopCharacteristics: 'bpmn:StandardLoopCharacteristics' + } + } + ]; + return loopEntries; + }; + + /** + * Get a list of menu items containing a button for the collection marker + * + * @param {djs.model.Base} element + * + * @return {Array} a list of menu items + */ + ReplaceMenuProvider.prototype._getDataObjectIsCollection = function(element) { + + var self = this; + var translate = this._translate; + + function toggleIsCollection(event, entry) { + self._modeling.updateModdleProperties( + element, + dataObject, + { isCollection: !entry.active }); + } + + var dataObject = element.businessObject.dataObjectRef, + isCollection = dataObject.isCollection; + + var dataObjectEntries = [ + { + id: 'toggle-is-collection', + className: 'bpmn-icon-parallel-mi-marker', + title: translate('Collection'), + active: isCollection, + action: toggleIsCollection, + } + ]; + return dataObjectEntries; + }; + + /** + * Get a list of menu items containing a button for the participant multiplicity marker + * + * @param {djs.model.Base} element + * + * @return {Array} a list of menu items + */ + ReplaceMenuProvider.prototype._getParticipantMultiplicity = function(element) { + + var self = this; + var bpmnFactory = this._bpmnFactory; + var translate = this._translate; + + function toggleParticipantMultiplicity(event, entry) { + var isActive = entry.active; + var participantMultiplicity; + + if (!isActive) { + participantMultiplicity = bpmnFactory.create('bpmn:ParticipantMultiplicity'); + } + + self._modeling.updateProperties( + element, + { participantMultiplicity: participantMultiplicity }); + } + + var participantMultiplicity = element.businessObject.participantMultiplicity; + + var participantEntries = [ + { + id: 'toggle-participant-multiplicity', + className: 'bpmn-icon-parallel-mi-marker', + title: translate('Participant Multiplicity'), + active: !!participantMultiplicity, + action: toggleParticipantMultiplicity, + } + ]; + return participantEntries; + }; + + + /** + * Get the menu items containing a button for the ad hoc marker + * + * @param {djs.model.Base} element + * + * @return {Object} a menu item + */ + ReplaceMenuProvider.prototype._getAdHocEntry = function(element) { + var translate = this._translate; + var businessObject = getBusinessObject(element); + + var isAdHoc = is$1(businessObject, 'bpmn:AdHocSubProcess'); + + var replaceElement = this._bpmnReplace.replaceElement; + + var adHocEntry = { + id: 'toggle-adhoc', + className: 'bpmn-icon-ad-hoc-marker', + title: translate('Ad-hoc'), + active: isAdHoc, + action: function(event, entry) { + if (isAdHoc) { + return replaceElement(element, { type: 'bpmn:SubProcess' }, { + autoResize: false, + layoutConnection: false + }); + } else { + return replaceElement(element, { type: 'bpmn:AdHocSubProcess' }, { + autoResize: false, + layoutConnection: false + }); + } + } + }; + + return adHocEntry; + }; + + var PopupMenuModule = { + __depends__: [ + PopupMenuModule$1, + ReplaceModule + ], + __init__: [ 'replaceMenuProvider' ], + replaceMenuProvider: [ 'type', ReplaceMenuProvider ] + }; + + var max$4 = Math.max, + min$2 = Math.min; + + var DEFAULT_CHILD_BOX_PADDING = 20; + + + /** + * Substract a TRBL from another + * + * @param {TRBL} trblA + * @param {TRBL} trblB + * + * @return {TRBL} + */ + function substractTRBL(trblA, trblB) { + return { + top: trblA.top - trblB.top, + right: trblA.right - trblB.right, + bottom: trblA.bottom - trblB.bottom, + left: trblA.left - trblB.left + }; + } + + /** + * Resize the given bounds by the specified delta from a given anchor point. + * + * @param {Bounds} bounds the bounding box that should be resized + * @param {string} direction in which the element is resized (nw, ne, se, sw) + * @param {Point} delta of the resize operation + * + * @return {Bounds} resized bounding box + */ + function resizeBounds$1(bounds, direction, delta) { + var dx = delta.x, + dy = delta.y; + + var newBounds = { + x: bounds.x, + y: bounds.y, + width: bounds.width, + height: bounds.height + }; + + if (direction.indexOf('n') !== -1) { + newBounds.y = bounds.y + dy; + newBounds.height = bounds.height - dy; + } else if (direction.indexOf('s') !== -1) { + newBounds.height = bounds.height + dy; + } + + if (direction.indexOf('e') !== -1) { + newBounds.width = bounds.width + dx; + } else if (direction.indexOf('w') !== -1) { + newBounds.x = bounds.x + dx; + newBounds.width = bounds.width - dx; + } + + return newBounds; + } + + + /** + * Resize the given bounds by applying the passed + * { top, right, bottom, left } delta. + * + * @param {Bounds} bounds + * @param {TRBL} trblResize + * + * @return {Bounds} + */ + function resizeTRBL(bounds, resize) { + return { + x: bounds.x + (resize.left || 0), + y: bounds.y + (resize.top || 0), + width: bounds.width - (resize.left || 0) + (resize.right || 0), + height: bounds.height - (resize.top || 0) + (resize.bottom || 0) + }; + } + + + function applyConstraints(attr, trbl, resizeConstraints) { + + var value = trbl[attr], + minValue = resizeConstraints.min && resizeConstraints.min[attr], + maxValue = resizeConstraints.max && resizeConstraints.max[attr]; + + if (isNumber(minValue)) { + value = (/top|left/.test(attr) ? min$2 : max$4)(value, minValue); + } + + if (isNumber(maxValue)) { + value = (/top|left/.test(attr) ? max$4 : min$2)(value, maxValue); + } + + return value; + } + + function ensureConstraints$1(currentBounds, resizeConstraints) { + + if (!resizeConstraints) { + return currentBounds; + } + + var currentTrbl = asTRBL(currentBounds); + + return asBounds({ + top: applyConstraints('top', currentTrbl, resizeConstraints), + right: applyConstraints('right', currentTrbl, resizeConstraints), + bottom: applyConstraints('bottom', currentTrbl, resizeConstraints), + left: applyConstraints('left', currentTrbl, resizeConstraints) + }); + } + + + function getMinResizeBounds(direction, currentBounds, minDimensions, childrenBounds) { + + var currentBox = asTRBL(currentBounds); + + var minBox = { + top: /n/.test(direction) ? currentBox.bottom - minDimensions.height : currentBox.top, + left: /w/.test(direction) ? currentBox.right - minDimensions.width : currentBox.left, + bottom: /s/.test(direction) ? currentBox.top + minDimensions.height : currentBox.bottom, + right: /e/.test(direction) ? currentBox.left + minDimensions.width : currentBox.right + }; + + var childrenBox = childrenBounds ? asTRBL(childrenBounds) : minBox; + + var combinedBox = { + top: min$2(minBox.top, childrenBox.top), + left: min$2(minBox.left, childrenBox.left), + bottom: max$4(minBox.bottom, childrenBox.bottom), + right: max$4(minBox.right, childrenBox.right) + }; + + return asBounds(combinedBox); + } + + function asPadding(mayBePadding, defaultValue) { + if (typeof mayBePadding !== 'undefined') { + return mayBePadding; + } else { + return DEFAULT_CHILD_BOX_PADDING; + } + } + + function addPadding$1(bbox, padding) { + var left, right, top, bottom; + + if (typeof padding === 'object') { + left = asPadding(padding.left); + right = asPadding(padding.right); + top = asPadding(padding.top); + bottom = asPadding(padding.bottom); + } else { + left = right = top = bottom = asPadding(padding); + } + + return { + x: bbox.x - left, + y: bbox.y - top, + width: bbox.width + left + right, + height: bbox.height + top + bottom + }; + } + + + /** + * Is the given element part of the resize + * targets min boundary box? + * + * This is the default implementation which excludes + * connections and labels. + * + * @param {djs.model.Base} element + */ + function isBBoxChild(element) { + + // exclude connections + if (element.waypoints) { + return false; + } + + // exclude labels + if (element.type === 'label') { + return false; + } + + return true; + } + + /** + * Return children bounding computed from a shapes children + * or a list of prefiltered children. + * + * @param {djs.model.Shape|Array} shapeOrChildren + * @param {number|Object} padding + * + * @return {Bounds} + */ + function computeChildrenBBox(shapeOrChildren, padding) { + + var elements; + + // compute based on shape + if (shapeOrChildren.length === undefined) { + + // grab all the children that are part of the + // parents children box + elements = filter(shapeOrChildren.children, isBBoxChild); + + } else { + elements = shapeOrChildren; + } + + if (elements.length) { + return addPadding$1(getBBox(elements), padding); + } + } + + var abs$4 = Math.abs; + + + function getTRBLResize(oldBounds, newBounds) { + return substractTRBL(asTRBL(newBounds), asTRBL(oldBounds)); + } + + + var LANE_PARENTS = [ + 'bpmn:Participant', + 'bpmn:Process', + 'bpmn:SubProcess' + ]; + + var LANE_INDENTATION = 30; + + + /** + * Collect all lane shapes in the given paren + * + * @param {djs.model.Shape} shape + * @param {Array} [collectedShapes] + * + * @return {Array} + */ + function collectLanes(shape, collectedShapes) { + + collectedShapes = collectedShapes || []; + + shape.children.filter(function(s) { + if (is$1(s, 'bpmn:Lane')) { + collectLanes(s, collectedShapes); + + collectedShapes.push(s); + } + }); + + return collectedShapes; + } + + + /** + * Return the lane children of the given element. + * + * @param {djs.model.Shape} shape + * + * @return {Array} + */ + function getChildLanes(shape) { + return shape.children.filter(function(c) { + return is$1(c, 'bpmn:Lane'); + }); + } + + + /** + * Return the root element containing the given lane shape + * + * @param {djs.model.Shape} shape + * + * @return {djs.model.Shape} + */ + function getLanesRoot(shape) { + return getParent(shape, LANE_PARENTS) || shape; + } + + + /** + * Compute the required resize operations for lanes + * adjacent to the given shape, assuming it will be + * resized to the given new bounds. + * + * @param {djs.model.Shape} shape + * @param {Bounds} newBounds + * + * @return {Array} + */ + function computeLanesResize(shape, newBounds) { + + var rootElement = getLanesRoot(shape); + + var initialShapes = is$1(rootElement, 'bpmn:Process') ? [] : [ rootElement ]; + + var allLanes = collectLanes(rootElement, initialShapes), + shapeTrbl = asTRBL(shape), + shapeNewTrbl = asTRBL(newBounds), + trblResize = getTRBLResize(shape, newBounds), + resizeNeeded = []; + + allLanes.forEach(function(other) { + + if (other === shape) { + return; + } + + var topResize = 0, + rightResize = trblResize.right, + bottomResize = 0, + leftResize = trblResize.left; + + var otherTrbl = asTRBL(other); + + if (trblResize.top) { + if (abs$4(otherTrbl.bottom - shapeTrbl.top) < 10) { + bottomResize = shapeNewTrbl.top - otherTrbl.bottom; + } + + if (abs$4(otherTrbl.top - shapeTrbl.top) < 5) { + topResize = shapeNewTrbl.top - otherTrbl.top; + } + } + + if (trblResize.bottom) { + if (abs$4(otherTrbl.top - shapeTrbl.bottom) < 10) { + topResize = shapeNewTrbl.bottom - otherTrbl.top; + } + + if (abs$4(otherTrbl.bottom - shapeTrbl.bottom) < 5) { + bottomResize = shapeNewTrbl.bottom - otherTrbl.bottom; + } + } + + if (topResize || rightResize || bottomResize || leftResize) { + + resizeNeeded.push({ + shape: other, + newBounds: resizeTRBL(other, { + top: topResize, + right: rightResize, + bottom: bottomResize, + left: leftResize + }) + }); + } + + }); + + return resizeNeeded; + } + + /** + * A provider for BPMN 2.0 elements context pad + */ + function ContextPadProvider( + config, injector, eventBus, + contextPad, modeling, elementFactory, + connect, create, popupMenu, + canvas, rules, translate) { + + config = config || {}; + + contextPad.registerProvider(this); + + this._contextPad = contextPad; + + this._modeling = modeling; + + this._elementFactory = elementFactory; + this._connect = connect; + this._create = create; + this._popupMenu = popupMenu; + this._canvas = canvas; + this._rules = rules; + this._translate = translate; + + if (config.autoPlace !== false) { + this._autoPlace = injector.get('autoPlace', false); + } + + eventBus.on('create.end', 250, function(event) { + var context = event.context, + shape = context.shape; + + if (!hasPrimaryModifier(event) || !contextPad.isOpen(shape)) { + return; + } + + var entries = contextPad.getEntries(shape); + + if (entries.replace) { + entries.replace.action.click(event, shape); + } + }); + } + + ContextPadProvider.$inject = [ + 'config.contextPad', + 'injector', + 'eventBus', + 'contextPad', + 'modeling', + 'elementFactory', + 'connect', + 'create', + 'popupMenu', + 'canvas', + 'rules', + 'translate' + ]; + + ContextPadProvider.prototype.getMultiElementContextPadEntries = function(elements) { + var modeling = this._modeling; + + var actions = {}; + + if (this._isDeleteAllowed(elements)) { + assign(actions, { + 'delete': { + group: 'edit', + className: 'bpmn-icon-trash', + title: this._translate('Remove'), + action: { + click: function(event, elements) { + modeling.removeElements(elements.slice()); + } + } + } + }); + } + + return actions; + }; + + /** + * @param {djs.model.Base[]} elements + * @return {boolean} + */ + ContextPadProvider.prototype._isDeleteAllowed = function(elements) { + + var baseAllowed = this._rules.allowed('elements.delete', { + elements: elements + }); + + if (isArray$3(baseAllowed)) { + return every(baseAllowed, function(element) { + return includes$7(baseAllowed, element); + }); + } + + return baseAllowed; + }; + + ContextPadProvider.prototype.getContextPadEntries = function(element) { + var contextPad = this._contextPad, + modeling = this._modeling, + + elementFactory = this._elementFactory, + connect = this._connect, + create = this._create, + popupMenu = this._popupMenu, + canvas = this._canvas, + rules = this._rules, + autoPlace = this._autoPlace, + translate = this._translate; + + var actions = {}; + + if (element.type === 'label') { + return actions; + } + + var businessObject = element.businessObject; + + function startConnect(event, element) { + connect.start(event, element); + } + + function removeElement(e, element) { + modeling.removeElements([ element ]); + } + + function getReplaceMenuPosition(element) { + + var Y_OFFSET = 5; + + var diagramContainer = canvas.getContainer(), + pad = contextPad.getPad(element).html; + + var diagramRect = diagramContainer.getBoundingClientRect(), + padRect = pad.getBoundingClientRect(); + + var top = padRect.top - diagramRect.top; + var left = padRect.left - diagramRect.left; + + var pos = { + x: left, + y: top + padRect.height + Y_OFFSET + }; + + return pos; + } + + + /** + * Create an append action + * + * @param {string} type + * @param {string} className + * @param {string} [title] + * @param {Object} [options] + * + * @return {Object} descriptor + */ + function appendAction(type, className, title, options) { + + if (typeof title !== 'string') { + options = title; + title = translate('Append {type}', { type: type.replace(/^bpmn:/, '') }); + } + + function appendStart(event, element) { + + var shape = elementFactory.createShape(assign({ type: type }, options)); + create.start(event, shape, { + source: element + }); + } + + + var append = autoPlace ? function(event, element) { + var shape = elementFactory.createShape(assign({ type: type }, options)); + + autoPlace.append(element, shape); + } : appendStart; + + + return { + group: 'model', + className: className, + title: title, + action: { + dragstart: appendStart, + click: append + } + }; + } + + function splitLaneHandler(count) { + + return function(event, element) { + + // actual split + modeling.splitLane(element, count); + + // refresh context pad after split to + // get rid of split icons + contextPad.open(element, true); + }; + } + + + if (isAny(businessObject, [ 'bpmn:Lane', 'bpmn:Participant' ]) && isExpanded(element)) { + + var childLanes = getChildLanes(element); + + assign(actions, { + 'lane-insert-above': { + group: 'lane-insert-above', + className: 'bpmn-icon-lane-insert-above', + title: translate('Add Lane above'), + action: { + click: function(event, element) { + modeling.addLane(element, 'top'); + } + } + } + }); + + if (childLanes.length < 2) { + + if (element.height >= 120) { + assign(actions, { + 'lane-divide-two': { + group: 'lane-divide', + className: 'bpmn-icon-lane-divide-two', + title: translate('Divide into two Lanes'), + action: { + click: splitLaneHandler(2) + } + } + }); + } + + if (element.height >= 180) { + assign(actions, { + 'lane-divide-three': { + group: 'lane-divide', + className: 'bpmn-icon-lane-divide-three', + title: translate('Divide into three Lanes'), + action: { + click: splitLaneHandler(3) + } + } + }); + } + } + + assign(actions, { + 'lane-insert-below': { + group: 'lane-insert-below', + className: 'bpmn-icon-lane-insert-below', + title: translate('Add Lane below'), + action: { + click: function(event, element) { + modeling.addLane(element, 'bottom'); + } + } + } + }); + + } + + if (is$1(businessObject, 'bpmn:FlowNode')) { + + if (is$1(businessObject, 'bpmn:EventBasedGateway')) { + + assign(actions, { + 'append.receive-task': appendAction( + 'bpmn:ReceiveTask', + 'bpmn-icon-receive-task', + translate('Append ReceiveTask') + ), + 'append.message-intermediate-event': appendAction( + 'bpmn:IntermediateCatchEvent', + 'bpmn-icon-intermediate-event-catch-message', + translate('Append MessageIntermediateCatchEvent'), + { eventDefinitionType: 'bpmn:MessageEventDefinition' } + ), + 'append.timer-intermediate-event': appendAction( + 'bpmn:IntermediateCatchEvent', + 'bpmn-icon-intermediate-event-catch-timer', + translate('Append TimerIntermediateCatchEvent'), + { eventDefinitionType: 'bpmn:TimerEventDefinition' } + ), + 'append.condition-intermediate-event': appendAction( + 'bpmn:IntermediateCatchEvent', + 'bpmn-icon-intermediate-event-catch-condition', + translate('Append ConditionIntermediateCatchEvent'), + { eventDefinitionType: 'bpmn:ConditionalEventDefinition' } + ), + 'append.signal-intermediate-event': appendAction( + 'bpmn:IntermediateCatchEvent', + 'bpmn-icon-intermediate-event-catch-signal', + translate('Append SignalIntermediateCatchEvent'), + { eventDefinitionType: 'bpmn:SignalEventDefinition' } + ) + }); + } else + + if (isEventType(businessObject, 'bpmn:BoundaryEvent', 'bpmn:CompensateEventDefinition')) { + + assign(actions, { + 'append.compensation-activity': + appendAction( + 'bpmn:Task', + 'bpmn-icon-task', + translate('Append compensation activity'), + { + isForCompensation: true + } + ) + }); + } else + + if (!is$1(businessObject, 'bpmn:EndEvent') && + !businessObject.isForCompensation && + !isEventType(businessObject, 'bpmn:IntermediateThrowEvent', 'bpmn:LinkEventDefinition') && + !isEventSubProcess(businessObject)) { + + assign(actions, { + 'append.end-event': appendAction( + 'bpmn:EndEvent', + 'bpmn-icon-end-event-none', + translate('Append EndEvent') + ), + 'append.gateway': appendAction( + 'bpmn:ExclusiveGateway', + 'bpmn-icon-gateway-none', + translate('Append Gateway') + ), + 'append.append-task': appendAction( + 'bpmn:Task', + 'bpmn-icon-task', + translate('Append Task') + ), + 'append.intermediate-event': appendAction( + 'bpmn:IntermediateThrowEvent', + 'bpmn-icon-intermediate-event-none', + translate('Append Intermediate/Boundary Event') + ) + }); + } + } + + if (!popupMenu.isEmpty(element, 'bpmn-replace')) { + + // Replace menu entry + assign(actions, { + 'replace': { + group: 'edit', + className: 'bpmn-icon-screw-wrench', + title: translate('Change type'), + action: { + click: function(event, element) { + + var position = assign(getReplaceMenuPosition(element), { + cursor: { x: event.x, y: event.y } + }); + + popupMenu.open(element, 'bpmn-replace', position); + } + } + } + }); + } + + if (is$1(businessObject, 'bpmn:SequenceFlow')) { + assign(actions, { + 'append.text-annotation': appendAction( + 'bpmn:TextAnnotation', + 'bpmn-icon-text-annotation' + ) + }); + } + + if ( + isAny(businessObject, [ + 'bpmn:FlowNode', + 'bpmn:InteractionNode', + 'bpmn:DataObjectReference', + 'bpmn:DataStoreReference', + ]) + ) { + assign(actions, { + 'append.text-annotation': appendAction( + 'bpmn:TextAnnotation', + 'bpmn-icon-text-annotation' + ), + + 'connect': { + group: 'connect', + className: 'bpmn-icon-connection-multi', + title: translate( + 'Connect using ' + + (businessObject.isForCompensation + ? '' + : 'Sequence/MessageFlow or ') + + 'Association' + ), + action: { + click: startConnect, + dragstart: startConnect, + }, + }, + }); + } + + if (is$1(businessObject, 'bpmn:TextAnnotation')) { + assign(actions, { + 'connect': { + group: 'connect', + className: 'bpmn-icon-connection-multi', + title: translate('Connect using Association'), + action: { + click: startConnect, + dragstart: startConnect, + }, + }, + }); + } + + if (isAny(businessObject, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ])) { + assign(actions, { + 'connect': { + group: 'connect', + className: 'bpmn-icon-connection-multi', + title: translate('Connect using DataInputAssociation'), + action: { + click: startConnect, + dragstart: startConnect + } + } + }); + } + + if (is$1(businessObject, 'bpmn:Group')) { + assign(actions, { + 'append.text-annotation': appendAction('bpmn:TextAnnotation', 'bpmn-icon-text-annotation') + }); + } + + // delete element entry, only show if allowed by rules + var deleteAllowed = rules.allowed('elements.delete', { elements: [ element ] }); + + if (isArray$3(deleteAllowed)) { + + // was the element returned as a deletion candidate? + deleteAllowed = deleteAllowed[0] === element; + } + + if (deleteAllowed) { + assign(actions, { + 'delete': { + group: 'edit', + className: 'bpmn-icon-trash', + title: translate('Remove'), + action: { + click: removeElement + } + } + }); + } + + return actions; + }; + + + // helpers ///////// + + function isEventType(eventBo, type, definition) { + + var isType = eventBo.$instanceOf(type); + var isDefinition = false; + + var definitions = eventBo.eventDefinitions || []; + forEach$1(definitions, function(def) { + if (def.$type === definition) { + isDefinition = true; + } + }); + + return isType && isDefinition; + } + + function includes$7(array, item) { + return array.indexOf(item) !== -1; + } + + var ContextPadModule = { + __depends__: [ + DirectEditingModule, + ContextPadModule$1, + SelectionModule, + ConnectModule, + CreateModule, + PopupMenuModule + ], + __init__: [ 'contextPadProvider' ], + contextPadProvider: [ 'type', ContextPadProvider ] + }; + + var AXIS_DIMENSIONS = { + horizontal: [ 'x', 'width' ], + vertical: [ 'y', 'height' ] + }; + + var THRESHOLD = 5; + + + /** + * Groups and filters elements and then trigger even distribution. + */ + function DistributeElements$1(modeling, rules) { + this._modeling = modeling; + + this._filters = []; + + this.registerFilter(function(elements) { + var allowed = rules.allowed('elements.distribute', { elements: elements }); + + if (isArray$3(allowed)) { + return allowed; + } + + return allowed ? elements : []; + }); + } + + DistributeElements$1.$inject = [ 'modeling', 'rules' ]; + + + /** + * Registers filter functions that allow external parties to filter + * out certain elements. + * + * @param {Function} filterFn + */ + DistributeElements$1.prototype.registerFilter = function(filterFn) { + if (typeof filterFn !== 'function') { + throw new Error('the filter has to be a function'); + } + + this._filters.push(filterFn); + }; + + /** + * Distributes the elements with a given orientation + * + * @param {Array} elements + * @param {string} orientation + */ + DistributeElements$1.prototype.trigger = function(elements, orientation) { + var modeling = this._modeling; + + var groups, + distributableElements; + + if (elements.length < 3) { + return; + } + + this._setOrientation(orientation); + + distributableElements = this._filterElements(elements); + + groups = this._createGroups(distributableElements); + + // nothing to distribute + if (groups.length <= 2) { + return; + } + + modeling.distributeElements(groups, this._axis, this._dimension); + + return groups; + }; + + /** + * Filters the elements with provided filters by external parties + * + * @param {Array[Elements]} elements + * + * @return {Array[Elements]} + */ + DistributeElements$1.prototype._filterElements = function(elements) { + var filters = this._filters, + axis = this._axis, + dimension = this._dimension, + distributableElements = [].concat(elements); + + if (!filters.length) { + return elements; + } + + forEach$1(filters, function(filterFn) { + distributableElements = filterFn(distributableElements, axis, dimension); + }); + + return distributableElements; + }; + + + /** + * Create range (min, max) groups. Also tries to group elements + * together that share the same range. + * + * @example + * var distributableElements = [ + * { + * range: { + * min: 100, + * max: 200 + * }, + * elements: [ { id: 'shape1', .. }] + * } + * ] + * + * @param {Array} elements + * + * @return {Array[Objects]} + */ + DistributeElements$1.prototype._createGroups = function(elements) { + var rangeGroups = [], + self = this, + axis = this._axis, + dimension = this._dimension; + + if (!axis) { + throw new Error('must have a defined "axis" and "dimension"'); + } + + // sort by 'left->right' or 'top->bottom' + var sortedElements = sortBy(elements, axis); + + forEach$1(sortedElements, function(element, idx) { + var elementRange = self._findRange(element, axis, dimension), + range; + + var previous = rangeGroups[rangeGroups.length - 1]; + + if (previous && self._hasIntersection(previous.range, elementRange)) { + rangeGroups[rangeGroups.length - 1].elements.push(element); + } else { + range = { range: elementRange, elements: [ element ] }; + + rangeGroups.push(range); + } + }); + + return rangeGroups; + }; + + + /** + * Maps a direction to the according axis and dimension + * + * @param {string} direction 'horizontal' or 'vertical' + */ + DistributeElements$1.prototype._setOrientation = function(direction) { + var orientation = AXIS_DIMENSIONS[direction]; + + this._axis = orientation[0]; + this._dimension = orientation[1]; + }; + + + /** + * Checks if the two ranges intercept each other + * + * @param {Object} rangeA {min, max} + * @param {Object} rangeB {min, max} + * + * @return {boolean} + */ + DistributeElements$1.prototype._hasIntersection = function(rangeA, rangeB) { + return Math.max(rangeA.min, rangeA.max) >= Math.min(rangeB.min, rangeB.max) && + Math.min(rangeA.min, rangeA.max) <= Math.max(rangeB.min, rangeB.max); + }; + + + /** + * Returns the min and max values for an element + * + * @param {Bounds} element + * @param {string} axis + * @param {string} dimension + * + * @return {{ min: number, max: number }} + */ + DistributeElements$1.prototype._findRange = function(element) { + var axis = element[this._axis], + dimension = element[this._dimension]; + + return { + min: axis + THRESHOLD, + max: axis + dimension - THRESHOLD + }; + }; + + var DistributeElementsModule$1 = { + __init__: [ 'distributeElements' ], + distributeElements: [ 'type', DistributeElements$1 ] + }; + + /** + * Registers element exclude filters for elements that + * currently do not support distribution. + */ + function BpmnDistributeElements(distributeElements, eventBus, rules) { + RuleProvider.call(this, eventBus); + } + + BpmnDistributeElements.$inject = [ 'distributeElements', 'eventBus', 'rules' ]; + + e(BpmnDistributeElements, RuleProvider); + + BpmnDistributeElements.prototype.init = function() { + this.addRule('elements.distribute', function(context) { + var elements = context.elements; + + elements = filter(elements, function(element) { + var cannotDistribute = isAny(element, [ + 'bpmn:Association', + 'bpmn:BoundaryEvent', + 'bpmn:DataInputAssociation', + 'bpmn:DataOutputAssociation', + 'bpmn:Lane', + 'bpmn:MessageFlow', + 'bpmn:SequenceFlow', + 'bpmn:TextAnnotation' + ]); + + return !(element.labelTarget || cannotDistribute); + }); + + // filter out elements which are children of any of the selected elements + elements = getParents$1(elements); + + if (elements.length < 3) { + return false; + } + + return elements; + }); + }; + + /** + * To change the icons, modify the SVGs in `./resources`, execute `npx svgo -f resources --datauri enc -o dist`, + * and then replace respective icons with the optimized data URIs in `./dist`. + */ + var icons = { + horizontal: 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linejoin%3Around%22%20d%3D%22M450%20400V150h900v250%22%2F%3E%3Crect%20x%3D%22150%22%20y%3D%22450%22%20width%3D%22600%22%20height%3D%221200%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%221050%22%20y%3D%22450%22%20width%3D%22600%22%20height%3D%22800%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E', + vertical: 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linejoin%3Around%22%20d%3D%22M400%201350H150V450h250%22%2F%3E%3Crect%20x%3D%22450%22%20y%3D%22150%22%20width%3D%221200%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%22450%22%20y%3D%221050%22%20width%3D%22800%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E', + }; + + var LOW_PRIORITY$g = 900; + + /** + * A provider for distribute elements popup menu. + */ + function DistributeElementsMenuProvider( + popupMenu, distributeElements, translate, rules) { + this._distributeElements = distributeElements; + this._translate = translate; + this._popupMenu = popupMenu; + this._rules = rules; + + popupMenu.registerProvider('align-elements', LOW_PRIORITY$g, this); + } + + DistributeElementsMenuProvider.$inject = [ + 'popupMenu', + 'distributeElements', + 'translate', + 'rules' + ]; + + DistributeElementsMenuProvider.prototype.getPopupMenuEntries = function(elements) { + var entries = {}; + + if (this._isAllowed(elements)) { + assign(entries, this._getEntries(elements)); + } + + return entries; + }; + + DistributeElementsMenuProvider.prototype._isAllowed = function(elements) { + return this._rules.allowed('elements.distribute', { elements: elements }); + }; + + DistributeElementsMenuProvider.prototype._getEntries = function(elements) { + var distributeElements = this._distributeElements, + translate = this._translate, + popupMenu = this._popupMenu; + + var entries = { + 'distribute-elements-horizontal': { + group: 'distribute', + title: translate('Distribute elements horizontally'), + className: 'bjs-align-elements-menu-entry', + imageUrl: icons['horizontal'], + action: function(event, entry) { + distributeElements.trigger(elements, 'horizontal'); + popupMenu.close(); + } + }, + 'distribute-elements-vertical': { + group: 'distribute', + title: translate('Distribute elements vertically'), + imageUrl: icons['vertical'], + action: function(event, entry) { + distributeElements.trigger(elements, 'vertical'); + popupMenu.close(); + } + }, + }; + + return entries; + }; + + var DistributeElementsModule = { + __depends__: [ + PopupMenuModule$1, + DistributeElementsModule$1 + ], + __init__: [ + 'bpmnDistributeElements', + 'distributeElementsMenuProvider' + ], + bpmnDistributeElements: [ 'type', BpmnDistributeElements ], + distributeElementsMenuProvider: [ 'type', DistributeElementsMenuProvider ] + }; + + var NOT_REGISTERED_ERROR = 'is not a registered action', + IS_REGISTERED_ERROR = 'is already registered'; + + + /** + * An interface that provides access to modeling actions by decoupling + * the one who requests the action to be triggered and the trigger itself. + * + * It's possible to add new actions by registering them with ´registerAction´ + * and likewise unregister existing ones with ´unregisterAction´. + * + * + * ## Life-Cycle and configuration + * + * The editor actions will wait for diagram initialization before + * registering default actions _and_ firing an `editorActions.init` event. + * + * Interested parties may listen to the `editorActions.init` event with + * low priority to check, which actions got registered. Other components + * may use the event to register their own actions via `registerAction`. + * + * @param {EventBus} eventBus + * @param {Injector} injector + */ + function EditorActions(eventBus, injector) { + + // initialize actions + this._actions = {}; + + var self = this; + + eventBus.on('diagram.init', function() { + + // all diagram modules got loaded; check which ones + // are available and register the respective default actions + self._registerDefaultActions(injector); + + // ask interested parties to register available editor + // actions on diagram initialization + eventBus.fire('editorActions.init', { + editorActions: self + }); + }); + + } + + EditorActions.$inject = [ + 'eventBus', + 'injector' + ]; + + /** + * Register default actions. + * + * @param {Injector} injector + */ + EditorActions.prototype._registerDefaultActions = function(injector) { + + // (1) retrieve optional components to integrate with + + var commandStack = injector.get('commandStack', false); + var modeling = injector.get('modeling', false); + var selection = injector.get('selection', false); + var zoomScroll = injector.get('zoomScroll', false); + var copyPaste = injector.get('copyPaste', false); + var canvas = injector.get('canvas', false); + var rules = injector.get('rules', false); + var keyboardMove = injector.get('keyboardMove', false); + var keyboardMoveSelection = injector.get('keyboardMoveSelection', false); + + // (2) check components and register actions + + if (commandStack) { + this.register('undo', function() { + commandStack.undo(); + }); + + this.register('redo', function() { + commandStack.redo(); + }); + } + + if (copyPaste && selection) { + this.register('copy', function() { + var selectedElements = selection.get(); + + if (selectedElements.length) { + return copyPaste.copy(selectedElements); + } + }); + } + + if (copyPaste) { + this.register('paste', function() { + copyPaste.paste(); + }); + } + + if (zoomScroll) { + this.register('stepZoom', function(opts) { + zoomScroll.stepZoom(opts.value); + }); + } + + if (canvas) { + this.register('zoom', function(opts) { + canvas.zoom(opts.value); + }); + } + + if (modeling && selection && rules) { + this.register('removeSelection', function() { + + var selectedElements = selection.get(); + + if (!selectedElements.length) { + return; + } + + var allowed = rules.allowed('elements.delete', { elements: selectedElements }), + removableElements; + + if (allowed === false) { + return; + } + else if (isArray$3(allowed)) { + removableElements = allowed; + } + else { + removableElements = selectedElements; + } + + if (removableElements.length) { + modeling.removeElements(removableElements.slice()); + } + }); + } + + if (keyboardMove) { + this.register('moveCanvas', function(opts) { + keyboardMove.moveCanvas(opts); + }); + } + + if (keyboardMoveSelection) { + this.register('moveSelection', function(opts) { + keyboardMoveSelection.moveSelection(opts.direction, opts.accelerated); + }); + } + + }; + + + /** + * Triggers a registered action + * + * @param {string} action + * @param {Object} opts + * + * @return {Unknown} Returns what the registered listener returns + */ + EditorActions.prototype.trigger = function(action, opts) { + if (!this._actions[action]) { + throw error(action, NOT_REGISTERED_ERROR); + } + + return this._actions[action](opts); + }; + + + /** + * Registers a collections of actions. + * The key of the object will be the name of the action. + * + * @example + * ´´´ + * var actions = { + * spaceTool: function() { + * spaceTool.activateSelection(); + * }, + * lassoTool: function() { + * lassoTool.activateSelection(); + * } + * ]; + * + * editorActions.register(actions); + * + * editorActions.isRegistered('spaceTool'); // true + * ´´´ + * + * @param {Object} actions + */ + EditorActions.prototype.register = function(actions, listener) { + var self = this; + + if (typeof actions === 'string') { + return this._registerAction(actions, listener); + } + + forEach$1(actions, function(listener, action) { + self._registerAction(action, listener); + }); + }; + + /** + * Registers a listener to an action key + * + * @param {string} action + * @param {Function} listener + */ + EditorActions.prototype._registerAction = function(action, listener) { + if (this.isRegistered(action)) { + throw error(action, IS_REGISTERED_ERROR); + } + + this._actions[action] = listener; + }; + + /** + * Unregister an existing action + * + * @param {string} action + */ + EditorActions.prototype.unregister = function(action) { + if (!this.isRegistered(action)) { + throw error(action, NOT_REGISTERED_ERROR); + } + + this._actions[action] = undefined; + }; + + /** + * Returns the number of actions that are currently registered + * + * @return {number} + */ + EditorActions.prototype.getActions = function() { + return Object.keys(this._actions); + }; + + /** + * Checks wether the given action is registered + * + * @param {string} action + * + * @return {boolean} + */ + EditorActions.prototype.isRegistered = function(action) { + return !!this._actions[action]; + }; + + + function error(action, message) { + return new Error(action + ' ' + message); + } + + var EditorActionsModule$1 = { + __init__: [ 'editorActions' ], + editorActions: [ 'type', EditorActions ] + }; + + /** + * Registers and executes BPMN specific editor actions. + * + * @param {Injector} injector + */ + function BpmnEditorActions(injector) { + injector.invoke(EditorActions, this); + } + + e(BpmnEditorActions, EditorActions); + + BpmnEditorActions.$inject = [ + 'injector' + ]; + + /** + * Register default actions. + * + * @param {Injector} injector + */ + BpmnEditorActions.prototype._registerDefaultActions = function(injector) { + + // (0) invoke super method + + EditorActions.prototype._registerDefaultActions.call(this, injector); + + // (1) retrieve optional components to integrate with + + var canvas = injector.get('canvas', false); + var elementRegistry = injector.get('elementRegistry', false); + var selection = injector.get('selection', false); + var spaceTool = injector.get('spaceTool', false); + var lassoTool = injector.get('lassoTool', false); + var handTool = injector.get('handTool', false); + var globalConnect = injector.get('globalConnect', false); + var distributeElements = injector.get('distributeElements', false); + var alignElements = injector.get('alignElements', false); + var directEditing = injector.get('directEditing', false); + var searchPad = injector.get('searchPad', false); + var modeling = injector.get('modeling', false); + + // (2) check components and register actions + + if (canvas && elementRegistry && selection) { + this._registerAction('selectElements', function() { + + // select all elements except for the invisible + // root element + var rootElement = canvas.getRootElement(); + + var elements = elementRegistry.filter(function(element) { + return element !== rootElement; + }); + + selection.select(elements); + + return elements; + }); + } + + if (spaceTool) { + this._registerAction('spaceTool', function() { + spaceTool.toggle(); + }); + } + + if (lassoTool) { + this._registerAction('lassoTool', function() { + lassoTool.toggle(); + }); + } + + if (handTool) { + this._registerAction('handTool', function() { + handTool.toggle(); + }); + } + + if (globalConnect) { + this._registerAction('globalConnectTool', function() { + globalConnect.toggle(); + }); + } + + if (selection && distributeElements) { + this._registerAction('distributeElements', function(opts) { + var currentSelection = selection.get(), + type = opts.type; + + if (currentSelection.length) { + distributeElements.trigger(currentSelection, type); + } + }); + } + + if (selection && alignElements) { + this._registerAction('alignElements', function(opts) { + var currentSelection = selection.get(), + aligneableElements = [], + type = opts.type; + + if (currentSelection.length) { + aligneableElements = filter(currentSelection, function(element) { + return !is$1(element, 'bpmn:Lane'); + }); + + alignElements.trigger(aligneableElements, type); + } + }); + } + + if (selection && modeling) { + this._registerAction('setColor', function(opts) { + var currentSelection = selection.get(); + + if (currentSelection.length) { + modeling.setColor(currentSelection, opts); + } + }); + } + + if (selection && directEditing) { + this._registerAction('directEditing', function() { + var currentSelection = selection.get(); + + if (currentSelection.length) { + directEditing.activate(currentSelection[0]); + } + }); + } + + if (searchPad) { + this._registerAction('find', function() { + searchPad.toggle(); + }); + } + + if (canvas && modeling) { + this._registerAction('moveToOrigin', function() { + var rootElement = canvas.getRootElement(), + boundingBox, + elements; + + if (is$1(rootElement, 'bpmn:Collaboration')) { + elements = elementRegistry.filter(function(element) { + return is$1(element.parent, 'bpmn:Collaboration'); + }); + } else { + elements = elementRegistry.filter(function(element) { + return element !== rootElement && !is$1(element.parent, 'bpmn:SubProcess'); + }); + } + + boundingBox = getBBox(elements); + + modeling.moveElements( + elements, + { x: -boundingBox.x, y: -boundingBox.y }, + rootElement + ); + }); + } + + }; + + var EditorActionsModule = { + __depends__: [ + EditorActionsModule$1 + ], + editorActions: [ 'type', BpmnEditorActions ] + }; + + function BpmnGridSnapping(eventBus) { + eventBus.on([ + 'create.init', + 'shape.move.init' + ], function(event) { + var context = event.context, + shape = event.shape; + + if (isAny(shape, [ + 'bpmn:Participant', + 'bpmn:SubProcess', + 'bpmn:TextAnnotation' + ])) { + if (!context.gridSnappingContext) { + context.gridSnappingContext = {}; + } + + context.gridSnappingContext.snapLocation = 'top-left'; + } + }); + } + + BpmnGridSnapping.$inject = [ 'eventBus' ]; + + var SPACING = 10; + + function quantize(value, quantum, fn) { + if (!fn) { + fn = 'round'; + } + + return Math[ fn ](value / quantum) * quantum; + } + + var LOWER_PRIORITY$1 = 1200; + var LOW_PRIORITY$f = 800; + + /** + * Basic grid snapping that covers connecting, creating, moving, resizing shapes, moving bendpoints + * and connection segments. + */ + function GridSnapping(elementRegistry, eventBus, config) { + + var active = !config || config.active !== false; + + this._eventBus = eventBus; + + var self = this; + + eventBus.on('diagram.init', LOW_PRIORITY$f, function() { + self.setActive(active); + }); + + eventBus.on([ + 'create.move', + 'create.end', + 'bendpoint.move.move', + 'bendpoint.move.end', + 'connect.move', + 'connect.end', + 'connectionSegment.move.move', + 'connectionSegment.move.end', + 'resize.move', + 'resize.end', + 'shape.move.move', + 'shape.move.end' + ], LOWER_PRIORITY$1, function(event) { + var originalEvent = event.originalEvent; + + if (!self.active || (originalEvent && isCmd(originalEvent))) { + return; + } + + var context = event.context, + gridSnappingContext = context.gridSnappingContext; + + if (!gridSnappingContext) { + gridSnappingContext = context.gridSnappingContext = {}; + } + + [ 'x', 'y' ].forEach(function(axis) { + var options = {}; + + // allow snapping with offset + var snapOffset = getSnapOffset(event, axis, elementRegistry); + + if (snapOffset) { + options.offset = snapOffset; + } + + // allow snapping with min and max + var snapConstraints = getSnapConstraints(event, axis); + + if (snapConstraints) { + assign(options, snapConstraints); + } + + if (!isSnapped(event, axis)) { + self.snapEvent(event, axis, options); + } + }); + }); + } + + /** + * Snap an events x or y with optional min, max and offset. + * + * @param {Object} event + * @param {string} axis + * @param {number} [options.min] + * @param {number} [options.max] + * @param {number} [options.offset] + */ + GridSnapping.prototype.snapEvent = function(event, axis, options) { + var snappedValue = this.snapValue(event[ axis ], options); + + setSnapped(event, axis, snappedValue); + }; + + /** + * Expose grid spacing for third parties (i.e. extensions). + * + * @return {number} spacing of grid dots + */ + GridSnapping.prototype.getGridSpacing = function() { + return SPACING; + }; + + /** + * Snap value with optional min, max and offset. + * + * @param {number} value + * @param {Object} options + * @param {number} [options.min] + * @param {number} [options.max] + * @param {number} [options.offset] + */ + GridSnapping.prototype.snapValue = function(value, options) { + var offset = 0; + + if (options && options.offset) { + offset = options.offset; + } + + value += offset; + + value = quantize(value, SPACING); + + var min, max; + + if (options && options.min) { + min = options.min; + + if (isNumber(min)) { + min = quantize(min + offset, SPACING, 'ceil'); + + value = Math.max(value, min); + } + } + + if (options && options.max) { + max = options.max; + + if (isNumber(max)) { + max = quantize(max + offset, SPACING, 'floor'); + + value = Math.min(value, max); + } + } + + value -= offset; + + return value; + }; + + GridSnapping.prototype.isActive = function() { + return this.active; + }; + + GridSnapping.prototype.setActive = function(active) { + this.active = active; + + this._eventBus.fire('gridSnapping.toggle', { active: active }); + }; + + GridSnapping.prototype.toggleActive = function() { + this.setActive(!this.active); + }; + + GridSnapping.$inject = [ + 'elementRegistry', + 'eventBus', + 'config.gridSnapping' + ]; + + // helpers ////////// + + /** + * Get minimum and maximum snap constraints. + * Constraints are cached. + * + * @param {Object} event + * @param {Object} event.context + * @param {string} axis + * + * @returns {boolean|Object} + */ + function getSnapConstraints(event, axis) { + var context = event.context, + createConstraints = context.createConstraints, + resizeConstraints = context.resizeConstraints || {}, + gridSnappingContext = context.gridSnappingContext, + snapConstraints = gridSnappingContext.snapConstraints; + + // cache snap constraints + if (snapConstraints && snapConstraints[ axis ]) { + return snapConstraints[ axis ]; + } + + if (!snapConstraints) { + snapConstraints = gridSnappingContext.snapConstraints = {}; + } + + if (!snapConstraints[ axis ]) { + snapConstraints[ axis ] = {}; + } + + var direction = context.direction; + + // create + if (createConstraints) { + if (isHorizontal$3(axis)) { + snapConstraints.x.min = createConstraints.left; + snapConstraints.x.max = createConstraints.right; + } else { + snapConstraints.y.min = createConstraints.top; + snapConstraints.y.max = createConstraints.bottom; + } + } + + // resize + var minResizeConstraints = resizeConstraints.min, + maxResizeConstraints = resizeConstraints.max; + + if (minResizeConstraints) { + if (isHorizontal$3(axis)) { + + if (isWest(direction)) { + snapConstraints.x.max = minResizeConstraints.left; + } else { + snapConstraints.x.min = minResizeConstraints.right; + } + + } else { + + if (isNorth(direction)) { + snapConstraints.y.max = minResizeConstraints.top; + } else { + snapConstraints.y.min = minResizeConstraints.bottom; + } + + } + } + + if (maxResizeConstraints) { + if (isHorizontal$3(axis)) { + + if (isWest(direction)) { + snapConstraints.x.min = maxResizeConstraints.left; + } else { + snapConstraints.x.max = maxResizeConstraints.right; + } + + } else { + + if (isNorth(direction)) { + snapConstraints.y.min = maxResizeConstraints.top; + } else { + snapConstraints.y.max = maxResizeConstraints.bottom; + } + + } + } + + return snapConstraints[ axis ]; + } + + /** + * Get snap offset. + * Offset is cached. + * + * @param {Object} event + * @param {string} axis + * @param {ElementRegistry} elementRegistry + * + * @returns {number} + */ + function getSnapOffset(event, axis, elementRegistry) { + var context = event.context, + shape = event.shape, + gridSnappingContext = context.gridSnappingContext, + snapLocation = gridSnappingContext.snapLocation, + snapOffset = gridSnappingContext.snapOffset; + + // cache snap offset + if (snapOffset && isNumber(snapOffset[ axis ])) { + return snapOffset[ axis ]; + } + + if (!snapOffset) { + snapOffset = gridSnappingContext.snapOffset = {}; + } + + if (!isNumber(snapOffset[ axis ])) { + snapOffset[ axis ] = 0; + } + + if (!shape) { + return snapOffset[ axis ]; + } + + if (!elementRegistry.get(shape.id)) { + + if (isHorizontal$3(axis)) { + snapOffset[ axis ] += shape[ axis ] + shape.width / 2; + } else { + snapOffset[ axis ] += shape[ axis ] + shape.height / 2; + } + } + + if (!snapLocation) { + return snapOffset[ axis ]; + } + + if (axis === 'x') { + if (/left/.test(snapLocation)) { + snapOffset[ axis ] -= shape.width / 2; + } else if (/right/.test(snapLocation)) { + snapOffset[ axis ] += shape.width / 2; + } + } else { + if (/top/.test(snapLocation)) { + snapOffset[ axis ] -= shape.height / 2; + } else if (/bottom/.test(snapLocation)) { + snapOffset[ axis ] += shape.height / 2; + } + } + + return snapOffset[ axis ]; + } + + function isHorizontal$3(axis) { + return axis === 'x'; + } + + function isNorth(direction) { + return direction.indexOf('n') !== -1; + } + + function isWest(direction) { + return direction.indexOf('w') !== -1; + } + + /** + * Integrates resizing with grid snapping. + */ + function ResizeBehavior$1(eventBus, gridSnapping) { + CommandInterceptor.call(this, eventBus); + + this._gridSnapping = gridSnapping; + + var self = this; + + this.preExecute('shape.resize', function(event) { + var context = event.context, + hints = context.hints || {}, + autoResize = hints.autoResize; + + if (!autoResize) { + return; + } + + var shape = context.shape, + newBounds = context.newBounds; + + if (isString(autoResize)) { + context.newBounds = self.snapComplex(newBounds, autoResize); + } else { + context.newBounds = self.snapSimple(shape, newBounds); + } + }); + } + + ResizeBehavior$1.$inject = [ + 'eventBus', + 'gridSnapping', + 'modeling' + ]; + + e(ResizeBehavior$1, CommandInterceptor); + + /** + * Snap width and height in relation to center. + * + * @param {djs.model.shape} shape + * @param {Bounds} newBounds + * + * @returns {Bounds} Snapped bounds. + */ + ResizeBehavior$1.prototype.snapSimple = function(shape, newBounds) { + var gridSnapping = this._gridSnapping; + + newBounds.width = gridSnapping.snapValue(newBounds.width, { + min: newBounds.width + }); + + newBounds.height = gridSnapping.snapValue(newBounds.height, { + min: newBounds.height + }); + + newBounds.x = shape.x + (shape.width / 2) - (newBounds.width / 2); + newBounds.y = shape.y + (shape.height / 2) - (newBounds.height / 2); + + return newBounds; + }; + + /** + * Snap x, y, width and height according to given directions. + * + * @param {Bounds} newBounds + * @param {string} directions - Directions as {n|w|s|e}. + * + * @returns {Bounds} Snapped bounds. + */ + ResizeBehavior$1.prototype.snapComplex = function(newBounds, directions) { + if (/w|e/.test(directions)) { + newBounds = this.snapHorizontally(newBounds, directions); + } + + if (/n|s/.test(directions)) { + newBounds = this.snapVertically(newBounds, directions); + } + + return newBounds; + }; + + /** + * Snap in one or both directions horizontally. + * + * @param {Bounds} newBounds + * @param {string} directions - Directions as {n|w|s|e}. + * + * @returns {Bounds} Snapped bounds. + */ + ResizeBehavior$1.prototype.snapHorizontally = function(newBounds, directions) { + var gridSnapping = this._gridSnapping, + west = /w/.test(directions), + east = /e/.test(directions); + + var snappedNewBounds = {}; + + snappedNewBounds.width = gridSnapping.snapValue(newBounds.width, { + min: newBounds.width + }); + + if (east) { + + // handle + if (west) { + snappedNewBounds.x = gridSnapping.snapValue(newBounds.x, { + max: newBounds.x + }); + + snappedNewBounds.width += gridSnapping.snapValue(newBounds.x - snappedNewBounds.x, { + min: newBounds.x - snappedNewBounds.x + }); + } + + // handle + else { + newBounds.x = newBounds.x + newBounds.width - snappedNewBounds.width; + } + } + + // assign snapped x and width + assign(newBounds, snappedNewBounds); + + return newBounds; + }; + + /** + * Snap in one or both directions vertically. + * + * @param {Bounds} newBounds + * @param {string} directions - Directions as {n|w|s|e}. + * + * @returns {Bounds} Snapped bounds. + */ + ResizeBehavior$1.prototype.snapVertically = function(newBounds, directions) { + var gridSnapping = this._gridSnapping, + north = /n/.test(directions), + south = /s/.test(directions); + + var snappedNewBounds = {}; + + snappedNewBounds.height = gridSnapping.snapValue(newBounds.height, { + min: newBounds.height + }); + + if (north) { + + // handle + if (south) { + snappedNewBounds.y = gridSnapping.snapValue(newBounds.y, { + max: newBounds.y + }); + + snappedNewBounds.height += gridSnapping.snapValue(newBounds.y - snappedNewBounds.y, { + min: newBounds.y - snappedNewBounds.y + }); + } + + // handle + else { + newBounds.y = newBounds.y + newBounds.height - snappedNewBounds.height; + } + } + + // assign snapped y and height + assign(newBounds, snappedNewBounds); + + return newBounds; + }; + + var HIGH_PRIORITY$f = 2000; + + /** + * Integrates space tool with grid snapping. + */ + function SpaceToolBehavior$1(eventBus, gridSnapping) { + eventBus.on([ + 'spaceTool.move', + 'spaceTool.end' + ], HIGH_PRIORITY$f, function(event) { + var context = event.context; + + if (!context.initialized) { + return; + } + + var axis = context.axis; + + var snapped; + + if (axis === 'x') { + + // snap delta x to multiple of 10 + snapped = gridSnapping.snapValue(event.dx); + + event.x = event.x + snapped - event.dx; + event.dx = snapped; + } else { + + // snap delta y to multiple of 10 + snapped = gridSnapping.snapValue(event.dy); + + event.y = event.y + snapped - event.dy; + event.dy = snapped; + } + }); + } + + SpaceToolBehavior$1.$inject = [ + 'eventBus', + 'gridSnapping' + ]; + + var GridSnappingBehaviorModule$1 = { + __init__: [ + 'gridSnappingResizeBehavior', + 'gridSnappingSpaceToolBehavior' + ], + gridSnappingResizeBehavior: [ 'type', ResizeBehavior$1 ], + gridSnappingSpaceToolBehavior: [ 'type', SpaceToolBehavior$1 ] + }; + + var GridSnappingModule$1 = { + __depends__: [ GridSnappingBehaviorModule$1 ], + __init__: [ 'gridSnapping' ], + gridSnapping: [ 'type', GridSnapping ] + }; + + var HIGH_PRIORITY$e = 2000; + + + function GridSnappingAutoPlaceBehavior(eventBus, gridSnapping) { + eventBus.on('autoPlace', HIGH_PRIORITY$e, function(context) { + var source = context.source, + sourceMid = getMid(source), + shape = context.shape; + + var position = getNewShapePosition(source, shape); + + [ 'x', 'y' ].forEach(function(axis) { + var options = {}; + + // do not snap if x/y equal + if (position[ axis ] === sourceMid[ axis ]) { + return; + } + + if (position[ axis ] > sourceMid[ axis ]) { + options.min = position[ axis ]; + } else { + options.max = position[ axis ]; + } + + if (is$1(shape, 'bpmn:TextAnnotation')) { + + if (isHorizontal$2(axis)) { + options.offset = -shape.width / 2; + } else { + options.offset = -shape.height / 2; + } + + } + + position[ axis ] = gridSnapping.snapValue(position[ axis ], options); + + }); + + // must be returned to be considered by auto place + return position; + }); + } + + GridSnappingAutoPlaceBehavior.$inject = [ + 'eventBus', + 'gridSnapping' + ]; + + // helpers ////////// + + function isHorizontal$2(axis) { + return axis === 'x'; + } + + var HIGHER_PRIORITY$4 = 1750; + + + function GridSnappingParticipantBehavior(canvas, eventBus, gridSnapping) { + eventBus.on([ + 'create.start', + 'shape.move.start' + ], HIGHER_PRIORITY$4, function(event) { + var context = event.context, + shape = context.shape, + rootElement = canvas.getRootElement(); + + if (!is$1(shape, 'bpmn:Participant') || + !is$1(rootElement, 'bpmn:Process') || + !rootElement.children.length) { + return; + } + + var createConstraints = context.createConstraints; + + if (!createConstraints) { + return; + } + + shape.width = gridSnapping.snapValue(shape.width, { min: shape.width }); + shape.height = gridSnapping.snapValue(shape.height, { min: shape.height }); + }); + } + + GridSnappingParticipantBehavior.$inject = [ + 'canvas', + 'eventBus', + 'gridSnapping' + ]; + + var HIGH_PRIORITY$d = 3000; + + + /** + * Snaps connections with Manhattan layout. + */ + function GridSnappingLayoutConnectionBehavior(eventBus, gridSnapping, modeling) { + CommandInterceptor.call(this, eventBus); + + this._gridSnapping = gridSnapping; + + var self = this; + + this.postExecuted([ + 'connection.create', + 'connection.layout' + ], HIGH_PRIORITY$d, function(event) { + var context = event.context, + connection = context.connection, + hints = context.hints || {}, + waypoints = connection.waypoints; + + if (hints.connectionStart || hints.connectionEnd || hints.createElementsBehavior === false) { + return; + } + + if (!hasMiddleSegments(waypoints)) { + return; + } + + modeling.updateWaypoints(connection, self.snapMiddleSegments(waypoints)); + }); + } + + GridSnappingLayoutConnectionBehavior.$inject = [ + 'eventBus', + 'gridSnapping', + 'modeling' + ]; + + e(GridSnappingLayoutConnectionBehavior, CommandInterceptor); + + /** + * Snap middle segments of a given connection. + * + * @param {Array} waypoints + * + * @returns {Array} + */ + GridSnappingLayoutConnectionBehavior.prototype.snapMiddleSegments = function(waypoints) { + var gridSnapping = this._gridSnapping, + snapped; + + waypoints = waypoints.slice(); + + for (var i = 1; i < waypoints.length - 2; i++) { + + snapped = snapSegment(gridSnapping, waypoints[i], waypoints[i + 1]); + + waypoints[i] = snapped[0]; + waypoints[i + 1] = snapped[1]; + } + + return waypoints; + }; + + + // helpers ////////// + + /** + * Check whether a connection has a middle segments. + * + * @param {Array} waypoints + * + * @returns {boolean} + */ + function hasMiddleSegments(waypoints) { + return waypoints.length > 3; + } + + /** + * Check whether an alignment is horizontal. + * + * @param {string} aligned + * + * @returns {boolean} + */ + function horizontallyAligned(aligned) { + return aligned === 'h'; + } + + /** + * Check whether an alignment is vertical. + * + * @param {string} aligned + * + * @returns {boolean} + */ + function verticallyAligned(aligned) { + return aligned === 'v'; + } + + /** + * Get middle segments from a given connection. + * + * @param {Array} waypoints + * + * @returns {Array} + */ + function snapSegment(gridSnapping, segmentStart, segmentEnd) { + + var aligned = pointsAligned(segmentStart, segmentEnd); + + var snapped = {}; + + if (horizontallyAligned(aligned)) { + + // snap horizontally + snapped.y = gridSnapping.snapValue(segmentStart.y); + } + + if (verticallyAligned(aligned)) { + + // snap vertically + snapped.x = gridSnapping.snapValue(segmentStart.x); + } + + if ('x' in snapped || 'y' in snapped) { + segmentStart = assign({}, segmentStart, snapped); + segmentEnd = assign({}, segmentEnd, snapped); + } + + return [ segmentStart, segmentEnd ]; + } + + var GridSnappingBehaviorModule = { + __init__: [ + 'gridSnappingAutoPlaceBehavior', + 'gridSnappingParticipantBehavior', + 'gridSnappingLayoutConnectionBehavior', + ], + gridSnappingAutoPlaceBehavior: [ 'type', GridSnappingAutoPlaceBehavior ], + gridSnappingParticipantBehavior: [ 'type', GridSnappingParticipantBehavior ], + gridSnappingLayoutConnectionBehavior: [ 'type', GridSnappingLayoutConnectionBehavior ] + }; + + var GridSnappingModule = { + __depends__: [ + GridSnappingModule$1, + GridSnappingBehaviorModule + ], + __init__: [ 'bpmnGridSnapping' ], + bpmnGridSnapping: [ 'type', BpmnGridSnapping ] + }; + + var LABEL_WIDTH = 30, + LABEL_HEIGHT = 30; + + + /** + * BPMN-specific hit zones and interaction fixes. + * + * @param {EventBus} eventBus + * @param {InteractionEvents} interactionEvents + */ + function BpmnInteractionEvents(eventBus, interactionEvents) { + + this._interactionEvents = interactionEvents; + + var self = this; + + eventBus.on([ + 'interactionEvents.createHit', + 'interactionEvents.updateHit' + ], function(context) { + var element = context.element, + gfx = context.gfx; + + if (is$1(element, 'bpmn:Lane')) { + return self.createParticipantHit(element, gfx); + } else + + if (is$1(element, 'bpmn:Participant')) { + if (isExpanded(element)) { + return self.createParticipantHit(element, gfx); + } else { + return self.createDefaultHit(element, gfx); + } + } else + + if (is$1(element, 'bpmn:SubProcess')) { + if (isExpanded(element)) { + return self.createSubProcessHit(element, gfx); + } else { + return self.createDefaultHit(element, gfx); + } + } + }); + + } + + BpmnInteractionEvents.$inject = [ + 'eventBus', + 'interactionEvents' + ]; + + + BpmnInteractionEvents.prototype.createDefaultHit = function(element, gfx) { + this._interactionEvents.removeHits(gfx); + + this._interactionEvents.createDefaultHit(element, gfx); + + // indicate that we created a hit + return true; + }; + + BpmnInteractionEvents.prototype.createParticipantHit = function(element, gfx) { + + // remove existing hits + this._interactionEvents.removeHits(gfx); + + // add body hit + this._interactionEvents.createBoxHit(gfx, 'no-move', { + width: element.width, + height: element.height + }); + + // add outline hit + this._interactionEvents.createBoxHit(gfx, 'click-stroke', { + width: element.width, + height: element.height + }); + + // add label hit + this._interactionEvents.createBoxHit(gfx, 'all', { + width: LABEL_WIDTH, + height: element.height + }); + + // indicate that we created a hit + return true; + }; + + BpmnInteractionEvents.prototype.createSubProcessHit = function(element, gfx) { + + // remove existing hits + this._interactionEvents.removeHits(gfx); + + // add body hit + this._interactionEvents.createBoxHit(gfx, 'no-move', { + width: element.width, + height: element.height + }); + + // add outline hit + this._interactionEvents.createBoxHit(gfx, 'click-stroke', { + width: element.width, + height: element.height + }); + + // add label hit + this._interactionEvents.createBoxHit(gfx, 'all', { + width: element.width, + height: LABEL_HEIGHT + }); + + // indicate that we created a hit + return true; + }; + + var InteractionEventsModule = { + __init__: [ 'bpmnInteractionEvents' ], + bpmnInteractionEvents: [ 'type', BpmnInteractionEvents ] + }; + + /** + * BPMN 2.0 specific keyboard bindings. + * + * @param {Injector} injector + */ + function BpmnKeyboardBindings(injector) { + injector.invoke(KeyboardBindings, this); + } + + e(BpmnKeyboardBindings, KeyboardBindings); + + BpmnKeyboardBindings.$inject = [ + 'injector' + ]; + + + /** + * Register available keyboard bindings. + * + * @param {Keyboard} keyboard + * @param {EditorActions} editorActions + */ + BpmnKeyboardBindings.prototype.registerBindings = function(keyboard, editorActions) { + + // inherit default bindings + KeyboardBindings.prototype.registerBindings.call(this, keyboard, editorActions); + + /** + * Add keyboard binding if respective editor action + * is registered. + * + * @param {string} action name + * @param {Function} fn that implements the key binding + */ + function addListener(action, fn) { + + if (editorActions.isRegistered(action)) { + keyboard.addListener(fn); + } + } + + // select all elements + // CTRL + A + addListener('selectElements', function(context) { + + var event = context.keyEvent; + + if (keyboard.isKey([ 'a', 'A' ], event) && keyboard.isCmd(event)) { + editorActions.trigger('selectElements'); + + return true; + } + }); + + // search labels + // CTRL + F + addListener('find', function(context) { + + var event = context.keyEvent; + + if (keyboard.isKey([ 'f', 'F' ], event) && keyboard.isCmd(event)) { + editorActions.trigger('find'); + + return true; + } + }); + + // activate space tool + // S + addListener('spaceTool', function(context) { + + var event = context.keyEvent; + + if (keyboard.hasModifier(event)) { + return; + } + + if (keyboard.isKey([ 's', 'S' ], event)) { + editorActions.trigger('spaceTool'); + + return true; + } + }); + + // activate lasso tool + // L + addListener('lassoTool', function(context) { + + var event = context.keyEvent; + + if (keyboard.hasModifier(event)) { + return; + } + + if (keyboard.isKey([ 'l', 'L' ], event)) { + editorActions.trigger('lassoTool'); + + return true; + } + }); + + // activate hand tool + // H + addListener('handTool', function(context) { + + var event = context.keyEvent; + + if (keyboard.hasModifier(event)) { + return; + } + + if (keyboard.isKey([ 'h', 'H' ], event)) { + editorActions.trigger('handTool'); + + return true; + } + }); + + // activate global connect tool + // C + addListener('globalConnectTool', function(context) { + + var event = context.keyEvent; + + if (keyboard.hasModifier(event)) { + return; + } + + if (keyboard.isKey([ 'c', 'C' ], event)) { + editorActions.trigger('globalConnectTool'); + + return true; + } + }); + + // activate direct editing + // E + addListener('directEditing', function(context) { + + var event = context.keyEvent; + + if (keyboard.hasModifier(event)) { + return; + } + + if (keyboard.isKey([ 'e', 'E' ], event)) { + editorActions.trigger('directEditing'); + + return true; + } + }); + + }; + + var KeyboardModule = { + __depends__: [ + KeyboardModule$1 + ], + __init__: [ 'keyboardBindings' ], + keyboardBindings: [ 'type', BpmnKeyboardBindings ] + }; + + var DEFAULT_CONFIG = { + moveSpeed: 1, + moveSpeedAccelerated: 10 + }; + + var HIGHER_PRIORITY$3 = 1500; + + var LEFT = 'left'; + var UP = 'up'; + var RIGHT = 'right'; + var DOWN = 'down'; + + var KEY_TO_DIRECTION = { + ArrowLeft: LEFT, + Left: LEFT, + ArrowUp: UP, + Up: UP, + ArrowRight: RIGHT, + Right: RIGHT, + ArrowDown: DOWN, + Down: DOWN + }; + + var DIRECTIONS_DELTA = { + left: function(speed) { + return { + x: -speed, + y: 0 + }; + }, + up: function(speed) { + return { + x: 0, + y: -speed + }; + }, + right: function(speed) { + return { + x: speed, + y: 0 + }; + }, + down: function(speed) { + return { + x: 0, + y: speed + }; + } + }; + + + /** + * Enables to move selection with keyboard arrows. + * Use with Shift for modified speed (default=1, with Shift=10). + * Pressed Cmd/Ctrl turns the feature off. + * + * @param {Object} config + * @param {number} [config.moveSpeed=1] + * @param {number} [config.moveSpeedAccelerated=10] + * @param {Keyboard} keyboard + * @param {Modeling} modeling + * @param {Selection} selection + */ + function KeyboardMoveSelection( + config, + keyboard, + modeling, + rules, + selection + ) { + + var self = this; + + this._config = assign({}, DEFAULT_CONFIG, config || {}); + + keyboard.addListener(HIGHER_PRIORITY$3, function(event) { + + var keyEvent = event.keyEvent; + + var direction = KEY_TO_DIRECTION[keyEvent.key]; + + if (!direction) { + return; + } + + if (keyboard.isCmd(keyEvent)) { + return; + } + + var accelerated = keyboard.isShift(keyEvent); + + self.moveSelection(direction, accelerated); + + return true; + }); + + + /** + * Move selected elements in the given direction, + * optionally specifying accelerated movement. + * + * @param {string} direction + * @param {boolean} [accelerated=false] + */ + this.moveSelection = function(direction, accelerated) { + + var selectedElements = selection.get(); + + if (!selectedElements.length) { + return; + } + + var speed = this._config[ + accelerated ? + 'moveSpeedAccelerated' : + 'moveSpeed' + ]; + + var delta = DIRECTIONS_DELTA[direction](speed); + + var canMove = rules.allowed('elements.move', { + shapes: selectedElements + }); + + if (canMove) { + modeling.moveElements(selectedElements, delta); + } + }; + + } + + KeyboardMoveSelection.$inject = [ + 'config.keyboardMoveSelection', + 'keyboard', + 'modeling', + 'rules', + 'selection' + ]; + + var KeyboardMoveSelectionModule = { + __depends__: [ + KeyboardModule$1, + SelectionModule + ], + __init__: [ + 'keyboardMoveSelection' + ], + keyboardMoveSelection: [ 'type', KeyboardMoveSelection ] + }; + + var DEFAULT_MIN_WIDTH = 10; + + + /** + * A component that provides resizing of shapes on the canvas. + * + * The following components are part of shape resize: + * + * * adding resize handles, + * * creating a visual during resize + * * checking resize rules + * * committing a change once finished + * + * + * ## Customizing + * + * It's possible to customize the resizing behaviour by intercepting 'resize.start' + * and providing the following parameters through the 'context': + * + * * minDimensions ({ width, height }): minimum shape dimensions + * + * * childrenBoxPadding ({ left, top, bottom, right } || number): + * gap between the minimum bounding box and the container + * + * f.ex: + * + * ```javascript + * eventBus.on('resize.start', 1500, function(event) { + * var context = event.context, + * + * context.minDimensions = { width: 140, height: 120 }; + * + * // Passing general padding + * context.childrenBoxPadding = 30; + * + * // Passing padding to a specific side + * context.childrenBoxPadding.left = 20; + * }); + * ``` + */ + function Resize(eventBus, rules, modeling, dragging) { + + this._dragging = dragging; + this._rules = rules; + + var self = this; + + + /** + * Handle resize move by specified delta. + * + * @param {Object} context + * @param {Point} delta + */ + function handleMove(context, delta) { + + var shape = context.shape, + direction = context.direction, + resizeConstraints = context.resizeConstraints, + newBounds; + + context.delta = delta; + + newBounds = resizeBounds$1(shape, direction, delta); + + // ensure constraints during resize + context.newBounds = ensureConstraints$1(newBounds, resizeConstraints); + + // update + cache executable state + context.canExecute = self.canResize(context); + } + + /** + * Handle resize start. + * + * @param {Object} context + */ + function handleStart(context) { + + var resizeConstraints = context.resizeConstraints, + + // evaluate minBounds for backwards compatibility + minBounds = context.minBounds; + + if (resizeConstraints !== undefined) { + return; + } + + if (minBounds === undefined) { + minBounds = self.computeMinResizeBox(context); + } + + context.resizeConstraints = { + min: asTRBL(minBounds) + }; + } + + /** + * Handle resize end. + * + * @param {Object} context + */ + function handleEnd(context) { + var shape = context.shape, + canExecute = context.canExecute, + newBounds = context.newBounds; + + if (canExecute) { + + // ensure we have actual pixel values for new bounds + // (important when zoom level was > 1 during move) + newBounds = roundBounds(newBounds); + + if (!boundsChanged(shape, newBounds)) { + + // no resize necessary + return; + } + + // perform the actual resize + modeling.resizeShape(shape, newBounds); + } + } + + + eventBus.on('resize.start', function(event) { + handleStart(event.context); + }); + + eventBus.on('resize.move', function(event) { + var delta = { + x: event.dx, + y: event.dy + }; + + handleMove(event.context, delta); + }); + + eventBus.on('resize.end', function(event) { + handleEnd(event.context); + }); + + } + + + Resize.prototype.canResize = function(context) { + var rules = this._rules; + + var ctx = pick(context, [ 'newBounds', 'shape', 'delta', 'direction' ]); + + return rules.allowed('shape.resize', ctx); + }; + + /** + * Activate a resize operation. + * + * You may specify additional contextual information and must specify a + * resize direction during activation of the resize event. + * + * @param {MouseEvent} event + * @param {djs.model.Shape} shape + * @param {Object|string} contextOrDirection + */ + Resize.prototype.activate = function(event, shape, contextOrDirection) { + var dragging = this._dragging, + context, + direction; + + if (typeof contextOrDirection === 'string') { + contextOrDirection = { + direction: contextOrDirection + }; + } + + context = assign({ shape: shape }, contextOrDirection); + + direction = context.direction; + + if (!direction) { + throw new Error('must provide a direction (n|w|s|e|nw|se|ne|sw)'); + } + + dragging.init(event, getReferencePoint$1(shape, direction), 'resize', { + autoActivate: true, + cursor: getCursor(direction), + data: { + shape: shape, + context: context + } + }); + }; + + Resize.prototype.computeMinResizeBox = function(context) { + var shape = context.shape, + direction = context.direction, + minDimensions, + childrenBounds; + + minDimensions = context.minDimensions || { + width: DEFAULT_MIN_WIDTH, + height: DEFAULT_MIN_WIDTH + }; + + // get children bounds + childrenBounds = computeChildrenBBox(shape, context.childrenBoxPadding); + + // get correct minimum bounds from given resize direction + // basically ensures that the minBounds is max(childrenBounds, minDimensions) + return getMinResizeBounds(direction, shape, minDimensions, childrenBounds); + }; + + + Resize.$inject = [ + 'eventBus', + 'rules', + 'modeling', + 'dragging' + ]; + + // helpers ////////// + + function boundsChanged(shape, newBounds) { + return shape.x !== newBounds.x || + shape.y !== newBounds.y || + shape.width !== newBounds.width || + shape.height !== newBounds.height; + } + + function getReferencePoint$1(shape, direction) { + var mid = getMid(shape), + trbl = asTRBL(shape); + + var referencePoint = { + x: mid.x, + y: mid.y + }; + + if (direction.indexOf('n') !== -1) { + referencePoint.y = trbl.top; + } else if (direction.indexOf('s') !== -1) { + referencePoint.y = trbl.bottom; + } + + if (direction.indexOf('e') !== -1) { + referencePoint.x = trbl.right; + } else if (direction.indexOf('w') !== -1) { + referencePoint.x = trbl.left; + } + + return referencePoint; + } + + function getCursor(direction) { + var prefix = 'resize-'; + + if (direction === 'n' || direction === 's') { + return prefix + 'ns'; + } else if (direction === 'e' || direction === 'w') { + return prefix + 'ew'; + } else if (direction === 'nw' || direction === 'se') { + return prefix + 'nwse'; + } else { + return prefix + 'nesw'; + } + } + + var MARKER_RESIZING$1 = 'djs-resizing', + MARKER_RESIZE_NOT_OK = 'resize-not-ok'; + + var LOW_PRIORITY$e = 500; + + + /** + * Provides previews for resizing shapes when resizing. + * + * @param {EventBus} eventBus + * @param {Canvas} canvas + * @param {PreviewSupport} previewSupport + */ + function ResizePreview(eventBus, canvas, previewSupport) { + + /** + * Update resizer frame. + * + * @param {Object} context + */ + function updateFrame(context) { + + var shape = context.shape, + bounds = context.newBounds, + frame = context.frame; + + if (!frame) { + frame = context.frame = previewSupport.addFrame(shape, canvas.getActiveLayer()); + + canvas.addMarker(shape, MARKER_RESIZING$1); + } + + if (bounds.width > 5) { + attr(frame, { x: bounds.x, width: bounds.width }); + } + + if (bounds.height > 5) { + attr(frame, { y: bounds.y, height: bounds.height }); + } + + if (context.canExecute) { + classes(frame).remove(MARKER_RESIZE_NOT_OK); + } else { + classes(frame).add(MARKER_RESIZE_NOT_OK); + } + } + + /** + * Remove resizer frame. + * + * @param {Object} context + */ + function removeFrame(context) { + var shape = context.shape, + frame = context.frame; + + if (frame) { + remove$1(context.frame); + } + + canvas.removeMarker(shape, MARKER_RESIZING$1); + } + + // add and update previews + eventBus.on('resize.move', LOW_PRIORITY$e, function(event) { + updateFrame(event.context); + }); + + // remove previews + eventBus.on('resize.cleanup', function(event) { + removeFrame(event.context); + }); + + } + + ResizePreview.$inject = [ + 'eventBus', + 'canvas', + 'previewSupport' + ]; + + var HANDLE_OFFSET = -6, + HANDLE_SIZE = 8, + HANDLE_HIT_SIZE = 20; + + var CLS_RESIZER = 'djs-resizer'; + + var directions = [ 'n', 'w', 's', 'e', 'nw', 'ne', 'se', 'sw' ]; + + + /** + * This component is responsible for adding resize handles. + * + * @param {EventBus} eventBus + * @param {Canvas} canvas + * @param {Selection} selection + * @param {Resize} resize + */ + function ResizeHandles(eventBus, canvas, selection, resize) { + + this._resize = resize; + this._canvas = canvas; + + var self = this; + + eventBus.on('selection.changed', function(e) { + var newSelection = e.newSelection; + + // remove old selection markers + self.removeResizers(); + + // add new selection markers ONLY if single selection + if (newSelection.length === 1) { + forEach$1(newSelection, bind(self.addResizer, self)); + } + }); + + eventBus.on('shape.changed', function(e) { + var shape = e.element; + + if (selection.isSelected(shape)) { + self.removeResizers(); + + self.addResizer(shape); + } + }); + } + + + ResizeHandles.prototype.makeDraggable = function(element, gfx, direction) { + var resize = this._resize; + + function startResize(event) { + + // only trigger on left mouse button + if (isPrimaryButton(event)) { + resize.activate(event, element, direction); + } + } + + componentEvent.bind(gfx, 'mousedown', startResize); + componentEvent.bind(gfx, 'touchstart', startResize); + }; + + + ResizeHandles.prototype._createResizer = function(element, x, y, direction) { + var resizersParent = this._getResizersParent(); + + var offset = getHandleOffset(direction); + + var group = create$1('g'); + + classes(group).add(CLS_RESIZER); + classes(group).add(CLS_RESIZER + '-' + element.id); + classes(group).add(CLS_RESIZER + '-' + direction); + + append(resizersParent, group); + + var visual = create$1('rect'); + + attr(visual, { + x: -HANDLE_SIZE / 2 + offset.x, + y: -HANDLE_SIZE / 2 + offset.y, + width: HANDLE_SIZE, + height: HANDLE_SIZE + }); + + classes(visual).add(CLS_RESIZER + '-visual'); + + append(group, visual); + + var hit = create$1('rect'); + + attr(hit, { + x: -HANDLE_HIT_SIZE / 2 + offset.x, + y: -HANDLE_HIT_SIZE / 2 + offset.y, + width: HANDLE_HIT_SIZE, + height: HANDLE_HIT_SIZE + }); + + classes(hit).add(CLS_RESIZER + '-hit'); + + append(group, hit); + + transform(group, x, y); + + return group; + }; + + ResizeHandles.prototype.createResizer = function(element, direction) { + var point = getReferencePoint$1(element, direction); + + var resizer = this._createResizer(element, point.x, point.y, direction); + + this.makeDraggable(element, resizer, direction); + }; + + // resize handles implementation /////////////////////////////// + + /** + * Add resizers for a given element. + * + * @param {djs.model.Element} element + */ + ResizeHandles.prototype.addResizer = function(element) { + var self = this; + + if (isConnection$a(element) || !this._resize.canResize({ shape: element })) { + return; + } + + forEach$1(directions, function(direction) { + self.createResizer(element, direction); + }); + }; + + /** + * Remove all resizers + */ + ResizeHandles.prototype.removeResizers = function() { + var resizersParent = this._getResizersParent(); + + clear(resizersParent); + }; + + ResizeHandles.prototype._getResizersParent = function() { + return this._canvas.getLayer('resizers'); + }; + + ResizeHandles.$inject = [ + 'eventBus', + 'canvas', + 'selection', + 'resize' + ]; + + // helpers ////////// + + function getHandleOffset(direction) { + var offset = { + x: 0, + y: 0 + }; + + if (direction.indexOf('e') !== -1) { + offset.x = -HANDLE_OFFSET; + } else if (direction.indexOf('w') !== -1) { + offset.x = HANDLE_OFFSET; + } + + if (direction.indexOf('s') !== -1) { + offset.y = -HANDLE_OFFSET; + } else if (direction.indexOf('n') !== -1) { + offset.y = HANDLE_OFFSET; + } + + return offset; + } + + function isConnection$a(element) { + return !!element.waypoints; + } + + var ResizeModule = { + __depends__: [ + RulesModule$1, + DraggingModule, + PreviewSupportModule + ], + __init__: [ + 'resize', + 'resizePreview', + 'resizeHandles' + ], + resize: [ 'type', Resize ], + resizePreview: [ 'type', ResizePreview ], + resizeHandles: [ 'type', ResizeHandles ] + }; + + var HIGH_PRIORITY$c = 2000; + + + function LabelEditingProvider( + eventBus, bpmnFactory, canvas, directEditing, + modeling, resizeHandles, textRenderer) { + + this._bpmnFactory = bpmnFactory; + this._canvas = canvas; + this._modeling = modeling; + this._textRenderer = textRenderer; + + directEditing.registerProvider(this); + + // listen to dblclick on non-root elements + eventBus.on('element.dblclick', function(event) { + activateDirectEdit(event.element, true); + }); + + // complete on followup canvas operation + eventBus.on([ + 'autoPlace.start', + 'canvas.viewbox.changing', + 'drag.init', + 'element.mousedown', + 'popupMenu.open', + 'root.set', + 'selection.changed' + ], function(event) { + + if (directEditing.isActive()) { + directEditing.complete(); + } + }); + + eventBus.on([ + 'shape.remove', + 'connection.remove' + ], HIGH_PRIORITY$c, function(event) { + + if (directEditing.isActive(event.element)) { + directEditing.cancel(); + } + }); + + // cancel on command stack changes + eventBus.on([ 'commandStack.changed' ], function(e) { + if (directEditing.isActive()) { + directEditing.cancel(); + } + }); + + + eventBus.on('directEditing.activate', function(event) { + resizeHandles.removeResizers(); + }); + + eventBus.on('create.end', 500, function(event) { + + var context = event.context, + element = context.shape, + canExecute = event.context.canExecute, + isTouch = event.isTouch; + + // TODO(nikku): we need to find a way to support the + // direct editing on mobile devices; right now this will + // break for desworkflowediting on mobile devices + // as it breaks the user interaction workflow + + // TODO(nre): we should temporarily focus the edited element + // here and release the focused viewport after the direct edit + // operation is finished + if (isTouch) { + return; + } + + if (!canExecute) { + return; + } + + if (context.hints && context.hints.createElementsBehavior === false) { + return; + } + + activateDirectEdit(element); + }); + + eventBus.on('autoPlace.end', 500, function(event) { + activateDirectEdit(event.shape); + }); + + + function activateDirectEdit(element, force) { + if (force || + isAny(element, [ 'bpmn:Task', 'bpmn:TextAnnotation' ]) || + isCollapsedSubProcess(element)) { + + directEditing.activate(element); + } + } + + } + + LabelEditingProvider.$inject = [ + 'eventBus', + 'bpmnFactory', + 'canvas', + 'directEditing', + 'modeling', + 'resizeHandles', + 'textRenderer' + ]; + + + /** + * Activate direct editing for activities and text annotations. + * + * @param {djs.model.Base} element + * + * @return {Object} an object with properties bounds (position and size), text and options + */ + LabelEditingProvider.prototype.activate = function(element) { + + // text + var text = getLabel(element); + + if (text === undefined) { + return; + } + + var context = { + text: text + }; + + // bounds + var bounds = this.getEditingBBox(element); + + assign(context, bounds); + + var options = {}; + + // tasks + if ( + isAny(element, [ + 'bpmn:Task', + 'bpmn:Participant', + 'bpmn:Lane', + 'bpmn:CallActivity' + ]) || + isCollapsedSubProcess(element) + ) { + assign(options, { + centerVertically: true + }); + } + + // external labels + if (isLabelExternal(element)) { + assign(options, { + autoResize: true + }); + } + + // text annotations + if (is$1(element, 'bpmn:TextAnnotation')) { + assign(options, { + resizable: true, + autoResize: true + }); + } + + assign(context, { + options: options + }); + + return context; + }; + + + /** + * Get the editing bounding box based on the element's size and position + * + * @param {djs.model.Base} element + * + * @return {Object} an object containing information about position + * and size (fixed or minimum and/or maximum) + */ + LabelEditingProvider.prototype.getEditingBBox = function(element) { + var canvas = this._canvas; + + var target = element.label || element; + + var bbox = canvas.getAbsoluteBBox(target); + + var mid = { + x: bbox.x + bbox.width / 2, + y: bbox.y + bbox.height / 2 + }; + + // default position + var bounds = { x: bbox.x, y: bbox.y }; + + var zoom = canvas.zoom(); + + var defaultStyle = this._textRenderer.getDefaultStyle(), + externalStyle = this._textRenderer.getExternalStyle(); + + // take zoom into account + var externalFontSize = externalStyle.fontSize * zoom, + externalLineHeight = externalStyle.lineHeight, + defaultFontSize = defaultStyle.fontSize * zoom, + defaultLineHeight = defaultStyle.lineHeight; + + var style = { + fontFamily: this._textRenderer.getDefaultStyle().fontFamily, + fontWeight: this._textRenderer.getDefaultStyle().fontWeight + }; + + // adjust for expanded pools AND lanes + if (is$1(element, 'bpmn:Lane') || isExpandedPool(element)) { + + assign(bounds, { + width: bbox.height, + height: 30 * zoom, + x: bbox.x - bbox.height / 2 + (15 * zoom), + y: mid.y - (30 * zoom) / 2 + }); + + assign(style, { + fontSize: defaultFontSize + 'px', + lineHeight: defaultLineHeight, + paddingTop: (7 * zoom) + 'px', + paddingBottom: (7 * zoom) + 'px', + paddingLeft: (5 * zoom) + 'px', + paddingRight: (5 * zoom) + 'px', + transform: 'rotate(-90deg)' + }); + } + + + // internal labels for tasks and collapsed call activities, + // sub processes and participants + if (isAny(element, [ 'bpmn:Task', 'bpmn:CallActivity' ]) || + isCollapsedPool(element) || + isCollapsedSubProcess(element)) { + + assign(bounds, { + width: bbox.width, + height: bbox.height + }); + + assign(style, { + fontSize: defaultFontSize + 'px', + lineHeight: defaultLineHeight, + paddingTop: (7 * zoom) + 'px', + paddingBottom: (7 * zoom) + 'px', + paddingLeft: (5 * zoom) + 'px', + paddingRight: (5 * zoom) + 'px' + }); + } + + + // internal labels for expanded sub processes + if (isExpandedSubProcess$1(element)) { + assign(bounds, { + width: bbox.width, + x: bbox.x + }); + + assign(style, { + fontSize: defaultFontSize + 'px', + lineHeight: defaultLineHeight, + paddingTop: (7 * zoom) + 'px', + paddingBottom: (7 * zoom) + 'px', + paddingLeft: (5 * zoom) + 'px', + paddingRight: (5 * zoom) + 'px' + }); + } + + var width = 90 * zoom, + paddingTop = 7 * zoom, + paddingBottom = 4 * zoom; + + // external labels for events, data elements, gateways, groups and connections + if (target.labelTarget) { + assign(bounds, { + width: width, + height: bbox.height + paddingTop + paddingBottom, + x: mid.x - width / 2, + y: bbox.y - paddingTop + }); + + assign(style, { + fontSize: externalFontSize + 'px', + lineHeight: externalLineHeight, + paddingTop: paddingTop + 'px', + paddingBottom: paddingBottom + 'px' + }); + } + + // external label not yet created + if (isLabelExternal(target) + && !hasExternalLabel(target) + && !isLabel$6(target)) { + + var externalLabelMid = getExternalLabelMid(element); + + var absoluteBBox = canvas.getAbsoluteBBox({ + x: externalLabelMid.x, + y: externalLabelMid.y, + width: 0, + height: 0 + }); + + var height = externalFontSize + paddingTop + paddingBottom; + + assign(bounds, { + width: width, + height: height, + x: absoluteBBox.x - width / 2, + y: absoluteBBox.y - height / 2 + }); + + assign(style, { + fontSize: externalFontSize + 'px', + lineHeight: externalLineHeight, + paddingTop: paddingTop + 'px', + paddingBottom: paddingBottom + 'px' + }); + } + + // text annotations + if (is$1(element, 'bpmn:TextAnnotation')) { + assign(bounds, { + width: bbox.width, + height: bbox.height, + minWidth: 30 * zoom, + minHeight: 10 * zoom + }); + + assign(style, { + textAlign: 'left', + paddingTop: (5 * zoom) + 'px', + paddingBottom: (7 * zoom) + 'px', + paddingLeft: (7 * zoom) + 'px', + paddingRight: (5 * zoom) + 'px', + fontSize: defaultFontSize + 'px', + lineHeight: defaultLineHeight + }); + } + + return { bounds: bounds, style: style }; + }; + + + LabelEditingProvider.prototype.update = function( + element, newLabel, + activeContextText, bounds) { + + var newBounds, + bbox; + + if (is$1(element, 'bpmn:TextAnnotation')) { + + bbox = this._canvas.getAbsoluteBBox(element); + + newBounds = { + x: element.x, + y: element.y, + width: element.width / bbox.width * bounds.width, + height: element.height / bbox.height * bounds.height + }; + } + + if (isEmptyText$1(newLabel)) { + newLabel = null; + } + + this._modeling.updateLabel(element, newLabel, newBounds); + }; + + + + // helpers ////////////////////// + + function isCollapsedSubProcess(element) { + return is$1(element, 'bpmn:SubProcess') && !isExpanded(element); + } + + function isExpandedSubProcess$1(element) { + return is$1(element, 'bpmn:SubProcess') && isExpanded(element); + } + + function isCollapsedPool(element) { + return is$1(element, 'bpmn:Participant') && !isExpanded(element); + } + + function isExpandedPool(element) { + return is$1(element, 'bpmn:Participant') && isExpanded(element); + } + + function isEmptyText$1(label) { + return !label || !label.trim(); + } + + var MARKER_HIDDEN = 'djs-element-hidden', + MARKER_LABEL_HIDDEN = 'djs-label-hidden'; + + + function LabelEditingPreview( + eventBus, canvas, elementRegistry, + pathMap) { + + var self = this; + + var defaultLayer = canvas.getDefaultLayer(); + + var element, absoluteElementBBox, gfx; + + eventBus.on('directEditing.activate', function(context) { + var activeProvider = context.active; + + element = activeProvider.element.label || activeProvider.element; + + // text annotation + if (is$1(element, 'bpmn:TextAnnotation')) { + absoluteElementBBox = canvas.getAbsoluteBBox(element); + + gfx = create$1('g'); + + var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.0, + my: 0.0 + } + }); + + var path = self.path = create$1('path'); + + attr(path, { + d: textPathData, + strokeWidth: 2, + stroke: getStrokeColor(element) + }); + + append(gfx, path); + + append(defaultLayer, gfx); + + translate$2(gfx, element.x, element.y); + } + + if (is$1(element, 'bpmn:TextAnnotation') || + element.labelTarget) { + canvas.addMarker(element, MARKER_HIDDEN); + } else if (is$1(element, 'bpmn:Task') || + is$1(element, 'bpmn:CallActivity') || + is$1(element, 'bpmn:SubProcess') || + is$1(element, 'bpmn:Participant')) { + canvas.addMarker(element, MARKER_LABEL_HIDDEN); + } + }); + + eventBus.on('directEditing.resize', function(context) { + + // text annotation + if (is$1(element, 'bpmn:TextAnnotation')) { + var height = context.height, + dy = context.dy; + + var newElementHeight = Math.max(element.height / absoluteElementBBox.height * (height + dy), 0); + + var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: newElementHeight, + position: { + mx: 0.0, + my: 0.0 + } + }); + + attr(self.path, { + d: textPathData + }); + } + }); + + eventBus.on([ 'directEditing.complete', 'directEditing.cancel' ], function(context) { + var activeProvider = context.active; + + if (activeProvider) { + canvas.removeMarker(activeProvider.element.label || activeProvider.element, MARKER_HIDDEN); + canvas.removeMarker(element, MARKER_LABEL_HIDDEN); + } + + element = undefined; + absoluteElementBBox = undefined; + + if (gfx) { + remove$1(gfx); + + gfx = undefined; + } + }); + } + + LabelEditingPreview.$inject = [ + 'eventBus', + 'canvas', + 'elementRegistry', + 'pathMap' + ]; + + + // helpers /////////////////// + + function getStrokeColor(element, defaultColor) { + var di = getDi(element); + + return di.get('stroke') || defaultColor || 'black'; + } + + var LabelEditingModule = { + __depends__: [ + ChangeSupportModule, + ResizeModule, + DirectEditingModule + ], + __init__: [ + 'labelEditingProvider', + 'labelEditingPreview' + ], + labelEditingProvider: [ 'type', LabelEditingProvider ], + labelEditingPreview: [ 'type', LabelEditingPreview ] + }; + + var ALIGNMENTS = [ + 'top', + 'bottom', + 'left', + 'right' + ]; + + var ELEMENT_LABEL_DISTANCE = 10; + + /** + * A component that makes sure that external labels are added + * together with respective elements and properly updated (DI wise) + * during move. + * + * @param {EventBus} eventBus + * @param {Modeling} modeling + */ + function AdaptiveLabelPositioningBehavior(eventBus, modeling) { + + CommandInterceptor.call(this, eventBus); + + this.postExecuted([ + 'connection.create', + 'connection.layout', + 'connection.updateWaypoints' + ], function(event) { + var context = event.context, + connection = context.connection, + source = connection.source, + target = connection.target, + hints = context.hints || {}; + + if (hints.createElementsBehavior !== false) { + checkLabelAdjustment(source); + checkLabelAdjustment(target); + } + }); + + + this.postExecuted([ + 'label.create' + ], function(event) { + var context = event.context, + shape = context.shape, + hints = context.hints || {}; + + if (hints.createElementsBehavior !== false) { + checkLabelAdjustment(shape.labelTarget); + } + }); + + + this.postExecuted([ + 'elements.create' + ], function(event) { + var context = event.context, + elements = context.elements, + hints = context.hints || {}; + + if (hints.createElementsBehavior !== false) { + elements.forEach(function(element) { + checkLabelAdjustment(element); + }); + } + }); + + function checkLabelAdjustment(element) { + + // skip non-existing labels + if (!hasExternalLabel(element)) { + return; + } + + var optimalPosition = getOptimalPosition(element); + + // no optimal position found + if (!optimalPosition) { + return; + } + + adjustLabelPosition(element, optimalPosition); + } + + function adjustLabelPosition(element, orientation) { + + var elementMid = getMid(element), + label = element.label, + labelMid = getMid(label); + + // ignore labels that are being created + if (!label.parent) { + return; + } + + var elementTrbl = asTRBL(element); + + var newLabelMid; + + switch (orientation) { + case 'top': + newLabelMid = { + x: elementMid.x, + y: elementTrbl.top - ELEMENT_LABEL_DISTANCE - label.height / 2 + }; + + break; + + case 'left': + + newLabelMid = { + x: elementTrbl.left - ELEMENT_LABEL_DISTANCE - label.width / 2, + y: elementMid.y + }; + + break; + + case 'bottom': + + newLabelMid = { + x: elementMid.x, + y: elementTrbl.bottom + ELEMENT_LABEL_DISTANCE + label.height / 2 + }; + + break; + + case 'right': + + newLabelMid = { + x: elementTrbl.right + ELEMENT_LABEL_DISTANCE + label.width / 2, + y: elementMid.y + }; + + break; + } + + var delta$1 = delta(newLabelMid, labelMid); + + modeling.moveShape(label, delta$1); + } + + } + + e(AdaptiveLabelPositioningBehavior, CommandInterceptor); + + AdaptiveLabelPositioningBehavior.$inject = [ + 'eventBus', + 'modeling' + ]; + + + // helpers ////////////////////// + + /** + * Return alignments which are taken by a boundary's host element + * + * @param {Shape} element + * + * @return {Array} + */ + function getTakenHostAlignments(element) { + + var hostElement = element.host, + elementMid = getMid(element), + hostOrientation = getOrientation(elementMid, hostElement); + + var freeAlignments; + + // check whether there is a multi-orientation, e.g. 'top-left' + if (hostOrientation.indexOf('-') >= 0) { + freeAlignments = hostOrientation.split('-'); + } else { + freeAlignments = [ hostOrientation ]; + } + + var takenAlignments = ALIGNMENTS.filter(function(alignment) { + + return freeAlignments.indexOf(alignment) === -1; + }); + + return takenAlignments; + + } + + /** + * Return alignments which are taken by related connections + * + * @param {Shape} element + * + * @return {Array} + */ + function getTakenConnectionAlignments(element) { + + var elementMid = getMid(element); + + var takenAlignments = [].concat( + element.incoming.map(function(c) { + return c.waypoints[c.waypoints.length - 2 ]; + }), + element.outgoing.map(function(c) { + return c.waypoints[1]; + }) + ).map(function(point) { + return getApproximateOrientation(elementMid, point); + }); + + return takenAlignments; + } + + /** + * Return the optimal label position around an element + * or _undefined_, if none was found. + * + * @param {Shape} element + * + * @return {string} positioning identifier + */ + function getOptimalPosition(element) { + + var labelMid = getMid(element.label); + + var elementMid = getMid(element); + + var labelOrientation = getApproximateOrientation(elementMid, labelMid); + + if (!isAligned(labelOrientation)) { + return; + } + + var takenAlignments = getTakenConnectionAlignments(element); + + if (element.host) { + var takenHostAlignments = getTakenHostAlignments(element); + + takenAlignments = takenAlignments.concat(takenHostAlignments); + } + + var freeAlignments = ALIGNMENTS.filter(function(alignment) { + + return takenAlignments.indexOf(alignment) === -1; + }); + + // NOTHING TO DO; label already aligned a.O.K. + if (freeAlignments.indexOf(labelOrientation) !== -1) { + return; + } + + return freeAlignments[0]; + } + + function getApproximateOrientation(p0, p1) { + return getOrientation(p1, p0, 5); + } + + function isAligned(orientation) { + return ALIGNMENTS.indexOf(orientation) !== -1; + } + + function AppendBehavior(eventBus, elementFactory, bpmnRules) { + + CommandInterceptor.call(this, eventBus); + + // assign correct shape position unless already set + + this.preExecute('shape.append', function(context) { + + var source = context.source, + shape = context.shape; + + if (!context.position) { + + if (is$1(shape, 'bpmn:TextAnnotation')) { + context.position = { + x: source.x + source.width / 2 + 75, + y: source.y - (50) - shape.height / 2 + }; + } else { + context.position = { + x: source.x + source.width + 80 + shape.width / 2, + y: source.y + source.height / 2 + }; + } + } + }, true); + } + + e(AppendBehavior, CommandInterceptor); + + AppendBehavior.$inject = [ + 'eventBus', + 'elementFactory', + 'bpmnRules' + ]; + + function AssociationBehavior(injector, modeling) { + injector.invoke(CommandInterceptor, this); + + this.postExecute('shape.move', function(context) { + var newParent = context.newParent, + shape = context.shape; + + var associations = filter(shape.incoming.concat(shape.outgoing), function(connection) { + return is$1(connection, 'bpmn:Association'); + }); + + forEach$1(associations, function(association) { + modeling.moveConnection(association, { x: 0, y: 0 }, newParent); + }); + }, true); + } + + e(AssociationBehavior, CommandInterceptor); + + AssociationBehavior.$inject = [ + 'injector', + 'modeling' + ]; + + var LOW_PRIORITY$d = 500; + + + /** + * Replace intermediate event with boundary event when creating or moving results in attached event. + */ + function AttachEventBehavior(bpmnReplace, injector) { + injector.invoke(CommandInterceptor, this); + + this._bpmnReplace = bpmnReplace; + + var self = this; + + this.postExecuted('elements.create', LOW_PRIORITY$d, function(context) { + var elements = context.elements; + + elements = elements.filter(function(shape) { + var host = shape.host; + + return shouldReplace$1(shape, host); + }); + + if (elements.length !== 1) { + return; + } + + elements.map(function(element) { + return elements.indexOf(element); + }).forEach(function(index) { + var host = elements[ index ]; + + context.elements[ index ] = self.replaceShape(elements[ index ], host); + }); + }, true); + + + this.preExecute('elements.move', LOW_PRIORITY$d, function(context) { + var shapes = context.shapes, + host = context.newHost; + + if (shapes.length !== 1) { + return; + } + + var shape = shapes[0]; + + if (shouldReplace$1(shape, host)) { + context.shapes = [ self.replaceShape(shape, host) ]; + } + }, true); + } + + AttachEventBehavior.$inject = [ + 'bpmnReplace', + 'injector' + ]; + + e(AttachEventBehavior, CommandInterceptor); + + AttachEventBehavior.prototype.replaceShape = function(shape, host) { + var eventDefinition = getEventDefinition$1(shape); + + var boundaryEvent = { + type: 'bpmn:BoundaryEvent', + host: host + }; + + if (eventDefinition) { + boundaryEvent.eventDefinitionType = eventDefinition.$type; + } + + return this._bpmnReplace.replaceElement(shape, boundaryEvent, { layoutConnection: false }); + }; + + + // helpers ////////// + + function getEventDefinition$1(element) { + var businessObject = getBusinessObject(element), + eventDefinitions = businessObject.eventDefinitions; + + return eventDefinitions && eventDefinitions[0]; + } + + function shouldReplace$1(shape, host) { + return !isLabel$6(shape) && + isAny(shape, [ 'bpmn:IntermediateThrowEvent', 'bpmn:IntermediateCatchEvent' ]) && !!host; + } + + /** + * BPMN specific boundary event behavior + */ + function BoundaryEventBehavior(eventBus, modeling) { + + CommandInterceptor.call(this, eventBus); + + function getBoundaryEvents(element) { + return filter(element.attachers, function(attacher) { + return is$1(attacher, 'bpmn:BoundaryEvent'); + }); + } + + // remove after connecting to event-based gateway + this.postExecute('connection.create', function(event) { + var source = event.context.source, + target = event.context.target, + boundaryEvents = getBoundaryEvents(target); + + if ( + is$1(source, 'bpmn:EventBasedGateway') && + is$1(target, 'bpmn:ReceiveTask') && + boundaryEvents.length > 0 + ) { + modeling.removeElements(boundaryEvents); + } + + }); + + // remove after replacing connected gateway with event-based gateway + this.postExecute('connection.reconnect', function(event) { + var oldSource = event.context.oldSource, + newSource = event.context.newSource; + + if (is$1(oldSource, 'bpmn:Gateway') && + is$1(newSource, 'bpmn:EventBasedGateway')) { + forEach$1(newSource.outgoing, function(connection) { + var target = connection.target, + attachedboundaryEvents = getBoundaryEvents(target); + + if (is$1(target, 'bpmn:ReceiveTask') && + attachedboundaryEvents.length > 0) { + modeling.removeElements(attachedboundaryEvents); + } + }); + } + }); + + } + + BoundaryEventBehavior.$inject = [ + 'eventBus', + 'modeling' + ]; + + e(BoundaryEventBehavior, CommandInterceptor); + + function CreateBehavior(injector) { + injector.invoke(CommandInterceptor, this); + + this.preExecute('shape.create', 1500, function(event) { + var context = event.context, + parent = context.parent, + shape = context.shape; + + if (is$1(parent, 'bpmn:Lane') && !is$1(shape, 'bpmn:Lane')) { + context.parent = getParent(parent, 'bpmn:Participant'); + } + }); + + } + + + CreateBehavior.$inject = [ 'injector' ]; + + e(CreateBehavior, CommandInterceptor); + + /** + * BPMN specific create data object behavior + */ + function CreateDataObjectBehavior(eventBus, bpmnFactory, moddle) { + + CommandInterceptor.call(this, eventBus); + + this.preExecute('shape.create', function(event) { + + var context = event.context, + shape = context.shape; + + if (is$1(shape, 'bpmn:DataObjectReference') && shape.type !== 'label') { + + // create a DataObject every time a DataObjectReference is created + var dataObject = bpmnFactory.create('bpmn:DataObject'); + + // set the reference to the DataObject + shape.businessObject.dataObjectRef = dataObject; + } + }); + + } + + CreateDataObjectBehavior.$inject = [ + 'eventBus', + 'bpmnFactory', + 'moddle' + ]; + + e(CreateDataObjectBehavior, CommandInterceptor); + + var HORIZONTAL_PARTICIPANT_PADDING = 20, + VERTICAL_PARTICIPANT_PADDING = 20; + + var PARTICIPANT_BORDER_WIDTH = 30; + + var HIGH_PRIORITY$b = 2000; + + + /** + * BPMN-specific behavior for creating participants. + */ + function CreateParticipantBehavior(canvas, eventBus, modeling) { + CommandInterceptor.call(this, eventBus); + + // fit participant + eventBus.on([ + 'create.start', + 'shape.move.start' + ], HIGH_PRIORITY$b, function(event) { + var context = event.context, + shape = context.shape, + rootElement = canvas.getRootElement(); + + if (!is$1(shape, 'bpmn:Participant') || + !is$1(rootElement, 'bpmn:Process') || + !rootElement.children.length) { + return; + } + + // ignore connections, groups and labels + var children = rootElement.children.filter(function(element) { + return !is$1(element, 'bpmn:Group') && + !isLabel$6(element) && + !isConnection$9(element); + }); + + // ensure for available children to calculate bounds + if (!children.length) { + return; + } + + var childrenBBox = getBBox(children); + + var participantBounds = getParticipantBounds(shape, childrenBBox); + + // assign width and height + assign(shape, participantBounds); + + // assign create constraints + context.createConstraints = getParticipantCreateConstraints(shape, childrenBBox); + }); + + // force hovering process when creating first participant + eventBus.on('create.start', HIGH_PRIORITY$b, function(event) { + var context = event.context, + shape = context.shape, + rootElement = canvas.getRootElement(), + rootElementGfx = canvas.getGraphics(rootElement); + + function ensureHoveringProcess(event) { + event.element = rootElement; + event.gfx = rootElementGfx; + } + + if (is$1(shape, 'bpmn:Participant') && is$1(rootElement, 'bpmn:Process')) { + eventBus.on('element.hover', HIGH_PRIORITY$b, ensureHoveringProcess); + + eventBus.once('create.cleanup', function() { + eventBus.off('element.hover', ensureHoveringProcess); + }); + } + }); + + // turn process into collaboration when creating first participant + function getOrCreateCollaboration() { + var rootElement = canvas.getRootElement(); + + if (is$1(rootElement, 'bpmn:Collaboration')) { + return rootElement; + } + + return modeling.makeCollaboration(); + } + + // when creating mutliple elements through `elements.create` parent must be set to collaboration + // and passed to `shape.create` as hint + this.preExecute('elements.create', HIGH_PRIORITY$b, function(context) { + var elements = context.elements, + parent = context.parent, + participant = findParticipant(elements), + hints; + + if (participant && is$1(parent, 'bpmn:Process')) { + context.parent = getOrCreateCollaboration(); + + hints = context.hints = context.hints || {}; + + hints.participant = participant; + hints.process = parent; + hints.processRef = getBusinessObject(participant).get('processRef'); + } + }, true); + + // when creating single shape through `shape.create` parent must be set to collaboration + // unless it was already set through `elements.create` + this.preExecute('shape.create', function(context) { + var parent = context.parent, + shape = context.shape; + + if (is$1(shape, 'bpmn:Participant') && is$1(parent, 'bpmn:Process')) { + context.parent = getOrCreateCollaboration(); + + context.process = parent; + context.processRef = getBusinessObject(shape).get('processRef'); + } + }, true); + + // #execute necessary because #preExecute not called on CommandStack#redo + this.execute('shape.create', function(context) { + var hints = context.hints || {}, + process = context.process || hints.process, + shape = context.shape, + participant = hints.participant; + + // both shape.create and elements.create must be handled + if (process && (!participant || shape === participant)) { + + // monkey-patch process ref + getBusinessObject(shape).set('processRef', getBusinessObject(process)); + } + }, true); + + this.revert('shape.create', function(context) { + var hints = context.hints || {}, + process = context.process || hints.process, + processRef = context.processRef || hints.processRef, + shape = context.shape, + participant = hints.participant; + + // both shape.create and elements.create must be handled + if (process && (!participant || shape === participant)) { + + // monkey-patch process ref + getBusinessObject(shape).set('processRef', processRef); + } + }, true); + + this.postExecute('shape.create', function(context) { + var hints = context.hints || {}, + process = context.process || context.hints.process, + shape = context.shape, + participant = hints.participant; + + if (process) { + var children = process.children.slice(); + + // both shape.create and elements.create must be handled + if (!participant) { + modeling.moveElements(children, { x: 0, y: 0 }, shape); + } else if (shape === participant) { + modeling.moveElements(children, { x: 0, y: 0 }, participant); + } + } + }, true); + } + + CreateParticipantBehavior.$inject = [ + 'canvas', + 'eventBus', + 'modeling' + ]; + + e(CreateParticipantBehavior, CommandInterceptor); + + // helpers ////////// + + function getParticipantBounds(shape, childrenBBox) { + childrenBBox = { + width: childrenBBox.width + HORIZONTAL_PARTICIPANT_PADDING * 2 + PARTICIPANT_BORDER_WIDTH, + height: childrenBBox.height + VERTICAL_PARTICIPANT_PADDING * 2 + }; + + var width = Math.max(shape.width, childrenBBox.width), + height = Math.max(shape.height, childrenBBox.height); + + return { + x: -width / 2, + y: -height / 2, + width: width, + height: height + }; + } + + function getParticipantCreateConstraints(shape, childrenBBox) { + childrenBBox = asTRBL(childrenBBox); + + return { + bottom: childrenBBox.top + shape.height / 2 - VERTICAL_PARTICIPANT_PADDING, + left: childrenBBox.right - shape.width / 2 + HORIZONTAL_PARTICIPANT_PADDING, + top: childrenBBox.bottom - shape.height / 2 + VERTICAL_PARTICIPANT_PADDING, + right: childrenBBox.left + shape.width / 2 - HORIZONTAL_PARTICIPANT_PADDING - PARTICIPANT_BORDER_WIDTH + }; + } + + function isConnection$9(element) { + return !!element.waypoints; + } + + function findParticipant(elements) { + return find(elements, function(element) { + return is$1(element, 'bpmn:Participant'); + }); + } + + var TARGET_REF_PLACEHOLDER_NAME = '__targetRef_placeholder'; + + + /** + * This behavior makes sure we always set a fake + * DataInputAssociation#targetRef as demanded by the BPMN 2.0 + * XSD schema. + * + * The reference is set to a bpmn:Property{ name: '__targetRef_placeholder' } + * which is created on the fly and cleaned up afterwards if not needed + * anymore. + * + * @param {EventBus} eventBus + * @param {BpmnFactory} bpmnFactory + */ + function DataInputAssociationBehavior(eventBus, bpmnFactory) { + + CommandInterceptor.call(this, eventBus); + + + this.executed([ + 'connection.create', + 'connection.delete', + 'connection.move', + 'connection.reconnect' + ], ifDataInputAssociation(fixTargetRef)); + + this.reverted([ + 'connection.create', + 'connection.delete', + 'connection.move', + 'connection.reconnect' + ], ifDataInputAssociation(fixTargetRef)); + + + function usesTargetRef(element, targetRef, removedConnection) { + + var inputAssociations = element.get('dataInputAssociations'); + + return find(inputAssociations, function(association) { + return association !== removedConnection && + association.targetRef === targetRef; + }); + } + + function getTargetRef(element, create) { + + var properties = element.get('properties'); + + var targetRefProp = find(properties, function(p) { + return p.name === TARGET_REF_PLACEHOLDER_NAME; + }); + + if (!targetRefProp && create) { + targetRefProp = bpmnFactory.create('bpmn:Property', { + name: TARGET_REF_PLACEHOLDER_NAME + }); + + add(properties, targetRefProp); + } + + return targetRefProp; + } + + function cleanupTargetRef(element, connection) { + + var targetRefProp = getTargetRef(element); + + if (!targetRefProp) { + return; + } + + if (!usesTargetRef(element, targetRefProp, connection)) { + remove(element.get('properties'), targetRefProp); + } + } + + /** + * Make sure targetRef is set to a valid property or + * `null` if the connection is detached. + * + * @param {Event} event + */ + function fixTargetRef(event) { + + var context = event.context, + connection = context.connection, + connectionBo = connection.businessObject, + target = connection.target, + targetBo = target && target.businessObject, + newTarget = context.newTarget, + newTargetBo = newTarget && newTarget.businessObject, + oldTarget = context.oldTarget || context.target, + oldTargetBo = oldTarget && oldTarget.businessObject; + + var dataAssociation = connection.businessObject, + targetRefProp; + + if (oldTargetBo && oldTargetBo !== targetBo) { + cleanupTargetRef(oldTargetBo, connectionBo); + } + + if (newTargetBo && newTargetBo !== targetBo) { + cleanupTargetRef(newTargetBo, connectionBo); + } + + if (targetBo) { + targetRefProp = getTargetRef(targetBo, true); + dataAssociation.targetRef = targetRefProp; + } else { + dataAssociation.targetRef = null; + } + } + } + + DataInputAssociationBehavior.$inject = [ + 'eventBus', + 'bpmnFactory' + ]; + + e(DataInputAssociationBehavior, CommandInterceptor); + + + /** + * Only call the given function when the event + * touches a bpmn:DataInputAssociation. + * + * @param {Function} fn + * @return {Function} + */ + function ifDataInputAssociation(fn) { + + return function(event) { + var context = event.context, + connection = context.connection; + + if (is$1(connection, 'bpmn:DataInputAssociation')) { + return fn(event); + } + }; + } + + function UpdateSemanticParentHandler(bpmnUpdater) { + this._bpmnUpdater = bpmnUpdater; + } + + UpdateSemanticParentHandler.$inject = [ 'bpmnUpdater' ]; + + + UpdateSemanticParentHandler.prototype.execute = function(context) { + var dataStoreBo = context.dataStoreBo, + dataStoreDi = context.dataStoreDi, + newSemanticParent = context.newSemanticParent, + newDiParent = context.newDiParent; + + context.oldSemanticParent = dataStoreBo.$parent; + context.oldDiParent = dataStoreDi.$parent; + + // update semantic parent + this._bpmnUpdater.updateSemanticParent(dataStoreBo, newSemanticParent); + + // update DI parent + this._bpmnUpdater.updateDiParent(dataStoreDi, newDiParent); + }; + + UpdateSemanticParentHandler.prototype.revert = function(context) { + var dataStoreBo = context.dataStoreBo, + dataStoreDi = context.dataStoreDi, + oldSemanticParent = context.oldSemanticParent, + oldDiParent = context.oldDiParent; + + // update semantic parent + this._bpmnUpdater.updateSemanticParent(dataStoreBo, oldSemanticParent); + + // update DI parent + this._bpmnUpdater.updateDiParent(dataStoreDi, oldDiParent); + }; + + /** + * BPMN specific data store behavior + */ + function DataStoreBehavior( + canvas, commandStack, elementRegistry, + eventBus) { + + CommandInterceptor.call(this, eventBus); + + commandStack.registerHandler('dataStore.updateContainment', UpdateSemanticParentHandler); + + function getFirstParticipantWithProcessRef() { + return elementRegistry.filter(function(element) { + return is$1(element, 'bpmn:Participant') && getBusinessObject(element).processRef; + })[0]; + } + + function getDataStores(element) { + return element.children.filter(function(child) { + return is$1(child, 'bpmn:DataStoreReference') && !child.labelTarget; + }); + } + + function updateDataStoreParent(dataStore, newDataStoreParent) { + var dataStoreBo = dataStore.businessObject || dataStore; + + newDataStoreParent = newDataStoreParent || getFirstParticipantWithProcessRef(); + + if (newDataStoreParent) { + var newDataStoreParentBo = newDataStoreParent.businessObject || newDataStoreParent; + + commandStack.execute('dataStore.updateContainment', { + dataStoreBo: dataStoreBo, + dataStoreDi: getDi(dataStore), + newSemanticParent: newDataStoreParentBo.processRef || newDataStoreParentBo, + newDiParent: getDi(newDataStoreParent) + }); + } + } + + + // disable auto-resize for data stores + this.preExecute('shape.create', function(event) { + + var context = event.context, + shape = context.shape; + + if (is$1(shape, 'bpmn:DataStoreReference') && + shape.type !== 'label') { + + if (!context.hints) { + context.hints = {}; + } + + // prevent auto resizing + context.hints.autoResize = false; + } + }); + + + // disable auto-resize for data stores + this.preExecute('elements.move', function(event) { + var context = event.context, + shapes = context.shapes; + + var dataStoreReferences = shapes.filter(function(shape) { + return is$1(shape, 'bpmn:DataStoreReference'); + }); + + if (dataStoreReferences.length) { + if (!context.hints) { + context.hints = {}; + } + + // prevent auto resizing for data store references + context.hints.autoResize = shapes.filter(function(shape) { + return !is$1(shape, 'bpmn:DataStoreReference'); + }); + } + }); + + + // update parent on data store created + this.postExecute('shape.create', function(event) { + var context = event.context, + shape = context.shape, + parent = shape.parent; + + + if (is$1(shape, 'bpmn:DataStoreReference') && + shape.type !== 'label' && + is$1(parent, 'bpmn:Collaboration')) { + + updateDataStoreParent(shape); + } + }); + + + // update parent on data store moved + this.postExecute('shape.move', function(event) { + var context = event.context, + shape = context.shape, + oldParent = context.oldParent, + parent = shape.parent; + + if (is$1(oldParent, 'bpmn:Collaboration')) { + + // do nothing if not necessary + return; + } + + if (is$1(shape, 'bpmn:DataStoreReference') && + shape.type !== 'label' && + is$1(parent, 'bpmn:Collaboration')) { + + var participant = is$1(oldParent, 'bpmn:Participant') ? + oldParent : + getAncestor(oldParent, 'bpmn:Participant'); + + updateDataStoreParent(shape, participant); + } + }); + + + // update data store parents on participant or subprocess deleted + this.postExecute('shape.delete', function(event) { + var context = event.context, + shape = context.shape, + rootElement = canvas.getRootElement(); + + if (isAny(shape, [ 'bpmn:Participant', 'bpmn:SubProcess' ]) + && is$1(rootElement, 'bpmn:Collaboration')) { + getDataStores(rootElement) + .filter(function(dataStore) { + return isDescendant(dataStore, shape); + }) + .forEach(function(dataStore) { + updateDataStoreParent(dataStore); + }); + } + }); + + // update data store parents on collaboration -> process + this.postExecute('canvas.updateRoot', function(event) { + var context = event.context, + oldRoot = context.oldRoot, + newRoot = context.newRoot; + + var dataStores = getDataStores(oldRoot); + + dataStores.forEach(function(dataStore) { + + if (is$1(newRoot, 'bpmn:Process')) { + updateDataStoreParent(dataStore, newRoot); + } + + }); + }); + } + + DataStoreBehavior.$inject = [ + 'canvas', + 'commandStack', + 'elementRegistry', + 'eventBus', + ]; + + e(DataStoreBehavior, CommandInterceptor); + + + // helpers ////////// + + function isDescendant(descendant, ancestor) { + var descendantBo = descendant.businessObject || descendant, + ancestorBo = ancestor.businessObject || ancestor; + + while (descendantBo.$parent) { + if (descendantBo.$parent === ancestorBo.processRef || ancestorBo) { + return true; + } + + descendantBo = descendantBo.$parent; + } + + return false; + } + + function getAncestor(element, type) { + + while (element.parent) { + if (is$1(element.parent, type)) { + return element.parent; + } + + element = element.parent; + } + } + + var LOW_PRIORITY$c = 500; + + + /** + * BPMN specific delete lane behavior + */ + function DeleteLaneBehavior(eventBus, modeling, spaceTool) { + + CommandInterceptor.call(this, eventBus); + + + function compensateLaneDelete(shape, oldParent) { + + var siblings = getChildLanes(oldParent); + + var topAffected = []; + var bottomAffected = []; + + eachElement(siblings, function(element) { + + if (element.y > shape.y) { + bottomAffected.push(element); + } else { + topAffected.push(element); + } + + return element.children; + }); + + if (!siblings.length) { + return; + } + + var offset; + + if (bottomAffected.length && topAffected.length) { + offset = shape.height / 2; + } else { + offset = shape.height; + } + + var topAdjustments, + bottomAdjustments; + + if (topAffected.length) { + topAdjustments = spaceTool.calculateAdjustments( + topAffected, 'y', offset, shape.y - 10); + + spaceTool.makeSpace( + topAdjustments.movingShapes, + topAdjustments.resizingShapes, + { x: 0, y: offset }, 's'); + } + + if (bottomAffected.length) { + bottomAdjustments = spaceTool.calculateAdjustments( + bottomAffected, 'y', -offset, shape.y + shape.height + 10); + + spaceTool.makeSpace( + bottomAdjustments.movingShapes, + bottomAdjustments.resizingShapes, + { x: 0, y: -offset }, 'n'); + } + } + + + /** + * Adjust sizes of other lanes after lane deletion + */ + this.postExecuted('shape.delete', LOW_PRIORITY$c, function(event) { + + var context = event.context, + hints = context.hints, + shape = context.shape, + oldParent = context.oldParent; + + // only compensate lane deletes + if (!is$1(shape, 'bpmn:Lane')) { + return; + } + + // compensate root deletes only + if (hints && hints.nested) { + return; + } + + compensateLaneDelete(shape, oldParent); + }); + } + + DeleteLaneBehavior.$inject = [ + 'eventBus', + 'modeling', + 'spaceTool' + ]; + + e(DeleteLaneBehavior, CommandInterceptor); + + var LOW_PRIORITY$b = 500; + + + /** + * Replace boundary event with intermediate event when creating or moving results in detached event. + */ + function DetachEventBehavior(bpmnReplace, injector) { + injector.invoke(CommandInterceptor, this); + + this._bpmnReplace = bpmnReplace; + + var self = this; + + this.postExecuted('elements.create', LOW_PRIORITY$b, function(context) { + var elements = context.elements; + + elements.filter(function(shape) { + var host = shape.host; + + return shouldReplace(shape, host); + }).map(function(shape) { + return elements.indexOf(shape); + }).forEach(function(index) { + context.elements[ index ] = self.replaceShape(elements[ index ]); + }); + }, true); + + this.preExecute('elements.move', LOW_PRIORITY$b, function(context) { + var shapes = context.shapes, + newHost = context.newHost; + + shapes.forEach(function(shape, index) { + var host = shape.host; + + if (shouldReplace(shape, includes$6(shapes, host) ? host : newHost)) { + shapes[ index ] = self.replaceShape(shape); + } + }); + }, true); + } + + DetachEventBehavior.$inject = [ + 'bpmnReplace', + 'injector' + ]; + + e(DetachEventBehavior, CommandInterceptor); + + DetachEventBehavior.prototype.replaceShape = function(shape) { + var eventDefinition = getEventDefinition(shape), + intermediateEvent; + + if (eventDefinition) { + intermediateEvent = { + type: 'bpmn:IntermediateCatchEvent', + eventDefinitionType: eventDefinition.$type + }; + } else { + intermediateEvent = { + type: 'bpmn:IntermediateThrowEvent' + }; + } + + return this._bpmnReplace.replaceElement(shape, intermediateEvent, { layoutConnection: false }); + }; + + + // helpers ////////// + + function getEventDefinition(element) { + var businessObject = getBusinessObject(element), + eventDefinitions = businessObject.eventDefinitions; + + return eventDefinitions && eventDefinitions[0]; + } + + function shouldReplace(shape, host) { + return !isLabel$6(shape) && is$1(shape, 'bpmn:BoundaryEvent') && !host; + } + + function includes$6(array, item) { + return array.indexOf(item) !== -1; + } + + function DropOnFlowBehavior(eventBus, bpmnRules, modeling) { + + CommandInterceptor.call(this, eventBus); + + /** + * Reconnect start / end of a connection after + * dropping an element on a flow. + */ + + function insertShape(shape, targetFlow, positionOrBounds) { + var waypoints = targetFlow.waypoints, + waypointsBefore, + waypointsAfter, + dockingPoint, + source, + target, + incomingConnection, + outgoingConnection, + oldOutgoing = shape.outgoing.slice(), + oldIncoming = shape.incoming.slice(); + + var mid; + + if (isNumber(positionOrBounds.width)) { + mid = getMid(positionOrBounds); + } else { + mid = positionOrBounds; + } + + var intersection = getApproxIntersection(waypoints, mid); + + if (intersection) { + waypointsBefore = waypoints.slice(0, intersection.index); + waypointsAfter = waypoints.slice(intersection.index + (intersection.bendpoint ? 1 : 0)); + + // due to inaccuracy intersection might have been found + if (!waypointsBefore.length || !waypointsAfter.length) { + return; + } + + dockingPoint = intersection.bendpoint ? waypoints[intersection.index] : mid; + + // if last waypointBefore is inside shape's bounds, ignore docking point + if (waypointsBefore.length === 1 || !isPointInsideBBox(shape, waypointsBefore[waypointsBefore.length - 1])) { + waypointsBefore.push(copy(dockingPoint)); + } + + // if first waypointAfter is inside shape's bounds, ignore docking point + if (waypointsAfter.length === 1 || !isPointInsideBBox(shape, waypointsAfter[0])) { + waypointsAfter.unshift(copy(dockingPoint)); + } + } + + source = targetFlow.source; + target = targetFlow.target; + + if (bpmnRules.canConnect(source, shape, targetFlow)) { + + // reconnect source -> inserted shape + modeling.reconnectEnd(targetFlow, shape, waypointsBefore || mid); + + incomingConnection = targetFlow; + } + + if (bpmnRules.canConnect(shape, target, targetFlow)) { + + if (!incomingConnection) { + + // reconnect inserted shape -> end + modeling.reconnectStart(targetFlow, shape, waypointsAfter || mid); + + outgoingConnection = targetFlow; + } else { + outgoingConnection = modeling.connect( + shape, target, { type: targetFlow.type, waypoints: waypointsAfter } + ); + } + } + + var duplicateConnections = [].concat( + + incomingConnection && filter(oldIncoming, function(connection) { + return connection.source === incomingConnection.source; + }) || [], + + outgoingConnection && filter(oldOutgoing, function(connection) { + return connection.target === outgoingConnection.target; + }) || [] + ); + + if (duplicateConnections.length) { + modeling.removeElements(duplicateConnections); + } + } + + this.preExecute('elements.move', function(context) { + + var newParent = context.newParent, + shapes = context.shapes, + delta = context.delta, + shape = shapes[0]; + + if (!shape || !newParent) { + return; + } + + // if the new parent is a connection, + // change it to the new parent's parent + if (newParent && newParent.waypoints) { + context.newParent = newParent = newParent.parent; + } + + var shapeMid = getMid(shape); + var newShapeMid = { + x: shapeMid.x + delta.x, + y: shapeMid.y + delta.y + }; + + // find a connection which intersects with the + // element's mid point + var connection = find(newParent.children, function(element) { + var canInsert = bpmnRules.canInsert(shapes, element); + + return canInsert && getApproxIntersection(element.waypoints, newShapeMid); + }); + + if (connection) { + context.targetFlow = connection; + context.position = newShapeMid; + } + + }, true); + + this.postExecuted('elements.move', function(context) { + + var shapes = context.shapes, + targetFlow = context.targetFlow, + position = context.position; + + if (targetFlow) { + insertShape(shapes[0], targetFlow, position); + } + + }, true); + + this.preExecute('shape.create', function(context) { + + var parent = context.parent, + shape = context.shape; + + if (bpmnRules.canInsert(shape, parent)) { + context.targetFlow = parent; + context.parent = parent.parent; + } + }, true); + + this.postExecuted('shape.create', function(context) { + + var shape = context.shape, + targetFlow = context.targetFlow, + positionOrBounds = context.position; + + if (targetFlow) { + insertShape(shape, targetFlow, positionOrBounds); + } + }, true); + } + + e(DropOnFlowBehavior, CommandInterceptor); + + DropOnFlowBehavior.$inject = [ + 'eventBus', + 'bpmnRules', + 'modeling' + ]; + + + // helpers ///////////////////// + + function isPointInsideBBox(bbox, point) { + var x = point.x, + y = point.y; + + return x >= bbox.x && + x <= bbox.x + bbox.width && + y >= bbox.y && + y <= bbox.y + bbox.height; + } + + function copy(obj) { + return assign({}, obj); + } + + function EventBasedGatewayBehavior(eventBus, modeling) { + + CommandInterceptor.call(this, eventBus); + + /** + * Remove existing sequence flows of event-based target before connecting + * from event-based gateway. + */ + this.preExecuted('connection.create', function(event) { + + var context = event.context, + source = context.source, + target = context.target, + existingIncomingConnections = target.incoming.slice(); + + if (context.hints && context.hints.createElementsBehavior === false) { + return; + } + + if ( + is$1(source, 'bpmn:EventBasedGateway') && + target.incoming.length + ) { + + existingIncomingConnections.filter(isSequenceFlow) + .forEach(function(sequenceFlow) { + modeling.removeConnection(sequenceFlow); + }); + } + }); + + /** + * After replacing shape with event-based gateway, remove incoming sequence + * flows of event-based targets which do not belong to event-based gateway + * source. + */ + this.preExecuted('shape.replace', function(event) { + + var newShape = event.context.newShape, + newShapeTargets, + newShapeTargetsIncomingSequenceFlows; + + if (!is$1(newShape, 'bpmn:EventBasedGateway')) { + return; + } + + newShapeTargets = newShape.outgoing.filter(isSequenceFlow) + .map(function(sequenceFlow) { + return sequenceFlow.target; + }); + + newShapeTargetsIncomingSequenceFlows = newShapeTargets.reduce(function(sequenceFlows, target) { + var incomingSequenceFlows = target.incoming.filter(isSequenceFlow); + + return sequenceFlows.concat(incomingSequenceFlows); + }, []); + + newShapeTargetsIncomingSequenceFlows.forEach(function(sequenceFlow) { + if (sequenceFlow.source !== newShape) { + modeling.removeConnection(sequenceFlow); + } + }); + }); + } + + EventBasedGatewayBehavior.$inject = [ + 'eventBus', + 'modeling' + ]; + + e(EventBasedGatewayBehavior, CommandInterceptor); + + + + // helpers ////////////////////// + + function isSequenceFlow(connection) { + return is$1(connection, 'bpmn:SequenceFlow'); + } + + var HIGH_PRIORITY$a = 1500; + var HIGHEST_PRIORITY = 2000; + + + /** + * Correct hover targets in certain situations to improve diagram interaction. + * + * @param {ElementRegistry} elementRegistry + * @param {EventBus} eventBus + * @param {Canvas} canvas + */ + function FixHoverBehavior(elementRegistry, eventBus, canvas) { + + eventBus.on([ + 'create.hover', + 'create.move', + 'create.out', + 'create.end', + 'shape.move.hover', + 'shape.move.move', + 'shape.move.out', + 'shape.move.end' + ], HIGH_PRIORITY$a, function(event) { + var context = event.context, + shape = context.shape || event.shape, + hover = event.hover; + + // ensure elements are not dropped onto a bpmn:Lane but onto + // the underlying bpmn:Participant + if (is$1(hover, 'bpmn:Lane') && !isAny(shape, [ 'bpmn:Lane', 'bpmn:Participant' ])) { + event.hover = getLanesRoot(hover); + event.hoverGfx = elementRegistry.getGraphics(event.hover); + } + + var rootElement = canvas.getRootElement(); + + // ensure bpmn:Group and label elements are dropped + // always onto the root + if (hover !== rootElement && (shape.labelTarget || is$1(shape, 'bpmn:Group'))) { + event.hover = rootElement; + event.hoverGfx = elementRegistry.getGraphics(event.hover); + } + }); + + eventBus.on([ + 'connect.hover', + 'connect.out', + 'connect.end', + 'connect.cleanup', + 'global-connect.hover', + 'global-connect.out', + 'global-connect.end', + 'global-connect.cleanup' + ], HIGH_PRIORITY$a, function(event) { + var hover = event.hover; + + // ensure connections start/end on bpmn:Participant, + // not the underlying bpmn:Lane + if (is$1(hover, 'bpmn:Lane')) { + event.hover = getLanesRoot(hover) || hover; + event.hoverGfx = elementRegistry.getGraphics(event.hover); + } + }); + + + eventBus.on([ + 'bendpoint.move.hover' + ], HIGH_PRIORITY$a, function(event) { + var context = event.context, + hover = event.hover, + type = context.type; + + // ensure reconnect start/end on bpmn:Participant, + // not the underlying bpmn:Lane + if (is$1(hover, 'bpmn:Lane') && /reconnect/.test(type)) { + event.hover = getLanesRoot(hover) || hover; + event.hoverGfx = elementRegistry.getGraphics(event.hover); + } + }); + + + eventBus.on([ + 'connect.start' + ], HIGH_PRIORITY$a, function(event) { + var context = event.context, + start = context.start; + + // ensure connect start on bpmn:Participant, + // not the underlying bpmn:Lane + if (is$1(start, 'bpmn:Lane')) { + context.start = getLanesRoot(start) || start; + } + }); + + + // allow movement of participants from lanes + eventBus.on('shape.move.start', HIGHEST_PRIORITY, function(event) { + var shape = event.shape; + + if (is$1(shape, 'bpmn:Lane')) { + event.shape = getLanesRoot(shape) || shape; + } + }); + + } + + FixHoverBehavior.$inject = [ + 'elementRegistry', + 'eventBus', + 'canvas' + ]; + + /** + * Creates a new bpmn:CategoryValue inside a new bpmn:Category + * + * @param {BpmnFactory} bpmnFactory + * + * @return {ModdleElement} categoryValue. + */ + function createCategory(bpmnFactory) { + return bpmnFactory.create('bpmn:Category'); + } + + /** + * Creates a new bpmn:CategoryValue inside a new bpmn:Category + * + * @param {BpmnFactory} bpmnFactory + * + * @return {ModdleElement} categoryValue. + */ + function createCategoryValue(bpmnFactory) { + return bpmnFactory.create('bpmn:CategoryValue'); + } + + /** + * Adds category value to definitions + * + * @param {ModdleElement} categoryValue + * @param {ModdleElement} category + * @param {ModdleElement} definitions + * + * @return {ModdleElement} categoryValue + */ + function linkCategoryValue(categoryValue, category, definitions) { + add(category.get('categoryValue'), categoryValue); + categoryValue.$parent = category; + + add(definitions.get('rootElements'), category); + category.$parent = definitions; + + return categoryValue; + } + + /** + * Unlink category value from parent + * + * @param {ModdleElement} categoryValue + * + * @return {ModdleElement} categoryValue + */ + function unlinkCategoryValue(categoryValue) { + var category = categoryValue.$parent; + + if (category) { + remove(category.get('categoryValue'), categoryValue); + categoryValue.$parent = null; + } + + return categoryValue; + } + + /** + * Unlink category from parent + * + * @param {ModdleElement} category + * @param {ModdleElement} definitions + * + * @return {ModdleElement} categoryValue + */ + function unlinkCategory(category) { + var definitions = category.$parent; + + if (definitions) { + remove(definitions.get('rootElements'), category); + category.$parent = null; + } + + return category; + } + + var LOWER_PRIORITY = 770; + + + /** + * BPMN specific Group behavior + */ + function GroupBehavior( + bpmnFactory, + bpmnjs, + elementRegistry, + eventBus, + injector, + moddleCopy + ) { + injector.invoke(CommandInterceptor, this); + + /** + * Returns all group element in the current registry + * + * @return {Array} a list of group shapes + */ + function getGroupElements() { + return elementRegistry.filter(function(e) { + return is$1(e, 'bpmn:Group'); + }); + } + + /** + * Returns true if given category is referenced in one of the given elements + * + * @param { djs.model.Element[] } elements + * @param { ModdleElement } category + * + * @return { boolean } + */ + function isReferencedCategory(elements, category) { + return elements.some(function(element) { + var businessObject = getBusinessObject(element); + + var _category = businessObject.categoryValueRef && businessObject.categoryValueRef.$parent; + + return _category === category; + }); + } + + /** + * Returns true if given categoryValue is referenced in one of the given elements + * + * @param { djs.model.Element[] } elements + * @param { ModdleElement } categoryValue + * + * @return { boolean } + */ + function isReferencedCategoryValue(elements, categoryValue) { + return elements.some(function(element) { + var businessObject = getBusinessObject(element); + + return businessObject.categoryValueRef === categoryValue; + }); + } + + /** + * Remove category value unless it is still referenced + * + * @param {ModdleElement} categoryValue + * @param {ModdleElement} category + * @param {ModdleElement} businessObject + */ + function removeCategoryValue(categoryValue, category, businessObject) { + + var groups = getGroupElements().filter(function(element) { + return element.businessObject !== businessObject; + }); + + if (category && !isReferencedCategory(groups, category)) { + unlinkCategory(category); + } + + if (categoryValue && !isReferencedCategoryValue(groups, categoryValue)) { + unlinkCategoryValue(categoryValue); + } + } + + /** + * Add category value + * + * @param {ModdleElement} categoryValue + * @param {ModdleElement} category + */ + function addCategoryValue(categoryValue, category) { + return linkCategoryValue(categoryValue, category, bpmnjs.getDefinitions()); + } + + function setCategoryValue(element, context) { + var businessObject = getBusinessObject(element), + categoryValue = businessObject.categoryValueRef; + + if (!categoryValue) { + categoryValue = + businessObject.categoryValueRef = + context.categoryValue = ( + context.categoryValue || createCategoryValue(bpmnFactory) + ); + } + + var category = categoryValue.$parent; + + if (!category) { + category = + categoryValue.$parent = + context.category = ( + context.category || createCategory(bpmnFactory) + ); + } + + addCategoryValue(categoryValue, category, bpmnjs.getDefinitions()); + } + + function unsetCategoryValue(element, context) { + var category = context.category, + categoryValue = context.categoryValue, + businessObject = getBusinessObject(element); + + if (categoryValue) { + businessObject.categoryValueRef = null; + + removeCategoryValue(categoryValue, category, businessObject); + } else { + removeCategoryValue(null, businessObject.categoryValueRef.$parent, businessObject); + } + } + + + // ensure category + value exist before label editing + + this.execute('label.create', function(event) { + var context = event.context, + labelTarget = context.labelTarget; + + if (!is$1(labelTarget, 'bpmn:Group')) { + return; + } + + setCategoryValue(labelTarget, context); + }); + + this.revert('label.create', function(event) { + var context = event.context, + labelTarget = context.labelTarget; + + if (!is$1(labelTarget, 'bpmn:Group')) { + return; + } + + unsetCategoryValue(labelTarget, context); + }); + + + // remove referenced category + value when group was deleted + + this.execute('shape.delete', function(event) { + + var context = event.context, + shape = context.shape, + businessObject = getBusinessObject(shape); + + if (!is$1(shape, 'bpmn:Group') || shape.labelTarget) { + return; + } + + var categoryValue = context.categoryValue = businessObject.categoryValueRef, + category; + + if (categoryValue) { + category = context.category = categoryValue.$parent; + + removeCategoryValue(categoryValue, category, businessObject); + + businessObject.categoryValueRef = null; + } + }); + + this.reverted('shape.delete', function(event) { + + var context = event.context, + shape = context.shape; + + if (!is$1(shape, 'bpmn:Group') || shape.labelTarget) { + return; + } + + var category = context.category, + categoryValue = context.categoryValue, + businessObject = getBusinessObject(shape); + + if (categoryValue) { + businessObject.categoryValueRef = categoryValue; + + addCategoryValue(categoryValue, category); + } + }); + + + // create new category + value when group was created + + this.execute('shape.create', function(event) { + var context = event.context, + shape = context.shape; + + if (!is$1(shape, 'bpmn:Group') || shape.labelTarget) { + return; + } + + if (getBusinessObject(shape).categoryValueRef) { + setCategoryValue(shape, context); + } + }); + + this.reverted('shape.create', function(event) { + + var context = event.context, + shape = context.shape; + + if (!is$1(shape, 'bpmn:Group') || shape.labelTarget) { + return; + } + + if (getBusinessObject(shape).categoryValueRef) { + unsetCategoryValue(shape, context); + } + }); + + + // copy + paste categoryValueRef with group + + function copy(bo, clone) { + var targetBo = bpmnFactory.create(bo.$type); + + return moddleCopy.copyElement(bo, targetBo, null, clone); + } + + eventBus.on('copyPaste.copyElement', LOWER_PRIORITY, function(context) { + var descriptor = context.descriptor, + element = context.element; + + if (!is$1(element, 'bpmn:Group') || element.labelTarget) { + return; + } + + var groupBo = getBusinessObject(element); + + if (groupBo.categoryValueRef) { + + var categoryValue = groupBo.categoryValueRef; + + descriptor.categoryValue = copy(categoryValue, true); + + if (categoryValue.$parent) { + descriptor.category = copy(categoryValue.$parent, true); + } + } + }); + + eventBus.on('copyPaste.pasteElement', LOWER_PRIORITY, function(context) { + var descriptor = context.descriptor, + businessObject = descriptor.businessObject, + categoryValue = descriptor.categoryValue, + category = descriptor.category; + + if (categoryValue) { + categoryValue = businessObject.categoryValueRef = copy(categoryValue); + } + + if (category) { + categoryValue.$parent = copy(category); + } + + delete descriptor.category; + delete descriptor.categoryValue; + }); + + } + + GroupBehavior.$inject = [ + 'bpmnFactory', + 'bpmnjs', + 'elementRegistry', + 'eventBus', + 'injector', + 'moddleCopy' + ]; + + e(GroupBehavior, CommandInterceptor); + + /** + * Returns the intersection between two line segments a and b. + * + * @param {Point} l1s + * @param {Point} l1e + * @param {Point} l2s + * @param {Point} l2e + * + * @return {Point} + */ + function lineIntersect(l1s, l1e, l2s, l2e) { + + // if the lines intersect, the result contains the x and y of the + // intersection (treating the lines as infinite) and booleans for + // whether line segment 1 or line segment 2 contain the point + var denominator, a, b, c, numerator; + + denominator = ((l2e.y - l2s.y) * (l1e.x - l1s.x)) - ((l2e.x - l2s.x) * (l1e.y - l1s.y)); + + if (denominator == 0) { + return null; + } + + a = l1s.y - l2s.y; + b = l1s.x - l2s.x; + numerator = ((l2e.x - l2s.x) * a) - ((l2e.y - l2s.y) * b); + + c = numerator / denominator; + + // if we cast these lines infinitely in + // both directions, they intersect here + return { + x: Math.round(l1s.x + (c * (l1e.x - l1s.x))), + y: Math.round(l1s.y + (c * (l1e.y - l1s.y))) + }; + } + + /** + * Fix broken dockings after DI imports. + * + * @param {EventBus} eventBus + */ + function ImportDockingFix(eventBus) { + + function adjustDocking(startPoint, nextPoint, elementMid) { + + var elementTop = { + x: elementMid.x, + y: elementMid.y - 50 + }; + + var elementLeft = { + x: elementMid.x - 50, + y: elementMid.y + }; + + var verticalIntersect = lineIntersect(startPoint, nextPoint, elementMid, elementTop), + horizontalIntersect = lineIntersect(startPoint, nextPoint, elementMid, elementLeft); + + // original is horizontal or vertical center cross intersection + var centerIntersect; + + if (verticalIntersect && horizontalIntersect) { + if (getDistance$1(verticalIntersect, elementMid) > getDistance$1(horizontalIntersect, elementMid)) { + centerIntersect = horizontalIntersect; + } else { + centerIntersect = verticalIntersect; + } + } else { + centerIntersect = verticalIntersect || horizontalIntersect; + } + + startPoint.original = centerIntersect; + } + + function fixDockings(connection) { + var waypoints = connection.waypoints; + + adjustDocking( + waypoints[0], + waypoints[1], + getMid(connection.source) + ); + + adjustDocking( + waypoints[waypoints.length - 1], + waypoints[waypoints.length - 2], + getMid(connection.target) + ); + } + + eventBus.on('bpmnElement.added', function(e) { + + var element = e.element; + + if (element.waypoints) { + fixDockings(element); + } + }); + } + + ImportDockingFix.$inject = [ + 'eventBus' + ]; + + + // helpers ////////////////////// + + function getDistance$1(p1, p2) { + return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)); + } + + /** + * A component that makes sure that each created or updated + * Pool and Lane is assigned an isHorizontal property set to true. + * + * @param {EventBus} eventBus + */ + function IsHorizontalFix(eventBus) { + + CommandInterceptor.call(this, eventBus); + + var elementTypesToUpdate = [ + 'bpmn:Participant', + 'bpmn:Lane' + ]; + + this.executed([ 'shape.move', 'shape.create', 'shape.resize' ], function(event) { + var shape = event.context.shape, + bo = getBusinessObject(shape), + di = getDi(shape); + + if (isAny(bo, elementTypesToUpdate) && !di.get('isHorizontal')) { + + // set attribute directly to avoid modeling#updateProperty side effects + di.set('isHorizontal', true); + } + }); + + } + + IsHorizontalFix.$inject = [ 'eventBus' ]; + + e(IsHorizontalFix, CommandInterceptor); + + var sqrt = Math.sqrt, + min$1 = Math.min, + max$3 = Math.max, + abs$3 = Math.abs; + + /** + * Calculate the square (power to two) of a number. + * + * @param {number} n + * + * @return {number} + */ + function sq(n) { + return Math.pow(n, 2); + } + + /** + * Get distance between two points. + * + * @param {Point} p1 + * @param {Point} p2 + * + * @return {number} + */ + function getDistance(p1, p2) { + return sqrt(sq(p1.x - p2.x) + sq(p1.y - p2.y)); + } + + /** + * Return the attachment of the given point on the specified line. + * + * The attachment is either a bendpoint (attached to the given point) + * or segment (attached to a location on a line segment) attachment: + * + * ```javascript + * var pointAttachment = { + * type: 'bendpoint', + * bendpointIndex: 3, + * position: { x: 10, y: 10 } // the attach point on the line + * }; + * + * var segmentAttachment = { + * type: 'segment', + * segmentIndex: 2, + * relativeLocation: 0.31, // attach point location between 0 (at start) and 1 (at end) + * position: { x: 10, y: 10 } // the attach point on the line + * }; + * ``` + * + * @param {Point} point + * @param {Array} line + * + * @return {Object} attachment + */ + function getAttachment(point, line) { + + var idx = 0, + segmentStart, + segmentEnd, + segmentStartDistance, + segmentEndDistance, + attachmentPosition, + minDistance, + intersections, + attachment, + attachmentDistance, + closestAttachmentDistance, + closestAttachment; + + for (idx = 0; idx < line.length - 1; idx++) { + + segmentStart = line[idx]; + segmentEnd = line[idx + 1]; + + if (pointsEqual(segmentStart, segmentEnd)) { + intersections = [ segmentStart ]; + } else { + segmentStartDistance = getDistance(point, segmentStart); + segmentEndDistance = getDistance(point, segmentEnd); + + minDistance = min$1(segmentStartDistance, segmentEndDistance); + + intersections = getCircleSegmentIntersections(segmentStart, segmentEnd, point, minDistance); + } + + if (intersections.length < 1) { + throw new Error('expected between [1, 2] circle -> line intersections'); + } + + // one intersection -> bendpoint attachment + if (intersections.length === 1) { + attachment = { + type: 'bendpoint', + position: intersections[0], + segmentIndex: idx, + bendpointIndex: pointsEqual(segmentStart, intersections[0]) ? idx : idx + 1 + }; + } + + // two intersections -> segment attachment + if (intersections.length === 2) { + + attachmentPosition = mid$1(intersections[0], intersections[1]); + + attachment = { + type: 'segment', + position: attachmentPosition, + segmentIndex: idx, + relativeLocation: getDistance(segmentStart, attachmentPosition) / getDistance(segmentStart, segmentEnd) + }; + } + + attachmentDistance = getDistance(attachment.position, point); + + if (!closestAttachment || closestAttachmentDistance > attachmentDistance) { + closestAttachment = attachment; + closestAttachmentDistance = attachmentDistance; + } + } + + return closestAttachment; + } + + /** + * Gets the intersection between a circle and a line segment. + * + * @param {Point} s1 segment start + * @param {Point} s2 segment end + * @param {Point} cc circle center + * @param {number} cr circle radius + * + * @return {Array} intersections + */ + function getCircleSegmentIntersections(s1, s2, cc, cr) { + + var baX = s2.x - s1.x; + var baY = s2.y - s1.y; + var caX = cc.x - s1.x; + var caY = cc.y - s1.y; + + var a = baX * baX + baY * baY; + var bBy2 = baX * caX + baY * caY; + var c = caX * caX + caY * caY - cr * cr; + + var pBy2 = bBy2 / a; + var q = c / a; + + var disc = pBy2 * pBy2 - q; + + // check against negative value to work around + // negative, very close to zero results (-4e-15) + // being produced in some environments + if (disc < 0 && disc > -0.000001) { + disc = 0; + } + + if (disc < 0) { + return []; + } + + // if disc == 0 ... dealt with later + var tmpSqrt = sqrt(disc); + var abScalingFactor1 = -pBy2 + tmpSqrt; + var abScalingFactor2 = -pBy2 - tmpSqrt; + + var i1 = { + x: s1.x - baX * abScalingFactor1, + y: s1.y - baY * abScalingFactor1 + }; + + if (disc === 0) { // abScalingFactor1 == abScalingFactor2 + return [ i1 ]; + } + + var i2 = { + x: s1.x - baX * abScalingFactor2, + y: s1.y - baY * abScalingFactor2 + }; + + // return only points on line segment + return [ i1, i2 ].filter(function(p) { + return isPointInSegment(p, s1, s2); + }); + } + + + function isPointInSegment(p, segmentStart, segmentEnd) { + return ( + fenced(p.x, segmentStart.x, segmentEnd.x) && + fenced(p.y, segmentStart.y, segmentEnd.y) + ); + } + + function fenced(n, rangeStart, rangeEnd) { + + // use matching threshold to work around + // precision errors in intersection computation + + return ( + n >= min$1(rangeStart, rangeEnd) - EQUAL_THRESHOLD && + n <= max$3(rangeStart, rangeEnd) + EQUAL_THRESHOLD + ); + } + + /** + * Calculate mid of two points. + * + * @param {Point} p1 + * @param {Point} p2 + * + * @return {Point} + */ + function mid$1(p1, p2) { + + return { + x: (p1.x + p2.x) / 2, + y: (p1.y + p2.y) / 2 + }; + } + + var EQUAL_THRESHOLD = 0.1; + + function pointsEqual(p1, p2) { + + return ( + abs$3(p1.x - p2.x) <= EQUAL_THRESHOLD && + abs$3(p1.y - p2.y) <= EQUAL_THRESHOLD + ); + } + + function findNewLineStartIndex(oldWaypoints, newWaypoints, attachment, hints) { + + var index = attachment.segmentIndex; + + var offset = newWaypoints.length - oldWaypoints.length; + + // segmentMove happened + if (hints.segmentMove) { + + var oldSegmentStartIndex = hints.segmentMove.segmentStartIndex, + newSegmentStartIndex = hints.segmentMove.newSegmentStartIndex; + + // if point was on moved segment return new segment index + if (index === oldSegmentStartIndex) { + return newSegmentStartIndex; + } + + // point is after new segment index + if (index >= newSegmentStartIndex) { + return (index + offset < newSegmentStartIndex) ? newSegmentStartIndex : index + offset; + } + + // if point is before new segment index + return index; + } + + // bendpointMove happened + if (hints.bendpointMove) { + + var insert = hints.bendpointMove.insert, + bendpointIndex = hints.bendpointMove.bendpointIndex, + newIndex; + + // waypoints length didnt change + if (offset === 0) { + return index; + } + + // point behind new/removed bendpoint + if (index >= bendpointIndex) { + newIndex = insert ? index + 1 : index - 1; + } + + // point before new/removed bendpoint + if (index < bendpointIndex) { + + newIndex = index; + + // decide point should take right or left segment + if (insert && attachment.type !== 'bendpoint' && bendpointIndex - 1 === index) { + + var rel = relativePositionMidWaypoint(newWaypoints, bendpointIndex); + + if (rel < attachment.relativeLocation) { + newIndex++; + } + } + } + + return newIndex; + } + + // start/end changed + if (offset === 0) { + return index; + } + + if (hints.connectionStart && index === 0) { + return 0; + } + + if (hints.connectionEnd && index === oldWaypoints.length - 2) { + return newWaypoints.length - 2; + } + + // if nothing fits, take the middle segment + return Math.floor((newWaypoints.length - 2) / 2); + } + + + /** + * Calculate the required adjustment (move delta) for the given point + * after the connection waypoints got updated. + * + * @param {Point} position + * @param {Array} newWaypoints + * @param {Array} oldWaypoints + * @param {Object} hints + * + * @return {Object} result + * @return {Point} result.point + * @return {Point} result.delta + */ + function getAnchorPointAdjustment(position, newWaypoints, oldWaypoints, hints) { + + var dx = 0, + dy = 0; + + var oldPosition = { + point: position, + delta: { x: 0, y: 0 } + }; + + // get closest attachment + var attachment = getAttachment(position, oldWaypoints), + oldLabelLineIndex = attachment.segmentIndex, + newLabelLineIndex = findNewLineStartIndex(oldWaypoints, newWaypoints, attachment, hints); + + + // should never happen + // TODO(@janstuemmel): throw an error here when connectionSegmentMove is refactored + if (newLabelLineIndex < 0 || + newLabelLineIndex > newWaypoints.length - 2 || + newLabelLineIndex === null) { + return oldPosition; + } + + var oldLabelLine = getLine(oldWaypoints, oldLabelLineIndex), + newLabelLine = getLine(newWaypoints, newLabelLineIndex), + oldFoot = attachment.position; + + var relativeFootPosition = getRelativeFootPosition(oldLabelLine, oldFoot), + angleDelta = getAngleDelta(oldLabelLine, newLabelLine); + + // special rule if label on bendpoint + if (attachment.type === 'bendpoint') { + + var offset = newWaypoints.length - oldWaypoints.length, + oldBendpointIndex = attachment.bendpointIndex, + oldBendpoint = oldWaypoints[oldBendpointIndex]; + + // bendpoint position hasn't changed, return same position + if (newWaypoints.indexOf(oldBendpoint) !== -1) { + return oldPosition; + } + + // new bendpoint and old bendpoint have same index, then just return the offset + if (offset === 0) { + var newBendpoint = newWaypoints[oldBendpointIndex]; + + dx = newBendpoint.x - attachment.position.x, + dy = newBendpoint.y - attachment.position.y; + + return { + delta: { + x: dx, + y: dy + }, + point: { + x: position.x + dx, + y: position.y + dy + } + }; + } + + // if bendpoints get removed + if (offset < 0 && oldBendpointIndex !== 0 && oldBendpointIndex < oldWaypoints.length - 1) { + relativeFootPosition = relativePositionMidWaypoint(oldWaypoints, oldBendpointIndex); + } + } + + var newFoot = { + x: (newLabelLine[1].x - newLabelLine[0].x) * relativeFootPosition + newLabelLine[0].x, + y: (newLabelLine[1].y - newLabelLine[0].y) * relativeFootPosition + newLabelLine[0].y + }; + + // the rotated vector to label + var newLabelVector = rotateVector({ + x: position.x - oldFoot.x, + y: position.y - oldFoot.y + }, angleDelta); + + // the new relative position + dx = newFoot.x + newLabelVector.x - position.x; + dy = newFoot.y + newLabelVector.y - position.y; + + return { + point: roundPoint(newFoot), + delta: roundPoint({ + x: dx, + y: dy + }) + }; + } + + + // HELPERS ////////////////////// + + function relativePositionMidWaypoint(waypoints, idx) { + + var distanceSegment1 = getDistancePointPoint(waypoints[idx - 1], waypoints[idx]), + distanceSegment2 = getDistancePointPoint(waypoints[idx], waypoints[idx + 1]); + + var relativePosition = distanceSegment1 / (distanceSegment1 + distanceSegment2); + + return relativePosition; + } + + function getAngleDelta(l1, l2) { + var a1 = getAngle(l1), + a2 = getAngle(l2); + return a2 - a1; + } + + function getLine(waypoints, idx) { + return [ waypoints[idx], waypoints[idx + 1] ]; + } + + function getRelativeFootPosition(line, foot) { + + var length = getDistancePointPoint(line[0], line[1]), + lengthToFoot = getDistancePointPoint(line[0], foot); + + return length === 0 ? 0 : lengthToFoot / length; + } + + /** + * Calculate the required adjustment (move delta) for the given label + * after the connection waypoints got updated. + * + * @param {djs.model.Label} label + * @param {Array} newWaypoints + * @param {Array} oldWaypoints + * @param {Object} hints + * + * @return {Point} delta + */ + function getLabelAdjustment(label, newWaypoints, oldWaypoints, hints) { + var labelPosition = getLabelMid(label); + + return getAnchorPointAdjustment(labelPosition, newWaypoints, oldWaypoints, hints).delta; + } + + + // HELPERS ////////////////////// + + function getLabelMid(label) { + return { + x: label.x + label.width / 2, + y: label.y + label.height / 2 + }; + } + + /** + * Calculates the absolute point relative to the new element's position + * + * @param {point} point [absolute] + * @param {bounds} oldBounds + * @param {bounds} newBounds + * + * @return {point} point [absolute] + */ + function getNewAttachPoint(point, oldBounds, newBounds) { + var oldCenter = center(oldBounds), + newCenter = center(newBounds), + oldDelta = delta(point, oldCenter); + + var newDelta = { + x: oldDelta.x * (newBounds.width / oldBounds.width), + y: oldDelta.y * (newBounds.height / oldBounds.height) + }; + + return roundPoint({ + x: newCenter.x + newDelta.x, + y: newCenter.y + newDelta.y + }); + } + + + /** + * Calculates the shape's delta relative to a new position + * of a certain element's bounds + * + * @param {djs.model.Shape} point [absolute] + * @param {bounds} oldBounds + * @param {bounds} newBounds + * + * @return {delta} delta + */ + function getNewAttachShapeDelta(shape, oldBounds, newBounds) { + var shapeCenter = center(shape), + oldCenter = center(oldBounds), + newCenter = center(newBounds), + shapeDelta = delta(shape, shapeCenter), + oldCenterDelta = delta(shapeCenter, oldCenter), + stickyPositionDelta = getStickyPositionDelta(shapeCenter, oldBounds, newBounds); + + if (stickyPositionDelta) { + return stickyPositionDelta; + } + + var newCenterDelta = { + x: oldCenterDelta.x * (newBounds.width / oldBounds.width), + y: oldCenterDelta.y * (newBounds.height / oldBounds.height) + }; + + var newShapeCenter = { + x: newCenter.x + newCenterDelta.x, + y: newCenter.y + newCenterDelta.y + }; + + return roundPoint({ + x: newShapeCenter.x + shapeDelta.x - shape.x, + y: newShapeCenter.y + shapeDelta.y - shape.y + }); + } + + function getStickyPositionDelta(oldShapeCenter, oldBounds, newBounds) { + var oldTRBL = asTRBL(oldBounds), + newTRBL = asTRBL(newBounds); + + if (isMoved(oldTRBL, newTRBL)) { + return null; + } + + var oldOrientation = getOrientation(oldBounds, oldShapeCenter), + stickyPositionDelta, + newShapeCenter, + newOrientation; + + if (oldOrientation === 'top') { + stickyPositionDelta = { + x: 0, + y: newTRBL.bottom - oldTRBL.bottom + }; + } else if (oldOrientation === 'bottom') { + stickyPositionDelta = { + x: 0, + y: newTRBL.top - oldTRBL.top + }; + } else if (oldOrientation === 'right') { + stickyPositionDelta = { + x: newTRBL.left - oldTRBL.left, + y: 0 + }; + } else if (oldOrientation === 'left') { + stickyPositionDelta = { + x: newTRBL.right - oldTRBL.right, + y: 0 + }; + } else { + + // fallback to proportional movement for corner-placed attachments + return null; + } + + newShapeCenter = { + x: oldShapeCenter.x + stickyPositionDelta.x, + y: oldShapeCenter.y + stickyPositionDelta.y + }; + + newOrientation = getOrientation(newBounds, newShapeCenter); + + if (newOrientation !== oldOrientation) { + + // fallback to proportional movement if orientation would otherwise change + return null; + } + + return stickyPositionDelta; + } + + function isMoved(oldTRBL, newTRBL) { + return isHorizontallyMoved(oldTRBL, newTRBL) || isVerticallyMoved(oldTRBL, newTRBL); + } + + function isHorizontallyMoved(oldTRBL, newTRBL) { + return oldTRBL.right !== newTRBL.right && oldTRBL.left !== newTRBL.left; + } + + function isVerticallyMoved(oldTRBL, newTRBL) { + return oldTRBL.top !== newTRBL.top && oldTRBL.bottom !== newTRBL.bottom; + } + + var DEFAULT_LABEL_DIMENSIONS = { + width: 90, + height: 20 + }; + + var NAME_PROPERTY = 'name'; + var TEXT_PROPERTY = 'text'; + + /** + * A component that makes sure that external labels are added + * together with respective elements and properly updated (DI wise) + * during move. + * + * @param {EventBus} eventBus + * @param {Modeling} modeling + * @param {BpmnFactory} bpmnFactory + * @param {TextRenderer} textRenderer + */ + function LabelBehavior( + eventBus, modeling, bpmnFactory, + textRenderer) { + + CommandInterceptor.call(this, eventBus); + + // update label if name property was updated + this.postExecute('element.updateProperties', function(e) { + var context = e.context, + element = context.element, + properties = context.properties; + + if (NAME_PROPERTY in properties) { + modeling.updateLabel(element, properties[NAME_PROPERTY]); + } + + if (TEXT_PROPERTY in properties + && is$1(element, 'bpmn:TextAnnotation')) { + + var newBounds = textRenderer.getTextAnnotationBounds( + { + x: element.x, + y: element.y, + width: element.width, + height: element.height + }, + properties[TEXT_PROPERTY] || '' + ); + + modeling.updateLabel(element, properties.text, newBounds); + } + }); + + // create label shape after shape/connection was created + this.postExecute([ 'shape.create', 'connection.create' ], function(e) { + var context = e.context, + hints = context.hints || {}; + + if (hints.createElementsBehavior === false) { + return; + } + + var element = context.shape || context.connection, + businessObject = element.businessObject; + + if (isLabel$6(element) || !isLabelExternal(element)) { + return; + } + + // only create label if attribute available + if (!getLabel(element)) { + return; + } + + var labelCenter = getExternalLabelMid(element); + + // we don't care about x and y + var labelDimensions = textRenderer.getExternalLabelBounds( + DEFAULT_LABEL_DIMENSIONS, + getLabel(element) + ); + + modeling.createLabel(element, labelCenter, { + id: businessObject.id + '_label', + businessObject: businessObject, + width: labelDimensions.width, + height: labelDimensions.height + }); + }); + + // update label after label shape was deleted + this.postExecute('shape.delete', function(event) { + var context = event.context, + labelTarget = context.labelTarget, + hints = context.hints || {}; + + // check if label + if (labelTarget && hints.unsetLabel !== false) { + modeling.updateLabel(labelTarget, null, null, { removeShape: false }); + } + }); + + // update di information on label creation + this.postExecute([ 'label.create' ], function(event) { + + var context = event.context, + element = context.shape, + labelTarget = context.labelTarget, + di; + + // we want to trigger on real labels only + if (!labelTarget) { + return; + } + + // we want to trigger on BPMN elements only + if (!is$1(labelTarget, 'bpmn:BaseElement')) { + return; + } + + di = getDi(labelTarget); + + if (!di.label) { + di.label = bpmnFactory.create('bpmndi:BPMNLabel', { + bounds: bpmnFactory.create('dc:Bounds') + }); + + element.di = di; + } + + assign(di.label.bounds, { + x: element.x, + y: element.y, + width: element.width, + height: element.height + }); + }); + + function getVisibleLabelAdjustment(event) { + + var context = event.context, + connection = context.connection, + label = connection.label, + hints = assign({}, context.hints), + newWaypoints = context.newWaypoints || connection.waypoints, + oldWaypoints = context.oldWaypoints; + + + if (typeof hints.startChanged === 'undefined') { + hints.startChanged = !!hints.connectionStart; + } + + if (typeof hints.endChanged === 'undefined') { + hints.endChanged = !!hints.connectionEnd; + } + + return getLabelAdjustment(label, newWaypoints, oldWaypoints, hints); + } + + this.postExecute([ + 'connection.layout', + 'connection.updateWaypoints' + ], function(event) { + var context = event.context, + hints = context.hints || {}; + + if (hints.labelBehavior === false) { + return; + } + + var connection = context.connection, + label = connection.label, + labelAdjustment; + + // handle missing label as well as the case + // that the label parent does not exist (yet), + // because it is being pasted / created via multi element create + // + // Cf. https://github.com/bpmn-io/bpmn-js/pull/1227 + if (!label || !label.parent) { + return; + } + + labelAdjustment = getVisibleLabelAdjustment(event); + + modeling.moveShape(label, labelAdjustment); + }); + + + // keep label position on shape replace + this.postExecute([ 'shape.replace' ], function(event) { + var context = event.context, + newShape = context.newShape, + oldShape = context.oldShape; + + var businessObject = getBusinessObject(newShape); + + if (businessObject + && isLabelExternal(businessObject) + && oldShape.label + && newShape.label) { + newShape.label.x = oldShape.label.x; + newShape.label.y = oldShape.label.y; + } + }); + + + // move external label after resizing + this.postExecute('shape.resize', function(event) { + + var context = event.context, + shape = context.shape, + newBounds = context.newBounds, + oldBounds = context.oldBounds; + + if (hasExternalLabel(shape)) { + + var label = shape.label, + labelMid = getMid(label), + edges = asEdges(oldBounds); + + // get nearest border point to label as reference point + var referencePoint = getReferencePoint(labelMid, edges); + + var delta = getReferencePointDelta(referencePoint, oldBounds, newBounds); + + modeling.moveShape(label, delta); + + } + + }); + + } + + e(LabelBehavior, CommandInterceptor); + + LabelBehavior.$inject = [ + 'eventBus', + 'modeling', + 'bpmnFactory', + 'textRenderer' + ]; + + // helpers ////////////////////// + + /** + * Calculates a reference point delta relative to a new position + * of a certain element's bounds + * + * @param {Point} point + * @param {Bounds} oldBounds + * @param {Bounds} newBounds + * + * @return {Delta} delta + */ + function getReferencePointDelta(referencePoint, oldBounds, newBounds) { + + var newReferencePoint = getNewAttachPoint(referencePoint, oldBounds, newBounds); + + return roundPoint(delta(newReferencePoint, referencePoint)); + } + + /** + * Generates the nearest point (reference point) for a given point + * onto given set of lines + * + * @param {Array} lines + * @param {Point} point + * + * @param {Point} + */ + function getReferencePoint(point, lines) { + + if (!lines.length) { + return; + } + + var nearestLine = getNearestLine(point, lines); + + return perpendicularFoot(point, nearestLine); + } + + /** + * Convert the given bounds to a lines array containing all edges + * + * @param {Bounds|Point} bounds + * + * @return Array + */ + function asEdges(bounds) { + return [ + [ // top + { + x: bounds.x, + y: bounds.y + }, + { + x: bounds.x + (bounds.width || 0), + y: bounds.y + } + ], + [ // right + { + x: bounds.x + (bounds.width || 0), + y: bounds.y + }, + { + x: bounds.x + (bounds.width || 0), + y: bounds.y + (bounds.height || 0) + } + ], + [ // bottom + { + x: bounds.x, + y: bounds.y + (bounds.height || 0) + }, + { + x: bounds.x + (bounds.width || 0), + y: bounds.y + (bounds.height || 0) + } + ], + [ // left + { + x: bounds.x, + y: bounds.y + }, + { + x: bounds.x, + y: bounds.y + (bounds.height || 0) + } + ] + ]; + } + + /** + * Returns the nearest line for a given point by distance + * @param {Point} point + * @param Array lines + * + * @return Array + */ + function getNearestLine(point, lines) { + + var distances = lines.map(function(l) { + return { + line: l, + distance: getDistancePointLine(point, l) + }; + }); + + var sorted = sortBy(distances, 'distance'); + + return sorted[0].line; + } + + /** + * Calculate the new point after the connection waypoints got updated. + * + * @param {djs.model.Label} label + * @param {Array} newWaypoints + * @param {Array} oldWaypoints + * @param {Object} hints + * + * @return {Point} point + */ + function getConnectionAdjustment(position, newWaypoints, oldWaypoints, hints) { + return getAnchorPointAdjustment(position, newWaypoints, oldWaypoints, hints).point; + } + + /** + * A component that makes sure that Associations connected to Connections + * are updated together with the Connection. + * + * @param {EventBus} eventBus + * @param {Modeling} modeling + */ + function LayoutConnectionBehavior( + eventBus, modeling) { + + CommandInterceptor.call(this, eventBus); + + function getnewAnchorPoint(event, point) { + + var context = event.context, + connection = context.connection, + hints = assign({}, context.hints), + newWaypoints = context.newWaypoints || connection.waypoints, + oldWaypoints = context.oldWaypoints; + + + if (typeof hints.startChanged === 'undefined') { + hints.startChanged = !!hints.connectionStart; + } + + if (typeof hints.endChanged === 'undefined') { + hints.endChanged = !!hints.connectionEnd; + } + + return getConnectionAdjustment(point, newWaypoints, oldWaypoints, hints); + } + + this.postExecute([ + 'connection.layout', + 'connection.updateWaypoints' + ], function(event) { + var context = event.context; + + var connection = context.connection, + outgoing = connection.outgoing, + incoming = connection.incoming; + + incoming.forEach(function(connection) { + var endPoint = connection.waypoints[connection.waypoints.length - 1]; + var newEndpoint = getnewAnchorPoint(event, endPoint); + + var newWaypoints = [].concat(connection.waypoints.slice(0, -1), [ newEndpoint ]); + + modeling.updateWaypoints(connection, newWaypoints); + }); + + outgoing.forEach(function(connection) { + var startpoint = connection.waypoints[0]; + var newStartpoint = getnewAnchorPoint(event, startpoint); + + var newWaypoints = [].concat([ newStartpoint ], connection.waypoints.slice(1)); + + modeling.updateWaypoints(connection, newWaypoints); + }); + + }); + + + this.postExecute([ + 'connection.move' + ], function(event) { + var context = event.context; + + var connection = context.connection, + outgoing = connection.outgoing, + incoming = connection.incoming, + delta = context.delta; + + incoming.forEach(function(connection) { + var endPoint = connection.waypoints[connection.waypoints.length - 1]; + var newEndpoint = { + x: endPoint.x + delta.x, + y: endPoint.y + delta.y + }; + + var newWaypoints = [].concat(connection.waypoints.slice(0, -1), [ newEndpoint ]); + + modeling.updateWaypoints(connection, newWaypoints); + }); + + outgoing.forEach(function(connection) { + var startpoint = connection.waypoints[0]; + var newStartpoint = { + x: startpoint.x + delta.x, + y: startpoint.y + delta.y + }; + + var newWaypoints = [].concat([ newStartpoint ], connection.waypoints.slice(1)); + + modeling.updateWaypoints(connection, newWaypoints); + }); + + }); + + } + + e(LayoutConnectionBehavior, CommandInterceptor); + + LayoutConnectionBehavior.$inject = [ + 'eventBus', + 'modeling' + ]; + + function getResizedSourceAnchor(connection, shape, oldBounds) { + + var waypoints = safeGetWaypoints(connection), + waypointsInsideNewBounds = getWaypointsInsideBounds(waypoints, shape), + oldAnchor = waypoints[0]; + + // new anchor is the last waypoint enclosed be resized source + if (waypointsInsideNewBounds.length) { + return waypointsInsideNewBounds[ waypointsInsideNewBounds.length - 1 ]; + } + + return getNewAttachPoint(oldAnchor.original || oldAnchor, oldBounds, shape); + } + + + function getResizedTargetAnchor(connection, shape, oldBounds) { + + var waypoints = safeGetWaypoints(connection), + waypointsInsideNewBounds = getWaypointsInsideBounds(waypoints, shape), + oldAnchor = waypoints[waypoints.length - 1]; + + // new anchor is the first waypoint enclosed be resized target + if (waypointsInsideNewBounds.length) { + return waypointsInsideNewBounds[ 0 ]; + } + + return getNewAttachPoint(oldAnchor.original || oldAnchor, oldBounds, shape); + } + + + function getMovedSourceAnchor(connection, source, moveDelta) { + + var waypoints = safeGetWaypoints(connection), + oldBounds = subtract(source, moveDelta), + oldAnchor = waypoints[ 0 ]; + + return getNewAttachPoint(oldAnchor.original || oldAnchor, oldBounds, source); + } + + + function getMovedTargetAnchor(connection, target, moveDelta) { + + var waypoints = safeGetWaypoints(connection), + oldBounds = subtract(target, moveDelta), + oldAnchor = waypoints[ waypoints.length - 1 ]; + + return getNewAttachPoint(oldAnchor.original || oldAnchor, oldBounds, target); + } + + + // helpers ////////////////////// + + function subtract(bounds, delta) { + return { + x: bounds.x - delta.x, + y: bounds.y - delta.y, + width: bounds.width, + height: bounds.height + }; + } + + + /** + * Return waypoints of given connection; throw if non exists (should not happen!!). + * + * @param {Connection} connection + * + * @return {Array} + */ + function safeGetWaypoints(connection) { + + var waypoints = connection.waypoints; + + if (!waypoints.length) { + throw new Error('connection#' + connection.id + ': no waypoints'); + } + + return waypoints; + } + + function getWaypointsInsideBounds(waypoints, bounds) { + var originalWaypoints = map(waypoints, getOriginal); + + return filter(originalWaypoints, function(waypoint) { + return isInsideBounds(waypoint, bounds); + }); + } + + /** + * Checks if point is inside bounds, incl. edges. + * + * @param {Point} point + * @param {Bounds} bounds + */ + function isInsideBounds(point, bounds) { + return getOrientation(bounds, point, 1) === 'intersect'; + } + + function getOriginal(point) { + return point.original || point; + } + + /** + * BPMN-specific message flow behavior. + */ + function MessageFlowBehavior(eventBus, modeling) { + + CommandInterceptor.call(this, eventBus); + + this.postExecute('shape.replace', function(context) { + var oldShape = context.oldShape, + newShape = context.newShape; + + if (!isParticipantCollapse(oldShape, newShape)) { + return; + } + + var messageFlows = getMessageFlows(oldShape); + + messageFlows.incoming.forEach(function(incoming) { + var anchor = getResizedTargetAnchor(incoming, newShape, oldShape); + + modeling.reconnectEnd(incoming, newShape, anchor); + }); + + messageFlows.outgoing.forEach(function(outgoing) { + var anchor = getResizedSourceAnchor(outgoing, newShape, oldShape); + + modeling.reconnectStart(outgoing, newShape, anchor); + }); + }, true); + + } + + MessageFlowBehavior.$inject = [ 'eventBus', 'modeling' ]; + + e(MessageFlowBehavior, CommandInterceptor); + + // helpers ////////// + + function isParticipantCollapse(oldShape, newShape) { + return is$1(oldShape, 'bpmn:Participant') + && isExpanded(oldShape) + && is$1(newShape, 'bpmn:Participant') + && !isExpanded(newShape); + } + + function getMessageFlows(parent) { + var elements = selfAndAllChildren([ parent ], false); + + var incoming = [], + outgoing = []; + + elements.forEach(function(element) { + if (element === parent) { + return; + } + + element.incoming.forEach(function(connection) { + if (is$1(connection, 'bpmn:MessageFlow')) { + incoming.push(connection); + } + }); + + element.outgoing.forEach(function(connection) { + if (is$1(connection, 'bpmn:MessageFlow')) { + outgoing.push(connection); + } + }); + }, []); + + return { + incoming: incoming, + outgoing: outgoing + }; + } + + var COLLAB_ERR_MSG = 'flow elements must be children of pools/participants'; + + function ModelingFeedback(eventBus, tooltips, translate) { + + function showError(position, message, timeout) { + tooltips.add({ + position: { + x: position.x + 5, + y: position.y + 5 + }, + type: 'error', + timeout: timeout || 2000, + html: '
    ' + message + '
    ' + }); + } + + eventBus.on([ 'shape.move.rejected', 'create.rejected' ], function(event) { + var context = event.context, + shape = context.shape, + target = context.target; + + if (is$1(target, 'bpmn:Collaboration') && is$1(shape, 'bpmn:FlowNode')) { + showError(event, translate(COLLAB_ERR_MSG)); + } + }); + + } + + ModelingFeedback.$inject = [ + 'eventBus', + 'tooltips', + 'translate' + ]; + + /** + * BPMN specific behavior ensuring that bpmndi:Label's dc:Bounds are removed + * when shape is resized. + */ + function RemoveEmbeddedLabelBoundsBehavior(eventBus, modeling) { + CommandInterceptor.call(this, eventBus); + + this.preExecute('shape.resize', function(context) { + var shape = context.shape; + + var di = getDi(shape), + label = di && di.get('label'), + bounds = label && label.get('bounds'); + + if (bounds) { + modeling.updateModdleProperties(shape, label, { + bounds: undefined + }); + } + }, true); + } + + e(RemoveEmbeddedLabelBoundsBehavior, CommandInterceptor); + + RemoveEmbeddedLabelBoundsBehavior.$inject = [ + 'eventBus', + 'modeling' + ]; + + function RemoveElementBehavior(eventBus, bpmnRules, modeling) { + + CommandInterceptor.call(this, eventBus); + + /** + * Combine sequence flows when deleting an element + * if there is one incoming and one outgoing + * sequence flow + */ + this.preExecute('shape.delete', function(e) { + + var shape = e.context.shape; + + // only handle [a] -> [shape] -> [b] patterns + if (shape.incoming.length !== 1 || shape.outgoing.length !== 1) { + return; + } + + var inConnection = shape.incoming[0], + outConnection = shape.outgoing[0]; + + // only handle sequence flows + if (!is$1(inConnection, 'bpmn:SequenceFlow') || !is$1(outConnection, 'bpmn:SequenceFlow')) { + return; + } + + if (bpmnRules.canConnect(inConnection.source, outConnection.target, inConnection)) { + + // compute new, combined waypoints + var newWaypoints = getNewWaypoints(inConnection.waypoints, outConnection.waypoints); + + modeling.reconnectEnd(inConnection, outConnection.target, newWaypoints); + } + }); + + } + + e(RemoveElementBehavior, CommandInterceptor); + + RemoveElementBehavior.$inject = [ + 'eventBus', + 'bpmnRules', + 'modeling' + ]; + + + // helpers ////////////////////// + + function getDocking$1(point) { + return point.original || point; + } + + + function getNewWaypoints(inWaypoints, outWaypoints) { + + var intersection = lineIntersect( + getDocking$1(inWaypoints[inWaypoints.length - 2]), + getDocking$1(inWaypoints[inWaypoints.length - 1]), + getDocking$1(outWaypoints[1]), + getDocking$1(outWaypoints[0])); + + if (intersection) { + return [].concat( + inWaypoints.slice(0, inWaypoints.length - 1), + [ intersection ], + outWaypoints.slice(1)); + } else { + return [ + getDocking$1(inWaypoints[0]), + getDocking$1(outWaypoints[outWaypoints.length - 1]) + ]; + } + } + + /** + * BPMN specific remove behavior + */ + function RemoveParticipantBehavior(eventBus, modeling) { + + CommandInterceptor.call(this, eventBus); + + + /** + * morph collaboration diagram into process diagram + * after the last participant has been removed + */ + + this.preExecute('shape.delete', function(context) { + + var shape = context.shape, + parent = shape.parent; + + // activate the behavior if the shape to be removed + // is a participant + if (is$1(shape, 'bpmn:Participant')) { + context.collaborationRoot = parent; + } + }, true); + + this.postExecute('shape.delete', function(context) { + + var collaborationRoot = context.collaborationRoot; + + if (collaborationRoot && !collaborationRoot.businessObject.participants.length) { + + // replace empty collaboration with process diagram + modeling.makeProcess(); + } + }, true); + + } + + RemoveParticipantBehavior.$inject = [ 'eventBus', 'modeling' ]; + + e(RemoveParticipantBehavior, CommandInterceptor); + + function ReplaceConnectionBehavior(eventBus, modeling, bpmnRules, injector) { + + CommandInterceptor.call(this, eventBus); + + var dragging = injector.get('dragging', false); + + function fixConnection(connection) { + + var source = connection.source, + target = connection.target, + parent = connection.parent; + + // do not do anything if connection + // is already deleted (may happen due to other + // behaviors plugged-in before) + if (!parent) { + return; + } + + var replacementType, + remove; + + /** + * Check if incoming or outgoing connections + * can stay or could be substituted with an + * appropriate replacement. + * + * This holds true for SequenceFlow <> MessageFlow. + */ + + if (is$1(connection, 'bpmn:SequenceFlow')) { + if (!bpmnRules.canConnectSequenceFlow(source, target)) { + remove = true; + } + + if (bpmnRules.canConnectMessageFlow(source, target)) { + replacementType = 'bpmn:MessageFlow'; + } + } + + // transform message flows into sequence flows, if possible + + if (is$1(connection, 'bpmn:MessageFlow')) { + + if (!bpmnRules.canConnectMessageFlow(source, target)) { + remove = true; + } + + if (bpmnRules.canConnectSequenceFlow(source, target)) { + replacementType = 'bpmn:SequenceFlow'; + } + } + + if (is$1(connection, 'bpmn:Association') && !bpmnRules.canConnectAssociation(source, target)) { + remove = true; + } + + + // remove invalid connection, + // unless it has been removed already + if (remove) { + modeling.removeConnection(connection); + } + + // replace SequenceFlow <> MessageFlow + + if (replacementType) { + modeling.connect(source, target, { + type: replacementType, + waypoints: connection.waypoints.slice() + }); + } + } + + function replaceReconnectedConnection(event) { + + var context = event.context, + connection = context.connection, + source = context.newSource || connection.source, + target = context.newTarget || connection.target, + allowed, + replacement; + + allowed = bpmnRules.canConnect(source, target); + + if (!allowed || allowed.type === connection.type) { + return; + } + + replacement = modeling.connect(source, target, { + type: allowed.type, + waypoints: connection.waypoints.slice() + }); + + // remove old connection + modeling.removeConnection(connection); + + // replace connection in context to reconnect end/start + context.connection = replacement; + + if (dragging) { + cleanDraggingSelection(connection, replacement); + } + } + + // monkey-patch selection saved in dragging in order to re-select it when operation is finished + function cleanDraggingSelection(oldConnection, newConnection) { + var context = dragging.context(), + previousSelection = context && context.payload.previousSelection, + index; + + // do nothing if not dragging or no selection was present + if (!previousSelection || !previousSelection.length) { + return; + } + + index = previousSelection.indexOf(oldConnection); + + if (index === -1) { + return; + } + + previousSelection.splice(index, 1, newConnection); + } + + // lifecycle hooks + + this.postExecuted('elements.move', function(context) { + + var closure = context.closure, + allConnections = closure.allConnections; + + forEach$1(allConnections, fixConnection); + }, true); + + this.preExecute('connection.reconnect', replaceReconnectedConnection); + + this.postExecuted('element.updateProperties', function(event) { + var context = event.context, + properties = context.properties, + element = context.element, + businessObject = element.businessObject, + connection; + + // remove condition on change to default + if (properties.default) { + connection = find( + element.outgoing, + matchPattern({ id: element.businessObject.default.id }) + ); + + if (connection) { + modeling.updateProperties(connection, { conditionExpression: undefined }); + } + } + + // remove default from source on change to conditional + if (properties.conditionExpression && businessObject.sourceRef.default === businessObject) { + modeling.updateProperties(element.source, { default: undefined }); + } + }); + } + + e(ReplaceConnectionBehavior, CommandInterceptor); + + ReplaceConnectionBehavior.$inject = [ + 'eventBus', + 'modeling', + 'bpmnRules', + 'injector' + ]; + + /** + * BPMN-specific replace behavior. + */ + function ReplaceElementBehaviour( + bpmnReplace, + bpmnRules, + elementRegistry, + injector, + modeling, + selection + ) { + injector.invoke(CommandInterceptor, this); + + this._bpmnReplace = bpmnReplace; + this._elementRegistry = elementRegistry; + this._selection = selection; + + // replace elements on create, e.g. during copy-paste + this.postExecuted([ 'elements.create' ], 500, function(event) { + var context = event.context, + target = context.parent, + elements = context.elements; + + var elementReplacements = reduce(elements, function(replacements, element) { + var canReplace = bpmnRules.canReplace([ element ], element.host || element.parent || target); + + return canReplace ? replacements.concat(canReplace.replacements) : replacements; + }, []); + + if (elementReplacements.length) { + this.replaceElements(elements, elementReplacements); + } + }, this); + + // replace elements on move + this.postExecuted([ 'elements.move' ], 500, function(event) { + var context = event.context, + target = context.newParent, + newHost = context.newHost, + elements = []; + + forEach$1(context.closure.topLevel, function(topLevelElements) { + if (isEventSubProcess(topLevelElements)) { + elements = elements.concat(topLevelElements.children); + } else { + elements = elements.concat(topLevelElements); + } + }); + + // set target to host if attaching + if (elements.length === 1 && newHost) { + target = newHost; + } + + var canReplace = bpmnRules.canReplace(elements, target); + + if (canReplace) { + this.replaceElements(elements, canReplace.replacements, newHost); + } + }, this); + + // update attachments on host replace + this.postExecute([ 'shape.replace' ], 1500, function(e) { + var context = e.context, + oldShape = context.oldShape, + newShape = context.newShape, + attachers = oldShape.attachers, + canReplace; + + if (attachers && attachers.length) { + canReplace = bpmnRules.canReplace(attachers, newShape); + + this.replaceElements(attachers, canReplace.replacements); + } + + }, this); + + // keep ID on shape replace + this.postExecuted([ 'shape.replace' ], 1500, function(e) { + var context = e.context, + oldShape = context.oldShape, + newShape = context.newShape; + + modeling.unclaimId(oldShape.businessObject.id, oldShape.businessObject); + modeling.updateProperties(newShape, { id: oldShape.id }); + }); + } + + e(ReplaceElementBehaviour, CommandInterceptor); + + ReplaceElementBehaviour.prototype.replaceElements = function(elements, newElements) { + var elementRegistry = this._elementRegistry, + bpmnReplace = this._bpmnReplace, + selection = this._selection; + + forEach$1(newElements, function(replacement) { + var newElement = { + type: replacement.newElementType + }; + + var oldElement = elementRegistry.get(replacement.oldElementId); + + var idx = elements.indexOf(oldElement); + + elements[idx] = bpmnReplace.replaceElement(oldElement, newElement, { select: false }); + }); + + if (newElements) { + selection.select(elements); + } + }; + + ReplaceElementBehaviour.$inject = [ + 'bpmnReplace', + 'bpmnRules', + 'elementRegistry', + 'injector', + 'modeling', + 'selection' + ]; + + var HIGH_PRIORITY$9 = 1500; + + var GROUP_MIN_DIMENSIONS = { width: 140, height: 120 }; + + var LANE_MIN_DIMENSIONS = { width: 300, height: 60 }; + + var PARTICIPANT_MIN_DIMENSIONS = { width: 300, height: 150 }; + + var SUB_PROCESS_MIN_DIMENSIONS = { width: 140, height: 120 }; + + var TEXT_ANNOTATION_MIN_DIMENSIONS = { width: 50, height: 30 }; + + /** + * Set minimum bounds/resize constraints on resize. + * + * @param {EventBus} eventBus + */ + function ResizeBehavior(eventBus) { + eventBus.on('resize.start', HIGH_PRIORITY$9, function(event) { + var context = event.context, + shape = context.shape, + direction = context.direction, + balanced = context.balanced; + + if (is$1(shape, 'bpmn:Lane') || is$1(shape, 'bpmn:Participant')) { + context.resizeConstraints = getParticipantResizeConstraints(shape, direction, balanced); + } + + if (is$1(shape, 'bpmn:Participant')) { + context.minDimensions = PARTICIPANT_MIN_DIMENSIONS; + } + + if (is$1(shape, 'bpmn:SubProcess') && isExpanded(shape)) { + context.minDimensions = SUB_PROCESS_MIN_DIMENSIONS; + } + + if (is$1(shape, 'bpmn:TextAnnotation')) { + context.minDimensions = TEXT_ANNOTATION_MIN_DIMENSIONS; + } + }); + } + + ResizeBehavior.$inject = [ 'eventBus' ]; + + + var abs$2 = Math.abs, + min = Math.min, + max$2 = Math.max; + + + function addToTrbl(trbl, attr, value, choice) { + var current = trbl[attr]; + + // make sure to set the value if it does not exist + // or apply the correct value by comparing against + // choice(value, currentValue) + trbl[attr] = current === undefined ? value : choice(value, current); + } + + function addMin(trbl, attr, value) { + return addToTrbl(trbl, attr, value, min); + } + + function addMax(trbl, attr, value) { + return addToTrbl(trbl, attr, value, max$2); + } + + var LANE_RIGHT_PADDING = 20, + LANE_LEFT_PADDING = 50, + LANE_TOP_PADDING = 20, + LANE_BOTTOM_PADDING = 20; + + function getParticipantResizeConstraints(laneShape, resizeDirection, balanced) { + var lanesRoot = getLanesRoot(laneShape); + + var isFirst = true, + isLast = true; + + // max top/bottom size for lanes + var allLanes = collectLanes(lanesRoot, [ lanesRoot ]); + + var laneTrbl = asTRBL(laneShape); + + var maxTrbl = {}, + minTrbl = {}; + + if (/e/.test(resizeDirection)) { + minTrbl.right = laneTrbl.left + LANE_MIN_DIMENSIONS.width; + } else + if (/w/.test(resizeDirection)) { + minTrbl.left = laneTrbl.right - LANE_MIN_DIMENSIONS.width; + } + + allLanes.forEach(function(other) { + + var otherTrbl = asTRBL(other); + + if (/n/.test(resizeDirection)) { + + if (otherTrbl.top < (laneTrbl.top - 10)) { + isFirst = false; + } + + // max top size (based on next element) + if (balanced && abs$2(laneTrbl.top - otherTrbl.bottom) < 10) { + addMax(maxTrbl, 'top', otherTrbl.top + LANE_MIN_DIMENSIONS.height); + } + + // min top size (based on self or nested element) + if (abs$2(laneTrbl.top - otherTrbl.top) < 5) { + addMin(minTrbl, 'top', otherTrbl.bottom - LANE_MIN_DIMENSIONS.height); + } + } + + if (/s/.test(resizeDirection)) { + + if (otherTrbl.bottom > (laneTrbl.bottom + 10)) { + isLast = false; + } + + // max bottom size (based on previous element) + if (balanced && abs$2(laneTrbl.bottom - otherTrbl.top) < 10) { + addMin(maxTrbl, 'bottom', otherTrbl.bottom - LANE_MIN_DIMENSIONS.height); + } + + // min bottom size (based on self or nested element) + if (abs$2(laneTrbl.bottom - otherTrbl.bottom) < 5) { + addMax(minTrbl, 'bottom', otherTrbl.top + LANE_MIN_DIMENSIONS.height); + } + } + }); + + // max top/bottom/left/right size based on flow nodes + var flowElements = lanesRoot.children.filter(function(s) { + return !s.hidden && !s.waypoints && (is$1(s, 'bpmn:FlowElement') || is$1(s, 'bpmn:Artifact')); + }); + + flowElements.forEach(function(flowElement) { + + var flowElementTrbl = asTRBL(flowElement); + + if (isFirst && /n/.test(resizeDirection)) { + addMin(minTrbl, 'top', flowElementTrbl.top - LANE_TOP_PADDING); + } + + if (/e/.test(resizeDirection)) { + addMax(minTrbl, 'right', flowElementTrbl.right + LANE_RIGHT_PADDING); + } + + if (isLast && /s/.test(resizeDirection)) { + addMax(minTrbl, 'bottom', flowElementTrbl.bottom + LANE_BOTTOM_PADDING); + } + + if (/w/.test(resizeDirection)) { + addMin(minTrbl, 'left', flowElementTrbl.left - LANE_LEFT_PADDING); + } + }); + + return { + min: minTrbl, + max: maxTrbl + }; + } + + var SLIGHTLY_HIGHER_PRIORITY = 1001; + + + /** + * Invoke {@link Modeling#resizeLane} instead of + * {@link Modeling#resizeShape} when resizing a Lane + * or Participant shape. + */ + function ResizeLaneBehavior(eventBus, modeling) { + + eventBus.on('resize.start', SLIGHTLY_HIGHER_PRIORITY + 500, function(event) { + var context = event.context, + shape = context.shape; + + if (is$1(shape, 'bpmn:Lane') || is$1(shape, 'bpmn:Participant')) { + + // should we resize the opposite lane(s) in + // order to compensate for the resize operation? + context.balanced = !hasPrimaryModifier(event); + } + }); + + /** + * Intercept resize end and call resize lane function instead. + */ + eventBus.on('resize.end', SLIGHTLY_HIGHER_PRIORITY, function(event) { + var context = event.context, + shape = context.shape, + canExecute = context.canExecute, + newBounds = context.newBounds; + + if (is$1(shape, 'bpmn:Lane') || is$1(shape, 'bpmn:Participant')) { + + if (canExecute) { + + // ensure we have actual pixel values for new bounds + // (important when zoom level was > 1 during move) + newBounds = roundBounds(newBounds); + + // perform the actual resize + modeling.resizeLane(shape, newBounds, context.balanced); + } + + // stop propagation + return false; + } + }); + } + + ResizeLaneBehavior.$inject = [ + 'eventBus', + 'modeling' + ]; + + var LOW_PRIORITY$a = 500; + + + /** + * Add referenced root elements (error, escalation, message, signal) if they don't exist. + * Copy referenced root elements on copy & paste. + */ + function RootElementReferenceBehavior( + bpmnjs, eventBus, injector, moddleCopy, bpmnFactory + ) { + injector.invoke(CommandInterceptor, this); + + function canHaveRootElementReference(element) { + return isAny(element, [ 'bpmn:ReceiveTask', 'bpmn:SendTask' ]) || + hasAnyEventDefinition(element, [ + 'bpmn:ErrorEventDefinition', + 'bpmn:EscalationEventDefinition', + 'bpmn:MessageEventDefinition', + 'bpmn:SignalEventDefinition' + ]); + } + + function hasRootElement(rootElement) { + var definitions = bpmnjs.getDefinitions(), + rootElements = definitions.get('rootElements'); + + return !!find(rootElements, matchPattern({ id: rootElement.id })); + } + + function getRootElementReferencePropertyName(eventDefinition) { + if (is$1(eventDefinition, 'bpmn:ErrorEventDefinition')) { + return 'errorRef'; + } else if (is$1(eventDefinition, 'bpmn:EscalationEventDefinition')) { + return 'escalationRef'; + } else if (is$1(eventDefinition, 'bpmn:MessageEventDefinition')) { + return 'messageRef'; + } else if (is$1(eventDefinition, 'bpmn:SignalEventDefinition')) { + return 'signalRef'; + } + } + + function getRootElement(businessObject) { + if (isAny(businessObject, [ 'bpmn:ReceiveTask', 'bpmn:SendTask' ])) { + return businessObject.get('messageRef'); + } + + var eventDefinitions = businessObject.get('eventDefinitions'), + eventDefinition = eventDefinitions[ 0 ]; + + return eventDefinition.get(getRootElementReferencePropertyName(eventDefinition)); + } + + function setRootElement(businessObject, rootElement) { + if (isAny(businessObject, [ 'bpmn:ReceiveTask', 'bpmn:SendTask' ])) { + return businessObject.set('messageRef', rootElement); + } + + var eventDefinitions = businessObject.get('eventDefinitions'), + eventDefinition = eventDefinitions[ 0 ]; + + return eventDefinition.set(getRootElementReferencePropertyName(eventDefinition), rootElement); + } + + // create shape + this.executed('shape.create', function(context) { + var shape = context.shape; + + if (!canHaveRootElementReference(shape)) { + return; + } + + var businessObject = getBusinessObject(shape), + rootElement = getRootElement(businessObject), + rootElements; + + if (rootElement && !hasRootElement(rootElement)) { + rootElements = bpmnjs.getDefinitions().get('rootElements'); + + // add root element + add(rootElements, rootElement); + + context.addedRootElement = rootElement; + } + }, true); + + this.reverted('shape.create', function(context) { + var addedRootElement = context.addedRootElement; + + if (!addedRootElement) { + return; + } + + var rootElements = bpmnjs.getDefinitions().get('rootElements'); + + // remove root element + remove(rootElements, addedRootElement); + }, true); + + eventBus.on('copyPaste.copyElement', function(context) { + var descriptor = context.descriptor, + element = context.element; + + if (element.labelTarget || !canHaveRootElementReference(element)) { + return; + } + + var businessObject = getBusinessObject(element), + rootElement = getRootElement(businessObject); + + if (rootElement) { + + // TODO(nikku): clone on copy + descriptor.referencedRootElement = rootElement; + } + }); + + eventBus.on('copyPaste.pasteElement', LOW_PRIORITY$a, function(context) { + var descriptor = context.descriptor, + businessObject = descriptor.businessObject, + referencedRootElement = descriptor.referencedRootElement; + + if (!referencedRootElement) { + return; + } + + if (!hasRootElement(referencedRootElement)) { + referencedRootElement = moddleCopy.copyElement( + referencedRootElement, + bpmnFactory.create(referencedRootElement.$type) + ); + } + + setRootElement(businessObject, referencedRootElement); + + delete descriptor.referencedRootElement; + }); + } + + RootElementReferenceBehavior.$inject = [ + 'bpmnjs', + 'eventBus', + 'injector', + 'moddleCopy', + 'bpmnFactory' + ]; + + e(RootElementReferenceBehavior, CommandInterceptor); + + // helpers ////////// + + function hasAnyEventDefinition(element, types) { + if (!isArray$3(types)) { + types = [ types ]; + } + + return some(types, function(type) { + return hasEventDefinition$2(element, type); + }); + } + + var max$1 = Math.max; + + + function SpaceToolBehavior(eventBus) { + eventBus.on('spaceTool.getMinDimensions', function(context) { + var shapes = context.shapes, + axis = context.axis, + start = context.start, + minDimensions = {}; + + forEach$1(shapes, function(shape) { + var id = shape.id; + + if (is$1(shape, 'bpmn:Participant')) { + + if (isHorizontal$1(axis)) { + minDimensions[ id ] = PARTICIPANT_MIN_DIMENSIONS; + } else { + minDimensions[ id ] = { + width: PARTICIPANT_MIN_DIMENSIONS.width, + height: getParticipantMinHeight(shape, start) + }; + } + + } + + if (is$1(shape, 'bpmn:SubProcess') && isExpanded(shape)) { + minDimensions[ id ] = SUB_PROCESS_MIN_DIMENSIONS; + } + + if (is$1(shape, 'bpmn:TextAnnotation')) { + minDimensions[ id ] = TEXT_ANNOTATION_MIN_DIMENSIONS; + } + + if (is$1(shape, 'bpmn:Group')) { + minDimensions[ id ] = GROUP_MIN_DIMENSIONS; + } + }); + + return minDimensions; + }); + } + + SpaceToolBehavior.$inject = [ 'eventBus' ]; + + + // helpers ////////// + function isHorizontal$1(axis) { + return axis === 'x'; + } + + /** + * Get minimum height for participant taking lanes into account. + * + * @param {} participant + * @param {number} start + * + * @returns {Object} + */ + function getParticipantMinHeight(participant, start) { + var lanesMinHeight; + + if (!hasChildLanes(participant)) { + return PARTICIPANT_MIN_DIMENSIONS.height; + } + + lanesMinHeight = getLanesMinHeight(participant, start); + + return max$1(PARTICIPANT_MIN_DIMENSIONS.height, lanesMinHeight); + } + + function hasChildLanes(element) { + return !!getChildLanes(element).length; + } + + function getLanesMinHeight(participant, resizeStart) { + var lanes = getChildLanes(participant), + resizedLane; + + // find the nested lane which is currently resized + resizedLane = findResizedLane(lanes, resizeStart); + + // resized lane cannot shrink below the minimum height + // but remaining lanes' dimensions are kept intact + return participant.height - resizedLane.height + LANE_MIN_DIMENSIONS.height; + } + + /** + * Find nested lane which is currently resized. + * + * @param {Array} lanes + * @param {number} resizeStart + */ + function findResizedLane(lanes, resizeStart) { + var i, lane, childLanes; + + for (i = 0; i < lanes.length; i++) { + lane = lanes[i]; + + // resizing current lane or a lane nested + if (resizeStart >= lane.y && resizeStart <= lane.y + lane.height) { + childLanes = getChildLanes(lane); + + // a nested lane is resized + if (childLanes.length) { + return findResizedLane(childLanes, resizeStart); + } + + // current lane is the resized one + return lane; + } + } + } + + var LOW_PRIORITY$9 = 400; + var HIGH_PRIORITY$8 = 600; + + var DEFAULT_POSITION = { + x: 180, + y: 160 + }; + + + /** + * Creates bpmndi:BPMNPlane elements and canvas planes when collapsed subprocesses are created. + * + * + * @param {Canvas} canvas + * @param {EventBus} eventBus + * @param {Modeling} modeling + * @param {ElementFactory} elementFactory + * @param {BpmnFactory} bpmnFactory + * @param {Bpmnjs} bpmnjs + * @param {ElementRegistry} elementRegistry + */ + function SubProcessPlaneBehavior( + canvas, eventBus, modeling, + elementFactory, bpmnFactory, bpmnjs, elementRegistry) { + + CommandInterceptor.call(this, eventBus); + + this._canvas = canvas; + this._eventBus = eventBus; + this._modeling = modeling; + this._elementFactory = elementFactory; + this._bpmnFactory = bpmnFactory; + this._bpmnjs = bpmnjs; + this._elementRegistry = elementRegistry; + + var self = this; + + function isCollapsedSubProcess(element) { + return is$1(element, 'bpmn:SubProcess') && !isExpanded(element); + } + + function createRoot(context) { + var shape = context.shape, + rootElement = context.newRootElement; + + var businessObject = getBusinessObject(shape); + + rootElement = self._addDiagram(rootElement || businessObject); + + context.newRootElement = canvas.addRootElement(rootElement); + } + + function removeRoot(context) { + var shape = context.shape; + + var businessObject = getBusinessObject(shape); + self._removeDiagram(businessObject); + + var rootElement = context.newRootElement = elementRegistry.get(getPlaneIdFromShape(businessObject)); + + canvas.removeRootElement(rootElement); + } + + // add plane elements for newly created sub-processes + // this ensures we can actually drill down into the element + this.executed('shape.create', function(context) { + var shape = context.shape; + if (!isCollapsedSubProcess(shape)) { + return; + } + + createRoot(context); + }, true); + + + this.postExecuted('shape.create', function(context) { + var shape = context.shape, + rootElement = context.newRootElement; + + if (!rootElement || !shape.children) { + return; + } + + self._showRecursively(shape.children); + + self._moveChildrenToShape(shape, rootElement); + }, true); + + + this.reverted('shape.create', function(context) { + var shape = context.shape; + if (!isCollapsedSubProcess(shape)) { + return; + } + + removeRoot(context); + }, true); + + + this.preExecuted('shape.delete', function(context) { + var shape = context.shape; + if (!isCollapsedSubProcess(shape)) { + return; + } + + var attachedRoot = elementRegistry.get(getPlaneIdFromShape(shape)); + + if (!attachedRoot) { + return; + } + + modeling.removeElements(attachedRoot.children.slice()); + }, true); + + + this.executed('shape.delete', function(context) { + var shape = context.shape; + if (!isCollapsedSubProcess(shape)) { + return; + } + removeRoot(context); + }, true); + + + this.reverted('shape.delete', function(context) { + var shape = context.shape; + if (!isCollapsedSubProcess(shape)) { + return; + } + + createRoot(context); + }, true); + + + this.preExecuted('shape.replace', function(context) { + var oldShape = context.oldShape; + var newShape = context.newShape; + + if (!isCollapsedSubProcess(oldShape) || !isCollapsedSubProcess(newShape)) { + return; + } + + // old plane could have content, + // we remove it so it is not recursively deleted from 'shape.delete' + context.oldRoot = canvas.removeRootElement(getPlaneIdFromShape(oldShape)); + }, true); + + + this.postExecuted('shape.replace', function(context) { + var newShape = context.newShape, + source = context.oldRoot, + target = canvas.findRoot(getPlaneIdFromShape(newShape)); + + if (!source || !target) { + return; + } + var elements = source.children; + + modeling.moveElements(elements, { x: 0, y: 0 }, target); + }, true); + + + // rename primary elements when the secondary element changes + // this ensures rootElement.id = element.id + '_plane' + this.executed('element.updateProperties', function(context) { + var shape = context.element; + + if (!is$1(shape, 'bpmn:SubProcess')) { + return; + } + + var properties = context.properties; + var oldProperties = context.oldProperties; + + var oldId = oldProperties.id, + newId = properties.id; + + if (oldId === newId) { + return; + } + + if (isPlane(shape)) { + elementRegistry.updateId(shape, toPlaneId(newId)); + elementRegistry.updateId(oldId, newId); + + return; + } + + var planeElement = elementRegistry.get(toPlaneId(oldId)); + + if (!planeElement) { + return; + } + + elementRegistry.updateId(toPlaneId(oldId), toPlaneId(newId)); + }, true); + + + this.reverted('element.updateProperties', function(context) { + var shape = context.element; + + if (!is$1(shape, 'bpmn:SubProcess')) { + return; + } + + var properties = context.properties; + var oldProperties = context.oldProperties; + + var oldId = oldProperties.id, + newId = properties.id; + + if (oldId === newId) { + return; + } + + if (isPlane(shape)) { + elementRegistry.updateId(shape, toPlaneId(oldId)); + elementRegistry.updateId(newId, oldId); + + return; + } + + var planeElement = elementRegistry.get(toPlaneId(newId)); + + if (!planeElement) { + return; + } + + elementRegistry.updateId(planeElement, toPlaneId(oldId)); + }, true); + + // re-throw element.changed to re-render primary shape if associated plane has + // changed (e.g. bpmn:name property has changed) + eventBus.on('element.changed', function(context) { + var element = context.element; + + if (!isPlane(element)) { + return; + } + + var plane = element; + + var primaryShape = elementRegistry.get(getShapeIdFromPlane(plane)); + + // do not re-throw if no associated primary shape (e.g. bpmn:Process) + if (!primaryShape || primaryShape === plane) { + return; + } + + eventBus.fire('element.changed', { element: primaryShape }); + }); + + + // create/remove plane for the subprocess + this.executed('shape.toggleCollapse', LOW_PRIORITY$9, function(context) { + var shape = context.shape; + + if (!is$1(shape, 'bpmn:SubProcess')) { + return; + } + + if (!isExpanded(shape)) { + createRoot(context); + self._showRecursively(shape.children); + } else { + removeRoot(context); + } + + }, true); + + + // create/remove plane for the subprocess + this.reverted('shape.toggleCollapse', LOW_PRIORITY$9, function(context) { + var shape = context.shape; + + if (!is$1(shape, 'bpmn:SubProcess')) { + return; + } + + if (!isExpanded(shape)) { + createRoot(context); + self._showRecursively(shape.children); + } else { + removeRoot(context); + } + + }, true); + + // move elements between planes + this.postExecuted('shape.toggleCollapse', HIGH_PRIORITY$8, function(context) { + var shape = context.shape; + + if (!is$1(shape, 'bpmn:SubProcess')) { + return; + } + + var rootElement = context.newRootElement; + + if (!rootElement) { + return; + } + + if (!isExpanded(shape)) { + + // collapsed + self._moveChildrenToShape(shape, rootElement); + + } else { + self._moveChildrenToShape(rootElement, shape); + } + }, true); + + + // copy-paste /////////// + + // add elements in plane to tree + eventBus.on('copyPaste.createTree', function(context) { + var element = context.element, + children = context.children; + + if (!isCollapsedSubProcess(element)) { + return; + } + + var id = getPlaneIdFromShape(element); + var parent = elementRegistry.get(id); + + if (parent) { + + // do not copy invisible root element + children.push.apply(children, parent.children); + } + }); + + // set plane children as direct children of collapsed shape + eventBus.on('copyPaste.copyElement', function(context) { + var descriptor = context.descriptor, + element = context.element, + elements = context.elements; + + var parent = element.parent; + + var isPlane = is$1(getDi(parent), 'bpmndi:BPMNPlane'); + if (!isPlane) { + return; + } + + var parentId = getShapeIdFromPlane(parent); + + var referencedShape = find(elements, function(element) { + return element.id === parentId; + }); + + if (!referencedShape) { + return; + } + + descriptor.parent = referencedShape.id; + }); + + // hide children during pasting + eventBus.on('copyPaste.pasteElement', function(context) { + var descriptor = context.descriptor; + + if (!descriptor.parent) { + return; + } + + if (isCollapsedSubProcess(descriptor.parent) || descriptor.parent.hidden) { + descriptor.hidden = true; + } + }); + + } + + e(SubProcessPlaneBehavior, CommandInterceptor); + + /** + * Moves the child elements from source to target. + * + * If the target is a plane, the children are moved to the top left corner. + * Otherwise, the center of the target is used. + * + * @param {Object|djs.model.Base} source + * @param {Object|djs.model.Base} target + */ + SubProcessPlaneBehavior.prototype._moveChildrenToShape = function(source, target) { + var modeling = this._modeling; + + var children = source.children; + var offset; + + if (!children) { + return; + } + + // add external labels that weren't children of sub process + children = children.concat(children.reduce(function(labels, child) { + if (child.label && child.label.parent !== source) { + return labels.concat(child.label); + } + + return labels; + }, [])); + + // only change plane if there are no visible children, but don't move them + var visibleChildren = children.filter(function(child) { + return !child.hidden; + }); + + if (!visibleChildren.length) { + modeling.moveElements(children, { x: 0, y: 0 }, target, { autoResize: false }); + return; + } + + var childrenBounds = getBBox(visibleChildren); + + // target is a plane + if (!target.x) { + offset = { + x: DEFAULT_POSITION.x - childrenBounds.x, + y: DEFAULT_POSITION.y - childrenBounds.y + }; + } + + // source is a plane + else { + + // move relative to the center of the shape + var targetMid = getMid(target); + var childrenMid = getMid(childrenBounds); + + offset = { + x: targetMid.x - childrenMid.x, + y: targetMid.y - childrenMid.y + }; + } + + modeling.moveElements(children, offset, target, { autoResize: false }); + }; + + /** + * Sets `hidden` property on all children of the given shape. + * + * @param {Array} elements + * @param {Boolean} [hidden] + * @returns {Array} all child elements + */ + SubProcessPlaneBehavior.prototype._showRecursively = function(elements, hidden) { + var self = this; + + var result = []; + elements.forEach(function(element) { + element.hidden = !!hidden; + + result = result.concat(element); + + if (element.children) { + result = result.concat( + self._showRecursively(element.children, element.collapsed || hidden) + ); + } + }); + + return result; + }; + + /** + * Adds a given rootElement to the bpmnDi diagrams. + * + * @param {Object} rootElement + * @returns {Object} planeElement + */ + SubProcessPlaneBehavior.prototype._addDiagram = function(planeElement) { + var bpmnjs = this._bpmnjs; + var diagrams = bpmnjs.getDefinitions().diagrams; + + if (!planeElement.businessObject) { + planeElement = this._createNewDiagram(planeElement); + } + + diagrams.push(planeElement.di.$parent); + + return planeElement; + }; + + + /** + * Creates a new plane element for the given sub process. + * + * @param {Object} bpmnElement + * + * @return {Object} new diagram element + */ + SubProcessPlaneBehavior.prototype._createNewDiagram = function(bpmnElement) { + var bpmnFactory = this._bpmnFactory; + var elementFactory = this._elementFactory; + + var diPlane = bpmnFactory.create('bpmndi:BPMNPlane', { + bpmnElement: bpmnElement + }); + var diDiagram = bpmnFactory.create('bpmndi:BPMNDiagram', { + plane: diPlane + }); + diPlane.$parent = diDiagram; + + // add a virtual element (not being drawn), + // a copy cat of our BpmnImporter code + var planeElement = elementFactory.createRoot({ + id: getPlaneIdFromShape(bpmnElement), + type: bpmnElement.$type, + di: diPlane, + businessObject: bpmnElement, + collapsed: true + }); + + return planeElement; + }; + + /** + * Removes the diagram for a given root element + * + * @param {Object} rootElement + * @returns {Object} removed bpmndi:BPMNDiagram + */ + SubProcessPlaneBehavior.prototype._removeDiagram = function(rootElement) { + var bpmnjs = this._bpmnjs; + + var diagrams = bpmnjs.getDefinitions().diagrams; + + var removedDiagram = find(diagrams, function(diagram) { + return diagram.plane.bpmnElement.id === rootElement.id; + }); + + diagrams.splice(diagrams.indexOf(removedDiagram), 1); + + return removedDiagram; + }; + + + SubProcessPlaneBehavior.$inject = [ + 'canvas', + 'eventBus', + 'modeling', + 'elementFactory', + 'bpmnFactory', + 'bpmnjs', + 'elementRegistry' + ]; + + /** + * Add start event replacing element with expanded sub process. + * + * @param {Injector} injector + * @param {Modeling} modeling + */ + function SubProcessStartEventBehavior(injector, modeling) { + injector.invoke(CommandInterceptor, this); + + this.postExecuted('shape.replace', function(event) { + var oldShape = event.context.oldShape, + newShape = event.context.newShape; + + if ( + !is$1(newShape, 'bpmn:SubProcess') || + ! (is$1(oldShape, 'bpmn:Task') || is$1(oldShape, 'bpmn:CallActivity')) || + !isExpanded(newShape) + ) { + return; + } + + var position = getStartEventPosition(newShape); + + modeling.createShape({ type: 'bpmn:StartEvent' }, position, newShape); + }); + } + + SubProcessStartEventBehavior.$inject = [ + 'injector', + 'modeling' + ]; + + e(SubProcessStartEventBehavior, CommandInterceptor); + + // helpers ////////// + + function getStartEventPosition(shape) { + return { + x: shape.x + shape.width / 6, + y: shape.y + shape.height / 2 + }; + } + + function ToggleCollapseConnectionBehaviour( + eventBus, modeling + ) { + + CommandInterceptor.call(this, eventBus); + + this.postExecuted('shape.toggleCollapse', 1500, function(context) { + + // var shape = context.shape; + var shape = context.shape; + + // only change connections when collapsing + if (isExpanded(shape)) { + return; + } + + var allChildren = selfAndAllChildren(shape); + + allChildren.forEach(function(child) { + + // Ensure that the connection array is not modified during iteration + var incomingConnections = child.incoming.slice(), + outgoingConnections = child.outgoing.slice(); + + forEach$1(incomingConnections, function(c) { + handleConnection(c, true); + }); + + forEach$1(outgoingConnections, function(c) { + handleConnection(c, false); + }); + }); + + + function handleConnection(c, incoming) { + if (allChildren.indexOf(c.source) !== -1 && allChildren.indexOf(c.target) !== -1) { + return; + } + + if (incoming) { + modeling.reconnectEnd(c, shape, getMid(shape)); + } else { + modeling.reconnectStart(c, shape, getMid(shape)); + } + + } + + }, true); + + } + + e(ToggleCollapseConnectionBehaviour, CommandInterceptor); + + ToggleCollapseConnectionBehaviour.$inject = [ + 'eventBus', + 'modeling', + ]; + + var LOW_PRIORITY$8 = 500; + + + function ToggleElementCollapseBehaviour( + eventBus, elementFactory, modeling, + resize) { + + CommandInterceptor.call(this, eventBus); + + + function hideEmptyLabels(children) { + if (children.length) { + children.forEach(function(child) { + if (child.type === 'label' && !child.businessObject.name) { + child.hidden = true; + } + }); + } + } + + function expandedBounds(shape, defaultSize) { + var children = shape.children, + newBounds = defaultSize, + visibleElements, + visibleBBox; + + visibleElements = filterVisible(children).concat([ shape ]); + + visibleBBox = computeChildrenBBox(visibleElements); + + if (visibleBBox) { + + // center to visibleBBox with max(defaultSize, childrenBounds) + newBounds.width = Math.max(visibleBBox.width, newBounds.width); + newBounds.height = Math.max(visibleBBox.height, newBounds.height); + + newBounds.x = visibleBBox.x + (visibleBBox.width - newBounds.width) / 2; + newBounds.y = visibleBBox.y + (visibleBBox.height - newBounds.height) / 2; + } else { + + // center to collapsed shape with defaultSize + newBounds.x = shape.x + (shape.width - newBounds.width) / 2; + newBounds.y = shape.y + (shape.height - newBounds.height) / 2; + } + + return newBounds; + } + + function collapsedBounds(shape, defaultSize) { + + return { + x: shape.x + (shape.width - defaultSize.width) / 2, + y: shape.y + (shape.height - defaultSize.height) / 2, + width: defaultSize.width, + height: defaultSize.height + }; + } + + this.executed([ 'shape.toggleCollapse' ], LOW_PRIORITY$8, function(e) { + + var context = e.context, + shape = context.shape; + + if (!is$1(shape, 'bpmn:SubProcess')) { + return; + } + + if (!shape.collapsed) { + + // all children got made visible through djs, hide empty labels + hideEmptyLabels(shape.children); + + // remove collapsed marker + getDi(shape).isExpanded = true; + } else { + + // place collapsed marker + getDi(shape).isExpanded = false; + } + }); + + this.reverted([ 'shape.toggleCollapse' ], LOW_PRIORITY$8, function(e) { + + var context = e.context; + var shape = context.shape; + + + // revert removing/placing collapsed marker + if (!shape.collapsed) { + getDi(shape).isExpanded = true; + + } else { + getDi(shape).isExpanded = false; + } + }); + + this.postExecuted([ 'shape.toggleCollapse' ], LOW_PRIORITY$8, function(e) { + var shape = e.context.shape, + defaultSize = elementFactory.getDefaultSize(shape), + newBounds; + + if (shape.collapsed) { + + // resize to default size of collapsed shapes + newBounds = collapsedBounds(shape, defaultSize); + } else { + + // resize to bounds of max(visible children, defaultSize) + newBounds = expandedBounds(shape, defaultSize); + } + + modeling.resizeShape(shape, newBounds, null, { + autoResize: shape.collapsed ? false : 'nwse' + }); + }); + + } + + + e(ToggleElementCollapseBehaviour, CommandInterceptor); + + ToggleElementCollapseBehaviour.$inject = [ + 'eventBus', + 'elementFactory', + 'modeling' + ]; + + + // helpers ////////////////////// + + function filterVisible(elements) { + return elements.filter(function(e) { + return !e.hidden; + }); + } + + /** + * Unclaims model IDs on element deletion. + * + * @param {Canvas} canvas + * @param {Injector} injector + * @param {Moddle} moddle + * @param {Modeling} modeling + */ + function UnclaimIdBehavior(canvas, injector, moddle, modeling) { + injector.invoke(CommandInterceptor, this); + + this.preExecute('shape.delete', function(event) { + var context = event.context, + shape = context.shape, + shapeBo = shape.businessObject; + + if (isLabel$6(shape)) { + return; + } + + if (is$1(shape, 'bpmn:Participant') && isExpanded(shape)) { + moddle.ids.unclaim(shapeBo.processRef.id); + } + + modeling.unclaimId(shapeBo.id, shapeBo); + }); + + + this.preExecute('connection.delete', function(event) { + var context = event.context, + connection = context.connection, + connectionBo = connection.businessObject; + + modeling.unclaimId(connectionBo.id, connectionBo); + }); + + this.preExecute('canvas.updateRoot', function() { + var rootElement = canvas.getRootElement(), + rootElementBo = rootElement.businessObject; + + if (is$1(rootElement, 'bpmn:Collaboration')) { + moddle.ids.unclaim(rootElementBo.id); + } + }); + } + + e(UnclaimIdBehavior, CommandInterceptor); + + UnclaimIdBehavior.$inject = [ 'canvas', 'injector', 'moddle', 'modeling' ]; + + /** + * A behavior that unsets the Default property of + * sequence flow source on element delete, if the + * removed element is the Gateway or Task's default flow. + * + * @param {EventBus} eventBus + * @param {Modeling} modeling + */ + function DeleteSequenceFlowBehavior(eventBus, modeling) { + + CommandInterceptor.call(this, eventBus); + + + this.preExecute('connection.delete', function(event) { + var context = event.context, + connection = context.connection, + source = connection.source; + + if (isDefaultFlow(connection, source)) { + modeling.updateProperties(source, { + 'default': null + }); + } + }); + } + + e(DeleteSequenceFlowBehavior, CommandInterceptor); + + DeleteSequenceFlowBehavior.$inject = [ + 'eventBus', + 'modeling' + ]; + + + // helpers ////////////////////// + + function isDefaultFlow(connection, source) { + + if (!is$1(connection, 'bpmn:SequenceFlow')) { + return false; + } + + var sourceBo = getBusinessObject(source), + sequenceFlow = getBusinessObject(connection); + + return sourceBo.get('default') === sequenceFlow; + } + + var LOW_PRIORITY$7 = 500, + HIGH_PRIORITY$7 = 5000; + + + /** + * BPMN specific delete lane behavior + */ + function UpdateFlowNodeRefsBehavior(eventBus, modeling, translate) { + + CommandInterceptor.call(this, eventBus); + + /** + * Ok, this is it: + * + * We have to update the Lane#flowNodeRefs _and_ + * FlowNode#lanes with every FlowNode move/resize and + * Lane move/resize. + * + * We want to group that stuff to recompute containments + * as efficient as possible. + * + * Yea! + */ + + // the update context + var context; + + + function initContext() { + context = context || new UpdateContext(); + context.enter(); + + return context; + } + + function getContext() { + if (!context) { + throw new Error(translate('out of bounds release')); + } + + return context; + } + + function releaseContext() { + + if (!context) { + throw new Error(translate('out of bounds release')); + } + + var triggerUpdate = context.leave(); + + if (triggerUpdate) { + modeling.updateLaneRefs(context.flowNodes, context.lanes); + + context = null; + } + + return triggerUpdate; + } + + + var laneRefUpdateEvents = [ + 'spaceTool', + 'lane.add', + 'lane.resize', + 'lane.split', + 'elements.create', + 'elements.delete', + 'elements.move', + 'shape.create', + 'shape.delete', + 'shape.move', + 'shape.resize' + ]; + + + // listen to a lot of stuff to group lane updates + + this.preExecute(laneRefUpdateEvents, HIGH_PRIORITY$7, function(event) { + initContext(); + }); + + this.postExecuted(laneRefUpdateEvents, LOW_PRIORITY$7, function(event) { + releaseContext(); + }); + + + // Mark flow nodes + lanes that need an update + + this.preExecute([ + 'shape.create', + 'shape.move', + 'shape.delete', + 'shape.resize' + ], function(event) { + + var context = event.context, + shape = context.shape; + + var updateContext = getContext(); + + // no need to update labels + if (shape.labelTarget) { + return; + } + + if (is$1(shape, 'bpmn:Lane')) { + updateContext.addLane(shape); + } + + if (is$1(shape, 'bpmn:FlowNode')) { + updateContext.addFlowNode(shape); + } + }); + } + + UpdateFlowNodeRefsBehavior.$inject = [ + 'eventBus', + 'modeling' , + 'translate' + ]; + + e(UpdateFlowNodeRefsBehavior, CommandInterceptor); + + + function UpdateContext() { + + this.flowNodes = []; + this.lanes = []; + + this.counter = 0; + + this.addLane = function(lane) { + this.lanes.push(lane); + }; + + this.addFlowNode = function(flowNode) { + this.flowNodes.push(flowNode); + }; + + this.enter = function() { + this.counter++; + }; + + this.leave = function() { + this.counter--; + + return !this.counter; + }; + } + + var BehaviorModule = { + __init__: [ + 'adaptiveLabelPositioningBehavior', + 'appendBehavior', + 'associationBehavior', + 'attachEventBehavior', + 'boundaryEventBehavior', + 'createBehavior', + 'createDataObjectBehavior', + 'createParticipantBehavior', + 'dataInputAssociationBehavior', + 'dataStoreBehavior', + 'deleteLaneBehavior', + 'detachEventBehavior', + 'dropOnFlowBehavior', + 'eventBasedGatewayBehavior', + 'fixHoverBehavior', + 'groupBehavior', + 'importDockingFix', + 'isHorizontalFix', + 'labelBehavior', + 'layoutConnectionBehavior', + 'messageFlowBehavior', + 'modelingFeedback', + 'removeElementBehavior', + 'removeEmbeddedLabelBoundsBehavior', + 'removeParticipantBehavior', + 'replaceConnectionBehavior', + 'replaceElementBehaviour', + 'resizeBehavior', + 'resizeLaneBehavior', + 'rootElementReferenceBehavior', + 'spaceToolBehavior', + 'subProcessPlaneBehavior', + 'subProcessStartEventBehavior', + 'toggleCollapseConnectionBehaviour', + 'toggleElementCollapseBehaviour', + 'unclaimIdBehavior', + 'updateFlowNodeRefsBehavior', + 'unsetDefaultFlowBehavior' + ], + adaptiveLabelPositioningBehavior: [ 'type', AdaptiveLabelPositioningBehavior ], + appendBehavior: [ 'type', AppendBehavior ], + associationBehavior: [ 'type', AssociationBehavior ], + attachEventBehavior: [ 'type', AttachEventBehavior ], + boundaryEventBehavior: [ 'type', BoundaryEventBehavior ], + createBehavior: [ 'type', CreateBehavior ], + createDataObjectBehavior: [ 'type', CreateDataObjectBehavior ], + createParticipantBehavior: [ 'type', CreateParticipantBehavior ], + dataInputAssociationBehavior: [ 'type', DataInputAssociationBehavior ], + dataStoreBehavior: [ 'type', DataStoreBehavior ], + deleteLaneBehavior: [ 'type', DeleteLaneBehavior ], + detachEventBehavior: [ 'type', DetachEventBehavior ], + dropOnFlowBehavior: [ 'type', DropOnFlowBehavior ], + eventBasedGatewayBehavior: [ 'type', EventBasedGatewayBehavior ], + fixHoverBehavior: [ 'type', FixHoverBehavior ], + groupBehavior: [ 'type', GroupBehavior ], + importDockingFix: [ 'type', ImportDockingFix ], + isHorizontalFix: [ 'type', IsHorizontalFix ], + labelBehavior: [ 'type', LabelBehavior ], + layoutConnectionBehavior: [ 'type', LayoutConnectionBehavior ], + messageFlowBehavior: [ 'type', MessageFlowBehavior ], + modelingFeedback: [ 'type', ModelingFeedback ], + removeElementBehavior: [ 'type', RemoveElementBehavior ], + removeEmbeddedLabelBoundsBehavior: [ 'type', RemoveEmbeddedLabelBoundsBehavior ], + removeParticipantBehavior: [ 'type', RemoveParticipantBehavior ], + replaceConnectionBehavior: [ 'type', ReplaceConnectionBehavior ], + replaceElementBehaviour: [ 'type', ReplaceElementBehaviour ], + resizeBehavior: [ 'type', ResizeBehavior ], + resizeLaneBehavior: [ 'type', ResizeLaneBehavior ], + rootElementReferenceBehavior: [ 'type', RootElementReferenceBehavior ], + spaceToolBehavior: [ 'type', SpaceToolBehavior ], + subProcessPlaneBehavior: [ 'type', SubProcessPlaneBehavior ], + subProcessStartEventBehavior: [ 'type', SubProcessStartEventBehavior ], + toggleCollapseConnectionBehaviour: [ 'type', ToggleCollapseConnectionBehaviour ], + toggleElementCollapseBehaviour : [ 'type', ToggleElementCollapseBehaviour ], + unclaimIdBehavior: [ 'type', UnclaimIdBehavior ], + unsetDefaultFlowBehavior: [ 'type', DeleteSequenceFlowBehavior ], + updateFlowNodeRefsBehavior: [ 'type', UpdateFlowNodeRefsBehavior ] + }; + + function getBoundaryAttachment(position, targetBounds) { + + var orientation = getOrientation(position, targetBounds, -15); + + if (orientation !== 'intersect') { + return orientation; + } else { + return null; + } + } + + /** + * BPMN specific modeling rule + */ + function BpmnRules(eventBus) { + RuleProvider.call(this, eventBus); + } + + e(BpmnRules, RuleProvider); + + BpmnRules.$inject = [ 'eventBus' ]; + + BpmnRules.prototype.init = function() { + + this.addRule('connection.start', function(context) { + var source = context.source; + + return canStartConnection(source); + }); + + this.addRule('connection.create', function(context) { + var source = context.source, + target = context.target, + hints = context.hints || {}, + targetParent = hints.targetParent, + targetAttach = hints.targetAttach; + + // don't allow incoming connections on + // newly created boundary events + // to boundary events + if (targetAttach) { + return false; + } + + // temporarily set target parent for scoping + // checks to work + if (targetParent) { + target.parent = targetParent; + } + + try { + return canConnect(source, target); + } finally { + + // unset temporary target parent + if (targetParent) { + target.parent = null; + } + } + }); + + this.addRule('connection.reconnect', function(context) { + + var connection = context.connection, + source = context.source, + target = context.target; + + return canConnect(source, target, connection); + }); + + this.addRule('connection.updateWaypoints', function(context) { + return { + type: context.connection.type + }; + }); + + this.addRule('shape.resize', function(context) { + + var shape = context.shape, + newBounds = context.newBounds; + + return canResize(shape, newBounds); + }); + + this.addRule('elements.create', function(context) { + var elements = context.elements, + position = context.position, + target = context.target; + + if (isConnection$8(target) && !canInsert(elements, target)) { + return false; + } + + return every(elements, function(element) { + if (isConnection$8(element)) { + return canConnect(element.source, element.target, element); + } + + if (element.host) { + return canAttach(element, element.host, null, position); + } + + return canCreate(element, target, null); + }); + }); + + this.addRule('elements.move', function(context) { + + var target = context.target, + shapes = context.shapes, + position = context.position; + + return canAttach(shapes, target, null, position) || + canReplace(shapes, target, position) || + canMove(shapes, target) || + canInsert(shapes, target); + }); + + this.addRule('shape.create', function(context) { + return canCreate( + context.shape, + context.target, + context.source, + context.position + ); + }); + + this.addRule('shape.attach', function(context) { + + return canAttach( + context.shape, + context.target, + null, + context.position + ); + }); + + this.addRule('element.copy', function(context) { + var element = context.element, + elements = context.elements; + + return canCopy(elements, element); + }); + }; + + BpmnRules.prototype.canConnectMessageFlow = canConnectMessageFlow; + + BpmnRules.prototype.canConnectSequenceFlow = canConnectSequenceFlow; + + BpmnRules.prototype.canConnectDataAssociation = canConnectDataAssociation; + + BpmnRules.prototype.canConnectAssociation = canConnectAssociation; + + BpmnRules.prototype.canMove = canMove; + + BpmnRules.prototype.canAttach = canAttach; + + BpmnRules.prototype.canReplace = canReplace; + + BpmnRules.prototype.canDrop = canDrop; + + BpmnRules.prototype.canInsert = canInsert; + + BpmnRules.prototype.canCreate = canCreate; + + BpmnRules.prototype.canConnect = canConnect; + + BpmnRules.prototype.canResize = canResize; + + BpmnRules.prototype.canCopy = canCopy; + + /** + * Utility functions for rule checking + */ + + /** + * Checks if given element can be used for starting connection. + * + * @param {Element} source + * @return {boolean} + */ + function canStartConnection(element) { + if (nonExistingOrLabel(element)) { + return null; + } + + return isAny(element, [ + 'bpmn:FlowNode', + 'bpmn:InteractionNode', + 'bpmn:DataObjectReference', + 'bpmn:DataStoreReference', + 'bpmn:Group', + 'bpmn:TextAnnotation' + ]); + } + + function nonExistingOrLabel(element) { + return !element || isLabel$6(element); + } + + function isSame$1(a, b) { + return a === b; + } + + function getOrganizationalParent(element) { + + do { + if (is$1(element, 'bpmn:Process')) { + return getBusinessObject(element); + } + + if (is$1(element, 'bpmn:Participant')) { + return ( + getBusinessObject(element).processRef || + getBusinessObject(element) + ); + } + } while ((element = element.parent)); + + } + + function isTextAnnotation(element) { + return is$1(element, 'bpmn:TextAnnotation'); + } + + function isGroup(element) { + return is$1(element, 'bpmn:Group') && !element.labelTarget; + } + + function isCompensationBoundary(element) { + return is$1(element, 'bpmn:BoundaryEvent') && + hasEventDefinition(element, 'bpmn:CompensateEventDefinition'); + } + + function isForCompensation(e) { + return getBusinessObject(e).isForCompensation; + } + + function isSameOrganization(a, b) { + var parentA = getOrganizationalParent(a), + parentB = getOrganizationalParent(b); + + return parentA === parentB; + } + + function isMessageFlowSource(element) { + return ( + is$1(element, 'bpmn:InteractionNode') && + !is$1(element, 'bpmn:BoundaryEvent') && ( + !is$1(element, 'bpmn:Event') || ( + is$1(element, 'bpmn:ThrowEvent') && + hasEventDefinitionOrNone(element, 'bpmn:MessageEventDefinition') + ) + ) + ); + } + + function isMessageFlowTarget(element) { + return ( + is$1(element, 'bpmn:InteractionNode') && + !isForCompensation(element) && ( + !is$1(element, 'bpmn:Event') || ( + is$1(element, 'bpmn:CatchEvent') && + hasEventDefinitionOrNone(element, 'bpmn:MessageEventDefinition') + ) + ) && !( + is$1(element, 'bpmn:BoundaryEvent') && + !hasEventDefinition(element, 'bpmn:MessageEventDefinition') + ) + ); + } + + function getScopeParent(element) { + + var parent = element; + + while ((parent = parent.parent)) { + + if (is$1(parent, 'bpmn:FlowElementsContainer')) { + return getBusinessObject(parent); + } + + if (is$1(parent, 'bpmn:Participant')) { + return getBusinessObject(parent).processRef; + } + } + + return null; + } + + function isSameScope(a, b) { + var scopeParentA = getScopeParent(a), + scopeParentB = getScopeParent(b); + + return scopeParentA === scopeParentB; + } + + function hasEventDefinition(element, eventDefinition) { + var bo = getBusinessObject(element); + + return !!find(bo.eventDefinitions || [], function(definition) { + return is$1(definition, eventDefinition); + }); + } + + function hasEventDefinitionOrNone(element, eventDefinition) { + var bo = getBusinessObject(element); + + return (bo.eventDefinitions || []).every(function(definition) { + return is$1(definition, eventDefinition); + }); + } + + function isSequenceFlowSource(element) { + return ( + is$1(element, 'bpmn:FlowNode') && + !is$1(element, 'bpmn:EndEvent') && + !isEventSubProcess(element) && + !(is$1(element, 'bpmn:IntermediateThrowEvent') && + hasEventDefinition(element, 'bpmn:LinkEventDefinition') + ) && + !isCompensationBoundary(element) && + !isForCompensation(element) + ); + } + + function isSequenceFlowTarget(element) { + return ( + is$1(element, 'bpmn:FlowNode') && + !is$1(element, 'bpmn:StartEvent') && + !is$1(element, 'bpmn:BoundaryEvent') && + !isEventSubProcess(element) && + !(is$1(element, 'bpmn:IntermediateCatchEvent') && + hasEventDefinition(element, 'bpmn:LinkEventDefinition') + ) && + !isForCompensation(element) + ); + } + + function isEventBasedTarget(element) { + return ( + is$1(element, 'bpmn:ReceiveTask') || ( + is$1(element, 'bpmn:IntermediateCatchEvent') && ( + hasEventDefinition(element, 'bpmn:MessageEventDefinition') || + hasEventDefinition(element, 'bpmn:TimerEventDefinition') || + hasEventDefinition(element, 'bpmn:ConditionalEventDefinition') || + hasEventDefinition(element, 'bpmn:SignalEventDefinition') + ) + ) + ); + } + + function isConnection$8(element) { + return element.waypoints; + } + + function getParents(element) { + + var parents = []; + + while (element) { + element = element.parent; + + if (element) { + parents.push(element); + } + } + + return parents; + } + + function isParent(possibleParent, element) { + var allParents = getParents(element); + return allParents.indexOf(possibleParent) !== -1; + } + + function canConnect(source, target, connection) { + + if (nonExistingOrLabel(source) || nonExistingOrLabel(target)) { + return null; + } + + if (!is$1(connection, 'bpmn:DataAssociation')) { + + if (canConnectMessageFlow(source, target)) { + return { type: 'bpmn:MessageFlow' }; + } + + if (canConnectSequenceFlow(source, target)) { + return { type: 'bpmn:SequenceFlow' }; + } + } + + var connectDataAssociation = canConnectDataAssociation(source, target); + + if (connectDataAssociation) { + return connectDataAssociation; + } + + if (isCompensationBoundary(source) && isForCompensation(target)) { + return { + type: 'bpmn:Association', + associationDirection: 'One' + }; + } + + if (canConnectAssociation(source, target)) { + + return { + type: 'bpmn:Association' + }; + } + + return false; + } + + /** + * Can an element be dropped into the target element + * + * @return {boolean} + */ + function canDrop(element, target, position) { + + // can move labels and groups everywhere + if (isLabel$6(element) || isGroup(element)) { + return true; + } + + + // disallow to create elements on collapsed pools + if (is$1(target, 'bpmn:Participant') && !isExpanded(target)) { + return false; + } + + // allow to create new participants on + // existing collaboration and process diagrams + if (is$1(element, 'bpmn:Participant')) { + return is$1(target, 'bpmn:Process') || is$1(target, 'bpmn:Collaboration'); + } + + // allow moving DataInput / DataOutput within its original container only + if (isAny(element, [ 'bpmn:DataInput', 'bpmn:DataOutput' ])) { + + if (element.parent) { + return target === element.parent; + } + } + + // allow creating lanes on participants and other lanes only + if (is$1(element, 'bpmn:Lane')) { + return is$1(target, 'bpmn:Participant') || is$1(target, 'bpmn:Lane'); + } + + // disallow dropping boundary events which cannot replace with intermediate event + if (is$1(element, 'bpmn:BoundaryEvent') && !isDroppableBoundaryEvent(element)) { + return false; + } + + // drop flow elements onto flow element containers + // and participants + if (is$1(element, 'bpmn:FlowElement') && !is$1(element, 'bpmn:DataStoreReference')) { + if (is$1(target, 'bpmn:FlowElementsContainer')) { + return isExpanded(target); + } + + return isAny(target, [ 'bpmn:Participant', 'bpmn:Lane' ]); + } + + // disallow dropping data store reference if there is no process to append to + if (is$1(element, 'bpmn:DataStoreReference') && is$1(target, 'bpmn:Collaboration')) { + return some(getBusinessObject(target).get('participants'), function(participant) { + return !!participant.get('processRef'); + }); + } + + // account for the fact that data associations are always + // rendered and moved to top (Process or Collaboration level) + // + // artifacts may be placed wherever, too + if (isAny(element, [ 'bpmn:Artifact', 'bpmn:DataAssociation', 'bpmn:DataStoreReference' ])) { + return isAny(target, [ + 'bpmn:Collaboration', + 'bpmn:Lane', + 'bpmn:Participant', + 'bpmn:Process', + 'bpmn:SubProcess' ]); + } + + if (is$1(element, 'bpmn:MessageFlow')) { + return is$1(target, 'bpmn:Collaboration') + || element.source.parent == target + || element.target.parent == target; + } + + return false; + } + + function isDroppableBoundaryEvent(event) { + return getBusinessObject(event).cancelActivity && ( + hasNoEventDefinition(event) || hasCommonBoundaryIntermediateEventDefinition(event) + ); + } + + function isBoundaryEvent(element) { + return !isLabel$6(element) && is$1(element, 'bpmn:BoundaryEvent'); + } + + function isLane(element) { + return is$1(element, 'bpmn:Lane'); + } + + /** + * We treat IntermediateThrowEvents as boundary events during create, + * this must be reflected in the rules. + */ + function isBoundaryCandidate(element) { + if (isBoundaryEvent(element)) { + return true; + } + + if (is$1(element, 'bpmn:IntermediateThrowEvent') && hasNoEventDefinition(element)) { + return true; + } + + return ( + is$1(element, 'bpmn:IntermediateCatchEvent') && + hasCommonBoundaryIntermediateEventDefinition(element) + ); + } + + function hasNoEventDefinition(element) { + var bo = getBusinessObject(element); + + return bo && !(bo.eventDefinitions && bo.eventDefinitions.length); + } + + function hasCommonBoundaryIntermediateEventDefinition(element) { + return hasOneOfEventDefinitions(element, [ + 'bpmn:MessageEventDefinition', + 'bpmn:TimerEventDefinition', + 'bpmn:SignalEventDefinition', + 'bpmn:ConditionalEventDefinition' + ]); + } + + function hasOneOfEventDefinitions(element, eventDefinitions) { + return eventDefinitions.some(function(definition) { + return hasEventDefinition(element, definition); + }); + } + + function isReceiveTaskAfterEventBasedGateway(element) { + return ( + is$1(element, 'bpmn:ReceiveTask') && + find(element.incoming, function(incoming) { + return is$1(incoming.source, 'bpmn:EventBasedGateway'); + }) + ); + } + + + function canAttach(elements, target, source, position) { + + if (!Array.isArray(elements)) { + elements = [ elements ]; + } + + // only (re-)attach one element at a time + if (elements.length !== 1) { + return false; + } + + var element = elements[0]; + + // do not attach labels + if (isLabel$6(element)) { + return false; + } + + // only handle boundary events + if (!isBoundaryCandidate(element)) { + return false; + } + + // disallow drop on event sub processes + if (isEventSubProcess(target)) { + return false; + } + + // only allow drop on non compensation activities + if (!is$1(target, 'bpmn:Activity') || isForCompensation(target)) { + return false; + } + + // only attach to subprocess border + if (position && !getBoundaryAttachment(position, target)) { + return false; + } + + // do not attach on receive tasks after event based gateways + if (isReceiveTaskAfterEventBasedGateway(target)) { + return false; + } + + return 'attach'; + } + + + /** + * Defines how to replace elements for a given target. + * + * Returns an array containing all elements which will be replaced. + * + * @example + * + * [{ id: 'IntermediateEvent_2', + * type: 'bpmn:StartEvent' + * }, + * { id: 'IntermediateEvent_5', + * type: 'bpmn:EndEvent' + * }] + * + * @param {Array} elements + * @param {Object} target + * + * @return {Object} an object containing all elements which have to be replaced + */ + function canReplace(elements, target, position) { + + if (!target) { + return false; + } + + var canExecute = { + replacements: [] + }; + + forEach$1(elements, function(element) { + + if (!isEventSubProcess(target)) { + + if (is$1(element, 'bpmn:StartEvent') && + element.type !== 'label' && + canDrop(element, target)) { + + // replace a non-interrupting start event by a blank interrupting start event + // when the target is not an event sub process + if (!isInterrupting(element)) { + canExecute.replacements.push({ + oldElementId: element.id, + newElementType: 'bpmn:StartEvent' + }); + } + + // replace an error/escalation/compensate start event by a blank interrupting start event + // when the target is not an event sub process + if (hasErrorEventDefinition(element) || + hasEscalationEventDefinition(element) || + hasCompensateEventDefinition(element)) { + canExecute.replacements.push({ + oldElementId: element.id, + newElementType: 'bpmn:StartEvent' + }); + } + + // replace a typed start event by a blank interrupting start event + // when the target is a sub process but not an event sub process + if (hasOneOfEventDefinitions(element, + [ + 'bpmn:MessageEventDefinition', + 'bpmn:TimerEventDefinition', + 'bpmn:SignalEventDefinition', + 'bpmn:ConditionalEventDefinition' + ]) && + is$1(target, 'bpmn:SubProcess')) { + canExecute.replacements.push({ + oldElementId: element.id, + newElementType: 'bpmn:StartEvent' + }); + } + } + } + + if (!is$1(target, 'bpmn:Transaction')) { + if (hasEventDefinition(element, 'bpmn:CancelEventDefinition') && + element.type !== 'label') { + + if (is$1(element, 'bpmn:EndEvent') && canDrop(element, target)) { + canExecute.replacements.push({ + oldElementId: element.id, + newElementType: 'bpmn:EndEvent' + }); + } + + if (is$1(element, 'bpmn:BoundaryEvent') && canAttach(element, target, null, position)) { + canExecute.replacements.push({ + oldElementId: element.id, + newElementType: 'bpmn:BoundaryEvent' + }); + } + } + } + }); + + return canExecute.replacements.length ? canExecute : false; + } + + function canMove(elements, target) { + + // do not move selection containing lanes + if (some(elements, isLane)) { + return false; + } + + // allow default move check to start move operation + if (!target) { + return true; + } + + return elements.every(function(element) { + return canDrop(element, target); + }); + } + + function canCreate(shape, target, source, position) { + + if (!target) { + return false; + } + + if (isLabel$6(shape) || isGroup(shape)) { + return true; + } + + if (isSame$1(source, target)) { + return false; + } + + // ensure we do not drop the element + // into source + if (source && isParent(source, target)) { + return false; + } + + return canDrop(shape, target) || canInsert(shape, target); + } + + function canResize(shape, newBounds) { + if (is$1(shape, 'bpmn:SubProcess')) { + return ( + isExpanded(shape) && ( + !newBounds || (newBounds.width >= 100 && newBounds.height >= 80) + ) + ); + } + + if (is$1(shape, 'bpmn:Lane')) { + return !newBounds || (newBounds.width >= 130 && newBounds.height >= 60); + } + + if (is$1(shape, 'bpmn:Participant')) { + return !newBounds || (newBounds.width >= 250 && newBounds.height >= 50); + } + + if (isTextAnnotation(shape)) { + return true; + } + + if (isGroup(shape)) { + return true; + } + + return false; + } + + /** + * Check, whether one side of the relationship + * is a text annotation. + */ + function isOneTextAnnotation(source, target) { + + var sourceTextAnnotation = isTextAnnotation(source), + targetTextAnnotation = isTextAnnotation(target); + + return ( + (sourceTextAnnotation || targetTextAnnotation) && + (sourceTextAnnotation !== targetTextAnnotation) + ); + } + + + function canConnectAssociation(source, target) { + + // compensation boundary events are exception + if (isCompensationBoundary(source) && isForCompensation(target)) { + return true; + } + + // don't connect parent <-> child + if (isParent(target, source) || isParent(source, target)) { + return false; + } + + // allow connection of associations between and + if (isOneTextAnnotation(source, target)) { + return true; + } + + // can connect associations where we can connect + // data associations, too (!) + return !!canConnectDataAssociation(source, target); + } + + function canConnectMessageFlow(source, target) { + + // during connect user might move mouse out of canvas + // https://github.com/bpmn-io/bpmn-js/issues/1033 + if (getRootElement(source) && !getRootElement(target)) { + return false; + } + + return ( + isMessageFlowSource(source) && + isMessageFlowTarget(target) && + !isSameOrganization(source, target) + ); + } + + function canConnectSequenceFlow(source, target) { + + if ( + isEventBasedTarget(target) && + target.incoming.length > 0 && + areOutgoingEventBasedGatewayConnections(target.incoming) && + !is$1(source, 'bpmn:EventBasedGateway') + ) { + return false; + } + + return isSequenceFlowSource(source) && + isSequenceFlowTarget(target) && + isSameScope(source, target) && + !(is$1(source, 'bpmn:EventBasedGateway') && !isEventBasedTarget(target)); + } + + + function canConnectDataAssociation(source, target) { + + if (isAny(source, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ]) && + isAny(target, [ 'bpmn:Activity', 'bpmn:ThrowEvent' ])) { + return { type: 'bpmn:DataInputAssociation' }; + } + + if (isAny(target, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ]) && + isAny(source, [ 'bpmn:Activity', 'bpmn:CatchEvent' ])) { + return { type: 'bpmn:DataOutputAssociation' }; + } + + return false; + } + + function canInsert(shape, flow, position) { + + if (!flow) { + return false; + } + + if (Array.isArray(shape)) { + if (shape.length !== 1) { + return false; + } + + shape = shape[0]; + } + + if (flow.source === shape || + flow.target === shape) { + return false; + } + + // return true if we can drop on the + // underlying flow parent + // + // at this point we are not really able to talk + // about connection rules (yet) + + return ( + isAny(flow, [ 'bpmn:SequenceFlow', 'bpmn:MessageFlow' ]) && + !isLabel$6(flow) && + is$1(shape, 'bpmn:FlowNode') && + !is$1(shape, 'bpmn:BoundaryEvent') && + canDrop(shape, flow.parent)); + } + + function includes$5(elements, element) { + return (elements && element) && elements.indexOf(element) !== -1; + } + + function canCopy(elements, element) { + if (isLabel$6(element)) { + return true; + } + + if (is$1(element, 'bpmn:Lane') && !includes$5(elements, element.parent)) { + return false; + } + + return true; + } + + function isOutgoingEventBasedGatewayConnection(connection) { + + if (connection && connection.source) { + return is$1(connection.source, 'bpmn:EventBasedGateway'); + } + } + + function areOutgoingEventBasedGatewayConnections(connections) { + connections = connections || []; + + return connections.some(isOutgoingEventBasedGatewayConnection); + } + + function getRootElement(element) { + return getParent(element, 'bpmn:Process') || getParent(element, 'bpmn:Collaboration'); + } + + var RulesModule = { + __depends__: [ + RulesModule$1 + ], + __init__: [ 'bpmnRules' ], + bpmnRules: [ 'type', BpmnRules ] + }; + + var HIGH_PRIORITY$6 = 2000; + + function BpmnDiOrdering(eventBus, canvas) { + + eventBus.on('saveXML.start', HIGH_PRIORITY$6, orderDi); + + function orderDi() { + var rootElements = canvas.getRootElements(); + + forEach$1(rootElements, function(root) { + var rootDi = getDi(root), + elements, + diElements; + + elements = selfAndAllChildren([ root ], false); + + // only bpmndi:Shape and bpmndi:Edge can be direct children of bpmndi:Plane + elements = filter(elements, function(element) { + return element !== root && !element.labelTarget; + }); + + diElements = map(elements, getDi); + + rootDi.set('planeElement', diElements); + }); + } + } + + BpmnDiOrdering.$inject = [ 'eventBus', 'canvas' ]; + + var DiOrderingModule = { + __init__: [ + 'bpmnDiOrdering' + ], + bpmnDiOrdering: [ 'type', BpmnDiOrdering ] + }; + + /** + * An abstract provider that allows modelers to implement a custom + * ordering of diagram elements on the canvas. + * + * It makes sure that the order is always preserved during element + * creation and move operations. + * + * In order to use this behavior, inherit from it and override + * the method {@link OrderingProvider#getOrdering}. + * + * @example + * + * ```javascript + * function CustomOrderingProvider(eventBus) { + * OrderingProvider.call(this, eventBus); + * + * this.getOrdering = function(element, newParent) { + * // always insert elements at the front + * // when moving + * return { + * index: 0, + * parent: newParent + * }; + * }; + * } + * ``` + * + * @param {EventBus} eventBus + */ + function OrderingProvider(eventBus) { + + CommandInterceptor.call(this, eventBus); + + + var self = this; + + this.preExecute([ 'shape.create', 'connection.create' ], function(event) { + + var context = event.context, + element = context.shape || context.connection, + parent = context.parent; + + var ordering = self.getOrdering(element, parent); + + if (ordering) { + + if (ordering.parent !== undefined) { + context.parent = ordering.parent; + } + + context.parentIndex = ordering.index; + } + }); + + this.preExecute([ 'shape.move', 'connection.move' ], function(event) { + + var context = event.context, + element = context.shape || context.connection, + parent = context.newParent || element.parent; + + var ordering = self.getOrdering(element, parent); + + if (ordering) { + + if (ordering.parent !== undefined) { + context.newParent = ordering.parent; + } + + context.newParentIndex = ordering.index; + } + }); + } + + /** + * Return a custom ordering of the element, both in terms + * of parent element and index in the new parent. + * + * Implementors of this method must return an object with + * `parent` _and_ `index` in it. + * + * @param {djs.model.Base} element + * @param {djs.model.Shape} newParent + * + * @return {Object} ordering descriptor + */ + OrderingProvider.prototype.getOrdering = function(element, newParent) { + return null; + }; + + e(OrderingProvider, CommandInterceptor); + + /** + * a simple ordering provider that makes sure: + * + * (0) labels and groups are rendered always on top + * (1) elements are ordered by a {level} property + */ + function BpmnOrderingProvider(eventBus, canvas, translate) { + + OrderingProvider.call(this, eventBus); + + var orders = [ + { type: 'bpmn:SubProcess', order: { level: 6 } }, + + // handle SequenceFlow(s) like message flows and render them always on top + { + type: 'bpmn:SequenceFlow', + order: { + level: 9, + containers: [ + 'bpmn:Participant', + 'bpmn:FlowElementsContainer' + ] + } + }, + + // handle DataAssociation(s) like message flows and render them always on top + { + type: 'bpmn:DataAssociation', + order: { + level: 9, + containers: [ + 'bpmn:Collaboration', + 'bpmn:FlowElementsContainer' + ] + } + }, + { + type: 'bpmn:MessageFlow', order: { + level: 9, + containers: [ 'bpmn:Collaboration' ] + } + }, + { + type: 'bpmn:Association', + order: { + level: 6, + containers: [ + 'bpmn:Participant', + 'bpmn:FlowElementsContainer', + 'bpmn:Collaboration' + ] + } + }, + { type: 'bpmn:BoundaryEvent', order: { level: 8 } }, + { + type: 'bpmn:Group', + order: { + level: 10, + containers: [ + 'bpmn:Collaboration', + 'bpmn:FlowElementsContainer' + ] + } + }, + { type: 'bpmn:FlowElement', order: { level: 5 } }, + { type: 'bpmn:Participant', order: { level: -2 } }, + { type: 'bpmn:Lane', order: { level: -1 } } + ]; + + function computeOrder(element) { + if (element.labelTarget) { + return { level: 10 }; + } + + var entry = find(orders, function(o) { + return isAny(element, [ o.type ]); + }); + + return entry && entry.order || { level: 1 }; + } + + function getOrder(element) { + + var order = element.order; + + if (!order) { + element.order = order = computeOrder(element); + } + + if (!order) { + throw new Error('no order for <' + element.id + '>'); + } + + return order; + } + + function findActualParent(element, newParent, containers) { + + var actualParent = newParent; + + while (actualParent) { + + if (isAny(actualParent, containers)) { + break; + } + + actualParent = actualParent.parent; + } + + if (!actualParent) { + throw new Error('no parent for <' + element.id + '> in <' + (newParent && newParent.id) + '>'); + } + + return actualParent; + } + + this.getOrdering = function(element, newParent) { + + // render labels always on top + if (element.labelTarget) { + return { + parent: canvas.findRoot(newParent) || canvas.getRootElement(), + index: -1 + }; + } + + var elementOrder = getOrder(element); + + if (elementOrder.containers) { + newParent = findActualParent(element, newParent, elementOrder.containers); + } + + var currentIndex = newParent.children.indexOf(element); + + var insertIndex = findIndex(newParent.children, function(child) { + + // do not compare with labels, they are created + // in the wrong order (right after elements) during import and + // mess up the positioning. + if (!element.labelTarget && child.labelTarget) { + return false; + } + + return elementOrder.level < getOrder(child).level; + }); + + + // if the element is already in the child list at + // a smaller index, we need to adjust the insert index. + // this takes into account that the element is being removed + // before being re-inserted + if (insertIndex !== -1) { + if (currentIndex !== -1 && currentIndex < insertIndex) { + insertIndex -= 1; + } + } + + return { + index: insertIndex, + parent: newParent + }; + }; + } + + BpmnOrderingProvider.$inject = [ 'eventBus', 'canvas', 'translate' ]; + + e(BpmnOrderingProvider, OrderingProvider); + + var OrderingModule = { + __depends__: [ + translate + ], + __init__: [ 'bpmnOrderingProvider' ], + bpmnOrderingProvider: [ 'type', BpmnOrderingProvider ] + }; + + /** + * A service that offers un- and redoable execution of commands. + * + * The command stack is responsible for executing modeling actions + * in a un- and redoable manner. To do this it delegates the actual + * command execution to {@link CommandHandler}s. + * + * Command handlers provide {@link CommandHandler#execute(ctx)} and + * {@link CommandHandler#revert(ctx)} methods to un- and redo a command + * identified by a command context. + * + * + * ## Life-Cycle events + * + * In the process the command stack fires a number of life-cycle events + * that other components to participate in the command execution. + * + * * preExecute + * * preExecuted + * * execute + * * executed + * * postExecute + * * postExecuted + * * revert + * * reverted + * + * A special event is used for validating, whether a command can be + * performed prior to its execution. + * + * * canExecute + * + * Each of the events is fired as `commandStack.{eventName}` and + * `commandStack.{commandName}.{eventName}`, respectively. This gives + * components fine grained control on where to hook into. + * + * The event object fired transports `command`, the name of the + * command and `context`, the command context. + * + * + * ## Creating Command Handlers + * + * Command handlers should provide the {@link CommandHandler#execute(ctx)} + * and {@link CommandHandler#revert(ctx)} methods to implement + * redoing and undoing of a command. + * + * A command handler _must_ ensure undo is performed properly in order + * not to break the undo chain. It must also return the shapes that + * got changed during the `execute` and `revert` operations. + * + * Command handlers may execute other modeling operations (and thus + * commands) in their `preExecute` and `postExecute` phases. The command + * stack will properly group all commands together into a logical unit + * that may be re- and undone atomically. + * + * Command handlers must not execute other commands from within their + * core implementation (`execute`, `revert`). + * + * + * ## Change Tracking + * + * During the execution of the CommandStack it will keep track of all + * elements that have been touched during the command's execution. + * + * At the end of the CommandStack execution it will notify interested + * components via an 'elements.changed' event with all the dirty + * elements. + * + * The event can be picked up by components that are interested in the fact + * that elements have been changed. One use case for this is updating + * their graphical representation after moving / resizing or deletion. + * + * @see CommandHandler + * + * @param {EventBus} eventBus + * @param {Injector} injector + */ + function CommandStack(eventBus, injector) { + + /** + * A map of all registered command handlers. + * + * @type {Object} + */ + this._handlerMap = {}; + + /** + * A stack containing all re/undoable actions on the diagram + * + * @type {Array} + */ + this._stack = []; + + /** + * The current index on the stack + * + * @type {number} + */ + this._stackIdx = -1; + + /** + * Current active commandStack execution + * + * @type {Object} + * @property {Object[]} actions + * @property {Object[]} dirty + * @property { 'undo' | 'redo' | 'clear' | 'execute' | null } trigger the cause of the current excecution + */ + this._currentExecution = { + actions: [], + dirty: [], + trigger: null + }; + + + this._injector = injector; + this._eventBus = eventBus; + + this._uid = 1; + + eventBus.on([ + 'diagram.destroy', + 'diagram.clear' + ], function() { + this.clear(false); + }, this); + } + + CommandStack.$inject = [ 'eventBus', 'injector' ]; + + + /** + * Execute a command + * + * @param {string} command the command to execute + * @param {Object} context the environment to execute the command in + */ + CommandStack.prototype.execute = function(command, context) { + if (!command) { + throw new Error('command required'); + } + + this._currentExecution.trigger = 'execute'; + + var action = { command: command, context: context }; + + this._pushAction(action); + this._internalExecute(action); + this._popAction(action); + }; + + + /** + * Ask whether a given command can be executed. + * + * Implementors may hook into the mechanism on two ways: + * + * * in event listeners: + * + * Users may prevent the execution via an event listener. + * It must prevent the default action for `commandStack.(.)canExecute` events. + * + * * in command handlers: + * + * If the method {@link CommandHandler#canExecute} is implemented in a handler + * it will be called to figure out whether the execution is allowed. + * + * @param {string} command the command to execute + * @param {Object} context the environment to execute the command in + * + * @return {boolean} true if the command can be executed + */ + CommandStack.prototype.canExecute = function(command, context) { + + var action = { command: command, context: context }; + + var handler = this._getHandler(command); + + var result = this._fire(command, 'canExecute', action); + + // handler#canExecute will only be called if no listener + // decided on a result already + if (result === undefined) { + if (!handler) { + return false; + } + + if (handler.canExecute) { + result = handler.canExecute(context); + } + } + + return result; + }; + + + /** + * Clear the command stack, erasing all undo / redo history + */ + CommandStack.prototype.clear = function(emit) { + this._stack.length = 0; + this._stackIdx = -1; + + if (emit !== false) { + this._fire('changed', { trigger: 'clear' }); + } + }; + + + /** + * Undo last command(s) + */ + CommandStack.prototype.undo = function() { + var action = this._getUndoAction(), + next; + + if (action) { + this._currentExecution.trigger = 'undo'; + + this._pushAction(action); + + while (action) { + this._internalUndo(action); + next = this._getUndoAction(); + + if (!next || next.id !== action.id) { + break; + } + + action = next; + } + + this._popAction(); + } + }; + + + /** + * Redo last command(s) + */ + CommandStack.prototype.redo = function() { + var action = this._getRedoAction(), + next; + + if (action) { + this._currentExecution.trigger = 'redo'; + + this._pushAction(action); + + while (action) { + this._internalExecute(action, true); + next = this._getRedoAction(); + + if (!next || next.id !== action.id) { + break; + } + + action = next; + } + + this._popAction(); + } + }; + + + /** + * Register a handler instance with the command stack + * + * @param {string} command + * @param {CommandHandler} handler + */ + CommandStack.prototype.register = function(command, handler) { + this._setHandler(command, handler); + }; + + + /** + * Register a handler type with the command stack + * by instantiating it and injecting its dependencies. + * + * @param {string} command + * @param {Function} a constructor for a {@link CommandHandler} + */ + CommandStack.prototype.registerHandler = function(command, handlerCls) { + + if (!command || !handlerCls) { + throw new Error('command and handlerCls must be defined'); + } + + var handler = this._injector.instantiate(handlerCls); + this.register(command, handler); + }; + + CommandStack.prototype.canUndo = function() { + return !!this._getUndoAction(); + }; + + CommandStack.prototype.canRedo = function() { + return !!this._getRedoAction(); + }; + + // stack access ////////////////////// + + CommandStack.prototype._getRedoAction = function() { + return this._stack[this._stackIdx + 1]; + }; + + + CommandStack.prototype._getUndoAction = function() { + return this._stack[this._stackIdx]; + }; + + + // internal functionality ////////////////////// + + CommandStack.prototype._internalUndo = function(action) { + var self = this; + + var command = action.command, + context = action.context; + + var handler = this._getHandler(command); + + // guard against illegal nested command stack invocations + this._atomicDo(function() { + self._fire(command, 'revert', action); + + if (handler.revert) { + self._markDirty(handler.revert(context)); + } + + self._revertedAction(action); + + self._fire(command, 'reverted', action); + }); + }; + + + CommandStack.prototype._fire = function(command, qualifier, event) { + if (arguments.length < 3) { + event = qualifier; + qualifier = null; + } + + var names = qualifier ? [ command + '.' + qualifier, qualifier ] : [ command ], + i, name, result; + + event = this._eventBus.createEvent(event); + + for (i = 0; (name = names[i]); i++) { + result = this._eventBus.fire('commandStack.' + name, event); + + if (event.cancelBubble) { + break; + } + } + + return result; + }; + + CommandStack.prototype._createId = function() { + return this._uid++; + }; + + CommandStack.prototype._atomicDo = function(fn) { + + var execution = this._currentExecution; + + execution.atomic = true; + + try { + fn(); + } finally { + execution.atomic = false; + } + }; + + CommandStack.prototype._internalExecute = function(action, redo) { + var self = this; + + var command = action.command, + context = action.context; + + var handler = this._getHandler(command); + + if (!handler) { + throw new Error('no command handler registered for <' + command + '>'); + } + + this._pushAction(action); + + if (!redo) { + this._fire(command, 'preExecute', action); + + if (handler.preExecute) { + handler.preExecute(context); + } + + this._fire(command, 'preExecuted', action); + } + + // guard against illegal nested command stack invocations + this._atomicDo(function() { + + self._fire(command, 'execute', action); + + if (handler.execute) { + + // actual execute + mark return results as dirty + self._markDirty(handler.execute(context)); + } + + // log to stack + self._executedAction(action, redo); + + self._fire(command, 'executed', action); + }); + + if (!redo) { + this._fire(command, 'postExecute', action); + + if (handler.postExecute) { + handler.postExecute(context); + } + + this._fire(command, 'postExecuted', action); + } + + this._popAction(action); + }; + + + CommandStack.prototype._pushAction = function(action) { + + var execution = this._currentExecution, + actions = execution.actions; + + var baseAction = actions[0]; + + if (execution.atomic) { + throw new Error('illegal invocation in or phase (action: ' + action.command + ')'); + } + + if (!action.id) { + action.id = (baseAction && baseAction.id) || this._createId(); + } + + actions.push(action); + }; + + + CommandStack.prototype._popAction = function() { + var execution = this._currentExecution, + trigger = execution.trigger, + actions = execution.actions, + dirty = execution.dirty; + + actions.pop(); + + if (!actions.length) { + this._eventBus.fire('elements.changed', { elements: uniqueBy('id', dirty.reverse()) }); + + dirty.length = 0; + + this._fire('changed', { trigger: trigger }); + + execution.trigger = null; + } + }; + + + CommandStack.prototype._markDirty = function(elements) { + var execution = this._currentExecution; + + if (!elements) { + return; + } + + elements = isArray$3(elements) ? elements : [ elements ]; + + execution.dirty = execution.dirty.concat(elements); + }; + + + CommandStack.prototype._executedAction = function(action, redo) { + var stackIdx = ++this._stackIdx; + + if (!redo) { + this._stack.splice(stackIdx, this._stack.length, action); + } + }; + + + CommandStack.prototype._revertedAction = function(action) { + this._stackIdx--; + }; + + + CommandStack.prototype._getHandler = function(command) { + return this._handlerMap[command]; + }; + + CommandStack.prototype._setHandler = function(command, handler) { + if (!command || !handler) { + throw new Error('command and handler required'); + } + + if (this._handlerMap[command]) { + throw new Error('overriding handler for command <' + command + '>'); + } + + this._handlerMap[command] = handler; + }; + + var CommandModule = { + commandStack: [ 'type', CommandStack ] + }; + + // document wide unique tooltip ids + var ids = new IdGenerator('tt'); + + + function createRoot(parentNode) { + var root = domify( + '
    ' + ); + + assign$1(root, { + position: 'absolute', + width: '0', + height: '0' + }); + + parentNode.insertBefore(root, parentNode.firstChild); + + return root; + } + + + function setPosition(el, x, y) { + assign$1(el, { left: x + 'px', top: y + 'px' }); + } + + function setVisible(el, visible) { + el.style.display = visible === false ? 'none' : ''; + } + + + var tooltipClass = 'djs-tooltip', + tooltipSelector = '.' + tooltipClass; + + /** + * A service that allows users to render tool tips on the diagram. + * + * The tooltip service will take care of updating the tooltip positioning + * during navigation + zooming. + * + * @example + * + * ```javascript + * + * // add a pink badge on the top left of the shape + * tooltips.add({ + * position: { + * x: 50, + * y: 100 + * }, + * html: '
    0
    ' + * }); + * + * // or with optional life span + * tooltips.add({ + * position: { + * top: -5, + * left: -5 + * }, + * html: '
    0
    ', + * ttl: 2000 + * }); + * + * // remove a tool tip + * var id = tooltips.add(...); + * tooltips.remove(id); + * ``` + * + * @param {EventBus} eventBus + * @param {Canvas} canvas + */ + function Tooltips(eventBus, canvas) { + + this._eventBus = eventBus; + this._canvas = canvas; + + this._ids = ids; + + this._tooltipDefaults = { + show: { + minZoom: 0.7, + maxZoom: 5.0 + } + }; + + /** + * Mapping tooltipId -> tooltip + */ + this._tooltips = {}; + + // root html element for all tooltips + this._tooltipRoot = createRoot(canvas.getContainer()); + + + var self = this; + + delegate.bind(this._tooltipRoot, tooltipSelector, 'mousedown', function(event) { + event.stopPropagation(); + }); + + delegate.bind(this._tooltipRoot, tooltipSelector, 'mouseover', function(event) { + self.trigger('mouseover', event); + }); + + delegate.bind(this._tooltipRoot, tooltipSelector, 'mouseout', function(event) { + self.trigger('mouseout', event); + }); + + this._init(); + } + + + Tooltips.$inject = [ 'eventBus', 'canvas' ]; + + + /** + * Adds a HTML tooltip to the diagram + * + * @param {Object} tooltip the tooltip configuration + * + * @param {string|DOMElement} tooltip.html html element to use as an tooltip + * @param {Object} [tooltip.show] show configuration + * @param {number} [tooltip.show.minZoom] minimal zoom level to show the tooltip + * @param {number} [tooltip.show.maxZoom] maximum zoom level to show the tooltip + * @param {Object} tooltip.position where to attach the tooltip + * @param {number} [tooltip.position.left] relative to element bbox left attachment + * @param {number} [tooltip.position.top] relative to element bbox top attachment + * @param {number} [tooltip.position.bottom] relative to element bbox bottom attachment + * @param {number} [tooltip.position.right] relative to element bbox right attachment + * @param {number} [tooltip.timeout=-1] + * + * @return {string} id that may be used to reference the tooltip for update or removal + */ + Tooltips.prototype.add = function(tooltip) { + + if (!tooltip.position) { + throw new Error('must specifiy tooltip position'); + } + + if (!tooltip.html) { + throw new Error('must specifiy tooltip html'); + } + + var id = this._ids.next(); + + tooltip = assign({}, this._tooltipDefaults, tooltip, { + id: id + }); + + this._addTooltip(tooltip); + + if (tooltip.timeout) { + this.setTimeout(tooltip); + } + + return id; + }; + + Tooltips.prototype.trigger = function(action, event) { + + var node = event.delegateTarget || event.target; + + var tooltip = this.get(attr$1(node, 'data-tooltip-id')); + + if (!tooltip) { + return; + } + + if (action === 'mouseover' && tooltip.timeout) { + this.clearTimeout(tooltip); + } + + if (action === 'mouseout' && tooltip.timeout) { + + // cut timeout after mouse out + tooltip.timeout = 1000; + + this.setTimeout(tooltip); + } + }; + + /** + * Get a tooltip with the given id + * + * @param {string} id + */ + Tooltips.prototype.get = function(id) { + + if (typeof id !== 'string') { + id = id.id; + } + + return this._tooltips[id]; + }; + + Tooltips.prototype.clearTimeout = function(tooltip) { + + tooltip = this.get(tooltip); + + if (!tooltip) { + return; + } + + var removeTimer = tooltip.removeTimer; + + if (removeTimer) { + clearTimeout(removeTimer); + tooltip.removeTimer = null; + } + }; + + Tooltips.prototype.setTimeout = function(tooltip) { + + tooltip = this.get(tooltip); + + if (!tooltip) { + return; + } + + this.clearTimeout(tooltip); + + var self = this; + + tooltip.removeTimer = setTimeout(function() { + self.remove(tooltip); + }, tooltip.timeout); + }; + + /** + * Remove an tooltip with the given id + * + * @param {string} id + */ + Tooltips.prototype.remove = function(id) { + + var tooltip = this.get(id); + + if (tooltip) { + remove$2(tooltip.html); + remove$2(tooltip.htmlContainer); + + delete tooltip.htmlContainer; + + delete this._tooltips[tooltip.id]; + } + }; + + + Tooltips.prototype.show = function() { + setVisible(this._tooltipRoot); + }; + + + Tooltips.prototype.hide = function() { + setVisible(this._tooltipRoot, false); + }; + + + Tooltips.prototype._updateRoot = function(viewbox) { + var a = viewbox.scale || 1; + var d = viewbox.scale || 1; + + var matrix = 'matrix(' + a + ',0,0,' + d + ',' + (-1 * viewbox.x * a) + ',' + (-1 * viewbox.y * d) + ')'; + + this._tooltipRoot.style.transform = matrix; + this._tooltipRoot.style['-ms-transform'] = matrix; + }; + + + Tooltips.prototype._addTooltip = function(tooltip) { + + var id = tooltip.id, + html = tooltip.html, + htmlContainer, + tooltipRoot = this._tooltipRoot; + + // unwrap jquery (for those who need it) + if (html.get && html.constructor.prototype.jquery) { + html = html.get(0); + } + + // create proper html elements from + // tooltip HTML strings + if (isString(html)) { + html = domify(html); + } + + htmlContainer = domify('
    '); + assign$1(htmlContainer, { position: 'absolute' }); + + htmlContainer.appendChild(html); + + if (tooltip.type) { + classes$1(htmlContainer).add('djs-tooltip-' + tooltip.type); + } + + if (tooltip.className) { + classes$1(htmlContainer).add(tooltip.className); + } + + tooltip.htmlContainer = htmlContainer; + + tooltipRoot.appendChild(htmlContainer); + + this._tooltips[id] = tooltip; + + this._updateTooltip(tooltip); + }; + + + Tooltips.prototype._updateTooltip = function(tooltip) { + + var position = tooltip.position, + htmlContainer = tooltip.htmlContainer; + + // update overlay html based on tooltip x, y + + setPosition(htmlContainer, position.x, position.y); + }; + + + Tooltips.prototype._updateTooltipVisibilty = function(viewbox) { + + forEach$1(this._tooltips, function(tooltip) { + var show = tooltip.show, + htmlContainer = tooltip.htmlContainer, + visible = true; + + if (show) { + if (show.minZoom > viewbox.scale || + show.maxZoom < viewbox.scale) { + visible = false; + } + + setVisible(htmlContainer, visible); + } + }); + }; + + Tooltips.prototype._init = function() { + + var self = this; + + // scroll/zoom integration + + function updateViewbox(viewbox) { + self._updateRoot(viewbox); + self._updateTooltipVisibilty(viewbox); + + self.show(); + } + + this._eventBus.on('canvas.viewbox.changing', function(event) { + self.hide(); + }); + + this._eventBus.on('canvas.viewbox.changed', function(event) { + updateViewbox(event.viewbox); + }); + }; + + var TooltipsModule = { + __init__: [ 'tooltips' ], + tooltips: [ 'type', Tooltips ] + }; + + /** + * Remove from the beginning of a collection until it is empty. + * + * This is a null-safe operation that ensures elements + * are being removed from the given collection until the + * collection is empty. + * + * The implementation deals with the fact that a remove operation + * may touch, i.e. remove multiple elements in the collection + * at a time. + * + * @param {Array} [collection] + * @param {Function} removeFn + * + * @return {Array} the cleared collection + */ + function saveClear(collection, removeFn) { + + if (typeof removeFn !== 'function') { + throw new Error('removeFn iterator must be a function'); + } + + if (!collection) { + return; + } + + var e; + + while ((e = collection[0])) { + removeFn(e); + } + + return collection; + } + + var LOW_PRIORITY$6 = 250, + HIGH_PRIORITY$5 = 1400; + + + /** + * A handler that makes sure labels are properly moved with + * their label targets. + * + * @param {didi.Injector} injector + * @param {EventBus} eventBus + * @param {Modeling} modeling + */ + function LabelSupport(injector, eventBus, modeling) { + + CommandInterceptor.call(this, eventBus); + + var movePreview = injector.get('movePreview', false); + + // remove labels from the collection that are being + // moved with other elements anyway + eventBus.on('shape.move.start', HIGH_PRIORITY$5, function(e) { + + var context = e.context, + shapes = context.shapes, + validatedShapes = context.validatedShapes; + + context.shapes = removeLabels(shapes); + context.validatedShapes = removeLabels(validatedShapes); + }); + + // add labels to visual's group + movePreview && eventBus.on('shape.move.start', LOW_PRIORITY$6, function(e) { + + var context = e.context, + shapes = context.shapes; + + var labels = []; + + forEach$1(shapes, function(element) { + + forEach$1(element.labels, function(label) { + + if (!label.hidden && context.shapes.indexOf(label) === -1) { + labels.push(label); + } + + if (element.labelTarget) { + labels.push(element); + } + }); + }); + + forEach$1(labels, function(label) { + movePreview.makeDraggable(context, label, true); + }); + + }); + + // add all labels to move closure + this.preExecuted('elements.move', HIGH_PRIORITY$5, function(e) { + var context = e.context, + closure = context.closure, + enclosedElements = closure.enclosedElements; + + var enclosedLabels = []; + + // find labels that are not part of + // move closure yet and add them + forEach$1(enclosedElements, function(element) { + forEach$1(element.labels, function(label) { + + if (!enclosedElements[label.id]) { + enclosedLabels.push(label); + } + }); + }); + + closure.addAll(enclosedLabels); + }); + + + this.preExecute([ + 'connection.delete', + 'shape.delete' + ], function(e) { + + var context = e.context, + element = context.connection || context.shape; + + saveClear(element.labels, function(label) { + modeling.removeShape(label, { nested: true }); + }); + }); + + + this.execute('shape.delete', function(e) { + + var context = e.context, + shape = context.shape, + labelTarget = shape.labelTarget; + + // unset labelTarget + if (labelTarget) { + context.labelTargetIndex = indexOf(labelTarget.labels, shape); + context.labelTarget = labelTarget; + + shape.labelTarget = null; + } + }); + + this.revert('shape.delete', function(e) { + + var context = e.context, + shape = context.shape, + labelTarget = context.labelTarget, + labelTargetIndex = context.labelTargetIndex; + + // restore labelTarget + if (labelTarget) { + add(labelTarget.labels, shape, labelTargetIndex); + + shape.labelTarget = labelTarget; + } + }); + + } + + e(LabelSupport, CommandInterceptor); + + LabelSupport.$inject = [ + 'injector', + 'eventBus', + 'modeling' + ]; + + + /** + * Return a filtered list of elements that do not + * contain attached elements with hosts being part + * of the selection. + * + * @param {Array} elements + * + * @return {Array} filtered + */ + function removeLabels(elements) { + + return filter(elements, function(element) { + + // filter out labels that are move together + // with their label targets + return elements.indexOf(element.labelTarget) === -1; + }); + } + + var LabelSupportModule = { + __init__: [ 'labelSupport' ], + labelSupport: [ 'type', LabelSupport ] + }; + + var LOW_PRIORITY$5 = 251, + HIGH_PRIORITY$4 = 1401; + + var MARKER_ATTACH$1 = 'attach-ok'; + + + /** + * Adds the notion of attached elements to the modeler. + * + * Optionally depends on `diagram-js/lib/features/move` to render + * the attached elements during move preview. + * + * Optionally depends on `diagram-js/lib/features/label-support` + * to render attached labels during move preview. + * + * @param {didi.Injector} injector + * @param {EventBus} eventBus + * @param {Canvas} canvas + * @param {Rules} rules + * @param {Modeling} modeling + */ + function AttachSupport(injector, eventBus, canvas, rules, modeling) { + + CommandInterceptor.call(this, eventBus); + + var movePreview = injector.get('movePreview', false); + + + // remove all the attached elements from the shapes to be validated + // add all the attached shapes to the overall list of moved shapes + eventBus.on('shape.move.start', HIGH_PRIORITY$4, function(e) { + + var context = e.context, + shapes = context.shapes, + validatedShapes = context.validatedShapes; + + context.shapes = addAttached(shapes); + + context.validatedShapes = removeAttached(validatedShapes); + }); + + // add attachers to the visual's group + movePreview && eventBus.on('shape.move.start', LOW_PRIORITY$5, function(e) { + + var context = e.context, + shapes = context.shapes, + attachers = getAttachers(shapes); + + forEach$1(attachers, function(attacher) { + movePreview.makeDraggable(context, attacher, true); + + forEach$1(attacher.labels, function(label) { + movePreview.makeDraggable(context, label, true); + }); + }); + }); + + // add attach-ok marker to current host + movePreview && eventBus.on('shape.move.start', function(event) { + var context = event.context, + shapes = context.shapes; + + if (shapes.length !== 1) { + return; + } + + var shape = shapes[0]; + + var host = shape.host; + + if (host) { + canvas.addMarker(host, MARKER_ATTACH$1); + + eventBus.once([ + 'shape.move.out', + 'shape.move.cleanup' + ], function() { + canvas.removeMarker(host, MARKER_ATTACH$1); + }); + } + }); + + // add all attachers to move closure + this.preExecuted('elements.move', HIGH_PRIORITY$4, function(e) { + var context = e.context, + closure = context.closure, + shapes = context.shapes, + attachers = getAttachers(shapes); + + forEach$1(attachers, function(attacher) { + closure.add(attacher, closure.topLevel[attacher.host.id]); + }); + }); + + // perform the attaching after shapes are done moving + this.postExecuted('elements.move', function(e) { + + var context = e.context, + shapes = context.shapes, + newHost = context.newHost, + attachers; + + // only single elements can be attached + // multiply elements can be detached + if (newHost && shapes.length !== 1) { + return; + } + + if (newHost) { + attachers = shapes; + } else { + + // find attachers moved without host + attachers = filter(shapes, function(shape) { + var host = shape.host; + + return isAttacher(shape) && !includes$4(shapes, host); + }); + } + + forEach$1(attachers, function(attacher) { + modeling.updateAttachment(attacher, newHost); + }); + }); + + // ensure invalid attachment connections are removed + this.postExecuted('elements.move', function(e) { + + var shapes = e.context.shapes; + + forEach$1(shapes, function(shape) { + + forEach$1(shape.attachers, function(attacher) { + + // remove invalid outgoing connections + forEach$1(attacher.outgoing.slice(), function(connection) { + var allowed = rules.allowed('connection.reconnect', { + connection: connection, + source: connection.source, + target: connection.target + }); + + if (!allowed) { + modeling.removeConnection(connection); + } + }); + + // remove invalid incoming connections + forEach$1(attacher.incoming.slice(), function(connection) { + var allowed = rules.allowed('connection.reconnect', { + connection: connection, + source: connection.source, + target: connection.target + }); + + if (!allowed) { + modeling.removeConnection(connection); + } + }); + }); + }); + }); + + this.postExecute('shape.create', function(e) { + var context = e.context, + shape = context.shape, + host = context.host; + + if (host) { + modeling.updateAttachment(shape, host); + } + }); + + // update attachments if the host is replaced + this.postExecute('shape.replace', function(e) { + + var context = e.context, + oldShape = context.oldShape, + newShape = context.newShape; + + // move the attachers to the new host + saveClear(oldShape.attachers, function(attacher) { + var allowed = rules.allowed('elements.move', { + target: newShape, + shapes: [ attacher ] + }); + + if (allowed === 'attach') { + modeling.updateAttachment(attacher, newShape); + } else { + modeling.removeShape(attacher); + } + }); + + // move attachers if new host has different size + if (newShape.attachers.length) { + + forEach$1(newShape.attachers, function(attacher) { + var delta = getNewAttachShapeDelta(attacher, oldShape, newShape); + modeling.moveShape(attacher, delta, attacher.parent); + }); + } + + }); + + // move shape on host resize + this.postExecute('shape.resize', function(event) { + var context = event.context, + shape = context.shape, + oldBounds = context.oldBounds, + newBounds = context.newBounds, + attachers = shape.attachers, + hints = context.hints || {}; + + if (hints.attachSupport === false) { + return; + } + + forEach$1(attachers, function(attacher) { + var delta = getNewAttachShapeDelta(attacher, oldBounds, newBounds); + + modeling.moveShape(attacher, delta, attacher.parent); + + forEach$1(attacher.labels, function(label) { + modeling.moveShape(label, delta, label.parent); + }); + }); + }); + + // remove attachments + this.preExecute('shape.delete', function(event) { + + var shape = event.context.shape; + + saveClear(shape.attachers, function(attacher) { + modeling.removeShape(attacher); + }); + + if (shape.host) { + modeling.updateAttachment(shape, null); + } + }); + } + + e(AttachSupport, CommandInterceptor); + + AttachSupport.$inject = [ + 'injector', + 'eventBus', + 'canvas', + 'rules', + 'modeling' + ]; + + + /** + * Return attachers of the given shapes + * + * @param {Array} shapes + * @return {Array} + */ + function getAttachers(shapes) { + return flatten(map(shapes, function(s) { + return s.attachers || []; + })); + } + + /** + * Return a combined list of elements and + * attachers. + * + * @param {Array} elements + * @return {Array} filtered + */ + function addAttached(elements) { + var attachers = getAttachers(elements); + + return unionBy('id', elements, attachers); + } + + /** + * Return a filtered list of elements that do not + * contain attached elements with hosts being part + * of the selection. + * + * @param {Array} elements + * + * @return {Array} filtered + */ + function removeAttached(elements) { + + var ids = groupBy(elements, 'id'); + + return filter(elements, function(element) { + while (element) { + + // host in selection + if (element.host && ids[element.host.id]) { + return false; + } + + element = element.parent; + } + + return true; + }); + } + + function isAttacher(shape) { + return !!shape.host; + } + + function includes$4(array, item) { + return array.indexOf(item) !== -1; + } + + var AttachSupportModule = { + __depends__: [ + RulesModule$1 + ], + __init__: [ 'attachSupport' ], + attachSupport: [ 'type', AttachSupport ] + }; + + var LOW_PRIORITY$4 = 250; + + /** + * The tool manager acts as middle-man between the available tool's and the Palette, + * it takes care of making sure that the correct active state is set. + * + * @param {Object} eventBus + * @param {Object} dragging + */ + function ToolManager(eventBus, dragging) { + this._eventBus = eventBus; + this._dragging = dragging; + + this._tools = []; + this._active = null; + } + + ToolManager.$inject = [ 'eventBus', 'dragging' ]; + + ToolManager.prototype.registerTool = function(name, events) { + var tools = this._tools; + + if (!events) { + throw new Error('A tool has to be registered with it\'s "events"'); + } + + tools.push(name); + + this.bindEvents(name, events); + }; + + ToolManager.prototype.isActive = function(tool) { + return tool && this._active === tool; + }; + + ToolManager.prototype.length = function(tool) { + return this._tools.length; + }; + + ToolManager.prototype.setActive = function(tool) { + var eventBus = this._eventBus; + + if (this._active !== tool) { + this._active = tool; + + eventBus.fire('tool-manager.update', { tool: tool }); + } + }; + + ToolManager.prototype.bindEvents = function(name, events) { + var eventBus = this._eventBus, + dragging = this._dragging; + + var eventsToRegister = []; + + eventBus.on(events.tool + '.init', function(event) { + var context = event.context; + + // Active tools that want to reactivate themselves must do this explicitly + if (!context.reactivate && this.isActive(name)) { + this.setActive(null); + + dragging.cancel(); + return; + } + + this.setActive(name); + + }, this); + + // Todo[ricardo]: add test cases + forEach$1(events, function(event) { + eventsToRegister.push(event + '.ended'); + eventsToRegister.push(event + '.canceled'); + }); + + eventBus.on(eventsToRegister, LOW_PRIORITY$4, function(event) { + + // We defer the de-activation of the tool to the .activate phase, + // so we're able to check if we want to toggle off the current + // active tool or switch to a new one + if (!this._active) { + return; + } + + if (isPaletteClick(event)) { + return; + } + + this.setActive(null); + }, this); + + }; + + + // helpers /////////////// + + /** + * Check if a given event is a palette click event. + * + * @param {EventBus.Event} event + * + * @return {boolean} + */ + function isPaletteClick(event) { + var target = event.originalEvent && event.originalEvent.target; + + return target && closest(target, '.group[data-group="tools"]'); + } + + var ToolManagerModule = { + __depends__: [ + DraggingModule + ], + __init__: [ 'toolManager' ], + toolManager: [ 'type', ToolManager ] + }; + + /** + * Return direction given axis and delta. + * + * @param {string} axis + * @param {number} delta + * + * @return {string} + */ + function getDirection(axis, delta) { + + if (axis === 'x') { + if (delta > 0) { + return 'e'; + } + + if (delta < 0) { + return 'w'; + } + } + + if (axis === 'y') { + if (delta > 0) { + return 's'; + } + + if (delta < 0) { + return 'n'; + } + } + + return null; + } + + /** + * Returns connections whose waypoints are to be updated. Waypoints are to be updated if start + * or end is to be moved or resized. + * + * @param {Array} + */ + function getWaypointsUpdatingConnections(movingShapes, resizingShapes) { + var waypointsUpdatingConnections = []; + + forEach$1(movingShapes.concat(resizingShapes), function(shape) { + var incoming = shape.incoming, + outgoing = shape.outgoing; + + forEach$1(incoming.concat(outgoing), function(connection) { + var source = connection.source, + target = connection.target; + + if (includes$3(movingShapes, source) || + includes$3(movingShapes, target) || + includes$3(resizingShapes, source) || + includes$3(resizingShapes, target)) { + + if (!includes$3(waypointsUpdatingConnections, connection)) { + waypointsUpdatingConnections.push(connection); + } + } + }); + }); + + return waypointsUpdatingConnections; + } + + function includes$3(array, item) { + return array.indexOf(item) !== -1; + } + + /** + * Resize bounds. + * + * @param {Object} bounds + * @param {number} bounds.x + * @param {number} bounds.y + * @param {number} bounds.width + * @param {number} bounds.height + * @param {string} direction + * @param {Object} delta + * @param {number} delta.x + * @param {number} delta.y + * + * @return {Object} + */ + function resizeBounds(bounds, direction, delta) { + var x = bounds.x, + y = bounds.y, + width = bounds.width, + height = bounds.height, + dx = delta.x, + dy = delta.y; + + switch (direction) { + case 'n': + return { + x: x, + y: y + dy, + width: width, + height: height - dy + }; + case 's': + return { + x: x, + y: y, + width: width, + height: height + dy + }; + case 'w': + return { + x: x + dx, + y: y, + width: width - dx, + height: height + }; + case 'e': + return { + x: x, + y: y, + width: width + dx, + height: height + }; + default: + throw new Error('unknown direction: ' + direction); + } + } + + var abs$1 = Math.abs, + round$4 = Math.round; + + var AXIS_TO_DIMENSION = { + x: 'width', + y: 'height' + }; + + var CURSOR_CROSSHAIR = 'crosshair'; + + var DIRECTION_TO_TRBL = { + n: 'top', + w: 'left', + s: 'bottom', + e: 'right' + }; + + var HIGH_PRIORITY$3 = 1500; + + var DIRECTION_TO_OPPOSITE = { + n: 's', + w: 'e', + s: 'n', + e: 'w' + }; + + var PADDING = 20; + + + /** + * Add or remove space by moving and resizing elements. + * + * @param {Canvas} canvas + * @param {Dragging} dragging + * @param {EventBus} eventBus + * @param {Modeling} modeling + * @param {Rules} rules + * @param {ToolManager} toolManager + * @param {Mouse} mouse + */ + function SpaceTool( + canvas, dragging, eventBus, + modeling, rules, toolManager, + mouse) { + + this._canvas = canvas; + this._dragging = dragging; + this._eventBus = eventBus; + this._modeling = modeling; + this._rules = rules; + this._toolManager = toolManager; + this._mouse = mouse; + + var self = this; + + toolManager.registerTool('space', { + tool: 'spaceTool.selection', + dragging: 'spaceTool' + }); + + eventBus.on('spaceTool.selection.end', function(event) { + eventBus.once('spaceTool.selection.ended', function() { + self.activateMakeSpace(event.originalEvent); + }); + }); + + eventBus.on('spaceTool.move', HIGH_PRIORITY$3 , function(event) { + var context = event.context, + initialized = context.initialized; + + if (!initialized) { + initialized = context.initialized = self.init(event, context); + } + + if (initialized) { + ensureConstraints(event); + } + }); + + eventBus.on('spaceTool.end', function(event) { + var context = event.context, + axis = context.axis, + direction = context.direction, + movingShapes = context.movingShapes, + resizingShapes = context.resizingShapes, + start = context.start; + + if (!context.initialized) { + return; + } + + ensureConstraints(event); + + var delta = { + x: 0, + y: 0 + }; + + delta[ axis ] = round$4(event[ 'd' + axis ]); + + self.makeSpace(movingShapes, resizingShapes, delta, direction, start); + + eventBus.once('spaceTool.ended', function(event) { + + // activate space tool selection after make space + self.activateSelection(event.originalEvent, true, true); + }); + }); + } + + SpaceTool.$inject = [ + 'canvas', + 'dragging', + 'eventBus', + 'modeling', + 'rules', + 'toolManager', + 'mouse' + ]; + + /** + * Activate space tool selection. + * + * @param {Object} event + * @param {boolean} autoActivate + */ + SpaceTool.prototype.activateSelection = function(event, autoActivate, reactivate) { + this._dragging.init(event, 'spaceTool.selection', { + autoActivate: autoActivate, + cursor: CURSOR_CROSSHAIR, + data: { + context: { + reactivate: reactivate + } + }, + trapClick: false + }); + }; + + /** + * Activate space tool make space. + * + * @param {MouseEvent} event + */ + SpaceTool.prototype.activateMakeSpace = function(event) { + this._dragging.init(event, 'spaceTool', { + autoActivate: true, + cursor: CURSOR_CROSSHAIR, + data: { + context: {} + } + }); + }; + + /** + * Make space. + * + * @param {Array} movingShapes + * @param {Array} resizingShapes + * @param {Object} delta + * @param {number} delta.x + * @param {number} delta.y + * @param {string} direction + * @param {number} start + */ + SpaceTool.prototype.makeSpace = function(movingShapes, resizingShapes, delta, direction, start) { + return this._modeling.createSpace(movingShapes, resizingShapes, delta, direction, start); + }; + + /** + * Initialize make space and return true if that was successful. + * + * @param {Object} event + * @param {Object} context + * + * @return {boolean} + */ + SpaceTool.prototype.init = function(event, context) { + var axis = abs$1(event.dx) > abs$1(event.dy) ? 'x' : 'y', + delta = event[ 'd' + axis ], + start = event[ axis ] - delta; + + if (abs$1(delta) < 5) { + return false; + } + + // invert delta to remove space when moving left + if (delta < 0) { + delta *= -1; + } + + // invert delta to add/remove space when removing/adding space if modifier key is pressed + if (hasPrimaryModifier(event)) { + delta *= -1; + } + + var direction = getDirection(axis, delta); + + var root = this._canvas.getRootElement(); + + var children = selfAndAllChildren(root, true); + + var elements = this.calculateAdjustments(children, axis, delta, start); + + var minDimensions = this._eventBus.fire('spaceTool.getMinDimensions', { + axis: axis, + direction: direction, + shapes: elements.resizingShapes, + start: start + }); + + var spaceToolConstraints = getSpaceToolConstraints(elements, axis, direction, start, minDimensions); + + assign( + context, + elements, + { + axis: axis, + direction: direction, + spaceToolConstraints: spaceToolConstraints, + start: start + } + ); + + set('resize-' + (axis === 'x' ? 'ew' : 'ns')); + + return true; + }; + + /** + * Get elements to be moved and resized. + * + * @param {Array} elements + * @param {string} axis + * @param {number} delta + * @param {number} start + * + * @return {Object} + */ + SpaceTool.prototype.calculateAdjustments = function(elements, axis, delta, start) { + var rules = this._rules; + + var movingShapes = [], + resizingShapes = []; + + forEach$1(elements, function(element) { + if (!element.parent || isConnection$7(element)) { + return; + } + + var shapeStart = element[ axis ], + shapeEnd = shapeStart + element[ AXIS_TO_DIMENSION[ axis ] ]; + + // shape to be moved + if ((delta > 0 && shapeStart > start) || (delta < 0 && shapeEnd < start)) { + return movingShapes.push(element); + } + + // shape to be resized + if (shapeStart < start && + shapeEnd > start && + rules.allowed('shape.resize', { shape: element }) + ) { + + return resizingShapes.push(element); + } + }); + + return { + movingShapes: movingShapes, + resizingShapes: resizingShapes + }; + }; + + SpaceTool.prototype.toggle = function() { + + if (this.isActive()) { + return this._dragging.cancel(); + } + + var mouseEvent = this._mouse.getLastMoveEvent(); + + this.activateSelection(mouseEvent, !!mouseEvent); + }; + + SpaceTool.prototype.isActive = function() { + var context = this._dragging.context(); + + return context && /^spaceTool/.test(context.prefix); + }; + + // helpers ////////// + + function addPadding(trbl) { + return { + top: trbl.top - PADDING, + right: trbl.right + PADDING, + bottom: trbl.bottom + PADDING, + left: trbl.left - PADDING + }; + } + + function ensureConstraints(event) { + var context = event.context, + spaceToolConstraints = context.spaceToolConstraints; + + if (!spaceToolConstraints) { + return; + } + + var x, y; + + if (isNumber(spaceToolConstraints.left)) { + x = Math.max(event.x, spaceToolConstraints.left); + + event.dx = event.dx + x - event.x; + event.x = x; + } + + if (isNumber(spaceToolConstraints.right)) { + x = Math.min(event.x, spaceToolConstraints.right); + + event.dx = event.dx + x - event.x; + event.x = x; + } + + if (isNumber(spaceToolConstraints.top)) { + y = Math.max(event.y, spaceToolConstraints.top); + + event.dy = event.dy + y - event.y; + event.y = y; + } + + if (isNumber(spaceToolConstraints.bottom)) { + y = Math.min(event.y, spaceToolConstraints.bottom); + + event.dy = event.dy + y - event.y; + event.y = y; + } + } + + function getSpaceToolConstraints(elements, axis, direction, start, minDimensions) { + var movingShapes = elements.movingShapes, + resizingShapes = elements.resizingShapes; + + if (!resizingShapes.length) { + return; + } + + var spaceToolConstraints = {}, + min, + max; + + forEach$1(resizingShapes, function(resizingShape) { + var resizingShapeBBox = asTRBL(resizingShape); + + // find children that are not moving or resizing + var nonMovingResizingChildren = filter(resizingShape.children, function(child) { + return !isConnection$7(child) && + !isLabel$2(child) && + !includes$2(movingShapes, child) && + !includes$2(resizingShapes, child); + }); + + // find children that are moving + var movingChildren = filter(resizingShape.children, function(child) { + return !isConnection$7(child) && !isLabel$2(child) && includes$2(movingShapes, child); + }); + + var minOrMax, + nonMovingResizingChildrenBBox, + movingChildrenBBox; + + if (nonMovingResizingChildren.length) { + nonMovingResizingChildrenBBox = addPadding(asTRBL(getBBox(nonMovingResizingChildren))); + + minOrMax = start - + resizingShapeBBox[ DIRECTION_TO_TRBL[ direction ] ] + + nonMovingResizingChildrenBBox[ DIRECTION_TO_TRBL[ direction ] ]; + + if (direction === 'n') { + spaceToolConstraints.bottom = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax; + } else if (direction === 'w') { + spaceToolConstraints.right = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax; + } else if (direction === 's') { + spaceToolConstraints.top = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax; + } else if (direction === 'e') { + spaceToolConstraints.left = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax; + } + } + + if (movingChildren.length) { + movingChildrenBBox = addPadding(asTRBL(getBBox(movingChildren))); + + minOrMax = start - + movingChildrenBBox[ DIRECTION_TO_TRBL[ DIRECTION_TO_OPPOSITE[ direction ] ] ] + + resizingShapeBBox[ DIRECTION_TO_TRBL[ DIRECTION_TO_OPPOSITE[ direction ] ] ]; + + if (direction === 'n') { + spaceToolConstraints.bottom = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax; + } else if (direction === 'w') { + spaceToolConstraints.right = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax; + } else if (direction === 's') { + spaceToolConstraints.top = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax; + } else if (direction === 'e') { + spaceToolConstraints.left = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax; + } + } + + var resizingShapeMinDimensions = minDimensions && minDimensions[ resizingShape.id ]; + + if (resizingShapeMinDimensions) { + if (direction === 'n') { + minOrMax = start + + resizingShape[ AXIS_TO_DIMENSION [ axis ] ] - + resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ]; + + spaceToolConstraints.bottom = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax; + } else if (direction === 'w') { + minOrMax = start + + resizingShape[ AXIS_TO_DIMENSION [ axis ] ] - + resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ]; + + spaceToolConstraints.right = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax; + } else if (direction === 's') { + minOrMax = start - + resizingShape[ AXIS_TO_DIMENSION [ axis ] ] + + resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ]; + + spaceToolConstraints.top = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax; + } else if (direction === 'e') { + minOrMax = start - + resizingShape[ AXIS_TO_DIMENSION [ axis ] ] + + resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ]; + + spaceToolConstraints.left = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax; + } + } + }); + + return spaceToolConstraints; + } + + function includes$2(array, item) { + return array.indexOf(item) !== -1; + } + + function isConnection$7(element) { + return !!element.waypoints; + } + + function isLabel$2(element) { + return !!element.labelTarget; + } + + var MARKER_DRAGGING$1 = 'djs-dragging', + MARKER_RESIZING = 'djs-resizing'; + + var LOW_PRIORITY$3 = 250; + + var max = Math.max; + + + /** + * Provides previews for selecting/moving/resizing shapes when creating/removing space. + * + * @param {EventBus} eventBus + * @param {ElementRegistry} elementRegistry + * @param {Canvas} canvas + * @param {Styles} styles + */ + function SpaceToolPreview( + eventBus, elementRegistry, canvas, + styles, previewSupport) { + + function addPreviewGfx(collection, dragGroup) { + forEach$1(collection, function(element) { + previewSupport.addDragger(element, dragGroup); + + canvas.addMarker(element, MARKER_DRAGGING$1); + }); + } + + // add crosshair + eventBus.on('spaceTool.selection.start', function(event) { + var space = canvas.getLayer('space'), + context = event.context; + + var orientation = { + x: 'M 0,-10000 L 0,10000', + y: 'M -10000,0 L 10000,0' + }; + + var crosshairGroup = create$1('g'); + attr(crosshairGroup, styles.cls('djs-crosshair-group', [ 'no-events' ])); + + append(space, crosshairGroup); + + // horizontal path + var pathX = create$1('path'); + attr(pathX, 'd', orientation.x); + classes(pathX).add('djs-crosshair'); + + append(crosshairGroup, pathX); + + // vertical path + var pathY = create$1('path'); + attr(pathY, 'd', orientation.y); + classes(pathY).add('djs-crosshair'); + + append(crosshairGroup, pathY); + + context.crosshairGroup = crosshairGroup; + }); + + // update crosshair + eventBus.on('spaceTool.selection.move', function(event) { + var crosshairGroup = event.context.crosshairGroup; + + translate$2(crosshairGroup, event.x, event.y); + }); + + // remove crosshair + eventBus.on('spaceTool.selection.cleanup', function(event) { + var context = event.context, + crosshairGroup = context.crosshairGroup; + + if (crosshairGroup) { + remove$1(crosshairGroup); + } + }); + + // add and update move/resize previews + eventBus.on('spaceTool.move', LOW_PRIORITY$3, function(event) { + + var context = event.context, + line = context.line, + axis = context.axis, + movingShapes = context.movingShapes, + resizingShapes = context.resizingShapes; + + if (!context.initialized) { + return; + } + + if (!context.dragGroup) { + var spaceLayer = canvas.getLayer('space'); + + line = create$1('path'); + attr(line, 'd', 'M0,0 L0,0'); + classes(line).add('djs-crosshair'); + + append(spaceLayer, line); + + context.line = line; + + var dragGroup = create$1('g'); + attr(dragGroup, styles.cls('djs-drag-group', [ 'no-events' ])); + + append(canvas.getActiveLayer(), dragGroup); + + // shapes + addPreviewGfx(movingShapes, dragGroup); + + // connections + var movingConnections = context.movingConnections = elementRegistry.filter(function(element) { + var sourceIsMoving = false; + + forEach$1(movingShapes, function(shape) { + forEach$1(shape.outgoing, function(connection) { + if (element === connection) { + sourceIsMoving = true; + } + }); + }); + + var targetIsMoving = false; + + forEach$1(movingShapes, function(shape) { + forEach$1(shape.incoming, function(connection) { + if (element === connection) { + targetIsMoving = true; + } + }); + }); + + var sourceIsResizing = false; + + forEach$1(resizingShapes, function(shape) { + forEach$1(shape.outgoing, function(connection) { + if (element === connection) { + sourceIsResizing = true; + } + }); + }); + + var targetIsResizing = false; + + forEach$1(resizingShapes, function(shape) { + forEach$1(shape.incoming, function(connection) { + if (element === connection) { + targetIsResizing = true; + } + }); + }); + + return isConnection$6(element) + && (sourceIsMoving || sourceIsResizing) + && (targetIsMoving || targetIsResizing); + }); + + + addPreviewGfx(movingConnections, dragGroup); + + context.dragGroup = dragGroup; + } + + if (!context.frameGroup) { + var frameGroup = create$1('g'); + attr(frameGroup, styles.cls('djs-frame-group', [ 'no-events' ])); + + append(canvas.getActiveLayer(), frameGroup); + + var frames = []; + + forEach$1(resizingShapes, function(shape) { + var frame = previewSupport.addFrame(shape, frameGroup); + + var initialBounds = frame.getBBox(); + + frames.push({ + element: frame, + initialBounds: initialBounds + }); + + canvas.addMarker(shape, MARKER_RESIZING); + }); + + context.frameGroup = frameGroup; + context.frames = frames; + } + + var orientation = { + x: 'M' + event.x + ', -10000 L' + event.x + ', 10000', + y: 'M -10000, ' + event.y + ' L 10000, ' + event.y + }; + + attr(line, { d: orientation[ axis ] }); + + var opposite = { x: 'y', y: 'x' }; + var delta = { x: event.dx, y: event.dy }; + delta[ opposite[ context.axis ] ] = 0; + + // update move previews + translate$2(context.dragGroup, delta.x, delta.y); + + // update resize previews + forEach$1(context.frames, function(frame) { + var element = frame.element, + initialBounds = frame.initialBounds, + width, + height; + + if (context.direction === 'e') { + attr(element, { + width: max(initialBounds.width + delta.x, 5) + }); + } else { + width = max(initialBounds.width - delta.x, 5); + + attr(element, { + width: width, + x: initialBounds.x + initialBounds.width - width + }); + } + + if (context.direction === 's') { + attr(element, { + height: max(initialBounds.height + delta.y, 5) + }); + } else { + height = max(initialBounds.height - delta.y, 5); + + attr(element, { + height: height, + y: initialBounds.y + initialBounds.height - height + }); + } + }); + + }); + + // remove move/resize previews + eventBus.on('spaceTool.cleanup', function(event) { + + var context = event.context, + movingShapes = context.movingShapes, + movingConnections = context.movingConnections, + resizingShapes = context.resizingShapes, + line = context.line, + dragGroup = context.dragGroup, + frameGroup = context.frameGroup; + + // moving shapes + forEach$1(movingShapes, function(shape) { + canvas.removeMarker(shape, MARKER_DRAGGING$1); + }); + + // moving connections + forEach$1(movingConnections, function(connection) { + canvas.removeMarker(connection, MARKER_DRAGGING$1); + }); + + if (dragGroup) { + remove$1(line); + remove$1(dragGroup); + } + + forEach$1(resizingShapes, function(shape) { + canvas.removeMarker(shape, MARKER_RESIZING); + }); + + if (frameGroup) { + remove$1(frameGroup); + } + }); + } + + SpaceToolPreview.$inject = [ + 'eventBus', + 'elementRegistry', + 'canvas', + 'styles', + 'previewSupport' + ]; + + + // helpers ////////////////////// + + /** + * Checks if an element is a connection. + */ + function isConnection$6(element) { + return element.waypoints; + } + + var SpaceToolModule = { + __init__: [ 'spaceToolPreview' ], + __depends__: [ + DraggingModule, + RulesModule$1, + ToolManagerModule, + PreviewSupportModule, + MouseModule + ], + spaceTool: [ 'type', SpaceTool ], + spaceToolPreview: [ 'type', SpaceToolPreview ] + }; + + function BpmnFactory(moddle) { + this._model = moddle; + } + + BpmnFactory.$inject = [ 'moddle' ]; + + + BpmnFactory.prototype._needsId = function(element) { + return isAny(element, [ + 'bpmn:RootElement', + 'bpmn:FlowElement', + 'bpmn:MessageFlow', + 'bpmn:DataAssociation', + 'bpmn:Artifact', + 'bpmn:Participant', + 'bpmn:Lane', + 'bpmn:LaneSet', + 'bpmn:Process', + 'bpmn:Collaboration', + 'bpmndi:BPMNShape', + 'bpmndi:BPMNEdge', + 'bpmndi:BPMNDiagram', + 'bpmndi:BPMNPlane', + 'bpmn:Property', + 'bpmn:CategoryValue' + ]); + }; + + BpmnFactory.prototype._ensureId = function(element) { + if (element.id) { + this._model.ids.claim(element.id, element); + return; + } + + // generate semantic ids for elements + // bpmn:SequenceFlow -> SequenceFlow_ID + var prefix; + + if (is$1(element, 'bpmn:Activity')) { + prefix = 'Activity'; + } else if (is$1(element, 'bpmn:Event')) { + prefix = 'Event'; + } else if (is$1(element, 'bpmn:Gateway')) { + prefix = 'Gateway'; + } else if (isAny(element, [ 'bpmn:SequenceFlow', 'bpmn:MessageFlow' ])) { + prefix = 'Flow'; + } else { + prefix = (element.$type || '').replace(/^[^:]*:/g, ''); + } + + prefix += '_'; + + if (!element.id && this._needsId(element)) { + element.id = this._model.ids.nextPrefixed(prefix, element); + } + }; + + + BpmnFactory.prototype.create = function(type, attrs) { + var element = this._model.create(type, attrs || {}); + + this._ensureId(element); + + return element; + }; + + + BpmnFactory.prototype.createDiLabel = function() { + return this.create('bpmndi:BPMNLabel', { + bounds: this.createDiBounds() + }); + }; + + + BpmnFactory.prototype.createDiShape = function(semantic, attrs) { + return this.create('bpmndi:BPMNShape', assign({ + bpmnElement: semantic, + bounds: this.createDiBounds() + }, attrs)); + }; + + + BpmnFactory.prototype.createDiBounds = function(bounds) { + return this.create('dc:Bounds', bounds); + }; + + + BpmnFactory.prototype.createDiWaypoints = function(waypoints) { + var self = this; + + return map(waypoints, function(pos) { + return self.createDiWaypoint(pos); + }); + }; + + BpmnFactory.prototype.createDiWaypoint = function(point) { + return this.create('dc:Point', pick(point, [ 'x', 'y' ])); + }; + + + BpmnFactory.prototype.createDiEdge = function(semantic, attrs) { + return this.create('bpmndi:BPMNEdge', assign({ + bpmnElement: semantic, + waypoint: this.createDiWaypoints([]) + }, attrs)); + }; + + BpmnFactory.prototype.createDiPlane = function(semantic, attrs) { + return this.create('bpmndi:BPMNPlane', assign({ + bpmnElement: semantic + }, attrs)); + }; + + /** + * A handler responsible for updating the underlying BPMN 2.0 XML + DI + * once changes on the diagram happen + */ + function BpmnUpdater( + eventBus, bpmnFactory, connectionDocking, + translate) { + + CommandInterceptor.call(this, eventBus); + + this._bpmnFactory = bpmnFactory; + this._translate = translate; + + var self = this; + + + + // connection cropping ////////////////////// + + // crop connection ends during create/update + function cropConnection(e) { + var context = e.context, + hints = context.hints || {}, + connection; + + if (!context.cropped && hints.createElementsBehavior !== false) { + connection = context.connection; + connection.waypoints = connectionDocking.getCroppedWaypoints(connection); + context.cropped = true; + } + } + + this.executed([ + 'connection.layout', + 'connection.create' + ], cropConnection); + + this.reverted([ 'connection.layout' ], function(e) { + delete e.context.cropped; + }); + + + + // BPMN + DI update ////////////////////// + + + // update parent + function updateParent(e) { + var context = e.context; + + self.updateParent(context.shape || context.connection, context.oldParent); + } + + function reverseUpdateParent(e) { + var context = e.context; + + var element = context.shape || context.connection, + + // oldParent is the (old) new parent, because we are undoing + oldParent = context.parent || context.newParent; + + self.updateParent(element, oldParent); + } + + this.executed([ + 'shape.move', + 'shape.create', + 'shape.delete', + 'connection.create', + 'connection.move', + 'connection.delete' + ], ifBpmn(updateParent)); + + this.reverted([ + 'shape.move', + 'shape.create', + 'shape.delete', + 'connection.create', + 'connection.move', + 'connection.delete' + ], ifBpmn(reverseUpdateParent)); + + /* + * ## Updating Parent + * + * When morphing a Process into a Collaboration or vice-versa, + * make sure that both the *semantic* and *di* parent of each element + * is updated. + * + */ + function updateRoot(event) { + var context = event.context, + oldRoot = context.oldRoot, + children = oldRoot.children; + + forEach$1(children, function(child) { + if (is$1(child, 'bpmn:BaseElement')) { + self.updateParent(child); + } + }); + } + + this.executed([ 'canvas.updateRoot' ], updateRoot); + this.reverted([ 'canvas.updateRoot' ], updateRoot); + + + // update bounds + function updateBounds(e) { + var shape = e.context.shape; + + if (!is$1(shape, 'bpmn:BaseElement')) { + return; + } + + self.updateBounds(shape); + } + + this.executed([ 'shape.move', 'shape.create', 'shape.resize' ], ifBpmn(function(event) { + + // exclude labels because they're handled separately during shape.changed + if (event.context.shape.type === 'label') { + return; + } + + updateBounds(event); + })); + + this.reverted([ 'shape.move', 'shape.create', 'shape.resize' ], ifBpmn(function(event) { + + // exclude labels because they're handled separately during shape.changed + if (event.context.shape.type === 'label') { + return; + } + + updateBounds(event); + })); + + // Handle labels separately. This is necessary, because the label bounds have to be updated + // every time its shape changes, not only on move, create and resize. + eventBus.on('shape.changed', function(event) { + if (event.element.type === 'label') { + updateBounds({ context: { shape: event.element } }); + } + }); + + // attach / detach connection + function updateConnection(e) { + self.updateConnection(e.context); + } + + this.executed([ + 'connection.create', + 'connection.move', + 'connection.delete', + 'connection.reconnect' + ], ifBpmn(updateConnection)); + + this.reverted([ + 'connection.create', + 'connection.move', + 'connection.delete', + 'connection.reconnect' + ], ifBpmn(updateConnection)); + + + // update waypoints + function updateConnectionWaypoints(e) { + self.updateConnectionWaypoints(e.context.connection); + } + + this.executed([ + 'connection.layout', + 'connection.move', + 'connection.updateWaypoints', + ], ifBpmn(updateConnectionWaypoints)); + + this.reverted([ + 'connection.layout', + 'connection.move', + 'connection.updateWaypoints', + ], ifBpmn(updateConnectionWaypoints)); + + // update conditional/default flows + this.executed('connection.reconnect', ifBpmn(function(event) { + var context = event.context, + connection = context.connection, + oldSource = context.oldSource, + newSource = context.newSource, + connectionBo = getBusinessObject(connection), + oldSourceBo = getBusinessObject(oldSource), + newSourceBo = getBusinessObject(newSource); + + // remove condition from connection on reconnect to new source + // if new source can NOT have condional sequence flow + if (connectionBo.conditionExpression && !isAny(newSourceBo, [ + 'bpmn:Activity', + 'bpmn:ExclusiveGateway', + 'bpmn:InclusiveGateway' + ])) { + context.oldConditionExpression = connectionBo.conditionExpression; + + delete connectionBo.conditionExpression; + } + + // remove default from old source flow on reconnect to new source + // if source changed + if (oldSource !== newSource && oldSourceBo.default === connectionBo) { + context.oldDefault = oldSourceBo.default; + + delete oldSourceBo.default; + } + })); + + this.reverted('connection.reconnect', ifBpmn(function(event) { + var context = event.context, + connection = context.connection, + oldSource = context.oldSource, + newSource = context.newSource, + connectionBo = getBusinessObject(connection), + oldSourceBo = getBusinessObject(oldSource), + newSourceBo = getBusinessObject(newSource); + + // add condition to connection on revert reconnect to new source + if (context.oldConditionExpression) { + connectionBo.conditionExpression = context.oldConditionExpression; + } + + // add default to old source on revert reconnect to new source + if (context.oldDefault) { + oldSourceBo.default = context.oldDefault; + + delete newSourceBo.default; + } + })); + + // update attachments + function updateAttachment(e) { + self.updateAttachment(e.context); + } + + this.executed([ 'element.updateAttachment' ], ifBpmn(updateAttachment)); + this.reverted([ 'element.updateAttachment' ], ifBpmn(updateAttachment)); + } + + e(BpmnUpdater, CommandInterceptor); + + BpmnUpdater.$inject = [ + 'eventBus', + 'bpmnFactory', + 'connectionDocking', + 'translate' + ]; + + + // implementation ////////////////////// + + BpmnUpdater.prototype.updateAttachment = function(context) { + + var shape = context.shape, + businessObject = shape.businessObject, + host = shape.host; + + businessObject.attachedToRef = host && host.businessObject; + }; + + BpmnUpdater.prototype.updateParent = function(element, oldParent) { + + // do not update BPMN 2.0 label parent + if (element instanceof Label) { + return; + } + + // data stores in collaborations are handled separately by DataStoreBehavior + if (is$1(element, 'bpmn:DataStoreReference') && + element.parent && + is$1(element.parent, 'bpmn:Collaboration')) { + return; + } + + var parentShape = element.parent; + + var businessObject = element.businessObject, + di = getDi(element), + parentBusinessObject = parentShape && parentShape.businessObject, + parentDi = getDi(parentShape); + + if (is$1(element, 'bpmn:FlowNode')) { + this.updateFlowNodeRefs(businessObject, parentBusinessObject, oldParent && oldParent.businessObject); + } + + if (is$1(element, 'bpmn:DataOutputAssociation')) { + if (element.source) { + parentBusinessObject = element.source.businessObject; + } else { + parentBusinessObject = null; + } + } + + if (is$1(element, 'bpmn:DataInputAssociation')) { + if (element.target) { + parentBusinessObject = element.target.businessObject; + } else { + parentBusinessObject = null; + } + } + + this.updateSemanticParent(businessObject, parentBusinessObject); + + if (is$1(element, 'bpmn:DataObjectReference') && businessObject.dataObjectRef) { + this.updateSemanticParent(businessObject.dataObjectRef, parentBusinessObject); + } + + this.updateDiParent(di, parentDi); + }; + + + BpmnUpdater.prototype.updateBounds = function(shape) { + + var di = getDi(shape), + embeddedLabelBounds = getEmbeddedLabelBounds(shape); + + // update embedded label bounds if possible + if (embeddedLabelBounds) { + var embeddedLabelBoundsDelta = delta(embeddedLabelBounds, di.get('bounds')); + + assign(embeddedLabelBounds, { + x: shape.x + embeddedLabelBoundsDelta.x, + y: shape.y + embeddedLabelBoundsDelta.y + }); + } + + var target = (shape instanceof Label) ? this._getLabel(di) : di; + + var bounds = target.bounds; + + if (!bounds) { + bounds = this._bpmnFactory.createDiBounds(); + target.set('bounds', bounds); + } + + assign(bounds, { + x: shape.x, + y: shape.y, + width: shape.width, + height: shape.height + }); + }; + + BpmnUpdater.prototype.updateFlowNodeRefs = function(businessObject, newContainment, oldContainment) { + + if (oldContainment === newContainment) { + return; + } + + var oldRefs, newRefs; + + if (is$1 (oldContainment, 'bpmn:Lane')) { + oldRefs = oldContainment.get('flowNodeRef'); + remove(oldRefs, businessObject); + } + + if (is$1(newContainment, 'bpmn:Lane')) { + newRefs = newContainment.get('flowNodeRef'); + add(newRefs, businessObject); + } + }; + + + // update existing sourceElement and targetElement di information + BpmnUpdater.prototype.updateDiConnection = function(connection, newSource, newTarget) { + var connectionDi = getDi(connection), + newSourceDi = getDi(newSource), + newTargetDi = getDi(newTarget); + + if (connectionDi.sourceElement && connectionDi.sourceElement.bpmnElement !== getBusinessObject(newSource)) { + connectionDi.sourceElement = newSource && newSourceDi; + } + + if (connectionDi.targetElement && connectionDi.targetElement.bpmnElement !== getBusinessObject(newTarget)) { + connectionDi.targetElement = newTarget && newTargetDi; + } + + }; + + + BpmnUpdater.prototype.updateDiParent = function(di, parentDi) { + + if (parentDi && !is$1(parentDi, 'bpmndi:BPMNPlane')) { + parentDi = parentDi.$parent; + } + + if (di.$parent === parentDi) { + return; + } + + var planeElements = (parentDi || di.$parent).get('planeElement'); + + if (parentDi) { + planeElements.push(di); + di.$parent = parentDi; + } else { + remove(planeElements, di); + di.$parent = null; + } + }; + + function getDefinitions(element) { + while (element && !is$1(element, 'bpmn:Definitions')) { + element = element.$parent; + } + + return element; + } + + BpmnUpdater.prototype.getLaneSet = function(container) { + + var laneSet, laneSets; + + // bpmn:Lane + if (is$1(container, 'bpmn:Lane')) { + laneSet = container.childLaneSet; + + if (!laneSet) { + laneSet = this._bpmnFactory.create('bpmn:LaneSet'); + container.childLaneSet = laneSet; + laneSet.$parent = container; + } + + return laneSet; + } + + // bpmn:Participant + if (is$1(container, 'bpmn:Participant')) { + container = container.processRef; + } + + // bpmn:FlowElementsContainer + laneSets = container.get('laneSets'); + laneSet = laneSets[0]; + + if (!laneSet) { + laneSet = this._bpmnFactory.create('bpmn:LaneSet'); + laneSet.$parent = container; + laneSets.push(laneSet); + } + + return laneSet; + }; + + BpmnUpdater.prototype.updateSemanticParent = function(businessObject, newParent, visualParent) { + + var containment, + translate = this._translate; + + if (businessObject.$parent === newParent) { + return; + } + + if (is$1(businessObject, 'bpmn:DataInput') || is$1(businessObject, 'bpmn:DataOutput')) { + + if (is$1(newParent, 'bpmn:Participant') && 'processRef' in newParent) { + newParent = newParent.processRef; + } + + // already in correct ioSpecification + if ('ioSpecification' in newParent && newParent.ioSpecification === businessObject.$parent) { + return; + } + } + + if (is$1(businessObject, 'bpmn:Lane')) { + + if (newParent) { + newParent = this.getLaneSet(newParent); + } + + containment = 'lanes'; + } else + + if (is$1(businessObject, 'bpmn:FlowElement')) { + + if (newParent) { + + if (is$1(newParent, 'bpmn:Participant')) { + newParent = newParent.processRef; + } else + + if (is$1(newParent, 'bpmn:Lane')) { + do { + + // unwrap Lane -> LaneSet -> (Lane | FlowElementsContainer) + newParent = newParent.$parent.$parent; + } while (is$1(newParent, 'bpmn:Lane')); + + } + } + + containment = 'flowElements'; + + } else + + if (is$1(businessObject, 'bpmn:Artifact')) { + + while (newParent && + !is$1(newParent, 'bpmn:Process') && + !is$1(newParent, 'bpmn:SubProcess') && + !is$1(newParent, 'bpmn:Collaboration')) { + + if (is$1(newParent, 'bpmn:Participant')) { + newParent = newParent.processRef; + break; + } else { + newParent = newParent.$parent; + } + } + + containment = 'artifacts'; + } else + + if (is$1(businessObject, 'bpmn:MessageFlow')) { + containment = 'messageFlows'; + + } else + + if (is$1(businessObject, 'bpmn:Participant')) { + containment = 'participants'; + + // make sure the participants process is properly attached / detached + // from the XML document + + var process = businessObject.processRef, + definitions; + + if (process) { + definitions = getDefinitions(businessObject.$parent || newParent); + + if (businessObject.$parent) { + remove(definitions.get('rootElements'), process); + process.$parent = null; + } + + if (newParent) { + add(definitions.get('rootElements'), process); + process.$parent = definitions; + } + } + } else + + if (is$1(businessObject, 'bpmn:DataOutputAssociation')) { + containment = 'dataOutputAssociations'; + } else + + if (is$1(businessObject, 'bpmn:DataInputAssociation')) { + containment = 'dataInputAssociations'; + } + + if (!containment) { + throw new Error(translate( + 'no parent for {element} in {parent}', + { + element: businessObject.id, + parent: newParent.id + } + )); + } + + var children; + + if (businessObject.$parent) { + + // remove from old parent + children = businessObject.$parent.get(containment); + remove(children, businessObject); + } + + if (!newParent) { + businessObject.$parent = null; + } else { + + // add to new parent + children = newParent.get(containment); + children.push(businessObject); + businessObject.$parent = newParent; + } + + if (visualParent) { + var diChildren = visualParent.get(containment); + + remove(children, businessObject); + + if (newParent) { + + if (!diChildren) { + diChildren = []; + newParent.set(containment, diChildren); + } + + diChildren.push(businessObject); + } + } + }; + + + BpmnUpdater.prototype.updateConnectionWaypoints = function(connection) { + var di = getDi(connection); + + di.set('waypoint', this._bpmnFactory.createDiWaypoints(connection.waypoints)); + }; + + + BpmnUpdater.prototype.updateConnection = function(context) { + var connection = context.connection, + businessObject = getBusinessObject(connection), + newSource = connection.source, + newSourceBo = getBusinessObject(newSource), + newTarget = connection.target, + newTargetBo = getBusinessObject(connection.target), + visualParent; + + if (!is$1(businessObject, 'bpmn:DataAssociation')) { + + var inverseSet = is$1(businessObject, 'bpmn:SequenceFlow'); + + if (businessObject.sourceRef !== newSourceBo) { + if (inverseSet) { + remove(businessObject.sourceRef && businessObject.sourceRef.get('outgoing'), businessObject); + + if (newSourceBo && newSourceBo.get('outgoing')) { + newSourceBo.get('outgoing').push(businessObject); + } + } + + businessObject.sourceRef = newSourceBo; + } + + if (businessObject.targetRef !== newTargetBo) { + if (inverseSet) { + remove(businessObject.targetRef && businessObject.targetRef.get('incoming'), businessObject); + + if (newTargetBo && newTargetBo.get('incoming')) { + newTargetBo.get('incoming').push(businessObject); + } + } + + businessObject.targetRef = newTargetBo; + } + } else + + if (is$1(businessObject, 'bpmn:DataInputAssociation')) { + + // handle obnoxious isMsome sourceRef + businessObject.get('sourceRef')[0] = newSourceBo; + + visualParent = context.parent || context.newParent || newTargetBo; + + this.updateSemanticParent(businessObject, newTargetBo, visualParent); + } else + + if (is$1(businessObject, 'bpmn:DataOutputAssociation')) { + visualParent = context.parent || context.newParent || newSourceBo; + + this.updateSemanticParent(businessObject, newSourceBo, visualParent); + + // targetRef = new target + businessObject.targetRef = newTargetBo; + } + + this.updateConnectionWaypoints(connection); + + this.updateDiConnection(connection, newSource, newTarget); + }; + + + // helpers ////////////////////// + + BpmnUpdater.prototype._getLabel = function(di) { + if (!di.label) { + di.label = this._bpmnFactory.createDiLabel(); + } + + return di.label; + }; + + + /** + * Make sure the event listener is only called + * if the touched element is a BPMN element. + * + * @param {Function} fn + * @return {Function} guarded function + */ + function ifBpmn(fn) { + + return function(event) { + + var context = event.context, + element = context.shape || context.connection; + + if (is$1(element, 'bpmn:BaseElement')) { + fn(event); + } + }; + } + + /** + * Return dc:Bounds of bpmndi:BPMNLabel if exists. + * + * @param {djs.model.shape} shape + * + * @returns {Object|undefined} + */ + function getEmbeddedLabelBounds(shape) { + if (!is$1(shape, 'bpmn:Activity')) { + return; + } + + var di = getDi(shape); + + if (!di) { + return; + } + + var label = di.get('label'); + + if (!label) { + return; + } + + return label.get('bounds'); + } + + /** + * A bpmn-aware factory for diagram-js shapes + */ + function ElementFactory(bpmnFactory, moddle, translate) { + ElementFactory$1.call(this); + + this._bpmnFactory = bpmnFactory; + this._moddle = moddle; + this._translate = translate; + } + + e(ElementFactory, ElementFactory$1); + + ElementFactory.$inject = [ + 'bpmnFactory', + 'moddle', + 'translate' + ]; + + ElementFactory.prototype.baseCreate = ElementFactory$1.prototype.create; + + ElementFactory.prototype.create = function(elementType, attrs) { + + // no special magic for labels, + // we assume their businessObjects have already been created + // and wired via attrs + if (elementType === 'label') { + var di = attrs.di || this._bpmnFactory.createDiLabel(); + return this.baseCreate(elementType, assign({ type: 'label', di: di }, DEFAULT_LABEL_SIZE, attrs)); + } + + return this.createBpmnElement(elementType, attrs); + }; + + ElementFactory.prototype.createBpmnElement = function(elementType, attrs) { + var size, + translate = this._translate; + + attrs = assign({}, attrs || {}); + + var businessObject = attrs.businessObject, + di = attrs.di; + + if (!businessObject) { + if (!attrs.type) { + throw new Error(translate('no shape type specified')); + } + + businessObject = this._bpmnFactory.create(attrs.type); + + ensureCompatDiRef(businessObject); + } + + if (!isModdleDi(di)) { + var diAttrs = assign( + {}, + di || {}, + { id: businessObject.id + '_di' } + ); + + if (elementType === 'root') { + di = this._bpmnFactory.createDiPlane(businessObject, diAttrs); + } else + if (elementType === 'connection') { + di = this._bpmnFactory.createDiEdge(businessObject, diAttrs); + } else { + di = this._bpmnFactory.createDiShape(businessObject, diAttrs); + } + } + + if (is$1(businessObject, 'bpmn:Group')) { + attrs = assign({ + isFrame: true + }, attrs); + } + + attrs = applyAttributes(businessObject, attrs, [ + 'processRef', + 'isInterrupting', + 'associationDirection', + 'isForCompensation' + ]); + + if (attrs.isExpanded) { + attrs = applyAttribute(di, attrs, 'isExpanded'); + } + + if (is$1(businessObject, 'bpmn:SubProcess')) { + attrs.collapsed = !isExpanded(businessObject, di); + } + + if (is$1(businessObject, 'bpmn:ExclusiveGateway')) { + di.isMarkerVisible = true; + } + + var eventDefinitions, + newEventDefinition; + + if (attrs.eventDefinitionType) { + eventDefinitions = businessObject.get('eventDefinitions') || []; + newEventDefinition = this._bpmnFactory.create(attrs.eventDefinitionType, attrs.eventDefinitionAttrs); + + if (attrs.eventDefinitionType === 'bpmn:ConditionalEventDefinition') { + newEventDefinition.condition = this._bpmnFactory.create('bpmn:FormalExpression'); + } + + eventDefinitions.push(newEventDefinition); + + newEventDefinition.$parent = businessObject; + businessObject.eventDefinitions = eventDefinitions; + + delete attrs.eventDefinitionType; + } + + size = this.getDefaultSize(businessObject, di); + + attrs = assign({ + id: businessObject.id + }, size, attrs, { + businessObject: businessObject, + di: di + }); + + return this.baseCreate(elementType, attrs); + }; + + + ElementFactory.prototype.getDefaultSize = function(element, di) { + + var bo = getBusinessObject(element); + di = di || getDi(element); + + if (is$1(bo, 'bpmn:SubProcess')) { + if (isExpanded(bo, di)) { + return { width: 350, height: 200 }; + } else { + return { width: 100, height: 80 }; + } + } + + if (is$1(bo, 'bpmn:Task')) { + return { width: 100, height: 80 }; + } + + if (is$1(bo, 'bpmn:Gateway')) { + return { width: 50, height: 50 }; + } + + if (is$1(bo, 'bpmn:Event')) { + return { width: 36, height: 36 }; + } + + if (is$1(bo, 'bpmn:Participant')) { + if (isExpanded(bo, di)) { + return { width: 600, height: 250 }; + } else { + return { width: 400, height: 60 }; + } + } + + if (is$1(bo, 'bpmn:Lane')) { + return { width: 400, height: 100 }; + } + + if (is$1(bo, 'bpmn:DataObjectReference')) { + return { width: 36, height: 50 }; + } + + if (is$1(bo, 'bpmn:DataStoreReference')) { + return { width: 50, height: 50 }; + } + + if (is$1(bo, 'bpmn:TextAnnotation')) { + return { width: 100, height: 30 }; + } + + if (is$1(bo, 'bpmn:Group')) { + return { width: 300, height: 300 }; + } + + return { width: 100, height: 80 }; + }; + + + /** + * Create participant. + * + * @param {boolean|Object} [attrs] attrs + * + * @returns {djs.model.Shape} + */ + ElementFactory.prototype.createParticipantShape = function(attrs) { + + if (!isObject(attrs)) { + attrs = { isExpanded: attrs }; + } + + attrs = assign({ type: 'bpmn:Participant' }, attrs || {}); + + // participants are expanded by default + if (attrs.isExpanded !== false) { + attrs.processRef = this._bpmnFactory.create('bpmn:Process'); + } + + return this.createShape(attrs); + }; + + + // helpers ////////////////////// + + /** + * Apply attributes from a map to the given element, + * remove attribute from the map on application. + * + * @param {Base} element + * @param {Object} attrs (in/out map of attributes) + * @param {Array} attributeNames name of attributes to apply + * + * @return {Object} changed attrs + */ + function applyAttributes(element, attrs, attributeNames) { + + forEach$1(attributeNames, function(property) { + attrs = applyAttribute(element, attrs, property); + }); + + return attrs; + } + + /** + * Apply named property to element and drain it from the attrs + * collection. + * + * @param {Base} element + * @param {Object} attrs (in/out map of attributes) + * @param {string} attributeName to apply + * + * @return {Object} changed attrs + */ + function applyAttribute(element, attrs, attributeName) { + if (attrs[attributeName] === undefined) { + return attrs; + } + + element[attributeName] = attrs[attributeName]; + + return omit(attrs, [ attributeName ]); + } + + + function isModdleDi(element) { + return isAny(element, [ + 'bpmndi:BPMNShape', + 'bpmndi:BPMNEdge', + 'bpmndi:BPMNDiagram', + 'bpmndi:BPMNPlane', + ]); + } + + /** + * A handler that align elements in a certain way. + * + */ + function AlignElements(modeling, canvas) { + this._modeling = modeling; + this._canvas = canvas; + } + + AlignElements.$inject = [ 'modeling', 'canvas' ]; + + + AlignElements.prototype.preExecute = function(context) { + var modeling = this._modeling; + + var elements = context.elements, + alignment = context.alignment; + + + forEach$1(elements, function(element) { + var delta = { + x: 0, + y: 0 + }; + + if (alignment.left) { + delta.x = alignment.left - element.x; + + } else if (alignment.right) { + delta.x = (alignment.right - element.width) - element.x; + + } else if (alignment.center) { + delta.x = (alignment.center - Math.round(element.width / 2)) - element.x; + + } else if (alignment.top) { + delta.y = alignment.top - element.y; + + } else if (alignment.bottom) { + delta.y = (alignment.bottom - element.height) - element.y; + + } else if (alignment.middle) { + delta.y = (alignment.middle - Math.round(element.height / 2)) - element.y; + } + + modeling.moveElements([ element ], delta, element.parent); + }); + }; + + AlignElements.prototype.postExecute = function(context) { + + }; + + /** + * A handler that implements reversible appending of shapes + * to a source shape. + * + * @param {canvas} Canvas + * @param {elementFactory} ElementFactory + * @param {modeling} Modeling + */ + function AppendShapeHandler(modeling) { + this._modeling = modeling; + } + + AppendShapeHandler.$inject = [ 'modeling' ]; + + + // api ////////////////////// + + + /** + * Creates a new shape + * + * @param {Object} context + * @param {ElementDescriptor} context.shape the new shape + * @param {ElementDescriptor} context.source the source object + * @param {ElementDescriptor} context.parent the parent object + * @param {Point} context.position position of the new element + */ + AppendShapeHandler.prototype.preExecute = function(context) { + + var source = context.source; + + if (!source) { + throw new Error('source required'); + } + + var target = context.target || source.parent, + shape = context.shape, + hints = context.hints || {}; + + shape = context.shape = + this._modeling.createShape( + shape, + context.position, + target, { attach: hints.attach }); + + context.shape = shape; + }; + + AppendShapeHandler.prototype.postExecute = function(context) { + var hints = context.hints || {}; + + if (!existsConnection(context.source, context.shape)) { + + // create connection + if (hints.connectionTarget === context.source) { + this._modeling.connect(context.shape, context.source, context.connection); + } else { + this._modeling.connect(context.source, context.shape, context.connection); + } + } + }; + + + function existsConnection(source, target) { + return some(source.outgoing, function(c) { + return c.target === target; + }); + } + + function CreateConnectionHandler(canvas, layouter) { + this._canvas = canvas; + this._layouter = layouter; + } + + CreateConnectionHandler.$inject = [ 'canvas', 'layouter' ]; + + + // api ////////////////////// + + + /** + * Appends a shape to a target shape + * + * @param {Object} context + * @param {djs.element.Base} context.source the source object + * @param {djs.element.Base} context.target the parent object + * @param {Point} context.position position of the new element + */ + CreateConnectionHandler.prototype.execute = function(context) { + + var connection = context.connection, + source = context.source, + target = context.target, + parent = context.parent, + parentIndex = context.parentIndex, + hints = context.hints; + + if (!source || !target) { + throw new Error('source and target required'); + } + + if (!parent) { + throw new Error('parent required'); + } + + connection.source = source; + connection.target = target; + + if (!connection.waypoints) { + connection.waypoints = this._layouter.layoutConnection(connection, hints); + } + + // add connection + this._canvas.addConnection(connection, parent, parentIndex); + + return connection; + }; + + CreateConnectionHandler.prototype.revert = function(context) { + var connection = context.connection; + + this._canvas.removeConnection(connection); + + connection.source = null; + connection.target = null; + + return connection; + }; + + var round$3 = Math.round; + + function CreateElementsHandler(modeling) { + this._modeling = modeling; + } + + CreateElementsHandler.$inject = [ + 'modeling' + ]; + + CreateElementsHandler.prototype.preExecute = function(context) { + var elements = context.elements, + parent = context.parent, + parentIndex = context.parentIndex, + position = context.position, + hints = context.hints; + + var modeling = this._modeling; + + // make sure each element has x and y + forEach$1(elements, function(element) { + if (!isNumber(element.x)) { + element.x = 0; + } + + if (!isNumber(element.y)) { + element.y = 0; + } + }); + + var visibleElements = filter(elements, function(element) { + return !element.hidden; + }); + + var bbox = getBBox(visibleElements); + + // center elements around position + forEach$1(elements, function(element) { + if (isConnection$5(element)) { + element.waypoints = map(element.waypoints, function(waypoint) { + return { + x: round$3(waypoint.x - bbox.x - bbox.width / 2 + position.x), + y: round$3(waypoint.y - bbox.y - bbox.height / 2 + position.y) + }; + }); + } + + assign(element, { + x: round$3(element.x - bbox.x - bbox.width / 2 + position.x), + y: round$3(element.y - bbox.y - bbox.height / 2 + position.y) + }); + }); + + var parents = getParents$1(elements); + + var cache = {}; + + forEach$1(elements, function(element) { + if (isConnection$5(element)) { + cache[ element.id ] = isNumber(parentIndex) ? + modeling.createConnection( + cache[ element.source.id ], + cache[ element.target.id ], + parentIndex, + element, + element.parent || parent, + hints + ) : + modeling.createConnection( + cache[ element.source.id ], + cache[ element.target.id ], + element, + element.parent || parent, + hints + ); + + return; + } + + var createShapeHints = assign({}, hints); + + if (parents.indexOf(element) === -1) { + createShapeHints.autoResize = false; + } + + cache[ element.id ] = isNumber(parentIndex) ? + modeling.createShape( + element, + pick(element, [ 'x', 'y', 'width', 'height' ]), + element.parent || parent, + parentIndex, + createShapeHints + ) : + modeling.createShape( + element, + pick(element, [ 'x', 'y', 'width', 'height' ]), + element.parent || parent, + createShapeHints + ); + }); + + context.elements = values(cache); + }; + + // helpers ////////// + + function isConnection$5(element) { + return !!element.waypoints; + } + + var round$2 = Math.round; + + + /** + * A handler that implements reversible addition of shapes. + * + * @param {canvas} Canvas + */ + function CreateShapeHandler(canvas) { + this._canvas = canvas; + } + + CreateShapeHandler.$inject = [ 'canvas' ]; + + + // api ////////////////////// + + + /** + * Appends a shape to a target shape + * + * @param {Object} context + * @param {djs.model.Base} context.parent the parent object + * @param {Point} context.position position of the new element + */ + CreateShapeHandler.prototype.execute = function(context) { + + var shape = context.shape, + positionOrBounds = context.position, + parent = context.parent, + parentIndex = context.parentIndex; + + if (!parent) { + throw new Error('parent required'); + } + + if (!positionOrBounds) { + throw new Error('position required'); + } + + // (1) add at event center position _or_ at given bounds + if (positionOrBounds.width !== undefined) { + assign(shape, positionOrBounds); + } else { + assign(shape, { + x: positionOrBounds.x - round$2(shape.width / 2), + y: positionOrBounds.y - round$2(shape.height / 2) + }); + } + + // (2) add to canvas + this._canvas.addShape(shape, parent, parentIndex); + + return shape; + }; + + + /** + * Undo append by removing the shape + */ + CreateShapeHandler.prototype.revert = function(context) { + + var shape = context.shape; + + // (3) remove form canvas + this._canvas.removeShape(shape); + + return shape; + }; + + /** + * A handler that attaches a label to a given target shape. + * + * @param {Canvas} canvas + */ + function CreateLabelHandler(canvas) { + CreateShapeHandler.call(this, canvas); + } + + e(CreateLabelHandler, CreateShapeHandler); + + CreateLabelHandler.$inject = [ 'canvas' ]; + + + // api ////////////////////// + + + var originalExecute = CreateShapeHandler.prototype.execute; + + /** + * Appends a label to a target shape. + * + * @method CreateLabelHandler#execute + * + * @param {Object} context + * @param {ElementDescriptor} context.target the element the label is attached to + * @param {ElementDescriptor} context.parent the parent object + * @param {Point} context.position position of the new element + */ + CreateLabelHandler.prototype.execute = function(context) { + + var label = context.shape; + + ensureValidDimensions(label); + + label.labelTarget = context.labelTarget; + + return originalExecute.call(this, context); + }; + + var originalRevert = CreateShapeHandler.prototype.revert; + + /** + * Undo append by removing the shape + */ + CreateLabelHandler.prototype.revert = function(context) { + context.shape.labelTarget = null; + + return originalRevert.call(this, context); + }; + + + // helpers ////////////////////// + + function ensureValidDimensions(label) { + + // make sure a label has valid { width, height } dimensions + [ 'width', 'height' ].forEach(function(prop) { + if (typeof label[prop] === 'undefined') { + label[prop] = 0; + } + }); + } + + /** + * A handler that implements reversible deletion of Connections. + */ + function DeleteConnectionHandler(canvas, modeling) { + this._canvas = canvas; + this._modeling = modeling; + } + + DeleteConnectionHandler.$inject = [ + 'canvas', + 'modeling' + ]; + + + /** + * - Remove connections + */ + DeleteConnectionHandler.prototype.preExecute = function(context) { + + var modeling = this._modeling; + + var connection = context.connection; + + // remove connections + saveClear(connection.incoming, function(connection) { + + // To make sure that the connection isn't removed twice + // For example if a container is removed + modeling.removeConnection(connection, { nested: true }); + }); + + saveClear(connection.outgoing, function(connection) { + modeling.removeConnection(connection, { nested: true }); + }); + + }; + + + DeleteConnectionHandler.prototype.execute = function(context) { + + var connection = context.connection, + parent = connection.parent; + + context.parent = parent; + + // remember containment + context.parentIndex = indexOf(parent.children, connection); + + context.source = connection.source; + context.target = connection.target; + + this._canvas.removeConnection(connection); + + connection.source = null; + connection.target = null; + + return connection; + }; + + /** + * Command revert implementation. + */ + DeleteConnectionHandler.prototype.revert = function(context) { + + var connection = context.connection, + parent = context.parent, + parentIndex = context.parentIndex; + + connection.source = context.source; + connection.target = context.target; + + // restore containment + add(parent.children, connection, parentIndex); + + this._canvas.addConnection(connection, parent); + + return connection; + }; + + function DeleteElementsHandler(modeling, elementRegistry) { + this._modeling = modeling; + this._elementRegistry = elementRegistry; + } + + DeleteElementsHandler.$inject = [ + 'modeling', + 'elementRegistry' + ]; + + + DeleteElementsHandler.prototype.postExecute = function(context) { + + var modeling = this._modeling, + elementRegistry = this._elementRegistry, + elements = context.elements; + + forEach$1(elements, function(element) { + + // element may have been removed with previous + // remove operations already (e.g. in case of nesting) + if (!elementRegistry.get(element.id)) { + return; + } + + if (element.waypoints) { + modeling.removeConnection(element); + } else { + modeling.removeShape(element); + } + }); + }; + + /** + * A handler that implements reversible deletion of shapes. + * + */ + function DeleteShapeHandler(canvas, modeling) { + this._canvas = canvas; + this._modeling = modeling; + } + + DeleteShapeHandler.$inject = [ 'canvas', 'modeling' ]; + + + /** + * - Remove connections + * - Remove all direct children + */ + DeleteShapeHandler.prototype.preExecute = function(context) { + + var modeling = this._modeling; + + var shape = context.shape; + + // remove connections + saveClear(shape.incoming, function(connection) { + + // To make sure that the connection isn't removed twice + // For example if a container is removed + modeling.removeConnection(connection, { nested: true }); + }); + + saveClear(shape.outgoing, function(connection) { + modeling.removeConnection(connection, { nested: true }); + }); + + // remove child shapes and connections + saveClear(shape.children, function(child) { + if (isConnection$4(child)) { + modeling.removeConnection(child, { nested: true }); + } else { + modeling.removeShape(child, { nested: true }); + } + }); + }; + + /** + * Remove shape and remember the parent + */ + DeleteShapeHandler.prototype.execute = function(context) { + var canvas = this._canvas; + + var shape = context.shape, + oldParent = shape.parent; + + context.oldParent = oldParent; + + // remove containment + context.oldParentIndex = indexOf(oldParent.children, shape); + + // remove shape + canvas.removeShape(shape); + + return shape; + }; + + + /** + * Command revert implementation + */ + DeleteShapeHandler.prototype.revert = function(context) { + + var canvas = this._canvas; + + var shape = context.shape, + oldParent = context.oldParent, + oldParentIndex = context.oldParentIndex; + + // restore containment + add(oldParent.children, shape, oldParentIndex); + + canvas.addShape(shape, oldParent); + + return shape; + }; + + function isConnection$4(element) { + return element.waypoints; + } + + /** + * A handler that distributes elements evenly. + */ + function DistributeElements(modeling) { + this._modeling = modeling; + } + + DistributeElements.$inject = [ 'modeling' ]; + + var OFF_AXIS = { + x: 'y', + y: 'x' + }; + + DistributeElements.prototype.preExecute = function(context) { + var modeling = this._modeling; + + var groups = context.groups, + axis = context.axis, + dimension = context.dimension; + + function updateRange(group, element) { + group.range.min = Math.min(element[axis], group.range.min); + group.range.max = Math.max(element[axis] + element[dimension], group.range.max); + } + + function center(element) { + return element[axis] + element[dimension] / 2; + } + + function lastIdx(arr) { + return arr.length - 1; + } + + function rangeDiff(range) { + return range.max - range.min; + } + + function centerElement(refCenter, element) { + var delta = { y: 0 }; + + delta[axis] = refCenter - center(element); + + if (delta[axis]) { + + delta[OFF_AXIS[axis]] = 0; + + modeling.moveElements([ element ], delta, element.parent); + } + } + + var firstGroup = groups[0], + lastGroupIdx = lastIdx(groups), + lastGroup = groups[ lastGroupIdx ]; + + var margin, + spaceInBetween, + groupsSize = 0; // the size of each range + + forEach$1(groups, function(group, idx) { + var sortedElements, + refElem, + refCenter; + + if (group.elements.length < 2) { + if (idx && idx !== groups.length - 1) { + updateRange(group, group.elements[0]); + + groupsSize += rangeDiff(group.range); + } + return; + } + + sortedElements = sortBy(group.elements, axis); + + refElem = sortedElements[0]; + + if (idx === lastGroupIdx) { + refElem = sortedElements[lastIdx(sortedElements)]; + } + + refCenter = center(refElem); + + // wanna update the ranges after the shapes have been centered + group.range = null; + + forEach$1(sortedElements, function(element) { + + centerElement(refCenter, element); + + if (group.range === null) { + group.range = { + min: element[axis], + max: element[axis] + element[dimension] + }; + + return; + } + + // update group's range after centering the range elements + updateRange(group, element); + }); + + if (idx && idx !== groups.length - 1) { + groupsSize += rangeDiff(group.range); + } + }); + + spaceInBetween = Math.abs(lastGroup.range.min - firstGroup.range.max); + + margin = Math.round((spaceInBetween - groupsSize) / (groups.length - 1)); + + if (margin < groups.length - 1) { + return; + } + + forEach$1(groups, function(group, groupIdx) { + var delta = {}, + prevGroup; + + if (group === firstGroup || group === lastGroup) { + return; + } + + prevGroup = groups[groupIdx - 1]; + + group.range.max = 0; + + forEach$1(group.elements, function(element, idx) { + delta[OFF_AXIS[axis]] = 0; + delta[axis] = (prevGroup.range.max - element[axis]) + margin; + + if (group.range.min !== element[axis]) { + delta[axis] += element[axis] - group.range.min; + } + + if (delta[axis]) { + modeling.moveElements([ element ], delta, element.parent); + } + + group.range.max = Math.max(element[axis] + element[dimension], idx ? group.range.max : 0); + }); + }); + }; + + DistributeElements.prototype.postExecute = function(context) { + + }; + + /** + * A handler that implements reversible moving of shapes. + */ + function LayoutConnectionHandler(layouter, canvas) { + this._layouter = layouter; + this._canvas = canvas; + } + + LayoutConnectionHandler.$inject = [ 'layouter', 'canvas' ]; + + LayoutConnectionHandler.prototype.execute = function(context) { + + var connection = context.connection; + + var oldWaypoints = connection.waypoints; + + assign(context, { + oldWaypoints: oldWaypoints + }); + + connection.waypoints = this._layouter.layoutConnection(connection, context.hints); + + return connection; + }; + + LayoutConnectionHandler.prototype.revert = function(context) { + + var connection = context.connection; + + connection.waypoints = context.oldWaypoints; + + return connection; + }; + + /** + * A handler that implements reversible moving of connections. + * + * The handler differs from the layout connection handler in a sense + * that it preserves the connection layout. + */ + function MoveConnectionHandler() { } + + + MoveConnectionHandler.prototype.execute = function(context) { + + var connection = context.connection, + delta = context.delta; + + var newParent = context.newParent || connection.parent, + newParentIndex = context.newParentIndex, + oldParent = connection.parent; + + // save old parent in context + context.oldParent = oldParent; + context.oldParentIndex = remove(oldParent.children, connection); + + // add to new parent at position + add(newParent.children, connection, newParentIndex); + + // update parent + connection.parent = newParent; + + // update waypoint positions + forEach$1(connection.waypoints, function(p) { + p.x += delta.x; + p.y += delta.y; + + if (p.original) { + p.original.x += delta.x; + p.original.y += delta.y; + } + }); + + return connection; + }; + + MoveConnectionHandler.prototype.revert = function(context) { + + var connection = context.connection, + newParent = connection.parent, + oldParent = context.oldParent, + oldParentIndex = context.oldParentIndex, + delta = context.delta; + + // remove from newParent + remove(newParent.children, connection); + + // restore previous location in old parent + add(oldParent.children, connection, oldParentIndex); + + // restore parent + connection.parent = oldParent; + + // revert to old waypoint positions + forEach$1(connection.waypoints, function(p) { + p.x -= delta.x; + p.y -= delta.y; + + if (p.original) { + p.original.x -= delta.x; + p.original.y -= delta.y; + } + }); + + return connection; + }; + + function MoveClosure() { + + this.allShapes = {}; + this.allConnections = {}; + + this.enclosedElements = {}; + this.enclosedConnections = {}; + + this.topLevel = {}; + } + + + MoveClosure.prototype.add = function(element, isTopLevel) { + return this.addAll([ element ], isTopLevel); + }; + + + MoveClosure.prototype.addAll = function(elements, isTopLevel) { + + var newClosure = getClosure(elements, !!isTopLevel, this); + + assign(this, newClosure); + + return this; + }; + + /** + * A helper that is able to carry out serialized move + * operations on multiple elements. + * + * @param {Modeling} modeling + */ + function MoveHelper(modeling) { + this._modeling = modeling; + } + + /** + * Move the specified elements and all children by the given delta. + * + * This moves all enclosed connections, too and layouts all affected + * external connections. + * + * @param {Array} elements + * @param {Point} delta + * @param {djs.model.Base} newParent applied to the first level of shapes + * + * @return {Array} list of touched elements + */ + MoveHelper.prototype.moveRecursive = function(elements, delta, newParent) { + if (!elements) { + return []; + } else { + return this.moveClosure(this.getClosure(elements), delta, newParent); + } + }; + + /** + * Move the given closure of elmements. + * + * @param {Object} closure + * @param {Point} delta + * @param {djs.model.Base} [newParent] + * @param {djs.model.Base} [newHost] + */ + MoveHelper.prototype.moveClosure = function(closure, delta, newParent, newHost, primaryShape) { + var modeling = this._modeling; + + var allShapes = closure.allShapes, + allConnections = closure.allConnections, + enclosedConnections = closure.enclosedConnections, + topLevel = closure.topLevel, + keepParent = false; + + if (primaryShape && primaryShape.parent === newParent) { + keepParent = true; + } + + // move all shapes + forEach$1(allShapes, function(shape) { + + // move the element according to the given delta + modeling.moveShape(shape, delta, topLevel[shape.id] && !keepParent && newParent, { + recurse: false, + layout: false + }); + }); + + // move all child connections / layout external connections + forEach$1(allConnections, function(c) { + + var sourceMoved = !!allShapes[c.source.id], + targetMoved = !!allShapes[c.target.id]; + + if (enclosedConnections[c.id] && sourceMoved && targetMoved) { + modeling.moveConnection(c, delta, topLevel[c.id] && !keepParent && newParent); + } else { + modeling.layoutConnection(c, { + connectionStart: sourceMoved && getMovedSourceAnchor(c, c.source, delta), + connectionEnd: targetMoved && getMovedTargetAnchor(c, c.target, delta) + }); + } + }); + }; + + /** + * Returns the closure for the selected elements + * + * @param {Array} elements + * @return {MoveClosure} closure + */ + MoveHelper.prototype.getClosure = function(elements) { + return new MoveClosure().addAll(elements, true); + }; + + /** + * A handler that implements reversible moving of shapes. + */ + function MoveElementsHandler(modeling) { + this._helper = new MoveHelper(modeling); + } + + MoveElementsHandler.$inject = [ 'modeling' ]; + + MoveElementsHandler.prototype.preExecute = function(context) { + context.closure = this._helper.getClosure(context.shapes); + }; + + MoveElementsHandler.prototype.postExecute = function(context) { + + var hints = context.hints, + primaryShape; + + if (hints && hints.primaryShape) { + primaryShape = hints.primaryShape; + hints.oldParent = primaryShape.parent; + } + + this._helper.moveClosure( + context.closure, + context.delta, + context.newParent, + context.newHost, + primaryShape + ); + }; + + /** + * A handler that implements reversible moving of shapes. + */ + function MoveShapeHandler(modeling) { + this._modeling = modeling; + + this._helper = new MoveHelper(modeling); + } + + MoveShapeHandler.$inject = [ 'modeling' ]; + + + MoveShapeHandler.prototype.execute = function(context) { + + var shape = context.shape, + delta = context.delta, + newParent = context.newParent || shape.parent, + newParentIndex = context.newParentIndex, + oldParent = shape.parent; + + context.oldBounds = pick(shape, [ 'x', 'y', 'width', 'height' ]); + + // save old parent in context + context.oldParent = oldParent; + context.oldParentIndex = remove(oldParent.children, shape); + + // add to new parent at position + add(newParent.children, shape, newParentIndex); + + // update shape parent + position + assign(shape, { + parent: newParent, + x: shape.x + delta.x, + y: shape.y + delta.y + }); + + return shape; + }; + + MoveShapeHandler.prototype.postExecute = function(context) { + + var shape = context.shape, + delta = context.delta, + hints = context.hints; + + var modeling = this._modeling; + + if (hints.layout !== false) { + + forEach$1(shape.incoming, function(c) { + modeling.layoutConnection(c, { + connectionEnd: getMovedTargetAnchor(c, shape, delta) + }); + }); + + forEach$1(shape.outgoing, function(c) { + modeling.layoutConnection(c, { + connectionStart: getMovedSourceAnchor(c, shape, delta) + }); + }); + } + + if (hints.recurse !== false) { + this.moveChildren(context); + } + }; + + MoveShapeHandler.prototype.revert = function(context) { + + var shape = context.shape, + oldParent = context.oldParent, + oldParentIndex = context.oldParentIndex, + delta = context.delta; + + // restore previous location in old parent + add(oldParent.children, shape, oldParentIndex); + + // revert to old position and parent + assign(shape, { + parent: oldParent, + x: shape.x - delta.x, + y: shape.y - delta.y + }); + + return shape; + }; + + MoveShapeHandler.prototype.moveChildren = function(context) { + + var delta = context.delta, + shape = context.shape; + + this._helper.moveRecursive(shape.children, delta, null); + }; + + MoveShapeHandler.prototype.getNewParent = function(context) { + return context.newParent || context.shape.parent; + }; + + /** + * Reconnect connection handler + */ + function ReconnectConnectionHandler(modeling) { + this._modeling = modeling; + } + + ReconnectConnectionHandler.$inject = [ 'modeling' ]; + + ReconnectConnectionHandler.prototype.execute = function(context) { + var newSource = context.newSource, + newTarget = context.newTarget, + connection = context.connection, + dockingOrPoints = context.dockingOrPoints; + + if (!newSource && !newTarget) { + throw new Error('newSource or newTarget required'); + } + + if (isArray$3(dockingOrPoints)) { + context.oldWaypoints = connection.waypoints; + connection.waypoints = dockingOrPoints; + } + + if (newSource) { + context.oldSource = connection.source; + connection.source = newSource; + } + + if (newTarget) { + context.oldTarget = connection.target; + connection.target = newTarget; + } + + return connection; + }; + + ReconnectConnectionHandler.prototype.postExecute = function(context) { + var connection = context.connection, + newSource = context.newSource, + newTarget = context.newTarget, + dockingOrPoints = context.dockingOrPoints, + hints = context.hints || {}; + + var layoutConnectionHints = {}; + + if (hints.connectionStart) { + layoutConnectionHints.connectionStart = hints.connectionStart; + } + + if (hints.connectionEnd) { + layoutConnectionHints.connectionEnd = hints.connectionEnd; + } + + if (hints.layoutConnection === false) { + return; + } + + if (newSource && (!newTarget || hints.docking === 'source')) { + layoutConnectionHints.connectionStart = layoutConnectionHints.connectionStart + || getDocking(isArray$3(dockingOrPoints) ? dockingOrPoints[ 0 ] : dockingOrPoints); + } + + if (newTarget && (!newSource || hints.docking === 'target')) { + layoutConnectionHints.connectionEnd = layoutConnectionHints.connectionEnd + || getDocking(isArray$3(dockingOrPoints) ? dockingOrPoints[ dockingOrPoints.length - 1 ] : dockingOrPoints); + } + + if (hints.newWaypoints) { + layoutConnectionHints.waypoints = hints.newWaypoints; + } + + this._modeling.layoutConnection(connection, layoutConnectionHints); + }; + + ReconnectConnectionHandler.prototype.revert = function(context) { + var oldSource = context.oldSource, + oldTarget = context.oldTarget, + oldWaypoints = context.oldWaypoints, + connection = context.connection; + + if (oldSource) { + connection.source = oldSource; + } + + if (oldTarget) { + connection.target = oldTarget; + } + + if (oldWaypoints) { + connection.waypoints = oldWaypoints; + } + + return connection; + }; + + + + // helpers ////////// + + function getDocking(point) { + return point.original || point; + } + + /** + * Replace shape by adding new shape and removing old shape. Incoming and outgoing connections will + * be kept if possible. + * + * @class + * @constructor + * + * @param {Modeling} modeling + * @param {Rules} rules + */ + function ReplaceShapeHandler(modeling, rules) { + this._modeling = modeling; + this._rules = rules; + } + + ReplaceShapeHandler.$inject = [ 'modeling', 'rules' ]; + + + /** + * Add new shape. + * + * @param {Object} context + * @param {djs.model.Shape} context.oldShape + * @param {Object} context.newData + * @param {string} context.newData.type + * @param {number} context.newData.x + * @param {number} context.newData.y + * @param {Object} [hints] + */ + ReplaceShapeHandler.prototype.preExecute = function(context) { + var self = this, + modeling = this._modeling, + rules = this._rules; + + var oldShape = context.oldShape, + newData = context.newData, + hints = context.hints || {}, + newShape; + + function canReconnect(source, target, connection) { + return rules.allowed('connection.reconnect', { + connection: connection, + source: source, + target: target + }); + } + + // (1) add new shape at given position + var position = { + x: newData.x, + y: newData.y + }; + + var oldBounds = { + x: oldShape.x, + y: oldShape.y, + width: oldShape.width, + height: oldShape.height + }; + + newShape = context.newShape = + context.newShape || + self.createShape(newData, position, oldShape.parent, hints); + + // (2) update host + if (oldShape.host) { + modeling.updateAttachment(newShape, oldShape.host); + } + + // (3) adopt all children from old shape + var children; + + if (hints.moveChildren !== false) { + children = oldShape.children.slice(); + + modeling.moveElements(children, { x: 0, y: 0 }, newShape, hints); + } + + // (4) reconnect connections to new shape if possible + var incoming = oldShape.incoming.slice(), + outgoing = oldShape.outgoing.slice(); + + forEach$1(incoming, function(connection) { + var source = connection.source, + allowed = canReconnect(source, newShape, connection); + + if (allowed) { + self.reconnectEnd( + connection, newShape, + getResizedTargetAnchor(connection, newShape, oldBounds), + hints + ); + } + }); + + forEach$1(outgoing, function(connection) { + var target = connection.target, + allowed = canReconnect(newShape, target, connection); + + if (allowed) { + self.reconnectStart( + connection, newShape, + getResizedSourceAnchor(connection, newShape, oldBounds), + hints + ); + } + }); + }; + + + /** + * Remove old shape. + */ + ReplaceShapeHandler.prototype.postExecute = function(context) { + var oldShape = context.oldShape; + + this._modeling.removeShape(oldShape); + }; + + + ReplaceShapeHandler.prototype.execute = function(context) {}; + + + ReplaceShapeHandler.prototype.revert = function(context) {}; + + + ReplaceShapeHandler.prototype.createShape = function(shape, position, target, hints) { + return this._modeling.createShape(shape, position, target, hints); + }; + + + ReplaceShapeHandler.prototype.reconnectStart = function(connection, newSource, dockingPoint, hints) { + this._modeling.reconnectStart(connection, newSource, dockingPoint, hints); + }; + + + ReplaceShapeHandler.prototype.reconnectEnd = function(connection, newTarget, dockingPoint, hints) { + this._modeling.reconnectEnd(connection, newTarget, dockingPoint, hints); + }; + + /** + * A handler that implements reversible resizing of shapes. + * + * @param {Modeling} modeling + */ + function ResizeShapeHandler(modeling) { + this._modeling = modeling; + } + + ResizeShapeHandler.$inject = [ 'modeling' ]; + + /** + * { + * shape: {....} + * newBounds: { + * width: 20, + * height: 40, + * x: 5, + * y: 10 + * } + * + * } + */ + ResizeShapeHandler.prototype.execute = function(context) { + var shape = context.shape, + newBounds = context.newBounds, + minBounds = context.minBounds; + + if (newBounds.x === undefined || newBounds.y === undefined || + newBounds.width === undefined || newBounds.height === undefined) { + throw new Error('newBounds must have {x, y, width, height} properties'); + } + + if (minBounds && (newBounds.width < minBounds.width + || newBounds.height < minBounds.height)) { + throw new Error('width and height cannot be less than minimum height and width'); + } else if (!minBounds + && newBounds.width < 10 || newBounds.height < 10) { + throw new Error('width and height cannot be less than 10px'); + } + + // save old bbox in context + context.oldBounds = { + width: shape.width, + height: shape.height, + x: shape.x, + y: shape.y + }; + + // update shape + assign(shape, { + width: newBounds.width, + height: newBounds.height, + x: newBounds.x, + y: newBounds.y + }); + + return shape; + }; + + ResizeShapeHandler.prototype.postExecute = function(context) { + var modeling = this._modeling; + + var shape = context.shape, + oldBounds = context.oldBounds, + hints = context.hints || {}; + + if (hints.layout === false) { + return; + } + + forEach$1(shape.incoming, function(c) { + modeling.layoutConnection(c, { + connectionEnd: getResizedTargetAnchor(c, shape, oldBounds) + }); + }); + + forEach$1(shape.outgoing, function(c) { + modeling.layoutConnection(c, { + connectionStart: getResizedSourceAnchor(c, shape, oldBounds) + }); + }); + + }; + + ResizeShapeHandler.prototype.revert = function(context) { + + var shape = context.shape, + oldBounds = context.oldBounds; + + // restore previous bbox + assign(shape, { + width: oldBounds.width, + height: oldBounds.height, + x: oldBounds.x, + y: oldBounds.y + }); + + return shape; + }; + + /** + * Add or remove space by moving and resizing shapes and updating connection waypoints. + */ + function SpaceToolHandler(modeling) { + this._modeling = modeling; + } + + SpaceToolHandler.$inject = [ 'modeling' ]; + + SpaceToolHandler.prototype.preExecute = function(context) { + var delta = context.delta, + direction = context.direction, + movingShapes = context.movingShapes, + resizingShapes = context.resizingShapes, + start = context.start, + oldBounds = {}; + + // (1) move shapes + this.moveShapes(movingShapes, delta); + + // (2a) save old bounds of resized shapes + forEach$1(resizingShapes, function(shape) { + oldBounds[shape.id] = getBounds(shape); + }); + + // (2b) resize shapes + this.resizeShapes(resizingShapes, delta, direction); + + // (3) update connection waypoints + this.updateConnectionWaypoints( + getWaypointsUpdatingConnections(movingShapes, resizingShapes), + delta, + direction, + start, + movingShapes, + resizingShapes, + oldBounds + ); + }; + + SpaceToolHandler.prototype.execute = function() {}; + SpaceToolHandler.prototype.revert = function() {}; + + SpaceToolHandler.prototype.moveShapes = function(shapes, delta) { + var self = this; + + forEach$1(shapes, function(element) { + self._modeling.moveShape(element, delta, null, { + autoResize: false, + layout: false, + recurse: false + }); + }); + }; + + SpaceToolHandler.prototype.resizeShapes = function(shapes, delta, direction) { + var self = this; + + forEach$1(shapes, function(shape) { + var newBounds = resizeBounds(shape, direction, delta); + + self._modeling.resizeShape(shape, newBounds, null, { + attachSupport: false, + autoResize: false, + layout: false + }); + }); + }; + + /** + * Update connections waypoints according to the rules: + * 1. Both source and target are moved/resized => move waypoints by the delta + * 2. Only one of source and target is moved/resized => re-layout connection with moved start/end + */ + SpaceToolHandler.prototype.updateConnectionWaypoints = function( + connections, + delta, + direction, + start, + movingShapes, + resizingShapes, + oldBounds + ) { + var self = this, + affectedShapes = movingShapes.concat(resizingShapes); + + forEach$1(connections, function(connection) { + var source = connection.source, + target = connection.target, + waypoints = copyWaypoints(connection), + axis = getAxisFromDirection(direction), + layoutHints = { + labelBehavior: false + }; + + if (includes$1(affectedShapes, source) && includes$1(affectedShapes, target)) { + + // move waypoints + waypoints = map(waypoints, function(waypoint) { + if (shouldMoveWaypoint(waypoint, start, direction)) { + + // move waypoint + waypoint[ axis ] = waypoint[ axis ] + delta[ axis ]; + } + + if (waypoint.original && shouldMoveWaypoint(waypoint.original, start, direction)) { + + // move waypoint original + waypoint.original[ axis ] = waypoint.original[ axis ] + delta[ axis ]; + } + + return waypoint; + }); + + self._modeling.updateWaypoints(connection, waypoints, { + labelBehavior: false + }); + } else if (includes$1(affectedShapes, source) || includes$1(affectedShapes, target)) { + + // re-layout connection with moved start/end + if (includes$1(movingShapes, source)) { + layoutHints.connectionStart = getMovedSourceAnchor(connection, source, delta); + } else if (includes$1(movingShapes, target)) { + layoutHints.connectionEnd = getMovedTargetAnchor(connection, target, delta); + } else if (includes$1(resizingShapes, source)) { + layoutHints.connectionStart = getResizedSourceAnchor( + connection, source, oldBounds[source.id] + ); + } else if (includes$1(resizingShapes, target)) { + layoutHints.connectionEnd = getResizedTargetAnchor( + connection, target, oldBounds[target.id] + ); + } + + self._modeling.layoutConnection(connection, layoutHints); + } + }); + }; + + + // helpers ////////// + + function copyWaypoint(waypoint) { + return assign({}, waypoint); + } + + function copyWaypoints(connection) { + return map(connection.waypoints, function(waypoint) { + + waypoint = copyWaypoint(waypoint); + + if (waypoint.original) { + waypoint.original = copyWaypoint(waypoint.original); + } + + return waypoint; + }); + } + + function getAxisFromDirection(direction) { + switch (direction) { + case 'n': + return 'y'; + case 'w': + return 'x'; + case 's': + return 'y'; + case 'e': + return 'x'; + } + } + + function shouldMoveWaypoint(waypoint, start, direction) { + var relevantAxis = getAxisFromDirection(direction); + + if (/e|s/.test(direction)) { + return waypoint[ relevantAxis ] > start; + } else if (/n|w/.test(direction)) { + return waypoint[ relevantAxis ] < start; + } + } + + function includes$1(array, item) { + return array.indexOf(item) !== -1; + } + + function getBounds(shape) { + return { + x: shape.x, + y: shape.y, + height: shape.height, + width: shape.width + }; + } + + /** + * A handler that toggles the collapsed state of an element + * and the visibility of all its children. + * + * @param {Modeling} modeling + */ + function ToggleShapeCollapseHandler(modeling) { + this._modeling = modeling; + } + + ToggleShapeCollapseHandler.$inject = [ 'modeling' ]; + + + ToggleShapeCollapseHandler.prototype.execute = function(context) { + + var shape = context.shape, + children = shape.children; + + // recursively remember previous visibility of children + context.oldChildrenVisibility = getElementsVisibilityRecursive(children); + + // toggle state + shape.collapsed = !shape.collapsed; + + // recursively hide/show children + var result = setHiddenRecursive(children, shape.collapsed); + + return [ shape ].concat(result); + }; + + + ToggleShapeCollapseHandler.prototype.revert = function(context) { + + var shape = context.shape, + oldChildrenVisibility = context.oldChildrenVisibility; + + var children = shape.children; + + // recursively set old visability of children + var result = restoreVisibilityRecursive(children, oldChildrenVisibility); + + // retoggle state + shape.collapsed = !shape.collapsed; + + return [ shape ].concat(result); + }; + + + // helpers ////////////////////// + + /** + * Return a map { elementId -> hiddenState}. + * + * @param {Array} elements + * + * @return {Object} + */ + function getElementsVisibilityRecursive(elements) { + + var result = {}; + + forEach$1(elements, function(element) { + result[element.id] = element.hidden; + + if (element.children) { + result = assign({}, result, getElementsVisibilityRecursive(element.children)); + } + }); + + return result; + } + + + function setHiddenRecursive(elements, newHidden) { + var result = []; + forEach$1(elements, function(element) { + element.hidden = newHidden; + + result = result.concat(element); + + if (element.children) { + result = result.concat(setHiddenRecursive(element.children, element.collapsed || newHidden)); + } + }); + + return result; + } + + function restoreVisibilityRecursive(elements, lastState) { + var result = []; + forEach$1(elements, function(element) { + element.hidden = lastState[element.id]; + + result = result.concat(element); + + if (element.children) { + result = result.concat(restoreVisibilityRecursive(element.children, lastState)); + } + }); + + return result; + } + + /** + * A handler that implements reversible attaching/detaching of shapes. + */ + function UpdateAttachmentHandler(modeling) { + this._modeling = modeling; + } + + UpdateAttachmentHandler.$inject = [ 'modeling' ]; + + + UpdateAttachmentHandler.prototype.execute = function(context) { + var shape = context.shape, + newHost = context.newHost, + oldHost = shape.host; + + // (0) detach from old host + context.oldHost = oldHost; + context.attacherIdx = removeAttacher(oldHost, shape); + + // (1) attach to new host + addAttacher(newHost, shape); + + // (2) update host + shape.host = newHost; + + return shape; + }; + + UpdateAttachmentHandler.prototype.revert = function(context) { + var shape = context.shape, + newHost = context.newHost, + oldHost = context.oldHost, + attacherIdx = context.attacherIdx; + + // (2) update host + shape.host = oldHost; + + // (1) attach to new host + removeAttacher(newHost, shape); + + // (0) detach from old host + addAttacher(oldHost, shape, attacherIdx); + + return shape; + }; + + + function removeAttacher(host, attacher) { + + // remove attacher from host + return remove(host && host.attachers, attacher); + } + + function addAttacher(host, attacher, idx) { + + if (!host) { + return; + } + + var attachers = host.attachers; + + if (!attachers) { + host.attachers = attachers = []; + } + + add(attachers, attacher, idx); + } + + function UpdateWaypointsHandler() { } + + UpdateWaypointsHandler.prototype.execute = function(context) { + + var connection = context.connection, + newWaypoints = context.newWaypoints; + + context.oldWaypoints = connection.waypoints; + + connection.waypoints = newWaypoints; + + return connection; + }; + + UpdateWaypointsHandler.prototype.revert = function(context) { + + var connection = context.connection, + oldWaypoints = context.oldWaypoints; + + connection.waypoints = oldWaypoints; + + return connection; + }; + + /** + * The basic modeling entry point. + * + * @param {EventBus} eventBus + * @param {ElementFactory} elementFactory + * @param {CommandStack} commandStack + */ + function Modeling$1(eventBus, elementFactory, commandStack) { + this._eventBus = eventBus; + this._elementFactory = elementFactory; + this._commandStack = commandStack; + + var self = this; + + eventBus.on('diagram.init', function() { + + // register modeling handlers + self.registerHandlers(commandStack); + }); + } + + Modeling$1.$inject = [ 'eventBus', 'elementFactory', 'commandStack' ]; + + + Modeling$1.prototype.getHandlers = function() { + return { + 'shape.append': AppendShapeHandler, + 'shape.create': CreateShapeHandler, + 'shape.delete': DeleteShapeHandler, + 'shape.move': MoveShapeHandler, + 'shape.resize': ResizeShapeHandler, + 'shape.replace': ReplaceShapeHandler, + 'shape.toggleCollapse': ToggleShapeCollapseHandler, + + 'spaceTool': SpaceToolHandler, + + 'label.create': CreateLabelHandler, + + 'connection.create': CreateConnectionHandler, + 'connection.delete': DeleteConnectionHandler, + 'connection.move': MoveConnectionHandler, + 'connection.layout': LayoutConnectionHandler, + + 'connection.updateWaypoints': UpdateWaypointsHandler, + + 'connection.reconnect': ReconnectConnectionHandler, + + 'elements.create': CreateElementsHandler, + 'elements.move': MoveElementsHandler, + 'elements.delete': DeleteElementsHandler, + + 'elements.distribute': DistributeElements, + 'elements.align': AlignElements, + + 'element.updateAttachment': UpdateAttachmentHandler + }; + }; + + /** + * Register handlers with the command stack + * + * @param {CommandStack} commandStack + */ + Modeling$1.prototype.registerHandlers = function(commandStack) { + forEach$1(this.getHandlers(), function(handler, id) { + commandStack.registerHandler(id, handler); + }); + }; + + + // modeling helpers ////////////////////// + + Modeling$1.prototype.moveShape = function(shape, delta, newParent, newParentIndex, hints) { + + if (typeof newParentIndex === 'object') { + hints = newParentIndex; + newParentIndex = null; + } + + var context = { + shape: shape, + delta: delta, + newParent: newParent, + newParentIndex: newParentIndex, + hints: hints || {} + }; + + this._commandStack.execute('shape.move', context); + }; + + + /** + * Update the attachment of the given shape. + * + * @param {djs.mode.Base} shape + * @param {djs.model.Base} [newHost] + */ + Modeling$1.prototype.updateAttachment = function(shape, newHost) { + var context = { + shape: shape, + newHost: newHost + }; + + this._commandStack.execute('element.updateAttachment', context); + }; + + + /** + * Move a number of shapes to a new target, either setting it as + * the new parent or attaching it. + * + * @param {Array} shapes + * @param {Point} delta + * @param {djs.model.Base} [target] + * @param {Object} [hints] + * @param {boolean} [hints.attach=false] + */ + Modeling$1.prototype.moveElements = function(shapes, delta, target, hints) { + + hints = hints || {}; + + var attach = hints.attach; + + var newParent = target, + newHost; + + if (attach === true) { + newHost = target; + newParent = target.parent; + } else + + if (attach === false) { + newHost = null; + } + + var context = { + shapes: shapes, + delta: delta, + newParent: newParent, + newHost: newHost, + hints: hints + }; + + this._commandStack.execute('elements.move', context); + }; + + + Modeling$1.prototype.moveConnection = function(connection, delta, newParent, newParentIndex, hints) { + + if (typeof newParentIndex === 'object') { + hints = newParentIndex; + newParentIndex = undefined; + } + + var context = { + connection: connection, + delta: delta, + newParent: newParent, + newParentIndex: newParentIndex, + hints: hints || {} + }; + + this._commandStack.execute('connection.move', context); + }; + + + Modeling$1.prototype.layoutConnection = function(connection, hints) { + var context = { + connection: connection, + hints: hints || {} + }; + + this._commandStack.execute('connection.layout', context); + }; + + + /** + * Create connection. + * + * @param {djs.model.Base} source + * @param {djs.model.Base} target + * @param {number} [parentIndex] + * @param {Object|djs.model.Connection} connection + * @param {djs.model.Base} parent + * @param {Object} hints + * + * @return {djs.model.Connection} the created connection. + */ + Modeling$1.prototype.createConnection = function(source, target, parentIndex, connection, parent, hints) { + + if (typeof parentIndex === 'object') { + hints = parent; + parent = connection; + connection = parentIndex; + parentIndex = undefined; + } + + connection = this._create('connection', connection); + + var context = { + source: source, + target: target, + parent: parent, + parentIndex: parentIndex, + connection: connection, + hints: hints + }; + + this._commandStack.execute('connection.create', context); + + return context.connection; + }; + + + /** + * Create a shape at the specified position. + * + * @param {djs.model.Shape|Object} shape + * @param {Point} position + * @param {djs.model.Shape|djs.model.Root} target + * @param {number} [parentIndex] position in parents children list + * @param {Object} [hints] + * @param {boolean} [hints.attach] whether to attach to target or become a child + * + * @return {djs.model.Shape} the created shape + */ + Modeling$1.prototype.createShape = function(shape, position, target, parentIndex, hints) { + + if (typeof parentIndex !== 'number') { + hints = parentIndex; + parentIndex = undefined; + } + + hints = hints || {}; + + var attach = hints.attach, + parent, + host; + + shape = this._create('shape', shape); + + if (attach) { + parent = target.parent; + host = target; + } else { + parent = target; + } + + var context = { + position: position, + shape: shape, + parent: parent, + parentIndex: parentIndex, + host: host, + hints: hints + }; + + this._commandStack.execute('shape.create', context); + + return context.shape; + }; + + + Modeling$1.prototype.createElements = function(elements, position, parent, parentIndex, hints) { + if (!isArray$3(elements)) { + elements = [ elements ]; + } + + if (typeof parentIndex !== 'number') { + hints = parentIndex; + parentIndex = undefined; + } + + hints = hints || {}; + + var context = { + position: position, + elements: elements, + parent: parent, + parentIndex: parentIndex, + hints: hints + }; + + this._commandStack.execute('elements.create', context); + + return context.elements; + }; + + + Modeling$1.prototype.createLabel = function(labelTarget, position, label, parent) { + + label = this._create('label', label); + + var context = { + labelTarget: labelTarget, + position: position, + parent: parent || labelTarget.parent, + shape: label + }; + + this._commandStack.execute('label.create', context); + + return context.shape; + }; + + + /** + * Append shape to given source, drawing a connection + * between source and the newly created shape. + * + * @param {djs.model.Shape} source + * @param {djs.model.Shape|Object} shape + * @param {Point} position + * @param {djs.model.Shape} target + * @param {Object} [hints] + * @param {boolean} [hints.attach] + * @param {djs.model.Connection|Object} [hints.connection] + * @param {djs.model.Base} [hints.connectionParent] + * + * @return {djs.model.Shape} the newly created shape + */ + Modeling$1.prototype.appendShape = function(source, shape, position, target, hints) { + + hints = hints || {}; + + shape = this._create('shape', shape); + + var context = { + source: source, + position: position, + target: target, + shape: shape, + connection: hints.connection, + connectionParent: hints.connectionParent, + hints: hints + }; + + this._commandStack.execute('shape.append', context); + + return context.shape; + }; + + + Modeling$1.prototype.removeElements = function(elements) { + var context = { + elements: elements + }; + + this._commandStack.execute('elements.delete', context); + }; + + + Modeling$1.prototype.distributeElements = function(groups, axis, dimension) { + var context = { + groups: groups, + axis: axis, + dimension: dimension + }; + + this._commandStack.execute('elements.distribute', context); + }; + + + Modeling$1.prototype.removeShape = function(shape, hints) { + var context = { + shape: shape, + hints: hints || {} + }; + + this._commandStack.execute('shape.delete', context); + }; + + + Modeling$1.prototype.removeConnection = function(connection, hints) { + var context = { + connection: connection, + hints: hints || {} + }; + + this._commandStack.execute('connection.delete', context); + }; + + Modeling$1.prototype.replaceShape = function(oldShape, newShape, hints) { + var context = { + oldShape: oldShape, + newData: newShape, + hints: hints || {} + }; + + this._commandStack.execute('shape.replace', context); + + return context.newShape; + }; + + Modeling$1.prototype.alignElements = function(elements, alignment) { + var context = { + elements: elements, + alignment: alignment + }; + + this._commandStack.execute('elements.align', context); + }; + + Modeling$1.prototype.resizeShape = function(shape, newBounds, minBounds, hints) { + var context = { + shape: shape, + newBounds: newBounds, + minBounds: minBounds, + hints: hints + }; + + this._commandStack.execute('shape.resize', context); + }; + + Modeling$1.prototype.createSpace = function(movingShapes, resizingShapes, delta, direction, start) { + var context = { + delta: delta, + direction: direction, + movingShapes: movingShapes, + resizingShapes: resizingShapes, + start: start + }; + + this._commandStack.execute('spaceTool', context); + }; + + Modeling$1.prototype.updateWaypoints = function(connection, newWaypoints, hints) { + var context = { + connection: connection, + newWaypoints: newWaypoints, + hints: hints || {} + }; + + this._commandStack.execute('connection.updateWaypoints', context); + }; + + Modeling$1.prototype.reconnect = function(connection, source, target, dockingOrPoints, hints) { + var context = { + connection: connection, + newSource: source, + newTarget: target, + dockingOrPoints: dockingOrPoints, + hints: hints || {} + }; + + this._commandStack.execute('connection.reconnect', context); + }; + + Modeling$1.prototype.reconnectStart = function(connection, newSource, dockingOrPoints, hints) { + if (!hints) { + hints = {}; + } + + this.reconnect(connection, newSource, connection.target, dockingOrPoints, assign(hints, { + docking: 'source' + })); + }; + + Modeling$1.prototype.reconnectEnd = function(connection, newTarget, dockingOrPoints, hints) { + if (!hints) { + hints = {}; + } + + this.reconnect(connection, connection.source, newTarget, dockingOrPoints, assign(hints, { + docking: 'target' + })); + }; + + Modeling$1.prototype.connect = function(source, target, attrs, hints) { + return this.createConnection(source, target, attrs || {}, source.parent, hints); + }; + + Modeling$1.prototype._create = function(type, attrs) { + if (attrs instanceof Base$1) { + return attrs; + } else { + return this._elementFactory.create(type, attrs); + } + }; + + Modeling$1.prototype.toggleCollapse = function(shape, hints) { + var context = { + shape: shape, + hints: hints || {} + }; + + this._commandStack.execute('shape.toggleCollapse', context); + }; + + function UpdateModdlePropertiesHandler(elementRegistry) { + this._elementRegistry = elementRegistry; + } + + UpdateModdlePropertiesHandler.$inject = [ 'elementRegistry' ]; + + UpdateModdlePropertiesHandler.prototype.execute = function(context) { + + var element = context.element, + moddleElement = context.moddleElement, + properties = context.properties; + + if (!moddleElement) { + throw new Error(' required'); + } + + // TODO(nikku): we need to ensure that ID properties + // are properly registered / unregistered via + // this._moddle.ids.assigned(id) + var changed = context.changed || this.getVisualReferences(moddleElement).concat(element); + var oldProperties = context.oldProperties || getModdleProperties(moddleElement, keys(properties)); + + setModdleProperties(moddleElement, properties); + + context.oldProperties = oldProperties; + context.changed = changed; + + return changed; + }; + + UpdateModdlePropertiesHandler.prototype.revert = function(context) { + var oldProperties = context.oldProperties, + moddleElement = context.moddleElement, + changed = context.changed; + + setModdleProperties(moddleElement, oldProperties); + + return changed; + }; + + /** + * Return visual references of given moddle element within the diagram. + * + * @param {ModdleElement} moddleElement + * + * @return {Array} + */ + UpdateModdlePropertiesHandler.prototype.getVisualReferences = function(moddleElement) { + + var elementRegistry = this._elementRegistry; + + if (is$1(moddleElement, 'bpmn:DataObject')) { + return getAllDataObjectReferences(moddleElement, elementRegistry); + } + + return []; + }; + + + // helpers ///////////////// + + function getModdleProperties(moddleElement, propertyNames) { + return reduce(propertyNames, function(result, key) { + result[key] = moddleElement.get(key); + return result; + }, {}); + } + + function setModdleProperties(moddleElement, properties) { + forEach$1(properties, function(value, key) { + moddleElement.set(key, value); + }); + } + + function getAllDataObjectReferences(dataObject, elementRegistry) { + return elementRegistry.filter(function(element) { + return ( + is$1(element, 'bpmn:DataObjectReference') && + getBusinessObject(element).dataObjectRef === dataObject + ); + }); + } + + var DEFAULT_FLOW = 'default', + ID = 'id', + DI = 'di'; + + var NULL_DIMENSIONS$1 = { + width: 0, + height: 0 + }; + + /** + * A handler that implements a BPMN 2.0 property update. + * + * This should be used to set simple properties on elements with + * an underlying BPMN business object. + * + * Use respective diagram-js provided handlers if you would + * like to perform automated modeling. + */ + function UpdatePropertiesHandler( + elementRegistry, moddle, translate, + modeling, textRenderer) { + + this._elementRegistry = elementRegistry; + this._moddle = moddle; + this._translate = translate; + this._modeling = modeling; + this._textRenderer = textRenderer; + } + + UpdatePropertiesHandler.$inject = [ + 'elementRegistry', + 'moddle', + 'translate', + 'modeling', + 'textRenderer' + ]; + + + // api ////////////////////// + + /** + * Updates a BPMN element with a list of new properties + * + * @param {Object} context + * @param {djs.model.Base} context.element the element to update + * @param {Object} context.properties a list of properties to set on the element's + * businessObject (the BPMN model element) + * + * @return {Array} the updated element + */ + UpdatePropertiesHandler.prototype.execute = function(context) { + + var element = context.element, + changed = [ element ], + translate = this._translate; + + if (!element) { + throw new Error(translate('element required')); + } + + var elementRegistry = this._elementRegistry, + ids = this._moddle.ids; + + var businessObject = element.businessObject, + properties = unwrapBusinessObjects(context.properties), + oldProperties = context.oldProperties || getProperties(element, properties); + + if (isIdChange(properties, businessObject)) { + ids.unclaim(businessObject[ID]); + + elementRegistry.updateId(element, properties[ID]); + + ids.claim(properties[ID], businessObject); + } + + // correctly indicate visual changes on default flow updates + if (DEFAULT_FLOW in properties) { + + if (properties[DEFAULT_FLOW]) { + changed.push(elementRegistry.get(properties[DEFAULT_FLOW].id)); + } + + if (businessObject[DEFAULT_FLOW]) { + changed.push(elementRegistry.get(businessObject[DEFAULT_FLOW].id)); + } + } + + // update properties + setProperties(element, properties); + + // store old values + context.oldProperties = oldProperties; + context.changed = changed; + + // indicate changed on objects affected by the update + return changed; + }; + + + UpdatePropertiesHandler.prototype.postExecute = function(context) { + var element = context.element, + label = element.label; + + var text = label && getBusinessObject(label).name; + + if (!text) { + return; + } + + // get layouted text bounds and resize external + // external label accordingly + var newLabelBounds = this._textRenderer.getExternalLabelBounds(label, text); + + this._modeling.resizeShape(label, newLabelBounds, NULL_DIMENSIONS$1); + }; + + /** + * Reverts the update on a BPMN elements properties. + * + * @param {Object} context + * + * @return {djs.model.Base} the updated element + */ + UpdatePropertiesHandler.prototype.revert = function(context) { + + var element = context.element, + properties = context.properties, + oldProperties = context.oldProperties, + businessObject = element.businessObject, + elementRegistry = this._elementRegistry, + ids = this._moddle.ids; + + // update properties + setProperties(element, oldProperties); + + if (isIdChange(properties, businessObject)) { + ids.unclaim(properties[ID]); + + elementRegistry.updateId(element, oldProperties[ID]); + + ids.claim(oldProperties[ID], businessObject); + } + + return context.changed; + }; + + + function isIdChange(properties, businessObject) { + return ID in properties && properties[ID] !== businessObject[ID]; + } + + + function getProperties(element, properties) { + var propertyNames = keys(properties), + businessObject = element.businessObject, + di = getDi(element); + + return reduce(propertyNames, function(result, key) { + + // handle DI separately + if (key !== DI) { + result[key] = businessObject.get(key); + + } else { + result[key] = getDiProperties(di, keys(properties.di)); + } + + return result; + }, {}); + } + + + function getDiProperties(di, propertyNames) { + return reduce(propertyNames, function(result, key) { + result[key] = di && di.get(key); + + return result; + }, {}); + } + + + function setProperties(element, properties) { + var businessObject = element.businessObject, + di = getDi(element); + + forEach$1(properties, function(value, key) { + + if (key !== DI) { + businessObject.set(key, value); + } else { + + // only update, if di exists + if (di) { + setDiProperties(di, value); + } + } + }); + } + + + function setDiProperties(di, properties) { + forEach$1(properties, function(value, key) { + di.set(key, value); + }); + } + + + var referencePropertyNames = [ 'default' ]; + + /** + * Make sure we unwrap the actual business object + * behind diagram element that may have been + * passed as arguments. + * + * @param {Object} properties + * + * @return {Object} unwrappedProps + */ + function unwrapBusinessObjects(properties) { + + var unwrappedProps = assign({}, properties); + + referencePropertyNames.forEach(function(name) { + if (name in properties) { + unwrappedProps[name] = getBusinessObject(unwrappedProps[name]); + } + }); + + return unwrappedProps; + } + + function UpdateCanvasRootHandler(canvas, modeling) { + this._canvas = canvas; + this._modeling = modeling; + } + + UpdateCanvasRootHandler.$inject = [ + 'canvas', + 'modeling' + ]; + + + UpdateCanvasRootHandler.prototype.execute = function(context) { + + var canvas = this._canvas; + + var newRoot = context.newRoot, + newRootBusinessObject = newRoot.businessObject, + oldRoot = canvas.getRootElement(), + oldRootBusinessObject = oldRoot.businessObject, + bpmnDefinitions = oldRootBusinessObject.$parent, + diPlane = getDi(oldRoot); + + // (1) replace process old <> new root + canvas.setRootElement(newRoot); + canvas.removeRootElement(oldRoot); + + // (2) update root elements + add(bpmnDefinitions.rootElements, newRootBusinessObject); + newRootBusinessObject.$parent = bpmnDefinitions; + + remove(bpmnDefinitions.rootElements, oldRootBusinessObject); + oldRootBusinessObject.$parent = null; + + // (3) wire di + oldRoot.di = null; + + diPlane.bpmnElement = newRootBusinessObject; + newRoot.di = diPlane; + + context.oldRoot = oldRoot; + + // TODO(nikku): return changed elements? + // return [ newRoot, oldRoot ]; + }; + + + UpdateCanvasRootHandler.prototype.revert = function(context) { + + var canvas = this._canvas; + + var newRoot = context.newRoot, + newRootBusinessObject = newRoot.businessObject, + oldRoot = context.oldRoot, + oldRootBusinessObject = oldRoot.businessObject, + bpmnDefinitions = newRootBusinessObject.$parent, + diPlane = getDi(newRoot); + + // (1) replace process old <> new root + canvas.setRootElement(oldRoot); + canvas.removeRootElement(newRoot); + + // (2) update root elements + remove(bpmnDefinitions.rootElements, newRootBusinessObject); + newRootBusinessObject.$parent = null; + + add(bpmnDefinitions.rootElements, oldRootBusinessObject); + oldRootBusinessObject.$parent = bpmnDefinitions; + + // (3) wire di + newRoot.di = null; + + diPlane.bpmnElement = oldRootBusinessObject; + oldRoot.di = diPlane; + + // TODO(nikku): return changed elements? + // return [ newRoot, oldRoot ]; + }; + + /** + * A handler that allows us to add a new lane + * above or below an existing one. + * + * @param {Modeling} modeling + * @param {SpaceTool} spaceTool + */ + function AddLaneHandler(modeling, spaceTool) { + this._modeling = modeling; + this._spaceTool = spaceTool; + } + + AddLaneHandler.$inject = [ + 'modeling', + 'spaceTool' + ]; + + + AddLaneHandler.prototype.preExecute = function(context) { + + var spaceTool = this._spaceTool, + modeling = this._modeling; + + var shape = context.shape, + location = context.location; + + var lanesRoot = getLanesRoot(shape); + + var isRoot = lanesRoot === shape, + laneParent = isRoot ? shape : shape.parent; + + var existingChildLanes = getChildLanes(laneParent); + + // (0) add a lane if we currently got none and are adding to root + if (!existingChildLanes.length) { + modeling.createShape({ type: 'bpmn:Lane' }, { + x: shape.x + LANE_INDENTATION, + y: shape.y, + width: shape.width - LANE_INDENTATION, + height: shape.height + }, laneParent); + } + + // (1) collect affected elements to create necessary space + var allAffected = []; + + eachElement(lanesRoot, function(element) { + allAffected.push(element); + + // handle element labels in the diagram root + if (element.label) { + allAffected.push(element.label); + } + + if (element === shape) { + return []; + } + + return filter(element.children, function(c) { + return c !== shape; + }); + }); + + var offset = location === 'top' ? -120 : 120, + lanePosition = location === 'top' ? shape.y : shape.y + shape.height, + spacePos = lanePosition + (location === 'top' ? 10 : -10), + direction = location === 'top' ? 'n' : 's'; + + var adjustments = spaceTool.calculateAdjustments(allAffected, 'y', offset, spacePos); + + spaceTool.makeSpace( + adjustments.movingShapes, + adjustments.resizingShapes, + { x: 0, y: offset }, + direction, + spacePos + ); + + // (2) create new lane at open space + context.newLane = modeling.createShape({ type: 'bpmn:Lane' }, { + x: shape.x + (isRoot ? LANE_INDENTATION : 0), + y: lanePosition - (location === 'top' ? 120 : 0), + width: shape.width - (isRoot ? LANE_INDENTATION : 0), + height: 120 + }, laneParent); + }; + + /** + * A handler that splits a lane into a number of sub-lanes, + * creating new sub lanes, if necessary. + * + * @param {Modeling} modeling + */ + function SplitLaneHandler(modeling, translate) { + this._modeling = modeling; + this._translate = translate; + } + + SplitLaneHandler.$inject = [ + 'modeling', + 'translate' + ]; + + + SplitLaneHandler.prototype.preExecute = function(context) { + + var modeling = this._modeling, + translate = this._translate; + + var shape = context.shape, + newLanesCount = context.count; + + var childLanes = getChildLanes(shape), + existingLanesCount = childLanes.length; + + if (existingLanesCount > newLanesCount) { + throw new Error(translate('more than {count} child lanes', { count: newLanesCount })); + } + + var newLanesHeight = Math.round(shape.height / newLanesCount); + + // Iterate from top to bottom in child lane order, + // resizing existing lanes and creating new ones + // so that they split the parent proportionally. + // + // Due to rounding related errors, the bottom lane + // needs to take up all the remaining space. + var laneY, + laneHeight, + laneBounds, + newLaneAttrs, + idx; + + for (idx = 0; idx < newLanesCount; idx++) { + + laneY = shape.y + idx * newLanesHeight; + + // if bottom lane + if (idx === newLanesCount - 1) { + laneHeight = shape.height - (newLanesHeight * idx); + } else { + laneHeight = newLanesHeight; + } + + laneBounds = { + x: shape.x + LANE_INDENTATION, + y: laneY, + width: shape.width - LANE_INDENTATION, + height: laneHeight + }; + + if (idx < existingLanesCount) { + + // resize existing lane + modeling.resizeShape(childLanes[idx], laneBounds); + } else { + + // create a new lane at position + newLaneAttrs = { + type: 'bpmn:Lane' + }; + + modeling.createShape(newLaneAttrs, laneBounds, shape); + } + } + }; + + /** + * A handler that resizes a lane. + * + * @param {Modeling} modeling + */ + function ResizeLaneHandler(modeling, spaceTool) { + this._modeling = modeling; + this._spaceTool = spaceTool; + } + + ResizeLaneHandler.$inject = [ + 'modeling', + 'spaceTool' + ]; + + + ResizeLaneHandler.prototype.preExecute = function(context) { + + var shape = context.shape, + newBounds = context.newBounds, + balanced = context.balanced; + + if (balanced !== false) { + this.resizeBalanced(shape, newBounds); + } else { + this.resizeSpace(shape, newBounds); + } + }; + + + /** + * Resize balanced, adjusting next / previous lane sizes. + * + * @param {djs.model.Shape} shape + * @param {Bounds} newBounds + */ + ResizeLaneHandler.prototype.resizeBalanced = function(shape, newBounds) { + + var modeling = this._modeling; + + var resizeNeeded = computeLanesResize(shape, newBounds); + + // resize the lane + modeling.resizeShape(shape, newBounds); + + // resize other lanes as needed + resizeNeeded.forEach(function(r) { + modeling.resizeShape(r.shape, r.newBounds); + }); + }; + + + /** + * Resize, making actual space and moving below / above elements. + * + * @param {djs.model.Shape} shape + * @param {Bounds} newBounds + */ + ResizeLaneHandler.prototype.resizeSpace = function(shape, newBounds) { + var spaceTool = this._spaceTool; + + var shapeTrbl = asTRBL(shape), + newTrbl = asTRBL(newBounds); + + var trblDiff = substractTRBL(newTrbl, shapeTrbl); + + var lanesRoot = getLanesRoot(shape); + + var allAffected = [], + allLanes = []; + + eachElement(lanesRoot, function(element) { + allAffected.push(element); + + if (is$1(element, 'bpmn:Lane') || is$1(element, 'bpmn:Participant')) { + allLanes.push(element); + } + + return element.children; + }); + + var change, + spacePos, + direction, + offset, + adjustments; + + if (trblDiff.bottom || trblDiff.top) { + + change = trblDiff.bottom || trblDiff.top; + spacePos = shape.y + (trblDiff.bottom ? shape.height : 0) + (trblDiff.bottom ? -10 : 10); + direction = trblDiff.bottom ? 's' : 'n'; + + offset = trblDiff.top > 0 || trblDiff.bottom < 0 ? -change : change; + + adjustments = spaceTool.calculateAdjustments(allAffected, 'y', offset, spacePos); + + spaceTool.makeSpace(adjustments.movingShapes, adjustments.resizingShapes, { x: 0, y: change }, direction); + } + + + if (trblDiff.left || trblDiff.right) { + + change = trblDiff.right || trblDiff.left; + spacePos = shape.x + (trblDiff.right ? shape.width : 0) + (trblDiff.right ? -10 : 100); + direction = trblDiff.right ? 'e' : 'w'; + + offset = trblDiff.left > 0 || trblDiff.right < 0 ? -change : change; + + adjustments = spaceTool.calculateAdjustments(allLanes, 'x', offset, spacePos); + + spaceTool.makeSpace(adjustments.movingShapes, adjustments.resizingShapes, { x: change, y: 0 }, direction); + } + }; + + var FLOW_NODE_REFS_ATTR = 'flowNodeRef', + LANES_ATTR = 'lanes'; + + + /** + * A handler that updates lane refs on changed elements + */ + function UpdateFlowNodeRefsHandler(elementRegistry) { + this._elementRegistry = elementRegistry; + } + + UpdateFlowNodeRefsHandler.$inject = [ + 'elementRegistry' + ]; + + + UpdateFlowNodeRefsHandler.prototype.computeUpdates = function(flowNodeShapes, laneShapes) { + + var handledNodes = []; + + var updates = []; + + var participantCache = {}; + + var allFlowNodeShapes = []; + + function isInLaneShape(element, laneShape) { + + var laneTrbl = asTRBL(laneShape); + + var elementMid = { + x: element.x + element.width / 2, + y: element.y + element.height / 2 + }; + + return elementMid.x > laneTrbl.left && + elementMid.x < laneTrbl.right && + elementMid.y > laneTrbl.top && + elementMid.y < laneTrbl.bottom; + } + + function addFlowNodeShape(flowNodeShape) { + if (handledNodes.indexOf(flowNodeShape) === -1) { + allFlowNodeShapes.push(flowNodeShape); + handledNodes.push(flowNodeShape); + } + } + + function getAllLaneShapes(flowNodeShape) { + + var root = getLanesRoot(flowNodeShape); + + if (!participantCache[root.id]) { + participantCache[root.id] = collectLanes(root); + } + + return participantCache[root.id]; + } + + function getNewLanes(flowNodeShape) { + if (!flowNodeShape.parent) { + return []; + } + + var allLaneShapes = getAllLaneShapes(flowNodeShape); + + return allLaneShapes.filter(function(l) { + return isInLaneShape(flowNodeShape, l); + }).map(function(shape) { + return shape.businessObject; + }); + } + + laneShapes.forEach(function(laneShape) { + var root = getLanesRoot(laneShape); + + if (!root || handledNodes.indexOf(root) !== -1) { + return; + } + + var children = root.children.filter(function(c) { + return is$1(c, 'bpmn:FlowNode'); + }); + + children.forEach(addFlowNodeShape); + + handledNodes.push(root); + }); + + flowNodeShapes.forEach(addFlowNodeShape); + + + allFlowNodeShapes.forEach(function(flowNodeShape) { + + var flowNode = flowNodeShape.businessObject; + + var lanes = flowNode.get(LANES_ATTR), + remove = lanes.slice(), + add = getNewLanes(flowNodeShape); + + updates.push({ flowNode: flowNode, remove: remove, add: add }); + }); + + laneShapes.forEach(function(laneShape) { + + var lane = laneShape.businessObject; + + // lane got removed XX-) + if (!laneShape.parent) { + lane.get(FLOW_NODE_REFS_ATTR).forEach(function(flowNode) { + updates.push({ flowNode: flowNode, remove: [ lane ], add: [] }); + }); + } + }); + + return updates; + }; + + UpdateFlowNodeRefsHandler.prototype.execute = function(context) { + + var updates = context.updates; + + if (!updates) { + updates = context.updates = this.computeUpdates(context.flowNodeShapes, context.laneShapes); + } + + + updates.forEach(function(update) { + + var flowNode = update.flowNode, + lanes = flowNode.get(LANES_ATTR); + + // unwire old + update.remove.forEach(function(oldLane) { + remove(lanes, oldLane); + remove(oldLane.get(FLOW_NODE_REFS_ATTR), flowNode); + }); + + // wire new + update.add.forEach(function(newLane) { + add(lanes, newLane); + add(newLane.get(FLOW_NODE_REFS_ATTR), flowNode); + }); + }); + + // TODO(nikku): return changed elements + // return [ ... ]; + }; + + + UpdateFlowNodeRefsHandler.prototype.revert = function(context) { + + var updates = context.updates; + + updates.forEach(function(update) { + + var flowNode = update.flowNode, + lanes = flowNode.get(LANES_ATTR); + + // unwire new + update.add.forEach(function(newLane) { + remove(lanes, newLane); + remove(newLane.get(FLOW_NODE_REFS_ATTR), flowNode); + }); + + // wire old + update.remove.forEach(function(oldLane) { + add(lanes, oldLane); + add(oldLane.get(FLOW_NODE_REFS_ATTR), flowNode); + }); + }); + + // TODO(nikku): return changed elements + // return [ ... ]; + }; + + function IdClaimHandler(moddle) { + this._moddle = moddle; + } + + IdClaimHandler.$inject = [ 'moddle' ]; + + + IdClaimHandler.prototype.execute = function(context) { + var ids = this._moddle.ids, + id = context.id, + element = context.element, + claiming = context.claiming; + + if (claiming) { + ids.claim(id, element); + } else { + ids.unclaim(id); + } + }; + + /** + * Command revert implementation. + */ + IdClaimHandler.prototype.revert = function(context) { + var ids = this._moddle.ids, + id = context.id, + element = context.element, + claiming = context.claiming; + + if (claiming) { + ids.unclaim(id); + } else { + ids.claim(id, element); + } + }; + + var DEFAULT_COLORS = { + fill: undefined, + stroke: undefined + }; + + + function SetColorHandler(commandStack) { + this._commandStack = commandStack; + + this._normalizeColor = function(color) { + + // Remove color for falsy values. + if (!color) { + return undefined; + } + + if (isString(color)) { + var hexColor = colorToHex(color); + + if (hexColor) { + return hexColor; + } + } + + throw new Error('invalid color value: ' + color); + }; + } + + SetColorHandler.$inject = [ + 'commandStack' + ]; + + + SetColorHandler.prototype.postExecute = function(context) { + var elements = context.elements, + colors = context.colors || DEFAULT_COLORS; + + var self = this; + + var di = {}; + + if ('fill' in colors) { + assign(di, { + 'background-color': this._normalizeColor(colors.fill) }); + } + + if ('stroke' in colors) { + assign(di, { + 'border-color': this._normalizeColor(colors.stroke) }); + } + + forEach$1(elements, function(element) { + var assignedDi = isConnection$3(element) ? pick(di, [ 'border-color' ]) : di; + + // TODO @barmac: remove once we drop bpmn.io properties + ensureLegacySupport(assignedDi); + + if (isLabel$6(element)) { + + // set label colors as bpmndi:BPMNLabel#color + self._commandStack.execute('element.updateModdleProperties', { + element: element, + moddleElement: getDi(element).label, + properties: { + color: di['border-color'] + } + }); + } else { + + // set colors bpmndi:BPMNEdge or bpmndi:BPMNShape + self._commandStack.execute('element.updateProperties', { + element: element, + properties: { + di: assignedDi + } + }); + } + }); + + }; + + /** + * Convert color from rgb(a)/hsl to hex. Returns `null` for unknown color names and for colors + * with alpha less than 1.0. This depends on `` serialization of the `context.fillStyle`. + * Cf. https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-fillstyle + * + * @example + * ```js + * var color = 'fuchsia'; + * console.log(colorToHex(color)); + * // "#ff00ff" + * color = 'rgba(1,2,3,0.4)'; + * console.log(colorToHex(color)); + * // null + * ``` + * + * @param {string} color + * @returns {string|null} + */ + function colorToHex(color) { + var context = document.createElement('canvas').getContext('2d'); + + // (0) Start with transparent to account for browser default values. + context.fillStyle = 'transparent'; + + // (1) Assign color so that it's serialized. + context.fillStyle = color; + + // (2) Return null for non-hex serialization result. + return /^#[0-9a-fA-F]{6}$/.test(context.fillStyle) ? context.fillStyle : null; + } + + function isConnection$3(element) { + return !!element.waypoints; + } + + /** + * Add legacy properties if required. + * @param {{ 'border-color': string?, 'background-color': string? }} di + */ + function ensureLegacySupport(di) { + if ('border-color' in di) { + di.stroke = di['border-color']; + } + + if ('background-color' in di) { + di.fill = di['background-color']; + } + } + + var NULL_DIMENSIONS = { + width: 0, + height: 0 + }; + + + /** + * A handler that updates the text of a BPMN element. + */ + function UpdateLabelHandler(modeling, textRenderer, bpmnFactory) { + + /** + * Creates an empty `diLabel` attribute for embedded labels. + * + * @param {djs.model.Base} element + * @param {string} text + */ + function ensureInternalLabelDi(element, text) { + if (isLabelExternal(element)) { + return; + } + + var di = getDi(element); + + if (text && !di.label) { + di.label = bpmnFactory.create('bpmndi:BPMNLabel'); + } + + if (!text && di.label) { + delete di.label; + } + } + + + /** + * Set the label and return the changed elements. + * + * Element parameter can be label itself or connection (i.e. sequence flow). + * + * @param {djs.model.Base} element + * @param {string} text + */ + function setText(element, text) { + + // external label if present + var label = element.label || element; + + var labelTarget = element.labelTarget || element; + + setLabel(label, text); + + ensureInternalLabelDi(element, text); + + return [ label, labelTarget ]; + } + + function preExecute(ctx) { + var element = ctx.element, + businessObject = element.businessObject, + newLabel = ctx.newLabel; + + if (!isLabel$6(element) + && isLabelExternal(element) + && !hasExternalLabel(element) + && !isEmptyText(newLabel)) { + + // create label + var paddingTop = 7; + + var labelCenter = getExternalLabelMid(element); + + labelCenter = { + x: labelCenter.x, + y: labelCenter.y + paddingTop + }; + + modeling.createLabel(element, labelCenter, { + id: businessObject.id + '_label', + businessObject: businessObject, + di: element.di + }); + } + } + + function execute(ctx) { + ctx.oldLabel = getLabel(ctx.element); + return setText(ctx.element, ctx.newLabel); + } + + function revert(ctx) { + return setText(ctx.element, ctx.oldLabel); + } + + function postExecute(ctx) { + var element = ctx.element, + label = element.label || element, + newLabel = ctx.newLabel, + newBounds = ctx.newBounds, + hints = ctx.hints || {}; + + // ignore internal labels for elements except text annotations + if (!isLabel$6(label) && !is$1(label, 'bpmn:TextAnnotation')) { + return; + } + + if (isLabel$6(label) && isEmptyText(newLabel)) { + + if (hints.removeShape !== false) { + modeling.removeShape(label, { unsetLabel: false }); + } + + return; + } + + var text = getLabel(label); + + // resize element based on label _or_ pre-defined bounds + if (typeof newBounds === 'undefined') { + newBounds = textRenderer.getExternalLabelBounds(label, text); + } + + // setting newBounds to false or _null_ will + // disable the postExecute resize operation + if (newBounds) { + modeling.resizeShape(label, newBounds, NULL_DIMENSIONS); + } + } + + // API + + this.preExecute = preExecute; + this.execute = execute; + this.revert = revert; + this.postExecute = postExecute; + } + + UpdateLabelHandler.$inject = [ + 'modeling', + 'textRenderer', + 'bpmnFactory' + ]; + + + // helpers /////////////////////// + + function isEmptyText(label) { + return !label || !label.trim(); + } + + /** + * BPMN 2.0 modeling features activator + * + * @param {EventBus} eventBus + * @param {ElementFactory} elementFactory + * @param {CommandStack} commandStack + * @param {BpmnRules} bpmnRules + */ + function Modeling( + eventBus, elementFactory, commandStack, + bpmnRules) { + + Modeling$1.call(this, eventBus, elementFactory, commandStack); + + this._bpmnRules = bpmnRules; + } + + e(Modeling, Modeling$1); + + Modeling.$inject = [ + 'eventBus', + 'elementFactory', + 'commandStack', + 'bpmnRules' + ]; + + + Modeling.prototype.getHandlers = function() { + var handlers = Modeling$1.prototype.getHandlers.call(this); + + handlers['element.updateModdleProperties'] = UpdateModdlePropertiesHandler; + handlers['element.updateProperties'] = UpdatePropertiesHandler; + handlers['canvas.updateRoot'] = UpdateCanvasRootHandler; + handlers['lane.add'] = AddLaneHandler; + handlers['lane.resize'] = ResizeLaneHandler; + handlers['lane.split'] = SplitLaneHandler; + handlers['lane.updateRefs'] = UpdateFlowNodeRefsHandler; + handlers['id.updateClaim'] = IdClaimHandler; + handlers['element.setColor'] = SetColorHandler; + handlers['element.updateLabel'] = UpdateLabelHandler; + + return handlers; + }; + + + Modeling.prototype.updateLabel = function(element, newLabel, newBounds, hints) { + this._commandStack.execute('element.updateLabel', { + element: element, + newLabel: newLabel, + newBounds: newBounds, + hints: hints || {} + }); + }; + + + Modeling.prototype.connect = function(source, target, attrs, hints) { + + var bpmnRules = this._bpmnRules; + + if (!attrs) { + attrs = bpmnRules.canConnect(source, target); + } + + if (!attrs) { + return; + } + + return this.createConnection(source, target, attrs, source.parent, hints); + }; + + + Modeling.prototype.updateModdleProperties = function(element, moddleElement, properties) { + this._commandStack.execute('element.updateModdleProperties', { + element: element, + moddleElement: moddleElement, + properties: properties + }); + }; + + Modeling.prototype.updateProperties = function(element, properties) { + this._commandStack.execute('element.updateProperties', { + element: element, + properties: properties + }); + }; + + Modeling.prototype.resizeLane = function(laneShape, newBounds, balanced) { + this._commandStack.execute('lane.resize', { + shape: laneShape, + newBounds: newBounds, + balanced: balanced + }); + }; + + Modeling.prototype.addLane = function(targetLaneShape, location) { + var context = { + shape: targetLaneShape, + location: location + }; + + this._commandStack.execute('lane.add', context); + + return context.newLane; + }; + + Modeling.prototype.splitLane = function(targetLane, count) { + this._commandStack.execute('lane.split', { + shape: targetLane, + count: count + }); + }; + + /** + * Transform the current diagram into a collaboration. + * + * @return {djs.model.Root} the new root element + */ + Modeling.prototype.makeCollaboration = function() { + + var collaborationElement = this._create('root', { + type: 'bpmn:Collaboration' + }); + + var context = { + newRoot: collaborationElement + }; + + this._commandStack.execute('canvas.updateRoot', context); + + return collaborationElement; + }; + + Modeling.prototype.updateLaneRefs = function(flowNodeShapes, laneShapes) { + + this._commandStack.execute('lane.updateRefs', { + flowNodeShapes: flowNodeShapes, + laneShapes: laneShapes + }); + }; + + /** + * Transform the current diagram into a process. + * + * @return {djs.model.Root} the new root element + */ + Modeling.prototype.makeProcess = function() { + + var processElement = this._create('root', { + type: 'bpmn:Process' + }); + + var context = { + newRoot: processElement + }; + + this._commandStack.execute('canvas.updateRoot', context); + }; + + + Modeling.prototype.claimId = function(id, moddleElement) { + this._commandStack.execute('id.updateClaim', { + id: id, + element: moddleElement, + claiming: true + }); + }; + + + Modeling.prototype.unclaimId = function(id, moddleElement) { + this._commandStack.execute('id.updateClaim', { + id: id, + element: moddleElement + }); + }; + + Modeling.prototype.setColor = function(elements, colors) { + if (!elements.length) { + elements = [ elements ]; + } + + this._commandStack.execute('element.setColor', { + elements: elements, + colors: colors + }); + }; + + /** + * A base connection layouter implementation + * that layouts the connection by directly connecting + * mid(source) + mid(target). + */ + function BaseLayouter() {} + + + /** + * Return the new layouted waypoints for the given connection. + * + * The connection passed is still unchanged; you may figure out about + * the new connection start / end via the layout hints provided. + * + * @param {djs.model.Connection} connection + * @param {Object} [hints] + * @param {Point} [hints.connectionStart] + * @param {Point} [hints.connectionEnd] + * @param {Point} [hints.source] + * @param {Point} [hints.target] + * + * @return {Array} the layouted connection waypoints + */ + BaseLayouter.prototype.layoutConnection = function(connection, hints) { + + hints = hints || {}; + + return [ + hints.connectionStart || getMid(hints.source || connection.source), + hints.connectionEnd || getMid(hints.target || connection.target) + ]; + }; + + var MIN_SEGMENT_LENGTH = 20, + POINT_ORIENTATION_PADDING = 5; + + var round$1 = Math.round; + + var INTERSECTION_THRESHOLD = 20, + ORIENTATION_THRESHOLD = { + 'h:h': 20, + 'v:v': 20, + 'h:v': -10, + 'v:h': -10 + }; + + function needsTurn(orientation, startDirection) { + return !{ + t: /top/, + r: /right/, + b: /bottom/, + l: /left/, + h: /./, + v: /./ + }[startDirection].test(orientation); + } + + function canLayoutStraight(direction, targetOrientation) { + return { + t: /top/, + r: /right/, + b: /bottom/, + l: /left/, + h: /left|right/, + v: /top|bottom/ + }[direction].test(targetOrientation); + } + + function getSegmentBendpoints(a, b, directions) { + var orientation = getOrientation(b, a, POINT_ORIENTATION_PADDING); + + var startDirection = directions.split(':')[0]; + + var xmid = round$1((b.x - a.x) / 2 + a.x), + ymid = round$1((b.y - a.y) / 2 + a.y); + + var segmentEnd, segmentDirections; + + var layoutStraight = canLayoutStraight(startDirection, orientation), + layoutHorizontal = /h|r|l/.test(startDirection), + layoutTurn = false; + + var turnNextDirections = false; + + if (layoutStraight) { + segmentEnd = layoutHorizontal ? { x: xmid, y: a.y } : { x: a.x, y: ymid }; + + segmentDirections = layoutHorizontal ? 'h:h' : 'v:v'; + } else { + layoutTurn = needsTurn(orientation, startDirection); + + segmentDirections = layoutHorizontal ? 'h:v' : 'v:h'; + + if (layoutTurn) { + + if (layoutHorizontal) { + turnNextDirections = ymid === a.y; + + segmentEnd = { + x: a.x + MIN_SEGMENT_LENGTH * (/l/.test(startDirection) ? -1 : 1), + y: turnNextDirections ? ymid + MIN_SEGMENT_LENGTH : ymid + }; + } else { + turnNextDirections = xmid === a.x; + + segmentEnd = { + x: turnNextDirections ? xmid + MIN_SEGMENT_LENGTH : xmid, + y: a.y + MIN_SEGMENT_LENGTH * (/t/.test(startDirection) ? -1 : 1) + }; + } + + } else { + segmentEnd = { + x: xmid, + y: ymid + }; + } + } + + return { + waypoints: getBendpoints(a, segmentEnd, segmentDirections).concat(segmentEnd), + directions: segmentDirections, + turnNextDirections: turnNextDirections + }; + } + + function getStartSegment(a, b, directions) { + return getSegmentBendpoints(a, b, directions); + } + + function getEndSegment(a, b, directions) { + var invertedSegment = getSegmentBendpoints(b, a, invertDirections(directions)); + + return { + waypoints: invertedSegment.waypoints.slice().reverse(), + directions: invertDirections(invertedSegment.directions), + turnNextDirections: invertedSegment.turnNextDirections + }; + } + + function getMidSegment(startSegment, endSegment) { + + var startDirection = startSegment.directions.split(':')[1], + endDirection = endSegment.directions.split(':')[0]; + + if (startSegment.turnNextDirections) { + startDirection = startDirection == 'h' ? 'v' : 'h'; + } + + if (endSegment.turnNextDirections) { + endDirection = endDirection == 'h' ? 'v' : 'h'; + } + + var directions = startDirection + ':' + endDirection; + + var bendpoints = getBendpoints( + startSegment.waypoints[startSegment.waypoints.length - 1], + endSegment.waypoints[0], + directions + ); + + return { + waypoints: bendpoints, + directions: directions + }; + } + + function invertDirections(directions) { + return directions.split(':').reverse().join(':'); + } + + /** + * Handle simple layouts with maximum two bendpoints. + */ + function getSimpleBendpoints(a, b, directions) { + + var xmid = round$1((b.x - a.x) / 2 + a.x), + ymid = round$1((b.y - a.y) / 2 + a.y); + + // one point, right or left from a + if (directions === 'h:v') { + return [ { x: b.x, y: a.y } ]; + } + + // one point, above or below a + if (directions === 'v:h') { + return [ { x: a.x, y: b.y } ]; + } + + // vertical segment between a and b + if (directions === 'h:h') { + return [ + { x: xmid, y: a.y }, + { x: xmid, y: b.y } + ]; + } + + // horizontal segment between a and b + if (directions === 'v:v') { + return [ + { x: a.x, y: ymid }, + { x: b.x, y: ymid } + ]; + } + + throw new Error('invalid directions: can only handle varians of [hv]:[hv]'); + } + + + /** + * Returns the mid points for a manhattan connection between two points. + * + * @example h:h (horizontal:horizontal) + * + * [a]----[x] + * | + * [x]----[b] + * + * @example h:v (horizontal:vertical) + * + * [a]----[x] + * | + * [b] + * + * @example h:r (horizontal:right) + * + * [a]----[x] + * | + * [b]-[x] + * + * @param {Point} a + * @param {Point} b + * @param {string} directions + * + * @return {Array} + */ + function getBendpoints(a, b, directions) { + directions = directions || 'h:h'; + + if (!isValidDirections(directions)) { + throw new Error( + 'unknown directions: <' + directions + '>: ' + + 'must be specified as : ' + + 'with start/end in { h,v,t,r,b,l }' + ); + } + + // compute explicit directions, involving trbl dockings + // using a three segmented layouting algorithm + if (isExplicitDirections(directions)) { + var startSegment = getStartSegment(a, b, directions), + endSegment = getEndSegment(a, b, directions), + midSegment = getMidSegment(startSegment, endSegment); + + return [].concat( + startSegment.waypoints, + midSegment.waypoints, + endSegment.waypoints + ); + } + + // handle simple [hv]:[hv] cases that can be easily computed + return getSimpleBendpoints(a, b, directions); + } + + /** + * Create a connection between the two points according + * to the manhattan layout (only horizontal and vertical) edges. + * + * @param {Point} a + * @param {Point} b + * + * @param {string} [directions='h:h'] specifies manhattan directions for each point as {adirection}:{bdirection}. + A directionfor a point is either `h` (horizontal) or `v` (vertical) + * + * @return {Array} + */ + function connectPoints(a, b, directions) { + + var points = getBendpoints(a, b, directions); + + points.unshift(a); + points.push(b); + + return withoutRedundantPoints(points); + } + + + /** + * Connect two rectangles using a manhattan layouted connection. + * + * @param {Bounds} source source rectangle + * @param {Bounds} target target rectangle + * @param {Point} [start] source docking + * @param {Point} [end] target docking + * + * @param {Object} [hints] + * @param {string} [hints.preserveDocking=source] preserve docking on selected side + * @param {Array} [hints.preferredLayouts] + * @param {Point|boolean} [hints.connectionStart] whether the start changed + * @param {Point|boolean} [hints.connectionEnd] whether the end changed + * + * @return {Array} connection points + */ + function connectRectangles(source, target, start, end, hints) { + + var preferredLayouts = hints && hints.preferredLayouts || []; + + var preferredLayout = without(preferredLayouts, 'straight')[0] || 'h:h'; + + var threshold = ORIENTATION_THRESHOLD[preferredLayout] || 0; + + var orientation = getOrientation(source, target, threshold); + + var directions = getDirections(orientation, preferredLayout); + + start = start || getMid(source); + end = end || getMid(target); + + var directionSplit = directions.split(':'); + + // compute actual docking points for start / end + // this ensures we properly layout only parts of the + // connection that lies in between the two rectangles + var startDocking = getDockingPoint(start, source, directionSplit[0], invertOrientation(orientation)), + endDocking = getDockingPoint(end, target, directionSplit[1], orientation); + + return connectPoints(startDocking, endDocking, directions); + } + + + /** + * Repair the connection between two rectangles, of which one has been updated. + * + * @param {Bounds} source + * @param {Bounds} target + * @param {Point} [start] + * @param {Point} [end] + * @param {Array} [waypoints] + * @param {Object} [hints] + * @param {Array} [hints.preferredLayouts] list of preferred layouts + * @param {boolean} [hints.connectionStart] + * @param {boolean} [hints.connectionEnd] + * + * @return {Array} repaired waypoints + */ + function repairConnection(source, target, start, end, waypoints, hints) { + + if (isArray$3(start)) { + waypoints = start; + hints = end; + + start = getMid(source); + end = getMid(target); + } + + hints = assign({ preferredLayouts: [] }, hints); + waypoints = waypoints || []; + + var preferredLayouts = hints.preferredLayouts, + preferStraight = preferredLayouts.indexOf('straight') !== -1, + repairedWaypoints; + + // just layout non-existing or simple connections + // attempt to render straight lines, if required + + // attempt to layout a straight line + repairedWaypoints = preferStraight && tryLayoutStraight(source, target, start, end, hints); + + if (repairedWaypoints) { + return repairedWaypoints; + } + + // try to layout from end + repairedWaypoints = hints.connectionEnd && tryRepairConnectionEnd(target, source, end, waypoints); + + if (repairedWaypoints) { + return repairedWaypoints; + } + + // try to layout from start + repairedWaypoints = hints.connectionStart && tryRepairConnectionStart(source, target, start, waypoints); + + if (repairedWaypoints) { + return repairedWaypoints; + } + + // or whether nothing seems to have changed + if (!hints.connectionStart && !hints.connectionEnd && waypoints && waypoints.length) { + return waypoints; + } + + // simply reconnect if nothing else worked + return connectRectangles(source, target, start, end, hints); + } + + + function inRange(a, start, end) { + return a >= start && a <= end; + } + + function isInRange(axis, a, b) { + var size = { + x: 'width', + y: 'height' + }; + + return inRange(a[axis], b[axis], b[axis] + b[size[axis]]); + } + + /** + * Layout a straight connection + * + * @param {Bounds} source + * @param {Bounds} target + * @param {Point} start + * @param {Point} end + * @param {Object} [hints] + * + * @return {Array|null} waypoints if straight layout worked + */ + function tryLayoutStraight(source, target, start, end, hints) { + var axis = {}, + primaryAxis, + orientation; + + orientation = getOrientation(source, target); + + // only layout a straight connection if shapes are + // horizontally or vertically aligned + if (!/^(top|bottom|left|right)$/.test(orientation)) { + return null; + } + + if (/top|bottom/.test(orientation)) { + primaryAxis = 'x'; + } + + if (/left|right/.test(orientation)) { + primaryAxis = 'y'; + } + + if (hints.preserveDocking === 'target') { + + if (!isInRange(primaryAxis, end, source)) { + return null; + } + + axis[primaryAxis] = end[primaryAxis]; + + return [ + { + x: axis.x !== undefined ? axis.x : start.x, + y: axis.y !== undefined ? axis.y : start.y, + original: { + x: axis.x !== undefined ? axis.x : start.x, + y: axis.y !== undefined ? axis.y : start.y + } + }, + { + x: end.x, + y: end.y + } + ]; + + } else { + + if (!isInRange(primaryAxis, start, target)) { + return null; + } + + axis[primaryAxis] = start[primaryAxis]; + + return [ + { + x: start.x, + y: start.y + }, + { + x: axis.x !== undefined ? axis.x : end.x, + y: axis.y !== undefined ? axis.y : end.y, + original: { + x: axis.x !== undefined ? axis.x : end.x, + y: axis.y !== undefined ? axis.y : end.y + } + } + ]; + } + + } + + /** + * Repair a connection from start. + * + * @param {Bounds} moved + * @param {Bounds} other + * @param {Point} newDocking + * @param {Array} points originalPoints from moved to other + * + * @return {Array|null} the repaired points between the two rectangles + */ + function tryRepairConnectionStart(moved, other, newDocking, points) { + return _tryRepairConnectionSide(moved, other, newDocking, points); + } + + /** + * Repair a connection from end. + * + * @param {Bounds} moved + * @param {Bounds} other + * @param {Point} newDocking + * @param {Array} points originalPoints from moved to other + * + * @return {Array|null} the repaired points between the two rectangles + */ + function tryRepairConnectionEnd(moved, other, newDocking, points) { + var waypoints = points.slice().reverse(); + + waypoints = _tryRepairConnectionSide(moved, other, newDocking, waypoints); + + return waypoints ? waypoints.reverse() : null; + } + + /** + * Repair a connection from one side that moved. + * + * @param {Bounds} moved + * @param {Bounds} other + * @param {Point} newDocking + * @param {Array} points originalPoints from moved to other + * + * @return {Array} the repaired points between the two rectangles + */ + function _tryRepairConnectionSide(moved, other, newDocking, points) { + + function needsRelayout(points) { + if (points.length < 3) { + return true; + } + + if (points.length > 4) { + return false; + } + + // relayout if two points overlap + // this is most likely due to + return !!find(points, function(p, idx) { + var q = points[idx - 1]; + + return q && pointDistance(p, q) < 3; + }); + } + + function repairBendpoint(candidate, oldPeer, newPeer) { + + var alignment = pointsAligned(oldPeer, candidate); + + switch (alignment) { + case 'v': + + // repair horizontal alignment + return { x: newPeer.x, y: candidate.y }; + case 'h': + + // repair vertical alignment + return { x: candidate.x, y: newPeer.y }; + } + + return { x: candidate.x, y: candidate. y }; + } + + function removeOverlapping(points, a, b) { + var i; + + for (i = points.length - 2; i !== 0; i--) { + + // intersects (?) break, remove all bendpoints up to this one and relayout + if (pointInRect(points[i], a, INTERSECTION_THRESHOLD) || + pointInRect(points[i], b, INTERSECTION_THRESHOLD)) { + + // return sliced old connection + return points.slice(i); + } + } + + return points; + } + + // (0) only repair what has layoutable bendpoints + + // (1) if only one bendpoint and on shape moved onto other shapes axis + // (horizontally / vertically), relayout + + if (needsRelayout(points)) { + return null; + } + + var oldDocking = points[0], + newPoints = points.slice(), + slicedPoints; + + // (2) repair only last line segment and only if it was layouted before + + newPoints[0] = newDocking; + newPoints[1] = repairBendpoint(newPoints[1], oldDocking, newDocking); + + + // (3) if shape intersects with any bendpoint after repair, + // remove all segments up to this bendpoint and repair from there + slicedPoints = removeOverlapping(newPoints, moved, other); + + if (slicedPoints !== newPoints) { + newPoints = _tryRepairConnectionSide(moved, other, newDocking, slicedPoints); + } + + // (4) do NOT repair if repaired bendpoints are aligned + if (newPoints && pointsAligned(newPoints)) { + return null; + } + + return newPoints; + } + + + /** + * Returns the manhattan directions connecting two rectangles + * with the given orientation. + * + * Will always return the default layout, if it is specific + * regarding sides already (trbl). + * + * @example + * + * getDirections('top'); // -> 'v:v' + * getDirections('intersect'); // -> 't:t' + * + * getDirections('top-right', 'v:h'); // -> 'v:h' + * getDirections('top-right', 'h:h'); // -> 'h:h' + * + * + * @param {string} orientation + * @param {string} defaultLayout + * + * @return {string} + */ + function getDirections(orientation, defaultLayout) { + + // don't override specific trbl directions + if (isExplicitDirections(defaultLayout)) { + return defaultLayout; + } + + switch (orientation) { + case 'intersect': + return 't:t'; + + case 'top': + case 'bottom': + return 'v:v'; + + case 'left': + case 'right': + return 'h:h'; + + // 'top-left' + // 'top-right' + // 'bottom-left' + // 'bottom-right' + default: + return defaultLayout; + } + } + + function isValidDirections(directions) { + return directions && /^h|v|t|r|b|l:h|v|t|r|b|l$/.test(directions); + } + + function isExplicitDirections(directions) { + return directions && /t|r|b|l/.test(directions); + } + + function invertOrientation(orientation) { + return { + 'top': 'bottom', + 'bottom': 'top', + 'left': 'right', + 'right': 'left', + 'top-left': 'bottom-right', + 'bottom-right': 'top-left', + 'top-right': 'bottom-left', + 'bottom-left': 'top-right', + }[orientation]; + } + + function getDockingPoint(point, rectangle, dockingDirection, targetOrientation) { + + // ensure we end up with a specific docking direction + // based on the targetOrientation, if is being passed + + if (dockingDirection === 'h') { + dockingDirection = /left/.test(targetOrientation) ? 'l' : 'r'; + } + + if (dockingDirection === 'v') { + dockingDirection = /top/.test(targetOrientation) ? 't' : 'b'; + } + + if (dockingDirection === 't') { + return { original: point, x: point.x, y: rectangle.y }; + } + + if (dockingDirection === 'r') { + return { original: point, x: rectangle.x + rectangle.width, y: point.y }; + } + + if (dockingDirection === 'b') { + return { original: point, x: point.x, y: rectangle.y + rectangle.height }; + } + + if (dockingDirection === 'l') { + return { original: point, x: rectangle.x, y: point.y }; + } + + throw new Error('unexpected dockingDirection: <' + dockingDirection + '>'); + } + + + /** + * Return list of waypoints with redundant ones filtered out. + * + * @example + * + * Original points: + * + * [x] ----- [x] ------ [x] + * | + * [x] ----- [x] - [x] + * + * Filtered: + * + * [x] ---------------- [x] + * | + * [x] ----------- [x] + * + * @param {Array} waypoints + * + * @return {Array} + */ + function withoutRedundantPoints(waypoints) { + return waypoints.reduce(function(points, p, idx) { + + var previous = points[points.length - 1], + next = waypoints[idx + 1]; + + if (!pointsOnLine(previous, next, p, 0)) { + points.push(p); + } + + return points; + }, []); + } + + var ATTACH_ORIENTATION_PADDING = -10, + BOUNDARY_TO_HOST_THRESHOLD$1 = 40; + + var oppositeOrientationMapping = { + 'top': 'bottom', + 'top-right': 'bottom-left', + 'top-left': 'bottom-right', + 'right': 'left', + 'bottom': 'top', + 'bottom-right': 'top-left', + 'bottom-left': 'top-right', + 'left': 'right' + }; + + var orientationDirectionMapping = { + top: 't', + right: 'r', + bottom: 'b', + left: 'l' + }; + + + function BpmnLayouter() {} + + e(BpmnLayouter, BaseLayouter); + + + BpmnLayouter.prototype.layoutConnection = function(connection, hints) { + if (!hints) { + hints = {}; + } + + var source = hints.source || connection.source, + target = hints.target || connection.target, + waypoints = hints.waypoints || connection.waypoints, + connectionStart = hints.connectionStart, + connectionEnd = hints.connectionEnd; + + var manhattanOptions, + updatedWaypoints; + + if (!connectionStart) { + connectionStart = getConnectionDocking(waypoints && waypoints[ 0 ], source); + } + + if (!connectionEnd) { + connectionEnd = getConnectionDocking(waypoints && waypoints[ waypoints.length - 1 ], target); + } + + // TODO(nikku): support vertical modeling + // and invert preferredLayouts accordingly + + if (is$1(connection, 'bpmn:Association') || + is$1(connection, 'bpmn:DataAssociation')) { + + if (waypoints && !isCompensationAssociation(source, target)) { + return [].concat([ connectionStart ], waypoints.slice(1, -1), [ connectionEnd ]); + } + } + + if (is$1(connection, 'bpmn:MessageFlow')) { + manhattanOptions = getMessageFlowManhattanOptions(source, target); + } else if (is$1(connection, 'bpmn:SequenceFlow') || isCompensationAssociation(source, target)) { + + // layout all connection between flow elements h:h, except for + // (1) outgoing of boundary events -> layout based on attach orientation and target orientation + // (2) incoming/outgoing of gateways -> v:h for outgoing, h:v for incoming + // (3) loops + if (source === target) { + manhattanOptions = { + preferredLayouts: getLoopPreferredLayout(source, connection) + }; + } else if (is$1(source, 'bpmn:BoundaryEvent')) { + manhattanOptions = { + preferredLayouts: getBoundaryEventPreferredLayouts(source, target, connectionEnd) + }; + } else if (isExpandedSubProcess(source) || isExpandedSubProcess(target)) { + manhattanOptions = getSubProcessManhattanOptions(source); + } else if (is$1(source, 'bpmn:Gateway')) { + manhattanOptions = { + preferredLayouts: [ 'v:h' ] + }; + } else if (is$1(target, 'bpmn:Gateway')) { + manhattanOptions = { + preferredLayouts: [ 'h:v' ] + }; + } else { + manhattanOptions = { + preferredLayouts: [ 'h:h' ] + }; + } + } + + if (manhattanOptions) { + manhattanOptions = assign(manhattanOptions, hints); + + updatedWaypoints = withoutRedundantPoints(repairConnection( + source, + target, + connectionStart, + connectionEnd, + waypoints, + manhattanOptions + )); + } + + return updatedWaypoints || [ connectionStart, connectionEnd ]; + }; + + + // helpers ////////// + + function getAttachOrientation(attachedElement) { + var hostElement = attachedElement.host; + + return getOrientation(getMid(attachedElement), hostElement, ATTACH_ORIENTATION_PADDING); + } + + function getMessageFlowManhattanOptions(source, target) { + return { + preferredLayouts: [ 'straight', 'v:v' ], + preserveDocking: getMessageFlowPreserveDocking(source, target) + }; + } + + function getMessageFlowPreserveDocking(source, target) { + + // (1) docking element connected to participant has precedence + if (is$1(target, 'bpmn:Participant')) { + return 'source'; + } + + if (is$1(source, 'bpmn:Participant')) { + return 'target'; + } + + // (2) docking element connected to expanded sub-process has precedence + if (isExpandedSubProcess(target)) { + return 'source'; + } + + if (isExpandedSubProcess(source)) { + return 'target'; + } + + // (3) docking event has precedence + if (is$1(target, 'bpmn:Event')) { + return 'target'; + } + + if (is$1(source, 'bpmn:Event')) { + return 'source'; + } + + return null; + } + + function getSubProcessManhattanOptions(source) { + return { + preferredLayouts: [ 'straight', 'h:h' ], + preserveDocking: getSubProcessPreserveDocking(source) + }; + } + + function getSubProcessPreserveDocking(source) { + return isExpandedSubProcess(source) ? 'target' : 'source'; + } + + function getConnectionDocking(point, shape) { + return point ? (point.original || point) : getMid(shape); + } + + function isCompensationAssociation(source, target) { + return is$1(target, 'bpmn:Activity') && + is$1(source, 'bpmn:BoundaryEvent') && + target.businessObject.isForCompensation; + } + + function isExpandedSubProcess(element) { + return is$1(element, 'bpmn:SubProcess') && isExpanded(element); + } + + function isSame(a, b) { + return a === b; + } + + function isAnyOrientation(orientation, orientations) { + return orientations.indexOf(orientation) !== -1; + } + + function getHorizontalOrientation(orientation) { + var matches = /right|left/.exec(orientation); + + return matches && matches[0]; + } + + function getVerticalOrientation(orientation) { + var matches = /top|bottom/.exec(orientation); + + return matches && matches[0]; + } + + function isOppositeOrientation(a, b) { + return oppositeOrientationMapping[a] === b; + } + + function isOppositeHorizontalOrientation(a, b) { + var horizontalOrientation = getHorizontalOrientation(a); + + var oppositeHorizontalOrientation = oppositeOrientationMapping[horizontalOrientation]; + + return b.indexOf(oppositeHorizontalOrientation) !== -1; + } + + function isOppositeVerticalOrientation(a, b) { + var verticalOrientation = getVerticalOrientation(a); + + var oppositeVerticalOrientation = oppositeOrientationMapping[verticalOrientation]; + + return b.indexOf(oppositeVerticalOrientation) !== -1; + } + + function isHorizontalOrientation(orientation) { + return orientation === 'right' || orientation === 'left'; + } + + function getLoopPreferredLayout(source, connection) { + var waypoints = connection.waypoints; + + var orientation = waypoints && waypoints.length && getOrientation(waypoints[0], source); + + if (orientation === 'top') { + return [ 't:r' ]; + } else if (orientation === 'right') { + return [ 'r:b' ]; + } else if (orientation === 'left') { + return [ 'l:t' ]; + } + + return [ 'b:l' ]; + } + + function getBoundaryEventPreferredLayouts(source, target, end) { + var sourceMid = getMid(source), + targetMid = getMid(target), + attachOrientation = getAttachOrientation(source), + sourceLayout, + targetLayout; + + var isLoop = isSame(source.host, target); + + var attachedToSide = isAnyOrientation(attachOrientation, [ 'top', 'right', 'bottom', 'left' ]); + + var targetOrientation = getOrientation(targetMid, sourceMid, { + x: source.width / 2 + target.width / 2, + y: source.height / 2 + target.height / 2 + }); + + if (isLoop) { + return getBoundaryEventLoopLayout(attachOrientation, attachedToSide, source, target, end); + } + + // source layout + sourceLayout = getBoundaryEventSourceLayout(attachOrientation, targetOrientation, attachedToSide); + + // target layout + targetLayout = getBoundaryEventTargetLayout(attachOrientation, targetOrientation, attachedToSide); + + return [ sourceLayout + ':' + targetLayout ]; + } + + function getBoundaryEventLoopLayout(attachOrientation, attachedToSide, source, target, end) { + var orientation = attachedToSide ? attachOrientation : getVerticalOrientation(attachOrientation), + sourceLayout = orientationDirectionMapping[ orientation ], + targetLayout; + + if (attachedToSide) { + if (isHorizontalOrientation(attachOrientation)) { + targetLayout = shouldConnectToSameSide('y', source, target, end) ? 'h' : 'b'; + } else { + targetLayout = shouldConnectToSameSide('x', source, target, end) ? 'v' : 'l'; + } + } else { + targetLayout = 'v'; + } + + return [ sourceLayout + ':' + targetLayout ]; + } + + function shouldConnectToSameSide(axis, source, target, end) { + var threshold = BOUNDARY_TO_HOST_THRESHOLD$1; + + return !( + areCloseOnAxis(axis, end, target, threshold) || + areCloseOnAxis(axis, end, { + x: target.x + target.width, + y: target.y + target.height + }, threshold) || + areCloseOnAxis(axis, end, getMid(source), threshold) + ); + } + + function areCloseOnAxis(axis, a, b, threshold) { + return Math.abs(a[ axis ] - b[ axis ]) < threshold; + } + + function getBoundaryEventSourceLayout(attachOrientation, targetOrientation, attachedToSide) { + + // attached to either top, right, bottom or left side + if (attachedToSide) { + return orientationDirectionMapping[ attachOrientation ]; + } + + // attached to either top-right, top-left, bottom-right or bottom-left corner + + // same vertical or opposite horizontal orientation + if (isSame( + getVerticalOrientation(attachOrientation), getVerticalOrientation(targetOrientation) + ) || isOppositeOrientation( + getHorizontalOrientation(attachOrientation), getHorizontalOrientation(targetOrientation) + )) { + return orientationDirectionMapping[ getVerticalOrientation(attachOrientation) ]; + } + + // fallback + return orientationDirectionMapping[ getHorizontalOrientation(attachOrientation) ]; + } + + function getBoundaryEventTargetLayout(attachOrientation, targetOrientation, attachedToSide) { + + // attached to either top, right, bottom or left side + if (attachedToSide) { + if (isHorizontalOrientation(attachOrientation)) { + + // orientation is right or left + + // opposite horizontal orientation or same orientation + if ( + isOppositeHorizontalOrientation(attachOrientation, targetOrientation) || + isSame(attachOrientation, targetOrientation) + ) { + return 'h'; + } + + // fallback + return 'v'; + } else { + + // orientation is top or bottom + + // opposite vertical orientation or same orientation + if ( + isOppositeVerticalOrientation(attachOrientation, targetOrientation) || + isSame(attachOrientation, targetOrientation) + ) { + return 'v'; + } + + // fallback + return 'h'; + } + } + + // attached to either top-right, top-left, bottom-right or bottom-left corner + + // orientation is right, left + // or same vertical orientation but also right or left + if (isHorizontalOrientation(targetOrientation) || + (isSame(getVerticalOrientation(attachOrientation), getVerticalOrientation(targetOrientation)) && + getHorizontalOrientation(targetOrientation))) { + return 'h'; + } else { + return 'v'; + } + } + + function dockingToPoint(docking) { + + // use the dockings actual point and + // retain the original docking + return assign({ original: docking.point.original || docking.point }, docking.actual); + } + + + /** + * A {@link ConnectionDocking} that crops connection waypoints based on + * the path(s) of the connection source and target. + * + * @param {djs.core.ElementRegistry} elementRegistry + */ + function CroppingConnectionDocking(elementRegistry, graphicsFactory) { + this._elementRegistry = elementRegistry; + this._graphicsFactory = graphicsFactory; + } + + CroppingConnectionDocking.$inject = [ 'elementRegistry', 'graphicsFactory' ]; + + + /** + * @inheritDoc ConnectionDocking#getCroppedWaypoints + */ + CroppingConnectionDocking.prototype.getCroppedWaypoints = function(connection, source, target) { + + source = source || connection.source; + target = target || connection.target; + + var sourceDocking = this.getDockingPoint(connection, source, true), + targetDocking = this.getDockingPoint(connection, target); + + var croppedWaypoints = connection.waypoints.slice(sourceDocking.idx + 1, targetDocking.idx); + + croppedWaypoints.unshift(dockingToPoint(sourceDocking)); + croppedWaypoints.push(dockingToPoint(targetDocking)); + + return croppedWaypoints; + }; + + /** + * Return the connection docking point on the specified shape + * + * @inheritDoc ConnectionDocking#getDockingPoint + */ + CroppingConnectionDocking.prototype.getDockingPoint = function(connection, shape, dockStart) { + + var waypoints = connection.waypoints, + dockingIdx, + dockingPoint, + croppedPoint; + + dockingIdx = dockStart ? 0 : waypoints.length - 1; + dockingPoint = waypoints[dockingIdx]; + + croppedPoint = this._getIntersection(shape, connection, dockStart); + + return { + point: dockingPoint, + actual: croppedPoint || dockingPoint, + idx: dockingIdx + }; + }; + + + // helpers ////////////////////// + + CroppingConnectionDocking.prototype._getIntersection = function(shape, connection, takeFirst) { + + var shapePath = this._getShapePath(shape), + connectionPath = this._getConnectionPath(connection); + + return getElementLineIntersection(shapePath, connectionPath, takeFirst); + }; + + CroppingConnectionDocking.prototype._getConnectionPath = function(connection) { + return this._graphicsFactory.getConnectionPath(connection); + }; + + CroppingConnectionDocking.prototype._getShapePath = function(shape) { + return this._graphicsFactory.getShapePath(shape); + }; + + CroppingConnectionDocking.prototype._getGfx = function(element) { + return this._elementRegistry.getGraphics(element); + }; + + var ModelingModule = { + __init__: [ + 'modeling', + 'bpmnUpdater' + ], + __depends__: [ + BehaviorModule, + RulesModule, + DiOrderingModule, + OrderingModule, + ReplaceModule, + CommandModule, + TooltipsModule, + LabelSupportModule, + AttachSupportModule, + SelectionModule, + ChangeSupportModule, + SpaceToolModule + ], + bpmnFactory: [ 'type', BpmnFactory ], + bpmnUpdater: [ 'type', BpmnUpdater ], + elementFactory: [ 'type', ElementFactory ], + modeling: [ 'type', Modeling ], + layouter: [ 'type', BpmnLayouter ], + connectionDocking: [ 'type', CroppingConnectionDocking ] + }; + + var LOW_PRIORITY$2 = 500, + MEDIUM_PRIORITY = 1250, + HIGH_PRIORITY$2 = 1500; + + var round = Math.round; + + function mid(element) { + return { + x: element.x + round(element.width / 2), + y: element.y + round(element.height / 2) + }; + } + + /** + * A plugin that makes shapes draggable / droppable. + * + * @param {EventBus} eventBus + * @param {Dragging} dragging + * @param {Modeling} modeling + * @param {Selection} selection + * @param {Rules} rules + */ + function MoveEvents( + eventBus, dragging, modeling, + selection, rules) { + + // rules + + function canMove(shapes, delta, position, target) { + + return rules.allowed('elements.move', { + shapes: shapes, + delta: delta, + position: position, + target: target + }); + } + + + // move events + + // assign a high priority to this handler to setup the environment + // others may hook up later, e.g. at default priority and modify + // the move environment. + // + // This sets up the context with + // + // * shape: the primary shape being moved + // * shapes: a list of shapes to be moved + // * validatedShapes: a list of shapes that are being checked + // against the rules before and during move + // + eventBus.on('shape.move.start', HIGH_PRIORITY$2, function(event) { + + var context = event.context, + shape = event.shape, + shapes = selection.get().slice(); + + // move only single shape if the dragged element + // is not part of the current selection + if (shapes.indexOf(shape) === -1) { + shapes = [ shape ]; + } + + // ensure we remove nested elements in the collection + // and add attachers for a proper dragger + shapes = removeNested(shapes); + + // attach shapes to drag context + assign(context, { + shapes: shapes, + validatedShapes: shapes, + shape: shape + }); + }); + + + // assign a high priority to this handler to setup the environment + // others may hook up later, e.g. at default priority and modify + // the move environment + // + eventBus.on('shape.move.start', MEDIUM_PRIORITY, function(event) { + + var context = event.context, + validatedShapes = context.validatedShapes, + canExecute; + + canExecute = context.canExecute = canMove(validatedShapes); + + // check if we can move the elements + if (!canExecute) { + return false; + } + }); + + // assign a low priority to this handler + // to let others modify the move event before we update + // the context + // + eventBus.on('shape.move.move', LOW_PRIORITY$2, function(event) { + + var context = event.context, + validatedShapes = context.validatedShapes, + hover = event.hover, + delta = { x: event.dx, y: event.dy }, + position = { x: event.x, y: event.y }, + canExecute; + + // check if we can move the elements + canExecute = canMove(validatedShapes, delta, position, hover); + + context.delta = delta; + context.canExecute = canExecute; + + // simply ignore move over + if (canExecute === null) { + context.target = null; + + return; + } + + context.target = hover; + }); + + eventBus.on('shape.move.end', function(event) { + + var context = event.context; + + var delta = context.delta, + canExecute = context.canExecute, + isAttach = canExecute === 'attach', + shapes = context.shapes; + + if (canExecute === false) { + return false; + } + + // ensure we have actual pixel values deltas + // (important when zoom level was > 1 during move) + delta.x = round(delta.x); + delta.y = round(delta.y); + + if (delta.x === 0 && delta.y === 0) { + + // didn't move + return; + } + + modeling.moveElements(shapes, delta, context.target, { + primaryShape: context.shape, + attach: isAttach + }); + }); + + + // move activation + + eventBus.on('element.mousedown', function(event) { + + if (!isPrimaryButton(event)) { + return; + } + + var originalEvent = getOriginal$1(event); + + if (!originalEvent) { + throw new Error('must supply DOM mousedown event'); + } + + return start(originalEvent, event.element); + }); + + /** + * Start move. + * + * @param {MouseEvent} event + * @param {djs.model.Shape} shape + * @param {boolean} [activate] + * @param {Object} [context] + */ + function start(event, element, activate, context) { + if (isObject(activate)) { + context = activate; + activate = false; + } + + // do not move connections or the root element + if (element.waypoints || !element.parent) { + return; + } + + // ignore non-draggable hits + if (classes(event.target).has('djs-hit-no-move')) { + return; + } + + var referencePoint = mid(element); + + dragging.init(event, referencePoint, 'shape.move', { + cursor: 'grabbing', + autoActivate: activate, + data: { + shape: element, + context: context || {} + } + }); + + // we've handled the event + return true; + } + + // API + + this.start = start; + } + + MoveEvents.$inject = [ + 'eventBus', + 'dragging', + 'modeling', + 'selection', + 'rules' + ]; + + + /** + * Return a filtered list of elements that do not contain + * those nested into others. + * + * @param {Array} elements + * + * @return {Array} filtered + */ + function removeNested(elements) { + + var ids = groupBy(elements, 'id'); + + return filter(elements, function(element) { + while ((element = element.parent)) { + + // parent in selection + if (ids[element.id]) { + return false; + } + } + + return true; + }); + } + + var LOW_PRIORITY$1 = 499; + + var MARKER_DRAGGING = 'djs-dragging', + MARKER_OK$1 = 'drop-ok', + MARKER_NOT_OK$1 = 'drop-not-ok', + MARKER_NEW_PARENT = 'new-parent', + MARKER_ATTACH = 'attach-ok'; + + + /** + * Provides previews for moving shapes when moving. + * + * @param {EventBus} eventBus + * @param {ElementRegistry} elementRegistry + * @param {Canvas} canvas + * @param {Styles} styles + */ + function MovePreview( + eventBus, canvas, styles, previewSupport) { + + function getVisualDragShapes(shapes) { + var elements = getAllDraggedElements(shapes); + + var filteredElements = removeEdges(elements); + + return filteredElements; + } + + function getAllDraggedElements(shapes) { + var allShapes = selfAndAllChildren(shapes, true); + + var allConnections = map(allShapes, function(shape) { + return (shape.incoming || []).concat(shape.outgoing || []); + }); + + return flatten(allShapes.concat(allConnections)); + } + + /** + * Sets drop marker on an element. + */ + function setMarker(element, marker) { + + [ MARKER_ATTACH, MARKER_OK$1, MARKER_NOT_OK$1, MARKER_NEW_PARENT ].forEach(function(m) { + + if (m === marker) { + canvas.addMarker(element, m); + } else { + canvas.removeMarker(element, m); + } + }); + } + + /** + * Make an element draggable. + * + * @param {Object} context + * @param {djs.model.Base} element + * @param {boolean} addMarker + */ + function makeDraggable(context, element, addMarker) { + + previewSupport.addDragger(element, context.dragGroup); + + if (addMarker) { + canvas.addMarker(element, MARKER_DRAGGING); + } + + if (context.allDraggedElements) { + context.allDraggedElements.push(element); + } else { + context.allDraggedElements = [ element ]; + } + } + + // assign a low priority to this handler + // to let others modify the move context before + // we draw things + eventBus.on('shape.move.start', LOW_PRIORITY$1, function(event) { + var context = event.context, + dragShapes = context.shapes, + allDraggedElements = context.allDraggedElements; + + var visuallyDraggedShapes = getVisualDragShapes(dragShapes); + + if (!context.dragGroup) { + var dragGroup = create$1('g'); + + attr(dragGroup, styles.cls('djs-drag-group', [ 'no-events' ])); + + var activeLayer = canvas.getActiveLayer(); + + append(activeLayer, dragGroup); + + context.dragGroup = dragGroup; + } + + // add previews + visuallyDraggedShapes.forEach(function(shape) { + previewSupport.addDragger(shape, context.dragGroup); + }); + + // cache all dragged elements / gfx + // so that we can quickly undo their state changes later + if (!allDraggedElements) { + allDraggedElements = getAllDraggedElements(dragShapes); + } else { + allDraggedElements = flatten([ + allDraggedElements, + getAllDraggedElements(dragShapes) + ]); + } + + // add dragging marker + forEach$1(allDraggedElements, function(e) { + canvas.addMarker(e, MARKER_DRAGGING); + }); + + context.allDraggedElements = allDraggedElements; + + // determine, if any of the dragged elements have different parents + context.differentParents = haveDifferentParents(dragShapes); + }); + + // update previews + eventBus.on('shape.move.move', LOW_PRIORITY$1, function(event) { + + var context = event.context, + dragGroup = context.dragGroup, + target = context.target, + parent = context.shape.parent, + canExecute = context.canExecute; + + if (target) { + if (canExecute === 'attach') { + setMarker(target, MARKER_ATTACH); + } else if (context.canExecute && target && target.id !== parent.id) { + setMarker(target, MARKER_NEW_PARENT); + } else { + setMarker(target, context.canExecute ? MARKER_OK$1 : MARKER_NOT_OK$1); + } + } + + translate$2(dragGroup, event.dx, event.dy); + }); + + eventBus.on([ 'shape.move.out', 'shape.move.cleanup' ], function(event) { + var context = event.context, + target = context.target; + + if (target) { + setMarker(target, null); + } + }); + + // remove previews + eventBus.on('shape.move.cleanup', function(event) { + + var context = event.context, + allDraggedElements = context.allDraggedElements, + dragGroup = context.dragGroup; + + + // remove dragging marker + forEach$1(allDraggedElements, function(e) { + canvas.removeMarker(e, MARKER_DRAGGING); + }); + + if (dragGroup) { + remove$1(dragGroup); + } + }); + + + // API ////////////////////// + + /** + * Make an element draggable. + * + * @param {Object} context + * @param {djs.model.Base} element + * @param {boolean} addMarker + */ + this.makeDraggable = makeDraggable; + } + + MovePreview.$inject = [ + 'eventBus', + 'canvas', + 'styles', + 'previewSupport' + ]; + + + // helpers ////////////////////// + + /** + * returns elements minus all connections + * where source or target is not elements + */ + function removeEdges(elements) { + + var filteredElements = filter(elements, function(element) { + + if (!isConnection$2(element)) { + return true; + } else { + + return ( + find(elements, matchPattern({ id: element.source.id })) && + find(elements, matchPattern({ id: element.target.id })) + ); + } + }); + + return filteredElements; + } + + function haveDifferentParents(elements) { + return size(groupBy(elements, function(e) { return e.parent && e.parent.id; })) !== 1; + } + + /** + * Checks if an element is a connection. + */ + function isConnection$2(element) { + return element.waypoints; + } + + var MoveModule = { + __depends__: [ + InteractionEventsModule$1, + SelectionModule, + OutlineModule, + RulesModule$1, + DraggingModule, + PreviewSupportModule + ], + __init__: [ + 'move', + 'movePreview' + ], + move: [ 'type', MoveEvents ], + movePreview: [ 'type', MovePreview ] + }; + + var TOGGLE_SELECTOR = '.djs-palette-toggle', + ENTRY_SELECTOR = '.entry', + ELEMENT_SELECTOR = TOGGLE_SELECTOR + ', ' + ENTRY_SELECTOR; + + var PALETTE_PREFIX = 'djs-palette-', + PALETTE_SHOWN_CLS = 'shown', + PALETTE_OPEN_CLS = 'open', + PALETTE_TWO_COLUMN_CLS = 'two-column'; + + var DEFAULT_PRIORITY = 1000; + + + /** + * A palette containing modeling elements. + */ + function Palette(eventBus, canvas) { + + this._eventBus = eventBus; + this._canvas = canvas; + + var self = this; + + eventBus.on('tool-manager.update', function(event) { + var tool = event.tool; + + self.updateToolHighlight(tool); + }); + + eventBus.on('i18n.changed', function() { + self._update(); + }); + + eventBus.on('diagram.init', function() { + + self._diagramInitialized = true; + + self._rebuild(); + }); + } + + Palette.$inject = [ 'eventBus', 'canvas' ]; + + + /** + * Register a provider with the palette + * + * @param {number} [priority=1000] + * @param {PaletteProvider} provider + * + * @example + * const paletteProvider = { + * getPaletteEntries: function() { + * return function(entries) { + * return { + * ...entries, + * 'entry-1': { + * label: 'My Entry', + * action: function() { alert("I have been clicked!"); } + * } + * }; + * } + * } + * }; + * + * palette.registerProvider(800, paletteProvider); + */ + Palette.prototype.registerProvider = function(priority, provider) { + if (!provider) { + provider = priority; + priority = DEFAULT_PRIORITY; + } + + this._eventBus.on('palette.getProviders', priority, function(event) { + event.providers.push(provider); + }); + + this._rebuild(); + }; + + + /** + * Returns the palette entries + * + * @return {Object} map of entries + */ + Palette.prototype.getEntries = function() { + var providers = this._getProviders(); + + return providers.reduce(addPaletteEntries, {}); + }; + + Palette.prototype._rebuild = function() { + + if (!this._diagramInitialized) { + return; + } + + var providers = this._getProviders(); + + if (!providers.length) { + return; + } + + if (!this._container) { + this._init(); + } + + this._update(); + }; + + /** + * Initialize + */ + Palette.prototype._init = function() { + + var self = this; + + var eventBus = this._eventBus; + + var parentContainer = this._getParentContainer(); + + var container = this._container = domify(Palette.HTML_MARKUP); + + parentContainer.appendChild(container); + classes$1(parentContainer).add(PALETTE_PREFIX + PALETTE_SHOWN_CLS); + + delegate.bind(container, ELEMENT_SELECTOR, 'click', function(event) { + + var target = event.delegateTarget; + + if (matchesSelector(target, TOGGLE_SELECTOR)) { + return self.toggle(); + } + + self.trigger('click', event); + }); + + // prevent drag propagation + componentEvent.bind(container, 'mousedown', function(event) { + event.stopPropagation(); + }); + + // prevent drag propagation + delegate.bind(container, ENTRY_SELECTOR, 'dragstart', function(event) { + self.trigger('dragstart', event); + }); + + eventBus.on('canvas.resized', this._layoutChanged, this); + + eventBus.fire('palette.create', { + container: container + }); + }; + + Palette.prototype._getProviders = function(id) { + + var event = this._eventBus.createEvent({ + type: 'palette.getProviders', + providers: [] + }); + + this._eventBus.fire(event); + + return event.providers; + }; + + /** + * Update palette state. + * + * @param {Object} [state] { open, twoColumn } + */ + Palette.prototype._toggleState = function(state) { + + state = state || {}; + + var parent = this._getParentContainer(), + container = this._container; + + var eventBus = this._eventBus; + + var twoColumn; + + var cls = classes$1(container), + parentCls = classes$1(parent); + + if ('twoColumn' in state) { + twoColumn = state.twoColumn; + } else { + twoColumn = this._needsCollapse(parent.clientHeight, this._entries || {}); + } + + // always update two column + cls.toggle(PALETTE_TWO_COLUMN_CLS, twoColumn); + parentCls.toggle(PALETTE_PREFIX + PALETTE_TWO_COLUMN_CLS, twoColumn); + + if ('open' in state) { + cls.toggle(PALETTE_OPEN_CLS, state.open); + parentCls.toggle(PALETTE_PREFIX + PALETTE_OPEN_CLS, state.open); + } + + eventBus.fire('palette.changed', { + twoColumn: twoColumn, + open: this.isOpen() + }); + }; + + Palette.prototype._update = function() { + + var entriesContainer = query('.djs-palette-entries', this._container), + entries = this._entries = this.getEntries(); + + clear$1(entriesContainer); + + forEach$1(entries, function(entry, id) { + + var grouping = entry.group || 'default'; + + var container = query('[data-group=' + cssEscape(grouping) + ']', entriesContainer); + if (!container) { + container = domify('
    '); + attr$1(container, 'data-group', grouping); + + entriesContainer.appendChild(container); + } + + var html = entry.html || ( + entry.separator ? + '
    ' : + '
    '); + + + var control = domify(html); + container.appendChild(control); + + if (!entry.separator) { + attr$1(control, 'data-action', id); + + if (entry.title) { + attr$1(control, 'title', entry.title); + } + + if (entry.className) { + addClasses(control, entry.className); + } + + if (entry.imageUrl) { + var image = domify(''); + attr$1(image, 'src', entry.imageUrl); + + control.appendChild(image); + } + } + }); + + // open after update + this.open(); + }; + + + /** + * Trigger an action available on the palette + * + * @param {string} action + * @param {Event} event + */ + Palette.prototype.trigger = function(action, event, autoActivate) { + var entries = this._entries, + entry, + handler, + originalEvent, + button = event.delegateTarget || event.target; + + if (!button) { + return event.preventDefault(); + } + + entry = entries[attr$1(button, 'data-action')]; + + // when user clicks on the palette and not on an action + if (!entry) { + return; + } + + handler = entry.action; + + originalEvent = event.originalEvent || event; + + // simple action (via callback function) + if (isFunction(handler)) { + if (action === 'click') { + handler(originalEvent, autoActivate); + } + } else { + if (handler[action]) { + handler[action](originalEvent, autoActivate); + } + } + + // silence other actions + event.preventDefault(); + }; + + Palette.prototype._layoutChanged = function() { + this._toggleState({}); + }; + + /** + * Do we need to collapse to two columns? + * + * @param {number} availableHeight + * @param {Object} entries + * + * @return {boolean} + */ + Palette.prototype._needsCollapse = function(availableHeight, entries) { + + // top margin + bottom toggle + bottom margin + // implementors must override this method if they + // change the palette styles + var margin = 20 + 10 + 20; + + var entriesHeight = Object.keys(entries).length * 46; + + return availableHeight < entriesHeight + margin; + }; + + /** + * Close the palette + */ + Palette.prototype.close = function() { + + this._toggleState({ + open: false, + twoColumn: false + }); + }; + + + /** + * Open the palette + */ + Palette.prototype.open = function() { + this._toggleState({ open: true }); + }; + + + Palette.prototype.toggle = function(open) { + if (this.isOpen()) { + this.close(); + } else { + this.open(); + } + }; + + Palette.prototype.isActiveTool = function(tool) { + return tool && this._activeTool === tool; + }; + + Palette.prototype.updateToolHighlight = function(name) { + var entriesContainer, + toolsContainer; + + if (!this._toolsContainer) { + entriesContainer = query('.djs-palette-entries', this._container); + + this._toolsContainer = query('[data-group=tools]', entriesContainer); + } + + toolsContainer = this._toolsContainer; + + forEach$1(toolsContainer.children, function(tool) { + var actionName = tool.getAttribute('data-action'); + + if (!actionName) { + return; + } + + var toolClasses = classes$1(tool); + + actionName = actionName.replace('-tool', ''); + + if (toolClasses.contains('entry') && actionName === name) { + toolClasses.add('highlighted-entry'); + } else { + toolClasses.remove('highlighted-entry'); + } + }); + }; + + + /** + * Return true if the palette is opened. + * + * @example + * + * palette.open(); + * + * if (palette.isOpen()) { + * // yes, we are open + * } + * + * @return {boolean} true if palette is opened + */ + Palette.prototype.isOpen = function() { + return classes$1(this._container).has(PALETTE_OPEN_CLS); + }; + + /** + * Get container the palette lives in. + * + * @return {Element} + */ + Palette.prototype._getParentContainer = function() { + return this._canvas.getContainer(); + }; + + + /* markup definition */ + + Palette.HTML_MARKUP = + '
    ' + + '
    ' + + '
    ' + + '
    '; + + + // helpers ////////////////////// + + function addClasses(element, classNames) { + + var classes = classes$1(element); + + var actualClassNames = isArray$3(classNames) ? classNames : classNames.split(/\s+/g); + actualClassNames.forEach(function(cls) { + classes.add(cls); + }); + } + + function addPaletteEntries(entries, provider) { + + var entriesOrUpdater = provider.getPaletteEntries(); + + if (isFunction(entriesOrUpdater)) { + return entriesOrUpdater(entries); + } + + forEach$1(entriesOrUpdater, function(entry, id) { + entries[id] = entry; + }); + + return entries; + } + + var PaletteModule$1 = { + __init__: [ 'palette' ], + palette: [ 'type', Palette ] + }; + + var LASSO_TOOL_CURSOR = 'crosshair'; + + + function LassoTool( + eventBus, canvas, dragging, + elementRegistry, selection, toolManager, + mouse) { + + this._selection = selection; + this._dragging = dragging; + this._mouse = mouse; + + var self = this; + + // lasso visuals implementation + + /** + * A helper that realizes the selection box visual + */ + var visuals = { + + create: function(context) { + var container = canvas.getActiveLayer(), + frame; + + frame = context.frame = create$1('rect'); + attr(frame, { + class: 'djs-lasso-overlay', + width: 1, + height: 1, + x: 0, + y: 0 + }); + + append(container, frame); + }, + + update: function(context) { + var frame = context.frame, + bbox = context.bbox; + + attr(frame, { + x: bbox.x, + y: bbox.y, + width: bbox.width, + height: bbox.height + }); + }, + + remove: function(context) { + + if (context.frame) { + remove$1(context.frame); + } + } + }; + + toolManager.registerTool('lasso', { + tool: 'lasso.selection', + dragging: 'lasso' + }); + + eventBus.on('lasso.selection.end', function(event) { + var target = event.originalEvent.target; + + // only reactive on diagram click + // on some occasions, event.hover is not set and we have to check if the target is an svg + if (!event.hover && !(target instanceof SVGElement)) { + return; + } + + eventBus.once('lasso.selection.ended', function() { + self.activateLasso(event.originalEvent, true); + }); + }); + + // lasso interaction implementation + + eventBus.on('lasso.end', function(event) { + + var bbox = toBBox(event); + + var elements = elementRegistry.filter(function(element) { + return element; + }); + + self.select(elements, bbox); + }); + + eventBus.on('lasso.start', function(event) { + + var context = event.context; + + context.bbox = toBBox(event); + visuals.create(context); + }); + + eventBus.on('lasso.move', function(event) { + + var context = event.context; + + context.bbox = toBBox(event); + visuals.update(context); + }); + + eventBus.on('lasso.cleanup', function(event) { + + var context = event.context; + + visuals.remove(context); + }); + + + // event integration + + eventBus.on('element.mousedown', 1500, function(event) { + + if (!hasSecondaryModifier(event)) { + return; + } + + self.activateLasso(event.originalEvent); + + // we've handled the event + return true; + }); + } + + LassoTool.$inject = [ + 'eventBus', + 'canvas', + 'dragging', + 'elementRegistry', + 'selection', + 'toolManager', + 'mouse' + ]; + + + LassoTool.prototype.activateLasso = function(event, autoActivate) { + + this._dragging.init(event, 'lasso', { + autoActivate: autoActivate, + cursor: LASSO_TOOL_CURSOR, + data: { + context: {} + } + }); + }; + + LassoTool.prototype.activateSelection = function(event, autoActivate) { + + this._dragging.init(event, 'lasso.selection', { + trapClick: false, + autoActivate: autoActivate, + cursor: LASSO_TOOL_CURSOR, + data: { + context: {} + } + }); + }; + + LassoTool.prototype.select = function(elements, bbox) { + var selectedElements = getEnclosedElements(elements, bbox); + + this._selection.select(values(selectedElements)); + }; + + LassoTool.prototype.toggle = function() { + if (this.isActive()) { + return this._dragging.cancel(); + } + + var mouseEvent = this._mouse.getLastMoveEvent(); + + this.activateSelection(mouseEvent, !!mouseEvent); + }; + + LassoTool.prototype.isActive = function() { + var context = this._dragging.context(); + + return context && /^lasso/.test(context.prefix); + }; + + + + function toBBox(event) { + + var start = { + + x: event.x - event.dx, + y: event.y - event.dy + }; + + var end = { + x: event.x, + y: event.y + }; + + var bbox; + + if ((start.x <= end.x && start.y < end.y) || + (start.x < end.x && start.y <= end.y)) { + + bbox = { + x: start.x, + y: start.y, + width: end.x - start.x, + height: end.y - start.y + }; + } else if ((start.x >= end.x && start.y < end.y) || + (start.x > end.x && start.y <= end.y)) { + + bbox = { + x: end.x, + y: start.y, + width: start.x - end.x, + height: end.y - start.y + }; + } else if ((start.x <= end.x && start.y > end.y) || + (start.x < end.x && start.y >= end.y)) { + + bbox = { + x: start.x, + y: end.y, + width: end.x - start.x, + height: start.y - end.y + }; + } else if ((start.x >= end.x && start.y > end.y) || + (start.x > end.x && start.y >= end.y)) { + + bbox = { + x: end.x, + y: end.y, + width: start.x - end.x, + height: start.y - end.y + }; + } else { + + bbox = { + x: end.x, + y: end.y, + width: 0, + height: 0 + }; + } + return bbox; + } + + var LassoToolModule = { + __depends__: [ + ToolManagerModule, + MouseModule + ], + __init__: [ 'lassoTool' ], + lassoTool: [ 'type', LassoTool ] + }; + + var HIGH_PRIORITY$1 = 1500; + var HAND_CURSOR = 'grab'; + + + function HandTool( + eventBus, canvas, dragging, + injector, toolManager, mouse) { + + this._dragging = dragging; + this._mouse = mouse; + + var self = this, + keyboard = injector.get('keyboard', false); + + toolManager.registerTool('hand', { + tool: 'hand', + dragging: 'hand.move' + }); + + eventBus.on('element.mousedown', HIGH_PRIORITY$1, function(event) { + + if (!hasPrimaryModifier(event)) { + return; + } + + self.activateMove(event.originalEvent, true); + + return false; + }); + + keyboard && keyboard.addListener(HIGH_PRIORITY$1, function(e) { + if (!isSpace(e.keyEvent) || self.isActive()) { + return; + } + + var mouseEvent = self._mouse.getLastMoveEvent(); + + self.activateMove(mouseEvent, !!mouseEvent); + }, 'keyboard.keydown'); + + keyboard && keyboard.addListener(HIGH_PRIORITY$1, function(e) { + if (!isSpace(e.keyEvent) || !self.isActive()) { + return; + } + + self.toggle(); + }, 'keyboard.keyup'); + + eventBus.on('hand.end', function(event) { + var target = event.originalEvent.target; + + // only reactive on diagram click + // on some occasions, event.hover is not set and we have to check if the target is an svg + if (!event.hover && !(target instanceof SVGElement)) { + return false; + } + + eventBus.once('hand.ended', function() { + self.activateMove(event.originalEvent, { reactivate: true }); + }); + + }); + + eventBus.on('hand.move.move', function(event) { + var scale = canvas.viewbox().scale; + + canvas.scroll({ + dx: event.dx * scale, + dy: event.dy * scale + }); + }); + + eventBus.on('hand.move.end', function(event) { + var context = event.context, + reactivate = context.reactivate; + + // Don't reactivate if the user is using the keyboard keybinding + if (!hasPrimaryModifier(event) && reactivate) { + + eventBus.once('hand.move.ended', function(event) { + self.activateHand(event.originalEvent, true, true); + }); + + } + + return false; + }); + + } + + HandTool.$inject = [ + 'eventBus', + 'canvas', + 'dragging', + 'injector', + 'toolManager', + 'mouse' + ]; + + + HandTool.prototype.activateMove = function(event, autoActivate, context) { + if (typeof autoActivate === 'object') { + context = autoActivate; + autoActivate = false; + } + + this._dragging.init(event, 'hand.move', { + autoActivate: autoActivate, + cursor: HAND_CURSOR, + data: { + context: context || {} + } + }); + }; + + HandTool.prototype.activateHand = function(event, autoActivate, reactivate) { + this._dragging.init(event, 'hand', { + trapClick: false, + autoActivate: autoActivate, + cursor: HAND_CURSOR, + data: { + context: { + reactivate: reactivate + } + } + }); + }; + + HandTool.prototype.toggle = function() { + if (this.isActive()) { + return this._dragging.cancel(); + } + + var mouseEvent = this._mouse.getLastMoveEvent(); + + this.activateHand(mouseEvent, !!mouseEvent); + }; + + HandTool.prototype.isActive = function() { + var context = this._dragging.context(); + + if (context) { + return /^(hand|hand\.move)$/.test(context.prefix); + } + + return false; + }; + + // helpers ////////// + + function isSpace(keyEvent) { + return isKey(' ', keyEvent); + } + + var HandToolModule = { + __depends__: [ + ToolManagerModule, + MouseModule + ], + __init__: [ 'handTool' ], + handTool: [ 'type', HandTool ] + }; + + var MARKER_OK = 'connect-ok', + MARKER_NOT_OK = 'connect-not-ok'; + + /** + * @class + * @constructor + * + * @param {EventBus} eventBus + * @param {Dragging} dragging + * @param {Connect} connect + * @param {Canvas} canvas + * @param {ToolManager} toolManager + * @param {Rules} rules + * @param {Mouse} mouse + */ + function GlobalConnect( + eventBus, dragging, connect, + canvas, toolManager, rules, + mouse) { + + var self = this; + + this._dragging = dragging; + this._rules = rules; + this._mouse = mouse; + + toolManager.registerTool('global-connect', { + tool: 'global-connect', + dragging: 'global-connect.drag' + }); + + eventBus.on('global-connect.hover', function(event) { + var context = event.context, + startTarget = event.hover; + + var canStartConnect = context.canStartConnect = self.canStartConnect(startTarget); + + // simply ignore hover + if (canStartConnect === null) { + return; + } + + context.startTarget = startTarget; + + canvas.addMarker(startTarget, canStartConnect ? MARKER_OK : MARKER_NOT_OK); + }); + + + eventBus.on([ 'global-connect.out', 'global-connect.cleanup' ], function(event) { + var startTarget = event.context.startTarget, + canStartConnect = event.context.canStartConnect; + + if (startTarget) { + canvas.removeMarker(startTarget, canStartConnect ? MARKER_OK : MARKER_NOT_OK); + } + }); + + + eventBus.on([ 'global-connect.ended' ], function(event) { + var context = event.context, + startTarget = context.startTarget, + startPosition = { + x: event.x, + y: event.y + }; + + var canStartConnect = self.canStartConnect(startTarget); + + if (!canStartConnect) { + return; + } + + eventBus.once('element.out', function() { + eventBus.once([ 'connect.ended', 'connect.canceled' ], function() { + eventBus.fire('global-connect.drag.ended'); + }); + + connect.start(null, startTarget, startPosition); + }); + + return false; + }); + } + + GlobalConnect.$inject = [ + 'eventBus', + 'dragging', + 'connect', + 'canvas', + 'toolManager', + 'rules', + 'mouse' + ]; + + /** + * Initiates tool activity. + */ + GlobalConnect.prototype.start = function(event, autoActivate) { + this._dragging.init(event, 'global-connect', { + autoActivate: autoActivate, + trapClick: false, + data: { + context: {} + } + }); + }; + + GlobalConnect.prototype.toggle = function() { + + if (this.isActive()) { + return this._dragging.cancel(); + } + + var mouseEvent = this._mouse.getLastMoveEvent(); + + return this.start(mouseEvent, !!mouseEvent); + }; + + GlobalConnect.prototype.isActive = function() { + var context = this._dragging.context(); + + return context && /^global-connect/.test(context.prefix); + }; + + /** + * Check if source shape can initiate connection. + * + * @param {Shape} startTarget + * @return {boolean} + */ + GlobalConnect.prototype.canStartConnect = function(startTarget) { + return this._rules.allowed('connection.start', { source: startTarget }); + }; + + var GlobalConnectModule = { + __depends__: [ + ConnectModule, + RulesModule$1, + DraggingModule, + ToolManagerModule, + MouseModule + ], + globalConnect: [ 'type', GlobalConnect ] + }; + + /** + * A palette provider for BPMN 2.0 elements. + */ + function PaletteProvider( + palette, create, elementFactory, + spaceTool, lassoTool, handTool, + globalConnect, translate) { + + this._palette = palette; + this._create = create; + this._elementFactory = elementFactory; + this._spaceTool = spaceTool; + this._lassoTool = lassoTool; + this._handTool = handTool; + this._globalConnect = globalConnect; + this._translate = translate; + + palette.registerProvider(this); + } + + PaletteProvider.$inject = [ + 'palette', + 'create', + 'elementFactory', + 'spaceTool', + 'lassoTool', + 'handTool', + 'globalConnect', + 'translate' + ]; + + + PaletteProvider.prototype.getPaletteEntries = function(element) { + + var actions = {}, + create = this._create, + elementFactory = this._elementFactory, + spaceTool = this._spaceTool, + lassoTool = this._lassoTool, + handTool = this._handTool, + globalConnect = this._globalConnect, + translate = this._translate; + + function createAction(type, group, className, title, options) { + + function createListener(event) { + var shape = elementFactory.createShape(assign({ type: type }, options)); + + if (options) { + var di = getDi(shape); + di.isExpanded = options.isExpanded; + } + + create.start(event, shape); + } + + var shortType = type.replace(/^bpmn:/, ''); + + return { + group: group, + className: className, + title: title || translate('Create {type}', { type: shortType }), + action: { + dragstart: createListener, + click: createListener + } + }; + } + + function createSubprocess(event) { + var subProcess = elementFactory.createShape({ + type: 'bpmn:SubProcess', + x: 0, + y: 0, + isExpanded: true + }); + + var startEvent = elementFactory.createShape({ + type: 'bpmn:StartEvent', + x: 40, + y: 82, + parent: subProcess + }); + + create.start(event, [ subProcess, startEvent ], { + hints: { + autoSelect: [ subProcess ] + } + }); + } + + function createParticipant(event) { + create.start(event, elementFactory.createParticipantShape()); + } + + assign(actions, { + 'hand-tool': { + group: 'tools', + className: 'bpmn-icon-hand-tool', + title: translate('Activate the hand tool'), + action: { + click: function(event) { + handTool.activateHand(event); + } + } + }, + 'lasso-tool': { + group: 'tools', + className: 'bpmn-icon-lasso-tool', + title: translate('Activate the lasso tool'), + action: { + click: function(event) { + lassoTool.activateSelection(event); + } + } + }, + 'space-tool': { + group: 'tools', + className: 'bpmn-icon-space-tool', + title: translate('Activate the create/remove space tool'), + action: { + click: function(event) { + spaceTool.activateSelection(event); + } + } + }, + 'global-connect-tool': { + group: 'tools', + className: 'bpmn-icon-connection-multi', + title: translate('Activate the global connect tool'), + action: { + click: function(event) { + globalConnect.start(event); + } + } + }, + 'tool-separator': { + group: 'tools', + separator: true + }, + 'create.start-event': createAction( + 'bpmn:StartEvent', 'event', 'bpmn-icon-start-event-none', + translate('Create StartEvent') + ), + 'create.intermediate-event': createAction( + 'bpmn:IntermediateThrowEvent', 'event', 'bpmn-icon-intermediate-event-none', + translate('Create Intermediate/Boundary Event') + ), + 'create.end-event': createAction( + 'bpmn:EndEvent', 'event', 'bpmn-icon-end-event-none', + translate('Create EndEvent') + ), + 'create.exclusive-gateway': createAction( + 'bpmn:ExclusiveGateway', 'gateway', 'bpmn-icon-gateway-none', + translate('Create Gateway') + ), + 'create.task': createAction( + 'bpmn:Task', 'activity', 'bpmn-icon-task', + translate('Create Task') + ), + 'create.data-object': createAction( + 'bpmn:DataObjectReference', 'data-object', 'bpmn-icon-data-object', + translate('Create DataObjectReference') + ), + 'create.data-store': createAction( + 'bpmn:DataStoreReference', 'data-store', 'bpmn-icon-data-store', + translate('Create DataStoreReference') + ), + 'create.subprocess-expanded': { + group: 'activity', + className: 'bpmn-icon-subprocess-expanded', + title: translate('Create expanded SubProcess'), + action: { + dragstart: createSubprocess, + click: createSubprocess + } + }, + 'create.participant-expanded': { + group: 'collaboration', + className: 'bpmn-icon-participant', + title: translate('Create Pool/Participant'), + action: { + dragstart: createParticipant, + click: createParticipant + } + }, + 'create.group': createAction( + 'bpmn:Group', 'artifact', 'bpmn-icon-group', + translate('Create Group') + ), + }); + + return actions; + }; + + var PaletteModule = { + __depends__: [ + PaletteModule$1, + CreateModule, + SpaceToolModule, + LassoToolModule, + HandToolModule, + GlobalConnectModule, + translate + ], + __init__: [ 'paletteProvider' ], + paletteProvider: [ 'type', PaletteProvider ] + }; + + var LOW_PRIORITY = 250; + + + function BpmnReplacePreview( + eventBus, elementRegistry, elementFactory, + canvas, previewSupport) { + + CommandInterceptor.call(this, eventBus); + + /** + * Replace the visuals of all elements in the context which can be replaced + * + * @param {Object} context + */ + function replaceVisual(context) { + + var replacements = context.canExecute.replacements; + + forEach$1(replacements, function(replacement) { + + var id = replacement.oldElementId; + + var newElement = { + type: replacement.newElementType + }; + + // if the visual of the element is already replaced + if (context.visualReplacements[id]) { + return; + } + + var element = elementRegistry.get(id); + + assign(newElement, { x: element.x, y: element.y }); + + // create a temporary shape + var tempShape = elementFactory.createShape(newElement); + + canvas.addShape(tempShape, element.parent); + + // select the original SVG element related to the element and hide it + var gfx = query('[data-element-id="' + cssEscape(element.id) + '"]', context.dragGroup); + + if (gfx) { + attr(gfx, { display: 'none' }); + } + + // clone the gfx of the temporary shape and add it to the drag group + var dragger = previewSupport.addDragger(tempShape, context.dragGroup); + + context.visualReplacements[id] = dragger; + + canvas.removeShape(tempShape); + }); + } + + /** + * Restore the original visuals of the previously replaced elements + * + * @param {Object} context + */ + function restoreVisual(context) { + + var visualReplacements = context.visualReplacements; + + forEach$1(visualReplacements, function(dragger, id) { + + var originalGfx = query('[data-element-id="' + cssEscape(id) + '"]', context.dragGroup); + + if (originalGfx) { + attr(originalGfx, { display: 'inline' }); + } + + dragger.remove(); + + if (visualReplacements[id]) { + delete visualReplacements[id]; + } + }); + } + + eventBus.on('shape.move.move', LOW_PRIORITY, function(event) { + + var context = event.context, + canExecute = context.canExecute; + + if (!context.visualReplacements) { + context.visualReplacements = {}; + } + + if (canExecute && canExecute.replacements) { + replaceVisual(context); + } else { + restoreVisual(context); + } + }); + } + + BpmnReplacePreview.$inject = [ + 'eventBus', + 'elementRegistry', + 'elementFactory', + 'canvas', + 'previewSupport' + ]; + + e(BpmnReplacePreview, CommandInterceptor); + + var ReplacePreviewModule = { + __depends__: [ + PreviewSupportModule + ], + __init__: [ 'bpmnReplacePreview' ], + bpmnReplacePreview: [ 'type', BpmnReplacePreview ] + }; + + var HIGHER_PRIORITY$2 = 1250; + + var BOUNDARY_TO_HOST_THRESHOLD = 40; + + var TARGET_BOUNDS_PADDING = 20, + TASK_BOUNDS_PADDING = 10; + + var TARGET_CENTER_PADDING = 20; + + var AXES = [ 'x', 'y' ]; + + var abs = Math.abs; + + /** + * Snap during connect. + * + * @param {EventBus} eventBus + */ + function BpmnConnectSnapping(eventBus) { + eventBus.on([ + 'connect.hover', + 'connect.move', + 'connect.end', + ], HIGHER_PRIORITY$2, function(event) { + var context = event.context, + canExecute = context.canExecute, + start = context.start, + hover = context.hover, + source = context.source, + target = context.target; + + // do NOT snap on CMD + if (event.originalEvent && isCmd(event.originalEvent)) { + return; + } + + if (!context.initialConnectionStart) { + context.initialConnectionStart = context.connectionStart; + } + + // snap hover + if (canExecute && hover) { + snapToShape(event, hover, getTargetBoundsPadding(hover)); + } + + if (hover && isAnyType(canExecute, [ + 'bpmn:Association', + 'bpmn:DataInputAssociation', + 'bpmn:DataOutputAssociation', + 'bpmn:SequenceFlow' + ])) { + context.connectionStart = mid$2(start); + + // snap hover + if (isAny(hover, [ 'bpmn:Event', 'bpmn:Gateway' ])) { + snapToPosition(event, mid$2(hover)); + } + + // snap hover + if (isAny(hover, [ 'bpmn:Task', 'bpmn:SubProcess' ])) { + snapToTargetMid(event, hover); + } + + // snap source and target + if (is$1(source, 'bpmn:BoundaryEvent') && target === source.host) { + snapBoundaryEventLoop(event); + } + + } else if (isType(canExecute, 'bpmn:MessageFlow')) { + + if (is$1(start, 'bpmn:Event')) { + + // snap start + context.connectionStart = mid$2(start); + } + + if (is$1(hover, 'bpmn:Event')) { + + // snap hover + snapToPosition(event, mid$2(hover)); + } + + } else { + + // un-snap source + context.connectionStart = context.initialConnectionStart; + } + }); + } + + BpmnConnectSnapping.$inject = [ 'eventBus' ]; + + + // helpers ////////// + + // snap to target if event in target + function snapToShape(event, target, padding) { + AXES.forEach(function(axis) { + var dimensionForAxis = getDimensionForAxis(axis, target); + + if (event[ axis ] < target[ axis ] + padding) { + setSnapped(event, axis, target[ axis ] + padding); + } else if (event[ axis ] > target[ axis ] + dimensionForAxis - padding) { + setSnapped(event, axis, target[ axis ] + dimensionForAxis - padding); + } + }); + } + + // snap to target mid if event in target mid + function snapToTargetMid(event, target) { + var targetMid = mid$2(target); + + AXES.forEach(function(axis) { + if (isMid(event, target, axis)) { + setSnapped(event, axis, targetMid[ axis ]); + } + }); + } + + // snap to prevent loop overlapping boundary event + function snapBoundaryEventLoop(event) { + var context = event.context, + source = context.source, + target = context.target; + + if (isReverse(context)) { + return; + } + + var sourceMid = mid$2(source), + orientation = getOrientation(sourceMid, target, -10), + axes = []; + + if (/top|bottom/.test(orientation)) { + axes.push('x'); + } + + if (/left|right/.test(orientation)) { + axes.push('y'); + } + + axes.forEach(function(axis) { + var coordinate = event[ axis ], newCoordinate; + + if (abs(coordinate - sourceMid[ axis ]) < BOUNDARY_TO_HOST_THRESHOLD) { + if (coordinate > sourceMid[ axis ]) { + newCoordinate = sourceMid[ axis ] + BOUNDARY_TO_HOST_THRESHOLD; + } + else { + newCoordinate = sourceMid[ axis ] - BOUNDARY_TO_HOST_THRESHOLD; + } + + setSnapped(event, axis, newCoordinate); + } + }); + } + + function snapToPosition(event, position) { + setSnapped(event, 'x', position.x); + setSnapped(event, 'y', position.y); + } + + function isType(attrs, type) { + return attrs && attrs.type === type; + } + + function isAnyType(attrs, types) { + return some(types, function(type) { + return isType(attrs, type); + }); + } + + function getDimensionForAxis(axis, element) { + return axis === 'x' ? element.width : element.height; + } + + function getTargetBoundsPadding(target) { + if (is$1(target, 'bpmn:Task')) { + return TASK_BOUNDS_PADDING; + } else { + return TARGET_BOUNDS_PADDING; + } + } + + function isMid(event, target, axis) { + return event[ axis ] > target[ axis ] + TARGET_CENTER_PADDING + && event[ axis ] < target[ axis ] + getDimensionForAxis(axis, target) - TARGET_CENTER_PADDING; + } + + function isReverse(context) { + var hover = context.hover, + source = context.source; + + return hover && source && hover === source; + } + + /** + * A snap context, containing the (possibly incomplete) + * mappings of drop targets (to identify the snapping) + * to computed snap points. + */ + function SnapContext() { + + /** + * Map mapping drop targets to + * a list of possible snappings. + * + * @type {Object} + */ + this._targets = {}; + + /** + * Map initial positioning of element + * regarding various snap directions. + * + * @type {Object} + */ + this._snapOrigins = {}; + + /** + * List of snap locations + * + * @type {Array} + */ + this._snapLocations = []; + + /** + * Map> of default snapping locations + * + * @type {Object} + */ + this._defaultSnaps = {}; + } + + + SnapContext.prototype.getSnapOrigin = function(snapLocation) { + return this._snapOrigins[snapLocation]; + }; + + + SnapContext.prototype.setSnapOrigin = function(snapLocation, initialValue) { + this._snapOrigins[snapLocation] = initialValue; + + if (this._snapLocations.indexOf(snapLocation) === -1) { + this._snapLocations.push(snapLocation); + } + }; + + + SnapContext.prototype.addDefaultSnap = function(type, point) { + + var snapValues = this._defaultSnaps[type]; + + if (!snapValues) { + snapValues = this._defaultSnaps[type] = []; + } + + snapValues.push(point); + }; + + /** + * Return a number of initialized snaps, i.e. snap locations such as + * top-left, mid, bottom-right and so forth. + * + * @return {Array} snapLocations + */ + SnapContext.prototype.getSnapLocations = function() { + return this._snapLocations; + }; + + /** + * Set the snap locations for this context. + * + * The order of locations determines precedence. + * + * @param {Array} snapLocations + */ + SnapContext.prototype.setSnapLocations = function(snapLocations) { + this._snapLocations = snapLocations; + }; + + /** + * Get snap points for a given target + * + * @param {Element|string} target + */ + SnapContext.prototype.pointsForTarget = function(target) { + + var targetId = target.id || target; + + var snapPoints = this._targets[targetId]; + + if (!snapPoints) { + snapPoints = this._targets[targetId] = new SnapPoints(); + snapPoints.initDefaults(this._defaultSnaps); + } + + return snapPoints; + }; + + + /** + * Creates the snap points and initializes them with the + * given default values. + * + * @param {Object>} [defaultPoints] + */ + function SnapPoints(defaultSnaps) { + + /** + * Map>> mapping snap locations, + * i.e. top-left, bottom-right, center to actual snap values. + * + * @type {Object} + */ + this._snapValues = {}; + } + + SnapPoints.prototype.add = function(snapLocation, point) { + + var snapValues = this._snapValues[snapLocation]; + + if (!snapValues) { + snapValues = this._snapValues[snapLocation] = { x: [], y: [] }; + } + + if (snapValues.x.indexOf(point.x) === -1) { + snapValues.x.push(point.x); + } + + if (snapValues.y.indexOf(point.y) === -1) { + snapValues.y.push(point.y); + } + }; + + + SnapPoints.prototype.snap = function(point, snapLocation, axis, tolerance) { + var snappingValues = this._snapValues[snapLocation]; + + return snappingValues && snapTo(point[axis], snappingValues[axis], tolerance); + }; + + /** + * Initialize a number of default snapping points. + * + * @param {Object} defaultSnaps + */ + SnapPoints.prototype.initDefaults = function(defaultSnaps) { + + var self = this; + + forEach$1(defaultSnaps || {}, function(snapPoints, snapLocation) { + forEach$1(snapPoints, function(point) { + self.add(snapLocation, point); + }); + }); + }; + + var HIGHER_PRIORITY$1 = 1250; + + + /** + * Snap during create and move. + * + * @param {EventBus} elementRegistry + * @param {EventBus} eventBus + * @param {Snapping} snapping + */ + function CreateMoveSnapping(elementRegistry, eventBus, snapping) { + var self = this; + + this._elementRegistry = elementRegistry; + + eventBus.on([ + 'create.start', + 'shape.move.start' + ], function(event) { + self.initSnap(event); + }); + + eventBus.on([ + 'create.move', + 'create.end', + 'shape.move.move', + 'shape.move.end' + ], HIGHER_PRIORITY$1, function(event) { + var context = event.context, + shape = context.shape, + snapContext = context.snapContext, + target = context.target; + + if (event.originalEvent && isCmd(event.originalEvent)) { + return; + } + + if (isSnapped(event) || !target) { + return; + } + + var snapPoints = snapContext.pointsForTarget(target); + + if (!snapPoints.initialized) { + snapPoints = self.addSnapTargetPoints(snapPoints, shape, target); + + snapPoints.initialized = true; + } + + snapping.snap(event, snapPoints); + }); + + eventBus.on([ + 'create.cleanup', + 'shape.move.cleanup' + ], function() { + snapping.hide(); + }); + } + + CreateMoveSnapping.$inject = [ + 'elementRegistry', + 'eventBus', + 'snapping' + ]; + + CreateMoveSnapping.prototype.initSnap = function(event) { + var elementRegistry = this._elementRegistry; + + var context = event.context, + shape = context.shape, + snapContext = context.snapContext; + + if (!snapContext) { + snapContext = context.snapContext = new SnapContext(); + } + + var shapeMid; + + if (elementRegistry.get(shape.id)) { + + // move + shapeMid = mid$2(shape, event); + } else { + + // create + shapeMid = { + x: event.x + mid$2(shape).x, + y: event.y + mid$2(shape).y + }; + } + + var shapeTopLeft = { + x: shapeMid.x - shape.width / 2, + y: shapeMid.y - shape.height / 2 + }, + shapeBottomRight = { + x: shapeMid.x + shape.width / 2, + y: shapeMid.y + shape.height / 2 + }; + + snapContext.setSnapOrigin('mid', { + x: shapeMid.x - event.x, + y: shapeMid.y - event.y + }); + + // snap labels to mid only + if (isLabel$1(shape)) { + return snapContext; + } + + snapContext.setSnapOrigin('top-left', { + x: shapeTopLeft.x - event.x, + y: shapeTopLeft.y - event.y + }); + + snapContext.setSnapOrigin('bottom-right', { + x: shapeBottomRight.x - event.x, + y: shapeBottomRight.y - event.y + }); + + return snapContext; + }; + + CreateMoveSnapping.prototype.addSnapTargetPoints = function(snapPoints, shape, target) { + var snapTargets = this.getSnapTargets(shape, target); + + forEach$1(snapTargets, function(snapTarget) { + + // handle labels + if (isLabel$1(snapTarget)) { + + if (isLabel$1(shape)) { + snapPoints.add('mid', mid$2(snapTarget)); + } + + return; + } + + // handle connections + if (isConnection$1(snapTarget)) { + + // ignore single segment connections + if (snapTarget.waypoints.length < 3) { + return; + } + + // ignore first and last waypoint + var waypoints = snapTarget.waypoints.slice(1, -1); + + forEach$1(waypoints, function(waypoint) { + snapPoints.add('mid', waypoint); + }); + + return; + } + + // handle shapes + snapPoints.add('mid', mid$2(snapTarget)); + }); + + if (!isNumber(shape.x) || !isNumber(shape.y)) { + return snapPoints; + } + + // snap to original position when moving + if (this._elementRegistry.get(shape.id)) { + snapPoints.add('mid', mid$2(shape)); + } + + return snapPoints; + }; + + CreateMoveSnapping.prototype.getSnapTargets = function(shape, target) { + return getChildren(target).filter(function(child) { + return !isHidden$1(child); + }); + }; + + // helpers ////////// + + function isConnection$1(element) { + return !!element.waypoints; + } + + function isHidden$1(element) { + return !!element.hidden; + } + + function isLabel$1(element) { + return !!element.labelTarget; + } + + var HIGH_PRIORITY = 1500; + + + /** + * Snap during create and move. + * + * @param {EventBus} eventBus + * @param {Injector} injector + */ + function BpmnCreateMoveSnapping(eventBus, injector) { + injector.invoke(CreateMoveSnapping, this); + + // creating first participant + eventBus.on([ 'create.move', 'create.end' ], HIGH_PRIORITY, setSnappedIfConstrained); + + // snap boundary events + eventBus.on([ + 'create.move', + 'create.end', + 'shape.move.move', + 'shape.move.end' + ], HIGH_PRIORITY, function(event) { + var context = event.context, + canExecute = context.canExecute, + target = context.target; + + var canAttach = canExecute && (canExecute === 'attach' || canExecute.attach); + + if (canAttach && !isSnapped(event)) { + snapBoundaryEvent(event, target); + } + }); + } + + e(BpmnCreateMoveSnapping, CreateMoveSnapping); + + BpmnCreateMoveSnapping.$inject = [ + 'eventBus', + 'injector' + ]; + + BpmnCreateMoveSnapping.prototype.initSnap = function(event) { + var snapContext = CreateMoveSnapping.prototype.initSnap.call(this, event); + + var shape = event.shape; + + var isMove = !!this._elementRegistry.get(shape.id); + + // snap to docking points + forEach$1(shape.outgoing, function(connection) { + var docking = connection.waypoints[0]; + + docking = docking.original || docking; + + snapContext.setSnapOrigin(connection.id + '-docking', getDockingSnapOrigin(docking, isMove, event)); + }); + + forEach$1(shape.incoming, function(connection) { + var docking = connection.waypoints[connection.waypoints.length - 1]; + + docking = docking.original || docking; + + snapContext.setSnapOrigin(connection.id + '-docking', getDockingSnapOrigin(docking, isMove, event)); + }); + + if (is$1(shape, 'bpmn:Participant')) { + + // snap to borders with higher priority + snapContext.setSnapLocations([ 'top-left', 'bottom-right', 'mid' ]); + } + + return snapContext; + }; + + BpmnCreateMoveSnapping.prototype.addSnapTargetPoints = function(snapPoints, shape, target) { + CreateMoveSnapping.prototype.addSnapTargetPoints.call(this, snapPoints, shape, target); + + var snapTargets = this.getSnapTargets(shape, target); + + forEach$1(snapTargets, function(snapTarget) { + + // handle TRBL alignment + // + // * with container elements + // * with text annotations + if (isContainer(snapTarget) || areAll([ shape, snapTarget ], 'bpmn:TextAnnotation')) { + snapPoints.add('top-left', topLeft(snapTarget)); + snapPoints.add('bottom-right', bottomRight(snapTarget)); + } + }); + + var elementRegistry = this._elementRegistry; + + // snap to docking points if not create mode + forEach$1(shape.incoming, function(connection) { + if (elementRegistry.get(shape.id)) { + + if (!includes(snapTargets, connection.source)) { + snapPoints.add('mid', getMid(connection.source)); + } + + var docking = connection.waypoints[0]; + snapPoints.add(connection.id + '-docking', docking.original || docking); + } + }); + + forEach$1(shape.outgoing, function(connection) { + if (elementRegistry.get(shape.id)) { + + if (!includes(snapTargets, connection.target)) { + snapPoints.add('mid', getMid(connection.target)); + } + + var docking = connection.waypoints[ connection.waypoints.length - 1 ]; + + snapPoints.add(connection.id + '-docking', docking.original || docking); + } + }); + + // add sequence flow parents as snap targets + if (is$1(target, 'bpmn:SequenceFlow')) { + snapPoints = this.addSnapTargetPoints(snapPoints, shape, target.parent); + } + + return snapPoints; + }; + + BpmnCreateMoveSnapping.prototype.getSnapTargets = function(shape, target) { + return CreateMoveSnapping.prototype.getSnapTargets.call(this, shape, target) + .filter(function(snapTarget) { + + // do not snap to lanes + return !is$1(snapTarget, 'bpmn:Lane'); + }); + }; + + // helpers ////////// + + function snapBoundaryEvent(event, target) { + var targetTRBL = asTRBL(target); + + var direction = getBoundaryAttachment(event, target); + + var context = event.context, + shape = context.shape; + + var offset; + + if (shape.parent) { + offset = { x: 0, y: 0 }; + } else { + offset = getMid(shape); + } + + if (/top/.test(direction)) { + setSnapped(event, 'y', targetTRBL.top - offset.y); + } else if (/bottom/.test(direction)) { + setSnapped(event, 'y', targetTRBL.bottom - offset.y); + } + + if (/left/.test(direction)) { + setSnapped(event, 'x', targetTRBL.left - offset.x); + } else if (/right/.test(direction)) { + setSnapped(event, 'x', targetTRBL.right - offset.x); + } + } + + function areAll(elements, type) { + return elements.every(function(el) { + return is$1(el, type); + }); + } + + function isContainer(element) { + if (is$1(element, 'bpmn:SubProcess') && isExpanded(element)) { + return true; + } + + return is$1(element, 'bpmn:Participant'); + } + + + function setSnappedIfConstrained(event) { + var context = event.context, + createConstraints = context.createConstraints; + + if (!createConstraints) { + return; + } + + var top = createConstraints.top, + right = createConstraints.right, + bottom = createConstraints.bottom, + left = createConstraints.left; + + if ((left && left >= event.x) || (right && right <= event.x)) { + setSnapped(event, 'x', event.x); + } + + if ((top && top >= event.y) || (bottom && bottom <= event.y)) { + setSnapped(event, 'y', event.y); + } + } + + function includes(array, value) { + return array.indexOf(value) !== -1; + } + + function getDockingSnapOrigin(docking, isMove, event) { + return isMove ? ( + { + x: docking.x - event.x, + y: docking.y - event.y + } + ) : { + x: docking.x, + y: docking.y + }; + } + + var HIGHER_PRIORITY = 1250; + + + /** + * Snap during resize. + * + * @param {EventBus} eventBus + * @param {Snapping} snapping + */ + function ResizeSnapping(eventBus, snapping) { + var self = this; + + eventBus.on([ 'resize.start' ], function(event) { + self.initSnap(event); + }); + + eventBus.on([ + 'resize.move', + 'resize.end', + ], HIGHER_PRIORITY, function(event) { + var context = event.context, + shape = context.shape, + parent = shape.parent, + direction = context.direction, + snapContext = context.snapContext; + + if (event.originalEvent && isCmd(event.originalEvent)) { + return; + } + + if (isSnapped(event)) { + return; + } + + var snapPoints = snapContext.pointsForTarget(parent); + + if (!snapPoints.initialized) { + snapPoints = self.addSnapTargetPoints(snapPoints, shape, parent, direction); + + snapPoints.initialized = true; + } + + if (isHorizontal(direction)) { + setSnapped(event, 'x', event.x); + } + + if (isVertical(direction)) { + setSnapped(event, 'y', event.y); + } + + snapping.snap(event, snapPoints); + }); + + eventBus.on([ 'resize.cleanup' ], function() { + snapping.hide(); + }); + } + + ResizeSnapping.prototype.initSnap = function(event) { + var context = event.context, + shape = context.shape, + direction = context.direction, + snapContext = context.snapContext; + + if (!snapContext) { + snapContext = context.snapContext = new SnapContext(); + } + + var snapOrigin = getSnapOrigin(shape, direction); + + snapContext.setSnapOrigin('corner', { + x: snapOrigin.x - event.x, + y: snapOrigin.y - event.y + }); + + return snapContext; + }; + + ResizeSnapping.prototype.addSnapTargetPoints = function(snapPoints, shape, target, direction) { + var snapTargets = this.getSnapTargets(shape, target); + + forEach$1(snapTargets, function(snapTarget) { + snapPoints.add('corner', bottomRight(snapTarget)); + snapPoints.add('corner', topLeft(snapTarget)); + }); + + snapPoints.add('corner', getSnapOrigin(shape, direction)); + + return snapPoints; + }; + + ResizeSnapping.$inject = [ + 'eventBus', + 'snapping' + ]; + + ResizeSnapping.prototype.getSnapTargets = function(shape, target) { + return getChildren(target).filter(function(child) { + return !isAttached(child, shape) + && !isConnection(child) + && !isHidden(child) + && !isLabel(child); + }); + }; + + // helpers ////////// + + function getSnapOrigin(shape, direction) { + var mid = getMid(shape), + trbl = asTRBL(shape); + + var snapOrigin = { + x: mid.x, + y: mid.y + }; + + if (direction.indexOf('n') !== -1) { + snapOrigin.y = trbl.top; + } else if (direction.indexOf('s') !== -1) { + snapOrigin.y = trbl.bottom; + } + + if (direction.indexOf('e') !== -1) { + snapOrigin.x = trbl.right; + } else if (direction.indexOf('w') !== -1) { + snapOrigin.x = trbl.left; + } + + return snapOrigin; + } + + function isAttached(element, host) { + return element.host === host; + } + + function isConnection(element) { + return !!element.waypoints; + } + + function isHidden(element) { + return !!element.hidden; + } + + function isLabel(element) { + return !!element.labelTarget; + } + + function isHorizontal(direction) { + return direction === 'n' || direction === 's'; + } + + function isVertical(direction) { + return direction === 'e' || direction === 'w'; + } + + var SNAP_TOLERANCE = 7; + + var SNAP_LINE_HIDE_DELAY = 1000; + + + /** + * Generic snapping feature. + * + * @param {EventBus} eventBus + * @param {Canvas} canvas + */ + function Snapping(canvas) { + this._canvas = canvas; + + // delay hide by 1000 seconds since last snap + this._asyncHide = debounce(bind(this.hide, this), SNAP_LINE_HIDE_DELAY); + } + + Snapping.$inject = [ 'canvas' ]; + + /** + * Snap an event to given snap points. + * + * @param {Event} event + * @param {SnapPoints} snapPoints + */ + Snapping.prototype.snap = function(event, snapPoints) { + var context = event.context, + snapContext = context.snapContext, + snapLocations = snapContext.getSnapLocations(); + + var snapping = { + x: isSnapped(event, 'x'), + y: isSnapped(event, 'y') + }; + + forEach$1(snapLocations, function(location) { + var snapOrigin = snapContext.getSnapOrigin(location); + + var snapCurrent = { + x: event.x + snapOrigin.x, + y: event.y + snapOrigin.y + }; + + // snap both axis if not snapped already + forEach$1([ 'x', 'y' ], function(axis) { + var locationSnapping; + + if (!snapping[axis]) { + locationSnapping = snapPoints.snap(snapCurrent, location, axis, SNAP_TOLERANCE); + + if (locationSnapping !== undefined) { + snapping[axis] = { + value: locationSnapping, + originValue: locationSnapping - snapOrigin[axis] + }; + } + } + }); + + // no need to continue snapping + if (snapping.x && snapping.y) { + return false; + } + }); + + // show snap lines + this.showSnapLine('vertical', snapping.x && snapping.x.value); + this.showSnapLine('horizontal', snapping.y && snapping.y.value); + + // snap event + forEach$1([ 'x', 'y' ], function(axis) { + var axisSnapping = snapping[axis]; + + if (isObject(axisSnapping)) { + setSnapped(event, axis, axisSnapping.originValue); + } + }); + }; + + Snapping.prototype._createLine = function(orientation) { + var root = this._canvas.getLayer('snap'); + + var line = create$1('path'); + + attr(line, { d: 'M0,0 L0,0' }); + + classes(line).add('djs-snap-line'); + + append(root, line); + + return { + update: function(position) { + + if (!isNumber(position)) { + attr(line, { display: 'none' }); + } else { + if (orientation === 'horizontal') { + attr(line, { + d: 'M-100000,' + position + ' L+100000,' + position, + display: '' + }); + } else { + attr(line, { + d: 'M ' + position + ',-100000 L ' + position + ', +100000', + display: '' + }); + } + } + } + }; + }; + + Snapping.prototype._createSnapLines = function() { + this._snapLines = { + horizontal: this._createLine('horizontal'), + vertical: this._createLine('vertical') + }; + }; + + Snapping.prototype.showSnapLine = function(orientation, position) { + + var line = this.getSnapLine(orientation); + + if (line) { + line.update(position); + } + + this._asyncHide(); + }; + + Snapping.prototype.getSnapLine = function(orientation) { + if (!this._snapLines) { + this._createSnapLines(); + } + + return this._snapLines[orientation]; + }; + + Snapping.prototype.hide = function() { + forEach$1(this._snapLines, function(snapLine) { + snapLine.update(); + }); + }; + + var SnappingModule$1 = { + __init__: [ + 'createMoveSnapping', + 'resizeSnapping', + 'snapping' + ], + createMoveSnapping: [ 'type', CreateMoveSnapping ], + resizeSnapping: [ 'type', ResizeSnapping ], + snapping: [ 'type', Snapping ] + }; + + var SnappingModule = { + __depends__: [ SnappingModule$1 ], + __init__: [ + 'connectSnapping', + 'createMoveSnapping' + ], + connectSnapping: [ 'type', BpmnConnectSnapping ], + createMoveSnapping: [ 'type', BpmnCreateMoveSnapping ] + }; + + /** + * Provides searching infrastructure + */ + function SearchPad(canvas, eventBus, overlays, selection) { + this._open = false; + this._results = []; + this._eventMaps = []; + + this._canvas = canvas; + this._eventBus = eventBus; + this._overlays = overlays; + this._selection = selection; + + // setup elements + this._container = domify(SearchPad.BOX_HTML); + this._searchInput = query(SearchPad.INPUT_SELECTOR, this._container); + this._resultsContainer = query(SearchPad.RESULTS_CONTAINER_SELECTOR, this._container); + + // attach search pad + this._canvas.getContainer().appendChild(this._container); + + // cleanup on destroy + eventBus.on([ 'canvas.destroy', 'diagram.destroy' ], this.close, this); + } + + + SearchPad.$inject = [ + 'canvas', + 'eventBus', + 'overlays', + 'selection' + ]; + + + /** + * Binds and keeps track of all event listereners + */ + SearchPad.prototype._bindEvents = function() { + var self = this; + + function listen(el, selector, type, fn) { + self._eventMaps.push({ + el: el, + type: type, + listener: delegate.bind(el, selector, type, fn) + }); + } + + // close search on clicking anywhere outside + listen(document, 'html', 'click', function(e) { + self.close(); + }); + + // stop event from propagating and closing search + // focus on input + listen(this._container, SearchPad.INPUT_SELECTOR, 'click', function(e) { + e.stopPropagation(); + e.delegateTarget.focus(); + }); + + // preselect result on hover + listen(this._container, SearchPad.RESULT_SELECTOR, 'mouseover', function(e) { + e.stopPropagation(); + self._scrollToNode(e.delegateTarget); + self._preselect(e.delegateTarget); + }); + + // selects desired result on mouse click + listen(this._container, SearchPad.RESULT_SELECTOR, 'click', function(e) { + e.stopPropagation(); + self._select(e.delegateTarget); + }); + + // prevent cursor in input from going left and right when using up/down to + // navigate results + listen(this._container, SearchPad.INPUT_SELECTOR, 'keydown', function(e) { + + // up + if (e.keyCode === 38) { + e.preventDefault(); + } + + // down + if (e.keyCode === 40) { + e.preventDefault(); + } + }); + + // handle keyboard input + listen(this._container, SearchPad.INPUT_SELECTOR, 'keyup', function(e) { + + // escape + if (e.keyCode === 27) { + return self.close(); + } + + // enter + if (e.keyCode === 13) { + var selected = self._getCurrentResult(); + + return selected ? self._select(selected) : self.close(); + } + + // up + if (e.keyCode === 38) { + return self._scrollToDirection(true); + } + + // down + if (e.keyCode === 40) { + return self._scrollToDirection(); + } + + // left && right + // do not search while navigating text input + if (e.keyCode === 37 || e.keyCode === 39) { + return; + } + + // anything else + self._search(e.delegateTarget.value); + }); + }; + + + /** + * Unbinds all previously established listeners + */ + SearchPad.prototype._unbindEvents = function() { + this._eventMaps.forEach(function(m) { + delegate.unbind(m.el, m.type, m.listener); + }); + }; + + + /** + * Performs a search for the given pattern. + * + * @param {string} pattern + */ + SearchPad.prototype._search = function(pattern) { + var self = this; + + this._clearResults(); + + // do not search on empty query + if (!pattern || pattern === '') { + return; + } + + var searchResults = this._searchProvider.find(pattern); + + if (!searchResults.length) { + return; + } + + // append new results + searchResults.forEach(function(result) { + var id = result.element.id; + var node = self._createResultNode(result, id); + self._results[id] = { + element: result.element, + node: node + }; + }); + + // preselect first result + var node = query(SearchPad.RESULT_SELECTOR, this._resultsContainer); + this._scrollToNode(node); + this._preselect(node); + }; + + + /** + * Navigate to the previous/next result. Defaults to next result. + * @param {boolean} previous + */ + SearchPad.prototype._scrollToDirection = function(previous) { + var selected = this._getCurrentResult(); + if (!selected) { + return; + } + + var node = previous ? selected.previousElementSibling : selected.nextElementSibling; + if (node) { + this._scrollToNode(node); + this._preselect(node); + } + }; + + + /** + * Scroll to the node if it is not visible. + * + * @param {Element} node + */ + SearchPad.prototype._scrollToNode = function(node) { + if (!node || node === this._getCurrentResult()) { + return; + } + + var nodeOffset = node.offsetTop; + var containerScroll = this._resultsContainer.scrollTop; + + var bottomScroll = nodeOffset - this._resultsContainer.clientHeight + node.clientHeight; + + if (nodeOffset < containerScroll) { + this._resultsContainer.scrollTop = nodeOffset; + } else if (containerScroll < bottomScroll) { + this._resultsContainer.scrollTop = bottomScroll; + } + }; + + + /** + * Clears all results data. + */ + SearchPad.prototype._clearResults = function() { + clear$1(this._resultsContainer); + + this._results = []; + + this._resetOverlay(); + + this._eventBus.fire('searchPad.cleared'); + }; + + + /** + * Get currently selected result. + * + * @return {Element} + */ + SearchPad.prototype._getCurrentResult = function() { + return query(SearchPad.RESULT_SELECTED_SELECTOR, this._resultsContainer); + }; + + + /** + * Create result DOM element within results container + * that corresponds to a search result. + * + * 'result' : one of the elements returned by SearchProvider + * 'id' : id attribute value to assign to the new DOM node + * return : created DOM element + * + * @param {SearchResult} result + * @param {string} id + * @return {Element} + */ + SearchPad.prototype._createResultNode = function(result, id) { + var node = domify(SearchPad.RESULT_HTML); + + // create only if available + if (result.primaryTokens.length > 0) { + createInnerTextNode(node, result.primaryTokens, SearchPad.RESULT_PRIMARY_HTML); + } + + // secondary tokens (represent element ID) are allways available + createInnerTextNode(node, result.secondaryTokens, SearchPad.RESULT_SECONDARY_HTML); + + attr$1(node, SearchPad.RESULT_ID_ATTRIBUTE, id); + + this._resultsContainer.appendChild(node); + + return node; + }; + + + /** + * Register search element provider. + * + * SearchProvider.find - provides search function over own elements + * (pattern) => [{ text: , element: }, ...] + * + * @param {SearchProvider} provider + */ + SearchPad.prototype.registerProvider = function(provider) { + this._searchProvider = provider; + }; + + + /** + * Open search pad. + */ + SearchPad.prototype.open = function() { + if (!this._searchProvider) { + throw new Error('no search provider registered'); + } + + if (this.isOpen()) { + return; + } + + this._bindEvents(); + + this._open = true; + + classes$1(this._container).add('open'); + + this._searchInput.focus(); + + this._eventBus.fire('searchPad.opened'); + }; + + + /** + * Close search pad. + */ + SearchPad.prototype.close = function() { + if (!this.isOpen()) { + return; + } + + this._unbindEvents(); + + this._open = false; + + classes$1(this._container).remove('open'); + + this._clearResults(); + + this._searchInput.value = ''; + this._searchInput.blur(); + + this._resetOverlay(); + + this._eventBus.fire('searchPad.closed'); + }; + + + /** + * Toggles search pad on/off. + */ + SearchPad.prototype.toggle = function() { + this.isOpen() ? this.close() : this.open(); + }; + + + /** + * Report state of search pad. + */ + SearchPad.prototype.isOpen = function() { + return this._open; + }; + + + /** + * Preselect result entry. + * + * @param {Element} element + */ + SearchPad.prototype._preselect = function(node) { + var selectedNode = this._getCurrentResult(); + + // already selected + if (node === selectedNode) { + return; + } + + // removing preselection from current node + if (selectedNode) { + classes$1(selectedNode).remove(SearchPad.RESULT_SELECTED_CLASS); + } + + var id = attr$1(node, SearchPad.RESULT_ID_ATTRIBUTE); + var element = this._results[id].element; + + classes$1(node).add(SearchPad.RESULT_SELECTED_CLASS); + + this._resetOverlay(element); + + this._canvas.scrollToElement(element, { top: 400 }); + + this._selection.select(element); + + this._eventBus.fire('searchPad.preselected', element); + }; + + + /** + * Select result node. + * + * @param {Element} element + */ + SearchPad.prototype._select = function(node) { + var id = attr$1(node, SearchPad.RESULT_ID_ATTRIBUTE); + var element = this._results[id].element; + + this.close(); + + this._resetOverlay(); + + this._canvas.scrollToElement(element, { top: 400 }); + + this._selection.select(element); + + this._eventBus.fire('searchPad.selected', element); + }; + + + /** + * Reset overlay removes and, optionally, set + * overlay to a new element. + * + * @param {Element} element + */ + SearchPad.prototype._resetOverlay = function(element) { + if (this._overlayId) { + this._overlays.remove(this._overlayId); + } + + if (element) { + var box = getBBox(element); + var overlay = constructOverlay(box); + this._overlayId = this._overlays.add(element, overlay); + } + }; + + + /** + * Construct overlay object for the given bounding box. + * + * @param {BoundingBox} box + * @return {Object} + */ + function constructOverlay(box) { + + var offset = 6; + var w = box.width + offset * 2; + var h = box.height + offset * 2; + + var styles = { + width: w + 'px', + height: h + 'px' + }; + + var html = domify('
    '); + + assign$1(html, styles); + + return { + position: { + bottom: h - offset, + right: w - offset + }, + show: true, + html: html + }; + } + + + /** + * Creates and appends child node from result tokens and HTML template. + * + * @param {Element} node + * @param {Array} tokens + * @param {string} template + */ + function createInnerTextNode(parentNode, tokens, template) { + var text = createHtmlText(tokens); + var childNode = domify(template); + childNode.innerHTML = text; + parentNode.appendChild(childNode); + } + + /** + * Create internal HTML markup from result tokens. + * Caters for highlighting pattern matched tokens. + * + * @param {Array} tokens + * @return {string} + */ + function createHtmlText(tokens) { + var htmlText = ''; + + tokens.forEach(function(t) { + if (t.matched) { + htmlText += '' + escapeHTML(t.matched) + ''; + } else { + htmlText += escapeHTML(t.normal); + } + }); + + return htmlText !== '' ? htmlText : null; + } + + + /** + * CONSTANTS + */ + SearchPad.CONTAINER_SELECTOR = '.djs-search-container'; + SearchPad.INPUT_SELECTOR = '.djs-search-input input'; + SearchPad.RESULTS_CONTAINER_SELECTOR = '.djs-search-results'; + SearchPad.RESULT_SELECTOR = '.djs-search-result'; + SearchPad.RESULT_SELECTED_CLASS = 'djs-search-result-selected'; + SearchPad.RESULT_SELECTED_SELECTOR = '.' + SearchPad.RESULT_SELECTED_CLASS; + SearchPad.RESULT_ID_ATTRIBUTE = 'data-result-id'; + SearchPad.RESULT_HIGHLIGHT_CLASS = 'djs-search-highlight'; + SearchPad.OVERLAY_CLASS = 'djs-search-overlay'; + + SearchPad.BOX_HTML = + '
    ' + + '
    ' + + '' + + '
    ' + + '
    ' + + '
    '; + + SearchPad.RESULT_HTML = + '
    '; + + SearchPad.RESULT_PRIMARY_HTML = + '
    '; + + SearchPad.RESULT_SECONDARY_HTML = + '

    '; + + var SearchPadModule = { + __depends__: [ + OverlaysModule, + SelectionModule + ], + searchPad: [ 'type', SearchPad ] + }; + + /** + * Provides ability to search through BPMN elements + */ + function BpmnSearchProvider(elementRegistry, searchPad, canvas) { + + this._elementRegistry = elementRegistry; + this._canvas = canvas; + + searchPad.registerProvider(this); + } + + BpmnSearchProvider.$inject = [ + 'elementRegistry', + 'searchPad', + 'canvas' + ]; + + + /** + * Finds all elements that match given pattern + * + * : + * { + * primaryTokens: >, + * secondaryTokens: >, + * element: + * } + * + * : + * { + * normal|matched: + * } + * + * @param {string} pattern + * @return {Array} + */ + BpmnSearchProvider.prototype.find = function(pattern) { + var rootElement = this._canvas.getRootElement(); + + var elements = this._elementRegistry.filter(function(element) { + if (element.labelTarget) { + return false; + } + return true; + }); + + // do not include root element + elements = filter(elements, function(element) { + return element !== rootElement; + }); + + elements = map(elements, function(element) { + return { + primaryTokens: matchAndSplit(getLabel(element), pattern), + secondaryTokens: matchAndSplit(element.id, pattern), + element: element + }; + }); + + // exclude non-matched elements + elements = filter(elements, function(element) { + return hasMatched(element.primaryTokens) || hasMatched(element.secondaryTokens); + }); + + elements = sortBy(elements, function(element) { + return getLabel(element.element) + element.element.id; + }); + + return elements; + }; + + + function hasMatched(tokens) { + var matched = filter(tokens, function(t) { + return !!t.matched; + }); + + return matched.length > 0; + } + + + function matchAndSplit(text, pattern) { + var tokens = [], + originalText = text; + + if (!text) { + return tokens; + } + + text = text.toLowerCase(); + pattern = pattern.toLowerCase(); + + var i = text.indexOf(pattern); + + if (i > -1) { + if (i !== 0) { + tokens.push({ + normal: originalText.substr(0, i) + }); + } + + tokens.push({ + matched: originalText.substr(i, pattern.length) + }); + + if (pattern.length + i < text.length) { + tokens.push({ + normal: originalText.substr(pattern.length + i, text.length) + }); + } + } else { + tokens.push({ + normal: originalText + }); + } + + return tokens; + } + + var SearchModule = { + __depends__: [ + SearchPadModule + ], + __init__: [ 'bpmnSearch' ], + bpmnSearch: [ 'type', BpmnSearchProvider ] + }; + + var initialDiagram = + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + ''; + + + /** + * A modeler for BPMN 2.0 diagrams. + * + * + * ## Extending the Modeler + * + * In order to extend the viewer pass extension modules to bootstrap via the + * `additionalModules` option. An extension module is an object that exposes + * named services. + * + * The following example depicts the integration of a simple + * logging component that integrates with interaction events: + * + * + * ```javascript + * + * // logging component + * function InteractionLogger(eventBus) { + * eventBus.on('element.hover', function(event) { + * console.log() + * }) + * } + * + * InteractionLogger.$inject = [ 'eventBus' ]; // minification save + * + * // extension module + * var extensionModule = { + * __init__: [ 'interactionLogger' ], + * interactionLogger: [ 'type', InteractionLogger ] + * }; + * + * // extend the viewer + * var bpmnModeler = new Modeler({ additionalModules: [ extensionModule ] }); + * bpmnModeler.importXML(...); + * ``` + * + * + * ## Customizing / Replacing Components + * + * You can replace individual diagram components by redefining them in override modules. + * This works for all components, including those defined in the core. + * + * Pass in override modules via the `options.additionalModules` flag like this: + * + * ```javascript + * function CustomContextPadProvider(contextPad) { + * + * contextPad.registerProvider(this); + * + * this.getContextPadEntries = function(element) { + * // no entries, effectively disable the context pad + * return {}; + * }; + * } + * + * CustomContextPadProvider.$inject = [ 'contextPad' ]; + * + * var overrideModule = { + * contextPadProvider: [ 'type', CustomContextPadProvider ] + * }; + * + * var bpmnModeler = new Modeler({ additionalModules: [ overrideModule ]}); + * ``` + * + * @param {Object} [options] configuration options to pass to the viewer + * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body. + * @param {string|number} [options.width] the width of the viewer + * @param {string|number} [options.height] the height of the viewer + * @param {Object} [options.moddleExtensions] extension packages to provide + * @param {Array} [options.modules] a list of modules to override the default modules + * @param {Array} [options.additionalModules] a list of modules to use with the default modules + */ + function Modeler(options) { + BaseModeler.call(this, options); + } + + e(Modeler, BaseModeler); + + + Modeler.Viewer = Viewer; + Modeler.NavigatedViewer = NavigatedViewer; + + /** + * The createDiagram result. + * + * @typedef {Object} CreateDiagramResult + * + * @property {Array} warnings + */ + + /** + * The createDiagram error. + * + * @typedef {Error} CreateDiagramError + * + * @property {Array} warnings + */ + + /** + * Create a new diagram to start modeling. + * + * Returns {Promise} + */ + Modeler.prototype.createDiagram = wrapForCompatibility(function createDiagram() { + return this.importXML(initialDiagram); + }); + + + Modeler.prototype._interactionModules = [ + + // non-modeling components + KeyboardMoveModule, + MoveCanvasModule, + TouchModule, + ZoomScrollModule + ]; + + Modeler.prototype._modelingModules = [ + + // modeling components + AlignElementsModule, + AutoPlaceModule, + AutoScrollModule, + AutoResizeModule, + BendpointsModule, + ConnectModule, + ConnectionPreviewModule, + ContextPadModule, + CopyPasteModule, + CreateModule, + DistributeElementsModule, + EditorActionsModule, + GridSnappingModule, + InteractionEventsModule, + KeyboardModule, + KeyboardMoveSelectionModule, + LabelEditingModule, + ModelingModule, + MoveModule, + PaletteModule, + ReplacePreviewModule, + ResizeModule, + SnappingModule, + SearchModule + ]; + + + // modules the modeler is composed of + // + // - viewer modules + // - interaction modules + // - modeling modules + + Modeler.prototype._modules = [].concat( + Viewer.prototype._modules, + Modeler.prototype._interactionModules, + Modeler.prototype._modelingModules + ); + + return Modeler; + +})); diff --git a/dist/bpmn-modeler.production.min.js b/dist/bpmn-modeler.production.min.js new file mode 100644 index 0000000..ec32eef --- /dev/null +++ b/dist/bpmn-modeler.production.min.js @@ -0,0 +1,34 @@ +/*! bpmn-js - bpmn-modeler v9.4.0 | Copyright (c) 2014-present, camunda Services GmbH | bpmn.io/license */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).BpmnJS=t()}(this,(function(){"use strict";function e(e,t){t&&(e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}))}var t=function(e,t){return e(t={exports:{}},t.exports),t.exports}((function(e){var t=e.exports=function(e,n){if(n||(n=16),void 0===e&&(e=128),e<=0)return"0";for(var i=Math.log(Math.pow(2,e))/Math.log(n),r=2;i===1/0;r*=2)i=Math.log(Math.pow(2,e/r))/Math.log(n)*r;var o=i-Math.floor(i),a="";for(r=0;r=Math.pow(2,e)?t(e,n):a};t.rack=function(e,n,i){var r=function(r){var a=0;do{if(a++>10){if(!i)throw new Error("too many ID collisions, use more bits");e+=i}var s=t(e,n)}while(Object.hasOwnProperty.call(o,s));return o[s]=r,s},o=r.hats={};return r.get=function(e){return r.hats[e]},r.set=function(e,t){return r.hats[e]=t,r},r.bits=e||128,r.base=n||16,r}}));function n(e){if(!(this instanceof n))return new n(e);e=e||[128,36,1],this._seed=e.length?t.rack(e[0],e[1],e[2]):e}function i(e){return Array.prototype.concat.apply([],e)}n.prototype.next=function(e){return this._seed(e||!0)},n.prototype.nextPrefixed=function(e,t){var n;do{n=e+this.next(!0)}while(this.assigned(n));return this.claim(n,t),n},n.prototype.claim=function(e,t){this._seed.set(e,t||!0)},n.prototype.assigned=function(e){return this._seed.get(e)||!1},n.prototype.unclaim=function(e){delete this._seed.hats[e]},n.prototype.clear=function(){var e,t=this._seed.hats;for(e in t)this.unclaim(e)};var r=Object.prototype.toString,o=Object.prototype.hasOwnProperty;function a(e){return void 0===e}function s(e){return void 0!==e}function c(e){return null==e}function p(e){return"[object Array]"===r.call(e)}function l(e){return"[object Object]"===r.call(e)}function u(e){return"[object Number]"===r.call(e)}function h(e){var t=r.call(e);return"[object Function]"===t||"[object AsyncFunction]"===t||"[object GeneratorFunction]"===t||"[object AsyncGeneratorFunction]"===t||"[object Proxy]"===t}function d(e){return"[object String]"===r.call(e)}function f(e,t){return o.call(e,t)}function m(e,t){var n;return t=B(t),g(e,(function(e,i){if(t(e,i))return n=e,!1})),n}function v(e,t){t=B(t);var n=p(e)?-1:void 0;return g(e,(function(e,i){if(t(e,i))return n=i,!1})),n}function y(e,t){var n=[];return g(e,(function(e,i){t(e,i)&&n.push(e)})),n}function g(e,t){var n;if(!a(e)){var i=p(e)?O:N;for(var r in e)if(f(e,r)&&!1===t(n=e[r],i(r)))return n}}function b(e,t){return a(e)?[]:(function(e){if(!p(e))throw new Error("must supply array")}(e),t=B(t),e.filter((function(e,n){return!t(e,n)})))}function x(e,t,n){return g(e,(function(e,i){n=t(n,e,i)})),n}function _(e,t){return!!x(e,(function(e,n,i){return e&&t(n,i)}),!0)}function E(e,t){return!!m(e,t)}function w(e,t){var n=[];return g(e,(function(e,i){n.push(t(e,i))})),n}function S(e){return e&&Object.keys(e)||[]}function C(e){return S(e).length}function A(e){return w(e,(function(e){return e}))}function R(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return t=M(t),g(e,(function(e){var i=t(e)||"_",r=n[i];r||(r=n[i]=[]),r.push(e)})),n}function T(e){e=M(e);for(var t={},n=arguments.length,i=new Array(n>1?n-1:0),r=1;r0)return s(p);e.apply(r,i),c()}function s(e){n=setTimeout(a,e)}function c(){n&&clearTimeout(n),n=o=i=r=void 0}function p(){o=Date.now();for(var e=arguments.length,a=new Array(e),c=0;c1?t-1:0),i=1;i1?n-1:0),r=1;r
    a',me=!he.getElementsByTagName("link").length,he=void 0);var ve={legend:[1,"
    ","
    "],tr:[2,"","
    "],col:[2,"","
    "],_default:me?[1,"X
    ","
    "]:[0,"",""]};function ye(e,t){return(t=t||document).querySelector(e)}function ge(e,t){return(t=t||document).querySelectorAll(e)}function be(e){e.parentNode&&e.parentNode.removeChild(e)}function xe(e,t){return t.appendChild(function(e,t){if(e.ownerDocument!==t.ownerDocument)try{return t.ownerDocument.importNode(e,!0)}catch(e){}return e}(e,t))}function _e(e,t){return xe(t,e),e}ve.td=ve.th=[3,"","
    "],ve.option=ve.optgroup=[1,'"],ve.thead=ve.tbody=ve.colgroup=ve.caption=ve.tfoot=[1,"","
    "],ve.polyline=ve.ellipse=ve.polygon=ve.circle=ve.text=ve.line=ve.path=ve.rect=ve.g=[1,'',""];var Ee={"alignment-baseline":1,"baseline-shift":1,clip:1,"clip-path":1,"clip-rule":1,color:1,"color-interpolation":1,"color-interpolation-filters":1,"color-profile":1,"color-rendering":1,cursor:1,direction:1,display:1,"dominant-baseline":1,"enable-background":1,fill:1,"fill-opacity":1,"fill-rule":1,filter:1,"flood-color":1,"flood-opacity":1,font:1,"font-family":1,"font-size":2,"font-size-adjust":1,"font-stretch":1,"font-style":1,"font-variant":1,"font-weight":1,"glyph-orientation-horizontal":1,"glyph-orientation-vertical":1,"image-rendering":1,kerning:1,"letter-spacing":1,"lighting-color":1,marker:1,"marker-end":1,"marker-mid":1,"marker-start":1,mask:1,opacity:1,overflow:1,"pointer-events":1,"shape-rendering":1,"stop-color":1,"stop-opacity":1,stroke:1,"stroke-dasharray":1,"stroke-dashoffset":1,"stroke-linecap":1,"stroke-linejoin":1,"stroke-miterlimit":1,"stroke-opacity":1,"stroke-width":2,"text-anchor":1,"text-decoration":1,"text-rendering":1,"unicode-bidi":1,visibility:1,"word-spacing":1,"writing-mode":1};function we(e,t,n){var i=t.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase(),r=Ee[i];r?(2===r&&"number"==typeof n&&(n=String(n)+"px"),e.style[i]=n):e.setAttributeNS(null,t,n)}function Se(e,t,n){if("string"==typeof t){if(void 0===n)return function(e,t){return Ee[t]?e.style[t]:e.getAttributeNS(null,t)}(e,t);we(e,t,n)}else!function(e,t){var n,i,r=Object.keys(t);for(n=0;i=r[n];n++)we(e,i,t[i])}(e,t);return e}function Ce(e,t){if(e.indexOf)return e.indexOf(t);for(var n=0;n"+e+"",t=!0);var n=function(e){var t;return(t=new DOMParser).async=!1,t.parseFromString(e,"text/xml")}(e);if(!t)return n;for(var i=document.createDocumentFragment(),r=n.firstChild;r.firstChild;)i.appendChild(r.firstChild);return i}function Le(e,t){var n;return"<"===e.charAt(0)?(n=je(e).firstChild,n=document.importNode(n,!0)):n=document.createElementNS(Ne,e),t&&Se(n,t),n}var Ie=null;function Fe(){return null===Ie&&(Ie=Le("svg")),Ie}function ze(e,t){var n,i,r=Object.keys(t);for(n=0;i=r[n];n++)e[i]=t[i];return e}function $e(e){return e?Fe().createSVGTransformFromMatrix(e):Fe().createSVGTransform()}var He=/([&<>]{1})/g,Ge=/([\n\r"]{1})/g,Ve={"&":"&","<":"<",">":">",'"':"'"};function We(e,t){return e.replace(t,(function(e,t){return Ve[t]||t}))}function Ue(e,t){var n,i,r,o,a;switch(e.nodeType){case 3:t.push(We(e.textContent,He));break;case 1:if(t.push("<",e.tagName),e.hasAttributes())for(n=0,i=(r=e.attributes).length;n"),n=0,i=(a=e.childNodes).length;n")}else t.push("/>");break;case 8:t.push("\x3c!--",We(e.nodeValue,He),"--\x3e");break;case 4:t.push("");break;default:throw new Error("unable to handle node "+e.nodeType)}return t}function qe(e,t){var n=je(t);if(Me(e),t){(function(e){return"#document-fragment"===e.nodeName})(n)||(n=n.documentElement);for(var i,r=(i=n.childNodes,Array.prototype.slice.call(i)),o=0;o ");return n.length=0,new Error(t?e+" (Resolving: "+t+")":e)};function s(e,o){if(!i[e]&&-1!==e.indexOf(".")){for(var c=e.split("."),p=s(c.shift());c.length;)p=p[c.shift()];return p}if(Je(r,e))return r[e];if(Je(i,e)){if(-1!==n.indexOf(e))throw n.push(e),a("Cannot resolve circular dependency!");return n.push(e),r[e]=i[e][0](i[e][1]),n.pop(),r[e]}return t.get(e,o)}function c(e,t){if(void 0===t&&(t={}),"function"!=typeof e){if(!Qe(e))throw new Error('Cannot invoke "'+e+'". Expected a function!');e=et(e.slice())}return{fn:e,dependencies:(e.$inject||rt(e)).map((function(e){return Je(t,e)?t[e]:s(e)}))}}function p(e){var t=c(e),n=t.fn,i=t.dependencies;return new(Function.prototype.bind.apply(n,[null].concat(i)))}function l(e,t,n){var i=c(e,n),r=i.fn,o=i.dependencies;return r.apply(t,o)}function u(e){return et((function(t){return e.get(t)}))}function h(e,t){if(t&&t.length){var n,r,a,s,c=Object.create(null),p=Object.create(null),l=[],h=[],d=[];for(var f in i)n=i[f],-1!==t.indexOf(f)&&("private"===n[2]?-1===(r=l.indexOf(n[3]))?(s=u(a=n[3].createChild([],t)),l.push(n[3]),h.push(a),d.push(s),c[f]=[s,f,"private",a]):c[f]=[d[r],f,"private",h[r]]:c[f]=[n[2],n[1]],p[f]=!0),"factory"!==n[2]&&"type"!==n[2]||!n[1].$scope||t.forEach((function(e){-1!==n[1].$scope.indexOf(e)&&(c[f]=[n[2],n[1]],p[e]=!0)}));t.forEach((function(e){if(!p[e])throw new Error('No provider for "'+e+'". Cannot use provider from the parent!')})),e.unshift(c)}return new ot(e,o)}var d,f,m={factory:l,type:p,value:function(e){return e}};function v(e,t){var n=e.__init__||[];return function(){n.forEach((function(e){"string"==typeof e?t.get(e):t.invoke(e)}))}}function y(e){var t=e.__exports__;if(t){var n=e.__modules__,r=Object.keys(e).reduce((function(t,n){return"__exports__"!==n&&"__modules__"!==n&&"__init__"!==n&&"__depends__"!==n&&(t[n]=e[n]),t}),Object.create(null)),a=h((n||[]).concat(r)),s=et((function(e){return a.get(e)}));t.forEach((function(e){i[e]=[s,e,"private",a]}));var c=(e.__init__||[]).slice();return c.unshift((function(){a.init()})),v(e=Object.assign({},e,{__init__:c}),a)}return Object.keys(e).forEach((function(t){if("__init__"!==t&&"__depends__"!==t)if("private"!==e[t][2]){var n=e[t][0],r=e[t][1];i[t]=[m[n],at(n,r),n]}else i[t]=e[t]})),v(e,o)}function g(e,t){return-1!==e.indexOf(t)||-1!==(e=(t.__depends__||[]).reduce(g,e)).indexOf(t)?e:e.concat(t)}this.get=s,this.invoke=l,this.instantiate=p,this.createChild=h,this.init=(d=e.reduce(g,[]).map(y),f=!1,function(){f||(f=!0,d.forEach((function(e){return e()})))})}function at(e,t){return"value"!==e&&Qe(t)&&(t=et(t.slice())),t}function st(e,t){var n=this;t=t||1e3,e.on(["render.shape","render.connection"],t,(function(e,t){var i=e.type,r=t.element,o=t.gfx,a=t.attrs;if(n.canRender(r))return"render.shape"===i?n.drawShape(o,r,a):n.drawConnection(o,r,a)})),e.on(["render.getShapePath","render.getConnectionPath"],t,(function(e,t){if(n.canRender(t))return"render.getShapePath"===e.type?n.getShapePath(t):n.getConnectionPath(t)}))}function ct(e){return e.join(",").replace(/,?([A-z]),?/g,"$1")}function pt(e){for(var t,n="",i=0;t=e[i];i++)n+=t.x+","+t.y+" ";return n}function lt(e,t){var n=Le("polyline");return Se(n,{points:pt(e)}),t&&Se(n,t),n}function ut(e){return y(e,(function(t){return!m(e,(function(e){return e!==t&&ht(t,e)}))}))}function ht(e,t){if(t){if(e===t)return t;if(e.parent)return ht(e.parent,t)}}function dt(e,t,n){var i=!n||-1===e.indexOf(t);return i&&e.push(t),i}function ft(e,t,n){n=n||0,p(e)||(e=[e]),g(e,(function(e,i){var r=t(e,i,n);p(r)&&r.length&&ft(r,t,n+1)}))}function mt(e,t){return function(e,t,n){var i=[],r=[];return ft(e,(function(e,o,a){dt(i,e,t);var s=e.children;if((-1===n||ar||void 0===r)&&(r=s+l),(c+p>o||void 0===o)&&(o=c+p)})),{x:n,y:i,height:o-i,width:r-n}}function yt(e){return"waypoints"in e?"connection":"x"in e?"shape":"root"}function gt(e){return!(!e||!e.isFrame)}function bt(e,t){return F({},e||{},t||{})}st.prototype.canRender=function(){},st.prototype.drawShape=function(){},st.prototype.drawConnection=function(){},st.prototype.getShapePath=function(){},st.prototype.getConnectionPath=function(){};function xt(e,t){st.call(this,e,1),this.CONNECTION_STYLE=t.style(["no-fill"],{strokeWidth:5,stroke:"fuchsia"}),this.SHAPE_STYLE=t.style({fill:"white",stroke:"fuchsia",strokeWidth:2}),this.FRAME_STYLE=t.style(["no-fill"],{stroke:"fuchsia",strokeDasharray:4,strokeWidth:2})}e(xt,st),xt.prototype.canRender=function(){return!0},xt.prototype.drawShape=function(e,t,n){var i=Le("rect");return Se(i,{x:0,y:0,width:t.width||0,height:t.height||0}),gt(t)?Se(i,F({},this.FRAME_STYLE,n||{})):Se(i,F({},this.SHAPE_STYLE,n||{})),_e(e,i),i},xt.prototype.drawConnection=function(e,t,n){var i=lt(t.waypoints,F({},this.CONNECTION_STYLE,n||{}));return _e(e,i),i},xt.prototype.getShapePath=function(e){var t=e.x,n=e.y,i=e.width;return ct([["M",t,n],["l",i,0],["l",0,e.height],["l",-i,0],["z"]])},xt.prototype.getConnectionPath=function(e){var t,n,i=e.waypoints,r=[];for(t=0;n=i[t];t++)n=n.original||n,r.push([0===t?"M":"L",n.x,n.y]);return ct(r)},xt.$inject=["eventBus","styles"];var _t={__init__:["defaultRenderer"],defaultRenderer:["type",xt],styles:["type",function(){var e={"no-fill":{fill:"none"},"no-border":{strokeOpacity:0},"no-events":{pointerEvents:"none"}},t=this;this.cls=function(e,t,n){return F(this.style(t,n),{class:e})},this.style=function(t,n){p(t)||n||(n=t,t=[]);var i=x(t,(function(t,n){return F(t,e[n]||{})}),{});return n?F(i,n):i},this.computeStyle=function(e,n,i){return p(n)||(i=n,n=[]),t.style(n||[],F({},i,e||{}))}}]};function Et(e,t){if(!e||!t)return-1;var n=e.indexOf(t);return-1!==n&&e.splice(n,1),n}function wt(e,t,n){if(e&&t){"number"!=typeof n&&(n=-1);var i=e.indexOf(t);if(-1!==i){if(i===n)return;if(-1===n)return;e.splice(i,1)}-1!==n?e.splice(n,0,t):e.push(t)}}function St(e,t){return e&&t?e.indexOf(t):-1}function Ct(e,t){return e&&t?Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2)):-1}function At(e,t,n,i){if(void 0===i&&(i=5),!e||!t||!n)return!1;var r=(t.x-e.x)*(n.y-e.y)-(t.y-e.y)*(n.x-e.x),o=Ct(e,t);return Math.abs(r/o)<=i}function Rt(e,t){var n;return function(e,t){var n;n=p(e)?e:[e,t];var i=n.slice().shift();return _(n,(function(e){return Math.abs(i.y-e.y)<=2}))}(n=p(e)?e:[e,t])?"h":!!function(e,t){var n;n=p(e)?e:[e,t];var i=n.slice().shift();return _(n,(function(e){return Math.abs(i.x-e.x)<=2}))}(n)&&"v"}function Tt(e,t,n){return n=n||0,e.x>t.x-n&&e.y>t.y-n&&e.x=e.x&&t<=e.x+e.width&&n>=e.y&&n<=e.y+e.height}function Jt(e,t,n,i,r){return e*(e*(-3*t+9*n-9*i+3*r)+6*t-12*n+6*i)-3*t+3*n}function en(e,t,n,i,r,o,a,s,c){null==c&&(c=1);for(var p=(c=c>1?1:c<0?0:c)/2,l=[-.1252,.1252,-.3678,.3678,-.5873,.5873,-.7699,.7699,-.9041,.9041,-.9816,.9816],u=[.2491,.2491,.2335,.2335,.2032,.2032,.1601,.1601,.1069,.1069,.0472,.0472],h=0,d=0;d<12;d++){var f=p*l[d]+p,m=Jt(f,e,n,r,a),v=Jt(f,t,i,o,s),y=m*m+v*v;h+=u[d]*Nt.sqrt(y)}return p*h}function tn(e,t,n,i,r,o,a,s){if(!(Lt(e,n)Lt(r,a)||Lt(t,i)Lt(o,s))){var c=(e*i-t*n)*(o-s)-(t-i)*(r*s-o*a),p=(e-n)*(o-s)-(t-i)*(r-a);if(p){var l=nn(((e*i-t*n)*(r-a)-(e-n)*(r*s-o*a))/p),u=nn(c/p),h=+l.toFixed(2),d=+u.toFixed(2);if(!(h<+jt(e,n).toFixed(2)||h>+Lt(e,n).toFixed(2)||h<+jt(r,a).toFixed(2)||h>+Lt(r,a).toFixed(2)||d<+jt(t,i).toFixed(2)||d>+Lt(t,i).toFixed(2)||d<+jt(o,s).toFixed(2)||d>+Lt(o,s).toFixed(2)))return{x:l,y:u}}}}function nn(e){return Math.round(1e11*e)/1e11}function rn(e,t,n){if(!function(e,t){return e=qt(e),Qt(t=qt(t),e.x,e.y)||Qt(t,e.x2,e.y)||Qt(t,e.x,e.y2)||Qt(t,e.x2,e.y2)||Qt(e,t.x,t.y)||Qt(e,t.x2,t.y)||Qt(e,t.x,t.y2)||Qt(e,t.x2,t.y2)||(e.xt.x||t.xe.x)&&(e.yt.y||t.ye.y)}(Zt(e),Zt(t)))return n?0:[];for(var i=en.apply(0,e),r=en.apply(0,t),o=an(e)?1:~~(i/5)||1,a=an(t)?1:~~(r/5)||1,s=[],c=[],p={},l=n?0:[],u=0;u=0&&E<=1&&w>=0&&w<=1&&(n?l++:l.push({x:_.x,y:_.y,t1:E,t2:w}))}}return l}function on(e){var t=Ut(e);if(t.abs)return Yt(t.abs);if(Ht(e)&&Ht(e&&e[0])||(e=function(e){if(!e)return null;var t=Ut(e);if(t.arr)return Vt(t.arr);var n={a:7,c:6,h:1,l:2,m:2,q:4,s:4,t:2,v:1,z:0},i=[];return Ht(e)&&Ht(e[0])&&(i=Vt(e)),i.length||String(e).replace(zt,(function(e,t,r){var o=[],a=t.toLowerCase();for(r.replace($t,(function(e,t){t&&o.push(+t)})),"m"==a&&o.length>2&&(i.push([t].concat(o.splice(0,2))),a="l",t="m"==t?"l":"L");o.length>=n[a]&&(i.push([t].concat(o.splice(0,n[a]))),n[a]););})),i.toString=Ut.toString,t.arr=Vt(i),i}(e)),!e||!e.length)return[["M",0,0]];var n,i=[],r=0,o=0,a=0,s=0,c=0;"M"==e[0][0]&&(a=r=+e[0][1],s=o=+e[0][2],c++,i[0]=["M",r,o]);for(var p,l,u=c,h=e.length;u=1e3&&delete i[r.shift()],r.push(n),i[n]=u.apply(0,t),i[n])});if(p)S=p[0],C=p[1],E=p[2],w=p[3];else{e=(l=m(e,t,-d)).x,t=l.y;var v=(e-(s=(l=m(s,c,-d)).x))/2,y=(t-(c=l.y))/2,g=v*v/(n*n)+y*y/(i*i);g>1&&(n*=g=Nt.sqrt(g),i*=g);var b=n*n,x=i*i,_=(o==a?-1:1)*Nt.sqrt(Ft((b*x-b*y*y-x*v*v)/(b*y*y+x*v*v))),E=_*n*y/i+(e+s)/2,w=_*-i*v/n+(t+c)/2,S=Nt.asin(((t-w)/i).toFixed(9)),C=Nt.asin(((c-w)/i).toFixed(9));(S=eC&&(S-=2*Ot),!a&&C>S&&(C-=2*Ot)}var A=C-S;if(Ft(A)>h){var R=C,T=s,P=c;C=S+h*(a&&C>S?1:-1),f=pn(s=E+n*Nt.cos(C),c=w+i*Nt.sin(C),n,i,r,0,a,T,P,[C,R,E,w])}A=C-S;var D=Nt.cos(S),k=Nt.sin(S),M=Nt.cos(C),B=Nt.sin(C),N=Nt.tan(A/4),O=4/3*n*N,j=4/3*i*N,L=[e,t],I=[e+O*k,t-j*D],F=[s+O*B,c-j*M],z=[s,c];if(I[0]=2*L[0]-I[0],I[1]=2*L[1]-I[1],p)return[I,F,z].concat(f);for(var $=[],H=0,G=(f=[I,F,z].concat(f).join().split(",")).length;H7){e[t].shift();for(var i=e[t];i.length;)a[t]="A",e.splice(t++,0,["C"].concat(i.splice(0,6)));e.splice(t,1),l=n.length}},a=[],s="",c="",p=0,l=n.length;p=r.right,s=i.top-n.y>=r.bottom,c=o?"top":s?"bottom":null,p=i.right+n.x<=r.left?"left":a?"right":null;return p&&c?c+"-"+p:p||c||"intersect"}function xn(e,t,n){var i=hn(e,t);return 1===i.length||2===i.length&&Ct(i[0],i[1])<1?fn(i[0]):i.length>1?(i=D(i,(function(e){var t=Math.floor(100*e.t2)||1;return t=((t=100-t)<10?"0":"")+t,e.segment2+"#"+t})),fn(i[n?0:i.length-1])):null}function _n(e,t){return Math.round(e*t)/t}function En(e){return u(e)?e+"px":e}function wn(e,t,n){var i=Le("g");Pe(i).add(t);var r=void 0!==n?n:e.childNodes.length-1;return e.insertBefore(i,e.childNodes[r]||null),i}var Sn={shape:["x","y","width","height"],connection:["waypoints"]};function Cn(e,t,n,i){this._eventBus=t,this._elementRegistry=i,this._graphicsFactory=n,this._rootsIdx=0,this._layers={},this._planes=[],this._rootElement=null,this._init(e||{})}function An(e,t){var n="matrix("+t.a+","+t.b+","+t.c+","+t.d+","+t.e+","+t.f+")";e.setAttribute("transform",n)}Cn.$inject=["config.canvas","eventBus","graphicsFactory","elementRegistry"],Cn.prototype._init=function(e){var t=this._eventBus,n=this._container=function(e){var t=(e=F({},{width:"100%",height:"100%"},e)).container||document.body,n=document.createElement("div");return n.setAttribute("class","djs-container"),K(n,{position:"relative",overflow:"hidden",width:En(e.width),height:En(e.height)}),t.appendChild(n),n}(e),i=this._svg=Le("svg");Se(i,{width:"100%",height:"100%"}),_e(n,i);var r=this._viewport=wn(i,"viewport");!1!==e.deferUpdate&&(this._viewboxChanged=j(L(this._viewboxChanged,this),300)),t.on("diagram.init",(function(){t.fire("canvas.init",{svg:i,viewport:r})}),this),t.on(["shape.added","connection.added","shape.removed","connection.removed","elements.changed","root.set"],(function(){delete this._cachedViewbox}),this),t.on("diagram.destroy",500,this._destroy,this),t.on("diagram.clear",500,this._clear,this)},Cn.prototype._destroy=function(e){this._eventBus.fire("canvas.destroy",{svg:this._svg,viewport:this._viewport});var t=this._container.parentNode;t&&t.removeChild(this._container),delete this._svg,delete this._container,delete this._layers,delete this._planes,delete this._rootElement,delete this._viewport},Cn.prototype._clear=function(){var e=this;this._elementRegistry.getAll().forEach((function(t){var n=yt(t);"root"===n?e.removeRootElement(t):e._removeElement(t,n)})),this._planes=[],this._rootElement=null,delete this._cachedViewbox},Cn.prototype.getDefaultLayer=function(){return this.getLayer("base",0)},Cn.prototype.getLayer=function(e,t){if(!e)throw new Error("must specify a name");var n=this._layers[e];if(n||(n=this._layers[e]=this._createLayer(e,t)),void 0!==t&&n.index!==t)throw new Error("layer <"+e+"> already created at index <"+t+">");return n.group},Cn.prototype._getChildIndex=function(e){return x(this._layers,(function(t,n){return n.visible&&e>=n.index&&t++,t}),0)},Cn.prototype._createLayer=function(e,t){void 0===t&&(t=1);var n=this._getChildIndex(t);return{group:wn(this._viewport,"layer-"+e,n),index:t,visible:!0}},Cn.prototype.showLayer=function(e){if(!e)throw new Error("must specify a name");var t=this._layers[e];if(!t)throw new Error("layer <"+e+"> does not exist");var n=this._viewport,i=t.group,r=t.index;if(t.visible)return i;var o=this._getChildIndex(r);return n.insertBefore(i,n.childNodes[o]||null),t.visible=!0,i},Cn.prototype.hideLayer=function(e){if(!e)throw new Error("must specify a name");var t=this._layers[e];if(!t)throw new Error("layer <"+e+"> does not exist");var n=t.group;return t.visible?(ke(n),t.visible=!1,n):n},Cn.prototype._removeLayer=function(e){var t=this._layers[e];t&&(delete this._layers[e],ke(t.group))},Cn.prototype.getActiveLayer=function(){var e=this._findPlaneForRoot(this.getRootElement());return e?e.layer:null},Cn.prototype.findRoot=function(e){if("string"==typeof e&&(e=this._elementRegistry.get(e)),e){var t=this._findPlaneForRoot(function(e){for(;e.parent;)e=e.parent;return e}(e))||{};return t.rootElement}},Cn.prototype.getRootElements=function(){return this._planes.map((function(e){return e.rootElement}))},Cn.prototype._findPlaneForRoot=function(e){return m(this._planes,(function(t){return t.rootElement===e}))},Cn.prototype.getContainer=function(){return this._container},Cn.prototype._updateMarker=function(e,t,n){var i;e.id||(e=this._elementRegistry.get(e)),(i=this._elementRegistry._elements[e.id])&&(g([i.gfx,i.secondaryGfx],(function(e){e&&(n?Pe(e).add(t):Pe(e).remove(t))})),this._eventBus.fire("element.marker.update",{element:e,gfx:i.gfx,marker:t,add:!!n}))},Cn.prototype.addMarker=function(e,t){this._updateMarker(e,t,!0)},Cn.prototype.removeMarker=function(e,t){this._updateMarker(e,t,!1)},Cn.prototype.hasMarker=function(e,t){return e.id||(e=this._elementRegistry.get(e)),Pe(this.getGraphics(e)).has(t)},Cn.prototype.toggleMarker=function(e,t){this.hasMarker(e,t)?this.removeMarker(e,t):this.addMarker(e,t)},Cn.prototype.getRootElement=function(){var e=this._rootElement;return e||this._planes.length?e:this.setRootElement(this.addRootElement(null))},Cn.prototype.addRootElement=function(e){var t=this._rootsIdx++;e||(e={id:"__implicitroot_"+t,children:[],isImplicit:!0});var n=e.layer="root-"+t;this._ensureValid("root",e);var i=this.getLayer(n,0);return this.hideLayer(n),this._addRoot(e,i),this._planes.push({rootElement:e,layer:i}),e},Cn.prototype.removeRootElement=function(e){if("string"==typeof e&&(e=this._elementRegistry.get(e)),this._findPlaneForRoot(e))return this._removeRoot(e),this._removeLayer(e.layer),this._planes=this._planes.filter((function(t){return t.rootElement!==e})),this._rootElement===e&&(this._rootElement=null),e},Cn.prototype.setRootElement=function(e,t){if(s(t))throw new Error("override not supported");if(e!==this._rootElement){if(!e)throw new Error("rootElement required");return this._findPlaneForRoot(e)||(e=this.addRootElement(e)),this._setRoot(e),e}},Cn.prototype._removeRoot=function(e){var t=this._elementRegistry,n=this._eventBus;n.fire("root.remove",{element:e}),n.fire("root.removed",{element:e}),t.remove(e)},Cn.prototype._addRoot=function(e,t){var n=this._elementRegistry,i=this._eventBus;i.fire("root.add",{element:e}),n.add(e,t),i.fire("root.added",{element:e,gfx:t})},Cn.prototype._setRoot=function(e,t){var n=this._rootElement;n&&(this._elementRegistry.updateGraphics(n,null,!0),this.hideLayer(n.layer)),e&&(t||(t=this._findPlaneForRoot(e).layer),this._elementRegistry.updateGraphics(e,this._svg,!0),this.showLayer(e.layer)),this._rootElement=e,this._eventBus.fire("root.set",{element:e})},Cn.prototype._ensureValid=function(e,t){if(!t.id)throw new Error("element must have an id");if(this._elementRegistry.get(t.id))throw new Error("element <"+t.id+"> already exists");var n=Sn[e],i=_(n,(function(e){return void 0!==t[e]}));if(!i)throw new Error("must supply { "+n.join(", ")+" } with "+e)},Cn.prototype._setParent=function(e,t,n){wt(t.children,e,n),e.parent=t},Cn.prototype._addElement=function(e,t,n,i){n=n||this.getRootElement();var r=this._eventBus,o=this._graphicsFactory;this._ensureValid(e,t),r.fire(e+".add",{element:t,parent:n}),this._setParent(t,n,i);var a=o.create(e,t,i);return this._elementRegistry.add(t,a),o.update(e,t,a),r.fire(e+".added",{element:t,gfx:a}),t},Cn.prototype.addShape=function(e,t,n){return this._addElement("shape",e,t,n)},Cn.prototype.addConnection=function(e,t,n){return this._addElement("connection",e,t,n)},Cn.prototype._removeElement=function(e,t){var n=this._elementRegistry,i=this._graphicsFactory,r=this._eventBus;if(e=n.get(e.id||e))return r.fire(t+".remove",{element:e}),i.remove(e),Et(e.parent&&e.parent.children,e),e.parent=null,r.fire(t+".removed",{element:e}),n.remove(e),e},Cn.prototype.removeShape=function(e){return this._removeElement(e,"shape")},Cn.prototype.removeConnection=function(e){return this._removeElement(e,"connection")},Cn.prototype.getGraphics=function(e,t){return this._elementRegistry.getGraphics(e,t)},Cn.prototype._changeViewbox=function(e){this._eventBus.fire("canvas.viewbox.changing"),e.apply(this),this._cachedViewbox=null,this._viewboxChanged()},Cn.prototype._viewboxChanged=function(){this._eventBus.fire("canvas.viewbox.changed",{viewbox:this.viewbox()})},Cn.prototype.viewbox=function(e){if(void 0===e&&this._cachedViewbox)return this._cachedViewbox;var t,n,i,r,o,a,s,c=this._viewport,p=this.getSize();return e?(this._changeViewbox((function(){o=Math.min(p.width/e.width,p.height/e.height);var t=this._svg.createSVGMatrix().scale(o).translate(-e.x,-e.y);Xe(c,t)})),e):(t=(i=this._rootElement?this.getActiveLayer():null)&&i.getBBox()||{},n=(r=Xe(c))?r.matrix:function(e,t,n,i,r,o){var a=Fe().createSVGMatrix();switch(arguments.length){case 0:return a;case 1:return ze(a,e);case 6:return ze(a,{a:e,b:t,c:n,d:i,e:r,f:o})}}(),o=_n(n.a,1e3),a=_n(-n.e||0,1e3),s=_n(-n.f||0,1e3),e=this._cachedViewbox={x:a?a/o:0,y:s?s/o:0,width:p.width/o,height:p.height/o,scale:o,inner:{width:t.width||0,height:t.height||0,x:t.x||0,y:t.y||0},outer:p})},Cn.prototype.scroll=function(e){var t=this._viewport,n=t.getCTM();return e&&this._changeViewbox((function(){e=F({dx:0,dy:0},e||{}),n=this._svg.createSVGMatrix().translate(e.dx,e.dy).multiply(n),An(t,n)})),{x:n.e,y:n.f}},Cn.prototype.scrollToElement=function(e,t){var n=100;"string"==typeof e&&(e=this._elementRegistry.get(e));var i=this.findRoot(e);i!==this.getRootElement()&&this.setRootElement(i),t||(t={}),"number"==typeof t&&(n=t),t={top:t.top||n,right:t.right||n,bottom:t.bottom||n,left:t.left||n};var r,o,a=vt(e),s=mn(a),c=this.viewbox(),p=this.zoom();c.y+=t.top/p,c.x+=t.left/p,c.width-=(t.right+t.left)/p,c.height-=(t.bottom+t.top)/p;var l=mn(c);if(a.width=0&&o.y>=0&&o.x+o.width<=r.width&&o.y+o.height<=r.height&&!e?n={x:0,y:0,width:Math.max(o.width+o.x,r.width),height:Math.max(o.height+o.y,r.height)}:(t=Math.min(1,r.width/o.width,r.height/o.height),n={x:o.x+(e?o.width/2-r.width/t/2:0),y:o.y+(e?o.height/2-r.height/t/2:0),width:r.width/t,height:r.height/t}),this.viewbox(n),this.viewbox(!1).scale},Cn.prototype._setZoom=function(e,t){var n,i,r,o,a=this._svg,s=this._viewport,c=a.createSVGMatrix(),p=a.createSVGPoint(),l=(i=s.getCTM()).a;return t?(n=F(p,t).matrixTransform(i.inverse()),r=c.translate(n.x,n.y).scale(1/l*e).translate(-n.x,-n.y),o=i.multiply(r)):o=c.scale(e),An(this._viewport,o),o},Cn.prototype.getSize=function(){return{width:this._container.clientWidth,height:this._container.clientHeight}},Cn.prototype.getAbsoluteBBox=function(e){var t,n=this.viewbox();e.waypoints?t=this.getGraphics(e).getBBox():t=e;return{x:t.x*n.scale-n.x*n.scale,y:t.y*n.scale-n.y*n.scale,width:t.width*n.scale,height:t.height*n.scale}},Cn.prototype.resized=function(){delete this._cachedViewbox,this._eventBus.fire("canvas.resized")};var Rn="data-element-id";function Tn(e){this._elements={},this._eventBus=e}Tn.$inject=["eventBus"],Tn.prototype.add=function(e,t,n){var i=e.id;this._validateId(i),Se(t,Rn,i),n&&Se(n,Rn,i),this._elements[i]={element:e,gfx:t,secondaryGfx:n}},Tn.prototype.remove=function(e){var t=this._elements,n=e.id||e,i=n&&t[n];i&&(Se(i.gfx,Rn,""),i.secondaryGfx&&Se(i.secondaryGfx,Rn,""),delete t[n])},Tn.prototype.updateId=function(e,t){this._validateId(t),"string"==typeof e&&(e=this.get(e)),this._eventBus.fire("element.updateId",{element:e,newId:t});var n=this.getGraphics(e),i=this.getGraphics(e,!0);this.remove(e),e.id=t,this.add(e,n,i)},Tn.prototype.updateGraphics=function(e,t,n){var i=e.id||e,r=this._elements[i];return n?r.secondaryGfx=t:r.gfx=t,t&&Se(t,Rn,i),t},Tn.prototype.get=function(e){var t;t="string"==typeof e?e:e&&Se(e,Rn);var n=this._elements[t];return n&&n.element},Tn.prototype.filter=function(e){var t=[];return this.forEach((function(n,i){e(n,i)&&t.push(n)})),t},Tn.prototype.find=function(e){for(var t=this._elements,n=Object.keys(t),i=0;i in ref");t=this.props[t]}t.collection?Mn(this,t,e):function(e,t,n){var i=t.inverse,r=n[t.name];Object.defineProperty(n,t.name,{configurable:t.configurable,enumerable:t.enumerable,get:function(){return r},set:function(t){if(t!==r){var o=r;r=null,o&&e.unset(o,i,n),r=t,e.set(r,i,n)}}})}(this,t,e)},Bn.prototype.ensureRefsCollection=function(e,t){var n=e[t.name];return kn.isExtended(n)||Mn(this,t,e),n},Bn.prototype.ensureBound=function(e,t){(function(e,t){return Object.prototype.hasOwnProperty.call(e,t.name||t)})(e,t)||this.bind(e,t)},Bn.prototype.unset=function(e,t,n){e&&(this.ensureBound(e,t),t.collection?this.ensureRefsCollection(e,t).remove(n):e[t.name]=void 0)},Bn.prototype.set=function(e,t,n){e&&(this.ensureBound(e,t),t.collection?this.ensureRefsCollection(e,t).add(n):e[t.name]=n)};var Nn=Bn;!function(e){e.exports=Nn,e.exports.Collection=Dn}(Pn);var On=kt(Pn.exports),jn=new On({name:"children",enumerable:!0,collection:!0},{name:"parent"}),Ln=new On({name:"labels",enumerable:!0,collection:!0},{name:"labelTarget"}),In=new On({name:"attachers",collection:!0},{name:"host"}),Fn=new On({name:"outgoing",collection:!0},{name:"source"}),zn=new On({name:"incoming",collection:!0},{name:"target"});function $n(){Object.defineProperty(this,"businessObject",{writable:!0}),Object.defineProperty(this,"label",{get:function(){return this.labels[0]},set:function(e){var t=this.label,n=this.labels;!e&&t?n.remove(t):n.add(e,0)}}),jn.bind(this,"parent"),Ln.bind(this,"labels"),Fn.bind(this,"outgoing"),zn.bind(this,"incoming")}function Hn(){$n.call(this),jn.bind(this,"children"),In.bind(this,"host"),In.bind(this,"attachers")}function Gn(){Hn.call(this)}function Vn(){Hn.call(this),Ln.bind(this,"labelTarget")}function Wn(){$n.call(this),Fn.bind(this,"source"),zn.bind(this,"target")}e(Hn,$n),e(Gn,Hn),e(Vn,Hn),e(Wn,$n);var Un={connection:Wn,shape:Hn,label:Vn,root:Gn};function qn(){this._uid=12}qn.prototype.createRoot=function(e){return this.create("root",e)},qn.prototype.createLabel=function(e){return this.create("label",e)},qn.prototype.createShape=function(e){return this.create("shape",e)},qn.prototype.createConnection=function(e){return this.create("connection",e)},qn.prototype.create=function(e,t){return(t=F({},t||{})).id||(t.id=e+"_"+this._uid++),function(e,t){var n=Un[e];if(!n)throw new Error("unknown type: <"+e+">");return F(new n,t)}(e,t)};var Kn=Array.prototype.slice;function Yn(){this._listeners={},this.on("diagram.destroy",1,this._destroy,this)}function Xn(){}function Zn(e){return e.childNodes[0]}function Qn(e,t,n,i,r){var o=$e();o.setTranslate(t,n);var a=$e();a.setRotate(i||0,0,0);var s=$e();s.setScale(r||1,r||1),Xe(e,[o,a,s])}function Jn(e,t,n){var i=$e();i.setTranslate(t,n),Xe(e,i)}function ei(e,t){var n=$e();n.setRotate(t,0,0),Xe(e,n)}function ti(e,t){this._eventBus=e,this._elementRegistry=t}function ni(e,t,n){var i=n||t.firstChild;e!==i&&t.insertBefore(e,i)}Yn.prototype.on=function(e,t,n,i){if(e=p(e)?e:[e],h(t)&&(i=n,n=t,t=1e3),!u(t))throw new Error("priority must be a number");var r=n;i&&((r=L(n,i)).__fn=n.__fn||n);var o=this;e.forEach((function(e){o._addListener(e,{priority:t,callback:r,next:null})}))},Yn.prototype.once=function(e,t,n,i){var r=this;if(h(t)&&(i=n,n=t,t=1e3),!u(t))throw new Error("priority must be a number");function o(){o.__isTomb=!0;var t=n.apply(i,arguments);return r.off(e,o),t}o.__fn=n,this.on(e,t,o)},Yn.prototype.off=function(e,t){e=p(e)?e:[e];var n=this;e.forEach((function(e){n._removeListener(e,t)}))},Yn.prototype.createEvent=function(e){var t=new Xn;return t.init(e),t},Yn.prototype.fire=function(e,t){var n,i,r,o;if(o=Kn.call(arguments),"object"==typeof e&&(e=(t=e).type),!e)throw new Error("no event type specified");if(i=this._listeners[e]){n=t instanceof Xn?t:this.createEvent(t),o[0]=n;var a=n.type;e!==a&&(n.type=e);try{r=this._invokeListeners(n,o,i)}finally{e!==a&&(n.type=a)}return void 0===r&&n.defaultPrevented&&(r=!1),r}},Yn.prototype.handleError=function(e){return!1===this.fire("error",{error:e})},Yn.prototype._destroy=function(){this._listeners={}},Yn.prototype._invokeListeners=function(e,t,n){for(var i;n&&!e.cancelBubble;)i=this._invokeListener(e,t,n),n=n.next;return i},Yn.prototype._invokeListener=function(e,t,n){var i;if(n.callback.__isTomb)return i;try{i=function(e,t){return e.apply(null,t)}(n.callback,t),void 0!==i&&(e.returnValue=i,e.stopPropagation()),!1===i&&e.preventDefault()}catch(e){if(!this.handleError(e))throw console.error("unhandled error in event listener",e),e}return i},Yn.prototype._addListener=function(e,t){var n,i=this._getListeners(e);if(i){for(;i;){if(i.priority or , got "+e);n=r[1],i=r[0]}return{name:e=(i?i+":":"")+n,prefix:i,localName:n}}function fi(e){this.ns=e,this.name=e.name,this.allTypes=[],this.allTypesByName={},this.properties=[],this.propertiesByName={}}function mi(e,t){this.packageMap={},this.typeMap={},this.packages=[],this.properties=t,g(e,L(this.registerPackage,this))}function vi(e,t,n){var i=t[n];if(i in e)throw new Error("package with "+n+" <"+i+"> already defined")}function yi(e){this.model=e}function gi(e,t,n){Object.defineProperty(e,t.name,{enumerable:!t.isReference,writable:!0,value:n,configurable:!0})}function bi(e){this.properties=new yi(this),this.factory=new si(this,this.properties),this.registry=new mi(e,this.properties),this.typeCache={}}fi.prototype.build=function(){return z(this,["ns","name","allTypes","allTypesByName","properties","propertiesByName","bodyProperty","idProperty"])},fi.prototype.addProperty=function(e,t,n){"boolean"==typeof t&&(n=t,t=void 0),this.addNamedProperty(e,!1!==n);var i=this.properties;void 0!==t?i.splice(t,0,e):i.push(e)},fi.prototype.replaceProperty=function(e,t,n){var i=e.ns,r=this.properties,o=this.propertiesByName,a=e.name!==t.name;if(e.isId){if(!t.isId)throw new Error("property <"+t.ns.name+"> must be id property to refine <"+e.ns.name+">");this.setIdProperty(t,!1)}if(e.isBody){if(!t.isBody)throw new Error("property <"+t.ns.name+"> must be body property to refine <"+e.ns.name+">");this.setBodyProperty(t,!1)}var s=r.indexOf(e);if(-1===s)throw new Error("property <"+i.name+"> not found in property list");r.splice(s,1),this.addProperty(t,n?void 0:s,a),o[i.name]=o[i.localName]=t},fi.prototype.redefineProperty=function(e,t,n){var i=e.ns.prefix,r=t.split("#"),o=di(r[0],i),a=di(r[1],o.prefix).name,s=this.propertiesByName[a];if(!s)throw new Error("refined property <"+a+"> not found");this.replaceProperty(s,e,n),delete e.redefines},fi.prototype.addNamedProperty=function(e,t){var n=e.ns,i=this.propertiesByName;t&&(this.assertNotDefined(e,n.name),this.assertNotDefined(e,n.localName)),i[n.name]=i[n.localName]=e},fi.prototype.removeNamedProperty=function(e){var t=e.ns,n=this.propertiesByName;delete n[t.name],delete n[t.localName]},fi.prototype.setBodyProperty=function(e,t){if(t&&this.bodyProperty)throw new Error("body property defined multiple times (<"+this.bodyProperty.ns.name+">, <"+e.ns.name+">)");this.bodyProperty=e},fi.prototype.setIdProperty=function(e,t){if(t&&this.idProperty)throw new Error("id property defined multiple times (<"+this.idProperty.ns.name+">, <"+e.ns.name+">)");this.idProperty=e},fi.prototype.assertNotDefined=function(e,t){var n=e.name,i=this.propertiesByName[n];if(i)throw new Error("property <"+n+"> already defined; override of <"+i.definedBy.ns.name+"#"+i.ns.name+"> by <"+e.definedBy.ns.name+"#"+e.ns.name+"> not allowed without redefines")},fi.prototype.hasProperty=function(e){return this.propertiesByName[e]},fi.prototype.addTrait=function(e,t){var n=this.allTypesByName,i=this.allTypes,r=e.name;r in n||(g(e.properties,L((function(n){n=F({},n,{name:n.ns.localName,inherited:t}),Object.defineProperty(n,"definedBy",{value:e});var i=n.replaces,r=n.redefines;i||r?this.redefineProperty(n,i||r,i):(n.isBody&&this.setBodyProperty(n),n.isId&&this.setIdProperty(n),this.addProperty(n))}),this)),i.push(e),n[r]=e)},mi.prototype.getPackage=function(e){return this.packageMap[e]},mi.prototype.getPackages=function(){return this.packages},mi.prototype.registerPackage=function(e){e=F({},e);var t=this.packageMap;vi(t,e,"prefix"),vi(t,e,"uri"),g(e.types,L((function(t){this.registerType(t,e)}),this)),t[e.uri]=t[e.prefix]=e,this.packages.push(e)},mi.prototype.registerType=function(e,t){var n=di((e=F({},e,{superClass:(e.superClass||[]).slice(),extends:(e.extends||[]).slice(),properties:(e.properties||[]).slice(),meta:F(e.meta||{})})).name,t.prefix),i=n.name,r={};g(e.properties,L((function(e){var t=di(e.name,n.prefix),i=t.name;ui(e.type)||(e.type=di(e.type,t.prefix).name),F(e,{ns:t,name:i}),r[i]=e}),this)),F(e,{ns:n,name:i,propertiesByName:r}),g(e.extends,L((function(e){var t=this.typeMap[e];t.traits=t.traits||[],t.traits.push(i)}),this)),this.definePackage(e,t),this.typeMap[i]=e},mi.prototype.mapTypes=function(e,t,n){var i=ui(e.name)?{name:e.name}:this.typeMap[e.name],r=this;function o(e){return a(e,!0)}function a(n,i){var o=di(n,ui(n)?"":e.prefix);r.mapTypes(o,t,i)}if(!i)throw new Error("unknown type <"+e.name+">");g(i.superClass,n?o:a),t(i,!n),g(i.traits,o)},mi.prototype.getEffectiveDescriptor=function(e){var t=di(e),n=new fi(t);this.mapTypes(t,(function(e,t){n.addTrait(e,t)}));var i=n.build();return this.definePackage(i,i.allTypes[i.allTypes.length-1].$pkg),i},mi.prototype.definePackage=function(e,t){this.properties.define(e,"$pkg",{value:t})},yi.prototype.set=function(e,t,n){var i=this.model.getPropertyDescriptor(e,t),r=i&&i.name;void 0===n?i?delete e[r]:delete e.$attrs[t]:i?r in e?e[r]=n:gi(e,i,n):e.$attrs[t]=n},yi.prototype.get=function(e,t){var n=this.model.getPropertyDescriptor(e,t);if(!n)return e.$attrs[t];var i=n.name;return!e[i]&&n.isMany&&gi(e,n,[]),e[i]},yi.prototype.define=function(e,t,n){if(!n.writable){var i=n.value;delete(n=F({},n,{get:function(){return i}})).value}Object.defineProperty(e,t,n)},yi.prototype.defineDescriptor=function(e,t){this.define(e,"$descriptor",{value:t})},yi.prototype.defineModel=function(e,t){this.define(e,"$model",{value:t})},bi.prototype.create=function(e,t){var n=this.getType(e);if(!n)throw new Error("unknown type <"+e+">");return new n(t)},bi.prototype.getType=function(e){var t=this.typeCache,n=d(e)?e:e.ns.name,i=t[n];return i||(e=this.registry.getEffectiveDescriptor(n),i=t[n]=this.factory.createType(e)),i},bi.prototype.createAny=function(e,t,n){var i=di(e),r={$type:e,$instanceOf:function(e){return e===this.$type}},o={name:e,isGeneric:!0,ns:{prefix:i.prefix,localName:i.localName,uri:t}};return this.properties.defineDescriptor(r,o),this.properties.defineModel(r,this),this.properties.define(r,"$parent",{enumerable:!1,writable:!0}),this.properties.define(r,"$instanceOf",{enumerable:!1,writable:!0}),g(n,(function(e,t){l(e)&&void 0!==e.value?r[e.name]=e.value:r[t]=e})),r},bi.prototype.getPackage=function(e){return this.registry.getPackage(e)},bi.prototype.getPackages=function(){return this.registry.getPackages()},bi.prototype.getElementDescriptor=function(e){return e.$descriptor},bi.prototype.hasType=function(e,t){return void 0===t&&(t=e,e=this),t in e.$model.getElementDescriptor(e).allTypesByName},bi.prototype.getPropertyDescriptor=function(e,t){return this.getElementDescriptor(e).propertiesByName[t]},bi.prototype.getTypeDescriptor=function(e){return this.registry.typeMap[e]};var xi=String.fromCharCode,_i=Object.prototype.hasOwnProperty,Ei=/&#(\d+);|&#x([0-9a-f]+);|&(\w+);/gi,wi={amp:"&",apos:"'",gt:">",lt:"<",quot:'"'};function Si(e,t,n,i){return i?_i.call(wi,i)?wi[i]:"&"+i+";":xi(t||parseInt(n,16))}function Ci(e){return e.length>3&&-1!==e.indexOf("&")?e.replace(Ei,Si):e}Object.keys(wi).forEach((function(e){wi[e.toUpperCase()]=wi[e]}));var Ai="xsi:type",Ri="non-whitespace outside of root node";function Ti(e){return new Error(e)}function Pi(e){return"missing namespace for prefix <"+e+">"}function Di(e){return{get:e,enumerable:!0}}function ki(e){var t,n={};for(t in e)n[t]=e[t];return n}function Mi(e){return e+"$uri"}function Bi(){return{line:0,column:0}}function Ni(e){throw e}function Oi(e){if(!this)return new Oi(e);var t,n,i,r,o,a,s,c,p,l=e&&e.proxy,u=Ni,h=Bi,d=!1,f=!1,m=null,v=!1;function y(e){e instanceof Error||(e=Ti(e)),m=e,u(e,h)}function g(e){o&&(e instanceof Error||(e=Ti(e)),o(e,h))}this.on=function(e,p){if("function"!=typeof p)throw Ti("required args ");switch(e){case"openTag":n=p;break;case"text":t=p;break;case"closeTag":i=p;break;case"error":u=p;break;case"warn":o=p;break;case"cdata":r=p;break;case"attention":c=p;break;case"question":s=p;break;case"comment":a=p;break;default:throw Ti("unsupported event: "+e)}return this},this.ns=function(e){if(void 0===e&&(e={}),"object"!=typeof e)throw Ti("required args ");var t,n={};for(t in e)n[t]=e[t];return n["http://www.w3.org/2001/XMLSchema-instance"]="xsi",f=!0,p=n,this},this.parse=function(e){if("string"!=typeof e)throw Ti("required args ");return m=null,function(e){var o,u,m,b,x,_,E,w,S,C,A,R=f?[]:null,T=f?function(e){var t,n,i={};for(t in e)i[n=e[t]]=n,i[Mi(n)]=t;return i}(p):null,P=[],D=0,k=!1,M=!1,B=0,N=0,O="",j=0;function L(){if(null!==A)return A;var e,t,n,i,r,o,a,s,c,l,u,h=f&&T.xmlns,m=f&&d?[]:null,v=j,y=O,b=y.length,x={},_={};e:for(;v8)){for((l<65||l>122||l>90&&l<97)&&95!==l&&58!==l&&(g("illegal first char attribute name"),c=!0),u=v+1;u96&&l<123||l>64&&l<91||l>47&&l<59||46===l||45===l||95===l)){if(32===l||l<14&&l>8){g("missing attribute value"),v=u;continue e}if(61===l)break;g("illegal attribute name char"),c=!0}if("xmlns:xmlns"===(s=y.substring(v,u))&&(g("illegal declaration of xmlns"),c=!0),34===(l=y.charCodeAt(u+1)))-1===(u=y.indexOf('"',v=u+2))&&-1!==(u=y.indexOf("'",v))&&(g("attribute value quote missmatch"),c=!0);else if(39===l)-1===(u=y.indexOf("'",v=u+2))&&-1!==(u=y.indexOf('"',v))&&(g("attribute value quote missmatch"),c=!0);else for(g("missing attribute value quotes"),c=!0,u+=1;u8);u++);for(-1===u&&(g("missing closing quotes"),u=b,c=!0),c||(o=y.substring(v,u)),v=u;u+18);u++)v===u&&(g("illegal character after attribute end"),c=!0);if(v=u+1,!c)if(s in _)g("attribute <"+s+"> already defined");else if(_[s]=!0,f)if(d){if(null!==(r="xmlns"===s?"xmlns":120===s.charCodeAt(0)&&"xmlns:"===s.substr(0,6)?s.substr(6):null)){if(e=Ci(o),t=Mi(r),!(a=p[e])){if("xmlns"===r||t in T&&T[t]!==e)do{a="ns"+D++}while(void 0!==T[a]);else a=r;p[e]=a}T[r]!==a&&(i||(T=ki(T),i=!0),T[r]=a,"xmlns"===r&&(T[Mi(a)]=e,h=a),T[t]=e),x[s]=o;continue}m.push(s,o)}else-1!==(l=s.indexOf(":"))?(n=T[s.substring(0,l)])?((s=h===n?s.substr(l+1):n+s.substr(l))===Ai&&(-1!==(l=o.indexOf(":"))?(n=o.substring(0,l),o=(n=T[n]||n)+o.substring(l)):o=h+":"+o),x[s]=o):g(Pi(s.substring(0,l))):x[s]=o;else x[s]=o}if(d)for(v=0,b=m.length;v=a&&(t=i.exec(e))&&!((s=t[0].length+t.index)>B);)r+=1,a=s;return-1==B?(o=s,n=e.substring(N)):0===N?n=e.substring(N,B):(o=B-a,n=-1==N?e.substring(B):e.substring(B,N+1)),{data:n,line:r,column:o}}h=I,l&&(C=Object.create({},{name:Di((function(){return w})),originalName:Di((function(){return S})),attrs:Di(L),ns:Di((function(){return T}))}));for(;-1!==N;){if(-1===(B=60===e.charCodeAt(N)?N:e.indexOf("<",N)))return P.length?y("unexpected end of file"):0===N?y("missing start tag"):void(N",B)))return y("unclosed cdata");if(r&&(r(e.substring(B+9,N),h),v))return;N+=3;continue}if(45===b&&45===e.charCodeAt(B+3)){if(-1===(N=e.indexOf("--\x3e",B)))return y("unclosed comment");if(a&&(a(e.substring(B+4,N),Ci,h),v))return;N+=3;continue}}if(63!==x){for(u=B+1;;u++){if(_=e.charCodeAt(u),isNaN(_))return N=-1,y("unclosed tag");if(34===_)u=-1!==(b=e.indexOf('"',u+1))?b:u;else if(39===_)u=-1!==(b=e.indexOf("'",u+1))?b:u;else if(62===_){N=u;break}}if(33!==x){if(A={},47===x){if(k=!1,M=!0,!P.length)return y("missing open tag");if(u=w=P.pop(),b=B+2+u.length,e.substring(B+2,b)!==u)return y("closing tag mismatch");for(;b8&&x<14))return y("close tag")}else{if(47===e.charCodeAt(N-1)?(u=w=e.substring(B+1,N-1),k=!0,M=!0):(u=w=e.substring(B+1,N),k=!0,M=!1),!(x>96&&x<123||x>64&&x<91||95===x||58===x))return y("illegal first char nodeName");for(b=1,m=u.length;b96&&x<123||x>64&&x<91||x>47&&x<59||45===x||95===x||46==x)){if(32===x||x<14&&x>8){w=u.substring(0,b),A=null;break}return y("invalid nodeName")}M||P.push(w)}if(f){if(o=T,k&&(M||R.push(o),null===A&&(d=-1!==u.indexOf("xmlns",b))&&(j=b,O=u,L(),d=!1)),S=w,-1!==(x=w.indexOf(":"))){if(!(E=T[w.substring(0,x)]))return y("missing namespace on <"+S+">");w=w.substr(x+1)}else E=T.xmlns;E&&(w=E+":"+w)}if(k&&(j=b,O=u,n&&(l?n(C,Ci,M,h):n(w,L,Ci,M,h),v)))return;if(M){if(i&&(i(l?C:w,Ci,k,h),v))return;f&&(T=k?o:R.pop())}N+=1}else{if(c&&(c(e.substring(B,N+1),Ci,h),v))return;N+=1}}else{if(-1===(N=e.indexOf("?>",B)))return y("unclosed question");if(s&&(s(e.substring(B,N+2),h),v))return;N+=2}}}(e),h=Bi,v=!1,m},this.stop=function(){v=!0}}function ji(e){return e.xml&&"lowerCase"===e.xml.tagAlias}var Li={xsi:"http://www.w3.org/2001/XMLSchema-instance",xml:"http://www.w3.org/XML/1998/namespace"},Ii="xsi:type";function Fi(e){return e.xml&&e.xml.serialize}function zi(e){return Fi(e)===Ii}function $i(e,t){return ji(t)?e.prefix+":"+((n=e.localName).charAt(0).toUpperCase()+n.slice(1)):e.name;var n}function Hi(e){return new Error(e)}function Gi(e){return e.$descriptor}function Vi(e){F(this,e),this.elementsById={},this.references=[],this.warnings=[],this.addReference=function(e){this.references.push(e)},this.addElement=function(e){if(!e)throw Hi("expected element");var t,n=this.elementsById,i=Gi(e).idProperty;if(i&&(t=e.get(i.name))){if(!/^([a-z][\w-.]*:)?[a-z_][\w-.]*$/i.test(t))throw new Error("illegal ID <"+t+">");if(n[t])throw Hi("duplicate ID <"+t+">");n[t]=e}},this.addWarning=function(e){this.warnings.push(e)}}function Wi(){}function Ui(){}function qi(){}function Ki(e,t){this.property=e,this.context=t}function Yi(e,t){this.element=t,this.propertyDesc=e}function Xi(){}function Zi(e,t,n){this.model=e,this.type=e.getType(t),this.context=n}function Qi(e,t,n){Zi.call(this,e,t,n)}function Ji(e,t,n){this.model=e,this.context=n}function er(e){e instanceof bi&&(e={model:e}),F(this,{lax:!1},e)}Wi.prototype.handleEnd=function(){},Wi.prototype.handleText=function(){},Wi.prototype.handleNode=function(){},Ui.prototype=Object.create(Wi.prototype),Ui.prototype.handleNode=function(){return this},qi.prototype=Object.create(Wi.prototype),qi.prototype.handleText=function(e){this.body=(this.body||"")+e},Ki.prototype=Object.create(qi.prototype),Ki.prototype.handleNode=function(e){if(this.element)throw Hi("expected no sub nodes");return this.element=this.createReference(e),this},Ki.prototype.handleEnd=function(){this.element.id=this.body},Ki.prototype.createReference=function(e){return{property:this.property.ns.name,id:""}},Yi.prototype=Object.create(qi.prototype),Yi.prototype.handleEnd=function(){var e=this.body||"",t=this.element,n=this.propertyDesc;e=li(n.type,e),n.isMany?t.get(n.name).push(e):t.set(n.name,e)},Xi.prototype=Object.create(qi.prototype),Xi.prototype.handleNode=function(e){var t=this,n=this.element;return n?t=this.handleChild(e):(n=this.element=this.createElement(e),this.context.addElement(n)),t},Zi.prototype=Object.create(Xi.prototype),Zi.prototype.addReference=function(e){this.context.addReference(e)},Zi.prototype.handleText=function(e){if(!Gi(this.element).bodyProperty)throw Hi("unexpected body text <"+e+">");qi.prototype.handleText.call(this,e)},Zi.prototype.handleEnd=function(){var e=this.body,t=this.element,n=Gi(t).bodyProperty;n&&void 0!==e&&(e=li(n.type,e),t.set(n.name,e))},Zi.prototype.createElement=function(e){var t,n=e.attributes,i=this.type,r=Gi(i),o=this.context,a=new i({}),s=this.model;return g(n,(function(e,n){var i=r.propertiesByName[n];i&&i.isReference?i.isMany?g(e.split(" "),(function(e){o.addReference({element:a,property:i.ns.name,id:e})})):o.addReference({element:a,property:i.ns.name,id:e}):(i?e=li(i.type,e):"xmlns"!==n&&(t=di(n,r.ns.prefix),s.getPackage(t.prefix)&&o.addWarning({message:"unknown attribute <"+n+">",element:a,property:n,value:e})),a.set(n,e))})),a},Zi.prototype.getPropertyForNode=function(e){var t,n,i=di(e.name),r=this.type,o=this.model,a=Gi(r),s=i.name,c=a.propertiesByName[s];if(c&&!c.isAttr)return zi(c)&&(t=e.attributes[Ii])?(t=function(e,t){var n=di(e);return function(e,t){var n=e.name,i=e.localName,r=t.xml&&t.xml.typePrefix;return r&&0===i.indexOf(r)?e.prefix+":"+i.slice(r.length):n}(n,t.getPackage(n.prefix))}(t,o),F({},c,{effectiveType:Gi(n=o.getType(t)).name})):c;var p=o.getPackage(i.prefix);if(p){if(t=$i(i,p),n=o.getType(t),c=m(a.properties,(function(e){return!e.isVirtual&&!e.isReference&&!e.isAttribute&&n.hasType(e.type)})))return F({},c,{effectiveType:Gi(n).name})}else if(c=m(a.properties,(function(e){return!e.isReference&&!e.isAttribute&&"Element"===e.type})))return c;throw Hi("unrecognized element <"+i.name+">")},Zi.prototype.toString=function(){return"ElementDescriptor["+Gi(this.type).name+"]"},Zi.prototype.valueHandler=function(e,t){return new Yi(e,t)},Zi.prototype.referenceHandler=function(e){return new Ki(e,this.context)},Zi.prototype.handler=function(e){return"Element"===e?new Ji(this.model,e,this.context):new Zi(this.model,e,this.context)},Zi.prototype.handleChild=function(e){var t,n,i,r;if(t=this.getPropertyForNode(e),i=this.element,hi(n=t.effectiveType||t.type))return this.valueHandler(t,i);var o=(r=t.isReference?this.referenceHandler(t).handleNode(e):this.handler(n).handleNode(e)).element;return void 0!==o&&(t.isMany?i.get(t.name).push(o):i.set(t.name,o),t.isReference?(F(o,{element:i}),this.context.addReference(o)):o.$parent=i),r},Qi.prototype=Object.create(Zi.prototype),Qi.prototype.createElement=function(e){var t=e.name,n=di(t),i=this.model,r=this.type,o=i.getPackage(n.prefix),a=o&&$i(n,o)||t;if(!r.hasType(a))throw Hi("unexpected element <"+e.originalName+">");return Zi.prototype.createElement.call(this,e)},Ji.prototype=Object.create(Xi.prototype),Ji.prototype.createElement=function(e){var t=e.name,n=di(t).prefix,i=e.ns[n+"$uri"],r=e.attributes;return this.model.createAny(t,i,r)},Ji.prototype.handleChild=function(e){var t=new Ji(this.model,"Element",this.context).handleNode(e),n=this.element,i=t.element;return void 0!==i&&((n.$children=n.$children||[]).push(i),i.$parent=n),t},Ji.prototype.handleEnd=function(){this.body&&(this.element.$body=this.body)},er.prototype.fromXML=function(e,t,n){var i=t.rootHandler;t instanceof Zi?(i=t,t={}):"string"==typeof t?(i=this.handler(t),t={}):"string"==typeof i&&(i=this.handler(i));var r=this.model,o=this.lax,a=new Vi(F({},t,{rootHandler:i})),s=new Oi({proxy:!0}),c=function(){var e=[];return Object.defineProperty(e,"peek",{value:function(){return this[this.length-1]}}),e}();function p(e,t,n){var i=t(),r=i.line,o=i.column,s=i.data;"<"===s.charAt(0)&&-1!==s.indexOf(" ")&&(s=s.slice(0,s.indexOf(" "))+">");var c="unparsable content "+(s?s+" ":"")+"detected\n\tline: "+r+"\n\tcolumn: "+o+"\n\tnested error: "+e.message;if(n)return a.addWarning({message:c,error:e}),!0;throw Hi(c)}function l(e,t){return p(e,t,!0)}i.context=a,c.push(i);var u=/^<\?xml /i,h=/ encoding="([^"]+)"/i,d=/^utf-8$/i;function f(e,t){try{c.peek().handleText(e)}catch(e){l(e,t)}}var m=r.getPackages().reduce((function(e,t){return e[t.uri]=t.prefix,e}),{"http://www.w3.org/XML/1998/namespace":"xml"});return s.ns(m).on("openTag",(function(e,t,n,i){var r=e.attrs||{},a=Object.keys(r).reduce((function(e,n){var i=t(r[n]);return e[n]=i,e}),{});!function(e,t){var n=c.peek();try{c.push(n.handleNode(e))}catch(e){p(e,t,o)&&c.push(new Ui)}}({name:e.name,originalName:e.originalName,attributes:a,ns:e.ns},i)})).on("question",(function(e){if(u.test(e)){var t=h.exec(e),n=t&&t[1];n&&!d.test(n)&&a.addWarning({message:"unsupported document encoding <"+n+">, falling back to UTF-8"})}})).on("closeTag",(function(){c.pop().handleEnd()})).on("cdata",f).on("text",(function(e,t,n){!function(e,t){e.trim()&&f(e,t)}(t(e),n)})).on("error",p).on("warn",l),new Promise((function(t,n){var r;try{s.parse(e),function(){var e,t,n=a.elementsById,i=a.references;for(e=0;t=i[e];e++){var r=t.element,o=n[t.id],s=Gi(r).propertiesByName[t.property];if(o||a.addWarning({message:"unresolved reference <"+t.id+">",element:t.element,property:t.property,value:t.id}),s.isMany){var c=r.get(s.name),p=c.indexOf(t);-1===p&&(p=c.length),o?c[p]=o:c.splice(p,1)}else r.set(s.name,o)}}()}catch(e){r=e}var o=i.element;r||o||(r=Hi("failed to parse document as <"+i.type.$descriptor.name+">"));var c=a.warnings,p=a.references,l=a.elementsById;return r?(r.warnings=c,n(r)):t({rootElement:o,elementsById:l,references:p,warnings:c})}))},er.prototype.handler=function(e){return new Qi(this.model,e)};var tr=/<|>|'|"|&|\n\r|\n/g,nr=/<|>|&/g;function ir(e){var t={},n={},i={},r=[],o=[];this.byUri=function(t){return n[t]||e&&e.byUri(t)},this.add=function(e,t){n[e.uri]=e,t?r.push(e):o.push(e),this.mapPrefix(e.prefix,e.uri)},this.uriByPrefix=function(e){return t[e||"xmlns"]},this.mapPrefix=function(e,n){t[e||"xmlns"]=n},this.getNSKey=function(e){return void 0!==e.prefix?e.uri+"|"+e.prefix:e.uri},this.logUsed=function(t){var n=t.uri,r=this.getNSKey(t);i[r]=this.byUri(n),e&&e.logUsed(t)},this.getUsed=function(e){var t=this;return[].concat(r,o).filter((function(e){var n=t.getNSKey(e);return i[n]}))}}function rr(e,t){return ji(t)?(n=e).charAt(0).toLowerCase()+n.slice(1):e;var n}function or(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})}function ar(e){return d(e)?e:(e.prefix?e.prefix+":":"")+e.localName}var sr={"\n":"#10","\n\r":"#10",'"':"#34","'":"#39","<":"#60",">":"#62","&":"#38"},cr={"<":"lt",">":"gt","&":"amp"};function pr(e,t,n){return(e=d(e)?e:""+e).replace(t,(function(e){return"&"+n[e]+";"}))}function lr(e){this.tagName=e}function ur(){}function hr(e){this.tagName=e}function dr(e,t){this.body=[],this.attrs=[],this.parent=e,this.propertyDescriptor=t}function fr(e,t){dr.call(this,e,t)}function mr(){this.value="",this.write=function(e){this.value+=e}}function vr(e,t){var n=[""];this.append=function(t){return e.write(t),this},this.appendNewLine=function(){return t&&e.write("\n"),this},this.appendIndent=function(){return t&&e.write(n.join(" ")),this},this.indent=function(){return n.push(""),this},this.unindent=function(){return n.pop(),this}}function yr(e){return e=F({format:!1,preamble:!0},e||{}),{toXML:function(t,n){var i=n||new mr,r=new vr(i,e.format);if(e.preamble&&r.append('\n'),(new dr).build(t).serializeTo(r),!n)return i.value}}}function gr(e,t){bi.call(this,e,t)}lr.prototype.build=function(e){return this.element=e,this},lr.prototype.serializeTo=function(e){e.appendIndent().append("<"+this.tagName+">"+this.element.id+"").appendNewLine()},ur.prototype.serializeValue=ur.prototype.serializeTo=function(e){e.append(this.escape?pr(this.value,nr,cr):this.value)},ur.prototype.build=function(e,t){return this.value=t,"String"===e.type&&-1!==t.search(nr)&&(this.escape=!0),this},or(hr,ur),hr.prototype.serializeTo=function(e){e.appendIndent().append("<"+this.tagName+">"),this.serializeValue(e),e.append("").appendNewLine()},dr.prototype.build=function(e){this.element=e;var t,n,i=e.$descriptor,r=this.propertyDescriptor,o=i.isGeneric;return t=o?this.parseGeneric(e):this.parseNsAttributes(e),this.ns=r?this.nsPropertyTagName(r):this.nsTagName(i),this.tagName=this.addTagName(this.ns),o||(n=function(e){return y(e.$descriptor.properties,(function(t){var n=t.name;if(t.isVirtual)return!1;if(!f(e,n))return!1;var i=e[n];return i!==t.default&&null!==i&&(!t.isMany||i.length)}))}(e),this.parseAttributes(y(n,(function(e){return e.isAttr}))),this.parseContainments(function(e){return y(e,(function(e){return!e.isAttr}))}(n))),this.parseGenericAttributes(e,t),this},dr.prototype.nsTagName=function(e){return function(e,t){return t.isGeneric?F({localName:t.ns.localName},e):F({localName:rr(t.ns.localName,t.$pkg)},e)}(this.logNamespaceUsed(e.ns),e)},dr.prototype.nsPropertyTagName=function(e){return function(e,t){return F({localName:t.ns.localName},e)}(this.logNamespaceUsed(e.ns),e)},dr.prototype.isLocalNs=function(e){return e.uri===this.ns.uri},dr.prototype.nsAttributeName=function(e){var t;if(t=d(e)?di(e):e.ns,e.inherited)return{localName:t.localName};var n=this.logNamespaceUsed(t);return this.getNamespaces().logUsed(n),this.isLocalNs(n)?{localName:t.localName}:F({localName:t.localName},n)},dr.prototype.parseGeneric=function(e){var t=this,n=this.body,i=[];return g(e,(function(r,o){"$body"===o?n.push((new ur).build({type:"String"},r)):"$children"===o?g(r,(function(e){n.push(new dr(t).build(e))})):0!==o.indexOf("$")&&t.parseNsAttribute(e,o,r)&&i.push({name:o,value:r})})),i},dr.prototype.parseNsAttribute=function(e,t,n){var i,r=e.$model,o=di(t);if("xmlns"===o.prefix&&(i={prefix:o.localName,uri:n}),o.prefix||"xmlns"!==o.localName||(i={uri:n}),!i)return{name:t,value:n};if(r&&r.getPackage(n))this.logNamespace(i,!0,!0);else{var a=this.logNamespaceUsed(i,!0);this.getNamespaces().logUsed(a)}},dr.prototype.parseNsAttributes=function(e,t){var n=this,i=e.$attrs,r=[];return g(i,(function(t,i){var o=n.parseNsAttribute(e,i,t);o&&r.push(o)})),r},dr.prototype.parseGenericAttributes=function(e,t){var n=this;g(t,(function(t){if(t.name!==Ii)try{n.addAttribute(n.nsAttributeName(t.name),t.value)}catch(n){console.warn("missing namespace information for ",t.name,"=",t.value,"on",e,n)}}))},dr.prototype.parseContainments=function(e){var t=this,n=this.body,i=this.element;g(e,(function(e){var r=i.get(e.name),o=e.isReference;if(e.isMany||(r=[r]),e.isBody)n.push((new ur).build(e,r[0]));else if(hi(e.type))g(r,(function(i){n.push(new hr(t.addTagName(t.nsPropertyTagName(e))).build(e,i))}));else if(o)g(r,(function(i){n.push(new lr(t.addTagName(t.nsPropertyTagName(e))).build(i))}));else{var a=zi(e),s=function(e){return"property"===Fi(e)}(e);g(r,(function(i){var r;r=a?new fr(t,e):s?new dr(t,e):new dr(t),n.push(r.build(i))}))}}))},dr.prototype.getNamespaces=function(e){var t,n=this.namespaces,i=this.parent;return n||(t=i&&i.getNamespaces(),e||!t?this.namespaces=n=new ir(t):n=t),n},dr.prototype.logNamespace=function(e,t,n){var i=this.getNamespaces(n),r=e.uri,o=e.prefix;return i.byUri(r)&&!n||i.add(e,t),i.mapPrefix(o,r),e},dr.prototype.logNamespaceUsed=function(e,t){var n,i,r,o=this.element.$model,a=this.getNamespaces(t),s=e.prefix,c=e.uri;if(!s&&!c)return{localName:e.localName};if(r=Li[s]||o&&(o.getPackage(s)||{}).uri,!(c=c||r||a.uriByPrefix(s)))throw new Error("no namespace uri given for prefix <"+s+">");if(!(e=a.byUri(c))){for(n=s,i=1;a.uriByPrefix(n);)n=s+"_"+i++;e=this.logNamespace({prefix:n,uri:c},r===c)}return s&&a.mapPrefix(s,c),e},dr.prototype.parseAttributes=function(e){var t=this,n=this.element;g(e,(function(e){var i=n.get(e.name);if(e.isReference)if(e.isMany){var r=[];g(i,(function(e){r.push(e.id)})),i=r.join(" ")}else i=i.id;t.addAttribute(t.nsAttributeName(e),i)}))},dr.prototype.addTagName=function(e){var t=this.logNamespaceUsed(e);return this.getNamespaces().logUsed(t),ar(e)},dr.prototype.addAttribute=function(e,t){var n=this.attrs;d(t)&&(t=pr(t,tr,sr));var i=v(n,(function(t){return t.name.localName===e.localName&&t.name.uri===e.uri&&t.name.prefix===e.prefix})),r={name:e,value:t};-1!==i?n.splice(i,1,r):n.push(r)},dr.prototype.serializeAttributes=function(e){var t=this.attrs,n=this.namespaces;n&&(t=function(e){return e.getUsed().filter((function(e){return"xml"!==e.prefix})).map((function(e){return{name:"xmlns"+(e.prefix?":"+e.prefix:""),value:e.uri}}))}(n).concat(t)),g(t,(function(t){e.append(" ").append(ar(t.name)).append('="').append(t.value).append('"')}))},dr.prototype.serializeTo=function(e){var t=this.body[0],n=t&&t.constructor!==ur;e.appendIndent().append("<"+this.tagName),this.serializeAttributes(e),e.append(t?">":" />"),t&&(n&&e.appendNewLine().indent(),g(this.body,(function(t){t.serializeTo(e)})),n&&e.unindent().appendIndent(),e.append("")),e.appendNewLine()},or(fr,dr),fr.prototype.parseNsAttributes=function(e){var t=dr.prototype.parseNsAttributes.call(this,e),n=e.$descriptor;if(n.name===this.propertyDescriptor.type)return t;var i=this.typeNs=this.nsTagName(n);this.getNamespaces().logUsed(this.typeNs);var r=e.$model.getPackage(i.uri),o=r.xml&&r.xml.typePrefix||"";return this.addAttribute(this.nsAttributeName(Ii),(i.prefix?i.prefix+":":"")+o+n.ns.localName),t},fr.prototype.isLocalNs=function(e){return e.uri===(this.typeNs||this.ns).uri},gr.prototype=Object.create(bi.prototype),gr.prototype.fromXML=function(e,t,n){d(t)||(n=t,t="bpmn:Definitions");var i=new er(F({model:this,lax:!0},n)),r=i.handler(t);return i.fromXML(e,r)},gr.prototype.toXML=function(e,t){var n=new yr(t);return new Promise((function(t,i){try{return t({xml:n.toXML(e)})}catch(e){return i(e)}}))};var br={bpmn:{name:"BPMN20",uri:"http://www.omg.org/spec/BPMN/20100524/MODEL",prefix:"bpmn",associations:[],types:[{name:"Interface",superClass:["RootElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"operations",type:"Operation",isMany:!0},{name:"implementationRef",isAttr:!0,type:"String"}]},{name:"Operation",superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"inMessageRef",type:"Message",isReference:!0},{name:"outMessageRef",type:"Message",isReference:!0},{name:"errorRef",type:"Error",isMany:!0,isReference:!0},{name:"implementationRef",isAttr:!0,type:"String"}]},{name:"EndPoint",superClass:["RootElement"]},{name:"Auditing",superClass:["BaseElement"]},{name:"GlobalTask",superClass:["CallableElement"],properties:[{name:"resources",type:"ResourceRole",isMany:!0}]},{name:"Monitoring",superClass:["BaseElement"]},{name:"Performer",superClass:["ResourceRole"]},{name:"Process",superClass:["FlowElementsContainer","CallableElement"],properties:[{name:"processType",type:"ProcessType",isAttr:!0},{name:"isClosed",isAttr:!0,type:"Boolean"},{name:"auditing",type:"Auditing"},{name:"monitoring",type:"Monitoring"},{name:"properties",type:"Property",isMany:!0},{name:"laneSets",isMany:!0,replaces:"FlowElementsContainer#laneSets",type:"LaneSet"},{name:"flowElements",isMany:!0,replaces:"FlowElementsContainer#flowElements",type:"FlowElement"},{name:"artifacts",type:"Artifact",isMany:!0},{name:"resources",type:"ResourceRole",isMany:!0},{name:"correlationSubscriptions",type:"CorrelationSubscription",isMany:!0},{name:"supports",type:"Process",isMany:!0,isReference:!0},{name:"definitionalCollaborationRef",type:"Collaboration",isAttr:!0,isReference:!0},{name:"isExecutable",isAttr:!0,type:"Boolean"}]},{name:"LaneSet",superClass:["BaseElement"],properties:[{name:"lanes",type:"Lane",isMany:!0},{name:"name",isAttr:!0,type:"String"}]},{name:"Lane",superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"partitionElementRef",type:"BaseElement",isAttr:!0,isReference:!0},{name:"partitionElement",type:"BaseElement"},{name:"flowNodeRef",type:"FlowNode",isMany:!0,isReference:!0},{name:"childLaneSet",type:"LaneSet",xml:{serialize:"xsi:type"}}]},{name:"GlobalManualTask",superClass:["GlobalTask"]},{name:"ManualTask",superClass:["Task"]},{name:"UserTask",superClass:["Task"],properties:[{name:"renderings",type:"Rendering",isMany:!0},{name:"implementation",isAttr:!0,type:"String"}]},{name:"Rendering",superClass:["BaseElement"]},{name:"HumanPerformer",superClass:["Performer"]},{name:"PotentialOwner",superClass:["HumanPerformer"]},{name:"GlobalUserTask",superClass:["GlobalTask"],properties:[{name:"implementation",isAttr:!0,type:"String"},{name:"renderings",type:"Rendering",isMany:!0}]},{name:"Gateway",isAbstract:!0,superClass:["FlowNode"],properties:[{name:"gatewayDirection",type:"GatewayDirection",default:"Unspecified",isAttr:!0}]},{name:"EventBasedGateway",superClass:["Gateway"],properties:[{name:"instantiate",default:!1,isAttr:!0,type:"Boolean"},{name:"eventGatewayType",type:"EventBasedGatewayType",isAttr:!0,default:"Exclusive"}]},{name:"ComplexGateway",superClass:["Gateway"],properties:[{name:"activationCondition",type:"Expression",xml:{serialize:"xsi:type"}},{name:"default",type:"SequenceFlow",isAttr:!0,isReference:!0}]},{name:"ExclusiveGateway",superClass:["Gateway"],properties:[{name:"default",type:"SequenceFlow",isAttr:!0,isReference:!0}]},{name:"InclusiveGateway",superClass:["Gateway"],properties:[{name:"default",type:"SequenceFlow",isAttr:!0,isReference:!0}]},{name:"ParallelGateway",superClass:["Gateway"]},{name:"RootElement",isAbstract:!0,superClass:["BaseElement"]},{name:"Relationship",superClass:["BaseElement"],properties:[{name:"type",isAttr:!0,type:"String"},{name:"direction",type:"RelationshipDirection",isAttr:!0},{name:"source",isMany:!0,isReference:!0,type:"Element"},{name:"target",isMany:!0,isReference:!0,type:"Element"}]},{name:"BaseElement",isAbstract:!0,properties:[{name:"id",isAttr:!0,type:"String",isId:!0},{name:"documentation",type:"Documentation",isMany:!0},{name:"extensionDefinitions",type:"ExtensionDefinition",isMany:!0,isReference:!0},{name:"extensionElements",type:"ExtensionElements"}]},{name:"Extension",properties:[{name:"mustUnderstand",default:!1,isAttr:!0,type:"Boolean"},{name:"definition",type:"ExtensionDefinition",isAttr:!0,isReference:!0}]},{name:"ExtensionDefinition",properties:[{name:"name",isAttr:!0,type:"String"},{name:"extensionAttributeDefinitions",type:"ExtensionAttributeDefinition",isMany:!0}]},{name:"ExtensionAttributeDefinition",properties:[{name:"name",isAttr:!0,type:"String"},{name:"type",isAttr:!0,type:"String"},{name:"isReference",default:!1,isAttr:!0,type:"Boolean"},{name:"extensionDefinition",type:"ExtensionDefinition",isAttr:!0,isReference:!0}]},{name:"ExtensionElements",properties:[{name:"valueRef",isAttr:!0,isReference:!0,type:"Element"},{name:"values",type:"Element",isMany:!0},{name:"extensionAttributeDefinition",type:"ExtensionAttributeDefinition",isAttr:!0,isReference:!0}]},{name:"Documentation",superClass:["BaseElement"],properties:[{name:"text",type:"String",isBody:!0},{name:"textFormat",default:"text/plain",isAttr:!0,type:"String"}]},{name:"Event",isAbstract:!0,superClass:["FlowNode","InteractionNode"],properties:[{name:"properties",type:"Property",isMany:!0}]},{name:"IntermediateCatchEvent",superClass:["CatchEvent"]},{name:"IntermediateThrowEvent",superClass:["ThrowEvent"]},{name:"EndEvent",superClass:["ThrowEvent"]},{name:"StartEvent",superClass:["CatchEvent"],properties:[{name:"isInterrupting",default:!0,isAttr:!0,type:"Boolean"}]},{name:"ThrowEvent",isAbstract:!0,superClass:["Event"],properties:[{name:"dataInputs",type:"DataInput",isMany:!0},{name:"dataInputAssociations",type:"DataInputAssociation",isMany:!0},{name:"inputSet",type:"InputSet"},{name:"eventDefinitions",type:"EventDefinition",isMany:!0},{name:"eventDefinitionRef",type:"EventDefinition",isMany:!0,isReference:!0}]},{name:"CatchEvent",isAbstract:!0,superClass:["Event"],properties:[{name:"parallelMultiple",isAttr:!0,type:"Boolean",default:!1},{name:"dataOutputs",type:"DataOutput",isMany:!0},{name:"dataOutputAssociations",type:"DataOutputAssociation",isMany:!0},{name:"outputSet",type:"OutputSet"},{name:"eventDefinitions",type:"EventDefinition",isMany:!0},{name:"eventDefinitionRef",type:"EventDefinition",isMany:!0,isReference:!0}]},{name:"BoundaryEvent",superClass:["CatchEvent"],properties:[{name:"cancelActivity",default:!0,isAttr:!0,type:"Boolean"},{name:"attachedToRef",type:"Activity",isAttr:!0,isReference:!0}]},{name:"EventDefinition",isAbstract:!0,superClass:["RootElement"]},{name:"CancelEventDefinition",superClass:["EventDefinition"]},{name:"ErrorEventDefinition",superClass:["EventDefinition"],properties:[{name:"errorRef",type:"Error",isAttr:!0,isReference:!0}]},{name:"TerminateEventDefinition",superClass:["EventDefinition"]},{name:"EscalationEventDefinition",superClass:["EventDefinition"],properties:[{name:"escalationRef",type:"Escalation",isAttr:!0,isReference:!0}]},{name:"Escalation",properties:[{name:"structureRef",type:"ItemDefinition",isAttr:!0,isReference:!0},{name:"name",isAttr:!0,type:"String"},{name:"escalationCode",isAttr:!0,type:"String"}],superClass:["RootElement"]},{name:"CompensateEventDefinition",superClass:["EventDefinition"],properties:[{name:"waitForCompletion",isAttr:!0,type:"Boolean",default:!0},{name:"activityRef",type:"Activity",isAttr:!0,isReference:!0}]},{name:"TimerEventDefinition",superClass:["EventDefinition"],properties:[{name:"timeDate",type:"Expression",xml:{serialize:"xsi:type"}},{name:"timeCycle",type:"Expression",xml:{serialize:"xsi:type"}},{name:"timeDuration",type:"Expression",xml:{serialize:"xsi:type"}}]},{name:"LinkEventDefinition",superClass:["EventDefinition"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"target",type:"LinkEventDefinition",isAttr:!0,isReference:!0},{name:"source",type:"LinkEventDefinition",isMany:!0,isReference:!0}]},{name:"MessageEventDefinition",superClass:["EventDefinition"],properties:[{name:"messageRef",type:"Message",isAttr:!0,isReference:!0},{name:"operationRef",type:"Operation",isAttr:!0,isReference:!0}]},{name:"ConditionalEventDefinition",superClass:["EventDefinition"],properties:[{name:"condition",type:"Expression",xml:{serialize:"xsi:type"}}]},{name:"SignalEventDefinition",superClass:["EventDefinition"],properties:[{name:"signalRef",type:"Signal",isAttr:!0,isReference:!0}]},{name:"Signal",superClass:["RootElement"],properties:[{name:"structureRef",type:"ItemDefinition",isAttr:!0,isReference:!0},{name:"name",isAttr:!0,type:"String"}]},{name:"ImplicitThrowEvent",superClass:["ThrowEvent"]},{name:"DataState",superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"}]},{name:"ItemAwareElement",superClass:["BaseElement"],properties:[{name:"itemSubjectRef",type:"ItemDefinition",isAttr:!0,isReference:!0},{name:"dataState",type:"DataState"}]},{name:"DataAssociation",superClass:["BaseElement"],properties:[{name:"sourceRef",type:"ItemAwareElement",isMany:!0,isReference:!0},{name:"targetRef",type:"ItemAwareElement",isReference:!0},{name:"transformation",type:"FormalExpression",xml:{serialize:"property"}},{name:"assignment",type:"Assignment",isMany:!0}]},{name:"DataInput",superClass:["ItemAwareElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"isCollection",default:!1,isAttr:!0,type:"Boolean"},{name:"inputSetRef",type:"InputSet",isMany:!0,isVirtual:!0,isReference:!0},{name:"inputSetWithOptional",type:"InputSet",isMany:!0,isVirtual:!0,isReference:!0},{name:"inputSetWithWhileExecuting",type:"InputSet",isMany:!0,isVirtual:!0,isReference:!0}]},{name:"DataOutput",superClass:["ItemAwareElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"isCollection",default:!1,isAttr:!0,type:"Boolean"},{name:"outputSetRef",type:"OutputSet",isMany:!0,isVirtual:!0,isReference:!0},{name:"outputSetWithOptional",type:"OutputSet",isMany:!0,isVirtual:!0,isReference:!0},{name:"outputSetWithWhileExecuting",type:"OutputSet",isMany:!0,isVirtual:!0,isReference:!0}]},{name:"InputSet",superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"dataInputRefs",type:"DataInput",isMany:!0,isReference:!0},{name:"optionalInputRefs",type:"DataInput",isMany:!0,isReference:!0},{name:"whileExecutingInputRefs",type:"DataInput",isMany:!0,isReference:!0},{name:"outputSetRefs",type:"OutputSet",isMany:!0,isReference:!0}]},{name:"OutputSet",superClass:["BaseElement"],properties:[{name:"dataOutputRefs",type:"DataOutput",isMany:!0,isReference:!0},{name:"name",isAttr:!0,type:"String"},{name:"inputSetRefs",type:"InputSet",isMany:!0,isReference:!0},{name:"optionalOutputRefs",type:"DataOutput",isMany:!0,isReference:!0},{name:"whileExecutingOutputRefs",type:"DataOutput",isMany:!0,isReference:!0}]},{name:"Property",superClass:["ItemAwareElement"],properties:[{name:"name",isAttr:!0,type:"String"}]},{name:"DataInputAssociation",superClass:["DataAssociation"]},{name:"DataOutputAssociation",superClass:["DataAssociation"]},{name:"InputOutputSpecification",superClass:["BaseElement"],properties:[{name:"dataInputs",type:"DataInput",isMany:!0},{name:"dataOutputs",type:"DataOutput",isMany:!0},{name:"inputSets",type:"InputSet",isMany:!0},{name:"outputSets",type:"OutputSet",isMany:!0}]},{name:"DataObject",superClass:["FlowElement","ItemAwareElement"],properties:[{name:"isCollection",default:!1,isAttr:!0,type:"Boolean"}]},{name:"InputOutputBinding",properties:[{name:"inputDataRef",type:"InputSet",isAttr:!0,isReference:!0},{name:"outputDataRef",type:"OutputSet",isAttr:!0,isReference:!0},{name:"operationRef",type:"Operation",isAttr:!0,isReference:!0}]},{name:"Assignment",superClass:["BaseElement"],properties:[{name:"from",type:"Expression",xml:{serialize:"xsi:type"}},{name:"to",type:"Expression",xml:{serialize:"xsi:type"}}]},{name:"DataStore",superClass:["RootElement","ItemAwareElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"capacity",isAttr:!0,type:"Integer"},{name:"isUnlimited",default:!0,isAttr:!0,type:"Boolean"}]},{name:"DataStoreReference",superClass:["ItemAwareElement","FlowElement"],properties:[{name:"dataStoreRef",type:"DataStore",isAttr:!0,isReference:!0}]},{name:"DataObjectReference",superClass:["ItemAwareElement","FlowElement"],properties:[{name:"dataObjectRef",type:"DataObject",isAttr:!0,isReference:!0}]},{name:"ConversationLink",superClass:["BaseElement"],properties:[{name:"sourceRef",type:"InteractionNode",isAttr:!0,isReference:!0},{name:"targetRef",type:"InteractionNode",isAttr:!0,isReference:!0},{name:"name",isAttr:!0,type:"String"}]},{name:"ConversationAssociation",superClass:["BaseElement"],properties:[{name:"innerConversationNodeRef",type:"ConversationNode",isAttr:!0,isReference:!0},{name:"outerConversationNodeRef",type:"ConversationNode",isAttr:!0,isReference:!0}]},{name:"CallConversation",superClass:["ConversationNode"],properties:[{name:"calledCollaborationRef",type:"Collaboration",isAttr:!0,isReference:!0},{name:"participantAssociations",type:"ParticipantAssociation",isMany:!0}]},{name:"Conversation",superClass:["ConversationNode"]},{name:"SubConversation",superClass:["ConversationNode"],properties:[{name:"conversationNodes",type:"ConversationNode",isMany:!0}]},{name:"ConversationNode",isAbstract:!0,superClass:["InteractionNode","BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"participantRef",type:"Participant",isMany:!0,isReference:!0},{name:"messageFlowRefs",type:"MessageFlow",isMany:!0,isReference:!0},{name:"correlationKeys",type:"CorrelationKey",isMany:!0}]},{name:"GlobalConversation",superClass:["Collaboration"]},{name:"PartnerEntity",superClass:["RootElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"participantRef",type:"Participant",isMany:!0,isReference:!0}]},{name:"PartnerRole",superClass:["RootElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"participantRef",type:"Participant",isMany:!0,isReference:!0}]},{name:"CorrelationProperty",superClass:["RootElement"],properties:[{name:"correlationPropertyRetrievalExpression",type:"CorrelationPropertyRetrievalExpression",isMany:!0},{name:"name",isAttr:!0,type:"String"},{name:"type",type:"ItemDefinition",isAttr:!0,isReference:!0}]},{name:"Error",superClass:["RootElement"],properties:[{name:"structureRef",type:"ItemDefinition",isAttr:!0,isReference:!0},{name:"name",isAttr:!0,type:"String"},{name:"errorCode",isAttr:!0,type:"String"}]},{name:"CorrelationKey",superClass:["BaseElement"],properties:[{name:"correlationPropertyRef",type:"CorrelationProperty",isMany:!0,isReference:!0},{name:"name",isAttr:!0,type:"String"}]},{name:"Expression",superClass:["BaseElement"],isAbstract:!1,properties:[{name:"body",isBody:!0,type:"String"}]},{name:"FormalExpression",superClass:["Expression"],properties:[{name:"language",isAttr:!0,type:"String"},{name:"evaluatesToTypeRef",type:"ItemDefinition",isAttr:!0,isReference:!0}]},{name:"Message",superClass:["RootElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"itemRef",type:"ItemDefinition",isAttr:!0,isReference:!0}]},{name:"ItemDefinition",superClass:["RootElement"],properties:[{name:"itemKind",type:"ItemKind",isAttr:!0},{name:"structureRef",isAttr:!0,type:"String"},{name:"isCollection",default:!1,isAttr:!0,type:"Boolean"},{name:"import",type:"Import",isAttr:!0,isReference:!0}]},{name:"FlowElement",isAbstract:!0,superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"auditing",type:"Auditing"},{name:"monitoring",type:"Monitoring"},{name:"categoryValueRef",type:"CategoryValue",isMany:!0,isReference:!0}]},{name:"SequenceFlow",superClass:["FlowElement"],properties:[{name:"isImmediate",isAttr:!0,type:"Boolean"},{name:"conditionExpression",type:"Expression",xml:{serialize:"xsi:type"}},{name:"sourceRef",type:"FlowNode",isAttr:!0,isReference:!0},{name:"targetRef",type:"FlowNode",isAttr:!0,isReference:!0}]},{name:"FlowElementsContainer",isAbstract:!0,superClass:["BaseElement"],properties:[{name:"laneSets",type:"LaneSet",isMany:!0},{name:"flowElements",type:"FlowElement",isMany:!0}]},{name:"CallableElement",isAbstract:!0,superClass:["RootElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"ioSpecification",type:"InputOutputSpecification",xml:{serialize:"property"}},{name:"supportedInterfaceRef",type:"Interface",isMany:!0,isReference:!0},{name:"ioBinding",type:"InputOutputBinding",isMany:!0,xml:{serialize:"property"}}]},{name:"FlowNode",isAbstract:!0,superClass:["FlowElement"],properties:[{name:"incoming",type:"SequenceFlow",isMany:!0,isReference:!0},{name:"outgoing",type:"SequenceFlow",isMany:!0,isReference:!0},{name:"lanes",type:"Lane",isMany:!0,isVirtual:!0,isReference:!0}]},{name:"CorrelationPropertyRetrievalExpression",superClass:["BaseElement"],properties:[{name:"messagePath",type:"FormalExpression"},{name:"messageRef",type:"Message",isAttr:!0,isReference:!0}]},{name:"CorrelationPropertyBinding",superClass:["BaseElement"],properties:[{name:"dataPath",type:"FormalExpression"},{name:"correlationPropertyRef",type:"CorrelationProperty",isAttr:!0,isReference:!0}]},{name:"Resource",superClass:["RootElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"resourceParameters",type:"ResourceParameter",isMany:!0}]},{name:"ResourceParameter",superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"isRequired",isAttr:!0,type:"Boolean"},{name:"type",type:"ItemDefinition",isAttr:!0,isReference:!0}]},{name:"CorrelationSubscription",superClass:["BaseElement"],properties:[{name:"correlationKeyRef",type:"CorrelationKey",isAttr:!0,isReference:!0},{name:"correlationPropertyBinding",type:"CorrelationPropertyBinding",isMany:!0}]},{name:"MessageFlow",superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"sourceRef",type:"InteractionNode",isAttr:!0,isReference:!0},{name:"targetRef",type:"InteractionNode",isAttr:!0,isReference:!0},{name:"messageRef",type:"Message",isAttr:!0,isReference:!0}]},{name:"MessageFlowAssociation",superClass:["BaseElement"],properties:[{name:"innerMessageFlowRef",type:"MessageFlow",isAttr:!0,isReference:!0},{name:"outerMessageFlowRef",type:"MessageFlow",isAttr:!0,isReference:!0}]},{name:"InteractionNode",isAbstract:!0,properties:[{name:"incomingConversationLinks",type:"ConversationLink",isMany:!0,isVirtual:!0,isReference:!0},{name:"outgoingConversationLinks",type:"ConversationLink",isMany:!0,isVirtual:!0,isReference:!0}]},{name:"Participant",superClass:["InteractionNode","BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"interfaceRef",type:"Interface",isMany:!0,isReference:!0},{name:"participantMultiplicity",type:"ParticipantMultiplicity"},{name:"endPointRefs",type:"EndPoint",isMany:!0,isReference:!0},{name:"processRef",type:"Process",isAttr:!0,isReference:!0}]},{name:"ParticipantAssociation",superClass:["BaseElement"],properties:[{name:"innerParticipantRef",type:"Participant",isAttr:!0,isReference:!0},{name:"outerParticipantRef",type:"Participant",isAttr:!0,isReference:!0}]},{name:"ParticipantMultiplicity",properties:[{name:"minimum",default:0,isAttr:!0,type:"Integer"},{name:"maximum",default:1,isAttr:!0,type:"Integer"}],superClass:["BaseElement"]},{name:"Collaboration",superClass:["RootElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"isClosed",isAttr:!0,type:"Boolean"},{name:"participants",type:"Participant",isMany:!0},{name:"messageFlows",type:"MessageFlow",isMany:!0},{name:"artifacts",type:"Artifact",isMany:!0},{name:"conversations",type:"ConversationNode",isMany:!0},{name:"conversationAssociations",type:"ConversationAssociation"},{name:"participantAssociations",type:"ParticipantAssociation",isMany:!0},{name:"messageFlowAssociations",type:"MessageFlowAssociation",isMany:!0},{name:"correlationKeys",type:"CorrelationKey",isMany:!0},{name:"choreographyRef",type:"Choreography",isMany:!0,isReference:!0},{name:"conversationLinks",type:"ConversationLink",isMany:!0}]},{name:"ChoreographyActivity",isAbstract:!0,superClass:["FlowNode"],properties:[{name:"participantRef",type:"Participant",isMany:!0,isReference:!0},{name:"initiatingParticipantRef",type:"Participant",isAttr:!0,isReference:!0},{name:"correlationKeys",type:"CorrelationKey",isMany:!0},{name:"loopType",type:"ChoreographyLoopType",default:"None",isAttr:!0}]},{name:"CallChoreography",superClass:["ChoreographyActivity"],properties:[{name:"calledChoreographyRef",type:"Choreography",isAttr:!0,isReference:!0},{name:"participantAssociations",type:"ParticipantAssociation",isMany:!0}]},{name:"SubChoreography",superClass:["ChoreographyActivity","FlowElementsContainer"],properties:[{name:"artifacts",type:"Artifact",isMany:!0}]},{name:"ChoreographyTask",superClass:["ChoreographyActivity"],properties:[{name:"messageFlowRef",type:"MessageFlow",isMany:!0,isReference:!0}]},{name:"Choreography",superClass:["Collaboration","FlowElementsContainer"]},{name:"GlobalChoreographyTask",superClass:["Choreography"],properties:[{name:"initiatingParticipantRef",type:"Participant",isAttr:!0,isReference:!0}]},{name:"TextAnnotation",superClass:["Artifact"],properties:[{name:"text",type:"String"},{name:"textFormat",default:"text/plain",isAttr:!0,type:"String"}]},{name:"Group",superClass:["Artifact"],properties:[{name:"categoryValueRef",type:"CategoryValue",isAttr:!0,isReference:!0}]},{name:"Association",superClass:["Artifact"],properties:[{name:"associationDirection",type:"AssociationDirection",isAttr:!0},{name:"sourceRef",type:"BaseElement",isAttr:!0,isReference:!0},{name:"targetRef",type:"BaseElement",isAttr:!0,isReference:!0}]},{name:"Category",superClass:["RootElement"],properties:[{name:"categoryValue",type:"CategoryValue",isMany:!0},{name:"name",isAttr:!0,type:"String"}]},{name:"Artifact",isAbstract:!0,superClass:["BaseElement"]},{name:"CategoryValue",superClass:["BaseElement"],properties:[{name:"categorizedFlowElements",type:"FlowElement",isMany:!0,isVirtual:!0,isReference:!0},{name:"value",isAttr:!0,type:"String"}]},{name:"Activity",isAbstract:!0,superClass:["FlowNode"],properties:[{name:"isForCompensation",default:!1,isAttr:!0,type:"Boolean"},{name:"default",type:"SequenceFlow",isAttr:!0,isReference:!0},{name:"ioSpecification",type:"InputOutputSpecification",xml:{serialize:"property"}},{name:"boundaryEventRefs",type:"BoundaryEvent",isMany:!0,isReference:!0},{name:"properties",type:"Property",isMany:!0},{name:"dataInputAssociations",type:"DataInputAssociation",isMany:!0},{name:"dataOutputAssociations",type:"DataOutputAssociation",isMany:!0},{name:"startQuantity",default:1,isAttr:!0,type:"Integer"},{name:"resources",type:"ResourceRole",isMany:!0},{name:"completionQuantity",default:1,isAttr:!0,type:"Integer"},{name:"loopCharacteristics",type:"LoopCharacteristics"}]},{name:"ServiceTask",superClass:["Task"],properties:[{name:"implementation",isAttr:!0,type:"String"},{name:"operationRef",type:"Operation",isAttr:!0,isReference:!0}]},{name:"SubProcess",superClass:["Activity","FlowElementsContainer","InteractionNode"],properties:[{name:"triggeredByEvent",default:!1,isAttr:!0,type:"Boolean"},{name:"artifacts",type:"Artifact",isMany:!0}]},{name:"LoopCharacteristics",isAbstract:!0,superClass:["BaseElement"]},{name:"MultiInstanceLoopCharacteristics",superClass:["LoopCharacteristics"],properties:[{name:"isSequential",default:!1,isAttr:!0,type:"Boolean"},{name:"behavior",type:"MultiInstanceBehavior",default:"All",isAttr:!0},{name:"loopCardinality",type:"Expression",xml:{serialize:"xsi:type"}},{name:"loopDataInputRef",type:"ItemAwareElement",isReference:!0},{name:"loopDataOutputRef",type:"ItemAwareElement",isReference:!0},{name:"inputDataItem",type:"DataInput",xml:{serialize:"property"}},{name:"outputDataItem",type:"DataOutput",xml:{serialize:"property"}},{name:"complexBehaviorDefinition",type:"ComplexBehaviorDefinition",isMany:!0},{name:"completionCondition",type:"Expression",xml:{serialize:"xsi:type"}},{name:"oneBehaviorEventRef",type:"EventDefinition",isAttr:!0,isReference:!0},{name:"noneBehaviorEventRef",type:"EventDefinition",isAttr:!0,isReference:!0}]},{name:"StandardLoopCharacteristics",superClass:["LoopCharacteristics"],properties:[{name:"testBefore",default:!1,isAttr:!0,type:"Boolean"},{name:"loopCondition",type:"Expression",xml:{serialize:"xsi:type"}},{name:"loopMaximum",type:"Integer",isAttr:!0}]},{name:"CallActivity",superClass:["Activity","InteractionNode"],properties:[{name:"calledElement",type:"String",isAttr:!0}]},{name:"Task",superClass:["Activity","InteractionNode"]},{name:"SendTask",superClass:["Task"],properties:[{name:"implementation",isAttr:!0,type:"String"},{name:"operationRef",type:"Operation",isAttr:!0,isReference:!0},{name:"messageRef",type:"Message",isAttr:!0,isReference:!0}]},{name:"ReceiveTask",superClass:["Task"],properties:[{name:"implementation",isAttr:!0,type:"String"},{name:"instantiate",default:!1,isAttr:!0,type:"Boolean"},{name:"operationRef",type:"Operation",isAttr:!0,isReference:!0},{name:"messageRef",type:"Message",isAttr:!0,isReference:!0}]},{name:"ScriptTask",superClass:["Task"],properties:[{name:"scriptFormat",isAttr:!0,type:"String"},{name:"script",type:"String"}]},{name:"BusinessRuleTask",superClass:["Task"],properties:[{name:"implementation",isAttr:!0,type:"String"}]},{name:"AdHocSubProcess",superClass:["SubProcess"],properties:[{name:"completionCondition",type:"Expression",xml:{serialize:"xsi:type"}},{name:"ordering",type:"AdHocOrdering",isAttr:!0},{name:"cancelRemainingInstances",default:!0,isAttr:!0,type:"Boolean"}]},{name:"Transaction",superClass:["SubProcess"],properties:[{name:"protocol",isAttr:!0,type:"String"},{name:"method",isAttr:!0,type:"String"}]},{name:"GlobalScriptTask",superClass:["GlobalTask"],properties:[{name:"scriptLanguage",isAttr:!0,type:"String"},{name:"script",isAttr:!0,type:"String"}]},{name:"GlobalBusinessRuleTask",superClass:["GlobalTask"],properties:[{name:"implementation",isAttr:!0,type:"String"}]},{name:"ComplexBehaviorDefinition",superClass:["BaseElement"],properties:[{name:"condition",type:"FormalExpression"},{name:"event",type:"ImplicitThrowEvent"}]},{name:"ResourceRole",superClass:["BaseElement"],properties:[{name:"resourceRef",type:"Resource",isReference:!0},{name:"resourceParameterBindings",type:"ResourceParameterBinding",isMany:!0},{name:"resourceAssignmentExpression",type:"ResourceAssignmentExpression"},{name:"name",isAttr:!0,type:"String"}]},{name:"ResourceParameterBinding",properties:[{name:"expression",type:"Expression",xml:{serialize:"xsi:type"}},{name:"parameterRef",type:"ResourceParameter",isAttr:!0,isReference:!0}],superClass:["BaseElement"]},{name:"ResourceAssignmentExpression",properties:[{name:"expression",type:"Expression",xml:{serialize:"xsi:type"}}],superClass:["BaseElement"]},{name:"Import",properties:[{name:"importType",isAttr:!0,type:"String"},{name:"location",isAttr:!0,type:"String"},{name:"namespace",isAttr:!0,type:"String"}]},{name:"Definitions",superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"targetNamespace",isAttr:!0,type:"String"},{name:"expressionLanguage",default:"http://www.w3.org/1999/XPath",isAttr:!0,type:"String"},{name:"typeLanguage",default:"http://www.w3.org/2001/XMLSchema",isAttr:!0,type:"String"},{name:"imports",type:"Import",isMany:!0},{name:"extensions",type:"Extension",isMany:!0},{name:"rootElements",type:"RootElement",isMany:!0},{name:"diagrams",isMany:!0,type:"bpmndi:BPMNDiagram"},{name:"exporter",isAttr:!0,type:"String"},{name:"relationships",type:"Relationship",isMany:!0},{name:"exporterVersion",isAttr:!0,type:"String"}]}],enumerations:[{name:"ProcessType",literalValues:[{name:"None"},{name:"Public"},{name:"Private"}]},{name:"GatewayDirection",literalValues:[{name:"Unspecified"},{name:"Converging"},{name:"Diverging"},{name:"Mixed"}]},{name:"EventBasedGatewayType",literalValues:[{name:"Parallel"},{name:"Exclusive"}]},{name:"RelationshipDirection",literalValues:[{name:"None"},{name:"Forward"},{name:"Backward"},{name:"Both"}]},{name:"ItemKind",literalValues:[{name:"Physical"},{name:"Information"}]},{name:"ChoreographyLoopType",literalValues:[{name:"None"},{name:"Standard"},{name:"MultiInstanceSequential"},{name:"MultiInstanceParallel"}]},{name:"AssociationDirection",literalValues:[{name:"None"},{name:"One"},{name:"Both"}]},{name:"MultiInstanceBehavior",literalValues:[{name:"None"},{name:"One"},{name:"All"},{name:"Complex"}]},{name:"AdHocOrdering",literalValues:[{name:"Parallel"},{name:"Sequential"}]}],xml:{tagAlias:"lowerCase",typePrefix:"t"}},bpmndi:{name:"BPMNDI",uri:"http://www.omg.org/spec/BPMN/20100524/DI",prefix:"bpmndi",types:[{name:"BPMNDiagram",properties:[{name:"plane",type:"BPMNPlane",redefines:"di:Diagram#rootElement"},{name:"labelStyle",type:"BPMNLabelStyle",isMany:!0}],superClass:["di:Diagram"]},{name:"BPMNPlane",properties:[{name:"bpmnElement",isAttr:!0,isReference:!0,type:"bpmn:BaseElement",redefines:"di:DiagramElement#modelElement"}],superClass:["di:Plane"]},{name:"BPMNShape",properties:[{name:"bpmnElement",isAttr:!0,isReference:!0,type:"bpmn:BaseElement",redefines:"di:DiagramElement#modelElement"},{name:"isHorizontal",isAttr:!0,type:"Boolean"},{name:"isExpanded",isAttr:!0,type:"Boolean"},{name:"isMarkerVisible",isAttr:!0,type:"Boolean"},{name:"label",type:"BPMNLabel"},{name:"isMessageVisible",isAttr:!0,type:"Boolean"},{name:"participantBandKind",type:"ParticipantBandKind",isAttr:!0},{name:"choreographyActivityShape",type:"BPMNShape",isAttr:!0,isReference:!0}],superClass:["di:LabeledShape"]},{name:"BPMNEdge",properties:[{name:"label",type:"BPMNLabel"},{name:"bpmnElement",isAttr:!0,isReference:!0,type:"bpmn:BaseElement",redefines:"di:DiagramElement#modelElement"},{name:"sourceElement",isAttr:!0,isReference:!0,type:"di:DiagramElement",redefines:"di:Edge#source"},{name:"targetElement",isAttr:!0,isReference:!0,type:"di:DiagramElement",redefines:"di:Edge#target"},{name:"messageVisibleKind",type:"MessageVisibleKind",isAttr:!0,default:"initiating"}],superClass:["di:LabeledEdge"]},{name:"BPMNLabel",properties:[{name:"labelStyle",type:"BPMNLabelStyle",isAttr:!0,isReference:!0,redefines:"di:DiagramElement#style"}],superClass:["di:Label"]},{name:"BPMNLabelStyle",properties:[{name:"font",type:"dc:Font"}],superClass:["di:Style"]}],enumerations:[{name:"ParticipantBandKind",literalValues:[{name:"top_initiating"},{name:"middle_initiating"},{name:"bottom_initiating"},{name:"top_non_initiating"},{name:"middle_non_initiating"},{name:"bottom_non_initiating"}]},{name:"MessageVisibleKind",literalValues:[{name:"initiating"},{name:"non_initiating"}]}],associations:[]},dc:{name:"DC",uri:"http://www.omg.org/spec/DD/20100524/DC",prefix:"dc",types:[{name:"Boolean"},{name:"Integer"},{name:"Real"},{name:"String"},{name:"Font",properties:[{name:"name",type:"String",isAttr:!0},{name:"size",type:"Real",isAttr:!0},{name:"isBold",type:"Boolean",isAttr:!0},{name:"isItalic",type:"Boolean",isAttr:!0},{name:"isUnderline",type:"Boolean",isAttr:!0},{name:"isStrikeThrough",type:"Boolean",isAttr:!0}]},{name:"Point",properties:[{name:"x",type:"Real",default:"0",isAttr:!0},{name:"y",type:"Real",default:"0",isAttr:!0}]},{name:"Bounds",properties:[{name:"x",type:"Real",default:"0",isAttr:!0},{name:"y",type:"Real",default:"0",isAttr:!0},{name:"width",type:"Real",isAttr:!0},{name:"height",type:"Real",isAttr:!0}]}],associations:[]},di:{name:"DI",uri:"http://www.omg.org/spec/DD/20100524/DI",prefix:"di",types:[{name:"DiagramElement",isAbstract:!0,properties:[{name:"id",isAttr:!0,isId:!0,type:"String"},{name:"extension",type:"Extension"},{name:"owningDiagram",type:"Diagram",isReadOnly:!0,isVirtual:!0,isReference:!0},{name:"owningElement",type:"DiagramElement",isReadOnly:!0,isVirtual:!0,isReference:!0},{name:"modelElement",isReadOnly:!0,isVirtual:!0,isReference:!0,type:"Element"},{name:"style",type:"Style",isReadOnly:!0,isVirtual:!0,isReference:!0},{name:"ownedElement",type:"DiagramElement",isReadOnly:!0,isMany:!0,isVirtual:!0}]},{name:"Node",isAbstract:!0,superClass:["DiagramElement"]},{name:"Edge",isAbstract:!0,superClass:["DiagramElement"],properties:[{name:"source",type:"DiagramElement",isReadOnly:!0,isVirtual:!0,isReference:!0},{name:"target",type:"DiagramElement",isReadOnly:!0,isVirtual:!0,isReference:!0},{name:"waypoint",isUnique:!1,isMany:!0,type:"dc:Point",xml:{serialize:"xsi:type"}}]},{name:"Diagram",isAbstract:!0,properties:[{name:"id",isAttr:!0,isId:!0,type:"String"},{name:"rootElement",type:"DiagramElement",isReadOnly:!0,isVirtual:!0},{name:"name",isAttr:!0,type:"String"},{name:"documentation",isAttr:!0,type:"String"},{name:"resolution",isAttr:!0,type:"Real"},{name:"ownedStyle",type:"Style",isReadOnly:!0,isMany:!0,isVirtual:!0}]},{name:"Shape",isAbstract:!0,superClass:["Node"],properties:[{name:"bounds",type:"dc:Bounds"}]},{name:"Plane",isAbstract:!0,superClass:["Node"],properties:[{name:"planeElement",type:"DiagramElement",subsettedProperty:"DiagramElement-ownedElement",isMany:!0}]},{name:"LabeledEdge",isAbstract:!0,superClass:["Edge"],properties:[{name:"ownedLabel",type:"Label",isReadOnly:!0,subsettedProperty:"DiagramElement-ownedElement",isMany:!0,isVirtual:!0}]},{name:"LabeledShape",isAbstract:!0,superClass:["Shape"],properties:[{name:"ownedLabel",type:"Label",isReadOnly:!0,subsettedProperty:"DiagramElement-ownedElement",isMany:!0,isVirtual:!0}]},{name:"Label",isAbstract:!0,superClass:["Node"],properties:[{name:"bounds",type:"dc:Bounds"}]},{name:"Style",isAbstract:!0,properties:[{name:"id",isAttr:!0,isId:!0,type:"String"}]},{name:"Extension",properties:[{name:"values",isMany:!0,type:"Element"}]}],associations:[],xml:{tagAlias:"lowerCase"}},bioc:{name:"bpmn.io colors for BPMN",uri:"http://bpmn.io/schema/bpmn/biocolor/1.0",prefix:"bioc",types:[{name:"ColoredShape",extends:["bpmndi:BPMNShape"],properties:[{name:"stroke",isAttr:!0,type:"String"},{name:"fill",isAttr:!0,type:"String"}]},{name:"ColoredEdge",extends:["bpmndi:BPMNEdge"],properties:[{name:"stroke",isAttr:!0,type:"String"},{name:"fill",isAttr:!0,type:"String"}]}],enumerations:[],associations:[]},color:{name:"BPMN in Color",uri:"http://www.omg.org/spec/BPMN/non-normative/color/1.0",prefix:"color",types:[{name:"ColoredLabel",extends:["bpmndi:BPMNLabel"],properties:[{name:"color",isAttr:!0,type:"String"}]},{name:"ColoredShape",extends:["bpmndi:BPMNShape"],properties:[{name:"background-color",isAttr:!0,type:"String"},{name:"border-color",isAttr:!0,type:"String"}]},{name:"ColoredEdge",extends:["bpmndi:BPMNEdge"],properties:[{name:"border-color",isAttr:!0,type:"String"}]}],enumerations:[],associations:[]}};function xr(e,t){return new gr(F({},br,e),t)}function _r(e){return e?"<"+e.$type+(e.id?' id="'+e.id:"")+'" />':""}function Er(e){return function(){if(!window.Promise)throw new Error("Promises is not supported in this environment. Please polyfill Promise.");var t=arguments.length;if(!(t>=1&&h(arguments[t-1])))return e.apply(this,arguments);var n=arguments[t-1];console.warn(new Error("Passing callbacks to "+e.name+" is deprecated and will be removed in a future major release. Please switch to promises: https://bpmn.io/l/moving-to-promises.html"));var i=Array.prototype.slice.call(arguments,0,-1);e.apply(this,i).then((function(e){var t=Object.keys(e)[0];return n(null,e[t])}),(function(e){return n(e,e.warnings)}))}}function wr(e){f(e,"di")||Object.defineProperty(e,"di",{enumerable:!1,get:function(){throw new Error("Tried to access di from the businessObject. The di is available through the diagram element only. For more information, see https://github.com/bpmn-io/bpmn-js/issues/1472")}})}function Sr(e,t){return e.$instanceOf(t)}function Cr(e,t){var n={},i=[],r={};function o(e,t){return function(n){e(n,t)}}function a(e){n[e.id]=e}function s(n,i){try{var o=r[n.id]&&function(n,i){if(n.gfx)throw new Error(t("already rendered {element}",{element:_r(n)}));return e.element(n,r[n.id],i)}(n,i);return a(n),o}catch(e){c(e.message,{element:n,error:e}),console.error(t("failed to import {element}",{element:_r(n)})),console.error(e)}}function c(t,n){e.error(t,n)}function p(e){var n=e.bpmnElement;n?r[n.id]?c(t("multiple DI elements defined for {element}",{element:_r(n)}),{element:n}):(r[n.id]=e,wr(n)):c(t("no bpmnElement referenced in {element}",{element:_r(e)}),{element:e})}function l(e){var t;p(t=e.plane),g(t.planeElement,u)}function u(e){p(e)}function h(){for(;i.length;)i.shift()()}function d(e,t){P(e,t),w(e.ioSpecification,t),E(e.artifacts,t),a(e)}function f(e,t){s(e,t)}function v(e,t){s(e,t)}function b(e,t){s(e,t)}function x(e,t){s(e,t)}function _(e,t){s(e,t)}function E(e,t){g(e,(function(e){Sr(e,"bpmn:Association")?i.push((function(){_(e,t)})):_(e,t)}))}function w(e,t){e&&(g(e.dataInputs,o(b,t)),g(e.dataOutputs,o(x,t)))}function S(e,t){P(e,t),E(e.artifacts,t)}function C(e,t){var n=s(e,t);Sr(e,"bpmn:SubProcess")&&S(e,n||t),Sr(e,"bpmn:Activity")&&w(e.ioSpecification,t),i.push((function(){g(e.dataInputAssociations,o(v,t)),g(e.dataOutputAssociations,o(v,t))}))}function A(e,t){s(e,t)}function R(e,t){i.push((function(){var n=s(e,t);e.childLaneSet&&T(e.childLaneSet,n||t),function(e){g(e.flowNodeRef,(function(t){var n=t.get("lanes");n&&n.push(e)}))}(e)}))}function T(e,t){g(e.lanes,o(R,t))}function P(e,n){!function(e,n){g(e,(function(e){Sr(e,"bpmn:SequenceFlow")?i.push((function(){!function(e,t){s(e,t)}(e,n)})):Sr(e,"bpmn:BoundaryEvent")?i.unshift((function(){C(e,n)})):Sr(e,"bpmn:FlowNode")?C(e,n):Sr(e,"bpmn:DataObject")||(Sr(e,"bpmn:DataStoreReference")||Sr(e,"bpmn:DataObjectReference")?A(e,n):c(t("unrecognized flowElement {element} in context {context}",{element:_r(e),context:n?_r(n.businessObject):"null"}),{element:e,context:n}))}))}(e.flowElements,n),e.laneSets&&function(e,t){g(e,o(T,t))}(e.laneSets,n)}function D(e,t){var n=s(e,t),i=e.processRef;i&&d(i,n||t)}return{handleDeferred:h,handleDefinitions:function(a,s){var u=a.diagrams;if(s&&-1===u.indexOf(s))throw new Error(t("diagram not part of bpmn:Definitions"));if(!s&&u&&u.length&&(s=u[0]),!s)throw new Error(t("no diagram to display"));r={},l(s);var v=s.plane;if(!v)throw new Error(t("no plane for {element}",{element:_r(s)}));var b=v.bpmnElement;if(!b){if(b=function(e){return m(e.rootElements,(function(e){return Sr(e,"bpmn:Process")||Sr(e,"bpmn:Collaboration")}))}(a),!b)throw new Error(t("no process or collaboration to display"));c(t("correcting missing bpmnElement on {plane} to {rootElement}",{plane:_r(v),rootElement:_r(b)})),v.bpmnElement=b,p(v)}var x,_,w=function(t,n){return e.root(t,r[t.id],n)}(b,v);if(Sr(b,"bpmn:Process")||Sr(b,"bpmn:SubProcess"))d(b,w);else{if(!Sr(b,"bpmn:Collaboration"))throw new Error(t("unsupported bpmnElement for {plane}: {rootElement}",{plane:_r(v),rootElement:_r(b)}));_=w,g((x=b).participants,o(D,_)),E(x.artifacts,_),i.push((function(){!function(e,t){g(e,o(f,t))}(x.messageFlows,_)})),function(e,t){var i=y(e,(function(e){return!n[e.id]&&Sr(e,"bpmn:Process")&&e.laneSets}));i.forEach(o(d,t))}(a.rootElements,w)}h()},handleSubProcess:S,registerDi:p}}function Ar(e,t){var n=Tr(e);return n&&"function"==typeof n.$instanceOf&&n.$instanceOf(t)}function Rr(e,t){return E(t,(function(t){return Ar(e,t)}))}function Tr(e){return e&&e.businessObject||e}function Pr(e){return e&&e.di}function Dr(e,t,n){var i,r,o,a,s=[];function c(e,t){var n=new Cr({root:function(e,t){return i.add(e,t)},element:function(e,t,n){return i.add(e,t,n)},error:function(e,t){s.push({message:e,context:t})}},o);t=t||e.diagrams&&e.diagrams[0];var r=function(e,t){if(!t)return;var n,i=t.plane.bpmnElement,r=i;Ar(i,"bpmn:Process")||Ar(i,"bpmn:Collaboration")||(r=function(e){var t=e;for(;t;){if(Ar(t,"bpmn:Process"))return t;t=t.$parent}} +/** + * This file must not be changed or exchanged. + * + * @see http://bpmn.io/license for more information. + */(i));n=Ar(r,"bpmn:Collaboration")?r:m(e.rootElements,(function(e){if(Ar(e,"bpmn:Collaboration"))return m(e.participants,(function(e){return e.processRef===r}))}));var o=[r];n&&(o=w(n.participants,(function(e){return e.processRef}))).push(n);var a=kr(o),s=[t],c=[i];return g(e.diagrams,(function(e){var t=e.plane.bpmnElement;-1!==a.indexOf(t)&&-1===c.indexOf(t)&&(s.push(e),c.push(t))})),s}(e,t);if(!r)throw new Error(o("no diagram to display"));g(r,(function(t){n.handleDefinitions(e,t)}));var c=t.plane.bpmnElement.id;a.setRootElement(a.findRoot(c+"_plane")||a.findRoot(c))}return new Promise((function(p,l){try{return i=e.get("bpmnImporter"),r=e.get("eventBus"),o=e.get("translate"),a=e.get("canvas"),r.fire("import.render.start",{definitions:t}),c(t,n),r.fire("import.render.complete",{error:undefined,warnings:s}),p({warnings:s})}catch(e){return e.warnings=s,l(e)}}))}function kr(e){var t=[];return g(e,(function(e){e&&(t.push(e),t=t.concat(kr(e.flowElements)))})),t}var Mr,Br='',Nr={verticalAlign:"middle"},Or={color:"#404040"},jr={zIndex:"1001",position:"fixed",top:"0",left:"0",right:"0",bottom:"0"},Lr={width:"100%",height:"100%",background:"rgba(40,40,40,0.2)"},Ir={position:"absolute",left:"50%",top:"40%",transform:"translate(-50%)",width:"260px",padding:"10px",background:"white",boxShadow:"0 1px 4px rgba(0,0,0,0.3)",fontFamily:"Helvetica, Arial, sans-serif",fontSize:"14px",display:"flex",lineHeight:"1.3"};function Fr(){Mr||(K(Mr=fe('
    Web-based tooling for BPMN, DMN and CMMN diagrams powered by bpmn.io.
    '),jr),K(ye("svg",Mr),Nr),K(ye(".backdrop",Mr),Lr),K(ye(".notice",Mr),Ir),K(ye(".link",Mr),Or,{margin:"15px 20px 15px 10px",alignSelf:"center"}),de.bind(Mr,".backdrop","click",(function(e){document.body.removeChild(Mr)}))),document.body.appendChild(Mr)} +/** + * The code in the area + * must not be changed. + * + * @see http://bpmn.io/license for more information. + */function zr(e){ +/** + * Adds the project logo to the diagram container as + * required by the bpmn.io license. + * + * @see http://bpmn.io/license + * + * @param {Element} container + */ +var t,n;e=F({},Hr,e),this._moddle=this._createModdle(e),this._container=this._createContainer(e),t=this._container,K(ye("svg",n=fe(''+Br+"")),Nr),K(n,Or,{position:"absolute",bottom:"15px",right:"15px",zIndex:"100"}),t.appendChild(n),le.bind(n,"click",(function(e){Fr(),e.preventDefault()})),this._init(this._container,this._moddle,e)}function $r(e,t){return e.warnings=t,e}e(zr,oi),zr.prototype.importXML=Er((function(e,t){var n=this;return new Promise((function(i,r){e=n._emit("import.parse.start",{xml:e})||e,n._moddle.fromXML(e,"bpmn:Definitions").then((function(e){var o,a,s=e.rootElement,c=e.references,p=e.warnings,l=e.elementsById;s=n._emit("import.parse.complete",(o={error:null,definitions:s,elementsById:l,references:c,warnings:p},a=n.get("eventBus").createEvent(o),Object.defineProperty(a,"context",{enumerable:!0,get:function(){return console.warn(new Error("import.parse.complete is deprecated and will be removed in future library versions")),{warnings:o.warnings,references:o.references,elementsById:o.elementsById}}}),a))||s,n.importDefinitions(s,t).then((function(e){var t=[].concat(p,e.warnings||[]);return n._emit("import.done",{error:null,warnings:t}),i({warnings:t})})).catch((function(e){var t=[].concat(p,e.warnings||[]);return n._emit("import.done",{error:e,warnings:t}),r($r(e,t))}))})).catch((function(e){return n._emit("import.parse.complete",{error:e}),e=function(e){var t=/unparsable content <([^>]+)> detected([\s\S]*)$/.exec(e.message);t&&(e.message="unparsable content <"+t[1]+"> detected; this may indicate an invalid BPMN 2.0 diagram file"+t[2]);return e}(e),n._emit("import.done",{error:e,warnings:e.warnings}),r(e)}))}))})),zr.prototype.importDefinitions=Er((function(e,t){var n=this;return new Promise((function(i,r){n._setDefinitions(e),n.open(t).then((function(e){var t=e.warnings;return i({warnings:t})})).catch((function(e){return r(e)}))}))})),zr.prototype.open=Er((function(e){var t=this._definitions,n=e,i=this;return new Promise((function(r,o){if(!t){var a=new Error("no XML imported");return o($r(a,[]))}if("string"==typeof e&&(n=function(e,t){if(!t)return null;return m(e.diagrams,(function(e){return e.id===t}))||null}(t,e),!n)){var s=new Error("BPMNDiagram <"+e+"> not found");return o($r(s,[]))}try{i.clear()}catch(e){return o($r(e,[]))}Dr(i,t,n).then((function(e){var t=e.warnings;return r({warnings:t})})).catch((function(e){return o(e)}))}))})),zr.prototype.saveXML=Er((function(e){e=e||{};var t=this,n=this._definitions;return new Promise((function(i){if(!n)return i({error:new Error("no definitions loaded")});n=t._emit("saveXML.start",{definitions:n})||n,t._moddle.toXML(n,e).then((function(e){var n=e.xml;return n=t._emit("saveXML.serialized",{xml:n})||n,i({xml:n})}))})).catch((function(e){return{error:e}})).then((function(e){t._emit("saveXML.done",e);var n=e.error;return n?Promise.reject(n):e}))})),zr.prototype.saveSVG=Er((function(e){var t=this;return new Promise((function(e,n){var i,r;t._emit("saveSVG.start");try{var o=t.get("canvas"),a=o.getActiveLayer(),s=ye("defs",o._svg),c=Ke(a),p=s?""+Ke(s)+"":"",l=a.getBBox();i='\n\x3c!-- created with bpmn-js / http://bpmn.io --\x3e\n\n'+p+c+""}catch(e){r=e}return t._emit("saveSVG.done",{error:r,svg:i}),r?n(r):e({svg:i})}))})),zr.prototype._setDefinitions=function(e){this._definitions=e},zr.prototype.getModules=function(){return this._modules},zr.prototype.clear=function(){this.getDefinitions()&&oi.prototype.clear.call(this)},zr.prototype.destroy=function(){oi.prototype.destroy.call(this),be(this._container)},zr.prototype.on=function(e,t,n,i){return this.get("eventBus").on(e,t,n,i)},zr.prototype.off=function(e,t){this.get("eventBus").off(e,t)},zr.prototype.attachTo=function(e){if(!e)throw new Error("parentNode required");this.detach(),e.get&&e.constructor.prototype.jquery&&(e=e.get(0)),"string"==typeof e&&(e=ye(e)),e.appendChild(this._container),this._emit("attach",{}),this.get("canvas").resized()},zr.prototype.getDefinitions=function(){return this._definitions},zr.prototype.detach=function(){var e=this._container,t=e.parentNode;t&&(this._emit("detach",{}),t.removeChild(e))},zr.prototype._init=function(e,t,n){var i=n.modules||this.getModules(),r=n.additionalModules||[],o=[].concat([{bpmnjs:["value",this],moddle:["value",t]}],i,r),a=F($(n,["additionalModules"]),{canvas:F({},n.canvas,{container:e}),modules:o});oi.call(this,a),n&&n.container&&this.attachTo(n.container)},zr.prototype._emit=function(e,t){return this.get("eventBus").fire(e,t)},zr.prototype._createContainer=function(e){var t=fe('
    ');return K(t,{width:Gr(e.width),height:Gr(e.height),position:e.position}),t},zr.prototype._createModdle=function(e){return new xr(F({},this._moddleExtensions,e.moddleExtensions))},zr.prototype._modules=[];var Hr={width:"100%",height:"100%",position:"relative"};function Gr(e){return e+(u(e)?"px":"")}function Vr(e){zr.call(this,e),this.on("import.parse.complete",(function(e){e.error||this._collectIds(e.definitions,e.elementsById)}),this),this.on("diagram.destroy",(function(){this.get("moddle").ids.clear()}),this)}function Wr(e,t){return!Ar(e,"bpmn:CallActivity")&&(Ar(e,"bpmn:SubProcess")?!(!(t=t||Pr(e))||!Ar(t,"bpmndi:BPMNPlane"))||t&&!!t.isExpanded:!Ar(e,"bpmn:Participant")||!!Tr(e).processRef)}function Ur(e){return e&&!!Tr(e).triggeredByEvent}function qr(e,t){var n=Tr(e),i=!1;return n.eventDefinitions&&g(n.eventDefinitions,(function(e){Ar(e,t)&&(i=!0)})),i}function Kr(e){return Ar(e,"bpmn:FlowElement")||Ar(e,"bpmn:Participant")||Ar(e,"bpmn:Lane")||Ar(e,"bpmn:SequenceFlow")||Ar(e,"bpmn:MessageFlow")||Ar(e,"bpmn:DataInput")||Ar(e,"bpmn:DataOutput")?"name":Ar(e,"bpmn:TextAnnotation")?"text":Ar(e,"bpmn:Group")?"categoryValueRef":void 0}function Yr(e){var t=e.businessObject,n=Kr(t);if(n)return"categoryValueRef"===n?function(e){var t=e.categoryValueRef;return t&&t.value||""}(t):t[n]||""}e(Vr,zr),Vr.prototype._createModdle=function(e){var t=zr.prototype._createModdle.call(this,e);return t.ids=new n([32,36,1]),t},Vr.prototype._collectIds=function(e,t){var n,i=e.$model.ids;for(n in i.clear(),t)i.claim(n,t[n])};var Xr="hsl(225, 10%, 15%)";function Zr(e,t,n){return E(e.eventDefinitions,(function(i){return i.$type===t&&function(e,t){return _(t,(function(t,n){return e[n]==t}))}(e,n)}))}function Qr(e){return e.businessObject}function Jr(e,t){var n=Pr(e);return n.get("color:background-color")||n.get("bioc:fill")||t||"white"}function eo(e,t){var n=Pr(e);return n.get("color:border-color")||n.get("bioc:stroke")||t||Xr}function to(e,t,n){var i=Pr(e).get("label");return i&&i.get("color:color")||t||eo(e,n)}var no=new n,io=.95;function ro(e,t,n,i,r,o,a){st.call(this,t,a);var s=e&&e.defaultFillColor,c=e&&e.defaultStrokeColor,p=e&&e.defaultLabelColor,u=no.next(),h={},d=n.computeStyle;function f(e,t){var n=F({fill:Xr,strokeWidth:1,strokeLinecap:"round",strokeDasharray:"none"},t.attrs),i=t.ref||{x:0,y:0},o=t.scale||1;"none"===n.strokeDasharray&&(n.strokeDasharray=[1e4,1]);var a=Le("marker");Se(t.element,n),_e(a,t.element),Se(a,{id:e,viewBox:"0 0 20 20",refX:i.x,refY:i.y,markerWidth:20*o,markerHeight:20*o,orient:"auto"});var s=ye("defs",r._svg);s||(s=Le("defs"),_e(r._svg,s)),_e(s,a),h[e]=a}function m(e){return e.replace(/[^0-9a-zA-z]+/g,"_")}function v(e,t,n){var i=e+"-"+m(t)+"-"+m(n)+"-"+u;return h[i]||function(e,t,n,i){if("sequenceflow-end"===t){var r=Le("path");Se(r,{d:"M 1 5 L 11 10 L 1 15 Z"}),f(e,{element:r,ref:{x:11,y:10},scale:.5,attrs:{fill:i,stroke:i}})}if("messageflow-start"===t){var o=Le("circle");Se(o,{cx:6,cy:6,r:3.5}),f(e,{element:o,attrs:{fill:n,stroke:i},ref:{x:6,y:6}})}if("messageflow-end"===t){var a=Le("path");Se(a,{d:"m 1 5 l 0 -3 l 7 3 l -7 3 z"}),f(e,{element:a,attrs:{fill:n,stroke:i,strokeLinecap:"butt"},ref:{x:8.5,y:5}})}if("association-start"===t){var s=Le("path");Se(s,{d:"M 11 5 L 1 10 L 11 15"}),f(e,{element:s,attrs:{fill:"none",stroke:i,strokeWidth:1.5},ref:{x:1,y:10},scale:.5})}if("association-end"===t){var c=Le("path");Se(c,{d:"M 1 5 L 11 10 L 1 15"}),f(e,{element:c,attrs:{fill:"none",stroke:i,strokeWidth:1.5},ref:{x:12,y:10},scale:.5})}if("conditional-flow-marker"===t){var p=Le("path");Se(p,{d:"M 0 10 L 8 6 L 16 10 L 8 14 Z"}),f(e,{element:p,attrs:{fill:n,stroke:i},ref:{x:-1,y:10},scale:.5})}if("conditional-default-flow-marker"===t){var l=Le("path");Se(l,{d:"M 6 4 L 10 16"}),f(e,{element:l,attrs:{stroke:i},ref:{x:0,y:10},scale:.5})}}(i,e,t,n),"url(#"+i+")"}function y(e,t,n,i,r){l(i)&&(r=i,i=0),i=i||0,"none"===(r=d(r,{stroke:Xr,strokeWidth:2,fill:"white"})).fill&&delete r.fillOpacity;var o=t/2,a=n/2,s=Le("circle");return Se(s,{cx:o,cy:a,r:Math.round((t+n)/4-i)}),Se(s,r),_e(e,s),s}function b(e,t,n,i,r,o){l(r)&&(o=r,r=0),r=r||0,o=d(o,{stroke:Xr,strokeWidth:2,fill:"white"});var a=Le("rect");return Se(a,{x:r,y:r,width:t-2*r,height:n-2*r,rx:i,ry:i}),Se(a,o),_e(e,a),a}function x(e,t,n){var i=lt(t,n=d(n,["no-fill"],{stroke:Xr,strokeWidth:2,fill:"none"}));return _e(e,i),i}function _(e,t,n){n=d(n,["no-fill"],{strokeWidth:2,stroke:Xr});var i=Le("path");return Se(i,{d:t}),Se(i,n),_e(e,i),i}function E(e,t,n,i){return _(t,n,F({"data-marker":e},i))}function w(e){return D[e]}function S(e){return function(t,n){return w(e)(t,n)}}function C(e,t){var n=Qr(e),i=function(e){return"bpmn:IntermediateThrowEvent"===e.$type||"bpmn:EndEvent"===e.$type}(n);return n.eventDefinitions&&n.eventDefinitions.length>1?n.parallelMultiple?w("bpmn:ParallelMultipleEventDefinition")(t,e,i):w("bpmn:MultipleEventDefinition")(t,e,i):Zr(n,"bpmn:MessageEventDefinition")?w("bpmn:MessageEventDefinition")(t,e,i):Zr(n,"bpmn:TimerEventDefinition")?w("bpmn:TimerEventDefinition")(t,e,i):Zr(n,"bpmn:ConditionalEventDefinition")?w("bpmn:ConditionalEventDefinition")(t,e):Zr(n,"bpmn:SignalEventDefinition")?w("bpmn:SignalEventDefinition")(t,e,i):Zr(n,"bpmn:EscalationEventDefinition")?w("bpmn:EscalationEventDefinition")(t,e,i):Zr(n,"bpmn:LinkEventDefinition")?w("bpmn:LinkEventDefinition")(t,e,i):Zr(n,"bpmn:ErrorEventDefinition")?w("bpmn:ErrorEventDefinition")(t,e,i):Zr(n,"bpmn:CancelEventDefinition")?w("bpmn:CancelEventDefinition")(t,e,i):Zr(n,"bpmn:CompensateEventDefinition")?w("bpmn:CompensateEventDefinition")(t,e,i):Zr(n,"bpmn:TerminateEventDefinition")?w("bpmn:TerminateEventDefinition")(t,e,i):null}function A(e,t,n){n=F({size:{width:100}},n);var i=o.createText(t||"",n);return Pe(i).add("djs-label"),_e(e,i),i}function R(e,t,n){return A(e,Qr(t).name,{box:t,align:n,padding:5,style:{fill:to(t,p,c)}})}function T(e,t,n){Qn(A(e,t,{box:{height:30,width:n.height},align:"center-middle",style:{fill:to(n,p,c)}}),0,-(-1*n.height),270)}function P(e){for(var t=e.waypoints,n="m "+t[0].x+","+t[0].y,i=1;i1)for(;n=i.shift();){if(!(n.length+oe?t.width:e}),0),m=o.top;"middle"===r.vertical&&(m+=(n.height-d)/2),m-=(s||p[0].height)/4;var v=Le("text");return Se(v,i),g(p,(function(e){var t;switch(m+=s||e.height,r.horizontal){case"left":t=o.left;break;case"right":t=(a?f:u)-o.right-e.width;break;default:t=Math.max(((a?f:u)-e.width)/2+o.left,0)}var n=Le("tspan");Se(n,{x:t,y:m}),n.textContent=e.text,_e(v,n)})),ke(h),{dimensions:{width:f,height:d},element:v}};function uo(e){var t=F({fontFamily:"Arial, sans-serif",fontSize:12,fontWeight:"normal",lineHeight:1.2},e&&e.defaultStyle||{}),n=parseInt(t.fontSize,10)-1,i=F({},t,{fontSize:n},e&&e.externalStyle||{}),r=new lo({style:t});this.getExternalLabelBounds=function(e,t){var n=r.getDimensions(t,{box:{width:90,height:30,x:e.width/2+e.x,y:e.height/2+e.y},style:i});return{x:Math.round(e.x+e.width/2-n.width/2),y:Math.round(e.y),width:Math.ceil(n.width),height:Math.ceil(n.height)}},this.getTextAnnotationBounds=function(e,n){var i=r.getDimensions(n,{box:e,style:t,align:"left-top",padding:5});return{x:e.x,y:e.y,width:e.width,height:Math.max(30,Math.round(i.height))}},this.createText=function(e,t){return r.createText(e,t||{})},this.getDefaultStyle=function(){return t},this.getExternalStyle=function(){return i}}uo.$inject=["config.textRenderer"];var ho=/\{([^{}]+)\}/g,fo=/(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g;var mo={__init__:["bpmnRenderer"],bpmnRenderer:["type",ro],textRenderer:["type",uo],pathMap:["type",function(){this.pathMap={EVENT_MESSAGE:{d:"m {mx},{my} l 0,{e.y1} l {e.x1},0 l 0,-{e.y1} z l {e.x0},{e.y0} l {e.x0},-{e.y0}",height:36,width:36,heightElements:[6,14],widthElements:[10.5,21]},EVENT_SIGNAL:{d:"M {mx},{my} l {e.x0},{e.y0} l -{e.x1},0 Z",height:36,width:36,heightElements:[18],widthElements:[10,20]},EVENT_ESCALATION:{d:"M {mx},{my} l {e.x0},{e.y0} l -{e.x0},-{e.y1} l -{e.x0},{e.y1} Z",height:36,width:36,heightElements:[20,7],widthElements:[8]},EVENT_CONDITIONAL:{d:"M {e.x0},{e.y0} l {e.x1},0 l 0,{e.y2} l -{e.x1},0 Z M {e.x2},{e.y3} l {e.x0},0 M {e.x2},{e.y4} l {e.x0},0 M {e.x2},{e.y5} l {e.x0},0 M {e.x2},{e.y6} l {e.x0},0 M {e.x2},{e.y7} l {e.x0},0 M {e.x2},{e.y8} l {e.x0},0 ",height:36,width:36,heightElements:[8.5,14.5,18,11.5,14.5,17.5,20.5,23.5,26.5],widthElements:[10.5,14.5,12.5]},EVENT_LINK:{d:"m {mx},{my} 0,{e.y0} -{e.x1},0 0,{e.y1} {e.x1},0 0,{e.y0} {e.x0},-{e.y2} -{e.x0},-{e.y2} z",height:36,width:36,heightElements:[4.4375,6.75,7.8125],widthElements:[9.84375,13.5]},EVENT_ERROR:{d:"m {mx},{my} {e.x0},-{e.y0} {e.x1},-{e.y1} {e.x2},{e.y2} {e.x3},-{e.y3} -{e.x4},{e.y4} -{e.x5},-{e.y5} z",height:36,width:36,heightElements:[.023,8.737,8.151,16.564,10.591,8.714],widthElements:[.085,6.672,6.97,4.273,5.337,6.636]},EVENT_CANCEL_45:{d:"m {mx},{my} -{e.x1},0 0,{e.x0} {e.x1},0 0,{e.y1} {e.x0},0 0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z",height:36,width:36,heightElements:[4.75,8.5],widthElements:[4.75,8.5]},EVENT_COMPENSATION:{d:"m {mx},{my} {e.x0},-{e.y0} 0,{e.y1} z m {e.x1},-{e.y2} {e.x2},-{e.y3} 0,{e.y1} -{e.x2},-{e.y3} z",height:36,width:36,heightElements:[6.5,13,.4,6.1],widthElements:[9,9.3,8.7]},EVENT_TIMER_WH:{d:"M {mx},{my} l {e.x0},-{e.y0} m -{e.x0},{e.y0} l {e.x1},{e.y1} ",height:36,width:36,heightElements:[10,2],widthElements:[3,7]},EVENT_TIMER_LINE:{d:"M {mx},{my} m {e.x0},{e.y0} l -{e.x1},{e.y1} ",height:36,width:36,heightElements:[10,3],widthElements:[0,0]},EVENT_MULTIPLE:{d:"m {mx},{my} {e.x1},-{e.y0} {e.x1},{e.y0} -{e.x0},{e.y1} -{e.x2},0 z",height:36,width:36,heightElements:[6.28099,12.56199],widthElements:[3.1405,9.42149,12.56198]},EVENT_PARALLEL_MULTIPLE:{d:"m {mx},{my} {e.x0},0 0,{e.y1} {e.x1},0 0,{e.y0} -{e.x1},0 0,{e.y1} -{e.x0},0 0,-{e.y1} -{e.x1},0 0,-{e.y0} {e.x1},0 z",height:36,width:36,heightElements:[2.56228,7.68683],widthElements:[2.56228,7.68683]},GATEWAY_EXCLUSIVE:{d:"m {mx},{my} {e.x0},{e.y0} {e.x1},{e.y0} {e.x2},0 {e.x4},{e.y2} {e.x4},{e.y1} {e.x2},0 {e.x1},{e.y3} {e.x0},{e.y3} {e.x3},0 {e.x5},{e.y1} {e.x5},{e.y2} {e.x3},0 z",height:17.5,width:17.5,heightElements:[8.5,6.5312,-6.5312,-8.5],widthElements:[6.5,-6.5,3,-3,5,-5]},GATEWAY_PARALLEL:{d:"m {mx},{my} 0,{e.y1} -{e.x1},0 0,{e.y0} {e.x1},0 0,{e.y1} {e.x0},0 0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z",height:30,width:30,heightElements:[5,12.5],widthElements:[5,12.5]},GATEWAY_EVENT_BASED:{d:"m {mx},{my} {e.x0},{e.y0} {e.x0},{e.y1} {e.x1},{e.y2} {e.x2},0 z",height:11,width:11,heightElements:[-6,6,12,-12],widthElements:[9,-3,-12]},GATEWAY_COMPLEX:{d:"m {mx},{my} 0,{e.y0} -{e.x0},-{e.y1} -{e.x1},{e.y2} {e.x0},{e.y1} -{e.x2},0 0,{e.y3} {e.x2},0 -{e.x0},{e.y1} l {e.x1},{e.y2} {e.x0},-{e.y1} 0,{e.y0} {e.x3},0 0,-{e.y0} {e.x0},{e.y1} {e.x1},-{e.y2} -{e.x0},-{e.y1} {e.x2},0 0,-{e.y3} -{e.x2},0 {e.x0},-{e.y1} -{e.x1},-{e.y2} -{e.x0},{e.y1} 0,-{e.y0} -{e.x3},0 z",height:17.125,width:17.125,heightElements:[4.875,3.4375,2.125,3],widthElements:[3.4375,2.125,4.875,3]},DATA_OBJECT_PATH:{d:"m 0,0 {e.x1},0 {e.x0},{e.y0} 0,{e.y1} -{e.x2},0 0,-{e.y2} {e.x1},0 0,{e.y0} {e.x0},0",height:61,width:51,heightElements:[10,50,60],widthElements:[10,40,50,60]},DATA_OBJECT_COLLECTION_PATH:{d:"m{mx},{my} m 3,2 l 0,10 m 3,-10 l 0,10 m 3,-10 l 0,10",height:10,width:10,heightElements:[],widthElements:[]},DATA_ARROW:{d:"m 5,9 9,0 0,-3 5,5 -5,5 0,-3 -9,0 z",height:61,width:51,heightElements:[],widthElements:[]},DATA_STORE:{d:"m {mx},{my} l 0,{e.y2} c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 l 0,-{e.y2} c -{e.x0},-{e.y1} -{e.x1},-{e.y1} -{e.x2},0c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 m -{e.x2},{e.y0}c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0m -{e.x2},{e.y0}c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0",height:61,width:61,heightElements:[7,10,45],widthElements:[2,58,60]},TEXT_ANNOTATION:{d:"m {mx}, {my} m 10,0 l -10,0 l 0,{e.y0} l 10,0",height:30,width:10,heightElements:[30],widthElements:[10]},MARKER_SUB_PROCESS:{d:"m{mx},{my} m 7,2 l 0,10 m -5,-5 l 10,0",height:10,width:10,heightElements:[],widthElements:[]},MARKER_PARALLEL:{d:"m{mx},{my} m 3,2 l 0,10 m 3,-10 l 0,10 m 3,-10 l 0,10",height:10,width:10,heightElements:[],widthElements:[]},MARKER_SEQUENTIAL:{d:"m{mx},{my} m 0,3 l 10,0 m -10,3 l 10,0 m -10,3 l 10,0",height:10,width:10,heightElements:[],widthElements:[]},MARKER_COMPENSATION:{d:"m {mx},{my} 7,-5 0,10 z m 7.1,-0.3 6.9,-4.7 0,10 -6.9,-4.7 z",height:10,width:21,heightElements:[],widthElements:[]},MARKER_LOOP:{d:"m {mx},{my} c 3.526979,0 6.386161,-2.829858 6.386161,-6.320661 0,-3.490806 -2.859182,-6.320661 -6.386161,-6.320661 -3.526978,0 -6.38616,2.829855 -6.38616,6.320661 0,1.745402 0.714797,3.325567 1.870463,4.469381 0.577834,0.571908 1.265885,1.034728 2.029916,1.35457 l -0.718163,-3.909793 m 0.718163,3.909793 -3.885211,0.802902",height:13.9,width:13.7,heightElements:[],widthElements:[]},MARKER_ADHOC:{d:"m {mx},{my} m 0.84461,2.64411 c 1.05533,-1.23780996 2.64337,-2.07882 4.29653,-1.97997996 2.05163,0.0805 3.85579,1.15803 5.76082,1.79107 1.06385,0.34139996 2.24454,0.1438 3.18759,-0.43767 0.61743,-0.33642 1.2775,-0.64078 1.7542,-1.17511 0,0.56023 0,1.12046 0,1.6807 -0.98706,0.96237996 -2.29792,1.62393996 -3.6918,1.66181996 -1.24459,0.0927 -2.46671,-0.2491 -3.59505,-0.74812 -1.35789,-0.55965 -2.75133,-1.33436996 -4.27027,-1.18121996 -1.37741,0.14601 -2.41842,1.13685996 -3.44288,1.96782996 z",height:4,width:15,heightElements:[],widthElements:[]},TASK_TYPE_SEND:{d:"m {mx},{my} l 0,{e.y1} l {e.x1},0 l 0,-{e.y1} z l {e.x0},{e.y0} l {e.x0},-{e.y0}",height:14,width:21,heightElements:[6,14],widthElements:[10.5,21]},TASK_TYPE_SCRIPT:{d:"m {mx},{my} c 9.966553,-6.27276 -8.000926,-7.91932 2.968968,-14.938 l -8.802728,0 c -10.969894,7.01868 6.997585,8.66524 -2.968967,14.938 z m -7,-12 l 5,0 m -4.5,3 l 4.5,0 m -3,3 l 5,0m -4,3 l 5,0",height:15,width:12.6,heightElements:[6,14],widthElements:[10.5,21]},TASK_TYPE_USER_1:{d:"m {mx},{my} c 0.909,-0.845 1.594,-2.049 1.594,-3.385 0,-2.554 -1.805,-4.62199999 -4.357,-4.62199999 -2.55199998,0 -4.28799998,2.06799999 -4.28799998,4.62199999 0,1.348 0.974,2.562 1.89599998,3.405 -0.52899998,0.187 -5.669,2.097 -5.794,4.7560005 v 6.718 h 17 v -6.718 c 0,-2.2980005 -5.5279996,-4.5950005 -6.0509996,-4.7760005 zm -8,6 l 0,5.5 m 11,0 l 0,-5"},TASK_TYPE_USER_2:{d:"m {mx},{my} m 2.162,1.009 c 0,2.4470005 -2.158,4.4310005 -4.821,4.4310005 -2.66499998,0 -4.822,-1.981 -4.822,-4.4310005 "},TASK_TYPE_USER_3:{d:"m {mx},{my} m -6.9,-3.80 c 0,0 2.25099998,-2.358 4.27399998,-1.177 2.024,1.181 4.221,1.537 4.124,0.965 -0.098,-0.57 -0.117,-3.79099999 -4.191,-4.13599999 -3.57499998,0.001 -4.20799998,3.36699999 -4.20699998,4.34799999 z"},TASK_TYPE_MANUAL:{d:"m {mx},{my} c 0.234,-0.01 5.604,0.008 8.029,0.004 0.808,0 1.271,-0.172 1.417,-0.752 0.227,-0.898 -0.334,-1.314 -1.338,-1.316 -2.467,-0.01 -7.886,-0.004 -8.108,-0.004 -0.014,-0.079 0.016,-0.533 0,-0.61 0.195,-0.042 8.507,0.006 9.616,0.002 0.877,-0.007 1.35,-0.438 1.353,-1.208 0.003,-0.768 -0.479,-1.09 -1.35,-1.091 -2.968,-0.002 -9.619,-0.013 -9.619,-0.013 v -0.591 c 0,0 5.052,-0.016 7.225,-0.016 0.888,-0.002 1.354,-0.416 1.351,-1.193 -0.006,-0.761 -0.492,-1.196 -1.361,-1.196 -3.473,-0.005 -10.86,-0.003 -11.0829995,-0.003 -0.022,-0.047 -0.045,-0.094 -0.069,-0.139 0.3939995,-0.319 2.0409995,-1.626 2.4149995,-2.017 0.469,-0.4870005 0.519,-1.1650005 0.162,-1.6040005 -0.414,-0.511 -0.973,-0.5 -1.48,-0.236 -1.4609995,0.764 -6.5999995,3.6430005 -7.7329995,4.2710005 -0.9,0.499 -1.516,1.253 -1.882,2.19 -0.37000002,0.95 -0.17,2.01 -0.166,2.979 0.004,0.718 -0.27300002,1.345 -0.055,2.063 0.629,2.087 2.425,3.312 4.859,3.318 4.6179995,0.014 9.2379995,-0.139 13.8569995,-0.158 0.755,-0.004 1.171,-0.301 1.182,-1.033 0.012,-0.754 -0.423,-0.969 -1.183,-0.973 -1.778,-0.01 -5.824,-0.004 -6.04,-0.004 10e-4,-0.084 0.003,-0.586 10e-4,-0.67 z"},TASK_TYPE_INSTANTIATING_SEND:{d:"m {mx},{my} l 0,8.4 l 12.6,0 l 0,-8.4 z l 6.3,3.6 l 6.3,-3.6"},TASK_TYPE_SERVICE:{d:"m {mx},{my} v -1.71335 c 0.352326,-0.0705 0.703932,-0.17838 1.047628,-0.32133 0.344416,-0.14465 0.665822,-0.32133 0.966377,-0.52145 l 1.19431,1.18005 1.567487,-1.57688 -1.195028,-1.18014 c 0.403376,-0.61394 0.683079,-1.29908 0.825447,-2.01824 l 1.622133,-0.01 v -2.2196 l -1.636514,0.01 c -0.07333,-0.35153 -0.178319,-0.70024 -0.323564,-1.04372 -0.145244,-0.34406 -0.321407,-0.6644 -0.522735,-0.96217 l 1.131035,-1.13631 -1.583305,-1.56293 -1.129598,1.13589 c -0.614052,-0.40108 -1.302883,-0.68093 -2.022633,-0.82247 l 0.0093,-1.61852 h -2.241173 l 0.0042,1.63124 c -0.353763,0.0736 -0.705369,0.17977 -1.049785,0.32371 -0.344415,0.14437 -0.665102,0.32092 -0.9635006,0.52046 l -1.1698628,-1.15823 -1.5667691,1.5792 1.1684265,1.15669 c -0.4026573,0.61283 -0.68308,1.29797 -0.8247287,2.01713 l -1.6588041,0.003 v 2.22174 l 1.6724648,-0.006 c 0.073327,0.35077 0.1797598,0.70243 0.3242851,1.04472 0.1452428,0.34448 0.3214064,0.6644 0.5227339,0.96066 l -1.1993431,1.19723 1.5840256,1.56011 1.1964668,-1.19348 c 0.6140517,0.40346 1.3028827,0.68232 2.0233517,0.82331 l 7.19e-4,1.69892 h 2.226848 z m 0.221462,-3.9957 c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z"},TASK_TYPE_SERVICE_FILL:{d:"m {mx},{my} c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z"},TASK_TYPE_BUSINESS_RULE_HEADER:{d:"m {mx},{my} 0,4 20,0 0,-4 z"},TASK_TYPE_BUSINESS_RULE_MAIN:{d:"m {mx},{my} 0,12 20,0 0,-12 zm 0,8 l 20,0 m -13,-4 l 0,8"},MESSAGE_FLOW_MARKER:{d:"m {mx},{my} m -10.5 ,-7 l 0,14 l 21,0 l 0,-14 z l 10.5,6 l 10.5,-6"}},this.getRawPath=function(e){return this.pathMap[e].d},this.getScaledPath=function(e,t){var n,i,r=this.pathMap[e];t.abspos?(n=t.abspos.x,i=t.abspos.y):(n=t.containerWidth*t.position.mx,i=t.containerHeight*t.position.my);var o={};if(t.position){for(var a=t.containerHeight/r.height*t.yScaleFactor,s=t.containerWidth/r.width*t.xScaleFactor,c=0;c=a.x&&c<=a.x+a.width&&p>=a.y&&p<=a.y+a.height||(n=this._canvas.findRoot(n))),this._canvas.addShape(i,n,o)}else{if(!Ar(t,"bpmndi:BPMNEdge"))throw new Error(l("unknown di {di} for element {semantic}",{di:_r(t),semantic:_r(e)}));var m=this._getSource(e),v=this._getTarget(e);r=n&&(n.hidden||n.collapsed),i=this._elementFactory.createConnection(wo(e,t,{hidden:r,source:m,target:v,waypoints:So(t,m,v)})),Ar(e,"bpmn:DataAssociation")&&(n=this._canvas.findRoot(n)),this._canvas.addConnection(i,n,o)}return go(e)&&Yr(i)&&this.addLabel(e,t,i),this._eventBus.fire("bpmnElement.added",{element:i}),i},Ao.prototype._attachBoundary=function(e,t){var n=this._translate,i=e.attachedToRef;if(!i)throw new Error(n("missing {semantic}#attachedToRef",{semantic:_r(e)}));var r=this._elementRegistry.get(i.id),o=r&&r.attachers;if(!r)throw Co(n,e,i,"attachedToRef");t.host=r,o||(r.attachers=o=[]),-1===o.indexOf(t)&&o.push(t)},Ao.prototype.addLabel=function(e,t,n){var i,r,o;return i=function(e,t){var n,i,r,o=e.label;return o&&o.bounds?(r=o.bounds,i={width:Math.max(yo.width,r.width),height:r.height},n={x:r.x+r.width/2,y:r.y+r.height/2}):(n=_o(t),i=yo),F({x:n.x-i.width/2,y:n.y-i.height/2},i)}(t,n),(r=Yr(n))&&(i=this._textRenderer.getExternalLabelBounds(i,r)),o=this._elementFactory.createLabel(wo(e,t,{id:e.id+"_label",labelTarget:n,type:"label",hidden:n.hidden||!Yr(n),x:Math.round(i.x),y:Math.round(i.y),width:Math.round(i.width),height:Math.round(i.height)})),this._canvas.addShape(o,n.parent)},Ao.prototype._getEnd=function(e,t){var n,i,r=e.$type,o=this._translate;if(i=e[t+"Ref"],"source"===t&&"bpmn:DataInputAssociation"===r&&(i=i&&i[0]),("source"===t&&"bpmn:DataOutputAssociation"===r||"target"===t&&"bpmn:DataInputAssociation"===r)&&(i=e.$parent),n=i&&this._getElement(i))return n;throw i?Co(o,e,i,t+"Ref"):new Error(o("{semantic}#{side} Ref not specified",{semantic:_r(e),side:t}))},Ao.prototype._getSource=function(e){return this._getEnd(e,"source")},Ao.prototype._getTarget=function(e){return this._getEnd(e,"target")},Ao.prototype._getElement=function(e){return this._elementRegistry.get(e.id)};var Ro={__depends__:[mo,{__depends__:[vo],bpmnImporter:["type",Ao]}]};function To(e){e&&"function"==typeof e.stopPropagation&&e.stopPropagation()}function Po(e){return e.originalEvent||e.srcEvent}function Do(e,t){To(e),To(Po(e))}function ko(e){return e.pointers&&e.pointers.length&&(e=e.pointers[0]),e.touches&&e.touches.length&&(e=e.touches[0]),e?{x:e.clientX,y:e.clientY}:null}function Mo(e,t){return(Po(e)||e).button===t}function Bo(e){return Mo(e,0)}function No(e){var t=Po(e)||e;return!!Bo(e)&&(/mac/i.test(navigator.platform)?t.metaKey:t.ctrlKey)}function Oo(e){var t=Po(e)||e;return Bo(e)&&t.shiftKey}function jo(e){return!0}function Lo(e){return Bo(e)||function(e){return Mo(e,1)}(e)}function Io(e,t,n){var i=this;function r(n,i,r){var o,a;(function(e,t){return!(c[e]||Bo)(t)})(n,i)||(r?a=t.getGraphics(r):(o=i.delegateTarget||i.target)&&(a=o,r=t.get(a)),a&&r&&!1===e.fire(n,{element:r,gfx:a,originalEvent:i})&&(i.stopPropagation(),i.preventDefault()))}var o={};function a(e){return o[e]}var s={click:"element.click",contextmenu:"element.contextmenu",dblclick:"element.dblclick",mousedown:"element.mousedown",mousemove:"element.mousemove",mouseover:"element.hover",mouseout:"element.out",mouseup:"element.mouseup"},c={"element.contextmenu":jo,"element.mousedown":Lo,"element.mouseup":Lo,"element.click":Lo,"element.dblclick":Lo};function p(e,t,n,i){var a=o[n]=function(e){r(n,e)};i&&(c[n]=i),a.$delegate=de.bind(e,"svg, .djs-element",t,a)}function l(e,t,n){var i=a(n);i&&de.unbind(e,t,i.$delegate)}e.on("canvas.destroy",(function(e){var t;t=e.svg,g(s,(function(e,n){l(t,n,e)}))})),e.on("canvas.init",(function(e){var t;t=e.svg,g(s,(function(e,n){p(t,n,e)}))})),e.on(["shape.added","connection.added"],(function(t){var n=t.element,i=t.gfx;e.fire("interactionEvents.createHit",{element:n,gfx:i})})),e.on(["shape.changed","connection.changed"],500,(function(t){var n=t.element,i=t.gfx;e.fire("interactionEvents.updateHit",{element:n,gfx:i})})),e.on("interactionEvents.createHit",500,(function(e){var t=e.element,n=e.gfx;i.createDefaultHit(t,n)})),e.on("interactionEvents.updateHit",(function(e){var t=e.element,n=e.gfx;i.updateDefaultHit(t,n)}));var u=f("djs-hit djs-hit-stroke"),h=f("djs-hit djs-hit-click-stroke"),d={all:f("djs-hit djs-hit-all"),"click-stroke":h,stroke:u,"no-move":f("djs-hit djs-hit-no-move")};function f(e,t){return t=F({stroke:"white",strokeWidth:15},t||{}),n.cls(e,["no-fill","no-border"],t)}function m(e,t){var n=d[t];if(!n)throw new Error("invalid hit type <"+t+">");return Se(e,n),e}function v(e,t){_e(e,t)}this.removeHits=function(e){g(ge(".djs-hit",e),ke)},this.createDefaultHit=function(e,t){var n,i=e.waypoints,r=e.isFrame;return i?this.createWaypointsHit(t,i):(n=r?"stroke":"all",this.createBoxHit(t,n,{width:e.width,height:e.height}))},this.createWaypointsHit=function(e,t){var n=lt(t);return m(n,"stroke"),v(e,n),n},this.createBoxHit=function(e,t,n){n=F({x:0,y:0},n);var i=Le("rect");return m(i,t),Se(i,n),v(e,i),i},this.updateDefaultHit=function(e,t){var n=ye(".djs-hit",t);if(n)return e.waypoints?function(e,t){Se(e,{points:pt(t)})}(n,e.waypoints):Se(n,{width:e.width,height:e.height}),n},this.fire=r,this.triggerMouseEvent=function(e,t,n){var i=s[e];if(!i)throw new Error("unmapped DOM event name <"+e+">");return r(i,t,n)},this.mouseHandler=a,this.registerEvent=p,this.unregisterEvent=l}Io.$inject=["eventBus","elementRegistry","styles"];var Fo={__init__:["interactionEvents"],interactionEvents:["type",Io]};function zo(e,t,n){this.offset=6;var i=t.cls("djs-outline",["no-fill"]),r=this;function o(e,t){var n=Le("rect");return Se(n,F({x:10,y:10,rx:3,width:100,height:100},i)),_e(e,n),n}e.on(["shape.added","shape.changed"],500,(function(e){var t=e.element,n=e.gfx,i=ye(".djs-outline",n);i||(i=o(n)),r.updateShapeOutline(i,t)})),e.on(["connection.added","connection.changed"],(function(e){var t=e.element,n=e.gfx,i=ye(".djs-outline",n);i||(i=o(n)),r.updateConnectionOutline(i,t)}))}zo.prototype.updateShapeOutline=function(e,t){Se(e,{x:-this.offset,y:-this.offset,width:t.width+2*this.offset,height:t.height+2*this.offset})},zo.prototype.updateConnectionOutline=function(e,t){var n=vt(t);Se(e,{x:n.x-this.offset,y:n.y-this.offset,width:n.width+2*this.offset,height:n.height+2*this.offset})},zo.$inject=["eventBus","styles","elementRegistry"];var $o={__init__:["outline"],outline:["type",zo]};function Ho(e,t){this._eventBus=e,this._canvas=t,this._selectedElements=[];var n=this;e.on(["shape.remove","connection.remove"],(function(e){var t=e.element;n.deselect(t)})),e.on(["diagram.clear","root.set"],(function(e){n.select(null)}))}Ho.$inject=["eventBus","canvas"],Ho.prototype.deselect=function(e){var t=this._selectedElements,n=t.indexOf(e);if(-1!==n){var i=t.slice();t.splice(n,1),this._eventBus.fire("selection.changed",{oldSelection:i,newSelection:t})}},Ho.prototype.get=function(){return this._selectedElements},Ho.prototype.isSelected=function(e){return-1!==this._selectedElements.indexOf(e)},Ho.prototype.select=function(e,t){var n=this._selectedElements,i=n.slice();p(e)||(e=e?[e]:[]);var r=this._canvas,o=r.getRootElement();e=e.filter((function(e){var t=r.findRoot(e);return o===t})),t?g(e,(function(e){-1===n.indexOf(e)&&n.push(e)})):this._selectedElements=n=e.slice(),this._eventBus.fire("selection.changed",{oldSelection:i,newSelection:n})};var Go="hover",Vo="selected";function Wo(e,t,n){this._canvas=e;var i=this;function r(t,n){e.addMarker(t,n)}function o(t,n){e.removeMarker(t,n)}this._multiSelectionBox=null,t.on("element.hover",(function(e){r(e.element,Go)})),t.on("element.out",(function(e){o(e.element,Go)})),t.on("selection.changed",(function(e){var t=e.oldSelection,n=e.newSelection;g(t,(function(e){-1===n.indexOf(e)&&o(e,Vo)})),g(n,(function(e){-1===t.indexOf(e)&&r(e,Vo)})),i._updateSelectionOutline(n)})),t.on("element.changed",(function(e){n.isSelected(e.element)&&i._updateSelectionOutline(n.get())}))}function Uo(e,t,n,i){e.on("create.end",500,(function(e){var n=e.context,i=n.canExecute,r=n.elements,o=(n.hints||{}).autoSelect;if(i){if(!1===o)return;p(o)?t.select(o):t.select(r.filter(qo))}})),e.on("connect.end",500,(function(e){var n=e.context.connection;n&&t.select(n)})),e.on("shape.move.end",500,(function(e){var n=e.previousSelection||[],r=i.get(e.context.shape.id);m(n,(function(e){return r.id===e.id}))||t.select(r)})),e.on("element.click",(function(e){if(Bo(e)){var i=e.element;i===n.getRootElement()&&(i=null);var r=t.isSelected(i),o=t.get().length>1,a=No(e)||Oo(e);if(r&&o)return a?t.deselect(i):t.select(i);r?t.deselect(i):t.select(i,a)}}))}function qo(e){return!e.hidden}Wo.$inject=["canvas","eventBus","selection"],Wo.prototype._updateSelectionOutline=function(e){var t=this._canvas.getLayer("selectionOutline");Me(t);var n=e.length>1;if(Pe(this._canvas.getContainer())[n?"add":"remove"]("djs-multi-select"),n){var i=function(e){return{x:e.x-6,y:e.y-6,width:e.width+12,height:e.height+12}}(vt(e)),r=Le("rect");Se(r,F({rx:3},i)),Pe(r).add("djs-selection-outline"),_e(t,r)}},Uo.$inject=["eventBus","selection","canvas","elementRegistry"];var Ko={__init__:["selectionVisuals","selectionBehavior"],__depends__:[Fo,$o],selection:["type",Ho],selectionVisuals:["type",Wo],selectionBehavior:["type",Uo]};function Yo(e){this._counter=0,this._prefix=(e?e+"-":"")+Math.floor(1e9*Math.random())+"-"}Yo.prototype.next=function(){return this._prefix+ ++this._counter};var Xo=new Yo("ov");function Zo(e,t,n,i){var r,o;this._eventBus=t,this._canvas=n,this._elementRegistry=i,this._ids=Xo,this._overlayDefaults=F({show:null,scale:!0},e&&e.defaults),this._overlays={},this._overlayContainers=[],this._overlayRoot=(r=n.getContainer(),K(o=fe('
    '),{position:"absolute",width:0,height:0}),r.insertBefore(o,r.firstChild),o),this._init()}function Qo(e,t,n){K(e,{left:t+"px",top:n+"px"})}function Jo(e,t){e.style.display=!1===t?"none":""}function ea(e,t){e.style["transform-origin"]="top left",["","-ms-","-webkit-"].forEach((function(n){e.style[n+"transform"]=t}))}Zo.$inject=["config.overlays","eventBus","canvas","elementRegistry"],Zo.prototype.get=function(e){if(d(e)&&(e={id:e}),d(e.element)&&(e.element=this._elementRegistry.get(e.element)),e.element){var t=this._getOverlayContainer(e.element,!0);return t?e.type?y(t.overlays,k({type:e.type})):t.overlays.slice():[]}return e.type?y(this._overlays,k({type:e.type})):e.id?this._overlays[e.id]:null},Zo.prototype.add=function(e,t,n){if(l(t)&&(n=t,t=null),e.id||(e=this._elementRegistry.get(e)),!n.position)throw new Error("must specifiy overlay position");if(!n.html)throw new Error("must specifiy overlay html");if(!e)throw new Error("invalid element specified");var i=this._ids.next();return n=F({},this._overlayDefaults,n,{id:i,type:t,element:e,html:n.html}),this._addOverlay(n),i},Zo.prototype.remove=function(e){var t=this.get(e)||[];p(t)||(t=[t]);var n=this;g(t,(function(e){var t=n._getOverlayContainer(e.element,!0);if(e&&(be(e.html),be(e.htmlContainer),delete e.htmlContainer,delete e.element,delete n._overlays[e.id]),t){var i=t.overlays.indexOf(e);-1!==i&&t.overlays.splice(i,1)}}))},Zo.prototype.show=function(){Jo(this._overlayRoot)},Zo.prototype.hide=function(){Jo(this._overlayRoot,!1)},Zo.prototype.clear=function(){this._overlays={},this._overlayContainers=[],ne(this._overlayRoot)},Zo.prototype._updateOverlayContainer=function(e){var t=e.element,n=e.html,i=t.x,r=t.y;if(t.waypoints){var o=vt(t);i=o.x,r=o.y}Qo(n,i,r),Y(e.html,"data-container-id",t.id)},Zo.prototype._updateOverlay=function(e){var t,n,i=e.position,r=e.htmlContainer,o=e.element,a=i.left,s=i.top;void 0!==i.right&&(t=o.waypoints?vt(o).width:o.width,a=-1*i.right+t);void 0!==i.bottom&&(n=o.waypoints?vt(o).height:o.height,s=-1*i.bottom+n);Qo(r,a||0,s||0),this._updateOverlayVisibilty(e,this._canvas.viewbox())},Zo.prototype._createOverlayContainer=function(e){var t=fe('
    ');K(t,{position:"absolute"}),this._overlayRoot.appendChild(t);var n={html:t,element:e,overlays:[]};return this._updateOverlayContainer(n),this._overlayContainers.push(n),n},Zo.prototype._updateRoot=function(e){var t=e.scale||1,n="matrix("+[t,0,0,t,-1*e.x*t,-1*e.y*t].join(",")+")";ea(this._overlayRoot,n)},Zo.prototype._getOverlayContainer=function(e,t){var n=m(this._overlayContainers,(function(t){return t.element===e}));return n||t?n:this._createOverlayContainer(e)},Zo.prototype._addOverlay=function(e){var t,n,i=e.id,r=e.element,o=e.html;o.get&&o.constructor.prototype.jquery&&(o=o.get(0)),d(o)&&(o=fe(o)),n=this._getOverlayContainer(r),K(t=fe('
    '),{position:"absolute"}),t.appendChild(o),e.type&&ee(t).add("djs-overlay-"+e.type),Jo(t,this._canvas.findRoot(r)===this._canvas.getRootElement()),e.htmlContainer=t,n.overlays.push(e),n.html.appendChild(t),this._overlays[i]=e,this._updateOverlay(e),this._updateOverlayVisibilty(e,this._canvas.viewbox())},Zo.prototype._updateOverlayVisibilty=function(e,t){var n=e.show,i=this._canvas.findRoot(e.element),r=n&&n.minZoom,o=n&&n.maxZoom,a=e.htmlContainer,c=!0;(i!==this._canvas.getRootElement()||n&&(s(r)&&r>t.scale||s(o)&&oi&&(r=(1/t.scale||1)*i)),s(r)&&(c="scale("+r+","+r+")"),ea(a,c)},Zo.prototype._updateOverlaysVisibilty=function(e){var t=this;g(this._overlays,(function(n){t._updateOverlayVisibilty(n,e)}))},Zo.prototype._init=function(){var e=this._eventBus,t=this;e.on("canvas.viewbox.changing",(function(e){t.hide()})),e.on("canvas.viewbox.changed",(function(e){var n;n=e.viewbox,t._updateRoot(n),t._updateOverlaysVisibilty(n),t.show()})),e.on(["shape.remove","connection.remove"],(function(e){var n=e.element;g(t.get({element:n}),(function(e){t.remove(e.id)}));var i=t._getOverlayContainer(n);if(i){be(i.html);var r=t._overlayContainers.indexOf(i);-1!==r&&t._overlayContainers.splice(r,1)}})),e.on("element.changed",500,(function(e){var n=e.element,i=t._getOverlayContainer(n,!0);i&&(g(i.overlays,(function(e){t._updateOverlay(e)})),t._updateOverlayContainer(i))})),e.on("element.marker.update",(function(e){var n=t._getOverlayContainer(e.element,!0);n&&ee(n.html)[e.add?"add":"remove"](e.marker)})),e.on("root.set",(function(){t._updateOverlaysVisibilty(t._canvas.viewbox())})),e.on("diagram.clear",this.clear,this)};var ta={__init__:["overlays"],overlays:["type",Zo]};function na(e,t,n,i){e.on("element.changed",(function(i){var r=i.element;(r.parent||r===t.getRootElement())&&(i.gfx=n.getGraphics(r)),i.gfx&&e.fire(yt(r)+".changed",i)})),e.on("elements.changed",(function(t){var n=t.elements;n.forEach((function(t){e.fire("element.changed",{element:t})})),i.updateContainments(n)})),e.on("shape.changed",(function(e){i.update("shape",e.element,e.gfx)})),e.on("connection.changed",(function(e){i.update("connection",e.element,e.gfx)}))}na.$inject=["eventBus","canvas","elementRegistry","graphicsFactory"];var ia={__init__:["changeSupport"],changeSupport:["type",na]};function ra(e){this._eventBus=e}ra.$inject=["eventBus"],ra.prototype.on=function(e,t,n,i,r,o){if((h(t)||u(t))&&(o=r,r=i,i=n,n=t,t=null),h(n)&&(o=r,r=i,i=n,n=1e3),l(r)&&(o=r,r=!1),!h(i))throw new Error("handlerFn must be a function");p(e)||(e=[e]);var a=this._eventBus;g(e,(function(e){var s=["commandStack",e,t].filter((function(e){return e})).join(".");a.on(s,n,r?function(e,t){return function(n){return e.call(t||null,n.context,n.command,n)}}(i,o):i,o)}))};function oa(e,t){t.invoke(ra,this),this.executed((function(t){var n=t.context;n.rootElement?e.setRootElement(n.rootElement):n.rootElement=e.getRootElement()})),this.revert((function(t){var n=t.context;n.rootElement&&e.setRootElement(n.rootElement)}))}g(["canExecute","preExecute","preExecuted","execute","executed","postExecute","postExecuted","revert","reverted"],(function(e){ra.prototype[e]=function(t,n,i,r,o){(h(t)||u(t))&&(o=r,r=i,i=n,n=t,t=null),this.on(t,e,n,i,r,o)}})),e(oa,ra),oa.$inject=["canvas","injector"];var aa={__init__:["rootElementsBehavior"],rootElementsBehavior:["type",oa]},sa={exports:{}}; +/*! https://mths.be/cssescape v1.5.1 by @mathias | MIT license */ +!function(e,t){var n;n=Dt,e.exports=function(e){if(e.CSS&&e.CSS.escape)return e.CSS.escape;var t=function(e){if(0==arguments.length)throw new TypeError("`CSS.escape` requires an argument.");for(var t,n=String(e),i=n.length,r=-1,o="",a=n.charCodeAt(0);++r=1&&t<=31||127==t||0==r&&t>=48&&t<=57||1==r&&t>=48&&t<=57&&45==a?"\\"+t.toString(16)+" ":0==r&&1==i&&45==t||!(t>=128||45==t||95==t||t>=48&&t<=57||t>=65&&t<=90||t>=97&&t<=122)?"\\"+n.charAt(r):n.charAt(r):o+="�";return o};return e.CSS||(e.CSS={}),e.CSS.escape=t,t}(n)}(sa);var ca=sa.exports,pa={"&":"&","<":"<",">":">",'"':""","'":"'"};function la(e){return(e=""+e)&&e.replace(/[&<>"']/g,(function(e){return pa[e]}))}var ua="_plane";function ha(e){return function(e){return e.replace(new RegExp("_plane$"),"")}(e.id)}function da(e){var t=e.id;return Ar(e,"bpmn:SubProcess")?va(t):t}function fa(e){return va(e)}function ma(e){return Ar(Pr(e),"bpmndi:BPMNPlane")}function va(e){return e+ua}function ya(e,t,n,i){var r=fe('
      '),o=i.getContainer(),a=ee(o);o.appendChild(r);var s=[];function c(e){e&&(s=function(e){for(var t=Tr(e),n=[],i=t;i;i=i.$parent)(Ar(i,"bpmn:SubProcess")||Ar(i,"bpmn:Process"))&&n.push(i);return n.reverse()}(e));var n=s.map((function(e){var n=la(e.name||e.id),r=fe('
    • '+n+"
    • "),o=i.findRoot(da(e))||i.findRoot(e.id);if(!o&&Ar(e,"bpmn:Process")){var a=t.find((function(t){var n=Tr(t);return n&&n.processRef&&n.processRef===e}));o=i.findRoot(a.id)}return r.addEventListener("click",(function(){i.setRootElement(o)})),r}));r.innerHTML="";var o=n.length>1;a.toggle("bjs-breadcrumbs-shown",o),n.forEach((function(e){r.appendChild(e)}))}e.on("element.changed",(function(e){var t=Tr(e.element);m(s,(function(e){return e===t}))&&c()})),e.on("root.set",(function(e){c(e.element)}))}function ga(e,t){var n=null,i=new ba;e.on("root.set",(function(e){var r=e.element,o=t.viewbox(),a=i.get(r);if(i.set(n,{x:o.x,y:o.y,zoom:o.scale}),n=r,!Ar(r,"bpmn:Collaboration")||a){a=a||{x:0,y:0,zoom:1};var s=(o.x-a.x)*o.scale,c=(o.y-a.y)*o.scale;0===s&&0===c||t.scroll({dx:s,dy:c}),a.zoom!==o.scale&&t.zoom(a.zoom,{x:0,y:0})}})),e.on("diagram.clear",(function(){i.clear(),n=null}))}function ba(){this._entries=[],this.set=function(e,t){var n=!1;for(var i in this._entries)if(this._entries[i][0]===e){this._entries[i][1]=t,n=!0;break}n||this._entries.push([e,t])},this.get=function(e){for(var t in this._entries)if(this._entries[t][0]===e)return this._entries[t][1];return null},this.clear=function(){this._entries.length=0},this.remove=function(e){var t=-1;for(var n in this._entries)if(this._entries[n][0]===e){t=n;break}-1!==t&&this._entries.splice(t,1)}}ya.$inject=["eventBus","elementRegistry","overlays","canvas"],ga.$inject=["eventBus","canvas"];var xa=180,_a=160;function Ea(e,t){this._eventBus=e,this._moddle=t;var n=this;e.on("import.render.start",1500,(function(e,t){n.handleImport(t.definitions)}))}function wa(e){return Ar(e,"bpmndi:BPMNDiagram")?e:wa(e.$parent)}Ea.prototype.handleImport=function(e){if(e.diagrams){var t=this;this._definitions=e,this._processToDiagramMap={},e.diagrams.forEach((function(e){e.plane&&e.plane.bpmnElement&&(t._processToDiagramMap[e.plane.bpmnElement.id]=e)}));var n=[];e.diagrams.forEach((function(e){var i=t.createNewDiagrams(e.plane);Array.prototype.push.apply(n,i)})),n.forEach((function(e){t.movePlaneElementsToOrigin(e.plane)}))}},Ea.prototype.createNewDiagrams=function(e){var t=this,n=[],i=[];e.get("planeElement").forEach((function(t){var r=t.bpmnElement;if(r){var o=r.$parent;Ar(r,"bpmn:SubProcess")&&!t.isExpanded&&n.push(r),function(e,t){var n=e.$parent;if(!Ar(n,"bpmn:SubProcess")||n===t.bpmnElement)return!1;if(Rr(e,["bpmn:DataInputAssociation","bpmn:DataOutputAssociation"]))return!1;return!0}(r,e)&&i.push({diElement:t,parent:o})}}));var r=[];return n.forEach((function(e){if(!t._processToDiagramMap[e.id]){var n=t.createDiagram(e);t._processToDiagramMap[e.id]=n,r.push(n)}})),i.forEach((function(e){for(var i=e.diElement,r=e.parent;r&&-1===n.indexOf(r);)r=r.$parent;if(r){var o=t._processToDiagramMap[r.id];t.moveToDiPlane(i,o.plane)}})),r},Ea.prototype.movePlaneElementsToOrigin=function(e){var t=e.get("planeElement"),n=function(e){var t={top:1/0,right:-1/0,bottom:-1/0,left:1/0};return e.planeElement.forEach((function(e){if(e.bounds){var n=mn(e.bounds);t.top=Math.min(n.top,t.top),t.left=Math.min(n.left,t.left)}})),vn(t)}(e),i=n.x-xa,r=n.y-_a;t.forEach((function(e){e.waypoint?e.waypoint.forEach((function(e){e.x=e.x-i,e.y=e.y-r})):e.bounds&&(e.bounds.x=e.bounds.x-i,e.bounds.y=e.bounds.y-r)}))},Ea.prototype.moveToDiPlane=function(e,t){var n=wa(e).plane.get("planeElement");n.splice(n.indexOf(e),1),t.get("planeElement").push(e)},Ea.prototype.createDiagram=function(e){var t=this._moddle.create("bpmndi:BPMNPlane",{bpmnElement:e}),n=this._moddle.create("bpmndi:BPMNDiagram",{plane:t});return t.$parent=n,t.bpmnElement=e,n.$parent=this._definitions,this._definitions.diagrams.push(n),n},Ea.$inject=["eventBus","moddle"];var Sa=250;function Ca(e,t,n,i){ra.call(this,t),this._canvas=e,this._eventBus=t,this._elementRegistry=n,this._overlays=i;var r=this;this.executed("shape.toggleCollapse",Sa,(function(e){var t=e.shape;r.canDrillDown(t)?r.addOverlay(t):r.removeOverlay(t)}),!0),this.reverted("shape.toggleCollapse",Sa,(function(e){var t=e.shape;r.canDrillDown(t)?r.addOverlay(t):r.removeOverlay(t)}),!0),this.executed(["shape.create","shape.move","shape.delete"],Sa,(function(e){var t=e.oldParent,n=e.newParent||e.parent,i=e.shape;r.canDrillDown(i)&&r.addOverlay(i),r.updateDrilldownOverlay(t),r.updateDrilldownOverlay(n),r.updateDrilldownOverlay(i)}),!0),this.reverted(["shape.create","shape.move","shape.delete"],Sa,(function(e){var t=e.oldParent,n=e.newParent||e.parent,i=e.shape;r.canDrillDown(i)&&r.addOverlay(i),r.updateDrilldownOverlay(t),r.updateDrilldownOverlay(n),r.updateDrilldownOverlay(i)}),!0),t.on("import.render.complete",(function(){n.filter((function(e){return r.canDrillDown(e)})).map((function(e){r.addOverlay(e)}))}))}e(Ca,ra),Ca.prototype.updateDrilldownOverlay=function(e){var t=this._canvas;if(e){var n=t.findRoot(e);n&&this.updateOverlayVisibility(n)}},Ca.prototype.canDrillDown=function(e){var t=this._canvas;return Ar(e,"bpmn:SubProcess")&&t.findRoot(da(e))},Ca.prototype.updateOverlayVisibility=function(e){var t=this._overlays,n=e.businessObject,i=t.get({element:n.id,type:"drilldown"})[0];if(i){var r=n&&n.flowElements&&n.flowElements.length;ee(i.html).toggle("bjs-drilldown-empty",!r)}},Ca.prototype.addOverlay=function(e){var t=this._canvas,n=this._overlays;n.get({element:e,type:"drilldown"}).length&&this.removeOverlay(e);var i=fe('');i.addEventListener("click",(function(){t.setRootElement(t.findRoot(da(e)))})),n.add(e,"drilldown",{position:{bottom:-7,right:-8},html:i}),this.updateOverlayVisibility(e)},Ca.prototype.removeOverlay=function(e){this._overlays.remove({element:e,type:"drilldown"})},Ca.$inject=["canvas","eventBus","elementRegistry","overlays"];var Aa={__depends__:[ta,ia,aa],__init__:["drilldownBreadcrumbs","drilldownOverlayBehavior","drilldownCentering","subprocessCompatibility"],drilldownBreadcrumbs:["type",ya],drilldownCentering:["type",ga],drilldownOverlayBehavior:["type",Ca],subprocessCompatibility:["type",Ea]};function Ra(e){zr.call(this,e)}e(Ra,zr),Ra.prototype._modules=[Ro,vo,Ko,ta,Aa],Ra.prototype._moddleExtensions={};var Ta=["c","C",67],Pa=["v","V",86],Da=["y","Y",89],ka=["z","Z",90];function Ma(e){return!e.altKey&&(e.ctrlKey||e.metaKey)}function Ba(e,t){return-1!==(e=p(e)?e:[e]).indexOf(t.key)||-1!==e.indexOf(t.keyCode)}function Na(e){return e.shiftKey}var Oa="keyboard.keydown",ja="input-handle-modified-keys";function La(e,t){var n=this;this._config=e||{},this._eventBus=t,this._keydownHandler=this._keydownHandler.bind(this),this._keyupHandler=this._keyupHandler.bind(this),t.on("diagram.destroy",(function(){n._fire("destroy"),n.unbind()})),t.on("diagram.init",(function(){n._fire("init")})),t.on("attach",(function(){e&&e.bindTo&&n.bind(e.bindTo)})),t.on("detach",(function(){n.unbind()}))}La.$inject=["config.keyboard","eventBus"],La.prototype._keydownHandler=function(e){this._keyHandler(e,Oa)},La.prototype._keyupHandler=function(e){this._keyHandler(e,"keyboard.keyup")},La.prototype._keyHandler=function(e,t){if(!this._isEventIgnored(e)){var n={keyEvent:e};this._eventBus.fire(t||Oa,n)&&e.preventDefault()}},La.prototype._isEventIgnored=function(e){return(t=e.target)&&(oe(t,"input, textarea")||"true"===t.contentEditable)&&this._isModifiedKeyIgnored(e);var t},La.prototype._isModifiedKeyIgnored=function(e){return!Ma(e)||-1===this._getAllowedModifiers(e.target).indexOf(e.key)},La.prototype._getAllowedModifiers=function(e){var t=ae(e,"[input-handle-modified-keys]",!0);return!t||this._node&&!this._node.contains(t)?[]:t.getAttribute(ja).split(",")},La.prototype.bind=function(e){this.unbind(),this._node=e,le.bind(e,"keydown",this._keydownHandler,!0),le.bind(e,"keyup",this._keyupHandler,!0),this._fire("bind")},La.prototype.getBinding=function(){return this._node},La.prototype.unbind=function(){var e=this._node;e&&(this._fire("unbind"),le.unbind(e,"keydown",this._keydownHandler,!0),le.unbind(e,"keyup",this._keyupHandler,!0)),this._node=null},La.prototype._fire=function(e){this._eventBus.fire("keyboard."+e,{node:this._node})},La.prototype.addListener=function(e,t,n){h(e)&&(n=t,t=e,e=1e3),this._eventBus.on(n||Oa,e,t)},La.prototype.removeListener=function(e,t){this._eventBus.off(t||Oa,e)},La.prototype.hasModifier=function(e){return e.ctrlKey||e.metaKey||e.shiftKey||e.altKey},La.prototype.isCmd=Ma,La.prototype.isShift=Na,La.prototype.isKey=Ba;function Ia(e,t){var n=this;e.on("editorActions.init",500,(function(e){var i=e.editorActions;n.registerBindings(t,i)}))}Ia.$inject=["eventBus","keyboard"],Ia.prototype.registerBindings=function(e,t){function n(n,i){t.isRegistered(n)&&e.addListener(i)}n("undo",(function(e){if(function(e){return Ma(e)&&!Na(e)&&Ba(ka,e)}(e.keyEvent))return t.trigger("undo"),!0})),n("redo",(function(e){if(function(e){return Ma(e)&&(Ba(Da,e)||Ba(ka,e)&&Na(e))}(e.keyEvent))return t.trigger("redo"),!0})),n("copy",(function(e){if(function(e){return Ma(e)&&Ba(Ta,e)}(e.keyEvent))return t.trigger("copy"),!0})),n("paste",(function(e){if(function(e){return Ma(e)&&Ba(Pa,e)}(e.keyEvent))return t.trigger("paste"),!0})),n("stepZoom",(function(e){var n=e.keyEvent;if(Ba(["+","Add","="],n)&&Ma(n))return t.trigger("stepZoom",{value:1}),!0})),n("stepZoom",(function(e){var n=e.keyEvent;if(Ba(["-","Subtract"],n)&&Ma(n))return t.trigger("stepZoom",{value:-1}),!0})),n("zoom",(function(e){var n=e.keyEvent;if(Ba("0",n)&&Ma(n))return t.trigger("zoom",{value:1}),!0})),n("removeSelection",(function(e){if(Ba(["Backspace","Delete","Del"],e.keyEvent))return t.trigger("removeSelection"),!0}))};var Fa={__init__:["keyboard","keyboardBindings"],keyboard:["type",La],keyboardBindings:["type",Ia]},za={moveSpeed:50,moveSpeedAccelerated:200};function $a(e,t,n){var i=this;this._config=F({},za,e||{}),t.addListener((function(e){var n=e.keyEvent,r=i._config;if(!t.isCmd(n))return;if(t.isKey(["ArrowLeft","Left","ArrowUp","Up","ArrowDown","Down","ArrowRight","Right"],n)){var o,a=t.isShift(n)?r.moveSpeedAccelerated:r.moveSpeed;switch(n.key){case"ArrowLeft":case"Left":o="left";break;case"ArrowUp":case"Up":o="up";break;case"ArrowRight":case"Right":o="right";break;case"ArrowDown":case"Down":o="down"}return i.moveCanvas({speed:a,direction:o}),!0}})),this.moveCanvas=function(e){var t=0,i=0,r=e.speed/Math.min(Math.sqrt(n.viewbox().scale),1);switch(e.direction){case"left":t=r;break;case"up":i=r;break;case"right":t=-r;break;case"down":i=-r}n.scroll({dx:t,dy:i})}}$a.$inject=["config.keyboardMove","keyboard","canvas"];var Ha={__depends__:[Fa],__init__:["keyboardMove"],keyboardMove:["type",$a]},Ga=/^djs-cursor-.*$/;function Va(e){var t=ee(document.body);t.removeMatching(Ga),e&&t.add("djs-cursor-"+e)}function Wa(){Va(null)}function Ua(e,t){function n(){return!1}return t=t||"element.click",e.once(t,5e3,n),function(){e.off(t,n)}}function qa(e){return{x:e.x+e.width/2,y:e.y+e.height/2}}function Ka(e,t){return{x:e.x-t.x,y:e.y-t.y}}function Ya(e,t){var n;function i(i){var r,o=n.start,a=n.button,s=ko(i),c=Ka(s,o);(!n.dragging&&(r=c,Math.sqrt(Math.pow(r.x,2)+Math.pow(r.y,2))>15)&&(n.dragging=!0,0===a&&Ua(e),Va("grab")),n.dragging)&&(c=Ka(s,n.last||n.start),t.scroll({dx:c.x,dy:c.y}),n.last=s);i.preventDefault()}function r(e){le.unbind(document,"mousemove",i),le.unbind(document,"mouseup",r),n=null,Wa()}e.on("element.mousedown",500,(function(e){return function(e){if(ae(e.target,".djs-draggable"))return;var t=e.button;if(t>=2||e.ctrlKey||e.shiftKey||e.altKey)return;return n={button:t,start:ko(e)},le.bind(document,"mousemove",i),le.bind(document,"mouseup",r),!0}(e.originalEvent)})),this.isActive=function(){return!!n}}Ya.$inject=["eventBus","canvas"];var Xa={__init__:["moveCanvas"],moveCanvas:["type",Ya]};function Za(e){return Math.log(e)/Math.log(10)}function Qa(e,t){var n=Za(e.min),i=Za(e.max);return(Math.abs(n)+Math.abs(i))/t}var Ja=Math.sign||function(e){return e>=0?1:-1},es={min:.2,max:4};function ts(e,t,n){e=e||{},this._enabled=!1,this._canvas=n,this._container=n._container,this._handleWheel=L(this._handleWheel,this),this._totalDelta=0,this._scale=e.scale||.75;var i=this;t.on("canvas.init",(function(t){i._init(!1!==e.enabled)}))}ts.$inject=["config.zoomScroll","eventBus","canvas"],ts.prototype.scroll=function(e){this._canvas.scroll(e)},ts.prototype.reset=function(){this._canvas.zoom("fit-viewport")},ts.prototype.zoom=function(e,t){var n=Qa(es,20);this._totalDelta+=e,Math.abs(this._totalDelta)>.1&&(this._zoom(e,t,n),this._totalDelta=0)},ts.prototype._handleWheel=function(e){if(!ae(e.target,".djs-scrollable",!0)){var t=this._container;e.preventDefault();var n,i=e.ctrlKey,r=e.shiftKey,o=-1*this._scale;if(o*=i?0===e.deltaMode?.02:.32:0===e.deltaMode?1:16,i){var a=t.getBoundingClientRect(),s={x:e.clientX-a.left,y:e.clientY-a.top};n=Math.sqrt(Math.pow(e.deltaY,2)+Math.pow(e.deltaX,2))*Ja(e.deltaY)*o,this.zoom(n,s)}else n=r?{dx:o*e.deltaY,dy:0}:{dx:o*e.deltaX,dy:o*e.deltaY},this.scroll(n)}},ts.prototype.stepZoom=function(e,t){var n=Qa(es,10);this._zoom(e,t,n)},ts.prototype._zoom=function(e,t,n){var i=this._canvas,r=e>0?1:-1,o=Za(i.zoom()),a=Math.round(o/n)*n;a+=n*r;var s,c,p=Math.pow(10,a);i.zoom((s=es,c=p,Math.max(s.min,Math.min(s.max,c))),t)},ts.prototype.toggle=function(e){var t=this._container,n=this._handleWheel,i=this._enabled;return void 0===e&&(e=!i),i!==e&&le[e?"bind":"unbind"](t,"wheel",n,!1),this._enabled=e,e},ts.prototype._init=function(e){this.toggle(e)};var ns={__init__:["zoomScroll"],zoomScroll:["type",ts]};function is(e){Ra.call(this,e)}e(is,Ra),is.prototype._navigationModules=[Ha,Xa,ns],is.prototype._modules=[].concat(Ra.prototype._modules,is.prototype._navigationModules);var rs={exports:{}}; +/*! Hammer.JS - v2.0.7 - 2016-04-22 + * http://hammerjs.github.io/ + * + * Copyright (c) 2016 Jorik Tangelder; + * Licensed under the MIT license */!function(e){!function(t,n,i,r){var o,a=["","webkit","Moz","MS","ms","o"],s=n.createElement("div"),c=Math.round,p=Math.abs,l=Date.now;function u(e,t,n){return setTimeout(g(e,n),t)}function h(e,t,n){return!!Array.isArray(e)&&(d(e,n[t],n),!0)}function d(e,t,n){var i;if(e)if(e.forEach)e.forEach(t,n);else if(e.length!==r)for(i=0;i\s*\(/gm,"{anonymous}()@"):"Unknown Stack Trace",o=t.console&&(t.console.warn||t.console.log);return o&&o.call(t.console,r,i),e.apply(this,arguments)}}o="function"!=typeof Object.assign?function(e){if(e===r||null===e)throw new TypeError("Cannot convert undefined or null to object");for(var t=Object(e),n=1;n-1}function C(e){return e.trim().split(/\s+/g)}function A(e,t,n){if(e.indexOf&&!n)return e.indexOf(t);for(var i=0;in[t]})):i.sort()),i}function P(e,t){for(var n,i,o=t[0].toUpperCase()+t.slice(1),s=0;s1&&!n.firstMultiple?n.firstMultiple=H(t):1===o&&(n.firstMultiple=!1);var a=n.firstInput,s=n.firstMultiple,c=s?s.center:a.center,u=t.center=G(i);t.timeStamp=l(),t.deltaTime=t.timeStamp-a.timeStamp,t.angle=q(c,u),t.distance=U(c,u),function(e,t){var n=t.center,i=e.offsetDelta||{},r=e.prevDelta||{},o=e.prevInput||{};1!==t.eventType&&4!==o.eventType||(r=e.prevDelta={x:o.deltaX||0,y:o.deltaY||0},i=e.offsetDelta={x:n.x,y:n.y});t.deltaX=r.x+(n.x-i.x),t.deltaY=r.y+(n.y-i.y)}(n,t),t.offsetDirection=W(t.deltaX,t.deltaY);var h=V(t.deltaTime,t.deltaX,t.deltaY);t.overallVelocityX=h.x,t.overallVelocityY=h.y,t.overallVelocity=p(h.x)>p(h.y)?h.x:h.y,t.scale=s?(d=s.pointers,f=i,U(f[0],f[1],F)/U(d[0],d[1],F)):1,t.rotation=s?function(e,t){return q(t[1],t[0],F)+q(e[1],e[0],F)}(s.pointers,i):0,t.maxPointers=n.prevInput?t.pointers.length>n.prevInput.maxPointers?t.pointers.length:n.prevInput.maxPointers:t.pointers.length,function(e,t){var n,i,o,a,s=e.lastInterval||t,c=t.timeStamp-s.timeStamp;if(8!=t.eventType&&(c>25||s.velocity===r)){var l=t.deltaX-s.deltaX,u=t.deltaY-s.deltaY,h=V(c,l,u);i=h.x,o=h.y,n=p(h.x)>p(h.y)?h.x:h.y,a=W(l,u),e.lastInterval=t}else n=s.velocity,i=s.velocityX,o=s.velocityY,a=s.direction;t.velocity=n,t.velocityX=i,t.velocityY=o,t.direction=a}(n,t);var d,f;var m=e.element;w(t.srcEvent.target,m)&&(m=t.srcEvent.target);t.target=m}(e,n),e.emit("hammer.input",n),e.recognize(n),e.session.prevInput=n}function H(e){for(var t=[],n=0;n=p(t)?e<0?2:4:t<0?8:16}function U(e,t,n){n||(n=I);var i=t[n[0]]-e[n[0]],r=t[n[1]]-e[n[1]];return Math.sqrt(i*i+r*r)}function q(e,t,n){n||(n=I);var i=t[n[0]]-e[n[0]],r=t[n[1]]-e[n[1]];return 180*Math.atan2(r,i)/Math.PI}z.prototype={handler:function(){},init:function(){this.evEl&&_(this.element,this.evEl,this.domHandler),this.evTarget&&_(this.target,this.evTarget,this.domHandler),this.evWin&&_(k(this.element),this.evWin,this.domHandler)},destroy:function(){this.evEl&&E(this.element,this.evEl,this.domHandler),this.evTarget&&E(this.target,this.evTarget,this.domHandler),this.evWin&&E(k(this.element),this.evWin,this.domHandler)}};var K={mousedown:1,mousemove:2,mouseup:4},Y="mousedown",X="mousemove mouseup";function Z(){this.evEl=Y,this.evWin=X,this.pressed=!1,z.apply(this,arguments)}y(Z,z,{handler:function(e){var t=K[e.type];1&t&&0===e.button&&(this.pressed=!0),2&t&&1!==e.which&&(t=4),this.pressed&&(4&t&&(this.pressed=!1),this.callback(this.manager,t,{pointers:[e],changedPointers:[e],pointerType:j,srcEvent:e}))}});var Q={pointerdown:1,pointermove:2,pointerup:4,pointercancel:8,pointerout:8},J={2:O,3:"pen",4:j,5:"kinect"},ee="pointerdown",te="pointermove pointerup pointercancel";function ne(){this.evEl=ee,this.evWin=te,z.apply(this,arguments),this.store=this.manager.session.pointerEvents=[]}t.MSPointerEvent&&!t.PointerEvent&&(ee="MSPointerDown",te="MSPointerMove MSPointerUp MSPointerCancel"),y(ne,z,{handler:function(e){var t=this.store,n=!1,i=e.type.toLowerCase().replace("ms",""),r=Q[i],o=J[e.pointerType]||e.pointerType,a=o==O,s=A(t,e.pointerId,"pointerId");1&r&&(0===e.button||a)?s<0&&(t.push(e),s=t.length-1):12&r&&(n=!0),s<0||(t[s]=e,this.callback(this.manager,r,{pointers:t,changedPointers:[e],pointerType:o,srcEvent:e}),n&&t.splice(s,1))}});var ie={touchstart:1,touchmove:2,touchend:4,touchcancel:8},re="touchstart",oe="touchstart touchmove touchend touchcancel";function ae(){this.evTarget=re,this.evWin=oe,this.started=!1,z.apply(this,arguments)}function se(e,t){var n=R(e.touches),i=R(e.changedTouches);return 12&t&&(n=T(n.concat(i),"identifier",!0)),[n,i]}y(ae,z,{handler:function(e){var t=ie[e.type];if(1===t&&(this.started=!0),this.started){var n=se.call(this,e,t);12&t&&n[0].length-n[1].length==0&&(this.started=!1),this.callback(this.manager,t,{pointers:n[0],changedPointers:n[1],pointerType:O,srcEvent:e})}}});var ce={touchstart:1,touchmove:2,touchend:4,touchcancel:8},pe="touchstart touchmove touchend touchcancel";function le(){this.evTarget=pe,this.targetIds={},z.apply(this,arguments)}function ue(e,t){var n=R(e.touches),i=this.targetIds;if(3&t&&1===n.length)return i[n[0].identifier]=!0,[n,n];var r,o,a=R(e.changedTouches),s=[],c=this.target;if(o=n.filter((function(e){return w(e.target,c)})),1===t)for(r=0;r-1&&i.splice(e,1)}),2500)}}function me(e){for(var t=e.srcEvent.clientX,n=e.srcEvent.clientY,i=0;i-1&&this.requireFail.splice(t,1),this},hasRequireFailures:function(){return this.requireFail.length>0},canRecognizeWith:function(e){return!!this.simultaneous[e.id]},emit:function(e){var t=this,n=this.state;function i(n){t.manager.emit(n,e)}n<8&&i(t.options.event+Te(n)),i(t.options.event),e.additionalEvent&&i(e.additionalEvent),n>=8&&i(t.options.event+Te(n))},tryEmit:function(e){if(this.canEmit())return this.emit(e);this.state=Ae},canEmit:function(){for(var e=0;et.threshold&&r&t.direction},attrTest:function(e){return ke.prototype.attrTest.call(this,e)&&(2&this.state||!(2&this.state)&&this.directionTest(e))},emit:function(e){this.pX=e.deltaX,this.pY=e.deltaY;var t=Pe(e.direction);t&&(e.additionalEvent=this.options.event+t),this._super.emit.call(this,e)}}),y(Be,ke,{defaults:{event:"pinch",threshold:0,pointers:2},getTouchAction:function(){return[_e]},attrTest:function(e){return this._super.attrTest.call(this,e)&&(Math.abs(e.scale-1)>this.options.threshold||2&this.state)},emit:function(e){if(1!==e.scale){var t=e.scale<1?"in":"out";e.additionalEvent=this.options.event+t}this._super.emit.call(this,e)}}),y(Ne,Re,{defaults:{event:"press",pointers:1,time:251,threshold:9},getTouchAction:function(){return[be]},process:function(e){var t=this.options,n=e.pointers.length===t.pointers,i=e.distancet.time;if(this._input=e,!i||!n||12&e.eventType&&!r)this.reset();else if(1&e.eventType)this.reset(),this._timer=u((function(){this.state=8,this.tryEmit()}),t.time,this);else if(4&e.eventType)return 8;return Ae},reset:function(){clearTimeout(this._timer)},emit:function(e){8===this.state&&(e&&4&e.eventType?this.manager.emit(this.options.event+"up",e):(this._input.timeStamp=l(),this.manager.emit(this.options.event,this._input)))}}),y(Oe,ke,{defaults:{event:"rotate",threshold:0,pointers:2},getTouchAction:function(){return[_e]},attrTest:function(e){return this._super.attrTest.call(this,e)&&(Math.abs(e.rotation)>this.options.threshold||2&this.state)}}),y(je,ke,{defaults:{event:"swipe",threshold:10,velocity:.3,direction:30,pointers:1},getTouchAction:function(){return Me.prototype.getTouchAction.call(this)},attrTest:function(e){var t,n=this.options.direction;return 30&n?t=e.overallVelocity:6&n?t=e.overallVelocityX:n&L&&(t=e.overallVelocityY),this._super.attrTest.call(this,e)&&n&e.offsetDirection&&e.distance>this.options.threshold&&e.maxPointers==this.options.pointers&&p(t)>this.options.velocity&&4&e.eventType},emit:function(e){var t=Pe(e.offsetDirection);t&&this.manager.emit(this.options.event+t,e),this.manager.emit(this.options.event,e)}}),y(Le,Re,{defaults:{event:"tap",pointers:1,taps:1,interval:300,time:250,threshold:9,posThreshold:10},getTouchAction:function(){return[xe]},process:function(e){var t=this.options,n=e.pointers.length===t.pointers,i=e.distance1&&(l=!0),e.elements.length})),l)return c[e]=ds(n).center,c;i=t[0],r=ds(t=D(t,(function(e){return e[a]+e[s]}))),c[e]=function(e,t){return Math.round((e[a]+t[a]+t[s])/2)}(i,r)}return c},ys.prototype.trigger=function(e,t){var n,i=this._modeling,r=y(e,(function(e){return!(e.waypoints||e.host||e.labelTarget)}));if(p(n=this._rules.allowed("elements.align",{elements:r}))&&(r=n),!(r.length<2)&&n){var o=D(r,vs[t]),a=this._alignmentPosition(t,o);i.alignElements(o,a)}};var gs={__init__:["alignElements"],alignElements:["type",ys]},bs=".entry";function xs(e,t,n,i){this._canvas=e,this._eventBus=n,this._overlays=i;var r=s(t&&t.scale)?t.scale:{min:1,max:1.5};this._overlaysConfig={scale:r},this._current=null,this._init()}function _s(e,t){return-1!==e.indexOf(t)}xs.$inject=["canvas","config.contextPad","eventBus","overlays"],xs.prototype._init=function(){var e=this;this._eventBus.on("selection.changed",(function(t){var n=t.newSelection,i=n.length?1===n.length?n[0]:n:null;i?e.open(i,!0):e.close()})),this._eventBus.on("elements.changed",(function(t){var n=t.elements,i=e._current;if(i){var r=i.target;E(p(r)?r:[r],(function(e){return _s(n,e)}))&&e.open(r,!0)}}))},xs.prototype.registerProvider=function(e,t){t||(t=e,e=1e3),this._eventBus.on("contextPad.getProviders",e,(function(e){e.providers.push(t)}))},xs.prototype.getEntries=function(e){var t=this._getProviders(),n=p(e)?"getMultiElementContextPadEntries":"getContextPadEntries",i={};return g(t,(function(t){if(h(t[n])){var r=t[n](e);h(r)?i=r(i):g(r,(function(e,t){i[t]=e}))}})),i},xs.prototype.trigger=function(e,t,n){var i,r,o=this._current.target,a=this._current.entries,s=t.delegateTarget||t.target;if(!s)return t.preventDefault();if(i=a[Y(s,"data-action")].action,r=t.originalEvent||t,h(i)){if("click"===e)return i(r,o,n)}else if(i[e])return i[e](r,o,n);t.preventDefault()},xs.prototype.open=function(e,t){!t&&this.isOpen(e)||(this.close(),this._updateAndOpen(e))},xs.prototype._getProviders=function(){var e=this._eventBus.createEvent({type:"contextPad.getProviders",providers:[]});return this._eventBus.fire(e),e.providers},xs.prototype._updateAndOpen=function(e){var t,n=this.getEntries(e),i=this.getPad(e),r=i.html;g(n,(function(e,n){var i,o=e.group||"default",a=fe(e.html||'
      ');Y(a,"data-action",n),(i=ye("[data-group="+ca(o)+"]",r))||(Y(i=fe('
      '),"data-group",o),r.appendChild(i)),i.appendChild(a),e.className&&function(e,t){var n=ee(e);(t=p(t)?t:t.split(/\s+/g)).forEach((function(e){n.add(e)}))}(a,e.className),e.title&&Y(a,"title",e.title),e.imageUrl&&(Y(t=fe(""),"src",e.imageUrl),t.style.width="100%",t.style.height="100%",a.appendChild(t))})),ee(r).add("open"),this._current={target:e,entries:n,pad:i},this._eventBus.fire("contextPad.open",{current:this._current})},xs.prototype.getPad=function(e){if(this.isOpen())return this._current.pad;var t=this,n=this._overlays,i=fe('
      '),r=this._getPosition(e),o=F({html:i},this._overlaysConfig,r);de.bind(i,bs,"click",(function(e){t.trigger("click",e)})),de.bind(i,bs,"dragstart",(function(e){t.trigger("dragstart",e)})),le.bind(i,"mousedown",(function(e){e.stopPropagation()}));var a=this._canvas.getRootElement();this._overlayId=n.add(a,"context-pad",o);var s=n.get(this._overlayId);return this._eventBus.fire("contextPad.create",{target:e,pad:s}),s},xs.prototype.close=function(){this.isOpen()&&(this._overlays.remove(this._overlayId),this._overlayId=null,this._eventBus.fire("contextPad.close",{current:this._current}),this._current=null)},xs.prototype.isOpen=function(e){var t=this._current;if(!t)return!1;if(!e)return!0;var n=t.target;return p(e)===p(n)&&(p(e)?e.length===n.length&&_(e,(function(e){return _s(n,e)})):n===e)},xs.prototype._getPosition=function(e){var t=vt(p(e)?e:[e]);return{position:{left:t.x+t.width+12,top:t.y-6}}};var Es={__depends__:[Fo,ta],contextPad:["type",xs]},ws="data-id",Ss=["contextPad.close","canvas.viewbox.changing","commandStack.changed"];function Cs(e,t,n){var i=s(e&&e.scale)?e.scale:{min:1,max:1.5};this._config={scale:i},this._eventBus=t,this._canvas=n,this._providers={},this._current={}}Cs.$inject=["config.popupMenu","eventBus","canvas"],Cs.prototype.registerProvider=function(e,t,n){n||(n=t,t=1e3),this._eventBus.on("popupMenu.getProviders."+e,t,(function(e){e.providers.push(n)}))},Cs.prototype.isEmpty=function(e,t){if(!e)throw new Error("element parameter is missing");if(!t)throw new Error("providerId parameter is missing");var n=this._getProviders(t);if(!n)return!0;var i=this._getEntries(e,n),r=this._getHeaderEntries(e,n),o=C(i)>0,a=r&&C(r)>0;return!o&&!a},Cs.prototype.open=function(e,t,n){var i=this._getProviders(t);if(!e)throw new Error("Element is missing");if(!i||!i.length)throw new Error("No registered providers for: "+t);if(!n)throw new Error("the position argument is missing");this.isOpen()&&this.close(),this._emit("open");var r=this._current={className:t,element:e,position:n},o=this._getEntries(e,i),a=this._getHeaderEntries(e,i);r.entries=F({},o,a),r.container=this._createContainer(t),C(a)&&r.container.appendChild(this._createEntries(a,"djs-popup-header")),C(o)&&r.container.appendChild(this._createEntries(o,"djs-popup-body"));var s=this._canvas.getContainer();this._attachContainer(r.container,s,n.cursor),this._bindAutoClose()},Cs.prototype.close=function(){this.isOpen()&&(this._emit("close"),this._unbindAutoClose(),be(this._current.container),this._current.container=null)},Cs.prototype.isOpen=function(){return!!this._current.container},Cs.prototype.trigger=function(e){e.preventDefault();var t=Y(e.delegateTarget||e.target,ws),n=this._getEntry(t);if(n.action)return n.action.call(null,e,n)},Cs.prototype._getProviders=function(e){var t=this._eventBus.createEvent({type:"popupMenu.getProviders."+e,providers:[]});return this._eventBus.fire(t),t.providers},Cs.prototype._getEntries=function(e,t){var n={};return g(t,(function(t){if(t.getPopupMenuEntries){var i=t.getPopupMenuEntries(e);h(i)?n=i(n):g(i,(function(e,t){n[t]=e}))}else g(t.getEntries(e),(function(e){var t=e.id;if(!t)throw new Error("every entry must have the id property set");n[t]=$(e,["id"])}))})),n},Cs.prototype._getHeaderEntries=function(e,t){var n={};return g(t,(function(t){if(t.getPopupMenuHeaderEntries){var i=t.getPopupMenuHeaderEntries(e);h(i)?n=i(n):g(i,(function(e,t){n[t]=e}))}else{if(!t.getHeaderEntries)return;g(t.getHeaderEntries(e),(function(e){var t=e.id;if(!t)throw new Error("every entry must have the id property set");n[t]=$(e,["id"])}))}})),n},Cs.prototype._getEntry=function(e){var t=this._current.entries[e];if(!t)throw new Error("entry not found");return t},Cs.prototype._emit=function(e){this._eventBus.fire("popupMenu."+e)},Cs.prototype._createContainer=function(e){var t=fe('
      '),n=this._current.position,i=this._current.className;return K(t,{position:"absolute",left:n.x+"px",top:n.y+"px",visibility:"hidden"}),ee(t).add(i),Y(t,"data-popup",e),t},Cs.prototype._attachContainer=function(e,t,n){var i=this;de.bind(e,".entry","click",(function(e){i.trigger(e)})),this._updateScale(e),t.appendChild(e),n&&this._assureIsInbounds(e,n),K(e,{visibility:"visible"})},Cs.prototype._updateScale=function(e){var t,n,i=this._canvas.zoom(),r=this._config.scale,o=i;!0!==r&&(!1===r?(t=1,n=1):(t=r.min,n=r.max),s(t)&&in&&(o=n)),function(e,t){e.style["transform-origin"]="top left",["","-ms-","-webkit-"].forEach((function(n){e.style[n+"transform"]=t}))}(e,"scale("+o+")")},Cs.prototype._assureIsInbounds=function(e,t){var n,i,r=this._canvas._container.getBoundingClientRect(),o=e.offsetLeft,a=e.offsetTop,s=e.scrollWidth,c=e.scrollHeight,p={},l=t.x-r.left,u=t.y-r.top;o+s>r.width&&(p.x=!0),a+c>r.height&&(p.y=!0),p.x&&p.y?(n=l-s+"px",i=u-c+"px"):p.x?(n=l-s+"px",i=u+"px"):p.y&&u"),i=this;return ee(n).add(t),g(e,(function(e,t){var r=i._createEntry(e,t),o=e.group||"default",a=ye("[data-group="+ca(o)+"]",n);a||(Y(a=fe('
      '),"data-group",o),n.appendChild(a)),a.appendChild(r)})),n},Cs.prototype._createEntry=function(e,t){var n=fe("
      "),i=ee(n);if(i.add("entry"),e.className&&e.className.split(" ").forEach((function(e){i.add(e)})),Y(n,ws,t),e.label){var r=fe("");r.textContent=e.label,n.appendChild(r)}if(e.imageUrl){var o=fe("");Y(o,"src",e.imageUrl),n.appendChild(o)}return!0===e.active&&i.add("active"),!0===e.disabled&&i.add("disabled"),e.title&&(n.title=e.title),n},Cs.prototype._bindAutoClose=function(){this._eventBus.once(Ss,this.close,this)},Cs.prototype._unbindAutoClose=function(){this._eventBus.off(Ss,this.close,this)};var As={__init__:["popupMenu"],popupMenu:["type",Cs]},Rs={align:"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%202000%202000%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M200%20150v1700%22%2F%3E%3Crect%20x%3D%22500%22%20y%3D%22150%22%20width%3D%221300%22%20height%3D%22700%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%22500%22%20y%3D%221150%22%20width%3D%22700%22%20height%3D%22700%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E",bottom:"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M150%201650h1500%22%2F%3E%3Crect%20x%3D%22150%22%20y%3D%22350%22%20width%3D%22600%22%20height%3D%221300%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%221050%22%20y%3D%22850%22%20width%3D%22600%22%20height%3D%22800%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E",center:"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M900%20150v1500%22%2F%3E%3Crect%20x%3D%22250%22%20y%3D%22150%22%20width%3D%221300%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%22500%22%20y%3D%221050%22%20width%3D%22800%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E",left:"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M100%20150v1500%22%2F%3E%3Crect%20x%3D%22100%22%20y%3D%22150%22%20width%3D%221300%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%22100%22%20y%3D%221050%22%20width%3D%22800%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E",right:"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M1650%20150v1500%22%2F%3E%3Crect%20x%3D%22350%22%20y%3D%22150%22%20width%3D%221300%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%22850%22%20y%3D%221050%22%20width%3D%22800%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E",top:"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M150%20150h1500%22%2F%3E%3Crect%20x%3D%22150%22%20y%3D%22150%22%20width%3D%22600%22%20height%3D%221300%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%221050%22%20y%3D%22150%22%20width%3D%22600%22%20height%3D%22800%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E",middle:"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M150%20900h1500%22%2F%3E%3Crect%20x%3D%22150%22%20y%3D%22250%22%20width%3D%22600%22%20height%3D%221300%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%221050%22%20y%3D%22500%22%20width%3D%22600%22%20height%3D%22800%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E"};function Ts(e,t,n,i){e.registerProvider(900,this),this._contextPad=e,this._popupMenu=t,this._translate=n,this._canvas=i}Ts.$inject=["contextPad","popupMenu","translate","canvas"],Ts.prototype.getMultiElementContextPadEntries=function(e){var t={};return this._isAllowed(e)&&F(t,this._getEntries(e)),t},Ts.prototype._isAllowed=function(e){return!this._popupMenu.isEmpty(e,"align-elements")},Ts.prototype._getEntries=function(e){var t=this;return{"align-elements":{group:"align-elements",title:t._translate("Align elements"),imageUrl:Rs.align,action:{click:function(e,n){var i=t._getMenuPosition(n);F(i,{cursor:{x:e.x,y:e.y}}),t._popupMenu.open(n,"align-elements",i)}}}}},Ts.prototype._getMenuPosition=function(e){var t=this._canvas.getContainer(),n=this._contextPad.getPad(e).html,i=t.getBoundingClientRect(),r=n.getBoundingClientRect(),o=r.top-i.top;return{x:r.left-i.left,y:o+r.height+5}};var Ps=["left","center","right","top","middle","bottom"];function Ds(e,t,n,i){this._alignElements=t,this._translate=n,this._popupMenu=e,this._rules=i,e.registerProvider("align-elements",this)}function ks(e){ra.call(this,e),this.init()}function Ms(e){ks.call(this,e)}Ds.$inject=["popupMenu","alignElements","translate","rules"],Ds.prototype.getPopupMenuEntries=function(e){var t={};return this._isAllowed(e)&&F(t,this._getEntries(e)),t},Ds.prototype._isAllowed=function(e){return this._rules.allowed("elements.align",{elements:e})},Ds.prototype._getEntries=function(e){var t=this._alignElements,n=this._translate,i=this._popupMenu,r={};return g(Ps,(function(o){r["align-elements-"+o]={group:"align",title:n("Align elements "+o),className:"bjs-align-elements-menu-entry",imageUrl:Rs[o],action:function(n,r){t.trigger(e,o),i.close()}}})),r},ks.$inject=["eventBus"],e(ks,ra),ks.prototype.addRule=function(e,t,n){var i=this;"string"==typeof e&&(e=[e]),e.forEach((function(e){i.canExecute(e,t,(function(e,t,i){return n(e)}),!0)}))},ks.prototype.init=function(){},Ms.$inject=["eventBus"],e(Ms,ks),Ms.prototype.init=function(){this.addRule("elements.align",(function(e){var t=y(e.elements,(function(e){return!(e.waypoints||e.host||e.labelTarget)}));return!((t=ut(t)).length<2)&&t}))};var Bs={__depends__:[gs,Es,As],__init__:["alignElementsContextPadProvider","alignElementsMenuProvider","bpmnAlignElements"],alignElementsContextPadProvider:["type",Ts],alignElementsMenuProvider:["type",Ds],bpmnAlignElements:["type",Ms]};function Ns(e,t,n,i){for(var r;r=js(e,n,t);)n=i(t,n,r);return n}function Os(e){return function(t,n,i){var r={x:n.x,y:n.y};return["x","y"].forEach((function(o){var a=e[o];if(a){var s="x"===o?"width":"height",c=a.margin,p=a.minDistance;r[o]=c<0?Math.min(i[o]+c-t[s]/2,n[o]-p+c):Math.max(i[o]+i[s]+c+t[s]/2,n[o]+p+c)}})),r}}function js(e,t,n){var i={x:t.x-n.width/2,y:t.y-n.height/2,width:n.width,height:n.height},r=function(e){var t=Ls(e);e.host&&(t=t.concat(Ls(e.host)));e.attachers&&(t=t.concat(e.attachers.reduce((function(e,t){return e.concat(Ls(t))}),[])));return t}(e);return m(r,(function(e){return e!==n&&"intersect"===bn(e,i,10)}))}function Ls(e){return(t=e,t.outgoing.map((function(e){return e.target}))).concat(function(e){return e.incoming.map((function(e){return e.source}))}(e));var t}function Is(){return!0}function Fs(e,t,n){e.on("autoPlace",100,(function(e){var t=e.shape;return function(e,t,n){n||(n={});var i=n.defaultDistance||50,r=gn(e);return{x:mn(e).right+i+t.width/2,y:r.y}}(e.source,t)})),e.on("autoPlace.end",(function(e){n.scrollToElement(e.shape)})),this.append=function(n,i,r){e.fire("autoPlace.start",{source:n,shape:i});var o=e.fire("autoPlace",{source:n,shape:i}),a=t.appendShape(n,i,o,n.parent,r);return e.fire("autoPlace.end",{source:n,shape:a}),a}}function zs(e,t){e.on("autoPlace.end",500,(function(e){t.select(e.shape)}))}Fs.$inject=["eventBus","modeling","canvas"],zs.$inject=["eventBus","selection"];var $s={__init__:["autoPlaceSelectionBehavior"],autoPlace:["type",Fs],autoPlaceSelectionBehavior:["type",zs]};function Hs(e,t){for("string"==typeof t&&(t=[t]);e=e.parent;)if(Rr(e,t))return e;return null}function Gs(e,t){return Ar(t,"bpmn:TextAnnotation")?function(e,t){var n=mn(e),i={x:n.right+t.width/2,y:n.top-50-t.height/2};(function(e){return!!e.waypoints})(e)&&((i=gn(e)).x+=100,i.y-=50);return Ns(e,t,i,Os({y:{margin:-30,minDistance:20}}))}(e,t):Rr(t,["bpmn:DataObjectReference","bpmn:DataStoreReference"])?function(e,t){var n=mn(e),i={x:n.right-10+t.width/2,y:n.bottom+40+t.width/2};return Ns(e,t,i,Os({x:{margin:30,minDistance:30}}))}(e,t):Ar(t,"bpmn:FlowNode")?function(e,t){var n=mn(e),i=gn(e),r=function(e,t){t||(t={});var n=t.defaultDistance||50,i=t.direction||"e",r=t.filter,o=t.getWeight||function(t){return t.source===e?1:-1},a=t.maxDistance||250,s=t.reference||"start";function c(e,t){return"n"===i?"start"===s?mn(e).top-mn(t).bottom:"center"===s?mn(e).top-gn(t).y:mn(e).top-mn(t).top:"w"===i?"start"===s?mn(e).left-mn(t).right:"center"===s?mn(e).left-gn(t).x:mn(e).left-mn(t).left:"s"===i?"start"===s?mn(t).top-mn(e).bottom:"center"===s?gn(t).y-mn(e).bottom:mn(t).bottom-mn(e).bottom:"start"===s?mn(t).left-mn(e).right:"center"===s?gn(t).x-mn(e).right:mn(t).right-mn(e).right}r||(r=Is);var p=e.incoming.filter(r).map((function(t){var n=o(t),i=n<0?c(t.source,e):c(e,t.source);return{id:t.source.id,distance:i,weight:n}})),l=e.outgoing.filter(r).map((function(t){var n=o(t),i=n>0?c(e,t.target):c(t.target,e);return{id:t.target.id,distance:i,weight:n}})),u=x(p.concat(l).reduce((function(e,t){return e[t.id+"__weight_"+t.weight]=t,e}),{}),(function(e,t){var n=t.distance,i=t.weight;return n<0||n>a||(e[String(n)]||(e[String(n)]=0),e[String(n)]+=1*i,(!e.distance||e[e.distance]t.top&&(n=n.concat("n"));e.rightt.left&&(n=n.concat("e"));return n}(z(t,["x","y","width","height"]),n);this.resize(t,n,{autoResize:i});var r=t.parent;r&&this._expand([t],r)}}},qs.prototype.getOffset=function(e){return{top:60,bottom:60,left:100,right:100}},qs.prototype.getPadding=function(e){return{top:2,bottom:2,left:15,right:15}},qs.prototype.resize=function(e,t,n){this._modeling.resizeShape(e,t,null,n)},Ks.$inject=["injector"],e(Ks,qs),Ks.prototype.resize=function(e,t,n){Ar(e,"bpmn:Participant")?this._modeling.resizeLane(e,t,null,n):this._modeling.resizeShape(e,t,null,n)},Ys.$inject=["eventBus"],e(Ys,ks),Ys.prototype.canResize=function(e,t){return!1},e(Xs,Ys),Xs.$inject=["eventBus","modeling"],Xs.prototype.canResize=function(e,t){if(Ar(t.di,"bpmndi:BPMNPlane"))return!1;if(!Ar(t,"bpmn:Participant")&&!Ar(t,"bpmn:Lane")&&!Ar(t,"bpmn:SubProcess"))return!1;var n=!0;return g(e,(function(e){(Ar(e,"bpmn:Lane")||e.labelTarget)&&(n=!1)})),n};var Zs={__init__:["bpmnAutoResize","bpmnAutoResizeProvider"],bpmnAutoResize:["type",Ks],bpmnAutoResizeProvider:["type",Xs]};function Qs(e,t,n){var i,r,o=this,a=n.get("dragging",!1);a&&t.on("drag.start",(function(n){t.once("drag.move",1500,(function(t){!function(t){if(!t.hover){var n=t.originalEvent,i=o._findTargetGfx(n),r=i&&e.get(i);i&&r&&(t.stopPropagation(),a.hover({element:r,gfx:i}),a.move(n))}}(t)}))})),t.on("element.hover",(function(e){i=e.gfx,r=e.element})),t.on("element.hover",1500,(function(e){r&&t.fire("element.out",{element:r,gfx:i})})),t.on("element.out",(function(){i=null,r=null})),this._findTargetGfx=function(e){var t;if(e instanceof MouseEvent)return t=ko(e),function(e){return ae(e,"svg, .djs-element",!0)}(document.elementFromPoint(t.x,t.y))}}Qs.$inject=["elementRegistry","eventBus","injector"];var Js={__init__:["hoverFix"],hoverFix:["type",Qs]},ec=Math.round,tc="djs-drag-active";function nc(e){e.preventDefault()}function ic(e,t,n,i){var r,o={threshold:5,trapClick:!0};function a(e){var n=t.viewbox(),i=t._container.getBoundingClientRect();return{x:n.x+(e.x-i.left)/n.scale,y:n.y+(e.y-i.top)/n.scale}}function s(t,n){n=n||r;var i=e.createEvent(F({},n.payload,n.data,{isTouch:n.isTouch}));return!1!==e.fire("drag."+t,i)&&e.fire(n.prefix+"."+t,i)}function c(e,i){var o,c=r.payload,p=r.displacement,l=r.globalStart,u=ko(e),h=Ka(u,l),d=r.localStart,f=a(u),v=Ka(f,d);if(!r.active&&(i||(o=h,Math.sqrt(Math.pow(o.x,2)+Math.pow(o.y,2))>r.threshold))){if(F(c,{x:ec(d.x+p.x),y:ec(d.y+p.y),dx:0,dy:0},{originalEvent:e}),!1===s("start"))return m();r.active=!0,r.keepSelection||(c.previousSelection=n.get(),n.select(null)),r.cursor&&Va(r.cursor),t.addMarker(t.getRootElement(),tc)}Do(e),r.active&&(F(c,{x:ec(f.x+p.x),y:ec(f.y+p.y),dx:ec(v.x),dy:ec(v.y)},{originalEvent:e}),s("move"))}function p(e){var t=!0;r.active&&(e&&(r.payload.originalEvent=e,Do(e)),t=s("end")),!1===t&&s("rejected"),s("ended",v(!0!==t))}function l(e){27===e.which&&(nc(e),m())}function u(t){var n;r.active&&(n=Ua(e),setTimeout(n,400),nc(t)),p(t)}function h(e){c(e)}function d(e){var t=r.payload;t.hoverGfx=e.gfx,t.hover=e.element,s("hover")}function f(e){s("out");var t=r.payload;t.hoverGfx=null,t.hover=null}function m(e){var t;if(r){var n=r.active;n&&s("cancel"),t=v(e),n&&s("canceled",t)}}function v(o){var a,v;s("cleanup"),Wa(),v=r.trapClick?u:p,le.unbind(document,"mousemove",c),le.unbind(document,"dragstart",nc),le.unbind(document,"selectstart",nc),le.unbind(document,"mousedown",v,!0),le.unbind(document,"mouseup",v,!0),le.unbind(document,"keyup",l),le.unbind(document,"touchstart",h,!0),le.unbind(document,"touchcancel",m,!0),le.unbind(document,"touchmove",c,!0),le.unbind(document,"touchend",p,!0),e.off("element.hover",d),e.off("element.out",f),t.removeMarker(t.getRootElement(),tc);var y=r.payload.previousSelection;return!1!==o&&y&&!n.get().length&&function(e){var t=e.filter((function(e){return i.get(e.id)}));t.length&&n.select(t)}(y),a=r,r=null,a}e.on("diagram.destroy",m),this.init=function(t,n,i,v){r&&m(!1),"string"==typeof n&&(v=i,i=n,n=null);var y,g,b,x,_,E=(v=F({},o,v||{})).data||{};x=v.trapClick?u:p,t?(y=Po(t)||t,g=ko(t),Do(t),"dragstart"===y.type&&nc(y)):(y=null,g={x:0,y:0}),b=a(g),n||(n=b),_=function(e){return"undefined"!=typeof TouchEvent&&e instanceof TouchEvent}(y),r=F({prefix:i,data:E,payload:{},globalStart:g,displacement:Ka(n,b),localStart:b,isTouch:_},v),v.manual||(_?(le.bind(document,"touchstart",h,!0),le.bind(document,"touchcancel",m,!0),le.bind(document,"touchmove",c,!0),le.bind(document,"touchend",p,!0)):(le.bind(document,"mousemove",c),le.bind(document,"dragstart",nc),le.bind(document,"selectstart",nc),le.bind(document,"mousedown",x,!0),le.bind(document,"mouseup",x,!0)),le.bind(document,"keyup",l),e.on("element.hover",d),e.on("element.out",f)),s("init"),v.autoActivate&&c(t,!0)},this.move=c,this.hover=d,this.out=f,this.end=p,this.cancel=m,this.context=function(){return r},this.setOptions=function(e){F(o,e)}}ic.$inject=["eventBus","canvas","selection","elementRegistry"];var rc={__depends__:[Js,Ko],dragging:["type",ic]};function oc(e,t,n){this._canvas=n,this._opts=F({scrollThresholdIn:[20,20,20,20],scrollThresholdOut:[0,0,0,0],scrollRepeatTimeout:15,scrollStep:10},e);var i=this;t.on("drag.move",(function(e){var t=i._toBorderPoint(e);i.startScroll(t)})),t.on(["drag.cleanup"],(function(){i.stopScroll()}))}function ac(e,t,n){return tv-3&&(n=bn(r.target,m),a===v-2?"intersect"===n&&(d.pop(),d[d.length-1]=m):"intersect"!==n&&d.push(u)),i.newWaypoints=r.waypoints=s(r,d),p(i,y,e),i.newSegmentStartIndex=o+y,c(e)})),t.on("connectionSegment.move.hover",(function(e){e.context.hover=e.hover,n.addMarker(e.hover,zc)})),t.on(["connectionSegment.move.out","connectionSegment.move.cleanup"],(function(e){var t=e.context.hover;t&&n.removeMarker(t,zc)})),t.on("connectionSegment.move.cleanup",(function(e){var t=e.context,i=t.connection;t.draggerGfx&&ke(t.draggerGfx),n.removeMarker(i,$c)})),t.on(["connectionSegment.move.cancel","connectionSegment.move.end"],(function(e){var t=e.context;t.connection.waypoints=t.originalWaypoints,c(e)})),t.on("connectionSegment.move.end",(function(e){var t=e.context,n=t.connection,i=t.newWaypoints,r=t.newSegmentStartIndex,a=function(e,t){var n=0;return{waypoints:e.filter((function(i,r){return!At(e[r-1],e[r+1],i)||(n=r<=t?n-1:n,!1)})),segmentOffset:n}}(i=i.map((function(e){return{original:e.original,x:Math.round(e.x),y:Math.round(e.y)}})),r),c=s(n,a.waypoints),p=a.segmentOffset,l={segmentMove:{segmentStartIndex:t.segmentStartIndex,newSegmentStartIndex:r+p}};o.updateWaypoints(n,c,l)}))}Uc.$inject=["injector","eventBus","canvas","dragging","graphicsFactory","modeling"];var qc=Math.abs,Kc=Math.round;function Yc(e){return{x:e.x,y:e.y}}function Xc(e){return{x:e.x+e.width,y:e.y+e.height}}function Zc(e,t){return!e||isNaN(e.x)||isNaN(e.y)?t:{x:Kc(e.x+e.width/2),y:Kc(e.y+e.height/2)}}function Qc(e,t){var n=e.snapped;return!!n&&("string"==typeof t?n[t]:n.x&&n.y)}function Jc(e,t,n){if("string"!=typeof t)throw new Error("axis must be in [x, y]");if("number"!=typeof n&&!1!==n)throw new Error("value must be Number or false");var i,r=e[t],o=e.snapped=e.snapped||{};return!1===n?o[t]=!1:(o[t]=!0,i=n-r,e[t]+=i,e["d"+t]+=i),r}function ep(e){return e.children||[]}var tp=Math.abs,np=Math.round;function ip(e){function t(e,t){if(p(e)){for(var n=e.length;n--;)if(tp(e[n]-t)<=10)return e[n]}else{var i=t%(e=+e);if(i<10)return t-i;if(i>e-10)return t-i+e}return t}function n(e,t){return e.waypoints?Cc(t,e):e.width?{x:np(e.width/2+e.x),y:np(e.height/2+e.y)}:void 0}e.on("connectionSegment.move.move",1500,(function(e){var i,r,o=function(e){var t=e.context,i=t.snapPoints,r=t.connection,o=r.waypoints,a=t.segmentStart,s=t.segmentStartIndex,c=t.segmentEnd,p=t.segmentEndIndex,l=t.axis;if(i)return i;var u=[o[s-1],a,c,o[p+1]];return s<2&&u.unshift(n(r.source,e)),p>o.length-3&&u.unshift(n(r.target,e)),t.snapPoints=i={horizontal:[],vertical:[]},g(u,(function(e){e&&(e=e.original||e,"y"===l&&i.horizontal.push(e.y),"x"===l&&i.vertical.push(e.x))})),i}(e),a=e.x,s=e.y;if(o){var c=a-(i=t(o.vertical,a)),p=s-(r=t(o.horizontal,s));F(e,{dx:e.dx-c,dy:e.dy-p,x:i,y:r}),(c||-1!==o.vertical.indexOf(a))&&Jc(e,"x",i),(p||-1!==o.horizontal.indexOf(s))&&Jc(e,"y",r)}})),e.on(["connect.hover","connect.move","connect.end"],1500,(function(e){var t,i=e.context.hover,r=i&&n(i,e);(t=i)&&t.waypoints&&r&&r.x&&r.y&&(Jc(e,"x",r.x),Jc(e,"y",r.y))})),e.on(["bendpoint.move.move","bendpoint.move.end"],1500,(function(e){var i,r,o=e.context,a=function(e){var t=e.snapPoints,n=e.connection.waypoints,i=e.bendpointIndex;if(t)return t;var r=[n[i-1],n[i+1]];return e.snapPoints=t={horizontal:[],vertical:[]},g(r,(function(e){e&&(e=e.original||e,t.horizontal.push(e.y),t.vertical.push(e.x))})),t}(o),s=o.hover,c=s&&n(s,e),p=e.x,l=e.y;if(a){var u=p-(i=t(c?a.vertical.concat([c.x]):a.vertical,p)),h=l-(r=t(c?a.horizontal.concat([c.y]):a.horizontal,l));F(e,{dx:e.dx-u,dy:e.dy-h,x:e.x-u,y:e.y-h}),(u||-1!==a.vertical.indexOf(p))&&Jc(e,"x",i),(h||-1!==a.horizontal.indexOf(l))&&Jc(e,"y",r)}}))}ip.$inject=["eventBus"];var rp={__depends__:[rc,pc],__init__:["bendpoints","bendpointSnapping","bendpointMovePreview"],bendpoints:["type",Ac],bendpointMove:["type",kc],bendpointMovePreview:["type",Fc],connectionSegmentMove:["type",Uc],bendpointSnapping:["type",ip]};function op(e,t,n,i){function r(e,t){return i.allowed("connection.create",{source:e,target:t})}e.on("connect.hover",(function(e){var t,n=e.context,i=n.start,o=e.hover;if(n.hover=o,!c(t=n.canExecute=r(i,o))){if(!1!==t)return n.source=i,void(n.target=o);c(t=n.canExecute=r(o,i))||!1!==t&&(n.source=o,n.target=i)}})),e.on(["connect.out","connect.cleanup"],(function(e){var t=e.context;t.hover=null,t.source=null,t.target=null,t.canExecute=!1})),e.on("connect.end",(function(e){var t=e.context,i=t.canExecute,r=t.connectionStart,o={x:e.x,y:e.y},a=t.source,s=t.target;if(!i)return!1;var c=null,p={connectionStart:ap(t)?o:r,connectionEnd:ap(t)?r:o};l(i)&&(c=i),t.connection=n.connect(a,s,c,p)})),this.start=function(e,n,i,r){l(i)||(r=i,i=gn(n)),t.init(e,"connect",{autoActivate:r,data:{shape:n,context:{start:n,connectionStart:i}}})}}function ap(e){var t=e.hover,n=e.source,i=e.target;return t&&n&&t===n&&n!==i}op.$inject=["eventBus","dragging","modeling","rules"];var sp="connect-ok",cp="connect-not-ok";function pp(e,t,n){var i=e.get("connectionPreview",!1);i&&t.on("connect.move",(function(e){var t=e.context,n=t.canExecute,r=t.hover,o=t.source,a=t.start,s=t.startPosition,c=t.target,p=t.connectionStart||s,l=t.connectionEnd||{x:e.x,y:e.y},u=p,h=l;ap(t)&&(u=l,h=p),i.drawPreview(t,n,{source:o||a,target:c||r,connectionStart:u,connectionEnd:h})})),t.on("connect.hover",900,(function(e){var t=e.context,i=e.hover,r=t.canExecute;null!==r&&n.addMarker(i,r?sp:cp)})),t.on(["connect.out","connect.cleanup"],1100,(function(e){var t=e.hover;t&&(n.removeMarker(t,sp),n.removeMarker(t,cp))})),i&&t.on("connect.cleanup",(function(e){i.cleanUp(e.context)}))}pp.$inject=["injector","eventBus","canvas"];var lp={__depends__:[Ko,pc,rc],__init__:["connectPreview"],connect:["type",op],connectPreview:["type",pp]};function up(e,t,n,i){this._canvas=t,this._graphicsFactory=n,this._elementFactory=i,this._connectionDocking=e.get("connectionDocking",!1),this._layouter=e.get("layouter",!1)}up.$inject=["injector","canvas","graphicsFactory","elementFactory"],up.prototype.drawPreview=function(e,t,n){n=n||{};var i,r,o,a=e.connectionPreviewGfx,s=e.getConnection,c=n.source,p=n.target,l=n.waypoints,u=n.connectionStart,h=n.connectionEnd,d=n.noLayout,f=n.noCropping,m=n.noNoop,v=this;a||(a=e.connectionPreviewGfx=this.createConnectionPreviewGfx()),Me(a),s||(s=e.getConnection=(r=function(e,t,n){return v.getConnection(e,t,n)},o={},function(e){var t=JSON.stringify(e),n=o[t];return n||(n=o[t]=r.apply(null,arguments)),n})),t&&(i=s(t,c,p)),i?(i.waypoints=l||[],this._layouter&&!d&&(i.waypoints=this._layouter.layoutConnection(i,{source:c,target:p,connectionStart:u,connectionEnd:h,waypoints:n.waypoints||i.waypoints})),i.waypoints&&i.waypoints.length||(i.waypoints=[c?gn(c):u,p?gn(p):h]),this._connectionDocking&&(c||p)&&!f&&(i.waypoints=this._connectionDocking.getCroppedWaypoints(i,c,p)),this._graphicsFactory.drawConnection(a,i)):!m&&this.drawNoopPreview(a,n)},up.prototype.drawNoopPreview=function(e,t){var n=t.source,i=t.target,r=t.connectionStart||gn(n),o=t.connectionEnd||gn(i),a=this.cropWaypoints(r,o,n,i);_e(e,this.createNoopConnection(a[0],a[1]))},up.prototype.cropWaypoints=function(e,t,n,i){var r=this._graphicsFactory,o=n&&r.getShapePath(n),a=i&&r.getShapePath(i),s=r.getConnectionPath({waypoints:[e,t]});return[e=n&&xn(o,s,!0)||e,t=i&&xn(a,s,!1)||t]},up.prototype.cleanUp=function(e){e&&e.connectionPreviewGfx&&ke(e.connectionPreviewGfx)},up.prototype.getConnection=function(e){var t=function(e){return l(e)?e:{}}(e);return this._elementFactory.createConnection(t)},up.prototype.createConnectionPreviewGfx=function(){var e=Le("g");return Se(e,{pointerEvents:"none"}),Pe(e).add("djs-connection-preview"),_e(this._canvas.getActiveLayer(),e),e},up.prototype.createNoopConnection=function(e,t){var n=Le("polyline");return Se(n,{stroke:"#333",strokeDasharray:[1],strokeWidth:2,"pointer-events":"none"}),Se(n,{points:[e.x,e.y,t.x,t.y]}),n};var hp={__init__:["connectionPreview"],connectionPreview:["type",up]},dp=Math.min,fp=Math.max;function mp(e){e.preventDefault()}function vp(e){e.stopPropagation()}function yp(e){this.container=e.container,this.parent=fe('
      '),this.content=ye("[contenteditable]",this.parent),this.keyHandler=e.keyHandler||function(){},this.resizeHandler=e.resizeHandler||function(){},this.autoResize=L(this.autoResize,this),this.handlePaste=L(this.handlePaste,this)}function gp(e,t){this._eventBus=e,this._providers=[],this._textbox=new yp({container:t.getContainer(),keyHandler:L(this._handleKey,this),resizeHandler:L(this._handleResize,this)})}yp.prototype.create=function(e,t,n,i){var r=this.parent,o=this.content,a=this.container;i=this.options=i||{};var s=z(t=this.style=t||{},["width","height","maxWidth","maxHeight","minWidth","minHeight","left","top","backgroundColor","position","overflow","border","wordWrap","textAlign","outline","transform"]);F(r.style,{width:e.width+"px",height:e.height+"px",maxWidth:e.maxWidth+"px",maxHeight:e.maxHeight+"px",minWidth:e.minWidth+"px",minHeight:e.minHeight+"px",left:e.x+"px",top:e.y+"px",backgroundColor:"#ffffff",position:"absolute",overflow:"visible",border:"1px solid #ccc",boxSizing:"border-box",wordWrap:"normal",textAlign:"center",outline:"none"},s);var c=z(t,["fontFamily","fontSize","fontWeight","lineHeight","padding","paddingTop","paddingRight","paddingBottom","paddingLeft"]);return F(o.style,{boxSizing:"border-box",width:"100%",outline:"none",wordWrap:"break-word"},c),i.centerVertically&&F(o.style,{position:"absolute",top:"50%",transform:"translate(0, -50%)"},c),o.innerText=n,le.bind(o,"keydown",this.keyHandler),le.bind(o,"mousedown",vp),le.bind(o,"paste",this.handlePaste),i.autoResize&&le.bind(o,"input",this.autoResize),i.resizable&&this.resizable(t),a.appendChild(r),this.setSelection(o.lastChild,o.lastChild&&o.lastChild.length),r},yp.prototype.handlePaste=function(e){var t,n=this.options,i=this.style;if(e.preventDefault(),t=e.clipboardData?e.clipboardData.getData("text/plain"):window.clipboardData.getData("Text"),this.insertText(t),n.autoResize){var r=this.autoResize(i);r&&this.resizeHandler(r)}},yp.prototype.insertText=function(e){e=e.replace(/\r\n|\r|\n/g,"\n"),document.execCommand("insertText",!1,e)||this._insertTextIE(e)},yp.prototype._insertTextIE=function(e){var t,n,i,r=this.getSelection(),o=r.startContainer,a=r.endContainer,s=r.startOffset,c=r.endOffset,p=r.commonAncestorContainer,l=(t=p.childNodes,[].slice.call(t));if(function(e){return e.nodeType===Node.TEXT_NODE}(p)){var u=o.textContent;o.textContent=u.substring(0,s)+e+u.substring(c),n=o,i=s+e.length}else if(o===this.content&&a===this.content){var h=document.createTextNode(e);this.content.insertBefore(h,l[s]),n=h,i=h.textContent.length}else{var d=l.indexOf(o),f=l.indexOf(a);l.forEach((function(t,n){n===d?t.textContent=o.textContent.substring(0,s)+e+a.textContent.substring(c):n>d&&n<=f&&be(t)})),n=o,i=s+e.length}n&&void 0!==i&&setTimeout((function(){self.setSelection(n,i)}))},yp.prototype.autoResize=function(){var e=this.parent,t=this.content,n=parseInt(this.style.fontSize)||12;if(t.scrollHeight>e.offsetHeight||t.scrollHeight
      ');var u=function(n){mp(n),vp(n);var u=dp(fp(p+n.clientX-s,i),o),h=dp(fp(l+n.clientY-c,r),a);t.style.width=u+"px",t.style.height=h+"px",e.resizeHandler({width:p,height:l,dx:n.clientX-s,dy:n.clientY-c})},h=function(e){mp(e),vp(e),le.unbind(document,"mousemove",u,!1),le.unbind(document,"mouseup",h,!1)};le.bind(n,"mousedown",(function(e){mp(e),vp(e),s=e.clientX,c=e.clientY;var n=t.getBoundingClientRect();p=n.width,l=n.height,le.bind(document,"mousemove",u),le.bind(document,"mouseup",h)}))}F(n.style,{position:"absolute",bottom:"0px",right:"0px",cursor:"nwse-resize",width:"0",height:"0",borderTop:(parseInt(this.style.fontSize)/4||3)+"px solid transparent",borderRight:(parseInt(this.style.fontSize)/4||3)+"px solid #ccc",borderBottom:(parseInt(this.style.fontSize)/4||3)+"px solid #ccc",borderLeft:(parseInt(this.style.fontSize)/4||3)+"px solid transparent"}),t.appendChild(n)},yp.prototype.destroy=function(){var e=this.parent,t=this.content,n=this.resizeHandle;t.innerText="",e.removeAttribute("style"),t.removeAttribute("style"),le.unbind(t,"keydown",this.keyHandler),le.unbind(t,"mousedown",vp),le.unbind(t,"input",this.autoResize),le.unbind(t,"paste",this.handlePaste),n&&(n.removeAttribute("style"),be(n)),be(e)},yp.prototype.getValue=function(){return this.content.innerText.trim()},yp.prototype.getSelection=function(){return window.getSelection().getRangeAt(0)},yp.prototype.setSelection=function(e,t){var n=document.createRange();null===e?n.selectNodeContents(this.content):(n.setStart(e,t),n.setEnd(e,t));var i=window.getSelection();i.removeAllRanges(),i.addRange(n)},gp.$inject=["eventBus","canvas"],gp.prototype.registerProvider=function(e){this._providers.push(e)},gp.prototype.isActive=function(e){return!(!this._active||e&&this._active.element!==e)},gp.prototype.cancel=function(){this._active&&(this._fire("cancel"),this.close())},gp.prototype._fire=function(e,t){this._eventBus.fire("directEditing."+e,t||{active:this._active})},gp.prototype.close=function(){this._textbox.destroy(),this._fire("deactivate"),this._active=null,this.resizable=void 0},gp.prototype.complete=function(){var e=this._active;if(e){var t,n=e.context.bounds,i=this.$textbox.getBoundingClientRect(),r=this.getValue();r===e.context.text&&i.height===n.height&&i.width===n.width||(t=this._textbox.container.getBoundingClientRect(),e.provider.update(e.element,r,e.context.text,{x:i.left-t.left,y:i.top-t.top,width:i.width,height:i.height})),this._fire("complete"),this.close()}},gp.prototype.getValue=function(){return this._textbox.getValue()},gp.prototype._handleKey=function(e){e.stopPropagation();var t=e.keyCode||e.charCode;return 27===t?(e.preventDefault(),this.cancel()):13!==t||e.shiftKey?void 0:(e.preventDefault(),this.complete())},gp.prototype._handleResize=function(e){this._fire("resize",e)},gp.prototype.activate=function(e){var t;this.isActive()&&this.cancel();var n=m(this._providers,(function(n){return(t=n.activate(e))?n:null}));return t&&(this.$textbox=this._textbox.create(t.bounds,t.style,t.text,t.options),this._active={element:e,context:t,provider:n},t.options&&t.options.resizable&&(this.resizable=!0),this._fire("activate")),!!t};var bp={__depends__:[Fo],__init__:["directEditing"],directEditing:["type",gp]},xp=["marker-start","marker-mid","marker-end"],_p=["circle","ellipse","line","path","polygon","polyline","rect"];function Ep(e,t,n,i){this._elementRegistry=e,this._canvas=n,this._styles=i,this._clonedMarkers={};var r=this;t.on("drag.cleanup",(function(){g(r._clonedMarkers,(function(e){ke(e)})),r._clonedMarkers={}}))}Ep.$inject=["elementRegistry","eventBus","canvas","styles"],Ep.prototype.getGfx=function(e){return this._elementRegistry.getGraphics(e)},Ep.prototype.addDragger=function(e,t,n){var i=Be(n=n||this.getGfx(e)),r=n.getBoundingClientRect();return this._cloneMarkers(Zn(i)),Se(i,this._styles.cls("djs-dragger",[],{x:r.top,y:r.left})),_e(t,i),i},Ep.prototype.addFrame=function(e,t){var n=Le("rect",{class:"djs-resize-overlay",width:e.width,height:e.height,x:e.x,y:e.y});return _e(t,n),n},Ep.prototype._cloneMarkers=function(e){var t=this;if(e.childNodes)for(var n=0;n=120&&F(h,{"lane-divide-two":{group:"lane-divide",className:"bpmn-icon-lane-divide-two",title:u("Divide into two Lanes"),action:{click:v(2)}}}),e.height>=180&&F(h,{"lane-divide-three":{group:"lane-divide",className:"bpmn-icon-lane-divide-three",title:u("Divide into three Lanes"),action:{click:v(3)}}})),F(h,{"lane-insert-below":{group:"lane-insert-below",className:"bpmn-icon-lane-insert-below",title:u("Add Lane below"),action:{click:function(e,t){n.addLane(t,"bottom")}}}})}Ar(d,"bpmn:FlowNode")&&(Ar(d,"bpmn:EventBasedGateway")?F(h,{"append.receive-task":m("bpmn:ReceiveTask","bpmn-icon-receive-task",u("Append ReceiveTask")),"append.message-intermediate-event":m("bpmn:IntermediateCatchEvent","bpmn-icon-intermediate-event-catch-message",u("Append MessageIntermediateCatchEvent"),{eventDefinitionType:"bpmn:MessageEventDefinition"}),"append.timer-intermediate-event":m("bpmn:IntermediateCatchEvent","bpmn-icon-intermediate-event-catch-timer",u("Append TimerIntermediateCatchEvent"),{eventDefinitionType:"bpmn:TimerEventDefinition"}),"append.condition-intermediate-event":m("bpmn:IntermediateCatchEvent","bpmn-icon-intermediate-event-catch-condition",u("Append ConditionIntermediateCatchEvent"),{eventDefinitionType:"bpmn:ConditionalEventDefinition"}),"append.signal-intermediate-event":m("bpmn:IntermediateCatchEvent","bpmn-icon-intermediate-event-catch-signal",u("Append SignalIntermediateCatchEvent"),{eventDefinitionType:"bpmn:SignalEventDefinition"})}):Fl(d,"bpmn:BoundaryEvent","bpmn:CompensateEventDefinition")?F(h,{"append.compensation-activity":m("bpmn:Task","bpmn-icon-task",u("Append compensation activity"),{isForCompensation:!0})}):Ar(d,"bpmn:EndEvent")||d.isForCompensation||Fl(d,"bpmn:IntermediateThrowEvent","bpmn:LinkEventDefinition")||Ur(d)||F(h,{"append.end-event":m("bpmn:EndEvent","bpmn-icon-end-event-none",u("Append EndEvent")),"append.gateway":m("bpmn:ExclusiveGateway","bpmn-icon-gateway-none",u("Append Gateway")),"append.append-task":m("bpmn:Task","bpmn-icon-task",u("Append Task")),"append.intermediate-event":m("bpmn:IntermediateThrowEvent","bpmn-icon-intermediate-event-none",u("Append Intermediate/Boundary Event"))})),a.isEmpty(e,"bpmn-replace")||F(h,{replace:{group:"edit",className:"bpmn-icon-screw-wrench",title:u("Change type"),action:{click:function(e,n){var i=F(function(e){var n=s.getContainer(),i=t.getPad(e).html,r=n.getBoundingClientRect(),o=i.getBoundingClientRect(),a=o.top-r.top;return{x:o.left-r.left,y:a+o.height+5}}(n),{cursor:{x:e.x,y:e.y}});a.open(n,"bpmn-replace",i)}}}}),Ar(d,"bpmn:SequenceFlow")&&F(h,{"append.text-annotation":m("bpmn:TextAnnotation","bpmn-icon-text-annotation")}),Rr(d,["bpmn:FlowNode","bpmn:InteractionNode","bpmn:DataObjectReference","bpmn:DataStoreReference"])&&F(h,{"append.text-annotation":m("bpmn:TextAnnotation","bpmn-icon-text-annotation"),connect:{group:"connect",className:"bpmn-icon-connection-multi",title:u("Connect using "+(d.isForCompensation?"":"Sequence/MessageFlow or ")+"Association"),action:{click:f,dragstart:f}}}),Ar(d,"bpmn:TextAnnotation")&&F(h,{connect:{group:"connect",className:"bpmn-icon-connection-multi",title:u("Connect using Association"),action:{click:f,dragstart:f}}}),Rr(d,["bpmn:DataObjectReference","bpmn:DataStoreReference"])&&F(h,{connect:{group:"connect",className:"bpmn-icon-connection-multi",title:u("Connect using DataInputAssociation"),action:{click:f,dragstart:f}}}),Ar(d,"bpmn:Group")&&F(h,{"append.text-annotation":m("bpmn:TextAnnotation","bpmn-icon-text-annotation")});var g=c.allowed("elements.delete",{elements:[e]});return p(g)&&(g=g[0]===e),g&&F(h,{delete:{group:"edit",className:"bpmn-icon-trash",title:u("Remove"),action:{click:function(e,t){n.removeElements([t])}}}}),h};var zl={__depends__:[bp,Es,Ko,lp,Np,wl],__init__:["contextPadProvider"],contextPadProvider:["type",Il]},$l={horizontal:["x","width"],vertical:["y","height"]};function Hl(e,t){this._modeling=e,this._filters=[],this.registerFilter((function(e){var n=t.allowed("elements.distribute",{elements:e});return p(n)?n:n?e:[]}))}Hl.$inject=["modeling","rules"],Hl.prototype.registerFilter=function(e){if("function"!=typeof e)throw new Error("the filter has to be a function");this._filters.push(e)},Hl.prototype.trigger=function(e,t){var n,i,r=this._modeling;if(!(e.length<3||(this._setOrientation(t),i=this._filterElements(e),(n=this._createGroups(i)).length<=2)))return r.distributeElements(n,this._axis,this._dimension),n},Hl.prototype._filterElements=function(e){var t=this._filters,n=this._axis,i=this._dimension,r=[].concat(e);return t.length?(g(t,(function(e){r=e(r,n,i)})),r):e},Hl.prototype._createGroups=function(e){var t=[],n=this,i=this._axis,r=this._dimension;if(!i)throw new Error('must have a defined "axis" and "dimension"');return g(D(e,i),(function(e,o){var a,s=n._findRange(e,i,r),c=t[t.length-1];c&&n._hasIntersection(c.range,s)?t[t.length-1].elements.push(e):(a={range:s,elements:[e]},t.push(a))})),t},Hl.prototype._setOrientation=function(e){var t=$l[e];this._axis=t[0],this._dimension=t[1]},Hl.prototype._hasIntersection=function(e,t){return Math.max(e.min,e.max)>=Math.min(t.min,t.max)&&Math.min(e.min,e.max)<=Math.max(t.min,t.max)},Hl.prototype._findRange=function(e){var t=e[this._axis];return{min:t+5,max:t+e[this._dimension]-5}};var Gl={__init__:["distributeElements"],distributeElements:["type",Hl]};function Vl(e,t,n){ks.call(this,t)}Vl.$inject=["distributeElements","eventBus","rules"],e(Vl,ks),Vl.prototype.init=function(){this.addRule("elements.distribute",(function(e){var t=e.elements;return!((t=ut(t=y(t,(function(e){var t=Rr(e,["bpmn:Association","bpmn:BoundaryEvent","bpmn:DataInputAssociation","bpmn:DataOutputAssociation","bpmn:Lane","bpmn:MessageFlow","bpmn:SequenceFlow","bpmn:TextAnnotation"]);return!(e.labelTarget||t)})))).length<3)&&t}))};var Wl="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linejoin%3Around%22%20d%3D%22M450%20400V150h900v250%22%2F%3E%3Crect%20x%3D%22150%22%20y%3D%22450%22%20width%3D%22600%22%20height%3D%221200%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%221050%22%20y%3D%22450%22%20width%3D%22600%22%20height%3D%22800%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E",Ul="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linejoin%3Around%22%20d%3D%22M400%201350H150V450h250%22%2F%3E%3Crect%20x%3D%22450%22%20y%3D%22150%22%20width%3D%221200%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%22450%22%20y%3D%221050%22%20width%3D%22800%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E";function ql(e,t,n,i){this._distributeElements=t,this._translate=n,this._popupMenu=e,this._rules=i,e.registerProvider("align-elements",900,this)}ql.$inject=["popupMenu","distributeElements","translate","rules"],ql.prototype.getPopupMenuEntries=function(e){var t={};return this._isAllowed(e)&&F(t,this._getEntries(e)),t},ql.prototype._isAllowed=function(e){return this._rules.allowed("elements.distribute",{elements:e})},ql.prototype._getEntries=function(e){var t=this._distributeElements,n=this._translate,i=this._popupMenu;return{"distribute-elements-horizontal":{group:"distribute",title:n("Distribute elements horizontally"),className:"bjs-align-elements-menu-entry",imageUrl:Wl,action:function(n,r){t.trigger(e,"horizontal"),i.close()}},"distribute-elements-vertical":{group:"distribute",title:n("Distribute elements vertically"),imageUrl:Ul,action:function(n,r){t.trigger(e,"vertical"),i.close()}}}};var Kl={__depends__:[As,Gl],__init__:["bpmnDistributeElements","distributeElementsMenuProvider"],bpmnDistributeElements:["type",Vl],distributeElementsMenuProvider:["type",ql]},Yl="is not a registered action";function Xl(e,t){this._actions={};var n=this;e.on("diagram.init",(function(){n._registerDefaultActions(t),e.fire("editorActions.init",{editorActions:n})}))}function Zl(e,t){return new Error(e+" "+t)}Xl.$inject=["eventBus","injector"],Xl.prototype._registerDefaultActions=function(e){var t=e.get("commandStack",!1),n=e.get("modeling",!1),i=e.get("selection",!1),r=e.get("zoomScroll",!1),o=e.get("copyPaste",!1),a=e.get("canvas",!1),s=e.get("rules",!1),c=e.get("keyboardMove",!1),l=e.get("keyboardMoveSelection",!1);t&&(this.register("undo",(function(){t.undo()})),this.register("redo",(function(){t.redo()}))),o&&i&&this.register("copy",(function(){var e=i.get();if(e.length)return o.copy(e)})),o&&this.register("paste",(function(){o.paste()})),r&&this.register("stepZoom",(function(e){r.stepZoom(e.value)})),a&&this.register("zoom",(function(e){a.zoom(e.value)})),n&&i&&s&&this.register("removeSelection",(function(){var e=i.get();if(e.length){var t,r=s.allowed("elements.delete",{elements:e});!1!==r&&(t=p(r)?r:e).length&&n.removeElements(t.slice())}})),c&&this.register("moveCanvas",(function(e){c.moveCanvas(e)})),l&&this.register("moveSelection",(function(e){l.moveSelection(e.direction,e.accelerated)}))},Xl.prototype.trigger=function(e,t){if(!this._actions[e])throw Zl(e,Yl);return this._actions[e](t)},Xl.prototype.register=function(e,t){var n=this;if("string"==typeof e)return this._registerAction(e,t);g(e,(function(e,t){n._registerAction(t,e)}))},Xl.prototype._registerAction=function(e,t){if(this.isRegistered(e))throw Zl(e,"is already registered");this._actions[e]=t},Xl.prototype.unregister=function(e){if(!this.isRegistered(e))throw Zl(e,Yl);this._actions[e]=void 0},Xl.prototype.getActions=function(){return Object.keys(this._actions)},Xl.prototype.isRegistered=function(e){return!!this._actions[e]};var Ql={__init__:["editorActions"],editorActions:["type",Xl]};function Jl(e){e.invoke(Xl,this)}e(Jl,Xl),Jl.$inject=["injector"],Jl.prototype._registerDefaultActions=function(e){Xl.prototype._registerDefaultActions.call(this,e);var t=e.get("canvas",!1),n=e.get("elementRegistry",!1),i=e.get("selection",!1),r=e.get("spaceTool",!1),o=e.get("lassoTool",!1),a=e.get("handTool",!1),s=e.get("globalConnect",!1),c=e.get("distributeElements",!1),p=e.get("alignElements",!1),l=e.get("directEditing",!1),u=e.get("searchPad",!1),h=e.get("modeling",!1);t&&n&&i&&this._registerAction("selectElements",(function(){var e=t.getRootElement(),r=n.filter((function(t){return t!==e}));return i.select(r),r})),r&&this._registerAction("spaceTool",(function(){r.toggle()})),o&&this._registerAction("lassoTool",(function(){o.toggle()})),a&&this._registerAction("handTool",(function(){a.toggle()})),s&&this._registerAction("globalConnectTool",(function(){s.toggle()})),i&&c&&this._registerAction("distributeElements",(function(e){var t=i.get(),n=e.type;t.length&&c.trigger(t,n)})),i&&p&&this._registerAction("alignElements",(function(e){var t=i.get(),n=[],r=e.type;t.length&&(n=y(t,(function(e){return!Ar(e,"bpmn:Lane")})),p.trigger(n,r))})),i&&h&&this._registerAction("setColor",(function(e){var t=i.get();t.length&&h.setColor(t,e)})),i&&l&&this._registerAction("directEditing",(function(){var e=i.get();e.length&&l.activate(e[0])})),u&&this._registerAction("find",(function(){u.toggle()})),t&&h&&this._registerAction("moveToOrigin",(function(){var e,i,r=t.getRootElement();e=vt(i=Ar(r,"bpmn:Collaboration")?n.filter((function(e){return Ar(e.parent,"bpmn:Collaboration")})):n.filter((function(e){return e!==r&&!Ar(e.parent,"bpmn:SubProcess")}))),h.moveElements(i,{x:-e.x,y:-e.y},r)}))};var eu={__depends__:[Ql],editorActions:["type",Jl]};function tu(e){e.on(["create.init","shape.move.init"],(function(e){var t=e.context;Rr(e.shape,["bpmn:Participant","bpmn:SubProcess","bpmn:TextAnnotation"])&&(t.gridSnappingContext||(t.gridSnappingContext={}),t.gridSnappingContext.snapLocation="top-left")}))}tu.$inject=["eventBus"];function nu(e,t,n){return n||(n="round"),Math[n](e/t)*t}function iu(e,t,n){var i=!n||!1!==n.active;this._eventBus=t;var r=this;t.on("diagram.init",800,(function(){r.setActive(i)})),t.on(["create.move","create.end","bendpoint.move.move","bendpoint.move.end","connect.move","connect.end","connectionSegment.move.move","connectionSegment.move.end","resize.move","resize.end","shape.move.move","shape.move.end"],1200,(function(t){var n=t.originalEvent;if(!(!r.active||n&&Ma(n))){var i=t.context,o=i.gridSnappingContext;o||(o=i.gridSnappingContext={}),["x","y"].forEach((function(n){var i={},o=function(e,t,n){var i=e.context,r=e.shape,o=i.gridSnappingContext,a=o.snapLocation,s=o.snapOffset;if(s&&u(s[t]))return s[t];s||(s=o.snapOffset={});u(s[t])||(s[t]=0);if(!r)return s[t];n.get(r.id)||(ru(t)?s[t]+=r[t]+r.width/2:s[t]+=r[t]+r.height/2);if(!a)return s[t];"x"===t?/left/.test(a)?s[t]-=r.width/2:/right/.test(a)&&(s[t]+=r.width/2):/top/.test(a)?s[t]-=r.height/2:/bottom/.test(a)&&(s[t]+=r.height/2);return s[t]}(t,n,e);o&&(i.offset=o);var a=function(e,t){var n=e.context,i=n.createConstraints,r=n.resizeConstraints||{},o=n.gridSnappingContext,a=o.snapConstraints;if(a&&a[t])return a[t];a||(a=o.snapConstraints={});a[t]||(a[t]={});var s=n.direction;i&&(ru(t)?(a.x.min=i.left,a.x.max=i.right):(a.y.min=i.top,a.y.max=i.bottom));var c=r.min,p=r.max;c&&(ru(t)?au(s)?a.x.max=c.left:a.x.min=c.right:ou(s)?a.y.max=c.top:a.y.min=c.bottom);p&&(ru(t)?au(s)?a.x.min=p.left:a.x.max=p.right:ou(s)?a.y.min=p.top:a.y.max=p.bottom);return a[t]}(t,n);a&&F(i,a),Qc(t,n)||r.snapEvent(t,n,i)}))}}))}function ru(e){return"x"===e}function ou(e){return-1!==e.indexOf("n")}function au(e){return-1!==e.indexOf("w")}function su(e,t){ra.call(this,e),this._gridSnapping=t;var n=this;this.preExecute("shape.resize",(function(e){var t=e.context,i=(t.hints||{}).autoResize;if(i){var r=t.shape,o=t.newBounds;d(i)?t.newBounds=n.snapComplex(o,i):t.newBounds=n.snapSimple(r,o)}}))}iu.prototype.snapEvent=function(e,t,n){Jc(e,t,this.snapValue(e[t],n))},iu.prototype.getGridSpacing=function(){return 10},iu.prototype.snapValue=function(e,t){var n,i,r=0;return t&&t.offset&&(r=t.offset),e=nu(e+=r,10),t&&t.min&&u(n=t.min)&&(n=nu(n+r,10,"ceil"),e=Math.max(e,n)),t&&t.max&&u(i=t.max)&&(i=nu(i+r,10,"floor"),e=Math.min(e,i)),e-=r},iu.prototype.isActive=function(){return this.active},iu.prototype.setActive=function(e){this.active=e,this._eventBus.fire("gridSnapping.toggle",{active:e})},iu.prototype.toggleActive=function(){this.setActive(!this.active)},iu.$inject=["elementRegistry","eventBus","config.gridSnapping"],su.$inject=["eventBus","gridSnapping","modeling"],e(su,ra),su.prototype.snapSimple=function(e,t){var n=this._gridSnapping;return t.width=n.snapValue(t.width,{min:t.width}),t.height=n.snapValue(t.height,{min:t.height}),t.x=e.x+e.width/2-t.width/2,t.y=e.y+e.height/2-t.height/2,t},su.prototype.snapComplex=function(e,t){return/w|e/.test(t)&&(e=this.snapHorizontally(e,t)),/n|s/.test(t)&&(e=this.snapVertically(e,t)),e},su.prototype.snapHorizontally=function(e,t){var n=this._gridSnapping,i=/w/.test(t),r=/e/.test(t),o={};return o.width=n.snapValue(e.width,{min:e.width}),r&&(i?(o.x=n.snapValue(e.x,{max:e.x}),o.width+=n.snapValue(e.x-o.x,{min:e.x-o.x})):e.x=e.x+e.width-o.width),F(e,o),e},su.prototype.snapVertically=function(e,t){var n=this._gridSnapping,i=/n/.test(t),r=/s/.test(t),o={};return o.height=n.snapValue(e.height,{min:e.height}),i&&(r?(o.y=n.snapValue(e.y,{max:e.y}),o.height+=n.snapValue(e.y-o.y,{min:e.y-o.y})):e.y=e.y+e.height-o.height),F(e,o),e};function cu(e,t){e.on(["spaceTool.move","spaceTool.end"],2e3,(function(e){var n,i=e.context;i.initialized&&("x"===i.axis?(n=t.snapValue(e.dx),e.x=e.x+n-e.dx,e.dx=n):(n=t.snapValue(e.dy),e.y=e.y+n-e.dy,e.dy=n))}))}cu.$inject=["eventBus","gridSnapping"];var pu={__depends__:[{__init__:["gridSnappingResizeBehavior","gridSnappingSpaceToolBehavior"],gridSnappingResizeBehavior:["type",su],gridSnappingSpaceToolBehavior:["type",cu]}],__init__:["gridSnapping"],gridSnapping:["type",iu]};function lu(e,t){e.on("autoPlace",2e3,(function(e){var n=e.source,i=gn(n),r=e.shape,o=Gs(n,r);return["x","y"].forEach((function(e){var n={};o[e]!==i[e]&&(o[e]>i[e]?n.min=o[e]:n.max=o[e],Ar(r,"bpmn:TextAnnotation")&&(!function(e){return"x"===e}(e)?n.offset=-r.height/2:n.offset=-r.width/2),o[e]=t.snapValue(o[e],n))})),o}))}lu.$inject=["eventBus","gridSnapping"];function uu(e,t,n){t.on(["create.start","shape.move.start"],1750,(function(t){var i=t.context,r=i.shape,o=e.getRootElement();Ar(r,"bpmn:Participant")&&Ar(o,"bpmn:Process")&&o.children.length&&(i.createConstraints&&(r.width=n.snapValue(r.width,{min:r.width}),r.height=n.snapValue(r.height,{min:r.height})))}))}uu.$inject=["canvas","eventBus","gridSnapping"];function hu(e,t,n){ra.call(this,e),this._gridSnapping=t;var i=this;this.postExecuted(["connection.create","connection.layout"],3e3,(function(e){var t=e.context,r=t.connection,o=t.hints||{},a=r.waypoints;o.connectionStart||o.connectionEnd||!1===o.createElementsBehavior||function(e){return e.length>3}(a)&&n.updateWaypoints(r,i.snapMiddleSegments(a))}))}function du(e,t,n){var i=Rt(t,n),r={};return function(e){return"h"===e}(i)&&(r.y=e.snapValue(t.y)),function(e){return"v"===e}(i)&&(r.x=e.snapValue(t.x)),("x"in r||"y"in r)&&(t=F({},t,r),n=F({},n,r)),[t,n]}hu.$inject=["eventBus","gridSnapping","modeling"],e(hu,ra),hu.prototype.snapMiddleSegments=function(e){var t,n=this._gridSnapping;e=e.slice();for(var i=1;i5&&Se(a,{x:o.x,width:o.width}),o.height>5&&Se(a,{y:o.y,height:o.height}),i.canExecute?Pe(a).remove(ku):Pe(a).add(ku)})),e.on("resize.cleanup",(function(e){var n,i;n=e.context,i=n.shape,n.frame&&ke(n.frame),t.removeMarker(i,Du)}))}Mu.$inject=["eventBus","canvas","previewSupport"];var Bu=-6,Nu="djs-resizer",Ou=["n","w","s","e","nw","ne","se","sw"];function ju(e,t,n,i){this._resize=i,this._canvas=t;var r=this;e.on("selection.changed",(function(e){var t=e.newSelection;r.removeResizers(),1===t.length&&g(t,L(r.addResizer,r))})),e.on("shape.changed",(function(e){var t=e.element;n.isSelected(t)&&(r.removeResizers(),r.addResizer(t))}))}ju.prototype.makeDraggable=function(e,t,n){var i=this._resize;function r(t){Bo(t)&&i.activate(t,e,n)}le.bind(t,"mousedown",r),le.bind(t,"touchstart",r)},ju.prototype._createResizer=function(e,t,n,i){var r=this._getResizersParent(),o=function(e){var t={x:0,y:0};-1!==e.indexOf("e")?t.x=6:-1!==e.indexOf("w")&&(t.x=Bu);-1!==e.indexOf("s")?t.y=6:-1!==e.indexOf("n")&&(t.y=Bu);return t}(i),a=Le("g");Pe(a).add(Nu),Pe(a).add("djs-resizer-"+e.id),Pe(a).add("djs-resizer-"+i),_e(r,a);var s=Le("rect");Se(s,{x:-4+o.x,y:-4+o.y,width:8,height:8}),Pe(s).add("djs-resizer-visual"),_e(a,s);var c=Le("rect");return Se(c,{x:-10+o.x,y:-10+o.y,width:20,height:20}),Pe(c).add("djs-resizer-hit"),_e(a,c),Qn(a,t,n),a},ju.prototype.createResizer=function(e,t){var n=Tu(e,t),i=this._createResizer(e,n.x,n.y,t);this.makeDraggable(e,i,t)},ju.prototype.addResizer=function(e){var t=this;!function(e){return!!e.waypoints}(e)&&this._resize.canResize({shape:e})&&g(Ou,(function(n){t.createResizer(e,n)}))},ju.prototype.removeResizers=function(){Me(this._getResizersParent())},ju.prototype._getResizersParent=function(){return this._canvas.getLayer("resizers")},ju.$inject=["eventBus","canvas","selection","resize"];var Lu={__depends__:[pc,rc,wp],__init__:["resize","resizePreview","resizeHandles"],resize:["type",Ru],resizePreview:["type",Mu],resizeHandles:["type",ju]};function Iu(e,t,n,i,r,o,a){function s(e,t){(t||Rr(e,["bpmn:Task","bpmn:TextAnnotation"])||Fu(e))&&i.activate(e)}this._bpmnFactory=t,this._canvas=n,this._modeling=r,this._textRenderer=a,i.registerProvider(this),e.on("element.dblclick",(function(e){s(e.element,!0)})),e.on(["autoPlace.start","canvas.viewbox.changing","drag.init","element.mousedown","popupMenu.open","root.set","selection.changed"],(function(e){i.isActive()&&i.complete()})),e.on(["shape.remove","connection.remove"],2e3,(function(e){i.isActive(e.element)&&i.cancel()})),e.on(["commandStack.changed"],(function(e){i.isActive()&&i.cancel()})),e.on("directEditing.activate",(function(e){o.removeResizers()})),e.on("create.end",500,(function(e){var t=e.context,n=t.shape,i=e.context.canExecute;e.isTouch||i&&(t.hints&&!1===t.hints.createElementsBehavior||s(n))})),e.on("autoPlace.end",500,(function(e){s(e.shape)}))}function Fu(e){return Ar(e,"bpmn:SubProcess")&&!Wr(e)}Iu.$inject=["eventBus","bpmnFactory","canvas","directEditing","modeling","resizeHandles","textRenderer"],Iu.prototype.activate=function(e){var t=Yr(e);if(void 0!==t){var n={text:t};F(n,this.getEditingBBox(e));var i={};return(Rr(e,["bpmn:Task","bpmn:Participant","bpmn:Lane","bpmn:CallActivity"])||Fu(e))&&F(i,{centerVertically:!0}),go(e)&&F(i,{autoResize:!0}),Ar(e,"bpmn:TextAnnotation")&&F(i,{resizable:!0,autoResize:!0}),F(n,{options:i}),n}},Iu.prototype.getEditingBBox=function(e){var t=this._canvas,n=e.label||e,i=t.getAbsoluteBBox(n),r=i.x+i.width/2,o=i.y+i.height/2,a={x:i.x,y:i.y},s=t.zoom(),c=this._textRenderer.getDefaultStyle(),p=this._textRenderer.getExternalStyle(),l=p.fontSize*s,u=p.lineHeight,h=c.fontSize*s,d=c.lineHeight,f={fontFamily:this._textRenderer.getDefaultStyle().fontFamily,fontWeight:this._textRenderer.getDefaultStyle().fontWeight};(Ar(e,"bpmn:Lane")||function(e){return Ar(e,"bpmn:Participant")&&Wr(e)}(e))&&(F(a,{width:i.height,height:30*s,x:i.x-i.height/2+15*s,y:o-30*s/2}),F(f,{fontSize:h+"px",lineHeight:d,paddingTop:7*s+"px",paddingBottom:7*s+"px",paddingLeft:5*s+"px",paddingRight:5*s+"px",transform:"rotate(-90deg)"})),(Rr(e,["bpmn:Task","bpmn:CallActivity"])||function(e){return Ar(e,"bpmn:Participant")&&!Wr(e)}(e)||Fu(e))&&(F(a,{width:i.width,height:i.height}),F(f,{fontSize:h+"px",lineHeight:d,paddingTop:7*s+"px",paddingBottom:7*s+"px",paddingLeft:5*s+"px",paddingRight:5*s+"px"})),function(e){return Ar(e,"bpmn:SubProcess")&&Wr(e)}(e)&&(F(a,{width:i.width,x:i.x}),F(f,{fontSize:h+"px",lineHeight:d,paddingTop:7*s+"px",paddingBottom:7*s+"px",paddingLeft:5*s+"px",paddingRight:5*s+"px"}));var m=90*s,v=7*s,y=4*s;if(n.labelTarget&&(F(a,{width:m,height:i.height+v+y,x:r-m/2,y:i.y-v}),F(f,{fontSize:l+"px",lineHeight:u,paddingTop:v+"px",paddingBottom:y+"px"})),go(n)&&!bo(n)&&!Eo(n)){var g=_o(e),b=t.getAbsoluteBBox({x:g.x,y:g.y,width:0,height:0}),x=l+v+y;F(a,{width:m,height:x,x:b.x-m/2,y:b.y-x/2}),F(f,{fontSize:l+"px",lineHeight:u,paddingTop:v+"px",paddingBottom:y+"px"})}return Ar(e,"bpmn:TextAnnotation")&&(F(a,{width:i.width,height:i.height,minWidth:30*s,minHeight:10*s}),F(f,{textAlign:"left",paddingTop:5*s+"px",paddingBottom:7*s+"px",paddingLeft:7*s+"px",paddingRight:5*s+"px",fontSize:h+"px",lineHeight:d})),{bounds:a,style:f}},Iu.prototype.update=function(e,t,n,i){var r,o,a;Ar(e,"bpmn:TextAnnotation")&&(o=this._canvas.getAbsoluteBBox(e),r={x:e.x,y:e.y,width:e.width/o.width*i.width,height:e.height/o.height*i.height}),(a=t)&&a.trim()||(t=null),this._modeling.updateLabel(e,t,r)};var zu="djs-element-hidden",$u="djs-label-hidden";function Hu(e,t,n,i){var r,o,a,s=this,c=t.getDefaultLayer();e.on("directEditing.activate",(function(e){var n=e.active;if(Ar(r=n.element.label||n.element,"bpmn:TextAnnotation")){o=t.getAbsoluteBBox(r),a=Le("g");var p=i.getScaledPath("TEXT_ANNOTATION",{xScaleFactor:1,yScaleFactor:1,containerWidth:r.width,containerHeight:r.height,position:{mx:0,my:0}}),l=s.path=Le("path");Se(l,{d:p,strokeWidth:2,stroke:Gu(r)}),_e(a,l),_e(c,a),Jn(a,r.x,r.y)}Ar(r,"bpmn:TextAnnotation")||r.labelTarget?t.addMarker(r,zu):(Ar(r,"bpmn:Task")||Ar(r,"bpmn:CallActivity")||Ar(r,"bpmn:SubProcess")||Ar(r,"bpmn:Participant"))&&t.addMarker(r,$u)})),e.on("directEditing.resize",(function(e){if(Ar(r,"bpmn:TextAnnotation")){var t=e.height,n=e.dy,a=Math.max(r.height/o.height*(t+n),0),c=i.getScaledPath("TEXT_ANNOTATION",{xScaleFactor:1,yScaleFactor:1,containerWidth:r.width,containerHeight:a,position:{mx:0,my:0}});Se(s.path,{d:c})}})),e.on(["directEditing.complete","directEditing.cancel"],(function(e){var n=e.active;n&&(t.removeMarker(n.element.label||n.element,zu),t.removeMarker(r,$u)),r=void 0,o=void 0,a&&(ke(a),a=void 0)}))}function Gu(e,t){return Pr(e).get("stroke")||t||"black"}Hu.$inject=["eventBus","canvas","elementRegistry","pathMap"];var Vu={__depends__:[ia,Lu,bp],__init__:["labelEditingProvider","labelEditingPreview"],labelEditingProvider:["type",Iu],labelEditingPreview:["type",Hu]},Wu=["top","bottom","left","right"],Uu=10;function qu(e,t){function n(e){if(bo(e)){var n=function(e){var t=gn(e.label),n=Ku(gn(e),t);if(i=n,-1===Wu.indexOf(i))return;var i;var r=function(e){var t=gn(e);return[].concat(e.incoming.map((function(e){return e.waypoints[e.waypoints.length-2]})),e.outgoing.map((function(e){return e.waypoints[1]}))).map((function(e){return Ku(t,e)}))}(e);if(e.host){var o=function(e){var t,n=e.host,i=bn(gn(e),n);t=i.indexOf("-")>=0?i.split("-"):[i];return Wu.filter((function(e){return-1===t.indexOf(e)}))}(e);r=r.concat(o)}var a=Wu.filter((function(e){return-1===r.indexOf(e)}));if(-1!==a.indexOf(n))return;return a[0]}(e);n&&function(e,n){var i=gn(e),r=e.label,o=gn(r);if(!r.parent)return;var a,s=mn(e);switch(n){case"top":a={x:i.x,y:s.top-Uu-r.height/2};break;case"left":a={x:s.left-Uu-r.width/2,y:i.y};break;case"bottom":a={x:i.x,y:s.bottom+Uu+r.height/2};break;case"right":a={x:s.right+Uu+r.width/2,y:i.y}}var c=Ka(a,o);t.moveShape(r,c)}(e,n)}}ra.call(this,e),this.postExecuted(["connection.create","connection.layout","connection.updateWaypoints"],(function(e){var t=e.context,i=t.connection,r=i.source,o=i.target;!1!==(t.hints||{}).createElementsBehavior&&(n(r),n(o))})),this.postExecuted(["label.create"],(function(e){var t=e.context,i=t.shape;!1!==(t.hints||{}).createElementsBehavior&&n(i.labelTarget)})),this.postExecuted(["elements.create"],(function(e){var t=e.context,i=t.elements;!1!==(t.hints||{}).createElementsBehavior&&i.forEach((function(e){n(e)}))}))}function Ku(e,t){return bn(t,e,5)}function Yu(e,t,n){ra.call(this,e),this.preExecute("shape.append",(function(e){var t=e.source,n=e.shape;e.position||(Ar(n,"bpmn:TextAnnotation")?e.position={x:t.x+t.width/2+75,y:t.y-50-n.height/2}:e.position={x:t.x+t.width+80+n.width/2,y:t.y+t.height/2})}),!0)}function Xu(e,t){e.invoke(ra,this),this.postExecute("shape.move",(function(e){var n=e.newParent,i=e.shape;g(y(i.incoming.concat(i.outgoing),(function(e){return Ar(e,"bpmn:Association")})),(function(e){t.moveConnection(e,{x:0,y:0},n)}))}),!0)}e(qu,ra),qu.$inject=["eventBus","modeling"],e(Yu,ra),Yu.$inject=["eventBus","elementFactory","bpmnRules"],e(Xu,ra),Xu.$inject=["injector","modeling"];function Zu(e,t){t.invoke(ra,this),this._bpmnReplace=e;var n=this;this.postExecuted("elements.create",500,(function(e){var t=e.elements;1===(t=t.filter((function(e){return Qu(e,e.host)}))).length&&t.map((function(e){return t.indexOf(e)})).forEach((function(i){var r=t[i];e.elements[i]=n.replaceShape(t[i],r)}))}),!0),this.preExecute("elements.move",500,(function(e){var t=e.shapes,i=e.newHost;if(1===t.length){var r=t[0];Qu(r,i)&&(e.shapes=[n.replaceShape(r,i)])}}),!0)}function Qu(e,t){return!Eo(e)&&Rr(e,["bpmn:IntermediateThrowEvent","bpmn:IntermediateCatchEvent"])&&!!t}function Ju(e,t){function n(e){return y(e.attachers,(function(e){return Ar(e,"bpmn:BoundaryEvent")}))}ra.call(this,e),this.postExecute("connection.create",(function(e){var i=e.context.source,r=e.context.target,o=n(r);Ar(i,"bpmn:EventBasedGateway")&&Ar(r,"bpmn:ReceiveTask")&&o.length>0&&t.removeElements(o)})),this.postExecute("connection.reconnect",(function(e){var i=e.context.oldSource,r=e.context.newSource;Ar(i,"bpmn:Gateway")&&Ar(r,"bpmn:EventBasedGateway")&&g(r.outgoing,(function(e){var i=e.target,r=n(i);Ar(i,"bpmn:ReceiveTask")&&r.length>0&&t.removeElements(r)}))}))}function eh(e){e.invoke(ra,this),this.preExecute("shape.create",1500,(function(e){var t=e.context,n=t.parent,i=t.shape;Ar(n,"bpmn:Lane")&&!Ar(i,"bpmn:Lane")&&(t.parent=Hs(n,"bpmn:Participant"))}))}function th(e,t,n){ra.call(this,e),this.preExecute("shape.create",(function(e){var n=e.context.shape;if(Ar(n,"bpmn:DataObjectReference")&&"label"!==n.type){var i=t.create("bpmn:DataObject");n.businessObject.dataObjectRef=i}}))}Zu.$inject=["bpmnReplace","injector"],e(Zu,ra),Zu.prototype.replaceShape=function(e,t){var n,i=(n=Tr(e).eventDefinitions)&&n[0],r={type:"bpmn:BoundaryEvent",host:t};return i&&(r.eventDefinitionType=i.$type),this._bpmnReplace.replaceElement(e,r,{layoutConnection:!1})},Ju.$inject=["eventBus","modeling"],e(Ju,ra),eh.$inject=["injector"],e(eh,ra),th.$inject=["eventBus","bpmnFactory","moddle"],e(th,ra);var nh=20,ih=20,rh=2e3;function oh(e,t,n){function i(){var t=e.getRootElement();return Ar(t,"bpmn:Collaboration")?t:n.makeCollaboration()}ra.call(this,t),t.on(["create.start","shape.move.start"],rh,(function(t){var n=t.context,i=n.shape,r=e.getRootElement();if(Ar(i,"bpmn:Participant")&&Ar(r,"bpmn:Process")&&r.children.length){var o=r.children.filter((function(e){return!Ar(e,"bpmn:Group")&&!Eo(e)&&!function(e){return!!e.waypoints}(e)}));if(o.length){var a=vt(o),s=function(e,t){t={width:t.width+40+30,height:t.height+40};var n=Math.max(e.width,t.width),i=Math.max(e.height,t.height);return{x:-n/2,y:-i/2,width:n,height:i}}(i,a);F(i,s),n.createConstraints=function(e,t){return{bottom:(t=mn(t)).top+e.height/2-ih,left:t.right-e.width/2+nh,top:t.bottom-e.height/2+ih,right:t.left+e.width/2-nh-30}}(i,a)}}})),t.on("create.start",rh,(function(n){var i=n.context.shape,r=e.getRootElement(),o=e.getGraphics(r);function a(e){e.element=r,e.gfx=o}Ar(i,"bpmn:Participant")&&Ar(r,"bpmn:Process")&&(t.on("element.hover",rh,a),t.once("create.cleanup",(function(){t.off("element.hover",a)})))})),this.preExecute("elements.create",rh,(function(e){var t,n=e.elements,r=e.parent,o=function(e){return m(e,(function(e){return Ar(e,"bpmn:Participant")}))}(n);o&&Ar(r,"bpmn:Process")&&(e.parent=i(),(t=e.hints=e.hints||{}).participant=o,t.process=r,t.processRef=Tr(o).get("processRef"))}),!0),this.preExecute("shape.create",(function(e){var t=e.parent,n=e.shape;Ar(n,"bpmn:Participant")&&Ar(t,"bpmn:Process")&&(e.parent=i(),e.process=t,e.processRef=Tr(n).get("processRef"))}),!0),this.execute("shape.create",(function(e){var t=e.hints||{},n=e.process||t.process,i=e.shape,r=t.participant;!n||r&&i!==r||Tr(i).set("processRef",Tr(n))}),!0),this.revert("shape.create",(function(e){var t=e.hints||{},n=e.process||t.process,i=e.processRef||t.processRef,r=e.shape,o=t.participant;!n||o&&r!==o||Tr(r).set("processRef",i)}),!0),this.postExecute("shape.create",(function(e){var t=e.hints||{},i=e.process||e.hints.process,r=e.shape,o=t.participant;if(i){var a=i.children.slice();o?r===o&&n.moveElements(a,{x:0,y:0},o):n.moveElements(a,{x:0,y:0},r)}}),!0)}oh.$inject=["canvas","eventBus","modeling"],e(oh,ra);var ah="__targetRef_placeholder";function sh(e,t){function n(e,n){var i=e.get("properties"),r=m(i,(function(e){return e.name===ah}));return!r&&n&&wt(i,r=t.create("bpmn:Property",{name:ah})),r}function i(e,t){var i=n(e);i&&(function(e,t,n){return m(e.get("dataInputAssociations"),(function(e){return e!==n&&e.targetRef===t}))}(e,i,t)||Et(e.get("properties"),i))}function r(e){var t,r=e.context,o=r.connection,a=o.businessObject,s=o.target,c=s&&s.businessObject,p=r.newTarget,l=p&&p.businessObject,u=r.oldTarget||r.target,h=u&&u.businessObject,d=o.businessObject;h&&h!==c&&i(h,a),l&&l!==c&&i(l,a),c?(t=n(c,!0),d.targetRef=t):d.targetRef=null}ra.call(this,e),this.executed(["connection.create","connection.delete","connection.move","connection.reconnect"],ch(r)),this.reverted(["connection.create","connection.delete","connection.move","connection.reconnect"],ch(r))}function ch(e){return function(t){if(Ar(t.context.connection,"bpmn:DataInputAssociation"))return e(t)}}function ph(e){this._bpmnUpdater=e}function lh(e,t,n,i){function r(e){return e.children.filter((function(e){return Ar(e,"bpmn:DataStoreReference")&&!e.labelTarget}))}function o(e,i){var r=e.businessObject||e;if(i=i||n.filter((function(e){return Ar(e,"bpmn:Participant")&&Tr(e).processRef}))[0]){var o=i.businessObject||i;t.execute("dataStore.updateContainment",{dataStoreBo:r,dataStoreDi:Pr(e),newSemanticParent:o.processRef||o,newDiParent:Pr(i)})}}ra.call(this,i),t.registerHandler("dataStore.updateContainment",ph),this.preExecute("shape.create",(function(e){var t=e.context,n=t.shape;Ar(n,"bpmn:DataStoreReference")&&"label"!==n.type&&(t.hints||(t.hints={}),t.hints.autoResize=!1)})),this.preExecute("elements.move",(function(e){var t=e.context,n=t.shapes;n.filter((function(e){return Ar(e,"bpmn:DataStoreReference")})).length&&(t.hints||(t.hints={}),t.hints.autoResize=n.filter((function(e){return!Ar(e,"bpmn:DataStoreReference")})))})),this.postExecute("shape.create",(function(e){var t=e.context.shape,n=t.parent;Ar(t,"bpmn:DataStoreReference")&&"label"!==t.type&&Ar(n,"bpmn:Collaboration")&&o(t)})),this.postExecute("shape.move",(function(e){var t=e.context,n=t.shape,i=t.oldParent,r=n.parent;Ar(i,"bpmn:Collaboration")||Ar(n,"bpmn:DataStoreReference")&&"label"!==n.type&&Ar(r,"bpmn:Collaboration")&&o(n,Ar(i,"bpmn:Participant")?i:function(e,t){for(;e.parent;){if(Ar(e.parent,t))return e.parent;e=e.parent}}(i,"bpmn:Participant"))})),this.postExecute("shape.delete",(function(t){var n=t.context.shape,i=e.getRootElement();Rr(n,["bpmn:Participant","bpmn:SubProcess"])&&Ar(i,"bpmn:Collaboration")&&r(i).filter((function(e){return function(e,t){var n=e.businessObject||e,i=t.businessObject||t;for(;n.$parent;){if(n.$parent===i.processRef||i)return!0;n=n.$parent}return!1}(e,n)})).forEach((function(e){o(e)}))})),this.postExecute("canvas.updateRoot",(function(e){var t=e.context,n=t.oldRoot,i=t.newRoot;r(n).forEach((function(e){Ar(i,"bpmn:Process")&&o(e,i)}))}))}sh.$inject=["eventBus","bpmnFactory"],e(sh,ra),ph.$inject=["bpmnUpdater"],ph.prototype.execute=function(e){var t=e.dataStoreBo,n=e.dataStoreDi,i=e.newSemanticParent,r=e.newDiParent;e.oldSemanticParent=t.$parent,e.oldDiParent=n.$parent,this._bpmnUpdater.updateSemanticParent(t,i),this._bpmnUpdater.updateDiParent(n,r)},ph.prototype.revert=function(e){var t=e.dataStoreBo,n=e.dataStoreDi,i=e.oldSemanticParent,r=e.oldDiParent;this._bpmnUpdater.updateSemanticParent(t,i),this._bpmnUpdater.updateDiParent(n,r)},lh.$inject=["canvas","commandStack","elementRegistry","eventBus"],e(lh,ra);function uh(e,t,n){ra.call(this,e),this.postExecuted("shape.delete",500,(function(e){var t=e.context,i=t.hints,r=t.shape,o=t.oldParent;Ar(r,"bpmn:Lane")&&(i&&i.nested||function(e,t){var i,r,o,a=Ol(t),s=[],c=[];ft(a,(function(t){return t.y>e.y?c.push(t):s.push(t),t.children})),a.length&&(i=c.length&&s.length?e.height/2:e.height,s.length&&(r=n.calculateAdjustments(s,"y",i,e.y-10),n.makeSpace(r.movingShapes,r.resizingShapes,{x:0,y:i},"s")),c.length&&(o=n.calculateAdjustments(c,"y",-i,e.y+e.height+10),n.makeSpace(o.movingShapes,o.resizingShapes,{x:0,y:-i},"n")))}(r,o))}))}uh.$inject=["eventBus","modeling","spaceTool"],e(uh,ra);function hh(e,t){t.invoke(ra,this),this._bpmnReplace=e;var n=this;this.postExecuted("elements.create",500,(function(e){var t=e.elements;t.filter((function(e){return dh(e,e.host)})).map((function(e){return t.indexOf(e)})).forEach((function(i){e.elements[i]=n.replaceShape(t[i])}))}),!0),this.preExecute("elements.move",500,(function(e){var t=e.shapes,i=e.newHost;t.forEach((function(e,r){var o,a=e.host;dh(e,(o=a,-1!==t.indexOf(o)?a:i))&&(t[r]=n.replaceShape(e))}))}),!0)}function dh(e,t){return!Eo(e)&&Ar(e,"bpmn:BoundaryEvent")&&!t}function fh(e,t,n){function i(e,i,r){var o,a,s,c,p,l,h,d,f=i.waypoints,m=e.outgoing.slice(),v=e.incoming.slice(),g=hc(f,d=u(r.width)?gn(r):r);if(g){if(o=f.slice(0,g.index),a=f.slice(g.index+(g.bendpoint?1:0)),!o.length||!a.length)return;s=g.bendpoint?f[g.index]:d,1!==o.length&&mh(e,o[o.length-1])||o.push(vh(s)),1!==a.length&&mh(e,a[0])||a.unshift(vh(s))}c=i.source,p=i.target,t.canConnect(c,e,i)&&(n.reconnectEnd(i,e,o||d),l=i),t.canConnect(e,p,i)&&(l?h=n.connect(e,p,{type:i.type,waypoints:a}):(n.reconnectStart(i,e,a||d),h=i));var b=[].concat(l&&y(v,(function(e){return e.source===l.source}))||[],h&&y(m,(function(e){return e.target===h.target}))||[]);b.length&&n.removeElements(b)}ra.call(this,e),this.preExecute("elements.move",(function(e){var n=e.newParent,i=e.shapes,r=e.delta,o=i[0];if(o&&n){n&&n.waypoints&&(e.newParent=n=n.parent);var a=gn(o),s={x:a.x+r.x,y:a.y+r.y},c=m(n.children,(function(e){return t.canInsert(i,e)&&hc(e.waypoints,s)}));c&&(e.targetFlow=c,e.position=s)}}),!0),this.postExecuted("elements.move",(function(e){var t=e.shapes,n=e.targetFlow,r=e.position;n&&i(t[0],n,r)}),!0),this.preExecute("shape.create",(function(e){var n=e.parent,i=e.shape;t.canInsert(i,n)&&(e.targetFlow=n,e.parent=n.parent)}),!0),this.postExecuted("shape.create",(function(e){var t=e.shape,n=e.targetFlow,r=e.position;n&&i(t,n,r)}),!0)}function mh(e,t){var n=t.x,i=t.y;return n>=e.x&&n<=e.x+e.width&&i>=e.y&&i<=e.y+e.height}function vh(e){return F({},e)}function yh(e,t){ra.call(this,e),this.preExecuted("connection.create",(function(e){var n=e.context,i=n.source,r=n.target,o=r.incoming.slice();n.hints&&!1===n.hints.createElementsBehavior||Ar(i,"bpmn:EventBasedGateway")&&r.incoming.length&&o.filter(gh).forEach((function(e){t.removeConnection(e)}))})),this.preExecuted("shape.replace",(function(e){var n=e.context.newShape;Ar(n,"bpmn:EventBasedGateway")&&n.outgoing.filter(gh).map((function(e){return e.target})).reduce((function(e,t){var n=t.incoming.filter(gh);return e.concat(n)}),[]).forEach((function(e){e.source!==n&&t.removeConnection(e)}))}))}function gh(e){return Ar(e,"bpmn:SequenceFlow")}hh.$inject=["bpmnReplace","injector"],e(hh,ra),hh.prototype.replaceShape=function(e){var t,n,i=(n=Tr(e).eventDefinitions)&&n[0];return t=i?{type:"bpmn:IntermediateCatchEvent",eventDefinitionType:i.$type}:{type:"bpmn:IntermediateThrowEvent"},this._bpmnReplace.replaceElement(e,t,{layoutConnection:!1})},e(fh,ra),fh.$inject=["eventBus","bpmnRules","modeling"],yh.$inject=["eventBus","modeling"],e(yh,ra);var bh=1500;function xh(e,t,n){t.on(["create.hover","create.move","create.out","create.end","shape.move.hover","shape.move.move","shape.move.out","shape.move.end"],bh,(function(t){var i=t.context.shape||t.shape,r=t.hover;Ar(r,"bpmn:Lane")&&!Rr(i,["bpmn:Lane","bpmn:Participant"])&&(t.hover=jl(r),t.hoverGfx=e.getGraphics(t.hover));var o=n.getRootElement();r!==o&&(i.labelTarget||Ar(i,"bpmn:Group"))&&(t.hover=o,t.hoverGfx=e.getGraphics(t.hover))})),t.on(["connect.hover","connect.out","connect.end","connect.cleanup","global-connect.hover","global-connect.out","global-connect.end","global-connect.cleanup"],bh,(function(t){var n=t.hover;Ar(n,"bpmn:Lane")&&(t.hover=jl(n)||n,t.hoverGfx=e.getGraphics(t.hover))})),t.on(["bendpoint.move.hover"],bh,(function(t){var n=t.context,i=t.hover,r=n.type;Ar(i,"bpmn:Lane")&&/reconnect/.test(r)&&(t.hover=jl(i)||i,t.hoverGfx=e.getGraphics(t.hover))})),t.on(["connect.start"],bh,(function(e){var t=e.context,n=t.start;Ar(n,"bpmn:Lane")&&(t.start=jl(n)||n)})),t.on("shape.move.start",2e3,(function(e){var t=e.shape;Ar(t,"bpmn:Lane")&&(e.shape=jl(t)||t)}))}xh.$inject=["elementRegistry","eventBus","canvas"];function _h(e,t,n,i,r,o){function a(e,t,i){var r=n.filter((function(e){return Ar(e,"bpmn:Group")})).filter((function(e){return e.businessObject!==i}));t&&!function(e,t){return e.some((function(e){var n=Tr(e);return(n.categoryValueRef&&n.categoryValueRef.$parent)===t}))}(r,t)&&function(e){var t=e.$parent;t&&(Et(t.get("rootElements"),e),e.$parent=null)}(t),e&&!function(e,t){return e.some((function(e){return Tr(e).categoryValueRef===t}))}(r,e)&&function(e){var t=e.$parent;t&&(Et(t.get("categoryValue"),e),e.$parent=null)}(e)}function s(e,n){return function(e,t,n){return wt(t.get("categoryValue"),e),e.$parent=t,wt(n.get("rootElements"),t),t.$parent=n,e}(e,n,t.getDefinitions())}function c(n,i){var r=Tr(n),o=r.categoryValueRef;o||(o=r.categoryValueRef=i.categoryValue=i.categoryValue||function(e){return e.create("bpmn:CategoryValue")}(e));var a=o.$parent;a||(a=o.$parent=i.category=i.category||function(e){return e.create("bpmn:Category")}(e)),s(o,a,t.getDefinitions())}function p(e,t){var n=t.category,i=t.categoryValue,r=Tr(e);i?(r.categoryValueRef=null,a(i,n,r)):a(null,r.categoryValueRef.$parent,r)}function l(t,n){var i=e.create(t.$type);return o.copyElement(t,i,null,n)}r.invoke(ra,this),this.execute("label.create",(function(e){var t=e.context,n=t.labelTarget;Ar(n,"bpmn:Group")&&c(n,t)})),this.revert("label.create",(function(e){var t=e.context,n=t.labelTarget;Ar(n,"bpmn:Group")&&p(n,t)})),this.execute("shape.delete",(function(e){var t=e.context,n=t.shape,i=Tr(n);if(Ar(n,"bpmn:Group")&&!n.labelTarget){var r=t.categoryValue=i.categoryValueRef;r&&(a(r,t.category=r.$parent,i),i.categoryValueRef=null)}})),this.reverted("shape.delete",(function(e){var t=e.context,n=t.shape;if(Ar(n,"bpmn:Group")&&!n.labelTarget){var i=t.category,r=t.categoryValue,o=Tr(n);r&&(o.categoryValueRef=r,s(r,i))}})),this.execute("shape.create",(function(e){var t=e.context,n=t.shape;Ar(n,"bpmn:Group")&&!n.labelTarget&&Tr(n).categoryValueRef&&c(n,t)})),this.reverted("shape.create",(function(e){var t=e.context,n=t.shape;Ar(n,"bpmn:Group")&&!n.labelTarget&&Tr(n).categoryValueRef&&p(n,t)})),i.on("copyPaste.copyElement",770,(function(e){var t=e.descriptor,n=e.element;if(Ar(n,"bpmn:Group")&&!n.labelTarget){var i=Tr(n);if(i.categoryValueRef){var r=i.categoryValueRef;t.categoryValue=l(r,!0),r.$parent&&(t.category=l(r.$parent,!0))}}})),i.on("copyPaste.pasteElement",770,(function(e){var t=e.descriptor,n=t.businessObject,i=t.categoryValue,r=t.category;i&&(i=n.categoryValueRef=l(i)),r&&(i.$parent=l(r)),delete t.category,delete t.categoryValue}))}function Eh(e,t,n,i){var r,o,a,s;return 0==(r=(i.y-n.y)*(t.x-e.x)-(i.x-n.x)*(t.y-e.y))?null:(o=e.y-n.y,a=e.x-n.x,s=((i.x-n.x)*o-(i.y-n.y)*a)/r,{x:Math.round(e.x+s*(t.x-e.x)),y:Math.round(e.y+s*(t.y-e.y))})}function wh(e){function t(e,t,n){var i,r={x:n.x,y:n.y-50},o={x:n.x-50,y:n.y},a=Eh(e,t,n,r),s=Eh(e,t,n,o);i=a&&s?Sh(a,n)>Sh(s,n)?s:a:a||s,e.original=i}e.on("bpmnElement.added",(function(e){var n,i,r=e.element;r.waypoints&&(t((i=(n=r).waypoints)[0],i[1],gn(n.source)),t(i[i.length-1],i[i.length-2],gn(n.target)))}))}function Sh(e,t){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))}function Ch(e){ra.call(this,e);var t=["bpmn:Participant","bpmn:Lane"];this.executed(["shape.move","shape.create","shape.resize"],(function(e){var n=e.context.shape,i=Tr(n),r=Pr(n);Rr(i,t)&&!r.get("isHorizontal")&&r.set("isHorizontal",!0)}))}_h.$inject=["bpmnFactory","bpmnjs","elementRegistry","eventBus","injector","moddleCopy"],e(_h,ra),wh.$inject=["eventBus"],Ch.$inject=["eventBus"],e(Ch,ra);var Ah=Math.sqrt,Rh=Math.min,Th=Math.max,Ph=Math.abs;function Dh(e){return Math.pow(e,2)}function kh(e,t){return Ah(Dh(e.x-t.x)+Dh(e.y-t.y))}function Mh(e,t,n,i){var r=t.x-e.x,o=t.y-e.y,a=n.x-e.x,s=n.y-e.y,c=r*r+o*o,p=(r*a+o*s)/c,l=p*p-(a*a+s*s-i*i)/c;if(l<0&&l>-1e-6&&(l=0),l<0)return[];var u=Ah(l),h=-p+u,d=-p-u,f={x:e.x-r*h,y:e.y-o*h};return 0===l?[f]:[f,{x:e.x-r*d,y:e.y-o*d}].filter((function(n){return function(e,t,n){return Bh(e.x,t.x,n.x)&&Bh(e.y,t.y,n.y)}(n,e,t)}))}function Bh(e,t,n){return e>=Rh(t,n)-Nh&&e<=Th(t,n)+Nh}var Nh=.1;function Oh(e,t){return Ph(e.x-t.x)<=Nh&&Ph(e.y-t.y)<=Nh}function jh(e,t,n,i){var r=0,o=0,a={point:e,delta:{x:0,y:0}},s=function(e,t){var n,i,r,o,a,s,c,p,l,u,h,d,f=0;for(f=0;f line intersections");1===s.length&&(c={type:"bendpoint",position:s[0],segmentIndex:f,bendpointIndex:Oh(n,s[0])?f:f+1}),2===s.length&&(h=s[0],d=s[1],c={type:"segment",position:a={x:(h.x+d.x)/2,y:(h.y+d.y)/2},segmentIndex:f,relativeLocation:kh(n,a)/kh(n,i)}),p=kh(c.position,e),(!u||l>p)&&(u=c,l=p)}return u}(e,n),c=s.segmentIndex,p=function(e,t,n,i){var r=n.segmentIndex,o=t.length-e.length;if(i.segmentMove){var a=i.segmentMove.segmentStartIndex,s=i.segmentMove.newSegmentStartIndex;return r===a?s:r>=s?r+o=l&&(c=p?r+1:r-1),rt.length-2||null===p)return a;var l,u,h=Ih(n,c),d=Ih(t,p),f=s.position,m=function(e,t){var n=yc(e[0],e[1]),i=yc(e[0],t);return 0===n?0:i/n}(h,f),v=(l=d,u=fc(h),fc(l)-u);if("bendpoint"===s.type){var y=t.length-n.length,g=s.bendpointIndex,b=n[g];if(-1!==t.indexOf(b))return a;if(0===y){var x=t[g];return{delta:{x:r=x.x-s.position.x,y:o=x.y-s.position.y},point:{x:e.x+r,y:e.y+o}}}y<0&&0!==g&&g"+r+"
      "}))}))}function id(e,t){ra.call(this,e),this.preExecute("shape.resize",(function(e){var n=e.shape,i=Pr(n),r=i&&i.get("label");r&&r.get("bounds")&&t.updateModdleProperties(n,r,{bounds:void 0})}),!0)}function rd(e,t,n){ra.call(this,e),this.preExecute("shape.delete",(function(e){var i=e.context.shape;if(1===i.incoming.length&&1===i.outgoing.length){var r=i.incoming[0],o=i.outgoing[0];if(Ar(r,"bpmn:SequenceFlow")&&Ar(o,"bpmn:SequenceFlow")&&t.canConnect(r.source,o.target,r)){var a=function(e,t){var n=Eh(od(e[e.length-2]),od(e[e.length-1]),od(t[1]),od(t[0]));return n?[].concat(e.slice(0,e.length-1),[n],t.slice(1)):[od(e[0]),od(t[t.length-1])]}(r.waypoints,o.waypoints);n.reconnectEnd(r,o.target,a)}}}))}function od(e){return e.original||e}function ad(e,t){ra.call(this,e),this.preExecute("shape.delete",(function(e){var t=e.shape,n=t.parent;Ar(t,"bpmn:Participant")&&(e.collaborationRoot=n)}),!0),this.postExecute("shape.delete",(function(e){var n=e.collaborationRoot;n&&!n.businessObject.participants.length&&t.makeProcess()}),!0)}function sd(e,t,n,i){ra.call(this,e);var r=i.get("dragging",!1);function o(e){var i,r,o=e.source,a=e.target;e.parent&&(Ar(e,"bpmn:SequenceFlow")&&(n.canConnectSequenceFlow(o,a)||(r=!0),n.canConnectMessageFlow(o,a)&&(i="bpmn:MessageFlow")),Ar(e,"bpmn:MessageFlow")&&(n.canConnectMessageFlow(o,a)||(r=!0),n.canConnectSequenceFlow(o,a)&&(i="bpmn:SequenceFlow")),Ar(e,"bpmn:Association")&&!n.canConnectAssociation(o,a)&&(r=!0),r&&t.removeConnection(e),i&&t.connect(o,a,{type:i,waypoints:e.waypoints.slice()}))}this.postExecuted("elements.move",(function(e){g(e.closure.allConnections,o)}),!0),this.preExecute("connection.reconnect",(function(e){var i,o,a=e.context,s=a.connection,c=a.newSource||s.source,p=a.newTarget||s.target;(i=n.canConnect(c,p))&&i.type!==s.type&&(o=t.connect(c,p,{type:i.type,waypoints:s.waypoints.slice()}),t.removeConnection(s),a.connection=o,r&&function(e,t){var n,i=r.context(),o=i&&i.payload.previousSelection;if(!o||!o.length)return;if(-1===(n=o.indexOf(e)))return;o.splice(n,1,t)}(s,o))})),this.postExecuted("element.updateProperties",(function(e){var n,i=e.context,r=i.properties,o=i.element,a=o.businessObject;r.default&&(n=m(o.outgoing,k({id:o.businessObject.default.id})))&&t.updateProperties(n,{conditionExpression:void 0}),r.conditionExpression&&a.sourceRef.default===a&&t.updateProperties(o.source,{default:void 0})}))}function cd(e,t,n,i,r,o){i.invoke(ra,this),this._bpmnReplace=e,this._elementRegistry=n,this._selection=o,this.postExecuted(["elements.create"],500,(function(e){var n=e.context,i=n.parent,r=n.elements,o=x(r,(function(e,n){var r=t.canReplace([n],n.host||n.parent||i);return r?e.concat(r.replacements):e}),[]);o.length&&this.replaceElements(r,o)}),this),this.postExecuted(["elements.move"],500,(function(e){var n=e.context,i=n.newParent,r=n.newHost,o=[];g(n.closure.topLevel,(function(e){o=Ur(e)?o.concat(e.children):o.concat(e)})),1===o.length&&r&&(i=r);var a=t.canReplace(o,i);a&&this.replaceElements(o,a.replacements,r)}),this),this.postExecute(["shape.replace"],1500,(function(e){var n,i=e.context,r=i.oldShape,o=i.newShape,a=r.attachers;a&&a.length&&(n=t.canReplace(a,o),this.replaceElements(a,n.replacements))}),this),this.postExecuted(["shape.replace"],1500,(function(e){var t=e.context,n=t.oldShape,i=t.newShape;r.unclaimId(n.businessObject.id,n.businessObject),r.updateProperties(i,{id:n.id})}))}nd.$inject=["eventBus","tooltips","translate"],e(id,ra),id.$inject=["eventBus","modeling"],e(rd,ra),rd.$inject=["eventBus","bpmnRules","modeling"],ad.$inject=["eventBus","modeling"],e(ad,ra),e(sd,ra),sd.$inject=["eventBus","modeling","bpmnRules","injector"],e(cd,ra),cd.prototype.replaceElements=function(e,t){var n=this._elementRegistry,i=this._bpmnReplace,r=this._selection;g(t,(function(t){var r={type:t.newElementType},o=n.get(t.oldElementId),a=e.indexOf(o);e[a]=i.replaceElement(o,r,{select:!1})})),t&&r.select(e)},cd.$inject=["bpmnReplace","bpmnRules","elementRegistry","injector","modeling","selection"];var pd={width:140,height:120},ld=300,ud=60,hd={width:300,height:150},dd={width:140,height:120},fd={width:50,height:30};function md(e){e.on("resize.start",1500,(function(e){var t=e.context,n=t.shape,i=t.direction,r=t.balanced;(Ar(n,"bpmn:Lane")||Ar(n,"bpmn:Participant"))&&(t.resizeConstraints=function(e,t,n){var i=jl(e),r=!0,o=!0,a=Nl(i,[i]),s=mn(e),c={},p={};/e/.test(t)?p.right=s.left+ld:/w/.test(t)&&(p.left=s.right-ld);return a.forEach((function(e){var i=mn(e);/n/.test(t)&&(i.tops.bottom+10&&(o=!1),n&&vd(s.bottom-i.top)<10&&xd(c,"bottom",i.bottom-ud),vd(s.bottom-i.bottom)<5&&_d(p,"bottom",i.top+ud))})),i.children.filter((function(e){return!e.hidden&&!e.waypoints&&(Ar(e,"bpmn:FlowElement")||Ar(e,"bpmn:Artifact"))})).forEach((function(e){var n=mn(e);r&&/n/.test(t)&&xd(p,"top",n.top-20),/e/.test(t)&&_d(p,"right",n.right+20),o&&/s/.test(t)&&_d(p,"bottom",n.bottom+20),/w/.test(t)&&xd(p,"left",n.left-50)})),{min:p,max:c}}(n,i,r)),Ar(n,"bpmn:Participant")&&(t.minDimensions=hd),Ar(n,"bpmn:SubProcess")&&Wr(n)&&(t.minDimensions=dd),Ar(n,"bpmn:TextAnnotation")&&(t.minDimensions=fd)}))}md.$inject=["eventBus"];var vd=Math.abs,yd=Math.min,gd=Math.max;function bd(e,t,n,i){var r=e[t];e[t]=void 0===r?n:i(n,r)}function xd(e,t,n){return bd(e,t,n,yd)}function _d(e,t,n){return bd(e,t,n,gd)}function Ed(e,t){e.on("resize.start",1501,(function(e){var t=e.context,n=t.shape;(Ar(n,"bpmn:Lane")||Ar(n,"bpmn:Participant"))&&(t.balanced=!No(e))})),e.on("resize.end",1001,(function(e){var n=e.context,i=n.shape,r=n.canExecute,o=n.newBounds;if(Ar(i,"bpmn:Lane")||Ar(i,"bpmn:Participant"))return r&&(o=dn(o),t.resizeLane(i,o,n.balanced)),!1}))}Ed.$inject=["eventBus","modeling"];function wd(e,t,n,i,r){function o(e){return Rr(e,["bpmn:ReceiveTask","bpmn:SendTask"])||function(e,t){p(t)||(t=[t]);return E(t,(function(t){return qr(e,t)}))}(e,["bpmn:ErrorEventDefinition","bpmn:EscalationEventDefinition","bpmn:MessageEventDefinition","bpmn:SignalEventDefinition"])}function a(t){return!!m(e.getDefinitions().get("rootElements"),k({id:t.id}))}function s(e){return Ar(e,"bpmn:ErrorEventDefinition")?"errorRef":Ar(e,"bpmn:EscalationEventDefinition")?"escalationRef":Ar(e,"bpmn:MessageEventDefinition")?"messageRef":Ar(e,"bpmn:SignalEventDefinition")?"signalRef":void 0}function c(e){if(Rr(e,["bpmn:ReceiveTask","bpmn:SendTask"]))return e.get("messageRef");var t=e.get("eventDefinitions")[0];return t.get(s(t))}n.invoke(ra,this),this.executed("shape.create",(function(t){var n=t.shape;if(o(n)){var i=c(Tr(n));i&&!a(i)&&(wt(e.getDefinitions().get("rootElements"),i),t.addedRootElement=i)}}),!0),this.reverted("shape.create",(function(t){var n=t.addedRootElement;n&&Et(e.getDefinitions().get("rootElements"),n)}),!0),t.on("copyPaste.copyElement",(function(e){var t=e.descriptor,n=e.element;if(!n.labelTarget&&o(n)){var i=c(Tr(n));i&&(t.referencedRootElement=i)}})),t.on("copyPaste.pasteElement",500,(function(e){var t=e.descriptor,n=t.businessObject,o=t.referencedRootElement;o&&(a(o)||(o=i.copyElement(o,r.create(o.$type))),function(e,t){if(Rr(e,["bpmn:ReceiveTask","bpmn:SendTask"]))return e.set("messageRef",t);var n=e.get("eventDefinitions")[0];n.set(s(n),t)}(n,o),delete t.referencedRootElement)}))}wd.$inject=["bpmnjs","eventBus","injector","moddleCopy","bpmnFactory"],e(wd,ra);var Sd=Math.max;function Cd(e){e.on("spaceTool.getMinDimensions",(function(e){var t=e.shapes,n=e.axis,i=e.start,r={};return g(t,(function(e){var t=e.id;Ar(e,"bpmn:Participant")&&(!function(e){return"x"===e}(n)?r[t]={width:hd.width,height:Ad(e,i)}:r[t]=hd),Ar(e,"bpmn:SubProcess")&&Wr(e)&&(r[t]=dd),Ar(e,"bpmn:TextAnnotation")&&(r[t]=fd),Ar(e,"bpmn:Group")&&(r[t]=pd)})),r}))}function Ad(e,t){var n;return Ol(e).length?(n=function(e,t){var n,i=Ol(e);return n=Rd(i,t),e.height-n.height+ud}(e,t),Sd(hd.height,n)):hd.height}function Rd(e,t){var n,i,r;for(n=0;n=(i=e[n]).y&&t<=i.y+i.height)return(r=Ol(i)).length?Rd(r,t):i}Cd.$inject=["eventBus"];var Td=180,Pd=160;function Dd(e,t,n,i,r,o,a){ra.call(this,t),this._canvas=e,this._eventBus=t,this._modeling=n,this._elementFactory=i,this._bpmnFactory=r,this._bpmnjs=o,this._elementRegistry=a;var s=this;function c(e){return Ar(e,"bpmn:SubProcess")&&!Wr(e)}function p(t){var n=t.shape,i=t.newRootElement,r=Tr(n);i=s._addDiagram(i||r),t.newRootElement=e.addRootElement(i)}function l(t){var n=Tr(t.shape);s._removeDiagram(n);var i=t.newRootElement=a.get(da(n));e.removeRootElement(i)}this.executed("shape.create",(function(e){c(e.shape)&&p(e)}),!0),this.postExecuted("shape.create",(function(e){var t=e.shape,n=e.newRootElement;n&&t.children&&(s._showRecursively(t.children),s._moveChildrenToShape(t,n))}),!0),this.reverted("shape.create",(function(e){c(e.shape)&&l(e)}),!0),this.preExecuted("shape.delete",(function(e){var t=e.shape;if(c(t)){var i=a.get(da(t));i&&n.removeElements(i.children.slice())}}),!0),this.executed("shape.delete",(function(e){c(e.shape)&&l(e)}),!0),this.reverted("shape.delete",(function(e){c(e.shape)&&p(e)}),!0),this.preExecuted("shape.replace",(function(t){var n=t.oldShape,i=t.newShape;c(n)&&c(i)&&(t.oldRoot=e.removeRootElement(da(n)))}),!0),this.postExecuted("shape.replace",(function(t){var i=t.newShape,r=t.oldRoot,o=e.findRoot(da(i));if(r&&o){var a=r.children;n.moveElements(a,{x:0,y:0},o)}}),!0),this.executed("element.updateProperties",(function(e){var t=e.element;if(Ar(t,"bpmn:SubProcess")){var n=e.properties,i=e.oldProperties.id,r=n.id;if(i!==r){if(ma(t))return a.updateId(t,fa(r)),void a.updateId(i,r);a.get(fa(i))&&a.updateId(fa(i),fa(r))}}}),!0),this.reverted("element.updateProperties",(function(e){var t=e.element;if(Ar(t,"bpmn:SubProcess")){var n=e.properties,i=e.oldProperties.id,r=n.id;if(i!==r){if(ma(t))return a.updateId(t,fa(i)),void a.updateId(r,i);var o=a.get(fa(r));o&&a.updateId(o,fa(i))}}}),!0),t.on("element.changed",(function(e){var n=e.element;if(ma(n)){var i=n,r=a.get(ha(i));r&&r!==i&&t.fire("element.changed",{element:r})}})),this.executed("shape.toggleCollapse",400,(function(e){var t=e.shape;Ar(t,"bpmn:SubProcess")&&(Wr(t)?l(e):(p(e),s._showRecursively(t.children)))}),!0),this.reverted("shape.toggleCollapse",400,(function(e){var t=e.shape;Ar(t,"bpmn:SubProcess")&&(Wr(t)?l(e):(p(e),s._showRecursively(t.children)))}),!0),this.postExecuted("shape.toggleCollapse",600,(function(e){var t=e.shape;if(Ar(t,"bpmn:SubProcess")){var n=e.newRootElement;n&&(Wr(t)?s._moveChildrenToShape(n,t):s._moveChildrenToShape(t,n))}}),!0),t.on("copyPaste.createTree",(function(e){var t=e.element,n=e.children;if(c(t)){var i=da(t),r=a.get(i);r&&n.push.apply(n,r.children)}})),t.on("copyPaste.copyElement",(function(e){var t=e.descriptor,n=e.element,i=e.elements,r=n.parent;if(Ar(Pr(r),"bpmndi:BPMNPlane")){var o=ha(r),a=m(i,(function(e){return e.id===o}));a&&(t.parent=a.id)}})),t.on("copyPaste.pasteElement",(function(e){var t=e.descriptor;t.parent&&(c(t.parent)||t.parent.hidden)&&(t.hidden=!0)}))}function kd(e,t){e.invoke(ra,this),this.postExecuted("shape.replace",(function(e){var n=e.context.oldShape,i=e.context.newShape;if(Ar(i,"bpmn:SubProcess")&&(Ar(n,"bpmn:Task")||Ar(n,"bpmn:CallActivity"))&&Wr(i)){var r,o={x:(r=i).x+r.width/6,y:r.y+r.height/2};t.createShape({type:"bpmn:StartEvent"},o,i)}}))}function Md(e,t){ra.call(this,e),this.postExecuted("shape.toggleCollapse",1500,(function(e){var n=e.shape;if(!Wr(n)){var i=mt(n);i.forEach((function(e){var t=e.incoming.slice(),n=e.outgoing.slice();g(t,(function(e){r(e,!0)})),g(n,(function(e){r(e,!1)}))}))}function r(e,r){-1!==i.indexOf(e.source)&&-1!==i.indexOf(e.target)||(r?t.reconnectEnd(e,n,gn(n)):t.reconnectStart(e,n,gn(n)))}}),!0)}e(Dd,ra),Dd.prototype._moveChildrenToShape=function(e,t){var n,i=this._modeling,r=e.children;if(r){var o=(r=r.concat(r.reduce((function(t,n){return n.label&&n.label.parent!==e?t.concat(n.label):t}),[]))).filter((function(e){return!e.hidden}));if(o.length){var a=vt(o);if(t.x){var s=gn(t),c=gn(a);n={x:s.x-c.x,y:s.y-c.y}}else n={x:Td-a.x,y:Pd-a.y};i.moveElements(r,n,t,{autoResize:!1})}else i.moveElements(r,{x:0,y:0},t,{autoResize:!1})}},Dd.prototype._showRecursively=function(e,t){var n=this,i=[];return e.forEach((function(e){e.hidden=!!t,i=i.concat(e),e.children&&(i=i.concat(n._showRecursively(e.children,e.collapsed||t)))})),i},Dd.prototype._addDiagram=function(e){var t=this._bpmnjs.getDefinitions().diagrams;return e.businessObject||(e=this._createNewDiagram(e)),t.push(e.di.$parent),e},Dd.prototype._createNewDiagram=function(e){var t=this._bpmnFactory,n=this._elementFactory,i=t.create("bpmndi:BPMNPlane",{bpmnElement:e}),r=t.create("bpmndi:BPMNDiagram",{plane:i});return i.$parent=r,n.createRoot({id:da(e),type:e.$type,di:i,businessObject:e,collapsed:!0})},Dd.prototype._removeDiagram=function(e){var t=this._bpmnjs.getDefinitions().diagrams,n=m(t,(function(t){return t.plane.bpmnElement.id===e.id}));return t.splice(t.indexOf(n),1),n},Dd.$inject=["canvas","eventBus","modeling","elementFactory","bpmnFactory","bpmnjs","elementRegistry"],kd.$inject=["injector","modeling"],e(kd,ra),e(Md,ra),Md.$inject=["eventBus","modeling"];function Bd(e,t,n,i){ra.call(this,e),this.executed(["shape.toggleCollapse"],500,(function(e){var t,n=e.context.shape;Ar(n,"bpmn:SubProcess")&&(n.collapsed?Pr(n).isExpanded=!1:((t=n.children).length&&t.forEach((function(e){"label"!==e.type||e.businessObject.name||(e.hidden=!0)})),Pr(n).isExpanded=!0))})),this.reverted(["shape.toggleCollapse"],500,(function(e){var t=e.context.shape;t.collapsed?Pr(t).isExpanded=!1:Pr(t).isExpanded=!0})),this.postExecuted(["shape.toggleCollapse"],500,(function(e){var i,r=e.context.shape,o=t.getDefaultSize(r);i=r.collapsed?function(e,t){return{x:e.x+(e.width-t.width)/2,y:e.y+(e.height-t.height)/2,width:t.width,height:t.height}}(r,o):function(e,t){var n,i,r,o=e.children,a=t;return n=(r=o,r.filter((function(e){return!e.hidden}))).concat([e]),(i=Dl(n))?(a.width=Math.max(i.width,a.width),a.height=Math.max(i.height,a.height),a.x=i.x+(i.width-a.width)/2,a.y=i.y+(i.height-a.height)/2):(a.x=e.x+(e.width-a.width)/2,a.y=e.y+(e.height-a.height)/2),a}(r,o),n.resizeShape(r,i,null,{autoResize:!r.collapsed&&"nwse"})}))}function Nd(e,t,n,i){t.invoke(ra,this),this.preExecute("shape.delete",(function(e){var t=e.context.shape,r=t.businessObject;Eo(t)||(Ar(t,"bpmn:Participant")&&Wr(t)&&n.ids.unclaim(r.processRef.id),i.unclaimId(r.id,r))})),this.preExecute("connection.delete",(function(e){var t=e.context.connection.businessObject;i.unclaimId(t.id,t)})),this.preExecute("canvas.updateRoot",(function(){var t=e.getRootElement(),i=t.businessObject;Ar(t,"bpmn:Collaboration")&&n.ids.unclaim(i.id)}))}function Od(e,t){ra.call(this,e),this.preExecute("connection.delete",(function(e){var n=e.context.connection,i=n.source;(function(e,t){if(!Ar(e,"bpmn:SequenceFlow"))return!1;var n=Tr(t),i=Tr(e);return n.get("default")===i})(n,i)&&t.updateProperties(i,{default:null})}))}e(Bd,ra),Bd.$inject=["eventBus","elementFactory","modeling"],e(Nd,ra),Nd.$inject=["canvas","injector","moddle","modeling"],e(Od,ra),Od.$inject=["eventBus","modeling"];function jd(e,t,n){var i;function r(){if(!i)throw new Error(n("out of bounds release"));return i}ra.call(this,e);var o=["spaceTool","lane.add","lane.resize","lane.split","elements.create","elements.delete","elements.move","shape.create","shape.delete","shape.move","shape.resize"];this.preExecute(o,5e3,(function(e){(i=i||new Ld).enter()})),this.postExecuted(o,500,(function(e){!function(){if(!i)throw new Error(n("out of bounds release"));var e=i.leave();e&&(t.updateLaneRefs(i.flowNodes,i.lanes),i=null)}()})),this.preExecute(["shape.create","shape.move","shape.delete","shape.resize"],(function(e){var t=e.context.shape,n=r();t.labelTarget||(Ar(t,"bpmn:Lane")&&n.addLane(t),Ar(t,"bpmn:FlowNode")&&n.addFlowNode(t))}))}function Ld(){this.flowNodes=[],this.lanes=[],this.counter=0,this.addLane=function(e){this.lanes.push(e)},this.addFlowNode=function(e){this.flowNodes.push(e)},this.enter=function(){this.counter++},this.leave=function(){return this.counter--,!this.counter}}jd.$inject=["eventBus","modeling","translate"],e(jd,ra);var Id={__init__:["adaptiveLabelPositioningBehavior","appendBehavior","associationBehavior","attachEventBehavior","boundaryEventBehavior","createBehavior","createDataObjectBehavior","createParticipantBehavior","dataInputAssociationBehavior","dataStoreBehavior","deleteLaneBehavior","detachEventBehavior","dropOnFlowBehavior","eventBasedGatewayBehavior","fixHoverBehavior","groupBehavior","importDockingFix","isHorizontalFix","labelBehavior","layoutConnectionBehavior","messageFlowBehavior","modelingFeedback","removeElementBehavior","removeEmbeddedLabelBoundsBehavior","removeParticipantBehavior","replaceConnectionBehavior","replaceElementBehaviour","resizeBehavior","resizeLaneBehavior","rootElementReferenceBehavior","spaceToolBehavior","subProcessPlaneBehavior","subProcessStartEventBehavior","toggleCollapseConnectionBehaviour","toggleElementCollapseBehaviour","unclaimIdBehavior","updateFlowNodeRefsBehavior","unsetDefaultFlowBehavior"],adaptiveLabelPositioningBehavior:["type",qu],appendBehavior:["type",Yu],associationBehavior:["type",Xu],attachEventBehavior:["type",Zu],boundaryEventBehavior:["type",Ju],createBehavior:["type",eh],createDataObjectBehavior:["type",th],createParticipantBehavior:["type",oh],dataInputAssociationBehavior:["type",sh],dataStoreBehavior:["type",lh],deleteLaneBehavior:["type",uh],detachEventBehavior:["type",hh],dropOnFlowBehavior:["type",fh],eventBasedGatewayBehavior:["type",yh],fixHoverBehavior:["type",xh],groupBehavior:["type",_h],importDockingFix:["type",wh],isHorizontalFix:["type",Ch],labelBehavior:["type",Vh],layoutConnectionBehavior:["type",Uh],messageFlowBehavior:["type",td],modelingFeedback:["type",nd],removeElementBehavior:["type",rd],removeEmbeddedLabelBoundsBehavior:["type",id],removeParticipantBehavior:["type",ad],replaceConnectionBehavior:["type",sd],replaceElementBehaviour:["type",cd],resizeBehavior:["type",md],resizeLaneBehavior:["type",Ed],rootElementReferenceBehavior:["type",wd],spaceToolBehavior:["type",Cd],subProcessPlaneBehavior:["type",Dd],subProcessStartEventBehavior:["type",kd],toggleCollapseConnectionBehaviour:["type",Md],toggleElementCollapseBehaviour:["type",Bd],unclaimIdBehavior:["type",Nd],unsetDefaultFlowBehavior:["type",Od],updateFlowNodeRefsBehavior:["type",jd]};function Fd(e,t){var n=bn(e,t,-15);return"intersect"!==n?n:null}function zd(e){ks.call(this,e)}function $d(e){return!e||Eo(e)}function Hd(e){do{if(Ar(e,"bpmn:Process"))return Tr(e);if(Ar(e,"bpmn:Participant"))return Tr(e).processRef||Tr(e)}while(e=e.parent)}function Gd(e){return Ar(e,"bpmn:TextAnnotation")}function Vd(e){return Ar(e,"bpmn:Group")&&!e.labelTarget}function Wd(e){return Ar(e,"bpmn:BoundaryEvent")&&Kd(e,"bpmn:CompensateEventDefinition")}function Ud(e){return Tr(e).isForCompensation}function qd(e){for(var t=e;t=t.parent;){if(Ar(t,"bpmn:FlowElementsContainer"))return Tr(t);if(Ar(t,"bpmn:Participant"))return Tr(t).processRef}return null}function Kd(e,t){return!!m(Tr(e).eventDefinitions||[],(function(e){return Ar(e,t)}))}function Yd(e,t){return(Tr(e).eventDefinitions||[]).every((function(e){return Ar(e,t)}))}function Xd(e){return Ar(e,"bpmn:ReceiveTask")||Ar(e,"bpmn:IntermediateCatchEvent")&&(Kd(e,"bpmn:MessageEventDefinition")||Kd(e,"bpmn:TimerEventDefinition")||Kd(e,"bpmn:ConditionalEventDefinition")||Kd(e,"bpmn:SignalEventDefinition"))}function Zd(e){return e.waypoints}function Qd(e,t){var n=function(e){for(var t=[];e;)(e=e.parent)&&t.push(e);return t}(t);return-1!==n.indexOf(e)}function Jd(e,t,n){if($d(e)||$d(t))return null;if(!Ar(n,"bpmn:DataAssociation")){if(df(e,t))return{type:"bpmn:MessageFlow"};if(ff(e,t))return{type:"bpmn:SequenceFlow"}}var i=mf(e,t);return i||(Wd(e)&&Ud(t)?{type:"bpmn:Association",associationDirection:"One"}:!!hf(e,t)&&{type:"bpmn:Association"})}function ef(e,t,n){return!(!Eo(e)&&!Vd(e))||!(Ar(t,"bpmn:Participant")&&!Wr(t))&&(Ar(e,"bpmn:Participant")?Ar(t,"bpmn:Process")||Ar(t,"bpmn:Collaboration"):Rr(e,["bpmn:DataInput","bpmn:DataOutput"])&&e.parent?t===e.parent:Ar(e,"bpmn:Lane")?Ar(t,"bpmn:Participant")||Ar(t,"bpmn:Lane"):!(Ar(e,"bpmn:BoundaryEvent")&&(i=e,!Tr(i).cancelActivity||!rf(i)&&!of(i)))&&(Ar(e,"bpmn:FlowElement")&&!Ar(e,"bpmn:DataStoreReference")?Ar(t,"bpmn:FlowElementsContainer")?Wr(t):Rr(t,["bpmn:Participant","bpmn:Lane"]):Ar(e,"bpmn:DataStoreReference")&&Ar(t,"bpmn:Collaboration")?E(Tr(t).get("participants"),(function(e){return!!e.get("processRef")})):Rr(e,["bpmn:Artifact","bpmn:DataAssociation","bpmn:DataStoreReference"])?Rr(t,["bpmn:Collaboration","bpmn:Lane","bpmn:Participant","bpmn:Process","bpmn:SubProcess"]):!!Ar(e,"bpmn:MessageFlow")&&(Ar(t,"bpmn:Collaboration")||e.source.parent==t||e.target.parent==t)));var i}function tf(e){return Ar(e,"bpmn:Lane")}function nf(e){return!!function(e){return!Eo(e)&&Ar(e,"bpmn:BoundaryEvent")}(e)||(!(!Ar(e,"bpmn:IntermediateThrowEvent")||!rf(e))||Ar(e,"bpmn:IntermediateCatchEvent")&&of(e))}function rf(e){var t=Tr(e);return t&&!(t.eventDefinitions&&t.eventDefinitions.length)}function of(e){return af(e,["bpmn:MessageEventDefinition","bpmn:TimerEventDefinition","bpmn:SignalEventDefinition","bpmn:ConditionalEventDefinition"])}function af(e,t){return t.some((function(t){return Kd(e,t)}))}function sf(e,t,n,i){if(Array.isArray(e)||(e=[e]),1!==e.length)return!1;var r=e[0];return!Eo(r)&&(!!nf(r)&&(!Ur(t)&&(!(!Ar(t,"bpmn:Activity")||Ud(t))&&(!(i&&!Fd(i,t))&&(!function(e){return Ar(e,"bpmn:ReceiveTask")&&m(e.incoming,(function(e){return Ar(e.source,"bpmn:EventBasedGateway")}))}(t)&&"attach")))))}function cf(e,t,n){if(!t)return!1;var i={replacements:[]};return g(e,(function(e){Ur(t)||Ar(e,"bpmn:StartEvent")&&"label"!==e.type&&ef(e,t)&&(function(e){return e&&!1!==Tr(e).isInterrupting}(e)||i.replacements.push({oldElementId:e.id,newElementType:"bpmn:StartEvent"}),(function(e){return qr(e,"bpmn:ErrorEventDefinition")}(e)||function(e){return qr(e,"bpmn:EscalationEventDefinition")}(e)||function(e){return qr(e,"bpmn:CompensateEventDefinition")}(e))&&i.replacements.push({oldElementId:e.id,newElementType:"bpmn:StartEvent"}),af(e,["bpmn:MessageEventDefinition","bpmn:TimerEventDefinition","bpmn:SignalEventDefinition","bpmn:ConditionalEventDefinition"])&&Ar(t,"bpmn:SubProcess")&&i.replacements.push({oldElementId:e.id,newElementType:"bpmn:StartEvent"})),Ar(t,"bpmn:Transaction")||Kd(e,"bpmn:CancelEventDefinition")&&"label"!==e.type&&(Ar(e,"bpmn:EndEvent")&&ef(e,t)&&i.replacements.push({oldElementId:e.id,newElementType:"bpmn:EndEvent"}),Ar(e,"bpmn:BoundaryEvent")&&sf(e,t,0,n)&&i.replacements.push({oldElementId:e.id,newElementType:"bpmn:BoundaryEvent"}))})),!!i.replacements.length&&i}function pf(e,t){return!E(e,tf)&&(!t||e.every((function(e){return ef(e,t)})))}function lf(e,t,n,i){return!!t&&(!(!Eo(e)&&!Vd(e))||n!==t&&((!n||!Qd(n,t))&&(ef(e,t)||vf(e,t))))}function uf(e,t){return Ar(e,"bpmn:SubProcess")?Wr(e)&&(!t||t.width>=100&&t.height>=80):Ar(e,"bpmn:Lane")?!t||t.width>=130&&t.height>=60:Ar(e,"bpmn:Participant")?!t||t.width>=250&&t.height>=50:!!Gd(e)||!!Vd(e)}function hf(e,t){return!(!Wd(e)||!Ud(t))||!Qd(t,e)&&!Qd(e,t)&&(!!function(e,t){var n=Gd(e),i=Gd(t);return(n||i)&&n!==i}(e,t)||!!mf(e,t))}function df(e,t){return!(bf(e)&&!bf(t))&&(Ar(i=e,"bpmn:InteractionNode")&&!Ar(i,"bpmn:BoundaryEvent")&&(!Ar(i,"bpmn:Event")||Ar(i,"bpmn:ThrowEvent")&&Yd(i,"bpmn:MessageEventDefinition"))&&function(e){return Ar(e,"bpmn:InteractionNode")&&!Ud(e)&&(!Ar(e,"bpmn:Event")||Ar(e,"bpmn:CatchEvent")&&Yd(e,"bpmn:MessageEventDefinition"))&&!(Ar(e,"bpmn:BoundaryEvent")&&!Kd(e,"bpmn:MessageEventDefinition"))}(t)&&(n=t,!(Hd(e)===Hd(n))));var n,i}function ff(e,t){return!(Xd(t)&&t.incoming.length>0&&(n=t.incoming,(n=n||[]).some(gf))&&!Ar(e,"bpmn:EventBasedGateway"))&&(Ar(r=e,"bpmn:FlowNode")&&!Ar(r,"bpmn:EndEvent")&&!Ur(r)&&!(Ar(r,"bpmn:IntermediateThrowEvent")&&Kd(r,"bpmn:LinkEventDefinition"))&&!Wd(r)&&!Ud(r)&&function(e){return Ar(e,"bpmn:FlowNode")&&!Ar(e,"bpmn:StartEvent")&&!Ar(e,"bpmn:BoundaryEvent")&&!Ur(e)&&!(Ar(e,"bpmn:IntermediateCatchEvent")&&Kd(e,"bpmn:LinkEventDefinition"))&&!Ud(e)}(t)&&(i=t,qd(e)===qd(i))&&!(Ar(e,"bpmn:EventBasedGateway")&&!Xd(t)));var n,i,r}function mf(e,t){return Rr(e,["bpmn:DataObjectReference","bpmn:DataStoreReference"])&&Rr(t,["bpmn:Activity","bpmn:ThrowEvent"])?{type:"bpmn:DataInputAssociation"}:!(!Rr(t,["bpmn:DataObjectReference","bpmn:DataStoreReference"])||!Rr(e,["bpmn:Activity","bpmn:CatchEvent"]))&&{type:"bpmn:DataOutputAssociation"}}function vf(e,t,n){if(!t)return!1;if(Array.isArray(e)){if(1!==e.length)return!1;e=e[0]}return t.source!==e&&t.target!==e&&(Rr(t,["bpmn:SequenceFlow","bpmn:MessageFlow"])&&!Eo(t)&&Ar(e,"bpmn:FlowNode")&&!Ar(e,"bpmn:BoundaryEvent")&&ef(e,t.parent))}function yf(e,t){return!!Eo(t)||!(Ar(t,"bpmn:Lane")&&!function(e,t){return e&&t&&-1!==e.indexOf(t)}(e,t.parent))}function gf(e){if(e&&e.source)return Ar(e.source,"bpmn:EventBasedGateway")}function bf(e){return Hs(e,"bpmn:Process")||Hs(e,"bpmn:Collaboration")}e(zd,ks),zd.$inject=["eventBus"],zd.prototype.init=function(){this.addRule("connection.start",(function(e){return function(e){if($d(e))return null;return Rr(e,["bpmn:FlowNode","bpmn:InteractionNode","bpmn:DataObjectReference","bpmn:DataStoreReference","bpmn:Group","bpmn:TextAnnotation"])}(e.source)})),this.addRule("connection.create",(function(e){var t=e.source,n=e.target,i=e.hints||{},r=i.targetParent;if(i.targetAttach)return!1;r&&(n.parent=r);try{return Jd(t,n)}finally{r&&(n.parent=null)}})),this.addRule("connection.reconnect",(function(e){var t=e.connection;return Jd(e.source,e.target,t)})),this.addRule("connection.updateWaypoints",(function(e){return{type:e.connection.type}})),this.addRule("shape.resize",(function(e){return uf(e.shape,e.newBounds)})),this.addRule("elements.create",(function(e){var t=e.elements,n=e.position,i=e.target;return!(Zd(i)&&!vf(t,i))&&_(t,(function(e){return Zd(e)?Jd(e.source,e.target,e):e.host?sf(e,e.host,null,n):lf(e,i,null)}))})),this.addRule("elements.move",(function(e){var t=e.target,n=e.shapes,i=e.position;return sf(n,t,null,i)||cf(n,t,i)||pf(n,t)||vf(n,t)})),this.addRule("shape.create",(function(e){return lf(e.shape,e.target,e.source,e.position)})),this.addRule("shape.attach",(function(e){return sf(e.shape,e.target,null,e.position)})),this.addRule("element.copy",(function(e){var t=e.element;return yf(e.elements,t)}))},zd.prototype.canConnectMessageFlow=df,zd.prototype.canConnectSequenceFlow=ff,zd.prototype.canConnectDataAssociation=mf,zd.prototype.canConnectAssociation=hf,zd.prototype.canMove=pf,zd.prototype.canAttach=sf,zd.prototype.canReplace=cf,zd.prototype.canDrop=ef,zd.prototype.canInsert=vf,zd.prototype.canCreate=lf,zd.prototype.canConnect=Jd,zd.prototype.canResize=uf,zd.prototype.canCopy=yf;var xf={__depends__:[pc],__init__:["bpmnRules"],bpmnRules:["type",zd]};function _f(e,t){e.on("saveXML.start",2e3,(function(){g(t.getRootElements(),(function(e){var t,n=Pr(e);t=w(y(mt([e],!1),(function(t){return t!==e&&!t.labelTarget})),Pr),n.set("planeElement",t)}))}))}_f.$inject=["eventBus","canvas"];var Ef={__init__:["bpmnDiOrdering"],bpmnDiOrdering:["type",_f]};function wf(e){ra.call(this,e);var t=this;this.preExecute(["shape.create","connection.create"],(function(e){var n=e.context,i=n.shape||n.connection,r=n.parent,o=t.getOrdering(i,r);o&&(void 0!==o.parent&&(n.parent=o.parent),n.parentIndex=o.index)})),this.preExecute(["shape.move","connection.move"],(function(e){var n=e.context,i=n.shape||n.connection,r=n.newParent||i.parent,o=t.getOrdering(i,r);o&&(void 0!==o.parent&&(n.newParent=o.parent),n.newParentIndex=o.index)}))}function Sf(e,t,n){wf.call(this,e);var i=[{type:"bpmn:SubProcess",order:{level:6}},{type:"bpmn:SequenceFlow",order:{level:9,containers:["bpmn:Participant","bpmn:FlowElementsContainer"]}},{type:"bpmn:DataAssociation",order:{level:9,containers:["bpmn:Collaboration","bpmn:FlowElementsContainer"]}},{type:"bpmn:MessageFlow",order:{level:9,containers:["bpmn:Collaboration"]}},{type:"bpmn:Association",order:{level:6,containers:["bpmn:Participant","bpmn:FlowElementsContainer","bpmn:Collaboration"]}},{type:"bpmn:BoundaryEvent",order:{level:8}},{type:"bpmn:Group",order:{level:10,containers:["bpmn:Collaboration","bpmn:FlowElementsContainer"]}},{type:"bpmn:FlowElement",order:{level:5}},{type:"bpmn:Participant",order:{level:-2}},{type:"bpmn:Lane",order:{level:-1}}];function r(e){var t=e.order;if(t||(e.order=t=function(e){if(e.labelTarget)return{level:10};var t=m(i,(function(t){return Rr(e,[t.type])}));return t&&t.order||{level:1}}(e)),!t)throw new Error("no order for <"+e.id+">");return t}this.getOrdering=function(e,n){if(e.labelTarget)return{parent:t.findRoot(n)||t.getRootElement(),index:-1};var i=r(e);i.containers&&(n=function(e,t,n){for(var i=t;i&&!Rr(i,n);)i=i.parent;if(!i)throw new Error("no parent for <"+e.id+"> in <"+(t&&t.id)+">");return i}(e,n,i.containers));var o=n.children.indexOf(e),a=v(n.children,(function(t){return!(!e.labelTarget&&t.labelTarget)&&i.level");this._pushAction(e),t||(this._fire(i,"preExecute",e),o.preExecute&&o.preExecute(r),this._fire(i,"preExecuted",e)),this._atomicDo((function(){n._fire(i,"execute",e),o.execute&&n._markDirty(o.execute(r)),n._executedAction(e,t),n._fire(i,"executed",e)})),t||(this._fire(i,"postExecute",e),o.postExecute&&o.postExecute(r),this._fire(i,"postExecuted",e)),this._popAction(e)},Af.prototype._pushAction=function(e){var t=this._currentExecution,n=t.actions,i=n[0];if(t.atomic)throw new Error("illegal invocation in or phase (action: "+e.command+")");e.id||(e.id=i&&i.id||this._createId()),n.push(e)},Af.prototype._popAction=function(){var e=this._currentExecution,t=e.trigger,n=e.actions,i=e.dirty;n.pop(),n.length||(this._eventBus.fire("elements.changed",{elements:T("id",i.reverse())}),i.length=0,this._fire("changed",{trigger:t}),e.trigger=null)},Af.prototype._markDirty=function(e){var t=this._currentExecution;e&&(e=p(e)?e:[e],t.dirty=t.dirty.concat(e))},Af.prototype._executedAction=function(e,t){var n=++this._stackIdx;t||this._stack.splice(n,this._stack.length,e)},Af.prototype._revertedAction=function(e){this._stackIdx--},Af.prototype._getHandler=function(e){return this._handlerMap[e]},Af.prototype._setHandler=function(e,t){if(!e||!t)throw new Error("command and handler required");if(this._handlerMap[e])throw new Error("overriding handler for command <"+e+">");this._handlerMap[e]=t};var Rf={commandStack:["type",Af]},Tf=new Yo("tt");function Pf(e,t){e.style.display=!1===t?"none":""}var Df=".djs-tooltip";function kf(e,t){var n,i;this._eventBus=e,this._canvas=t,this._ids=Tf,this._tooltipDefaults={show:{minZoom:.7,maxZoom:5}},this._tooltips={},this._tooltipRoot=(n=t.getContainer(),K(i=fe('
      '),{position:"absolute",width:"0",height:"0"}),n.insertBefore(i,n.firstChild),i);var r=this;de.bind(this._tooltipRoot,Df,"mousedown",(function(e){e.stopPropagation()})),de.bind(this._tooltipRoot,Df,"mouseover",(function(e){r.trigger("mouseover",e)})),de.bind(this._tooltipRoot,Df,"mouseout",(function(e){r.trigger("mouseout",e)})),this._init()}kf.$inject=["eventBus","canvas"],kf.prototype.add=function(e){if(!e.position)throw new Error("must specifiy tooltip position");if(!e.html)throw new Error("must specifiy tooltip html");var t=this._ids.next();return e=F({},this._tooltipDefaults,e,{id:t}),this._addTooltip(e),e.timeout&&this.setTimeout(e),t},kf.prototype.trigger=function(e,t){var n=t.delegateTarget||t.target,i=this.get(Y(n,"data-tooltip-id"));i&&("mouseover"===e&&i.timeout&&this.clearTimeout(i),"mouseout"===e&&i.timeout&&(i.timeout=1e3,this.setTimeout(i)))},kf.prototype.get=function(e){return"string"!=typeof e&&(e=e.id),this._tooltips[e]},kf.prototype.clearTimeout=function(e){if(e=this.get(e)){var t=e.removeTimer;t&&(clearTimeout(t),e.removeTimer=null)}},kf.prototype.setTimeout=function(e){if(e=this.get(e)){this.clearTimeout(e);var t=this;e.removeTimer=setTimeout((function(){t.remove(e)}),e.timeout)}},kf.prototype.remove=function(e){var t=this.get(e);t&&(be(t.html),be(t.htmlContainer),delete t.htmlContainer,delete this._tooltips[t.id])},kf.prototype.show=function(){Pf(this._tooltipRoot)},kf.prototype.hide=function(){Pf(this._tooltipRoot,!1)},kf.prototype._updateRoot=function(e){var t=e.scale||1,n=e.scale||1,i="matrix("+t+",0,0,"+n+","+-1*e.x*t+","+-1*e.y*n+")";this._tooltipRoot.style.transform=i,this._tooltipRoot.style["-ms-transform"]=i},kf.prototype._addTooltip=function(e){var t,n=e.id,i=e.html,r=this._tooltipRoot;i.get&&i.constructor.prototype.jquery&&(i=i.get(0)),d(i)&&(i=fe(i)),K(t=fe('
      '),{position:"absolute"}),t.appendChild(i),e.type&&ee(t).add("djs-tooltip-"+e.type),e.className&&ee(t).add(e.className),e.htmlContainer=t,r.appendChild(t),this._tooltips[n]=e,this._updateTooltip(e)},kf.prototype._updateTooltip=function(e){var t,n,i,r=e.position,o=e.htmlContainer;t=o,n=r.x,i=r.y,K(t,{left:n+"px",top:i+"px"})},kf.prototype._updateTooltipVisibilty=function(e){g(this._tooltips,(function(t){var n=t.show,i=t.htmlContainer,r=!0;n&&((n.minZoom>e.scale||n.maxZoomVf(e.dy)?"x":"y",i=e["d"+n],r=e[n]-i;if(Vf(i)<5)return!1;i<0&&(i*=-1),No(e)&&(i*=-1);var o=function(e,t){if("x"===e){if(t>0)return"e";if(t<0)return"w"}if("y"===e){if(t>0)return"s";if(t<0)return"n"}return null}(n,i),a=mt(this._canvas.getRootElement(),!0),s=this.calculateAdjustments(a,n,i,r),c=function(e,t,n,i,r){var o=e.movingShapes,a=e.resizingShapes;if(!a.length)return;var s,c,p={};return g(a,(function(e){var l,h,d,f=mn(e),m=y(e.children,(function(e){return!(em(e)||tm(e)||Jf(o,e)||Jf(a,e))})),v=y(e.children,(function(e){return!em(e)&&!tm(e)&&Jf(o,e)}));m.length&&(h=Zf(mn(vt(m))),l=i-f[Kf[n]]+h[Kf[n]],"n"===n?p.bottom=c=u(c)?Math.min(c,l):l:"w"===n?p.right=c=u(c)?Math.min(c,l):l:"s"===n?p.top=s=u(s)?Math.max(s,l):l:"e"===n&&(p.left=s=u(s)?Math.max(s,l):l)),v.length&&(d=Zf(mn(vt(v))),l=i-d[Kf[Yf[n]]]+f[Kf[Yf[n]]],"n"===n?p.bottom=c=u(c)?Math.min(c,l):l:"w"===n?p.right=c=u(c)?Math.min(c,l):l:"s"===n?p.top=s=u(s)?Math.max(s,l):l:"e"===n&&(p.left=s=u(s)?Math.max(s,l):l));var g=r&&r[e.id];g&&("n"===n?(l=i+e[Uf[t]]-g[Uf[t]],p.bottom=c=u(c)?Math.min(c,l):l):"w"===n?(l=i+e[Uf[t]]-g[Uf[t]],p.right=c=u(c)?Math.min(c,l):l):"s"===n?(l=i-e[Uf[t]]+g[Uf[t]],p.top=s=u(s)?Math.max(s,l):l):"e"===n&&(l=i-e[Uf[t]]+g[Uf[t]],p.left=s=u(s)?Math.max(s,l):l))})),p}(s,n,o,r,this._eventBus.fire("spaceTool.getMinDimensions",{axis:n,direction:o,shapes:s.resizingShapes,start:r}));return F(t,s,{axis:n,direction:o,spaceToolConstraints:c,start:r}),Va("resize-"+("x"===n?"ew":"ns")),!0},Xf.prototype.calculateAdjustments=function(e,t,n,i){var r=this._rules,o=[],a=[];return g(e,(function(e){if(e.parent&&!em(e)){var s=e[t],c=s+e[Uf[t]];return n>0&&s>i||n<0&&ci&&r.allowed("shape.resize",{shape:e})?a.push(e):void 0}})),{movingShapes:o,resizingShapes:a}},Xf.prototype.toggle=function(){if(this.isActive())return this._dragging.cancel();var e=this._mouse.getLastMoveEvent();this.activateSelection(e,!!e)},Xf.prototype.isActive=function(){var e=this._dragging.context();return e&&/^spaceTool/.test(e.prefix)};var nm="djs-dragging",im="djs-resizing",rm=Math.max;function om(e,t,n,i,r){function o(e,t){g(e,(function(e){r.addDragger(e,t),n.addMarker(e,nm)}))}e.on("spaceTool.selection.start",(function(e){var t=n.getLayer("space"),r=e.context,o="M 0,-10000 L 0,10000",a="M -10000,0 L 10000,0",s=Le("g");Se(s,i.cls("djs-crosshair-group",["no-events"])),_e(t,s);var c=Le("path");Se(c,"d",o),Pe(c).add("djs-crosshair"),_e(s,c);var p=Le("path");Se(p,"d",a),Pe(p).add("djs-crosshair"),_e(s,p),r.crosshairGroup=s})),e.on("spaceTool.selection.move",(function(e){Jn(e.context.crosshairGroup,e.x,e.y)})),e.on("spaceTool.selection.cleanup",(function(e){var t=e.context.crosshairGroup;t&&ke(t)})),e.on("spaceTool.move",250,(function(e){var a=e.context,s=a.line,c=a.axis,p=a.movingShapes,l=a.resizingShapes;if(a.initialized){if(!a.dragGroup){var u=n.getLayer("space");Se(s=Le("path"),"d","M0,0 L0,0"),Pe(s).add("djs-crosshair"),_e(u,s),a.line=s;var h=Le("g");Se(h,i.cls("djs-drag-group",["no-events"])),_e(n.getActiveLayer(),h),o(p,h),o(a.movingConnections=t.filter((function(e){var t=!1;g(p,(function(n){g(n.outgoing,(function(n){e===n&&(t=!0)}))}));var n=!1;g(p,(function(t){g(t.incoming,(function(t){e===t&&(n=!0)}))}));var i=!1;g(l,(function(t){g(t.outgoing,(function(t){e===t&&(i=!0)}))}));var r=!1;return g(l,(function(t){g(t.incoming,(function(t){e===t&&(r=!0)}))})),function(e){return e.waypoints}(e)&&(t||i)&&(n||r)})),h),a.dragGroup=h}if(!a.frameGroup){var d=Le("g");Se(d,i.cls("djs-frame-group",["no-events"])),_e(n.getActiveLayer(),d);var f=[];g(l,(function(e){var t=r.addFrame(e,d),i=t.getBBox();f.push({element:t,initialBounds:i}),n.addMarker(e,im)})),a.frameGroup=d,a.frames=f}Se(s,{d:{x:"M"+e.x+", -10000 L"+e.x+", 10000",y:"M -10000, "+e.y+" L 10000, "+e.y}[c]});var m={x:e.dx,y:e.dy};m[{x:"y",y:"x"}[a.axis]]=0,Jn(a.dragGroup,m.x,m.y),g(a.frames,(function(e){var t,n,i=e.element,r=e.initialBounds;"e"===a.direction?Se(i,{width:rm(r.width+m.x,5)}):Se(i,{width:t=rm(r.width-m.x,5),x:r.x+r.width-t}),"s"===a.direction?Se(i,{height:rm(r.height+m.y,5)}):Se(i,{height:n=rm(r.height-m.y,5),y:r.y+r.height-n})}))}})),e.on("spaceTool.cleanup",(function(e){var t=e.context,i=t.movingShapes,r=t.movingConnections,o=t.resizingShapes,a=t.line,s=t.dragGroup,c=t.frameGroup;g(i,(function(e){n.removeMarker(e,nm)})),g(r,(function(e){n.removeMarker(e,nm)})),s&&(ke(a),ke(s)),g(o,(function(e){n.removeMarker(e,im)})),c&&ke(c)}))}om.$inject=["eventBus","elementRegistry","canvas","styles","previewSupport"];var am={__init__:["spaceToolPreview"],__depends__:[rc,pc,Hf,wp,Ip],spaceTool:["type",Xf],spaceToolPreview:["type",om]};function sm(e){this._model=e}function cm(e,t,n,i){ra.call(this,e),this._bpmnFactory=t,this._translate=i;var r=this;function o(e){g(e.context.oldRoot.children,(function(e){Ar(e,"bpmn:BaseElement")&&r.updateParent(e)}))}function a(e){var t=e.context.shape;Ar(t,"bpmn:BaseElement")&&r.updateBounds(t)}function s(e){r.updateConnection(e.context)}function c(e){r.updateConnectionWaypoints(e.context.connection)}function p(e){r.updateAttachment(e.context)}this.executed(["connection.layout","connection.create"],(function(e){var t,i=e.context,r=i.hints||{};i.cropped||!1===r.createElementsBehavior||((t=i.connection).waypoints=n.getCroppedWaypoints(t),i.cropped=!0)})),this.reverted(["connection.layout"],(function(e){delete e.context.cropped})),this.executed(["shape.move","shape.create","shape.delete","connection.create","connection.move","connection.delete"],pm((function(e){var t=e.context;r.updateParent(t.shape||t.connection,t.oldParent)}))),this.reverted(["shape.move","shape.create","shape.delete","connection.create","connection.move","connection.delete"],pm((function(e){var t=e.context,n=t.shape||t.connection,i=t.parent||t.newParent;r.updateParent(n,i)}))),this.executed(["canvas.updateRoot"],o),this.reverted(["canvas.updateRoot"],o),this.executed(["shape.move","shape.create","shape.resize"],pm((function(e){"label"!==e.context.shape.type&&a(e)}))),this.reverted(["shape.move","shape.create","shape.resize"],pm((function(e){"label"!==e.context.shape.type&&a(e)}))),e.on("shape.changed",(function(e){"label"===e.element.type&&a({context:{shape:e.element}})})),this.executed(["connection.create","connection.move","connection.delete","connection.reconnect"],pm(s)),this.reverted(["connection.create","connection.move","connection.delete","connection.reconnect"],pm(s)),this.executed(["connection.layout","connection.move","connection.updateWaypoints"],pm(c)),this.reverted(["connection.layout","connection.move","connection.updateWaypoints"],pm(c)),this.executed("connection.reconnect",pm((function(e){var t=e.context,n=t.connection,i=t.oldSource,r=t.newSource,o=Tr(n),a=Tr(i),s=Tr(r);o.conditionExpression&&!Rr(s,["bpmn:Activity","bpmn:ExclusiveGateway","bpmn:InclusiveGateway"])&&(t.oldConditionExpression=o.conditionExpression,delete o.conditionExpression),i!==r&&a.default===o&&(t.oldDefault=a.default,delete a.default)}))),this.reverted("connection.reconnect",pm((function(e){var t=e.context,n=t.connection,i=t.oldSource,r=t.newSource,o=Tr(n),a=Tr(i),s=Tr(r);t.oldConditionExpression&&(o.conditionExpression=t.oldConditionExpression),t.oldDefault&&(a.default=t.oldDefault,delete s.default)}))),this.executed(["element.updateAttachment"],pm(p)),this.reverted(["element.updateAttachment"],pm(p))}function pm(e){return function(t){var n=t.context;Ar(n.shape||n.connection,"bpmn:BaseElement")&&e(t)}}function lm(e,t,n){qn.call(this),this._bpmnFactory=e,this._moddle=t,this._translate=n}function um(e,t,n){return void 0===t[n]?t:(e[n]=t[n],$(t,[n]))}function hm(e,t){this._modeling=e,this._canvas=t}function dm(e){this._modeling=e}function fm(e,t){this._canvas=e,this._layouter=t}sm.$inject=["moddle"],sm.prototype._needsId=function(e){return Rr(e,["bpmn:RootElement","bpmn:FlowElement","bpmn:MessageFlow","bpmn:DataAssociation","bpmn:Artifact","bpmn:Participant","bpmn:Lane","bpmn:LaneSet","bpmn:Process","bpmn:Collaboration","bpmndi:BPMNShape","bpmndi:BPMNEdge","bpmndi:BPMNDiagram","bpmndi:BPMNPlane","bpmn:Property","bpmn:CategoryValue"])},sm.prototype._ensureId=function(e){var t;e.id?this._model.ids.claim(e.id,e):(t=Ar(e,"bpmn:Activity")?"Activity":Ar(e,"bpmn:Event")?"Event":Ar(e,"bpmn:Gateway")?"Gateway":Rr(e,["bpmn:SequenceFlow","bpmn:MessageFlow"])?"Flow":(e.$type||"").replace(/^[^:]*:/g,""),t+="_",!e.id&&this._needsId(e)&&(e.id=this._model.ids.nextPrefixed(t,e)))},sm.prototype.create=function(e,t){var n=this._model.create(e,t||{});return this._ensureId(n),n},sm.prototype.createDiLabel=function(){return this.create("bpmndi:BPMNLabel",{bounds:this.createDiBounds()})},sm.prototype.createDiShape=function(e,t){return this.create("bpmndi:BPMNShape",F({bpmnElement:e,bounds:this.createDiBounds()},t))},sm.prototype.createDiBounds=function(e){return this.create("dc:Bounds",e)},sm.prototype.createDiWaypoints=function(e){var t=this;return w(e,(function(e){return t.createDiWaypoint(e)}))},sm.prototype.createDiWaypoint=function(e){return this.create("dc:Point",z(e,["x","y"]))},sm.prototype.createDiEdge=function(e,t){return this.create("bpmndi:BPMNEdge",F({bpmnElement:e,waypoint:this.createDiWaypoints([])},t))},sm.prototype.createDiPlane=function(e,t){return this.create("bpmndi:BPMNPlane",F({bpmnElement:e},t))},e(cm,ra),cm.$inject=["eventBus","bpmnFactory","connectionDocking","translate"],cm.prototype.updateAttachment=function(e){var t=e.shape,n=t.businessObject,i=t.host;n.attachedToRef=i&&i.businessObject},cm.prototype.updateParent=function(e,t){if(!(e instanceof Vn||Ar(e,"bpmn:DataStoreReference")&&e.parent&&Ar(e.parent,"bpmn:Collaboration"))){var n=e.parent,i=e.businessObject,r=Pr(e),o=n&&n.businessObject,a=Pr(n);Ar(e,"bpmn:FlowNode")&&this.updateFlowNodeRefs(i,o,t&&t.businessObject),Ar(e,"bpmn:DataOutputAssociation")&&(o=e.source?e.source.businessObject:null),Ar(e,"bpmn:DataInputAssociation")&&(o=e.target?e.target.businessObject:null),this.updateSemanticParent(i,o),Ar(e,"bpmn:DataObjectReference")&&i.dataObjectRef&&this.updateSemanticParent(i.dataObjectRef,o),this.updateDiParent(r,a)}},cm.prototype.updateBounds=function(e){var t=Pr(e),n=function(e){if(!Ar(e,"bpmn:Activity"))return;var t=Pr(e);if(!t)return;var n=t.get("label");if(!n)return;return n.get("bounds")}(e);if(n){var i=Ka(n,t.get("bounds"));F(n,{x:e.x+i.x,y:e.y+i.y})}var r=e instanceof Vn?this._getLabel(t):t,o=r.bounds;o||(o=this._bpmnFactory.createDiBounds(),r.set("bounds",o)),F(o,{x:e.x,y:e.y,width:e.width,height:e.height})},cm.prototype.updateFlowNodeRefs=function(e,t,n){n!==t&&(Ar(n,"bpmn:Lane")&&Et(n.get("flowNodeRef"),e),Ar(t,"bpmn:Lane")&&wt(t.get("flowNodeRef"),e))},cm.prototype.updateDiConnection=function(e,t,n){var i=Pr(e),r=Pr(t),o=Pr(n);i.sourceElement&&i.sourceElement.bpmnElement!==Tr(t)&&(i.sourceElement=t&&r),i.targetElement&&i.targetElement.bpmnElement!==Tr(n)&&(i.targetElement=n&&o)},cm.prototype.updateDiParent=function(e,t){if(t&&!Ar(t,"bpmndi:BPMNPlane")&&(t=t.$parent),e.$parent!==t){var n=(t||e.$parent).get("planeElement");t?(n.push(e),e.$parent=t):(Et(n,e),e.$parent=null)}},cm.prototype.getLaneSet=function(e){var t,n;return Ar(e,"bpmn:Lane")?((t=e.childLaneSet)||(t=this._bpmnFactory.create("bpmn:LaneSet"),e.childLaneSet=t,t.$parent=e),t):(Ar(e,"bpmn:Participant")&&(e=e.processRef),(t=(n=e.get("laneSets"))[0])||((t=this._bpmnFactory.create("bpmn:LaneSet")).$parent=e,n.push(t)),t)},cm.prototype.updateSemanticParent=function(e,t,n){var i,r=this._translate;if(e.$parent!==t&&(!Ar(e,"bpmn:DataInput")&&!Ar(e,"bpmn:DataOutput")||(Ar(t,"bpmn:Participant")&&"processRef"in t&&(t=t.processRef),!("ioSpecification"in t)||t.ioSpecification!==e.$parent))){if(Ar(e,"bpmn:Lane"))t&&(t=this.getLaneSet(t)),i="lanes";else if(Ar(e,"bpmn:FlowElement")){if(t)if(Ar(t,"bpmn:Participant"))t=t.processRef;else if(Ar(t,"bpmn:Lane"))do{t=t.$parent.$parent}while(Ar(t,"bpmn:Lane"));i="flowElements"}else if(Ar(e,"bpmn:Artifact")){for(;t&&!Ar(t,"bpmn:Process")&&!Ar(t,"bpmn:SubProcess")&&!Ar(t,"bpmn:Collaboration");){if(Ar(t,"bpmn:Participant")){t=t.processRef;break}t=t.$parent}i="artifacts"}else if(Ar(e,"bpmn:MessageFlow"))i="messageFlows";else if(Ar(e,"bpmn:Participant")){i="participants";var o,a=e.processRef;a&&(o=function(e){for(;e&&!Ar(e,"bpmn:Definitions");)e=e.$parent;return e}(e.$parent||t),e.$parent&&(Et(o.get("rootElements"),a),a.$parent=null),t&&(wt(o.get("rootElements"),a),a.$parent=o))}else Ar(e,"bpmn:DataOutputAssociation")?i="dataOutputAssociations":Ar(e,"bpmn:DataInputAssociation")&&(i="dataInputAssociations");if(!i)throw new Error(r("no parent for {element} in {parent}",{element:e.id,parent:t.id}));var s;if(e.$parent&&Et(s=e.$parent.get(i),e),t?((s=t.get(i)).push(e),e.$parent=t):e.$parent=null,n){var c=n.get(i);Et(s,e),t&&(c||(c=[],t.set(i,c)),c.push(e))}}},cm.prototype.updateConnectionWaypoints=function(e){Pr(e).set("waypoint",this._bpmnFactory.createDiWaypoints(e.waypoints))},cm.prototype.updateConnection=function(e){var t,n=e.connection,i=Tr(n),r=n.source,o=Tr(r),a=n.target,s=Tr(n.target);if(Ar(i,"bpmn:DataAssociation"))Ar(i,"bpmn:DataInputAssociation")?(i.get("sourceRef")[0]=o,t=e.parent||e.newParent||s,this.updateSemanticParent(i,s,t)):Ar(i,"bpmn:DataOutputAssociation")&&(t=e.parent||e.newParent||o,this.updateSemanticParent(i,o,t),i.targetRef=s);else{var c=Ar(i,"bpmn:SequenceFlow");i.sourceRef!==o&&(c&&(Et(i.sourceRef&&i.sourceRef.get("outgoing"),i),o&&o.get("outgoing")&&o.get("outgoing").push(i)),i.sourceRef=o),i.targetRef!==s&&(c&&(Et(i.targetRef&&i.targetRef.get("incoming"),i),s&&s.get("incoming")&&s.get("incoming").push(i)),i.targetRef=s)}this.updateConnectionWaypoints(n),this.updateDiConnection(n,r,a)},cm.prototype._getLabel=function(e){return e.label||(e.label=this._bpmnFactory.createDiLabel()),e.label},e(lm,qn),lm.$inject=["bpmnFactory","moddle","translate"],lm.prototype.baseCreate=qn.prototype.create,lm.prototype.create=function(e,t){if("label"===e){var n=t.di||this._bpmnFactory.createDiLabel();return this.baseCreate(e,F({type:"label",di:n},yo,t))}return this.createBpmnElement(e,t)},lm.prototype.createBpmnElement=function(e,t){var n,i,r,o=this._translate,a=(t=F({},t||{})).businessObject,s=t.di;if(!a){if(!t.type)throw new Error(o("no shape type specified"));wr(a=this._bpmnFactory.create(t.type))}if(!Rr(s,["bpmndi:BPMNShape","bpmndi:BPMNEdge","bpmndi:BPMNDiagram","bpmndi:BPMNPlane"])){var c=F({},s||{},{id:a.id+"_di"});s="root"===e?this._bpmnFactory.createDiPlane(a,c):"connection"===e?this._bpmnFactory.createDiEdge(a,c):this._bpmnFactory.createDiShape(a,c)}return Ar(a,"bpmn:Group")&&(t=F({isFrame:!0},t)),t=function(e,t,n){return g(n,(function(n){t=um(e,t,n)})),t}(a,t,["processRef","isInterrupting","associationDirection","isForCompensation"]),t.isExpanded&&(t=um(s,t,"isExpanded")),Ar(a,"bpmn:SubProcess")&&(t.collapsed=!Wr(a,s)),Ar(a,"bpmn:ExclusiveGateway")&&(s.isMarkerVisible=!0),t.eventDefinitionType&&(i=a.get("eventDefinitions")||[],r=this._bpmnFactory.create(t.eventDefinitionType,t.eventDefinitionAttrs),"bpmn:ConditionalEventDefinition"===t.eventDefinitionType&&(r.condition=this._bpmnFactory.create("bpmn:FormalExpression")),i.push(r),r.$parent=a,a.eventDefinitions=i,delete t.eventDefinitionType),n=this.getDefaultSize(a,s),t=F({id:a.id},n,t,{businessObject:a,di:s}),this.baseCreate(e,t)},lm.prototype.getDefaultSize=function(e,t){var n=Tr(e);return t=t||Pr(e),Ar(n,"bpmn:SubProcess")?Wr(n,t)?{width:350,height:200}:{width:100,height:80}:Ar(n,"bpmn:Task")?{width:100,height:80}:Ar(n,"bpmn:Gateway")?{width:50,height:50}:Ar(n,"bpmn:Event")?{width:36,height:36}:Ar(n,"bpmn:Participant")?Wr(n,t)?{width:600,height:250}:{width:400,height:60}:Ar(n,"bpmn:Lane")?{width:400,height:100}:Ar(n,"bpmn:DataObjectReference")?{width:36,height:50}:Ar(n,"bpmn:DataStoreReference")?{width:50,height:50}:Ar(n,"bpmn:TextAnnotation")?{width:100,height:30}:Ar(n,"bpmn:Group")?{width:300,height:300}:{width:100,height:80}},lm.prototype.createParticipantShape=function(e){return l(e)||(e={isExpanded:e}),!1!==(e=F({type:"bpmn:Participant"},e||{})).isExpanded&&(e.processRef=this._bpmnFactory.create("bpmn:Process")),this.createShape(e)},hm.$inject=["modeling","canvas"],hm.prototype.preExecute=function(e){var t=this._modeling,n=e.elements,i=e.alignment;g(n,(function(e){var n={x:0,y:0};i.left?n.x=i.left-e.x:i.right?n.x=i.right-e.width-e.x:i.center?n.x=i.center-Math.round(e.width/2)-e.x:i.top?n.y=i.top-e.y:i.bottom?n.y=i.bottom-e.height-e.y:i.middle&&(n.y=i.middle-Math.round(e.height/2)-e.y),t.moveElements([e],n,e.parent)}))},hm.prototype.postExecute=function(e){},dm.$inject=["modeling"],dm.prototype.preExecute=function(e){var t=e.source;if(!t)throw new Error("source required");var n=e.target||t.parent,i=e.shape,r=e.hints||{};i=e.shape=this._modeling.createShape(i,e.position,n,{attach:r.attach}),e.shape=i},dm.prototype.postExecute=function(e){var t,n,i=e.hints||{};t=e.source,n=e.shape,E(t.outgoing,(function(e){return e.target===n}))||(i.connectionTarget===e.source?this._modeling.connect(e.shape,e.source,e.connection):this._modeling.connect(e.source,e.shape,e.connection))},fm.$inject=["canvas","layouter"],fm.prototype.execute=function(e){var t=e.connection,n=e.source,i=e.target,r=e.parent,o=e.parentIndex,a=e.hints;if(!n||!i)throw new Error("source and target required");if(!r)throw new Error("parent required");return t.source=n,t.target=i,t.waypoints||(t.waypoints=this._layouter.layoutConnection(t,a)),this._canvas.addConnection(t,r,o),t},fm.prototype.revert=function(e){var t=e.connection;return this._canvas.removeConnection(t),t.source=null,t.target=null,t};var mm=Math.round;function vm(e){this._modeling=e}function ym(e){return!!e.waypoints}vm.$inject=["modeling"],vm.prototype.preExecute=function(e){var t=e.elements,n=e.parent,i=e.parentIndex,r=e.position,o=e.hints,a=this._modeling;g(t,(function(e){u(e.x)||(e.x=0),u(e.y)||(e.y=0)}));var s=vt(y(t,(function(e){return!e.hidden})));g(t,(function(e){ym(e)&&(e.waypoints=w(e.waypoints,(function(e){return{x:mm(e.x-s.x-s.width/2+r.x),y:mm(e.y-s.y-s.height/2+r.y)}}))),F(e,{x:mm(e.x-s.x-s.width/2+r.x),y:mm(e.y-s.y-s.height/2+r.y)})}));var c=ut(t),p={};g(t,(function(e){if(ym(e))p[e.id]=u(i)?a.createConnection(p[e.source.id],p[e.target.id],i,e,e.parent||n,o):a.createConnection(p[e.source.id],p[e.target.id],e,e.parent||n,o);else{var t=F({},o);-1===c.indexOf(e)&&(t.autoResize=!1),p[e.id]=u(i)?a.createShape(e,z(e,["x","y","width","height"]),e.parent||n,i,t):a.createShape(e,z(e,["x","y","width","height"]),e.parent||n,t)}})),e.elements=A(p)};var gm=Math.round;function bm(e){this._canvas=e}function xm(e){bm.call(this,e)}bm.$inject=["canvas"],bm.prototype.execute=function(e){var t=e.shape,n=e.position,i=e.parent,r=e.parentIndex;if(!i)throw new Error("parent required");if(!n)throw new Error("position required");return void 0!==n.width?F(t,n):F(t,{x:n.x-gm(t.width/2),y:n.y-gm(t.height/2)}),this._canvas.addShape(t,i,r),t},bm.prototype.revert=function(e){var t=e.shape;return this._canvas.removeShape(t),t},e(xm,bm),xm.$inject=["canvas"];var _m=bm.prototype.execute;xm.prototype.execute=function(e){var t=e.shape;return function(e){["width","height"].forEach((function(t){void 0===e[t]&&(e[t]=0)}))}(t),t.labelTarget=e.labelTarget,_m.call(this,e)};var Em=bm.prototype.revert;function wm(e,t){this._canvas=e,this._modeling=t}function Sm(e,t){this._modeling=e,this._elementRegistry=t}function Cm(e,t){this._canvas=e,this._modeling=t}function Am(e){this._modeling=e}xm.prototype.revert=function(e){return e.shape.labelTarget=null,Em.call(this,e)},wm.$inject=["canvas","modeling"],wm.prototype.preExecute=function(e){var t=this._modeling,n=e.connection;Bf(n.incoming,(function(e){t.removeConnection(e,{nested:!0})})),Bf(n.outgoing,(function(e){t.removeConnection(e,{nested:!0})}))},wm.prototype.execute=function(e){var t=e.connection,n=t.parent;return e.parent=n,e.parentIndex=St(n.children,t),e.source=t.source,e.target=t.target,this._canvas.removeConnection(t),t.source=null,t.target=null,t},wm.prototype.revert=function(e){var t=e.connection,n=e.parent,i=e.parentIndex;return t.source=e.source,t.target=e.target,wt(n.children,t,i),this._canvas.addConnection(t,n),t},Sm.$inject=["modeling","elementRegistry"],Sm.prototype.postExecute=function(e){var t=this._modeling,n=this._elementRegistry;g(e.elements,(function(e){n.get(e.id)&&(e.waypoints?t.removeConnection(e):t.removeShape(e))}))},Cm.$inject=["canvas","modeling"],Cm.prototype.preExecute=function(e){var t=this._modeling,n=e.shape;Bf(n.incoming,(function(e){t.removeConnection(e,{nested:!0})})),Bf(n.outgoing,(function(e){t.removeConnection(e,{nested:!0})})),Bf(n.children,(function(e){e.waypoints?t.removeConnection(e,{nested:!0}):t.removeShape(e,{nested:!0})}))},Cm.prototype.execute=function(e){var t=this._canvas,n=e.shape,i=n.parent;return e.oldParent=i,e.oldParentIndex=St(i.children,n),t.removeShape(n),n},Cm.prototype.revert=function(e){var t=this._canvas,n=e.shape,i=e.oldParent,r=e.oldParentIndex;return wt(i.children,n,r),t.addShape(n,i),n},Am.$inject=["modeling"];var Rm={x:"y",y:"x"};function Tm(e,t){this._layouter=e,this._canvas=t}function Pm(){}function Dm(){this.allShapes={},this.allConnections={},this.enclosedElements={},this.enclosedConnections={},this.topLevel={}}function km(e){this._modeling=e}function Mm(e){this._helper=new km(e)}function Bm(e){this._modeling=e,this._helper=new km(e)}function Nm(e){this._modeling=e}function Om(e){return e.original||e}function jm(e,t){this._modeling=e,this._rules=t}function Lm(e){this._modeling=e}function Im(e){this._modeling=e}function Fm(e){return F({},e)}function zm(e){switch(e){case"n":case"s":return"y";case"w":case"e":return"x"}}function $m(e,t,n){var i=zm(n);return/e|s/.test(n)?e[i]>t:/n|w/.test(n)?e[i] required");var r=e.changed||this.getVisualReferences(n).concat(t),o=e.oldProperties||function(e,t){return x(t,(function(t,n){return t[n]=e.get(n),t}),{})}(n,S(i));return Jm(n,i),e.oldProperties=o,e.changed=r,r},Qm.prototype.revert=function(e){var t=e.oldProperties,n=e.moddleElement,i=e.changed;return Jm(n,t),i},Qm.prototype.getVisualReferences=function(e){var t=this._elementRegistry;return Ar(e,"bpmn:DataObject")?function(e,t){return t.filter((function(t){return Ar(t,"bpmn:DataObjectReference")&&Tr(t).dataObjectRef===e}))}(e,t):[]};var ev="default",tv="id",nv="di",iv={width:0,height:0};function rv(e,t,n,i,r){this._elementRegistry=e,this._moddle=t,this._translate=n,this._modeling=i,this._textRenderer=r}function ov(e,t){return tv in e&&e.id!==t.id}function av(e,t){var n=e.businessObject,i=Pr(e);g(t,(function(e,t){t!==nv?n.set(t,e):i&&function(e,t){g(t,(function(t,n){e.set(n,t)}))}(i,e)}))}rv.$inject=["elementRegistry","moddle","translate","modeling","textRenderer"],rv.prototype.execute=function(e){var t=e.element,n=[t],i=this._translate;if(!t)throw new Error(i("element required"));var r=this._elementRegistry,o=this._moddle.ids,a=t.businessObject,s=function(e){var t=F({},e);return sv.forEach((function(n){n in e&&(t[n]=Tr(t[n]))})),t}(e.properties),c=e.oldProperties||function(e,t){var n=S(t),i=e.businessObject,r=Pr(e);return x(n,(function(e,n){return e[n]=n!==nv?i.get(n):function(e,t){return x(t,(function(t,n){return t[n]=e&&e.get(n),t}),{})}(r,S(t.di)),e}),{})}(t,s);return ov(s,a)&&(o.unclaim(a.id),r.updateId(t,s.id),o.claim(s.id,a)),ev in s&&(s.default&&n.push(r.get(s.default.id)),a.default&&n.push(r.get(a.default.id))),av(t,s),e.oldProperties=c,e.changed=n,n},rv.prototype.postExecute=function(e){var t=e.element.label,n=t&&Tr(t).name;if(n){var i=this._textRenderer.getExternalLabelBounds(t,n);this._modeling.resizeShape(t,i,iv)}},rv.prototype.revert=function(e){var t=e.element,n=e.properties,i=e.oldProperties,r=t.businessObject,o=this._elementRegistry,a=this._moddle.ids;return av(t,i),ov(n,r)&&(a.unclaim(n.id),o.updateId(t,i.id),a.claim(i.id,r)),e.changed};var sv=["default"];function cv(e,t){this._canvas=e,this._modeling=t}function pv(e,t){this._modeling=e,this._spaceTool=t}function lv(e,t){this._modeling=e,this._translate=t}function uv(e,t){this._modeling=e,this._spaceTool=t}cv.$inject=["canvas","modeling"],cv.prototype.execute=function(e){var t=this._canvas,n=e.newRoot,i=n.businessObject,r=t.getRootElement(),o=r.businessObject,a=o.$parent,s=Pr(r);t.setRootElement(n),t.removeRootElement(r),wt(a.rootElements,i),i.$parent=a,Et(a.rootElements,o),o.$parent=null,r.di=null,s.bpmnElement=i,n.di=s,e.oldRoot=r},cv.prototype.revert=function(e){var t=this._canvas,n=e.newRoot,i=n.businessObject,r=e.oldRoot,o=r.businessObject,a=i.$parent,s=Pr(n);t.setRootElement(r),t.removeRootElement(n),Et(a.rootElements,i),i.$parent=null,wt(a.rootElements,o),o.$parent=a,n.di=null,s.bpmnElement=o,r.di=s},pv.$inject=["modeling","spaceTool"],pv.prototype.preExecute=function(e){var t=this._spaceTool,n=this._modeling,i=e.shape,r=e.location,o=jl(i),a=o===i,s=a?i:i.parent;Ol(s).length||n.createShape({type:"bpmn:Lane"},{x:i.x+Bl,y:i.y,width:i.width-Bl,height:i.height},s);var c=[];ft(o,(function(e){return c.push(e),e.label&&c.push(e.label),e===i?[]:y(e.children,(function(e){return e!==i}))}));var p="top"===r?-120:120,l="top"===r?i.y:i.y+i.height,u=l+("top"===r?10:-10),h="top"===r?"n":"s",d=t.calculateAdjustments(c,"y",p,u);t.makeSpace(d.movingShapes,d.resizingShapes,{x:0,y:p},h,u),e.newLane=n.createShape({type:"bpmn:Lane"},{x:i.x+(a?Bl:0),y:l-("top"===r?120:0),width:i.width-(a?Bl:0),height:120},s)},lv.$inject=["modeling","translate"],lv.prototype.preExecute=function(e){var t=this._modeling,n=this._translate,i=e.shape,r=e.count,o=Ol(i),a=o.length;if(a>r)throw new Error(n("more than {count} child lanes",{count:r}));var s,c,p,l,u,h=Math.round(i.height/r);for(u=0;u0||p.bottom<0?-n:n,a=s.calculateAdjustments(u,"y",o,i),s.makeSpace(a.movingShapes,a.resizingShapes,{x:0,y:n},r)),(p.left||p.right)&&(n=p.right||p.left,i=e.x+(p.right?e.width:0)+(p.right?-10:100),r=p.right?"e":"w",o=p.left>0||p.right<0?-n:n,a=s.calculateAdjustments(h,"x",o,i),s.makeSpace(a.movingShapes,a.resizingShapes,{x:n,y:0},r))};var hv="flowNodeRef",dv="lanes";function fv(e){this._elementRegistry=e}function mv(e){this._moddle=e}fv.$inject=["elementRegistry"],fv.prototype.computeUpdates=function(e,t){var n=[],i=[],r={},o=[];function a(e){-1===n.indexOf(e)&&(o.push(e),n.push(e))}function s(e){if(!e.parent)return[];var t=function(e){var t=jl(e);return r[t.id]||(r[t.id]=Nl(t)),r[t.id]}(e);return t.filter((function(t){return n=e,i=mn(t),r=n.x+n.width/2,o=n.y+n.height/2,r>i.left&&ri.top&&o: must be specified as : with start/end in { h,v,t,r,b,l }");if(Mv(n)){var i=function(e,t,n){return Cv(e,t,n)}(e,t,n),r=function(e,t,n){var i=Cv(t,e,Av(n));return{waypoints:i.waypoints.slice().reverse(),directions:Av(i.directions),turnNextDirections:i.turnNextDirections}}(e,t,n),o=function(e,t){var n=e.directions.split(":")[1],i=t.directions.split(":")[0];e.turnNextDirections&&(n="h"==n?"v":"h"),t.turnNextDirections&&(i="h"==i?"v":"h");var r=n+":"+i;return{waypoints:Rv(e.waypoints[e.waypoints.length-1],t.waypoints[0],r),directions:r}}(i,r);return[].concat(i.waypoints,o.waypoints,r.waypoints)}return function(e,t,n){var i=wv((t.x-e.x)/2+e.x),r=wv((t.y-e.y)/2+e.y);if("h:v"===n)return[{x:t.x,y:e.y}];if("v:h"===n)return[{x:e.x,y:t.y}];if("h:h"===n)return[{x:i,y:e.y},{x:i,y:t.y}];if("v:v"===n)return[{x:e.x,y:r},{x:t.x,y:r}];throw new Error("invalid directions: can only handle varians of [hv]:[hv]")}(e,t,n)}function Tv(e,t,n,i,r){var o=b(r&&r.preferredLayouts||[],"straight")[0]||"h:h",a=bn(e,t,Sv[o]||0),s=function(e,t){if(Mv(t))return t;switch(e){case"intersect":return"t:t";case"top":case"bottom":return"v:v";case"left":case"right":return"h:h";default:return t}}(a,o);n=n||gn(e),i=i||gn(t);var c=s.split(":"),p=Bv(n,e,c[0],function(e){return{top:"bottom",bottom:"top",left:"right",right:"left","top-left":"bottom-right","bottom-right":"top-left","top-right":"bottom-left","bottom-left":"top-right"}[e]}(a));return function(e,t,n){var i=Rv(e,t,n);return i.unshift(e),i.push(t),Nv(i)}(p,Bv(i,t,c[1],a),s)}function Pv(e,t,n,i,r,o){var a;return p(n)&&(r=n,o=i,n=gn(e),i=gn(t)),r=r||[],a=-1!==(o=F({preferredLayouts:[]},o)).preferredLayouts.indexOf("straight")&&function(e,t,n,i,r){var o,a,s={};if(a=bn(e,t),!/^(top|bottom|left|right)$/.test(a))return null;/top|bottom/.test(a)&&(o="x");/left|right/.test(a)&&(o="y");return"target"===r.preserveDocking?Dv(o,i,e)?(s[o]=i[o],[{x:void 0!==s.x?s.x:n.x,y:void 0!==s.y?s.y:n.y,original:{x:void 0!==s.x?s.x:n.x,y:void 0!==s.y?s.y:n.y}},{x:i.x,y:i.y}]):null:Dv(o,n,t)?(s[o]=n[o],[{x:n.x,y:n.y},{x:void 0!==s.x?s.x:i.x,y:void 0!==s.y?s.y:i.y,original:{x:void 0!==s.x?s.x:i.x,y:void 0!==s.y?s.y:i.y}}]):null}(e,t,n,i,o),a||(a=o.connectionEnd&&function(e,t,n,i){var r=i.slice().reverse();return(r=kv(e,t,n,r))?r.reverse():null}(t,e,i,r),a||((a=o.connectionStart&&kv(e,t,n,r))||(!o.connectionStart&&!o.connectionEnd&&r&&r.length?r:Tv(e,t,n,i,o))))}function Dv(e,t,n){return function(e,t,n){return e>=t&&e<=n}(t[e],n[e],n[e]+n[{x:"width",y:"height"}[e]])}function kv(e,t,n,i){if(function(e){return e.length<3||!(e.length>4)&&!!m(e,(function(t,n){var i=e[n-1];return i&&Ct(t,i)<3}))}(i))return null;var r,o=i[0],a=i.slice();return a[0]=n,a[1]=function(e,t,n){switch(Rt(t,e)){case"v":return{x:n.x,y:e.y};case"h":return{x:e.x,y:n.y}}return{x:e.x,y:e.y}}(a[1],o,n),r=function(e,t,n){var i;for(i=e.length-2;0!==i;i--)if(Tt(e[i],t,20)||Tt(e[i],n,20))return e.slice(i);return e}(a,e,t),r!==a&&(a=kv(e,t,n,r)),a&&Rt(a)?null:a}function Mv(e){return e&&/t|r|b|l/.test(e)}function Bv(e,t,n,i){if("h"===n&&(n=/left/.test(i)?"l":"r"),"v"===n&&(n=/top/.test(i)?"t":"b"),"t"===n)return{original:e,x:e.x,y:t.y};if("r"===n)return{original:e,x:t.x+t.width,y:e.y};if("b"===n)return{original:e,x:e.x,y:t.y+t.height};if("l"===n)return{original:e,x:t.x,y:e.y};throw new Error("unexpected dockingDirection: <"+n+">")}function Nv(e){return e.reduce((function(t,n,i){return At(t[t.length-1],e[i+1],n,0)||t.push(n),t}),[])}var Ov={top:"bottom","top-right":"bottom-left","top-left":"bottom-right",right:"left",bottom:"top","bottom-right":"top-left","bottom-left":"top-right",left:"right"},jv={top:"t",right:"r",bottom:"b",left:"l"};function Lv(){}function Iv(e,t){return Ar(t,"bpmn:Participant")?"source":Ar(e,"bpmn:Participant")?"target":Hv(t)?"source":Hv(e)||Ar(t,"bpmn:Event")?"target":Ar(e,"bpmn:Event")?"source":null}function Fv(e){return Hv(e)?"target":"source"}function zv(e,t){return e?e.original||e:gn(t)}function $v(e,t){return Ar(t,"bpmn:Activity")&&Ar(e,"bpmn:BoundaryEvent")&&t.businessObject.isForCompensation}function Hv(e){return Ar(e,"bpmn:SubProcess")&&Wr(e)}function Gv(e,t){return e===t}function Vv(e){var t=/right|left/.exec(e);return t&&t[0]}function Wv(e){var t=/top|bottom/.exec(e);return t&&t[0]}function Uv(e){return"right"===e||"left"===e}function qv(e,t){var n=t.waypoints,i=n&&n.length&&bn(n[0],e);return"top"===i?["t:r"]:"right"===i?["r:b"]:"left"===i?["l:t"]:["b:l"]}function Kv(e,t,n){var i,r,o,a,s=gn(e),c=gn(t),p=(a=(o=e).host,bn(gn(o),a,-10)),l=Gv(e.host,t),u=-1!==["top","right","bottom","left"].indexOf(p),h=bn(c,s,{x:e.width/2+t.width/2,y:e.height/2+t.height/2});return l?function(e,t,n,i,r){var o,a=t?e:Wv(e),s=jv[a];o=t?Uv(e)?Yv("y",n,i,r)?"h":"b":Yv("x",n,i,r)?"v":"l":"v";return[s+":"+o]}(p,u,e,t,n):(i=function(e,t,n){if(n)return jv[e];if(Gv(Wv(e),Wv(t))||(i=Vv(e),r=Vv(t),Ov[i]===r))return jv[Wv(e)];var i,r;return jv[Vv(e)]}(p,h,u),r=function(e,t,n){if(n)return Uv(e)?(i=t,r=Vv(e),o=Ov[r],-1!==i.indexOf(o)||Gv(e,t)?"h":"v"):function(e,t){var n=Wv(e),i=Ov[n];return-1!==t.indexOf(i)}(e,t)||Gv(e,t)?"v":"h";var i,r,o;return Uv(t)||Gv(Wv(e),Wv(t))&&Vv(t)?"h":"v"}(p,h,u),[i+":"+r])}function Yv(e,t,n,i){return!(Xv(e,i,n,40)||Xv(e,i,{x:n.x+n.width,y:n.y+n.height},40)||Xv(e,i,gn(t),40))}function Xv(e,t,n,i){return Math.abs(t[e]-n[e])
      '),"data-group",i),e.appendChild(r));var o=t.html||(t.separator?'
      ':'
      '),a=fe(o);if(r.appendChild(a),!t.separator&&(Y(a,"data-action",n),t.title&&Y(a,"title",t.title),t.className&&function(e,t){var n=ee(e);(p(t)?t:t.split(/\s+/g)).forEach((function(e){n.add(e)}))}(a,t.className),t.imageUrl)){var s=fe("");Y(s,"src",t.imageUrl),a.appendChild(s)}})),this.open()},dy.prototype.trigger=function(e,t,n){var i,r,o,a=this._entries,s=t.delegateTarget||t.target;if(!s)return t.preventDefault();(i=a[Y(s,"data-action")])&&(r=i.action,o=t.originalEvent||t,h(r)?"click"===e&&r(o,n):r[e]&&r[e](o,n),t.preventDefault())},dy.prototype._layoutChanged=function(){this._toggleState({})},dy.prototype._needsCollapse=function(e,t){return e<46*Object.keys(t).length+50},dy.prototype.close=function(){this._toggleState({open:!1,twoColumn:!1})},dy.prototype.open=function(){this._toggleState({open:!0})},dy.prototype.toggle=function(e){this.isOpen()?this.close():this.open()},dy.prototype.isActiveTool=function(e){return e&&this._activeTool===e},dy.prototype.updateToolHighlight=function(e){var t;this._toolsContainer||(t=ye(".djs-palette-entries",this._container),this._toolsContainer=ye("[data-group=tools]",t)),g(this._toolsContainer.children,(function(t){var n=t.getAttribute("data-action");if(n){var i=ee(t);n=n.replace("-tool",""),i.contains("entry")&&n===e?i.add("highlighted-entry"):i.remove("highlighted-entry")}}))},dy.prototype.isOpen=function(){return ee(this._container).has(uy)},dy.prototype._getParentContainer=function(){return this._canvas.getContainer()},dy.HTML_MARKUP='
      ';var my={__init__:["palette"],palette:["type",dy]},vy="crosshair";function yy(e,t,n,i,r,o,a){this._selection=r,this._dragging=n,this._mouse=a;var s=this,c=function(e){var n,i=t.getActiveLayer();Se(n=e.frame=Le("rect"),{class:"djs-lasso-overlay",width:1,height:1,x:0,y:0}),_e(i,n)},p=function(e){var t=e.frame,n=e.bbox;Se(t,{x:n.x,y:n.y,width:n.width,height:n.height})},l=function(e){e.frame&&ke(e.frame)};o.registerTool("lasso",{tool:"lasso.selection",dragging:"lasso"}),e.on("lasso.selection.end",(function(t){var n=t.originalEvent.target;(t.hover||n instanceof SVGElement)&&e.once("lasso.selection.ended",(function(){s.activateLasso(t.originalEvent,!0)}))})),e.on("lasso.end",(function(e){var t=gy(e),n=i.filter((function(e){return e}));s.select(n,t)})),e.on("lasso.start",(function(e){var t=e.context;t.bbox=gy(e),c(t)})),e.on("lasso.move",(function(e){var t=e.context;t.bbox=gy(e),p(t)})),e.on("lasso.cleanup",(function(e){var t=e.context;l(t)})),e.on("element.mousedown",1500,(function(e){if(Oo(e))return s.activateLasso(e.originalEvent),!0}))}function gy(e){var t={x:e.x-e.dx,y:e.y-e.dy},n={x:e.x,y:e.y};return t.x<=n.x&&t.y=n.x&&t.yn.x&&t.y<=n.y?{x:n.x,y:t.y,width:t.x-n.x,height:n.y-t.y}:t.x<=n.x&&t.y>n.y||t.x=n.y?{x:t.x,y:n.y,width:n.x-t.x,height:t.y-n.y}:t.x>=n.x&&t.y>n.y||t.x>n.x&&t.y>=n.y?{x:n.x,y:n.y,width:t.x-n.x,height:t.y-n.y}:{x:n.x,y:n.y,width:0,height:0}}yy.$inject=["eventBus","canvas","dragging","elementRegistry","selection","toolManager","mouse"],yy.prototype.activateLasso=function(e,t){this._dragging.init(e,"lasso",{autoActivate:t,cursor:vy,data:{context:{}}})},yy.prototype.activateSelection=function(e,t){this._dragging.init(e,"lasso.selection",{trapClick:!1,autoActivate:t,cursor:vy,data:{context:{}}})},yy.prototype.select=function(e,t){var n=function(e,t){var n={};return g(e,(function(e){var i=e;i.waypoints&&(i=vt(i)),!u(t.y)&&i.x>t.x&&(n[e.id]=e),!u(t.x)&&i.y>t.y&&(n[e.id]=e),i.x>t.x&&i.y>t.y&&(u(t.width)&&u(t.height)&&i.width+i.xt[i]+r-n&&Jc(e,i,t[i]+r-n)}))}(e,r,function(e){return Ar(e,"bpmn:Task")?10:20}(r)),r&&function(e,t){return E(t,(function(t){return Iy(e,t)}))}(n,["bpmn:Association","bpmn:DataInputAssociation","bpmn:DataOutputAssociation","bpmn:SequenceFlow"])?(t.connectionStart=Zc(i),Rr(r,["bpmn:Event","bpmn:Gateway"])&&Ly(e,Zc(r)),Rr(r,["bpmn:Task","bpmn:SubProcess"])&&function(e,t){var n=Zc(t);Ny.forEach((function(i){(function(e,t,n){return e[n]>t[n]+20&&e[n]r[t]?r[t]+By:r[t]-By,Jc(e,t,n))}))}(e)):Iy(n,"bpmn:MessageFlow")?(Ar(i,"bpmn:Event")&&(t.connectionStart=Zc(i)),Ar(r,"bpmn:Event")&&Ly(e,Zc(r))):t.connectionStart=t.initialConnectionStart)}))}function Ly(e,t){Jc(e,"x",t.x),Jc(e,"y",t.y)}function Iy(e,t){return e&&e.type===t}function Fy(e,t){return"x"===e?t.width:t.height}function zy(){this._targets={},this._snapOrigins={},this._snapLocations=[],this._defaultSnaps={}}function $y(e){this._snapValues={}}jy.$inject=["eventBus"],zy.prototype.getSnapOrigin=function(e){return this._snapOrigins[e]},zy.prototype.setSnapOrigin=function(e,t){this._snapOrigins[e]=t,-1===this._snapLocations.indexOf(e)&&this._snapLocations.push(e)},zy.prototype.addDefaultSnap=function(e,t){var n=this._defaultSnaps[e];n||(n=this._defaultSnaps[e]=[]),n.push(t)},zy.prototype.getSnapLocations=function(){return this._snapLocations},zy.prototype.setSnapLocations=function(e){this._snapLocations=e},zy.prototype.pointsForTarget=function(e){var t=e.id||e,n=this._targets[t];return n||(n=this._targets[t]=new $y).initDefaults(this._defaultSnaps),n},$y.prototype.add=function(e,t){var n=this._snapValues[e];n||(n=this._snapValues[e]={x:[],y:[]}),-1===n.x.indexOf(t.x)&&n.x.push(t.x),-1===n.y.indexOf(t.y)&&n.y.push(t.y)},$y.prototype.snap=function(e,t,n,i){var r=this._snapValues[t];return r&&function(e,t,n){var i,r;for(n=void 0===n?10:n,i=0;i=e.x||i&&i<=e.x)&&Jc(e,"x",e.x),(n&&n>=e.y||r&&r<=e.y)&&Jc(e,"y",e.y)}}function Uy(e,t){return-1!==e.indexOf(t)}function qy(e,t,n){return t?{x:e.x-n.x,y:e.y-n.y}:{x:e.x,y:e.y}}e(Vy,Hy),Vy.$inject=["eventBus","injector"],Vy.prototype.initSnap=function(e){var t=Hy.prototype.initSnap.call(this,e),n=e.shape,i=!!this._elementRegistry.get(n.id);return g(n.outgoing,(function(n){var r=n.waypoints[0];r=r.original||r,t.setSnapOrigin(n.id+"-docking",qy(r,i,e))})),g(n.incoming,(function(n){var r=n.waypoints[n.waypoints.length-1];r=r.original||r,t.setSnapOrigin(n.id+"-docking",qy(r,i,e))})),Ar(n,"bpmn:Participant")&&t.setSnapLocations(["top-left","bottom-right","mid"]),t},Vy.prototype.addSnapTargetPoints=function(e,t,n){Hy.prototype.addSnapTargetPoints.call(this,e,t,n);var i=this.getSnapTargets(t,n);g(i,(function(n){var i;(function(e){if(Ar(e,"bpmn:SubProcess")&&Wr(e))return!0;return Ar(e,"bpmn:Participant")}(n)||(i="bpmn:TextAnnotation",[t,n].every((function(e){return Ar(e,i)}))))&&(e.add("top-left",Yc(n)),e.add("bottom-right",Xc(n)))}));var r=this._elementRegistry;return g(t.incoming,(function(n){if(r.get(t.id)){Uy(i,n.source)||e.add("mid",gn(n.source));var o=n.waypoints[0];e.add(n.id+"-docking",o.original||o)}})),g(t.outgoing,(function(n){if(r.get(t.id)){Uy(i,n.target)||e.add("mid",gn(n.target));var o=n.waypoints[n.waypoints.length-1];e.add(n.id+"-docking",o.original||o)}})),Ar(n,"bpmn:SequenceFlow")&&(e=this.addSnapTargetPoints(e,t,n.parent)),e},Vy.prototype.getSnapTargets=function(e,t){return Hy.prototype.getSnapTargets.call(this,e,t).filter((function(e){return!Ar(e,"bpmn:Lane")}))};function Ky(e,t){var n=this;e.on(["resize.start"],(function(e){n.initSnap(e)})),e.on(["resize.move","resize.end"],1250,(function(e){var i=e.context,r=i.shape,o=r.parent,a=i.direction,s=i.snapContext;if(!(e.originalEvent&&Ma(e.originalEvent)||Qc(e))){var c=s.pointsForTarget(o);c.initialized||((c=n.addSnapTargetPoints(c,r,o,a)).initialized=!0),function(e){return"n"===e||"s"===e}(a)&&Jc(e,"x",e.x),function(e){return"e"===e||"w"===e}(a)&&Jc(e,"y",e.y),t.snap(e,c)}})),e.on(["resize.cleanup"],(function(){t.hide()}))}function Yy(e,t){var n=gn(e),i=mn(e),r={x:n.x,y:n.y};return-1!==t.indexOf("n")?r.y=i.top:-1!==t.indexOf("s")&&(r.y=i.bottom),-1!==t.indexOf("e")?r.x=i.right:-1!==t.indexOf("w")&&(r.x=i.left),r}Ky.prototype.initSnap=function(e){var t=e.context,n=t.shape,i=t.direction,r=t.snapContext;r||(r=t.snapContext=new zy);var o=Yy(n,i);return r.setSnapOrigin("corner",{x:o.x-e.x,y:o.y-e.y}),r},Ky.prototype.addSnapTargetPoints=function(e,t,n,i){return g(this.getSnapTargets(t,n),(function(t){e.add("corner",Xc(t)),e.add("corner",Yc(t))})),e.add("corner",Yy(t,i)),e},Ky.$inject=["eventBus","snapping"],Ky.prototype.getSnapTargets=function(e,t){return ep(t).filter((function(t){return!(n=t,i=e,n.host===i||function(e){return!!e.waypoints}(t)||function(e){return!!e.hidden}(t)||function(e){return!!e.labelTarget}(t));var n,i}))};function Xy(e){this._canvas=e,this._asyncHide=j(L(this.hide,this),1e3)}Xy.$inject=["canvas"],Xy.prototype.snap=function(e,t){var n=e.context.snapContext,i=n.getSnapLocations(),r={x:Qc(e,"x"),y:Qc(e,"y")};g(i,(function(i){var o=n.getSnapOrigin(i),a={x:e.x+o.x,y:e.y+o.y};if(g(["x","y"],(function(e){var n;r[e]||void 0!==(n=t.snap(a,i,e,7))&&(r[e]={value:n,originValue:n-o[e]})})),r.x&&r.y)return!1})),this.showSnapLine("vertical",r.x&&r.x.value),this.showSnapLine("horizontal",r.y&&r.y.value),g(["x","y"],(function(t){var n=r[t];l(n)&&Jc(e,t,n.originValue)}))},Xy.prototype._createLine=function(e){var t=this._canvas.getLayer("snap"),n=Le("path");return Se(n,{d:"M0,0 L0,0"}),Pe(n).add("djs-snap-line"),_e(t,n),{update:function(t){u(t)?Se(n,"horizontal"===e?{d:"M-100000,"+t+" L+100000,"+t,display:""}:{d:"M "+t+",-100000 L "+t+", +100000",display:""}):Se(n,{display:"none"})}}},Xy.prototype._createSnapLines=function(){this._snapLines={horizontal:this._createLine("horizontal"),vertical:this._createLine("vertical")}},Xy.prototype.showSnapLine=function(e,t){var n=this.getSnapLine(e);n&&n.update(t),this._asyncHide()},Xy.prototype.getSnapLine=function(e){return this._snapLines||this._createSnapLines(),this._snapLines[e]},Xy.prototype.hide=function(){g(this._snapLines,(function(e){e.update()}))};var Zy={__depends__:[{__init__:["createMoveSnapping","resizeSnapping","snapping"],createMoveSnapping:["type",Hy],resizeSnapping:["type",Ky],snapping:["type",Xy]}],__init__:["connectSnapping","createMoveSnapping"],connectSnapping:["type",jy],createMoveSnapping:["type",Vy]};function Qy(e,t,n,i){this._open=!1,this._results=[],this._eventMaps=[],this._canvas=e,this._eventBus=t,this._overlays=n,this._selection=i,this._container=fe(Qy.BOX_HTML),this._searchInput=ye(Qy.INPUT_SELECTOR,this._container),this._resultsContainer=ye(Qy.RESULTS_CONTAINER_SELECTOR,this._container),this._canvas.getContainer().appendChild(this._container),t.on(["canvas.destroy","diagram.destroy"],this.close,this)}function Jy(e,t,n){var i=function(e){var t="";return e.forEach((function(e){e.matched?t+=''+la(e.matched)+"":t+=la(e.normal)})),""!==t?t:null}(t),r=fe(n);r.innerHTML=i,e.appendChild(r)}Qy.$inject=["canvas","eventBus","overlays","selection"],Qy.prototype._bindEvents=function(){var e=this;function t(t,n,i,r){e._eventMaps.push({el:t,type:i,listener:de.bind(t,n,i,r)})}t(document,"html","click",(function(t){e.close()})),t(this._container,Qy.INPUT_SELECTOR,"click",(function(e){e.stopPropagation(),e.delegateTarget.focus()})),t(this._container,Qy.RESULT_SELECTOR,"mouseover",(function(t){t.stopPropagation(),e._scrollToNode(t.delegateTarget),e._preselect(t.delegateTarget)})),t(this._container,Qy.RESULT_SELECTOR,"click",(function(t){t.stopPropagation(),e._select(t.delegateTarget)})),t(this._container,Qy.INPUT_SELECTOR,"keydown",(function(e){38===e.keyCode&&e.preventDefault(),40===e.keyCode&&e.preventDefault()})),t(this._container,Qy.INPUT_SELECTOR,"keyup",(function(t){if(27===t.keyCode)return e.close();if(13===t.keyCode){var n=e._getCurrentResult();return n?e._select(n):e.close()}return 38===t.keyCode?e._scrollToDirection(!0):40===t.keyCode?e._scrollToDirection():void(37!==t.keyCode&&39!==t.keyCode&&e._search(t.delegateTarget.value))}))},Qy.prototype._unbindEvents=function(){this._eventMaps.forEach((function(e){de.unbind(e.el,e.type,e.listener)}))},Qy.prototype._search=function(e){var t=this;if(this._clearResults(),e&&""!==e){var n=this._searchProvider.find(e);if(n.length){n.forEach((function(e){var n=e.element.id,i=t._createResultNode(e,n);t._results[n]={element:e.element,node:i}}));var i=ye(Qy.RESULT_SELECTOR,this._resultsContainer);this._scrollToNode(i),this._preselect(i)}}},Qy.prototype._scrollToDirection=function(e){var t=this._getCurrentResult();if(t){var n=e?t.previousElementSibling:t.nextElementSibling;n&&(this._scrollToNode(n),this._preselect(n))}},Qy.prototype._scrollToNode=function(e){if(e&&e!==this._getCurrentResult()){var t=e.offsetTop,n=this._resultsContainer.scrollTop,i=t-this._resultsContainer.clientHeight+e.clientHeight;t0&&Jy(n,e.primaryTokens,Qy.RESULT_PRIMARY_HTML),Jy(n,e.secondaryTokens,Qy.RESULT_SECONDARY_HTML),Y(n,Qy.RESULT_ID_ATTRIBUTE,t),this._resultsContainer.appendChild(n),n},Qy.prototype.registerProvider=function(e){this._searchProvider=e},Qy.prototype.open=function(){if(!this._searchProvider)throw new Error("no search provider registered");this.isOpen()||(this._bindEvents(),this._open=!0,ee(this._container).add("open"),this._searchInput.focus(),this._eventBus.fire("searchPad.opened"))},Qy.prototype.close=function(){this.isOpen()&&(this._unbindEvents(),this._open=!1,ee(this._container).remove("open"),this._clearResults(),this._searchInput.value="",this._searchInput.blur(),this._resetOverlay(),this._eventBus.fire("searchPad.closed"))},Qy.prototype.toggle=function(){this.isOpen()?this.close():this.open()},Qy.prototype.isOpen=function(){return this._open},Qy.prototype._preselect=function(e){var t=this._getCurrentResult();if(e!==t){t&&ee(t).remove(Qy.RESULT_SELECTED_CLASS);var n=Y(e,Qy.RESULT_ID_ATTRIBUTE),i=this._results[n].element;ee(e).add(Qy.RESULT_SELECTED_CLASS),this._resetOverlay(i),this._canvas.scrollToElement(i,{top:400}),this._selection.select(i),this._eventBus.fire("searchPad.preselected",i)}},Qy.prototype._select=function(e){var t=Y(e,Qy.RESULT_ID_ATTRIBUTE),n=this._results[t].element;this.close(),this._resetOverlay(),this._canvas.scrollToElement(n,{top:400}),this._selection.select(n),this._eventBus.fire("searchPad.selected",n)},Qy.prototype._resetOverlay=function(e){if(this._overlayId&&this._overlays.remove(this._overlayId),e){var t=function(e){var t=6,n=e.width+2*t,i=e.height+2*t,r={width:n+"px",height:i+"px"},o=fe('
      ');return K(o,r),{position:{bottom:i-t,right:n-t},show:!0,html:o}}(vt(e));this._overlayId=this._overlays.add(e,t)}},Qy.CONTAINER_SELECTOR=".djs-search-container",Qy.INPUT_SELECTOR=".djs-search-input input",Qy.RESULTS_CONTAINER_SELECTOR=".djs-search-results",Qy.RESULT_SELECTOR=".djs-search-result",Qy.RESULT_SELECTED_CLASS="djs-search-result-selected",Qy.RESULT_SELECTED_SELECTOR="."+Qy.RESULT_SELECTED_CLASS,Qy.RESULT_ID_ATTRIBUTE="data-result-id",Qy.RESULT_HIGHLIGHT_CLASS="djs-search-highlight",Qy.OVERLAY_CLASS="djs-search-overlay",Qy.BOX_HTML='
      ',Qy.RESULT_HTML='
      ',Qy.RESULT_PRIMARY_HTML='
      ',Qy.RESULT_SECONDARY_HTML='

      ';var eg={__depends__:[ta,Ko],searchPad:["type",Qy]};function tg(e,t,n){this._elementRegistry=e,this._canvas=n,t.registerProvider(this)}function ng(e){return y(e,(function(e){return!!e.matched})).length>0}function ig(e,t){var n=[],i=e;if(!e)return n;e=e.toLowerCase(),t=t.toLowerCase();var r=e.indexOf(t);return r>-1?(0!==r&&n.push({normal:i.substr(0,r)}),n.push({matched:i.substr(r,t.length)}),t.length+r')})),og.prototype._interactionModules=[Ha,Xa,hs,ns],og.prototype._modelingModules=[Bs,Us,sc,Zs,rp,lp,hp,zl,Zp,Np,Kl,eu,fu,vu,gu,Au,Vu,Jv,cy,Dy,My,Lu,Zy,rg],og.prototype._modules=[].concat(Ra.prototype._modules,og.prototype._interactionModules,og.prototype._modelingModules),og})); diff --git a/dist/bpmn-navigated-viewer.development.js b/dist/bpmn-navigated-viewer.development.js new file mode 100644 index 0000000..c77fda8 --- /dev/null +++ b/dist/bpmn-navigated-viewer.development.js @@ -0,0 +1,22665 @@ +/*! + * bpmn-js - bpmn-navigated-viewer v9.4.0 + * + * Copyright (c) 2014-present, camunda Services GmbH + * + * Released under the bpmn.io license + * http://bpmn.io/license + * + * Source Code: https://github.com/bpmn-io/bpmn-js + * + * Date: 2022-08-22 + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.BpmnJS = factory()); +})(this, (function () { 'use strict'; + + function e(e,t){t&&(e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}));} + + /** + * Flatten array, one level deep. + * + * @param {Array} arr + * + * @return {Array} + */ + + var nativeToString$1 = Object.prototype.toString; + var nativeHasOwnProperty$1 = Object.prototype.hasOwnProperty; + function isUndefined$2(obj) { + return obj === undefined; + } + function isDefined(obj) { + return obj !== undefined; + } + function isArray$2(obj) { + return nativeToString$1.call(obj) === '[object Array]'; + } + function isObject(obj) { + return nativeToString$1.call(obj) === '[object Object]'; + } + function isNumber(obj) { + return nativeToString$1.call(obj) === '[object Number]'; + } + function isFunction(obj) { + var tag = nativeToString$1.call(obj); + return tag === '[object Function]' || tag === '[object AsyncFunction]' || tag === '[object GeneratorFunction]' || tag === '[object AsyncGeneratorFunction]' || tag === '[object Proxy]'; + } + function isString(obj) { + return nativeToString$1.call(obj) === '[object String]'; + } + /** + * Return true, if target owns a property with the given key. + * + * @param {Object} target + * @param {String} key + * + * @return {Boolean} + */ + + function has$1(target, key) { + return nativeHasOwnProperty$1.call(target, key); + } + + /** + * Find element in collection. + * + * @param {Array|Object} collection + * @param {Function|Object} matcher + * + * @return {Object} + */ + + function find(collection, matcher) { + matcher = toMatcher(matcher); + var match; + forEach$1(collection, function (val, key) { + if (matcher(val, key)) { + match = val; + return false; + } + }); + return match; + } + /** + * Find element index in collection. + * + * @param {Array|Object} collection + * @param {Function} matcher + * + * @return {Object} + */ + + function findIndex(collection, matcher) { + matcher = toMatcher(matcher); + var idx = isArray$2(collection) ? -1 : undefined; + forEach$1(collection, function (val, key) { + if (matcher(val, key)) { + idx = key; + return false; + } + }); + return idx; + } + /** + * Find element in collection. + * + * @param {Array|Object} collection + * @param {Function} matcher + * + * @return {Array} result + */ + + function filter(collection, matcher) { + var result = []; + forEach$1(collection, function (val, key) { + if (matcher(val, key)) { + result.push(val); + } + }); + return result; + } + /** + * Iterate over collection; returning something + * (non-undefined) will stop iteration. + * + * @param {Array|Object} collection + * @param {Function} iterator + * + * @return {Object} return result that stopped the iteration + */ + + function forEach$1(collection, iterator) { + var val, result; + + if (isUndefined$2(collection)) { + return; + } + + var convertKey = isArray$2(collection) ? toNum$1 : identity$1; + + for (var key in collection) { + if (has$1(collection, key)) { + val = collection[key]; + result = iterator(val, convertKey(key)); + + if (result === false) { + return val; + } + } + } + } + /** + * Reduce collection, returning a single result. + * + * @param {Object|Array} collection + * @param {Function} iterator + * @param {Any} result + * + * @return {Any} result returned from last iterator + */ + + function reduce(collection, iterator, result) { + forEach$1(collection, function (value, idx) { + result = iterator(result, value, idx); + }); + return result; + } + /** + * Return true if every element in the collection + * matches the criteria. + * + * @param {Object|Array} collection + * @param {Function} matcher + * + * @return {Boolean} + */ + + function every(collection, matcher) { + return !!reduce(collection, function (matches, val, key) { + return matches && matcher(val, key); + }, true); + } + /** + * Return true if some elements in the collection + * match the criteria. + * + * @param {Object|Array} collection + * @param {Function} matcher + * + * @return {Boolean} + */ + + function some(collection, matcher) { + return !!find(collection, matcher); + } + /** + * Transform a collection into another collection + * by piping each member through the given fn. + * + * @param {Object|Array} collection + * @param {Function} fn + * + * @return {Array} transformed collection + */ + + function map(collection, fn) { + var result = []; + forEach$1(collection, function (val, key) { + result.push(fn(val, key)); + }); + return result; + } + /** + * Create an object pattern matcher. + * + * @example + * + * const matcher = matchPattern({ id: 1 }); + * + * let element = find(elements, matcher); + * + * @param {Object} pattern + * + * @return {Function} matcherFn + */ + + function matchPattern(pattern) { + return function (el) { + return every(pattern, function (val, key) { + return el[key] === val; + }); + }; + } + + function toMatcher(matcher) { + return isFunction(matcher) ? matcher : function (e) { + return e === matcher; + }; + } + + function identity$1(arg) { + return arg; + } + + function toNum$1(arg) { + return Number(arg); + } + + /** + * Debounce fn, calling it only once if the given time + * elapsed between calls. + * + * Lodash-style the function exposes methods to `#clear` + * and `#flush` to control internal behavior. + * + * @param {Function} fn + * @param {Number} timeout + * + * @return {Function} debounced function + */ + function debounce(fn, timeout) { + var timer; + var lastArgs; + var lastThis; + var lastNow; + + function fire(force) { + var now = Date.now(); + var scheduledDiff = force ? 0 : lastNow + timeout - now; + + if (scheduledDiff > 0) { + return schedule(scheduledDiff); + } + + fn.apply(lastThis, lastArgs); + clear(); + } + + function schedule(timeout) { + timer = setTimeout(fire, timeout); + } + + function clear() { + if (timer) { + clearTimeout(timer); + } + + timer = lastNow = lastArgs = lastThis = undefined; + } + + function flush() { + if (timer) { + fire(true); + } + + clear(); + } + + function callback() { + lastNow = Date.now(); + + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + lastArgs = args; + lastThis = this; // ensure an execution is scheduled + + if (!timer) { + schedule(timeout); + } + } + + callback.flush = flush; + callback.cancel = clear; + return callback; + } + /** + * Bind function against target . + * + * @param {Function} fn + * @param {Object} target + * + * @return {Function} bound function + */ + + function bind(fn, target) { + return fn.bind(target); + } + + function _extends() { + _extends = Object.assign || function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; + }; + + return _extends.apply(this, arguments); + } + + /** + * Convenience wrapper for `Object.assign`. + * + * @param {Object} target + * @param {...Object} others + * + * @return {Object} the target + */ + + function assign(target) { + for (var _len = arguments.length, others = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + others[_key - 1] = arguments[_key]; + } + + return _extends.apply(void 0, [target].concat(others)); + } + /** + * Pick given properties from the target object. + * + * @param {Object} target + * @param {Array} properties + * + * @return {Object} target + */ + + function pick(target, properties) { + var result = {}; + var obj = Object(target); + forEach$1(properties, function (prop) { + if (prop in obj) { + result[prop] = target[prop]; + } + }); + return result; + } + /** + * Pick all target properties, excluding the given ones. + * + * @param {Object} target + * @param {Array} properties + * + * @return {Object} target + */ + + function omit(target, properties) { + var result = {}; + var obj = Object(target); + forEach$1(obj, function (prop, key) { + if (properties.indexOf(key) === -1) { + result[key] = prop; + } + }); + return result; + } + + var DEFAULT_RENDER_PRIORITY$1 = 1000; + + /** + * The base implementation of shape and connection renderers. + * + * @param {EventBus} eventBus + * @param {number} [renderPriority=1000] + */ + function BaseRenderer(eventBus, renderPriority) { + var self = this; + + renderPriority = renderPriority || DEFAULT_RENDER_PRIORITY$1; + + eventBus.on([ 'render.shape', 'render.connection' ], renderPriority, function(evt, context) { + var type = evt.type, + element = context.element, + visuals = context.gfx, + attrs = context.attrs; + + if (self.canRender(element)) { + if (type === 'render.shape') { + return self.drawShape(visuals, element, attrs); + } else { + return self.drawConnection(visuals, element, attrs); + } + } + }); + + eventBus.on([ 'render.getShapePath', 'render.getConnectionPath' ], renderPriority, function(evt, element) { + if (self.canRender(element)) { + if (evt.type === 'render.getShapePath') { + return self.getShapePath(element); + } else { + return self.getConnectionPath(element); + } + } + }); + } + + /** + * Should check whether *this* renderer can render + * the element/connection. + * + * @param {element} element + * + * @returns {boolean} + */ + BaseRenderer.prototype.canRender = function() {}; + + /** + * Provides the shape's snap svg element to be drawn on the `canvas`. + * + * @param {djs.Graphics} visuals + * @param {Shape} shape + * + * @returns {Snap.svg} [returns a Snap.svg paper element ] + */ + BaseRenderer.prototype.drawShape = function() {}; + + /** + * Provides the shape's snap svg element to be drawn on the `canvas`. + * + * @param {djs.Graphics} visuals + * @param {Connection} connection + * + * @returns {Snap.svg} [returns a Snap.svg paper element ] + */ + BaseRenderer.prototype.drawConnection = function() {}; + + /** + * Gets the SVG path of a shape that represents it's visual bounds. + * + * @param {Shape} shape + * + * @return {string} svg path + */ + BaseRenderer.prototype.getShapePath = function() {}; + + /** + * Gets the SVG path of a connection that represents it's visual bounds. + * + * @param {Connection} connection + * + * @return {string} svg path + */ + BaseRenderer.prototype.getConnectionPath = function() {}; + + /** + * Is an element of the given BPMN type? + * + * @param {djs.model.Base|ModdleElement} element + * @param {string} type + * + * @return {boolean} + */ + function is$1(element, type) { + var bo = getBusinessObject(element); + + return bo && (typeof bo.$instanceOf === 'function') && bo.$instanceOf(type); + } + + + /** + * Return true if element has any of the given types. + * + * @param {djs.model.Base} element + * @param {Array} types + * + * @return {boolean} + */ + function isAny(element, types) { + return some(types, function(t) { + return is$1(element, t); + }); + } + + /** + * Return the business object for a given element. + * + * @param {djs.model.Base|ModdleElement} element + * + * @return {ModdleElement} + */ + function getBusinessObject(element) { + return (element && element.businessObject) || element; + } + + /** + * Return the di object for a given element. + * + * @param {djs.model.Base} element + * + * @return {ModdleElement} + */ + function getDi(element) { + return element && element.di; + } + + function isExpanded(element, di) { + + if (is$1(element, 'bpmn:CallActivity')) { + return false; + } + + if (is$1(element, 'bpmn:SubProcess')) { + di = di || getDi(element); + + if (di && is$1(di, 'bpmndi:BPMNPlane')) { + return true; + } + + return di && !!di.isExpanded; + } + + if (is$1(element, 'bpmn:Participant')) { + return !!getBusinessObject(element).processRef; + } + + return true; + } + + function isEventSubProcess(element) { + return element && !!getBusinessObject(element).triggeredByEvent; + } + + function getLabelAttr(semantic) { + if ( + is$1(semantic, 'bpmn:FlowElement') || + is$1(semantic, 'bpmn:Participant') || + is$1(semantic, 'bpmn:Lane') || + is$1(semantic, 'bpmn:SequenceFlow') || + is$1(semantic, 'bpmn:MessageFlow') || + is$1(semantic, 'bpmn:DataInput') || + is$1(semantic, 'bpmn:DataOutput') + ) { + return 'name'; + } + + if (is$1(semantic, 'bpmn:TextAnnotation')) { + return 'text'; + } + + if (is$1(semantic, 'bpmn:Group')) { + return 'categoryValueRef'; + } + } + + function getCategoryValue(semantic) { + var categoryValueRef = semantic['categoryValueRef']; + + if (!categoryValueRef) { + return ''; + } + + + return categoryValueRef.value || ''; + } + + function getLabel(element) { + var semantic = element.businessObject, + attr = getLabelAttr(semantic); + + if (attr) { + + if (attr === 'categoryValueRef') { + + return getCategoryValue(semantic); + } + + return semantic[attr] || ''; + } + } + + function ensureImported(element, target) { + + if (element.ownerDocument !== target.ownerDocument) { + try { + // may fail on webkit + return target.ownerDocument.importNode(element, true); + } catch (e) { + // ignore + } + } + + return element; + } + + /** + * appendTo utility + */ + + /** + * Append a node to a target element and return the appended node. + * + * @param {SVGElement} element + * @param {SVGElement} target + * + * @return {SVGElement} the appended node + */ + function appendTo(element, target) { + return target.appendChild(ensureImported(element, target)); + } + + /** + * append utility + */ + + /** + * Append a node to an element + * + * @param {SVGElement} element + * @param {SVGElement} node + * + * @return {SVGElement} the element + */ + function append(target, node) { + appendTo(node, target); + return target; + } + + /** + * attribute accessor utility + */ + + var LENGTH_ATTR = 2; + + var CSS_PROPERTIES = { + 'alignment-baseline': 1, + 'baseline-shift': 1, + 'clip': 1, + 'clip-path': 1, + 'clip-rule': 1, + 'color': 1, + 'color-interpolation': 1, + 'color-interpolation-filters': 1, + 'color-profile': 1, + 'color-rendering': 1, + 'cursor': 1, + 'direction': 1, + 'display': 1, + 'dominant-baseline': 1, + 'enable-background': 1, + 'fill': 1, + 'fill-opacity': 1, + 'fill-rule': 1, + 'filter': 1, + 'flood-color': 1, + 'flood-opacity': 1, + 'font': 1, + 'font-family': 1, + 'font-size': LENGTH_ATTR, + 'font-size-adjust': 1, + 'font-stretch': 1, + 'font-style': 1, + 'font-variant': 1, + 'font-weight': 1, + 'glyph-orientation-horizontal': 1, + 'glyph-orientation-vertical': 1, + 'image-rendering': 1, + 'kerning': 1, + 'letter-spacing': 1, + 'lighting-color': 1, + 'marker': 1, + 'marker-end': 1, + 'marker-mid': 1, + 'marker-start': 1, + 'mask': 1, + 'opacity': 1, + 'overflow': 1, + 'pointer-events': 1, + 'shape-rendering': 1, + 'stop-color': 1, + 'stop-opacity': 1, + 'stroke': 1, + 'stroke-dasharray': 1, + 'stroke-dashoffset': 1, + 'stroke-linecap': 1, + 'stroke-linejoin': 1, + 'stroke-miterlimit': 1, + 'stroke-opacity': 1, + 'stroke-width': LENGTH_ATTR, + 'text-anchor': 1, + 'text-decoration': 1, + 'text-rendering': 1, + 'unicode-bidi': 1, + 'visibility': 1, + 'word-spacing': 1, + 'writing-mode': 1 + }; + + + function getAttribute(node, name) { + if (CSS_PROPERTIES[name]) { + return node.style[name]; + } else { + return node.getAttributeNS(null, name); + } + } + + function setAttribute(node, name, value) { + var hyphenated = name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); + + var type = CSS_PROPERTIES[hyphenated]; + + if (type) { + // append pixel unit, unless present + if (type === LENGTH_ATTR && typeof value === 'number') { + value = String(value) + 'px'; + } + + node.style[hyphenated] = value; + } else { + node.setAttributeNS(null, name, value); + } + } + + function setAttributes(node, attrs) { + + var names = Object.keys(attrs), i, name; + + for (i = 0, name; (name = names[i]); i++) { + setAttribute(node, name, attrs[name]); + } + } + + /** + * Gets or sets raw attributes on a node. + * + * @param {SVGElement} node + * @param {Object} [attrs] + * @param {String} [name] + * @param {String} [value] + * + * @return {String} + */ + function attr$1(node, name, value) { + if (typeof name === 'string') { + if (value !== undefined) { + setAttribute(node, name, value); + } else { + return getAttribute(node, name); + } + } else { + setAttributes(node, name); + } + + return node; + } + + /** + * Clear utility + */ + function index(arr, obj) { + if (arr.indexOf) { + return arr.indexOf(obj); + } + + + for (var i = 0; i < arr.length; ++i) { + if (arr[i] === obj) { + return i; + } + } + + return -1; + } + + var re$1 = /\s+/; + + var toString$1 = Object.prototype.toString; + + function defined(o) { + return typeof o !== 'undefined'; + } + + /** + * Wrap `el` in a `ClassList`. + * + * @param {Element} el + * @return {ClassList} + * @api public + */ + + function classes$1(el) { + return new ClassList$1(el); + } + + function ClassList$1(el) { + if (!el || !el.nodeType) { + throw new Error('A DOM element reference is required'); + } + this.el = el; + this.list = el.classList; + } + + /** + * Add class `name` if not already present. + * + * @param {String} name + * @return {ClassList} + * @api public + */ + + ClassList$1.prototype.add = function(name) { + + // classList + if (this.list) { + this.list.add(name); + return this; + } + + // fallback + var arr = this.array(); + var i = index(arr, name); + if (!~i) { + arr.push(name); + } + + if (defined(this.el.className.baseVal)) { + this.el.className.baseVal = arr.join(' '); + } else { + this.el.className = arr.join(' '); + } + + return this; + }; + + /** + * Remove class `name` when present, or + * pass a regular expression to remove + * any which match. + * + * @param {String|RegExp} name + * @return {ClassList} + * @api public + */ + + ClassList$1.prototype.remove = function(name) { + if ('[object RegExp]' === toString$1.call(name)) { + return this.removeMatching(name); + } + + // classList + if (this.list) { + this.list.remove(name); + return this; + } + + // fallback + var arr = this.array(); + var i = index(arr, name); + if (~i) { + arr.splice(i, 1); + } + this.el.className.baseVal = arr.join(' '); + return this; + }; + + /** + * Remove all classes matching `re`. + * + * @param {RegExp} re + * @return {ClassList} + * @api private + */ + + ClassList$1.prototype.removeMatching = function(re) { + var arr = this.array(); + for (var i = 0; i < arr.length; i++) { + if (re.test(arr[i])) { + this.remove(arr[i]); + } + } + return this; + }; + + /** + * Toggle class `name`, can force state via `force`. + * + * For browsers that support classList, but do not support `force` yet, + * the mistake will be detected and corrected. + * + * @param {String} name + * @param {Boolean} force + * @return {ClassList} + * @api public + */ + + ClassList$1.prototype.toggle = function(name, force) { + // classList + if (this.list) { + if (defined(force)) { + if (force !== this.list.toggle(name, force)) { + this.list.toggle(name); // toggle again to correct + } + } else { + this.list.toggle(name); + } + return this; + } + + // fallback + if (defined(force)) { + if (!force) { + this.remove(name); + } else { + this.add(name); + } + } else { + if (this.has(name)) { + this.remove(name); + } else { + this.add(name); + } + } + + return this; + }; + + /** + * Return an array of classes. + * + * @return {Array} + * @api public + */ + + ClassList$1.prototype.array = function() { + var className = this.el.getAttribute('class') || ''; + var str = className.replace(/^\s+|\s+$/g, ''); + var arr = str.split(re$1); + if ('' === arr[0]) { + arr.shift(); + } + return arr; + }; + + /** + * Check if class `name` is present. + * + * @param {String} name + * @return {ClassList} + * @api public + */ + + ClassList$1.prototype.has = + ClassList$1.prototype.contains = function(name) { + return ( + this.list ? + this.list.contains(name) : + !! ~index(this.array(), name) + ); + }; + + function remove$2(element) { + var parent = element.parentNode; + + if (parent) { + parent.removeChild(element); + } + + return element; + } + + /** + * Clear utility + */ + + /** + * Removes all children from the given element + * + * @param {DOMElement} element + * @return {DOMElement} the element (for chaining) + */ + function clear$1(element) { + var child; + + while ((child = element.firstChild)) { + remove$2(child); + } + + return element; + } + + var ns = { + svg: 'http://www.w3.org/2000/svg' + }; + + /** + * DOM parsing utility + */ + + var SVG_START = '' + svg + ''; + unwrap = true; + } + + var parsed = parseDocument(svg); + + if (!unwrap) { + return parsed; + } + + var fragment = document.createDocumentFragment(); + + var parent = parsed.firstChild; + + while (parent.firstChild) { + fragment.appendChild(parent.firstChild); + } + + return fragment; + } + + function parseDocument(svg) { + + var parser; + + // parse + parser = new DOMParser(); + parser.async = false; + + return parser.parseFromString(svg, 'text/xml'); + } + + /** + * Create utility for SVG elements + */ + + + /** + * Create a specific type from name or SVG markup. + * + * @param {String} name the name or markup of the element + * @param {Object} [attrs] attributes to set on the element + * + * @returns {SVGElement} + */ + function create$1(name, attrs) { + var element; + + if (name.charAt(0) === '<') { + element = parse$1(name).firstChild; + element = document.importNode(element, true); + } else { + element = document.createElementNS(ns.svg, name); + } + + if (attrs) { + attr$1(element, attrs); + } + + return element; + } + + /** + * Geometry helpers + */ + + // fake node used to instantiate svg geometry elements + var node = null; + + function getNode() { + if (node === null) { + node = create$1('svg'); + } + + return node; + } + + function extend$1(object, props) { + var i, k, keys = Object.keys(props); + + for (i = 0; (k = keys[i]); i++) { + object[k] = props[k]; + } + + return object; + } + + /** + * Create matrix via args. + * + * @example + * + * createMatrix({ a: 1, b: 1 }); + * createMatrix(); + * createMatrix(1, 2, 0, 0, 30, 20); + * + * @return {SVGMatrix} + */ + function createMatrix(a, b, c, d, e, f) { + var matrix = getNode().createSVGMatrix(); + + switch (arguments.length) { + case 0: + return matrix; + case 1: + return extend$1(matrix, a); + case 6: + return extend$1(matrix, { + a: a, + b: b, + c: c, + d: d, + e: e, + f: f + }); + } + } + + function createTransform(matrix) { + if (matrix) { + return getNode().createSVGTransformFromMatrix(matrix); + } else { + return getNode().createSVGTransform(); + } + } + + /** + * Serialization util + */ + + var TEXT_ENTITIES = /([&<>]{1})/g; + var ATTR_ENTITIES = /([\n\r"]{1})/g; + + var ENTITY_REPLACEMENT = { + '&': '&', + '<': '<', + '>': '>', + '"': '\'' + }; + + function escape$1(str, pattern) { + + function replaceFn(match, entity) { + return ENTITY_REPLACEMENT[entity] || entity; + } + + return str.replace(pattern, replaceFn); + } + + function serialize(node, output) { + + var i, len, attrMap, attrNode, childNodes; + + switch (node.nodeType) { + // TEXT + case 3: + // replace special XML characters + output.push(escape$1(node.textContent, TEXT_ENTITIES)); + break; + + // ELEMENT + case 1: + output.push('<', node.tagName); + + if (node.hasAttributes()) { + attrMap = node.attributes; + for (i = 0, len = attrMap.length; i < len; ++i) { + attrNode = attrMap.item(i); + output.push(' ', attrNode.name, '="', escape$1(attrNode.value, ATTR_ENTITIES), '"'); + } + } + + if (node.hasChildNodes()) { + output.push('>'); + childNodes = node.childNodes; + for (i = 0, len = childNodes.length; i < len; ++i) { + serialize(childNodes.item(i), output); + } + output.push(''); + } else { + output.push('/>'); + } + break; + + // COMMENT + case 8: + output.push(''); + break; + + // CDATA + case 4: + output.push(''); + break; + + default: + throw new Error('unable to handle node ' + node.nodeType); + } + + return output; + } + + /** + * innerHTML like functionality for SVG elements. + * based on innerSVG (https://code.google.com/p/innersvg) + */ + + + function set$1(element, svg) { + + var parsed = parse$1(svg); + + // clear element contents + clear$1(element); + + if (!svg) { + return; + } + + if (!isFragment(parsed)) { + // extract from parsed document + parsed = parsed.documentElement; + } + + var nodes = slice$1(parsed.childNodes); + + // import + append each node + for (var i = 0; i < nodes.length; i++) { + appendTo(nodes[i], element); + } + + } + + function get(element) { + var child = element.firstChild, + output = []; + + while (child) { + serialize(child, output); + child = child.nextSibling; + } + + return output.join(''); + } + + function isFragment(node) { + return node.nodeName === '#document-fragment'; + } + + function innerSVG(element, svg) { + + if (svg !== undefined) { + + try { + set$1(element, svg); + } catch (e) { + throw new Error('error parsing SVG: ' + e.message); + } + + return element; + } else { + return get(element); + } + } + + + function slice$1(arr) { + return Array.prototype.slice.call(arr); + } + + /** + * transform accessor utility + */ + + function wrapMatrix(transformList, transform) { + if (transform instanceof SVGMatrix) { + return transformList.createSVGTransformFromMatrix(transform); + } + + return transform; + } + + + function setTransforms(transformList, transforms) { + var i, t; + + transformList.clear(); + + for (i = 0; (t = transforms[i]); i++) { + transformList.appendItem(wrapMatrix(transformList, t)); + } + } + + /** + * Get or set the transforms on the given node. + * + * @param {SVGElement} node + * @param {SVGTransform|SVGMatrix|Array} [transforms] + * + * @return {SVGTransform} the consolidated transform + */ + function transform$1(node, transforms) { + var transformList = node.transform.baseVal; + + if (transforms) { + + if (!Array.isArray(transforms)) { + transforms = [ transforms ]; + } + + setTransforms(transformList, transforms); + } + + return transformList.consolidate(); + } + + function componentsToPath(elements) { + return elements.join(',').replace(/,?([A-z]),?/g, '$1'); + } + + function toSVGPoints(points) { + var result = ''; + + for (var i = 0, p; (p = points[i]); i++) { + result += p.x + ',' + p.y + ' '; + } + + return result; + } + + function createLine(points, attrs) { + + var line = create$1('polyline'); + attr$1(line, { points: toSVGPoints(points) }); + + if (attrs) { + attr$1(line, attrs); + } + + return line; + } + + function updateLine(gfx, points) { + attr$1(gfx, { points: toSVGPoints(points) }); + + return gfx; + } + + var black = 'hsl(225, 10%, 15%)'; + + // element utils ////////////////////// + + /** + * Checks if eventDefinition of the given element matches with semantic type. + * + * @return {boolean} true if element is of the given semantic type + */ + function isTypedEvent(event, eventDefinitionType, filter) { + + function matches(definition, filter) { + return every(filter, function(val, key) { + + // we want a == conversion here, to be able to catch + // undefined == false and friends + /* jshint -W116 */ + return definition[key] == val; + }); + } + + return some(event.eventDefinitions, function(definition) { + return definition.$type === eventDefinitionType && matches(event, filter); + }); + } + + function isThrowEvent(event) { + return (event.$type === 'bpmn:IntermediateThrowEvent') || (event.$type === 'bpmn:EndEvent'); + } + + function isCollection(element) { + var dataObject = element.dataObjectRef; + + return element.isCollection || (dataObject && dataObject.isCollection); + } + + function getSemantic(element) { + return element.businessObject; + } + + + // color access ////////////////////// + + function getFillColor(element, defaultColor) { + var di = getDi(element); + + return di.get('color:background-color') || di.get('bioc:fill') || defaultColor || 'white'; + } + + function getStrokeColor(element, defaultColor) { + var di = getDi(element); + + return di.get('color:border-color') || di.get('bioc:stroke') || defaultColor || black; + } + + function getLabelColor(element, defaultColor, defaultStrokeColor) { + var di = getDi(element), + label = di.get('label'); + + return label && label.get('color:color') || defaultColor || + getStrokeColor(element, defaultStrokeColor); + } + + // cropping path customizations ////////////////////// + + function getCirclePath(shape) { + + var cx = shape.x + shape.width / 2, + cy = shape.y + shape.height / 2, + radius = shape.width / 2; + + var circlePath = [ + [ 'M', cx, cy ], + [ 'm', 0, -radius ], + [ 'a', radius, radius, 0, 1, 1, 0, 2 * radius ], + [ 'a', radius, radius, 0, 1, 1, 0, -2 * radius ], + [ 'z' ] + ]; + + return componentsToPath(circlePath); + } + + function getRoundRectPath(shape, borderRadius) { + + var x = shape.x, + y = shape.y, + width = shape.width, + height = shape.height; + + var roundRectPath = [ + [ 'M', x + borderRadius, y ], + [ 'l', width - borderRadius * 2, 0 ], + [ 'a', borderRadius, borderRadius, 0, 0, 1, borderRadius, borderRadius ], + [ 'l', 0, height - borderRadius * 2 ], + [ 'a', borderRadius, borderRadius, 0, 0, 1, -borderRadius, borderRadius ], + [ 'l', borderRadius * 2 - width, 0 ], + [ 'a', borderRadius, borderRadius, 0, 0, 1, -borderRadius, -borderRadius ], + [ 'l', 0, borderRadius * 2 - height ], + [ 'a', borderRadius, borderRadius, 0, 0, 1, borderRadius, -borderRadius ], + [ 'z' ] + ]; + + return componentsToPath(roundRectPath); + } + + function getDiamondPath(shape) { + + var width = shape.width, + height = shape.height, + x = shape.x, + y = shape.y, + halfWidth = width / 2, + halfHeight = height / 2; + + var diamondPath = [ + [ 'M', x + halfWidth, y ], + [ 'l', halfWidth, halfHeight ], + [ 'l', -halfWidth, halfHeight ], + [ 'l', -halfWidth, -halfHeight ], + [ 'z' ] + ]; + + return componentsToPath(diamondPath); + } + + function getRectPath(shape) { + var x = shape.x, + y = shape.y, + width = shape.width, + height = shape.height; + + var rectPath = [ + [ 'M', x, y ], + [ 'l', width, 0 ], + [ 'l', 0, height ], + [ 'l', -width, 0 ], + [ 'z' ] + ]; + + return componentsToPath(rectPath); + } + + /** + * Flatten array, one level deep. + * + * @param {Array} arr + * + * @return {Array} + */ + + var nativeToString = Object.prototype.toString; + var nativeHasOwnProperty = Object.prototype.hasOwnProperty; + function isUndefined$1(obj) { + return obj === undefined; + } + function isArray$1(obj) { + return nativeToString.call(obj) === '[object Array]'; + } + /** + * Return true, if target owns a property with the given key. + * + * @param {Object} target + * @param {String} key + * + * @return {Boolean} + */ + + function has(target, key) { + return nativeHasOwnProperty.call(target, key); + } + /** + * Iterate over collection; returning something + * (non-undefined) will stop iteration. + * + * @param {Array|Object} collection + * @param {Function} iterator + * + * @return {Object} return result that stopped the iteration + */ + + function forEach(collection, iterator) { + var val, result; + + if (isUndefined$1(collection)) { + return; + } + + var convertKey = isArray$1(collection) ? toNum : identity; + + for (var key in collection) { + if (has(collection, key)) { + val = collection[key]; + result = iterator(val, convertKey(key)); + + if (result === false) { + return val; + } + } + } + } + + function identity(arg) { + return arg; + } + + function toNum(arg) { + return Number(arg); + } + + /** + * Assigns style attributes in a style-src compliant way. + * + * @param {Element} element + * @param {...Object} styleSources + * + * @return {Element} the element + */ + function assign$1(element) { + var target = element.style; + + for (var _len = arguments.length, styleSources = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + styleSources[_key - 1] = arguments[_key]; + } + + forEach(styleSources, function (style) { + if (!style) { + return; + } + + forEach(style, function (value, key) { + target[key] = value; + }); + }); + + return element; + } + + /** + * Set attribute `name` to `val`, or get attr `name`. + * + * @param {Element} el + * @param {String} name + * @param {String} [val] + * @api public + */ + function attr(el, name, val) { + // get + if (arguments.length == 2) { + return el.getAttribute(name); + } + + // remove + if (val === null) { + return el.removeAttribute(name); + } + + // set + el.setAttribute(name, val); + + return el; + } + + var indexOf = [].indexOf; + + var indexof = function(arr, obj){ + if (indexOf) return arr.indexOf(obj); + for (var i = 0; i < arr.length; ++i) { + if (arr[i] === obj) return i; + } + return -1; + }; + + /** + * Taken from https://github.com/component/classes + * + * Without the component bits. + */ + + /** + * Whitespace regexp. + */ + + var re = /\s+/; + + /** + * toString reference. + */ + + var toString = Object.prototype.toString; + + /** + * Wrap `el` in a `ClassList`. + * + * @param {Element} el + * @return {ClassList} + * @api public + */ + + function classes(el) { + return new ClassList(el); + } + + /** + * Initialize a new ClassList for `el`. + * + * @param {Element} el + * @api private + */ + + function ClassList(el) { + if (!el || !el.nodeType) { + throw new Error('A DOM element reference is required'); + } + this.el = el; + this.list = el.classList; + } + + /** + * Add class `name` if not already present. + * + * @param {String} name + * @return {ClassList} + * @api public + */ + + ClassList.prototype.add = function (name) { + // classList + if (this.list) { + this.list.add(name); + return this; + } + + // fallback + var arr = this.array(); + var i = indexof(arr, name); + if (!~i) arr.push(name); + this.el.className = arr.join(' '); + return this; + }; + + /** + * Remove class `name` when present, or + * pass a regular expression to remove + * any which match. + * + * @param {String|RegExp} name + * @return {ClassList} + * @api public + */ + + ClassList.prototype.remove = function (name) { + if ('[object RegExp]' == toString.call(name)) { + return this.removeMatching(name); + } + + // classList + if (this.list) { + this.list.remove(name); + return this; + } + + // fallback + var arr = this.array(); + var i = indexof(arr, name); + if (~i) arr.splice(i, 1); + this.el.className = arr.join(' '); + return this; + }; + + /** + * Remove all classes matching `re`. + * + * @param {RegExp} re + * @return {ClassList} + * @api private + */ + + ClassList.prototype.removeMatching = function (re) { + var arr = this.array(); + for (var i = 0; i < arr.length; i++) { + if (re.test(arr[i])) { + this.remove(arr[i]); + } + } + return this; + }; + + /** + * Toggle class `name`, can force state via `force`. + * + * For browsers that support classList, but do not support `force` yet, + * the mistake will be detected and corrected. + * + * @param {String} name + * @param {Boolean} force + * @return {ClassList} + * @api public + */ + + ClassList.prototype.toggle = function (name, force) { + // classList + if (this.list) { + if ('undefined' !== typeof force) { + if (force !== this.list.toggle(name, force)) { + this.list.toggle(name); // toggle again to correct + } + } else { + this.list.toggle(name); + } + return this; + } + + // fallback + if ('undefined' !== typeof force) { + if (!force) { + this.remove(name); + } else { + this.add(name); + } + } else { + if (this.has(name)) { + this.remove(name); + } else { + this.add(name); + } + } + + return this; + }; + + /** + * Return an array of classes. + * + * @return {Array} + * @api public + */ + + ClassList.prototype.array = function () { + var className = this.el.getAttribute('class') || ''; + var str = className.replace(/^\s+|\s+$/g, ''); + var arr = str.split(re); + if ('' === arr[0]) arr.shift(); + return arr; + }; + + /** + * Check if class `name` is present. + * + * @param {String} name + * @return {ClassList} + * @api public + */ + + ClassList.prototype.has = ClassList.prototype.contains = function (name) { + return this.list ? this.list.contains(name) : !!~indexof(this.array(), name); + }; + + /** + * Remove all children from the given element. + */ + function clear(el) { + + var c; + + while (el.childNodes.length) { + c = el.childNodes[0]; + el.removeChild(c); + } + + return el; + } + + var proto = typeof Element !== 'undefined' ? Element.prototype : {}; + var vendor = proto.matches + || proto.matchesSelector + || proto.webkitMatchesSelector + || proto.mozMatchesSelector + || proto.msMatchesSelector + || proto.oMatchesSelector; + + var matchesSelector = match; + + /** + * Match `el` to `selector`. + * + * @param {Element} el + * @param {String} selector + * @return {Boolean} + * @api public + */ + + function match(el, selector) { + if (!el || el.nodeType !== 1) return false; + if (vendor) return vendor.call(el, selector); + var nodes = el.parentNode.querySelectorAll(selector); + for (var i = 0; i < nodes.length; i++) { + if (nodes[i] == el) return true; + } + return false; + } + + /** + * Closest + * + * @param {Element} el + * @param {String} selector + * @param {Boolean} checkYourSelf (optional) + */ + function closest (element, selector, checkYourSelf) { + var currentElem = checkYourSelf ? element : element.parentNode; + + while (currentElem && currentElem.nodeType !== document.DOCUMENT_NODE && currentElem.nodeType !== document.DOCUMENT_FRAGMENT_NODE) { + + if (matchesSelector(currentElem, selector)) { + return currentElem; + } + + currentElem = currentElem.parentNode; + } + + return matchesSelector(currentElem, selector) ? currentElem : null; + } + + var bind$1 = window.addEventListener ? 'addEventListener' : 'attachEvent', + unbind = window.removeEventListener ? 'removeEventListener' : 'detachEvent', + prefix$6 = bind$1 !== 'addEventListener' ? 'on' : ''; + + /** + * Bind `el` event `type` to `fn`. + * + * @param {Element} el + * @param {String} type + * @param {Function} fn + * @param {Boolean} capture + * @return {Function} + * @api public + */ + + var bind_1 = function(el, type, fn, capture){ + el[bind$1](prefix$6 + type, fn, capture || false); + return fn; + }; + + /** + * Unbind `el` event `type`'s callback `fn`. + * + * @param {Element} el + * @param {String} type + * @param {Function} fn + * @param {Boolean} capture + * @return {Function} + * @api public + */ + + var unbind_1 = function(el, type, fn, capture){ + el[unbind](prefix$6 + type, fn, capture || false); + return fn; + }; + + var componentEvent = { + bind: bind_1, + unbind: unbind_1 + }; + + /** + * Module dependencies. + */ + + /** + * Delegate event `type` to `selector` + * and invoke `fn(e)`. A callback function + * is returned which may be passed to `.unbind()`. + * + * @param {Element} el + * @param {String} selector + * @param {String} type + * @param {Function} fn + * @param {Boolean} capture + * @return {Function} + * @api public + */ + + // Some events don't bubble, so we want to bind to the capture phase instead + // when delegating. + var forceCaptureEvents = ['focus', 'blur']; + + function bind$2(el, selector, type, fn, capture) { + if (forceCaptureEvents.indexOf(type) !== -1) { + capture = true; + } + + return componentEvent.bind(el, type, function (e) { + var target = e.target || e.srcElement; + e.delegateTarget = closest(target, selector, true); + if (e.delegateTarget) { + fn.call(el, e); + } + }, capture); + } + + /** + * Unbind event `type`'s callback `fn`. + * + * @param {Element} el + * @param {String} type + * @param {Function} fn + * @param {Boolean} capture + * @api public + */ + function unbind$1(el, type, fn, capture) { + if (forceCaptureEvents.indexOf(type) !== -1) { + capture = true; + } + + return componentEvent.unbind(el, type, fn, capture); + } + + var delegate = { + bind: bind$2, + unbind: unbind$1 + }; + + /** + * Expose `parse`. + */ + + var domify = parse; + + /** + * Tests for browser support. + */ + + var innerHTMLBug = false; + var bugTestDiv; + if (typeof document !== 'undefined') { + bugTestDiv = document.createElement('div'); + // Setup + bugTestDiv.innerHTML = '
      a'; + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + innerHTMLBug = !bugTestDiv.getElementsByTagName('link').length; + bugTestDiv = undefined; + } + + /** + * Wrap map from jquery. + */ + + var map$1 = { + legend: [1, '
      ', '
      '], + tr: [2, '', '
      '], + col: [2, '', '
      '], + // for script/link/style tags to work in IE6-8, you have to wrap + // in a div with a non-whitespace character in front, ha! + _default: innerHTMLBug ? [1, 'X
      ', '
      '] : [0, '', ''] + }; + + map$1.td = + map$1.th = [3, '', '
      ']; + + map$1.option = + map$1.optgroup = [1, '']; + + map$1.thead = + map$1.tbody = + map$1.colgroup = + map$1.caption = + map$1.tfoot = [1, '', '
      ']; + + map$1.polyline = + map$1.ellipse = + map$1.polygon = + map$1.circle = + map$1.text = + map$1.line = + map$1.path = + map$1.rect = + map$1.g = [1, '','']; + + /** + * Parse `html` and return a DOM Node instance, which could be a TextNode, + * HTML DOM Node of some kind (
      for example), or a DocumentFragment + * instance, depending on the contents of the `html` string. + * + * @param {String} html - HTML string to "domify" + * @param {Document} doc - The `document` instance to create the Node for + * @return {DOMNode} the TextNode, DOM Node, or DocumentFragment instance + * @api private + */ + + function parse(html, doc) { + if ('string' != typeof html) throw new TypeError('String expected'); + + // default to the global `document` object + if (!doc) doc = document; + + // tag name + var m = /<([\w:]+)/.exec(html); + if (!m) return doc.createTextNode(html); + + html = html.replace(/^\s+|\s+$/g, ''); // Remove leading/trailing whitespace + + var tag = m[1]; + + // body support + if (tag == 'body') { + var el = doc.createElement('html'); + el.innerHTML = html; + return el.removeChild(el.lastChild); + } + + // wrap map + var wrap = map$1[tag] || map$1._default; + var depth = wrap[0]; + var prefix = wrap[1]; + var suffix = wrap[2]; + var el = doc.createElement('div'); + el.innerHTML = prefix + html + suffix; + while (depth--) el = el.lastChild; + + // one element + if (el.firstChild == el.lastChild) { + return el.removeChild(el.firstChild); + } + + // several elements + var fragment = doc.createDocumentFragment(); + while (el.firstChild) { + fragment.appendChild(el.removeChild(el.firstChild)); + } + + return fragment; + } + + function query(selector, el) { + el = el || document; + + return el.querySelector(selector); + } + + function all(selector, el) { + el = el || document; + + return el.querySelectorAll(selector); + } + + function remove$1(el) { + el.parentNode && el.parentNode.removeChild(el); + } + + /** + * @param {} element + * @param {number} x + * @param {number} y + * @param {number} angle + * @param {number} amount + */ + function transform(gfx, x, y, angle, amount) { + var translate = createTransform(); + translate.setTranslate(x, y); + + var rotate = createTransform(); + rotate.setRotate(angle || 0, 0, 0); + + var scale = createTransform(); + scale.setScale(amount || 1, amount || 1); + + transform$1(gfx, [ translate, rotate, scale ]); + } + + + /** + * @param {SVGElement} element + * @param {number} x + * @param {number} y + */ + function translate$1(gfx, x, y) { + var translate = createTransform(); + translate.setTranslate(x, y); + + transform$1(gfx, translate); + } + + + /** + * @param {SVGElement} element + * @param {number} angle + */ + function rotate(gfx, angle) { + var rotate = createTransform(); + rotate.setRotate(angle, 0, 0); + + transform$1(gfx, rotate); + } + + function createCommonjsModule(fn, module) { + return module = { exports: {} }, fn(module, module.exports), module.exports; + } + + var hat_1 = createCommonjsModule(function (module) { + var hat = module.exports = function (bits, base) { + if (!base) base = 16; + if (bits === undefined) bits = 128; + if (bits <= 0) return '0'; + + var digits = Math.log(Math.pow(2, bits)) / Math.log(base); + for (var i = 2; digits === Infinity; i *= 2) { + digits = Math.log(Math.pow(2, bits / i)) / Math.log(base) * i; + } + + var rem = digits - Math.floor(digits); + + var res = ''; + + for (var i = 0; i < Math.floor(digits); i++) { + var x = Math.floor(Math.random() * base).toString(base); + res = x + res; + } + + if (rem) { + var b = Math.pow(base, rem); + var x = Math.floor(Math.random() * b).toString(base); + res = x + res; + } + + var parsed = parseInt(res, base); + if (parsed !== Infinity && parsed >= Math.pow(2, bits)) { + return hat(bits, base) + } + else return res; + }; + + hat.rack = function (bits, base, expandBy) { + var fn = function (data) { + var iters = 0; + do { + if (iters ++ > 10) { + if (expandBy) bits += expandBy; + else throw new Error('too many ID collisions, use more bits') + } + + var id = hat(bits, base); + } while (Object.hasOwnProperty.call(hats, id)); + + hats[id] = data; + return id; + }; + var hats = fn.hats = {}; + + fn.get = function (id) { + return fn.hats[id]; + }; + + fn.set = function (id, value) { + fn.hats[id] = value; + return fn; + }; + + fn.bits = bits || 128; + fn.base = base || 16; + return fn; + }; + }); + + /** + * Create a new id generator / cache instance. + * + * You may optionally provide a seed that is used internally. + * + * @param {Seed} seed + */ + + function Ids(seed) { + if (!(this instanceof Ids)) { + return new Ids(seed); + } + + seed = seed || [128, 36, 1]; + this._seed = seed.length ? hat_1.rack(seed[0], seed[1], seed[2]) : seed; + } + /** + * Generate a next id. + * + * @param {Object} [element] element to bind the id to + * + * @return {String} id + */ + + Ids.prototype.next = function (element) { + return this._seed(element || true); + }; + /** + * Generate a next id with a given prefix. + * + * @param {Object} [element] element to bind the id to + * + * @return {String} id + */ + + + Ids.prototype.nextPrefixed = function (prefix, element) { + var id; + + do { + id = prefix + this.next(true); + } while (this.assigned(id)); // claim {prefix}{random} + + + this.claim(id, element); // return + + return id; + }; + /** + * Manually claim an existing id. + * + * @param {String} id + * @param {String} [element] element the id is claimed by + */ + + + Ids.prototype.claim = function (id, element) { + this._seed.set(id, element || true); + }; + /** + * Returns true if the given id has already been assigned. + * + * @param {String} id + * @return {Boolean} + */ + + + Ids.prototype.assigned = function (id) { + return this._seed.get(id) || false; + }; + /** + * Unclaim an id. + * + * @param {String} id the id to unclaim + */ + + + Ids.prototype.unclaim = function (id) { + delete this._seed.hats[id]; + }; + /** + * Clear all claimed ids. + */ + + + Ids.prototype.clear = function () { + var hats = this._seed.hats, + id; + + for (id in hats) { + this.unclaim(id); + } + }; + + var RENDERER_IDS = new Ids(); + + var TASK_BORDER_RADIUS = 10; + var INNER_OUTER_DIST = 3; + + var DEFAULT_FILL_OPACITY = .95, + HIGH_FILL_OPACITY = .35; + + var ELEMENT_LABEL_DISTANCE = 10; + + function BpmnRenderer( + config, eventBus, styles, pathMap, + canvas, textRenderer, priority) { + + BaseRenderer.call(this, eventBus, priority); + + var defaultFillColor = config && config.defaultFillColor, + defaultStrokeColor = config && config.defaultStrokeColor, + defaultLabelColor = config && config.defaultLabelColor; + + var rendererId = RENDERER_IDS.next(); + + var markers = {}; + + var computeStyle = styles.computeStyle; + + function addMarker(id, options) { + var attrs = assign({ + fill: black, + strokeWidth: 1, + strokeLinecap: 'round', + strokeDasharray: 'none' + }, options.attrs); + + var ref = options.ref || { x: 0, y: 0 }; + + var scale = options.scale || 1; + + // fix for safari / chrome / firefox bug not correctly + // resetting stroke dash array + if (attrs.strokeDasharray === 'none') { + attrs.strokeDasharray = [ 10000, 1 ]; + } + + var marker = create$1('marker'); + + attr$1(options.element, attrs); + + append(marker, options.element); + + attr$1(marker, { + id: id, + viewBox: '0 0 20 20', + refX: ref.x, + refY: ref.y, + markerWidth: 20 * scale, + markerHeight: 20 * scale, + orient: 'auto' + }); + + var defs = query('defs', canvas._svg); + + if (!defs) { + defs = create$1('defs'); + + append(canvas._svg, defs); + } + + append(defs, marker); + + markers[id] = marker; + } + + function colorEscape(str) { + + // only allow characters and numbers + return str.replace(/[^0-9a-zA-z]+/g, '_'); + } + + function marker(type, fill, stroke) { + var id = type + '-' + colorEscape(fill) + '-' + colorEscape(stroke) + '-' + rendererId; + + if (!markers[id]) { + createMarker(id, type, fill, stroke); + } + + return 'url(#' + id + ')'; + } + + function createMarker(id, type, fill, stroke) { + + if (type === 'sequenceflow-end') { + var sequenceflowEnd = create$1('path'); + attr$1(sequenceflowEnd, { d: 'M 1 5 L 11 10 L 1 15 Z' }); + + addMarker(id, { + element: sequenceflowEnd, + ref: { x: 11, y: 10 }, + scale: 0.5, + attrs: { + fill: stroke, + stroke: stroke + } + }); + } + + if (type === 'messageflow-start') { + var messageflowStart = create$1('circle'); + attr$1(messageflowStart, { cx: 6, cy: 6, r: 3.5 }); + + addMarker(id, { + element: messageflowStart, + attrs: { + fill: fill, + stroke: stroke + }, + ref: { x: 6, y: 6 } + }); + } + + if (type === 'messageflow-end') { + var messageflowEnd = create$1('path'); + attr$1(messageflowEnd, { d: 'm 1 5 l 0 -3 l 7 3 l -7 3 z' }); + + addMarker(id, { + element: messageflowEnd, + attrs: { + fill: fill, + stroke: stroke, + strokeLinecap: 'butt' + }, + ref: { x: 8.5, y: 5 } + }); + } + + if (type === 'association-start') { + var associationStart = create$1('path'); + attr$1(associationStart, { d: 'M 11 5 L 1 10 L 11 15' }); + + addMarker(id, { + element: associationStart, + attrs: { + fill: 'none', + stroke: stroke, + strokeWidth: 1.5 + }, + ref: { x: 1, y: 10 }, + scale: 0.5 + }); + } + + if (type === 'association-end') { + var associationEnd = create$1('path'); + attr$1(associationEnd, { d: 'M 1 5 L 11 10 L 1 15' }); + + addMarker(id, { + element: associationEnd, + attrs: { + fill: 'none', + stroke: stroke, + strokeWidth: 1.5 + }, + ref: { x: 12, y: 10 }, + scale: 0.5 + }); + } + + if (type === 'conditional-flow-marker') { + var conditionalflowMarker = create$1('path'); + attr$1(conditionalflowMarker, { d: 'M 0 10 L 8 6 L 16 10 L 8 14 Z' }); + + addMarker(id, { + element: conditionalflowMarker, + attrs: { + fill: fill, + stroke: stroke + }, + ref: { x: -1, y: 10 }, + scale: 0.5 + }); + } + + if (type === 'conditional-default-flow-marker') { + var conditionaldefaultflowMarker = create$1('path'); + attr$1(conditionaldefaultflowMarker, { d: 'M 6 4 L 10 16' }); + + addMarker(id, { + element: conditionaldefaultflowMarker, + attrs: { + stroke: stroke + }, + ref: { x: 0, y: 10 }, + scale: 0.5 + }); + } + } + + function drawCircle(parentGfx, width, height, offset, attrs) { + + if (isObject(offset)) { + attrs = offset; + offset = 0; + } + + offset = offset || 0; + + attrs = computeStyle(attrs, { + stroke: black, + strokeWidth: 2, + fill: 'white' + }); + + if (attrs.fill === 'none') { + delete attrs.fillOpacity; + } + + var cx = width / 2, + cy = height / 2; + + var circle = create$1('circle'); + attr$1(circle, { + cx: cx, + cy: cy, + r: Math.round((width + height) / 4 - offset) + }); + attr$1(circle, attrs); + + append(parentGfx, circle); + + return circle; + } + + function drawRect(parentGfx, width, height, r, offset, attrs) { + + if (isObject(offset)) { + attrs = offset; + offset = 0; + } + + offset = offset || 0; + + attrs = computeStyle(attrs, { + stroke: black, + strokeWidth: 2, + fill: 'white' + }); + + var rect = create$1('rect'); + attr$1(rect, { + x: offset, + y: offset, + width: width - offset * 2, + height: height - offset * 2, + rx: r, + ry: r + }); + attr$1(rect, attrs); + + append(parentGfx, rect); + + return rect; + } + + function drawDiamond(parentGfx, width, height, attrs) { + + var x_2 = width / 2; + var y_2 = height / 2; + + var points = [ { x: x_2, y: 0 }, { x: width, y: y_2 }, { x: x_2, y: height }, { x: 0, y: y_2 } ]; + + var pointsString = points.map(function(point) { + return point.x + ',' + point.y; + }).join(' '); + + attrs = computeStyle(attrs, { + stroke: black, + strokeWidth: 2, + fill: 'white' + }); + + var polygon = create$1('polygon'); + attr$1(polygon, { + points: pointsString + }); + attr$1(polygon, attrs); + + append(parentGfx, polygon); + + return polygon; + } + + function drawLine(parentGfx, waypoints, attrs) { + attrs = computeStyle(attrs, [ 'no-fill' ], { + stroke: black, + strokeWidth: 2, + fill: 'none' + }); + + var line = createLine(waypoints, attrs); + + append(parentGfx, line); + + return line; + } + + function drawPath(parentGfx, d, attrs) { + + attrs = computeStyle(attrs, [ 'no-fill' ], { + strokeWidth: 2, + stroke: black + }); + + var path = create$1('path'); + attr$1(path, { d: d }); + attr$1(path, attrs); + + append(parentGfx, path); + + return path; + } + + function drawMarker(type, parentGfx, path, attrs) { + return drawPath(parentGfx, path, assign({ 'data-marker': type }, attrs)); + } + + function renderer(type) { + return handlers[type]; + } + + function as(type) { + return function(parentGfx, element) { + return renderer(type)(parentGfx, element); + }; + } + + function renderEventContent(element, parentGfx) { + + var event = getSemantic(element); + var isThrowing = isThrowEvent(event); + + if (event.eventDefinitions && event.eventDefinitions.length > 1) { + if (event.parallelMultiple) { + return renderer('bpmn:ParallelMultipleEventDefinition')(parentGfx, element, isThrowing); + } + else { + return renderer('bpmn:MultipleEventDefinition')(parentGfx, element, isThrowing); + } + } + + if (isTypedEvent(event, 'bpmn:MessageEventDefinition')) { + return renderer('bpmn:MessageEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:TimerEventDefinition')) { + return renderer('bpmn:TimerEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:ConditionalEventDefinition')) { + return renderer('bpmn:ConditionalEventDefinition')(parentGfx, element); + } + + if (isTypedEvent(event, 'bpmn:SignalEventDefinition')) { + return renderer('bpmn:SignalEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:EscalationEventDefinition')) { + return renderer('bpmn:EscalationEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:LinkEventDefinition')) { + return renderer('bpmn:LinkEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:ErrorEventDefinition')) { + return renderer('bpmn:ErrorEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:CancelEventDefinition')) { + return renderer('bpmn:CancelEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:CompensateEventDefinition')) { + return renderer('bpmn:CompensateEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:TerminateEventDefinition')) { + return renderer('bpmn:TerminateEventDefinition')(parentGfx, element, isThrowing); + } + + return null; + } + + function renderLabel(parentGfx, label, options) { + + options = assign({ + size: { + width: 100 + } + }, options); + + var text = textRenderer.createText(label || '', options); + + classes$1(text).add('djs-label'); + + append(parentGfx, text); + + return text; + } + + function renderEmbeddedLabel(parentGfx, element, align) { + var semantic = getSemantic(element); + + return renderLabel(parentGfx, semantic.name, { + box: element, + align: align, + padding: 5, + style: { + fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor) + } + }); + } + + function renderExternalLabel(parentGfx, element) { + + var box = { + width: 90, + height: 30, + x: element.width / 2 + element.x, + y: element.height / 2 + element.y + }; + + return renderLabel(parentGfx, getLabel(element), { + box: box, + fitBox: true, + style: assign( + {}, + textRenderer.getExternalStyle(), + { + fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor) + } + ) + }); + } + + function renderLaneLabel(parentGfx, text, element) { + var textBox = renderLabel(parentGfx, text, { + box: { + height: 30, + width: element.height + }, + align: 'center-middle', + style: { + fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor) + } + }); + + var top = -1 * element.height; + + transform(textBox, 0, -top, 270); + } + + function createPathFromConnection(connection) { + var waypoints = connection.waypoints; + + var pathData = 'm ' + waypoints[0].x + ',' + waypoints[0].y; + for (var i = 1; i < waypoints.length; i++) { + pathData += 'L' + waypoints[i].x + ',' + waypoints[i].y + ' '; + } + return pathData; + } + + var handlers = this.handlers = { + 'bpmn:Event': function(parentGfx, element, attrs) { + + if (!('fillOpacity' in attrs)) { + attrs.fillOpacity = DEFAULT_FILL_OPACITY; + } + + return drawCircle(parentGfx, element.width, element.height, attrs); + }, + 'bpmn:StartEvent': function(parentGfx, element) { + var attrs = { + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + var semantic = getSemantic(element); + + if (!semantic.isInterrupting) { + attrs = { + strokeDasharray: '6', + strokeLinecap: 'round', + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }; + } + + var circle = renderer('bpmn:Event')(parentGfx, element, attrs); + + renderEventContent(element, parentGfx); + + return circle; + }, + 'bpmn:MessageEventDefinition': function(parentGfx, element, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_MESSAGE', { + xScaleFactor: 0.9, + yScaleFactor: 0.9, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.235, + my: 0.315 + } + }); + + var fill = isThrowing ? getStrokeColor(element, defaultStrokeColor) : getFillColor(element, defaultFillColor); + var stroke = isThrowing ? getFillColor(element, defaultFillColor) : getStrokeColor(element, defaultStrokeColor); + + var messagePath = drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: stroke + }); + + return messagePath; + }, + 'bpmn:TimerEventDefinition': function(parentGfx, element) { + var circle = drawCircle(parentGfx, element.width, element.height, 0.2 * element.height, { + strokeWidth: 2, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var pathData = pathMap.getScaledPath('EVENT_TIMER_WH', { + xScaleFactor: 0.75, + yScaleFactor: 0.75, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.5, + my: 0.5 + } + }); + + drawPath(parentGfx, pathData, { + strokeWidth: 2, + strokeLinecap: 'square', + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + for (var i = 0;i < 12; i++) { + + var linePathData = pathMap.getScaledPath('EVENT_TIMER_LINE', { + xScaleFactor: 0.75, + yScaleFactor: 0.75, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.5, + my: 0.5 + } + }); + + var width = element.width / 2; + var height = element.height / 2; + + drawPath(parentGfx, linePathData, { + strokeWidth: 1, + strokeLinecap: 'square', + transform: 'rotate(' + (i * 30) + ',' + height + ',' + width + ')', + stroke: getStrokeColor(element, defaultStrokeColor) + }); + } + + return circle; + }, + 'bpmn:EscalationEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_ESCALATION', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.5, + my: 0.2 + } + }); + + var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor(event, defaultStrokeColor) + }); + }, + 'bpmn:ConditionalEventDefinition': function(parentGfx, event) { + var pathData = pathMap.getScaledPath('EVENT_CONDITIONAL', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.5, + my: 0.222 + } + }); + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + stroke: getStrokeColor(event, defaultStrokeColor) + }); + }, + 'bpmn:LinkEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_LINK', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.57, + my: 0.263 + } + }); + + var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor(event, defaultStrokeColor) + }); + }, + 'bpmn:ErrorEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_ERROR', { + xScaleFactor: 1.1, + yScaleFactor: 1.1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.2, + my: 0.722 + } + }); + + var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor(event, defaultStrokeColor) + }); + }, + 'bpmn:CancelEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_CANCEL_45', { + xScaleFactor: 1.0, + yScaleFactor: 1.0, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.638, + my: -0.055 + } + }); + + var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none'; + + var path = drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor(event, defaultStrokeColor) + }); + + rotate(path, 45); + + return path; + }, + 'bpmn:CompensateEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_COMPENSATION', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.22, + my: 0.5 + } + }); + + var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor(event, defaultStrokeColor) + }); + }, + 'bpmn:SignalEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_SIGNAL', { + xScaleFactor: 0.9, + yScaleFactor: 0.9, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.5, + my: 0.2 + } + }); + + var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor(event, defaultStrokeColor) + }); + }, + 'bpmn:MultipleEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_MULTIPLE', { + xScaleFactor: 1.1, + yScaleFactor: 1.1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.222, + my: 0.36 + } + }); + + var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill + }); + }, + 'bpmn:ParallelMultipleEventDefinition': function(parentGfx, event) { + var pathData = pathMap.getScaledPath('EVENT_PARALLEL_MULTIPLE', { + xScaleFactor: 1.2, + yScaleFactor: 1.2, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.458, + my: 0.194 + } + }); + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getStrokeColor(event, defaultStrokeColor), + stroke: getStrokeColor(event, defaultStrokeColor) + }); + }, + 'bpmn:EndEvent': function(parentGfx, element) { + var circle = renderer('bpmn:Event')(parentGfx, element, { + strokeWidth: 4, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + renderEventContent(element, parentGfx); + + return circle; + }, + 'bpmn:TerminateEventDefinition': function(parentGfx, element) { + var circle = drawCircle(parentGfx, element.width, element.height, 8, { + strokeWidth: 4, + fill: getStrokeColor(element, defaultStrokeColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return circle; + }, + 'bpmn:IntermediateEvent': function(parentGfx, element) { + var outer = renderer('bpmn:Event')(parentGfx, element, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + /* inner */ + drawCircle(parentGfx, element.width, element.height, INNER_OUTER_DIST, { + strokeWidth: 1, + fill: getFillColor(element, 'none'), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + renderEventContent(element, parentGfx); + + return outer; + }, + 'bpmn:IntermediateCatchEvent': as('bpmn:IntermediateEvent'), + 'bpmn:IntermediateThrowEvent': as('bpmn:IntermediateEvent'), + + 'bpmn:Activity': function(parentGfx, element, attrs) { + + attrs = attrs || {}; + + if (!('fillOpacity' in attrs)) { + attrs.fillOpacity = DEFAULT_FILL_OPACITY; + } + + return drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS, attrs); + }, + + 'bpmn:Task': function(parentGfx, element) { + var attrs = { + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + var rect = renderer('bpmn:Activity')(parentGfx, element, attrs); + + renderEmbeddedLabel(parentGfx, element, 'center-middle'); + attachTaskMarkers(parentGfx, element); + + return rect; + }, + 'bpmn:ServiceTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var pathDataBG = pathMap.getScaledPath('TASK_TYPE_SERVICE', { + abspos: { + x: 12, + y: 18 + } + }); + + /* service bg */ drawPath(parentGfx, pathDataBG, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var fillPathData = pathMap.getScaledPath('TASK_TYPE_SERVICE_FILL', { + abspos: { + x: 17.2, + y: 18 + } + }); + + /* service fill */ drawPath(parentGfx, fillPathData, { + strokeWidth: 0, + fill: getFillColor(element, defaultFillColor) + }); + + var pathData = pathMap.getScaledPath('TASK_TYPE_SERVICE', { + abspos: { + x: 17, + y: 22 + } + }); + + /* service */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:UserTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var x = 15; + var y = 12; + + var pathData = pathMap.getScaledPath('TASK_TYPE_USER_1', { + abspos: { + x: x, + y: y + } + }); + + /* user path */ drawPath(parentGfx, pathData, { + strokeWidth: 0.5, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var pathData2 = pathMap.getScaledPath('TASK_TYPE_USER_2', { + abspos: { + x: x, + y: y + } + }); + + /* user2 path */ drawPath(parentGfx, pathData2, { + strokeWidth: 0.5, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var pathData3 = pathMap.getScaledPath('TASK_TYPE_USER_3', { + abspos: { + x: x, + y: y + } + }); + + /* user3 path */ drawPath(parentGfx, pathData3, { + strokeWidth: 0.5, + fill: getStrokeColor(element, defaultStrokeColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:ManualTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var pathData = pathMap.getScaledPath('TASK_TYPE_MANUAL', { + abspos: { + x: 17, + y: 15 + } + }); + + /* manual path */ drawPath(parentGfx, pathData, { + strokeWidth: 0.5, // 0.25, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:SendTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var pathData = pathMap.getScaledPath('TASK_TYPE_SEND', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: 21, + containerHeight: 14, + position: { + mx: 0.285, + my: 0.357 + } + }); + + /* send path */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getStrokeColor(element, defaultStrokeColor), + stroke: getFillColor(element, defaultFillColor) + }); + + return task; + }, + 'bpmn:ReceiveTask' : function(parentGfx, element) { + var semantic = getSemantic(element); + + var task = renderer('bpmn:Task')(parentGfx, element); + var pathData; + + if (semantic.instantiate) { + drawCircle(parentGfx, 28, 28, 20 * 0.22, { strokeWidth: 1 }); + + pathData = pathMap.getScaledPath('TASK_TYPE_INSTANTIATING_SEND', { + abspos: { + x: 7.77, + y: 9.52 + } + }); + } else { + + pathData = pathMap.getScaledPath('TASK_TYPE_SEND', { + xScaleFactor: 0.9, + yScaleFactor: 0.9, + containerWidth: 21, + containerHeight: 14, + position: { + mx: 0.3, + my: 0.4 + } + }); + } + + /* receive path */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:ScriptTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var pathData = pathMap.getScaledPath('TASK_TYPE_SCRIPT', { + abspos: { + x: 15, + y: 20 + } + }); + + /* script path */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:BusinessRuleTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var headerPathData = pathMap.getScaledPath('TASK_TYPE_BUSINESS_RULE_HEADER', { + abspos: { + x: 8, + y: 8 + } + }); + + var businessHeaderPath = drawPath(parentGfx, headerPathData); + attr$1(businessHeaderPath, { + strokeWidth: 1, + fill: getFillColor(element, '#aaaaaa'), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var headerData = pathMap.getScaledPath('TASK_TYPE_BUSINESS_RULE_MAIN', { + abspos: { + x: 8, + y: 8 + } + }); + + var businessPath = drawPath(parentGfx, headerData); + attr$1(businessPath, { + strokeWidth: 1, + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:SubProcess': function(parentGfx, element, attrs) { + attrs = assign({ + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }, attrs); + + var rect = renderer('bpmn:Activity')(parentGfx, element, attrs); + + var expanded = isExpanded(element); + + if (isEventSubProcess(element)) { + attr$1(rect, { + strokeDasharray: '1,2' + }); + } + + renderEmbeddedLabel(parentGfx, element, expanded ? 'center-top' : 'center-middle'); + + if (expanded) { + attachTaskMarkers(parentGfx, element); + } else { + attachTaskMarkers(parentGfx, element, [ 'SubProcessMarker' ]); + } + + return rect; + }, + 'bpmn:AdHocSubProcess': function(parentGfx, element) { + return renderer('bpmn:SubProcess')(parentGfx, element); + }, + 'bpmn:Transaction': function(parentGfx, element) { + var outer = renderer('bpmn:SubProcess')(parentGfx, element); + + var innerAttrs = styles.style([ 'no-fill', 'no-events' ], { + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + /* inner path */ drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS - 2, INNER_OUTER_DIST, innerAttrs); + + return outer; + }, + 'bpmn:CallActivity': function(parentGfx, element) { + return renderer('bpmn:SubProcess')(parentGfx, element, { + strokeWidth: 5 + }); + }, + 'bpmn:Participant': function(parentGfx, element) { + + var attrs = { + fillOpacity: DEFAULT_FILL_OPACITY, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + var lane = renderer('bpmn:Lane')(parentGfx, element, attrs); + + var expandedPool = isExpanded(element); + + if (expandedPool) { + drawLine(parentGfx, [ + { x: 30, y: 0 }, + { x: 30, y: element.height } + ], { + stroke: getStrokeColor(element, defaultStrokeColor) + }); + var text = getSemantic(element).name; + renderLaneLabel(parentGfx, text, element); + } else { + + // Collapsed pool draw text inline + var text2 = getSemantic(element).name; + renderLabel(parentGfx, text2, { + box: element, align: 'center-middle', + style: { + fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor) + } + }); + } + + var participantMultiplicity = !!(getSemantic(element).participantMultiplicity); + + if (participantMultiplicity) { + renderer('ParticipantMultiplicityMarker')(parentGfx, element); + } + + return lane; + }, + 'bpmn:Lane': function(parentGfx, element, attrs) { + var rect = drawRect(parentGfx, element.width, element.height, 0, assign({ + fill: getFillColor(element, defaultFillColor), + fillOpacity: HIGH_FILL_OPACITY, + stroke: getStrokeColor(element, defaultStrokeColor) + }, attrs)); + + var semantic = getSemantic(element); + + if (semantic.$type === 'bpmn:Lane') { + var text = semantic.name; + renderLaneLabel(parentGfx, text, element); + } + + return rect; + }, + 'bpmn:InclusiveGateway': function(parentGfx, element) { + var diamond = renderer('bpmn:Gateway')(parentGfx, element); + + /* circle path */ + drawCircle(parentGfx, element.width, element.height, element.height * 0.24, { + strokeWidth: 2.5, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return diamond; + }, + 'bpmn:ExclusiveGateway': function(parentGfx, element) { + var diamond = renderer('bpmn:Gateway')(parentGfx, element); + + var pathData = pathMap.getScaledPath('GATEWAY_EXCLUSIVE', { + xScaleFactor: 0.4, + yScaleFactor: 0.4, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.32, + my: 0.3 + } + }); + + if ((getDi(element).isMarkerVisible)) { + drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getStrokeColor(element, defaultStrokeColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + } + + return diamond; + }, + 'bpmn:ComplexGateway': function(parentGfx, element) { + var diamond = renderer('bpmn:Gateway')(parentGfx, element); + + var pathData = pathMap.getScaledPath('GATEWAY_COMPLEX', { + xScaleFactor: 0.5, + yScaleFactor:0.5, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.46, + my: 0.26 + } + }); + + /* complex path */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getStrokeColor(element, defaultStrokeColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return diamond; + }, + 'bpmn:ParallelGateway': function(parentGfx, element) { + var diamond = renderer('bpmn:Gateway')(parentGfx, element); + + var pathData = pathMap.getScaledPath('GATEWAY_PARALLEL', { + xScaleFactor: 0.6, + yScaleFactor:0.6, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.46, + my: 0.2 + } + }); + + /* parallel path */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getStrokeColor(element, defaultStrokeColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return diamond; + }, + 'bpmn:EventBasedGateway': function(parentGfx, element) { + + var semantic = getSemantic(element); + + var diamond = renderer('bpmn:Gateway')(parentGfx, element); + + /* outer circle path */ drawCircle(parentGfx, element.width, element.height, element.height * 0.20, { + strokeWidth: 1, + fill: 'none', + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var type = semantic.eventGatewayType; + var instantiate = !!semantic.instantiate; + + function drawEvent() { + + var pathData = pathMap.getScaledPath('GATEWAY_EVENT_BASED', { + xScaleFactor: 0.18, + yScaleFactor: 0.18, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.36, + my: 0.44 + } + }); + + var attrs = { + strokeWidth: 2, + fill: getFillColor(element, 'none'), + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + /* event path */ drawPath(parentGfx, pathData, attrs); + } + + if (type === 'Parallel') { + + var pathData = pathMap.getScaledPath('GATEWAY_PARALLEL', { + xScaleFactor: 0.4, + yScaleFactor:0.4, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.474, + my: 0.296 + } + }); + + var parallelPath = drawPath(parentGfx, pathData); + attr$1(parallelPath, { + strokeWidth: 1, + fill: 'none' + }); + } else if (type === 'Exclusive') { + + if (!instantiate) { + var innerCircle = drawCircle(parentGfx, element.width, element.height, element.height * 0.26); + attr$1(innerCircle, { + strokeWidth: 1, + fill: 'none', + stroke: getStrokeColor(element, defaultStrokeColor) + }); + } + + drawEvent(); + } + + + return diamond; + }, + 'bpmn:Gateway': function(parentGfx, element) { + var attrs = { + fill: getFillColor(element, defaultFillColor), + fillOpacity: DEFAULT_FILL_OPACITY, + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + return drawDiamond(parentGfx, element.width, element.height, attrs); + }, + 'bpmn:SequenceFlow': function(parentGfx, element) { + var pathData = createPathFromConnection(element); + + var fill = getFillColor(element, defaultFillColor), + stroke = getStrokeColor(element, defaultStrokeColor); + + var attrs = { + strokeLinejoin: 'round', + markerEnd: marker('sequenceflow-end', fill, stroke), + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + var path = drawPath(parentGfx, pathData, attrs); + + var sequenceFlow = getSemantic(element); + + var source; + + if (element.source) { + source = element.source.businessObject; + + // conditional flow marker + if (sequenceFlow.conditionExpression && source.$instanceOf('bpmn:Activity')) { + attr$1(path, { + markerStart: marker('conditional-flow-marker', fill, stroke) + }); + } + + // default marker + if (source.default && (source.$instanceOf('bpmn:Gateway') || source.$instanceOf('bpmn:Activity')) && + source.default === sequenceFlow) { + attr$1(path, { + markerStart: marker('conditional-default-flow-marker', fill, stroke) + }); + } + } + + return path; + }, + 'bpmn:Association': function(parentGfx, element, attrs) { + + var semantic = getSemantic(element); + + var fill = getFillColor(element, defaultFillColor), + stroke = getStrokeColor(element, defaultStrokeColor); + + attrs = assign({ + strokeDasharray: '0.5, 5', + strokeLinecap: 'round', + strokeLinejoin: 'round', + stroke: getStrokeColor(element, defaultStrokeColor) + }, attrs || {}); + + if (semantic.associationDirection === 'One' || + semantic.associationDirection === 'Both') { + attrs.markerEnd = marker('association-end', fill, stroke); + } + + if (semantic.associationDirection === 'Both') { + attrs.markerStart = marker('association-start', fill, stroke); + } + + return drawLine(parentGfx, element.waypoints, attrs); + }, + 'bpmn:DataInputAssociation': function(parentGfx, element) { + var fill = getFillColor(element, defaultFillColor), + stroke = getStrokeColor(element, defaultStrokeColor); + + return renderer('bpmn:Association')(parentGfx, element, { + markerEnd: marker('association-end', fill, stroke) + }); + }, + 'bpmn:DataOutputAssociation': function(parentGfx, element) { + var fill = getFillColor(element, defaultFillColor), + stroke = getStrokeColor(element, defaultStrokeColor); + + return renderer('bpmn:Association')(parentGfx, element, { + markerEnd: marker('association-end', fill, stroke) + }); + }, + 'bpmn:MessageFlow': function(parentGfx, element) { + + var semantic = getSemantic(element), + di = getDi(element); + + var fill = getFillColor(element, defaultFillColor), + stroke = getStrokeColor(element, defaultStrokeColor); + + var pathData = createPathFromConnection(element); + + var attrs = { + markerEnd: marker('messageflow-end', fill, stroke), + markerStart: marker('messageflow-start', fill, stroke), + strokeDasharray: '10, 12', + strokeLinecap: 'round', + strokeLinejoin: 'round', + strokeWidth: '1.5px', + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + var path = drawPath(parentGfx, pathData, attrs); + + if (semantic.messageRef) { + var midPoint = path.getPointAtLength(path.getTotalLength() / 2); + + var markerPathData = pathMap.getScaledPath('MESSAGE_FLOW_MARKER', { + abspos: { + x: midPoint.x, + y: midPoint.y + } + }); + + var messageAttrs = { strokeWidth: 1 }; + + if (di.messageVisibleKind === 'initiating') { + messageAttrs.fill = 'white'; + messageAttrs.stroke = black; + } else { + messageAttrs.fill = '#888'; + messageAttrs.stroke = 'white'; + } + + var message = drawPath(parentGfx, markerPathData, messageAttrs); + + var labelText = semantic.messageRef.name; + var label = renderLabel(parentGfx, labelText, { + align: 'center-top', + fitBox: true, + style: { + fill: getStrokeColor(element, defaultLabelColor) + } + }); + + var messageBounds = message.getBBox(), + labelBounds = label.getBBox(); + + var translateX = midPoint.x - labelBounds.width / 2, + translateY = midPoint.y + messageBounds.height / 2 + ELEMENT_LABEL_DISTANCE; + + transform(label, translateX, translateY, 0); + + } + + return path; + }, + 'bpmn:DataObject': function(parentGfx, element) { + var pathData = pathMap.getScaledPath('DATA_OBJECT_PATH', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.474, + my: 0.296 + } + }); + + var elementObject = drawPath(parentGfx, pathData, { + fill: getFillColor(element, defaultFillColor), + fillOpacity: DEFAULT_FILL_OPACITY, + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var semantic = getSemantic(element); + + if (isCollection(semantic)) { + renderDataItemCollection(parentGfx, element); + } + + return elementObject; + }, + 'bpmn:DataObjectReference': as('bpmn:DataObject'), + 'bpmn:DataInput': function(parentGfx, element) { + + var arrowPathData = pathMap.getRawPath('DATA_ARROW'); + + // page + var elementObject = renderer('bpmn:DataObject')(parentGfx, element); + + /* input arrow path */ drawPath(parentGfx, arrowPathData, { strokeWidth: 1 }); + + return elementObject; + }, + 'bpmn:DataOutput': function(parentGfx, element) { + var arrowPathData = pathMap.getRawPath('DATA_ARROW'); + + // page + var elementObject = renderer('bpmn:DataObject')(parentGfx, element); + + /* output arrow path */ drawPath(parentGfx, arrowPathData, { + strokeWidth: 1, + fill: black + }); + + return elementObject; + }, + 'bpmn:DataStoreReference': function(parentGfx, element) { + var DATA_STORE_PATH = pathMap.getScaledPath('DATA_STORE', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0, + my: 0.133 + } + }); + + var elementStore = drawPath(parentGfx, DATA_STORE_PATH, { + strokeWidth: 2, + fill: getFillColor(element, defaultFillColor), + fillOpacity: DEFAULT_FILL_OPACITY, + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return elementStore; + }, + 'bpmn:BoundaryEvent': function(parentGfx, element) { + + var semantic = getSemantic(element), + cancel = semantic.cancelActivity; + + var attrs = { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + if (!cancel) { + attrs.strokeDasharray = '6'; + attrs.strokeLinecap = 'round'; + } + + // apply fillOpacity + var outerAttrs = assign({}, attrs, { + fillOpacity: 1 + }); + + // apply no-fill + var innerAttrs = assign({}, attrs, { + fill: 'none' + }); + + var outer = renderer('bpmn:Event')(parentGfx, element, outerAttrs); + + /* inner path */ drawCircle(parentGfx, element.width, element.height, INNER_OUTER_DIST, innerAttrs); + + renderEventContent(element, parentGfx); + + return outer; + }, + 'bpmn:Group': function(parentGfx, element) { + + var group = drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS, { + stroke: getStrokeColor(element, defaultStrokeColor), + strokeWidth: 1, + strokeDasharray: '8,3,1,3', + fill: 'none', + pointerEvents: 'none' + }); + + return group; + }, + 'label': function(parentGfx, element) { + return renderExternalLabel(parentGfx, element); + }, + 'bpmn:TextAnnotation': function(parentGfx, element) { + var style = { + 'fill': 'none', + 'stroke': 'none' + }; + + var textElement = drawRect(parentGfx, element.width, element.height, 0, 0, style); + + var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.0, + my: 0.0 + } + }); + + drawPath(parentGfx, textPathData, { + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var text = getSemantic(element).text || ''; + renderLabel(parentGfx, text, { + box: element, + align: 'left-top', + padding: 5, + style: { + fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor) + } + }); + + return textElement; + }, + 'ParticipantMultiplicityMarker': function(parentGfx, element) { + var markerPath = pathMap.getScaledPath('MARKER_PARALLEL', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2) / element.width), + my: (element.height - 15) / element.height + } + }); + + drawMarker('participant-multiplicity', parentGfx, markerPath, { + strokeWidth: 2, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + }, + 'SubProcessMarker': function(parentGfx, element) { + var markerRect = drawRect(parentGfx, 14, 14, 0, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + // Process marker is placed in the middle of the box + // therefore fixed values can be used here + translate$1(markerRect, element.width / 2 - 7.5, element.height - 20); + + var markerPath = pathMap.getScaledPath('MARKER_SUB_PROCESS', { + xScaleFactor: 1.5, + yScaleFactor: 1.5, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: (element.width / 2 - 7.5) / element.width, + my: (element.height - 20) / element.height + } + }); + + drawMarker('sub-process', parentGfx, markerPath, { + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + }, + 'ParallelMarker': function(parentGfx, element, position) { + var markerPath = pathMap.getScaledPath('MARKER_PARALLEL', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2 + position.parallel) / element.width), + my: (element.height - 20) / element.height + } + }); + + drawMarker('parallel', parentGfx, markerPath, { + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + }, + 'SequentialMarker': function(parentGfx, element, position) { + var markerPath = pathMap.getScaledPath('MARKER_SEQUENTIAL', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2 + position.seq) / element.width), + my: (element.height - 19) / element.height + } + }); + + drawMarker('sequential', parentGfx, markerPath, { + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + }, + 'CompensationMarker': function(parentGfx, element, position) { + var markerMath = pathMap.getScaledPath('MARKER_COMPENSATION', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2 + position.compensation) / element.width), + my: (element.height - 13) / element.height + } + }); + + drawMarker('compensation', parentGfx, markerMath, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + }, + 'LoopMarker': function(parentGfx, element, position) { + var markerPath = pathMap.getScaledPath('MARKER_LOOP', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2 + position.loop) / element.width), + my: (element.height - 7) / element.height + } + }); + + drawMarker('loop', parentGfx, markerPath, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor), + strokeLinecap: 'round', + strokeMiterlimit: 0.5 + }); + }, + 'AdhocMarker': function(parentGfx, element, position) { + var markerPath = pathMap.getScaledPath('MARKER_ADHOC', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2 + position.adhoc) / element.width), + my: (element.height - 15) / element.height + } + }); + + drawMarker('adhoc', parentGfx, markerPath, { + strokeWidth: 1, + fill: getStrokeColor(element, defaultStrokeColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + } + }; + + function attachTaskMarkers(parentGfx, element, taskMarkers) { + var obj = getSemantic(element); + + var subprocess = taskMarkers && taskMarkers.indexOf('SubProcessMarker') !== -1; + var position; + + if (subprocess) { + position = { + seq: -21, + parallel: -22, + compensation: -42, + loop: -18, + adhoc: 10 + }; + } else { + position = { + seq: -3, + parallel: -6, + compensation: -27, + loop: 0, + adhoc: 10 + }; + } + + forEach$1(taskMarkers, function(marker) { + renderer(marker)(parentGfx, element, position); + }); + + if (obj.isForCompensation) { + renderer('CompensationMarker')(parentGfx, element, position); + } + + if (obj.$type === 'bpmn:AdHocSubProcess') { + renderer('AdhocMarker')(parentGfx, element, position); + } + + var loopCharacteristics = obj.loopCharacteristics, + isSequential = loopCharacteristics && loopCharacteristics.isSequential; + + if (loopCharacteristics) { + + if (isSequential === undefined) { + renderer('LoopMarker')(parentGfx, element, position); + } + + if (isSequential === false) { + renderer('ParallelMarker')(parentGfx, element, position); + } + + if (isSequential === true) { + renderer('SequentialMarker')(parentGfx, element, position); + } + } + } + + function renderDataItemCollection(parentGfx, element) { + + var yPosition = (element.height - 18) / element.height; + + var pathData = pathMap.getScaledPath('DATA_OBJECT_COLLECTION_PATH', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.33, + my: yPosition + } + }); + + /* collection path */ drawPath(parentGfx, pathData, { + strokeWidth: 2 + }); + } + + + // extension API, use at your own risk + this._drawPath = drawPath; + + this._renderer = renderer; + } + + + e(BpmnRenderer, BaseRenderer); + + BpmnRenderer.$inject = [ + 'config.bpmnRenderer', + 'eventBus', + 'styles', + 'pathMap', + 'canvas', + 'textRenderer' + ]; + + + BpmnRenderer.prototype.canRender = function(element) { + return is$1(element, 'bpmn:BaseElement'); + }; + + BpmnRenderer.prototype.drawShape = function(parentGfx, element) { + var type = element.type; + var h = this._renderer(type); + + /* jshint -W040 */ + return h(parentGfx, element); + }; + + BpmnRenderer.prototype.drawConnection = function(parentGfx, element) { + var type = element.type; + var h = this._renderer(type); + + /* jshint -W040 */ + return h(parentGfx, element); + }; + + BpmnRenderer.prototype.getShapePath = function(element) { + + if (is$1(element, 'bpmn:Event')) { + return getCirclePath(element); + } + + if (is$1(element, 'bpmn:Activity')) { + return getRoundRectPath(element, TASK_BORDER_RADIUS); + } + + if (is$1(element, 'bpmn:Gateway')) { + return getDiamondPath(element); + } + + return getRectPath(element); + }; + + var DEFAULT_BOX_PADDING = 0; + + var DEFAULT_LABEL_SIZE$1 = { + width: 150, + height: 50 + }; + + + function parseAlign(align) { + + var parts = align.split('-'); + + return { + horizontal: parts[0] || 'center', + vertical: parts[1] || 'top' + }; + } + + function parsePadding(padding) { + + if (isObject(padding)) { + return assign({ top: 0, left: 0, right: 0, bottom: 0 }, padding); + } else { + return { + top: padding, + left: padding, + right: padding, + bottom: padding + }; + } + } + + function getTextBBox(text, fakeText) { + + fakeText.textContent = text; + + var textBBox; + + try { + var bbox, + emptyLine = text === ''; + + // add dummy text, when line is empty to + // determine correct height + fakeText.textContent = emptyLine ? 'dummy' : text; + + textBBox = fakeText.getBBox(); + + // take text rendering related horizontal + // padding into account + bbox = { + width: textBBox.width + textBBox.x * 2, + height: textBBox.height + }; + + if (emptyLine) { + + // correct width + bbox.width = 0; + } + + return bbox; + } catch (e) { + return { width: 0, height: 0 }; + } + } + + + /** + * Layout the next line and return the layouted element. + * + * Alters the lines passed. + * + * @param {Array} lines + * @return {Object} the line descriptor, an object { width, height, text } + */ + function layoutNext(lines, maxWidth, fakeText) { + + var originalLine = lines.shift(), + fitLine = originalLine; + + var textBBox; + + for (;;) { + textBBox = getTextBBox(fitLine, fakeText); + + textBBox.width = fitLine ? textBBox.width : 0; + + // try to fit + if (fitLine === ' ' || fitLine === '' || textBBox.width < Math.round(maxWidth) || fitLine.length < 2) { + return fit(lines, fitLine, originalLine, textBBox); + } + + fitLine = shortenLine(fitLine, textBBox.width, maxWidth); + } + } + + function fit(lines, fitLine, originalLine, textBBox) { + if (fitLine.length < originalLine.length) { + var remainder = originalLine.slice(fitLine.length).trim(); + + lines.unshift(remainder); + } + + return { + width: textBBox.width, + height: textBBox.height, + text: fitLine + }; + } + + var SOFT_BREAK = '\u00AD'; + + + /** + * Shortens a line based on spacing and hyphens. + * Returns the shortened result on success. + * + * @param {string} line + * @param {number} maxLength the maximum characters of the string + * @return {string} the shortened string + */ + function semanticShorten(line, maxLength) { + + var parts = line.split(/(\s|-|\u00AD)/g), + part, + shortenedParts = [], + length = 0; + + // try to shorten via break chars + if (parts.length > 1) { + + while ((part = parts.shift())) { + if (part.length + length < maxLength) { + shortenedParts.push(part); + length += part.length; + } else { + + // remove previous part, too if hyphen does not fit anymore + if (part === '-' || part === SOFT_BREAK) { + shortenedParts.pop(); + } + + break; + } + } + } + + var last = shortenedParts[shortenedParts.length - 1]; + + // translate trailing soft break to actual hyphen + if (last && last === SOFT_BREAK) { + shortenedParts[shortenedParts.length - 1] = '-'; + } + + return shortenedParts.join(''); + } + + + function shortenLine(line, width, maxWidth) { + var length = Math.max(line.length * (maxWidth / width), 1); + + // try to shorten semantically (i.e. based on spaces and hyphens) + var shortenedLine = semanticShorten(line, length); + + if (!shortenedLine) { + + // force shorten by cutting the long word + shortenedLine = line.slice(0, Math.max(Math.round(length - 1), 1)); + } + + return shortenedLine; + } + + + function getHelperSvg() { + var helperSvg = document.getElementById('helper-svg'); + + if (!helperSvg) { + helperSvg = create$1('svg'); + + attr$1(helperSvg, { + id: 'helper-svg' + }); + + assign$1(helperSvg, { + visibility: 'hidden', + position: 'fixed', + width: 0, + height: 0 + }); + + document.body.appendChild(helperSvg); + } + + return helperSvg; + } + + + /** + * Creates a new label utility + * + * @param {Object} config + * @param {Dimensions} config.size + * @param {number} config.padding + * @param {Object} config.style + * @param {string} config.align + */ + function Text(config) { + + this._config = assign({}, { + size: DEFAULT_LABEL_SIZE$1, + padding: DEFAULT_BOX_PADDING, + style: {}, + align: 'center-top' + }, config || {}); + } + + /** + * Returns the layouted text as an SVG element. + * + * @param {string} text + * @param {Object} options + * + * @return {SVGElement} + */ + Text.prototype.createText = function(text, options) { + return this.layoutText(text, options).element; + }; + + /** + * Returns a labels layouted dimensions. + * + * @param {string} text to layout + * @param {Object} options + * + * @return {Dimensions} + */ + Text.prototype.getDimensions = function(text, options) { + return this.layoutText(text, options).dimensions; + }; + + /** + * Creates and returns a label and its bounding box. + * + * @method Text#createText + * + * @param {string} text the text to render on the label + * @param {Object} options + * @param {string} options.align how to align in the bounding box. + * Any of { 'center-middle', 'center-top' }, + * defaults to 'center-top'. + * @param {string} options.style style to be applied to the text + * @param {boolean} options.fitBox indicates if box will be recalculated to + * fit text + * + * @return {Object} { element, dimensions } + */ + Text.prototype.layoutText = function(text, options) { + var box = assign({}, this._config.size, options.box), + style = assign({}, this._config.style, options.style), + align = parseAlign(options.align || this._config.align), + padding = parsePadding(options.padding !== undefined ? options.padding : this._config.padding), + fitBox = options.fitBox || false; + + var lineHeight = getLineHeight(style); + + // we split text by lines and normalize + // {soft break} + {line break} => { line break } + var lines = text.split(/\u00AD?\r?\n/), + layouted = []; + + var maxWidth = box.width - padding.left - padding.right; + + // ensure correct rendering by attaching helper text node to invisible SVG + var helperText = create$1('text'); + attr$1(helperText, { x: 0, y: 0 }); + attr$1(helperText, style); + + var helperSvg = getHelperSvg(); + + append(helperSvg, helperText); + + while (lines.length) { + layouted.push(layoutNext(lines, maxWidth, helperText)); + } + + if (align.vertical === 'middle') { + padding.top = padding.bottom = 0; + } + + var totalHeight = reduce(layouted, function(sum, line, idx) { + return sum + (lineHeight || line.height); + }, 0) + padding.top + padding.bottom; + + var maxLineWidth = reduce(layouted, function(sum, line, idx) { + return line.width > sum ? line.width : sum; + }, 0); + + // the y position of the next line + var y = padding.top; + + if (align.vertical === 'middle') { + y += (box.height - totalHeight) / 2; + } + + // magic number initial offset + y -= (lineHeight || layouted[0].height) / 4; + + + var textElement = create$1('text'); + + attr$1(textElement, style); + + // layout each line taking into account that parent + // shape might resize to fit text size + forEach$1(layouted, function(line) { + + var x; + + y += (lineHeight || line.height); + + switch (align.horizontal) { + case 'left': + x = padding.left; + break; + + case 'right': + x = ((fitBox ? maxLineWidth : maxWidth) + - padding.right - line.width); + break; + + default: + + // aka center + x = Math.max((((fitBox ? maxLineWidth : maxWidth) + - line.width) / 2 + padding.left), 0); + } + + var tspan = create$1('tspan'); + attr$1(tspan, { x: x, y: y }); + + tspan.textContent = line.text; + + append(textElement, tspan); + }); + + remove$2(helperText); + + var dimensions = { + width: maxLineWidth, + height: totalHeight + }; + + return { + dimensions: dimensions, + element: textElement + }; + }; + + + function getLineHeight(style) { + if ('fontSize' in style && 'lineHeight' in style) { + return style.lineHeight * parseInt(style.fontSize, 10); + } + } + + var DEFAULT_FONT_SIZE = 12; + var LINE_HEIGHT_RATIO = 1.2; + + var MIN_TEXT_ANNOTATION_HEIGHT = 30; + + + function TextRenderer(config) { + + var defaultStyle = assign({ + fontFamily: 'Arial, sans-serif', + fontSize: DEFAULT_FONT_SIZE, + fontWeight: 'normal', + lineHeight: LINE_HEIGHT_RATIO + }, config && config.defaultStyle || {}); + + var fontSize = parseInt(defaultStyle.fontSize, 10) - 1; + + var externalStyle = assign({}, defaultStyle, { + fontSize: fontSize + }, config && config.externalStyle || {}); + + var textUtil = new Text({ + style: defaultStyle + }); + + /** + * Get the new bounds of an externally rendered, + * layouted label. + * + * @param {Bounds} bounds + * @param {string} text + * + * @return {Bounds} + */ + this.getExternalLabelBounds = function(bounds, text) { + + var layoutedDimensions = textUtil.getDimensions(text, { + box: { + width: 90, + height: 30, + x: bounds.width / 2 + bounds.x, + y: bounds.height / 2 + bounds.y + }, + style: externalStyle + }); + + // resize label shape to fit label text + return { + x: Math.round(bounds.x + bounds.width / 2 - layoutedDimensions.width / 2), + y: Math.round(bounds.y), + width: Math.ceil(layoutedDimensions.width), + height: Math.ceil(layoutedDimensions.height) + }; + + }; + + /** + * Get the new bounds of text annotation. + * + * @param {Bounds} bounds + * @param {string} text + * + * @return {Bounds} + */ + this.getTextAnnotationBounds = function(bounds, text) { + + var layoutedDimensions = textUtil.getDimensions(text, { + box: bounds, + style: defaultStyle, + align: 'left-top', + padding: 5 + }); + + return { + x: bounds.x, + y: bounds.y, + width: bounds.width, + height: Math.max(MIN_TEXT_ANNOTATION_HEIGHT, Math.round(layoutedDimensions.height)) + }; + }; + + /** + * Create a layouted text element. + * + * @param {string} text + * @param {Object} [options] + * + * @return {SVGElement} rendered text + */ + this.createText = function(text, options) { + return textUtil.createText(text, options || {}); + }; + + /** + * Get default text style. + */ + this.getDefaultStyle = function() { + return defaultStyle; + }; + + /** + * Get the external text style. + */ + this.getExternalStyle = function() { + return externalStyle; + }; + + } + + TextRenderer.$inject = [ + 'config.textRenderer' + ]; + + /** + * Map containing SVG paths needed by BpmnRenderer. + */ + + function PathMap() { + + /** + * Contains a map of path elements + * + *

      Path definition

      + * A parameterized path is defined like this: + *
      +     * 'GATEWAY_PARALLEL': {
      +     *   d: 'm {mx},{my} {e.x0},0 0,{e.x1} {e.x1},0 0,{e.y0} -{e.x1},0 0,{e.y1} ' +
      +            '-{e.x0},0 0,-{e.y1} -{e.x1},0 0,-{e.y0} {e.x1},0 z',
      +     *   height: 17.5,
      +     *   width:  17.5,
      +     *   heightElements: [2.5, 7.5],
      +     *   widthElements: [2.5, 7.5]
      +     * }
      +     * 
      + *

      It's important to specify a correct height and width for the path as the scaling + * is based on the ratio between the specified height and width in this object and the + * height and width that is set as scale target (Note x,y coordinates will be scaled with + * individual ratios).

      + *

      The 'heightElements' and 'widthElements' array must contain the values that will be scaled. + * The scaling is based on the computed ratios. + * Coordinates on the y axis should be in the heightElement's array, they will be scaled using + * the computed ratio coefficient. + * In the parameterized path the scaled values can be accessed through the 'e' object in {} brackets. + *

        + *
      • The values for the y axis can be accessed in the path string using {e.y0}, {e.y1}, ....
      • + *
      • The values for the x axis can be accessed in the path string using {e.x0}, {e.x1}, ....
      • + *
      + * The numbers x0, x1 respectively y0, y1, ... map to the corresponding array index. + *

      + */ + this.pathMap = { + 'EVENT_MESSAGE': { + d: 'm {mx},{my} l 0,{e.y1} l {e.x1},0 l 0,-{e.y1} z l {e.x0},{e.y0} l {e.x0},-{e.y0}', + height: 36, + width: 36, + heightElements: [ 6, 14 ], + widthElements: [ 10.5, 21 ] + }, + 'EVENT_SIGNAL': { + d: 'M {mx},{my} l {e.x0},{e.y0} l -{e.x1},0 Z', + height: 36, + width: 36, + heightElements: [ 18 ], + widthElements: [ 10, 20 ] + }, + 'EVENT_ESCALATION': { + d: 'M {mx},{my} l {e.x0},{e.y0} l -{e.x0},-{e.y1} l -{e.x0},{e.y1} Z', + height: 36, + width: 36, + heightElements: [ 20, 7 ], + widthElements: [ 8 ] + }, + 'EVENT_CONDITIONAL': { + d: 'M {e.x0},{e.y0} l {e.x1},0 l 0,{e.y2} l -{e.x1},0 Z ' + + 'M {e.x2},{e.y3} l {e.x0},0 ' + + 'M {e.x2},{e.y4} l {e.x0},0 ' + + 'M {e.x2},{e.y5} l {e.x0},0 ' + + 'M {e.x2},{e.y6} l {e.x0},0 ' + + 'M {e.x2},{e.y7} l {e.x0},0 ' + + 'M {e.x2},{e.y8} l {e.x0},0 ', + height: 36, + width: 36, + heightElements: [ 8.5, 14.5, 18, 11.5, 14.5, 17.5, 20.5, 23.5, 26.5 ], + widthElements: [ 10.5, 14.5, 12.5 ] + }, + 'EVENT_LINK': { + d: 'm {mx},{my} 0,{e.y0} -{e.x1},0 0,{e.y1} {e.x1},0 0,{e.y0} {e.x0},-{e.y2} -{e.x0},-{e.y2} z', + height: 36, + width: 36, + heightElements: [ 4.4375, 6.75, 7.8125 ], + widthElements: [ 9.84375, 13.5 ] + }, + 'EVENT_ERROR': { + d: 'm {mx},{my} {e.x0},-{e.y0} {e.x1},-{e.y1} {e.x2},{e.y2} {e.x3},-{e.y3} -{e.x4},{e.y4} -{e.x5},-{e.y5} z', + height: 36, + width: 36, + heightElements: [ 0.023, 8.737, 8.151, 16.564, 10.591, 8.714 ], + widthElements: [ 0.085, 6.672, 6.97, 4.273, 5.337, 6.636 ] + }, + 'EVENT_CANCEL_45': { + d: 'm {mx},{my} -{e.x1},0 0,{e.x0} {e.x1},0 0,{e.y1} {e.x0},0 ' + + '0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z', + height: 36, + width: 36, + heightElements: [ 4.75, 8.5 ], + widthElements: [ 4.75, 8.5 ] + }, + 'EVENT_COMPENSATION': { + d: 'm {mx},{my} {e.x0},-{e.y0} 0,{e.y1} z m {e.x1},-{e.y2} {e.x2},-{e.y3} 0,{e.y1} -{e.x2},-{e.y3} z', + height: 36, + width: 36, + heightElements: [ 6.5, 13, 0.4, 6.1 ], + widthElements: [ 9, 9.3, 8.7 ] + }, + 'EVENT_TIMER_WH': { + d: 'M {mx},{my} l {e.x0},-{e.y0} m -{e.x0},{e.y0} l {e.x1},{e.y1} ', + height: 36, + width: 36, + heightElements: [ 10, 2 ], + widthElements: [ 3, 7 ] + }, + 'EVENT_TIMER_LINE': { + d: 'M {mx},{my} ' + + 'm {e.x0},{e.y0} l -{e.x1},{e.y1} ', + height: 36, + width: 36, + heightElements: [ 10, 3 ], + widthElements: [ 0, 0 ] + }, + 'EVENT_MULTIPLE': { + d:'m {mx},{my} {e.x1},-{e.y0} {e.x1},{e.y0} -{e.x0},{e.y1} -{e.x2},0 z', + height: 36, + width: 36, + heightElements: [ 6.28099, 12.56199 ], + widthElements: [ 3.1405, 9.42149, 12.56198 ] + }, + 'EVENT_PARALLEL_MULTIPLE': { + d:'m {mx},{my} {e.x0},0 0,{e.y1} {e.x1},0 0,{e.y0} -{e.x1},0 0,{e.y1} ' + + '-{e.x0},0 0,-{e.y1} -{e.x1},0 0,-{e.y0} {e.x1},0 z', + height: 36, + width: 36, + heightElements: [ 2.56228, 7.68683 ], + widthElements: [ 2.56228, 7.68683 ] + }, + 'GATEWAY_EXCLUSIVE': { + d:'m {mx},{my} {e.x0},{e.y0} {e.x1},{e.y0} {e.x2},0 {e.x4},{e.y2} ' + + '{e.x4},{e.y1} {e.x2},0 {e.x1},{e.y3} {e.x0},{e.y3} ' + + '{e.x3},0 {e.x5},{e.y1} {e.x5},{e.y2} {e.x3},0 z', + height: 17.5, + width: 17.5, + heightElements: [ 8.5, 6.5312, -6.5312, -8.5 ], + widthElements: [ 6.5, -6.5, 3, -3, 5, -5 ] + }, + 'GATEWAY_PARALLEL': { + d:'m {mx},{my} 0,{e.y1} -{e.x1},0 0,{e.y0} {e.x1},0 0,{e.y1} {e.x0},0 ' + + '0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z', + height: 30, + width: 30, + heightElements: [ 5, 12.5 ], + widthElements: [ 5, 12.5 ] + }, + 'GATEWAY_EVENT_BASED': { + d:'m {mx},{my} {e.x0},{e.y0} {e.x0},{e.y1} {e.x1},{e.y2} {e.x2},0 z', + height: 11, + width: 11, + heightElements: [ -6, 6, 12, -12 ], + widthElements: [ 9, -3, -12 ] + }, + 'GATEWAY_COMPLEX': { + d:'m {mx},{my} 0,{e.y0} -{e.x0},-{e.y1} -{e.x1},{e.y2} {e.x0},{e.y1} -{e.x2},0 0,{e.y3} ' + + '{e.x2},0 -{e.x0},{e.y1} l {e.x1},{e.y2} {e.x0},-{e.y1} 0,{e.y0} {e.x3},0 0,-{e.y0} {e.x0},{e.y1} ' + + '{e.x1},-{e.y2} -{e.x0},-{e.y1} {e.x2},0 0,-{e.y3} -{e.x2},0 {e.x0},-{e.y1} -{e.x1},-{e.y2} ' + + '-{e.x0},{e.y1} 0,-{e.y0} -{e.x3},0 z', + height: 17.125, + width: 17.125, + heightElements: [ 4.875, 3.4375, 2.125, 3 ], + widthElements: [ 3.4375, 2.125, 4.875, 3 ] + }, + 'DATA_OBJECT_PATH': { + d:'m 0,0 {e.x1},0 {e.x0},{e.y0} 0,{e.y1} -{e.x2},0 0,-{e.y2} {e.x1},0 0,{e.y0} {e.x0},0', + height: 61, + width: 51, + heightElements: [ 10, 50, 60 ], + widthElements: [ 10, 40, 50, 60 ] + }, + 'DATA_OBJECT_COLLECTION_PATH': { + d: 'm{mx},{my} m 3,2 l 0,10 m 3,-10 l 0,10 m 3,-10 l 0,10', + height: 10, + width: 10, + heightElements: [], + widthElements: [] + }, + 'DATA_ARROW': { + d:'m 5,9 9,0 0,-3 5,5 -5,5 0,-3 -9,0 z', + height: 61, + width: 51, + heightElements: [], + widthElements: [] + }, + 'DATA_STORE': { + d:'m {mx},{my} ' + + 'l 0,{e.y2} ' + + 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 ' + + 'l 0,-{e.y2} ' + + 'c -{e.x0},-{e.y1} -{e.x1},-{e.y1} -{e.x2},0' + + 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 ' + + 'm -{e.x2},{e.y0}' + + 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0' + + 'm -{e.x2},{e.y0}' + + 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0', + height: 61, + width: 61, + heightElements: [ 7, 10, 45 ], + widthElements: [ 2, 58, 60 ] + }, + 'TEXT_ANNOTATION': { + d: 'm {mx}, {my} m 10,0 l -10,0 l 0,{e.y0} l 10,0', + height: 30, + width: 10, + heightElements: [ 30 ], + widthElements: [ 10 ] + }, + 'MARKER_SUB_PROCESS': { + d: 'm{mx},{my} m 7,2 l 0,10 m -5,-5 l 10,0', + height: 10, + width: 10, + heightElements: [], + widthElements: [] + }, + 'MARKER_PARALLEL': { + d: 'm{mx},{my} m 3,2 l 0,10 m 3,-10 l 0,10 m 3,-10 l 0,10', + height: 10, + width: 10, + heightElements: [], + widthElements: [] + }, + 'MARKER_SEQUENTIAL': { + d: 'm{mx},{my} m 0,3 l 10,0 m -10,3 l 10,0 m -10,3 l 10,0', + height: 10, + width: 10, + heightElements: [], + widthElements: [] + }, + 'MARKER_COMPENSATION': { + d: 'm {mx},{my} 7,-5 0,10 z m 7.1,-0.3 6.9,-4.7 0,10 -6.9,-4.7 z', + height: 10, + width: 21, + heightElements: [], + widthElements: [] + }, + 'MARKER_LOOP': { + d: 'm {mx},{my} c 3.526979,0 6.386161,-2.829858 6.386161,-6.320661 0,-3.490806 -2.859182,-6.320661 ' + + '-6.386161,-6.320661 -3.526978,0 -6.38616,2.829855 -6.38616,6.320661 0,1.745402 ' + + '0.714797,3.325567 1.870463,4.469381 0.577834,0.571908 1.265885,1.034728 2.029916,1.35457 ' + + 'l -0.718163,-3.909793 m 0.718163,3.909793 -3.885211,0.802902', + height: 13.9, + width: 13.7, + heightElements: [], + widthElements: [] + }, + 'MARKER_ADHOC': { + d: 'm {mx},{my} m 0.84461,2.64411 c 1.05533,-1.23780996 2.64337,-2.07882 4.29653,-1.97997996 2.05163,0.0805 ' + + '3.85579,1.15803 5.76082,1.79107 1.06385,0.34139996 2.24454,0.1438 3.18759,-0.43767 0.61743,-0.33642 ' + + '1.2775,-0.64078 1.7542,-1.17511 0,0.56023 0,1.12046 0,1.6807 -0.98706,0.96237996 -2.29792,1.62393996 ' + + '-3.6918,1.66181996 -1.24459,0.0927 -2.46671,-0.2491 -3.59505,-0.74812 -1.35789,-0.55965 ' + + '-2.75133,-1.33436996 -4.27027,-1.18121996 -1.37741,0.14601 -2.41842,1.13685996 -3.44288,1.96782996 z', + height: 4, + width: 15, + heightElements: [], + widthElements: [] + }, + 'TASK_TYPE_SEND': { + d: 'm {mx},{my} l 0,{e.y1} l {e.x1},0 l 0,-{e.y1} z l {e.x0},{e.y0} l {e.x0},-{e.y0}', + height: 14, + width: 21, + heightElements: [ 6, 14 ], + widthElements: [ 10.5, 21 ] + }, + 'TASK_TYPE_SCRIPT': { + d: 'm {mx},{my} c 9.966553,-6.27276 -8.000926,-7.91932 2.968968,-14.938 l -8.802728,0 ' + + 'c -10.969894,7.01868 6.997585,8.66524 -2.968967,14.938 z ' + + 'm -7,-12 l 5,0 ' + + 'm -4.5,3 l 4.5,0 ' + + 'm -3,3 l 5,0' + + 'm -4,3 l 5,0', + height: 15, + width: 12.6, + heightElements: [ 6, 14 ], + widthElements: [ 10.5, 21 ] + }, + 'TASK_TYPE_USER_1': { + d: 'm {mx},{my} c 0.909,-0.845 1.594,-2.049 1.594,-3.385 0,-2.554 -1.805,-4.62199999 ' + + '-4.357,-4.62199999 -2.55199998,0 -4.28799998,2.06799999 -4.28799998,4.62199999 0,1.348 ' + + '0.974,2.562 1.89599998,3.405 -0.52899998,0.187 -5.669,2.097 -5.794,4.7560005 v 6.718 ' + + 'h 17 v -6.718 c 0,-2.2980005 -5.5279996,-4.5950005 -6.0509996,-4.7760005 z' + + 'm -8,6 l 0,5.5 m 11,0 l 0,-5' + }, + 'TASK_TYPE_USER_2': { + d: 'm {mx},{my} m 2.162,1.009 c 0,2.4470005 -2.158,4.4310005 -4.821,4.4310005 ' + + '-2.66499998,0 -4.822,-1.981 -4.822,-4.4310005 ' + }, + 'TASK_TYPE_USER_3': { + d: 'm {mx},{my} m -6.9,-3.80 c 0,0 2.25099998,-2.358 4.27399998,-1.177 2.024,1.181 4.221,1.537 ' + + '4.124,0.965 -0.098,-0.57 -0.117,-3.79099999 -4.191,-4.13599999 -3.57499998,0.001 ' + + '-4.20799998,3.36699999 -4.20699998,4.34799999 z' + }, + 'TASK_TYPE_MANUAL': { + d: 'm {mx},{my} c 0.234,-0.01 5.604,0.008 8.029,0.004 0.808,0 1.271,-0.172 1.417,-0.752 0.227,-0.898 ' + + '-0.334,-1.314 -1.338,-1.316 -2.467,-0.01 -7.886,-0.004 -8.108,-0.004 -0.014,-0.079 0.016,-0.533 0,-0.61 ' + + '0.195,-0.042 8.507,0.006 9.616,0.002 0.877,-0.007 1.35,-0.438 1.353,-1.208 0.003,-0.768 -0.479,-1.09 ' + + '-1.35,-1.091 -2.968,-0.002 -9.619,-0.013 -9.619,-0.013 v -0.591 c 0,0 5.052,-0.016 7.225,-0.016 ' + + '0.888,-0.002 1.354,-0.416 1.351,-1.193 -0.006,-0.761 -0.492,-1.196 -1.361,-1.196 -3.473,-0.005 ' + + '-10.86,-0.003 -11.0829995,-0.003 -0.022,-0.047 -0.045,-0.094 -0.069,-0.139 0.3939995,-0.319 ' + + '2.0409995,-1.626 2.4149995,-2.017 0.469,-0.4870005 0.519,-1.1650005 0.162,-1.6040005 -0.414,-0.511 ' + + '-0.973,-0.5 -1.48,-0.236 -1.4609995,0.764 -6.5999995,3.6430005 -7.7329995,4.2710005 -0.9,0.499 ' + + '-1.516,1.253 -1.882,2.19 -0.37000002,0.95 -0.17,2.01 -0.166,2.979 0.004,0.718 -0.27300002,1.345 ' + + '-0.055,2.063 0.629,2.087 2.425,3.312 4.859,3.318 4.6179995,0.014 9.2379995,-0.139 13.8569995,-0.158 ' + + '0.755,-0.004 1.171,-0.301 1.182,-1.033 0.012,-0.754 -0.423,-0.969 -1.183,-0.973 -1.778,-0.01 ' + + '-5.824,-0.004 -6.04,-0.004 10e-4,-0.084 0.003,-0.586 10e-4,-0.67 z' + }, + 'TASK_TYPE_INSTANTIATING_SEND': { + d: 'm {mx},{my} l 0,8.4 l 12.6,0 l 0,-8.4 z l 6.3,3.6 l 6.3,-3.6' + }, + 'TASK_TYPE_SERVICE': { + d: 'm {mx},{my} v -1.71335 c 0.352326,-0.0705 0.703932,-0.17838 1.047628,-0.32133 ' + + '0.344416,-0.14465 0.665822,-0.32133 0.966377,-0.52145 l 1.19431,1.18005 1.567487,-1.57688 ' + + '-1.195028,-1.18014 c 0.403376,-0.61394 0.683079,-1.29908 0.825447,-2.01824 l 1.622133,-0.01 ' + + 'v -2.2196 l -1.636514,0.01 c -0.07333,-0.35153 -0.178319,-0.70024 -0.323564,-1.04372 ' + + '-0.145244,-0.34406 -0.321407,-0.6644 -0.522735,-0.96217 l 1.131035,-1.13631 -1.583305,-1.56293 ' + + '-1.129598,1.13589 c -0.614052,-0.40108 -1.302883,-0.68093 -2.022633,-0.82247 l 0.0093,-1.61852 ' + + 'h -2.241173 l 0.0042,1.63124 c -0.353763,0.0736 -0.705369,0.17977 -1.049785,0.32371 -0.344415,0.14437 ' + + '-0.665102,0.32092 -0.9635006,0.52046 l -1.1698628,-1.15823 -1.5667691,1.5792 1.1684265,1.15669 ' + + 'c -0.4026573,0.61283 -0.68308,1.29797 -0.8247287,2.01713 l -1.6588041,0.003 v 2.22174 ' + + 'l 1.6724648,-0.006 c 0.073327,0.35077 0.1797598,0.70243 0.3242851,1.04472 0.1452428,0.34448 ' + + '0.3214064,0.6644 0.5227339,0.96066 l -1.1993431,1.19723 1.5840256,1.56011 1.1964668,-1.19348 ' + + 'c 0.6140517,0.40346 1.3028827,0.68232 2.0233517,0.82331 l 7.19e-4,1.69892 h 2.226848 z ' + + 'm 0.221462,-3.9957 c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 ' + + '0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 ' + + '0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z' + }, + 'TASK_TYPE_SERVICE_FILL': { + d: 'm {mx},{my} c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 ' + + '0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 ' + + '0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z' + }, + 'TASK_TYPE_BUSINESS_RULE_HEADER': { + d: 'm {mx},{my} 0,4 20,0 0,-4 z' + }, + 'TASK_TYPE_BUSINESS_RULE_MAIN': { + d: 'm {mx},{my} 0,12 20,0 0,-12 z' + + 'm 0,8 l 20,0 ' + + 'm -13,-4 l 0,8' + }, + 'MESSAGE_FLOW_MARKER': { + d: 'm {mx},{my} m -10.5 ,-7 l 0,14 l 21,0 l 0,-14 z l 10.5,6 l 10.5,-6' + } + }; + + this.getRawPath = function getRawPath(pathId) { + return this.pathMap[pathId].d; + }; + + /** + * Scales the path to the given height and width. + *

      Use case

      + *

      Use case is to scale the content of elements (event, gateways) based + * on the element bounding box's size. + *

      + *

      Why not transform

      + *

      Scaling a path with transform() will also scale the stroke and IE does not support + * the option 'non-scaling-stroke' to prevent this. + * Also there are use cases where only some parts of a path should be + * scaled.

      + * + * @param {string} pathId The ID of the path. + * @param {Object} param

      + * Example param object scales the path to 60% size of the container (data.width, data.height). + *

      +     *   {
      +     *     xScaleFactor: 0.6,
      +     *     yScaleFactor:0.6,
      +     *     containerWidth: data.width,
      +     *     containerHeight: data.height,
      +     *     position: {
      +     *       mx: 0.46,
      +     *       my: 0.2,
      +     *     }
      +     *   }
      +     *   
      + *
        + *
      • targetpathwidth = xScaleFactor * containerWidth
      • + *
      • targetpathheight = yScaleFactor * containerHeight
      • + *
      • Position is used to set the starting coordinate of the path. M is computed: + *
          + *
        • position.x * containerWidth
        • + *
        • position.y * containerHeight
        • + *
        + * Center of the container
         position: {
        +     *       mx: 0.5,
        +     *       my: 0.5,
        +     *     }
        + * Upper left corner of the container + *
         position: {
        +     *       mx: 0.0,
        +     *       my: 0.0,
        +     *     }
        + *
      • + *
      + *

      + * + */ + this.getScaledPath = function getScaledPath(pathId, param) { + var rawPath = this.pathMap[pathId]; + + // positioning + // compute the start point of the path + var mx, my; + + if (param.abspos) { + mx = param.abspos.x; + my = param.abspos.y; + } else { + mx = param.containerWidth * param.position.mx; + my = param.containerHeight * param.position.my; + } + + var coordinates = {}; // map for the scaled coordinates + if (param.position) { + + // path + var heightRatio = (param.containerHeight / rawPath.height) * param.yScaleFactor; + var widthRatio = (param.containerWidth / rawPath.width) * param.xScaleFactor; + + + // Apply height ratio + for (var heightIndex = 0; heightIndex < rawPath.heightElements.length; heightIndex++) { + coordinates['y' + heightIndex] = rawPath.heightElements[heightIndex] * heightRatio; + } + + // Apply width ratio + for (var widthIndex = 0; widthIndex < rawPath.widthElements.length; widthIndex++) { + coordinates['x' + widthIndex] = rawPath.widthElements[widthIndex] * widthRatio; + } + } + + // Apply value to raw path + var path = format( + rawPath.d, { + mx: mx, + my: my, + e: coordinates + } + ); + return path; + }; + } + + // helpers ////////////////////// + + // copied and adjusted from https://github.com/adobe-webplatform/Snap.svg/blob/master/src/svg.js + var tokenRegex = /\{([^{}]+)\}/g, + objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g; // matches .xxxxx or ["xxxxx"] to run over object properties + + function replacer(all, key, obj) { + var res = obj; + key.replace(objNotationRegex, function(all, name, quote, quotedName, isFunc) { + name = name || quotedName; + if (res) { + if (name in res) { + res = res[name]; + } + typeof res == 'function' && isFunc && (res = res()); + } + }); + res = (res == null || res == obj ? all : res) + ''; + + return res; + } + + function format(str, obj) { + return String(str).replace(tokenRegex, function(all, key) { + return replacer(all, key, obj); + }); + } + + var DrawModule$1 = { + __init__: [ 'bpmnRenderer' ], + bpmnRenderer: [ 'type', BpmnRenderer ], + textRenderer: [ 'type', TextRenderer ], + pathMap: [ 'type', PathMap ] + }; + + /** + * A simple translation stub to be used for multi-language support + * in diagrams. Can be easily replaced with a more sophisticated + * solution. + * + * @example + * + * // use it inside any diagram component by injecting `translate`. + * + * function MyService(translate) { + * alert(translate('HELLO {you}', { you: 'You!' })); + * } + * + * @param {string} template to interpolate + * @param {Object} [replacements] a map with substitutes + * + * @return {string} the translated string + */ + function translate(template, replacements) { + + replacements = replacements || {}; + + return template.replace(/{([^}]+)}/g, function(_, key) { + return replacements[key] || '{' + key + '}'; + }); + } + + var TranslateModule = { + translate: [ 'value', translate ] + }; + + var DEFAULT_LABEL_SIZE = { + width: 90, + height: 20 + }; + + var FLOW_LABEL_INDENT = 15; + + + /** + * Returns true if the given semantic has an external label + * + * @param {BpmnElement} semantic + * @return {boolean} true if has label + */ + function isLabelExternal(semantic) { + return is$1(semantic, 'bpmn:Event') || + is$1(semantic, 'bpmn:Gateway') || + is$1(semantic, 'bpmn:DataStoreReference') || + is$1(semantic, 'bpmn:DataObjectReference') || + is$1(semantic, 'bpmn:DataInput') || + is$1(semantic, 'bpmn:DataOutput') || + is$1(semantic, 'bpmn:SequenceFlow') || + is$1(semantic, 'bpmn:MessageFlow') || + is$1(semantic, 'bpmn:Group'); + } + + /** + * Get the position for sequence flow labels + * + * @param {Array} waypoints + * @return {Point} the label position + */ + function getFlowLabelPosition(waypoints) { + + // get the waypoints mid + var mid = waypoints.length / 2 - 1; + + var first = waypoints[Math.floor(mid)]; + var second = waypoints[Math.ceil(mid + 0.01)]; + + // get position + var position = getWaypointsMid(waypoints); + + // calculate angle + var angle = Math.atan((second.y - first.y) / (second.x - first.x)); + + var x = position.x, + y = position.y; + + if (Math.abs(angle) < Math.PI / 2) { + y -= FLOW_LABEL_INDENT; + } else { + x += FLOW_LABEL_INDENT; + } + + return { x: x, y: y }; + } + + + /** + * Get the middle of a number of waypoints + * + * @param {Array} waypoints + * @return {Point} the mid point + */ + function getWaypointsMid(waypoints) { + + var mid = waypoints.length / 2 - 1; + + var first = waypoints[Math.floor(mid)]; + var second = waypoints[Math.ceil(mid + 0.01)]; + + return { + x: first.x + (second.x - first.x) / 2, + y: first.y + (second.y - first.y) / 2 + }; + } + + + function getExternalLabelMid(element) { + + if (element.waypoints) { + return getFlowLabelPosition(element.waypoints); + } else if (is$1(element, 'bpmn:Group')) { + return { + x: element.x + element.width / 2, + y: element.y + DEFAULT_LABEL_SIZE.height / 2 + }; + } else { + return { + x: element.x + element.width / 2, + y: element.y + element.height + DEFAULT_LABEL_SIZE.height / 2 + }; + } + } + + + /** + * Returns the bounds of an elements label, parsed from the elements DI or + * generated from its bounds. + * + * @param {BpmndDi} di + * @param {djs.model.Base} element + */ + function getExternalLabelBounds(di, element) { + + var mid, + size, + bounds, + label = di.label; + + if (label && label.bounds) { + bounds = label.bounds; + + size = { + width: Math.max(DEFAULT_LABEL_SIZE.width, bounds.width), + height: bounds.height + }; + + mid = { + x: bounds.x + bounds.width / 2, + y: bounds.y + bounds.height / 2 + }; + } else { + + mid = getExternalLabelMid(element); + + size = DEFAULT_LABEL_SIZE; + } + + return assign({ + x: mid.x - size.width / 2, + y: mid.y - size.height / 2 + }, size); + } + + var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + + function getDefaultExportFromCjs (x) { + return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; + } + + function roundPoint(point) { + + return { + x: Math.round(point.x), + y: Math.round(point.y) + }; + } + + + /** + * Convert the given bounds to a { top, left, bottom, right } descriptor. + * + * @param {Bounds|Point} bounds + * + * @return {Object} + */ + function asTRBL(bounds) { + return { + top: bounds.y, + right: bounds.x + (bounds.width || 0), + bottom: bounds.y + (bounds.height || 0), + left: bounds.x + }; + } + + + /** + * Convert a { top, left, bottom, right } to an objects bounds. + * + * @param {Object} trbl + * + * @return {Bounds} + */ + function asBounds(trbl) { + return { + x: trbl.left, + y: trbl.top, + width: trbl.right - trbl.left, + height: trbl.bottom - trbl.top + }; + } + + + /** + * Get the mid of the given bounds or point. + * + * @param {Bounds|Point} bounds + * + * @return {Point} + */ + function getBoundsMid(bounds) { + return roundPoint({ + x: bounds.x + (bounds.width || 0) / 2, + y: bounds.y + (bounds.height || 0) / 2 + }); + } + + + /** + * Get the mid of the given Connection. + * + * @param {djs.Base.Connection} connection + * + * @return {Point} + */ + function getConnectionMid(connection) { + var waypoints = connection.waypoints; + + // calculate total length and length of each segment + var parts = waypoints.reduce(function(parts, point, index) { + + var lastPoint = waypoints[index - 1]; + + if (lastPoint) { + var lastPart = parts[parts.length - 1]; + + var startLength = lastPart && lastPart.endLength || 0; + var length = distance(lastPoint, point); + + parts.push({ + start: lastPoint, + end: point, + startLength: startLength, + endLength: startLength + length, + length: length + }); + } + + return parts; + }, []); + + var totalLength = parts.reduce(function(length, part) { + return length + part.length; + }, 0); + + // find which segement contains middle point + var midLength = totalLength / 2; + + var i = 0; + var midSegment = parts[i]; + + while (midSegment.endLength < midLength) { + midSegment = parts[++i]; + } + + // calculate relative position on mid segment + var segmentProgress = (midLength - midSegment.startLength) / midSegment.length; + + var midPoint = { + x: midSegment.start.x + (midSegment.end.x - midSegment.start.x) * segmentProgress, + y: midSegment.start.y + (midSegment.end.y - midSegment.start.y) * segmentProgress + }; + + return midPoint; + } + + + /** + * Get the mid of the given Element. + * + * @param {djs.Base.Connection} connection + * + * @return {Point} + */ + function getMid(element) { + if (isConnection(element)) { + return getConnectionMid(element); + } + + return getBoundsMid(element); + } + + // helpers ////////////////////// + + function distance(a, b) { + return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2)); + } + + function isConnection(element) { + return !!element.waypoints; + } + + function elementToString(e) { + if (!e) { + return ''; + } + + return '<' + e.$type + (e.id ? ' id="' + e.id : '') + '" />'; + } + + /** + * @param {ModdleElement} semantic + * @param {ModdleElement} di + * @param {Object} [attrs=null] + * + * @return {Object} + */ + function elementData(semantic, di, attrs) { + return assign({ + id: semantic.id, + type: semantic.$type, + businessObject: semantic, + di: di + }, attrs); + } + + function getWaypoints(di, source, target) { + + var waypoints = di.waypoint; + + if (!waypoints || waypoints.length < 2) { + return [ getMid(source), getMid(target) ]; + } + + return waypoints.map(function(p) { + return { x: p.x, y: p.y }; + }); + } + + function notYetDrawn(translate, semantic, refSemantic, property) { + return new Error(translate('element {element} referenced by {referenced}#{property} not yet drawn', { + element: elementToString(refSemantic), + referenced: elementToString(semantic), + property: property + })); + } + + + /** + * An importer that adds bpmn elements to the canvas + * + * @param {EventBus} eventBus + * @param {Canvas} canvas + * @param {ElementFactory} elementFactory + * @param {ElementRegistry} elementRegistry + * @param {Function} translate + * @param {TextRenderer} textRenderer + */ + function BpmnImporter( + eventBus, canvas, elementFactory, + elementRegistry, translate, textRenderer) { + + this._eventBus = eventBus; + this._canvas = canvas; + this._elementFactory = elementFactory; + this._elementRegistry = elementRegistry; + this._translate = translate; + this._textRenderer = textRenderer; + } + + BpmnImporter.$inject = [ + 'eventBus', + 'canvas', + 'elementFactory', + 'elementRegistry', + 'translate', + 'textRenderer' + ]; + + + /** + * Add bpmn element (semantic) to the canvas onto the + * specified parent shape. + */ + BpmnImporter.prototype.add = function(semantic, di, parentElement) { + var element, + translate = this._translate, + hidden; + + var parentIndex; + + // ROOT ELEMENT + // handle the special case that we deal with a + // invisible root element (process, subprocess or collaboration) + if (is$1(di, 'bpmndi:BPMNPlane')) { + + var attrs = is$1(semantic, 'bpmn:SubProcess') + ? { id: semantic.id + '_plane' } + : {}; + + // add a virtual element (not being drawn) + element = this._elementFactory.createRoot(elementData(semantic, di, attrs)); + + this._canvas.addRootElement(element); + } + + // SHAPE + else if (is$1(di, 'bpmndi:BPMNShape')) { + + var collapsed = !isExpanded(semantic, di), + isFrame = isFrameElement$1(semantic); + + hidden = parentElement && (parentElement.hidden || parentElement.collapsed); + + var bounds = di.bounds; + + element = this._elementFactory.createShape(elementData(semantic, di, { + collapsed: collapsed, + hidden: hidden, + x: Math.round(bounds.x), + y: Math.round(bounds.y), + width: Math.round(bounds.width), + height: Math.round(bounds.height), + isFrame: isFrame + })); + + if (is$1(semantic, 'bpmn:BoundaryEvent')) { + this._attachBoundary(semantic, element); + } + + // insert lanes behind other flow nodes (cf. #727) + if (is$1(semantic, 'bpmn:Lane')) { + parentIndex = 0; + } + + if (is$1(semantic, 'bpmn:DataStoreReference')) { + + // check whether data store is inside our outside of its semantic parent + if (!isPointInsideBBox(parentElement, getMid(bounds))) { + parentElement = this._canvas.findRoot(parentElement); + } + } + + this._canvas.addShape(element, parentElement, parentIndex); + } + + // CONNECTION + else if (is$1(di, 'bpmndi:BPMNEdge')) { + + var source = this._getSource(semantic), + target = this._getTarget(semantic); + + hidden = parentElement && (parentElement.hidden || parentElement.collapsed); + + element = this._elementFactory.createConnection(elementData(semantic, di, { + hidden: hidden, + source: source, + target: target, + waypoints: getWaypoints(di, source, target) + })); + + if (is$1(semantic, 'bpmn:DataAssociation')) { + + // render always on top; this ensures DataAssociations + // are rendered correctly across different "hacks" people + // love to model such as cross participant / sub process + // associations + parentElement = this._canvas.findRoot(parentElement); + } + + this._canvas.addConnection(element, parentElement, parentIndex); + } else { + throw new Error(translate('unknown di {di} for element {semantic}', { + di: elementToString(di), + semantic: elementToString(semantic) + })); + } + + // (optional) LABEL + if (isLabelExternal(semantic) && getLabel(element)) { + this.addLabel(semantic, di, element); + } + + + this._eventBus.fire('bpmnElement.added', { element: element }); + + return element; + }; + + + /** + * Attach the boundary element to the given host + * + * @param {ModdleElement} boundarySemantic + * @param {djs.model.Base} boundaryElement + */ + BpmnImporter.prototype._attachBoundary = function(boundarySemantic, boundaryElement) { + var translate = this._translate; + var hostSemantic = boundarySemantic.attachedToRef; + + if (!hostSemantic) { + throw new Error(translate('missing {semantic}#attachedToRef', { + semantic: elementToString(boundarySemantic) + })); + } + + var host = this._elementRegistry.get(hostSemantic.id), + attachers = host && host.attachers; + + if (!host) { + throw notYetDrawn(translate, boundarySemantic, hostSemantic, 'attachedToRef'); + } + + // wire element.host <> host.attachers + boundaryElement.host = host; + + if (!attachers) { + host.attachers = attachers = []; + } + + if (attachers.indexOf(boundaryElement) === -1) { + attachers.push(boundaryElement); + } + }; + + + /** + * add label for an element + */ + BpmnImporter.prototype.addLabel = function(semantic, di, element) { + var bounds, + text, + label; + + bounds = getExternalLabelBounds(di, element); + + text = getLabel(element); + + if (text) { + + // get corrected bounds from actual layouted text + bounds = this._textRenderer.getExternalLabelBounds(bounds, text); + } + + label = this._elementFactory.createLabel(elementData(semantic, di, { + id: semantic.id + '_label', + labelTarget: element, + type: 'label', + hidden: element.hidden || !getLabel(element), + x: Math.round(bounds.x), + y: Math.round(bounds.y), + width: Math.round(bounds.width), + height: Math.round(bounds.height) + })); + + return this._canvas.addShape(label, element.parent); + }; + + /** + * Return the drawn connection end based on the given side. + * + * @throws {Error} if the end is not yet drawn + */ + BpmnImporter.prototype._getEnd = function(semantic, side) { + + var element, + refSemantic, + type = semantic.$type, + translate = this._translate; + + refSemantic = semantic[side + 'Ref']; + + // handle mysterious isMany DataAssociation#sourceRef + if (side === 'source' && type === 'bpmn:DataInputAssociation') { + refSemantic = refSemantic && refSemantic[0]; + } + + // fix source / target for DataInputAssociation / DataOutputAssociation + if (side === 'source' && type === 'bpmn:DataOutputAssociation' || + side === 'target' && type === 'bpmn:DataInputAssociation') { + + refSemantic = semantic.$parent; + } + + element = refSemantic && this._getElement(refSemantic); + + if (element) { + return element; + } + + if (refSemantic) { + throw notYetDrawn(translate, semantic, refSemantic, side + 'Ref'); + } else { + throw new Error(translate('{semantic}#{side} Ref not specified', { + semantic: elementToString(semantic), + side: side + })); + } + }; + + BpmnImporter.prototype._getSource = function(semantic) { + return this._getEnd(semantic, 'source'); + }; + + BpmnImporter.prototype._getTarget = function(semantic) { + return this._getEnd(semantic, 'target'); + }; + + + BpmnImporter.prototype._getElement = function(semantic) { + return this._elementRegistry.get(semantic.id); + }; + + + // helpers //////////////////// + + function isPointInsideBBox(bbox, point) { + var x = point.x, + y = point.y; + + return x >= bbox.x && + x <= bbox.x + bbox.width && + y >= bbox.y && + y <= bbox.y + bbox.height; + } + + function isFrameElement$1(semantic) { + return is$1(semantic, 'bpmn:Group'); + } + + var ImportModule = { + __depends__: [ + TranslateModule + ], + bpmnImporter: [ 'type', BpmnImporter ] + }; + + var CoreModule$1 = { + __depends__: [ + DrawModule$1, + ImportModule + ] + }; + + function getOriginal(event) { + return event.originalEvent || event.srcEvent; + } + + + function toPoint(event) { + + if (event.pointers && event.pointers.length) { + event = event.pointers[0]; + } + + if (event.touches && event.touches.length) { + event = event.touches[0]; + } + + return event ? { + x: event.clientX, + y: event.clientY + } : null; + } + + function isMac() { + return (/mac/i).test(navigator.platform); + } + + function isButton(event, button) { + return (getOriginal(event) || event).button === button; + } + + function isPrimaryButton(event) { + + // button === 0 -> left áka primary mouse button + return isButton(event, 0); + } + + function isAuxiliaryButton(event) { + + // button === 1 -> auxiliary áka wheel button + return isButton(event, 1); + } + + function hasPrimaryModifier(event) { + var originalEvent = getOriginal(event) || event; + + if (!isPrimaryButton(event)) { + return false; + } + + // Use cmd as primary modifier key for mac OS + if (isMac()) { + return originalEvent.metaKey; + } else { + return originalEvent.ctrlKey; + } + } + + + function hasSecondaryModifier(event) { + var originalEvent = getOriginal(event) || event; + + return isPrimaryButton(event) && originalEvent.shiftKey; + } + + function allowAll(event) { return true; } + + function allowPrimaryAndAuxiliary(event) { + return isPrimaryButton(event) || isAuxiliaryButton(event); + } + + var LOW_PRIORITY$4 = 500; + + + /** + * A plugin that provides interaction events for diagram elements. + * + * It emits the following events: + * + * * element.click + * * element.contextmenu + * * element.dblclick + * * element.hover + * * element.mousedown + * * element.mousemove + * * element.mouseup + * * element.out + * + * Each event is a tuple { element, gfx, originalEvent }. + * + * Canceling the event via Event#preventDefault() + * prevents the original DOM operation. + * + * @param {EventBus} eventBus + */ + function InteractionEvents(eventBus, elementRegistry, styles) { + + var self = this; + + /** + * Fire an interaction event. + * + * @param {string} type local event name, e.g. element.click. + * @param {DOMEvent} event native event + * @param {djs.model.Base} [element] the diagram element to emit the event on; + * defaults to the event target + */ + function fire(type, event, element) { + + if (isIgnored(type, event)) { + return; + } + + var target, gfx, returnValue; + + if (!element) { + target = event.delegateTarget || event.target; + + if (target) { + gfx = target; + element = elementRegistry.get(gfx); + } + } else { + gfx = elementRegistry.getGraphics(element); + } + + if (!gfx || !element) { + return; + } + + returnValue = eventBus.fire(type, { + element: element, + gfx: gfx, + originalEvent: event + }); + + if (returnValue === false) { + event.stopPropagation(); + event.preventDefault(); + } + } + + // TODO(nikku): document this + var handlers = {}; + + function mouseHandler(localEventName) { + return handlers[localEventName]; + } + + function isIgnored(localEventName, event) { + + var filter = ignoredFilters[localEventName] || isPrimaryButton; + + // only react on left mouse button interactions + // except for interaction events that are enabled + // for secundary mouse button + return !filter(event); + } + + var bindings = { + click: 'element.click', + contextmenu: 'element.contextmenu', + dblclick: 'element.dblclick', + mousedown: 'element.mousedown', + mousemove: 'element.mousemove', + mouseover: 'element.hover', + mouseout: 'element.out', + mouseup: 'element.mouseup', + }; + + var ignoredFilters = { + 'element.contextmenu': allowAll, + 'element.mousedown': allowPrimaryAndAuxiliary, + 'element.mouseup': allowPrimaryAndAuxiliary, + 'element.click': allowPrimaryAndAuxiliary, + 'element.dblclick': allowPrimaryAndAuxiliary + }; + + + // manual event trigger ////////// + + /** + * Trigger an interaction event (based on a native dom event) + * on the target shape or connection. + * + * @param {string} eventName the name of the triggered DOM event + * @param {MouseEvent} event + * @param {djs.model.Base} targetElement + */ + function triggerMouseEvent(eventName, event, targetElement) { + + // i.e. element.mousedown... + var localEventName = bindings[eventName]; + + if (!localEventName) { + throw new Error('unmapped DOM event name <' + eventName + '>'); + } + + return fire(localEventName, event, targetElement); + } + + + var ELEMENT_SELECTOR = 'svg, .djs-element'; + + // event handling /////// + + function registerEvent(node, event, localEvent, ignoredFilter) { + + var handler = handlers[localEvent] = function(event) { + fire(localEvent, event); + }; + + if (ignoredFilter) { + ignoredFilters[localEvent] = ignoredFilter; + } + + handler.$delegate = delegate.bind(node, ELEMENT_SELECTOR, event, handler); + } + + function unregisterEvent(node, event, localEvent) { + + var handler = mouseHandler(localEvent); + + if (!handler) { + return; + } + + delegate.unbind(node, event, handler.$delegate); + } + + function registerEvents(svg) { + forEach$1(bindings, function(val, key) { + registerEvent(svg, key, val); + }); + } + + function unregisterEvents(svg) { + forEach$1(bindings, function(val, key) { + unregisterEvent(svg, key, val); + }); + } + + eventBus.on('canvas.destroy', function(event) { + unregisterEvents(event.svg); + }); + + eventBus.on('canvas.init', function(event) { + registerEvents(event.svg); + }); + + + // hit box updating //////////////// + + eventBus.on([ 'shape.added', 'connection.added' ], function(event) { + var element = event.element, + gfx = event.gfx; + + eventBus.fire('interactionEvents.createHit', { element: element, gfx: gfx }); + }); + + // Update djs-hit on change. + // A low priortity is necessary, because djs-hit of labels has to be updated + // after the label bounds have been updated in the renderer. + eventBus.on([ + 'shape.changed', + 'connection.changed' + ], LOW_PRIORITY$4, function(event) { + + var element = event.element, + gfx = event.gfx; + + eventBus.fire('interactionEvents.updateHit', { element: element, gfx: gfx }); + }); + + eventBus.on('interactionEvents.createHit', LOW_PRIORITY$4, function(event) { + var element = event.element, + gfx = event.gfx; + + self.createDefaultHit(element, gfx); + }); + + eventBus.on('interactionEvents.updateHit', function(event) { + var element = event.element, + gfx = event.gfx; + + self.updateDefaultHit(element, gfx); + }); + + + // hit styles //////////// + + var STROKE_HIT_STYLE = createHitStyle('djs-hit djs-hit-stroke'); + + var CLICK_STROKE_HIT_STYLE = createHitStyle('djs-hit djs-hit-click-stroke'); + + var ALL_HIT_STYLE = createHitStyle('djs-hit djs-hit-all'); + + var NO_MOVE_HIT_STYLE = createHitStyle('djs-hit djs-hit-no-move'); + + var HIT_TYPES = { + 'all': ALL_HIT_STYLE, + 'click-stroke': CLICK_STROKE_HIT_STYLE, + 'stroke': STROKE_HIT_STYLE, + 'no-move': NO_MOVE_HIT_STYLE + }; + + function createHitStyle(classNames, attrs) { + + attrs = assign({ + stroke: 'white', + strokeWidth: 15 + }, attrs || {}); + + return styles.cls(classNames, [ 'no-fill', 'no-border' ], attrs); + } + + + // style helpers /////////////// + + function applyStyle(hit, type) { + + var attrs = HIT_TYPES[type]; + + if (!attrs) { + throw new Error('invalid hit type <' + type + '>'); + } + + attr$1(hit, attrs); + + return hit; + } + + function appendHit(gfx, hit) { + append(gfx, hit); + } + + + // API + + /** + * Remove hints on the given graphics. + * + * @param {SVGElement} gfx + */ + this.removeHits = function(gfx) { + var hits = all('.djs-hit', gfx); + + forEach$1(hits, remove$2); + }; + + /** + * Create default hit for the given element. + * + * @param {djs.model.Base} element + * @param {SVGElement} gfx + * + * @return {SVGElement} created hit + */ + this.createDefaultHit = function(element, gfx) { + var waypoints = element.waypoints, + isFrame = element.isFrame, + boxType; + + if (waypoints) { + return this.createWaypointsHit(gfx, waypoints); + } else { + + boxType = isFrame ? 'stroke' : 'all'; + + return this.createBoxHit(gfx, boxType, { + width: element.width, + height: element.height + }); + } + }; + + /** + * Create hits for the given waypoints. + * + * @param {SVGElement} gfx + * @param {Array} waypoints + * + * @return {SVGElement} + */ + this.createWaypointsHit = function(gfx, waypoints) { + + var hit = createLine(waypoints); + + applyStyle(hit, 'stroke'); + + appendHit(gfx, hit); + + return hit; + }; + + /** + * Create hits for a box. + * + * @param {SVGElement} gfx + * @param {string} hitType + * @param {Object} attrs + * + * @return {SVGElement} + */ + this.createBoxHit = function(gfx, type, attrs) { + + attrs = assign({ + x: 0, + y: 0 + }, attrs); + + var hit = create$1('rect'); + + applyStyle(hit, type); + + attr$1(hit, attrs); + + appendHit(gfx, hit); + + return hit; + }; + + /** + * Update default hit of the element. + * + * @param {djs.model.Base} element + * @param {SVGElement} gfx + * + * @return {SVGElement} updated hit + */ + this.updateDefaultHit = function(element, gfx) { + + var hit = query('.djs-hit', gfx); + + if (!hit) { + return; + } + + if (element.waypoints) { + updateLine(hit, element.waypoints); + } else { + attr$1(hit, { + width: element.width, + height: element.height + }); + } + + return hit; + }; + + this.fire = fire; + + this.triggerMouseEvent = triggerMouseEvent; + + this.mouseHandler = mouseHandler; + + this.registerEvent = registerEvent; + this.unregisterEvent = unregisterEvent; + } + + + InteractionEvents.$inject = [ + 'eventBus', + 'elementRegistry', + 'styles' + ]; + + + /** + * An event indicating that the mouse hovered over an element + * + * @event element.hover + * + * @type {Object} + * @property {djs.model.Base} element + * @property {SVGElement} gfx + * @property {Event} originalEvent + */ + + /** + * An event indicating that the mouse has left an element + * + * @event element.out + * + * @type {Object} + * @property {djs.model.Base} element + * @property {SVGElement} gfx + * @property {Event} originalEvent + */ + + /** + * An event indicating that the mouse has clicked an element + * + * @event element.click + * + * @type {Object} + * @property {djs.model.Base} element + * @property {SVGElement} gfx + * @property {Event} originalEvent + */ + + /** + * An event indicating that the mouse has double clicked an element + * + * @event element.dblclick + * + * @type {Object} + * @property {djs.model.Base} element + * @property {SVGElement} gfx + * @property {Event} originalEvent + */ + + /** + * An event indicating that the mouse has gone down on an element. + * + * @event element.mousedown + * + * @type {Object} + * @property {djs.model.Base} element + * @property {SVGElement} gfx + * @property {Event} originalEvent + */ + + /** + * An event indicating that the mouse has gone up on an element. + * + * @event element.mouseup + * + * @type {Object} + * @property {djs.model.Base} element + * @property {SVGElement} gfx + * @property {Event} originalEvent + */ + + /** + * An event indicating that the context menu action is triggered + * via mouse or touch controls. + * + * @event element.contextmenu + * + * @type {Object} + * @property {djs.model.Base} element + * @property {SVGElement} gfx + * @property {Event} originalEvent + */ + + var InteractionEventsModule = { + __init__: [ 'interactionEvents' ], + interactionEvents: [ 'type', InteractionEvents ] + }; + + /** + * Returns the surrounding bbox for all elements in + * the array or the element primitive. + * + * @param {Array|djs.model.Shape} elements + * @param {boolean} [stopRecursion=false] + * + * @return {Bounds} + */ + function getBBox(elements, stopRecursion) { + + stopRecursion = !!stopRecursion; + if (!isArray$2(elements)) { + elements = [ elements ]; + } + + var minX, + minY, + maxX, + maxY; + + forEach$1(elements, function(element) { + + // If element is a connection the bbox must be computed first + var bbox = element; + if (element.waypoints && !stopRecursion) { + bbox = getBBox(element.waypoints, true); + } + + var x = bbox.x, + y = bbox.y, + height = bbox.height || 0, + width = bbox.width || 0; + + if (x < minX || minX === undefined) { + minX = x; + } + if (y < minY || minY === undefined) { + minY = y; + } + + if ((x + width) > maxX || maxX === undefined) { + maxX = x + width; + } + if ((y + height) > maxY || maxY === undefined) { + maxY = y + height; + } + }); + + return { + x: minX, + y: minY, + height: maxY - minY, + width: maxX - minX + }; + } + + + function getType(element) { + + if ('waypoints' in element) { + return 'connection'; + } + + if ('x' in element) { + return 'shape'; + } + + return 'root'; + } + + function isFrameElement(element) { + + return !!(element && element.isFrame); + } + + var LOW_PRIORITY$3 = 500; + + + /** + * @class + * + * A plugin that adds an outline to shapes and connections that may be activated and styled + * via CSS classes. + * + * @param {EventBus} eventBus + * @param {Styles} styles + * @param {ElementRegistry} elementRegistry + */ + function Outline(eventBus, styles, elementRegistry) { + + this.offset = 6; + + var OUTLINE_STYLE = styles.cls('djs-outline', [ 'no-fill' ]); + + var self = this; + + function createOutline(gfx, bounds) { + var outline = create$1('rect'); + + attr$1(outline, assign({ + x: 10, + y: 10, + rx: 3, + width: 100, + height: 100 + }, OUTLINE_STYLE)); + + append(gfx, outline); + + return outline; + } + + // A low priortity is necessary, because outlines of labels have to be updated + // after the label bounds have been updated in the renderer. + eventBus.on([ 'shape.added', 'shape.changed' ], LOW_PRIORITY$3, function(event) { + var element = event.element, + gfx = event.gfx; + + var outline = query('.djs-outline', gfx); + + if (!outline) { + outline = createOutline(gfx); + } + + self.updateShapeOutline(outline, element); + }); + + eventBus.on([ 'connection.added', 'connection.changed' ], function(event) { + var element = event.element, + gfx = event.gfx; + + var outline = query('.djs-outline', gfx); + + if (!outline) { + outline = createOutline(gfx); + } + + self.updateConnectionOutline(outline, element); + }); + } + + + /** + * Updates the outline of a shape respecting the dimension of the + * element and an outline offset. + * + * @param {SVGElement} outline + * @param {djs.model.Base} element + */ + Outline.prototype.updateShapeOutline = function(outline, element) { + + attr$1(outline, { + x: -this.offset, + y: -this.offset, + width: element.width + this.offset * 2, + height: element.height + this.offset * 2 + }); + + }; + + + /** + * Updates the outline of a connection respecting the bounding box of + * the connection and an outline offset. + * + * @param {SVGElement} outline + * @param {djs.model.Base} element + */ + Outline.prototype.updateConnectionOutline = function(outline, connection) { + + var bbox = getBBox(connection); + + attr$1(outline, { + x: bbox.x - this.offset, + y: bbox.y - this.offset, + width: bbox.width + this.offset * 2, + height: bbox.height + this.offset * 2 + }); + + }; + + + Outline.$inject = [ 'eventBus', 'styles', 'elementRegistry' ]; + + var OutlineModule = { + __init__: [ 'outline' ], + outline: [ 'type', Outline ] + }; + + /** + * A service that offers the current selection in a diagram. + * Offers the api to control the selection, too. + * + * @class + * + * @param {EventBus} eventBus the event bus + */ + function Selection(eventBus, canvas) { + + this._eventBus = eventBus; + this._canvas = canvas; + + this._selectedElements = []; + + var self = this; + + eventBus.on([ 'shape.remove', 'connection.remove' ], function(e) { + var element = e.element; + self.deselect(element); + }); + + eventBus.on([ 'diagram.clear', 'root.set' ], function(e) { + self.select(null); + }); + } + + Selection.$inject = [ 'eventBus', 'canvas' ]; + + + Selection.prototype.deselect = function(element) { + var selectedElements = this._selectedElements; + + var idx = selectedElements.indexOf(element); + + if (idx !== -1) { + var oldSelection = selectedElements.slice(); + + selectedElements.splice(idx, 1); + + this._eventBus.fire('selection.changed', { oldSelection: oldSelection, newSelection: selectedElements }); + } + }; + + + Selection.prototype.get = function() { + return this._selectedElements; + }; + + Selection.prototype.isSelected = function(element) { + return this._selectedElements.indexOf(element) !== -1; + }; + + + /** + * This method selects one or more elements on the diagram. + * + * By passing an additional add parameter you can decide whether or not the element(s) + * should be added to the already existing selection or not. + * + * @method Selection#select + * + * @param {Object|Object[]} elements element or array of elements to be selected + * @param {boolean} [add] whether the element(s) should be appended to the current selection, defaults to false + */ + Selection.prototype.select = function(elements, add) { + var selectedElements = this._selectedElements, + oldSelection = selectedElements.slice(); + + if (!isArray$2(elements)) { + elements = elements ? [ elements ] : []; + } + + var canvas = this._canvas; + + var rootElement = canvas.getRootElement(); + + elements = elements.filter(function(element) { + var elementRoot = canvas.findRoot(element); + + return rootElement === elementRoot; + }); + + // selection may be cleared by passing an empty array or null + // to the method + if (add) { + forEach$1(elements, function(element) { + if (selectedElements.indexOf(element) !== -1) { + + // already selected + return; + } else { + selectedElements.push(element); + } + }); + } else { + this._selectedElements = selectedElements = elements.slice(); + } + + this._eventBus.fire('selection.changed', { oldSelection: oldSelection, newSelection: selectedElements }); + }; + + var MARKER_HOVER = 'hover', + MARKER_SELECTED = 'selected'; + + var SELECTION_OUTLINE_PADDING = 6; + + + /** + * A plugin that adds a visible selection UI to shapes and connections + * by appending the hover and selected classes to them. + * + * @class + * + * Makes elements selectable, too. + * + * @param {Canvas} canvas + * @param {EventBus} eventBus + */ + function SelectionVisuals(canvas, eventBus, selection) { + this._canvas = canvas; + + var self = this; + + this._multiSelectionBox = null; + + function addMarker(e, cls) { + canvas.addMarker(e, cls); + } + + function removeMarker(e, cls) { + canvas.removeMarker(e, cls); + } + + eventBus.on('element.hover', function(event) { + addMarker(event.element, MARKER_HOVER); + }); + + eventBus.on('element.out', function(event) { + removeMarker(event.element, MARKER_HOVER); + }); + + eventBus.on('selection.changed', function(event) { + + function deselect(s) { + removeMarker(s, MARKER_SELECTED); + } + + function select(s) { + addMarker(s, MARKER_SELECTED); + } + + var oldSelection = event.oldSelection, + newSelection = event.newSelection; + + forEach$1(oldSelection, function(e) { + if (newSelection.indexOf(e) === -1) { + deselect(e); + } + }); + + forEach$1(newSelection, function(e) { + if (oldSelection.indexOf(e) === -1) { + select(e); + } + }); + + self._updateSelectionOutline(newSelection); + }); + + + eventBus.on('element.changed', function(event) { + if (selection.isSelected(event.element)) { + self._updateSelectionOutline(selection.get()); + } + }); + } + + SelectionVisuals.$inject = [ + 'canvas', + 'eventBus', + 'selection' + ]; + + SelectionVisuals.prototype._updateSelectionOutline = function(selection) { + var layer = this._canvas.getLayer('selectionOutline'); + + clear$1(layer); + + var enabled = selection.length > 1; + + var container = this._canvas.getContainer(); + + classes$1(container)[enabled ? 'add' : 'remove']('djs-multi-select'); + + if (!enabled) { + return; + } + + var bBox = addSelectionOutlinePadding(getBBox(selection)); + + var rect = create$1('rect'); + + attr$1(rect, assign({ + rx: 3 + }, bBox)); + + classes$1(rect).add('djs-selection-outline'); + + append(layer, rect); + }; + + // helpers ////////// + + function addSelectionOutlinePadding(bBox) { + return { + x: bBox.x - SELECTION_OUTLINE_PADDING, + y: bBox.y - SELECTION_OUTLINE_PADDING, + width: bBox.width + SELECTION_OUTLINE_PADDING * 2, + height: bBox.height + SELECTION_OUTLINE_PADDING * 2 + }; + } + + function SelectionBehavior(eventBus, selection, canvas, elementRegistry) { + + // Select elements on create + eventBus.on('create.end', 500, function(event) { + var context = event.context, + canExecute = context.canExecute, + elements = context.elements, + hints = context.hints || {}, + autoSelect = hints.autoSelect; + + if (canExecute) { + if (autoSelect === false) { + + // Select no elements + return; + } + + if (isArray$2(autoSelect)) { + selection.select(autoSelect); + } else { + + // Select all elements by default + selection.select(elements.filter(isShown)); + } + } + }); + + // Select connection targets on connect + eventBus.on('connect.end', 500, function(event) { + var context = event.context, + connection = context.connection; + + if (connection) { + selection.select(connection); + } + }); + + // Select shapes on move + eventBus.on('shape.move.end', 500, function(event) { + var previousSelection = event.previousSelection || []; + + var shape = elementRegistry.get(event.context.shape.id); + + // Always select main shape on move + var isSelected = find(previousSelection, function(selectedShape) { + return shape.id === selectedShape.id; + }); + + if (!isSelected) { + selection.select(shape); + } + }); + + // Select elements on click + eventBus.on('element.click', function(event) { + + if (!isPrimaryButton(event)) { + return; + } + + var element = event.element; + + if (element === canvas.getRootElement()) { + element = null; + } + + var isSelected = selection.isSelected(element), + isMultiSelect = selection.get().length > 1; + + // Add to selection if CTRL or SHIFT pressed + var add = hasPrimaryModifier(event) || hasSecondaryModifier(event); + + if (isSelected && isMultiSelect) { + if (add) { + + // Deselect element + return selection.deselect(element); + } else { + + // Select element only + return selection.select(element); + } + } else if (!isSelected) { + + // Select element + selection.select(element, add); + } else { + + // Deselect element + selection.deselect(element); + } + }); + } + + SelectionBehavior.$inject = [ + 'eventBus', + 'selection', + 'canvas', + 'elementRegistry' + ]; + + + function isShown(element) { + return !element.hidden; + } + + var SelectionModule = { + __init__: [ 'selectionVisuals', 'selectionBehavior' ], + __depends__: [ + InteractionEventsModule, + OutlineModule + ], + selection: [ 'type', Selection ], + selectionVisuals: [ 'type', SelectionVisuals ], + selectionBehavior: [ 'type', SelectionBehavior ] + }; + + /** + * Util that provides unique IDs. + * + * @class djs.util.IdGenerator + * @constructor + * @memberOf djs.util + * + * The ids can be customized via a given prefix and contain a random value to avoid collisions. + * + * @param {string} prefix a prefix to prepend to generated ids (for better readability) + */ + function IdGenerator(prefix) { + + this._counter = 0; + this._prefix = (prefix ? prefix + '-' : '') + Math.floor(Math.random() * 1000000000) + '-'; + } + + /** + * Returns a next unique ID. + * + * @method djs.util.IdGenerator#next + * + * @returns {string} the id + */ + IdGenerator.prototype.next = function() { + return this._prefix + (++this._counter); + }; + + // document wide unique overlay ids + var ids = new IdGenerator('ov'); + + var LOW_PRIORITY$2 = 500; + + + /** + * A service that allows users to attach overlays to diagram elements. + * + * The overlay service will take care of overlay positioning during updates. + * + * @example + * + * // add a pink badge on the top left of the shape + * overlays.add(someShape, { + * position: { + * top: -5, + * left: -5 + * }, + * html: '
      0
      ' + * }); + * + * // or add via shape id + * + * overlays.add('some-element-id', { + * position: { + * top: -5, + * left: -5 + * } + * html: '
      0
      ' + * }); + * + * // or add with optional type + * + * overlays.add(someShape, 'badge', { + * position: { + * top: -5, + * left: -5 + * } + * html: '
      0
      ' + * }); + * + * + * // remove an overlay + * + * var id = overlays.add(...); + * overlays.remove(id); + * + * + * You may configure overlay defaults during tool by providing a `config` module + * with `overlays.defaults` as an entry: + * + * { + * overlays: { + * defaults: { + * show: { + * minZoom: 0.7, + * maxZoom: 5.0 + * }, + * scale: { + * min: 1 + * } + * } + * } + * + * @param {Object} config + * @param {EventBus} eventBus + * @param {Canvas} canvas + * @param {ElementRegistry} elementRegistry + */ + function Overlays(config, eventBus, canvas, elementRegistry) { + + this._eventBus = eventBus; + this._canvas = canvas; + this._elementRegistry = elementRegistry; + + this._ids = ids; + + this._overlayDefaults = assign({ + + // no show constraints + show: null, + + // always scale + scale: true + }, config && config.defaults); + + /** + * Mapping overlayId -> overlay + */ + this._overlays = {}; + + /** + * Mapping elementId -> overlay container + */ + this._overlayContainers = []; + + // root html element for all overlays + this._overlayRoot = createRoot(canvas.getContainer()); + + this._init(); + } + + + Overlays.$inject = [ + 'config.overlays', + 'eventBus', + 'canvas', + 'elementRegistry' + ]; + + + /** + * Returns the overlay with the specified id or a list of overlays + * for an element with a given type. + * + * @example + * + * // return the single overlay with the given id + * overlays.get('some-id'); + * + * // return all overlays for the shape + * overlays.get({ element: someShape }); + * + * // return all overlays on shape with type 'badge' + * overlays.get({ element: someShape, type: 'badge' }); + * + * // shape can also be specified as id + * overlays.get({ element: 'element-id', type: 'badge' }); + * + * + * @param {Object} search + * @param {string} [search.id] + * @param {string|djs.model.Base} [search.element] + * @param {string} [search.type] + * + * @return {Object|Array} the overlay(s) + */ + Overlays.prototype.get = function(search) { + + if (isString(search)) { + search = { id: search }; + } + + if (isString(search.element)) { + search.element = this._elementRegistry.get(search.element); + } + + if (search.element) { + var container = this._getOverlayContainer(search.element, true); + + // return a list of overlays when searching by element (+type) + if (container) { + return search.type ? filter(container.overlays, matchPattern({ type: search.type })) : container.overlays.slice(); + } else { + return []; + } + } else + if (search.type) { + return filter(this._overlays, matchPattern({ type: search.type })); + } else { + + // return single element when searching by id + return search.id ? this._overlays[search.id] : null; + } + }; + + /** + * Adds a HTML overlay to an element. + * + * @param {string|djs.model.Base} element attach overlay to this shape + * @param {string} [type] optional type to assign to the overlay + * @param {Object} overlay the overlay configuration + * + * @param {string|DOMElement} overlay.html html element to use as an overlay + * @param {Object} [overlay.show] show configuration + * @param {number} [overlay.show.minZoom] minimal zoom level to show the overlay + * @param {number} [overlay.show.maxZoom] maximum zoom level to show the overlay + * @param {Object} overlay.position where to attach the overlay + * @param {number} [overlay.position.left] relative to element bbox left attachment + * @param {number} [overlay.position.top] relative to element bbox top attachment + * @param {number} [overlay.position.bottom] relative to element bbox bottom attachment + * @param {number} [overlay.position.right] relative to element bbox right attachment + * @param {boolean|Object} [overlay.scale=true] false to preserve the same size regardless of + * diagram zoom + * @param {number} [overlay.scale.min] + * @param {number} [overlay.scale.max] + * + * @return {string} id that may be used to reference the overlay for update or removal + */ + Overlays.prototype.add = function(element, type, overlay) { + + if (isObject(type)) { + overlay = type; + type = null; + } + + if (!element.id) { + element = this._elementRegistry.get(element); + } + + if (!overlay.position) { + throw new Error('must specifiy overlay position'); + } + + if (!overlay.html) { + throw new Error('must specifiy overlay html'); + } + + if (!element) { + throw new Error('invalid element specified'); + } + + var id = this._ids.next(); + + overlay = assign({}, this._overlayDefaults, overlay, { + id: id, + type: type, + element: element, + html: overlay.html + }); + + this._addOverlay(overlay); + + return id; + }; + + + /** + * Remove an overlay with the given id or all overlays matching the given filter. + * + * @see Overlays#get for filter options. + * + * @param {string|object} [filter] + */ + Overlays.prototype.remove = function(filter) { + + var overlays = this.get(filter) || []; + + if (!isArray$2(overlays)) { + overlays = [ overlays ]; + } + + var self = this; + + forEach$1(overlays, function(overlay) { + + var container = self._getOverlayContainer(overlay.element, true); + + if (overlay) { + remove$1(overlay.html); + remove$1(overlay.htmlContainer); + + delete overlay.htmlContainer; + delete overlay.element; + + delete self._overlays[overlay.id]; + } + + if (container) { + var idx = container.overlays.indexOf(overlay); + if (idx !== -1) { + container.overlays.splice(idx, 1); + } + } + }); + + }; + + + Overlays.prototype.show = function() { + setVisible(this._overlayRoot); + }; + + + Overlays.prototype.hide = function() { + setVisible(this._overlayRoot, false); + }; + + Overlays.prototype.clear = function() { + this._overlays = {}; + + this._overlayContainers = []; + + clear(this._overlayRoot); + }; + + Overlays.prototype._updateOverlayContainer = function(container) { + var element = container.element, + html = container.html; + + // update container left,top according to the elements x,y coordinates + // this ensures we can attach child elements relative to this container + + var x = element.x, + y = element.y; + + if (element.waypoints) { + var bbox = getBBox(element); + x = bbox.x; + y = bbox.y; + } + + setPosition(html, x, y); + + attr(container.html, 'data-container-id', element.id); + }; + + + Overlays.prototype._updateOverlay = function(overlay) { + + var position = overlay.position, + htmlContainer = overlay.htmlContainer, + element = overlay.element; + + // update overlay html relative to shape because + // it is already positioned on the element + + // update relative + var left = position.left, + top = position.top; + + if (position.right !== undefined) { + + var width; + + if (element.waypoints) { + width = getBBox(element).width; + } else { + width = element.width; + } + + left = position.right * -1 + width; + } + + if (position.bottom !== undefined) { + + var height; + + if (element.waypoints) { + height = getBBox(element).height; + } else { + height = element.height; + } + + top = position.bottom * -1 + height; + } + + setPosition(htmlContainer, left || 0, top || 0); + this._updateOverlayVisibilty(overlay, this._canvas.viewbox()); + }; + + + Overlays.prototype._createOverlayContainer = function(element) { + var html = domify('
      '); + assign$1(html, { position: 'absolute' }); + + this._overlayRoot.appendChild(html); + + var container = { + html: html, + element: element, + overlays: [] + }; + + this._updateOverlayContainer(container); + + this._overlayContainers.push(container); + + return container; + }; + + + Overlays.prototype._updateRoot = function(viewbox) { + var scale = viewbox.scale || 1; + + var matrix = 'matrix(' + + [ + scale, + 0, + 0, + scale, + -1 * viewbox.x * scale, + -1 * viewbox.y * scale + ].join(',') + + ')'; + + setTransform(this._overlayRoot, matrix); + }; + + + Overlays.prototype._getOverlayContainer = function(element, raw) { + var container = find(this._overlayContainers, function(c) { + return c.element === element; + }); + + + if (!container && !raw) { + return this._createOverlayContainer(element); + } + + return container; + }; + + + Overlays.prototype._addOverlay = function(overlay) { + + var id = overlay.id, + element = overlay.element, + html = overlay.html, + htmlContainer, + overlayContainer; + + // unwrap jquery (for those who need it) + if (html.get && html.constructor.prototype.jquery) { + html = html.get(0); + } + + // create proper html elements from + // overlay HTML strings + if (isString(html)) { + html = domify(html); + } + + overlayContainer = this._getOverlayContainer(element); + + htmlContainer = domify('
      '); + assign$1(htmlContainer, { position: 'absolute' }); + + htmlContainer.appendChild(html); + + if (overlay.type) { + classes(htmlContainer).add('djs-overlay-' + overlay.type); + } + + var elementRoot = this._canvas.findRoot(element); + var activeRoot = this._canvas.getRootElement(); + + setVisible(htmlContainer, elementRoot === activeRoot); + + overlay.htmlContainer = htmlContainer; + + overlayContainer.overlays.push(overlay); + overlayContainer.html.appendChild(htmlContainer); + + this._overlays[id] = overlay; + + this._updateOverlay(overlay); + this._updateOverlayVisibilty(overlay, this._canvas.viewbox()); + }; + + + Overlays.prototype._updateOverlayVisibilty = function(overlay, viewbox) { + var show = overlay.show, + rootElement = this._canvas.findRoot(overlay.element), + minZoom = show && show.minZoom, + maxZoom = show && show.maxZoom, + htmlContainer = overlay.htmlContainer, + activeRootElement = this._canvas.getRootElement(), + visible = true; + + if (rootElement !== activeRootElement) { + visible = false; + } else if (show) { + if ( + (isDefined(minZoom) && minZoom > viewbox.scale) || + (isDefined(maxZoom) && maxZoom < viewbox.scale) + ) { + visible = false; + } + } + + setVisible(htmlContainer, visible); + + this._updateOverlayScale(overlay, viewbox); + }; + + + Overlays.prototype._updateOverlayScale = function(overlay, viewbox) { + var shouldScale = overlay.scale, + minScale, + maxScale, + htmlContainer = overlay.htmlContainer; + + var scale, transform = ''; + + if (shouldScale !== true) { + + if (shouldScale === false) { + minScale = 1; + maxScale = 1; + } else { + minScale = shouldScale.min; + maxScale = shouldScale.max; + } + + if (isDefined(minScale) && viewbox.scale < minScale) { + scale = (1 / viewbox.scale || 1) * minScale; + } + + if (isDefined(maxScale) && viewbox.scale > maxScale) { + scale = (1 / viewbox.scale || 1) * maxScale; + } + } + + if (isDefined(scale)) { + transform = 'scale(' + scale + ',' + scale + ')'; + } + + setTransform(htmlContainer, transform); + }; + + + Overlays.prototype._updateOverlaysVisibilty = function(viewbox) { + + var self = this; + + forEach$1(this._overlays, function(overlay) { + self._updateOverlayVisibilty(overlay, viewbox); + }); + }; + + + Overlays.prototype._init = function() { + + var eventBus = this._eventBus; + + var self = this; + + + // scroll/zoom integration + + function updateViewbox(viewbox) { + self._updateRoot(viewbox); + self._updateOverlaysVisibilty(viewbox); + + self.show(); + } + + eventBus.on('canvas.viewbox.changing', function(event) { + self.hide(); + }); + + eventBus.on('canvas.viewbox.changed', function(event) { + updateViewbox(event.viewbox); + }); + + + // remove integration + + eventBus.on([ 'shape.remove', 'connection.remove' ], function(e) { + var element = e.element; + var overlays = self.get({ element: element }); + + forEach$1(overlays, function(o) { + self.remove(o.id); + }); + + var container = self._getOverlayContainer(element); + + if (container) { + remove$1(container.html); + var i = self._overlayContainers.indexOf(container); + if (i !== -1) { + self._overlayContainers.splice(i, 1); + } + } + }); + + + // move integration + + eventBus.on('element.changed', LOW_PRIORITY$2, function(e) { + var element = e.element; + + var container = self._getOverlayContainer(element, true); + + if (container) { + forEach$1(container.overlays, function(overlay) { + self._updateOverlay(overlay); + }); + + self._updateOverlayContainer(container); + } + }); + + + // marker integration, simply add them on the overlays as classes, too. + + eventBus.on('element.marker.update', function(e) { + var container = self._getOverlayContainer(e.element, true); + if (container) { + classes(container.html)[e.add ? 'add' : 'remove'](e.marker); + } + }); + + + eventBus.on('root.set', function() { + self._updateOverlaysVisibilty(self._canvas.viewbox()); + }); + + // clear overlays with diagram + + eventBus.on('diagram.clear', this.clear, this); + }; + + + + // helpers ///////////////////////////// + + function createRoot(parentNode) { + var root = domify( + '
      ' + ); + + assign$1(root, { + position: 'absolute', + width: 0, + height: 0 + }); + + parentNode.insertBefore(root, parentNode.firstChild); + + return root; + } + + function setPosition(el, x, y) { + assign$1(el, { left: x + 'px', top: y + 'px' }); + } + + /** + * Set element visible + * + * @param {DOMElement} el + * @param {boolean} [visible=true] + */ + function setVisible(el, visible) { + el.style.display = visible === false ? 'none' : ''; + } + + function setTransform(el, transform) { + + el.style['transform-origin'] = 'top left'; + + [ '', '-ms-', '-webkit-' ].forEach(function(prefix) { + el.style[prefix + 'transform'] = transform; + }); + } + + var OverlaysModule = { + __init__: [ 'overlays' ], + overlays: [ 'type', Overlays ] + }; + + /** + * Adds change support to the diagram, including + * + *
        + *
      • redrawing shapes and connections on change
      • + *
      + * + * @param {EventBus} eventBus + * @param {Canvas} canvas + * @param {ElementRegistry} elementRegistry + * @param {GraphicsFactory} graphicsFactory + */ + function ChangeSupport( + eventBus, canvas, elementRegistry, + graphicsFactory) { + + + // redraw shapes / connections on change + + eventBus.on('element.changed', function(event) { + + var element = event.element; + + // element might have been deleted and replaced by new element with same ID + // thus check for parent of element except for root element + if (element.parent || element === canvas.getRootElement()) { + event.gfx = elementRegistry.getGraphics(element); + } + + // shape + gfx may have been deleted + if (!event.gfx) { + return; + } + + eventBus.fire(getType(element) + '.changed', event); + }); + + eventBus.on('elements.changed', function(event) { + + var elements = event.elements; + + elements.forEach(function(e) { + eventBus.fire('element.changed', { element: e }); + }); + + graphicsFactory.updateContainments(elements); + }); + + eventBus.on('shape.changed', function(event) { + graphicsFactory.update('shape', event.element, event.gfx); + }); + + eventBus.on('connection.changed', function(event) { + graphicsFactory.update('connection', event.element, event.gfx); + }); + } + + ChangeSupport.$inject = [ + 'eventBus', + 'canvas', + 'elementRegistry', + 'graphicsFactory' + ]; + + var ChangeSupportModule = { + __init__: [ 'changeSupport' ], + changeSupport: [ 'type', ChangeSupport ] + }; + + var DEFAULT_PRIORITY$2 = 1000; + + /** + * A utility that can be used to plug-in into the command execution for + * extension and/or validation. + * + * @param {EventBus} eventBus + * + * @example + * + * import inherits from 'inherits-browser'; + * + * import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + * + * function CommandLogger(eventBus) { + * CommandInterceptor.call(this, eventBus); + * + * this.preExecute(function(event) { + * console.log('command pre-execute', event); + * }); + * } + * + * inherits(CommandLogger, CommandInterceptor); + * + */ + function CommandInterceptor(eventBus) { + this._eventBus = eventBus; + } + + CommandInterceptor.$inject = [ 'eventBus' ]; + + function unwrapEvent(fn, that) { + return function(event) { + return fn.call(that || null, event.context, event.command, event); + }; + } + + /** + * Register an interceptor for a command execution + * + * @param {string|Array} [events] list of commands to register on + * @param {string} [hook] command hook, i.e. preExecute, executed to listen on + * @param {number} [priority] the priority on which to hook into the execution + * @param {Function} handlerFn interceptor to be invoked with (event) + * @param {boolean} unwrap if true, unwrap the event and pass (context, command, event) to the + * listener instead + * @param {Object} [that] Pass context (`this`) to the handler function + */ + CommandInterceptor.prototype.on = function(events, hook, priority, handlerFn, unwrap, that) { + + if (isFunction(hook) || isNumber(hook)) { + that = unwrap; + unwrap = handlerFn; + handlerFn = priority; + priority = hook; + hook = null; + } + + if (isFunction(priority)) { + that = unwrap; + unwrap = handlerFn; + handlerFn = priority; + priority = DEFAULT_PRIORITY$2; + } + + if (isObject(unwrap)) { + that = unwrap; + unwrap = false; + } + + if (!isFunction(handlerFn)) { + throw new Error('handlerFn must be a function'); + } + + if (!isArray$2(events)) { + events = [ events ]; + } + + var eventBus = this._eventBus; + + forEach$1(events, function(event) { + + // concat commandStack(.event)?(.hook)? + var fullEvent = [ 'commandStack', event, hook ].filter(function(e) { return e; }).join('.'); + + eventBus.on(fullEvent, priority, unwrap ? unwrapEvent(handlerFn, that) : handlerFn, that); + }); + }; + + + var hooks = [ + 'canExecute', + 'preExecute', + 'preExecuted', + 'execute', + 'executed', + 'postExecute', + 'postExecuted', + 'revert', + 'reverted' + ]; + + /* + * Install hook shortcuts + * + * This will generate the CommandInterceptor#(preExecute|...|reverted) methods + * which will in term forward to CommandInterceptor#on. + */ + forEach$1(hooks, function(hook) { + + /** + * {canExecute|preExecute|preExecuted|execute|executed|postExecute|postExecuted|revert|reverted} + * + * A named hook for plugging into the command execution + * + * @param {string|Array} [events] list of commands to register on + * @param {number} [priority] the priority on which to hook into the execution + * @param {Function} handlerFn interceptor to be invoked with (event) + * @param {boolean} [unwrap=false] if true, unwrap the event and pass (context, command, event) to the + * listener instead + * @param {Object} [that] Pass context (`this`) to the handler function + */ + CommandInterceptor.prototype[hook] = function(events, priority, handlerFn, unwrap, that) { + + if (isFunction(events) || isNumber(events)) { + that = unwrap; + unwrap = handlerFn; + handlerFn = priority; + priority = events; + events = null; + } + + this.on(events, hook, priority, handlerFn, unwrap, that); + }; + }); + + /** + * A modeling behavior that ensures we set the correct root element + * as we undo and redo commands. + * + * @param {Canvas} canvas + * @param {didi.Injector} injector + */ + function RootElementsBehavior(canvas, injector) { + + injector.invoke(CommandInterceptor, this); + + this.executed(function(event) { + var context = event.context; + + if (context.rootElement) { + canvas.setRootElement(context.rootElement); + } else { + context.rootElement = canvas.getRootElement(); + } + }); + + this.revert(function(event) { + var context = event.context; + + if (context.rootElement) { + canvas.setRootElement(context.rootElement); + } + }); + } + + e(RootElementsBehavior, CommandInterceptor); + + RootElementsBehavior.$inject = [ 'canvas', 'injector' ]; + + var RootElementsModule = { + __init__: [ 'rootElementsBehavior' ], + rootElementsBehavior: [ 'type', RootElementsBehavior ] + }; + + var css_escape = {exports: {}}; + + /*! https://mths.be/cssescape v1.5.1 by @mathias | MIT license */ + + (function (module, exports) { + (function(root, factory) { + // https://github.com/umdjs/umd/blob/master/returnExports.js + { + // For Node.js. + module.exports = factory(root); + } + }(typeof commonjsGlobal != 'undefined' ? commonjsGlobal : commonjsGlobal, function(root) { + + if (root.CSS && root.CSS.escape) { + return root.CSS.escape; + } + + // https://drafts.csswg.org/cssom/#serialize-an-identifier + var cssEscape = function(value) { + if (arguments.length == 0) { + throw new TypeError('`CSS.escape` requires an argument.'); + } + var string = String(value); + var length = string.length; + var index = -1; + var codeUnit; + var result = ''; + var firstCodeUnit = string.charCodeAt(0); + while (++index < length) { + codeUnit = string.charCodeAt(index); + // Note: there’s no need to special-case astral symbols, surrogate + // pairs, or lone surrogates. + + // If the character is NULL (U+0000), then the REPLACEMENT CHARACTER + // (U+FFFD). + if (codeUnit == 0x0000) { + result += '\uFFFD'; + continue; + } + + if ( + // If the character is in the range [\1-\1F] (U+0001 to U+001F) or is + // U+007F, […] + (codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F || + // If the character is the first character and is in the range [0-9] + // (U+0030 to U+0039), […] + (index == 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) || + // If the character is the second character and is in the range [0-9] + // (U+0030 to U+0039) and the first character is a `-` (U+002D), […] + ( + index == 1 && + codeUnit >= 0x0030 && codeUnit <= 0x0039 && + firstCodeUnit == 0x002D + ) + ) { + // https://drafts.csswg.org/cssom/#escape-a-character-as-code-point + result += '\\' + codeUnit.toString(16) + ' '; + continue; + } + + if ( + // If the character is the first character and is a `-` (U+002D), and + // there is no second character, […] + index == 0 && + length == 1 && + codeUnit == 0x002D + ) { + result += '\\' + string.charAt(index); + continue; + } + + // If the character is not handled by one of the above rules and is + // greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or + // is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to + // U+005A), or [a-z] (U+0061 to U+007A), […] + if ( + codeUnit >= 0x0080 || + codeUnit == 0x002D || + codeUnit == 0x005F || + codeUnit >= 0x0030 && codeUnit <= 0x0039 || + codeUnit >= 0x0041 && codeUnit <= 0x005A || + codeUnit >= 0x0061 && codeUnit <= 0x007A + ) { + // the character itself + result += string.charAt(index); + continue; + } + + // Otherwise, the escaped character. + // https://drafts.csswg.org/cssom/#escape-a-character + result += '\\' + string.charAt(index); + + } + return result; + }; + + if (!root.CSS) { + root.CSS = {}; + } + + root.CSS.escape = cssEscape; + return cssEscape; + + })); + } (css_escape)); + + var HTML_ESCAPE_MAP = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + '\'': ''' + }; + + function escapeHTML(str) { + str = '' + str; + + return str && str.replace(/[&<>"']/g, function(match) { + return HTML_ESCAPE_MAP[match]; + }); + } + + var planeSuffix = '_plane'; + + /** + * Get plane ID for a primary shape. + * + * @param {djs.model.Base|ModdleElement} element + * + * @returns {String} + */ + function getPlaneIdFromShape(element) { + var id = element.id; + + if (is$1(element, 'bpmn:SubProcess')) { + return addPlaneSuffix(id); + } + + return id; + } + + function addPlaneSuffix(id) { + return id + planeSuffix; + } + + var OPEN_CLASS = 'bjs-breadcrumbs-shown'; + + + /** + * Adds overlays that allow switching planes on collapsed subprocesses. + * + * @param {eventBus} eventBus + * @param {elementRegistry} elementRegistry + * @param {overlays} overlays + * @param {canvas} canvas + */ + function DrilldownBreadcrumbs(eventBus, elementRegistry, overlays, canvas) { + var breadcrumbs = domify('
        '); + var container = canvas.getContainer(); + var containerClasses = classes(container); + container.appendChild(breadcrumbs); + + var boParents = []; + + // update breadcrumbs if name or ID of the primary shape changes + eventBus.on('element.changed', function(e) { + var shape = e.element, + bo = getBusinessObject(shape); + + var isPresent = find(boParents, function(el) { + return el === bo; + }); + + if (!isPresent) { + return; + } + + updateBreadcrumbs(); + }); + + /** + * Updates the displayed breadcrumbs. If no element is provided, only the + * labels are updated. + * + * @param {djs.model.Base} [element] + */ + function updateBreadcrumbs(element) { + if (element) { + boParents = getBoParentChain(element); + } + + var path = boParents.map(function(parent) { + var title = escapeHTML(parent.name || parent.id); + var link = domify('
      • ' + title + '
      • '); + + var parentPlane = canvas.findRoot(getPlaneIdFromShape(parent)) || canvas.findRoot(parent.id); + + // when the root is a collaboration, the process does not have a corresponding + // element in the elementRegisty. Instead, we search for the corresponding participant + if (!parentPlane && is$1(parent, 'bpmn:Process')) { + var participant = elementRegistry.find(function(element) { + var bo = getBusinessObject(element); + return bo && bo.processRef && bo.processRef === parent; + }); + + parentPlane = canvas.findRoot(participant.id); + } + + link.addEventListener('click', function() { + canvas.setRootElement(parentPlane); + }); + + return link; + }); + + breadcrumbs.innerHTML = ''; + + // show breadcrumbs and expose state to .djs-container + var visible = path.length > 1; + containerClasses.toggle(OPEN_CLASS, visible); + + path.forEach(function(el) { + breadcrumbs.appendChild(el); + }); + } + + eventBus.on('root.set', function(event) { + updateBreadcrumbs(event.element); + }); + + } + + DrilldownBreadcrumbs.$inject = [ 'eventBus', 'elementRegistry', 'overlays', 'canvas' ]; + + + // helpers ////////// + + /** + * Returns the parents for the element using the business object chain, + * starting with the root element. + * + * @param {djs.model.Shape} child + * + * @returns {Array} parents + */ + function getBoParentChain(child) { + var bo = getBusinessObject(child); + + var parents = []; + + for (var element = bo; element; element = element.$parent) { + if (is$1(element, 'bpmn:SubProcess') || is$1(element, 'bpmn:Process')) { + parents.push(element); + } + } + + return parents.reverse(); + } + + /** + * Move collapsed subprocesses into view when drilling down. + * + * Zoom and scroll are saved in a session. + * + * @param {eventBus} eventBus + * @param {canvas} canvas + */ + function DrilldownCentering(eventBus, canvas) { + + var currentRoot = null; + var positionMap = new Map(); + + eventBus.on('root.set', function(event) { + var newRoot = event.element; + var currentViewbox = canvas.viewbox(); + var storedViewbox = positionMap.get(newRoot); + + positionMap.set(currentRoot, { + x: currentViewbox.x, + y: currentViewbox.y, + zoom: currentViewbox.scale + }); + + currentRoot = newRoot; + + // current root was replaced with a collaboration, we don't update the viewbox + if (is$1(newRoot, 'bpmn:Collaboration') && !storedViewbox) { + return; + } + + storedViewbox = storedViewbox || { x: 0, y: 0, zoom: 1 }; + + var dx = (currentViewbox.x - storedViewbox.x) * currentViewbox.scale, + dy = (currentViewbox.y - storedViewbox.y) * currentViewbox.scale; + + if (dx !== 0 || dy !== 0) { + canvas.scroll({ + dx: dx, + dy: dy + }); + } + + if (storedViewbox.zoom !== currentViewbox.scale) { + canvas.zoom(storedViewbox.zoom, { x: 0, y: 0 }); + } + }); + + eventBus.on('diagram.clear', function() { + positionMap.clear(); + currentRoot = null; + }); + + } + + DrilldownCentering.$inject = [ 'eventBus', 'canvas' ]; + + + /** + * ES5 Map implementation. Works. + */ + function Map() { + + this._entries = []; + + this.set = function(key, value) { + + var found = false; + + for (var k in this._entries) { + if (this._entries[k][0] === key) { + this._entries[k][1] = value; + + found = true; + + break; + } + } + + if (!found) { + this._entries.push([ key, value ]); + } + }; + + this.get = function(key) { + + for (var k in this._entries) { + if (this._entries[k][0] === key) { + return this._entries[k][1]; + } + } + + return null; + }; + + this.clear = function() { + this._entries.length = 0; + }; + + this.remove = function(key) { + + var idx = -1; + + for (var k in this._entries) { + if (this._entries[k][0] === key) { + idx = k; + + break; + } + } + + if (idx !== -1) { + this._entries.splice(idx, 1); + } + }; + } + + var DEFAULT_POSITION = { + x: 180, + y: 160 + }; + + /** + * Hook into `import.render.start` and create new planes for diagrams with + * collapsed subprocesses and all dis on the same plane. + * + * @param {eventBus} eventBus + * @param {moddle} moddle + */ + function SubprocessCompatibility(eventBus, moddle) { + this._eventBus = eventBus; + this._moddle = moddle; + + var self = this; + + eventBus.on('import.render.start', 1500, function(e, context) { + self.handleImport(context.definitions); + }); + } + + SubprocessCompatibility.prototype.handleImport = function(definitions) { + if (!definitions.diagrams) { + return; + } + + var self = this; + this._definitions = definitions; + this._processToDiagramMap = {}; + + definitions.diagrams.forEach(function(diagram) { + if (!diagram.plane || !diagram.plane.bpmnElement) { + return; + } + + self._processToDiagramMap[diagram.plane.bpmnElement.id] = diagram; + }); + + var newDiagrams = []; + definitions.diagrams.forEach(function(diagram) { + var createdDiagrams = self.createNewDiagrams(diagram.plane); + Array.prototype.push.apply(newDiagrams, createdDiagrams); + }); + + newDiagrams.forEach(function(diagram) { + self.movePlaneElementsToOrigin(diagram.plane); + }); + }; + + + /** + * Moves all DI elements from collapsed subprocesses to a new plane. + * + * @param {Object} plane + * @return {Array} new diagrams created for the collapsed subprocesses + */ + SubprocessCompatibility.prototype.createNewDiagrams = function(plane) { + var self = this; + + var collapsedElements = []; + var elementsToMove = []; + + plane.get('planeElement').forEach(function(diElement) { + var bo = diElement.bpmnElement; + + if (!bo) { + return; + } + + var parent = bo.$parent; + + if (is$1(bo, 'bpmn:SubProcess') && !diElement.isExpanded) { + collapsedElements.push(bo); + } + + if (shouldMoveToPlane(bo, plane)) { + + // don't change the array while we iterate over it + elementsToMove.push({ diElement: diElement, parent: parent }); + } + }); + + var newDiagrams = []; + + // create new planes for all collapsed subprocesses, even when they are empty + collapsedElements.forEach(function(element) { + if (!self._processToDiagramMap[element.id]) { + var diagram = self.createDiagram(element); + self._processToDiagramMap[element.id] = diagram; + newDiagrams.push(diagram); + } + }); + + elementsToMove.forEach(function(element) { + var diElement = element.diElement; + var parent = element.parent; + + // parent is expanded, get nearest collapsed parent + while (parent && collapsedElements.indexOf(parent) === -1) { + parent = parent.$parent; + } + + // false positive, all parents are expanded + if (!parent) { + return; + } + + var diagram = self._processToDiagramMap[parent.id]; + self.moveToDiPlane(diElement, diagram.plane); + }); + + return newDiagrams; + }; + + SubprocessCompatibility.prototype.movePlaneElementsToOrigin = function(plane) { + var elements = plane.get('planeElement'); + + // get bounding box of all elements + var planeBounds = getPlaneBounds(plane); + + var offset = { + x: planeBounds.x - DEFAULT_POSITION.x, + y: planeBounds.y - DEFAULT_POSITION.y + }; + + elements.forEach(function(diElement) { + if (diElement.waypoint) { + diElement.waypoint.forEach(function(waypoint) { + waypoint.x = waypoint.x - offset.x; + waypoint.y = waypoint.y - offset.y; + }); + } else if (diElement.bounds) { + diElement.bounds.x = diElement.bounds.x - offset.x; + diElement.bounds.y = diElement.bounds.y - offset.y; + } + }); + }; + + + SubprocessCompatibility.prototype.moveToDiPlane = function(diElement, newPlane) { + var containingDiagram = findRootDiagram(diElement); + + // remove DI from old Plane and add it to the new one + var parentPlaneElement = containingDiagram.plane.get('planeElement'); + parentPlaneElement.splice(parentPlaneElement.indexOf(diElement), 1); + newPlane.get('planeElement').push(diElement); + }; + + + SubprocessCompatibility.prototype.createDiagram = function(bo) { + var plane = this._moddle.create('bpmndi:BPMNPlane', { bpmnElement: bo }); + var diagram = this._moddle.create('bpmndi:BPMNDiagram', { + plane: plane + }); + plane.$parent = diagram; + plane.bpmnElement = bo; + diagram.$parent = this._definitions; + this._definitions.diagrams.push(diagram); + return diagram; + }; + + SubprocessCompatibility.$inject = [ 'eventBus', 'moddle' ]; + + + // helpers ////////////////////////// + + function findRootDiagram(element) { + if (is$1(element, 'bpmndi:BPMNDiagram')) { + return element; + } else { + return findRootDiagram(element.$parent); + } + } + + function getPlaneBounds(plane) { + var planeTrbl = { + top: Infinity, + right: -Infinity, + bottom: -Infinity, + left: Infinity + }; + + plane.planeElement.forEach(function(element) { + if (!element.bounds) { + return; + } + + var trbl = asTRBL(element.bounds); + + planeTrbl.top = Math.min(trbl.top, planeTrbl.top); + planeTrbl.left = Math.min(trbl.left, planeTrbl.left); + }); + + return asBounds(planeTrbl); + } + + function shouldMoveToPlane(bo, plane) { + var parent = bo.$parent; + + // don't move elements that are already on the plane + if (!is$1(parent, 'bpmn:SubProcess') || parent === plane.bpmnElement) { + return false; + } + + // dataAssociations are children of the subprocess but rendered on process level + // cf. https://github.com/bpmn-io/bpmn-js/issues/1619 + if (isAny(bo, [ 'bpmn:DataInputAssociation', 'bpmn:DataOutputAssociation' ])) { + return false; + } + + return true; + } + + var LOW_PRIORITY$1 = 250; + var ARROW_DOWN_SVG = ''; + + var EMPTY_MARKER = 'bjs-drilldown-empty'; + + function DrilldownOverlayBehavior( + canvas, eventBus, elementRegistry, overlays + ) { + CommandInterceptor.call(this, eventBus); + + this._canvas = canvas; + this._eventBus = eventBus; + this._elementRegistry = elementRegistry; + this._overlays = overlays; + + var self = this; + + this.executed('shape.toggleCollapse', LOW_PRIORITY$1, function(context) { + var shape = context.shape; + + // Add overlay to the collapsed shape + if (self.canDrillDown(shape)) { + self.addOverlay(shape); + } else { + self.removeOverlay(shape); + } + }, true); + + + this.reverted('shape.toggleCollapse', LOW_PRIORITY$1, function(context) { + var shape = context.shape; + + // Add overlay to the collapsed shape + if (self.canDrillDown(shape)) { + self.addOverlay(shape); + } else { + self.removeOverlay(shape); + } + }, true); + + + this.executed([ 'shape.create', 'shape.move', 'shape.delete' ], LOW_PRIORITY$1, + function(context) { + var oldParent = context.oldParent, + newParent = context.newParent || context.parent, + shape = context.shape; + + // Add overlay to the collapsed shape + if (self.canDrillDown(shape)) { + self.addOverlay(shape); + } + + self.updateDrilldownOverlay(oldParent); + self.updateDrilldownOverlay(newParent); + self.updateDrilldownOverlay(shape); + }, true); + + + this.reverted([ 'shape.create', 'shape.move', 'shape.delete' ], LOW_PRIORITY$1, + function(context) { + var oldParent = context.oldParent, + newParent = context.newParent || context.parent, + shape = context.shape; + + // Add overlay to the collapsed shape + if (self.canDrillDown(shape)) { + self.addOverlay(shape); + } + + self.updateDrilldownOverlay(oldParent); + self.updateDrilldownOverlay(newParent); + self.updateDrilldownOverlay(shape); + }, true); + + + eventBus.on('import.render.complete', function() { + elementRegistry.filter(function(e) { + return self.canDrillDown(e); + }).map(function(el) { + self.addOverlay(el); + }); + }); + + } + + e(DrilldownOverlayBehavior, CommandInterceptor); + + DrilldownOverlayBehavior.prototype.updateDrilldownOverlay = function(shape) { + var canvas = this._canvas; + + if (!shape) { + return; + } + + var root = canvas.findRoot(shape); + if (root) { + this.updateOverlayVisibility(root); + } + }; + + + DrilldownOverlayBehavior.prototype.canDrillDown = function(element) { + var canvas = this._canvas; + return is$1(element, 'bpmn:SubProcess') && canvas.findRoot(getPlaneIdFromShape(element)); + }; + + /** + * Updates visibility of the drilldown overlay. If the plane has no elements, + * the drilldown will be only shown when the element is selected. + * + * @param {djs.model.Shape|djs.model.Root} element collapsed shape or root element + */ + DrilldownOverlayBehavior.prototype.updateOverlayVisibility = function(element) { + var overlays = this._overlays; + + var bo = element.businessObject; + + var overlay = overlays.get({ element: bo.id, type: 'drilldown' })[0]; + + if (!overlay) { + return; + } + + var hasContent = bo && bo.flowElements && bo.flowElements.length; + classes(overlay.html).toggle(EMPTY_MARKER, !hasContent); + }; + + /** + * Attaches a drilldown button to the given element. We assume that the plane has + * the same id as the element. + * + * @param {djs.model.Shape} element collapsed shape + */ + DrilldownOverlayBehavior.prototype.addOverlay = function(element) { + var canvas = this._canvas; + var overlays = this._overlays; + + var existingOverlays = overlays.get({ element: element, type: 'drilldown' }); + if (existingOverlays.length) { + this.removeOverlay(element); + } + + var button = domify(''); + + button.addEventListener('click', function() { + canvas.setRootElement(canvas.findRoot(getPlaneIdFromShape(element))); + }); + + overlays.add(element, 'drilldown', { + position: { + bottom: -7, + right: -8 + }, + html: button + }); + + this.updateOverlayVisibility(element); + }; + + DrilldownOverlayBehavior.prototype.removeOverlay = function(element) { + var overlays = this._overlays; + + overlays.remove({ + element: element, + type: 'drilldown' + }); + }; + + DrilldownOverlayBehavior.$inject = [ + 'canvas', + 'eventBus', + 'elementRegistry', + 'overlays' + ]; + + var DrilldownModdule = { + __depends__: [ OverlaysModule, ChangeSupportModule, RootElementsModule ], + __init__: [ 'drilldownBreadcrumbs', 'drilldownOverlayBehavior', 'drilldownCentering', 'subprocessCompatibility' ], + drilldownBreadcrumbs: [ 'type', DrilldownBreadcrumbs ], + drilldownCentering: [ 'type', DrilldownCentering ], + drilldownOverlayBehavior: [ 'type', DrilldownOverlayBehavior ], + subprocessCompatibility: [ 'type', SubprocessCompatibility ] + }; + + var CLASS_PATTERN = /^class /; + + + /** + * @param {function} fn + * + * @return {boolean} + */ + function isClass(fn) { + return CLASS_PATTERN.test(fn.toString()); + } + + /** + * @param {any} obj + * + * @return {boolean} + */ + function isArray(obj) { + return Object.prototype.toString.call(obj) === '[object Array]'; + } + + /** + * @param {any} obj + * @param {string} prop + * + * @return {boolean} + */ + function hasOwnProp(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); + } + + /** + * @typedef {import('./index').InjectAnnotated } InjectAnnotated + */ + + /** + * @template T + * + * @params {[...string[], T] | ...string[], T} args + * + * @return {T & InjectAnnotated} + */ + function annotate() { + var args = Array.prototype.slice.call(arguments); + + if (args.length === 1 && isArray(args[0])) { + args = args[0]; + } + + var fn = args.pop(); + + fn.$inject = args; + + return fn; + } + + + // Current limitations: + // - can't put into "function arg" comments + // function /* (no parenthesis like this) */ (){} + // function abc( /* xx (no parenthesis like this) */ a, b) {} + // + // Just put the comment before function or inside: + // /* (((this is fine))) */ function(a, b) {} + // function abc(a) { /* (((this is fine))) */} + // + // - can't reliably auto-annotate constructor; we'll match the + // first constructor(...) pattern found which may be the one + // of a nested class, too. + + var CONSTRUCTOR_ARGS = /constructor\s*[^(]*\(\s*([^)]*)\)/m; + var FN_ARGS = /^(?:async\s+)?(?:function\s*[^(]*)?(?:\(\s*([^)]*)\)|(\w+))/m; + var FN_ARG = /\/\*([^*]*)\*\//m; + + /** + * @param {unknown} fn + * + * @return {string[]} + */ + function parseAnnotations(fn) { + + if (typeof fn !== 'function') { + throw new Error('Cannot annotate "' + fn + '". Expected a function!'); + } + + var match = fn.toString().match(isClass(fn) ? CONSTRUCTOR_ARGS : FN_ARGS); + + // may parse class without constructor + if (!match) { + return []; + } + + var args = match[1] || match[2]; + + return args && args.split(',').map(function(arg) { + var argMatch = arg.match(FN_ARG); + return (argMatch && argMatch[1] || arg).trim(); + }) || []; + } + + /** + * @typedef { import('./index').ModuleDeclaration } ModuleDeclaration + * @typedef { import('./index').ModuleDefinition } ModuleDefinition + * @typedef { import('./index').InjectorContext } InjectorContext + */ + + /** + * Create a new injector with the given modules. + * + * @param {ModuleDefinition[]} modules + * @param {InjectorContext} [parent] + */ + function Injector(modules, parent) { + parent = parent || { + get: function(name, strict) { + currentlyResolving.push(name); + + if (strict === false) { + return null; + } else { + throw error('No provider for "' + name + '"!'); + } + } + }; + + var currentlyResolving = []; + var providers = this._providers = Object.create(parent._providers || null); + var instances = this._instances = Object.create(null); + + var self = instances.injector = this; + + var error = function(msg) { + var stack = currentlyResolving.join(' -> '); + currentlyResolving.length = 0; + return new Error(stack ? msg + ' (Resolving: ' + stack + ')' : msg); + }; + + /** + * Return a named service. + * + * @param {string} name + * @param {boolean} [strict=true] if false, resolve missing services to null + * + * @return {any} + */ + function get(name, strict) { + if (!providers[name] && name.indexOf('.') !== -1) { + var parts = name.split('.'); + var pivot = get(parts.shift()); + + while (parts.length) { + pivot = pivot[parts.shift()]; + } + + return pivot; + } + + if (hasOwnProp(instances, name)) { + return instances[name]; + } + + if (hasOwnProp(providers, name)) { + if (currentlyResolving.indexOf(name) !== -1) { + currentlyResolving.push(name); + throw error('Cannot resolve circular dependency!'); + } + + currentlyResolving.push(name); + instances[name] = providers[name][0](providers[name][1]); + currentlyResolving.pop(); + + return instances[name]; + } + + return parent.get(name, strict); + } + + function fnDef(fn, locals) { + + if (typeof locals === 'undefined') { + locals = {}; + } + + if (typeof fn !== 'function') { + if (isArray(fn)) { + fn = annotate(fn.slice()); + } else { + throw new Error('Cannot invoke "' + fn + '". Expected a function!'); + } + } + + var inject = fn.$inject || parseAnnotations(fn); + var dependencies = inject.map(function(dep) { + if (hasOwnProp(locals, dep)) { + return locals[dep]; + } else { + return get(dep); + } + }); + + return { + fn: fn, + dependencies: dependencies + }; + } + + function instantiate(Type) { + var def = fnDef(Type); + + var fn = def.fn, + dependencies = def.dependencies; + + // instantiate var args constructor + var Constructor = Function.prototype.bind.apply(fn, [ null ].concat(dependencies)); + + return new Constructor(); + } + + function invoke(func, context, locals) { + var def = fnDef(func, locals); + + var fn = def.fn, + dependencies = def.dependencies; + + return fn.apply(context, dependencies); + } + + /** + * @param {Injector} childInjector + * + * @return {Function} + */ + function createPrivateInjectorFactory(childInjector) { + return annotate(function(key) { + return childInjector.get(key); + }); + } + + /** + * @param {ModuleDefinition[]} modules + * @param {string[]} [forceNewInstances] + * + * @return {Injector} + */ + function createChild(modules, forceNewInstances) { + if (forceNewInstances && forceNewInstances.length) { + var fromParentModule = Object.create(null); + var matchedScopes = Object.create(null); + + var privateInjectorsCache = []; + var privateChildInjectors = []; + var privateChildFactories = []; + + var provider; + var cacheIdx; + var privateChildInjector; + var privateChildInjectorFactory; + for (var name in providers) { + provider = providers[name]; + + if (forceNewInstances.indexOf(name) !== -1) { + if (provider[2] === 'private') { + cacheIdx = privateInjectorsCache.indexOf(provider[3]); + if (cacheIdx === -1) { + privateChildInjector = provider[3].createChild([], forceNewInstances); + privateChildInjectorFactory = createPrivateInjectorFactory(privateChildInjector); + privateInjectorsCache.push(provider[3]); + privateChildInjectors.push(privateChildInjector); + privateChildFactories.push(privateChildInjectorFactory); + fromParentModule[name] = [ privateChildInjectorFactory, name, 'private', privateChildInjector ]; + } else { + fromParentModule[name] = [ privateChildFactories[cacheIdx], name, 'private', privateChildInjectors[cacheIdx] ]; + } + } else { + fromParentModule[name] = [ provider[2], provider[1] ]; + } + matchedScopes[name] = true; + } + + if ((provider[2] === 'factory' || provider[2] === 'type') && provider[1].$scope) { + /* jshint -W083 */ + forceNewInstances.forEach(function(scope) { + if (provider[1].$scope.indexOf(scope) !== -1) { + fromParentModule[name] = [ provider[2], provider[1] ]; + matchedScopes[scope] = true; + } + }); + } + } + + forceNewInstances.forEach(function(scope) { + if (!matchedScopes[scope]) { + throw new Error('No provider for "' + scope + '". Cannot use provider from the parent!'); + } + }); + + modules.unshift(fromParentModule); + } + + return new Injector(modules, self); + } + + var factoryMap = { + factory: invoke, + type: instantiate, + value: function(value) { + return value; + } + }; + + /** + * @param {ModuleDefinition} moduleDefinition + * @param {Injector} injector + */ + function createInitializer(moduleDefinition, injector) { + + var initializers = moduleDefinition.__init__ || []; + + return function() { + initializers.forEach(function(initializer) { + + // eagerly resolve component (fn or string) + if (typeof initializer === 'string') { + injector.get(initializer); + } else { + injector.invoke(initializer); + } + }); + }; + } + + /** + * @param {ModuleDefinition} moduleDefinition + */ + function loadModule(moduleDefinition) { + + var moduleExports = moduleDefinition.__exports__; + + // private module + if (moduleExports) { + var nestedModules = moduleDefinition.__modules__; + + var clonedModule = Object.keys(moduleDefinition).reduce(function(clonedModule, key) { + + if (key !== '__exports__' && key !== '__modules__' && key !== '__init__' && key !== '__depends__') { + clonedModule[key] = moduleDefinition[key]; + } + + return clonedModule; + }, Object.create(null)); + + var childModules = (nestedModules || []).concat(clonedModule); + + var privateInjector = createChild(childModules); + var getFromPrivateInjector = annotate(function(key) { + return privateInjector.get(key); + }); + + moduleExports.forEach(function(key) { + providers[key] = [ getFromPrivateInjector, key, 'private', privateInjector ]; + }); + + // ensure child injector initializes + var initializers = (moduleDefinition.__init__ || []).slice(); + + initializers.unshift(function() { + privateInjector.init(); + }); + + moduleDefinition = Object.assign({}, moduleDefinition, { + __init__: initializers + }); + + return createInitializer(moduleDefinition, privateInjector); + } + + // normal module + Object.keys(moduleDefinition).forEach(function(key) { + + if (key === '__init__' || key === '__depends__') { + return; + } + + if (moduleDefinition[key][2] === 'private') { + providers[key] = moduleDefinition[key]; + return; + } + + var type = moduleDefinition[key][0]; + var value = moduleDefinition[key][1]; + + providers[key] = [ factoryMap[type], arrayUnwrap(type, value), type ]; + }); + + return createInitializer(moduleDefinition, self); + } + + /** + * @param {ModuleDefinition[]} moduleDefinitions + * @param {ModuleDefinition} moduleDefinition + * + * @return {ModuleDefinition[]} + */ + function resolveDependencies(moduleDefinitions, moduleDefinition) { + + if (moduleDefinitions.indexOf(moduleDefinition) !== -1) { + return moduleDefinitions; + } + + moduleDefinitions = (moduleDefinition.__depends__ || []).reduce(resolveDependencies, moduleDefinitions); + + if (moduleDefinitions.indexOf(moduleDefinition) !== -1) { + return moduleDefinitions; + } + + return moduleDefinitions.concat(moduleDefinition); + } + + /** + * @param {ModuleDefinition[]} moduleDefinitions + * + * @return { () => void } initializerFn + */ + function bootstrap(moduleDefinitions) { + + var initializers = moduleDefinitions + .reduce(resolveDependencies, []) + .map(loadModule); + + var initialized = false; + + return function() { + + if (initialized) { + return; + } + + initialized = true; + + initializers.forEach(function(initializer) { + return initializer(); + }); + }; + } + + // public API + this.get = get; + this.invoke = invoke; + this.instantiate = instantiate; + this.createChild = createChild; + + // setup + this.init = bootstrap(modules); + } + + + // helpers /////////////// + + function arrayUnwrap(type, value) { + if (type !== 'value' && isArray(value)) { + value = annotate(value.slice()); + } + + return value; + } + + // apply default renderer with lowest possible priority + // so that it only kicks in if noone else could render + var DEFAULT_RENDER_PRIORITY = 1; + + /** + * The default renderer used for shapes and connections. + * + * @param {EventBus} eventBus + * @param {Styles} styles + */ + function DefaultRenderer(eventBus, styles) { + + // + BaseRenderer.call(this, eventBus, DEFAULT_RENDER_PRIORITY); + + this.CONNECTION_STYLE = styles.style([ 'no-fill' ], { strokeWidth: 5, stroke: 'fuchsia' }); + this.SHAPE_STYLE = styles.style({ fill: 'white', stroke: 'fuchsia', strokeWidth: 2 }); + this.FRAME_STYLE = styles.style([ 'no-fill' ], { stroke: 'fuchsia', strokeDasharray: 4, strokeWidth: 2 }); + } + + e(DefaultRenderer, BaseRenderer); + + + DefaultRenderer.prototype.canRender = function() { + return true; + }; + + DefaultRenderer.prototype.drawShape = function drawShape(visuals, element, attrs) { + var rect = create$1('rect'); + + attr$1(rect, { + x: 0, + y: 0, + width: element.width || 0, + height: element.height || 0 + }); + + if (isFrameElement(element)) { + attr$1(rect, assign({}, this.FRAME_STYLE, attrs || {})); + } else { + attr$1(rect, assign({}, this.SHAPE_STYLE, attrs || {})); + } + + append(visuals, rect); + + return rect; + }; + + DefaultRenderer.prototype.drawConnection = function drawConnection(visuals, connection, attrs) { + + var line = createLine(connection.waypoints, assign({}, this.CONNECTION_STYLE, attrs || {})); + append(visuals, line); + + return line; + }; + + DefaultRenderer.prototype.getShapePath = function getShapePath(shape) { + + var x = shape.x, + y = shape.y, + width = shape.width, + height = shape.height; + + var shapePath = [ + [ 'M', x, y ], + [ 'l', width, 0 ], + [ 'l', 0, height ], + [ 'l', -width, 0 ], + [ 'z' ] + ]; + + return componentsToPath(shapePath); + }; + + DefaultRenderer.prototype.getConnectionPath = function getConnectionPath(connection) { + var waypoints = connection.waypoints; + + var idx, point, connectionPath = []; + + for (idx = 0; (point = waypoints[idx]); idx++) { + + // take invisible docking into account + // when creating the path + point = point.original || point; + + connectionPath.push([ idx === 0 ? 'M' : 'L', point.x, point.y ]); + } + + return componentsToPath(connectionPath); + }; + + + DefaultRenderer.$inject = [ 'eventBus', 'styles' ]; + + /** + * A component that manages shape styles + */ + function Styles() { + + var defaultTraits = { + + 'no-fill': { + fill: 'none' + }, + 'no-border': { + strokeOpacity: 0.0 + }, + 'no-events': { + pointerEvents: 'none' + } + }; + + var self = this; + + /** + * Builds a style definition from a className, a list of traits and an object of additional attributes. + * + * @param {string} className + * @param {Array} traits + * @param {Object} additionalAttrs + * + * @return {Object} the style defintion + */ + this.cls = function(className, traits, additionalAttrs) { + var attrs = this.style(traits, additionalAttrs); + + return assign(attrs, { 'class': className }); + }; + + /** + * Builds a style definition from a list of traits and an object of additional attributes. + * + * @param {Array} traits + * @param {Object} additionalAttrs + * + * @return {Object} the style defintion + */ + this.style = function(traits, additionalAttrs) { + + if (!isArray$2(traits) && !additionalAttrs) { + additionalAttrs = traits; + traits = []; + } + + var attrs = reduce(traits, function(attrs, t) { + return assign(attrs, defaultTraits[t] || {}); + }, {}); + + return additionalAttrs ? assign(attrs, additionalAttrs) : attrs; + }; + + this.computeStyle = function(custom, traits, defaultStyles) { + if (!isArray$2(traits)) { + defaultStyles = traits; + traits = []; + } + + return self.style(traits || [], assign({}, defaultStyles, custom || {})); + }; + } + + var DrawModule = { + __init__: [ 'defaultRenderer' ], + defaultRenderer: [ 'type', DefaultRenderer ], + styles: [ 'type', Styles ] + }; + + /** + * Failsafe remove an element from a collection + * + * @param {Array} [collection] + * @param {Object} [element] + * + * @return {number} the previous index of the element + */ + function remove(collection, element) { + + if (!collection || !element) { + return -1; + } + + var idx = collection.indexOf(element); + + if (idx !== -1) { + collection.splice(idx, 1); + } + + return idx; + } + + /** + * Fail save add an element to the given connection, ensuring + * it does not yet exist. + * + * @param {Array} collection + * @param {Object} element + * @param {number} idx + */ + function add(collection, element, idx) { + + if (!collection || !element) { + return; + } + + if (typeof idx !== 'number') { + idx = -1; + } + + var currentIdx = collection.indexOf(element); + + if (currentIdx !== -1) { + + if (currentIdx === idx) { + + // nothing to do, position has not changed + return; + } else { + + if (idx !== -1) { + + // remove from current position + collection.splice(currentIdx, 1); + } else { + + // already exists in collection + return; + } + } + } + + if (idx !== -1) { + + // insert at specified position + collection.splice(idx, 0, element); + } else { + + // push to end + collection.push(element); + } + } + + function round(number, resolution) { + return Math.round(number * resolution) / resolution; + } + + function ensurePx(number) { + return isNumber(number) ? number + 'px' : number; + } + + function findRoot(element) { + while (element.parent) { + element = element.parent; + } + + return element; + } + + /** + * Creates a HTML container element for a SVG element with + * the given configuration + * + * @param {Object} options + * @return {HTMLElement} the container element + */ + function createContainer(options) { + + options = assign({}, { width: '100%', height: '100%' }, options); + + var container = options.container || document.body; + + // create a
        around the svg element with the respective size + // this way we can always get the correct container size + // (this is impossible for elements at the moment) + var parent = document.createElement('div'); + parent.setAttribute('class', 'djs-container'); + + assign$1(parent, { + position: 'relative', + overflow: 'hidden', + width: ensurePx(options.width), + height: ensurePx(options.height) + }); + + container.appendChild(parent); + + return parent; + } + + function createGroup(parent, cls, childIndex) { + var group = create$1('g'); + classes$1(group).add(cls); + + var index = childIndex !== undefined ? childIndex : parent.childNodes.length - 1; + + // must ensure second argument is node or _null_ + // cf. https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore + parent.insertBefore(group, parent.childNodes[index] || null); + + return group; + } + + var BASE_LAYER = 'base'; + + // render plane contents behind utility layers + var PLANE_LAYER_INDEX = 0; + var UTILITY_LAYER_INDEX = 1; + + + var REQUIRED_MODEL_ATTRS = { + shape: [ 'x', 'y', 'width', 'height' ], + connection: [ 'waypoints' ] + }; + + /** + * The main drawing canvas. + * + * @class + * @constructor + * + * @emits Canvas#canvas.init + * + * @param {Object} config + * @param {EventBus} eventBus + * @param {GraphicsFactory} graphicsFactory + * @param {ElementRegistry} elementRegistry + */ + function Canvas(config, eventBus, graphicsFactory, elementRegistry) { + + this._eventBus = eventBus; + this._elementRegistry = elementRegistry; + this._graphicsFactory = graphicsFactory; + + this._rootsIdx = 0; + + this._layers = {}; + this._planes = []; + this._rootElement = null; + + this._init(config || {}); + } + + Canvas.$inject = [ + 'config.canvas', + 'eventBus', + 'graphicsFactory', + 'elementRegistry' + ]; + + /** + * Creates a element that is wrapped into a
        . + * This way we are always able to correctly figure out the size of the svg element + * by querying the parent node. + + * (It is not possible to get the size of a svg element cross browser @ 2014-04-01) + + *
        + * + * ... + * + *
        + */ + Canvas.prototype._init = function(config) { + + var eventBus = this._eventBus; + + // html container + var container = this._container = createContainer(config); + + var svg = this._svg = create$1('svg'); + attr$1(svg, { width: '100%', height: '100%' }); + + append(container, svg); + + var viewport = this._viewport = createGroup(svg, 'viewport'); + + // debounce canvas.viewbox.changed events + // for smoother diagram interaction + if (config.deferUpdate !== false) { + this._viewboxChanged = debounce(bind(this._viewboxChanged, this), 300); + } + + eventBus.on('diagram.init', function() { + + /** + * An event indicating that the canvas is ready to be drawn on. + * + * @memberOf Canvas + * + * @event canvas.init + * + * @type {Object} + * @property {SVGElement} svg the created svg element + * @property {SVGElement} viewport the direct parent of diagram elements and shapes + */ + eventBus.fire('canvas.init', { + svg: svg, + viewport: viewport + }); + + }, this); + + // reset viewbox on shape changes to + // recompute the viewbox + eventBus.on([ + 'shape.added', + 'connection.added', + 'shape.removed', + 'connection.removed', + 'elements.changed', + 'root.set' + ], function() { + delete this._cachedViewbox; + }, this); + + eventBus.on('diagram.destroy', 500, this._destroy, this); + eventBus.on('diagram.clear', 500, this._clear, this); + }; + + Canvas.prototype._destroy = function(emit) { + this._eventBus.fire('canvas.destroy', { + svg: this._svg, + viewport: this._viewport + }); + + var parent = this._container.parentNode; + + if (parent) { + parent.removeChild(this._container); + } + + delete this._svg; + delete this._container; + delete this._layers; + delete this._planes; + delete this._rootElement; + delete this._viewport; + }; + + Canvas.prototype._clear = function() { + + var self = this; + + var allElements = this._elementRegistry.getAll(); + + // remove all elements + allElements.forEach(function(element) { + var type = getType(element); + + if (type === 'root') { + self.removeRootElement(element); + } else { + self._removeElement(element, type); + } + }); + + // remove all planes + this._planes = []; + this._rootElement = null; + + // force recomputation of view box + delete this._cachedViewbox; + }; + + /** + * Returns the default layer on which + * all elements are drawn. + * + * @returns {SVGElement} + */ + Canvas.prototype.getDefaultLayer = function() { + return this.getLayer(BASE_LAYER, PLANE_LAYER_INDEX); + }; + + /** + * Returns a layer that is used to draw elements + * or annotations on it. + * + * Non-existing layers retrieved through this method + * will be created. During creation, the optional index + * may be used to create layers below or above existing layers. + * A layer with a certain index is always created above all + * existing layers with the same index. + * + * @param {string} name + * @param {number} index + * + * @returns {SVGElement} + */ + Canvas.prototype.getLayer = function(name, index) { + + if (!name) { + throw new Error('must specify a name'); + } + + var layer = this._layers[name]; + + if (!layer) { + layer = this._layers[name] = this._createLayer(name, index); + } + + // throw an error if layer creation / retrival is + // requested on different index + if (typeof index !== 'undefined' && layer.index !== index) { + throw new Error('layer <' + name + '> already created at index <' + index + '>'); + } + + return layer.group; + }; + + /** + * For a given index, return the number of layers that have a higher index and + * are visible. + * + * This is used to determine the node a layer should be inserted at. + * + * @param {Number} index + * @returns {Number} + */ + Canvas.prototype._getChildIndex = function(index) { + return reduce(this._layers, function(childIndex, layer) { + if (layer.visible && index >= layer.index) { + childIndex++; + } + + return childIndex; + }, 0); + }; + + /** + * Creates a given layer and returns it. + * + * @param {string} name + * @param {number} [index=0] + * + * @return {Object} layer descriptor with { index, group: SVGGroup } + */ + Canvas.prototype._createLayer = function(name, index) { + + if (typeof index === 'undefined') { + index = UTILITY_LAYER_INDEX; + } + + var childIndex = this._getChildIndex(index); + + return { + group: createGroup(this._viewport, 'layer-' + name, childIndex), + index: index, + visible: true + }; + }; + + + /** + * Shows a given layer. + * + * @param {String} layer + * @returns {SVGElement} + */ + Canvas.prototype.showLayer = function(name) { + + if (!name) { + throw new Error('must specify a name'); + } + + var layer = this._layers[name]; + + if (!layer) { + throw new Error('layer <' + name + '> does not exist'); + } + + var viewport = this._viewport; + var group = layer.group; + var index = layer.index; + + if (layer.visible) { + return group; + } + + var childIndex = this._getChildIndex(index); + + viewport.insertBefore(group, viewport.childNodes[childIndex] || null); + + layer.visible = true; + + return group; + }; + + /** + * Hides a given layer. + * + * @param {String} layer + * @returns {SVGElement} + */ + Canvas.prototype.hideLayer = function(name) { + + if (!name) { + throw new Error('must specify a name'); + } + + var layer = this._layers[name]; + + if (!layer) { + throw new Error('layer <' + name + '> does not exist'); + } + + var group = layer.group; + + if (!layer.visible) { + return group; + } + + remove$2(group); + + layer.visible = false; + + return group; + }; + + + Canvas.prototype._removeLayer = function(name) { + + var layer = this._layers[name]; + + if (layer) { + delete this._layers[name]; + + remove$2(layer.group); + } + }; + + /** + * Returns the currently active layer. Can be null. + * + * @returns {SVGElement|null} + */ + Canvas.prototype.getActiveLayer = function() { + var plane = this._findPlaneForRoot(this.getRootElement()); + + if (!plane) { + return null; + } + + return plane.layer; + }; + + + /** + * Returns the plane which contains the given element. + * + * @param {string|djs.model.Base} element + * + * @return {djs.model.Base} root for element + */ + Canvas.prototype.findRoot = function(element) { + if (typeof element === 'string') { + element = this._elementRegistry.get(element); + } + + if (!element) { + return; + } + + var plane = this._findPlaneForRoot( + findRoot(element) + ) || {}; + + return plane.rootElement; + }; + + /** + * Return a list of all root elements on the diagram. + * + * @return {djs.model.Root[]} + */ + Canvas.prototype.getRootElements = function() { + return this._planes.map(function(plane) { + return plane.rootElement; + }); + }; + + Canvas.prototype._findPlaneForRoot = function(rootElement) { + return find(this._planes, function(plane) { + return plane.rootElement === rootElement; + }); + }; + + + /** + * Returns the html element that encloses the + * drawing canvas. + * + * @return {DOMNode} + */ + Canvas.prototype.getContainer = function() { + return this._container; + }; + + + // markers ////////////////////// + + Canvas.prototype._updateMarker = function(element, marker, add) { + var container; + + if (!element.id) { + element = this._elementRegistry.get(element); + } + + // we need to access all + container = this._elementRegistry._elements[element.id]; + + if (!container) { + return; + } + + forEach$1([ container.gfx, container.secondaryGfx ], function(gfx) { + if (gfx) { + + // invoke either addClass or removeClass based on mode + if (add) { + classes$1(gfx).add(marker); + } else { + classes$1(gfx).remove(marker); + } + } + }); + + /** + * An event indicating that a marker has been updated for an element + * + * @event element.marker.update + * @type {Object} + * @property {djs.model.Element} element the shape + * @property {Object} gfx the graphical representation of the shape + * @property {string} marker + * @property {boolean} add true if the marker was added, false if it got removed + */ + this._eventBus.fire('element.marker.update', { element: element, gfx: container.gfx, marker: marker, add: !!add }); + }; + + + /** + * Adds a marker to an element (basically a css class). + * + * Fires the element.marker.update event, making it possible to + * integrate extension into the marker life-cycle, too. + * + * @example + * canvas.addMarker('foo', 'some-marker'); + * + * var fooGfx = canvas.getGraphics('foo'); + * + * fooGfx; // ... + * + * @param {string|djs.model.Base} element + * @param {string} marker + */ + Canvas.prototype.addMarker = function(element, marker) { + this._updateMarker(element, marker, true); + }; + + + /** + * Remove a marker from an element. + * + * Fires the element.marker.update event, making it possible to + * integrate extension into the marker life-cycle, too. + * + * @param {string|djs.model.Base} element + * @param {string} marker + */ + Canvas.prototype.removeMarker = function(element, marker) { + this._updateMarker(element, marker, false); + }; + + /** + * Check the existence of a marker on element. + * + * @param {string|djs.model.Base} element + * @param {string} marker + */ + Canvas.prototype.hasMarker = function(element, marker) { + if (!element.id) { + element = this._elementRegistry.get(element); + } + + var gfx = this.getGraphics(element); + + return classes$1(gfx).has(marker); + }; + + /** + * Toggles a marker on an element. + * + * Fires the element.marker.update event, making it possible to + * integrate extension into the marker life-cycle, too. + * + * @param {string|djs.model.Base} element + * @param {string} marker + */ + Canvas.prototype.toggleMarker = function(element, marker) { + if (this.hasMarker(element, marker)) { + this.removeMarker(element, marker); + } else { + this.addMarker(element, marker); + } + }; + + /** + * Returns the current root element. + * + * Supports two different modes for handling root elements: + * + * 1. if no root element has been added before, an implicit root will be added + * and returned. This is used in applications that don't require explicit + * root elements. + * + * 2. when root elements have been added before calling `getRootElement`, + * root elements can be null. This is used for applications that want to manage + * root elements themselves. + * + * @returns {Object|djs.model.Root|null} rootElement. + */ + Canvas.prototype.getRootElement = function() { + var rootElement = this._rootElement; + + // can return null if root elements are present but none was set yet + if (rootElement || this._planes.length) { + return rootElement; + } + + return this.setRootElement(this.addRootElement(null)); + }; + + /** + * Adds a given root element and returns it. + * + * @param {Object|djs.model.Root} rootElement + * + * @return {Object|djs.model.Root} rootElement + */ + + Canvas.prototype.addRootElement = function(rootElement) { + var idx = this._rootsIdx++; + + if (!rootElement) { + rootElement = { + id: '__implicitroot_' + idx, + children: [], + isImplicit: true + }; + } + + var layerName = rootElement.layer = 'root-' + idx; + + this._ensureValid('root', rootElement); + + var layer = this.getLayer(layerName, PLANE_LAYER_INDEX); + + this.hideLayer(layerName); + + this._addRoot(rootElement, layer); + + this._planes.push({ + rootElement: rootElement, + layer: layer + }); + + return rootElement; + }; + + /** + * Removes a given rootElement and returns it. + * + * @param {djs.model.Root|String} rootElement + * + * @return {Object|djs.model.Root} rootElement + */ + Canvas.prototype.removeRootElement = function(rootElement) { + + if (typeof rootElement === 'string') { + rootElement = this._elementRegistry.get(rootElement); + } + + var plane = this._findPlaneForRoot(rootElement); + + if (!plane) { + return; + } + + // hook up life-cycle events + this._removeRoot(rootElement); + + // clean up layer + this._removeLayer(rootElement.layer); + + // clean up plane + this._planes = this._planes.filter(function(plane) { + return plane.rootElement !== rootElement; + }); + + // clean up active root + if (this._rootElement === rootElement) { + this._rootElement = null; + } + + return rootElement; + }; + + + // root element handling ////////////////////// + + /** + * Sets a given element as the new root element for the canvas + * and returns the new root element. + * + * @param {Object|djs.model.Root} rootElement + * + * @return {Object|djs.model.Root} new root element + */ + Canvas.prototype.setRootElement = function(rootElement, override) { + + if (isDefined(override)) { + throw new Error('override not supported'); + } + + if (rootElement === this._rootElement) { + return; + } + + var plane; + + if (!rootElement) { + throw new Error('rootElement required'); + } + + plane = this._findPlaneForRoot(rootElement); + + // give set add semantics for backwards compatibility + if (!plane) { + rootElement = this.addRootElement(rootElement); + } + + this._setRoot(rootElement); + + return rootElement; + }; + + + Canvas.prototype._removeRoot = function(element) { + var elementRegistry = this._elementRegistry, + eventBus = this._eventBus; + + // simulate element remove event sequence + eventBus.fire('root.remove', { element: element }); + eventBus.fire('root.removed', { element: element }); + + elementRegistry.remove(element); + }; + + + Canvas.prototype._addRoot = function(element, gfx) { + var elementRegistry = this._elementRegistry, + eventBus = this._eventBus; + + // resemble element add event sequence + eventBus.fire('root.add', { element: element }); + + elementRegistry.add(element, gfx); + + eventBus.fire('root.added', { element: element, gfx: gfx }); + }; + + + Canvas.prototype._setRoot = function(rootElement, layer) { + + var currentRoot = this._rootElement; + + if (currentRoot) { + + // un-associate previous root element + this._elementRegistry.updateGraphics(currentRoot, null, true); + + // hide previous layer + this.hideLayer(currentRoot.layer); + } + + if (rootElement) { + + if (!layer) { + layer = this._findPlaneForRoot(rootElement).layer; + } + + // associate element with + this._elementRegistry.updateGraphics(rootElement, this._svg, true); + + // show root layer + this.showLayer(rootElement.layer); + } + + this._rootElement = rootElement; + + this._eventBus.fire('root.set', { element: rootElement }); + }; + + // add functionality ////////////////////// + + Canvas.prototype._ensureValid = function(type, element) { + if (!element.id) { + throw new Error('element must have an id'); + } + + if (this._elementRegistry.get(element.id)) { + throw new Error('element <' + element.id + '> already exists'); + } + + var requiredAttrs = REQUIRED_MODEL_ATTRS[type]; + + var valid = every(requiredAttrs, function(attr) { + return typeof element[attr] !== 'undefined'; + }); + + if (!valid) { + throw new Error( + 'must supply { ' + requiredAttrs.join(', ') + ' } with ' + type); + } + }; + + Canvas.prototype._setParent = function(element, parent, parentIndex) { + add(parent.children, element, parentIndex); + element.parent = parent; + }; + + /** + * Adds an element to the canvas. + * + * This wires the parent <-> child relationship between the element and + * a explicitly specified parent or an implicit root element. + * + * During add it emits the events + * + * * <{type}.add> (element, parent) + * * <{type}.added> (element, gfx) + * + * Extensions may hook into these events to perform their magic. + * + * @param {string} type + * @param {Object|djs.model.Base} element + * @param {Object|djs.model.Base} [parent] + * @param {number} [parentIndex] + * + * @return {Object|djs.model.Base} the added element + */ + Canvas.prototype._addElement = function(type, element, parent, parentIndex) { + + parent = parent || this.getRootElement(); + + var eventBus = this._eventBus, + graphicsFactory = this._graphicsFactory; + + this._ensureValid(type, element); + + eventBus.fire(type + '.add', { element: element, parent: parent }); + + this._setParent(element, parent, parentIndex); + + // create graphics + var gfx = graphicsFactory.create(type, element, parentIndex); + + this._elementRegistry.add(element, gfx); + + // update its visual + graphicsFactory.update(type, element, gfx); + + eventBus.fire(type + '.added', { element: element, gfx: gfx }); + + return element; + }; + + /** + * Adds a shape to the canvas + * + * @param {Object|djs.model.Shape} shape to add to the diagram + * @param {djs.model.Base} [parent] + * @param {number} [parentIndex] + * + * @return {djs.model.Shape} the added shape + */ + Canvas.prototype.addShape = function(shape, parent, parentIndex) { + return this._addElement('shape', shape, parent, parentIndex); + }; + + /** + * Adds a connection to the canvas + * + * @param {Object|djs.model.Connection} connection to add to the diagram + * @param {djs.model.Base} [parent] + * @param {number} [parentIndex] + * + * @return {djs.model.Connection} the added connection + */ + Canvas.prototype.addConnection = function(connection, parent, parentIndex) { + return this._addElement('connection', connection, parent, parentIndex); + }; + + + /** + * Internal remove element + */ + Canvas.prototype._removeElement = function(element, type) { + + var elementRegistry = this._elementRegistry, + graphicsFactory = this._graphicsFactory, + eventBus = this._eventBus; + + element = elementRegistry.get(element.id || element); + + if (!element) { + + // element was removed already + return; + } + + eventBus.fire(type + '.remove', { element: element }); + + graphicsFactory.remove(element); + + // unset parent <-> child relationship + remove(element.parent && element.parent.children, element); + element.parent = null; + + eventBus.fire(type + '.removed', { element: element }); + + elementRegistry.remove(element); + + return element; + }; + + + /** + * Removes a shape from the canvas + * + * @param {string|djs.model.Shape} shape or shape id to be removed + * + * @return {djs.model.Shape} the removed shape + */ + Canvas.prototype.removeShape = function(shape) { + + /** + * An event indicating that a shape is about to be removed from the canvas. + * + * @memberOf Canvas + * + * @event shape.remove + * @type {Object} + * @property {djs.model.Shape} element the shape descriptor + * @property {Object} gfx the graphical representation of the shape + */ + + /** + * An event indicating that a shape has been removed from the canvas. + * + * @memberOf Canvas + * + * @event shape.removed + * @type {Object} + * @property {djs.model.Shape} element the shape descriptor + * @property {Object} gfx the graphical representation of the shape + */ + return this._removeElement(shape, 'shape'); + }; + + + /** + * Removes a connection from the canvas + * + * @param {string|djs.model.Connection} connection or connection id to be removed + * + * @return {djs.model.Connection} the removed connection + */ + Canvas.prototype.removeConnection = function(connection) { + + /** + * An event indicating that a connection is about to be removed from the canvas. + * + * @memberOf Canvas + * + * @event connection.remove + * @type {Object} + * @property {djs.model.Connection} element the connection descriptor + * @property {Object} gfx the graphical representation of the connection + */ + + /** + * An event indicating that a connection has been removed from the canvas. + * + * @memberOf Canvas + * + * @event connection.removed + * @type {Object} + * @property {djs.model.Connection} element the connection descriptor + * @property {Object} gfx the graphical representation of the connection + */ + return this._removeElement(connection, 'connection'); + }; + + + /** + * Return the graphical object underlaying a certain diagram element + * + * @param {string|djs.model.Base} element descriptor of the element + * @param {boolean} [secondary=false] whether to return the secondary connected element + * + * @return {SVGElement} + */ + Canvas.prototype.getGraphics = function(element, secondary) { + return this._elementRegistry.getGraphics(element, secondary); + }; + + + /** + * Perform a viewbox update via a given change function. + * + * @param {Function} changeFn + */ + Canvas.prototype._changeViewbox = function(changeFn) { + + // notify others of the upcoming viewbox change + this._eventBus.fire('canvas.viewbox.changing'); + + // perform actual change + changeFn.apply(this); + + // reset the cached viewbox so that + // a new get operation on viewbox or zoom + // triggers a viewbox re-computation + this._cachedViewbox = null; + + // notify others of the change; this step + // may or may not be debounced + this._viewboxChanged(); + }; + + Canvas.prototype._viewboxChanged = function() { + this._eventBus.fire('canvas.viewbox.changed', { viewbox: this.viewbox() }); + }; + + + /** + * Gets or sets the view box of the canvas, i.e. the + * area that is currently displayed. + * + * The getter may return a cached viewbox (if it is currently + * changing). To force a recomputation, pass `false` as the first argument. + * + * @example + * + * canvas.viewbox({ x: 100, y: 100, width: 500, height: 500 }) + * + * // sets the visible area of the diagram to (100|100) -> (600|100) + * // and and scales it according to the diagram width + * + * var viewbox = canvas.viewbox(); // pass `false` to force recomputing the box. + * + * console.log(viewbox); + * // { + * // inner: Dimensions, + * // outer: Dimensions, + * // scale, + * // x, y, + * // width, height + * // } + * + * // if the current diagram is zoomed and scrolled, you may reset it to the + * // default zoom via this method, too: + * + * var zoomedAndScrolledViewbox = canvas.viewbox(); + * + * canvas.viewbox({ + * x: 0, + * y: 0, + * width: zoomedAndScrolledViewbox.outer.width, + * height: zoomedAndScrolledViewbox.outer.height + * }); + * + * @param {Object} [box] the new view box to set + * @param {number} box.x the top left X coordinate of the canvas visible in view box + * @param {number} box.y the top left Y coordinate of the canvas visible in view box + * @param {number} box.width the visible width + * @param {number} box.height + * + * @return {Object} the current view box + */ + Canvas.prototype.viewbox = function(box) { + + if (box === undefined && this._cachedViewbox) { + return this._cachedViewbox; + } + + var viewport = this._viewport, + innerBox, + outerBox = this.getSize(), + matrix, + activeLayer, + transform, + scale, + x, y; + + if (!box) { + + // compute the inner box based on the + // diagrams active layer. This allows us to exclude + // external components, such as overlays + + activeLayer = this._rootElement ? this.getActiveLayer() : null; + innerBox = activeLayer && activeLayer.getBBox() || {}; + + transform = transform$1(viewport); + matrix = transform ? transform.matrix : createMatrix(); + scale = round(matrix.a, 1000); + + x = round(-matrix.e || 0, 1000); + y = round(-matrix.f || 0, 1000); + + box = this._cachedViewbox = { + x: x ? x / scale : 0, + y: y ? y / scale : 0, + width: outerBox.width / scale, + height: outerBox.height / scale, + scale: scale, + inner: { + width: innerBox.width || 0, + height: innerBox.height || 0, + x: innerBox.x || 0, + y: innerBox.y || 0 + }, + outer: outerBox + }; + + return box; + } else { + + this._changeViewbox(function() { + scale = Math.min(outerBox.width / box.width, outerBox.height / box.height); + + var matrix = this._svg.createSVGMatrix() + .scale(scale) + .translate(-box.x, -box.y); + + transform$1(viewport, matrix); + }); + } + + return box; + }; + + + /** + * Gets or sets the scroll of the canvas. + * + * @param {Object} [delta] the new scroll to apply. + * + * @param {number} [delta.dx] + * @param {number} [delta.dy] + */ + Canvas.prototype.scroll = function(delta) { + + var node = this._viewport; + var matrix = node.getCTM(); + + if (delta) { + this._changeViewbox(function() { + delta = assign({ dx: 0, dy: 0 }, delta || {}); + + matrix = this._svg.createSVGMatrix().translate(delta.dx, delta.dy).multiply(matrix); + + setCTM(node, matrix); + }); + } + + return { x: matrix.e, y: matrix.f }; + }; + + /** + * Scrolls the viewbox to contain the given element. + * Optionally specify a padding to be applied to the edges. + * + * @param {Object|String} [element] the element to scroll to. + * @param {Object|Number} [padding=100] the padding to be applied. Can also specify top, bottom, left and right. + * + */ + Canvas.prototype.scrollToElement = function(element, padding) { + var defaultPadding = 100; + + if (typeof element === 'string') { + element = this._elementRegistry.get(element); + } + + // set to correct rootElement + var rootElement = this.findRoot(element); + + if (rootElement !== this.getRootElement()) { + this.setRootElement(rootElement); + } + + if (!padding) { + padding = {}; + } + if (typeof padding === 'number') { + defaultPadding = padding; + } + + padding = { + top: padding.top || defaultPadding, + right: padding.right || defaultPadding, + bottom: padding.bottom || defaultPadding, + left: padding.left || defaultPadding + }; + + var elementBounds = getBBox(element), + elementTrbl = asTRBL(elementBounds), + viewboxBounds = this.viewbox(), + zoom = this.zoom(), + dx, dy; + + // shrink viewboxBounds with padding + viewboxBounds.y += padding.top / zoom; + viewboxBounds.x += padding.left / zoom; + viewboxBounds.width -= (padding.right + padding.left) / zoom; + viewboxBounds.height -= (padding.bottom + padding.top) / zoom; + + var viewboxTrbl = asTRBL(viewboxBounds); + + var canFit = elementBounds.width < viewboxBounds.width && elementBounds.height < viewboxBounds.height; + + if (!canFit) { + + // top-left when element can't fit + dx = elementBounds.x - viewboxBounds.x; + dy = elementBounds.y - viewboxBounds.y; + + } else { + + var dRight = Math.max(0, elementTrbl.right - viewboxTrbl.right), + dLeft = Math.min(0, elementTrbl.left - viewboxTrbl.left), + dBottom = Math.max(0, elementTrbl.bottom - viewboxTrbl.bottom), + dTop = Math.min(0, elementTrbl.top - viewboxTrbl.top); + + dx = dRight || dLeft; + dy = dBottom || dTop; + + } + + this.scroll({ dx: -dx * zoom, dy: -dy * zoom }); + }; + + /** + * Gets or sets the current zoom of the canvas, optionally zooming + * to the specified position. + * + * The getter may return a cached zoom level. Call it with `false` as + * the first argument to force recomputation of the current level. + * + * @param {string|number} [newScale] the new zoom level, either a number, i.e. 0.9, + * or `fit-viewport` to adjust the size to fit the current viewport + * @param {string|Point} [center] the reference point { x: .., y: ..} to zoom to, 'auto' to zoom into mid or null + * + * @return {number} the current scale + */ + Canvas.prototype.zoom = function(newScale, center) { + + if (!newScale) { + return this.viewbox(newScale).scale; + } + + if (newScale === 'fit-viewport') { + return this._fitViewport(center); + } + + var outer, + matrix; + + this._changeViewbox(function() { + + if (typeof center !== 'object') { + outer = this.viewbox().outer; + + center = { + x: outer.width / 2, + y: outer.height / 2 + }; + } + + matrix = this._setZoom(newScale, center); + }); + + return round(matrix.a, 1000); + }; + + function setCTM(node, m) { + var mstr = 'matrix(' + m.a + ',' + m.b + ',' + m.c + ',' + m.d + ',' + m.e + ',' + m.f + ')'; + node.setAttribute('transform', mstr); + } + + Canvas.prototype._fitViewport = function(center) { + + var vbox = this.viewbox(), + outer = vbox.outer, + inner = vbox.inner, + newScale, + newViewbox; + + // display the complete diagram without zooming in. + // instead of relying on internal zoom, we perform a + // hard reset on the canvas viewbox to realize this + // + // if diagram does not need to be zoomed in, we focus it around + // the diagram origin instead + + if (inner.x >= 0 && + inner.y >= 0 && + inner.x + inner.width <= outer.width && + inner.y + inner.height <= outer.height && + !center) { + + newViewbox = { + x: 0, + y: 0, + width: Math.max(inner.width + inner.x, outer.width), + height: Math.max(inner.height + inner.y, outer.height) + }; + } else { + + newScale = Math.min(1, outer.width / inner.width, outer.height / inner.height); + newViewbox = { + x: inner.x + (center ? inner.width / 2 - outer.width / newScale / 2 : 0), + y: inner.y + (center ? inner.height / 2 - outer.height / newScale / 2 : 0), + width: outer.width / newScale, + height: outer.height / newScale + }; + } + + this.viewbox(newViewbox); + + return this.viewbox(false).scale; + }; + + + Canvas.prototype._setZoom = function(scale, center) { + + var svg = this._svg, + viewport = this._viewport; + + var matrix = svg.createSVGMatrix(); + var point = svg.createSVGPoint(); + + var centerPoint, + originalPoint, + currentMatrix, + scaleMatrix, + newMatrix; + + currentMatrix = viewport.getCTM(); + + var currentScale = currentMatrix.a; + + if (center) { + centerPoint = assign(point, center); + + // revert applied viewport transformations + originalPoint = centerPoint.matrixTransform(currentMatrix.inverse()); + + // create scale matrix + scaleMatrix = matrix + .translate(originalPoint.x, originalPoint.y) + .scale(1 / currentScale * scale) + .translate(-originalPoint.x, -originalPoint.y); + + newMatrix = currentMatrix.multiply(scaleMatrix); + } else { + newMatrix = matrix.scale(scale); + } + + setCTM(this._viewport, newMatrix); + + return newMatrix; + }; + + + /** + * Returns the size of the canvas + * + * @return {Dimensions} + */ + Canvas.prototype.getSize = function() { + return { + width: this._container.clientWidth, + height: this._container.clientHeight + }; + }; + + + /** + * Return the absolute bounding box for the given element + * + * The absolute bounding box may be used to display overlays in the + * callers (browser) coordinate system rather than the zoomed in/out + * canvas coordinates. + * + * @param {ElementDescriptor} element + * @return {Bounds} the absolute bounding box + */ + Canvas.prototype.getAbsoluteBBox = function(element) { + var vbox = this.viewbox(); + var bbox; + + // connection + // use svg bbox + if (element.waypoints) { + var gfx = this.getGraphics(element); + + bbox = gfx.getBBox(); + } + + // shapes + // use data + else { + bbox = element; + } + + var x = bbox.x * vbox.scale - vbox.x * vbox.scale; + var y = bbox.y * vbox.scale - vbox.y * vbox.scale; + + var width = bbox.width * vbox.scale; + var height = bbox.height * vbox.scale; + + return { + x: x, + y: y, + width: width, + height: height + }; + }; + + /** + * Fires an event in order other modules can react to the + * canvas resizing + */ + Canvas.prototype.resized = function() { + + // force recomputation of view box + delete this._cachedViewbox; + + this._eventBus.fire('canvas.resized'); + }; + + var ELEMENT_ID = 'data-element-id'; + + + /** + * @class + * + * A registry that keeps track of all shapes in the diagram. + */ + function ElementRegistry(eventBus) { + this._elements = {}; + + this._eventBus = eventBus; + } + + ElementRegistry.$inject = [ 'eventBus' ]; + + /** + * Register a pair of (element, gfx, (secondaryGfx)). + * + * @param {djs.model.Base} element + * @param {SVGElement} gfx + * @param {SVGElement} [secondaryGfx] optional other element to register, too + */ + ElementRegistry.prototype.add = function(element, gfx, secondaryGfx) { + + var id = element.id; + + this._validateId(id); + + // associate dom node with element + attr$1(gfx, ELEMENT_ID, id); + + if (secondaryGfx) { + attr$1(secondaryGfx, ELEMENT_ID, id); + } + + this._elements[id] = { element: element, gfx: gfx, secondaryGfx: secondaryGfx }; + }; + + /** + * Removes an element from the registry. + * + * @param {djs.model.Base} element + */ + ElementRegistry.prototype.remove = function(element) { + var elements = this._elements, + id = element.id || element, + container = id && elements[id]; + + if (container) { + + // unset element id on gfx + attr$1(container.gfx, ELEMENT_ID, ''); + + if (container.secondaryGfx) { + attr$1(container.secondaryGfx, ELEMENT_ID, ''); + } + + delete elements[id]; + } + }; + + /** + * Update the id of an element + * + * @param {djs.model.Base} element + * @param {string} newId + */ + ElementRegistry.prototype.updateId = function(element, newId) { + + this._validateId(newId); + + if (typeof element === 'string') { + element = this.get(element); + } + + this._eventBus.fire('element.updateId', { + element: element, + newId: newId + }); + + var gfx = this.getGraphics(element), + secondaryGfx = this.getGraphics(element, true); + + this.remove(element); + + element.id = newId; + + this.add(element, gfx, secondaryGfx); + }; + + /** + * Update the graphics of an element + * + * @param {djs.model.Base} element + * @param {SVGElement} gfx + * @param {boolean} [secondary=false] whether to update the secondary connected element + */ + ElementRegistry.prototype.updateGraphics = function(filter, gfx, secondary) { + var id = filter.id || filter; + + var container = this._elements[id]; + + if (secondary) { + container.secondaryGfx = gfx; + } else { + container.gfx = gfx; + } + + if (gfx) { + attr$1(gfx, ELEMENT_ID, id); + } + + return gfx; + }; + + /** + * Return the model element for a given id or graphics. + * + * @example + * + * elementRegistry.get('SomeElementId_1'); + * elementRegistry.get(gfx); + * + * + * @param {string|SVGElement} filter for selecting the element + * + * @return {djs.model.Base} + */ + ElementRegistry.prototype.get = function(filter) { + var id; + + if (typeof filter === 'string') { + id = filter; + } else { + id = filter && attr$1(filter, ELEMENT_ID); + } + + var container = this._elements[id]; + return container && container.element; + }; + + /** + * Return all elements that match a given filter function. + * + * @param {Function} fn + * + * @return {Array} + */ + ElementRegistry.prototype.filter = function(fn) { + + var filtered = []; + + this.forEach(function(element, gfx) { + if (fn(element, gfx)) { + filtered.push(element); + } + }); + + return filtered; + }; + + /** + * Return the first element that satisfies the provided testing function. + * + * @param {Function} fn + * + * @return {djs.model.Base} + */ + ElementRegistry.prototype.find = function(fn) { + var map = this._elements, + keys = Object.keys(map); + + for (var i = 0; i < keys.length; i++) { + var id = keys[i], + container = map[id], + element = container.element, + gfx = container.gfx; + + if (fn(element, gfx)) { + return element; + } + } + }; + + /** + * Return all rendered model elements. + * + * @return {Array} + */ + ElementRegistry.prototype.getAll = function() { + return this.filter(function(e) { return e; }); + }; + + /** + * Iterate over all diagram elements. + * + * @param {Function} fn + */ + ElementRegistry.prototype.forEach = function(fn) { + + var map = this._elements; + + Object.keys(map).forEach(function(id) { + var container = map[id], + element = container.element, + gfx = container.gfx; + + return fn(element, gfx); + }); + }; + + /** + * Return the graphical representation of an element or its id. + * + * @example + * elementRegistry.getGraphics('SomeElementId_1'); + * elementRegistry.getGraphics(rootElement); // + * + * elementRegistry.getGraphics(rootElement, true); // + * + * + * @param {string|djs.model.Base} filter + * @param {boolean} [secondary=false] whether to return the secondary connected element + * + * @return {SVGElement} + */ + ElementRegistry.prototype.getGraphics = function(filter, secondary) { + var id = filter.id || filter; + + var container = this._elements[id]; + return container && (secondary ? container.secondaryGfx : container.gfx); + }; + + /** + * Validate the suitability of the given id and signals a problem + * with an exception. + * + * @param {string} id + * + * @throws {Error} if id is empty or already assigned + */ + ElementRegistry.prototype._validateId = function(id) { + if (!id) { + throw new Error('element must have an id'); + } + + if (this._elements[id]) { + throw new Error('element with id ' + id + ' already added'); + } + }; + + var objectRefs = {exports: {}}; + + var collection = {}; + + /** + * An empty collection stub. Use {@link RefsCollection.extend} to extend a + * collection with ref semantics. + * + * @class RefsCollection + */ + + /** + * Extends a collection with {@link Refs} aware methods + * + * @memberof RefsCollection + * @static + * + * @param {Array} collection + * @param {Refs} refs instance + * @param {Object} property represented by the collection + * @param {Object} target object the collection is attached to + * + * @return {RefsCollection} the extended array + */ + function extend(collection, refs, property, target) { + + var inverseProperty = property.inverse; + + /** + * Removes the given element from the array and returns it. + * + * @method RefsCollection#remove + * + * @param {Object} element the element to remove + */ + Object.defineProperty(collection, 'remove', { + value: function(element) { + var idx = this.indexOf(element); + if (idx !== -1) { + this.splice(idx, 1); + + // unset inverse + refs.unset(element, inverseProperty, target); + } + + return element; + } + }); + + /** + * Returns true if the collection contains the given element + * + * @method RefsCollection#contains + * + * @param {Object} element the element to check for + */ + Object.defineProperty(collection, 'contains', { + value: function(element) { + return this.indexOf(element) !== -1; + } + }); + + /** + * Adds an element to the array, unless it exists already (set semantics). + * + * @method RefsCollection#add + * + * @param {Object} element the element to add + * @param {Number} optional index to add element to + * (possibly moving other elements around) + */ + Object.defineProperty(collection, 'add', { + value: function(element, idx) { + + var currentIdx = this.indexOf(element); + + if (typeof idx === 'undefined') { + + if (currentIdx !== -1) { + // element already in collection (!) + return; + } + + // add to end of array, as no idx is specified + idx = this.length; + } + + // handle already in collection + if (currentIdx !== -1) { + + // remove element from currentIdx + this.splice(currentIdx, 1); + } + + // add element at idx + this.splice(idx, 0, element); + + if (currentIdx === -1) { + // set inverse, unless element was + // in collection already + refs.set(element, inverseProperty, target); + } + } + }); + + // a simple marker, identifying this element + // as being a refs collection + Object.defineProperty(collection, '__refs_collection', { + value: true + }); + + return collection; + } + + + function isExtended(collection) { + return collection.__refs_collection === true; + } + + collection.extend = extend; + + collection.isExtended = isExtended; + + var Collection = collection; + + function hasOwnProperty$1(e, property) { + return Object.prototype.hasOwnProperty.call(e, property.name || property); + } + + function defineCollectionProperty(ref, property, target) { + + var collection = Collection.extend(target[property.name] || [], ref, property, target); + + Object.defineProperty(target, property.name, { + enumerable: property.enumerable, + value: collection + }); + + if (collection.length) { + + collection.forEach(function(o) { + ref.set(o, property.inverse, target); + }); + } + } + + + function defineProperty$1(ref, property, target) { + + var inverseProperty = property.inverse; + + var _value = target[property.name]; + + Object.defineProperty(target, property.name, { + configurable: property.configurable, + enumerable: property.enumerable, + + get: function() { + return _value; + }, + + set: function(value) { + + // return if we already performed all changes + if (value === _value) { + return; + } + + var old = _value; + + // temporary set null + _value = null; + + if (old) { + ref.unset(old, inverseProperty, target); + } + + // set new value + _value = value; + + // set inverse value + ref.set(_value, inverseProperty, target); + } + }); + + } + + /** + * Creates a new references object defining two inversly related + * attribute descriptors a and b. + * + *

        + * When bound to an object using {@link Refs#bind} the references + * get activated and ensure that add and remove operations are applied + * reversely, too. + *

        + * + *

        + * For attributes represented as collections {@link Refs} provides the + * {@link RefsCollection#add}, {@link RefsCollection#remove} and {@link RefsCollection#contains} extensions + * that must be used to properly hook into the inverse change mechanism. + *

        + * + * @class Refs + * + * @classdesc A bi-directional reference between two attributes. + * + * @param {Refs.AttributeDescriptor} a property descriptor + * @param {Refs.AttributeDescriptor} b property descriptor + * + * @example + * + * var refs = Refs({ name: 'wheels', collection: true, enumerable: true }, { name: 'car' }); + * + * var car = { name: 'toyota' }; + * var wheels = [{ pos: 'front-left' }, { pos: 'front-right' }]; + * + * refs.bind(car, 'wheels'); + * + * car.wheels // [] + * car.wheels.add(wheels[0]); + * car.wheels.add(wheels[1]); + * + * car.wheels // [{ pos: 'front-left' }, { pos: 'front-right' }] + * + * wheels[0].car // { name: 'toyota' }; + * car.wheels.remove(wheels[0]); + * + * wheels[0].car // undefined + */ + function Refs$1(a, b) { + + if (!(this instanceof Refs$1)) { + return new Refs$1(a, b); + } + + // link + a.inverse = b; + b.inverse = a; + + this.props = {}; + this.props[a.name] = a; + this.props[b.name] = b; + } + + /** + * Binds one side of a bi-directional reference to a + * target object. + * + * @memberOf Refs + * + * @param {Object} target + * @param {String} property + */ + Refs$1.prototype.bind = function(target, property) { + if (typeof property === 'string') { + if (!this.props[property]) { + throw new Error('no property <' + property + '> in ref'); + } + property = this.props[property]; + } + + if (property.collection) { + defineCollectionProperty(this, property, target); + } else { + defineProperty$1(this, property, target); + } + }; + + Refs$1.prototype.ensureRefsCollection = function(target, property) { + + var collection = target[property.name]; + + if (!Collection.isExtended(collection)) { + defineCollectionProperty(this, property, target); + } + + return collection; + }; + + Refs$1.prototype.ensureBound = function(target, property) { + if (!hasOwnProperty$1(target, property)) { + this.bind(target, property); + } + }; + + Refs$1.prototype.unset = function(target, property, value) { + + if (target) { + this.ensureBound(target, property); + + if (property.collection) { + this.ensureRefsCollection(target, property).remove(value); + } else { + target[property.name] = undefined; + } + } + }; + + Refs$1.prototype.set = function(target, property, value) { + + if (target) { + this.ensureBound(target, property); + + if (property.collection) { + this.ensureRefsCollection(target, property).add(value); + } else { + target[property.name] = value; + } + } + }; + + var refs = Refs$1; + + (function (module) { + module.exports = refs; + + module.exports.Collection = collection; + } (objectRefs)); + + var Refs = /*@__PURE__*/getDefaultExportFromCjs(objectRefs.exports); + + var parentRefs = new Refs({ name: 'children', enumerable: true, collection: true }, { name: 'parent' }), + labelRefs = new Refs({ name: 'labels', enumerable: true, collection: true }, { name: 'labelTarget' }), + attacherRefs = new Refs({ name: 'attachers', collection: true }, { name: 'host' }), + outgoingRefs = new Refs({ name: 'outgoing', collection: true }, { name: 'source' }), + incomingRefs = new Refs({ name: 'incoming', collection: true }, { name: 'target' }); + + /** + * @namespace djs.model + */ + + /** + * @memberOf djs.model + */ + + /** + * The basic graphical representation + * + * @class + * + * @abstract + */ + function Base$1() { + + /** + * The object that backs up the shape + * + * @name Base#businessObject + * @type Object + */ + Object.defineProperty(this, 'businessObject', { + writable: true + }); + + + /** + * Single label support, will mapped to multi label array + * + * @name Base#label + * @type Object + */ + Object.defineProperty(this, 'label', { + get: function() { + return this.labels[0]; + }, + set: function(newLabel) { + + var label = this.label, + labels = this.labels; + + if (!newLabel && label) { + labels.remove(label); + } else { + labels.add(newLabel, 0); + } + } + }); + + /** + * The parent shape + * + * @name Base#parent + * @type Shape + */ + parentRefs.bind(this, 'parent'); + + /** + * The list of labels + * + * @name Base#labels + * @type Label + */ + labelRefs.bind(this, 'labels'); + + /** + * The list of outgoing connections + * + * @name Base#outgoing + * @type Array + */ + outgoingRefs.bind(this, 'outgoing'); + + /** + * The list of incoming connections + * + * @name Base#incoming + * @type Array + */ + incomingRefs.bind(this, 'incoming'); + } + + + /** + * A graphical object + * + * @class + * @constructor + * + * @extends Base + */ + function Shape() { + Base$1.call(this); + + /** + * Indicates frame shapes + * + * @name Shape#isFrame + * @type boolean + */ + + /** + * The list of children + * + * @name Shape#children + * @type Array + */ + parentRefs.bind(this, 'children'); + + /** + * @name Shape#host + * @type Shape + */ + attacherRefs.bind(this, 'host'); + + /** + * @name Shape#attachers + * @type Shape + */ + attacherRefs.bind(this, 'attachers'); + } + + e(Shape, Base$1); + + + /** + * A root graphical object + * + * @class + * @constructor + * + * @extends Shape + */ + function Root() { + Shape.call(this); + } + + e(Root, Shape); + + + /** + * A label for an element + * + * @class + * @constructor + * + * @extends Shape + */ + function Label() { + Shape.call(this); + + /** + * The labeled element + * + * @name Label#labelTarget + * @type Base + */ + labelRefs.bind(this, 'labelTarget'); + } + + e(Label, Shape); + + + /** + * A connection between two elements + * + * @class + * @constructor + * + * @extends Base + */ + function Connection() { + Base$1.call(this); + + /** + * The element this connection originates from + * + * @name Connection#source + * @type Base + */ + outgoingRefs.bind(this, 'source'); + + /** + * The element this connection points to + * + * @name Connection#target + * @type Base + */ + incomingRefs.bind(this, 'target'); + } + + e(Connection, Base$1); + + + var types$6 = { + connection: Connection, + shape: Shape, + label: Label, + root: Root + }; + + /** + * Creates a new model element of the specified type + * + * @method create + * + * @example + * + * var shape1 = Model.create('shape', { x: 10, y: 10, width: 100, height: 100 }); + * var shape2 = Model.create('shape', { x: 210, y: 210, width: 100, height: 100 }); + * + * var connection = Model.create('connection', { waypoints: [ { x: 110, y: 55 }, {x: 210, y: 55 } ] }); + * + * @param {string} type lower-cased model name + * @param {Object} attrs attributes to initialize the new model instance with + * + * @return {Base} the new model instance + */ + function create(type, attrs) { + var Type = types$6[type]; + if (!Type) { + throw new Error('unknown type: <' + type + '>'); + } + return assign(new Type(), attrs); + } + + /** + * A factory for diagram-js shapes + */ + function ElementFactory() { + this._uid = 12; + } + + + ElementFactory.prototype.createRoot = function(attrs) { + return this.create('root', attrs); + }; + + ElementFactory.prototype.createLabel = function(attrs) { + return this.create('label', attrs); + }; + + ElementFactory.prototype.createShape = function(attrs) { + return this.create('shape', attrs); + }; + + ElementFactory.prototype.createConnection = function(attrs) { + return this.create('connection', attrs); + }; + + /** + * Create a model element with the given type and + * a number of pre-set attributes. + * + * @param {string} type + * @param {Object} attrs + * @return {djs.model.Base} the newly created model instance + */ + ElementFactory.prototype.create = function(type, attrs) { + + attrs = assign({}, attrs || {}); + + if (!attrs.id) { + attrs.id = type + '_' + (this._uid++); + } + + return create(type, attrs); + }; + + var FN_REF = '__fn'; + + var DEFAULT_PRIORITY$1 = 1000; + + var slice = Array.prototype.slice; + + /** + * A general purpose event bus. + * + * This component is used to communicate across a diagram instance. + * Other parts of a diagram can use it to listen to and broadcast events. + * + * + * ## Registering for Events + * + * The event bus provides the {@link EventBus#on} and {@link EventBus#once} + * methods to register for events. {@link EventBus#off} can be used to + * remove event registrations. Listeners receive an instance of {@link Event} + * as the first argument. It allows them to hook into the event execution. + * + * ```javascript + * + * // listen for event + * eventBus.on('foo', function(event) { + * + * // access event type + * event.type; // 'foo' + * + * // stop propagation to other listeners + * event.stopPropagation(); + * + * // prevent event default + * event.preventDefault(); + * }); + * + * // listen for event with custom payload + * eventBus.on('bar', function(event, payload) { + * console.log(payload); + * }); + * + * // listen for event returning value + * eventBus.on('foobar', function(event) { + * + * // stop event propagation + prevent default + * return false; + * + * // stop event propagation + return custom result + * return { + * complex: 'listening result' + * }; + * }); + * + * + * // listen with custom priority (default=1000, higher is better) + * eventBus.on('priorityfoo', 1500, function(event) { + * console.log('invoked first!'); + * }); + * + * + * // listen for event and pass the context (`this`) + * eventBus.on('foobar', function(event) { + * this.foo(); + * }, this); + * ``` + * + * + * ## Emitting Events + * + * Events can be emitted via the event bus using {@link EventBus#fire}. + * + * ```javascript + * + * // false indicates that the default action + * // was prevented by listeners + * if (eventBus.fire('foo') === false) { + * console.log('default has been prevented!'); + * }; + * + * + * // custom args + return value listener + * eventBus.on('sum', function(event, a, b) { + * return a + b; + * }); + * + * // you can pass custom arguments + retrieve result values. + * var sum = eventBus.fire('sum', 1, 2); + * console.log(sum); // 3 + * ``` + */ + function EventBus() { + this._listeners = {}; + + // cleanup on destroy on lowest priority to allow + // message passing until the bitter end + this.on('diagram.destroy', 1, this._destroy, this); + } + + + /** + * Register an event listener for events with the given name. + * + * The callback will be invoked with `event, ...additionalArguments` + * that have been passed to {@link EventBus#fire}. + * + * Returning false from a listener will prevent the events default action + * (if any is specified). To stop an event from being processed further in + * other listeners execute {@link Event#stopPropagation}. + * + * Returning anything but `undefined` from a listener will stop the listener propagation. + * + * @param {string|Array} events + * @param {number} [priority=1000] the priority in which this listener is called, larger is higher + * @param {Function} callback + * @param {Object} [that] Pass context (`this`) to the callback + */ + EventBus.prototype.on = function(events, priority, callback, that) { + + events = isArray$2(events) ? events : [ events ]; + + if (isFunction(priority)) { + that = callback; + callback = priority; + priority = DEFAULT_PRIORITY$1; + } + + if (!isNumber(priority)) { + throw new Error('priority must be a number'); + } + + var actualCallback = callback; + + if (that) { + actualCallback = bind(callback, that); + + // make sure we remember and are able to remove + // bound callbacks via {@link #off} using the original + // callback + actualCallback[FN_REF] = callback[FN_REF] || callback; + } + + var self = this; + + events.forEach(function(e) { + self._addListener(e, { + priority: priority, + callback: actualCallback, + next: null + }); + }); + }; + + + /** + * Register an event listener that is executed only once. + * + * @param {string} event the event name to register for + * @param {number} [priority=1000] the priority in which this listener is called, larger is higher + * @param {Function} callback the callback to execute + * @param {Object} [that] Pass context (`this`) to the callback + */ + EventBus.prototype.once = function(event, priority, callback, that) { + var self = this; + + if (isFunction(priority)) { + that = callback; + callback = priority; + priority = DEFAULT_PRIORITY$1; + } + + if (!isNumber(priority)) { + throw new Error('priority must be a number'); + } + + function wrappedCallback() { + wrappedCallback.__isTomb = true; + + var result = callback.apply(that, arguments); + + self.off(event, wrappedCallback); + + return result; + } + + // make sure we remember and are able to remove + // bound callbacks via {@link #off} using the original + // callback + wrappedCallback[FN_REF] = callback; + + this.on(event, priority, wrappedCallback); + }; + + + /** + * Removes event listeners by event and callback. + * + * If no callback is given, all listeners for a given event name are being removed. + * + * @param {string|Array} events + * @param {Function} [callback] + */ + EventBus.prototype.off = function(events, callback) { + + events = isArray$2(events) ? events : [ events ]; + + var self = this; + + events.forEach(function(event) { + self._removeListener(event, callback); + }); + + }; + + + /** + * Create an EventBus event. + * + * @param {Object} data + * + * @return {Object} event, recognized by the eventBus + */ + EventBus.prototype.createEvent = function(data) { + var event = new InternalEvent(); + + event.init(data); + + return event; + }; + + + /** + * Fires a named event. + * + * @example + * + * // fire event by name + * events.fire('foo'); + * + * // fire event object with nested type + * var event = { type: 'foo' }; + * events.fire(event); + * + * // fire event with explicit type + * var event = { x: 10, y: 20 }; + * events.fire('element.moved', event); + * + * // pass additional arguments to the event + * events.on('foo', function(event, bar) { + * alert(bar); + * }); + * + * events.fire({ type: 'foo' }, 'I am bar!'); + * + * @param {string} [name] the optional event name + * @param {Object} [event] the event object + * @param {...Object} additional arguments to be passed to the callback functions + * + * @return {boolean} the events return value, if specified or false if the + * default action was prevented by listeners + */ + EventBus.prototype.fire = function(type, data) { + var event, + firstListener, + returnValue, + args; + + args = slice.call(arguments); + + if (typeof type === 'object') { + data = type; + type = data.type; + } + + if (!type) { + throw new Error('no event type specified'); + } + + firstListener = this._listeners[type]; + + if (!firstListener) { + return; + } + + // we make sure we fire instances of our home made + // events here. We wrap them only once, though + if (data instanceof InternalEvent) { + + // we are fine, we alread have an event + event = data; + } else { + event = this.createEvent(data); + } + + // ensure we pass the event as the first parameter + args[0] = event; + + // original event type (in case we delegate) + var originalType = event.type; + + // update event type before delegation + if (type !== originalType) { + event.type = type; + } + + try { + returnValue = this._invokeListeners(event, args, firstListener); + } finally { + + // reset event type after delegation + if (type !== originalType) { + event.type = originalType; + } + } + + // set the return value to false if the event default + // got prevented and no other return value exists + if (returnValue === undefined && event.defaultPrevented) { + returnValue = false; + } + + return returnValue; + }; + + + EventBus.prototype.handleError = function(error) { + return this.fire('error', { error: error }) === false; + }; + + + EventBus.prototype._destroy = function() { + this._listeners = {}; + }; + + EventBus.prototype._invokeListeners = function(event, args, listener) { + + var returnValue; + + while (listener) { + + // handle stopped propagation + if (event.cancelBubble) { + break; + } + + returnValue = this._invokeListener(event, args, listener); + + listener = listener.next; + } + + return returnValue; + }; + + EventBus.prototype._invokeListener = function(event, args, listener) { + + var returnValue; + + if (listener.callback.__isTomb) { + return returnValue; + } + + try { + + // returning false prevents the default action + returnValue = invokeFunction(listener.callback, args); + + // stop propagation on return value + if (returnValue !== undefined) { + event.returnValue = returnValue; + event.stopPropagation(); + } + + // prevent default on return false + if (returnValue === false) { + event.preventDefault(); + } + } catch (error) { + if (!this.handleError(error)) { + console.error('unhandled error in event listener', error); + + throw error; + } + } + + return returnValue; + }; + + /* + * Add new listener with a certain priority to the list + * of listeners (for the given event). + * + * The semantics of listener registration / listener execution are + * first register, first serve: New listeners will always be inserted + * after existing listeners with the same priority. + * + * Example: Inserting two listeners with priority 1000 and 1300 + * + * * before: [ 1500, 1500, 1000, 1000 ] + * * after: [ 1500, 1500, (new=1300), 1000, 1000, (new=1000) ] + * + * @param {string} event + * @param {Object} listener { priority, callback } + */ + EventBus.prototype._addListener = function(event, newListener) { + + var listener = this._getListeners(event), + previousListener; + + // no prior listeners + if (!listener) { + this._setListeners(event, newListener); + + return; + } + + // ensure we order listeners by priority from + // 0 (high) to n > 0 (low) + while (listener) { + + if (listener.priority < newListener.priority) { + + newListener.next = listener; + + if (previousListener) { + previousListener.next = newListener; + } else { + this._setListeners(event, newListener); + } + + return; + } + + previousListener = listener; + listener = listener.next; + } + + // add new listener to back + previousListener.next = newListener; + }; + + + EventBus.prototype._getListeners = function(name) { + return this._listeners[name]; + }; + + EventBus.prototype._setListeners = function(name, listener) { + this._listeners[name] = listener; + }; + + EventBus.prototype._removeListener = function(event, callback) { + + var listener = this._getListeners(event), + nextListener, + previousListener, + listenerCallback; + + if (!callback) { + + // clear listeners + this._setListeners(event, null); + + return; + } + + while (listener) { + + nextListener = listener.next; + + listenerCallback = listener.callback; + + if (listenerCallback === callback || listenerCallback[FN_REF] === callback) { + if (previousListener) { + previousListener.next = nextListener; + } else { + + // new first listener + this._setListeners(event, nextListener); + } + } + + previousListener = listener; + listener = nextListener; + } + }; + + /** + * A event that is emitted via the event bus. + */ + function InternalEvent() { } + + InternalEvent.prototype.stopPropagation = function() { + this.cancelBubble = true; + }; + + InternalEvent.prototype.preventDefault = function() { + this.defaultPrevented = true; + }; + + InternalEvent.prototype.init = function(data) { + assign(this, data || {}); + }; + + + /** + * Invoke function. Be fast... + * + * @param {Function} fn + * @param {Array} args + * + * @return {Any} + */ + function invokeFunction(fn, args) { + return fn.apply(null, args); + } + + /** + * SVGs for elements are generated by the {@link GraphicsFactory}. + * + * This utility gives quick access to the important semantic + * parts of an element. + */ + + /** + * Returns the visual part of a diagram element + * + * @param {Snap} gfx + * + * @return {Snap} + */ + function getVisual(gfx) { + return gfx.childNodes[0]; + } + + /** + * Returns the children for a given diagram element. + * + * @param {Snap} gfx + * @return {Snap} + */ + function getChildren(gfx) { + return gfx.parentNode.childNodes[1]; + } + + /** + * A factory that creates graphical elements + * + * @param {EventBus} eventBus + * @param {ElementRegistry} elementRegistry + */ + function GraphicsFactory(eventBus, elementRegistry) { + this._eventBus = eventBus; + this._elementRegistry = elementRegistry; + } + + GraphicsFactory.$inject = [ 'eventBus' , 'elementRegistry' ]; + + + GraphicsFactory.prototype._getChildrenContainer = function(element) { + + var gfx = this._elementRegistry.getGraphics(element); + + var childrenGfx; + + // root element + if (!element.parent) { + childrenGfx = gfx; + } else { + childrenGfx = getChildren(gfx); + if (!childrenGfx) { + childrenGfx = create$1('g'); + classes$1(childrenGfx).add('djs-children'); + + append(gfx.parentNode, childrenGfx); + } + } + + return childrenGfx; + }; + + /** + * Clears the graphical representation of the element and returns the + * cleared visual (the element). + */ + GraphicsFactory.prototype._clear = function(gfx) { + var visual = getVisual(gfx); + + clear(visual); + + return visual; + }; + + /** + * Creates a gfx container for shapes and connections + * + * The layout is as follows: + * + * + * + * + * + * + * + * + * + * + * + * + * + * @param {string} type the type of the element, i.e. shape | connection + * @param {SVGElement} [childrenGfx] + * @param {number} [parentIndex] position to create container in parent + * @param {boolean} [isFrame] is frame element + * + * @return {SVGElement} + */ + GraphicsFactory.prototype._createContainer = function( + type, childrenGfx, parentIndex, isFrame + ) { + var outerGfx = create$1('g'); + classes$1(outerGfx).add('djs-group'); + + // insert node at position + if (typeof parentIndex !== 'undefined') { + prependTo(outerGfx, childrenGfx, childrenGfx.childNodes[parentIndex]); + } else { + append(childrenGfx, outerGfx); + } + + var gfx = create$1('g'); + classes$1(gfx).add('djs-element'); + classes$1(gfx).add('djs-' + type); + + if (isFrame) { + classes$1(gfx).add('djs-frame'); + } + + append(outerGfx, gfx); + + // create visual + var visual = create$1('g'); + classes$1(visual).add('djs-visual'); + + append(gfx, visual); + + return gfx; + }; + + GraphicsFactory.prototype.create = function(type, element, parentIndex) { + var childrenGfx = this._getChildrenContainer(element.parent); + return this._createContainer(type, childrenGfx, parentIndex, isFrameElement(element)); + }; + + GraphicsFactory.prototype.updateContainments = function(elements) { + + var self = this, + elementRegistry = this._elementRegistry, + parents; + + parents = reduce(elements, function(map, e) { + + if (e.parent) { + map[e.parent.id] = e.parent; + } + + return map; + }, {}); + + // update all parents of changed and reorganized their children + // in the correct order (as indicated in our model) + forEach$1(parents, function(parent) { + + var children = parent.children; + + if (!children) { + return; + } + + var childrenGfx = self._getChildrenContainer(parent); + + forEach$1(children.slice().reverse(), function(child) { + var childGfx = elementRegistry.getGraphics(child); + + prependTo(childGfx.parentNode, childrenGfx); + }); + }); + }; + + GraphicsFactory.prototype.drawShape = function(visual, element) { + var eventBus = this._eventBus; + + return eventBus.fire('render.shape', { gfx: visual, element: element }); + }; + + GraphicsFactory.prototype.getShapePath = function(element) { + var eventBus = this._eventBus; + + return eventBus.fire('render.getShapePath', element); + }; + + GraphicsFactory.prototype.drawConnection = function(visual, element) { + var eventBus = this._eventBus; + + return eventBus.fire('render.connection', { gfx: visual, element: element }); + }; + + GraphicsFactory.prototype.getConnectionPath = function(waypoints) { + var eventBus = this._eventBus; + + return eventBus.fire('render.getConnectionPath', waypoints); + }; + + GraphicsFactory.prototype.update = function(type, element, gfx) { + + // do NOT update root element + if (!element.parent) { + return; + } + + var visual = this._clear(gfx); + + // redraw + if (type === 'shape') { + this.drawShape(visual, element); + + // update positioning + translate$1(gfx, element.x, element.y); + } else + if (type === 'connection') { + this.drawConnection(visual, element); + } else { + throw new Error('unknown type: ' + type); + } + + if (element.hidden) { + attr$1(gfx, 'display', 'none'); + } else { + attr$1(gfx, 'display', 'block'); + } + }; + + GraphicsFactory.prototype.remove = function(element) { + var gfx = this._elementRegistry.getGraphics(element); + + // remove + remove$2(gfx.parentNode); + }; + + + // helpers ////////// + + function prependTo(newNode, parentNode, siblingNode) { + var node = siblingNode || parentNode.firstChild; + + // do not prepend node to itself to prevent IE from crashing + // https://github.com/bpmn-io/bpmn-js/issues/746 + if (newNode === node) { + return; + } + + parentNode.insertBefore(newNode, node); + } + + var CoreModule = { + __depends__: [ DrawModule ], + __init__: [ 'canvas' ], + canvas: [ 'type', Canvas ], + elementRegistry: [ 'type', ElementRegistry ], + elementFactory: [ 'type', ElementFactory ], + eventBus: [ 'type', EventBus ], + graphicsFactory: [ 'type', GraphicsFactory ] + }; + + /** + * @typedef { import('didi').ModuleDeclaration } Module + */ + + /** + * Bootstrap an injector from a list of modules, instantiating a number of default components + * + * @param {Array} modules + * + * @return {Injector} a injector to use to access the components + */ + function bootstrap(modules) { + var injector = new Injector(modules); + + injector.init(); + + return injector; + } + + /** + * Creates an injector from passed options. + * + * @param {Object} options + * @return {Injector} + */ + function createInjector(options) { + + options = options || {}; + + var configModule = { + 'config': [ 'value', options ] + }; + + var modules = [ configModule, CoreModule ].concat(options.modules || []); + + return bootstrap(modules); + } + + + /** + * The main diagram-js entry point that bootstraps the diagram with the given + * configuration. + * + * To register extensions with the diagram, pass them as Array to the constructor. + * + * @class djs.Diagram + * @memberOf djs + * @constructor + * + * @example + * + * Creating a plug-in that logs whenever a shape is added to the canvas. + * + * // plug-in implemenentation + * function MyLoggingPlugin(eventBus) { + * eventBus.on('shape.added', function(event) { + * console.log('shape ', event.shape, ' was added to the diagram'); + * }); + * } + * + * // export as module + * export default { + * __init__: [ 'myLoggingPlugin' ], + * myLoggingPlugin: [ 'type', MyLoggingPlugin ] + * }; + * + * + * // instantiate the diagram with the new plug-in + * + * import MyLoggingModule from 'path-to-my-logging-plugin'; + * + * var diagram = new Diagram({ + * modules: [ + * MyLoggingModule + * ] + * }); + * + * diagram.invoke([ 'canvas', function(canvas) { + * // add shape to drawing canvas + * canvas.addShape({ x: 10, y: 10 }); + * }); + * + * // 'shape ... was added to the diagram' logged to console + * + * @param {Object} options + * @param {Array} [options.modules] external modules to instantiate with the diagram + * @param {Injector} [injector] an (optional) injector to bootstrap the diagram with + */ + function Diagram(options, injector) { + + // create injector unless explicitly specified + this.injector = injector = injector || createInjector(options); + + // API + + /** + * Resolves a diagram service + * + * @method Diagram#get + * + * @param {string} name the name of the diagram service to be retrieved + * @param {boolean} [strict=true] if false, resolve missing services to null + */ + this.get = injector.get; + + /** + * Executes a function into which diagram services are injected + * + * @method Diagram#invoke + * + * @param {Function|Object[]} fn the function to resolve + * @param {Object} locals a number of locals to use to resolve certain dependencies + */ + this.invoke = injector.invoke; + + // init + + // indicate via event + + + /** + * An event indicating that all plug-ins are loaded. + * + * Use this event to fire other events to interested plug-ins + * + * @memberOf Diagram + * + * @event diagram.init + * + * @example + * + * eventBus.on('diagram.init', function() { + * eventBus.fire('my-custom-event', { foo: 'BAR' }); + * }); + * + * @type {Object} + */ + this.get('eventBus').fire('diagram.init'); + } + + + /** + * Destroys the diagram + * + * @method Diagram#destroy + */ + Diagram.prototype.destroy = function() { + this.get('eventBus').fire('diagram.destroy'); + }; + + /** + * Clear the diagram, removing all contents. + */ + Diagram.prototype.clear = function() { + this.get('eventBus').fire('diagram.clear'); + }; + + /** + * Moddle base element. + */ + function Base() { } + + Base.prototype.get = function(name) { + return this.$model.properties.get(this, name); + }; + + Base.prototype.set = function(name, value) { + this.$model.properties.set(this, name, value); + }; + + /** + * A model element factory. + * + * @param {Moddle} model + * @param {Properties} properties + */ + function Factory(model, properties) { + this.model = model; + this.properties = properties; + } + + + Factory.prototype.createType = function(descriptor) { + + var model = this.model; + + var props = this.properties, + prototype = Object.create(Base.prototype); + + // initialize default values + forEach$1(descriptor.properties, function(p) { + if (!p.isMany && p.default !== undefined) { + prototype[p.name] = p.default; + } + }); + + props.defineModel(prototype, model); + props.defineDescriptor(prototype, descriptor); + + var name = descriptor.ns.name; + + /** + * The new type constructor + */ + function ModdleElement(attrs) { + props.define(this, '$type', { value: name, enumerable: true }); + props.define(this, '$attrs', { value: {} }); + props.define(this, '$parent', { writable: true }); + + forEach$1(attrs, bind(function(val, key) { + this.set(key, val); + }, this)); + } + + ModdleElement.prototype = prototype; + + ModdleElement.hasType = prototype.$instanceOf = this.model.hasType; + + // static links + props.defineModel(ModdleElement, model); + props.defineDescriptor(ModdleElement, descriptor); + + return ModdleElement; + }; + + /** + * Built-in moddle types + */ + var BUILTINS = { + String: true, + Boolean: true, + Integer: true, + Real: true, + Element: true + }; + + /** + * Converters for built in types from string representations + */ + var TYPE_CONVERTERS = { + String: function(s) { return s; }, + Boolean: function(s) { return s === 'true'; }, + Integer: function(s) { return parseInt(s, 10); }, + Real: function(s) { return parseFloat(s); } + }; + + /** + * Convert a type to its real representation + */ + function coerceType(type, value) { + + var converter = TYPE_CONVERTERS[type]; + + if (converter) { + return converter(value); + } else { + return value; + } + } + + /** + * Return whether the given type is built-in + */ + function isBuiltIn(type) { + return !!BUILTINS[type]; + } + + /** + * Return whether the given type is simple + */ + function isSimple(type) { + return !!TYPE_CONVERTERS[type]; + } + + /** + * Parses a namespaced attribute name of the form (ns:)localName to an object, + * given a default prefix to assume in case no explicit namespace is given. + * + * @param {String} name + * @param {String} [defaultPrefix] the default prefix to take, if none is present. + * + * @return {Object} the parsed name + */ + function parseName(name, defaultPrefix) { + var parts = name.split(/:/), + localName, prefix; + + // no prefix (i.e. only local name) + if (parts.length === 1) { + localName = name; + prefix = defaultPrefix; + } else + // prefix + local name + if (parts.length === 2) { + localName = parts[1]; + prefix = parts[0]; + } else { + throw new Error('expected or , got ' + name); + } + + name = (prefix ? prefix + ':' : '') + localName; + + return { + name: name, + prefix: prefix, + localName: localName + }; + } + + /** + * A utility to build element descriptors. + */ + function DescriptorBuilder(nameNs) { + this.ns = nameNs; + this.name = nameNs.name; + this.allTypes = []; + this.allTypesByName = {}; + this.properties = []; + this.propertiesByName = {}; + } + + + DescriptorBuilder.prototype.build = function() { + return pick(this, [ + 'ns', + 'name', + 'allTypes', + 'allTypesByName', + 'properties', + 'propertiesByName', + 'bodyProperty', + 'idProperty' + ]); + }; + + /** + * Add property at given index. + * + * @param {Object} p + * @param {Number} [idx] + * @param {Boolean} [validate=true] + */ + DescriptorBuilder.prototype.addProperty = function(p, idx, validate) { + + if (typeof idx === 'boolean') { + validate = idx; + idx = undefined; + } + + this.addNamedProperty(p, validate !== false); + + var properties = this.properties; + + if (idx !== undefined) { + properties.splice(idx, 0, p); + } else { + properties.push(p); + } + }; + + + DescriptorBuilder.prototype.replaceProperty = function(oldProperty, newProperty, replace) { + var oldNameNs = oldProperty.ns; + + var props = this.properties, + propertiesByName = this.propertiesByName, + rename = oldProperty.name !== newProperty.name; + + if (oldProperty.isId) { + if (!newProperty.isId) { + throw new Error( + 'property <' + newProperty.ns.name + '> must be id property ' + + 'to refine <' + oldProperty.ns.name + '>'); + } + + this.setIdProperty(newProperty, false); + } + + if (oldProperty.isBody) { + + if (!newProperty.isBody) { + throw new Error( + 'property <' + newProperty.ns.name + '> must be body property ' + + 'to refine <' + oldProperty.ns.name + '>'); + } + + // TODO: Check compatibility + this.setBodyProperty(newProperty, false); + } + + // validate existence and get location of old property + var idx = props.indexOf(oldProperty); + if (idx === -1) { + throw new Error('property <' + oldNameNs.name + '> not found in property list'); + } + + // remove old property + props.splice(idx, 1); + + // replacing the named property is intentional + // + // * validate only if this is a "rename" operation + // * add at specific index unless we "replace" + // + this.addProperty(newProperty, replace ? undefined : idx, rename); + + // make new property available under old name + propertiesByName[oldNameNs.name] = propertiesByName[oldNameNs.localName] = newProperty; + }; + + + DescriptorBuilder.prototype.redefineProperty = function(p, targetPropertyName, replace) { + + var nsPrefix = p.ns.prefix; + var parts = targetPropertyName.split('#'); + + var name = parseName(parts[0], nsPrefix); + var attrName = parseName(parts[1], name.prefix).name; + + var redefinedProperty = this.propertiesByName[attrName]; + if (!redefinedProperty) { + throw new Error('refined property <' + attrName + '> not found'); + } else { + this.replaceProperty(redefinedProperty, p, replace); + } + + delete p.redefines; + }; + + DescriptorBuilder.prototype.addNamedProperty = function(p, validate) { + var ns = p.ns, + propsByName = this.propertiesByName; + + if (validate) { + this.assertNotDefined(p, ns.name); + this.assertNotDefined(p, ns.localName); + } + + propsByName[ns.name] = propsByName[ns.localName] = p; + }; + + DescriptorBuilder.prototype.removeNamedProperty = function(p) { + var ns = p.ns, + propsByName = this.propertiesByName; + + delete propsByName[ns.name]; + delete propsByName[ns.localName]; + }; + + DescriptorBuilder.prototype.setBodyProperty = function(p, validate) { + + if (validate && this.bodyProperty) { + throw new Error( + 'body property defined multiple times ' + + '(<' + this.bodyProperty.ns.name + '>, <' + p.ns.name + '>)'); + } + + this.bodyProperty = p; + }; + + DescriptorBuilder.prototype.setIdProperty = function(p, validate) { + + if (validate && this.idProperty) { + throw new Error( + 'id property defined multiple times ' + + '(<' + this.idProperty.ns.name + '>, <' + p.ns.name + '>)'); + } + + this.idProperty = p; + }; + + DescriptorBuilder.prototype.assertNotDefined = function(p, name) { + var propertyName = p.name, + definedProperty = this.propertiesByName[propertyName]; + + if (definedProperty) { + throw new Error( + 'property <' + propertyName + '> already defined; ' + + 'override of <' + definedProperty.definedBy.ns.name + '#' + definedProperty.ns.name + '> by ' + + '<' + p.definedBy.ns.name + '#' + p.ns.name + '> not allowed without redefines'); + } + }; + + DescriptorBuilder.prototype.hasProperty = function(name) { + return this.propertiesByName[name]; + }; + + DescriptorBuilder.prototype.addTrait = function(t, inherited) { + + var typesByName = this.allTypesByName, + types = this.allTypes; + + var typeName = t.name; + + if (typeName in typesByName) { + return; + } + + forEach$1(t.properties, bind(function(p) { + + // clone property to allow extensions + p = assign({}, p, { + name: p.ns.localName, + inherited: inherited + }); + + Object.defineProperty(p, 'definedBy', { + value: t + }); + + var replaces = p.replaces, + redefines = p.redefines; + + // add replace/redefine support + if (replaces || redefines) { + this.redefineProperty(p, replaces || redefines, replaces); + } else { + if (p.isBody) { + this.setBodyProperty(p); + } + if (p.isId) { + this.setIdProperty(p); + } + this.addProperty(p); + } + }, this)); + + types.push(t); + typesByName[typeName] = t; + }; + + /** + * A registry of Moddle packages. + * + * @param {Array} packages + * @param {Properties} properties + */ + function Registry(packages, properties) { + this.packageMap = {}; + this.typeMap = {}; + + this.packages = []; + + this.properties = properties; + + forEach$1(packages, bind(this.registerPackage, this)); + } + + + Registry.prototype.getPackage = function(uriOrPrefix) { + return this.packageMap[uriOrPrefix]; + }; + + Registry.prototype.getPackages = function() { + return this.packages; + }; + + + Registry.prototype.registerPackage = function(pkg) { + + // copy package + pkg = assign({}, pkg); + + var pkgMap = this.packageMap; + + ensureAvailable(pkgMap, pkg, 'prefix'); + ensureAvailable(pkgMap, pkg, 'uri'); + + // register types + forEach$1(pkg.types, bind(function(descriptor) { + this.registerType(descriptor, pkg); + }, this)); + + pkgMap[pkg.uri] = pkgMap[pkg.prefix] = pkg; + this.packages.push(pkg); + }; + + + /** + * Register a type from a specific package with us + */ + Registry.prototype.registerType = function(type, pkg) { + + type = assign({}, type, { + superClass: (type.superClass || []).slice(), + extends: (type.extends || []).slice(), + properties: (type.properties || []).slice(), + meta: assign((type.meta || {})) + }); + + var ns = parseName(type.name, pkg.prefix), + name = ns.name, + propertiesByName = {}; + + // parse properties + forEach$1(type.properties, bind(function(p) { + + // namespace property names + var propertyNs = parseName(p.name, ns.prefix), + propertyName = propertyNs.name; + + // namespace property types + if (!isBuiltIn(p.type)) { + p.type = parseName(p.type, propertyNs.prefix).name; + } + + assign(p, { + ns: propertyNs, + name: propertyName + }); + + propertiesByName[propertyName] = p; + }, this)); + + // update ns + name + assign(type, { + ns: ns, + name: name, + propertiesByName: propertiesByName + }); + + forEach$1(type.extends, bind(function(extendsName) { + var extended = this.typeMap[extendsName]; + + extended.traits = extended.traits || []; + extended.traits.push(name); + }, this)); + + // link to package + this.definePackage(type, pkg); + + // register + this.typeMap[name] = type; + }; + + + /** + * Traverse the type hierarchy from bottom to top, + * calling iterator with (type, inherited) for all elements in + * the inheritance chain. + * + * @param {Object} nsName + * @param {Function} iterator + * @param {Boolean} [trait=false] + */ + Registry.prototype.mapTypes = function(nsName, iterator, trait) { + + var type = isBuiltIn(nsName.name) ? { name: nsName.name } : this.typeMap[nsName.name]; + + var self = this; + + /** + * Traverse the selected trait. + * + * @param {String} cls + */ + function traverseTrait(cls) { + return traverseSuper(cls, true); + } + + /** + * Traverse the selected super type or trait + * + * @param {String} cls + * @param {Boolean} [trait=false] + */ + function traverseSuper(cls, trait) { + var parentNs = parseName(cls, isBuiltIn(cls) ? '' : nsName.prefix); + self.mapTypes(parentNs, iterator, trait); + } + + if (!type) { + throw new Error('unknown type <' + nsName.name + '>'); + } + + forEach$1(type.superClass, trait ? traverseTrait : traverseSuper); + + // call iterator with (type, inherited=!trait) + iterator(type, !trait); + + forEach$1(type.traits, traverseTrait); + }; + + + /** + * Returns the effective descriptor for a type. + * + * @param {String} type the namespaced name (ns:localName) of the type + * + * @return {Descriptor} the resulting effective descriptor + */ + Registry.prototype.getEffectiveDescriptor = function(name) { + + var nsName = parseName(name); + + var builder = new DescriptorBuilder(nsName); + + this.mapTypes(nsName, function(type, inherited) { + builder.addTrait(type, inherited); + }); + + var descriptor = builder.build(); + + // define package link + this.definePackage(descriptor, descriptor.allTypes[descriptor.allTypes.length - 1].$pkg); + + return descriptor; + }; + + + Registry.prototype.definePackage = function(target, pkg) { + this.properties.define(target, '$pkg', { value: pkg }); + }; + + + + ///////// helpers //////////////////////////// + + function ensureAvailable(packageMap, pkg, identifierKey) { + + var value = pkg[identifierKey]; + + if (value in packageMap) { + throw new Error('package with ' + identifierKey + ' <' + value + '> already defined'); + } + } + + /** + * A utility that gets and sets properties of model elements. + * + * @param {Model} model + */ + function Properties(model) { + this.model = model; + } + + + /** + * Sets a named property on the target element. + * If the value is undefined, the property gets deleted. + * + * @param {Object} target + * @param {String} name + * @param {Object} value + */ + Properties.prototype.set = function(target, name, value) { + + var property = this.model.getPropertyDescriptor(target, name); + + var propertyName = property && property.name; + + if (isUndefined(value)) { + // unset the property, if the specified value is undefined; + // delete from $attrs (for extensions) or the target itself + if (property) { + delete target[propertyName]; + } else { + delete target.$attrs[name]; + } + } else { + // set the property, defining well defined properties on the fly + // or simply updating them in target.$attrs (for extensions) + if (property) { + if (propertyName in target) { + target[propertyName] = value; + } else { + defineProperty(target, property, value); + } + } else { + target.$attrs[name] = value; + } + } + }; + + /** + * Returns the named property of the given element + * + * @param {Object} target + * @param {String} name + * + * @return {Object} + */ + Properties.prototype.get = function(target, name) { + + var property = this.model.getPropertyDescriptor(target, name); + + if (!property) { + return target.$attrs[name]; + } + + var propertyName = property.name; + + // check if access to collection property and lazily initialize it + if (!target[propertyName] && property.isMany) { + defineProperty(target, property, []); + } + + return target[propertyName]; + }; + + + /** + * Define a property on the target element + * + * @param {Object} target + * @param {String} name + * @param {Object} options + */ + Properties.prototype.define = function(target, name, options) { + + if (!options.writable) { + + var value = options.value; + + // use getters for read-only variables to support ES6 proxies + // cf. https://github.com/bpmn-io/internal-docs/issues/386 + options = assign({}, options, { + get: function() { return value; } + }); + + delete options.value; + } + + Object.defineProperty(target, name, options); + }; + + + /** + * Define the descriptor for an element + */ + Properties.prototype.defineDescriptor = function(target, descriptor) { + this.define(target, '$descriptor', { value: descriptor }); + }; + + /** + * Define the model for an element + */ + Properties.prototype.defineModel = function(target, model) { + this.define(target, '$model', { value: model }); + }; + + + function isUndefined(val) { + return typeof val === 'undefined'; + } + + function defineProperty(target, property, value) { + Object.defineProperty(target, property.name, { + enumerable: !property.isReference, + writable: true, + value: value, + configurable: true + }); + } + + //// Moddle implementation ///////////////////////////////////////////////// + + /** + * @class Moddle + * + * A model that can be used to create elements of a specific type. + * + * @example + * + * var Moddle = require('moddle'); + * + * var pkg = { + * name: 'mypackage', + * prefix: 'my', + * types: [ + * { name: 'Root' } + * ] + * }; + * + * var moddle = new Moddle([pkg]); + * + * @param {Array} packages the packages to contain + */ + function Moddle(packages) { + + this.properties = new Properties(this); + + this.factory = new Factory(this, this.properties); + this.registry = new Registry(packages, this.properties); + + this.typeCache = {}; + } + + + /** + * Create an instance of the specified type. + * + * @method Moddle#create + * + * @example + * + * var foo = moddle.create('my:Foo'); + * var bar = moddle.create('my:Bar', { id: 'BAR_1' }); + * + * @param {String|Object} descriptor the type descriptor or name know to the model + * @param {Object} attrs a number of attributes to initialize the model instance with + * @return {Object} model instance + */ + Moddle.prototype.create = function(descriptor, attrs) { + var Type = this.getType(descriptor); + + if (!Type) { + throw new Error('unknown type <' + descriptor + '>'); + } + + return new Type(attrs); + }; + + + /** + * Returns the type representing a given descriptor + * + * @method Moddle#getType + * + * @example + * + * var Foo = moddle.getType('my:Foo'); + * var foo = new Foo({ 'id' : 'FOO_1' }); + * + * @param {String|Object} descriptor the type descriptor or name know to the model + * @return {Object} the type representing the descriptor + */ + Moddle.prototype.getType = function(descriptor) { + + var cache = this.typeCache; + + var name = isString(descriptor) ? descriptor : descriptor.ns.name; + + var type = cache[name]; + + if (!type) { + descriptor = this.registry.getEffectiveDescriptor(name); + type = cache[name] = this.factory.createType(descriptor); + } + + return type; + }; + + + /** + * Creates an any-element type to be used within model instances. + * + * This can be used to create custom elements that lie outside the meta-model. + * The created element contains all the meta-data required to serialize it + * as part of meta-model elements. + * + * @method Moddle#createAny + * + * @example + * + * var foo = moddle.createAny('vendor:Foo', 'http://vendor', { + * value: 'bar' + * }); + * + * var container = moddle.create('my:Container', 'http://my', { + * any: [ foo ] + * }); + * + * // go ahead and serialize the stuff + * + * + * @param {String} name the name of the element + * @param {String} nsUri the namespace uri of the element + * @param {Object} [properties] a map of properties to initialize the instance with + * @return {Object} the any type instance + */ + Moddle.prototype.createAny = function(name, nsUri, properties) { + + var nameNs = parseName(name); + + var element = { + $type: name, + $instanceOf: function(type) { + return type === this.$type; + } + }; + + var descriptor = { + name: name, + isGeneric: true, + ns: { + prefix: nameNs.prefix, + localName: nameNs.localName, + uri: nsUri + } + }; + + this.properties.defineDescriptor(element, descriptor); + this.properties.defineModel(element, this); + this.properties.define(element, '$parent', { enumerable: false, writable: true }); + this.properties.define(element, '$instanceOf', { enumerable: false, writable: true }); + + forEach$1(properties, function(a, key) { + if (isObject(a) && a.value !== undefined) { + element[a.name] = a.value; + } else { + element[key] = a; + } + }); + + return element; + }; + + /** + * Returns a registered package by uri or prefix + * + * @return {Object} the package + */ + Moddle.prototype.getPackage = function(uriOrPrefix) { + return this.registry.getPackage(uriOrPrefix); + }; + + /** + * Returns a snapshot of all known packages + * + * @return {Object} the package + */ + Moddle.prototype.getPackages = function() { + return this.registry.getPackages(); + }; + + /** + * Returns the descriptor for an element + */ + Moddle.prototype.getElementDescriptor = function(element) { + return element.$descriptor; + }; + + /** + * Returns true if the given descriptor or instance + * represents the given type. + * + * May be applied to this, if element is omitted. + */ + Moddle.prototype.hasType = function(element, type) { + if (type === undefined) { + type = element; + element = this; + } + + var descriptor = element.$model.getElementDescriptor(element); + + return (type in descriptor.allTypesByName); + }; + + /** + * Returns the descriptor of an elements named property + */ + Moddle.prototype.getPropertyDescriptor = function(element, property) { + return this.getElementDescriptor(element).propertiesByName[property]; + }; + + /** + * Returns a mapped type's descriptor + */ + Moddle.prototype.getTypeDescriptor = function(type) { + return this.registry.typeMap[type]; + }; + + var fromCharCode = String.fromCharCode; + + var hasOwnProperty = Object.prototype.hasOwnProperty; + + var ENTITY_PATTERN = /&#(\d+);|&#x([0-9a-f]+);|&(\w+);/ig; + + var ENTITY_MAPPING = { + 'amp': '&', + 'apos': '\'', + 'gt': '>', + 'lt': '<', + 'quot': '"' + }; + + // map UPPERCASE variants of supported special chars + Object.keys(ENTITY_MAPPING).forEach(function(k) { + ENTITY_MAPPING[k.toUpperCase()] = ENTITY_MAPPING[k]; + }); + + + function replaceEntities(_, d, x, z) { + + // reserved names, i.e.   + if (z) { + if (hasOwnProperty.call(ENTITY_MAPPING, z)) { + return ENTITY_MAPPING[z]; + } else { + + // fall back to original value + return '&' + z + ';'; + } + } + + // decimal encoded char + if (d) { + return fromCharCode(d); + } + + // hex encoded char + return fromCharCode(parseInt(x, 16)); + } + + + /** + * A basic entity decoder that can decode a minimal + * sub-set of reserved names (&) as well as + * hex (ય) and decimal (ӏ) encoded characters. + * + * @param {string} str + * + * @return {string} decoded string + */ + function decodeEntities(s) { + if (s.length > 3 && s.indexOf('&') !== -1) { + return s.replace(ENTITY_PATTERN, replaceEntities); + } + + return s; + } + + var XSI_URI = 'http://www.w3.org/2001/XMLSchema-instance'; + var XSI_PREFIX = 'xsi'; + var XSI_TYPE$1 = 'xsi:type'; + + var NON_WHITESPACE_OUTSIDE_ROOT_NODE = 'non-whitespace outside of root node'; + + function error$1(msg) { + return new Error(msg); + } + + function missingNamespaceForPrefix(prefix) { + return 'missing namespace for prefix <' + prefix + '>'; + } + + function getter(getFn) { + return { + 'get': getFn, + 'enumerable': true + }; + } + + function cloneNsMatrix(nsMatrix) { + var clone = {}, key; + for (key in nsMatrix) { + clone[key] = nsMatrix[key]; + } + return clone; + } + + function uriPrefix(prefix) { + return prefix + '$uri'; + } + + function buildNsMatrix(nsUriToPrefix) { + var nsMatrix = {}, + uri, + prefix; + + for (uri in nsUriToPrefix) { + prefix = nsUriToPrefix[uri]; + nsMatrix[prefix] = prefix; + nsMatrix[uriPrefix(prefix)] = uri; + } + + return nsMatrix; + } + + function noopGetContext() { + return { 'line': 0, 'column': 0 }; + } + + function throwFunc(err) { + throw err; + } + + /** + * Creates a new parser with the given options. + * + * @constructor + * + * @param {!Object=} options + */ + function Parser(options) { + + if (!this) { + return new Parser(options); + } + + var proxy = options && options['proxy']; + + var onText, + onOpenTag, + onCloseTag, + onCDATA, + onError = throwFunc, + onWarning, + onComment, + onQuestion, + onAttention; + + var getContext = noopGetContext; + + /** + * Do we need to parse the current elements attributes for namespaces? + * + * @type {boolean} + */ + var maybeNS = false; + + /** + * Do we process namespaces at all? + * + * @type {boolean} + */ + var isNamespace = false; + + /** + * The caught error returned on parse end + * + * @type {Error} + */ + var returnError = null; + + /** + * Should we stop parsing? + * + * @type {boolean} + */ + var parseStop = false; + + /** + * A map of { uri: prefix } used by the parser. + * + * This map will ensure we can normalize prefixes during processing; + * for each uri, only one prefix will be exposed to the handlers. + * + * @type {!Object}} + */ + var nsUriToPrefix; + + /** + * Handle parse error. + * + * @param {string|Error} err + */ + function handleError(err) { + if (!(err instanceof Error)) { + err = error$1(err); + } + + returnError = err; + + onError(err, getContext); + } + + /** + * Handle parse error. + * + * @param {string|Error} err + */ + function handleWarning(err) { + + if (!onWarning) { + return; + } + + if (!(err instanceof Error)) { + err = error$1(err); + } + + onWarning(err, getContext); + } + + /** + * Register parse listener. + * + * @param {string} name + * @param {Function} cb + * + * @return {Parser} + */ + this['on'] = function(name, cb) { + + if (typeof cb !== 'function') { + throw error$1('required args '); + } + + switch (name) { + case 'openTag': onOpenTag = cb; break; + case 'text': onText = cb; break; + case 'closeTag': onCloseTag = cb; break; + case 'error': onError = cb; break; + case 'warn': onWarning = cb; break; + case 'cdata': onCDATA = cb; break; + case 'attention': onAttention = cb; break; // + case 'question': onQuestion = cb; break; // + case 'comment': onComment = cb; break; + default: + throw error$1('unsupported event: ' + name); + } + + return this; + }; + + /** + * Set the namespace to prefix mapping. + * + * @example + * + * parser.ns({ + * 'http://foo': 'foo', + * 'http://bar': 'bar' + * }); + * + * @param {!Object} nsMap + * + * @return {Parser} + */ + this['ns'] = function(nsMap) { + + if (typeof nsMap === 'undefined') { + nsMap = {}; + } + + if (typeof nsMap !== 'object') { + throw error$1('required args '); + } + + var _nsUriToPrefix = {}, k; + + for (k in nsMap) { + _nsUriToPrefix[k] = nsMap[k]; + } + + // FORCE default mapping for schema instance + _nsUriToPrefix[XSI_URI] = XSI_PREFIX; + + isNamespace = true; + nsUriToPrefix = _nsUriToPrefix; + + return this; + }; + + /** + * Parse xml string. + * + * @param {string} xml + * + * @return {Error} returnError, if not thrown + */ + this['parse'] = function(xml) { + if (typeof xml !== 'string') { + throw error$1('required args '); + } + + returnError = null; + + parse(xml); + + getContext = noopGetContext; + parseStop = false; + + return returnError; + }; + + /** + * Stop parsing. + */ + this['stop'] = function() { + parseStop = true; + }; + + /** + * Parse string, invoking configured listeners on element. + * + * @param {string} xml + */ + function parse(xml) { + var nsMatrixStack = isNamespace ? [] : null, + nsMatrix = isNamespace ? buildNsMatrix(nsUriToPrefix) : null, + _nsMatrix, + nodeStack = [], + anonymousNsCount = 0, + tagStart = false, + tagEnd = false, + i = 0, j = 0, + x, y, q, w, v, + xmlns, + elementName, + _elementName, + elementProxy + ; + + var attrsString = '', + attrsStart = 0, + cachedAttrs // false = parsed with errors, null = needs parsing + ; + + /** + * Parse attributes on demand and returns the parsed attributes. + * + * Return semantics: (1) `false` on attribute parse error, + * (2) object hash on extracted attrs. + * + * @return {boolean|Object} + */ + function getAttrs() { + if (cachedAttrs !== null) { + return cachedAttrs; + } + + var nsUri, + nsUriPrefix, + nsName, + defaultAlias = isNamespace && nsMatrix['xmlns'], + attrList = isNamespace && maybeNS ? [] : null, + i = attrsStart, + s = attrsString, + l = s.length, + hasNewMatrix, + newalias, + value, + alias, + name, + attrs = {}, + seenAttrs = {}, + skipAttr, + w, + j; + + parseAttr: + for (; i < l; i++) { + skipAttr = false; + w = s.charCodeAt(i); + + if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE={ \f\n\r\t\v} + continue; + } + + // wait for non whitespace character + if (w < 65 || w > 122 || (w > 90 && w < 97)) { + if (w !== 95 && w !== 58) { // char 95"_" 58":" + handleWarning('illegal first char attribute name'); + skipAttr = true; + } + } + + // parse attribute name + for (j = i + 1; j < l; j++) { + w = s.charCodeAt(j); + + if ( + w > 96 && w < 123 || + w > 64 && w < 91 || + w > 47 && w < 59 || + w === 46 || // '.' + w === 45 || // '-' + w === 95 // '_' + ) { + continue; + } + + // unexpected whitespace + if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE + handleWarning('missing attribute value'); + i = j; + + continue parseAttr; + } + + // expected "=" + if (w === 61) { // "=" == 61 + break; + } + + handleWarning('illegal attribute name char'); + skipAttr = true; + } + + name = s.substring(i, j); + + if (name === 'xmlns:xmlns') { + handleWarning('illegal declaration of xmlns'); + skipAttr = true; + } + + w = s.charCodeAt(j + 1); + + if (w === 34) { // '"' + j = s.indexOf('"', i = j + 2); + + if (j === -1) { + j = s.indexOf('\'', i); + + if (j !== -1) { + handleWarning('attribute value quote missmatch'); + skipAttr = true; + } + } + + } else if (w === 39) { // "'" + j = s.indexOf('\'', i = j + 2); + + if (j === -1) { + j = s.indexOf('"', i); + + if (j !== -1) { + handleWarning('attribute value quote missmatch'); + skipAttr = true; + } + } + + } else { + handleWarning('missing attribute value quotes'); + skipAttr = true; + + // skip to next space + for (j = j + 1; j < l; j++) { + w = s.charCodeAt(j + 1); + + if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE + break; + } + } + + } + + if (j === -1) { + handleWarning('missing closing quotes'); + + j = l; + skipAttr = true; + } + + if (!skipAttr) { + value = s.substring(i, j); + } + + i = j; + + // ensure SPACE follows attribute + // skip illegal content otherwise + // example a="b"c + for (; j + 1 < l; j++) { + w = s.charCodeAt(j + 1); + + if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE + break; + } + + // FIRST ILLEGAL CHAR + if (i === j) { + handleWarning('illegal character after attribute end'); + skipAttr = true; + } + } + + // advance cursor to next attribute + i = j + 1; + + if (skipAttr) { + continue parseAttr; + } + + // check attribute re-declaration + if (name in seenAttrs) { + handleWarning('attribute <' + name + '> already defined'); + continue; + } + + seenAttrs[name] = true; + + if (!isNamespace) { + attrs[name] = value; + continue; + } + + // try to extract namespace information + if (maybeNS) { + newalias = ( + name === 'xmlns' + ? 'xmlns' + : (name.charCodeAt(0) === 120 && name.substr(0, 6) === 'xmlns:') + ? name.substr(6) + : null + ); + + // handle xmlns(:alias) assignment + if (newalias !== null) { + nsUri = decodeEntities(value); + nsUriPrefix = uriPrefix(newalias); + + alias = nsUriToPrefix[nsUri]; + + if (!alias) { + + // no prefix defined or prefix collision + if ( + (newalias === 'xmlns') || + (nsUriPrefix in nsMatrix && nsMatrix[nsUriPrefix] !== nsUri) + ) { + + // alocate free ns prefix + do { + alias = 'ns' + (anonymousNsCount++); + } while (typeof nsMatrix[alias] !== 'undefined'); + } else { + alias = newalias; + } + + nsUriToPrefix[nsUri] = alias; + } + + if (nsMatrix[newalias] !== alias) { + if (!hasNewMatrix) { + nsMatrix = cloneNsMatrix(nsMatrix); + hasNewMatrix = true; + } + + nsMatrix[newalias] = alias; + if (newalias === 'xmlns') { + nsMatrix[uriPrefix(alias)] = nsUri; + defaultAlias = alias; + } + + nsMatrix[nsUriPrefix] = nsUri; + } + + // expose xmlns(:asd)="..." in attributes + attrs[name] = value; + continue; + } + + // collect attributes until all namespace + // declarations are processed + attrList.push(name, value); + continue; + + } /** end if (maybeNs) */ + + // handle attributes on element without + // namespace declarations + w = name.indexOf(':'); + if (w === -1) { + attrs[name] = value; + continue; + } + + // normalize ns attribute name + if (!(nsName = nsMatrix[name.substring(0, w)])) { + handleWarning(missingNamespaceForPrefix(name.substring(0, w))); + continue; + } + + name = defaultAlias === nsName + ? name.substr(w + 1) + : nsName + name.substr(w); + + // end: normalize ns attribute name + + // normalize xsi:type ns attribute value + if (name === XSI_TYPE$1) { + w = value.indexOf(':'); + + if (w !== -1) { + nsName = value.substring(0, w); + + // handle default prefixes, i.e. xs:String gracefully + nsName = nsMatrix[nsName] || nsName; + value = nsName + value.substring(w); + } else { + value = defaultAlias + ':' + value; + } + } + + // end: normalize xsi:type ns attribute value + + attrs[name] = value; + } + + + // handle deferred, possibly namespaced attributes + if (maybeNS) { + + // normalize captured attributes + for (i = 0, l = attrList.length; i < l; i++) { + + name = attrList[i++]; + value = attrList[i]; + + w = name.indexOf(':'); + + if (w !== -1) { + + // normalize ns attribute name + if (!(nsName = nsMatrix[name.substring(0, w)])) { + handleWarning(missingNamespaceForPrefix(name.substring(0, w))); + continue; + } + + name = defaultAlias === nsName + ? name.substr(w + 1) + : nsName + name.substr(w); + + // end: normalize ns attribute name + + // normalize xsi:type ns attribute value + if (name === XSI_TYPE$1) { + w = value.indexOf(':'); + + if (w !== -1) { + nsName = value.substring(0, w); + + // handle default prefixes, i.e. xs:String gracefully + nsName = nsMatrix[nsName] || nsName; + value = nsName + value.substring(w); + } else { + value = defaultAlias + ':' + value; + } + } + + // end: normalize xsi:type ns attribute value + } + + attrs[name] = value; + } + + // end: normalize captured attributes + } + + return cachedAttrs = attrs; + } + + /** + * Extract the parse context { line, column, part } + * from the current parser position. + * + * @return {Object} parse context + */ + function getParseContext() { + var splitsRe = /(\r\n|\r|\n)/g; + + var line = 0; + var column = 0; + var startOfLine = 0; + var endOfLine = j; + var match; + var data; + + while (i >= startOfLine) { + + match = splitsRe.exec(xml); + + if (!match) { + break; + } + + // end of line = (break idx + break chars) + endOfLine = match[0].length + match.index; + + if (endOfLine > i) { + break; + } + + // advance to next line + line += 1; + + startOfLine = endOfLine; + } + + // EOF errors + if (i == -1) { + column = endOfLine; + data = xml.substring(j); + } else + + // start errors + if (j === 0) { + data = xml.substring(j, i); + } + + // other errors + else { + column = i - startOfLine; + data = (j == -1 ? xml.substring(i) : xml.substring(i, j + 1)); + } + + return { + 'data': data, + 'line': line, + 'column': column + }; + } + + getContext = getParseContext; + + + if (proxy) { + elementProxy = Object.create({}, { + 'name': getter(function() { + return elementName; + }), + 'originalName': getter(function() { + return _elementName; + }), + 'attrs': getter(getAttrs), + 'ns': getter(function() { + return nsMatrix; + }) + }); + } + + // actual parse logic + while (j !== -1) { + + if (xml.charCodeAt(j) === 60) { // "<" + i = j; + } else { + i = xml.indexOf('<', j); + } + + // parse end + if (i === -1) { + if (nodeStack.length) { + return handleError('unexpected end of file'); + } + + if (j === 0) { + return handleError('missing start tag'); + } + + if (j < xml.length) { + if (xml.substring(j).trim()) { + handleWarning(NON_WHITESPACE_OUTSIDE_ROOT_NODE); + } + } + + return; + } + + // parse text + if (j !== i) { + + if (nodeStack.length) { + if (onText) { + onText(xml.substring(j, i), decodeEntities, getContext); + + if (parseStop) { + return; + } + } + } else { + if (xml.substring(j, i).trim()) { + handleWarning(NON_WHITESPACE_OUTSIDE_ROOT_NODE); + + if (parseStop) { + return; + } + } + } + } + + w = xml.charCodeAt(i+1); + + // parse comments + CDATA + if (w === 33) { // "!" + q = xml.charCodeAt(i+2); + + // CDATA section + if (q === 91 && xml.substr(i + 3, 6) === 'CDATA[') { // 91 == "[" + j = xml.indexOf(']]>', i); + if (j === -1) { + return handleError('unclosed cdata'); + } + + if (onCDATA) { + onCDATA(xml.substring(i + 9, j), getContext); + if (parseStop) { + return; + } + } + + j += 3; + continue; + } + + // comment + if (q === 45 && xml.charCodeAt(i + 3) === 45) { // 45 == "-" + j = xml.indexOf('-->', i); + if (j === -1) { + return handleError('unclosed comment'); + } + + + if (onComment) { + onComment(xml.substring(i + 4, j), decodeEntities, getContext); + if (parseStop) { + return; + } + } + + j += 3; + continue; + } + } + + // parse question + if (w === 63) { // "?" + j = xml.indexOf('?>', i); + if (j === -1) { + return handleError('unclosed question'); + } + + if (onQuestion) { + onQuestion(xml.substring(i, j + 2), getContext); + if (parseStop) { + return; + } + } + + j += 2; + continue; + } + + // find matching closing tag for attention or standard tags + // for that we must skip through attribute values + // (enclosed in single or double quotes) + for (x = i + 1; ; x++) { + v = xml.charCodeAt(x); + if (isNaN(v)) { + j = -1; + return handleError('unclosed tag'); + } + + // [10] AttValue ::= '"' ([^<&"] | Reference)* '"' | "'" ([^<&'] | Reference)* "'" + // skips the quoted string + // (double quotes) does not appear in a literal enclosed by (double quotes) + // (single quote) does not appear in a literal enclosed by (single quote) + if (v === 34) { // '"' + q = xml.indexOf('"', x + 1); + x = q !== -1 ? q : x; + } else if (v === 39) { // "'" + q = xml.indexOf("'", x + 1); + x = q !== -1 ? q : x; + } else if (v === 62) { // '>' + j = x; + break; + } + } + + + // parse attention + // previously comment and CDATA have already been parsed + if (w === 33) { // "!" + + if (onAttention) { + onAttention(xml.substring(i, j + 1), decodeEntities, getContext); + if (parseStop) { + return; + } + } + + j += 1; + continue; + } + + // don't process attributes; + // there are none + cachedAttrs = {}; + + // if (xml.charCodeAt(i+1) === 47) { // close tag match + x = elementName = nodeStack.pop(); + q = i + 2 + x.length; + + if (xml.substring(i + 2, q) !== x) { + return handleError('closing tag mismatch'); + } + + // verify chars in close tag + for (; q < j; q++) { + w = xml.charCodeAt(q); + + if (w === 32 || (w > 8 && w < 14)) { // \f\n\r\t\v space + continue; + } + + return handleError('close tag'); + } + + } else { + if (xml.charCodeAt(j - 1) === 47) { // .../> + x = elementName = xml.substring(i + 1, j - 1); + + tagStart = true; + tagEnd = true; + + } else { + x = elementName = xml.substring(i + 1, j); + + tagStart = true; + tagEnd = false; + } + + if (!(w > 96 && w < 123 || w > 64 && w < 91 || w === 95 || w === 58)) { // char 95"_" 58":" + return handleError('illegal first char nodeName'); + } + + for (q = 1, y = x.length; q < y; q++) { + w = x.charCodeAt(q); + + if (w > 96 && w < 123 || w > 64 && w < 91 || w > 47 && w < 59 || w === 45 || w === 95 || w == 46) { + continue; + } + + if (w === 32 || (w < 14 && w > 8)) { // \f\n\r\t\v space + elementName = x.substring(0, q); + + // maybe there are attributes + cachedAttrs = null; + break; + } + + return handleError('invalid nodeName'); + } + + if (!tagEnd) { + nodeStack.push(elementName); + } + } + + if (isNamespace) { + + _nsMatrix = nsMatrix; + + if (tagStart) { + + // remember old namespace + // unless we're self-closing + if (!tagEnd) { + nsMatrixStack.push(_nsMatrix); + } + + if (cachedAttrs === null) { + + // quick check, whether there may be namespace + // declarations on the node; if that is the case + // we need to eagerly parse the node attributes + if ((maybeNS = x.indexOf('xmlns', q) !== -1)) { + attrsStart = q; + attrsString = x; + + getAttrs(); + + maybeNS = false; + } + } + } + + _elementName = elementName; + + w = elementName.indexOf(':'); + if (w !== -1) { + xmlns = nsMatrix[elementName.substring(0, w)]; + + // prefix given; namespace must exist + if (!xmlns) { + return handleError('missing namespace on <' + _elementName + '>'); + } + + elementName = elementName.substr(w + 1); + } else { + xmlns = nsMatrix['xmlns']; + + // if no default namespace is defined, + // we'll import the element as anonymous. + // + // it is up to users to correct that to the document defined + // targetNamespace, or whatever their undersanding of the + // XML spec mandates. + } + + // adjust namespace prefixs as configured + if (xmlns) { + elementName = xmlns + ':' + elementName; + } + + } + + if (tagStart) { + attrsStart = q; + attrsString = x; + + if (onOpenTag) { + if (proxy) { + onOpenTag(elementProxy, decodeEntities, tagEnd, getContext); + } else { + onOpenTag(elementName, getAttrs, decodeEntities, tagEnd, getContext); + } + + if (parseStop) { + return; + } + } + + } + + if (tagEnd) { + + if (onCloseTag) { + onCloseTag(proxy ? elementProxy : elementName, decodeEntities, tagStart, getContext); + + if (parseStop) { + return; + } + } + + // restore old namespace + if (isNamespace) { + if (!tagStart) { + nsMatrix = nsMatrixStack.pop(); + } else { + nsMatrix = _nsMatrix; + } + } + } + + j += 1; + } + } /** end parse */ + + } + + function hasLowerCaseAlias(pkg) { + return pkg.xml && pkg.xml.tagAlias === 'lowerCase'; + } + + var DEFAULT_NS_MAP = { + 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', + 'xml': 'http://www.w3.org/XML/1998/namespace' + }; + + var XSI_TYPE = 'xsi:type'; + + function serializeFormat(element) { + return element.xml && element.xml.serialize; + } + + function serializeAsType(element) { + return serializeFormat(element) === XSI_TYPE; + } + + function serializeAsProperty(element) { + return serializeFormat(element) === 'property'; + } + + function capitalize(str) { + return str.charAt(0).toUpperCase() + str.slice(1); + } + + function aliasToName(aliasNs, pkg) { + + if (!hasLowerCaseAlias(pkg)) { + return aliasNs.name; + } + + return aliasNs.prefix + ':' + capitalize(aliasNs.localName); + } + + function prefixedToName(nameNs, pkg) { + + var name = nameNs.name, + localName = nameNs.localName; + + var typePrefix = pkg.xml && pkg.xml.typePrefix; + + if (typePrefix && localName.indexOf(typePrefix) === 0) { + return nameNs.prefix + ':' + localName.slice(typePrefix.length); + } else { + return name; + } + } + + function normalizeXsiTypeName(name, model) { + + var nameNs = parseName(name); + var pkg = model.getPackage(nameNs.prefix); + + return prefixedToName(nameNs, pkg); + } + + function error(message) { + return new Error(message); + } + + /** + * Get the moddle descriptor for a given instance or type. + * + * @param {ModdleElement|Function} element + * + * @return {Object} the moddle descriptor + */ + function getModdleDescriptor(element) { + return element.$descriptor; + } + + + /** + * A parse context. + * + * @class + * + * @param {Object} options + * @param {ElementHandler} options.rootHandler the root handler for parsing a document + * @param {boolean} [options.lax=false] whether or not to ignore invalid elements + */ + function Context(options) { + + /** + * @property {ElementHandler} rootHandler + */ + + /** + * @property {Boolean} lax + */ + + assign(this, options); + + this.elementsById = {}; + this.references = []; + this.warnings = []; + + /** + * Add an unresolved reference. + * + * @param {Object} reference + */ + this.addReference = function(reference) { + this.references.push(reference); + }; + + /** + * Add a processed element. + * + * @param {ModdleElement} element + */ + this.addElement = function(element) { + + if (!element) { + throw error('expected element'); + } + + var elementsById = this.elementsById; + + var descriptor = getModdleDescriptor(element); + + var idProperty = descriptor.idProperty, + id; + + if (idProperty) { + id = element.get(idProperty.name); + + if (id) { + + // for QName validation as per http://www.w3.org/TR/REC-xml/#NT-NameChar + if (!/^([a-z][\w-.]*:)?[a-z_][\w-.]*$/i.test(id)) { + throw new Error('illegal ID <' + id + '>'); + } + + if (elementsById[id]) { + throw error('duplicate ID <' + id + '>'); + } + + elementsById[id] = element; + } + } + }; + + /** + * Add an import warning. + * + * @param {Object} warning + * @param {String} warning.message + * @param {Error} [warning.error] + */ + this.addWarning = function(warning) { + this.warnings.push(warning); + }; + } + + function BaseHandler() {} + + BaseHandler.prototype.handleEnd = function() {}; + BaseHandler.prototype.handleText = function() {}; + BaseHandler.prototype.handleNode = function() {}; + + + /** + * A simple pass through handler that does nothing except for + * ignoring all input it receives. + * + * This is used to ignore unknown elements and + * attributes. + */ + function NoopHandler() { } + + NoopHandler.prototype = Object.create(BaseHandler.prototype); + + NoopHandler.prototype.handleNode = function() { + return this; + }; + + function BodyHandler() {} + + BodyHandler.prototype = Object.create(BaseHandler.prototype); + + BodyHandler.prototype.handleText = function(text) { + this.body = (this.body || '') + text; + }; + + function ReferenceHandler(property, context) { + this.property = property; + this.context = context; + } + + ReferenceHandler.prototype = Object.create(BodyHandler.prototype); + + ReferenceHandler.prototype.handleNode = function(node) { + + if (this.element) { + throw error('expected no sub nodes'); + } else { + this.element = this.createReference(node); + } + + return this; + }; + + ReferenceHandler.prototype.handleEnd = function() { + this.element.id = this.body; + }; + + ReferenceHandler.prototype.createReference = function(node) { + return { + property: this.property.ns.name, + id: '' + }; + }; + + function ValueHandler(propertyDesc, element) { + this.element = element; + this.propertyDesc = propertyDesc; + } + + ValueHandler.prototype = Object.create(BodyHandler.prototype); + + ValueHandler.prototype.handleEnd = function() { + + var value = this.body || '', + element = this.element, + propertyDesc = this.propertyDesc; + + value = coerceType(propertyDesc.type, value); + + if (propertyDesc.isMany) { + element.get(propertyDesc.name).push(value); + } else { + element.set(propertyDesc.name, value); + } + }; + + + function BaseElementHandler() {} + + BaseElementHandler.prototype = Object.create(BodyHandler.prototype); + + BaseElementHandler.prototype.handleNode = function(node) { + var parser = this, + element = this.element; + + if (!element) { + element = this.element = this.createElement(node); + + this.context.addElement(element); + } else { + parser = this.handleChild(node); + } + + return parser; + }; + + /** + * @class Reader.ElementHandler + * + */ + function ElementHandler(model, typeName, context) { + this.model = model; + this.type = model.getType(typeName); + this.context = context; + } + + ElementHandler.prototype = Object.create(BaseElementHandler.prototype); + + ElementHandler.prototype.addReference = function(reference) { + this.context.addReference(reference); + }; + + ElementHandler.prototype.handleText = function(text) { + + var element = this.element, + descriptor = getModdleDescriptor(element), + bodyProperty = descriptor.bodyProperty; + + if (!bodyProperty) { + throw error('unexpected body text <' + text + '>'); + } + + BodyHandler.prototype.handleText.call(this, text); + }; + + ElementHandler.prototype.handleEnd = function() { + + var value = this.body, + element = this.element, + descriptor = getModdleDescriptor(element), + bodyProperty = descriptor.bodyProperty; + + if (bodyProperty && value !== undefined) { + value = coerceType(bodyProperty.type, value); + element.set(bodyProperty.name, value); + } + }; + + /** + * Create an instance of the model from the given node. + * + * @param {Element} node the xml node + */ + ElementHandler.prototype.createElement = function(node) { + var attributes = node.attributes, + Type = this.type, + descriptor = getModdleDescriptor(Type), + context = this.context, + instance = new Type({}), + model = this.model, + propNameNs; + + forEach$1(attributes, function(value, name) { + + var prop = descriptor.propertiesByName[name], + values; + + if (prop && prop.isReference) { + + if (!prop.isMany) { + context.addReference({ + element: instance, + property: prop.ns.name, + id: value + }); + } else { + + // IDREFS: parse references as whitespace-separated list + values = value.split(' '); + + forEach$1(values, function(v) { + context.addReference({ + element: instance, + property: prop.ns.name, + id: v + }); + }); + } + + } else { + if (prop) { + value = coerceType(prop.type, value); + } else + if (name !== 'xmlns') { + propNameNs = parseName(name, descriptor.ns.prefix); + + // check whether attribute is defined in a well-known namespace + // if that is the case we emit a warning to indicate potential misuse + if (model.getPackage(propNameNs.prefix)) { + + context.addWarning({ + message: 'unknown attribute <' + name + '>', + element: instance, + property: name, + value: value + }); + } + } + + instance.set(name, value); + } + }); + + return instance; + }; + + ElementHandler.prototype.getPropertyForNode = function(node) { + + var name = node.name; + var nameNs = parseName(name); + + var type = this.type, + model = this.model, + descriptor = getModdleDescriptor(type); + + var propertyName = nameNs.name, + property = descriptor.propertiesByName[propertyName], + elementTypeName, + elementType; + + // search for properties by name first + + if (property && !property.isAttr) { + + if (serializeAsType(property)) { + elementTypeName = node.attributes[XSI_TYPE]; + + // xsi type is optional, if it does not exists the + // default type is assumed + if (elementTypeName) { + + // take possible type prefixes from XML + // into account, i.e.: xsi:type="t{ActualType}" + elementTypeName = normalizeXsiTypeName(elementTypeName, model); + + elementType = model.getType(elementTypeName); + + return assign({}, property, { + effectiveType: getModdleDescriptor(elementType).name + }); + } + } + + // search for properties by name first + return property; + } + + var pkg = model.getPackage(nameNs.prefix); + + if (pkg) { + elementTypeName = aliasToName(nameNs, pkg); + elementType = model.getType(elementTypeName); + + // search for collection members later + property = find(descriptor.properties, function(p) { + return !p.isVirtual && !p.isReference && !p.isAttribute && elementType.hasType(p.type); + }); + + if (property) { + return assign({}, property, { + effectiveType: getModdleDescriptor(elementType).name + }); + } + } else { + + // parse unknown element (maybe extension) + property = find(descriptor.properties, function(p) { + return !p.isReference && !p.isAttribute && p.type === 'Element'; + }); + + if (property) { + return property; + } + } + + throw error('unrecognized element <' + nameNs.name + '>'); + }; + + ElementHandler.prototype.toString = function() { + return 'ElementDescriptor[' + getModdleDescriptor(this.type).name + ']'; + }; + + ElementHandler.prototype.valueHandler = function(propertyDesc, element) { + return new ValueHandler(propertyDesc, element); + }; + + ElementHandler.prototype.referenceHandler = function(propertyDesc) { + return new ReferenceHandler(propertyDesc, this.context); + }; + + ElementHandler.prototype.handler = function(type) { + if (type === 'Element') { + return new GenericElementHandler(this.model, type, this.context); + } else { + return new ElementHandler(this.model, type, this.context); + } + }; + + /** + * Handle the child element parsing + * + * @param {Element} node the xml node + */ + ElementHandler.prototype.handleChild = function(node) { + var propertyDesc, type, element, childHandler; + + propertyDesc = this.getPropertyForNode(node); + element = this.element; + + type = propertyDesc.effectiveType || propertyDesc.type; + + if (isSimple(type)) { + return this.valueHandler(propertyDesc, element); + } + + if (propertyDesc.isReference) { + childHandler = this.referenceHandler(propertyDesc).handleNode(node); + } else { + childHandler = this.handler(type).handleNode(node); + } + + var newElement = childHandler.element; + + // child handles may decide to skip elements + // by not returning anything + if (newElement !== undefined) { + + if (propertyDesc.isMany) { + element.get(propertyDesc.name).push(newElement); + } else { + element.set(propertyDesc.name, newElement); + } + + if (propertyDesc.isReference) { + assign(newElement, { + element: element + }); + + this.context.addReference(newElement); + } else { + + // establish child -> parent relationship + newElement.$parent = element; + } + } + + return childHandler; + }; + + /** + * An element handler that performs special validation + * to ensure the node it gets initialized with matches + * the handlers type (namespace wise). + * + * @param {Moddle} model + * @param {String} typeName + * @param {Context} context + */ + function RootElementHandler(model, typeName, context) { + ElementHandler.call(this, model, typeName, context); + } + + RootElementHandler.prototype = Object.create(ElementHandler.prototype); + + RootElementHandler.prototype.createElement = function(node) { + + var name = node.name, + nameNs = parseName(name), + model = this.model, + type = this.type, + pkg = model.getPackage(nameNs.prefix), + typeName = pkg && aliasToName(nameNs, pkg) || name; + + // verify the correct namespace if we parse + // the first element in the handler tree + // + // this ensures we don't mistakenly import wrong namespace elements + if (!type.hasType(typeName)) { + throw error('unexpected element <' + node.originalName + '>'); + } + + return ElementHandler.prototype.createElement.call(this, node); + }; + + + function GenericElementHandler(model, typeName, context) { + this.model = model; + this.context = context; + } + + GenericElementHandler.prototype = Object.create(BaseElementHandler.prototype); + + GenericElementHandler.prototype.createElement = function(node) { + + var name = node.name, + ns = parseName(name), + prefix = ns.prefix, + uri = node.ns[prefix + '$uri'], + attributes = node.attributes; + + return this.model.createAny(name, uri, attributes); + }; + + GenericElementHandler.prototype.handleChild = function(node) { + + var handler = new GenericElementHandler(this.model, 'Element', this.context).handleNode(node), + element = this.element; + + var newElement = handler.element, + children; + + if (newElement !== undefined) { + children = element.$children = element.$children || []; + children.push(newElement); + + // establish child -> parent relationship + newElement.$parent = element; + } + + return handler; + }; + + GenericElementHandler.prototype.handleEnd = function() { + if (this.body) { + this.element.$body = this.body; + } + }; + + /** + * A reader for a meta-model + * + * @param {Object} options + * @param {Model} options.model used to read xml files + * @param {Boolean} options.lax whether to make parse errors warnings + */ + function Reader(options) { + + if (options instanceof Moddle) { + options = { + model: options + }; + } + + assign(this, { lax: false }, options); + } + + /** + * The fromXML result. + * + * @typedef {Object} ParseResult + * + * @property {ModdleElement} rootElement + * @property {Array} references + * @property {Array} warnings + * @property {Object} elementsById - a mapping containing each ID -> ModdleElement + */ + + /** + * The fromXML result. + * + * @typedef {Error} ParseError + * + * @property {Array} warnings + */ + + /** + * Parse the given XML into a moddle document tree. + * + * @param {String} xml + * @param {ElementHandler|Object} options or rootHandler + * + * @returns {Promise} + */ + Reader.prototype.fromXML = function(xml, options, done) { + + var rootHandler = options.rootHandler; + + if (options instanceof ElementHandler) { + + // root handler passed via (xml, { rootHandler: ElementHandler }, ...) + rootHandler = options; + options = {}; + } else { + if (typeof options === 'string') { + + // rootHandler passed via (xml, 'someString', ...) + rootHandler = this.handler(options); + options = {}; + } else if (typeof rootHandler === 'string') { + + // rootHandler passed via (xml, { rootHandler: 'someString' }, ...) + rootHandler = this.handler(rootHandler); + } + } + + var model = this.model, + lax = this.lax; + + var context = new Context(assign({}, options, { rootHandler: rootHandler })), + parser = new Parser({ proxy: true }), + stack = createStack(); + + rootHandler.context = context; + + // push root handler + stack.push(rootHandler); + + + /** + * Handle error. + * + * @param {Error} err + * @param {Function} getContext + * @param {boolean} lax + * + * @return {boolean} true if handled + */ + function handleError(err, getContext, lax) { + + var ctx = getContext(); + + var line = ctx.line, + column = ctx.column, + data = ctx.data; + + // we receive the full context data here, + // for elements trim down the information + // to the tag name, only + if (data.charAt(0) === '<' && data.indexOf(' ') !== -1) { + data = data.slice(0, data.indexOf(' ')) + '>'; + } + + var message = + 'unparsable content ' + (data ? data + ' ' : '') + 'detected\n\t' + + 'line: ' + line + '\n\t' + + 'column: ' + column + '\n\t' + + 'nested error: ' + err.message; + + if (lax) { + context.addWarning({ + message: message, + error: err + }); + + return true; + } else { + throw error(message); + } + } + + function handleWarning(err, getContext) { + + // just like handling errors in mode + return handleError(err, getContext, true); + } + + /** + * Resolve collected references on parse end. + */ + function resolveReferences() { + + var elementsById = context.elementsById; + var references = context.references; + + var i, r; + + for (i = 0; (r = references[i]); i++) { + var element = r.element; + var reference = elementsById[r.id]; + var property = getModdleDescriptor(element).propertiesByName[r.property]; + + if (!reference) { + context.addWarning({ + message: 'unresolved reference <' + r.id + '>', + element: r.element, + property: r.property, + value: r.id + }); + } + + if (property.isMany) { + var collection = element.get(property.name), + idx = collection.indexOf(r); + + // we replace an existing place holder (idx != -1) or + // append to the collection instead + if (idx === -1) { + idx = collection.length; + } + + if (!reference) { + + // remove unresolvable reference + collection.splice(idx, 1); + } else { + + // add or update reference in collection + collection[idx] = reference; + } + } else { + element.set(property.name, reference); + } + } + } + + function handleClose() { + stack.pop().handleEnd(); + } + + var PREAMBLE_START_PATTERN = /^<\?xml /i; + + var ENCODING_PATTERN = / encoding="([^"]+)"/i; + + var UTF_8_PATTERN = /^utf-8$/i; + + function handleQuestion(question) { + + if (!PREAMBLE_START_PATTERN.test(question)) { + return; + } + + var match = ENCODING_PATTERN.exec(question); + var encoding = match && match[1]; + + if (!encoding || UTF_8_PATTERN.test(encoding)) { + return; + } + + context.addWarning({ + message: + 'unsupported document encoding <' + encoding + '>, ' + + 'falling back to UTF-8' + }); + } + + function handleOpen(node, getContext) { + var handler = stack.peek(); + + try { + stack.push(handler.handleNode(node)); + } catch (err) { + + if (handleError(err, getContext, lax)) { + stack.push(new NoopHandler()); + } + } + } + + function handleCData(text, getContext) { + + try { + stack.peek().handleText(text); + } catch (err) { + handleWarning(err, getContext); + } + } + + function handleText(text, getContext) { + + // strip whitespace only nodes, i.e. before + // sections and in between tags + + if (!text.trim()) { + return; + } + + handleCData(text, getContext); + } + + var uriMap = model.getPackages().reduce(function(uriMap, p) { + uriMap[p.uri] = p.prefix; + + return uriMap; + }, { + 'http://www.w3.org/XML/1998/namespace': 'xml' // add default xml ns + }); + parser + .ns(uriMap) + .on('openTag', function(obj, decodeStr, selfClosing, getContext) { + + // gracefully handle unparsable attributes (attrs=false) + var attrs = obj.attrs || {}; + + var decodedAttrs = Object.keys(attrs).reduce(function(d, key) { + var value = decodeStr(attrs[key]); + + d[key] = value; + + return d; + }, {}); + + var node = { + name: obj.name, + originalName: obj.originalName, + attributes: decodedAttrs, + ns: obj.ns + }; + + handleOpen(node, getContext); + }) + .on('question', handleQuestion) + .on('closeTag', handleClose) + .on('cdata', handleCData) + .on('text', function(text, decodeEntities, getContext) { + handleText(decodeEntities(text), getContext); + }) + .on('error', handleError) + .on('warn', handleWarning); + + // async XML parsing to make sure the execution environment + // (node or brower) is kept responsive and that certain optimization + // strategies can kick in. + return new Promise(function(resolve, reject) { + + var err; + + try { + parser.parse(xml); + + resolveReferences(); + } catch (e) { + err = e; + } + + var rootElement = rootHandler.element; + + if (!err && !rootElement) { + err = error('failed to parse document as <' + rootHandler.type.$descriptor.name + '>'); + } + + var warnings = context.warnings; + var references = context.references; + var elementsById = context.elementsById; + + if (err) { + err.warnings = warnings; + + return reject(err); + } else { + return resolve({ + rootElement: rootElement, + elementsById: elementsById, + references: references, + warnings: warnings + }); + } + }); + }; + + Reader.prototype.handler = function(name) { + return new RootElementHandler(this.model, name); + }; + + + // helpers ////////////////////////// + + function createStack() { + var stack = []; + + Object.defineProperty(stack, 'peek', { + value: function() { + return this[this.length - 1]; + } + }); + + return stack; + } + + var XML_PREAMBLE = '\n'; + + var ESCAPE_ATTR_CHARS = /<|>|'|"|&|\n\r|\n/g; + var ESCAPE_CHARS = /<|>|&/g; + + + function Namespaces(parent) { + + var prefixMap = {}; + var uriMap = {}; + var used = {}; + + var wellknown = []; + var custom = []; + + // API + + this.byUri = function(uri) { + return uriMap[uri] || ( + parent && parent.byUri(uri) + ); + }; + + this.add = function(ns, isWellknown) { + + uriMap[ns.uri] = ns; + + if (isWellknown) { + wellknown.push(ns); + } else { + custom.push(ns); + } + + this.mapPrefix(ns.prefix, ns.uri); + }; + + this.uriByPrefix = function(prefix) { + return prefixMap[prefix || 'xmlns']; + }; + + this.mapPrefix = function(prefix, uri) { + prefixMap[prefix || 'xmlns'] = uri; + }; + + this.getNSKey = function(ns) { + return (ns.prefix !== undefined) ? (ns.uri + '|' + ns.prefix) : ns.uri; + }; + + this.logUsed = function(ns) { + + var uri = ns.uri; + var nsKey = this.getNSKey(ns); + + used[nsKey] = this.byUri(uri); + + // Inform parent recursively about the usage of this NS + if (parent) { + parent.logUsed(ns); + } + }; + + this.getUsed = function(ns) { + + function isUsed(ns) { + var nsKey = self.getNSKey(ns); + + return used[nsKey]; + } + + var self = this; + + var allNs = [].concat(wellknown, custom); + + return allNs.filter(isUsed); + }; + + } + + function lower(string) { + return string.charAt(0).toLowerCase() + string.slice(1); + } + + function nameToAlias(name, pkg) { + if (hasLowerCaseAlias(pkg)) { + return lower(name); + } else { + return name; + } + } + + function inherits(ctor, superCtor) { + ctor.super_ = superCtor; + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + } + + function nsName(ns) { + if (isString(ns)) { + return ns; + } else { + return (ns.prefix ? ns.prefix + ':' : '') + ns.localName; + } + } + + function getNsAttrs(namespaces) { + + return namespaces.getUsed().filter(function(ns) { + + // do not serialize built in namespace + return ns.prefix !== 'xml'; + }).map(function(ns) { + var name = 'xmlns' + (ns.prefix ? ':' + ns.prefix : ''); + return { name: name, value: ns.uri }; + }); + + } + + function getElementNs(ns, descriptor) { + if (descriptor.isGeneric) { + return assign({ localName: descriptor.ns.localName }, ns); + } else { + return assign({ localName: nameToAlias(descriptor.ns.localName, descriptor.$pkg) }, ns); + } + } + + function getPropertyNs(ns, descriptor) { + return assign({ localName: descriptor.ns.localName }, ns); + } + + function getSerializableProperties(element) { + var descriptor = element.$descriptor; + + return filter(descriptor.properties, function(p) { + var name = p.name; + + if (p.isVirtual) { + return false; + } + + // do not serialize defaults + if (!has$1(element, name)) { + return false; + } + + var value = element[name]; + + // do not serialize default equals + if (value === p.default) { + return false; + } + + // do not serialize null properties + if (value === null) { + return false; + } + + return p.isMany ? value.length : true; + }); + } + + var ESCAPE_ATTR_MAP = { + '\n': '#10', + '\n\r': '#10', + '"': '#34', + '\'': '#39', + '<': '#60', + '>': '#62', + '&': '#38' + }; + + var ESCAPE_MAP = { + '<': 'lt', + '>': 'gt', + '&': 'amp' + }; + + function escape(str, charPattern, replaceMap) { + + // ensure we are handling strings here + str = isString(str) ? str : '' + str; + + return str.replace(charPattern, function(s) { + return '&' + replaceMap[s] + ';'; + }); + } + + /** + * Escape a string attribute to not contain any bad values (line breaks, '"', ...) + * + * @param {String} str the string to escape + * @return {String} the escaped string + */ + function escapeAttr(str) { + return escape(str, ESCAPE_ATTR_CHARS, ESCAPE_ATTR_MAP); + } + + function escapeBody(str) { + return escape(str, ESCAPE_CHARS, ESCAPE_MAP); + } + + function filterAttributes(props) { + return filter(props, function(p) { return p.isAttr; }); + } + + function filterContained(props) { + return filter(props, function(p) { return !p.isAttr; }); + } + + + function ReferenceSerializer(tagName) { + this.tagName = tagName; + } + + ReferenceSerializer.prototype.build = function(element) { + this.element = element; + return this; + }; + + ReferenceSerializer.prototype.serializeTo = function(writer) { + writer + .appendIndent() + .append('<' + this.tagName + '>' + this.element.id + '') + .appendNewLine(); + }; + + function BodySerializer() {} + + BodySerializer.prototype.serializeValue = + BodySerializer.prototype.serializeTo = function(writer) { + writer.append( + this.escape + ? escapeBody(this.value) + : this.value + ); + }; + + BodySerializer.prototype.build = function(prop, value) { + this.value = value; + + if (prop.type === 'String' && value.search(ESCAPE_CHARS) !== -1) { + this.escape = true; + } + + return this; + }; + + function ValueSerializer(tagName) { + this.tagName = tagName; + } + + inherits(ValueSerializer, BodySerializer); + + ValueSerializer.prototype.serializeTo = function(writer) { + + writer + .appendIndent() + .append('<' + this.tagName + '>'); + + this.serializeValue(writer); + + writer + .append('') + .appendNewLine(); + }; + + function ElementSerializer(parent, propertyDescriptor) { + this.body = []; + this.attrs = []; + + this.parent = parent; + this.propertyDescriptor = propertyDescriptor; + } + + ElementSerializer.prototype.build = function(element) { + this.element = element; + + var elementDescriptor = element.$descriptor, + propertyDescriptor = this.propertyDescriptor; + + var otherAttrs, + properties; + + var isGeneric = elementDescriptor.isGeneric; + + if (isGeneric) { + otherAttrs = this.parseGeneric(element); + } else { + otherAttrs = this.parseNsAttributes(element); + } + + if (propertyDescriptor) { + this.ns = this.nsPropertyTagName(propertyDescriptor); + } else { + this.ns = this.nsTagName(elementDescriptor); + } + + // compute tag name + this.tagName = this.addTagName(this.ns); + + if (!isGeneric) { + properties = getSerializableProperties(element); + + this.parseAttributes(filterAttributes(properties)); + this.parseContainments(filterContained(properties)); + } + + this.parseGenericAttributes(element, otherAttrs); + + return this; + }; + + ElementSerializer.prototype.nsTagName = function(descriptor) { + var effectiveNs = this.logNamespaceUsed(descriptor.ns); + return getElementNs(effectiveNs, descriptor); + }; + + ElementSerializer.prototype.nsPropertyTagName = function(descriptor) { + var effectiveNs = this.logNamespaceUsed(descriptor.ns); + return getPropertyNs(effectiveNs, descriptor); + }; + + ElementSerializer.prototype.isLocalNs = function(ns) { + return ns.uri === this.ns.uri; + }; + + /** + * Get the actual ns attribute name for the given element. + * + * @param {Object} element + * @param {Boolean} [element.inherited=false] + * + * @return {Object} nsName + */ + ElementSerializer.prototype.nsAttributeName = function(element) { + + var ns; + + if (isString(element)) { + ns = parseName(element); + } else { + ns = element.ns; + } + + // return just local name for inherited attributes + if (element.inherited) { + return { localName: ns.localName }; + } + + // parse + log effective ns + var effectiveNs = this.logNamespaceUsed(ns); + + // LOG ACTUAL namespace use + this.getNamespaces().logUsed(effectiveNs); + + // strip prefix if same namespace like parent + if (this.isLocalNs(effectiveNs)) { + return { localName: ns.localName }; + } else { + return assign({ localName: ns.localName }, effectiveNs); + } + }; + + ElementSerializer.prototype.parseGeneric = function(element) { + + var self = this, + body = this.body; + + var attributes = []; + + forEach$1(element, function(val, key) { + + var nonNsAttr; + + if (key === '$body') { + body.push(new BodySerializer().build({ type: 'String' }, val)); + } else + if (key === '$children') { + forEach$1(val, function(child) { + body.push(new ElementSerializer(self).build(child)); + }); + } else + if (key.indexOf('$') !== 0) { + nonNsAttr = self.parseNsAttribute(element, key, val); + + if (nonNsAttr) { + attributes.push({ name: key, value: val }); + } + } + }); + + return attributes; + }; + + ElementSerializer.prototype.parseNsAttribute = function(element, name, value) { + var model = element.$model; + + var nameNs = parseName(name); + + var ns; + + // parse xmlns:foo="http://foo.bar" + if (nameNs.prefix === 'xmlns') { + ns = { prefix: nameNs.localName, uri: value }; + } + + // parse xmlns="http://foo.bar" + if (!nameNs.prefix && nameNs.localName === 'xmlns') { + ns = { uri: value }; + } + + if (!ns) { + return { + name: name, + value: value + }; + } + + if (model && model.getPackage(value)) { + + // register well known namespace + this.logNamespace(ns, true, true); + } else { + + // log custom namespace directly as used + var actualNs = this.logNamespaceUsed(ns, true); + + this.getNamespaces().logUsed(actualNs); + } + }; + + + /** + * Parse namespaces and return a list of left over generic attributes + * + * @param {Object} element + * @return {Array} + */ + ElementSerializer.prototype.parseNsAttributes = function(element, attrs) { + var self = this; + + var genericAttrs = element.$attrs; + + var attributes = []; + + // parse namespace attributes first + // and log them. push non namespace attributes to a list + // and process them later + forEach$1(genericAttrs, function(value, name) { + + var nonNsAttr = self.parseNsAttribute(element, name, value); + + if (nonNsAttr) { + attributes.push(nonNsAttr); + } + }); + + return attributes; + }; + + ElementSerializer.prototype.parseGenericAttributes = function(element, attributes) { + + var self = this; + + forEach$1(attributes, function(attr) { + + // do not serialize xsi:type attribute + // it is set manually based on the actual implementation type + if (attr.name === XSI_TYPE) { + return; + } + + try { + self.addAttribute(self.nsAttributeName(attr.name), attr.value); + } catch (e) { + console.warn( + 'missing namespace information for ', + attr.name, '=', attr.value, 'on', element, + e); + } + }); + }; + + ElementSerializer.prototype.parseContainments = function(properties) { + + var self = this, + body = this.body, + element = this.element; + + forEach$1(properties, function(p) { + var value = element.get(p.name), + isReference = p.isReference, + isMany = p.isMany; + + if (!isMany) { + value = [ value ]; + } + + if (p.isBody) { + body.push(new BodySerializer().build(p, value[0])); + } else + if (isSimple(p.type)) { + forEach$1(value, function(v) { + body.push(new ValueSerializer(self.addTagName(self.nsPropertyTagName(p))).build(p, v)); + }); + } else + if (isReference) { + forEach$1(value, function(v) { + body.push(new ReferenceSerializer(self.addTagName(self.nsPropertyTagName(p))).build(v)); + }); + } else { + + // allow serialization via type + // rather than element name + var asType = serializeAsType(p), + asProperty = serializeAsProperty(p); + + forEach$1(value, function(v) { + var serializer; + + if (asType) { + serializer = new TypeSerializer(self, p); + } else + if (asProperty) { + serializer = new ElementSerializer(self, p); + } else { + serializer = new ElementSerializer(self); + } + + body.push(serializer.build(v)); + }); + } + }); + }; + + ElementSerializer.prototype.getNamespaces = function(local) { + + var namespaces = this.namespaces, + parent = this.parent, + parentNamespaces; + + if (!namespaces) { + parentNamespaces = parent && parent.getNamespaces(); + + if (local || !parentNamespaces) { + this.namespaces = namespaces = new Namespaces(parentNamespaces); + } else { + namespaces = parentNamespaces; + } + } + + return namespaces; + }; + + ElementSerializer.prototype.logNamespace = function(ns, wellknown, local) { + var namespaces = this.getNamespaces(local); + + var nsUri = ns.uri, + nsPrefix = ns.prefix; + + var existing = namespaces.byUri(nsUri); + + if (!existing || local) { + namespaces.add(ns, wellknown); + } + + namespaces.mapPrefix(nsPrefix, nsUri); + + return ns; + }; + + ElementSerializer.prototype.logNamespaceUsed = function(ns, local) { + var element = this.element, + model = element.$model, + namespaces = this.getNamespaces(local); + + // ns may be + // + // * prefix only + // * prefix:uri + // * localName only + + var prefix = ns.prefix, + uri = ns.uri, + newPrefix, idx, + wellknownUri; + + // handle anonymous namespaces (elementForm=unqualified), cf. #23 + if (!prefix && !uri) { + return { localName: ns.localName }; + } + + wellknownUri = DEFAULT_NS_MAP[prefix] || model && (model.getPackage(prefix) || {}).uri; + + uri = uri || wellknownUri || namespaces.uriByPrefix(prefix); + + if (!uri) { + throw new Error('no namespace uri given for prefix <' + prefix + '>'); + } + + ns = namespaces.byUri(uri); + + if (!ns) { + newPrefix = prefix; + idx = 1; + + // find a prefix that is not mapped yet + while (namespaces.uriByPrefix(newPrefix)) { + newPrefix = prefix + '_' + idx++; + } + + ns = this.logNamespace({ prefix: newPrefix, uri: uri }, wellknownUri === uri); + } + + if (prefix) { + namespaces.mapPrefix(prefix, uri); + } + + return ns; + }; + + ElementSerializer.prototype.parseAttributes = function(properties) { + var self = this, + element = this.element; + + forEach$1(properties, function(p) { + + var value = element.get(p.name); + + if (p.isReference) { + + if (!p.isMany) { + value = value.id; + } + else { + var values = []; + forEach$1(value, function(v) { + values.push(v.id); + }); + + // IDREFS is a whitespace-separated list of references. + value = values.join(' '); + } + + } + + self.addAttribute(self.nsAttributeName(p), value); + }); + }; + + ElementSerializer.prototype.addTagName = function(nsTagName) { + var actualNs = this.logNamespaceUsed(nsTagName); + + this.getNamespaces().logUsed(actualNs); + + return nsName(nsTagName); + }; + + ElementSerializer.prototype.addAttribute = function(name, value) { + var attrs = this.attrs; + + if (isString(value)) { + value = escapeAttr(value); + } + + // de-duplicate attributes + // https://github.com/bpmn-io/moddle-xml/issues/66 + var idx = findIndex(attrs, function(element) { + return ( + element.name.localName === name.localName && + element.name.uri === name.uri && + element.name.prefix === name.prefix + ); + }); + + var attr = { name: name, value: value }; + + if (idx !== -1) { + attrs.splice(idx, 1, attr); + } else { + attrs.push(attr); + } + }; + + ElementSerializer.prototype.serializeAttributes = function(writer) { + var attrs = this.attrs, + namespaces = this.namespaces; + + if (namespaces) { + attrs = getNsAttrs(namespaces).concat(attrs); + } + + forEach$1(attrs, function(a) { + writer + .append(' ') + .append(nsName(a.name)).append('="').append(a.value).append('"'); + }); + }; + + ElementSerializer.prototype.serializeTo = function(writer) { + var firstBody = this.body[0], + indent = firstBody && firstBody.constructor !== BodySerializer; + + writer + .appendIndent() + .append('<' + this.tagName); + + this.serializeAttributes(writer); + + writer.append(firstBody ? '>' : ' />'); + + if (firstBody) { + + if (indent) { + writer + .appendNewLine() + .indent(); + } + + forEach$1(this.body, function(b) { + b.serializeTo(writer); + }); + + if (indent) { + writer + .unindent() + .appendIndent(); + } + + writer.append(''); + } + + writer.appendNewLine(); + }; + + /** + * A serializer for types that handles serialization of data types + */ + function TypeSerializer(parent, propertyDescriptor) { + ElementSerializer.call(this, parent, propertyDescriptor); + } + + inherits(TypeSerializer, ElementSerializer); + + TypeSerializer.prototype.parseNsAttributes = function(element) { + + // extracted attributes + var attributes = ElementSerializer.prototype.parseNsAttributes.call(this, element); + + var descriptor = element.$descriptor; + + // only serialize xsi:type if necessary + if (descriptor.name === this.propertyDescriptor.type) { + return attributes; + } + + var typeNs = this.typeNs = this.nsTagName(descriptor); + this.getNamespaces().logUsed(this.typeNs); + + // add xsi:type attribute to represent the elements + // actual type + + var pkg = element.$model.getPackage(typeNs.uri), + typePrefix = (pkg.xml && pkg.xml.typePrefix) || ''; + + this.addAttribute( + this.nsAttributeName(XSI_TYPE), + (typeNs.prefix ? typeNs.prefix + ':' : '') + typePrefix + descriptor.ns.localName + ); + + return attributes; + }; + + TypeSerializer.prototype.isLocalNs = function(ns) { + return ns.uri === (this.typeNs || this.ns).uri; + }; + + function SavingWriter() { + this.value = ''; + + this.write = function(str) { + this.value += str; + }; + } + + function FormatingWriter(out, format) { + + var indent = ['']; + + this.append = function(str) { + out.write(str); + + return this; + }; + + this.appendNewLine = function() { + if (format) { + out.write('\n'); + } + + return this; + }; + + this.appendIndent = function() { + if (format) { + out.write(indent.join(' ')); + } + + return this; + }; + + this.indent = function() { + indent.push(''); + return this; + }; + + this.unindent = function() { + indent.pop(); + return this; + }; + } + + /** + * A writer for meta-model backed document trees + * + * @param {Object} options output options to pass into the writer + */ + function Writer(options) { + + options = assign({ format: false, preamble: true }, options || {}); + + function toXML(tree, writer) { + var internalWriter = writer || new SavingWriter(); + var formatingWriter = new FormatingWriter(internalWriter, options.format); + + if (options.preamble) { + formatingWriter.append(XML_PREAMBLE); + } + + new ElementSerializer().build(tree).serializeTo(formatingWriter); + + if (!writer) { + return internalWriter.value; + } + } + + return { + toXML: toXML + }; + } + + /** + * A sub class of {@link Moddle} with support for import and export of BPMN 2.0 xml files. + * + * @class BpmnModdle + * @extends Moddle + * + * @param {Object|Array} packages to use for instantiating the model + * @param {Object} [options] additional options to pass over + */ + function BpmnModdle(packages, options) { + Moddle.call(this, packages, options); + } + + BpmnModdle.prototype = Object.create(Moddle.prototype); + + /** + * The fromXML result. + * + * @typedef {Object} ParseResult + * + * @property {ModdleElement} rootElement + * @property {Array} references + * @property {Array} warnings + * @property {Object} elementsById - a mapping containing each ID -> ModdleElement + */ + + /** + * The fromXML error. + * + * @typedef {Error} ParseError + * + * @property {Array} warnings + */ + + /** + * Instantiates a BPMN model tree from a given xml string. + * + * @param {String} xmlStr + * @param {String} [typeName='bpmn:Definitions'] name of the root element + * @param {Object} [options] options to pass to the underlying reader + * + * @returns {Promise} + */ + BpmnModdle.prototype.fromXML = function(xmlStr, typeName, options) { + + if (!isString(typeName)) { + options = typeName; + typeName = 'bpmn:Definitions'; + } + + var reader = new Reader(assign({ model: this, lax: true }, options)); + var rootHandler = reader.handler(typeName); + + return reader.fromXML(xmlStr, rootHandler); + }; + + + /** + * The toXML result. + * + * @typedef {Object} SerializationResult + * + * @property {String} xml + */ + + /** + * Serializes a BPMN 2.0 object tree to XML. + * + * @param {String} element the root element, typically an instance of `bpmn:Definitions` + * @param {Object} [options] to pass to the underlying writer + * + * @returns {Promise} + */ + BpmnModdle.prototype.toXML = function(element, options) { + + var writer = new Writer(options); + + return new Promise(function(resolve, reject) { + try { + var result = writer.toXML(element); + + return resolve({ + xml: result + }); + } catch (err) { + return reject(err); + } + }); + }; + + var name$5 = "BPMN20"; + var uri$5 = "http://www.omg.org/spec/BPMN/20100524/MODEL"; + var prefix$5 = "bpmn"; + var associations$5 = [ + ]; + var types$5 = [ + { + name: "Interface", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "operations", + type: "Operation", + isMany: true + }, + { + name: "implementationRef", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Operation", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "inMessageRef", + type: "Message", + isReference: true + }, + { + name: "outMessageRef", + type: "Message", + isReference: true + }, + { + name: "errorRef", + type: "Error", + isMany: true, + isReference: true + }, + { + name: "implementationRef", + isAttr: true, + type: "String" + } + ] + }, + { + name: "EndPoint", + superClass: [ + "RootElement" + ] + }, + { + name: "Auditing", + superClass: [ + "BaseElement" + ] + }, + { + name: "GlobalTask", + superClass: [ + "CallableElement" + ], + properties: [ + { + name: "resources", + type: "ResourceRole", + isMany: true + } + ] + }, + { + name: "Monitoring", + superClass: [ + "BaseElement" + ] + }, + { + name: "Performer", + superClass: [ + "ResourceRole" + ] + }, + { + name: "Process", + superClass: [ + "FlowElementsContainer", + "CallableElement" + ], + properties: [ + { + name: "processType", + type: "ProcessType", + isAttr: true + }, + { + name: "isClosed", + isAttr: true, + type: "Boolean" + }, + { + name: "auditing", + type: "Auditing" + }, + { + name: "monitoring", + type: "Monitoring" + }, + { + name: "properties", + type: "Property", + isMany: true + }, + { + name: "laneSets", + isMany: true, + replaces: "FlowElementsContainer#laneSets", + type: "LaneSet" + }, + { + name: "flowElements", + isMany: true, + replaces: "FlowElementsContainer#flowElements", + type: "FlowElement" + }, + { + name: "artifacts", + type: "Artifact", + isMany: true + }, + { + name: "resources", + type: "ResourceRole", + isMany: true + }, + { + name: "correlationSubscriptions", + type: "CorrelationSubscription", + isMany: true + }, + { + name: "supports", + type: "Process", + isMany: true, + isReference: true + }, + { + name: "definitionalCollaborationRef", + type: "Collaboration", + isAttr: true, + isReference: true + }, + { + name: "isExecutable", + isAttr: true, + type: "Boolean" + } + ] + }, + { + name: "LaneSet", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "lanes", + type: "Lane", + isMany: true + }, + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Lane", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "partitionElementRef", + type: "BaseElement", + isAttr: true, + isReference: true + }, + { + name: "partitionElement", + type: "BaseElement" + }, + { + name: "flowNodeRef", + type: "FlowNode", + isMany: true, + isReference: true + }, + { + name: "childLaneSet", + type: "LaneSet", + xml: { + serialize: "xsi:type" + } + } + ] + }, + { + name: "GlobalManualTask", + superClass: [ + "GlobalTask" + ] + }, + { + name: "ManualTask", + superClass: [ + "Task" + ] + }, + { + name: "UserTask", + superClass: [ + "Task" + ], + properties: [ + { + name: "renderings", + type: "Rendering", + isMany: true + }, + { + name: "implementation", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Rendering", + superClass: [ + "BaseElement" + ] + }, + { + name: "HumanPerformer", + superClass: [ + "Performer" + ] + }, + { + name: "PotentialOwner", + superClass: [ + "HumanPerformer" + ] + }, + { + name: "GlobalUserTask", + superClass: [ + "GlobalTask" + ], + properties: [ + { + name: "implementation", + isAttr: true, + type: "String" + }, + { + name: "renderings", + type: "Rendering", + isMany: true + } + ] + }, + { + name: "Gateway", + isAbstract: true, + superClass: [ + "FlowNode" + ], + properties: [ + { + name: "gatewayDirection", + type: "GatewayDirection", + "default": "Unspecified", + isAttr: true + } + ] + }, + { + name: "EventBasedGateway", + superClass: [ + "Gateway" + ], + properties: [ + { + name: "instantiate", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "eventGatewayType", + type: "EventBasedGatewayType", + isAttr: true, + "default": "Exclusive" + } + ] + }, + { + name: "ComplexGateway", + superClass: [ + "Gateway" + ], + properties: [ + { + name: "activationCondition", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "default", + type: "SequenceFlow", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ExclusiveGateway", + superClass: [ + "Gateway" + ], + properties: [ + { + name: "default", + type: "SequenceFlow", + isAttr: true, + isReference: true + } + ] + }, + { + name: "InclusiveGateway", + superClass: [ + "Gateway" + ], + properties: [ + { + name: "default", + type: "SequenceFlow", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ParallelGateway", + superClass: [ + "Gateway" + ] + }, + { + name: "RootElement", + isAbstract: true, + superClass: [ + "BaseElement" + ] + }, + { + name: "Relationship", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "type", + isAttr: true, + type: "String" + }, + { + name: "direction", + type: "RelationshipDirection", + isAttr: true + }, + { + name: "source", + isMany: true, + isReference: true, + type: "Element" + }, + { + name: "target", + isMany: true, + isReference: true, + type: "Element" + } + ] + }, + { + name: "BaseElement", + isAbstract: true, + properties: [ + { + name: "id", + isAttr: true, + type: "String", + isId: true + }, + { + name: "documentation", + type: "Documentation", + isMany: true + }, + { + name: "extensionDefinitions", + type: "ExtensionDefinition", + isMany: true, + isReference: true + }, + { + name: "extensionElements", + type: "ExtensionElements" + } + ] + }, + { + name: "Extension", + properties: [ + { + name: "mustUnderstand", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "definition", + type: "ExtensionDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ExtensionDefinition", + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "extensionAttributeDefinitions", + type: "ExtensionAttributeDefinition", + isMany: true + } + ] + }, + { + name: "ExtensionAttributeDefinition", + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "type", + isAttr: true, + type: "String" + }, + { + name: "isReference", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "extensionDefinition", + type: "ExtensionDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ExtensionElements", + properties: [ + { + name: "valueRef", + isAttr: true, + isReference: true, + type: "Element" + }, + { + name: "values", + type: "Element", + isMany: true + }, + { + name: "extensionAttributeDefinition", + type: "ExtensionAttributeDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Documentation", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "text", + type: "String", + isBody: true + }, + { + name: "textFormat", + "default": "text/plain", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Event", + isAbstract: true, + superClass: [ + "FlowNode", + "InteractionNode" + ], + properties: [ + { + name: "properties", + type: "Property", + isMany: true + } + ] + }, + { + name: "IntermediateCatchEvent", + superClass: [ + "CatchEvent" + ] + }, + { + name: "IntermediateThrowEvent", + superClass: [ + "ThrowEvent" + ] + }, + { + name: "EndEvent", + superClass: [ + "ThrowEvent" + ] + }, + { + name: "StartEvent", + superClass: [ + "CatchEvent" + ], + properties: [ + { + name: "isInterrupting", + "default": true, + isAttr: true, + type: "Boolean" + } + ] + }, + { + name: "ThrowEvent", + isAbstract: true, + superClass: [ + "Event" + ], + properties: [ + { + name: "dataInputs", + type: "DataInput", + isMany: true + }, + { + name: "dataInputAssociations", + type: "DataInputAssociation", + isMany: true + }, + { + name: "inputSet", + type: "InputSet" + }, + { + name: "eventDefinitions", + type: "EventDefinition", + isMany: true + }, + { + name: "eventDefinitionRef", + type: "EventDefinition", + isMany: true, + isReference: true + } + ] + }, + { + name: "CatchEvent", + isAbstract: true, + superClass: [ + "Event" + ], + properties: [ + { + name: "parallelMultiple", + isAttr: true, + type: "Boolean", + "default": false + }, + { + name: "dataOutputs", + type: "DataOutput", + isMany: true + }, + { + name: "dataOutputAssociations", + type: "DataOutputAssociation", + isMany: true + }, + { + name: "outputSet", + type: "OutputSet" + }, + { + name: "eventDefinitions", + type: "EventDefinition", + isMany: true + }, + { + name: "eventDefinitionRef", + type: "EventDefinition", + isMany: true, + isReference: true + } + ] + }, + { + name: "BoundaryEvent", + superClass: [ + "CatchEvent" + ], + properties: [ + { + name: "cancelActivity", + "default": true, + isAttr: true, + type: "Boolean" + }, + { + name: "attachedToRef", + type: "Activity", + isAttr: true, + isReference: true + } + ] + }, + { + name: "EventDefinition", + isAbstract: true, + superClass: [ + "RootElement" + ] + }, + { + name: "CancelEventDefinition", + superClass: [ + "EventDefinition" + ] + }, + { + name: "ErrorEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "errorRef", + type: "Error", + isAttr: true, + isReference: true + } + ] + }, + { + name: "TerminateEventDefinition", + superClass: [ + "EventDefinition" + ] + }, + { + name: "EscalationEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "escalationRef", + type: "Escalation", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Escalation", + properties: [ + { + name: "structureRef", + type: "ItemDefinition", + isAttr: true, + isReference: true + }, + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "escalationCode", + isAttr: true, + type: "String" + } + ], + superClass: [ + "RootElement" + ] + }, + { + name: "CompensateEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "waitForCompletion", + isAttr: true, + type: "Boolean", + "default": true + }, + { + name: "activityRef", + type: "Activity", + isAttr: true, + isReference: true + } + ] + }, + { + name: "TimerEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "timeDate", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "timeCycle", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "timeDuration", + type: "Expression", + xml: { + serialize: "xsi:type" + } + } + ] + }, + { + name: "LinkEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "target", + type: "LinkEventDefinition", + isAttr: true, + isReference: true + }, + { + name: "source", + type: "LinkEventDefinition", + isMany: true, + isReference: true + } + ] + }, + { + name: "MessageEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "messageRef", + type: "Message", + isAttr: true, + isReference: true + }, + { + name: "operationRef", + type: "Operation", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ConditionalEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "condition", + type: "Expression", + xml: { + serialize: "xsi:type" + } + } + ] + }, + { + name: "SignalEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "signalRef", + type: "Signal", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Signal", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "structureRef", + type: "ItemDefinition", + isAttr: true, + isReference: true + }, + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ImplicitThrowEvent", + superClass: [ + "ThrowEvent" + ] + }, + { + name: "DataState", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ItemAwareElement", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "itemSubjectRef", + type: "ItemDefinition", + isAttr: true, + isReference: true + }, + { + name: "dataState", + type: "DataState" + } + ] + }, + { + name: "DataAssociation", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "sourceRef", + type: "ItemAwareElement", + isMany: true, + isReference: true + }, + { + name: "targetRef", + type: "ItemAwareElement", + isReference: true + }, + { + name: "transformation", + type: "FormalExpression", + xml: { + serialize: "property" + } + }, + { + name: "assignment", + type: "Assignment", + isMany: true + } + ] + }, + { + name: "DataInput", + superClass: [ + "ItemAwareElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "isCollection", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "inputSetRef", + type: "InputSet", + isMany: true, + isVirtual: true, + isReference: true + }, + { + name: "inputSetWithOptional", + type: "InputSet", + isMany: true, + isVirtual: true, + isReference: true + }, + { + name: "inputSetWithWhileExecuting", + type: "InputSet", + isMany: true, + isVirtual: true, + isReference: true + } + ] + }, + { + name: "DataOutput", + superClass: [ + "ItemAwareElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "isCollection", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "outputSetRef", + type: "OutputSet", + isMany: true, + isVirtual: true, + isReference: true + }, + { + name: "outputSetWithOptional", + type: "OutputSet", + isMany: true, + isVirtual: true, + isReference: true + }, + { + name: "outputSetWithWhileExecuting", + type: "OutputSet", + isMany: true, + isVirtual: true, + isReference: true + } + ] + }, + { + name: "InputSet", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "dataInputRefs", + type: "DataInput", + isMany: true, + isReference: true + }, + { + name: "optionalInputRefs", + type: "DataInput", + isMany: true, + isReference: true + }, + { + name: "whileExecutingInputRefs", + type: "DataInput", + isMany: true, + isReference: true + }, + { + name: "outputSetRefs", + type: "OutputSet", + isMany: true, + isReference: true + } + ] + }, + { + name: "OutputSet", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "dataOutputRefs", + type: "DataOutput", + isMany: true, + isReference: true + }, + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "inputSetRefs", + type: "InputSet", + isMany: true, + isReference: true + }, + { + name: "optionalOutputRefs", + type: "DataOutput", + isMany: true, + isReference: true + }, + { + name: "whileExecutingOutputRefs", + type: "DataOutput", + isMany: true, + isReference: true + } + ] + }, + { + name: "Property", + superClass: [ + "ItemAwareElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "DataInputAssociation", + superClass: [ + "DataAssociation" + ] + }, + { + name: "DataOutputAssociation", + superClass: [ + "DataAssociation" + ] + }, + { + name: "InputOutputSpecification", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "dataInputs", + type: "DataInput", + isMany: true + }, + { + name: "dataOutputs", + type: "DataOutput", + isMany: true + }, + { + name: "inputSets", + type: "InputSet", + isMany: true + }, + { + name: "outputSets", + type: "OutputSet", + isMany: true + } + ] + }, + { + name: "DataObject", + superClass: [ + "FlowElement", + "ItemAwareElement" + ], + properties: [ + { + name: "isCollection", + "default": false, + isAttr: true, + type: "Boolean" + } + ] + }, + { + name: "InputOutputBinding", + properties: [ + { + name: "inputDataRef", + type: "InputSet", + isAttr: true, + isReference: true + }, + { + name: "outputDataRef", + type: "OutputSet", + isAttr: true, + isReference: true + }, + { + name: "operationRef", + type: "Operation", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Assignment", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "from", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "to", + type: "Expression", + xml: { + serialize: "xsi:type" + } + } + ] + }, + { + name: "DataStore", + superClass: [ + "RootElement", + "ItemAwareElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "capacity", + isAttr: true, + type: "Integer" + }, + { + name: "isUnlimited", + "default": true, + isAttr: true, + type: "Boolean" + } + ] + }, + { + name: "DataStoreReference", + superClass: [ + "ItemAwareElement", + "FlowElement" + ], + properties: [ + { + name: "dataStoreRef", + type: "DataStore", + isAttr: true, + isReference: true + } + ] + }, + { + name: "DataObjectReference", + superClass: [ + "ItemAwareElement", + "FlowElement" + ], + properties: [ + { + name: "dataObjectRef", + type: "DataObject", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ConversationLink", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "sourceRef", + type: "InteractionNode", + isAttr: true, + isReference: true + }, + { + name: "targetRef", + type: "InteractionNode", + isAttr: true, + isReference: true + }, + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ConversationAssociation", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "innerConversationNodeRef", + type: "ConversationNode", + isAttr: true, + isReference: true + }, + { + name: "outerConversationNodeRef", + type: "ConversationNode", + isAttr: true, + isReference: true + } + ] + }, + { + name: "CallConversation", + superClass: [ + "ConversationNode" + ], + properties: [ + { + name: "calledCollaborationRef", + type: "Collaboration", + isAttr: true, + isReference: true + }, + { + name: "participantAssociations", + type: "ParticipantAssociation", + isMany: true + } + ] + }, + { + name: "Conversation", + superClass: [ + "ConversationNode" + ] + }, + { + name: "SubConversation", + superClass: [ + "ConversationNode" + ], + properties: [ + { + name: "conversationNodes", + type: "ConversationNode", + isMany: true + } + ] + }, + { + name: "ConversationNode", + isAbstract: true, + superClass: [ + "InteractionNode", + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "participantRef", + type: "Participant", + isMany: true, + isReference: true + }, + { + name: "messageFlowRefs", + type: "MessageFlow", + isMany: true, + isReference: true + }, + { + name: "correlationKeys", + type: "CorrelationKey", + isMany: true + } + ] + }, + { + name: "GlobalConversation", + superClass: [ + "Collaboration" + ] + }, + { + name: "PartnerEntity", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "participantRef", + type: "Participant", + isMany: true, + isReference: true + } + ] + }, + { + name: "PartnerRole", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "participantRef", + type: "Participant", + isMany: true, + isReference: true + } + ] + }, + { + name: "CorrelationProperty", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "correlationPropertyRetrievalExpression", + type: "CorrelationPropertyRetrievalExpression", + isMany: true + }, + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "type", + type: "ItemDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Error", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "structureRef", + type: "ItemDefinition", + isAttr: true, + isReference: true + }, + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "errorCode", + isAttr: true, + type: "String" + } + ] + }, + { + name: "CorrelationKey", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "correlationPropertyRef", + type: "CorrelationProperty", + isMany: true, + isReference: true + }, + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Expression", + superClass: [ + "BaseElement" + ], + isAbstract: false, + properties: [ + { + name: "body", + isBody: true, + type: "String" + } + ] + }, + { + name: "FormalExpression", + superClass: [ + "Expression" + ], + properties: [ + { + name: "language", + isAttr: true, + type: "String" + }, + { + name: "evaluatesToTypeRef", + type: "ItemDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Message", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "itemRef", + type: "ItemDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ItemDefinition", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "itemKind", + type: "ItemKind", + isAttr: true + }, + { + name: "structureRef", + isAttr: true, + type: "String" + }, + { + name: "isCollection", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "import", + type: "Import", + isAttr: true, + isReference: true + } + ] + }, + { + name: "FlowElement", + isAbstract: true, + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "auditing", + type: "Auditing" + }, + { + name: "monitoring", + type: "Monitoring" + }, + { + name: "categoryValueRef", + type: "CategoryValue", + isMany: true, + isReference: true + } + ] + }, + { + name: "SequenceFlow", + superClass: [ + "FlowElement" + ], + properties: [ + { + name: "isImmediate", + isAttr: true, + type: "Boolean" + }, + { + name: "conditionExpression", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "sourceRef", + type: "FlowNode", + isAttr: true, + isReference: true + }, + { + name: "targetRef", + type: "FlowNode", + isAttr: true, + isReference: true + } + ] + }, + { + name: "FlowElementsContainer", + isAbstract: true, + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "laneSets", + type: "LaneSet", + isMany: true + }, + { + name: "flowElements", + type: "FlowElement", + isMany: true + } + ] + }, + { + name: "CallableElement", + isAbstract: true, + superClass: [ + "RootElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "ioSpecification", + type: "InputOutputSpecification", + xml: { + serialize: "property" + } + }, + { + name: "supportedInterfaceRef", + type: "Interface", + isMany: true, + isReference: true + }, + { + name: "ioBinding", + type: "InputOutputBinding", + isMany: true, + xml: { + serialize: "property" + } + } + ] + }, + { + name: "FlowNode", + isAbstract: true, + superClass: [ + "FlowElement" + ], + properties: [ + { + name: "incoming", + type: "SequenceFlow", + isMany: true, + isReference: true + }, + { + name: "outgoing", + type: "SequenceFlow", + isMany: true, + isReference: true + }, + { + name: "lanes", + type: "Lane", + isMany: true, + isVirtual: true, + isReference: true + } + ] + }, + { + name: "CorrelationPropertyRetrievalExpression", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "messagePath", + type: "FormalExpression" + }, + { + name: "messageRef", + type: "Message", + isAttr: true, + isReference: true + } + ] + }, + { + name: "CorrelationPropertyBinding", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "dataPath", + type: "FormalExpression" + }, + { + name: "correlationPropertyRef", + type: "CorrelationProperty", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Resource", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "resourceParameters", + type: "ResourceParameter", + isMany: true + } + ] + }, + { + name: "ResourceParameter", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "isRequired", + isAttr: true, + type: "Boolean" + }, + { + name: "type", + type: "ItemDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "CorrelationSubscription", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "correlationKeyRef", + type: "CorrelationKey", + isAttr: true, + isReference: true + }, + { + name: "correlationPropertyBinding", + type: "CorrelationPropertyBinding", + isMany: true + } + ] + }, + { + name: "MessageFlow", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "sourceRef", + type: "InteractionNode", + isAttr: true, + isReference: true + }, + { + name: "targetRef", + type: "InteractionNode", + isAttr: true, + isReference: true + }, + { + name: "messageRef", + type: "Message", + isAttr: true, + isReference: true + } + ] + }, + { + name: "MessageFlowAssociation", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "innerMessageFlowRef", + type: "MessageFlow", + isAttr: true, + isReference: true + }, + { + name: "outerMessageFlowRef", + type: "MessageFlow", + isAttr: true, + isReference: true + } + ] + }, + { + name: "InteractionNode", + isAbstract: true, + properties: [ + { + name: "incomingConversationLinks", + type: "ConversationLink", + isMany: true, + isVirtual: true, + isReference: true + }, + { + name: "outgoingConversationLinks", + type: "ConversationLink", + isMany: true, + isVirtual: true, + isReference: true + } + ] + }, + { + name: "Participant", + superClass: [ + "InteractionNode", + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "interfaceRef", + type: "Interface", + isMany: true, + isReference: true + }, + { + name: "participantMultiplicity", + type: "ParticipantMultiplicity" + }, + { + name: "endPointRefs", + type: "EndPoint", + isMany: true, + isReference: true + }, + { + name: "processRef", + type: "Process", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ParticipantAssociation", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "innerParticipantRef", + type: "Participant", + isAttr: true, + isReference: true + }, + { + name: "outerParticipantRef", + type: "Participant", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ParticipantMultiplicity", + properties: [ + { + name: "minimum", + "default": 0, + isAttr: true, + type: "Integer" + }, + { + name: "maximum", + "default": 1, + isAttr: true, + type: "Integer" + } + ], + superClass: [ + "BaseElement" + ] + }, + { + name: "Collaboration", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "isClosed", + isAttr: true, + type: "Boolean" + }, + { + name: "participants", + type: "Participant", + isMany: true + }, + { + name: "messageFlows", + type: "MessageFlow", + isMany: true + }, + { + name: "artifacts", + type: "Artifact", + isMany: true + }, + { + name: "conversations", + type: "ConversationNode", + isMany: true + }, + { + name: "conversationAssociations", + type: "ConversationAssociation" + }, + { + name: "participantAssociations", + type: "ParticipantAssociation", + isMany: true + }, + { + name: "messageFlowAssociations", + type: "MessageFlowAssociation", + isMany: true + }, + { + name: "correlationKeys", + type: "CorrelationKey", + isMany: true + }, + { + name: "choreographyRef", + type: "Choreography", + isMany: true, + isReference: true + }, + { + name: "conversationLinks", + type: "ConversationLink", + isMany: true + } + ] + }, + { + name: "ChoreographyActivity", + isAbstract: true, + superClass: [ + "FlowNode" + ], + properties: [ + { + name: "participantRef", + type: "Participant", + isMany: true, + isReference: true + }, + { + name: "initiatingParticipantRef", + type: "Participant", + isAttr: true, + isReference: true + }, + { + name: "correlationKeys", + type: "CorrelationKey", + isMany: true + }, + { + name: "loopType", + type: "ChoreographyLoopType", + "default": "None", + isAttr: true + } + ] + }, + { + name: "CallChoreography", + superClass: [ + "ChoreographyActivity" + ], + properties: [ + { + name: "calledChoreographyRef", + type: "Choreography", + isAttr: true, + isReference: true + }, + { + name: "participantAssociations", + type: "ParticipantAssociation", + isMany: true + } + ] + }, + { + name: "SubChoreography", + superClass: [ + "ChoreographyActivity", + "FlowElementsContainer" + ], + properties: [ + { + name: "artifacts", + type: "Artifact", + isMany: true + } + ] + }, + { + name: "ChoreographyTask", + superClass: [ + "ChoreographyActivity" + ], + properties: [ + { + name: "messageFlowRef", + type: "MessageFlow", + isMany: true, + isReference: true + } + ] + }, + { + name: "Choreography", + superClass: [ + "Collaboration", + "FlowElementsContainer" + ] + }, + { + name: "GlobalChoreographyTask", + superClass: [ + "Choreography" + ], + properties: [ + { + name: "initiatingParticipantRef", + type: "Participant", + isAttr: true, + isReference: true + } + ] + }, + { + name: "TextAnnotation", + superClass: [ + "Artifact" + ], + properties: [ + { + name: "text", + type: "String" + }, + { + name: "textFormat", + "default": "text/plain", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Group", + superClass: [ + "Artifact" + ], + properties: [ + { + name: "categoryValueRef", + type: "CategoryValue", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Association", + superClass: [ + "Artifact" + ], + properties: [ + { + name: "associationDirection", + type: "AssociationDirection", + isAttr: true + }, + { + name: "sourceRef", + type: "BaseElement", + isAttr: true, + isReference: true + }, + { + name: "targetRef", + type: "BaseElement", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Category", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "categoryValue", + type: "CategoryValue", + isMany: true + }, + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Artifact", + isAbstract: true, + superClass: [ + "BaseElement" + ] + }, + { + name: "CategoryValue", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "categorizedFlowElements", + type: "FlowElement", + isMany: true, + isVirtual: true, + isReference: true + }, + { + name: "value", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Activity", + isAbstract: true, + superClass: [ + "FlowNode" + ], + properties: [ + { + name: "isForCompensation", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "default", + type: "SequenceFlow", + isAttr: true, + isReference: true + }, + { + name: "ioSpecification", + type: "InputOutputSpecification", + xml: { + serialize: "property" + } + }, + { + name: "boundaryEventRefs", + type: "BoundaryEvent", + isMany: true, + isReference: true + }, + { + name: "properties", + type: "Property", + isMany: true + }, + { + name: "dataInputAssociations", + type: "DataInputAssociation", + isMany: true + }, + { + name: "dataOutputAssociations", + type: "DataOutputAssociation", + isMany: true + }, + { + name: "startQuantity", + "default": 1, + isAttr: true, + type: "Integer" + }, + { + name: "resources", + type: "ResourceRole", + isMany: true + }, + { + name: "completionQuantity", + "default": 1, + isAttr: true, + type: "Integer" + }, + { + name: "loopCharacteristics", + type: "LoopCharacteristics" + } + ] + }, + { + name: "ServiceTask", + superClass: [ + "Task" + ], + properties: [ + { + name: "implementation", + isAttr: true, + type: "String" + }, + { + name: "operationRef", + type: "Operation", + isAttr: true, + isReference: true + } + ] + }, + { + name: "SubProcess", + superClass: [ + "Activity", + "FlowElementsContainer", + "InteractionNode" + ], + properties: [ + { + name: "triggeredByEvent", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "artifacts", + type: "Artifact", + isMany: true + } + ] + }, + { + name: "LoopCharacteristics", + isAbstract: true, + superClass: [ + "BaseElement" + ] + }, + { + name: "MultiInstanceLoopCharacteristics", + superClass: [ + "LoopCharacteristics" + ], + properties: [ + { + name: "isSequential", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "behavior", + type: "MultiInstanceBehavior", + "default": "All", + isAttr: true + }, + { + name: "loopCardinality", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "loopDataInputRef", + type: "ItemAwareElement", + isReference: true + }, + { + name: "loopDataOutputRef", + type: "ItemAwareElement", + isReference: true + }, + { + name: "inputDataItem", + type: "DataInput", + xml: { + serialize: "property" + } + }, + { + name: "outputDataItem", + type: "DataOutput", + xml: { + serialize: "property" + } + }, + { + name: "complexBehaviorDefinition", + type: "ComplexBehaviorDefinition", + isMany: true + }, + { + name: "completionCondition", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "oneBehaviorEventRef", + type: "EventDefinition", + isAttr: true, + isReference: true + }, + { + name: "noneBehaviorEventRef", + type: "EventDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "StandardLoopCharacteristics", + superClass: [ + "LoopCharacteristics" + ], + properties: [ + { + name: "testBefore", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "loopCondition", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "loopMaximum", + type: "Integer", + isAttr: true + } + ] + }, + { + name: "CallActivity", + superClass: [ + "Activity", + "InteractionNode" + ], + properties: [ + { + name: "calledElement", + type: "String", + isAttr: true + } + ] + }, + { + name: "Task", + superClass: [ + "Activity", + "InteractionNode" + ] + }, + { + name: "SendTask", + superClass: [ + "Task" + ], + properties: [ + { + name: "implementation", + isAttr: true, + type: "String" + }, + { + name: "operationRef", + type: "Operation", + isAttr: true, + isReference: true + }, + { + name: "messageRef", + type: "Message", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ReceiveTask", + superClass: [ + "Task" + ], + properties: [ + { + name: "implementation", + isAttr: true, + type: "String" + }, + { + name: "instantiate", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "operationRef", + type: "Operation", + isAttr: true, + isReference: true + }, + { + name: "messageRef", + type: "Message", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ScriptTask", + superClass: [ + "Task" + ], + properties: [ + { + name: "scriptFormat", + isAttr: true, + type: "String" + }, + { + name: "script", + type: "String" + } + ] + }, + { + name: "BusinessRuleTask", + superClass: [ + "Task" + ], + properties: [ + { + name: "implementation", + isAttr: true, + type: "String" + } + ] + }, + { + name: "AdHocSubProcess", + superClass: [ + "SubProcess" + ], + properties: [ + { + name: "completionCondition", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "ordering", + type: "AdHocOrdering", + isAttr: true + }, + { + name: "cancelRemainingInstances", + "default": true, + isAttr: true, + type: "Boolean" + } + ] + }, + { + name: "Transaction", + superClass: [ + "SubProcess" + ], + properties: [ + { + name: "protocol", + isAttr: true, + type: "String" + }, + { + name: "method", + isAttr: true, + type: "String" + } + ] + }, + { + name: "GlobalScriptTask", + superClass: [ + "GlobalTask" + ], + properties: [ + { + name: "scriptLanguage", + isAttr: true, + type: "String" + }, + { + name: "script", + isAttr: true, + type: "String" + } + ] + }, + { + name: "GlobalBusinessRuleTask", + superClass: [ + "GlobalTask" + ], + properties: [ + { + name: "implementation", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ComplexBehaviorDefinition", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "condition", + type: "FormalExpression" + }, + { + name: "event", + type: "ImplicitThrowEvent" + } + ] + }, + { + name: "ResourceRole", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "resourceRef", + type: "Resource", + isReference: true + }, + { + name: "resourceParameterBindings", + type: "ResourceParameterBinding", + isMany: true + }, + { + name: "resourceAssignmentExpression", + type: "ResourceAssignmentExpression" + }, + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ResourceParameterBinding", + properties: [ + { + name: "expression", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "parameterRef", + type: "ResourceParameter", + isAttr: true, + isReference: true + } + ], + superClass: [ + "BaseElement" + ] + }, + { + name: "ResourceAssignmentExpression", + properties: [ + { + name: "expression", + type: "Expression", + xml: { + serialize: "xsi:type" + } + } + ], + superClass: [ + "BaseElement" + ] + }, + { + name: "Import", + properties: [ + { + name: "importType", + isAttr: true, + type: "String" + }, + { + name: "location", + isAttr: true, + type: "String" + }, + { + name: "namespace", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Definitions", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "targetNamespace", + isAttr: true, + type: "String" + }, + { + name: "expressionLanguage", + "default": "http://www.w3.org/1999/XPath", + isAttr: true, + type: "String" + }, + { + name: "typeLanguage", + "default": "http://www.w3.org/2001/XMLSchema", + isAttr: true, + type: "String" + }, + { + name: "imports", + type: "Import", + isMany: true + }, + { + name: "extensions", + type: "Extension", + isMany: true + }, + { + name: "rootElements", + type: "RootElement", + isMany: true + }, + { + name: "diagrams", + isMany: true, + type: "bpmndi:BPMNDiagram" + }, + { + name: "exporter", + isAttr: true, + type: "String" + }, + { + name: "relationships", + type: "Relationship", + isMany: true + }, + { + name: "exporterVersion", + isAttr: true, + type: "String" + } + ] + } + ]; + var enumerations$3 = [ + { + name: "ProcessType", + literalValues: [ + { + name: "None" + }, + { + name: "Public" + }, + { + name: "Private" + } + ] + }, + { + name: "GatewayDirection", + literalValues: [ + { + name: "Unspecified" + }, + { + name: "Converging" + }, + { + name: "Diverging" + }, + { + name: "Mixed" + } + ] + }, + { + name: "EventBasedGatewayType", + literalValues: [ + { + name: "Parallel" + }, + { + name: "Exclusive" + } + ] + }, + { + name: "RelationshipDirection", + literalValues: [ + { + name: "None" + }, + { + name: "Forward" + }, + { + name: "Backward" + }, + { + name: "Both" + } + ] + }, + { + name: "ItemKind", + literalValues: [ + { + name: "Physical" + }, + { + name: "Information" + } + ] + }, + { + name: "ChoreographyLoopType", + literalValues: [ + { + name: "None" + }, + { + name: "Standard" + }, + { + name: "MultiInstanceSequential" + }, + { + name: "MultiInstanceParallel" + } + ] + }, + { + name: "AssociationDirection", + literalValues: [ + { + name: "None" + }, + { + name: "One" + }, + { + name: "Both" + } + ] + }, + { + name: "MultiInstanceBehavior", + literalValues: [ + { + name: "None" + }, + { + name: "One" + }, + { + name: "All" + }, + { + name: "Complex" + } + ] + }, + { + name: "AdHocOrdering", + literalValues: [ + { + name: "Parallel" + }, + { + name: "Sequential" + } + ] + } + ]; + var xml$1 = { + tagAlias: "lowerCase", + typePrefix: "t" + }; + var BpmnPackage = { + name: name$5, + uri: uri$5, + prefix: prefix$5, + associations: associations$5, + types: types$5, + enumerations: enumerations$3, + xml: xml$1 + }; + + var name$4 = "BPMNDI"; + var uri$4 = "http://www.omg.org/spec/BPMN/20100524/DI"; + var prefix$4 = "bpmndi"; + var types$4 = [ + { + name: "BPMNDiagram", + properties: [ + { + name: "plane", + type: "BPMNPlane", + redefines: "di:Diagram#rootElement" + }, + { + name: "labelStyle", + type: "BPMNLabelStyle", + isMany: true + } + ], + superClass: [ + "di:Diagram" + ] + }, + { + name: "BPMNPlane", + properties: [ + { + name: "bpmnElement", + isAttr: true, + isReference: true, + type: "bpmn:BaseElement", + redefines: "di:DiagramElement#modelElement" + } + ], + superClass: [ + "di:Plane" + ] + }, + { + name: "BPMNShape", + properties: [ + { + name: "bpmnElement", + isAttr: true, + isReference: true, + type: "bpmn:BaseElement", + redefines: "di:DiagramElement#modelElement" + }, + { + name: "isHorizontal", + isAttr: true, + type: "Boolean" + }, + { + name: "isExpanded", + isAttr: true, + type: "Boolean" + }, + { + name: "isMarkerVisible", + isAttr: true, + type: "Boolean" + }, + { + name: "label", + type: "BPMNLabel" + }, + { + name: "isMessageVisible", + isAttr: true, + type: "Boolean" + }, + { + name: "participantBandKind", + type: "ParticipantBandKind", + isAttr: true + }, + { + name: "choreographyActivityShape", + type: "BPMNShape", + isAttr: true, + isReference: true + } + ], + superClass: [ + "di:LabeledShape" + ] + }, + { + name: "BPMNEdge", + properties: [ + { + name: "label", + type: "BPMNLabel" + }, + { + name: "bpmnElement", + isAttr: true, + isReference: true, + type: "bpmn:BaseElement", + redefines: "di:DiagramElement#modelElement" + }, + { + name: "sourceElement", + isAttr: true, + isReference: true, + type: "di:DiagramElement", + redefines: "di:Edge#source" + }, + { + name: "targetElement", + isAttr: true, + isReference: true, + type: "di:DiagramElement", + redefines: "di:Edge#target" + }, + { + name: "messageVisibleKind", + type: "MessageVisibleKind", + isAttr: true, + "default": "initiating" + } + ], + superClass: [ + "di:LabeledEdge" + ] + }, + { + name: "BPMNLabel", + properties: [ + { + name: "labelStyle", + type: "BPMNLabelStyle", + isAttr: true, + isReference: true, + redefines: "di:DiagramElement#style" + } + ], + superClass: [ + "di:Label" + ] + }, + { + name: "BPMNLabelStyle", + properties: [ + { + name: "font", + type: "dc:Font" + } + ], + superClass: [ + "di:Style" + ] + } + ]; + var enumerations$2 = [ + { + name: "ParticipantBandKind", + literalValues: [ + { + name: "top_initiating" + }, + { + name: "middle_initiating" + }, + { + name: "bottom_initiating" + }, + { + name: "top_non_initiating" + }, + { + name: "middle_non_initiating" + }, + { + name: "bottom_non_initiating" + } + ] + }, + { + name: "MessageVisibleKind", + literalValues: [ + { + name: "initiating" + }, + { + name: "non_initiating" + } + ] + } + ]; + var associations$4 = [ + ]; + var BpmnDiPackage = { + name: name$4, + uri: uri$4, + prefix: prefix$4, + types: types$4, + enumerations: enumerations$2, + associations: associations$4 + }; + + var name$3 = "DC"; + var uri$3 = "http://www.omg.org/spec/DD/20100524/DC"; + var prefix$3 = "dc"; + var types$3 = [ + { + name: "Boolean" + }, + { + name: "Integer" + }, + { + name: "Real" + }, + { + name: "String" + }, + { + name: "Font", + properties: [ + { + name: "name", + type: "String", + isAttr: true + }, + { + name: "size", + type: "Real", + isAttr: true + }, + { + name: "isBold", + type: "Boolean", + isAttr: true + }, + { + name: "isItalic", + type: "Boolean", + isAttr: true + }, + { + name: "isUnderline", + type: "Boolean", + isAttr: true + }, + { + name: "isStrikeThrough", + type: "Boolean", + isAttr: true + } + ] + }, + { + name: "Point", + properties: [ + { + name: "x", + type: "Real", + "default": "0", + isAttr: true + }, + { + name: "y", + type: "Real", + "default": "0", + isAttr: true + } + ] + }, + { + name: "Bounds", + properties: [ + { + name: "x", + type: "Real", + "default": "0", + isAttr: true + }, + { + name: "y", + type: "Real", + "default": "0", + isAttr: true + }, + { + name: "width", + type: "Real", + isAttr: true + }, + { + name: "height", + type: "Real", + isAttr: true + } + ] + } + ]; + var associations$3 = [ + ]; + var DcPackage = { + name: name$3, + uri: uri$3, + prefix: prefix$3, + types: types$3, + associations: associations$3 + }; + + var name$2 = "DI"; + var uri$2 = "http://www.omg.org/spec/DD/20100524/DI"; + var prefix$2 = "di"; + var types$2 = [ + { + name: "DiagramElement", + isAbstract: true, + properties: [ + { + name: "id", + isAttr: true, + isId: true, + type: "String" + }, + { + name: "extension", + type: "Extension" + }, + { + name: "owningDiagram", + type: "Diagram", + isReadOnly: true, + isVirtual: true, + isReference: true + }, + { + name: "owningElement", + type: "DiagramElement", + isReadOnly: true, + isVirtual: true, + isReference: true + }, + { + name: "modelElement", + isReadOnly: true, + isVirtual: true, + isReference: true, + type: "Element" + }, + { + name: "style", + type: "Style", + isReadOnly: true, + isVirtual: true, + isReference: true + }, + { + name: "ownedElement", + type: "DiagramElement", + isReadOnly: true, + isMany: true, + isVirtual: true + } + ] + }, + { + name: "Node", + isAbstract: true, + superClass: [ + "DiagramElement" + ] + }, + { + name: "Edge", + isAbstract: true, + superClass: [ + "DiagramElement" + ], + properties: [ + { + name: "source", + type: "DiagramElement", + isReadOnly: true, + isVirtual: true, + isReference: true + }, + { + name: "target", + type: "DiagramElement", + isReadOnly: true, + isVirtual: true, + isReference: true + }, + { + name: "waypoint", + isUnique: false, + isMany: true, + type: "dc:Point", + xml: { + serialize: "xsi:type" + } + } + ] + }, + { + name: "Diagram", + isAbstract: true, + properties: [ + { + name: "id", + isAttr: true, + isId: true, + type: "String" + }, + { + name: "rootElement", + type: "DiagramElement", + isReadOnly: true, + isVirtual: true + }, + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "documentation", + isAttr: true, + type: "String" + }, + { + name: "resolution", + isAttr: true, + type: "Real" + }, + { + name: "ownedStyle", + type: "Style", + isReadOnly: true, + isMany: true, + isVirtual: true + } + ] + }, + { + name: "Shape", + isAbstract: true, + superClass: [ + "Node" + ], + properties: [ + { + name: "bounds", + type: "dc:Bounds" + } + ] + }, + { + name: "Plane", + isAbstract: true, + superClass: [ + "Node" + ], + properties: [ + { + name: "planeElement", + type: "DiagramElement", + subsettedProperty: "DiagramElement-ownedElement", + isMany: true + } + ] + }, + { + name: "LabeledEdge", + isAbstract: true, + superClass: [ + "Edge" + ], + properties: [ + { + name: "ownedLabel", + type: "Label", + isReadOnly: true, + subsettedProperty: "DiagramElement-ownedElement", + isMany: true, + isVirtual: true + } + ] + }, + { + name: "LabeledShape", + isAbstract: true, + superClass: [ + "Shape" + ], + properties: [ + { + name: "ownedLabel", + type: "Label", + isReadOnly: true, + subsettedProperty: "DiagramElement-ownedElement", + isMany: true, + isVirtual: true + } + ] + }, + { + name: "Label", + isAbstract: true, + superClass: [ + "Node" + ], + properties: [ + { + name: "bounds", + type: "dc:Bounds" + } + ] + }, + { + name: "Style", + isAbstract: true, + properties: [ + { + name: "id", + isAttr: true, + isId: true, + type: "String" + } + ] + }, + { + name: "Extension", + properties: [ + { + name: "values", + isMany: true, + type: "Element" + } + ] + } + ]; + var associations$2 = [ + ]; + var xml = { + tagAlias: "lowerCase" + }; + var DiPackage = { + name: name$2, + uri: uri$2, + prefix: prefix$2, + types: types$2, + associations: associations$2, + xml: xml + }; + + var name$1 = "bpmn.io colors for BPMN"; + var uri$1 = "http://bpmn.io/schema/bpmn/biocolor/1.0"; + var prefix$1 = "bioc"; + var types$1 = [ + { + name: "ColoredShape", + "extends": [ + "bpmndi:BPMNShape" + ], + properties: [ + { + name: "stroke", + isAttr: true, + type: "String" + }, + { + name: "fill", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ColoredEdge", + "extends": [ + "bpmndi:BPMNEdge" + ], + properties: [ + { + name: "stroke", + isAttr: true, + type: "String" + }, + { + name: "fill", + isAttr: true, + type: "String" + } + ] + } + ]; + var enumerations$1 = [ + ]; + var associations$1 = [ + ]; + var BiocPackage = { + name: name$1, + uri: uri$1, + prefix: prefix$1, + types: types$1, + enumerations: enumerations$1, + associations: associations$1 + }; + + var name = "BPMN in Color"; + var uri = "http://www.omg.org/spec/BPMN/non-normative/color/1.0"; + var prefix = "color"; + var types = [ + { + name: "ColoredLabel", + "extends": [ + "bpmndi:BPMNLabel" + ], + properties: [ + { + name: "color", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ColoredShape", + "extends": [ + "bpmndi:BPMNShape" + ], + properties: [ + { + name: "background-color", + isAttr: true, + type: "String" + }, + { + name: "border-color", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ColoredEdge", + "extends": [ + "bpmndi:BPMNEdge" + ], + properties: [ + { + name: "border-color", + isAttr: true, + type: "String" + } + ] + } + ]; + var enumerations = [ + ]; + var associations = [ + ]; + var BpmnInColorPackage = { + name: name, + uri: uri, + prefix: prefix, + types: types, + enumerations: enumerations, + associations: associations + }; + + var packages = { + bpmn: BpmnPackage, + bpmndi: BpmnDiPackage, + dc: DcPackage, + di: DiPackage, + bioc: BiocPackage, + color: BpmnInColorPackage + }; + + function simple(additionalPackages, options) { + var pks = assign({}, packages, additionalPackages); + + return new BpmnModdle(pks, options); + } + + // TODO(nikku): remove with future bpmn-js version + + /** + * Wraps APIs to check: + * + * 1) If a callback is passed -> Warn users about callback deprecation. + * 2) If Promise class is implemented in current environment. + * + * @private + */ + function wrapForCompatibility(api) { + + return function() { + + if (!window.Promise) { + throw new Error('Promises is not supported in this environment. Please polyfill Promise.'); + } + + var argLen = arguments.length; + if (argLen >= 1 && isFunction(arguments[argLen - 1])) { + + var callback = arguments[argLen - 1]; + + console.warn(new Error( + 'Passing callbacks to ' + api.name + ' is deprecated and will be removed in a future major release. ' + + 'Please switch to promises: https://bpmn.io/l/moving-to-promises.html' + )); + + var argsWithoutCallback = Array.prototype.slice.call(arguments, 0, -1); + + api.apply(this, argsWithoutCallback).then(function(result) { + + var firstKey = Object.keys(result)[0]; + + // The APIs we are wrapping all resolve a single item depending on the API. + // For instance, importXML resolves { warnings } and saveXML returns { xml }. + // That's why we can call the callback with the first item of result. + return callback(null, result[firstKey]); + + // Passing a second paramter instead of catch because we don't want to + // catch errors thrown by callback(). + }, function(err) { + + return callback(err, err.warnings); + }); + } else { + + return api.apply(this, arguments); + } + }; + } + + + // TODO(nikku): remove with future bpmn-js version + + var DI_ERROR_MESSAGE = 'Tried to access di from the businessObject. The di is available through the diagram element only. For more information, see https://github.com/bpmn-io/bpmn-js/issues/1472'; + + function ensureCompatDiRef(businessObject) { + + // bpmnElement can have multiple independent DIs + if (!has$1(businessObject, 'di')) { + Object.defineProperty(businessObject, 'di', { + enumerable: false, + get: function() { + throw new Error(DI_ERROR_MESSAGE); + } + }); + } + } + + /** + * Returns true if an element has the given meta-model type + * + * @param {ModdleElement} element + * @param {string} type + * + * @return {boolean} + */ + function is(element, type) { + return element.$instanceOf(type); + } + + + /** + * Find a suitable display candidate for definitions where the DI does not + * correctly specify one. + */ + function findDisplayCandidate(definitions) { + return find(definitions.rootElements, function(e) { + return is(e, 'bpmn:Process') || is(e, 'bpmn:Collaboration'); + }); + } + + + function BpmnTreeWalker(handler, translate) { + + // list of containers already walked + var handledElements = {}; + + // list of elements to handle deferred to ensure + // prerequisites are drawn + var deferred = []; + + var diMap = {}; + + // Helpers ////////////////////// + + function contextual(fn, ctx) { + return function(e) { + fn(e, ctx); + }; + } + + function handled(element) { + handledElements[element.id] = element; + } + + function isHandled(element) { + return handledElements[element.id]; + } + + function visit(element, ctx) { + + var gfx = element.gfx; + + // avoid multiple rendering of elements + if (gfx) { + throw new Error( + translate('already rendered {element}', { element: elementToString(element) }) + ); + } + + // call handler + return handler.element(element, diMap[element.id], ctx); + } + + function visitRoot(element, diagram) { + return handler.root(element, diMap[element.id], diagram); + } + + function visitIfDi(element, ctx) { + + try { + var gfx = diMap[element.id] && visit(element, ctx); + + handled(element); + + return gfx; + } catch (e) { + logError(e.message, { element: element, error: e }); + + console.error(translate('failed to import {element}', { element: elementToString(element) })); + console.error(e); + } + } + + function logError(message, context) { + handler.error(message, context); + } + + // DI handling ////////////////////// + + function registerDi(di) { + var bpmnElement = di.bpmnElement; + + if (bpmnElement) { + if (diMap[bpmnElement.id]) { + logError( + translate('multiple DI elements defined for {element}', { + element: elementToString(bpmnElement) + }), + { element: bpmnElement } + ); + } else { + diMap[bpmnElement.id] = di; + + ensureCompatDiRef(bpmnElement); + } + } else { + logError( + translate('no bpmnElement referenced in {element}', { + element: elementToString(di) + }), + { element: di } + ); + } + } + + function handleDiagram(diagram) { + handlePlane(diagram.plane); + } + + function handlePlane(plane) { + registerDi(plane); + + forEach$1(plane.planeElement, handlePlaneElement); + } + + function handlePlaneElement(planeElement) { + registerDi(planeElement); + } + + + // Semantic handling ////////////////////// + + /** + * Handle definitions and return the rendered diagram (if any) + * + * @param {ModdleElement} definitions to walk and import + * @param {ModdleElement} [diagram] specific diagram to import and display + * + * @throws {Error} if no diagram to display could be found + */ + function handleDefinitions(definitions, diagram) { + + // make sure we walk the correct bpmnElement + + var diagrams = definitions.diagrams; + + if (diagram && diagrams.indexOf(diagram) === -1) { + throw new Error(translate('diagram not part of bpmn:Definitions')); + } + + if (!diagram && diagrams && diagrams.length) { + diagram = diagrams[0]; + } + + // no diagram -> nothing to import + if (!diagram) { + throw new Error(translate('no diagram to display')); + } + + // load DI from selected diagram only + diMap = {}; + handleDiagram(diagram); + + + var plane = diagram.plane; + + if (!plane) { + throw new Error(translate( + 'no plane for {element}', + { element: elementToString(diagram) } + )); + } + + var rootElement = plane.bpmnElement; + + // ensure we default to a suitable display candidate (process or collaboration), + // even if non is specified in DI + if (!rootElement) { + rootElement = findDisplayCandidate(definitions); + + if (!rootElement) { + throw new Error(translate('no process or collaboration to display')); + } else { + + logError( + translate('correcting missing bpmnElement on {plane} to {rootElement}', { + plane: elementToString(plane), + rootElement: elementToString(rootElement) + }) + ); + + // correct DI on the fly + plane.bpmnElement = rootElement; + registerDi(plane); + } + } + + + var ctx = visitRoot(rootElement, plane); + + if (is(rootElement, 'bpmn:Process') || is(rootElement, 'bpmn:SubProcess')) { + handleProcess(rootElement, ctx); + } else if (is(rootElement, 'bpmn:Collaboration')) { + handleCollaboration(rootElement, ctx); + + // force drawing of everything not yet drawn that is part of the target DI + handleUnhandledProcesses(definitions.rootElements, ctx); + } else { + throw new Error( + translate('unsupported bpmnElement for {plane}: {rootElement}', { + plane: elementToString(plane), + rootElement: elementToString(rootElement) + }) + ); + } + + // handle all deferred elements + handleDeferred(); + } + + function handleDeferred() { + + var fn; + + // drain deferred until empty + while (deferred.length) { + fn = deferred.shift(); + + fn(); + } + } + + function handleProcess(process, context) { + handleFlowElementsContainer(process, context); + handleIoSpecification(process.ioSpecification, context); + + handleArtifacts(process.artifacts, context); + + // log process handled + handled(process); + } + + function handleUnhandledProcesses(rootElements, ctx) { + + // walk through all processes that have not yet been drawn and draw them + // if they contain lanes with DI information. + // we do this to pass the free-floating lane test cases in the MIWG test suite + var processes = filter(rootElements, function(e) { + return !isHandled(e) && is(e, 'bpmn:Process') && e.laneSets; + }); + + processes.forEach(contextual(handleProcess, ctx)); + } + + function handleMessageFlow(messageFlow, context) { + visitIfDi(messageFlow, context); + } + + function handleMessageFlows(messageFlows, context) { + forEach$1(messageFlows, contextual(handleMessageFlow, context)); + } + + function handleDataAssociation(association, context) { + visitIfDi(association, context); + } + + function handleDataInput(dataInput, context) { + visitIfDi(dataInput, context); + } + + function handleDataOutput(dataOutput, context) { + visitIfDi(dataOutput, context); + } + + function handleArtifact(artifact, context) { + + // bpmn:TextAnnotation + // bpmn:Group + // bpmn:Association + + visitIfDi(artifact, context); + } + + function handleArtifacts(artifacts, context) { + + forEach$1(artifacts, function(e) { + if (is(e, 'bpmn:Association')) { + deferred.push(function() { + handleArtifact(e, context); + }); + } else { + handleArtifact(e, context); + } + }); + } + + function handleIoSpecification(ioSpecification, context) { + + if (!ioSpecification) { + return; + } + + forEach$1(ioSpecification.dataInputs, contextual(handleDataInput, context)); + forEach$1(ioSpecification.dataOutputs, contextual(handleDataOutput, context)); + } + + function handleSubProcess(subProcess, context) { + handleFlowElementsContainer(subProcess, context); + handleArtifacts(subProcess.artifacts, context); + } + + function handleFlowNode(flowNode, context) { + var childCtx = visitIfDi(flowNode, context); + + if (is(flowNode, 'bpmn:SubProcess')) { + handleSubProcess(flowNode, childCtx || context); + } + + if (is(flowNode, 'bpmn:Activity')) { + handleIoSpecification(flowNode.ioSpecification, context); + } + + // defer handling of associations + // affected types: + // + // * bpmn:Activity + // * bpmn:ThrowEvent + // * bpmn:CatchEvent + // + deferred.push(function() { + forEach$1(flowNode.dataInputAssociations, contextual(handleDataAssociation, context)); + forEach$1(flowNode.dataOutputAssociations, contextual(handleDataAssociation, context)); + }); + } + + function handleSequenceFlow(sequenceFlow, context) { + visitIfDi(sequenceFlow, context); + } + + function handleDataElement(dataObject, context) { + visitIfDi(dataObject, context); + } + + function handleLane(lane, context) { + + deferred.push(function() { + + var newContext = visitIfDi(lane, context); + + if (lane.childLaneSet) { + handleLaneSet(lane.childLaneSet, newContext || context); + } + + wireFlowNodeRefs(lane); + }); + } + + function handleLaneSet(laneSet, context) { + forEach$1(laneSet.lanes, contextual(handleLane, context)); + } + + function handleLaneSets(laneSets, context) { + forEach$1(laneSets, contextual(handleLaneSet, context)); + } + + function handleFlowElementsContainer(container, context) { + handleFlowElements(container.flowElements, context); + + if (container.laneSets) { + handleLaneSets(container.laneSets, context); + } + } + + function handleFlowElements(flowElements, context) { + forEach$1(flowElements, function(e) { + if (is(e, 'bpmn:SequenceFlow')) { + deferred.push(function() { + handleSequenceFlow(e, context); + }); + } else if (is(e, 'bpmn:BoundaryEvent')) { + deferred.unshift(function() { + handleFlowNode(e, context); + }); + } else if (is(e, 'bpmn:FlowNode')) { + handleFlowNode(e, context); + } else if (is(e, 'bpmn:DataObject')) ; else if (is(e, 'bpmn:DataStoreReference')) { + handleDataElement(e, context); + } else if (is(e, 'bpmn:DataObjectReference')) { + handleDataElement(e, context); + } else { + logError( + translate('unrecognized flowElement {element} in context {context}', { + element: elementToString(e), + context: (context ? elementToString(context.businessObject) : 'null') + }), + { element: e, context: context } + ); + } + }); + } + + function handleParticipant(participant, context) { + var newCtx = visitIfDi(participant, context); + + var process = participant.processRef; + if (process) { + handleProcess(process, newCtx || context); + } + } + + function handleCollaboration(collaboration, context) { + + forEach$1(collaboration.participants, contextual(handleParticipant, context)); + + handleArtifacts(collaboration.artifacts, context); + + // handle message flows latest in the process + deferred.push(function() { + handleMessageFlows(collaboration.messageFlows, context); + }); + } + + + function wireFlowNodeRefs(lane) { + + // wire the virtual flowNodeRefs <-> relationship + forEach$1(lane.flowNodeRef, function(flowNode) { + var lanes = flowNode.get('lanes'); + + if (lanes) { + lanes.push(lane); + } + }); + } + + // API ////////////////////// + + return { + handleDeferred: handleDeferred, + handleDefinitions: handleDefinitions, + handleSubProcess: handleSubProcess, + registerDi: registerDi + }; + } + + /** + * The importBpmnDiagram result. + * + * @typedef {Object} ImportBPMNDiagramResult + * + * @property {Array} warnings + */ + + /** + * The importBpmnDiagram error. + * + * @typedef {Error} ImportBPMNDiagramError + * + * @property {Array} warnings + */ + + /** + * Import the definitions into a diagram. + * + * Errors and warnings are reported through the specified callback. + * + * @param {djs.Diagram} diagram + * @param {ModdleElement} definitions + * @param {ModdleElement} [bpmnDiagram] the diagram to be rendered + * (if not provided, the first one will be rendered) + * + * Returns {Promise} + */ + function importBpmnDiagram(diagram, definitions, bpmnDiagram) { + + var importer, + eventBus, + translate, + canvas; + + var error, + warnings = []; + + /** + * Walk the diagram semantically, importing (=drawing) + * all elements you encounter. + * + * @param {ModdleElement} definitions + * @param {ModdleElement} bpmnDiagram + */ + function render(definitions, bpmnDiagram) { + + var visitor = { + + root: function(element, di) { + return importer.add(element, di); + }, + + element: function(element, di, parentShape) { + return importer.add(element, di, parentShape); + }, + + error: function(message, context) { + warnings.push({ message: message, context: context }); + } + }; + + var walker = new BpmnTreeWalker(visitor, translate); + + + bpmnDiagram = bpmnDiagram || (definitions.diagrams && definitions.diagrams[0]); + + var diagramsToImport = getDiagramsToImport(definitions, bpmnDiagram); + + if (!diagramsToImport) { + throw new Error(translate('no diagram to display')); + } + + // traverse BPMN 2.0 document model, + // starting at definitions + forEach$1(diagramsToImport, function(diagram) { + walker.handleDefinitions(definitions, diagram); + }); + + var rootId = bpmnDiagram.plane.bpmnElement.id; + + // we do need to account for different ways we create root elements + // each nested imported do have the `_plane` suffix, while + // the root is found under the business object ID + canvas.setRootElement( + canvas.findRoot(rootId + '_plane') || canvas.findRoot(rootId) + ); + } + + return new Promise(function(resolve, reject) { + try { + importer = diagram.get('bpmnImporter'); + eventBus = diagram.get('eventBus'); + translate = diagram.get('translate'); + canvas = diagram.get('canvas'); + + eventBus.fire('import.render.start', { definitions: definitions }); + + render(definitions, bpmnDiagram); + + eventBus.fire('import.render.complete', { + error: error, + warnings: warnings + }); + + return resolve({ warnings: warnings }); + } catch (e) { + + e.warnings = warnings; + return reject(e); + } + }); + } + + /** + * Returns all diagrams in the same hierarchy as the requested diagram. + * Includes all parent and sub process diagrams. + * + * @param {Array} definitions + * @param {Object} bpmnDiagram + * + * @returns {Array} + */ + function getDiagramsToImport(definitions, bpmnDiagram) { + if (!bpmnDiagram) { + return; + } + + var bpmnElement = bpmnDiagram.plane.bpmnElement, + rootElement = bpmnElement; + + if (!is$1(bpmnElement, 'bpmn:Process') && !is$1(bpmnElement, 'bpmn:Collaboration')) { + rootElement = findRootProcess(bpmnElement); + } + + // in case the process is part of a collaboration, the plane references the + // collaboration, not the process + var collaboration; + + if (is$1(rootElement, 'bpmn:Collaboration')) { + collaboration = rootElement; + } else { + collaboration = find(definitions.rootElements, function(element) { + if (!is$1(element, 'bpmn:Collaboration')) { + return; + } + + return find(element.participants, function(participant) { + return participant.processRef === rootElement; + }); + }); + } + + var rootElements = [ rootElement ]; + + // all collaboration processes can contain sub-diagrams + if (collaboration) { + rootElements = map(collaboration.participants, function(participant) { + return participant.processRef; + }); + + rootElements.push(collaboration); + } + + var allChildren = selfAndAllFlowElements(rootElements); + + // if we have multiple diagrams referencing the same element, we + // use the first in the file + var diagramsToImport = [ bpmnDiagram ]; + var handledElements = [ bpmnElement ]; + + forEach$1(definitions.diagrams, function(diagram) { + var businessObject = diagram.plane.bpmnElement; + + if ( + allChildren.indexOf(businessObject) !== -1 && + handledElements.indexOf(businessObject) === -1 + ) { + diagramsToImport.push(diagram); + handledElements.push(businessObject); + } + }); + + + return diagramsToImport; + } + + function selfAndAllFlowElements(elements) { + var result = []; + + forEach$1(elements, function(element) { + if (!element) { + return; + } + + result.push(element); + + result = result.concat(selfAndAllFlowElements(element.flowElements)); + }); + + return result; + } + + function findRootProcess(element) { + var parent = element; + + while (parent) { + if (is$1(parent, 'bpmn:Process')) { + return parent; + } + + parent = parent.$parent; + } + } + + /** + * This file must not be changed or exchanged. + * + * @see http://bpmn.io/license for more information. + */ + + + // inlined ../../resources/logo.svg + var BPMNIO_LOGO_SVG = ''; + + var BPMNIO_IMG = BPMNIO_LOGO_SVG; + + var LOGO_STYLES = { + verticalAlign: 'middle' + }; + + var LINK_STYLES = { + 'color': '#404040' + }; + + var LIGHTBOX_STYLES = { + 'zIndex': '1001', + 'position': 'fixed', + 'top': '0', + 'left': '0', + 'right': '0', + 'bottom': '0' + }; + + var BACKDROP_STYLES = { + 'width': '100%', + 'height': '100%', + 'background': 'rgba(40,40,40,0.2)' + }; + + var NOTICE_STYLES = { + 'position': 'absolute', + 'left': '50%', + 'top': '40%', + 'transform': 'translate(-50%)', + 'width': '260px', + 'padding': '10px', + 'background': 'white', + 'boxShadow': '0 1px 4px rgba(0,0,0,0.3)', + 'fontFamily': 'Helvetica, Arial, sans-serif', + 'fontSize': '14px', + 'display': 'flex', + 'lineHeight': '1.3' + }; + + var LIGHTBOX_MARKUP = + '
        ' + + '
        ' + + '
        ' + + '' + + BPMNIO_IMG + + '' + + '' + + 'Web-based tooling for BPMN, DMN and CMMN diagrams ' + + 'powered by bpmn.io.' + + '' + + '
        ' + + '
        '; + + + var lightbox; + + function createLightbox() { + lightbox = domify(LIGHTBOX_MARKUP); + + assign$1(lightbox, LIGHTBOX_STYLES); + assign$1(query('svg', lightbox), LOGO_STYLES); + assign$1(query('.backdrop', lightbox), BACKDROP_STYLES); + assign$1(query('.notice', lightbox), NOTICE_STYLES); + assign$1(query('.link', lightbox), LINK_STYLES, { + 'margin': '15px 20px 15px 10px', + 'alignSelf': 'center' + }); + } + + function open() { + + if (!lightbox) { + createLightbox(); + + delegate.bind(lightbox, '.backdrop', 'click', function(event) { + document.body.removeChild(lightbox); + }); + } + + document.body.appendChild(lightbox); + } + + /** + * The code in the area + * must not be changed. + * + * @see http://bpmn.io/license for more information. + */ + + /** + * A base viewer for BPMN 2.0 diagrams. + * + * Have a look at {@link Viewer}, {@link NavigatedViewer} or {@link Modeler} for + * bundles that include actual features. + * + * @param {Object} [options] configuration options to pass to the viewer + * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body. + * @param {string|number} [options.width] the width of the viewer + * @param {string|number} [options.height] the height of the viewer + * @param {Object} [options.moddleExtensions] extension packages to provide + * @param {Array} [options.modules] a list of modules to override the default modules + * @param {Array} [options.additionalModules] a list of modules to use with the default modules + */ + function BaseViewer(options) { + + options = assign({}, DEFAULT_OPTIONS, options); + + this._moddle = this._createModdle(options); + + this._container = this._createContainer(options); + + /* */ + + addProjectLogo(this._container); + + /* */ + + this._init(this._container, this._moddle, options); + } + + e(BaseViewer, Diagram); + + /** + * The importXML result. + * + * @typedef {Object} ImportXMLResult + * + * @property {Array} warnings + */ + + /** + * The importXML error. + * + * @typedef {Error} ImportXMLError + * + * @property {Array} warnings + */ + + /** + * Parse and render a BPMN 2.0 diagram. + * + * Once finished the viewer reports back the result to the + * provided callback function with (err, warnings). + * + * ## Life-Cycle Events + * + * During import the viewer will fire life-cycle events: + * + * * import.parse.start (about to read model from xml) + * * import.parse.complete (model read; may have worked or not) + * * import.render.start (graphical import start) + * * import.render.complete (graphical import finished) + * * import.done (everything done) + * + * You can use these events to hook into the life-cycle. + * + * @param {string} xml the BPMN 2.0 xml + * @param {ModdleElement|string} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered) + * + * Returns {Promise} + */ + BaseViewer.prototype.importXML = wrapForCompatibility(function importXML(xml, bpmnDiagram) { + + var self = this; + + function ParseCompleteEvent(data) { + + var event = self.get('eventBus').createEvent(data); + + // TODO(nikku): remove with future bpmn-js version + Object.defineProperty(event, 'context', { + enumerable: true, + get: function() { + + console.warn(new Error( + 'import.parse.complete is deprecated ' + + 'and will be removed in future library versions' + )); + + return { + warnings: data.warnings, + references: data.references, + elementsById: data.elementsById + }; + } + }); + + return event; + } + + return new Promise(function(resolve, reject) { + + // hook in pre-parse listeners + + // allow xml manipulation + xml = self._emit('import.parse.start', { xml: xml }) || xml; + + self._moddle.fromXML(xml, 'bpmn:Definitions').then(function(result) { + var definitions = result.rootElement; + var references = result.references; + var parseWarnings = result.warnings; + var elementsById = result.elementsById; + + // hook in post parse listeners + + // allow definitions manipulation + definitions = self._emit('import.parse.complete', ParseCompleteEvent({ + error: null, + definitions: definitions, + elementsById: elementsById, + references: references, + warnings: parseWarnings + })) || definitions; + + self.importDefinitions(definitions, bpmnDiagram).then(function(result) { + var allWarnings = [].concat(parseWarnings, result.warnings || []); + + self._emit('import.done', { error: null, warnings: allWarnings }); + + return resolve({ warnings: allWarnings }); + }).catch(function(err) { + var allWarnings = [].concat(parseWarnings, err.warnings || []); + + self._emit('import.done', { error: err, warnings: allWarnings }); + + return reject(addWarningsToError(err, allWarnings)); + }); + }).catch(function(err) { + + self._emit('import.parse.complete', { + error: err + }); + + err = checkValidationError(err); + + self._emit('import.done', { error: err, warnings: err.warnings }); + + return reject(err); + }); + }); + }); + + /** + * The importDefinitions result. + * + * @typedef {Object} ImportDefinitionsResult + * + * @property {Array} warnings + */ + + /** + * The importDefinitions error. + * + * @typedef {Error} ImportDefinitionsError + * + * @property {Array} warnings + */ + + /** + * Import parsed definitions and render a BPMN 2.0 diagram. + * + * Once finished the viewer reports back the result to the + * provided callback function with (err, warnings). + * + * ## Life-Cycle Events + * + * During import the viewer will fire life-cycle events: + * + * * import.render.start (graphical import start) + * * import.render.complete (graphical import finished) + * + * You can use these events to hook into the life-cycle. + * + * @param {ModdleElement} definitions parsed BPMN 2.0 definitions + * @param {ModdleElement|string} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered) + * + * Returns {Promise} + */ + BaseViewer.prototype.importDefinitions = wrapForCompatibility(function importDefinitions(definitions, bpmnDiagram) { + + var self = this; + + return new Promise(function(resolve, reject) { + + self._setDefinitions(definitions); + + self.open(bpmnDiagram).then(function(result) { + + var warnings = result.warnings; + + return resolve({ warnings: warnings }); + }).catch(function(err) { + + return reject(err); + }); + }); + }); + + /** + * The open result. + * + * @typedef {Object} OpenResult + * + * @property {Array} warnings + */ + + /** + * The open error. + * + * @typedef {Error} OpenError + * + * @property {Array} warnings + */ + + /** + * Open diagram of previously imported XML. + * + * Once finished the viewer reports back the result to the + * provided callback function with (err, warnings). + * + * ## Life-Cycle Events + * + * During switch the viewer will fire life-cycle events: + * + * * import.render.start (graphical import start) + * * import.render.complete (graphical import finished) + * + * You can use these events to hook into the life-cycle. + * + * @param {string|ModdleElement} [bpmnDiagramOrId] id or the diagram to open + * + * Returns {Promise} + */ + BaseViewer.prototype.open = wrapForCompatibility(function open(bpmnDiagramOrId) { + + var definitions = this._definitions; + var bpmnDiagram = bpmnDiagramOrId; + + var self = this; + + return new Promise(function(resolve, reject) { + if (!definitions) { + var err1 = new Error('no XML imported'); + + return reject(addWarningsToError(err1, [])); + } + + if (typeof bpmnDiagramOrId === 'string') { + bpmnDiagram = findBPMNDiagram(definitions, bpmnDiagramOrId); + + if (!bpmnDiagram) { + var err2 = new Error('BPMNDiagram <' + bpmnDiagramOrId + '> not found'); + + return reject(addWarningsToError(err2, [])); + } + } + + // clear existing rendered diagram + // catch synchronous exceptions during #clear() + try { + self.clear(); + } catch (error) { + + return reject(addWarningsToError(error, [])); + } + + // perform graphical import + importBpmnDiagram(self, definitions, bpmnDiagram).then(function(result) { + + var warnings = result.warnings; + + return resolve({ warnings: warnings }); + }).catch(function(err) { + + return reject(err); + }); + }); + }); + + /** + * The saveXML result. + * + * @typedef {Object} SaveXMLResult + * + * @property {string} xml + */ + + /** + * Export the currently displayed BPMN 2.0 diagram as + * a BPMN 2.0 XML document. + * + * ## Life-Cycle Events + * + * During XML saving the viewer will fire life-cycle events: + * + * * saveXML.start (before serialization) + * * saveXML.serialized (after xml generation) + * * saveXML.done (everything done) + * + * You can use these events to hook into the life-cycle. + * + * @param {Object} [options] export options + * @param {boolean} [options.format=false] output formatted XML + * @param {boolean} [options.preamble=true] output preamble + * + * Returns {Promise} + */ + BaseViewer.prototype.saveXML = wrapForCompatibility(function saveXML(options) { + + options = options || {}; + + var self = this; + + var definitions = this._definitions; + + return new Promise(function(resolve) { + + if (!definitions) { + return resolve({ + error: new Error('no definitions loaded') + }); + } + + // allow to fiddle around with definitions + definitions = self._emit('saveXML.start', { + definitions: definitions + }) || definitions; + + self._moddle.toXML(definitions, options).then(function(result) { + + var xml = result.xml; + + xml = self._emit('saveXML.serialized', { + xml: xml + }) || xml; + + return resolve({ + xml: xml + }); + }); + }).catch(function(error) { + return { error: error }; + }).then(function(result) { + + self._emit('saveXML.done', result); + + var error = result.error; + + if (error) { + return Promise.reject(error); + } + + return result; + }); + }); + + /** + * The saveSVG result. + * + * @typedef {Object} SaveSVGResult + * + * @property {string} svg + */ + + /** + * Export the currently displayed BPMN 2.0 diagram as + * an SVG image. + * + * ## Life-Cycle Events + * + * During SVG saving the viewer will fire life-cycle events: + * + * * saveSVG.start (before serialization) + * * saveSVG.done (everything done) + * + * You can use these events to hook into the life-cycle. + * + * @param {Object} [options] + * + * Returns {Promise} + */ + BaseViewer.prototype.saveSVG = wrapForCompatibility(function saveSVG(options) { + + var self = this; + + return new Promise(function(resolve, reject) { + + self._emit('saveSVG.start'); + + var svg, err; + + try { + var canvas = self.get('canvas'); + + var contentNode = canvas.getActiveLayer(), + defsNode = query('defs', canvas._svg); + + var contents = innerSVG(contentNode), + defs = defsNode ? '' + innerSVG(defsNode) + '' : ''; + + var bbox = contentNode.getBBox(); + + svg = + '\n' + + '\n' + + '\n' + + '' + + defs + contents + + ''; + } catch (e) { + err = e; + } + + self._emit('saveSVG.done', { + error: err, + svg: svg + }); + + if (!err) { + return resolve({ svg: svg }); + } + + return reject(err); + }); + }); + + /** + * Get a named diagram service. + * + * @example + * + * var elementRegistry = viewer.get('elementRegistry'); + * var startEventShape = elementRegistry.get('StartEvent_1'); + * + * @param {string} name + * + * @return {Object} diagram service instance + * + * @method BaseViewer#get + */ + + /** + * Invoke a function in the context of this viewer. + * + * @example + * + * viewer.invoke(function(elementRegistry) { + * var startEventShape = elementRegistry.get('StartEvent_1'); + * }); + * + * @param {Function} fn to be invoked + * + * @return {Object} the functions return value + * + * @method BaseViewer#invoke + */ + + + BaseViewer.prototype._setDefinitions = function(definitions) { + this._definitions = definitions; + }; + + BaseViewer.prototype.getModules = function() { + return this._modules; + }; + + /** + * Remove all drawn elements from the viewer. + * + * After calling this method the viewer can still + * be reused for opening another diagram. + * + * @method BaseViewer#clear + */ + BaseViewer.prototype.clear = function() { + if (!this.getDefinitions()) { + + // no diagram to clear + return; + } + + // remove drawn elements + Diagram.prototype.clear.call(this); + }; + + /** + * Destroy the viewer instance and remove all its + * remainders from the document tree. + */ + BaseViewer.prototype.destroy = function() { + + // diagram destroy + Diagram.prototype.destroy.call(this); + + // dom detach + remove$1(this._container); + }; + + /** + * Register an event listener + * + * Remove a previously added listener via {@link #off(event, callback)}. + * + * @param {string} event + * @param {number} [priority] + * @param {Function} callback + * @param {Object} [that] + */ + BaseViewer.prototype.on = function(event, priority, callback, target) { + return this.get('eventBus').on(event, priority, callback, target); + }; + + /** + * De-register an event listener + * + * @param {string} event + * @param {Function} callback + */ + BaseViewer.prototype.off = function(event, callback) { + this.get('eventBus').off(event, callback); + }; + + BaseViewer.prototype.attachTo = function(parentNode) { + + if (!parentNode) { + throw new Error('parentNode required'); + } + + // ensure we detach from the + // previous, old parent + this.detach(); + + // unwrap jQuery if provided + if (parentNode.get && parentNode.constructor.prototype.jquery) { + parentNode = parentNode.get(0); + } + + if (typeof parentNode === 'string') { + parentNode = query(parentNode); + } + + parentNode.appendChild(this._container); + + this._emit('attach', {}); + + this.get('canvas').resized(); + }; + + BaseViewer.prototype.getDefinitions = function() { + return this._definitions; + }; + + BaseViewer.prototype.detach = function() { + + var container = this._container, + parentNode = container.parentNode; + + if (!parentNode) { + return; + } + + this._emit('detach', {}); + + parentNode.removeChild(container); + }; + + BaseViewer.prototype._init = function(container, moddle, options) { + + var baseModules = options.modules || this.getModules(), + additionalModules = options.additionalModules || [], + staticModules = [ + { + bpmnjs: [ 'value', this ], + moddle: [ 'value', moddle ] + } + ]; + + var diagramModules = [].concat(staticModules, baseModules, additionalModules); + + var diagramOptions = assign(omit(options, [ 'additionalModules' ]), { + canvas: assign({}, options.canvas, { container: container }), + modules: diagramModules + }); + + // invoke diagram constructor + Diagram.call(this, diagramOptions); + + if (options && options.container) { + this.attachTo(options.container); + } + }; + + /** + * Emit an event on the underlying {@link EventBus} + * + * @param {string} type + * @param {Object} event + * + * @return {Object} event processing result (if any) + */ + BaseViewer.prototype._emit = function(type, event) { + return this.get('eventBus').fire(type, event); + }; + + BaseViewer.prototype._createContainer = function(options) { + + var container = domify('
        '); + + assign$1(container, { + width: ensureUnit(options.width), + height: ensureUnit(options.height), + position: options.position + }); + + return container; + }; + + BaseViewer.prototype._createModdle = function(options) { + var moddleOptions = assign({}, this._moddleExtensions, options.moddleExtensions); + + return new simple(moddleOptions); + }; + + BaseViewer.prototype._modules = []; + + // helpers /////////////// + + function addWarningsToError(err, warningsAry) { + err.warnings = warningsAry; + return err; + } + + function checkValidationError(err) { + + // check if we can help the user by indicating wrong BPMN 2.0 xml + // (in case he or the exporting tool did not get that right) + + var pattern = /unparsable content <([^>]+)> detected([\s\S]*)$/; + var match = pattern.exec(err.message); + + if (match) { + err.message = + 'unparsable content <' + match[1] + '> detected; ' + + 'this may indicate an invalid BPMN 2.0 diagram file' + match[2]; + } + + return err; + } + + var DEFAULT_OPTIONS = { + width: '100%', + height: '100%', + position: 'relative' + }; + + + /** + * Ensure the passed argument is a proper unit (defaulting to px) + */ + function ensureUnit(val) { + return val + (isNumber(val) ? 'px' : ''); + } + + + /** + * Find BPMNDiagram in definitions by ID + * + * @param {ModdleElement} definitions + * @param {string} diagramId + * + * @return {ModdleElement|null} + */ + function findBPMNDiagram(definitions, diagramId) { + if (!diagramId) { + return null; + } + + return find(definitions.diagrams, function(element) { + return element.id === diagramId; + }) || null; + } + + /** + * Adds the project logo to the diagram container as + * required by the bpmn.io license. + * + * @see http://bpmn.io/license + * + * @param {Element} container + */ + function addProjectLogo(container) { + var img = BPMNIO_IMG; + + var linkMarkup = + '' + + img + + ''; + + var linkElement = domify(linkMarkup); + + assign$1(query('svg', linkElement), LOGO_STYLES); + assign$1(linkElement, LINK_STYLES, { + position: 'absolute', + bottom: '15px', + right: '15px', + zIndex: '100' + }); + + container.appendChild(linkElement); + + componentEvent.bind(linkElement, 'click', function(event) { + open(); + + event.preventDefault(); + }); + } + + /* */ + + /** + * A viewer for BPMN 2.0 diagrams. + * + * Have a look at {@link NavigatedViewer} or {@link Modeler} for bundles that include + * additional features. + * + * + * ## Extending the Viewer + * + * In order to extend the viewer pass extension modules to bootstrap via the + * `additionalModules` option. An extension module is an object that exposes + * named services. + * + * The following example depicts the integration of a simple + * logging component that integrates with interaction events: + * + * + * ```javascript + * + * // logging component + * function InteractionLogger(eventBus) { + * eventBus.on('element.hover', function(event) { + * console.log() + * }) + * } + * + * InteractionLogger.$inject = [ 'eventBus' ]; // minification save + * + * // extension module + * var extensionModule = { + * __init__: [ 'interactionLogger' ], + * interactionLogger: [ 'type', InteractionLogger ] + * }; + * + * // extend the viewer + * var bpmnViewer = new Viewer({ additionalModules: [ extensionModule ] }); + * bpmnViewer.importXML(...); + * ``` + * + * @param {Object} [options] configuration options to pass to the viewer + * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body. + * @param {string|number} [options.width] the width of the viewer + * @param {string|number} [options.height] the height of the viewer + * @param {Object} [options.moddleExtensions] extension packages to provide + * @param {Array} [options.modules] a list of modules to override the default modules + * @param {Array} [options.additionalModules] a list of modules to use with the default modules + */ + function Viewer(options) { + BaseViewer.call(this, options); + } + + e(Viewer, BaseViewer); + + // modules the viewer is composed of + Viewer.prototype._modules = [ + CoreModule$1, + TranslateModule, + SelectionModule, + OverlaysModule, + DrilldownModdule + ]; + + // default moddle extensions the viewer is composed of + Viewer.prototype._moddleExtensions = {}; + + var KEYCODE_C = 67; + var KEYCODE_V = 86; + var KEYCODE_Y = 89; + var KEYCODE_Z = 90; + + var KEYS_COPY = [ 'c', 'C', KEYCODE_C ]; + var KEYS_PASTE = [ 'v', 'V', KEYCODE_V ]; + var KEYS_REDO = [ 'y', 'Y', KEYCODE_Y ]; + var KEYS_UNDO = [ 'z', 'Z', KEYCODE_Z ]; + + /** + * Returns true if event was triggered with any modifier + * @param {KeyboardEvent} event + */ + function hasModifier(event) { + return (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey); + } + + /** + * @param {KeyboardEvent} event + */ + function isCmd(event) { + + // ensure we don't react to AltGr + // (mapped to CTRL + ALT) + if (event.altKey) { + return false; + } + + return event.ctrlKey || event.metaKey; + } + + /** + * Checks if key pressed is one of provided keys. + * + * @param {string|Array} keys + * @param {KeyboardEvent} event + */ + function isKey(keys, event) { + keys = isArray$2(keys) ? keys : [ keys ]; + + return keys.indexOf(event.key) !== -1 || keys.indexOf(event.keyCode) !== -1; + } + + /** + * @param {KeyboardEvent} event + */ + function isShift(event) { + return event.shiftKey; + } + + function isCopy(event) { + return isCmd(event) && isKey(KEYS_COPY, event); + } + + function isPaste(event) { + return isCmd(event) && isKey(KEYS_PASTE, event); + } + + function isUndo(event) { + return isCmd(event) && !isShift(event) && isKey(KEYS_UNDO, event); + } + + function isRedo(event) { + return isCmd(event) && ( + isKey(KEYS_REDO, event) || ( + isKey(KEYS_UNDO, event) && isShift(event) + ) + ); + } + + var KEYDOWN_EVENT = 'keyboard.keydown', + KEYUP_EVENT = 'keyboard.keyup'; + + var HANDLE_MODIFIER_ATTRIBUTE = 'input-handle-modified-keys'; + + var DEFAULT_PRIORITY = 1000; + + /** + * A keyboard abstraction that may be activated and + * deactivated by users at will, consuming key events + * and triggering diagram actions. + * + * For keys pressed down, keyboard fires `keyboard.keydown` event. + * The event context contains one field which is `KeyboardEvent` event. + * + * The implementation fires the following key events that allow + * other components to hook into key handling: + * + * - keyboard.bind + * - keyboard.unbind + * - keyboard.init + * - keyboard.destroy + * + * All events contain one field which is node. + * + * A default binding for the keyboard may be specified via the + * `keyboard.bindTo` configuration option. + * + * @param {Config} config + * @param {EventBus} eventBus + */ + function Keyboard(config, eventBus) { + var self = this; + + this._config = config || {}; + this._eventBus = eventBus; + + this._keydownHandler = this._keydownHandler.bind(this); + this._keyupHandler = this._keyupHandler.bind(this); + + // properly clean dom registrations + eventBus.on('diagram.destroy', function() { + self._fire('destroy'); + + self.unbind(); + }); + + eventBus.on('diagram.init', function() { + self._fire('init'); + }); + + eventBus.on('attach', function() { + if (config && config.bindTo) { + self.bind(config.bindTo); + } + }); + + eventBus.on('detach', function() { + self.unbind(); + }); + } + + Keyboard.$inject = [ + 'config.keyboard', + 'eventBus' + ]; + + Keyboard.prototype._keydownHandler = function(event) { + this._keyHandler(event, KEYDOWN_EVENT); + }; + + Keyboard.prototype._keyupHandler = function(event) { + this._keyHandler(event, KEYUP_EVENT); + }; + + Keyboard.prototype._keyHandler = function(event, type) { + var eventBusResult; + + if (this._isEventIgnored(event)) { + return; + } + + var context = { + keyEvent: event + }; + + eventBusResult = this._eventBus.fire(type || KEYDOWN_EVENT, context); + + if (eventBusResult) { + event.preventDefault(); + } + }; + + Keyboard.prototype._isEventIgnored = function(event) { + return isInput(event.target) && this._isModifiedKeyIgnored(event); + }; + + Keyboard.prototype._isModifiedKeyIgnored = function(event) { + if (!isCmd(event)) { + return true; + } + + var allowedModifiers = this._getAllowedModifiers(event.target); + return allowedModifiers.indexOf(event.key) === -1; + }; + + Keyboard.prototype._getAllowedModifiers = function(element) { + var modifierContainer = closest(element, '[' + HANDLE_MODIFIER_ATTRIBUTE + ']', true); + + if (!modifierContainer || (this._node && !this._node.contains(modifierContainer))) { + return []; + } + + return modifierContainer.getAttribute(HANDLE_MODIFIER_ATTRIBUTE).split(','); + }; + + Keyboard.prototype.bind = function(node) { + + // make sure that the keyboard is only bound once to the DOM + this.unbind(); + + this._node = node; + + // bind key events + componentEvent.bind(node, 'keydown', this._keydownHandler, true); + componentEvent.bind(node, 'keyup', this._keyupHandler, true); + + this._fire('bind'); + }; + + Keyboard.prototype.getBinding = function() { + return this._node; + }; + + Keyboard.prototype.unbind = function() { + var node = this._node; + + if (node) { + this._fire('unbind'); + + // unbind key events + componentEvent.unbind(node, 'keydown', this._keydownHandler, true); + componentEvent.unbind(node, 'keyup', this._keyupHandler, true); + } + + this._node = null; + }; + + Keyboard.prototype._fire = function(event) { + this._eventBus.fire('keyboard.' + event, { node: this._node }); + }; + + /** + * Add a listener function that is notified with `KeyboardEvent` whenever + * the keyboard is bound and the user presses a key. If no priority is + * provided, the default value of 1000 is used. + * + * @param {number} [priority] + * @param {Function} listener + * @param {string} type + */ + Keyboard.prototype.addListener = function(priority, listener, type) { + if (isFunction(priority)) { + type = listener; + listener = priority; + priority = DEFAULT_PRIORITY; + } + + this._eventBus.on(type || KEYDOWN_EVENT, priority, listener); + }; + + Keyboard.prototype.removeListener = function(listener, type) { + this._eventBus.off(type || KEYDOWN_EVENT, listener); + }; + + Keyboard.prototype.hasModifier = hasModifier; + Keyboard.prototype.isCmd = isCmd; + Keyboard.prototype.isShift = isShift; + Keyboard.prototype.isKey = isKey; + + + + // helpers /////// + + function isInput(target) { + return target && (matchesSelector(target, 'input, textarea') || target.contentEditable === 'true'); + } + + var LOW_PRIORITY = 500; + + + /** + * Adds default keyboard bindings. + * + * This does not pull in any features will bind only actions that + * have previously been registered against the editorActions component. + * + * @param {EventBus} eventBus + * @param {Keyboard} keyboard + */ + function KeyboardBindings(eventBus, keyboard) { + + var self = this; + + eventBus.on('editorActions.init', LOW_PRIORITY, function(event) { + + var editorActions = event.editorActions; + + self.registerBindings(keyboard, editorActions); + }); + } + + KeyboardBindings.$inject = [ + 'eventBus', + 'keyboard' + ]; + + + /** + * Register available keyboard bindings. + * + * @param {Keyboard} keyboard + * @param {EditorActions} editorActions + */ + KeyboardBindings.prototype.registerBindings = function(keyboard, editorActions) { + + /** + * Add keyboard binding if respective editor action + * is registered. + * + * @param {string} action name + * @param {Function} fn that implements the key binding + */ + function addListener(action, fn) { + + if (editorActions.isRegistered(action)) { + keyboard.addListener(fn); + } + } + + + // undo + // (CTRL|CMD) + Z + addListener('undo', function(context) { + + var event = context.keyEvent; + + if (isUndo(event)) { + editorActions.trigger('undo'); + + return true; + } + }); + + // redo + // CTRL + Y + // CMD + SHIFT + Z + addListener('redo', function(context) { + + var event = context.keyEvent; + + if (isRedo(event)) { + editorActions.trigger('redo'); + + return true; + } + }); + + // copy + // CTRL/CMD + C + addListener('copy', function(context) { + + var event = context.keyEvent; + + if (isCopy(event)) { + editorActions.trigger('copy'); + + return true; + } + }); + + // paste + // CTRL/CMD + V + addListener('paste', function(context) { + + var event = context.keyEvent; + + if (isPaste(event)) { + editorActions.trigger('paste'); + + return true; + } + }); + + // zoom in one step + // CTRL/CMD + + + addListener('stepZoom', function(context) { + + var event = context.keyEvent; + + // quirk: it has to be triggered by `=` as well to work on international keyboard layout + // cf: https://github.com/bpmn-io/bpmn-js/issues/1362#issuecomment-722989754 + if (isKey([ '+', 'Add', '=' ], event) && isCmd(event)) { + editorActions.trigger('stepZoom', { value: 1 }); + + return true; + } + }); + + // zoom out one step + // CTRL + - + addListener('stepZoom', function(context) { + + var event = context.keyEvent; + + if (isKey([ '-', 'Subtract' ], event) && isCmd(event)) { + editorActions.trigger('stepZoom', { value: -1 }); + + return true; + } + }); + + // zoom to the default level + // CTRL + 0 + addListener('zoom', function(context) { + + var event = context.keyEvent; + + if (isKey('0', event) && isCmd(event)) { + editorActions.trigger('zoom', { value: 1 }); + + return true; + } + }); + + // delete selected element + // DEL + addListener('removeSelection', function(context) { + + var event = context.keyEvent; + + if (isKey([ 'Backspace', 'Delete', 'Del' ], event)) { + editorActions.trigger('removeSelection'); + + return true; + } + }); + }; + + var KeyboardModule = { + __init__: [ 'keyboard', 'keyboardBindings' ], + keyboard: [ 'type', Keyboard ], + keyboardBindings: [ 'type', KeyboardBindings ] + }; + + var DEFAULT_CONFIG = { + moveSpeed: 50, + moveSpeedAccelerated: 200 + }; + + + /** + * A feature that allows users to move the canvas using the keyboard. + * + * @param {Object} config + * @param {number} [config.moveSpeed=50] + * @param {number} [config.moveSpeedAccelerated=200] + * @param {Keyboard} keyboard + * @param {Canvas} canvas + */ + function KeyboardMove( + config, + keyboard, + canvas + ) { + + var self = this; + + this._config = assign({}, DEFAULT_CONFIG, config || {}); + + keyboard.addListener(arrowsListener); + + + function arrowsListener(context) { + + var event = context.keyEvent, + config = self._config; + + if (!keyboard.isCmd(event)) { + return; + } + + if (keyboard.isKey([ + 'ArrowLeft', 'Left', + 'ArrowUp', 'Up', + 'ArrowDown', 'Down', + 'ArrowRight', 'Right' + ], event)) { + + var speed = ( + keyboard.isShift(event) ? + config.moveSpeedAccelerated : + config.moveSpeed + ); + + var direction; + + switch (event.key) { + case 'ArrowLeft': + case 'Left': + direction = 'left'; + break; + case 'ArrowUp': + case 'Up': + direction = 'up'; + break; + case 'ArrowRight': + case 'Right': + direction = 'right'; + break; + case 'ArrowDown': + case 'Down': + direction = 'down'; + break; + } + + self.moveCanvas({ + speed: speed, + direction: direction + }); + + return true; + } + } + + this.moveCanvas = function(opts) { + + var dx = 0, + dy = 0, + speed = opts.speed; + + var actualSpeed = speed / Math.min(Math.sqrt(canvas.viewbox().scale), 1); + + switch (opts.direction) { + case 'left': // Left + dx = actualSpeed; + break; + case 'up': // Up + dy = actualSpeed; + break; + case 'right': // Right + dx = -actualSpeed; + break; + case 'down': // Down + dy = -actualSpeed; + break; + } + + canvas.scroll({ + dx: dx, + dy: dy + }); + }; + + } + + + KeyboardMove.$inject = [ + 'config.keyboardMove', + 'keyboard', + 'canvas' + ]; + + var KeyboardMoveModule = { + __depends__: [ + KeyboardModule + ], + __init__: [ 'keyboardMove' ], + keyboardMove: [ 'type', KeyboardMove ] + }; + + var CURSOR_CLS_PATTERN = /^djs-cursor-.*$/; + + + function set(mode) { + var classes$1 = classes(document.body); + + classes$1.removeMatching(CURSOR_CLS_PATTERN); + + if (mode) { + classes$1.add('djs-cursor-' + mode); + } + } + + function unset() { + set(null); + } + + var TRAP_PRIORITY = 5000; + + /** + * Installs a click trap that prevents a ghost click following a dragging operation. + * + * @return {Function} a function to immediately remove the installed trap. + */ + function install(eventBus, eventName) { + + eventName = eventName || 'element.click'; + + function trap() { + return false; + } + + eventBus.once(eventName, TRAP_PRIORITY, trap); + + return function() { + eventBus.off(eventName, trap); + }; + } + + function delta(a, b) { + return { + x: a.x - b.x, + y: a.y - b.y + }; + } + + var THRESHOLD = 15; + + + /** + * Move the canvas via mouse. + * + * @param {EventBus} eventBus + * @param {Canvas} canvas + */ + function MoveCanvas(eventBus, canvas) { + + var context; + + + // listen for move on element mouse down; + // allow others to hook into the event before us though + // (dragging / element moving will do this) + eventBus.on('element.mousedown', 500, function(e) { + return handleStart(e.originalEvent); + }); + + + function handleMove(event) { + + var start = context.start, + button = context.button, + position = toPoint(event), + delta$1 = delta(position, start); + + if (!context.dragging && length(delta$1) > THRESHOLD) { + context.dragging = true; + + if (button === 0) { + install(eventBus); + } + + set('grab'); + } + + if (context.dragging) { + + var lastPosition = context.last || context.start; + + delta$1 = delta(position, lastPosition); + + canvas.scroll({ + dx: delta$1.x, + dy: delta$1.y + }); + + context.last = position; + } + + // prevent select + event.preventDefault(); + } + + + function handleEnd(event) { + componentEvent.unbind(document, 'mousemove', handleMove); + componentEvent.unbind(document, 'mouseup', handleEnd); + + context = null; + + unset(); + } + + function handleStart(event) { + + // event is already handled by '.djs-draggable' + if (closest(event.target, '.djs-draggable')) { + return; + } + + var button = event.button; + + // reject right mouse button or modifier key + if (button >= 2 || event.ctrlKey || event.shiftKey || event.altKey) { + return; + } + + context = { + button: button, + start: toPoint(event) + }; + + componentEvent.bind(document, 'mousemove', handleMove); + componentEvent.bind(document, 'mouseup', handleEnd); + + // we've handled the event + return true; + } + + this.isActive = function() { + return !!context; + }; + + } + + + MoveCanvas.$inject = [ + 'eventBus', + 'canvas' + ]; + + + + // helpers /////// + + function length(point) { + return Math.sqrt(Math.pow(point.x, 2) + Math.pow(point.y, 2)); + } + + var MoveCanvasModule = { + __init__: [ 'moveCanvas' ], + moveCanvas: [ 'type', MoveCanvas ] + }; + + /** + * Get the logarithm of x with base 10 + * @param {Integer} value + */ + function log10(x) { + return Math.log(x) / Math.log(10); + } + + /** + * Get step size for given range and number of steps. + * + * @param {Object} range + * @param {number} range.min + * @param {number} range.max + */ + function getStepSize(range, steps) { + + var minLinearRange = log10(range.min), + maxLinearRange = log10(range.max); + + var absoluteLinearRange = Math.abs(minLinearRange) + Math.abs(maxLinearRange); + + return absoluteLinearRange / steps; + } + + function cap(range, scale) { + return Math.max(range.min, Math.min(range.max, scale)); + } + + var sign = Math.sign || function(n) { + return n >= 0 ? 1 : -1; + }; + + var RANGE = { min: 0.2, max: 4 }, + NUM_STEPS = 10; + + var DELTA_THRESHOLD = 0.1; + + var DEFAULT_SCALE = 0.75; + + /** + * An implementation of zooming and scrolling within the + * {@link Canvas} via the mouse wheel. + * + * Mouse wheel zooming / scrolling may be disabled using + * the {@link toggle(enabled)} method. + * + * @param {Object} [config] + * @param {boolean} [config.enabled=true] default enabled state + * @param {number} [config.scale=.75] scroll sensivity + * @param {EventBus} eventBus + * @param {Canvas} canvas + */ + function ZoomScroll(config, eventBus, canvas) { + + config = config || {}; + + this._enabled = false; + + this._canvas = canvas; + this._container = canvas._container; + + this._handleWheel = bind(this._handleWheel, this); + + this._totalDelta = 0; + this._scale = config.scale || DEFAULT_SCALE; + + var self = this; + + eventBus.on('canvas.init', function(e) { + self._init(config.enabled !== false); + }); + } + + ZoomScroll.$inject = [ + 'config.zoomScroll', + 'eventBus', + 'canvas' + ]; + + ZoomScroll.prototype.scroll = function scroll(delta) { + this._canvas.scroll(delta); + }; + + + ZoomScroll.prototype.reset = function reset() { + this._canvas.zoom('fit-viewport'); + }; + + /** + * Zoom depending on delta. + * + * @param {number} delta + * @param {Object} position + */ + ZoomScroll.prototype.zoom = function zoom(delta, position) { + + // zoom with half the step size of stepZoom + var stepSize = getStepSize(RANGE, NUM_STEPS * 2); + + // add until threshold reached + this._totalDelta += delta; + + if (Math.abs(this._totalDelta) > DELTA_THRESHOLD) { + this._zoom(delta, position, stepSize); + + // reset + this._totalDelta = 0; + } + }; + + + ZoomScroll.prototype._handleWheel = function handleWheel(event) { + + // event is already handled by '.djs-scrollable' + if (closest(event.target, '.djs-scrollable', true)) { + return; + } + + var element = this._container; + + event.preventDefault(); + + // pinch to zoom is mapped to wheel + ctrlKey = true + // in modern browsers (!) + + var isZoom = event.ctrlKey; + + var isHorizontalScroll = event.shiftKey; + + var factor = -1 * this._scale, + delta; + + if (isZoom) { + factor *= event.deltaMode === 0 ? 0.020 : 0.32; + } else { + factor *= event.deltaMode === 0 ? 1.0 : 16.0; + } + + if (isZoom) { + var elementRect = element.getBoundingClientRect(); + + var offset = { + x: event.clientX - elementRect.left, + y: event.clientY - elementRect.top + }; + + delta = ( + Math.sqrt( + Math.pow(event.deltaY, 2) + + Math.pow(event.deltaX, 2) + ) * sign(event.deltaY) * factor + ); + + // zoom in relative to diagram {x,y} coordinates + this.zoom(delta, offset); + } else { + + if (isHorizontalScroll) { + delta = { + dx: factor * event.deltaY, + dy: 0 + }; + } else { + delta = { + dx: factor * event.deltaX, + dy: factor * event.deltaY + }; + } + + this.scroll(delta); + } + }; + + /** + * Zoom with fixed step size. + * + * @param {number} delta - Zoom delta (1 for zooming in, -1 for out). + * @param {Object} position + */ + ZoomScroll.prototype.stepZoom = function stepZoom(delta, position) { + + var stepSize = getStepSize(RANGE, NUM_STEPS); + + this._zoom(delta, position, stepSize); + }; + + + /** + * Zoom in/out given a step size. + * + * @param {number} delta + * @param {Object} position + * @param {number} stepSize + */ + ZoomScroll.prototype._zoom = function(delta, position, stepSize) { + var canvas = this._canvas; + + var direction = delta > 0 ? 1 : -1; + + var currentLinearZoomLevel = log10(canvas.zoom()); + + // snap to a proximate zoom step + var newLinearZoomLevel = Math.round(currentLinearZoomLevel / stepSize) * stepSize; + + // increase or decrease one zoom step in the given direction + newLinearZoomLevel += stepSize * direction; + + // calculate the absolute logarithmic zoom level based on the linear zoom level + // (e.g. 2 for an absolute x2 zoom) + var newLogZoomLevel = Math.pow(10, newLinearZoomLevel); + + canvas.zoom(cap(RANGE, newLogZoomLevel), position); + }; + + + /** + * Toggle the zoom scroll ability via mouse wheel. + * + * @param {boolean} [newEnabled] new enabled state + */ + ZoomScroll.prototype.toggle = function toggle(newEnabled) { + + var element = this._container; + var handleWheel = this._handleWheel; + + var oldEnabled = this._enabled; + + if (typeof newEnabled === 'undefined') { + newEnabled = !oldEnabled; + } + + // only react on actual changes + if (oldEnabled !== newEnabled) { + + // add or remove wheel listener based on + // changed enabled state + componentEvent[newEnabled ? 'bind' : 'unbind'](element, 'wheel', handleWheel, false); + } + + this._enabled = newEnabled; + + return newEnabled; + }; + + + ZoomScroll.prototype._init = function(newEnabled) { + this.toggle(newEnabled); + }; + + var ZoomScrollModule = { + __init__: [ 'zoomScroll' ], + zoomScroll: [ 'type', ZoomScroll ] + }; + + /** + * A viewer that includes mouse navigation facilities + * + * @param {Object} options + */ + function NavigatedViewer(options) { + Viewer.call(this, options); + } + + e(NavigatedViewer, Viewer); + + + NavigatedViewer.prototype._navigationModules = [ + KeyboardMoveModule, + MoveCanvasModule, + ZoomScrollModule + ]; + + NavigatedViewer.prototype._modules = [].concat( + Viewer.prototype._modules, + NavigatedViewer.prototype._navigationModules + ); + + return NavigatedViewer; + +})); diff --git a/dist/bpmn-navigated-viewer.production.min.js b/dist/bpmn-navigated-viewer.production.min.js new file mode 100644 index 0000000..6b6c9db --- /dev/null +++ b/dist/bpmn-navigated-viewer.production.min.js @@ -0,0 +1,24 @@ +/*! bpmn-js - bpmn-navigated-viewer v9.4.0 | Copyright (c) 2014-present, camunda Services GmbH | bpmn.io/license */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).BpmnJS=t()}(this,(function(){"use strict";function e(e,t){t&&(e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}))}var t=Object.prototype.toString,n=Object.prototype.hasOwnProperty;function i(e){return void 0!==e}function r(e){return"[object Array]"===t.call(e)}function o(e){return"[object Object]"===t.call(e)}function a(e){return"[object Number]"===t.call(e)}function s(e){var n=t.call(e);return"[object Function]"===n||"[object AsyncFunction]"===n||"[object GeneratorFunction]"===n||"[object AsyncGeneratorFunction]"===n||"[object Proxy]"===n}function l(e){return"[object String]"===t.call(e)}function p(e,t){return n.call(e,t)}function c(e,t){var n;return t=g(t),h(e,(function(e,i){if(t(e,i))return n=e,!1})),n}function u(e,t){var n=[];return h(e,(function(e,i){t(e,i)&&n.push(e)})),n}function h(e,t){var n;if(void 0!==e){var i=r(e)?b:v;for(var o in e)if(p(e,o)&&!1===t(n=e[o],i(o)))return n}}function m(e,t,n){return h(e,(function(e,i){n=t(n,e,i)})),n}function f(e,t){return!!m(e,(function(e,n,i){return e&&t(n,i)}),!0)}function d(e,t){return!!c(e,t)}function y(e){return function(t){return f(e,(function(e,n){return t[n]===e}))}}function g(e){return s(e)?e:function(t){return t===e}}function v(e){return e}function b(e){return Number(e)}function x(e,t){return e.bind(t)}function w(){return w=Object.assign||function(e){for(var t=1;t1?t-1:0),i=1;i"+e+"",t=!0);var n=function(e){var t;return(t=new DOMParser).async=!1,t.parseFromString(e,"text/xml")}(e);if(!t)return n;for(var i=document.createDocumentFragment(),r=n.firstChild;r.firstChild;)i.appendChild(r.firstChild);return i}function H(e,t){var n;return"<"===e.charAt(0)?(n=$(e).firstChild,n=document.importNode(n,!0)):n=document.createElementNS(W,e),t&&N(n,t),n}var K=null;function U(){return null===K&&(K=H("svg")),K}function q(e,t){var n,i,r=Object.keys(t);for(n=0;i=r[n];n++)e[i]=t[i];return e}function Y(e){return e?U().createSVGTransformFromMatrix(e):U().createSVGTransform()}var X=/([&<>]{1})/g,Z=/([\n\r"]{1})/g,J={"&":"&","<":"<",">":">",'"':"'"};function Q(e,t){return e.replace(t,(function(e,t){return J[t]||t}))}function ee(e,t){var n,i,r,o,a;switch(e.nodeType){case 3:t.push(Q(e.textContent,X));break;case 1:if(t.push("<",e.tagName),e.hasAttributes())for(n=0,i=(r=e.attributes).length;n"),n=0,i=(a=e.childNodes).length;n")}else t.push("/>");break;case 8:t.push("\x3c!--",Q(e.nodeValue,X),"--\x3e");break;case 4:t.push("");break;default:throw new Error("unable to handle node "+e.nodeType)}return t}function te(e,t){var n=$(t);if(z(e),t){(function(e){return"#document-fragment"===e.nodeName})(n)||(n=n.documentElement);for(var i,r=(i=n.childNodes,Array.prototype.slice.call(i)),o=0;o1?n-1:0),r=1;r
        a',Ve=!Ie.getElementsByTagName("link").length,Ie=void 0);var ze={legend:[1,"
        ","
        "],tr:[2,"","
        "],col:[2,"","
        "],_default:Ve?[1,"X
        ","
        "]:[0,"",""]};function We(e,t){return(t=t||document).querySelector(e)}function Ge(e){e.parentNode&&e.parentNode.removeChild(e)}function $e(e,t,n,i,r){var o=Y();o.setTranslate(t,n);var a=Y();a.setRotate(i||0,0,0);var s=Y();s.setScale(r||1,r||1),re(e,[o,a,s])}function He(e,t,n){var i=Y();i.setTranslate(t,n),re(e,i)}ze.td=ze.th=[3,"","
        "],ze.option=ze.optgroup=[1,'"],ze.thead=ze.tbody=ze.colgroup=ze.caption=ze.tfoot=[1,"","
        "],ze.polyline=ze.ellipse=ze.polygon=ze.circle=ze.text=ze.line=ze.path=ze.rect=ze.g=[1,'',""];var Ke=function(e,t){return e(t={exports:{}},t.exports),t.exports}((function(e){var t=e.exports=function(e,n){if(n||(n=16),void 0===e&&(e=128),e<=0)return"0";for(var i=Math.log(Math.pow(2,e))/Math.log(n),r=2;i===1/0;r*=2)i=Math.log(Math.pow(2,e/r))/Math.log(n)*r;var o=i-Math.floor(i),a="";for(r=0;r=Math.pow(2,e)?t(e,n):a};t.rack=function(e,n,i){var r=function(r){var a=0;do{if(a++>10){if(!i)throw new Error("too many ID collisions, use more bits");e+=i}var s=t(e,n)}while(Object.hasOwnProperty.call(o,s));return o[s]=r,s},o=r.hats={};return r.get=function(e){return r.hats[e]},r.set=function(e,t){return r.hats[e]=t,r},r.bits=e||128,r.base=n||16,r}}));function Ue(e){if(!(this instanceof Ue))return new Ue(e);e=e||[128,36,1],this._seed=e.length?Ke.rack(e[0],e[1],e[2]):e}Ue.prototype.next=function(e){return this._seed(e||!0)},Ue.prototype.nextPrefixed=function(e,t){var n;do{n=e+this.next(!0)}while(this.assigned(n));return this.claim(n,t),n},Ue.prototype.claim=function(e,t){this._seed.set(e,t||!0)},Ue.prototype.assigned=function(e){return this._seed.get(e)||!1},Ue.prototype.unclaim=function(e){delete this._seed.hats[e]},Ue.prototype.clear=function(){var e,t=this._seed.hats;for(e in t)this.unclaim(e)};var qe=new Ue,Ye=.95;function Xe(e,t,n,i,r,a,s){_.call(this,t,s);var l=e&&e.defaultFillColor,p=e&&e.defaultStrokeColor,c=e&&e.defaultLabelColor,u=qe.next(),m={},f=n.computeStyle;function d(e,t){var n=E({fill:le,strokeWidth:1,strokeLinecap:"round",strokeDasharray:"none"},t.attrs),i=t.ref||{x:0,y:0},o=t.scale||1;"none"===n.strokeDasharray&&(n.strokeDasharray=[1e4,1]);var a=H("marker");N(t.element,n),P(a,t.element),N(a,{id:e,viewBox:"0 0 20 20",refX:i.x,refY:i.y,markerWidth:20*o,markerHeight:20*o,orient:"auto"});var s=We("defs",r._svg);s||(s=H("defs"),P(r._svg,s)),P(s,a),m[e]=a}function y(e){return e.replace(/[^0-9a-zA-z]+/g,"_")}function g(e,t,n){var i=e+"-"+y(t)+"-"+y(n)+"-"+u;return m[i]||function(e,t,n,i){if("sequenceflow-end"===t){var r=H("path");N(r,{d:"M 1 5 L 11 10 L 1 15 Z"}),d(e,{element:r,ref:{x:11,y:10},scale:.5,attrs:{fill:i,stroke:i}})}if("messageflow-start"===t){var o=H("circle");N(o,{cx:6,cy:6,r:3.5}),d(e,{element:o,attrs:{fill:n,stroke:i},ref:{x:6,y:6}})}if("messageflow-end"===t){var a=H("path");N(a,{d:"m 1 5 l 0 -3 l 7 3 l -7 3 z"}),d(e,{element:a,attrs:{fill:n,stroke:i,strokeLinecap:"butt"},ref:{x:8.5,y:5}})}if("association-start"===t){var s=H("path");N(s,{d:"M 11 5 L 1 10 L 11 15"}),d(e,{element:s,attrs:{fill:"none",stroke:i,strokeWidth:1.5},ref:{x:1,y:10},scale:.5})}if("association-end"===t){var l=H("path");N(l,{d:"M 1 5 L 11 10 L 1 15"}),d(e,{element:l,attrs:{fill:"none",stroke:i,strokeWidth:1.5},ref:{x:12,y:10},scale:.5})}if("conditional-flow-marker"===t){var p=H("path");N(p,{d:"M 0 10 L 8 6 L 16 10 L 8 14 Z"}),d(e,{element:p,attrs:{fill:n,stroke:i},ref:{x:-1,y:10},scale:.5})}if("conditional-default-flow-marker"===t){var c=H("path");N(c,{d:"M 6 4 L 10 16"}),d(e,{element:c,attrs:{stroke:i},ref:{x:0,y:10},scale:.5})}}(i,e,t,n),"url(#"+i+")"}function v(e,t,n,i,r){o(i)&&(r=i,i=0),i=i||0,"none"===(r=f(r,{stroke:le,strokeWidth:2,fill:"white"})).fill&&delete r.fillOpacity;var a=t/2,s=n/2,l=H("circle");return N(l,{cx:a,cy:s,r:Math.round((t+n)/4-i)}),N(l,r),P(e,l),l}function b(e,t,n,i,r,a){o(r)&&(a=r,r=0),r=r||0,a=f(a,{stroke:le,strokeWidth:2,fill:"white"});var s=H("rect");return N(s,{x:r,y:r,width:t-2*r,height:n-2*r,rx:i,ry:i}),N(s,a),P(e,s),s}function x(e,t,n){var i=se(t,n=f(n,["no-fill"],{stroke:le,strokeWidth:2,fill:"none"}));return P(e,i),i}function w(e,t,n){n=f(n,["no-fill"],{strokeWidth:2,stroke:le});var i=H("path");return N(i,{d:t}),N(i,n),P(e,i),i}function A(e,t,n,i){return w(t,n,E({"data-marker":e},i))}function k(e){return F[e]}function D(e){return function(t,n){return k(e)(t,n)}}function T(e,t){var n=ce(e),i=function(e){return"bpmn:IntermediateThrowEvent"===e.$type||"bpmn:EndEvent"===e.$type}(n);return n.eventDefinitions&&n.eventDefinitions.length>1?n.parallelMultiple?k("bpmn:ParallelMultipleEventDefinition")(t,e,i):k("bpmn:MultipleEventDefinition")(t,e,i):pe(n,"bpmn:MessageEventDefinition")?k("bpmn:MessageEventDefinition")(t,e,i):pe(n,"bpmn:TimerEventDefinition")?k("bpmn:TimerEventDefinition")(t,e,i):pe(n,"bpmn:ConditionalEventDefinition")?k("bpmn:ConditionalEventDefinition")(t,e):pe(n,"bpmn:SignalEventDefinition")?k("bpmn:SignalEventDefinition")(t,e,i):pe(n,"bpmn:EscalationEventDefinition")?k("bpmn:EscalationEventDefinition")(t,e,i):pe(n,"bpmn:LinkEventDefinition")?k("bpmn:LinkEventDefinition")(t,e,i):pe(n,"bpmn:ErrorEventDefinition")?k("bpmn:ErrorEventDefinition")(t,e,i):pe(n,"bpmn:CancelEventDefinition")?k("bpmn:CancelEventDefinition")(t,e,i):pe(n,"bpmn:CompensateEventDefinition")?k("bpmn:CompensateEventDefinition")(t,e,i):pe(n,"bpmn:TerminateEventDefinition")?k("bpmn:TerminateEventDefinition")(t,e,i):null}function O(e,t,n){n=E({size:{width:100}},n);var i=a.createText(t||"",n);return j(i).add("djs-label"),P(e,i),i}function B(e,t,n){return O(e,ce(t).name,{box:t,align:n,padding:5,style:{fill:me(t,c,p)}})}function L(e,t,n){$e(O(e,t,{box:{height:30,width:n.height},align:"center-middle",style:{fill:me(n,c,p)}}),0,-(-1*n.height),270)}function I(e){for(var t=e.waypoints,n="m "+t[0].x+","+t[0].y,i=1;i1)for(;n=i.shift();){if(!(n.length+oe?t.width:e}),0),g=a.top;"middle"===r.vertical&&(g+=(n.height-d)/2),g-=(l||c[0].height)/4;var v=H("text");return N(v,i),h(c,(function(e){var t;switch(g+=l||e.height,r.horizontal){case"left":t=a.left;break;case"right":t=(s?y:u)-a.right-e.width;break;default:t=Math.max(((s?y:u)-e.width)/2+a.left,0)}var n=H("tspan");N(n,{x:t,y:g}),n.textContent=e.text,P(v,n)})),V(f),{dimensions:{width:y,height:d},element:v}};function it(e){var t=E({fontFamily:"Arial, sans-serif",fontSize:12,fontWeight:"normal",lineHeight:1.2},e&&e.defaultStyle||{}),n=parseInt(t.fontSize,10)-1,i=E({},t,{fontSize:n},e&&e.externalStyle||{}),r=new nt({style:t});this.getExternalLabelBounds=function(e,t){var n=r.getDimensions(t,{box:{width:90,height:30,x:e.width/2+e.x,y:e.height/2+e.y},style:i});return{x:Math.round(e.x+e.width/2-n.width/2),y:Math.round(e.y),width:Math.ceil(n.width),height:Math.ceil(n.height)}},this.getTextAnnotationBounds=function(e,n){var i=r.getDimensions(n,{box:e,style:t,align:"left-top",padding:5});return{x:e.x,y:e.y,width:e.width,height:Math.max(30,Math.round(i.height))}},this.createText=function(e,t){return r.createText(e,t||{})},this.getDefaultStyle=function(){return t},this.getExternalStyle=function(){return i}}it.$inject=["config.textRenderer"];var rt=/\{([^{}]+)\}/g,ot=/(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g;var at={__init__:["bpmnRenderer"],bpmnRenderer:["type",Xe],textRenderer:["type",it],pathMap:["type",function(){this.pathMap={EVENT_MESSAGE:{d:"m {mx},{my} l 0,{e.y1} l {e.x1},0 l 0,-{e.y1} z l {e.x0},{e.y0} l {e.x0},-{e.y0}",height:36,width:36,heightElements:[6,14],widthElements:[10.5,21]},EVENT_SIGNAL:{d:"M {mx},{my} l {e.x0},{e.y0} l -{e.x1},0 Z",height:36,width:36,heightElements:[18],widthElements:[10,20]},EVENT_ESCALATION:{d:"M {mx},{my} l {e.x0},{e.y0} l -{e.x0},-{e.y1} l -{e.x0},{e.y1} Z",height:36,width:36,heightElements:[20,7],widthElements:[8]},EVENT_CONDITIONAL:{d:"M {e.x0},{e.y0} l {e.x1},0 l 0,{e.y2} l -{e.x1},0 Z M {e.x2},{e.y3} l {e.x0},0 M {e.x2},{e.y4} l {e.x0},0 M {e.x2},{e.y5} l {e.x0},0 M {e.x2},{e.y6} l {e.x0},0 M {e.x2},{e.y7} l {e.x0},0 M {e.x2},{e.y8} l {e.x0},0 ",height:36,width:36,heightElements:[8.5,14.5,18,11.5,14.5,17.5,20.5,23.5,26.5],widthElements:[10.5,14.5,12.5]},EVENT_LINK:{d:"m {mx},{my} 0,{e.y0} -{e.x1},0 0,{e.y1} {e.x1},0 0,{e.y0} {e.x0},-{e.y2} -{e.x0},-{e.y2} z",height:36,width:36,heightElements:[4.4375,6.75,7.8125],widthElements:[9.84375,13.5]},EVENT_ERROR:{d:"m {mx},{my} {e.x0},-{e.y0} {e.x1},-{e.y1} {e.x2},{e.y2} {e.x3},-{e.y3} -{e.x4},{e.y4} -{e.x5},-{e.y5} z",height:36,width:36,heightElements:[.023,8.737,8.151,16.564,10.591,8.714],widthElements:[.085,6.672,6.97,4.273,5.337,6.636]},EVENT_CANCEL_45:{d:"m {mx},{my} -{e.x1},0 0,{e.x0} {e.x1},0 0,{e.y1} {e.x0},0 0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z",height:36,width:36,heightElements:[4.75,8.5],widthElements:[4.75,8.5]},EVENT_COMPENSATION:{d:"m {mx},{my} {e.x0},-{e.y0} 0,{e.y1} z m {e.x1},-{e.y2} {e.x2},-{e.y3} 0,{e.y1} -{e.x2},-{e.y3} z",height:36,width:36,heightElements:[6.5,13,.4,6.1],widthElements:[9,9.3,8.7]},EVENT_TIMER_WH:{d:"M {mx},{my} l {e.x0},-{e.y0} m -{e.x0},{e.y0} l {e.x1},{e.y1} ",height:36,width:36,heightElements:[10,2],widthElements:[3,7]},EVENT_TIMER_LINE:{d:"M {mx},{my} m {e.x0},{e.y0} l -{e.x1},{e.y1} ",height:36,width:36,heightElements:[10,3],widthElements:[0,0]},EVENT_MULTIPLE:{d:"m {mx},{my} {e.x1},-{e.y0} {e.x1},{e.y0} -{e.x0},{e.y1} -{e.x2},0 z",height:36,width:36,heightElements:[6.28099,12.56199],widthElements:[3.1405,9.42149,12.56198]},EVENT_PARALLEL_MULTIPLE:{d:"m {mx},{my} {e.x0},0 0,{e.y1} {e.x1},0 0,{e.y0} -{e.x1},0 0,{e.y1} -{e.x0},0 0,-{e.y1} -{e.x1},0 0,-{e.y0} {e.x1},0 z",height:36,width:36,heightElements:[2.56228,7.68683],widthElements:[2.56228,7.68683]},GATEWAY_EXCLUSIVE:{d:"m {mx},{my} {e.x0},{e.y0} {e.x1},{e.y0} {e.x2},0 {e.x4},{e.y2} {e.x4},{e.y1} {e.x2},0 {e.x1},{e.y3} {e.x0},{e.y3} {e.x3},0 {e.x5},{e.y1} {e.x5},{e.y2} {e.x3},0 z",height:17.5,width:17.5,heightElements:[8.5,6.5312,-6.5312,-8.5],widthElements:[6.5,-6.5,3,-3,5,-5]},GATEWAY_PARALLEL:{d:"m {mx},{my} 0,{e.y1} -{e.x1},0 0,{e.y0} {e.x1},0 0,{e.y1} {e.x0},0 0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z",height:30,width:30,heightElements:[5,12.5],widthElements:[5,12.5]},GATEWAY_EVENT_BASED:{d:"m {mx},{my} {e.x0},{e.y0} {e.x0},{e.y1} {e.x1},{e.y2} {e.x2},0 z",height:11,width:11,heightElements:[-6,6,12,-12],widthElements:[9,-3,-12]},GATEWAY_COMPLEX:{d:"m {mx},{my} 0,{e.y0} -{e.x0},-{e.y1} -{e.x1},{e.y2} {e.x0},{e.y1} -{e.x2},0 0,{e.y3} {e.x2},0 -{e.x0},{e.y1} l {e.x1},{e.y2} {e.x0},-{e.y1} 0,{e.y0} {e.x3},0 0,-{e.y0} {e.x0},{e.y1} {e.x1},-{e.y2} -{e.x0},-{e.y1} {e.x2},0 0,-{e.y3} -{e.x2},0 {e.x0},-{e.y1} -{e.x1},-{e.y2} -{e.x0},{e.y1} 0,-{e.y0} -{e.x3},0 z",height:17.125,width:17.125,heightElements:[4.875,3.4375,2.125,3],widthElements:[3.4375,2.125,4.875,3]},DATA_OBJECT_PATH:{d:"m 0,0 {e.x1},0 {e.x0},{e.y0} 0,{e.y1} -{e.x2},0 0,-{e.y2} {e.x1},0 0,{e.y0} {e.x0},0",height:61,width:51,heightElements:[10,50,60],widthElements:[10,40,50,60]},DATA_OBJECT_COLLECTION_PATH:{d:"m{mx},{my} m 3,2 l 0,10 m 3,-10 l 0,10 m 3,-10 l 0,10",height:10,width:10,heightElements:[],widthElements:[]},DATA_ARROW:{d:"m 5,9 9,0 0,-3 5,5 -5,5 0,-3 -9,0 z",height:61,width:51,heightElements:[],widthElements:[]},DATA_STORE:{d:"m {mx},{my} l 0,{e.y2} c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 l 0,-{e.y2} c -{e.x0},-{e.y1} -{e.x1},-{e.y1} -{e.x2},0c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 m -{e.x2},{e.y0}c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0m -{e.x2},{e.y0}c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0",height:61,width:61,heightElements:[7,10,45],widthElements:[2,58,60]},TEXT_ANNOTATION:{d:"m {mx}, {my} m 10,0 l -10,0 l 0,{e.y0} l 10,0",height:30,width:10,heightElements:[30],widthElements:[10]},MARKER_SUB_PROCESS:{d:"m{mx},{my} m 7,2 l 0,10 m -5,-5 l 10,0",height:10,width:10,heightElements:[],widthElements:[]},MARKER_PARALLEL:{d:"m{mx},{my} m 3,2 l 0,10 m 3,-10 l 0,10 m 3,-10 l 0,10",height:10,width:10,heightElements:[],widthElements:[]},MARKER_SEQUENTIAL:{d:"m{mx},{my} m 0,3 l 10,0 m -10,3 l 10,0 m -10,3 l 10,0",height:10,width:10,heightElements:[],widthElements:[]},MARKER_COMPENSATION:{d:"m {mx},{my} 7,-5 0,10 z m 7.1,-0.3 6.9,-4.7 0,10 -6.9,-4.7 z",height:10,width:21,heightElements:[],widthElements:[]},MARKER_LOOP:{d:"m {mx},{my} c 3.526979,0 6.386161,-2.829858 6.386161,-6.320661 0,-3.490806 -2.859182,-6.320661 -6.386161,-6.320661 -3.526978,0 -6.38616,2.829855 -6.38616,6.320661 0,1.745402 0.714797,3.325567 1.870463,4.469381 0.577834,0.571908 1.265885,1.034728 2.029916,1.35457 l -0.718163,-3.909793 m 0.718163,3.909793 -3.885211,0.802902",height:13.9,width:13.7,heightElements:[],widthElements:[]},MARKER_ADHOC:{d:"m {mx},{my} m 0.84461,2.64411 c 1.05533,-1.23780996 2.64337,-2.07882 4.29653,-1.97997996 2.05163,0.0805 3.85579,1.15803 5.76082,1.79107 1.06385,0.34139996 2.24454,0.1438 3.18759,-0.43767 0.61743,-0.33642 1.2775,-0.64078 1.7542,-1.17511 0,0.56023 0,1.12046 0,1.6807 -0.98706,0.96237996 -2.29792,1.62393996 -3.6918,1.66181996 -1.24459,0.0927 -2.46671,-0.2491 -3.59505,-0.74812 -1.35789,-0.55965 -2.75133,-1.33436996 -4.27027,-1.18121996 -1.37741,0.14601 -2.41842,1.13685996 -3.44288,1.96782996 z",height:4,width:15,heightElements:[],widthElements:[]},TASK_TYPE_SEND:{d:"m {mx},{my} l 0,{e.y1} l {e.x1},0 l 0,-{e.y1} z l {e.x0},{e.y0} l {e.x0},-{e.y0}",height:14,width:21,heightElements:[6,14],widthElements:[10.5,21]},TASK_TYPE_SCRIPT:{d:"m {mx},{my} c 9.966553,-6.27276 -8.000926,-7.91932 2.968968,-14.938 l -8.802728,0 c -10.969894,7.01868 6.997585,8.66524 -2.968967,14.938 z m -7,-12 l 5,0 m -4.5,3 l 4.5,0 m -3,3 l 5,0m -4,3 l 5,0",height:15,width:12.6,heightElements:[6,14],widthElements:[10.5,21]},TASK_TYPE_USER_1:{d:"m {mx},{my} c 0.909,-0.845 1.594,-2.049 1.594,-3.385 0,-2.554 -1.805,-4.62199999 -4.357,-4.62199999 -2.55199998,0 -4.28799998,2.06799999 -4.28799998,4.62199999 0,1.348 0.974,2.562 1.89599998,3.405 -0.52899998,0.187 -5.669,2.097 -5.794,4.7560005 v 6.718 h 17 v -6.718 c 0,-2.2980005 -5.5279996,-4.5950005 -6.0509996,-4.7760005 zm -8,6 l 0,5.5 m 11,0 l 0,-5"},TASK_TYPE_USER_2:{d:"m {mx},{my} m 2.162,1.009 c 0,2.4470005 -2.158,4.4310005 -4.821,4.4310005 -2.66499998,0 -4.822,-1.981 -4.822,-4.4310005 "},TASK_TYPE_USER_3:{d:"m {mx},{my} m -6.9,-3.80 c 0,0 2.25099998,-2.358 4.27399998,-1.177 2.024,1.181 4.221,1.537 4.124,0.965 -0.098,-0.57 -0.117,-3.79099999 -4.191,-4.13599999 -3.57499998,0.001 -4.20799998,3.36699999 -4.20699998,4.34799999 z"},TASK_TYPE_MANUAL:{d:"m {mx},{my} c 0.234,-0.01 5.604,0.008 8.029,0.004 0.808,0 1.271,-0.172 1.417,-0.752 0.227,-0.898 -0.334,-1.314 -1.338,-1.316 -2.467,-0.01 -7.886,-0.004 -8.108,-0.004 -0.014,-0.079 0.016,-0.533 0,-0.61 0.195,-0.042 8.507,0.006 9.616,0.002 0.877,-0.007 1.35,-0.438 1.353,-1.208 0.003,-0.768 -0.479,-1.09 -1.35,-1.091 -2.968,-0.002 -9.619,-0.013 -9.619,-0.013 v -0.591 c 0,0 5.052,-0.016 7.225,-0.016 0.888,-0.002 1.354,-0.416 1.351,-1.193 -0.006,-0.761 -0.492,-1.196 -1.361,-1.196 -3.473,-0.005 -10.86,-0.003 -11.0829995,-0.003 -0.022,-0.047 -0.045,-0.094 -0.069,-0.139 0.3939995,-0.319 2.0409995,-1.626 2.4149995,-2.017 0.469,-0.4870005 0.519,-1.1650005 0.162,-1.6040005 -0.414,-0.511 -0.973,-0.5 -1.48,-0.236 -1.4609995,0.764 -6.5999995,3.6430005 -7.7329995,4.2710005 -0.9,0.499 -1.516,1.253 -1.882,2.19 -0.37000002,0.95 -0.17,2.01 -0.166,2.979 0.004,0.718 -0.27300002,1.345 -0.055,2.063 0.629,2.087 2.425,3.312 4.859,3.318 4.6179995,0.014 9.2379995,-0.139 13.8569995,-0.158 0.755,-0.004 1.171,-0.301 1.182,-1.033 0.012,-0.754 -0.423,-0.969 -1.183,-0.973 -1.778,-0.01 -5.824,-0.004 -6.04,-0.004 10e-4,-0.084 0.003,-0.586 10e-4,-0.67 z"},TASK_TYPE_INSTANTIATING_SEND:{d:"m {mx},{my} l 0,8.4 l 12.6,0 l 0,-8.4 z l 6.3,3.6 l 6.3,-3.6"},TASK_TYPE_SERVICE:{d:"m {mx},{my} v -1.71335 c 0.352326,-0.0705 0.703932,-0.17838 1.047628,-0.32133 0.344416,-0.14465 0.665822,-0.32133 0.966377,-0.52145 l 1.19431,1.18005 1.567487,-1.57688 -1.195028,-1.18014 c 0.403376,-0.61394 0.683079,-1.29908 0.825447,-2.01824 l 1.622133,-0.01 v -2.2196 l -1.636514,0.01 c -0.07333,-0.35153 -0.178319,-0.70024 -0.323564,-1.04372 -0.145244,-0.34406 -0.321407,-0.6644 -0.522735,-0.96217 l 1.131035,-1.13631 -1.583305,-1.56293 -1.129598,1.13589 c -0.614052,-0.40108 -1.302883,-0.68093 -2.022633,-0.82247 l 0.0093,-1.61852 h -2.241173 l 0.0042,1.63124 c -0.353763,0.0736 -0.705369,0.17977 -1.049785,0.32371 -0.344415,0.14437 -0.665102,0.32092 -0.9635006,0.52046 l -1.1698628,-1.15823 -1.5667691,1.5792 1.1684265,1.15669 c -0.4026573,0.61283 -0.68308,1.29797 -0.8247287,2.01713 l -1.6588041,0.003 v 2.22174 l 1.6724648,-0.006 c 0.073327,0.35077 0.1797598,0.70243 0.3242851,1.04472 0.1452428,0.34448 0.3214064,0.6644 0.5227339,0.96066 l -1.1993431,1.19723 1.5840256,1.56011 1.1964668,-1.19348 c 0.6140517,0.40346 1.3028827,0.68232 2.0233517,0.82331 l 7.19e-4,1.69892 h 2.226848 z m 0.221462,-3.9957 c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z"},TASK_TYPE_SERVICE_FILL:{d:"m {mx},{my} c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z"},TASK_TYPE_BUSINESS_RULE_HEADER:{d:"m {mx},{my} 0,4 20,0 0,-4 z"},TASK_TYPE_BUSINESS_RULE_MAIN:{d:"m {mx},{my} 0,12 20,0 0,-12 zm 0,8 l 20,0 m -13,-4 l 0,8"},MESSAGE_FLOW_MARKER:{d:"m {mx},{my} m -10.5 ,-7 l 0,14 l 21,0 l 0,-14 z l 10.5,6 l 10.5,-6"}},this.getRawPath=function(e){return this.pathMap[e].d},this.getScaledPath=function(e,t){var n,i,r=this.pathMap[e];t.abspos?(n=t.abspos.x,i=t.abspos.y):(n=t.containerWidth*t.position.mx,i=t.containerHeight*t.position.my);var o={};if(t.position){for(var a=t.containerHeight/r.height*t.yScaleFactor,s=t.containerWidth/r.width*t.xScaleFactor,l=0;l':""}function vt(e,t,n){return E({id:e.id,type:e.$type,businessObject:e,di:t},n)}function bt(e,t,n){var i=e.waypoint;return!i||i.length<2?[yt(t),yt(n)]:i.map((function(e){return{x:e.x,y:e.y}}))}function xt(e,t,n,i){return new Error(e("element {element} referenced by {referenced}#{property} not yet drawn",{element:gt(n),referenced:gt(t),property:i}))}function wt(e,t,n,i,r,o){this._eventBus=e,this._canvas=t,this._elementFactory=n,this._elementRegistry=i,this._translate=r,this._textRenderer=o}wt.$inject=["eventBus","canvas","elementFactory","elementRegistry","translate","textRenderer"],wt.prototype.add=function(e,t,n){var i,r,o,a,s,l,p,c=this._translate;if(A(t,"bpmndi:BPMNPlane")){var u=A(e,"bpmn:SubProcess")?{id:e.id+"_plane"}:{};i=this._elementFactory.createRoot(vt(e,t,u)),this._canvas.addRootElement(i)}else if(A(t,"bpmndi:BPMNShape")){var h=!C(e,t),m=function(e){return A(e,"bpmn:Group")}(e);r=n&&(n.hidden||n.collapsed);var f=t.bounds;i=this._elementFactory.createShape(vt(e,t,{collapsed:h,hidden:r,x:Math.round(f.x),y:Math.round(f.y),width:Math.round(f.width),height:Math.round(f.height),isFrame:m})),A(e,"bpmn:BoundaryEvent")&&this._attachBoundary(e,i),A(e,"bpmn:Lane")&&(o=0),A(e,"bpmn:DataStoreReference")&&(a=n,s=yt(f),l=s.x,p=s.y,l>=a.x&&l<=a.x+a.width&&p>=a.y&&p<=a.y+a.height||(n=this._canvas.findRoot(n))),this._canvas.addShape(i,n,o)}else{if(!A(t,"bpmndi:BPMNEdge"))throw new Error(c("unknown di {di} for element {semantic}",{di:gt(t),semantic:gt(e)}));var d=this._getSource(e),y=this._getTarget(e);r=n&&(n.hidden||n.collapsed),i=this._elementFactory.createConnection(vt(e,t,{hidden:r,source:d,target:y,waypoints:bt(t,d,y)})),A(e,"bpmn:DataAssociation")&&(n=this._canvas.findRoot(n)),this._canvas.addConnection(i,n,o)}return function(e){return A(e,"bpmn:Event")||A(e,"bpmn:Gateway")||A(e,"bpmn:DataStoreReference")||A(e,"bpmn:DataObjectReference")||A(e,"bpmn:DataInput")||A(e,"bpmn:DataOutput")||A(e,"bpmn:SequenceFlow")||A(e,"bpmn:MessageFlow")||A(e,"bpmn:Group")}(e)&&M(i)&&this.addLabel(e,t,i),this._eventBus.fire("bpmnElement.added",{element:i}),i},wt.prototype._attachBoundary=function(e,t){var n=this._translate,i=e.attachedToRef;if(!i)throw new Error(n("missing {semantic}#attachedToRef",{semantic:gt(e)}));var r=this._elementRegistry.get(i.id),o=r&&r.attachers;if(!r)throw xt(n,e,i,"attachedToRef");t.host=r,o||(r.attachers=o=[]),-1===o.indexOf(t)&&o.push(t)},wt.prototype.addLabel=function(e,t,n){var i,r,o;return i=ct(t,n),(r=M(n))&&(i=this._textRenderer.getExternalLabelBounds(i,r)),o=this._elementFactory.createLabel(vt(e,t,{id:e.id+"_label",labelTarget:n,type:"label",hidden:n.hidden||!M(n),x:Math.round(i.x),y:Math.round(i.y),width:Math.round(i.width),height:Math.round(i.height)})),this._canvas.addShape(o,n.parent)},wt.prototype._getEnd=function(e,t){var n,i,r=e.$type,o=this._translate;if(i=e[t+"Ref"],"source"===t&&"bpmn:DataInputAssociation"===r&&(i=i&&i[0]),("source"===t&&"bpmn:DataOutputAssociation"===r||"target"===t&&"bpmn:DataInputAssociation"===r)&&(i=e.$parent),n=i&&this._getElement(i))return n;throw i?xt(o,e,i,t+"Ref"):new Error(o("{semantic}#{side} Ref not specified",{semantic:gt(e),side:t}))},wt.prototype._getSource=function(e){return this._getEnd(e,"source")},wt.prototype._getTarget=function(e){return this._getEnd(e,"target")},wt.prototype._getElement=function(e){return this._elementRegistry.get(e.id)};var Et={__depends__:[at,{__depends__:[st],bpmnImporter:["type",wt]}]};function _t(e){return e.originalEvent||e.srcEvent}function At(e){return e.pointers&&e.pointers.length&&(e=e.pointers[0]),e.touches&&e.touches.length&&(e=e.touches[0]),e?{x:e.clientX,y:e.clientY}:null}function St(e,t){return(_t(e)||e).button===t}function Rt(e){return St(e,0)}function Ct(e){var t=_t(e)||e;return!!Rt(e)&&(/mac/i.test(navigator.platform)?t.metaKey:t.ctrlKey)}function Mt(e){return!0}function kt(e){return Rt(e)||function(e){return St(e,1)}(e)}function Pt(e,t,n){var i=this;function r(n,i,r){var o,a;(function(e,t){return!(l[e]||Rt)(t)})(n,i)||(r?a=t.getGraphics(r):(o=i.delegateTarget||i.target)&&(a=o,r=t.get(a)),a&&r&&!1===e.fire(n,{element:r,gfx:a,originalEvent:i})&&(i.stopPropagation(),i.preventDefault()))}var o={};function a(e){return o[e]}var s={click:"element.click",contextmenu:"element.contextmenu",dblclick:"element.dblclick",mousedown:"element.mousedown",mousemove:"element.mousemove",mouseover:"element.hover",mouseout:"element.out",mouseup:"element.mouseup"},l={"element.contextmenu":Mt,"element.mousedown":kt,"element.mouseup":kt,"element.click":kt,"element.dblclick":kt};function p(e,t,n,i){var a=o[n]=function(e){r(n,e)};i&&(l[n]=i),a.$delegate=je.bind(e,"svg, .djs-element",t,a)}function c(e,t,n){var i=a(n);i&&je.unbind(e,t,i.$delegate)}e.on("canvas.destroy",(function(e){var t;t=e.svg,h(s,(function(e,n){c(t,n,e)}))})),e.on("canvas.init",(function(e){var t;t=e.svg,h(s,(function(e,n){p(t,n,e)}))})),e.on(["shape.added","connection.added"],(function(t){var n=t.element,i=t.gfx;e.fire("interactionEvents.createHit",{element:n,gfx:i})})),e.on(["shape.changed","connection.changed"],500,(function(t){var n=t.element,i=t.gfx;e.fire("interactionEvents.updateHit",{element:n,gfx:i})})),e.on("interactionEvents.createHit",500,(function(e){var t=e.element,n=e.gfx;i.createDefaultHit(t,n)})),e.on("interactionEvents.updateHit",(function(e){var t=e.element,n=e.gfx;i.updateDefaultHit(t,n)}));var u=d("djs-hit djs-hit-stroke"),m=d("djs-hit djs-hit-click-stroke"),f={all:d("djs-hit djs-hit-all"),"click-stroke":m,stroke:u,"no-move":d("djs-hit djs-hit-no-move")};function d(e,t){return t=E({stroke:"white",strokeWidth:15},t||{}),n.cls(e,["no-fill","no-border"],t)}function y(e,t){var n=f[t];if(!n)throw new Error("invalid hit type <"+t+">");return N(e,n),e}function g(e,t){P(e,t)}this.removeHits=function(e){var t;h((t=".djs-hit",(e||document).querySelectorAll(t)),V)},this.createDefaultHit=function(e,t){var n,i=e.waypoints,r=e.isFrame;return i?this.createWaypointsHit(t,i):(n=r?"stroke":"all",this.createBoxHit(t,n,{width:e.width,height:e.height}))},this.createWaypointsHit=function(e,t){var n=se(t);return y(n,"stroke"),g(e,n),n},this.createBoxHit=function(e,t,n){n=E({x:0,y:0},n);var i=H("rect");return y(i,t),N(i,n),g(e,i),i},this.updateDefaultHit=function(e,t){var n=We(".djs-hit",t);if(n)return e.waypoints?function(e,t){N(e,{points:ae(t)})}(n,e.waypoints):N(n,{width:e.width,height:e.height}),n},this.fire=r,this.triggerMouseEvent=function(e,t,n){var i=s[e];if(!i)throw new Error("unmapped DOM event name <"+e+">");return r(i,t,n)},this.mouseHandler=a,this.registerEvent=p,this.unregisterEvent=c}Pt.$inject=["eventBus","elementRegistry","styles"];var Dt={__init__:["interactionEvents"],interactionEvents:["type",Pt]};function Tt(e,t){var n,i,o,a;return t=!!t,r(e)||(e=[e]),h(e,(function(e){var r=e;e.waypoints&&!t&&(r=Tt(e.waypoints,!0));var s=r.x,l=r.y,p=r.height||0,c=r.width||0;(so||void 0===o)&&(o=s+c),(l+p>a||void 0===a)&&(a=l+p)})),{x:n,y:i,height:a-i,width:o-n}}function Nt(e){return"waypoints"in e?"connection":"x"in e?"shape":"root"}function Ot(e){return!(!e||!e.isFrame)}function Bt(e,t,n){this.offset=6;var i=t.cls("djs-outline",["no-fill"]),r=this;function o(e,t){var n=H("rect");return N(n,E({x:10,y:10,rx:3,width:100,height:100},i)),P(e,n),n}e.on(["shape.added","shape.changed"],500,(function(e){var t=e.element,n=e.gfx,i=We(".djs-outline",n);i||(i=o(n)),r.updateShapeOutline(i,t)})),e.on(["connection.added","connection.changed"],(function(e){var t=e.element,n=e.gfx,i=We(".djs-outline",n);i||(i=o(n)),r.updateConnectionOutline(i,t)}))}Bt.prototype.updateShapeOutline=function(e,t){N(e,{x:-this.offset,y:-this.offset,width:t.width+2*this.offset,height:t.height+2*this.offset})},Bt.prototype.updateConnectionOutline=function(e,t){var n=Tt(t);N(e,{x:n.x-this.offset,y:n.y-this.offset,width:n.width+2*this.offset,height:n.height+2*this.offset})},Bt.$inject=["eventBus","styles","elementRegistry"];var Lt={__init__:["outline"],outline:["type",Bt]};function It(e,t){this._eventBus=e,this._canvas=t,this._selectedElements=[];var n=this;e.on(["shape.remove","connection.remove"],(function(e){var t=e.element;n.deselect(t)})),e.on(["diagram.clear","root.set"],(function(e){n.select(null)}))}It.$inject=["eventBus","canvas"],It.prototype.deselect=function(e){var t=this._selectedElements,n=t.indexOf(e);if(-1!==n){var i=t.slice();t.splice(n,1),this._eventBus.fire("selection.changed",{oldSelection:i,newSelection:t})}},It.prototype.get=function(){return this._selectedElements},It.prototype.isSelected=function(e){return-1!==this._selectedElements.indexOf(e)},It.prototype.select=function(e,t){var n=this._selectedElements,i=n.slice();r(e)||(e=e?[e]:[]);var o=this._canvas,a=o.getRootElement();e=e.filter((function(e){var t=o.findRoot(e);return a===t})),t?h(e,(function(e){-1===n.indexOf(e)&&n.push(e)})):this._selectedElements=n=e.slice(),this._eventBus.fire("selection.changed",{oldSelection:i,newSelection:n})};var jt="hover",Ft="selected";function Vt(e,t,n){this._canvas=e;var i=this;function r(t,n){e.addMarker(t,n)}function o(t,n){e.removeMarker(t,n)}this._multiSelectionBox=null,t.on("element.hover",(function(e){r(e.element,jt)})),t.on("element.out",(function(e){o(e.element,jt)})),t.on("selection.changed",(function(e){var t=e.oldSelection,n=e.newSelection;h(t,(function(e){-1===n.indexOf(e)&&o(e,Ft)})),h(n,(function(e){-1===t.indexOf(e)&&r(e,Ft)})),i._updateSelectionOutline(n)})),t.on("element.changed",(function(e){n.isSelected(e.element)&&i._updateSelectionOutline(n.get())}))}function zt(e,t,n,i){e.on("create.end",500,(function(e){var n=e.context,i=n.canExecute,o=n.elements,a=(n.hints||{}).autoSelect;if(i){if(!1===a)return;r(a)?t.select(a):t.select(o.filter(Wt))}})),e.on("connect.end",500,(function(e){var n=e.context.connection;n&&t.select(n)})),e.on("shape.move.end",500,(function(e){var n=e.previousSelection||[],r=i.get(e.context.shape.id);c(n,(function(e){return r.id===e.id}))||t.select(r)})),e.on("element.click",(function(e){if(Rt(e)){var i=e.element;i===n.getRootElement()&&(i=null);var r=t.isSelected(i),o=t.get().length>1,a=Ct(e)||function(e){var t=_t(e)||e;return Rt(e)&&t.shiftKey}(e);if(r&&o)return a?t.deselect(i):t.select(i);r?t.deselect(i):t.select(i,a)}}))}function Wt(e){return!e.hidden}Vt.$inject=["canvas","eventBus","selection"],Vt.prototype._updateSelectionOutline=function(e){var t=this._canvas.getLayer("selectionOutline");z(t);var n=e.length>1;if(j(this._canvas.getContainer())[n?"add":"remove"]("djs-multi-select"),n){var i=function(e){return{x:e.x-6,y:e.y-6,width:e.width+12,height:e.height+12}}(Tt(e)),r=H("rect");N(r,E({rx:3},i)),j(r).add("djs-selection-outline"),P(t,r)}},zt.$inject=["eventBus","selection","canvas","elementRegistry"];var Gt={__init__:["selectionVisuals","selectionBehavior"],__depends__:[Dt,Lt],selection:["type",It],selectionVisuals:["type",Vt],selectionBehavior:["type",zt]};function $t(e){this._counter=0,this._prefix=(e?e+"-":"")+Math.floor(1e9*Math.random())+"-"}$t.prototype.next=function(){return this._prefix+ ++this._counter};var Ht=new $t("ov");function Kt(e,t,n,i){var r,o;this._eventBus=t,this._canvas=n,this._elementRegistry=i,this._ids=Ht,this._overlayDefaults=E({show:null,scale:!0},e&&e.defaults),this._overlays={},this._overlayContainers=[],this._overlayRoot=(r=n.getContainer(),xe(o=Fe('
        '),{position:"absolute",width:0,height:0}),r.insertBefore(o,r.firstChild),o),this._init()}function Ut(e,t,n){xe(e,{left:t+"px",top:n+"px"})}function qt(e,t){e.style.display=!1===t?"none":""}function Yt(e,t){e.style["transform-origin"]="top left",["","-ms-","-webkit-"].forEach((function(n){e.style[n+"transform"]=t}))}Kt.$inject=["config.overlays","eventBus","canvas","elementRegistry"],Kt.prototype.get=function(e){if(l(e)&&(e={id:e}),l(e.element)&&(e.element=this._elementRegistry.get(e.element)),e.element){var t=this._getOverlayContainer(e.element,!0);return t?e.type?u(t.overlays,y({type:e.type})):t.overlays.slice():[]}return e.type?u(this._overlays,y({type:e.type})):e.id?this._overlays[e.id]:null},Kt.prototype.add=function(e,t,n){if(o(t)&&(n=t,t=null),e.id||(e=this._elementRegistry.get(e)),!n.position)throw new Error("must specifiy overlay position");if(!n.html)throw new Error("must specifiy overlay html");if(!e)throw new Error("invalid element specified");var i=this._ids.next();return n=E({},this._overlayDefaults,n,{id:i,type:t,element:e,html:n.html}),this._addOverlay(n),i},Kt.prototype.remove=function(e){var t=this.get(e)||[];r(t)||(t=[t]);var n=this;h(t,(function(e){var t=n._getOverlayContainer(e.element,!0);if(e&&(Ge(e.html),Ge(e.htmlContainer),delete e.htmlContainer,delete e.element,delete n._overlays[e.id]),t){var i=t.overlays.indexOf(e);-1!==i&&t.overlays.splice(i,1)}}))},Kt.prototype.show=function(){qt(this._overlayRoot)},Kt.prototype.hide=function(){qt(this._overlayRoot,!1)},Kt.prototype.clear=function(){this._overlays={},this._overlayContainers=[],Ce(this._overlayRoot)},Kt.prototype._updateOverlayContainer=function(e){var t=e.element,n=e.html,i=t.x,r=t.y;if(t.waypoints){var o=Tt(t);i=o.x,r=o.y}Ut(n,i,r),function(e,t,n){2==arguments.length?e.getAttribute(t):null===n?e.removeAttribute(t):e.setAttribute(t,n)}(e.html,"data-container-id",t.id)},Kt.prototype._updateOverlay=function(e){var t,n,i=e.position,r=e.htmlContainer,o=e.element,a=i.left,s=i.top;void 0!==i.right&&(t=o.waypoints?Tt(o).width:o.width,a=-1*i.right+t);void 0!==i.bottom&&(n=o.waypoints?Tt(o).height:o.height,s=-1*i.bottom+n);Ut(r,a||0,s||0),this._updateOverlayVisibilty(e,this._canvas.viewbox())},Kt.prototype._createOverlayContainer=function(e){var t=Fe('
        ');xe(t,{position:"absolute"}),this._overlayRoot.appendChild(t);var n={html:t,element:e,overlays:[]};return this._updateOverlayContainer(n),this._overlayContainers.push(n),n},Kt.prototype._updateRoot=function(e){var t=e.scale||1,n="matrix("+[t,0,0,t,-1*e.x*t,-1*e.y*t].join(",")+")";Yt(this._overlayRoot,n)},Kt.prototype._getOverlayContainer=function(e,t){var n=c(this._overlayContainers,(function(t){return t.element===e}));return n||t?n:this._createOverlayContainer(e)},Kt.prototype._addOverlay=function(e){var t,n,i=e.id,r=e.element,o=e.html;o.get&&o.constructor.prototype.jquery&&(o=o.get(0)),l(o)&&(o=Fe(o)),n=this._getOverlayContainer(r),xe(t=Fe('
        '),{position:"absolute"}),t.appendChild(o),e.type&&Se(t).add("djs-overlay-"+e.type),qt(t,this._canvas.findRoot(r)===this._canvas.getRootElement()),e.htmlContainer=t,n.overlays.push(e),n.html.appendChild(t),this._overlays[i]=e,this._updateOverlay(e),this._updateOverlayVisibilty(e,this._canvas.viewbox())},Kt.prototype._updateOverlayVisibilty=function(e,t){var n=e.show,r=this._canvas.findRoot(e.element),o=n&&n.minZoom,a=n&&n.maxZoom,s=e.htmlContainer,l=!0;(r!==this._canvas.getRootElement()||n&&(i(o)&&o>t.scale||i(a)&&ar&&(o=(1/t.scale||1)*r)),i(o)&&(l="scale("+o+","+o+")"),Yt(s,l)},Kt.prototype._updateOverlaysVisibilty=function(e){var t=this;h(this._overlays,(function(n){t._updateOverlayVisibilty(n,e)}))},Kt.prototype._init=function(){var e=this._eventBus,t=this;e.on("canvas.viewbox.changing",(function(e){t.hide()})),e.on("canvas.viewbox.changed",(function(e){var n;n=e.viewbox,t._updateRoot(n),t._updateOverlaysVisibilty(n),t.show()})),e.on(["shape.remove","connection.remove"],(function(e){var n=e.element;h(t.get({element:n}),(function(e){t.remove(e.id)}));var i=t._getOverlayContainer(n);if(i){Ge(i.html);var r=t._overlayContainers.indexOf(i);-1!==r&&t._overlayContainers.splice(r,1)}})),e.on("element.changed",500,(function(e){var n=e.element,i=t._getOverlayContainer(n,!0);i&&(h(i.overlays,(function(e){t._updateOverlay(e)})),t._updateOverlayContainer(i))})),e.on("element.marker.update",(function(e){var n=t._getOverlayContainer(e.element,!0);n&&Se(n.html)[e.add?"add":"remove"](e.marker)})),e.on("root.set",(function(){t._updateOverlaysVisibilty(t._canvas.viewbox())})),e.on("diagram.clear",this.clear,this)};var Xt={__init__:["overlays"],overlays:["type",Kt]};function Zt(e,t,n,i){e.on("element.changed",(function(i){var r=i.element;(r.parent||r===t.getRootElement())&&(i.gfx=n.getGraphics(r)),i.gfx&&e.fire(Nt(r)+".changed",i)})),e.on("elements.changed",(function(t){var n=t.elements;n.forEach((function(t){e.fire("element.changed",{element:t})})),i.updateContainments(n)})),e.on("shape.changed",(function(e){i.update("shape",e.element,e.gfx)})),e.on("connection.changed",(function(e){i.update("connection",e.element,e.gfx)}))}Zt.$inject=["eventBus","canvas","elementRegistry","graphicsFactory"];var Jt={__init__:["changeSupport"],changeSupport:["type",Zt]};function Qt(e){this._eventBus=e}Qt.$inject=["eventBus"],Qt.prototype.on=function(e,t,n,i,l,p){if((s(t)||a(t))&&(p=l,l=i,i=n,n=t,t=null),s(n)&&(p=l,l=i,i=n,n=1e3),o(l)&&(p=l,l=!1),!s(i))throw new Error("handlerFn must be a function");r(e)||(e=[e]);var c=this._eventBus;h(e,(function(e){var r=["commandStack",e,t].filter((function(e){return e})).join(".");c.on(r,n,l?function(e,t){return function(n){return e.call(t||null,n.context,n.command,n)}}(i,p):i,p)}))};function en(e,t){t.invoke(Qt,this),this.executed((function(t){var n=t.context;n.rootElement?e.setRootElement(n.rootElement):n.rootElement=e.getRootElement()})),this.revert((function(t){var n=t.context;n.rootElement&&e.setRootElement(n.rootElement)}))}h(["canExecute","preExecute","preExecuted","execute","executed","postExecute","postExecuted","revert","reverted"],(function(e){Qt.prototype[e]=function(t,n,i,r,o){(s(t)||a(t))&&(o=r,r=i,i=n,n=t,t=null),this.on(t,e,n,i,r,o)}})),e(en,Qt),en.$inject=["canvas","injector"];var tn={__init__:["rootElementsBehavior"],rootElementsBehavior:["type",en]}; +/*! https://mths.be/cssescape v1.5.1 by @mathias | MIT license */ +!function(e,t){var n;n=ut,e.exports=function(e){if(e.CSS&&e.CSS.escape)return e.CSS.escape;var t=function(e){if(0==arguments.length)throw new TypeError("`CSS.escape` requires an argument.");for(var t,n=String(e),i=n.length,r=-1,o="",a=n.charCodeAt(0);++r=1&&t<=31||127==t||0==r&&t>=48&&t<=57||1==r&&t>=48&&t<=57&&45==a?"\\"+t.toString(16)+" ":0==r&&1==i&&45==t||!(t>=128||45==t||95==t||t>=48&&t<=57||t>=65&&t<=90||t>=97&&t<=122)?"\\"+n.charAt(r):n.charAt(r):o+="�";return o};return e.CSS||(e.CSS={}),e.CSS.escape=t,t}(n)}({exports:{}});var nn={"&":"&","<":"<",">":">",'"':""","'":"'"};var rn="_plane";function on(e){var t=e.id;return A(e,"bpmn:SubProcess")?function(e){return e+rn}(t):t}function an(e,t,n,i){var r=Fe('
          '),o=i.getContainer(),a=Se(o);o.appendChild(r);var s=[];function l(e){e&&(s=function(e){for(var t=S(e),n=[],i=t;i;i=i.$parent)(A(i,"bpmn:SubProcess")||A(i,"bpmn:Process"))&&n.push(i);return n.reverse()}(e));var n=s.map((function(e){var n,r=(n=""+(n=e.name||e.id))&&n.replace(/[&<>"']/g,(function(e){return nn[e]})),o=Fe('
        • '+r+"
        • "),a=i.findRoot(on(e))||i.findRoot(e.id);if(!a&&A(e,"bpmn:Process")){var s=t.find((function(t){var n=S(t);return n&&n.processRef&&n.processRef===e}));a=i.findRoot(s.id)}return o.addEventListener("click",(function(){i.setRootElement(a)})),o}));r.innerHTML="";var o=n.length>1;a.toggle("bjs-breadcrumbs-shown",o),n.forEach((function(e){r.appendChild(e)}))}e.on("element.changed",(function(e){var t=S(e.element);c(s,(function(e){return e===t}))&&l()})),e.on("root.set",(function(e){l(e.element)}))}function sn(e,t){var n=null,i=new ln;e.on("root.set",(function(e){var r=e.element,o=t.viewbox(),a=i.get(r);if(i.set(n,{x:o.x,y:o.y,zoom:o.scale}),n=r,!A(r,"bpmn:Collaboration")||a){a=a||{x:0,y:0,zoom:1};var s=(o.x-a.x)*o.scale,l=(o.y-a.y)*o.scale;0===s&&0===l||t.scroll({dx:s,dy:l}),a.zoom!==o.scale&&t.zoom(a.zoom,{x:0,y:0})}})),e.on("diagram.clear",(function(){i.clear(),n=null}))}function ln(){this._entries=[],this.set=function(e,t){var n=!1;for(var i in this._entries)if(this._entries[i][0]===e){this._entries[i][1]=t,n=!0;break}n||this._entries.push([e,t])},this.get=function(e){for(var t in this._entries)if(this._entries[t][0]===e)return this._entries[t][1];return null},this.clear=function(){this._entries.length=0},this.remove=function(e){var t=-1;for(var n in this._entries)if(this._entries[n][0]===e){t=n;break}-1!==t&&this._entries.splice(t,1)}}an.$inject=["eventBus","elementRegistry","overlays","canvas"],sn.$inject=["eventBus","canvas"];var pn=180,cn=160;function un(e,t){this._eventBus=e,this._moddle=t;var n=this;e.on("import.render.start",1500,(function(e,t){n.handleImport(t.definitions)}))}function hn(e){return A(e,"bpmndi:BPMNDiagram")?e:hn(e.$parent)}un.prototype.handleImport=function(e){if(e.diagrams){var t=this;this._definitions=e,this._processToDiagramMap={},e.diagrams.forEach((function(e){e.plane&&e.plane.bpmnElement&&(t._processToDiagramMap[e.plane.bpmnElement.id]=e)}));var n=[];e.diagrams.forEach((function(e){var i=t.createNewDiagrams(e.plane);Array.prototype.push.apply(n,i)})),n.forEach((function(e){t.movePlaneElementsToOrigin(e.plane)}))}},un.prototype.createNewDiagrams=function(e){var t=this,n=[],i=[];e.get("planeElement").forEach((function(t){var r=t.bpmnElement;if(r){var o=r.$parent;A(r,"bpmn:SubProcess")&&!t.isExpanded&&n.push(r),function(e,t){var n=e.$parent;if(!A(n,"bpmn:SubProcess")||n===t.bpmnElement)return!1;if(function(e,t){return d(t,(function(t){return A(e,t)}))}(e,["bpmn:DataInputAssociation","bpmn:DataOutputAssociation"]))return!1;return!0}(r,e)&&i.push({diElement:t,parent:o})}}));var r=[];return n.forEach((function(e){if(!t._processToDiagramMap[e.id]){var n=t.createDiagram(e);t._processToDiagramMap[e.id]=n,r.push(n)}})),i.forEach((function(e){for(var i=e.diElement,r=e.parent;r&&-1===n.indexOf(r);)r=r.$parent;if(r){var o=t._processToDiagramMap[r.id];t.moveToDiPlane(i,o.plane)}})),r},un.prototype.movePlaneElementsToOrigin=function(e){var t=e.get("planeElement"),n=function(e){var t={top:1/0,right:-1/0,bottom:-1/0,left:1/0};return e.planeElement.forEach((function(e){if(e.bounds){var n=mt(e.bounds);t.top=Math.min(n.top,t.top),t.left=Math.min(n.left,t.left)}})),function(e){return{x:e.left,y:e.top,width:e.right-e.left,height:e.bottom-e.top}}(t)}(e),i=n.x-pn,r=n.y-cn;t.forEach((function(e){e.waypoint?e.waypoint.forEach((function(e){e.x=e.x-i,e.y=e.y-r})):e.bounds&&(e.bounds.x=e.bounds.x-i,e.bounds.y=e.bounds.y-r)}))},un.prototype.moveToDiPlane=function(e,t){var n=hn(e).plane.get("planeElement");n.splice(n.indexOf(e),1),t.get("planeElement").push(e)},un.prototype.createDiagram=function(e){var t=this._moddle.create("bpmndi:BPMNPlane",{bpmnElement:e}),n=this._moddle.create("bpmndi:BPMNDiagram",{plane:t});return t.$parent=n,t.bpmnElement=e,n.$parent=this._definitions,this._definitions.diagrams.push(n),n},un.$inject=["eventBus","moddle"];var mn=250;function fn(e,t,n,i){Qt.call(this,t),this._canvas=e,this._eventBus=t,this._elementRegistry=n,this._overlays=i;var r=this;this.executed("shape.toggleCollapse",mn,(function(e){var t=e.shape;r.canDrillDown(t)?r.addOverlay(t):r.removeOverlay(t)}),!0),this.reverted("shape.toggleCollapse",mn,(function(e){var t=e.shape;r.canDrillDown(t)?r.addOverlay(t):r.removeOverlay(t)}),!0),this.executed(["shape.create","shape.move","shape.delete"],mn,(function(e){var t=e.oldParent,n=e.newParent||e.parent,i=e.shape;r.canDrillDown(i)&&r.addOverlay(i),r.updateDrilldownOverlay(t),r.updateDrilldownOverlay(n),r.updateDrilldownOverlay(i)}),!0),this.reverted(["shape.create","shape.move","shape.delete"],mn,(function(e){var t=e.oldParent,n=e.newParent||e.parent,i=e.shape;r.canDrillDown(i)&&r.addOverlay(i),r.updateDrilldownOverlay(t),r.updateDrilldownOverlay(n),r.updateDrilldownOverlay(i)}),!0),t.on("import.render.complete",(function(){n.filter((function(e){return r.canDrillDown(e)})).map((function(e){r.addOverlay(e)}))}))}e(fn,Qt),fn.prototype.updateDrilldownOverlay=function(e){var t=this._canvas;if(e){var n=t.findRoot(e);n&&this.updateOverlayVisibility(n)}},fn.prototype.canDrillDown=function(e){var t=this._canvas;return A(e,"bpmn:SubProcess")&&t.findRoot(on(e))},fn.prototype.updateOverlayVisibility=function(e){var t=this._overlays,n=e.businessObject,i=t.get({element:n.id,type:"drilldown"})[0];if(i){var r=n&&n.flowElements&&n.flowElements.length;Se(i.html).toggle("bjs-drilldown-empty",!r)}},fn.prototype.addOverlay=function(e){var t=this._canvas,n=this._overlays;n.get({element:e,type:"drilldown"}).length&&this.removeOverlay(e);var i=Fe('');i.addEventListener("click",(function(){t.setRootElement(t.findRoot(on(e)))})),n.add(e,"drilldown",{position:{bottom:-7,right:-8},html:i}),this.updateOverlayVisibility(e)},fn.prototype.removeOverlay=function(e){this._overlays.remove({element:e,type:"drilldown"})},fn.$inject=["canvas","eventBus","elementRegistry","overlays"];var dn={__depends__:[Xt,Jt,tn],__init__:["drilldownBreadcrumbs","drilldownOverlayBehavior","drilldownCentering","subprocessCompatibility"],drilldownBreadcrumbs:["type",an],drilldownCentering:["type",sn],drilldownOverlayBehavior:["type",fn],subprocessCompatibility:["type",un]},yn=/^class /;function gn(e){return"[object Array]"===Object.prototype.toString.call(e)}function vn(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function bn(){var e=Array.prototype.slice.call(arguments);1===e.length&&gn(e[0])&&(e=e[0]);var t=e.pop();return t.$inject=e,t}var xn=/constructor\s*[^(]*\(\s*([^)]*)\)/m,wn=/^(?:async\s+)?(?:function\s*[^(]*)?(?:\(\s*([^)]*)\)|(\w+))/m,En=/\/\*([^*]*)\*\//m;function _n(e){if("function"!=typeof e)throw new Error('Cannot annotate "'+e+'". Expected a function!');var t=e.toString().match(function(e){return yn.test(e.toString())}(e)?xn:wn);if(!t)return[];var n=t[1]||t[2];return n&&n.split(",").map((function(e){var t=e.match(En);return(t&&t[1]||e).trim()}))||[]}function An(e,t){t=t||{get:function(e,t){if(n.push(e),!1===t)return null;throw a('No provider for "'+e+'"!')}};var n=[],i=this._providers=Object.create(t._providers||null),r=this._instances=Object.create(null),o=r.injector=this,a=function(e){var t=n.join(" -> ");return n.length=0,new Error(t?e+" (Resolving: "+t+")":e)};function s(e,o){if(!i[e]&&-1!==e.indexOf(".")){for(var l=e.split("."),p=s(l.shift());l.length;)p=p[l.shift()];return p}if(vn(r,e))return r[e];if(vn(i,e)){if(-1!==n.indexOf(e))throw n.push(e),a("Cannot resolve circular dependency!");return n.push(e),r[e]=i[e][0](i[e][1]),n.pop(),r[e]}return t.get(e,o)}function l(e,t){if(void 0===t&&(t={}),"function"!=typeof e){if(!gn(e))throw new Error('Cannot invoke "'+e+'". Expected a function!');e=bn(e.slice())}return{fn:e,dependencies:(e.$inject||_n(e)).map((function(e){return vn(t,e)?t[e]:s(e)}))}}function p(e){var t=l(e),n=t.fn,i=t.dependencies;return new(Function.prototype.bind.apply(n,[null].concat(i)))}function c(e,t,n){var i=l(e,n),r=i.fn,o=i.dependencies;return r.apply(t,o)}function u(e){return bn((function(t){return e.get(t)}))}function h(e,t){if(t&&t.length){var n,r,a,s,l=Object.create(null),p=Object.create(null),c=[],h=[],m=[];for(var f in i)n=i[f],-1!==t.indexOf(f)&&("private"===n[2]?-1===(r=c.indexOf(n[3]))?(s=u(a=n[3].createChild([],t)),c.push(n[3]),h.push(a),m.push(s),l[f]=[s,f,"private",a]):l[f]=[m[r],f,"private",h[r]]:l[f]=[n[2],n[1]],p[f]=!0),"factory"!==n[2]&&"type"!==n[2]||!n[1].$scope||t.forEach((function(e){-1!==n[1].$scope.indexOf(e)&&(l[f]=[n[2],n[1]],p[e]=!0)}));t.forEach((function(e){if(!p[e])throw new Error('No provider for "'+e+'". Cannot use provider from the parent!')})),e.unshift(l)}return new An(e,o)}var m,f,d={factory:c,type:p,value:function(e){return e}};function y(e,t){var n=e.__init__||[];return function(){n.forEach((function(e){"string"==typeof e?t.get(e):t.invoke(e)}))}}function g(e){var t=e.__exports__;if(t){var n=e.__modules__,r=Object.keys(e).reduce((function(t,n){return"__exports__"!==n&&"__modules__"!==n&&"__init__"!==n&&"__depends__"!==n&&(t[n]=e[n]),t}),Object.create(null)),a=h((n||[]).concat(r)),s=bn((function(e){return a.get(e)}));t.forEach((function(e){i[e]=[s,e,"private",a]}));var l=(e.__init__||[]).slice();return l.unshift((function(){a.init()})),y(e=Object.assign({},e,{__init__:l}),a)}return Object.keys(e).forEach((function(t){if("__init__"!==t&&"__depends__"!==t)if("private"!==e[t][2]){var n=e[t][0],r=e[t][1];i[t]=[d[n],Sn(n,r),n]}else i[t]=e[t]})),y(e,o)}function v(e,t){return-1!==e.indexOf(t)||-1!==(e=(t.__depends__||[]).reduce(v,e)).indexOf(t)?e:e.concat(t)}this.get=s,this.invoke=c,this.instantiate=p,this.createChild=h,this.init=(m=e.reduce(v,[]).map(g),f=!1,function(){f||(f=!0,m.forEach((function(e){return e()})))})}function Sn(e,t){return"value"!==e&&gn(t)&&(t=bn(t.slice())),t}function Rn(e,t){_.call(this,e,1),this.CONNECTION_STYLE=t.style(["no-fill"],{strokeWidth:5,stroke:"fuchsia"}),this.SHAPE_STYLE=t.style({fill:"white",stroke:"fuchsia",strokeWidth:2}),this.FRAME_STYLE=t.style(["no-fill"],{stroke:"fuchsia",strokeDasharray:4,strokeWidth:2})}e(Rn,_),Rn.prototype.canRender=function(){return!0},Rn.prototype.drawShape=function(e,t,n){var i=H("rect");return N(i,{x:0,y:0,width:t.width||0,height:t.height||0}),Ot(t)?N(i,E({},this.FRAME_STYLE,n||{})):N(i,E({},this.SHAPE_STYLE,n||{})),P(e,i),i},Rn.prototype.drawConnection=function(e,t,n){var i=se(t.waypoints,E({},this.CONNECTION_STYLE,n||{}));return P(e,i),i},Rn.prototype.getShapePath=function(e){var t=e.x,n=e.y,i=e.width;return oe([["M",t,n],["l",i,0],["l",0,e.height],["l",-i,0],["z"]])},Rn.prototype.getConnectionPath=function(e){var t,n,i=e.waypoints,r=[];for(t=0;n=i[t];t++)n=n.original||n,r.push([0===t?"M":"L",n.x,n.y]);return oe(r)},Rn.$inject=["eventBus","styles"];var Cn={__init__:["defaultRenderer"],defaultRenderer:["type",Rn],styles:["type",function(){var e={"no-fill":{fill:"none"},"no-border":{strokeOpacity:0},"no-events":{pointerEvents:"none"}},t=this;this.cls=function(e,t,n){return E(this.style(t,n),{class:e})},this.style=function(t,n){r(t)||n||(n=t,t=[]);var i=m(t,(function(t,n){return E(t,e[n]||{})}),{});return n?E(i,n):i},this.computeStyle=function(e,n,i){return r(n)||(i=n,n=[]),t.style(n||[],E({},i,e||{}))}}]};function Mn(e,t){return Math.round(e*t)/t}function kn(e){return a(e)?e+"px":e}function Pn(e,t,n){var i=H("g");j(i).add(t);var r=void 0!==n?n:e.childNodes.length-1;return e.insertBefore(i,e.childNodes[r]||null),i}var Dn={shape:["x","y","width","height"],connection:["waypoints"]};function Tn(e,t,n,i){this._eventBus=t,this._elementRegistry=i,this._graphicsFactory=n,this._rootsIdx=0,this._layers={},this._planes=[],this._rootElement=null,this._init(e||{})}function Nn(e,t){var n="matrix("+t.a+","+t.b+","+t.c+","+t.d+","+t.e+","+t.f+")";e.setAttribute("transform",n)}Tn.$inject=["config.canvas","eventBus","graphicsFactory","elementRegistry"],Tn.prototype._init=function(e){var t=this._eventBus,n=this._container=function(e){var t=(e=E({},{width:"100%",height:"100%"},e)).container||document.body,n=document.createElement("div");return n.setAttribute("class","djs-container"),xe(n,{position:"relative",overflow:"hidden",width:kn(e.width),height:kn(e.height)}),t.appendChild(n),n}(e),i=this._svg=H("svg");N(i,{width:"100%",height:"100%"}),P(n,i);var r=this._viewport=Pn(i,"viewport");!1!==e.deferUpdate&&(this._viewboxChanged=function(e,t){var n,i,r,o;function a(n){var a=Date.now(),p=n?0:o+t-a;if(p>0)return s(p);e.apply(r,i),l()}function s(e){n=setTimeout(a,e)}function l(){n&&clearTimeout(n),n=o=i=r=void 0}function p(){o=Date.now();for(var e=arguments.length,a=new Array(e),l=0;l already created at index <"+t+">");return n.group},Tn.prototype._getChildIndex=function(e){return m(this._layers,(function(t,n){return n.visible&&e>=n.index&&t++,t}),0)},Tn.prototype._createLayer=function(e,t){void 0===t&&(t=1);var n=this._getChildIndex(t);return{group:Pn(this._viewport,"layer-"+e,n),index:t,visible:!0}},Tn.prototype.showLayer=function(e){if(!e)throw new Error("must specify a name");var t=this._layers[e];if(!t)throw new Error("layer <"+e+"> does not exist");var n=this._viewport,i=t.group,r=t.index;if(t.visible)return i;var o=this._getChildIndex(r);return n.insertBefore(i,n.childNodes[o]||null),t.visible=!0,i},Tn.prototype.hideLayer=function(e){if(!e)throw new Error("must specify a name");var t=this._layers[e];if(!t)throw new Error("layer <"+e+"> does not exist");var n=t.group;return t.visible?(V(n),t.visible=!1,n):n},Tn.prototype._removeLayer=function(e){var t=this._layers[e];t&&(delete this._layers[e],V(t.group))},Tn.prototype.getActiveLayer=function(){var e=this._findPlaneForRoot(this.getRootElement());return e?e.layer:null},Tn.prototype.findRoot=function(e){if("string"==typeof e&&(e=this._elementRegistry.get(e)),e){var t=this._findPlaneForRoot(function(e){for(;e.parent;)e=e.parent;return e}(e))||{};return t.rootElement}},Tn.prototype.getRootElements=function(){return this._planes.map((function(e){return e.rootElement}))},Tn.prototype._findPlaneForRoot=function(e){return c(this._planes,(function(t){return t.rootElement===e}))},Tn.prototype.getContainer=function(){return this._container},Tn.prototype._updateMarker=function(e,t,n){var i;e.id||(e=this._elementRegistry.get(e)),(i=this._elementRegistry._elements[e.id])&&(h([i.gfx,i.secondaryGfx],(function(e){e&&(n?j(e).add(t):j(e).remove(t))})),this._eventBus.fire("element.marker.update",{element:e,gfx:i.gfx,marker:t,add:!!n}))},Tn.prototype.addMarker=function(e,t){this._updateMarker(e,t,!0)},Tn.prototype.removeMarker=function(e,t){this._updateMarker(e,t,!1)},Tn.prototype.hasMarker=function(e,t){return e.id||(e=this._elementRegistry.get(e)),j(this.getGraphics(e)).has(t)},Tn.prototype.toggleMarker=function(e,t){this.hasMarker(e,t)?this.removeMarker(e,t):this.addMarker(e,t)},Tn.prototype.getRootElement=function(){var e=this._rootElement;return e||this._planes.length?e:this.setRootElement(this.addRootElement(null))},Tn.prototype.addRootElement=function(e){var t=this._rootsIdx++;e||(e={id:"__implicitroot_"+t,children:[],isImplicit:!0});var n=e.layer="root-"+t;this._ensureValid("root",e);var i=this.getLayer(n,0);return this.hideLayer(n),this._addRoot(e,i),this._planes.push({rootElement:e,layer:i}),e},Tn.prototype.removeRootElement=function(e){if("string"==typeof e&&(e=this._elementRegistry.get(e)),this._findPlaneForRoot(e))return this._removeRoot(e),this._removeLayer(e.layer),this._planes=this._planes.filter((function(t){return t.rootElement!==e})),this._rootElement===e&&(this._rootElement=null),e},Tn.prototype.setRootElement=function(e,t){if(i(t))throw new Error("override not supported");if(e!==this._rootElement){if(!e)throw new Error("rootElement required");return this._findPlaneForRoot(e)||(e=this.addRootElement(e)),this._setRoot(e),e}},Tn.prototype._removeRoot=function(e){var t=this._elementRegistry,n=this._eventBus;n.fire("root.remove",{element:e}),n.fire("root.removed",{element:e}),t.remove(e)},Tn.prototype._addRoot=function(e,t){var n=this._elementRegistry,i=this._eventBus;i.fire("root.add",{element:e}),n.add(e,t),i.fire("root.added",{element:e,gfx:t})},Tn.prototype._setRoot=function(e,t){var n=this._rootElement;n&&(this._elementRegistry.updateGraphics(n,null,!0),this.hideLayer(n.layer)),e&&(t||(t=this._findPlaneForRoot(e).layer),this._elementRegistry.updateGraphics(e,this._svg,!0),this.showLayer(e.layer)),this._rootElement=e,this._eventBus.fire("root.set",{element:e})},Tn.prototype._ensureValid=function(e,t){if(!t.id)throw new Error("element must have an id");if(this._elementRegistry.get(t.id))throw new Error("element <"+t.id+"> already exists");var n=Dn[e],i=f(n,(function(e){return void 0!==t[e]}));if(!i)throw new Error("must supply { "+n.join(", ")+" } with "+e)},Tn.prototype._setParent=function(e,t,n){!function(e,t,n){if(e&&t){"number"!=typeof n&&(n=-1);var i=e.indexOf(t);if(-1!==i){if(i===n)return;if(-1===n)return;e.splice(i,1)}-1!==n?e.splice(n,0,t):e.push(t)}}(t.children,e,n),e.parent=t},Tn.prototype._addElement=function(e,t,n,i){n=n||this.getRootElement();var r=this._eventBus,o=this._graphicsFactory;this._ensureValid(e,t),r.fire(e+".add",{element:t,parent:n}),this._setParent(t,n,i);var a=o.create(e,t,i);return this._elementRegistry.add(t,a),o.update(e,t,a),r.fire(e+".added",{element:t,gfx:a}),t},Tn.prototype.addShape=function(e,t,n){return this._addElement("shape",e,t,n)},Tn.prototype.addConnection=function(e,t,n){return this._addElement("connection",e,t,n)},Tn.prototype._removeElement=function(e,t){var n=this._elementRegistry,i=this._graphicsFactory,r=this._eventBus;if(e=n.get(e.id||e))return r.fire(t+".remove",{element:e}),i.remove(e),function(e,t){if(!e||!t)return-1;var n=e.indexOf(t);-1!==n&&e.splice(n,1)}(e.parent&&e.parent.children,e),e.parent=null,r.fire(t+".removed",{element:e}),n.remove(e),e},Tn.prototype.removeShape=function(e){return this._removeElement(e,"shape")},Tn.prototype.removeConnection=function(e){return this._removeElement(e,"connection")},Tn.prototype.getGraphics=function(e,t){return this._elementRegistry.getGraphics(e,t)},Tn.prototype._changeViewbox=function(e){this._eventBus.fire("canvas.viewbox.changing"),e.apply(this),this._cachedViewbox=null,this._viewboxChanged()},Tn.prototype._viewboxChanged=function(){this._eventBus.fire("canvas.viewbox.changed",{viewbox:this.viewbox()})},Tn.prototype.viewbox=function(e){if(void 0===e&&this._cachedViewbox)return this._cachedViewbox;var t,n,i,r,o,a,s,l=this._viewport,p=this.getSize();return e?(this._changeViewbox((function(){o=Math.min(p.width/e.width,p.height/e.height);var t=this._svg.createSVGMatrix().scale(o).translate(-e.x,-e.y);re(l,t)})),e):(t=(i=this._rootElement?this.getActiveLayer():null)&&i.getBBox()||{},n=(r=re(l))?r.matrix:function(e,t,n,i,r,o){var a=U().createSVGMatrix();switch(arguments.length){case 0:return a;case 1:return q(a,e);case 6:return q(a,{a:e,b:t,c:n,d:i,e:r,f:o})}}(),o=Mn(n.a,1e3),a=Mn(-n.e||0,1e3),s=Mn(-n.f||0,1e3),e=this._cachedViewbox={x:a?a/o:0,y:s?s/o:0,width:p.width/o,height:p.height/o,scale:o,inner:{width:t.width||0,height:t.height||0,x:t.x||0,y:t.y||0},outer:p})},Tn.prototype.scroll=function(e){var t=this._viewport,n=t.getCTM();return e&&this._changeViewbox((function(){e=E({dx:0,dy:0},e||{}),n=this._svg.createSVGMatrix().translate(e.dx,e.dy).multiply(n),Nn(t,n)})),{x:n.e,y:n.f}},Tn.prototype.scrollToElement=function(e,t){var n=100;"string"==typeof e&&(e=this._elementRegistry.get(e));var i=this.findRoot(e);i!==this.getRootElement()&&this.setRootElement(i),t||(t={}),"number"==typeof t&&(n=t),t={top:t.top||n,right:t.right||n,bottom:t.bottom||n,left:t.left||n};var r,o,a=Tt(e),s=mt(a),l=this.viewbox(),p=this.zoom();l.y+=t.top/p,l.x+=t.left/p,l.width-=(t.right+t.left)/p,l.height-=(t.bottom+t.top)/p;var c=mt(l);if(a.width=0&&o.y>=0&&o.x+o.width<=r.width&&o.y+o.height<=r.height&&!e?n={x:0,y:0,width:Math.max(o.width+o.x,r.width),height:Math.max(o.height+o.y,r.height)}:(t=Math.min(1,r.width/o.width,r.height/o.height),n={x:o.x+(e?o.width/2-r.width/t/2:0),y:o.y+(e?o.height/2-r.height/t/2:0),width:r.width/t,height:r.height/t}),this.viewbox(n),this.viewbox(!1).scale},Tn.prototype._setZoom=function(e,t){var n,i,r,o,a=this._svg,s=this._viewport,l=a.createSVGMatrix(),p=a.createSVGPoint(),c=(i=s.getCTM()).a;return t?(n=E(p,t).matrixTransform(i.inverse()),r=l.translate(n.x,n.y).scale(1/c*e).translate(-n.x,-n.y),o=i.multiply(r)):o=l.scale(e),Nn(this._viewport,o),o},Tn.prototype.getSize=function(){return{width:this._container.clientWidth,height:this._container.clientHeight}},Tn.prototype.getAbsoluteBBox=function(e){var t,n=this.viewbox();e.waypoints?t=this.getGraphics(e).getBBox():t=e;return{x:t.x*n.scale-n.x*n.scale,y:t.y*n.scale-n.y*n.scale,width:t.width*n.scale,height:t.height*n.scale}},Tn.prototype.resized=function(){delete this._cachedViewbox,this._eventBus.fire("canvas.resized")};var On="data-element-id";function Bn(e){this._elements={},this._eventBus=e}Bn.$inject=["eventBus"],Bn.prototype.add=function(e,t,n){var i=e.id;this._validateId(i),N(t,On,i),n&&N(n,On,i),this._elements[i]={element:e,gfx:t,secondaryGfx:n}},Bn.prototype.remove=function(e){var t=this._elements,n=e.id||e,i=n&&t[n];i&&(N(i.gfx,On,""),i.secondaryGfx&&N(i.secondaryGfx,On,""),delete t[n])},Bn.prototype.updateId=function(e,t){this._validateId(t),"string"==typeof e&&(e=this.get(e)),this._eventBus.fire("element.updateId",{element:e,newId:t});var n=this.getGraphics(e),i=this.getGraphics(e,!0);this.remove(e),e.id=t,this.add(e,n,i)},Bn.prototype.updateGraphics=function(e,t,n){var i=e.id||e,r=this._elements[i];return n?r.secondaryGfx=t:r.gfx=t,t&&N(t,On,i),t},Bn.prototype.get=function(e){var t;t="string"==typeof e?e:e&&N(e,On);var n=this._elements[t];return n&&n.element},Bn.prototype.filter=function(e){var t=[];return this.forEach((function(n,i){e(n,i)&&t.push(n)})),t},Bn.prototype.find=function(e){for(var t=this._elements,n=Object.keys(t),i=0;i in ref");t=this.props[t]}t.collection?Fn(this,t,e):function(e,t,n){var i=t.inverse,r=n[t.name];Object.defineProperty(n,t.name,{configurable:t.configurable,enumerable:t.enumerable,get:function(){return r},set:function(t){if(t!==r){var o=r;r=null,o&&e.unset(o,i,n),r=t,e.set(r,i,n)}}})}(this,t,e)},Vn.prototype.ensureRefsCollection=function(e,t){var n=e[t.name];return jn.isExtended(n)||Fn(this,t,e),n},Vn.prototype.ensureBound=function(e,t){(function(e,t){return Object.prototype.hasOwnProperty.call(e,t.name||t)})(e,t)||this.bind(e,t)},Vn.prototype.unset=function(e,t,n){e&&(this.ensureBound(e,t),t.collection?this.ensureRefsCollection(e,t).remove(n):e[t.name]=void 0)},Vn.prototype.set=function(e,t,n){e&&(this.ensureBound(e,t),t.collection?this.ensureRefsCollection(e,t).add(n):e[t.name]=n)};var zn=Vn;!function(e){e.exports=zn,e.exports.Collection=In}(Ln);var Wn=ht(Ln.exports),Gn=new Wn({name:"children",enumerable:!0,collection:!0},{name:"parent"}),$n=new Wn({name:"labels",enumerable:!0,collection:!0},{name:"labelTarget"}),Hn=new Wn({name:"attachers",collection:!0},{name:"host"}),Kn=new Wn({name:"outgoing",collection:!0},{name:"source"}),Un=new Wn({name:"incoming",collection:!0},{name:"target"});function qn(){Object.defineProperty(this,"businessObject",{writable:!0}),Object.defineProperty(this,"label",{get:function(){return this.labels[0]},set:function(e){var t=this.label,n=this.labels;!e&&t?n.remove(t):n.add(e,0)}}),Gn.bind(this,"parent"),$n.bind(this,"labels"),Kn.bind(this,"outgoing"),Un.bind(this,"incoming")}function Yn(){qn.call(this),Gn.bind(this,"children"),Hn.bind(this,"host"),Hn.bind(this,"attachers")}function Xn(){Yn.call(this)}function Zn(){Yn.call(this),$n.bind(this,"labelTarget")}function Jn(){qn.call(this),Kn.bind(this,"source"),Un.bind(this,"target")}e(Yn,qn),e(Xn,Yn),e(Zn,Yn),e(Jn,qn);var Qn={connection:Jn,shape:Yn,label:Zn,root:Xn};function ei(){this._uid=12}ei.prototype.createRoot=function(e){return this.create("root",e)},ei.prototype.createLabel=function(e){return this.create("label",e)},ei.prototype.createShape=function(e){return this.create("shape",e)},ei.prototype.createConnection=function(e){return this.create("connection",e)},ei.prototype.create=function(e,t){return(t=E({},t||{})).id||(t.id=e+"_"+this._uid++),function(e,t){var n=Qn[e];if(!n)throw new Error("unknown type: <"+e+">");return E(new n,t)}(e,t)};var ti=Array.prototype.slice;function ni(){this._listeners={},this.on("diagram.destroy",1,this._destroy,this)}function ii(){}function ri(e,t){this._eventBus=e,this._elementRegistry=t}function oi(e,t,n){var i=n||t.firstChild;e!==i&&t.insertBefore(e,i)}ni.prototype.on=function(e,t,n,i){if(e=r(e)?e:[e],s(t)&&(i=n,n=t,t=1e3),!a(t))throw new Error("priority must be a number");var o=n;i&&((o=x(n,i)).__fn=n.__fn||n);var l=this;e.forEach((function(e){l._addListener(e,{priority:t,callback:o,next:null})}))},ni.prototype.once=function(e,t,n,i){var r=this;if(s(t)&&(i=n,n=t,t=1e3),!a(t))throw new Error("priority must be a number");function o(){o.__isTomb=!0;var t=n.apply(i,arguments);return r.off(e,o),t}o.__fn=n,this.on(e,t,o)},ni.prototype.off=function(e,t){e=r(e)?e:[e];var n=this;e.forEach((function(e){n._removeListener(e,t)}))},ni.prototype.createEvent=function(e){var t=new ii;return t.init(e),t},ni.prototype.fire=function(e,t){var n,i,r,o;if(o=ti.call(arguments),"object"==typeof e&&(e=(t=e).type),!e)throw new Error("no event type specified");if(i=this._listeners[e]){n=t instanceof ii?t:this.createEvent(t),o[0]=n;var a=n.type;e!==a&&(n.type=e);try{r=this._invokeListeners(n,o,i)}finally{e!==a&&(n.type=a)}return void 0===r&&n.defaultPrevented&&(r=!1),r}},ni.prototype.handleError=function(e){return!1===this.fire("error",{error:e})},ni.prototype._destroy=function(){this._listeners={}},ni.prototype._invokeListeners=function(e,t,n){for(var i;n&&!e.cancelBubble;)i=this._invokeListener(e,t,n),n=n.next;return i},ni.prototype._invokeListener=function(e,t,n){var i;if(n.callback.__isTomb)return i;try{i=function(e,t){return e.apply(null,t)}(n.callback,t),void 0!==i&&(e.returnValue=i,e.stopPropagation()),!1===i&&e.preventDefault()}catch(e){if(!this.handleError(e))throw console.error("unhandled error in event listener",e),e}return i},ni.prototype._addListener=function(e,t){var n,i=this._getListeners(e);if(i){for(;i;){if(i.priority or , got "+e);n=r[1],i=r[0]}return{name:e=(i?i+":":"")+n,prefix:i,localName:n}}function gi(e){this.ns=e,this.name=e.name,this.allTypes=[],this.allTypesByName={},this.properties=[],this.propertiesByName={}}function vi(e,t){this.packageMap={},this.typeMap={},this.packages=[],this.properties=t,h(e,x(this.registerPackage,this))}function bi(e,t,n){var i=t[n];if(i in e)throw new Error("package with "+n+" <"+i+"> already defined")}function xi(e){this.model=e}function wi(e,t,n){Object.defineProperty(e,t.name,{enumerable:!t.isReference,writable:!0,value:n,configurable:!0})}function Ei(e){this.properties=new xi(this),this.factory=new ci(this,this.properties),this.registry=new vi(e,this.properties),this.typeCache={}}gi.prototype.build=function(){return e=this,t=["ns","name","allTypes","allTypesByName","properties","propertiesByName","bodyProperty","idProperty"],n={},i=Object(e),h(t,(function(t){t in i&&(n[t]=e[t])})),n;var e,t,n,i},gi.prototype.addProperty=function(e,t,n){"boolean"==typeof t&&(n=t,t=void 0),this.addNamedProperty(e,!1!==n);var i=this.properties;void 0!==t?i.splice(t,0,e):i.push(e)},gi.prototype.replaceProperty=function(e,t,n){var i=e.ns,r=this.properties,o=this.propertiesByName,a=e.name!==t.name;if(e.isId){if(!t.isId)throw new Error("property <"+t.ns.name+"> must be id property to refine <"+e.ns.name+">");this.setIdProperty(t,!1)}if(e.isBody){if(!t.isBody)throw new Error("property <"+t.ns.name+"> must be body property to refine <"+e.ns.name+">");this.setBodyProperty(t,!1)}var s=r.indexOf(e);if(-1===s)throw new Error("property <"+i.name+"> not found in property list");r.splice(s,1),this.addProperty(t,n?void 0:s,a),o[i.name]=o[i.localName]=t},gi.prototype.redefineProperty=function(e,t,n){var i=e.ns.prefix,r=t.split("#"),o=yi(r[0],i),a=yi(r[1],o.prefix).name,s=this.propertiesByName[a];if(!s)throw new Error("refined property <"+a+"> not found");this.replaceProperty(s,e,n),delete e.redefines},gi.prototype.addNamedProperty=function(e,t){var n=e.ns,i=this.propertiesByName;t&&(this.assertNotDefined(e,n.name),this.assertNotDefined(e,n.localName)),i[n.name]=i[n.localName]=e},gi.prototype.removeNamedProperty=function(e){var t=e.ns,n=this.propertiesByName;delete n[t.name],delete n[t.localName]},gi.prototype.setBodyProperty=function(e,t){if(t&&this.bodyProperty)throw new Error("body property defined multiple times (<"+this.bodyProperty.ns.name+">, <"+e.ns.name+">)");this.bodyProperty=e},gi.prototype.setIdProperty=function(e,t){if(t&&this.idProperty)throw new Error("id property defined multiple times (<"+this.idProperty.ns.name+">, <"+e.ns.name+">)");this.idProperty=e},gi.prototype.assertNotDefined=function(e,t){var n=e.name,i=this.propertiesByName[n];if(i)throw new Error("property <"+n+"> already defined; override of <"+i.definedBy.ns.name+"#"+i.ns.name+"> by <"+e.definedBy.ns.name+"#"+e.ns.name+"> not allowed without redefines")},gi.prototype.hasProperty=function(e){return this.propertiesByName[e]},gi.prototype.addTrait=function(e,t){var n=this.allTypesByName,i=this.allTypes,r=e.name;r in n||(h(e.properties,x((function(n){n=E({},n,{name:n.ns.localName,inherited:t}),Object.defineProperty(n,"definedBy",{value:e});var i=n.replaces,r=n.redefines;i||r?this.redefineProperty(n,i||r,i):(n.isBody&&this.setBodyProperty(n),n.isId&&this.setIdProperty(n),this.addProperty(n))}),this)),i.push(e),n[r]=e)},vi.prototype.getPackage=function(e){return this.packageMap[e]},vi.prototype.getPackages=function(){return this.packages},vi.prototype.registerPackage=function(e){e=E({},e);var t=this.packageMap;bi(t,e,"prefix"),bi(t,e,"uri"),h(e.types,x((function(t){this.registerType(t,e)}),this)),t[e.uri]=t[e.prefix]=e,this.packages.push(e)},vi.prototype.registerType=function(e,t){var n=yi((e=E({},e,{superClass:(e.superClass||[]).slice(),extends:(e.extends||[]).slice(),properties:(e.properties||[]).slice(),meta:E(e.meta||{})})).name,t.prefix),i=n.name,r={};h(e.properties,x((function(e){var t=yi(e.name,n.prefix),i=t.name;fi(e.type)||(e.type=yi(e.type,t.prefix).name),E(e,{ns:t,name:i}),r[i]=e}),this)),E(e,{ns:n,name:i,propertiesByName:r}),h(e.extends,x((function(e){var t=this.typeMap[e];t.traits=t.traits||[],t.traits.push(i)}),this)),this.definePackage(e,t),this.typeMap[i]=e},vi.prototype.mapTypes=function(e,t,n){var i=fi(e.name)?{name:e.name}:this.typeMap[e.name],r=this;function o(e){return a(e,!0)}function a(n,i){var o=yi(n,fi(n)?"":e.prefix);r.mapTypes(o,t,i)}if(!i)throw new Error("unknown type <"+e.name+">");h(i.superClass,n?o:a),t(i,!n),h(i.traits,o)},vi.prototype.getEffectiveDescriptor=function(e){var t=yi(e),n=new gi(t);this.mapTypes(t,(function(e,t){n.addTrait(e,t)}));var i=n.build();return this.definePackage(i,i.allTypes[i.allTypes.length-1].$pkg),i},vi.prototype.definePackage=function(e,t){this.properties.define(e,"$pkg",{value:t})},xi.prototype.set=function(e,t,n){var i=this.model.getPropertyDescriptor(e,t),r=i&&i.name;void 0===n?i?delete e[r]:delete e.$attrs[t]:i?r in e?e[r]=n:wi(e,i,n):e.$attrs[t]=n},xi.prototype.get=function(e,t){var n=this.model.getPropertyDescriptor(e,t);if(!n)return e.$attrs[t];var i=n.name;return!e[i]&&n.isMany&&wi(e,n,[]),e[i]},xi.prototype.define=function(e,t,n){if(!n.writable){var i=n.value;delete(n=E({},n,{get:function(){return i}})).value}Object.defineProperty(e,t,n)},xi.prototype.defineDescriptor=function(e,t){this.define(e,"$descriptor",{value:t})},xi.prototype.defineModel=function(e,t){this.define(e,"$model",{value:t})},Ei.prototype.create=function(e,t){var n=this.getType(e);if(!n)throw new Error("unknown type <"+e+">");return new n(t)},Ei.prototype.getType=function(e){var t=this.typeCache,n=l(e)?e:e.ns.name,i=t[n];return i||(e=this.registry.getEffectiveDescriptor(n),i=t[n]=this.factory.createType(e)),i},Ei.prototype.createAny=function(e,t,n){var i=yi(e),r={$type:e,$instanceOf:function(e){return e===this.$type}},a={name:e,isGeneric:!0,ns:{prefix:i.prefix,localName:i.localName,uri:t}};return this.properties.defineDescriptor(r,a),this.properties.defineModel(r,this),this.properties.define(r,"$parent",{enumerable:!1,writable:!0}),this.properties.define(r,"$instanceOf",{enumerable:!1,writable:!0}),h(n,(function(e,t){o(e)&&void 0!==e.value?r[e.name]=e.value:r[t]=e})),r},Ei.prototype.getPackage=function(e){return this.registry.getPackage(e)},Ei.prototype.getPackages=function(){return this.registry.getPackages()},Ei.prototype.getElementDescriptor=function(e){return e.$descriptor},Ei.prototype.hasType=function(e,t){return void 0===t&&(t=e,e=this),t in e.$model.getElementDescriptor(e).allTypesByName},Ei.prototype.getPropertyDescriptor=function(e,t){return this.getElementDescriptor(e).propertiesByName[t]},Ei.prototype.getTypeDescriptor=function(e){return this.registry.typeMap[e]};var _i=String.fromCharCode,Ai=Object.prototype.hasOwnProperty,Si=/&#(\d+);|&#x([0-9a-f]+);|&(\w+);/gi,Ri={amp:"&",apos:"'",gt:">",lt:"<",quot:'"'};function Ci(e,t,n,i){return i?Ai.call(Ri,i)?Ri[i]:"&"+i+";":_i(t||parseInt(n,16))}function Mi(e){return e.length>3&&-1!==e.indexOf("&")?e.replace(Si,Ci):e}Object.keys(Ri).forEach((function(e){Ri[e.toUpperCase()]=Ri[e]}));var ki="xsi:type",Pi="non-whitespace outside of root node";function Di(e){return new Error(e)}function Ti(e){return"missing namespace for prefix <"+e+">"}function Ni(e){return{get:e,enumerable:!0}}function Oi(e){var t,n={};for(t in e)n[t]=e[t];return n}function Bi(e){return e+"$uri"}function Li(){return{line:0,column:0}}function Ii(e){throw e}function ji(e){if(!this)return new ji(e);var t,n,i,r,o,a,s,l,p,c=e&&e.proxy,u=Ii,h=Li,m=!1,f=!1,d=null,y=!1;function g(e){e instanceof Error||(e=Di(e)),d=e,u(e,h)}function v(e){o&&(e instanceof Error||(e=Di(e)),o(e,h))}this.on=function(e,p){if("function"!=typeof p)throw Di("required args ");switch(e){case"openTag":n=p;break;case"text":t=p;break;case"closeTag":i=p;break;case"error":u=p;break;case"warn":o=p;break;case"cdata":r=p;break;case"attention":l=p;break;case"question":s=p;break;case"comment":a=p;break;default:throw Di("unsupported event: "+e)}return this},this.ns=function(e){if(void 0===e&&(e={}),"object"!=typeof e)throw Di("required args ");var t,n={};for(t in e)n[t]=e[t];return n["http://www.w3.org/2001/XMLSchema-instance"]="xsi",f=!0,p=n,this},this.parse=function(e){if("string"!=typeof e)throw Di("required args ");return d=null,function(e){var o,u,d,b,x,w,E,_,A,S,R,C=f?[]:null,M=f?function(e){var t,n,i={};for(t in e)i[n=e[t]]=n,i[Bi(n)]=t;return i}(p):null,k=[],P=0,D=!1,T=!1,N=0,O=0,B="",L=0;function I(){if(null!==R)return R;var e,t,n,i,r,o,a,s,l,c,u,h=f&&M.xmlns,d=f&&m?[]:null,y=L,g=B,b=g.length,x={},w={};e:for(;y8)){for((c<65||c>122||c>90&&c<97)&&95!==c&&58!==c&&(v("illegal first char attribute name"),l=!0),u=y+1;u96&&c<123||c>64&&c<91||c>47&&c<59||46===c||45===c||95===c)){if(32===c||c<14&&c>8){v("missing attribute value"),y=u;continue e}if(61===c)break;v("illegal attribute name char"),l=!0}if("xmlns:xmlns"===(s=g.substring(y,u))&&(v("illegal declaration of xmlns"),l=!0),34===(c=g.charCodeAt(u+1)))-1===(u=g.indexOf('"',y=u+2))&&-1!==(u=g.indexOf("'",y))&&(v("attribute value quote missmatch"),l=!0);else if(39===c)-1===(u=g.indexOf("'",y=u+2))&&-1!==(u=g.indexOf('"',y))&&(v("attribute value quote missmatch"),l=!0);else for(v("missing attribute value quotes"),l=!0,u+=1;u8);u++);for(-1===u&&(v("missing closing quotes"),u=b,l=!0),l||(o=g.substring(y,u)),y=u;u+18);u++)y===u&&(v("illegal character after attribute end"),l=!0);if(y=u+1,!l)if(s in w)v("attribute <"+s+"> already defined");else if(w[s]=!0,f)if(m){if(null!==(r="xmlns"===s?"xmlns":120===s.charCodeAt(0)&&"xmlns:"===s.substr(0,6)?s.substr(6):null)){if(e=Mi(o),t=Bi(r),!(a=p[e])){if("xmlns"===r||t in M&&M[t]!==e)do{a="ns"+P++}while(void 0!==M[a]);else a=r;p[e]=a}M[r]!==a&&(i||(M=Oi(M),i=!0),M[r]=a,"xmlns"===r&&(M[Bi(a)]=e,h=a),M[t]=e),x[s]=o;continue}d.push(s,o)}else-1!==(c=s.indexOf(":"))?(n=M[s.substring(0,c)])?((s=h===n?s.substr(c+1):n+s.substr(c))===ki&&(-1!==(c=o.indexOf(":"))?(n=o.substring(0,c),o=(n=M[n]||n)+o.substring(c)):o=h+":"+o),x[s]=o):v(Ti(s.substring(0,c))):x[s]=o;else x[s]=o}if(m)for(y=0,b=d.length;y=a&&(t=i.exec(e))&&!((s=t[0].length+t.index)>N);)r+=1,a=s;return-1==N?(o=s,n=e.substring(O)):0===O?n=e.substring(O,N):(o=N-a,n=-1==O?e.substring(N):e.substring(N,O+1)),{data:n,line:r,column:o}}h=j,c&&(S=Object.create({},{name:Ni((function(){return _})),originalName:Ni((function(){return A})),attrs:Ni(I),ns:Ni((function(){return M}))}));for(;-1!==O;){if(-1===(N=60===e.charCodeAt(O)?O:e.indexOf("<",O)))return k.length?g("unexpected end of file"):0===O?g("missing start tag"):void(O",N)))return g("unclosed cdata");if(r&&(r(e.substring(N+9,O),h),y))return;O+=3;continue}if(45===b&&45===e.charCodeAt(N+3)){if(-1===(O=e.indexOf("--\x3e",N)))return g("unclosed comment");if(a&&(a(e.substring(N+4,O),Mi,h),y))return;O+=3;continue}}if(63!==x){for(u=N+1;;u++){if(w=e.charCodeAt(u),isNaN(w))return O=-1,g("unclosed tag");if(34===w)u=-1!==(b=e.indexOf('"',u+1))?b:u;else if(39===w)u=-1!==(b=e.indexOf("'",u+1))?b:u;else if(62===w){O=u;break}}if(33!==x){if(R={},47===x){if(D=!1,T=!0,!k.length)return g("missing open tag");if(u=_=k.pop(),b=N+2+u.length,e.substring(N+2,b)!==u)return g("closing tag mismatch");for(;b8&&x<14))return g("close tag")}else{if(47===e.charCodeAt(O-1)?(u=_=e.substring(N+1,O-1),D=!0,T=!0):(u=_=e.substring(N+1,O),D=!0,T=!1),!(x>96&&x<123||x>64&&x<91||95===x||58===x))return g("illegal first char nodeName");for(b=1,d=u.length;b96&&x<123||x>64&&x<91||x>47&&x<59||45===x||95===x||46==x)){if(32===x||x<14&&x>8){_=u.substring(0,b),R=null;break}return g("invalid nodeName")}T||k.push(_)}if(f){if(o=M,D&&(T||C.push(o),null===R&&(m=-1!==u.indexOf("xmlns",b))&&(L=b,B=u,I(),m=!1)),A=_,-1!==(x=_.indexOf(":"))){if(!(E=M[_.substring(0,x)]))return g("missing namespace on <"+A+">");_=_.substr(x+1)}else E=M.xmlns;E&&(_=E+":"+_)}if(D&&(L=b,B=u,n&&(c?n(S,Mi,T,h):n(_,I,Mi,T,h),y)))return;if(T){if(i&&(i(c?S:_,Mi,D,h),y))return;f&&(M=D?o:C.pop())}O+=1}else{if(l&&(l(e.substring(N,O+1),Mi,h),y))return;O+=1}}else{if(-1===(O=e.indexOf("?>",N)))return g("unclosed question");if(s&&(s(e.substring(N,O+2),h),y))return;O+=2}}}(e),h=Li,y=!1,d},this.stop=function(){y=!0}}function Fi(e){return e.xml&&"lowerCase"===e.xml.tagAlias}var Vi={xsi:"http://www.w3.org/2001/XMLSchema-instance",xml:"http://www.w3.org/XML/1998/namespace"},zi="xsi:type";function Wi(e){return e.xml&&e.xml.serialize}function Gi(e){return Wi(e)===zi}function $i(e,t){return Fi(t)?e.prefix+":"+((n=e.localName).charAt(0).toUpperCase()+n.slice(1)):e.name;var n}function Hi(e){return new Error(e)}function Ki(e){return e.$descriptor}function Ui(e){E(this,e),this.elementsById={},this.references=[],this.warnings=[],this.addReference=function(e){this.references.push(e)},this.addElement=function(e){if(!e)throw Hi("expected element");var t,n=this.elementsById,i=Ki(e).idProperty;if(i&&(t=e.get(i.name))){if(!/^([a-z][\w-.]*:)?[a-z_][\w-.]*$/i.test(t))throw new Error("illegal ID <"+t+">");if(n[t])throw Hi("duplicate ID <"+t+">");n[t]=e}},this.addWarning=function(e){this.warnings.push(e)}}function qi(){}function Yi(){}function Xi(){}function Zi(e,t){this.property=e,this.context=t}function Ji(e,t){this.element=t,this.propertyDesc=e}function Qi(){}function er(e,t,n){this.model=e,this.type=e.getType(t),this.context=n}function tr(e,t,n){er.call(this,e,t,n)}function nr(e,t,n){this.model=e,this.context=n}function ir(e){e instanceof Ei&&(e={model:e}),E(this,{lax:!1},e)}qi.prototype.handleEnd=function(){},qi.prototype.handleText=function(){},qi.prototype.handleNode=function(){},Yi.prototype=Object.create(qi.prototype),Yi.prototype.handleNode=function(){return this},Xi.prototype=Object.create(qi.prototype),Xi.prototype.handleText=function(e){this.body=(this.body||"")+e},Zi.prototype=Object.create(Xi.prototype),Zi.prototype.handleNode=function(e){if(this.element)throw Hi("expected no sub nodes");return this.element=this.createReference(e),this},Zi.prototype.handleEnd=function(){this.element.id=this.body},Zi.prototype.createReference=function(e){return{property:this.property.ns.name,id:""}},Ji.prototype=Object.create(Xi.prototype),Ji.prototype.handleEnd=function(){var e=this.body||"",t=this.element,n=this.propertyDesc;e=mi(n.type,e),n.isMany?t.get(n.name).push(e):t.set(n.name,e)},Qi.prototype=Object.create(Xi.prototype),Qi.prototype.handleNode=function(e){var t=this,n=this.element;return n?t=this.handleChild(e):(n=this.element=this.createElement(e),this.context.addElement(n)),t},er.prototype=Object.create(Qi.prototype),er.prototype.addReference=function(e){this.context.addReference(e)},er.prototype.handleText=function(e){if(!Ki(this.element).bodyProperty)throw Hi("unexpected body text <"+e+">");Xi.prototype.handleText.call(this,e)},er.prototype.handleEnd=function(){var e=this.body,t=this.element,n=Ki(t).bodyProperty;n&&void 0!==e&&(e=mi(n.type,e),t.set(n.name,e))},er.prototype.createElement=function(e){var t,n=e.attributes,i=this.type,r=Ki(i),o=this.context,a=new i({}),s=this.model;return h(n,(function(e,n){var i=r.propertiesByName[n];i&&i.isReference?i.isMany?h(e.split(" "),(function(e){o.addReference({element:a,property:i.ns.name,id:e})})):o.addReference({element:a,property:i.ns.name,id:e}):(i?e=mi(i.type,e):"xmlns"!==n&&(t=yi(n,r.ns.prefix),s.getPackage(t.prefix)&&o.addWarning({message:"unknown attribute <"+n+">",element:a,property:n,value:e})),a.set(n,e))})),a},er.prototype.getPropertyForNode=function(e){var t,n,i=yi(e.name),r=this.type,o=this.model,a=Ki(r),s=i.name,l=a.propertiesByName[s];if(l&&!l.isAttr)return Gi(l)&&(t=e.attributes[zi])?(t=function(e,t){var n=yi(e);return function(e,t){var n=e.name,i=e.localName,r=t.xml&&t.xml.typePrefix;return r&&0===i.indexOf(r)?e.prefix+":"+i.slice(r.length):n}(n,t.getPackage(n.prefix))}(t,o),E({},l,{effectiveType:Ki(n=o.getType(t)).name})):l;var p=o.getPackage(i.prefix);if(p){if(t=$i(i,p),n=o.getType(t),l=c(a.properties,(function(e){return!e.isVirtual&&!e.isReference&&!e.isAttribute&&n.hasType(e.type)})))return E({},l,{effectiveType:Ki(n).name})}else if(l=c(a.properties,(function(e){return!e.isReference&&!e.isAttribute&&"Element"===e.type})))return l;throw Hi("unrecognized element <"+i.name+">")},er.prototype.toString=function(){return"ElementDescriptor["+Ki(this.type).name+"]"},er.prototype.valueHandler=function(e,t){return new Ji(e,t)},er.prototype.referenceHandler=function(e){return new Zi(e,this.context)},er.prototype.handler=function(e){return"Element"===e?new nr(this.model,e,this.context):new er(this.model,e,this.context)},er.prototype.handleChild=function(e){var t,n,i,r;if(t=this.getPropertyForNode(e),i=this.element,di(n=t.effectiveType||t.type))return this.valueHandler(t,i);var o=(r=t.isReference?this.referenceHandler(t).handleNode(e):this.handler(n).handleNode(e)).element;return void 0!==o&&(t.isMany?i.get(t.name).push(o):i.set(t.name,o),t.isReference?(E(o,{element:i}),this.context.addReference(o)):o.$parent=i),r},tr.prototype=Object.create(er.prototype),tr.prototype.createElement=function(e){var t=e.name,n=yi(t),i=this.model,r=this.type,o=i.getPackage(n.prefix),a=o&&$i(n,o)||t;if(!r.hasType(a))throw Hi("unexpected element <"+e.originalName+">");return er.prototype.createElement.call(this,e)},nr.prototype=Object.create(Qi.prototype),nr.prototype.createElement=function(e){var t=e.name,n=yi(t).prefix,i=e.ns[n+"$uri"],r=e.attributes;return this.model.createAny(t,i,r)},nr.prototype.handleChild=function(e){var t=new nr(this.model,"Element",this.context).handleNode(e),n=this.element,i=t.element;return void 0!==i&&((n.$children=n.$children||[]).push(i),i.$parent=n),t},nr.prototype.handleEnd=function(){this.body&&(this.element.$body=this.body)},ir.prototype.fromXML=function(e,t,n){var i=t.rootHandler;t instanceof er?(i=t,t={}):"string"==typeof t?(i=this.handler(t),t={}):"string"==typeof i&&(i=this.handler(i));var r=this.model,o=this.lax,a=new Ui(E({},t,{rootHandler:i})),s=new ji({proxy:!0}),l=function(){var e=[];return Object.defineProperty(e,"peek",{value:function(){return this[this.length-1]}}),e}();function p(e,t,n){var i=t(),r=i.line,o=i.column,s=i.data;"<"===s.charAt(0)&&-1!==s.indexOf(" ")&&(s=s.slice(0,s.indexOf(" "))+">");var l="unparsable content "+(s?s+" ":"")+"detected\n\tline: "+r+"\n\tcolumn: "+o+"\n\tnested error: "+e.message;if(n)return a.addWarning({message:l,error:e}),!0;throw Hi(l)}function c(e,t){return p(e,t,!0)}i.context=a,l.push(i);var u=/^<\?xml /i,h=/ encoding="([^"]+)"/i,m=/^utf-8$/i;function f(e,t){try{l.peek().handleText(e)}catch(e){c(e,t)}}var d=r.getPackages().reduce((function(e,t){return e[t.uri]=t.prefix,e}),{"http://www.w3.org/XML/1998/namespace":"xml"});return s.ns(d).on("openTag",(function(e,t,n,i){var r=e.attrs||{},a=Object.keys(r).reduce((function(e,n){var i=t(r[n]);return e[n]=i,e}),{});!function(e,t){var n=l.peek();try{l.push(n.handleNode(e))}catch(e){p(e,t,o)&&l.push(new Yi)}}({name:e.name,originalName:e.originalName,attributes:a,ns:e.ns},i)})).on("question",(function(e){if(u.test(e)){var t=h.exec(e),n=t&&t[1];n&&!m.test(n)&&a.addWarning({message:"unsupported document encoding <"+n+">, falling back to UTF-8"})}})).on("closeTag",(function(){l.pop().handleEnd()})).on("cdata",f).on("text",(function(e,t,n){!function(e,t){e.trim()&&f(e,t)}(t(e),n)})).on("error",p).on("warn",c),new Promise((function(t,n){var r;try{s.parse(e),function(){var e,t,n=a.elementsById,i=a.references;for(e=0;t=i[e];e++){var r=t.element,o=n[t.id],s=Ki(r).propertiesByName[t.property];if(o||a.addWarning({message:"unresolved reference <"+t.id+">",element:t.element,property:t.property,value:t.id}),s.isMany){var l=r.get(s.name),p=l.indexOf(t);-1===p&&(p=l.length),o?l[p]=o:l.splice(p,1)}else r.set(s.name,o)}}()}catch(e){r=e}var o=i.element;r||o||(r=Hi("failed to parse document as <"+i.type.$descriptor.name+">"));var l=a.warnings,p=a.references,c=a.elementsById;return r?(r.warnings=l,n(r)):t({rootElement:o,elementsById:c,references:p,warnings:l})}))},ir.prototype.handler=function(e){return new tr(this.model,e)};var rr=/<|>|'|"|&|\n\r|\n/g,or=/<|>|&/g;function ar(e){var t={},n={},i={},r=[],o=[];this.byUri=function(t){return n[t]||e&&e.byUri(t)},this.add=function(e,t){n[e.uri]=e,t?r.push(e):o.push(e),this.mapPrefix(e.prefix,e.uri)},this.uriByPrefix=function(e){return t[e||"xmlns"]},this.mapPrefix=function(e,n){t[e||"xmlns"]=n},this.getNSKey=function(e){return void 0!==e.prefix?e.uri+"|"+e.prefix:e.uri},this.logUsed=function(t){var n=t.uri,r=this.getNSKey(t);i[r]=this.byUri(n),e&&e.logUsed(t)},this.getUsed=function(e){var t=this;return[].concat(r,o).filter((function(e){var n=t.getNSKey(e);return i[n]}))}}function sr(e,t){return Fi(t)?(n=e).charAt(0).toLowerCase()+n.slice(1):e;var n}function lr(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})}function pr(e){return l(e)?e:(e.prefix?e.prefix+":":"")+e.localName}var cr={"\n":"#10","\n\r":"#10",'"':"#34","'":"#39","<":"#60",">":"#62","&":"#38"},ur={"<":"lt",">":"gt","&":"amp"};function hr(e,t,n){return(e=l(e)?e:""+e).replace(t,(function(e){return"&"+n[e]+";"}))}function mr(e){this.tagName=e}function fr(){}function dr(e){this.tagName=e}function yr(e,t){this.body=[],this.attrs=[],this.parent=e,this.propertyDescriptor=t}function gr(e,t){yr.call(this,e,t)}function vr(){this.value="",this.write=function(e){this.value+=e}}function br(e,t){var n=[""];this.append=function(t){return e.write(t),this},this.appendNewLine=function(){return t&&e.write("\n"),this},this.appendIndent=function(){return t&&e.write(n.join(" ")),this},this.indent=function(){return n.push(""),this},this.unindent=function(){return n.pop(),this}}function xr(e){return e=E({format:!1,preamble:!0},e||{}),{toXML:function(t,n){var i=n||new vr,r=new br(i,e.format);if(e.preamble&&r.append('\n'),(new yr).build(t).serializeTo(r),!n)return i.value}}}function wr(e,t){Ei.call(this,e,t)}mr.prototype.build=function(e){return this.element=e,this},mr.prototype.serializeTo=function(e){e.appendIndent().append("<"+this.tagName+">"+this.element.id+"").appendNewLine()},fr.prototype.serializeValue=fr.prototype.serializeTo=function(e){e.append(this.escape?hr(this.value,or,ur):this.value)},fr.prototype.build=function(e,t){return this.value=t,"String"===e.type&&-1!==t.search(or)&&(this.escape=!0),this},lr(dr,fr),dr.prototype.serializeTo=function(e){e.appendIndent().append("<"+this.tagName+">"),this.serializeValue(e),e.append("").appendNewLine()},yr.prototype.build=function(e){this.element=e;var t,n,i=e.$descriptor,r=this.propertyDescriptor,o=i.isGeneric;return t=o?this.parseGeneric(e):this.parseNsAttributes(e),this.ns=r?this.nsPropertyTagName(r):this.nsTagName(i),this.tagName=this.addTagName(this.ns),o||(n=function(e){return u(e.$descriptor.properties,(function(t){var n=t.name;if(t.isVirtual)return!1;if(!p(e,n))return!1;var i=e[n];return i!==t.default&&null!==i&&(!t.isMany||i.length)}))}(e),this.parseAttributes(u(n,(function(e){return e.isAttr}))),this.parseContainments(function(e){return u(e,(function(e){return!e.isAttr}))}(n))),this.parseGenericAttributes(e,t),this},yr.prototype.nsTagName=function(e){return function(e,t){return t.isGeneric?E({localName:t.ns.localName},e):E({localName:sr(t.ns.localName,t.$pkg)},e)}(this.logNamespaceUsed(e.ns),e)},yr.prototype.nsPropertyTagName=function(e){return function(e,t){return E({localName:t.ns.localName},e)}(this.logNamespaceUsed(e.ns),e)},yr.prototype.isLocalNs=function(e){return e.uri===this.ns.uri},yr.prototype.nsAttributeName=function(e){var t;if(t=l(e)?yi(e):e.ns,e.inherited)return{localName:t.localName};var n=this.logNamespaceUsed(t);return this.getNamespaces().logUsed(n),this.isLocalNs(n)?{localName:t.localName}:E({localName:t.localName},n)},yr.prototype.parseGeneric=function(e){var t=this,n=this.body,i=[];return h(e,(function(r,o){"$body"===o?n.push((new fr).build({type:"String"},r)):"$children"===o?h(r,(function(e){n.push(new yr(t).build(e))})):0!==o.indexOf("$")&&t.parseNsAttribute(e,o,r)&&i.push({name:o,value:r})})),i},yr.prototype.parseNsAttribute=function(e,t,n){var i,r=e.$model,o=yi(t);if("xmlns"===o.prefix&&(i={prefix:o.localName,uri:n}),o.prefix||"xmlns"!==o.localName||(i={uri:n}),!i)return{name:t,value:n};if(r&&r.getPackage(n))this.logNamespace(i,!0,!0);else{var a=this.logNamespaceUsed(i,!0);this.getNamespaces().logUsed(a)}},yr.prototype.parseNsAttributes=function(e,t){var n=this,i=e.$attrs,r=[];return h(i,(function(t,i){var o=n.parseNsAttribute(e,i,t);o&&r.push(o)})),r},yr.prototype.parseGenericAttributes=function(e,t){var n=this;h(t,(function(t){if(t.name!==zi)try{n.addAttribute(n.nsAttributeName(t.name),t.value)}catch(n){console.warn("missing namespace information for ",t.name,"=",t.value,"on",e,n)}}))},yr.prototype.parseContainments=function(e){var t=this,n=this.body,i=this.element;h(e,(function(e){var r=i.get(e.name),o=e.isReference;if(e.isMany||(r=[r]),e.isBody)n.push((new fr).build(e,r[0]));else if(di(e.type))h(r,(function(i){n.push(new dr(t.addTagName(t.nsPropertyTagName(e))).build(e,i))}));else if(o)h(r,(function(i){n.push(new mr(t.addTagName(t.nsPropertyTagName(e))).build(i))}));else{var a=Gi(e),s=function(e){return"property"===Wi(e)}(e);h(r,(function(i){var r;r=a?new gr(t,e):s?new yr(t,e):new yr(t),n.push(r.build(i))}))}}))},yr.prototype.getNamespaces=function(e){var t,n=this.namespaces,i=this.parent;return n||(t=i&&i.getNamespaces(),e||!t?this.namespaces=n=new ar(t):n=t),n},yr.prototype.logNamespace=function(e,t,n){var i=this.getNamespaces(n),r=e.uri,o=e.prefix;return i.byUri(r)&&!n||i.add(e,t),i.mapPrefix(o,r),e},yr.prototype.logNamespaceUsed=function(e,t){var n,i,r,o=this.element.$model,a=this.getNamespaces(t),s=e.prefix,l=e.uri;if(!s&&!l)return{localName:e.localName};if(r=Vi[s]||o&&(o.getPackage(s)||{}).uri,!(l=l||r||a.uriByPrefix(s)))throw new Error("no namespace uri given for prefix <"+s+">");if(!(e=a.byUri(l))){for(n=s,i=1;a.uriByPrefix(n);)n=s+"_"+i++;e=this.logNamespace({prefix:n,uri:l},r===l)}return s&&a.mapPrefix(s,l),e},yr.prototype.parseAttributes=function(e){var t=this,n=this.element;h(e,(function(e){var i=n.get(e.name);if(e.isReference)if(e.isMany){var r=[];h(i,(function(e){r.push(e.id)})),i=r.join(" ")}else i=i.id;t.addAttribute(t.nsAttributeName(e),i)}))},yr.prototype.addTagName=function(e){var t=this.logNamespaceUsed(e);return this.getNamespaces().logUsed(t),pr(e)},yr.prototype.addAttribute=function(e,t){var n=this.attrs;l(t)&&(t=hr(t,rr,cr));var i=function(e,t){t=g(t);var n=r(e)?-1:void 0;return h(e,(function(e,i){if(t(e,i))return n=i,!1})),n}(n,(function(t){return t.name.localName===e.localName&&t.name.uri===e.uri&&t.name.prefix===e.prefix})),o={name:e,value:t};-1!==i?n.splice(i,1,o):n.push(o)},yr.prototype.serializeAttributes=function(e){var t=this.attrs,n=this.namespaces;n&&(t=function(e){return e.getUsed().filter((function(e){return"xml"!==e.prefix})).map((function(e){return{name:"xmlns"+(e.prefix?":"+e.prefix:""),value:e.uri}}))}(n).concat(t)),h(t,(function(t){e.append(" ").append(pr(t.name)).append('="').append(t.value).append('"')}))},yr.prototype.serializeTo=function(e){var t=this.body[0],n=t&&t.constructor!==fr;e.appendIndent().append("<"+this.tagName),this.serializeAttributes(e),e.append(t?">":" />"),t&&(n&&e.appendNewLine().indent(),h(this.body,(function(t){t.serializeTo(e)})),n&&e.unindent().appendIndent(),e.append("")),e.appendNewLine()},lr(gr,yr),gr.prototype.parseNsAttributes=function(e){var t=yr.prototype.parseNsAttributes.call(this,e),n=e.$descriptor;if(n.name===this.propertyDescriptor.type)return t;var i=this.typeNs=this.nsTagName(n);this.getNamespaces().logUsed(this.typeNs);var r=e.$model.getPackage(i.uri),o=r.xml&&r.xml.typePrefix||"";return this.addAttribute(this.nsAttributeName(zi),(i.prefix?i.prefix+":":"")+o+n.ns.localName),t},gr.prototype.isLocalNs=function(e){return e.uri===(this.typeNs||this.ns).uri},wr.prototype=Object.create(Ei.prototype),wr.prototype.fromXML=function(e,t,n){l(t)||(n=t,t="bpmn:Definitions");var i=new ir(E({model:this,lax:!0},n)),r=i.handler(t);return i.fromXML(e,r)},wr.prototype.toXML=function(e,t){var n=new xr(t);return new Promise((function(t,i){try{return t({xml:n.toXML(e)})}catch(e){return i(e)}}))};var Er={bpmn:{name:"BPMN20",uri:"http://www.omg.org/spec/BPMN/20100524/MODEL",prefix:"bpmn",associations:[],types:[{name:"Interface",superClass:["RootElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"operations",type:"Operation",isMany:!0},{name:"implementationRef",isAttr:!0,type:"String"}]},{name:"Operation",superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"inMessageRef",type:"Message",isReference:!0},{name:"outMessageRef",type:"Message",isReference:!0},{name:"errorRef",type:"Error",isMany:!0,isReference:!0},{name:"implementationRef",isAttr:!0,type:"String"}]},{name:"EndPoint",superClass:["RootElement"]},{name:"Auditing",superClass:["BaseElement"]},{name:"GlobalTask",superClass:["CallableElement"],properties:[{name:"resources",type:"ResourceRole",isMany:!0}]},{name:"Monitoring",superClass:["BaseElement"]},{name:"Performer",superClass:["ResourceRole"]},{name:"Process",superClass:["FlowElementsContainer","CallableElement"],properties:[{name:"processType",type:"ProcessType",isAttr:!0},{name:"isClosed",isAttr:!0,type:"Boolean"},{name:"auditing",type:"Auditing"},{name:"monitoring",type:"Monitoring"},{name:"properties",type:"Property",isMany:!0},{name:"laneSets",isMany:!0,replaces:"FlowElementsContainer#laneSets",type:"LaneSet"},{name:"flowElements",isMany:!0,replaces:"FlowElementsContainer#flowElements",type:"FlowElement"},{name:"artifacts",type:"Artifact",isMany:!0},{name:"resources",type:"ResourceRole",isMany:!0},{name:"correlationSubscriptions",type:"CorrelationSubscription",isMany:!0},{name:"supports",type:"Process",isMany:!0,isReference:!0},{name:"definitionalCollaborationRef",type:"Collaboration",isAttr:!0,isReference:!0},{name:"isExecutable",isAttr:!0,type:"Boolean"}]},{name:"LaneSet",superClass:["BaseElement"],properties:[{name:"lanes",type:"Lane",isMany:!0},{name:"name",isAttr:!0,type:"String"}]},{name:"Lane",superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"partitionElementRef",type:"BaseElement",isAttr:!0,isReference:!0},{name:"partitionElement",type:"BaseElement"},{name:"flowNodeRef",type:"FlowNode",isMany:!0,isReference:!0},{name:"childLaneSet",type:"LaneSet",xml:{serialize:"xsi:type"}}]},{name:"GlobalManualTask",superClass:["GlobalTask"]},{name:"ManualTask",superClass:["Task"]},{name:"UserTask",superClass:["Task"],properties:[{name:"renderings",type:"Rendering",isMany:!0},{name:"implementation",isAttr:!0,type:"String"}]},{name:"Rendering",superClass:["BaseElement"]},{name:"HumanPerformer",superClass:["Performer"]},{name:"PotentialOwner",superClass:["HumanPerformer"]},{name:"GlobalUserTask",superClass:["GlobalTask"],properties:[{name:"implementation",isAttr:!0,type:"String"},{name:"renderings",type:"Rendering",isMany:!0}]},{name:"Gateway",isAbstract:!0,superClass:["FlowNode"],properties:[{name:"gatewayDirection",type:"GatewayDirection",default:"Unspecified",isAttr:!0}]},{name:"EventBasedGateway",superClass:["Gateway"],properties:[{name:"instantiate",default:!1,isAttr:!0,type:"Boolean"},{name:"eventGatewayType",type:"EventBasedGatewayType",isAttr:!0,default:"Exclusive"}]},{name:"ComplexGateway",superClass:["Gateway"],properties:[{name:"activationCondition",type:"Expression",xml:{serialize:"xsi:type"}},{name:"default",type:"SequenceFlow",isAttr:!0,isReference:!0}]},{name:"ExclusiveGateway",superClass:["Gateway"],properties:[{name:"default",type:"SequenceFlow",isAttr:!0,isReference:!0}]},{name:"InclusiveGateway",superClass:["Gateway"],properties:[{name:"default",type:"SequenceFlow",isAttr:!0,isReference:!0}]},{name:"ParallelGateway",superClass:["Gateway"]},{name:"RootElement",isAbstract:!0,superClass:["BaseElement"]},{name:"Relationship",superClass:["BaseElement"],properties:[{name:"type",isAttr:!0,type:"String"},{name:"direction",type:"RelationshipDirection",isAttr:!0},{name:"source",isMany:!0,isReference:!0,type:"Element"},{name:"target",isMany:!0,isReference:!0,type:"Element"}]},{name:"BaseElement",isAbstract:!0,properties:[{name:"id",isAttr:!0,type:"String",isId:!0},{name:"documentation",type:"Documentation",isMany:!0},{name:"extensionDefinitions",type:"ExtensionDefinition",isMany:!0,isReference:!0},{name:"extensionElements",type:"ExtensionElements"}]},{name:"Extension",properties:[{name:"mustUnderstand",default:!1,isAttr:!0,type:"Boolean"},{name:"definition",type:"ExtensionDefinition",isAttr:!0,isReference:!0}]},{name:"ExtensionDefinition",properties:[{name:"name",isAttr:!0,type:"String"},{name:"extensionAttributeDefinitions",type:"ExtensionAttributeDefinition",isMany:!0}]},{name:"ExtensionAttributeDefinition",properties:[{name:"name",isAttr:!0,type:"String"},{name:"type",isAttr:!0,type:"String"},{name:"isReference",default:!1,isAttr:!0,type:"Boolean"},{name:"extensionDefinition",type:"ExtensionDefinition",isAttr:!0,isReference:!0}]},{name:"ExtensionElements",properties:[{name:"valueRef",isAttr:!0,isReference:!0,type:"Element"},{name:"values",type:"Element",isMany:!0},{name:"extensionAttributeDefinition",type:"ExtensionAttributeDefinition",isAttr:!0,isReference:!0}]},{name:"Documentation",superClass:["BaseElement"],properties:[{name:"text",type:"String",isBody:!0},{name:"textFormat",default:"text/plain",isAttr:!0,type:"String"}]},{name:"Event",isAbstract:!0,superClass:["FlowNode","InteractionNode"],properties:[{name:"properties",type:"Property",isMany:!0}]},{name:"IntermediateCatchEvent",superClass:["CatchEvent"]},{name:"IntermediateThrowEvent",superClass:["ThrowEvent"]},{name:"EndEvent",superClass:["ThrowEvent"]},{name:"StartEvent",superClass:["CatchEvent"],properties:[{name:"isInterrupting",default:!0,isAttr:!0,type:"Boolean"}]},{name:"ThrowEvent",isAbstract:!0,superClass:["Event"],properties:[{name:"dataInputs",type:"DataInput",isMany:!0},{name:"dataInputAssociations",type:"DataInputAssociation",isMany:!0},{name:"inputSet",type:"InputSet"},{name:"eventDefinitions",type:"EventDefinition",isMany:!0},{name:"eventDefinitionRef",type:"EventDefinition",isMany:!0,isReference:!0}]},{name:"CatchEvent",isAbstract:!0,superClass:["Event"],properties:[{name:"parallelMultiple",isAttr:!0,type:"Boolean",default:!1},{name:"dataOutputs",type:"DataOutput",isMany:!0},{name:"dataOutputAssociations",type:"DataOutputAssociation",isMany:!0},{name:"outputSet",type:"OutputSet"},{name:"eventDefinitions",type:"EventDefinition",isMany:!0},{name:"eventDefinitionRef",type:"EventDefinition",isMany:!0,isReference:!0}]},{name:"BoundaryEvent",superClass:["CatchEvent"],properties:[{name:"cancelActivity",default:!0,isAttr:!0,type:"Boolean"},{name:"attachedToRef",type:"Activity",isAttr:!0,isReference:!0}]},{name:"EventDefinition",isAbstract:!0,superClass:["RootElement"]},{name:"CancelEventDefinition",superClass:["EventDefinition"]},{name:"ErrorEventDefinition",superClass:["EventDefinition"],properties:[{name:"errorRef",type:"Error",isAttr:!0,isReference:!0}]},{name:"TerminateEventDefinition",superClass:["EventDefinition"]},{name:"EscalationEventDefinition",superClass:["EventDefinition"],properties:[{name:"escalationRef",type:"Escalation",isAttr:!0,isReference:!0}]},{name:"Escalation",properties:[{name:"structureRef",type:"ItemDefinition",isAttr:!0,isReference:!0},{name:"name",isAttr:!0,type:"String"},{name:"escalationCode",isAttr:!0,type:"String"}],superClass:["RootElement"]},{name:"CompensateEventDefinition",superClass:["EventDefinition"],properties:[{name:"waitForCompletion",isAttr:!0,type:"Boolean",default:!0},{name:"activityRef",type:"Activity",isAttr:!0,isReference:!0}]},{name:"TimerEventDefinition",superClass:["EventDefinition"],properties:[{name:"timeDate",type:"Expression",xml:{serialize:"xsi:type"}},{name:"timeCycle",type:"Expression",xml:{serialize:"xsi:type"}},{name:"timeDuration",type:"Expression",xml:{serialize:"xsi:type"}}]},{name:"LinkEventDefinition",superClass:["EventDefinition"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"target",type:"LinkEventDefinition",isAttr:!0,isReference:!0},{name:"source",type:"LinkEventDefinition",isMany:!0,isReference:!0}]},{name:"MessageEventDefinition",superClass:["EventDefinition"],properties:[{name:"messageRef",type:"Message",isAttr:!0,isReference:!0},{name:"operationRef",type:"Operation",isAttr:!0,isReference:!0}]},{name:"ConditionalEventDefinition",superClass:["EventDefinition"],properties:[{name:"condition",type:"Expression",xml:{serialize:"xsi:type"}}]},{name:"SignalEventDefinition",superClass:["EventDefinition"],properties:[{name:"signalRef",type:"Signal",isAttr:!0,isReference:!0}]},{name:"Signal",superClass:["RootElement"],properties:[{name:"structureRef",type:"ItemDefinition",isAttr:!0,isReference:!0},{name:"name",isAttr:!0,type:"String"}]},{name:"ImplicitThrowEvent",superClass:["ThrowEvent"]},{name:"DataState",superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"}]},{name:"ItemAwareElement",superClass:["BaseElement"],properties:[{name:"itemSubjectRef",type:"ItemDefinition",isAttr:!0,isReference:!0},{name:"dataState",type:"DataState"}]},{name:"DataAssociation",superClass:["BaseElement"],properties:[{name:"sourceRef",type:"ItemAwareElement",isMany:!0,isReference:!0},{name:"targetRef",type:"ItemAwareElement",isReference:!0},{name:"transformation",type:"FormalExpression",xml:{serialize:"property"}},{name:"assignment",type:"Assignment",isMany:!0}]},{name:"DataInput",superClass:["ItemAwareElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"isCollection",default:!1,isAttr:!0,type:"Boolean"},{name:"inputSetRef",type:"InputSet",isMany:!0,isVirtual:!0,isReference:!0},{name:"inputSetWithOptional",type:"InputSet",isMany:!0,isVirtual:!0,isReference:!0},{name:"inputSetWithWhileExecuting",type:"InputSet",isMany:!0,isVirtual:!0,isReference:!0}]},{name:"DataOutput",superClass:["ItemAwareElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"isCollection",default:!1,isAttr:!0,type:"Boolean"},{name:"outputSetRef",type:"OutputSet",isMany:!0,isVirtual:!0,isReference:!0},{name:"outputSetWithOptional",type:"OutputSet",isMany:!0,isVirtual:!0,isReference:!0},{name:"outputSetWithWhileExecuting",type:"OutputSet",isMany:!0,isVirtual:!0,isReference:!0}]},{name:"InputSet",superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"dataInputRefs",type:"DataInput",isMany:!0,isReference:!0},{name:"optionalInputRefs",type:"DataInput",isMany:!0,isReference:!0},{name:"whileExecutingInputRefs",type:"DataInput",isMany:!0,isReference:!0},{name:"outputSetRefs",type:"OutputSet",isMany:!0,isReference:!0}]},{name:"OutputSet",superClass:["BaseElement"],properties:[{name:"dataOutputRefs",type:"DataOutput",isMany:!0,isReference:!0},{name:"name",isAttr:!0,type:"String"},{name:"inputSetRefs",type:"InputSet",isMany:!0,isReference:!0},{name:"optionalOutputRefs",type:"DataOutput",isMany:!0,isReference:!0},{name:"whileExecutingOutputRefs",type:"DataOutput",isMany:!0,isReference:!0}]},{name:"Property",superClass:["ItemAwareElement"],properties:[{name:"name",isAttr:!0,type:"String"}]},{name:"DataInputAssociation",superClass:["DataAssociation"]},{name:"DataOutputAssociation",superClass:["DataAssociation"]},{name:"InputOutputSpecification",superClass:["BaseElement"],properties:[{name:"dataInputs",type:"DataInput",isMany:!0},{name:"dataOutputs",type:"DataOutput",isMany:!0},{name:"inputSets",type:"InputSet",isMany:!0},{name:"outputSets",type:"OutputSet",isMany:!0}]},{name:"DataObject",superClass:["FlowElement","ItemAwareElement"],properties:[{name:"isCollection",default:!1,isAttr:!0,type:"Boolean"}]},{name:"InputOutputBinding",properties:[{name:"inputDataRef",type:"InputSet",isAttr:!0,isReference:!0},{name:"outputDataRef",type:"OutputSet",isAttr:!0,isReference:!0},{name:"operationRef",type:"Operation",isAttr:!0,isReference:!0}]},{name:"Assignment",superClass:["BaseElement"],properties:[{name:"from",type:"Expression",xml:{serialize:"xsi:type"}},{name:"to",type:"Expression",xml:{serialize:"xsi:type"}}]},{name:"DataStore",superClass:["RootElement","ItemAwareElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"capacity",isAttr:!0,type:"Integer"},{name:"isUnlimited",default:!0,isAttr:!0,type:"Boolean"}]},{name:"DataStoreReference",superClass:["ItemAwareElement","FlowElement"],properties:[{name:"dataStoreRef",type:"DataStore",isAttr:!0,isReference:!0}]},{name:"DataObjectReference",superClass:["ItemAwareElement","FlowElement"],properties:[{name:"dataObjectRef",type:"DataObject",isAttr:!0,isReference:!0}]},{name:"ConversationLink",superClass:["BaseElement"],properties:[{name:"sourceRef",type:"InteractionNode",isAttr:!0,isReference:!0},{name:"targetRef",type:"InteractionNode",isAttr:!0,isReference:!0},{name:"name",isAttr:!0,type:"String"}]},{name:"ConversationAssociation",superClass:["BaseElement"],properties:[{name:"innerConversationNodeRef",type:"ConversationNode",isAttr:!0,isReference:!0},{name:"outerConversationNodeRef",type:"ConversationNode",isAttr:!0,isReference:!0}]},{name:"CallConversation",superClass:["ConversationNode"],properties:[{name:"calledCollaborationRef",type:"Collaboration",isAttr:!0,isReference:!0},{name:"participantAssociations",type:"ParticipantAssociation",isMany:!0}]},{name:"Conversation",superClass:["ConversationNode"]},{name:"SubConversation",superClass:["ConversationNode"],properties:[{name:"conversationNodes",type:"ConversationNode",isMany:!0}]},{name:"ConversationNode",isAbstract:!0,superClass:["InteractionNode","BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"participantRef",type:"Participant",isMany:!0,isReference:!0},{name:"messageFlowRefs",type:"MessageFlow",isMany:!0,isReference:!0},{name:"correlationKeys",type:"CorrelationKey",isMany:!0}]},{name:"GlobalConversation",superClass:["Collaboration"]},{name:"PartnerEntity",superClass:["RootElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"participantRef",type:"Participant",isMany:!0,isReference:!0}]},{name:"PartnerRole",superClass:["RootElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"participantRef",type:"Participant",isMany:!0,isReference:!0}]},{name:"CorrelationProperty",superClass:["RootElement"],properties:[{name:"correlationPropertyRetrievalExpression",type:"CorrelationPropertyRetrievalExpression",isMany:!0},{name:"name",isAttr:!0,type:"String"},{name:"type",type:"ItemDefinition",isAttr:!0,isReference:!0}]},{name:"Error",superClass:["RootElement"],properties:[{name:"structureRef",type:"ItemDefinition",isAttr:!0,isReference:!0},{name:"name",isAttr:!0,type:"String"},{name:"errorCode",isAttr:!0,type:"String"}]},{name:"CorrelationKey",superClass:["BaseElement"],properties:[{name:"correlationPropertyRef",type:"CorrelationProperty",isMany:!0,isReference:!0},{name:"name",isAttr:!0,type:"String"}]},{name:"Expression",superClass:["BaseElement"],isAbstract:!1,properties:[{name:"body",isBody:!0,type:"String"}]},{name:"FormalExpression",superClass:["Expression"],properties:[{name:"language",isAttr:!0,type:"String"},{name:"evaluatesToTypeRef",type:"ItemDefinition",isAttr:!0,isReference:!0}]},{name:"Message",superClass:["RootElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"itemRef",type:"ItemDefinition",isAttr:!0,isReference:!0}]},{name:"ItemDefinition",superClass:["RootElement"],properties:[{name:"itemKind",type:"ItemKind",isAttr:!0},{name:"structureRef",isAttr:!0,type:"String"},{name:"isCollection",default:!1,isAttr:!0,type:"Boolean"},{name:"import",type:"Import",isAttr:!0,isReference:!0}]},{name:"FlowElement",isAbstract:!0,superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"auditing",type:"Auditing"},{name:"monitoring",type:"Monitoring"},{name:"categoryValueRef",type:"CategoryValue",isMany:!0,isReference:!0}]},{name:"SequenceFlow",superClass:["FlowElement"],properties:[{name:"isImmediate",isAttr:!0,type:"Boolean"},{name:"conditionExpression",type:"Expression",xml:{serialize:"xsi:type"}},{name:"sourceRef",type:"FlowNode",isAttr:!0,isReference:!0},{name:"targetRef",type:"FlowNode",isAttr:!0,isReference:!0}]},{name:"FlowElementsContainer",isAbstract:!0,superClass:["BaseElement"],properties:[{name:"laneSets",type:"LaneSet",isMany:!0},{name:"flowElements",type:"FlowElement",isMany:!0}]},{name:"CallableElement",isAbstract:!0,superClass:["RootElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"ioSpecification",type:"InputOutputSpecification",xml:{serialize:"property"}},{name:"supportedInterfaceRef",type:"Interface",isMany:!0,isReference:!0},{name:"ioBinding",type:"InputOutputBinding",isMany:!0,xml:{serialize:"property"}}]},{name:"FlowNode",isAbstract:!0,superClass:["FlowElement"],properties:[{name:"incoming",type:"SequenceFlow",isMany:!0,isReference:!0},{name:"outgoing",type:"SequenceFlow",isMany:!0,isReference:!0},{name:"lanes",type:"Lane",isMany:!0,isVirtual:!0,isReference:!0}]},{name:"CorrelationPropertyRetrievalExpression",superClass:["BaseElement"],properties:[{name:"messagePath",type:"FormalExpression"},{name:"messageRef",type:"Message",isAttr:!0,isReference:!0}]},{name:"CorrelationPropertyBinding",superClass:["BaseElement"],properties:[{name:"dataPath",type:"FormalExpression"},{name:"correlationPropertyRef",type:"CorrelationProperty",isAttr:!0,isReference:!0}]},{name:"Resource",superClass:["RootElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"resourceParameters",type:"ResourceParameter",isMany:!0}]},{name:"ResourceParameter",superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"isRequired",isAttr:!0,type:"Boolean"},{name:"type",type:"ItemDefinition",isAttr:!0,isReference:!0}]},{name:"CorrelationSubscription",superClass:["BaseElement"],properties:[{name:"correlationKeyRef",type:"CorrelationKey",isAttr:!0,isReference:!0},{name:"correlationPropertyBinding",type:"CorrelationPropertyBinding",isMany:!0}]},{name:"MessageFlow",superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"sourceRef",type:"InteractionNode",isAttr:!0,isReference:!0},{name:"targetRef",type:"InteractionNode",isAttr:!0,isReference:!0},{name:"messageRef",type:"Message",isAttr:!0,isReference:!0}]},{name:"MessageFlowAssociation",superClass:["BaseElement"],properties:[{name:"innerMessageFlowRef",type:"MessageFlow",isAttr:!0,isReference:!0},{name:"outerMessageFlowRef",type:"MessageFlow",isAttr:!0,isReference:!0}]},{name:"InteractionNode",isAbstract:!0,properties:[{name:"incomingConversationLinks",type:"ConversationLink",isMany:!0,isVirtual:!0,isReference:!0},{name:"outgoingConversationLinks",type:"ConversationLink",isMany:!0,isVirtual:!0,isReference:!0}]},{name:"Participant",superClass:["InteractionNode","BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"interfaceRef",type:"Interface",isMany:!0,isReference:!0},{name:"participantMultiplicity",type:"ParticipantMultiplicity"},{name:"endPointRefs",type:"EndPoint",isMany:!0,isReference:!0},{name:"processRef",type:"Process",isAttr:!0,isReference:!0}]},{name:"ParticipantAssociation",superClass:["BaseElement"],properties:[{name:"innerParticipantRef",type:"Participant",isAttr:!0,isReference:!0},{name:"outerParticipantRef",type:"Participant",isAttr:!0,isReference:!0}]},{name:"ParticipantMultiplicity",properties:[{name:"minimum",default:0,isAttr:!0,type:"Integer"},{name:"maximum",default:1,isAttr:!0,type:"Integer"}],superClass:["BaseElement"]},{name:"Collaboration",superClass:["RootElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"isClosed",isAttr:!0,type:"Boolean"},{name:"participants",type:"Participant",isMany:!0},{name:"messageFlows",type:"MessageFlow",isMany:!0},{name:"artifacts",type:"Artifact",isMany:!0},{name:"conversations",type:"ConversationNode",isMany:!0},{name:"conversationAssociations",type:"ConversationAssociation"},{name:"participantAssociations",type:"ParticipantAssociation",isMany:!0},{name:"messageFlowAssociations",type:"MessageFlowAssociation",isMany:!0},{name:"correlationKeys",type:"CorrelationKey",isMany:!0},{name:"choreographyRef",type:"Choreography",isMany:!0,isReference:!0},{name:"conversationLinks",type:"ConversationLink",isMany:!0}]},{name:"ChoreographyActivity",isAbstract:!0,superClass:["FlowNode"],properties:[{name:"participantRef",type:"Participant",isMany:!0,isReference:!0},{name:"initiatingParticipantRef",type:"Participant",isAttr:!0,isReference:!0},{name:"correlationKeys",type:"CorrelationKey",isMany:!0},{name:"loopType",type:"ChoreographyLoopType",default:"None",isAttr:!0}]},{name:"CallChoreography",superClass:["ChoreographyActivity"],properties:[{name:"calledChoreographyRef",type:"Choreography",isAttr:!0,isReference:!0},{name:"participantAssociations",type:"ParticipantAssociation",isMany:!0}]},{name:"SubChoreography",superClass:["ChoreographyActivity","FlowElementsContainer"],properties:[{name:"artifacts",type:"Artifact",isMany:!0}]},{name:"ChoreographyTask",superClass:["ChoreographyActivity"],properties:[{name:"messageFlowRef",type:"MessageFlow",isMany:!0,isReference:!0}]},{name:"Choreography",superClass:["Collaboration","FlowElementsContainer"]},{name:"GlobalChoreographyTask",superClass:["Choreography"],properties:[{name:"initiatingParticipantRef",type:"Participant",isAttr:!0,isReference:!0}]},{name:"TextAnnotation",superClass:["Artifact"],properties:[{name:"text",type:"String"},{name:"textFormat",default:"text/plain",isAttr:!0,type:"String"}]},{name:"Group",superClass:["Artifact"],properties:[{name:"categoryValueRef",type:"CategoryValue",isAttr:!0,isReference:!0}]},{name:"Association",superClass:["Artifact"],properties:[{name:"associationDirection",type:"AssociationDirection",isAttr:!0},{name:"sourceRef",type:"BaseElement",isAttr:!0,isReference:!0},{name:"targetRef",type:"BaseElement",isAttr:!0,isReference:!0}]},{name:"Category",superClass:["RootElement"],properties:[{name:"categoryValue",type:"CategoryValue",isMany:!0},{name:"name",isAttr:!0,type:"String"}]},{name:"Artifact",isAbstract:!0,superClass:["BaseElement"]},{name:"CategoryValue",superClass:["BaseElement"],properties:[{name:"categorizedFlowElements",type:"FlowElement",isMany:!0,isVirtual:!0,isReference:!0},{name:"value",isAttr:!0,type:"String"}]},{name:"Activity",isAbstract:!0,superClass:["FlowNode"],properties:[{name:"isForCompensation",default:!1,isAttr:!0,type:"Boolean"},{name:"default",type:"SequenceFlow",isAttr:!0,isReference:!0},{name:"ioSpecification",type:"InputOutputSpecification",xml:{serialize:"property"}},{name:"boundaryEventRefs",type:"BoundaryEvent",isMany:!0,isReference:!0},{name:"properties",type:"Property",isMany:!0},{name:"dataInputAssociations",type:"DataInputAssociation",isMany:!0},{name:"dataOutputAssociations",type:"DataOutputAssociation",isMany:!0},{name:"startQuantity",default:1,isAttr:!0,type:"Integer"},{name:"resources",type:"ResourceRole",isMany:!0},{name:"completionQuantity",default:1,isAttr:!0,type:"Integer"},{name:"loopCharacteristics",type:"LoopCharacteristics"}]},{name:"ServiceTask",superClass:["Task"],properties:[{name:"implementation",isAttr:!0,type:"String"},{name:"operationRef",type:"Operation",isAttr:!0,isReference:!0}]},{name:"SubProcess",superClass:["Activity","FlowElementsContainer","InteractionNode"],properties:[{name:"triggeredByEvent",default:!1,isAttr:!0,type:"Boolean"},{name:"artifacts",type:"Artifact",isMany:!0}]},{name:"LoopCharacteristics",isAbstract:!0,superClass:["BaseElement"]},{name:"MultiInstanceLoopCharacteristics",superClass:["LoopCharacteristics"],properties:[{name:"isSequential",default:!1,isAttr:!0,type:"Boolean"},{name:"behavior",type:"MultiInstanceBehavior",default:"All",isAttr:!0},{name:"loopCardinality",type:"Expression",xml:{serialize:"xsi:type"}},{name:"loopDataInputRef",type:"ItemAwareElement",isReference:!0},{name:"loopDataOutputRef",type:"ItemAwareElement",isReference:!0},{name:"inputDataItem",type:"DataInput",xml:{serialize:"property"}},{name:"outputDataItem",type:"DataOutput",xml:{serialize:"property"}},{name:"complexBehaviorDefinition",type:"ComplexBehaviorDefinition",isMany:!0},{name:"completionCondition",type:"Expression",xml:{serialize:"xsi:type"}},{name:"oneBehaviorEventRef",type:"EventDefinition",isAttr:!0,isReference:!0},{name:"noneBehaviorEventRef",type:"EventDefinition",isAttr:!0,isReference:!0}]},{name:"StandardLoopCharacteristics",superClass:["LoopCharacteristics"],properties:[{name:"testBefore",default:!1,isAttr:!0,type:"Boolean"},{name:"loopCondition",type:"Expression",xml:{serialize:"xsi:type"}},{name:"loopMaximum",type:"Integer",isAttr:!0}]},{name:"CallActivity",superClass:["Activity","InteractionNode"],properties:[{name:"calledElement",type:"String",isAttr:!0}]},{name:"Task",superClass:["Activity","InteractionNode"]},{name:"SendTask",superClass:["Task"],properties:[{name:"implementation",isAttr:!0,type:"String"},{name:"operationRef",type:"Operation",isAttr:!0,isReference:!0},{name:"messageRef",type:"Message",isAttr:!0,isReference:!0}]},{name:"ReceiveTask",superClass:["Task"],properties:[{name:"implementation",isAttr:!0,type:"String"},{name:"instantiate",default:!1,isAttr:!0,type:"Boolean"},{name:"operationRef",type:"Operation",isAttr:!0,isReference:!0},{name:"messageRef",type:"Message",isAttr:!0,isReference:!0}]},{name:"ScriptTask",superClass:["Task"],properties:[{name:"scriptFormat",isAttr:!0,type:"String"},{name:"script",type:"String"}]},{name:"BusinessRuleTask",superClass:["Task"],properties:[{name:"implementation",isAttr:!0,type:"String"}]},{name:"AdHocSubProcess",superClass:["SubProcess"],properties:[{name:"completionCondition",type:"Expression",xml:{serialize:"xsi:type"}},{name:"ordering",type:"AdHocOrdering",isAttr:!0},{name:"cancelRemainingInstances",default:!0,isAttr:!0,type:"Boolean"}]},{name:"Transaction",superClass:["SubProcess"],properties:[{name:"protocol",isAttr:!0,type:"String"},{name:"method",isAttr:!0,type:"String"}]},{name:"GlobalScriptTask",superClass:["GlobalTask"],properties:[{name:"scriptLanguage",isAttr:!0,type:"String"},{name:"script",isAttr:!0,type:"String"}]},{name:"GlobalBusinessRuleTask",superClass:["GlobalTask"],properties:[{name:"implementation",isAttr:!0,type:"String"}]},{name:"ComplexBehaviorDefinition",superClass:["BaseElement"],properties:[{name:"condition",type:"FormalExpression"},{name:"event",type:"ImplicitThrowEvent"}]},{name:"ResourceRole",superClass:["BaseElement"],properties:[{name:"resourceRef",type:"Resource",isReference:!0},{name:"resourceParameterBindings",type:"ResourceParameterBinding",isMany:!0},{name:"resourceAssignmentExpression",type:"ResourceAssignmentExpression"},{name:"name",isAttr:!0,type:"String"}]},{name:"ResourceParameterBinding",properties:[{name:"expression",type:"Expression",xml:{serialize:"xsi:type"}},{name:"parameterRef",type:"ResourceParameter",isAttr:!0,isReference:!0}],superClass:["BaseElement"]},{name:"ResourceAssignmentExpression",properties:[{name:"expression",type:"Expression",xml:{serialize:"xsi:type"}}],superClass:["BaseElement"]},{name:"Import",properties:[{name:"importType",isAttr:!0,type:"String"},{name:"location",isAttr:!0,type:"String"},{name:"namespace",isAttr:!0,type:"String"}]},{name:"Definitions",superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"targetNamespace",isAttr:!0,type:"String"},{name:"expressionLanguage",default:"http://www.w3.org/1999/XPath",isAttr:!0,type:"String"},{name:"typeLanguage",default:"http://www.w3.org/2001/XMLSchema",isAttr:!0,type:"String"},{name:"imports",type:"Import",isMany:!0},{name:"extensions",type:"Extension",isMany:!0},{name:"rootElements",type:"RootElement",isMany:!0},{name:"diagrams",isMany:!0,type:"bpmndi:BPMNDiagram"},{name:"exporter",isAttr:!0,type:"String"},{name:"relationships",type:"Relationship",isMany:!0},{name:"exporterVersion",isAttr:!0,type:"String"}]}],enumerations:[{name:"ProcessType",literalValues:[{name:"None"},{name:"Public"},{name:"Private"}]},{name:"GatewayDirection",literalValues:[{name:"Unspecified"},{name:"Converging"},{name:"Diverging"},{name:"Mixed"}]},{name:"EventBasedGatewayType",literalValues:[{name:"Parallel"},{name:"Exclusive"}]},{name:"RelationshipDirection",literalValues:[{name:"None"},{name:"Forward"},{name:"Backward"},{name:"Both"}]},{name:"ItemKind",literalValues:[{name:"Physical"},{name:"Information"}]},{name:"ChoreographyLoopType",literalValues:[{name:"None"},{name:"Standard"},{name:"MultiInstanceSequential"},{name:"MultiInstanceParallel"}]},{name:"AssociationDirection",literalValues:[{name:"None"},{name:"One"},{name:"Both"}]},{name:"MultiInstanceBehavior",literalValues:[{name:"None"},{name:"One"},{name:"All"},{name:"Complex"}]},{name:"AdHocOrdering",literalValues:[{name:"Parallel"},{name:"Sequential"}]}],xml:{tagAlias:"lowerCase",typePrefix:"t"}},bpmndi:{name:"BPMNDI",uri:"http://www.omg.org/spec/BPMN/20100524/DI",prefix:"bpmndi",types:[{name:"BPMNDiagram",properties:[{name:"plane",type:"BPMNPlane",redefines:"di:Diagram#rootElement"},{name:"labelStyle",type:"BPMNLabelStyle",isMany:!0}],superClass:["di:Diagram"]},{name:"BPMNPlane",properties:[{name:"bpmnElement",isAttr:!0,isReference:!0,type:"bpmn:BaseElement",redefines:"di:DiagramElement#modelElement"}],superClass:["di:Plane"]},{name:"BPMNShape",properties:[{name:"bpmnElement",isAttr:!0,isReference:!0,type:"bpmn:BaseElement",redefines:"di:DiagramElement#modelElement"},{name:"isHorizontal",isAttr:!0,type:"Boolean"},{name:"isExpanded",isAttr:!0,type:"Boolean"},{name:"isMarkerVisible",isAttr:!0,type:"Boolean"},{name:"label",type:"BPMNLabel"},{name:"isMessageVisible",isAttr:!0,type:"Boolean"},{name:"participantBandKind",type:"ParticipantBandKind",isAttr:!0},{name:"choreographyActivityShape",type:"BPMNShape",isAttr:!0,isReference:!0}],superClass:["di:LabeledShape"]},{name:"BPMNEdge",properties:[{name:"label",type:"BPMNLabel"},{name:"bpmnElement",isAttr:!0,isReference:!0,type:"bpmn:BaseElement",redefines:"di:DiagramElement#modelElement"},{name:"sourceElement",isAttr:!0,isReference:!0,type:"di:DiagramElement",redefines:"di:Edge#source"},{name:"targetElement",isAttr:!0,isReference:!0,type:"di:DiagramElement",redefines:"di:Edge#target"},{name:"messageVisibleKind",type:"MessageVisibleKind",isAttr:!0,default:"initiating"}],superClass:["di:LabeledEdge"]},{name:"BPMNLabel",properties:[{name:"labelStyle",type:"BPMNLabelStyle",isAttr:!0,isReference:!0,redefines:"di:DiagramElement#style"}],superClass:["di:Label"]},{name:"BPMNLabelStyle",properties:[{name:"font",type:"dc:Font"}],superClass:["di:Style"]}],enumerations:[{name:"ParticipantBandKind",literalValues:[{name:"top_initiating"},{name:"middle_initiating"},{name:"bottom_initiating"},{name:"top_non_initiating"},{name:"middle_non_initiating"},{name:"bottom_non_initiating"}]},{name:"MessageVisibleKind",literalValues:[{name:"initiating"},{name:"non_initiating"}]}],associations:[]},dc:{name:"DC",uri:"http://www.omg.org/spec/DD/20100524/DC",prefix:"dc",types:[{name:"Boolean"},{name:"Integer"},{name:"Real"},{name:"String"},{name:"Font",properties:[{name:"name",type:"String",isAttr:!0},{name:"size",type:"Real",isAttr:!0},{name:"isBold",type:"Boolean",isAttr:!0},{name:"isItalic",type:"Boolean",isAttr:!0},{name:"isUnderline",type:"Boolean",isAttr:!0},{name:"isStrikeThrough",type:"Boolean",isAttr:!0}]},{name:"Point",properties:[{name:"x",type:"Real",default:"0",isAttr:!0},{name:"y",type:"Real",default:"0",isAttr:!0}]},{name:"Bounds",properties:[{name:"x",type:"Real",default:"0",isAttr:!0},{name:"y",type:"Real",default:"0",isAttr:!0},{name:"width",type:"Real",isAttr:!0},{name:"height",type:"Real",isAttr:!0}]}],associations:[]},di:{name:"DI",uri:"http://www.omg.org/spec/DD/20100524/DI",prefix:"di",types:[{name:"DiagramElement",isAbstract:!0,properties:[{name:"id",isAttr:!0,isId:!0,type:"String"},{name:"extension",type:"Extension"},{name:"owningDiagram",type:"Diagram",isReadOnly:!0,isVirtual:!0,isReference:!0},{name:"owningElement",type:"DiagramElement",isReadOnly:!0,isVirtual:!0,isReference:!0},{name:"modelElement",isReadOnly:!0,isVirtual:!0,isReference:!0,type:"Element"},{name:"style",type:"Style",isReadOnly:!0,isVirtual:!0,isReference:!0},{name:"ownedElement",type:"DiagramElement",isReadOnly:!0,isMany:!0,isVirtual:!0}]},{name:"Node",isAbstract:!0,superClass:["DiagramElement"]},{name:"Edge",isAbstract:!0,superClass:["DiagramElement"],properties:[{name:"source",type:"DiagramElement",isReadOnly:!0,isVirtual:!0,isReference:!0},{name:"target",type:"DiagramElement",isReadOnly:!0,isVirtual:!0,isReference:!0},{name:"waypoint",isUnique:!1,isMany:!0,type:"dc:Point",xml:{serialize:"xsi:type"}}]},{name:"Diagram",isAbstract:!0,properties:[{name:"id",isAttr:!0,isId:!0,type:"String"},{name:"rootElement",type:"DiagramElement",isReadOnly:!0,isVirtual:!0},{name:"name",isAttr:!0,type:"String"},{name:"documentation",isAttr:!0,type:"String"},{name:"resolution",isAttr:!0,type:"Real"},{name:"ownedStyle",type:"Style",isReadOnly:!0,isMany:!0,isVirtual:!0}]},{name:"Shape",isAbstract:!0,superClass:["Node"],properties:[{name:"bounds",type:"dc:Bounds"}]},{name:"Plane",isAbstract:!0,superClass:["Node"],properties:[{name:"planeElement",type:"DiagramElement",subsettedProperty:"DiagramElement-ownedElement",isMany:!0}]},{name:"LabeledEdge",isAbstract:!0,superClass:["Edge"],properties:[{name:"ownedLabel",type:"Label",isReadOnly:!0,subsettedProperty:"DiagramElement-ownedElement",isMany:!0,isVirtual:!0}]},{name:"LabeledShape",isAbstract:!0,superClass:["Shape"],properties:[{name:"ownedLabel",type:"Label",isReadOnly:!0,subsettedProperty:"DiagramElement-ownedElement",isMany:!0,isVirtual:!0}]},{name:"Label",isAbstract:!0,superClass:["Node"],properties:[{name:"bounds",type:"dc:Bounds"}]},{name:"Style",isAbstract:!0,properties:[{name:"id",isAttr:!0,isId:!0,type:"String"}]},{name:"Extension",properties:[{name:"values",isMany:!0,type:"Element"}]}],associations:[],xml:{tagAlias:"lowerCase"}},bioc:{name:"bpmn.io colors for BPMN",uri:"http://bpmn.io/schema/bpmn/biocolor/1.0",prefix:"bioc",types:[{name:"ColoredShape",extends:["bpmndi:BPMNShape"],properties:[{name:"stroke",isAttr:!0,type:"String"},{name:"fill",isAttr:!0,type:"String"}]},{name:"ColoredEdge",extends:["bpmndi:BPMNEdge"],properties:[{name:"stroke",isAttr:!0,type:"String"},{name:"fill",isAttr:!0,type:"String"}]}],enumerations:[],associations:[]},color:{name:"BPMN in Color",uri:"http://www.omg.org/spec/BPMN/non-normative/color/1.0",prefix:"color",types:[{name:"ColoredLabel",extends:["bpmndi:BPMNLabel"],properties:[{name:"color",isAttr:!0,type:"String"}]},{name:"ColoredShape",extends:["bpmndi:BPMNShape"],properties:[{name:"background-color",isAttr:!0,type:"String"},{name:"border-color",isAttr:!0,type:"String"}]},{name:"ColoredEdge",extends:["bpmndi:BPMNEdge"],properties:[{name:"border-color",isAttr:!0,type:"String"}]}],enumerations:[],associations:[]}};function _r(e,t){return new wr(E({},Er,e),t)}function Ar(e){return function(){if(!window.Promise)throw new Error("Promises is not supported in this environment. Please polyfill Promise.");var t=arguments.length;if(!(t>=1&&s(arguments[t-1])))return e.apply(this,arguments);var n=arguments[t-1];console.warn(new Error("Passing callbacks to "+e.name+" is deprecated and will be removed in a future major release. Please switch to promises: https://bpmn.io/l/moving-to-promises.html"));var i=Array.prototype.slice.call(arguments,0,-1);e.apply(this,i).then((function(e){var t=Object.keys(e)[0];return n(null,e[t])}),(function(e){return n(e,e.warnings)}))}}function Sr(e,t){return e.$instanceOf(t)}function Rr(e,t){var n={},i=[],r={};function o(e,t){return function(n){e(n,t)}}function a(e){n[e.id]=e}function s(n,i){try{var o=r[n.id]&&function(n,i){if(n.gfx)throw new Error(t("already rendered {element}",{element:gt(n)}));return e.element(n,r[n.id],i)}(n,i);return a(n),o}catch(e){l(e.message,{element:n,error:e}),console.error(t("failed to import {element}",{element:gt(n)})),console.error(e)}}function l(t,n){e.error(t,n)}function m(e){var n,i=e.bpmnElement;i?r[i.id]?l(t("multiple DI elements defined for {element}",{element:gt(i)}),{element:i}):(r[i.id]=e,p(n=i,"di")||Object.defineProperty(n,"di",{enumerable:!1,get:function(){throw new Error("Tried to access di from the businessObject. The di is available through the diagram element only. For more information, see https://github.com/bpmn-io/bpmn-js/issues/1472")}})):l(t("no bpmnElement referenced in {element}",{element:gt(e)}),{element:e})}function f(e){var t;m(t=e.plane),h(t.planeElement,d)}function d(e){m(e)}function y(){for(;i.length;)i.shift()()}function g(e,t){P(e,t),A(e.ioSpecification,t),_(e.artifacts,t),a(e)}function v(e,t){s(e,t)}function b(e,t){s(e,t)}function x(e,t){s(e,t)}function w(e,t){s(e,t)}function E(e,t){s(e,t)}function _(e,t){h(e,(function(e){Sr(e,"bpmn:Association")?i.push((function(){E(e,t)})):E(e,t)}))}function A(e,t){e&&(h(e.dataInputs,o(x,t)),h(e.dataOutputs,o(w,t)))}function S(e,t){P(e,t),_(e.artifacts,t)}function R(e,t){var n=s(e,t);Sr(e,"bpmn:SubProcess")&&S(e,n||t),Sr(e,"bpmn:Activity")&&A(e.ioSpecification,t),i.push((function(){h(e.dataInputAssociations,o(b,t)),h(e.dataOutputAssociations,o(b,t))}))}function C(e,t){s(e,t)}function M(e,t){i.push((function(){var n=s(e,t);e.childLaneSet&&k(e.childLaneSet,n||t),function(e){h(e.flowNodeRef,(function(t){var n=t.get("lanes");n&&n.push(e)}))}(e)}))}function k(e,t){h(e.lanes,o(M,t))}function P(e,n){!function(e,n){h(e,(function(e){Sr(e,"bpmn:SequenceFlow")?i.push((function(){!function(e,t){s(e,t)}(e,n)})):Sr(e,"bpmn:BoundaryEvent")?i.unshift((function(){R(e,n)})):Sr(e,"bpmn:FlowNode")?R(e,n):Sr(e,"bpmn:DataObject")||(Sr(e,"bpmn:DataStoreReference")||Sr(e,"bpmn:DataObjectReference")?C(e,n):l(t("unrecognized flowElement {element} in context {context}",{element:gt(e),context:n?gt(n.businessObject):"null"}),{element:e,context:n}))}))}(e.flowElements,n),e.laneSets&&function(e,t){h(e,o(k,t))}(e.laneSets,n)}function D(e,t){var n=s(e,t),i=e.processRef;i&&g(i,n||t)}return{handleDeferred:y,handleDefinitions:function(a,s){var p=a.diagrams;if(s&&-1===p.indexOf(s))throw new Error(t("diagram not part of bpmn:Definitions"));if(!s&&p&&p.length&&(s=p[0]),!s)throw new Error(t("no diagram to display"));r={},f(s);var d=s.plane;if(!d)throw new Error(t("no plane for {element}",{element:gt(s)}));var b=d.bpmnElement;if(!b){if(b=function(e){return c(e.rootElements,(function(e){return Sr(e,"bpmn:Process")||Sr(e,"bpmn:Collaboration")}))}(a),!b)throw new Error(t("no process or collaboration to display"));l(t("correcting missing bpmnElement on {plane} to {rootElement}",{plane:gt(d),rootElement:gt(b)})),d.bpmnElement=b,m(d)}var x,w,E=function(t,n){return e.root(t,r[t.id],n)}(b,d);if(Sr(b,"bpmn:Process")||Sr(b,"bpmn:SubProcess"))g(b,E);else{if(!Sr(b,"bpmn:Collaboration"))throw new Error(t("unsupported bpmnElement for {plane}: {rootElement}",{plane:gt(d),rootElement:gt(b)}));w=E,h((x=b).participants,o(D,w)),_(x.artifacts,w),i.push((function(){!function(e,t){h(e,o(v,t))}(x.messageFlows,w)})),function(e,t){var i=u(e,(function(e){return!n[e.id]&&Sr(e,"bpmn:Process")&&e.laneSets}));i.forEach(o(g,t))}(a.rootElements,E)}y()},handleSubProcess:S,registerDi:m}}function Cr(e,t,n){var i,r,o,a,s=[];function l(e,t){var n=new Rr({root:function(e,t){return i.add(e,t)},element:function(e,t,n){return i.add(e,t,n)},error:function(e,t){s.push({message:e,context:t})}},o);t=t||e.diagrams&&e.diagrams[0];var r=function(e,t){if(!t)return;var n,i=t.plane.bpmnElement,r=i;A(i,"bpmn:Process")||A(i,"bpmn:Collaboration")||(r=function(e){var t=e;for(;t;){if(A(t,"bpmn:Process"))return t;t=t.$parent}} +/** + * This file must not be changed or exchanged. + * + * @see http://bpmn.io/license for more information. + */(i));n=A(r,"bpmn:Collaboration")?r:c(e.rootElements,(function(e){if(A(e,"bpmn:Collaboration"))return c(e.participants,(function(e){return e.processRef===r}))}));var o=[r];n&&(o=function(e,t){var n=[];return h(e,(function(e,i){n.push(t(e,i))})),n}(n.participants,(function(e){return e.processRef})),o.push(n));var a=Mr(o),s=[t],l=[i];return h(e.diagrams,(function(e){var t=e.plane.bpmnElement;-1!==a.indexOf(t)&&-1===l.indexOf(t)&&(s.push(e),l.push(t))})),s}(e,t);if(!r)throw new Error(o("no diagram to display"));h(r,(function(t){n.handleDefinitions(e,t)}));var l=t.plane.bpmnElement.id;a.setRootElement(a.findRoot(l+"_plane")||a.findRoot(l))}return new Promise((function(p,c){try{return i=e.get("bpmnImporter"),r=e.get("eventBus"),o=e.get("translate"),a=e.get("canvas"),r.fire("import.render.start",{definitions:t}),l(t,n),r.fire("import.render.complete",{error:undefined,warnings:s}),p({warnings:s})}catch(e){return e.warnings=s,c(e)}}))}function Mr(e){var t=[];return h(e,(function(e){e&&(t.push(e),t=t.concat(Mr(e.flowElements)))})),t}var kr,Pr='',Dr={verticalAlign:"middle"},Tr={color:"#404040"},Nr={zIndex:"1001",position:"fixed",top:"0",left:"0",right:"0",bottom:"0"},Or={width:"100%",height:"100%",background:"rgba(40,40,40,0.2)"},Br={position:"absolute",left:"50%",top:"40%",transform:"translate(-50%)",width:"260px",padding:"10px",background:"white",boxShadow:"0 1px 4px rgba(0,0,0,0.3)",fontFamily:"Helvetica, Arial, sans-serif",fontSize:"14px",display:"flex",lineHeight:"1.3"};function Lr(){kr||(xe(kr=Fe('
          Web-based tooling for BPMN, DMN and CMMN diagrams powered by bpmn.io.
          '),Nr),xe(We("svg",kr),Dr),xe(We(".backdrop",kr),Or),xe(We(".notice",kr),Br),xe(We(".link",kr),Tr,{margin:"15px 20px 15px 10px",alignSelf:"center"}),je.bind(kr,".backdrop","click",(function(e){document.body.removeChild(kr)}))),document.body.appendChild(kr)} +/** + * The code in the area + * must not be changed. + * + * @see http://bpmn.io/license for more information. + */function Ir(e){ +/** + * Adds the project logo to the diagram container as + * required by the bpmn.io license. + * + * @see http://bpmn.io/license + * + * @param {Element} container + */ +var t,n;e=E({},Fr,e),this._moddle=this._createModdle(e),this._container=this._createContainer(e),t=this._container,xe(We("svg",n=Fe(''+Pr+"")),Dr),xe(n,Tr,{position:"absolute",bottom:"15px",right:"15px",zIndex:"100"}),t.appendChild(n),Be.bind(n,"click",(function(e){Lr(),e.preventDefault()})),this._init(this._container,this._moddle,e)}function jr(e,t){return e.warnings=t,e}e(Ir,li),Ir.prototype.importXML=Ar((function(e,t){var n=this;return new Promise((function(i,r){e=n._emit("import.parse.start",{xml:e})||e,n._moddle.fromXML(e,"bpmn:Definitions").then((function(e){var o,a,s=e.rootElement,l=e.references,p=e.warnings,c=e.elementsById;s=n._emit("import.parse.complete",(o={error:null,definitions:s,elementsById:c,references:l,warnings:p},a=n.get("eventBus").createEvent(o),Object.defineProperty(a,"context",{enumerable:!0,get:function(){return console.warn(new Error("import.parse.complete is deprecated and will be removed in future library versions")),{warnings:o.warnings,references:o.references,elementsById:o.elementsById}}}),a))||s,n.importDefinitions(s,t).then((function(e){var t=[].concat(p,e.warnings||[]);return n._emit("import.done",{error:null,warnings:t}),i({warnings:t})})).catch((function(e){var t=[].concat(p,e.warnings||[]);return n._emit("import.done",{error:e,warnings:t}),r(jr(e,t))}))})).catch((function(e){return n._emit("import.parse.complete",{error:e}),e=function(e){var t=/unparsable content <([^>]+)> detected([\s\S]*)$/.exec(e.message);t&&(e.message="unparsable content <"+t[1]+"> detected; this may indicate an invalid BPMN 2.0 diagram file"+t[2]);return e}(e),n._emit("import.done",{error:e,warnings:e.warnings}),r(e)}))}))})),Ir.prototype.importDefinitions=Ar((function(e,t){var n=this;return new Promise((function(i,r){n._setDefinitions(e),n.open(t).then((function(e){var t=e.warnings;return i({warnings:t})})).catch((function(e){return r(e)}))}))})),Ir.prototype.open=Ar((function(e){var t=this._definitions,n=e,i=this;return new Promise((function(r,o){if(!t){var a=new Error("no XML imported");return o(jr(a,[]))}if("string"==typeof e&&(n=function(e,t){if(!t)return null;return c(e.diagrams,(function(e){return e.id===t}))||null}(t,e),!n)){var s=new Error("BPMNDiagram <"+e+"> not found");return o(jr(s,[]))}try{i.clear()}catch(e){return o(jr(e,[]))}Cr(i,t,n).then((function(e){var t=e.warnings;return r({warnings:t})})).catch((function(e){return o(e)}))}))})),Ir.prototype.saveXML=Ar((function(e){e=e||{};var t=this,n=this._definitions;return new Promise((function(i){if(!n)return i({error:new Error("no definitions loaded")});n=t._emit("saveXML.start",{definitions:n})||n,t._moddle.toXML(n,e).then((function(e){var n=e.xml;return n=t._emit("saveXML.serialized",{xml:n})||n,i({xml:n})}))})).catch((function(e){return{error:e}})).then((function(e){t._emit("saveXML.done",e);var n=e.error;return n?Promise.reject(n):e}))})),Ir.prototype.saveSVG=Ar((function(e){var t=this;return new Promise((function(e,n){var i,r;t._emit("saveSVG.start");try{var o=t.get("canvas"),a=o.getActiveLayer(),s=We("defs",o._svg),l=ne(a),p=s?""+ne(s)+"":"",c=a.getBBox();i='\n\x3c!-- created with bpmn-js / http://bpmn.io --\x3e\n\n'+p+l+""}catch(e){r=e}return t._emit("saveSVG.done",{error:r,svg:i}),r?n(r):e({svg:i})}))})),Ir.prototype._setDefinitions=function(e){this._definitions=e},Ir.prototype.getModules=function(){return this._modules},Ir.prototype.clear=function(){this.getDefinitions()&&li.prototype.clear.call(this)},Ir.prototype.destroy=function(){li.prototype.destroy.call(this),Ge(this._container)},Ir.prototype.on=function(e,t,n,i){return this.get("eventBus").on(e,t,n,i)},Ir.prototype.off=function(e,t){this.get("eventBus").off(e,t)},Ir.prototype.attachTo=function(e){if(!e)throw new Error("parentNode required");this.detach(),e.get&&e.constructor.prototype.jquery&&(e=e.get(0)),"string"==typeof e&&(e=We(e)),e.appendChild(this._container),this._emit("attach",{}),this.get("canvas").resized()},Ir.prototype.getDefinitions=function(){return this._definitions},Ir.prototype.detach=function(){var e=this._container,t=e.parentNode;t&&(this._emit("detach",{}),t.removeChild(e))},Ir.prototype._init=function(e,t,n){var i,r,o=n.modules||this.getModules(),a=n.additionalModules||[],s=[].concat([{bpmnjs:["value",this],moddle:["value",t]}],o,a),l=E((i=["additionalModules"],r={},h(Object(n),(function(e,t){-1===i.indexOf(t)&&(r[t]=e)})),r),{canvas:E({},n.canvas,{container:e}),modules:s});li.call(this,l),n&&n.container&&this.attachTo(n.container)},Ir.prototype._emit=function(e,t){return this.get("eventBus").fire(e,t)},Ir.prototype._createContainer=function(e){var t=Fe('
          ');return xe(t,{width:Vr(e.width),height:Vr(e.height),position:e.position}),t},Ir.prototype._createModdle=function(e){return new _r(E({},this._moddleExtensions,e.moddleExtensions))},Ir.prototype._modules=[];var Fr={width:"100%",height:"100%",position:"relative"};function Vr(e){return e+(a(e)?"px":"")}function zr(e){Ir.call(this,e)}e(zr,Ir),zr.prototype._modules=[Et,st,Gt,Xt,dn],zr.prototype._moddleExtensions={};var Wr=["c","C",67],Gr=["v","V",86],$r=["y","Y",89],Hr=["z","Z",90];function Kr(e){return!e.altKey&&(e.ctrlKey||e.metaKey)}function Ur(e,t){return-1!==(e=r(e)?e:[e]).indexOf(t.key)||-1!==e.indexOf(t.keyCode)}function qr(e){return e.shiftKey}var Yr="keyboard.keydown",Xr="input-handle-modified-keys";function Zr(e,t){var n=this;this._config=e||{},this._eventBus=t,this._keydownHandler=this._keydownHandler.bind(this),this._keyupHandler=this._keyupHandler.bind(this),t.on("diagram.destroy",(function(){n._fire("destroy"),n.unbind()})),t.on("diagram.init",(function(){n._fire("init")})),t.on("attach",(function(){e&&e.bindTo&&n.bind(e.bindTo)})),t.on("detach",(function(){n.unbind()}))}Zr.$inject=["config.keyboard","eventBus"],Zr.prototype._keydownHandler=function(e){this._keyHandler(e,Yr)},Zr.prototype._keyupHandler=function(e){this._keyHandler(e,"keyboard.keyup")},Zr.prototype._keyHandler=function(e,t){if(!this._isEventIgnored(e)){var n={keyEvent:e};this._eventBus.fire(t||Yr,n)&&e.preventDefault()}},Zr.prototype._isEventIgnored=function(e){return(t=e.target)&&(Pe(t,"input, textarea")||"true"===t.contentEditable)&&this._isModifiedKeyIgnored(e);var t},Zr.prototype._isModifiedKeyIgnored=function(e){return!Kr(e)||-1===this._getAllowedModifiers(e.target).indexOf(e.key)},Zr.prototype._getAllowedModifiers=function(e){var t=De(e,"[input-handle-modified-keys]",!0);return!t||this._node&&!this._node.contains(t)?[]:t.getAttribute(Xr).split(",")},Zr.prototype.bind=function(e){this.unbind(),this._node=e,Be.bind(e,"keydown",this._keydownHandler,!0),Be.bind(e,"keyup",this._keyupHandler,!0),this._fire("bind")},Zr.prototype.getBinding=function(){return this._node},Zr.prototype.unbind=function(){var e=this._node;e&&(this._fire("unbind"),Be.unbind(e,"keydown",this._keydownHandler,!0),Be.unbind(e,"keyup",this._keyupHandler,!0)),this._node=null},Zr.prototype._fire=function(e){this._eventBus.fire("keyboard."+e,{node:this._node})},Zr.prototype.addListener=function(e,t,n){s(e)&&(n=t,t=e,e=1e3),this._eventBus.on(n||Yr,e,t)},Zr.prototype.removeListener=function(e,t){this._eventBus.off(t||Yr,e)},Zr.prototype.hasModifier=function(e){return e.ctrlKey||e.metaKey||e.shiftKey||e.altKey},Zr.prototype.isCmd=Kr,Zr.prototype.isShift=qr,Zr.prototype.isKey=Ur;function Jr(e,t){var n=this;e.on("editorActions.init",500,(function(e){var i=e.editorActions;n.registerBindings(t,i)}))}Jr.$inject=["eventBus","keyboard"],Jr.prototype.registerBindings=function(e,t){function n(n,i){t.isRegistered(n)&&e.addListener(i)}n("undo",(function(e){if(function(e){return Kr(e)&&!qr(e)&&Ur(Hr,e)}(e.keyEvent))return t.trigger("undo"),!0})),n("redo",(function(e){if(function(e){return Kr(e)&&(Ur($r,e)||Ur(Hr,e)&&qr(e))}(e.keyEvent))return t.trigger("redo"),!0})),n("copy",(function(e){if(function(e){return Kr(e)&&Ur(Wr,e)}(e.keyEvent))return t.trigger("copy"),!0})),n("paste",(function(e){if(function(e){return Kr(e)&&Ur(Gr,e)}(e.keyEvent))return t.trigger("paste"),!0})),n("stepZoom",(function(e){var n=e.keyEvent;if(Ur(["+","Add","="],n)&&Kr(n))return t.trigger("stepZoom",{value:1}),!0})),n("stepZoom",(function(e){var n=e.keyEvent;if(Ur(["-","Subtract"],n)&&Kr(n))return t.trigger("stepZoom",{value:-1}),!0})),n("zoom",(function(e){var n=e.keyEvent;if(Ur("0",n)&&Kr(n))return t.trigger("zoom",{value:1}),!0})),n("removeSelection",(function(e){if(Ur(["Backspace","Delete","Del"],e.keyEvent))return t.trigger("removeSelection"),!0}))};var Qr={__init__:["keyboard","keyboardBindings"],keyboard:["type",Zr],keyboardBindings:["type",Jr]},eo={moveSpeed:50,moveSpeedAccelerated:200};function to(e,t,n){var i=this;this._config=E({},eo,e||{}),t.addListener((function(e){var n=e.keyEvent,r=i._config;if(!t.isCmd(n))return;if(t.isKey(["ArrowLeft","Left","ArrowUp","Up","ArrowDown","Down","ArrowRight","Right"],n)){var o,a=t.isShift(n)?r.moveSpeedAccelerated:r.moveSpeed;switch(n.key){case"ArrowLeft":case"Left":o="left";break;case"ArrowUp":case"Up":o="up";break;case"ArrowRight":case"Right":o="right";break;case"ArrowDown":case"Down":o="down"}return i.moveCanvas({speed:a,direction:o}),!0}})),this.moveCanvas=function(e){var t=0,i=0,r=e.speed/Math.min(Math.sqrt(n.viewbox().scale),1);switch(e.direction){case"left":t=r;break;case"up":i=r;break;case"right":t=-r;break;case"down":i=-r}n.scroll({dx:t,dy:i})}}to.$inject=["config.keyboardMove","keyboard","canvas"];var no={__depends__:[Qr],__init__:["keyboardMove"],keyboardMove:["type",to]},io=/^djs-cursor-.*$/;function ro(e){var t=Se(document.body);t.removeMatching(io),e&&t.add("djs-cursor-"+e)}function oo(e,t){return{x:e.x-t.x,y:e.y-t.y}}function ao(e,t){var n;function i(i){var r,o=n.start,a=n.button,s=At(i),l=oo(s,o);(!n.dragging&&(r=l,Math.sqrt(Math.pow(r.x,2)+Math.pow(r.y,2))>15)&&(n.dragging=!0,0===a&&function(e,t){function n(){return!1}t=t||"element.click",e.once(t,5e3,n)}(e),ro("grab")),n.dragging)&&(l=oo(s,n.last||n.start),t.scroll({dx:l.x,dy:l.y}),n.last=s);i.preventDefault()}function r(e){Be.unbind(document,"mousemove",i),Be.unbind(document,"mouseup",r),n=null,ro(null)}e.on("element.mousedown",500,(function(e){return function(e){if(De(e.target,".djs-draggable"))return;var t=e.button;if(t>=2||e.ctrlKey||e.shiftKey||e.altKey)return;return n={button:t,start:At(e)},Be.bind(document,"mousemove",i),Be.bind(document,"mouseup",r),!0}(e.originalEvent)})),this.isActive=function(){return!!n}}ao.$inject=["eventBus","canvas"];var so={__init__:["moveCanvas"],moveCanvas:["type",ao]};function lo(e){return Math.log(e)/Math.log(10)}function po(e,t){var n=lo(e.min),i=lo(e.max);return(Math.abs(n)+Math.abs(i))/t}var co=Math.sign||function(e){return e>=0?1:-1},uo={min:.2,max:4};function ho(e,t,n){e=e||{},this._enabled=!1,this._canvas=n,this._container=n._container,this._handleWheel=x(this._handleWheel,this),this._totalDelta=0,this._scale=e.scale||.75;var i=this;t.on("canvas.init",(function(t){i._init(!1!==e.enabled)}))}ho.$inject=["config.zoomScroll","eventBus","canvas"],ho.prototype.scroll=function(e){this._canvas.scroll(e)},ho.prototype.reset=function(){this._canvas.zoom("fit-viewport")},ho.prototype.zoom=function(e,t){var n=po(uo,20);this._totalDelta+=e,Math.abs(this._totalDelta)>.1&&(this._zoom(e,t,n),this._totalDelta=0)},ho.prototype._handleWheel=function(e){if(!De(e.target,".djs-scrollable",!0)){var t=this._container;e.preventDefault();var n,i=e.ctrlKey,r=e.shiftKey,o=-1*this._scale;if(o*=i?0===e.deltaMode?.02:.32:0===e.deltaMode?1:16,i){var a=t.getBoundingClientRect(),s={x:e.clientX-a.left,y:e.clientY-a.top};n=Math.sqrt(Math.pow(e.deltaY,2)+Math.pow(e.deltaX,2))*co(e.deltaY)*o,this.zoom(n,s)}else n=r?{dx:o*e.deltaY,dy:0}:{dx:o*e.deltaX,dy:o*e.deltaY},this.scroll(n)}},ho.prototype.stepZoom=function(e,t){var n=po(uo,10);this._zoom(e,t,n)},ho.prototype._zoom=function(e,t,n){var i=this._canvas,r=e>0?1:-1,o=lo(i.zoom()),a=Math.round(o/n)*n;a+=n*r;var s,l,p=Math.pow(10,a);i.zoom((s=uo,l=p,Math.max(s.min,Math.min(s.max,l))),t)},ho.prototype.toggle=function(e){var t=this._container,n=this._handleWheel,i=this._enabled;return void 0===e&&(e=!i),i!==e&&Be[e?"bind":"unbind"](t,"wheel",n,!1),this._enabled=e,e},ho.prototype._init=function(e){this.toggle(e)};var mo={__init__:["zoomScroll"],zoomScroll:["type",ho]};function fo(e){zr.call(this,e)}return e(fo,zr),fo.prototype._navigationModules=[no,so,mo],fo.prototype._modules=[].concat(zr.prototype._modules,fo.prototype._navigationModules),fo})); diff --git a/dist/bpmn-viewer.development.js b/dist/bpmn-viewer.development.js new file mode 100644 index 0000000..36a08ec --- /dev/null +++ b/dist/bpmn-viewer.development.js @@ -0,0 +1,21654 @@ +/*! + * bpmn-js - bpmn-viewer v9.4.0 + * + * Copyright (c) 2014-present, camunda Services GmbH + * + * Released under the bpmn.io license + * http://bpmn.io/license + * + * Source Code: https://github.com/bpmn-io/bpmn-js + * + * Date: 2022-08-22 + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.BpmnJS = factory()); +})(this, (function () { 'use strict'; + + function e(e,t){t&&(e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}));} + + /** + * Flatten array, one level deep. + * + * @param {Array} arr + * + * @return {Array} + */ + + var nativeToString$1 = Object.prototype.toString; + var nativeHasOwnProperty$1 = Object.prototype.hasOwnProperty; + function isUndefined$2(obj) { + return obj === undefined; + } + function isDefined(obj) { + return obj !== undefined; + } + function isArray$2(obj) { + return nativeToString$1.call(obj) === '[object Array]'; + } + function isObject(obj) { + return nativeToString$1.call(obj) === '[object Object]'; + } + function isNumber(obj) { + return nativeToString$1.call(obj) === '[object Number]'; + } + function isFunction(obj) { + var tag = nativeToString$1.call(obj); + return tag === '[object Function]' || tag === '[object AsyncFunction]' || tag === '[object GeneratorFunction]' || tag === '[object AsyncGeneratorFunction]' || tag === '[object Proxy]'; + } + function isString(obj) { + return nativeToString$1.call(obj) === '[object String]'; + } + /** + * Return true, if target owns a property with the given key. + * + * @param {Object} target + * @param {String} key + * + * @return {Boolean} + */ + + function has$1(target, key) { + return nativeHasOwnProperty$1.call(target, key); + } + + /** + * Find element in collection. + * + * @param {Array|Object} collection + * @param {Function|Object} matcher + * + * @return {Object} + */ + + function find(collection, matcher) { + matcher = toMatcher(matcher); + var match; + forEach$1(collection, function (val, key) { + if (matcher(val, key)) { + match = val; + return false; + } + }); + return match; + } + /** + * Find element index in collection. + * + * @param {Array|Object} collection + * @param {Function} matcher + * + * @return {Object} + */ + + function findIndex(collection, matcher) { + matcher = toMatcher(matcher); + var idx = isArray$2(collection) ? -1 : undefined; + forEach$1(collection, function (val, key) { + if (matcher(val, key)) { + idx = key; + return false; + } + }); + return idx; + } + /** + * Find element in collection. + * + * @param {Array|Object} collection + * @param {Function} matcher + * + * @return {Array} result + */ + + function filter(collection, matcher) { + var result = []; + forEach$1(collection, function (val, key) { + if (matcher(val, key)) { + result.push(val); + } + }); + return result; + } + /** + * Iterate over collection; returning something + * (non-undefined) will stop iteration. + * + * @param {Array|Object} collection + * @param {Function} iterator + * + * @return {Object} return result that stopped the iteration + */ + + function forEach$1(collection, iterator) { + var val, result; + + if (isUndefined$2(collection)) { + return; + } + + var convertKey = isArray$2(collection) ? toNum$1 : identity$1; + + for (var key in collection) { + if (has$1(collection, key)) { + val = collection[key]; + result = iterator(val, convertKey(key)); + + if (result === false) { + return val; + } + } + } + } + /** + * Reduce collection, returning a single result. + * + * @param {Object|Array} collection + * @param {Function} iterator + * @param {Any} result + * + * @return {Any} result returned from last iterator + */ + + function reduce(collection, iterator, result) { + forEach$1(collection, function (value, idx) { + result = iterator(result, value, idx); + }); + return result; + } + /** + * Return true if every element in the collection + * matches the criteria. + * + * @param {Object|Array} collection + * @param {Function} matcher + * + * @return {Boolean} + */ + + function every(collection, matcher) { + return !!reduce(collection, function (matches, val, key) { + return matches && matcher(val, key); + }, true); + } + /** + * Return true if some elements in the collection + * match the criteria. + * + * @param {Object|Array} collection + * @param {Function} matcher + * + * @return {Boolean} + */ + + function some(collection, matcher) { + return !!find(collection, matcher); + } + /** + * Transform a collection into another collection + * by piping each member through the given fn. + * + * @param {Object|Array} collection + * @param {Function} fn + * + * @return {Array} transformed collection + */ + + function map(collection, fn) { + var result = []; + forEach$1(collection, function (val, key) { + result.push(fn(val, key)); + }); + return result; + } + /** + * Create an object pattern matcher. + * + * @example + * + * const matcher = matchPattern({ id: 1 }); + * + * let element = find(elements, matcher); + * + * @param {Object} pattern + * + * @return {Function} matcherFn + */ + + function matchPattern(pattern) { + return function (el) { + return every(pattern, function (val, key) { + return el[key] === val; + }); + }; + } + + function toMatcher(matcher) { + return isFunction(matcher) ? matcher : function (e) { + return e === matcher; + }; + } + + function identity$1(arg) { + return arg; + } + + function toNum$1(arg) { + return Number(arg); + } + + /** + * Debounce fn, calling it only once if the given time + * elapsed between calls. + * + * Lodash-style the function exposes methods to `#clear` + * and `#flush` to control internal behavior. + * + * @param {Function} fn + * @param {Number} timeout + * + * @return {Function} debounced function + */ + function debounce(fn, timeout) { + var timer; + var lastArgs; + var lastThis; + var lastNow; + + function fire(force) { + var now = Date.now(); + var scheduledDiff = force ? 0 : lastNow + timeout - now; + + if (scheduledDiff > 0) { + return schedule(scheduledDiff); + } + + fn.apply(lastThis, lastArgs); + clear(); + } + + function schedule(timeout) { + timer = setTimeout(fire, timeout); + } + + function clear() { + if (timer) { + clearTimeout(timer); + } + + timer = lastNow = lastArgs = lastThis = undefined; + } + + function flush() { + if (timer) { + fire(true); + } + + clear(); + } + + function callback() { + lastNow = Date.now(); + + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + lastArgs = args; + lastThis = this; // ensure an execution is scheduled + + if (!timer) { + schedule(timeout); + } + } + + callback.flush = flush; + callback.cancel = clear; + return callback; + } + /** + * Bind function against target . + * + * @param {Function} fn + * @param {Object} target + * + * @return {Function} bound function + */ + + function bind(fn, target) { + return fn.bind(target); + } + + function _extends() { + _extends = Object.assign || function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; + }; + + return _extends.apply(this, arguments); + } + + /** + * Convenience wrapper for `Object.assign`. + * + * @param {Object} target + * @param {...Object} others + * + * @return {Object} the target + */ + + function assign(target) { + for (var _len = arguments.length, others = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + others[_key - 1] = arguments[_key]; + } + + return _extends.apply(void 0, [target].concat(others)); + } + /** + * Pick given properties from the target object. + * + * @param {Object} target + * @param {Array} properties + * + * @return {Object} target + */ + + function pick(target, properties) { + var result = {}; + var obj = Object(target); + forEach$1(properties, function (prop) { + if (prop in obj) { + result[prop] = target[prop]; + } + }); + return result; + } + /** + * Pick all target properties, excluding the given ones. + * + * @param {Object} target + * @param {Array} properties + * + * @return {Object} target + */ + + function omit(target, properties) { + var result = {}; + var obj = Object(target); + forEach$1(obj, function (prop, key) { + if (properties.indexOf(key) === -1) { + result[key] = prop; + } + }); + return result; + } + + var DEFAULT_RENDER_PRIORITY$1 = 1000; + + /** + * The base implementation of shape and connection renderers. + * + * @param {EventBus} eventBus + * @param {number} [renderPriority=1000] + */ + function BaseRenderer(eventBus, renderPriority) { + var self = this; + + renderPriority = renderPriority || DEFAULT_RENDER_PRIORITY$1; + + eventBus.on([ 'render.shape', 'render.connection' ], renderPriority, function(evt, context) { + var type = evt.type, + element = context.element, + visuals = context.gfx, + attrs = context.attrs; + + if (self.canRender(element)) { + if (type === 'render.shape') { + return self.drawShape(visuals, element, attrs); + } else { + return self.drawConnection(visuals, element, attrs); + } + } + }); + + eventBus.on([ 'render.getShapePath', 'render.getConnectionPath' ], renderPriority, function(evt, element) { + if (self.canRender(element)) { + if (evt.type === 'render.getShapePath') { + return self.getShapePath(element); + } else { + return self.getConnectionPath(element); + } + } + }); + } + + /** + * Should check whether *this* renderer can render + * the element/connection. + * + * @param {element} element + * + * @returns {boolean} + */ + BaseRenderer.prototype.canRender = function() {}; + + /** + * Provides the shape's snap svg element to be drawn on the `canvas`. + * + * @param {djs.Graphics} visuals + * @param {Shape} shape + * + * @returns {Snap.svg} [returns a Snap.svg paper element ] + */ + BaseRenderer.prototype.drawShape = function() {}; + + /** + * Provides the shape's snap svg element to be drawn on the `canvas`. + * + * @param {djs.Graphics} visuals + * @param {Connection} connection + * + * @returns {Snap.svg} [returns a Snap.svg paper element ] + */ + BaseRenderer.prototype.drawConnection = function() {}; + + /** + * Gets the SVG path of a shape that represents it's visual bounds. + * + * @param {Shape} shape + * + * @return {string} svg path + */ + BaseRenderer.prototype.getShapePath = function() {}; + + /** + * Gets the SVG path of a connection that represents it's visual bounds. + * + * @param {Connection} connection + * + * @return {string} svg path + */ + BaseRenderer.prototype.getConnectionPath = function() {}; + + /** + * Is an element of the given BPMN type? + * + * @param {djs.model.Base|ModdleElement} element + * @param {string} type + * + * @return {boolean} + */ + function is$1(element, type) { + var bo = getBusinessObject(element); + + return bo && (typeof bo.$instanceOf === 'function') && bo.$instanceOf(type); + } + + + /** + * Return true if element has any of the given types. + * + * @param {djs.model.Base} element + * @param {Array} types + * + * @return {boolean} + */ + function isAny(element, types) { + return some(types, function(t) { + return is$1(element, t); + }); + } + + /** + * Return the business object for a given element. + * + * @param {djs.model.Base|ModdleElement} element + * + * @return {ModdleElement} + */ + function getBusinessObject(element) { + return (element && element.businessObject) || element; + } + + /** + * Return the di object for a given element. + * + * @param {djs.model.Base} element + * + * @return {ModdleElement} + */ + function getDi(element) { + return element && element.di; + } + + function isExpanded(element, di) { + + if (is$1(element, 'bpmn:CallActivity')) { + return false; + } + + if (is$1(element, 'bpmn:SubProcess')) { + di = di || getDi(element); + + if (di && is$1(di, 'bpmndi:BPMNPlane')) { + return true; + } + + return di && !!di.isExpanded; + } + + if (is$1(element, 'bpmn:Participant')) { + return !!getBusinessObject(element).processRef; + } + + return true; + } + + function isEventSubProcess(element) { + return element && !!getBusinessObject(element).triggeredByEvent; + } + + function getLabelAttr(semantic) { + if ( + is$1(semantic, 'bpmn:FlowElement') || + is$1(semantic, 'bpmn:Participant') || + is$1(semantic, 'bpmn:Lane') || + is$1(semantic, 'bpmn:SequenceFlow') || + is$1(semantic, 'bpmn:MessageFlow') || + is$1(semantic, 'bpmn:DataInput') || + is$1(semantic, 'bpmn:DataOutput') + ) { + return 'name'; + } + + if (is$1(semantic, 'bpmn:TextAnnotation')) { + return 'text'; + } + + if (is$1(semantic, 'bpmn:Group')) { + return 'categoryValueRef'; + } + } + + function getCategoryValue(semantic) { + var categoryValueRef = semantic['categoryValueRef']; + + if (!categoryValueRef) { + return ''; + } + + + return categoryValueRef.value || ''; + } + + function getLabel(element) { + var semantic = element.businessObject, + attr = getLabelAttr(semantic); + + if (attr) { + + if (attr === 'categoryValueRef') { + + return getCategoryValue(semantic); + } + + return semantic[attr] || ''; + } + } + + function ensureImported(element, target) { + + if (element.ownerDocument !== target.ownerDocument) { + try { + // may fail on webkit + return target.ownerDocument.importNode(element, true); + } catch (e) { + // ignore + } + } + + return element; + } + + /** + * appendTo utility + */ + + /** + * Append a node to a target element and return the appended node. + * + * @param {SVGElement} element + * @param {SVGElement} target + * + * @return {SVGElement} the appended node + */ + function appendTo(element, target) { + return target.appendChild(ensureImported(element, target)); + } + + /** + * append utility + */ + + /** + * Append a node to an element + * + * @param {SVGElement} element + * @param {SVGElement} node + * + * @return {SVGElement} the element + */ + function append(target, node) { + appendTo(node, target); + return target; + } + + /** + * attribute accessor utility + */ + + var LENGTH_ATTR = 2; + + var CSS_PROPERTIES = { + 'alignment-baseline': 1, + 'baseline-shift': 1, + 'clip': 1, + 'clip-path': 1, + 'clip-rule': 1, + 'color': 1, + 'color-interpolation': 1, + 'color-interpolation-filters': 1, + 'color-profile': 1, + 'color-rendering': 1, + 'cursor': 1, + 'direction': 1, + 'display': 1, + 'dominant-baseline': 1, + 'enable-background': 1, + 'fill': 1, + 'fill-opacity': 1, + 'fill-rule': 1, + 'filter': 1, + 'flood-color': 1, + 'flood-opacity': 1, + 'font': 1, + 'font-family': 1, + 'font-size': LENGTH_ATTR, + 'font-size-adjust': 1, + 'font-stretch': 1, + 'font-style': 1, + 'font-variant': 1, + 'font-weight': 1, + 'glyph-orientation-horizontal': 1, + 'glyph-orientation-vertical': 1, + 'image-rendering': 1, + 'kerning': 1, + 'letter-spacing': 1, + 'lighting-color': 1, + 'marker': 1, + 'marker-end': 1, + 'marker-mid': 1, + 'marker-start': 1, + 'mask': 1, + 'opacity': 1, + 'overflow': 1, + 'pointer-events': 1, + 'shape-rendering': 1, + 'stop-color': 1, + 'stop-opacity': 1, + 'stroke': 1, + 'stroke-dasharray': 1, + 'stroke-dashoffset': 1, + 'stroke-linecap': 1, + 'stroke-linejoin': 1, + 'stroke-miterlimit': 1, + 'stroke-opacity': 1, + 'stroke-width': LENGTH_ATTR, + 'text-anchor': 1, + 'text-decoration': 1, + 'text-rendering': 1, + 'unicode-bidi': 1, + 'visibility': 1, + 'word-spacing': 1, + 'writing-mode': 1 + }; + + + function getAttribute(node, name) { + if (CSS_PROPERTIES[name]) { + return node.style[name]; + } else { + return node.getAttributeNS(null, name); + } + } + + function setAttribute(node, name, value) { + var hyphenated = name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); + + var type = CSS_PROPERTIES[hyphenated]; + + if (type) { + // append pixel unit, unless present + if (type === LENGTH_ATTR && typeof value === 'number') { + value = String(value) + 'px'; + } + + node.style[hyphenated] = value; + } else { + node.setAttributeNS(null, name, value); + } + } + + function setAttributes(node, attrs) { + + var names = Object.keys(attrs), i, name; + + for (i = 0, name; (name = names[i]); i++) { + setAttribute(node, name, attrs[name]); + } + } + + /** + * Gets or sets raw attributes on a node. + * + * @param {SVGElement} node + * @param {Object} [attrs] + * @param {String} [name] + * @param {String} [value] + * + * @return {String} + */ + function attr$1(node, name, value) { + if (typeof name === 'string') { + if (value !== undefined) { + setAttribute(node, name, value); + } else { + return getAttribute(node, name); + } + } else { + setAttributes(node, name); + } + + return node; + } + + /** + * Clear utility + */ + function index(arr, obj) { + if (arr.indexOf) { + return arr.indexOf(obj); + } + + + for (var i = 0; i < arr.length; ++i) { + if (arr[i] === obj) { + return i; + } + } + + return -1; + } + + var re$1 = /\s+/; + + var toString$1 = Object.prototype.toString; + + function defined(o) { + return typeof o !== 'undefined'; + } + + /** + * Wrap `el` in a `ClassList`. + * + * @param {Element} el + * @return {ClassList} + * @api public + */ + + function classes$1(el) { + return new ClassList$1(el); + } + + function ClassList$1(el) { + if (!el || !el.nodeType) { + throw new Error('A DOM element reference is required'); + } + this.el = el; + this.list = el.classList; + } + + /** + * Add class `name` if not already present. + * + * @param {String} name + * @return {ClassList} + * @api public + */ + + ClassList$1.prototype.add = function(name) { + + // classList + if (this.list) { + this.list.add(name); + return this; + } + + // fallback + var arr = this.array(); + var i = index(arr, name); + if (!~i) { + arr.push(name); + } + + if (defined(this.el.className.baseVal)) { + this.el.className.baseVal = arr.join(' '); + } else { + this.el.className = arr.join(' '); + } + + return this; + }; + + /** + * Remove class `name` when present, or + * pass a regular expression to remove + * any which match. + * + * @param {String|RegExp} name + * @return {ClassList} + * @api public + */ + + ClassList$1.prototype.remove = function(name) { + if ('[object RegExp]' === toString$1.call(name)) { + return this.removeMatching(name); + } + + // classList + if (this.list) { + this.list.remove(name); + return this; + } + + // fallback + var arr = this.array(); + var i = index(arr, name); + if (~i) { + arr.splice(i, 1); + } + this.el.className.baseVal = arr.join(' '); + return this; + }; + + /** + * Remove all classes matching `re`. + * + * @param {RegExp} re + * @return {ClassList} + * @api private + */ + + ClassList$1.prototype.removeMatching = function(re) { + var arr = this.array(); + for (var i = 0; i < arr.length; i++) { + if (re.test(arr[i])) { + this.remove(arr[i]); + } + } + return this; + }; + + /** + * Toggle class `name`, can force state via `force`. + * + * For browsers that support classList, but do not support `force` yet, + * the mistake will be detected and corrected. + * + * @param {String} name + * @param {Boolean} force + * @return {ClassList} + * @api public + */ + + ClassList$1.prototype.toggle = function(name, force) { + // classList + if (this.list) { + if (defined(force)) { + if (force !== this.list.toggle(name, force)) { + this.list.toggle(name); // toggle again to correct + } + } else { + this.list.toggle(name); + } + return this; + } + + // fallback + if (defined(force)) { + if (!force) { + this.remove(name); + } else { + this.add(name); + } + } else { + if (this.has(name)) { + this.remove(name); + } else { + this.add(name); + } + } + + return this; + }; + + /** + * Return an array of classes. + * + * @return {Array} + * @api public + */ + + ClassList$1.prototype.array = function() { + var className = this.el.getAttribute('class') || ''; + var str = className.replace(/^\s+|\s+$/g, ''); + var arr = str.split(re$1); + if ('' === arr[0]) { + arr.shift(); + } + return arr; + }; + + /** + * Check if class `name` is present. + * + * @param {String} name + * @return {ClassList} + * @api public + */ + + ClassList$1.prototype.has = + ClassList$1.prototype.contains = function(name) { + return ( + this.list ? + this.list.contains(name) : + !! ~index(this.array(), name) + ); + }; + + function remove$2(element) { + var parent = element.parentNode; + + if (parent) { + parent.removeChild(element); + } + + return element; + } + + /** + * Clear utility + */ + + /** + * Removes all children from the given element + * + * @param {DOMElement} element + * @return {DOMElement} the element (for chaining) + */ + function clear$1(element) { + var child; + + while ((child = element.firstChild)) { + remove$2(child); + } + + return element; + } + + var ns = { + svg: 'http://www.w3.org/2000/svg' + }; + + /** + * DOM parsing utility + */ + + var SVG_START = '' + svg + ''; + unwrap = true; + } + + var parsed = parseDocument(svg); + + if (!unwrap) { + return parsed; + } + + var fragment = document.createDocumentFragment(); + + var parent = parsed.firstChild; + + while (parent.firstChild) { + fragment.appendChild(parent.firstChild); + } + + return fragment; + } + + function parseDocument(svg) { + + var parser; + + // parse + parser = new DOMParser(); + parser.async = false; + + return parser.parseFromString(svg, 'text/xml'); + } + + /** + * Create utility for SVG elements + */ + + + /** + * Create a specific type from name or SVG markup. + * + * @param {String} name the name or markup of the element + * @param {Object} [attrs] attributes to set on the element + * + * @returns {SVGElement} + */ + function create$1(name, attrs) { + var element; + + if (name.charAt(0) === '<') { + element = parse$1(name).firstChild; + element = document.importNode(element, true); + } else { + element = document.createElementNS(ns.svg, name); + } + + if (attrs) { + attr$1(element, attrs); + } + + return element; + } + + /** + * Geometry helpers + */ + + // fake node used to instantiate svg geometry elements + var node = null; + + function getNode() { + if (node === null) { + node = create$1('svg'); + } + + return node; + } + + function extend$1(object, props) { + var i, k, keys = Object.keys(props); + + for (i = 0; (k = keys[i]); i++) { + object[k] = props[k]; + } + + return object; + } + + /** + * Create matrix via args. + * + * @example + * + * createMatrix({ a: 1, b: 1 }); + * createMatrix(); + * createMatrix(1, 2, 0, 0, 30, 20); + * + * @return {SVGMatrix} + */ + function createMatrix(a, b, c, d, e, f) { + var matrix = getNode().createSVGMatrix(); + + switch (arguments.length) { + case 0: + return matrix; + case 1: + return extend$1(matrix, a); + case 6: + return extend$1(matrix, { + a: a, + b: b, + c: c, + d: d, + e: e, + f: f + }); + } + } + + function createTransform(matrix) { + if (matrix) { + return getNode().createSVGTransformFromMatrix(matrix); + } else { + return getNode().createSVGTransform(); + } + } + + /** + * Serialization util + */ + + var TEXT_ENTITIES = /([&<>]{1})/g; + var ATTR_ENTITIES = /([\n\r"]{1})/g; + + var ENTITY_REPLACEMENT = { + '&': '&', + '<': '<', + '>': '>', + '"': '\'' + }; + + function escape$1(str, pattern) { + + function replaceFn(match, entity) { + return ENTITY_REPLACEMENT[entity] || entity; + } + + return str.replace(pattern, replaceFn); + } + + function serialize(node, output) { + + var i, len, attrMap, attrNode, childNodes; + + switch (node.nodeType) { + // TEXT + case 3: + // replace special XML characters + output.push(escape$1(node.textContent, TEXT_ENTITIES)); + break; + + // ELEMENT + case 1: + output.push('<', node.tagName); + + if (node.hasAttributes()) { + attrMap = node.attributes; + for (i = 0, len = attrMap.length; i < len; ++i) { + attrNode = attrMap.item(i); + output.push(' ', attrNode.name, '="', escape$1(attrNode.value, ATTR_ENTITIES), '"'); + } + } + + if (node.hasChildNodes()) { + output.push('>'); + childNodes = node.childNodes; + for (i = 0, len = childNodes.length; i < len; ++i) { + serialize(childNodes.item(i), output); + } + output.push(''); + } else { + output.push('/>'); + } + break; + + // COMMENT + case 8: + output.push(''); + break; + + // CDATA + case 4: + output.push(''); + break; + + default: + throw new Error('unable to handle node ' + node.nodeType); + } + + return output; + } + + /** + * innerHTML like functionality for SVG elements. + * based on innerSVG (https://code.google.com/p/innersvg) + */ + + + function set(element, svg) { + + var parsed = parse$1(svg); + + // clear element contents + clear$1(element); + + if (!svg) { + return; + } + + if (!isFragment(parsed)) { + // extract from parsed document + parsed = parsed.documentElement; + } + + var nodes = slice$1(parsed.childNodes); + + // import + append each node + for (var i = 0; i < nodes.length; i++) { + appendTo(nodes[i], element); + } + + } + + function get(element) { + var child = element.firstChild, + output = []; + + while (child) { + serialize(child, output); + child = child.nextSibling; + } + + return output.join(''); + } + + function isFragment(node) { + return node.nodeName === '#document-fragment'; + } + + function innerSVG(element, svg) { + + if (svg !== undefined) { + + try { + set(element, svg); + } catch (e) { + throw new Error('error parsing SVG: ' + e.message); + } + + return element; + } else { + return get(element); + } + } + + + function slice$1(arr) { + return Array.prototype.slice.call(arr); + } + + /** + * transform accessor utility + */ + + function wrapMatrix(transformList, transform) { + if (transform instanceof SVGMatrix) { + return transformList.createSVGTransformFromMatrix(transform); + } + + return transform; + } + + + function setTransforms(transformList, transforms) { + var i, t; + + transformList.clear(); + + for (i = 0; (t = transforms[i]); i++) { + transformList.appendItem(wrapMatrix(transformList, t)); + } + } + + /** + * Get or set the transforms on the given node. + * + * @param {SVGElement} node + * @param {SVGTransform|SVGMatrix|Array} [transforms] + * + * @return {SVGTransform} the consolidated transform + */ + function transform$1(node, transforms) { + var transformList = node.transform.baseVal; + + if (transforms) { + + if (!Array.isArray(transforms)) { + transforms = [ transforms ]; + } + + setTransforms(transformList, transforms); + } + + return transformList.consolidate(); + } + + function componentsToPath(elements) { + return elements.join(',').replace(/,?([A-z]),?/g, '$1'); + } + + function toSVGPoints(points) { + var result = ''; + + for (var i = 0, p; (p = points[i]); i++) { + result += p.x + ',' + p.y + ' '; + } + + return result; + } + + function createLine(points, attrs) { + + var line = create$1('polyline'); + attr$1(line, { points: toSVGPoints(points) }); + + if (attrs) { + attr$1(line, attrs); + } + + return line; + } + + function updateLine(gfx, points) { + attr$1(gfx, { points: toSVGPoints(points) }); + + return gfx; + } + + var black = 'hsl(225, 10%, 15%)'; + + // element utils ////////////////////// + + /** + * Checks if eventDefinition of the given element matches with semantic type. + * + * @return {boolean} true if element is of the given semantic type + */ + function isTypedEvent(event, eventDefinitionType, filter) { + + function matches(definition, filter) { + return every(filter, function(val, key) { + + // we want a == conversion here, to be able to catch + // undefined == false and friends + /* jshint -W116 */ + return definition[key] == val; + }); + } + + return some(event.eventDefinitions, function(definition) { + return definition.$type === eventDefinitionType && matches(event, filter); + }); + } + + function isThrowEvent(event) { + return (event.$type === 'bpmn:IntermediateThrowEvent') || (event.$type === 'bpmn:EndEvent'); + } + + function isCollection(element) { + var dataObject = element.dataObjectRef; + + return element.isCollection || (dataObject && dataObject.isCollection); + } + + function getSemantic(element) { + return element.businessObject; + } + + + // color access ////////////////////// + + function getFillColor(element, defaultColor) { + var di = getDi(element); + + return di.get('color:background-color') || di.get('bioc:fill') || defaultColor || 'white'; + } + + function getStrokeColor(element, defaultColor) { + var di = getDi(element); + + return di.get('color:border-color') || di.get('bioc:stroke') || defaultColor || black; + } + + function getLabelColor(element, defaultColor, defaultStrokeColor) { + var di = getDi(element), + label = di.get('label'); + + return label && label.get('color:color') || defaultColor || + getStrokeColor(element, defaultStrokeColor); + } + + // cropping path customizations ////////////////////// + + function getCirclePath(shape) { + + var cx = shape.x + shape.width / 2, + cy = shape.y + shape.height / 2, + radius = shape.width / 2; + + var circlePath = [ + [ 'M', cx, cy ], + [ 'm', 0, -radius ], + [ 'a', radius, radius, 0, 1, 1, 0, 2 * radius ], + [ 'a', radius, radius, 0, 1, 1, 0, -2 * radius ], + [ 'z' ] + ]; + + return componentsToPath(circlePath); + } + + function getRoundRectPath(shape, borderRadius) { + + var x = shape.x, + y = shape.y, + width = shape.width, + height = shape.height; + + var roundRectPath = [ + [ 'M', x + borderRadius, y ], + [ 'l', width - borderRadius * 2, 0 ], + [ 'a', borderRadius, borderRadius, 0, 0, 1, borderRadius, borderRadius ], + [ 'l', 0, height - borderRadius * 2 ], + [ 'a', borderRadius, borderRadius, 0, 0, 1, -borderRadius, borderRadius ], + [ 'l', borderRadius * 2 - width, 0 ], + [ 'a', borderRadius, borderRadius, 0, 0, 1, -borderRadius, -borderRadius ], + [ 'l', 0, borderRadius * 2 - height ], + [ 'a', borderRadius, borderRadius, 0, 0, 1, borderRadius, -borderRadius ], + [ 'z' ] + ]; + + return componentsToPath(roundRectPath); + } + + function getDiamondPath(shape) { + + var width = shape.width, + height = shape.height, + x = shape.x, + y = shape.y, + halfWidth = width / 2, + halfHeight = height / 2; + + var diamondPath = [ + [ 'M', x + halfWidth, y ], + [ 'l', halfWidth, halfHeight ], + [ 'l', -halfWidth, halfHeight ], + [ 'l', -halfWidth, -halfHeight ], + [ 'z' ] + ]; + + return componentsToPath(diamondPath); + } + + function getRectPath(shape) { + var x = shape.x, + y = shape.y, + width = shape.width, + height = shape.height; + + var rectPath = [ + [ 'M', x, y ], + [ 'l', width, 0 ], + [ 'l', 0, height ], + [ 'l', -width, 0 ], + [ 'z' ] + ]; + + return componentsToPath(rectPath); + } + + /** + * Flatten array, one level deep. + * + * @param {Array} arr + * + * @return {Array} + */ + + var nativeToString = Object.prototype.toString; + var nativeHasOwnProperty = Object.prototype.hasOwnProperty; + function isUndefined$1(obj) { + return obj === undefined; + } + function isArray$1(obj) { + return nativeToString.call(obj) === '[object Array]'; + } + /** + * Return true, if target owns a property with the given key. + * + * @param {Object} target + * @param {String} key + * + * @return {Boolean} + */ + + function has(target, key) { + return nativeHasOwnProperty.call(target, key); + } + /** + * Iterate over collection; returning something + * (non-undefined) will stop iteration. + * + * @param {Array|Object} collection + * @param {Function} iterator + * + * @return {Object} return result that stopped the iteration + */ + + function forEach(collection, iterator) { + var val, result; + + if (isUndefined$1(collection)) { + return; + } + + var convertKey = isArray$1(collection) ? toNum : identity; + + for (var key in collection) { + if (has(collection, key)) { + val = collection[key]; + result = iterator(val, convertKey(key)); + + if (result === false) { + return val; + } + } + } + } + + function identity(arg) { + return arg; + } + + function toNum(arg) { + return Number(arg); + } + + /** + * Assigns style attributes in a style-src compliant way. + * + * @param {Element} element + * @param {...Object} styleSources + * + * @return {Element} the element + */ + function assign$1(element) { + var target = element.style; + + for (var _len = arguments.length, styleSources = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + styleSources[_key - 1] = arguments[_key]; + } + + forEach(styleSources, function (style) { + if (!style) { + return; + } + + forEach(style, function (value, key) { + target[key] = value; + }); + }); + + return element; + } + + /** + * Set attribute `name` to `val`, or get attr `name`. + * + * @param {Element} el + * @param {String} name + * @param {String} [val] + * @api public + */ + function attr(el, name, val) { + // get + if (arguments.length == 2) { + return el.getAttribute(name); + } + + // remove + if (val === null) { + return el.removeAttribute(name); + } + + // set + el.setAttribute(name, val); + + return el; + } + + var indexOf = [].indexOf; + + var indexof = function(arr, obj){ + if (indexOf) return arr.indexOf(obj); + for (var i = 0; i < arr.length; ++i) { + if (arr[i] === obj) return i; + } + return -1; + }; + + /** + * Taken from https://github.com/component/classes + * + * Without the component bits. + */ + + /** + * Whitespace regexp. + */ + + var re = /\s+/; + + /** + * toString reference. + */ + + var toString = Object.prototype.toString; + + /** + * Wrap `el` in a `ClassList`. + * + * @param {Element} el + * @return {ClassList} + * @api public + */ + + function classes(el) { + return new ClassList(el); + } + + /** + * Initialize a new ClassList for `el`. + * + * @param {Element} el + * @api private + */ + + function ClassList(el) { + if (!el || !el.nodeType) { + throw new Error('A DOM element reference is required'); + } + this.el = el; + this.list = el.classList; + } + + /** + * Add class `name` if not already present. + * + * @param {String} name + * @return {ClassList} + * @api public + */ + + ClassList.prototype.add = function (name) { + // classList + if (this.list) { + this.list.add(name); + return this; + } + + // fallback + var arr = this.array(); + var i = indexof(arr, name); + if (!~i) arr.push(name); + this.el.className = arr.join(' '); + return this; + }; + + /** + * Remove class `name` when present, or + * pass a regular expression to remove + * any which match. + * + * @param {String|RegExp} name + * @return {ClassList} + * @api public + */ + + ClassList.prototype.remove = function (name) { + if ('[object RegExp]' == toString.call(name)) { + return this.removeMatching(name); + } + + // classList + if (this.list) { + this.list.remove(name); + return this; + } + + // fallback + var arr = this.array(); + var i = indexof(arr, name); + if (~i) arr.splice(i, 1); + this.el.className = arr.join(' '); + return this; + }; + + /** + * Remove all classes matching `re`. + * + * @param {RegExp} re + * @return {ClassList} + * @api private + */ + + ClassList.prototype.removeMatching = function (re) { + var arr = this.array(); + for (var i = 0; i < arr.length; i++) { + if (re.test(arr[i])) { + this.remove(arr[i]); + } + } + return this; + }; + + /** + * Toggle class `name`, can force state via `force`. + * + * For browsers that support classList, but do not support `force` yet, + * the mistake will be detected and corrected. + * + * @param {String} name + * @param {Boolean} force + * @return {ClassList} + * @api public + */ + + ClassList.prototype.toggle = function (name, force) { + // classList + if (this.list) { + if ('undefined' !== typeof force) { + if (force !== this.list.toggle(name, force)) { + this.list.toggle(name); // toggle again to correct + } + } else { + this.list.toggle(name); + } + return this; + } + + // fallback + if ('undefined' !== typeof force) { + if (!force) { + this.remove(name); + } else { + this.add(name); + } + } else { + if (this.has(name)) { + this.remove(name); + } else { + this.add(name); + } + } + + return this; + }; + + /** + * Return an array of classes. + * + * @return {Array} + * @api public + */ + + ClassList.prototype.array = function () { + var className = this.el.getAttribute('class') || ''; + var str = className.replace(/^\s+|\s+$/g, ''); + var arr = str.split(re); + if ('' === arr[0]) arr.shift(); + return arr; + }; + + /** + * Check if class `name` is present. + * + * @param {String} name + * @return {ClassList} + * @api public + */ + + ClassList.prototype.has = ClassList.prototype.contains = function (name) { + return this.list ? this.list.contains(name) : !!~indexof(this.array(), name); + }; + + /** + * Remove all children from the given element. + */ + function clear(el) { + + var c; + + while (el.childNodes.length) { + c = el.childNodes[0]; + el.removeChild(c); + } + + return el; + } + + var proto = typeof Element !== 'undefined' ? Element.prototype : {}; + var vendor = proto.matches + || proto.matchesSelector + || proto.webkitMatchesSelector + || proto.mozMatchesSelector + || proto.msMatchesSelector + || proto.oMatchesSelector; + + var matchesSelector = match; + + /** + * Match `el` to `selector`. + * + * @param {Element} el + * @param {String} selector + * @return {Boolean} + * @api public + */ + + function match(el, selector) { + if (!el || el.nodeType !== 1) return false; + if (vendor) return vendor.call(el, selector); + var nodes = el.parentNode.querySelectorAll(selector); + for (var i = 0; i < nodes.length; i++) { + if (nodes[i] == el) return true; + } + return false; + } + + /** + * Closest + * + * @param {Element} el + * @param {String} selector + * @param {Boolean} checkYourSelf (optional) + */ + function closest (element, selector, checkYourSelf) { + var currentElem = checkYourSelf ? element : element.parentNode; + + while (currentElem && currentElem.nodeType !== document.DOCUMENT_NODE && currentElem.nodeType !== document.DOCUMENT_FRAGMENT_NODE) { + + if (matchesSelector(currentElem, selector)) { + return currentElem; + } + + currentElem = currentElem.parentNode; + } + + return matchesSelector(currentElem, selector) ? currentElem : null; + } + + var bind$1 = window.addEventListener ? 'addEventListener' : 'attachEvent', + unbind = window.removeEventListener ? 'removeEventListener' : 'detachEvent', + prefix$6 = bind$1 !== 'addEventListener' ? 'on' : ''; + + /** + * Bind `el` event `type` to `fn`. + * + * @param {Element} el + * @param {String} type + * @param {Function} fn + * @param {Boolean} capture + * @return {Function} + * @api public + */ + + var bind_1 = function(el, type, fn, capture){ + el[bind$1](prefix$6 + type, fn, capture || false); + return fn; + }; + + /** + * Unbind `el` event `type`'s callback `fn`. + * + * @param {Element} el + * @param {String} type + * @param {Function} fn + * @param {Boolean} capture + * @return {Function} + * @api public + */ + + var unbind_1 = function(el, type, fn, capture){ + el[unbind](prefix$6 + type, fn, capture || false); + return fn; + }; + + var componentEvent = { + bind: bind_1, + unbind: unbind_1 + }; + + /** + * Module dependencies. + */ + + /** + * Delegate event `type` to `selector` + * and invoke `fn(e)`. A callback function + * is returned which may be passed to `.unbind()`. + * + * @param {Element} el + * @param {String} selector + * @param {String} type + * @param {Function} fn + * @param {Boolean} capture + * @return {Function} + * @api public + */ + + // Some events don't bubble, so we want to bind to the capture phase instead + // when delegating. + var forceCaptureEvents = ['focus', 'blur']; + + function bind$2(el, selector, type, fn, capture) { + if (forceCaptureEvents.indexOf(type) !== -1) { + capture = true; + } + + return componentEvent.bind(el, type, function (e) { + var target = e.target || e.srcElement; + e.delegateTarget = closest(target, selector, true); + if (e.delegateTarget) { + fn.call(el, e); + } + }, capture); + } + + /** + * Unbind event `type`'s callback `fn`. + * + * @param {Element} el + * @param {String} type + * @param {Function} fn + * @param {Boolean} capture + * @api public + */ + function unbind$1(el, type, fn, capture) { + if (forceCaptureEvents.indexOf(type) !== -1) { + capture = true; + } + + return componentEvent.unbind(el, type, fn, capture); + } + + var delegate = { + bind: bind$2, + unbind: unbind$1 + }; + + /** + * Expose `parse`. + */ + + var domify = parse; + + /** + * Tests for browser support. + */ + + var innerHTMLBug = false; + var bugTestDiv; + if (typeof document !== 'undefined') { + bugTestDiv = document.createElement('div'); + // Setup + bugTestDiv.innerHTML = '
          a'; + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + innerHTMLBug = !bugTestDiv.getElementsByTagName('link').length; + bugTestDiv = undefined; + } + + /** + * Wrap map from jquery. + */ + + var map$1 = { + legend: [1, '
          ', '
          '], + tr: [2, '', '
          '], + col: [2, '', '
          '], + // for script/link/style tags to work in IE6-8, you have to wrap + // in a div with a non-whitespace character in front, ha! + _default: innerHTMLBug ? [1, 'X
          ', '
          '] : [0, '', ''] + }; + + map$1.td = + map$1.th = [3, '', '
          ']; + + map$1.option = + map$1.optgroup = [1, '']; + + map$1.thead = + map$1.tbody = + map$1.colgroup = + map$1.caption = + map$1.tfoot = [1, '', '
          ']; + + map$1.polyline = + map$1.ellipse = + map$1.polygon = + map$1.circle = + map$1.text = + map$1.line = + map$1.path = + map$1.rect = + map$1.g = [1, '','']; + + /** + * Parse `html` and return a DOM Node instance, which could be a TextNode, + * HTML DOM Node of some kind (
          for example), or a DocumentFragment + * instance, depending on the contents of the `html` string. + * + * @param {String} html - HTML string to "domify" + * @param {Document} doc - The `document` instance to create the Node for + * @return {DOMNode} the TextNode, DOM Node, or DocumentFragment instance + * @api private + */ + + function parse(html, doc) { + if ('string' != typeof html) throw new TypeError('String expected'); + + // default to the global `document` object + if (!doc) doc = document; + + // tag name + var m = /<([\w:]+)/.exec(html); + if (!m) return doc.createTextNode(html); + + html = html.replace(/^\s+|\s+$/g, ''); // Remove leading/trailing whitespace + + var tag = m[1]; + + // body support + if (tag == 'body') { + var el = doc.createElement('html'); + el.innerHTML = html; + return el.removeChild(el.lastChild); + } + + // wrap map + var wrap = map$1[tag] || map$1._default; + var depth = wrap[0]; + var prefix = wrap[1]; + var suffix = wrap[2]; + var el = doc.createElement('div'); + el.innerHTML = prefix + html + suffix; + while (depth--) el = el.lastChild; + + // one element + if (el.firstChild == el.lastChild) { + return el.removeChild(el.firstChild); + } + + // several elements + var fragment = doc.createDocumentFragment(); + while (el.firstChild) { + fragment.appendChild(el.removeChild(el.firstChild)); + } + + return fragment; + } + + function query(selector, el) { + el = el || document; + + return el.querySelector(selector); + } + + function all(selector, el) { + el = el || document; + + return el.querySelectorAll(selector); + } + + function remove$1(el) { + el.parentNode && el.parentNode.removeChild(el); + } + + /** + * @param {} element + * @param {number} x + * @param {number} y + * @param {number} angle + * @param {number} amount + */ + function transform(gfx, x, y, angle, amount) { + var translate = createTransform(); + translate.setTranslate(x, y); + + var rotate = createTransform(); + rotate.setRotate(angle || 0, 0, 0); + + var scale = createTransform(); + scale.setScale(amount || 1, amount || 1); + + transform$1(gfx, [ translate, rotate, scale ]); + } + + + /** + * @param {SVGElement} element + * @param {number} x + * @param {number} y + */ + function translate$1(gfx, x, y) { + var translate = createTransform(); + translate.setTranslate(x, y); + + transform$1(gfx, translate); + } + + + /** + * @param {SVGElement} element + * @param {number} angle + */ + function rotate(gfx, angle) { + var rotate = createTransform(); + rotate.setRotate(angle, 0, 0); + + transform$1(gfx, rotate); + } + + function createCommonjsModule(fn, module) { + return module = { exports: {} }, fn(module, module.exports), module.exports; + } + + var hat_1 = createCommonjsModule(function (module) { + var hat = module.exports = function (bits, base) { + if (!base) base = 16; + if (bits === undefined) bits = 128; + if (bits <= 0) return '0'; + + var digits = Math.log(Math.pow(2, bits)) / Math.log(base); + for (var i = 2; digits === Infinity; i *= 2) { + digits = Math.log(Math.pow(2, bits / i)) / Math.log(base) * i; + } + + var rem = digits - Math.floor(digits); + + var res = ''; + + for (var i = 0; i < Math.floor(digits); i++) { + var x = Math.floor(Math.random() * base).toString(base); + res = x + res; + } + + if (rem) { + var b = Math.pow(base, rem); + var x = Math.floor(Math.random() * b).toString(base); + res = x + res; + } + + var parsed = parseInt(res, base); + if (parsed !== Infinity && parsed >= Math.pow(2, bits)) { + return hat(bits, base) + } + else return res; + }; + + hat.rack = function (bits, base, expandBy) { + var fn = function (data) { + var iters = 0; + do { + if (iters ++ > 10) { + if (expandBy) bits += expandBy; + else throw new Error('too many ID collisions, use more bits') + } + + var id = hat(bits, base); + } while (Object.hasOwnProperty.call(hats, id)); + + hats[id] = data; + return id; + }; + var hats = fn.hats = {}; + + fn.get = function (id) { + return fn.hats[id]; + }; + + fn.set = function (id, value) { + fn.hats[id] = value; + return fn; + }; + + fn.bits = bits || 128; + fn.base = base || 16; + return fn; + }; + }); + + /** + * Create a new id generator / cache instance. + * + * You may optionally provide a seed that is used internally. + * + * @param {Seed} seed + */ + + function Ids(seed) { + if (!(this instanceof Ids)) { + return new Ids(seed); + } + + seed = seed || [128, 36, 1]; + this._seed = seed.length ? hat_1.rack(seed[0], seed[1], seed[2]) : seed; + } + /** + * Generate a next id. + * + * @param {Object} [element] element to bind the id to + * + * @return {String} id + */ + + Ids.prototype.next = function (element) { + return this._seed(element || true); + }; + /** + * Generate a next id with a given prefix. + * + * @param {Object} [element] element to bind the id to + * + * @return {String} id + */ + + + Ids.prototype.nextPrefixed = function (prefix, element) { + var id; + + do { + id = prefix + this.next(true); + } while (this.assigned(id)); // claim {prefix}{random} + + + this.claim(id, element); // return + + return id; + }; + /** + * Manually claim an existing id. + * + * @param {String} id + * @param {String} [element] element the id is claimed by + */ + + + Ids.prototype.claim = function (id, element) { + this._seed.set(id, element || true); + }; + /** + * Returns true if the given id has already been assigned. + * + * @param {String} id + * @return {Boolean} + */ + + + Ids.prototype.assigned = function (id) { + return this._seed.get(id) || false; + }; + /** + * Unclaim an id. + * + * @param {String} id the id to unclaim + */ + + + Ids.prototype.unclaim = function (id) { + delete this._seed.hats[id]; + }; + /** + * Clear all claimed ids. + */ + + + Ids.prototype.clear = function () { + var hats = this._seed.hats, + id; + + for (id in hats) { + this.unclaim(id); + } + }; + + var RENDERER_IDS = new Ids(); + + var TASK_BORDER_RADIUS = 10; + var INNER_OUTER_DIST = 3; + + var DEFAULT_FILL_OPACITY = .95, + HIGH_FILL_OPACITY = .35; + + var ELEMENT_LABEL_DISTANCE = 10; + + function BpmnRenderer( + config, eventBus, styles, pathMap, + canvas, textRenderer, priority) { + + BaseRenderer.call(this, eventBus, priority); + + var defaultFillColor = config && config.defaultFillColor, + defaultStrokeColor = config && config.defaultStrokeColor, + defaultLabelColor = config && config.defaultLabelColor; + + var rendererId = RENDERER_IDS.next(); + + var markers = {}; + + var computeStyle = styles.computeStyle; + + function addMarker(id, options) { + var attrs = assign({ + fill: black, + strokeWidth: 1, + strokeLinecap: 'round', + strokeDasharray: 'none' + }, options.attrs); + + var ref = options.ref || { x: 0, y: 0 }; + + var scale = options.scale || 1; + + // fix for safari / chrome / firefox bug not correctly + // resetting stroke dash array + if (attrs.strokeDasharray === 'none') { + attrs.strokeDasharray = [ 10000, 1 ]; + } + + var marker = create$1('marker'); + + attr$1(options.element, attrs); + + append(marker, options.element); + + attr$1(marker, { + id: id, + viewBox: '0 0 20 20', + refX: ref.x, + refY: ref.y, + markerWidth: 20 * scale, + markerHeight: 20 * scale, + orient: 'auto' + }); + + var defs = query('defs', canvas._svg); + + if (!defs) { + defs = create$1('defs'); + + append(canvas._svg, defs); + } + + append(defs, marker); + + markers[id] = marker; + } + + function colorEscape(str) { + + // only allow characters and numbers + return str.replace(/[^0-9a-zA-z]+/g, '_'); + } + + function marker(type, fill, stroke) { + var id = type + '-' + colorEscape(fill) + '-' + colorEscape(stroke) + '-' + rendererId; + + if (!markers[id]) { + createMarker(id, type, fill, stroke); + } + + return 'url(#' + id + ')'; + } + + function createMarker(id, type, fill, stroke) { + + if (type === 'sequenceflow-end') { + var sequenceflowEnd = create$1('path'); + attr$1(sequenceflowEnd, { d: 'M 1 5 L 11 10 L 1 15 Z' }); + + addMarker(id, { + element: sequenceflowEnd, + ref: { x: 11, y: 10 }, + scale: 0.5, + attrs: { + fill: stroke, + stroke: stroke + } + }); + } + + if (type === 'messageflow-start') { + var messageflowStart = create$1('circle'); + attr$1(messageflowStart, { cx: 6, cy: 6, r: 3.5 }); + + addMarker(id, { + element: messageflowStart, + attrs: { + fill: fill, + stroke: stroke + }, + ref: { x: 6, y: 6 } + }); + } + + if (type === 'messageflow-end') { + var messageflowEnd = create$1('path'); + attr$1(messageflowEnd, { d: 'm 1 5 l 0 -3 l 7 3 l -7 3 z' }); + + addMarker(id, { + element: messageflowEnd, + attrs: { + fill: fill, + stroke: stroke, + strokeLinecap: 'butt' + }, + ref: { x: 8.5, y: 5 } + }); + } + + if (type === 'association-start') { + var associationStart = create$1('path'); + attr$1(associationStart, { d: 'M 11 5 L 1 10 L 11 15' }); + + addMarker(id, { + element: associationStart, + attrs: { + fill: 'none', + stroke: stroke, + strokeWidth: 1.5 + }, + ref: { x: 1, y: 10 }, + scale: 0.5 + }); + } + + if (type === 'association-end') { + var associationEnd = create$1('path'); + attr$1(associationEnd, { d: 'M 1 5 L 11 10 L 1 15' }); + + addMarker(id, { + element: associationEnd, + attrs: { + fill: 'none', + stroke: stroke, + strokeWidth: 1.5 + }, + ref: { x: 12, y: 10 }, + scale: 0.5 + }); + } + + if (type === 'conditional-flow-marker') { + var conditionalflowMarker = create$1('path'); + attr$1(conditionalflowMarker, { d: 'M 0 10 L 8 6 L 16 10 L 8 14 Z' }); + + addMarker(id, { + element: conditionalflowMarker, + attrs: { + fill: fill, + stroke: stroke + }, + ref: { x: -1, y: 10 }, + scale: 0.5 + }); + } + + if (type === 'conditional-default-flow-marker') { + var conditionaldefaultflowMarker = create$1('path'); + attr$1(conditionaldefaultflowMarker, { d: 'M 6 4 L 10 16' }); + + addMarker(id, { + element: conditionaldefaultflowMarker, + attrs: { + stroke: stroke + }, + ref: { x: 0, y: 10 }, + scale: 0.5 + }); + } + } + + function drawCircle(parentGfx, width, height, offset, attrs) { + + if (isObject(offset)) { + attrs = offset; + offset = 0; + } + + offset = offset || 0; + + attrs = computeStyle(attrs, { + stroke: black, + strokeWidth: 2, + fill: 'white' + }); + + if (attrs.fill === 'none') { + delete attrs.fillOpacity; + } + + var cx = width / 2, + cy = height / 2; + + var circle = create$1('circle'); + attr$1(circle, { + cx: cx, + cy: cy, + r: Math.round((width + height) / 4 - offset) + }); + attr$1(circle, attrs); + + append(parentGfx, circle); + + return circle; + } + + function drawRect(parentGfx, width, height, r, offset, attrs) { + + if (isObject(offset)) { + attrs = offset; + offset = 0; + } + + offset = offset || 0; + + attrs = computeStyle(attrs, { + stroke: black, + strokeWidth: 2, + fill: 'white' + }); + + var rect = create$1('rect'); + attr$1(rect, { + x: offset, + y: offset, + width: width - offset * 2, + height: height - offset * 2, + rx: r, + ry: r + }); + attr$1(rect, attrs); + + append(parentGfx, rect); + + return rect; + } + + function drawDiamond(parentGfx, width, height, attrs) { + + var x_2 = width / 2; + var y_2 = height / 2; + + var points = [ { x: x_2, y: 0 }, { x: width, y: y_2 }, { x: x_2, y: height }, { x: 0, y: y_2 } ]; + + var pointsString = points.map(function(point) { + return point.x + ',' + point.y; + }).join(' '); + + attrs = computeStyle(attrs, { + stroke: black, + strokeWidth: 2, + fill: 'white' + }); + + var polygon = create$1('polygon'); + attr$1(polygon, { + points: pointsString + }); + attr$1(polygon, attrs); + + append(parentGfx, polygon); + + return polygon; + } + + function drawLine(parentGfx, waypoints, attrs) { + attrs = computeStyle(attrs, [ 'no-fill' ], { + stroke: black, + strokeWidth: 2, + fill: 'none' + }); + + var line = createLine(waypoints, attrs); + + append(parentGfx, line); + + return line; + } + + function drawPath(parentGfx, d, attrs) { + + attrs = computeStyle(attrs, [ 'no-fill' ], { + strokeWidth: 2, + stroke: black + }); + + var path = create$1('path'); + attr$1(path, { d: d }); + attr$1(path, attrs); + + append(parentGfx, path); + + return path; + } + + function drawMarker(type, parentGfx, path, attrs) { + return drawPath(parentGfx, path, assign({ 'data-marker': type }, attrs)); + } + + function renderer(type) { + return handlers[type]; + } + + function as(type) { + return function(parentGfx, element) { + return renderer(type)(parentGfx, element); + }; + } + + function renderEventContent(element, parentGfx) { + + var event = getSemantic(element); + var isThrowing = isThrowEvent(event); + + if (event.eventDefinitions && event.eventDefinitions.length > 1) { + if (event.parallelMultiple) { + return renderer('bpmn:ParallelMultipleEventDefinition')(parentGfx, element, isThrowing); + } + else { + return renderer('bpmn:MultipleEventDefinition')(parentGfx, element, isThrowing); + } + } + + if (isTypedEvent(event, 'bpmn:MessageEventDefinition')) { + return renderer('bpmn:MessageEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:TimerEventDefinition')) { + return renderer('bpmn:TimerEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:ConditionalEventDefinition')) { + return renderer('bpmn:ConditionalEventDefinition')(parentGfx, element); + } + + if (isTypedEvent(event, 'bpmn:SignalEventDefinition')) { + return renderer('bpmn:SignalEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:EscalationEventDefinition')) { + return renderer('bpmn:EscalationEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:LinkEventDefinition')) { + return renderer('bpmn:LinkEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:ErrorEventDefinition')) { + return renderer('bpmn:ErrorEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:CancelEventDefinition')) { + return renderer('bpmn:CancelEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:CompensateEventDefinition')) { + return renderer('bpmn:CompensateEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:TerminateEventDefinition')) { + return renderer('bpmn:TerminateEventDefinition')(parentGfx, element, isThrowing); + } + + return null; + } + + function renderLabel(parentGfx, label, options) { + + options = assign({ + size: { + width: 100 + } + }, options); + + var text = textRenderer.createText(label || '', options); + + classes$1(text).add('djs-label'); + + append(parentGfx, text); + + return text; + } + + function renderEmbeddedLabel(parentGfx, element, align) { + var semantic = getSemantic(element); + + return renderLabel(parentGfx, semantic.name, { + box: element, + align: align, + padding: 5, + style: { + fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor) + } + }); + } + + function renderExternalLabel(parentGfx, element) { + + var box = { + width: 90, + height: 30, + x: element.width / 2 + element.x, + y: element.height / 2 + element.y + }; + + return renderLabel(parentGfx, getLabel(element), { + box: box, + fitBox: true, + style: assign( + {}, + textRenderer.getExternalStyle(), + { + fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor) + } + ) + }); + } + + function renderLaneLabel(parentGfx, text, element) { + var textBox = renderLabel(parentGfx, text, { + box: { + height: 30, + width: element.height + }, + align: 'center-middle', + style: { + fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor) + } + }); + + var top = -1 * element.height; + + transform(textBox, 0, -top, 270); + } + + function createPathFromConnection(connection) { + var waypoints = connection.waypoints; + + var pathData = 'm ' + waypoints[0].x + ',' + waypoints[0].y; + for (var i = 1; i < waypoints.length; i++) { + pathData += 'L' + waypoints[i].x + ',' + waypoints[i].y + ' '; + } + return pathData; + } + + var handlers = this.handlers = { + 'bpmn:Event': function(parentGfx, element, attrs) { + + if (!('fillOpacity' in attrs)) { + attrs.fillOpacity = DEFAULT_FILL_OPACITY; + } + + return drawCircle(parentGfx, element.width, element.height, attrs); + }, + 'bpmn:StartEvent': function(parentGfx, element) { + var attrs = { + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + var semantic = getSemantic(element); + + if (!semantic.isInterrupting) { + attrs = { + strokeDasharray: '6', + strokeLinecap: 'round', + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }; + } + + var circle = renderer('bpmn:Event')(parentGfx, element, attrs); + + renderEventContent(element, parentGfx); + + return circle; + }, + 'bpmn:MessageEventDefinition': function(parentGfx, element, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_MESSAGE', { + xScaleFactor: 0.9, + yScaleFactor: 0.9, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.235, + my: 0.315 + } + }); + + var fill = isThrowing ? getStrokeColor(element, defaultStrokeColor) : getFillColor(element, defaultFillColor); + var stroke = isThrowing ? getFillColor(element, defaultFillColor) : getStrokeColor(element, defaultStrokeColor); + + var messagePath = drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: stroke + }); + + return messagePath; + }, + 'bpmn:TimerEventDefinition': function(parentGfx, element) { + var circle = drawCircle(parentGfx, element.width, element.height, 0.2 * element.height, { + strokeWidth: 2, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var pathData = pathMap.getScaledPath('EVENT_TIMER_WH', { + xScaleFactor: 0.75, + yScaleFactor: 0.75, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.5, + my: 0.5 + } + }); + + drawPath(parentGfx, pathData, { + strokeWidth: 2, + strokeLinecap: 'square', + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + for (var i = 0;i < 12; i++) { + + var linePathData = pathMap.getScaledPath('EVENT_TIMER_LINE', { + xScaleFactor: 0.75, + yScaleFactor: 0.75, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.5, + my: 0.5 + } + }); + + var width = element.width / 2; + var height = element.height / 2; + + drawPath(parentGfx, linePathData, { + strokeWidth: 1, + strokeLinecap: 'square', + transform: 'rotate(' + (i * 30) + ',' + height + ',' + width + ')', + stroke: getStrokeColor(element, defaultStrokeColor) + }); + } + + return circle; + }, + 'bpmn:EscalationEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_ESCALATION', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.5, + my: 0.2 + } + }); + + var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor(event, defaultStrokeColor) + }); + }, + 'bpmn:ConditionalEventDefinition': function(parentGfx, event) { + var pathData = pathMap.getScaledPath('EVENT_CONDITIONAL', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.5, + my: 0.222 + } + }); + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + stroke: getStrokeColor(event, defaultStrokeColor) + }); + }, + 'bpmn:LinkEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_LINK', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.57, + my: 0.263 + } + }); + + var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor(event, defaultStrokeColor) + }); + }, + 'bpmn:ErrorEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_ERROR', { + xScaleFactor: 1.1, + yScaleFactor: 1.1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.2, + my: 0.722 + } + }); + + var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor(event, defaultStrokeColor) + }); + }, + 'bpmn:CancelEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_CANCEL_45', { + xScaleFactor: 1.0, + yScaleFactor: 1.0, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.638, + my: -0.055 + } + }); + + var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none'; + + var path = drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor(event, defaultStrokeColor) + }); + + rotate(path, 45); + + return path; + }, + 'bpmn:CompensateEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_COMPENSATION', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.22, + my: 0.5 + } + }); + + var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor(event, defaultStrokeColor) + }); + }, + 'bpmn:SignalEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_SIGNAL', { + xScaleFactor: 0.9, + yScaleFactor: 0.9, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.5, + my: 0.2 + } + }); + + var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor(event, defaultStrokeColor) + }); + }, + 'bpmn:MultipleEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_MULTIPLE', { + xScaleFactor: 1.1, + yScaleFactor: 1.1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.222, + my: 0.36 + } + }); + + var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill + }); + }, + 'bpmn:ParallelMultipleEventDefinition': function(parentGfx, event) { + var pathData = pathMap.getScaledPath('EVENT_PARALLEL_MULTIPLE', { + xScaleFactor: 1.2, + yScaleFactor: 1.2, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.458, + my: 0.194 + } + }); + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getStrokeColor(event, defaultStrokeColor), + stroke: getStrokeColor(event, defaultStrokeColor) + }); + }, + 'bpmn:EndEvent': function(parentGfx, element) { + var circle = renderer('bpmn:Event')(parentGfx, element, { + strokeWidth: 4, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + renderEventContent(element, parentGfx); + + return circle; + }, + 'bpmn:TerminateEventDefinition': function(parentGfx, element) { + var circle = drawCircle(parentGfx, element.width, element.height, 8, { + strokeWidth: 4, + fill: getStrokeColor(element, defaultStrokeColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return circle; + }, + 'bpmn:IntermediateEvent': function(parentGfx, element) { + var outer = renderer('bpmn:Event')(parentGfx, element, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + /* inner */ + drawCircle(parentGfx, element.width, element.height, INNER_OUTER_DIST, { + strokeWidth: 1, + fill: getFillColor(element, 'none'), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + renderEventContent(element, parentGfx); + + return outer; + }, + 'bpmn:IntermediateCatchEvent': as('bpmn:IntermediateEvent'), + 'bpmn:IntermediateThrowEvent': as('bpmn:IntermediateEvent'), + + 'bpmn:Activity': function(parentGfx, element, attrs) { + + attrs = attrs || {}; + + if (!('fillOpacity' in attrs)) { + attrs.fillOpacity = DEFAULT_FILL_OPACITY; + } + + return drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS, attrs); + }, + + 'bpmn:Task': function(parentGfx, element) { + var attrs = { + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + var rect = renderer('bpmn:Activity')(parentGfx, element, attrs); + + renderEmbeddedLabel(parentGfx, element, 'center-middle'); + attachTaskMarkers(parentGfx, element); + + return rect; + }, + 'bpmn:ServiceTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var pathDataBG = pathMap.getScaledPath('TASK_TYPE_SERVICE', { + abspos: { + x: 12, + y: 18 + } + }); + + /* service bg */ drawPath(parentGfx, pathDataBG, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var fillPathData = pathMap.getScaledPath('TASK_TYPE_SERVICE_FILL', { + abspos: { + x: 17.2, + y: 18 + } + }); + + /* service fill */ drawPath(parentGfx, fillPathData, { + strokeWidth: 0, + fill: getFillColor(element, defaultFillColor) + }); + + var pathData = pathMap.getScaledPath('TASK_TYPE_SERVICE', { + abspos: { + x: 17, + y: 22 + } + }); + + /* service */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:UserTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var x = 15; + var y = 12; + + var pathData = pathMap.getScaledPath('TASK_TYPE_USER_1', { + abspos: { + x: x, + y: y + } + }); + + /* user path */ drawPath(parentGfx, pathData, { + strokeWidth: 0.5, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var pathData2 = pathMap.getScaledPath('TASK_TYPE_USER_2', { + abspos: { + x: x, + y: y + } + }); + + /* user2 path */ drawPath(parentGfx, pathData2, { + strokeWidth: 0.5, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var pathData3 = pathMap.getScaledPath('TASK_TYPE_USER_3', { + abspos: { + x: x, + y: y + } + }); + + /* user3 path */ drawPath(parentGfx, pathData3, { + strokeWidth: 0.5, + fill: getStrokeColor(element, defaultStrokeColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:ManualTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var pathData = pathMap.getScaledPath('TASK_TYPE_MANUAL', { + abspos: { + x: 17, + y: 15 + } + }); + + /* manual path */ drawPath(parentGfx, pathData, { + strokeWidth: 0.5, // 0.25, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:SendTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var pathData = pathMap.getScaledPath('TASK_TYPE_SEND', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: 21, + containerHeight: 14, + position: { + mx: 0.285, + my: 0.357 + } + }); + + /* send path */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getStrokeColor(element, defaultStrokeColor), + stroke: getFillColor(element, defaultFillColor) + }); + + return task; + }, + 'bpmn:ReceiveTask' : function(parentGfx, element) { + var semantic = getSemantic(element); + + var task = renderer('bpmn:Task')(parentGfx, element); + var pathData; + + if (semantic.instantiate) { + drawCircle(parentGfx, 28, 28, 20 * 0.22, { strokeWidth: 1 }); + + pathData = pathMap.getScaledPath('TASK_TYPE_INSTANTIATING_SEND', { + abspos: { + x: 7.77, + y: 9.52 + } + }); + } else { + + pathData = pathMap.getScaledPath('TASK_TYPE_SEND', { + xScaleFactor: 0.9, + yScaleFactor: 0.9, + containerWidth: 21, + containerHeight: 14, + position: { + mx: 0.3, + my: 0.4 + } + }); + } + + /* receive path */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:ScriptTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var pathData = pathMap.getScaledPath('TASK_TYPE_SCRIPT', { + abspos: { + x: 15, + y: 20 + } + }); + + /* script path */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:BusinessRuleTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var headerPathData = pathMap.getScaledPath('TASK_TYPE_BUSINESS_RULE_HEADER', { + abspos: { + x: 8, + y: 8 + } + }); + + var businessHeaderPath = drawPath(parentGfx, headerPathData); + attr$1(businessHeaderPath, { + strokeWidth: 1, + fill: getFillColor(element, '#aaaaaa'), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var headerData = pathMap.getScaledPath('TASK_TYPE_BUSINESS_RULE_MAIN', { + abspos: { + x: 8, + y: 8 + } + }); + + var businessPath = drawPath(parentGfx, headerData); + attr$1(businessPath, { + strokeWidth: 1, + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:SubProcess': function(parentGfx, element, attrs) { + attrs = assign({ + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }, attrs); + + var rect = renderer('bpmn:Activity')(parentGfx, element, attrs); + + var expanded = isExpanded(element); + + if (isEventSubProcess(element)) { + attr$1(rect, { + strokeDasharray: '1,2' + }); + } + + renderEmbeddedLabel(parentGfx, element, expanded ? 'center-top' : 'center-middle'); + + if (expanded) { + attachTaskMarkers(parentGfx, element); + } else { + attachTaskMarkers(parentGfx, element, [ 'SubProcessMarker' ]); + } + + return rect; + }, + 'bpmn:AdHocSubProcess': function(parentGfx, element) { + return renderer('bpmn:SubProcess')(parentGfx, element); + }, + 'bpmn:Transaction': function(parentGfx, element) { + var outer = renderer('bpmn:SubProcess')(parentGfx, element); + + var innerAttrs = styles.style([ 'no-fill', 'no-events' ], { + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + /* inner path */ drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS - 2, INNER_OUTER_DIST, innerAttrs); + + return outer; + }, + 'bpmn:CallActivity': function(parentGfx, element) { + return renderer('bpmn:SubProcess')(parentGfx, element, { + strokeWidth: 5 + }); + }, + 'bpmn:Participant': function(parentGfx, element) { + + var attrs = { + fillOpacity: DEFAULT_FILL_OPACITY, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + var lane = renderer('bpmn:Lane')(parentGfx, element, attrs); + + var expandedPool = isExpanded(element); + + if (expandedPool) { + drawLine(parentGfx, [ + { x: 30, y: 0 }, + { x: 30, y: element.height } + ], { + stroke: getStrokeColor(element, defaultStrokeColor) + }); + var text = getSemantic(element).name; + renderLaneLabel(parentGfx, text, element); + } else { + + // Collapsed pool draw text inline + var text2 = getSemantic(element).name; + renderLabel(parentGfx, text2, { + box: element, align: 'center-middle', + style: { + fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor) + } + }); + } + + var participantMultiplicity = !!(getSemantic(element).participantMultiplicity); + + if (participantMultiplicity) { + renderer('ParticipantMultiplicityMarker')(parentGfx, element); + } + + return lane; + }, + 'bpmn:Lane': function(parentGfx, element, attrs) { + var rect = drawRect(parentGfx, element.width, element.height, 0, assign({ + fill: getFillColor(element, defaultFillColor), + fillOpacity: HIGH_FILL_OPACITY, + stroke: getStrokeColor(element, defaultStrokeColor) + }, attrs)); + + var semantic = getSemantic(element); + + if (semantic.$type === 'bpmn:Lane') { + var text = semantic.name; + renderLaneLabel(parentGfx, text, element); + } + + return rect; + }, + 'bpmn:InclusiveGateway': function(parentGfx, element) { + var diamond = renderer('bpmn:Gateway')(parentGfx, element); + + /* circle path */ + drawCircle(parentGfx, element.width, element.height, element.height * 0.24, { + strokeWidth: 2.5, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return diamond; + }, + 'bpmn:ExclusiveGateway': function(parentGfx, element) { + var diamond = renderer('bpmn:Gateway')(parentGfx, element); + + var pathData = pathMap.getScaledPath('GATEWAY_EXCLUSIVE', { + xScaleFactor: 0.4, + yScaleFactor: 0.4, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.32, + my: 0.3 + } + }); + + if ((getDi(element).isMarkerVisible)) { + drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getStrokeColor(element, defaultStrokeColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + } + + return diamond; + }, + 'bpmn:ComplexGateway': function(parentGfx, element) { + var diamond = renderer('bpmn:Gateway')(parentGfx, element); + + var pathData = pathMap.getScaledPath('GATEWAY_COMPLEX', { + xScaleFactor: 0.5, + yScaleFactor:0.5, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.46, + my: 0.26 + } + }); + + /* complex path */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getStrokeColor(element, defaultStrokeColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return diamond; + }, + 'bpmn:ParallelGateway': function(parentGfx, element) { + var diamond = renderer('bpmn:Gateway')(parentGfx, element); + + var pathData = pathMap.getScaledPath('GATEWAY_PARALLEL', { + xScaleFactor: 0.6, + yScaleFactor:0.6, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.46, + my: 0.2 + } + }); + + /* parallel path */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getStrokeColor(element, defaultStrokeColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return diamond; + }, + 'bpmn:EventBasedGateway': function(parentGfx, element) { + + var semantic = getSemantic(element); + + var diamond = renderer('bpmn:Gateway')(parentGfx, element); + + /* outer circle path */ drawCircle(parentGfx, element.width, element.height, element.height * 0.20, { + strokeWidth: 1, + fill: 'none', + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var type = semantic.eventGatewayType; + var instantiate = !!semantic.instantiate; + + function drawEvent() { + + var pathData = pathMap.getScaledPath('GATEWAY_EVENT_BASED', { + xScaleFactor: 0.18, + yScaleFactor: 0.18, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.36, + my: 0.44 + } + }); + + var attrs = { + strokeWidth: 2, + fill: getFillColor(element, 'none'), + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + /* event path */ drawPath(parentGfx, pathData, attrs); + } + + if (type === 'Parallel') { + + var pathData = pathMap.getScaledPath('GATEWAY_PARALLEL', { + xScaleFactor: 0.4, + yScaleFactor:0.4, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.474, + my: 0.296 + } + }); + + var parallelPath = drawPath(parentGfx, pathData); + attr$1(parallelPath, { + strokeWidth: 1, + fill: 'none' + }); + } else if (type === 'Exclusive') { + + if (!instantiate) { + var innerCircle = drawCircle(parentGfx, element.width, element.height, element.height * 0.26); + attr$1(innerCircle, { + strokeWidth: 1, + fill: 'none', + stroke: getStrokeColor(element, defaultStrokeColor) + }); + } + + drawEvent(); + } + + + return diamond; + }, + 'bpmn:Gateway': function(parentGfx, element) { + var attrs = { + fill: getFillColor(element, defaultFillColor), + fillOpacity: DEFAULT_FILL_OPACITY, + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + return drawDiamond(parentGfx, element.width, element.height, attrs); + }, + 'bpmn:SequenceFlow': function(parentGfx, element) { + var pathData = createPathFromConnection(element); + + var fill = getFillColor(element, defaultFillColor), + stroke = getStrokeColor(element, defaultStrokeColor); + + var attrs = { + strokeLinejoin: 'round', + markerEnd: marker('sequenceflow-end', fill, stroke), + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + var path = drawPath(parentGfx, pathData, attrs); + + var sequenceFlow = getSemantic(element); + + var source; + + if (element.source) { + source = element.source.businessObject; + + // conditional flow marker + if (sequenceFlow.conditionExpression && source.$instanceOf('bpmn:Activity')) { + attr$1(path, { + markerStart: marker('conditional-flow-marker', fill, stroke) + }); + } + + // default marker + if (source.default && (source.$instanceOf('bpmn:Gateway') || source.$instanceOf('bpmn:Activity')) && + source.default === sequenceFlow) { + attr$1(path, { + markerStart: marker('conditional-default-flow-marker', fill, stroke) + }); + } + } + + return path; + }, + 'bpmn:Association': function(parentGfx, element, attrs) { + + var semantic = getSemantic(element); + + var fill = getFillColor(element, defaultFillColor), + stroke = getStrokeColor(element, defaultStrokeColor); + + attrs = assign({ + strokeDasharray: '0.5, 5', + strokeLinecap: 'round', + strokeLinejoin: 'round', + stroke: getStrokeColor(element, defaultStrokeColor) + }, attrs || {}); + + if (semantic.associationDirection === 'One' || + semantic.associationDirection === 'Both') { + attrs.markerEnd = marker('association-end', fill, stroke); + } + + if (semantic.associationDirection === 'Both') { + attrs.markerStart = marker('association-start', fill, stroke); + } + + return drawLine(parentGfx, element.waypoints, attrs); + }, + 'bpmn:DataInputAssociation': function(parentGfx, element) { + var fill = getFillColor(element, defaultFillColor), + stroke = getStrokeColor(element, defaultStrokeColor); + + return renderer('bpmn:Association')(parentGfx, element, { + markerEnd: marker('association-end', fill, stroke) + }); + }, + 'bpmn:DataOutputAssociation': function(parentGfx, element) { + var fill = getFillColor(element, defaultFillColor), + stroke = getStrokeColor(element, defaultStrokeColor); + + return renderer('bpmn:Association')(parentGfx, element, { + markerEnd: marker('association-end', fill, stroke) + }); + }, + 'bpmn:MessageFlow': function(parentGfx, element) { + + var semantic = getSemantic(element), + di = getDi(element); + + var fill = getFillColor(element, defaultFillColor), + stroke = getStrokeColor(element, defaultStrokeColor); + + var pathData = createPathFromConnection(element); + + var attrs = { + markerEnd: marker('messageflow-end', fill, stroke), + markerStart: marker('messageflow-start', fill, stroke), + strokeDasharray: '10, 12', + strokeLinecap: 'round', + strokeLinejoin: 'round', + strokeWidth: '1.5px', + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + var path = drawPath(parentGfx, pathData, attrs); + + if (semantic.messageRef) { + var midPoint = path.getPointAtLength(path.getTotalLength() / 2); + + var markerPathData = pathMap.getScaledPath('MESSAGE_FLOW_MARKER', { + abspos: { + x: midPoint.x, + y: midPoint.y + } + }); + + var messageAttrs = { strokeWidth: 1 }; + + if (di.messageVisibleKind === 'initiating') { + messageAttrs.fill = 'white'; + messageAttrs.stroke = black; + } else { + messageAttrs.fill = '#888'; + messageAttrs.stroke = 'white'; + } + + var message = drawPath(parentGfx, markerPathData, messageAttrs); + + var labelText = semantic.messageRef.name; + var label = renderLabel(parentGfx, labelText, { + align: 'center-top', + fitBox: true, + style: { + fill: getStrokeColor(element, defaultLabelColor) + } + }); + + var messageBounds = message.getBBox(), + labelBounds = label.getBBox(); + + var translateX = midPoint.x - labelBounds.width / 2, + translateY = midPoint.y + messageBounds.height / 2 + ELEMENT_LABEL_DISTANCE; + + transform(label, translateX, translateY, 0); + + } + + return path; + }, + 'bpmn:DataObject': function(parentGfx, element) { + var pathData = pathMap.getScaledPath('DATA_OBJECT_PATH', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.474, + my: 0.296 + } + }); + + var elementObject = drawPath(parentGfx, pathData, { + fill: getFillColor(element, defaultFillColor), + fillOpacity: DEFAULT_FILL_OPACITY, + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var semantic = getSemantic(element); + + if (isCollection(semantic)) { + renderDataItemCollection(parentGfx, element); + } + + return elementObject; + }, + 'bpmn:DataObjectReference': as('bpmn:DataObject'), + 'bpmn:DataInput': function(parentGfx, element) { + + var arrowPathData = pathMap.getRawPath('DATA_ARROW'); + + // page + var elementObject = renderer('bpmn:DataObject')(parentGfx, element); + + /* input arrow path */ drawPath(parentGfx, arrowPathData, { strokeWidth: 1 }); + + return elementObject; + }, + 'bpmn:DataOutput': function(parentGfx, element) { + var arrowPathData = pathMap.getRawPath('DATA_ARROW'); + + // page + var elementObject = renderer('bpmn:DataObject')(parentGfx, element); + + /* output arrow path */ drawPath(parentGfx, arrowPathData, { + strokeWidth: 1, + fill: black + }); + + return elementObject; + }, + 'bpmn:DataStoreReference': function(parentGfx, element) { + var DATA_STORE_PATH = pathMap.getScaledPath('DATA_STORE', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0, + my: 0.133 + } + }); + + var elementStore = drawPath(parentGfx, DATA_STORE_PATH, { + strokeWidth: 2, + fill: getFillColor(element, defaultFillColor), + fillOpacity: DEFAULT_FILL_OPACITY, + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return elementStore; + }, + 'bpmn:BoundaryEvent': function(parentGfx, element) { + + var semantic = getSemantic(element), + cancel = semantic.cancelActivity; + + var attrs = { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + if (!cancel) { + attrs.strokeDasharray = '6'; + attrs.strokeLinecap = 'round'; + } + + // apply fillOpacity + var outerAttrs = assign({}, attrs, { + fillOpacity: 1 + }); + + // apply no-fill + var innerAttrs = assign({}, attrs, { + fill: 'none' + }); + + var outer = renderer('bpmn:Event')(parentGfx, element, outerAttrs); + + /* inner path */ drawCircle(parentGfx, element.width, element.height, INNER_OUTER_DIST, innerAttrs); + + renderEventContent(element, parentGfx); + + return outer; + }, + 'bpmn:Group': function(parentGfx, element) { + + var group = drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS, { + stroke: getStrokeColor(element, defaultStrokeColor), + strokeWidth: 1, + strokeDasharray: '8,3,1,3', + fill: 'none', + pointerEvents: 'none' + }); + + return group; + }, + 'label': function(parentGfx, element) { + return renderExternalLabel(parentGfx, element); + }, + 'bpmn:TextAnnotation': function(parentGfx, element) { + var style = { + 'fill': 'none', + 'stroke': 'none' + }; + + var textElement = drawRect(parentGfx, element.width, element.height, 0, 0, style); + + var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.0, + my: 0.0 + } + }); + + drawPath(parentGfx, textPathData, { + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var text = getSemantic(element).text || ''; + renderLabel(parentGfx, text, { + box: element, + align: 'left-top', + padding: 5, + style: { + fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor) + } + }); + + return textElement; + }, + 'ParticipantMultiplicityMarker': function(parentGfx, element) { + var markerPath = pathMap.getScaledPath('MARKER_PARALLEL', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2) / element.width), + my: (element.height - 15) / element.height + } + }); + + drawMarker('participant-multiplicity', parentGfx, markerPath, { + strokeWidth: 2, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + }, + 'SubProcessMarker': function(parentGfx, element) { + var markerRect = drawRect(parentGfx, 14, 14, 0, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + // Process marker is placed in the middle of the box + // therefore fixed values can be used here + translate$1(markerRect, element.width / 2 - 7.5, element.height - 20); + + var markerPath = pathMap.getScaledPath('MARKER_SUB_PROCESS', { + xScaleFactor: 1.5, + yScaleFactor: 1.5, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: (element.width / 2 - 7.5) / element.width, + my: (element.height - 20) / element.height + } + }); + + drawMarker('sub-process', parentGfx, markerPath, { + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + }, + 'ParallelMarker': function(parentGfx, element, position) { + var markerPath = pathMap.getScaledPath('MARKER_PARALLEL', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2 + position.parallel) / element.width), + my: (element.height - 20) / element.height + } + }); + + drawMarker('parallel', parentGfx, markerPath, { + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + }, + 'SequentialMarker': function(parentGfx, element, position) { + var markerPath = pathMap.getScaledPath('MARKER_SEQUENTIAL', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2 + position.seq) / element.width), + my: (element.height - 19) / element.height + } + }); + + drawMarker('sequential', parentGfx, markerPath, { + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + }, + 'CompensationMarker': function(parentGfx, element, position) { + var markerMath = pathMap.getScaledPath('MARKER_COMPENSATION', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2 + position.compensation) / element.width), + my: (element.height - 13) / element.height + } + }); + + drawMarker('compensation', parentGfx, markerMath, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + }, + 'LoopMarker': function(parentGfx, element, position) { + var markerPath = pathMap.getScaledPath('MARKER_LOOP', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2 + position.loop) / element.width), + my: (element.height - 7) / element.height + } + }); + + drawMarker('loop', parentGfx, markerPath, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor), + strokeLinecap: 'round', + strokeMiterlimit: 0.5 + }); + }, + 'AdhocMarker': function(parentGfx, element, position) { + var markerPath = pathMap.getScaledPath('MARKER_ADHOC', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2 + position.adhoc) / element.width), + my: (element.height - 15) / element.height + } + }); + + drawMarker('adhoc', parentGfx, markerPath, { + strokeWidth: 1, + fill: getStrokeColor(element, defaultStrokeColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + } + }; + + function attachTaskMarkers(parentGfx, element, taskMarkers) { + var obj = getSemantic(element); + + var subprocess = taskMarkers && taskMarkers.indexOf('SubProcessMarker') !== -1; + var position; + + if (subprocess) { + position = { + seq: -21, + parallel: -22, + compensation: -42, + loop: -18, + adhoc: 10 + }; + } else { + position = { + seq: -3, + parallel: -6, + compensation: -27, + loop: 0, + adhoc: 10 + }; + } + + forEach$1(taskMarkers, function(marker) { + renderer(marker)(parentGfx, element, position); + }); + + if (obj.isForCompensation) { + renderer('CompensationMarker')(parentGfx, element, position); + } + + if (obj.$type === 'bpmn:AdHocSubProcess') { + renderer('AdhocMarker')(parentGfx, element, position); + } + + var loopCharacteristics = obj.loopCharacteristics, + isSequential = loopCharacteristics && loopCharacteristics.isSequential; + + if (loopCharacteristics) { + + if (isSequential === undefined) { + renderer('LoopMarker')(parentGfx, element, position); + } + + if (isSequential === false) { + renderer('ParallelMarker')(parentGfx, element, position); + } + + if (isSequential === true) { + renderer('SequentialMarker')(parentGfx, element, position); + } + } + } + + function renderDataItemCollection(parentGfx, element) { + + var yPosition = (element.height - 18) / element.height; + + var pathData = pathMap.getScaledPath('DATA_OBJECT_COLLECTION_PATH', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.33, + my: yPosition + } + }); + + /* collection path */ drawPath(parentGfx, pathData, { + strokeWidth: 2 + }); + } + + + // extension API, use at your own risk + this._drawPath = drawPath; + + this._renderer = renderer; + } + + + e(BpmnRenderer, BaseRenderer); + + BpmnRenderer.$inject = [ + 'config.bpmnRenderer', + 'eventBus', + 'styles', + 'pathMap', + 'canvas', + 'textRenderer' + ]; + + + BpmnRenderer.prototype.canRender = function(element) { + return is$1(element, 'bpmn:BaseElement'); + }; + + BpmnRenderer.prototype.drawShape = function(parentGfx, element) { + var type = element.type; + var h = this._renderer(type); + + /* jshint -W040 */ + return h(parentGfx, element); + }; + + BpmnRenderer.prototype.drawConnection = function(parentGfx, element) { + var type = element.type; + var h = this._renderer(type); + + /* jshint -W040 */ + return h(parentGfx, element); + }; + + BpmnRenderer.prototype.getShapePath = function(element) { + + if (is$1(element, 'bpmn:Event')) { + return getCirclePath(element); + } + + if (is$1(element, 'bpmn:Activity')) { + return getRoundRectPath(element, TASK_BORDER_RADIUS); + } + + if (is$1(element, 'bpmn:Gateway')) { + return getDiamondPath(element); + } + + return getRectPath(element); + }; + + var DEFAULT_BOX_PADDING = 0; + + var DEFAULT_LABEL_SIZE$1 = { + width: 150, + height: 50 + }; + + + function parseAlign(align) { + + var parts = align.split('-'); + + return { + horizontal: parts[0] || 'center', + vertical: parts[1] || 'top' + }; + } + + function parsePadding(padding) { + + if (isObject(padding)) { + return assign({ top: 0, left: 0, right: 0, bottom: 0 }, padding); + } else { + return { + top: padding, + left: padding, + right: padding, + bottom: padding + }; + } + } + + function getTextBBox(text, fakeText) { + + fakeText.textContent = text; + + var textBBox; + + try { + var bbox, + emptyLine = text === ''; + + // add dummy text, when line is empty to + // determine correct height + fakeText.textContent = emptyLine ? 'dummy' : text; + + textBBox = fakeText.getBBox(); + + // take text rendering related horizontal + // padding into account + bbox = { + width: textBBox.width + textBBox.x * 2, + height: textBBox.height + }; + + if (emptyLine) { + + // correct width + bbox.width = 0; + } + + return bbox; + } catch (e) { + return { width: 0, height: 0 }; + } + } + + + /** + * Layout the next line and return the layouted element. + * + * Alters the lines passed. + * + * @param {Array} lines + * @return {Object} the line descriptor, an object { width, height, text } + */ + function layoutNext(lines, maxWidth, fakeText) { + + var originalLine = lines.shift(), + fitLine = originalLine; + + var textBBox; + + for (;;) { + textBBox = getTextBBox(fitLine, fakeText); + + textBBox.width = fitLine ? textBBox.width : 0; + + // try to fit + if (fitLine === ' ' || fitLine === '' || textBBox.width < Math.round(maxWidth) || fitLine.length < 2) { + return fit(lines, fitLine, originalLine, textBBox); + } + + fitLine = shortenLine(fitLine, textBBox.width, maxWidth); + } + } + + function fit(lines, fitLine, originalLine, textBBox) { + if (fitLine.length < originalLine.length) { + var remainder = originalLine.slice(fitLine.length).trim(); + + lines.unshift(remainder); + } + + return { + width: textBBox.width, + height: textBBox.height, + text: fitLine + }; + } + + var SOFT_BREAK = '\u00AD'; + + + /** + * Shortens a line based on spacing and hyphens. + * Returns the shortened result on success. + * + * @param {string} line + * @param {number} maxLength the maximum characters of the string + * @return {string} the shortened string + */ + function semanticShorten(line, maxLength) { + + var parts = line.split(/(\s|-|\u00AD)/g), + part, + shortenedParts = [], + length = 0; + + // try to shorten via break chars + if (parts.length > 1) { + + while ((part = parts.shift())) { + if (part.length + length < maxLength) { + shortenedParts.push(part); + length += part.length; + } else { + + // remove previous part, too if hyphen does not fit anymore + if (part === '-' || part === SOFT_BREAK) { + shortenedParts.pop(); + } + + break; + } + } + } + + var last = shortenedParts[shortenedParts.length - 1]; + + // translate trailing soft break to actual hyphen + if (last && last === SOFT_BREAK) { + shortenedParts[shortenedParts.length - 1] = '-'; + } + + return shortenedParts.join(''); + } + + + function shortenLine(line, width, maxWidth) { + var length = Math.max(line.length * (maxWidth / width), 1); + + // try to shorten semantically (i.e. based on spaces and hyphens) + var shortenedLine = semanticShorten(line, length); + + if (!shortenedLine) { + + // force shorten by cutting the long word + shortenedLine = line.slice(0, Math.max(Math.round(length - 1), 1)); + } + + return shortenedLine; + } + + + function getHelperSvg() { + var helperSvg = document.getElementById('helper-svg'); + + if (!helperSvg) { + helperSvg = create$1('svg'); + + attr$1(helperSvg, { + id: 'helper-svg' + }); + + assign$1(helperSvg, { + visibility: 'hidden', + position: 'fixed', + width: 0, + height: 0 + }); + + document.body.appendChild(helperSvg); + } + + return helperSvg; + } + + + /** + * Creates a new label utility + * + * @param {Object} config + * @param {Dimensions} config.size + * @param {number} config.padding + * @param {Object} config.style + * @param {string} config.align + */ + function Text(config) { + + this._config = assign({}, { + size: DEFAULT_LABEL_SIZE$1, + padding: DEFAULT_BOX_PADDING, + style: {}, + align: 'center-top' + }, config || {}); + } + + /** + * Returns the layouted text as an SVG element. + * + * @param {string} text + * @param {Object} options + * + * @return {SVGElement} + */ + Text.prototype.createText = function(text, options) { + return this.layoutText(text, options).element; + }; + + /** + * Returns a labels layouted dimensions. + * + * @param {string} text to layout + * @param {Object} options + * + * @return {Dimensions} + */ + Text.prototype.getDimensions = function(text, options) { + return this.layoutText(text, options).dimensions; + }; + + /** + * Creates and returns a label and its bounding box. + * + * @method Text#createText + * + * @param {string} text the text to render on the label + * @param {Object} options + * @param {string} options.align how to align in the bounding box. + * Any of { 'center-middle', 'center-top' }, + * defaults to 'center-top'. + * @param {string} options.style style to be applied to the text + * @param {boolean} options.fitBox indicates if box will be recalculated to + * fit text + * + * @return {Object} { element, dimensions } + */ + Text.prototype.layoutText = function(text, options) { + var box = assign({}, this._config.size, options.box), + style = assign({}, this._config.style, options.style), + align = parseAlign(options.align || this._config.align), + padding = parsePadding(options.padding !== undefined ? options.padding : this._config.padding), + fitBox = options.fitBox || false; + + var lineHeight = getLineHeight(style); + + // we split text by lines and normalize + // {soft break} + {line break} => { line break } + var lines = text.split(/\u00AD?\r?\n/), + layouted = []; + + var maxWidth = box.width - padding.left - padding.right; + + // ensure correct rendering by attaching helper text node to invisible SVG + var helperText = create$1('text'); + attr$1(helperText, { x: 0, y: 0 }); + attr$1(helperText, style); + + var helperSvg = getHelperSvg(); + + append(helperSvg, helperText); + + while (lines.length) { + layouted.push(layoutNext(lines, maxWidth, helperText)); + } + + if (align.vertical === 'middle') { + padding.top = padding.bottom = 0; + } + + var totalHeight = reduce(layouted, function(sum, line, idx) { + return sum + (lineHeight || line.height); + }, 0) + padding.top + padding.bottom; + + var maxLineWidth = reduce(layouted, function(sum, line, idx) { + return line.width > sum ? line.width : sum; + }, 0); + + // the y position of the next line + var y = padding.top; + + if (align.vertical === 'middle') { + y += (box.height - totalHeight) / 2; + } + + // magic number initial offset + y -= (lineHeight || layouted[0].height) / 4; + + + var textElement = create$1('text'); + + attr$1(textElement, style); + + // layout each line taking into account that parent + // shape might resize to fit text size + forEach$1(layouted, function(line) { + + var x; + + y += (lineHeight || line.height); + + switch (align.horizontal) { + case 'left': + x = padding.left; + break; + + case 'right': + x = ((fitBox ? maxLineWidth : maxWidth) + - padding.right - line.width); + break; + + default: + + // aka center + x = Math.max((((fitBox ? maxLineWidth : maxWidth) + - line.width) / 2 + padding.left), 0); + } + + var tspan = create$1('tspan'); + attr$1(tspan, { x: x, y: y }); + + tspan.textContent = line.text; + + append(textElement, tspan); + }); + + remove$2(helperText); + + var dimensions = { + width: maxLineWidth, + height: totalHeight + }; + + return { + dimensions: dimensions, + element: textElement + }; + }; + + + function getLineHeight(style) { + if ('fontSize' in style && 'lineHeight' in style) { + return style.lineHeight * parseInt(style.fontSize, 10); + } + } + + var DEFAULT_FONT_SIZE = 12; + var LINE_HEIGHT_RATIO = 1.2; + + var MIN_TEXT_ANNOTATION_HEIGHT = 30; + + + function TextRenderer(config) { + + var defaultStyle = assign({ + fontFamily: 'Arial, sans-serif', + fontSize: DEFAULT_FONT_SIZE, + fontWeight: 'normal', + lineHeight: LINE_HEIGHT_RATIO + }, config && config.defaultStyle || {}); + + var fontSize = parseInt(defaultStyle.fontSize, 10) - 1; + + var externalStyle = assign({}, defaultStyle, { + fontSize: fontSize + }, config && config.externalStyle || {}); + + var textUtil = new Text({ + style: defaultStyle + }); + + /** + * Get the new bounds of an externally rendered, + * layouted label. + * + * @param {Bounds} bounds + * @param {string} text + * + * @return {Bounds} + */ + this.getExternalLabelBounds = function(bounds, text) { + + var layoutedDimensions = textUtil.getDimensions(text, { + box: { + width: 90, + height: 30, + x: bounds.width / 2 + bounds.x, + y: bounds.height / 2 + bounds.y + }, + style: externalStyle + }); + + // resize label shape to fit label text + return { + x: Math.round(bounds.x + bounds.width / 2 - layoutedDimensions.width / 2), + y: Math.round(bounds.y), + width: Math.ceil(layoutedDimensions.width), + height: Math.ceil(layoutedDimensions.height) + }; + + }; + + /** + * Get the new bounds of text annotation. + * + * @param {Bounds} bounds + * @param {string} text + * + * @return {Bounds} + */ + this.getTextAnnotationBounds = function(bounds, text) { + + var layoutedDimensions = textUtil.getDimensions(text, { + box: bounds, + style: defaultStyle, + align: 'left-top', + padding: 5 + }); + + return { + x: bounds.x, + y: bounds.y, + width: bounds.width, + height: Math.max(MIN_TEXT_ANNOTATION_HEIGHT, Math.round(layoutedDimensions.height)) + }; + }; + + /** + * Create a layouted text element. + * + * @param {string} text + * @param {Object} [options] + * + * @return {SVGElement} rendered text + */ + this.createText = function(text, options) { + return textUtil.createText(text, options || {}); + }; + + /** + * Get default text style. + */ + this.getDefaultStyle = function() { + return defaultStyle; + }; + + /** + * Get the external text style. + */ + this.getExternalStyle = function() { + return externalStyle; + }; + + } + + TextRenderer.$inject = [ + 'config.textRenderer' + ]; + + /** + * Map containing SVG paths needed by BpmnRenderer. + */ + + function PathMap() { + + /** + * Contains a map of path elements + * + *

          Path definition

          + * A parameterized path is defined like this: + *
          +     * 'GATEWAY_PARALLEL': {
          +     *   d: 'm {mx},{my} {e.x0},0 0,{e.x1} {e.x1},0 0,{e.y0} -{e.x1},0 0,{e.y1} ' +
          +            '-{e.x0},0 0,-{e.y1} -{e.x1},0 0,-{e.y0} {e.x1},0 z',
          +     *   height: 17.5,
          +     *   width:  17.5,
          +     *   heightElements: [2.5, 7.5],
          +     *   widthElements: [2.5, 7.5]
          +     * }
          +     * 
          + *

          It's important to specify a correct height and width for the path as the scaling + * is based on the ratio between the specified height and width in this object and the + * height and width that is set as scale target (Note x,y coordinates will be scaled with + * individual ratios).

          + *

          The 'heightElements' and 'widthElements' array must contain the values that will be scaled. + * The scaling is based on the computed ratios. + * Coordinates on the y axis should be in the heightElement's array, they will be scaled using + * the computed ratio coefficient. + * In the parameterized path the scaled values can be accessed through the 'e' object in {} brackets. + *

            + *
          • The values for the y axis can be accessed in the path string using {e.y0}, {e.y1}, ....
          • + *
          • The values for the x axis can be accessed in the path string using {e.x0}, {e.x1}, ....
          • + *
          + * The numbers x0, x1 respectively y0, y1, ... map to the corresponding array index. + *

          + */ + this.pathMap = { + 'EVENT_MESSAGE': { + d: 'm {mx},{my} l 0,{e.y1} l {e.x1},0 l 0,-{e.y1} z l {e.x0},{e.y0} l {e.x0},-{e.y0}', + height: 36, + width: 36, + heightElements: [ 6, 14 ], + widthElements: [ 10.5, 21 ] + }, + 'EVENT_SIGNAL': { + d: 'M {mx},{my} l {e.x0},{e.y0} l -{e.x1},0 Z', + height: 36, + width: 36, + heightElements: [ 18 ], + widthElements: [ 10, 20 ] + }, + 'EVENT_ESCALATION': { + d: 'M {mx},{my} l {e.x0},{e.y0} l -{e.x0},-{e.y1} l -{e.x0},{e.y1} Z', + height: 36, + width: 36, + heightElements: [ 20, 7 ], + widthElements: [ 8 ] + }, + 'EVENT_CONDITIONAL': { + d: 'M {e.x0},{e.y0} l {e.x1},0 l 0,{e.y2} l -{e.x1},0 Z ' + + 'M {e.x2},{e.y3} l {e.x0},0 ' + + 'M {e.x2},{e.y4} l {e.x0},0 ' + + 'M {e.x2},{e.y5} l {e.x0},0 ' + + 'M {e.x2},{e.y6} l {e.x0},0 ' + + 'M {e.x2},{e.y7} l {e.x0},0 ' + + 'M {e.x2},{e.y8} l {e.x0},0 ', + height: 36, + width: 36, + heightElements: [ 8.5, 14.5, 18, 11.5, 14.5, 17.5, 20.5, 23.5, 26.5 ], + widthElements: [ 10.5, 14.5, 12.5 ] + }, + 'EVENT_LINK': { + d: 'm {mx},{my} 0,{e.y0} -{e.x1},0 0,{e.y1} {e.x1},0 0,{e.y0} {e.x0},-{e.y2} -{e.x0},-{e.y2} z', + height: 36, + width: 36, + heightElements: [ 4.4375, 6.75, 7.8125 ], + widthElements: [ 9.84375, 13.5 ] + }, + 'EVENT_ERROR': { + d: 'm {mx},{my} {e.x0},-{e.y0} {e.x1},-{e.y1} {e.x2},{e.y2} {e.x3},-{e.y3} -{e.x4},{e.y4} -{e.x5},-{e.y5} z', + height: 36, + width: 36, + heightElements: [ 0.023, 8.737, 8.151, 16.564, 10.591, 8.714 ], + widthElements: [ 0.085, 6.672, 6.97, 4.273, 5.337, 6.636 ] + }, + 'EVENT_CANCEL_45': { + d: 'm {mx},{my} -{e.x1},0 0,{e.x0} {e.x1},0 0,{e.y1} {e.x0},0 ' + + '0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z', + height: 36, + width: 36, + heightElements: [ 4.75, 8.5 ], + widthElements: [ 4.75, 8.5 ] + }, + 'EVENT_COMPENSATION': { + d: 'm {mx},{my} {e.x0},-{e.y0} 0,{e.y1} z m {e.x1},-{e.y2} {e.x2},-{e.y3} 0,{e.y1} -{e.x2},-{e.y3} z', + height: 36, + width: 36, + heightElements: [ 6.5, 13, 0.4, 6.1 ], + widthElements: [ 9, 9.3, 8.7 ] + }, + 'EVENT_TIMER_WH': { + d: 'M {mx},{my} l {e.x0},-{e.y0} m -{e.x0},{e.y0} l {e.x1},{e.y1} ', + height: 36, + width: 36, + heightElements: [ 10, 2 ], + widthElements: [ 3, 7 ] + }, + 'EVENT_TIMER_LINE': { + d: 'M {mx},{my} ' + + 'm {e.x0},{e.y0} l -{e.x1},{e.y1} ', + height: 36, + width: 36, + heightElements: [ 10, 3 ], + widthElements: [ 0, 0 ] + }, + 'EVENT_MULTIPLE': { + d:'m {mx},{my} {e.x1},-{e.y0} {e.x1},{e.y0} -{e.x0},{e.y1} -{e.x2},0 z', + height: 36, + width: 36, + heightElements: [ 6.28099, 12.56199 ], + widthElements: [ 3.1405, 9.42149, 12.56198 ] + }, + 'EVENT_PARALLEL_MULTIPLE': { + d:'m {mx},{my} {e.x0},0 0,{e.y1} {e.x1},0 0,{e.y0} -{e.x1},0 0,{e.y1} ' + + '-{e.x0},0 0,-{e.y1} -{e.x1},0 0,-{e.y0} {e.x1},0 z', + height: 36, + width: 36, + heightElements: [ 2.56228, 7.68683 ], + widthElements: [ 2.56228, 7.68683 ] + }, + 'GATEWAY_EXCLUSIVE': { + d:'m {mx},{my} {e.x0},{e.y0} {e.x1},{e.y0} {e.x2},0 {e.x4},{e.y2} ' + + '{e.x4},{e.y1} {e.x2},0 {e.x1},{e.y3} {e.x0},{e.y3} ' + + '{e.x3},0 {e.x5},{e.y1} {e.x5},{e.y2} {e.x3},0 z', + height: 17.5, + width: 17.5, + heightElements: [ 8.5, 6.5312, -6.5312, -8.5 ], + widthElements: [ 6.5, -6.5, 3, -3, 5, -5 ] + }, + 'GATEWAY_PARALLEL': { + d:'m {mx},{my} 0,{e.y1} -{e.x1},0 0,{e.y0} {e.x1},0 0,{e.y1} {e.x0},0 ' + + '0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z', + height: 30, + width: 30, + heightElements: [ 5, 12.5 ], + widthElements: [ 5, 12.5 ] + }, + 'GATEWAY_EVENT_BASED': { + d:'m {mx},{my} {e.x0},{e.y0} {e.x0},{e.y1} {e.x1},{e.y2} {e.x2},0 z', + height: 11, + width: 11, + heightElements: [ -6, 6, 12, -12 ], + widthElements: [ 9, -3, -12 ] + }, + 'GATEWAY_COMPLEX': { + d:'m {mx},{my} 0,{e.y0} -{e.x0},-{e.y1} -{e.x1},{e.y2} {e.x0},{e.y1} -{e.x2},0 0,{e.y3} ' + + '{e.x2},0 -{e.x0},{e.y1} l {e.x1},{e.y2} {e.x0},-{e.y1} 0,{e.y0} {e.x3},0 0,-{e.y0} {e.x0},{e.y1} ' + + '{e.x1},-{e.y2} -{e.x0},-{e.y1} {e.x2},0 0,-{e.y3} -{e.x2},0 {e.x0},-{e.y1} -{e.x1},-{e.y2} ' + + '-{e.x0},{e.y1} 0,-{e.y0} -{e.x3},0 z', + height: 17.125, + width: 17.125, + heightElements: [ 4.875, 3.4375, 2.125, 3 ], + widthElements: [ 3.4375, 2.125, 4.875, 3 ] + }, + 'DATA_OBJECT_PATH': { + d:'m 0,0 {e.x1},0 {e.x0},{e.y0} 0,{e.y1} -{e.x2},0 0,-{e.y2} {e.x1},0 0,{e.y0} {e.x0},0', + height: 61, + width: 51, + heightElements: [ 10, 50, 60 ], + widthElements: [ 10, 40, 50, 60 ] + }, + 'DATA_OBJECT_COLLECTION_PATH': { + d: 'm{mx},{my} m 3,2 l 0,10 m 3,-10 l 0,10 m 3,-10 l 0,10', + height: 10, + width: 10, + heightElements: [], + widthElements: [] + }, + 'DATA_ARROW': { + d:'m 5,9 9,0 0,-3 5,5 -5,5 0,-3 -9,0 z', + height: 61, + width: 51, + heightElements: [], + widthElements: [] + }, + 'DATA_STORE': { + d:'m {mx},{my} ' + + 'l 0,{e.y2} ' + + 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 ' + + 'l 0,-{e.y2} ' + + 'c -{e.x0},-{e.y1} -{e.x1},-{e.y1} -{e.x2},0' + + 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 ' + + 'm -{e.x2},{e.y0}' + + 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0' + + 'm -{e.x2},{e.y0}' + + 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0', + height: 61, + width: 61, + heightElements: [ 7, 10, 45 ], + widthElements: [ 2, 58, 60 ] + }, + 'TEXT_ANNOTATION': { + d: 'm {mx}, {my} m 10,0 l -10,0 l 0,{e.y0} l 10,0', + height: 30, + width: 10, + heightElements: [ 30 ], + widthElements: [ 10 ] + }, + 'MARKER_SUB_PROCESS': { + d: 'm{mx},{my} m 7,2 l 0,10 m -5,-5 l 10,0', + height: 10, + width: 10, + heightElements: [], + widthElements: [] + }, + 'MARKER_PARALLEL': { + d: 'm{mx},{my} m 3,2 l 0,10 m 3,-10 l 0,10 m 3,-10 l 0,10', + height: 10, + width: 10, + heightElements: [], + widthElements: [] + }, + 'MARKER_SEQUENTIAL': { + d: 'm{mx},{my} m 0,3 l 10,0 m -10,3 l 10,0 m -10,3 l 10,0', + height: 10, + width: 10, + heightElements: [], + widthElements: [] + }, + 'MARKER_COMPENSATION': { + d: 'm {mx},{my} 7,-5 0,10 z m 7.1,-0.3 6.9,-4.7 0,10 -6.9,-4.7 z', + height: 10, + width: 21, + heightElements: [], + widthElements: [] + }, + 'MARKER_LOOP': { + d: 'm {mx},{my} c 3.526979,0 6.386161,-2.829858 6.386161,-6.320661 0,-3.490806 -2.859182,-6.320661 ' + + '-6.386161,-6.320661 -3.526978,0 -6.38616,2.829855 -6.38616,6.320661 0,1.745402 ' + + '0.714797,3.325567 1.870463,4.469381 0.577834,0.571908 1.265885,1.034728 2.029916,1.35457 ' + + 'l -0.718163,-3.909793 m 0.718163,3.909793 -3.885211,0.802902', + height: 13.9, + width: 13.7, + heightElements: [], + widthElements: [] + }, + 'MARKER_ADHOC': { + d: 'm {mx},{my} m 0.84461,2.64411 c 1.05533,-1.23780996 2.64337,-2.07882 4.29653,-1.97997996 2.05163,0.0805 ' + + '3.85579,1.15803 5.76082,1.79107 1.06385,0.34139996 2.24454,0.1438 3.18759,-0.43767 0.61743,-0.33642 ' + + '1.2775,-0.64078 1.7542,-1.17511 0,0.56023 0,1.12046 0,1.6807 -0.98706,0.96237996 -2.29792,1.62393996 ' + + '-3.6918,1.66181996 -1.24459,0.0927 -2.46671,-0.2491 -3.59505,-0.74812 -1.35789,-0.55965 ' + + '-2.75133,-1.33436996 -4.27027,-1.18121996 -1.37741,0.14601 -2.41842,1.13685996 -3.44288,1.96782996 z', + height: 4, + width: 15, + heightElements: [], + widthElements: [] + }, + 'TASK_TYPE_SEND': { + d: 'm {mx},{my} l 0,{e.y1} l {e.x1},0 l 0,-{e.y1} z l {e.x0},{e.y0} l {e.x0},-{e.y0}', + height: 14, + width: 21, + heightElements: [ 6, 14 ], + widthElements: [ 10.5, 21 ] + }, + 'TASK_TYPE_SCRIPT': { + d: 'm {mx},{my} c 9.966553,-6.27276 -8.000926,-7.91932 2.968968,-14.938 l -8.802728,0 ' + + 'c -10.969894,7.01868 6.997585,8.66524 -2.968967,14.938 z ' + + 'm -7,-12 l 5,0 ' + + 'm -4.5,3 l 4.5,0 ' + + 'm -3,3 l 5,0' + + 'm -4,3 l 5,0', + height: 15, + width: 12.6, + heightElements: [ 6, 14 ], + widthElements: [ 10.5, 21 ] + }, + 'TASK_TYPE_USER_1': { + d: 'm {mx},{my} c 0.909,-0.845 1.594,-2.049 1.594,-3.385 0,-2.554 -1.805,-4.62199999 ' + + '-4.357,-4.62199999 -2.55199998,0 -4.28799998,2.06799999 -4.28799998,4.62199999 0,1.348 ' + + '0.974,2.562 1.89599998,3.405 -0.52899998,0.187 -5.669,2.097 -5.794,4.7560005 v 6.718 ' + + 'h 17 v -6.718 c 0,-2.2980005 -5.5279996,-4.5950005 -6.0509996,-4.7760005 z' + + 'm -8,6 l 0,5.5 m 11,0 l 0,-5' + }, + 'TASK_TYPE_USER_2': { + d: 'm {mx},{my} m 2.162,1.009 c 0,2.4470005 -2.158,4.4310005 -4.821,4.4310005 ' + + '-2.66499998,0 -4.822,-1.981 -4.822,-4.4310005 ' + }, + 'TASK_TYPE_USER_3': { + d: 'm {mx},{my} m -6.9,-3.80 c 0,0 2.25099998,-2.358 4.27399998,-1.177 2.024,1.181 4.221,1.537 ' + + '4.124,0.965 -0.098,-0.57 -0.117,-3.79099999 -4.191,-4.13599999 -3.57499998,0.001 ' + + '-4.20799998,3.36699999 -4.20699998,4.34799999 z' + }, + 'TASK_TYPE_MANUAL': { + d: 'm {mx},{my} c 0.234,-0.01 5.604,0.008 8.029,0.004 0.808,0 1.271,-0.172 1.417,-0.752 0.227,-0.898 ' + + '-0.334,-1.314 -1.338,-1.316 -2.467,-0.01 -7.886,-0.004 -8.108,-0.004 -0.014,-0.079 0.016,-0.533 0,-0.61 ' + + '0.195,-0.042 8.507,0.006 9.616,0.002 0.877,-0.007 1.35,-0.438 1.353,-1.208 0.003,-0.768 -0.479,-1.09 ' + + '-1.35,-1.091 -2.968,-0.002 -9.619,-0.013 -9.619,-0.013 v -0.591 c 0,0 5.052,-0.016 7.225,-0.016 ' + + '0.888,-0.002 1.354,-0.416 1.351,-1.193 -0.006,-0.761 -0.492,-1.196 -1.361,-1.196 -3.473,-0.005 ' + + '-10.86,-0.003 -11.0829995,-0.003 -0.022,-0.047 -0.045,-0.094 -0.069,-0.139 0.3939995,-0.319 ' + + '2.0409995,-1.626 2.4149995,-2.017 0.469,-0.4870005 0.519,-1.1650005 0.162,-1.6040005 -0.414,-0.511 ' + + '-0.973,-0.5 -1.48,-0.236 -1.4609995,0.764 -6.5999995,3.6430005 -7.7329995,4.2710005 -0.9,0.499 ' + + '-1.516,1.253 -1.882,2.19 -0.37000002,0.95 -0.17,2.01 -0.166,2.979 0.004,0.718 -0.27300002,1.345 ' + + '-0.055,2.063 0.629,2.087 2.425,3.312 4.859,3.318 4.6179995,0.014 9.2379995,-0.139 13.8569995,-0.158 ' + + '0.755,-0.004 1.171,-0.301 1.182,-1.033 0.012,-0.754 -0.423,-0.969 -1.183,-0.973 -1.778,-0.01 ' + + '-5.824,-0.004 -6.04,-0.004 10e-4,-0.084 0.003,-0.586 10e-4,-0.67 z' + }, + 'TASK_TYPE_INSTANTIATING_SEND': { + d: 'm {mx},{my} l 0,8.4 l 12.6,0 l 0,-8.4 z l 6.3,3.6 l 6.3,-3.6' + }, + 'TASK_TYPE_SERVICE': { + d: 'm {mx},{my} v -1.71335 c 0.352326,-0.0705 0.703932,-0.17838 1.047628,-0.32133 ' + + '0.344416,-0.14465 0.665822,-0.32133 0.966377,-0.52145 l 1.19431,1.18005 1.567487,-1.57688 ' + + '-1.195028,-1.18014 c 0.403376,-0.61394 0.683079,-1.29908 0.825447,-2.01824 l 1.622133,-0.01 ' + + 'v -2.2196 l -1.636514,0.01 c -0.07333,-0.35153 -0.178319,-0.70024 -0.323564,-1.04372 ' + + '-0.145244,-0.34406 -0.321407,-0.6644 -0.522735,-0.96217 l 1.131035,-1.13631 -1.583305,-1.56293 ' + + '-1.129598,1.13589 c -0.614052,-0.40108 -1.302883,-0.68093 -2.022633,-0.82247 l 0.0093,-1.61852 ' + + 'h -2.241173 l 0.0042,1.63124 c -0.353763,0.0736 -0.705369,0.17977 -1.049785,0.32371 -0.344415,0.14437 ' + + '-0.665102,0.32092 -0.9635006,0.52046 l -1.1698628,-1.15823 -1.5667691,1.5792 1.1684265,1.15669 ' + + 'c -0.4026573,0.61283 -0.68308,1.29797 -0.8247287,2.01713 l -1.6588041,0.003 v 2.22174 ' + + 'l 1.6724648,-0.006 c 0.073327,0.35077 0.1797598,0.70243 0.3242851,1.04472 0.1452428,0.34448 ' + + '0.3214064,0.6644 0.5227339,0.96066 l -1.1993431,1.19723 1.5840256,1.56011 1.1964668,-1.19348 ' + + 'c 0.6140517,0.40346 1.3028827,0.68232 2.0233517,0.82331 l 7.19e-4,1.69892 h 2.226848 z ' + + 'm 0.221462,-3.9957 c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 ' + + '0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 ' + + '0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z' + }, + 'TASK_TYPE_SERVICE_FILL': { + d: 'm {mx},{my} c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 ' + + '0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 ' + + '0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z' + }, + 'TASK_TYPE_BUSINESS_RULE_HEADER': { + d: 'm {mx},{my} 0,4 20,0 0,-4 z' + }, + 'TASK_TYPE_BUSINESS_RULE_MAIN': { + d: 'm {mx},{my} 0,12 20,0 0,-12 z' + + 'm 0,8 l 20,0 ' + + 'm -13,-4 l 0,8' + }, + 'MESSAGE_FLOW_MARKER': { + d: 'm {mx},{my} m -10.5 ,-7 l 0,14 l 21,0 l 0,-14 z l 10.5,6 l 10.5,-6' + } + }; + + this.getRawPath = function getRawPath(pathId) { + return this.pathMap[pathId].d; + }; + + /** + * Scales the path to the given height and width. + *

          Use case

          + *

          Use case is to scale the content of elements (event, gateways) based + * on the element bounding box's size. + *

          + *

          Why not transform

          + *

          Scaling a path with transform() will also scale the stroke and IE does not support + * the option 'non-scaling-stroke' to prevent this. + * Also there are use cases where only some parts of a path should be + * scaled.

          + * + * @param {string} pathId The ID of the path. + * @param {Object} param

          + * Example param object scales the path to 60% size of the container (data.width, data.height). + *

          +     *   {
          +     *     xScaleFactor: 0.6,
          +     *     yScaleFactor:0.6,
          +     *     containerWidth: data.width,
          +     *     containerHeight: data.height,
          +     *     position: {
          +     *       mx: 0.46,
          +     *       my: 0.2,
          +     *     }
          +     *   }
          +     *   
          + *
            + *
          • targetpathwidth = xScaleFactor * containerWidth
          • + *
          • targetpathheight = yScaleFactor * containerHeight
          • + *
          • Position is used to set the starting coordinate of the path. M is computed: + *
              + *
            • position.x * containerWidth
            • + *
            • position.y * containerHeight
            • + *
            + * Center of the container
             position: {
            +     *       mx: 0.5,
            +     *       my: 0.5,
            +     *     }
            + * Upper left corner of the container + *
             position: {
            +     *       mx: 0.0,
            +     *       my: 0.0,
            +     *     }
            + *
          • + *
          + *

          + * + */ + this.getScaledPath = function getScaledPath(pathId, param) { + var rawPath = this.pathMap[pathId]; + + // positioning + // compute the start point of the path + var mx, my; + + if (param.abspos) { + mx = param.abspos.x; + my = param.abspos.y; + } else { + mx = param.containerWidth * param.position.mx; + my = param.containerHeight * param.position.my; + } + + var coordinates = {}; // map for the scaled coordinates + if (param.position) { + + // path + var heightRatio = (param.containerHeight / rawPath.height) * param.yScaleFactor; + var widthRatio = (param.containerWidth / rawPath.width) * param.xScaleFactor; + + + // Apply height ratio + for (var heightIndex = 0; heightIndex < rawPath.heightElements.length; heightIndex++) { + coordinates['y' + heightIndex] = rawPath.heightElements[heightIndex] * heightRatio; + } + + // Apply width ratio + for (var widthIndex = 0; widthIndex < rawPath.widthElements.length; widthIndex++) { + coordinates['x' + widthIndex] = rawPath.widthElements[widthIndex] * widthRatio; + } + } + + // Apply value to raw path + var path = format( + rawPath.d, { + mx: mx, + my: my, + e: coordinates + } + ); + return path; + }; + } + + // helpers ////////////////////// + + // copied and adjusted from https://github.com/adobe-webplatform/Snap.svg/blob/master/src/svg.js + var tokenRegex = /\{([^{}]+)\}/g, + objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g; // matches .xxxxx or ["xxxxx"] to run over object properties + + function replacer(all, key, obj) { + var res = obj; + key.replace(objNotationRegex, function(all, name, quote, quotedName, isFunc) { + name = name || quotedName; + if (res) { + if (name in res) { + res = res[name]; + } + typeof res == 'function' && isFunc && (res = res()); + } + }); + res = (res == null || res == obj ? all : res) + ''; + + return res; + } + + function format(str, obj) { + return String(str).replace(tokenRegex, function(all, key) { + return replacer(all, key, obj); + }); + } + + var DrawModule$1 = { + __init__: [ 'bpmnRenderer' ], + bpmnRenderer: [ 'type', BpmnRenderer ], + textRenderer: [ 'type', TextRenderer ], + pathMap: [ 'type', PathMap ] + }; + + /** + * A simple translation stub to be used for multi-language support + * in diagrams. Can be easily replaced with a more sophisticated + * solution. + * + * @example + * + * // use it inside any diagram component by injecting `translate`. + * + * function MyService(translate) { + * alert(translate('HELLO {you}', { you: 'You!' })); + * } + * + * @param {string} template to interpolate + * @param {Object} [replacements] a map with substitutes + * + * @return {string} the translated string + */ + function translate(template, replacements) { + + replacements = replacements || {}; + + return template.replace(/{([^}]+)}/g, function(_, key) { + return replacements[key] || '{' + key + '}'; + }); + } + + var TranslateModule = { + translate: [ 'value', translate ] + }; + + var DEFAULT_LABEL_SIZE = { + width: 90, + height: 20 + }; + + var FLOW_LABEL_INDENT = 15; + + + /** + * Returns true if the given semantic has an external label + * + * @param {BpmnElement} semantic + * @return {boolean} true if has label + */ + function isLabelExternal(semantic) { + return is$1(semantic, 'bpmn:Event') || + is$1(semantic, 'bpmn:Gateway') || + is$1(semantic, 'bpmn:DataStoreReference') || + is$1(semantic, 'bpmn:DataObjectReference') || + is$1(semantic, 'bpmn:DataInput') || + is$1(semantic, 'bpmn:DataOutput') || + is$1(semantic, 'bpmn:SequenceFlow') || + is$1(semantic, 'bpmn:MessageFlow') || + is$1(semantic, 'bpmn:Group'); + } + + /** + * Get the position for sequence flow labels + * + * @param {Array} waypoints + * @return {Point} the label position + */ + function getFlowLabelPosition(waypoints) { + + // get the waypoints mid + var mid = waypoints.length / 2 - 1; + + var first = waypoints[Math.floor(mid)]; + var second = waypoints[Math.ceil(mid + 0.01)]; + + // get position + var position = getWaypointsMid(waypoints); + + // calculate angle + var angle = Math.atan((second.y - first.y) / (second.x - first.x)); + + var x = position.x, + y = position.y; + + if (Math.abs(angle) < Math.PI / 2) { + y -= FLOW_LABEL_INDENT; + } else { + x += FLOW_LABEL_INDENT; + } + + return { x: x, y: y }; + } + + + /** + * Get the middle of a number of waypoints + * + * @param {Array} waypoints + * @return {Point} the mid point + */ + function getWaypointsMid(waypoints) { + + var mid = waypoints.length / 2 - 1; + + var first = waypoints[Math.floor(mid)]; + var second = waypoints[Math.ceil(mid + 0.01)]; + + return { + x: first.x + (second.x - first.x) / 2, + y: first.y + (second.y - first.y) / 2 + }; + } + + + function getExternalLabelMid(element) { + + if (element.waypoints) { + return getFlowLabelPosition(element.waypoints); + } else if (is$1(element, 'bpmn:Group')) { + return { + x: element.x + element.width / 2, + y: element.y + DEFAULT_LABEL_SIZE.height / 2 + }; + } else { + return { + x: element.x + element.width / 2, + y: element.y + element.height + DEFAULT_LABEL_SIZE.height / 2 + }; + } + } + + + /** + * Returns the bounds of an elements label, parsed from the elements DI or + * generated from its bounds. + * + * @param {BpmndDi} di + * @param {djs.model.Base} element + */ + function getExternalLabelBounds(di, element) { + + var mid, + size, + bounds, + label = di.label; + + if (label && label.bounds) { + bounds = label.bounds; + + size = { + width: Math.max(DEFAULT_LABEL_SIZE.width, bounds.width), + height: bounds.height + }; + + mid = { + x: bounds.x + bounds.width / 2, + y: bounds.y + bounds.height / 2 + }; + } else { + + mid = getExternalLabelMid(element); + + size = DEFAULT_LABEL_SIZE; + } + + return assign({ + x: mid.x - size.width / 2, + y: mid.y - size.height / 2 + }, size); + } + + var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + + function getDefaultExportFromCjs (x) { + return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; + } + + function roundPoint(point) { + + return { + x: Math.round(point.x), + y: Math.round(point.y) + }; + } + + + /** + * Convert the given bounds to a { top, left, bottom, right } descriptor. + * + * @param {Bounds|Point} bounds + * + * @return {Object} + */ + function asTRBL(bounds) { + return { + top: bounds.y, + right: bounds.x + (bounds.width || 0), + bottom: bounds.y + (bounds.height || 0), + left: bounds.x + }; + } + + + /** + * Convert a { top, left, bottom, right } to an objects bounds. + * + * @param {Object} trbl + * + * @return {Bounds} + */ + function asBounds(trbl) { + return { + x: trbl.left, + y: trbl.top, + width: trbl.right - trbl.left, + height: trbl.bottom - trbl.top + }; + } + + + /** + * Get the mid of the given bounds or point. + * + * @param {Bounds|Point} bounds + * + * @return {Point} + */ + function getBoundsMid(bounds) { + return roundPoint({ + x: bounds.x + (bounds.width || 0) / 2, + y: bounds.y + (bounds.height || 0) / 2 + }); + } + + + /** + * Get the mid of the given Connection. + * + * @param {djs.Base.Connection} connection + * + * @return {Point} + */ + function getConnectionMid(connection) { + var waypoints = connection.waypoints; + + // calculate total length and length of each segment + var parts = waypoints.reduce(function(parts, point, index) { + + var lastPoint = waypoints[index - 1]; + + if (lastPoint) { + var lastPart = parts[parts.length - 1]; + + var startLength = lastPart && lastPart.endLength || 0; + var length = distance(lastPoint, point); + + parts.push({ + start: lastPoint, + end: point, + startLength: startLength, + endLength: startLength + length, + length: length + }); + } + + return parts; + }, []); + + var totalLength = parts.reduce(function(length, part) { + return length + part.length; + }, 0); + + // find which segement contains middle point + var midLength = totalLength / 2; + + var i = 0; + var midSegment = parts[i]; + + while (midSegment.endLength < midLength) { + midSegment = parts[++i]; + } + + // calculate relative position on mid segment + var segmentProgress = (midLength - midSegment.startLength) / midSegment.length; + + var midPoint = { + x: midSegment.start.x + (midSegment.end.x - midSegment.start.x) * segmentProgress, + y: midSegment.start.y + (midSegment.end.y - midSegment.start.y) * segmentProgress + }; + + return midPoint; + } + + + /** + * Get the mid of the given Element. + * + * @param {djs.Base.Connection} connection + * + * @return {Point} + */ + function getMid(element) { + if (isConnection(element)) { + return getConnectionMid(element); + } + + return getBoundsMid(element); + } + + // helpers ////////////////////// + + function distance(a, b) { + return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2)); + } + + function isConnection(element) { + return !!element.waypoints; + } + + function elementToString(e) { + if (!e) { + return ''; + } + + return '<' + e.$type + (e.id ? ' id="' + e.id : '') + '" />'; + } + + /** + * @param {ModdleElement} semantic + * @param {ModdleElement} di + * @param {Object} [attrs=null] + * + * @return {Object} + */ + function elementData(semantic, di, attrs) { + return assign({ + id: semantic.id, + type: semantic.$type, + businessObject: semantic, + di: di + }, attrs); + } + + function getWaypoints(di, source, target) { + + var waypoints = di.waypoint; + + if (!waypoints || waypoints.length < 2) { + return [ getMid(source), getMid(target) ]; + } + + return waypoints.map(function(p) { + return { x: p.x, y: p.y }; + }); + } + + function notYetDrawn(translate, semantic, refSemantic, property) { + return new Error(translate('element {element} referenced by {referenced}#{property} not yet drawn', { + element: elementToString(refSemantic), + referenced: elementToString(semantic), + property: property + })); + } + + + /** + * An importer that adds bpmn elements to the canvas + * + * @param {EventBus} eventBus + * @param {Canvas} canvas + * @param {ElementFactory} elementFactory + * @param {ElementRegistry} elementRegistry + * @param {Function} translate + * @param {TextRenderer} textRenderer + */ + function BpmnImporter( + eventBus, canvas, elementFactory, + elementRegistry, translate, textRenderer) { + + this._eventBus = eventBus; + this._canvas = canvas; + this._elementFactory = elementFactory; + this._elementRegistry = elementRegistry; + this._translate = translate; + this._textRenderer = textRenderer; + } + + BpmnImporter.$inject = [ + 'eventBus', + 'canvas', + 'elementFactory', + 'elementRegistry', + 'translate', + 'textRenderer' + ]; + + + /** + * Add bpmn element (semantic) to the canvas onto the + * specified parent shape. + */ + BpmnImporter.prototype.add = function(semantic, di, parentElement) { + var element, + translate = this._translate, + hidden; + + var parentIndex; + + // ROOT ELEMENT + // handle the special case that we deal with a + // invisible root element (process, subprocess or collaboration) + if (is$1(di, 'bpmndi:BPMNPlane')) { + + var attrs = is$1(semantic, 'bpmn:SubProcess') + ? { id: semantic.id + '_plane' } + : {}; + + // add a virtual element (not being drawn) + element = this._elementFactory.createRoot(elementData(semantic, di, attrs)); + + this._canvas.addRootElement(element); + } + + // SHAPE + else if (is$1(di, 'bpmndi:BPMNShape')) { + + var collapsed = !isExpanded(semantic, di), + isFrame = isFrameElement$1(semantic); + + hidden = parentElement && (parentElement.hidden || parentElement.collapsed); + + var bounds = di.bounds; + + element = this._elementFactory.createShape(elementData(semantic, di, { + collapsed: collapsed, + hidden: hidden, + x: Math.round(bounds.x), + y: Math.round(bounds.y), + width: Math.round(bounds.width), + height: Math.round(bounds.height), + isFrame: isFrame + })); + + if (is$1(semantic, 'bpmn:BoundaryEvent')) { + this._attachBoundary(semantic, element); + } + + // insert lanes behind other flow nodes (cf. #727) + if (is$1(semantic, 'bpmn:Lane')) { + parentIndex = 0; + } + + if (is$1(semantic, 'bpmn:DataStoreReference')) { + + // check whether data store is inside our outside of its semantic parent + if (!isPointInsideBBox(parentElement, getMid(bounds))) { + parentElement = this._canvas.findRoot(parentElement); + } + } + + this._canvas.addShape(element, parentElement, parentIndex); + } + + // CONNECTION + else if (is$1(di, 'bpmndi:BPMNEdge')) { + + var source = this._getSource(semantic), + target = this._getTarget(semantic); + + hidden = parentElement && (parentElement.hidden || parentElement.collapsed); + + element = this._elementFactory.createConnection(elementData(semantic, di, { + hidden: hidden, + source: source, + target: target, + waypoints: getWaypoints(di, source, target) + })); + + if (is$1(semantic, 'bpmn:DataAssociation')) { + + // render always on top; this ensures DataAssociations + // are rendered correctly across different "hacks" people + // love to model such as cross participant / sub process + // associations + parentElement = this._canvas.findRoot(parentElement); + } + + this._canvas.addConnection(element, parentElement, parentIndex); + } else { + throw new Error(translate('unknown di {di} for element {semantic}', { + di: elementToString(di), + semantic: elementToString(semantic) + })); + } + + // (optional) LABEL + if (isLabelExternal(semantic) && getLabel(element)) { + this.addLabel(semantic, di, element); + } + + + this._eventBus.fire('bpmnElement.added', { element: element }); + + return element; + }; + + + /** + * Attach the boundary element to the given host + * + * @param {ModdleElement} boundarySemantic + * @param {djs.model.Base} boundaryElement + */ + BpmnImporter.prototype._attachBoundary = function(boundarySemantic, boundaryElement) { + var translate = this._translate; + var hostSemantic = boundarySemantic.attachedToRef; + + if (!hostSemantic) { + throw new Error(translate('missing {semantic}#attachedToRef', { + semantic: elementToString(boundarySemantic) + })); + } + + var host = this._elementRegistry.get(hostSemantic.id), + attachers = host && host.attachers; + + if (!host) { + throw notYetDrawn(translate, boundarySemantic, hostSemantic, 'attachedToRef'); + } + + // wire element.host <> host.attachers + boundaryElement.host = host; + + if (!attachers) { + host.attachers = attachers = []; + } + + if (attachers.indexOf(boundaryElement) === -1) { + attachers.push(boundaryElement); + } + }; + + + /** + * add label for an element + */ + BpmnImporter.prototype.addLabel = function(semantic, di, element) { + var bounds, + text, + label; + + bounds = getExternalLabelBounds(di, element); + + text = getLabel(element); + + if (text) { + + // get corrected bounds from actual layouted text + bounds = this._textRenderer.getExternalLabelBounds(bounds, text); + } + + label = this._elementFactory.createLabel(elementData(semantic, di, { + id: semantic.id + '_label', + labelTarget: element, + type: 'label', + hidden: element.hidden || !getLabel(element), + x: Math.round(bounds.x), + y: Math.round(bounds.y), + width: Math.round(bounds.width), + height: Math.round(bounds.height) + })); + + return this._canvas.addShape(label, element.parent); + }; + + /** + * Return the drawn connection end based on the given side. + * + * @throws {Error} if the end is not yet drawn + */ + BpmnImporter.prototype._getEnd = function(semantic, side) { + + var element, + refSemantic, + type = semantic.$type, + translate = this._translate; + + refSemantic = semantic[side + 'Ref']; + + // handle mysterious isMany DataAssociation#sourceRef + if (side === 'source' && type === 'bpmn:DataInputAssociation') { + refSemantic = refSemantic && refSemantic[0]; + } + + // fix source / target for DataInputAssociation / DataOutputAssociation + if (side === 'source' && type === 'bpmn:DataOutputAssociation' || + side === 'target' && type === 'bpmn:DataInputAssociation') { + + refSemantic = semantic.$parent; + } + + element = refSemantic && this._getElement(refSemantic); + + if (element) { + return element; + } + + if (refSemantic) { + throw notYetDrawn(translate, semantic, refSemantic, side + 'Ref'); + } else { + throw new Error(translate('{semantic}#{side} Ref not specified', { + semantic: elementToString(semantic), + side: side + })); + } + }; + + BpmnImporter.prototype._getSource = function(semantic) { + return this._getEnd(semantic, 'source'); + }; + + BpmnImporter.prototype._getTarget = function(semantic) { + return this._getEnd(semantic, 'target'); + }; + + + BpmnImporter.prototype._getElement = function(semantic) { + return this._elementRegistry.get(semantic.id); + }; + + + // helpers //////////////////// + + function isPointInsideBBox(bbox, point) { + var x = point.x, + y = point.y; + + return x >= bbox.x && + x <= bbox.x + bbox.width && + y >= bbox.y && + y <= bbox.y + bbox.height; + } + + function isFrameElement$1(semantic) { + return is$1(semantic, 'bpmn:Group'); + } + + var ImportModule = { + __depends__: [ + TranslateModule + ], + bpmnImporter: [ 'type', BpmnImporter ] + }; + + var CoreModule$1 = { + __depends__: [ + DrawModule$1, + ImportModule + ] + }; + + function getOriginal(event) { + return event.originalEvent || event.srcEvent; + } + + function isMac() { + return (/mac/i).test(navigator.platform); + } + + function isButton(event, button) { + return (getOriginal(event) || event).button === button; + } + + function isPrimaryButton(event) { + + // button === 0 -> left áka primary mouse button + return isButton(event, 0); + } + + function isAuxiliaryButton(event) { + + // button === 1 -> auxiliary áka wheel button + return isButton(event, 1); + } + + function hasPrimaryModifier(event) { + var originalEvent = getOriginal(event) || event; + + if (!isPrimaryButton(event)) { + return false; + } + + // Use cmd as primary modifier key for mac OS + if (isMac()) { + return originalEvent.metaKey; + } else { + return originalEvent.ctrlKey; + } + } + + + function hasSecondaryModifier(event) { + var originalEvent = getOriginal(event) || event; + + return isPrimaryButton(event) && originalEvent.shiftKey; + } + + function allowAll(event) { return true; } + + function allowPrimaryAndAuxiliary(event) { + return isPrimaryButton(event) || isAuxiliaryButton(event); + } + + var LOW_PRIORITY$3 = 500; + + + /** + * A plugin that provides interaction events for diagram elements. + * + * It emits the following events: + * + * * element.click + * * element.contextmenu + * * element.dblclick + * * element.hover + * * element.mousedown + * * element.mousemove + * * element.mouseup + * * element.out + * + * Each event is a tuple { element, gfx, originalEvent }. + * + * Canceling the event via Event#preventDefault() + * prevents the original DOM operation. + * + * @param {EventBus} eventBus + */ + function InteractionEvents(eventBus, elementRegistry, styles) { + + var self = this; + + /** + * Fire an interaction event. + * + * @param {string} type local event name, e.g. element.click. + * @param {DOMEvent} event native event + * @param {djs.model.Base} [element] the diagram element to emit the event on; + * defaults to the event target + */ + function fire(type, event, element) { + + if (isIgnored(type, event)) { + return; + } + + var target, gfx, returnValue; + + if (!element) { + target = event.delegateTarget || event.target; + + if (target) { + gfx = target; + element = elementRegistry.get(gfx); + } + } else { + gfx = elementRegistry.getGraphics(element); + } + + if (!gfx || !element) { + return; + } + + returnValue = eventBus.fire(type, { + element: element, + gfx: gfx, + originalEvent: event + }); + + if (returnValue === false) { + event.stopPropagation(); + event.preventDefault(); + } + } + + // TODO(nikku): document this + var handlers = {}; + + function mouseHandler(localEventName) { + return handlers[localEventName]; + } + + function isIgnored(localEventName, event) { + + var filter = ignoredFilters[localEventName] || isPrimaryButton; + + // only react on left mouse button interactions + // except for interaction events that are enabled + // for secundary mouse button + return !filter(event); + } + + var bindings = { + click: 'element.click', + contextmenu: 'element.contextmenu', + dblclick: 'element.dblclick', + mousedown: 'element.mousedown', + mousemove: 'element.mousemove', + mouseover: 'element.hover', + mouseout: 'element.out', + mouseup: 'element.mouseup', + }; + + var ignoredFilters = { + 'element.contextmenu': allowAll, + 'element.mousedown': allowPrimaryAndAuxiliary, + 'element.mouseup': allowPrimaryAndAuxiliary, + 'element.click': allowPrimaryAndAuxiliary, + 'element.dblclick': allowPrimaryAndAuxiliary + }; + + + // manual event trigger ////////// + + /** + * Trigger an interaction event (based on a native dom event) + * on the target shape or connection. + * + * @param {string} eventName the name of the triggered DOM event + * @param {MouseEvent} event + * @param {djs.model.Base} targetElement + */ + function triggerMouseEvent(eventName, event, targetElement) { + + // i.e. element.mousedown... + var localEventName = bindings[eventName]; + + if (!localEventName) { + throw new Error('unmapped DOM event name <' + eventName + '>'); + } + + return fire(localEventName, event, targetElement); + } + + + var ELEMENT_SELECTOR = 'svg, .djs-element'; + + // event handling /////// + + function registerEvent(node, event, localEvent, ignoredFilter) { + + var handler = handlers[localEvent] = function(event) { + fire(localEvent, event); + }; + + if (ignoredFilter) { + ignoredFilters[localEvent] = ignoredFilter; + } + + handler.$delegate = delegate.bind(node, ELEMENT_SELECTOR, event, handler); + } + + function unregisterEvent(node, event, localEvent) { + + var handler = mouseHandler(localEvent); + + if (!handler) { + return; + } + + delegate.unbind(node, event, handler.$delegate); + } + + function registerEvents(svg) { + forEach$1(bindings, function(val, key) { + registerEvent(svg, key, val); + }); + } + + function unregisterEvents(svg) { + forEach$1(bindings, function(val, key) { + unregisterEvent(svg, key, val); + }); + } + + eventBus.on('canvas.destroy', function(event) { + unregisterEvents(event.svg); + }); + + eventBus.on('canvas.init', function(event) { + registerEvents(event.svg); + }); + + + // hit box updating //////////////// + + eventBus.on([ 'shape.added', 'connection.added' ], function(event) { + var element = event.element, + gfx = event.gfx; + + eventBus.fire('interactionEvents.createHit', { element: element, gfx: gfx }); + }); + + // Update djs-hit on change. + // A low priortity is necessary, because djs-hit of labels has to be updated + // after the label bounds have been updated in the renderer. + eventBus.on([ + 'shape.changed', + 'connection.changed' + ], LOW_PRIORITY$3, function(event) { + + var element = event.element, + gfx = event.gfx; + + eventBus.fire('interactionEvents.updateHit', { element: element, gfx: gfx }); + }); + + eventBus.on('interactionEvents.createHit', LOW_PRIORITY$3, function(event) { + var element = event.element, + gfx = event.gfx; + + self.createDefaultHit(element, gfx); + }); + + eventBus.on('interactionEvents.updateHit', function(event) { + var element = event.element, + gfx = event.gfx; + + self.updateDefaultHit(element, gfx); + }); + + + // hit styles //////////// + + var STROKE_HIT_STYLE = createHitStyle('djs-hit djs-hit-stroke'); + + var CLICK_STROKE_HIT_STYLE = createHitStyle('djs-hit djs-hit-click-stroke'); + + var ALL_HIT_STYLE = createHitStyle('djs-hit djs-hit-all'); + + var NO_MOVE_HIT_STYLE = createHitStyle('djs-hit djs-hit-no-move'); + + var HIT_TYPES = { + 'all': ALL_HIT_STYLE, + 'click-stroke': CLICK_STROKE_HIT_STYLE, + 'stroke': STROKE_HIT_STYLE, + 'no-move': NO_MOVE_HIT_STYLE + }; + + function createHitStyle(classNames, attrs) { + + attrs = assign({ + stroke: 'white', + strokeWidth: 15 + }, attrs || {}); + + return styles.cls(classNames, [ 'no-fill', 'no-border' ], attrs); + } + + + // style helpers /////////////// + + function applyStyle(hit, type) { + + var attrs = HIT_TYPES[type]; + + if (!attrs) { + throw new Error('invalid hit type <' + type + '>'); + } + + attr$1(hit, attrs); + + return hit; + } + + function appendHit(gfx, hit) { + append(gfx, hit); + } + + + // API + + /** + * Remove hints on the given graphics. + * + * @param {SVGElement} gfx + */ + this.removeHits = function(gfx) { + var hits = all('.djs-hit', gfx); + + forEach$1(hits, remove$2); + }; + + /** + * Create default hit for the given element. + * + * @param {djs.model.Base} element + * @param {SVGElement} gfx + * + * @return {SVGElement} created hit + */ + this.createDefaultHit = function(element, gfx) { + var waypoints = element.waypoints, + isFrame = element.isFrame, + boxType; + + if (waypoints) { + return this.createWaypointsHit(gfx, waypoints); + } else { + + boxType = isFrame ? 'stroke' : 'all'; + + return this.createBoxHit(gfx, boxType, { + width: element.width, + height: element.height + }); + } + }; + + /** + * Create hits for the given waypoints. + * + * @param {SVGElement} gfx + * @param {Array} waypoints + * + * @return {SVGElement} + */ + this.createWaypointsHit = function(gfx, waypoints) { + + var hit = createLine(waypoints); + + applyStyle(hit, 'stroke'); + + appendHit(gfx, hit); + + return hit; + }; + + /** + * Create hits for a box. + * + * @param {SVGElement} gfx + * @param {string} hitType + * @param {Object} attrs + * + * @return {SVGElement} + */ + this.createBoxHit = function(gfx, type, attrs) { + + attrs = assign({ + x: 0, + y: 0 + }, attrs); + + var hit = create$1('rect'); + + applyStyle(hit, type); + + attr$1(hit, attrs); + + appendHit(gfx, hit); + + return hit; + }; + + /** + * Update default hit of the element. + * + * @param {djs.model.Base} element + * @param {SVGElement} gfx + * + * @return {SVGElement} updated hit + */ + this.updateDefaultHit = function(element, gfx) { + + var hit = query('.djs-hit', gfx); + + if (!hit) { + return; + } + + if (element.waypoints) { + updateLine(hit, element.waypoints); + } else { + attr$1(hit, { + width: element.width, + height: element.height + }); + } + + return hit; + }; + + this.fire = fire; + + this.triggerMouseEvent = triggerMouseEvent; + + this.mouseHandler = mouseHandler; + + this.registerEvent = registerEvent; + this.unregisterEvent = unregisterEvent; + } + + + InteractionEvents.$inject = [ + 'eventBus', + 'elementRegistry', + 'styles' + ]; + + + /** + * An event indicating that the mouse hovered over an element + * + * @event element.hover + * + * @type {Object} + * @property {djs.model.Base} element + * @property {SVGElement} gfx + * @property {Event} originalEvent + */ + + /** + * An event indicating that the mouse has left an element + * + * @event element.out + * + * @type {Object} + * @property {djs.model.Base} element + * @property {SVGElement} gfx + * @property {Event} originalEvent + */ + + /** + * An event indicating that the mouse has clicked an element + * + * @event element.click + * + * @type {Object} + * @property {djs.model.Base} element + * @property {SVGElement} gfx + * @property {Event} originalEvent + */ + + /** + * An event indicating that the mouse has double clicked an element + * + * @event element.dblclick + * + * @type {Object} + * @property {djs.model.Base} element + * @property {SVGElement} gfx + * @property {Event} originalEvent + */ + + /** + * An event indicating that the mouse has gone down on an element. + * + * @event element.mousedown + * + * @type {Object} + * @property {djs.model.Base} element + * @property {SVGElement} gfx + * @property {Event} originalEvent + */ + + /** + * An event indicating that the mouse has gone up on an element. + * + * @event element.mouseup + * + * @type {Object} + * @property {djs.model.Base} element + * @property {SVGElement} gfx + * @property {Event} originalEvent + */ + + /** + * An event indicating that the context menu action is triggered + * via mouse or touch controls. + * + * @event element.contextmenu + * + * @type {Object} + * @property {djs.model.Base} element + * @property {SVGElement} gfx + * @property {Event} originalEvent + */ + + var InteractionEventsModule = { + __init__: [ 'interactionEvents' ], + interactionEvents: [ 'type', InteractionEvents ] + }; + + /** + * Returns the surrounding bbox for all elements in + * the array or the element primitive. + * + * @param {Array|djs.model.Shape} elements + * @param {boolean} [stopRecursion=false] + * + * @return {Bounds} + */ + function getBBox(elements, stopRecursion) { + + stopRecursion = !!stopRecursion; + if (!isArray$2(elements)) { + elements = [ elements ]; + } + + var minX, + minY, + maxX, + maxY; + + forEach$1(elements, function(element) { + + // If element is a connection the bbox must be computed first + var bbox = element; + if (element.waypoints && !stopRecursion) { + bbox = getBBox(element.waypoints, true); + } + + var x = bbox.x, + y = bbox.y, + height = bbox.height || 0, + width = bbox.width || 0; + + if (x < minX || minX === undefined) { + minX = x; + } + if (y < minY || minY === undefined) { + minY = y; + } + + if ((x + width) > maxX || maxX === undefined) { + maxX = x + width; + } + if ((y + height) > maxY || maxY === undefined) { + maxY = y + height; + } + }); + + return { + x: minX, + y: minY, + height: maxY - minY, + width: maxX - minX + }; + } + + + function getType(element) { + + if ('waypoints' in element) { + return 'connection'; + } + + if ('x' in element) { + return 'shape'; + } + + return 'root'; + } + + function isFrameElement(element) { + + return !!(element && element.isFrame); + } + + var LOW_PRIORITY$2 = 500; + + + /** + * @class + * + * A plugin that adds an outline to shapes and connections that may be activated and styled + * via CSS classes. + * + * @param {EventBus} eventBus + * @param {Styles} styles + * @param {ElementRegistry} elementRegistry + */ + function Outline(eventBus, styles, elementRegistry) { + + this.offset = 6; + + var OUTLINE_STYLE = styles.cls('djs-outline', [ 'no-fill' ]); + + var self = this; + + function createOutline(gfx, bounds) { + var outline = create$1('rect'); + + attr$1(outline, assign({ + x: 10, + y: 10, + rx: 3, + width: 100, + height: 100 + }, OUTLINE_STYLE)); + + append(gfx, outline); + + return outline; + } + + // A low priortity is necessary, because outlines of labels have to be updated + // after the label bounds have been updated in the renderer. + eventBus.on([ 'shape.added', 'shape.changed' ], LOW_PRIORITY$2, function(event) { + var element = event.element, + gfx = event.gfx; + + var outline = query('.djs-outline', gfx); + + if (!outline) { + outline = createOutline(gfx); + } + + self.updateShapeOutline(outline, element); + }); + + eventBus.on([ 'connection.added', 'connection.changed' ], function(event) { + var element = event.element, + gfx = event.gfx; + + var outline = query('.djs-outline', gfx); + + if (!outline) { + outline = createOutline(gfx); + } + + self.updateConnectionOutline(outline, element); + }); + } + + + /** + * Updates the outline of a shape respecting the dimension of the + * element and an outline offset. + * + * @param {SVGElement} outline + * @param {djs.model.Base} element + */ + Outline.prototype.updateShapeOutline = function(outline, element) { + + attr$1(outline, { + x: -this.offset, + y: -this.offset, + width: element.width + this.offset * 2, + height: element.height + this.offset * 2 + }); + + }; + + + /** + * Updates the outline of a connection respecting the bounding box of + * the connection and an outline offset. + * + * @param {SVGElement} outline + * @param {djs.model.Base} element + */ + Outline.prototype.updateConnectionOutline = function(outline, connection) { + + var bbox = getBBox(connection); + + attr$1(outline, { + x: bbox.x - this.offset, + y: bbox.y - this.offset, + width: bbox.width + this.offset * 2, + height: bbox.height + this.offset * 2 + }); + + }; + + + Outline.$inject = [ 'eventBus', 'styles', 'elementRegistry' ]; + + var OutlineModule = { + __init__: [ 'outline' ], + outline: [ 'type', Outline ] + }; + + /** + * A service that offers the current selection in a diagram. + * Offers the api to control the selection, too. + * + * @class + * + * @param {EventBus} eventBus the event bus + */ + function Selection(eventBus, canvas) { + + this._eventBus = eventBus; + this._canvas = canvas; + + this._selectedElements = []; + + var self = this; + + eventBus.on([ 'shape.remove', 'connection.remove' ], function(e) { + var element = e.element; + self.deselect(element); + }); + + eventBus.on([ 'diagram.clear', 'root.set' ], function(e) { + self.select(null); + }); + } + + Selection.$inject = [ 'eventBus', 'canvas' ]; + + + Selection.prototype.deselect = function(element) { + var selectedElements = this._selectedElements; + + var idx = selectedElements.indexOf(element); + + if (idx !== -1) { + var oldSelection = selectedElements.slice(); + + selectedElements.splice(idx, 1); + + this._eventBus.fire('selection.changed', { oldSelection: oldSelection, newSelection: selectedElements }); + } + }; + + + Selection.prototype.get = function() { + return this._selectedElements; + }; + + Selection.prototype.isSelected = function(element) { + return this._selectedElements.indexOf(element) !== -1; + }; + + + /** + * This method selects one or more elements on the diagram. + * + * By passing an additional add parameter you can decide whether or not the element(s) + * should be added to the already existing selection or not. + * + * @method Selection#select + * + * @param {Object|Object[]} elements element or array of elements to be selected + * @param {boolean} [add] whether the element(s) should be appended to the current selection, defaults to false + */ + Selection.prototype.select = function(elements, add) { + var selectedElements = this._selectedElements, + oldSelection = selectedElements.slice(); + + if (!isArray$2(elements)) { + elements = elements ? [ elements ] : []; + } + + var canvas = this._canvas; + + var rootElement = canvas.getRootElement(); + + elements = elements.filter(function(element) { + var elementRoot = canvas.findRoot(element); + + return rootElement === elementRoot; + }); + + // selection may be cleared by passing an empty array or null + // to the method + if (add) { + forEach$1(elements, function(element) { + if (selectedElements.indexOf(element) !== -1) { + + // already selected + return; + } else { + selectedElements.push(element); + } + }); + } else { + this._selectedElements = selectedElements = elements.slice(); + } + + this._eventBus.fire('selection.changed', { oldSelection: oldSelection, newSelection: selectedElements }); + }; + + var MARKER_HOVER = 'hover', + MARKER_SELECTED = 'selected'; + + var SELECTION_OUTLINE_PADDING = 6; + + + /** + * A plugin that adds a visible selection UI to shapes and connections + * by appending the hover and selected classes to them. + * + * @class + * + * Makes elements selectable, too. + * + * @param {Canvas} canvas + * @param {EventBus} eventBus + */ + function SelectionVisuals(canvas, eventBus, selection) { + this._canvas = canvas; + + var self = this; + + this._multiSelectionBox = null; + + function addMarker(e, cls) { + canvas.addMarker(e, cls); + } + + function removeMarker(e, cls) { + canvas.removeMarker(e, cls); + } + + eventBus.on('element.hover', function(event) { + addMarker(event.element, MARKER_HOVER); + }); + + eventBus.on('element.out', function(event) { + removeMarker(event.element, MARKER_HOVER); + }); + + eventBus.on('selection.changed', function(event) { + + function deselect(s) { + removeMarker(s, MARKER_SELECTED); + } + + function select(s) { + addMarker(s, MARKER_SELECTED); + } + + var oldSelection = event.oldSelection, + newSelection = event.newSelection; + + forEach$1(oldSelection, function(e) { + if (newSelection.indexOf(e) === -1) { + deselect(e); + } + }); + + forEach$1(newSelection, function(e) { + if (oldSelection.indexOf(e) === -1) { + select(e); + } + }); + + self._updateSelectionOutline(newSelection); + }); + + + eventBus.on('element.changed', function(event) { + if (selection.isSelected(event.element)) { + self._updateSelectionOutline(selection.get()); + } + }); + } + + SelectionVisuals.$inject = [ + 'canvas', + 'eventBus', + 'selection' + ]; + + SelectionVisuals.prototype._updateSelectionOutline = function(selection) { + var layer = this._canvas.getLayer('selectionOutline'); + + clear$1(layer); + + var enabled = selection.length > 1; + + var container = this._canvas.getContainer(); + + classes$1(container)[enabled ? 'add' : 'remove']('djs-multi-select'); + + if (!enabled) { + return; + } + + var bBox = addSelectionOutlinePadding(getBBox(selection)); + + var rect = create$1('rect'); + + attr$1(rect, assign({ + rx: 3 + }, bBox)); + + classes$1(rect).add('djs-selection-outline'); + + append(layer, rect); + }; + + // helpers ////////// + + function addSelectionOutlinePadding(bBox) { + return { + x: bBox.x - SELECTION_OUTLINE_PADDING, + y: bBox.y - SELECTION_OUTLINE_PADDING, + width: bBox.width + SELECTION_OUTLINE_PADDING * 2, + height: bBox.height + SELECTION_OUTLINE_PADDING * 2 + }; + } + + function SelectionBehavior(eventBus, selection, canvas, elementRegistry) { + + // Select elements on create + eventBus.on('create.end', 500, function(event) { + var context = event.context, + canExecute = context.canExecute, + elements = context.elements, + hints = context.hints || {}, + autoSelect = hints.autoSelect; + + if (canExecute) { + if (autoSelect === false) { + + // Select no elements + return; + } + + if (isArray$2(autoSelect)) { + selection.select(autoSelect); + } else { + + // Select all elements by default + selection.select(elements.filter(isShown)); + } + } + }); + + // Select connection targets on connect + eventBus.on('connect.end', 500, function(event) { + var context = event.context, + connection = context.connection; + + if (connection) { + selection.select(connection); + } + }); + + // Select shapes on move + eventBus.on('shape.move.end', 500, function(event) { + var previousSelection = event.previousSelection || []; + + var shape = elementRegistry.get(event.context.shape.id); + + // Always select main shape on move + var isSelected = find(previousSelection, function(selectedShape) { + return shape.id === selectedShape.id; + }); + + if (!isSelected) { + selection.select(shape); + } + }); + + // Select elements on click + eventBus.on('element.click', function(event) { + + if (!isPrimaryButton(event)) { + return; + } + + var element = event.element; + + if (element === canvas.getRootElement()) { + element = null; + } + + var isSelected = selection.isSelected(element), + isMultiSelect = selection.get().length > 1; + + // Add to selection if CTRL or SHIFT pressed + var add = hasPrimaryModifier(event) || hasSecondaryModifier(event); + + if (isSelected && isMultiSelect) { + if (add) { + + // Deselect element + return selection.deselect(element); + } else { + + // Select element only + return selection.select(element); + } + } else if (!isSelected) { + + // Select element + selection.select(element, add); + } else { + + // Deselect element + selection.deselect(element); + } + }); + } + + SelectionBehavior.$inject = [ + 'eventBus', + 'selection', + 'canvas', + 'elementRegistry' + ]; + + + function isShown(element) { + return !element.hidden; + } + + var SelectionModule = { + __init__: [ 'selectionVisuals', 'selectionBehavior' ], + __depends__: [ + InteractionEventsModule, + OutlineModule + ], + selection: [ 'type', Selection ], + selectionVisuals: [ 'type', SelectionVisuals ], + selectionBehavior: [ 'type', SelectionBehavior ] + }; + + /** + * Util that provides unique IDs. + * + * @class djs.util.IdGenerator + * @constructor + * @memberOf djs.util + * + * The ids can be customized via a given prefix and contain a random value to avoid collisions. + * + * @param {string} prefix a prefix to prepend to generated ids (for better readability) + */ + function IdGenerator(prefix) { + + this._counter = 0; + this._prefix = (prefix ? prefix + '-' : '') + Math.floor(Math.random() * 1000000000) + '-'; + } + + /** + * Returns a next unique ID. + * + * @method djs.util.IdGenerator#next + * + * @returns {string} the id + */ + IdGenerator.prototype.next = function() { + return this._prefix + (++this._counter); + }; + + // document wide unique overlay ids + var ids = new IdGenerator('ov'); + + var LOW_PRIORITY$1 = 500; + + + /** + * A service that allows users to attach overlays to diagram elements. + * + * The overlay service will take care of overlay positioning during updates. + * + * @example + * + * // add a pink badge on the top left of the shape + * overlays.add(someShape, { + * position: { + * top: -5, + * left: -5 + * }, + * html: '
          0
          ' + * }); + * + * // or add via shape id + * + * overlays.add('some-element-id', { + * position: { + * top: -5, + * left: -5 + * } + * html: '
          0
          ' + * }); + * + * // or add with optional type + * + * overlays.add(someShape, 'badge', { + * position: { + * top: -5, + * left: -5 + * } + * html: '
          0
          ' + * }); + * + * + * // remove an overlay + * + * var id = overlays.add(...); + * overlays.remove(id); + * + * + * You may configure overlay defaults during tool by providing a `config` module + * with `overlays.defaults` as an entry: + * + * { + * overlays: { + * defaults: { + * show: { + * minZoom: 0.7, + * maxZoom: 5.0 + * }, + * scale: { + * min: 1 + * } + * } + * } + * + * @param {Object} config + * @param {EventBus} eventBus + * @param {Canvas} canvas + * @param {ElementRegistry} elementRegistry + */ + function Overlays(config, eventBus, canvas, elementRegistry) { + + this._eventBus = eventBus; + this._canvas = canvas; + this._elementRegistry = elementRegistry; + + this._ids = ids; + + this._overlayDefaults = assign({ + + // no show constraints + show: null, + + // always scale + scale: true + }, config && config.defaults); + + /** + * Mapping overlayId -> overlay + */ + this._overlays = {}; + + /** + * Mapping elementId -> overlay container + */ + this._overlayContainers = []; + + // root html element for all overlays + this._overlayRoot = createRoot(canvas.getContainer()); + + this._init(); + } + + + Overlays.$inject = [ + 'config.overlays', + 'eventBus', + 'canvas', + 'elementRegistry' + ]; + + + /** + * Returns the overlay with the specified id or a list of overlays + * for an element with a given type. + * + * @example + * + * // return the single overlay with the given id + * overlays.get('some-id'); + * + * // return all overlays for the shape + * overlays.get({ element: someShape }); + * + * // return all overlays on shape with type 'badge' + * overlays.get({ element: someShape, type: 'badge' }); + * + * // shape can also be specified as id + * overlays.get({ element: 'element-id', type: 'badge' }); + * + * + * @param {Object} search + * @param {string} [search.id] + * @param {string|djs.model.Base} [search.element] + * @param {string} [search.type] + * + * @return {Object|Array} the overlay(s) + */ + Overlays.prototype.get = function(search) { + + if (isString(search)) { + search = { id: search }; + } + + if (isString(search.element)) { + search.element = this._elementRegistry.get(search.element); + } + + if (search.element) { + var container = this._getOverlayContainer(search.element, true); + + // return a list of overlays when searching by element (+type) + if (container) { + return search.type ? filter(container.overlays, matchPattern({ type: search.type })) : container.overlays.slice(); + } else { + return []; + } + } else + if (search.type) { + return filter(this._overlays, matchPattern({ type: search.type })); + } else { + + // return single element when searching by id + return search.id ? this._overlays[search.id] : null; + } + }; + + /** + * Adds a HTML overlay to an element. + * + * @param {string|djs.model.Base} element attach overlay to this shape + * @param {string} [type] optional type to assign to the overlay + * @param {Object} overlay the overlay configuration + * + * @param {string|DOMElement} overlay.html html element to use as an overlay + * @param {Object} [overlay.show] show configuration + * @param {number} [overlay.show.minZoom] minimal zoom level to show the overlay + * @param {number} [overlay.show.maxZoom] maximum zoom level to show the overlay + * @param {Object} overlay.position where to attach the overlay + * @param {number} [overlay.position.left] relative to element bbox left attachment + * @param {number} [overlay.position.top] relative to element bbox top attachment + * @param {number} [overlay.position.bottom] relative to element bbox bottom attachment + * @param {number} [overlay.position.right] relative to element bbox right attachment + * @param {boolean|Object} [overlay.scale=true] false to preserve the same size regardless of + * diagram zoom + * @param {number} [overlay.scale.min] + * @param {number} [overlay.scale.max] + * + * @return {string} id that may be used to reference the overlay for update or removal + */ + Overlays.prototype.add = function(element, type, overlay) { + + if (isObject(type)) { + overlay = type; + type = null; + } + + if (!element.id) { + element = this._elementRegistry.get(element); + } + + if (!overlay.position) { + throw new Error('must specifiy overlay position'); + } + + if (!overlay.html) { + throw new Error('must specifiy overlay html'); + } + + if (!element) { + throw new Error('invalid element specified'); + } + + var id = this._ids.next(); + + overlay = assign({}, this._overlayDefaults, overlay, { + id: id, + type: type, + element: element, + html: overlay.html + }); + + this._addOverlay(overlay); + + return id; + }; + + + /** + * Remove an overlay with the given id or all overlays matching the given filter. + * + * @see Overlays#get for filter options. + * + * @param {string|object} [filter] + */ + Overlays.prototype.remove = function(filter) { + + var overlays = this.get(filter) || []; + + if (!isArray$2(overlays)) { + overlays = [ overlays ]; + } + + var self = this; + + forEach$1(overlays, function(overlay) { + + var container = self._getOverlayContainer(overlay.element, true); + + if (overlay) { + remove$1(overlay.html); + remove$1(overlay.htmlContainer); + + delete overlay.htmlContainer; + delete overlay.element; + + delete self._overlays[overlay.id]; + } + + if (container) { + var idx = container.overlays.indexOf(overlay); + if (idx !== -1) { + container.overlays.splice(idx, 1); + } + } + }); + + }; + + + Overlays.prototype.show = function() { + setVisible(this._overlayRoot); + }; + + + Overlays.prototype.hide = function() { + setVisible(this._overlayRoot, false); + }; + + Overlays.prototype.clear = function() { + this._overlays = {}; + + this._overlayContainers = []; + + clear(this._overlayRoot); + }; + + Overlays.prototype._updateOverlayContainer = function(container) { + var element = container.element, + html = container.html; + + // update container left,top according to the elements x,y coordinates + // this ensures we can attach child elements relative to this container + + var x = element.x, + y = element.y; + + if (element.waypoints) { + var bbox = getBBox(element); + x = bbox.x; + y = bbox.y; + } + + setPosition(html, x, y); + + attr(container.html, 'data-container-id', element.id); + }; + + + Overlays.prototype._updateOverlay = function(overlay) { + + var position = overlay.position, + htmlContainer = overlay.htmlContainer, + element = overlay.element; + + // update overlay html relative to shape because + // it is already positioned on the element + + // update relative + var left = position.left, + top = position.top; + + if (position.right !== undefined) { + + var width; + + if (element.waypoints) { + width = getBBox(element).width; + } else { + width = element.width; + } + + left = position.right * -1 + width; + } + + if (position.bottom !== undefined) { + + var height; + + if (element.waypoints) { + height = getBBox(element).height; + } else { + height = element.height; + } + + top = position.bottom * -1 + height; + } + + setPosition(htmlContainer, left || 0, top || 0); + this._updateOverlayVisibilty(overlay, this._canvas.viewbox()); + }; + + + Overlays.prototype._createOverlayContainer = function(element) { + var html = domify('
          '); + assign$1(html, { position: 'absolute' }); + + this._overlayRoot.appendChild(html); + + var container = { + html: html, + element: element, + overlays: [] + }; + + this._updateOverlayContainer(container); + + this._overlayContainers.push(container); + + return container; + }; + + + Overlays.prototype._updateRoot = function(viewbox) { + var scale = viewbox.scale || 1; + + var matrix = 'matrix(' + + [ + scale, + 0, + 0, + scale, + -1 * viewbox.x * scale, + -1 * viewbox.y * scale + ].join(',') + + ')'; + + setTransform(this._overlayRoot, matrix); + }; + + + Overlays.prototype._getOverlayContainer = function(element, raw) { + var container = find(this._overlayContainers, function(c) { + return c.element === element; + }); + + + if (!container && !raw) { + return this._createOverlayContainer(element); + } + + return container; + }; + + + Overlays.prototype._addOverlay = function(overlay) { + + var id = overlay.id, + element = overlay.element, + html = overlay.html, + htmlContainer, + overlayContainer; + + // unwrap jquery (for those who need it) + if (html.get && html.constructor.prototype.jquery) { + html = html.get(0); + } + + // create proper html elements from + // overlay HTML strings + if (isString(html)) { + html = domify(html); + } + + overlayContainer = this._getOverlayContainer(element); + + htmlContainer = domify('
          '); + assign$1(htmlContainer, { position: 'absolute' }); + + htmlContainer.appendChild(html); + + if (overlay.type) { + classes(htmlContainer).add('djs-overlay-' + overlay.type); + } + + var elementRoot = this._canvas.findRoot(element); + var activeRoot = this._canvas.getRootElement(); + + setVisible(htmlContainer, elementRoot === activeRoot); + + overlay.htmlContainer = htmlContainer; + + overlayContainer.overlays.push(overlay); + overlayContainer.html.appendChild(htmlContainer); + + this._overlays[id] = overlay; + + this._updateOverlay(overlay); + this._updateOverlayVisibilty(overlay, this._canvas.viewbox()); + }; + + + Overlays.prototype._updateOverlayVisibilty = function(overlay, viewbox) { + var show = overlay.show, + rootElement = this._canvas.findRoot(overlay.element), + minZoom = show && show.minZoom, + maxZoom = show && show.maxZoom, + htmlContainer = overlay.htmlContainer, + activeRootElement = this._canvas.getRootElement(), + visible = true; + + if (rootElement !== activeRootElement) { + visible = false; + } else if (show) { + if ( + (isDefined(minZoom) && minZoom > viewbox.scale) || + (isDefined(maxZoom) && maxZoom < viewbox.scale) + ) { + visible = false; + } + } + + setVisible(htmlContainer, visible); + + this._updateOverlayScale(overlay, viewbox); + }; + + + Overlays.prototype._updateOverlayScale = function(overlay, viewbox) { + var shouldScale = overlay.scale, + minScale, + maxScale, + htmlContainer = overlay.htmlContainer; + + var scale, transform = ''; + + if (shouldScale !== true) { + + if (shouldScale === false) { + minScale = 1; + maxScale = 1; + } else { + minScale = shouldScale.min; + maxScale = shouldScale.max; + } + + if (isDefined(minScale) && viewbox.scale < minScale) { + scale = (1 / viewbox.scale || 1) * minScale; + } + + if (isDefined(maxScale) && viewbox.scale > maxScale) { + scale = (1 / viewbox.scale || 1) * maxScale; + } + } + + if (isDefined(scale)) { + transform = 'scale(' + scale + ',' + scale + ')'; + } + + setTransform(htmlContainer, transform); + }; + + + Overlays.prototype._updateOverlaysVisibilty = function(viewbox) { + + var self = this; + + forEach$1(this._overlays, function(overlay) { + self._updateOverlayVisibilty(overlay, viewbox); + }); + }; + + + Overlays.prototype._init = function() { + + var eventBus = this._eventBus; + + var self = this; + + + // scroll/zoom integration + + function updateViewbox(viewbox) { + self._updateRoot(viewbox); + self._updateOverlaysVisibilty(viewbox); + + self.show(); + } + + eventBus.on('canvas.viewbox.changing', function(event) { + self.hide(); + }); + + eventBus.on('canvas.viewbox.changed', function(event) { + updateViewbox(event.viewbox); + }); + + + // remove integration + + eventBus.on([ 'shape.remove', 'connection.remove' ], function(e) { + var element = e.element; + var overlays = self.get({ element: element }); + + forEach$1(overlays, function(o) { + self.remove(o.id); + }); + + var container = self._getOverlayContainer(element); + + if (container) { + remove$1(container.html); + var i = self._overlayContainers.indexOf(container); + if (i !== -1) { + self._overlayContainers.splice(i, 1); + } + } + }); + + + // move integration + + eventBus.on('element.changed', LOW_PRIORITY$1, function(e) { + var element = e.element; + + var container = self._getOverlayContainer(element, true); + + if (container) { + forEach$1(container.overlays, function(overlay) { + self._updateOverlay(overlay); + }); + + self._updateOverlayContainer(container); + } + }); + + + // marker integration, simply add them on the overlays as classes, too. + + eventBus.on('element.marker.update', function(e) { + var container = self._getOverlayContainer(e.element, true); + if (container) { + classes(container.html)[e.add ? 'add' : 'remove'](e.marker); + } + }); + + + eventBus.on('root.set', function() { + self._updateOverlaysVisibilty(self._canvas.viewbox()); + }); + + // clear overlays with diagram + + eventBus.on('diagram.clear', this.clear, this); + }; + + + + // helpers ///////////////////////////// + + function createRoot(parentNode) { + var root = domify( + '
          ' + ); + + assign$1(root, { + position: 'absolute', + width: 0, + height: 0 + }); + + parentNode.insertBefore(root, parentNode.firstChild); + + return root; + } + + function setPosition(el, x, y) { + assign$1(el, { left: x + 'px', top: y + 'px' }); + } + + /** + * Set element visible + * + * @param {DOMElement} el + * @param {boolean} [visible=true] + */ + function setVisible(el, visible) { + el.style.display = visible === false ? 'none' : ''; + } + + function setTransform(el, transform) { + + el.style['transform-origin'] = 'top left'; + + [ '', '-ms-', '-webkit-' ].forEach(function(prefix) { + el.style[prefix + 'transform'] = transform; + }); + } + + var OverlaysModule = { + __init__: [ 'overlays' ], + overlays: [ 'type', Overlays ] + }; + + /** + * Adds change support to the diagram, including + * + *
            + *
          • redrawing shapes and connections on change
          • + *
          + * + * @param {EventBus} eventBus + * @param {Canvas} canvas + * @param {ElementRegistry} elementRegistry + * @param {GraphicsFactory} graphicsFactory + */ + function ChangeSupport( + eventBus, canvas, elementRegistry, + graphicsFactory) { + + + // redraw shapes / connections on change + + eventBus.on('element.changed', function(event) { + + var element = event.element; + + // element might have been deleted and replaced by new element with same ID + // thus check for parent of element except for root element + if (element.parent || element === canvas.getRootElement()) { + event.gfx = elementRegistry.getGraphics(element); + } + + // shape + gfx may have been deleted + if (!event.gfx) { + return; + } + + eventBus.fire(getType(element) + '.changed', event); + }); + + eventBus.on('elements.changed', function(event) { + + var elements = event.elements; + + elements.forEach(function(e) { + eventBus.fire('element.changed', { element: e }); + }); + + graphicsFactory.updateContainments(elements); + }); + + eventBus.on('shape.changed', function(event) { + graphicsFactory.update('shape', event.element, event.gfx); + }); + + eventBus.on('connection.changed', function(event) { + graphicsFactory.update('connection', event.element, event.gfx); + }); + } + + ChangeSupport.$inject = [ + 'eventBus', + 'canvas', + 'elementRegistry', + 'graphicsFactory' + ]; + + var ChangeSupportModule = { + __init__: [ 'changeSupport' ], + changeSupport: [ 'type', ChangeSupport ] + }; + + var DEFAULT_PRIORITY$1 = 1000; + + /** + * A utility that can be used to plug-in into the command execution for + * extension and/or validation. + * + * @param {EventBus} eventBus + * + * @example + * + * import inherits from 'inherits-browser'; + * + * import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + * + * function CommandLogger(eventBus) { + * CommandInterceptor.call(this, eventBus); + * + * this.preExecute(function(event) { + * console.log('command pre-execute', event); + * }); + * } + * + * inherits(CommandLogger, CommandInterceptor); + * + */ + function CommandInterceptor(eventBus) { + this._eventBus = eventBus; + } + + CommandInterceptor.$inject = [ 'eventBus' ]; + + function unwrapEvent(fn, that) { + return function(event) { + return fn.call(that || null, event.context, event.command, event); + }; + } + + /** + * Register an interceptor for a command execution + * + * @param {string|Array} [events] list of commands to register on + * @param {string} [hook] command hook, i.e. preExecute, executed to listen on + * @param {number} [priority] the priority on which to hook into the execution + * @param {Function} handlerFn interceptor to be invoked with (event) + * @param {boolean} unwrap if true, unwrap the event and pass (context, command, event) to the + * listener instead + * @param {Object} [that] Pass context (`this`) to the handler function + */ + CommandInterceptor.prototype.on = function(events, hook, priority, handlerFn, unwrap, that) { + + if (isFunction(hook) || isNumber(hook)) { + that = unwrap; + unwrap = handlerFn; + handlerFn = priority; + priority = hook; + hook = null; + } + + if (isFunction(priority)) { + that = unwrap; + unwrap = handlerFn; + handlerFn = priority; + priority = DEFAULT_PRIORITY$1; + } + + if (isObject(unwrap)) { + that = unwrap; + unwrap = false; + } + + if (!isFunction(handlerFn)) { + throw new Error('handlerFn must be a function'); + } + + if (!isArray$2(events)) { + events = [ events ]; + } + + var eventBus = this._eventBus; + + forEach$1(events, function(event) { + + // concat commandStack(.event)?(.hook)? + var fullEvent = [ 'commandStack', event, hook ].filter(function(e) { return e; }).join('.'); + + eventBus.on(fullEvent, priority, unwrap ? unwrapEvent(handlerFn, that) : handlerFn, that); + }); + }; + + + var hooks = [ + 'canExecute', + 'preExecute', + 'preExecuted', + 'execute', + 'executed', + 'postExecute', + 'postExecuted', + 'revert', + 'reverted' + ]; + + /* + * Install hook shortcuts + * + * This will generate the CommandInterceptor#(preExecute|...|reverted) methods + * which will in term forward to CommandInterceptor#on. + */ + forEach$1(hooks, function(hook) { + + /** + * {canExecute|preExecute|preExecuted|execute|executed|postExecute|postExecuted|revert|reverted} + * + * A named hook for plugging into the command execution + * + * @param {string|Array} [events] list of commands to register on + * @param {number} [priority] the priority on which to hook into the execution + * @param {Function} handlerFn interceptor to be invoked with (event) + * @param {boolean} [unwrap=false] if true, unwrap the event and pass (context, command, event) to the + * listener instead + * @param {Object} [that] Pass context (`this`) to the handler function + */ + CommandInterceptor.prototype[hook] = function(events, priority, handlerFn, unwrap, that) { + + if (isFunction(events) || isNumber(events)) { + that = unwrap; + unwrap = handlerFn; + handlerFn = priority; + priority = events; + events = null; + } + + this.on(events, hook, priority, handlerFn, unwrap, that); + }; + }); + + /** + * A modeling behavior that ensures we set the correct root element + * as we undo and redo commands. + * + * @param {Canvas} canvas + * @param {didi.Injector} injector + */ + function RootElementsBehavior(canvas, injector) { + + injector.invoke(CommandInterceptor, this); + + this.executed(function(event) { + var context = event.context; + + if (context.rootElement) { + canvas.setRootElement(context.rootElement); + } else { + context.rootElement = canvas.getRootElement(); + } + }); + + this.revert(function(event) { + var context = event.context; + + if (context.rootElement) { + canvas.setRootElement(context.rootElement); + } + }); + } + + e(RootElementsBehavior, CommandInterceptor); + + RootElementsBehavior.$inject = [ 'canvas', 'injector' ]; + + var RootElementsModule = { + __init__: [ 'rootElementsBehavior' ], + rootElementsBehavior: [ 'type', RootElementsBehavior ] + }; + + var css_escape = {exports: {}}; + + /*! https://mths.be/cssescape v1.5.1 by @mathias | MIT license */ + + (function (module, exports) { + (function(root, factory) { + // https://github.com/umdjs/umd/blob/master/returnExports.js + { + // For Node.js. + module.exports = factory(root); + } + }(typeof commonjsGlobal != 'undefined' ? commonjsGlobal : commonjsGlobal, function(root) { + + if (root.CSS && root.CSS.escape) { + return root.CSS.escape; + } + + // https://drafts.csswg.org/cssom/#serialize-an-identifier + var cssEscape = function(value) { + if (arguments.length == 0) { + throw new TypeError('`CSS.escape` requires an argument.'); + } + var string = String(value); + var length = string.length; + var index = -1; + var codeUnit; + var result = ''; + var firstCodeUnit = string.charCodeAt(0); + while (++index < length) { + codeUnit = string.charCodeAt(index); + // Note: there’s no need to special-case astral symbols, surrogate + // pairs, or lone surrogates. + + // If the character is NULL (U+0000), then the REPLACEMENT CHARACTER + // (U+FFFD). + if (codeUnit == 0x0000) { + result += '\uFFFD'; + continue; + } + + if ( + // If the character is in the range [\1-\1F] (U+0001 to U+001F) or is + // U+007F, […] + (codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F || + // If the character is the first character and is in the range [0-9] + // (U+0030 to U+0039), […] + (index == 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) || + // If the character is the second character and is in the range [0-9] + // (U+0030 to U+0039) and the first character is a `-` (U+002D), […] + ( + index == 1 && + codeUnit >= 0x0030 && codeUnit <= 0x0039 && + firstCodeUnit == 0x002D + ) + ) { + // https://drafts.csswg.org/cssom/#escape-a-character-as-code-point + result += '\\' + codeUnit.toString(16) + ' '; + continue; + } + + if ( + // If the character is the first character and is a `-` (U+002D), and + // there is no second character, […] + index == 0 && + length == 1 && + codeUnit == 0x002D + ) { + result += '\\' + string.charAt(index); + continue; + } + + // If the character is not handled by one of the above rules and is + // greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or + // is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to + // U+005A), or [a-z] (U+0061 to U+007A), […] + if ( + codeUnit >= 0x0080 || + codeUnit == 0x002D || + codeUnit == 0x005F || + codeUnit >= 0x0030 && codeUnit <= 0x0039 || + codeUnit >= 0x0041 && codeUnit <= 0x005A || + codeUnit >= 0x0061 && codeUnit <= 0x007A + ) { + // the character itself + result += string.charAt(index); + continue; + } + + // Otherwise, the escaped character. + // https://drafts.csswg.org/cssom/#escape-a-character + result += '\\' + string.charAt(index); + + } + return result; + }; + + if (!root.CSS) { + root.CSS = {}; + } + + root.CSS.escape = cssEscape; + return cssEscape; + + })); + } (css_escape)); + + var HTML_ESCAPE_MAP = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + '\'': ''' + }; + + function escapeHTML(str) { + str = '' + str; + + return str && str.replace(/[&<>"']/g, function(match) { + return HTML_ESCAPE_MAP[match]; + }); + } + + var planeSuffix = '_plane'; + + /** + * Get plane ID for a primary shape. + * + * @param {djs.model.Base|ModdleElement} element + * + * @returns {String} + */ + function getPlaneIdFromShape(element) { + var id = element.id; + + if (is$1(element, 'bpmn:SubProcess')) { + return addPlaneSuffix(id); + } + + return id; + } + + function addPlaneSuffix(id) { + return id + planeSuffix; + } + + var OPEN_CLASS = 'bjs-breadcrumbs-shown'; + + + /** + * Adds overlays that allow switching planes on collapsed subprocesses. + * + * @param {eventBus} eventBus + * @param {elementRegistry} elementRegistry + * @param {overlays} overlays + * @param {canvas} canvas + */ + function DrilldownBreadcrumbs(eventBus, elementRegistry, overlays, canvas) { + var breadcrumbs = domify('
            '); + var container = canvas.getContainer(); + var containerClasses = classes(container); + container.appendChild(breadcrumbs); + + var boParents = []; + + // update breadcrumbs if name or ID of the primary shape changes + eventBus.on('element.changed', function(e) { + var shape = e.element, + bo = getBusinessObject(shape); + + var isPresent = find(boParents, function(el) { + return el === bo; + }); + + if (!isPresent) { + return; + } + + updateBreadcrumbs(); + }); + + /** + * Updates the displayed breadcrumbs. If no element is provided, only the + * labels are updated. + * + * @param {djs.model.Base} [element] + */ + function updateBreadcrumbs(element) { + if (element) { + boParents = getBoParentChain(element); + } + + var path = boParents.map(function(parent) { + var title = escapeHTML(parent.name || parent.id); + var link = domify('
          • ' + title + '
          • '); + + var parentPlane = canvas.findRoot(getPlaneIdFromShape(parent)) || canvas.findRoot(parent.id); + + // when the root is a collaboration, the process does not have a corresponding + // element in the elementRegisty. Instead, we search for the corresponding participant + if (!parentPlane && is$1(parent, 'bpmn:Process')) { + var participant = elementRegistry.find(function(element) { + var bo = getBusinessObject(element); + return bo && bo.processRef && bo.processRef === parent; + }); + + parentPlane = canvas.findRoot(participant.id); + } + + link.addEventListener('click', function() { + canvas.setRootElement(parentPlane); + }); + + return link; + }); + + breadcrumbs.innerHTML = ''; + + // show breadcrumbs and expose state to .djs-container + var visible = path.length > 1; + containerClasses.toggle(OPEN_CLASS, visible); + + path.forEach(function(el) { + breadcrumbs.appendChild(el); + }); + } + + eventBus.on('root.set', function(event) { + updateBreadcrumbs(event.element); + }); + + } + + DrilldownBreadcrumbs.$inject = [ 'eventBus', 'elementRegistry', 'overlays', 'canvas' ]; + + + // helpers ////////// + + /** + * Returns the parents for the element using the business object chain, + * starting with the root element. + * + * @param {djs.model.Shape} child + * + * @returns {Array} parents + */ + function getBoParentChain(child) { + var bo = getBusinessObject(child); + + var parents = []; + + for (var element = bo; element; element = element.$parent) { + if (is$1(element, 'bpmn:SubProcess') || is$1(element, 'bpmn:Process')) { + parents.push(element); + } + } + + return parents.reverse(); + } + + /** + * Move collapsed subprocesses into view when drilling down. + * + * Zoom and scroll are saved in a session. + * + * @param {eventBus} eventBus + * @param {canvas} canvas + */ + function DrilldownCentering(eventBus, canvas) { + + var currentRoot = null; + var positionMap = new Map(); + + eventBus.on('root.set', function(event) { + var newRoot = event.element; + var currentViewbox = canvas.viewbox(); + var storedViewbox = positionMap.get(newRoot); + + positionMap.set(currentRoot, { + x: currentViewbox.x, + y: currentViewbox.y, + zoom: currentViewbox.scale + }); + + currentRoot = newRoot; + + // current root was replaced with a collaboration, we don't update the viewbox + if (is$1(newRoot, 'bpmn:Collaboration') && !storedViewbox) { + return; + } + + storedViewbox = storedViewbox || { x: 0, y: 0, zoom: 1 }; + + var dx = (currentViewbox.x - storedViewbox.x) * currentViewbox.scale, + dy = (currentViewbox.y - storedViewbox.y) * currentViewbox.scale; + + if (dx !== 0 || dy !== 0) { + canvas.scroll({ + dx: dx, + dy: dy + }); + } + + if (storedViewbox.zoom !== currentViewbox.scale) { + canvas.zoom(storedViewbox.zoom, { x: 0, y: 0 }); + } + }); + + eventBus.on('diagram.clear', function() { + positionMap.clear(); + currentRoot = null; + }); + + } + + DrilldownCentering.$inject = [ 'eventBus', 'canvas' ]; + + + /** + * ES5 Map implementation. Works. + */ + function Map() { + + this._entries = []; + + this.set = function(key, value) { + + var found = false; + + for (var k in this._entries) { + if (this._entries[k][0] === key) { + this._entries[k][1] = value; + + found = true; + + break; + } + } + + if (!found) { + this._entries.push([ key, value ]); + } + }; + + this.get = function(key) { + + for (var k in this._entries) { + if (this._entries[k][0] === key) { + return this._entries[k][1]; + } + } + + return null; + }; + + this.clear = function() { + this._entries.length = 0; + }; + + this.remove = function(key) { + + var idx = -1; + + for (var k in this._entries) { + if (this._entries[k][0] === key) { + idx = k; + + break; + } + } + + if (idx !== -1) { + this._entries.splice(idx, 1); + } + }; + } + + var DEFAULT_POSITION = { + x: 180, + y: 160 + }; + + /** + * Hook into `import.render.start` and create new planes for diagrams with + * collapsed subprocesses and all dis on the same plane. + * + * @param {eventBus} eventBus + * @param {moddle} moddle + */ + function SubprocessCompatibility(eventBus, moddle) { + this._eventBus = eventBus; + this._moddle = moddle; + + var self = this; + + eventBus.on('import.render.start', 1500, function(e, context) { + self.handleImport(context.definitions); + }); + } + + SubprocessCompatibility.prototype.handleImport = function(definitions) { + if (!definitions.diagrams) { + return; + } + + var self = this; + this._definitions = definitions; + this._processToDiagramMap = {}; + + definitions.diagrams.forEach(function(diagram) { + if (!diagram.plane || !diagram.plane.bpmnElement) { + return; + } + + self._processToDiagramMap[diagram.plane.bpmnElement.id] = diagram; + }); + + var newDiagrams = []; + definitions.diagrams.forEach(function(diagram) { + var createdDiagrams = self.createNewDiagrams(diagram.plane); + Array.prototype.push.apply(newDiagrams, createdDiagrams); + }); + + newDiagrams.forEach(function(diagram) { + self.movePlaneElementsToOrigin(diagram.plane); + }); + }; + + + /** + * Moves all DI elements from collapsed subprocesses to a new plane. + * + * @param {Object} plane + * @return {Array} new diagrams created for the collapsed subprocesses + */ + SubprocessCompatibility.prototype.createNewDiagrams = function(plane) { + var self = this; + + var collapsedElements = []; + var elementsToMove = []; + + plane.get('planeElement').forEach(function(diElement) { + var bo = diElement.bpmnElement; + + if (!bo) { + return; + } + + var parent = bo.$parent; + + if (is$1(bo, 'bpmn:SubProcess') && !diElement.isExpanded) { + collapsedElements.push(bo); + } + + if (shouldMoveToPlane(bo, plane)) { + + // don't change the array while we iterate over it + elementsToMove.push({ diElement: diElement, parent: parent }); + } + }); + + var newDiagrams = []; + + // create new planes for all collapsed subprocesses, even when they are empty + collapsedElements.forEach(function(element) { + if (!self._processToDiagramMap[element.id]) { + var diagram = self.createDiagram(element); + self._processToDiagramMap[element.id] = diagram; + newDiagrams.push(diagram); + } + }); + + elementsToMove.forEach(function(element) { + var diElement = element.diElement; + var parent = element.parent; + + // parent is expanded, get nearest collapsed parent + while (parent && collapsedElements.indexOf(parent) === -1) { + parent = parent.$parent; + } + + // false positive, all parents are expanded + if (!parent) { + return; + } + + var diagram = self._processToDiagramMap[parent.id]; + self.moveToDiPlane(diElement, diagram.plane); + }); + + return newDiagrams; + }; + + SubprocessCompatibility.prototype.movePlaneElementsToOrigin = function(plane) { + var elements = plane.get('planeElement'); + + // get bounding box of all elements + var planeBounds = getPlaneBounds(plane); + + var offset = { + x: planeBounds.x - DEFAULT_POSITION.x, + y: planeBounds.y - DEFAULT_POSITION.y + }; + + elements.forEach(function(diElement) { + if (diElement.waypoint) { + diElement.waypoint.forEach(function(waypoint) { + waypoint.x = waypoint.x - offset.x; + waypoint.y = waypoint.y - offset.y; + }); + } else if (diElement.bounds) { + diElement.bounds.x = diElement.bounds.x - offset.x; + diElement.bounds.y = diElement.bounds.y - offset.y; + } + }); + }; + + + SubprocessCompatibility.prototype.moveToDiPlane = function(diElement, newPlane) { + var containingDiagram = findRootDiagram(diElement); + + // remove DI from old Plane and add it to the new one + var parentPlaneElement = containingDiagram.plane.get('planeElement'); + parentPlaneElement.splice(parentPlaneElement.indexOf(diElement), 1); + newPlane.get('planeElement').push(diElement); + }; + + + SubprocessCompatibility.prototype.createDiagram = function(bo) { + var plane = this._moddle.create('bpmndi:BPMNPlane', { bpmnElement: bo }); + var diagram = this._moddle.create('bpmndi:BPMNDiagram', { + plane: plane + }); + plane.$parent = diagram; + plane.bpmnElement = bo; + diagram.$parent = this._definitions; + this._definitions.diagrams.push(diagram); + return diagram; + }; + + SubprocessCompatibility.$inject = [ 'eventBus', 'moddle' ]; + + + // helpers ////////////////////////// + + function findRootDiagram(element) { + if (is$1(element, 'bpmndi:BPMNDiagram')) { + return element; + } else { + return findRootDiagram(element.$parent); + } + } + + function getPlaneBounds(plane) { + var planeTrbl = { + top: Infinity, + right: -Infinity, + bottom: -Infinity, + left: Infinity + }; + + plane.planeElement.forEach(function(element) { + if (!element.bounds) { + return; + } + + var trbl = asTRBL(element.bounds); + + planeTrbl.top = Math.min(trbl.top, planeTrbl.top); + planeTrbl.left = Math.min(trbl.left, planeTrbl.left); + }); + + return asBounds(planeTrbl); + } + + function shouldMoveToPlane(bo, plane) { + var parent = bo.$parent; + + // don't move elements that are already on the plane + if (!is$1(parent, 'bpmn:SubProcess') || parent === plane.bpmnElement) { + return false; + } + + // dataAssociations are children of the subprocess but rendered on process level + // cf. https://github.com/bpmn-io/bpmn-js/issues/1619 + if (isAny(bo, [ 'bpmn:DataInputAssociation', 'bpmn:DataOutputAssociation' ])) { + return false; + } + + return true; + } + + var LOW_PRIORITY = 250; + var ARROW_DOWN_SVG = ''; + + var EMPTY_MARKER = 'bjs-drilldown-empty'; + + function DrilldownOverlayBehavior( + canvas, eventBus, elementRegistry, overlays + ) { + CommandInterceptor.call(this, eventBus); + + this._canvas = canvas; + this._eventBus = eventBus; + this._elementRegistry = elementRegistry; + this._overlays = overlays; + + var self = this; + + this.executed('shape.toggleCollapse', LOW_PRIORITY, function(context) { + var shape = context.shape; + + // Add overlay to the collapsed shape + if (self.canDrillDown(shape)) { + self.addOverlay(shape); + } else { + self.removeOverlay(shape); + } + }, true); + + + this.reverted('shape.toggleCollapse', LOW_PRIORITY, function(context) { + var shape = context.shape; + + // Add overlay to the collapsed shape + if (self.canDrillDown(shape)) { + self.addOverlay(shape); + } else { + self.removeOverlay(shape); + } + }, true); + + + this.executed([ 'shape.create', 'shape.move', 'shape.delete' ], LOW_PRIORITY, + function(context) { + var oldParent = context.oldParent, + newParent = context.newParent || context.parent, + shape = context.shape; + + // Add overlay to the collapsed shape + if (self.canDrillDown(shape)) { + self.addOverlay(shape); + } + + self.updateDrilldownOverlay(oldParent); + self.updateDrilldownOverlay(newParent); + self.updateDrilldownOverlay(shape); + }, true); + + + this.reverted([ 'shape.create', 'shape.move', 'shape.delete' ], LOW_PRIORITY, + function(context) { + var oldParent = context.oldParent, + newParent = context.newParent || context.parent, + shape = context.shape; + + // Add overlay to the collapsed shape + if (self.canDrillDown(shape)) { + self.addOverlay(shape); + } + + self.updateDrilldownOverlay(oldParent); + self.updateDrilldownOverlay(newParent); + self.updateDrilldownOverlay(shape); + }, true); + + + eventBus.on('import.render.complete', function() { + elementRegistry.filter(function(e) { + return self.canDrillDown(e); + }).map(function(el) { + self.addOverlay(el); + }); + }); + + } + + e(DrilldownOverlayBehavior, CommandInterceptor); + + DrilldownOverlayBehavior.prototype.updateDrilldownOverlay = function(shape) { + var canvas = this._canvas; + + if (!shape) { + return; + } + + var root = canvas.findRoot(shape); + if (root) { + this.updateOverlayVisibility(root); + } + }; + + + DrilldownOverlayBehavior.prototype.canDrillDown = function(element) { + var canvas = this._canvas; + return is$1(element, 'bpmn:SubProcess') && canvas.findRoot(getPlaneIdFromShape(element)); + }; + + /** + * Updates visibility of the drilldown overlay. If the plane has no elements, + * the drilldown will be only shown when the element is selected. + * + * @param {djs.model.Shape|djs.model.Root} element collapsed shape or root element + */ + DrilldownOverlayBehavior.prototype.updateOverlayVisibility = function(element) { + var overlays = this._overlays; + + var bo = element.businessObject; + + var overlay = overlays.get({ element: bo.id, type: 'drilldown' })[0]; + + if (!overlay) { + return; + } + + var hasContent = bo && bo.flowElements && bo.flowElements.length; + classes(overlay.html).toggle(EMPTY_MARKER, !hasContent); + }; + + /** + * Attaches a drilldown button to the given element. We assume that the plane has + * the same id as the element. + * + * @param {djs.model.Shape} element collapsed shape + */ + DrilldownOverlayBehavior.prototype.addOverlay = function(element) { + var canvas = this._canvas; + var overlays = this._overlays; + + var existingOverlays = overlays.get({ element: element, type: 'drilldown' }); + if (existingOverlays.length) { + this.removeOverlay(element); + } + + var button = domify(''); + + button.addEventListener('click', function() { + canvas.setRootElement(canvas.findRoot(getPlaneIdFromShape(element))); + }); + + overlays.add(element, 'drilldown', { + position: { + bottom: -7, + right: -8 + }, + html: button + }); + + this.updateOverlayVisibility(element); + }; + + DrilldownOverlayBehavior.prototype.removeOverlay = function(element) { + var overlays = this._overlays; + + overlays.remove({ + element: element, + type: 'drilldown' + }); + }; + + DrilldownOverlayBehavior.$inject = [ + 'canvas', + 'eventBus', + 'elementRegistry', + 'overlays' + ]; + + var DrilldownModdule = { + __depends__: [ OverlaysModule, ChangeSupportModule, RootElementsModule ], + __init__: [ 'drilldownBreadcrumbs', 'drilldownOverlayBehavior', 'drilldownCentering', 'subprocessCompatibility' ], + drilldownBreadcrumbs: [ 'type', DrilldownBreadcrumbs ], + drilldownCentering: [ 'type', DrilldownCentering ], + drilldownOverlayBehavior: [ 'type', DrilldownOverlayBehavior ], + subprocessCompatibility: [ 'type', SubprocessCompatibility ] + }; + + var CLASS_PATTERN = /^class /; + + + /** + * @param {function} fn + * + * @return {boolean} + */ + function isClass(fn) { + return CLASS_PATTERN.test(fn.toString()); + } + + /** + * @param {any} obj + * + * @return {boolean} + */ + function isArray(obj) { + return Object.prototype.toString.call(obj) === '[object Array]'; + } + + /** + * @param {any} obj + * @param {string} prop + * + * @return {boolean} + */ + function hasOwnProp(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); + } + + /** + * @typedef {import('./index').InjectAnnotated } InjectAnnotated + */ + + /** + * @template T + * + * @params {[...string[], T] | ...string[], T} args + * + * @return {T & InjectAnnotated} + */ + function annotate() { + var args = Array.prototype.slice.call(arguments); + + if (args.length === 1 && isArray(args[0])) { + args = args[0]; + } + + var fn = args.pop(); + + fn.$inject = args; + + return fn; + } + + + // Current limitations: + // - can't put into "function arg" comments + // function /* (no parenthesis like this) */ (){} + // function abc( /* xx (no parenthesis like this) */ a, b) {} + // + // Just put the comment before function or inside: + // /* (((this is fine))) */ function(a, b) {} + // function abc(a) { /* (((this is fine))) */} + // + // - can't reliably auto-annotate constructor; we'll match the + // first constructor(...) pattern found which may be the one + // of a nested class, too. + + var CONSTRUCTOR_ARGS = /constructor\s*[^(]*\(\s*([^)]*)\)/m; + var FN_ARGS = /^(?:async\s+)?(?:function\s*[^(]*)?(?:\(\s*([^)]*)\)|(\w+))/m; + var FN_ARG = /\/\*([^*]*)\*\//m; + + /** + * @param {unknown} fn + * + * @return {string[]} + */ + function parseAnnotations(fn) { + + if (typeof fn !== 'function') { + throw new Error('Cannot annotate "' + fn + '". Expected a function!'); + } + + var match = fn.toString().match(isClass(fn) ? CONSTRUCTOR_ARGS : FN_ARGS); + + // may parse class without constructor + if (!match) { + return []; + } + + var args = match[1] || match[2]; + + return args && args.split(',').map(function(arg) { + var argMatch = arg.match(FN_ARG); + return (argMatch && argMatch[1] || arg).trim(); + }) || []; + } + + /** + * @typedef { import('./index').ModuleDeclaration } ModuleDeclaration + * @typedef { import('./index').ModuleDefinition } ModuleDefinition + * @typedef { import('./index').InjectorContext } InjectorContext + */ + + /** + * Create a new injector with the given modules. + * + * @param {ModuleDefinition[]} modules + * @param {InjectorContext} [parent] + */ + function Injector(modules, parent) { + parent = parent || { + get: function(name, strict) { + currentlyResolving.push(name); + + if (strict === false) { + return null; + } else { + throw error('No provider for "' + name + '"!'); + } + } + }; + + var currentlyResolving = []; + var providers = this._providers = Object.create(parent._providers || null); + var instances = this._instances = Object.create(null); + + var self = instances.injector = this; + + var error = function(msg) { + var stack = currentlyResolving.join(' -> '); + currentlyResolving.length = 0; + return new Error(stack ? msg + ' (Resolving: ' + stack + ')' : msg); + }; + + /** + * Return a named service. + * + * @param {string} name + * @param {boolean} [strict=true] if false, resolve missing services to null + * + * @return {any} + */ + function get(name, strict) { + if (!providers[name] && name.indexOf('.') !== -1) { + var parts = name.split('.'); + var pivot = get(parts.shift()); + + while (parts.length) { + pivot = pivot[parts.shift()]; + } + + return pivot; + } + + if (hasOwnProp(instances, name)) { + return instances[name]; + } + + if (hasOwnProp(providers, name)) { + if (currentlyResolving.indexOf(name) !== -1) { + currentlyResolving.push(name); + throw error('Cannot resolve circular dependency!'); + } + + currentlyResolving.push(name); + instances[name] = providers[name][0](providers[name][1]); + currentlyResolving.pop(); + + return instances[name]; + } + + return parent.get(name, strict); + } + + function fnDef(fn, locals) { + + if (typeof locals === 'undefined') { + locals = {}; + } + + if (typeof fn !== 'function') { + if (isArray(fn)) { + fn = annotate(fn.slice()); + } else { + throw new Error('Cannot invoke "' + fn + '". Expected a function!'); + } + } + + var inject = fn.$inject || parseAnnotations(fn); + var dependencies = inject.map(function(dep) { + if (hasOwnProp(locals, dep)) { + return locals[dep]; + } else { + return get(dep); + } + }); + + return { + fn: fn, + dependencies: dependencies + }; + } + + function instantiate(Type) { + var def = fnDef(Type); + + var fn = def.fn, + dependencies = def.dependencies; + + // instantiate var args constructor + var Constructor = Function.prototype.bind.apply(fn, [ null ].concat(dependencies)); + + return new Constructor(); + } + + function invoke(func, context, locals) { + var def = fnDef(func, locals); + + var fn = def.fn, + dependencies = def.dependencies; + + return fn.apply(context, dependencies); + } + + /** + * @param {Injector} childInjector + * + * @return {Function} + */ + function createPrivateInjectorFactory(childInjector) { + return annotate(function(key) { + return childInjector.get(key); + }); + } + + /** + * @param {ModuleDefinition[]} modules + * @param {string[]} [forceNewInstances] + * + * @return {Injector} + */ + function createChild(modules, forceNewInstances) { + if (forceNewInstances && forceNewInstances.length) { + var fromParentModule = Object.create(null); + var matchedScopes = Object.create(null); + + var privateInjectorsCache = []; + var privateChildInjectors = []; + var privateChildFactories = []; + + var provider; + var cacheIdx; + var privateChildInjector; + var privateChildInjectorFactory; + for (var name in providers) { + provider = providers[name]; + + if (forceNewInstances.indexOf(name) !== -1) { + if (provider[2] === 'private') { + cacheIdx = privateInjectorsCache.indexOf(provider[3]); + if (cacheIdx === -1) { + privateChildInjector = provider[3].createChild([], forceNewInstances); + privateChildInjectorFactory = createPrivateInjectorFactory(privateChildInjector); + privateInjectorsCache.push(provider[3]); + privateChildInjectors.push(privateChildInjector); + privateChildFactories.push(privateChildInjectorFactory); + fromParentModule[name] = [ privateChildInjectorFactory, name, 'private', privateChildInjector ]; + } else { + fromParentModule[name] = [ privateChildFactories[cacheIdx], name, 'private', privateChildInjectors[cacheIdx] ]; + } + } else { + fromParentModule[name] = [ provider[2], provider[1] ]; + } + matchedScopes[name] = true; + } + + if ((provider[2] === 'factory' || provider[2] === 'type') && provider[1].$scope) { + /* jshint -W083 */ + forceNewInstances.forEach(function(scope) { + if (provider[1].$scope.indexOf(scope) !== -1) { + fromParentModule[name] = [ provider[2], provider[1] ]; + matchedScopes[scope] = true; + } + }); + } + } + + forceNewInstances.forEach(function(scope) { + if (!matchedScopes[scope]) { + throw new Error('No provider for "' + scope + '". Cannot use provider from the parent!'); + } + }); + + modules.unshift(fromParentModule); + } + + return new Injector(modules, self); + } + + var factoryMap = { + factory: invoke, + type: instantiate, + value: function(value) { + return value; + } + }; + + /** + * @param {ModuleDefinition} moduleDefinition + * @param {Injector} injector + */ + function createInitializer(moduleDefinition, injector) { + + var initializers = moduleDefinition.__init__ || []; + + return function() { + initializers.forEach(function(initializer) { + + // eagerly resolve component (fn or string) + if (typeof initializer === 'string') { + injector.get(initializer); + } else { + injector.invoke(initializer); + } + }); + }; + } + + /** + * @param {ModuleDefinition} moduleDefinition + */ + function loadModule(moduleDefinition) { + + var moduleExports = moduleDefinition.__exports__; + + // private module + if (moduleExports) { + var nestedModules = moduleDefinition.__modules__; + + var clonedModule = Object.keys(moduleDefinition).reduce(function(clonedModule, key) { + + if (key !== '__exports__' && key !== '__modules__' && key !== '__init__' && key !== '__depends__') { + clonedModule[key] = moduleDefinition[key]; + } + + return clonedModule; + }, Object.create(null)); + + var childModules = (nestedModules || []).concat(clonedModule); + + var privateInjector = createChild(childModules); + var getFromPrivateInjector = annotate(function(key) { + return privateInjector.get(key); + }); + + moduleExports.forEach(function(key) { + providers[key] = [ getFromPrivateInjector, key, 'private', privateInjector ]; + }); + + // ensure child injector initializes + var initializers = (moduleDefinition.__init__ || []).slice(); + + initializers.unshift(function() { + privateInjector.init(); + }); + + moduleDefinition = Object.assign({}, moduleDefinition, { + __init__: initializers + }); + + return createInitializer(moduleDefinition, privateInjector); + } + + // normal module + Object.keys(moduleDefinition).forEach(function(key) { + + if (key === '__init__' || key === '__depends__') { + return; + } + + if (moduleDefinition[key][2] === 'private') { + providers[key] = moduleDefinition[key]; + return; + } + + var type = moduleDefinition[key][0]; + var value = moduleDefinition[key][1]; + + providers[key] = [ factoryMap[type], arrayUnwrap(type, value), type ]; + }); + + return createInitializer(moduleDefinition, self); + } + + /** + * @param {ModuleDefinition[]} moduleDefinitions + * @param {ModuleDefinition} moduleDefinition + * + * @return {ModuleDefinition[]} + */ + function resolveDependencies(moduleDefinitions, moduleDefinition) { + + if (moduleDefinitions.indexOf(moduleDefinition) !== -1) { + return moduleDefinitions; + } + + moduleDefinitions = (moduleDefinition.__depends__ || []).reduce(resolveDependencies, moduleDefinitions); + + if (moduleDefinitions.indexOf(moduleDefinition) !== -1) { + return moduleDefinitions; + } + + return moduleDefinitions.concat(moduleDefinition); + } + + /** + * @param {ModuleDefinition[]} moduleDefinitions + * + * @return { () => void } initializerFn + */ + function bootstrap(moduleDefinitions) { + + var initializers = moduleDefinitions + .reduce(resolveDependencies, []) + .map(loadModule); + + var initialized = false; + + return function() { + + if (initialized) { + return; + } + + initialized = true; + + initializers.forEach(function(initializer) { + return initializer(); + }); + }; + } + + // public API + this.get = get; + this.invoke = invoke; + this.instantiate = instantiate; + this.createChild = createChild; + + // setup + this.init = bootstrap(modules); + } + + + // helpers /////////////// + + function arrayUnwrap(type, value) { + if (type !== 'value' && isArray(value)) { + value = annotate(value.slice()); + } + + return value; + } + + // apply default renderer with lowest possible priority + // so that it only kicks in if noone else could render + var DEFAULT_RENDER_PRIORITY = 1; + + /** + * The default renderer used for shapes and connections. + * + * @param {EventBus} eventBus + * @param {Styles} styles + */ + function DefaultRenderer(eventBus, styles) { + + // + BaseRenderer.call(this, eventBus, DEFAULT_RENDER_PRIORITY); + + this.CONNECTION_STYLE = styles.style([ 'no-fill' ], { strokeWidth: 5, stroke: 'fuchsia' }); + this.SHAPE_STYLE = styles.style({ fill: 'white', stroke: 'fuchsia', strokeWidth: 2 }); + this.FRAME_STYLE = styles.style([ 'no-fill' ], { stroke: 'fuchsia', strokeDasharray: 4, strokeWidth: 2 }); + } + + e(DefaultRenderer, BaseRenderer); + + + DefaultRenderer.prototype.canRender = function() { + return true; + }; + + DefaultRenderer.prototype.drawShape = function drawShape(visuals, element, attrs) { + var rect = create$1('rect'); + + attr$1(rect, { + x: 0, + y: 0, + width: element.width || 0, + height: element.height || 0 + }); + + if (isFrameElement(element)) { + attr$1(rect, assign({}, this.FRAME_STYLE, attrs || {})); + } else { + attr$1(rect, assign({}, this.SHAPE_STYLE, attrs || {})); + } + + append(visuals, rect); + + return rect; + }; + + DefaultRenderer.prototype.drawConnection = function drawConnection(visuals, connection, attrs) { + + var line = createLine(connection.waypoints, assign({}, this.CONNECTION_STYLE, attrs || {})); + append(visuals, line); + + return line; + }; + + DefaultRenderer.prototype.getShapePath = function getShapePath(shape) { + + var x = shape.x, + y = shape.y, + width = shape.width, + height = shape.height; + + var shapePath = [ + [ 'M', x, y ], + [ 'l', width, 0 ], + [ 'l', 0, height ], + [ 'l', -width, 0 ], + [ 'z' ] + ]; + + return componentsToPath(shapePath); + }; + + DefaultRenderer.prototype.getConnectionPath = function getConnectionPath(connection) { + var waypoints = connection.waypoints; + + var idx, point, connectionPath = []; + + for (idx = 0; (point = waypoints[idx]); idx++) { + + // take invisible docking into account + // when creating the path + point = point.original || point; + + connectionPath.push([ idx === 0 ? 'M' : 'L', point.x, point.y ]); + } + + return componentsToPath(connectionPath); + }; + + + DefaultRenderer.$inject = [ 'eventBus', 'styles' ]; + + /** + * A component that manages shape styles + */ + function Styles() { + + var defaultTraits = { + + 'no-fill': { + fill: 'none' + }, + 'no-border': { + strokeOpacity: 0.0 + }, + 'no-events': { + pointerEvents: 'none' + } + }; + + var self = this; + + /** + * Builds a style definition from a className, a list of traits and an object of additional attributes. + * + * @param {string} className + * @param {Array} traits + * @param {Object} additionalAttrs + * + * @return {Object} the style defintion + */ + this.cls = function(className, traits, additionalAttrs) { + var attrs = this.style(traits, additionalAttrs); + + return assign(attrs, { 'class': className }); + }; + + /** + * Builds a style definition from a list of traits and an object of additional attributes. + * + * @param {Array} traits + * @param {Object} additionalAttrs + * + * @return {Object} the style defintion + */ + this.style = function(traits, additionalAttrs) { + + if (!isArray$2(traits) && !additionalAttrs) { + additionalAttrs = traits; + traits = []; + } + + var attrs = reduce(traits, function(attrs, t) { + return assign(attrs, defaultTraits[t] || {}); + }, {}); + + return additionalAttrs ? assign(attrs, additionalAttrs) : attrs; + }; + + this.computeStyle = function(custom, traits, defaultStyles) { + if (!isArray$2(traits)) { + defaultStyles = traits; + traits = []; + } + + return self.style(traits || [], assign({}, defaultStyles, custom || {})); + }; + } + + var DrawModule = { + __init__: [ 'defaultRenderer' ], + defaultRenderer: [ 'type', DefaultRenderer ], + styles: [ 'type', Styles ] + }; + + /** + * Failsafe remove an element from a collection + * + * @param {Array} [collection] + * @param {Object} [element] + * + * @return {number} the previous index of the element + */ + function remove(collection, element) { + + if (!collection || !element) { + return -1; + } + + var idx = collection.indexOf(element); + + if (idx !== -1) { + collection.splice(idx, 1); + } + + return idx; + } + + /** + * Fail save add an element to the given connection, ensuring + * it does not yet exist. + * + * @param {Array} collection + * @param {Object} element + * @param {number} idx + */ + function add(collection, element, idx) { + + if (!collection || !element) { + return; + } + + if (typeof idx !== 'number') { + idx = -1; + } + + var currentIdx = collection.indexOf(element); + + if (currentIdx !== -1) { + + if (currentIdx === idx) { + + // nothing to do, position has not changed + return; + } else { + + if (idx !== -1) { + + // remove from current position + collection.splice(currentIdx, 1); + } else { + + // already exists in collection + return; + } + } + } + + if (idx !== -1) { + + // insert at specified position + collection.splice(idx, 0, element); + } else { + + // push to end + collection.push(element); + } + } + + function round(number, resolution) { + return Math.round(number * resolution) / resolution; + } + + function ensurePx(number) { + return isNumber(number) ? number + 'px' : number; + } + + function findRoot(element) { + while (element.parent) { + element = element.parent; + } + + return element; + } + + /** + * Creates a HTML container element for a SVG element with + * the given configuration + * + * @param {Object} options + * @return {HTMLElement} the container element + */ + function createContainer(options) { + + options = assign({}, { width: '100%', height: '100%' }, options); + + var container = options.container || document.body; + + // create a
            around the svg element with the respective size + // this way we can always get the correct container size + // (this is impossible for elements at the moment) + var parent = document.createElement('div'); + parent.setAttribute('class', 'djs-container'); + + assign$1(parent, { + position: 'relative', + overflow: 'hidden', + width: ensurePx(options.width), + height: ensurePx(options.height) + }); + + container.appendChild(parent); + + return parent; + } + + function createGroup(parent, cls, childIndex) { + var group = create$1('g'); + classes$1(group).add(cls); + + var index = childIndex !== undefined ? childIndex : parent.childNodes.length - 1; + + // must ensure second argument is node or _null_ + // cf. https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore + parent.insertBefore(group, parent.childNodes[index] || null); + + return group; + } + + var BASE_LAYER = 'base'; + + // render plane contents behind utility layers + var PLANE_LAYER_INDEX = 0; + var UTILITY_LAYER_INDEX = 1; + + + var REQUIRED_MODEL_ATTRS = { + shape: [ 'x', 'y', 'width', 'height' ], + connection: [ 'waypoints' ] + }; + + /** + * The main drawing canvas. + * + * @class + * @constructor + * + * @emits Canvas#canvas.init + * + * @param {Object} config + * @param {EventBus} eventBus + * @param {GraphicsFactory} graphicsFactory + * @param {ElementRegistry} elementRegistry + */ + function Canvas(config, eventBus, graphicsFactory, elementRegistry) { + + this._eventBus = eventBus; + this._elementRegistry = elementRegistry; + this._graphicsFactory = graphicsFactory; + + this._rootsIdx = 0; + + this._layers = {}; + this._planes = []; + this._rootElement = null; + + this._init(config || {}); + } + + Canvas.$inject = [ + 'config.canvas', + 'eventBus', + 'graphicsFactory', + 'elementRegistry' + ]; + + /** + * Creates a element that is wrapped into a
            . + * This way we are always able to correctly figure out the size of the svg element + * by querying the parent node. + + * (It is not possible to get the size of a svg element cross browser @ 2014-04-01) + + *
            + * + * ... + * + *
            + */ + Canvas.prototype._init = function(config) { + + var eventBus = this._eventBus; + + // html container + var container = this._container = createContainer(config); + + var svg = this._svg = create$1('svg'); + attr$1(svg, { width: '100%', height: '100%' }); + + append(container, svg); + + var viewport = this._viewport = createGroup(svg, 'viewport'); + + // debounce canvas.viewbox.changed events + // for smoother diagram interaction + if (config.deferUpdate !== false) { + this._viewboxChanged = debounce(bind(this._viewboxChanged, this), 300); + } + + eventBus.on('diagram.init', function() { + + /** + * An event indicating that the canvas is ready to be drawn on. + * + * @memberOf Canvas + * + * @event canvas.init + * + * @type {Object} + * @property {SVGElement} svg the created svg element + * @property {SVGElement} viewport the direct parent of diagram elements and shapes + */ + eventBus.fire('canvas.init', { + svg: svg, + viewport: viewport + }); + + }, this); + + // reset viewbox on shape changes to + // recompute the viewbox + eventBus.on([ + 'shape.added', + 'connection.added', + 'shape.removed', + 'connection.removed', + 'elements.changed', + 'root.set' + ], function() { + delete this._cachedViewbox; + }, this); + + eventBus.on('diagram.destroy', 500, this._destroy, this); + eventBus.on('diagram.clear', 500, this._clear, this); + }; + + Canvas.prototype._destroy = function(emit) { + this._eventBus.fire('canvas.destroy', { + svg: this._svg, + viewport: this._viewport + }); + + var parent = this._container.parentNode; + + if (parent) { + parent.removeChild(this._container); + } + + delete this._svg; + delete this._container; + delete this._layers; + delete this._planes; + delete this._rootElement; + delete this._viewport; + }; + + Canvas.prototype._clear = function() { + + var self = this; + + var allElements = this._elementRegistry.getAll(); + + // remove all elements + allElements.forEach(function(element) { + var type = getType(element); + + if (type === 'root') { + self.removeRootElement(element); + } else { + self._removeElement(element, type); + } + }); + + // remove all planes + this._planes = []; + this._rootElement = null; + + // force recomputation of view box + delete this._cachedViewbox; + }; + + /** + * Returns the default layer on which + * all elements are drawn. + * + * @returns {SVGElement} + */ + Canvas.prototype.getDefaultLayer = function() { + return this.getLayer(BASE_LAYER, PLANE_LAYER_INDEX); + }; + + /** + * Returns a layer that is used to draw elements + * or annotations on it. + * + * Non-existing layers retrieved through this method + * will be created. During creation, the optional index + * may be used to create layers below or above existing layers. + * A layer with a certain index is always created above all + * existing layers with the same index. + * + * @param {string} name + * @param {number} index + * + * @returns {SVGElement} + */ + Canvas.prototype.getLayer = function(name, index) { + + if (!name) { + throw new Error('must specify a name'); + } + + var layer = this._layers[name]; + + if (!layer) { + layer = this._layers[name] = this._createLayer(name, index); + } + + // throw an error if layer creation / retrival is + // requested on different index + if (typeof index !== 'undefined' && layer.index !== index) { + throw new Error('layer <' + name + '> already created at index <' + index + '>'); + } + + return layer.group; + }; + + /** + * For a given index, return the number of layers that have a higher index and + * are visible. + * + * This is used to determine the node a layer should be inserted at. + * + * @param {Number} index + * @returns {Number} + */ + Canvas.prototype._getChildIndex = function(index) { + return reduce(this._layers, function(childIndex, layer) { + if (layer.visible && index >= layer.index) { + childIndex++; + } + + return childIndex; + }, 0); + }; + + /** + * Creates a given layer and returns it. + * + * @param {string} name + * @param {number} [index=0] + * + * @return {Object} layer descriptor with { index, group: SVGGroup } + */ + Canvas.prototype._createLayer = function(name, index) { + + if (typeof index === 'undefined') { + index = UTILITY_LAYER_INDEX; + } + + var childIndex = this._getChildIndex(index); + + return { + group: createGroup(this._viewport, 'layer-' + name, childIndex), + index: index, + visible: true + }; + }; + + + /** + * Shows a given layer. + * + * @param {String} layer + * @returns {SVGElement} + */ + Canvas.prototype.showLayer = function(name) { + + if (!name) { + throw new Error('must specify a name'); + } + + var layer = this._layers[name]; + + if (!layer) { + throw new Error('layer <' + name + '> does not exist'); + } + + var viewport = this._viewport; + var group = layer.group; + var index = layer.index; + + if (layer.visible) { + return group; + } + + var childIndex = this._getChildIndex(index); + + viewport.insertBefore(group, viewport.childNodes[childIndex] || null); + + layer.visible = true; + + return group; + }; + + /** + * Hides a given layer. + * + * @param {String} layer + * @returns {SVGElement} + */ + Canvas.prototype.hideLayer = function(name) { + + if (!name) { + throw new Error('must specify a name'); + } + + var layer = this._layers[name]; + + if (!layer) { + throw new Error('layer <' + name + '> does not exist'); + } + + var group = layer.group; + + if (!layer.visible) { + return group; + } + + remove$2(group); + + layer.visible = false; + + return group; + }; + + + Canvas.prototype._removeLayer = function(name) { + + var layer = this._layers[name]; + + if (layer) { + delete this._layers[name]; + + remove$2(layer.group); + } + }; + + /** + * Returns the currently active layer. Can be null. + * + * @returns {SVGElement|null} + */ + Canvas.prototype.getActiveLayer = function() { + var plane = this._findPlaneForRoot(this.getRootElement()); + + if (!plane) { + return null; + } + + return plane.layer; + }; + + + /** + * Returns the plane which contains the given element. + * + * @param {string|djs.model.Base} element + * + * @return {djs.model.Base} root for element + */ + Canvas.prototype.findRoot = function(element) { + if (typeof element === 'string') { + element = this._elementRegistry.get(element); + } + + if (!element) { + return; + } + + var plane = this._findPlaneForRoot( + findRoot(element) + ) || {}; + + return plane.rootElement; + }; + + /** + * Return a list of all root elements on the diagram. + * + * @return {djs.model.Root[]} + */ + Canvas.prototype.getRootElements = function() { + return this._planes.map(function(plane) { + return plane.rootElement; + }); + }; + + Canvas.prototype._findPlaneForRoot = function(rootElement) { + return find(this._planes, function(plane) { + return plane.rootElement === rootElement; + }); + }; + + + /** + * Returns the html element that encloses the + * drawing canvas. + * + * @return {DOMNode} + */ + Canvas.prototype.getContainer = function() { + return this._container; + }; + + + // markers ////////////////////// + + Canvas.prototype._updateMarker = function(element, marker, add) { + var container; + + if (!element.id) { + element = this._elementRegistry.get(element); + } + + // we need to access all + container = this._elementRegistry._elements[element.id]; + + if (!container) { + return; + } + + forEach$1([ container.gfx, container.secondaryGfx ], function(gfx) { + if (gfx) { + + // invoke either addClass or removeClass based on mode + if (add) { + classes$1(gfx).add(marker); + } else { + classes$1(gfx).remove(marker); + } + } + }); + + /** + * An event indicating that a marker has been updated for an element + * + * @event element.marker.update + * @type {Object} + * @property {djs.model.Element} element the shape + * @property {Object} gfx the graphical representation of the shape + * @property {string} marker + * @property {boolean} add true if the marker was added, false if it got removed + */ + this._eventBus.fire('element.marker.update', { element: element, gfx: container.gfx, marker: marker, add: !!add }); + }; + + + /** + * Adds a marker to an element (basically a css class). + * + * Fires the element.marker.update event, making it possible to + * integrate extension into the marker life-cycle, too. + * + * @example + * canvas.addMarker('foo', 'some-marker'); + * + * var fooGfx = canvas.getGraphics('foo'); + * + * fooGfx; // ... + * + * @param {string|djs.model.Base} element + * @param {string} marker + */ + Canvas.prototype.addMarker = function(element, marker) { + this._updateMarker(element, marker, true); + }; + + + /** + * Remove a marker from an element. + * + * Fires the element.marker.update event, making it possible to + * integrate extension into the marker life-cycle, too. + * + * @param {string|djs.model.Base} element + * @param {string} marker + */ + Canvas.prototype.removeMarker = function(element, marker) { + this._updateMarker(element, marker, false); + }; + + /** + * Check the existence of a marker on element. + * + * @param {string|djs.model.Base} element + * @param {string} marker + */ + Canvas.prototype.hasMarker = function(element, marker) { + if (!element.id) { + element = this._elementRegistry.get(element); + } + + var gfx = this.getGraphics(element); + + return classes$1(gfx).has(marker); + }; + + /** + * Toggles a marker on an element. + * + * Fires the element.marker.update event, making it possible to + * integrate extension into the marker life-cycle, too. + * + * @param {string|djs.model.Base} element + * @param {string} marker + */ + Canvas.prototype.toggleMarker = function(element, marker) { + if (this.hasMarker(element, marker)) { + this.removeMarker(element, marker); + } else { + this.addMarker(element, marker); + } + }; + + /** + * Returns the current root element. + * + * Supports two different modes for handling root elements: + * + * 1. if no root element has been added before, an implicit root will be added + * and returned. This is used in applications that don't require explicit + * root elements. + * + * 2. when root elements have been added before calling `getRootElement`, + * root elements can be null. This is used for applications that want to manage + * root elements themselves. + * + * @returns {Object|djs.model.Root|null} rootElement. + */ + Canvas.prototype.getRootElement = function() { + var rootElement = this._rootElement; + + // can return null if root elements are present but none was set yet + if (rootElement || this._planes.length) { + return rootElement; + } + + return this.setRootElement(this.addRootElement(null)); + }; + + /** + * Adds a given root element and returns it. + * + * @param {Object|djs.model.Root} rootElement + * + * @return {Object|djs.model.Root} rootElement + */ + + Canvas.prototype.addRootElement = function(rootElement) { + var idx = this._rootsIdx++; + + if (!rootElement) { + rootElement = { + id: '__implicitroot_' + idx, + children: [], + isImplicit: true + }; + } + + var layerName = rootElement.layer = 'root-' + idx; + + this._ensureValid('root', rootElement); + + var layer = this.getLayer(layerName, PLANE_LAYER_INDEX); + + this.hideLayer(layerName); + + this._addRoot(rootElement, layer); + + this._planes.push({ + rootElement: rootElement, + layer: layer + }); + + return rootElement; + }; + + /** + * Removes a given rootElement and returns it. + * + * @param {djs.model.Root|String} rootElement + * + * @return {Object|djs.model.Root} rootElement + */ + Canvas.prototype.removeRootElement = function(rootElement) { + + if (typeof rootElement === 'string') { + rootElement = this._elementRegistry.get(rootElement); + } + + var plane = this._findPlaneForRoot(rootElement); + + if (!plane) { + return; + } + + // hook up life-cycle events + this._removeRoot(rootElement); + + // clean up layer + this._removeLayer(rootElement.layer); + + // clean up plane + this._planes = this._planes.filter(function(plane) { + return plane.rootElement !== rootElement; + }); + + // clean up active root + if (this._rootElement === rootElement) { + this._rootElement = null; + } + + return rootElement; + }; + + + // root element handling ////////////////////// + + /** + * Sets a given element as the new root element for the canvas + * and returns the new root element. + * + * @param {Object|djs.model.Root} rootElement + * + * @return {Object|djs.model.Root} new root element + */ + Canvas.prototype.setRootElement = function(rootElement, override) { + + if (isDefined(override)) { + throw new Error('override not supported'); + } + + if (rootElement === this._rootElement) { + return; + } + + var plane; + + if (!rootElement) { + throw new Error('rootElement required'); + } + + plane = this._findPlaneForRoot(rootElement); + + // give set add semantics for backwards compatibility + if (!plane) { + rootElement = this.addRootElement(rootElement); + } + + this._setRoot(rootElement); + + return rootElement; + }; + + + Canvas.prototype._removeRoot = function(element) { + var elementRegistry = this._elementRegistry, + eventBus = this._eventBus; + + // simulate element remove event sequence + eventBus.fire('root.remove', { element: element }); + eventBus.fire('root.removed', { element: element }); + + elementRegistry.remove(element); + }; + + + Canvas.prototype._addRoot = function(element, gfx) { + var elementRegistry = this._elementRegistry, + eventBus = this._eventBus; + + // resemble element add event sequence + eventBus.fire('root.add', { element: element }); + + elementRegistry.add(element, gfx); + + eventBus.fire('root.added', { element: element, gfx: gfx }); + }; + + + Canvas.prototype._setRoot = function(rootElement, layer) { + + var currentRoot = this._rootElement; + + if (currentRoot) { + + // un-associate previous root element + this._elementRegistry.updateGraphics(currentRoot, null, true); + + // hide previous layer + this.hideLayer(currentRoot.layer); + } + + if (rootElement) { + + if (!layer) { + layer = this._findPlaneForRoot(rootElement).layer; + } + + // associate element with + this._elementRegistry.updateGraphics(rootElement, this._svg, true); + + // show root layer + this.showLayer(rootElement.layer); + } + + this._rootElement = rootElement; + + this._eventBus.fire('root.set', { element: rootElement }); + }; + + // add functionality ////////////////////// + + Canvas.prototype._ensureValid = function(type, element) { + if (!element.id) { + throw new Error('element must have an id'); + } + + if (this._elementRegistry.get(element.id)) { + throw new Error('element <' + element.id + '> already exists'); + } + + var requiredAttrs = REQUIRED_MODEL_ATTRS[type]; + + var valid = every(requiredAttrs, function(attr) { + return typeof element[attr] !== 'undefined'; + }); + + if (!valid) { + throw new Error( + 'must supply { ' + requiredAttrs.join(', ') + ' } with ' + type); + } + }; + + Canvas.prototype._setParent = function(element, parent, parentIndex) { + add(parent.children, element, parentIndex); + element.parent = parent; + }; + + /** + * Adds an element to the canvas. + * + * This wires the parent <-> child relationship between the element and + * a explicitly specified parent or an implicit root element. + * + * During add it emits the events + * + * * <{type}.add> (element, parent) + * * <{type}.added> (element, gfx) + * + * Extensions may hook into these events to perform their magic. + * + * @param {string} type + * @param {Object|djs.model.Base} element + * @param {Object|djs.model.Base} [parent] + * @param {number} [parentIndex] + * + * @return {Object|djs.model.Base} the added element + */ + Canvas.prototype._addElement = function(type, element, parent, parentIndex) { + + parent = parent || this.getRootElement(); + + var eventBus = this._eventBus, + graphicsFactory = this._graphicsFactory; + + this._ensureValid(type, element); + + eventBus.fire(type + '.add', { element: element, parent: parent }); + + this._setParent(element, parent, parentIndex); + + // create graphics + var gfx = graphicsFactory.create(type, element, parentIndex); + + this._elementRegistry.add(element, gfx); + + // update its visual + graphicsFactory.update(type, element, gfx); + + eventBus.fire(type + '.added', { element: element, gfx: gfx }); + + return element; + }; + + /** + * Adds a shape to the canvas + * + * @param {Object|djs.model.Shape} shape to add to the diagram + * @param {djs.model.Base} [parent] + * @param {number} [parentIndex] + * + * @return {djs.model.Shape} the added shape + */ + Canvas.prototype.addShape = function(shape, parent, parentIndex) { + return this._addElement('shape', shape, parent, parentIndex); + }; + + /** + * Adds a connection to the canvas + * + * @param {Object|djs.model.Connection} connection to add to the diagram + * @param {djs.model.Base} [parent] + * @param {number} [parentIndex] + * + * @return {djs.model.Connection} the added connection + */ + Canvas.prototype.addConnection = function(connection, parent, parentIndex) { + return this._addElement('connection', connection, parent, parentIndex); + }; + + + /** + * Internal remove element + */ + Canvas.prototype._removeElement = function(element, type) { + + var elementRegistry = this._elementRegistry, + graphicsFactory = this._graphicsFactory, + eventBus = this._eventBus; + + element = elementRegistry.get(element.id || element); + + if (!element) { + + // element was removed already + return; + } + + eventBus.fire(type + '.remove', { element: element }); + + graphicsFactory.remove(element); + + // unset parent <-> child relationship + remove(element.parent && element.parent.children, element); + element.parent = null; + + eventBus.fire(type + '.removed', { element: element }); + + elementRegistry.remove(element); + + return element; + }; + + + /** + * Removes a shape from the canvas + * + * @param {string|djs.model.Shape} shape or shape id to be removed + * + * @return {djs.model.Shape} the removed shape + */ + Canvas.prototype.removeShape = function(shape) { + + /** + * An event indicating that a shape is about to be removed from the canvas. + * + * @memberOf Canvas + * + * @event shape.remove + * @type {Object} + * @property {djs.model.Shape} element the shape descriptor + * @property {Object} gfx the graphical representation of the shape + */ + + /** + * An event indicating that a shape has been removed from the canvas. + * + * @memberOf Canvas + * + * @event shape.removed + * @type {Object} + * @property {djs.model.Shape} element the shape descriptor + * @property {Object} gfx the graphical representation of the shape + */ + return this._removeElement(shape, 'shape'); + }; + + + /** + * Removes a connection from the canvas + * + * @param {string|djs.model.Connection} connection or connection id to be removed + * + * @return {djs.model.Connection} the removed connection + */ + Canvas.prototype.removeConnection = function(connection) { + + /** + * An event indicating that a connection is about to be removed from the canvas. + * + * @memberOf Canvas + * + * @event connection.remove + * @type {Object} + * @property {djs.model.Connection} element the connection descriptor + * @property {Object} gfx the graphical representation of the connection + */ + + /** + * An event indicating that a connection has been removed from the canvas. + * + * @memberOf Canvas + * + * @event connection.removed + * @type {Object} + * @property {djs.model.Connection} element the connection descriptor + * @property {Object} gfx the graphical representation of the connection + */ + return this._removeElement(connection, 'connection'); + }; + + + /** + * Return the graphical object underlaying a certain diagram element + * + * @param {string|djs.model.Base} element descriptor of the element + * @param {boolean} [secondary=false] whether to return the secondary connected element + * + * @return {SVGElement} + */ + Canvas.prototype.getGraphics = function(element, secondary) { + return this._elementRegistry.getGraphics(element, secondary); + }; + + + /** + * Perform a viewbox update via a given change function. + * + * @param {Function} changeFn + */ + Canvas.prototype._changeViewbox = function(changeFn) { + + // notify others of the upcoming viewbox change + this._eventBus.fire('canvas.viewbox.changing'); + + // perform actual change + changeFn.apply(this); + + // reset the cached viewbox so that + // a new get operation on viewbox or zoom + // triggers a viewbox re-computation + this._cachedViewbox = null; + + // notify others of the change; this step + // may or may not be debounced + this._viewboxChanged(); + }; + + Canvas.prototype._viewboxChanged = function() { + this._eventBus.fire('canvas.viewbox.changed', { viewbox: this.viewbox() }); + }; + + + /** + * Gets or sets the view box of the canvas, i.e. the + * area that is currently displayed. + * + * The getter may return a cached viewbox (if it is currently + * changing). To force a recomputation, pass `false` as the first argument. + * + * @example + * + * canvas.viewbox({ x: 100, y: 100, width: 500, height: 500 }) + * + * // sets the visible area of the diagram to (100|100) -> (600|100) + * // and and scales it according to the diagram width + * + * var viewbox = canvas.viewbox(); // pass `false` to force recomputing the box. + * + * console.log(viewbox); + * // { + * // inner: Dimensions, + * // outer: Dimensions, + * // scale, + * // x, y, + * // width, height + * // } + * + * // if the current diagram is zoomed and scrolled, you may reset it to the + * // default zoom via this method, too: + * + * var zoomedAndScrolledViewbox = canvas.viewbox(); + * + * canvas.viewbox({ + * x: 0, + * y: 0, + * width: zoomedAndScrolledViewbox.outer.width, + * height: zoomedAndScrolledViewbox.outer.height + * }); + * + * @param {Object} [box] the new view box to set + * @param {number} box.x the top left X coordinate of the canvas visible in view box + * @param {number} box.y the top left Y coordinate of the canvas visible in view box + * @param {number} box.width the visible width + * @param {number} box.height + * + * @return {Object} the current view box + */ + Canvas.prototype.viewbox = function(box) { + + if (box === undefined && this._cachedViewbox) { + return this._cachedViewbox; + } + + var viewport = this._viewport, + innerBox, + outerBox = this.getSize(), + matrix, + activeLayer, + transform, + scale, + x, y; + + if (!box) { + + // compute the inner box based on the + // diagrams active layer. This allows us to exclude + // external components, such as overlays + + activeLayer = this._rootElement ? this.getActiveLayer() : null; + innerBox = activeLayer && activeLayer.getBBox() || {}; + + transform = transform$1(viewport); + matrix = transform ? transform.matrix : createMatrix(); + scale = round(matrix.a, 1000); + + x = round(-matrix.e || 0, 1000); + y = round(-matrix.f || 0, 1000); + + box = this._cachedViewbox = { + x: x ? x / scale : 0, + y: y ? y / scale : 0, + width: outerBox.width / scale, + height: outerBox.height / scale, + scale: scale, + inner: { + width: innerBox.width || 0, + height: innerBox.height || 0, + x: innerBox.x || 0, + y: innerBox.y || 0 + }, + outer: outerBox + }; + + return box; + } else { + + this._changeViewbox(function() { + scale = Math.min(outerBox.width / box.width, outerBox.height / box.height); + + var matrix = this._svg.createSVGMatrix() + .scale(scale) + .translate(-box.x, -box.y); + + transform$1(viewport, matrix); + }); + } + + return box; + }; + + + /** + * Gets or sets the scroll of the canvas. + * + * @param {Object} [delta] the new scroll to apply. + * + * @param {number} [delta.dx] + * @param {number} [delta.dy] + */ + Canvas.prototype.scroll = function(delta) { + + var node = this._viewport; + var matrix = node.getCTM(); + + if (delta) { + this._changeViewbox(function() { + delta = assign({ dx: 0, dy: 0 }, delta || {}); + + matrix = this._svg.createSVGMatrix().translate(delta.dx, delta.dy).multiply(matrix); + + setCTM(node, matrix); + }); + } + + return { x: matrix.e, y: matrix.f }; + }; + + /** + * Scrolls the viewbox to contain the given element. + * Optionally specify a padding to be applied to the edges. + * + * @param {Object|String} [element] the element to scroll to. + * @param {Object|Number} [padding=100] the padding to be applied. Can also specify top, bottom, left and right. + * + */ + Canvas.prototype.scrollToElement = function(element, padding) { + var defaultPadding = 100; + + if (typeof element === 'string') { + element = this._elementRegistry.get(element); + } + + // set to correct rootElement + var rootElement = this.findRoot(element); + + if (rootElement !== this.getRootElement()) { + this.setRootElement(rootElement); + } + + if (!padding) { + padding = {}; + } + if (typeof padding === 'number') { + defaultPadding = padding; + } + + padding = { + top: padding.top || defaultPadding, + right: padding.right || defaultPadding, + bottom: padding.bottom || defaultPadding, + left: padding.left || defaultPadding + }; + + var elementBounds = getBBox(element), + elementTrbl = asTRBL(elementBounds), + viewboxBounds = this.viewbox(), + zoom = this.zoom(), + dx, dy; + + // shrink viewboxBounds with padding + viewboxBounds.y += padding.top / zoom; + viewboxBounds.x += padding.left / zoom; + viewboxBounds.width -= (padding.right + padding.left) / zoom; + viewboxBounds.height -= (padding.bottom + padding.top) / zoom; + + var viewboxTrbl = asTRBL(viewboxBounds); + + var canFit = elementBounds.width < viewboxBounds.width && elementBounds.height < viewboxBounds.height; + + if (!canFit) { + + // top-left when element can't fit + dx = elementBounds.x - viewboxBounds.x; + dy = elementBounds.y - viewboxBounds.y; + + } else { + + var dRight = Math.max(0, elementTrbl.right - viewboxTrbl.right), + dLeft = Math.min(0, elementTrbl.left - viewboxTrbl.left), + dBottom = Math.max(0, elementTrbl.bottom - viewboxTrbl.bottom), + dTop = Math.min(0, elementTrbl.top - viewboxTrbl.top); + + dx = dRight || dLeft; + dy = dBottom || dTop; + + } + + this.scroll({ dx: -dx * zoom, dy: -dy * zoom }); + }; + + /** + * Gets or sets the current zoom of the canvas, optionally zooming + * to the specified position. + * + * The getter may return a cached zoom level. Call it with `false` as + * the first argument to force recomputation of the current level. + * + * @param {string|number} [newScale] the new zoom level, either a number, i.e. 0.9, + * or `fit-viewport` to adjust the size to fit the current viewport + * @param {string|Point} [center] the reference point { x: .., y: ..} to zoom to, 'auto' to zoom into mid or null + * + * @return {number} the current scale + */ + Canvas.prototype.zoom = function(newScale, center) { + + if (!newScale) { + return this.viewbox(newScale).scale; + } + + if (newScale === 'fit-viewport') { + return this._fitViewport(center); + } + + var outer, + matrix; + + this._changeViewbox(function() { + + if (typeof center !== 'object') { + outer = this.viewbox().outer; + + center = { + x: outer.width / 2, + y: outer.height / 2 + }; + } + + matrix = this._setZoom(newScale, center); + }); + + return round(matrix.a, 1000); + }; + + function setCTM(node, m) { + var mstr = 'matrix(' + m.a + ',' + m.b + ',' + m.c + ',' + m.d + ',' + m.e + ',' + m.f + ')'; + node.setAttribute('transform', mstr); + } + + Canvas.prototype._fitViewport = function(center) { + + var vbox = this.viewbox(), + outer = vbox.outer, + inner = vbox.inner, + newScale, + newViewbox; + + // display the complete diagram without zooming in. + // instead of relying on internal zoom, we perform a + // hard reset on the canvas viewbox to realize this + // + // if diagram does not need to be zoomed in, we focus it around + // the diagram origin instead + + if (inner.x >= 0 && + inner.y >= 0 && + inner.x + inner.width <= outer.width && + inner.y + inner.height <= outer.height && + !center) { + + newViewbox = { + x: 0, + y: 0, + width: Math.max(inner.width + inner.x, outer.width), + height: Math.max(inner.height + inner.y, outer.height) + }; + } else { + + newScale = Math.min(1, outer.width / inner.width, outer.height / inner.height); + newViewbox = { + x: inner.x + (center ? inner.width / 2 - outer.width / newScale / 2 : 0), + y: inner.y + (center ? inner.height / 2 - outer.height / newScale / 2 : 0), + width: outer.width / newScale, + height: outer.height / newScale + }; + } + + this.viewbox(newViewbox); + + return this.viewbox(false).scale; + }; + + + Canvas.prototype._setZoom = function(scale, center) { + + var svg = this._svg, + viewport = this._viewport; + + var matrix = svg.createSVGMatrix(); + var point = svg.createSVGPoint(); + + var centerPoint, + originalPoint, + currentMatrix, + scaleMatrix, + newMatrix; + + currentMatrix = viewport.getCTM(); + + var currentScale = currentMatrix.a; + + if (center) { + centerPoint = assign(point, center); + + // revert applied viewport transformations + originalPoint = centerPoint.matrixTransform(currentMatrix.inverse()); + + // create scale matrix + scaleMatrix = matrix + .translate(originalPoint.x, originalPoint.y) + .scale(1 / currentScale * scale) + .translate(-originalPoint.x, -originalPoint.y); + + newMatrix = currentMatrix.multiply(scaleMatrix); + } else { + newMatrix = matrix.scale(scale); + } + + setCTM(this._viewport, newMatrix); + + return newMatrix; + }; + + + /** + * Returns the size of the canvas + * + * @return {Dimensions} + */ + Canvas.prototype.getSize = function() { + return { + width: this._container.clientWidth, + height: this._container.clientHeight + }; + }; + + + /** + * Return the absolute bounding box for the given element + * + * The absolute bounding box may be used to display overlays in the + * callers (browser) coordinate system rather than the zoomed in/out + * canvas coordinates. + * + * @param {ElementDescriptor} element + * @return {Bounds} the absolute bounding box + */ + Canvas.prototype.getAbsoluteBBox = function(element) { + var vbox = this.viewbox(); + var bbox; + + // connection + // use svg bbox + if (element.waypoints) { + var gfx = this.getGraphics(element); + + bbox = gfx.getBBox(); + } + + // shapes + // use data + else { + bbox = element; + } + + var x = bbox.x * vbox.scale - vbox.x * vbox.scale; + var y = bbox.y * vbox.scale - vbox.y * vbox.scale; + + var width = bbox.width * vbox.scale; + var height = bbox.height * vbox.scale; + + return { + x: x, + y: y, + width: width, + height: height + }; + }; + + /** + * Fires an event in order other modules can react to the + * canvas resizing + */ + Canvas.prototype.resized = function() { + + // force recomputation of view box + delete this._cachedViewbox; + + this._eventBus.fire('canvas.resized'); + }; + + var ELEMENT_ID = 'data-element-id'; + + + /** + * @class + * + * A registry that keeps track of all shapes in the diagram. + */ + function ElementRegistry(eventBus) { + this._elements = {}; + + this._eventBus = eventBus; + } + + ElementRegistry.$inject = [ 'eventBus' ]; + + /** + * Register a pair of (element, gfx, (secondaryGfx)). + * + * @param {djs.model.Base} element + * @param {SVGElement} gfx + * @param {SVGElement} [secondaryGfx] optional other element to register, too + */ + ElementRegistry.prototype.add = function(element, gfx, secondaryGfx) { + + var id = element.id; + + this._validateId(id); + + // associate dom node with element + attr$1(gfx, ELEMENT_ID, id); + + if (secondaryGfx) { + attr$1(secondaryGfx, ELEMENT_ID, id); + } + + this._elements[id] = { element: element, gfx: gfx, secondaryGfx: secondaryGfx }; + }; + + /** + * Removes an element from the registry. + * + * @param {djs.model.Base} element + */ + ElementRegistry.prototype.remove = function(element) { + var elements = this._elements, + id = element.id || element, + container = id && elements[id]; + + if (container) { + + // unset element id on gfx + attr$1(container.gfx, ELEMENT_ID, ''); + + if (container.secondaryGfx) { + attr$1(container.secondaryGfx, ELEMENT_ID, ''); + } + + delete elements[id]; + } + }; + + /** + * Update the id of an element + * + * @param {djs.model.Base} element + * @param {string} newId + */ + ElementRegistry.prototype.updateId = function(element, newId) { + + this._validateId(newId); + + if (typeof element === 'string') { + element = this.get(element); + } + + this._eventBus.fire('element.updateId', { + element: element, + newId: newId + }); + + var gfx = this.getGraphics(element), + secondaryGfx = this.getGraphics(element, true); + + this.remove(element); + + element.id = newId; + + this.add(element, gfx, secondaryGfx); + }; + + /** + * Update the graphics of an element + * + * @param {djs.model.Base} element + * @param {SVGElement} gfx + * @param {boolean} [secondary=false] whether to update the secondary connected element + */ + ElementRegistry.prototype.updateGraphics = function(filter, gfx, secondary) { + var id = filter.id || filter; + + var container = this._elements[id]; + + if (secondary) { + container.secondaryGfx = gfx; + } else { + container.gfx = gfx; + } + + if (gfx) { + attr$1(gfx, ELEMENT_ID, id); + } + + return gfx; + }; + + /** + * Return the model element for a given id or graphics. + * + * @example + * + * elementRegistry.get('SomeElementId_1'); + * elementRegistry.get(gfx); + * + * + * @param {string|SVGElement} filter for selecting the element + * + * @return {djs.model.Base} + */ + ElementRegistry.prototype.get = function(filter) { + var id; + + if (typeof filter === 'string') { + id = filter; + } else { + id = filter && attr$1(filter, ELEMENT_ID); + } + + var container = this._elements[id]; + return container && container.element; + }; + + /** + * Return all elements that match a given filter function. + * + * @param {Function} fn + * + * @return {Array} + */ + ElementRegistry.prototype.filter = function(fn) { + + var filtered = []; + + this.forEach(function(element, gfx) { + if (fn(element, gfx)) { + filtered.push(element); + } + }); + + return filtered; + }; + + /** + * Return the first element that satisfies the provided testing function. + * + * @param {Function} fn + * + * @return {djs.model.Base} + */ + ElementRegistry.prototype.find = function(fn) { + var map = this._elements, + keys = Object.keys(map); + + for (var i = 0; i < keys.length; i++) { + var id = keys[i], + container = map[id], + element = container.element, + gfx = container.gfx; + + if (fn(element, gfx)) { + return element; + } + } + }; + + /** + * Return all rendered model elements. + * + * @return {Array} + */ + ElementRegistry.prototype.getAll = function() { + return this.filter(function(e) { return e; }); + }; + + /** + * Iterate over all diagram elements. + * + * @param {Function} fn + */ + ElementRegistry.prototype.forEach = function(fn) { + + var map = this._elements; + + Object.keys(map).forEach(function(id) { + var container = map[id], + element = container.element, + gfx = container.gfx; + + return fn(element, gfx); + }); + }; + + /** + * Return the graphical representation of an element or its id. + * + * @example + * elementRegistry.getGraphics('SomeElementId_1'); + * elementRegistry.getGraphics(rootElement); // + * + * elementRegistry.getGraphics(rootElement, true); // + * + * + * @param {string|djs.model.Base} filter + * @param {boolean} [secondary=false] whether to return the secondary connected element + * + * @return {SVGElement} + */ + ElementRegistry.prototype.getGraphics = function(filter, secondary) { + var id = filter.id || filter; + + var container = this._elements[id]; + return container && (secondary ? container.secondaryGfx : container.gfx); + }; + + /** + * Validate the suitability of the given id and signals a problem + * with an exception. + * + * @param {string} id + * + * @throws {Error} if id is empty or already assigned + */ + ElementRegistry.prototype._validateId = function(id) { + if (!id) { + throw new Error('element must have an id'); + } + + if (this._elements[id]) { + throw new Error('element with id ' + id + ' already added'); + } + }; + + var objectRefs = {exports: {}}; + + var collection = {}; + + /** + * An empty collection stub. Use {@link RefsCollection.extend} to extend a + * collection with ref semantics. + * + * @class RefsCollection + */ + + /** + * Extends a collection with {@link Refs} aware methods + * + * @memberof RefsCollection + * @static + * + * @param {Array} collection + * @param {Refs} refs instance + * @param {Object} property represented by the collection + * @param {Object} target object the collection is attached to + * + * @return {RefsCollection} the extended array + */ + function extend(collection, refs, property, target) { + + var inverseProperty = property.inverse; + + /** + * Removes the given element from the array and returns it. + * + * @method RefsCollection#remove + * + * @param {Object} element the element to remove + */ + Object.defineProperty(collection, 'remove', { + value: function(element) { + var idx = this.indexOf(element); + if (idx !== -1) { + this.splice(idx, 1); + + // unset inverse + refs.unset(element, inverseProperty, target); + } + + return element; + } + }); + + /** + * Returns true if the collection contains the given element + * + * @method RefsCollection#contains + * + * @param {Object} element the element to check for + */ + Object.defineProperty(collection, 'contains', { + value: function(element) { + return this.indexOf(element) !== -1; + } + }); + + /** + * Adds an element to the array, unless it exists already (set semantics). + * + * @method RefsCollection#add + * + * @param {Object} element the element to add + * @param {Number} optional index to add element to + * (possibly moving other elements around) + */ + Object.defineProperty(collection, 'add', { + value: function(element, idx) { + + var currentIdx = this.indexOf(element); + + if (typeof idx === 'undefined') { + + if (currentIdx !== -1) { + // element already in collection (!) + return; + } + + // add to end of array, as no idx is specified + idx = this.length; + } + + // handle already in collection + if (currentIdx !== -1) { + + // remove element from currentIdx + this.splice(currentIdx, 1); + } + + // add element at idx + this.splice(idx, 0, element); + + if (currentIdx === -1) { + // set inverse, unless element was + // in collection already + refs.set(element, inverseProperty, target); + } + } + }); + + // a simple marker, identifying this element + // as being a refs collection + Object.defineProperty(collection, '__refs_collection', { + value: true + }); + + return collection; + } + + + function isExtended(collection) { + return collection.__refs_collection === true; + } + + collection.extend = extend; + + collection.isExtended = isExtended; + + var Collection = collection; + + function hasOwnProperty$1(e, property) { + return Object.prototype.hasOwnProperty.call(e, property.name || property); + } + + function defineCollectionProperty(ref, property, target) { + + var collection = Collection.extend(target[property.name] || [], ref, property, target); + + Object.defineProperty(target, property.name, { + enumerable: property.enumerable, + value: collection + }); + + if (collection.length) { + + collection.forEach(function(o) { + ref.set(o, property.inverse, target); + }); + } + } + + + function defineProperty$1(ref, property, target) { + + var inverseProperty = property.inverse; + + var _value = target[property.name]; + + Object.defineProperty(target, property.name, { + configurable: property.configurable, + enumerable: property.enumerable, + + get: function() { + return _value; + }, + + set: function(value) { + + // return if we already performed all changes + if (value === _value) { + return; + } + + var old = _value; + + // temporary set null + _value = null; + + if (old) { + ref.unset(old, inverseProperty, target); + } + + // set new value + _value = value; + + // set inverse value + ref.set(_value, inverseProperty, target); + } + }); + + } + + /** + * Creates a new references object defining two inversly related + * attribute descriptors a and b. + * + *

            + * When bound to an object using {@link Refs#bind} the references + * get activated and ensure that add and remove operations are applied + * reversely, too. + *

            + * + *

            + * For attributes represented as collections {@link Refs} provides the + * {@link RefsCollection#add}, {@link RefsCollection#remove} and {@link RefsCollection#contains} extensions + * that must be used to properly hook into the inverse change mechanism. + *

            + * + * @class Refs + * + * @classdesc A bi-directional reference between two attributes. + * + * @param {Refs.AttributeDescriptor} a property descriptor + * @param {Refs.AttributeDescriptor} b property descriptor + * + * @example + * + * var refs = Refs({ name: 'wheels', collection: true, enumerable: true }, { name: 'car' }); + * + * var car = { name: 'toyota' }; + * var wheels = [{ pos: 'front-left' }, { pos: 'front-right' }]; + * + * refs.bind(car, 'wheels'); + * + * car.wheels // [] + * car.wheels.add(wheels[0]); + * car.wheels.add(wheels[1]); + * + * car.wheels // [{ pos: 'front-left' }, { pos: 'front-right' }] + * + * wheels[0].car // { name: 'toyota' }; + * car.wheels.remove(wheels[0]); + * + * wheels[0].car // undefined + */ + function Refs$1(a, b) { + + if (!(this instanceof Refs$1)) { + return new Refs$1(a, b); + } + + // link + a.inverse = b; + b.inverse = a; + + this.props = {}; + this.props[a.name] = a; + this.props[b.name] = b; + } + + /** + * Binds one side of a bi-directional reference to a + * target object. + * + * @memberOf Refs + * + * @param {Object} target + * @param {String} property + */ + Refs$1.prototype.bind = function(target, property) { + if (typeof property === 'string') { + if (!this.props[property]) { + throw new Error('no property <' + property + '> in ref'); + } + property = this.props[property]; + } + + if (property.collection) { + defineCollectionProperty(this, property, target); + } else { + defineProperty$1(this, property, target); + } + }; + + Refs$1.prototype.ensureRefsCollection = function(target, property) { + + var collection = target[property.name]; + + if (!Collection.isExtended(collection)) { + defineCollectionProperty(this, property, target); + } + + return collection; + }; + + Refs$1.prototype.ensureBound = function(target, property) { + if (!hasOwnProperty$1(target, property)) { + this.bind(target, property); + } + }; + + Refs$1.prototype.unset = function(target, property, value) { + + if (target) { + this.ensureBound(target, property); + + if (property.collection) { + this.ensureRefsCollection(target, property).remove(value); + } else { + target[property.name] = undefined; + } + } + }; + + Refs$1.prototype.set = function(target, property, value) { + + if (target) { + this.ensureBound(target, property); + + if (property.collection) { + this.ensureRefsCollection(target, property).add(value); + } else { + target[property.name] = value; + } + } + }; + + var refs = Refs$1; + + (function (module) { + module.exports = refs; + + module.exports.Collection = collection; + } (objectRefs)); + + var Refs = /*@__PURE__*/getDefaultExportFromCjs(objectRefs.exports); + + var parentRefs = new Refs({ name: 'children', enumerable: true, collection: true }, { name: 'parent' }), + labelRefs = new Refs({ name: 'labels', enumerable: true, collection: true }, { name: 'labelTarget' }), + attacherRefs = new Refs({ name: 'attachers', collection: true }, { name: 'host' }), + outgoingRefs = new Refs({ name: 'outgoing', collection: true }, { name: 'source' }), + incomingRefs = new Refs({ name: 'incoming', collection: true }, { name: 'target' }); + + /** + * @namespace djs.model + */ + + /** + * @memberOf djs.model + */ + + /** + * The basic graphical representation + * + * @class + * + * @abstract + */ + function Base$1() { + + /** + * The object that backs up the shape + * + * @name Base#businessObject + * @type Object + */ + Object.defineProperty(this, 'businessObject', { + writable: true + }); + + + /** + * Single label support, will mapped to multi label array + * + * @name Base#label + * @type Object + */ + Object.defineProperty(this, 'label', { + get: function() { + return this.labels[0]; + }, + set: function(newLabel) { + + var label = this.label, + labels = this.labels; + + if (!newLabel && label) { + labels.remove(label); + } else { + labels.add(newLabel, 0); + } + } + }); + + /** + * The parent shape + * + * @name Base#parent + * @type Shape + */ + parentRefs.bind(this, 'parent'); + + /** + * The list of labels + * + * @name Base#labels + * @type Label + */ + labelRefs.bind(this, 'labels'); + + /** + * The list of outgoing connections + * + * @name Base#outgoing + * @type Array + */ + outgoingRefs.bind(this, 'outgoing'); + + /** + * The list of incoming connections + * + * @name Base#incoming + * @type Array + */ + incomingRefs.bind(this, 'incoming'); + } + + + /** + * A graphical object + * + * @class + * @constructor + * + * @extends Base + */ + function Shape() { + Base$1.call(this); + + /** + * Indicates frame shapes + * + * @name Shape#isFrame + * @type boolean + */ + + /** + * The list of children + * + * @name Shape#children + * @type Array + */ + parentRefs.bind(this, 'children'); + + /** + * @name Shape#host + * @type Shape + */ + attacherRefs.bind(this, 'host'); + + /** + * @name Shape#attachers + * @type Shape + */ + attacherRefs.bind(this, 'attachers'); + } + + e(Shape, Base$1); + + + /** + * A root graphical object + * + * @class + * @constructor + * + * @extends Shape + */ + function Root() { + Shape.call(this); + } + + e(Root, Shape); + + + /** + * A label for an element + * + * @class + * @constructor + * + * @extends Shape + */ + function Label() { + Shape.call(this); + + /** + * The labeled element + * + * @name Label#labelTarget + * @type Base + */ + labelRefs.bind(this, 'labelTarget'); + } + + e(Label, Shape); + + + /** + * A connection between two elements + * + * @class + * @constructor + * + * @extends Base + */ + function Connection() { + Base$1.call(this); + + /** + * The element this connection originates from + * + * @name Connection#source + * @type Base + */ + outgoingRefs.bind(this, 'source'); + + /** + * The element this connection points to + * + * @name Connection#target + * @type Base + */ + incomingRefs.bind(this, 'target'); + } + + e(Connection, Base$1); + + + var types$6 = { + connection: Connection, + shape: Shape, + label: Label, + root: Root + }; + + /** + * Creates a new model element of the specified type + * + * @method create + * + * @example + * + * var shape1 = Model.create('shape', { x: 10, y: 10, width: 100, height: 100 }); + * var shape2 = Model.create('shape', { x: 210, y: 210, width: 100, height: 100 }); + * + * var connection = Model.create('connection', { waypoints: [ { x: 110, y: 55 }, {x: 210, y: 55 } ] }); + * + * @param {string} type lower-cased model name + * @param {Object} attrs attributes to initialize the new model instance with + * + * @return {Base} the new model instance + */ + function create(type, attrs) { + var Type = types$6[type]; + if (!Type) { + throw new Error('unknown type: <' + type + '>'); + } + return assign(new Type(), attrs); + } + + /** + * A factory for diagram-js shapes + */ + function ElementFactory() { + this._uid = 12; + } + + + ElementFactory.prototype.createRoot = function(attrs) { + return this.create('root', attrs); + }; + + ElementFactory.prototype.createLabel = function(attrs) { + return this.create('label', attrs); + }; + + ElementFactory.prototype.createShape = function(attrs) { + return this.create('shape', attrs); + }; + + ElementFactory.prototype.createConnection = function(attrs) { + return this.create('connection', attrs); + }; + + /** + * Create a model element with the given type and + * a number of pre-set attributes. + * + * @param {string} type + * @param {Object} attrs + * @return {djs.model.Base} the newly created model instance + */ + ElementFactory.prototype.create = function(type, attrs) { + + attrs = assign({}, attrs || {}); + + if (!attrs.id) { + attrs.id = type + '_' + (this._uid++); + } + + return create(type, attrs); + }; + + var FN_REF = '__fn'; + + var DEFAULT_PRIORITY = 1000; + + var slice = Array.prototype.slice; + + /** + * A general purpose event bus. + * + * This component is used to communicate across a diagram instance. + * Other parts of a diagram can use it to listen to and broadcast events. + * + * + * ## Registering for Events + * + * The event bus provides the {@link EventBus#on} and {@link EventBus#once} + * methods to register for events. {@link EventBus#off} can be used to + * remove event registrations. Listeners receive an instance of {@link Event} + * as the first argument. It allows them to hook into the event execution. + * + * ```javascript + * + * // listen for event + * eventBus.on('foo', function(event) { + * + * // access event type + * event.type; // 'foo' + * + * // stop propagation to other listeners + * event.stopPropagation(); + * + * // prevent event default + * event.preventDefault(); + * }); + * + * // listen for event with custom payload + * eventBus.on('bar', function(event, payload) { + * console.log(payload); + * }); + * + * // listen for event returning value + * eventBus.on('foobar', function(event) { + * + * // stop event propagation + prevent default + * return false; + * + * // stop event propagation + return custom result + * return { + * complex: 'listening result' + * }; + * }); + * + * + * // listen with custom priority (default=1000, higher is better) + * eventBus.on('priorityfoo', 1500, function(event) { + * console.log('invoked first!'); + * }); + * + * + * // listen for event and pass the context (`this`) + * eventBus.on('foobar', function(event) { + * this.foo(); + * }, this); + * ``` + * + * + * ## Emitting Events + * + * Events can be emitted via the event bus using {@link EventBus#fire}. + * + * ```javascript + * + * // false indicates that the default action + * // was prevented by listeners + * if (eventBus.fire('foo') === false) { + * console.log('default has been prevented!'); + * }; + * + * + * // custom args + return value listener + * eventBus.on('sum', function(event, a, b) { + * return a + b; + * }); + * + * // you can pass custom arguments + retrieve result values. + * var sum = eventBus.fire('sum', 1, 2); + * console.log(sum); // 3 + * ``` + */ + function EventBus() { + this._listeners = {}; + + // cleanup on destroy on lowest priority to allow + // message passing until the bitter end + this.on('diagram.destroy', 1, this._destroy, this); + } + + + /** + * Register an event listener for events with the given name. + * + * The callback will be invoked with `event, ...additionalArguments` + * that have been passed to {@link EventBus#fire}. + * + * Returning false from a listener will prevent the events default action + * (if any is specified). To stop an event from being processed further in + * other listeners execute {@link Event#stopPropagation}. + * + * Returning anything but `undefined` from a listener will stop the listener propagation. + * + * @param {string|Array} events + * @param {number} [priority=1000] the priority in which this listener is called, larger is higher + * @param {Function} callback + * @param {Object} [that] Pass context (`this`) to the callback + */ + EventBus.prototype.on = function(events, priority, callback, that) { + + events = isArray$2(events) ? events : [ events ]; + + if (isFunction(priority)) { + that = callback; + callback = priority; + priority = DEFAULT_PRIORITY; + } + + if (!isNumber(priority)) { + throw new Error('priority must be a number'); + } + + var actualCallback = callback; + + if (that) { + actualCallback = bind(callback, that); + + // make sure we remember and are able to remove + // bound callbacks via {@link #off} using the original + // callback + actualCallback[FN_REF] = callback[FN_REF] || callback; + } + + var self = this; + + events.forEach(function(e) { + self._addListener(e, { + priority: priority, + callback: actualCallback, + next: null + }); + }); + }; + + + /** + * Register an event listener that is executed only once. + * + * @param {string} event the event name to register for + * @param {number} [priority=1000] the priority in which this listener is called, larger is higher + * @param {Function} callback the callback to execute + * @param {Object} [that] Pass context (`this`) to the callback + */ + EventBus.prototype.once = function(event, priority, callback, that) { + var self = this; + + if (isFunction(priority)) { + that = callback; + callback = priority; + priority = DEFAULT_PRIORITY; + } + + if (!isNumber(priority)) { + throw new Error('priority must be a number'); + } + + function wrappedCallback() { + wrappedCallback.__isTomb = true; + + var result = callback.apply(that, arguments); + + self.off(event, wrappedCallback); + + return result; + } + + // make sure we remember and are able to remove + // bound callbacks via {@link #off} using the original + // callback + wrappedCallback[FN_REF] = callback; + + this.on(event, priority, wrappedCallback); + }; + + + /** + * Removes event listeners by event and callback. + * + * If no callback is given, all listeners for a given event name are being removed. + * + * @param {string|Array} events + * @param {Function} [callback] + */ + EventBus.prototype.off = function(events, callback) { + + events = isArray$2(events) ? events : [ events ]; + + var self = this; + + events.forEach(function(event) { + self._removeListener(event, callback); + }); + + }; + + + /** + * Create an EventBus event. + * + * @param {Object} data + * + * @return {Object} event, recognized by the eventBus + */ + EventBus.prototype.createEvent = function(data) { + var event = new InternalEvent(); + + event.init(data); + + return event; + }; + + + /** + * Fires a named event. + * + * @example + * + * // fire event by name + * events.fire('foo'); + * + * // fire event object with nested type + * var event = { type: 'foo' }; + * events.fire(event); + * + * // fire event with explicit type + * var event = { x: 10, y: 20 }; + * events.fire('element.moved', event); + * + * // pass additional arguments to the event + * events.on('foo', function(event, bar) { + * alert(bar); + * }); + * + * events.fire({ type: 'foo' }, 'I am bar!'); + * + * @param {string} [name] the optional event name + * @param {Object} [event] the event object + * @param {...Object} additional arguments to be passed to the callback functions + * + * @return {boolean} the events return value, if specified or false if the + * default action was prevented by listeners + */ + EventBus.prototype.fire = function(type, data) { + var event, + firstListener, + returnValue, + args; + + args = slice.call(arguments); + + if (typeof type === 'object') { + data = type; + type = data.type; + } + + if (!type) { + throw new Error('no event type specified'); + } + + firstListener = this._listeners[type]; + + if (!firstListener) { + return; + } + + // we make sure we fire instances of our home made + // events here. We wrap them only once, though + if (data instanceof InternalEvent) { + + // we are fine, we alread have an event + event = data; + } else { + event = this.createEvent(data); + } + + // ensure we pass the event as the first parameter + args[0] = event; + + // original event type (in case we delegate) + var originalType = event.type; + + // update event type before delegation + if (type !== originalType) { + event.type = type; + } + + try { + returnValue = this._invokeListeners(event, args, firstListener); + } finally { + + // reset event type after delegation + if (type !== originalType) { + event.type = originalType; + } + } + + // set the return value to false if the event default + // got prevented and no other return value exists + if (returnValue === undefined && event.defaultPrevented) { + returnValue = false; + } + + return returnValue; + }; + + + EventBus.prototype.handleError = function(error) { + return this.fire('error', { error: error }) === false; + }; + + + EventBus.prototype._destroy = function() { + this._listeners = {}; + }; + + EventBus.prototype._invokeListeners = function(event, args, listener) { + + var returnValue; + + while (listener) { + + // handle stopped propagation + if (event.cancelBubble) { + break; + } + + returnValue = this._invokeListener(event, args, listener); + + listener = listener.next; + } + + return returnValue; + }; + + EventBus.prototype._invokeListener = function(event, args, listener) { + + var returnValue; + + if (listener.callback.__isTomb) { + return returnValue; + } + + try { + + // returning false prevents the default action + returnValue = invokeFunction(listener.callback, args); + + // stop propagation on return value + if (returnValue !== undefined) { + event.returnValue = returnValue; + event.stopPropagation(); + } + + // prevent default on return false + if (returnValue === false) { + event.preventDefault(); + } + } catch (error) { + if (!this.handleError(error)) { + console.error('unhandled error in event listener', error); + + throw error; + } + } + + return returnValue; + }; + + /* + * Add new listener with a certain priority to the list + * of listeners (for the given event). + * + * The semantics of listener registration / listener execution are + * first register, first serve: New listeners will always be inserted + * after existing listeners with the same priority. + * + * Example: Inserting two listeners with priority 1000 and 1300 + * + * * before: [ 1500, 1500, 1000, 1000 ] + * * after: [ 1500, 1500, (new=1300), 1000, 1000, (new=1000) ] + * + * @param {string} event + * @param {Object} listener { priority, callback } + */ + EventBus.prototype._addListener = function(event, newListener) { + + var listener = this._getListeners(event), + previousListener; + + // no prior listeners + if (!listener) { + this._setListeners(event, newListener); + + return; + } + + // ensure we order listeners by priority from + // 0 (high) to n > 0 (low) + while (listener) { + + if (listener.priority < newListener.priority) { + + newListener.next = listener; + + if (previousListener) { + previousListener.next = newListener; + } else { + this._setListeners(event, newListener); + } + + return; + } + + previousListener = listener; + listener = listener.next; + } + + // add new listener to back + previousListener.next = newListener; + }; + + + EventBus.prototype._getListeners = function(name) { + return this._listeners[name]; + }; + + EventBus.prototype._setListeners = function(name, listener) { + this._listeners[name] = listener; + }; + + EventBus.prototype._removeListener = function(event, callback) { + + var listener = this._getListeners(event), + nextListener, + previousListener, + listenerCallback; + + if (!callback) { + + // clear listeners + this._setListeners(event, null); + + return; + } + + while (listener) { + + nextListener = listener.next; + + listenerCallback = listener.callback; + + if (listenerCallback === callback || listenerCallback[FN_REF] === callback) { + if (previousListener) { + previousListener.next = nextListener; + } else { + + // new first listener + this._setListeners(event, nextListener); + } + } + + previousListener = listener; + listener = nextListener; + } + }; + + /** + * A event that is emitted via the event bus. + */ + function InternalEvent() { } + + InternalEvent.prototype.stopPropagation = function() { + this.cancelBubble = true; + }; + + InternalEvent.prototype.preventDefault = function() { + this.defaultPrevented = true; + }; + + InternalEvent.prototype.init = function(data) { + assign(this, data || {}); + }; + + + /** + * Invoke function. Be fast... + * + * @param {Function} fn + * @param {Array} args + * + * @return {Any} + */ + function invokeFunction(fn, args) { + return fn.apply(null, args); + } + + /** + * SVGs for elements are generated by the {@link GraphicsFactory}. + * + * This utility gives quick access to the important semantic + * parts of an element. + */ + + /** + * Returns the visual part of a diagram element + * + * @param {Snap} gfx + * + * @return {Snap} + */ + function getVisual(gfx) { + return gfx.childNodes[0]; + } + + /** + * Returns the children for a given diagram element. + * + * @param {Snap} gfx + * @return {Snap} + */ + function getChildren(gfx) { + return gfx.parentNode.childNodes[1]; + } + + /** + * A factory that creates graphical elements + * + * @param {EventBus} eventBus + * @param {ElementRegistry} elementRegistry + */ + function GraphicsFactory(eventBus, elementRegistry) { + this._eventBus = eventBus; + this._elementRegistry = elementRegistry; + } + + GraphicsFactory.$inject = [ 'eventBus' , 'elementRegistry' ]; + + + GraphicsFactory.prototype._getChildrenContainer = function(element) { + + var gfx = this._elementRegistry.getGraphics(element); + + var childrenGfx; + + // root element + if (!element.parent) { + childrenGfx = gfx; + } else { + childrenGfx = getChildren(gfx); + if (!childrenGfx) { + childrenGfx = create$1('g'); + classes$1(childrenGfx).add('djs-children'); + + append(gfx.parentNode, childrenGfx); + } + } + + return childrenGfx; + }; + + /** + * Clears the graphical representation of the element and returns the + * cleared visual (the element). + */ + GraphicsFactory.prototype._clear = function(gfx) { + var visual = getVisual(gfx); + + clear(visual); + + return visual; + }; + + /** + * Creates a gfx container for shapes and connections + * + * The layout is as follows: + * + * + * + * + * + * + * + * + * + * + * + * + * + * @param {string} type the type of the element, i.e. shape | connection + * @param {SVGElement} [childrenGfx] + * @param {number} [parentIndex] position to create container in parent + * @param {boolean} [isFrame] is frame element + * + * @return {SVGElement} + */ + GraphicsFactory.prototype._createContainer = function( + type, childrenGfx, parentIndex, isFrame + ) { + var outerGfx = create$1('g'); + classes$1(outerGfx).add('djs-group'); + + // insert node at position + if (typeof parentIndex !== 'undefined') { + prependTo(outerGfx, childrenGfx, childrenGfx.childNodes[parentIndex]); + } else { + append(childrenGfx, outerGfx); + } + + var gfx = create$1('g'); + classes$1(gfx).add('djs-element'); + classes$1(gfx).add('djs-' + type); + + if (isFrame) { + classes$1(gfx).add('djs-frame'); + } + + append(outerGfx, gfx); + + // create visual + var visual = create$1('g'); + classes$1(visual).add('djs-visual'); + + append(gfx, visual); + + return gfx; + }; + + GraphicsFactory.prototype.create = function(type, element, parentIndex) { + var childrenGfx = this._getChildrenContainer(element.parent); + return this._createContainer(type, childrenGfx, parentIndex, isFrameElement(element)); + }; + + GraphicsFactory.prototype.updateContainments = function(elements) { + + var self = this, + elementRegistry = this._elementRegistry, + parents; + + parents = reduce(elements, function(map, e) { + + if (e.parent) { + map[e.parent.id] = e.parent; + } + + return map; + }, {}); + + // update all parents of changed and reorganized their children + // in the correct order (as indicated in our model) + forEach$1(parents, function(parent) { + + var children = parent.children; + + if (!children) { + return; + } + + var childrenGfx = self._getChildrenContainer(parent); + + forEach$1(children.slice().reverse(), function(child) { + var childGfx = elementRegistry.getGraphics(child); + + prependTo(childGfx.parentNode, childrenGfx); + }); + }); + }; + + GraphicsFactory.prototype.drawShape = function(visual, element) { + var eventBus = this._eventBus; + + return eventBus.fire('render.shape', { gfx: visual, element: element }); + }; + + GraphicsFactory.prototype.getShapePath = function(element) { + var eventBus = this._eventBus; + + return eventBus.fire('render.getShapePath', element); + }; + + GraphicsFactory.prototype.drawConnection = function(visual, element) { + var eventBus = this._eventBus; + + return eventBus.fire('render.connection', { gfx: visual, element: element }); + }; + + GraphicsFactory.prototype.getConnectionPath = function(waypoints) { + var eventBus = this._eventBus; + + return eventBus.fire('render.getConnectionPath', waypoints); + }; + + GraphicsFactory.prototype.update = function(type, element, gfx) { + + // do NOT update root element + if (!element.parent) { + return; + } + + var visual = this._clear(gfx); + + // redraw + if (type === 'shape') { + this.drawShape(visual, element); + + // update positioning + translate$1(gfx, element.x, element.y); + } else + if (type === 'connection') { + this.drawConnection(visual, element); + } else { + throw new Error('unknown type: ' + type); + } + + if (element.hidden) { + attr$1(gfx, 'display', 'none'); + } else { + attr$1(gfx, 'display', 'block'); + } + }; + + GraphicsFactory.prototype.remove = function(element) { + var gfx = this._elementRegistry.getGraphics(element); + + // remove + remove$2(gfx.parentNode); + }; + + + // helpers ////////// + + function prependTo(newNode, parentNode, siblingNode) { + var node = siblingNode || parentNode.firstChild; + + // do not prepend node to itself to prevent IE from crashing + // https://github.com/bpmn-io/bpmn-js/issues/746 + if (newNode === node) { + return; + } + + parentNode.insertBefore(newNode, node); + } + + var CoreModule = { + __depends__: [ DrawModule ], + __init__: [ 'canvas' ], + canvas: [ 'type', Canvas ], + elementRegistry: [ 'type', ElementRegistry ], + elementFactory: [ 'type', ElementFactory ], + eventBus: [ 'type', EventBus ], + graphicsFactory: [ 'type', GraphicsFactory ] + }; + + /** + * @typedef { import('didi').ModuleDeclaration } Module + */ + + /** + * Bootstrap an injector from a list of modules, instantiating a number of default components + * + * @param {Array} modules + * + * @return {Injector} a injector to use to access the components + */ + function bootstrap(modules) { + var injector = new Injector(modules); + + injector.init(); + + return injector; + } + + /** + * Creates an injector from passed options. + * + * @param {Object} options + * @return {Injector} + */ + function createInjector(options) { + + options = options || {}; + + var configModule = { + 'config': [ 'value', options ] + }; + + var modules = [ configModule, CoreModule ].concat(options.modules || []); + + return bootstrap(modules); + } + + + /** + * The main diagram-js entry point that bootstraps the diagram with the given + * configuration. + * + * To register extensions with the diagram, pass them as Array to the constructor. + * + * @class djs.Diagram + * @memberOf djs + * @constructor + * + * @example + * + * Creating a plug-in that logs whenever a shape is added to the canvas. + * + * // plug-in implemenentation + * function MyLoggingPlugin(eventBus) { + * eventBus.on('shape.added', function(event) { + * console.log('shape ', event.shape, ' was added to the diagram'); + * }); + * } + * + * // export as module + * export default { + * __init__: [ 'myLoggingPlugin' ], + * myLoggingPlugin: [ 'type', MyLoggingPlugin ] + * }; + * + * + * // instantiate the diagram with the new plug-in + * + * import MyLoggingModule from 'path-to-my-logging-plugin'; + * + * var diagram = new Diagram({ + * modules: [ + * MyLoggingModule + * ] + * }); + * + * diagram.invoke([ 'canvas', function(canvas) { + * // add shape to drawing canvas + * canvas.addShape({ x: 10, y: 10 }); + * }); + * + * // 'shape ... was added to the diagram' logged to console + * + * @param {Object} options + * @param {Array} [options.modules] external modules to instantiate with the diagram + * @param {Injector} [injector] an (optional) injector to bootstrap the diagram with + */ + function Diagram(options, injector) { + + // create injector unless explicitly specified + this.injector = injector = injector || createInjector(options); + + // API + + /** + * Resolves a diagram service + * + * @method Diagram#get + * + * @param {string} name the name of the diagram service to be retrieved + * @param {boolean} [strict=true] if false, resolve missing services to null + */ + this.get = injector.get; + + /** + * Executes a function into which diagram services are injected + * + * @method Diagram#invoke + * + * @param {Function|Object[]} fn the function to resolve + * @param {Object} locals a number of locals to use to resolve certain dependencies + */ + this.invoke = injector.invoke; + + // init + + // indicate via event + + + /** + * An event indicating that all plug-ins are loaded. + * + * Use this event to fire other events to interested plug-ins + * + * @memberOf Diagram + * + * @event diagram.init + * + * @example + * + * eventBus.on('diagram.init', function() { + * eventBus.fire('my-custom-event', { foo: 'BAR' }); + * }); + * + * @type {Object} + */ + this.get('eventBus').fire('diagram.init'); + } + + + /** + * Destroys the diagram + * + * @method Diagram#destroy + */ + Diagram.prototype.destroy = function() { + this.get('eventBus').fire('diagram.destroy'); + }; + + /** + * Clear the diagram, removing all contents. + */ + Diagram.prototype.clear = function() { + this.get('eventBus').fire('diagram.clear'); + }; + + /** + * Moddle base element. + */ + function Base() { } + + Base.prototype.get = function(name) { + return this.$model.properties.get(this, name); + }; + + Base.prototype.set = function(name, value) { + this.$model.properties.set(this, name, value); + }; + + /** + * A model element factory. + * + * @param {Moddle} model + * @param {Properties} properties + */ + function Factory(model, properties) { + this.model = model; + this.properties = properties; + } + + + Factory.prototype.createType = function(descriptor) { + + var model = this.model; + + var props = this.properties, + prototype = Object.create(Base.prototype); + + // initialize default values + forEach$1(descriptor.properties, function(p) { + if (!p.isMany && p.default !== undefined) { + prototype[p.name] = p.default; + } + }); + + props.defineModel(prototype, model); + props.defineDescriptor(prototype, descriptor); + + var name = descriptor.ns.name; + + /** + * The new type constructor + */ + function ModdleElement(attrs) { + props.define(this, '$type', { value: name, enumerable: true }); + props.define(this, '$attrs', { value: {} }); + props.define(this, '$parent', { writable: true }); + + forEach$1(attrs, bind(function(val, key) { + this.set(key, val); + }, this)); + } + + ModdleElement.prototype = prototype; + + ModdleElement.hasType = prototype.$instanceOf = this.model.hasType; + + // static links + props.defineModel(ModdleElement, model); + props.defineDescriptor(ModdleElement, descriptor); + + return ModdleElement; + }; + + /** + * Built-in moddle types + */ + var BUILTINS = { + String: true, + Boolean: true, + Integer: true, + Real: true, + Element: true + }; + + /** + * Converters for built in types from string representations + */ + var TYPE_CONVERTERS = { + String: function(s) { return s; }, + Boolean: function(s) { return s === 'true'; }, + Integer: function(s) { return parseInt(s, 10); }, + Real: function(s) { return parseFloat(s); } + }; + + /** + * Convert a type to its real representation + */ + function coerceType(type, value) { + + var converter = TYPE_CONVERTERS[type]; + + if (converter) { + return converter(value); + } else { + return value; + } + } + + /** + * Return whether the given type is built-in + */ + function isBuiltIn(type) { + return !!BUILTINS[type]; + } + + /** + * Return whether the given type is simple + */ + function isSimple(type) { + return !!TYPE_CONVERTERS[type]; + } + + /** + * Parses a namespaced attribute name of the form (ns:)localName to an object, + * given a default prefix to assume in case no explicit namespace is given. + * + * @param {String} name + * @param {String} [defaultPrefix] the default prefix to take, if none is present. + * + * @return {Object} the parsed name + */ + function parseName(name, defaultPrefix) { + var parts = name.split(/:/), + localName, prefix; + + // no prefix (i.e. only local name) + if (parts.length === 1) { + localName = name; + prefix = defaultPrefix; + } else + // prefix + local name + if (parts.length === 2) { + localName = parts[1]; + prefix = parts[0]; + } else { + throw new Error('expected or , got ' + name); + } + + name = (prefix ? prefix + ':' : '') + localName; + + return { + name: name, + prefix: prefix, + localName: localName + }; + } + + /** + * A utility to build element descriptors. + */ + function DescriptorBuilder(nameNs) { + this.ns = nameNs; + this.name = nameNs.name; + this.allTypes = []; + this.allTypesByName = {}; + this.properties = []; + this.propertiesByName = {}; + } + + + DescriptorBuilder.prototype.build = function() { + return pick(this, [ + 'ns', + 'name', + 'allTypes', + 'allTypesByName', + 'properties', + 'propertiesByName', + 'bodyProperty', + 'idProperty' + ]); + }; + + /** + * Add property at given index. + * + * @param {Object} p + * @param {Number} [idx] + * @param {Boolean} [validate=true] + */ + DescriptorBuilder.prototype.addProperty = function(p, idx, validate) { + + if (typeof idx === 'boolean') { + validate = idx; + idx = undefined; + } + + this.addNamedProperty(p, validate !== false); + + var properties = this.properties; + + if (idx !== undefined) { + properties.splice(idx, 0, p); + } else { + properties.push(p); + } + }; + + + DescriptorBuilder.prototype.replaceProperty = function(oldProperty, newProperty, replace) { + var oldNameNs = oldProperty.ns; + + var props = this.properties, + propertiesByName = this.propertiesByName, + rename = oldProperty.name !== newProperty.name; + + if (oldProperty.isId) { + if (!newProperty.isId) { + throw new Error( + 'property <' + newProperty.ns.name + '> must be id property ' + + 'to refine <' + oldProperty.ns.name + '>'); + } + + this.setIdProperty(newProperty, false); + } + + if (oldProperty.isBody) { + + if (!newProperty.isBody) { + throw new Error( + 'property <' + newProperty.ns.name + '> must be body property ' + + 'to refine <' + oldProperty.ns.name + '>'); + } + + // TODO: Check compatibility + this.setBodyProperty(newProperty, false); + } + + // validate existence and get location of old property + var idx = props.indexOf(oldProperty); + if (idx === -1) { + throw new Error('property <' + oldNameNs.name + '> not found in property list'); + } + + // remove old property + props.splice(idx, 1); + + // replacing the named property is intentional + // + // * validate only if this is a "rename" operation + // * add at specific index unless we "replace" + // + this.addProperty(newProperty, replace ? undefined : idx, rename); + + // make new property available under old name + propertiesByName[oldNameNs.name] = propertiesByName[oldNameNs.localName] = newProperty; + }; + + + DescriptorBuilder.prototype.redefineProperty = function(p, targetPropertyName, replace) { + + var nsPrefix = p.ns.prefix; + var parts = targetPropertyName.split('#'); + + var name = parseName(parts[0], nsPrefix); + var attrName = parseName(parts[1], name.prefix).name; + + var redefinedProperty = this.propertiesByName[attrName]; + if (!redefinedProperty) { + throw new Error('refined property <' + attrName + '> not found'); + } else { + this.replaceProperty(redefinedProperty, p, replace); + } + + delete p.redefines; + }; + + DescriptorBuilder.prototype.addNamedProperty = function(p, validate) { + var ns = p.ns, + propsByName = this.propertiesByName; + + if (validate) { + this.assertNotDefined(p, ns.name); + this.assertNotDefined(p, ns.localName); + } + + propsByName[ns.name] = propsByName[ns.localName] = p; + }; + + DescriptorBuilder.prototype.removeNamedProperty = function(p) { + var ns = p.ns, + propsByName = this.propertiesByName; + + delete propsByName[ns.name]; + delete propsByName[ns.localName]; + }; + + DescriptorBuilder.prototype.setBodyProperty = function(p, validate) { + + if (validate && this.bodyProperty) { + throw new Error( + 'body property defined multiple times ' + + '(<' + this.bodyProperty.ns.name + '>, <' + p.ns.name + '>)'); + } + + this.bodyProperty = p; + }; + + DescriptorBuilder.prototype.setIdProperty = function(p, validate) { + + if (validate && this.idProperty) { + throw new Error( + 'id property defined multiple times ' + + '(<' + this.idProperty.ns.name + '>, <' + p.ns.name + '>)'); + } + + this.idProperty = p; + }; + + DescriptorBuilder.prototype.assertNotDefined = function(p, name) { + var propertyName = p.name, + definedProperty = this.propertiesByName[propertyName]; + + if (definedProperty) { + throw new Error( + 'property <' + propertyName + '> already defined; ' + + 'override of <' + definedProperty.definedBy.ns.name + '#' + definedProperty.ns.name + '> by ' + + '<' + p.definedBy.ns.name + '#' + p.ns.name + '> not allowed without redefines'); + } + }; + + DescriptorBuilder.prototype.hasProperty = function(name) { + return this.propertiesByName[name]; + }; + + DescriptorBuilder.prototype.addTrait = function(t, inherited) { + + var typesByName = this.allTypesByName, + types = this.allTypes; + + var typeName = t.name; + + if (typeName in typesByName) { + return; + } + + forEach$1(t.properties, bind(function(p) { + + // clone property to allow extensions + p = assign({}, p, { + name: p.ns.localName, + inherited: inherited + }); + + Object.defineProperty(p, 'definedBy', { + value: t + }); + + var replaces = p.replaces, + redefines = p.redefines; + + // add replace/redefine support + if (replaces || redefines) { + this.redefineProperty(p, replaces || redefines, replaces); + } else { + if (p.isBody) { + this.setBodyProperty(p); + } + if (p.isId) { + this.setIdProperty(p); + } + this.addProperty(p); + } + }, this)); + + types.push(t); + typesByName[typeName] = t; + }; + + /** + * A registry of Moddle packages. + * + * @param {Array} packages + * @param {Properties} properties + */ + function Registry(packages, properties) { + this.packageMap = {}; + this.typeMap = {}; + + this.packages = []; + + this.properties = properties; + + forEach$1(packages, bind(this.registerPackage, this)); + } + + + Registry.prototype.getPackage = function(uriOrPrefix) { + return this.packageMap[uriOrPrefix]; + }; + + Registry.prototype.getPackages = function() { + return this.packages; + }; + + + Registry.prototype.registerPackage = function(pkg) { + + // copy package + pkg = assign({}, pkg); + + var pkgMap = this.packageMap; + + ensureAvailable(pkgMap, pkg, 'prefix'); + ensureAvailable(pkgMap, pkg, 'uri'); + + // register types + forEach$1(pkg.types, bind(function(descriptor) { + this.registerType(descriptor, pkg); + }, this)); + + pkgMap[pkg.uri] = pkgMap[pkg.prefix] = pkg; + this.packages.push(pkg); + }; + + + /** + * Register a type from a specific package with us + */ + Registry.prototype.registerType = function(type, pkg) { + + type = assign({}, type, { + superClass: (type.superClass || []).slice(), + extends: (type.extends || []).slice(), + properties: (type.properties || []).slice(), + meta: assign((type.meta || {})) + }); + + var ns = parseName(type.name, pkg.prefix), + name = ns.name, + propertiesByName = {}; + + // parse properties + forEach$1(type.properties, bind(function(p) { + + // namespace property names + var propertyNs = parseName(p.name, ns.prefix), + propertyName = propertyNs.name; + + // namespace property types + if (!isBuiltIn(p.type)) { + p.type = parseName(p.type, propertyNs.prefix).name; + } + + assign(p, { + ns: propertyNs, + name: propertyName + }); + + propertiesByName[propertyName] = p; + }, this)); + + // update ns + name + assign(type, { + ns: ns, + name: name, + propertiesByName: propertiesByName + }); + + forEach$1(type.extends, bind(function(extendsName) { + var extended = this.typeMap[extendsName]; + + extended.traits = extended.traits || []; + extended.traits.push(name); + }, this)); + + // link to package + this.definePackage(type, pkg); + + // register + this.typeMap[name] = type; + }; + + + /** + * Traverse the type hierarchy from bottom to top, + * calling iterator with (type, inherited) for all elements in + * the inheritance chain. + * + * @param {Object} nsName + * @param {Function} iterator + * @param {Boolean} [trait=false] + */ + Registry.prototype.mapTypes = function(nsName, iterator, trait) { + + var type = isBuiltIn(nsName.name) ? { name: nsName.name } : this.typeMap[nsName.name]; + + var self = this; + + /** + * Traverse the selected trait. + * + * @param {String} cls + */ + function traverseTrait(cls) { + return traverseSuper(cls, true); + } + + /** + * Traverse the selected super type or trait + * + * @param {String} cls + * @param {Boolean} [trait=false] + */ + function traverseSuper(cls, trait) { + var parentNs = parseName(cls, isBuiltIn(cls) ? '' : nsName.prefix); + self.mapTypes(parentNs, iterator, trait); + } + + if (!type) { + throw new Error('unknown type <' + nsName.name + '>'); + } + + forEach$1(type.superClass, trait ? traverseTrait : traverseSuper); + + // call iterator with (type, inherited=!trait) + iterator(type, !trait); + + forEach$1(type.traits, traverseTrait); + }; + + + /** + * Returns the effective descriptor for a type. + * + * @param {String} type the namespaced name (ns:localName) of the type + * + * @return {Descriptor} the resulting effective descriptor + */ + Registry.prototype.getEffectiveDescriptor = function(name) { + + var nsName = parseName(name); + + var builder = new DescriptorBuilder(nsName); + + this.mapTypes(nsName, function(type, inherited) { + builder.addTrait(type, inherited); + }); + + var descriptor = builder.build(); + + // define package link + this.definePackage(descriptor, descriptor.allTypes[descriptor.allTypes.length - 1].$pkg); + + return descriptor; + }; + + + Registry.prototype.definePackage = function(target, pkg) { + this.properties.define(target, '$pkg', { value: pkg }); + }; + + + + ///////// helpers //////////////////////////// + + function ensureAvailable(packageMap, pkg, identifierKey) { + + var value = pkg[identifierKey]; + + if (value in packageMap) { + throw new Error('package with ' + identifierKey + ' <' + value + '> already defined'); + } + } + + /** + * A utility that gets and sets properties of model elements. + * + * @param {Model} model + */ + function Properties(model) { + this.model = model; + } + + + /** + * Sets a named property on the target element. + * If the value is undefined, the property gets deleted. + * + * @param {Object} target + * @param {String} name + * @param {Object} value + */ + Properties.prototype.set = function(target, name, value) { + + var property = this.model.getPropertyDescriptor(target, name); + + var propertyName = property && property.name; + + if (isUndefined(value)) { + // unset the property, if the specified value is undefined; + // delete from $attrs (for extensions) or the target itself + if (property) { + delete target[propertyName]; + } else { + delete target.$attrs[name]; + } + } else { + // set the property, defining well defined properties on the fly + // or simply updating them in target.$attrs (for extensions) + if (property) { + if (propertyName in target) { + target[propertyName] = value; + } else { + defineProperty(target, property, value); + } + } else { + target.$attrs[name] = value; + } + } + }; + + /** + * Returns the named property of the given element + * + * @param {Object} target + * @param {String} name + * + * @return {Object} + */ + Properties.prototype.get = function(target, name) { + + var property = this.model.getPropertyDescriptor(target, name); + + if (!property) { + return target.$attrs[name]; + } + + var propertyName = property.name; + + // check if access to collection property and lazily initialize it + if (!target[propertyName] && property.isMany) { + defineProperty(target, property, []); + } + + return target[propertyName]; + }; + + + /** + * Define a property on the target element + * + * @param {Object} target + * @param {String} name + * @param {Object} options + */ + Properties.prototype.define = function(target, name, options) { + + if (!options.writable) { + + var value = options.value; + + // use getters for read-only variables to support ES6 proxies + // cf. https://github.com/bpmn-io/internal-docs/issues/386 + options = assign({}, options, { + get: function() { return value; } + }); + + delete options.value; + } + + Object.defineProperty(target, name, options); + }; + + + /** + * Define the descriptor for an element + */ + Properties.prototype.defineDescriptor = function(target, descriptor) { + this.define(target, '$descriptor', { value: descriptor }); + }; + + /** + * Define the model for an element + */ + Properties.prototype.defineModel = function(target, model) { + this.define(target, '$model', { value: model }); + }; + + + function isUndefined(val) { + return typeof val === 'undefined'; + } + + function defineProperty(target, property, value) { + Object.defineProperty(target, property.name, { + enumerable: !property.isReference, + writable: true, + value: value, + configurable: true + }); + } + + //// Moddle implementation ///////////////////////////////////////////////// + + /** + * @class Moddle + * + * A model that can be used to create elements of a specific type. + * + * @example + * + * var Moddle = require('moddle'); + * + * var pkg = { + * name: 'mypackage', + * prefix: 'my', + * types: [ + * { name: 'Root' } + * ] + * }; + * + * var moddle = new Moddle([pkg]); + * + * @param {Array} packages the packages to contain + */ + function Moddle(packages) { + + this.properties = new Properties(this); + + this.factory = new Factory(this, this.properties); + this.registry = new Registry(packages, this.properties); + + this.typeCache = {}; + } + + + /** + * Create an instance of the specified type. + * + * @method Moddle#create + * + * @example + * + * var foo = moddle.create('my:Foo'); + * var bar = moddle.create('my:Bar', { id: 'BAR_1' }); + * + * @param {String|Object} descriptor the type descriptor or name know to the model + * @param {Object} attrs a number of attributes to initialize the model instance with + * @return {Object} model instance + */ + Moddle.prototype.create = function(descriptor, attrs) { + var Type = this.getType(descriptor); + + if (!Type) { + throw new Error('unknown type <' + descriptor + '>'); + } + + return new Type(attrs); + }; + + + /** + * Returns the type representing a given descriptor + * + * @method Moddle#getType + * + * @example + * + * var Foo = moddle.getType('my:Foo'); + * var foo = new Foo({ 'id' : 'FOO_1' }); + * + * @param {String|Object} descriptor the type descriptor or name know to the model + * @return {Object} the type representing the descriptor + */ + Moddle.prototype.getType = function(descriptor) { + + var cache = this.typeCache; + + var name = isString(descriptor) ? descriptor : descriptor.ns.name; + + var type = cache[name]; + + if (!type) { + descriptor = this.registry.getEffectiveDescriptor(name); + type = cache[name] = this.factory.createType(descriptor); + } + + return type; + }; + + + /** + * Creates an any-element type to be used within model instances. + * + * This can be used to create custom elements that lie outside the meta-model. + * The created element contains all the meta-data required to serialize it + * as part of meta-model elements. + * + * @method Moddle#createAny + * + * @example + * + * var foo = moddle.createAny('vendor:Foo', 'http://vendor', { + * value: 'bar' + * }); + * + * var container = moddle.create('my:Container', 'http://my', { + * any: [ foo ] + * }); + * + * // go ahead and serialize the stuff + * + * + * @param {String} name the name of the element + * @param {String} nsUri the namespace uri of the element + * @param {Object} [properties] a map of properties to initialize the instance with + * @return {Object} the any type instance + */ + Moddle.prototype.createAny = function(name, nsUri, properties) { + + var nameNs = parseName(name); + + var element = { + $type: name, + $instanceOf: function(type) { + return type === this.$type; + } + }; + + var descriptor = { + name: name, + isGeneric: true, + ns: { + prefix: nameNs.prefix, + localName: nameNs.localName, + uri: nsUri + } + }; + + this.properties.defineDescriptor(element, descriptor); + this.properties.defineModel(element, this); + this.properties.define(element, '$parent', { enumerable: false, writable: true }); + this.properties.define(element, '$instanceOf', { enumerable: false, writable: true }); + + forEach$1(properties, function(a, key) { + if (isObject(a) && a.value !== undefined) { + element[a.name] = a.value; + } else { + element[key] = a; + } + }); + + return element; + }; + + /** + * Returns a registered package by uri or prefix + * + * @return {Object} the package + */ + Moddle.prototype.getPackage = function(uriOrPrefix) { + return this.registry.getPackage(uriOrPrefix); + }; + + /** + * Returns a snapshot of all known packages + * + * @return {Object} the package + */ + Moddle.prototype.getPackages = function() { + return this.registry.getPackages(); + }; + + /** + * Returns the descriptor for an element + */ + Moddle.prototype.getElementDescriptor = function(element) { + return element.$descriptor; + }; + + /** + * Returns true if the given descriptor or instance + * represents the given type. + * + * May be applied to this, if element is omitted. + */ + Moddle.prototype.hasType = function(element, type) { + if (type === undefined) { + type = element; + element = this; + } + + var descriptor = element.$model.getElementDescriptor(element); + + return (type in descriptor.allTypesByName); + }; + + /** + * Returns the descriptor of an elements named property + */ + Moddle.prototype.getPropertyDescriptor = function(element, property) { + return this.getElementDescriptor(element).propertiesByName[property]; + }; + + /** + * Returns a mapped type's descriptor + */ + Moddle.prototype.getTypeDescriptor = function(type) { + return this.registry.typeMap[type]; + }; + + var fromCharCode = String.fromCharCode; + + var hasOwnProperty = Object.prototype.hasOwnProperty; + + var ENTITY_PATTERN = /&#(\d+);|&#x([0-9a-f]+);|&(\w+);/ig; + + var ENTITY_MAPPING = { + 'amp': '&', + 'apos': '\'', + 'gt': '>', + 'lt': '<', + 'quot': '"' + }; + + // map UPPERCASE variants of supported special chars + Object.keys(ENTITY_MAPPING).forEach(function(k) { + ENTITY_MAPPING[k.toUpperCase()] = ENTITY_MAPPING[k]; + }); + + + function replaceEntities(_, d, x, z) { + + // reserved names, i.e.   + if (z) { + if (hasOwnProperty.call(ENTITY_MAPPING, z)) { + return ENTITY_MAPPING[z]; + } else { + + // fall back to original value + return '&' + z + ';'; + } + } + + // decimal encoded char + if (d) { + return fromCharCode(d); + } + + // hex encoded char + return fromCharCode(parseInt(x, 16)); + } + + + /** + * A basic entity decoder that can decode a minimal + * sub-set of reserved names (&) as well as + * hex (ય) and decimal (ӏ) encoded characters. + * + * @param {string} str + * + * @return {string} decoded string + */ + function decodeEntities(s) { + if (s.length > 3 && s.indexOf('&') !== -1) { + return s.replace(ENTITY_PATTERN, replaceEntities); + } + + return s; + } + + var XSI_URI = 'http://www.w3.org/2001/XMLSchema-instance'; + var XSI_PREFIX = 'xsi'; + var XSI_TYPE$1 = 'xsi:type'; + + var NON_WHITESPACE_OUTSIDE_ROOT_NODE = 'non-whitespace outside of root node'; + + function error$1(msg) { + return new Error(msg); + } + + function missingNamespaceForPrefix(prefix) { + return 'missing namespace for prefix <' + prefix + '>'; + } + + function getter(getFn) { + return { + 'get': getFn, + 'enumerable': true + }; + } + + function cloneNsMatrix(nsMatrix) { + var clone = {}, key; + for (key in nsMatrix) { + clone[key] = nsMatrix[key]; + } + return clone; + } + + function uriPrefix(prefix) { + return prefix + '$uri'; + } + + function buildNsMatrix(nsUriToPrefix) { + var nsMatrix = {}, + uri, + prefix; + + for (uri in nsUriToPrefix) { + prefix = nsUriToPrefix[uri]; + nsMatrix[prefix] = prefix; + nsMatrix[uriPrefix(prefix)] = uri; + } + + return nsMatrix; + } + + function noopGetContext() { + return { 'line': 0, 'column': 0 }; + } + + function throwFunc(err) { + throw err; + } + + /** + * Creates a new parser with the given options. + * + * @constructor + * + * @param {!Object=} options + */ + function Parser(options) { + + if (!this) { + return new Parser(options); + } + + var proxy = options && options['proxy']; + + var onText, + onOpenTag, + onCloseTag, + onCDATA, + onError = throwFunc, + onWarning, + onComment, + onQuestion, + onAttention; + + var getContext = noopGetContext; + + /** + * Do we need to parse the current elements attributes for namespaces? + * + * @type {boolean} + */ + var maybeNS = false; + + /** + * Do we process namespaces at all? + * + * @type {boolean} + */ + var isNamespace = false; + + /** + * The caught error returned on parse end + * + * @type {Error} + */ + var returnError = null; + + /** + * Should we stop parsing? + * + * @type {boolean} + */ + var parseStop = false; + + /** + * A map of { uri: prefix } used by the parser. + * + * This map will ensure we can normalize prefixes during processing; + * for each uri, only one prefix will be exposed to the handlers. + * + * @type {!Object}} + */ + var nsUriToPrefix; + + /** + * Handle parse error. + * + * @param {string|Error} err + */ + function handleError(err) { + if (!(err instanceof Error)) { + err = error$1(err); + } + + returnError = err; + + onError(err, getContext); + } + + /** + * Handle parse error. + * + * @param {string|Error} err + */ + function handleWarning(err) { + + if (!onWarning) { + return; + } + + if (!(err instanceof Error)) { + err = error$1(err); + } + + onWarning(err, getContext); + } + + /** + * Register parse listener. + * + * @param {string} name + * @param {Function} cb + * + * @return {Parser} + */ + this['on'] = function(name, cb) { + + if (typeof cb !== 'function') { + throw error$1('required args '); + } + + switch (name) { + case 'openTag': onOpenTag = cb; break; + case 'text': onText = cb; break; + case 'closeTag': onCloseTag = cb; break; + case 'error': onError = cb; break; + case 'warn': onWarning = cb; break; + case 'cdata': onCDATA = cb; break; + case 'attention': onAttention = cb; break; // + case 'question': onQuestion = cb; break; // + case 'comment': onComment = cb; break; + default: + throw error$1('unsupported event: ' + name); + } + + return this; + }; + + /** + * Set the namespace to prefix mapping. + * + * @example + * + * parser.ns({ + * 'http://foo': 'foo', + * 'http://bar': 'bar' + * }); + * + * @param {!Object} nsMap + * + * @return {Parser} + */ + this['ns'] = function(nsMap) { + + if (typeof nsMap === 'undefined') { + nsMap = {}; + } + + if (typeof nsMap !== 'object') { + throw error$1('required args '); + } + + var _nsUriToPrefix = {}, k; + + for (k in nsMap) { + _nsUriToPrefix[k] = nsMap[k]; + } + + // FORCE default mapping for schema instance + _nsUriToPrefix[XSI_URI] = XSI_PREFIX; + + isNamespace = true; + nsUriToPrefix = _nsUriToPrefix; + + return this; + }; + + /** + * Parse xml string. + * + * @param {string} xml + * + * @return {Error} returnError, if not thrown + */ + this['parse'] = function(xml) { + if (typeof xml !== 'string') { + throw error$1('required args '); + } + + returnError = null; + + parse(xml); + + getContext = noopGetContext; + parseStop = false; + + return returnError; + }; + + /** + * Stop parsing. + */ + this['stop'] = function() { + parseStop = true; + }; + + /** + * Parse string, invoking configured listeners on element. + * + * @param {string} xml + */ + function parse(xml) { + var nsMatrixStack = isNamespace ? [] : null, + nsMatrix = isNamespace ? buildNsMatrix(nsUriToPrefix) : null, + _nsMatrix, + nodeStack = [], + anonymousNsCount = 0, + tagStart = false, + tagEnd = false, + i = 0, j = 0, + x, y, q, w, v, + xmlns, + elementName, + _elementName, + elementProxy + ; + + var attrsString = '', + attrsStart = 0, + cachedAttrs // false = parsed with errors, null = needs parsing + ; + + /** + * Parse attributes on demand and returns the parsed attributes. + * + * Return semantics: (1) `false` on attribute parse error, + * (2) object hash on extracted attrs. + * + * @return {boolean|Object} + */ + function getAttrs() { + if (cachedAttrs !== null) { + return cachedAttrs; + } + + var nsUri, + nsUriPrefix, + nsName, + defaultAlias = isNamespace && nsMatrix['xmlns'], + attrList = isNamespace && maybeNS ? [] : null, + i = attrsStart, + s = attrsString, + l = s.length, + hasNewMatrix, + newalias, + value, + alias, + name, + attrs = {}, + seenAttrs = {}, + skipAttr, + w, + j; + + parseAttr: + for (; i < l; i++) { + skipAttr = false; + w = s.charCodeAt(i); + + if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE={ \f\n\r\t\v} + continue; + } + + // wait for non whitespace character + if (w < 65 || w > 122 || (w > 90 && w < 97)) { + if (w !== 95 && w !== 58) { // char 95"_" 58":" + handleWarning('illegal first char attribute name'); + skipAttr = true; + } + } + + // parse attribute name + for (j = i + 1; j < l; j++) { + w = s.charCodeAt(j); + + if ( + w > 96 && w < 123 || + w > 64 && w < 91 || + w > 47 && w < 59 || + w === 46 || // '.' + w === 45 || // '-' + w === 95 // '_' + ) { + continue; + } + + // unexpected whitespace + if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE + handleWarning('missing attribute value'); + i = j; + + continue parseAttr; + } + + // expected "=" + if (w === 61) { // "=" == 61 + break; + } + + handleWarning('illegal attribute name char'); + skipAttr = true; + } + + name = s.substring(i, j); + + if (name === 'xmlns:xmlns') { + handleWarning('illegal declaration of xmlns'); + skipAttr = true; + } + + w = s.charCodeAt(j + 1); + + if (w === 34) { // '"' + j = s.indexOf('"', i = j + 2); + + if (j === -1) { + j = s.indexOf('\'', i); + + if (j !== -1) { + handleWarning('attribute value quote missmatch'); + skipAttr = true; + } + } + + } else if (w === 39) { // "'" + j = s.indexOf('\'', i = j + 2); + + if (j === -1) { + j = s.indexOf('"', i); + + if (j !== -1) { + handleWarning('attribute value quote missmatch'); + skipAttr = true; + } + } + + } else { + handleWarning('missing attribute value quotes'); + skipAttr = true; + + // skip to next space + for (j = j + 1; j < l; j++) { + w = s.charCodeAt(j + 1); + + if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE + break; + } + } + + } + + if (j === -1) { + handleWarning('missing closing quotes'); + + j = l; + skipAttr = true; + } + + if (!skipAttr) { + value = s.substring(i, j); + } + + i = j; + + // ensure SPACE follows attribute + // skip illegal content otherwise + // example a="b"c + for (; j + 1 < l; j++) { + w = s.charCodeAt(j + 1); + + if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE + break; + } + + // FIRST ILLEGAL CHAR + if (i === j) { + handleWarning('illegal character after attribute end'); + skipAttr = true; + } + } + + // advance cursor to next attribute + i = j + 1; + + if (skipAttr) { + continue parseAttr; + } + + // check attribute re-declaration + if (name in seenAttrs) { + handleWarning('attribute <' + name + '> already defined'); + continue; + } + + seenAttrs[name] = true; + + if (!isNamespace) { + attrs[name] = value; + continue; + } + + // try to extract namespace information + if (maybeNS) { + newalias = ( + name === 'xmlns' + ? 'xmlns' + : (name.charCodeAt(0) === 120 && name.substr(0, 6) === 'xmlns:') + ? name.substr(6) + : null + ); + + // handle xmlns(:alias) assignment + if (newalias !== null) { + nsUri = decodeEntities(value); + nsUriPrefix = uriPrefix(newalias); + + alias = nsUriToPrefix[nsUri]; + + if (!alias) { + + // no prefix defined or prefix collision + if ( + (newalias === 'xmlns') || + (nsUriPrefix in nsMatrix && nsMatrix[nsUriPrefix] !== nsUri) + ) { + + // alocate free ns prefix + do { + alias = 'ns' + (anonymousNsCount++); + } while (typeof nsMatrix[alias] !== 'undefined'); + } else { + alias = newalias; + } + + nsUriToPrefix[nsUri] = alias; + } + + if (nsMatrix[newalias] !== alias) { + if (!hasNewMatrix) { + nsMatrix = cloneNsMatrix(nsMatrix); + hasNewMatrix = true; + } + + nsMatrix[newalias] = alias; + if (newalias === 'xmlns') { + nsMatrix[uriPrefix(alias)] = nsUri; + defaultAlias = alias; + } + + nsMatrix[nsUriPrefix] = nsUri; + } + + // expose xmlns(:asd)="..." in attributes + attrs[name] = value; + continue; + } + + // collect attributes until all namespace + // declarations are processed + attrList.push(name, value); + continue; + + } /** end if (maybeNs) */ + + // handle attributes on element without + // namespace declarations + w = name.indexOf(':'); + if (w === -1) { + attrs[name] = value; + continue; + } + + // normalize ns attribute name + if (!(nsName = nsMatrix[name.substring(0, w)])) { + handleWarning(missingNamespaceForPrefix(name.substring(0, w))); + continue; + } + + name = defaultAlias === nsName + ? name.substr(w + 1) + : nsName + name.substr(w); + + // end: normalize ns attribute name + + // normalize xsi:type ns attribute value + if (name === XSI_TYPE$1) { + w = value.indexOf(':'); + + if (w !== -1) { + nsName = value.substring(0, w); + + // handle default prefixes, i.e. xs:String gracefully + nsName = nsMatrix[nsName] || nsName; + value = nsName + value.substring(w); + } else { + value = defaultAlias + ':' + value; + } + } + + // end: normalize xsi:type ns attribute value + + attrs[name] = value; + } + + + // handle deferred, possibly namespaced attributes + if (maybeNS) { + + // normalize captured attributes + for (i = 0, l = attrList.length; i < l; i++) { + + name = attrList[i++]; + value = attrList[i]; + + w = name.indexOf(':'); + + if (w !== -1) { + + // normalize ns attribute name + if (!(nsName = nsMatrix[name.substring(0, w)])) { + handleWarning(missingNamespaceForPrefix(name.substring(0, w))); + continue; + } + + name = defaultAlias === nsName + ? name.substr(w + 1) + : nsName + name.substr(w); + + // end: normalize ns attribute name + + // normalize xsi:type ns attribute value + if (name === XSI_TYPE$1) { + w = value.indexOf(':'); + + if (w !== -1) { + nsName = value.substring(0, w); + + // handle default prefixes, i.e. xs:String gracefully + nsName = nsMatrix[nsName] || nsName; + value = nsName + value.substring(w); + } else { + value = defaultAlias + ':' + value; + } + } + + // end: normalize xsi:type ns attribute value + } + + attrs[name] = value; + } + + // end: normalize captured attributes + } + + return cachedAttrs = attrs; + } + + /** + * Extract the parse context { line, column, part } + * from the current parser position. + * + * @return {Object} parse context + */ + function getParseContext() { + var splitsRe = /(\r\n|\r|\n)/g; + + var line = 0; + var column = 0; + var startOfLine = 0; + var endOfLine = j; + var match; + var data; + + while (i >= startOfLine) { + + match = splitsRe.exec(xml); + + if (!match) { + break; + } + + // end of line = (break idx + break chars) + endOfLine = match[0].length + match.index; + + if (endOfLine > i) { + break; + } + + // advance to next line + line += 1; + + startOfLine = endOfLine; + } + + // EOF errors + if (i == -1) { + column = endOfLine; + data = xml.substring(j); + } else + + // start errors + if (j === 0) { + data = xml.substring(j, i); + } + + // other errors + else { + column = i - startOfLine; + data = (j == -1 ? xml.substring(i) : xml.substring(i, j + 1)); + } + + return { + 'data': data, + 'line': line, + 'column': column + }; + } + + getContext = getParseContext; + + + if (proxy) { + elementProxy = Object.create({}, { + 'name': getter(function() { + return elementName; + }), + 'originalName': getter(function() { + return _elementName; + }), + 'attrs': getter(getAttrs), + 'ns': getter(function() { + return nsMatrix; + }) + }); + } + + // actual parse logic + while (j !== -1) { + + if (xml.charCodeAt(j) === 60) { // "<" + i = j; + } else { + i = xml.indexOf('<', j); + } + + // parse end + if (i === -1) { + if (nodeStack.length) { + return handleError('unexpected end of file'); + } + + if (j === 0) { + return handleError('missing start tag'); + } + + if (j < xml.length) { + if (xml.substring(j).trim()) { + handleWarning(NON_WHITESPACE_OUTSIDE_ROOT_NODE); + } + } + + return; + } + + // parse text + if (j !== i) { + + if (nodeStack.length) { + if (onText) { + onText(xml.substring(j, i), decodeEntities, getContext); + + if (parseStop) { + return; + } + } + } else { + if (xml.substring(j, i).trim()) { + handleWarning(NON_WHITESPACE_OUTSIDE_ROOT_NODE); + + if (parseStop) { + return; + } + } + } + } + + w = xml.charCodeAt(i+1); + + // parse comments + CDATA + if (w === 33) { // "!" + q = xml.charCodeAt(i+2); + + // CDATA section + if (q === 91 && xml.substr(i + 3, 6) === 'CDATA[') { // 91 == "[" + j = xml.indexOf(']]>', i); + if (j === -1) { + return handleError('unclosed cdata'); + } + + if (onCDATA) { + onCDATA(xml.substring(i + 9, j), getContext); + if (parseStop) { + return; + } + } + + j += 3; + continue; + } + + // comment + if (q === 45 && xml.charCodeAt(i + 3) === 45) { // 45 == "-" + j = xml.indexOf('-->', i); + if (j === -1) { + return handleError('unclosed comment'); + } + + + if (onComment) { + onComment(xml.substring(i + 4, j), decodeEntities, getContext); + if (parseStop) { + return; + } + } + + j += 3; + continue; + } + } + + // parse question + if (w === 63) { // "?" + j = xml.indexOf('?>', i); + if (j === -1) { + return handleError('unclosed question'); + } + + if (onQuestion) { + onQuestion(xml.substring(i, j + 2), getContext); + if (parseStop) { + return; + } + } + + j += 2; + continue; + } + + // find matching closing tag for attention or standard tags + // for that we must skip through attribute values + // (enclosed in single or double quotes) + for (x = i + 1; ; x++) { + v = xml.charCodeAt(x); + if (isNaN(v)) { + j = -1; + return handleError('unclosed tag'); + } + + // [10] AttValue ::= '"' ([^<&"] | Reference)* '"' | "'" ([^<&'] | Reference)* "'" + // skips the quoted string + // (double quotes) does not appear in a literal enclosed by (double quotes) + // (single quote) does not appear in a literal enclosed by (single quote) + if (v === 34) { // '"' + q = xml.indexOf('"', x + 1); + x = q !== -1 ? q : x; + } else if (v === 39) { // "'" + q = xml.indexOf("'", x + 1); + x = q !== -1 ? q : x; + } else if (v === 62) { // '>' + j = x; + break; + } + } + + + // parse attention + // previously comment and CDATA have already been parsed + if (w === 33) { // "!" + + if (onAttention) { + onAttention(xml.substring(i, j + 1), decodeEntities, getContext); + if (parseStop) { + return; + } + } + + j += 1; + continue; + } + + // don't process attributes; + // there are none + cachedAttrs = {}; + + // if (xml.charCodeAt(i+1) === 47) { // close tag match + x = elementName = nodeStack.pop(); + q = i + 2 + x.length; + + if (xml.substring(i + 2, q) !== x) { + return handleError('closing tag mismatch'); + } + + // verify chars in close tag + for (; q < j; q++) { + w = xml.charCodeAt(q); + + if (w === 32 || (w > 8 && w < 14)) { // \f\n\r\t\v space + continue; + } + + return handleError('close tag'); + } + + } else { + if (xml.charCodeAt(j - 1) === 47) { // .../> + x = elementName = xml.substring(i + 1, j - 1); + + tagStart = true; + tagEnd = true; + + } else { + x = elementName = xml.substring(i + 1, j); + + tagStart = true; + tagEnd = false; + } + + if (!(w > 96 && w < 123 || w > 64 && w < 91 || w === 95 || w === 58)) { // char 95"_" 58":" + return handleError('illegal first char nodeName'); + } + + for (q = 1, y = x.length; q < y; q++) { + w = x.charCodeAt(q); + + if (w > 96 && w < 123 || w > 64 && w < 91 || w > 47 && w < 59 || w === 45 || w === 95 || w == 46) { + continue; + } + + if (w === 32 || (w < 14 && w > 8)) { // \f\n\r\t\v space + elementName = x.substring(0, q); + + // maybe there are attributes + cachedAttrs = null; + break; + } + + return handleError('invalid nodeName'); + } + + if (!tagEnd) { + nodeStack.push(elementName); + } + } + + if (isNamespace) { + + _nsMatrix = nsMatrix; + + if (tagStart) { + + // remember old namespace + // unless we're self-closing + if (!tagEnd) { + nsMatrixStack.push(_nsMatrix); + } + + if (cachedAttrs === null) { + + // quick check, whether there may be namespace + // declarations on the node; if that is the case + // we need to eagerly parse the node attributes + if ((maybeNS = x.indexOf('xmlns', q) !== -1)) { + attrsStart = q; + attrsString = x; + + getAttrs(); + + maybeNS = false; + } + } + } + + _elementName = elementName; + + w = elementName.indexOf(':'); + if (w !== -1) { + xmlns = nsMatrix[elementName.substring(0, w)]; + + // prefix given; namespace must exist + if (!xmlns) { + return handleError('missing namespace on <' + _elementName + '>'); + } + + elementName = elementName.substr(w + 1); + } else { + xmlns = nsMatrix['xmlns']; + + // if no default namespace is defined, + // we'll import the element as anonymous. + // + // it is up to users to correct that to the document defined + // targetNamespace, or whatever their undersanding of the + // XML spec mandates. + } + + // adjust namespace prefixs as configured + if (xmlns) { + elementName = xmlns + ':' + elementName; + } + + } + + if (tagStart) { + attrsStart = q; + attrsString = x; + + if (onOpenTag) { + if (proxy) { + onOpenTag(elementProxy, decodeEntities, tagEnd, getContext); + } else { + onOpenTag(elementName, getAttrs, decodeEntities, tagEnd, getContext); + } + + if (parseStop) { + return; + } + } + + } + + if (tagEnd) { + + if (onCloseTag) { + onCloseTag(proxy ? elementProxy : elementName, decodeEntities, tagStart, getContext); + + if (parseStop) { + return; + } + } + + // restore old namespace + if (isNamespace) { + if (!tagStart) { + nsMatrix = nsMatrixStack.pop(); + } else { + nsMatrix = _nsMatrix; + } + } + } + + j += 1; + } + } /** end parse */ + + } + + function hasLowerCaseAlias(pkg) { + return pkg.xml && pkg.xml.tagAlias === 'lowerCase'; + } + + var DEFAULT_NS_MAP = { + 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', + 'xml': 'http://www.w3.org/XML/1998/namespace' + }; + + var XSI_TYPE = 'xsi:type'; + + function serializeFormat(element) { + return element.xml && element.xml.serialize; + } + + function serializeAsType(element) { + return serializeFormat(element) === XSI_TYPE; + } + + function serializeAsProperty(element) { + return serializeFormat(element) === 'property'; + } + + function capitalize(str) { + return str.charAt(0).toUpperCase() + str.slice(1); + } + + function aliasToName(aliasNs, pkg) { + + if (!hasLowerCaseAlias(pkg)) { + return aliasNs.name; + } + + return aliasNs.prefix + ':' + capitalize(aliasNs.localName); + } + + function prefixedToName(nameNs, pkg) { + + var name = nameNs.name, + localName = nameNs.localName; + + var typePrefix = pkg.xml && pkg.xml.typePrefix; + + if (typePrefix && localName.indexOf(typePrefix) === 0) { + return nameNs.prefix + ':' + localName.slice(typePrefix.length); + } else { + return name; + } + } + + function normalizeXsiTypeName(name, model) { + + var nameNs = parseName(name); + var pkg = model.getPackage(nameNs.prefix); + + return prefixedToName(nameNs, pkg); + } + + function error(message) { + return new Error(message); + } + + /** + * Get the moddle descriptor for a given instance or type. + * + * @param {ModdleElement|Function} element + * + * @return {Object} the moddle descriptor + */ + function getModdleDescriptor(element) { + return element.$descriptor; + } + + + /** + * A parse context. + * + * @class + * + * @param {Object} options + * @param {ElementHandler} options.rootHandler the root handler for parsing a document + * @param {boolean} [options.lax=false] whether or not to ignore invalid elements + */ + function Context(options) { + + /** + * @property {ElementHandler} rootHandler + */ + + /** + * @property {Boolean} lax + */ + + assign(this, options); + + this.elementsById = {}; + this.references = []; + this.warnings = []; + + /** + * Add an unresolved reference. + * + * @param {Object} reference + */ + this.addReference = function(reference) { + this.references.push(reference); + }; + + /** + * Add a processed element. + * + * @param {ModdleElement} element + */ + this.addElement = function(element) { + + if (!element) { + throw error('expected element'); + } + + var elementsById = this.elementsById; + + var descriptor = getModdleDescriptor(element); + + var idProperty = descriptor.idProperty, + id; + + if (idProperty) { + id = element.get(idProperty.name); + + if (id) { + + // for QName validation as per http://www.w3.org/TR/REC-xml/#NT-NameChar + if (!/^([a-z][\w-.]*:)?[a-z_][\w-.]*$/i.test(id)) { + throw new Error('illegal ID <' + id + '>'); + } + + if (elementsById[id]) { + throw error('duplicate ID <' + id + '>'); + } + + elementsById[id] = element; + } + } + }; + + /** + * Add an import warning. + * + * @param {Object} warning + * @param {String} warning.message + * @param {Error} [warning.error] + */ + this.addWarning = function(warning) { + this.warnings.push(warning); + }; + } + + function BaseHandler() {} + + BaseHandler.prototype.handleEnd = function() {}; + BaseHandler.prototype.handleText = function() {}; + BaseHandler.prototype.handleNode = function() {}; + + + /** + * A simple pass through handler that does nothing except for + * ignoring all input it receives. + * + * This is used to ignore unknown elements and + * attributes. + */ + function NoopHandler() { } + + NoopHandler.prototype = Object.create(BaseHandler.prototype); + + NoopHandler.prototype.handleNode = function() { + return this; + }; + + function BodyHandler() {} + + BodyHandler.prototype = Object.create(BaseHandler.prototype); + + BodyHandler.prototype.handleText = function(text) { + this.body = (this.body || '') + text; + }; + + function ReferenceHandler(property, context) { + this.property = property; + this.context = context; + } + + ReferenceHandler.prototype = Object.create(BodyHandler.prototype); + + ReferenceHandler.prototype.handleNode = function(node) { + + if (this.element) { + throw error('expected no sub nodes'); + } else { + this.element = this.createReference(node); + } + + return this; + }; + + ReferenceHandler.prototype.handleEnd = function() { + this.element.id = this.body; + }; + + ReferenceHandler.prototype.createReference = function(node) { + return { + property: this.property.ns.name, + id: '' + }; + }; + + function ValueHandler(propertyDesc, element) { + this.element = element; + this.propertyDesc = propertyDesc; + } + + ValueHandler.prototype = Object.create(BodyHandler.prototype); + + ValueHandler.prototype.handleEnd = function() { + + var value = this.body || '', + element = this.element, + propertyDesc = this.propertyDesc; + + value = coerceType(propertyDesc.type, value); + + if (propertyDesc.isMany) { + element.get(propertyDesc.name).push(value); + } else { + element.set(propertyDesc.name, value); + } + }; + + + function BaseElementHandler() {} + + BaseElementHandler.prototype = Object.create(BodyHandler.prototype); + + BaseElementHandler.prototype.handleNode = function(node) { + var parser = this, + element = this.element; + + if (!element) { + element = this.element = this.createElement(node); + + this.context.addElement(element); + } else { + parser = this.handleChild(node); + } + + return parser; + }; + + /** + * @class Reader.ElementHandler + * + */ + function ElementHandler(model, typeName, context) { + this.model = model; + this.type = model.getType(typeName); + this.context = context; + } + + ElementHandler.prototype = Object.create(BaseElementHandler.prototype); + + ElementHandler.prototype.addReference = function(reference) { + this.context.addReference(reference); + }; + + ElementHandler.prototype.handleText = function(text) { + + var element = this.element, + descriptor = getModdleDescriptor(element), + bodyProperty = descriptor.bodyProperty; + + if (!bodyProperty) { + throw error('unexpected body text <' + text + '>'); + } + + BodyHandler.prototype.handleText.call(this, text); + }; + + ElementHandler.prototype.handleEnd = function() { + + var value = this.body, + element = this.element, + descriptor = getModdleDescriptor(element), + bodyProperty = descriptor.bodyProperty; + + if (bodyProperty && value !== undefined) { + value = coerceType(bodyProperty.type, value); + element.set(bodyProperty.name, value); + } + }; + + /** + * Create an instance of the model from the given node. + * + * @param {Element} node the xml node + */ + ElementHandler.prototype.createElement = function(node) { + var attributes = node.attributes, + Type = this.type, + descriptor = getModdleDescriptor(Type), + context = this.context, + instance = new Type({}), + model = this.model, + propNameNs; + + forEach$1(attributes, function(value, name) { + + var prop = descriptor.propertiesByName[name], + values; + + if (prop && prop.isReference) { + + if (!prop.isMany) { + context.addReference({ + element: instance, + property: prop.ns.name, + id: value + }); + } else { + + // IDREFS: parse references as whitespace-separated list + values = value.split(' '); + + forEach$1(values, function(v) { + context.addReference({ + element: instance, + property: prop.ns.name, + id: v + }); + }); + } + + } else { + if (prop) { + value = coerceType(prop.type, value); + } else + if (name !== 'xmlns') { + propNameNs = parseName(name, descriptor.ns.prefix); + + // check whether attribute is defined in a well-known namespace + // if that is the case we emit a warning to indicate potential misuse + if (model.getPackage(propNameNs.prefix)) { + + context.addWarning({ + message: 'unknown attribute <' + name + '>', + element: instance, + property: name, + value: value + }); + } + } + + instance.set(name, value); + } + }); + + return instance; + }; + + ElementHandler.prototype.getPropertyForNode = function(node) { + + var name = node.name; + var nameNs = parseName(name); + + var type = this.type, + model = this.model, + descriptor = getModdleDescriptor(type); + + var propertyName = nameNs.name, + property = descriptor.propertiesByName[propertyName], + elementTypeName, + elementType; + + // search for properties by name first + + if (property && !property.isAttr) { + + if (serializeAsType(property)) { + elementTypeName = node.attributes[XSI_TYPE]; + + // xsi type is optional, if it does not exists the + // default type is assumed + if (elementTypeName) { + + // take possible type prefixes from XML + // into account, i.e.: xsi:type="t{ActualType}" + elementTypeName = normalizeXsiTypeName(elementTypeName, model); + + elementType = model.getType(elementTypeName); + + return assign({}, property, { + effectiveType: getModdleDescriptor(elementType).name + }); + } + } + + // search for properties by name first + return property; + } + + var pkg = model.getPackage(nameNs.prefix); + + if (pkg) { + elementTypeName = aliasToName(nameNs, pkg); + elementType = model.getType(elementTypeName); + + // search for collection members later + property = find(descriptor.properties, function(p) { + return !p.isVirtual && !p.isReference && !p.isAttribute && elementType.hasType(p.type); + }); + + if (property) { + return assign({}, property, { + effectiveType: getModdleDescriptor(elementType).name + }); + } + } else { + + // parse unknown element (maybe extension) + property = find(descriptor.properties, function(p) { + return !p.isReference && !p.isAttribute && p.type === 'Element'; + }); + + if (property) { + return property; + } + } + + throw error('unrecognized element <' + nameNs.name + '>'); + }; + + ElementHandler.prototype.toString = function() { + return 'ElementDescriptor[' + getModdleDescriptor(this.type).name + ']'; + }; + + ElementHandler.prototype.valueHandler = function(propertyDesc, element) { + return new ValueHandler(propertyDesc, element); + }; + + ElementHandler.prototype.referenceHandler = function(propertyDesc) { + return new ReferenceHandler(propertyDesc, this.context); + }; + + ElementHandler.prototype.handler = function(type) { + if (type === 'Element') { + return new GenericElementHandler(this.model, type, this.context); + } else { + return new ElementHandler(this.model, type, this.context); + } + }; + + /** + * Handle the child element parsing + * + * @param {Element} node the xml node + */ + ElementHandler.prototype.handleChild = function(node) { + var propertyDesc, type, element, childHandler; + + propertyDesc = this.getPropertyForNode(node); + element = this.element; + + type = propertyDesc.effectiveType || propertyDesc.type; + + if (isSimple(type)) { + return this.valueHandler(propertyDesc, element); + } + + if (propertyDesc.isReference) { + childHandler = this.referenceHandler(propertyDesc).handleNode(node); + } else { + childHandler = this.handler(type).handleNode(node); + } + + var newElement = childHandler.element; + + // child handles may decide to skip elements + // by not returning anything + if (newElement !== undefined) { + + if (propertyDesc.isMany) { + element.get(propertyDesc.name).push(newElement); + } else { + element.set(propertyDesc.name, newElement); + } + + if (propertyDesc.isReference) { + assign(newElement, { + element: element + }); + + this.context.addReference(newElement); + } else { + + // establish child -> parent relationship + newElement.$parent = element; + } + } + + return childHandler; + }; + + /** + * An element handler that performs special validation + * to ensure the node it gets initialized with matches + * the handlers type (namespace wise). + * + * @param {Moddle} model + * @param {String} typeName + * @param {Context} context + */ + function RootElementHandler(model, typeName, context) { + ElementHandler.call(this, model, typeName, context); + } + + RootElementHandler.prototype = Object.create(ElementHandler.prototype); + + RootElementHandler.prototype.createElement = function(node) { + + var name = node.name, + nameNs = parseName(name), + model = this.model, + type = this.type, + pkg = model.getPackage(nameNs.prefix), + typeName = pkg && aliasToName(nameNs, pkg) || name; + + // verify the correct namespace if we parse + // the first element in the handler tree + // + // this ensures we don't mistakenly import wrong namespace elements + if (!type.hasType(typeName)) { + throw error('unexpected element <' + node.originalName + '>'); + } + + return ElementHandler.prototype.createElement.call(this, node); + }; + + + function GenericElementHandler(model, typeName, context) { + this.model = model; + this.context = context; + } + + GenericElementHandler.prototype = Object.create(BaseElementHandler.prototype); + + GenericElementHandler.prototype.createElement = function(node) { + + var name = node.name, + ns = parseName(name), + prefix = ns.prefix, + uri = node.ns[prefix + '$uri'], + attributes = node.attributes; + + return this.model.createAny(name, uri, attributes); + }; + + GenericElementHandler.prototype.handleChild = function(node) { + + var handler = new GenericElementHandler(this.model, 'Element', this.context).handleNode(node), + element = this.element; + + var newElement = handler.element, + children; + + if (newElement !== undefined) { + children = element.$children = element.$children || []; + children.push(newElement); + + // establish child -> parent relationship + newElement.$parent = element; + } + + return handler; + }; + + GenericElementHandler.prototype.handleEnd = function() { + if (this.body) { + this.element.$body = this.body; + } + }; + + /** + * A reader for a meta-model + * + * @param {Object} options + * @param {Model} options.model used to read xml files + * @param {Boolean} options.lax whether to make parse errors warnings + */ + function Reader(options) { + + if (options instanceof Moddle) { + options = { + model: options + }; + } + + assign(this, { lax: false }, options); + } + + /** + * The fromXML result. + * + * @typedef {Object} ParseResult + * + * @property {ModdleElement} rootElement + * @property {Array} references + * @property {Array} warnings + * @property {Object} elementsById - a mapping containing each ID -> ModdleElement + */ + + /** + * The fromXML result. + * + * @typedef {Error} ParseError + * + * @property {Array} warnings + */ + + /** + * Parse the given XML into a moddle document tree. + * + * @param {String} xml + * @param {ElementHandler|Object} options or rootHandler + * + * @returns {Promise} + */ + Reader.prototype.fromXML = function(xml, options, done) { + + var rootHandler = options.rootHandler; + + if (options instanceof ElementHandler) { + + // root handler passed via (xml, { rootHandler: ElementHandler }, ...) + rootHandler = options; + options = {}; + } else { + if (typeof options === 'string') { + + // rootHandler passed via (xml, 'someString', ...) + rootHandler = this.handler(options); + options = {}; + } else if (typeof rootHandler === 'string') { + + // rootHandler passed via (xml, { rootHandler: 'someString' }, ...) + rootHandler = this.handler(rootHandler); + } + } + + var model = this.model, + lax = this.lax; + + var context = new Context(assign({}, options, { rootHandler: rootHandler })), + parser = new Parser({ proxy: true }), + stack = createStack(); + + rootHandler.context = context; + + // push root handler + stack.push(rootHandler); + + + /** + * Handle error. + * + * @param {Error} err + * @param {Function} getContext + * @param {boolean} lax + * + * @return {boolean} true if handled + */ + function handleError(err, getContext, lax) { + + var ctx = getContext(); + + var line = ctx.line, + column = ctx.column, + data = ctx.data; + + // we receive the full context data here, + // for elements trim down the information + // to the tag name, only + if (data.charAt(0) === '<' && data.indexOf(' ') !== -1) { + data = data.slice(0, data.indexOf(' ')) + '>'; + } + + var message = + 'unparsable content ' + (data ? data + ' ' : '') + 'detected\n\t' + + 'line: ' + line + '\n\t' + + 'column: ' + column + '\n\t' + + 'nested error: ' + err.message; + + if (lax) { + context.addWarning({ + message: message, + error: err + }); + + return true; + } else { + throw error(message); + } + } + + function handleWarning(err, getContext) { + + // just like handling errors in mode + return handleError(err, getContext, true); + } + + /** + * Resolve collected references on parse end. + */ + function resolveReferences() { + + var elementsById = context.elementsById; + var references = context.references; + + var i, r; + + for (i = 0; (r = references[i]); i++) { + var element = r.element; + var reference = elementsById[r.id]; + var property = getModdleDescriptor(element).propertiesByName[r.property]; + + if (!reference) { + context.addWarning({ + message: 'unresolved reference <' + r.id + '>', + element: r.element, + property: r.property, + value: r.id + }); + } + + if (property.isMany) { + var collection = element.get(property.name), + idx = collection.indexOf(r); + + // we replace an existing place holder (idx != -1) or + // append to the collection instead + if (idx === -1) { + idx = collection.length; + } + + if (!reference) { + + // remove unresolvable reference + collection.splice(idx, 1); + } else { + + // add or update reference in collection + collection[idx] = reference; + } + } else { + element.set(property.name, reference); + } + } + } + + function handleClose() { + stack.pop().handleEnd(); + } + + var PREAMBLE_START_PATTERN = /^<\?xml /i; + + var ENCODING_PATTERN = / encoding="([^"]+)"/i; + + var UTF_8_PATTERN = /^utf-8$/i; + + function handleQuestion(question) { + + if (!PREAMBLE_START_PATTERN.test(question)) { + return; + } + + var match = ENCODING_PATTERN.exec(question); + var encoding = match && match[1]; + + if (!encoding || UTF_8_PATTERN.test(encoding)) { + return; + } + + context.addWarning({ + message: + 'unsupported document encoding <' + encoding + '>, ' + + 'falling back to UTF-8' + }); + } + + function handleOpen(node, getContext) { + var handler = stack.peek(); + + try { + stack.push(handler.handleNode(node)); + } catch (err) { + + if (handleError(err, getContext, lax)) { + stack.push(new NoopHandler()); + } + } + } + + function handleCData(text, getContext) { + + try { + stack.peek().handleText(text); + } catch (err) { + handleWarning(err, getContext); + } + } + + function handleText(text, getContext) { + + // strip whitespace only nodes, i.e. before + // sections and in between tags + + if (!text.trim()) { + return; + } + + handleCData(text, getContext); + } + + var uriMap = model.getPackages().reduce(function(uriMap, p) { + uriMap[p.uri] = p.prefix; + + return uriMap; + }, { + 'http://www.w3.org/XML/1998/namespace': 'xml' // add default xml ns + }); + parser + .ns(uriMap) + .on('openTag', function(obj, decodeStr, selfClosing, getContext) { + + // gracefully handle unparsable attributes (attrs=false) + var attrs = obj.attrs || {}; + + var decodedAttrs = Object.keys(attrs).reduce(function(d, key) { + var value = decodeStr(attrs[key]); + + d[key] = value; + + return d; + }, {}); + + var node = { + name: obj.name, + originalName: obj.originalName, + attributes: decodedAttrs, + ns: obj.ns + }; + + handleOpen(node, getContext); + }) + .on('question', handleQuestion) + .on('closeTag', handleClose) + .on('cdata', handleCData) + .on('text', function(text, decodeEntities, getContext) { + handleText(decodeEntities(text), getContext); + }) + .on('error', handleError) + .on('warn', handleWarning); + + // async XML parsing to make sure the execution environment + // (node or brower) is kept responsive and that certain optimization + // strategies can kick in. + return new Promise(function(resolve, reject) { + + var err; + + try { + parser.parse(xml); + + resolveReferences(); + } catch (e) { + err = e; + } + + var rootElement = rootHandler.element; + + if (!err && !rootElement) { + err = error('failed to parse document as <' + rootHandler.type.$descriptor.name + '>'); + } + + var warnings = context.warnings; + var references = context.references; + var elementsById = context.elementsById; + + if (err) { + err.warnings = warnings; + + return reject(err); + } else { + return resolve({ + rootElement: rootElement, + elementsById: elementsById, + references: references, + warnings: warnings + }); + } + }); + }; + + Reader.prototype.handler = function(name) { + return new RootElementHandler(this.model, name); + }; + + + // helpers ////////////////////////// + + function createStack() { + var stack = []; + + Object.defineProperty(stack, 'peek', { + value: function() { + return this[this.length - 1]; + } + }); + + return stack; + } + + var XML_PREAMBLE = '\n'; + + var ESCAPE_ATTR_CHARS = /<|>|'|"|&|\n\r|\n/g; + var ESCAPE_CHARS = /<|>|&/g; + + + function Namespaces(parent) { + + var prefixMap = {}; + var uriMap = {}; + var used = {}; + + var wellknown = []; + var custom = []; + + // API + + this.byUri = function(uri) { + return uriMap[uri] || ( + parent && parent.byUri(uri) + ); + }; + + this.add = function(ns, isWellknown) { + + uriMap[ns.uri] = ns; + + if (isWellknown) { + wellknown.push(ns); + } else { + custom.push(ns); + } + + this.mapPrefix(ns.prefix, ns.uri); + }; + + this.uriByPrefix = function(prefix) { + return prefixMap[prefix || 'xmlns']; + }; + + this.mapPrefix = function(prefix, uri) { + prefixMap[prefix || 'xmlns'] = uri; + }; + + this.getNSKey = function(ns) { + return (ns.prefix !== undefined) ? (ns.uri + '|' + ns.prefix) : ns.uri; + }; + + this.logUsed = function(ns) { + + var uri = ns.uri; + var nsKey = this.getNSKey(ns); + + used[nsKey] = this.byUri(uri); + + // Inform parent recursively about the usage of this NS + if (parent) { + parent.logUsed(ns); + } + }; + + this.getUsed = function(ns) { + + function isUsed(ns) { + var nsKey = self.getNSKey(ns); + + return used[nsKey]; + } + + var self = this; + + var allNs = [].concat(wellknown, custom); + + return allNs.filter(isUsed); + }; + + } + + function lower(string) { + return string.charAt(0).toLowerCase() + string.slice(1); + } + + function nameToAlias(name, pkg) { + if (hasLowerCaseAlias(pkg)) { + return lower(name); + } else { + return name; + } + } + + function inherits(ctor, superCtor) { + ctor.super_ = superCtor; + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + } + + function nsName(ns) { + if (isString(ns)) { + return ns; + } else { + return (ns.prefix ? ns.prefix + ':' : '') + ns.localName; + } + } + + function getNsAttrs(namespaces) { + + return namespaces.getUsed().filter(function(ns) { + + // do not serialize built in namespace + return ns.prefix !== 'xml'; + }).map(function(ns) { + var name = 'xmlns' + (ns.prefix ? ':' + ns.prefix : ''); + return { name: name, value: ns.uri }; + }); + + } + + function getElementNs(ns, descriptor) { + if (descriptor.isGeneric) { + return assign({ localName: descriptor.ns.localName }, ns); + } else { + return assign({ localName: nameToAlias(descriptor.ns.localName, descriptor.$pkg) }, ns); + } + } + + function getPropertyNs(ns, descriptor) { + return assign({ localName: descriptor.ns.localName }, ns); + } + + function getSerializableProperties(element) { + var descriptor = element.$descriptor; + + return filter(descriptor.properties, function(p) { + var name = p.name; + + if (p.isVirtual) { + return false; + } + + // do not serialize defaults + if (!has$1(element, name)) { + return false; + } + + var value = element[name]; + + // do not serialize default equals + if (value === p.default) { + return false; + } + + // do not serialize null properties + if (value === null) { + return false; + } + + return p.isMany ? value.length : true; + }); + } + + var ESCAPE_ATTR_MAP = { + '\n': '#10', + '\n\r': '#10', + '"': '#34', + '\'': '#39', + '<': '#60', + '>': '#62', + '&': '#38' + }; + + var ESCAPE_MAP = { + '<': 'lt', + '>': 'gt', + '&': 'amp' + }; + + function escape(str, charPattern, replaceMap) { + + // ensure we are handling strings here + str = isString(str) ? str : '' + str; + + return str.replace(charPattern, function(s) { + return '&' + replaceMap[s] + ';'; + }); + } + + /** + * Escape a string attribute to not contain any bad values (line breaks, '"', ...) + * + * @param {String} str the string to escape + * @return {String} the escaped string + */ + function escapeAttr(str) { + return escape(str, ESCAPE_ATTR_CHARS, ESCAPE_ATTR_MAP); + } + + function escapeBody(str) { + return escape(str, ESCAPE_CHARS, ESCAPE_MAP); + } + + function filterAttributes(props) { + return filter(props, function(p) { return p.isAttr; }); + } + + function filterContained(props) { + return filter(props, function(p) { return !p.isAttr; }); + } + + + function ReferenceSerializer(tagName) { + this.tagName = tagName; + } + + ReferenceSerializer.prototype.build = function(element) { + this.element = element; + return this; + }; + + ReferenceSerializer.prototype.serializeTo = function(writer) { + writer + .appendIndent() + .append('<' + this.tagName + '>' + this.element.id + '') + .appendNewLine(); + }; + + function BodySerializer() {} + + BodySerializer.prototype.serializeValue = + BodySerializer.prototype.serializeTo = function(writer) { + writer.append( + this.escape + ? escapeBody(this.value) + : this.value + ); + }; + + BodySerializer.prototype.build = function(prop, value) { + this.value = value; + + if (prop.type === 'String' && value.search(ESCAPE_CHARS) !== -1) { + this.escape = true; + } + + return this; + }; + + function ValueSerializer(tagName) { + this.tagName = tagName; + } + + inherits(ValueSerializer, BodySerializer); + + ValueSerializer.prototype.serializeTo = function(writer) { + + writer + .appendIndent() + .append('<' + this.tagName + '>'); + + this.serializeValue(writer); + + writer + .append('') + .appendNewLine(); + }; + + function ElementSerializer(parent, propertyDescriptor) { + this.body = []; + this.attrs = []; + + this.parent = parent; + this.propertyDescriptor = propertyDescriptor; + } + + ElementSerializer.prototype.build = function(element) { + this.element = element; + + var elementDescriptor = element.$descriptor, + propertyDescriptor = this.propertyDescriptor; + + var otherAttrs, + properties; + + var isGeneric = elementDescriptor.isGeneric; + + if (isGeneric) { + otherAttrs = this.parseGeneric(element); + } else { + otherAttrs = this.parseNsAttributes(element); + } + + if (propertyDescriptor) { + this.ns = this.nsPropertyTagName(propertyDescriptor); + } else { + this.ns = this.nsTagName(elementDescriptor); + } + + // compute tag name + this.tagName = this.addTagName(this.ns); + + if (!isGeneric) { + properties = getSerializableProperties(element); + + this.parseAttributes(filterAttributes(properties)); + this.parseContainments(filterContained(properties)); + } + + this.parseGenericAttributes(element, otherAttrs); + + return this; + }; + + ElementSerializer.prototype.nsTagName = function(descriptor) { + var effectiveNs = this.logNamespaceUsed(descriptor.ns); + return getElementNs(effectiveNs, descriptor); + }; + + ElementSerializer.prototype.nsPropertyTagName = function(descriptor) { + var effectiveNs = this.logNamespaceUsed(descriptor.ns); + return getPropertyNs(effectiveNs, descriptor); + }; + + ElementSerializer.prototype.isLocalNs = function(ns) { + return ns.uri === this.ns.uri; + }; + + /** + * Get the actual ns attribute name for the given element. + * + * @param {Object} element + * @param {Boolean} [element.inherited=false] + * + * @return {Object} nsName + */ + ElementSerializer.prototype.nsAttributeName = function(element) { + + var ns; + + if (isString(element)) { + ns = parseName(element); + } else { + ns = element.ns; + } + + // return just local name for inherited attributes + if (element.inherited) { + return { localName: ns.localName }; + } + + // parse + log effective ns + var effectiveNs = this.logNamespaceUsed(ns); + + // LOG ACTUAL namespace use + this.getNamespaces().logUsed(effectiveNs); + + // strip prefix if same namespace like parent + if (this.isLocalNs(effectiveNs)) { + return { localName: ns.localName }; + } else { + return assign({ localName: ns.localName }, effectiveNs); + } + }; + + ElementSerializer.prototype.parseGeneric = function(element) { + + var self = this, + body = this.body; + + var attributes = []; + + forEach$1(element, function(val, key) { + + var nonNsAttr; + + if (key === '$body') { + body.push(new BodySerializer().build({ type: 'String' }, val)); + } else + if (key === '$children') { + forEach$1(val, function(child) { + body.push(new ElementSerializer(self).build(child)); + }); + } else + if (key.indexOf('$') !== 0) { + nonNsAttr = self.parseNsAttribute(element, key, val); + + if (nonNsAttr) { + attributes.push({ name: key, value: val }); + } + } + }); + + return attributes; + }; + + ElementSerializer.prototype.parseNsAttribute = function(element, name, value) { + var model = element.$model; + + var nameNs = parseName(name); + + var ns; + + // parse xmlns:foo="http://foo.bar" + if (nameNs.prefix === 'xmlns') { + ns = { prefix: nameNs.localName, uri: value }; + } + + // parse xmlns="http://foo.bar" + if (!nameNs.prefix && nameNs.localName === 'xmlns') { + ns = { uri: value }; + } + + if (!ns) { + return { + name: name, + value: value + }; + } + + if (model && model.getPackage(value)) { + + // register well known namespace + this.logNamespace(ns, true, true); + } else { + + // log custom namespace directly as used + var actualNs = this.logNamespaceUsed(ns, true); + + this.getNamespaces().logUsed(actualNs); + } + }; + + + /** + * Parse namespaces and return a list of left over generic attributes + * + * @param {Object} element + * @return {Array} + */ + ElementSerializer.prototype.parseNsAttributes = function(element, attrs) { + var self = this; + + var genericAttrs = element.$attrs; + + var attributes = []; + + // parse namespace attributes first + // and log them. push non namespace attributes to a list + // and process them later + forEach$1(genericAttrs, function(value, name) { + + var nonNsAttr = self.parseNsAttribute(element, name, value); + + if (nonNsAttr) { + attributes.push(nonNsAttr); + } + }); + + return attributes; + }; + + ElementSerializer.prototype.parseGenericAttributes = function(element, attributes) { + + var self = this; + + forEach$1(attributes, function(attr) { + + // do not serialize xsi:type attribute + // it is set manually based on the actual implementation type + if (attr.name === XSI_TYPE) { + return; + } + + try { + self.addAttribute(self.nsAttributeName(attr.name), attr.value); + } catch (e) { + console.warn( + 'missing namespace information for ', + attr.name, '=', attr.value, 'on', element, + e); + } + }); + }; + + ElementSerializer.prototype.parseContainments = function(properties) { + + var self = this, + body = this.body, + element = this.element; + + forEach$1(properties, function(p) { + var value = element.get(p.name), + isReference = p.isReference, + isMany = p.isMany; + + if (!isMany) { + value = [ value ]; + } + + if (p.isBody) { + body.push(new BodySerializer().build(p, value[0])); + } else + if (isSimple(p.type)) { + forEach$1(value, function(v) { + body.push(new ValueSerializer(self.addTagName(self.nsPropertyTagName(p))).build(p, v)); + }); + } else + if (isReference) { + forEach$1(value, function(v) { + body.push(new ReferenceSerializer(self.addTagName(self.nsPropertyTagName(p))).build(v)); + }); + } else { + + // allow serialization via type + // rather than element name + var asType = serializeAsType(p), + asProperty = serializeAsProperty(p); + + forEach$1(value, function(v) { + var serializer; + + if (asType) { + serializer = new TypeSerializer(self, p); + } else + if (asProperty) { + serializer = new ElementSerializer(self, p); + } else { + serializer = new ElementSerializer(self); + } + + body.push(serializer.build(v)); + }); + } + }); + }; + + ElementSerializer.prototype.getNamespaces = function(local) { + + var namespaces = this.namespaces, + parent = this.parent, + parentNamespaces; + + if (!namespaces) { + parentNamespaces = parent && parent.getNamespaces(); + + if (local || !parentNamespaces) { + this.namespaces = namespaces = new Namespaces(parentNamespaces); + } else { + namespaces = parentNamespaces; + } + } + + return namespaces; + }; + + ElementSerializer.prototype.logNamespace = function(ns, wellknown, local) { + var namespaces = this.getNamespaces(local); + + var nsUri = ns.uri, + nsPrefix = ns.prefix; + + var existing = namespaces.byUri(nsUri); + + if (!existing || local) { + namespaces.add(ns, wellknown); + } + + namespaces.mapPrefix(nsPrefix, nsUri); + + return ns; + }; + + ElementSerializer.prototype.logNamespaceUsed = function(ns, local) { + var element = this.element, + model = element.$model, + namespaces = this.getNamespaces(local); + + // ns may be + // + // * prefix only + // * prefix:uri + // * localName only + + var prefix = ns.prefix, + uri = ns.uri, + newPrefix, idx, + wellknownUri; + + // handle anonymous namespaces (elementForm=unqualified), cf. #23 + if (!prefix && !uri) { + return { localName: ns.localName }; + } + + wellknownUri = DEFAULT_NS_MAP[prefix] || model && (model.getPackage(prefix) || {}).uri; + + uri = uri || wellknownUri || namespaces.uriByPrefix(prefix); + + if (!uri) { + throw new Error('no namespace uri given for prefix <' + prefix + '>'); + } + + ns = namespaces.byUri(uri); + + if (!ns) { + newPrefix = prefix; + idx = 1; + + // find a prefix that is not mapped yet + while (namespaces.uriByPrefix(newPrefix)) { + newPrefix = prefix + '_' + idx++; + } + + ns = this.logNamespace({ prefix: newPrefix, uri: uri }, wellknownUri === uri); + } + + if (prefix) { + namespaces.mapPrefix(prefix, uri); + } + + return ns; + }; + + ElementSerializer.prototype.parseAttributes = function(properties) { + var self = this, + element = this.element; + + forEach$1(properties, function(p) { + + var value = element.get(p.name); + + if (p.isReference) { + + if (!p.isMany) { + value = value.id; + } + else { + var values = []; + forEach$1(value, function(v) { + values.push(v.id); + }); + + // IDREFS is a whitespace-separated list of references. + value = values.join(' '); + } + + } + + self.addAttribute(self.nsAttributeName(p), value); + }); + }; + + ElementSerializer.prototype.addTagName = function(nsTagName) { + var actualNs = this.logNamespaceUsed(nsTagName); + + this.getNamespaces().logUsed(actualNs); + + return nsName(nsTagName); + }; + + ElementSerializer.prototype.addAttribute = function(name, value) { + var attrs = this.attrs; + + if (isString(value)) { + value = escapeAttr(value); + } + + // de-duplicate attributes + // https://github.com/bpmn-io/moddle-xml/issues/66 + var idx = findIndex(attrs, function(element) { + return ( + element.name.localName === name.localName && + element.name.uri === name.uri && + element.name.prefix === name.prefix + ); + }); + + var attr = { name: name, value: value }; + + if (idx !== -1) { + attrs.splice(idx, 1, attr); + } else { + attrs.push(attr); + } + }; + + ElementSerializer.prototype.serializeAttributes = function(writer) { + var attrs = this.attrs, + namespaces = this.namespaces; + + if (namespaces) { + attrs = getNsAttrs(namespaces).concat(attrs); + } + + forEach$1(attrs, function(a) { + writer + .append(' ') + .append(nsName(a.name)).append('="').append(a.value).append('"'); + }); + }; + + ElementSerializer.prototype.serializeTo = function(writer) { + var firstBody = this.body[0], + indent = firstBody && firstBody.constructor !== BodySerializer; + + writer + .appendIndent() + .append('<' + this.tagName); + + this.serializeAttributes(writer); + + writer.append(firstBody ? '>' : ' />'); + + if (firstBody) { + + if (indent) { + writer + .appendNewLine() + .indent(); + } + + forEach$1(this.body, function(b) { + b.serializeTo(writer); + }); + + if (indent) { + writer + .unindent() + .appendIndent(); + } + + writer.append(''); + } + + writer.appendNewLine(); + }; + + /** + * A serializer for types that handles serialization of data types + */ + function TypeSerializer(parent, propertyDescriptor) { + ElementSerializer.call(this, parent, propertyDescriptor); + } + + inherits(TypeSerializer, ElementSerializer); + + TypeSerializer.prototype.parseNsAttributes = function(element) { + + // extracted attributes + var attributes = ElementSerializer.prototype.parseNsAttributes.call(this, element); + + var descriptor = element.$descriptor; + + // only serialize xsi:type if necessary + if (descriptor.name === this.propertyDescriptor.type) { + return attributes; + } + + var typeNs = this.typeNs = this.nsTagName(descriptor); + this.getNamespaces().logUsed(this.typeNs); + + // add xsi:type attribute to represent the elements + // actual type + + var pkg = element.$model.getPackage(typeNs.uri), + typePrefix = (pkg.xml && pkg.xml.typePrefix) || ''; + + this.addAttribute( + this.nsAttributeName(XSI_TYPE), + (typeNs.prefix ? typeNs.prefix + ':' : '') + typePrefix + descriptor.ns.localName + ); + + return attributes; + }; + + TypeSerializer.prototype.isLocalNs = function(ns) { + return ns.uri === (this.typeNs || this.ns).uri; + }; + + function SavingWriter() { + this.value = ''; + + this.write = function(str) { + this.value += str; + }; + } + + function FormatingWriter(out, format) { + + var indent = ['']; + + this.append = function(str) { + out.write(str); + + return this; + }; + + this.appendNewLine = function() { + if (format) { + out.write('\n'); + } + + return this; + }; + + this.appendIndent = function() { + if (format) { + out.write(indent.join(' ')); + } + + return this; + }; + + this.indent = function() { + indent.push(''); + return this; + }; + + this.unindent = function() { + indent.pop(); + return this; + }; + } + + /** + * A writer for meta-model backed document trees + * + * @param {Object} options output options to pass into the writer + */ + function Writer(options) { + + options = assign({ format: false, preamble: true }, options || {}); + + function toXML(tree, writer) { + var internalWriter = writer || new SavingWriter(); + var formatingWriter = new FormatingWriter(internalWriter, options.format); + + if (options.preamble) { + formatingWriter.append(XML_PREAMBLE); + } + + new ElementSerializer().build(tree).serializeTo(formatingWriter); + + if (!writer) { + return internalWriter.value; + } + } + + return { + toXML: toXML + }; + } + + /** + * A sub class of {@link Moddle} with support for import and export of BPMN 2.0 xml files. + * + * @class BpmnModdle + * @extends Moddle + * + * @param {Object|Array} packages to use for instantiating the model + * @param {Object} [options] additional options to pass over + */ + function BpmnModdle(packages, options) { + Moddle.call(this, packages, options); + } + + BpmnModdle.prototype = Object.create(Moddle.prototype); + + /** + * The fromXML result. + * + * @typedef {Object} ParseResult + * + * @property {ModdleElement} rootElement + * @property {Array} references + * @property {Array} warnings + * @property {Object} elementsById - a mapping containing each ID -> ModdleElement + */ + + /** + * The fromXML error. + * + * @typedef {Error} ParseError + * + * @property {Array} warnings + */ + + /** + * Instantiates a BPMN model tree from a given xml string. + * + * @param {String} xmlStr + * @param {String} [typeName='bpmn:Definitions'] name of the root element + * @param {Object} [options] options to pass to the underlying reader + * + * @returns {Promise} + */ + BpmnModdle.prototype.fromXML = function(xmlStr, typeName, options) { + + if (!isString(typeName)) { + options = typeName; + typeName = 'bpmn:Definitions'; + } + + var reader = new Reader(assign({ model: this, lax: true }, options)); + var rootHandler = reader.handler(typeName); + + return reader.fromXML(xmlStr, rootHandler); + }; + + + /** + * The toXML result. + * + * @typedef {Object} SerializationResult + * + * @property {String} xml + */ + + /** + * Serializes a BPMN 2.0 object tree to XML. + * + * @param {String} element the root element, typically an instance of `bpmn:Definitions` + * @param {Object} [options] to pass to the underlying writer + * + * @returns {Promise} + */ + BpmnModdle.prototype.toXML = function(element, options) { + + var writer = new Writer(options); + + return new Promise(function(resolve, reject) { + try { + var result = writer.toXML(element); + + return resolve({ + xml: result + }); + } catch (err) { + return reject(err); + } + }); + }; + + var name$5 = "BPMN20"; + var uri$5 = "http://www.omg.org/spec/BPMN/20100524/MODEL"; + var prefix$5 = "bpmn"; + var associations$5 = [ + ]; + var types$5 = [ + { + name: "Interface", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "operations", + type: "Operation", + isMany: true + }, + { + name: "implementationRef", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Operation", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "inMessageRef", + type: "Message", + isReference: true + }, + { + name: "outMessageRef", + type: "Message", + isReference: true + }, + { + name: "errorRef", + type: "Error", + isMany: true, + isReference: true + }, + { + name: "implementationRef", + isAttr: true, + type: "String" + } + ] + }, + { + name: "EndPoint", + superClass: [ + "RootElement" + ] + }, + { + name: "Auditing", + superClass: [ + "BaseElement" + ] + }, + { + name: "GlobalTask", + superClass: [ + "CallableElement" + ], + properties: [ + { + name: "resources", + type: "ResourceRole", + isMany: true + } + ] + }, + { + name: "Monitoring", + superClass: [ + "BaseElement" + ] + }, + { + name: "Performer", + superClass: [ + "ResourceRole" + ] + }, + { + name: "Process", + superClass: [ + "FlowElementsContainer", + "CallableElement" + ], + properties: [ + { + name: "processType", + type: "ProcessType", + isAttr: true + }, + { + name: "isClosed", + isAttr: true, + type: "Boolean" + }, + { + name: "auditing", + type: "Auditing" + }, + { + name: "monitoring", + type: "Monitoring" + }, + { + name: "properties", + type: "Property", + isMany: true + }, + { + name: "laneSets", + isMany: true, + replaces: "FlowElementsContainer#laneSets", + type: "LaneSet" + }, + { + name: "flowElements", + isMany: true, + replaces: "FlowElementsContainer#flowElements", + type: "FlowElement" + }, + { + name: "artifacts", + type: "Artifact", + isMany: true + }, + { + name: "resources", + type: "ResourceRole", + isMany: true + }, + { + name: "correlationSubscriptions", + type: "CorrelationSubscription", + isMany: true + }, + { + name: "supports", + type: "Process", + isMany: true, + isReference: true + }, + { + name: "definitionalCollaborationRef", + type: "Collaboration", + isAttr: true, + isReference: true + }, + { + name: "isExecutable", + isAttr: true, + type: "Boolean" + } + ] + }, + { + name: "LaneSet", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "lanes", + type: "Lane", + isMany: true + }, + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Lane", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "partitionElementRef", + type: "BaseElement", + isAttr: true, + isReference: true + }, + { + name: "partitionElement", + type: "BaseElement" + }, + { + name: "flowNodeRef", + type: "FlowNode", + isMany: true, + isReference: true + }, + { + name: "childLaneSet", + type: "LaneSet", + xml: { + serialize: "xsi:type" + } + } + ] + }, + { + name: "GlobalManualTask", + superClass: [ + "GlobalTask" + ] + }, + { + name: "ManualTask", + superClass: [ + "Task" + ] + }, + { + name: "UserTask", + superClass: [ + "Task" + ], + properties: [ + { + name: "renderings", + type: "Rendering", + isMany: true + }, + { + name: "implementation", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Rendering", + superClass: [ + "BaseElement" + ] + }, + { + name: "HumanPerformer", + superClass: [ + "Performer" + ] + }, + { + name: "PotentialOwner", + superClass: [ + "HumanPerformer" + ] + }, + { + name: "GlobalUserTask", + superClass: [ + "GlobalTask" + ], + properties: [ + { + name: "implementation", + isAttr: true, + type: "String" + }, + { + name: "renderings", + type: "Rendering", + isMany: true + } + ] + }, + { + name: "Gateway", + isAbstract: true, + superClass: [ + "FlowNode" + ], + properties: [ + { + name: "gatewayDirection", + type: "GatewayDirection", + "default": "Unspecified", + isAttr: true + } + ] + }, + { + name: "EventBasedGateway", + superClass: [ + "Gateway" + ], + properties: [ + { + name: "instantiate", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "eventGatewayType", + type: "EventBasedGatewayType", + isAttr: true, + "default": "Exclusive" + } + ] + }, + { + name: "ComplexGateway", + superClass: [ + "Gateway" + ], + properties: [ + { + name: "activationCondition", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "default", + type: "SequenceFlow", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ExclusiveGateway", + superClass: [ + "Gateway" + ], + properties: [ + { + name: "default", + type: "SequenceFlow", + isAttr: true, + isReference: true + } + ] + }, + { + name: "InclusiveGateway", + superClass: [ + "Gateway" + ], + properties: [ + { + name: "default", + type: "SequenceFlow", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ParallelGateway", + superClass: [ + "Gateway" + ] + }, + { + name: "RootElement", + isAbstract: true, + superClass: [ + "BaseElement" + ] + }, + { + name: "Relationship", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "type", + isAttr: true, + type: "String" + }, + { + name: "direction", + type: "RelationshipDirection", + isAttr: true + }, + { + name: "source", + isMany: true, + isReference: true, + type: "Element" + }, + { + name: "target", + isMany: true, + isReference: true, + type: "Element" + } + ] + }, + { + name: "BaseElement", + isAbstract: true, + properties: [ + { + name: "id", + isAttr: true, + type: "String", + isId: true + }, + { + name: "documentation", + type: "Documentation", + isMany: true + }, + { + name: "extensionDefinitions", + type: "ExtensionDefinition", + isMany: true, + isReference: true + }, + { + name: "extensionElements", + type: "ExtensionElements" + } + ] + }, + { + name: "Extension", + properties: [ + { + name: "mustUnderstand", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "definition", + type: "ExtensionDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ExtensionDefinition", + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "extensionAttributeDefinitions", + type: "ExtensionAttributeDefinition", + isMany: true + } + ] + }, + { + name: "ExtensionAttributeDefinition", + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "type", + isAttr: true, + type: "String" + }, + { + name: "isReference", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "extensionDefinition", + type: "ExtensionDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ExtensionElements", + properties: [ + { + name: "valueRef", + isAttr: true, + isReference: true, + type: "Element" + }, + { + name: "values", + type: "Element", + isMany: true + }, + { + name: "extensionAttributeDefinition", + type: "ExtensionAttributeDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Documentation", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "text", + type: "String", + isBody: true + }, + { + name: "textFormat", + "default": "text/plain", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Event", + isAbstract: true, + superClass: [ + "FlowNode", + "InteractionNode" + ], + properties: [ + { + name: "properties", + type: "Property", + isMany: true + } + ] + }, + { + name: "IntermediateCatchEvent", + superClass: [ + "CatchEvent" + ] + }, + { + name: "IntermediateThrowEvent", + superClass: [ + "ThrowEvent" + ] + }, + { + name: "EndEvent", + superClass: [ + "ThrowEvent" + ] + }, + { + name: "StartEvent", + superClass: [ + "CatchEvent" + ], + properties: [ + { + name: "isInterrupting", + "default": true, + isAttr: true, + type: "Boolean" + } + ] + }, + { + name: "ThrowEvent", + isAbstract: true, + superClass: [ + "Event" + ], + properties: [ + { + name: "dataInputs", + type: "DataInput", + isMany: true + }, + { + name: "dataInputAssociations", + type: "DataInputAssociation", + isMany: true + }, + { + name: "inputSet", + type: "InputSet" + }, + { + name: "eventDefinitions", + type: "EventDefinition", + isMany: true + }, + { + name: "eventDefinitionRef", + type: "EventDefinition", + isMany: true, + isReference: true + } + ] + }, + { + name: "CatchEvent", + isAbstract: true, + superClass: [ + "Event" + ], + properties: [ + { + name: "parallelMultiple", + isAttr: true, + type: "Boolean", + "default": false + }, + { + name: "dataOutputs", + type: "DataOutput", + isMany: true + }, + { + name: "dataOutputAssociations", + type: "DataOutputAssociation", + isMany: true + }, + { + name: "outputSet", + type: "OutputSet" + }, + { + name: "eventDefinitions", + type: "EventDefinition", + isMany: true + }, + { + name: "eventDefinitionRef", + type: "EventDefinition", + isMany: true, + isReference: true + } + ] + }, + { + name: "BoundaryEvent", + superClass: [ + "CatchEvent" + ], + properties: [ + { + name: "cancelActivity", + "default": true, + isAttr: true, + type: "Boolean" + }, + { + name: "attachedToRef", + type: "Activity", + isAttr: true, + isReference: true + } + ] + }, + { + name: "EventDefinition", + isAbstract: true, + superClass: [ + "RootElement" + ] + }, + { + name: "CancelEventDefinition", + superClass: [ + "EventDefinition" + ] + }, + { + name: "ErrorEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "errorRef", + type: "Error", + isAttr: true, + isReference: true + } + ] + }, + { + name: "TerminateEventDefinition", + superClass: [ + "EventDefinition" + ] + }, + { + name: "EscalationEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "escalationRef", + type: "Escalation", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Escalation", + properties: [ + { + name: "structureRef", + type: "ItemDefinition", + isAttr: true, + isReference: true + }, + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "escalationCode", + isAttr: true, + type: "String" + } + ], + superClass: [ + "RootElement" + ] + }, + { + name: "CompensateEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "waitForCompletion", + isAttr: true, + type: "Boolean", + "default": true + }, + { + name: "activityRef", + type: "Activity", + isAttr: true, + isReference: true + } + ] + }, + { + name: "TimerEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "timeDate", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "timeCycle", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "timeDuration", + type: "Expression", + xml: { + serialize: "xsi:type" + } + } + ] + }, + { + name: "LinkEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "target", + type: "LinkEventDefinition", + isAttr: true, + isReference: true + }, + { + name: "source", + type: "LinkEventDefinition", + isMany: true, + isReference: true + } + ] + }, + { + name: "MessageEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "messageRef", + type: "Message", + isAttr: true, + isReference: true + }, + { + name: "operationRef", + type: "Operation", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ConditionalEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "condition", + type: "Expression", + xml: { + serialize: "xsi:type" + } + } + ] + }, + { + name: "SignalEventDefinition", + superClass: [ + "EventDefinition" + ], + properties: [ + { + name: "signalRef", + type: "Signal", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Signal", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "structureRef", + type: "ItemDefinition", + isAttr: true, + isReference: true + }, + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ImplicitThrowEvent", + superClass: [ + "ThrowEvent" + ] + }, + { + name: "DataState", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ItemAwareElement", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "itemSubjectRef", + type: "ItemDefinition", + isAttr: true, + isReference: true + }, + { + name: "dataState", + type: "DataState" + } + ] + }, + { + name: "DataAssociation", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "sourceRef", + type: "ItemAwareElement", + isMany: true, + isReference: true + }, + { + name: "targetRef", + type: "ItemAwareElement", + isReference: true + }, + { + name: "transformation", + type: "FormalExpression", + xml: { + serialize: "property" + } + }, + { + name: "assignment", + type: "Assignment", + isMany: true + } + ] + }, + { + name: "DataInput", + superClass: [ + "ItemAwareElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "isCollection", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "inputSetRef", + type: "InputSet", + isMany: true, + isVirtual: true, + isReference: true + }, + { + name: "inputSetWithOptional", + type: "InputSet", + isMany: true, + isVirtual: true, + isReference: true + }, + { + name: "inputSetWithWhileExecuting", + type: "InputSet", + isMany: true, + isVirtual: true, + isReference: true + } + ] + }, + { + name: "DataOutput", + superClass: [ + "ItemAwareElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "isCollection", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "outputSetRef", + type: "OutputSet", + isMany: true, + isVirtual: true, + isReference: true + }, + { + name: "outputSetWithOptional", + type: "OutputSet", + isMany: true, + isVirtual: true, + isReference: true + }, + { + name: "outputSetWithWhileExecuting", + type: "OutputSet", + isMany: true, + isVirtual: true, + isReference: true + } + ] + }, + { + name: "InputSet", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "dataInputRefs", + type: "DataInput", + isMany: true, + isReference: true + }, + { + name: "optionalInputRefs", + type: "DataInput", + isMany: true, + isReference: true + }, + { + name: "whileExecutingInputRefs", + type: "DataInput", + isMany: true, + isReference: true + }, + { + name: "outputSetRefs", + type: "OutputSet", + isMany: true, + isReference: true + } + ] + }, + { + name: "OutputSet", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "dataOutputRefs", + type: "DataOutput", + isMany: true, + isReference: true + }, + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "inputSetRefs", + type: "InputSet", + isMany: true, + isReference: true + }, + { + name: "optionalOutputRefs", + type: "DataOutput", + isMany: true, + isReference: true + }, + { + name: "whileExecutingOutputRefs", + type: "DataOutput", + isMany: true, + isReference: true + } + ] + }, + { + name: "Property", + superClass: [ + "ItemAwareElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "DataInputAssociation", + superClass: [ + "DataAssociation" + ] + }, + { + name: "DataOutputAssociation", + superClass: [ + "DataAssociation" + ] + }, + { + name: "InputOutputSpecification", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "dataInputs", + type: "DataInput", + isMany: true + }, + { + name: "dataOutputs", + type: "DataOutput", + isMany: true + }, + { + name: "inputSets", + type: "InputSet", + isMany: true + }, + { + name: "outputSets", + type: "OutputSet", + isMany: true + } + ] + }, + { + name: "DataObject", + superClass: [ + "FlowElement", + "ItemAwareElement" + ], + properties: [ + { + name: "isCollection", + "default": false, + isAttr: true, + type: "Boolean" + } + ] + }, + { + name: "InputOutputBinding", + properties: [ + { + name: "inputDataRef", + type: "InputSet", + isAttr: true, + isReference: true + }, + { + name: "outputDataRef", + type: "OutputSet", + isAttr: true, + isReference: true + }, + { + name: "operationRef", + type: "Operation", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Assignment", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "from", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "to", + type: "Expression", + xml: { + serialize: "xsi:type" + } + } + ] + }, + { + name: "DataStore", + superClass: [ + "RootElement", + "ItemAwareElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "capacity", + isAttr: true, + type: "Integer" + }, + { + name: "isUnlimited", + "default": true, + isAttr: true, + type: "Boolean" + } + ] + }, + { + name: "DataStoreReference", + superClass: [ + "ItemAwareElement", + "FlowElement" + ], + properties: [ + { + name: "dataStoreRef", + type: "DataStore", + isAttr: true, + isReference: true + } + ] + }, + { + name: "DataObjectReference", + superClass: [ + "ItemAwareElement", + "FlowElement" + ], + properties: [ + { + name: "dataObjectRef", + type: "DataObject", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ConversationLink", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "sourceRef", + type: "InteractionNode", + isAttr: true, + isReference: true + }, + { + name: "targetRef", + type: "InteractionNode", + isAttr: true, + isReference: true + }, + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ConversationAssociation", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "innerConversationNodeRef", + type: "ConversationNode", + isAttr: true, + isReference: true + }, + { + name: "outerConversationNodeRef", + type: "ConversationNode", + isAttr: true, + isReference: true + } + ] + }, + { + name: "CallConversation", + superClass: [ + "ConversationNode" + ], + properties: [ + { + name: "calledCollaborationRef", + type: "Collaboration", + isAttr: true, + isReference: true + }, + { + name: "participantAssociations", + type: "ParticipantAssociation", + isMany: true + } + ] + }, + { + name: "Conversation", + superClass: [ + "ConversationNode" + ] + }, + { + name: "SubConversation", + superClass: [ + "ConversationNode" + ], + properties: [ + { + name: "conversationNodes", + type: "ConversationNode", + isMany: true + } + ] + }, + { + name: "ConversationNode", + isAbstract: true, + superClass: [ + "InteractionNode", + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "participantRef", + type: "Participant", + isMany: true, + isReference: true + }, + { + name: "messageFlowRefs", + type: "MessageFlow", + isMany: true, + isReference: true + }, + { + name: "correlationKeys", + type: "CorrelationKey", + isMany: true + } + ] + }, + { + name: "GlobalConversation", + superClass: [ + "Collaboration" + ] + }, + { + name: "PartnerEntity", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "participantRef", + type: "Participant", + isMany: true, + isReference: true + } + ] + }, + { + name: "PartnerRole", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "participantRef", + type: "Participant", + isMany: true, + isReference: true + } + ] + }, + { + name: "CorrelationProperty", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "correlationPropertyRetrievalExpression", + type: "CorrelationPropertyRetrievalExpression", + isMany: true + }, + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "type", + type: "ItemDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Error", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "structureRef", + type: "ItemDefinition", + isAttr: true, + isReference: true + }, + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "errorCode", + isAttr: true, + type: "String" + } + ] + }, + { + name: "CorrelationKey", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "correlationPropertyRef", + type: "CorrelationProperty", + isMany: true, + isReference: true + }, + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Expression", + superClass: [ + "BaseElement" + ], + isAbstract: false, + properties: [ + { + name: "body", + isBody: true, + type: "String" + } + ] + }, + { + name: "FormalExpression", + superClass: [ + "Expression" + ], + properties: [ + { + name: "language", + isAttr: true, + type: "String" + }, + { + name: "evaluatesToTypeRef", + type: "ItemDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Message", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "itemRef", + type: "ItemDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ItemDefinition", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "itemKind", + type: "ItemKind", + isAttr: true + }, + { + name: "structureRef", + isAttr: true, + type: "String" + }, + { + name: "isCollection", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "import", + type: "Import", + isAttr: true, + isReference: true + } + ] + }, + { + name: "FlowElement", + isAbstract: true, + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "auditing", + type: "Auditing" + }, + { + name: "monitoring", + type: "Monitoring" + }, + { + name: "categoryValueRef", + type: "CategoryValue", + isMany: true, + isReference: true + } + ] + }, + { + name: "SequenceFlow", + superClass: [ + "FlowElement" + ], + properties: [ + { + name: "isImmediate", + isAttr: true, + type: "Boolean" + }, + { + name: "conditionExpression", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "sourceRef", + type: "FlowNode", + isAttr: true, + isReference: true + }, + { + name: "targetRef", + type: "FlowNode", + isAttr: true, + isReference: true + } + ] + }, + { + name: "FlowElementsContainer", + isAbstract: true, + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "laneSets", + type: "LaneSet", + isMany: true + }, + { + name: "flowElements", + type: "FlowElement", + isMany: true + } + ] + }, + { + name: "CallableElement", + isAbstract: true, + superClass: [ + "RootElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "ioSpecification", + type: "InputOutputSpecification", + xml: { + serialize: "property" + } + }, + { + name: "supportedInterfaceRef", + type: "Interface", + isMany: true, + isReference: true + }, + { + name: "ioBinding", + type: "InputOutputBinding", + isMany: true, + xml: { + serialize: "property" + } + } + ] + }, + { + name: "FlowNode", + isAbstract: true, + superClass: [ + "FlowElement" + ], + properties: [ + { + name: "incoming", + type: "SequenceFlow", + isMany: true, + isReference: true + }, + { + name: "outgoing", + type: "SequenceFlow", + isMany: true, + isReference: true + }, + { + name: "lanes", + type: "Lane", + isMany: true, + isVirtual: true, + isReference: true + } + ] + }, + { + name: "CorrelationPropertyRetrievalExpression", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "messagePath", + type: "FormalExpression" + }, + { + name: "messageRef", + type: "Message", + isAttr: true, + isReference: true + } + ] + }, + { + name: "CorrelationPropertyBinding", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "dataPath", + type: "FormalExpression" + }, + { + name: "correlationPropertyRef", + type: "CorrelationProperty", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Resource", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "resourceParameters", + type: "ResourceParameter", + isMany: true + } + ] + }, + { + name: "ResourceParameter", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "isRequired", + isAttr: true, + type: "Boolean" + }, + { + name: "type", + type: "ItemDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "CorrelationSubscription", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "correlationKeyRef", + type: "CorrelationKey", + isAttr: true, + isReference: true + }, + { + name: "correlationPropertyBinding", + type: "CorrelationPropertyBinding", + isMany: true + } + ] + }, + { + name: "MessageFlow", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "sourceRef", + type: "InteractionNode", + isAttr: true, + isReference: true + }, + { + name: "targetRef", + type: "InteractionNode", + isAttr: true, + isReference: true + }, + { + name: "messageRef", + type: "Message", + isAttr: true, + isReference: true + } + ] + }, + { + name: "MessageFlowAssociation", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "innerMessageFlowRef", + type: "MessageFlow", + isAttr: true, + isReference: true + }, + { + name: "outerMessageFlowRef", + type: "MessageFlow", + isAttr: true, + isReference: true + } + ] + }, + { + name: "InteractionNode", + isAbstract: true, + properties: [ + { + name: "incomingConversationLinks", + type: "ConversationLink", + isMany: true, + isVirtual: true, + isReference: true + }, + { + name: "outgoingConversationLinks", + type: "ConversationLink", + isMany: true, + isVirtual: true, + isReference: true + } + ] + }, + { + name: "Participant", + superClass: [ + "InteractionNode", + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "interfaceRef", + type: "Interface", + isMany: true, + isReference: true + }, + { + name: "participantMultiplicity", + type: "ParticipantMultiplicity" + }, + { + name: "endPointRefs", + type: "EndPoint", + isMany: true, + isReference: true + }, + { + name: "processRef", + type: "Process", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ParticipantAssociation", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "innerParticipantRef", + type: "Participant", + isAttr: true, + isReference: true + }, + { + name: "outerParticipantRef", + type: "Participant", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ParticipantMultiplicity", + properties: [ + { + name: "minimum", + "default": 0, + isAttr: true, + type: "Integer" + }, + { + name: "maximum", + "default": 1, + isAttr: true, + type: "Integer" + } + ], + superClass: [ + "BaseElement" + ] + }, + { + name: "Collaboration", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "isClosed", + isAttr: true, + type: "Boolean" + }, + { + name: "participants", + type: "Participant", + isMany: true + }, + { + name: "messageFlows", + type: "MessageFlow", + isMany: true + }, + { + name: "artifacts", + type: "Artifact", + isMany: true + }, + { + name: "conversations", + type: "ConversationNode", + isMany: true + }, + { + name: "conversationAssociations", + type: "ConversationAssociation" + }, + { + name: "participantAssociations", + type: "ParticipantAssociation", + isMany: true + }, + { + name: "messageFlowAssociations", + type: "MessageFlowAssociation", + isMany: true + }, + { + name: "correlationKeys", + type: "CorrelationKey", + isMany: true + }, + { + name: "choreographyRef", + type: "Choreography", + isMany: true, + isReference: true + }, + { + name: "conversationLinks", + type: "ConversationLink", + isMany: true + } + ] + }, + { + name: "ChoreographyActivity", + isAbstract: true, + superClass: [ + "FlowNode" + ], + properties: [ + { + name: "participantRef", + type: "Participant", + isMany: true, + isReference: true + }, + { + name: "initiatingParticipantRef", + type: "Participant", + isAttr: true, + isReference: true + }, + { + name: "correlationKeys", + type: "CorrelationKey", + isMany: true + }, + { + name: "loopType", + type: "ChoreographyLoopType", + "default": "None", + isAttr: true + } + ] + }, + { + name: "CallChoreography", + superClass: [ + "ChoreographyActivity" + ], + properties: [ + { + name: "calledChoreographyRef", + type: "Choreography", + isAttr: true, + isReference: true + }, + { + name: "participantAssociations", + type: "ParticipantAssociation", + isMany: true + } + ] + }, + { + name: "SubChoreography", + superClass: [ + "ChoreographyActivity", + "FlowElementsContainer" + ], + properties: [ + { + name: "artifacts", + type: "Artifact", + isMany: true + } + ] + }, + { + name: "ChoreographyTask", + superClass: [ + "ChoreographyActivity" + ], + properties: [ + { + name: "messageFlowRef", + type: "MessageFlow", + isMany: true, + isReference: true + } + ] + }, + { + name: "Choreography", + superClass: [ + "Collaboration", + "FlowElementsContainer" + ] + }, + { + name: "GlobalChoreographyTask", + superClass: [ + "Choreography" + ], + properties: [ + { + name: "initiatingParticipantRef", + type: "Participant", + isAttr: true, + isReference: true + } + ] + }, + { + name: "TextAnnotation", + superClass: [ + "Artifact" + ], + properties: [ + { + name: "text", + type: "String" + }, + { + name: "textFormat", + "default": "text/plain", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Group", + superClass: [ + "Artifact" + ], + properties: [ + { + name: "categoryValueRef", + type: "CategoryValue", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Association", + superClass: [ + "Artifact" + ], + properties: [ + { + name: "associationDirection", + type: "AssociationDirection", + isAttr: true + }, + { + name: "sourceRef", + type: "BaseElement", + isAttr: true, + isReference: true + }, + { + name: "targetRef", + type: "BaseElement", + isAttr: true, + isReference: true + } + ] + }, + { + name: "Category", + superClass: [ + "RootElement" + ], + properties: [ + { + name: "categoryValue", + type: "CategoryValue", + isMany: true + }, + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Artifact", + isAbstract: true, + superClass: [ + "BaseElement" + ] + }, + { + name: "CategoryValue", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "categorizedFlowElements", + type: "FlowElement", + isMany: true, + isVirtual: true, + isReference: true + }, + { + name: "value", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Activity", + isAbstract: true, + superClass: [ + "FlowNode" + ], + properties: [ + { + name: "isForCompensation", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "default", + type: "SequenceFlow", + isAttr: true, + isReference: true + }, + { + name: "ioSpecification", + type: "InputOutputSpecification", + xml: { + serialize: "property" + } + }, + { + name: "boundaryEventRefs", + type: "BoundaryEvent", + isMany: true, + isReference: true + }, + { + name: "properties", + type: "Property", + isMany: true + }, + { + name: "dataInputAssociations", + type: "DataInputAssociation", + isMany: true + }, + { + name: "dataOutputAssociations", + type: "DataOutputAssociation", + isMany: true + }, + { + name: "startQuantity", + "default": 1, + isAttr: true, + type: "Integer" + }, + { + name: "resources", + type: "ResourceRole", + isMany: true + }, + { + name: "completionQuantity", + "default": 1, + isAttr: true, + type: "Integer" + }, + { + name: "loopCharacteristics", + type: "LoopCharacteristics" + } + ] + }, + { + name: "ServiceTask", + superClass: [ + "Task" + ], + properties: [ + { + name: "implementation", + isAttr: true, + type: "String" + }, + { + name: "operationRef", + type: "Operation", + isAttr: true, + isReference: true + } + ] + }, + { + name: "SubProcess", + superClass: [ + "Activity", + "FlowElementsContainer", + "InteractionNode" + ], + properties: [ + { + name: "triggeredByEvent", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "artifacts", + type: "Artifact", + isMany: true + } + ] + }, + { + name: "LoopCharacteristics", + isAbstract: true, + superClass: [ + "BaseElement" + ] + }, + { + name: "MultiInstanceLoopCharacteristics", + superClass: [ + "LoopCharacteristics" + ], + properties: [ + { + name: "isSequential", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "behavior", + type: "MultiInstanceBehavior", + "default": "All", + isAttr: true + }, + { + name: "loopCardinality", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "loopDataInputRef", + type: "ItemAwareElement", + isReference: true + }, + { + name: "loopDataOutputRef", + type: "ItemAwareElement", + isReference: true + }, + { + name: "inputDataItem", + type: "DataInput", + xml: { + serialize: "property" + } + }, + { + name: "outputDataItem", + type: "DataOutput", + xml: { + serialize: "property" + } + }, + { + name: "complexBehaviorDefinition", + type: "ComplexBehaviorDefinition", + isMany: true + }, + { + name: "completionCondition", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "oneBehaviorEventRef", + type: "EventDefinition", + isAttr: true, + isReference: true + }, + { + name: "noneBehaviorEventRef", + type: "EventDefinition", + isAttr: true, + isReference: true + } + ] + }, + { + name: "StandardLoopCharacteristics", + superClass: [ + "LoopCharacteristics" + ], + properties: [ + { + name: "testBefore", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "loopCondition", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "loopMaximum", + type: "Integer", + isAttr: true + } + ] + }, + { + name: "CallActivity", + superClass: [ + "Activity", + "InteractionNode" + ], + properties: [ + { + name: "calledElement", + type: "String", + isAttr: true + } + ] + }, + { + name: "Task", + superClass: [ + "Activity", + "InteractionNode" + ] + }, + { + name: "SendTask", + superClass: [ + "Task" + ], + properties: [ + { + name: "implementation", + isAttr: true, + type: "String" + }, + { + name: "operationRef", + type: "Operation", + isAttr: true, + isReference: true + }, + { + name: "messageRef", + type: "Message", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ReceiveTask", + superClass: [ + "Task" + ], + properties: [ + { + name: "implementation", + isAttr: true, + type: "String" + }, + { + name: "instantiate", + "default": false, + isAttr: true, + type: "Boolean" + }, + { + name: "operationRef", + type: "Operation", + isAttr: true, + isReference: true + }, + { + name: "messageRef", + type: "Message", + isAttr: true, + isReference: true + } + ] + }, + { + name: "ScriptTask", + superClass: [ + "Task" + ], + properties: [ + { + name: "scriptFormat", + isAttr: true, + type: "String" + }, + { + name: "script", + type: "String" + } + ] + }, + { + name: "BusinessRuleTask", + superClass: [ + "Task" + ], + properties: [ + { + name: "implementation", + isAttr: true, + type: "String" + } + ] + }, + { + name: "AdHocSubProcess", + superClass: [ + "SubProcess" + ], + properties: [ + { + name: "completionCondition", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "ordering", + type: "AdHocOrdering", + isAttr: true + }, + { + name: "cancelRemainingInstances", + "default": true, + isAttr: true, + type: "Boolean" + } + ] + }, + { + name: "Transaction", + superClass: [ + "SubProcess" + ], + properties: [ + { + name: "protocol", + isAttr: true, + type: "String" + }, + { + name: "method", + isAttr: true, + type: "String" + } + ] + }, + { + name: "GlobalScriptTask", + superClass: [ + "GlobalTask" + ], + properties: [ + { + name: "scriptLanguage", + isAttr: true, + type: "String" + }, + { + name: "script", + isAttr: true, + type: "String" + } + ] + }, + { + name: "GlobalBusinessRuleTask", + superClass: [ + "GlobalTask" + ], + properties: [ + { + name: "implementation", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ComplexBehaviorDefinition", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "condition", + type: "FormalExpression" + }, + { + name: "event", + type: "ImplicitThrowEvent" + } + ] + }, + { + name: "ResourceRole", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "resourceRef", + type: "Resource", + isReference: true + }, + { + name: "resourceParameterBindings", + type: "ResourceParameterBinding", + isMany: true + }, + { + name: "resourceAssignmentExpression", + type: "ResourceAssignmentExpression" + }, + { + name: "name", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ResourceParameterBinding", + properties: [ + { + name: "expression", + type: "Expression", + xml: { + serialize: "xsi:type" + } + }, + { + name: "parameterRef", + type: "ResourceParameter", + isAttr: true, + isReference: true + } + ], + superClass: [ + "BaseElement" + ] + }, + { + name: "ResourceAssignmentExpression", + properties: [ + { + name: "expression", + type: "Expression", + xml: { + serialize: "xsi:type" + } + } + ], + superClass: [ + "BaseElement" + ] + }, + { + name: "Import", + properties: [ + { + name: "importType", + isAttr: true, + type: "String" + }, + { + name: "location", + isAttr: true, + type: "String" + }, + { + name: "namespace", + isAttr: true, + type: "String" + } + ] + }, + { + name: "Definitions", + superClass: [ + "BaseElement" + ], + properties: [ + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "targetNamespace", + isAttr: true, + type: "String" + }, + { + name: "expressionLanguage", + "default": "http://www.w3.org/1999/XPath", + isAttr: true, + type: "String" + }, + { + name: "typeLanguage", + "default": "http://www.w3.org/2001/XMLSchema", + isAttr: true, + type: "String" + }, + { + name: "imports", + type: "Import", + isMany: true + }, + { + name: "extensions", + type: "Extension", + isMany: true + }, + { + name: "rootElements", + type: "RootElement", + isMany: true + }, + { + name: "diagrams", + isMany: true, + type: "bpmndi:BPMNDiagram" + }, + { + name: "exporter", + isAttr: true, + type: "String" + }, + { + name: "relationships", + type: "Relationship", + isMany: true + }, + { + name: "exporterVersion", + isAttr: true, + type: "String" + } + ] + } + ]; + var enumerations$3 = [ + { + name: "ProcessType", + literalValues: [ + { + name: "None" + }, + { + name: "Public" + }, + { + name: "Private" + } + ] + }, + { + name: "GatewayDirection", + literalValues: [ + { + name: "Unspecified" + }, + { + name: "Converging" + }, + { + name: "Diverging" + }, + { + name: "Mixed" + } + ] + }, + { + name: "EventBasedGatewayType", + literalValues: [ + { + name: "Parallel" + }, + { + name: "Exclusive" + } + ] + }, + { + name: "RelationshipDirection", + literalValues: [ + { + name: "None" + }, + { + name: "Forward" + }, + { + name: "Backward" + }, + { + name: "Both" + } + ] + }, + { + name: "ItemKind", + literalValues: [ + { + name: "Physical" + }, + { + name: "Information" + } + ] + }, + { + name: "ChoreographyLoopType", + literalValues: [ + { + name: "None" + }, + { + name: "Standard" + }, + { + name: "MultiInstanceSequential" + }, + { + name: "MultiInstanceParallel" + } + ] + }, + { + name: "AssociationDirection", + literalValues: [ + { + name: "None" + }, + { + name: "One" + }, + { + name: "Both" + } + ] + }, + { + name: "MultiInstanceBehavior", + literalValues: [ + { + name: "None" + }, + { + name: "One" + }, + { + name: "All" + }, + { + name: "Complex" + } + ] + }, + { + name: "AdHocOrdering", + literalValues: [ + { + name: "Parallel" + }, + { + name: "Sequential" + } + ] + } + ]; + var xml$1 = { + tagAlias: "lowerCase", + typePrefix: "t" + }; + var BpmnPackage = { + name: name$5, + uri: uri$5, + prefix: prefix$5, + associations: associations$5, + types: types$5, + enumerations: enumerations$3, + xml: xml$1 + }; + + var name$4 = "BPMNDI"; + var uri$4 = "http://www.omg.org/spec/BPMN/20100524/DI"; + var prefix$4 = "bpmndi"; + var types$4 = [ + { + name: "BPMNDiagram", + properties: [ + { + name: "plane", + type: "BPMNPlane", + redefines: "di:Diagram#rootElement" + }, + { + name: "labelStyle", + type: "BPMNLabelStyle", + isMany: true + } + ], + superClass: [ + "di:Diagram" + ] + }, + { + name: "BPMNPlane", + properties: [ + { + name: "bpmnElement", + isAttr: true, + isReference: true, + type: "bpmn:BaseElement", + redefines: "di:DiagramElement#modelElement" + } + ], + superClass: [ + "di:Plane" + ] + }, + { + name: "BPMNShape", + properties: [ + { + name: "bpmnElement", + isAttr: true, + isReference: true, + type: "bpmn:BaseElement", + redefines: "di:DiagramElement#modelElement" + }, + { + name: "isHorizontal", + isAttr: true, + type: "Boolean" + }, + { + name: "isExpanded", + isAttr: true, + type: "Boolean" + }, + { + name: "isMarkerVisible", + isAttr: true, + type: "Boolean" + }, + { + name: "label", + type: "BPMNLabel" + }, + { + name: "isMessageVisible", + isAttr: true, + type: "Boolean" + }, + { + name: "participantBandKind", + type: "ParticipantBandKind", + isAttr: true + }, + { + name: "choreographyActivityShape", + type: "BPMNShape", + isAttr: true, + isReference: true + } + ], + superClass: [ + "di:LabeledShape" + ] + }, + { + name: "BPMNEdge", + properties: [ + { + name: "label", + type: "BPMNLabel" + }, + { + name: "bpmnElement", + isAttr: true, + isReference: true, + type: "bpmn:BaseElement", + redefines: "di:DiagramElement#modelElement" + }, + { + name: "sourceElement", + isAttr: true, + isReference: true, + type: "di:DiagramElement", + redefines: "di:Edge#source" + }, + { + name: "targetElement", + isAttr: true, + isReference: true, + type: "di:DiagramElement", + redefines: "di:Edge#target" + }, + { + name: "messageVisibleKind", + type: "MessageVisibleKind", + isAttr: true, + "default": "initiating" + } + ], + superClass: [ + "di:LabeledEdge" + ] + }, + { + name: "BPMNLabel", + properties: [ + { + name: "labelStyle", + type: "BPMNLabelStyle", + isAttr: true, + isReference: true, + redefines: "di:DiagramElement#style" + } + ], + superClass: [ + "di:Label" + ] + }, + { + name: "BPMNLabelStyle", + properties: [ + { + name: "font", + type: "dc:Font" + } + ], + superClass: [ + "di:Style" + ] + } + ]; + var enumerations$2 = [ + { + name: "ParticipantBandKind", + literalValues: [ + { + name: "top_initiating" + }, + { + name: "middle_initiating" + }, + { + name: "bottom_initiating" + }, + { + name: "top_non_initiating" + }, + { + name: "middle_non_initiating" + }, + { + name: "bottom_non_initiating" + } + ] + }, + { + name: "MessageVisibleKind", + literalValues: [ + { + name: "initiating" + }, + { + name: "non_initiating" + } + ] + } + ]; + var associations$4 = [ + ]; + var BpmnDiPackage = { + name: name$4, + uri: uri$4, + prefix: prefix$4, + types: types$4, + enumerations: enumerations$2, + associations: associations$4 + }; + + var name$3 = "DC"; + var uri$3 = "http://www.omg.org/spec/DD/20100524/DC"; + var prefix$3 = "dc"; + var types$3 = [ + { + name: "Boolean" + }, + { + name: "Integer" + }, + { + name: "Real" + }, + { + name: "String" + }, + { + name: "Font", + properties: [ + { + name: "name", + type: "String", + isAttr: true + }, + { + name: "size", + type: "Real", + isAttr: true + }, + { + name: "isBold", + type: "Boolean", + isAttr: true + }, + { + name: "isItalic", + type: "Boolean", + isAttr: true + }, + { + name: "isUnderline", + type: "Boolean", + isAttr: true + }, + { + name: "isStrikeThrough", + type: "Boolean", + isAttr: true + } + ] + }, + { + name: "Point", + properties: [ + { + name: "x", + type: "Real", + "default": "0", + isAttr: true + }, + { + name: "y", + type: "Real", + "default": "0", + isAttr: true + } + ] + }, + { + name: "Bounds", + properties: [ + { + name: "x", + type: "Real", + "default": "0", + isAttr: true + }, + { + name: "y", + type: "Real", + "default": "0", + isAttr: true + }, + { + name: "width", + type: "Real", + isAttr: true + }, + { + name: "height", + type: "Real", + isAttr: true + } + ] + } + ]; + var associations$3 = [ + ]; + var DcPackage = { + name: name$3, + uri: uri$3, + prefix: prefix$3, + types: types$3, + associations: associations$3 + }; + + var name$2 = "DI"; + var uri$2 = "http://www.omg.org/spec/DD/20100524/DI"; + var prefix$2 = "di"; + var types$2 = [ + { + name: "DiagramElement", + isAbstract: true, + properties: [ + { + name: "id", + isAttr: true, + isId: true, + type: "String" + }, + { + name: "extension", + type: "Extension" + }, + { + name: "owningDiagram", + type: "Diagram", + isReadOnly: true, + isVirtual: true, + isReference: true + }, + { + name: "owningElement", + type: "DiagramElement", + isReadOnly: true, + isVirtual: true, + isReference: true + }, + { + name: "modelElement", + isReadOnly: true, + isVirtual: true, + isReference: true, + type: "Element" + }, + { + name: "style", + type: "Style", + isReadOnly: true, + isVirtual: true, + isReference: true + }, + { + name: "ownedElement", + type: "DiagramElement", + isReadOnly: true, + isMany: true, + isVirtual: true + } + ] + }, + { + name: "Node", + isAbstract: true, + superClass: [ + "DiagramElement" + ] + }, + { + name: "Edge", + isAbstract: true, + superClass: [ + "DiagramElement" + ], + properties: [ + { + name: "source", + type: "DiagramElement", + isReadOnly: true, + isVirtual: true, + isReference: true + }, + { + name: "target", + type: "DiagramElement", + isReadOnly: true, + isVirtual: true, + isReference: true + }, + { + name: "waypoint", + isUnique: false, + isMany: true, + type: "dc:Point", + xml: { + serialize: "xsi:type" + } + } + ] + }, + { + name: "Diagram", + isAbstract: true, + properties: [ + { + name: "id", + isAttr: true, + isId: true, + type: "String" + }, + { + name: "rootElement", + type: "DiagramElement", + isReadOnly: true, + isVirtual: true + }, + { + name: "name", + isAttr: true, + type: "String" + }, + { + name: "documentation", + isAttr: true, + type: "String" + }, + { + name: "resolution", + isAttr: true, + type: "Real" + }, + { + name: "ownedStyle", + type: "Style", + isReadOnly: true, + isMany: true, + isVirtual: true + } + ] + }, + { + name: "Shape", + isAbstract: true, + superClass: [ + "Node" + ], + properties: [ + { + name: "bounds", + type: "dc:Bounds" + } + ] + }, + { + name: "Plane", + isAbstract: true, + superClass: [ + "Node" + ], + properties: [ + { + name: "planeElement", + type: "DiagramElement", + subsettedProperty: "DiagramElement-ownedElement", + isMany: true + } + ] + }, + { + name: "LabeledEdge", + isAbstract: true, + superClass: [ + "Edge" + ], + properties: [ + { + name: "ownedLabel", + type: "Label", + isReadOnly: true, + subsettedProperty: "DiagramElement-ownedElement", + isMany: true, + isVirtual: true + } + ] + }, + { + name: "LabeledShape", + isAbstract: true, + superClass: [ + "Shape" + ], + properties: [ + { + name: "ownedLabel", + type: "Label", + isReadOnly: true, + subsettedProperty: "DiagramElement-ownedElement", + isMany: true, + isVirtual: true + } + ] + }, + { + name: "Label", + isAbstract: true, + superClass: [ + "Node" + ], + properties: [ + { + name: "bounds", + type: "dc:Bounds" + } + ] + }, + { + name: "Style", + isAbstract: true, + properties: [ + { + name: "id", + isAttr: true, + isId: true, + type: "String" + } + ] + }, + { + name: "Extension", + properties: [ + { + name: "values", + isMany: true, + type: "Element" + } + ] + } + ]; + var associations$2 = [ + ]; + var xml = { + tagAlias: "lowerCase" + }; + var DiPackage = { + name: name$2, + uri: uri$2, + prefix: prefix$2, + types: types$2, + associations: associations$2, + xml: xml + }; + + var name$1 = "bpmn.io colors for BPMN"; + var uri$1 = "http://bpmn.io/schema/bpmn/biocolor/1.0"; + var prefix$1 = "bioc"; + var types$1 = [ + { + name: "ColoredShape", + "extends": [ + "bpmndi:BPMNShape" + ], + properties: [ + { + name: "stroke", + isAttr: true, + type: "String" + }, + { + name: "fill", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ColoredEdge", + "extends": [ + "bpmndi:BPMNEdge" + ], + properties: [ + { + name: "stroke", + isAttr: true, + type: "String" + }, + { + name: "fill", + isAttr: true, + type: "String" + } + ] + } + ]; + var enumerations$1 = [ + ]; + var associations$1 = [ + ]; + var BiocPackage = { + name: name$1, + uri: uri$1, + prefix: prefix$1, + types: types$1, + enumerations: enumerations$1, + associations: associations$1 + }; + + var name = "BPMN in Color"; + var uri = "http://www.omg.org/spec/BPMN/non-normative/color/1.0"; + var prefix = "color"; + var types = [ + { + name: "ColoredLabel", + "extends": [ + "bpmndi:BPMNLabel" + ], + properties: [ + { + name: "color", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ColoredShape", + "extends": [ + "bpmndi:BPMNShape" + ], + properties: [ + { + name: "background-color", + isAttr: true, + type: "String" + }, + { + name: "border-color", + isAttr: true, + type: "String" + } + ] + }, + { + name: "ColoredEdge", + "extends": [ + "bpmndi:BPMNEdge" + ], + properties: [ + { + name: "border-color", + isAttr: true, + type: "String" + } + ] + } + ]; + var enumerations = [ + ]; + var associations = [ + ]; + var BpmnInColorPackage = { + name: name, + uri: uri, + prefix: prefix, + types: types, + enumerations: enumerations, + associations: associations + }; + + var packages = { + bpmn: BpmnPackage, + bpmndi: BpmnDiPackage, + dc: DcPackage, + di: DiPackage, + bioc: BiocPackage, + color: BpmnInColorPackage + }; + + function simple(additionalPackages, options) { + var pks = assign({}, packages, additionalPackages); + + return new BpmnModdle(pks, options); + } + + // TODO(nikku): remove with future bpmn-js version + + /** + * Wraps APIs to check: + * + * 1) If a callback is passed -> Warn users about callback deprecation. + * 2) If Promise class is implemented in current environment. + * + * @private + */ + function wrapForCompatibility(api) { + + return function() { + + if (!window.Promise) { + throw new Error('Promises is not supported in this environment. Please polyfill Promise.'); + } + + var argLen = arguments.length; + if (argLen >= 1 && isFunction(arguments[argLen - 1])) { + + var callback = arguments[argLen - 1]; + + console.warn(new Error( + 'Passing callbacks to ' + api.name + ' is deprecated and will be removed in a future major release. ' + + 'Please switch to promises: https://bpmn.io/l/moving-to-promises.html' + )); + + var argsWithoutCallback = Array.prototype.slice.call(arguments, 0, -1); + + api.apply(this, argsWithoutCallback).then(function(result) { + + var firstKey = Object.keys(result)[0]; + + // The APIs we are wrapping all resolve a single item depending on the API. + // For instance, importXML resolves { warnings } and saveXML returns { xml }. + // That's why we can call the callback with the first item of result. + return callback(null, result[firstKey]); + + // Passing a second paramter instead of catch because we don't want to + // catch errors thrown by callback(). + }, function(err) { + + return callback(err, err.warnings); + }); + } else { + + return api.apply(this, arguments); + } + }; + } + + + // TODO(nikku): remove with future bpmn-js version + + var DI_ERROR_MESSAGE = 'Tried to access di from the businessObject. The di is available through the diagram element only. For more information, see https://github.com/bpmn-io/bpmn-js/issues/1472'; + + function ensureCompatDiRef(businessObject) { + + // bpmnElement can have multiple independent DIs + if (!has$1(businessObject, 'di')) { + Object.defineProperty(businessObject, 'di', { + enumerable: false, + get: function() { + throw new Error(DI_ERROR_MESSAGE); + } + }); + } + } + + /** + * Returns true if an element has the given meta-model type + * + * @param {ModdleElement} element + * @param {string} type + * + * @return {boolean} + */ + function is(element, type) { + return element.$instanceOf(type); + } + + + /** + * Find a suitable display candidate for definitions where the DI does not + * correctly specify one. + */ + function findDisplayCandidate(definitions) { + return find(definitions.rootElements, function(e) { + return is(e, 'bpmn:Process') || is(e, 'bpmn:Collaboration'); + }); + } + + + function BpmnTreeWalker(handler, translate) { + + // list of containers already walked + var handledElements = {}; + + // list of elements to handle deferred to ensure + // prerequisites are drawn + var deferred = []; + + var diMap = {}; + + // Helpers ////////////////////// + + function contextual(fn, ctx) { + return function(e) { + fn(e, ctx); + }; + } + + function handled(element) { + handledElements[element.id] = element; + } + + function isHandled(element) { + return handledElements[element.id]; + } + + function visit(element, ctx) { + + var gfx = element.gfx; + + // avoid multiple rendering of elements + if (gfx) { + throw new Error( + translate('already rendered {element}', { element: elementToString(element) }) + ); + } + + // call handler + return handler.element(element, diMap[element.id], ctx); + } + + function visitRoot(element, diagram) { + return handler.root(element, diMap[element.id], diagram); + } + + function visitIfDi(element, ctx) { + + try { + var gfx = diMap[element.id] && visit(element, ctx); + + handled(element); + + return gfx; + } catch (e) { + logError(e.message, { element: element, error: e }); + + console.error(translate('failed to import {element}', { element: elementToString(element) })); + console.error(e); + } + } + + function logError(message, context) { + handler.error(message, context); + } + + // DI handling ////////////////////// + + function registerDi(di) { + var bpmnElement = di.bpmnElement; + + if (bpmnElement) { + if (diMap[bpmnElement.id]) { + logError( + translate('multiple DI elements defined for {element}', { + element: elementToString(bpmnElement) + }), + { element: bpmnElement } + ); + } else { + diMap[bpmnElement.id] = di; + + ensureCompatDiRef(bpmnElement); + } + } else { + logError( + translate('no bpmnElement referenced in {element}', { + element: elementToString(di) + }), + { element: di } + ); + } + } + + function handleDiagram(diagram) { + handlePlane(diagram.plane); + } + + function handlePlane(plane) { + registerDi(plane); + + forEach$1(plane.planeElement, handlePlaneElement); + } + + function handlePlaneElement(planeElement) { + registerDi(planeElement); + } + + + // Semantic handling ////////////////////// + + /** + * Handle definitions and return the rendered diagram (if any) + * + * @param {ModdleElement} definitions to walk and import + * @param {ModdleElement} [diagram] specific diagram to import and display + * + * @throws {Error} if no diagram to display could be found + */ + function handleDefinitions(definitions, diagram) { + + // make sure we walk the correct bpmnElement + + var diagrams = definitions.diagrams; + + if (diagram && diagrams.indexOf(diagram) === -1) { + throw new Error(translate('diagram not part of bpmn:Definitions')); + } + + if (!diagram && diagrams && diagrams.length) { + diagram = diagrams[0]; + } + + // no diagram -> nothing to import + if (!diagram) { + throw new Error(translate('no diagram to display')); + } + + // load DI from selected diagram only + diMap = {}; + handleDiagram(diagram); + + + var plane = diagram.plane; + + if (!plane) { + throw new Error(translate( + 'no plane for {element}', + { element: elementToString(diagram) } + )); + } + + var rootElement = plane.bpmnElement; + + // ensure we default to a suitable display candidate (process or collaboration), + // even if non is specified in DI + if (!rootElement) { + rootElement = findDisplayCandidate(definitions); + + if (!rootElement) { + throw new Error(translate('no process or collaboration to display')); + } else { + + logError( + translate('correcting missing bpmnElement on {plane} to {rootElement}', { + plane: elementToString(plane), + rootElement: elementToString(rootElement) + }) + ); + + // correct DI on the fly + plane.bpmnElement = rootElement; + registerDi(plane); + } + } + + + var ctx = visitRoot(rootElement, plane); + + if (is(rootElement, 'bpmn:Process') || is(rootElement, 'bpmn:SubProcess')) { + handleProcess(rootElement, ctx); + } else if (is(rootElement, 'bpmn:Collaboration')) { + handleCollaboration(rootElement, ctx); + + // force drawing of everything not yet drawn that is part of the target DI + handleUnhandledProcesses(definitions.rootElements, ctx); + } else { + throw new Error( + translate('unsupported bpmnElement for {plane}: {rootElement}', { + plane: elementToString(plane), + rootElement: elementToString(rootElement) + }) + ); + } + + // handle all deferred elements + handleDeferred(); + } + + function handleDeferred() { + + var fn; + + // drain deferred until empty + while (deferred.length) { + fn = deferred.shift(); + + fn(); + } + } + + function handleProcess(process, context) { + handleFlowElementsContainer(process, context); + handleIoSpecification(process.ioSpecification, context); + + handleArtifacts(process.artifacts, context); + + // log process handled + handled(process); + } + + function handleUnhandledProcesses(rootElements, ctx) { + + // walk through all processes that have not yet been drawn and draw them + // if they contain lanes with DI information. + // we do this to pass the free-floating lane test cases in the MIWG test suite + var processes = filter(rootElements, function(e) { + return !isHandled(e) && is(e, 'bpmn:Process') && e.laneSets; + }); + + processes.forEach(contextual(handleProcess, ctx)); + } + + function handleMessageFlow(messageFlow, context) { + visitIfDi(messageFlow, context); + } + + function handleMessageFlows(messageFlows, context) { + forEach$1(messageFlows, contextual(handleMessageFlow, context)); + } + + function handleDataAssociation(association, context) { + visitIfDi(association, context); + } + + function handleDataInput(dataInput, context) { + visitIfDi(dataInput, context); + } + + function handleDataOutput(dataOutput, context) { + visitIfDi(dataOutput, context); + } + + function handleArtifact(artifact, context) { + + // bpmn:TextAnnotation + // bpmn:Group + // bpmn:Association + + visitIfDi(artifact, context); + } + + function handleArtifacts(artifacts, context) { + + forEach$1(artifacts, function(e) { + if (is(e, 'bpmn:Association')) { + deferred.push(function() { + handleArtifact(e, context); + }); + } else { + handleArtifact(e, context); + } + }); + } + + function handleIoSpecification(ioSpecification, context) { + + if (!ioSpecification) { + return; + } + + forEach$1(ioSpecification.dataInputs, contextual(handleDataInput, context)); + forEach$1(ioSpecification.dataOutputs, contextual(handleDataOutput, context)); + } + + function handleSubProcess(subProcess, context) { + handleFlowElementsContainer(subProcess, context); + handleArtifacts(subProcess.artifacts, context); + } + + function handleFlowNode(flowNode, context) { + var childCtx = visitIfDi(flowNode, context); + + if (is(flowNode, 'bpmn:SubProcess')) { + handleSubProcess(flowNode, childCtx || context); + } + + if (is(flowNode, 'bpmn:Activity')) { + handleIoSpecification(flowNode.ioSpecification, context); + } + + // defer handling of associations + // affected types: + // + // * bpmn:Activity + // * bpmn:ThrowEvent + // * bpmn:CatchEvent + // + deferred.push(function() { + forEach$1(flowNode.dataInputAssociations, contextual(handleDataAssociation, context)); + forEach$1(flowNode.dataOutputAssociations, contextual(handleDataAssociation, context)); + }); + } + + function handleSequenceFlow(sequenceFlow, context) { + visitIfDi(sequenceFlow, context); + } + + function handleDataElement(dataObject, context) { + visitIfDi(dataObject, context); + } + + function handleLane(lane, context) { + + deferred.push(function() { + + var newContext = visitIfDi(lane, context); + + if (lane.childLaneSet) { + handleLaneSet(lane.childLaneSet, newContext || context); + } + + wireFlowNodeRefs(lane); + }); + } + + function handleLaneSet(laneSet, context) { + forEach$1(laneSet.lanes, contextual(handleLane, context)); + } + + function handleLaneSets(laneSets, context) { + forEach$1(laneSets, contextual(handleLaneSet, context)); + } + + function handleFlowElementsContainer(container, context) { + handleFlowElements(container.flowElements, context); + + if (container.laneSets) { + handleLaneSets(container.laneSets, context); + } + } + + function handleFlowElements(flowElements, context) { + forEach$1(flowElements, function(e) { + if (is(e, 'bpmn:SequenceFlow')) { + deferred.push(function() { + handleSequenceFlow(e, context); + }); + } else if (is(e, 'bpmn:BoundaryEvent')) { + deferred.unshift(function() { + handleFlowNode(e, context); + }); + } else if (is(e, 'bpmn:FlowNode')) { + handleFlowNode(e, context); + } else if (is(e, 'bpmn:DataObject')) ; else if (is(e, 'bpmn:DataStoreReference')) { + handleDataElement(e, context); + } else if (is(e, 'bpmn:DataObjectReference')) { + handleDataElement(e, context); + } else { + logError( + translate('unrecognized flowElement {element} in context {context}', { + element: elementToString(e), + context: (context ? elementToString(context.businessObject) : 'null') + }), + { element: e, context: context } + ); + } + }); + } + + function handleParticipant(participant, context) { + var newCtx = visitIfDi(participant, context); + + var process = participant.processRef; + if (process) { + handleProcess(process, newCtx || context); + } + } + + function handleCollaboration(collaboration, context) { + + forEach$1(collaboration.participants, contextual(handleParticipant, context)); + + handleArtifacts(collaboration.artifacts, context); + + // handle message flows latest in the process + deferred.push(function() { + handleMessageFlows(collaboration.messageFlows, context); + }); + } + + + function wireFlowNodeRefs(lane) { + + // wire the virtual flowNodeRefs <-> relationship + forEach$1(lane.flowNodeRef, function(flowNode) { + var lanes = flowNode.get('lanes'); + + if (lanes) { + lanes.push(lane); + } + }); + } + + // API ////////////////////// + + return { + handleDeferred: handleDeferred, + handleDefinitions: handleDefinitions, + handleSubProcess: handleSubProcess, + registerDi: registerDi + }; + } + + /** + * The importBpmnDiagram result. + * + * @typedef {Object} ImportBPMNDiagramResult + * + * @property {Array} warnings + */ + + /** + * The importBpmnDiagram error. + * + * @typedef {Error} ImportBPMNDiagramError + * + * @property {Array} warnings + */ + + /** + * Import the definitions into a diagram. + * + * Errors and warnings are reported through the specified callback. + * + * @param {djs.Diagram} diagram + * @param {ModdleElement} definitions + * @param {ModdleElement} [bpmnDiagram] the diagram to be rendered + * (if not provided, the first one will be rendered) + * + * Returns {Promise} + */ + function importBpmnDiagram(diagram, definitions, bpmnDiagram) { + + var importer, + eventBus, + translate, + canvas; + + var error, + warnings = []; + + /** + * Walk the diagram semantically, importing (=drawing) + * all elements you encounter. + * + * @param {ModdleElement} definitions + * @param {ModdleElement} bpmnDiagram + */ + function render(definitions, bpmnDiagram) { + + var visitor = { + + root: function(element, di) { + return importer.add(element, di); + }, + + element: function(element, di, parentShape) { + return importer.add(element, di, parentShape); + }, + + error: function(message, context) { + warnings.push({ message: message, context: context }); + } + }; + + var walker = new BpmnTreeWalker(visitor, translate); + + + bpmnDiagram = bpmnDiagram || (definitions.diagrams && definitions.diagrams[0]); + + var diagramsToImport = getDiagramsToImport(definitions, bpmnDiagram); + + if (!diagramsToImport) { + throw new Error(translate('no diagram to display')); + } + + // traverse BPMN 2.0 document model, + // starting at definitions + forEach$1(diagramsToImport, function(diagram) { + walker.handleDefinitions(definitions, diagram); + }); + + var rootId = bpmnDiagram.plane.bpmnElement.id; + + // we do need to account for different ways we create root elements + // each nested imported do have the `_plane` suffix, while + // the root is found under the business object ID + canvas.setRootElement( + canvas.findRoot(rootId + '_plane') || canvas.findRoot(rootId) + ); + } + + return new Promise(function(resolve, reject) { + try { + importer = diagram.get('bpmnImporter'); + eventBus = diagram.get('eventBus'); + translate = diagram.get('translate'); + canvas = diagram.get('canvas'); + + eventBus.fire('import.render.start', { definitions: definitions }); + + render(definitions, bpmnDiagram); + + eventBus.fire('import.render.complete', { + error: error, + warnings: warnings + }); + + return resolve({ warnings: warnings }); + } catch (e) { + + e.warnings = warnings; + return reject(e); + } + }); + } + + /** + * Returns all diagrams in the same hierarchy as the requested diagram. + * Includes all parent and sub process diagrams. + * + * @param {Array} definitions + * @param {Object} bpmnDiagram + * + * @returns {Array} + */ + function getDiagramsToImport(definitions, bpmnDiagram) { + if (!bpmnDiagram) { + return; + } + + var bpmnElement = bpmnDiagram.plane.bpmnElement, + rootElement = bpmnElement; + + if (!is$1(bpmnElement, 'bpmn:Process') && !is$1(bpmnElement, 'bpmn:Collaboration')) { + rootElement = findRootProcess(bpmnElement); + } + + // in case the process is part of a collaboration, the plane references the + // collaboration, not the process + var collaboration; + + if (is$1(rootElement, 'bpmn:Collaboration')) { + collaboration = rootElement; + } else { + collaboration = find(definitions.rootElements, function(element) { + if (!is$1(element, 'bpmn:Collaboration')) { + return; + } + + return find(element.participants, function(participant) { + return participant.processRef === rootElement; + }); + }); + } + + var rootElements = [ rootElement ]; + + // all collaboration processes can contain sub-diagrams + if (collaboration) { + rootElements = map(collaboration.participants, function(participant) { + return participant.processRef; + }); + + rootElements.push(collaboration); + } + + var allChildren = selfAndAllFlowElements(rootElements); + + // if we have multiple diagrams referencing the same element, we + // use the first in the file + var diagramsToImport = [ bpmnDiagram ]; + var handledElements = [ bpmnElement ]; + + forEach$1(definitions.diagrams, function(diagram) { + var businessObject = diagram.plane.bpmnElement; + + if ( + allChildren.indexOf(businessObject) !== -1 && + handledElements.indexOf(businessObject) === -1 + ) { + diagramsToImport.push(diagram); + handledElements.push(businessObject); + } + }); + + + return diagramsToImport; + } + + function selfAndAllFlowElements(elements) { + var result = []; + + forEach$1(elements, function(element) { + if (!element) { + return; + } + + result.push(element); + + result = result.concat(selfAndAllFlowElements(element.flowElements)); + }); + + return result; + } + + function findRootProcess(element) { + var parent = element; + + while (parent) { + if (is$1(parent, 'bpmn:Process')) { + return parent; + } + + parent = parent.$parent; + } + } + + /** + * This file must not be changed or exchanged. + * + * @see http://bpmn.io/license for more information. + */ + + + // inlined ../../resources/logo.svg + var BPMNIO_LOGO_SVG = ''; + + var BPMNIO_IMG = BPMNIO_LOGO_SVG; + + var LOGO_STYLES = { + verticalAlign: 'middle' + }; + + var LINK_STYLES = { + 'color': '#404040' + }; + + var LIGHTBOX_STYLES = { + 'zIndex': '1001', + 'position': 'fixed', + 'top': '0', + 'left': '0', + 'right': '0', + 'bottom': '0' + }; + + var BACKDROP_STYLES = { + 'width': '100%', + 'height': '100%', + 'background': 'rgba(40,40,40,0.2)' + }; + + var NOTICE_STYLES = { + 'position': 'absolute', + 'left': '50%', + 'top': '40%', + 'transform': 'translate(-50%)', + 'width': '260px', + 'padding': '10px', + 'background': 'white', + 'boxShadow': '0 1px 4px rgba(0,0,0,0.3)', + 'fontFamily': 'Helvetica, Arial, sans-serif', + 'fontSize': '14px', + 'display': 'flex', + 'lineHeight': '1.3' + }; + + var LIGHTBOX_MARKUP = + '
            ' + + '
            ' + + '
            ' + + '' + + BPMNIO_IMG + + '' + + '' + + 'Web-based tooling for BPMN, DMN and CMMN diagrams ' + + 'powered by bpmn.io.' + + '' + + '
            ' + + '
            '; + + + var lightbox; + + function createLightbox() { + lightbox = domify(LIGHTBOX_MARKUP); + + assign$1(lightbox, LIGHTBOX_STYLES); + assign$1(query('svg', lightbox), LOGO_STYLES); + assign$1(query('.backdrop', lightbox), BACKDROP_STYLES); + assign$1(query('.notice', lightbox), NOTICE_STYLES); + assign$1(query('.link', lightbox), LINK_STYLES, { + 'margin': '15px 20px 15px 10px', + 'alignSelf': 'center' + }); + } + + function open() { + + if (!lightbox) { + createLightbox(); + + delegate.bind(lightbox, '.backdrop', 'click', function(event) { + document.body.removeChild(lightbox); + }); + } + + document.body.appendChild(lightbox); + } + + /** + * The code in the area + * must not be changed. + * + * @see http://bpmn.io/license for more information. + */ + + /** + * A base viewer for BPMN 2.0 diagrams. + * + * Have a look at {@link Viewer}, {@link NavigatedViewer} or {@link Modeler} for + * bundles that include actual features. + * + * @param {Object} [options] configuration options to pass to the viewer + * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body. + * @param {string|number} [options.width] the width of the viewer + * @param {string|number} [options.height] the height of the viewer + * @param {Object} [options.moddleExtensions] extension packages to provide + * @param {Array} [options.modules] a list of modules to override the default modules + * @param {Array} [options.additionalModules] a list of modules to use with the default modules + */ + function BaseViewer(options) { + + options = assign({}, DEFAULT_OPTIONS, options); + + this._moddle = this._createModdle(options); + + this._container = this._createContainer(options); + + /* */ + + addProjectLogo(this._container); + + /* */ + + this._init(this._container, this._moddle, options); + } + + e(BaseViewer, Diagram); + + /** + * The importXML result. + * + * @typedef {Object} ImportXMLResult + * + * @property {Array} warnings + */ + + /** + * The importXML error. + * + * @typedef {Error} ImportXMLError + * + * @property {Array} warnings + */ + + /** + * Parse and render a BPMN 2.0 diagram. + * + * Once finished the viewer reports back the result to the + * provided callback function with (err, warnings). + * + * ## Life-Cycle Events + * + * During import the viewer will fire life-cycle events: + * + * * import.parse.start (about to read model from xml) + * * import.parse.complete (model read; may have worked or not) + * * import.render.start (graphical import start) + * * import.render.complete (graphical import finished) + * * import.done (everything done) + * + * You can use these events to hook into the life-cycle. + * + * @param {string} xml the BPMN 2.0 xml + * @param {ModdleElement|string} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered) + * + * Returns {Promise} + */ + BaseViewer.prototype.importXML = wrapForCompatibility(function importXML(xml, bpmnDiagram) { + + var self = this; + + function ParseCompleteEvent(data) { + + var event = self.get('eventBus').createEvent(data); + + // TODO(nikku): remove with future bpmn-js version + Object.defineProperty(event, 'context', { + enumerable: true, + get: function() { + + console.warn(new Error( + 'import.parse.complete is deprecated ' + + 'and will be removed in future library versions' + )); + + return { + warnings: data.warnings, + references: data.references, + elementsById: data.elementsById + }; + } + }); + + return event; + } + + return new Promise(function(resolve, reject) { + + // hook in pre-parse listeners + + // allow xml manipulation + xml = self._emit('import.parse.start', { xml: xml }) || xml; + + self._moddle.fromXML(xml, 'bpmn:Definitions').then(function(result) { + var definitions = result.rootElement; + var references = result.references; + var parseWarnings = result.warnings; + var elementsById = result.elementsById; + + // hook in post parse listeners + + // allow definitions manipulation + definitions = self._emit('import.parse.complete', ParseCompleteEvent({ + error: null, + definitions: definitions, + elementsById: elementsById, + references: references, + warnings: parseWarnings + })) || definitions; + + self.importDefinitions(definitions, bpmnDiagram).then(function(result) { + var allWarnings = [].concat(parseWarnings, result.warnings || []); + + self._emit('import.done', { error: null, warnings: allWarnings }); + + return resolve({ warnings: allWarnings }); + }).catch(function(err) { + var allWarnings = [].concat(parseWarnings, err.warnings || []); + + self._emit('import.done', { error: err, warnings: allWarnings }); + + return reject(addWarningsToError(err, allWarnings)); + }); + }).catch(function(err) { + + self._emit('import.parse.complete', { + error: err + }); + + err = checkValidationError(err); + + self._emit('import.done', { error: err, warnings: err.warnings }); + + return reject(err); + }); + }); + }); + + /** + * The importDefinitions result. + * + * @typedef {Object} ImportDefinitionsResult + * + * @property {Array} warnings + */ + + /** + * The importDefinitions error. + * + * @typedef {Error} ImportDefinitionsError + * + * @property {Array} warnings + */ + + /** + * Import parsed definitions and render a BPMN 2.0 diagram. + * + * Once finished the viewer reports back the result to the + * provided callback function with (err, warnings). + * + * ## Life-Cycle Events + * + * During import the viewer will fire life-cycle events: + * + * * import.render.start (graphical import start) + * * import.render.complete (graphical import finished) + * + * You can use these events to hook into the life-cycle. + * + * @param {ModdleElement} definitions parsed BPMN 2.0 definitions + * @param {ModdleElement|string} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered) + * + * Returns {Promise} + */ + BaseViewer.prototype.importDefinitions = wrapForCompatibility(function importDefinitions(definitions, bpmnDiagram) { + + var self = this; + + return new Promise(function(resolve, reject) { + + self._setDefinitions(definitions); + + self.open(bpmnDiagram).then(function(result) { + + var warnings = result.warnings; + + return resolve({ warnings: warnings }); + }).catch(function(err) { + + return reject(err); + }); + }); + }); + + /** + * The open result. + * + * @typedef {Object} OpenResult + * + * @property {Array} warnings + */ + + /** + * The open error. + * + * @typedef {Error} OpenError + * + * @property {Array} warnings + */ + + /** + * Open diagram of previously imported XML. + * + * Once finished the viewer reports back the result to the + * provided callback function with (err, warnings). + * + * ## Life-Cycle Events + * + * During switch the viewer will fire life-cycle events: + * + * * import.render.start (graphical import start) + * * import.render.complete (graphical import finished) + * + * You can use these events to hook into the life-cycle. + * + * @param {string|ModdleElement} [bpmnDiagramOrId] id or the diagram to open + * + * Returns {Promise} + */ + BaseViewer.prototype.open = wrapForCompatibility(function open(bpmnDiagramOrId) { + + var definitions = this._definitions; + var bpmnDiagram = bpmnDiagramOrId; + + var self = this; + + return new Promise(function(resolve, reject) { + if (!definitions) { + var err1 = new Error('no XML imported'); + + return reject(addWarningsToError(err1, [])); + } + + if (typeof bpmnDiagramOrId === 'string') { + bpmnDiagram = findBPMNDiagram(definitions, bpmnDiagramOrId); + + if (!bpmnDiagram) { + var err2 = new Error('BPMNDiagram <' + bpmnDiagramOrId + '> not found'); + + return reject(addWarningsToError(err2, [])); + } + } + + // clear existing rendered diagram + // catch synchronous exceptions during #clear() + try { + self.clear(); + } catch (error) { + + return reject(addWarningsToError(error, [])); + } + + // perform graphical import + importBpmnDiagram(self, definitions, bpmnDiagram).then(function(result) { + + var warnings = result.warnings; + + return resolve({ warnings: warnings }); + }).catch(function(err) { + + return reject(err); + }); + }); + }); + + /** + * The saveXML result. + * + * @typedef {Object} SaveXMLResult + * + * @property {string} xml + */ + + /** + * Export the currently displayed BPMN 2.0 diagram as + * a BPMN 2.0 XML document. + * + * ## Life-Cycle Events + * + * During XML saving the viewer will fire life-cycle events: + * + * * saveXML.start (before serialization) + * * saveXML.serialized (after xml generation) + * * saveXML.done (everything done) + * + * You can use these events to hook into the life-cycle. + * + * @param {Object} [options] export options + * @param {boolean} [options.format=false] output formatted XML + * @param {boolean} [options.preamble=true] output preamble + * + * Returns {Promise} + */ + BaseViewer.prototype.saveXML = wrapForCompatibility(function saveXML(options) { + + options = options || {}; + + var self = this; + + var definitions = this._definitions; + + return new Promise(function(resolve) { + + if (!definitions) { + return resolve({ + error: new Error('no definitions loaded') + }); + } + + // allow to fiddle around with definitions + definitions = self._emit('saveXML.start', { + definitions: definitions + }) || definitions; + + self._moddle.toXML(definitions, options).then(function(result) { + + var xml = result.xml; + + xml = self._emit('saveXML.serialized', { + xml: xml + }) || xml; + + return resolve({ + xml: xml + }); + }); + }).catch(function(error) { + return { error: error }; + }).then(function(result) { + + self._emit('saveXML.done', result); + + var error = result.error; + + if (error) { + return Promise.reject(error); + } + + return result; + }); + }); + + /** + * The saveSVG result. + * + * @typedef {Object} SaveSVGResult + * + * @property {string} svg + */ + + /** + * Export the currently displayed BPMN 2.0 diagram as + * an SVG image. + * + * ## Life-Cycle Events + * + * During SVG saving the viewer will fire life-cycle events: + * + * * saveSVG.start (before serialization) + * * saveSVG.done (everything done) + * + * You can use these events to hook into the life-cycle. + * + * @param {Object} [options] + * + * Returns {Promise} + */ + BaseViewer.prototype.saveSVG = wrapForCompatibility(function saveSVG(options) { + + var self = this; + + return new Promise(function(resolve, reject) { + + self._emit('saveSVG.start'); + + var svg, err; + + try { + var canvas = self.get('canvas'); + + var contentNode = canvas.getActiveLayer(), + defsNode = query('defs', canvas._svg); + + var contents = innerSVG(contentNode), + defs = defsNode ? '' + innerSVG(defsNode) + '' : ''; + + var bbox = contentNode.getBBox(); + + svg = + '\n' + + '\n' + + '\n' + + '' + + defs + contents + + ''; + } catch (e) { + err = e; + } + + self._emit('saveSVG.done', { + error: err, + svg: svg + }); + + if (!err) { + return resolve({ svg: svg }); + } + + return reject(err); + }); + }); + + /** + * Get a named diagram service. + * + * @example + * + * var elementRegistry = viewer.get('elementRegistry'); + * var startEventShape = elementRegistry.get('StartEvent_1'); + * + * @param {string} name + * + * @return {Object} diagram service instance + * + * @method BaseViewer#get + */ + + /** + * Invoke a function in the context of this viewer. + * + * @example + * + * viewer.invoke(function(elementRegistry) { + * var startEventShape = elementRegistry.get('StartEvent_1'); + * }); + * + * @param {Function} fn to be invoked + * + * @return {Object} the functions return value + * + * @method BaseViewer#invoke + */ + + + BaseViewer.prototype._setDefinitions = function(definitions) { + this._definitions = definitions; + }; + + BaseViewer.prototype.getModules = function() { + return this._modules; + }; + + /** + * Remove all drawn elements from the viewer. + * + * After calling this method the viewer can still + * be reused for opening another diagram. + * + * @method BaseViewer#clear + */ + BaseViewer.prototype.clear = function() { + if (!this.getDefinitions()) { + + // no diagram to clear + return; + } + + // remove drawn elements + Diagram.prototype.clear.call(this); + }; + + /** + * Destroy the viewer instance and remove all its + * remainders from the document tree. + */ + BaseViewer.prototype.destroy = function() { + + // diagram destroy + Diagram.prototype.destroy.call(this); + + // dom detach + remove$1(this._container); + }; + + /** + * Register an event listener + * + * Remove a previously added listener via {@link #off(event, callback)}. + * + * @param {string} event + * @param {number} [priority] + * @param {Function} callback + * @param {Object} [that] + */ + BaseViewer.prototype.on = function(event, priority, callback, target) { + return this.get('eventBus').on(event, priority, callback, target); + }; + + /** + * De-register an event listener + * + * @param {string} event + * @param {Function} callback + */ + BaseViewer.prototype.off = function(event, callback) { + this.get('eventBus').off(event, callback); + }; + + BaseViewer.prototype.attachTo = function(parentNode) { + + if (!parentNode) { + throw new Error('parentNode required'); + } + + // ensure we detach from the + // previous, old parent + this.detach(); + + // unwrap jQuery if provided + if (parentNode.get && parentNode.constructor.prototype.jquery) { + parentNode = parentNode.get(0); + } + + if (typeof parentNode === 'string') { + parentNode = query(parentNode); + } + + parentNode.appendChild(this._container); + + this._emit('attach', {}); + + this.get('canvas').resized(); + }; + + BaseViewer.prototype.getDefinitions = function() { + return this._definitions; + }; + + BaseViewer.prototype.detach = function() { + + var container = this._container, + parentNode = container.parentNode; + + if (!parentNode) { + return; + } + + this._emit('detach', {}); + + parentNode.removeChild(container); + }; + + BaseViewer.prototype._init = function(container, moddle, options) { + + var baseModules = options.modules || this.getModules(), + additionalModules = options.additionalModules || [], + staticModules = [ + { + bpmnjs: [ 'value', this ], + moddle: [ 'value', moddle ] + } + ]; + + var diagramModules = [].concat(staticModules, baseModules, additionalModules); + + var diagramOptions = assign(omit(options, [ 'additionalModules' ]), { + canvas: assign({}, options.canvas, { container: container }), + modules: diagramModules + }); + + // invoke diagram constructor + Diagram.call(this, diagramOptions); + + if (options && options.container) { + this.attachTo(options.container); + } + }; + + /** + * Emit an event on the underlying {@link EventBus} + * + * @param {string} type + * @param {Object} event + * + * @return {Object} event processing result (if any) + */ + BaseViewer.prototype._emit = function(type, event) { + return this.get('eventBus').fire(type, event); + }; + + BaseViewer.prototype._createContainer = function(options) { + + var container = domify('
            '); + + assign$1(container, { + width: ensureUnit(options.width), + height: ensureUnit(options.height), + position: options.position + }); + + return container; + }; + + BaseViewer.prototype._createModdle = function(options) { + var moddleOptions = assign({}, this._moddleExtensions, options.moddleExtensions); + + return new simple(moddleOptions); + }; + + BaseViewer.prototype._modules = []; + + // helpers /////////////// + + function addWarningsToError(err, warningsAry) { + err.warnings = warningsAry; + return err; + } + + function checkValidationError(err) { + + // check if we can help the user by indicating wrong BPMN 2.0 xml + // (in case he or the exporting tool did not get that right) + + var pattern = /unparsable content <([^>]+)> detected([\s\S]*)$/; + var match = pattern.exec(err.message); + + if (match) { + err.message = + 'unparsable content <' + match[1] + '> detected; ' + + 'this may indicate an invalid BPMN 2.0 diagram file' + match[2]; + } + + return err; + } + + var DEFAULT_OPTIONS = { + width: '100%', + height: '100%', + position: 'relative' + }; + + + /** + * Ensure the passed argument is a proper unit (defaulting to px) + */ + function ensureUnit(val) { + return val + (isNumber(val) ? 'px' : ''); + } + + + /** + * Find BPMNDiagram in definitions by ID + * + * @param {ModdleElement} definitions + * @param {string} diagramId + * + * @return {ModdleElement|null} + */ + function findBPMNDiagram(definitions, diagramId) { + if (!diagramId) { + return null; + } + + return find(definitions.diagrams, function(element) { + return element.id === diagramId; + }) || null; + } + + /** + * Adds the project logo to the diagram container as + * required by the bpmn.io license. + * + * @see http://bpmn.io/license + * + * @param {Element} container + */ + function addProjectLogo(container) { + var img = BPMNIO_IMG; + + var linkMarkup = + '' + + img + + ''; + + var linkElement = domify(linkMarkup); + + assign$1(query('svg', linkElement), LOGO_STYLES); + assign$1(linkElement, LINK_STYLES, { + position: 'absolute', + bottom: '15px', + right: '15px', + zIndex: '100' + }); + + container.appendChild(linkElement); + + componentEvent.bind(linkElement, 'click', function(event) { + open(); + + event.preventDefault(); + }); + } + + /* */ + + /** + * A viewer for BPMN 2.0 diagrams. + * + * Have a look at {@link NavigatedViewer} or {@link Modeler} for bundles that include + * additional features. + * + * + * ## Extending the Viewer + * + * In order to extend the viewer pass extension modules to bootstrap via the + * `additionalModules` option. An extension module is an object that exposes + * named services. + * + * The following example depicts the integration of a simple + * logging component that integrates with interaction events: + * + * + * ```javascript + * + * // logging component + * function InteractionLogger(eventBus) { + * eventBus.on('element.hover', function(event) { + * console.log() + * }) + * } + * + * InteractionLogger.$inject = [ 'eventBus' ]; // minification save + * + * // extension module + * var extensionModule = { + * __init__: [ 'interactionLogger' ], + * interactionLogger: [ 'type', InteractionLogger ] + * }; + * + * // extend the viewer + * var bpmnViewer = new Viewer({ additionalModules: [ extensionModule ] }); + * bpmnViewer.importXML(...); + * ``` + * + * @param {Object} [options] configuration options to pass to the viewer + * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body. + * @param {string|number} [options.width] the width of the viewer + * @param {string|number} [options.height] the height of the viewer + * @param {Object} [options.moddleExtensions] extension packages to provide + * @param {Array} [options.modules] a list of modules to override the default modules + * @param {Array} [options.additionalModules] a list of modules to use with the default modules + */ + function Viewer(options) { + BaseViewer.call(this, options); + } + + e(Viewer, BaseViewer); + + // modules the viewer is composed of + Viewer.prototype._modules = [ + CoreModule$1, + TranslateModule, + SelectionModule, + OverlaysModule, + DrilldownModdule + ]; + + // default moddle extensions the viewer is composed of + Viewer.prototype._moddleExtensions = {}; + + return Viewer; + +})); diff --git a/dist/bpmn-viewer.production.min.js b/dist/bpmn-viewer.production.min.js new file mode 100644 index 0000000..844e20b --- /dev/null +++ b/dist/bpmn-viewer.production.min.js @@ -0,0 +1,24 @@ +/*! bpmn-js - bpmn-viewer v9.4.0 | Copyright (c) 2014-present, camunda Services GmbH | bpmn.io/license */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).BpmnJS=t()}(this,(function(){"use strict";function e(e,t){t&&(e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}))}var t=Object.prototype.toString,n=Object.prototype.hasOwnProperty;function i(e){return void 0!==e}function r(e){return"[object Array]"===t.call(e)}function o(e){return"[object Object]"===t.call(e)}function a(e){return"[object Number]"===t.call(e)}function s(e){var n=t.call(e);return"[object Function]"===n||"[object AsyncFunction]"===n||"[object GeneratorFunction]"===n||"[object AsyncGeneratorFunction]"===n||"[object Proxy]"===n}function l(e){return"[object String]"===t.call(e)}function p(e,t){return n.call(e,t)}function c(e,t){var n;return t=g(t),h(e,(function(e,i){if(t(e,i))return n=e,!1})),n}function u(e,t){var n=[];return h(e,(function(e,i){t(e,i)&&n.push(e)})),n}function h(e,t){var n;if(void 0!==e){var i=r(e)?x:v;for(var o in e)if(p(e,o)&&!1===t(n=e[o],i(o)))return n}}function m(e,t,n){return h(e,(function(e,i){n=t(n,e,i)})),n}function f(e,t){return!!m(e,(function(e,n,i){return e&&t(n,i)}),!0)}function d(e,t){return!!c(e,t)}function y(e){return function(t){return f(e,(function(e,n){return t[n]===e}))}}function g(e){return s(e)?e:function(t){return t===e}}function v(e){return e}function x(e){return Number(e)}function b(e,t){return e.bind(t)}function w(){return w=Object.assign||function(e){for(var t=1;t1?t-1:0),i=1;i"+e+"",t=!0);var n=function(e){var t;return(t=new DOMParser).async=!1,t.parseFromString(e,"text/xml")}(e);if(!t)return n;for(var i=document.createDocumentFragment(),r=n.firstChild;r.firstChild;)i.appendChild(r.firstChild);return i}function H(e,t){var n;return"<"===e.charAt(0)?(n=$(e).firstChild,n=document.importNode(n,!0)):n=document.createElementNS(W,e),t&&N(n,t),n}var K=null;function U(){return null===K&&(K=H("svg")),K}function q(e,t){var n,i,r=Object.keys(t);for(n=0;i=r[n];n++)e[i]=t[i];return e}function Y(e){return e?U().createSVGTransformFromMatrix(e):U().createSVGTransform()}var X=/([&<>]{1})/g,Z=/([\n\r"]{1})/g,J={"&":"&","<":"<",">":">",'"':"'"};function Q(e,t){return e.replace(t,(function(e,t){return J[t]||t}))}function ee(e,t){var n,i,r,o,a;switch(e.nodeType){case 3:t.push(Q(e.textContent,X));break;case 1:if(t.push("<",e.tagName),e.hasAttributes())for(n=0,i=(r=e.attributes).length;n"),n=0,i=(a=e.childNodes).length;n")}else t.push("/>");break;case 8:t.push("\x3c!--",Q(e.nodeValue,X),"--\x3e");break;case 4:t.push("");break;default:throw new Error("unable to handle node "+e.nodeType)}return t}function te(e,t){var n=$(t);if(z(e),t){(function(e){return"#document-fragment"===e.nodeName})(n)||(n=n.documentElement);for(var i,r=(i=n.childNodes,Array.prototype.slice.call(i)),o=0;o1?n-1:0),r=1;r
            a',Ve=!Ie.getElementsByTagName("link").length,Ie=void 0);var ze={legend:[1,"
            ","
            "],tr:[2,"","
            "],col:[2,"","
            "],_default:Ve?[1,"X
            ","
            "]:[0,"",""]};function We(e,t){return(t=t||document).querySelector(e)}function Ge(e){e.parentNode&&e.parentNode.removeChild(e)}function $e(e,t,n,i,r){var o=Y();o.setTranslate(t,n);var a=Y();a.setRotate(i||0,0,0);var s=Y();s.setScale(r||1,r||1),re(e,[o,a,s])}function He(e,t,n){var i=Y();i.setTranslate(t,n),re(e,i)}ze.td=ze.th=[3,"","
            "],ze.option=ze.optgroup=[1,'"],ze.thead=ze.tbody=ze.colgroup=ze.caption=ze.tfoot=[1,"","
            "],ze.polyline=ze.ellipse=ze.polygon=ze.circle=ze.text=ze.line=ze.path=ze.rect=ze.g=[1,'',""];var Ke=function(e,t){return e(t={exports:{}},t.exports),t.exports}((function(e){var t=e.exports=function(e,n){if(n||(n=16),void 0===e&&(e=128),e<=0)return"0";for(var i=Math.log(Math.pow(2,e))/Math.log(n),r=2;i===1/0;r*=2)i=Math.log(Math.pow(2,e/r))/Math.log(n)*r;var o=i-Math.floor(i),a="";for(r=0;r=Math.pow(2,e)?t(e,n):a};t.rack=function(e,n,i){var r=function(r){var a=0;do{if(a++>10){if(!i)throw new Error("too many ID collisions, use more bits");e+=i}var s=t(e,n)}while(Object.hasOwnProperty.call(o,s));return o[s]=r,s},o=r.hats={};return r.get=function(e){return r.hats[e]},r.set=function(e,t){return r.hats[e]=t,r},r.bits=e||128,r.base=n||16,r}}));function Ue(e){if(!(this instanceof Ue))return new Ue(e);e=e||[128,36,1],this._seed=e.length?Ke.rack(e[0],e[1],e[2]):e}Ue.prototype.next=function(e){return this._seed(e||!0)},Ue.prototype.nextPrefixed=function(e,t){var n;do{n=e+this.next(!0)}while(this.assigned(n));return this.claim(n,t),n},Ue.prototype.claim=function(e,t){this._seed.set(e,t||!0)},Ue.prototype.assigned=function(e){return this._seed.get(e)||!1},Ue.prototype.unclaim=function(e){delete this._seed.hats[e]},Ue.prototype.clear=function(){var e,t=this._seed.hats;for(e in t)this.unclaim(e)};var qe=new Ue,Ye=.95;function Xe(e,t,n,i,r,a,s){_.call(this,t,s);var l=e&&e.defaultFillColor,p=e&&e.defaultStrokeColor,c=e&&e.defaultLabelColor,u=qe.next(),m={},f=n.computeStyle;function d(e,t){var n=E({fill:le,strokeWidth:1,strokeLinecap:"round",strokeDasharray:"none"},t.attrs),i=t.ref||{x:0,y:0},o=t.scale||1;"none"===n.strokeDasharray&&(n.strokeDasharray=[1e4,1]);var a=H("marker");N(t.element,n),P(a,t.element),N(a,{id:e,viewBox:"0 0 20 20",refX:i.x,refY:i.y,markerWidth:20*o,markerHeight:20*o,orient:"auto"});var s=We("defs",r._svg);s||(s=H("defs"),P(r._svg,s)),P(s,a),m[e]=a}function y(e){return e.replace(/[^0-9a-zA-z]+/g,"_")}function g(e,t,n){var i=e+"-"+y(t)+"-"+y(n)+"-"+u;return m[i]||function(e,t,n,i){if("sequenceflow-end"===t){var r=H("path");N(r,{d:"M 1 5 L 11 10 L 1 15 Z"}),d(e,{element:r,ref:{x:11,y:10},scale:.5,attrs:{fill:i,stroke:i}})}if("messageflow-start"===t){var o=H("circle");N(o,{cx:6,cy:6,r:3.5}),d(e,{element:o,attrs:{fill:n,stroke:i},ref:{x:6,y:6}})}if("messageflow-end"===t){var a=H("path");N(a,{d:"m 1 5 l 0 -3 l 7 3 l -7 3 z"}),d(e,{element:a,attrs:{fill:n,stroke:i,strokeLinecap:"butt"},ref:{x:8.5,y:5}})}if("association-start"===t){var s=H("path");N(s,{d:"M 11 5 L 1 10 L 11 15"}),d(e,{element:s,attrs:{fill:"none",stroke:i,strokeWidth:1.5},ref:{x:1,y:10},scale:.5})}if("association-end"===t){var l=H("path");N(l,{d:"M 1 5 L 11 10 L 1 15"}),d(e,{element:l,attrs:{fill:"none",stroke:i,strokeWidth:1.5},ref:{x:12,y:10},scale:.5})}if("conditional-flow-marker"===t){var p=H("path");N(p,{d:"M 0 10 L 8 6 L 16 10 L 8 14 Z"}),d(e,{element:p,attrs:{fill:n,stroke:i},ref:{x:-1,y:10},scale:.5})}if("conditional-default-flow-marker"===t){var c=H("path");N(c,{d:"M 6 4 L 10 16"}),d(e,{element:c,attrs:{stroke:i},ref:{x:0,y:10},scale:.5})}}(i,e,t,n),"url(#"+i+")"}function v(e,t,n,i,r){o(i)&&(r=i,i=0),i=i||0,"none"===(r=f(r,{stroke:le,strokeWidth:2,fill:"white"})).fill&&delete r.fillOpacity;var a=t/2,s=n/2,l=H("circle");return N(l,{cx:a,cy:s,r:Math.round((t+n)/4-i)}),N(l,r),P(e,l),l}function x(e,t,n,i,r,a){o(r)&&(a=r,r=0),r=r||0,a=f(a,{stroke:le,strokeWidth:2,fill:"white"});var s=H("rect");return N(s,{x:r,y:r,width:t-2*r,height:n-2*r,rx:i,ry:i}),N(s,a),P(e,s),s}function b(e,t,n){var i=se(t,n=f(n,["no-fill"],{stroke:le,strokeWidth:2,fill:"none"}));return P(e,i),i}function w(e,t,n){n=f(n,["no-fill"],{strokeWidth:2,stroke:le});var i=H("path");return N(i,{d:t}),N(i,n),P(e,i),i}function A(e,t,n,i){return w(t,n,E({"data-marker":e},i))}function k(e){return F[e]}function T(e){return function(t,n){return k(e)(t,n)}}function D(e,t){var n=ce(e),i=function(e){return"bpmn:IntermediateThrowEvent"===e.$type||"bpmn:EndEvent"===e.$type}(n);return n.eventDefinitions&&n.eventDefinitions.length>1?n.parallelMultiple?k("bpmn:ParallelMultipleEventDefinition")(t,e,i):k("bpmn:MultipleEventDefinition")(t,e,i):pe(n,"bpmn:MessageEventDefinition")?k("bpmn:MessageEventDefinition")(t,e,i):pe(n,"bpmn:TimerEventDefinition")?k("bpmn:TimerEventDefinition")(t,e,i):pe(n,"bpmn:ConditionalEventDefinition")?k("bpmn:ConditionalEventDefinition")(t,e):pe(n,"bpmn:SignalEventDefinition")?k("bpmn:SignalEventDefinition")(t,e,i):pe(n,"bpmn:EscalationEventDefinition")?k("bpmn:EscalationEventDefinition")(t,e,i):pe(n,"bpmn:LinkEventDefinition")?k("bpmn:LinkEventDefinition")(t,e,i):pe(n,"bpmn:ErrorEventDefinition")?k("bpmn:ErrorEventDefinition")(t,e,i):pe(n,"bpmn:CancelEventDefinition")?k("bpmn:CancelEventDefinition")(t,e,i):pe(n,"bpmn:CompensateEventDefinition")?k("bpmn:CompensateEventDefinition")(t,e,i):pe(n,"bpmn:TerminateEventDefinition")?k("bpmn:TerminateEventDefinition")(t,e,i):null}function O(e,t,n){n=E({size:{width:100}},n);var i=a.createText(t||"",n);return j(i).add("djs-label"),P(e,i),i}function B(e,t,n){return O(e,ce(t).name,{box:t,align:n,padding:5,style:{fill:me(t,c,p)}})}function L(e,t,n){$e(O(e,t,{box:{height:30,width:n.height},align:"center-middle",style:{fill:me(n,c,p)}}),0,-(-1*n.height),270)}function I(e){for(var t=e.waypoints,n="m "+t[0].x+","+t[0].y,i=1;i1)for(;n=i.shift();){if(!(n.length+oe?t.width:e}),0),g=a.top;"middle"===r.vertical&&(g+=(n.height-d)/2),g-=(l||c[0].height)/4;var v=H("text");return N(v,i),h(c,(function(e){var t;switch(g+=l||e.height,r.horizontal){case"left":t=a.left;break;case"right":t=(s?y:u)-a.right-e.width;break;default:t=Math.max(((s?y:u)-e.width)/2+a.left,0)}var n=H("tspan");N(n,{x:t,y:g}),n.textContent=e.text,P(v,n)})),V(f),{dimensions:{width:y,height:d},element:v}};function it(e){var t=E({fontFamily:"Arial, sans-serif",fontSize:12,fontWeight:"normal",lineHeight:1.2},e&&e.defaultStyle||{}),n=parseInt(t.fontSize,10)-1,i=E({},t,{fontSize:n},e&&e.externalStyle||{}),r=new nt({style:t});this.getExternalLabelBounds=function(e,t){var n=r.getDimensions(t,{box:{width:90,height:30,x:e.width/2+e.x,y:e.height/2+e.y},style:i});return{x:Math.round(e.x+e.width/2-n.width/2),y:Math.round(e.y),width:Math.ceil(n.width),height:Math.ceil(n.height)}},this.getTextAnnotationBounds=function(e,n){var i=r.getDimensions(n,{box:e,style:t,align:"left-top",padding:5});return{x:e.x,y:e.y,width:e.width,height:Math.max(30,Math.round(i.height))}},this.createText=function(e,t){return r.createText(e,t||{})},this.getDefaultStyle=function(){return t},this.getExternalStyle=function(){return i}}it.$inject=["config.textRenderer"];var rt=/\{([^{}]+)\}/g,ot=/(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g;var at={__init__:["bpmnRenderer"],bpmnRenderer:["type",Xe],textRenderer:["type",it],pathMap:["type",function(){this.pathMap={EVENT_MESSAGE:{d:"m {mx},{my} l 0,{e.y1} l {e.x1},0 l 0,-{e.y1} z l {e.x0},{e.y0} l {e.x0},-{e.y0}",height:36,width:36,heightElements:[6,14],widthElements:[10.5,21]},EVENT_SIGNAL:{d:"M {mx},{my} l {e.x0},{e.y0} l -{e.x1},0 Z",height:36,width:36,heightElements:[18],widthElements:[10,20]},EVENT_ESCALATION:{d:"M {mx},{my} l {e.x0},{e.y0} l -{e.x0},-{e.y1} l -{e.x0},{e.y1} Z",height:36,width:36,heightElements:[20,7],widthElements:[8]},EVENT_CONDITIONAL:{d:"M {e.x0},{e.y0} l {e.x1},0 l 0,{e.y2} l -{e.x1},0 Z M {e.x2},{e.y3} l {e.x0},0 M {e.x2},{e.y4} l {e.x0},0 M {e.x2},{e.y5} l {e.x0},0 M {e.x2},{e.y6} l {e.x0},0 M {e.x2},{e.y7} l {e.x0},0 M {e.x2},{e.y8} l {e.x0},0 ",height:36,width:36,heightElements:[8.5,14.5,18,11.5,14.5,17.5,20.5,23.5,26.5],widthElements:[10.5,14.5,12.5]},EVENT_LINK:{d:"m {mx},{my} 0,{e.y0} -{e.x1},0 0,{e.y1} {e.x1},0 0,{e.y0} {e.x0},-{e.y2} -{e.x0},-{e.y2} z",height:36,width:36,heightElements:[4.4375,6.75,7.8125],widthElements:[9.84375,13.5]},EVENT_ERROR:{d:"m {mx},{my} {e.x0},-{e.y0} {e.x1},-{e.y1} {e.x2},{e.y2} {e.x3},-{e.y3} -{e.x4},{e.y4} -{e.x5},-{e.y5} z",height:36,width:36,heightElements:[.023,8.737,8.151,16.564,10.591,8.714],widthElements:[.085,6.672,6.97,4.273,5.337,6.636]},EVENT_CANCEL_45:{d:"m {mx},{my} -{e.x1},0 0,{e.x0} {e.x1},0 0,{e.y1} {e.x0},0 0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z",height:36,width:36,heightElements:[4.75,8.5],widthElements:[4.75,8.5]},EVENT_COMPENSATION:{d:"m {mx},{my} {e.x0},-{e.y0} 0,{e.y1} z m {e.x1},-{e.y2} {e.x2},-{e.y3} 0,{e.y1} -{e.x2},-{e.y3} z",height:36,width:36,heightElements:[6.5,13,.4,6.1],widthElements:[9,9.3,8.7]},EVENT_TIMER_WH:{d:"M {mx},{my} l {e.x0},-{e.y0} m -{e.x0},{e.y0} l {e.x1},{e.y1} ",height:36,width:36,heightElements:[10,2],widthElements:[3,7]},EVENT_TIMER_LINE:{d:"M {mx},{my} m {e.x0},{e.y0} l -{e.x1},{e.y1} ",height:36,width:36,heightElements:[10,3],widthElements:[0,0]},EVENT_MULTIPLE:{d:"m {mx},{my} {e.x1},-{e.y0} {e.x1},{e.y0} -{e.x0},{e.y1} -{e.x2},0 z",height:36,width:36,heightElements:[6.28099,12.56199],widthElements:[3.1405,9.42149,12.56198]},EVENT_PARALLEL_MULTIPLE:{d:"m {mx},{my} {e.x0},0 0,{e.y1} {e.x1},0 0,{e.y0} -{e.x1},0 0,{e.y1} -{e.x0},0 0,-{e.y1} -{e.x1},0 0,-{e.y0} {e.x1},0 z",height:36,width:36,heightElements:[2.56228,7.68683],widthElements:[2.56228,7.68683]},GATEWAY_EXCLUSIVE:{d:"m {mx},{my} {e.x0},{e.y0} {e.x1},{e.y0} {e.x2},0 {e.x4},{e.y2} {e.x4},{e.y1} {e.x2},0 {e.x1},{e.y3} {e.x0},{e.y3} {e.x3},0 {e.x5},{e.y1} {e.x5},{e.y2} {e.x3},0 z",height:17.5,width:17.5,heightElements:[8.5,6.5312,-6.5312,-8.5],widthElements:[6.5,-6.5,3,-3,5,-5]},GATEWAY_PARALLEL:{d:"m {mx},{my} 0,{e.y1} -{e.x1},0 0,{e.y0} {e.x1},0 0,{e.y1} {e.x0},0 0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z",height:30,width:30,heightElements:[5,12.5],widthElements:[5,12.5]},GATEWAY_EVENT_BASED:{d:"m {mx},{my} {e.x0},{e.y0} {e.x0},{e.y1} {e.x1},{e.y2} {e.x2},0 z",height:11,width:11,heightElements:[-6,6,12,-12],widthElements:[9,-3,-12]},GATEWAY_COMPLEX:{d:"m {mx},{my} 0,{e.y0} -{e.x0},-{e.y1} -{e.x1},{e.y2} {e.x0},{e.y1} -{e.x2},0 0,{e.y3} {e.x2},0 -{e.x0},{e.y1} l {e.x1},{e.y2} {e.x0},-{e.y1} 0,{e.y0} {e.x3},0 0,-{e.y0} {e.x0},{e.y1} {e.x1},-{e.y2} -{e.x0},-{e.y1} {e.x2},0 0,-{e.y3} -{e.x2},0 {e.x0},-{e.y1} -{e.x1},-{e.y2} -{e.x0},{e.y1} 0,-{e.y0} -{e.x3},0 z",height:17.125,width:17.125,heightElements:[4.875,3.4375,2.125,3],widthElements:[3.4375,2.125,4.875,3]},DATA_OBJECT_PATH:{d:"m 0,0 {e.x1},0 {e.x0},{e.y0} 0,{e.y1} -{e.x2},0 0,-{e.y2} {e.x1},0 0,{e.y0} {e.x0},0",height:61,width:51,heightElements:[10,50,60],widthElements:[10,40,50,60]},DATA_OBJECT_COLLECTION_PATH:{d:"m{mx},{my} m 3,2 l 0,10 m 3,-10 l 0,10 m 3,-10 l 0,10",height:10,width:10,heightElements:[],widthElements:[]},DATA_ARROW:{d:"m 5,9 9,0 0,-3 5,5 -5,5 0,-3 -9,0 z",height:61,width:51,heightElements:[],widthElements:[]},DATA_STORE:{d:"m {mx},{my} l 0,{e.y2} c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 l 0,-{e.y2} c -{e.x0},-{e.y1} -{e.x1},-{e.y1} -{e.x2},0c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 m -{e.x2},{e.y0}c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0m -{e.x2},{e.y0}c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0",height:61,width:61,heightElements:[7,10,45],widthElements:[2,58,60]},TEXT_ANNOTATION:{d:"m {mx}, {my} m 10,0 l -10,0 l 0,{e.y0} l 10,0",height:30,width:10,heightElements:[30],widthElements:[10]},MARKER_SUB_PROCESS:{d:"m{mx},{my} m 7,2 l 0,10 m -5,-5 l 10,0",height:10,width:10,heightElements:[],widthElements:[]},MARKER_PARALLEL:{d:"m{mx},{my} m 3,2 l 0,10 m 3,-10 l 0,10 m 3,-10 l 0,10",height:10,width:10,heightElements:[],widthElements:[]},MARKER_SEQUENTIAL:{d:"m{mx},{my} m 0,3 l 10,0 m -10,3 l 10,0 m -10,3 l 10,0",height:10,width:10,heightElements:[],widthElements:[]},MARKER_COMPENSATION:{d:"m {mx},{my} 7,-5 0,10 z m 7.1,-0.3 6.9,-4.7 0,10 -6.9,-4.7 z",height:10,width:21,heightElements:[],widthElements:[]},MARKER_LOOP:{d:"m {mx},{my} c 3.526979,0 6.386161,-2.829858 6.386161,-6.320661 0,-3.490806 -2.859182,-6.320661 -6.386161,-6.320661 -3.526978,0 -6.38616,2.829855 -6.38616,6.320661 0,1.745402 0.714797,3.325567 1.870463,4.469381 0.577834,0.571908 1.265885,1.034728 2.029916,1.35457 l -0.718163,-3.909793 m 0.718163,3.909793 -3.885211,0.802902",height:13.9,width:13.7,heightElements:[],widthElements:[]},MARKER_ADHOC:{d:"m {mx},{my} m 0.84461,2.64411 c 1.05533,-1.23780996 2.64337,-2.07882 4.29653,-1.97997996 2.05163,0.0805 3.85579,1.15803 5.76082,1.79107 1.06385,0.34139996 2.24454,0.1438 3.18759,-0.43767 0.61743,-0.33642 1.2775,-0.64078 1.7542,-1.17511 0,0.56023 0,1.12046 0,1.6807 -0.98706,0.96237996 -2.29792,1.62393996 -3.6918,1.66181996 -1.24459,0.0927 -2.46671,-0.2491 -3.59505,-0.74812 -1.35789,-0.55965 -2.75133,-1.33436996 -4.27027,-1.18121996 -1.37741,0.14601 -2.41842,1.13685996 -3.44288,1.96782996 z",height:4,width:15,heightElements:[],widthElements:[]},TASK_TYPE_SEND:{d:"m {mx},{my} l 0,{e.y1} l {e.x1},0 l 0,-{e.y1} z l {e.x0},{e.y0} l {e.x0},-{e.y0}",height:14,width:21,heightElements:[6,14],widthElements:[10.5,21]},TASK_TYPE_SCRIPT:{d:"m {mx},{my} c 9.966553,-6.27276 -8.000926,-7.91932 2.968968,-14.938 l -8.802728,0 c -10.969894,7.01868 6.997585,8.66524 -2.968967,14.938 z m -7,-12 l 5,0 m -4.5,3 l 4.5,0 m -3,3 l 5,0m -4,3 l 5,0",height:15,width:12.6,heightElements:[6,14],widthElements:[10.5,21]},TASK_TYPE_USER_1:{d:"m {mx},{my} c 0.909,-0.845 1.594,-2.049 1.594,-3.385 0,-2.554 -1.805,-4.62199999 -4.357,-4.62199999 -2.55199998,0 -4.28799998,2.06799999 -4.28799998,4.62199999 0,1.348 0.974,2.562 1.89599998,3.405 -0.52899998,0.187 -5.669,2.097 -5.794,4.7560005 v 6.718 h 17 v -6.718 c 0,-2.2980005 -5.5279996,-4.5950005 -6.0509996,-4.7760005 zm -8,6 l 0,5.5 m 11,0 l 0,-5"},TASK_TYPE_USER_2:{d:"m {mx},{my} m 2.162,1.009 c 0,2.4470005 -2.158,4.4310005 -4.821,4.4310005 -2.66499998,0 -4.822,-1.981 -4.822,-4.4310005 "},TASK_TYPE_USER_3:{d:"m {mx},{my} m -6.9,-3.80 c 0,0 2.25099998,-2.358 4.27399998,-1.177 2.024,1.181 4.221,1.537 4.124,0.965 -0.098,-0.57 -0.117,-3.79099999 -4.191,-4.13599999 -3.57499998,0.001 -4.20799998,3.36699999 -4.20699998,4.34799999 z"},TASK_TYPE_MANUAL:{d:"m {mx},{my} c 0.234,-0.01 5.604,0.008 8.029,0.004 0.808,0 1.271,-0.172 1.417,-0.752 0.227,-0.898 -0.334,-1.314 -1.338,-1.316 -2.467,-0.01 -7.886,-0.004 -8.108,-0.004 -0.014,-0.079 0.016,-0.533 0,-0.61 0.195,-0.042 8.507,0.006 9.616,0.002 0.877,-0.007 1.35,-0.438 1.353,-1.208 0.003,-0.768 -0.479,-1.09 -1.35,-1.091 -2.968,-0.002 -9.619,-0.013 -9.619,-0.013 v -0.591 c 0,0 5.052,-0.016 7.225,-0.016 0.888,-0.002 1.354,-0.416 1.351,-1.193 -0.006,-0.761 -0.492,-1.196 -1.361,-1.196 -3.473,-0.005 -10.86,-0.003 -11.0829995,-0.003 -0.022,-0.047 -0.045,-0.094 -0.069,-0.139 0.3939995,-0.319 2.0409995,-1.626 2.4149995,-2.017 0.469,-0.4870005 0.519,-1.1650005 0.162,-1.6040005 -0.414,-0.511 -0.973,-0.5 -1.48,-0.236 -1.4609995,0.764 -6.5999995,3.6430005 -7.7329995,4.2710005 -0.9,0.499 -1.516,1.253 -1.882,2.19 -0.37000002,0.95 -0.17,2.01 -0.166,2.979 0.004,0.718 -0.27300002,1.345 -0.055,2.063 0.629,2.087 2.425,3.312 4.859,3.318 4.6179995,0.014 9.2379995,-0.139 13.8569995,-0.158 0.755,-0.004 1.171,-0.301 1.182,-1.033 0.012,-0.754 -0.423,-0.969 -1.183,-0.973 -1.778,-0.01 -5.824,-0.004 -6.04,-0.004 10e-4,-0.084 0.003,-0.586 10e-4,-0.67 z"},TASK_TYPE_INSTANTIATING_SEND:{d:"m {mx},{my} l 0,8.4 l 12.6,0 l 0,-8.4 z l 6.3,3.6 l 6.3,-3.6"},TASK_TYPE_SERVICE:{d:"m {mx},{my} v -1.71335 c 0.352326,-0.0705 0.703932,-0.17838 1.047628,-0.32133 0.344416,-0.14465 0.665822,-0.32133 0.966377,-0.52145 l 1.19431,1.18005 1.567487,-1.57688 -1.195028,-1.18014 c 0.403376,-0.61394 0.683079,-1.29908 0.825447,-2.01824 l 1.622133,-0.01 v -2.2196 l -1.636514,0.01 c -0.07333,-0.35153 -0.178319,-0.70024 -0.323564,-1.04372 -0.145244,-0.34406 -0.321407,-0.6644 -0.522735,-0.96217 l 1.131035,-1.13631 -1.583305,-1.56293 -1.129598,1.13589 c -0.614052,-0.40108 -1.302883,-0.68093 -2.022633,-0.82247 l 0.0093,-1.61852 h -2.241173 l 0.0042,1.63124 c -0.353763,0.0736 -0.705369,0.17977 -1.049785,0.32371 -0.344415,0.14437 -0.665102,0.32092 -0.9635006,0.52046 l -1.1698628,-1.15823 -1.5667691,1.5792 1.1684265,1.15669 c -0.4026573,0.61283 -0.68308,1.29797 -0.8247287,2.01713 l -1.6588041,0.003 v 2.22174 l 1.6724648,-0.006 c 0.073327,0.35077 0.1797598,0.70243 0.3242851,1.04472 0.1452428,0.34448 0.3214064,0.6644 0.5227339,0.96066 l -1.1993431,1.19723 1.5840256,1.56011 1.1964668,-1.19348 c 0.6140517,0.40346 1.3028827,0.68232 2.0233517,0.82331 l 7.19e-4,1.69892 h 2.226848 z m 0.221462,-3.9957 c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z"},TASK_TYPE_SERVICE_FILL:{d:"m {mx},{my} c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z"},TASK_TYPE_BUSINESS_RULE_HEADER:{d:"m {mx},{my} 0,4 20,0 0,-4 z"},TASK_TYPE_BUSINESS_RULE_MAIN:{d:"m {mx},{my} 0,12 20,0 0,-12 zm 0,8 l 20,0 m -13,-4 l 0,8"},MESSAGE_FLOW_MARKER:{d:"m {mx},{my} m -10.5 ,-7 l 0,14 l 21,0 l 0,-14 z l 10.5,6 l 10.5,-6"}},this.getRawPath=function(e){return this.pathMap[e].d},this.getScaledPath=function(e,t){var n,i,r=this.pathMap[e];t.abspos?(n=t.abspos.x,i=t.abspos.y):(n=t.containerWidth*t.position.mx,i=t.containerHeight*t.position.my);var o={};if(t.position){for(var a=t.containerHeight/r.height*t.yScaleFactor,s=t.containerWidth/r.width*t.xScaleFactor,l=0;l':""}function vt(e,t,n){return E({id:e.id,type:e.$type,businessObject:e,di:t},n)}function xt(e,t,n){var i=e.waypoint;return!i||i.length<2?[yt(t),yt(n)]:i.map((function(e){return{x:e.x,y:e.y}}))}function bt(e,t,n,i){return new Error(e("element {element} referenced by {referenced}#{property} not yet drawn",{element:gt(n),referenced:gt(t),property:i}))}function wt(e,t,n,i,r,o){this._eventBus=e,this._canvas=t,this._elementFactory=n,this._elementRegistry=i,this._translate=r,this._textRenderer=o}wt.$inject=["eventBus","canvas","elementFactory","elementRegistry","translate","textRenderer"],wt.prototype.add=function(e,t,n){var i,r,o,a,s,l,p,c=this._translate;if(A(t,"bpmndi:BPMNPlane")){var u=A(e,"bpmn:SubProcess")?{id:e.id+"_plane"}:{};i=this._elementFactory.createRoot(vt(e,t,u)),this._canvas.addRootElement(i)}else if(A(t,"bpmndi:BPMNShape")){var h=!C(e,t),m=function(e){return A(e,"bpmn:Group")}(e);r=n&&(n.hidden||n.collapsed);var f=t.bounds;i=this._elementFactory.createShape(vt(e,t,{collapsed:h,hidden:r,x:Math.round(f.x),y:Math.round(f.y),width:Math.round(f.width),height:Math.round(f.height),isFrame:m})),A(e,"bpmn:BoundaryEvent")&&this._attachBoundary(e,i),A(e,"bpmn:Lane")&&(o=0),A(e,"bpmn:DataStoreReference")&&(a=n,s=yt(f),l=s.x,p=s.y,l>=a.x&&l<=a.x+a.width&&p>=a.y&&p<=a.y+a.height||(n=this._canvas.findRoot(n))),this._canvas.addShape(i,n,o)}else{if(!A(t,"bpmndi:BPMNEdge"))throw new Error(c("unknown di {di} for element {semantic}",{di:gt(t),semantic:gt(e)}));var d=this._getSource(e),y=this._getTarget(e);r=n&&(n.hidden||n.collapsed),i=this._elementFactory.createConnection(vt(e,t,{hidden:r,source:d,target:y,waypoints:xt(t,d,y)})),A(e,"bpmn:DataAssociation")&&(n=this._canvas.findRoot(n)),this._canvas.addConnection(i,n,o)}return function(e){return A(e,"bpmn:Event")||A(e,"bpmn:Gateway")||A(e,"bpmn:DataStoreReference")||A(e,"bpmn:DataObjectReference")||A(e,"bpmn:DataInput")||A(e,"bpmn:DataOutput")||A(e,"bpmn:SequenceFlow")||A(e,"bpmn:MessageFlow")||A(e,"bpmn:Group")}(e)&&M(i)&&this.addLabel(e,t,i),this._eventBus.fire("bpmnElement.added",{element:i}),i},wt.prototype._attachBoundary=function(e,t){var n=this._translate,i=e.attachedToRef;if(!i)throw new Error(n("missing {semantic}#attachedToRef",{semantic:gt(e)}));var r=this._elementRegistry.get(i.id),o=r&&r.attachers;if(!r)throw bt(n,e,i,"attachedToRef");t.host=r,o||(r.attachers=o=[]),-1===o.indexOf(t)&&o.push(t)},wt.prototype.addLabel=function(e,t,n){var i,r,o;return i=ct(t,n),(r=M(n))&&(i=this._textRenderer.getExternalLabelBounds(i,r)),o=this._elementFactory.createLabel(vt(e,t,{id:e.id+"_label",labelTarget:n,type:"label",hidden:n.hidden||!M(n),x:Math.round(i.x),y:Math.round(i.y),width:Math.round(i.width),height:Math.round(i.height)})),this._canvas.addShape(o,n.parent)},wt.prototype._getEnd=function(e,t){var n,i,r=e.$type,o=this._translate;if(i=e[t+"Ref"],"source"===t&&"bpmn:DataInputAssociation"===r&&(i=i&&i[0]),("source"===t&&"bpmn:DataOutputAssociation"===r||"target"===t&&"bpmn:DataInputAssociation"===r)&&(i=e.$parent),n=i&&this._getElement(i))return n;throw i?bt(o,e,i,t+"Ref"):new Error(o("{semantic}#{side} Ref not specified",{semantic:gt(e),side:t}))},wt.prototype._getSource=function(e){return this._getEnd(e,"source")},wt.prototype._getTarget=function(e){return this._getEnd(e,"target")},wt.prototype._getElement=function(e){return this._elementRegistry.get(e.id)};var Et={__depends__:[at,{__depends__:[st],bpmnImporter:["type",wt]}]};function _t(e){return e.originalEvent||e.srcEvent}function At(e,t){return(_t(e)||e).button===t}function St(e){return At(e,0)}function Rt(e){var t=_t(e)||e;return!!St(e)&&(/mac/i.test(navigator.platform)?t.metaKey:t.ctrlKey)}function Ct(e){return!0}function Mt(e){return St(e)||function(e){return At(e,1)}(e)}function kt(e,t,n){var i=this;function r(n,i,r){var o,a;(function(e,t){return!(l[e]||St)(t)})(n,i)||(r?a=t.getGraphics(r):(o=i.delegateTarget||i.target)&&(a=o,r=t.get(a)),a&&r&&!1===e.fire(n,{element:r,gfx:a,originalEvent:i})&&(i.stopPropagation(),i.preventDefault()))}var o={};function a(e){return o[e]}var s={click:"element.click",contextmenu:"element.contextmenu",dblclick:"element.dblclick",mousedown:"element.mousedown",mousemove:"element.mousemove",mouseover:"element.hover",mouseout:"element.out",mouseup:"element.mouseup"},l={"element.contextmenu":Ct,"element.mousedown":Mt,"element.mouseup":Mt,"element.click":Mt,"element.dblclick":Mt};function p(e,t,n,i){var a=o[n]=function(e){r(n,e)};i&&(l[n]=i),a.$delegate=je.bind(e,"svg, .djs-element",t,a)}function c(e,t,n){var i=a(n);i&&je.unbind(e,t,i.$delegate)}e.on("canvas.destroy",(function(e){var t;t=e.svg,h(s,(function(e,n){c(t,n,e)}))})),e.on("canvas.init",(function(e){var t;t=e.svg,h(s,(function(e,n){p(t,n,e)}))})),e.on(["shape.added","connection.added"],(function(t){var n=t.element,i=t.gfx;e.fire("interactionEvents.createHit",{element:n,gfx:i})})),e.on(["shape.changed","connection.changed"],500,(function(t){var n=t.element,i=t.gfx;e.fire("interactionEvents.updateHit",{element:n,gfx:i})})),e.on("interactionEvents.createHit",500,(function(e){var t=e.element,n=e.gfx;i.createDefaultHit(t,n)})),e.on("interactionEvents.updateHit",(function(e){var t=e.element,n=e.gfx;i.updateDefaultHit(t,n)}));var u=d("djs-hit djs-hit-stroke"),m=d("djs-hit djs-hit-click-stroke"),f={all:d("djs-hit djs-hit-all"),"click-stroke":m,stroke:u,"no-move":d("djs-hit djs-hit-no-move")};function d(e,t){return t=E({stroke:"white",strokeWidth:15},t||{}),n.cls(e,["no-fill","no-border"],t)}function y(e,t){var n=f[t];if(!n)throw new Error("invalid hit type <"+t+">");return N(e,n),e}function g(e,t){P(e,t)}this.removeHits=function(e){var t;h((t=".djs-hit",(e||document).querySelectorAll(t)),V)},this.createDefaultHit=function(e,t){var n,i=e.waypoints,r=e.isFrame;return i?this.createWaypointsHit(t,i):(n=r?"stroke":"all",this.createBoxHit(t,n,{width:e.width,height:e.height}))},this.createWaypointsHit=function(e,t){var n=se(t);return y(n,"stroke"),g(e,n),n},this.createBoxHit=function(e,t,n){n=E({x:0,y:0},n);var i=H("rect");return y(i,t),N(i,n),g(e,i),i},this.updateDefaultHit=function(e,t){var n=We(".djs-hit",t);if(n)return e.waypoints?function(e,t){N(e,{points:ae(t)})}(n,e.waypoints):N(n,{width:e.width,height:e.height}),n},this.fire=r,this.triggerMouseEvent=function(e,t,n){var i=s[e];if(!i)throw new Error("unmapped DOM event name <"+e+">");return r(i,t,n)},this.mouseHandler=a,this.registerEvent=p,this.unregisterEvent=c}kt.$inject=["eventBus","elementRegistry","styles"];var Pt={__init__:["interactionEvents"],interactionEvents:["type",kt]};function Tt(e,t){var n,i,o,a;return t=!!t,r(e)||(e=[e]),h(e,(function(e){var r=e;e.waypoints&&!t&&(r=Tt(e.waypoints,!0));var s=r.x,l=r.y,p=r.height||0,c=r.width||0;(so||void 0===o)&&(o=s+c),(l+p>a||void 0===a)&&(a=l+p)})),{x:n,y:i,height:a-i,width:o-n}}function Dt(e){return"waypoints"in e?"connection":"x"in e?"shape":"root"}function Nt(e){return!(!e||!e.isFrame)}function Ot(e,t,n){this.offset=6;var i=t.cls("djs-outline",["no-fill"]),r=this;function o(e,t){var n=H("rect");return N(n,E({x:10,y:10,rx:3,width:100,height:100},i)),P(e,n),n}e.on(["shape.added","shape.changed"],500,(function(e){var t=e.element,n=e.gfx,i=We(".djs-outline",n);i||(i=o(n)),r.updateShapeOutline(i,t)})),e.on(["connection.added","connection.changed"],(function(e){var t=e.element,n=e.gfx,i=We(".djs-outline",n);i||(i=o(n)),r.updateConnectionOutline(i,t)}))}Ot.prototype.updateShapeOutline=function(e,t){N(e,{x:-this.offset,y:-this.offset,width:t.width+2*this.offset,height:t.height+2*this.offset})},Ot.prototype.updateConnectionOutline=function(e,t){var n=Tt(t);N(e,{x:n.x-this.offset,y:n.y-this.offset,width:n.width+2*this.offset,height:n.height+2*this.offset})},Ot.$inject=["eventBus","styles","elementRegistry"];var Bt={__init__:["outline"],outline:["type",Ot]};function Lt(e,t){this._eventBus=e,this._canvas=t,this._selectedElements=[];var n=this;e.on(["shape.remove","connection.remove"],(function(e){var t=e.element;n.deselect(t)})),e.on(["diagram.clear","root.set"],(function(e){n.select(null)}))}Lt.$inject=["eventBus","canvas"],Lt.prototype.deselect=function(e){var t=this._selectedElements,n=t.indexOf(e);if(-1!==n){var i=t.slice();t.splice(n,1),this._eventBus.fire("selection.changed",{oldSelection:i,newSelection:t})}},Lt.prototype.get=function(){return this._selectedElements},Lt.prototype.isSelected=function(e){return-1!==this._selectedElements.indexOf(e)},Lt.prototype.select=function(e,t){var n=this._selectedElements,i=n.slice();r(e)||(e=e?[e]:[]);var o=this._canvas,a=o.getRootElement();e=e.filter((function(e){var t=o.findRoot(e);return a===t})),t?h(e,(function(e){-1===n.indexOf(e)&&n.push(e)})):this._selectedElements=n=e.slice(),this._eventBus.fire("selection.changed",{oldSelection:i,newSelection:n})};var It="hover",jt="selected";function Ft(e,t,n){this._canvas=e;var i=this;function r(t,n){e.addMarker(t,n)}function o(t,n){e.removeMarker(t,n)}this._multiSelectionBox=null,t.on("element.hover",(function(e){r(e.element,It)})),t.on("element.out",(function(e){o(e.element,It)})),t.on("selection.changed",(function(e){var t=e.oldSelection,n=e.newSelection;h(t,(function(e){-1===n.indexOf(e)&&o(e,jt)})),h(n,(function(e){-1===t.indexOf(e)&&r(e,jt)})),i._updateSelectionOutline(n)})),t.on("element.changed",(function(e){n.isSelected(e.element)&&i._updateSelectionOutline(n.get())}))}function Vt(e,t,n,i){e.on("create.end",500,(function(e){var n=e.context,i=n.canExecute,o=n.elements,a=(n.hints||{}).autoSelect;if(i){if(!1===a)return;r(a)?t.select(a):t.select(o.filter(zt))}})),e.on("connect.end",500,(function(e){var n=e.context.connection;n&&t.select(n)})),e.on("shape.move.end",500,(function(e){var n=e.previousSelection||[],r=i.get(e.context.shape.id);c(n,(function(e){return r.id===e.id}))||t.select(r)})),e.on("element.click",(function(e){if(St(e)){var i=e.element;i===n.getRootElement()&&(i=null);var r=t.isSelected(i),o=t.get().length>1,a=Rt(e)||function(e){var t=_t(e)||e;return St(e)&&t.shiftKey}(e);if(r&&o)return a?t.deselect(i):t.select(i);r?t.deselect(i):t.select(i,a)}}))}function zt(e){return!e.hidden}Ft.$inject=["canvas","eventBus","selection"],Ft.prototype._updateSelectionOutline=function(e){var t=this._canvas.getLayer("selectionOutline");z(t);var n=e.length>1;if(j(this._canvas.getContainer())[n?"add":"remove"]("djs-multi-select"),n){var i=function(e){return{x:e.x-6,y:e.y-6,width:e.width+12,height:e.height+12}}(Tt(e)),r=H("rect");N(r,E({rx:3},i)),j(r).add("djs-selection-outline"),P(t,r)}},Vt.$inject=["eventBus","selection","canvas","elementRegistry"];var Wt={__init__:["selectionVisuals","selectionBehavior"],__depends__:[Pt,Bt],selection:["type",Lt],selectionVisuals:["type",Ft],selectionBehavior:["type",Vt]};function Gt(e){this._counter=0,this._prefix=(e?e+"-":"")+Math.floor(1e9*Math.random())+"-"}Gt.prototype.next=function(){return this._prefix+ ++this._counter};var $t=new Gt("ov");function Ht(e,t,n,i){var r,o;this._eventBus=t,this._canvas=n,this._elementRegistry=i,this._ids=$t,this._overlayDefaults=E({show:null,scale:!0},e&&e.defaults),this._overlays={},this._overlayContainers=[],this._overlayRoot=(r=n.getContainer(),be(o=Fe('
            '),{position:"absolute",width:0,height:0}),r.insertBefore(o,r.firstChild),o),this._init()}function Kt(e,t,n){be(e,{left:t+"px",top:n+"px"})}function Ut(e,t){e.style.display=!1===t?"none":""}function qt(e,t){e.style["transform-origin"]="top left",["","-ms-","-webkit-"].forEach((function(n){e.style[n+"transform"]=t}))}Ht.$inject=["config.overlays","eventBus","canvas","elementRegistry"],Ht.prototype.get=function(e){if(l(e)&&(e={id:e}),l(e.element)&&(e.element=this._elementRegistry.get(e.element)),e.element){var t=this._getOverlayContainer(e.element,!0);return t?e.type?u(t.overlays,y({type:e.type})):t.overlays.slice():[]}return e.type?u(this._overlays,y({type:e.type})):e.id?this._overlays[e.id]:null},Ht.prototype.add=function(e,t,n){if(o(t)&&(n=t,t=null),e.id||(e=this._elementRegistry.get(e)),!n.position)throw new Error("must specifiy overlay position");if(!n.html)throw new Error("must specifiy overlay html");if(!e)throw new Error("invalid element specified");var i=this._ids.next();return n=E({},this._overlayDefaults,n,{id:i,type:t,element:e,html:n.html}),this._addOverlay(n),i},Ht.prototype.remove=function(e){var t=this.get(e)||[];r(t)||(t=[t]);var n=this;h(t,(function(e){var t=n._getOverlayContainer(e.element,!0);if(e&&(Ge(e.html),Ge(e.htmlContainer),delete e.htmlContainer,delete e.element,delete n._overlays[e.id]),t){var i=t.overlays.indexOf(e);-1!==i&&t.overlays.splice(i,1)}}))},Ht.prototype.show=function(){Ut(this._overlayRoot)},Ht.prototype.hide=function(){Ut(this._overlayRoot,!1)},Ht.prototype.clear=function(){this._overlays={},this._overlayContainers=[],Ce(this._overlayRoot)},Ht.prototype._updateOverlayContainer=function(e){var t=e.element,n=e.html,i=t.x,r=t.y;if(t.waypoints){var o=Tt(t);i=o.x,r=o.y}Kt(n,i,r),function(e,t,n){2==arguments.length?e.getAttribute(t):null===n?e.removeAttribute(t):e.setAttribute(t,n)}(e.html,"data-container-id",t.id)},Ht.prototype._updateOverlay=function(e){var t,n,i=e.position,r=e.htmlContainer,o=e.element,a=i.left,s=i.top;void 0!==i.right&&(t=o.waypoints?Tt(o).width:o.width,a=-1*i.right+t);void 0!==i.bottom&&(n=o.waypoints?Tt(o).height:o.height,s=-1*i.bottom+n);Kt(r,a||0,s||0),this._updateOverlayVisibilty(e,this._canvas.viewbox())},Ht.prototype._createOverlayContainer=function(e){var t=Fe('
            ');be(t,{position:"absolute"}),this._overlayRoot.appendChild(t);var n={html:t,element:e,overlays:[]};return this._updateOverlayContainer(n),this._overlayContainers.push(n),n},Ht.prototype._updateRoot=function(e){var t=e.scale||1,n="matrix("+[t,0,0,t,-1*e.x*t,-1*e.y*t].join(",")+")";qt(this._overlayRoot,n)},Ht.prototype._getOverlayContainer=function(e,t){var n=c(this._overlayContainers,(function(t){return t.element===e}));return n||t?n:this._createOverlayContainer(e)},Ht.prototype._addOverlay=function(e){var t,n,i=e.id,r=e.element,o=e.html;o.get&&o.constructor.prototype.jquery&&(o=o.get(0)),l(o)&&(o=Fe(o)),n=this._getOverlayContainer(r),be(t=Fe('
            '),{position:"absolute"}),t.appendChild(o),e.type&&Se(t).add("djs-overlay-"+e.type),Ut(t,this._canvas.findRoot(r)===this._canvas.getRootElement()),e.htmlContainer=t,n.overlays.push(e),n.html.appendChild(t),this._overlays[i]=e,this._updateOverlay(e),this._updateOverlayVisibilty(e,this._canvas.viewbox())},Ht.prototype._updateOverlayVisibilty=function(e,t){var n=e.show,r=this._canvas.findRoot(e.element),o=n&&n.minZoom,a=n&&n.maxZoom,s=e.htmlContainer,l=!0;(r!==this._canvas.getRootElement()||n&&(i(o)&&o>t.scale||i(a)&&ar&&(o=(1/t.scale||1)*r)),i(o)&&(l="scale("+o+","+o+")"),qt(s,l)},Ht.prototype._updateOverlaysVisibilty=function(e){var t=this;h(this._overlays,(function(n){t._updateOverlayVisibilty(n,e)}))},Ht.prototype._init=function(){var e=this._eventBus,t=this;e.on("canvas.viewbox.changing",(function(e){t.hide()})),e.on("canvas.viewbox.changed",(function(e){var n;n=e.viewbox,t._updateRoot(n),t._updateOverlaysVisibilty(n),t.show()})),e.on(["shape.remove","connection.remove"],(function(e){var n=e.element;h(t.get({element:n}),(function(e){t.remove(e.id)}));var i=t._getOverlayContainer(n);if(i){Ge(i.html);var r=t._overlayContainers.indexOf(i);-1!==r&&t._overlayContainers.splice(r,1)}})),e.on("element.changed",500,(function(e){var n=e.element,i=t._getOverlayContainer(n,!0);i&&(h(i.overlays,(function(e){t._updateOverlay(e)})),t._updateOverlayContainer(i))})),e.on("element.marker.update",(function(e){var n=t._getOverlayContainer(e.element,!0);n&&Se(n.html)[e.add?"add":"remove"](e.marker)})),e.on("root.set",(function(){t._updateOverlaysVisibilty(t._canvas.viewbox())})),e.on("diagram.clear",this.clear,this)};var Yt={__init__:["overlays"],overlays:["type",Ht]};function Xt(e,t,n,i){e.on("element.changed",(function(i){var r=i.element;(r.parent||r===t.getRootElement())&&(i.gfx=n.getGraphics(r)),i.gfx&&e.fire(Dt(r)+".changed",i)})),e.on("elements.changed",(function(t){var n=t.elements;n.forEach((function(t){e.fire("element.changed",{element:t})})),i.updateContainments(n)})),e.on("shape.changed",(function(e){i.update("shape",e.element,e.gfx)})),e.on("connection.changed",(function(e){i.update("connection",e.element,e.gfx)}))}Xt.$inject=["eventBus","canvas","elementRegistry","graphicsFactory"];var Zt={__init__:["changeSupport"],changeSupport:["type",Xt]};function Jt(e){this._eventBus=e}Jt.$inject=["eventBus"],Jt.prototype.on=function(e,t,n,i,l,p){if((s(t)||a(t))&&(p=l,l=i,i=n,n=t,t=null),s(n)&&(p=l,l=i,i=n,n=1e3),o(l)&&(p=l,l=!1),!s(i))throw new Error("handlerFn must be a function");r(e)||(e=[e]);var c=this._eventBus;h(e,(function(e){var r=["commandStack",e,t].filter((function(e){return e})).join(".");c.on(r,n,l?function(e,t){return function(n){return e.call(t||null,n.context,n.command,n)}}(i,p):i,p)}))};function Qt(e,t){t.invoke(Jt,this),this.executed((function(t){var n=t.context;n.rootElement?e.setRootElement(n.rootElement):n.rootElement=e.getRootElement()})),this.revert((function(t){var n=t.context;n.rootElement&&e.setRootElement(n.rootElement)}))}h(["canExecute","preExecute","preExecuted","execute","executed","postExecute","postExecuted","revert","reverted"],(function(e){Jt.prototype[e]=function(t,n,i,r,o){(s(t)||a(t))&&(o=r,r=i,i=n,n=t,t=null),this.on(t,e,n,i,r,o)}})),e(Qt,Jt),Qt.$inject=["canvas","injector"];var en={__init__:["rootElementsBehavior"],rootElementsBehavior:["type",Qt]}; +/*! https://mths.be/cssescape v1.5.1 by @mathias | MIT license */ +!function(e,t){var n;n=ut,e.exports=function(e){if(e.CSS&&e.CSS.escape)return e.CSS.escape;var t=function(e){if(0==arguments.length)throw new TypeError("`CSS.escape` requires an argument.");for(var t,n=String(e),i=n.length,r=-1,o="",a=n.charCodeAt(0);++r=1&&t<=31||127==t||0==r&&t>=48&&t<=57||1==r&&t>=48&&t<=57&&45==a?"\\"+t.toString(16)+" ":0==r&&1==i&&45==t||!(t>=128||45==t||95==t||t>=48&&t<=57||t>=65&&t<=90||t>=97&&t<=122)?"\\"+n.charAt(r):n.charAt(r):o+="�";return o};return e.CSS||(e.CSS={}),e.CSS.escape=t,t}(n)}({exports:{}});var tn={"&":"&","<":"<",">":">",'"':""","'":"'"};var nn="_plane";function rn(e){var t=e.id;return A(e,"bpmn:SubProcess")?function(e){return e+nn}(t):t}function on(e,t,n,i){var r=Fe('
              '),o=i.getContainer(),a=Se(o);o.appendChild(r);var s=[];function l(e){e&&(s=function(e){for(var t=S(e),n=[],i=t;i;i=i.$parent)(A(i,"bpmn:SubProcess")||A(i,"bpmn:Process"))&&n.push(i);return n.reverse()}(e));var n=s.map((function(e){var n,r=(n=""+(n=e.name||e.id))&&n.replace(/[&<>"']/g,(function(e){return tn[e]})),o=Fe('
            • '+r+"
            • "),a=i.findRoot(rn(e))||i.findRoot(e.id);if(!a&&A(e,"bpmn:Process")){var s=t.find((function(t){var n=S(t);return n&&n.processRef&&n.processRef===e}));a=i.findRoot(s.id)}return o.addEventListener("click",(function(){i.setRootElement(a)})),o}));r.innerHTML="";var o=n.length>1;a.toggle("bjs-breadcrumbs-shown",o),n.forEach((function(e){r.appendChild(e)}))}e.on("element.changed",(function(e){var t=S(e.element);c(s,(function(e){return e===t}))&&l()})),e.on("root.set",(function(e){l(e.element)}))}function an(e,t){var n=null,i=new sn;e.on("root.set",(function(e){var r=e.element,o=t.viewbox(),a=i.get(r);if(i.set(n,{x:o.x,y:o.y,zoom:o.scale}),n=r,!A(r,"bpmn:Collaboration")||a){a=a||{x:0,y:0,zoom:1};var s=(o.x-a.x)*o.scale,l=(o.y-a.y)*o.scale;0===s&&0===l||t.scroll({dx:s,dy:l}),a.zoom!==o.scale&&t.zoom(a.zoom,{x:0,y:0})}})),e.on("diagram.clear",(function(){i.clear(),n=null}))}function sn(){this._entries=[],this.set=function(e,t){var n=!1;for(var i in this._entries)if(this._entries[i][0]===e){this._entries[i][1]=t,n=!0;break}n||this._entries.push([e,t])},this.get=function(e){for(var t in this._entries)if(this._entries[t][0]===e)return this._entries[t][1];return null},this.clear=function(){this._entries.length=0},this.remove=function(e){var t=-1;for(var n in this._entries)if(this._entries[n][0]===e){t=n;break}-1!==t&&this._entries.splice(t,1)}}on.$inject=["eventBus","elementRegistry","overlays","canvas"],an.$inject=["eventBus","canvas"];var ln=180,pn=160;function cn(e,t){this._eventBus=e,this._moddle=t;var n=this;e.on("import.render.start",1500,(function(e,t){n.handleImport(t.definitions)}))}function un(e){return A(e,"bpmndi:BPMNDiagram")?e:un(e.$parent)}cn.prototype.handleImport=function(e){if(e.diagrams){var t=this;this._definitions=e,this._processToDiagramMap={},e.diagrams.forEach((function(e){e.plane&&e.plane.bpmnElement&&(t._processToDiagramMap[e.plane.bpmnElement.id]=e)}));var n=[];e.diagrams.forEach((function(e){var i=t.createNewDiagrams(e.plane);Array.prototype.push.apply(n,i)})),n.forEach((function(e){t.movePlaneElementsToOrigin(e.plane)}))}},cn.prototype.createNewDiagrams=function(e){var t=this,n=[],i=[];e.get("planeElement").forEach((function(t){var r=t.bpmnElement;if(r){var o=r.$parent;A(r,"bpmn:SubProcess")&&!t.isExpanded&&n.push(r),function(e,t){var n=e.$parent;if(!A(n,"bpmn:SubProcess")||n===t.bpmnElement)return!1;if(function(e,t){return d(t,(function(t){return A(e,t)}))}(e,["bpmn:DataInputAssociation","bpmn:DataOutputAssociation"]))return!1;return!0}(r,e)&&i.push({diElement:t,parent:o})}}));var r=[];return n.forEach((function(e){if(!t._processToDiagramMap[e.id]){var n=t.createDiagram(e);t._processToDiagramMap[e.id]=n,r.push(n)}})),i.forEach((function(e){for(var i=e.diElement,r=e.parent;r&&-1===n.indexOf(r);)r=r.$parent;if(r){var o=t._processToDiagramMap[r.id];t.moveToDiPlane(i,o.plane)}})),r},cn.prototype.movePlaneElementsToOrigin=function(e){var t=e.get("planeElement"),n=function(e){var t={top:1/0,right:-1/0,bottom:-1/0,left:1/0};return e.planeElement.forEach((function(e){if(e.bounds){var n=mt(e.bounds);t.top=Math.min(n.top,t.top),t.left=Math.min(n.left,t.left)}})),function(e){return{x:e.left,y:e.top,width:e.right-e.left,height:e.bottom-e.top}}(t)}(e),i=n.x-ln,r=n.y-pn;t.forEach((function(e){e.waypoint?e.waypoint.forEach((function(e){e.x=e.x-i,e.y=e.y-r})):e.bounds&&(e.bounds.x=e.bounds.x-i,e.bounds.y=e.bounds.y-r)}))},cn.prototype.moveToDiPlane=function(e,t){var n=un(e).plane.get("planeElement");n.splice(n.indexOf(e),1),t.get("planeElement").push(e)},cn.prototype.createDiagram=function(e){var t=this._moddle.create("bpmndi:BPMNPlane",{bpmnElement:e}),n=this._moddle.create("bpmndi:BPMNDiagram",{plane:t});return t.$parent=n,t.bpmnElement=e,n.$parent=this._definitions,this._definitions.diagrams.push(n),n},cn.$inject=["eventBus","moddle"];var hn=250;function mn(e,t,n,i){Jt.call(this,t),this._canvas=e,this._eventBus=t,this._elementRegistry=n,this._overlays=i;var r=this;this.executed("shape.toggleCollapse",hn,(function(e){var t=e.shape;r.canDrillDown(t)?r.addOverlay(t):r.removeOverlay(t)}),!0),this.reverted("shape.toggleCollapse",hn,(function(e){var t=e.shape;r.canDrillDown(t)?r.addOverlay(t):r.removeOverlay(t)}),!0),this.executed(["shape.create","shape.move","shape.delete"],hn,(function(e){var t=e.oldParent,n=e.newParent||e.parent,i=e.shape;r.canDrillDown(i)&&r.addOverlay(i),r.updateDrilldownOverlay(t),r.updateDrilldownOverlay(n),r.updateDrilldownOverlay(i)}),!0),this.reverted(["shape.create","shape.move","shape.delete"],hn,(function(e){var t=e.oldParent,n=e.newParent||e.parent,i=e.shape;r.canDrillDown(i)&&r.addOverlay(i),r.updateDrilldownOverlay(t),r.updateDrilldownOverlay(n),r.updateDrilldownOverlay(i)}),!0),t.on("import.render.complete",(function(){n.filter((function(e){return r.canDrillDown(e)})).map((function(e){r.addOverlay(e)}))}))}e(mn,Jt),mn.prototype.updateDrilldownOverlay=function(e){var t=this._canvas;if(e){var n=t.findRoot(e);n&&this.updateOverlayVisibility(n)}},mn.prototype.canDrillDown=function(e){var t=this._canvas;return A(e,"bpmn:SubProcess")&&t.findRoot(rn(e))},mn.prototype.updateOverlayVisibility=function(e){var t=this._overlays,n=e.businessObject,i=t.get({element:n.id,type:"drilldown"})[0];if(i){var r=n&&n.flowElements&&n.flowElements.length;Se(i.html).toggle("bjs-drilldown-empty",!r)}},mn.prototype.addOverlay=function(e){var t=this._canvas,n=this._overlays;n.get({element:e,type:"drilldown"}).length&&this.removeOverlay(e);var i=Fe('');i.addEventListener("click",(function(){t.setRootElement(t.findRoot(rn(e)))})),n.add(e,"drilldown",{position:{bottom:-7,right:-8},html:i}),this.updateOverlayVisibility(e)},mn.prototype.removeOverlay=function(e){this._overlays.remove({element:e,type:"drilldown"})},mn.$inject=["canvas","eventBus","elementRegistry","overlays"];var fn={__depends__:[Yt,Zt,en],__init__:["drilldownBreadcrumbs","drilldownOverlayBehavior","drilldownCentering","subprocessCompatibility"],drilldownBreadcrumbs:["type",on],drilldownCentering:["type",an],drilldownOverlayBehavior:["type",mn],subprocessCompatibility:["type",cn]},dn=/^class /;function yn(e){return"[object Array]"===Object.prototype.toString.call(e)}function gn(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function vn(){var e=Array.prototype.slice.call(arguments);1===e.length&&yn(e[0])&&(e=e[0]);var t=e.pop();return t.$inject=e,t}var xn=/constructor\s*[^(]*\(\s*([^)]*)\)/m,bn=/^(?:async\s+)?(?:function\s*[^(]*)?(?:\(\s*([^)]*)\)|(\w+))/m,wn=/\/\*([^*]*)\*\//m;function En(e){if("function"!=typeof e)throw new Error('Cannot annotate "'+e+'". Expected a function!');var t=e.toString().match(function(e){return dn.test(e.toString())}(e)?xn:bn);if(!t)return[];var n=t[1]||t[2];return n&&n.split(",").map((function(e){var t=e.match(wn);return(t&&t[1]||e).trim()}))||[]}function _n(e,t){t=t||{get:function(e,t){if(n.push(e),!1===t)return null;throw a('No provider for "'+e+'"!')}};var n=[],i=this._providers=Object.create(t._providers||null),r=this._instances=Object.create(null),o=r.injector=this,a=function(e){var t=n.join(" -> ");return n.length=0,new Error(t?e+" (Resolving: "+t+")":e)};function s(e,o){if(!i[e]&&-1!==e.indexOf(".")){for(var l=e.split("."),p=s(l.shift());l.length;)p=p[l.shift()];return p}if(gn(r,e))return r[e];if(gn(i,e)){if(-1!==n.indexOf(e))throw n.push(e),a("Cannot resolve circular dependency!");return n.push(e),r[e]=i[e][0](i[e][1]),n.pop(),r[e]}return t.get(e,o)}function l(e,t){if(void 0===t&&(t={}),"function"!=typeof e){if(!yn(e))throw new Error('Cannot invoke "'+e+'". Expected a function!');e=vn(e.slice())}return{fn:e,dependencies:(e.$inject||En(e)).map((function(e){return gn(t,e)?t[e]:s(e)}))}}function p(e){var t=l(e),n=t.fn,i=t.dependencies;return new(Function.prototype.bind.apply(n,[null].concat(i)))}function c(e,t,n){var i=l(e,n),r=i.fn,o=i.dependencies;return r.apply(t,o)}function u(e){return vn((function(t){return e.get(t)}))}function h(e,t){if(t&&t.length){var n,r,a,s,l=Object.create(null),p=Object.create(null),c=[],h=[],m=[];for(var f in i)n=i[f],-1!==t.indexOf(f)&&("private"===n[2]?-1===(r=c.indexOf(n[3]))?(s=u(a=n[3].createChild([],t)),c.push(n[3]),h.push(a),m.push(s),l[f]=[s,f,"private",a]):l[f]=[m[r],f,"private",h[r]]:l[f]=[n[2],n[1]],p[f]=!0),"factory"!==n[2]&&"type"!==n[2]||!n[1].$scope||t.forEach((function(e){-1!==n[1].$scope.indexOf(e)&&(l[f]=[n[2],n[1]],p[e]=!0)}));t.forEach((function(e){if(!p[e])throw new Error('No provider for "'+e+'". Cannot use provider from the parent!')})),e.unshift(l)}return new _n(e,o)}var m,f,d={factory:c,type:p,value:function(e){return e}};function y(e,t){var n=e.__init__||[];return function(){n.forEach((function(e){"string"==typeof e?t.get(e):t.invoke(e)}))}}function g(e){var t=e.__exports__;if(t){var n=e.__modules__,r=Object.keys(e).reduce((function(t,n){return"__exports__"!==n&&"__modules__"!==n&&"__init__"!==n&&"__depends__"!==n&&(t[n]=e[n]),t}),Object.create(null)),a=h((n||[]).concat(r)),s=vn((function(e){return a.get(e)}));t.forEach((function(e){i[e]=[s,e,"private",a]}));var l=(e.__init__||[]).slice();return l.unshift((function(){a.init()})),y(e=Object.assign({},e,{__init__:l}),a)}return Object.keys(e).forEach((function(t){if("__init__"!==t&&"__depends__"!==t)if("private"!==e[t][2]){var n=e[t][0],r=e[t][1];i[t]=[d[n],An(n,r),n]}else i[t]=e[t]})),y(e,o)}function v(e,t){return-1!==e.indexOf(t)||-1!==(e=(t.__depends__||[]).reduce(v,e)).indexOf(t)?e:e.concat(t)}this.get=s,this.invoke=c,this.instantiate=p,this.createChild=h,this.init=(m=e.reduce(v,[]).map(g),f=!1,function(){f||(f=!0,m.forEach((function(e){return e()})))})}function An(e,t){return"value"!==e&&yn(t)&&(t=vn(t.slice())),t}function Sn(e,t){_.call(this,e,1),this.CONNECTION_STYLE=t.style(["no-fill"],{strokeWidth:5,stroke:"fuchsia"}),this.SHAPE_STYLE=t.style({fill:"white",stroke:"fuchsia",strokeWidth:2}),this.FRAME_STYLE=t.style(["no-fill"],{stroke:"fuchsia",strokeDasharray:4,strokeWidth:2})}e(Sn,_),Sn.prototype.canRender=function(){return!0},Sn.prototype.drawShape=function(e,t,n){var i=H("rect");return N(i,{x:0,y:0,width:t.width||0,height:t.height||0}),Nt(t)?N(i,E({},this.FRAME_STYLE,n||{})):N(i,E({},this.SHAPE_STYLE,n||{})),P(e,i),i},Sn.prototype.drawConnection=function(e,t,n){var i=se(t.waypoints,E({},this.CONNECTION_STYLE,n||{}));return P(e,i),i},Sn.prototype.getShapePath=function(e){var t=e.x,n=e.y,i=e.width;return oe([["M",t,n],["l",i,0],["l",0,e.height],["l",-i,0],["z"]])},Sn.prototype.getConnectionPath=function(e){var t,n,i=e.waypoints,r=[];for(t=0;n=i[t];t++)n=n.original||n,r.push([0===t?"M":"L",n.x,n.y]);return oe(r)},Sn.$inject=["eventBus","styles"];var Rn={__init__:["defaultRenderer"],defaultRenderer:["type",Sn],styles:["type",function(){var e={"no-fill":{fill:"none"},"no-border":{strokeOpacity:0},"no-events":{pointerEvents:"none"}},t=this;this.cls=function(e,t,n){return E(this.style(t,n),{class:e})},this.style=function(t,n){r(t)||n||(n=t,t=[]);var i=m(t,(function(t,n){return E(t,e[n]||{})}),{});return n?E(i,n):i},this.computeStyle=function(e,n,i){return r(n)||(i=n,n=[]),t.style(n||[],E({},i,e||{}))}}]};function Cn(e,t){return Math.round(e*t)/t}function Mn(e){return a(e)?e+"px":e}function kn(e,t,n){var i=H("g");j(i).add(t);var r=void 0!==n?n:e.childNodes.length-1;return e.insertBefore(i,e.childNodes[r]||null),i}var Pn={shape:["x","y","width","height"],connection:["waypoints"]};function Tn(e,t,n,i){this._eventBus=t,this._elementRegistry=i,this._graphicsFactory=n,this._rootsIdx=0,this._layers={},this._planes=[],this._rootElement=null,this._init(e||{})}function Dn(e,t){var n="matrix("+t.a+","+t.b+","+t.c+","+t.d+","+t.e+","+t.f+")";e.setAttribute("transform",n)}Tn.$inject=["config.canvas","eventBus","graphicsFactory","elementRegistry"],Tn.prototype._init=function(e){var t=this._eventBus,n=this._container=function(e){var t=(e=E({},{width:"100%",height:"100%"},e)).container||document.body,n=document.createElement("div");return n.setAttribute("class","djs-container"),be(n,{position:"relative",overflow:"hidden",width:Mn(e.width),height:Mn(e.height)}),t.appendChild(n),n}(e),i=this._svg=H("svg");N(i,{width:"100%",height:"100%"}),P(n,i);var r=this._viewport=kn(i,"viewport");!1!==e.deferUpdate&&(this._viewboxChanged=function(e,t){var n,i,r,o;function a(n){var a=Date.now(),p=n?0:o+t-a;if(p>0)return s(p);e.apply(r,i),l()}function s(e){n=setTimeout(a,e)}function l(){n&&clearTimeout(n),n=o=i=r=void 0}function p(){o=Date.now();for(var e=arguments.length,a=new Array(e),l=0;l already created at index <"+t+">");return n.group},Tn.prototype._getChildIndex=function(e){return m(this._layers,(function(t,n){return n.visible&&e>=n.index&&t++,t}),0)},Tn.prototype._createLayer=function(e,t){void 0===t&&(t=1);var n=this._getChildIndex(t);return{group:kn(this._viewport,"layer-"+e,n),index:t,visible:!0}},Tn.prototype.showLayer=function(e){if(!e)throw new Error("must specify a name");var t=this._layers[e];if(!t)throw new Error("layer <"+e+"> does not exist");var n=this._viewport,i=t.group,r=t.index;if(t.visible)return i;var o=this._getChildIndex(r);return n.insertBefore(i,n.childNodes[o]||null),t.visible=!0,i},Tn.prototype.hideLayer=function(e){if(!e)throw new Error("must specify a name");var t=this._layers[e];if(!t)throw new Error("layer <"+e+"> does not exist");var n=t.group;return t.visible?(V(n),t.visible=!1,n):n},Tn.prototype._removeLayer=function(e){var t=this._layers[e];t&&(delete this._layers[e],V(t.group))},Tn.prototype.getActiveLayer=function(){var e=this._findPlaneForRoot(this.getRootElement());return e?e.layer:null},Tn.prototype.findRoot=function(e){if("string"==typeof e&&(e=this._elementRegistry.get(e)),e){var t=this._findPlaneForRoot(function(e){for(;e.parent;)e=e.parent;return e}(e))||{};return t.rootElement}},Tn.prototype.getRootElements=function(){return this._planes.map((function(e){return e.rootElement}))},Tn.prototype._findPlaneForRoot=function(e){return c(this._planes,(function(t){return t.rootElement===e}))},Tn.prototype.getContainer=function(){return this._container},Tn.prototype._updateMarker=function(e,t,n){var i;e.id||(e=this._elementRegistry.get(e)),(i=this._elementRegistry._elements[e.id])&&(h([i.gfx,i.secondaryGfx],(function(e){e&&(n?j(e).add(t):j(e).remove(t))})),this._eventBus.fire("element.marker.update",{element:e,gfx:i.gfx,marker:t,add:!!n}))},Tn.prototype.addMarker=function(e,t){this._updateMarker(e,t,!0)},Tn.prototype.removeMarker=function(e,t){this._updateMarker(e,t,!1)},Tn.prototype.hasMarker=function(e,t){return e.id||(e=this._elementRegistry.get(e)),j(this.getGraphics(e)).has(t)},Tn.prototype.toggleMarker=function(e,t){this.hasMarker(e,t)?this.removeMarker(e,t):this.addMarker(e,t)},Tn.prototype.getRootElement=function(){var e=this._rootElement;return e||this._planes.length?e:this.setRootElement(this.addRootElement(null))},Tn.prototype.addRootElement=function(e){var t=this._rootsIdx++;e||(e={id:"__implicitroot_"+t,children:[],isImplicit:!0});var n=e.layer="root-"+t;this._ensureValid("root",e);var i=this.getLayer(n,0);return this.hideLayer(n),this._addRoot(e,i),this._planes.push({rootElement:e,layer:i}),e},Tn.prototype.removeRootElement=function(e){if("string"==typeof e&&(e=this._elementRegistry.get(e)),this._findPlaneForRoot(e))return this._removeRoot(e),this._removeLayer(e.layer),this._planes=this._planes.filter((function(t){return t.rootElement!==e})),this._rootElement===e&&(this._rootElement=null),e},Tn.prototype.setRootElement=function(e,t){if(i(t))throw new Error("override not supported");if(e!==this._rootElement){if(!e)throw new Error("rootElement required");return this._findPlaneForRoot(e)||(e=this.addRootElement(e)),this._setRoot(e),e}},Tn.prototype._removeRoot=function(e){var t=this._elementRegistry,n=this._eventBus;n.fire("root.remove",{element:e}),n.fire("root.removed",{element:e}),t.remove(e)},Tn.prototype._addRoot=function(e,t){var n=this._elementRegistry,i=this._eventBus;i.fire("root.add",{element:e}),n.add(e,t),i.fire("root.added",{element:e,gfx:t})},Tn.prototype._setRoot=function(e,t){var n=this._rootElement;n&&(this._elementRegistry.updateGraphics(n,null,!0),this.hideLayer(n.layer)),e&&(t||(t=this._findPlaneForRoot(e).layer),this._elementRegistry.updateGraphics(e,this._svg,!0),this.showLayer(e.layer)),this._rootElement=e,this._eventBus.fire("root.set",{element:e})},Tn.prototype._ensureValid=function(e,t){if(!t.id)throw new Error("element must have an id");if(this._elementRegistry.get(t.id))throw new Error("element <"+t.id+"> already exists");var n=Pn[e],i=f(n,(function(e){return void 0!==t[e]}));if(!i)throw new Error("must supply { "+n.join(", ")+" } with "+e)},Tn.prototype._setParent=function(e,t,n){!function(e,t,n){if(e&&t){"number"!=typeof n&&(n=-1);var i=e.indexOf(t);if(-1!==i){if(i===n)return;if(-1===n)return;e.splice(i,1)}-1!==n?e.splice(n,0,t):e.push(t)}}(t.children,e,n),e.parent=t},Tn.prototype._addElement=function(e,t,n,i){n=n||this.getRootElement();var r=this._eventBus,o=this._graphicsFactory;this._ensureValid(e,t),r.fire(e+".add",{element:t,parent:n}),this._setParent(t,n,i);var a=o.create(e,t,i);return this._elementRegistry.add(t,a),o.update(e,t,a),r.fire(e+".added",{element:t,gfx:a}),t},Tn.prototype.addShape=function(e,t,n){return this._addElement("shape",e,t,n)},Tn.prototype.addConnection=function(e,t,n){return this._addElement("connection",e,t,n)},Tn.prototype._removeElement=function(e,t){var n=this._elementRegistry,i=this._graphicsFactory,r=this._eventBus;if(e=n.get(e.id||e))return r.fire(t+".remove",{element:e}),i.remove(e),function(e,t){if(!e||!t)return-1;var n=e.indexOf(t);-1!==n&&e.splice(n,1)}(e.parent&&e.parent.children,e),e.parent=null,r.fire(t+".removed",{element:e}),n.remove(e),e},Tn.prototype.removeShape=function(e){return this._removeElement(e,"shape")},Tn.prototype.removeConnection=function(e){return this._removeElement(e,"connection")},Tn.prototype.getGraphics=function(e,t){return this._elementRegistry.getGraphics(e,t)},Tn.prototype._changeViewbox=function(e){this._eventBus.fire("canvas.viewbox.changing"),e.apply(this),this._cachedViewbox=null,this._viewboxChanged()},Tn.prototype._viewboxChanged=function(){this._eventBus.fire("canvas.viewbox.changed",{viewbox:this.viewbox()})},Tn.prototype.viewbox=function(e){if(void 0===e&&this._cachedViewbox)return this._cachedViewbox;var t,n,i,r,o,a,s,l=this._viewport,p=this.getSize();return e?(this._changeViewbox((function(){o=Math.min(p.width/e.width,p.height/e.height);var t=this._svg.createSVGMatrix().scale(o).translate(-e.x,-e.y);re(l,t)})),e):(t=(i=this._rootElement?this.getActiveLayer():null)&&i.getBBox()||{},n=(r=re(l))?r.matrix:function(e,t,n,i,r,o){var a=U().createSVGMatrix();switch(arguments.length){case 0:return a;case 1:return q(a,e);case 6:return q(a,{a:e,b:t,c:n,d:i,e:r,f:o})}}(),o=Cn(n.a,1e3),a=Cn(-n.e||0,1e3),s=Cn(-n.f||0,1e3),e=this._cachedViewbox={x:a?a/o:0,y:s?s/o:0,width:p.width/o,height:p.height/o,scale:o,inner:{width:t.width||0,height:t.height||0,x:t.x||0,y:t.y||0},outer:p})},Tn.prototype.scroll=function(e){var t=this._viewport,n=t.getCTM();return e&&this._changeViewbox((function(){e=E({dx:0,dy:0},e||{}),n=this._svg.createSVGMatrix().translate(e.dx,e.dy).multiply(n),Dn(t,n)})),{x:n.e,y:n.f}},Tn.prototype.scrollToElement=function(e,t){var n=100;"string"==typeof e&&(e=this._elementRegistry.get(e));var i=this.findRoot(e);i!==this.getRootElement()&&this.setRootElement(i),t||(t={}),"number"==typeof t&&(n=t),t={top:t.top||n,right:t.right||n,bottom:t.bottom||n,left:t.left||n};var r,o,a=Tt(e),s=mt(a),l=this.viewbox(),p=this.zoom();l.y+=t.top/p,l.x+=t.left/p,l.width-=(t.right+t.left)/p,l.height-=(t.bottom+t.top)/p;var c=mt(l);if(a.width=0&&o.y>=0&&o.x+o.width<=r.width&&o.y+o.height<=r.height&&!e?n={x:0,y:0,width:Math.max(o.width+o.x,r.width),height:Math.max(o.height+o.y,r.height)}:(t=Math.min(1,r.width/o.width,r.height/o.height),n={x:o.x+(e?o.width/2-r.width/t/2:0),y:o.y+(e?o.height/2-r.height/t/2:0),width:r.width/t,height:r.height/t}),this.viewbox(n),this.viewbox(!1).scale},Tn.prototype._setZoom=function(e,t){var n,i,r,o,a=this._svg,s=this._viewport,l=a.createSVGMatrix(),p=a.createSVGPoint(),c=(i=s.getCTM()).a;return t?(n=E(p,t).matrixTransform(i.inverse()),r=l.translate(n.x,n.y).scale(1/c*e).translate(-n.x,-n.y),o=i.multiply(r)):o=l.scale(e),Dn(this._viewport,o),o},Tn.prototype.getSize=function(){return{width:this._container.clientWidth,height:this._container.clientHeight}},Tn.prototype.getAbsoluteBBox=function(e){var t,n=this.viewbox();e.waypoints?t=this.getGraphics(e).getBBox():t=e;return{x:t.x*n.scale-n.x*n.scale,y:t.y*n.scale-n.y*n.scale,width:t.width*n.scale,height:t.height*n.scale}},Tn.prototype.resized=function(){delete this._cachedViewbox,this._eventBus.fire("canvas.resized")};var Nn="data-element-id";function On(e){this._elements={},this._eventBus=e}On.$inject=["eventBus"],On.prototype.add=function(e,t,n){var i=e.id;this._validateId(i),N(t,Nn,i),n&&N(n,Nn,i),this._elements[i]={element:e,gfx:t,secondaryGfx:n}},On.prototype.remove=function(e){var t=this._elements,n=e.id||e,i=n&&t[n];i&&(N(i.gfx,Nn,""),i.secondaryGfx&&N(i.secondaryGfx,Nn,""),delete t[n])},On.prototype.updateId=function(e,t){this._validateId(t),"string"==typeof e&&(e=this.get(e)),this._eventBus.fire("element.updateId",{element:e,newId:t});var n=this.getGraphics(e),i=this.getGraphics(e,!0);this.remove(e),e.id=t,this.add(e,n,i)},On.prototype.updateGraphics=function(e,t,n){var i=e.id||e,r=this._elements[i];return n?r.secondaryGfx=t:r.gfx=t,t&&N(t,Nn,i),t},On.prototype.get=function(e){var t;t="string"==typeof e?e:e&&N(e,Nn);var n=this._elements[t];return n&&n.element},On.prototype.filter=function(e){var t=[];return this.forEach((function(n,i){e(n,i)&&t.push(n)})),t},On.prototype.find=function(e){for(var t=this._elements,n=Object.keys(t),i=0;i in ref");t=this.props[t]}t.collection?jn(this,t,e):function(e,t,n){var i=t.inverse,r=n[t.name];Object.defineProperty(n,t.name,{configurable:t.configurable,enumerable:t.enumerable,get:function(){return r},set:function(t){if(t!==r){var o=r;r=null,o&&e.unset(o,i,n),r=t,e.set(r,i,n)}}})}(this,t,e)},Fn.prototype.ensureRefsCollection=function(e,t){var n=e[t.name];return In.isExtended(n)||jn(this,t,e),n},Fn.prototype.ensureBound=function(e,t){(function(e,t){return Object.prototype.hasOwnProperty.call(e,t.name||t)})(e,t)||this.bind(e,t)},Fn.prototype.unset=function(e,t,n){e&&(this.ensureBound(e,t),t.collection?this.ensureRefsCollection(e,t).remove(n):e[t.name]=void 0)},Fn.prototype.set=function(e,t,n){e&&(this.ensureBound(e,t),t.collection?this.ensureRefsCollection(e,t).add(n):e[t.name]=n)};var Vn=Fn;!function(e){e.exports=Vn,e.exports.Collection=Ln}(Bn);var zn=ht(Bn.exports),Wn=new zn({name:"children",enumerable:!0,collection:!0},{name:"parent"}),Gn=new zn({name:"labels",enumerable:!0,collection:!0},{name:"labelTarget"}),$n=new zn({name:"attachers",collection:!0},{name:"host"}),Hn=new zn({name:"outgoing",collection:!0},{name:"source"}),Kn=new zn({name:"incoming",collection:!0},{name:"target"});function Un(){Object.defineProperty(this,"businessObject",{writable:!0}),Object.defineProperty(this,"label",{get:function(){return this.labels[0]},set:function(e){var t=this.label,n=this.labels;!e&&t?n.remove(t):n.add(e,0)}}),Wn.bind(this,"parent"),Gn.bind(this,"labels"),Hn.bind(this,"outgoing"),Kn.bind(this,"incoming")}function qn(){Un.call(this),Wn.bind(this,"children"),$n.bind(this,"host"),$n.bind(this,"attachers")}function Yn(){qn.call(this)}function Xn(){qn.call(this),Gn.bind(this,"labelTarget")}function Zn(){Un.call(this),Hn.bind(this,"source"),Kn.bind(this,"target")}e(qn,Un),e(Yn,qn),e(Xn,qn),e(Zn,Un);var Jn={connection:Zn,shape:qn,label:Xn,root:Yn};function Qn(){this._uid=12}Qn.prototype.createRoot=function(e){return this.create("root",e)},Qn.prototype.createLabel=function(e){return this.create("label",e)},Qn.prototype.createShape=function(e){return this.create("shape",e)},Qn.prototype.createConnection=function(e){return this.create("connection",e)},Qn.prototype.create=function(e,t){return(t=E({},t||{})).id||(t.id=e+"_"+this._uid++),function(e,t){var n=Jn[e];if(!n)throw new Error("unknown type: <"+e+">");return E(new n,t)}(e,t)};var ei=Array.prototype.slice;function ti(){this._listeners={},this.on("diagram.destroy",1,this._destroy,this)}function ni(){}function ii(e,t){this._eventBus=e,this._elementRegistry=t}function ri(e,t,n){var i=n||t.firstChild;e!==i&&t.insertBefore(e,i)}ti.prototype.on=function(e,t,n,i){if(e=r(e)?e:[e],s(t)&&(i=n,n=t,t=1e3),!a(t))throw new Error("priority must be a number");var o=n;i&&((o=b(n,i)).__fn=n.__fn||n);var l=this;e.forEach((function(e){l._addListener(e,{priority:t,callback:o,next:null})}))},ti.prototype.once=function(e,t,n,i){var r=this;if(s(t)&&(i=n,n=t,t=1e3),!a(t))throw new Error("priority must be a number");function o(){o.__isTomb=!0;var t=n.apply(i,arguments);return r.off(e,o),t}o.__fn=n,this.on(e,t,o)},ti.prototype.off=function(e,t){e=r(e)?e:[e];var n=this;e.forEach((function(e){n._removeListener(e,t)}))},ti.prototype.createEvent=function(e){var t=new ni;return t.init(e),t},ti.prototype.fire=function(e,t){var n,i,r,o;if(o=ei.call(arguments),"object"==typeof e&&(e=(t=e).type),!e)throw new Error("no event type specified");if(i=this._listeners[e]){n=t instanceof ni?t:this.createEvent(t),o[0]=n;var a=n.type;e!==a&&(n.type=e);try{r=this._invokeListeners(n,o,i)}finally{e!==a&&(n.type=a)}return void 0===r&&n.defaultPrevented&&(r=!1),r}},ti.prototype.handleError=function(e){return!1===this.fire("error",{error:e})},ti.prototype._destroy=function(){this._listeners={}},ti.prototype._invokeListeners=function(e,t,n){for(var i;n&&!e.cancelBubble;)i=this._invokeListener(e,t,n),n=n.next;return i},ti.prototype._invokeListener=function(e,t,n){var i;if(n.callback.__isTomb)return i;try{i=function(e,t){return e.apply(null,t)}(n.callback,t),void 0!==i&&(e.returnValue=i,e.stopPropagation()),!1===i&&e.preventDefault()}catch(e){if(!this.handleError(e))throw console.error("unhandled error in event listener",e),e}return i},ti.prototype._addListener=function(e,t){var n,i=this._getListeners(e);if(i){for(;i;){if(i.priority or , got "+e);n=r[1],i=r[0]}return{name:e=(i?i+":":"")+n,prefix:i,localName:n}}function yi(e){this.ns=e,this.name=e.name,this.allTypes=[],this.allTypesByName={},this.properties=[],this.propertiesByName={}}function gi(e,t){this.packageMap={},this.typeMap={},this.packages=[],this.properties=t,h(e,b(this.registerPackage,this))}function vi(e,t,n){var i=t[n];if(i in e)throw new Error("package with "+n+" <"+i+"> already defined")}function xi(e){this.model=e}function bi(e,t,n){Object.defineProperty(e,t.name,{enumerable:!t.isReference,writable:!0,value:n,configurable:!0})}function wi(e){this.properties=new xi(this),this.factory=new pi(this,this.properties),this.registry=new gi(e,this.properties),this.typeCache={}}yi.prototype.build=function(){return e=this,t=["ns","name","allTypes","allTypesByName","properties","propertiesByName","bodyProperty","idProperty"],n={},i=Object(e),h(t,(function(t){t in i&&(n[t]=e[t])})),n;var e,t,n,i},yi.prototype.addProperty=function(e,t,n){"boolean"==typeof t&&(n=t,t=void 0),this.addNamedProperty(e,!1!==n);var i=this.properties;void 0!==t?i.splice(t,0,e):i.push(e)},yi.prototype.replaceProperty=function(e,t,n){var i=e.ns,r=this.properties,o=this.propertiesByName,a=e.name!==t.name;if(e.isId){if(!t.isId)throw new Error("property <"+t.ns.name+"> must be id property to refine <"+e.ns.name+">");this.setIdProperty(t,!1)}if(e.isBody){if(!t.isBody)throw new Error("property <"+t.ns.name+"> must be body property to refine <"+e.ns.name+">");this.setBodyProperty(t,!1)}var s=r.indexOf(e);if(-1===s)throw new Error("property <"+i.name+"> not found in property list");r.splice(s,1),this.addProperty(t,n?void 0:s,a),o[i.name]=o[i.localName]=t},yi.prototype.redefineProperty=function(e,t,n){var i=e.ns.prefix,r=t.split("#"),o=di(r[0],i),a=di(r[1],o.prefix).name,s=this.propertiesByName[a];if(!s)throw new Error("refined property <"+a+"> not found");this.replaceProperty(s,e,n),delete e.redefines},yi.prototype.addNamedProperty=function(e,t){var n=e.ns,i=this.propertiesByName;t&&(this.assertNotDefined(e,n.name),this.assertNotDefined(e,n.localName)),i[n.name]=i[n.localName]=e},yi.prototype.removeNamedProperty=function(e){var t=e.ns,n=this.propertiesByName;delete n[t.name],delete n[t.localName]},yi.prototype.setBodyProperty=function(e,t){if(t&&this.bodyProperty)throw new Error("body property defined multiple times (<"+this.bodyProperty.ns.name+">, <"+e.ns.name+">)");this.bodyProperty=e},yi.prototype.setIdProperty=function(e,t){if(t&&this.idProperty)throw new Error("id property defined multiple times (<"+this.idProperty.ns.name+">, <"+e.ns.name+">)");this.idProperty=e},yi.prototype.assertNotDefined=function(e,t){var n=e.name,i=this.propertiesByName[n];if(i)throw new Error("property <"+n+"> already defined; override of <"+i.definedBy.ns.name+"#"+i.ns.name+"> by <"+e.definedBy.ns.name+"#"+e.ns.name+"> not allowed without redefines")},yi.prototype.hasProperty=function(e){return this.propertiesByName[e]},yi.prototype.addTrait=function(e,t){var n=this.allTypesByName,i=this.allTypes,r=e.name;r in n||(h(e.properties,b((function(n){n=E({},n,{name:n.ns.localName,inherited:t}),Object.defineProperty(n,"definedBy",{value:e});var i=n.replaces,r=n.redefines;i||r?this.redefineProperty(n,i||r,i):(n.isBody&&this.setBodyProperty(n),n.isId&&this.setIdProperty(n),this.addProperty(n))}),this)),i.push(e),n[r]=e)},gi.prototype.getPackage=function(e){return this.packageMap[e]},gi.prototype.getPackages=function(){return this.packages},gi.prototype.registerPackage=function(e){e=E({},e);var t=this.packageMap;vi(t,e,"prefix"),vi(t,e,"uri"),h(e.types,b((function(t){this.registerType(t,e)}),this)),t[e.uri]=t[e.prefix]=e,this.packages.push(e)},gi.prototype.registerType=function(e,t){var n=di((e=E({},e,{superClass:(e.superClass||[]).slice(),extends:(e.extends||[]).slice(),properties:(e.properties||[]).slice(),meta:E(e.meta||{})})).name,t.prefix),i=n.name,r={};h(e.properties,b((function(e){var t=di(e.name,n.prefix),i=t.name;mi(e.type)||(e.type=di(e.type,t.prefix).name),E(e,{ns:t,name:i}),r[i]=e}),this)),E(e,{ns:n,name:i,propertiesByName:r}),h(e.extends,b((function(e){var t=this.typeMap[e];t.traits=t.traits||[],t.traits.push(i)}),this)),this.definePackage(e,t),this.typeMap[i]=e},gi.prototype.mapTypes=function(e,t,n){var i=mi(e.name)?{name:e.name}:this.typeMap[e.name],r=this;function o(e){return a(e,!0)}function a(n,i){var o=di(n,mi(n)?"":e.prefix);r.mapTypes(o,t,i)}if(!i)throw new Error("unknown type <"+e.name+">");h(i.superClass,n?o:a),t(i,!n),h(i.traits,o)},gi.prototype.getEffectiveDescriptor=function(e){var t=di(e),n=new yi(t);this.mapTypes(t,(function(e,t){n.addTrait(e,t)}));var i=n.build();return this.definePackage(i,i.allTypes[i.allTypes.length-1].$pkg),i},gi.prototype.definePackage=function(e,t){this.properties.define(e,"$pkg",{value:t})},xi.prototype.set=function(e,t,n){var i=this.model.getPropertyDescriptor(e,t),r=i&&i.name;void 0===n?i?delete e[r]:delete e.$attrs[t]:i?r in e?e[r]=n:bi(e,i,n):e.$attrs[t]=n},xi.prototype.get=function(e,t){var n=this.model.getPropertyDescriptor(e,t);if(!n)return e.$attrs[t];var i=n.name;return!e[i]&&n.isMany&&bi(e,n,[]),e[i]},xi.prototype.define=function(e,t,n){if(!n.writable){var i=n.value;delete(n=E({},n,{get:function(){return i}})).value}Object.defineProperty(e,t,n)},xi.prototype.defineDescriptor=function(e,t){this.define(e,"$descriptor",{value:t})},xi.prototype.defineModel=function(e,t){this.define(e,"$model",{value:t})},wi.prototype.create=function(e,t){var n=this.getType(e);if(!n)throw new Error("unknown type <"+e+">");return new n(t)},wi.prototype.getType=function(e){var t=this.typeCache,n=l(e)?e:e.ns.name,i=t[n];return i||(e=this.registry.getEffectiveDescriptor(n),i=t[n]=this.factory.createType(e)),i},wi.prototype.createAny=function(e,t,n){var i=di(e),r={$type:e,$instanceOf:function(e){return e===this.$type}},a={name:e,isGeneric:!0,ns:{prefix:i.prefix,localName:i.localName,uri:t}};return this.properties.defineDescriptor(r,a),this.properties.defineModel(r,this),this.properties.define(r,"$parent",{enumerable:!1,writable:!0}),this.properties.define(r,"$instanceOf",{enumerable:!1,writable:!0}),h(n,(function(e,t){o(e)&&void 0!==e.value?r[e.name]=e.value:r[t]=e})),r},wi.prototype.getPackage=function(e){return this.registry.getPackage(e)},wi.prototype.getPackages=function(){return this.registry.getPackages()},wi.prototype.getElementDescriptor=function(e){return e.$descriptor},wi.prototype.hasType=function(e,t){return void 0===t&&(t=e,e=this),t in e.$model.getElementDescriptor(e).allTypesByName},wi.prototype.getPropertyDescriptor=function(e,t){return this.getElementDescriptor(e).propertiesByName[t]},wi.prototype.getTypeDescriptor=function(e){return this.registry.typeMap[e]};var Ei=String.fromCharCode,_i=Object.prototype.hasOwnProperty,Ai=/&#(\d+);|&#x([0-9a-f]+);|&(\w+);/gi,Si={amp:"&",apos:"'",gt:">",lt:"<",quot:'"'};function Ri(e,t,n,i){return i?_i.call(Si,i)?Si[i]:"&"+i+";":Ei(t||parseInt(n,16))}function Ci(e){return e.length>3&&-1!==e.indexOf("&")?e.replace(Ai,Ri):e}Object.keys(Si).forEach((function(e){Si[e.toUpperCase()]=Si[e]}));var Mi="xsi:type",ki="non-whitespace outside of root node";function Pi(e){return new Error(e)}function Ti(e){return"missing namespace for prefix <"+e+">"}function Di(e){return{get:e,enumerable:!0}}function Ni(e){var t,n={};for(t in e)n[t]=e[t];return n}function Oi(e){return e+"$uri"}function Bi(){return{line:0,column:0}}function Li(e){throw e}function Ii(e){if(!this)return new Ii(e);var t,n,i,r,o,a,s,l,p,c=e&&e.proxy,u=Li,h=Bi,m=!1,f=!1,d=null,y=!1;function g(e){e instanceof Error||(e=Pi(e)),d=e,u(e,h)}function v(e){o&&(e instanceof Error||(e=Pi(e)),o(e,h))}this.on=function(e,p){if("function"!=typeof p)throw Pi("required args ");switch(e){case"openTag":n=p;break;case"text":t=p;break;case"closeTag":i=p;break;case"error":u=p;break;case"warn":o=p;break;case"cdata":r=p;break;case"attention":l=p;break;case"question":s=p;break;case"comment":a=p;break;default:throw Pi("unsupported event: "+e)}return this},this.ns=function(e){if(void 0===e&&(e={}),"object"!=typeof e)throw Pi("required args ");var t,n={};for(t in e)n[t]=e[t];return n["http://www.w3.org/2001/XMLSchema-instance"]="xsi",f=!0,p=n,this},this.parse=function(e){if("string"!=typeof e)throw Pi("required args ");return d=null,function(e){var o,u,d,x,b,w,E,_,A,S,R,C=f?[]:null,M=f?function(e){var t,n,i={};for(t in e)i[n=e[t]]=n,i[Oi(n)]=t;return i}(p):null,k=[],P=0,T=!1,D=!1,N=0,O=0,B="",L=0;function I(){if(null!==R)return R;var e,t,n,i,r,o,a,s,l,c,u,h=f&&M.xmlns,d=f&&m?[]:null,y=L,g=B,x=g.length,b={},w={};e:for(;y8)){for((c<65||c>122||c>90&&c<97)&&95!==c&&58!==c&&(v("illegal first char attribute name"),l=!0),u=y+1;u96&&c<123||c>64&&c<91||c>47&&c<59||46===c||45===c||95===c)){if(32===c||c<14&&c>8){v("missing attribute value"),y=u;continue e}if(61===c)break;v("illegal attribute name char"),l=!0}if("xmlns:xmlns"===(s=g.substring(y,u))&&(v("illegal declaration of xmlns"),l=!0),34===(c=g.charCodeAt(u+1)))-1===(u=g.indexOf('"',y=u+2))&&-1!==(u=g.indexOf("'",y))&&(v("attribute value quote missmatch"),l=!0);else if(39===c)-1===(u=g.indexOf("'",y=u+2))&&-1!==(u=g.indexOf('"',y))&&(v("attribute value quote missmatch"),l=!0);else for(v("missing attribute value quotes"),l=!0,u+=1;u8);u++);for(-1===u&&(v("missing closing quotes"),u=x,l=!0),l||(o=g.substring(y,u)),y=u;u+18);u++)y===u&&(v("illegal character after attribute end"),l=!0);if(y=u+1,!l)if(s in w)v("attribute <"+s+"> already defined");else if(w[s]=!0,f)if(m){if(null!==(r="xmlns"===s?"xmlns":120===s.charCodeAt(0)&&"xmlns:"===s.substr(0,6)?s.substr(6):null)){if(e=Ci(o),t=Oi(r),!(a=p[e])){if("xmlns"===r||t in M&&M[t]!==e)do{a="ns"+P++}while(void 0!==M[a]);else a=r;p[e]=a}M[r]!==a&&(i||(M=Ni(M),i=!0),M[r]=a,"xmlns"===r&&(M[Oi(a)]=e,h=a),M[t]=e),b[s]=o;continue}d.push(s,o)}else-1!==(c=s.indexOf(":"))?(n=M[s.substring(0,c)])?((s=h===n?s.substr(c+1):n+s.substr(c))===Mi&&(-1!==(c=o.indexOf(":"))?(n=o.substring(0,c),o=(n=M[n]||n)+o.substring(c)):o=h+":"+o),b[s]=o):v(Ti(s.substring(0,c))):b[s]=o;else b[s]=o}if(m)for(y=0,x=d.length;y=a&&(t=i.exec(e))&&!((s=t[0].length+t.index)>N);)r+=1,a=s;return-1==N?(o=s,n=e.substring(O)):0===O?n=e.substring(O,N):(o=N-a,n=-1==O?e.substring(N):e.substring(N,O+1)),{data:n,line:r,column:o}}h=j,c&&(S=Object.create({},{name:Di((function(){return _})),originalName:Di((function(){return A})),attrs:Di(I),ns:Di((function(){return M}))}));for(;-1!==O;){if(-1===(N=60===e.charCodeAt(O)?O:e.indexOf("<",O)))return k.length?g("unexpected end of file"):0===O?g("missing start tag"):void(O",N)))return g("unclosed cdata");if(r&&(r(e.substring(N+9,O),h),y))return;O+=3;continue}if(45===x&&45===e.charCodeAt(N+3)){if(-1===(O=e.indexOf("--\x3e",N)))return g("unclosed comment");if(a&&(a(e.substring(N+4,O),Ci,h),y))return;O+=3;continue}}if(63!==b){for(u=N+1;;u++){if(w=e.charCodeAt(u),isNaN(w))return O=-1,g("unclosed tag");if(34===w)u=-1!==(x=e.indexOf('"',u+1))?x:u;else if(39===w)u=-1!==(x=e.indexOf("'",u+1))?x:u;else if(62===w){O=u;break}}if(33!==b){if(R={},47===b){if(T=!1,D=!0,!k.length)return g("missing open tag");if(u=_=k.pop(),x=N+2+u.length,e.substring(N+2,x)!==u)return g("closing tag mismatch");for(;x8&&b<14))return g("close tag")}else{if(47===e.charCodeAt(O-1)?(u=_=e.substring(N+1,O-1),T=!0,D=!0):(u=_=e.substring(N+1,O),T=!0,D=!1),!(b>96&&b<123||b>64&&b<91||95===b||58===b))return g("illegal first char nodeName");for(x=1,d=u.length;x96&&b<123||b>64&&b<91||b>47&&b<59||45===b||95===b||46==b)){if(32===b||b<14&&b>8){_=u.substring(0,x),R=null;break}return g("invalid nodeName")}D||k.push(_)}if(f){if(o=M,T&&(D||C.push(o),null===R&&(m=-1!==u.indexOf("xmlns",x))&&(L=x,B=u,I(),m=!1)),A=_,-1!==(b=_.indexOf(":"))){if(!(E=M[_.substring(0,b)]))return g("missing namespace on <"+A+">");_=_.substr(b+1)}else E=M.xmlns;E&&(_=E+":"+_)}if(T&&(L=x,B=u,n&&(c?n(S,Ci,D,h):n(_,I,Ci,D,h),y)))return;if(D){if(i&&(i(c?S:_,Ci,T,h),y))return;f&&(M=T?o:C.pop())}O+=1}else{if(l&&(l(e.substring(N,O+1),Ci,h),y))return;O+=1}}else{if(-1===(O=e.indexOf("?>",N)))return g("unclosed question");if(s&&(s(e.substring(N,O+2),h),y))return;O+=2}}}(e),h=Bi,y=!1,d},this.stop=function(){y=!0}}function ji(e){return e.xml&&"lowerCase"===e.xml.tagAlias}var Fi={xsi:"http://www.w3.org/2001/XMLSchema-instance",xml:"http://www.w3.org/XML/1998/namespace"},Vi="xsi:type";function zi(e){return e.xml&&e.xml.serialize}function Wi(e){return zi(e)===Vi}function Gi(e,t){return ji(t)?e.prefix+":"+((n=e.localName).charAt(0).toUpperCase()+n.slice(1)):e.name;var n}function $i(e){return new Error(e)}function Hi(e){return e.$descriptor}function Ki(e){E(this,e),this.elementsById={},this.references=[],this.warnings=[],this.addReference=function(e){this.references.push(e)},this.addElement=function(e){if(!e)throw $i("expected element");var t,n=this.elementsById,i=Hi(e).idProperty;if(i&&(t=e.get(i.name))){if(!/^([a-z][\w-.]*:)?[a-z_][\w-.]*$/i.test(t))throw new Error("illegal ID <"+t+">");if(n[t])throw $i("duplicate ID <"+t+">");n[t]=e}},this.addWarning=function(e){this.warnings.push(e)}}function Ui(){}function qi(){}function Yi(){}function Xi(e,t){this.property=e,this.context=t}function Zi(e,t){this.element=t,this.propertyDesc=e}function Ji(){}function Qi(e,t,n){this.model=e,this.type=e.getType(t),this.context=n}function er(e,t,n){Qi.call(this,e,t,n)}function tr(e,t,n){this.model=e,this.context=n}function nr(e){e instanceof wi&&(e={model:e}),E(this,{lax:!1},e)}Ui.prototype.handleEnd=function(){},Ui.prototype.handleText=function(){},Ui.prototype.handleNode=function(){},qi.prototype=Object.create(Ui.prototype),qi.prototype.handleNode=function(){return this},Yi.prototype=Object.create(Ui.prototype),Yi.prototype.handleText=function(e){this.body=(this.body||"")+e},Xi.prototype=Object.create(Yi.prototype),Xi.prototype.handleNode=function(e){if(this.element)throw $i("expected no sub nodes");return this.element=this.createReference(e),this},Xi.prototype.handleEnd=function(){this.element.id=this.body},Xi.prototype.createReference=function(e){return{property:this.property.ns.name,id:""}},Zi.prototype=Object.create(Yi.prototype),Zi.prototype.handleEnd=function(){var e=this.body||"",t=this.element,n=this.propertyDesc;e=hi(n.type,e),n.isMany?t.get(n.name).push(e):t.set(n.name,e)},Ji.prototype=Object.create(Yi.prototype),Ji.prototype.handleNode=function(e){var t=this,n=this.element;return n?t=this.handleChild(e):(n=this.element=this.createElement(e),this.context.addElement(n)),t},Qi.prototype=Object.create(Ji.prototype),Qi.prototype.addReference=function(e){this.context.addReference(e)},Qi.prototype.handleText=function(e){if(!Hi(this.element).bodyProperty)throw $i("unexpected body text <"+e+">");Yi.prototype.handleText.call(this,e)},Qi.prototype.handleEnd=function(){var e=this.body,t=this.element,n=Hi(t).bodyProperty;n&&void 0!==e&&(e=hi(n.type,e),t.set(n.name,e))},Qi.prototype.createElement=function(e){var t,n=e.attributes,i=this.type,r=Hi(i),o=this.context,a=new i({}),s=this.model;return h(n,(function(e,n){var i=r.propertiesByName[n];i&&i.isReference?i.isMany?h(e.split(" "),(function(e){o.addReference({element:a,property:i.ns.name,id:e})})):o.addReference({element:a,property:i.ns.name,id:e}):(i?e=hi(i.type,e):"xmlns"!==n&&(t=di(n,r.ns.prefix),s.getPackage(t.prefix)&&o.addWarning({message:"unknown attribute <"+n+">",element:a,property:n,value:e})),a.set(n,e))})),a},Qi.prototype.getPropertyForNode=function(e){var t,n,i=di(e.name),r=this.type,o=this.model,a=Hi(r),s=i.name,l=a.propertiesByName[s];if(l&&!l.isAttr)return Wi(l)&&(t=e.attributes[Vi])?(t=function(e,t){var n=di(e);return function(e,t){var n=e.name,i=e.localName,r=t.xml&&t.xml.typePrefix;return r&&0===i.indexOf(r)?e.prefix+":"+i.slice(r.length):n}(n,t.getPackage(n.prefix))}(t,o),E({},l,{effectiveType:Hi(n=o.getType(t)).name})):l;var p=o.getPackage(i.prefix);if(p){if(t=Gi(i,p),n=o.getType(t),l=c(a.properties,(function(e){return!e.isVirtual&&!e.isReference&&!e.isAttribute&&n.hasType(e.type)})))return E({},l,{effectiveType:Hi(n).name})}else if(l=c(a.properties,(function(e){return!e.isReference&&!e.isAttribute&&"Element"===e.type})))return l;throw $i("unrecognized element <"+i.name+">")},Qi.prototype.toString=function(){return"ElementDescriptor["+Hi(this.type).name+"]"},Qi.prototype.valueHandler=function(e,t){return new Zi(e,t)},Qi.prototype.referenceHandler=function(e){return new Xi(e,this.context)},Qi.prototype.handler=function(e){return"Element"===e?new tr(this.model,e,this.context):new Qi(this.model,e,this.context)},Qi.prototype.handleChild=function(e){var t,n,i,r;if(t=this.getPropertyForNode(e),i=this.element,fi(n=t.effectiveType||t.type))return this.valueHandler(t,i);var o=(r=t.isReference?this.referenceHandler(t).handleNode(e):this.handler(n).handleNode(e)).element;return void 0!==o&&(t.isMany?i.get(t.name).push(o):i.set(t.name,o),t.isReference?(E(o,{element:i}),this.context.addReference(o)):o.$parent=i),r},er.prototype=Object.create(Qi.prototype),er.prototype.createElement=function(e){var t=e.name,n=di(t),i=this.model,r=this.type,o=i.getPackage(n.prefix),a=o&&Gi(n,o)||t;if(!r.hasType(a))throw $i("unexpected element <"+e.originalName+">");return Qi.prototype.createElement.call(this,e)},tr.prototype=Object.create(Ji.prototype),tr.prototype.createElement=function(e){var t=e.name,n=di(t).prefix,i=e.ns[n+"$uri"],r=e.attributes;return this.model.createAny(t,i,r)},tr.prototype.handleChild=function(e){var t=new tr(this.model,"Element",this.context).handleNode(e),n=this.element,i=t.element;return void 0!==i&&((n.$children=n.$children||[]).push(i),i.$parent=n),t},tr.prototype.handleEnd=function(){this.body&&(this.element.$body=this.body)},nr.prototype.fromXML=function(e,t,n){var i=t.rootHandler;t instanceof Qi?(i=t,t={}):"string"==typeof t?(i=this.handler(t),t={}):"string"==typeof i&&(i=this.handler(i));var r=this.model,o=this.lax,a=new Ki(E({},t,{rootHandler:i})),s=new Ii({proxy:!0}),l=function(){var e=[];return Object.defineProperty(e,"peek",{value:function(){return this[this.length-1]}}),e}();function p(e,t,n){var i=t(),r=i.line,o=i.column,s=i.data;"<"===s.charAt(0)&&-1!==s.indexOf(" ")&&(s=s.slice(0,s.indexOf(" "))+">");var l="unparsable content "+(s?s+" ":"")+"detected\n\tline: "+r+"\n\tcolumn: "+o+"\n\tnested error: "+e.message;if(n)return a.addWarning({message:l,error:e}),!0;throw $i(l)}function c(e,t){return p(e,t,!0)}i.context=a,l.push(i);var u=/^<\?xml /i,h=/ encoding="([^"]+)"/i,m=/^utf-8$/i;function f(e,t){try{l.peek().handleText(e)}catch(e){c(e,t)}}var d=r.getPackages().reduce((function(e,t){return e[t.uri]=t.prefix,e}),{"http://www.w3.org/XML/1998/namespace":"xml"});return s.ns(d).on("openTag",(function(e,t,n,i){var r=e.attrs||{},a=Object.keys(r).reduce((function(e,n){var i=t(r[n]);return e[n]=i,e}),{});!function(e,t){var n=l.peek();try{l.push(n.handleNode(e))}catch(e){p(e,t,o)&&l.push(new qi)}}({name:e.name,originalName:e.originalName,attributes:a,ns:e.ns},i)})).on("question",(function(e){if(u.test(e)){var t=h.exec(e),n=t&&t[1];n&&!m.test(n)&&a.addWarning({message:"unsupported document encoding <"+n+">, falling back to UTF-8"})}})).on("closeTag",(function(){l.pop().handleEnd()})).on("cdata",f).on("text",(function(e,t,n){!function(e,t){e.trim()&&f(e,t)}(t(e),n)})).on("error",p).on("warn",c),new Promise((function(t,n){var r;try{s.parse(e),function(){var e,t,n=a.elementsById,i=a.references;for(e=0;t=i[e];e++){var r=t.element,o=n[t.id],s=Hi(r).propertiesByName[t.property];if(o||a.addWarning({message:"unresolved reference <"+t.id+">",element:t.element,property:t.property,value:t.id}),s.isMany){var l=r.get(s.name),p=l.indexOf(t);-1===p&&(p=l.length),o?l[p]=o:l.splice(p,1)}else r.set(s.name,o)}}()}catch(e){r=e}var o=i.element;r||o||(r=$i("failed to parse document as <"+i.type.$descriptor.name+">"));var l=a.warnings,p=a.references,c=a.elementsById;return r?(r.warnings=l,n(r)):t({rootElement:o,elementsById:c,references:p,warnings:l})}))},nr.prototype.handler=function(e){return new er(this.model,e)};var ir=/<|>|'|"|&|\n\r|\n/g,rr=/<|>|&/g;function or(e){var t={},n={},i={},r=[],o=[];this.byUri=function(t){return n[t]||e&&e.byUri(t)},this.add=function(e,t){n[e.uri]=e,t?r.push(e):o.push(e),this.mapPrefix(e.prefix,e.uri)},this.uriByPrefix=function(e){return t[e||"xmlns"]},this.mapPrefix=function(e,n){t[e||"xmlns"]=n},this.getNSKey=function(e){return void 0!==e.prefix?e.uri+"|"+e.prefix:e.uri},this.logUsed=function(t){var n=t.uri,r=this.getNSKey(t);i[r]=this.byUri(n),e&&e.logUsed(t)},this.getUsed=function(e){var t=this;return[].concat(r,o).filter((function(e){var n=t.getNSKey(e);return i[n]}))}}function ar(e,t){return ji(t)?(n=e).charAt(0).toLowerCase()+n.slice(1):e;var n}function sr(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})}function lr(e){return l(e)?e:(e.prefix?e.prefix+":":"")+e.localName}var pr={"\n":"#10","\n\r":"#10",'"':"#34","'":"#39","<":"#60",">":"#62","&":"#38"},cr={"<":"lt",">":"gt","&":"amp"};function ur(e,t,n){return(e=l(e)?e:""+e).replace(t,(function(e){return"&"+n[e]+";"}))}function hr(e){this.tagName=e}function mr(){}function fr(e){this.tagName=e}function dr(e,t){this.body=[],this.attrs=[],this.parent=e,this.propertyDescriptor=t}function yr(e,t){dr.call(this,e,t)}function gr(){this.value="",this.write=function(e){this.value+=e}}function vr(e,t){var n=[""];this.append=function(t){return e.write(t),this},this.appendNewLine=function(){return t&&e.write("\n"),this},this.appendIndent=function(){return t&&e.write(n.join(" ")),this},this.indent=function(){return n.push(""),this},this.unindent=function(){return n.pop(),this}}function xr(e){return e=E({format:!1,preamble:!0},e||{}),{toXML:function(t,n){var i=n||new gr,r=new vr(i,e.format);if(e.preamble&&r.append('\n'),(new dr).build(t).serializeTo(r),!n)return i.value}}}function br(e,t){wi.call(this,e,t)}hr.prototype.build=function(e){return this.element=e,this},hr.prototype.serializeTo=function(e){e.appendIndent().append("<"+this.tagName+">"+this.element.id+"").appendNewLine()},mr.prototype.serializeValue=mr.prototype.serializeTo=function(e){e.append(this.escape?ur(this.value,rr,cr):this.value)},mr.prototype.build=function(e,t){return this.value=t,"String"===e.type&&-1!==t.search(rr)&&(this.escape=!0),this},sr(fr,mr),fr.prototype.serializeTo=function(e){e.appendIndent().append("<"+this.tagName+">"),this.serializeValue(e),e.append("").appendNewLine()},dr.prototype.build=function(e){this.element=e;var t,n,i=e.$descriptor,r=this.propertyDescriptor,o=i.isGeneric;return t=o?this.parseGeneric(e):this.parseNsAttributes(e),this.ns=r?this.nsPropertyTagName(r):this.nsTagName(i),this.tagName=this.addTagName(this.ns),o||(n=function(e){return u(e.$descriptor.properties,(function(t){var n=t.name;if(t.isVirtual)return!1;if(!p(e,n))return!1;var i=e[n];return i!==t.default&&null!==i&&(!t.isMany||i.length)}))}(e),this.parseAttributes(u(n,(function(e){return e.isAttr}))),this.parseContainments(function(e){return u(e,(function(e){return!e.isAttr}))}(n))),this.parseGenericAttributes(e,t),this},dr.prototype.nsTagName=function(e){return function(e,t){return t.isGeneric?E({localName:t.ns.localName},e):E({localName:ar(t.ns.localName,t.$pkg)},e)}(this.logNamespaceUsed(e.ns),e)},dr.prototype.nsPropertyTagName=function(e){return function(e,t){return E({localName:t.ns.localName},e)}(this.logNamespaceUsed(e.ns),e)},dr.prototype.isLocalNs=function(e){return e.uri===this.ns.uri},dr.prototype.nsAttributeName=function(e){var t;if(t=l(e)?di(e):e.ns,e.inherited)return{localName:t.localName};var n=this.logNamespaceUsed(t);return this.getNamespaces().logUsed(n),this.isLocalNs(n)?{localName:t.localName}:E({localName:t.localName},n)},dr.prototype.parseGeneric=function(e){var t=this,n=this.body,i=[];return h(e,(function(r,o){"$body"===o?n.push((new mr).build({type:"String"},r)):"$children"===o?h(r,(function(e){n.push(new dr(t).build(e))})):0!==o.indexOf("$")&&t.parseNsAttribute(e,o,r)&&i.push({name:o,value:r})})),i},dr.prototype.parseNsAttribute=function(e,t,n){var i,r=e.$model,o=di(t);if("xmlns"===o.prefix&&(i={prefix:o.localName,uri:n}),o.prefix||"xmlns"!==o.localName||(i={uri:n}),!i)return{name:t,value:n};if(r&&r.getPackage(n))this.logNamespace(i,!0,!0);else{var a=this.logNamespaceUsed(i,!0);this.getNamespaces().logUsed(a)}},dr.prototype.parseNsAttributes=function(e,t){var n=this,i=e.$attrs,r=[];return h(i,(function(t,i){var o=n.parseNsAttribute(e,i,t);o&&r.push(o)})),r},dr.prototype.parseGenericAttributes=function(e,t){var n=this;h(t,(function(t){if(t.name!==Vi)try{n.addAttribute(n.nsAttributeName(t.name),t.value)}catch(n){console.warn("missing namespace information for ",t.name,"=",t.value,"on",e,n)}}))},dr.prototype.parseContainments=function(e){var t=this,n=this.body,i=this.element;h(e,(function(e){var r=i.get(e.name),o=e.isReference;if(e.isMany||(r=[r]),e.isBody)n.push((new mr).build(e,r[0]));else if(fi(e.type))h(r,(function(i){n.push(new fr(t.addTagName(t.nsPropertyTagName(e))).build(e,i))}));else if(o)h(r,(function(i){n.push(new hr(t.addTagName(t.nsPropertyTagName(e))).build(i))}));else{var a=Wi(e),s=function(e){return"property"===zi(e)}(e);h(r,(function(i){var r;r=a?new yr(t,e):s?new dr(t,e):new dr(t),n.push(r.build(i))}))}}))},dr.prototype.getNamespaces=function(e){var t,n=this.namespaces,i=this.parent;return n||(t=i&&i.getNamespaces(),e||!t?this.namespaces=n=new or(t):n=t),n},dr.prototype.logNamespace=function(e,t,n){var i=this.getNamespaces(n),r=e.uri,o=e.prefix;return i.byUri(r)&&!n||i.add(e,t),i.mapPrefix(o,r),e},dr.prototype.logNamespaceUsed=function(e,t){var n,i,r,o=this.element.$model,a=this.getNamespaces(t),s=e.prefix,l=e.uri;if(!s&&!l)return{localName:e.localName};if(r=Fi[s]||o&&(o.getPackage(s)||{}).uri,!(l=l||r||a.uriByPrefix(s)))throw new Error("no namespace uri given for prefix <"+s+">");if(!(e=a.byUri(l))){for(n=s,i=1;a.uriByPrefix(n);)n=s+"_"+i++;e=this.logNamespace({prefix:n,uri:l},r===l)}return s&&a.mapPrefix(s,l),e},dr.prototype.parseAttributes=function(e){var t=this,n=this.element;h(e,(function(e){var i=n.get(e.name);if(e.isReference)if(e.isMany){var r=[];h(i,(function(e){r.push(e.id)})),i=r.join(" ")}else i=i.id;t.addAttribute(t.nsAttributeName(e),i)}))},dr.prototype.addTagName=function(e){var t=this.logNamespaceUsed(e);return this.getNamespaces().logUsed(t),lr(e)},dr.prototype.addAttribute=function(e,t){var n=this.attrs;l(t)&&(t=ur(t,ir,pr));var i=function(e,t){t=g(t);var n=r(e)?-1:void 0;return h(e,(function(e,i){if(t(e,i))return n=i,!1})),n}(n,(function(t){return t.name.localName===e.localName&&t.name.uri===e.uri&&t.name.prefix===e.prefix})),o={name:e,value:t};-1!==i?n.splice(i,1,o):n.push(o)},dr.prototype.serializeAttributes=function(e){var t=this.attrs,n=this.namespaces;n&&(t=function(e){return e.getUsed().filter((function(e){return"xml"!==e.prefix})).map((function(e){return{name:"xmlns"+(e.prefix?":"+e.prefix:""),value:e.uri}}))}(n).concat(t)),h(t,(function(t){e.append(" ").append(lr(t.name)).append('="').append(t.value).append('"')}))},dr.prototype.serializeTo=function(e){var t=this.body[0],n=t&&t.constructor!==mr;e.appendIndent().append("<"+this.tagName),this.serializeAttributes(e),e.append(t?">":" />"),t&&(n&&e.appendNewLine().indent(),h(this.body,(function(t){t.serializeTo(e)})),n&&e.unindent().appendIndent(),e.append("")),e.appendNewLine()},sr(yr,dr),yr.prototype.parseNsAttributes=function(e){var t=dr.prototype.parseNsAttributes.call(this,e),n=e.$descriptor;if(n.name===this.propertyDescriptor.type)return t;var i=this.typeNs=this.nsTagName(n);this.getNamespaces().logUsed(this.typeNs);var r=e.$model.getPackage(i.uri),o=r.xml&&r.xml.typePrefix||"";return this.addAttribute(this.nsAttributeName(Vi),(i.prefix?i.prefix+":":"")+o+n.ns.localName),t},yr.prototype.isLocalNs=function(e){return e.uri===(this.typeNs||this.ns).uri},br.prototype=Object.create(wi.prototype),br.prototype.fromXML=function(e,t,n){l(t)||(n=t,t="bpmn:Definitions");var i=new nr(E({model:this,lax:!0},n)),r=i.handler(t);return i.fromXML(e,r)},br.prototype.toXML=function(e,t){var n=new xr(t);return new Promise((function(t,i){try{return t({xml:n.toXML(e)})}catch(e){return i(e)}}))};var wr={bpmn:{name:"BPMN20",uri:"http://www.omg.org/spec/BPMN/20100524/MODEL",prefix:"bpmn",associations:[],types:[{name:"Interface",superClass:["RootElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"operations",type:"Operation",isMany:!0},{name:"implementationRef",isAttr:!0,type:"String"}]},{name:"Operation",superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"inMessageRef",type:"Message",isReference:!0},{name:"outMessageRef",type:"Message",isReference:!0},{name:"errorRef",type:"Error",isMany:!0,isReference:!0},{name:"implementationRef",isAttr:!0,type:"String"}]},{name:"EndPoint",superClass:["RootElement"]},{name:"Auditing",superClass:["BaseElement"]},{name:"GlobalTask",superClass:["CallableElement"],properties:[{name:"resources",type:"ResourceRole",isMany:!0}]},{name:"Monitoring",superClass:["BaseElement"]},{name:"Performer",superClass:["ResourceRole"]},{name:"Process",superClass:["FlowElementsContainer","CallableElement"],properties:[{name:"processType",type:"ProcessType",isAttr:!0},{name:"isClosed",isAttr:!0,type:"Boolean"},{name:"auditing",type:"Auditing"},{name:"monitoring",type:"Monitoring"},{name:"properties",type:"Property",isMany:!0},{name:"laneSets",isMany:!0,replaces:"FlowElementsContainer#laneSets",type:"LaneSet"},{name:"flowElements",isMany:!0,replaces:"FlowElementsContainer#flowElements",type:"FlowElement"},{name:"artifacts",type:"Artifact",isMany:!0},{name:"resources",type:"ResourceRole",isMany:!0},{name:"correlationSubscriptions",type:"CorrelationSubscription",isMany:!0},{name:"supports",type:"Process",isMany:!0,isReference:!0},{name:"definitionalCollaborationRef",type:"Collaboration",isAttr:!0,isReference:!0},{name:"isExecutable",isAttr:!0,type:"Boolean"}]},{name:"LaneSet",superClass:["BaseElement"],properties:[{name:"lanes",type:"Lane",isMany:!0},{name:"name",isAttr:!0,type:"String"}]},{name:"Lane",superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"partitionElementRef",type:"BaseElement",isAttr:!0,isReference:!0},{name:"partitionElement",type:"BaseElement"},{name:"flowNodeRef",type:"FlowNode",isMany:!0,isReference:!0},{name:"childLaneSet",type:"LaneSet",xml:{serialize:"xsi:type"}}]},{name:"GlobalManualTask",superClass:["GlobalTask"]},{name:"ManualTask",superClass:["Task"]},{name:"UserTask",superClass:["Task"],properties:[{name:"renderings",type:"Rendering",isMany:!0},{name:"implementation",isAttr:!0,type:"String"}]},{name:"Rendering",superClass:["BaseElement"]},{name:"HumanPerformer",superClass:["Performer"]},{name:"PotentialOwner",superClass:["HumanPerformer"]},{name:"GlobalUserTask",superClass:["GlobalTask"],properties:[{name:"implementation",isAttr:!0,type:"String"},{name:"renderings",type:"Rendering",isMany:!0}]},{name:"Gateway",isAbstract:!0,superClass:["FlowNode"],properties:[{name:"gatewayDirection",type:"GatewayDirection",default:"Unspecified",isAttr:!0}]},{name:"EventBasedGateway",superClass:["Gateway"],properties:[{name:"instantiate",default:!1,isAttr:!0,type:"Boolean"},{name:"eventGatewayType",type:"EventBasedGatewayType",isAttr:!0,default:"Exclusive"}]},{name:"ComplexGateway",superClass:["Gateway"],properties:[{name:"activationCondition",type:"Expression",xml:{serialize:"xsi:type"}},{name:"default",type:"SequenceFlow",isAttr:!0,isReference:!0}]},{name:"ExclusiveGateway",superClass:["Gateway"],properties:[{name:"default",type:"SequenceFlow",isAttr:!0,isReference:!0}]},{name:"InclusiveGateway",superClass:["Gateway"],properties:[{name:"default",type:"SequenceFlow",isAttr:!0,isReference:!0}]},{name:"ParallelGateway",superClass:["Gateway"]},{name:"RootElement",isAbstract:!0,superClass:["BaseElement"]},{name:"Relationship",superClass:["BaseElement"],properties:[{name:"type",isAttr:!0,type:"String"},{name:"direction",type:"RelationshipDirection",isAttr:!0},{name:"source",isMany:!0,isReference:!0,type:"Element"},{name:"target",isMany:!0,isReference:!0,type:"Element"}]},{name:"BaseElement",isAbstract:!0,properties:[{name:"id",isAttr:!0,type:"String",isId:!0},{name:"documentation",type:"Documentation",isMany:!0},{name:"extensionDefinitions",type:"ExtensionDefinition",isMany:!0,isReference:!0},{name:"extensionElements",type:"ExtensionElements"}]},{name:"Extension",properties:[{name:"mustUnderstand",default:!1,isAttr:!0,type:"Boolean"},{name:"definition",type:"ExtensionDefinition",isAttr:!0,isReference:!0}]},{name:"ExtensionDefinition",properties:[{name:"name",isAttr:!0,type:"String"},{name:"extensionAttributeDefinitions",type:"ExtensionAttributeDefinition",isMany:!0}]},{name:"ExtensionAttributeDefinition",properties:[{name:"name",isAttr:!0,type:"String"},{name:"type",isAttr:!0,type:"String"},{name:"isReference",default:!1,isAttr:!0,type:"Boolean"},{name:"extensionDefinition",type:"ExtensionDefinition",isAttr:!0,isReference:!0}]},{name:"ExtensionElements",properties:[{name:"valueRef",isAttr:!0,isReference:!0,type:"Element"},{name:"values",type:"Element",isMany:!0},{name:"extensionAttributeDefinition",type:"ExtensionAttributeDefinition",isAttr:!0,isReference:!0}]},{name:"Documentation",superClass:["BaseElement"],properties:[{name:"text",type:"String",isBody:!0},{name:"textFormat",default:"text/plain",isAttr:!0,type:"String"}]},{name:"Event",isAbstract:!0,superClass:["FlowNode","InteractionNode"],properties:[{name:"properties",type:"Property",isMany:!0}]},{name:"IntermediateCatchEvent",superClass:["CatchEvent"]},{name:"IntermediateThrowEvent",superClass:["ThrowEvent"]},{name:"EndEvent",superClass:["ThrowEvent"]},{name:"StartEvent",superClass:["CatchEvent"],properties:[{name:"isInterrupting",default:!0,isAttr:!0,type:"Boolean"}]},{name:"ThrowEvent",isAbstract:!0,superClass:["Event"],properties:[{name:"dataInputs",type:"DataInput",isMany:!0},{name:"dataInputAssociations",type:"DataInputAssociation",isMany:!0},{name:"inputSet",type:"InputSet"},{name:"eventDefinitions",type:"EventDefinition",isMany:!0},{name:"eventDefinitionRef",type:"EventDefinition",isMany:!0,isReference:!0}]},{name:"CatchEvent",isAbstract:!0,superClass:["Event"],properties:[{name:"parallelMultiple",isAttr:!0,type:"Boolean",default:!1},{name:"dataOutputs",type:"DataOutput",isMany:!0},{name:"dataOutputAssociations",type:"DataOutputAssociation",isMany:!0},{name:"outputSet",type:"OutputSet"},{name:"eventDefinitions",type:"EventDefinition",isMany:!0},{name:"eventDefinitionRef",type:"EventDefinition",isMany:!0,isReference:!0}]},{name:"BoundaryEvent",superClass:["CatchEvent"],properties:[{name:"cancelActivity",default:!0,isAttr:!0,type:"Boolean"},{name:"attachedToRef",type:"Activity",isAttr:!0,isReference:!0}]},{name:"EventDefinition",isAbstract:!0,superClass:["RootElement"]},{name:"CancelEventDefinition",superClass:["EventDefinition"]},{name:"ErrorEventDefinition",superClass:["EventDefinition"],properties:[{name:"errorRef",type:"Error",isAttr:!0,isReference:!0}]},{name:"TerminateEventDefinition",superClass:["EventDefinition"]},{name:"EscalationEventDefinition",superClass:["EventDefinition"],properties:[{name:"escalationRef",type:"Escalation",isAttr:!0,isReference:!0}]},{name:"Escalation",properties:[{name:"structureRef",type:"ItemDefinition",isAttr:!0,isReference:!0},{name:"name",isAttr:!0,type:"String"},{name:"escalationCode",isAttr:!0,type:"String"}],superClass:["RootElement"]},{name:"CompensateEventDefinition",superClass:["EventDefinition"],properties:[{name:"waitForCompletion",isAttr:!0,type:"Boolean",default:!0},{name:"activityRef",type:"Activity",isAttr:!0,isReference:!0}]},{name:"TimerEventDefinition",superClass:["EventDefinition"],properties:[{name:"timeDate",type:"Expression",xml:{serialize:"xsi:type"}},{name:"timeCycle",type:"Expression",xml:{serialize:"xsi:type"}},{name:"timeDuration",type:"Expression",xml:{serialize:"xsi:type"}}]},{name:"LinkEventDefinition",superClass:["EventDefinition"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"target",type:"LinkEventDefinition",isAttr:!0,isReference:!0},{name:"source",type:"LinkEventDefinition",isMany:!0,isReference:!0}]},{name:"MessageEventDefinition",superClass:["EventDefinition"],properties:[{name:"messageRef",type:"Message",isAttr:!0,isReference:!0},{name:"operationRef",type:"Operation",isAttr:!0,isReference:!0}]},{name:"ConditionalEventDefinition",superClass:["EventDefinition"],properties:[{name:"condition",type:"Expression",xml:{serialize:"xsi:type"}}]},{name:"SignalEventDefinition",superClass:["EventDefinition"],properties:[{name:"signalRef",type:"Signal",isAttr:!0,isReference:!0}]},{name:"Signal",superClass:["RootElement"],properties:[{name:"structureRef",type:"ItemDefinition",isAttr:!0,isReference:!0},{name:"name",isAttr:!0,type:"String"}]},{name:"ImplicitThrowEvent",superClass:["ThrowEvent"]},{name:"DataState",superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"}]},{name:"ItemAwareElement",superClass:["BaseElement"],properties:[{name:"itemSubjectRef",type:"ItemDefinition",isAttr:!0,isReference:!0},{name:"dataState",type:"DataState"}]},{name:"DataAssociation",superClass:["BaseElement"],properties:[{name:"sourceRef",type:"ItemAwareElement",isMany:!0,isReference:!0},{name:"targetRef",type:"ItemAwareElement",isReference:!0},{name:"transformation",type:"FormalExpression",xml:{serialize:"property"}},{name:"assignment",type:"Assignment",isMany:!0}]},{name:"DataInput",superClass:["ItemAwareElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"isCollection",default:!1,isAttr:!0,type:"Boolean"},{name:"inputSetRef",type:"InputSet",isMany:!0,isVirtual:!0,isReference:!0},{name:"inputSetWithOptional",type:"InputSet",isMany:!0,isVirtual:!0,isReference:!0},{name:"inputSetWithWhileExecuting",type:"InputSet",isMany:!0,isVirtual:!0,isReference:!0}]},{name:"DataOutput",superClass:["ItemAwareElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"isCollection",default:!1,isAttr:!0,type:"Boolean"},{name:"outputSetRef",type:"OutputSet",isMany:!0,isVirtual:!0,isReference:!0},{name:"outputSetWithOptional",type:"OutputSet",isMany:!0,isVirtual:!0,isReference:!0},{name:"outputSetWithWhileExecuting",type:"OutputSet",isMany:!0,isVirtual:!0,isReference:!0}]},{name:"InputSet",superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"dataInputRefs",type:"DataInput",isMany:!0,isReference:!0},{name:"optionalInputRefs",type:"DataInput",isMany:!0,isReference:!0},{name:"whileExecutingInputRefs",type:"DataInput",isMany:!0,isReference:!0},{name:"outputSetRefs",type:"OutputSet",isMany:!0,isReference:!0}]},{name:"OutputSet",superClass:["BaseElement"],properties:[{name:"dataOutputRefs",type:"DataOutput",isMany:!0,isReference:!0},{name:"name",isAttr:!0,type:"String"},{name:"inputSetRefs",type:"InputSet",isMany:!0,isReference:!0},{name:"optionalOutputRefs",type:"DataOutput",isMany:!0,isReference:!0},{name:"whileExecutingOutputRefs",type:"DataOutput",isMany:!0,isReference:!0}]},{name:"Property",superClass:["ItemAwareElement"],properties:[{name:"name",isAttr:!0,type:"String"}]},{name:"DataInputAssociation",superClass:["DataAssociation"]},{name:"DataOutputAssociation",superClass:["DataAssociation"]},{name:"InputOutputSpecification",superClass:["BaseElement"],properties:[{name:"dataInputs",type:"DataInput",isMany:!0},{name:"dataOutputs",type:"DataOutput",isMany:!0},{name:"inputSets",type:"InputSet",isMany:!0},{name:"outputSets",type:"OutputSet",isMany:!0}]},{name:"DataObject",superClass:["FlowElement","ItemAwareElement"],properties:[{name:"isCollection",default:!1,isAttr:!0,type:"Boolean"}]},{name:"InputOutputBinding",properties:[{name:"inputDataRef",type:"InputSet",isAttr:!0,isReference:!0},{name:"outputDataRef",type:"OutputSet",isAttr:!0,isReference:!0},{name:"operationRef",type:"Operation",isAttr:!0,isReference:!0}]},{name:"Assignment",superClass:["BaseElement"],properties:[{name:"from",type:"Expression",xml:{serialize:"xsi:type"}},{name:"to",type:"Expression",xml:{serialize:"xsi:type"}}]},{name:"DataStore",superClass:["RootElement","ItemAwareElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"capacity",isAttr:!0,type:"Integer"},{name:"isUnlimited",default:!0,isAttr:!0,type:"Boolean"}]},{name:"DataStoreReference",superClass:["ItemAwareElement","FlowElement"],properties:[{name:"dataStoreRef",type:"DataStore",isAttr:!0,isReference:!0}]},{name:"DataObjectReference",superClass:["ItemAwareElement","FlowElement"],properties:[{name:"dataObjectRef",type:"DataObject",isAttr:!0,isReference:!0}]},{name:"ConversationLink",superClass:["BaseElement"],properties:[{name:"sourceRef",type:"InteractionNode",isAttr:!0,isReference:!0},{name:"targetRef",type:"InteractionNode",isAttr:!0,isReference:!0},{name:"name",isAttr:!0,type:"String"}]},{name:"ConversationAssociation",superClass:["BaseElement"],properties:[{name:"innerConversationNodeRef",type:"ConversationNode",isAttr:!0,isReference:!0},{name:"outerConversationNodeRef",type:"ConversationNode",isAttr:!0,isReference:!0}]},{name:"CallConversation",superClass:["ConversationNode"],properties:[{name:"calledCollaborationRef",type:"Collaboration",isAttr:!0,isReference:!0},{name:"participantAssociations",type:"ParticipantAssociation",isMany:!0}]},{name:"Conversation",superClass:["ConversationNode"]},{name:"SubConversation",superClass:["ConversationNode"],properties:[{name:"conversationNodes",type:"ConversationNode",isMany:!0}]},{name:"ConversationNode",isAbstract:!0,superClass:["InteractionNode","BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"participantRef",type:"Participant",isMany:!0,isReference:!0},{name:"messageFlowRefs",type:"MessageFlow",isMany:!0,isReference:!0},{name:"correlationKeys",type:"CorrelationKey",isMany:!0}]},{name:"GlobalConversation",superClass:["Collaboration"]},{name:"PartnerEntity",superClass:["RootElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"participantRef",type:"Participant",isMany:!0,isReference:!0}]},{name:"PartnerRole",superClass:["RootElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"participantRef",type:"Participant",isMany:!0,isReference:!0}]},{name:"CorrelationProperty",superClass:["RootElement"],properties:[{name:"correlationPropertyRetrievalExpression",type:"CorrelationPropertyRetrievalExpression",isMany:!0},{name:"name",isAttr:!0,type:"String"},{name:"type",type:"ItemDefinition",isAttr:!0,isReference:!0}]},{name:"Error",superClass:["RootElement"],properties:[{name:"structureRef",type:"ItemDefinition",isAttr:!0,isReference:!0},{name:"name",isAttr:!0,type:"String"},{name:"errorCode",isAttr:!0,type:"String"}]},{name:"CorrelationKey",superClass:["BaseElement"],properties:[{name:"correlationPropertyRef",type:"CorrelationProperty",isMany:!0,isReference:!0},{name:"name",isAttr:!0,type:"String"}]},{name:"Expression",superClass:["BaseElement"],isAbstract:!1,properties:[{name:"body",isBody:!0,type:"String"}]},{name:"FormalExpression",superClass:["Expression"],properties:[{name:"language",isAttr:!0,type:"String"},{name:"evaluatesToTypeRef",type:"ItemDefinition",isAttr:!0,isReference:!0}]},{name:"Message",superClass:["RootElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"itemRef",type:"ItemDefinition",isAttr:!0,isReference:!0}]},{name:"ItemDefinition",superClass:["RootElement"],properties:[{name:"itemKind",type:"ItemKind",isAttr:!0},{name:"structureRef",isAttr:!0,type:"String"},{name:"isCollection",default:!1,isAttr:!0,type:"Boolean"},{name:"import",type:"Import",isAttr:!0,isReference:!0}]},{name:"FlowElement",isAbstract:!0,superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"auditing",type:"Auditing"},{name:"monitoring",type:"Monitoring"},{name:"categoryValueRef",type:"CategoryValue",isMany:!0,isReference:!0}]},{name:"SequenceFlow",superClass:["FlowElement"],properties:[{name:"isImmediate",isAttr:!0,type:"Boolean"},{name:"conditionExpression",type:"Expression",xml:{serialize:"xsi:type"}},{name:"sourceRef",type:"FlowNode",isAttr:!0,isReference:!0},{name:"targetRef",type:"FlowNode",isAttr:!0,isReference:!0}]},{name:"FlowElementsContainer",isAbstract:!0,superClass:["BaseElement"],properties:[{name:"laneSets",type:"LaneSet",isMany:!0},{name:"flowElements",type:"FlowElement",isMany:!0}]},{name:"CallableElement",isAbstract:!0,superClass:["RootElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"ioSpecification",type:"InputOutputSpecification",xml:{serialize:"property"}},{name:"supportedInterfaceRef",type:"Interface",isMany:!0,isReference:!0},{name:"ioBinding",type:"InputOutputBinding",isMany:!0,xml:{serialize:"property"}}]},{name:"FlowNode",isAbstract:!0,superClass:["FlowElement"],properties:[{name:"incoming",type:"SequenceFlow",isMany:!0,isReference:!0},{name:"outgoing",type:"SequenceFlow",isMany:!0,isReference:!0},{name:"lanes",type:"Lane",isMany:!0,isVirtual:!0,isReference:!0}]},{name:"CorrelationPropertyRetrievalExpression",superClass:["BaseElement"],properties:[{name:"messagePath",type:"FormalExpression"},{name:"messageRef",type:"Message",isAttr:!0,isReference:!0}]},{name:"CorrelationPropertyBinding",superClass:["BaseElement"],properties:[{name:"dataPath",type:"FormalExpression"},{name:"correlationPropertyRef",type:"CorrelationProperty",isAttr:!0,isReference:!0}]},{name:"Resource",superClass:["RootElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"resourceParameters",type:"ResourceParameter",isMany:!0}]},{name:"ResourceParameter",superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"isRequired",isAttr:!0,type:"Boolean"},{name:"type",type:"ItemDefinition",isAttr:!0,isReference:!0}]},{name:"CorrelationSubscription",superClass:["BaseElement"],properties:[{name:"correlationKeyRef",type:"CorrelationKey",isAttr:!0,isReference:!0},{name:"correlationPropertyBinding",type:"CorrelationPropertyBinding",isMany:!0}]},{name:"MessageFlow",superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"sourceRef",type:"InteractionNode",isAttr:!0,isReference:!0},{name:"targetRef",type:"InteractionNode",isAttr:!0,isReference:!0},{name:"messageRef",type:"Message",isAttr:!0,isReference:!0}]},{name:"MessageFlowAssociation",superClass:["BaseElement"],properties:[{name:"innerMessageFlowRef",type:"MessageFlow",isAttr:!0,isReference:!0},{name:"outerMessageFlowRef",type:"MessageFlow",isAttr:!0,isReference:!0}]},{name:"InteractionNode",isAbstract:!0,properties:[{name:"incomingConversationLinks",type:"ConversationLink",isMany:!0,isVirtual:!0,isReference:!0},{name:"outgoingConversationLinks",type:"ConversationLink",isMany:!0,isVirtual:!0,isReference:!0}]},{name:"Participant",superClass:["InteractionNode","BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"interfaceRef",type:"Interface",isMany:!0,isReference:!0},{name:"participantMultiplicity",type:"ParticipantMultiplicity"},{name:"endPointRefs",type:"EndPoint",isMany:!0,isReference:!0},{name:"processRef",type:"Process",isAttr:!0,isReference:!0}]},{name:"ParticipantAssociation",superClass:["BaseElement"],properties:[{name:"innerParticipantRef",type:"Participant",isAttr:!0,isReference:!0},{name:"outerParticipantRef",type:"Participant",isAttr:!0,isReference:!0}]},{name:"ParticipantMultiplicity",properties:[{name:"minimum",default:0,isAttr:!0,type:"Integer"},{name:"maximum",default:1,isAttr:!0,type:"Integer"}],superClass:["BaseElement"]},{name:"Collaboration",superClass:["RootElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"isClosed",isAttr:!0,type:"Boolean"},{name:"participants",type:"Participant",isMany:!0},{name:"messageFlows",type:"MessageFlow",isMany:!0},{name:"artifacts",type:"Artifact",isMany:!0},{name:"conversations",type:"ConversationNode",isMany:!0},{name:"conversationAssociations",type:"ConversationAssociation"},{name:"participantAssociations",type:"ParticipantAssociation",isMany:!0},{name:"messageFlowAssociations",type:"MessageFlowAssociation",isMany:!0},{name:"correlationKeys",type:"CorrelationKey",isMany:!0},{name:"choreographyRef",type:"Choreography",isMany:!0,isReference:!0},{name:"conversationLinks",type:"ConversationLink",isMany:!0}]},{name:"ChoreographyActivity",isAbstract:!0,superClass:["FlowNode"],properties:[{name:"participantRef",type:"Participant",isMany:!0,isReference:!0},{name:"initiatingParticipantRef",type:"Participant",isAttr:!0,isReference:!0},{name:"correlationKeys",type:"CorrelationKey",isMany:!0},{name:"loopType",type:"ChoreographyLoopType",default:"None",isAttr:!0}]},{name:"CallChoreography",superClass:["ChoreographyActivity"],properties:[{name:"calledChoreographyRef",type:"Choreography",isAttr:!0,isReference:!0},{name:"participantAssociations",type:"ParticipantAssociation",isMany:!0}]},{name:"SubChoreography",superClass:["ChoreographyActivity","FlowElementsContainer"],properties:[{name:"artifacts",type:"Artifact",isMany:!0}]},{name:"ChoreographyTask",superClass:["ChoreographyActivity"],properties:[{name:"messageFlowRef",type:"MessageFlow",isMany:!0,isReference:!0}]},{name:"Choreography",superClass:["Collaboration","FlowElementsContainer"]},{name:"GlobalChoreographyTask",superClass:["Choreography"],properties:[{name:"initiatingParticipantRef",type:"Participant",isAttr:!0,isReference:!0}]},{name:"TextAnnotation",superClass:["Artifact"],properties:[{name:"text",type:"String"},{name:"textFormat",default:"text/plain",isAttr:!0,type:"String"}]},{name:"Group",superClass:["Artifact"],properties:[{name:"categoryValueRef",type:"CategoryValue",isAttr:!0,isReference:!0}]},{name:"Association",superClass:["Artifact"],properties:[{name:"associationDirection",type:"AssociationDirection",isAttr:!0},{name:"sourceRef",type:"BaseElement",isAttr:!0,isReference:!0},{name:"targetRef",type:"BaseElement",isAttr:!0,isReference:!0}]},{name:"Category",superClass:["RootElement"],properties:[{name:"categoryValue",type:"CategoryValue",isMany:!0},{name:"name",isAttr:!0,type:"String"}]},{name:"Artifact",isAbstract:!0,superClass:["BaseElement"]},{name:"CategoryValue",superClass:["BaseElement"],properties:[{name:"categorizedFlowElements",type:"FlowElement",isMany:!0,isVirtual:!0,isReference:!0},{name:"value",isAttr:!0,type:"String"}]},{name:"Activity",isAbstract:!0,superClass:["FlowNode"],properties:[{name:"isForCompensation",default:!1,isAttr:!0,type:"Boolean"},{name:"default",type:"SequenceFlow",isAttr:!0,isReference:!0},{name:"ioSpecification",type:"InputOutputSpecification",xml:{serialize:"property"}},{name:"boundaryEventRefs",type:"BoundaryEvent",isMany:!0,isReference:!0},{name:"properties",type:"Property",isMany:!0},{name:"dataInputAssociations",type:"DataInputAssociation",isMany:!0},{name:"dataOutputAssociations",type:"DataOutputAssociation",isMany:!0},{name:"startQuantity",default:1,isAttr:!0,type:"Integer"},{name:"resources",type:"ResourceRole",isMany:!0},{name:"completionQuantity",default:1,isAttr:!0,type:"Integer"},{name:"loopCharacteristics",type:"LoopCharacteristics"}]},{name:"ServiceTask",superClass:["Task"],properties:[{name:"implementation",isAttr:!0,type:"String"},{name:"operationRef",type:"Operation",isAttr:!0,isReference:!0}]},{name:"SubProcess",superClass:["Activity","FlowElementsContainer","InteractionNode"],properties:[{name:"triggeredByEvent",default:!1,isAttr:!0,type:"Boolean"},{name:"artifacts",type:"Artifact",isMany:!0}]},{name:"LoopCharacteristics",isAbstract:!0,superClass:["BaseElement"]},{name:"MultiInstanceLoopCharacteristics",superClass:["LoopCharacteristics"],properties:[{name:"isSequential",default:!1,isAttr:!0,type:"Boolean"},{name:"behavior",type:"MultiInstanceBehavior",default:"All",isAttr:!0},{name:"loopCardinality",type:"Expression",xml:{serialize:"xsi:type"}},{name:"loopDataInputRef",type:"ItemAwareElement",isReference:!0},{name:"loopDataOutputRef",type:"ItemAwareElement",isReference:!0},{name:"inputDataItem",type:"DataInput",xml:{serialize:"property"}},{name:"outputDataItem",type:"DataOutput",xml:{serialize:"property"}},{name:"complexBehaviorDefinition",type:"ComplexBehaviorDefinition",isMany:!0},{name:"completionCondition",type:"Expression",xml:{serialize:"xsi:type"}},{name:"oneBehaviorEventRef",type:"EventDefinition",isAttr:!0,isReference:!0},{name:"noneBehaviorEventRef",type:"EventDefinition",isAttr:!0,isReference:!0}]},{name:"StandardLoopCharacteristics",superClass:["LoopCharacteristics"],properties:[{name:"testBefore",default:!1,isAttr:!0,type:"Boolean"},{name:"loopCondition",type:"Expression",xml:{serialize:"xsi:type"}},{name:"loopMaximum",type:"Integer",isAttr:!0}]},{name:"CallActivity",superClass:["Activity","InteractionNode"],properties:[{name:"calledElement",type:"String",isAttr:!0}]},{name:"Task",superClass:["Activity","InteractionNode"]},{name:"SendTask",superClass:["Task"],properties:[{name:"implementation",isAttr:!0,type:"String"},{name:"operationRef",type:"Operation",isAttr:!0,isReference:!0},{name:"messageRef",type:"Message",isAttr:!0,isReference:!0}]},{name:"ReceiveTask",superClass:["Task"],properties:[{name:"implementation",isAttr:!0,type:"String"},{name:"instantiate",default:!1,isAttr:!0,type:"Boolean"},{name:"operationRef",type:"Operation",isAttr:!0,isReference:!0},{name:"messageRef",type:"Message",isAttr:!0,isReference:!0}]},{name:"ScriptTask",superClass:["Task"],properties:[{name:"scriptFormat",isAttr:!0,type:"String"},{name:"script",type:"String"}]},{name:"BusinessRuleTask",superClass:["Task"],properties:[{name:"implementation",isAttr:!0,type:"String"}]},{name:"AdHocSubProcess",superClass:["SubProcess"],properties:[{name:"completionCondition",type:"Expression",xml:{serialize:"xsi:type"}},{name:"ordering",type:"AdHocOrdering",isAttr:!0},{name:"cancelRemainingInstances",default:!0,isAttr:!0,type:"Boolean"}]},{name:"Transaction",superClass:["SubProcess"],properties:[{name:"protocol",isAttr:!0,type:"String"},{name:"method",isAttr:!0,type:"String"}]},{name:"GlobalScriptTask",superClass:["GlobalTask"],properties:[{name:"scriptLanguage",isAttr:!0,type:"String"},{name:"script",isAttr:!0,type:"String"}]},{name:"GlobalBusinessRuleTask",superClass:["GlobalTask"],properties:[{name:"implementation",isAttr:!0,type:"String"}]},{name:"ComplexBehaviorDefinition",superClass:["BaseElement"],properties:[{name:"condition",type:"FormalExpression"},{name:"event",type:"ImplicitThrowEvent"}]},{name:"ResourceRole",superClass:["BaseElement"],properties:[{name:"resourceRef",type:"Resource",isReference:!0},{name:"resourceParameterBindings",type:"ResourceParameterBinding",isMany:!0},{name:"resourceAssignmentExpression",type:"ResourceAssignmentExpression"},{name:"name",isAttr:!0,type:"String"}]},{name:"ResourceParameterBinding",properties:[{name:"expression",type:"Expression",xml:{serialize:"xsi:type"}},{name:"parameterRef",type:"ResourceParameter",isAttr:!0,isReference:!0}],superClass:["BaseElement"]},{name:"ResourceAssignmentExpression",properties:[{name:"expression",type:"Expression",xml:{serialize:"xsi:type"}}],superClass:["BaseElement"]},{name:"Import",properties:[{name:"importType",isAttr:!0,type:"String"},{name:"location",isAttr:!0,type:"String"},{name:"namespace",isAttr:!0,type:"String"}]},{name:"Definitions",superClass:["BaseElement"],properties:[{name:"name",isAttr:!0,type:"String"},{name:"targetNamespace",isAttr:!0,type:"String"},{name:"expressionLanguage",default:"http://www.w3.org/1999/XPath",isAttr:!0,type:"String"},{name:"typeLanguage",default:"http://www.w3.org/2001/XMLSchema",isAttr:!0,type:"String"},{name:"imports",type:"Import",isMany:!0},{name:"extensions",type:"Extension",isMany:!0},{name:"rootElements",type:"RootElement",isMany:!0},{name:"diagrams",isMany:!0,type:"bpmndi:BPMNDiagram"},{name:"exporter",isAttr:!0,type:"String"},{name:"relationships",type:"Relationship",isMany:!0},{name:"exporterVersion",isAttr:!0,type:"String"}]}],enumerations:[{name:"ProcessType",literalValues:[{name:"None"},{name:"Public"},{name:"Private"}]},{name:"GatewayDirection",literalValues:[{name:"Unspecified"},{name:"Converging"},{name:"Diverging"},{name:"Mixed"}]},{name:"EventBasedGatewayType",literalValues:[{name:"Parallel"},{name:"Exclusive"}]},{name:"RelationshipDirection",literalValues:[{name:"None"},{name:"Forward"},{name:"Backward"},{name:"Both"}]},{name:"ItemKind",literalValues:[{name:"Physical"},{name:"Information"}]},{name:"ChoreographyLoopType",literalValues:[{name:"None"},{name:"Standard"},{name:"MultiInstanceSequential"},{name:"MultiInstanceParallel"}]},{name:"AssociationDirection",literalValues:[{name:"None"},{name:"One"},{name:"Both"}]},{name:"MultiInstanceBehavior",literalValues:[{name:"None"},{name:"One"},{name:"All"},{name:"Complex"}]},{name:"AdHocOrdering",literalValues:[{name:"Parallel"},{name:"Sequential"}]}],xml:{tagAlias:"lowerCase",typePrefix:"t"}},bpmndi:{name:"BPMNDI",uri:"http://www.omg.org/spec/BPMN/20100524/DI",prefix:"bpmndi",types:[{name:"BPMNDiagram",properties:[{name:"plane",type:"BPMNPlane",redefines:"di:Diagram#rootElement"},{name:"labelStyle",type:"BPMNLabelStyle",isMany:!0}],superClass:["di:Diagram"]},{name:"BPMNPlane",properties:[{name:"bpmnElement",isAttr:!0,isReference:!0,type:"bpmn:BaseElement",redefines:"di:DiagramElement#modelElement"}],superClass:["di:Plane"]},{name:"BPMNShape",properties:[{name:"bpmnElement",isAttr:!0,isReference:!0,type:"bpmn:BaseElement",redefines:"di:DiagramElement#modelElement"},{name:"isHorizontal",isAttr:!0,type:"Boolean"},{name:"isExpanded",isAttr:!0,type:"Boolean"},{name:"isMarkerVisible",isAttr:!0,type:"Boolean"},{name:"label",type:"BPMNLabel"},{name:"isMessageVisible",isAttr:!0,type:"Boolean"},{name:"participantBandKind",type:"ParticipantBandKind",isAttr:!0},{name:"choreographyActivityShape",type:"BPMNShape",isAttr:!0,isReference:!0}],superClass:["di:LabeledShape"]},{name:"BPMNEdge",properties:[{name:"label",type:"BPMNLabel"},{name:"bpmnElement",isAttr:!0,isReference:!0,type:"bpmn:BaseElement",redefines:"di:DiagramElement#modelElement"},{name:"sourceElement",isAttr:!0,isReference:!0,type:"di:DiagramElement",redefines:"di:Edge#source"},{name:"targetElement",isAttr:!0,isReference:!0,type:"di:DiagramElement",redefines:"di:Edge#target"},{name:"messageVisibleKind",type:"MessageVisibleKind",isAttr:!0,default:"initiating"}],superClass:["di:LabeledEdge"]},{name:"BPMNLabel",properties:[{name:"labelStyle",type:"BPMNLabelStyle",isAttr:!0,isReference:!0,redefines:"di:DiagramElement#style"}],superClass:["di:Label"]},{name:"BPMNLabelStyle",properties:[{name:"font",type:"dc:Font"}],superClass:["di:Style"]}],enumerations:[{name:"ParticipantBandKind",literalValues:[{name:"top_initiating"},{name:"middle_initiating"},{name:"bottom_initiating"},{name:"top_non_initiating"},{name:"middle_non_initiating"},{name:"bottom_non_initiating"}]},{name:"MessageVisibleKind",literalValues:[{name:"initiating"},{name:"non_initiating"}]}],associations:[]},dc:{name:"DC",uri:"http://www.omg.org/spec/DD/20100524/DC",prefix:"dc",types:[{name:"Boolean"},{name:"Integer"},{name:"Real"},{name:"String"},{name:"Font",properties:[{name:"name",type:"String",isAttr:!0},{name:"size",type:"Real",isAttr:!0},{name:"isBold",type:"Boolean",isAttr:!0},{name:"isItalic",type:"Boolean",isAttr:!0},{name:"isUnderline",type:"Boolean",isAttr:!0},{name:"isStrikeThrough",type:"Boolean",isAttr:!0}]},{name:"Point",properties:[{name:"x",type:"Real",default:"0",isAttr:!0},{name:"y",type:"Real",default:"0",isAttr:!0}]},{name:"Bounds",properties:[{name:"x",type:"Real",default:"0",isAttr:!0},{name:"y",type:"Real",default:"0",isAttr:!0},{name:"width",type:"Real",isAttr:!0},{name:"height",type:"Real",isAttr:!0}]}],associations:[]},di:{name:"DI",uri:"http://www.omg.org/spec/DD/20100524/DI",prefix:"di",types:[{name:"DiagramElement",isAbstract:!0,properties:[{name:"id",isAttr:!0,isId:!0,type:"String"},{name:"extension",type:"Extension"},{name:"owningDiagram",type:"Diagram",isReadOnly:!0,isVirtual:!0,isReference:!0},{name:"owningElement",type:"DiagramElement",isReadOnly:!0,isVirtual:!0,isReference:!0},{name:"modelElement",isReadOnly:!0,isVirtual:!0,isReference:!0,type:"Element"},{name:"style",type:"Style",isReadOnly:!0,isVirtual:!0,isReference:!0},{name:"ownedElement",type:"DiagramElement",isReadOnly:!0,isMany:!0,isVirtual:!0}]},{name:"Node",isAbstract:!0,superClass:["DiagramElement"]},{name:"Edge",isAbstract:!0,superClass:["DiagramElement"],properties:[{name:"source",type:"DiagramElement",isReadOnly:!0,isVirtual:!0,isReference:!0},{name:"target",type:"DiagramElement",isReadOnly:!0,isVirtual:!0,isReference:!0},{name:"waypoint",isUnique:!1,isMany:!0,type:"dc:Point",xml:{serialize:"xsi:type"}}]},{name:"Diagram",isAbstract:!0,properties:[{name:"id",isAttr:!0,isId:!0,type:"String"},{name:"rootElement",type:"DiagramElement",isReadOnly:!0,isVirtual:!0},{name:"name",isAttr:!0,type:"String"},{name:"documentation",isAttr:!0,type:"String"},{name:"resolution",isAttr:!0,type:"Real"},{name:"ownedStyle",type:"Style",isReadOnly:!0,isMany:!0,isVirtual:!0}]},{name:"Shape",isAbstract:!0,superClass:["Node"],properties:[{name:"bounds",type:"dc:Bounds"}]},{name:"Plane",isAbstract:!0,superClass:["Node"],properties:[{name:"planeElement",type:"DiagramElement",subsettedProperty:"DiagramElement-ownedElement",isMany:!0}]},{name:"LabeledEdge",isAbstract:!0,superClass:["Edge"],properties:[{name:"ownedLabel",type:"Label",isReadOnly:!0,subsettedProperty:"DiagramElement-ownedElement",isMany:!0,isVirtual:!0}]},{name:"LabeledShape",isAbstract:!0,superClass:["Shape"],properties:[{name:"ownedLabel",type:"Label",isReadOnly:!0,subsettedProperty:"DiagramElement-ownedElement",isMany:!0,isVirtual:!0}]},{name:"Label",isAbstract:!0,superClass:["Node"],properties:[{name:"bounds",type:"dc:Bounds"}]},{name:"Style",isAbstract:!0,properties:[{name:"id",isAttr:!0,isId:!0,type:"String"}]},{name:"Extension",properties:[{name:"values",isMany:!0,type:"Element"}]}],associations:[],xml:{tagAlias:"lowerCase"}},bioc:{name:"bpmn.io colors for BPMN",uri:"http://bpmn.io/schema/bpmn/biocolor/1.0",prefix:"bioc",types:[{name:"ColoredShape",extends:["bpmndi:BPMNShape"],properties:[{name:"stroke",isAttr:!0,type:"String"},{name:"fill",isAttr:!0,type:"String"}]},{name:"ColoredEdge",extends:["bpmndi:BPMNEdge"],properties:[{name:"stroke",isAttr:!0,type:"String"},{name:"fill",isAttr:!0,type:"String"}]}],enumerations:[],associations:[]},color:{name:"BPMN in Color",uri:"http://www.omg.org/spec/BPMN/non-normative/color/1.0",prefix:"color",types:[{name:"ColoredLabel",extends:["bpmndi:BPMNLabel"],properties:[{name:"color",isAttr:!0,type:"String"}]},{name:"ColoredShape",extends:["bpmndi:BPMNShape"],properties:[{name:"background-color",isAttr:!0,type:"String"},{name:"border-color",isAttr:!0,type:"String"}]},{name:"ColoredEdge",extends:["bpmndi:BPMNEdge"],properties:[{name:"border-color",isAttr:!0,type:"String"}]}],enumerations:[],associations:[]}};function Er(e,t){return new br(E({},wr,e),t)}function _r(e){return function(){if(!window.Promise)throw new Error("Promises is not supported in this environment. Please polyfill Promise.");var t=arguments.length;if(!(t>=1&&s(arguments[t-1])))return e.apply(this,arguments);var n=arguments[t-1];console.warn(new Error("Passing callbacks to "+e.name+" is deprecated and will be removed in a future major release. Please switch to promises: https://bpmn.io/l/moving-to-promises.html"));var i=Array.prototype.slice.call(arguments,0,-1);e.apply(this,i).then((function(e){var t=Object.keys(e)[0];return n(null,e[t])}),(function(e){return n(e,e.warnings)}))}}function Ar(e,t){return e.$instanceOf(t)}function Sr(e,t){var n={},i=[],r={};function o(e,t){return function(n){e(n,t)}}function a(e){n[e.id]=e}function s(n,i){try{var o=r[n.id]&&function(n,i){if(n.gfx)throw new Error(t("already rendered {element}",{element:gt(n)}));return e.element(n,r[n.id],i)}(n,i);return a(n),o}catch(e){l(e.message,{element:n,error:e}),console.error(t("failed to import {element}",{element:gt(n)})),console.error(e)}}function l(t,n){e.error(t,n)}function m(e){var n,i=e.bpmnElement;i?r[i.id]?l(t("multiple DI elements defined for {element}",{element:gt(i)}),{element:i}):(r[i.id]=e,p(n=i,"di")||Object.defineProperty(n,"di",{enumerable:!1,get:function(){throw new Error("Tried to access di from the businessObject. The di is available through the diagram element only. For more information, see https://github.com/bpmn-io/bpmn-js/issues/1472")}})):l(t("no bpmnElement referenced in {element}",{element:gt(e)}),{element:e})}function f(e){var t;m(t=e.plane),h(t.planeElement,d)}function d(e){m(e)}function y(){for(;i.length;)i.shift()()}function g(e,t){P(e,t),A(e.ioSpecification,t),_(e.artifacts,t),a(e)}function v(e,t){s(e,t)}function x(e,t){s(e,t)}function b(e,t){s(e,t)}function w(e,t){s(e,t)}function E(e,t){s(e,t)}function _(e,t){h(e,(function(e){Ar(e,"bpmn:Association")?i.push((function(){E(e,t)})):E(e,t)}))}function A(e,t){e&&(h(e.dataInputs,o(b,t)),h(e.dataOutputs,o(w,t)))}function S(e,t){P(e,t),_(e.artifacts,t)}function R(e,t){var n=s(e,t);Ar(e,"bpmn:SubProcess")&&S(e,n||t),Ar(e,"bpmn:Activity")&&A(e.ioSpecification,t),i.push((function(){h(e.dataInputAssociations,o(x,t)),h(e.dataOutputAssociations,o(x,t))}))}function C(e,t){s(e,t)}function M(e,t){i.push((function(){var n=s(e,t);e.childLaneSet&&k(e.childLaneSet,n||t),function(e){h(e.flowNodeRef,(function(t){var n=t.get("lanes");n&&n.push(e)}))}(e)}))}function k(e,t){h(e.lanes,o(M,t))}function P(e,n){!function(e,n){h(e,(function(e){Ar(e,"bpmn:SequenceFlow")?i.push((function(){!function(e,t){s(e,t)}(e,n)})):Ar(e,"bpmn:BoundaryEvent")?i.unshift((function(){R(e,n)})):Ar(e,"bpmn:FlowNode")?R(e,n):Ar(e,"bpmn:DataObject")||(Ar(e,"bpmn:DataStoreReference")||Ar(e,"bpmn:DataObjectReference")?C(e,n):l(t("unrecognized flowElement {element} in context {context}",{element:gt(e),context:n?gt(n.businessObject):"null"}),{element:e,context:n}))}))}(e.flowElements,n),e.laneSets&&function(e,t){h(e,o(k,t))}(e.laneSets,n)}function T(e,t){var n=s(e,t),i=e.processRef;i&&g(i,n||t)}return{handleDeferred:y,handleDefinitions:function(a,s){var p=a.diagrams;if(s&&-1===p.indexOf(s))throw new Error(t("diagram not part of bpmn:Definitions"));if(!s&&p&&p.length&&(s=p[0]),!s)throw new Error(t("no diagram to display"));r={},f(s);var d=s.plane;if(!d)throw new Error(t("no plane for {element}",{element:gt(s)}));var x=d.bpmnElement;if(!x){if(x=function(e){return c(e.rootElements,(function(e){return Ar(e,"bpmn:Process")||Ar(e,"bpmn:Collaboration")}))}(a),!x)throw new Error(t("no process or collaboration to display"));l(t("correcting missing bpmnElement on {plane} to {rootElement}",{plane:gt(d),rootElement:gt(x)})),d.bpmnElement=x,m(d)}var b,w,E=function(t,n){return e.root(t,r[t.id],n)}(x,d);if(Ar(x,"bpmn:Process")||Ar(x,"bpmn:SubProcess"))g(x,E);else{if(!Ar(x,"bpmn:Collaboration"))throw new Error(t("unsupported bpmnElement for {plane}: {rootElement}",{plane:gt(d),rootElement:gt(x)}));w=E,h((b=x).participants,o(T,w)),_(b.artifacts,w),i.push((function(){!function(e,t){h(e,o(v,t))}(b.messageFlows,w)})),function(e,t){var i=u(e,(function(e){return!n[e.id]&&Ar(e,"bpmn:Process")&&e.laneSets}));i.forEach(o(g,t))}(a.rootElements,E)}y()},handleSubProcess:S,registerDi:m}}function Rr(e,t,n){var i,r,o,a,s=[];function l(e,t){var n=new Sr({root:function(e,t){return i.add(e,t)},element:function(e,t,n){return i.add(e,t,n)},error:function(e,t){s.push({message:e,context:t})}},o);t=t||e.diagrams&&e.diagrams[0];var r=function(e,t){if(!t)return;var n,i=t.plane.bpmnElement,r=i;A(i,"bpmn:Process")||A(i,"bpmn:Collaboration")||(r=function(e){var t=e;for(;t;){if(A(t,"bpmn:Process"))return t;t=t.$parent}} +/** + * This file must not be changed or exchanged. + * + * @see http://bpmn.io/license for more information. + */(i));n=A(r,"bpmn:Collaboration")?r:c(e.rootElements,(function(e){if(A(e,"bpmn:Collaboration"))return c(e.participants,(function(e){return e.processRef===r}))}));var o=[r];n&&(o=function(e,t){var n=[];return h(e,(function(e,i){n.push(t(e,i))})),n}(n.participants,(function(e){return e.processRef})),o.push(n));var a=Cr(o),s=[t],l=[i];return h(e.diagrams,(function(e){var t=e.plane.bpmnElement;-1!==a.indexOf(t)&&-1===l.indexOf(t)&&(s.push(e),l.push(t))})),s}(e,t);if(!r)throw new Error(o("no diagram to display"));h(r,(function(t){n.handleDefinitions(e,t)}));var l=t.plane.bpmnElement.id;a.setRootElement(a.findRoot(l+"_plane")||a.findRoot(l))}return new Promise((function(p,c){try{return i=e.get("bpmnImporter"),r=e.get("eventBus"),o=e.get("translate"),a=e.get("canvas"),r.fire("import.render.start",{definitions:t}),l(t,n),r.fire("import.render.complete",{error:undefined,warnings:s}),p({warnings:s})}catch(e){return e.warnings=s,c(e)}}))}function Cr(e){var t=[];return h(e,(function(e){e&&(t.push(e),t=t.concat(Cr(e.flowElements)))})),t}var Mr,kr='',Pr={verticalAlign:"middle"},Tr={color:"#404040"},Dr={zIndex:"1001",position:"fixed",top:"0",left:"0",right:"0",bottom:"0"},Nr={width:"100%",height:"100%",background:"rgba(40,40,40,0.2)"},Or={position:"absolute",left:"50%",top:"40%",transform:"translate(-50%)",width:"260px",padding:"10px",background:"white",boxShadow:"0 1px 4px rgba(0,0,0,0.3)",fontFamily:"Helvetica, Arial, sans-serif",fontSize:"14px",display:"flex",lineHeight:"1.3"};function Br(){Mr||(be(Mr=Fe('
              Web-based tooling for BPMN, DMN and CMMN diagrams powered by bpmn.io.
              '),Dr),be(We("svg",Mr),Pr),be(We(".backdrop",Mr),Nr),be(We(".notice",Mr),Or),be(We(".link",Mr),Tr,{margin:"15px 20px 15px 10px",alignSelf:"center"}),je.bind(Mr,".backdrop","click",(function(e){document.body.removeChild(Mr)}))),document.body.appendChild(Mr)} +/** + * The code in the area + * must not be changed. + * + * @see http://bpmn.io/license for more information. + */function Lr(e){ +/** + * Adds the project logo to the diagram container as + * required by the bpmn.io license. + * + * @see http://bpmn.io/license + * + * @param {Element} container + */ +var t,n;e=E({},jr,e),this._moddle=this._createModdle(e),this._container=this._createContainer(e),t=this._container,be(We("svg",n=Fe(''+kr+"")),Pr),be(n,Tr,{position:"absolute",bottom:"15px",right:"15px",zIndex:"100"}),t.appendChild(n),Oe(n,"click",(function(e){Br(),e.preventDefault()})),this._init(this._container,this._moddle,e)}function Ir(e,t){return e.warnings=t,e}e(Lr,si),Lr.prototype.importXML=_r((function(e,t){var n=this;return new Promise((function(i,r){e=n._emit("import.parse.start",{xml:e})||e,n._moddle.fromXML(e,"bpmn:Definitions").then((function(e){var o,a,s=e.rootElement,l=e.references,p=e.warnings,c=e.elementsById;s=n._emit("import.parse.complete",(o={error:null,definitions:s,elementsById:c,references:l,warnings:p},a=n.get("eventBus").createEvent(o),Object.defineProperty(a,"context",{enumerable:!0,get:function(){return console.warn(new Error("import.parse.complete is deprecated and will be removed in future library versions")),{warnings:o.warnings,references:o.references,elementsById:o.elementsById}}}),a))||s,n.importDefinitions(s,t).then((function(e){var t=[].concat(p,e.warnings||[]);return n._emit("import.done",{error:null,warnings:t}),i({warnings:t})})).catch((function(e){var t=[].concat(p,e.warnings||[]);return n._emit("import.done",{error:e,warnings:t}),r(Ir(e,t))}))})).catch((function(e){return n._emit("import.parse.complete",{error:e}),e=function(e){var t=/unparsable content <([^>]+)> detected([\s\S]*)$/.exec(e.message);t&&(e.message="unparsable content <"+t[1]+"> detected; this may indicate an invalid BPMN 2.0 diagram file"+t[2]);return e}(e),n._emit("import.done",{error:e,warnings:e.warnings}),r(e)}))}))})),Lr.prototype.importDefinitions=_r((function(e,t){var n=this;return new Promise((function(i,r){n._setDefinitions(e),n.open(t).then((function(e){var t=e.warnings;return i({warnings:t})})).catch((function(e){return r(e)}))}))})),Lr.prototype.open=_r((function(e){var t=this._definitions,n=e,i=this;return new Promise((function(r,o){if(!t){var a=new Error("no XML imported");return o(Ir(a,[]))}if("string"==typeof e&&(n=function(e,t){if(!t)return null;return c(e.diagrams,(function(e){return e.id===t}))||null}(t,e),!n)){var s=new Error("BPMNDiagram <"+e+"> not found");return o(Ir(s,[]))}try{i.clear()}catch(e){return o(Ir(e,[]))}Rr(i,t,n).then((function(e){var t=e.warnings;return r({warnings:t})})).catch((function(e){return o(e)}))}))})),Lr.prototype.saveXML=_r((function(e){e=e||{};var t=this,n=this._definitions;return new Promise((function(i){if(!n)return i({error:new Error("no definitions loaded")});n=t._emit("saveXML.start",{definitions:n})||n,t._moddle.toXML(n,e).then((function(e){var n=e.xml;return n=t._emit("saveXML.serialized",{xml:n})||n,i({xml:n})}))})).catch((function(e){return{error:e}})).then((function(e){t._emit("saveXML.done",e);var n=e.error;return n?Promise.reject(n):e}))})),Lr.prototype.saveSVG=_r((function(e){var t=this;return new Promise((function(e,n){var i,r;t._emit("saveSVG.start");try{var o=t.get("canvas"),a=o.getActiveLayer(),s=We("defs",o._svg),l=ne(a),p=s?""+ne(s)+"":"",c=a.getBBox();i='\n\x3c!-- created with bpmn-js / http://bpmn.io --\x3e\n\n'+p+l+""}catch(e){r=e}return t._emit("saveSVG.done",{error:r,svg:i}),r?n(r):e({svg:i})}))})),Lr.prototype._setDefinitions=function(e){this._definitions=e},Lr.prototype.getModules=function(){return this._modules},Lr.prototype.clear=function(){this.getDefinitions()&&si.prototype.clear.call(this)},Lr.prototype.destroy=function(){si.prototype.destroy.call(this),Ge(this._container)},Lr.prototype.on=function(e,t,n,i){return this.get("eventBus").on(e,t,n,i)},Lr.prototype.off=function(e,t){this.get("eventBus").off(e,t)},Lr.prototype.attachTo=function(e){if(!e)throw new Error("parentNode required");this.detach(),e.get&&e.constructor.prototype.jquery&&(e=e.get(0)),"string"==typeof e&&(e=We(e)),e.appendChild(this._container),this._emit("attach",{}),this.get("canvas").resized()},Lr.prototype.getDefinitions=function(){return this._definitions},Lr.prototype.detach=function(){var e=this._container,t=e.parentNode;t&&(this._emit("detach",{}),t.removeChild(e))},Lr.prototype._init=function(e,t,n){var i,r,o=n.modules||this.getModules(),a=n.additionalModules||[],s=[].concat([{bpmnjs:["value",this],moddle:["value",t]}],o,a),l=E((i=["additionalModules"],r={},h(Object(n),(function(e,t){-1===i.indexOf(t)&&(r[t]=e)})),r),{canvas:E({},n.canvas,{container:e}),modules:s});si.call(this,l),n&&n.container&&this.attachTo(n.container)},Lr.prototype._emit=function(e,t){return this.get("eventBus").fire(e,t)},Lr.prototype._createContainer=function(e){var t=Fe('
              ');return be(t,{width:Fr(e.width),height:Fr(e.height),position:e.position}),t},Lr.prototype._createModdle=function(e){return new Er(E({},this._moddleExtensions,e.moddleExtensions))},Lr.prototype._modules=[];var jr={width:"100%",height:"100%",position:"relative"};function Fr(e){return e+(a(e)?"px":"")}function Vr(e){Lr.call(this,e)}return e(Vr,Lr),Vr.prototype._modules=[Et,st,Wt,Yt,fn],Vr.prototype._moddleExtensions={},Vr})); diff --git a/index.js b/index.js new file mode 100644 index 0000000..180f579 --- /dev/null +++ b/index.js @@ -0,0 +1,3 @@ +export { + default +} from './lib/Viewer'; \ No newline at end of file diff --git a/lib/BaseModeler.js b/lib/BaseModeler.js new file mode 100644 index 0000000..474c00e --- /dev/null +++ b/lib/BaseModeler.js @@ -0,0 +1,74 @@ +import inherits from 'inherits-browser'; + +import Ids from 'ids'; + +import BaseViewer from './BaseViewer'; + + +/** + * A base modeler for BPMN 2.0 diagrams. + * + * Have a look at {@link Modeler} for a bundle that includes actual features. + * + * @param {Object} [options] configuration options to pass to the viewer + * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body. + * @param {string|number} [options.width] the width of the viewer + * @param {string|number} [options.height] the height of the viewer + * @param {Object} [options.moddleExtensions] extension packages to provide + * @param {Array} [options.modules] a list of modules to override the default modules + * @param {Array} [options.additionalModules] a list of modules to use with the default modules + */ +export default function BaseModeler(options) { + BaseViewer.call(this, options); + + // hook ID collection into the modeler + this.on('import.parse.complete', function(event) { + if (!event.error) { + this._collectIds(event.definitions, event.elementsById); + } + }, this); + + this.on('diagram.destroy', function() { + this.get('moddle').ids.clear(); + }, this); +} + +inherits(BaseModeler, BaseViewer); + + +/** + * Create a moddle instance, attaching ids to it. + * + * @param {Object} options + */ +BaseModeler.prototype._createModdle = function(options) { + var moddle = BaseViewer.prototype._createModdle.call(this, options); + + // attach ids to moddle to be able to track + // and validated ids in the BPMN 2.0 XML document + // tree + moddle.ids = new Ids([ 32, 36, 1 ]); + + return moddle; +}; + +/** + * Collect ids processed during parsing of the + * definitions object. + * + * @param {ModdleElement} definitions + * @param {Context} context + */ +BaseModeler.prototype._collectIds = function(definitions, elementsById) { + + var moddle = definitions.$model, + ids = moddle.ids, + id; + + // remove references from previous import + ids.clear(); + + for (id in elementsById) { + ids.claim(id, elementsById[id]); + } +}; diff --git a/lib/BaseViewer.js b/lib/BaseViewer.js new file mode 100644 index 0000000..c34d927 --- /dev/null +++ b/lib/BaseViewer.js @@ -0,0 +1,786 @@ +/** + * The code in the area + * must not be changed. + * + * @see http://bpmn.io/license for more information. + */ +import { + assign, + find, + isNumber, + omit +} from 'min-dash'; + +import { + domify, + assignStyle, + query as domQuery, + remove as domRemove +} from 'min-dom'; + +import { + innerSVG +} from 'tiny-svg'; + +import Diagram from 'diagram-js'; +import BpmnModdle from 'bpmn-moddle'; + +import inherits from 'inherits-browser'; + +import { + importBpmnDiagram +} from './import/Importer'; + +import { + wrapForCompatibility +} from './util/CompatibilityUtil'; + +/** + * A base viewer for BPMN 2.0 diagrams. + * + * Have a look at {@link Viewer}, {@link NavigatedViewer} or {@link Modeler} for + * bundles that include actual features. + * + * @param {Object} [options] configuration options to pass to the viewer + * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body. + * @param {string|number} [options.width] the width of the viewer + * @param {string|number} [options.height] the height of the viewer + * @param {Object} [options.moddleExtensions] extension packages to provide + * @param {Array} [options.modules] a list of modules to override the default modules + * @param {Array} [options.additionalModules] a list of modules to use with the default modules + */ +export default function BaseViewer(options) { + + options = assign({}, DEFAULT_OPTIONS, options); + + this._moddle = this._createModdle(options); + + this._container = this._createContainer(options); + + /* */ + + addProjectLogo(this._container); + + /* */ + + this._init(this._container, this._moddle, options); +} + +inherits(BaseViewer, Diagram); + +/** +* The importXML result. +* +* @typedef {Object} ImportXMLResult +* +* @property {Array} warnings +*/ + +/** +* The importXML error. +* +* @typedef {Error} ImportXMLError +* +* @property {Array} warnings +*/ + +/** + * Parse and render a BPMN 2.0 diagram. + * + * Once finished the viewer reports back the result to the + * provided callback function with (err, warnings). + * + * ## Life-Cycle Events + * + * During import the viewer will fire life-cycle events: + * + * * import.parse.start (about to read model from xml) + * * import.parse.complete (model read; may have worked or not) + * * import.render.start (graphical import start) + * * import.render.complete (graphical import finished) + * * import.done (everything done) + * + * You can use these events to hook into the life-cycle. + * + * @param {string} xml the BPMN 2.0 xml + * @param {ModdleElement|string} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered) + * + * Returns {Promise} + */ +BaseViewer.prototype.importXML = wrapForCompatibility(function importXML(xml, bpmnDiagram) { + + var self = this; + + function ParseCompleteEvent(data) { + + var event = self.get('eventBus').createEvent(data); + + // TODO(nikku): remove with future bpmn-js version + Object.defineProperty(event, 'context', { + enumerable: true, + get: function() { + + console.warn(new Error( + 'import.parse.complete is deprecated ' + + 'and will be removed in future library versions' + )); + + return { + warnings: data.warnings, + references: data.references, + elementsById: data.elementsById + }; + } + }); + + return event; + } + + return new Promise(function(resolve, reject) { + + // hook in pre-parse listeners + + // allow xml manipulation + xml = self._emit('import.parse.start', { xml: xml }) || xml; + + self._moddle.fromXML(xml, 'bpmn:Definitions').then(function(result) { + var definitions = result.rootElement; + var references = result.references; + var parseWarnings = result.warnings; + var elementsById = result.elementsById; + + // hook in post parse listeners + + // allow definitions manipulation + definitions = self._emit('import.parse.complete', ParseCompleteEvent({ + error: null, + definitions: definitions, + elementsById: elementsById, + references: references, + warnings: parseWarnings + })) || definitions; + + self.importDefinitions(definitions, bpmnDiagram).then(function(result) { + var allWarnings = [].concat(parseWarnings, result.warnings || []); + + self._emit('import.done', { error: null, warnings: allWarnings }); + + return resolve({ warnings: allWarnings }); + }).catch(function(err) { + var allWarnings = [].concat(parseWarnings, err.warnings || []); + + self._emit('import.done', { error: err, warnings: allWarnings }); + + return reject(addWarningsToError(err, allWarnings)); + }); + }).catch(function(err) { + + self._emit('import.parse.complete', { + error: err + }); + + err = checkValidationError(err); + + self._emit('import.done', { error: err, warnings: err.warnings }); + + return reject(err); + }); + }); +}); + +/** +* The importDefinitions result. +* +* @typedef {Object} ImportDefinitionsResult +* +* @property {Array} warnings +*/ + +/** +* The importDefinitions error. +* +* @typedef {Error} ImportDefinitionsError +* +* @property {Array} warnings +*/ + +/** + * Import parsed definitions and render a BPMN 2.0 diagram. + * + * Once finished the viewer reports back the result to the + * provided callback function with (err, warnings). + * + * ## Life-Cycle Events + * + * During import the viewer will fire life-cycle events: + * + * * import.render.start (graphical import start) + * * import.render.complete (graphical import finished) + * + * You can use these events to hook into the life-cycle. + * + * @param {ModdleElement} definitions parsed BPMN 2.0 definitions + * @param {ModdleElement|string} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered) + * + * Returns {Promise} + */ +BaseViewer.prototype.importDefinitions = wrapForCompatibility(function importDefinitions(definitions, bpmnDiagram) { + + var self = this; + + return new Promise(function(resolve, reject) { + + self._setDefinitions(definitions); + + self.open(bpmnDiagram).then(function(result) { + + var warnings = result.warnings; + + return resolve({ warnings: warnings }); + }).catch(function(err) { + + return reject(err); + }); + }); +}); + +/** + * The open result. + * + * @typedef {Object} OpenResult + * + * @property {Array} warnings + */ + +/** +* The open error. +* +* @typedef {Error} OpenError +* +* @property {Array} warnings +*/ + +/** + * Open diagram of previously imported XML. + * + * Once finished the viewer reports back the result to the + * provided callback function with (err, warnings). + * + * ## Life-Cycle Events + * + * During switch the viewer will fire life-cycle events: + * + * * import.render.start (graphical import start) + * * import.render.complete (graphical import finished) + * + * You can use these events to hook into the life-cycle. + * + * @param {string|ModdleElement} [bpmnDiagramOrId] id or the diagram to open + * + * Returns {Promise} + */ +BaseViewer.prototype.open = wrapForCompatibility(function open(bpmnDiagramOrId) { + + var definitions = this._definitions; + var bpmnDiagram = bpmnDiagramOrId; + + var self = this; + + return new Promise(function(resolve, reject) { + if (!definitions) { + var err1 = new Error('no XML imported'); + + return reject(addWarningsToError(err1, [])); + } + + if (typeof bpmnDiagramOrId === 'string') { + bpmnDiagram = findBPMNDiagram(definitions, bpmnDiagramOrId); + + if (!bpmnDiagram) { + var err2 = new Error('BPMNDiagram <' + bpmnDiagramOrId + '> not found'); + + return reject(addWarningsToError(err2, [])); + } + } + + // clear existing rendered diagram + // catch synchronous exceptions during #clear() + try { + self.clear(); + } catch (error) { + + return reject(addWarningsToError(error, [])); + } + + // perform graphical import + importBpmnDiagram(self, definitions, bpmnDiagram).then(function(result) { + + var warnings = result.warnings; + + return resolve({ warnings: warnings }); + }).catch(function(err) { + + return reject(err); + }); + }); +}); + +/** + * The saveXML result. + * + * @typedef {Object} SaveXMLResult + * + * @property {string} xml + */ + +/** + * Export the currently displayed BPMN 2.0 diagram as + * a BPMN 2.0 XML document. + * + * ## Life-Cycle Events + * + * During XML saving the viewer will fire life-cycle events: + * + * * saveXML.start (before serialization) + * * saveXML.serialized (after xml generation) + * * saveXML.done (everything done) + * + * You can use these events to hook into the life-cycle. + * + * @param {Object} [options] export options + * @param {boolean} [options.format=false] output formatted XML + * @param {boolean} [options.preamble=true] output preamble + * + * Returns {Promise} + */ +BaseViewer.prototype.saveXML = wrapForCompatibility(function saveXML(options) { + + options = options || {}; + + var self = this; + + var definitions = this._definitions; + + return new Promise(function(resolve) { + + if (!definitions) { + return resolve({ + error: new Error('no definitions loaded') + }); + } + + // allow to fiddle around with definitions + definitions = self._emit('saveXML.start', { + definitions: definitions + }) || definitions; + + self._moddle.toXML(definitions, options).then(function(result) { + + var xml = result.xml; + + xml = self._emit('saveXML.serialized', { + xml: xml + }) || xml; + + return resolve({ + xml: xml + }); + }); + }).catch(function(error) { + return { error: error }; + }).then(function(result) { + + self._emit('saveXML.done', result); + + var error = result.error; + + if (error) { + return Promise.reject(error); + } + + return result; + }); +}); + +/** + * The saveSVG result. + * + * @typedef {Object} SaveSVGResult + * + * @property {string} svg + */ + +/** + * Export the currently displayed BPMN 2.0 diagram as + * an SVG image. + * + * ## Life-Cycle Events + * + * During SVG saving the viewer will fire life-cycle events: + * + * * saveSVG.start (before serialization) + * * saveSVG.done (everything done) + * + * You can use these events to hook into the life-cycle. + * + * @param {Object} [options] + * + * Returns {Promise} + */ +BaseViewer.prototype.saveSVG = wrapForCompatibility(function saveSVG(options) { + + options = options || {}; + + var self = this; + + return new Promise(function(resolve, reject) { + + self._emit('saveSVG.start'); + + var svg, err; + + try { + var canvas = self.get('canvas'); + + var contentNode = canvas.getActiveLayer(), + defsNode = domQuery('defs', canvas._svg); + + var contents = innerSVG(contentNode), + defs = defsNode ? '' + innerSVG(defsNode) + '' : ''; + + var bbox = contentNode.getBBox(); + + svg = + '\n' + + '\n' + + '\n' + + '' + + defs + contents + + ''; + } catch (e) { + err = e; + } + + self._emit('saveSVG.done', { + error: err, + svg: svg + }); + + if (!err) { + return resolve({ svg: svg }); + } + + return reject(err); + }); +}); + +/** + * Get a named diagram service. + * + * @example + * + * var elementRegistry = viewer.get('elementRegistry'); + * var startEventShape = elementRegistry.get('StartEvent_1'); + * + * @param {string} name + * + * @return {Object} diagram service instance + * + * @method BaseViewer#get + */ + +/** + * Invoke a function in the context of this viewer. + * + * @example + * + * viewer.invoke(function(elementRegistry) { + * var startEventShape = elementRegistry.get('StartEvent_1'); + * }); + * + * @param {Function} fn to be invoked + * + * @return {Object} the functions return value + * + * @method BaseViewer#invoke + */ + + +BaseViewer.prototype._setDefinitions = function(definitions) { + this._definitions = definitions; +}; + +BaseViewer.prototype.getModules = function() { + return this._modules; +}; + +/** + * Remove all drawn elements from the viewer. + * + * After calling this method the viewer can still + * be reused for opening another diagram. + * + * @method BaseViewer#clear + */ +BaseViewer.prototype.clear = function() { + if (!this.getDefinitions()) { + + // no diagram to clear + return; + } + + // remove drawn elements + Diagram.prototype.clear.call(this); +}; + +/** + * Destroy the viewer instance and remove all its + * remainders from the document tree. + */ +BaseViewer.prototype.destroy = function() { + + // diagram destroy + Diagram.prototype.destroy.call(this); + + // dom detach + domRemove(this._container); +}; + +/** + * Register an event listener + * + * Remove a previously added listener via {@link #off(event, callback)}. + * + * @param {string} event + * @param {number} [priority] + * @param {Function} callback + * @param {Object} [that] + */ +BaseViewer.prototype.on = function(event, priority, callback, target) { + return this.get('eventBus').on(event, priority, callback, target); +}; + +/** + * De-register an event listener + * + * @param {string} event + * @param {Function} callback + */ +BaseViewer.prototype.off = function(event, callback) { + this.get('eventBus').off(event, callback); +}; + +BaseViewer.prototype.attachTo = function(parentNode) { + + if (!parentNode) { + throw new Error('parentNode required'); + } + + // ensure we detach from the + // previous, old parent + this.detach(); + + // unwrap jQuery if provided + if (parentNode.get && parentNode.constructor.prototype.jquery) { + parentNode = parentNode.get(0); + } + + if (typeof parentNode === 'string') { + parentNode = domQuery(parentNode); + } + + parentNode.appendChild(this._container); + + this._emit('attach', {}); + + this.get('canvas').resized(); +}; + +BaseViewer.prototype.getDefinitions = function() { + return this._definitions; +}; + +BaseViewer.prototype.detach = function() { + + var container = this._container, + parentNode = container.parentNode; + + if (!parentNode) { + return; + } + + this._emit('detach', {}); + + parentNode.removeChild(container); +}; + +BaseViewer.prototype._init = function(container, moddle, options) { + + var baseModules = options.modules || this.getModules(), + additionalModules = options.additionalModules || [], + staticModules = [ + { + bpmnjs: [ 'value', this ], + moddle: [ 'value', moddle ] + } + ]; + + var diagramModules = [].concat(staticModules, baseModules, additionalModules); + + var diagramOptions = assign(omit(options, [ 'additionalModules' ]), { + canvas: assign({}, options.canvas, { container: container }), + modules: diagramModules + }); + + // invoke diagram constructor + Diagram.call(this, diagramOptions); + + if (options && options.container) { + this.attachTo(options.container); + } +}; + +/** + * Emit an event on the underlying {@link EventBus} + * + * @param {string} type + * @param {Object} event + * + * @return {Object} event processing result (if any) + */ +BaseViewer.prototype._emit = function(type, event) { + return this.get('eventBus').fire(type, event); +}; + +BaseViewer.prototype._createContainer = function(options) { + + var container = domify('
              '); + + assignStyle(container, { + width: ensureUnit(options.width), + height: ensureUnit(options.height), + position: options.position + }); + + return container; +}; + +BaseViewer.prototype._createModdle = function(options) { + var moddleOptions = assign({}, this._moddleExtensions, options.moddleExtensions); + + return new BpmnModdle(moddleOptions); +}; + +BaseViewer.prototype._modules = []; + +// helpers /////////////// + +function addWarningsToError(err, warningsAry) { + err.warnings = warningsAry; + return err; +} + +function checkValidationError(err) { + + // check if we can help the user by indicating wrong BPMN 2.0 xml + // (in case he or the exporting tool did not get that right) + + var pattern = /unparsable content <([^>]+)> detected([\s\S]*)$/; + var match = pattern.exec(err.message); + + if (match) { + err.message = + 'unparsable content <' + match[1] + '> detected; ' + + 'this may indicate an invalid BPMN 2.0 diagram file' + match[2]; + } + + return err; +} + +var DEFAULT_OPTIONS = { + width: '100%', + height: '100%', + position: 'relative' +}; + + +/** + * Ensure the passed argument is a proper unit (defaulting to px) + */ +function ensureUnit(val) { + return val + (isNumber(val) ? 'px' : ''); +} + + +/** + * Find BPMNDiagram in definitions by ID + * + * @param {ModdleElement} definitions + * @param {string} diagramId + * + * @return {ModdleElement|null} + */ +function findBPMNDiagram(definitions, diagramId) { + if (!diagramId) { + return null; + } + + return find(definitions.diagrams, function(element) { + return element.id === diagramId; + }) || null; +} + + +/* */ + +import { + open as openPoweredBy, + BPMNIO_IMG, + LOGO_STYLES, + LINK_STYLES +} from './util/PoweredByUtil'; + +import { + event as domEvent +} from 'min-dom'; + +/** + * Adds the project logo to the diagram container as + * required by the bpmn.io license. + * + * @see http://bpmn.io/license + * + * @param {Element} container + */ +function addProjectLogo(container) { + var img = BPMNIO_IMG; + + var linkMarkup = + '' + + img + + ''; + + var linkElement = domify(linkMarkup); + + assignStyle(domQuery('svg', linkElement), LOGO_STYLES); + assignStyle(linkElement, LINK_STYLES, { + position: 'absolute', + bottom: '15px', + right: '15px', + zIndex: '100' + }); + + container.appendChild(linkElement); + + domEvent.bind(linkElement, 'click', function(event) { + openPoweredBy(); + + event.preventDefault(); + }); +} + +/* */ diff --git a/lib/Modeler.js b/lib/Modeler.js new file mode 100644 index 0000000..7aedb9d --- /dev/null +++ b/lib/Modeler.js @@ -0,0 +1,220 @@ +import inherits from 'inherits-browser'; + +import BaseModeler from './BaseModeler'; + +import Viewer from './Viewer'; +import NavigatedViewer from './NavigatedViewer'; + +import KeyboardMoveModule from 'diagram-js/lib/navigation/keyboard-move'; +import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas'; +import TouchModule from 'diagram-js/lib/navigation/touch'; +import ZoomScrollModule from 'diagram-js/lib/navigation/zoomscroll'; + +import AlignElementsModule from './features/align-elements'; +import AutoPlaceModule from './features/auto-place'; +import AutoResizeModule from './features/auto-resize'; +import AutoScrollModule from 'diagram-js/lib/features/auto-scroll'; +import BendpointsModule from 'diagram-js/lib/features/bendpoints'; +import ConnectModule from 'diagram-js/lib/features/connect'; +import ConnectionPreviewModule from 'diagram-js/lib/features/connection-preview'; +import ContextPadModule from './features/context-pad'; +import CopyPasteModule from './features/copy-paste'; +import CreateModule from 'diagram-js/lib/features/create'; +import DistributeElementsModule from './features/distribute-elements'; +import EditorActionsModule from './features/editor-actions'; +import GridSnappingModule from './features/grid-snapping'; +import InteractionEventsModule from './features/interaction-events'; +import KeyboardModule from './features/keyboard'; +import KeyboardMoveSelectionModule from 'diagram-js/lib/features/keyboard-move-selection'; +import LabelEditingModule from './features/label-editing'; +import ModelingModule from './features/modeling'; +import MoveModule from 'diagram-js/lib/features/move'; +import PaletteModule from './features/palette'; +import ReplacePreviewModule from './features/replace-preview'; +import ResizeModule from 'diagram-js/lib/features/resize'; +import SnappingModule from './features/snapping'; +import SearchModule from './features/search'; + +import { + wrapForCompatibility +} from './util/CompatibilityUtil'; + +var initialDiagram = + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + ''; + + +/** + * A modeler for BPMN 2.0 diagrams. + * + * + * ## Extending the Modeler + * + * In order to extend the viewer pass extension modules to bootstrap via the + * `additionalModules` option. An extension module is an object that exposes + * named services. + * + * The following example depicts the integration of a simple + * logging component that integrates with interaction events: + * + * + * ```javascript + * + * // logging component + * function InteractionLogger(eventBus) { + * eventBus.on('element.hover', function(event) { + * console.log() + * }) + * } + * + * InteractionLogger.$inject = [ 'eventBus' ]; // minification save + * + * // extension module + * var extensionModule = { + * __init__: [ 'interactionLogger' ], + * interactionLogger: [ 'type', InteractionLogger ] + * }; + * + * // extend the viewer + * var bpmnModeler = new Modeler({ additionalModules: [ extensionModule ] }); + * bpmnModeler.importXML(...); + * ``` + * + * + * ## Customizing / Replacing Components + * + * You can replace individual diagram components by redefining them in override modules. + * This works for all components, including those defined in the core. + * + * Pass in override modules via the `options.additionalModules` flag like this: + * + * ```javascript + * function CustomContextPadProvider(contextPad) { + * + * contextPad.registerProvider(this); + * + * this.getContextPadEntries = function(element) { + * // no entries, effectively disable the context pad + * return {}; + * }; + * } + * + * CustomContextPadProvider.$inject = [ 'contextPad' ]; + * + * var overrideModule = { + * contextPadProvider: [ 'type', CustomContextPadProvider ] + * }; + * + * var bpmnModeler = new Modeler({ additionalModules: [ overrideModule ]}); + * ``` + * + * @param {Object} [options] configuration options to pass to the viewer + * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body. + * @param {string|number} [options.width] the width of the viewer + * @param {string|number} [options.height] the height of the viewer + * @param {Object} [options.moddleExtensions] extension packages to provide + * @param {Array} [options.modules] a list of modules to override the default modules + * @param {Array} [options.additionalModules] a list of modules to use with the default modules + */ +export default function Modeler(options) { + BaseModeler.call(this, options); +} + +inherits(Modeler, BaseModeler); + + +Modeler.Viewer = Viewer; +Modeler.NavigatedViewer = NavigatedViewer; + +/** +* The createDiagram result. +* +* @typedef {Object} CreateDiagramResult +* +* @property {Array} warnings +*/ + +/** +* The createDiagram error. +* +* @typedef {Error} CreateDiagramError +* +* @property {Array} warnings +*/ + +/** + * Create a new diagram to start modeling. + * + * Returns {Promise} + */ +Modeler.prototype.createDiagram = wrapForCompatibility(function createDiagram() { + return this.importXML(initialDiagram); +}); + + +Modeler.prototype._interactionModules = [ + + // non-modeling components + KeyboardMoveModule, + MoveCanvasModule, + TouchModule, + ZoomScrollModule +]; + +Modeler.prototype._modelingModules = [ + + // modeling components + AlignElementsModule, + AutoPlaceModule, + AutoScrollModule, + AutoResizeModule, + BendpointsModule, + ConnectModule, + ConnectionPreviewModule, + ContextPadModule, + CopyPasteModule, + CreateModule, + DistributeElementsModule, + EditorActionsModule, + GridSnappingModule, + InteractionEventsModule, + KeyboardModule, + KeyboardMoveSelectionModule, + LabelEditingModule, + ModelingModule, + MoveModule, + PaletteModule, + ReplacePreviewModule, + ResizeModule, + SnappingModule, + SearchModule +]; + + +// modules the modeler is composed of +// +// - viewer modules +// - interaction modules +// - modeling modules + +Modeler.prototype._modules = [].concat( + Viewer.prototype._modules, + Modeler.prototype._interactionModules, + Modeler.prototype._modelingModules +); diff --git a/lib/NavigatedViewer.js b/lib/NavigatedViewer.js new file mode 100644 index 0000000..7da2ded --- /dev/null +++ b/lib/NavigatedViewer.js @@ -0,0 +1,31 @@ +import inherits from 'inherits-browser'; + +import Viewer from './Viewer'; + +import KeyboardMoveModule from 'diagram-js/lib/navigation/keyboard-move'; +import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas'; +import ZoomScrollModule from 'diagram-js/lib/navigation/zoomscroll'; + + +/** + * A viewer that includes mouse navigation facilities + * + * @param {Object} options + */ +export default function NavigatedViewer(options) { + Viewer.call(this, options); +} + +inherits(NavigatedViewer, Viewer); + + +NavigatedViewer.prototype._navigationModules = [ + KeyboardMoveModule, + MoveCanvasModule, + ZoomScrollModule +]; + +NavigatedViewer.prototype._modules = [].concat( + Viewer.prototype._modules, + NavigatedViewer.prototype._navigationModules +); \ No newline at end of file diff --git a/lib/Viewer.js b/lib/Viewer.js new file mode 100644 index 0000000..ac4a1db --- /dev/null +++ b/lib/Viewer.js @@ -0,0 +1,75 @@ +import inherits from 'inherits-browser'; + +import CoreModule from './core'; +import TranslateModule from 'diagram-js/lib/i18n/translate'; +import SelectionModule from 'diagram-js/lib/features/selection'; +import OverlaysModule from 'diagram-js/lib/features/overlays'; +import DrilldownModdule from './features/drilldown'; + +import BaseViewer from './BaseViewer'; + + +/** + * A viewer for BPMN 2.0 diagrams. + * + * Have a look at {@link NavigatedViewer} or {@link Modeler} for bundles that include + * additional features. + * + * + * ## Extending the Viewer + * + * In order to extend the viewer pass extension modules to bootstrap via the + * `additionalModules` option. An extension module is an object that exposes + * named services. + * + * The following example depicts the integration of a simple + * logging component that integrates with interaction events: + * + * + * ```javascript + * + * // logging component + * function InteractionLogger(eventBus) { + * eventBus.on('element.hover', function(event) { + * console.log() + * }) + * } + * + * InteractionLogger.$inject = [ 'eventBus' ]; // minification save + * + * // extension module + * var extensionModule = { + * __init__: [ 'interactionLogger' ], + * interactionLogger: [ 'type', InteractionLogger ] + * }; + * + * // extend the viewer + * var bpmnViewer = new Viewer({ additionalModules: [ extensionModule ] }); + * bpmnViewer.importXML(...); + * ``` + * + * @param {Object} [options] configuration options to pass to the viewer + * @param {DOMElement} [options.container] the container to render the viewer in, defaults to body. + * @param {string|number} [options.width] the width of the viewer + * @param {string|number} [options.height] the height of the viewer + * @param {Object} [options.moddleExtensions] extension packages to provide + * @param {Array} [options.modules] a list of modules to override the default modules + * @param {Array} [options.additionalModules] a list of modules to use with the default modules + */ +export default function Viewer(options) { + BaseViewer.call(this, options); +} + +inherits(Viewer, BaseViewer); + +// modules the viewer is composed of +Viewer.prototype._modules = [ + CoreModule, + TranslateModule, + SelectionModule, + OverlaysModule, + DrilldownModdule +]; + +// default moddle extensions the viewer is composed of +Viewer.prototype._moddleExtensions = {}; \ No newline at end of file diff --git a/lib/core/index.js b/lib/core/index.js new file mode 100644 index 0000000..a90e647 --- /dev/null +++ b/lib/core/index.js @@ -0,0 +1,9 @@ +import DrawModule from '../draw'; +import ImportModule from '../import'; + +export default { + __depends__: [ + DrawModule, + ImportModule + ] +}; \ No newline at end of file diff --git a/lib/draw/BpmnRenderUtil.js b/lib/draw/BpmnRenderUtil.js new file mode 100644 index 0000000..bd20ad4 --- /dev/null +++ b/lib/draw/BpmnRenderUtil.js @@ -0,0 +1,157 @@ +import { + every, + some +} from 'min-dash'; + +import { + getDi +} from '../util/ModelUtil'; + +import { + componentsToPath +} from 'diagram-js/lib/util/RenderUtil'; + +// re-export getDi for compatibility +export { getDi }; + +export var black = 'hsl(225, 10%, 15%)'; + +// element utils ////////////////////// + +/** + * Checks if eventDefinition of the given element matches with semantic type. + * + * @return {boolean} true if element is of the given semantic type + */ +export function isTypedEvent(event, eventDefinitionType, filter) { + + function matches(definition, filter) { + return every(filter, function(val, key) { + + // we want a == conversion here, to be able to catch + // undefined == false and friends + /* jshint -W116 */ + return definition[key] == val; + }); + } + + return some(event.eventDefinitions, function(definition) { + return definition.$type === eventDefinitionType && matches(event, filter); + }); +} + +export function isThrowEvent(event) { + return (event.$type === 'bpmn:IntermediateThrowEvent') || (event.$type === 'bpmn:EndEvent'); +} + +export function isCollection(element) { + var dataObject = element.dataObjectRef; + + return element.isCollection || (dataObject && dataObject.isCollection); +} + +export function getSemantic(element) { + return element.businessObject; +} + + +// color access ////////////////////// + +export function getFillColor(element, defaultColor) { + var di = getDi(element); + + return di.get('color:background-color') || di.get('bioc:fill') || defaultColor || 'white'; +} + +export function getStrokeColor(element, defaultColor) { + var di = getDi(element); + + return di.get('color:border-color') || di.get('bioc:stroke') || defaultColor || black; +} + +export function getLabelColor(element, defaultColor, defaultStrokeColor) { + var di = getDi(element), + label = di.get('label'); + + return label && label.get('color:color') || defaultColor || + getStrokeColor(element, defaultStrokeColor); +} + +// cropping path customizations ////////////////////// + +export function getCirclePath(shape) { + + var cx = shape.x + shape.width / 2, + cy = shape.y + shape.height / 2, + radius = shape.width / 2; + + var circlePath = [ + [ 'M', cx, cy ], + [ 'm', 0, -radius ], + [ 'a', radius, radius, 0, 1, 1, 0, 2 * radius ], + [ 'a', radius, radius, 0, 1, 1, 0, -2 * radius ], + [ 'z' ] + ]; + + return componentsToPath(circlePath); +} + +export function getRoundRectPath(shape, borderRadius) { + + var x = shape.x, + y = shape.y, + width = shape.width, + height = shape.height; + + var roundRectPath = [ + [ 'M', x + borderRadius, y ], + [ 'l', width - borderRadius * 2, 0 ], + [ 'a', borderRadius, borderRadius, 0, 0, 1, borderRadius, borderRadius ], + [ 'l', 0, height - borderRadius * 2 ], + [ 'a', borderRadius, borderRadius, 0, 0, 1, -borderRadius, borderRadius ], + [ 'l', borderRadius * 2 - width, 0 ], + [ 'a', borderRadius, borderRadius, 0, 0, 1, -borderRadius, -borderRadius ], + [ 'l', 0, borderRadius * 2 - height ], + [ 'a', borderRadius, borderRadius, 0, 0, 1, borderRadius, -borderRadius ], + [ 'z' ] + ]; + + return componentsToPath(roundRectPath); +} + +export function getDiamondPath(shape) { + + var width = shape.width, + height = shape.height, + x = shape.x, + y = shape.y, + halfWidth = width / 2, + halfHeight = height / 2; + + var diamondPath = [ + [ 'M', x + halfWidth, y ], + [ 'l', halfWidth, halfHeight ], + [ 'l', -halfWidth, halfHeight ], + [ 'l', -halfWidth, -halfHeight ], + [ 'z' ] + ]; + + return componentsToPath(diamondPath); +} + +export function getRectPath(shape) { + var x = shape.x, + y = shape.y, + width = shape.width, + height = shape.height; + + var rectPath = [ + [ 'M', x, y ], + [ 'l', width, 0 ], + [ 'l', 0, height ], + [ 'l', -width, 0 ], + [ 'z' ] + ]; + + return componentsToPath(rectPath); +} \ No newline at end of file diff --git a/lib/draw/BpmnRenderer.js b/lib/draw/BpmnRenderer.js new file mode 100644 index 0000000..8dee875 --- /dev/null +++ b/lib/draw/BpmnRenderer.js @@ -0,0 +1,1928 @@ +import inherits from 'inherits-browser'; + +import { + isObject, + assign, + forEach +} from 'min-dash'; + +import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer'; + +import { + isExpanded, + isEventSubProcess +} from '../util/DiUtil'; + +import { + getLabel +} from '../features/label-editing/LabelUtil'; + +import { is } from '../util/ModelUtil'; + +import { + createLine +} from 'diagram-js/lib/util/RenderUtil'; + +import { + isTypedEvent, + isThrowEvent, + isCollection, + getDi, + getSemantic, + getCirclePath, + getRoundRectPath, + getDiamondPath, + getRectPath, + getFillColor, + getStrokeColor, + getLabelColor +} from './BpmnRenderUtil'; + +import { + query as domQuery +} from 'min-dom'; + +import { + append as svgAppend, + attr as svgAttr, + create as svgCreate, + classes as svgClasses +} from 'tiny-svg'; + +import { + rotate, + transform, + translate +} from 'diagram-js/lib/util/SvgTransformUtil'; + +import Ids from 'ids'; + +import { black } from './BpmnRenderUtil'; + +var RENDERER_IDS = new Ids(); + +var TASK_BORDER_RADIUS = 10; +var INNER_OUTER_DIST = 3; + +var DEFAULT_FILL_OPACITY = .95, + HIGH_FILL_OPACITY = .35; + +var ELEMENT_LABEL_DISTANCE = 10; + +export default function BpmnRenderer( + config, eventBus, styles, pathMap, + canvas, textRenderer, priority) { + + BaseRenderer.call(this, eventBus, priority); + + var defaultFillColor = config && config.defaultFillColor, + defaultStrokeColor = config && config.defaultStrokeColor, + defaultLabelColor = config && config.defaultLabelColor; + + var rendererId = RENDERER_IDS.next(); + + var markers = {}; + + var computeStyle = styles.computeStyle; + + function addMarker(id, options) { + var attrs = assign({ + fill: black, + strokeWidth: 1, + strokeLinecap: 'round', + strokeDasharray: 'none' + }, options.attrs); + + var ref = options.ref || { x: 0, y: 0 }; + + var scale = options.scale || 1; + + // fix for safari / chrome / firefox bug not correctly + // resetting stroke dash array + if (attrs.strokeDasharray === 'none') { + attrs.strokeDasharray = [ 10000, 1 ]; + } + + var marker = svgCreate('marker'); + + svgAttr(options.element, attrs); + + svgAppend(marker, options.element); + + svgAttr(marker, { + id: id, + viewBox: '0 0 20 20', + refX: ref.x, + refY: ref.y, + markerWidth: 20 * scale, + markerHeight: 20 * scale, + orient: 'auto' + }); + + var defs = domQuery('defs', canvas._svg); + + if (!defs) { + defs = svgCreate('defs'); + + svgAppend(canvas._svg, defs); + } + + svgAppend(defs, marker); + + markers[id] = marker; + } + + function colorEscape(str) { + + // only allow characters and numbers + return str.replace(/[^0-9a-zA-z]+/g, '_'); + } + + function marker(type, fill, stroke) { + var id = type + '-' + colorEscape(fill) + '-' + colorEscape(stroke) + '-' + rendererId; + + if (!markers[id]) { + createMarker(id, type, fill, stroke); + } + + return 'url(#' + id + ')'; + } + + function createMarker(id, type, fill, stroke) { + + if (type === 'sequenceflow-end') { + var sequenceflowEnd = svgCreate('path'); + svgAttr(sequenceflowEnd, { d: 'M 1 5 L 11 10 L 1 15 Z' }); + + addMarker(id, { + element: sequenceflowEnd, + ref: { x: 11, y: 10 }, + scale: 0.5, + attrs: { + fill: stroke, + stroke: stroke + } + }); + } + + if (type === 'messageflow-start') { + var messageflowStart = svgCreate('circle'); + svgAttr(messageflowStart, { cx: 6, cy: 6, r: 3.5 }); + + addMarker(id, { + element: messageflowStart, + attrs: { + fill: fill, + stroke: stroke + }, + ref: { x: 6, y: 6 } + }); + } + + if (type === 'messageflow-end') { + var messageflowEnd = svgCreate('path'); + svgAttr(messageflowEnd, { d: 'm 1 5 l 0 -3 l 7 3 l -7 3 z' }); + + addMarker(id, { + element: messageflowEnd, + attrs: { + fill: fill, + stroke: stroke, + strokeLinecap: 'butt' + }, + ref: { x: 8.5, y: 5 } + }); + } + + if (type === 'association-start') { + var associationStart = svgCreate('path'); + svgAttr(associationStart, { d: 'M 11 5 L 1 10 L 11 15' }); + + addMarker(id, { + element: associationStart, + attrs: { + fill: 'none', + stroke: stroke, + strokeWidth: 1.5 + }, + ref: { x: 1, y: 10 }, + scale: 0.5 + }); + } + + if (type === 'association-end') { + var associationEnd = svgCreate('path'); + svgAttr(associationEnd, { d: 'M 1 5 L 11 10 L 1 15' }); + + addMarker(id, { + element: associationEnd, + attrs: { + fill: 'none', + stroke: stroke, + strokeWidth: 1.5 + }, + ref: { x: 12, y: 10 }, + scale: 0.5 + }); + } + + if (type === 'conditional-flow-marker') { + var conditionalflowMarker = svgCreate('path'); + svgAttr(conditionalflowMarker, { d: 'M 0 10 L 8 6 L 16 10 L 8 14 Z' }); + + addMarker(id, { + element: conditionalflowMarker, + attrs: { + fill: fill, + stroke: stroke + }, + ref: { x: -1, y: 10 }, + scale: 0.5 + }); + } + + if (type === 'conditional-default-flow-marker') { + var conditionaldefaultflowMarker = svgCreate('path'); + svgAttr(conditionaldefaultflowMarker, { d: 'M 6 4 L 10 16' }); + + addMarker(id, { + element: conditionaldefaultflowMarker, + attrs: { + stroke: stroke + }, + ref: { x: 0, y: 10 }, + scale: 0.5 + }); + } + } + + function drawCircle(parentGfx, width, height, offset, attrs) { + + if (isObject(offset)) { + attrs = offset; + offset = 0; + } + + offset = offset || 0; + + attrs = computeStyle(attrs, { + stroke: black, + strokeWidth: 2, + fill: 'white' + }); + + if (attrs.fill === 'none') { + delete attrs.fillOpacity; + } + + var cx = width / 2, + cy = height / 2; + + var circle = svgCreate('circle'); + svgAttr(circle, { + cx: cx, + cy: cy, + r: Math.round((width + height) / 4 - offset) + }); + svgAttr(circle, attrs); + + svgAppend(parentGfx, circle); + + return circle; + } + + function drawRect(parentGfx, width, height, r, offset, attrs) { + + if (isObject(offset)) { + attrs = offset; + offset = 0; + } + + offset = offset || 0; + + attrs = computeStyle(attrs, { + stroke: black, + strokeWidth: 2, + fill: 'white' + }); + + var rect = svgCreate('rect'); + svgAttr(rect, { + x: offset, + y: offset, + width: width - offset * 2, + height: height - offset * 2, + rx: r, + ry: r + }); + svgAttr(rect, attrs); + + svgAppend(parentGfx, rect); + + return rect; + } + + function drawDiamond(parentGfx, width, height, attrs) { + + var x_2 = width / 2; + var y_2 = height / 2; + + var points = [ { x: x_2, y: 0 }, { x: width, y: y_2 }, { x: x_2, y: height }, { x: 0, y: y_2 } ]; + + var pointsString = points.map(function(point) { + return point.x + ',' + point.y; + }).join(' '); + + attrs = computeStyle(attrs, { + stroke: black, + strokeWidth: 2, + fill: 'white' + }); + + var polygon = svgCreate('polygon'); + svgAttr(polygon, { + points: pointsString + }); + svgAttr(polygon, attrs); + + svgAppend(parentGfx, polygon); + + return polygon; + } + + function drawLine(parentGfx, waypoints, attrs) { + attrs = computeStyle(attrs, [ 'no-fill' ], { + stroke: black, + strokeWidth: 2, + fill: 'none' + }); + + var line = createLine(waypoints, attrs); + + svgAppend(parentGfx, line); + + return line; + } + + function drawPath(parentGfx, d, attrs) { + + attrs = computeStyle(attrs, [ 'no-fill' ], { + strokeWidth: 2, + stroke: black + }); + + var path = svgCreate('path'); + svgAttr(path, { d: d }); + svgAttr(path, attrs); + + svgAppend(parentGfx, path); + + return path; + } + + function drawMarker(type, parentGfx, path, attrs) { + return drawPath(parentGfx, path, assign({ 'data-marker': type }, attrs)); + } + + function renderer(type) { + return handlers[type]; + } + + function as(type) { + return function(parentGfx, element) { + return renderer(type)(parentGfx, element); + }; + } + + function renderEventContent(element, parentGfx) { + + var event = getSemantic(element); + var isThrowing = isThrowEvent(event); + + if (event.eventDefinitions && event.eventDefinitions.length > 1) { + if (event.parallelMultiple) { + return renderer('bpmn:ParallelMultipleEventDefinition')(parentGfx, element, isThrowing); + } + else { + return renderer('bpmn:MultipleEventDefinition')(parentGfx, element, isThrowing); + } + } + + if (isTypedEvent(event, 'bpmn:MessageEventDefinition')) { + return renderer('bpmn:MessageEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:TimerEventDefinition')) { + return renderer('bpmn:TimerEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:ConditionalEventDefinition')) { + return renderer('bpmn:ConditionalEventDefinition')(parentGfx, element); + } + + if (isTypedEvent(event, 'bpmn:SignalEventDefinition')) { + return renderer('bpmn:SignalEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:EscalationEventDefinition')) { + return renderer('bpmn:EscalationEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:LinkEventDefinition')) { + return renderer('bpmn:LinkEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:ErrorEventDefinition')) { + return renderer('bpmn:ErrorEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:CancelEventDefinition')) { + return renderer('bpmn:CancelEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:CompensateEventDefinition')) { + return renderer('bpmn:CompensateEventDefinition')(parentGfx, element, isThrowing); + } + + if (isTypedEvent(event, 'bpmn:TerminateEventDefinition')) { + return renderer('bpmn:TerminateEventDefinition')(parentGfx, element, isThrowing); + } + + return null; + } + + function renderLabel(parentGfx, label, options) { + + options = assign({ + size: { + width: 100 + } + }, options); + + var text = textRenderer.createText(label || '', options); + + svgClasses(text).add('djs-label'); + + svgAppend(parentGfx, text); + + return text; + } + + function renderEmbeddedLabel(parentGfx, element, align) { + var semantic = getSemantic(element); + + return renderLabel(parentGfx, semantic.name, { + box: element, + align: align, + padding: 5, + style: { + fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor) + } + }); + } + + function renderExternalLabel(parentGfx, element) { + + var box = { + width: 90, + height: 30, + x: element.width / 2 + element.x, + y: element.height / 2 + element.y + }; + + return renderLabel(parentGfx, getLabel(element), { + box: box, + fitBox: true, + style: assign( + {}, + textRenderer.getExternalStyle(), + { + fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor) + } + ) + }); + } + + function renderLaneLabel(parentGfx, text, element) { + var textBox = renderLabel(parentGfx, text, { + box: { + height: 30, + width: element.height + }, + align: 'center-middle', + style: { + fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor) + } + }); + + var top = -1 * element.height; + + transform(textBox, 0, -top, 270); + } + + function createPathFromConnection(connection) { + var waypoints = connection.waypoints; + + var pathData = 'm ' + waypoints[0].x + ',' + waypoints[0].y; + for (var i = 1; i < waypoints.length; i++) { + pathData += 'L' + waypoints[i].x + ',' + waypoints[i].y + ' '; + } + return pathData; + } + + var handlers = this.handlers = { + 'bpmn:Event': function(parentGfx, element, attrs) { + + if (!('fillOpacity' in attrs)) { + attrs.fillOpacity = DEFAULT_FILL_OPACITY; + } + + return drawCircle(parentGfx, element.width, element.height, attrs); + }, + 'bpmn:StartEvent': function(parentGfx, element) { + var attrs = { + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + var semantic = getSemantic(element); + + if (!semantic.isInterrupting) { + attrs = { + strokeDasharray: '6', + strokeLinecap: 'round', + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }; + } + + var circle = renderer('bpmn:Event')(parentGfx, element, attrs); + + renderEventContent(element, parentGfx); + + return circle; + }, + 'bpmn:MessageEventDefinition': function(parentGfx, element, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_MESSAGE', { + xScaleFactor: 0.9, + yScaleFactor: 0.9, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.235, + my: 0.315 + } + }); + + var fill = isThrowing ? getStrokeColor(element, defaultStrokeColor) : getFillColor(element, defaultFillColor); + var stroke = isThrowing ? getFillColor(element, defaultFillColor) : getStrokeColor(element, defaultStrokeColor); + + var messagePath = drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: stroke + }); + + return messagePath; + }, + 'bpmn:TimerEventDefinition': function(parentGfx, element) { + var circle = drawCircle(parentGfx, element.width, element.height, 0.2 * element.height, { + strokeWidth: 2, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var pathData = pathMap.getScaledPath('EVENT_TIMER_WH', { + xScaleFactor: 0.75, + yScaleFactor: 0.75, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.5, + my: 0.5 + } + }); + + drawPath(parentGfx, pathData, { + strokeWidth: 2, + strokeLinecap: 'square', + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + for (var i = 0;i < 12; i++) { + + var linePathData = pathMap.getScaledPath('EVENT_TIMER_LINE', { + xScaleFactor: 0.75, + yScaleFactor: 0.75, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.5, + my: 0.5 + } + }); + + var width = element.width / 2; + var height = element.height / 2; + + drawPath(parentGfx, linePathData, { + strokeWidth: 1, + strokeLinecap: 'square', + transform: 'rotate(' + (i * 30) + ',' + height + ',' + width + ')', + stroke: getStrokeColor(element, defaultStrokeColor) + }); + } + + return circle; + }, + 'bpmn:EscalationEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_ESCALATION', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.5, + my: 0.2 + } + }); + + var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor(event, defaultStrokeColor) + }); + }, + 'bpmn:ConditionalEventDefinition': function(parentGfx, event) { + var pathData = pathMap.getScaledPath('EVENT_CONDITIONAL', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.5, + my: 0.222 + } + }); + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + stroke: getStrokeColor(event, defaultStrokeColor) + }); + }, + 'bpmn:LinkEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_LINK', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.57, + my: 0.263 + } + }); + + var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor(event, defaultStrokeColor) + }); + }, + 'bpmn:ErrorEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_ERROR', { + xScaleFactor: 1.1, + yScaleFactor: 1.1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.2, + my: 0.722 + } + }); + + var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor(event, defaultStrokeColor) + }); + }, + 'bpmn:CancelEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_CANCEL_45', { + xScaleFactor: 1.0, + yScaleFactor: 1.0, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.638, + my: -0.055 + } + }); + + var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none'; + + var path = drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor(event, defaultStrokeColor) + }); + + rotate(path, 45); + + return path; + }, + 'bpmn:CompensateEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_COMPENSATION', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.22, + my: 0.5 + } + }); + + var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor(event, defaultStrokeColor) + }); + }, + 'bpmn:SignalEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_SIGNAL', { + xScaleFactor: 0.9, + yScaleFactor: 0.9, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.5, + my: 0.2 + } + }); + + var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill, + stroke: getStrokeColor(event, defaultStrokeColor) + }); + }, + 'bpmn:MultipleEventDefinition': function(parentGfx, event, isThrowing) { + var pathData = pathMap.getScaledPath('EVENT_MULTIPLE', { + xScaleFactor: 1.1, + yScaleFactor: 1.1, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.222, + my: 0.36 + } + }); + + var fill = isThrowing ? getStrokeColor(event, defaultStrokeColor) : 'none'; + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: fill + }); + }, + 'bpmn:ParallelMultipleEventDefinition': function(parentGfx, event) { + var pathData = pathMap.getScaledPath('EVENT_PARALLEL_MULTIPLE', { + xScaleFactor: 1.2, + yScaleFactor: 1.2, + containerWidth: event.width, + containerHeight: event.height, + position: { + mx: 0.458, + my: 0.194 + } + }); + + return drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getStrokeColor(event, defaultStrokeColor), + stroke: getStrokeColor(event, defaultStrokeColor) + }); + }, + 'bpmn:EndEvent': function(parentGfx, element) { + var circle = renderer('bpmn:Event')(parentGfx, element, { + strokeWidth: 4, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + renderEventContent(element, parentGfx, true); + + return circle; + }, + 'bpmn:TerminateEventDefinition': function(parentGfx, element) { + var circle = drawCircle(parentGfx, element.width, element.height, 8, { + strokeWidth: 4, + fill: getStrokeColor(element, defaultStrokeColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return circle; + }, + 'bpmn:IntermediateEvent': function(parentGfx, element) { + var outer = renderer('bpmn:Event')(parentGfx, element, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + /* inner */ + drawCircle(parentGfx, element.width, element.height, INNER_OUTER_DIST, { + strokeWidth: 1, + fill: getFillColor(element, 'none'), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + renderEventContent(element, parentGfx); + + return outer; + }, + 'bpmn:IntermediateCatchEvent': as('bpmn:IntermediateEvent'), + 'bpmn:IntermediateThrowEvent': as('bpmn:IntermediateEvent'), + + 'bpmn:Activity': function(parentGfx, element, attrs) { + + attrs = attrs || {}; + + if (!('fillOpacity' in attrs)) { + attrs.fillOpacity = DEFAULT_FILL_OPACITY; + } + + return drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS, attrs); + }, + + 'bpmn:Task': function(parentGfx, element) { + var attrs = { + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + var rect = renderer('bpmn:Activity')(parentGfx, element, attrs); + + renderEmbeddedLabel(parentGfx, element, 'center-middle'); + attachTaskMarkers(parentGfx, element); + + return rect; + }, + 'bpmn:ServiceTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var pathDataBG = pathMap.getScaledPath('TASK_TYPE_SERVICE', { + abspos: { + x: 12, + y: 18 + } + }); + + /* service bg */ drawPath(parentGfx, pathDataBG, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var fillPathData = pathMap.getScaledPath('TASK_TYPE_SERVICE_FILL', { + abspos: { + x: 17.2, + y: 18 + } + }); + + /* service fill */ drawPath(parentGfx, fillPathData, { + strokeWidth: 0, + fill: getFillColor(element, defaultFillColor) + }); + + var pathData = pathMap.getScaledPath('TASK_TYPE_SERVICE', { + abspos: { + x: 17, + y: 22 + } + }); + + /* service */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:UserTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var x = 15; + var y = 12; + + var pathData = pathMap.getScaledPath('TASK_TYPE_USER_1', { + abspos: { + x: x, + y: y + } + }); + + /* user path */ drawPath(parentGfx, pathData, { + strokeWidth: 0.5, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var pathData2 = pathMap.getScaledPath('TASK_TYPE_USER_2', { + abspos: { + x: x, + y: y + } + }); + + /* user2 path */ drawPath(parentGfx, pathData2, { + strokeWidth: 0.5, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var pathData3 = pathMap.getScaledPath('TASK_TYPE_USER_3', { + abspos: { + x: x, + y: y + } + }); + + /* user3 path */ drawPath(parentGfx, pathData3, { + strokeWidth: 0.5, + fill: getStrokeColor(element, defaultStrokeColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:ManualTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var pathData = pathMap.getScaledPath('TASK_TYPE_MANUAL', { + abspos: { + x: 17, + y: 15 + } + }); + + /* manual path */ drawPath(parentGfx, pathData, { + strokeWidth: 0.5, // 0.25, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:SendTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var pathData = pathMap.getScaledPath('TASK_TYPE_SEND', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: 21, + containerHeight: 14, + position: { + mx: 0.285, + my: 0.357 + } + }); + + /* send path */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getStrokeColor(element, defaultStrokeColor), + stroke: getFillColor(element, defaultFillColor) + }); + + return task; + }, + 'bpmn:ReceiveTask' : function(parentGfx, element) { + var semantic = getSemantic(element); + + var task = renderer('bpmn:Task')(parentGfx, element); + var pathData; + + if (semantic.instantiate) { + drawCircle(parentGfx, 28, 28, 20 * 0.22, { strokeWidth: 1 }); + + pathData = pathMap.getScaledPath('TASK_TYPE_INSTANTIATING_SEND', { + abspos: { + x: 7.77, + y: 9.52 + } + }); + } else { + + pathData = pathMap.getScaledPath('TASK_TYPE_SEND', { + xScaleFactor: 0.9, + yScaleFactor: 0.9, + containerWidth: 21, + containerHeight: 14, + position: { + mx: 0.3, + my: 0.4 + } + }); + } + + /* receive path */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:ScriptTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var pathData = pathMap.getScaledPath('TASK_TYPE_SCRIPT', { + abspos: { + x: 15, + y: 20 + } + }); + + /* script path */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:BusinessRuleTask': function(parentGfx, element) { + var task = renderer('bpmn:Task')(parentGfx, element); + + var headerPathData = pathMap.getScaledPath('TASK_TYPE_BUSINESS_RULE_HEADER', { + abspos: { + x: 8, + y: 8 + } + }); + + var businessHeaderPath = drawPath(parentGfx, headerPathData); + svgAttr(businessHeaderPath, { + strokeWidth: 1, + fill: getFillColor(element, '#aaaaaa'), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var headerData = pathMap.getScaledPath('TASK_TYPE_BUSINESS_RULE_MAIN', { + abspos: { + x: 8, + y: 8 + } + }); + + var businessPath = drawPath(parentGfx, headerData); + svgAttr(businessPath, { + strokeWidth: 1, + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return task; + }, + 'bpmn:SubProcess': function(parentGfx, element, attrs) { + attrs = assign({ + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }, attrs); + + var rect = renderer('bpmn:Activity')(parentGfx, element, attrs); + + var expanded = isExpanded(element); + + if (isEventSubProcess(element)) { + svgAttr(rect, { + strokeDasharray: '1,2' + }); + } + + renderEmbeddedLabel(parentGfx, element, expanded ? 'center-top' : 'center-middle'); + + if (expanded) { + attachTaskMarkers(parentGfx, element); + } else { + attachTaskMarkers(parentGfx, element, [ 'SubProcessMarker' ]); + } + + return rect; + }, + 'bpmn:AdHocSubProcess': function(parentGfx, element) { + return renderer('bpmn:SubProcess')(parentGfx, element); + }, + 'bpmn:Transaction': function(parentGfx, element) { + var outer = renderer('bpmn:SubProcess')(parentGfx, element); + + var innerAttrs = styles.style([ 'no-fill', 'no-events' ], { + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + /* inner path */ drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS - 2, INNER_OUTER_DIST, innerAttrs); + + return outer; + }, + 'bpmn:CallActivity': function(parentGfx, element) { + return renderer('bpmn:SubProcess')(parentGfx, element, { + strokeWidth: 5 + }); + }, + 'bpmn:Participant': function(parentGfx, element) { + + var attrs = { + fillOpacity: DEFAULT_FILL_OPACITY, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + var lane = renderer('bpmn:Lane')(parentGfx, element, attrs); + + var expandedPool = isExpanded(element); + + if (expandedPool) { + drawLine(parentGfx, [ + { x: 30, y: 0 }, + { x: 30, y: element.height } + ], { + stroke: getStrokeColor(element, defaultStrokeColor) + }); + var text = getSemantic(element).name; + renderLaneLabel(parentGfx, text, element); + } else { + + // Collapsed pool draw text inline + var text2 = getSemantic(element).name; + renderLabel(parentGfx, text2, { + box: element, align: 'center-middle', + style: { + fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor) + } + }); + } + + var participantMultiplicity = !!(getSemantic(element).participantMultiplicity); + + if (participantMultiplicity) { + renderer('ParticipantMultiplicityMarker')(parentGfx, element); + } + + return lane; + }, + 'bpmn:Lane': function(parentGfx, element, attrs) { + var rect = drawRect(parentGfx, element.width, element.height, 0, assign({ + fill: getFillColor(element, defaultFillColor), + fillOpacity: HIGH_FILL_OPACITY, + stroke: getStrokeColor(element, defaultStrokeColor) + }, attrs)); + + var semantic = getSemantic(element); + + if (semantic.$type === 'bpmn:Lane') { + var text = semantic.name; + renderLaneLabel(parentGfx, text, element); + } + + return rect; + }, + 'bpmn:InclusiveGateway': function(parentGfx, element) { + var diamond = renderer('bpmn:Gateway')(parentGfx, element); + + /* circle path */ + drawCircle(parentGfx, element.width, element.height, element.height * 0.24, { + strokeWidth: 2.5, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return diamond; + }, + 'bpmn:ExclusiveGateway': function(parentGfx, element) { + var diamond = renderer('bpmn:Gateway')(parentGfx, element); + + var pathData = pathMap.getScaledPath('GATEWAY_EXCLUSIVE', { + xScaleFactor: 0.4, + yScaleFactor: 0.4, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.32, + my: 0.3 + } + }); + + if ((getDi(element).isMarkerVisible)) { + drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getStrokeColor(element, defaultStrokeColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + } + + return diamond; + }, + 'bpmn:ComplexGateway': function(parentGfx, element) { + var diamond = renderer('bpmn:Gateway')(parentGfx, element); + + var pathData = pathMap.getScaledPath('GATEWAY_COMPLEX', { + xScaleFactor: 0.5, + yScaleFactor:0.5, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.46, + my: 0.26 + } + }); + + /* complex path */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getStrokeColor(element, defaultStrokeColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return diamond; + }, + 'bpmn:ParallelGateway': function(parentGfx, element) { + var diamond = renderer('bpmn:Gateway')(parentGfx, element); + + var pathData = pathMap.getScaledPath('GATEWAY_PARALLEL', { + xScaleFactor: 0.6, + yScaleFactor:0.6, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.46, + my: 0.2 + } + }); + + /* parallel path */ drawPath(parentGfx, pathData, { + strokeWidth: 1, + fill: getStrokeColor(element, defaultStrokeColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return diamond; + }, + 'bpmn:EventBasedGateway': function(parentGfx, element) { + + var semantic = getSemantic(element); + + var diamond = renderer('bpmn:Gateway')(parentGfx, element); + + /* outer circle path */ drawCircle(parentGfx, element.width, element.height, element.height * 0.20, { + strokeWidth: 1, + fill: 'none', + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var type = semantic.eventGatewayType; + var instantiate = !!semantic.instantiate; + + function drawEvent() { + + var pathData = pathMap.getScaledPath('GATEWAY_EVENT_BASED', { + xScaleFactor: 0.18, + yScaleFactor: 0.18, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.36, + my: 0.44 + } + }); + + var attrs = { + strokeWidth: 2, + fill: getFillColor(element, 'none'), + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + /* event path */ drawPath(parentGfx, pathData, attrs); + } + + if (type === 'Parallel') { + + var pathData = pathMap.getScaledPath('GATEWAY_PARALLEL', { + xScaleFactor: 0.4, + yScaleFactor:0.4, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.474, + my: 0.296 + } + }); + + var parallelPath = drawPath(parentGfx, pathData); + svgAttr(parallelPath, { + strokeWidth: 1, + fill: 'none' + }); + } else if (type === 'Exclusive') { + + if (!instantiate) { + var innerCircle = drawCircle(parentGfx, element.width, element.height, element.height * 0.26); + svgAttr(innerCircle, { + strokeWidth: 1, + fill: 'none', + stroke: getStrokeColor(element, defaultStrokeColor) + }); + } + + drawEvent(); + } + + + return diamond; + }, + 'bpmn:Gateway': function(parentGfx, element) { + var attrs = { + fill: getFillColor(element, defaultFillColor), + fillOpacity: DEFAULT_FILL_OPACITY, + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + return drawDiamond(parentGfx, element.width, element.height, attrs); + }, + 'bpmn:SequenceFlow': function(parentGfx, element) { + var pathData = createPathFromConnection(element); + + var fill = getFillColor(element, defaultFillColor), + stroke = getStrokeColor(element, defaultStrokeColor); + + var attrs = { + strokeLinejoin: 'round', + markerEnd: marker('sequenceflow-end', fill, stroke), + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + var path = drawPath(parentGfx, pathData, attrs); + + var sequenceFlow = getSemantic(element); + + var source; + + if (element.source) { + source = element.source.businessObject; + + // conditional flow marker + if (sequenceFlow.conditionExpression && source.$instanceOf('bpmn:Activity')) { + svgAttr(path, { + markerStart: marker('conditional-flow-marker', fill, stroke) + }); + } + + // default marker + if (source.default && (source.$instanceOf('bpmn:Gateway') || source.$instanceOf('bpmn:Activity')) && + source.default === sequenceFlow) { + svgAttr(path, { + markerStart: marker('conditional-default-flow-marker', fill, stroke) + }); + } + } + + return path; + }, + 'bpmn:Association': function(parentGfx, element, attrs) { + + var semantic = getSemantic(element); + + var fill = getFillColor(element, defaultFillColor), + stroke = getStrokeColor(element, defaultStrokeColor); + + attrs = assign({ + strokeDasharray: '0.5, 5', + strokeLinecap: 'round', + strokeLinejoin: 'round', + stroke: getStrokeColor(element, defaultStrokeColor) + }, attrs || {}); + + if (semantic.associationDirection === 'One' || + semantic.associationDirection === 'Both') { + attrs.markerEnd = marker('association-end', fill, stroke); + } + + if (semantic.associationDirection === 'Both') { + attrs.markerStart = marker('association-start', fill, stroke); + } + + return drawLine(parentGfx, element.waypoints, attrs); + }, + 'bpmn:DataInputAssociation': function(parentGfx, element) { + var fill = getFillColor(element, defaultFillColor), + stroke = getStrokeColor(element, defaultStrokeColor); + + return renderer('bpmn:Association')(parentGfx, element, { + markerEnd: marker('association-end', fill, stroke) + }); + }, + 'bpmn:DataOutputAssociation': function(parentGfx, element) { + var fill = getFillColor(element, defaultFillColor), + stroke = getStrokeColor(element, defaultStrokeColor); + + return renderer('bpmn:Association')(parentGfx, element, { + markerEnd: marker('association-end', fill, stroke) + }); + }, + 'bpmn:MessageFlow': function(parentGfx, element) { + + var semantic = getSemantic(element), + di = getDi(element); + + var fill = getFillColor(element, defaultFillColor), + stroke = getStrokeColor(element, defaultStrokeColor); + + var pathData = createPathFromConnection(element); + + var attrs = { + markerEnd: marker('messageflow-end', fill, stroke), + markerStart: marker('messageflow-start', fill, stroke), + strokeDasharray: '10, 12', + strokeLinecap: 'round', + strokeLinejoin: 'round', + strokeWidth: '1.5px', + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + var path = drawPath(parentGfx, pathData, attrs); + + if (semantic.messageRef) { + var midPoint = path.getPointAtLength(path.getTotalLength() / 2); + + var markerPathData = pathMap.getScaledPath('MESSAGE_FLOW_MARKER', { + abspos: { + x: midPoint.x, + y: midPoint.y + } + }); + + var messageAttrs = { strokeWidth: 1 }; + + if (di.messageVisibleKind === 'initiating') { + messageAttrs.fill = 'white'; + messageAttrs.stroke = black; + } else { + messageAttrs.fill = '#888'; + messageAttrs.stroke = 'white'; + } + + var message = drawPath(parentGfx, markerPathData, messageAttrs); + + var labelText = semantic.messageRef.name; + var label = renderLabel(parentGfx, labelText, { + align: 'center-top', + fitBox: true, + style: { + fill: getStrokeColor(element, defaultLabelColor, defaultStrokeColor) + } + }); + + var messageBounds = message.getBBox(), + labelBounds = label.getBBox(); + + var translateX = midPoint.x - labelBounds.width / 2, + translateY = midPoint.y + messageBounds.height / 2 + ELEMENT_LABEL_DISTANCE; + + transform(label, translateX, translateY, 0); + + } + + return path; + }, + 'bpmn:DataObject': function(parentGfx, element) { + var pathData = pathMap.getScaledPath('DATA_OBJECT_PATH', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.474, + my: 0.296 + } + }); + + var elementObject = drawPath(parentGfx, pathData, { + fill: getFillColor(element, defaultFillColor), + fillOpacity: DEFAULT_FILL_OPACITY, + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var semantic = getSemantic(element); + + if (isCollection(semantic)) { + renderDataItemCollection(parentGfx, element); + } + + return elementObject; + }, + 'bpmn:DataObjectReference': as('bpmn:DataObject'), + 'bpmn:DataInput': function(parentGfx, element) { + + var arrowPathData = pathMap.getRawPath('DATA_ARROW'); + + // page + var elementObject = renderer('bpmn:DataObject')(parentGfx, element); + + /* input arrow path */ drawPath(parentGfx, arrowPathData, { strokeWidth: 1 }); + + return elementObject; + }, + 'bpmn:DataOutput': function(parentGfx, element) { + var arrowPathData = pathMap.getRawPath('DATA_ARROW'); + + // page + var elementObject = renderer('bpmn:DataObject')(parentGfx, element); + + /* output arrow path */ drawPath(parentGfx, arrowPathData, { + strokeWidth: 1, + fill: black + }); + + return elementObject; + }, + 'bpmn:DataStoreReference': function(parentGfx, element) { + var DATA_STORE_PATH = pathMap.getScaledPath('DATA_STORE', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0, + my: 0.133 + } + }); + + var elementStore = drawPath(parentGfx, DATA_STORE_PATH, { + strokeWidth: 2, + fill: getFillColor(element, defaultFillColor), + fillOpacity: DEFAULT_FILL_OPACITY, + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + return elementStore; + }, + 'bpmn:BoundaryEvent': function(parentGfx, element) { + + var semantic = getSemantic(element), + cancel = semantic.cancelActivity; + + var attrs = { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }; + + if (!cancel) { + attrs.strokeDasharray = '6'; + attrs.strokeLinecap = 'round'; + } + + // apply fillOpacity + var outerAttrs = assign({}, attrs, { + fillOpacity: 1 + }); + + // apply no-fill + var innerAttrs = assign({}, attrs, { + fill: 'none' + }); + + var outer = renderer('bpmn:Event')(parentGfx, element, outerAttrs); + + /* inner path */ drawCircle(parentGfx, element.width, element.height, INNER_OUTER_DIST, innerAttrs); + + renderEventContent(element, parentGfx); + + return outer; + }, + 'bpmn:Group': function(parentGfx, element) { + + var group = drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS, { + stroke: getStrokeColor(element, defaultStrokeColor), + strokeWidth: 1, + strokeDasharray: '8,3,1,3', + fill: 'none', + pointerEvents: 'none' + }); + + return group; + }, + 'label': function(parentGfx, element) { + return renderExternalLabel(parentGfx, element); + }, + 'bpmn:TextAnnotation': function(parentGfx, element) { + var style = { + 'fill': 'none', + 'stroke': 'none' + }; + + var textElement = drawRect(parentGfx, element.width, element.height, 0, 0, style); + + var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.0, + my: 0.0 + } + }); + + drawPath(parentGfx, textPathData, { + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + var text = getSemantic(element).text || ''; + renderLabel(parentGfx, text, { + box: element, + align: 'left-top', + padding: 5, + style: { + fill: getLabelColor(element, defaultLabelColor, defaultStrokeColor) + } + }); + + return textElement; + }, + 'ParticipantMultiplicityMarker': function(parentGfx, element) { + var markerPath = pathMap.getScaledPath('MARKER_PARALLEL', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2) / element.width), + my: (element.height - 15) / element.height + } + }); + + drawMarker('participant-multiplicity', parentGfx, markerPath, { + strokeWidth: 2, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + }, + 'SubProcessMarker': function(parentGfx, element) { + var markerRect = drawRect(parentGfx, 14, 14, 0, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + + // Process marker is placed in the middle of the box + // therefore fixed values can be used here + translate(markerRect, element.width / 2 - 7.5, element.height - 20); + + var markerPath = pathMap.getScaledPath('MARKER_SUB_PROCESS', { + xScaleFactor: 1.5, + yScaleFactor: 1.5, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: (element.width / 2 - 7.5) / element.width, + my: (element.height - 20) / element.height + } + }); + + drawMarker('sub-process', parentGfx, markerPath, { + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + }, + 'ParallelMarker': function(parentGfx, element, position) { + var markerPath = pathMap.getScaledPath('MARKER_PARALLEL', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2 + position.parallel) / element.width), + my: (element.height - 20) / element.height + } + }); + + drawMarker('parallel', parentGfx, markerPath, { + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + }, + 'SequentialMarker': function(parentGfx, element, position) { + var markerPath = pathMap.getScaledPath('MARKER_SEQUENTIAL', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2 + position.seq) / element.width), + my: (element.height - 19) / element.height + } + }); + + drawMarker('sequential', parentGfx, markerPath, { + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + }, + 'CompensationMarker': function(parentGfx, element, position) { + var markerMath = pathMap.getScaledPath('MARKER_COMPENSATION', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2 + position.compensation) / element.width), + my: (element.height - 13) / element.height + } + }); + + drawMarker('compensation', parentGfx, markerMath, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + }, + 'LoopMarker': function(parentGfx, element, position) { + var markerPath = pathMap.getScaledPath('MARKER_LOOP', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2 + position.loop) / element.width), + my: (element.height - 7) / element.height + } + }); + + drawMarker('loop', parentGfx, markerPath, { + strokeWidth: 1, + fill: getFillColor(element, defaultFillColor), + stroke: getStrokeColor(element, defaultStrokeColor), + strokeLinecap: 'round', + strokeMiterlimit: 0.5 + }); + }, + 'AdhocMarker': function(parentGfx, element, position) { + var markerPath = pathMap.getScaledPath('MARKER_ADHOC', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: ((element.width / 2 + position.adhoc) / element.width), + my: (element.height - 15) / element.height + } + }); + + drawMarker('adhoc', parentGfx, markerPath, { + strokeWidth: 1, + fill: getStrokeColor(element, defaultStrokeColor), + stroke: getStrokeColor(element, defaultStrokeColor) + }); + } + }; + + function attachTaskMarkers(parentGfx, element, taskMarkers) { + var obj = getSemantic(element); + + var subprocess = taskMarkers && taskMarkers.indexOf('SubProcessMarker') !== -1; + var position; + + if (subprocess) { + position = { + seq: -21, + parallel: -22, + compensation: -42, + loop: -18, + adhoc: 10 + }; + } else { + position = { + seq: -3, + parallel: -6, + compensation: -27, + loop: 0, + adhoc: 10 + }; + } + + forEach(taskMarkers, function(marker) { + renderer(marker)(parentGfx, element, position); + }); + + if (obj.isForCompensation) { + renderer('CompensationMarker')(parentGfx, element, position); + } + + if (obj.$type === 'bpmn:AdHocSubProcess') { + renderer('AdhocMarker')(parentGfx, element, position); + } + + var loopCharacteristics = obj.loopCharacteristics, + isSequential = loopCharacteristics && loopCharacteristics.isSequential; + + if (loopCharacteristics) { + + if (isSequential === undefined) { + renderer('LoopMarker')(parentGfx, element, position); + } + + if (isSequential === false) { + renderer('ParallelMarker')(parentGfx, element, position); + } + + if (isSequential === true) { + renderer('SequentialMarker')(parentGfx, element, position); + } + } + } + + function renderDataItemCollection(parentGfx, element) { + + var yPosition = (element.height - 18) / element.height; + + var pathData = pathMap.getScaledPath('DATA_OBJECT_COLLECTION_PATH', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.33, + my: yPosition + } + }); + + /* collection path */ drawPath(parentGfx, pathData, { + strokeWidth: 2 + }); + } + + + // extension API, use at your own risk + this._drawPath = drawPath; + + this._renderer = renderer; +} + + +inherits(BpmnRenderer, BaseRenderer); + +BpmnRenderer.$inject = [ + 'config.bpmnRenderer', + 'eventBus', + 'styles', + 'pathMap', + 'canvas', + 'textRenderer' +]; + + +BpmnRenderer.prototype.canRender = function(element) { + return is(element, 'bpmn:BaseElement'); +}; + +BpmnRenderer.prototype.drawShape = function(parentGfx, element) { + var type = element.type; + var h = this._renderer(type); + + /* jshint -W040 */ + return h(parentGfx, element); +}; + +BpmnRenderer.prototype.drawConnection = function(parentGfx, element) { + var type = element.type; + var h = this._renderer(type); + + /* jshint -W040 */ + return h(parentGfx, element); +}; + +BpmnRenderer.prototype.getShapePath = function(element) { + + if (is(element, 'bpmn:Event')) { + return getCirclePath(element); + } + + if (is(element, 'bpmn:Activity')) { + return getRoundRectPath(element, TASK_BORDER_RADIUS); + } + + if (is(element, 'bpmn:Gateway')) { + return getDiamondPath(element); + } + + return getRectPath(element); +}; diff --git a/lib/draw/PathMap.js b/lib/draw/PathMap.js new file mode 100644 index 0000000..343cbc1 --- /dev/null +++ b/lib/draw/PathMap.js @@ -0,0 +1,471 @@ +/** + * Map containing SVG paths needed by BpmnRenderer. + */ + +export default function PathMap() { + + /** + * Contains a map of path elements + * + *

              Path definition

              + * A parameterized path is defined like this: + *
              +   * 'GATEWAY_PARALLEL': {
              +   *   d: 'm {mx},{my} {e.x0},0 0,{e.x1} {e.x1},0 0,{e.y0} -{e.x1},0 0,{e.y1} ' +
              +          '-{e.x0},0 0,-{e.y1} -{e.x1},0 0,-{e.y0} {e.x1},0 z',
              +   *   height: 17.5,
              +   *   width:  17.5,
              +   *   heightElements: [2.5, 7.5],
              +   *   widthElements: [2.5, 7.5]
              +   * }
              +   * 
              + *

              It's important to specify a correct height and width for the path as the scaling + * is based on the ratio between the specified height and width in this object and the + * height and width that is set as scale target (Note x,y coordinates will be scaled with + * individual ratios).

              + *

              The 'heightElements' and 'widthElements' array must contain the values that will be scaled. + * The scaling is based on the computed ratios. + * Coordinates on the y axis should be in the heightElement's array, they will be scaled using + * the computed ratio coefficient. + * In the parameterized path the scaled values can be accessed through the 'e' object in {} brackets. + *

                + *
              • The values for the y axis can be accessed in the path string using {e.y0}, {e.y1}, ....
              • + *
              • The values for the x axis can be accessed in the path string using {e.x0}, {e.x1}, ....
              • + *
              + * The numbers x0, x1 respectively y0, y1, ... map to the corresponding array index. + *

              + */ + this.pathMap = { + 'EVENT_MESSAGE': { + d: 'm {mx},{my} l 0,{e.y1} l {e.x1},0 l 0,-{e.y1} z l {e.x0},{e.y0} l {e.x0},-{e.y0}', + height: 36, + width: 36, + heightElements: [ 6, 14 ], + widthElements: [ 10.5, 21 ] + }, + 'EVENT_SIGNAL': { + d: 'M {mx},{my} l {e.x0},{e.y0} l -{e.x1},0 Z', + height: 36, + width: 36, + heightElements: [ 18 ], + widthElements: [ 10, 20 ] + }, + 'EVENT_ESCALATION': { + d: 'M {mx},{my} l {e.x0},{e.y0} l -{e.x0},-{e.y1} l -{e.x0},{e.y1} Z', + height: 36, + width: 36, + heightElements: [ 20, 7 ], + widthElements: [ 8 ] + }, + 'EVENT_CONDITIONAL': { + d: 'M {e.x0},{e.y0} l {e.x1},0 l 0,{e.y2} l -{e.x1},0 Z ' + + 'M {e.x2},{e.y3} l {e.x0},0 ' + + 'M {e.x2},{e.y4} l {e.x0},0 ' + + 'M {e.x2},{e.y5} l {e.x0},0 ' + + 'M {e.x2},{e.y6} l {e.x0},0 ' + + 'M {e.x2},{e.y7} l {e.x0},0 ' + + 'M {e.x2},{e.y8} l {e.x0},0 ', + height: 36, + width: 36, + heightElements: [ 8.5, 14.5, 18, 11.5, 14.5, 17.5, 20.5, 23.5, 26.5 ], + widthElements: [ 10.5, 14.5, 12.5 ] + }, + 'EVENT_LINK': { + d: 'm {mx},{my} 0,{e.y0} -{e.x1},0 0,{e.y1} {e.x1},0 0,{e.y0} {e.x0},-{e.y2} -{e.x0},-{e.y2} z', + height: 36, + width: 36, + heightElements: [ 4.4375, 6.75, 7.8125 ], + widthElements: [ 9.84375, 13.5 ] + }, + 'EVENT_ERROR': { + d: 'm {mx},{my} {e.x0},-{e.y0} {e.x1},-{e.y1} {e.x2},{e.y2} {e.x3},-{e.y3} -{e.x4},{e.y4} -{e.x5},-{e.y5} z', + height: 36, + width: 36, + heightElements: [ 0.023, 8.737, 8.151, 16.564, 10.591, 8.714 ], + widthElements: [ 0.085, 6.672, 6.97, 4.273, 5.337, 6.636 ] + }, + 'EVENT_CANCEL_45': { + d: 'm {mx},{my} -{e.x1},0 0,{e.x0} {e.x1},0 0,{e.y1} {e.x0},0 ' + + '0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z', + height: 36, + width: 36, + heightElements: [ 4.75, 8.5 ], + widthElements: [ 4.75, 8.5 ] + }, + 'EVENT_COMPENSATION': { + d: 'm {mx},{my} {e.x0},-{e.y0} 0,{e.y1} z m {e.x1},-{e.y2} {e.x2},-{e.y3} 0,{e.y1} -{e.x2},-{e.y3} z', + height: 36, + width: 36, + heightElements: [ 6.5, 13, 0.4, 6.1 ], + widthElements: [ 9, 9.3, 8.7 ] + }, + 'EVENT_TIMER_WH': { + d: 'M {mx},{my} l {e.x0},-{e.y0} m -{e.x0},{e.y0} l {e.x1},{e.y1} ', + height: 36, + width: 36, + heightElements: [ 10, 2 ], + widthElements: [ 3, 7 ] + }, + 'EVENT_TIMER_LINE': { + d: 'M {mx},{my} ' + + 'm {e.x0},{e.y0} l -{e.x1},{e.y1} ', + height: 36, + width: 36, + heightElements: [ 10, 3 ], + widthElements: [ 0, 0 ] + }, + 'EVENT_MULTIPLE': { + d:'m {mx},{my} {e.x1},-{e.y0} {e.x1},{e.y0} -{e.x0},{e.y1} -{e.x2},0 z', + height: 36, + width: 36, + heightElements: [ 6.28099, 12.56199 ], + widthElements: [ 3.1405, 9.42149, 12.56198 ] + }, + 'EVENT_PARALLEL_MULTIPLE': { + d:'m {mx},{my} {e.x0},0 0,{e.y1} {e.x1},0 0,{e.y0} -{e.x1},0 0,{e.y1} ' + + '-{e.x0},0 0,-{e.y1} -{e.x1},0 0,-{e.y0} {e.x1},0 z', + height: 36, + width: 36, + heightElements: [ 2.56228, 7.68683 ], + widthElements: [ 2.56228, 7.68683 ] + }, + 'GATEWAY_EXCLUSIVE': { + d:'m {mx},{my} {e.x0},{e.y0} {e.x1},{e.y0} {e.x2},0 {e.x4},{e.y2} ' + + '{e.x4},{e.y1} {e.x2},0 {e.x1},{e.y3} {e.x0},{e.y3} ' + + '{e.x3},0 {e.x5},{e.y1} {e.x5},{e.y2} {e.x3},0 z', + height: 17.5, + width: 17.5, + heightElements: [ 8.5, 6.5312, -6.5312, -8.5 ], + widthElements: [ 6.5, -6.5, 3, -3, 5, -5 ] + }, + 'GATEWAY_PARALLEL': { + d:'m {mx},{my} 0,{e.y1} -{e.x1},0 0,{e.y0} {e.x1},0 0,{e.y1} {e.x0},0 ' + + '0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z', + height: 30, + width: 30, + heightElements: [ 5, 12.5 ], + widthElements: [ 5, 12.5 ] + }, + 'GATEWAY_EVENT_BASED': { + d:'m {mx},{my} {e.x0},{e.y0} {e.x0},{e.y1} {e.x1},{e.y2} {e.x2},0 z', + height: 11, + width: 11, + heightElements: [ -6, 6, 12, -12 ], + widthElements: [ 9, -3, -12 ] + }, + 'GATEWAY_COMPLEX': { + d:'m {mx},{my} 0,{e.y0} -{e.x0},-{e.y1} -{e.x1},{e.y2} {e.x0},{e.y1} -{e.x2},0 0,{e.y3} ' + + '{e.x2},0 -{e.x0},{e.y1} l {e.x1},{e.y2} {e.x0},-{e.y1} 0,{e.y0} {e.x3},0 0,-{e.y0} {e.x0},{e.y1} ' + + '{e.x1},-{e.y2} -{e.x0},-{e.y1} {e.x2},0 0,-{e.y3} -{e.x2},0 {e.x0},-{e.y1} -{e.x1},-{e.y2} ' + + '-{e.x0},{e.y1} 0,-{e.y0} -{e.x3},0 z', + height: 17.125, + width: 17.125, + heightElements: [ 4.875, 3.4375, 2.125, 3 ], + widthElements: [ 3.4375, 2.125, 4.875, 3 ] + }, + 'DATA_OBJECT_PATH': { + d:'m 0,0 {e.x1},0 {e.x0},{e.y0} 0,{e.y1} -{e.x2},0 0,-{e.y2} {e.x1},0 0,{e.y0} {e.x0},0', + height: 61, + width: 51, + heightElements: [ 10, 50, 60 ], + widthElements: [ 10, 40, 50, 60 ] + }, + 'DATA_OBJECT_COLLECTION_PATH': { + d: 'm{mx},{my} m 3,2 l 0,10 m 3,-10 l 0,10 m 3,-10 l 0,10', + height: 10, + width: 10, + heightElements: [], + widthElements: [] + }, + 'DATA_ARROW': { + d:'m 5,9 9,0 0,-3 5,5 -5,5 0,-3 -9,0 z', + height: 61, + width: 51, + heightElements: [], + widthElements: [] + }, + 'DATA_STORE': { + d:'m {mx},{my} ' + + 'l 0,{e.y2} ' + + 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 ' + + 'l 0,-{e.y2} ' + + 'c -{e.x0},-{e.y1} -{e.x1},-{e.y1} -{e.x2},0' + + 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 ' + + 'm -{e.x2},{e.y0}' + + 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0' + + 'm -{e.x2},{e.y0}' + + 'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0', + height: 61, + width: 61, + heightElements: [ 7, 10, 45 ], + widthElements: [ 2, 58, 60 ] + }, + 'TEXT_ANNOTATION': { + d: 'm {mx}, {my} m 10,0 l -10,0 l 0,{e.y0} l 10,0', + height: 30, + width: 10, + heightElements: [ 30 ], + widthElements: [ 10 ] + }, + 'MARKER_SUB_PROCESS': { + d: 'm{mx},{my} m 7,2 l 0,10 m -5,-5 l 10,0', + height: 10, + width: 10, + heightElements: [], + widthElements: [] + }, + 'MARKER_PARALLEL': { + d: 'm{mx},{my} m 3,2 l 0,10 m 3,-10 l 0,10 m 3,-10 l 0,10', + height: 10, + width: 10, + heightElements: [], + widthElements: [] + }, + 'MARKER_SEQUENTIAL': { + d: 'm{mx},{my} m 0,3 l 10,0 m -10,3 l 10,0 m -10,3 l 10,0', + height: 10, + width: 10, + heightElements: [], + widthElements: [] + }, + 'MARKER_COMPENSATION': { + d: 'm {mx},{my} 7,-5 0,10 z m 7.1,-0.3 6.9,-4.7 0,10 -6.9,-4.7 z', + height: 10, + width: 21, + heightElements: [], + widthElements: [] + }, + 'MARKER_LOOP': { + d: 'm {mx},{my} c 3.526979,0 6.386161,-2.829858 6.386161,-6.320661 0,-3.490806 -2.859182,-6.320661 ' + + '-6.386161,-6.320661 -3.526978,0 -6.38616,2.829855 -6.38616,6.320661 0,1.745402 ' + + '0.714797,3.325567 1.870463,4.469381 0.577834,0.571908 1.265885,1.034728 2.029916,1.35457 ' + + 'l -0.718163,-3.909793 m 0.718163,3.909793 -3.885211,0.802902', + height: 13.9, + width: 13.7, + heightElements: [], + widthElements: [] + }, + 'MARKER_ADHOC': { + d: 'm {mx},{my} m 0.84461,2.64411 c 1.05533,-1.23780996 2.64337,-2.07882 4.29653,-1.97997996 2.05163,0.0805 ' + + '3.85579,1.15803 5.76082,1.79107 1.06385,0.34139996 2.24454,0.1438 3.18759,-0.43767 0.61743,-0.33642 ' + + '1.2775,-0.64078 1.7542,-1.17511 0,0.56023 0,1.12046 0,1.6807 -0.98706,0.96237996 -2.29792,1.62393996 ' + + '-3.6918,1.66181996 -1.24459,0.0927 -2.46671,-0.2491 -3.59505,-0.74812 -1.35789,-0.55965 ' + + '-2.75133,-1.33436996 -4.27027,-1.18121996 -1.37741,0.14601 -2.41842,1.13685996 -3.44288,1.96782996 z', + height: 4, + width: 15, + heightElements: [], + widthElements: [] + }, + 'TASK_TYPE_SEND': { + d: 'm {mx},{my} l 0,{e.y1} l {e.x1},0 l 0,-{e.y1} z l {e.x0},{e.y0} l {e.x0},-{e.y0}', + height: 14, + width: 21, + heightElements: [ 6, 14 ], + widthElements: [ 10.5, 21 ] + }, + 'TASK_TYPE_SCRIPT': { + d: 'm {mx},{my} c 9.966553,-6.27276 -8.000926,-7.91932 2.968968,-14.938 l -8.802728,0 ' + + 'c -10.969894,7.01868 6.997585,8.66524 -2.968967,14.938 z ' + + 'm -7,-12 l 5,0 ' + + 'm -4.5,3 l 4.5,0 ' + + 'm -3,3 l 5,0' + + 'm -4,3 l 5,0', + height: 15, + width: 12.6, + heightElements: [ 6, 14 ], + widthElements: [ 10.5, 21 ] + }, + 'TASK_TYPE_USER_1': { + d: 'm {mx},{my} c 0.909,-0.845 1.594,-2.049 1.594,-3.385 0,-2.554 -1.805,-4.62199999 ' + + '-4.357,-4.62199999 -2.55199998,0 -4.28799998,2.06799999 -4.28799998,4.62199999 0,1.348 ' + + '0.974,2.562 1.89599998,3.405 -0.52899998,0.187 -5.669,2.097 -5.794,4.7560005 v 6.718 ' + + 'h 17 v -6.718 c 0,-2.2980005 -5.5279996,-4.5950005 -6.0509996,-4.7760005 z' + + 'm -8,6 l 0,5.5 m 11,0 l 0,-5' + }, + 'TASK_TYPE_USER_2': { + d: 'm {mx},{my} m 2.162,1.009 c 0,2.4470005 -2.158,4.4310005 -4.821,4.4310005 ' + + '-2.66499998,0 -4.822,-1.981 -4.822,-4.4310005 ' + }, + 'TASK_TYPE_USER_3': { + d: 'm {mx},{my} m -6.9,-3.80 c 0,0 2.25099998,-2.358 4.27399998,-1.177 2.024,1.181 4.221,1.537 ' + + '4.124,0.965 -0.098,-0.57 -0.117,-3.79099999 -4.191,-4.13599999 -3.57499998,0.001 ' + + '-4.20799998,3.36699999 -4.20699998,4.34799999 z' + }, + 'TASK_TYPE_MANUAL': { + d: 'm {mx},{my} c 0.234,-0.01 5.604,0.008 8.029,0.004 0.808,0 1.271,-0.172 1.417,-0.752 0.227,-0.898 ' + + '-0.334,-1.314 -1.338,-1.316 -2.467,-0.01 -7.886,-0.004 -8.108,-0.004 -0.014,-0.079 0.016,-0.533 0,-0.61 ' + + '0.195,-0.042 8.507,0.006 9.616,0.002 0.877,-0.007 1.35,-0.438 1.353,-1.208 0.003,-0.768 -0.479,-1.09 ' + + '-1.35,-1.091 -2.968,-0.002 -9.619,-0.013 -9.619,-0.013 v -0.591 c 0,0 5.052,-0.016 7.225,-0.016 ' + + '0.888,-0.002 1.354,-0.416 1.351,-1.193 -0.006,-0.761 -0.492,-1.196 -1.361,-1.196 -3.473,-0.005 ' + + '-10.86,-0.003 -11.0829995,-0.003 -0.022,-0.047 -0.045,-0.094 -0.069,-0.139 0.3939995,-0.319 ' + + '2.0409995,-1.626 2.4149995,-2.017 0.469,-0.4870005 0.519,-1.1650005 0.162,-1.6040005 -0.414,-0.511 ' + + '-0.973,-0.5 -1.48,-0.236 -1.4609995,0.764 -6.5999995,3.6430005 -7.7329995,4.2710005 -0.9,0.499 ' + + '-1.516,1.253 -1.882,2.19 -0.37000002,0.95 -0.17,2.01 -0.166,2.979 0.004,0.718 -0.27300002,1.345 ' + + '-0.055,2.063 0.629,2.087 2.425,3.312 4.859,3.318 4.6179995,0.014 9.2379995,-0.139 13.8569995,-0.158 ' + + '0.755,-0.004 1.171,-0.301 1.182,-1.033 0.012,-0.754 -0.423,-0.969 -1.183,-0.973 -1.778,-0.01 ' + + '-5.824,-0.004 -6.04,-0.004 10e-4,-0.084 0.003,-0.586 10e-4,-0.67 z' + }, + 'TASK_TYPE_INSTANTIATING_SEND': { + d: 'm {mx},{my} l 0,8.4 l 12.6,0 l 0,-8.4 z l 6.3,3.6 l 6.3,-3.6' + }, + 'TASK_TYPE_SERVICE': { + d: 'm {mx},{my} v -1.71335 c 0.352326,-0.0705 0.703932,-0.17838 1.047628,-0.32133 ' + + '0.344416,-0.14465 0.665822,-0.32133 0.966377,-0.52145 l 1.19431,1.18005 1.567487,-1.57688 ' + + '-1.195028,-1.18014 c 0.403376,-0.61394 0.683079,-1.29908 0.825447,-2.01824 l 1.622133,-0.01 ' + + 'v -2.2196 l -1.636514,0.01 c -0.07333,-0.35153 -0.178319,-0.70024 -0.323564,-1.04372 ' + + '-0.145244,-0.34406 -0.321407,-0.6644 -0.522735,-0.96217 l 1.131035,-1.13631 -1.583305,-1.56293 ' + + '-1.129598,1.13589 c -0.614052,-0.40108 -1.302883,-0.68093 -2.022633,-0.82247 l 0.0093,-1.61852 ' + + 'h -2.241173 l 0.0042,1.63124 c -0.353763,0.0736 -0.705369,0.17977 -1.049785,0.32371 -0.344415,0.14437 ' + + '-0.665102,0.32092 -0.9635006,0.52046 l -1.1698628,-1.15823 -1.5667691,1.5792 1.1684265,1.15669 ' + + 'c -0.4026573,0.61283 -0.68308,1.29797 -0.8247287,2.01713 l -1.6588041,0.003 v 2.22174 ' + + 'l 1.6724648,-0.006 c 0.073327,0.35077 0.1797598,0.70243 0.3242851,1.04472 0.1452428,0.34448 ' + + '0.3214064,0.6644 0.5227339,0.96066 l -1.1993431,1.19723 1.5840256,1.56011 1.1964668,-1.19348 ' + + 'c 0.6140517,0.40346 1.3028827,0.68232 2.0233517,0.82331 l 7.19e-4,1.69892 h 2.226848 z ' + + 'm 0.221462,-3.9957 c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 ' + + '0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 ' + + '0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z' + }, + 'TASK_TYPE_SERVICE_FILL': { + d: 'm {mx},{my} c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 ' + + '0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 ' + + '0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z' + }, + 'TASK_TYPE_BUSINESS_RULE_HEADER': { + d: 'm {mx},{my} 0,4 20,0 0,-4 z' + }, + 'TASK_TYPE_BUSINESS_RULE_MAIN': { + d: 'm {mx},{my} 0,12 20,0 0,-12 z' + + 'm 0,8 l 20,0 ' + + 'm -13,-4 l 0,8' + }, + 'MESSAGE_FLOW_MARKER': { + d: 'm {mx},{my} m -10.5 ,-7 l 0,14 l 21,0 l 0,-14 z l 10.5,6 l 10.5,-6' + } + }; + + this.getRawPath = function getRawPath(pathId) { + return this.pathMap[pathId].d; + }; + + /** + * Scales the path to the given height and width. + *

              Use case

              + *

              Use case is to scale the content of elements (event, gateways) based + * on the element bounding box's size. + *

              + *

              Why not transform

              + *

              Scaling a path with transform() will also scale the stroke and IE does not support + * the option 'non-scaling-stroke' to prevent this. + * Also there are use cases where only some parts of a path should be + * scaled.

              + * + * @param {string} pathId The ID of the path. + * @param {Object} param

              + * Example param object scales the path to 60% size of the container (data.width, data.height). + *

              +   *   {
              +   *     xScaleFactor: 0.6,
              +   *     yScaleFactor:0.6,
              +   *     containerWidth: data.width,
              +   *     containerHeight: data.height,
              +   *     position: {
              +   *       mx: 0.46,
              +   *       my: 0.2,
              +   *     }
              +   *   }
              +   *   
              + *
                + *
              • targetpathwidth = xScaleFactor * containerWidth
              • + *
              • targetpathheight = yScaleFactor * containerHeight
              • + *
              • Position is used to set the starting coordinate of the path. M is computed: + *
                  + *
                • position.x * containerWidth
                • + *
                • position.y * containerHeight
                • + *
                + * Center of the container
                 position: {
                +   *       mx: 0.5,
                +   *       my: 0.5,
                +   *     }
                + * Upper left corner of the container + *
                 position: {
                +   *       mx: 0.0,
                +   *       my: 0.0,
                +   *     }
                + *
              • + *
              + *

              + * + */ + this.getScaledPath = function getScaledPath(pathId, param) { + var rawPath = this.pathMap[pathId]; + + // positioning + // compute the start point of the path + var mx, my; + + if (param.abspos) { + mx = param.abspos.x; + my = param.abspos.y; + } else { + mx = param.containerWidth * param.position.mx; + my = param.containerHeight * param.position.my; + } + + var coordinates = {}; // map for the scaled coordinates + if (param.position) { + + // path + var heightRatio = (param.containerHeight / rawPath.height) * param.yScaleFactor; + var widthRatio = (param.containerWidth / rawPath.width) * param.xScaleFactor; + + + // Apply height ratio + for (var heightIndex = 0; heightIndex < rawPath.heightElements.length; heightIndex++) { + coordinates['y' + heightIndex] = rawPath.heightElements[heightIndex] * heightRatio; + } + + // Apply width ratio + for (var widthIndex = 0; widthIndex < rawPath.widthElements.length; widthIndex++) { + coordinates['x' + widthIndex] = rawPath.widthElements[widthIndex] * widthRatio; + } + } + + // Apply value to raw path + var path = format( + rawPath.d, { + mx: mx, + my: my, + e: coordinates + } + ); + return path; + }; +} + +// helpers ////////////////////// + +// copied and adjusted from https://github.com/adobe-webplatform/Snap.svg/blob/master/src/svg.js +var tokenRegex = /\{([^{}]+)\}/g, + objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g; // matches .xxxxx or ["xxxxx"] to run over object properties + +function replacer(all, key, obj) { + var res = obj; + key.replace(objNotationRegex, function(all, name, quote, quotedName, isFunc) { + name = name || quotedName; + if (res) { + if (name in res) { + res = res[name]; + } + typeof res == 'function' && isFunc && (res = res()); + } + }); + res = (res == null || res == obj ? all : res) + ''; + + return res; +} + +function format(str, obj) { + return String(str).replace(tokenRegex, function(all, key) { + return replacer(all, key, obj); + }); +} diff --git a/lib/draw/TextRenderer.js b/lib/draw/TextRenderer.js new file mode 100644 index 0000000..e70b809 --- /dev/null +++ b/lib/draw/TextRenderer.js @@ -0,0 +1,116 @@ +import { assign } from 'min-dash'; + +import TextUtil from 'diagram-js/lib/util/Text'; + +var DEFAULT_FONT_SIZE = 12; +var LINE_HEIGHT_RATIO = 1.2; + +var MIN_TEXT_ANNOTATION_HEIGHT = 30; + + +export default function TextRenderer(config) { + + var defaultStyle = assign({ + fontFamily: 'Arial, sans-serif', + fontSize: DEFAULT_FONT_SIZE, + fontWeight: 'normal', + lineHeight: LINE_HEIGHT_RATIO + }, config && config.defaultStyle || {}); + + var fontSize = parseInt(defaultStyle.fontSize, 10) - 1; + + var externalStyle = assign({}, defaultStyle, { + fontSize: fontSize + }, config && config.externalStyle || {}); + + var textUtil = new TextUtil({ + style: defaultStyle + }); + + /** + * Get the new bounds of an externally rendered, + * layouted label. + * + * @param {Bounds} bounds + * @param {string} text + * + * @return {Bounds} + */ + this.getExternalLabelBounds = function(bounds, text) { + + var layoutedDimensions = textUtil.getDimensions(text, { + box: { + width: 90, + height: 30, + x: bounds.width / 2 + bounds.x, + y: bounds.height / 2 + bounds.y + }, + style: externalStyle + }); + + // resize label shape to fit label text + return { + x: Math.round(bounds.x + bounds.width / 2 - layoutedDimensions.width / 2), + y: Math.round(bounds.y), + width: Math.ceil(layoutedDimensions.width), + height: Math.ceil(layoutedDimensions.height) + }; + + }; + + /** + * Get the new bounds of text annotation. + * + * @param {Bounds} bounds + * @param {string} text + * + * @return {Bounds} + */ + this.getTextAnnotationBounds = function(bounds, text) { + + var layoutedDimensions = textUtil.getDimensions(text, { + box: bounds, + style: defaultStyle, + align: 'left-top', + padding: 5 + }); + + return { + x: bounds.x, + y: bounds.y, + width: bounds.width, + height: Math.max(MIN_TEXT_ANNOTATION_HEIGHT, Math.round(layoutedDimensions.height)) + }; + }; + + /** + * Create a layouted text element. + * + * @param {string} text + * @param {Object} [options] + * + * @return {SVGElement} rendered text + */ + this.createText = function(text, options) { + return textUtil.createText(text, options || {}); + }; + + /** + * Get default text style. + */ + this.getDefaultStyle = function() { + return defaultStyle; + }; + + /** + * Get the external text style. + */ + this.getExternalStyle = function() { + return externalStyle; + }; + +} + +TextRenderer.$inject = [ + 'config.textRenderer' +]; \ No newline at end of file diff --git a/lib/draw/index.js b/lib/draw/index.js new file mode 100644 index 0000000..5902fde --- /dev/null +++ b/lib/draw/index.js @@ -0,0 +1,11 @@ +import BpmnRenderer from './BpmnRenderer'; +import TextRenderer from './TextRenderer'; + +import PathMap from './PathMap'; + +export default { + __init__: [ 'bpmnRenderer' ], + bpmnRenderer: [ 'type', BpmnRenderer ], + textRenderer: [ 'type', TextRenderer ], + pathMap: [ 'type', PathMap ] +}; diff --git a/lib/features/align-elements/AlignElementsContextPadProvider.js b/lib/features/align-elements/AlignElementsContextPadProvider.js new file mode 100644 index 0000000..235d053 --- /dev/null +++ b/lib/features/align-elements/AlignElementsContextPadProvider.js @@ -0,0 +1,87 @@ +import { + assign +} from 'min-dash'; + +import ICONS from './AlignElementsIcons'; + +var LOW_PRIORITY = 900; + +/** + * A provider for align elements context pad button + */ +export default function AlignElementsContextPadProvider(contextPad, popupMenu, translate, canvas) { + + contextPad.registerProvider(LOW_PRIORITY, this); + + this._contextPad = contextPad; + this._popupMenu = popupMenu; + this._translate = translate; + this._canvas = canvas; +} + +AlignElementsContextPadProvider.$inject = [ + 'contextPad', + 'popupMenu', + 'translate', + 'canvas' +]; + +AlignElementsContextPadProvider.prototype.getMultiElementContextPadEntries = function(elements) { + var actions = {}; + + if (this._isAllowed(elements)) { + assign(actions, this._getEntries(elements)); + } + + return actions; +}; + +AlignElementsContextPadProvider.prototype._isAllowed = function(elements) { + return !this._popupMenu.isEmpty(elements, 'align-elements'); +}; + +AlignElementsContextPadProvider.prototype._getEntries = function(elements) { + var self = this; + + return { + 'align-elements': { + group: 'align-elements', + title: self._translate('Align elements'), + imageUrl: ICONS['align'], + action: { + click: function(event, elements) { + var position = self._getMenuPosition(elements); + + assign(position, { + cursor: { + x: event.x, + y: event.y + } + }); + + self._popupMenu.open(elements, 'align-elements', position); + } + } + } + }; +}; + +AlignElementsContextPadProvider.prototype._getMenuPosition = function(elements) { + var Y_OFFSET = 5; + + var diagramContainer = this._canvas.getContainer(), + pad = this._contextPad.getPad(elements).html; + + var diagramRect = diagramContainer.getBoundingClientRect(), + padRect = pad.getBoundingClientRect(); + + var top = padRect.top - diagramRect.top; + var left = padRect.left - diagramRect.left; + + var pos = { + x: left, + y: top + padRect.height + Y_OFFSET + }; + + return pos; +}; diff --git a/lib/features/align-elements/AlignElementsIcons.js b/lib/features/align-elements/AlignElementsIcons.js new file mode 100644 index 0000000..86aeb6a --- /dev/null +++ b/lib/features/align-elements/AlignElementsIcons.js @@ -0,0 +1,15 @@ +/** + * To change the icons, modify the SVGs in `./resources`, execute `npx svgo -f resources --datauri enc -o dist`, + * and then replace respective icons with the optimized data URIs in `./dist`. + */ +var icons = { + align: 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%202000%202000%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M200%20150v1700%22%2F%3E%3Crect%20x%3D%22500%22%20y%3D%22150%22%20width%3D%221300%22%20height%3D%22700%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%22500%22%20y%3D%221150%22%20width%3D%22700%22%20height%3D%22700%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E', + bottom: 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M150%201650h1500%22%2F%3E%3Crect%20x%3D%22150%22%20y%3D%22350%22%20width%3D%22600%22%20height%3D%221300%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%221050%22%20y%3D%22850%22%20width%3D%22600%22%20height%3D%22800%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E', + center: 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M900%20150v1500%22%2F%3E%3Crect%20x%3D%22250%22%20y%3D%22150%22%20width%3D%221300%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%22500%22%20y%3D%221050%22%20width%3D%22800%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E', + left: 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M100%20150v1500%22%2F%3E%3Crect%20x%3D%22100%22%20y%3D%22150%22%20width%3D%221300%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%22100%22%20y%3D%221050%22%20width%3D%22800%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E', + right: 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M1650%20150v1500%22%2F%3E%3Crect%20x%3D%22350%22%20y%3D%22150%22%20width%3D%221300%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%22850%22%20y%3D%221050%22%20width%3D%22800%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E', + top: 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M150%20150h1500%22%2F%3E%3Crect%20x%3D%22150%22%20y%3D%22150%22%20width%3D%22600%22%20height%3D%221300%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%221050%22%20y%3D%22150%22%20width%3D%22600%22%20height%3D%22800%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E', + middle: 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22stroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linecap%3Around%22%20d%3D%22M150%20900h1500%22%2F%3E%3Crect%20x%3D%22150%22%20y%3D%22250%22%20width%3D%22600%22%20height%3D%221300%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%221050%22%20y%3D%22500%22%20width%3D%22600%22%20height%3D%22800%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E' +}; + +export default icons; diff --git a/lib/features/align-elements/AlignElementsMenuProvider.js b/lib/features/align-elements/AlignElementsMenuProvider.js new file mode 100644 index 0000000..61805a7 --- /dev/null +++ b/lib/features/align-elements/AlignElementsMenuProvider.js @@ -0,0 +1,72 @@ +import ICONS from './AlignElementsIcons'; + +import { + assign, + forEach, +} from 'min-dash'; + +var ALIGNMENT_OPTIONS = [ + 'left', + 'center', + 'right', + 'top', + 'middle', + 'bottom' +]; + +/** + * A provider for align elements popup menu. + */ +export default function AlignElementsMenuProvider(popupMenu, alignElements, translate, rules) { + + this._alignElements = alignElements; + this._translate = translate; + this._popupMenu = popupMenu; + this._rules = rules; + + popupMenu.registerProvider('align-elements', this); +} + +AlignElementsMenuProvider.$inject = [ + 'popupMenu', + 'alignElements', + 'translate', + 'rules' +]; + +AlignElementsMenuProvider.prototype.getPopupMenuEntries = function(elements) { + var entries = {}; + + if (this._isAllowed(elements)) { + assign(entries, this._getEntries(elements)); + } + + return entries; +}; + +AlignElementsMenuProvider.prototype._isAllowed = function(elements) { + return this._rules.allowed('elements.align', { elements: elements }); +}; + +AlignElementsMenuProvider.prototype._getEntries = function(elements) { + var alignElements = this._alignElements, + translate = this._translate, + popupMenu = this._popupMenu; + + var entries = {}; + + forEach(ALIGNMENT_OPTIONS, function(alignment) { + entries[ 'align-elements-' + alignment ] = { + group: 'align', + title: translate('Align elements ' + alignment), + className: 'bjs-align-elements-menu-entry', + imageUrl: ICONS[alignment], + action: function(event, entry) { + alignElements.trigger(elements, alignment); + popupMenu.close(); + } + }; + }); + + return entries; +}; diff --git a/lib/features/align-elements/BpmnAlignElements.js b/lib/features/align-elements/BpmnAlignElements.js new file mode 100644 index 0000000..451b806 --- /dev/null +++ b/lib/features/align-elements/BpmnAlignElements.js @@ -0,0 +1,39 @@ +import inherits from 'inherits-browser'; + +import RuleProvider from 'diagram-js/lib/features/rules/RuleProvider'; +import { getParents } from 'diagram-js/lib/util/Elements'; + +import { + filter +} from 'min-dash'; + +/** + * Rule provider for alignment of BPMN elements. + */ +export default function BpmnAlignElements(eventBus) { + RuleProvider.call(this, eventBus); +} + +BpmnAlignElements.$inject = [ 'eventBus' ]; + +inherits(BpmnAlignElements, RuleProvider); + +BpmnAlignElements.prototype.init = function() { + this.addRule('elements.align', function(context) { + var elements = context.elements; + + // filter out elements which cannot be aligned + var filteredElements = filter(elements, function(element) { + return !(element.waypoints || element.host || element.labelTarget); + }); + + // filter out elements which are children of any of the selected elements + filteredElements = getParents(filteredElements); + + if (filteredElements.length < 2) { + return false; + } + + return filteredElements; + }); +}; diff --git a/lib/features/align-elements/index.js b/lib/features/align-elements/index.js new file mode 100644 index 0000000..397e461 --- /dev/null +++ b/lib/features/align-elements/index.js @@ -0,0 +1,23 @@ +import AlignElementsModule from 'diagram-js/lib/features/align-elements'; +import ContextPadModule from 'diagram-js/lib/features/context-pad'; +import PopupMenuModule from 'diagram-js/lib/features/popup-menu'; + +import AlignElementsContextPadProvider from './AlignElementsContextPadProvider'; +import AlignElementsMenuProvider from './AlignElementsMenuProvider'; +import BpmnAlignElements from './BpmnAlignElements'; + +export default { + __depends__: [ + AlignElementsModule, + ContextPadModule, + PopupMenuModule + ], + __init__: [ + 'alignElementsContextPadProvider', + 'alignElementsMenuProvider', + 'bpmnAlignElements' + ], + alignElementsContextPadProvider: [ 'type', AlignElementsContextPadProvider ], + alignElementsMenuProvider: [ 'type', AlignElementsMenuProvider ], + bpmnAlignElements: [ 'type', BpmnAlignElements ] +}; diff --git a/lib/features/align-elements/resources/align-bottom-tool.svg b/lib/features/align-elements/resources/align-bottom-tool.svg new file mode 100644 index 0000000..ff578d9 --- /dev/null +++ b/lib/features/align-elements/resources/align-bottom-tool.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/lib/features/align-elements/resources/align-horizontal-center-tool.svg b/lib/features/align-elements/resources/align-horizontal-center-tool.svg new file mode 100644 index 0000000..699e1a7 --- /dev/null +++ b/lib/features/align-elements/resources/align-horizontal-center-tool.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/lib/features/align-elements/resources/align-left-tool.svg b/lib/features/align-elements/resources/align-left-tool.svg new file mode 100644 index 0000000..236f92a --- /dev/null +++ b/lib/features/align-elements/resources/align-left-tool.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/lib/features/align-elements/resources/align-right-tool.svg b/lib/features/align-elements/resources/align-right-tool.svg new file mode 100644 index 0000000..ac040e7 --- /dev/null +++ b/lib/features/align-elements/resources/align-right-tool.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/lib/features/align-elements/resources/align-tool.svg b/lib/features/align-elements/resources/align-tool.svg new file mode 100644 index 0000000..7dfa51e --- /dev/null +++ b/lib/features/align-elements/resources/align-tool.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/lib/features/align-elements/resources/align-top-tool.svg b/lib/features/align-elements/resources/align-top-tool.svg new file mode 100644 index 0000000..2cdc23d --- /dev/null +++ b/lib/features/align-elements/resources/align-top-tool.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/lib/features/align-elements/resources/align-vertical-center-tool.svg b/lib/features/align-elements/resources/align-vertical-center-tool.svg new file mode 100644 index 0000000..3b56063 --- /dev/null +++ b/lib/features/align-elements/resources/align-vertical-center-tool.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/lib/features/auto-place/BpmnAutoPlace.js b/lib/features/auto-place/BpmnAutoPlace.js new file mode 100644 index 0000000..cd2e117 --- /dev/null +++ b/lib/features/auto-place/BpmnAutoPlace.js @@ -0,0 +1,18 @@ +import { getNewShapePosition } from './BpmnAutoPlaceUtil'; + + +/** + * BPMN auto-place behavior. + * + * @param {EventBus} eventBus + */ +export default function AutoPlace(eventBus) { + eventBus.on('autoPlace', function(context) { + var shape = context.shape, + source = context.source; + + return getNewShapePosition(source, shape); + }); +} + +AutoPlace.$inject = [ 'eventBus' ]; \ No newline at end of file diff --git a/lib/features/auto-place/BpmnAutoPlaceUtil.js b/lib/features/auto-place/BpmnAutoPlaceUtil.js new file mode 100644 index 0000000..c815eee --- /dev/null +++ b/lib/features/auto-place/BpmnAutoPlaceUtil.js @@ -0,0 +1,148 @@ +import { is } from '../../util/ModelUtil'; +import { isAny } from '../modeling/util/ModelingUtil'; + +import { + getMid, + asTRBL, + getOrientation +} from 'diagram-js/lib/layout/LayoutUtil'; + +import { + findFreePosition, + generateGetNextPosition, + getConnectedDistance +} from 'diagram-js/lib/features/auto-place/AutoPlaceUtil'; + + +/** + * Find the new position for the target element to + * connect to source. + * + * @param {djs.model.Shape} source + * @param {djs.model.Shape} element + * + * @return {Point} + */ +export function getNewShapePosition(source, element) { + + if (is(element, 'bpmn:TextAnnotation')) { + return getTextAnnotationPosition(source, element); + } + + if (isAny(element, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ])) { + return getDataElementPosition(source, element); + } + + if (is(element, 'bpmn:FlowNode')) { + return getFlowNodePosition(source, element); + } +} + +/** + * Always try to place element right of source; + * compute actual distance from previous nodes in flow. + */ +export function getFlowNodePosition(source, element) { + + var sourceTrbl = asTRBL(source); + var sourceMid = getMid(source); + + var horizontalDistance = getConnectedDistance(source, { + filter: function(connection) { + return is(connection, 'bpmn:SequenceFlow'); + } + }); + + var margin = 30, + minDistance = 80, + orientation = 'left'; + + if (is(source, 'bpmn:BoundaryEvent')) { + orientation = getOrientation(source, source.host, -25); + + if (orientation.indexOf('top') !== -1) { + margin *= -1; + } + } + + var position = { + x: sourceTrbl.right + horizontalDistance + element.width / 2, + y: sourceMid.y + getVerticalDistance(orientation, minDistance) + }; + + var nextPositionDirection = { + y: { + margin: margin, + minDistance: minDistance + } + }; + + return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection)); +} + + +function getVerticalDistance(orientation, minDistance) { + if (orientation.indexOf('top') != -1) { + return -1 * minDistance; + } else if (orientation.indexOf('bottom') != -1) { + return minDistance; + } else { + return 0; + } +} + + +/** + * Always try to place text annotations top right of source. + */ +export function getTextAnnotationPosition(source, element) { + + var sourceTrbl = asTRBL(source); + + var position = { + x: sourceTrbl.right + element.width / 2, + y: sourceTrbl.top - 50 - element.height / 2 + }; + + if (isConnection(source)) { + position = getMid(source); + position.x += 100; + position.y -= 50; + } + + var nextPositionDirection = { + y: { + margin: -30, + minDistance: 20 + } + }; + + return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection)); +} + + +/** + * Always put element bottom right of source. + */ +export function getDataElementPosition(source, element) { + + var sourceTrbl = asTRBL(source); + + var position = { + x: sourceTrbl.right - 10 + element.width / 2, + y: sourceTrbl.bottom + 40 + element.width / 2 + }; + + var nextPositionDirection = { + x: { + margin: 30, + minDistance: 30 + } + }; + + return findFreePosition(source, element, position, generateGetNextPosition(nextPositionDirection)); +} + +function isConnection(element) { + return !!element.waypoints; +} \ No newline at end of file diff --git a/lib/features/auto-place/index.js b/lib/features/auto-place/index.js new file mode 100644 index 0000000..4751650 --- /dev/null +++ b/lib/features/auto-place/index.js @@ -0,0 +1,9 @@ +import AutoPlaceModule from 'diagram-js/lib/features/auto-place'; + +import BpmnAutoPlace from './BpmnAutoPlace'; + +export default { + __depends__: [ AutoPlaceModule ], + __init__: [ 'bpmnAutoPlace' ], + bpmnAutoPlace: [ 'type', BpmnAutoPlace ] +}; \ No newline at end of file diff --git a/lib/features/auto-resize/BpmnAutoResize.js b/lib/features/auto-resize/BpmnAutoResize.js new file mode 100644 index 0000000..fce4c5f --- /dev/null +++ b/lib/features/auto-resize/BpmnAutoResize.js @@ -0,0 +1,38 @@ +import AutoResize from 'diagram-js/lib/features/auto-resize/AutoResize'; + +import inherits from 'inherits-browser'; + +import { is } from '../../util/ModelUtil'; + + +/** + * Sub class of the AutoResize module which implements a BPMN + * specific resize function. + */ +export default function BpmnAutoResize(injector) { + + injector.invoke(AutoResize, this); +} + +BpmnAutoResize.$inject = [ + 'injector' +]; + +inherits(BpmnAutoResize, AutoResize); + + +/** + * Resize shapes and lanes. + * + * @param {djs.model.Shape} target + * @param {Bounds} newBounds + * @param {Object} hints + */ +BpmnAutoResize.prototype.resize = function(target, newBounds, hints) { + + if (is(target, 'bpmn:Participant')) { + this._modeling.resizeLane(target, newBounds, null, hints); + } else { + this._modeling.resizeShape(target, newBounds, null, hints); + } +}; \ No newline at end of file diff --git a/lib/features/auto-resize/BpmnAutoResizeProvider.js b/lib/features/auto-resize/BpmnAutoResizeProvider.js new file mode 100644 index 0000000..7f23105 --- /dev/null +++ b/lib/features/auto-resize/BpmnAutoResizeProvider.js @@ -0,0 +1,57 @@ +import { is } from '../../util/ModelUtil'; + +import inherits from 'inherits-browser'; + +import { forEach } from 'min-dash'; + +import AutoResizeProvider from 'diagram-js/lib/features/auto-resize/AutoResizeProvider'; + + +/** + * This module is a provider for automatically resizing parent BPMN elements + */ +export default function BpmnAutoResizeProvider(eventBus, modeling) { + AutoResizeProvider.call(this, eventBus); + + this._modeling = modeling; +} + +inherits(BpmnAutoResizeProvider, AutoResizeProvider); + +BpmnAutoResizeProvider.$inject = [ + 'eventBus', + 'modeling' +]; + + +/** + * Check if the given target can be expanded + * + * @param {djs.model.Shape} target + * + * @return {boolean} + */ +BpmnAutoResizeProvider.prototype.canResize = function(elements, target) { + + // do not resize plane elements: + // root elements, collapsed sub-processes + if (is(target.di, 'bpmndi:BPMNPlane')) { + return false; + } + + if (!is(target, 'bpmn:Participant') && !is(target, 'bpmn:Lane') && !(is(target, 'bpmn:SubProcess'))) { + return false; + } + + var canResize = true; + + forEach(elements, function(element) { + + if (is(element, 'bpmn:Lane') || element.labelTarget) { + canResize = false; + return; + } + }); + + return canResize; +}; diff --git a/lib/features/auto-resize/index.js b/lib/features/auto-resize/index.js new file mode 100644 index 0000000..2289ddc --- /dev/null +++ b/lib/features/auto-resize/index.js @@ -0,0 +1,12 @@ +import BpmnAutoResize from './BpmnAutoResize'; +import BpmnAutoResizeProvider from './BpmnAutoResizeProvider'; + + +export default { + __init__: [ + 'bpmnAutoResize', + 'bpmnAutoResizeProvider' + ], + bpmnAutoResize: [ 'type', BpmnAutoResize ], + bpmnAutoResizeProvider: [ 'type', BpmnAutoResizeProvider ] +}; diff --git a/lib/features/context-pad/ContextPadProvider.js b/lib/features/context-pad/ContextPadProvider.js new file mode 100644 index 0000000..f055f54 --- /dev/null +++ b/lib/features/context-pad/ContextPadProvider.js @@ -0,0 +1,526 @@ +import { + assign, + forEach, + isArray, + every +} from 'min-dash'; + +import { + is +} from '../../util/ModelUtil'; + +import { + isExpanded, + isEventSubProcess +} from '../../util/DiUtil'; + +import { + isAny +} from '../modeling/util/ModelingUtil'; + +import { + getChildLanes +} from '../modeling/util/LaneUtil'; + +import { + hasPrimaryModifier +} from 'diagram-js/lib/util/Mouse'; + + +/** + * A provider for BPMN 2.0 elements context pad + */ +export default function ContextPadProvider( + config, injector, eventBus, + contextPad, modeling, elementFactory, + connect, create, popupMenu, + canvas, rules, translate) { + + config = config || {}; + + contextPad.registerProvider(this); + + this._contextPad = contextPad; + + this._modeling = modeling; + + this._elementFactory = elementFactory; + this._connect = connect; + this._create = create; + this._popupMenu = popupMenu; + this._canvas = canvas; + this._rules = rules; + this._translate = translate; + + if (config.autoPlace !== false) { + this._autoPlace = injector.get('autoPlace', false); + } + + eventBus.on('create.end', 250, function(event) { + var context = event.context, + shape = context.shape; + + if (!hasPrimaryModifier(event) || !contextPad.isOpen(shape)) { + return; + } + + var entries = contextPad.getEntries(shape); + + if (entries.replace) { + entries.replace.action.click(event, shape); + } + }); +} + +ContextPadProvider.$inject = [ + 'config.contextPad', + 'injector', + 'eventBus', + 'contextPad', + 'modeling', + 'elementFactory', + 'connect', + 'create', + 'popupMenu', + 'canvas', + 'rules', + 'translate' +]; + +ContextPadProvider.prototype.getMultiElementContextPadEntries = function(elements) { + var modeling = this._modeling; + + var actions = {}; + + if (this._isDeleteAllowed(elements)) { + assign(actions, { + 'delete': { + group: 'edit', + className: 'bpmn-icon-trash', + title: this._translate('Remove'), + action: { + click: function(event, elements) { + modeling.removeElements(elements.slice()); + } + } + } + }); + } + + return actions; +}; + +/** + * @param {djs.model.Base[]} elements + * @return {boolean} + */ +ContextPadProvider.prototype._isDeleteAllowed = function(elements) { + + var baseAllowed = this._rules.allowed('elements.delete', { + elements: elements + }); + + if (isArray(baseAllowed)) { + return every(baseAllowed, function(element) { + return includes(baseAllowed, element); + }); + } + + return baseAllowed; +}; + +ContextPadProvider.prototype.getContextPadEntries = function(element) { + var contextPad = this._contextPad, + modeling = this._modeling, + + elementFactory = this._elementFactory, + connect = this._connect, + create = this._create, + popupMenu = this._popupMenu, + canvas = this._canvas, + rules = this._rules, + autoPlace = this._autoPlace, + translate = this._translate; + + var actions = {}; + + if (element.type === 'label') { + return actions; + } + + var businessObject = element.businessObject; + + function startConnect(event, element) { + connect.start(event, element); + } + + function removeElement(e, element) { + modeling.removeElements([ element ]); + } + + function getReplaceMenuPosition(element) { + + var Y_OFFSET = 5; + + var diagramContainer = canvas.getContainer(), + pad = contextPad.getPad(element).html; + + var diagramRect = diagramContainer.getBoundingClientRect(), + padRect = pad.getBoundingClientRect(); + + var top = padRect.top - diagramRect.top; + var left = padRect.left - diagramRect.left; + + var pos = { + x: left, + y: top + padRect.height + Y_OFFSET + }; + + return pos; + } + + + /** + * Create an append action + * + * @param {string} type + * @param {string} className + * @param {string} [title] + * @param {Object} [options] + * + * @return {Object} descriptor + */ + function appendAction(type, className, title, options) { + + if (typeof title !== 'string') { + options = title; + title = translate('Append {type}', { type: type.replace(/^bpmn:/, '') }); + } + + function appendStart(event, element) { + + var shape = elementFactory.createShape(assign({ type: type }, options)); + create.start(event, shape, { + source: element + }); + } + + + var append = autoPlace ? function(event, element) { + var shape = elementFactory.createShape(assign({ type: type }, options)); + + autoPlace.append(element, shape); + } : appendStart; + + + return { + group: 'model', + className: className, + title: title, + action: { + dragstart: appendStart, + click: append + } + }; + } + + function splitLaneHandler(count) { + + return function(event, element) { + + // actual split + modeling.splitLane(element, count); + + // refresh context pad after split to + // get rid of split icons + contextPad.open(element, true); + }; + } + + + if (isAny(businessObject, [ 'bpmn:Lane', 'bpmn:Participant' ]) && isExpanded(element)) { + + var childLanes = getChildLanes(element); + + assign(actions, { + 'lane-insert-above': { + group: 'lane-insert-above', + className: 'bpmn-icon-lane-insert-above', + title: translate('Add Lane above'), + action: { + click: function(event, element) { + modeling.addLane(element, 'top'); + } + } + } + }); + + if (childLanes.length < 2) { + + if (element.height >= 120) { + assign(actions, { + 'lane-divide-two': { + group: 'lane-divide', + className: 'bpmn-icon-lane-divide-two', + title: translate('Divide into two Lanes'), + action: { + click: splitLaneHandler(2) + } + } + }); + } + + if (element.height >= 180) { + assign(actions, { + 'lane-divide-three': { + group: 'lane-divide', + className: 'bpmn-icon-lane-divide-three', + title: translate('Divide into three Lanes'), + action: { + click: splitLaneHandler(3) + } + } + }); + } + } + + assign(actions, { + 'lane-insert-below': { + group: 'lane-insert-below', + className: 'bpmn-icon-lane-insert-below', + title: translate('Add Lane below'), + action: { + click: function(event, element) { + modeling.addLane(element, 'bottom'); + } + } + } + }); + + } + + if (is(businessObject, 'bpmn:FlowNode')) { + + if (is(businessObject, 'bpmn:EventBasedGateway')) { + + assign(actions, { + 'append.receive-task': appendAction( + 'bpmn:ReceiveTask', + 'bpmn-icon-receive-task', + translate('Append ReceiveTask') + ), + 'append.message-intermediate-event': appendAction( + 'bpmn:IntermediateCatchEvent', + 'bpmn-icon-intermediate-event-catch-message', + translate('Append MessageIntermediateCatchEvent'), + { eventDefinitionType: 'bpmn:MessageEventDefinition' } + ), + 'append.timer-intermediate-event': appendAction( + 'bpmn:IntermediateCatchEvent', + 'bpmn-icon-intermediate-event-catch-timer', + translate('Append TimerIntermediateCatchEvent'), + { eventDefinitionType: 'bpmn:TimerEventDefinition' } + ), + 'append.condition-intermediate-event': appendAction( + 'bpmn:IntermediateCatchEvent', + 'bpmn-icon-intermediate-event-catch-condition', + translate('Append ConditionIntermediateCatchEvent'), + { eventDefinitionType: 'bpmn:ConditionalEventDefinition' } + ), + 'append.signal-intermediate-event': appendAction( + 'bpmn:IntermediateCatchEvent', + 'bpmn-icon-intermediate-event-catch-signal', + translate('Append SignalIntermediateCatchEvent'), + { eventDefinitionType: 'bpmn:SignalEventDefinition' } + ) + }); + } else + + if (isEventType(businessObject, 'bpmn:BoundaryEvent', 'bpmn:CompensateEventDefinition')) { + + assign(actions, { + 'append.compensation-activity': + appendAction( + 'bpmn:Task', + 'bpmn-icon-task', + translate('Append compensation activity'), + { + isForCompensation: true + } + ) + }); + } else + + if (!is(businessObject, 'bpmn:EndEvent') && + !businessObject.isForCompensation && + !isEventType(businessObject, 'bpmn:IntermediateThrowEvent', 'bpmn:LinkEventDefinition') && + !isEventSubProcess(businessObject)) { + + assign(actions, { + 'append.end-event': appendAction( + 'bpmn:EndEvent', + 'bpmn-icon-end-event-none', + translate('Append EndEvent') + ), + 'append.gateway': appendAction( + 'bpmn:ExclusiveGateway', + 'bpmn-icon-gateway-none', + translate('Append Gateway') + ), + 'append.append-task': appendAction( + 'bpmn:Task', + 'bpmn-icon-task', + translate('Append Task') + ), + 'append.intermediate-event': appendAction( + 'bpmn:IntermediateThrowEvent', + 'bpmn-icon-intermediate-event-none', + translate('Append Intermediate/Boundary Event') + ) + }); + } + } + + if (!popupMenu.isEmpty(element, 'bpmn-replace')) { + + // Replace menu entry + assign(actions, { + 'replace': { + group: 'edit', + className: 'bpmn-icon-screw-wrench', + title: translate('Change type'), + action: { + click: function(event, element) { + + var position = assign(getReplaceMenuPosition(element), { + cursor: { x: event.x, y: event.y } + }); + + popupMenu.open(element, 'bpmn-replace', position); + } + } + } + }); + } + + if (is(businessObject, 'bpmn:SequenceFlow')) { + assign(actions, { + 'append.text-annotation': appendAction( + 'bpmn:TextAnnotation', + 'bpmn-icon-text-annotation' + ) + }); + } + + if ( + isAny(businessObject, [ + 'bpmn:FlowNode', + 'bpmn:InteractionNode', + 'bpmn:DataObjectReference', + 'bpmn:DataStoreReference', + ]) + ) { + assign(actions, { + 'append.text-annotation': appendAction( + 'bpmn:TextAnnotation', + 'bpmn-icon-text-annotation' + ), + + 'connect': { + group: 'connect', + className: 'bpmn-icon-connection-multi', + title: translate( + 'Connect using ' + + (businessObject.isForCompensation + ? '' + : 'Sequence/MessageFlow or ') + + 'Association' + ), + action: { + click: startConnect, + dragstart: startConnect, + }, + }, + }); + } + + if (is(businessObject, 'bpmn:TextAnnotation')) { + assign(actions, { + 'connect': { + group: 'connect', + className: 'bpmn-icon-connection-multi', + title: translate('Connect using Association'), + action: { + click: startConnect, + dragstart: startConnect, + }, + }, + }); + } + + if (isAny(businessObject, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ])) { + assign(actions, { + 'connect': { + group: 'connect', + className: 'bpmn-icon-connection-multi', + title: translate('Connect using DataInputAssociation'), + action: { + click: startConnect, + dragstart: startConnect + } + } + }); + } + + if (is(businessObject, 'bpmn:Group')) { + assign(actions, { + 'append.text-annotation': appendAction('bpmn:TextAnnotation', 'bpmn-icon-text-annotation') + }); + } + + // delete element entry, only show if allowed by rules + var deleteAllowed = rules.allowed('elements.delete', { elements: [ element ] }); + + if (isArray(deleteAllowed)) { + + // was the element returned as a deletion candidate? + deleteAllowed = deleteAllowed[0] === element; + } + + if (deleteAllowed) { + assign(actions, { + 'delete': { + group: 'edit', + className: 'bpmn-icon-trash', + title: translate('Remove'), + action: { + click: removeElement + } + } + }); + } + + return actions; +}; + + +// helpers ///////// + +function isEventType(eventBo, type, definition) { + + var isType = eventBo.$instanceOf(type); + var isDefinition = false; + + var definitions = eventBo.eventDefinitions || []; + forEach(definitions, function(def) { + if (def.$type === definition) { + isDefinition = true; + } + }); + + return isType && isDefinition; +} + +function includes(array, item) { + return array.indexOf(item) !== -1; +} \ No newline at end of file diff --git a/lib/features/context-pad/index.js b/lib/features/context-pad/index.js new file mode 100644 index 0000000..44752b4 --- /dev/null +++ b/lib/features/context-pad/index.js @@ -0,0 +1,21 @@ +import DirectEditingModule from 'diagram-js-direct-editing'; +import ContextPadModule from 'diagram-js/lib/features/context-pad'; +import SelectionModule from 'diagram-js/lib/features/selection'; +import ConnectModule from 'diagram-js/lib/features/connect'; +import CreateModule from 'diagram-js/lib/features/create'; +import PopupMenuModule from '../popup-menu'; + +import ContextPadProvider from './ContextPadProvider'; + +export default { + __depends__: [ + DirectEditingModule, + ContextPadModule, + SelectionModule, + ConnectModule, + CreateModule, + PopupMenuModule + ], + __init__: [ 'contextPadProvider' ], + contextPadProvider: [ 'type', ContextPadProvider ] +}; \ No newline at end of file diff --git a/lib/features/copy-paste/BpmnCopyPaste.js b/lib/features/copy-paste/BpmnCopyPaste.js new file mode 100644 index 0000000..3bd6cba --- /dev/null +++ b/lib/features/copy-paste/BpmnCopyPaste.js @@ -0,0 +1,185 @@ +import { + getBusinessObject, + getDi, + is +} from '../../util/ModelUtil'; + +import { + forEach, + isArray, + isUndefined, + omit, + reduce +} from 'min-dash'; + +function copyProperties(source, target, properties) { + if (!isArray(properties)) { + properties = [ properties ]; + } + + forEach(properties, function(property) { + if (!isUndefined(source[property])) { + target[property] = source[property]; + } + }); +} + +var LOW_PRIORITY = 750; + + +export default function BpmnCopyPaste(bpmnFactory, eventBus, moddleCopy) { + + function copy(bo, clone) { + var targetBo = bpmnFactory.create(bo.$type); + + return moddleCopy.copyElement(bo, targetBo, null, clone); + } + + eventBus.on('copyPaste.copyElement', LOW_PRIORITY, function(context) { + var descriptor = context.descriptor, + element = context.element, + businessObject = getBusinessObject(element); + + // do not copy business object + di for labels; + // will be pulled from the referenced label target + if (isLabel(element)) { + return descriptor; + } + + var businessObjectCopy = descriptor.businessObject = copy(businessObject, true); + var diCopy = descriptor.di = copy(getDi(element), true); + diCopy.bpmnElement = businessObjectCopy; + + copyProperties(businessObjectCopy, descriptor, 'name'); + copyProperties(diCopy, descriptor, 'isExpanded'); + + // default sequence flow + if (businessObject.default) { + descriptor.default = businessObject.default.id; + } + }); + + var referencesKey = '-bpmn-js-refs'; + + function getReferences(cache) { + return (cache[referencesKey] = cache[referencesKey] || {}); + } + + function setReferences(cache, references) { + cache[referencesKey] = references; + } + + function resolveReferences(descriptor, cache, references) { + var businessObject = getBusinessObject(descriptor); + + // default sequence flows + if (descriptor.default) { + + // relationship cannot be resolved immediately + references[ descriptor.default ] = { + element: businessObject, + property: 'default' + }; + } + + // boundary events + if (descriptor.host) { + + // relationship can be resolved immediately + getBusinessObject(descriptor).attachedToRef = getBusinessObject(cache[ descriptor.host ]); + } + + return omit(references, reduce(references, function(array, reference, key) { + var element = reference.element, + property = reference.property; + + if (key === descriptor.id) { + element[ property ] = businessObject; + + array.push(descriptor.id); + } + + return array; + }, [])); + } + + eventBus.on('copyPaste.pasteElement', function(context) { + var cache = context.cache, + descriptor = context.descriptor, + businessObject = descriptor.businessObject, + di = descriptor.di; + + // wire existing di + businessObject for external label + if (isLabel(descriptor)) { + descriptor.businessObject = getBusinessObject(cache[ descriptor.labelTarget ]); + descriptor.di = getDi(cache[ descriptor.labelTarget ]); + + return; + } + + businessObject = descriptor.businessObject = copy(businessObject); + + di = descriptor.di = copy(di); + di.bpmnElement = businessObject; + + copyProperties(descriptor, businessObject, [ + 'isExpanded', + 'name' + ]); + + descriptor.type = businessObject.$type; + }); + + // copy + paste processRef with participant + + eventBus.on('copyPaste.copyElement', LOW_PRIORITY, function(context) { + var descriptor = context.descriptor, + element = context.element; + + if (!is(element, 'bpmn:Participant')) { + return; + } + + var participantBo = getBusinessObject(element); + + if (participantBo.processRef) { + descriptor.processRef = copy(participantBo.processRef, true); + } + }); + + eventBus.on('copyPaste.pasteElement', function(context) { + var descriptor = context.descriptor, + processRef = descriptor.processRef; + + if (processRef) { + descriptor.processRef = copy(processRef); + } + }); + + // resolve references + + eventBus.on('copyPaste.pasteElement', LOW_PRIORITY, function(context) { + var cache = context.cache, + descriptor = context.descriptor; + + // resolve references e.g. default sequence flow + setReferences( + cache, + resolveReferences(descriptor, cache, getReferences(cache)) + ); + }); + +} + + +BpmnCopyPaste.$inject = [ + 'bpmnFactory', + 'eventBus', + 'moddleCopy' +]; + +// helpers ////////// + +function isLabel(element) { + return !!element.labelTarget; +} diff --git a/lib/features/copy-paste/ModdleCopy.js b/lib/features/copy-paste/ModdleCopy.js new file mode 100644 index 0000000..e310498 --- /dev/null +++ b/lib/features/copy-paste/ModdleCopy.js @@ -0,0 +1,304 @@ +import { + find, + forEach, + isArray, + isDefined, + isObject, + matchPattern, + reduce, + has, + sortBy +} from 'min-dash'; + +var DISALLOWED_PROPERTIES = [ + 'artifacts', + 'dataInputAssociations', + 'dataOutputAssociations', + 'default', + 'flowElements', + 'lanes', + 'incoming', + 'outgoing', + 'categoryValue' +]; + +/** + * @typedef {Function} listener + * + * @param {Object} context + * @param {Array} context.propertyNames + * @param {ModdleElement} context.sourceElement + * @param {ModdleElement} context.targetElement + * + * @returns {Array|boolean} - Return properties to be copied or false to disallow + * copying. + */ + +/** + * @typedef {Function} listener + * + * @param {Object} context + * @param {ModdleElement} context.parent + * @param {*} context.property + * @param {string} context.propertyName + * + * @returns {*|boolean} - Return copied property or false to disallow + * copying. + */ + +/** + * @typedef {Function} listener + * + * @param {Object} context + * @param {ModdleElement} context.parent + * @param {*} context.property + * @param {string} context.propertyName + * + * @returns {boolean} - Return false to disallow + * setting copied property. + */ + +/** + * Utility for copying model properties from source element to target element. + * + * @param {EventBus} eventBus + * @param {BpmnFactory} bpmnFactory + * @param {BpmnModdle} moddle + */ +export default function ModdleCopy(eventBus, bpmnFactory, moddle) { + this._bpmnFactory = bpmnFactory; + this._eventBus = eventBus; + this._moddle = moddle; + + // copy extension elements last + eventBus.on('moddleCopy.canCopyProperties', function(context) { + var propertyNames = context.propertyNames; + + if (!propertyNames || !propertyNames.length) { + return; + } + + return sortBy(propertyNames, function(propertyName) { + return propertyName === 'extensionElements'; + }); + }); + + // default check whether property can be copied + eventBus.on('moddleCopy.canCopyProperty', function(context) { + var parent = context.parent, + parentDescriptor = isObject(parent) && parent.$descriptor, + propertyName = context.propertyName; + + if (propertyName && DISALLOWED_PROPERTIES.indexOf(propertyName) !== -1) { + + // disallow copying property + return false; + } + + if (propertyName && + parentDescriptor && + !find(parentDescriptor.properties, matchPattern({ name: propertyName }))) { + + // disallow copying property + return false; + } + }); + + // do NOT allow to copy empty extension elements + eventBus.on('moddleCopy.canSetCopiedProperty', function(context) { + var property = context.property; + + if (is(property, 'bpmn:ExtensionElements') && (!property.values || !property.values.length)) { + + // disallow setting copied property + return false; + } + }); +} + +ModdleCopy.$inject = [ + 'eventBus', + 'bpmnFactory', + 'moddle' +]; + +/** + * Copy model properties of source element to target element. + * + * @param {ModdleElement} sourceElement + * @param {ModdleElement} targetElement + * @param {Array} [propertyNames] + * @param {boolean} clone + * + * @param {ModdleElement} + */ +ModdleCopy.prototype.copyElement = function(sourceElement, targetElement, propertyNames, clone) { + var self = this; + + if (propertyNames && !isArray(propertyNames)) { + propertyNames = [ propertyNames ]; + } + + propertyNames = propertyNames || getPropertyNames(sourceElement.$descriptor); + + var canCopyProperties = this._eventBus.fire('moddleCopy.canCopyProperties', { + propertyNames: propertyNames, + sourceElement: sourceElement, + targetElement: targetElement, + clone: clone + }); + + if (canCopyProperties === false) { + return targetElement; + } + + if (isArray(canCopyProperties)) { + propertyNames = canCopyProperties; + } + + // copy properties + forEach(propertyNames, function(propertyName) { + var sourceProperty; + + if (has(sourceElement, propertyName)) { + sourceProperty = sourceElement.get(propertyName); + } + + var copiedProperty = self.copyProperty(sourceProperty, targetElement, propertyName, clone); + + if (!isDefined(copiedProperty)) { + return; + } + + var canSetProperty = self._eventBus.fire('moddleCopy.canSetCopiedProperty', { + parent: targetElement, + property: copiedProperty, + propertyName: propertyName + }); + + if (canSetProperty === false) { + return; + } + + // TODO(nikku): unclaim old IDs if ID property is copied over + // this._moddle.getPropertyDescriptor(parent, propertyName) + targetElement.set(propertyName, copiedProperty); + }); + + return targetElement; +}; + +/** + * Copy model property. + * + * @param {*} property + * @param {ModdleElement} parent + * @param {string} propertyName + * @param {boolean} clone + * + * @returns {*} + */ +ModdleCopy.prototype.copyProperty = function(property, parent, propertyName, clone) { + var self = this; + + // allow others to copy property + var copiedProperty = this._eventBus.fire('moddleCopy.canCopyProperty', { + parent: parent, + property: property, + propertyName: propertyName, + clone: clone + }); + + // return if copying is NOT allowed + if (copiedProperty === false) { + return; + } + + if (copiedProperty) { + if (isObject(copiedProperty) && copiedProperty.$type && !copiedProperty.$parent) { + copiedProperty.$parent = parent; + } + + return copiedProperty; + } + + var propertyDescriptor = this._moddle.getPropertyDescriptor(parent, propertyName); + + // do NOT copy references + if (propertyDescriptor.isReference) { + return; + } + + // copy id + if (propertyDescriptor.isId) { + return property && this._copyId(property, parent, clone); + } + + // copy arrays + if (isArray(property)) { + return reduce(property, function(childProperties, childProperty) { + + // recursion + copiedProperty = self.copyProperty(childProperty, parent, propertyName, clone); + + // copying might NOT be allowed + if (copiedProperty) { + return childProperties.concat(copiedProperty); + } + + return childProperties; + }, []); + } + + // copy model elements + if (isObject(property) && property.$type) { + if (this._moddle.getElementDescriptor(property).isGeneric) { + return; + } + + copiedProperty = self._bpmnFactory.create(property.$type); + + copiedProperty.$parent = parent; + + // recursion + copiedProperty = self.copyElement(property, copiedProperty, null, clone); + + return copiedProperty; + } + + // copy primitive properties + return property; +}; + +ModdleCopy.prototype._copyId = function(id, element, clone) { + + if (clone) { + return id; + } + + // disallow if already taken + if (this._moddle.ids.assigned(id)) { + return; + } else { + + this._moddle.ids.claim(id, element); + return id; + } +}; + +// helpers ////////// + +export function getPropertyNames(descriptor, keepDefaultProperties) { + return reduce(descriptor.properties, function(properties, property) { + + if (keepDefaultProperties && property.default) { + return properties; + } + + return properties.concat(property.name); + }, []); +} + +function is(element, type) { + return element && (typeof element.$instanceOf === 'function') && element.$instanceOf(type); +} \ No newline at end of file diff --git a/lib/features/copy-paste/index.js b/lib/features/copy-paste/index.js new file mode 100644 index 0000000..18eba49 --- /dev/null +++ b/lib/features/copy-paste/index.js @@ -0,0 +1,13 @@ +import CopyPasteModule from 'diagram-js/lib/features/copy-paste'; + +import BpmnCopyPaste from './BpmnCopyPaste'; +import ModdleCopy from './ModdleCopy'; + +export default { + __depends__: [ + CopyPasteModule + ], + __init__: [ 'bpmnCopyPaste', 'moddleCopy' ], + bpmnCopyPaste: [ 'type', BpmnCopyPaste ], + moddleCopy: [ 'type', ModdleCopy ] +}; diff --git a/lib/features/di-ordering/BpmnDiOrdering.js b/lib/features/di-ordering/BpmnDiOrdering.js new file mode 100644 index 0000000..9b345a1 --- /dev/null +++ b/lib/features/di-ordering/BpmnDiOrdering.js @@ -0,0 +1,40 @@ +import { getDi } from '../../util/ModelUtil'; + +import { + filter, + forEach, + map +} from 'min-dash'; + +import { selfAndAllChildren } from 'diagram-js/lib/util/Elements'; + + +var HIGH_PRIORITY = 2000; + +export default function BpmnDiOrdering(eventBus, canvas) { + + eventBus.on('saveXML.start', HIGH_PRIORITY, orderDi); + + function orderDi() { + var rootElements = canvas.getRootElements(); + + forEach(rootElements, function(root) { + var rootDi = getDi(root), + elements, + diElements; + + elements = selfAndAllChildren([ root ], false); + + // only bpmndi:Shape and bpmndi:Edge can be direct children of bpmndi:Plane + elements = filter(elements, function(element) { + return element !== root && !element.labelTarget; + }); + + diElements = map(elements, getDi); + + rootDi.set('planeElement', diElements); + }); + } +} + +BpmnDiOrdering.$inject = [ 'eventBus', 'canvas' ]; diff --git a/lib/features/di-ordering/index.js b/lib/features/di-ordering/index.js new file mode 100644 index 0000000..52bcbd0 --- /dev/null +++ b/lib/features/di-ordering/index.js @@ -0,0 +1,8 @@ +import BpmnDiOrdering from '../di-ordering/BpmnDiOrdering'; + +export default { + __init__: [ + 'bpmnDiOrdering' + ], + bpmnDiOrdering: [ 'type', BpmnDiOrdering ] +}; \ No newline at end of file diff --git a/lib/features/distribute-elements/BpmnDistributeElements.js b/lib/features/distribute-elements/BpmnDistributeElements.js new file mode 100644 index 0000000..4e641c4 --- /dev/null +++ b/lib/features/distribute-elements/BpmnDistributeElements.js @@ -0,0 +1,55 @@ +import inherits from 'inherits-browser'; + +import RuleProvider from 'diagram-js/lib/features/rules/RuleProvider'; +import { getParents } from 'diagram-js/lib/util/Elements'; + +import { + filter +} from 'min-dash'; + +import { + isAny +} from '../modeling/util/ModelingUtil'; + + +/** + * Registers element exclude filters for elements that + * currently do not support distribution. + */ +export default function BpmnDistributeElements(distributeElements, eventBus, rules) { + RuleProvider.call(this, eventBus); +} + +BpmnDistributeElements.$inject = [ 'distributeElements', 'eventBus', 'rules' ]; + +inherits(BpmnDistributeElements, RuleProvider); + +BpmnDistributeElements.prototype.init = function() { + this.addRule('elements.distribute', function(context) { + var elements = context.elements; + + elements = filter(elements, function(element) { + var cannotDistribute = isAny(element, [ + 'bpmn:Association', + 'bpmn:BoundaryEvent', + 'bpmn:DataInputAssociation', + 'bpmn:DataOutputAssociation', + 'bpmn:Lane', + 'bpmn:MessageFlow', + 'bpmn:SequenceFlow', + 'bpmn:TextAnnotation' + ]); + + return !(element.labelTarget || cannotDistribute); + }); + + // filter out elements which are children of any of the selected elements + elements = getParents(elements); + + if (elements.length < 3) { + return false; + } + + return elements; + }); +}; diff --git a/lib/features/distribute-elements/DistributeElementsIcons.js b/lib/features/distribute-elements/DistributeElementsIcons.js new file mode 100644 index 0000000..b1621a9 --- /dev/null +++ b/lib/features/distribute-elements/DistributeElementsIcons.js @@ -0,0 +1,10 @@ +/** + * To change the icons, modify the SVGs in `./resources`, execute `npx svgo -f resources --datauri enc -o dist`, + * and then replace respective icons with the optimized data URIs in `./dist`. + */ +var icons = { + horizontal: 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linejoin%3Around%22%20d%3D%22M450%20400V150h900v250%22%2F%3E%3Crect%20x%3D%22150%22%20y%3D%22450%22%20width%3D%22600%22%20height%3D%221200%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%221050%22%20y%3D%22450%22%20width%3D%22600%22%20height%3D%22800%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E', + vertical: 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201800%201800%22%3E%3Cpath%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bstroke-linejoin%3Around%22%20d%3D%22M400%201350H150V450h250%22%2F%3E%3Crect%20x%3D%22450%22%20y%3D%22150%22%20width%3D%221200%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3Anone%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%22%2F%3E%3Crect%20x%3D%22450%22%20y%3D%221050%22%20width%3D%22800%22%20height%3D%22600%22%20rx%3D%221%22%20style%3D%22fill%3AcurrentColor%3Bstroke%3AcurrentColor%3Bstroke-width%3A100%3Bopacity%3A.5%22%2F%3E%3C%2Fsvg%3E', +}; + +export default icons; diff --git a/lib/features/distribute-elements/DistributeElementsMenuProvider.js b/lib/features/distribute-elements/DistributeElementsMenuProvider.js new file mode 100644 index 0000000..7fbefd1 --- /dev/null +++ b/lib/features/distribute-elements/DistributeElementsMenuProvider.js @@ -0,0 +1,69 @@ +import ICONS from './DistributeElementsIcons'; + +import { assign } from 'min-dash'; + +var LOW_PRIORITY = 900; + +/** + * A provider for distribute elements popup menu. + */ +export default function DistributeElementsMenuProvider( + popupMenu, distributeElements, translate, rules) { + this._distributeElements = distributeElements; + this._translate = translate; + this._popupMenu = popupMenu; + this._rules = rules; + + popupMenu.registerProvider('align-elements', LOW_PRIORITY, this); +} + +DistributeElementsMenuProvider.$inject = [ + 'popupMenu', + 'distributeElements', + 'translate', + 'rules' +]; + +DistributeElementsMenuProvider.prototype.getPopupMenuEntries = function(elements) { + var entries = {}; + + if (this._isAllowed(elements)) { + assign(entries, this._getEntries(elements)); + } + + return entries; +}; + +DistributeElementsMenuProvider.prototype._isAllowed = function(elements) { + return this._rules.allowed('elements.distribute', { elements: elements }); +}; + +DistributeElementsMenuProvider.prototype._getEntries = function(elements) { + var distributeElements = this._distributeElements, + translate = this._translate, + popupMenu = this._popupMenu; + + var entries = { + 'distribute-elements-horizontal': { + group: 'distribute', + title: translate('Distribute elements horizontally'), + className: 'bjs-align-elements-menu-entry', + imageUrl: ICONS['horizontal'], + action: function(event, entry) { + distributeElements.trigger(elements, 'horizontal'); + popupMenu.close(); + } + }, + 'distribute-elements-vertical': { + group: 'distribute', + title: translate('Distribute elements vertically'), + imageUrl: ICONS['vertical'], + action: function(event, entry) { + distributeElements.trigger(elements, 'vertical'); + popupMenu.close(); + } + }, + }; + + return entries; +}; diff --git a/lib/features/distribute-elements/index.js b/lib/features/distribute-elements/index.js new file mode 100644 index 0000000..15ea2f0 --- /dev/null +++ b/lib/features/distribute-elements/index.js @@ -0,0 +1,19 @@ +import DistributeElementsModule from 'diagram-js/lib/features/distribute-elements'; +import PopupMenuModule from 'diagram-js/lib/features/popup-menu'; + +import BpmnDistributeElements from './BpmnDistributeElements'; +import DistributeElementsMenuProvider from './DistributeElementsMenuProvider'; + + +export default { + __depends__: [ + PopupMenuModule, + DistributeElementsModule + ], + __init__: [ + 'bpmnDistributeElements', + 'distributeElementsMenuProvider' + ], + bpmnDistributeElements: [ 'type', BpmnDistributeElements ], + distributeElementsMenuProvider: [ 'type', DistributeElementsMenuProvider ] +}; diff --git a/lib/features/distribute-elements/resources/distribute-horizontally-tool.svg b/lib/features/distribute-elements/resources/distribute-horizontally-tool.svg new file mode 100644 index 0000000..0c1e2c4 --- /dev/null +++ b/lib/features/distribute-elements/resources/distribute-horizontally-tool.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/lib/features/distribute-elements/resources/distribute-vertically-tool.svg b/lib/features/distribute-elements/resources/distribute-vertically-tool.svg new file mode 100644 index 0000000..446c99a --- /dev/null +++ b/lib/features/distribute-elements/resources/distribute-vertically-tool.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/lib/features/drilldown/DrilldownBreadcrumbs.js b/lib/features/drilldown/DrilldownBreadcrumbs.js new file mode 100644 index 0000000..930d49a --- /dev/null +++ b/lib/features/drilldown/DrilldownBreadcrumbs.js @@ -0,0 +1,122 @@ +import { domify, classes } from 'min-dom'; +import { find } from 'min-dash'; + +import { escapeHTML } from 'diagram-js/lib/util/EscapeUtil'; +import { getBusinessObject, is } from '../../util/ModelUtil'; +import { + getPlaneIdFromShape +} from '../../util/DrilldownUtil'; + +var OPEN_CLASS = 'bjs-breadcrumbs-shown'; + + +/** + * Adds overlays that allow switching planes on collapsed subprocesses. + * + * @param {eventBus} eventBus + * @param {elementRegistry} elementRegistry + * @param {overlays} overlays + * @param {canvas} canvas + */ +export default function DrilldownBreadcrumbs(eventBus, elementRegistry, overlays, canvas) { + var breadcrumbs = domify('
                '); + var container = canvas.getContainer(); + var containerClasses = classes(container); + container.appendChild(breadcrumbs); + + var boParents = []; + + // update breadcrumbs if name or ID of the primary shape changes + eventBus.on('element.changed', function(e) { + var shape = e.element, + bo = getBusinessObject(shape); + + var isPresent = find(boParents, function(el) { + return el === bo; + }); + + if (!isPresent) { + return; + } + + updateBreadcrumbs(); + }); + + /** + * Updates the displayed breadcrumbs. If no element is provided, only the + * labels are updated. + * + * @param {djs.model.Base} [element] + */ + function updateBreadcrumbs(element) { + if (element) { + boParents = getBoParentChain(element); + } + + var path = boParents.map(function(parent) { + var title = escapeHTML(parent.name || parent.id); + var link = domify('
              • ' + title + '
              • '); + + var parentPlane = canvas.findRoot(getPlaneIdFromShape(parent)) || canvas.findRoot(parent.id); + + // when the root is a collaboration, the process does not have a corresponding + // element in the elementRegisty. Instead, we search for the corresponding participant + if (!parentPlane && is(parent, 'bpmn:Process')) { + var participant = elementRegistry.find(function(element) { + var bo = getBusinessObject(element); + return bo && bo.processRef && bo.processRef === parent; + }); + + parentPlane = canvas.findRoot(participant.id); + } + + link.addEventListener('click', function() { + canvas.setRootElement(parentPlane); + }); + + return link; + }); + + breadcrumbs.innerHTML = ''; + + // show breadcrumbs and expose state to .djs-container + var visible = path.length > 1; + containerClasses.toggle(OPEN_CLASS, visible); + + path.forEach(function(el) { + breadcrumbs.appendChild(el); + }); + } + + eventBus.on('root.set', function(event) { + updateBreadcrumbs(event.element); + }); + +} + +DrilldownBreadcrumbs.$inject = [ 'eventBus', 'elementRegistry', 'overlays', 'canvas' ]; + + +// helpers ////////// + +/** + * Returns the parents for the element using the business object chain, + * starting with the root element. + * + * @param {djs.model.Shape} child + * + * @returns {Array} parents + */ +function getBoParentChain(child) { + var bo = getBusinessObject(child); + + var parents = []; + + for (var element = bo; element; element = element.$parent) { + if (is(element, 'bpmn:SubProcess') || is(element, 'bpmn:Process')) { + parents.push(element); + } + } + + return parents.reverse(); +} \ No newline at end of file diff --git a/lib/features/drilldown/DrilldownCentering.js b/lib/features/drilldown/DrilldownCentering.js new file mode 100644 index 0000000..c7f346d --- /dev/null +++ b/lib/features/drilldown/DrilldownCentering.js @@ -0,0 +1,118 @@ +import { is } from '../../util/ModelUtil'; + +/** + * Move collapsed subprocesses into view when drilling down. + * + * Zoom and scroll are saved in a session. + * + * @param {eventBus} eventBus + * @param {canvas} canvas + */ +export default function DrilldownCentering(eventBus, canvas) { + + var currentRoot = null; + var positionMap = new Map(); + + eventBus.on('root.set', function(event) { + var newRoot = event.element; + var currentViewbox = canvas.viewbox(); + var storedViewbox = positionMap.get(newRoot); + + positionMap.set(currentRoot, { + x: currentViewbox.x, + y: currentViewbox.y, + zoom: currentViewbox.scale + }); + + currentRoot = newRoot; + + // current root was replaced with a collaboration, we don't update the viewbox + if (is(newRoot, 'bpmn:Collaboration') && !storedViewbox) { + return; + } + + storedViewbox = storedViewbox || { x: 0, y: 0, zoom: 1 }; + + var dx = (currentViewbox.x - storedViewbox.x) * currentViewbox.scale, + dy = (currentViewbox.y - storedViewbox.y) * currentViewbox.scale; + + if (dx !== 0 || dy !== 0) { + canvas.scroll({ + dx: dx, + dy: dy + }); + } + + if (storedViewbox.zoom !== currentViewbox.scale) { + canvas.zoom(storedViewbox.zoom, { x: 0, y: 0 }); + } + }); + + eventBus.on('diagram.clear', function() { + positionMap.clear(); + currentRoot = null; + }); + +} + +DrilldownCentering.$inject = [ 'eventBus', 'canvas' ]; + + +/** + * ES5 Map implementation. Works. + */ +function Map() { + + this._entries = []; + + this.set = function(key, value) { + + var found = false; + + for (var k in this._entries) { + if (this._entries[k][0] === key) { + this._entries[k][1] = value; + + found = true; + + break; + } + } + + if (!found) { + this._entries.push([ key, value ]); + } + }; + + this.get = function(key) { + + for (var k in this._entries) { + if (this._entries[k][0] === key) { + return this._entries[k][1]; + } + } + + return null; + }; + + this.clear = function() { + this._entries.length = 0; + }; + + this.remove = function(key) { + + var idx = -1; + + for (var k in this._entries) { + if (this._entries[k][0] === key) { + idx = k; + + break; + } + } + + if (idx !== -1) { + this._entries.splice(idx, 1); + } + }; +} \ No newline at end of file diff --git a/lib/features/drilldown/DrilldownOverlayBehavior.js b/lib/features/drilldown/DrilldownOverlayBehavior.js new file mode 100644 index 0000000..98cc38f --- /dev/null +++ b/lib/features/drilldown/DrilldownOverlayBehavior.js @@ -0,0 +1,181 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; +import { is } from '../../util/ModelUtil'; +import { classes, domify } from 'min-dom'; +import { getPlaneIdFromShape } from '../../util/DrilldownUtil'; + +var LOW_PRIORITY = 250; +var ARROW_DOWN_SVG = ''; + +var EMPTY_MARKER = 'bjs-drilldown-empty'; + +export default function DrilldownOverlayBehavior( + canvas, eventBus, elementRegistry, overlays +) { + CommandInterceptor.call(this, eventBus); + + this._canvas = canvas; + this._eventBus = eventBus; + this._elementRegistry = elementRegistry; + this._overlays = overlays; + + var self = this; + + this.executed('shape.toggleCollapse', LOW_PRIORITY, function(context) { + var shape = context.shape; + + // Add overlay to the collapsed shape + if (self.canDrillDown(shape)) { + self.addOverlay(shape); + } else { + self.removeOverlay(shape); + } + }, true); + + + this.reverted('shape.toggleCollapse', LOW_PRIORITY, function(context) { + var shape = context.shape; + + // Add overlay to the collapsed shape + if (self.canDrillDown(shape)) { + self.addOverlay(shape); + } else { + self.removeOverlay(shape); + } + }, true); + + + this.executed([ 'shape.create', 'shape.move', 'shape.delete' ], LOW_PRIORITY, + function(context) { + var oldParent = context.oldParent, + newParent = context.newParent || context.parent, + shape = context.shape; + + // Add overlay to the collapsed shape + if (self.canDrillDown(shape)) { + self.addOverlay(shape); + } + + self.updateDrilldownOverlay(oldParent); + self.updateDrilldownOverlay(newParent); + self.updateDrilldownOverlay(shape); + }, true); + + + this.reverted([ 'shape.create', 'shape.move', 'shape.delete' ], LOW_PRIORITY, + function(context) { + var oldParent = context.oldParent, + newParent = context.newParent || context.parent, + shape = context.shape; + + // Add overlay to the collapsed shape + if (self.canDrillDown(shape)) { + self.addOverlay(shape); + } + + self.updateDrilldownOverlay(oldParent); + self.updateDrilldownOverlay(newParent); + self.updateDrilldownOverlay(shape); + }, true); + + + eventBus.on('import.render.complete', function() { + elementRegistry.filter(function(e) { + return self.canDrillDown(e); + }).map(function(el) { + self.addOverlay(el); + }); + }); + +} + +inherits(DrilldownOverlayBehavior, CommandInterceptor); + +DrilldownOverlayBehavior.prototype.updateDrilldownOverlay = function(shape) { + var canvas = this._canvas; + + if (!shape) { + return; + } + + var root = canvas.findRoot(shape); + if (root) { + this.updateOverlayVisibility(root); + } +}; + + +DrilldownOverlayBehavior.prototype.canDrillDown = function(element) { + var canvas = this._canvas; + return is(element, 'bpmn:SubProcess') && canvas.findRoot(getPlaneIdFromShape(element)); +}; + +/** + * Updates visibility of the drilldown overlay. If the plane has no elements, + * the drilldown will be only shown when the element is selected. + * + * @param {djs.model.Shape|djs.model.Root} element collapsed shape or root element + */ +DrilldownOverlayBehavior.prototype.updateOverlayVisibility = function(element) { + var overlays = this._overlays; + + var bo = element.businessObject; + + var overlay = overlays.get({ element: bo.id, type: 'drilldown' })[0]; + + if (!overlay) { + return; + } + + var hasContent = bo && bo.flowElements && bo.flowElements.length; + classes(overlay.html).toggle(EMPTY_MARKER, !hasContent); +}; + +/** + * Attaches a drilldown button to the given element. We assume that the plane has + * the same id as the element. + * + * @param {djs.model.Shape} element collapsed shape + */ +DrilldownOverlayBehavior.prototype.addOverlay = function(element) { + var canvas = this._canvas; + var overlays = this._overlays; + + var existingOverlays = overlays.get({ element: element, type: 'drilldown' }); + if (existingOverlays.length) { + this.removeOverlay(element); + } + + var button = domify(''); + + button.addEventListener('click', function() { + canvas.setRootElement(canvas.findRoot(getPlaneIdFromShape(element))); + }); + + overlays.add(element, 'drilldown', { + position: { + bottom: -7, + right: -8 + }, + html: button + }); + + this.updateOverlayVisibility(element); +}; + +DrilldownOverlayBehavior.prototype.removeOverlay = function(element) { + var overlays = this._overlays; + + overlays.remove({ + element: element, + type: 'drilldown' + }); +}; + +DrilldownOverlayBehavior.$inject = [ + 'canvas', + 'eventBus', + 'elementRegistry', + 'overlays' +]; \ No newline at end of file diff --git a/lib/features/drilldown/SubprocessCompatibility.js b/lib/features/drilldown/SubprocessCompatibility.js new file mode 100644 index 0000000..19dedcf --- /dev/null +++ b/lib/features/drilldown/SubprocessCompatibility.js @@ -0,0 +1,218 @@ + +import { asBounds, asTRBL } from 'diagram-js/lib/layout/LayoutUtil'; +import { is, isAny } from '../../util/ModelUtil'; + +var DEFAULT_POSITION = { + x: 180, + y: 160 +}; + +/** + * Hook into `import.render.start` and create new planes for diagrams with + * collapsed subprocesses and all dis on the same plane. + * + * @param {eventBus} eventBus + * @param {moddle} moddle + */ +export default function SubprocessCompatibility(eventBus, moddle) { + this._eventBus = eventBus; + this._moddle = moddle; + + var self = this; + + eventBus.on('import.render.start', 1500, function(e, context) { + self.handleImport(context.definitions); + }); +} + +SubprocessCompatibility.prototype.handleImport = function(definitions) { + if (!definitions.diagrams) { + return; + } + + var self = this; + this._definitions = definitions; + this._processToDiagramMap = {}; + + definitions.diagrams.forEach(function(diagram) { + if (!diagram.plane || !diagram.plane.bpmnElement) { + return; + } + + self._processToDiagramMap[diagram.plane.bpmnElement.id] = diagram; + }); + + var newDiagrams = []; + definitions.diagrams.forEach(function(diagram) { + var createdDiagrams = self.createNewDiagrams(diagram.plane); + Array.prototype.push.apply(newDiagrams, createdDiagrams); + }); + + newDiagrams.forEach(function(diagram) { + self.movePlaneElementsToOrigin(diagram.plane); + }); +}; + + +/** + * Moves all DI elements from collapsed subprocesses to a new plane. + * + * @param {Object} plane + * @return {Array} new diagrams created for the collapsed subprocesses + */ +SubprocessCompatibility.prototype.createNewDiagrams = function(plane) { + var self = this; + + var collapsedElements = []; + var elementsToMove = []; + + plane.get('planeElement').forEach(function(diElement) { + var bo = diElement.bpmnElement; + + if (!bo) { + return; + } + + var parent = bo.$parent; + + if (is(bo, 'bpmn:SubProcess') && !diElement.isExpanded) { + collapsedElements.push(bo); + } + + if (shouldMoveToPlane(bo, plane)) { + + // don't change the array while we iterate over it + elementsToMove.push({ diElement: diElement, parent: parent }); + } + }); + + var newDiagrams = []; + + // create new planes for all collapsed subprocesses, even when they are empty + collapsedElements.forEach(function(element) { + if (!self._processToDiagramMap[element.id]) { + var diagram = self.createDiagram(element); + self._processToDiagramMap[element.id] = diagram; + newDiagrams.push(diagram); + } + }); + + elementsToMove.forEach(function(element) { + var diElement = element.diElement; + var parent = element.parent; + + // parent is expanded, get nearest collapsed parent + while (parent && collapsedElements.indexOf(parent) === -1) { + parent = parent.$parent; + } + + // false positive, all parents are expanded + if (!parent) { + return; + } + + var diagram = self._processToDiagramMap[parent.id]; + self.moveToDiPlane(diElement, diagram.plane); + }); + + return newDiagrams; +}; + +SubprocessCompatibility.prototype.movePlaneElementsToOrigin = function(plane) { + var elements = plane.get('planeElement'); + + // get bounding box of all elements + var planeBounds = getPlaneBounds(plane); + + var offset = { + x: planeBounds.x - DEFAULT_POSITION.x, + y: planeBounds.y - DEFAULT_POSITION.y + }; + + elements.forEach(function(diElement) { + if (diElement.waypoint) { + diElement.waypoint.forEach(function(waypoint) { + waypoint.x = waypoint.x - offset.x; + waypoint.y = waypoint.y - offset.y; + }); + } else if (diElement.bounds) { + diElement.bounds.x = diElement.bounds.x - offset.x; + diElement.bounds.y = diElement.bounds.y - offset.y; + } + }); +}; + + +SubprocessCompatibility.prototype.moveToDiPlane = function(diElement, newPlane) { + var containingDiagram = findRootDiagram(diElement); + + // remove DI from old Plane and add it to the new one + var parentPlaneElement = containingDiagram.plane.get('planeElement'); + parentPlaneElement.splice(parentPlaneElement.indexOf(diElement), 1); + newPlane.get('planeElement').push(diElement); +}; + + +SubprocessCompatibility.prototype.createDiagram = function(bo) { + var plane = this._moddle.create('bpmndi:BPMNPlane', { bpmnElement: bo }); + var diagram = this._moddle.create('bpmndi:BPMNDiagram', { + plane: plane + }); + plane.$parent = diagram; + plane.bpmnElement = bo; + diagram.$parent = this._definitions; + this._definitions.diagrams.push(diagram); + return diagram; +}; + +SubprocessCompatibility.$inject = [ 'eventBus', 'moddle' ]; + + +// helpers ////////////////////////// + +function findRootDiagram(element) { + if (is(element, 'bpmndi:BPMNDiagram')) { + return element; + } else { + return findRootDiagram(element.$parent); + } +} + +function getPlaneBounds(plane) { + var planeTrbl = { + top: Infinity, + right: -Infinity, + bottom: -Infinity, + left: Infinity + }; + + plane.planeElement.forEach(function(element) { + if (!element.bounds) { + return; + } + + var trbl = asTRBL(element.bounds); + + planeTrbl.top = Math.min(trbl.top, planeTrbl.top); + planeTrbl.left = Math.min(trbl.left, planeTrbl.left); + }); + + return asBounds(planeTrbl); +} + +function shouldMoveToPlane(bo, plane) { + var parent = bo.$parent; + + // don't move elements that are already on the plane + if (!is(parent, 'bpmn:SubProcess') || parent === plane.bpmnElement) { + return false; + } + + // dataAssociations are children of the subprocess but rendered on process level + // cf. https://github.com/bpmn-io/bpmn-js/issues/1619 + if (isAny(bo, [ 'bpmn:DataInputAssociation', 'bpmn:DataOutputAssociation' ])) { + return false; + } + + return true; +} diff --git a/lib/features/drilldown/index.js b/lib/features/drilldown/index.js new file mode 100644 index 0000000..fe32796 --- /dev/null +++ b/lib/features/drilldown/index.js @@ -0,0 +1,17 @@ +import OverlaysModule from 'diagram-js/lib/features/overlays'; +import ChangeSupportModule from 'diagram-js/lib/features/change-support'; +import RootElementsModule from 'diagram-js/lib/features/root-elements'; + +import DrilldownBreadcrumbs from './DrilldownBreadcrumbs'; +import DrilldownCentering from './DrilldownCentering'; +import SubprocessCompatibility from './SubprocessCompatibility'; +import DrilldownOverlayBehavior from './DrilldownOverlayBehavior'; + +export default { + __depends__: [ OverlaysModule, ChangeSupportModule, RootElementsModule ], + __init__: [ 'drilldownBreadcrumbs', 'drilldownOverlayBehavior', 'drilldownCentering', 'subprocessCompatibility' ], + drilldownBreadcrumbs: [ 'type', DrilldownBreadcrumbs ], + drilldownCentering: [ 'type', DrilldownCentering ], + drilldownOverlayBehavior: [ 'type', DrilldownOverlayBehavior ], + subprocessCompatibility: [ 'type', SubprocessCompatibility ] +}; \ No newline at end of file diff --git a/lib/features/editor-actions/BpmnEditorActions.js b/lib/features/editor-actions/BpmnEditorActions.js new file mode 100644 index 0000000..c15b408 --- /dev/null +++ b/lib/features/editor-actions/BpmnEditorActions.js @@ -0,0 +1,177 @@ +import inherits from 'inherits-browser'; + +import EditorActions from 'diagram-js/lib/features/editor-actions/EditorActions'; + +import { filter } from 'min-dash'; + +import { is } from '../../util/ModelUtil'; + +import { + getBBox +} from 'diagram-js/lib/util/Elements'; + + +/** + * Registers and executes BPMN specific editor actions. + * + * @param {Injector} injector + */ +export default function BpmnEditorActions(injector) { + injector.invoke(EditorActions, this); +} + +inherits(BpmnEditorActions, EditorActions); + +BpmnEditorActions.$inject = [ + 'injector' +]; + +/** + * Register default actions. + * + * @param {Injector} injector + */ +BpmnEditorActions.prototype._registerDefaultActions = function(injector) { + + // (0) invoke super method + + EditorActions.prototype._registerDefaultActions.call(this, injector); + + // (1) retrieve optional components to integrate with + + var canvas = injector.get('canvas', false); + var elementRegistry = injector.get('elementRegistry', false); + var selection = injector.get('selection', false); + var spaceTool = injector.get('spaceTool', false); + var lassoTool = injector.get('lassoTool', false); + var handTool = injector.get('handTool', false); + var globalConnect = injector.get('globalConnect', false); + var distributeElements = injector.get('distributeElements', false); + var alignElements = injector.get('alignElements', false); + var directEditing = injector.get('directEditing', false); + var searchPad = injector.get('searchPad', false); + var modeling = injector.get('modeling', false); + + // (2) check components and register actions + + if (canvas && elementRegistry && selection) { + this._registerAction('selectElements', function() { + + // select all elements except for the invisible + // root element + var rootElement = canvas.getRootElement(); + + var elements = elementRegistry.filter(function(element) { + return element !== rootElement; + }); + + selection.select(elements); + + return elements; + }); + } + + if (spaceTool) { + this._registerAction('spaceTool', function() { + spaceTool.toggle(); + }); + } + + if (lassoTool) { + this._registerAction('lassoTool', function() { + lassoTool.toggle(); + }); + } + + if (handTool) { + this._registerAction('handTool', function() { + handTool.toggle(); + }); + } + + if (globalConnect) { + this._registerAction('globalConnectTool', function() { + globalConnect.toggle(); + }); + } + + if (selection && distributeElements) { + this._registerAction('distributeElements', function(opts) { + var currentSelection = selection.get(), + type = opts.type; + + if (currentSelection.length) { + distributeElements.trigger(currentSelection, type); + } + }); + } + + if (selection && alignElements) { + this._registerAction('alignElements', function(opts) { + var currentSelection = selection.get(), + aligneableElements = [], + type = opts.type; + + if (currentSelection.length) { + aligneableElements = filter(currentSelection, function(element) { + return !is(element, 'bpmn:Lane'); + }); + + alignElements.trigger(aligneableElements, type); + } + }); + } + + if (selection && modeling) { + this._registerAction('setColor', function(opts) { + var currentSelection = selection.get(); + + if (currentSelection.length) { + modeling.setColor(currentSelection, opts); + } + }); + } + + if (selection && directEditing) { + this._registerAction('directEditing', function() { + var currentSelection = selection.get(); + + if (currentSelection.length) { + directEditing.activate(currentSelection[0]); + } + }); + } + + if (searchPad) { + this._registerAction('find', function() { + searchPad.toggle(); + }); + } + + if (canvas && modeling) { + this._registerAction('moveToOrigin', function() { + var rootElement = canvas.getRootElement(), + boundingBox, + elements; + + if (is(rootElement, 'bpmn:Collaboration')) { + elements = elementRegistry.filter(function(element) { + return is(element.parent, 'bpmn:Collaboration'); + }); + } else { + elements = elementRegistry.filter(function(element) { + return element !== rootElement && !is(element.parent, 'bpmn:SubProcess'); + }); + } + + boundingBox = getBBox(elements); + + modeling.moveElements( + elements, + { x: -boundingBox.x, y: -boundingBox.y }, + rootElement + ); + }); + } + +}; \ No newline at end of file diff --git a/lib/features/editor-actions/index.js b/lib/features/editor-actions/index.js new file mode 100644 index 0000000..bed7d7e --- /dev/null +++ b/lib/features/editor-actions/index.js @@ -0,0 +1,10 @@ +import EditorActionsModule from 'diagram-js/lib/features/editor-actions'; + +import BpmnEditorActions from './BpmnEditorActions'; + +export default { + __depends__: [ + EditorActionsModule + ], + editorActions: [ 'type', BpmnEditorActions ] +}; diff --git a/lib/features/grid-snapping/BpmnGridSnapping.js b/lib/features/grid-snapping/BpmnGridSnapping.js new file mode 100644 index 0000000..f18146d --- /dev/null +++ b/lib/features/grid-snapping/BpmnGridSnapping.js @@ -0,0 +1,25 @@ +import { isAny } from '../modeling/util/ModelingUtil'; + +export default function BpmnGridSnapping(eventBus) { + eventBus.on([ + 'create.init', + 'shape.move.init' + ], function(event) { + var context = event.context, + shape = event.shape; + + if (isAny(shape, [ + 'bpmn:Participant', + 'bpmn:SubProcess', + 'bpmn:TextAnnotation' + ])) { + if (!context.gridSnappingContext) { + context.gridSnappingContext = {}; + } + + context.gridSnappingContext.snapLocation = 'top-left'; + } + }); +} + +BpmnGridSnapping.$inject = [ 'eventBus' ]; \ No newline at end of file diff --git a/lib/features/grid-snapping/behavior/GridSnappingAutoPlaceBehavior.js b/lib/features/grid-snapping/behavior/GridSnappingAutoPlaceBehavior.js new file mode 100644 index 0000000..89493a1 --- /dev/null +++ b/lib/features/grid-snapping/behavior/GridSnappingAutoPlaceBehavior.js @@ -0,0 +1,59 @@ +import { getNewShapePosition } from '../../auto-place/BpmnAutoPlaceUtil'; + +import { getMid } from 'diagram-js/lib/layout/LayoutUtil'; +import { is } from '../../../util/ModelUtil'; + +var HIGH_PRIORITY = 2000; + + +export default function GridSnappingAutoPlaceBehavior(eventBus, gridSnapping) { + eventBus.on('autoPlace', HIGH_PRIORITY, function(context) { + var source = context.source, + sourceMid = getMid(source), + shape = context.shape; + + var position = getNewShapePosition(source, shape); + + [ 'x', 'y' ].forEach(function(axis) { + var options = {}; + + // do not snap if x/y equal + if (position[ axis ] === sourceMid[ axis ]) { + return; + } + + if (position[ axis ] > sourceMid[ axis ]) { + options.min = position[ axis ]; + } else { + options.max = position[ axis ]; + } + + if (is(shape, 'bpmn:TextAnnotation')) { + + if (isHorizontal(axis)) { + options.offset = -shape.width / 2; + } else { + options.offset = -shape.height / 2; + } + + } + + position[ axis ] = gridSnapping.snapValue(position[ axis ], options); + + }); + + // must be returned to be considered by auto place + return position; + }); +} + +GridSnappingAutoPlaceBehavior.$inject = [ + 'eventBus', + 'gridSnapping' +]; + +// helpers ////////// + +function isHorizontal(axis) { + return axis === 'x'; +} \ No newline at end of file diff --git a/lib/features/grid-snapping/behavior/GridSnappingLayoutConnectionBehavior.js b/lib/features/grid-snapping/behavior/GridSnappingLayoutConnectionBehavior.js new file mode 100644 index 0000000..48d184e --- /dev/null +++ b/lib/features/grid-snapping/behavior/GridSnappingLayoutConnectionBehavior.js @@ -0,0 +1,144 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { pointsAligned } from 'diagram-js/lib/util/Geometry'; + +import { + assign +} from 'min-dash'; + +var HIGH_PRIORITY = 3000; + + +/** + * Snaps connections with Manhattan layout. + */ +export default function GridSnappingLayoutConnectionBehavior(eventBus, gridSnapping, modeling) { + CommandInterceptor.call(this, eventBus); + + this._gridSnapping = gridSnapping; + + var self = this; + + this.postExecuted([ + 'connection.create', + 'connection.layout' + ], HIGH_PRIORITY, function(event) { + var context = event.context, + connection = context.connection, + hints = context.hints || {}, + waypoints = connection.waypoints; + + if (hints.connectionStart || hints.connectionEnd || hints.createElementsBehavior === false) { + return; + } + + if (!hasMiddleSegments(waypoints)) { + return; + } + + modeling.updateWaypoints(connection, self.snapMiddleSegments(waypoints)); + }); +} + +GridSnappingLayoutConnectionBehavior.$inject = [ + 'eventBus', + 'gridSnapping', + 'modeling' +]; + +inherits(GridSnappingLayoutConnectionBehavior, CommandInterceptor); + +/** + * Snap middle segments of a given connection. + * + * @param {Array} waypoints + * + * @returns {Array} + */ +GridSnappingLayoutConnectionBehavior.prototype.snapMiddleSegments = function(waypoints) { + var gridSnapping = this._gridSnapping, + snapped; + + waypoints = waypoints.slice(); + + for (var i = 1; i < waypoints.length - 2; i++) { + + snapped = snapSegment(gridSnapping, waypoints[i], waypoints[i + 1]); + + waypoints[i] = snapped[0]; + waypoints[i + 1] = snapped[1]; + } + + return waypoints; +}; + + +// helpers ////////// + +/** + * Check whether a connection has a middle segments. + * + * @param {Array} waypoints + * + * @returns {boolean} + */ +function hasMiddleSegments(waypoints) { + return waypoints.length > 3; +} + +/** + * Check whether an alignment is horizontal. + * + * @param {string} aligned + * + * @returns {boolean} + */ +function horizontallyAligned(aligned) { + return aligned === 'h'; +} + +/** + * Check whether an alignment is vertical. + * + * @param {string} aligned + * + * @returns {boolean} + */ +function verticallyAligned(aligned) { + return aligned === 'v'; +} + +/** + * Get middle segments from a given connection. + * + * @param {Array} waypoints + * + * @returns {Array} + */ +function snapSegment(gridSnapping, segmentStart, segmentEnd) { + + var aligned = pointsAligned(segmentStart, segmentEnd); + + var snapped = {}; + + if (horizontallyAligned(aligned)) { + + // snap horizontally + snapped.y = gridSnapping.snapValue(segmentStart.y); + } + + if (verticallyAligned(aligned)) { + + // snap vertically + snapped.x = gridSnapping.snapValue(segmentStart.x); + } + + if ('x' in snapped || 'y' in snapped) { + segmentStart = assign({}, segmentStart, snapped); + segmentEnd = assign({}, segmentEnd, snapped); + } + + return [ segmentStart, segmentEnd ]; +} \ No newline at end of file diff --git a/lib/features/grid-snapping/behavior/GridSnappingParticipantBehavior.js b/lib/features/grid-snapping/behavior/GridSnappingParticipantBehavior.js new file mode 100644 index 0000000..48e6130 --- /dev/null +++ b/lib/features/grid-snapping/behavior/GridSnappingParticipantBehavior.js @@ -0,0 +1,36 @@ +import { is } from '../../../util/ModelUtil'; + +var HIGHER_PRIORITY = 1750; + + +export default function GridSnappingParticipantBehavior(canvas, eventBus, gridSnapping) { + eventBus.on([ + 'create.start', + 'shape.move.start' + ], HIGHER_PRIORITY, function(event) { + var context = event.context, + shape = context.shape, + rootElement = canvas.getRootElement(); + + if (!is(shape, 'bpmn:Participant') || + !is(rootElement, 'bpmn:Process') || + !rootElement.children.length) { + return; + } + + var createConstraints = context.createConstraints; + + if (!createConstraints) { + return; + } + + shape.width = gridSnapping.snapValue(shape.width, { min: shape.width }); + shape.height = gridSnapping.snapValue(shape.height, { min: shape.height }); + }); +} + +GridSnappingParticipantBehavior.$inject = [ + 'canvas', + 'eventBus', + 'gridSnapping' +]; \ No newline at end of file diff --git a/lib/features/grid-snapping/behavior/index.js b/lib/features/grid-snapping/behavior/index.js new file mode 100644 index 0000000..68038c1 --- /dev/null +++ b/lib/features/grid-snapping/behavior/index.js @@ -0,0 +1,14 @@ +import GridSnappingAutoPlaceBehavior from './GridSnappingAutoPlaceBehavior'; +import GridSnappingParticipantBehavior from './GridSnappingParticipantBehavior'; +import GridSnappingLayoutConnectionBehavior from './GridSnappingLayoutConnectionBehavior'; + +export default { + __init__: [ + 'gridSnappingAutoPlaceBehavior', + 'gridSnappingParticipantBehavior', + 'gridSnappingLayoutConnectionBehavior', + ], + gridSnappingAutoPlaceBehavior: [ 'type', GridSnappingAutoPlaceBehavior ], + gridSnappingParticipantBehavior: [ 'type', GridSnappingParticipantBehavior ], + gridSnappingLayoutConnectionBehavior: [ 'type', GridSnappingLayoutConnectionBehavior ] +}; \ No newline at end of file diff --git a/lib/features/grid-snapping/index.js b/lib/features/grid-snapping/index.js new file mode 100644 index 0000000..b80f7d1 --- /dev/null +++ b/lib/features/grid-snapping/index.js @@ -0,0 +1,13 @@ +import BpmnGridSnapping from './BpmnGridSnapping'; +import GridSnappingModule from 'diagram-js/lib/features/grid-snapping'; + +import GridSnappingBehaviorModule from './behavior'; + +export default { + __depends__: [ + GridSnappingModule, + GridSnappingBehaviorModule + ], + __init__: [ 'bpmnGridSnapping' ], + bpmnGridSnapping: [ 'type', BpmnGridSnapping ] +}; \ No newline at end of file diff --git a/lib/features/interaction-events/BpmnInteractionEvents.js b/lib/features/interaction-events/BpmnInteractionEvents.js new file mode 100644 index 0000000..e4f09d6 --- /dev/null +++ b/lib/features/interaction-events/BpmnInteractionEvents.js @@ -0,0 +1,118 @@ +import { is } from '../../util/ModelUtil'; + +import { isExpanded } from '../../util/DiUtil'; + +var LABEL_WIDTH = 30, + LABEL_HEIGHT = 30; + + +/** + * BPMN-specific hit zones and interaction fixes. + * + * @param {EventBus} eventBus + * @param {InteractionEvents} interactionEvents + */ +export default function BpmnInteractionEvents(eventBus, interactionEvents) { + + this._interactionEvents = interactionEvents; + + var self = this; + + eventBus.on([ + 'interactionEvents.createHit', + 'interactionEvents.updateHit' + ], function(context) { + var element = context.element, + gfx = context.gfx; + + if (is(element, 'bpmn:Lane')) { + return self.createParticipantHit(element, gfx); + } else + + if (is(element, 'bpmn:Participant')) { + if (isExpanded(element)) { + return self.createParticipantHit(element, gfx); + } else { + return self.createDefaultHit(element, gfx); + } + } else + + if (is(element, 'bpmn:SubProcess')) { + if (isExpanded(element)) { + return self.createSubProcessHit(element, gfx); + } else { + return self.createDefaultHit(element, gfx); + } + } + }); + +} + +BpmnInteractionEvents.$inject = [ + 'eventBus', + 'interactionEvents' +]; + + +BpmnInteractionEvents.prototype.createDefaultHit = function(element, gfx) { + this._interactionEvents.removeHits(gfx); + + this._interactionEvents.createDefaultHit(element, gfx); + + // indicate that we created a hit + return true; +}; + +BpmnInteractionEvents.prototype.createParticipantHit = function(element, gfx) { + + // remove existing hits + this._interactionEvents.removeHits(gfx); + + // add body hit + this._interactionEvents.createBoxHit(gfx, 'no-move', { + width: element.width, + height: element.height + }); + + // add outline hit + this._interactionEvents.createBoxHit(gfx, 'click-stroke', { + width: element.width, + height: element.height + }); + + // add label hit + this._interactionEvents.createBoxHit(gfx, 'all', { + width: LABEL_WIDTH, + height: element.height + }); + + // indicate that we created a hit + return true; +}; + +BpmnInteractionEvents.prototype.createSubProcessHit = function(element, gfx) { + + // remove existing hits + this._interactionEvents.removeHits(gfx); + + // add body hit + this._interactionEvents.createBoxHit(gfx, 'no-move', { + width: element.width, + height: element.height + }); + + // add outline hit + this._interactionEvents.createBoxHit(gfx, 'click-stroke', { + width: element.width, + height: element.height + }); + + // add label hit + this._interactionEvents.createBoxHit(gfx, 'all', { + width: element.width, + height: LABEL_HEIGHT + }); + + // indicate that we created a hit + return true; +}; \ No newline at end of file diff --git a/lib/features/interaction-events/index.js b/lib/features/interaction-events/index.js new file mode 100644 index 0000000..85ebac7 --- /dev/null +++ b/lib/features/interaction-events/index.js @@ -0,0 +1,6 @@ +import BpmnInteractionEvents from './BpmnInteractionEvents'; + +export default { + __init__: [ 'bpmnInteractionEvents' ], + bpmnInteractionEvents: [ 'type', BpmnInteractionEvents ] +}; \ No newline at end of file diff --git a/lib/features/keyboard/BpmnKeyboardBindings.js b/lib/features/keyboard/BpmnKeyboardBindings.js new file mode 100644 index 0000000..f74b3e5 --- /dev/null +++ b/lib/features/keyboard/BpmnKeyboardBindings.js @@ -0,0 +1,158 @@ +import inherits from 'inherits-browser'; + +import KeyboardBindings from 'diagram-js/lib/features/keyboard/KeyboardBindings'; + + +/** + * BPMN 2.0 specific keyboard bindings. + * + * @param {Injector} injector + */ +export default function BpmnKeyboardBindings(injector) { + injector.invoke(KeyboardBindings, this); +} + +inherits(BpmnKeyboardBindings, KeyboardBindings); + +BpmnKeyboardBindings.$inject = [ + 'injector' +]; + + +/** + * Register available keyboard bindings. + * + * @param {Keyboard} keyboard + * @param {EditorActions} editorActions + */ +BpmnKeyboardBindings.prototype.registerBindings = function(keyboard, editorActions) { + + // inherit default bindings + KeyboardBindings.prototype.registerBindings.call(this, keyboard, editorActions); + + /** + * Add keyboard binding if respective editor action + * is registered. + * + * @param {string} action name + * @param {Function} fn that implements the key binding + */ + function addListener(action, fn) { + + if (editorActions.isRegistered(action)) { + keyboard.addListener(fn); + } + } + + // select all elements + // CTRL + A + addListener('selectElements', function(context) { + + var event = context.keyEvent; + + if (keyboard.isKey([ 'a', 'A' ], event) && keyboard.isCmd(event)) { + editorActions.trigger('selectElements'); + + return true; + } + }); + + // search labels + // CTRL + F + addListener('find', function(context) { + + var event = context.keyEvent; + + if (keyboard.isKey([ 'f', 'F' ], event) && keyboard.isCmd(event)) { + editorActions.trigger('find'); + + return true; + } + }); + + // activate space tool + // S + addListener('spaceTool', function(context) { + + var event = context.keyEvent; + + if (keyboard.hasModifier(event)) { + return; + } + + if (keyboard.isKey([ 's', 'S' ], event)) { + editorActions.trigger('spaceTool'); + + return true; + } + }); + + // activate lasso tool + // L + addListener('lassoTool', function(context) { + + var event = context.keyEvent; + + if (keyboard.hasModifier(event)) { + return; + } + + if (keyboard.isKey([ 'l', 'L' ], event)) { + editorActions.trigger('lassoTool'); + + return true; + } + }); + + // activate hand tool + // H + addListener('handTool', function(context) { + + var event = context.keyEvent; + + if (keyboard.hasModifier(event)) { + return; + } + + if (keyboard.isKey([ 'h', 'H' ], event)) { + editorActions.trigger('handTool'); + + return true; + } + }); + + // activate global connect tool + // C + addListener('globalConnectTool', function(context) { + + var event = context.keyEvent; + + if (keyboard.hasModifier(event)) { + return; + } + + if (keyboard.isKey([ 'c', 'C' ], event)) { + editorActions.trigger('globalConnectTool'); + + return true; + } + }); + + // activate direct editing + // E + addListener('directEditing', function(context) { + + var event = context.keyEvent; + + if (keyboard.hasModifier(event)) { + return; + } + + if (keyboard.isKey([ 'e', 'E' ], event)) { + editorActions.trigger('directEditing'); + + return true; + } + }); + +}; \ No newline at end of file diff --git a/lib/features/keyboard/index.js b/lib/features/keyboard/index.js new file mode 100644 index 0000000..26d04e1 --- /dev/null +++ b/lib/features/keyboard/index.js @@ -0,0 +1,11 @@ +import KeyboardModule from 'diagram-js/lib/features/keyboard'; + +import BpmnKeyboardBindings from './BpmnKeyboardBindings'; + +export default { + __depends__: [ + KeyboardModule + ], + __init__: [ 'keyboardBindings' ], + keyboardBindings: [ 'type', BpmnKeyboardBindings ] +}; diff --git a/lib/features/label-editing/LabelEditingPreview.js b/lib/features/label-editing/LabelEditingPreview.js new file mode 100644 index 0000000..4ce2cf8 --- /dev/null +++ b/lib/features/label-editing/LabelEditingPreview.js @@ -0,0 +1,138 @@ +import { + append as svgAppend, + attr as svgAttr, + create as svgCreate, + remove as svgRemove +} from 'tiny-svg'; + +import { + getDi, + is +} from '../../util/ModelUtil'; + +import { + translate +} from 'diagram-js/lib/util/SvgTransformUtil'; + +var MARKER_HIDDEN = 'djs-element-hidden', + MARKER_LABEL_HIDDEN = 'djs-label-hidden'; + + +export default function LabelEditingPreview( + eventBus, canvas, elementRegistry, + pathMap) { + + var self = this; + + var defaultLayer = canvas.getDefaultLayer(); + + var element, absoluteElementBBox, gfx; + + eventBus.on('directEditing.activate', function(context) { + var activeProvider = context.active; + + element = activeProvider.element.label || activeProvider.element; + + // text annotation + if (is(element, 'bpmn:TextAnnotation')) { + absoluteElementBBox = canvas.getAbsoluteBBox(element); + + gfx = svgCreate('g'); + + var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: element.height, + position: { + mx: 0.0, + my: 0.0 + } + }); + + var path = self.path = svgCreate('path'); + + svgAttr(path, { + d: textPathData, + strokeWidth: 2, + stroke: getStrokeColor(element) + }); + + svgAppend(gfx, path); + + svgAppend(defaultLayer, gfx); + + translate(gfx, element.x, element.y); + } + + if (is(element, 'bpmn:TextAnnotation') || + element.labelTarget) { + canvas.addMarker(element, MARKER_HIDDEN); + } else if (is(element, 'bpmn:Task') || + is(element, 'bpmn:CallActivity') || + is(element, 'bpmn:SubProcess') || + is(element, 'bpmn:Participant')) { + canvas.addMarker(element, MARKER_LABEL_HIDDEN); + } + }); + + eventBus.on('directEditing.resize', function(context) { + + // text annotation + if (is(element, 'bpmn:TextAnnotation')) { + var height = context.height, + dy = context.dy; + + var newElementHeight = Math.max(element.height / absoluteElementBBox.height * (height + dy), 0); + + var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', { + xScaleFactor: 1, + yScaleFactor: 1, + containerWidth: element.width, + containerHeight: newElementHeight, + position: { + mx: 0.0, + my: 0.0 + } + }); + + svgAttr(self.path, { + d: textPathData + }); + } + }); + + eventBus.on([ 'directEditing.complete', 'directEditing.cancel' ], function(context) { + var activeProvider = context.active; + + if (activeProvider) { + canvas.removeMarker(activeProvider.element.label || activeProvider.element, MARKER_HIDDEN); + canvas.removeMarker(element, MARKER_LABEL_HIDDEN); + } + + element = undefined; + absoluteElementBBox = undefined; + + if (gfx) { + svgRemove(gfx); + + gfx = undefined; + } + }); +} + +LabelEditingPreview.$inject = [ + 'eventBus', + 'canvas', + 'elementRegistry', + 'pathMap' +]; + + +// helpers /////////////////// + +function getStrokeColor(element, defaultColor) { + var di = getDi(element); + + return di.get('stroke') || defaultColor || 'black'; +} \ No newline at end of file diff --git a/lib/features/label-editing/LabelEditingProvider.js b/lib/features/label-editing/LabelEditingProvider.js new file mode 100644 index 0000000..ecfd607 --- /dev/null +++ b/lib/features/label-editing/LabelEditingProvider.js @@ -0,0 +1,426 @@ +import { + assign +} from 'min-dash'; + +import { + getLabel +} from './LabelUtil'; + +import { + is +} from '../../util/ModelUtil'; + +import { isAny } from '../modeling/util/ModelingUtil'; +import { isExpanded } from '../../util/DiUtil'; + +import { + getExternalLabelMid, + isLabelExternal, + hasExternalLabel, + isLabel +} from '../../util/LabelUtil'; + +var HIGH_PRIORITY = 2000; + + +export default function LabelEditingProvider( + eventBus, bpmnFactory, canvas, directEditing, + modeling, resizeHandles, textRenderer) { + + this._bpmnFactory = bpmnFactory; + this._canvas = canvas; + this._modeling = modeling; + this._textRenderer = textRenderer; + + directEditing.registerProvider(this); + + // listen to dblclick on non-root elements + eventBus.on('element.dblclick', function(event) { + activateDirectEdit(event.element, true); + }); + + // complete on followup canvas operation + eventBus.on([ + 'autoPlace.start', + 'canvas.viewbox.changing', + 'drag.init', + 'element.mousedown', + 'popupMenu.open', + 'root.set', + 'selection.changed' + ], function(event) { + + if (directEditing.isActive()) { + directEditing.complete(); + } + }); + + eventBus.on([ + 'shape.remove', + 'connection.remove' + ], HIGH_PRIORITY, function(event) { + + if (directEditing.isActive(event.element)) { + directEditing.cancel(); + } + }); + + // cancel on command stack changes + eventBus.on([ 'commandStack.changed' ], function(e) { + if (directEditing.isActive()) { + directEditing.cancel(); + } + }); + + + eventBus.on('directEditing.activate', function(event) { + resizeHandles.removeResizers(); + }); + + eventBus.on('create.end', 500, function(event) { + + var context = event.context, + element = context.shape, + canExecute = event.context.canExecute, + isTouch = event.isTouch; + + // TODO(nikku): we need to find a way to support the + // direct editing on mobile devices; right now this will + // break for desworkflowediting on mobile devices + // as it breaks the user interaction workflow + + // TODO(nre): we should temporarily focus the edited element + // here and release the focused viewport after the direct edit + // operation is finished + if (isTouch) { + return; + } + + if (!canExecute) { + return; + } + + if (context.hints && context.hints.createElementsBehavior === false) { + return; + } + + activateDirectEdit(element); + }); + + eventBus.on('autoPlace.end', 500, function(event) { + activateDirectEdit(event.shape); + }); + + + function activateDirectEdit(element, force) { + if (force || + isAny(element, [ 'bpmn:Task', 'bpmn:TextAnnotation' ]) || + isCollapsedSubProcess(element)) { + + directEditing.activate(element); + } + } + +} + +LabelEditingProvider.$inject = [ + 'eventBus', + 'bpmnFactory', + 'canvas', + 'directEditing', + 'modeling', + 'resizeHandles', + 'textRenderer' +]; + + +/** + * Activate direct editing for activities and text annotations. + * + * @param {djs.model.Base} element + * + * @return {Object} an object with properties bounds (position and size), text and options + */ +LabelEditingProvider.prototype.activate = function(element) { + + // text + var text = getLabel(element); + + if (text === undefined) { + return; + } + + var context = { + text: text + }; + + // bounds + var bounds = this.getEditingBBox(element); + + assign(context, bounds); + + var options = {}; + + // tasks + if ( + isAny(element, [ + 'bpmn:Task', + 'bpmn:Participant', + 'bpmn:Lane', + 'bpmn:CallActivity' + ]) || + isCollapsedSubProcess(element) + ) { + assign(options, { + centerVertically: true + }); + } + + // external labels + if (isLabelExternal(element)) { + assign(options, { + autoResize: true + }); + } + + // text annotations + if (is(element, 'bpmn:TextAnnotation')) { + assign(options, { + resizable: true, + autoResize: true + }); + } + + assign(context, { + options: options + }); + + return context; +}; + + +/** + * Get the editing bounding box based on the element's size and position + * + * @param {djs.model.Base} element + * + * @return {Object} an object containing information about position + * and size (fixed or minimum and/or maximum) + */ +LabelEditingProvider.prototype.getEditingBBox = function(element) { + var canvas = this._canvas; + + var target = element.label || element; + + var bbox = canvas.getAbsoluteBBox(target); + + var mid = { + x: bbox.x + bbox.width / 2, + y: bbox.y + bbox.height / 2 + }; + + // default position + var bounds = { x: bbox.x, y: bbox.y }; + + var zoom = canvas.zoom(); + + var defaultStyle = this._textRenderer.getDefaultStyle(), + externalStyle = this._textRenderer.getExternalStyle(); + + // take zoom into account + var externalFontSize = externalStyle.fontSize * zoom, + externalLineHeight = externalStyle.lineHeight, + defaultFontSize = defaultStyle.fontSize * zoom, + defaultLineHeight = defaultStyle.lineHeight; + + var style = { + fontFamily: this._textRenderer.getDefaultStyle().fontFamily, + fontWeight: this._textRenderer.getDefaultStyle().fontWeight + }; + + // adjust for expanded pools AND lanes + if (is(element, 'bpmn:Lane') || isExpandedPool(element)) { + + assign(bounds, { + width: bbox.height, + height: 30 * zoom, + x: bbox.x - bbox.height / 2 + (15 * zoom), + y: mid.y - (30 * zoom) / 2 + }); + + assign(style, { + fontSize: defaultFontSize + 'px', + lineHeight: defaultLineHeight, + paddingTop: (7 * zoom) + 'px', + paddingBottom: (7 * zoom) + 'px', + paddingLeft: (5 * zoom) + 'px', + paddingRight: (5 * zoom) + 'px', + transform: 'rotate(-90deg)' + }); + } + + + // internal labels for tasks and collapsed call activities, + // sub processes and participants + if (isAny(element, [ 'bpmn:Task', 'bpmn:CallActivity' ]) || + isCollapsedPool(element) || + isCollapsedSubProcess(element)) { + + assign(bounds, { + width: bbox.width, + height: bbox.height + }); + + assign(style, { + fontSize: defaultFontSize + 'px', + lineHeight: defaultLineHeight, + paddingTop: (7 * zoom) + 'px', + paddingBottom: (7 * zoom) + 'px', + paddingLeft: (5 * zoom) + 'px', + paddingRight: (5 * zoom) + 'px' + }); + } + + + // internal labels for expanded sub processes + if (isExpandedSubProcess(element)) { + assign(bounds, { + width: bbox.width, + x: bbox.x + }); + + assign(style, { + fontSize: defaultFontSize + 'px', + lineHeight: defaultLineHeight, + paddingTop: (7 * zoom) + 'px', + paddingBottom: (7 * zoom) + 'px', + paddingLeft: (5 * zoom) + 'px', + paddingRight: (5 * zoom) + 'px' + }); + } + + var width = 90 * zoom, + paddingTop = 7 * zoom, + paddingBottom = 4 * zoom; + + // external labels for events, data elements, gateways, groups and connections + if (target.labelTarget) { + assign(bounds, { + width: width, + height: bbox.height + paddingTop + paddingBottom, + x: mid.x - width / 2, + y: bbox.y - paddingTop + }); + + assign(style, { + fontSize: externalFontSize + 'px', + lineHeight: externalLineHeight, + paddingTop: paddingTop + 'px', + paddingBottom: paddingBottom + 'px' + }); + } + + // external label not yet created + if (isLabelExternal(target) + && !hasExternalLabel(target) + && !isLabel(target)) { + + var externalLabelMid = getExternalLabelMid(element); + + var absoluteBBox = canvas.getAbsoluteBBox({ + x: externalLabelMid.x, + y: externalLabelMid.y, + width: 0, + height: 0 + }); + + var height = externalFontSize + paddingTop + paddingBottom; + + assign(bounds, { + width: width, + height: height, + x: absoluteBBox.x - width / 2, + y: absoluteBBox.y - height / 2 + }); + + assign(style, { + fontSize: externalFontSize + 'px', + lineHeight: externalLineHeight, + paddingTop: paddingTop + 'px', + paddingBottom: paddingBottom + 'px' + }); + } + + // text annotations + if (is(element, 'bpmn:TextAnnotation')) { + assign(bounds, { + width: bbox.width, + height: bbox.height, + minWidth: 30 * zoom, + minHeight: 10 * zoom + }); + + assign(style, { + textAlign: 'left', + paddingTop: (5 * zoom) + 'px', + paddingBottom: (7 * zoom) + 'px', + paddingLeft: (7 * zoom) + 'px', + paddingRight: (5 * zoom) + 'px', + fontSize: defaultFontSize + 'px', + lineHeight: defaultLineHeight + }); + } + + return { bounds: bounds, style: style }; +}; + + +LabelEditingProvider.prototype.update = function( + element, newLabel, + activeContextText, bounds) { + + var newBounds, + bbox; + + if (is(element, 'bpmn:TextAnnotation')) { + + bbox = this._canvas.getAbsoluteBBox(element); + + newBounds = { + x: element.x, + y: element.y, + width: element.width / bbox.width * bounds.width, + height: element.height / bbox.height * bounds.height + }; + } + + if (isEmptyText(newLabel)) { + newLabel = null; + } + + this._modeling.updateLabel(element, newLabel, newBounds); +}; + + + +// helpers ////////////////////// + +function isCollapsedSubProcess(element) { + return is(element, 'bpmn:SubProcess') && !isExpanded(element); +} + +function isExpandedSubProcess(element) { + return is(element, 'bpmn:SubProcess') && isExpanded(element); +} + +function isCollapsedPool(element) { + return is(element, 'bpmn:Participant') && !isExpanded(element); +} + +function isExpandedPool(element) { + return is(element, 'bpmn:Participant') && isExpanded(element); +} + +function isEmptyText(label) { + return !label || !label.trim(); +} \ No newline at end of file diff --git a/lib/features/label-editing/LabelUtil.js b/lib/features/label-editing/LabelUtil.js new file mode 100644 index 0000000..25f75bd --- /dev/null +++ b/lib/features/label-editing/LabelUtil.js @@ -0,0 +1,67 @@ +import { is } from '../../util/ModelUtil'; + +function getLabelAttr(semantic) { + if ( + is(semantic, 'bpmn:FlowElement') || + is(semantic, 'bpmn:Participant') || + is(semantic, 'bpmn:Lane') || + is(semantic, 'bpmn:SequenceFlow') || + is(semantic, 'bpmn:MessageFlow') || + is(semantic, 'bpmn:DataInput') || + is(semantic, 'bpmn:DataOutput') + ) { + return 'name'; + } + + if (is(semantic, 'bpmn:TextAnnotation')) { + return 'text'; + } + + if (is(semantic, 'bpmn:Group')) { + return 'categoryValueRef'; + } +} + +function getCategoryValue(semantic) { + var categoryValueRef = semantic['categoryValueRef']; + + if (!categoryValueRef) { + return ''; + } + + + return categoryValueRef.value || ''; +} + +export function getLabel(element) { + var semantic = element.businessObject, + attr = getLabelAttr(semantic); + + if (attr) { + + if (attr === 'categoryValueRef') { + + return getCategoryValue(semantic); + } + + return semantic[attr] || ''; + } +} + + +export function setLabel(element, text, isExternal) { + var semantic = element.businessObject, + attr = getLabelAttr(semantic); + + if (attr) { + + if (attr === 'categoryValueRef') { + semantic['categoryValueRef'].value = text; + } else { + semantic[attr] = text; + } + + } + + return element; +} \ No newline at end of file diff --git a/lib/features/label-editing/cmd/UpdateLabelHandler.js b/lib/features/label-editing/cmd/UpdateLabelHandler.js new file mode 100644 index 0000000..692da3d --- /dev/null +++ b/lib/features/label-editing/cmd/UpdateLabelHandler.js @@ -0,0 +1,165 @@ +import { + setLabel, + getLabel +} from '../LabelUtil'; + +import { + getExternalLabelMid, + isLabelExternal, + hasExternalLabel, + isLabel +} from '../../../util/LabelUtil'; + +import { + getDi, + is +} from '../../../util/ModelUtil'; + +var NULL_DIMENSIONS = { + width: 0, + height: 0 +}; + + +/** + * A handler that updates the text of a BPMN element. + */ +export default function UpdateLabelHandler(modeling, textRenderer, bpmnFactory) { + + /** + * Creates an empty `diLabel` attribute for embedded labels. + * + * @param {djs.model.Base} element + * @param {string} text + */ + function ensureInternalLabelDi(element, text) { + if (isLabelExternal(element)) { + return; + } + + var di = getDi(element); + + if (text && !di.label) { + di.label = bpmnFactory.create('bpmndi:BPMNLabel'); + } + + if (!text && di.label) { + delete di.label; + } + } + + + /** + * Set the label and return the changed elements. + * + * Element parameter can be label itself or connection (i.e. sequence flow). + * + * @param {djs.model.Base} element + * @param {string} text + */ + function setText(element, text) { + + // external label if present + var label = element.label || element; + + var labelTarget = element.labelTarget || element; + + setLabel(label, text, labelTarget !== label); + + ensureInternalLabelDi(element, text); + + return [ label, labelTarget ]; + } + + function preExecute(ctx) { + var element = ctx.element, + businessObject = element.businessObject, + newLabel = ctx.newLabel; + + if (!isLabel(element) + && isLabelExternal(element) + && !hasExternalLabel(element) + && !isEmptyText(newLabel)) { + + // create label + var paddingTop = 7; + + var labelCenter = getExternalLabelMid(element); + + labelCenter = { + x: labelCenter.x, + y: labelCenter.y + paddingTop + }; + + modeling.createLabel(element, labelCenter, { + id: businessObject.id + '_label', + businessObject: businessObject, + di: element.di + }); + } + } + + function execute(ctx) { + ctx.oldLabel = getLabel(ctx.element); + return setText(ctx.element, ctx.newLabel); + } + + function revert(ctx) { + return setText(ctx.element, ctx.oldLabel); + } + + function postExecute(ctx) { + var element = ctx.element, + label = element.label || element, + newLabel = ctx.newLabel, + newBounds = ctx.newBounds, + hints = ctx.hints || {}; + + // ignore internal labels for elements except text annotations + if (!isLabel(label) && !is(label, 'bpmn:TextAnnotation')) { + return; + } + + if (isLabel(label) && isEmptyText(newLabel)) { + + if (hints.removeShape !== false) { + modeling.removeShape(label, { unsetLabel: false }); + } + + return; + } + + var text = getLabel(label); + + // resize element based on label _or_ pre-defined bounds + if (typeof newBounds === 'undefined') { + newBounds = textRenderer.getExternalLabelBounds(label, text); + } + + // setting newBounds to false or _null_ will + // disable the postExecute resize operation + if (newBounds) { + modeling.resizeShape(label, newBounds, NULL_DIMENSIONS); + } + } + + // API + + this.preExecute = preExecute; + this.execute = execute; + this.revert = revert; + this.postExecute = postExecute; +} + +UpdateLabelHandler.$inject = [ + 'modeling', + 'textRenderer', + 'bpmnFactory' +]; + + +// helpers /////////////////////// + +function isEmptyText(label) { + return !label || !label.trim(); +} \ No newline at end of file diff --git a/lib/features/label-editing/index.js b/lib/features/label-editing/index.js new file mode 100644 index 0000000..e22cf29 --- /dev/null +++ b/lib/features/label-editing/index.js @@ -0,0 +1,21 @@ +import ChangeSupportModule from 'diagram-js/lib/features/change-support'; +import ResizeModule from 'diagram-js/lib/features/resize'; +import DirectEditingModule from 'diagram-js-direct-editing'; + +import LabelEditingProvider from './LabelEditingProvider'; +import LabelEditingPreview from './LabelEditingPreview'; + + +export default { + __depends__: [ + ChangeSupportModule, + ResizeModule, + DirectEditingModule + ], + __init__: [ + 'labelEditingProvider', + 'labelEditingPreview' + ], + labelEditingProvider: [ 'type', LabelEditingProvider ], + labelEditingPreview: [ 'type', LabelEditingPreview ] +}; diff --git a/lib/features/modeling/BpmnFactory.js b/lib/features/modeling/BpmnFactory.js new file mode 100644 index 0000000..2cb1257 --- /dev/null +++ b/lib/features/modeling/BpmnFactory.js @@ -0,0 +1,127 @@ +import { + map, + assign, + pick +} from 'min-dash'; + +import { + isAny +} from './util/ModelingUtil'; + +import { + is +} from '../../util/ModelUtil'; + + +export default function BpmnFactory(moddle) { + this._model = moddle; +} + +BpmnFactory.$inject = [ 'moddle' ]; + + +BpmnFactory.prototype._needsId = function(element) { + return isAny(element, [ + 'bpmn:RootElement', + 'bpmn:FlowElement', + 'bpmn:MessageFlow', + 'bpmn:DataAssociation', + 'bpmn:Artifact', + 'bpmn:Participant', + 'bpmn:Lane', + 'bpmn:LaneSet', + 'bpmn:Process', + 'bpmn:Collaboration', + 'bpmndi:BPMNShape', + 'bpmndi:BPMNEdge', + 'bpmndi:BPMNDiagram', + 'bpmndi:BPMNPlane', + 'bpmn:Property', + 'bpmn:CategoryValue' + ]); +}; + +BpmnFactory.prototype._ensureId = function(element) { + if (element.id) { + this._model.ids.claim(element.id, element); + return; + } + + // generate semantic ids for elements + // bpmn:SequenceFlow -> SequenceFlow_ID + var prefix; + + if (is(element, 'bpmn:Activity')) { + prefix = 'Activity'; + } else if (is(element, 'bpmn:Event')) { + prefix = 'Event'; + } else if (is(element, 'bpmn:Gateway')) { + prefix = 'Gateway'; + } else if (isAny(element, [ 'bpmn:SequenceFlow', 'bpmn:MessageFlow' ])) { + prefix = 'Flow'; + } else { + prefix = (element.$type || '').replace(/^[^:]*:/g, ''); + } + + prefix += '_'; + + if (!element.id && this._needsId(element)) { + element.id = this._model.ids.nextPrefixed(prefix, element); + } +}; + + +BpmnFactory.prototype.create = function(type, attrs) { + var element = this._model.create(type, attrs || {}); + + this._ensureId(element); + + return element; +}; + + +BpmnFactory.prototype.createDiLabel = function() { + return this.create('bpmndi:BPMNLabel', { + bounds: this.createDiBounds() + }); +}; + + +BpmnFactory.prototype.createDiShape = function(semantic, attrs) { + return this.create('bpmndi:BPMNShape', assign({ + bpmnElement: semantic, + bounds: this.createDiBounds() + }, attrs)); +}; + + +BpmnFactory.prototype.createDiBounds = function(bounds) { + return this.create('dc:Bounds', bounds); +}; + + +BpmnFactory.prototype.createDiWaypoints = function(waypoints) { + var self = this; + + return map(waypoints, function(pos) { + return self.createDiWaypoint(pos); + }); +}; + +BpmnFactory.prototype.createDiWaypoint = function(point) { + return this.create('dc:Point', pick(point, [ 'x', 'y' ])); +}; + + +BpmnFactory.prototype.createDiEdge = function(semantic, attrs) { + return this.create('bpmndi:BPMNEdge', assign({ + bpmnElement: semantic, + waypoint: this.createDiWaypoints([]) + }, attrs)); +}; + +BpmnFactory.prototype.createDiPlane = function(semantic, attrs) { + return this.create('bpmndi:BPMNPlane', assign({ + bpmnElement: semantic + }, attrs)); +}; \ No newline at end of file diff --git a/lib/features/modeling/BpmnLayouter.js b/lib/features/modeling/BpmnLayouter.js new file mode 100644 index 0000000..0f5d769 --- /dev/null +++ b/lib/features/modeling/BpmnLayouter.js @@ -0,0 +1,399 @@ +import inherits from 'inherits-browser'; + +import { + assign +} from 'min-dash'; + +import BaseLayouter from 'diagram-js/lib/layout/BaseLayouter'; + +import { + repairConnection, + withoutRedundantPoints +} from 'diagram-js/lib/layout/ManhattanLayout'; + +import { + getMid, + getOrientation +} from 'diagram-js/lib/layout/LayoutUtil'; + +import { + isExpanded +} from '../../util/DiUtil'; + +import { is } from '../../util/ModelUtil'; + +var ATTACH_ORIENTATION_PADDING = -10, + BOUNDARY_TO_HOST_THRESHOLD = 40; + +var oppositeOrientationMapping = { + 'top': 'bottom', + 'top-right': 'bottom-left', + 'top-left': 'bottom-right', + 'right': 'left', + 'bottom': 'top', + 'bottom-right': 'top-left', + 'bottom-left': 'top-right', + 'left': 'right' +}; + +var orientationDirectionMapping = { + top: 't', + right: 'r', + bottom: 'b', + left: 'l' +}; + + +export default function BpmnLayouter() {} + +inherits(BpmnLayouter, BaseLayouter); + + +BpmnLayouter.prototype.layoutConnection = function(connection, hints) { + if (!hints) { + hints = {}; + } + + var source = hints.source || connection.source, + target = hints.target || connection.target, + waypoints = hints.waypoints || connection.waypoints, + connectionStart = hints.connectionStart, + connectionEnd = hints.connectionEnd; + + var manhattanOptions, + updatedWaypoints; + + if (!connectionStart) { + connectionStart = getConnectionDocking(waypoints && waypoints[ 0 ], source); + } + + if (!connectionEnd) { + connectionEnd = getConnectionDocking(waypoints && waypoints[ waypoints.length - 1 ], target); + } + + // TODO(nikku): support vertical modeling + // and invert preferredLayouts accordingly + + if (is(connection, 'bpmn:Association') || + is(connection, 'bpmn:DataAssociation')) { + + if (waypoints && !isCompensationAssociation(source, target)) { + return [].concat([ connectionStart ], waypoints.slice(1, -1), [ connectionEnd ]); + } + } + + if (is(connection, 'bpmn:MessageFlow')) { + manhattanOptions = getMessageFlowManhattanOptions(source, target); + } else if (is(connection, 'bpmn:SequenceFlow') || isCompensationAssociation(source, target)) { + + // layout all connection between flow elements h:h, except for + // (1) outgoing of boundary events -> layout based on attach orientation and target orientation + // (2) incoming/outgoing of gateways -> v:h for outgoing, h:v for incoming + // (3) loops + if (source === target) { + manhattanOptions = { + preferredLayouts: getLoopPreferredLayout(source, connection) + }; + } else if (is(source, 'bpmn:BoundaryEvent')) { + manhattanOptions = { + preferredLayouts: getBoundaryEventPreferredLayouts(source, target, connectionEnd) + }; + } else if (isExpandedSubProcess(source) || isExpandedSubProcess(target)) { + manhattanOptions = getSubProcessManhattanOptions(source); + } else if (is(source, 'bpmn:Gateway')) { + manhattanOptions = { + preferredLayouts: [ 'v:h' ] + }; + } else if (is(target, 'bpmn:Gateway')) { + manhattanOptions = { + preferredLayouts: [ 'h:v' ] + }; + } else { + manhattanOptions = { + preferredLayouts: [ 'h:h' ] + }; + } + } + + if (manhattanOptions) { + manhattanOptions = assign(manhattanOptions, hints); + + updatedWaypoints = withoutRedundantPoints(repairConnection( + source, + target, + connectionStart, + connectionEnd, + waypoints, + manhattanOptions + )); + } + + return updatedWaypoints || [ connectionStart, connectionEnd ]; +}; + + +// helpers ////////// + +function getAttachOrientation(attachedElement) { + var hostElement = attachedElement.host; + + return getOrientation(getMid(attachedElement), hostElement, ATTACH_ORIENTATION_PADDING); +} + +function getMessageFlowManhattanOptions(source, target) { + return { + preferredLayouts: [ 'straight', 'v:v' ], + preserveDocking: getMessageFlowPreserveDocking(source, target) + }; +} + +function getMessageFlowPreserveDocking(source, target) { + + // (1) docking element connected to participant has precedence + if (is(target, 'bpmn:Participant')) { + return 'source'; + } + + if (is(source, 'bpmn:Participant')) { + return 'target'; + } + + // (2) docking element connected to expanded sub-process has precedence + if (isExpandedSubProcess(target)) { + return 'source'; + } + + if (isExpandedSubProcess(source)) { + return 'target'; + } + + // (3) docking event has precedence + if (is(target, 'bpmn:Event')) { + return 'target'; + } + + if (is(source, 'bpmn:Event')) { + return 'source'; + } + + return null; +} + +function getSubProcessManhattanOptions(source) { + return { + preferredLayouts: [ 'straight', 'h:h' ], + preserveDocking: getSubProcessPreserveDocking(source) + }; +} + +function getSubProcessPreserveDocking(source) { + return isExpandedSubProcess(source) ? 'target' : 'source'; +} + +function getConnectionDocking(point, shape) { + return point ? (point.original || point) : getMid(shape); +} + +function isCompensationAssociation(source, target) { + return is(target, 'bpmn:Activity') && + is(source, 'bpmn:BoundaryEvent') && + target.businessObject.isForCompensation; +} + +function isExpandedSubProcess(element) { + return is(element, 'bpmn:SubProcess') && isExpanded(element); +} + +function isSame(a, b) { + return a === b; +} + +function isAnyOrientation(orientation, orientations) { + return orientations.indexOf(orientation) !== -1; +} + +function getHorizontalOrientation(orientation) { + var matches = /right|left/.exec(orientation); + + return matches && matches[0]; +} + +function getVerticalOrientation(orientation) { + var matches = /top|bottom/.exec(orientation); + + return matches && matches[0]; +} + +function isOppositeOrientation(a, b) { + return oppositeOrientationMapping[a] === b; +} + +function isOppositeHorizontalOrientation(a, b) { + var horizontalOrientation = getHorizontalOrientation(a); + + var oppositeHorizontalOrientation = oppositeOrientationMapping[horizontalOrientation]; + + return b.indexOf(oppositeHorizontalOrientation) !== -1; +} + +function isOppositeVerticalOrientation(a, b) { + var verticalOrientation = getVerticalOrientation(a); + + var oppositeVerticalOrientation = oppositeOrientationMapping[verticalOrientation]; + + return b.indexOf(oppositeVerticalOrientation) !== -1; +} + +function isHorizontalOrientation(orientation) { + return orientation === 'right' || orientation === 'left'; +} + +function getLoopPreferredLayout(source, connection) { + var waypoints = connection.waypoints; + + var orientation = waypoints && waypoints.length && getOrientation(waypoints[0], source); + + if (orientation === 'top') { + return [ 't:r' ]; + } else if (orientation === 'right') { + return [ 'r:b' ]; + } else if (orientation === 'left') { + return [ 'l:t' ]; + } + + return [ 'b:l' ]; +} + +function getBoundaryEventPreferredLayouts(source, target, end) { + var sourceMid = getMid(source), + targetMid = getMid(target), + attachOrientation = getAttachOrientation(source), + sourceLayout, + targetLayout; + + var isLoop = isSame(source.host, target); + + var attachedToSide = isAnyOrientation(attachOrientation, [ 'top', 'right', 'bottom', 'left' ]); + + var targetOrientation = getOrientation(targetMid, sourceMid, { + x: source.width / 2 + target.width / 2, + y: source.height / 2 + target.height / 2 + }); + + if (isLoop) { + return getBoundaryEventLoopLayout(attachOrientation, attachedToSide, source, target, end); + } + + // source layout + sourceLayout = getBoundaryEventSourceLayout(attachOrientation, targetOrientation, attachedToSide); + + // target layout + targetLayout = getBoundaryEventTargetLayout(attachOrientation, targetOrientation, attachedToSide); + + return [ sourceLayout + ':' + targetLayout ]; +} + +function getBoundaryEventLoopLayout(attachOrientation, attachedToSide, source, target, end) { + var orientation = attachedToSide ? attachOrientation : getVerticalOrientation(attachOrientation), + sourceLayout = orientationDirectionMapping[ orientation ], + targetLayout; + + if (attachedToSide) { + if (isHorizontalOrientation(attachOrientation)) { + targetLayout = shouldConnectToSameSide('y', source, target, end) ? 'h' : 'b'; + } else { + targetLayout = shouldConnectToSameSide('x', source, target, end) ? 'v' : 'l'; + } + } else { + targetLayout = 'v'; + } + + return [ sourceLayout + ':' + targetLayout ]; +} + +function shouldConnectToSameSide(axis, source, target, end) { + var threshold = BOUNDARY_TO_HOST_THRESHOLD; + + return !( + areCloseOnAxis(axis, end, target, threshold) || + areCloseOnAxis(axis, end, { + x: target.x + target.width, + y: target.y + target.height + }, threshold) || + areCloseOnAxis(axis, end, getMid(source), threshold) + ); +} + +function areCloseOnAxis(axis, a, b, threshold) { + return Math.abs(a[ axis ] - b[ axis ]) < threshold; +} + +function getBoundaryEventSourceLayout(attachOrientation, targetOrientation, attachedToSide) { + + // attached to either top, right, bottom or left side + if (attachedToSide) { + return orientationDirectionMapping[ attachOrientation ]; + } + + // attached to either top-right, top-left, bottom-right or bottom-left corner + + // same vertical or opposite horizontal orientation + if (isSame( + getVerticalOrientation(attachOrientation), getVerticalOrientation(targetOrientation) + ) || isOppositeOrientation( + getHorizontalOrientation(attachOrientation), getHorizontalOrientation(targetOrientation) + )) { + return orientationDirectionMapping[ getVerticalOrientation(attachOrientation) ]; + } + + // fallback + return orientationDirectionMapping[ getHorizontalOrientation(attachOrientation) ]; +} + +function getBoundaryEventTargetLayout(attachOrientation, targetOrientation, attachedToSide) { + + // attached to either top, right, bottom or left side + if (attachedToSide) { + if (isHorizontalOrientation(attachOrientation)) { + + // orientation is right or left + + // opposite horizontal orientation or same orientation + if ( + isOppositeHorizontalOrientation(attachOrientation, targetOrientation) || + isSame(attachOrientation, targetOrientation) + ) { + return 'h'; + } + + // fallback + return 'v'; + } else { + + // orientation is top or bottom + + // opposite vertical orientation or same orientation + if ( + isOppositeVerticalOrientation(attachOrientation, targetOrientation) || + isSame(attachOrientation, targetOrientation) + ) { + return 'v'; + } + + // fallback + return 'h'; + } + } + + // attached to either top-right, top-left, bottom-right or bottom-left corner + + // orientation is right, left + // or same vertical orientation but also right or left + if (isHorizontalOrientation(targetOrientation) || + (isSame(getVerticalOrientation(attachOrientation), getVerticalOrientation(targetOrientation)) && + getHorizontalOrientation(targetOrientation))) { + return 'h'; + } else { + return 'v'; + } +} diff --git a/lib/features/modeling/BpmnUpdater.js b/lib/features/modeling/BpmnUpdater.js new file mode 100644 index 0000000..6eb8ba4 --- /dev/null +++ b/lib/features/modeling/BpmnUpdater.js @@ -0,0 +1,762 @@ +import { + assign, + forEach +} from 'min-dash'; + +import inherits from 'inherits-browser'; + +import { + remove as collectionRemove, + add as collectionAdd +} from 'diagram-js/lib/util/Collections'; + +import { + Label +} from 'diagram-js/lib/model'; + +import { + getBusinessObject, + getDi, + is +} from '../../util/ModelUtil'; + +import { + isAny +} from './util/ModelingUtil'; + +import { + delta +} from 'diagram-js/lib/util/PositionUtil'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +/** + * A handler responsible for updating the underlying BPMN 2.0 XML + DI + * once changes on the diagram happen + */ +export default function BpmnUpdater( + eventBus, bpmnFactory, connectionDocking, + translate) { + + CommandInterceptor.call(this, eventBus); + + this._bpmnFactory = bpmnFactory; + this._translate = translate; + + var self = this; + + + + // connection cropping ////////////////////// + + // crop connection ends during create/update + function cropConnection(e) { + var context = e.context, + hints = context.hints || {}, + connection; + + if (!context.cropped && hints.createElementsBehavior !== false) { + connection = context.connection; + connection.waypoints = connectionDocking.getCroppedWaypoints(connection); + context.cropped = true; + } + } + + this.executed([ + 'connection.layout', + 'connection.create' + ], cropConnection); + + this.reverted([ 'connection.layout' ], function(e) { + delete e.context.cropped; + }); + + + + // BPMN + DI update ////////////////////// + + + // update parent + function updateParent(e) { + var context = e.context; + + self.updateParent(context.shape || context.connection, context.oldParent); + } + + function reverseUpdateParent(e) { + var context = e.context; + + var element = context.shape || context.connection, + + // oldParent is the (old) new parent, because we are undoing + oldParent = context.parent || context.newParent; + + self.updateParent(element, oldParent); + } + + this.executed([ + 'shape.move', + 'shape.create', + 'shape.delete', + 'connection.create', + 'connection.move', + 'connection.delete' + ], ifBpmn(updateParent)); + + this.reverted([ + 'shape.move', + 'shape.create', + 'shape.delete', + 'connection.create', + 'connection.move', + 'connection.delete' + ], ifBpmn(reverseUpdateParent)); + + /* + * ## Updating Parent + * + * When morphing a Process into a Collaboration or vice-versa, + * make sure that both the *semantic* and *di* parent of each element + * is updated. + * + */ + function updateRoot(event) { + var context = event.context, + oldRoot = context.oldRoot, + children = oldRoot.children; + + forEach(children, function(child) { + if (is(child, 'bpmn:BaseElement')) { + self.updateParent(child); + } + }); + } + + this.executed([ 'canvas.updateRoot' ], updateRoot); + this.reverted([ 'canvas.updateRoot' ], updateRoot); + + + // update bounds + function updateBounds(e) { + var shape = e.context.shape; + + if (!is(shape, 'bpmn:BaseElement')) { + return; + } + + self.updateBounds(shape); + } + + this.executed([ 'shape.move', 'shape.create', 'shape.resize' ], ifBpmn(function(event) { + + // exclude labels because they're handled separately during shape.changed + if (event.context.shape.type === 'label') { + return; + } + + updateBounds(event); + })); + + this.reverted([ 'shape.move', 'shape.create', 'shape.resize' ], ifBpmn(function(event) { + + // exclude labels because they're handled separately during shape.changed + if (event.context.shape.type === 'label') { + return; + } + + updateBounds(event); + })); + + // Handle labels separately. This is necessary, because the label bounds have to be updated + // every time its shape changes, not only on move, create and resize. + eventBus.on('shape.changed', function(event) { + if (event.element.type === 'label') { + updateBounds({ context: { shape: event.element } }); + } + }); + + // attach / detach connection + function updateConnection(e) { + self.updateConnection(e.context); + } + + this.executed([ + 'connection.create', + 'connection.move', + 'connection.delete', + 'connection.reconnect' + ], ifBpmn(updateConnection)); + + this.reverted([ + 'connection.create', + 'connection.move', + 'connection.delete', + 'connection.reconnect' + ], ifBpmn(updateConnection)); + + + // update waypoints + function updateConnectionWaypoints(e) { + self.updateConnectionWaypoints(e.context.connection); + } + + this.executed([ + 'connection.layout', + 'connection.move', + 'connection.updateWaypoints', + ], ifBpmn(updateConnectionWaypoints)); + + this.reverted([ + 'connection.layout', + 'connection.move', + 'connection.updateWaypoints', + ], ifBpmn(updateConnectionWaypoints)); + + // update conditional/default flows + this.executed('connection.reconnect', ifBpmn(function(event) { + var context = event.context, + connection = context.connection, + oldSource = context.oldSource, + newSource = context.newSource, + connectionBo = getBusinessObject(connection), + oldSourceBo = getBusinessObject(oldSource), + newSourceBo = getBusinessObject(newSource); + + // remove condition from connection on reconnect to new source + // if new source can NOT have condional sequence flow + if (connectionBo.conditionExpression && !isAny(newSourceBo, [ + 'bpmn:Activity', + 'bpmn:ExclusiveGateway', + 'bpmn:InclusiveGateway' + ])) { + context.oldConditionExpression = connectionBo.conditionExpression; + + delete connectionBo.conditionExpression; + } + + // remove default from old source flow on reconnect to new source + // if source changed + if (oldSource !== newSource && oldSourceBo.default === connectionBo) { + context.oldDefault = oldSourceBo.default; + + delete oldSourceBo.default; + } + })); + + this.reverted('connection.reconnect', ifBpmn(function(event) { + var context = event.context, + connection = context.connection, + oldSource = context.oldSource, + newSource = context.newSource, + connectionBo = getBusinessObject(connection), + oldSourceBo = getBusinessObject(oldSource), + newSourceBo = getBusinessObject(newSource); + + // add condition to connection on revert reconnect to new source + if (context.oldConditionExpression) { + connectionBo.conditionExpression = context.oldConditionExpression; + } + + // add default to old source on revert reconnect to new source + if (context.oldDefault) { + oldSourceBo.default = context.oldDefault; + + delete newSourceBo.default; + } + })); + + // update attachments + function updateAttachment(e) { + self.updateAttachment(e.context); + } + + this.executed([ 'element.updateAttachment' ], ifBpmn(updateAttachment)); + this.reverted([ 'element.updateAttachment' ], ifBpmn(updateAttachment)); +} + +inherits(BpmnUpdater, CommandInterceptor); + +BpmnUpdater.$inject = [ + 'eventBus', + 'bpmnFactory', + 'connectionDocking', + 'translate' +]; + + +// implementation ////////////////////// + +BpmnUpdater.prototype.updateAttachment = function(context) { + + var shape = context.shape, + businessObject = shape.businessObject, + host = shape.host; + + businessObject.attachedToRef = host && host.businessObject; +}; + +BpmnUpdater.prototype.updateParent = function(element, oldParent) { + + // do not update BPMN 2.0 label parent + if (element instanceof Label) { + return; + } + + // data stores in collaborations are handled separately by DataStoreBehavior + if (is(element, 'bpmn:DataStoreReference') && + element.parent && + is(element.parent, 'bpmn:Collaboration')) { + return; + } + + var parentShape = element.parent; + + var businessObject = element.businessObject, + di = getDi(element), + parentBusinessObject = parentShape && parentShape.businessObject, + parentDi = getDi(parentShape); + + if (is(element, 'bpmn:FlowNode')) { + this.updateFlowNodeRefs(businessObject, parentBusinessObject, oldParent && oldParent.businessObject); + } + + if (is(element, 'bpmn:DataOutputAssociation')) { + if (element.source) { + parentBusinessObject = element.source.businessObject; + } else { + parentBusinessObject = null; + } + } + + if (is(element, 'bpmn:DataInputAssociation')) { + if (element.target) { + parentBusinessObject = element.target.businessObject; + } else { + parentBusinessObject = null; + } + } + + this.updateSemanticParent(businessObject, parentBusinessObject); + + if (is(element, 'bpmn:DataObjectReference') && businessObject.dataObjectRef) { + this.updateSemanticParent(businessObject.dataObjectRef, parentBusinessObject); + } + + this.updateDiParent(di, parentDi); +}; + + +BpmnUpdater.prototype.updateBounds = function(shape) { + + var di = getDi(shape), + embeddedLabelBounds = getEmbeddedLabelBounds(shape); + + // update embedded label bounds if possible + if (embeddedLabelBounds) { + var embeddedLabelBoundsDelta = delta(embeddedLabelBounds, di.get('bounds')); + + assign(embeddedLabelBounds, { + x: shape.x + embeddedLabelBoundsDelta.x, + y: shape.y + embeddedLabelBoundsDelta.y + }); + } + + var target = (shape instanceof Label) ? this._getLabel(di) : di; + + var bounds = target.bounds; + + if (!bounds) { + bounds = this._bpmnFactory.createDiBounds(); + target.set('bounds', bounds); + } + + assign(bounds, { + x: shape.x, + y: shape.y, + width: shape.width, + height: shape.height + }); +}; + +BpmnUpdater.prototype.updateFlowNodeRefs = function(businessObject, newContainment, oldContainment) { + + if (oldContainment === newContainment) { + return; + } + + var oldRefs, newRefs; + + if (is (oldContainment, 'bpmn:Lane')) { + oldRefs = oldContainment.get('flowNodeRef'); + collectionRemove(oldRefs, businessObject); + } + + if (is(newContainment, 'bpmn:Lane')) { + newRefs = newContainment.get('flowNodeRef'); + collectionAdd(newRefs, businessObject); + } +}; + + +// update existing sourceElement and targetElement di information +BpmnUpdater.prototype.updateDiConnection = function(connection, newSource, newTarget) { + var connectionDi = getDi(connection), + newSourceDi = getDi(newSource), + newTargetDi = getDi(newTarget); + + if (connectionDi.sourceElement && connectionDi.sourceElement.bpmnElement !== getBusinessObject(newSource)) { + connectionDi.sourceElement = newSource && newSourceDi; + } + + if (connectionDi.targetElement && connectionDi.targetElement.bpmnElement !== getBusinessObject(newTarget)) { + connectionDi.targetElement = newTarget && newTargetDi; + } + +}; + + +BpmnUpdater.prototype.updateDiParent = function(di, parentDi) { + + if (parentDi && !is(parentDi, 'bpmndi:BPMNPlane')) { + parentDi = parentDi.$parent; + } + + if (di.$parent === parentDi) { + return; + } + + var planeElements = (parentDi || di.$parent).get('planeElement'); + + if (parentDi) { + planeElements.push(di); + di.$parent = parentDi; + } else { + collectionRemove(planeElements, di); + di.$parent = null; + } +}; + +function getDefinitions(element) { + while (element && !is(element, 'bpmn:Definitions')) { + element = element.$parent; + } + + return element; +} + +BpmnUpdater.prototype.getLaneSet = function(container) { + + var laneSet, laneSets; + + // bpmn:Lane + if (is(container, 'bpmn:Lane')) { + laneSet = container.childLaneSet; + + if (!laneSet) { + laneSet = this._bpmnFactory.create('bpmn:LaneSet'); + container.childLaneSet = laneSet; + laneSet.$parent = container; + } + + return laneSet; + } + + // bpmn:Participant + if (is(container, 'bpmn:Participant')) { + container = container.processRef; + } + + // bpmn:FlowElementsContainer + laneSets = container.get('laneSets'); + laneSet = laneSets[0]; + + if (!laneSet) { + laneSet = this._bpmnFactory.create('bpmn:LaneSet'); + laneSet.$parent = container; + laneSets.push(laneSet); + } + + return laneSet; +}; + +BpmnUpdater.prototype.updateSemanticParent = function(businessObject, newParent, visualParent) { + + var containment, + translate = this._translate; + + if (businessObject.$parent === newParent) { + return; + } + + if (is(businessObject, 'bpmn:DataInput') || is(businessObject, 'bpmn:DataOutput')) { + + if (is(newParent, 'bpmn:Participant') && 'processRef' in newParent) { + newParent = newParent.processRef; + } + + // already in correct ioSpecification + if ('ioSpecification' in newParent && newParent.ioSpecification === businessObject.$parent) { + return; + } + } + + if (is(businessObject, 'bpmn:Lane')) { + + if (newParent) { + newParent = this.getLaneSet(newParent); + } + + containment = 'lanes'; + } else + + if (is(businessObject, 'bpmn:FlowElement')) { + + if (newParent) { + + if (is(newParent, 'bpmn:Participant')) { + newParent = newParent.processRef; + } else + + if (is(newParent, 'bpmn:Lane')) { + do { + + // unwrap Lane -> LaneSet -> (Lane | FlowElementsContainer) + newParent = newParent.$parent.$parent; + } while (is(newParent, 'bpmn:Lane')); + + } + } + + containment = 'flowElements'; + + } else + + if (is(businessObject, 'bpmn:Artifact')) { + + while (newParent && + !is(newParent, 'bpmn:Process') && + !is(newParent, 'bpmn:SubProcess') && + !is(newParent, 'bpmn:Collaboration')) { + + if (is(newParent, 'bpmn:Participant')) { + newParent = newParent.processRef; + break; + } else { + newParent = newParent.$parent; + } + } + + containment = 'artifacts'; + } else + + if (is(businessObject, 'bpmn:MessageFlow')) { + containment = 'messageFlows'; + + } else + + if (is(businessObject, 'bpmn:Participant')) { + containment = 'participants'; + + // make sure the participants process is properly attached / detached + // from the XML document + + var process = businessObject.processRef, + definitions; + + if (process) { + definitions = getDefinitions(businessObject.$parent || newParent); + + if (businessObject.$parent) { + collectionRemove(definitions.get('rootElements'), process); + process.$parent = null; + } + + if (newParent) { + collectionAdd(definitions.get('rootElements'), process); + process.$parent = definitions; + } + } + } else + + if (is(businessObject, 'bpmn:DataOutputAssociation')) { + containment = 'dataOutputAssociations'; + } else + + if (is(businessObject, 'bpmn:DataInputAssociation')) { + containment = 'dataInputAssociations'; + } + + if (!containment) { + throw new Error(translate( + 'no parent for {element} in {parent}', + { + element: businessObject.id, + parent: newParent.id + } + )); + } + + var children; + + if (businessObject.$parent) { + + // remove from old parent + children = businessObject.$parent.get(containment); + collectionRemove(children, businessObject); + } + + if (!newParent) { + businessObject.$parent = null; + } else { + + // add to new parent + children = newParent.get(containment); + children.push(businessObject); + businessObject.$parent = newParent; + } + + if (visualParent) { + var diChildren = visualParent.get(containment); + + collectionRemove(children, businessObject); + + if (newParent) { + + if (!diChildren) { + diChildren = []; + newParent.set(containment, diChildren); + } + + diChildren.push(businessObject); + } + } +}; + + +BpmnUpdater.prototype.updateConnectionWaypoints = function(connection) { + var di = getDi(connection); + + di.set('waypoint', this._bpmnFactory.createDiWaypoints(connection.waypoints)); +}; + + +BpmnUpdater.prototype.updateConnection = function(context) { + var connection = context.connection, + businessObject = getBusinessObject(connection), + newSource = connection.source, + newSourceBo = getBusinessObject(newSource), + newTarget = connection.target, + newTargetBo = getBusinessObject(connection.target), + visualParent; + + if (!is(businessObject, 'bpmn:DataAssociation')) { + + var inverseSet = is(businessObject, 'bpmn:SequenceFlow'); + + if (businessObject.sourceRef !== newSourceBo) { + if (inverseSet) { + collectionRemove(businessObject.sourceRef && businessObject.sourceRef.get('outgoing'), businessObject); + + if (newSourceBo && newSourceBo.get('outgoing')) { + newSourceBo.get('outgoing').push(businessObject); + } + } + + businessObject.sourceRef = newSourceBo; + } + + if (businessObject.targetRef !== newTargetBo) { + if (inverseSet) { + collectionRemove(businessObject.targetRef && businessObject.targetRef.get('incoming'), businessObject); + + if (newTargetBo && newTargetBo.get('incoming')) { + newTargetBo.get('incoming').push(businessObject); + } + } + + businessObject.targetRef = newTargetBo; + } + } else + + if (is(businessObject, 'bpmn:DataInputAssociation')) { + + // handle obnoxious isMsome sourceRef + businessObject.get('sourceRef')[0] = newSourceBo; + + visualParent = context.parent || context.newParent || newTargetBo; + + this.updateSemanticParent(businessObject, newTargetBo, visualParent); + } else + + if (is(businessObject, 'bpmn:DataOutputAssociation')) { + visualParent = context.parent || context.newParent || newSourceBo; + + this.updateSemanticParent(businessObject, newSourceBo, visualParent); + + // targetRef = new target + businessObject.targetRef = newTargetBo; + } + + this.updateConnectionWaypoints(connection); + + this.updateDiConnection(connection, newSource, newTarget); +}; + + +// helpers ////////////////////// + +BpmnUpdater.prototype._getLabel = function(di) { + if (!di.label) { + di.label = this._bpmnFactory.createDiLabel(); + } + + return di.label; +}; + + +/** + * Make sure the event listener is only called + * if the touched element is a BPMN element. + * + * @param {Function} fn + * @return {Function} guarded function + */ +function ifBpmn(fn) { + + return function(event) { + + var context = event.context, + element = context.shape || context.connection; + + if (is(element, 'bpmn:BaseElement')) { + fn(event); + } + }; +} + +/** + * Return dc:Bounds of bpmndi:BPMNLabel if exists. + * + * @param {djs.model.shape} shape + * + * @returns {Object|undefined} + */ +function getEmbeddedLabelBounds(shape) { + if (!is(shape, 'bpmn:Activity')) { + return; + } + + var di = getDi(shape); + + if (!di) { + return; + } + + var label = di.get('label'); + + if (!label) { + return; + } + + return label.get('bounds'); +} \ No newline at end of file diff --git a/lib/features/modeling/ElementFactory.js b/lib/features/modeling/ElementFactory.js new file mode 100644 index 0000000..3b97c55 --- /dev/null +++ b/lib/features/modeling/ElementFactory.js @@ -0,0 +1,292 @@ +import { + assign, + forEach, + isObject, + omit +} from 'min-dash'; + +import inherits from 'inherits-browser'; + +import { + getBusinessObject, + getDi, + is +} from '../../util/ModelUtil'; + +import { + isAny +} from '../modeling/util/ModelingUtil'; + +import { + isExpanded +} from '../../util/DiUtil'; + +import BaseElementFactory from 'diagram-js/lib/core/ElementFactory'; + +import { + DEFAULT_LABEL_SIZE +} from '../../util/LabelUtil'; + +import { + ensureCompatDiRef +} from '../../util/CompatibilityUtil'; + + +/** + * A bpmn-aware factory for diagram-js shapes + */ +export default function ElementFactory(bpmnFactory, moddle, translate) { + BaseElementFactory.call(this); + + this._bpmnFactory = bpmnFactory; + this._moddle = moddle; + this._translate = translate; +} + +inherits(ElementFactory, BaseElementFactory); + +ElementFactory.$inject = [ + 'bpmnFactory', + 'moddle', + 'translate' +]; + +ElementFactory.prototype.baseCreate = BaseElementFactory.prototype.create; + +ElementFactory.prototype.create = function(elementType, attrs) { + + // no special magic for labels, + // we assume their businessObjects have already been created + // and wired via attrs + if (elementType === 'label') { + var di = attrs.di || this._bpmnFactory.createDiLabel(); + return this.baseCreate(elementType, assign({ type: 'label', di: di }, DEFAULT_LABEL_SIZE, attrs)); + } + + return this.createBpmnElement(elementType, attrs); +}; + +ElementFactory.prototype.createBpmnElement = function(elementType, attrs) { + var size, + translate = this._translate; + + attrs = assign({}, attrs || {}); + + var businessObject = attrs.businessObject, + di = attrs.di; + + if (!businessObject) { + if (!attrs.type) { + throw new Error(translate('no shape type specified')); + } + + businessObject = this._bpmnFactory.create(attrs.type); + + ensureCompatDiRef(businessObject); + } + + if (!isModdleDi(di)) { + var diAttrs = assign( + {}, + di || {}, + { id: businessObject.id + '_di' } + ); + + if (elementType === 'root') { + di = this._bpmnFactory.createDiPlane(businessObject, diAttrs); + } else + if (elementType === 'connection') { + di = this._bpmnFactory.createDiEdge(businessObject, diAttrs); + } else { + di = this._bpmnFactory.createDiShape(businessObject, diAttrs); + } + } + + if (is(businessObject, 'bpmn:Group')) { + attrs = assign({ + isFrame: true + }, attrs); + } + + attrs = applyAttributes(businessObject, attrs, [ + 'processRef', + 'isInterrupting', + 'associationDirection', + 'isForCompensation' + ]); + + if (attrs.isExpanded) { + attrs = applyAttribute(di, attrs, 'isExpanded'); + } + + if (is(businessObject, 'bpmn:SubProcess')) { + attrs.collapsed = !isExpanded(businessObject, di); + } + + if (is(businessObject, 'bpmn:ExclusiveGateway')) { + di.isMarkerVisible = true; + } + + var eventDefinitions, + newEventDefinition; + + if (attrs.eventDefinitionType) { + eventDefinitions = businessObject.get('eventDefinitions') || []; + newEventDefinition = this._bpmnFactory.create(attrs.eventDefinitionType, attrs.eventDefinitionAttrs); + + if (attrs.eventDefinitionType === 'bpmn:ConditionalEventDefinition') { + newEventDefinition.condition = this._bpmnFactory.create('bpmn:FormalExpression'); + } + + eventDefinitions.push(newEventDefinition); + + newEventDefinition.$parent = businessObject; + businessObject.eventDefinitions = eventDefinitions; + + delete attrs.eventDefinitionType; + } + + size = this.getDefaultSize(businessObject, di); + + attrs = assign({ + id: businessObject.id + }, size, attrs, { + businessObject: businessObject, + di: di + }); + + return this.baseCreate(elementType, attrs); +}; + + +ElementFactory.prototype.getDefaultSize = function(element, di) { + + var bo = getBusinessObject(element); + di = di || getDi(element); + + if (is(bo, 'bpmn:SubProcess')) { + if (isExpanded(bo, di)) { + return { width: 350, height: 200 }; + } else { + return { width: 100, height: 80 }; + } + } + + if (is(bo, 'bpmn:Task')) { + return { width: 100, height: 80 }; + } + + if (is(bo, 'bpmn:Gateway')) { + return { width: 50, height: 50 }; + } + + if (is(bo, 'bpmn:Event')) { + return { width: 36, height: 36 }; + } + + if (is(bo, 'bpmn:Participant')) { + if (isExpanded(bo, di)) { + return { width: 600, height: 250 }; + } else { + return { width: 400, height: 60 }; + } + } + + if (is(bo, 'bpmn:Lane')) { + return { width: 400, height: 100 }; + } + + if (is(bo, 'bpmn:DataObjectReference')) { + return { width: 36, height: 50 }; + } + + if (is(bo, 'bpmn:DataStoreReference')) { + return { width: 50, height: 50 }; + } + + if (is(bo, 'bpmn:TextAnnotation')) { + return { width: 100, height: 30 }; + } + + if (is(bo, 'bpmn:Group')) { + return { width: 300, height: 300 }; + } + + return { width: 100, height: 80 }; +}; + + +/** + * Create participant. + * + * @param {boolean|Object} [attrs] attrs + * + * @returns {djs.model.Shape} + */ +ElementFactory.prototype.createParticipantShape = function(attrs) { + + if (!isObject(attrs)) { + attrs = { isExpanded: attrs }; + } + + attrs = assign({ type: 'bpmn:Participant' }, attrs || {}); + + // participants are expanded by default + if (attrs.isExpanded !== false) { + attrs.processRef = this._bpmnFactory.create('bpmn:Process'); + } + + return this.createShape(attrs); +}; + + +// helpers ////////////////////// + +/** + * Apply attributes from a map to the given element, + * remove attribute from the map on application. + * + * @param {Base} element + * @param {Object} attrs (in/out map of attributes) + * @param {Array} attributeNames name of attributes to apply + * + * @return {Object} changed attrs + */ +function applyAttributes(element, attrs, attributeNames) { + + forEach(attributeNames, function(property) { + attrs = applyAttribute(element, attrs, property); + }); + + return attrs; +} + +/** + * Apply named property to element and drain it from the attrs + * collection. + * + * @param {Base} element + * @param {Object} attrs (in/out map of attributes) + * @param {string} attributeName to apply + * + * @return {Object} changed attrs + */ +function applyAttribute(element, attrs, attributeName) { + if (attrs[attributeName] === undefined) { + return attrs; + } + + element[attributeName] = attrs[attributeName]; + + return omit(attrs, [ attributeName ]); +} + + +function isModdleDi(element) { + return isAny(element, [ + 'bpmndi:BPMNShape', + 'bpmndi:BPMNEdge', + 'bpmndi:BPMNDiagram', + 'bpmndi:BPMNPlane', + ]); +} \ No newline at end of file diff --git a/lib/features/modeling/Modeling.js b/lib/features/modeling/Modeling.js new file mode 100644 index 0000000..c2888d7 --- /dev/null +++ b/lib/features/modeling/Modeling.js @@ -0,0 +1,202 @@ +import inherits from 'inherits-browser'; + +import BaseModeling from 'diagram-js/lib/features/modeling/Modeling'; + +import UpdateModdlePropertiesHandler from './cmd/UpdateModdlePropertiesHandler'; +import UpdatePropertiesHandler from './cmd/UpdatePropertiesHandler'; +import UpdateCanvasRootHandler from './cmd/UpdateCanvasRootHandler'; +import AddLaneHandler from './cmd/AddLaneHandler'; +import SplitLaneHandler from './cmd/SplitLaneHandler'; +import ResizeLaneHandler from './cmd/ResizeLaneHandler'; +import UpdateFlowNodeRefsHandler from './cmd/UpdateFlowNodeRefsHandler'; +import IdClaimHandler from './cmd/IdClaimHandler'; +import SetColorHandler from './cmd/SetColorHandler'; + +import UpdateLabelHandler from '../label-editing/cmd/UpdateLabelHandler'; + + +/** + * BPMN 2.0 modeling features activator + * + * @param {EventBus} eventBus + * @param {ElementFactory} elementFactory + * @param {CommandStack} commandStack + * @param {BpmnRules} bpmnRules + */ +export default function Modeling( + eventBus, elementFactory, commandStack, + bpmnRules) { + + BaseModeling.call(this, eventBus, elementFactory, commandStack); + + this._bpmnRules = bpmnRules; +} + +inherits(Modeling, BaseModeling); + +Modeling.$inject = [ + 'eventBus', + 'elementFactory', + 'commandStack', + 'bpmnRules' +]; + + +Modeling.prototype.getHandlers = function() { + var handlers = BaseModeling.prototype.getHandlers.call(this); + + handlers['element.updateModdleProperties'] = UpdateModdlePropertiesHandler; + handlers['element.updateProperties'] = UpdatePropertiesHandler; + handlers['canvas.updateRoot'] = UpdateCanvasRootHandler; + handlers['lane.add'] = AddLaneHandler; + handlers['lane.resize'] = ResizeLaneHandler; + handlers['lane.split'] = SplitLaneHandler; + handlers['lane.updateRefs'] = UpdateFlowNodeRefsHandler; + handlers['id.updateClaim'] = IdClaimHandler; + handlers['element.setColor'] = SetColorHandler; + handlers['element.updateLabel'] = UpdateLabelHandler; + + return handlers; +}; + + +Modeling.prototype.updateLabel = function(element, newLabel, newBounds, hints) { + this._commandStack.execute('element.updateLabel', { + element: element, + newLabel: newLabel, + newBounds: newBounds, + hints: hints || {} + }); +}; + + +Modeling.prototype.connect = function(source, target, attrs, hints) { + + var bpmnRules = this._bpmnRules; + + if (!attrs) { + attrs = bpmnRules.canConnect(source, target); + } + + if (!attrs) { + return; + } + + return this.createConnection(source, target, attrs, source.parent, hints); +}; + + +Modeling.prototype.updateModdleProperties = function(element, moddleElement, properties) { + this._commandStack.execute('element.updateModdleProperties', { + element: element, + moddleElement: moddleElement, + properties: properties + }); +}; + +Modeling.prototype.updateProperties = function(element, properties) { + this._commandStack.execute('element.updateProperties', { + element: element, + properties: properties + }); +}; + +Modeling.prototype.resizeLane = function(laneShape, newBounds, balanced) { + this._commandStack.execute('lane.resize', { + shape: laneShape, + newBounds: newBounds, + balanced: balanced + }); +}; + +Modeling.prototype.addLane = function(targetLaneShape, location) { + var context = { + shape: targetLaneShape, + location: location + }; + + this._commandStack.execute('lane.add', context); + + return context.newLane; +}; + +Modeling.prototype.splitLane = function(targetLane, count) { + this._commandStack.execute('lane.split', { + shape: targetLane, + count: count + }); +}; + +/** + * Transform the current diagram into a collaboration. + * + * @return {djs.model.Root} the new root element + */ +Modeling.prototype.makeCollaboration = function() { + + var collaborationElement = this._create('root', { + type: 'bpmn:Collaboration' + }); + + var context = { + newRoot: collaborationElement + }; + + this._commandStack.execute('canvas.updateRoot', context); + + return collaborationElement; +}; + +Modeling.prototype.updateLaneRefs = function(flowNodeShapes, laneShapes) { + + this._commandStack.execute('lane.updateRefs', { + flowNodeShapes: flowNodeShapes, + laneShapes: laneShapes + }); +}; + +/** + * Transform the current diagram into a process. + * + * @return {djs.model.Root} the new root element + */ +Modeling.prototype.makeProcess = function() { + + var processElement = this._create('root', { + type: 'bpmn:Process' + }); + + var context = { + newRoot: processElement + }; + + this._commandStack.execute('canvas.updateRoot', context); +}; + + +Modeling.prototype.claimId = function(id, moddleElement) { + this._commandStack.execute('id.updateClaim', { + id: id, + element: moddleElement, + claiming: true + }); +}; + + +Modeling.prototype.unclaimId = function(id, moddleElement) { + this._commandStack.execute('id.updateClaim', { + id: id, + element: moddleElement + }); +}; + +Modeling.prototype.setColor = function(elements, colors) { + if (!elements.length) { + elements = [ elements ]; + } + + this._commandStack.execute('element.setColor', { + elements: elements, + colors: colors + }); +}; diff --git a/lib/features/modeling/behavior/AdaptiveLabelPositioningBehavior.js b/lib/features/modeling/behavior/AdaptiveLabelPositioningBehavior.js new file mode 100644 index 0000000..be52452 --- /dev/null +++ b/lib/features/modeling/behavior/AdaptiveLabelPositioningBehavior.js @@ -0,0 +1,274 @@ +import inherits from 'inherits-browser'; + +import { + getOrientation, + getMid, + asTRBL +} from 'diagram-js/lib/layout/LayoutUtil'; + +import { + substract +} from 'diagram-js/lib/util/Math'; + +import { + hasExternalLabel +} from '../../../util/LabelUtil'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +var ALIGNMENTS = [ + 'top', + 'bottom', + 'left', + 'right' +]; + +var ELEMENT_LABEL_DISTANCE = 10; + +/** + * A component that makes sure that external labels are added + * together with respective elements and properly updated (DI wise) + * during move. + * + * @param {EventBus} eventBus + * @param {Modeling} modeling + */ +export default function AdaptiveLabelPositioningBehavior(eventBus, modeling) { + + CommandInterceptor.call(this, eventBus); + + this.postExecuted([ + 'connection.create', + 'connection.layout', + 'connection.updateWaypoints' + ], function(event) { + var context = event.context, + connection = context.connection, + source = connection.source, + target = connection.target, + hints = context.hints || {}; + + if (hints.createElementsBehavior !== false) { + checkLabelAdjustment(source); + checkLabelAdjustment(target); + } + }); + + + this.postExecuted([ + 'label.create' + ], function(event) { + var context = event.context, + shape = context.shape, + hints = context.hints || {}; + + if (hints.createElementsBehavior !== false) { + checkLabelAdjustment(shape.labelTarget); + } + }); + + + this.postExecuted([ + 'elements.create' + ], function(event) { + var context = event.context, + elements = context.elements, + hints = context.hints || {}; + + if (hints.createElementsBehavior !== false) { + elements.forEach(function(element) { + checkLabelAdjustment(element); + }); + } + }); + + function checkLabelAdjustment(element) { + + // skip non-existing labels + if (!hasExternalLabel(element)) { + return; + } + + var optimalPosition = getOptimalPosition(element); + + // no optimal position found + if (!optimalPosition) { + return; + } + + adjustLabelPosition(element, optimalPosition); + } + + function adjustLabelPosition(element, orientation) { + + var elementMid = getMid(element), + label = element.label, + labelMid = getMid(label); + + // ignore labels that are being created + if (!label.parent) { + return; + } + + var elementTrbl = asTRBL(element); + + var newLabelMid; + + switch (orientation) { + case 'top': + newLabelMid = { + x: elementMid.x, + y: elementTrbl.top - ELEMENT_LABEL_DISTANCE - label.height / 2 + }; + + break; + + case 'left': + + newLabelMid = { + x: elementTrbl.left - ELEMENT_LABEL_DISTANCE - label.width / 2, + y: elementMid.y + }; + + break; + + case 'bottom': + + newLabelMid = { + x: elementMid.x, + y: elementTrbl.bottom + ELEMENT_LABEL_DISTANCE + label.height / 2 + }; + + break; + + case 'right': + + newLabelMid = { + x: elementTrbl.right + ELEMENT_LABEL_DISTANCE + label.width / 2, + y: elementMid.y + }; + + break; + } + + var delta = substract(newLabelMid, labelMid); + + modeling.moveShape(label, delta); + } + +} + +inherits(AdaptiveLabelPositioningBehavior, CommandInterceptor); + +AdaptiveLabelPositioningBehavior.$inject = [ + 'eventBus', + 'modeling' +]; + + +// helpers ////////////////////// + +/** + * Return alignments which are taken by a boundary's host element + * + * @param {Shape} element + * + * @return {Array} + */ +function getTakenHostAlignments(element) { + + var hostElement = element.host, + elementMid = getMid(element), + hostOrientation = getOrientation(elementMid, hostElement); + + var freeAlignments; + + // check whether there is a multi-orientation, e.g. 'top-left' + if (hostOrientation.indexOf('-') >= 0) { + freeAlignments = hostOrientation.split('-'); + } else { + freeAlignments = [ hostOrientation ]; + } + + var takenAlignments = ALIGNMENTS.filter(function(alignment) { + + return freeAlignments.indexOf(alignment) === -1; + }); + + return takenAlignments; + +} + +/** + * Return alignments which are taken by related connections + * + * @param {Shape} element + * + * @return {Array} + */ +function getTakenConnectionAlignments(element) { + + var elementMid = getMid(element); + + var takenAlignments = [].concat( + element.incoming.map(function(c) { + return c.waypoints[c.waypoints.length - 2 ]; + }), + element.outgoing.map(function(c) { + return c.waypoints[1]; + }) + ).map(function(point) { + return getApproximateOrientation(elementMid, point); + }); + + return takenAlignments; +} + +/** + * Return the optimal label position around an element + * or _undefined_, if none was found. + * + * @param {Shape} element + * + * @return {string} positioning identifier + */ +function getOptimalPosition(element) { + + var labelMid = getMid(element.label); + + var elementMid = getMid(element); + + var labelOrientation = getApproximateOrientation(elementMid, labelMid); + + if (!isAligned(labelOrientation)) { + return; + } + + var takenAlignments = getTakenConnectionAlignments(element); + + if (element.host) { + var takenHostAlignments = getTakenHostAlignments(element); + + takenAlignments = takenAlignments.concat(takenHostAlignments); + } + + var freeAlignments = ALIGNMENTS.filter(function(alignment) { + + return takenAlignments.indexOf(alignment) === -1; + }); + + // NOTHING TO DO; label already aligned a.O.K. + if (freeAlignments.indexOf(labelOrientation) !== -1) { + return; + } + + return freeAlignments[0]; +} + +function getApproximateOrientation(p0, p1) { + return getOrientation(p1, p0, 5); +} + +function isAligned(orientation) { + return ALIGNMENTS.indexOf(orientation) !== -1; +} diff --git a/lib/features/modeling/behavior/AppendBehavior.js b/lib/features/modeling/behavior/AppendBehavior.js new file mode 100644 index 0000000..ffafd57 --- /dev/null +++ b/lib/features/modeling/behavior/AppendBehavior.js @@ -0,0 +1,42 @@ +import inherits from 'inherits-browser'; + +import { is } from '../../../util/ModelUtil'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + + +export default function AppendBehavior(eventBus, elementFactory, bpmnRules) { + + CommandInterceptor.call(this, eventBus); + + // assign correct shape position unless already set + + this.preExecute('shape.append', function(context) { + + var source = context.source, + shape = context.shape; + + if (!context.position) { + + if (is(shape, 'bpmn:TextAnnotation')) { + context.position = { + x: source.x + source.width / 2 + 75, + y: source.y - (50) - shape.height / 2 + }; + } else { + context.position = { + x: source.x + source.width + 80 + shape.width / 2, + y: source.y + source.height / 2 + }; + } + } + }, true); +} + +inherits(AppendBehavior, CommandInterceptor); + +AppendBehavior.$inject = [ + 'eventBus', + 'elementFactory', + 'bpmnRules' +]; \ No newline at end of file diff --git a/lib/features/modeling/behavior/AssociationBehavior.js b/lib/features/modeling/behavior/AssociationBehavior.js new file mode 100644 index 0000000..d86c4d2 --- /dev/null +++ b/lib/features/modeling/behavior/AssociationBehavior.js @@ -0,0 +1,35 @@ +import inherits from 'inherits-browser'; + +import { is } from '../../../util/ModelUtil'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { + filter, + forEach +} from 'min-dash'; + + +export default function AssociationBehavior(injector, modeling) { + injector.invoke(CommandInterceptor, this); + + this.postExecute('shape.move', function(context) { + var newParent = context.newParent, + shape = context.shape; + + var associations = filter(shape.incoming.concat(shape.outgoing), function(connection) { + return is(connection, 'bpmn:Association'); + }); + + forEach(associations, function(association) { + modeling.moveConnection(association, { x: 0, y: 0 }, newParent); + }); + }, true); +} + +inherits(AssociationBehavior, CommandInterceptor); + +AssociationBehavior.$inject = [ + 'injector', + 'modeling' +]; \ No newline at end of file diff --git a/lib/features/modeling/behavior/AttachEventBehavior.js b/lib/features/modeling/behavior/AttachEventBehavior.js new file mode 100644 index 0000000..f089fbd --- /dev/null +++ b/lib/features/modeling/behavior/AttachEventBehavior.js @@ -0,0 +1,98 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { getBusinessObject } from '../../../util/ModelUtil'; + +import { isAny } from '../util/ModelingUtil'; + +import { isLabel } from '../../../util/LabelUtil'; + +var LOW_PRIORITY = 500; + + +/** + * Replace intermediate event with boundary event when creating or moving results in attached event. + */ +export default function AttachEventBehavior(bpmnReplace, injector) { + injector.invoke(CommandInterceptor, this); + + this._bpmnReplace = bpmnReplace; + + var self = this; + + this.postExecuted('elements.create', LOW_PRIORITY, function(context) { + var elements = context.elements; + + elements = elements.filter(function(shape) { + var host = shape.host; + + return shouldReplace(shape, host); + }); + + if (elements.length !== 1) { + return; + } + + elements.map(function(element) { + return elements.indexOf(element); + }).forEach(function(index) { + var host = elements[ index ]; + + context.elements[ index ] = self.replaceShape(elements[ index ], host); + }); + }, true); + + + this.preExecute('elements.move', LOW_PRIORITY, function(context) { + var shapes = context.shapes, + host = context.newHost; + + if (shapes.length !== 1) { + return; + } + + var shape = shapes[0]; + + if (shouldReplace(shape, host)) { + context.shapes = [ self.replaceShape(shape, host) ]; + } + }, true); +} + +AttachEventBehavior.$inject = [ + 'bpmnReplace', + 'injector' +]; + +inherits(AttachEventBehavior, CommandInterceptor); + +AttachEventBehavior.prototype.replaceShape = function(shape, host) { + var eventDefinition = getEventDefinition(shape); + + var boundaryEvent = { + type: 'bpmn:BoundaryEvent', + host: host + }; + + if (eventDefinition) { + boundaryEvent.eventDefinitionType = eventDefinition.$type; + } + + return this._bpmnReplace.replaceElement(shape, boundaryEvent, { layoutConnection: false }); +}; + + +// helpers ////////// + +function getEventDefinition(element) { + var businessObject = getBusinessObject(element), + eventDefinitions = businessObject.eventDefinitions; + + return eventDefinitions && eventDefinitions[0]; +} + +function shouldReplace(shape, host) { + return !isLabel(shape) && + isAny(shape, [ 'bpmn:IntermediateThrowEvent', 'bpmn:IntermediateCatchEvent' ]) && !!host; +} diff --git a/lib/features/modeling/behavior/BoundaryEventBehavior.js b/lib/features/modeling/behavior/BoundaryEventBehavior.js new file mode 100644 index 0000000..26ef21f --- /dev/null +++ b/lib/features/modeling/behavior/BoundaryEventBehavior.js @@ -0,0 +1,68 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { is } from '../../../util/ModelUtil'; + +import { + filter, + forEach +} from 'min-dash'; + + +/** + * BPMN specific boundary event behavior + */ +export default function BoundaryEventBehavior(eventBus, modeling) { + + CommandInterceptor.call(this, eventBus); + + function getBoundaryEvents(element) { + return filter(element.attachers, function(attacher) { + return is(attacher, 'bpmn:BoundaryEvent'); + }); + } + + // remove after connecting to event-based gateway + this.postExecute('connection.create', function(event) { + var source = event.context.source, + target = event.context.target, + boundaryEvents = getBoundaryEvents(target); + + if ( + is(source, 'bpmn:EventBasedGateway') && + is(target, 'bpmn:ReceiveTask') && + boundaryEvents.length > 0 + ) { + modeling.removeElements(boundaryEvents); + } + + }); + + // remove after replacing connected gateway with event-based gateway + this.postExecute('connection.reconnect', function(event) { + var oldSource = event.context.oldSource, + newSource = event.context.newSource; + + if (is(oldSource, 'bpmn:Gateway') && + is(newSource, 'bpmn:EventBasedGateway')) { + forEach(newSource.outgoing, function(connection) { + var target = connection.target, + attachedboundaryEvents = getBoundaryEvents(target); + + if (is(target, 'bpmn:ReceiveTask') && + attachedboundaryEvents.length > 0) { + modeling.removeElements(attachedboundaryEvents); + } + }); + } + }); + +} + +BoundaryEventBehavior.$inject = [ + 'eventBus', + 'modeling' +]; + +inherits(BoundaryEventBehavior, CommandInterceptor); \ No newline at end of file diff --git a/lib/features/modeling/behavior/CreateBehavior.js b/lib/features/modeling/behavior/CreateBehavior.js new file mode 100644 index 0000000..98aaaed --- /dev/null +++ b/lib/features/modeling/behavior/CreateBehavior.js @@ -0,0 +1,28 @@ +import inherits from 'inherits-browser'; + +import { is } from '../../../util/ModelUtil'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { getParent } from '../util/ModelingUtil'; + + +export default function CreateBehavior(injector) { + injector.invoke(CommandInterceptor, this); + + this.preExecute('shape.create', 1500, function(event) { + var context = event.context, + parent = context.parent, + shape = context.shape; + + if (is(parent, 'bpmn:Lane') && !is(shape, 'bpmn:Lane')) { + context.parent = getParent(parent, 'bpmn:Participant'); + } + }); + +} + + +CreateBehavior.$inject = [ 'injector' ]; + +inherits(CreateBehavior, CommandInterceptor); \ No newline at end of file diff --git a/lib/features/modeling/behavior/CreateDataObjectBehavior.js b/lib/features/modeling/behavior/CreateDataObjectBehavior.js new file mode 100644 index 0000000..b0387f0 --- /dev/null +++ b/lib/features/modeling/behavior/CreateDataObjectBehavior.js @@ -0,0 +1,38 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { is } from '../../../util/ModelUtil'; + + +/** + * BPMN specific create data object behavior + */ +export default function CreateDataObjectBehavior(eventBus, bpmnFactory, moddle) { + + CommandInterceptor.call(this, eventBus); + + this.preExecute('shape.create', function(event) { + + var context = event.context, + shape = context.shape; + + if (is(shape, 'bpmn:DataObjectReference') && shape.type !== 'label') { + + // create a DataObject every time a DataObjectReference is created + var dataObject = bpmnFactory.create('bpmn:DataObject'); + + // set the reference to the DataObject + shape.businessObject.dataObjectRef = dataObject; + } + }); + +} + +CreateDataObjectBehavior.$inject = [ + 'eventBus', + 'bpmnFactory', + 'moddle' +]; + +inherits(CreateDataObjectBehavior, CommandInterceptor); \ No newline at end of file diff --git a/lib/features/modeling/behavior/CreateParticipantBehavior.js b/lib/features/modeling/behavior/CreateParticipantBehavior.js new file mode 100644 index 0000000..296c4d3 --- /dev/null +++ b/lib/features/modeling/behavior/CreateParticipantBehavior.js @@ -0,0 +1,230 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { getBusinessObject, is } from '../../../util/ModelUtil'; + +import { isLabel } from '../../../util/LabelUtil'; + +import { getBBox } from 'diagram-js/lib/util/Elements'; + +import { + assign, + find +} from 'min-dash'; + +import { asTRBL } from 'diagram-js/lib/layout/LayoutUtil'; + +var HORIZONTAL_PARTICIPANT_PADDING = 20, + VERTICAL_PARTICIPANT_PADDING = 20; + +export var PARTICIPANT_BORDER_WIDTH = 30; + +var HIGH_PRIORITY = 2000; + + +/** + * BPMN-specific behavior for creating participants. + */ +export default function CreateParticipantBehavior(canvas, eventBus, modeling) { + CommandInterceptor.call(this, eventBus); + + // fit participant + eventBus.on([ + 'create.start', + 'shape.move.start' + ], HIGH_PRIORITY, function(event) { + var context = event.context, + shape = context.shape, + rootElement = canvas.getRootElement(); + + if (!is(shape, 'bpmn:Participant') || + !is(rootElement, 'bpmn:Process') || + !rootElement.children.length) { + return; + } + + // ignore connections, groups and labels + var children = rootElement.children.filter(function(element) { + return !is(element, 'bpmn:Group') && + !isLabel(element) && + !isConnection(element); + }); + + // ensure for available children to calculate bounds + if (!children.length) { + return; + } + + var childrenBBox = getBBox(children); + + var participantBounds = getParticipantBounds(shape, childrenBBox); + + // assign width and height + assign(shape, participantBounds); + + // assign create constraints + context.createConstraints = getParticipantCreateConstraints(shape, childrenBBox); + }); + + // force hovering process when creating first participant + eventBus.on('create.start', HIGH_PRIORITY, function(event) { + var context = event.context, + shape = context.shape, + rootElement = canvas.getRootElement(), + rootElementGfx = canvas.getGraphics(rootElement); + + function ensureHoveringProcess(event) { + event.element = rootElement; + event.gfx = rootElementGfx; + } + + if (is(shape, 'bpmn:Participant') && is(rootElement, 'bpmn:Process')) { + eventBus.on('element.hover', HIGH_PRIORITY, ensureHoveringProcess); + + eventBus.once('create.cleanup', function() { + eventBus.off('element.hover', ensureHoveringProcess); + }); + } + }); + + // turn process into collaboration when creating first participant + function getOrCreateCollaboration() { + var rootElement = canvas.getRootElement(); + + if (is(rootElement, 'bpmn:Collaboration')) { + return rootElement; + } + + return modeling.makeCollaboration(); + } + + // when creating mutliple elements through `elements.create` parent must be set to collaboration + // and passed to `shape.create` as hint + this.preExecute('elements.create', HIGH_PRIORITY, function(context) { + var elements = context.elements, + parent = context.parent, + participant = findParticipant(elements), + hints; + + if (participant && is(parent, 'bpmn:Process')) { + context.parent = getOrCreateCollaboration(); + + hints = context.hints = context.hints || {}; + + hints.participant = participant; + hints.process = parent; + hints.processRef = getBusinessObject(participant).get('processRef'); + } + }, true); + + // when creating single shape through `shape.create` parent must be set to collaboration + // unless it was already set through `elements.create` + this.preExecute('shape.create', function(context) { + var parent = context.parent, + shape = context.shape; + + if (is(shape, 'bpmn:Participant') && is(parent, 'bpmn:Process')) { + context.parent = getOrCreateCollaboration(); + + context.process = parent; + context.processRef = getBusinessObject(shape).get('processRef'); + } + }, true); + + // #execute necessary because #preExecute not called on CommandStack#redo + this.execute('shape.create', function(context) { + var hints = context.hints || {}, + process = context.process || hints.process, + shape = context.shape, + participant = hints.participant; + + // both shape.create and elements.create must be handled + if (process && (!participant || shape === participant)) { + + // monkey-patch process ref + getBusinessObject(shape).set('processRef', getBusinessObject(process)); + } + }, true); + + this.revert('shape.create', function(context) { + var hints = context.hints || {}, + process = context.process || hints.process, + processRef = context.processRef || hints.processRef, + shape = context.shape, + participant = hints.participant; + + // both shape.create and elements.create must be handled + if (process && (!participant || shape === participant)) { + + // monkey-patch process ref + getBusinessObject(shape).set('processRef', processRef); + } + }, true); + + this.postExecute('shape.create', function(context) { + var hints = context.hints || {}, + process = context.process || context.hints.process, + shape = context.shape, + participant = hints.participant; + + if (process) { + var children = process.children.slice(); + + // both shape.create and elements.create must be handled + if (!participant) { + modeling.moveElements(children, { x: 0, y: 0 }, shape); + } else if (shape === participant) { + modeling.moveElements(children, { x: 0, y: 0 }, participant); + } + } + }, true); +} + +CreateParticipantBehavior.$inject = [ + 'canvas', + 'eventBus', + 'modeling' +]; + +inherits(CreateParticipantBehavior, CommandInterceptor); + +// helpers ////////// + +function getParticipantBounds(shape, childrenBBox) { + childrenBBox = { + width: childrenBBox.width + HORIZONTAL_PARTICIPANT_PADDING * 2 + PARTICIPANT_BORDER_WIDTH, + height: childrenBBox.height + VERTICAL_PARTICIPANT_PADDING * 2 + }; + + var width = Math.max(shape.width, childrenBBox.width), + height = Math.max(shape.height, childrenBBox.height); + + return { + x: -width / 2, + y: -height / 2, + width: width, + height: height + }; +} + +function getParticipantCreateConstraints(shape, childrenBBox) { + childrenBBox = asTRBL(childrenBBox); + + return { + bottom: childrenBBox.top + shape.height / 2 - VERTICAL_PARTICIPANT_PADDING, + left: childrenBBox.right - shape.width / 2 + HORIZONTAL_PARTICIPANT_PADDING, + top: childrenBBox.bottom - shape.height / 2 + VERTICAL_PARTICIPANT_PADDING, + right: childrenBBox.left + shape.width / 2 - HORIZONTAL_PARTICIPANT_PADDING - PARTICIPANT_BORDER_WIDTH + }; +} + +function isConnection(element) { + return !!element.waypoints; +} + +function findParticipant(elements) { + return find(elements, function(element) { + return is(element, 'bpmn:Participant'); + }); +} \ No newline at end of file diff --git a/lib/features/modeling/behavior/DataInputAssociationBehavior.js b/lib/features/modeling/behavior/DataInputAssociationBehavior.js new file mode 100644 index 0000000..f0e6a5d --- /dev/null +++ b/lib/features/modeling/behavior/DataInputAssociationBehavior.js @@ -0,0 +1,158 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { + add as collectionAdd, + remove as collectionRemove +} from 'diagram-js/lib/util/Collections'; + +import { + find +} from 'min-dash'; + +import { + is +} from '../../../util/ModelUtil'; + +var TARGET_REF_PLACEHOLDER_NAME = '__targetRef_placeholder'; + + +/** + * This behavior makes sure we always set a fake + * DataInputAssociation#targetRef as demanded by the BPMN 2.0 + * XSD schema. + * + * The reference is set to a bpmn:Property{ name: '__targetRef_placeholder' } + * which is created on the fly and cleaned up afterwards if not needed + * anymore. + * + * @param {EventBus} eventBus + * @param {BpmnFactory} bpmnFactory + */ +export default function DataInputAssociationBehavior(eventBus, bpmnFactory) { + + CommandInterceptor.call(this, eventBus); + + + this.executed([ + 'connection.create', + 'connection.delete', + 'connection.move', + 'connection.reconnect' + ], ifDataInputAssociation(fixTargetRef)); + + this.reverted([ + 'connection.create', + 'connection.delete', + 'connection.move', + 'connection.reconnect' + ], ifDataInputAssociation(fixTargetRef)); + + + function usesTargetRef(element, targetRef, removedConnection) { + + var inputAssociations = element.get('dataInputAssociations'); + + return find(inputAssociations, function(association) { + return association !== removedConnection && + association.targetRef === targetRef; + }); + } + + function getTargetRef(element, create) { + + var properties = element.get('properties'); + + var targetRefProp = find(properties, function(p) { + return p.name === TARGET_REF_PLACEHOLDER_NAME; + }); + + if (!targetRefProp && create) { + targetRefProp = bpmnFactory.create('bpmn:Property', { + name: TARGET_REF_PLACEHOLDER_NAME + }); + + collectionAdd(properties, targetRefProp); + } + + return targetRefProp; + } + + function cleanupTargetRef(element, connection) { + + var targetRefProp = getTargetRef(element); + + if (!targetRefProp) { + return; + } + + if (!usesTargetRef(element, targetRefProp, connection)) { + collectionRemove(element.get('properties'), targetRefProp); + } + } + + /** + * Make sure targetRef is set to a valid property or + * `null` if the connection is detached. + * + * @param {Event} event + */ + function fixTargetRef(event) { + + var context = event.context, + connection = context.connection, + connectionBo = connection.businessObject, + target = connection.target, + targetBo = target && target.businessObject, + newTarget = context.newTarget, + newTargetBo = newTarget && newTarget.businessObject, + oldTarget = context.oldTarget || context.target, + oldTargetBo = oldTarget && oldTarget.businessObject; + + var dataAssociation = connection.businessObject, + targetRefProp; + + if (oldTargetBo && oldTargetBo !== targetBo) { + cleanupTargetRef(oldTargetBo, connectionBo); + } + + if (newTargetBo && newTargetBo !== targetBo) { + cleanupTargetRef(newTargetBo, connectionBo); + } + + if (targetBo) { + targetRefProp = getTargetRef(targetBo, true); + dataAssociation.targetRef = targetRefProp; + } else { + dataAssociation.targetRef = null; + } + } +} + +DataInputAssociationBehavior.$inject = [ + 'eventBus', + 'bpmnFactory' +]; + +inherits(DataInputAssociationBehavior, CommandInterceptor); + + +/** + * Only call the given function when the event + * touches a bpmn:DataInputAssociation. + * + * @param {Function} fn + * @return {Function} + */ +function ifDataInputAssociation(fn) { + + return function(event) { + var context = event.context, + connection = context.connection; + + if (is(connection, 'bpmn:DataInputAssociation')) { + return fn(event); + } + }; +} \ No newline at end of file diff --git a/lib/features/modeling/behavior/DataStoreBehavior.js b/lib/features/modeling/behavior/DataStoreBehavior.js new file mode 100644 index 0000000..5f2af46 --- /dev/null +++ b/lib/features/modeling/behavior/DataStoreBehavior.js @@ -0,0 +1,212 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { + getBusinessObject, + getDi, + is +} from '../../../util/ModelUtil'; + +import { isAny } from '../util/ModelingUtil'; + +import UpdateSemanticParentHandler from '../cmd/UpdateSemanticParentHandler'; + + +/** + * BPMN specific data store behavior + */ +export default function DataStoreBehavior( + canvas, commandStack, elementRegistry, + eventBus) { + + CommandInterceptor.call(this, eventBus); + + commandStack.registerHandler('dataStore.updateContainment', UpdateSemanticParentHandler); + + function getFirstParticipantWithProcessRef() { + return elementRegistry.filter(function(element) { + return is(element, 'bpmn:Participant') && getBusinessObject(element).processRef; + })[0]; + } + + function getDataStores(element) { + return element.children.filter(function(child) { + return is(child, 'bpmn:DataStoreReference') && !child.labelTarget; + }); + } + + function updateDataStoreParent(dataStore, newDataStoreParent) { + var dataStoreBo = dataStore.businessObject || dataStore; + + newDataStoreParent = newDataStoreParent || getFirstParticipantWithProcessRef(); + + if (newDataStoreParent) { + var newDataStoreParentBo = newDataStoreParent.businessObject || newDataStoreParent; + + commandStack.execute('dataStore.updateContainment', { + dataStoreBo: dataStoreBo, + dataStoreDi: getDi(dataStore), + newSemanticParent: newDataStoreParentBo.processRef || newDataStoreParentBo, + newDiParent: getDi(newDataStoreParent) + }); + } + } + + + // disable auto-resize for data stores + this.preExecute('shape.create', function(event) { + + var context = event.context, + shape = context.shape; + + if (is(shape, 'bpmn:DataStoreReference') && + shape.type !== 'label') { + + if (!context.hints) { + context.hints = {}; + } + + // prevent auto resizing + context.hints.autoResize = false; + } + }); + + + // disable auto-resize for data stores + this.preExecute('elements.move', function(event) { + var context = event.context, + shapes = context.shapes; + + var dataStoreReferences = shapes.filter(function(shape) { + return is(shape, 'bpmn:DataStoreReference'); + }); + + if (dataStoreReferences.length) { + if (!context.hints) { + context.hints = {}; + } + + // prevent auto resizing for data store references + context.hints.autoResize = shapes.filter(function(shape) { + return !is(shape, 'bpmn:DataStoreReference'); + }); + } + }); + + + // update parent on data store created + this.postExecute('shape.create', function(event) { + var context = event.context, + shape = context.shape, + parent = shape.parent; + + + if (is(shape, 'bpmn:DataStoreReference') && + shape.type !== 'label' && + is(parent, 'bpmn:Collaboration')) { + + updateDataStoreParent(shape); + } + }); + + + // update parent on data store moved + this.postExecute('shape.move', function(event) { + var context = event.context, + shape = context.shape, + oldParent = context.oldParent, + parent = shape.parent; + + if (is(oldParent, 'bpmn:Collaboration')) { + + // do nothing if not necessary + return; + } + + if (is(shape, 'bpmn:DataStoreReference') && + shape.type !== 'label' && + is(parent, 'bpmn:Collaboration')) { + + var participant = is(oldParent, 'bpmn:Participant') ? + oldParent : + getAncestor(oldParent, 'bpmn:Participant'); + + updateDataStoreParent(shape, participant); + } + }); + + + // update data store parents on participant or subprocess deleted + this.postExecute('shape.delete', function(event) { + var context = event.context, + shape = context.shape, + rootElement = canvas.getRootElement(); + + if (isAny(shape, [ 'bpmn:Participant', 'bpmn:SubProcess' ]) + && is(rootElement, 'bpmn:Collaboration')) { + getDataStores(rootElement) + .filter(function(dataStore) { + return isDescendant(dataStore, shape); + }) + .forEach(function(dataStore) { + updateDataStoreParent(dataStore); + }); + } + }); + + // update data store parents on collaboration -> process + this.postExecute('canvas.updateRoot', function(event) { + var context = event.context, + oldRoot = context.oldRoot, + newRoot = context.newRoot; + + var dataStores = getDataStores(oldRoot); + + dataStores.forEach(function(dataStore) { + + if (is(newRoot, 'bpmn:Process')) { + updateDataStoreParent(dataStore, newRoot); + } + + }); + }); +} + +DataStoreBehavior.$inject = [ + 'canvas', + 'commandStack', + 'elementRegistry', + 'eventBus', +]; + +inherits(DataStoreBehavior, CommandInterceptor); + + +// helpers ////////// + +function isDescendant(descendant, ancestor) { + var descendantBo = descendant.businessObject || descendant, + ancestorBo = ancestor.businessObject || ancestor; + + while (descendantBo.$parent) { + if (descendantBo.$parent === ancestorBo.processRef || ancestorBo) { + return true; + } + + descendantBo = descendantBo.$parent; + } + + return false; +} + +function getAncestor(element, type) { + + while (element.parent) { + if (is(element.parent, type)) { + return element.parent; + } + + element = element.parent; + } +} \ No newline at end of file diff --git a/lib/features/modeling/behavior/DeleteLaneBehavior.js b/lib/features/modeling/behavior/DeleteLaneBehavior.js new file mode 100644 index 0000000..0e617e4 --- /dev/null +++ b/lib/features/modeling/behavior/DeleteLaneBehavior.js @@ -0,0 +1,112 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { is } from '../../../util/ModelUtil'; + +import { + getChildLanes +} from '../util/LaneUtil'; + +import { + eachElement +} from 'diagram-js/lib/util/Elements'; + + +var LOW_PRIORITY = 500; + + +/** + * BPMN specific delete lane behavior + */ +export default function DeleteLaneBehavior(eventBus, modeling, spaceTool) { + + CommandInterceptor.call(this, eventBus); + + + function compensateLaneDelete(shape, oldParent) { + + var siblings = getChildLanes(oldParent); + + var topAffected = []; + var bottomAffected = []; + + eachElement(siblings, function(element) { + + if (element.y > shape.y) { + bottomAffected.push(element); + } else { + topAffected.push(element); + } + + return element.children; + }); + + if (!siblings.length) { + return; + } + + var offset; + + if (bottomAffected.length && topAffected.length) { + offset = shape.height / 2; + } else { + offset = shape.height; + } + + var topAdjustments, + bottomAdjustments; + + if (topAffected.length) { + topAdjustments = spaceTool.calculateAdjustments( + topAffected, 'y', offset, shape.y - 10); + + spaceTool.makeSpace( + topAdjustments.movingShapes, + topAdjustments.resizingShapes, + { x: 0, y: offset }, 's'); + } + + if (bottomAffected.length) { + bottomAdjustments = spaceTool.calculateAdjustments( + bottomAffected, 'y', -offset, shape.y + shape.height + 10); + + spaceTool.makeSpace( + bottomAdjustments.movingShapes, + bottomAdjustments.resizingShapes, + { x: 0, y: -offset }, 'n'); + } + } + + + /** + * Adjust sizes of other lanes after lane deletion + */ + this.postExecuted('shape.delete', LOW_PRIORITY, function(event) { + + var context = event.context, + hints = context.hints, + shape = context.shape, + oldParent = context.oldParent; + + // only compensate lane deletes + if (!is(shape, 'bpmn:Lane')) { + return; + } + + // compensate root deletes only + if (hints && hints.nested) { + return; + } + + compensateLaneDelete(shape, oldParent); + }); +} + +DeleteLaneBehavior.$inject = [ + 'eventBus', + 'modeling', + 'spaceTool' +]; + +inherits(DeleteLaneBehavior, CommandInterceptor); \ No newline at end of file diff --git a/lib/features/modeling/behavior/DetachEventBehavior.js b/lib/features/modeling/behavior/DetachEventBehavior.js new file mode 100644 index 0000000..b5f4452 --- /dev/null +++ b/lib/features/modeling/behavior/DetachEventBehavior.js @@ -0,0 +1,94 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { + getBusinessObject, + is +} from '../../../util/ModelUtil'; + +import { isLabel } from '../../../util/LabelUtil'; + +var LOW_PRIORITY = 500; + + +/** + * Replace boundary event with intermediate event when creating or moving results in detached event. + */ +export default function DetachEventBehavior(bpmnReplace, injector) { + injector.invoke(CommandInterceptor, this); + + this._bpmnReplace = bpmnReplace; + + var self = this; + + this.postExecuted('elements.create', LOW_PRIORITY, function(context) { + var elements = context.elements; + + elements.filter(function(shape) { + var host = shape.host; + + return shouldReplace(shape, host); + }).map(function(shape) { + return elements.indexOf(shape); + }).forEach(function(index) { + context.elements[ index ] = self.replaceShape(elements[ index ]); + }); + }, true); + + this.preExecute('elements.move', LOW_PRIORITY, function(context) { + var shapes = context.shapes, + newHost = context.newHost; + + shapes.forEach(function(shape, index) { + var host = shape.host; + + if (shouldReplace(shape, includes(shapes, host) ? host : newHost)) { + shapes[ index ] = self.replaceShape(shape); + } + }); + }, true); +} + +DetachEventBehavior.$inject = [ + 'bpmnReplace', + 'injector' +]; + +inherits(DetachEventBehavior, CommandInterceptor); + +DetachEventBehavior.prototype.replaceShape = function(shape) { + var eventDefinition = getEventDefinition(shape), + intermediateEvent; + + if (eventDefinition) { + intermediateEvent = { + type: 'bpmn:IntermediateCatchEvent', + eventDefinitionType: eventDefinition.$type + }; + } else { + intermediateEvent = { + type: 'bpmn:IntermediateThrowEvent' + }; + } + + return this._bpmnReplace.replaceElement(shape, intermediateEvent, { layoutConnection: false }); +}; + + +// helpers ////////// + +function getEventDefinition(element) { + var businessObject = getBusinessObject(element), + eventDefinitions = businessObject.eventDefinitions; + + return eventDefinitions && eventDefinitions[0]; +} + +function shouldReplace(shape, host) { + return !isLabel(shape) && is(shape, 'bpmn:BoundaryEvent') && !host; +} + +function includes(array, item) { + return array.indexOf(item) !== -1; +} \ No newline at end of file diff --git a/lib/features/modeling/behavior/DropOnFlowBehavior.js b/lib/features/modeling/behavior/DropOnFlowBehavior.js new file mode 100644 index 0000000..fc1dfba --- /dev/null +++ b/lib/features/modeling/behavior/DropOnFlowBehavior.js @@ -0,0 +1,211 @@ +import inherits from 'inherits-browser'; + +import { + assign, + filter, + find, + isNumber +} from 'min-dash'; + +import { getMid } from 'diagram-js/lib/layout/LayoutUtil'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { + getApproxIntersection +} from 'diagram-js/lib/util/LineIntersection'; + + +export default function DropOnFlowBehavior(eventBus, bpmnRules, modeling) { + + CommandInterceptor.call(this, eventBus); + + /** + * Reconnect start / end of a connection after + * dropping an element on a flow. + */ + + function insertShape(shape, targetFlow, positionOrBounds) { + var waypoints = targetFlow.waypoints, + waypointsBefore, + waypointsAfter, + dockingPoint, + source, + target, + incomingConnection, + outgoingConnection, + oldOutgoing = shape.outgoing.slice(), + oldIncoming = shape.incoming.slice(); + + var mid; + + if (isNumber(positionOrBounds.width)) { + mid = getMid(positionOrBounds); + } else { + mid = positionOrBounds; + } + + var intersection = getApproxIntersection(waypoints, mid); + + if (intersection) { + waypointsBefore = waypoints.slice(0, intersection.index); + waypointsAfter = waypoints.slice(intersection.index + (intersection.bendpoint ? 1 : 0)); + + // due to inaccuracy intersection might have been found + if (!waypointsBefore.length || !waypointsAfter.length) { + return; + } + + dockingPoint = intersection.bendpoint ? waypoints[intersection.index] : mid; + + // if last waypointBefore is inside shape's bounds, ignore docking point + if (waypointsBefore.length === 1 || !isPointInsideBBox(shape, waypointsBefore[waypointsBefore.length - 1])) { + waypointsBefore.push(copy(dockingPoint)); + } + + // if first waypointAfter is inside shape's bounds, ignore docking point + if (waypointsAfter.length === 1 || !isPointInsideBBox(shape, waypointsAfter[0])) { + waypointsAfter.unshift(copy(dockingPoint)); + } + } + + source = targetFlow.source; + target = targetFlow.target; + + if (bpmnRules.canConnect(source, shape, targetFlow)) { + + // reconnect source -> inserted shape + modeling.reconnectEnd(targetFlow, shape, waypointsBefore || mid); + + incomingConnection = targetFlow; + } + + if (bpmnRules.canConnect(shape, target, targetFlow)) { + + if (!incomingConnection) { + + // reconnect inserted shape -> end + modeling.reconnectStart(targetFlow, shape, waypointsAfter || mid); + + outgoingConnection = targetFlow; + } else { + outgoingConnection = modeling.connect( + shape, target, { type: targetFlow.type, waypoints: waypointsAfter } + ); + } + } + + var duplicateConnections = [].concat( + + incomingConnection && filter(oldIncoming, function(connection) { + return connection.source === incomingConnection.source; + }) || [], + + outgoingConnection && filter(oldOutgoing, function(connection) { + return connection.target === outgoingConnection.target; + }) || [] + ); + + if (duplicateConnections.length) { + modeling.removeElements(duplicateConnections); + } + } + + this.preExecute('elements.move', function(context) { + + var newParent = context.newParent, + shapes = context.shapes, + delta = context.delta, + shape = shapes[0]; + + if (!shape || !newParent) { + return; + } + + // if the new parent is a connection, + // change it to the new parent's parent + if (newParent && newParent.waypoints) { + context.newParent = newParent = newParent.parent; + } + + var shapeMid = getMid(shape); + var newShapeMid = { + x: shapeMid.x + delta.x, + y: shapeMid.y + delta.y + }; + + // find a connection which intersects with the + // element's mid point + var connection = find(newParent.children, function(element) { + var canInsert = bpmnRules.canInsert(shapes, element); + + return canInsert && getApproxIntersection(element.waypoints, newShapeMid); + }); + + if (connection) { + context.targetFlow = connection; + context.position = newShapeMid; + } + + }, true); + + this.postExecuted('elements.move', function(context) { + + var shapes = context.shapes, + targetFlow = context.targetFlow, + position = context.position; + + if (targetFlow) { + insertShape(shapes[0], targetFlow, position); + } + + }, true); + + this.preExecute('shape.create', function(context) { + + var parent = context.parent, + shape = context.shape; + + if (bpmnRules.canInsert(shape, parent)) { + context.targetFlow = parent; + context.parent = parent.parent; + } + }, true); + + this.postExecuted('shape.create', function(context) { + + var shape = context.shape, + targetFlow = context.targetFlow, + positionOrBounds = context.position; + + if (targetFlow) { + insertShape(shape, targetFlow, positionOrBounds); + } + }, true); +} + +inherits(DropOnFlowBehavior, CommandInterceptor); + +DropOnFlowBehavior.$inject = [ + 'eventBus', + 'bpmnRules', + 'modeling' +]; + + +// helpers ///////////////////// + +function isPointInsideBBox(bbox, point) { + var x = point.x, + y = point.y; + + return x >= bbox.x && + x <= bbox.x + bbox.width && + y >= bbox.y && + y <= bbox.y + bbox.height; +} + +function copy(obj) { + return assign({}, obj); +} + diff --git a/lib/features/modeling/behavior/EventBasedGatewayBehavior.js b/lib/features/modeling/behavior/EventBasedGatewayBehavior.js new file mode 100644 index 0000000..503dfa1 --- /dev/null +++ b/lib/features/modeling/behavior/EventBasedGatewayBehavior.js @@ -0,0 +1,85 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { is } from '../../../util/ModelUtil'; + +export default function EventBasedGatewayBehavior(eventBus, modeling) { + + CommandInterceptor.call(this, eventBus); + + /** + * Remove existing sequence flows of event-based target before connecting + * from event-based gateway. + */ + this.preExecuted('connection.create', function(event) { + + var context = event.context, + source = context.source, + target = context.target, + existingIncomingConnections = target.incoming.slice(); + + if (context.hints && context.hints.createElementsBehavior === false) { + return; + } + + if ( + is(source, 'bpmn:EventBasedGateway') && + target.incoming.length + ) { + + existingIncomingConnections.filter(isSequenceFlow) + .forEach(function(sequenceFlow) { + modeling.removeConnection(sequenceFlow); + }); + } + }); + + /** + * After replacing shape with event-based gateway, remove incoming sequence + * flows of event-based targets which do not belong to event-based gateway + * source. + */ + this.preExecuted('shape.replace', function(event) { + + var newShape = event.context.newShape, + newShapeTargets, + newShapeTargetsIncomingSequenceFlows; + + if (!is(newShape, 'bpmn:EventBasedGateway')) { + return; + } + + newShapeTargets = newShape.outgoing.filter(isSequenceFlow) + .map(function(sequenceFlow) { + return sequenceFlow.target; + }); + + newShapeTargetsIncomingSequenceFlows = newShapeTargets.reduce(function(sequenceFlows, target) { + var incomingSequenceFlows = target.incoming.filter(isSequenceFlow); + + return sequenceFlows.concat(incomingSequenceFlows); + }, []); + + newShapeTargetsIncomingSequenceFlows.forEach(function(sequenceFlow) { + if (sequenceFlow.source !== newShape) { + modeling.removeConnection(sequenceFlow); + } + }); + }); +} + +EventBasedGatewayBehavior.$inject = [ + 'eventBus', + 'modeling' +]; + +inherits(EventBasedGatewayBehavior, CommandInterceptor); + + + +// helpers ////////////////////// + +function isSequenceFlow(connection) { + return is(connection, 'bpmn:SequenceFlow'); +} diff --git a/lib/features/modeling/behavior/FixHoverBehavior.js b/lib/features/modeling/behavior/FixHoverBehavior.js new file mode 100644 index 0000000..a1f6cb7 --- /dev/null +++ b/lib/features/modeling/behavior/FixHoverBehavior.js @@ -0,0 +1,117 @@ +import { getLanesRoot } from '../util/LaneUtil'; + +import { is } from '../../../util/ModelUtil'; + +import { isAny } from '../util/ModelingUtil'; + +var HIGH_PRIORITY = 1500; +var HIGHEST_PRIORITY = 2000; + + +/** + * Correct hover targets in certain situations to improve diagram interaction. + * + * @param {ElementRegistry} elementRegistry + * @param {EventBus} eventBus + * @param {Canvas} canvas + */ +export default function FixHoverBehavior(elementRegistry, eventBus, canvas) { + + eventBus.on([ + 'create.hover', + 'create.move', + 'create.out', + 'create.end', + 'shape.move.hover', + 'shape.move.move', + 'shape.move.out', + 'shape.move.end' + ], HIGH_PRIORITY, function(event) { + var context = event.context, + shape = context.shape || event.shape, + hover = event.hover; + + // ensure elements are not dropped onto a bpmn:Lane but onto + // the underlying bpmn:Participant + if (is(hover, 'bpmn:Lane') && !isAny(shape, [ 'bpmn:Lane', 'bpmn:Participant' ])) { + event.hover = getLanesRoot(hover); + event.hoverGfx = elementRegistry.getGraphics(event.hover); + } + + var rootElement = canvas.getRootElement(); + + // ensure bpmn:Group and label elements are dropped + // always onto the root + if (hover !== rootElement && (shape.labelTarget || is(shape, 'bpmn:Group'))) { + event.hover = rootElement; + event.hoverGfx = elementRegistry.getGraphics(event.hover); + } + }); + + eventBus.on([ + 'connect.hover', + 'connect.out', + 'connect.end', + 'connect.cleanup', + 'global-connect.hover', + 'global-connect.out', + 'global-connect.end', + 'global-connect.cleanup' + ], HIGH_PRIORITY, function(event) { + var hover = event.hover; + + // ensure connections start/end on bpmn:Participant, + // not the underlying bpmn:Lane + if (is(hover, 'bpmn:Lane')) { + event.hover = getLanesRoot(hover) || hover; + event.hoverGfx = elementRegistry.getGraphics(event.hover); + } + }); + + + eventBus.on([ + 'bendpoint.move.hover' + ], HIGH_PRIORITY, function(event) { + var context = event.context, + hover = event.hover, + type = context.type; + + // ensure reconnect start/end on bpmn:Participant, + // not the underlying bpmn:Lane + if (is(hover, 'bpmn:Lane') && /reconnect/.test(type)) { + event.hover = getLanesRoot(hover) || hover; + event.hoverGfx = elementRegistry.getGraphics(event.hover); + } + }); + + + eventBus.on([ + 'connect.start' + ], HIGH_PRIORITY, function(event) { + var context = event.context, + start = context.start; + + // ensure connect start on bpmn:Participant, + // not the underlying bpmn:Lane + if (is(start, 'bpmn:Lane')) { + context.start = getLanesRoot(start) || start; + } + }); + + + // allow movement of participants from lanes + eventBus.on('shape.move.start', HIGHEST_PRIORITY, function(event) { + var shape = event.shape; + + if (is(shape, 'bpmn:Lane')) { + event.shape = getLanesRoot(shape) || shape; + } + }); + +} + +FixHoverBehavior.$inject = [ + 'elementRegistry', + 'eventBus', + 'canvas' +]; \ No newline at end of file diff --git a/lib/features/modeling/behavior/GroupBehavior.js b/lib/features/modeling/behavior/GroupBehavior.js new file mode 100644 index 0000000..c0508a7 --- /dev/null +++ b/lib/features/modeling/behavior/GroupBehavior.js @@ -0,0 +1,311 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { + getBusinessObject, + is +} from '../../../util/ModelUtil'; + +import { + createCategory, + createCategoryValue, + linkCategoryValue, + unlinkCategory, + unlinkCategoryValue +} from './util/CategoryUtil'; + + +var LOWER_PRIORITY = 770; + + +/** + * BPMN specific Group behavior + */ +export default function GroupBehavior( + bpmnFactory, + bpmnjs, + elementRegistry, + eventBus, + injector, + moddleCopy +) { + injector.invoke(CommandInterceptor, this); + + /** + * Returns all group element in the current registry + * + * @return {Array} a list of group shapes + */ + function getGroupElements() { + return elementRegistry.filter(function(e) { + return is(e, 'bpmn:Group'); + }); + } + + /** + * Returns true if given category is referenced in one of the given elements + * + * @param { djs.model.Element[] } elements + * @param { ModdleElement } category + * + * @return { boolean } + */ + function isReferencedCategory(elements, category) { + return elements.some(function(element) { + var businessObject = getBusinessObject(element); + + var _category = businessObject.categoryValueRef && businessObject.categoryValueRef.$parent; + + return _category === category; + }); + } + + /** + * Returns true if given categoryValue is referenced in one of the given elements + * + * @param { djs.model.Element[] } elements + * @param { ModdleElement } categoryValue + * + * @return { boolean } + */ + function isReferencedCategoryValue(elements, categoryValue) { + return elements.some(function(element) { + var businessObject = getBusinessObject(element); + + return businessObject.categoryValueRef === categoryValue; + }); + } + + /** + * Remove category value unless it is still referenced + * + * @param {ModdleElement} categoryValue + * @param {ModdleElement} category + * @param {ModdleElement} businessObject + */ + function removeCategoryValue(categoryValue, category, businessObject) { + + var groups = getGroupElements().filter(function(element) { + return element.businessObject !== businessObject; + }); + + if (category && !isReferencedCategory(groups, category)) { + unlinkCategory(category); + } + + if (categoryValue && !isReferencedCategoryValue(groups, categoryValue)) { + unlinkCategoryValue(categoryValue); + } + } + + /** + * Add category value + * + * @param {ModdleElement} categoryValue + * @param {ModdleElement} category + */ + function addCategoryValue(categoryValue, category) { + return linkCategoryValue(categoryValue, category, bpmnjs.getDefinitions()); + } + + function setCategoryValue(element, context) { + var businessObject = getBusinessObject(element), + categoryValue = businessObject.categoryValueRef; + + if (!categoryValue) { + categoryValue = + businessObject.categoryValueRef = + context.categoryValue = ( + context.categoryValue || createCategoryValue(bpmnFactory) + ); + } + + var category = categoryValue.$parent; + + if (!category) { + category = + categoryValue.$parent = + context.category = ( + context.category || createCategory(bpmnFactory) + ); + } + + addCategoryValue(categoryValue, category, bpmnjs.getDefinitions()); + } + + function unsetCategoryValue(element, context) { + var category = context.category, + categoryValue = context.categoryValue, + businessObject = getBusinessObject(element); + + if (categoryValue) { + businessObject.categoryValueRef = null; + + removeCategoryValue(categoryValue, category, businessObject); + } else { + removeCategoryValue(null, businessObject.categoryValueRef.$parent, businessObject); + } + } + + + // ensure category + value exist before label editing + + this.execute('label.create', function(event) { + var context = event.context, + labelTarget = context.labelTarget; + + if (!is(labelTarget, 'bpmn:Group')) { + return; + } + + setCategoryValue(labelTarget, context); + }); + + this.revert('label.create', function(event) { + var context = event.context, + labelTarget = context.labelTarget; + + if (!is(labelTarget, 'bpmn:Group')) { + return; + } + + unsetCategoryValue(labelTarget, context); + }); + + + // remove referenced category + value when group was deleted + + this.execute('shape.delete', function(event) { + + var context = event.context, + shape = context.shape, + businessObject = getBusinessObject(shape); + + if (!is(shape, 'bpmn:Group') || shape.labelTarget) { + return; + } + + var categoryValue = context.categoryValue = businessObject.categoryValueRef, + category; + + if (categoryValue) { + category = context.category = categoryValue.$parent; + + removeCategoryValue(categoryValue, category, businessObject); + + businessObject.categoryValueRef = null; + } + }); + + this.reverted('shape.delete', function(event) { + + var context = event.context, + shape = context.shape; + + if (!is(shape, 'bpmn:Group') || shape.labelTarget) { + return; + } + + var category = context.category, + categoryValue = context.categoryValue, + businessObject = getBusinessObject(shape); + + if (categoryValue) { + businessObject.categoryValueRef = categoryValue; + + addCategoryValue(categoryValue, category); + } + }); + + + // create new category + value when group was created + + this.execute('shape.create', function(event) { + var context = event.context, + shape = context.shape; + + if (!is(shape, 'bpmn:Group') || shape.labelTarget) { + return; + } + + if (getBusinessObject(shape).categoryValueRef) { + setCategoryValue(shape, context); + } + }); + + this.reverted('shape.create', function(event) { + + var context = event.context, + shape = context.shape; + + if (!is(shape, 'bpmn:Group') || shape.labelTarget) { + return; + } + + if (getBusinessObject(shape).categoryValueRef) { + unsetCategoryValue(shape, context); + } + }); + + + // copy + paste categoryValueRef with group + + function copy(bo, clone) { + var targetBo = bpmnFactory.create(bo.$type); + + return moddleCopy.copyElement(bo, targetBo, null, clone); + } + + eventBus.on('copyPaste.copyElement', LOWER_PRIORITY, function(context) { + var descriptor = context.descriptor, + element = context.element; + + if (!is(element, 'bpmn:Group') || element.labelTarget) { + return; + } + + var groupBo = getBusinessObject(element); + + if (groupBo.categoryValueRef) { + + var categoryValue = groupBo.categoryValueRef; + + descriptor.categoryValue = copy(categoryValue, true); + + if (categoryValue.$parent) { + descriptor.category = copy(categoryValue.$parent, true); + } + } + }); + + eventBus.on('copyPaste.pasteElement', LOWER_PRIORITY, function(context) { + var descriptor = context.descriptor, + businessObject = descriptor.businessObject, + categoryValue = descriptor.categoryValue, + category = descriptor.category; + + if (categoryValue) { + categoryValue = businessObject.categoryValueRef = copy(categoryValue); + } + + if (category) { + categoryValue.$parent = copy(category); + } + + delete descriptor.category; + delete descriptor.categoryValue; + }); + +} + +GroupBehavior.$inject = [ + 'bpmnFactory', + 'bpmnjs', + 'elementRegistry', + 'eventBus', + 'injector', + 'moddleCopy' +]; + +inherits(GroupBehavior, CommandInterceptor); \ No newline at end of file diff --git a/lib/features/modeling/behavior/ImportDockingFix.js b/lib/features/modeling/behavior/ImportDockingFix.js new file mode 100644 index 0000000..1483db0 --- /dev/null +++ b/lib/features/modeling/behavior/ImportDockingFix.js @@ -0,0 +1,81 @@ +import { + getMid +} from 'diagram-js/lib/layout/LayoutUtil'; + +import lineIntersect from './util/LineIntersect'; + + +/** + * Fix broken dockings after DI imports. + * + * @param {EventBus} eventBus + */ +export default function ImportDockingFix(eventBus) { + + function adjustDocking(startPoint, nextPoint, elementMid) { + + var elementTop = { + x: elementMid.x, + y: elementMid.y - 50 + }; + + var elementLeft = { + x: elementMid.x - 50, + y: elementMid.y + }; + + var verticalIntersect = lineIntersect(startPoint, nextPoint, elementMid, elementTop), + horizontalIntersect = lineIntersect(startPoint, nextPoint, elementMid, elementLeft); + + // original is horizontal or vertical center cross intersection + var centerIntersect; + + if (verticalIntersect && horizontalIntersect) { + if (getDistance(verticalIntersect, elementMid) > getDistance(horizontalIntersect, elementMid)) { + centerIntersect = horizontalIntersect; + } else { + centerIntersect = verticalIntersect; + } + } else { + centerIntersect = verticalIntersect || horizontalIntersect; + } + + startPoint.original = centerIntersect; + } + + function fixDockings(connection) { + var waypoints = connection.waypoints; + + adjustDocking( + waypoints[0], + waypoints[1], + getMid(connection.source) + ); + + adjustDocking( + waypoints[waypoints.length - 1], + waypoints[waypoints.length - 2], + getMid(connection.target) + ); + } + + eventBus.on('bpmnElement.added', function(e) { + + var element = e.element; + + if (element.waypoints) { + fixDockings(element); + } + }); +} + +ImportDockingFix.$inject = [ + 'eventBus' +]; + + +// helpers ////////////////////// + +function getDistance(p1, p2) { + return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)); +} \ No newline at end of file diff --git a/lib/features/modeling/behavior/IsHorizontalFix.js b/lib/features/modeling/behavior/IsHorizontalFix.js new file mode 100644 index 0000000..25fcc65 --- /dev/null +++ b/lib/features/modeling/behavior/IsHorizontalFix.js @@ -0,0 +1,45 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { + getBusinessObject, + getDi +} from '../../../util/ModelUtil'; + +import { + isAny +} from '../util/ModelingUtil'; + +/** + * A component that makes sure that each created or updated + * Pool and Lane is assigned an isHorizontal property set to true. + * + * @param {EventBus} eventBus + */ +export default function IsHorizontalFix(eventBus) { + + CommandInterceptor.call(this, eventBus); + + var elementTypesToUpdate = [ + 'bpmn:Participant', + 'bpmn:Lane' + ]; + + this.executed([ 'shape.move', 'shape.create', 'shape.resize' ], function(event) { + var shape = event.context.shape, + bo = getBusinessObject(shape), + di = getDi(shape); + + if (isAny(bo, elementTypesToUpdate) && !di.get('isHorizontal')) { + + // set attribute directly to avoid modeling#updateProperty side effects + di.set('isHorizontal', true); + } + }); + +} + +IsHorizontalFix.$inject = [ 'eventBus' ]; + +inherits(IsHorizontalFix, CommandInterceptor); diff --git a/lib/features/modeling/behavior/LabelBehavior.js b/lib/features/modeling/behavior/LabelBehavior.js new file mode 100644 index 0000000..28a7897 --- /dev/null +++ b/lib/features/modeling/behavior/LabelBehavior.js @@ -0,0 +1,403 @@ +import { + assign +} from 'min-dash'; + +import inherits from 'inherits-browser'; + +import { + is, + getBusinessObject, + getDi +} from '../../../util/ModelUtil'; + +import { + isLabelExternal, + getExternalLabelMid, + hasExternalLabel, + isLabel +} from '../../../util/LabelUtil'; + +import { + getLabel +} from '../../label-editing/LabelUtil'; + +import { + getLabelAdjustment +} from './util/LabelLayoutUtil'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { + getNewAttachPoint +} from 'diagram-js/lib/util/AttachUtil'; + +import { + getMid, + roundPoint +} from 'diagram-js/lib/layout/LayoutUtil'; + +import { + delta +} from 'diagram-js/lib/util/PositionUtil'; + +import { + sortBy +} from 'min-dash'; + +import { + getDistancePointLine, + perpendicularFoot +} from './util/GeometricUtil'; + +var DEFAULT_LABEL_DIMENSIONS = { + width: 90, + height: 20 +}; + +var NAME_PROPERTY = 'name'; +var TEXT_PROPERTY = 'text'; + +/** + * A component that makes sure that external labels are added + * together with respective elements and properly updated (DI wise) + * during move. + * + * @param {EventBus} eventBus + * @param {Modeling} modeling + * @param {BpmnFactory} bpmnFactory + * @param {TextRenderer} textRenderer + */ +export default function LabelBehavior( + eventBus, modeling, bpmnFactory, + textRenderer) { + + CommandInterceptor.call(this, eventBus); + + // update label if name property was updated + this.postExecute('element.updateProperties', function(e) { + var context = e.context, + element = context.element, + properties = context.properties; + + if (NAME_PROPERTY in properties) { + modeling.updateLabel(element, properties[NAME_PROPERTY]); + } + + if (TEXT_PROPERTY in properties + && is(element, 'bpmn:TextAnnotation')) { + + var newBounds = textRenderer.getTextAnnotationBounds( + { + x: element.x, + y: element.y, + width: element.width, + height: element.height + }, + properties[TEXT_PROPERTY] || '' + ); + + modeling.updateLabel(element, properties.text, newBounds); + } + }); + + // create label shape after shape/connection was created + this.postExecute([ 'shape.create', 'connection.create' ], function(e) { + var context = e.context, + hints = context.hints || {}; + + if (hints.createElementsBehavior === false) { + return; + } + + var element = context.shape || context.connection, + businessObject = element.businessObject; + + if (isLabel(element) || !isLabelExternal(element)) { + return; + } + + // only create label if attribute available + if (!getLabel(element)) { + return; + } + + var labelCenter = getExternalLabelMid(element); + + // we don't care about x and y + var labelDimensions = textRenderer.getExternalLabelBounds( + DEFAULT_LABEL_DIMENSIONS, + getLabel(element) + ); + + modeling.createLabel(element, labelCenter, { + id: businessObject.id + '_label', + businessObject: businessObject, + width: labelDimensions.width, + height: labelDimensions.height + }); + }); + + // update label after label shape was deleted + this.postExecute('shape.delete', function(event) { + var context = event.context, + labelTarget = context.labelTarget, + hints = context.hints || {}; + + // check if label + if (labelTarget && hints.unsetLabel !== false) { + modeling.updateLabel(labelTarget, null, null, { removeShape: false }); + } + }); + + // update di information on label creation + this.postExecute([ 'label.create' ], function(event) { + + var context = event.context, + element = context.shape, + labelTarget = context.labelTarget, + di; + + // we want to trigger on real labels only + if (!labelTarget) { + return; + } + + // we want to trigger on BPMN elements only + if (!is(labelTarget, 'bpmn:BaseElement')) { + return; + } + + di = getDi(labelTarget); + + if (!di.label) { + di.label = bpmnFactory.create('bpmndi:BPMNLabel', { + bounds: bpmnFactory.create('dc:Bounds') + }); + + element.di = di; + } + + assign(di.label.bounds, { + x: element.x, + y: element.y, + width: element.width, + height: element.height + }); + }); + + function getVisibleLabelAdjustment(event) { + + var context = event.context, + connection = context.connection, + label = connection.label, + hints = assign({}, context.hints), + newWaypoints = context.newWaypoints || connection.waypoints, + oldWaypoints = context.oldWaypoints; + + + if (typeof hints.startChanged === 'undefined') { + hints.startChanged = !!hints.connectionStart; + } + + if (typeof hints.endChanged === 'undefined') { + hints.endChanged = !!hints.connectionEnd; + } + + return getLabelAdjustment(label, newWaypoints, oldWaypoints, hints); + } + + this.postExecute([ + 'connection.layout', + 'connection.updateWaypoints' + ], function(event) { + var context = event.context, + hints = context.hints || {}; + + if (hints.labelBehavior === false) { + return; + } + + var connection = context.connection, + label = connection.label, + labelAdjustment; + + // handle missing label as well as the case + // that the label parent does not exist (yet), + // because it is being pasted / created via multi element create + // + // Cf. https://github.com/bpmn-io/bpmn-js/pull/1227 + if (!label || !label.parent) { + return; + } + + labelAdjustment = getVisibleLabelAdjustment(event); + + modeling.moveShape(label, labelAdjustment); + }); + + + // keep label position on shape replace + this.postExecute([ 'shape.replace' ], function(event) { + var context = event.context, + newShape = context.newShape, + oldShape = context.oldShape; + + var businessObject = getBusinessObject(newShape); + + if (businessObject + && isLabelExternal(businessObject) + && oldShape.label + && newShape.label) { + newShape.label.x = oldShape.label.x; + newShape.label.y = oldShape.label.y; + } + }); + + + // move external label after resizing + this.postExecute('shape.resize', function(event) { + + var context = event.context, + shape = context.shape, + newBounds = context.newBounds, + oldBounds = context.oldBounds; + + if (hasExternalLabel(shape)) { + + var label = shape.label, + labelMid = getMid(label), + edges = asEdges(oldBounds); + + // get nearest border point to label as reference point + var referencePoint = getReferencePoint(labelMid, edges); + + var delta = getReferencePointDelta(referencePoint, oldBounds, newBounds); + + modeling.moveShape(label, delta); + + } + + }); + +} + +inherits(LabelBehavior, CommandInterceptor); + +LabelBehavior.$inject = [ + 'eventBus', + 'modeling', + 'bpmnFactory', + 'textRenderer' +]; + +// helpers ////////////////////// + +/** + * Calculates a reference point delta relative to a new position + * of a certain element's bounds + * + * @param {Point} point + * @param {Bounds} oldBounds + * @param {Bounds} newBounds + * + * @return {Delta} delta + */ +export function getReferencePointDelta(referencePoint, oldBounds, newBounds) { + + var newReferencePoint = getNewAttachPoint(referencePoint, oldBounds, newBounds); + + return roundPoint(delta(newReferencePoint, referencePoint)); +} + +/** + * Generates the nearest point (reference point) for a given point + * onto given set of lines + * + * @param {Array} lines + * @param {Point} point + * + * @param {Point} + */ +export function getReferencePoint(point, lines) { + + if (!lines.length) { + return; + } + + var nearestLine = getNearestLine(point, lines); + + return perpendicularFoot(point, nearestLine); +} + +/** + * Convert the given bounds to a lines array containing all edges + * + * @param {Bounds|Point} bounds + * + * @return Array + */ +export function asEdges(bounds) { + return [ + [ // top + { + x: bounds.x, + y: bounds.y + }, + { + x: bounds.x + (bounds.width || 0), + y: bounds.y + } + ], + [ // right + { + x: bounds.x + (bounds.width || 0), + y: bounds.y + }, + { + x: bounds.x + (bounds.width || 0), + y: bounds.y + (bounds.height || 0) + } + ], + [ // bottom + { + x: bounds.x, + y: bounds.y + (bounds.height || 0) + }, + { + x: bounds.x + (bounds.width || 0), + y: bounds.y + (bounds.height || 0) + } + ], + [ // left + { + x: bounds.x, + y: bounds.y + }, + { + x: bounds.x, + y: bounds.y + (bounds.height || 0) + } + ] + ]; +} + +/** + * Returns the nearest line for a given point by distance + * @param {Point} point + * @param Array lines + * + * @return Array + */ +function getNearestLine(point, lines) { + + var distances = lines.map(function(l) { + return { + line: l, + distance: getDistancePointLine(point, l) + }; + }); + + var sorted = sortBy(distances, 'distance'); + + return sorted[0].line; +} diff --git a/lib/features/modeling/behavior/LayoutConnectionBehavior.js b/lib/features/modeling/behavior/LayoutConnectionBehavior.js new file mode 100644 index 0000000..0963fb3 --- /dev/null +++ b/lib/features/modeling/behavior/LayoutConnectionBehavior.js @@ -0,0 +1,117 @@ +import { + assign +} from 'min-dash'; + +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { getConnectionAdjustment as getConnectionAnchorPoint } from './util/ConnectionLayoutUtil'; + +/** + * A component that makes sure that Associations connected to Connections + * are updated together with the Connection. + * + * @param {EventBus} eventBus + * @param {Modeling} modeling + */ +export default function LayoutConnectionBehavior( + eventBus, modeling) { + + CommandInterceptor.call(this, eventBus); + + function getnewAnchorPoint(event, point) { + + var context = event.context, + connection = context.connection, + hints = assign({}, context.hints), + newWaypoints = context.newWaypoints || connection.waypoints, + oldWaypoints = context.oldWaypoints; + + + if (typeof hints.startChanged === 'undefined') { + hints.startChanged = !!hints.connectionStart; + } + + if (typeof hints.endChanged === 'undefined') { + hints.endChanged = !!hints.connectionEnd; + } + + return getConnectionAnchorPoint(point, newWaypoints, oldWaypoints, hints); + } + + this.postExecute([ + 'connection.layout', + 'connection.updateWaypoints' + ], function(event) { + var context = event.context; + + var connection = context.connection, + outgoing = connection.outgoing, + incoming = connection.incoming; + + incoming.forEach(function(connection) { + var endPoint = connection.waypoints[connection.waypoints.length - 1]; + var newEndpoint = getnewAnchorPoint(event, endPoint); + + var newWaypoints = [].concat(connection.waypoints.slice(0, -1), [ newEndpoint ]); + + modeling.updateWaypoints(connection, newWaypoints); + }); + + outgoing.forEach(function(connection) { + var startpoint = connection.waypoints[0]; + var newStartpoint = getnewAnchorPoint(event, startpoint); + + var newWaypoints = [].concat([ newStartpoint ], connection.waypoints.slice(1)); + + modeling.updateWaypoints(connection, newWaypoints); + }); + + }); + + + this.postExecute([ + 'connection.move' + ], function(event) { + var context = event.context; + + var connection = context.connection, + outgoing = connection.outgoing, + incoming = connection.incoming, + delta = context.delta; + + incoming.forEach(function(connection) { + var endPoint = connection.waypoints[connection.waypoints.length - 1]; + var newEndpoint = { + x: endPoint.x + delta.x, + y: endPoint.y + delta.y + }; + + var newWaypoints = [].concat(connection.waypoints.slice(0, -1), [ newEndpoint ]); + + modeling.updateWaypoints(connection, newWaypoints); + }); + + outgoing.forEach(function(connection) { + var startpoint = connection.waypoints[0]; + var newStartpoint = { + x: startpoint.x + delta.x, + y: startpoint.y + delta.y + }; + + var newWaypoints = [].concat([ newStartpoint ], connection.waypoints.slice(1)); + + modeling.updateWaypoints(connection, newWaypoints); + }); + + }); + +} + +inherits(LayoutConnectionBehavior, CommandInterceptor); + +LayoutConnectionBehavior.$inject = [ + 'eventBus', + 'modeling' +]; diff --git a/lib/features/modeling/behavior/MessageFlowBehavior.js b/lib/features/modeling/behavior/MessageFlowBehavior.js new file mode 100644 index 0000000..d797723 --- /dev/null +++ b/lib/features/modeling/behavior/MessageFlowBehavior.js @@ -0,0 +1,89 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { is } from '../../../util/ModelUtil'; + +import { isExpanded } from '../../../util/DiUtil'; + +import { selfAndAllChildren } from 'diagram-js/lib/util/Elements'; + +import { + getResizedSourceAnchor, + getResizedTargetAnchor +} from 'diagram-js/lib/features/modeling/cmd/helper/AnchorsHelper'; + +/** + * BPMN-specific message flow behavior. + */ +export default function MessageFlowBehavior(eventBus, modeling) { + + CommandInterceptor.call(this, eventBus); + + this.postExecute('shape.replace', function(context) { + var oldShape = context.oldShape, + newShape = context.newShape; + + if (!isParticipantCollapse(oldShape, newShape)) { + return; + } + + var messageFlows = getMessageFlows(oldShape); + + messageFlows.incoming.forEach(function(incoming) { + var anchor = getResizedTargetAnchor(incoming, newShape, oldShape); + + modeling.reconnectEnd(incoming, newShape, anchor); + }); + + messageFlows.outgoing.forEach(function(outgoing) { + var anchor = getResizedSourceAnchor(outgoing, newShape, oldShape); + + modeling.reconnectStart(outgoing, newShape, anchor); + }); + }, true); + +} + +MessageFlowBehavior.$inject = [ 'eventBus', 'modeling' ]; + +inherits(MessageFlowBehavior, CommandInterceptor); + +// helpers ////////// + +function isParticipantCollapse(oldShape, newShape) { + return is(oldShape, 'bpmn:Participant') + && isExpanded(oldShape) + && is(newShape, 'bpmn:Participant') + && !isExpanded(newShape); +} + +function getMessageFlows(parent) { + var elements = selfAndAllChildren([ parent ], false); + + var incoming = [], + outgoing = []; + + elements.forEach(function(element) { + if (element === parent) { + return; + } + + element.incoming.forEach(function(connection) { + if (is(connection, 'bpmn:MessageFlow')) { + incoming.push(connection); + } + }); + + element.outgoing.forEach(function(connection) { + if (is(connection, 'bpmn:MessageFlow')) { + outgoing.push(connection); + } + }); + }, []); + + return { + incoming: incoming, + outgoing: outgoing + }; +} \ No newline at end of file diff --git a/lib/features/modeling/behavior/ModelingFeedback.js b/lib/features/modeling/behavior/ModelingFeedback.js new file mode 100644 index 0000000..083af51 --- /dev/null +++ b/lib/features/modeling/behavior/ModelingFeedback.js @@ -0,0 +1,35 @@ +import { is } from '../../../util/ModelUtil'; + +var COLLAB_ERR_MSG = 'flow elements must be children of pools/participants'; + +export default function ModelingFeedback(eventBus, tooltips, translate) { + + function showError(position, message, timeout) { + tooltips.add({ + position: { + x: position.x + 5, + y: position.y + 5 + }, + type: 'error', + timeout: timeout || 2000, + html: '
                ' + message + '
                ' + }); + } + + eventBus.on([ 'shape.move.rejected', 'create.rejected' ], function(event) { + var context = event.context, + shape = context.shape, + target = context.target; + + if (is(target, 'bpmn:Collaboration') && is(shape, 'bpmn:FlowNode')) { + showError(event, translate(COLLAB_ERR_MSG)); + } + }); + +} + +ModelingFeedback.$inject = [ + 'eventBus', + 'tooltips', + 'translate' +]; diff --git a/lib/features/modeling/behavior/RemoveElementBehavior.js b/lib/features/modeling/behavior/RemoveElementBehavior.js new file mode 100644 index 0000000..0fb06f2 --- /dev/null +++ b/lib/features/modeling/behavior/RemoveElementBehavior.js @@ -0,0 +1,82 @@ +import inherits from 'inherits-browser'; + +import { is } from '../../../util/ModelUtil'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import lineIntersect from './util/LineIntersect'; + + +export default function RemoveElementBehavior(eventBus, bpmnRules, modeling) { + + CommandInterceptor.call(this, eventBus); + + /** + * Combine sequence flows when deleting an element + * if there is one incoming and one outgoing + * sequence flow + */ + this.preExecute('shape.delete', function(e) { + + var shape = e.context.shape; + + // only handle [a] -> [shape] -> [b] patterns + if (shape.incoming.length !== 1 || shape.outgoing.length !== 1) { + return; + } + + var inConnection = shape.incoming[0], + outConnection = shape.outgoing[0]; + + // only handle sequence flows + if (!is(inConnection, 'bpmn:SequenceFlow') || !is(outConnection, 'bpmn:SequenceFlow')) { + return; + } + + if (bpmnRules.canConnect(inConnection.source, outConnection.target, inConnection)) { + + // compute new, combined waypoints + var newWaypoints = getNewWaypoints(inConnection.waypoints, outConnection.waypoints); + + modeling.reconnectEnd(inConnection, outConnection.target, newWaypoints); + } + }); + +} + +inherits(RemoveElementBehavior, CommandInterceptor); + +RemoveElementBehavior.$inject = [ + 'eventBus', + 'bpmnRules', + 'modeling' +]; + + +// helpers ////////////////////// + +function getDocking(point) { + return point.original || point; +} + + +function getNewWaypoints(inWaypoints, outWaypoints) { + + var intersection = lineIntersect( + getDocking(inWaypoints[inWaypoints.length - 2]), + getDocking(inWaypoints[inWaypoints.length - 1]), + getDocking(outWaypoints[1]), + getDocking(outWaypoints[0])); + + if (intersection) { + return [].concat( + inWaypoints.slice(0, inWaypoints.length - 1), + [ intersection ], + outWaypoints.slice(1)); + } else { + return [ + getDocking(inWaypoints[0]), + getDocking(outWaypoints[outWaypoints.length - 1]) + ]; + } +} \ No newline at end of file diff --git a/lib/features/modeling/behavior/RemoveEmbeddedLabelBoundsBehavior.js b/lib/features/modeling/behavior/RemoveEmbeddedLabelBoundsBehavior.js new file mode 100644 index 0000000..d91996a --- /dev/null +++ b/lib/features/modeling/behavior/RemoveEmbeddedLabelBoundsBehavior.js @@ -0,0 +1,34 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { getDi } from '../../../util/ModelUtil'; + +/** + * BPMN specific behavior ensuring that bpmndi:Label's dc:Bounds are removed + * when shape is resized. + */ +export default function RemoveEmbeddedLabelBoundsBehavior(eventBus, modeling) { + CommandInterceptor.call(this, eventBus); + + this.preExecute('shape.resize', function(context) { + var shape = context.shape; + + var di = getDi(shape), + label = di && di.get('label'), + bounds = label && label.get('bounds'); + + if (bounds) { + modeling.updateModdleProperties(shape, label, { + bounds: undefined + }); + } + }, true); +} + +inherits(RemoveEmbeddedLabelBoundsBehavior, CommandInterceptor); + +RemoveEmbeddedLabelBoundsBehavior.$inject = [ + 'eventBus', + 'modeling' +]; \ No newline at end of file diff --git a/lib/features/modeling/behavior/RemoveParticipantBehavior.js b/lib/features/modeling/behavior/RemoveParticipantBehavior.js new file mode 100644 index 0000000..500df22 --- /dev/null +++ b/lib/features/modeling/behavior/RemoveParticipantBehavior.js @@ -0,0 +1,48 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { is } from '../../../util/ModelUtil'; + + +/** + * BPMN specific remove behavior + */ +export default function RemoveParticipantBehavior(eventBus, modeling) { + + CommandInterceptor.call(this, eventBus); + + + /** + * morph collaboration diagram into process diagram + * after the last participant has been removed + */ + + this.preExecute('shape.delete', function(context) { + + var shape = context.shape, + parent = shape.parent; + + // activate the behavior if the shape to be removed + // is a participant + if (is(shape, 'bpmn:Participant')) { + context.collaborationRoot = parent; + } + }, true); + + this.postExecute('shape.delete', function(context) { + + var collaborationRoot = context.collaborationRoot; + + if (collaborationRoot && !collaborationRoot.businessObject.participants.length) { + + // replace empty collaboration with process diagram + modeling.makeProcess(); + } + }, true); + +} + +RemoveParticipantBehavior.$inject = [ 'eventBus', 'modeling' ]; + +inherits(RemoveParticipantBehavior, CommandInterceptor); \ No newline at end of file diff --git a/lib/features/modeling/behavior/ReplaceConnectionBehavior.js b/lib/features/modeling/behavior/ReplaceConnectionBehavior.js new file mode 100644 index 0000000..99fa22b --- /dev/null +++ b/lib/features/modeling/behavior/ReplaceConnectionBehavior.js @@ -0,0 +1,184 @@ +import { + forEach, + find, + matchPattern +} from 'min-dash'; + +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { is } from '../../../util/ModelUtil'; + + +export default function ReplaceConnectionBehavior(eventBus, modeling, bpmnRules, injector) { + + CommandInterceptor.call(this, eventBus); + + var dragging = injector.get('dragging', false); + + function fixConnection(connection) { + + var source = connection.source, + target = connection.target, + parent = connection.parent; + + // do not do anything if connection + // is already deleted (may happen due to other + // behaviors plugged-in before) + if (!parent) { + return; + } + + var replacementType, + remove; + + /** + * Check if incoming or outgoing connections + * can stay or could be substituted with an + * appropriate replacement. + * + * This holds true for SequenceFlow <> MessageFlow. + */ + + if (is(connection, 'bpmn:SequenceFlow')) { + if (!bpmnRules.canConnectSequenceFlow(source, target)) { + remove = true; + } + + if (bpmnRules.canConnectMessageFlow(source, target)) { + replacementType = 'bpmn:MessageFlow'; + } + } + + // transform message flows into sequence flows, if possible + + if (is(connection, 'bpmn:MessageFlow')) { + + if (!bpmnRules.canConnectMessageFlow(source, target)) { + remove = true; + } + + if (bpmnRules.canConnectSequenceFlow(source, target)) { + replacementType = 'bpmn:SequenceFlow'; + } + } + + if (is(connection, 'bpmn:Association') && !bpmnRules.canConnectAssociation(source, target)) { + remove = true; + } + + + // remove invalid connection, + // unless it has been removed already + if (remove) { + modeling.removeConnection(connection); + } + + // replace SequenceFlow <> MessageFlow + + if (replacementType) { + modeling.connect(source, target, { + type: replacementType, + waypoints: connection.waypoints.slice() + }); + } + } + + function replaceReconnectedConnection(event) { + + var context = event.context, + connection = context.connection, + source = context.newSource || connection.source, + target = context.newTarget || connection.target, + allowed, + replacement; + + allowed = bpmnRules.canConnect(source, target); + + if (!allowed || allowed.type === connection.type) { + return; + } + + replacement = modeling.connect(source, target, { + type: allowed.type, + waypoints: connection.waypoints.slice() + }); + + // remove old connection + modeling.removeConnection(connection); + + // replace connection in context to reconnect end/start + context.connection = replacement; + + if (dragging) { + cleanDraggingSelection(connection, replacement); + } + } + + // monkey-patch selection saved in dragging in order to re-select it when operation is finished + function cleanDraggingSelection(oldConnection, newConnection) { + var context = dragging.context(), + previousSelection = context && context.payload.previousSelection, + index; + + // do nothing if not dragging or no selection was present + if (!previousSelection || !previousSelection.length) { + return; + } + + index = previousSelection.indexOf(oldConnection); + + if (index === -1) { + return; + } + + previousSelection.splice(index, 1, newConnection); + } + + // lifecycle hooks + + this.postExecuted('elements.move', function(context) { + + var closure = context.closure, + allConnections = closure.allConnections; + + forEach(allConnections, fixConnection); + }, true); + + this.preExecute('connection.reconnect', replaceReconnectedConnection); + + this.postExecuted('element.updateProperties', function(event) { + var context = event.context, + properties = context.properties, + element = context.element, + businessObject = element.businessObject, + connection; + + // remove condition on change to default + if (properties.default) { + connection = find( + element.outgoing, + matchPattern({ id: element.businessObject.default.id }) + ); + + if (connection) { + modeling.updateProperties(connection, { conditionExpression: undefined }); + } + } + + // remove default from source on change to conditional + if (properties.conditionExpression && businessObject.sourceRef.default === businessObject) { + modeling.updateProperties(element.source, { default: undefined }); + } + }); +} + +inherits(ReplaceConnectionBehavior, CommandInterceptor); + +ReplaceConnectionBehavior.$inject = [ + 'eventBus', + 'modeling', + 'bpmnRules', + 'injector' +]; diff --git a/lib/features/modeling/behavior/ReplaceElementBehaviour.js b/lib/features/modeling/behavior/ReplaceElementBehaviour.js new file mode 100644 index 0000000..420cb15 --- /dev/null +++ b/lib/features/modeling/behavior/ReplaceElementBehaviour.js @@ -0,0 +1,129 @@ +import inherits from 'inherits-browser'; + +import { forEach, reduce } from 'min-dash'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { isEventSubProcess } from '../../../util/DiUtil'; + + +/** + * BPMN-specific replace behavior. + */ +export default function ReplaceElementBehaviour( + bpmnReplace, + bpmnRules, + elementRegistry, + injector, + modeling, + selection +) { + injector.invoke(CommandInterceptor, this); + + this._bpmnReplace = bpmnReplace; + this._elementRegistry = elementRegistry; + this._selection = selection; + + // replace elements on create, e.g. during copy-paste + this.postExecuted([ 'elements.create' ], 500, function(event) { + var context = event.context, + target = context.parent, + elements = context.elements; + + var elementReplacements = reduce(elements, function(replacements, element) { + var canReplace = bpmnRules.canReplace([ element ], element.host || element.parent || target); + + return canReplace ? replacements.concat(canReplace.replacements) : replacements; + }, []); + + if (elementReplacements.length) { + this.replaceElements(elements, elementReplacements); + } + }, this); + + // replace elements on move + this.postExecuted([ 'elements.move' ], 500, function(event) { + var context = event.context, + target = context.newParent, + newHost = context.newHost, + elements = []; + + forEach(context.closure.topLevel, function(topLevelElements) { + if (isEventSubProcess(topLevelElements)) { + elements = elements.concat(topLevelElements.children); + } else { + elements = elements.concat(topLevelElements); + } + }); + + // set target to host if attaching + if (elements.length === 1 && newHost) { + target = newHost; + } + + var canReplace = bpmnRules.canReplace(elements, target); + + if (canReplace) { + this.replaceElements(elements, canReplace.replacements, newHost); + } + }, this); + + // update attachments on host replace + this.postExecute([ 'shape.replace' ], 1500, function(e) { + var context = e.context, + oldShape = context.oldShape, + newShape = context.newShape, + attachers = oldShape.attachers, + canReplace; + + if (attachers && attachers.length) { + canReplace = bpmnRules.canReplace(attachers, newShape); + + this.replaceElements(attachers, canReplace.replacements); + } + + }, this); + + // keep ID on shape replace + this.postExecuted([ 'shape.replace' ], 1500, function(e) { + var context = e.context, + oldShape = context.oldShape, + newShape = context.newShape; + + modeling.unclaimId(oldShape.businessObject.id, oldShape.businessObject); + modeling.updateProperties(newShape, { id: oldShape.id }); + }); +} + +inherits(ReplaceElementBehaviour, CommandInterceptor); + +ReplaceElementBehaviour.prototype.replaceElements = function(elements, newElements) { + var elementRegistry = this._elementRegistry, + bpmnReplace = this._bpmnReplace, + selection = this._selection; + + forEach(newElements, function(replacement) { + var newElement = { + type: replacement.newElementType + }; + + var oldElement = elementRegistry.get(replacement.oldElementId); + + var idx = elements.indexOf(oldElement); + + elements[idx] = bpmnReplace.replaceElement(oldElement, newElement, { select: false }); + }); + + if (newElements) { + selection.select(elements); + } +}; + +ReplaceElementBehaviour.$inject = [ + 'bpmnReplace', + 'bpmnRules', + 'elementRegistry', + 'injector', + 'modeling', + 'selection' +]; diff --git a/lib/features/modeling/behavior/ResizeBehavior.js b/lib/features/modeling/behavior/ResizeBehavior.js new file mode 100644 index 0000000..8c7d7ea --- /dev/null +++ b/lib/features/modeling/behavior/ResizeBehavior.js @@ -0,0 +1,176 @@ +import { is } from '../../../util/ModelUtil'; + +import { isExpanded } from '../../../util/DiUtil'; + +import { + asTRBL +} from 'diagram-js/lib/layout/LayoutUtil'; + +import { + collectLanes, + getLanesRoot +} from '../util/LaneUtil'; + +var HIGH_PRIORITY = 1500; + +export var GROUP_MIN_DIMENSIONS = { width: 140, height: 120 }; + +export var LANE_MIN_DIMENSIONS = { width: 300, height: 60 }; + +export var PARTICIPANT_MIN_DIMENSIONS = { width: 300, height: 150 }; + +export var SUB_PROCESS_MIN_DIMENSIONS = { width: 140, height: 120 }; + +export var TEXT_ANNOTATION_MIN_DIMENSIONS = { width: 50, height: 30 }; + +/** + * Set minimum bounds/resize constraints on resize. + * + * @param {EventBus} eventBus + */ +export default function ResizeBehavior(eventBus) { + eventBus.on('resize.start', HIGH_PRIORITY, function(event) { + var context = event.context, + shape = context.shape, + direction = context.direction, + balanced = context.balanced; + + if (is(shape, 'bpmn:Lane') || is(shape, 'bpmn:Participant')) { + context.resizeConstraints = getParticipantResizeConstraints(shape, direction, balanced); + } + + if (is(shape, 'bpmn:Participant')) { + context.minDimensions = PARTICIPANT_MIN_DIMENSIONS; + } + + if (is(shape, 'bpmn:SubProcess') && isExpanded(shape)) { + context.minDimensions = SUB_PROCESS_MIN_DIMENSIONS; + } + + if (is(shape, 'bpmn:TextAnnotation')) { + context.minDimensions = TEXT_ANNOTATION_MIN_DIMENSIONS; + } + }); +} + +ResizeBehavior.$inject = [ 'eventBus' ]; + + +var abs = Math.abs, + min = Math.min, + max = Math.max; + + +function addToTrbl(trbl, attr, value, choice) { + var current = trbl[attr]; + + // make sure to set the value if it does not exist + // or apply the correct value by comparing against + // choice(value, currentValue) + trbl[attr] = current === undefined ? value : choice(value, current); +} + +function addMin(trbl, attr, value) { + return addToTrbl(trbl, attr, value, min); +} + +function addMax(trbl, attr, value) { + return addToTrbl(trbl, attr, value, max); +} + +var LANE_RIGHT_PADDING = 20, + LANE_LEFT_PADDING = 50, + LANE_TOP_PADDING = 20, + LANE_BOTTOM_PADDING = 20; + +export function getParticipantResizeConstraints(laneShape, resizeDirection, balanced) { + var lanesRoot = getLanesRoot(laneShape); + + var isFirst = true, + isLast = true; + + // max top/bottom size for lanes + var allLanes = collectLanes(lanesRoot, [ lanesRoot ]); + + var laneTrbl = asTRBL(laneShape); + + var maxTrbl = {}, + minTrbl = {}; + + if (/e/.test(resizeDirection)) { + minTrbl.right = laneTrbl.left + LANE_MIN_DIMENSIONS.width; + } else + if (/w/.test(resizeDirection)) { + minTrbl.left = laneTrbl.right - LANE_MIN_DIMENSIONS.width; + } + + allLanes.forEach(function(other) { + + var otherTrbl = asTRBL(other); + + if (/n/.test(resizeDirection)) { + + if (otherTrbl.top < (laneTrbl.top - 10)) { + isFirst = false; + } + + // max top size (based on next element) + if (balanced && abs(laneTrbl.top - otherTrbl.bottom) < 10) { + addMax(maxTrbl, 'top', otherTrbl.top + LANE_MIN_DIMENSIONS.height); + } + + // min top size (based on self or nested element) + if (abs(laneTrbl.top - otherTrbl.top) < 5) { + addMin(minTrbl, 'top', otherTrbl.bottom - LANE_MIN_DIMENSIONS.height); + } + } + + if (/s/.test(resizeDirection)) { + + if (otherTrbl.bottom > (laneTrbl.bottom + 10)) { + isLast = false; + } + + // max bottom size (based on previous element) + if (balanced && abs(laneTrbl.bottom - otherTrbl.top) < 10) { + addMin(maxTrbl, 'bottom', otherTrbl.bottom - LANE_MIN_DIMENSIONS.height); + } + + // min bottom size (based on self or nested element) + if (abs(laneTrbl.bottom - otherTrbl.bottom) < 5) { + addMax(minTrbl, 'bottom', otherTrbl.top + LANE_MIN_DIMENSIONS.height); + } + } + }); + + // max top/bottom/left/right size based on flow nodes + var flowElements = lanesRoot.children.filter(function(s) { + return !s.hidden && !s.waypoints && (is(s, 'bpmn:FlowElement') || is(s, 'bpmn:Artifact')); + }); + + flowElements.forEach(function(flowElement) { + + var flowElementTrbl = asTRBL(flowElement); + + if (isFirst && /n/.test(resizeDirection)) { + addMin(minTrbl, 'top', flowElementTrbl.top - LANE_TOP_PADDING); + } + + if (/e/.test(resizeDirection)) { + addMax(minTrbl, 'right', flowElementTrbl.right + LANE_RIGHT_PADDING); + } + + if (isLast && /s/.test(resizeDirection)) { + addMax(minTrbl, 'bottom', flowElementTrbl.bottom + LANE_BOTTOM_PADDING); + } + + if (/w/.test(resizeDirection)) { + addMin(minTrbl, 'left', flowElementTrbl.left - LANE_LEFT_PADDING); + } + }); + + return { + min: minTrbl, + max: maxTrbl + }; +} \ No newline at end of file diff --git a/lib/features/modeling/behavior/ResizeLaneBehavior.js b/lib/features/modeling/behavior/ResizeLaneBehavior.js new file mode 100644 index 0000000..c8981a9 --- /dev/null +++ b/lib/features/modeling/behavior/ResizeLaneBehavior.js @@ -0,0 +1,63 @@ +import { is } from '../../../util/ModelUtil'; + +import { + roundBounds +} from 'diagram-js/lib/layout/LayoutUtil'; + +import { + hasPrimaryModifier +} from 'diagram-js/lib/util/Mouse'; + +var SLIGHTLY_HIGHER_PRIORITY = 1001; + + +/** + * Invoke {@link Modeling#resizeLane} instead of + * {@link Modeling#resizeShape} when resizing a Lane + * or Participant shape. + */ +export default function ResizeLaneBehavior(eventBus, modeling) { + + eventBus.on('resize.start', SLIGHTLY_HIGHER_PRIORITY + 500, function(event) { + var context = event.context, + shape = context.shape; + + if (is(shape, 'bpmn:Lane') || is(shape, 'bpmn:Participant')) { + + // should we resize the opposite lane(s) in + // order to compensate for the resize operation? + context.balanced = !hasPrimaryModifier(event); + } + }); + + /** + * Intercept resize end and call resize lane function instead. + */ + eventBus.on('resize.end', SLIGHTLY_HIGHER_PRIORITY, function(event) { + var context = event.context, + shape = context.shape, + canExecute = context.canExecute, + newBounds = context.newBounds; + + if (is(shape, 'bpmn:Lane') || is(shape, 'bpmn:Participant')) { + + if (canExecute) { + + // ensure we have actual pixel values for new bounds + // (important when zoom level was > 1 during move) + newBounds = roundBounds(newBounds); + + // perform the actual resize + modeling.resizeLane(shape, newBounds, context.balanced); + } + + // stop propagation + return false; + } + }); +} + +ResizeLaneBehavior.$inject = [ + 'eventBus', + 'modeling' +]; diff --git a/lib/features/modeling/behavior/RootElementReferenceBehavior.js b/lib/features/modeling/behavior/RootElementReferenceBehavior.js new file mode 100644 index 0000000..01e3b7c --- /dev/null +++ b/lib/features/modeling/behavior/RootElementReferenceBehavior.js @@ -0,0 +1,184 @@ +import inherits from 'inherits-browser'; + +import { + find, + isArray, + matchPattern, + some +} from 'min-dash'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { + add as collectionAdd, + remove as collectionRemove +} from 'diagram-js/lib/util/Collections'; + +import { + getBusinessObject, + is +} from '../../../util/ModelUtil'; + +import { isAny } from '../util/ModelingUtil'; + +import { hasEventDefinition } from '../../../util/DiUtil'; + +var LOW_PRIORITY = 500; + + +/** + * Add referenced root elements (error, escalation, message, signal) if they don't exist. + * Copy referenced root elements on copy & paste. + */ +export default function RootElementReferenceBehavior( + bpmnjs, eventBus, injector, moddleCopy, bpmnFactory +) { + injector.invoke(CommandInterceptor, this); + + function canHaveRootElementReference(element) { + return isAny(element, [ 'bpmn:ReceiveTask', 'bpmn:SendTask' ]) || + hasAnyEventDefinition(element, [ + 'bpmn:ErrorEventDefinition', + 'bpmn:EscalationEventDefinition', + 'bpmn:MessageEventDefinition', + 'bpmn:SignalEventDefinition' + ]); + } + + function hasRootElement(rootElement) { + var definitions = bpmnjs.getDefinitions(), + rootElements = definitions.get('rootElements'); + + return !!find(rootElements, matchPattern({ id: rootElement.id })); + } + + function getRootElementReferencePropertyName(eventDefinition) { + if (is(eventDefinition, 'bpmn:ErrorEventDefinition')) { + return 'errorRef'; + } else if (is(eventDefinition, 'bpmn:EscalationEventDefinition')) { + return 'escalationRef'; + } else if (is(eventDefinition, 'bpmn:MessageEventDefinition')) { + return 'messageRef'; + } else if (is(eventDefinition, 'bpmn:SignalEventDefinition')) { + return 'signalRef'; + } + } + + function getRootElement(businessObject) { + if (isAny(businessObject, [ 'bpmn:ReceiveTask', 'bpmn:SendTask' ])) { + return businessObject.get('messageRef'); + } + + var eventDefinitions = businessObject.get('eventDefinitions'), + eventDefinition = eventDefinitions[ 0 ]; + + return eventDefinition.get(getRootElementReferencePropertyName(eventDefinition)); + } + + function setRootElement(businessObject, rootElement) { + if (isAny(businessObject, [ 'bpmn:ReceiveTask', 'bpmn:SendTask' ])) { + return businessObject.set('messageRef', rootElement); + } + + var eventDefinitions = businessObject.get('eventDefinitions'), + eventDefinition = eventDefinitions[ 0 ]; + + return eventDefinition.set(getRootElementReferencePropertyName(eventDefinition), rootElement); + } + + // create shape + this.executed('shape.create', function(context) { + var shape = context.shape; + + if (!canHaveRootElementReference(shape)) { + return; + } + + var businessObject = getBusinessObject(shape), + rootElement = getRootElement(businessObject), + rootElements; + + if (rootElement && !hasRootElement(rootElement)) { + rootElements = bpmnjs.getDefinitions().get('rootElements'); + + // add root element + collectionAdd(rootElements, rootElement); + + context.addedRootElement = rootElement; + } + }, true); + + this.reverted('shape.create', function(context) { + var addedRootElement = context.addedRootElement; + + if (!addedRootElement) { + return; + } + + var rootElements = bpmnjs.getDefinitions().get('rootElements'); + + // remove root element + collectionRemove(rootElements, addedRootElement); + }, true); + + eventBus.on('copyPaste.copyElement', function(context) { + var descriptor = context.descriptor, + element = context.element; + + if (element.labelTarget || !canHaveRootElementReference(element)) { + return; + } + + var businessObject = getBusinessObject(element), + rootElement = getRootElement(businessObject); + + if (rootElement) { + + // TODO(nikku): clone on copy + descriptor.referencedRootElement = rootElement; + } + }); + + eventBus.on('copyPaste.pasteElement', LOW_PRIORITY, function(context) { + var descriptor = context.descriptor, + businessObject = descriptor.businessObject, + referencedRootElement = descriptor.referencedRootElement; + + if (!referencedRootElement) { + return; + } + + if (!hasRootElement(referencedRootElement)) { + referencedRootElement = moddleCopy.copyElement( + referencedRootElement, + bpmnFactory.create(referencedRootElement.$type) + ); + } + + setRootElement(businessObject, referencedRootElement); + + delete descriptor.referencedRootElement; + }); +} + +RootElementReferenceBehavior.$inject = [ + 'bpmnjs', + 'eventBus', + 'injector', + 'moddleCopy', + 'bpmnFactory' +]; + +inherits(RootElementReferenceBehavior, CommandInterceptor); + +// helpers ////////// + +function hasAnyEventDefinition(element, types) { + if (!isArray(types)) { + types = [ types ]; + } + + return some(types, function(type) { + return hasEventDefinition(element, type); + }); +} \ No newline at end of file diff --git a/lib/features/modeling/behavior/SpaceToolBehavior.js b/lib/features/modeling/behavior/SpaceToolBehavior.js new file mode 100644 index 0000000..dd09954 --- /dev/null +++ b/lib/features/modeling/behavior/SpaceToolBehavior.js @@ -0,0 +1,129 @@ +import { forEach } from 'min-dash'; + +import { is } from '../../../util/ModelUtil'; + +import { isExpanded } from '../../../util/DiUtil'; + +import { + GROUP_MIN_DIMENSIONS, + LANE_MIN_DIMENSIONS, + PARTICIPANT_MIN_DIMENSIONS, + SUB_PROCESS_MIN_DIMENSIONS, + TEXT_ANNOTATION_MIN_DIMENSIONS +} from './ResizeBehavior'; + +import { getChildLanes } from '../util/LaneUtil'; + +var max = Math.max; + + +export default function SpaceToolBehavior(eventBus) { + eventBus.on('spaceTool.getMinDimensions', function(context) { + var shapes = context.shapes, + axis = context.axis, + start = context.start, + minDimensions = {}; + + forEach(shapes, function(shape) { + var id = shape.id; + + if (is(shape, 'bpmn:Participant')) { + + if (isHorizontal(axis)) { + minDimensions[ id ] = PARTICIPANT_MIN_DIMENSIONS; + } else { + minDimensions[ id ] = { + width: PARTICIPANT_MIN_DIMENSIONS.width, + height: getParticipantMinHeight(shape, start) + }; + } + + } + + if (is(shape, 'bpmn:SubProcess') && isExpanded(shape)) { + minDimensions[ id ] = SUB_PROCESS_MIN_DIMENSIONS; + } + + if (is(shape, 'bpmn:TextAnnotation')) { + minDimensions[ id ] = TEXT_ANNOTATION_MIN_DIMENSIONS; + } + + if (is(shape, 'bpmn:Group')) { + minDimensions[ id ] = GROUP_MIN_DIMENSIONS; + } + }); + + return minDimensions; + }); +} + +SpaceToolBehavior.$inject = [ 'eventBus' ]; + + +// helpers ////////// +function isHorizontal(axis) { + return axis === 'x'; +} + +/** + * Get minimum height for participant taking lanes into account. + * + * @param {} participant + * @param {number} start + * + * @returns {Object} + */ +function getParticipantMinHeight(participant, start) { + var lanesMinHeight; + + if (!hasChildLanes(participant)) { + return PARTICIPANT_MIN_DIMENSIONS.height; + } + + lanesMinHeight = getLanesMinHeight(participant, start); + + return max(PARTICIPANT_MIN_DIMENSIONS.height, lanesMinHeight); +} + +function hasChildLanes(element) { + return !!getChildLanes(element).length; +} + +function getLanesMinHeight(participant, resizeStart) { + var lanes = getChildLanes(participant), + resizedLane; + + // find the nested lane which is currently resized + resizedLane = findResizedLane(lanes, resizeStart); + + // resized lane cannot shrink below the minimum height + // but remaining lanes' dimensions are kept intact + return participant.height - resizedLane.height + LANE_MIN_DIMENSIONS.height; +} + +/** + * Find nested lane which is currently resized. + * + * @param {Array} lanes + * @param {number} resizeStart + */ +function findResizedLane(lanes, resizeStart) { + var i, lane, childLanes; + + for (i = 0; i < lanes.length; i++) { + lane = lanes[i]; + + // resizing current lane or a lane nested + if (resizeStart >= lane.y && resizeStart <= lane.y + lane.height) { + childLanes = getChildLanes(lane); + + // a nested lane is resized + if (childLanes.length) { + return findResizedLane(childLanes, resizeStart); + } + + // current lane is the resized one + return lane; + } + } +} diff --git a/lib/features/modeling/behavior/SubProcessPlaneBehavior.js b/lib/features/modeling/behavior/SubProcessPlaneBehavior.js new file mode 100644 index 0000000..6aa3444 --- /dev/null +++ b/lib/features/modeling/behavior/SubProcessPlaneBehavior.js @@ -0,0 +1,566 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { find } from 'min-dash'; +import { isExpanded } from '../../../util/DiUtil'; +import { getBusinessObject, getDi, is } from '../../../util/ModelUtil'; +import { getMid } from 'diagram-js/lib/layout/LayoutUtil'; +import { getBBox } from 'diagram-js/lib/util/Elements'; +import { + getPlaneIdFromShape, + getShapeIdFromPlane, + isPlane, + toPlaneId +} from '../../../util/DrilldownUtil'; + + +var LOW_PRIORITY = 400; +var HIGH_PRIORITY = 600; + +var DEFAULT_POSITION = { + x: 180, + y: 160 +}; + + +/** + * Creates bpmndi:BPMNPlane elements and canvas planes when collapsed subprocesses are created. + * + * + * @param {Canvas} canvas + * @param {EventBus} eventBus + * @param {Modeling} modeling + * @param {ElementFactory} elementFactory + * @param {BpmnFactory} bpmnFactory + * @param {Bpmnjs} bpmnjs + * @param {ElementRegistry} elementRegistry + */ +export default function SubProcessPlaneBehavior( + canvas, eventBus, modeling, + elementFactory, bpmnFactory, bpmnjs, elementRegistry) { + + CommandInterceptor.call(this, eventBus); + + this._canvas = canvas; + this._eventBus = eventBus; + this._modeling = modeling; + this._elementFactory = elementFactory; + this._bpmnFactory = bpmnFactory; + this._bpmnjs = bpmnjs; + this._elementRegistry = elementRegistry; + + var self = this; + + function isCollapsedSubProcess(element) { + return is(element, 'bpmn:SubProcess') && !isExpanded(element); + } + + function createRoot(context) { + var shape = context.shape, + rootElement = context.newRootElement; + + var businessObject = getBusinessObject(shape); + + rootElement = self._addDiagram(rootElement || businessObject); + + context.newRootElement = canvas.addRootElement(rootElement); + } + + function removeRoot(context) { + var shape = context.shape; + + var businessObject = getBusinessObject(shape); + self._removeDiagram(businessObject); + + var rootElement = context.newRootElement = elementRegistry.get(getPlaneIdFromShape(businessObject)); + + canvas.removeRootElement(rootElement); + } + + // add plane elements for newly created sub-processes + // this ensures we can actually drill down into the element + this.executed('shape.create', function(context) { + var shape = context.shape; + if (!isCollapsedSubProcess(shape)) { + return; + } + + createRoot(context); + }, true); + + + this.postExecuted('shape.create', function(context) { + var shape = context.shape, + rootElement = context.newRootElement; + + if (!rootElement || !shape.children) { + return; + } + + self._showRecursively(shape.children); + + self._moveChildrenToShape(shape, rootElement); + }, true); + + + this.reverted('shape.create', function(context) { + var shape = context.shape; + if (!isCollapsedSubProcess(shape)) { + return; + } + + removeRoot(context); + }, true); + + + this.preExecuted('shape.delete', function(context) { + var shape = context.shape; + if (!isCollapsedSubProcess(shape)) { + return; + } + + var attachedRoot = elementRegistry.get(getPlaneIdFromShape(shape)); + + if (!attachedRoot) { + return; + } + + modeling.removeElements(attachedRoot.children.slice()); + }, true); + + + this.executed('shape.delete', function(context) { + var shape = context.shape; + if (!isCollapsedSubProcess(shape)) { + return; + } + removeRoot(context); + }, true); + + + this.reverted('shape.delete', function(context) { + var shape = context.shape; + if (!isCollapsedSubProcess(shape)) { + return; + } + + createRoot(context); + }, true); + + + this.preExecuted('shape.replace', function(context) { + var oldShape = context.oldShape; + var newShape = context.newShape; + + if (!isCollapsedSubProcess(oldShape) || !isCollapsedSubProcess(newShape)) { + return; + } + + // old plane could have content, + // we remove it so it is not recursively deleted from 'shape.delete' + context.oldRoot = canvas.removeRootElement(getPlaneIdFromShape(oldShape)); + }, true); + + + this.postExecuted('shape.replace', function(context) { + var newShape = context.newShape, + source = context.oldRoot, + target = canvas.findRoot(getPlaneIdFromShape(newShape)); + + if (!source || !target) { + return; + } + var elements = source.children; + + modeling.moveElements(elements, { x: 0, y: 0 }, target); + }, true); + + + // rename primary elements when the secondary element changes + // this ensures rootElement.id = element.id + '_plane' + this.executed('element.updateProperties', function(context) { + var shape = context.element; + + if (!is(shape, 'bpmn:SubProcess')) { + return; + } + + var properties = context.properties; + var oldProperties = context.oldProperties; + + var oldId = oldProperties.id, + newId = properties.id; + + if (oldId === newId) { + return; + } + + if (isPlane(shape)) { + elementRegistry.updateId(shape, toPlaneId(newId)); + elementRegistry.updateId(oldId, newId); + + return; + } + + var planeElement = elementRegistry.get(toPlaneId(oldId)); + + if (!planeElement) { + return; + } + + elementRegistry.updateId(toPlaneId(oldId), toPlaneId(newId)); + }, true); + + + this.reverted('element.updateProperties', function(context) { + var shape = context.element; + + if (!is(shape, 'bpmn:SubProcess')) { + return; + } + + var properties = context.properties; + var oldProperties = context.oldProperties; + + var oldId = oldProperties.id, + newId = properties.id; + + if (oldId === newId) { + return; + } + + if (isPlane(shape)) { + elementRegistry.updateId(shape, toPlaneId(oldId)); + elementRegistry.updateId(newId, oldId); + + return; + } + + var planeElement = elementRegistry.get(toPlaneId(newId)); + + if (!planeElement) { + return; + } + + elementRegistry.updateId(planeElement, toPlaneId(oldId)); + }, true); + + // re-throw element.changed to re-render primary shape if associated plane has + // changed (e.g. bpmn:name property has changed) + eventBus.on('element.changed', function(context) { + var element = context.element; + + if (!isPlane(element)) { + return; + } + + var plane = element; + + var primaryShape = elementRegistry.get(getShapeIdFromPlane(plane)); + + // do not re-throw if no associated primary shape (e.g. bpmn:Process) + if (!primaryShape || primaryShape === plane) { + return; + } + + eventBus.fire('element.changed', { element: primaryShape }); + }); + + + // create/remove plane for the subprocess + this.executed('shape.toggleCollapse', LOW_PRIORITY, function(context) { + var shape = context.shape; + + if (!is(shape, 'bpmn:SubProcess')) { + return; + } + + if (!isExpanded(shape)) { + createRoot(context); + self._showRecursively(shape.children); + } else { + removeRoot(context); + } + + }, true); + + + // create/remove plane for the subprocess + this.reverted('shape.toggleCollapse', LOW_PRIORITY, function(context) { + var shape = context.shape; + + if (!is(shape, 'bpmn:SubProcess')) { + return; + } + + if (!isExpanded(shape)) { + createRoot(context); + self._showRecursively(shape.children); + } else { + removeRoot(context); + } + + }, true); + + // move elements between planes + this.postExecuted('shape.toggleCollapse', HIGH_PRIORITY, function(context) { + var shape = context.shape; + + if (!is(shape, 'bpmn:SubProcess')) { + return; + } + + var rootElement = context.newRootElement; + + if (!rootElement) { + return; + } + + if (!isExpanded(shape)) { + + // collapsed + self._moveChildrenToShape(shape, rootElement); + + } else { + self._moveChildrenToShape(rootElement, shape); + } + }, true); + + + // copy-paste /////////// + + // add elements in plane to tree + eventBus.on('copyPaste.createTree', function(context) { + var element = context.element, + children = context.children; + + if (!isCollapsedSubProcess(element)) { + return; + } + + var id = getPlaneIdFromShape(element); + var parent = elementRegistry.get(id); + + if (parent) { + + // do not copy invisible root element + children.push.apply(children, parent.children); + } + }); + + // set plane children as direct children of collapsed shape + eventBus.on('copyPaste.copyElement', function(context) { + var descriptor = context.descriptor, + element = context.element, + elements = context.elements; + + var parent = element.parent; + + var isPlane = is(getDi(parent), 'bpmndi:BPMNPlane'); + if (!isPlane) { + return; + } + + var parentId = getShapeIdFromPlane(parent); + + var referencedShape = find(elements, function(element) { + return element.id === parentId; + }); + + if (!referencedShape) { + return; + } + + descriptor.parent = referencedShape.id; + }); + + // hide children during pasting + eventBus.on('copyPaste.pasteElement', function(context) { + var descriptor = context.descriptor; + + if (!descriptor.parent) { + return; + } + + if (isCollapsedSubProcess(descriptor.parent) || descriptor.parent.hidden) { + descriptor.hidden = true; + } + }); + +} + +inherits(SubProcessPlaneBehavior, CommandInterceptor); + +/** + * Moves the child elements from source to target. + * + * If the target is a plane, the children are moved to the top left corner. + * Otherwise, the center of the target is used. + * + * @param {Object|djs.model.Base} source + * @param {Object|djs.model.Base} target + */ +SubProcessPlaneBehavior.prototype._moveChildrenToShape = function(source, target) { + var modeling = this._modeling; + + var children = source.children; + var offset; + + if (!children) { + return; + } + + // add external labels that weren't children of sub process + children = children.concat(children.reduce(function(labels, child) { + if (child.label && child.label.parent !== source) { + return labels.concat(child.label); + } + + return labels; + }, [])); + + // only change plane if there are no visible children, but don't move them + var visibleChildren = children.filter(function(child) { + return !child.hidden; + }); + + if (!visibleChildren.length) { + modeling.moveElements(children, { x: 0, y: 0 }, target, { autoResize: false }); + return; + } + + var childrenBounds = getBBox(visibleChildren); + + // target is a plane + if (!target.x) { + offset = { + x: DEFAULT_POSITION.x - childrenBounds.x, + y: DEFAULT_POSITION.y - childrenBounds.y + }; + } + + // source is a plane + else { + + // move relative to the center of the shape + var targetMid = getMid(target); + var childrenMid = getMid(childrenBounds); + + offset = { + x: targetMid.x - childrenMid.x, + y: targetMid.y - childrenMid.y + }; + } + + modeling.moveElements(children, offset, target, { autoResize: false }); +}; + +/** + * Sets `hidden` property on all children of the given shape. + * + * @param {Array} elements + * @param {Boolean} [hidden] + * @returns {Array} all child elements + */ +SubProcessPlaneBehavior.prototype._showRecursively = function(elements, hidden) { + var self = this; + + var result = []; + elements.forEach(function(element) { + element.hidden = !!hidden; + + result = result.concat(element); + + if (element.children) { + result = result.concat( + self._showRecursively(element.children, element.collapsed || hidden) + ); + } + }); + + return result; +}; + +/** +* Adds a given rootElement to the bpmnDi diagrams. +* +* @param {Object} rootElement +* @returns {Object} planeElement +*/ +SubProcessPlaneBehavior.prototype._addDiagram = function(planeElement) { + var bpmnjs = this._bpmnjs; + var diagrams = bpmnjs.getDefinitions().diagrams; + + if (!planeElement.businessObject) { + planeElement = this._createNewDiagram(planeElement); + } + + diagrams.push(planeElement.di.$parent); + + return planeElement; +}; + + +/** +* Creates a new plane element for the given sub process. +* +* @param {Object} bpmnElement +* +* @return {Object} new diagram element +*/ +SubProcessPlaneBehavior.prototype._createNewDiagram = function(bpmnElement) { + var bpmnFactory = this._bpmnFactory; + var elementFactory = this._elementFactory; + + var diPlane = bpmnFactory.create('bpmndi:BPMNPlane', { + bpmnElement: bpmnElement + }); + var diDiagram = bpmnFactory.create('bpmndi:BPMNDiagram', { + plane: diPlane + }); + diPlane.$parent = diDiagram; + + // add a virtual element (not being drawn), + // a copy cat of our BpmnImporter code + var planeElement = elementFactory.createRoot({ + id: getPlaneIdFromShape(bpmnElement), + type: bpmnElement.$type, + di: diPlane, + businessObject: bpmnElement, + collapsed: true + }); + + return planeElement; +}; + +/** + * Removes the diagram for a given root element + * + * @param {Object} rootElement + * @returns {Object} removed bpmndi:BPMNDiagram + */ +SubProcessPlaneBehavior.prototype._removeDiagram = function(rootElement) { + var bpmnjs = this._bpmnjs; + + var diagrams = bpmnjs.getDefinitions().diagrams; + + var removedDiagram = find(diagrams, function(diagram) { + return diagram.plane.bpmnElement.id === rootElement.id; + }); + + diagrams.splice(diagrams.indexOf(removedDiagram), 1); + + return removedDiagram; +}; + + +SubProcessPlaneBehavior.$inject = [ + 'canvas', + 'eventBus', + 'modeling', + 'elementFactory', + 'bpmnFactory', + 'bpmnjs', + 'elementRegistry' +]; diff --git a/lib/features/modeling/behavior/SubProcessStartEventBehavior.js b/lib/features/modeling/behavior/SubProcessStartEventBehavior.js new file mode 100644 index 0000000..e4c1720 --- /dev/null +++ b/lib/features/modeling/behavior/SubProcessStartEventBehavior.js @@ -0,0 +1,49 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { is } from '../../../util/ModelUtil'; +import { isExpanded } from '../../../util/DiUtil.js'; + +/** + * Add start event replacing element with expanded sub process. + * + * @param {Injector} injector + * @param {Modeling} modeling + */ +export default function SubProcessStartEventBehavior(injector, modeling) { + injector.invoke(CommandInterceptor, this); + + this.postExecuted('shape.replace', function(event) { + var oldShape = event.context.oldShape, + newShape = event.context.newShape; + + if ( + !is(newShape, 'bpmn:SubProcess') || + ! (is(oldShape, 'bpmn:Task') || is(oldShape, 'bpmn:CallActivity')) || + !isExpanded(newShape) + ) { + return; + } + + var position = getStartEventPosition(newShape); + + modeling.createShape({ type: 'bpmn:StartEvent' }, position, newShape); + }); +} + +SubProcessStartEventBehavior.$inject = [ + 'injector', + 'modeling' +]; + +inherits(SubProcessStartEventBehavior, CommandInterceptor); + +// helpers ////////// + +function getStartEventPosition(shape) { + return { + x: shape.x + shape.width / 6, + y: shape.y + shape.height / 2 + }; +} diff --git a/lib/features/modeling/behavior/ToggleCollapseConnectionBehaviour.js b/lib/features/modeling/behavior/ToggleCollapseConnectionBehaviour.js new file mode 100644 index 0000000..b677617 --- /dev/null +++ b/lib/features/modeling/behavior/ToggleCollapseConnectionBehaviour.js @@ -0,0 +1,74 @@ + +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { + forEach +} from 'min-dash'; + +import { getMid } from 'diagram-js/lib/layout/LayoutUtil'; +import { selfAndAllChildren } from 'diagram-js/lib/util/Elements'; + +import { isExpanded } from '../../../util/DiUtil'; + + +export default function ToggleCollapseConnectionBehaviour( + eventBus, modeling +) { + + CommandInterceptor.call(this, eventBus); + + this.postExecuted('shape.toggleCollapse', 1500, function(context) { + + // var shape = context.shape; + var shape = context.shape; + + // only change connections when collapsing + if (isExpanded(shape)) { + return; + } + + var allChildren = selfAndAllChildren(shape); + + allChildren.forEach(function(child) { + + // Ensure that the connection array is not modified during iteration + var incomingConnections = child.incoming.slice(), + outgoingConnections = child.outgoing.slice(); + + forEach(incomingConnections, function(c) { + handleConnection(c, true); + }); + + forEach(outgoingConnections, function(c) { + handleConnection(c, false); + }); + }); + + + function handleConnection(c, incoming) { + if (allChildren.indexOf(c.source) !== -1 && allChildren.indexOf(c.target) !== -1) { + return; + } + + if (incoming) { + modeling.reconnectEnd(c, shape, getMid(shape)); + } else { + modeling.reconnectStart(c, shape, getMid(shape)); + } + + } + + }, true); + +} + +inherits(ToggleCollapseConnectionBehaviour, CommandInterceptor); + +ToggleCollapseConnectionBehaviour.$inject = [ + 'eventBus', + 'modeling', +]; + + diff --git a/lib/features/modeling/behavior/ToggleElementCollapseBehaviour.js b/lib/features/modeling/behavior/ToggleElementCollapseBehaviour.js new file mode 100644 index 0000000..55f259f --- /dev/null +++ b/lib/features/modeling/behavior/ToggleElementCollapseBehaviour.js @@ -0,0 +1,149 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { + getDi, + is +} from '../../../util/ModelUtil'; + +import { + computeChildrenBBox +} from 'diagram-js/lib/features/resize/ResizeUtil'; + + +var LOW_PRIORITY = 500; + + +export default function ToggleElementCollapseBehaviour( + eventBus, elementFactory, modeling, + resize) { + + CommandInterceptor.call(this, eventBus); + + + function hideEmptyLabels(children) { + if (children.length) { + children.forEach(function(child) { + if (child.type === 'label' && !child.businessObject.name) { + child.hidden = true; + } + }); + } + } + + function expandedBounds(shape, defaultSize) { + var children = shape.children, + newBounds = defaultSize, + visibleElements, + visibleBBox; + + visibleElements = filterVisible(children).concat([ shape ]); + + visibleBBox = computeChildrenBBox(visibleElements); + + if (visibleBBox) { + + // center to visibleBBox with max(defaultSize, childrenBounds) + newBounds.width = Math.max(visibleBBox.width, newBounds.width); + newBounds.height = Math.max(visibleBBox.height, newBounds.height); + + newBounds.x = visibleBBox.x + (visibleBBox.width - newBounds.width) / 2; + newBounds.y = visibleBBox.y + (visibleBBox.height - newBounds.height) / 2; + } else { + + // center to collapsed shape with defaultSize + newBounds.x = shape.x + (shape.width - newBounds.width) / 2; + newBounds.y = shape.y + (shape.height - newBounds.height) / 2; + } + + return newBounds; + } + + function collapsedBounds(shape, defaultSize) { + + return { + x: shape.x + (shape.width - defaultSize.width) / 2, + y: shape.y + (shape.height - defaultSize.height) / 2, + width: defaultSize.width, + height: defaultSize.height + }; + } + + this.executed([ 'shape.toggleCollapse' ], LOW_PRIORITY, function(e) { + + var context = e.context, + shape = context.shape; + + if (!is(shape, 'bpmn:SubProcess')) { + return; + } + + if (!shape.collapsed) { + + // all children got made visible through djs, hide empty labels + hideEmptyLabels(shape.children); + + // remove collapsed marker + getDi(shape).isExpanded = true; + } else { + + // place collapsed marker + getDi(shape).isExpanded = false; + } + }); + + this.reverted([ 'shape.toggleCollapse' ], LOW_PRIORITY, function(e) { + + var context = e.context; + var shape = context.shape; + + + // revert removing/placing collapsed marker + if (!shape.collapsed) { + getDi(shape).isExpanded = true; + + } else { + getDi(shape).isExpanded = false; + } + }); + + this.postExecuted([ 'shape.toggleCollapse' ], LOW_PRIORITY, function(e) { + var shape = e.context.shape, + defaultSize = elementFactory.getDefaultSize(shape), + newBounds; + + if (shape.collapsed) { + + // resize to default size of collapsed shapes + newBounds = collapsedBounds(shape, defaultSize); + } else { + + // resize to bounds of max(visible children, defaultSize) + newBounds = expandedBounds(shape, defaultSize); + } + + modeling.resizeShape(shape, newBounds, null, { + autoResize: shape.collapsed ? false : 'nwse' + }); + }); + +} + + +inherits(ToggleElementCollapseBehaviour, CommandInterceptor); + +ToggleElementCollapseBehaviour.$inject = [ + 'eventBus', + 'elementFactory', + 'modeling' +]; + + +// helpers ////////////////////// + +function filterVisible(elements) { + return elements.filter(function(e) { + return !e.hidden; + }); +} \ No newline at end of file diff --git a/lib/features/modeling/behavior/UnclaimIdBehavior.js b/lib/features/modeling/behavior/UnclaimIdBehavior.js new file mode 100644 index 0000000..ffafb87 --- /dev/null +++ b/lib/features/modeling/behavior/UnclaimIdBehavior.js @@ -0,0 +1,58 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { is } from '../../../util/ModelUtil'; +import { isExpanded } from '../../../util/DiUtil'; +import { isLabel } from '../../../util/LabelUtil'; + + +/** + * Unclaims model IDs on element deletion. + * + * @param {Canvas} canvas + * @param {Injector} injector + * @param {Moddle} moddle + * @param {Modeling} modeling + */ +export default function UnclaimIdBehavior(canvas, injector, moddle, modeling) { + injector.invoke(CommandInterceptor, this); + + this.preExecute('shape.delete', function(event) { + var context = event.context, + shape = context.shape, + shapeBo = shape.businessObject; + + if (isLabel(shape)) { + return; + } + + if (is(shape, 'bpmn:Participant') && isExpanded(shape)) { + moddle.ids.unclaim(shapeBo.processRef.id); + } + + modeling.unclaimId(shapeBo.id, shapeBo); + }); + + + this.preExecute('connection.delete', function(event) { + var context = event.context, + connection = context.connection, + connectionBo = connection.businessObject; + + modeling.unclaimId(connectionBo.id, connectionBo); + }); + + this.preExecute('canvas.updateRoot', function() { + var rootElement = canvas.getRootElement(), + rootElementBo = rootElement.businessObject; + + if (is(rootElement, 'bpmn:Collaboration')) { + moddle.ids.unclaim(rootElementBo.id); + } + }); +} + +inherits(UnclaimIdBehavior, CommandInterceptor); + +UnclaimIdBehavior.$inject = [ 'canvas', 'injector', 'moddle', 'modeling' ]; \ No newline at end of file diff --git a/lib/features/modeling/behavior/UnsetDefaultFlowBehavior.js b/lib/features/modeling/behavior/UnsetDefaultFlowBehavior.js new file mode 100644 index 0000000..18da9c7 --- /dev/null +++ b/lib/features/modeling/behavior/UnsetDefaultFlowBehavior.js @@ -0,0 +1,57 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { + getBusinessObject, + is +} from '../../../util/ModelUtil'; + + +/** + * A behavior that unsets the Default property of + * sequence flow source on element delete, if the + * removed element is the Gateway or Task's default flow. + * + * @param {EventBus} eventBus + * @param {Modeling} modeling + */ +export default function DeleteSequenceFlowBehavior(eventBus, modeling) { + + CommandInterceptor.call(this, eventBus); + + + this.preExecute('connection.delete', function(event) { + var context = event.context, + connection = context.connection, + source = connection.source; + + if (isDefaultFlow(connection, source)) { + modeling.updateProperties(source, { + 'default': null + }); + } + }); +} + +inherits(DeleteSequenceFlowBehavior, CommandInterceptor); + +DeleteSequenceFlowBehavior.$inject = [ + 'eventBus', + 'modeling' +]; + + +// helpers ////////////////////// + +function isDefaultFlow(connection, source) { + + if (!is(connection, 'bpmn:SequenceFlow')) { + return false; + } + + var sourceBo = getBusinessObject(source), + sequenceFlow = getBusinessObject(connection); + + return sourceBo.get('default') === sequenceFlow; +} \ No newline at end of file diff --git a/lib/features/modeling/behavior/UpdateFlowNodeRefsBehavior.js b/lib/features/modeling/behavior/UpdateFlowNodeRefsBehavior.js new file mode 100644 index 0000000..7f9ea5c --- /dev/null +++ b/lib/features/modeling/behavior/UpdateFlowNodeRefsBehavior.js @@ -0,0 +1,158 @@ +import inherits from 'inherits-browser'; + +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import { + is +} from '../../../util/ModelUtil'; + +var LOW_PRIORITY = 500, + HIGH_PRIORITY = 5000; + + +/** + * BPMN specific delete lane behavior + */ +export default function UpdateFlowNodeRefsBehavior(eventBus, modeling, translate) { + + CommandInterceptor.call(this, eventBus); + + /** + * Ok, this is it: + * + * We have to update the Lane#flowNodeRefs _and_ + * FlowNode#lanes with every FlowNode move/resize and + * Lane move/resize. + * + * We want to group that stuff to recompute containments + * as efficient as possible. + * + * Yea! + */ + + // the update context + var context; + + + function initContext() { + context = context || new UpdateContext(); + context.enter(); + + return context; + } + + function getContext() { + if (!context) { + throw new Error(translate('out of bounds release')); + } + + return context; + } + + function releaseContext() { + + if (!context) { + throw new Error(translate('out of bounds release')); + } + + var triggerUpdate = context.leave(); + + if (triggerUpdate) { + modeling.updateLaneRefs(context.flowNodes, context.lanes); + + context = null; + } + + return triggerUpdate; + } + + + var laneRefUpdateEvents = [ + 'spaceTool', + 'lane.add', + 'lane.resize', + 'lane.split', + 'elements.create', + 'elements.delete', + 'elements.move', + 'shape.create', + 'shape.delete', + 'shape.move', + 'shape.resize' + ]; + + + // listen to a lot of stuff to group lane updates + + this.preExecute(laneRefUpdateEvents, HIGH_PRIORITY, function(event) { + initContext(); + }); + + this.postExecuted(laneRefUpdateEvents, LOW_PRIORITY, function(event) { + releaseContext(); + }); + + + // Mark flow nodes + lanes that need an update + + this.preExecute([ + 'shape.create', + 'shape.move', + 'shape.delete', + 'shape.resize' + ], function(event) { + + var context = event.context, + shape = context.shape; + + var updateContext = getContext(); + + // no need to update labels + if (shape.labelTarget) { + return; + } + + if (is(shape, 'bpmn:Lane')) { + updateContext.addLane(shape); + } + + if (is(shape, 'bpmn:FlowNode')) { + updateContext.addFlowNode(shape); + } + }); +} + +UpdateFlowNodeRefsBehavior.$inject = [ + 'eventBus', + 'modeling' , + 'translate' +]; + +inherits(UpdateFlowNodeRefsBehavior, CommandInterceptor); + + +function UpdateContext() { + + this.flowNodes = []; + this.lanes = []; + + this.counter = 0; + + this.addLane = function(lane) { + this.lanes.push(lane); + }; + + this.addFlowNode = function(flowNode) { + this.flowNodes.push(flowNode); + }; + + this.enter = function() { + this.counter++; + }; + + this.leave = function() { + this.counter--; + + return !this.counter; + }; +} \ No newline at end of file diff --git a/lib/features/modeling/behavior/index.js b/lib/features/modeling/behavior/index.js new file mode 100644 index 0000000..059c74b --- /dev/null +++ b/lib/features/modeling/behavior/index.js @@ -0,0 +1,119 @@ +import AdaptiveLabelPositioningBehavior from './AdaptiveLabelPositioningBehavior'; +import AppendBehavior from './AppendBehavior'; +import AssociationBehavior from './AssociationBehavior'; +import AttachEventBehavior from './AttachEventBehavior'; +import BoundaryEventBehavior from './BoundaryEventBehavior'; +import CreateBehavior from './CreateBehavior'; +import CreateDataObjectBehavior from './CreateDataObjectBehavior'; +import CreateParticipantBehavior from './CreateParticipantBehavior'; +import DataInputAssociationBehavior from './DataInputAssociationBehavior'; +import DataStoreBehavior from './DataStoreBehavior'; +import DeleteLaneBehavior from './DeleteLaneBehavior'; +import DetachEventBehavior from './DetachEventBehavior'; +import DropOnFlowBehavior from './DropOnFlowBehavior'; +import EventBasedGatewayBehavior from './EventBasedGatewayBehavior'; +import FixHoverBehavior from './FixHoverBehavior'; +import GroupBehavior from './GroupBehavior'; +import ImportDockingFix from './ImportDockingFix'; +import IsHorizontalFix from './IsHorizontalFix'; +import LabelBehavior from './LabelBehavior'; +import LayoutConnectionBehavior from './LayoutConnectionBehavior'; +import MessageFlowBehavior from './MessageFlowBehavior'; +import ModelingFeedback from './ModelingFeedback'; +import RemoveEmbeddedLabelBoundsBehavior from './RemoveEmbeddedLabelBoundsBehavior'; +import RemoveElementBehavior from './RemoveElementBehavior'; +import RemoveParticipantBehavior from './RemoveParticipantBehavior'; +import ReplaceConnectionBehavior from './ReplaceConnectionBehavior'; +import ReplaceElementBehaviour from './ReplaceElementBehaviour'; +import ResizeBehavior from './ResizeBehavior'; +import ResizeLaneBehavior from './ResizeLaneBehavior'; +import RootElementReferenceBehavior from './RootElementReferenceBehavior'; +import SpaceToolBehavior from './SpaceToolBehavior'; +import SubProcessPlaneBehavior from './SubProcessPlaneBehavior'; +import SubProcessStartEventBehavior from './SubProcessStartEventBehavior'; +import ToggleCollapseConnectionBehaviour from './ToggleCollapseConnectionBehaviour'; +import ToggleElementCollapseBehaviour from './ToggleElementCollapseBehaviour'; +import UnclaimIdBehavior from './UnclaimIdBehavior'; +import UnsetDefaultFlowBehavior from './UnsetDefaultFlowBehavior'; +import UpdateFlowNodeRefsBehavior from './UpdateFlowNodeRefsBehavior'; + +export default { + __init__: [ + 'adaptiveLabelPositioningBehavior', + 'appendBehavior', + 'associationBehavior', + 'attachEventBehavior', + 'boundaryEventBehavior', + 'createBehavior', + 'createDataObjectBehavior', + 'createParticipantBehavior', + 'dataInputAssociationBehavior', + 'dataStoreBehavior', + 'deleteLaneBehavior', + 'detachEventBehavior', + 'dropOnFlowBehavior', + 'eventBasedGatewayBehavior', + 'fixHoverBehavior', + 'groupBehavior', + 'importDockingFix', + 'isHorizontalFix', + 'labelBehavior', + 'layoutConnectionBehavior', + 'messageFlowBehavior', + 'modelingFeedback', + 'removeElementBehavior', + 'removeEmbeddedLabelBoundsBehavior', + 'removeParticipantBehavior', + 'replaceConnectionBehavior', + 'replaceElementBehaviour', + 'resizeBehavior', + 'resizeLaneBehavior', + 'rootElementReferenceBehavior', + 'spaceToolBehavior', + 'subProcessPlaneBehavior', + 'subProcessStartEventBehavior', + 'toggleCollapseConnectionBehaviour', + 'toggleElementCollapseBehaviour', + 'unclaimIdBehavior', + 'updateFlowNodeRefsBehavior', + 'unsetDefaultFlowBehavior' + ], + adaptiveLabelPositioningBehavior: [ 'type', AdaptiveLabelPositioningBehavior ], + appendBehavior: [ 'type', AppendBehavior ], + associationBehavior: [ 'type', AssociationBehavior ], + attachEventBehavior: [ 'type', AttachEventBehavior ], + boundaryEventBehavior: [ 'type', BoundaryEventBehavior ], + createBehavior: [ 'type', CreateBehavior ], + createDataObjectBehavior: [ 'type', CreateDataObjectBehavior ], + createParticipantBehavior: [ 'type', CreateParticipantBehavior ], + dataInputAssociationBehavior: [ 'type', DataInputAssociationBehavior ], + dataStoreBehavior: [ 'type', DataStoreBehavior ], + deleteLaneBehavior: [ 'type', DeleteLaneBehavior ], + detachEventBehavior: [ 'type', DetachEventBehavior ], + dropOnFlowBehavior: [ 'type', DropOnFlowBehavior ], + eventBasedGatewayBehavior: [ 'type', EventBasedGatewayBehavior ], + fixHoverBehavior: [ 'type', FixHoverBehavior ], + groupBehavior: [ 'type', GroupBehavior ], + importDockingFix: [ 'type', ImportDockingFix ], + isHorizontalFix: [ 'type', IsHorizontalFix ], + labelBehavior: [ 'type', LabelBehavior ], + layoutConnectionBehavior: [ 'type', LayoutConnectionBehavior ], + messageFlowBehavior: [ 'type', MessageFlowBehavior ], + modelingFeedback: [ 'type', ModelingFeedback ], + removeElementBehavior: [ 'type', RemoveElementBehavior ], + removeEmbeddedLabelBoundsBehavior: [ 'type', RemoveEmbeddedLabelBoundsBehavior ], + removeParticipantBehavior: [ 'type', RemoveParticipantBehavior ], + replaceConnectionBehavior: [ 'type', ReplaceConnectionBehavior ], + replaceElementBehaviour: [ 'type', ReplaceElementBehaviour ], + resizeBehavior: [ 'type', ResizeBehavior ], + resizeLaneBehavior: [ 'type', ResizeLaneBehavior ], + rootElementReferenceBehavior: [ 'type', RootElementReferenceBehavior ], + spaceToolBehavior: [ 'type', SpaceToolBehavior ], + subProcessPlaneBehavior: [ 'type', SubProcessPlaneBehavior ], + subProcessStartEventBehavior: [ 'type', SubProcessStartEventBehavior ], + toggleCollapseConnectionBehaviour: [ 'type', ToggleCollapseConnectionBehaviour ], + toggleElementCollapseBehaviour : [ 'type', ToggleElementCollapseBehaviour ], + unclaimIdBehavior: [ 'type', UnclaimIdBehavior ], + unsetDefaultFlowBehavior: [ 'type', UnsetDefaultFlowBehavior ], + updateFlowNodeRefsBehavior: [ 'type', UpdateFlowNodeRefsBehavior ] +}; diff --git a/lib/features/modeling/behavior/util/CategoryUtil.js b/lib/features/modeling/behavior/util/CategoryUtil.js new file mode 100644 index 0000000..c5abc30 --- /dev/null +++ b/lib/features/modeling/behavior/util/CategoryUtil.js @@ -0,0 +1,83 @@ +import { + add as collectionAdd, + remove as collectionRemove +} from 'diagram-js/lib/util/Collections'; + + +/** + * Creates a new bpmn:CategoryValue inside a new bpmn:Category + * + * @param {BpmnFactory} bpmnFactory + * + * @return {ModdleElement} categoryValue. + */ +export function createCategory(bpmnFactory) { + return bpmnFactory.create('bpmn:Category'); +} + +/** + * Creates a new bpmn:CategoryValue inside a new bpmn:Category + * + * @param {BpmnFactory} bpmnFactory + * + * @return {ModdleElement} categoryValue. + */ +export function createCategoryValue(bpmnFactory) { + return bpmnFactory.create('bpmn:CategoryValue'); +} + +/** + * Adds category value to definitions + * + * @param {ModdleElement} categoryValue + * @param {ModdleElement} category + * @param {ModdleElement} definitions + * + * @return {ModdleElement} categoryValue + */ +export function linkCategoryValue(categoryValue, category, definitions) { + collectionAdd(category.get('categoryValue'), categoryValue); + categoryValue.$parent = category; + + collectionAdd(definitions.get('rootElements'), category); + category.$parent = definitions; + + return categoryValue; +} + +/** + * Unlink category value from parent + * + * @param {ModdleElement} categoryValue + * + * @return {ModdleElement} categoryValue + */ +export function unlinkCategoryValue(categoryValue) { + var category = categoryValue.$parent; + + if (category) { + collectionRemove(category.get('categoryValue'), categoryValue); + categoryValue.$parent = null; + } + + return categoryValue; +} + +/** + * Unlink category from parent + * + * @param {ModdleElement} category + * @param {ModdleElement} definitions + * + * @return {ModdleElement} categoryValue + */ +export function unlinkCategory(category) { + var definitions = category.$parent; + + if (definitions) { + collectionRemove(definitions.get('rootElements'), category); + category.$parent = null; + } + + return category; +} \ No newline at end of file diff --git a/lib/features/modeling/behavior/util/ConnectionLayoutUtil.js b/lib/features/modeling/behavior/util/ConnectionLayoutUtil.js new file mode 100644 index 0000000..20300f0 --- /dev/null +++ b/lib/features/modeling/behavior/util/ConnectionLayoutUtil.js @@ -0,0 +1,16 @@ +import { getAnchorPointAdjustment } from './LayoutUtil'; + +/** + * Calculate the new point after the connection waypoints got updated. + * + * @param {djs.model.Label} label + * @param {Array} newWaypoints + * @param {Array} oldWaypoints + * @param {Object} hints + * + * @return {Point} point + */ +export function getConnectionAdjustment(position, newWaypoints, oldWaypoints, hints) { + return getAnchorPointAdjustment(position, newWaypoints, oldWaypoints, hints).point; +} + diff --git a/lib/features/modeling/behavior/util/GeometricUtil.js b/lib/features/modeling/behavior/util/GeometricUtil.js new file mode 100644 index 0000000..c29d64f --- /dev/null +++ b/lib/features/modeling/behavior/util/GeometricUtil.js @@ -0,0 +1 @@ +export * from 'diagram-js/lib/features/bendpoints/GeometricUtil'; \ No newline at end of file diff --git a/lib/features/modeling/behavior/util/LabelLayoutUtil.js b/lib/features/modeling/behavior/util/LabelLayoutUtil.js new file mode 100644 index 0000000..8b925d7 --- /dev/null +++ b/lib/features/modeling/behavior/util/LabelLayoutUtil.js @@ -0,0 +1,33 @@ +import { findNewLineStartIndex, getAnchorPointAdjustment } from './LayoutUtil'; + +export function findNewLabelLineStartIndex(oldWaypoints, newWaypoints, attachment, hints) { + return findNewLineStartIndex(oldWaypoints, newWaypoints, attachment, hints); +} + + +/** + * Calculate the required adjustment (move delta) for the given label + * after the connection waypoints got updated. + * + * @param {djs.model.Label} label + * @param {Array} newWaypoints + * @param {Array} oldWaypoints + * @param {Object} hints + * + * @return {Point} delta + */ +export function getLabelAdjustment(label, newWaypoints, oldWaypoints, hints) { + var labelPosition = getLabelMid(label); + + return getAnchorPointAdjustment(labelPosition, newWaypoints, oldWaypoints, hints).delta; +} + + +// HELPERS ////////////////////// + +function getLabelMid(label) { + return { + x: label.x + label.width / 2, + y: label.y + label.height / 2 + }; +} diff --git a/lib/features/modeling/behavior/util/LayoutUtil.js b/lib/features/modeling/behavior/util/LayoutUtil.js new file mode 100644 index 0000000..39e0f7e --- /dev/null +++ b/lib/features/modeling/behavior/util/LayoutUtil.js @@ -0,0 +1,230 @@ +import { + getDistancePointPoint, + rotateVector, + getAngle +} from './GeometricUtil'; + +import { + getAttachment +} from './LineAttachmentUtil'; + +import { + roundPoint +} from 'diagram-js/lib/layout/LayoutUtil'; + + +export function findNewLineStartIndex(oldWaypoints, newWaypoints, attachment, hints) { + + var index = attachment.segmentIndex; + + var offset = newWaypoints.length - oldWaypoints.length; + + // segmentMove happened + if (hints.segmentMove) { + + var oldSegmentStartIndex = hints.segmentMove.segmentStartIndex, + newSegmentStartIndex = hints.segmentMove.newSegmentStartIndex; + + // if point was on moved segment return new segment index + if (index === oldSegmentStartIndex) { + return newSegmentStartIndex; + } + + // point is after new segment index + if (index >= newSegmentStartIndex) { + return (index + offset < newSegmentStartIndex) ? newSegmentStartIndex : index + offset; + } + + // if point is before new segment index + return index; + } + + // bendpointMove happened + if (hints.bendpointMove) { + + var insert = hints.bendpointMove.insert, + bendpointIndex = hints.bendpointMove.bendpointIndex, + newIndex; + + // waypoints length didnt change + if (offset === 0) { + return index; + } + + // point behind new/removed bendpoint + if (index >= bendpointIndex) { + newIndex = insert ? index + 1 : index - 1; + } + + // point before new/removed bendpoint + if (index < bendpointIndex) { + + newIndex = index; + + // decide point should take right or left segment + if (insert && attachment.type !== 'bendpoint' && bendpointIndex - 1 === index) { + + var rel = relativePositionMidWaypoint(newWaypoints, bendpointIndex); + + if (rel < attachment.relativeLocation) { + newIndex++; + } + } + } + + return newIndex; + } + + // start/end changed + if (offset === 0) { + return index; + } + + if (hints.connectionStart && index === 0) { + return 0; + } + + if (hints.connectionEnd && index === oldWaypoints.length - 2) { + return newWaypoints.length - 2; + } + + // if nothing fits, take the middle segment + return Math.floor((newWaypoints.length - 2) / 2); +} + + +/** + * Calculate the required adjustment (move delta) for the given point + * after the connection waypoints got updated. + * + * @param {Point} position + * @param {Array} newWaypoints + * @param {Array} oldWaypoints + * @param {Object} hints + * + * @return {Object} result + * @return {Point} result.point + * @return {Point} result.delta + */ +export function getAnchorPointAdjustment(position, newWaypoints, oldWaypoints, hints) { + + var dx = 0, + dy = 0; + + var oldPosition = { + point: position, + delta: { x: 0, y: 0 } + }; + + // get closest attachment + var attachment = getAttachment(position, oldWaypoints), + oldLabelLineIndex = attachment.segmentIndex, + newLabelLineIndex = findNewLineStartIndex(oldWaypoints, newWaypoints, attachment, hints); + + + // should never happen + // TODO(@janstuemmel): throw an error here when connectionSegmentMove is refactored + if (newLabelLineIndex < 0 || + newLabelLineIndex > newWaypoints.length - 2 || + newLabelLineIndex === null) { + return oldPosition; + } + + var oldLabelLine = getLine(oldWaypoints, oldLabelLineIndex), + newLabelLine = getLine(newWaypoints, newLabelLineIndex), + oldFoot = attachment.position; + + var relativeFootPosition = getRelativeFootPosition(oldLabelLine, oldFoot), + angleDelta = getAngleDelta(oldLabelLine, newLabelLine); + + // special rule if label on bendpoint + if (attachment.type === 'bendpoint') { + + var offset = newWaypoints.length - oldWaypoints.length, + oldBendpointIndex = attachment.bendpointIndex, + oldBendpoint = oldWaypoints[oldBendpointIndex]; + + // bendpoint position hasn't changed, return same position + if (newWaypoints.indexOf(oldBendpoint) !== -1) { + return oldPosition; + } + + // new bendpoint and old bendpoint have same index, then just return the offset + if (offset === 0) { + var newBendpoint = newWaypoints[oldBendpointIndex]; + + dx = newBendpoint.x - attachment.position.x, + dy = newBendpoint.y - attachment.position.y; + + return { + delta: { + x: dx, + y: dy + }, + point: { + x: position.x + dx, + y: position.y + dy + } + }; + } + + // if bendpoints get removed + if (offset < 0 && oldBendpointIndex !== 0 && oldBendpointIndex < oldWaypoints.length - 1) { + relativeFootPosition = relativePositionMidWaypoint(oldWaypoints, oldBendpointIndex); + } + } + + var newFoot = { + x: (newLabelLine[1].x - newLabelLine[0].x) * relativeFootPosition + newLabelLine[0].x, + y: (newLabelLine[1].y - newLabelLine[0].y) * relativeFootPosition + newLabelLine[0].y + }; + + // the rotated vector to label + var newLabelVector = rotateVector({ + x: position.x - oldFoot.x, + y: position.y - oldFoot.y + }, angleDelta); + + // the new relative position + dx = newFoot.x + newLabelVector.x - position.x; + dy = newFoot.y + newLabelVector.y - position.y; + + return { + point: roundPoint(newFoot), + delta: roundPoint({ + x: dx, + y: dy + }) + }; +} + + +// HELPERS ////////////////////// + +function relativePositionMidWaypoint(waypoints, idx) { + + var distanceSegment1 = getDistancePointPoint(waypoints[idx - 1], waypoints[idx]), + distanceSegment2 = getDistancePointPoint(waypoints[idx], waypoints[idx + 1]); + + var relativePosition = distanceSegment1 / (distanceSegment1 + distanceSegment2); + + return relativePosition; +} + +function getAngleDelta(l1, l2) { + var a1 = getAngle(l1), + a2 = getAngle(l2); + return a2 - a1; +} + +function getLine(waypoints, idx) { + return [ waypoints[idx], waypoints[idx + 1] ]; +} + +function getRelativeFootPosition(line, foot) { + + var length = getDistancePointPoint(line[0], line[1]), + lengthToFoot = getDistancePointPoint(line[0], foot); + + return length === 0 ? 0 : lengthToFoot / length; +} diff --git a/lib/features/modeling/behavior/util/LineAttachmentUtil.js b/lib/features/modeling/behavior/util/LineAttachmentUtil.js new file mode 100644 index 0000000..da90b8f --- /dev/null +++ b/lib/features/modeling/behavior/util/LineAttachmentUtil.js @@ -0,0 +1,229 @@ +var sqrt = Math.sqrt, + min = Math.min, + max = Math.max, + abs = Math.abs; + +/** + * Calculate the square (power to two) of a number. + * + * @param {number} n + * + * @return {number} + */ +function sq(n) { + return Math.pow(n, 2); +} + +/** + * Get distance between two points. + * + * @param {Point} p1 + * @param {Point} p2 + * + * @return {number} + */ +function getDistance(p1, p2) { + return sqrt(sq(p1.x - p2.x) + sq(p1.y - p2.y)); +} + +/** + * Return the attachment of the given point on the specified line. + * + * The attachment is either a bendpoint (attached to the given point) + * or segment (attached to a location on a line segment) attachment: + * + * ```javascript + * var pointAttachment = { + * type: 'bendpoint', + * bendpointIndex: 3, + * position: { x: 10, y: 10 } // the attach point on the line + * }; + * + * var segmentAttachment = { + * type: 'segment', + * segmentIndex: 2, + * relativeLocation: 0.31, // attach point location between 0 (at start) and 1 (at end) + * position: { x: 10, y: 10 } // the attach point on the line + * }; + * ``` + * + * @param {Point} point + * @param {Array} line + * + * @return {Object} attachment + */ +export function getAttachment(point, line) { + + var idx = 0, + segmentStart, + segmentEnd, + segmentStartDistance, + segmentEndDistance, + attachmentPosition, + minDistance, + intersections, + attachment, + attachmentDistance, + closestAttachmentDistance, + closestAttachment; + + for (idx = 0; idx < line.length - 1; idx++) { + + segmentStart = line[idx]; + segmentEnd = line[idx + 1]; + + if (pointsEqual(segmentStart, segmentEnd)) { + intersections = [ segmentStart ]; + } else { + segmentStartDistance = getDistance(point, segmentStart); + segmentEndDistance = getDistance(point, segmentEnd); + + minDistance = min(segmentStartDistance, segmentEndDistance); + + intersections = getCircleSegmentIntersections(segmentStart, segmentEnd, point, minDistance); + } + + if (intersections.length < 1) { + throw new Error('expected between [1, 2] circle -> line intersections'); + } + + // one intersection -> bendpoint attachment + if (intersections.length === 1) { + attachment = { + type: 'bendpoint', + position: intersections[0], + segmentIndex: idx, + bendpointIndex: pointsEqual(segmentStart, intersections[0]) ? idx : idx + 1 + }; + } + + // two intersections -> segment attachment + if (intersections.length === 2) { + + attachmentPosition = mid(intersections[0], intersections[1]); + + attachment = { + type: 'segment', + position: attachmentPosition, + segmentIndex: idx, + relativeLocation: getDistance(segmentStart, attachmentPosition) / getDistance(segmentStart, segmentEnd) + }; + } + + attachmentDistance = getDistance(attachment.position, point); + + if (!closestAttachment || closestAttachmentDistance > attachmentDistance) { + closestAttachment = attachment; + closestAttachmentDistance = attachmentDistance; + } + } + + return closestAttachment; +} + +/** + * Gets the intersection between a circle and a line segment. + * + * @param {Point} s1 segment start + * @param {Point} s2 segment end + * @param {Point} cc circle center + * @param {number} cr circle radius + * + * @return {Array} intersections + */ +function getCircleSegmentIntersections(s1, s2, cc, cr) { + + var baX = s2.x - s1.x; + var baY = s2.y - s1.y; + var caX = cc.x - s1.x; + var caY = cc.y - s1.y; + + var a = baX * baX + baY * baY; + var bBy2 = baX * caX + baY * caY; + var c = caX * caX + caY * caY - cr * cr; + + var pBy2 = bBy2 / a; + var q = c / a; + + var disc = pBy2 * pBy2 - q; + + // check against negative value to work around + // negative, very close to zero results (-4e-15) + // being produced in some environments + if (disc < 0 && disc > -0.000001) { + disc = 0; + } + + if (disc < 0) { + return []; + } + + // if disc == 0 ... dealt with later + var tmpSqrt = sqrt(disc); + var abScalingFactor1 = -pBy2 + tmpSqrt; + var abScalingFactor2 = -pBy2 - tmpSqrt; + + var i1 = { + x: s1.x - baX * abScalingFactor1, + y: s1.y - baY * abScalingFactor1 + }; + + if (disc === 0) { // abScalingFactor1 == abScalingFactor2 + return [ i1 ]; + } + + var i2 = { + x: s1.x - baX * abScalingFactor2, + y: s1.y - baY * abScalingFactor2 + }; + + // return only points on line segment + return [ i1, i2 ].filter(function(p) { + return isPointInSegment(p, s1, s2); + }); +} + + +function isPointInSegment(p, segmentStart, segmentEnd) { + return ( + fenced(p.x, segmentStart.x, segmentEnd.x) && + fenced(p.y, segmentStart.y, segmentEnd.y) + ); +} + +function fenced(n, rangeStart, rangeEnd) { + + // use matching threshold to work around + // precision errors in intersection computation + + return ( + n >= min(rangeStart, rangeEnd) - EQUAL_THRESHOLD && + n <= max(rangeStart, rangeEnd) + EQUAL_THRESHOLD + ); +} + +/** + * Calculate mid of two points. + * + * @param {Point} p1 + * @param {Point} p2 + * + * @return {Point} + */ +function mid(p1, p2) { + + return { + x: (p1.x + p2.x) / 2, + y: (p1.y + p2.y) / 2 + }; +} + +var EQUAL_THRESHOLD = 0.1; + +function pointsEqual(p1, p2) { + + return ( + abs(p1.x - p2.x) <= EQUAL_THRESHOLD && + abs(p1.y - p2.y) <= EQUAL_THRESHOLD + ); +} diff --git a/lib/features/modeling/behavior/util/LineIntersect.js b/lib/features/modeling/behavior/util/LineIntersect.js new file mode 100644 index 0000000..a26f920 --- /dev/null +++ b/lib/features/modeling/behavior/util/LineIntersect.js @@ -0,0 +1,36 @@ +/** + * Returns the intersection between two line segments a and b. + * + * @param {Point} l1s + * @param {Point} l1e + * @param {Point} l2s + * @param {Point} l2e + * + * @return {Point} + */ +export default function lineIntersect(l1s, l1e, l2s, l2e) { + + // if the lines intersect, the result contains the x and y of the + // intersection (treating the lines as infinite) and booleans for + // whether line segment 1 or line segment 2 contain the point + var denominator, a, b, c, numerator; + + denominator = ((l2e.y - l2s.y) * (l1e.x - l1s.x)) - ((l2e.x - l2s.x) * (l1e.y - l1s.y)); + + if (denominator == 0) { + return null; + } + + a = l1s.y - l2s.y; + b = l1s.x - l2s.x; + numerator = ((l2e.x - l2s.x) * a) - ((l2e.y - l2s.y) * b); + + c = numerator / denominator; + + // if we cast these lines infinitely in + // both directions, they intersect here + return { + x: Math.round(l1s.x + (c * (l1e.x - l1s.x))), + y: Math.round(l1s.y + (c * (l1e.y - l1s.y))) + }; +} \ No newline at end of file diff --git a/lib/features/modeling/behavior/util/ResizeUtil.js b/lib/features/modeling/behavior/util/ResizeUtil.js new file mode 100644 index 0000000..49b66e6 --- /dev/null +++ b/lib/features/modeling/behavior/util/ResizeUtil.js @@ -0,0 +1 @@ +export { getParticipantResizeConstraints } from '../ResizeBehavior'; \ No newline at end of file diff --git a/lib/features/modeling/cmd/AddLaneHandler.js b/lib/features/modeling/cmd/AddLaneHandler.js new file mode 100644 index 0000000..bc9a683 --- /dev/null +++ b/lib/features/modeling/cmd/AddLaneHandler.js @@ -0,0 +1,101 @@ +import { + filter +} from 'min-dash'; + +import { + eachElement +} from 'diagram-js/lib/util/Elements'; + +import { + getLanesRoot, + getChildLanes, + LANE_INDENTATION +} from '../util/LaneUtil'; + + +/** + * A handler that allows us to add a new lane + * above or below an existing one. + * + * @param {Modeling} modeling + * @param {SpaceTool} spaceTool + */ +export default function AddLaneHandler(modeling, spaceTool) { + this._modeling = modeling; + this._spaceTool = spaceTool; +} + +AddLaneHandler.$inject = [ + 'modeling', + 'spaceTool' +]; + + +AddLaneHandler.prototype.preExecute = function(context) { + + var spaceTool = this._spaceTool, + modeling = this._modeling; + + var shape = context.shape, + location = context.location; + + var lanesRoot = getLanesRoot(shape); + + var isRoot = lanesRoot === shape, + laneParent = isRoot ? shape : shape.parent; + + var existingChildLanes = getChildLanes(laneParent); + + // (0) add a lane if we currently got none and are adding to root + if (!existingChildLanes.length) { + modeling.createShape({ type: 'bpmn:Lane' }, { + x: shape.x + LANE_INDENTATION, + y: shape.y, + width: shape.width - LANE_INDENTATION, + height: shape.height + }, laneParent); + } + + // (1) collect affected elements to create necessary space + var allAffected = []; + + eachElement(lanesRoot, function(element) { + allAffected.push(element); + + // handle element labels in the diagram root + if (element.label) { + allAffected.push(element.label); + } + + if (element === shape) { + return []; + } + + return filter(element.children, function(c) { + return c !== shape; + }); + }); + + var offset = location === 'top' ? -120 : 120, + lanePosition = location === 'top' ? shape.y : shape.y + shape.height, + spacePos = lanePosition + (location === 'top' ? 10 : -10), + direction = location === 'top' ? 'n' : 's'; + + var adjustments = spaceTool.calculateAdjustments(allAffected, 'y', offset, spacePos); + + spaceTool.makeSpace( + adjustments.movingShapes, + adjustments.resizingShapes, + { x: 0, y: offset }, + direction, + spacePos + ); + + // (2) create new lane at open space + context.newLane = modeling.createShape({ type: 'bpmn:Lane' }, { + x: shape.x + (isRoot ? LANE_INDENTATION : 0), + y: lanePosition - (location === 'top' ? 120 : 0), + width: shape.width - (isRoot ? LANE_INDENTATION : 0), + height: 120 + }, laneParent); +}; diff --git a/lib/features/modeling/cmd/IdClaimHandler.js b/lib/features/modeling/cmd/IdClaimHandler.js new file mode 100644 index 0000000..d8bf3c8 --- /dev/null +++ b/lib/features/modeling/cmd/IdClaimHandler.js @@ -0,0 +1,36 @@ +export default function IdClaimHandler(moddle) { + this._moddle = moddle; +} + +IdClaimHandler.$inject = [ 'moddle' ]; + + +IdClaimHandler.prototype.execute = function(context) { + var ids = this._moddle.ids, + id = context.id, + element = context.element, + claiming = context.claiming; + + if (claiming) { + ids.claim(id, element); + } else { + ids.unclaim(id); + } +}; + +/** + * Command revert implementation. + */ +IdClaimHandler.prototype.revert = function(context) { + var ids = this._moddle.ids, + id = context.id, + element = context.element, + claiming = context.claiming; + + if (claiming) { + ids.unclaim(id); + } else { + ids.claim(id, element); + } +}; + diff --git a/lib/features/modeling/cmd/ResizeLaneHandler.js b/lib/features/modeling/cmd/ResizeLaneHandler.js new file mode 100644 index 0000000..3e7ea09 --- /dev/null +++ b/lib/features/modeling/cmd/ResizeLaneHandler.js @@ -0,0 +1,134 @@ +import { is } from '../../../util/ModelUtil'; + +import { + getLanesRoot, + computeLanesResize +} from '../util/LaneUtil'; + +import { + eachElement +} from 'diagram-js/lib/util/Elements'; + +import { + asTRBL +} from 'diagram-js/lib/layout/LayoutUtil'; + +import { + substractTRBL +} from 'diagram-js/lib/features/resize/ResizeUtil'; + + +/** + * A handler that resizes a lane. + * + * @param {Modeling} modeling + */ +export default function ResizeLaneHandler(modeling, spaceTool) { + this._modeling = modeling; + this._spaceTool = spaceTool; +} + +ResizeLaneHandler.$inject = [ + 'modeling', + 'spaceTool' +]; + + +ResizeLaneHandler.prototype.preExecute = function(context) { + + var shape = context.shape, + newBounds = context.newBounds, + balanced = context.balanced; + + if (balanced !== false) { + this.resizeBalanced(shape, newBounds); + } else { + this.resizeSpace(shape, newBounds); + } +}; + + +/** + * Resize balanced, adjusting next / previous lane sizes. + * + * @param {djs.model.Shape} shape + * @param {Bounds} newBounds + */ +ResizeLaneHandler.prototype.resizeBalanced = function(shape, newBounds) { + + var modeling = this._modeling; + + var resizeNeeded = computeLanesResize(shape, newBounds); + + // resize the lane + modeling.resizeShape(shape, newBounds); + + // resize other lanes as needed + resizeNeeded.forEach(function(r) { + modeling.resizeShape(r.shape, r.newBounds); + }); +}; + + +/** + * Resize, making actual space and moving below / above elements. + * + * @param {djs.model.Shape} shape + * @param {Bounds} newBounds + */ +ResizeLaneHandler.prototype.resizeSpace = function(shape, newBounds) { + var spaceTool = this._spaceTool; + + var shapeTrbl = asTRBL(shape), + newTrbl = asTRBL(newBounds); + + var trblDiff = substractTRBL(newTrbl, shapeTrbl); + + var lanesRoot = getLanesRoot(shape); + + var allAffected = [], + allLanes = []; + + eachElement(lanesRoot, function(element) { + allAffected.push(element); + + if (is(element, 'bpmn:Lane') || is(element, 'bpmn:Participant')) { + allLanes.push(element); + } + + return element.children; + }); + + var change, + spacePos, + direction, + offset, + adjustments; + + if (trblDiff.bottom || trblDiff.top) { + + change = trblDiff.bottom || trblDiff.top; + spacePos = shape.y + (trblDiff.bottom ? shape.height : 0) + (trblDiff.bottom ? -10 : 10); + direction = trblDiff.bottom ? 's' : 'n'; + + offset = trblDiff.top > 0 || trblDiff.bottom < 0 ? -change : change; + + adjustments = spaceTool.calculateAdjustments(allAffected, 'y', offset, spacePos); + + spaceTool.makeSpace(adjustments.movingShapes, adjustments.resizingShapes, { x: 0, y: change }, direction); + } + + + if (trblDiff.left || trblDiff.right) { + + change = trblDiff.right || trblDiff.left; + spacePos = shape.x + (trblDiff.right ? shape.width : 0) + (trblDiff.right ? -10 : 100); + direction = trblDiff.right ? 'e' : 'w'; + + offset = trblDiff.left > 0 || trblDiff.right < 0 ? -change : change; + + adjustments = spaceTool.calculateAdjustments(allLanes, 'x', offset, spacePos); + + spaceTool.makeSpace(adjustments.movingShapes, adjustments.resizingShapes, { x: change, y: 0 }, direction); + } +}; \ No newline at end of file diff --git a/lib/features/modeling/cmd/SetColorHandler.js b/lib/features/modeling/cmd/SetColorHandler.js new file mode 100644 index 0000000..5b67086 --- /dev/null +++ b/lib/features/modeling/cmd/SetColorHandler.js @@ -0,0 +1,144 @@ +import { + assign, + forEach, + isString, + pick +} from 'min-dash'; + +import { + getDi +} from '../../../util/ModelUtil'; + +import { + isLabel +} from '../../../util/LabelUtil'; + +var DEFAULT_COLORS = { + fill: undefined, + stroke: undefined +}; + + +export default function SetColorHandler(commandStack) { + this._commandStack = commandStack; + + this._normalizeColor = function(color) { + + // Remove color for falsy values. + if (!color) { + return undefined; + } + + if (isString(color)) { + var hexColor = colorToHex(color); + + if (hexColor) { + return hexColor; + } + } + + throw new Error('invalid color value: ' + color); + }; +} + +SetColorHandler.$inject = [ + 'commandStack' +]; + + +SetColorHandler.prototype.postExecute = function(context) { + var elements = context.elements, + colors = context.colors || DEFAULT_COLORS; + + var self = this; + + var di = {}; + + if ('fill' in colors) { + assign(di, { + 'background-color': this._normalizeColor(colors.fill) }); + } + + if ('stroke' in colors) { + assign(di, { + 'border-color': this._normalizeColor(colors.stroke) }); + } + + forEach(elements, function(element) { + var assignedDi = isConnection(element) ? pick(di, [ 'border-color' ]) : di; + + // TODO @barmac: remove once we drop bpmn.io properties + ensureLegacySupport(assignedDi); + + if (isLabel(element)) { + + // set label colors as bpmndi:BPMNLabel#color + self._commandStack.execute('element.updateModdleProperties', { + element: element, + moddleElement: getDi(element).label, + properties: { + color: di['border-color'] + } + }); + } else { + + // set colors bpmndi:BPMNEdge or bpmndi:BPMNShape + self._commandStack.execute('element.updateProperties', { + element: element, + properties: { + di: assignedDi + } + }); + } + }); + +}; + +/** + * Convert color from rgb(a)/hsl to hex. Returns `null` for unknown color names and for colors + * with alpha less than 1.0. This depends on `` serialization of the `context.fillStyle`. + * Cf. https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-fillstyle + * + * @example + * ```js + * var color = 'fuchsia'; + * console.log(colorToHex(color)); + * // "#ff00ff" + * color = 'rgba(1,2,3,0.4)'; + * console.log(colorToHex(color)); + * // null + * ``` + * + * @param {string} color + * @returns {string|null} + */ +function colorToHex(color) { + var context = document.createElement('canvas').getContext('2d'); + + // (0) Start with transparent to account for browser default values. + context.fillStyle = 'transparent'; + + // (1) Assign color so that it's serialized. + context.fillStyle = color; + + // (2) Return null for non-hex serialization result. + return /^#[0-9a-fA-F]{6}$/.test(context.fillStyle) ? context.fillStyle : null; +} + +function isConnection(element) { + return !!element.waypoints; +} + +/** + * Add legacy properties if required. + * @param {{ 'border-color': string?, 'background-color': string? }} di + */ +function ensureLegacySupport(di) { + if ('border-color' in di) { + di.stroke = di['border-color']; + } + + if ('background-color' in di) { + di.fill = di['background-color']; + } +} diff --git a/lib/features/modeling/cmd/SplitLaneHandler.js b/lib/features/modeling/cmd/SplitLaneHandler.js new file mode 100644 index 0000000..aafa55c --- /dev/null +++ b/lib/features/modeling/cmd/SplitLaneHandler.js @@ -0,0 +1,85 @@ +import { + getChildLanes, + LANE_INDENTATION +} from '../util/LaneUtil'; + + +/** + * A handler that splits a lane into a number of sub-lanes, + * creating new sub lanes, if necessary. + * + * @param {Modeling} modeling + */ +export default function SplitLaneHandler(modeling, translate) { + this._modeling = modeling; + this._translate = translate; +} + +SplitLaneHandler.$inject = [ + 'modeling', + 'translate' +]; + + +SplitLaneHandler.prototype.preExecute = function(context) { + + var modeling = this._modeling, + translate = this._translate; + + var shape = context.shape, + newLanesCount = context.count; + + var childLanes = getChildLanes(shape), + existingLanesCount = childLanes.length; + + if (existingLanesCount > newLanesCount) { + throw new Error(translate('more than {count} child lanes', { count: newLanesCount })); + } + + var newLanesHeight = Math.round(shape.height / newLanesCount); + + // Iterate from top to bottom in child lane order, + // resizing existing lanes and creating new ones + // so that they split the parent proportionally. + // + // Due to rounding related errors, the bottom lane + // needs to take up all the remaining space. + var laneY, + laneHeight, + laneBounds, + newLaneAttrs, + idx; + + for (idx = 0; idx < newLanesCount; idx++) { + + laneY = shape.y + idx * newLanesHeight; + + // if bottom lane + if (idx === newLanesCount - 1) { + laneHeight = shape.height - (newLanesHeight * idx); + } else { + laneHeight = newLanesHeight; + } + + laneBounds = { + x: shape.x + LANE_INDENTATION, + y: laneY, + width: shape.width - LANE_INDENTATION, + height: laneHeight + }; + + if (idx < existingLanesCount) { + + // resize existing lane + modeling.resizeShape(childLanes[idx], laneBounds); + } else { + + // create a new lane at position + newLaneAttrs = { + type: 'bpmn:Lane' + }; + + modeling.createShape(newLaneAttrs, laneBounds, shape); + } + } +}; diff --git a/lib/features/modeling/cmd/UpdateCanvasRootHandler.js b/lib/features/modeling/cmd/UpdateCanvasRootHandler.js new file mode 100644 index 0000000..70aced7 --- /dev/null +++ b/lib/features/modeling/cmd/UpdateCanvasRootHandler.js @@ -0,0 +1,85 @@ +import { + add as collectionAdd, + remove as collectionRemove +} from 'diagram-js/lib/util/Collections'; + +import { getDi } from '../../../util/ModelUtil'; + + +export default function UpdateCanvasRootHandler(canvas, modeling) { + this._canvas = canvas; + this._modeling = modeling; +} + +UpdateCanvasRootHandler.$inject = [ + 'canvas', + 'modeling' +]; + + +UpdateCanvasRootHandler.prototype.execute = function(context) { + + var canvas = this._canvas; + + var newRoot = context.newRoot, + newRootBusinessObject = newRoot.businessObject, + oldRoot = canvas.getRootElement(), + oldRootBusinessObject = oldRoot.businessObject, + bpmnDefinitions = oldRootBusinessObject.$parent, + diPlane = getDi(oldRoot); + + // (1) replace process old <> new root + canvas.setRootElement(newRoot); + canvas.removeRootElement(oldRoot); + + // (2) update root elements + collectionAdd(bpmnDefinitions.rootElements, newRootBusinessObject); + newRootBusinessObject.$parent = bpmnDefinitions; + + collectionRemove(bpmnDefinitions.rootElements, oldRootBusinessObject); + oldRootBusinessObject.$parent = null; + + // (3) wire di + oldRoot.di = null; + + diPlane.bpmnElement = newRootBusinessObject; + newRoot.di = diPlane; + + context.oldRoot = oldRoot; + + // TODO(nikku): return changed elements? + // return [ newRoot, oldRoot ]; +}; + + +UpdateCanvasRootHandler.prototype.revert = function(context) { + + var canvas = this._canvas; + + var newRoot = context.newRoot, + newRootBusinessObject = newRoot.businessObject, + oldRoot = context.oldRoot, + oldRootBusinessObject = oldRoot.businessObject, + bpmnDefinitions = newRootBusinessObject.$parent, + diPlane = getDi(newRoot); + + // (1) replace process old <> new root + canvas.setRootElement(oldRoot); + canvas.removeRootElement(newRoot); + + // (2) update root elements + collectionRemove(bpmnDefinitions.rootElements, newRootBusinessObject); + newRootBusinessObject.$parent = null; + + collectionAdd(bpmnDefinitions.rootElements, oldRootBusinessObject); + oldRootBusinessObject.$parent = bpmnDefinitions; + + // (3) wire di + newRoot.di = null; + + diPlane.bpmnElement = oldRootBusinessObject; + oldRoot.di = diPlane; + + // TODO(nikku): return changed elements? + // return [ newRoot, oldRoot ]; +}; \ No newline at end of file diff --git a/lib/features/modeling/cmd/UpdateFlowNodeRefsHandler.js b/lib/features/modeling/cmd/UpdateFlowNodeRefsHandler.js new file mode 100644 index 0000000..efd30d0 --- /dev/null +++ b/lib/features/modeling/cmd/UpdateFlowNodeRefsHandler.js @@ -0,0 +1,193 @@ +import { + collectLanes, + getLanesRoot +} from '../util/LaneUtil'; + +import { + is +} from '../../../util/ModelUtil'; + +import { + add as collectionAdd, + remove as collectionRemove +} from 'diagram-js/lib/util/Collections'; + +import { + asTRBL +} from 'diagram-js/lib/layout/LayoutUtil'; + +var FLOW_NODE_REFS_ATTR = 'flowNodeRef', + LANES_ATTR = 'lanes'; + + +/** + * A handler that updates lane refs on changed elements + */ +export default function UpdateFlowNodeRefsHandler(elementRegistry) { + this._elementRegistry = elementRegistry; +} + +UpdateFlowNodeRefsHandler.$inject = [ + 'elementRegistry' +]; + + +UpdateFlowNodeRefsHandler.prototype.computeUpdates = function(flowNodeShapes, laneShapes) { + + var handledNodes = []; + + var updates = []; + + var participantCache = {}; + + var allFlowNodeShapes = []; + + function isInLaneShape(element, laneShape) { + + var laneTrbl = asTRBL(laneShape); + + var elementMid = { + x: element.x + element.width / 2, + y: element.y + element.height / 2 + }; + + return elementMid.x > laneTrbl.left && + elementMid.x < laneTrbl.right && + elementMid.y > laneTrbl.top && + elementMid.y < laneTrbl.bottom; + } + + function addFlowNodeShape(flowNodeShape) { + if (handledNodes.indexOf(flowNodeShape) === -1) { + allFlowNodeShapes.push(flowNodeShape); + handledNodes.push(flowNodeShape); + } + } + + function getAllLaneShapes(flowNodeShape) { + + var root = getLanesRoot(flowNodeShape); + + if (!participantCache[root.id]) { + participantCache[root.id] = collectLanes(root); + } + + return participantCache[root.id]; + } + + function getNewLanes(flowNodeShape) { + if (!flowNodeShape.parent) { + return []; + } + + var allLaneShapes = getAllLaneShapes(flowNodeShape); + + return allLaneShapes.filter(function(l) { + return isInLaneShape(flowNodeShape, l); + }).map(function(shape) { + return shape.businessObject; + }); + } + + laneShapes.forEach(function(laneShape) { + var root = getLanesRoot(laneShape); + + if (!root || handledNodes.indexOf(root) !== -1) { + return; + } + + var children = root.children.filter(function(c) { + return is(c, 'bpmn:FlowNode'); + }); + + children.forEach(addFlowNodeShape); + + handledNodes.push(root); + }); + + flowNodeShapes.forEach(addFlowNodeShape); + + + allFlowNodeShapes.forEach(function(flowNodeShape) { + + var flowNode = flowNodeShape.businessObject; + + var lanes = flowNode.get(LANES_ATTR), + remove = lanes.slice(), + add = getNewLanes(flowNodeShape); + + updates.push({ flowNode: flowNode, remove: remove, add: add }); + }); + + laneShapes.forEach(function(laneShape) { + + var lane = laneShape.businessObject; + + // lane got removed XX-) + if (!laneShape.parent) { + lane.get(FLOW_NODE_REFS_ATTR).forEach(function(flowNode) { + updates.push({ flowNode: flowNode, remove: [ lane ], add: [] }); + }); + } + }); + + return updates; +}; + +UpdateFlowNodeRefsHandler.prototype.execute = function(context) { + + var updates = context.updates; + + if (!updates) { + updates = context.updates = this.computeUpdates(context.flowNodeShapes, context.laneShapes); + } + + + updates.forEach(function(update) { + + var flowNode = update.flowNode, + lanes = flowNode.get(LANES_ATTR); + + // unwire old + update.remove.forEach(function(oldLane) { + collectionRemove(lanes, oldLane); + collectionRemove(oldLane.get(FLOW_NODE_REFS_ATTR), flowNode); + }); + + // wire new + update.add.forEach(function(newLane) { + collectionAdd(lanes, newLane); + collectionAdd(newLane.get(FLOW_NODE_REFS_ATTR), flowNode); + }); + }); + + // TODO(nikku): return changed elements + // return [ ... ]; +}; + + +UpdateFlowNodeRefsHandler.prototype.revert = function(context) { + + var updates = context.updates; + + updates.forEach(function(update) { + + var flowNode = update.flowNode, + lanes = flowNode.get(LANES_ATTR); + + // unwire new + update.add.forEach(function(newLane) { + collectionRemove(lanes, newLane); + collectionRemove(newLane.get(FLOW_NODE_REFS_ATTR), flowNode); + }); + + // wire old + update.remove.forEach(function(oldLane) { + collectionAdd(lanes, oldLane); + collectionAdd(oldLane.get(FLOW_NODE_REFS_ATTR), flowNode); + }); + }); + + // TODO(nikku): return changed elements + // return [ ... ]; +}; diff --git a/lib/features/modeling/cmd/UpdateModdlePropertiesHandler.js b/lib/features/modeling/cmd/UpdateModdlePropertiesHandler.js new file mode 100644 index 0000000..b543132 --- /dev/null +++ b/lib/features/modeling/cmd/UpdateModdlePropertiesHandler.js @@ -0,0 +1,93 @@ +import { + reduce, + keys, + forEach +} from 'min-dash'; + +import { + is, + getBusinessObject +} from '../../../util/ModelUtil'; + +export default function UpdateModdlePropertiesHandler(elementRegistry) { + this._elementRegistry = elementRegistry; +} + +UpdateModdlePropertiesHandler.$inject = [ 'elementRegistry' ]; + +UpdateModdlePropertiesHandler.prototype.execute = function(context) { + + var element = context.element, + moddleElement = context.moddleElement, + properties = context.properties; + + if (!moddleElement) { + throw new Error(' required'); + } + + // TODO(nikku): we need to ensure that ID properties + // are properly registered / unregistered via + // this._moddle.ids.assigned(id) + var changed = context.changed || this.getVisualReferences(moddleElement).concat(element); + var oldProperties = context.oldProperties || getModdleProperties(moddleElement, keys(properties)); + + setModdleProperties(moddleElement, properties); + + context.oldProperties = oldProperties; + context.changed = changed; + + return changed; +}; + +UpdateModdlePropertiesHandler.prototype.revert = function(context) { + var oldProperties = context.oldProperties, + moddleElement = context.moddleElement, + changed = context.changed; + + setModdleProperties(moddleElement, oldProperties); + + return changed; +}; + +/** + * Return visual references of given moddle element within the diagram. + * + * @param {ModdleElement} moddleElement + * + * @return {Array} + */ +UpdateModdlePropertiesHandler.prototype.getVisualReferences = function(moddleElement) { + + var elementRegistry = this._elementRegistry; + + if (is(moddleElement, 'bpmn:DataObject')) { + return getAllDataObjectReferences(moddleElement, elementRegistry); + } + + return []; +}; + + +// helpers ///////////////// + +function getModdleProperties(moddleElement, propertyNames) { + return reduce(propertyNames, function(result, key) { + result[key] = moddleElement.get(key); + return result; + }, {}); +} + +function setModdleProperties(moddleElement, properties) { + forEach(properties, function(value, key) { + moddleElement.set(key, value); + }); +} + +function getAllDataObjectReferences(dataObject, elementRegistry) { + return elementRegistry.filter(function(element) { + return ( + is(element, 'bpmn:DataObjectReference') && + getBusinessObject(element).dataObjectRef === dataObject + ); + }); +} diff --git a/lib/features/modeling/cmd/UpdatePropertiesHandler.js b/lib/features/modeling/cmd/UpdatePropertiesHandler.js new file mode 100644 index 0000000..437c4d0 --- /dev/null +++ b/lib/features/modeling/cmd/UpdatePropertiesHandler.js @@ -0,0 +1,242 @@ +import { + reduce, + keys, + forEach, + assign +} from 'min-dash'; + +import { + getBusinessObject, + getDi +} from '../../../util/ModelUtil'; + +var DEFAULT_FLOW = 'default', + ID = 'id', + DI = 'di'; + +var NULL_DIMENSIONS = { + width: 0, + height: 0 +}; + +/** + * A handler that implements a BPMN 2.0 property update. + * + * This should be used to set simple properties on elements with + * an underlying BPMN business object. + * + * Use respective diagram-js provided handlers if you would + * like to perform automated modeling. + */ +export default function UpdatePropertiesHandler( + elementRegistry, moddle, translate, + modeling, textRenderer) { + + this._elementRegistry = elementRegistry; + this._moddle = moddle; + this._translate = translate; + this._modeling = modeling; + this._textRenderer = textRenderer; +} + +UpdatePropertiesHandler.$inject = [ + 'elementRegistry', + 'moddle', + 'translate', + 'modeling', + 'textRenderer' +]; + + +// api ////////////////////// + +/** + * Updates a BPMN element with a list of new properties + * + * @param {Object} context + * @param {djs.model.Base} context.element the element to update + * @param {Object} context.properties a list of properties to set on the element's + * businessObject (the BPMN model element) + * + * @return {Array} the updated element + */ +UpdatePropertiesHandler.prototype.execute = function(context) { + + var element = context.element, + changed = [ element ], + translate = this._translate; + + if (!element) { + throw new Error(translate('element required')); + } + + var elementRegistry = this._elementRegistry, + ids = this._moddle.ids; + + var businessObject = element.businessObject, + properties = unwrapBusinessObjects(context.properties), + oldProperties = context.oldProperties || getProperties(element, properties); + + if (isIdChange(properties, businessObject)) { + ids.unclaim(businessObject[ID]); + + elementRegistry.updateId(element, properties[ID]); + + ids.claim(properties[ID], businessObject); + } + + // correctly indicate visual changes on default flow updates + if (DEFAULT_FLOW in properties) { + + if (properties[DEFAULT_FLOW]) { + changed.push(elementRegistry.get(properties[DEFAULT_FLOW].id)); + } + + if (businessObject[DEFAULT_FLOW]) { + changed.push(elementRegistry.get(businessObject[DEFAULT_FLOW].id)); + } + } + + // update properties + setProperties(element, properties); + + // store old values + context.oldProperties = oldProperties; + context.changed = changed; + + // indicate changed on objects affected by the update + return changed; +}; + + +UpdatePropertiesHandler.prototype.postExecute = function(context) { + var element = context.element, + label = element.label; + + var text = label && getBusinessObject(label).name; + + if (!text) { + return; + } + + // get layouted text bounds and resize external + // external label accordingly + var newLabelBounds = this._textRenderer.getExternalLabelBounds(label, text); + + this._modeling.resizeShape(label, newLabelBounds, NULL_DIMENSIONS); +}; + +/** + * Reverts the update on a BPMN elements properties. + * + * @param {Object} context + * + * @return {djs.model.Base} the updated element + */ +UpdatePropertiesHandler.prototype.revert = function(context) { + + var element = context.element, + properties = context.properties, + oldProperties = context.oldProperties, + businessObject = element.businessObject, + elementRegistry = this._elementRegistry, + ids = this._moddle.ids; + + // update properties + setProperties(element, oldProperties); + + if (isIdChange(properties, businessObject)) { + ids.unclaim(properties[ID]); + + elementRegistry.updateId(element, oldProperties[ID]); + + ids.claim(oldProperties[ID], businessObject); + } + + return context.changed; +}; + + +function isIdChange(properties, businessObject) { + return ID in properties && properties[ID] !== businessObject[ID]; +} + + +function getProperties(element, properties) { + var propertyNames = keys(properties), + businessObject = element.businessObject, + di = getDi(element); + + return reduce(propertyNames, function(result, key) { + + // handle DI separately + if (key !== DI) { + result[key] = businessObject.get(key); + + } else { + result[key] = getDiProperties(di, keys(properties.di)); + } + + return result; + }, {}); +} + + +function getDiProperties(di, propertyNames) { + return reduce(propertyNames, function(result, key) { + result[key] = di && di.get(key); + + return result; + }, {}); +} + + +function setProperties(element, properties) { + var businessObject = element.businessObject, + di = getDi(element); + + forEach(properties, function(value, key) { + + if (key !== DI) { + businessObject.set(key, value); + } else { + + // only update, if di exists + if (di) { + setDiProperties(di, value); + } + } + }); +} + + +function setDiProperties(di, properties) { + forEach(properties, function(value, key) { + di.set(key, value); + }); +} + + +var referencePropertyNames = [ 'default' ]; + +/** + * Make sure we unwrap the actual business object + * behind diagram element that may have been + * passed as arguments. + * + * @param {Object} properties + * + * @return {Object} unwrappedProps + */ +function unwrapBusinessObjects(properties) { + + var unwrappedProps = assign({}, properties); + + referencePropertyNames.forEach(function(name) { + if (name in properties) { + unwrappedProps[name] = getBusinessObject(unwrappedProps[name]); + } + }); + + return unwrappedProps; +} \ No newline at end of file diff --git a/lib/features/modeling/cmd/UpdateSemanticParentHandler.js b/lib/features/modeling/cmd/UpdateSemanticParentHandler.js new file mode 100644 index 0000000..fb16fad --- /dev/null +++ b/lib/features/modeling/cmd/UpdateSemanticParentHandler.js @@ -0,0 +1,36 @@ +export default function UpdateSemanticParentHandler(bpmnUpdater) { + this._bpmnUpdater = bpmnUpdater; +} + +UpdateSemanticParentHandler.$inject = [ 'bpmnUpdater' ]; + + +UpdateSemanticParentHandler.prototype.execute = function(context) { + var dataStoreBo = context.dataStoreBo, + dataStoreDi = context.dataStoreDi, + newSemanticParent = context.newSemanticParent, + newDiParent = context.newDiParent; + + context.oldSemanticParent = dataStoreBo.$parent; + context.oldDiParent = dataStoreDi.$parent; + + // update semantic parent + this._bpmnUpdater.updateSemanticParent(dataStoreBo, newSemanticParent); + + // update DI parent + this._bpmnUpdater.updateDiParent(dataStoreDi, newDiParent); +}; + +UpdateSemanticParentHandler.prototype.revert = function(context) { + var dataStoreBo = context.dataStoreBo, + dataStoreDi = context.dataStoreDi, + oldSemanticParent = context.oldSemanticParent, + oldDiParent = context.oldDiParent; + + // update semantic parent + this._bpmnUpdater.updateSemanticParent(dataStoreBo, oldSemanticParent); + + // update DI parent + this._bpmnUpdater.updateDiParent(dataStoreDi, oldDiParent); +}; + diff --git a/lib/features/modeling/index.js b/lib/features/modeling/index.js new file mode 100644 index 0000000..0d5b89b --- /dev/null +++ b/lib/features/modeling/index.js @@ -0,0 +1,48 @@ +import BehaviorModule from './behavior'; +import RulesModule from '../rules'; +import DiOrderingModule from '../di-ordering'; +import OrderingModule from '../ordering'; +import ReplaceModule from '../replace'; + +import CommandModule from 'diagram-js/lib/command'; +import TooltipsModule from 'diagram-js/lib/features/tooltips'; +import LabelSupportModule from 'diagram-js/lib/features/label-support'; +import AttachSupportModule from 'diagram-js/lib/features/attach-support'; +import SelectionModule from 'diagram-js/lib/features/selection'; +import ChangeSupportModule from 'diagram-js/lib/features/change-support'; +import SpaceToolModule from 'diagram-js/lib/features/space-tool'; + +import BpmnFactory from './BpmnFactory'; +import BpmnUpdater from './BpmnUpdater'; +import ElementFactory from './ElementFactory'; +import Modeling from './Modeling'; +import BpmnLayouter from './BpmnLayouter'; +import CroppingConnectionDocking from 'diagram-js/lib/layout/CroppingConnectionDocking'; + + +export default { + __init__: [ + 'modeling', + 'bpmnUpdater' + ], + __depends__: [ + BehaviorModule, + RulesModule, + DiOrderingModule, + OrderingModule, + ReplaceModule, + CommandModule, + TooltipsModule, + LabelSupportModule, + AttachSupportModule, + SelectionModule, + ChangeSupportModule, + SpaceToolModule + ], + bpmnFactory: [ 'type', BpmnFactory ], + bpmnUpdater: [ 'type', BpmnUpdater ], + elementFactory: [ 'type', ElementFactory ], + modeling: [ 'type', Modeling ], + layouter: [ 'type', BpmnLayouter ], + connectionDocking: [ 'type', CroppingConnectionDocking ] +}; \ No newline at end of file diff --git a/lib/features/modeling/util/LaneUtil.js b/lib/features/modeling/util/LaneUtil.js new file mode 100644 index 0000000..3223d3b --- /dev/null +++ b/lib/features/modeling/util/LaneUtil.js @@ -0,0 +1,154 @@ +import { is } from '../../../util/ModelUtil'; + +import { + getParent +} from './ModelingUtil'; + +import { + asTRBL +} from 'diagram-js/lib/layout/LayoutUtil'; + +import { + substractTRBL, + resizeTRBL +} from 'diagram-js/lib/features/resize/ResizeUtil'; + +var abs = Math.abs; + + +function getTRBLResize(oldBounds, newBounds) { + return substractTRBL(asTRBL(newBounds), asTRBL(oldBounds)); +} + + +var LANE_PARENTS = [ + 'bpmn:Participant', + 'bpmn:Process', + 'bpmn:SubProcess' +]; + +export var LANE_INDENTATION = 30; + + +/** + * Collect all lane shapes in the given paren + * + * @param {djs.model.Shape} shape + * @param {Array} [collectedShapes] + * + * @return {Array} + */ +export function collectLanes(shape, collectedShapes) { + + collectedShapes = collectedShapes || []; + + shape.children.filter(function(s) { + if (is(s, 'bpmn:Lane')) { + collectLanes(s, collectedShapes); + + collectedShapes.push(s); + } + }); + + return collectedShapes; +} + + +/** + * Return the lane children of the given element. + * + * @param {djs.model.Shape} shape + * + * @return {Array} + */ +export function getChildLanes(shape) { + return shape.children.filter(function(c) { + return is(c, 'bpmn:Lane'); + }); +} + + +/** + * Return the root element containing the given lane shape + * + * @param {djs.model.Shape} shape + * + * @return {djs.model.Shape} + */ +export function getLanesRoot(shape) { + return getParent(shape, LANE_PARENTS) || shape; +} + + +/** + * Compute the required resize operations for lanes + * adjacent to the given shape, assuming it will be + * resized to the given new bounds. + * + * @param {djs.model.Shape} shape + * @param {Bounds} newBounds + * + * @return {Array} + */ +export function computeLanesResize(shape, newBounds) { + + var rootElement = getLanesRoot(shape); + + var initialShapes = is(rootElement, 'bpmn:Process') ? [] : [ rootElement ]; + + var allLanes = collectLanes(rootElement, initialShapes), + shapeTrbl = asTRBL(shape), + shapeNewTrbl = asTRBL(newBounds), + trblResize = getTRBLResize(shape, newBounds), + resizeNeeded = []; + + allLanes.forEach(function(other) { + + if (other === shape) { + return; + } + + var topResize = 0, + rightResize = trblResize.right, + bottomResize = 0, + leftResize = trblResize.left; + + var otherTrbl = asTRBL(other); + + if (trblResize.top) { + if (abs(otherTrbl.bottom - shapeTrbl.top) < 10) { + bottomResize = shapeNewTrbl.top - otherTrbl.bottom; + } + + if (abs(otherTrbl.top - shapeTrbl.top) < 5) { + topResize = shapeNewTrbl.top - otherTrbl.top; + } + } + + if (trblResize.bottom) { + if (abs(otherTrbl.top - shapeTrbl.bottom) < 10) { + topResize = shapeNewTrbl.bottom - otherTrbl.top; + } + + if (abs(otherTrbl.bottom - shapeTrbl.bottom) < 5) { + bottomResize = shapeNewTrbl.bottom - otherTrbl.bottom; + } + } + + if (topResize || rightResize || bottomResize || leftResize) { + + resizeNeeded.push({ + shape: other, + newBounds: resizeTRBL(other, { + top: topResize, + right: rightResize, + bottom: bottomResize, + left: leftResize + }) + }); + } + + }); + + return resizeNeeded; +} \ No newline at end of file diff --git a/lib/features/modeling/util/ModelingUtil.js b/lib/features/modeling/util/ModelingUtil.js new file mode 100644 index 0000000..98875d0 --- /dev/null +++ b/lib/features/modeling/util/ModelingUtil.js @@ -0,0 +1,26 @@ +export { is, isAny } from '../../../util/ModelUtil'; + +import { isAny } from '../../../util/ModelUtil'; + +/** + * Return the parent of the element with any of the given types. + * + * @param {djs.model.Base} element + * @param {string|Array} anyType + * + * @return {djs.model.Base} + */ +export function getParent(element, anyType) { + + if (typeof anyType === 'string') { + anyType = [ anyType ]; + } + + while ((element = element.parent)) { + if (isAny(element, anyType)) { + return element; + } + } + + return null; +} \ No newline at end of file diff --git a/lib/features/ordering/BpmnOrderingProvider.js b/lib/features/ordering/BpmnOrderingProvider.js new file mode 100644 index 0000000..b290668 --- /dev/null +++ b/lib/features/ordering/BpmnOrderingProvider.js @@ -0,0 +1,181 @@ +import inherits from 'inherits-browser'; + +import OrderingProvider from 'diagram-js/lib/features/ordering/OrderingProvider'; + +import { + isAny +} from '../modeling/util/ModelingUtil'; + +import { + findIndex, + find +} from 'min-dash'; + + +/** + * a simple ordering provider that makes sure: + * + * (0) labels and groups are rendered always on top + * (1) elements are ordered by a {level} property + */ +export default function BpmnOrderingProvider(eventBus, canvas, translate) { + + OrderingProvider.call(this, eventBus); + + var orders = [ + { type: 'bpmn:SubProcess', order: { level: 6 } }, + + // handle SequenceFlow(s) like message flows and render them always on top + { + type: 'bpmn:SequenceFlow', + order: { + level: 9, + containers: [ + 'bpmn:Participant', + 'bpmn:FlowElementsContainer' + ] + } + }, + + // handle DataAssociation(s) like message flows and render them always on top + { + type: 'bpmn:DataAssociation', + order: { + level: 9, + containers: [ + 'bpmn:Collaboration', + 'bpmn:FlowElementsContainer' + ] + } + }, + { + type: 'bpmn:MessageFlow', order: { + level: 9, + containers: [ 'bpmn:Collaboration' ] + } + }, + { + type: 'bpmn:Association', + order: { + level: 6, + containers: [ + 'bpmn:Participant', + 'bpmn:FlowElementsContainer', + 'bpmn:Collaboration' + ] + } + }, + { type: 'bpmn:BoundaryEvent', order: { level: 8 } }, + { + type: 'bpmn:Group', + order: { + level: 10, + containers: [ + 'bpmn:Collaboration', + 'bpmn:FlowElementsContainer' + ] + } + }, + { type: 'bpmn:FlowElement', order: { level: 5 } }, + { type: 'bpmn:Participant', order: { level: -2 } }, + { type: 'bpmn:Lane', order: { level: -1 } } + ]; + + function computeOrder(element) { + if (element.labelTarget) { + return { level: 10 }; + } + + var entry = find(orders, function(o) { + return isAny(element, [ o.type ]); + }); + + return entry && entry.order || { level: 1 }; + } + + function getOrder(element) { + + var order = element.order; + + if (!order) { + element.order = order = computeOrder(element); + } + + if (!order) { + throw new Error('no order for <' + element.id + '>'); + } + + return order; + } + + function findActualParent(element, newParent, containers) { + + var actualParent = newParent; + + while (actualParent) { + + if (isAny(actualParent, containers)) { + break; + } + + actualParent = actualParent.parent; + } + + if (!actualParent) { + throw new Error('no parent for <' + element.id + '> in <' + (newParent && newParent.id) + '>'); + } + + return actualParent; + } + + this.getOrdering = function(element, newParent) { + + // render labels always on top + if (element.labelTarget) { + return { + parent: canvas.findRoot(newParent) || canvas.getRootElement(), + index: -1 + }; + } + + var elementOrder = getOrder(element); + + if (elementOrder.containers) { + newParent = findActualParent(element, newParent, elementOrder.containers); + } + + var currentIndex = newParent.children.indexOf(element); + + var insertIndex = findIndex(newParent.children, function(child) { + + // do not compare with labels, they are created + // in the wrong order (right after elements) during import and + // mess up the positioning. + if (!element.labelTarget && child.labelTarget) { + return false; + } + + return elementOrder.level < getOrder(child).level; + }); + + + // if the element is already in the child list at + // a smaller index, we need to adjust the insert index. + // this takes into account that the element is being removed + // before being re-inserted + if (insertIndex !== -1) { + if (currentIndex !== -1 && currentIndex < insertIndex) { + insertIndex -= 1; + } + } + + return { + index: insertIndex, + parent: newParent + }; + }; +} + +BpmnOrderingProvider.$inject = [ 'eventBus', 'canvas', 'translate' ]; + +inherits(BpmnOrderingProvider, OrderingProvider); \ No newline at end of file diff --git a/lib/features/ordering/index.js b/lib/features/ordering/index.js new file mode 100644 index 0000000..079c8f7 --- /dev/null +++ b/lib/features/ordering/index.js @@ -0,0 +1,11 @@ +import translate from 'diagram-js/lib/i18n/translate'; + +import BpmnOrderingProvider from './BpmnOrderingProvider'; + +export default { + __depends__: [ + translate + ], + __init__: [ 'bpmnOrderingProvider' ], + bpmnOrderingProvider: [ 'type', BpmnOrderingProvider ] +}; \ No newline at end of file diff --git a/lib/features/palette/PaletteProvider.js b/lib/features/palette/PaletteProvider.js new file mode 100644 index 0000000..198a452 --- /dev/null +++ b/lib/features/palette/PaletteProvider.js @@ -0,0 +1,200 @@ +import { + assign +} from 'min-dash'; +import { getDi } from '../../util/ModelUtil'; + + +/** + * A palette provider for BPMN 2.0 elements. + */ +export default function PaletteProvider( + palette, create, elementFactory, + spaceTool, lassoTool, handTool, + globalConnect, translate) { + + this._palette = palette; + this._create = create; + this._elementFactory = elementFactory; + this._spaceTool = spaceTool; + this._lassoTool = lassoTool; + this._handTool = handTool; + this._globalConnect = globalConnect; + this._translate = translate; + + palette.registerProvider(this); +} + +PaletteProvider.$inject = [ + 'palette', + 'create', + 'elementFactory', + 'spaceTool', + 'lassoTool', + 'handTool', + 'globalConnect', + 'translate' +]; + + +PaletteProvider.prototype.getPaletteEntries = function(element) { + + var actions = {}, + create = this._create, + elementFactory = this._elementFactory, + spaceTool = this._spaceTool, + lassoTool = this._lassoTool, + handTool = this._handTool, + globalConnect = this._globalConnect, + translate = this._translate; + + function createAction(type, group, className, title, options) { + + function createListener(event) { + var shape = elementFactory.createShape(assign({ type: type }, options)); + + if (options) { + var di = getDi(shape); + di.isExpanded = options.isExpanded; + } + + create.start(event, shape); + } + + var shortType = type.replace(/^bpmn:/, ''); + + return { + group: group, + className: className, + title: title || translate('Create {type}', { type: shortType }), + action: { + dragstart: createListener, + click: createListener + } + }; + } + + function createSubprocess(event) { + var subProcess = elementFactory.createShape({ + type: 'bpmn:SubProcess', + x: 0, + y: 0, + isExpanded: true + }); + + var startEvent = elementFactory.createShape({ + type: 'bpmn:StartEvent', + x: 40, + y: 82, + parent: subProcess + }); + + create.start(event, [ subProcess, startEvent ], { + hints: { + autoSelect: [ subProcess ] + } + }); + } + + function createParticipant(event) { + create.start(event, elementFactory.createParticipantShape()); + } + + assign(actions, { + 'hand-tool': { + group: 'tools', + className: 'bpmn-icon-hand-tool', + title: translate('Activate the hand tool'), + action: { + click: function(event) { + handTool.activateHand(event); + } + } + }, + 'lasso-tool': { + group: 'tools', + className: 'bpmn-icon-lasso-tool', + title: translate('Activate the lasso tool'), + action: { + click: function(event) { + lassoTool.activateSelection(event); + } + } + }, + 'space-tool': { + group: 'tools', + className: 'bpmn-icon-space-tool', + title: translate('Activate the create/remove space tool'), + action: { + click: function(event) { + spaceTool.activateSelection(event); + } + } + }, + 'global-connect-tool': { + group: 'tools', + className: 'bpmn-icon-connection-multi', + title: translate('Activate the global connect tool'), + action: { + click: function(event) { + globalConnect.start(event); + } + } + }, + 'tool-separator': { + group: 'tools', + separator: true + }, + 'create.start-event': createAction( + 'bpmn:StartEvent', 'event', 'bpmn-icon-start-event-none', + translate('Create StartEvent') + ), + 'create.intermediate-event': createAction( + 'bpmn:IntermediateThrowEvent', 'event', 'bpmn-icon-intermediate-event-none', + translate('Create Intermediate/Boundary Event') + ), + 'create.end-event': createAction( + 'bpmn:EndEvent', 'event', 'bpmn-icon-end-event-none', + translate('Create EndEvent') + ), + 'create.exclusive-gateway': createAction( + 'bpmn:ExclusiveGateway', 'gateway', 'bpmn-icon-gateway-none', + translate('Create Gateway') + ), + 'create.task': createAction( + 'bpmn:Task', 'activity', 'bpmn-icon-task', + translate('Create Task') + ), + 'create.data-object': createAction( + 'bpmn:DataObjectReference', 'data-object', 'bpmn-icon-data-object', + translate('Create DataObjectReference') + ), + 'create.data-store': createAction( + 'bpmn:DataStoreReference', 'data-store', 'bpmn-icon-data-store', + translate('Create DataStoreReference') + ), + 'create.subprocess-expanded': { + group: 'activity', + className: 'bpmn-icon-subprocess-expanded', + title: translate('Create expanded SubProcess'), + action: { + dragstart: createSubprocess, + click: createSubprocess + } + }, + 'create.participant-expanded': { + group: 'collaboration', + className: 'bpmn-icon-participant', + title: translate('Create Pool/Participant'), + action: { + dragstart: createParticipant, + click: createParticipant + } + }, + 'create.group': createAction( + 'bpmn:Group', 'artifact', 'bpmn-icon-group', + translate('Create Group') + ), + }); + + return actions; +}; diff --git a/lib/features/palette/index.js b/lib/features/palette/index.js new file mode 100644 index 0000000..ed93fba --- /dev/null +++ b/lib/features/palette/index.js @@ -0,0 +1,23 @@ +import PaletteModule from 'diagram-js/lib/features/palette'; +import CreateModule from 'diagram-js/lib/features/create'; +import SpaceToolModule from 'diagram-js/lib/features/space-tool'; +import LassoToolModule from 'diagram-js/lib/features/lasso-tool'; +import HandToolModule from 'diagram-js/lib/features/hand-tool'; +import GlobalConnectModule from 'diagram-js/lib/features/global-connect'; +import translate from 'diagram-js/lib/i18n/translate'; + +import PaletteProvider from './PaletteProvider'; + +export default { + __depends__: [ + PaletteModule, + CreateModule, + SpaceToolModule, + LassoToolModule, + HandToolModule, + GlobalConnectModule, + translate + ], + __init__: [ 'paletteProvider' ], + paletteProvider: [ 'type', PaletteProvider ] +}; diff --git a/lib/features/popup-menu/ReplaceMenuProvider.js b/lib/features/popup-menu/ReplaceMenuProvider.js new file mode 100644 index 0000000..6ba9a85 --- /dev/null +++ b/lib/features/popup-menu/ReplaceMenuProvider.js @@ -0,0 +1,607 @@ +import { + getBusinessObject, + is +} from '../../util/ModelUtil'; + +import { + isEventSubProcess, + isExpanded +} from '../../util/DiUtil'; + +import { + isDifferentType +} from './util/TypeUtil'; + +import { + forEach, + filter, + isUndefined +} from 'min-dash'; + +import * as replaceOptions from '../replace/ReplaceOptions'; + + +/** + * This module is an element agnostic replace menu provider for the popup menu. + */ +export default function ReplaceMenuProvider( + bpmnFactory, popupMenu, modeling, moddle, + bpmnReplace, rules, translate) { + + this._bpmnFactory = bpmnFactory; + this._popupMenu = popupMenu; + this._modeling = modeling; + this._moddle = moddle; + this._bpmnReplace = bpmnReplace; + this._rules = rules; + this._translate = translate; + + this.register(); +} + +ReplaceMenuProvider.$inject = [ + 'bpmnFactory', + 'popupMenu', + 'modeling', + 'moddle', + 'bpmnReplace', + 'rules', + 'translate' +]; + + +/** + * Register replace menu provider in the popup menu + */ +ReplaceMenuProvider.prototype.register = function() { + this._popupMenu.registerProvider('bpmn-replace', this); +}; + + +/** + * Get all entries from replaceOptions for the given element and apply filters + * on them. Get for example only elements, which are different from the current one. + * + * @param {djs.model.Base} element + * + * @return {Array} a list of menu entry items + */ +ReplaceMenuProvider.prototype.getEntries = function(element) { + + var businessObject = element.businessObject; + + var rules = this._rules; + + var entries; + + if (!rules.allowed('shape.replace', { element: element })) { + return []; + } + + var differentType = isDifferentType(element); + + if (is(businessObject, 'bpmn:DataObjectReference')) { + return this._createEntries(element, replaceOptions.DATA_OBJECT_REFERENCE); + } + + if (is(businessObject, 'bpmn:DataStoreReference') && !is(element.parent, 'bpmn:Collaboration')) { + return this._createEntries(element, replaceOptions.DATA_STORE_REFERENCE); + } + + // start events outside sub processes + if (is(businessObject, 'bpmn:StartEvent') && !is(businessObject.$parent, 'bpmn:SubProcess')) { + + entries = filter(replaceOptions.START_EVENT, differentType); + + return this._createEntries(element, entries); + } + + // expanded/collapsed pools + if (is(businessObject, 'bpmn:Participant')) { + + entries = filter(replaceOptions.PARTICIPANT, function(entry) { + return isExpanded(element) !== entry.target.isExpanded; + }); + + return this._createEntries(element, entries); + } + + // start events inside event sub processes + if (is(businessObject, 'bpmn:StartEvent') && isEventSubProcess(businessObject.$parent)) { + entries = filter(replaceOptions.EVENT_SUB_PROCESS_START_EVENT, function(entry) { + + var target = entry.target; + + var isInterrupting = target.isInterrupting !== false; + + var isInterruptingEqual = getBusinessObject(element).isInterrupting === isInterrupting; + + // filters elements which types and event definition are equal but have have different interrupting types + return differentType(entry) || !differentType(entry) && !isInterruptingEqual; + + }); + + return this._createEntries(element, entries); + } + + // start events inside sub processes + if (is(businessObject, 'bpmn:StartEvent') && !isEventSubProcess(businessObject.$parent) + && is(businessObject.$parent, 'bpmn:SubProcess')) { + entries = filter(replaceOptions.START_EVENT_SUB_PROCESS, differentType); + + return this._createEntries(element, entries); + } + + // end events + if (is(businessObject, 'bpmn:EndEvent')) { + + entries = filter(replaceOptions.END_EVENT, function(entry) { + var target = entry.target; + + // hide cancel end events outside transactions + if (target.eventDefinitionType == 'bpmn:CancelEventDefinition' && !is(businessObject.$parent, 'bpmn:Transaction')) { + return false; + } + + return differentType(entry); + }); + + return this._createEntries(element, entries); + } + + // boundary events + if (is(businessObject, 'bpmn:BoundaryEvent')) { + + entries = filter(replaceOptions.BOUNDARY_EVENT, function(entry) { + + var target = entry.target; + + if (target.eventDefinitionType == 'bpmn:CancelEventDefinition' && + !is(businessObject.attachedToRef, 'bpmn:Transaction')) { + return false; + } + var cancelActivity = target.cancelActivity !== false; + + var isCancelActivityEqual = businessObject.cancelActivity == cancelActivity; + + return differentType(entry) || !differentType(entry) && !isCancelActivityEqual; + }); + + return this._createEntries(element, entries); + } + + // intermediate events + if (is(businessObject, 'bpmn:IntermediateCatchEvent') || + is(businessObject, 'bpmn:IntermediateThrowEvent')) { + + entries = filter(replaceOptions.INTERMEDIATE_EVENT, differentType); + + return this._createEntries(element, entries); + } + + // gateways + if (is(businessObject, 'bpmn:Gateway')) { + + entries = filter(replaceOptions.GATEWAY, differentType); + + return this._createEntries(element, entries); + } + + // transactions + if (is(businessObject, 'bpmn:Transaction')) { + + entries = filter(replaceOptions.TRANSACTION, differentType); + + return this._createEntries(element, entries); + } + + // expanded event sub processes + if (isEventSubProcess(businessObject) && isExpanded(element)) { + + entries = filter(replaceOptions.EVENT_SUB_PROCESS, differentType); + + return this._createEntries(element, entries); + } + + // expanded sub processes + if (is(businessObject, 'bpmn:SubProcess') && isExpanded(element)) { + + entries = filter(replaceOptions.SUBPROCESS_EXPANDED, differentType); + + return this._createEntries(element, entries); + } + + // collapsed ad hoc sub processes + if (is(businessObject, 'bpmn:AdHocSubProcess') && !isExpanded(element)) { + + entries = filter(replaceOptions.TASK, function(entry) { + + var target = entry.target; + + var isTargetSubProcess = target.type === 'bpmn:SubProcess'; + + var isTargetExpanded = target.isExpanded === true; + + return isDifferentType(element, target) && (!isTargetSubProcess || isTargetExpanded); + }); + + return this._createEntries(element, entries); + } + + // sequence flows + if (is(businessObject, 'bpmn:SequenceFlow')) { + return this._createSequenceFlowEntries(element, replaceOptions.SEQUENCE_FLOW); + } + + // flow nodes + if (is(businessObject, 'bpmn:FlowNode')) { + entries = filter(replaceOptions.TASK, differentType); + + // collapsed SubProcess can not be replaced with itself + if (is(businessObject, 'bpmn:SubProcess') && !isExpanded(element)) { + entries = filter(entries, function(entry) { + return entry.label !== 'Sub Process (collapsed)'; + }); + } + + return this._createEntries(element, entries); + } + + return []; +}; + + +/** + * Get a list of header items for the given element. This includes buttons + * for multi instance markers and for the ad hoc marker. + * + * @param {djs.model.Base} element + * + * @return {Array} a list of menu entry items + */ +ReplaceMenuProvider.prototype.getHeaderEntries = function(element) { + + var headerEntries = []; + + if (is(element, 'bpmn:Activity') && !isEventSubProcess(element)) { + headerEntries = headerEntries.concat(this._getLoopEntries(element)); + } + + if (is(element, 'bpmn:DataObjectReference')) { + headerEntries = headerEntries.concat(this._getDataObjectIsCollection(element)); + } + + if (is(element, 'bpmn:Participant')) { + headerEntries = headerEntries.concat(this._getParticipantMultiplicity(element)); + } + + if (is(element, 'bpmn:SubProcess') && + !is(element, 'bpmn:Transaction') && + !isEventSubProcess(element)) { + headerEntries.push(this._getAdHocEntry(element)); + } + + return headerEntries; +}; + + +/** + * Creates an array of menu entry objects for a given element and filters the replaceOptions + * according to a filter function. + * + * @param {djs.model.Base} element + * @param {Object} replaceOptions + * + * @return {Array} a list of menu items + */ +ReplaceMenuProvider.prototype._createEntries = function(element, replaceOptions) { + var menuEntries = []; + + var self = this; + + forEach(replaceOptions, function(definition) { + var entry = self._createMenuEntry(definition, element); + + menuEntries.push(entry); + }); + + return menuEntries; +}; + +/** + * Creates an array of menu entry objects for a given sequence flow. + * + * @param {djs.model.Base} element + * @param {Object} replaceOptions + + * @return {Array} a list of menu items + */ +ReplaceMenuProvider.prototype._createSequenceFlowEntries = function(element, replaceOptions) { + + var businessObject = getBusinessObject(element); + + var menuEntries = []; + + var modeling = this._modeling, + moddle = this._moddle; + + var self = this; + + forEach(replaceOptions, function(entry) { + + switch (entry.actionName) { + case 'replace-with-default-flow': + if (businessObject.sourceRef.default !== businessObject && + (is(businessObject.sourceRef, 'bpmn:ExclusiveGateway') || + is(businessObject.sourceRef, 'bpmn:InclusiveGateway') || + is(businessObject.sourceRef, 'bpmn:ComplexGateway') || + is(businessObject.sourceRef, 'bpmn:Activity'))) { + + menuEntries.push(self._createMenuEntry(entry, element, function() { + modeling.updateProperties(element.source, { default: businessObject }); + })); + } + break; + case 'replace-with-conditional-flow': + if (!businessObject.conditionExpression && is(businessObject.sourceRef, 'bpmn:Activity')) { + + menuEntries.push(self._createMenuEntry(entry, element, function() { + var conditionExpression = moddle.create('bpmn:FormalExpression', { body: '' }); + + modeling.updateProperties(element, { conditionExpression: conditionExpression }); + })); + } + break; + default: + + // default flows + if (is(businessObject.sourceRef, 'bpmn:Activity') && businessObject.conditionExpression) { + return menuEntries.push(self._createMenuEntry(entry, element, function() { + modeling.updateProperties(element, { conditionExpression: undefined }); + })); + } + + // conditional flows + if ((is(businessObject.sourceRef, 'bpmn:ExclusiveGateway') || + is(businessObject.sourceRef, 'bpmn:InclusiveGateway') || + is(businessObject.sourceRef, 'bpmn:ComplexGateway') || + is(businessObject.sourceRef, 'bpmn:Activity')) && + businessObject.sourceRef.default === businessObject) { + + return menuEntries.push(self._createMenuEntry(entry, element, function() { + modeling.updateProperties(element.source, { default: undefined }); + })); + } + } + }); + + return menuEntries; +}; + + +/** + * Creates and returns a single menu entry item. + * + * @param {Object} definition a single replace options definition object + * @param {djs.model.Base} element + * @param {Function} [action] an action callback function which gets called when + * the menu entry is being triggered. + * + * @return {Object} menu entry item + */ +ReplaceMenuProvider.prototype._createMenuEntry = function(definition, element, action) { + var translate = this._translate; + var replaceElement = this._bpmnReplace.replaceElement; + + var replaceAction = function() { + return replaceElement(element, definition.target); + }; + + var label = definition.label; + if (label && typeof label === 'function') { + label = label(element); + } + + action = action || replaceAction; + + var menuEntry = { + label: translate(label), + className: definition.className, + id: definition.actionName, + action: action + }; + + return menuEntry; +}; + +/** + * Get a list of menu items containing buttons for multi instance markers + * + * @param {djs.model.Base} element + * + * @return {Array} a list of menu items + */ +ReplaceMenuProvider.prototype._getLoopEntries = function(element) { + + var self = this; + var translate = this._translate; + + function toggleLoopEntry(event, entry) { + var newLoopCharacteristics = getBusinessObject(element).loopCharacteristics; + + if (entry.active) { + newLoopCharacteristics = undefined; + } else { + if (isUndefined(entry.options.isSequential) || !newLoopCharacteristics + || !is(newLoopCharacteristics, entry.options.loopCharacteristics)) { + newLoopCharacteristics = self._moddle.create(entry.options.loopCharacteristics); + } + + newLoopCharacteristics.isSequential = entry.options.isSequential; + } + self._modeling.updateProperties(element, { loopCharacteristics: newLoopCharacteristics }); + } + + var businessObject = getBusinessObject(element), + loopCharacteristics = businessObject.loopCharacteristics; + + var isSequential, + isLoop, + isParallel; + + if (loopCharacteristics) { + isSequential = loopCharacteristics.isSequential; + isLoop = loopCharacteristics.isSequential === undefined; + isParallel = loopCharacteristics.isSequential !== undefined && !loopCharacteristics.isSequential; + } + + + var loopEntries = [ + { + id: 'toggle-parallel-mi', + className: 'bpmn-icon-parallel-mi-marker', + title: translate('Parallel Multi Instance'), + active: isParallel, + action: toggleLoopEntry, + options: { + loopCharacteristics: 'bpmn:MultiInstanceLoopCharacteristics', + isSequential: false + } + }, + { + id: 'toggle-sequential-mi', + className: 'bpmn-icon-sequential-mi-marker', + title: translate('Sequential Multi Instance'), + active: isSequential, + action: toggleLoopEntry, + options: { + loopCharacteristics: 'bpmn:MultiInstanceLoopCharacteristics', + isSequential: true + } + }, + { + id: 'toggle-loop', + className: 'bpmn-icon-loop-marker', + title: translate('Loop'), + active: isLoop, + action: toggleLoopEntry, + options: { + loopCharacteristics: 'bpmn:StandardLoopCharacteristics' + } + } + ]; + return loopEntries; +}; + +/** + * Get a list of menu items containing a button for the collection marker + * + * @param {djs.model.Base} element + * + * @return {Array} a list of menu items + */ +ReplaceMenuProvider.prototype._getDataObjectIsCollection = function(element) { + + var self = this; + var translate = this._translate; + + function toggleIsCollection(event, entry) { + self._modeling.updateModdleProperties( + element, + dataObject, + { isCollection: !entry.active }); + } + + var dataObject = element.businessObject.dataObjectRef, + isCollection = dataObject.isCollection; + + var dataObjectEntries = [ + { + id: 'toggle-is-collection', + className: 'bpmn-icon-parallel-mi-marker', + title: translate('Collection'), + active: isCollection, + action: toggleIsCollection, + } + ]; + return dataObjectEntries; +}; + +/** + * Get a list of menu items containing a button for the participant multiplicity marker + * + * @param {djs.model.Base} element + * + * @return {Array} a list of menu items + */ +ReplaceMenuProvider.prototype._getParticipantMultiplicity = function(element) { + + var self = this; + var bpmnFactory = this._bpmnFactory; + var translate = this._translate; + + function toggleParticipantMultiplicity(event, entry) { + var isActive = entry.active; + var participantMultiplicity; + + if (!isActive) { + participantMultiplicity = bpmnFactory.create('bpmn:ParticipantMultiplicity'); + } + + self._modeling.updateProperties( + element, + { participantMultiplicity: participantMultiplicity }); + } + + var participantMultiplicity = element.businessObject.participantMultiplicity; + + var participantEntries = [ + { + id: 'toggle-participant-multiplicity', + className: 'bpmn-icon-parallel-mi-marker', + title: translate('Participant Multiplicity'), + active: !!participantMultiplicity, + action: toggleParticipantMultiplicity, + } + ]; + return participantEntries; +}; + + +/** + * Get the menu items containing a button for the ad hoc marker + * + * @param {djs.model.Base} element + * + * @return {Object} a menu item + */ +ReplaceMenuProvider.prototype._getAdHocEntry = function(element) { + var translate = this._translate; + var businessObject = getBusinessObject(element); + + var isAdHoc = is(businessObject, 'bpmn:AdHocSubProcess'); + + var replaceElement = this._bpmnReplace.replaceElement; + + var adHocEntry = { + id: 'toggle-adhoc', + className: 'bpmn-icon-ad-hoc-marker', + title: translate('Ad-hoc'), + active: isAdHoc, + action: function(event, entry) { + if (isAdHoc) { + return replaceElement(element, { type: 'bpmn:SubProcess' }, { + autoResize: false, + layoutConnection: false + }); + } else { + return replaceElement(element, { type: 'bpmn:AdHocSubProcess' }, { + autoResize: false, + layoutConnection: false + }); + } + } + }; + + return adHocEntry; +}; diff --git a/lib/features/popup-menu/index.js b/lib/features/popup-menu/index.js new file mode 100644 index 0000000..decb9b5 --- /dev/null +++ b/lib/features/popup-menu/index.js @@ -0,0 +1,14 @@ +import PopupMenuModule from 'diagram-js/lib/features/popup-menu'; +import ReplaceModule from '../replace'; + +import ReplaceMenuProvider from './ReplaceMenuProvider'; + + +export default { + __depends__: [ + PopupMenuModule, + ReplaceModule + ], + __init__: [ 'replaceMenuProvider' ], + replaceMenuProvider: [ 'type', ReplaceMenuProvider ] +}; \ No newline at end of file diff --git a/lib/features/popup-menu/util/TypeUtil.js b/lib/features/popup-menu/util/TypeUtil.js new file mode 100644 index 0000000..99ae6a9 --- /dev/null +++ b/lib/features/popup-menu/util/TypeUtil.js @@ -0,0 +1,44 @@ +import { + getBusinessObject +} from '../../../util/ModelUtil'; + +import { + isExpanded +} from '../../../util/DiUtil'; + + +/** + * Returns true, if an element is from a different type + * than a target definition. Takes into account the type, + * event definition type and triggeredByEvent property. + * + * @param {djs.model.Base} element + * + * @return {boolean} + */ +export function isDifferentType(element) { + + return function(entry) { + var target = entry.target; + + var businessObject = getBusinessObject(element), + eventDefinition = businessObject.eventDefinitions && businessObject.eventDefinitions[0]; + + var isTypeEqual = businessObject.$type === target.type; + + var isEventDefinitionEqual = ( + (eventDefinition && eventDefinition.$type) === target.eventDefinitionType + ); + + var isTriggeredByEventEqual = ( + businessObject.triggeredByEvent === target.triggeredByEvent + ); + + var isExpandedEqual = ( + target.isExpanded === undefined || + target.isExpanded === isExpanded(element) + ); + + return !isTypeEqual || !isEventDefinitionEqual || !isTriggeredByEventEqual || !isExpandedEqual; + }; +} \ No newline at end of file diff --git a/lib/features/replace-preview/BpmnReplacePreview.js b/lib/features/replace-preview/BpmnReplacePreview.js new file mode 100644 index 0000000..92f6c45 --- /dev/null +++ b/lib/features/replace-preview/BpmnReplacePreview.js @@ -0,0 +1,126 @@ +import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; + +import inherits from 'inherits-browser'; + +import cssEscape from 'css.escape'; + +import { + assign, + forEach +} from 'min-dash'; + +import { + query as domQuery +} from 'min-dom'; + +import { + attr as svgAttr +} from 'tiny-svg'; + +var LOW_PRIORITY = 250; + + +export default function BpmnReplacePreview( + eventBus, elementRegistry, elementFactory, + canvas, previewSupport) { + + CommandInterceptor.call(this, eventBus); + + /** + * Replace the visuals of all elements in the context which can be replaced + * + * @param {Object} context + */ + function replaceVisual(context) { + + var replacements = context.canExecute.replacements; + + forEach(replacements, function(replacement) { + + var id = replacement.oldElementId; + + var newElement = { + type: replacement.newElementType + }; + + // if the visual of the element is already replaced + if (context.visualReplacements[id]) { + return; + } + + var element = elementRegistry.get(id); + + assign(newElement, { x: element.x, y: element.y }); + + // create a temporary shape + var tempShape = elementFactory.createShape(newElement); + + canvas.addShape(tempShape, element.parent); + + // select the original SVG element related to the element and hide it + var gfx = domQuery('[data-element-id="' + cssEscape(element.id) + '"]', context.dragGroup); + + if (gfx) { + svgAttr(gfx, { display: 'none' }); + } + + // clone the gfx of the temporary shape and add it to the drag group + var dragger = previewSupport.addDragger(tempShape, context.dragGroup); + + context.visualReplacements[id] = dragger; + + canvas.removeShape(tempShape); + }); + } + + /** + * Restore the original visuals of the previously replaced elements + * + * @param {Object} context + */ + function restoreVisual(context) { + + var visualReplacements = context.visualReplacements; + + forEach(visualReplacements, function(dragger, id) { + + var originalGfx = domQuery('[data-element-id="' + cssEscape(id) + '"]', context.dragGroup); + + if (originalGfx) { + svgAttr(originalGfx, { display: 'inline' }); + } + + dragger.remove(); + + if (visualReplacements[id]) { + delete visualReplacements[id]; + } + }); + } + + eventBus.on('shape.move.move', LOW_PRIORITY, function(event) { + + var context = event.context, + canExecute = context.canExecute; + + if (!context.visualReplacements) { + context.visualReplacements = {}; + } + + if (canExecute && canExecute.replacements) { + replaceVisual(context); + } else { + restoreVisual(context); + } + }); +} + +BpmnReplacePreview.$inject = [ + 'eventBus', + 'elementRegistry', + 'elementFactory', + 'canvas', + 'previewSupport' +]; + +inherits(BpmnReplacePreview, CommandInterceptor); \ No newline at end of file diff --git a/lib/features/replace-preview/index.js b/lib/features/replace-preview/index.js new file mode 100644 index 0000000..02a75a1 --- /dev/null +++ b/lib/features/replace-preview/index.js @@ -0,0 +1,11 @@ +import PreviewSupportModule from 'diagram-js/lib/features/preview-support'; + +import BpmnReplacePreview from './BpmnReplacePreview'; + +export default { + __depends__: [ + PreviewSupportModule + ], + __init__: [ 'bpmnReplacePreview' ], + bpmnReplacePreview: [ 'type', BpmnReplacePreview ] +}; diff --git a/lib/features/replace/BpmnReplace.js b/lib/features/replace/BpmnReplace.js new file mode 100644 index 0000000..24678c5 --- /dev/null +++ b/lib/features/replace/BpmnReplace.js @@ -0,0 +1,331 @@ +import { + pick, + assign, + filter, + forEach, + isArray, + isUndefined, + has +} from 'min-dash'; + +import { + is, + getBusinessObject +} from '../../util/ModelUtil'; + +import { + isAny +} from '../modeling/util/ModelingUtil'; + +import { + isExpanded, + isEventSubProcess +} from '../../util/DiUtil'; + +import { getPropertyNames } from '../copy-paste/ModdleCopy'; + +function copyProperties(source, target, properties) { + if (!isArray(properties)) { + properties = [ properties ]; + } + + forEach(properties, function(property) { + if (!isUndefined(source[property])) { + target[property] = source[property]; + } + }); +} + + +var CUSTOM_PROPERTIES = [ + 'cancelActivity', + 'instantiate', + 'eventGatewayType', + 'triggeredByEvent', + 'isInterrupting' +]; + +/** + * Check if element should be collapsed or expanded. + */ +function shouldToggleCollapsed(element, targetElement) { + + var oldCollapsed = ( + element && has(element, 'collapsed') ? element.collapsed : !isExpanded(element) + ); + + var targetCollapsed; + + if (targetElement && (has(targetElement, 'collapsed') || has(targetElement, 'isExpanded'))) { + + // property is explicitly set so use it + targetCollapsed = ( + has(targetElement, 'collapsed') ? targetElement.collapsed : !targetElement.isExpanded + ); + } else { + + // keep old state + targetCollapsed = oldCollapsed; + } + + if (oldCollapsed !== targetCollapsed) { + return true; + } + + return false; +} + + +/** + * This module takes care of replacing BPMN elements + */ +export default function BpmnReplace( + bpmnFactory, + elementFactory, + moddleCopy, + modeling, + replace, + rules, + selection +) { + + /** + * Prepares a new business object for the replacement element + * and triggers the replace operation. + * + * @param {djs.model.Base} element + * @param {Object} target + * @param {Object} [hints] + * + * @return {djs.model.Base} the newly created element + */ + function replaceElement(element, target, hints) { + + hints = hints || {}; + + var type = target.type, + oldBusinessObject = element.businessObject; + + if (isSubProcess(oldBusinessObject) && type === 'bpmn:SubProcess') { + if (shouldToggleCollapsed(element, target)) { + + // expanding or collapsing process + modeling.toggleCollapse(element); + + return element; + } + } + + var newBusinessObject = bpmnFactory.create(type); + + var newElement = { + type: type, + businessObject: newBusinessObject, + }; + + newElement.di = {}; + + // colors will be set to DI + copyProperties(element.di, newElement.di, [ + 'fill', + 'stroke', + 'background-color', + 'border-color', + 'color' + ]); + + var elementProps = getPropertyNames(oldBusinessObject.$descriptor), + newElementProps = getPropertyNames(newBusinessObject.$descriptor, true), + copyProps = intersection(elementProps, newElementProps); + + // initialize special properties defined in target definition + assign(newBusinessObject, pick(target, CUSTOM_PROPERTIES)); + + var properties = filter(copyProps, function(propertyName) { + + // copying event definitions, unless we replace + if (propertyName === 'eventDefinitions') { + return hasEventDefinition(element, target.eventDefinitionType); + } + + // retain loop characteristics if the target element + // is not an event sub process + if (propertyName === 'loopCharacteristics') { + return !isEventSubProcess(newBusinessObject); + } + + // so the applied properties from 'target' don't get lost + if (has(newBusinessObject, propertyName)) { + return false; + } + + if (propertyName === 'processRef' && target.isExpanded === false) { + return false; + } + + if (propertyName === 'triggeredByEvent') { + return false; + } + + return true; + }); + + newBusinessObject = moddleCopy.copyElement( + oldBusinessObject, + newBusinessObject, + properties + ); + + // initialize custom BPMN extensions + if (target.eventDefinitionType) { + + // only initialize with new eventDefinition + // if we did not set an event definition yet, + // i.e. because we copied it + if (!hasEventDefinition(newBusinessObject, target.eventDefinitionType)) { + newElement.eventDefinitionType = target.eventDefinitionType; + newElement.eventDefinitionAttrs = target.eventDefinitionAttrs; + } + } + + if (is(oldBusinessObject, 'bpmn:Activity')) { + + if (isSubProcess(oldBusinessObject)) { + + // no toggeling, so keep old state + newElement.isExpanded = isExpanded(element); + } + + // else if property is explicitly set, use it + else if (target && has(target, 'isExpanded')) { + newElement.isExpanded = target.isExpanded; + + // assign default size of new expanded element + var defaultSize = elementFactory.getDefaultSize(newBusinessObject, { + isExpanded: newElement.isExpanded + }); + + newElement.width = defaultSize.width; + newElement.height = defaultSize.height; + + // keep element centered + newElement.x = element.x - (newElement.width - element.width) / 2; + newElement.y = element.y - (newElement.height - element.height) / 2; + } + + // TODO: need also to respect min/max Size + // copy size, from an expanded subprocess to an expanded alternative subprocess + // except bpmn:Task, because Task is always expanded + if ((isExpanded(element) && !is(oldBusinessObject, 'bpmn:Task')) && newElement.isExpanded) { + newElement.width = element.width; + newElement.height = element.height; + } + } + + // remove children if not expanding sub process + if (isSubProcess(oldBusinessObject) && !isSubProcess(newBusinessObject)) { + hints.moveChildren = false; + } + + // transform collapsed/expanded pools + if (is(oldBusinessObject, 'bpmn:Participant')) { + + // create expanded pool + if (target.isExpanded === true) { + newBusinessObject.processRef = bpmnFactory.create('bpmn:Process'); + } else { + + // remove children when transforming to collapsed pool + hints.moveChildren = false; + } + + // apply same width and default height + newElement.width = element.width; + newElement.height = elementFactory.getDefaultSize(newElement).height; + } + + if (!rules.allowed('shape.resize', { shape: newBusinessObject })) { + newElement.height = elementFactory.getDefaultSize(newElement).height; + newElement.width = elementFactory.getDefaultSize(newElement).width; + } + + newBusinessObject.name = oldBusinessObject.name; + + // retain default flow's reference between inclusive <-> exclusive gateways and activities + if ( + isAny(oldBusinessObject, [ + 'bpmn:ExclusiveGateway', + 'bpmn:InclusiveGateway', + 'bpmn:Activity' + ]) && + isAny(newBusinessObject, [ + 'bpmn:ExclusiveGateway', + 'bpmn:InclusiveGateway', + 'bpmn:Activity' + ]) + ) { + newBusinessObject.default = oldBusinessObject.default; + } + + if ( + target.host && + !is(oldBusinessObject, 'bpmn:BoundaryEvent') && + is(newBusinessObject, 'bpmn:BoundaryEvent') + ) { + newElement.host = target.host; + } + + // The DataStoreReference element is 14px wider than the DataObjectReference element + // This ensures that they stay centered on the x axis when replaced + if ( + newElement.type === 'bpmn:DataStoreReference' || + newElement.type === 'bpmn:DataObjectReference' + ) { + newElement.x = element.x + (element.width - newElement.width) / 2; + } + + + newElement = replace.replaceElement(element, newElement, hints); + + if (hints.select !== false) { + selection.select(newElement); + } + + return newElement; + } + + this.replaceElement = replaceElement; +} + +BpmnReplace.$inject = [ + 'bpmnFactory', + 'elementFactory', + 'moddleCopy', + 'modeling', + 'replace', + 'rules', + 'selection' +]; + + +function isSubProcess(bo) { + return is(bo, 'bpmn:SubProcess'); +} + +function hasEventDefinition(element, type) { + + var bo = getBusinessObject(element); + + return type && bo.get('eventDefinitions').some(function(definition) { + return is(definition, type); + }); +} + +/** + * Compute intersection between two arrays. + */ +function intersection(a1, a2) { + return a1.filter(function(el) { + return a2.indexOf(el) !== -1; + }); +} diff --git a/lib/features/replace/ReplaceOptions.js b/lib/features/replace/ReplaceOptions.js new file mode 100644 index 0000000..bb9d8d1 --- /dev/null +++ b/lib/features/replace/ReplaceOptions.js @@ -0,0 +1,850 @@ +export var START_EVENT = [ + { + label: 'Start Event', + actionName: 'replace-with-none-start', + className: 'bpmn-icon-start-event-none', + target: { + type: 'bpmn:StartEvent' + } + }, + { + label: 'Intermediate Throw Event', + actionName: 'replace-with-none-intermediate-throwing', + className: 'bpmn-icon-intermediate-event-none', + target: { + type: 'bpmn:IntermediateThrowEvent' + } + }, + { + label: 'End Event', + actionName: 'replace-with-none-end', + className: 'bpmn-icon-end-event-none', + target: { + type: 'bpmn:EndEvent' + } + }, + { + label: 'Message Start Event', + actionName: 'replace-with-message-start', + className: 'bpmn-icon-start-event-message', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:MessageEventDefinition' + } + }, + { + label: 'Timer Start Event', + actionName: 'replace-with-timer-start', + className: 'bpmn-icon-start-event-timer', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:TimerEventDefinition' + } + }, + { + label: 'Conditional Start Event', + actionName: 'replace-with-conditional-start', + className: 'bpmn-icon-start-event-condition', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:ConditionalEventDefinition' + } + }, + { + label: 'Signal Start Event', + actionName: 'replace-with-signal-start', + className: 'bpmn-icon-start-event-signal', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:SignalEventDefinition' + } + } +]; + +export var START_EVENT_SUB_PROCESS = [ + { + label: 'Start Event', + actionName: 'replace-with-none-start', + className: 'bpmn-icon-start-event-none', + target: { + type: 'bpmn:StartEvent' + } + }, + { + label: 'Intermediate Throw Event', + actionName: 'replace-with-none-intermediate-throwing', + className: 'bpmn-icon-intermediate-event-none', + target: { + type: 'bpmn:IntermediateThrowEvent' + } + }, + { + label: 'End Event', + actionName: 'replace-with-none-end', + className: 'bpmn-icon-end-event-none', + target: { + type: 'bpmn:EndEvent' + } + } +]; + +export var INTERMEDIATE_EVENT = [ + { + label: 'Start Event', + actionName: 'replace-with-none-start', + className: 'bpmn-icon-start-event-none', + target: { + type: 'bpmn:StartEvent' + } + }, + { + label: 'Intermediate Throw Event', + actionName: 'replace-with-none-intermediate-throw', + className: 'bpmn-icon-intermediate-event-none', + target: { + type: 'bpmn:IntermediateThrowEvent' + } + }, + { + label: 'End Event', + actionName: 'replace-with-none-end', + className: 'bpmn-icon-end-event-none', + target: { + type: 'bpmn:EndEvent' + } + }, + { + label: 'Message Intermediate Catch Event', + actionName: 'replace-with-message-intermediate-catch', + className: 'bpmn-icon-intermediate-event-catch-message', + target: { + type: 'bpmn:IntermediateCatchEvent', + eventDefinitionType: 'bpmn:MessageEventDefinition' + } + }, + { + label: 'Message Intermediate Throw Event', + actionName: 'replace-with-message-intermediate-throw', + className: 'bpmn-icon-intermediate-event-throw-message', + target: { + type: 'bpmn:IntermediateThrowEvent', + eventDefinitionType: 'bpmn:MessageEventDefinition' + } + }, + { + label: 'Timer Intermediate Catch Event', + actionName: 'replace-with-timer-intermediate-catch', + className: 'bpmn-icon-intermediate-event-catch-timer', + target: { + type: 'bpmn:IntermediateCatchEvent', + eventDefinitionType: 'bpmn:TimerEventDefinition' + } + }, + { + label: 'Escalation Intermediate Throw Event', + actionName: 'replace-with-escalation-intermediate-throw', + className: 'bpmn-icon-intermediate-event-throw-escalation', + target: { + type: 'bpmn:IntermediateThrowEvent', + eventDefinitionType: 'bpmn:EscalationEventDefinition' + } + }, + { + label: 'Conditional Intermediate Catch Event', + actionName: 'replace-with-conditional-intermediate-catch', + className: 'bpmn-icon-intermediate-event-catch-condition', + target: { + type: 'bpmn:IntermediateCatchEvent', + eventDefinitionType: 'bpmn:ConditionalEventDefinition' + } + }, + { + label: 'Link Intermediate Catch Event', + actionName: 'replace-with-link-intermediate-catch', + className: 'bpmn-icon-intermediate-event-catch-link', + target: { + type: 'bpmn:IntermediateCatchEvent', + eventDefinitionType: 'bpmn:LinkEventDefinition', + eventDefinitionAttrs: { + name: '' + } + } + }, + { + label: 'Link Intermediate Throw Event', + actionName: 'replace-with-link-intermediate-throw', + className: 'bpmn-icon-intermediate-event-throw-link', + target: { + type: 'bpmn:IntermediateThrowEvent', + eventDefinitionType: 'bpmn:LinkEventDefinition', + eventDefinitionAttrs: { + name: '' + } + } + }, + { + label: 'Compensation Intermediate Throw Event', + actionName: 'replace-with-compensation-intermediate-throw', + className: 'bpmn-icon-intermediate-event-throw-compensation', + target: { + type: 'bpmn:IntermediateThrowEvent', + eventDefinitionType: 'bpmn:CompensateEventDefinition' + } + }, + { + label: 'Signal Intermediate Catch Event', + actionName: 'replace-with-signal-intermediate-catch', + className: 'bpmn-icon-intermediate-event-catch-signal', + target: { + type: 'bpmn:IntermediateCatchEvent', + eventDefinitionType: 'bpmn:SignalEventDefinition' + } + }, + { + label: 'Signal Intermediate Throw Event', + actionName: 'replace-with-signal-intermediate-throw', + className: 'bpmn-icon-intermediate-event-throw-signal', + target: { + type: 'bpmn:IntermediateThrowEvent', + eventDefinitionType: 'bpmn:SignalEventDefinition' + } + } +]; + +export var END_EVENT = [ + { + label: 'Start Event', + actionName: 'replace-with-none-start', + className: 'bpmn-icon-start-event-none', + target: { + type: 'bpmn:StartEvent' + } + }, + { + label: 'Intermediate Throw Event', + actionName: 'replace-with-none-intermediate-throw', + className: 'bpmn-icon-intermediate-event-none', + target: { + type: 'bpmn:IntermediateThrowEvent' + } + }, + { + label: 'End Event', + actionName: 'replace-with-none-end', + className: 'bpmn-icon-end-event-none', + target: { + type: 'bpmn:EndEvent' + } + }, + { + label: 'Message End Event', + actionName: 'replace-with-message-end', + className: 'bpmn-icon-end-event-message', + target: { + type: 'bpmn:EndEvent', + eventDefinitionType: 'bpmn:MessageEventDefinition' + } + }, + { + label: 'Escalation End Event', + actionName: 'replace-with-escalation-end', + className: 'bpmn-icon-end-event-escalation', + target: { + type: 'bpmn:EndEvent', + eventDefinitionType: 'bpmn:EscalationEventDefinition' + } + }, + { + label: 'Error End Event', + actionName: 'replace-with-error-end', + className: 'bpmn-icon-end-event-error', + target: { + type: 'bpmn:EndEvent', + eventDefinitionType: 'bpmn:ErrorEventDefinition' + } + }, + { + label: 'Cancel End Event', + actionName: 'replace-with-cancel-end', + className: 'bpmn-icon-end-event-cancel', + target: { + type: 'bpmn:EndEvent', + eventDefinitionType: 'bpmn:CancelEventDefinition' + } + }, + { + label: 'Compensation End Event', + actionName: 'replace-with-compensation-end', + className: 'bpmn-icon-end-event-compensation', + target: { + type: 'bpmn:EndEvent', + eventDefinitionType: 'bpmn:CompensateEventDefinition' + } + }, + { + label: 'Signal End Event', + actionName: 'replace-with-signal-end', + className: 'bpmn-icon-end-event-signal', + target: { + type: 'bpmn:EndEvent', + eventDefinitionType: 'bpmn:SignalEventDefinition' + } + }, + { + label: 'Terminate End Event', + actionName: 'replace-with-terminate-end', + className: 'bpmn-icon-end-event-terminate', + target: { + type: 'bpmn:EndEvent', + eventDefinitionType: 'bpmn:TerminateEventDefinition' + } + } +]; + +export var GATEWAY = [ + { + label: 'Exclusive Gateway', + actionName: 'replace-with-exclusive-gateway', + className: 'bpmn-icon-gateway-xor', + target: { + type: 'bpmn:ExclusiveGateway' + } + }, + { + label: 'Parallel Gateway', + actionName: 'replace-with-parallel-gateway', + className: 'bpmn-icon-gateway-parallel', + target: { + type: 'bpmn:ParallelGateway' + } + }, + { + label: 'Inclusive Gateway', + actionName: 'replace-with-inclusive-gateway', + className: 'bpmn-icon-gateway-or', + target: { + type: 'bpmn:InclusiveGateway' + } + }, + { + label: 'Complex Gateway', + actionName: 'replace-with-complex-gateway', + className: 'bpmn-icon-gateway-complex', + target: { + type: 'bpmn:ComplexGateway' + } + }, + { + label: 'Event based Gateway', + actionName: 'replace-with-event-based-gateway', + className: 'bpmn-icon-gateway-eventbased', + target: { + type: 'bpmn:EventBasedGateway', + instantiate: false, + eventGatewayType: 'Exclusive' + } + } + + // Gateways deactivated until https://github.com/bpmn-io/bpmn-js/issues/194 + // { + // label: 'Event based instantiating Gateway', + // actionName: 'replace-with-exclusive-event-based-gateway', + // className: 'bpmn-icon-exclusive-event-based', + // target: { + // type: 'bpmn:EventBasedGateway' + // }, + // options: { + // businessObject: { instantiate: true, eventGatewayType: 'Exclusive' } + // } + // }, + // { + // label: 'Parallel Event based instantiating Gateway', + // actionName: 'replace-with-parallel-event-based-instantiate-gateway', + // className: 'bpmn-icon-parallel-event-based-instantiate-gateway', + // target: { + // type: 'bpmn:EventBasedGateway' + // }, + // options: { + // businessObject: { instantiate: true, eventGatewayType: 'Parallel' } + // } + // } +]; + +export var SUBPROCESS_EXPANDED = [ + { + label: 'Transaction', + actionName: 'replace-with-transaction', + className: 'bpmn-icon-transaction', + target: { + type: 'bpmn:Transaction', + isExpanded: true + } + }, + { + label: 'Event Sub Process', + actionName: 'replace-with-event-subprocess', + className: 'bpmn-icon-event-subprocess-expanded', + target: { + type: 'bpmn:SubProcess', + triggeredByEvent: true, + isExpanded: true + } + }, + { + label: 'Sub Process (collapsed)', + actionName: 'replace-with-collapsed-subprocess', + className: 'bpmn-icon-subprocess-collapsed', + target: { + type: 'bpmn:SubProcess', + isExpanded: false + } + } +]; + +export var TRANSACTION = [ + { + label: 'Sub Process', + actionName: 'replace-with-subprocess', + className: 'bpmn-icon-subprocess-expanded', + target: { + type: 'bpmn:SubProcess', + isExpanded: true + } + }, + { + label: 'Event Sub Process', + actionName: 'replace-with-event-subprocess', + className: 'bpmn-icon-event-subprocess-expanded', + target: { + type: 'bpmn:SubProcess', + triggeredByEvent: true, + isExpanded: true + } + } +]; + +export var EVENT_SUB_PROCESS = [ + { + label: 'Sub Process', + actionName: 'replace-with-subprocess', + className: 'bpmn-icon-subprocess-expanded', + target: { + type: 'bpmn:SubProcess', + isExpanded: true + } + }, + { + label: 'Transaction', + actionName: 'replace-with-transaction', + className: 'bpmn-icon-transaction', + target: { + type: 'bpmn:Transaction', + isExpanded: true + } + } +]; + +export var TASK = [ + { + label: 'Task', + actionName: 'replace-with-task', + className: 'bpmn-icon-task', + target: { + type: 'bpmn:Task' + } + }, + { + label: 'Send Task', + actionName: 'replace-with-send-task', + className: 'bpmn-icon-send', + target: { + type: 'bpmn:SendTask' + } + }, + { + label: 'Receive Task', + actionName: 'replace-with-receive-task', + className: 'bpmn-icon-receive', + target: { + type: 'bpmn:ReceiveTask' + } + }, + { + label: 'User Task', + actionName: 'replace-with-user-task', + className: 'bpmn-icon-user', + target: { + type: 'bpmn:UserTask' + } + }, + { + label: 'Manual Task', + actionName: 'replace-with-manual-task', + className: 'bpmn-icon-manual', + target: { + type: 'bpmn:ManualTask' + } + }, + { + label: 'Business Rule Task', + actionName: 'replace-with-rule-task', + className: 'bpmn-icon-business-rule', + target: { + type: 'bpmn:BusinessRuleTask' + } + }, + { + label: 'Service Task', + actionName: 'replace-with-service-task', + className: 'bpmn-icon-service', + target: { + type: 'bpmn:ServiceTask' + } + }, + { + label: 'Script Task', + actionName: 'replace-with-script-task', + className: 'bpmn-icon-script', + target: { + type: 'bpmn:ScriptTask' + } + }, + { + label: 'Call Activity', + actionName: 'replace-with-call-activity', + className: 'bpmn-icon-call-activity', + target: { + type: 'bpmn:CallActivity' + } + }, + { + label: 'Sub Process (collapsed)', + actionName: 'replace-with-collapsed-subprocess', + className: 'bpmn-icon-subprocess-collapsed', + target: { + type: 'bpmn:SubProcess', + isExpanded: false + } + }, + { + label: 'Sub Process (expanded)', + actionName: 'replace-with-expanded-subprocess', + className: 'bpmn-icon-subprocess-expanded', + target: { + type: 'bpmn:SubProcess', + isExpanded: true + } + } +]; + +export var DATA_OBJECT_REFERENCE = [ + { + label: 'Data Store Reference', + actionName: 'replace-with-data-store-reference', + className: 'bpmn-icon-data-store', + target: { + type: 'bpmn:DataStoreReference' + } + } +]; + +export var DATA_STORE_REFERENCE = [ + { + label: 'Data Object Reference', + actionName: 'replace-with-data-object-reference', + className: 'bpmn-icon-data-object', + target: { + type: 'bpmn:DataObjectReference' + } + } +]; + +export var BOUNDARY_EVENT = [ + { + label: 'Message Boundary Event', + actionName: 'replace-with-message-boundary', + className: 'bpmn-icon-intermediate-event-catch-message', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:MessageEventDefinition' + } + }, + { + label: 'Timer Boundary Event', + actionName: 'replace-with-timer-boundary', + className: 'bpmn-icon-intermediate-event-catch-timer', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:TimerEventDefinition' + } + }, + { + label: 'Escalation Boundary Event', + actionName: 'replace-with-escalation-boundary', + className: 'bpmn-icon-intermediate-event-catch-escalation', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:EscalationEventDefinition' + } + }, + { + label: 'Conditional Boundary Event', + actionName: 'replace-with-conditional-boundary', + className: 'bpmn-icon-intermediate-event-catch-condition', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:ConditionalEventDefinition' + } + }, + { + label: 'Error Boundary Event', + actionName: 'replace-with-error-boundary', + className: 'bpmn-icon-intermediate-event-catch-error', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:ErrorEventDefinition' + } + }, + { + label: 'Cancel Boundary Event', + actionName: 'replace-with-cancel-boundary', + className: 'bpmn-icon-intermediate-event-catch-cancel', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:CancelEventDefinition' + } + }, + { + label: 'Signal Boundary Event', + actionName: 'replace-with-signal-boundary', + className: 'bpmn-icon-intermediate-event-catch-signal', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:SignalEventDefinition' + } + }, + { + label: 'Compensation Boundary Event', + actionName: 'replace-with-compensation-boundary', + className: 'bpmn-icon-intermediate-event-catch-compensation', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:CompensateEventDefinition' + } + }, + { + label: 'Message Boundary Event (non-interrupting)', + actionName: 'replace-with-non-interrupting-message-boundary', + className: 'bpmn-icon-intermediate-event-catch-non-interrupting-message', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:MessageEventDefinition', + cancelActivity: false + } + }, + { + label: 'Timer Boundary Event (non-interrupting)', + actionName: 'replace-with-non-interrupting-timer-boundary', + className: 'bpmn-icon-intermediate-event-catch-non-interrupting-timer', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:TimerEventDefinition', + cancelActivity: false + } + }, + { + label: 'Escalation Boundary Event (non-interrupting)', + actionName: 'replace-with-non-interrupting-escalation-boundary', + className: 'bpmn-icon-intermediate-event-catch-non-interrupting-escalation', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:EscalationEventDefinition', + cancelActivity: false + } + }, + { + label: 'Conditional Boundary Event (non-interrupting)', + actionName: 'replace-with-non-interrupting-conditional-boundary', + className: 'bpmn-icon-intermediate-event-catch-non-interrupting-condition', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:ConditionalEventDefinition', + cancelActivity: false + } + }, + { + label: 'Signal Boundary Event (non-interrupting)', + actionName: 'replace-with-non-interrupting-signal-boundary', + className: 'bpmn-icon-intermediate-event-catch-non-interrupting-signal', + target: { + type: 'bpmn:BoundaryEvent', + eventDefinitionType: 'bpmn:SignalEventDefinition', + cancelActivity: false + } + } +]; + +export var EVENT_SUB_PROCESS_START_EVENT = [ + { + label: 'Message Start Event', + actionName: 'replace-with-message-start', + className: 'bpmn-icon-start-event-message', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:MessageEventDefinition' + } + }, + { + label: 'Timer Start Event', + actionName: 'replace-with-timer-start', + className: 'bpmn-icon-start-event-timer', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:TimerEventDefinition' + } + }, + { + label: 'Conditional Start Event', + actionName: 'replace-with-conditional-start', + className: 'bpmn-icon-start-event-condition', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:ConditionalEventDefinition' + } + }, + { + label: 'Signal Start Event', + actionName: 'replace-with-signal-start', + className: 'bpmn-icon-start-event-signal', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:SignalEventDefinition' + } + }, + { + label: 'Error Start Event', + actionName: 'replace-with-error-start', + className: 'bpmn-icon-start-event-error', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:ErrorEventDefinition' + } + }, + { + label: 'Escalation Start Event', + actionName: 'replace-with-escalation-start', + className: 'bpmn-icon-start-event-escalation', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:EscalationEventDefinition' + } + }, + { + label: 'Compensation Start Event', + actionName: 'replace-with-compensation-start', + className: 'bpmn-icon-start-event-compensation', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:CompensateEventDefinition' + } + }, + { + label: 'Message Start Event (non-interrupting)', + actionName: 'replace-with-non-interrupting-message-start', + className: 'bpmn-icon-start-event-non-interrupting-message', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:MessageEventDefinition', + isInterrupting: false + } + }, + { + label: 'Timer Start Event (non-interrupting)', + actionName: 'replace-with-non-interrupting-timer-start', + className: 'bpmn-icon-start-event-non-interrupting-timer', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:TimerEventDefinition', + isInterrupting: false + } + }, + { + label: 'Conditional Start Event (non-interrupting)', + actionName: 'replace-with-non-interrupting-conditional-start', + className: 'bpmn-icon-start-event-non-interrupting-condition', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:ConditionalEventDefinition', + isInterrupting: false + } + }, + { + label: 'Signal Start Event (non-interrupting)', + actionName: 'replace-with-non-interrupting-signal-start', + className: 'bpmn-icon-start-event-non-interrupting-signal', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:SignalEventDefinition', + isInterrupting: false + } + }, + { + label: 'Escalation Start Event (non-interrupting)', + actionName: 'replace-with-non-interrupting-escalation-start', + className: 'bpmn-icon-start-event-non-interrupting-escalation', + target: { + type: 'bpmn:StartEvent', + eventDefinitionType: 'bpmn:EscalationEventDefinition', + isInterrupting: false + } + } +]; + +export var SEQUENCE_FLOW = [ + { + label: 'Sequence Flow', + actionName: 'replace-with-sequence-flow', + className: 'bpmn-icon-connection' + }, + { + label: 'Default Flow', + actionName: 'replace-with-default-flow', + className: 'bpmn-icon-default-flow' + }, + { + label: 'Conditional Flow', + actionName: 'replace-with-conditional-flow', + className: 'bpmn-icon-conditional-flow' + } +]; + +export var PARTICIPANT = [ + { + label: 'Expanded Pool', + actionName: 'replace-with-expanded-pool', + className: 'bpmn-icon-participant', + target: { + type: 'bpmn:Participant', + isExpanded: true + } + }, + { + label: function(element) { + var label = 'Empty Pool'; + + if (element.children && element.children.length) { + label += ' (removes content)'; + } + + return label; + }, + actionName: 'replace-with-collapsed-pool', + + // TODO(@janstuemmel): maybe design new icon + className: 'bpmn-icon-lane', + target: { + type: 'bpmn:Participant', + isExpanded: false + } + } +]; diff --git a/lib/features/replace/index.js b/lib/features/replace/index.js new file mode 100644 index 0000000..dec4091 --- /dev/null +++ b/lib/features/replace/index.js @@ -0,0 +1,14 @@ +import CopyPasteModule from '../copy-paste'; +import ReplaceModule from 'diagram-js/lib/features/replace'; +import SelectionModule from 'diagram-js/lib/features/selection'; + +import BpmnReplace from './BpmnReplace'; + +export default { + __depends__: [ + CopyPasteModule, + ReplaceModule, + SelectionModule + ], + bpmnReplace: [ 'type', BpmnReplace ] +}; diff --git a/lib/features/rules/BpmnRules.js b/lib/features/rules/BpmnRules.js new file mode 100644 index 0000000..a2a0009 --- /dev/null +++ b/lib/features/rules/BpmnRules.js @@ -0,0 +1,954 @@ +import { + every, + find, + forEach, + some +} from 'min-dash'; + +import inherits from 'inherits-browser'; + +import { + is, + getBusinessObject +} from '../../util/ModelUtil'; + +import { + getParent, + isAny +} from '../modeling/util/ModelingUtil'; + +import { + isLabel +} from '../../util/LabelUtil'; + +import { + isExpanded, + isEventSubProcess, + isInterrupting, + hasErrorEventDefinition, + hasEscalationEventDefinition, + hasCompensateEventDefinition +} from '../../util/DiUtil'; + +import RuleProvider from 'diagram-js/lib/features/rules/RuleProvider'; + +import { + getBoundaryAttachment as isBoundaryAttachment +} from '../snapping/BpmnSnappingUtil'; + + +/** + * BPMN specific modeling rule + */ +export default function BpmnRules(eventBus) { + RuleProvider.call(this, eventBus); +} + +inherits(BpmnRules, RuleProvider); + +BpmnRules.$inject = [ 'eventBus' ]; + +BpmnRules.prototype.init = function() { + + this.addRule('connection.start', function(context) { + var source = context.source; + + return canStartConnection(source); + }); + + this.addRule('connection.create', function(context) { + var source = context.source, + target = context.target, + hints = context.hints || {}, + targetParent = hints.targetParent, + targetAttach = hints.targetAttach; + + // don't allow incoming connections on + // newly created boundary events + // to boundary events + if (targetAttach) { + return false; + } + + // temporarily set target parent for scoping + // checks to work + if (targetParent) { + target.parent = targetParent; + } + + try { + return canConnect(source, target); + } finally { + + // unset temporary target parent + if (targetParent) { + target.parent = null; + } + } + }); + + this.addRule('connection.reconnect', function(context) { + + var connection = context.connection, + source = context.source, + target = context.target; + + return canConnect(source, target, connection); + }); + + this.addRule('connection.updateWaypoints', function(context) { + return { + type: context.connection.type + }; + }); + + this.addRule('shape.resize', function(context) { + + var shape = context.shape, + newBounds = context.newBounds; + + return canResize(shape, newBounds); + }); + + this.addRule('elements.create', function(context) { + var elements = context.elements, + position = context.position, + target = context.target; + + if (isConnection(target) && !canInsert(elements, target, position)) { + return false; + } + + return every(elements, function(element) { + if (isConnection(element)) { + return canConnect(element.source, element.target, element); + } + + if (element.host) { + return canAttach(element, element.host, null, position); + } + + return canCreate(element, target, null, position); + }); + }); + + this.addRule('elements.move', function(context) { + + var target = context.target, + shapes = context.shapes, + position = context.position; + + return canAttach(shapes, target, null, position) || + canReplace(shapes, target, position) || + canMove(shapes, target, position) || + canInsert(shapes, target, position); + }); + + this.addRule('shape.create', function(context) { + return canCreate( + context.shape, + context.target, + context.source, + context.position + ); + }); + + this.addRule('shape.attach', function(context) { + + return canAttach( + context.shape, + context.target, + null, + context.position + ); + }); + + this.addRule('element.copy', function(context) { + var element = context.element, + elements = context.elements; + + return canCopy(elements, element); + }); +}; + +BpmnRules.prototype.canConnectMessageFlow = canConnectMessageFlow; + +BpmnRules.prototype.canConnectSequenceFlow = canConnectSequenceFlow; + +BpmnRules.prototype.canConnectDataAssociation = canConnectDataAssociation; + +BpmnRules.prototype.canConnectAssociation = canConnectAssociation; + +BpmnRules.prototype.canMove = canMove; + +BpmnRules.prototype.canAttach = canAttach; + +BpmnRules.prototype.canReplace = canReplace; + +BpmnRules.prototype.canDrop = canDrop; + +BpmnRules.prototype.canInsert = canInsert; + +BpmnRules.prototype.canCreate = canCreate; + +BpmnRules.prototype.canConnect = canConnect; + +BpmnRules.prototype.canResize = canResize; + +BpmnRules.prototype.canCopy = canCopy; + +/** + * Utility functions for rule checking + */ + +/** + * Checks if given element can be used for starting connection. + * + * @param {Element} source + * @return {boolean} + */ +function canStartConnection(element) { + if (nonExistingOrLabel(element)) { + return null; + } + + return isAny(element, [ + 'bpmn:FlowNode', + 'bpmn:InteractionNode', + 'bpmn:DataObjectReference', + 'bpmn:DataStoreReference', + 'bpmn:Group', + 'bpmn:TextAnnotation' + ]); +} + +function nonExistingOrLabel(element) { + return !element || isLabel(element); +} + +function isSame(a, b) { + return a === b; +} + +function getOrganizationalParent(element) { + + do { + if (is(element, 'bpmn:Process')) { + return getBusinessObject(element); + } + + if (is(element, 'bpmn:Participant')) { + return ( + getBusinessObject(element).processRef || + getBusinessObject(element) + ); + } + } while ((element = element.parent)); + +} + +function isTextAnnotation(element) { + return is(element, 'bpmn:TextAnnotation'); +} + +function isGroup(element) { + return is(element, 'bpmn:Group') && !element.labelTarget; +} + +function isCompensationBoundary(element) { + return is(element, 'bpmn:BoundaryEvent') && + hasEventDefinition(element, 'bpmn:CompensateEventDefinition'); +} + +function isForCompensation(e) { + return getBusinessObject(e).isForCompensation; +} + +function isSameOrganization(a, b) { + var parentA = getOrganizationalParent(a), + parentB = getOrganizationalParent(b); + + return parentA === parentB; +} + +function isMessageFlowSource(element) { + return ( + is(element, 'bpmn:InteractionNode') && + !is(element, 'bpmn:BoundaryEvent') && ( + !is(element, 'bpmn:Event') || ( + is(element, 'bpmn:ThrowEvent') && + hasEventDefinitionOrNone(element, 'bpmn:MessageEventDefinition') + ) + ) + ); +} + +function isMessageFlowTarget(element) { + return ( + is(element, 'bpmn:InteractionNode') && + !isForCompensation(element) && ( + !is(element, 'bpmn:Event') || ( + is(element, 'bpmn:CatchEvent') && + hasEventDefinitionOrNone(element, 'bpmn:MessageEventDefinition') + ) + ) && !( + is(element, 'bpmn:BoundaryEvent') && + !hasEventDefinition(element, 'bpmn:MessageEventDefinition') + ) + ); +} + +function getScopeParent(element) { + + var parent = element; + + while ((parent = parent.parent)) { + + if (is(parent, 'bpmn:FlowElementsContainer')) { + return getBusinessObject(parent); + } + + if (is(parent, 'bpmn:Participant')) { + return getBusinessObject(parent).processRef; + } + } + + return null; +} + +function isSameScope(a, b) { + var scopeParentA = getScopeParent(a), + scopeParentB = getScopeParent(b); + + return scopeParentA === scopeParentB; +} + +function hasEventDefinition(element, eventDefinition) { + var bo = getBusinessObject(element); + + return !!find(bo.eventDefinitions || [], function(definition) { + return is(definition, eventDefinition); + }); +} + +function hasEventDefinitionOrNone(element, eventDefinition) { + var bo = getBusinessObject(element); + + return (bo.eventDefinitions || []).every(function(definition) { + return is(definition, eventDefinition); + }); +} + +function isSequenceFlowSource(element) { + return ( + is(element, 'bpmn:FlowNode') && + !is(element, 'bpmn:EndEvent') && + !isEventSubProcess(element) && + !(is(element, 'bpmn:IntermediateThrowEvent') && + hasEventDefinition(element, 'bpmn:LinkEventDefinition') + ) && + !isCompensationBoundary(element) && + !isForCompensation(element) + ); +} + +function isSequenceFlowTarget(element) { + return ( + is(element, 'bpmn:FlowNode') && + !is(element, 'bpmn:StartEvent') && + !is(element, 'bpmn:BoundaryEvent') && + !isEventSubProcess(element) && + !(is(element, 'bpmn:IntermediateCatchEvent') && + hasEventDefinition(element, 'bpmn:LinkEventDefinition') + ) && + !isForCompensation(element) + ); +} + +function isEventBasedTarget(element) { + return ( + is(element, 'bpmn:ReceiveTask') || ( + is(element, 'bpmn:IntermediateCatchEvent') && ( + hasEventDefinition(element, 'bpmn:MessageEventDefinition') || + hasEventDefinition(element, 'bpmn:TimerEventDefinition') || + hasEventDefinition(element, 'bpmn:ConditionalEventDefinition') || + hasEventDefinition(element, 'bpmn:SignalEventDefinition') + ) + ) + ); +} + +function isConnection(element) { + return element.waypoints; +} + +function getParents(element) { + + var parents = []; + + while (element) { + element = element.parent; + + if (element) { + parents.push(element); + } + } + + return parents; +} + +function isParent(possibleParent, element) { + var allParents = getParents(element); + return allParents.indexOf(possibleParent) !== -1; +} + +function canConnect(source, target, connection) { + + if (nonExistingOrLabel(source) || nonExistingOrLabel(target)) { + return null; + } + + if (!is(connection, 'bpmn:DataAssociation')) { + + if (canConnectMessageFlow(source, target)) { + return { type: 'bpmn:MessageFlow' }; + } + + if (canConnectSequenceFlow(source, target)) { + return { type: 'bpmn:SequenceFlow' }; + } + } + + var connectDataAssociation = canConnectDataAssociation(source, target); + + if (connectDataAssociation) { + return connectDataAssociation; + } + + if (isCompensationBoundary(source) && isForCompensation(target)) { + return { + type: 'bpmn:Association', + associationDirection: 'One' + }; + } + + if (canConnectAssociation(source, target)) { + + return { + type: 'bpmn:Association' + }; + } + + return false; +} + +/** + * Can an element be dropped into the target element + * + * @return {boolean} + */ +function canDrop(element, target, position) { + + // can move labels and groups everywhere + if (isLabel(element) || isGroup(element)) { + return true; + } + + + // disallow to create elements on collapsed pools + if (is(target, 'bpmn:Participant') && !isExpanded(target)) { + return false; + } + + // allow to create new participants on + // existing collaboration and process diagrams + if (is(element, 'bpmn:Participant')) { + return is(target, 'bpmn:Process') || is(target, 'bpmn:Collaboration'); + } + + // allow moving DataInput / DataOutput within its original container only + if (isAny(element, [ 'bpmn:DataInput', 'bpmn:DataOutput' ])) { + + if (element.parent) { + return target === element.parent; + } + } + + // allow creating lanes on participants and other lanes only + if (is(element, 'bpmn:Lane')) { + return is(target, 'bpmn:Participant') || is(target, 'bpmn:Lane'); + } + + // disallow dropping boundary events which cannot replace with intermediate event + if (is(element, 'bpmn:BoundaryEvent') && !isDroppableBoundaryEvent(element)) { + return false; + } + + // drop flow elements onto flow element containers + // and participants + if (is(element, 'bpmn:FlowElement') && !is(element, 'bpmn:DataStoreReference')) { + if (is(target, 'bpmn:FlowElementsContainer')) { + return isExpanded(target); + } + + return isAny(target, [ 'bpmn:Participant', 'bpmn:Lane' ]); + } + + // disallow dropping data store reference if there is no process to append to + if (is(element, 'bpmn:DataStoreReference') && is(target, 'bpmn:Collaboration')) { + return some(getBusinessObject(target).get('participants'), function(participant) { + return !!participant.get('processRef'); + }); + } + + // account for the fact that data associations are always + // rendered and moved to top (Process or Collaboration level) + // + // artifacts may be placed wherever, too + if (isAny(element, [ 'bpmn:Artifact', 'bpmn:DataAssociation', 'bpmn:DataStoreReference' ])) { + return isAny(target, [ + 'bpmn:Collaboration', + 'bpmn:Lane', + 'bpmn:Participant', + 'bpmn:Process', + 'bpmn:SubProcess' ]); + } + + if (is(element, 'bpmn:MessageFlow')) { + return is(target, 'bpmn:Collaboration') + || element.source.parent == target + || element.target.parent == target; + } + + return false; +} + +function isDroppableBoundaryEvent(event) { + return getBusinessObject(event).cancelActivity && ( + hasNoEventDefinition(event) || hasCommonBoundaryIntermediateEventDefinition(event) + ); +} + +function isBoundaryEvent(element) { + return !isLabel(element) && is(element, 'bpmn:BoundaryEvent'); +} + +function isLane(element) { + return is(element, 'bpmn:Lane'); +} + +/** + * We treat IntermediateThrowEvents as boundary events during create, + * this must be reflected in the rules. + */ +function isBoundaryCandidate(element) { + if (isBoundaryEvent(element)) { + return true; + } + + if (is(element, 'bpmn:IntermediateThrowEvent') && hasNoEventDefinition(element)) { + return true; + } + + return ( + is(element, 'bpmn:IntermediateCatchEvent') && + hasCommonBoundaryIntermediateEventDefinition(element) + ); +} + +function hasNoEventDefinition(element) { + var bo = getBusinessObject(element); + + return bo && !(bo.eventDefinitions && bo.eventDefinitions.length); +} + +function hasCommonBoundaryIntermediateEventDefinition(element) { + return hasOneOfEventDefinitions(element, [ + 'bpmn:MessageEventDefinition', + 'bpmn:TimerEventDefinition', + 'bpmn:SignalEventDefinition', + 'bpmn:ConditionalEventDefinition' + ]); +} + +function hasOneOfEventDefinitions(element, eventDefinitions) { + return eventDefinitions.some(function(definition) { + return hasEventDefinition(element, definition); + }); +} + +function isReceiveTaskAfterEventBasedGateway(element) { + return ( + is(element, 'bpmn:ReceiveTask') && + find(element.incoming, function(incoming) { + return is(incoming.source, 'bpmn:EventBasedGateway'); + }) + ); +} + + +function canAttach(elements, target, source, position) { + + if (!Array.isArray(elements)) { + elements = [ elements ]; + } + + // only (re-)attach one element at a time + if (elements.length !== 1) { + return false; + } + + var element = elements[0]; + + // do not attach labels + if (isLabel(element)) { + return false; + } + + // only handle boundary events + if (!isBoundaryCandidate(element)) { + return false; + } + + // disallow drop on event sub processes + if (isEventSubProcess(target)) { + return false; + } + + // only allow drop on non compensation activities + if (!is(target, 'bpmn:Activity') || isForCompensation(target)) { + return false; + } + + // only attach to subprocess border + if (position && !isBoundaryAttachment(position, target)) { + return false; + } + + // do not attach on receive tasks after event based gateways + if (isReceiveTaskAfterEventBasedGateway(target)) { + return false; + } + + return 'attach'; +} + + +/** + * Defines how to replace elements for a given target. + * + * Returns an array containing all elements which will be replaced. + * + * @example + * + * [{ id: 'IntermediateEvent_2', + * type: 'bpmn:StartEvent' + * }, + * { id: 'IntermediateEvent_5', + * type: 'bpmn:EndEvent' + * }] + * + * @param {Array} elements + * @param {Object} target + * + * @return {Object} an object containing all elements which have to be replaced + */ +function canReplace(elements, target, position) { + + if (!target) { + return false; + } + + var canExecute = { + replacements: [] + }; + + forEach(elements, function(element) { + + if (!isEventSubProcess(target)) { + + if (is(element, 'bpmn:StartEvent') && + element.type !== 'label' && + canDrop(element, target)) { + + // replace a non-interrupting start event by a blank interrupting start event + // when the target is not an event sub process + if (!isInterrupting(element)) { + canExecute.replacements.push({ + oldElementId: element.id, + newElementType: 'bpmn:StartEvent' + }); + } + + // replace an error/escalation/compensate start event by a blank interrupting start event + // when the target is not an event sub process + if (hasErrorEventDefinition(element) || + hasEscalationEventDefinition(element) || + hasCompensateEventDefinition(element)) { + canExecute.replacements.push({ + oldElementId: element.id, + newElementType: 'bpmn:StartEvent' + }); + } + + // replace a typed start event by a blank interrupting start event + // when the target is a sub process but not an event sub process + if (hasOneOfEventDefinitions(element, + [ + 'bpmn:MessageEventDefinition', + 'bpmn:TimerEventDefinition', + 'bpmn:SignalEventDefinition', + 'bpmn:ConditionalEventDefinition' + ]) && + is(target, 'bpmn:SubProcess')) { + canExecute.replacements.push({ + oldElementId: element.id, + newElementType: 'bpmn:StartEvent' + }); + } + } + } + + if (!is(target, 'bpmn:Transaction')) { + if (hasEventDefinition(element, 'bpmn:CancelEventDefinition') && + element.type !== 'label') { + + if (is(element, 'bpmn:EndEvent') && canDrop(element, target)) { + canExecute.replacements.push({ + oldElementId: element.id, + newElementType: 'bpmn:EndEvent' + }); + } + + if (is(element, 'bpmn:BoundaryEvent') && canAttach(element, target, null, position)) { + canExecute.replacements.push({ + oldElementId: element.id, + newElementType: 'bpmn:BoundaryEvent' + }); + } + } + } + }); + + return canExecute.replacements.length ? canExecute : false; +} + +function canMove(elements, target) { + + // do not move selection containing lanes + if (some(elements, isLane)) { + return false; + } + + // allow default move check to start move operation + if (!target) { + return true; + } + + return elements.every(function(element) { + return canDrop(element, target); + }); +} + +function canCreate(shape, target, source, position) { + + if (!target) { + return false; + } + + if (isLabel(shape) || isGroup(shape)) { + return true; + } + + if (isSame(source, target)) { + return false; + } + + // ensure we do not drop the element + // into source + if (source && isParent(source, target)) { + return false; + } + + return canDrop(shape, target, position) || canInsert(shape, target, position); +} + +function canResize(shape, newBounds) { + if (is(shape, 'bpmn:SubProcess')) { + return ( + isExpanded(shape) && ( + !newBounds || (newBounds.width >= 100 && newBounds.height >= 80) + ) + ); + } + + if (is(shape, 'bpmn:Lane')) { + return !newBounds || (newBounds.width >= 130 && newBounds.height >= 60); + } + + if (is(shape, 'bpmn:Participant')) { + return !newBounds || (newBounds.width >= 250 && newBounds.height >= 50); + } + + if (isTextAnnotation(shape)) { + return true; + } + + if (isGroup(shape)) { + return true; + } + + return false; +} + +/** + * Check, whether one side of the relationship + * is a text annotation. + */ +function isOneTextAnnotation(source, target) { + + var sourceTextAnnotation = isTextAnnotation(source), + targetTextAnnotation = isTextAnnotation(target); + + return ( + (sourceTextAnnotation || targetTextAnnotation) && + (sourceTextAnnotation !== targetTextAnnotation) + ); +} + + +function canConnectAssociation(source, target) { + + // compensation boundary events are exception + if (isCompensationBoundary(source) && isForCompensation(target)) { + return true; + } + + // don't connect parent <-> child + if (isParent(target, source) || isParent(source, target)) { + return false; + } + + // allow connection of associations between and + if (isOneTextAnnotation(source, target)) { + return true; + } + + // can connect associations where we can connect + // data associations, too (!) + return !!canConnectDataAssociation(source, target); +} + +function canConnectMessageFlow(source, target) { + + // during connect user might move mouse out of canvas + // https://github.com/bpmn-io/bpmn-js/issues/1033 + if (getRootElement(source) && !getRootElement(target)) { + return false; + } + + return ( + isMessageFlowSource(source) && + isMessageFlowTarget(target) && + !isSameOrganization(source, target) + ); +} + +function canConnectSequenceFlow(source, target) { + + if ( + isEventBasedTarget(target) && + target.incoming.length > 0 && + areOutgoingEventBasedGatewayConnections(target.incoming) && + !is(source, 'bpmn:EventBasedGateway') + ) { + return false; + } + + return isSequenceFlowSource(source) && + isSequenceFlowTarget(target) && + isSameScope(source, target) && + !(is(source, 'bpmn:EventBasedGateway') && !isEventBasedTarget(target)); +} + + +function canConnectDataAssociation(source, target) { + + if (isAny(source, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ]) && + isAny(target, [ 'bpmn:Activity', 'bpmn:ThrowEvent' ])) { + return { type: 'bpmn:DataInputAssociation' }; + } + + if (isAny(target, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ]) && + isAny(source, [ 'bpmn:Activity', 'bpmn:CatchEvent' ])) { + return { type: 'bpmn:DataOutputAssociation' }; + } + + return false; +} + +function canInsert(shape, flow, position) { + + if (!flow) { + return false; + } + + if (Array.isArray(shape)) { + if (shape.length !== 1) { + return false; + } + + shape = shape[0]; + } + + if (flow.source === shape || + flow.target === shape) { + return false; + } + + // return true if we can drop on the + // underlying flow parent + // + // at this point we are not really able to talk + // about connection rules (yet) + + return ( + isAny(flow, [ 'bpmn:SequenceFlow', 'bpmn:MessageFlow' ]) && + !isLabel(flow) && + is(shape, 'bpmn:FlowNode') && + !is(shape, 'bpmn:BoundaryEvent') && + canDrop(shape, flow.parent, position)); +} + +function includes(elements, element) { + return (elements && element) && elements.indexOf(element) !== -1; +} + +function canCopy(elements, element) { + if (isLabel(element)) { + return true; + } + + if (is(element, 'bpmn:Lane') && !includes(elements, element.parent)) { + return false; + } + + return true; +} + +function isOutgoingEventBasedGatewayConnection(connection) { + + if (connection && connection.source) { + return is(connection.source, 'bpmn:EventBasedGateway'); + } +} + +function areOutgoingEventBasedGatewayConnections(connections) { + connections = connections || []; + + return connections.some(isOutgoingEventBasedGatewayConnection); +} + +function getRootElement(element) { + return getParent(element, 'bpmn:Process') || getParent(element, 'bpmn:Collaboration'); +} diff --git a/lib/features/rules/index.js b/lib/features/rules/index.js new file mode 100644 index 0000000..2a5eeef --- /dev/null +++ b/lib/features/rules/index.js @@ -0,0 +1,11 @@ +import RulesModule from 'diagram-js/lib/features/rules'; + +import BpmnRules from './BpmnRules'; + +export default { + __depends__: [ + RulesModule + ], + __init__: [ 'bpmnRules' ], + bpmnRules: [ 'type', BpmnRules ] +}; diff --git a/lib/features/search/BpmnSearchProvider.js b/lib/features/search/BpmnSearchProvider.js new file mode 100644 index 0000000..d606f1a --- /dev/null +++ b/lib/features/search/BpmnSearchProvider.js @@ -0,0 +1,129 @@ +import { + map, + filter, + sortBy +} from 'min-dash'; + +import { + getLabel +} from '../label-editing/LabelUtil'; + + +/** + * Provides ability to search through BPMN elements + */ +export default function BpmnSearchProvider(elementRegistry, searchPad, canvas) { + + this._elementRegistry = elementRegistry; + this._canvas = canvas; + + searchPad.registerProvider(this); +} + +BpmnSearchProvider.$inject = [ + 'elementRegistry', + 'searchPad', + 'canvas' +]; + + +/** + * Finds all elements that match given pattern + * + * : + * { + * primaryTokens: >, + * secondaryTokens: >, + * element: + * } + * + * : + * { + * normal|matched: + * } + * + * @param {string} pattern + * @return {Array} + */ +BpmnSearchProvider.prototype.find = function(pattern) { + var rootElement = this._canvas.getRootElement(); + + var elements = this._elementRegistry.filter(function(element) { + if (element.labelTarget) { + return false; + } + return true; + }); + + // do not include root element + elements = filter(elements, function(element) { + return element !== rootElement; + }); + + elements = map(elements, function(element) { + return { + primaryTokens: matchAndSplit(getLabel(element), pattern), + secondaryTokens: matchAndSplit(element.id, pattern), + element: element + }; + }); + + // exclude non-matched elements + elements = filter(elements, function(element) { + return hasMatched(element.primaryTokens) || hasMatched(element.secondaryTokens); + }); + + elements = sortBy(elements, function(element) { + return getLabel(element.element) + element.element.id; + }); + + return elements; +}; + + +function hasMatched(tokens) { + var matched = filter(tokens, function(t) { + return !!t.matched; + }); + + return matched.length > 0; +} + + +function matchAndSplit(text, pattern) { + var tokens = [], + originalText = text; + + if (!text) { + return tokens; + } + + text = text.toLowerCase(); + pattern = pattern.toLowerCase(); + + var i = text.indexOf(pattern); + + if (i > -1) { + if (i !== 0) { + tokens.push({ + normal: originalText.substr(0, i) + }); + } + + tokens.push({ + matched: originalText.substr(i, pattern.length) + }); + + if (pattern.length + i < text.length) { + tokens.push({ + normal: originalText.substr(pattern.length + i, text.length) + }); + } + } else { + tokens.push({ + normal: originalText + }); + } + + return tokens; +} \ No newline at end of file diff --git a/lib/features/search/index.js b/lib/features/search/index.js new file mode 100644 index 0000000..b7050ae --- /dev/null +++ b/lib/features/search/index.js @@ -0,0 +1,12 @@ +import SearchPadModule from 'diagram-js/lib/features/search-pad'; + +import BpmnSearchProvider from './BpmnSearchProvider'; + + +export default { + __depends__: [ + SearchPadModule + ], + __init__: [ 'bpmnSearch' ], + bpmnSearch: [ 'type', BpmnSearchProvider ] +}; diff --git a/lib/features/snapping/BpmnConnectSnapping.js b/lib/features/snapping/BpmnConnectSnapping.js new file mode 100644 index 0000000..fd24bc1 --- /dev/null +++ b/lib/features/snapping/BpmnConnectSnapping.js @@ -0,0 +1,212 @@ +import { + mid, + setSnapped +} from 'diagram-js/lib/features/snapping/SnapUtil'; + +import { isCmd } from 'diagram-js/lib/features/keyboard/KeyboardUtil'; + +import { + getOrientation +} from 'diagram-js/lib/layout/LayoutUtil'; + +import { is } from '../../util/ModelUtil'; + +import { isAny } from '../modeling/util/ModelingUtil'; + +import { some } from 'min-dash'; + +var HIGHER_PRIORITY = 1250; + +var BOUNDARY_TO_HOST_THRESHOLD = 40; + +var TARGET_BOUNDS_PADDING = 20, + TASK_BOUNDS_PADDING = 10; + +var TARGET_CENTER_PADDING = 20; + +var AXES = [ 'x', 'y' ]; + +var abs = Math.abs; + +/** + * Snap during connect. + * + * @param {EventBus} eventBus + */ +export default function BpmnConnectSnapping(eventBus) { + eventBus.on([ + 'connect.hover', + 'connect.move', + 'connect.end', + ], HIGHER_PRIORITY, function(event) { + var context = event.context, + canExecute = context.canExecute, + start = context.start, + hover = context.hover, + source = context.source, + target = context.target; + + // do NOT snap on CMD + if (event.originalEvent && isCmd(event.originalEvent)) { + return; + } + + if (!context.initialConnectionStart) { + context.initialConnectionStart = context.connectionStart; + } + + // snap hover + if (canExecute && hover) { + snapToShape(event, hover, getTargetBoundsPadding(hover)); + } + + if (hover && isAnyType(canExecute, [ + 'bpmn:Association', + 'bpmn:DataInputAssociation', + 'bpmn:DataOutputAssociation', + 'bpmn:SequenceFlow' + ])) { + context.connectionStart = mid(start); + + // snap hover + if (isAny(hover, [ 'bpmn:Event', 'bpmn:Gateway' ])) { + snapToPosition(event, mid(hover)); + } + + // snap hover + if (isAny(hover, [ 'bpmn:Task', 'bpmn:SubProcess' ])) { + snapToTargetMid(event, hover); + } + + // snap source and target + if (is(source, 'bpmn:BoundaryEvent') && target === source.host) { + snapBoundaryEventLoop(event); + } + + } else if (isType(canExecute, 'bpmn:MessageFlow')) { + + if (is(start, 'bpmn:Event')) { + + // snap start + context.connectionStart = mid(start); + } + + if (is(hover, 'bpmn:Event')) { + + // snap hover + snapToPosition(event, mid(hover)); + } + + } else { + + // un-snap source + context.connectionStart = context.initialConnectionStart; + } + }); +} + +BpmnConnectSnapping.$inject = [ 'eventBus' ]; + + +// helpers ////////// + +// snap to target if event in target +function snapToShape(event, target, padding) { + AXES.forEach(function(axis) { + var dimensionForAxis = getDimensionForAxis(axis, target); + + if (event[ axis ] < target[ axis ] + padding) { + setSnapped(event, axis, target[ axis ] + padding); + } else if (event[ axis ] > target[ axis ] + dimensionForAxis - padding) { + setSnapped(event, axis, target[ axis ] + dimensionForAxis - padding); + } + }); +} + +// snap to target mid if event in target mid +function snapToTargetMid(event, target) { + var targetMid = mid(target); + + AXES.forEach(function(axis) { + if (isMid(event, target, axis)) { + setSnapped(event, axis, targetMid[ axis ]); + } + }); +} + +// snap to prevent loop overlapping boundary event +function snapBoundaryEventLoop(event) { + var context = event.context, + source = context.source, + target = context.target; + + if (isReverse(context)) { + return; + } + + var sourceMid = mid(source), + orientation = getOrientation(sourceMid, target, -10), + axes = []; + + if (/top|bottom/.test(orientation)) { + axes.push('x'); + } + + if (/left|right/.test(orientation)) { + axes.push('y'); + } + + axes.forEach(function(axis) { + var coordinate = event[ axis ], newCoordinate; + + if (abs(coordinate - sourceMid[ axis ]) < BOUNDARY_TO_HOST_THRESHOLD) { + if (coordinate > sourceMid[ axis ]) { + newCoordinate = sourceMid[ axis ] + BOUNDARY_TO_HOST_THRESHOLD; + } + else { + newCoordinate = sourceMid[ axis ] - BOUNDARY_TO_HOST_THRESHOLD; + } + + setSnapped(event, axis, newCoordinate); + } + }); +} + +function snapToPosition(event, position) { + setSnapped(event, 'x', position.x); + setSnapped(event, 'y', position.y); +} + +function isType(attrs, type) { + return attrs && attrs.type === type; +} + +function isAnyType(attrs, types) { + return some(types, function(type) { + return isType(attrs, type); + }); +} + +function getDimensionForAxis(axis, element) { + return axis === 'x' ? element.width : element.height; +} + +function getTargetBoundsPadding(target) { + if (is(target, 'bpmn:Task')) { + return TASK_BOUNDS_PADDING; + } else { + return TARGET_BOUNDS_PADDING; + } +} + +function isMid(event, target, axis) { + return event[ axis ] > target[ axis ] + TARGET_CENTER_PADDING + && event[ axis ] < target[ axis ] + getDimensionForAxis(axis, target) - TARGET_CENTER_PADDING; +} + +function isReverse(context) { + var hover = context.hover, + source = context.source; + + return hover && source && hover === source; +} \ No newline at end of file diff --git a/lib/features/snapping/BpmnCreateMoveSnapping.js b/lib/features/snapping/BpmnCreateMoveSnapping.js new file mode 100644 index 0000000..a36a248 --- /dev/null +++ b/lib/features/snapping/BpmnCreateMoveSnapping.js @@ -0,0 +1,243 @@ +import inherits from 'inherits-browser'; + +import CreateMoveSnapping from 'diagram-js/lib/features/snapping/CreateMoveSnapping'; + +import { + isSnapped, + setSnapped, + topLeft, + bottomRight +} from 'diagram-js/lib/features/snapping/SnapUtil'; + +import { isExpanded } from '../../util/DiUtil'; + +import { is } from '../../util/ModelUtil'; + +import { + asTRBL, + getMid +} from 'diagram-js/lib/layout/LayoutUtil'; + +import { getBoundaryAttachment } from './BpmnSnappingUtil'; + +import { forEach } from 'min-dash'; + +var HIGH_PRIORITY = 1500; + + +/** + * Snap during create and move. + * + * @param {EventBus} eventBus + * @param {Injector} injector + */ +export default function BpmnCreateMoveSnapping(eventBus, injector) { + injector.invoke(CreateMoveSnapping, this); + + // creating first participant + eventBus.on([ 'create.move', 'create.end' ], HIGH_PRIORITY, setSnappedIfConstrained); + + // snap boundary events + eventBus.on([ + 'create.move', + 'create.end', + 'shape.move.move', + 'shape.move.end' + ], HIGH_PRIORITY, function(event) { + var context = event.context, + canExecute = context.canExecute, + target = context.target; + + var canAttach = canExecute && (canExecute === 'attach' || canExecute.attach); + + if (canAttach && !isSnapped(event)) { + snapBoundaryEvent(event, target); + } + }); +} + +inherits(BpmnCreateMoveSnapping, CreateMoveSnapping); + +BpmnCreateMoveSnapping.$inject = [ + 'eventBus', + 'injector' +]; + +BpmnCreateMoveSnapping.prototype.initSnap = function(event) { + var snapContext = CreateMoveSnapping.prototype.initSnap.call(this, event); + + var shape = event.shape; + + var isMove = !!this._elementRegistry.get(shape.id); + + // snap to docking points + forEach(shape.outgoing, function(connection) { + var docking = connection.waypoints[0]; + + docking = docking.original || docking; + + snapContext.setSnapOrigin(connection.id + '-docking', getDockingSnapOrigin(docking, isMove, event)); + }); + + forEach(shape.incoming, function(connection) { + var docking = connection.waypoints[connection.waypoints.length - 1]; + + docking = docking.original || docking; + + snapContext.setSnapOrigin(connection.id + '-docking', getDockingSnapOrigin(docking, isMove, event)); + }); + + if (is(shape, 'bpmn:Participant')) { + + // snap to borders with higher priority + snapContext.setSnapLocations([ 'top-left', 'bottom-right', 'mid' ]); + } + + return snapContext; +}; + +BpmnCreateMoveSnapping.prototype.addSnapTargetPoints = function(snapPoints, shape, target) { + CreateMoveSnapping.prototype.addSnapTargetPoints.call(this, snapPoints, shape, target); + + var snapTargets = this.getSnapTargets(shape, target); + + forEach(snapTargets, function(snapTarget) { + + // handle TRBL alignment + // + // * with container elements + // * with text annotations + if (isContainer(snapTarget) || areAll([ shape, snapTarget ], 'bpmn:TextAnnotation')) { + snapPoints.add('top-left', topLeft(snapTarget)); + snapPoints.add('bottom-right', bottomRight(snapTarget)); + } + }); + + var elementRegistry = this._elementRegistry; + + // snap to docking points if not create mode + forEach(shape.incoming, function(connection) { + if (elementRegistry.get(shape.id)) { + + if (!includes(snapTargets, connection.source)) { + snapPoints.add('mid', getMid(connection.source)); + } + + var docking = connection.waypoints[0]; + snapPoints.add(connection.id + '-docking', docking.original || docking); + } + }); + + forEach(shape.outgoing, function(connection) { + if (elementRegistry.get(shape.id)) { + + if (!includes(snapTargets, connection.target)) { + snapPoints.add('mid', getMid(connection.target)); + } + + var docking = connection.waypoints[ connection.waypoints.length - 1 ]; + + snapPoints.add(connection.id + '-docking', docking.original || docking); + } + }); + + // add sequence flow parents as snap targets + if (is(target, 'bpmn:SequenceFlow')) { + snapPoints = this.addSnapTargetPoints(snapPoints, shape, target.parent); + } + + return snapPoints; +}; + +BpmnCreateMoveSnapping.prototype.getSnapTargets = function(shape, target) { + return CreateMoveSnapping.prototype.getSnapTargets.call(this, shape, target) + .filter(function(snapTarget) { + + // do not snap to lanes + return !is(snapTarget, 'bpmn:Lane'); + }); +}; + +// helpers ////////// + +function snapBoundaryEvent(event, target) { + var targetTRBL = asTRBL(target); + + var direction = getBoundaryAttachment(event, target); + + var context = event.context, + shape = context.shape; + + var offset; + + if (shape.parent) { + offset = { x: 0, y: 0 }; + } else { + offset = getMid(shape); + } + + if (/top/.test(direction)) { + setSnapped(event, 'y', targetTRBL.top - offset.y); + } else if (/bottom/.test(direction)) { + setSnapped(event, 'y', targetTRBL.bottom - offset.y); + } + + if (/left/.test(direction)) { + setSnapped(event, 'x', targetTRBL.left - offset.x); + } else if (/right/.test(direction)) { + setSnapped(event, 'x', targetTRBL.right - offset.x); + } +} + +function areAll(elements, type) { + return elements.every(function(el) { + return is(el, type); + }); +} + +function isContainer(element) { + if (is(element, 'bpmn:SubProcess') && isExpanded(element)) { + return true; + } + + return is(element, 'bpmn:Participant'); +} + + +function setSnappedIfConstrained(event) { + var context = event.context, + createConstraints = context.createConstraints; + + if (!createConstraints) { + return; + } + + var top = createConstraints.top, + right = createConstraints.right, + bottom = createConstraints.bottom, + left = createConstraints.left; + + if ((left && left >= event.x) || (right && right <= event.x)) { + setSnapped(event, 'x', event.x); + } + + if ((top && top >= event.y) || (bottom && bottom <= event.y)) { + setSnapped(event, 'y', event.y); + } +} + +function includes(array, value) { + return array.indexOf(value) !== -1; +} + +function getDockingSnapOrigin(docking, isMove, event) { + return isMove ? ( + { + x: docking.x - event.x, + y: docking.y - event.y + } + ) : { + x: docking.x, + y: docking.y + }; +} diff --git a/lib/features/snapping/BpmnSnappingUtil.js b/lib/features/snapping/BpmnSnappingUtil.js new file mode 100644 index 0000000..a88882b --- /dev/null +++ b/lib/features/snapping/BpmnSnappingUtil.js @@ -0,0 +1,12 @@ +import { getOrientation } from 'diagram-js/lib/layout/LayoutUtil'; + +export function getBoundaryAttachment(position, targetBounds) { + + var orientation = getOrientation(position, targetBounds, -15); + + if (orientation !== 'intersect') { + return orientation; + } else { + return null; + } +} \ No newline at end of file diff --git a/lib/features/snapping/index.js b/lib/features/snapping/index.js new file mode 100644 index 0000000..88acf99 --- /dev/null +++ b/lib/features/snapping/index.js @@ -0,0 +1,13 @@ +import BpmnConnectSnapping from './BpmnConnectSnapping'; +import BpmnCreateMoveSnapping from './BpmnCreateMoveSnapping'; +import SnappingModule from 'diagram-js/lib/features/snapping'; + +export default { + __depends__: [ SnappingModule ], + __init__: [ + 'connectSnapping', + 'createMoveSnapping' + ], + connectSnapping: [ 'type', BpmnConnectSnapping ], + createMoveSnapping: [ 'type', BpmnCreateMoveSnapping ] +}; \ No newline at end of file diff --git a/lib/import/BpmnImporter.js b/lib/import/BpmnImporter.js new file mode 100644 index 0000000..5894895 --- /dev/null +++ b/lib/import/BpmnImporter.js @@ -0,0 +1,347 @@ +import { + assign +} from 'min-dash'; + +import { is } from '../util/ModelUtil'; + +import { + isLabelExternal, + getExternalLabelBounds +} from '../util/LabelUtil'; + +import { + getMid +} from 'diagram-js/lib/layout/LayoutUtil'; + +import { + isExpanded +} from '../util/DiUtil'; + +import { + getLabel +} from '../features/label-editing/LabelUtil'; + +import { + elementToString +} from './Util'; + + +/** + * @param {ModdleElement} semantic + * @param {ModdleElement} di + * @param {Object} [attrs=null] + * + * @return {Object} + */ +function elementData(semantic, di, attrs) { + return assign({ + id: semantic.id, + type: semantic.$type, + businessObject: semantic, + di: di + }, attrs); +} + +function getWaypoints(di, source, target) { + + var waypoints = di.waypoint; + + if (!waypoints || waypoints.length < 2) { + return [ getMid(source), getMid(target) ]; + } + + return waypoints.map(function(p) { + return { x: p.x, y: p.y }; + }); +} + +function notYetDrawn(translate, semantic, refSemantic, property) { + return new Error(translate('element {element} referenced by {referenced}#{property} not yet drawn', { + element: elementToString(refSemantic), + referenced: elementToString(semantic), + property: property + })); +} + + +/** + * An importer that adds bpmn elements to the canvas + * + * @param {EventBus} eventBus + * @param {Canvas} canvas + * @param {ElementFactory} elementFactory + * @param {ElementRegistry} elementRegistry + * @param {Function} translate + * @param {TextRenderer} textRenderer + */ +export default function BpmnImporter( + eventBus, canvas, elementFactory, + elementRegistry, translate, textRenderer) { + + this._eventBus = eventBus; + this._canvas = canvas; + this._elementFactory = elementFactory; + this._elementRegistry = elementRegistry; + this._translate = translate; + this._textRenderer = textRenderer; +} + +BpmnImporter.$inject = [ + 'eventBus', + 'canvas', + 'elementFactory', + 'elementRegistry', + 'translate', + 'textRenderer' +]; + + +/** + * Add bpmn element (semantic) to the canvas onto the + * specified parent shape. + */ +BpmnImporter.prototype.add = function(semantic, di, parentElement) { + var element, + translate = this._translate, + hidden; + + var parentIndex; + + // ROOT ELEMENT + // handle the special case that we deal with a + // invisible root element (process, subprocess or collaboration) + if (is(di, 'bpmndi:BPMNPlane')) { + + var attrs = is(semantic, 'bpmn:SubProcess') + ? { id: semantic.id + '_plane' } + : {}; + + // add a virtual element (not being drawn) + element = this._elementFactory.createRoot(elementData(semantic, di, attrs)); + + this._canvas.addRootElement(element); + } + + // SHAPE + else if (is(di, 'bpmndi:BPMNShape')) { + + var collapsed = !isExpanded(semantic, di), + isFrame = isFrameElement(semantic); + + hidden = parentElement && (parentElement.hidden || parentElement.collapsed); + + var bounds = di.bounds; + + element = this._elementFactory.createShape(elementData(semantic, di, { + collapsed: collapsed, + hidden: hidden, + x: Math.round(bounds.x), + y: Math.round(bounds.y), + width: Math.round(bounds.width), + height: Math.round(bounds.height), + isFrame: isFrame + })); + + if (is(semantic, 'bpmn:BoundaryEvent')) { + this._attachBoundary(semantic, element); + } + + // insert lanes behind other flow nodes (cf. #727) + if (is(semantic, 'bpmn:Lane')) { + parentIndex = 0; + } + + if (is(semantic, 'bpmn:DataStoreReference')) { + + // check whether data store is inside our outside of its semantic parent + if (!isPointInsideBBox(parentElement, getMid(bounds))) { + parentElement = this._canvas.findRoot(parentElement); + } + } + + this._canvas.addShape(element, parentElement, parentIndex); + } + + // CONNECTION + else if (is(di, 'bpmndi:BPMNEdge')) { + + var source = this._getSource(semantic), + target = this._getTarget(semantic); + + hidden = parentElement && (parentElement.hidden || parentElement.collapsed); + + element = this._elementFactory.createConnection(elementData(semantic, di, { + hidden: hidden, + source: source, + target: target, + waypoints: getWaypoints(di, source, target) + })); + + if (is(semantic, 'bpmn:DataAssociation')) { + + // render always on top; this ensures DataAssociations + // are rendered correctly across different "hacks" people + // love to model such as cross participant / sub process + // associations + parentElement = this._canvas.findRoot(parentElement); + } + + this._canvas.addConnection(element, parentElement, parentIndex); + } else { + throw new Error(translate('unknown di {di} for element {semantic}', { + di: elementToString(di), + semantic: elementToString(semantic) + })); + } + + // (optional) LABEL + if (isLabelExternal(semantic) && getLabel(element)) { + this.addLabel(semantic, di, element); + } + + + this._eventBus.fire('bpmnElement.added', { element: element }); + + return element; +}; + + +/** + * Attach the boundary element to the given host + * + * @param {ModdleElement} boundarySemantic + * @param {djs.model.Base} boundaryElement + */ +BpmnImporter.prototype._attachBoundary = function(boundarySemantic, boundaryElement) { + var translate = this._translate; + var hostSemantic = boundarySemantic.attachedToRef; + + if (!hostSemantic) { + throw new Error(translate('missing {semantic}#attachedToRef', { + semantic: elementToString(boundarySemantic) + })); + } + + var host = this._elementRegistry.get(hostSemantic.id), + attachers = host && host.attachers; + + if (!host) { + throw notYetDrawn(translate, boundarySemantic, hostSemantic, 'attachedToRef'); + } + + // wire element.host <> host.attachers + boundaryElement.host = host; + + if (!attachers) { + host.attachers = attachers = []; + } + + if (attachers.indexOf(boundaryElement) === -1) { + attachers.push(boundaryElement); + } +}; + + +/** + * add label for an element + */ +BpmnImporter.prototype.addLabel = function(semantic, di, element) { + var bounds, + text, + label; + + bounds = getExternalLabelBounds(di, element); + + text = getLabel(element); + + if (text) { + + // get corrected bounds from actual layouted text + bounds = this._textRenderer.getExternalLabelBounds(bounds, text); + } + + label = this._elementFactory.createLabel(elementData(semantic, di, { + id: semantic.id + '_label', + labelTarget: element, + type: 'label', + hidden: element.hidden || !getLabel(element), + x: Math.round(bounds.x), + y: Math.round(bounds.y), + width: Math.round(bounds.width), + height: Math.round(bounds.height) + })); + + return this._canvas.addShape(label, element.parent); +}; + +/** + * Return the drawn connection end based on the given side. + * + * @throws {Error} if the end is not yet drawn + */ +BpmnImporter.prototype._getEnd = function(semantic, side) { + + var element, + refSemantic, + type = semantic.$type, + translate = this._translate; + + refSemantic = semantic[side + 'Ref']; + + // handle mysterious isMany DataAssociation#sourceRef + if (side === 'source' && type === 'bpmn:DataInputAssociation') { + refSemantic = refSemantic && refSemantic[0]; + } + + // fix source / target for DataInputAssociation / DataOutputAssociation + if (side === 'source' && type === 'bpmn:DataOutputAssociation' || + side === 'target' && type === 'bpmn:DataInputAssociation') { + + refSemantic = semantic.$parent; + } + + element = refSemantic && this._getElement(refSemantic); + + if (element) { + return element; + } + + if (refSemantic) { + throw notYetDrawn(translate, semantic, refSemantic, side + 'Ref'); + } else { + throw new Error(translate('{semantic}#{side} Ref not specified', { + semantic: elementToString(semantic), + side: side + })); + } +}; + +BpmnImporter.prototype._getSource = function(semantic) { + return this._getEnd(semantic, 'source'); +}; + +BpmnImporter.prototype._getTarget = function(semantic) { + return this._getEnd(semantic, 'target'); +}; + + +BpmnImporter.prototype._getElement = function(semantic) { + return this._elementRegistry.get(semantic.id); +}; + + +// helpers //////////////////// + +function isPointInsideBBox(bbox, point) { + var x = point.x, + y = point.y; + + return x >= bbox.x && + x <= bbox.x + bbox.width && + y >= bbox.y && + y <= bbox.y + bbox.height; +} + +function isFrameElement(semantic) { + return is(semantic, 'bpmn:Group'); +} \ No newline at end of file diff --git a/lib/import/BpmnTreeWalker.js b/lib/import/BpmnTreeWalker.js new file mode 100644 index 0000000..760f028 --- /dev/null +++ b/lib/import/BpmnTreeWalker.js @@ -0,0 +1,465 @@ +import { + filter, + find, + forEach +} from 'min-dash'; + +import { + elementToString +} from './Util'; + +import { + ensureCompatDiRef +} from '../util/CompatibilityUtil'; + + +/** + * Returns true if an element has the given meta-model type + * + * @param {ModdleElement} element + * @param {string} type + * + * @return {boolean} + */ +function is(element, type) { + return element.$instanceOf(type); +} + + +/** + * Find a suitable display candidate for definitions where the DI does not + * correctly specify one. + */ +function findDisplayCandidate(definitions) { + return find(definitions.rootElements, function(e) { + return is(e, 'bpmn:Process') || is(e, 'bpmn:Collaboration'); + }); +} + + +export default function BpmnTreeWalker(handler, translate) { + + // list of containers already walked + var handledElements = {}; + + // list of elements to handle deferred to ensure + // prerequisites are drawn + var deferred = []; + + var diMap = {}; + + // Helpers ////////////////////// + + function contextual(fn, ctx) { + return function(e) { + fn(e, ctx); + }; + } + + function handled(element) { + handledElements[element.id] = element; + } + + function isHandled(element) { + return handledElements[element.id]; + } + + function visit(element, ctx) { + + var gfx = element.gfx; + + // avoid multiple rendering of elements + if (gfx) { + throw new Error( + translate('already rendered {element}', { element: elementToString(element) }) + ); + } + + // call handler + return handler.element(element, diMap[element.id], ctx); + } + + function visitRoot(element, diagram) { + return handler.root(element, diMap[element.id], diagram); + } + + function visitIfDi(element, ctx) { + + try { + var gfx = diMap[element.id] && visit(element, ctx); + + handled(element); + + return gfx; + } catch (e) { + logError(e.message, { element: element, error: e }); + + console.error(translate('failed to import {element}', { element: elementToString(element) })); + console.error(e); + } + } + + function logError(message, context) { + handler.error(message, context); + } + + // DI handling ////////////////////// + + function registerDi(di) { + var bpmnElement = di.bpmnElement; + + if (bpmnElement) { + if (diMap[bpmnElement.id]) { + logError( + translate('multiple DI elements defined for {element}', { + element: elementToString(bpmnElement) + }), + { element: bpmnElement } + ); + } else { + diMap[bpmnElement.id] = di; + + ensureCompatDiRef(bpmnElement); + } + } else { + logError( + translate('no bpmnElement referenced in {element}', { + element: elementToString(di) + }), + { element: di } + ); + } + } + + function handleDiagram(diagram) { + handlePlane(diagram.plane); + } + + function handlePlane(plane) { + registerDi(plane); + + forEach(plane.planeElement, handlePlaneElement); + } + + function handlePlaneElement(planeElement) { + registerDi(planeElement); + } + + + // Semantic handling ////////////////////// + + /** + * Handle definitions and return the rendered diagram (if any) + * + * @param {ModdleElement} definitions to walk and import + * @param {ModdleElement} [diagram] specific diagram to import and display + * + * @throws {Error} if no diagram to display could be found + */ + function handleDefinitions(definitions, diagram) { + + // make sure we walk the correct bpmnElement + + var diagrams = definitions.diagrams; + + if (diagram && diagrams.indexOf(diagram) === -1) { + throw new Error(translate('diagram not part of bpmn:Definitions')); + } + + if (!diagram && diagrams && diagrams.length) { + diagram = diagrams[0]; + } + + // no diagram -> nothing to import + if (!diagram) { + throw new Error(translate('no diagram to display')); + } + + // load DI from selected diagram only + diMap = {}; + handleDiagram(diagram); + + + var plane = diagram.plane; + + if (!plane) { + throw new Error(translate( + 'no plane for {element}', + { element: elementToString(diagram) } + )); + } + + var rootElement = plane.bpmnElement; + + // ensure we default to a suitable display candidate (process or collaboration), + // even if non is specified in DI + if (!rootElement) { + rootElement = findDisplayCandidate(definitions); + + if (!rootElement) { + throw new Error(translate('no process or collaboration to display')); + } else { + + logError( + translate('correcting missing bpmnElement on {plane} to {rootElement}', { + plane: elementToString(plane), + rootElement: elementToString(rootElement) + }) + ); + + // correct DI on the fly + plane.bpmnElement = rootElement; + registerDi(plane); + } + } + + + var ctx = visitRoot(rootElement, plane); + + if (is(rootElement, 'bpmn:Process') || is(rootElement, 'bpmn:SubProcess')) { + handleProcess(rootElement, ctx); + } else if (is(rootElement, 'bpmn:Collaboration')) { + handleCollaboration(rootElement, ctx); + + // force drawing of everything not yet drawn that is part of the target DI + handleUnhandledProcesses(definitions.rootElements, ctx); + } else { + throw new Error( + translate('unsupported bpmnElement for {plane}: {rootElement}', { + plane: elementToString(plane), + rootElement: elementToString(rootElement) + }) + ); + } + + // handle all deferred elements + handleDeferred(deferred); + } + + function handleDeferred() { + + var fn; + + // drain deferred until empty + while (deferred.length) { + fn = deferred.shift(); + + fn(); + } + } + + function handleProcess(process, context) { + handleFlowElementsContainer(process, context); + handleIoSpecification(process.ioSpecification, context); + + handleArtifacts(process.artifacts, context); + + // log process handled + handled(process); + } + + function handleUnhandledProcesses(rootElements, ctx) { + + // walk through all processes that have not yet been drawn and draw them + // if they contain lanes with DI information. + // we do this to pass the free-floating lane test cases in the MIWG test suite + var processes = filter(rootElements, function(e) { + return !isHandled(e) && is(e, 'bpmn:Process') && e.laneSets; + }); + + processes.forEach(contextual(handleProcess, ctx)); + } + + function handleMessageFlow(messageFlow, context) { + visitIfDi(messageFlow, context); + } + + function handleMessageFlows(messageFlows, context) { + forEach(messageFlows, contextual(handleMessageFlow, context)); + } + + function handleDataAssociation(association, context) { + visitIfDi(association, context); + } + + function handleDataInput(dataInput, context) { + visitIfDi(dataInput, context); + } + + function handleDataOutput(dataOutput, context) { + visitIfDi(dataOutput, context); + } + + function handleArtifact(artifact, context) { + + // bpmn:TextAnnotation + // bpmn:Group + // bpmn:Association + + visitIfDi(artifact, context); + } + + function handleArtifacts(artifacts, context) { + + forEach(artifacts, function(e) { + if (is(e, 'bpmn:Association')) { + deferred.push(function() { + handleArtifact(e, context); + }); + } else { + handleArtifact(e, context); + } + }); + } + + function handleIoSpecification(ioSpecification, context) { + + if (!ioSpecification) { + return; + } + + forEach(ioSpecification.dataInputs, contextual(handleDataInput, context)); + forEach(ioSpecification.dataOutputs, contextual(handleDataOutput, context)); + } + + function handleSubProcess(subProcess, context) { + handleFlowElementsContainer(subProcess, context); + handleArtifacts(subProcess.artifacts, context); + } + + function handleFlowNode(flowNode, context) { + var childCtx = visitIfDi(flowNode, context); + + if (is(flowNode, 'bpmn:SubProcess')) { + handleSubProcess(flowNode, childCtx || context); + } + + if (is(flowNode, 'bpmn:Activity')) { + handleIoSpecification(flowNode.ioSpecification, context); + } + + // defer handling of associations + // affected types: + // + // * bpmn:Activity + // * bpmn:ThrowEvent + // * bpmn:CatchEvent + // + deferred.push(function() { + forEach(flowNode.dataInputAssociations, contextual(handleDataAssociation, context)); + forEach(flowNode.dataOutputAssociations, contextual(handleDataAssociation, context)); + }); + } + + function handleSequenceFlow(sequenceFlow, context) { + visitIfDi(sequenceFlow, context); + } + + function handleDataElement(dataObject, context) { + visitIfDi(dataObject, context); + } + + function handleLane(lane, context) { + + deferred.push(function() { + + var newContext = visitIfDi(lane, context); + + if (lane.childLaneSet) { + handleLaneSet(lane.childLaneSet, newContext || context); + } + + wireFlowNodeRefs(lane); + }); + } + + function handleLaneSet(laneSet, context) { + forEach(laneSet.lanes, contextual(handleLane, context)); + } + + function handleLaneSets(laneSets, context) { + forEach(laneSets, contextual(handleLaneSet, context)); + } + + function handleFlowElementsContainer(container, context) { + handleFlowElements(container.flowElements, context); + + if (container.laneSets) { + handleLaneSets(container.laneSets, context); + } + } + + function handleFlowElements(flowElements, context) { + forEach(flowElements, function(e) { + if (is(e, 'bpmn:SequenceFlow')) { + deferred.push(function() { + handleSequenceFlow(e, context); + }); + } else if (is(e, 'bpmn:BoundaryEvent')) { + deferred.unshift(function() { + handleFlowNode(e, context); + }); + } else if (is(e, 'bpmn:FlowNode')) { + handleFlowNode(e, context); + } else if (is(e, 'bpmn:DataObject')) { + + // SKIP (assume correct referencing via DataObjectReference) + } else if (is(e, 'bpmn:DataStoreReference')) { + handleDataElement(e, context); + } else if (is(e, 'bpmn:DataObjectReference')) { + handleDataElement(e, context); + } else { + logError( + translate('unrecognized flowElement {element} in context {context}', { + element: elementToString(e), + context: (context ? elementToString(context.businessObject) : 'null') + }), + { element: e, context: context } + ); + } + }); + } + + function handleParticipant(participant, context) { + var newCtx = visitIfDi(participant, context); + + var process = participant.processRef; + if (process) { + handleProcess(process, newCtx || context); + } + } + + function handleCollaboration(collaboration, context) { + + forEach(collaboration.participants, contextual(handleParticipant, context)); + + handleArtifacts(collaboration.artifacts, context); + + // handle message flows latest in the process + deferred.push(function() { + handleMessageFlows(collaboration.messageFlows, context); + }); + } + + + function wireFlowNodeRefs(lane) { + + // wire the virtual flowNodeRefs <-> relationship + forEach(lane.flowNodeRef, function(flowNode) { + var lanes = flowNode.get('lanes'); + + if (lanes) { + lanes.push(lane); + } + }); + } + + // API ////////////////////// + + return { + handleDeferred: handleDeferred, + handleDefinitions: handleDefinitions, + handleSubProcess: handleSubProcess, + registerDi: registerDi + }; +} \ No newline at end of file diff --git a/lib/import/Importer.js b/lib/import/Importer.js new file mode 100644 index 0000000..af1552c --- /dev/null +++ b/lib/import/Importer.js @@ -0,0 +1,225 @@ +import { + find, + forEach, + map +} from 'min-dash'; + +import BpmnTreeWalker from './BpmnTreeWalker'; + +import { is } from '../util/ModelUtil'; + + +/** + * The importBpmnDiagram result. + * + * @typedef {Object} ImportBPMNDiagramResult + * + * @property {Array} warnings + */ + +/** +* The importBpmnDiagram error. +* +* @typedef {Error} ImportBPMNDiagramError +* +* @property {Array} warnings +*/ + +/** + * Import the definitions into a diagram. + * + * Errors and warnings are reported through the specified callback. + * + * @param {djs.Diagram} diagram + * @param {ModdleElement} definitions + * @param {ModdleElement} [bpmnDiagram] the diagram to be rendered + * (if not provided, the first one will be rendered) + * + * Returns {Promise} + */ +export function importBpmnDiagram(diagram, definitions, bpmnDiagram) { + + var importer, + eventBus, + translate, + canvas; + + var error, + warnings = []; + + /** + * Walk the diagram semantically, importing (=drawing) + * all elements you encounter. + * + * @param {ModdleElement} definitions + * @param {ModdleElement} bpmnDiagram + */ + function render(definitions, bpmnDiagram) { + + var visitor = { + + root: function(element, di) { + return importer.add(element, di); + }, + + element: function(element, di, parentShape) { + return importer.add(element, di, parentShape); + }, + + error: function(message, context) { + warnings.push({ message: message, context: context }); + } + }; + + var walker = new BpmnTreeWalker(visitor, translate); + + + bpmnDiagram = bpmnDiagram || (definitions.diagrams && definitions.diagrams[0]); + + var diagramsToImport = getDiagramsToImport(definitions, bpmnDiagram); + + if (!diagramsToImport) { + throw new Error(translate('no diagram to display')); + } + + // traverse BPMN 2.0 document model, + // starting at definitions + forEach(diagramsToImport, function(diagram) { + walker.handleDefinitions(definitions, diagram); + }); + + var rootId = bpmnDiagram.plane.bpmnElement.id; + + // we do need to account for different ways we create root elements + // each nested imported do have the `_plane` suffix, while + // the root is found under the business object ID + canvas.setRootElement( + canvas.findRoot(rootId + '_plane') || canvas.findRoot(rootId) + ); + } + + return new Promise(function(resolve, reject) { + try { + importer = diagram.get('bpmnImporter'); + eventBus = diagram.get('eventBus'); + translate = diagram.get('translate'); + canvas = diagram.get('canvas'); + + eventBus.fire('import.render.start', { definitions: definitions }); + + render(definitions, bpmnDiagram); + + eventBus.fire('import.render.complete', { + error: error, + warnings: warnings + }); + + return resolve({ warnings: warnings }); + } catch (e) { + + e.warnings = warnings; + return reject(e); + } + }); +} + +/** + * Returns all diagrams in the same hierarchy as the requested diagram. + * Includes all parent and sub process diagrams. + * + * @param {Array} definitions + * @param {Object} bpmnDiagram + * + * @returns {Array} + */ +function getDiagramsToImport(definitions, bpmnDiagram) { + if (!bpmnDiagram) { + return; + } + + var bpmnElement = bpmnDiagram.plane.bpmnElement, + rootElement = bpmnElement; + + if (!is(bpmnElement, 'bpmn:Process') && !is(bpmnElement, 'bpmn:Collaboration')) { + rootElement = findRootProcess(bpmnElement); + } + + // in case the process is part of a collaboration, the plane references the + // collaboration, not the process + var collaboration; + + if (is(rootElement, 'bpmn:Collaboration')) { + collaboration = rootElement; + } else { + collaboration = find(definitions.rootElements, function(element) { + if (!is(element, 'bpmn:Collaboration')) { + return; + } + + return find(element.participants, function(participant) { + return participant.processRef === rootElement; + }); + }); + } + + var rootElements = [ rootElement ]; + + // all collaboration processes can contain sub-diagrams + if (collaboration) { + rootElements = map(collaboration.participants, function(participant) { + return participant.processRef; + }); + + rootElements.push(collaboration); + } + + var allChildren = selfAndAllFlowElements(rootElements); + + // if we have multiple diagrams referencing the same element, we + // use the first in the file + var diagramsToImport = [ bpmnDiagram ]; + var handledElements = [ bpmnElement ]; + + forEach(definitions.diagrams, function(diagram) { + var businessObject = diagram.plane.bpmnElement; + + if ( + allChildren.indexOf(businessObject) !== -1 && + handledElements.indexOf(businessObject) === -1 + ) { + diagramsToImport.push(diagram); + handledElements.push(businessObject); + } + }); + + + return diagramsToImport; +} + +function selfAndAllFlowElements(elements) { + var result = []; + + forEach(elements, function(element) { + if (!element) { + return; + } + + result.push(element); + + result = result.concat(selfAndAllFlowElements(element.flowElements)); + }); + + return result; +} + +function findRootProcess(element) { + var parent = element; + + while (parent) { + if (is(parent, 'bpmn:Process')) { + return parent; + } + + parent = parent.$parent; + } +} \ No newline at end of file diff --git a/lib/import/Util.js b/lib/import/Util.js new file mode 100644 index 0000000..0eef1f4 --- /dev/null +++ b/lib/import/Util.js @@ -0,0 +1,7 @@ +export function elementToString(e) { + if (!e) { + return ''; + } + + return '<' + e.$type + (e.id ? ' id="' + e.id : '') + '" />'; +} \ No newline at end of file diff --git a/lib/import/index.js b/lib/import/index.js new file mode 100644 index 0000000..7739e3e --- /dev/null +++ b/lib/import/index.js @@ -0,0 +1,10 @@ +import translate from 'diagram-js/lib/i18n/translate'; + +import BpmnImporter from './BpmnImporter'; + +export default { + __depends__: [ + translate + ], + bpmnImporter: [ 'type', BpmnImporter ] +}; \ No newline at end of file diff --git a/lib/util/CompatibilityUtil.js b/lib/util/CompatibilityUtil.js new file mode 100644 index 0000000..fdc8635 --- /dev/null +++ b/lib/util/CompatibilityUtil.js @@ -0,0 +1,74 @@ +import { + has, + isFunction +} from 'min-dash'; + +// TODO(nikku): remove with future bpmn-js version + +/** + * Wraps APIs to check: + * + * 1) If a callback is passed -> Warn users about callback deprecation. + * 2) If Promise class is implemented in current environment. + * + * @private + */ +export function wrapForCompatibility(api) { + + return function() { + + if (!window.Promise) { + throw new Error('Promises is not supported in this environment. Please polyfill Promise.'); + } + + var argLen = arguments.length; + if (argLen >= 1 && isFunction(arguments[argLen - 1])) { + + var callback = arguments[argLen - 1]; + + console.warn(new Error( + 'Passing callbacks to ' + api.name + ' is deprecated and will be removed in a future major release. ' + + 'Please switch to promises: https://bpmn.io/l/moving-to-promises.html' + )); + + var argsWithoutCallback = Array.prototype.slice.call(arguments, 0, -1); + + api.apply(this, argsWithoutCallback).then(function(result) { + + var firstKey = Object.keys(result)[0]; + + // The APIs we are wrapping all resolve a single item depending on the API. + // For instance, importXML resolves { warnings } and saveXML returns { xml }. + // That's why we can call the callback with the first item of result. + return callback(null, result[firstKey]); + + // Passing a second paramter instead of catch because we don't want to + // catch errors thrown by callback(). + }, function(err) { + + return callback(err, err.warnings); + }); + } else { + + return api.apply(this, arguments); + } + }; +} + + +// TODO(nikku): remove with future bpmn-js version + +var DI_ERROR_MESSAGE = 'Tried to access di from the businessObject. The di is available through the diagram element only. For more information, see https://github.com/bpmn-io/bpmn-js/issues/1472'; + +export function ensureCompatDiRef(businessObject) { + + // bpmnElement can have multiple independent DIs + if (!has(businessObject, 'di')) { + Object.defineProperty(businessObject, 'di', { + enumerable: false, + get: function() { + throw new Error(DI_ERROR_MESSAGE); + } + }); + } +} \ No newline at end of file diff --git a/lib/util/DiUtil.js b/lib/util/DiUtil.js new file mode 100644 index 0000000..b35db88 --- /dev/null +++ b/lib/util/DiUtil.js @@ -0,0 +1,68 @@ +import { + is, + getBusinessObject, + getDi +} from './ModelUtil'; + +import { + forEach +} from 'min-dash'; + + +export function isExpanded(element, di) { + + if (is(element, 'bpmn:CallActivity')) { + return false; + } + + if (is(element, 'bpmn:SubProcess')) { + di = di || getDi(element); + + if (di && is(di, 'bpmndi:BPMNPlane')) { + return true; + } + + return di && !!di.isExpanded; + } + + if (is(element, 'bpmn:Participant')) { + return !!getBusinessObject(element).processRef; + } + + return true; +} + +export function isInterrupting(element) { + return element && getBusinessObject(element).isInterrupting !== false; +} + +export function isEventSubProcess(element) { + return element && !!getBusinessObject(element).triggeredByEvent; +} + +export function hasEventDefinition(element, eventType) { + var bo = getBusinessObject(element), + hasEventDefinition = false; + + if (bo.eventDefinitions) { + forEach(bo.eventDefinitions, function(event) { + if (is(event, eventType)) { + hasEventDefinition = true; + } + }); + } + + return hasEventDefinition; +} + +export function hasErrorEventDefinition(element) { + return hasEventDefinition(element, 'bpmn:ErrorEventDefinition'); +} + +export function hasEscalationEventDefinition(element) { + return hasEventDefinition(element, 'bpmn:EscalationEventDefinition'); +} + +export function hasCompensateEventDefinition(element) { + return hasEventDefinition(element, 'bpmn:CompensateEventDefinition'); +} diff --git a/lib/util/DrilldownUtil.js b/lib/util/DrilldownUtil.js new file mode 100644 index 0000000..896ff25 --- /dev/null +++ b/lib/util/DrilldownUtil.js @@ -0,0 +1,66 @@ +import { getDi, is } from './ModelUtil'; + + +export var planeSuffix = '_plane'; + +/** + * Get primary shape ID for a plane. + * + * @param {djs.model.Base|ModdleElement} element + * + * @returns {String} + */ +export function getShapeIdFromPlane(element) { + var id = element.id; + + return removePlaneSuffix(id); +} + +/** + * Get plane ID for a primary shape. + * + * @param {djs.model.Base|ModdleElement} element + * + * @returns {String} + */ +export function getPlaneIdFromShape(element) { + var id = element.id; + + if (is(element, 'bpmn:SubProcess')) { + return addPlaneSuffix(id); + } + + return id; +} + +/** + * Get plane ID for primary shape ID. + * + * @param {String} id + * + * @returns {String} + */ +export function toPlaneId(id) { + return addPlaneSuffix(id); +} + +/** + * Check wether element is plane. + * + * @param {djs.model.Base|ModdleElement} element + * + * @returns {Boolean} + */ +export function isPlane(element) { + var di = getDi(element); + + return is(di, 'bpmndi:BPMNPlane'); +} + +function addPlaneSuffix(id) { + return id + planeSuffix; +} + +function removePlaneSuffix(id) { + return id.replace(new RegExp(planeSuffix + '$'), ''); +} \ No newline at end of file diff --git a/lib/util/LabelUtil.js b/lib/util/LabelUtil.js new file mode 100644 index 0000000..0f0fae6 --- /dev/null +++ b/lib/util/LabelUtil.js @@ -0,0 +1,156 @@ +import { + assign +} from 'min-dash'; + +import { is } from './ModelUtil'; + + +export var DEFAULT_LABEL_SIZE = { + width: 90, + height: 20 +}; + +export var FLOW_LABEL_INDENT = 15; + + +/** + * Returns true if the given semantic has an external label + * + * @param {BpmnElement} semantic + * @return {boolean} true if has label + */ +export function isLabelExternal(semantic) { + return is(semantic, 'bpmn:Event') || + is(semantic, 'bpmn:Gateway') || + is(semantic, 'bpmn:DataStoreReference') || + is(semantic, 'bpmn:DataObjectReference') || + is(semantic, 'bpmn:DataInput') || + is(semantic, 'bpmn:DataOutput') || + is(semantic, 'bpmn:SequenceFlow') || + is(semantic, 'bpmn:MessageFlow') || + is(semantic, 'bpmn:Group'); +} + +/** + * Returns true if the given element has an external label + * + * @param {djs.model.shape} element + * @return {boolean} true if has label + */ +export function hasExternalLabel(element) { + return isLabel(element.label); +} + +/** + * Get the position for sequence flow labels + * + * @param {Array} waypoints + * @return {Point} the label position + */ +export function getFlowLabelPosition(waypoints) { + + // get the waypoints mid + var mid = waypoints.length / 2 - 1; + + var first = waypoints[Math.floor(mid)]; + var second = waypoints[Math.ceil(mid + 0.01)]; + + // get position + var position = getWaypointsMid(waypoints); + + // calculate angle + var angle = Math.atan((second.y - first.y) / (second.x - first.x)); + + var x = position.x, + y = position.y; + + if (Math.abs(angle) < Math.PI / 2) { + y -= FLOW_LABEL_INDENT; + } else { + x += FLOW_LABEL_INDENT; + } + + return { x: x, y: y }; +} + + +/** + * Get the middle of a number of waypoints + * + * @param {Array} waypoints + * @return {Point} the mid point + */ +export function getWaypointsMid(waypoints) { + + var mid = waypoints.length / 2 - 1; + + var first = waypoints[Math.floor(mid)]; + var second = waypoints[Math.ceil(mid + 0.01)]; + + return { + x: first.x + (second.x - first.x) / 2, + y: first.y + (second.y - first.y) / 2 + }; +} + + +export function getExternalLabelMid(element) { + + if (element.waypoints) { + return getFlowLabelPosition(element.waypoints); + } else if (is(element, 'bpmn:Group')) { + return { + x: element.x + element.width / 2, + y: element.y + DEFAULT_LABEL_SIZE.height / 2 + }; + } else { + return { + x: element.x + element.width / 2, + y: element.y + element.height + DEFAULT_LABEL_SIZE.height / 2 + }; + } +} + + +/** + * Returns the bounds of an elements label, parsed from the elements DI or + * generated from its bounds. + * + * @param {BpmndDi} di + * @param {djs.model.Base} element + */ +export function getExternalLabelBounds(di, element) { + + var mid, + size, + bounds, + label = di.label; + + if (label && label.bounds) { + bounds = label.bounds; + + size = { + width: Math.max(DEFAULT_LABEL_SIZE.width, bounds.width), + height: bounds.height + }; + + mid = { + x: bounds.x + bounds.width / 2, + y: bounds.y + bounds.height / 2 + }; + } else { + + mid = getExternalLabelMid(element); + + size = DEFAULT_LABEL_SIZE; + } + + return assign({ + x: mid.x - size.width / 2, + y: mid.y - size.height / 2 + }, size); +} + +export function isLabel(element) { + return element && !!element.labelTarget; +} diff --git a/lib/util/ModelUtil.js b/lib/util/ModelUtil.js new file mode 100644 index 0000000..3a75fc4 --- /dev/null +++ b/lib/util/ModelUtil.js @@ -0,0 +1,55 @@ +import { + some +} from 'min-dash'; + + +/** + * Is an element of the given BPMN type? + * + * @param {djs.model.Base|ModdleElement} element + * @param {string} type + * + * @return {boolean} + */ +export function is(element, type) { + var bo = getBusinessObject(element); + + return bo && (typeof bo.$instanceOf === 'function') && bo.$instanceOf(type); +} + + +/** + * Return true if element has any of the given types. + * + * @param {djs.model.Base} element + * @param {Array} types + * + * @return {boolean} + */ +export function isAny(element, types) { + return some(types, function(t) { + return is(element, t); + }); +} + +/** + * Return the business object for a given element. + * + * @param {djs.model.Base|ModdleElement} element + * + * @return {ModdleElement} + */ +export function getBusinessObject(element) { + return (element && element.businessObject) || element; +} + +/** + * Return the di object for a given element. + * + * @param {djs.model.Base} element + * + * @return {ModdleElement} + */ +export function getDi(element) { + return element && element.di; +} \ No newline at end of file diff --git a/lib/util/PoweredByUtil.js b/lib/util/PoweredByUtil.js new file mode 100644 index 0000000..5a541a8 --- /dev/null +++ b/lib/util/PoweredByUtil.js @@ -0,0 +1,99 @@ +/** + * This file must not be changed or exchanged. + * + * @see http://bpmn.io/license for more information. + */ + +import { + assignStyle, + domify, + delegate as domDelegate, + query as domQuery +} from 'min-dom'; + + +// inlined ../../resources/logo.svg +var BPMNIO_LOGO_SVG = ''; + +export var BPMNIO_IMG = BPMNIO_LOGO_SVG; + +export var LOGO_STYLES = { + verticalAlign: 'middle' +}; + +export var LINK_STYLES = { + 'color': '#404040' +}; + +var LIGHTBOX_STYLES = { + 'zIndex': '1001', + 'position': 'fixed', + 'top': '0', + 'left': '0', + 'right': '0', + 'bottom': '0' +}; + +var BACKDROP_STYLES = { + 'width': '100%', + 'height': '100%', + 'background': 'rgba(40,40,40,0.2)' +}; + +var NOTICE_STYLES = { + 'position': 'absolute', + 'left': '50%', + 'top': '40%', + 'transform': 'translate(-50%)', + 'width': '260px', + 'padding': '10px', + 'background': 'white', + 'boxShadow': '0 1px 4px rgba(0,0,0,0.3)', + 'fontFamily': 'Helvetica, Arial, sans-serif', + 'fontSize': '14px', + 'display': 'flex', + 'lineHeight': '1.3' +}; + +var LIGHTBOX_MARKUP = + '
                ' + + '
                ' + + '
                ' + + '' + + BPMNIO_IMG + + '' + + '' + + 'Web-based tooling for BPMN, DMN and CMMN diagrams ' + + 'powered by bpmn.io.' + + '' + + '
                ' + + '
                '; + + +var lightbox; + +function createLightbox() { + lightbox = domify(LIGHTBOX_MARKUP); + + assignStyle(lightbox, LIGHTBOX_STYLES); + assignStyle(domQuery('svg', lightbox), LOGO_STYLES); + assignStyle(domQuery('.backdrop', lightbox), BACKDROP_STYLES); + assignStyle(domQuery('.notice', lightbox), NOTICE_STYLES); + assignStyle(domQuery('.link', lightbox), LINK_STYLES, { + 'margin': '15px 20px 15px 10px', + 'alignSelf': 'center' + }); +} + +export function open() { + + if (!lightbox) { + createLightbox(); + + domDelegate.bind(lightbox, '.backdrop', 'click', function(event) { + document.body.removeChild(lightbox); + }); + } + + document.body.appendChild(lightbox); +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..10cd657 --- /dev/null +++ b/package.json @@ -0,0 +1,109 @@ +{ + "name": "bpmn-js", + "version": "9.4.0", + "description": "A bpmn 2.0 toolkit and web modeler", + "main": "index.js", + "files": [ + "dist", + "lib", + "test/util", + "test/helper", + "test/matchers", + "!.eslintrc" + ], + "scripts": { + "all": "run-s lint test distro test:distro", + "lint": "eslint .", + "start": "cross-env SINGLE_START=modeler npm run dev", + "start:viewer": "cross-env SINGLE_START=viewer npm run dev", + "start:navigated-viewer": "cross-env SINGLE_START=navigated-viewer npm run dev", + "dev": "npm test -- --auto-watch --no-single-run", + "test": "karma start test/config/karma.unit.js", + "distro": "node tasks/build-distro.js", + "collect-translations": "cross-env COLLECT_TRANSLATIONS=1 npm test", + "test:distro": "node tasks/test-distro.js", + "postversion": "run-s distro test:distro", + "prepare": "run-s distro" + }, + "repository": { + "type": "git", + "url": "https://github.com/bpmn-io/bpmn-js" + }, + "keywords": [ + "bpmn", + "bpmn-js", + "toolkit", + "web modeler", + "modeler", + "modeling", + "process modeling" + ], + "author": { + "name": "Nico Rehwaldt", + "url": "https://github.com/nikku" + }, + "contributors": [ + { + "name": "bpmn.io contributors", + "url": "https://github.com/bpmn-io" + } + ], + "license": "SEE LICENSE IN LICENSE", + "sideEffects": [ + "*.css" + ], + "devDependencies": { + "@babel/core": "^7.18.10", + "@rollup/plugin-commonjs": "^22.0.2", + "@rollup/plugin-node-resolve": "^13.3.0", + "babel-loader": "^8.2.5", + "babel-plugin-istanbul": "^6.1.1", + "bpmn-font": "^0.10.0", + "camunda-bpmn-moddle": "^4.0.1", + "chai": "4.1.2", + "chai-match": "^1.1.1", + "cpx": "^1.5.0", + "cross-env": "^7.0.3", + "del": "^6.0.0", + "eslint": "^8.22.0", + "eslint-plugin-bpmn-io": "^0.14.1", + "eslint-plugin-import": "^2.26.0", + "execa": "^5.1.1", + "karma": "^6.4.0", + "karma-chrome-launcher": "^3.1.1", + "karma-coverage": "^2.2.0", + "karma-debug-launcher": "0.0.4", + "karma-env-preprocessor": "^0.1.1", + "karma-firefox-launcher": "^2.1.2", + "karma-mocha": "^2.0.1", + "karma-safari-launcher": "^1.0.0", + "karma-sinon-chai": "^2.0.2", + "karma-webpack": "^5.0.0", + "mkdirp": "^0.5.1", + "mocha": "^8.4.0", + "mocha-test-container-support": "0.2.0", + "npm-run-all": "^4.1.2", + "promise-polyfill": "^8.2.0", + "puppeteer": "^16.1.1", + "rollup": "^2.78.0", + "rollup-plugin-json": "^4.0.0", + "rollup-plugin-license": "^2.8.1", + "rollup-plugin-replace": "^2.2.0", + "rollup-plugin-terser": "^7.0.2", + "sinon": "^7.5.0", + "sinon-chai": "^3.7.0", + "webpack": "^5.74.0" + }, + "dependencies": { + "bpmn-moddle": "^7.1.3", + "css.escape": "^1.5.1", + "diagram-js": "^8.9.0", + "diagram-js-direct-editing": "^1.7.0", + "ids": "^1.0.0", + "inherits-browser": "0.0.1", + "min-dash": "^3.5.2", + "min-dom": "^3.2.1", + "object-refs": "^0.3.0", + "tiny-svg": "^2.2.4" + } +}