@ -0,0 +1,813 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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,3 @@
@@ -0,0 +1,3 @@
|
||||
export { |
||||
default |
||||
} from './lib/Viewer'; |
@ -0,0 +1,74 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -0,0 +1,9 @@
|
||||
import DrawModule from '../draw'; |
||||
import ImportModule from '../import'; |
||||
|
||||
export default { |
||||
__depends__: [ |
||||
DrawModule, |
||||
ImportModule |
||||
] |
||||
}; |
@ -0,0 +1,157 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -0,0 +1,8 @@
|
||||
import BpmnDiOrdering from '../di-ordering/BpmnDiOrdering'; |
||||
|
||||
export default { |
||||
__init__: [ |
||||
'bpmnDiOrdering' |
||||
], |
||||
bpmnDiOrdering: [ 'type', BpmnDiOrdering ] |
||||
}; |
@ -0,0 +1,55 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -0,0 +1,6 @@
|
||||
import BpmnInteractionEvents from './BpmnInteractionEvents'; |
||||
|
||||
export default { |
||||
__init__: [ 'bpmnInteractionEvents' ], |
||||
bpmnInteractionEvents: [ 'type', BpmnInteractionEvents ] |
||||
}; |
@ -0,0 +1,158 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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; |
||||
} |