cpee 2.1.134 → 2.1.136

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/cockpit/compliance-view.html +12 -13
  4. data/cockpit/compliance.html +12 -13
  5. data/cockpit/css/llm.css +3 -3
  6. data/cockpit/css/llmmodel.css +140 -0
  7. data/cockpit/css/model.css +0 -1
  8. data/cockpit/css/ui.css +13 -7
  9. data/cockpit/css/wfadaptor.css +15 -10
  10. data/cockpit/edit.html +17 -13
  11. data/cockpit/graph.html +8 -8
  12. data/cockpit/index.html +12 -13
  13. data/cockpit/js/build-bpmn2.js +302 -0
  14. data/cockpit/js/details.js +27 -10
  15. data/cockpit/js/instance.js +193 -203
  16. data/cockpit/js/llm-ui-bubble.js +36 -0
  17. data/cockpit/js/llm-ui-status.js +34 -0
  18. data/cockpit/js/llm-ui.js +87 -0
  19. data/cockpit/js/llm.js +463 -118
  20. data/cockpit/js/parameters.js +45 -14
  21. data/cockpit/js/wfadaptor.js +394 -75
  22. data/cockpit/llm.html +17 -15
  23. data/cockpit/llmmodel.html +18 -17
  24. data/cockpit/model.html +9 -9
  25. data/cockpit/only_llm.html +15 -13
  26. data/cockpit/themes/base.js +1 -1
  27. data/cockpit/themes/packed/theme.js +3 -0
  28. data/cockpit/themes/presetlr/rngs/alternative.rng +28 -0
  29. data/cockpit/themes/presetlr/rngs/call.rng +249 -0
  30. data/cockpit/themes/presetlr/rngs/callmanipulate.rng +281 -0
  31. data/cockpit/themes/presetlr/rngs/choose.rng +27 -0
  32. data/cockpit/themes/presetlr/rngs/closed_loop.rng +62 -0
  33. data/cockpit/themes/presetlr/rngs/closed_loop_cancel.rng +5 -0
  34. data/cockpit/themes/presetlr/rngs/closed_loop_control.rng +31 -0
  35. data/cockpit/themes/presetlr/rngs/closed_loop_measuring.rng +12 -0
  36. data/cockpit/themes/presetlr/rngs/critical.rng +8 -0
  37. data/cockpit/themes/presetlr/rngs/escape.rng +5 -0
  38. data/cockpit/themes/presetlr/rngs/group.rng +3 -0
  39. data/cockpit/themes/presetlr/rngs/loop.rng +40 -0
  40. data/cockpit/themes/presetlr/rngs/manipulate.rng +76 -0
  41. data/cockpit/themes/presetlr/rngs/otherwise.rng +25 -0
  42. data/cockpit/themes/presetlr/rngs/parallel.rng +24 -0
  43. data/cockpit/themes/presetlr/rngs/parallel_branch.rng +5 -0
  44. data/cockpit/themes/presetlr/rngs/scripts.rng +23 -0
  45. data/cockpit/themes/presetlr/rngs/start.rng +143 -0
  46. data/cockpit/themes/presetlr/rngs/stop.rng +5 -0
  47. data/cockpit/themes/presetlr/rngs/terminate.rng +8 -0
  48. data/cockpit/themes/presetlr/rngs/wait_for_signal.rng +8 -0
  49. data/cockpit/themes/presetlr/symbols/alternative.svg +19 -0
  50. data/cockpit/themes/presetlr/symbols/arrow.svg +1 -0
  51. data/cockpit/themes/presetlr/symbols/call.svg +1 -0
  52. data/cockpit/themes/presetlr/symbols/call_sensor.svg +1 -0
  53. data/cockpit/themes/presetlr/symbols/callmanipulate.svg +1 -0
  54. data/cockpit/themes/presetlr/symbols/callmanipulate_sensor.svg +1 -0
  55. data/cockpit/themes/presetlr/symbols/callmanipulate_sensor_signal.svg +1 -0
  56. data/cockpit/themes/presetlr/symbols/callmanipulate_signal.svg +1 -0
  57. data/cockpit/themes/presetlr/symbols/choose.svg +15 -0
  58. data/cockpit/themes/presetlr/symbols/choose_exclusive.svg +15 -0
  59. data/cockpit/themes/presetlr/symbols/choose_exclusive_finish.svg +1 -0
  60. data/cockpit/themes/presetlr/symbols/choose_inclusive.svg +13 -0
  61. data/cockpit/themes/presetlr/symbols/choose_inclusive_finish.svg +1 -0
  62. data/cockpit/themes/presetlr/symbols/closed_loop.svg +1 -0
  63. data/cockpit/themes/presetlr/symbols/closed_loop_cancel.svg +1 -0
  64. data/cockpit/themes/presetlr/symbols/closed_loop_control.svg +1 -0
  65. data/cockpit/themes/presetlr/symbols/closed_loop_measuring.svg +1 -0
  66. data/cockpit/themes/presetlr/symbols/complex.svg +1 -0
  67. data/cockpit/themes/presetlr/symbols/critical.svg +1 -0
  68. data/cockpit/themes/presetlr/symbols/delete.svg +1 -0
  69. data/cockpit/themes/presetlr/symbols/end.svg +1 -0
  70. data/cockpit/themes/presetlr/symbols/escape.svg +1 -0
  71. data/cockpit/themes/presetlr/symbols/event_end.svg +1 -0
  72. data/cockpit/themes/presetlr/symbols/event_start.svg +1 -0
  73. data/cockpit/themes/presetlr/symbols/loop.svg +15 -0
  74. data/cockpit/themes/presetlr/symbols/loop_end.svg +15 -0
  75. data/cockpit/themes/presetlr/symbols/loop_end_pre.svg +15 -0
  76. data/cockpit/themes/presetlr/symbols/manipulate.svg +1 -0
  77. data/cockpit/themes/presetlr/symbols/mark.svg +1 -0
  78. data/cockpit/themes/presetlr/symbols/otherwise.svg +7 -0
  79. data/cockpit/themes/presetlr/symbols/parallel.svg +1 -0
  80. data/cockpit/themes/presetlr/symbols/parallel_branch.svg +7 -0
  81. data/cockpit/themes/presetlr/symbols/parallel_branch_event.svg +14 -0
  82. data/cockpit/themes/presetlr/symbols/parallel_branch_event_rev.svg +11 -0
  83. data/cockpit/themes/presetlr/symbols/parallel_branch_normal.svg +7 -0
  84. data/cockpit/themes/presetlr/symbols/parallel_eventbased_exclusive.svg +1 -0
  85. data/cockpit/themes/presetlr/symbols/parallel_eventbased_parallel.svg +1 -0
  86. data/cockpit/themes/presetlr/symbols/scripts.svg +1 -0
  87. data/cockpit/themes/presetlr/symbols/start.svg +1 -0
  88. data/cockpit/themes/presetlr/symbols/start_event.svg +1 -0
  89. data/cockpit/themes/presetlr/symbols/stop.svg +1 -0
  90. data/cockpit/themes/presetlr/symbols/terminate.svg +1 -0
  91. data/cockpit/themes/presetlr/symbols/wait_for_signal.svg +1 -0
  92. data/cockpit/themes/presetlr/theme.js +54 -0
  93. data/cockpit/themes/wellformed/rngs/alternative.rng +28 -0
  94. data/cockpit/themes/wellformed/rngs/call.rng +249 -0
  95. data/cockpit/themes/wellformed/rngs/callmanipulate.rng +281 -0
  96. data/cockpit/themes/wellformed/rngs/choose.rng +27 -0
  97. data/cockpit/themes/wellformed/rngs/closed_loop.rng +62 -0
  98. data/cockpit/themes/wellformed/rngs/closed_loop_cancel.rng +5 -0
  99. data/cockpit/themes/wellformed/rngs/closed_loop_control.rng +31 -0
  100. data/cockpit/themes/wellformed/rngs/closed_loop_measuring.rng +12 -0
  101. data/cockpit/themes/wellformed/rngs/critical.rng +8 -0
  102. data/cockpit/themes/wellformed/rngs/escape.rng +5 -0
  103. data/cockpit/themes/wellformed/rngs/group.rng +3 -0
  104. data/cockpit/themes/wellformed/rngs/loop.rng +40 -0
  105. data/cockpit/themes/wellformed/rngs/manipulate.rng +76 -0
  106. data/cockpit/themes/wellformed/rngs/otherwise.rng +25 -0
  107. data/cockpit/themes/wellformed/rngs/parallel.rng +24 -0
  108. data/cockpit/themes/wellformed/rngs/parallel_branch.rng +5 -0
  109. data/cockpit/themes/wellformed/rngs/scripts.rng +23 -0
  110. data/cockpit/themes/wellformed/rngs/start.rng +143 -0
  111. data/cockpit/themes/wellformed/rngs/stop.rng +5 -0
  112. data/cockpit/themes/wellformed/rngs/terminate.rng +8 -0
  113. data/cockpit/themes/wellformed/rngs/wait_for_signal.rng +8 -0
  114. data/cockpit/themes/wellformed/symbols/alternative.svg +1 -0
  115. data/cockpit/themes/wellformed/symbols/arrow.svg +1 -0
  116. data/cockpit/themes/wellformed/symbols/call.svg +1 -0
  117. data/cockpit/themes/wellformed/symbols/call_sensor.svg +1 -0
  118. data/cockpit/themes/wellformed/symbols/callmanipulate.svg +1 -0
  119. data/cockpit/themes/wellformed/symbols/callmanipulate_sensor.svg +1 -0
  120. data/cockpit/themes/wellformed/symbols/callmanipulate_sensor_signal.svg +1 -0
  121. data/cockpit/themes/wellformed/symbols/callmanipulate_signal.svg +1 -0
  122. data/cockpit/themes/wellformed/symbols/choose.svg +1 -0
  123. data/cockpit/themes/wellformed/symbols/choose_exclusive.svg +1 -0
  124. data/cockpit/themes/wellformed/symbols/choose_exclusive_finish.svg +1 -0
  125. data/cockpit/themes/wellformed/symbols/choose_inclusive.svg +1 -0
  126. data/cockpit/themes/wellformed/symbols/choose_inclusive_finish.svg +1 -0
  127. data/cockpit/themes/wellformed/symbols/closed_loop.svg +1 -0
  128. data/cockpit/themes/wellformed/symbols/closed_loop_cancel.svg +1 -0
  129. data/cockpit/themes/wellformed/symbols/closed_loop_control.svg +1 -0
  130. data/cockpit/themes/wellformed/symbols/closed_loop_measuring.svg +1 -0
  131. data/cockpit/themes/wellformed/symbols/complex.svg +1 -0
  132. data/cockpit/themes/wellformed/symbols/critical.svg +1 -0
  133. data/cockpit/themes/wellformed/symbols/delete.svg +1 -0
  134. data/cockpit/themes/wellformed/symbols/end.svg +1 -0
  135. data/cockpit/themes/wellformed/symbols/escape.svg +1 -0
  136. data/cockpit/themes/wellformed/symbols/event_end.svg +1 -0
  137. data/cockpit/themes/wellformed/symbols/event_start.svg +6 -0
  138. data/cockpit/themes/wellformed/symbols/loop.svg +1 -0
  139. data/cockpit/themes/wellformed/symbols/loop_end.svg +1 -0
  140. data/cockpit/themes/wellformed/symbols/manipulate.svg +1 -0
  141. data/cockpit/themes/wellformed/symbols/mark.svg +1 -0
  142. data/cockpit/themes/wellformed/symbols/otherwise.svg +1 -0
  143. data/cockpit/themes/wellformed/symbols/parallel.svg +1 -0
  144. data/cockpit/themes/wellformed/symbols/parallel_branch.svg +1 -0
  145. data/cockpit/themes/wellformed/symbols/parallel_branch_event.svg +1 -0
  146. data/cockpit/themes/wellformed/symbols/parallel_branch_event_rev.svg +11 -0
  147. data/cockpit/themes/wellformed/symbols/parallel_branch_normal.svg +1 -0
  148. data/cockpit/themes/wellformed/symbols/parallel_eventbased_exclusive.svg +1 -0
  149. data/cockpit/themes/wellformed/symbols/parallel_eventbased_parallel.svg +1 -0
  150. data/cockpit/themes/wellformed/symbols/scripts.svg +1 -0
  151. data/cockpit/themes/wellformed/symbols/start.svg +1 -0
  152. data/cockpit/themes/wellformed/symbols/start_event.svg +1 -0
  153. data/cockpit/themes/wellformed/symbols/stop.svg +1 -0
  154. data/cockpit/themes/wellformed/symbols/terminate.svg +1 -0
  155. data/cockpit/themes/wellformed/symbols/wait_for_signal.svg +1 -0
  156. data/cockpit/themes/wellformed/theme.js +32 -0
  157. data/cockpit/track.html +8 -8
  158. data/cpee.gemspec +2 -2
  159. data/lib/callbacks/callbacks.rng +4 -4
  160. data/lib/callbacks.xml +4 -4
  161. data/lib/cpee/constants.rb +4 -4
  162. data/lib/cpee/fail.rb +4 -4
  163. data/lib/cpee/implementation.rb +5 -4
  164. data/lib/cpee/implementation_callbacks.rb +4 -4
  165. data/lib/cpee/implementation_notifications.rb +4 -4
  166. data/lib/cpee/implementation_properties.rb +108 -12
  167. data/lib/cpee/message.rb +4 -4
  168. data/lib/cpee/persistence.rb +6 -4
  169. data/lib/cpee/redis.rb +4 -4
  170. data/lib/cpee/statemachine.rb +4 -4
  171. data/lib/cpee/value_helper.rb +4 -4
  172. data/lib/cpee.xml +4 -4
  173. data/lib/engine/executionhandler-list.rng +4 -4
  174. data/lib/engine/instance-info.rng +4 -4
  175. data/lib/engine/instances.rng +4 -4
  176. data/lib/engine.xml +4 -4
  177. data/lib/properties/t_dslx.rng +346 -0
  178. data/lib/properties/transformation.rng +14 -0
  179. data/lib/properties.xml +5 -4
  180. data/server/executionhandlers/eval/backend/opts.yaml +1 -1
  181. data/server/executionhandlers/eval/connection.rb +4 -4
  182. data/server/executionhandlers/eval/execution.rb +4 -4
  183. data/server/executionhandlers/ruby/backend/opts.yaml +1 -1
  184. data/server/executionhandlers/ruby/connection.rb +5 -6
  185. data/server/executionhandlers/ruby/controller.rb +4 -4
  186. data/server/executionhandlers/ruby/dsl_to_dslx.xsl +6 -2
  187. data/server/executionhandlers/ruby/execution.rb +4 -4
  188. data/server/resources/properties.empty +5 -4
  189. data/server/resources/properties.init +5 -4
  190. data/server/resources/topics.xml +4 -4
  191. data/server/routing/end.pid +1 -0
  192. data/server/routing/end.rb +4 -4
  193. data/server/routing/forward-events-00.pid +1 -0
  194. data/server/routing/forward-events.rb +4 -4
  195. data/server/routing/forward-votes.pid +1 -0
  196. data/server/routing/forward-votes.rb +4 -4
  197. data/server/routing/persist.pid +1 -0
  198. data/server/routing/persist.rb +6 -4
  199. data/tools/server/cpee +1 -1
  200. data/tools/server/resources/properties.init +4 -4
  201. metadata +140 -5
  202. data/cockpit/CPEE.xml +0 -8
  203. data/cockpit/Signavio.xml +0 -8
  204. data/cockpit/transformations.xml +0 -4
  205. /data/{COPYING → LICENSE} +0 -0
