cpee 2.1.135 → 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 +12 -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 -1
  192. data/server/routing/end.rb +4 -4
  193. data/server/routing/forward-events-00.pid +1 -1
  194. data/server/routing/forward-events.rb +4 -4
  195. data/server/routing/forward-votes.pid +1 -1
  196. data/server/routing/forward-votes.rb +4 -4
  197. data/server/routing/persist.pid +1 -1
  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 +136 -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
@@ -1,16 +1,18 @@
1
1
  /*
2
- This file is part of CPEE.
2
+ This file is part of cpee-layout.
3
3
 
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,
6
- either version 3 of the License, or (at your option) any later version.
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.
7
8
 
8
- CPEE is distributed in the hope that it will be useful, but WITHOUT ANY
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.
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.
11
13
 
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
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
14
16
  <http://www.gnu.org/licenses/>.
15
17
  */
16
18
 
@@ -57,12 +59,20 @@ function WfAdaptor(theme_base,doit) { // Controller {{{
57
59
  this.set_label_container = function(container) { // {{{
58
60
  illustrator.set_label_container(container);
59
61
  } // }}}
62
+ this.get_svg = function() { // {{{
63
+ return illustrator.get_svg();
64
+ } // }}}
65
+ this.get_svg_metrics = function() { // {{{
66
+ return illustrator.get_svg_metrics();
67
+ } // }}}
60
68
 
61
69
  function loadTheme(doit) { //{{{
62
70
  manifestation = new WFAdaptorManifestation(self);
63
71
  illustrator.compact = manifestation.compact == true ? true : false;
64
72
  illustrator.rotated_labels = manifestation.rotated_labels == true ? true : false;
65
73
  illustrator.striped = manifestation.striped == true ? true : false;
74
+ illustrator.lr = manifestation.lr == true ? true : false;
75
+ illustrator.wellformed_pre_test = manifestation.wellformed_pre_test == true ? true : false;
66
76
  description.source = manifestation.source;
67
77
  var deferreds = [];
68
78
  // copy parent stuff
@@ -235,9 +245,15 @@ function WfIllustrator(wf_adaptor) { // View {{{
235
245
  this.dim = {};
236
246
  this.dim.symbols = [];
237
247
  this.dim.connections = [];
248
+ this.dim.implied = []; // symbols that an icon draws in a neighbouring cell (theme: implied), for the metrics only
249
+ this.dim.labels = {}; // the shown label of each cell ('row,col'), for the metrics only
250
+ this.dim.rows = []; // lr only: start and size of each symbol along the rows
238
251
  this.compact = true;
239
252
  this.rotated_labels = true;
240
253
  this.striped = true;
254
+ this.lr = false;
255
+ this.wellformed_pre_test = false; // experimental: pre_test loops are drawn reversed
256
+ this.inverted = false; // true while drawing inside such a loop, nested pre_test loops toggle it
241
257
  this.global_style = {};
242
258
  // private
243
259
  var self = this;
@@ -256,6 +272,18 @@ function WfIllustrator(wf_adaptor) { // View {{{
256
272
  ' <marker id="endarrow" viewBox="0 0 10 10" refX="7" refY="5" orient="auto" markerUnits="strokeWidth" markerWidth="14" markerHeight="5.2">' +
257
273
  ' <path d="M 2,2 L 6.21,4.11 Q 8,5 6.21,5.89 L 2,8 Z"/>' +
258
274
  ' </marker>' +
275
+ ' <marker id="arrowrev" viewBox="0 0 10 10" refX="28.5" refY="5" orient="auto-start-reverse" markerUnits="strokeWidth" markerWidth="14" markerHeight="3.5">' +
276
+ ' <path d="m 2 2 l 6 3 l -6 3 z"/>' +
277
+ ' </marker>' +
278
+ ' <marker id="endarrowrev" viewBox="0 0 10 10" refX="7" refY="5" orient="auto-start-reverse" markerUnits="strokeWidth" markerWidth="14" markerHeight="5.2">' +
279
+ ' <path d="M 2,2 L 6.21,4.11 Q 8,5 6.21,5.89 L 2,8 Z"/>' +
280
+ ' </marker>' +
281
+ ' <marker id="arrowdiag" viewBox="0 0 10 10" refX="24.5" refY="5" orient="auto" markerUnits="strokeWidth" markerWidth="14" markerHeight="3.5">' +
282
+ ' <path d="m 2 2 l 6 3 l -6 3 z"/>' +
283
+ ' </marker>' +
284
+ ' <marker id="arrowdiagrev" viewBox="0 0 10 10" refX="24.5" refY="5" orient="auto-start-reverse" markerUnits="strokeWidth" markerWidth="14" markerHeight="3.5">' +
285
+ ' <path d="m 2 2 l 6 3 l -6 3 z"/>' +
286
+ ' </marker>' +
259
287
  ' <clipPath id="startclip">' +
260
288
  ' <rect x="-4" y="-4" width="35" height="38"/>' +
261
289
  ' </clipPath>' +
@@ -287,11 +315,142 @@ function WfIllustrator(wf_adaptor) { // View {{{
287
315
  let w = self.dim.get_x_max(0,graph.max.row,graph.max.col,'start');
288
316
  if (w == 0) { w = bb.x + bb.width + self.width_shift; }
289
317
  // the alternative is bb.x + bb.width + self.width_shift, but this is bad when clipped elements
318
+ if (self.lr) { w = Math.max(w, bb.x + bb.width + self.width_shift); } // lr: labels can reach across the flow, e.g. rotated below a gateway; clipped labels only reach along it
290
319
 
291
- self.svg.container.attr('height', bb.y + bb.height + self.height_shift); // small border on the bottom
292
- self.svg.container.attr('width', w); // small border on the right
293
- self.svg.container.attr('data-pos-matrix', JSON.stringify(self.dim.symbols));
294
- self.svg.container.attr('data-con-list', JSON.stringify(self.dim.connections));
320
+ if (self.lr) { // mirror at the diagonal: rows go to the right, columns go down
321
+ graph.svg.attr('transform',lr_matrix());
322
+ self.svg.container.attr('height', w - lr_shift() + self.height_shift);
323
+ self.svg.container.attr('width', bb.y + bb.height + self.height_shift + lr_shift());
324
+ } else {
325
+ self.svg.container.attr('height', bb.y + bb.height + self.height_shift); // small border on the bottom
326
+ self.svg.container.attr('width', w); // small border on the right
327
+ }
328
+ } // }}}
329
+ this.get_svg = function() { // {{{
330
+ // standalone copy for export: the rules of all stylesheets marked with
331
+ // data-include-export are inlined into the style attributes (var()
332
+ // resolved, in cascade order), further columns from the label container are
333
+ // merged in, and interaction state and the render time are removed
334
+ var svgs = [self.svg.container.get(0)];
335
+ if (self.svg.label_container) {
336
+ self.svg.label_container.children('svg').not(self.svg.container).each(function(_,ele) { svgs.push(ele); });
337
+ }
338
+ var clones = svgs.map(function(s) { return s.cloneNode(true); });
339
+
340
+ var root = getComputedStyle(document.documentElement);
341
+ var rules = [];
342
+ var family = '';
343
+ $.each(document.styleSheets, function(_,sheet) {
344
+ if (!sheet.ownerNode || !sheet.ownerNode.hasAttribute('data-include-export')) { return; }
345
+ $.each(sheet.cssRules, function(_,rule) {
346
+ if (!rule.selectorText || rule.selectorText == ':root') { return; }
347
+ if (rule.selectorText == 'svg') {
348
+ if (rule.style.getPropertyValue('font-family') != '') {
349
+ family = getComputedStyle(svgs[0]).getPropertyValue('font-family').replace(/["']/g,'').trim();
350
+ }
351
+ return;
352
+ }
353
+ var css = rule.style.cssText.replace(/var\((--[\w-]+)\)/g, function(m,v) { return root.getPropertyValue(v).trim() || m; });
354
+ rule.selectorText.split(',').forEach(function(sel) {
355
+ rules.push({ sel: sel.trim(), spec: specificity(sel), order: rules.length, css: css });
356
+ });
357
+ });
358
+ });
359
+ rules.sort(function(a,b) { return a.spec - b.spec || a.order - b.order; });
360
+ family = family.replace(/-/,' ').split(' ').map(function(w) { return w[0].toUpperCase() + w.substr(1); }).join(' '); // adwaita-sans -> Adwaita Sans, the installed name
361
+
362
+ clones.forEach(function(clone) {
363
+ $(clone).find('.duration').remove();
364
+ $(clone).find('.selected').removeClass('selected');
365
+ $(clone).find('.hover').removeClass('hover');
366
+ $(clone).find('*').each(function(_,el) {
367
+ var decl = [];
368
+ rules.forEach(function(r) {
369
+ try { if (el.matches(r.sel)) { decl.push(r.css); } } catch (e) { } // e.g. unsupported pseudo classes
370
+ });
371
+ var own = el.getAttribute('style');
372
+ if (own && own.trim() != '') { decl.push(own.trim().replace(/;?$/,';')); }
373
+ if ($(el).is('text.super, g.hoverstyle, g.markstyle')) { decl.push('display: none;'); }
374
+ if (el.nodeName == 'text' && family != '') { decl.push('font-family: \'' + family + '\';'); }
375
+ if (decl.length > 0) { el.setAttribute('style', decl.join(' ')); }
376
+ });
377
+ });
378
+
379
+ var svg = clones.shift();
380
+ var width = parseInt(svg.getAttribute('width'));
381
+ clones.forEach(function(clone) {
382
+ var gr = $X('<g transform="translate(' + width + ')" xmlns="http://www.w3.org/2000/svg"></g>');
383
+ gr.append($(clone).children());
384
+ width += parseInt(clone.getAttribute('width'));
385
+ $(svg).append(gr);
386
+ });
387
+ svg.setAttribute('width', width + 1);
388
+ return svg;
389
+ } // }}}
390
+ this.get_svg_metrics = function() { // {{{
391
+ // the layout of the last drawing as it is shown (a copy, empty cells are
392
+ // null): pos_matrix holds the symbols per row and column, x and width are
393
+ // horizontal; con_list the connections between them. Rows and columns are
394
+ // 0-indexed here, the layout counts from 1. lr is laid out top to bottom
395
+ // and mirrored at the diagonal, so rows and columns are swapped back, and x
396
+ // and width come from the rows (as in get_x, x is the symbol position plus
397
+ // width_shift). geometry: a symbol is drawn shift left of its x, and the
398
+ // symbols of row r start at top + r * row. label_box is where the label is
399
+ // shown, in the coordinates of the svg
400
+ var center_x = (self.width - self.default_width) / 2;
401
+ var center_y = (self.height - self.default_height) / 2;
402
+ var geometry = self.lr ? // lr: x runs along the internal rows, the rows are the internal columns, lr_matrix moves both by lr_shift
403
+ { shift: self.width_shift - center_y, top: 2 * self.height_shift + center_x, row: self.width } :
404
+ { shift: self.width_shift - center_x, top: self.height_shift + center_y, row: self.height };
405
+ var pos =function(r,c) { return self.lr ? { row: c - 1, col: r - 1 } : { row: r - 1, col: c - 1 }; };
406
+ var row_start = function(r,c) { // like get_row, but symbols without an own entry (e.g. alternative) also find one to the right
407
+ var rs = self.dim.rows[r] || [];
408
+ for (var j = c; j >= 0; j--) { if (rs[j]) { return rs[j].start; } }
409
+ for (var j = c + 1; j < rs.length; j++) { if (rs[j]) { return rs[j].start; } }
410
+ return 0;
411
+ };
412
+ var pos_matrix = [];
413
+ self.dim.symbols.forEach(function(row,r) {
414
+ if (!row) { return; }
415
+ row.forEach(function(sym,c) {
416
+ if (!sym || !sym.type || r == 0 || c == 0) { return; } // without type the cell is only reserved (set_x), nothing is drawn there
417
+ var p = pos(r,c);
418
+ var s = $.extend({}, sym);
419
+ if (self.lr) {
420
+ s.x = row_start(r,c) + self.width;
421
+ s.width = (self.dim.rows[r] && self.dim.rows[r][c] && self.dim.rows[r][c].size) || self.height;
422
+ }
423
+ var lab = self.dim.labels[r + ',' + c];
424
+ if (lab) {
425
+ var b = get_box(lab);
426
+ s.label_box = { x: b.x, y: b.y, width: b.width, height: b.height };
427
+ }
428
+ if (!pos_matrix[p.row]) { pos_matrix[p.row] = []; }
429
+ pos_matrix[p.row][p.col] = s;
430
+ });
431
+ });
432
+ self.dim.implied.forEach(function(i) { // x/y are cells in the icon; lr mirrors the icon, so its x runs along the rows there
433
+ var dr = self.lr ? (i.x || 0) : (i.y || 0), dc = self.lr ? (i.y || 0) : (i.x || 0);
434
+ var r = i.row + dr, c = i.col + dc;
435
+ var p = pos(r,c);
436
+ var src = self.dim.symbols[i.row][i.col];
437
+ if (!pos_matrix[p.row]) { pos_matrix[p.row] = []; }
438
+ if (pos_matrix[p.row][p.col]) { return; } // a real symbol wins
439
+ pos_matrix[p.row][p.col] = self.lr ?
440
+ { x: row_start(i.row,i.col) + self.width + dr * self.height, width: self.height, type: i.type, eid: src.eid } :
441
+ { x: src.x + dc * self.width, width: self.width, type: i.type, eid: src.eid };
442
+ });
443
+ var con_list = self.dim.connections.map(function(c) {
444
+ return { from: pos(c.from.row,c.from.col), to: pos(c.to.row,c.to.col) };
445
+ });
446
+ return JSON.parse(JSON.stringify({ lr: self.lr == true, geometry: geometry, pos_matrix: pos_matrix, con_list: con_list })); // holes become null
447
+ } // }}}
448
+ var specificity = function(sel) { // {{{ ids, classes/attributes/pseudo classes, types
449
+ var s = sel.replace(/::[\w-]+/g,'');
450
+ var a = (s.match(/#[\w-]+/g) || []).length;
451
+ var b = (s.match(/\.[\w-]+|\[[^\]]*\]|:[\w-]+/g) || []).length;
452
+ var c = (s.replace(/\[[^\]]*\]/g,'').match(/(^|[\s>+~(])[a-zA-Z][\w-]*/g) || []).length;
453
+ return a * 10000 + b * 100 + c;
295
454
  } // }}}
296
455
  this.set_duration = function(start) { //{{{
297
456
  self.svg.container.append(
@@ -320,6 +479,9 @@ function WfIllustrator(wf_adaptor) { // View {{{
320
479
  $('> defs > [belongs-to=element]', self.svg.container).each(function() {$(this).remove()});
321
480
  self.dim.symbols = [];
322
481
  self.dim.connections = [];
482
+ self.dim.implied = [];
483
+ self.dim.labels = {};
484
+ self.dim.rows = [];
323
485
  } // }}}
324
486
  var get_symbol = this.get_symbol = function() { // {{{
325
487
  } // }}}
@@ -345,7 +507,8 @@ function WfIllustrator(wf_adaptor) { // View {{{
345
507
  return line;
346
508
  } //}}}
347
509
 
348
- var set_x = this.dim.set_x = function(row,col,twidth,sname) { //{{{
510
+ var set_x = this.dim.set_x = function(row,col,twidth) { //{{{
511
+ // reserves the space of a cell; what is drawn there (type, label, ...) is set by set_x_cond
349
512
  if (!self.dim.symbols[row]) { self.dim.symbols[row] = []; }
350
513
  if (!self.dim.symbols[row][col]) { self.dim.symbols[row][col] = {}; }
351
514
 
@@ -385,10 +548,14 @@ function WfIllustrator(wf_adaptor) { // View {{{
385
548
  } else {
386
549
  self.dim.symbols[row][col].width = twidth;
387
550
  }
388
- self.dim.symbols[row][col].type = sname;
389
551
  // console.log('set_x ',row,col,debug_dim());
390
552
  } //}}}
391
553
  var set_x_cond = this.dim.set_x_cond = function(row,col,tx,twidth,sname,label,subtype,sty,eid) { //{{{
554
+ if (self.lr) { // labels widen the row, not the column
555
+ set_row(row,col,[]);
556
+ self.dim.rows[row][col].size = Math.max(self.dim.rows[row][col].size || 0, twidth);
557
+ twidth = self.width;
558
+ }
392
559
  if (!self.dim.symbols[row]) { self.dim.symbols[row] = []; }
393
560
  if (!self.dim.symbols[row][col] || self.dim.symbols[row][col].width < twidth) {
394
561
  self.dim.symbols[row][col] = {};
@@ -496,6 +663,57 @@ function WfIllustrator(wf_adaptor) { // View {{{
496
663
  var get_y = this.draw.get_y = function(row) { // {{{
497
664
  return { y: row * self.height - self.height, height_shift: self.height_shift};
498
665
  } // }}}
666
+ var lr_shift = function() { // {{{
667
+ // mirroring swaps the left (width - width_shift) and top (height_shift) margins, so shift them back
668
+ return self.width - self.width_shift - self.height_shift;
669
+ } // }}}
670
+ var lr_matrix = function() { // {{{
671
+ return 'matrix(0,1,1,0,' + lr_shift() + ',' + (self.height_shift - lr_shift()) + ')'; // top margin is twice the height_shift
672
+ } // }}}
673
+ var set_row = this.dim.set_row = function(row,col,prev) { // {{{
674
+ // lr: a symbol starts where its predecessors end, so only things in the same block are aligned
675
+ if (!self.lr) { return; }
676
+ if (!self.dim.rows[row]) { self.dim.rows[row] = []; }
677
+ if (self.dim.rows[row][col]) { return; }
678
+ let mx = 0;
679
+ prev.forEach((p) => {
680
+ if (!p) { return; }
681
+ if (p.row == 0 || p.col == 0) { p = { row: 1, col: 1 }; } // first element connects to description
682
+ mx = Math.max(mx, get_row_end(p.row,p.col));
683
+ });
684
+ for (let i=1; i<row; i++) { // and after everything before in this and the following columns, e.g. blocks in vertical parallels share columns
685
+ for (let j=col; self.dim.rows[i] && j<self.dim.rows[i].length; j++) {
686
+ if (self.dim.rows[i][j]) { mx = Math.max(mx, get_row_end(i,j)); }
687
+ }
688
+ }
689
+ self.dim.rows[row][col] = { start: mx };
690
+ } // }}}
691
+ var get_row = this.dim.get_row = function(row,col) { // {{{
692
+ if (!self.lr) { return (row - 1) * self.height; }
693
+ for (let j=col; j>=0; j--) { // e.g. wide connections point to empty columns
694
+ if (self.dim.rows[row] && self.dim.rows[row][j]) { return self.dim.rows[row][j].start; }
695
+ }
696
+ return 0;
697
+ } // }}}
698
+ var get_row_end = this.dim.get_row_end = function(row,col) { // {{{
699
+ if (!self.lr) { return row * self.height; }
700
+ let size = self.dim.rows[row] && self.dim.rows[row][col] && self.dim.rows[row][col].size;
701
+ return get_row(row,col) + (size || self.height);
702
+ } // }}}
703
+ var get_row_max = this.dim.get_row_max = function(rowf,rowt,colf,colt) { // {{{
704
+ if (!self.lr) { return rowt * self.height; }
705
+ let mx = 0;
706
+ let last = false;
707
+ for (let i=rowf; i<=rowt; i++) {
708
+ for (let j=colf; j<=colt; j++) {
709
+ if (self.dim.rows[i] && self.dim.rows[i][j]) {
710
+ mx = Math.max(mx, get_row_end(i,j));
711
+ if (i == rowt) { last = true; }
712
+ }
713
+ }
714
+ }
715
+ return last ? mx : mx + self.height; // closing symbols are drawn after the border
716
+ } // }}}
499
717
 
500
718
  var get_width = this.draw.get_width = function(g) { //{{{
501
719
  let t = g.clone();
@@ -505,6 +723,22 @@ function WfIllustrator(wf_adaptor) { // View {{{
505
723
  t.remove();
506
724
  return w;
507
725
  } //}}}
726
+ var get_label_end = this.draw.get_label_end = function(lab,frame) { //{{{
727
+ // where a label ends, in the coordinates of frame (one of its ancestors),
728
+ // including all transforms in between, so it does not matter if the label
729
+ // is beside the icon, below it, rotated or right aligned
730
+ let inner = lab.clone();
731
+ lab.parentsUntil(frame).each(function(_,p) {
732
+ let w = $X('<g xmlns="http://www.w3.org/2000/svg"></g>');
733
+ if ($(p).attr('transform')) { w.attr('transform',$(p).attr('transform')); }
734
+ inner = w.append(inner);
735
+ });
736
+ let g = $X('<g xmlns="http://www.w3.org/2000/svg"></g>').append(inner);
737
+ self.svg.container.append(g);
738
+ let bb = g[0].getBBox();
739
+ g.remove();
740
+ return Math.max(0, bb.x + bb.width);
741
+ } //}}}
508
742
  var get_height = this.draw.get_height = function(g) { //{{{
509
743
  let t = g.clone();
510
744
  self.svg.container.append(t);
@@ -516,18 +750,24 @@ function WfIllustrator(wf_adaptor) { // View {{{
516
750
  var get_dim = this.draw.get_dim = function(g) { //{{{
517
751
  let element = g.clone();
518
752
  self.svg.container.append(element);
519
- let svg = element[0].ownerSVGElement;
753
+ let dim = get_box(element[0]);
754
+ element.remove();
755
+ return dim;
756
+ } //}}}
757
+ var get_box = this.draw.get_box = function(el) { //{{{
758
+ // the enclosing box of a drawn element, in the coordinates of its svg,
759
+ // including all transforms (e.g. its blocks and the lr mirroring)
760
+ let svg = el.ownerSVGElement;
520
761
 
521
- var r = element[0].getBBox();
762
+ var r = el.getBBox();
522
763
 
523
764
  if (!svg) {
524
- element[0].remove();
525
765
  return { x: 0, y: 0, cx: 0, cy: 0, width: 0, height: 0 };
526
766
  }
527
767
 
528
768
  var p = svg.createSVGPoint();
529
769
 
530
- var matrix = svg.getScreenCTM().inverse().multiply(element[0].getScreenCTM());
770
+ var matrix = svg.getScreenCTM().inverse().multiply(el.getScreenCTM());
531
771
 
532
772
  p.x = r.x;
533
773
  p.y = r.y;
@@ -553,8 +793,6 @@ function WfIllustrator(wf_adaptor) { // View {{{
553
793
  var width = maxX - minX;
554
794
  var height = maxY - minY;
555
795
 
556
- element.remove();
557
-
558
796
  return {
559
797
  x: minX,
560
798
  y: minY,
@@ -589,11 +827,11 @@ function WfIllustrator(wf_adaptor) { // View {{{
589
827
 
590
828
  let dstart = get_x(parent_row,col,'symbol ' + sname);
591
829
 
592
- let sstart = dstart + center_x - self.width_shift;
593
- let stop = row * self.height + center_y - (self.height-self.height_shift);
830
+ let sstart = dstart + center_x - self.width_shift;
831
+ let stop = get_row(row,col) + center_y + self.height_shift;
594
832
 
595
833
  var g = $X('<g class="element" element-row="' + (row-1) + '" element-type="' + sname + '" element-id="' + id + '" xmlns="http://www.w3.org/2000/svg">' +
596
- '<g transform="translate(' + String(sstart) + ',' + String(stop) + ')"></g>' +
834
+ '<g transform="translate(' + String(sstart) + ',' + String(stop) + ')' + (self.lr ? ' matrix(0,1,1,0,0,0)' : '') + '"></g>' + // lr: mirror back, so that the symbol is upright
597
835
  '</g>');
598
836
 
599
837
  // add the element-endpoint and other stuff to each symbol (from theme info function)
@@ -702,9 +940,10 @@ function WfIllustrator(wf_adaptor) { // View {{{
702
940
  lab.text(title);
703
941
  }
704
942
  }
705
- let width = this.get_width(lab);
943
+ if (self.elements[sname].hidelabel) { lab.addClass('invisible'); } // still takes its space
706
944
  if (mid.length > 0) {
707
945
  if (end.length > 0) {
946
+ let width = this.get_label_end(lab,mid); // the clip path is inside part-middle
708
947
  let pos = get_dim(mid);
709
948
  mid.attr('clip-path','url(#ele-' + id + ')');
710
949
  let clip = $X('<clipPath belongs-to="element" id="ele-' + id + '" xmlns="http://www.w3.org/2000/svg">' +
@@ -723,7 +962,7 @@ function WfIllustrator(wf_adaptor) { // View {{{
723
962
  lab.addClass('rotate');
724
963
  tdim = self.width;
725
964
  } else {
726
- tdim = self.width + width + self.width_shift_label;
965
+ tdim = Math.max(self.width, this.get_label_end(lab,sym) + self.width_shift_label);
727
966
  }
728
967
  set_x_cond(row,col,dstart,tdim,sname,title,subtype,sty,id);
729
968
  }
@@ -743,6 +982,12 @@ function WfIllustrator(wf_adaptor) { // View {{{
743
982
  $('.part-end',sym).remove();
744
983
  set_x_cond(row,col,dstart,self.width,sname,title,subtype,sty,id);
745
984
  }
985
+ if (self.elements[sname].implied) { self.dim.implied.push($.extend({row: row, col: col}, self.elements[sname].implied)); }
986
+ if (self.elements[sname].hidelabel) { // only takes its space, it is not shown
987
+ self.dim.symbols[row][col].label = undefined;
988
+ } else if (lab.length > 0 && lab.text() != '' && $.contains(sym[0],lab[0])) {
989
+ self.dim.labels[row + ',' + col] = lab[0];
990
+ }
746
991
 
747
992
 
748
993
  $(g[0].childNodes[0]).append(sym);
@@ -758,10 +1003,10 @@ function WfIllustrator(wf_adaptor) { // View {{{
758
1003
  );
759
1004
  }
760
1005
 
761
- // hover
1006
+ // hover, top left of the symbol, or top right if the theme says so (e.g. because of a label on the left)
762
1007
  $(g[0].childNodes[0]).append(
763
1008
  $X(
764
- '<g class="hoverstyle markstyle" xmlns="http://www.w3.org/2000/svg">' +
1009
+ '<g class="hoverstyle markstyle"' + (self.elements[sname].mark_position == 'right' ? ' transform="translate(27,0)"' : '') + ' xmlns="http://www.w3.org/2000/svg">' +
765
1010
  '<circle cx="2" cy="0" r="7"/>' +
766
1011
  '<path d="m -1 0 l 2 2 l 4 -4" class="standline"/>' +
767
1012
  '</g>'
@@ -781,65 +1026,79 @@ function WfIllustrator(wf_adaptor) { // View {{{
781
1026
  let bstart = get_x(p1.row,p1.col,'border from');
782
1027
  let bend = get_x_plus(p1.row,p2.row,p2.col,'border to');
783
1028
  group.prepend($X('<rect element-id="' + id + '" x="' + (bstart - 1.1 * self.width_shift - self.group_extend) + '" ' +
784
- 'y="' + ((p1.row-1)*self.height+self.height_shift/2-self.group_extend+yshrink) + '" ' +
1029
+ 'y="' + (get_row(p1.row,p1.col)+self.height_shift/2-self.group_extend+yshrink) + '" ' +
785
1030
  'width="' + (bend-bstart+2*self.group_extend) + '" ' +
786
- 'height="' + (((p2.row+1)-p1.row)*self.height+2*self.group_extend-2*yshrink) + '" ' +
1031
+ 'height="' + (get_row_max(p1.row,p2.row,p1.col,p2.col)-get_row(p1.row,p1.col)+2*self.group_extend-2*yshrink) + '" ' +
787
1032
  'class="block" rx="12" ry="12" xmlns="http://www.w3.org/2000/svg"/>'));
788
1033
  } // }}}
789
1034
  var draw_tile = this.draw.draw_tile = function(id, p1, p2, group, yshrink=0) { // {{{
790
1035
  let bstart = get_x(p1.row,p1.col,'tile from');
791
1036
  let bend = get_x_plus(p1.row,p2.row,p2.col,'tile to');
792
1037
  group.prepend($X('<rect element-id="' + id + '" x="' + (bstart - 1.1 * self.width_shift - self.group_extend) + '" ' +
793
- 'y="' + ((p1.row-1)*self.height+self.height_shift/2-self.group_extend+yshrink) + '" ' +
1038
+ 'y="' + (get_row(p1.row,p1.col)+self.height_shift/2-self.group_extend+yshrink) + '" ' +
794
1039
  'width="' + (bend-bstart+2*self.group_extend) + '" ' +
795
- 'height="' + (((p2.row+1)-p1.row)*self.height+2*self.group_extend-2*yshrink) + '" ' +
1040
+ 'height="' + (get_row_max(p1.row,p2.row,p1.col,p2.col)-get_row(p1.row,p1.col)+2*self.group_extend-2*yshrink) + '" ' +
796
1041
  'class="tile" rx="12" ry="12" xmlns="http://www.w3.org/2000/svg"/>'));
797
1042
  } // }}}
798
- var draw_connection = this.draw.draw_connection = function(group, start, end, context_row, arrow) { // {{{
1043
+ var draw_connection = this.draw.draw_connection = function(group, start, end, context_row, arrow, around=false) { // {{{
799
1044
  let sr = Math.min(start.row,end.row);
800
1045
  let cstart = get_x(sr,start.col,'conn from');
801
1046
  let cend = get_x(sr,end.col,'conn to');
802
- self.dim.connections.push({from: {row: start.row, col: start.col}, to: {row: end.row, col: end.col}});
1047
+ let from = {row: start.row, col: start.col}, to = {row: end.row, col: end.col};
1048
+ self.dim.connections.push(self.inverted ? {from: to, to: from} : {from: from, to: to}); // inverted: the arrow points to the start
803
1049
 
804
1050
  if(((end['row']-start['row']) == 0) && ((end['col']-start['col']) == 0)) return;
805
- var line;
806
- if (arrow)
807
- line = $X('<path xmlns="http://www.w3.org/2000/svg" class="edge" marker-end="url(#' + (end.structural ? 'endarrow' : 'arrow') + ')"/>');
808
- else
809
- line = $X('<path xmlns="http://www.w3.org/2000/svg" class="edge"/>');
810
- if (end['row']-start['row'] == 0 || end['col']-start['col'] == 0) { // straight line
811
- line.attr("d", "M " + String(cstart) + "," + String(start['row']*self.height-15+start.yoffset) +" "+
812
- String(cend) + "," + String(end['row']*self.height-15+end.yoffset)
1051
+ var line = $X('<path xmlns="http://www.w3.org/2000/svg" class="edge"/>');
1052
+ let diag = false; // the arrow sits on a diagonal segment (into a gateway), so it has to be closer to the center
1053
+ let sy = get_row(start['row'],start['col']) + self.height - 15 + start.yoffset; // center of the symbols
1054
+ let ey = get_row(end['row'],end['col']) + self.height - 15 + end.yoffset;
1055
+ if (around) { // e.g. back in an empty loop: leave at 45 degrees to the right, go straight, enter at 45 degrees
1056
+ diag = true;
1057
+ let off = 7.5 + 7; // from the center to the corner of the gateway, then 7 further
1058
+ line.attr("d", "M " + String(cstart) + "," + String(sy) +" "+
1059
+ String(cstart+off) + "," + String(sy-off) +" "+
1060
+ String(cend+off) + "," + String(ey+off) +" "+
1061
+ String(cend) + "," + String(ey)
1062
+ );
1063
+ } else if (end['row']-start['row'] == 0 || end['col']-start['col'] == 0) { // straight line
1064
+ line.attr("d", "M " + String(cstart) + "," + String(sy) +" "+
1065
+ String(cend) + "," + String(ey)
813
1066
  );
814
1067
  } else if (end['row']-start['row'] > 0) { // downwards
815
1068
  if (end['col']-start['col'] > 0) {// left - right
816
1069
  if (self.compact) {
817
- line.attr("d", "M " + String(cstart) + "," + String(start['row']*self.height-15+start.yoffset) +" "+
818
- String(cstart+14) + "," + String((end['row']-1)*self.height) +" "+ // first turn of horizontal-line going away from node
819
- String(cend) + "," + String((end['row']-1)*self.height) +" "+
820
- String(cend) + "," + String(end['row']*self.height-15+end.yoffset)
1070
+ diag = self.inverted;
1071
+ line.attr("d", "M " + String(cstart) + "," + String(sy) +" "+
1072
+ String(cstart+14) + "," + String(get_row_end(start['row'],start['col'])) +" "+ // first turn of horizontal-line going away from node
1073
+ String(cend) + "," + String(get_row_end(start['row'],start['col'])) +" "+
1074
+ String(cend) + "," + String(ey)
821
1075
  );
822
1076
  } else {
823
- line.attr("d", "M " + String(cstart) + "," + String(start['row']*self.height-15+start.yoffset) +" "+
824
- String(cend) + "," + String(start['row']*self.height-15+start.yoffset) +" "+
825
- String(cend) + "," + String(end['row']*self.height-15+end.yoffset)
1077
+ line.attr("d", "M " + String(cstart) + "," + String(sy) +" "+
1078
+ String(cend) + "," + String(sy) +" "+
1079
+ String(cend) + "," + String(ey)
826
1080
  );
827
1081
  }
828
1082
  } else { // right - left
829
- line.attr("d", "M " + String(cstart) + "," + String(start['row']*self.height-15+start.yoffset) +" "+
830
- String(cstart) + "," + String(end['row']*self.height-32) +" "+
831
- String(cend+14) + "," + String(end['row']*self.height-32) +" "+ // last turn of horizontal-line going into the node
832
- String(cend) + "," + String(end['row']*self.height-15+end.yoffset)
1083
+ diag = !self.inverted;
1084
+ line.attr("d", "M " + String(cstart) + "," + String(sy) +" "+
1085
+ String(cstart) + "," + String(get_row(end['row'],end['col'])+self.height-32) +" "+
1086
+ String(cend+14) + "," + String(get_row(end['row'],end['col'])+self.height-32) +" "+ // last turn of horizontal-line going into the node
1087
+ String(cend) + "," + String(ey)
833
1088
  );
834
1089
  }
835
1090
  } else if(end['row']-start['row'] < 0) { // upwards
836
- line.attr("d", "M " + String(cstart) + "," + String(start['row']*self.height-15+start.yoffset) +" "+
837
- String(cstart) + "," + String((self.dim.symbols.length-1)*self.height+4) +" "+
838
- String(cend+15) + "," + String((self.dim.symbols.length-1)*self.height+4) +" "+
839
- String(cend+15) + "," + String(end['row']*self.height+15)+" "+
840
- String(cend) + "," + String(end['row']*self.height-15+end.yoffset)
1091
+ diag = !self.inverted;
1092
+ let by = (self.lr ? get_row_end(start['row'],start['col']) : (self.dim.symbols.length-1)*self.height) + 4;
1093
+ line.attr("d", "M " + String(cstart) + "," + String(sy) +" "+
1094
+ String(cstart) + "," + String(by) +" "+
1095
+ String(cend+15) + "," + String(by) +" "+
1096
+ String(cend+15) + "," + String(get_row(end['row'],end['col'])+self.height+15)+" "+
1097
+ String(cend) + "," + String(ey)
841
1098
  );
842
1099
  }
1100
+ if (arrow) { line.attr(self.inverted ? 'marker-start' : 'marker-end', 'url(#' + (end.structural ? 'endarrow' : (diag ? 'arrowdiag' : 'arrow')) + (self.inverted ? 'rev' : '') + ')'); } // inverted: arrow points to the start
1101
+ if (self.lr) { line.attr('transform',lr_matrix()); } // connections are not part of the graph group
843
1102
  self.svg.container.append(line);
844
1103
  } // }}}
845
1104
  // }}}
@@ -985,6 +1244,7 @@ function WfDescription(wf_adaptor, wf_illustrator) { // Model {{{
985
1244
  // }}}
986
1245
  // Adaption functions {{{
987
1246
  this.insert_after = function(new_node, target, source_opts) { // {{{
1247
+ reset_used_id_list();
988
1248
  if ($.isArray(new_node)) {
989
1249
  $.each(new_node,function(k,v){
990
1250
  var nn = self.source(v,source_opts);
@@ -999,6 +1259,7 @@ function WfDescription(wf_adaptor, wf_illustrator) { // Model {{{
999
1259
  update();
1000
1260
  } // }}}
1001
1261
  this.insert_first_into = function(new_node, target, source_opts) { // {{{
1262
+ reset_used_id_list();
1002
1263
  if ($.isArray(new_node)) {
1003
1264
  $.each(new_node,function(k,v){
1004
1265
  var nn = self.source(v,source_opts);
@@ -1013,6 +1274,7 @@ function WfDescription(wf_adaptor, wf_illustrator) { // Model {{{
1013
1274
  update();
1014
1275
  } // }}}
1015
1276
  this.insert_last_into = function(new_node, target) { // {{{
1277
+ reset_used_id_list();
1016
1278
  if ($.isArray(new_node)) {
1017
1279
  $.each(new_node,function(k,v){
1018
1280
  var nn = self.source(v);
@@ -1057,11 +1319,15 @@ function WfDescription(wf_adaptor, wf_illustrator) { // Model {{{
1057
1319
  $(this).removeAttr('svg-label');
1058
1320
  });
1059
1321
  } //}}}
1060
- var parse = function(root, parent_pos) { // private {{{
1322
+ var reversed_loop = function(node) { // private {{{
1323
+ return illustrator.wellformed_pre_test && node.tagName == 'loop' && $(node).attr('mode') == 'pre_test';
1324
+ } // }}}
1325
+ var parse = function(root, parent_pos, first_prev) { // private {{{
1061
1326
  var pos = JSON.parse(JSON.stringify(parent_pos));
1062
1327
  var max = {'row': 0,'col': 0};
1063
- var prev = [parent_pos]; // connects parent with child(s), depending on the expansion
1328
+ var prev = [first_prev || parent_pos]; // connects parent with child(s), depending on the expansion
1064
1329
  var endnodes = [];
1330
+ var markers = []; // wellformed_pre_test: branch markers, drawn in one row below all branches
1065
1331
  var sname = sym_name(root.tagName,root);
1066
1332
  var root_expansion = illustrator.elements[root.tagName].expansion(root);
1067
1333
  var block = { 'max': {'row': pos.row, 'col': pos.col}, 'endnodes': [], 'svg': $X('<g class="group" xmlns="http://www.w3.org/2000/svg"/>') };
@@ -1080,10 +1346,12 @@ function WfDescription(wf_adaptor, wf_illustrator) { // Model {{{
1080
1346
  labels.push({...{row: pos.row, element_id: 'start', tname: 'start', label: illustrator.elements[sname].label(root)},...illustrator.draw.get_y(pos.row)});
1081
1347
  }
1082
1348
  illustrator.global_style = (illustrator.elements[sname].style && root) ? illustrator.elements[sname].style(root) : {};
1083
- illustrator.draw.draw_symbol(sname, 'description', 'START', pos.row, pos.row, pos.row, pos.col, block.svg, false, {}, {})
1349
+ illustrator.draw.draw_symbol(sname, 'description', '', pos.row, pos.row, pos.row, pos.col, block.svg, false, {}, {})
1084
1350
  } // }}}
1085
1351
 
1086
- $(root).children().filter(function(){ return this.localName[0] != '_'; }).each(function() {
1352
+ var children = $(root).children().filter(function(){ return this.localName[0] != '_'; });
1353
+ if (illustrator.inverted && root_expansion != 'horizontal') { children = $(children.get().reverse()); } // wellformed_pre_test: everything inside is drawn in reverse order, but not the branches
1354
+ children.each(function() {
1087
1355
  var context = this;
1088
1356
  var tname = context.tagName;
1089
1357
  var sname = sym_name(tname,context);
@@ -1096,32 +1364,52 @@ function WfDescription(wf_adaptor, wf_illustrator) { // Model {{{
1096
1364
  if(root_expansion == 'horizontal') {
1097
1365
  // for noindent themes do not indent the first column
1098
1366
  if (!pos.noindent || (pos.noindent && endnodes.length > 1)) {
1099
- illustrator.dim.set_x(pos.row,pos.col,illustrator.width,sname);
1367
+ illustrator.dim.set_x(pos.row,pos.col,illustrator.width);
1100
1368
  pos.col++;
1101
1369
  }
1102
1370
  if (!illustrator.compact) {
1103
1371
  if (block.max.row) {
1104
- illustrator.dim.set_x(pos.row,pos.col,illustrator.width,sname);
1372
+ illustrator.dim.set_x(pos.row,pos.col,illustrator.width);
1105
1373
  pos.row = block.max.row + 1;
1106
1374
  }
1107
1375
  }
1108
1376
  }
1109
1377
 
1378
+ var bottom = false;
1110
1379
  if(illustrator.elements[tname] != undefined && illustrator.elements[tname].type == 'complex') {
1380
+ bottom = illustrator.inverted && root_expansion == 'horizontal'; // wellformed_pre_test: branch markers are drawn below their content
1111
1381
  if(illustrator.elements[tname] != undefined && !illustrator.elements[tname].svg) pos.row--;
1112
1382
  // TODO: Remaining problem is the order inside the svg. Thats why the connection is above the icon
1113
1383
 
1114
- illustrator.dim.set_x(pos.row,pos.col,illustrator.width,sname);
1384
+ illustrator.dim.set_x(pos.row,pos.col,illustrator.width);
1385
+ illustrator.dim.set_row(pos.row,pos.col,prev);
1386
+ if ((bottom ? !illustrator.lr : (illustrator.lr && illustrator.elements[tname].expansion(context) == 'vertical' && !illustrator.elements[tname].col_shift(context))) && illustrator.elements[sname].svg) { // lr: the content follows the label in the same column, bottom: the marker is drawn after the next branches, so measure the symbol before it is drawn
1387
+ let lab = (illustrator.elements[sname].label ? illustrator.elements[sname].label(context) : []) || [];
1388
+ let tit = lab.find((l) => l && l.column == 'Label' && l.value);
1389
+ illustrator.draw.draw_symbol(sname, 'measure', tit ? tit.value : undefined, pos.row, pos.row, pos.row, pos.col, $X('<g xmlns="http://www.w3.org/2000/svg"/>'), true, {}, {});
1390
+ }
1115
1391
  // but fuuuuu, we calculate the gateways only later, so we couldnt even have them bigger for now
1116
1392
 
1117
1393
  // console.log('----> down', tname, parent_pos.row, pos.row, parent_pos.col, pos.col, illustrator.dim.debug());
1118
1394
 
1119
- let tblock = parse(context, JSON.parse(JSON.stringify(pos)));
1395
+ if (reversed_loop(context)) { illustrator.inverted = !illustrator.inverted; }
1396
+ let tblock;
1397
+ if (bottom) { // content starts in the row of the marker and connects to the gateway
1398
+ let up = JSON.parse(JSON.stringify(pos));
1399
+ up.row--;
1400
+ tblock = parse(context, up, prev[0]);
1401
+ } else {
1402
+ tblock = parse(context, JSON.parse(JSON.stringify(pos)));
1403
+ }
1404
+ if (reversed_loop(context)) { illustrator.inverted = !illustrator.inverted; }
1120
1405
 
1121
1406
  // merge block and tblock, enforce the max, as the second branch could have fewer nodes than the first
1122
1407
  block.max.row = Math.max(block.max.row,tblock.max.row);
1123
1408
  block.max.col = Math.max(block.max.col,tblock.max.col);
1124
1409
  block.endnodes = tblock.endnodes;
1410
+ if (bottom) { // markers are drawn after all branches, see below
1411
+ markers.push({ tname: tname, sname: sname, context: context, prev: tblock.endnodes, col: pos.col, top: pos.row, maxcol: tblock.max.col });
1412
+ }
1125
1413
 
1126
1414
  // console.log('<---- up', tname, parent_pos.row, block.max.row, parent_pos.col, block.max.col, illustrator.dim.debug());
1127
1415
 
@@ -1138,9 +1426,11 @@ function WfDescription(wf_adaptor, wf_illustrator) { // Model {{{
1138
1426
  // }}}
1139
1427
 
1140
1428
  var g;
1141
- set_details(tname,sname,pos,context);
1142
- var origpos = JSON.parse(JSON.stringify(pos));
1143
- [g, endnodes] = draw_position(tname,parent_pos,origpos,prev,block,endnodes,context);
1429
+ if (!bottom) {
1430
+ set_details(tname,sname,pos,context);
1431
+ var origpos = JSON.parse(JSON.stringify(pos));
1432
+ [g, endnodes] = draw_position(tname,parent_pos,origpos,prev,block,endnodes,context);
1433
+ }
1144
1434
 
1145
1435
  // Prepare next iteration {{{
1146
1436
  if(root_expansion == 'vertical') { prev = JSON.parse(JSON.stringify(endnodes)); pos.row = block.max.row;} // covers e.g. input's for alternative, parallel_branch, ... everything with horizontal expansion
@@ -1155,6 +1445,11 @@ function WfDescription(wf_adaptor, wf_illustrator) { // Model {{{
1155
1445
  pos.row++;
1156
1446
  max.row++;
1157
1447
  block.max.row = pos.row;
1448
+ // the arrows to the closing symbol belong to the loop. empty loops: the
1449
+ // straight arrow comes from the opening symbol, not from the content, and
1450
+ // the way back goes around the side, so both point the other way
1451
+ let flip = reversed_loop(context) != (illustrator.elements[csname].closeblock == true && pos.row == origpos.row + 1);
1452
+ if (flip) { illustrator.inverted = !illustrator.inverted; }
1158
1453
  if (illustrator.elements[sname].endnodes == 'this') {
1159
1454
  pos.col++;
1160
1455
  if (pos.col > max.col) {
@@ -1168,6 +1463,7 @@ function WfDescription(wf_adaptor, wf_illustrator) { // Model {{{
1168
1463
  set_details(ctname,csname,pos,context,true);
1169
1464
  [undefined, endnodes] = draw_position(ctname,parent_pos,pos,prev,block,[],context,{svg: g, pos: origpos});
1170
1465
  }
1466
+ if (flip) { illustrator.inverted = !illustrator.inverted; }
1171
1467
  if (illustrator.elements[sname].border == 'structural') {
1172
1468
  // structural border: outgoing arrow starts at the group border instead of from the icon
1173
1469
  // small y offset.
@@ -1181,8 +1477,27 @@ function WfDescription(wf_adaptor, wf_illustrator) { // Model {{{
1181
1477
  }
1182
1478
  });
1183
1479
 
1480
+ if (markers.length > 0) {
1481
+ max.row++;
1482
+ block.max.row = max.row;
1483
+ let maxcol = block.max.col;
1484
+ if (illustrator.lr) { // all markers start at the same position, after the longest branch
1485
+ markers.forEach((m) => { illustrator.dim.set_row(max.row,m.col,m.prev); });
1486
+ let start = Math.max(...markers.map((m) => illustrator.dim.get_row(max.row,m.col)));
1487
+ markers.forEach((m) => { illustrator.dim.rows[max.row][m.col].start = start; });
1488
+ }
1489
+ markers.forEach((m) => {
1490
+ let mpos = { row: max.row, col: m.col, final: false, wide: false, yoffset: 0, top: m.top, marker: true }; // the tile starts at the top of the branch
1491
+ block.endnodes = [mpos]; // the marker is the end of its branch
1492
+ block.max.col = m.maxcol; // the tile only covers its own branch
1493
+ set_details(m.tname,m.sname,mpos,m.context);
1494
+ draw_position(m.tname,parent_pos,mpos,m.prev,block,endnodes,m.context);
1495
+ });
1496
+ block.max.col = maxcol;
1497
+ }
1498
+
1184
1499
  if($(root).children().filter(function(){ return this.attributes['svg-id'] != undefined; }).length == 0) { // empty complex found
1185
- endnodes = [parent_pos];
1500
+ endnodes = [prev[0]]; // parent_pos, or for markers below their content the gateway
1186
1501
  max.row = parent_pos.row;
1187
1502
  max.col = parent_pos.col;
1188
1503
  }
@@ -1233,6 +1548,7 @@ function WfDescription(wf_adaptor, wf_illustrator) { // Model {{{
1233
1548
  // console.log('----- pos ' + sname, parent_pos.row, block.max.row, parent_pos.col, block.max.col, block, illustrator.dim.debug());
1234
1549
 
1235
1550
  // Draw Symbol {{{
1551
+ illustrator.dim.set_row(pos.row,pos.col,prev);
1236
1552
  let style = {};
1237
1553
  if (illustrator.elements[sname].style && context) {
1238
1554
  style = illustrator.elements[sname].style(context);
@@ -1265,7 +1581,7 @@ function WfDescription(wf_adaptor, wf_illustrator) { // Model {{{
1265
1581
  if (illustrator.elements[sname].closing_symbol) {
1266
1582
  illustrator.draw.draw_tile($(context).attr('svg-id'), pos, { col: wide, row: block.max.row + 1 }, block.svg, yshrink);
1267
1583
  } else {
1268
- illustrator.draw.draw_tile($(context).attr('svg-id'), pos, { col: wide, row: block.max.row }, block.svg, yshrink);
1584
+ illustrator.draw.draw_tile($(context).attr('svg-id'), pos.top ? { row: pos.top, col: pos.col } : pos, { col: wide, row: block.max.row }, block.svg, yshrink); // top: marker below its content
1269
1585
  }
1270
1586
  }
1271
1587
  }
@@ -1287,6 +1603,9 @@ function WfDescription(wf_adaptor, wf_illustrator) { // Model {{{
1287
1603
  connection_case = 1;
1288
1604
  // console.log('++ case 1',prev[node].row,pos.row,illustrator.dim.debug());
1289
1605
  illustrator.draw.draw_connection(block.svg, pos, second.pos, 0, true);
1606
+ } else { // no content: the way back goes around the side
1607
+ connection_case = 7;
1608
+ illustrator.draw.draw_connection(block.svg, pos, second.pos, 0, true, true);
1290
1609
  }
1291
1610
  } else {
1292
1611
  for (let node=0; node < block.endnodes.length; node++) {
@@ -1305,9 +1624,9 @@ function WfDescription(wf_adaptor, wf_illustrator) { // Model {{{
1305
1624
  connection_case = 3;
1306
1625
  illustrator.draw.draw_connection(block.svg, { row: 1, col: 1, yoffset: 0 }, conn_target, 0, true);
1307
1626
  } else {
1308
- if (illustrator.elements[sname].noarrow == undefined || illustrator.elements[sname].noarrow == false) {
1627
+ if (illustrator.elements[sname].noarrow == undefined || illustrator.elements[sname].noarrow == false || illustrator.inverted) { // inverted: the arrow points from the marker to the content
1309
1628
  for (let node=0; node < prev.length; node++) {
1310
- if (prev[node] && !prev[node].final) {
1629
+ if (prev[node] && !(illustrator.inverted ? pos.final : prev[node].final)) { // inverted: nothing goes out of a final element, which is drawn as the target
1311
1630
  if (prev[node].wide) {
1312
1631
  var pn = JSON.parse(JSON.stringify(prev[node]));
1313
1632
  if (pos.col > prev[node].col) {
@@ -1320,10 +1639,10 @@ function WfDescription(wf_adaptor, wf_illustrator) { // Model {{{
1320
1639
  connection_case = 5;
1321
1640
  if (prev.length == 1) {
1322
1641
  // console.log('++ case 5a',parent_pos.row, '---', prev[node].row,pos.row,illustrator.dim.debug());
1323
- illustrator.draw.draw_connection(block.svg, prev[node], conn_target, 0, true);
1642
+ illustrator.draw.draw_connection(block.svg, prev[node], conn_target, 0, !prev[node].marker);
1324
1643
  } else {
1325
1644
  // console.log('++ case 5b',prev[node].row,pos.row,illustrator.dim.debug());
1326
- illustrator.draw.draw_connection(block.svg, prev[node], conn_target, 0, true);
1645
+ illustrator.draw.draw_connection(block.svg, prev[node], conn_target, 0, !prev[node].marker); // no arrow into markers drawn below their content
1327
1646
  }
1328
1647
  }
1329
1648
  }