@ -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. |
@ -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). |
After Width: | Height: | Size: 130 KiB |
@ -0,0 +1,144 @@ |
|||||||
|
.bjs-container { |
||||||
|
--bjs-font-family: Arial, sans-serif; |
||||||
|
|
||||||
|
--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-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-05: hsla(0, 0%, 0%, 5%); |
||||||
|
--color-black-opacity-10: hsla(0, 0%, 0%, 10%); |
||||||
|
|
||||||
|
--breadcrumbs-font-family: var(--bjs-font-family); |
||||||
|
--breadcrumbs-item-color: var(--color-blue-205-100-50); |
||||||
|
--breadcrumbs-arrow-color: var(--color-black); |
||||||
|
--drilldown-fill-color: var(--color-white); |
||||||
|
--drilldown-background-color: var(--color-blue-205-100-50); |
||||||
|
} |
||||||
|
|
||||||
|
.bjs-breadcrumbs { |
||||||
|
position: absolute; |
||||||
|
display: none; |
||||||
|
flex-wrap: wrap; |
||||||
|
align-items: center; |
||||||
|
top: 30px; |
||||||
|
left: 30px; |
||||||
|
padding: 0px; |
||||||
|
margin: 0px; |
||||||
|
font-family: var(--breadcrumbs-font-family); |
||||||
|
font-size: 16px; |
||||||
|
line-height: normal; |
||||||
|
} |
||||||
|
|
||||||
|
.bjs-breadcrumbs-shown .bjs-breadcrumbs { |
||||||
|
display: flex; |
||||||
|
} |
||||||
|
|
||||||
|
.djs-palette-shown .bjs-breadcrumbs { |
||||||
|
left: 90px; |
||||||
|
} |
||||||
|
|
||||||
|
.djs-palette-shown.djs-palette-two-column .bjs-breadcrumbs { |
||||||
|
left: 140px; |
||||||
|
} |
||||||
|
|
||||||
|
.bjs-breadcrumbs li { |
||||||
|
display: inline-flex; |
||||||
|
padding-bottom: 5px; |
||||||
|
} |
||||||
|
|
||||||
|
.bjs-breadcrumbs li a { |
||||||
|
cursor: pointer; |
||||||
|
color: var(--breadcrumbs-item-color); |
||||||
|
} |
||||||
|
|
||||||
|
.bjs-breadcrumbs li:last-of-type a { |
||||||
|
color: inherit; |
||||||
|
cursor: default; |
||||||
|
} |
||||||
|
|
||||||
|
.bjs-breadcrumbs li:not(:first-child)::before { |
||||||
|
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="17" height="17" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" /><path d="M0 0h24v24H0z" fill="none" /></svg>'); |
||||||
|
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; |
||||||
|
} |
@ -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; |
||||||
|
} |
@ -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<didi.Module>} [options.modules] a list of modules to override the default modules |
||||||
|
* @param {Array<didi.Module>} [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]); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,786 @@ |
|||||||
|
/** |
||||||
|
* The code in the <project-logo></project-logo> 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<didi.Module>} [options.modules] a list of modules to override the default modules |
||||||
|
* @param {Array<didi.Module>} [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); |
||||||
|
|
||||||
|
/* <project-logo> */ |
||||||
|
|
||||||
|
addProjectLogo(this._container); |
||||||
|
|
||||||
|
/* </project-logo> */ |
||||||
|
|
||||||
|
this._init(this._container, this._moddle, options); |
||||||
|
} |
||||||
|
|
||||||
|
inherits(BaseViewer, Diagram); |
||||||
|
|
||||||
|
/** |
||||||
|
* The importXML result. |
||||||
|
* |
||||||
|
* @typedef {Object} ImportXMLResult |
||||||
|
* |
||||||
|
* @property {Array<string>} warnings |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* The importXML error. |
||||||
|
* |
||||||
|
* @typedef {Error} ImportXMLError |
||||||
|
* |
||||||
|
* @property {Array<string>} 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<BPMNDiagram>|string} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered) |
||||||
|
* |
||||||
|
* Returns {Promise<ImportXMLResult, ImportXMLError>} |
||||||
|
*/ |
||||||
|
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 <context> 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<string>} warnings |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* The importDefinitions error. |
||||||
|
* |
||||||
|
* @typedef {Error} ImportDefinitionsError |
||||||
|
* |
||||||
|
* @property {Array<string>} 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>} definitions parsed BPMN 2.0 definitions |
||||||
|
* @param {ModdleElement<BPMNDiagram>|string} [bpmnDiagram] BPMN diagram or id of diagram to render (if not provided, the first one will be rendered) |
||||||
|
* |
||||||
|
* Returns {Promise<ImportDefinitionsResult, ImportDefinitionsError>} |
||||||
|
*/ |
||||||
|
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<string>} warnings |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* The open error. |
||||||
|
* |
||||||
|
* @typedef {Error} OpenError |
||||||
|
* |
||||||
|
* @property {Array<string>} 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<BPMNDiagram>} [bpmnDiagramOrId] id or the diagram to open |
||||||
|
* |
||||||
|
* Returns {Promise<OpenResult, OpenError>} |
||||||
|
*/ |
||||||
|
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<SaveXMLResult, Error>} |
||||||
|
*/ |
||||||
|
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<SaveSVGResult, Error>} |
||||||
|
*/ |
||||||
|
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 ? '<defs>' + innerSVG(defsNode) + '</defs>' : ''; |
||||||
|
|
||||||
|
var bbox = contentNode.getBBox(); |
||||||
|
|
||||||
|
svg = |
||||||
|
'<?xml version="1.0" encoding="utf-8"?>\n' + |
||||||
|
'<!-- created with bpmn-js / http://bpmn.io -->\n' + |
||||||
|
'<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n' + |
||||||
|
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ' + |
||||||
|
'width="' + bbox.width + '" height="' + bbox.height + '" ' + |
||||||
|
'viewBox="' + bbox.x + ' ' + bbox.y + ' ' + bbox.width + ' ' + bbox.height + '" version="1.1">' + |
||||||
|
defs + contents + |
||||||
|
'</svg>'; |
||||||
|
} 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('<div class="bjs-container"></div>'); |
||||||
|
|
||||||
|
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>} definitions |
||||||
|
* @param {string} diagramId |
||||||
|
* |
||||||
|
* @return {ModdleElement<BPMNDiagram>|null} |
||||||
|
*/ |
||||||
|
function findBPMNDiagram(definitions, diagramId) { |
||||||
|
if (!diagramId) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
return find(definitions.diagrams, function(element) { |
||||||
|
return element.id === diagramId; |
||||||
|
}) || null; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/* <project-logo> */ |
||||||
|
|
||||||
|
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 = |
||||||
|
'<a href="http://bpmn.io" ' + |
||||||
|
'target="_blank" ' + |
||||||
|
'class="bjs-powered-by" ' + |
||||||
|
'title="Powered by bpmn.io" ' + |
||||||
|
'>' + |
||||||
|
img + |
||||||
|
'</a>'; |
||||||
|
|
||||||
|
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(); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/* </project-logo> */ |
@ -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 = |
||||||
|
'<?xml version="1.0" encoding="UTF-8"?>' + |
||||||
|
'<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' + |
||||||
|
'xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" ' + |
||||||
|
'xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" ' + |
||||||
|
'xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" ' + |
||||||
|
'targetNamespace="http://bpmn.io/schema/bpmn" ' + |
||||||
|
'id="Definitions_1">' + |
||||||
|
'<bpmn:process id="Process_1" isExecutable="false">' + |
||||||
|
'<bpmn:startEvent id="StartEvent_1"/>' + |
||||||
|
'</bpmn:process>' + |
||||||
|
'<bpmndi:BPMNDiagram id="BPMNDiagram_1">' + |
||||||
|
'<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">' + |
||||||
|
'<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">' + |
||||||
|
'<dc:Bounds height="36.0" width="36.0" x="173.0" y="102.0"/>' + |
||||||
|
'</bpmndi:BPMNShape>' + |
||||||
|
'</bpmndi:BPMNPlane>' + |
||||||
|
'</bpmndi:BPMNDiagram>' + |
||||||
|
'</bpmn:definitions>'; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 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<didi.Module>} [options.modules] a list of modules to override the default modules |
||||||
|
* @param {Array<didi.Module>} [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<string>} warnings |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* The createDiagram error. |
||||||
|
* |
||||||
|
* @typedef {Error} CreateDiagramError |
||||||
|
* |
||||||
|
* @property {Array<string>} warnings |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a new diagram to start modeling. |
||||||
|
* |
||||||
|
* Returns {Promise<CreateDiagramResult, CreateDiagramError>} |
||||||
|
*/ |
||||||
|
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 |
||||||
|
); |
@ -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 |
||||||
|
); |
@ -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<didi.Module>} [options.modules] a list of modules to override the default modules |
||||||
|
* @param {Array<didi.Module>} [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 = {}; |
@ -0,0 +1,9 @@ |
|||||||
|
import DrawModule from '../draw'; |
||||||
|
import ImportModule from '../import'; |
||||||
|
|
||||||
|
export default { |
||||||
|
__depends__: [ |
||||||
|
DrawModule, |
||||||
|
ImportModule |
||||||
|
] |
||||||
|
}; |
@ -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); |
||||||
|
} |
@ -0,0 +1,471 @@ |
|||||||
|
/** |
||||||
|
* Map containing SVG paths needed by BpmnRenderer. |
||||||
|
*/ |
||||||
|
|
||||||
|
export default function PathMap() { |
||||||
|
|
||||||
|
/** |
||||||
|
* Contains a map of path elements |
||||||
|
* |
||||||
|
* <h1>Path definition</h1> |
||||||
|
* A parameterized path is defined like this: |
||||||
|
* <pre> |
||||||
|
* '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] |
||||||
|
* } |
||||||
|
* </pre> |
||||||
|
* <p>It's important to specify a correct <b>height and width</b> 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).</p> |
||||||
|
* <p>The '<b>heightElements</b>' and '<b>widthElements</b>' 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 <b>heightElement</b>'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. |
||||||
|
* <ul> |
||||||
|
* <li>The values for the y axis can be accessed in the path string using {e.y0}, {e.y1}, ....</li> |
||||||
|
* <li>The values for the x axis can be accessed in the path string using {e.x0}, {e.x1}, ....</li> |
||||||
|
* </ul> |
||||||
|
* The numbers x0, x1 respectively y0, y1, ... map to the corresponding array index. |
||||||
|
* </p> |
||||||
|
*/ |
||||||
|
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. |
||||||
|
* <h1>Use case</h1> |
||||||
|
* <p>Use case is to scale the content of elements (event, gateways) based |
||||||
|
* on the element bounding box's size. |
||||||
|
* </p> |
||||||
|
* <h1>Why not transform</h1> |
||||||
|
* <p>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.</p> |
||||||
|
* |
||||||
|
* @param {string} pathId The ID of the path. |
||||||
|
* @param {Object} param <p> |
||||||
|
* Example param object scales the path to 60% size of the container (data.width, data.height). |
||||||
|
* <pre> |
||||||
|
* { |
||||||
|
* xScaleFactor: 0.6, |
||||||
|
* yScaleFactor:0.6, |
||||||
|
* containerWidth: data.width, |
||||||
|
* containerHeight: data.height, |
||||||
|
* position: { |
||||||
|
* mx: 0.46, |
||||||
|
* my: 0.2, |
||||||
|
* } |
||||||
|
* } |
||||||
|
* </pre> |
||||||
|
* <ul> |
||||||
|
* <li>targetpathwidth = xScaleFactor * containerWidth</li> |
||||||
|
* <li>targetpathheight = yScaleFactor * containerHeight</li> |
||||||
|
* <li>Position is used to set the starting coordinate of the path. M is computed: |
||||||
|
* <ul> |
||||||
|
* <li>position.x * containerWidth</li> |
||||||
|
* <li>position.y * containerHeight</li> |
||||||
|
* </ul> |
||||||
|
* Center of the container <pre> position: { |
||||||
|
* mx: 0.5, |
||||||
|
* my: 0.5, |
||||||
|
* }</pre> |
||||||
|
* Upper left corner of the container |
||||||
|
* <pre> position: { |
||||||
|
* mx: 0.0, |
||||||
|
* my: 0.0, |
||||||
|
* }</pre> |
||||||
|
* </li> |
||||||
|
* </ul> |
||||||
|
* </p> |
||||||
|
* |
||||||
|
*/ |
||||||
|
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); |
||||||
|
}); |
||||||
|
} |
@ -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' |
||||||
|
]; |
@ -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 ] |
||||||
|
}; |
@ -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; |
||||||
|
}; |
@ -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; |
@ -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; |
||||||
|
}; |
@ -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; |
||||||
|
}); |
||||||
|
}; |
@ -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 ] |
||||||
|
}; |
After Width: | Height: | Size: 448 B |
After Width: | Height: | Size: 446 B |
After Width: | Height: | Size: 446 B |
After Width: | Height: | Size: 448 B |
After Width: | Height: | Size: 446 B |
After Width: | Height: | Size: 446 B |
After Width: | Height: | Size: 446 B |
@ -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' ]; |
@ -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; |
||||||
|
} |
@ -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 ] |
||||||
|
}; |
@ -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); |
||||||
|
} |
||||||
|
}; |
@ -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; |
||||||
|
}; |
@ -0,0 +1,12 @@ |
|||||||
|
import BpmnAutoResize from './BpmnAutoResize'; |
||||||
|
import BpmnAutoResizeProvider from './BpmnAutoResizeProvider'; |
||||||
|
|
||||||
|
|
||||||
|
export default { |
||||||
|
__init__: [ |
||||||
|
'bpmnAutoResize', |
||||||
|
'bpmnAutoResizeProvider' |
||||||
|
], |
||||||
|
bpmnAutoResize: [ 'type', BpmnAutoResize ], |
||||||
|
bpmnAutoResizeProvider: [ 'type', BpmnAutoResizeProvider ] |
||||||
|
}; |
@ -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; |
||||||
|
} |
@ -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 ] |
||||||
|
}; |
@ -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; |
||||||
|
} |
@ -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} <moddleCopy.canCopyProperties> listener |
||||||
|
* |
||||||
|
* @param {Object} context |
||||||
|
* @param {Array<string>} context.propertyNames |
||||||
|
* @param {ModdleElement} context.sourceElement |
||||||
|
* @param {ModdleElement} context.targetElement |
||||||
|
* |
||||||
|
* @returns {Array<string>|boolean} - Return properties to be copied or false to disallow |
||||||
|
* copying. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* @typedef {Function} <moddleCopy.canCopyProperty> 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} <moddleCopy.canSetCopiedProperty> 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<string>} [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); |
||||||
|
} |
@ -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 ] |
||||||
|
}; |
@ -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' ]; |
@ -0,0 +1,8 @@ |
|||||||
|
import BpmnDiOrdering from '../di-ordering/BpmnDiOrdering'; |
||||||
|
|
||||||
|
export default { |
||||||
|
__init__: [ |
||||||
|
'bpmnDiOrdering' |
||||||
|
], |
||||||
|
bpmnDiOrdering: [ 'type', BpmnDiOrdering ] |
||||||
|
}; |
@ -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; |
||||||
|
}); |
||||||
|
}; |
@ -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; |
@ -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; |
||||||
|
}; |
@ -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 ] |
||||||
|
}; |
After Width: | Height: | Size: 467 B |
After Width: | Height: | Size: 467 B |
@ -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('<ul class="bjs-breadcrumbs"></ul>'); |
||||||
|
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('<li><span class="bjs-crumb"><a title="' + title + '">' + title + '</a></span></li>'); |
||||||
|
|
||||||
|
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<djs.model.Shape>} 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(); |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
@ -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 = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M4.81801948,3.50735931 L10.4996894,9.1896894 L10.5,4 L12,4 L12,12 L4,12 L4,10.5 L9.6896894,10.4996894 L3.75735931,4.56801948 C3.46446609,4.27512627 3.46446609,3.80025253 3.75735931,3.50735931 C4.05025253,3.21446609 4.52512627,3.21446609 4.81801948,3.50735931 Z"/></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 class="bjs-drilldown">' + ARROW_DOWN_SVG + '</button>'); |
||||||
|
|
||||||
|
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' |
||||||
|
]; |
@ -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; |
||||||
|
} |
@ -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 ] |
||||||
|
}; |
@ -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 |
||||||
|
); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
}; |
@ -0,0 +1,10 @@ |
|||||||
|
import EditorActionsModule from 'diagram-js/lib/features/editor-actions'; |
||||||
|
|
||||||
|
import BpmnEditorActions from './BpmnEditorActions'; |
||||||
|
|
||||||
|
export default { |
||||||
|
__depends__: [ |
||||||
|
EditorActionsModule |
||||||
|
], |
||||||
|
editorActions: [ 'type', BpmnEditorActions ] |
||||||
|
}; |
@ -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' ]; |
@ -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'; |
||||||
|
} |
@ -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<Point>} waypoints |
||||||
|
* |
||||||
|
* @returns {Array<Point>} |
||||||
|
*/ |
||||||
|
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 ]; |
||||||
|
} |
@ -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' |
||||||
|
]; |
@ -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 ] |
||||||
|
}; |
@ -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 ] |
||||||
|
}; |
@ -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; |
||||||
|
}; |
@ -0,0 +1,6 @@ |
|||||||
|
import BpmnInteractionEvents from './BpmnInteractionEvents'; |
||||||
|
|
||||||
|
export default { |
||||||
|
__init__: [ 'bpmnInteractionEvents' ], |
||||||
|
bpmnInteractionEvents: [ 'type', BpmnInteractionEvents ] |
||||||
|
}; |
@ -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; |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
}; |
@ -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 ] |
||||||
|
}; |
@ -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'; |
||||||
|
} |
@ -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(); |
||||||
|
} |
@ -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; |
||||||
|
} |
@ -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(); |
||||||
|
} |
@ -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 ] |
||||||
|
}; |
@ -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)); |
||||||
|
}; |
@ -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'; |
||||||
|
} |
||||||
|
} |
@ -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'); |
||||||
|
} |
@ -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<string>} 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', |
||||||
|
]); |
||||||
|
} |
@ -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 |
||||||
|
}); |
||||||
|
}; |
@ -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<string>} |
||||||
|
*/ |
||||||
|
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<string>} |
||||||
|
*/ |
||||||
|
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; |
||||||
|
} |
@ -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' |
||||||
|
]; |
@ -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' |
||||||
|
]; |
@ -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; |
||||||
|
} |
@ -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); |
@ -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); |
@ -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); |
@ -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'); |
||||||
|
}); |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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); |
@ -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; |
||||||
|
} |