data/cockpit/index.html CHANGED
@@ -2,15 +2,15 @@
2
2
  This file is part of CPEE.
3
3
 
4
4
  CPEE is free software: you can redistribute it and/or modify it under the terms
5
- of the GNU General Public License as published by the Free Software Foundation,
5
+ of the GNU Lesser General Public License as published by the Free Software Foundation,
6
6
  either version 3 of the License, or (at your option) any later version.
7
7
 
8
8
  CPEE is distributed in the hope that it will be useful, but WITHOUT ANY
9
9
  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
10
- PARTICULAR PURPOSE. See the GNU General Public License for more details.
10
+ PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
11
11
 
12
- You should have received a copy of the GNU General Public License along with
13
- CPEE (file COPYING in the main directory). If not, see
12
+ You should have received a copy of the GNU Lesser General Public License along with
13
+ CPEE (file LICENSE in the main directory). If not, see
14
14
  <http://www.gnu.org/licenses/>.
15
15
  -->
16
16
 
@@ -28,7 +28,7 @@
28
28
  <script type="text/javascript" src="/js_libs/jquery.svg.min.js"></script>
29
29
  <script type="text/javascript" src="/js_libs/jquery.svgdom.min.js"></script>
30
30
  <script type="text/javascript" src="/js_libs/vkbeautify.js"></script>
31
- <script type="text/javascript" src="/js_libs/util.js"></script>
31
+ <script type="text/javascript" src="/js_libs/uidash_util.js"></script>
32
32
  <script type="text/javascript" src="/js_libs/printf.js"></script>
33
33
  <script type="text/javascript" src="/js_libs/strftime.min.js"></script>
34
34
  <script type="text/javascript" src="/js_libs/parsequery.js"></script>
@@ -39,11 +39,11 @@
39
39
  <script type="text/javascript" src="/js_libs/relaxngui.js"></script>
40
40
 
41
41
  <script type="text/javascript" src="/js_libs/uidash.js"></script>
42
- <script type="text/javascript" src="/js_libs/custommenu.js"></script>
42
+ <script type="text/javascript" src="/js_libs/uidash_custommenu.js"></script>
43
43
 
44
- <script type="text/javascript" src="/js_libs/build-bpmn.js"></script>
44
+ <script type="text/javascript" src="js/build-bpmn2.js"></script>
45
45
 
46
- <link rel="stylesheet" href="/js_libs/custommenu.css" type="text/css"/>
46
+ <link rel="stylesheet" href="/js_libs/uidash_custommenu.css" type="text/css"/>
47
47
  <link rel="stylesheet" href="/js_libs/uidash.css" type="text/css"/>
48
48
 
49
49
  <link rel="stylesheet" href="/js_libs/relaxngui.css" type="text/css"/>
@@ -66,7 +66,7 @@
66
66
  <link rel="stylesheet" href="/global_ui/uicpee.css" type="text/css"/>
67
67
  <style></style>
68
68
  </head>
69
- <body data-base-port="8298" data-base-engine="engine/" data-base-themes="themes/" is="x-ui-">
69
+ <body data-base-port="9298" data-base-engine="engine/" data-base-themes="themes/" is="x-ui-">
70
70
  <div id='disclaimer' class='hidden'> <!--{{{-->
71
71
  <h1>Disclaimer</h1>
72
72
 
@@ -119,7 +119,6 @@
119
119
  <div class='hidden' id='relaxngworker'></div>
120
120
 
121
121
  <div class='menu' id='templates'></div>
122
- <div class='menu' id='modeltypes'></div>
123
122
 
124
123
  <template id="label">
125
124
  <svg width="20" height="20" xmlns="http://www.w3.org/2000/svg" style="position: absolute; top: 0; left: 0;" class="displaylabel">
@@ -168,7 +167,7 @@
168
167
  <form id='fuckchrome'>
169
168
  <input type='file' name='testsetfile' id='testsetfile'/>
170
169
  </form>
171
- <button title='a template includes various settings, subscriptions or a (partial) model' name="loadtestset">load template</button><button title='a testset includes various settings, subscriptions or a model' name="loadtestsetfile">load testset</button>
170
+ <button title='a template includes various settings, subscriptions or a (partial) model' name="loadtestset">load<br>template</button><button title='a testset includes various settings, subscriptions or a model' name="loadtestsetfile">load<br>testset</button>
172
171
  </div>
173
172
  <div>
174
173
  <input type='file' name='modelfile' id='modelfile'/>
@@ -182,8 +181,8 @@
182
181
  <button title='the SVG contains the graphical repesentation of the CPEE tree as you see it on the screen' name="savesvgfile">save svg<br>graph</button>
183
182
  </div>
184
183
  <div>
185
- <a id="savebpmnfile" href="" download=""></a>
186
- <button title='the BPMN includes a graphical representation that can be loaded in Signavio and Camundal' name="savebpmnfile">save BPMN</button>
184
+ <a id="savemodelfile" href="" download=""></a>
185
+ <button title='the model in the format of the current model type (e.g. CPEE, BPMN, Mermaid)' name="savemodelfile">save model</button>
187
186
  </div>
188
187
  </div><div class='section' id='modifiers'>
189
188
  <template id="item">
@@ -0,0 +1,302 @@
1
+ /*
2
+ This file is part of cpee-layout.
3
+
4
+ cpee-layout is free software: you can redistribute it and/or modify it under
5
+ the terms of the GNU Lesser General Public License as published by the Free
6
+ Software Foundation, either version 3 of the License, or (at your option) any
7
+ later version.
8
+
9
+ cpee-layout is distributed in the hope that it will be useful, but WITHOUT
10
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
+ FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12
+ details.
13
+
14
+ You should have received a copy of the GNU Lesser General Public License
15
+ along with cpee-layout (file LICENSE in the main directory). If not, see
16
+ <http://www.gnu.org/licenses/>.
17
+
18
+ The CPEE type mapping and the BPMN output follow build-bpmn.js,
19
+ Copyright 2026 David Kindermann (Apache-2.0).
20
+ */
21
+
22
+ //
23
+ // build-bpmn2.js
24
+ //
25
+ // Creates BPMN XML from wfadaptor's get_svg_metrics():
26
+ // bpmn2_convert(pos_matrix, con_list, lr, geometry), like david_bpmn_convert of
27
+ // build-bpmn.js plus the orientation and the geometry of the drawing. Rows and columns are
28
+ // 0-indexed.
29
+ //
30
+ // Unlike build-bpmn.js, the layout is not computed again: every symbol is
31
+ // placed where the metrics (and so the SVG) have it, and the flows are drawn
32
+ // with right angles only, entering their targets perpendicularly.
33
+ //
34
+ // 1) nodes cells → BPMN shapes, or placeholders that flows pass through
35
+ // 2) flows con_list, followed through placeholders from shape to shape
36
+ // 3) route right-angled waypoints, bending where the placeholders are
37
+ // 4) xml process and diagram interchange
38
+ //
39
+
40
+ // the svg rows (geometry.row apart) are spread to ROW here, so that tasks have
41
+ // the bpmn-js default height (rows are vertical in lr as well)
42
+ const SYM = 30; // symbol size
43
+ const TASK_H = 80; // task height, as bpmn-js; its icon is in the top left corner, so one line of text stays below it
44
+ const ROW = TASK_H + 10; // row distance, the gap stays as in the svg
45
+ const GAP = 10; // space between a task and the next symbol
46
+ const SPACE = 10; // extra space in front of every column, the svg is a bit tight for bpmn
47
+ const PAD = 20; // tasks are this much wider on both sides, bpmn-js puts its text close to the border
48
+ const BACK = 20; // flows that go around (loops) go back this far right of the target's line
49
+
50
+ // cpee types → BPMN element (kind) and event definition (def), or placeholder.
51
+ // Tasks, and all types not listed, get their kind from the subtype
52
+ const TYPE_MAP = {
53
+ description: { kind: "startEvent" },
54
+ start_event: { kind: "startEvent", def: "message" },
55
+ event_start: { kind: "startEvent", def: "signal" }, // the start of a signal branch
56
+ end: { kind: "endEvent" },
57
+ end_event: { kind: "endEvent", def: "message" },
58
+ event_end: { kind: "endEvent", def: "terminate" },
59
+ terminate: { kind: "endEvent", def: "terminate" },
60
+ call: { task: true },
61
+ callmanipulate: { task: true },
62
+ manipulate: { task: true },
63
+ loop_head: { kind: "exclusiveGateway" },
64
+ loop_tail: { kind: "exclusiveGateway" },
65
+ loop_finish: { kind: "exclusiveGateway" },
66
+ loop_tail_pre: { kind: "exclusiveGateway" }, // presetlr: pre_test loops
67
+ loop_finish_pre: { kind: "exclusiveGateway" },
68
+ parallel_start: { kind: "parallelGateway" },
69
+ parallel_simple: { kind: "parallelGateway" },
70
+ parallel_complex: { kind: "complexGateway" },
71
+ parallel_eventbased_parallel: { kind: "complexGateway" },
72
+ parallel_branch_event: { kind: "intermediateThrowEvent", def: "signal" },
73
+ parallel_branch_event_rev: { kind: "intermediateThrowEvent", def: "signal" },
74
+ parallel_branch_normal: { placeholder: "pass" },
75
+ choose_inclusive: { kind: "inclusiveGateway" },
76
+ choose_inclusive_finish: { kind: "inclusiveGateway" },
77
+ choose_exclusive: { kind: "exclusiveGateway" },
78
+ choose_exclusive_finish: { kind: "exclusiveGateway" },
79
+ alternative: { placeholder: "case" }, // its label names the flow
80
+ otherwise: { placeholder: "default" }, // its label names the flow, without one the flow is the default
81
+ stop: { placeholder: "pass" }
82
+ };
83
+ const TASK_SUBTYPES = { user: "userTask", service: "serviceTask", script: "scriptTask" };
84
+ const EVENT_SUBTYPES = {
85
+ timer: { kind: "intermediateCatchEvent", def: "timer" },
86
+ message_receive: { kind: "intermediateCatchEvent", def: "message" },
87
+ message_send: { kind: "intermediateThrowEvent", def: "message" }
88
+ };
89
+
90
+ const fmt = (n) => Number(n).toFixed(2).replace(/\.?0+$/, "");
91
+ const key = (r, c) => r + "," + c;
92
+ function xmlEscape(s = "") {
93
+ return String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
94
+ }
95
+ function xmlEventDef(def, id) {
96
+ return def ? `<${def}EventDefinition id="${id}_def"/>` : "";
97
+ }
98
+ function xmlSignavioBg(color) { // Signavio: background color per shape
99
+ return `<extensionElements><signavio:signavioMetaData metaKey="bgcolor" metaValue="${xmlEscape(color || "#ffffff")}"/></extensionElements>`;
100
+ }
101
+
102
+ // 1) nodes: kind is the BPMN element, or null for placeholders. Names are the
103
+ // labels only, the svg has no room for more. x is where the symbol is drawn,
104
+ // mid the middle of its row, labelBox where its label is, relative to the symbol
105
+ function buildNodes(matrix, geometry) {
106
+ const nodes = new Map();
107
+ let n = 0;
108
+ matrix.forEach((row, r) => (row || []).forEach((cell, c) => {
109
+ if (!cell) return;
110
+ const t = TYPE_MAP[cell.type] || { task: true };
111
+ const node = { r, c, x: cell.x - geometry.shift + SPACE * c, mid: r * ROW + geometry.top + TASK_H / 2, width: cell.width, fill: cell.fill, id: "n" + (++n), kind: null, label: cell.label || null };
112
+ const l = cell.label_box;
113
+ if (l) node.labelBox = { dx: l.x - (cell.x - geometry.shift), dy: l.y - (geometry.top + r * geometry.row), w: l.width, h: l.height };
114
+ if (!t.task) {
115
+ Object.assign(node, t);
116
+ } else { // the subtype comes from the endpoint's properties (cockpit only)
117
+ const sub = typeof cell.subtype === "string" ? cell.subtype : "";
118
+ const ev = sub.startsWith("event/") ? (EVENT_SUBTYPES[sub.split("/").slice(1).join("_")] || EVENT_SUBTYPES[sub.split("/")[1]] || { kind: "intermediateCatchEvent" }) : null;
119
+ if (ev) {
120
+ Object.assign(node, { kind: ev.kind, def: ev.def });
121
+ } else {
122
+ node.kind = sub == "marker/subprocess" ? "subProcess"
123
+ : sub.startsWith("task/") ? (TASK_SUBTYPES[sub.split("/")[1]] || "serviceTask")
124
+ : cell.type == "manipulate" ? "scriptTask" : "serviceTask";
125
+ }
126
+ }
127
+ nodes.set(key(r, c), node);
128
+ }));
129
+ // every column after one with a task moves by the width tasks gain
130
+ const taskCols = new Set([...nodes.values()].filter(isTask).map((n) => n.c));
131
+ for (const n of nodes.values()) n.x += 2 * PAD * [...taskCols].filter((c) => c < n.c).length;
132
+ return nodes;
133
+ }
134
+
135
+ // bounds as in the svg: tasks span their label and the row, everything else
136
+ // is a symbol in the middle of the row
137
+ function bounds(node) {
138
+ const x = node.x;
139
+ if (!node.kind) return { x: x + SYM / 2, y: node.mid, w: 0, h: 0 }; // placeholders are points
140
+ if (isTask(node)) return { x, y: node.mid - TASK_H / 2, w: Math.max(SYM, node.width - GAP) + 2 * PAD, h: TASK_H };
141
+ return { x, y: node.mid - SYM / 2, w: SYM, h: SYM };
142
+ }
143
+ const isTask = (node) => /Task$|^subProcess$/.test(node.kind);
144
+
145
+ // labels of events, gateways and placeholders are where the svg shows them,
146
+ // relative to their symbol. Labels the svg does not show have no bounds
147
+ function labelBounds(node) {
148
+ const l = node.labelBox;
149
+ return l ? { x: node.x + l.dx, y: node.mid - SYM / 2 + l.dy, w: l.w, h: l.h } : null;
150
+ }
151
+
152
+ // 2) flows from shape to shape, following con_list through placeholders
153
+ function buildFlows(nodes, conlist) {
154
+ const out = new Map();
155
+ for (const e of conlist) {
156
+ const f = key(e.from.row, e.from.col), t = key(e.to.row, e.to.col);
157
+ if (!nodes.has(f) || !nodes.has(t)) throw new Error("bpmn2_convert: con_list connects an empty cell, " + f + " -> " + t);
158
+ if (!out.has(f)) out.set(f, []);
159
+ out.get(f).push(t);
160
+ }
161
+ const flows = [];
162
+ const seen = new Set();
163
+ for (const [k, src] of nodes) {
164
+ if (!src.kind) continue;
165
+ const walk = (from, via, anchor, visited) => {
166
+ for (const t of out.get(from) || []) {
167
+ const n = nodes.get(t);
168
+ if (n.kind) {
169
+ const sig = src.id + ">" + n.id;
170
+ if (seen.has(sig)) continue;
171
+ seen.add(sig);
172
+ flows.push({ src, dst: n, via, anchor });
173
+ } else if (!visited.has(t)) {
174
+ visited.add(t);
175
+ walk(t, via.concat([n]), n.placeholder != "pass" ? n : anchor, visited);
176
+ }
177
+ }
178
+ };
179
+ walk(k, [], null, new Set([k]));
180
+ }
181
+ for (const f of flows) {
182
+ if (!f.anchor) continue;
183
+ if (f.anchor.label) f.name = f.anchor.label;
184
+ else if (f.anchor.placeholder == "default" && f.src.kind == "exclusiveGateway") f.isDefault = true;
185
+ }
186
+ return flows;
187
+ }
188
+
189
+ // 3) right angles only. a is the position along the flow, c across it
190
+ function route(flow, lr, nodes) {
191
+ const P = (a, c) => lr ? { x: a, y: c } : { x: c, y: a };
192
+ const box = (n) => {
193
+ const b = bounds(n);
194
+ const s = { x: n.kind ? b.x + SYM / 2 : b.x, y: n.mid }; // the symbol, not the label
195
+ const next = n.mid + ROW / 2; // between this row and the next
196
+ return lr ? { n, a: s.x, c: s.y, a0: b.x, a1: b.x + b.w, c0: b.y, c1: b.y + b.h, turn: GAP / 2 }
197
+ : { n, a: s.y, c: s.x, a0: b.y, a1: b.y + b.h, c0: b.x, c1: b.x + b.w, turn: next - (b.y + b.h) };
198
+ };
199
+ const shapes = [...nodes.values()].filter((n) => n.kind).map(box);
200
+ const blocking = (c, from, to, except) => shapes.filter((b) => !except.includes(b.n) && b.c0 <= c && c <= b.c1 && b.a1 > Math.min(from, to) && b.a0 < Math.max(from, to));
201
+ // a leg follows its own direction (dir), forwards or backwards along the flow
202
+ const leg = (s, d) => {
203
+ const dA = d.a - s.a, dC = d.c - s.c, dir = dA < 0 ? -1 : 1, except = [s.n, d.n];
204
+ const out = dir > 0 ? s.a1 : s.a0, into = dir > 0 ? d.a0 : d.a1; // the sides facing each other
205
+ if (Math.abs(dA) < 0.5) return dC > 0 ? [P(s.a, s.c1), P(d.a, d.c0)] : [P(s.a, s.c0), P(d.a, d.c1)]; // straight across
206
+ if (Math.abs(dC) < 0.5 && !blocking(s.c, s.a, d.a, except).length) return [P(out, s.c), P(into, d.c)]; // straight along
207
+ if (dC > 0 && !blocking(d.c, s.a, d.a, except).length) return [P(s.a, s.c1), P(s.a, d.c), P(into, d.c)]; // branch out: across, then along into the target
208
+ if (dC < 0 && !blocking(s.c, s.a, d.a, except).length) return [P(out, s.c), P(d.a, s.c), P(d.a, d.c1)]; // join: along, then across into the target
209
+ // shapes in the way (e.g. the content of a loop): leave on the far side, turn,
210
+ // and go back parallel to the target's line, BACK beside it on the right (or
211
+ // beside what is in the way). Symbols are entered at 45 degrees
212
+ const far = dir > 0 ? s.a0 : s.a1, turn = far - dir * s.turn;
213
+ const inway = blocking(d.c + BACK, turn, into, except);
214
+ const lane = inway.length ? Math.max(...inway.map((b) => b.c1)) + BACK : d.c + BACK;
215
+ if (d.n.kind && !isTask(d.n)) {
216
+ const edge = /Gateway$/.test(d.n.kind) ? SYM / 4 : SYM / 2 / Math.SQRT2; // the border at 45 degrees: diamond or circle
217
+ return [P(far, s.c), P(turn, s.c), P(turn, lane), P(d.a - dir * (lane - d.c), lane), P(d.a - dir * edge, d.c + edge)];
218
+ }
219
+ const end = lane <= d.c1 ? [P(into, lane)] : [P(d.a, lane), P(d.a, d.c1)];
220
+ return [P(far, s.c), P(turn, s.c), P(turn, lane)].concat(end);
221
+ };
222
+ // source and target on one line, along or across the flow: straight, unless
223
+ // another shape is in the way (e.g. the content of a loop)
224
+ const s = box(flow.src), d = box(flow.dst), except = [flow.src, flow.dst];
225
+ const across = shapes.some((b) => !except.includes(b.n) && b.a0 <= s.a && s.a <= b.a1 && b.c1 > Math.min(s.c, d.c) && b.c0 < Math.max(s.c, d.c));
226
+ if (flow.src !== flow.dst) {
227
+ if (Math.abs(d.c - s.c) < 0.5 && !blocking(s.c, s.a, d.a, except).length) return d.a > s.a ? [P(s.a1, s.c), P(d.a0, d.c)] : [P(s.a0, s.c), P(d.a1, d.c)];
228
+ if (Math.abs(d.a - s.a) < 0.5 && !across) return d.c > s.c ? [P(s.a, s.c1), P(d.a, d.c0)] : [P(s.a, s.c0), P(d.a, d.c1)];
229
+ }
230
+
231
+ const stops = [flow.src].concat(flow.via, [flow.dst]).map(box);
232
+ let pts = [];
233
+ for (let i = 0; i < stops.length - 1; i++) pts = pts.concat(leg(stops[i], stops[i + 1]));
234
+ // drop repeated points and points in the middle of a straight line
235
+ pts = pts.filter((p, i) => i == 0 || p.x != pts[i - 1].x || p.y != pts[i - 1].y);
236
+ return pts.filter((p, i) => {
237
+ if (i == 0 || i == pts.length - 1) return true;
238
+ const a = pts[i - 1], b = pts[i + 1];
239
+ return !((a.x == p.x && p.x == b.x) || (a.y == p.y && p.y == b.y));
240
+ });
241
+ }
242
+
243
+ // 4) xml
244
+ function generateXML(nodes, flows, lr) {
245
+ const shapes = [...nodes.values()].filter((n) => n.kind);
246
+ flows.forEach((f, i) => { f.id = "e" + (i + 1); });
247
+ const defaults = new Map(flows.filter((f) => f.isDefault).map((f) => [f.src.id, f.id]));
248
+ const nameAttr = (s) => s ? ` name="${xmlEscape(s)}"` : "";
249
+
250
+ const proc = shapes.map((s) => {
251
+ const def = defaults.has(s.id) ? ` default="${defaults.get(s.id)}"` : "";
252
+ return ` <${s.kind} id="${s.id}"${nameAttr(s.label)}${def}>${xmlSignavioBg(s.fill)}${xmlEventDef(s.def, s.id)}</${s.kind}>`;
253
+ }).concat(flows.map((f) =>
254
+ ` <sequenceFlow id="${f.id}" sourceRef="${f.src.id}" targetRef="${f.dst.id}"${nameAttr(f.name)}/>`
255
+ )).join("\n");
256
+
257
+ const xmlBounds = (b, indent) => `${indent}<omgdc:Bounds x="${fmt(b.x)}" y="${fmt(b.y)}" width="${fmt(b.w)}" height="${fmt(b.h)}"/>\n`;
258
+ const xmlLabel = (b) => b ? ` <bpmndi:BPMNLabel>\n${xmlBounds(b, " ")} </bpmndi:BPMNLabel>\n` : "";
259
+ const di = shapes.map((s) => {
260
+ const marker = /Gateway$/.test(s.kind) ? ' isMarkerVisible="true"' : "";
261
+ return ` <bpmndi:BPMNShape id="${s.id}_gui" bpmnElement="${s.id}"${marker}>\n` +
262
+ xmlBounds(bounds(s), " ") +
263
+ xmlLabel(s.label && !isTask(s) ? labelBounds(s) : null) +
264
+ ` </bpmndi:BPMNShape>`;
265
+ }).concat(flows.map((f) =>
266
+ ` <bpmndi:BPMNEdge id="${f.id}_gui" bpmnElement="${f.id}">\n` +
267
+ route(f, lr, nodes).map((p) => ` <omgdi:waypoint x="${fmt(p.x)}" y="${fmt(p.y)}"/>`).join("\n") + "\n" +
268
+ xmlLabel(f.name ? labelBounds(f.anchor) : null) +
269
+ ` </bpmndi:BPMNEdge>`
270
+ )).join("\n");
271
+
272
+ return `<?xml version="1.0" encoding="UTF-8"?>
273
+ <definitions
274
+ xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
275
+ xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
276
+ xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
277
+ xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
278
+ xmlns:signavio="http://www.signavio.com"
279
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
280
+ targetNamespace="https://example.com/bpmn"
281
+ >
282
+ <process id="process1" isExecutable="false">
283
+ ${proc}
284
+ </process>
285
+ <bpmndi:BPMNDiagram id="diagram1">
286
+ <bpmndi:BPMNPlane id="plane1" bpmnElement="process1">
287
+ ${di}
288
+ </bpmndi:BPMNPlane>
289
+ </bpmndi:BPMNDiagram>
290
+ </definitions>`;
291
+ }
292
+
293
+ // returns the BPMN XML as a string
294
+ function bpmn2_convert(dpm, dcl, lr, geometry) {
295
+ if (!Array.isArray(dpm) || !Array.isArray(dcl) || typeof lr !== "boolean" || !geometry) throw new Error("bpmn2_convert: needs pos_matrix, con_list, lr and geometry of get_svg_metrics");
296
+ const nodes = buildNodes(dpm, geometry);
297
+ return generateXML(nodes, buildFlows(nodes, dcl), lr);
298
+ }
299
+
300
+ if (typeof window !== "undefined") {
301
+ window.bpmn2_convert = bpmn2_convert;
302
+ }
@@ -1,11 +1,15 @@
1
1
  var details_updated = new Event("details:updated", {"bubbles":true, "cancelable":false});
2
+ var details_timer;
2
3
 
3
4
  $(document).ready(function() {
4
- var timer;
5
-
6
5
  $(document).on('input','#dat_details input, #dat_details textarea, #dat_details [contenteditable]',function(e){
7
- clearTimeout(timer);
8
- timer = setTimeout(do_main_save, 5000);
6
+ clearTimeout(details_timer);
7
+ let details = save['details'];
8
+ details_timer = setTimeout(() => {
9
+ details_timer = undefined;
10
+ // safety net, details_leave should have been called when details were emptied or replaced
11
+ if (details === save['details'] && $('#dat_details').children().length > 0) { do_main_save(); }
12
+ }, 5000);
9
13
  });
10
14
  // only for contenteditable divs
11
15
  $(document).on('keypress','#dat_details div[contenteditable]',function(e){
@@ -15,19 +19,31 @@ $(document).ready(function() {
15
19
  }
16
20
  });
17
21
  $(document).on('relaxngui_remove', '#dat_details', function(e){
18
- clearTimeout(timer);
22
+ details_leave(false);
19
23
  do_main_save();
20
24
  });
21
25
  $(document).on('relaxngui_move', '#dat_details', function(e){
22
- clearTimeout(timer);
26
+ details_leave(false);
23
27
  do_main_save();
24
28
  });
25
29
  $(document).on('relaxngui_change', '#dat_details', function(e){
26
- clearTimeout(timer);
30
+ details_leave(false);
27
31
  do_main_save();
28
32
  });
29
33
  });
30
34
 
35
+ // call before details are emptied or replaced. a pending (not yet saved)
36
+ // change is saved when flush is true (same model stays), otherwise dropped
37
+ // (model is replaced, saving would overwrite the new one).
38
+ function details_leave(flush) { //{{{
39
+ let pending = details_timer != undefined;
40
+ clearTimeout(details_timer);
41
+ details_timer = undefined;
42
+ if (flush && pending && save['details'] && $('#dat_details').children().length > 0) {
43
+ do_main_save();
44
+ }
45
+ } //}}}
46
+
31
47
  function do_main_save() { //{{{
32
48
  if (save['details'].has_changed()) {
33
49
  do_main_work(save['details_target'].svgid);
@@ -106,13 +122,14 @@ function do_main_work(svgid) { //{{{
106
122
  } else {
107
123
  $.ajax({
108
124
  type: "PUT",
109
- url: url + "/properties/description/",
125
+ url: url + "/properties/dslx/",
110
126
  contentType: 'text/xml',
111
127
  headers: {
112
- 'Content-ID': 'description',
128
+ 'Content-ID': 'dslx',
113
129
  'CPEE-Event-Source': myid
114
130
  },
115
- data: desc.get_description()
131
+ data: desc.get_description(),
132
+ error: report_model_failure
116
133
  });
117
134
  format_instance_pos();
118
135