drakongen 1.4.7 → 1.7.0

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 (105) hide show
  1. package/browser/drakongen.js +945 -375
  2. package/browsertest.html +9 -1
  3. package/buildexamples.js +51 -4
  4. package/exampleproject/foo.txt +2 -0
  5. package/examples/20. /320/227/320/276/320/273/320/276/321/202/320/276/320/271 - /320/273/320/265/320/262/321/213/320/271 final.drakon" +66 -0
  6. package/examples/21. /320/227/320/276/320/273/320/276/321/202/320/276/320/271 - /320/277/321/200/320/260/320/262/321/213/320/271 final.drakon" +66 -0
  7. package/examples/Complex arrow-parallel.drakon +116 -0
  8. package/examples/Complex arrow-parallel.txt +40 -0
  9. package/examples/Complex arrow.txt +1 -0
  10. package/examples/How to tune PID on a quadcopter.txt +1 -0
  11. package/examples/NestedArrowLoop.drakon +38 -0
  12. package/examples/NestedArrowLoop.txt +20 -0
  13. package/examples/all icons.drakon +227 -0
  14. package/examples/all icons.txt +68 -0
  15. package/examples/ar01-action.drakon +31 -0
  16. package/examples/ar01-action.txt +9 -0
  17. package/examples/ar10-parallel.drakon +70 -0
  18. package/examples/ar10-parallel.txt +20 -0
  19. package/examples/badShortCircuit.drakon +34 -0
  20. package/examples/badShortCircuit.txt +7 -0
  21. package/examples/getToken.txt +2 -0
  22. package/examples/monitorPaymentStatus.drakon +56 -0
  23. package/examples/monitorPaymentStatus.txt +25 -0
  24. package/examples/no-loop/00.Empty.txt +6 -0
  25. package/examples/no-loop/01. /320/221/320/265/320/273/321/213/320/271.txt" +5 -0
  26. package/examples/no-loop/02. /320/247/321/221/321/200/320/275/321/213/320/271.txt" +8 -0
  27. package/examples/no-loop/03. /320/241/320/265/321/200/321/213/320/271.txt" +7 -0
  28. package/examples/no-loop/04. /320/221/321/203/321/200/321/213/320/271.txt" +7 -0
  29. package/examples/no-loop/05. /320/226/321/221/320/273/321/202/321/213/320/271.txt" +7 -0
  30. package/examples/no-loop/06. /320/221/320/260/320/263/321/200/320/276/320/262/321/213/320/271.txt" +9 -0
  31. package/examples/no-loop/07. /320/244/320/270/320/276/320/273/320/265/321/202/320/276/320/262/321/213/320/271.txt" +12 -0
  32. package/examples/no-loop/08. /320/221/320/270/321/200/321/216/320/267/320/276/320/262/321/213/320/271.txt" +7 -0
  33. package/examples/no-loop/09. /320/236/321/200/320/260/320/275/320/266/320/265/320/262/321/213/320/271.txt" +9 -0
  34. package/examples/no-loop/10. /320/240/320/276/320/267/320/276/320/262/321/213/320/271.txt" +13 -0
  35. package/examples/no-loop/11. /320/227/320/260/321/211/320/270/321/202/320/275/321/213/320/271.txt" +9 -0
  36. package/examples/no-loop/12. /320/221/320/276/320/273/320/276/321/202/320/275/321/213/320/271.txt" +9 -0
  37. package/examples/no-loop/13. /320/241/320/260/320/273/320/260/321/202/320/276/320/262/321/213/320/271.txt" +15 -0
  38. package/examples/no-loop/14. /320/227/320/276/320/273/320/276/321/202/320/276/320/271.txt" +17 -0
  39. package/examples/no-loop/15. /320/241/320/270/320/275/320/270/320/271.txt" +20 -0
  40. package/examples/no-loop/16. /320/223/320/276/320/273/321/203/320/261/320/276/320/271.txt" +11 -0
  41. package/examples/no-loop/17. /320/241/320/260/320/273/320/260/321/202/320/276/320/262/321/213/320/271.txt" +10 -0
  42. package/examples/no-loop/18. /320/241/321/202/320/260/320/273/321/214/320/275/320/276/320/271.txt" +15 -0
  43. package/examples/no-loop/19. Lilla.txt +11 -0
  44. package/examples/no-loop/20. /320/227/320/276/320/273/320/276/321/202/320/276/320/271 - /320/273/320/265/320/262/321/213/320/271 final.txt" +16 -0
  45. package/examples/no-loop/21. /320/227/320/276/320/273/320/276/321/202/320/276/320/271 - /320/277/321/200/320/260/320/262/321/213/320/271 final.txt" +17 -0
  46. package/examples/no-loop/Adaptive design.txt +31 -0
  47. package/examples/no-loop/And test.txt +9 -0
  48. package/examples/no-loop/Comments.txt +19 -0
  49. package/examples/no-loop/Logical_And.txt +9 -0
  50. package/examples/no-loop/Logical_And_Inv.txt +9 -0
  51. package/examples/no-loop/Logical_And_Inv_x2.txt +15 -0
  52. package/examples/no-loop/Logical_Or.txt +9 -0
  53. package/examples/no-loop/Logical_Or_Inv.txt +9 -0
  54. package/examples/no-loop/Mind one.txt +18 -0
  55. package/examples/no-loop/Or test.txt +9 -0
  56. package/examples/no-loop/Silhouette test 1.txt +31 -0
  57. package/examples/no-loop/badShortCircuit.txt +7 -0
  58. package/examples/no-loop/getToken.txt +34 -0
  59. package/examples/no-loop/monitorPaymentStatus.txt +25 -0
  60. package/examples/no-loop/sil2.txt +29 -0
  61. package/examples/no-loop/tmp.txt +35 -0
  62. package/examples/no-loop//320/241/320/265/320/274/320/260/320/275/321/202/320/270/321/207/320/265/321/201/320/272/320/276/320/265 /321/217/320/264/321/200/320/276.txt" +36 -0
  63. package/examples/parallel-3.drakon +49 -0
  64. package/examples/parallel-3.txt +12 -0
  65. package/examples/parallel-arrow-loop.drakon +44 -0
  66. package/examples/parallel-arrow-loop.txt +12 -0
  67. package/examples/parallel-empty-nested.drakon +40 -0
  68. package/examples/parallel-empty-nested.txt +11 -0
  69. package/examples/parallel-empty.drakon +27 -0
  70. package/examples/parallel-empty.txt +8 -0
  71. package/examples/parallel-nested.drakon +67 -0
  72. package/examples/parallel-nested.txt +16 -0
  73. package/examples/sil2.txt +1 -0
  74. package/examples/tmp.txt +2 -0
  75. package/examples//320/241/320/265/320/274/320/260/320/275/321/202/320/270/321/207/320/265/321/201/320/272/320/276/320/265 /321/217/320/264/321/200/320/276.free" +130 -0
  76. package/examples//320/241/320/265/320/274/320/260/320/275/321/202/320/270/321/207/320/265/321/201/320/272/320/276/320/265 /321/217/320/264/321/200/320/276.txt" +36 -0
  77. package/package.json +1 -1
  78. package/prompts/noloop/decrement_if_count.drakon +34 -0
  79. package/prompts/noloop/flow_no_loop.drakon +26 -0
  80. package/prompts/noloop/group_stack_by_id.drakon +56 -0
  81. package/prompts/noloop/increment_if_count.drakon +34 -0
  82. package/prompts/noloop/merge_incoming_branches.drakon +105 -0
  83. package/prompts/noloop/recurse_traversal.drakon +88 -0
  84. package/prompts/noloop/traverse_node.drakon +62 -0
  85. package/secondary/Secondary.drakon +72 -0
  86. package/src/browserTools.js +1 -1
  87. package/src/drakonToPromptStruct.js +4 -3
  88. package/src/drakonToStruct.js +170 -15
  89. package/src/drakongen.js +44 -19
  90. package/src/free.js +69 -0
  91. package/src/index.js +42 -15
  92. package/src/main.js +10 -4
  93. package/src/nodeTools.js +1 -1
  94. package/src/noloop.js +181 -0
  95. package/src/printPseudo.js +29 -3
  96. package/src/structFlow.js +381 -334
  97. package/src/technicalTree.js +24 -1
  98. package/src/translate.js +21 -3
  99. package/src/treeTools.js +21 -0
  100. package/test_secondary.js +14 -0
  101. package/.vscode/launch.json +0 -18
  102. package/exampleproject/.drakontech/history.json +0 -12
  103. package/exampleproject/.drakontech/opened.txt +0 -1
  104. package/examples/.drakontech/history.json +0 -124
  105. package/examples/.drakontech/opened.txt +0 -1
@@ -14,7 +14,7 @@ function htmlToString(html) {
14
14
 
15
15
  root.childNodes.forEach((node) => {
16
16
  if (node.tagName === 'P') {
17
- output.push(node.textContent.trim());
17
+ output.push(node.textContent);
18
18
  } else if (node.tagName === 'UL') {
19
19
  node.childNodes.forEach((item) => {
20
20
  if (item.tagName === 'LI') {
@@ -39,16 +39,17 @@ const {drakonToStruct} = require("./drakonToStruct");
39
39
  const {printPseudo, printWithIndent, makeIndent} = require('./printPseudo');
40
40
  const {addRange, sortByProperty} = require("./tools")
41
41
 
42
- function drakonToPseudocode(drakonJson, name, filename, htmlToString, translate) {
43
- var diagram = drakonToStruct(drakonJson, name, filename, translate)
42
+ function drakonToPseudocode(drakonJson, name, filename, htmlToString, translate, options) {
43
+ var diagram = drakonToStruct(drakonJson, name, filename, translate, htmlToString, options)
44
44
  var lines = []
45
45
 
46
46
  lines.push("## " + translate("Procedure") + " \"" + diagram.name + "\"")
47
+ lines.push("")
47
48
  if (diagram.params) {
48
49
  lines.push(translate("Parameters") + ":")
49
50
  addRange(lines, htmlToString(diagram.params))
51
+ lines.push("")
50
52
  }
51
- lines.push("")
52
53
  lines.push(translate("Algorithm") + ":")
53
54
 
54
55
  if (diagram.branches.length === 0) {
@@ -153,13 +154,21 @@ function createMindNode(name) {
153
154
  }
154
155
 
155
156
  module.exports = { drakonToPseudocode, mindToTree };
156
- },{"./drakonToStruct":3,"./printPseudo":5,"./tools":8}],3:[function(require,module,exports){
157
+ },{"./drakonToStruct":3,"./printPseudo":7,"./tools":10}],3:[function(require,module,exports){
157
158
  const { structFlow, redirectNode } = require("./structFlow");
158
159
  const { createError, remove } = require("./tools");
159
160
 
160
161
  var translate;
161
162
 
162
- function drakonToStruct(drakonJson, name, filename, translateFunction) {
163
+ function drakonToStruct(
164
+ drakonJson,
165
+ name,
166
+ filename,
167
+ translateFunction,
168
+ htmlToString,
169
+ options,
170
+ ) {
171
+ options = options || {};
163
172
  translate = translateFunction;
164
173
  let drakonGraph;
165
174
  try {
@@ -186,23 +195,112 @@ function drakonToStruct(drakonJson, name, filename, translateFunction) {
186
195
  };
187
196
  }
188
197
 
189
- buildTwoWayConnections(nodes, firstNodeId);
198
+ handleParallel(nodes, undefined, firstNodeId, {}, undefined);
199
+ buildTwoWayConnections(nodes, firstNodeId, htmlToString);
190
200
 
191
201
  rewireSelectsMarkLoops(nodes, filename);
192
202
  branches.forEach((branch) =>
193
- checkBranchIsReferenced(branch, firstNodeId, filename),
203
+ checkBranchIsReferenced(
204
+ branch,
205
+ firstNodeId,
206
+ filename,
207
+ options,
208
+ htmlToString,
209
+ ),
194
210
  );
195
211
  rewireShortcircuit(nodes, filename);
196
212
  branches.forEach((branch) => cutOffBranch(nodes, branch));
197
- var branchTrees = structFlow(nodes, branches, filename, translate);
213
+ var branchTrees = structFlow(nodes, branches, filename, translate, options);
198
214
  return {
199
215
  name: name,
200
216
  params: drakonGraph.params || "",
201
217
  description: drakonGraph.description || "",
202
218
  branches: branchTrees,
219
+ secondary: findSecondary(branchTrees, options, htmlToString),
203
220
  };
204
221
  }
205
222
 
223
+ function findSecondary(branchTrees, options, htmlToString) {
224
+ if (!options || !options.secondary) {
225
+ return undefined;
226
+ }
227
+ var ordinal = 0;
228
+ for (var branch of branchTrees) {
229
+ var name = htmlToString(branch.name)[0];
230
+ if (name === options.secondary) {
231
+ return ordinal;
232
+ }
233
+ ordinal++;
234
+ }
235
+ return undefined;
236
+ }
237
+
238
+ function handleParallel(nodes, prevNode, nodeId, visited, proc) {
239
+ if (!nodeId) {
240
+ return;
241
+ }
242
+ var node = nodes[nodeId];
243
+ if (node.type === "parend") {
244
+ if (!proc) {
245
+ throw new Error("handleParallel: no proc for parend");
246
+ }
247
+ var endId = proc.end;
248
+ var end;
249
+ if (endId) {
250
+ end = nodes[endId];
251
+ } else {
252
+ end = {
253
+ type: "end",
254
+ id: proc.id + "-" + proc.ordinal + "-end",
255
+ prev: [],
256
+ };
257
+ nodes[end.id] = end;
258
+ proc.end = end.id;
259
+ proc.next = node.one;
260
+ }
261
+ redirectNode(nodes, prevNode, nodeId, end.id);
262
+ return;
263
+ }
264
+ if (nodeId in visited) {
265
+ return;
266
+ }
267
+ visited[nodeId] = true;
268
+ if (node.type === "parbegin") {
269
+ node.procs = [];
270
+ var ordinal = 0;
271
+ var current = node;
272
+ while (true) {
273
+ var start = {
274
+ id: nodeId + "-" + ordinal + "-start",
275
+ type: "action",
276
+ prev: [],
277
+ one: current.one,
278
+ };
279
+ nodes[start.id] = start;
280
+ var childProc = {
281
+ id: nodeId,
282
+ ordinal: ordinal,
283
+ start: start.id,
284
+ };
285
+ var next = current.two;
286
+ node.procs.push(childProc);
287
+ handleParallel(nodes, start, start.one, {}, childProc);
288
+ delete current.one;
289
+ delete current.two;
290
+ if (!next) {
291
+ break;
292
+ }
293
+ current = nodes[next];
294
+ ordinal++;
295
+ }
296
+ node.one = node.procs[0].next;
297
+ handleParallel(nodes, node, node.one, visited, proc);
298
+ } else {
299
+ handleParallel(nodes, node, node.one, visited, proc);
300
+ handleParallel(nodes, node, node.two, visited, proc);
301
+ }
302
+ }
303
+
206
304
  function drakonToGraph(drakonJson, name, filename, translateFunction) {
207
305
  translate = translateFunction;
208
306
  let drakonGraph;
@@ -227,7 +325,13 @@ function drakonToGraph(drakonJson, name, filename, translateFunction) {
227
325
  rewireSelectsMarkLoops(nodes, filename);
228
326
  rewireShortcircuit(nodes, filename);
229
327
  branches.forEach((branch) =>
230
- checkBranchIsReferenced(branch, firstNodeId, filename),
328
+ checkBranchIsReferenced(
329
+ branch,
330
+ firstNodeId,
331
+ filename,
332
+ undefined,
333
+ undefined,
334
+ ),
231
335
  );
232
336
  branches.forEach((branch) => cutOffBranch(nodes, branch));
233
337
 
@@ -241,16 +345,35 @@ function drakonToGraph(drakonJson, name, filename, translateFunction) {
241
345
  };
242
346
  }
243
347
 
244
- function checkBranchIsReferenced(branch, firstNodeId, filename) {
348
+ function checkBranchIsReferenced(
349
+ branch,
350
+ firstNodeId,
351
+ filename,
352
+ options,
353
+ htmlToString,
354
+ ) {
245
355
  if (branch.id === firstNodeId) {
246
356
  return;
247
357
  }
248
- if (branch.prev.length === 0) {
249
- throw createError(
250
- translate("A silhouette branch is not referenced"),
251
- filename,
252
- branch.id,
253
- );
358
+ if (options && htmlToString) {
359
+ var branchName = htmlToString(branch.content)[0];
360
+ if (branchName === options.secondary) {
361
+ if (branch.prev.length > 0) {
362
+ throw createError(
363
+ translate("A secondary branch is referenced"),
364
+ filename,
365
+ branch.id,
366
+ );
367
+ }
368
+ } else {
369
+ if (branch.prev.length === 0) {
370
+ throw createError(
371
+ translate("A silhouette branch is not referenced"),
372
+ filename,
373
+ branch.id,
374
+ );
375
+ }
376
+ }
254
377
  }
255
378
  }
256
379
 
@@ -327,14 +450,18 @@ function addFakeEnd(nodes, prev, node, end, addresses) {
327
450
  node.prev = remove(node.prev, prev.id);
328
451
  }
329
452
 
330
- function buildTwoWayConnections(nodes, firstNodeId) {
453
+ function buildTwoWayConnections(nodes, firstNodeId, htmlToString) {
331
454
  for (var id in nodes) {
332
455
  var node = nodes[id];
333
456
  node.id = id;
334
457
  node.prev = [];
335
458
  }
336
459
 
337
- traverse(nodes, firstNodeId, {}, connectBack);
460
+ var visitor = function (nodes, node) {
461
+ return connectBack(nodes, node, htmlToString);
462
+ };
463
+
464
+ traverse(nodes, firstNodeId, {}, visitor);
338
465
  }
339
466
 
340
467
  function findStartNode(nodes, filename, branches) {
@@ -373,6 +500,9 @@ function findStartNode(nodes, filename, branches) {
373
500
  id,
374
501
  );
375
502
  }
503
+ } else if (node.final) {
504
+ delete node.one
505
+ delete node.two
376
506
  }
377
507
  }
378
508
 
@@ -395,6 +525,7 @@ function rewireSelectsMarkLoops(nodes, filename) {
395
525
 
396
526
  function rewireSelect(nodes, selectNode, filename) {
397
527
  var caseNodeId = selectNode.one;
528
+ var caseNode0 = nodes[caseNodeId];
398
529
  while (caseNodeId) {
399
530
  var caseNode = nodes[caseNodeId];
400
531
  caseNodeId = caseNode.two;
@@ -434,6 +565,7 @@ function rewireSelect(nodes, selectNode, filename) {
434
565
  removeNodeOne(nodes, caseNode.id);
435
566
  }
436
567
  }
568
+ caseNode0.side = selectNode.side;
437
569
  removeNodeOne(nodes, selectNode.id);
438
570
  }
439
571
 
@@ -608,9 +740,14 @@ function traverse(nodes, nodeId, visited, action) {
608
740
  action(nodes, node);
609
741
  traverse(nodes, node.one, visited, action);
610
742
  traverse(nodes, node.two, visited, action);
743
+ if (node.procs) {
744
+ for (var proc of node.procs) {
745
+ traverse(nodes, proc.start, visited, action);
746
+ }
747
+ }
611
748
  }
612
749
 
613
- function connectBack(nodes, node) {
750
+ function connectBack(nodes, node, htmlToString) {
614
751
  if (node.one) {
615
752
  var one = nodes[node.one];
616
753
  one.prev.push(node.id);
@@ -619,6 +756,25 @@ function connectBack(nodes, node) {
619
756
  var two = nodes[node.two];
620
757
  two.prev.push(node.id);
621
758
  }
759
+
760
+ if (node.side) {
761
+ var side = nodes[node.side].content;
762
+ if (side) {
763
+ node.side = decodeSide(side, htmlToString);
764
+ } else {
765
+ delete node.side;
766
+ }
767
+ }
768
+ }
769
+
770
+ function decodeSide(content, htmlToString) {
771
+ var text = htmlToString(content);
772
+ var oneLine = text.join(" ");
773
+ if (oneLine.indexOf("=") === -1) {
774
+ return translate("Do for") + " " + oneLine;
775
+ } else {
776
+ return translate("Start at") + " " + oneLine;
777
+ }
622
778
  }
623
779
 
624
780
  function markLoopBody(nodes, start, filename) {
@@ -641,32 +797,310 @@ function markLoopBody(nodes, start, filename) {
641
797
 
642
798
  module.exports = { drakonToStruct, drakonToGraph };
643
799
 
644
- },{"./structFlow":6,"./tools":8}],4:[function(require,module,exports){
645
- const { drakonToPseudocode, mindToTree } = require('./drakonToPromptStruct');
646
- const { htmlToString } = require("./browserTools")
647
- const { setUpLanguage, translate } = require("./translate")
800
+ },{"./structFlow":8,"./tools":10}],4:[function(require,module,exports){
801
+ const { drakonToPseudocode, mindToTree } = require("./drakonToPromptStruct");
802
+ const { htmlToString } = require("./browserTools");
803
+ const { setUpLanguage, translate } = require("./translate");
648
804
  const { drakonToStruct } = require("./drakonToStruct");
649
-
805
+ const { freeDiagramToText } = require("./free");
650
806
 
651
807
  window.drakongen = {
652
- toPseudocode: function (drakonJson, name, filename, language) {
653
- setUpLanguage(language)
654
- return drakonToPseudocode(drakonJson, name, filename, htmlToString, translate).text
655
- },
808
+ toPseudocode: function (drakonJson, name, filename, language) {
809
+ setUpLanguage(language);
810
+ return drakonToPseudocode(
811
+ drakonJson,
812
+ name,
813
+ filename,
814
+ htmlToString,
815
+ translate,
816
+ ).text;
817
+ },
818
+
819
+ toMindTree: function (mindJson, name, filename, language) {
820
+ setUpLanguage(language);
821
+ var result = mindToTree(mindJson, name, filename, htmlToString);
822
+ return result.text;
823
+ },
824
+
825
+ freeToText: function (freeJson, name, filename, language) {
826
+ setUpLanguage(language);
827
+ var result = freeDiagramToText(
828
+ freeJson,
829
+ name,
830
+ filename,
831
+ translate,
832
+ htmlToString,
833
+ );
834
+ return result.text;
835
+ },
836
+
837
+ toTree: function (drakonJson, name, filename, language, options) {
838
+ setUpLanguage(language);
839
+ var result = drakonToStruct(
840
+ drakonJson,
841
+ name,
842
+ filename,
843
+ translate,
844
+ htmlToString,
845
+ options,
846
+ );
847
+ return JSON.stringify(result, null, 4);
848
+ },
849
+ };
850
+
851
+ },{"./browserTools":1,"./drakonToPromptStruct":2,"./drakonToStruct":3,"./free":5,"./translate":11}],5:[function(require,module,exports){
852
+ var {addRange} = require("./tools")
853
+ const { createError } = require("./tools");
656
854
 
657
- toMindTree: function (mindJson, name, filename, language) {
658
- setUpLanguage(language)
659
- var result = mindToTree(mindJson, name, filename, htmlToString)
660
- return result.text
661
- },
855
+ var translate
662
856
 
663
- toTree: function (drakonJson, name, filename, language) {
664
- setUpLanguage(language)
665
- var result = drakonToStruct(drakonJson, name, filename, translate)
666
- return JSON.stringify(result, null, 4)
857
+ function compareVertically(box1, box2) {
858
+ if (box1.top + box1.height <= box2.top) return -1;
859
+ if (box2.top + box2.height <= box1.top) return 1;
860
+ return 0;
861
+ }
862
+
863
+ function compareHorizontally(box1, box2) {
864
+ if (box1.left + box1.width <= box2.left) return -1;
865
+ if (box2.left + box2.width <= box1.left) return 1;
866
+ return 0;
867
+ }
868
+
869
+ function byTopLeft(box1, box2) {
870
+ var vertical = compareVertically(box1, box2)
871
+ if (vertical == 0) {
872
+ return compareHorizontally(box1, box2)
873
+ }
874
+ return vertical
875
+ }
876
+
877
+ function parseDiagram(freeJson, filename) {
878
+ let diagram;
879
+ try {
880
+ freeJson = freeJson || ""
881
+ freeJson = freeJson.trim()
882
+ freeJson = freeJson || "{}"
883
+ diagram = JSON.parse(freeJson);
884
+ } catch (error) {
885
+ var message = translate("Error parsing JSON") + ": " + error.message
886
+ throw createError(message, filename)
887
+ }
888
+ return diagram
889
+ }
890
+
891
+ function sortedItems(diagram) {
892
+ var items = diagram.items || {};
893
+ var result = [];
894
+ for (var id in items) {
895
+ var item = items[id];
896
+ if (item.content && item.top && item.left && item.width && item.height) {
897
+ result.push(item);
898
+ }
667
899
  }
900
+ result.sort(byTopLeft);
901
+ return result;
668
902
  }
669
- },{"./browserTools":1,"./drakonToPromptStruct":2,"./drakonToStruct":3,"./translate":9}],5:[function(require,module,exports){
903
+
904
+ function freeDiagramToText(freeJson, name, filename, translateFunction, htmlToString) {
905
+ translate = translateFunction
906
+ var diagram = parseDiagram(freeJson, filename)
907
+ var sorted = sortedItems(diagram)
908
+ var lines = []
909
+ lines.push("## " + name)
910
+ lines.push("")
911
+ for (var item of sorted) {
912
+ var content = htmlToString(item.content)
913
+ addRange(lines, content)
914
+ lines.push("")
915
+ }
916
+ var text = lines.join("\n")
917
+ return {text:text}
918
+ }
919
+
920
+ module.exports = {freeDiagramToText}
921
+ },{"./tools":10}],6:[function(require,module,exports){
922
+ "use strict";
923
+
924
+ function decrement_if_count(context, node) {
925
+ var i;
926
+ var if_id;
927
+ var if_node;
928
+
929
+ for (i = 0; i < node.stack.length; i++) {
930
+ if_id = node.stack[i];
931
+ if_node = context.nodes[if_id];
932
+ if_node.branching--;
933
+ }
934
+ }
935
+
936
+ function flow_no_loop(nodes, start_node_id) {
937
+ var context;
938
+
939
+ context = {
940
+ nodes: nodes
941
+ };
942
+
943
+ traverse_node(
944
+ context,
945
+ start_node_id,
946
+ []
947
+ );
948
+ }
949
+
950
+ function group_stack_by_id(stack) {
951
+ var counts_by_id;
952
+ var i;
953
+ var element;
954
+ var existing;
955
+
956
+ counts_by_id = {};
957
+
958
+ for (i = 0; i < stack.length; i++) {
959
+ element = stack[i];
960
+
961
+ if (element in counts_by_id) {
962
+ existing = counts_by_id[element];
963
+ } else {
964
+ existing = 0;
965
+ }
966
+
967
+ counts_by_id[element] = existing + 1;
968
+ }
969
+
970
+ return counts_by_id;
971
+ }
972
+
973
+ function increment_if_count(context, node) {
974
+ var i;
975
+ var if_id;
976
+ var if_node;
977
+
978
+ for (i = 0; i < node.stack.length; i++) {
979
+ if_id = node.stack[i];
980
+ if_node = context.nodes[if_id];
981
+ if_node.branching++;
982
+ }
983
+ }
984
+
985
+ function merge_incoming_branches(context, node_id, node, stack) {
986
+ var common;
987
+ var counts_by_id;
988
+ var processed_stack;
989
+ var ids;
990
+ var i;
991
+ var id;
992
+ var count;
993
+ var if_node;
994
+
995
+ common = node.stack.concat(stack);
996
+ counts_by_id = group_stack_by_id(common);
997
+ processed_stack = [];
998
+
999
+ ids = Object.keys(counts_by_id);
1000
+
1001
+ for (i = 0; i < ids.length; i++) {
1002
+ id = ids[i];
1003
+ count = counts_by_id[id];
1004
+
1005
+ if_node = context.nodes[id];
1006
+
1007
+ if (!if_node.next) {
1008
+ if (if_node.branching === 1) {
1009
+ if_node.next = node_id;
1010
+ } else {
1011
+ if_node.branching -= count - 1;
1012
+
1013
+ if (if_node.branching === 1) {
1014
+ if_node.next = node_id;
1015
+ } else {
1016
+ processed_stack.push(id);
1017
+ }
1018
+ }
1019
+ }
1020
+ }
1021
+
1022
+ node.stack = processed_stack;
1023
+ }
1024
+
1025
+ function recurse_traversal(context, node_id, node) {
1026
+ var stack1;
1027
+ var stack2;
1028
+
1029
+ if (node.type === "question") {
1030
+ increment_if_count(
1031
+ context,
1032
+ node
1033
+ );
1034
+
1035
+ stack1 = node.stack.slice();
1036
+ stack1.push(node_id);
1037
+
1038
+ stack2 = node.stack.slice();
1039
+ stack2.push(node_id);
1040
+
1041
+ traverse_node(
1042
+ context,
1043
+ node.one,
1044
+ stack1
1045
+ );
1046
+
1047
+ traverse_node(
1048
+ context,
1049
+ node.two,
1050
+ stack2
1051
+ );
1052
+ } else {
1053
+ if (node.final) {
1054
+ decrement_if_count(
1055
+ context,
1056
+ node
1057
+ );
1058
+ } else {
1059
+ stack1 = node.stack.slice();
1060
+
1061
+ traverse_node(
1062
+ context,
1063
+ node.one,
1064
+ stack1
1065
+ );
1066
+ }
1067
+ }
1068
+ }
1069
+
1070
+ function traverse_node(context, node_id, stack) {
1071
+ var node;
1072
+
1073
+ if (node_id) {
1074
+ node = context.nodes[node_id];
1075
+
1076
+ if (!node.stack) {
1077
+ node.stack = [];
1078
+ node.refs = node.prev.length;
1079
+ }
1080
+
1081
+ node.refs--;
1082
+
1083
+ merge_incoming_branches(
1084
+ context,
1085
+ node_id,
1086
+ node,
1087
+ stack
1088
+ );
1089
+
1090
+ if (!(node.refs > 0)) {
1091
+ recurse_traversal(
1092
+ context,
1093
+ node_id,
1094
+ node
1095
+ );
1096
+ }
1097
+ }
1098
+ }
1099
+
1100
+ module.exports = {
1101
+ flow_no_loop: flow_no_loop
1102
+ };
1103
+ },{}],7:[function(require,module,exports){
670
1104
  var {addRange} = require("./tools")
671
1105
 
672
1106
  function makeIndent(depth) {
@@ -679,8 +1113,11 @@ function printWithIndent(lines, indent, output) {
679
1113
  }
680
1114
 
681
1115
  function printPseudo(algorithm, translate, output, htmlToString) {
682
- function printStructuredContent(content, indent, output) {
1116
+ function printStructuredContent(content, indent, output, side) {
683
1117
  var lines = printStructuredContentNoIdent(content)
1118
+ if (side) {
1119
+ lines[0] = side + ": " + lines[0]
1120
+ }
684
1121
  printWithIndent(lines, indent, output)
685
1122
  }
686
1123
 
@@ -744,19 +1181,39 @@ function printPseudo(algorithm, translate, output, htmlToString) {
744
1181
  printError(step, indent, output)
745
1182
  } else if (step.type === "break") {
746
1183
  output.push(indent + translate("break"))
1184
+ } else if (step.type === "parbegin") {
1185
+ printParbegin(step, depth, output)
747
1186
  } else {
748
1187
  printOther(step, indent, output)
749
1188
  }
750
1189
  }
751
1190
  }
1191
+
1192
+ function printParbegin(step, depth, output) {
1193
+ const indent2 = makeIndent(depth + 1)
1194
+ const indent = makeIndent(depth)
1195
+ printWithIndent([translate("Group of parallel processes")], indent, output)
1196
+ for (var proc of step.procs) {
1197
+ printWithIndent([translate("Parallel process") + " " + (proc.ordinal + 1)], indent2, output)
1198
+ printSteps(proc.body, depth + 2, output)
1199
+ }
1200
+ }
752
1201
 
753
1202
  function printOther(step, indent, output) {
1203
+ var side = step.side
754
1204
  if (!step.content && !step.secondary) {return}
755
1205
  if (step.secondary) {
756
- printStructuredContent(step.secondary, indent, output)
1206
+ printStructuredContent(step.secondary, indent, output, side)
1207
+ side = undefined
757
1208
  }
758
1209
  if (step.content) {
759
- printStructuredContent(step.content, indent, output)
1210
+ var content = step.content
1211
+ if (step.type === "pause") {
1212
+ content = translate("Pause") + " " + htmlToString(content).join(" ")
1213
+ } else if (step.type === "timer") {
1214
+ content = translate("Start timer") + " " + htmlToString(content).join(" ")
1215
+ }
1216
+ printStructuredContent(content, indent, output, side)
760
1217
  }
761
1218
  }
762
1219
 
@@ -797,6 +1254,9 @@ function printPseudo(algorithm, translate, output, htmlToString) {
797
1254
  var content = step.content
798
1255
  var lines = printStructuredContentNoIdent(content)
799
1256
  lines[0] = translate("if") + " " + lines[0]
1257
+ if (step.side) {
1258
+ lines[0] = step.side + ": " + lines[0]
1259
+ }
800
1260
  printWithIndent(lines, indent, output)
801
1261
  addRange(output, yesBody)
802
1262
  if (!empty(noBody)) {
@@ -825,392 +1285,440 @@ function printPseudo(algorithm, translate, output, htmlToString) {
825
1285
  }
826
1286
 
827
1287
  module.exports = {printPseudo, printWithIndent, makeIndent}
828
- },{"./tools":8}],6:[function(require,module,exports){
829
- var {buildTree} = require("./technicalTree")
1288
+ },{"./tools":10}],8:[function(require,module,exports){
1289
+ var { buildTree } = require("./technicalTree");
830
1290
  const { createError, sortByProperty } = require("./tools");
831
- const { optimizeTree } = require("./treeTools")
1291
+ const { optimizeTree } = require("./treeTools");
1292
+ const { flow_no_loop } = require("./noloop")
832
1293
 
833
1294
  function redirectNode(nodes, node, from, to) {
834
- if (node.one === from) {
835
- node.one = to;
836
- }
837
- if (node.two === from) {
838
- node.two = to;
839
- }
840
- if (node.next === from) {
841
- node.next = to
842
- }
843
- if (node.start && node.type === "loopend") {
844
- start = nodes[node.start]
845
- if (start.next === from) {
846
- start.next = to
847
- }
1295
+ if (node.one === from) {
1296
+ node.one = to;
1297
+ }
1298
+ if (node.two === from) {
1299
+ node.two = to;
1300
+ }
1301
+ if (node.next === from) {
1302
+ node.next = to;
1303
+ }
1304
+ if (node.start && node.type === "loopend") {
1305
+ start = nodes[node.start];
1306
+ if (start.next === from) {
1307
+ start.next = to;
848
1308
  }
1309
+ }
849
1310
  }
850
1311
 
851
- function structFlow(nodes, branches, filename, translate) {
852
-
853
-
854
-
855
- function flowGraph(nodes, nodeId, branchingStack) {
856
- if (!nodeId) {return;}
857
-
858
- const node = nodes[nodeId];
1312
+ function structFlow(nodes, branches, filename, translate, options) {
1313
+ function flowGraph(nodes, nodeId, branchingStack) {
1314
+ if (!nodeId) {
1315
+ return;
1316
+ }
859
1317
 
860
- if (!node.stack) {
861
- node.stack = [];
862
- node.remaining = node.prev.length;
863
- }
864
- node.remaining--;
1318
+ const node = nodes[nodeId];
865
1319
 
866
- mergeBranchingStack(nodes, node, branchingStack);
867
- if (node.remaining > 0) {return;}
1320
+ if (!node.stack) {
1321
+ node.stack = [];
1322
+ node.remaining = node.prev.length;
1323
+ }
1324
+ node.remaining--;
868
1325
 
869
- if (node.type === "question") {
870
- for (let i = 0; i < node.stack.length; i++) {
871
- const questionId = node.stack[i];
872
- const question = nodes[questionId];
873
- question.branching++;
874
- }
1326
+ mergeBranchingStack(nodes, node, branchingStack);
1327
+ if (node.remaining > 0) {
1328
+ return;
1329
+ }
875
1330
 
876
- const stackOne = node.stack.slice();
877
- const stackTwo = node.stack.slice();
878
- stackOne.push(nodeId);
879
- stackTwo.push(nodeId);
1331
+ if (node.type === "question") {
1332
+ for (let i = 0; i < node.stack.length; i++) {
1333
+ const questionId = node.stack[i];
1334
+ const question = nodes[questionId];
1335
+ question.branching++;
1336
+ }
880
1337
 
881
- flowGraph(nodes, node.two, stackTwo);
882
- flowGraph(nodes, node.one, stackOne);
883
- } else if (node.type === "arrow-loop") {
884
- const stackOne = node.stack.slice();
885
- stackOne.push(nodeId);
886
- flowGraph(nodes, node.one, stackOne);
887
- } else if (node.type === "arrow-stub") {
888
- decrementBranchingForArrow(nodes, node)
889
- } else {
890
- flowGraph(nodes, node.one, node.stack);
891
- }
1338
+ const stackOne = node.stack.slice();
1339
+ const stackTwo = node.stack.slice();
1340
+ stackOne.push(nodeId);
1341
+ stackTwo.push(nodeId);
1342
+
1343
+ flowGraph(nodes, node.two, stackTwo);
1344
+ flowGraph(nodes, node.one, stackOne);
1345
+ } else if (node.type === "arrow-loop") {
1346
+ const stackOne = node.stack.slice();
1347
+ stackOne.push(nodeId);
1348
+ flowGraph(nodes, node.one, stackOne);
1349
+ } else if (node.type === "arrow-stub") {
1350
+ decrementBranchingForArrow(nodes, node);
1351
+ } else if (node.type === "parbegin") {
1352
+ for (var proc of node.procs) {
1353
+ flowGraph(nodes, proc.start, []);
1354
+ }
1355
+ flowGraph(nodes, node.one, node.stack);
1356
+ } else {
1357
+ flowGraph(nodes, node.one, node.stack);
892
1358
  }
1359
+ }
893
1360
 
894
- function decrementBranchingForArrow(nodes, node) {
895
- var algonode = nodes[node.arrow]
896
- algonode.branching--
897
- }
1361
+ function decrementBranchingForArrow(nodes, node) {
1362
+ var algonode = nodes[node.arrow];
1363
+ algonode.branching--;
1364
+ }
898
1365
 
899
- function decrementQuestions(nodes, algonode, dictionary) {
900
- var stub = nodes[algonode.stub]
901
- for (var id of stub.stack) {
902
- var snode = nodes[id]
903
- if (id != algonode) {
904
- if (id in dictionary) {
905
- snode.branching--
906
- }
907
- }
1366
+ function decrementQuestions(nodes, algonode, dictionary) {
1367
+ var stub = nodes[algonode.stub];
1368
+ for (var id of stub.stack) {
1369
+ var snode = nodes[id];
1370
+ if (id !== algonode.id) {
1371
+ if (id in dictionary) {
1372
+ snode.branching--;
908
1373
  }
909
- return stub
1374
+ }
910
1375
  }
1376
+ return stub;
1377
+ }
911
1378
 
1379
+ function mergeBranchingStack(nodes, node, branchingStack) {
1380
+ // Append all elements of the branching stack to node.stack
1381
+ addRange(node.stack, branchingStack);
912
1382
 
1383
+ // Build a dictionary of occurrences
1384
+ const dictionary = buildDictionaryOfOccurences(node);
913
1385
 
914
- function mergeBranchingStack(nodes, node, branchingStack) {
915
- // Append all elements of the branching stack to node.stack
916
- addRange(node.stack, branchingStack)
1386
+ // Merge all nodes
1387
+ mergeAll(nodes, node, dictionary);
917
1388
 
918
- // Build a dictionary of occurrences
919
- const dictionary = buildDictionaryOfOccurences(node);
1389
+ // Rebuild the stack
1390
+ node.stack = buildStackFromDictionary(dictionary);
1391
+ }
920
1392
 
921
- // Merge all nodes
922
- mergeAll(nodes, node, dictionary);
1393
+ function addRange(dst, src) {
1394
+ for (let i = 0; i < src.length; i++) {
1395
+ dst.push(src[i]);
1396
+ }
1397
+ }
923
1398
 
924
- // Rebuild the stack
925
- node.stack = buildStackFromDictionary(dictionary);
1399
+ function buildStackFromDictionary(dictionary) {
1400
+ const rebuiltStack = [];
1401
+ for (const id in dictionary) {
1402
+ if (dictionary[id] > 0) {
1403
+ rebuiltStack.push(id);
1404
+ }
926
1405
  }
1406
+ return rebuiltStack;
1407
+ }
927
1408
 
928
- function addRange(dst, src) {
929
- for (let i = 0; i < src.length; i++) {
930
- dst.push(src[i]);
931
- }
1409
+ function buildDictionaryOfOccurences(node) {
1410
+ const dictionary = {};
1411
+ for (let i = 0; i < node.stack.length; i++) {
1412
+ const id = node.stack[i];
1413
+ dictionary[id] = (dictionary[id] || 0) + 1;
932
1414
  }
1415
+ return dictionary;
1416
+ }
933
1417
 
934
- function buildStackFromDictionary(dictionary) {
935
- const rebuiltStack = [];
936
- for (const id in dictionary) {
937
- if (dictionary[id] > 0) {
938
- rebuiltStack.push(id);
939
- }
1418
+ function mergeAll(nodes, node, dictionary) {
1419
+ for (const id in dictionary) {
1420
+ const occurrences = dictionary[id];
1421
+ const algonode = nodes[id];
1422
+ if (occurrences > 1) {
1423
+ algonode.branching--;
1424
+ dictionary[id] = occurrences - 1;
1425
+ }
1426
+ if (algonode.branching === 1) {
1427
+ if (algonode.type === "arrow-loop" && !algonode.next) {
1428
+ if (!isInMap(node.astack, id)) {
1429
+ algonode.next = node.id;
1430
+ dictionary[algonode.id] = 0;
1431
+ var stub = decrementQuestions(nodes, algonode, dictionary);
1432
+ stub.one = node.id;
1433
+ }
940
1434
  }
941
- return rebuiltStack;
1435
+ }
942
1436
  }
943
1437
 
944
- function buildDictionaryOfOccurences(node) {
945
- const dictionary = {};
946
- for (let i = 0; i < node.stack.length; i++) {
947
- const id = node.stack[i];
948
- dictionary[id] = (dictionary[id] || 0) + 1;
1438
+ for (const id in dictionary) {
1439
+ const algonode = nodes[id];
1440
+ if (algonode.branching === 1) {
1441
+ if (algonode.type === "question") {
1442
+ algonode.next = node.id;
1443
+ dictionary[algonode.id] = 0;
949
1444
  }
950
- return dictionary;
1445
+ }
1446
+ }
1447
+ }
1448
+ function isInMap(map, key) {
1449
+ if (!map) {
1450
+ return false;
951
1451
  }
1452
+ return key in map;
1453
+ }
952
1454
 
953
- function mergeAll(nodes, node, dictionary) {
954
- for (const id in dictionary) {
955
- const occurrences = dictionary[id];
956
- const algonode = nodes[id];
957
- if (occurrences > 1) {
958
- algonode.branching--;
959
- dictionary[id] = occurrences - 1;
960
- }
961
- if (algonode.branching === 1) {
962
- if (algonode.type === "arrow-loop" && !algonode.next) {
963
- if (!isInMap(node.astack, id)) {
964
- algonode.next = node.id
965
- dictionary[algonode.id] = 0;
966
- var stub = decrementQuestions(nodes, algonode, dictionary)
967
- stub.one = node.id
968
- }
969
- }
970
- }
971
- }
972
-
973
- for (const id in dictionary) {
974
- const algonode = nodes[id];
975
- if (algonode.branching === 1) {
976
- if (algonode.type === "question") {
977
- algonode.next = node.id;
978
- dictionary[algonode.id] = 0;
979
- }
980
- }
981
- }
1455
+ function prepareQuestions(nodes) {
1456
+ for (const nodeId in nodes) {
1457
+ const node = nodes[nodeId];
1458
+ if (node.type === "question") {
1459
+ node.branching = 2;
1460
+ } else if (node.type === "arrow-loop") {
1461
+ node.branching = 1;
1462
+ }
982
1463
  }
983
- function isInMap(map, key) {
984
- if (!map) { return false }
985
- return key in map
1464
+ }
1465
+
1466
+ function rewireArrows(nodes, branches) {
1467
+ branches.forEach((branch) =>
1468
+ rewireArrowsInBranch(nodes, branch.id, branch.next, []),
1469
+ );
1470
+ for (var id in nodes) {
1471
+ var node = nodes[id];
1472
+ if (node.type === "arrow-loop") {
1473
+ var stub = insertArrowStub(nodes, node);
1474
+ var visited = {};
1475
+ fillAStack(nodes, stub, stub.arrow, visited);
1476
+ }
986
1477
  }
1478
+ }
987
1479
 
988
- function prepareQuestions(nodes) {
989
- for (const nodeId in nodes) {
990
- const node = nodes[nodeId];
991
- if (node.type === "question") {
992
- node.branching = 2;
993
- } else if (node.type === "arrow-loop") {
994
- node.branching = 1;
995
- }
996
- }
1480
+ function fillAStack(nodes, node, arrowId, visited) {
1481
+ if (node.id in visited) {
1482
+ return;
997
1483
  }
1484
+ visited[node.id] = true;
1485
+ if (!node.astack) {
1486
+ node.astack = {};
1487
+ }
1488
+ node.astack[arrowId] = true;
1489
+ if (node.id === arrowId) {
1490
+ return;
1491
+ }
1492
+ for (var prevId of node.prev) {
1493
+ var prev = nodes[prevId];
1494
+ fillAStack(nodes, prev, arrowId, visited);
1495
+ }
1496
+ }
998
1497
 
999
- function rewireArrows(nodes, branches) {
1000
- branches.forEach(branch => rewireArrowsInBranch(nodes, branch.id, branch.next, []))
1001
- for (var id in nodes) {
1002
- var node = nodes[id]
1003
- if (node.type === "arrow-loop") {
1004
- var stub = insertArrowStub(nodes, node)
1005
- fillAStack(nodes, stub, stub.arrow)
1006
- }
1007
- }
1498
+ function rewireArrowsInBranch(nodes, prevNodeId, nodeId, arrowStack) {
1499
+ if (!nodeId) {
1500
+ return;
1008
1501
  }
1009
-
1010
- function fillAStack(nodes, node, arrowId) {
1011
- if (!node.astack) {
1012
- node.astack = {}
1013
- }
1014
- node.astack[arrowId] = true
1015
- if (node.id === arrowId) {
1016
- return
1017
- }
1018
- for (var prevId of node.prev) {
1019
- var prev = nodes[prevId]
1020
- fillAStack(nodes, prev, arrowId)
1021
- }
1502
+ var node = nodes[nodeId];
1503
+ if (node.type === "branch") {
1504
+ return;
1022
1505
  }
1023
-
1024
- function rewireArrowsInBranch(nodes, prevNodeId, nodeId, arrowStack) {
1025
- if (!nodeId) {return}
1026
- var node = nodes[nodeId]
1027
- if (node.type === "branch") {
1028
- return
1029
- }
1030
- if (node.type === "arrow-loop") {
1031
- if (!node.noloop) {
1032
- node.noloop = {}
1033
- }
1034
- if (arrowStack.includes(nodeId)) {
1035
- return
1036
- }
1037
- node.noloop[prevNodeId] = true
1038
- arrowStack = arrowStack.slice()
1039
- arrowStack.push(nodeId)
1040
- rewireArrowsInBranch(nodes, nodeId, node.one, arrowStack)
1041
- } else if (node.type === "question") {
1042
- var left = arrowStack.slice()
1043
- var right = arrowStack.slice()
1044
- rewireArrowsInBranch(nodes, nodeId, node.one, left)
1045
- rewireArrowsInBranch(nodes, nodeId, node.two, right)
1046
- } else {
1047
- rewireArrowsInBranch(nodes, nodeId, node.one, arrowStack)
1048
- }
1506
+ if (node.type === "arrow-loop") {
1507
+ if (!node.noloop) {
1508
+ node.noloop = {};
1509
+ }
1510
+ if (arrowStack.includes(nodeId)) {
1511
+ return;
1512
+ }
1513
+ node.noloop[prevNodeId] = true;
1514
+ arrowStack = arrowStack.slice();
1515
+ arrowStack.push(nodeId);
1516
+ rewireArrowsInBranch(nodes, nodeId, node.one, arrowStack);
1517
+ } else if (node.type === "question") {
1518
+ var left = arrowStack.slice();
1519
+ var right = arrowStack.slice();
1520
+ rewireArrowsInBranch(nodes, nodeId, node.one, left);
1521
+ rewireArrowsInBranch(nodes, nodeId, node.two, right);
1522
+ } else if (node.type === "parbegin") {
1523
+ for (var proc of node.procs) {
1524
+ rewireArrowsInBranch(nodes, undefined, proc.start, []);
1525
+ }
1526
+ rewireArrowsInBranch(nodes, nodeId, node.one, arrowStack);
1527
+ } else {
1528
+ rewireArrowsInBranch(nodes, nodeId, node.one, arrowStack);
1049
1529
  }
1050
-
1051
- function insertArrowStub(nodes, node) {
1052
- var stub = {
1053
- type: "arrow-stub",
1054
- id: "arrow-stub-" + node.id,
1055
- arrow: node.id,
1056
- prev: []
1057
- }
1058
- nodes[stub.id] = stub
1059
- node.stub = stub.id
1060
- var prev2 = []
1061
- for (var prevId of node.prev) {
1062
- if (prevId in node.noloop) {
1063
- prev2.push(prevId)
1064
- } else {
1065
- stub.prev.push(prevId)
1066
- var prev = nodes[prevId]
1067
- redirectNode(nodes, prev, node.id, stub.id)
1068
- }
1069
- }
1070
- node.prev = prev2
1071
- return stub
1530
+ }
1531
+
1532
+ function insertArrowStub(nodes, node) {
1533
+ var stub = {
1534
+ type: "arrow-stub",
1535
+ id: "arrow-stub-" + node.id,
1536
+ arrow: node.id,
1537
+ prev: [],
1538
+ };
1539
+ nodes[stub.id] = stub;
1540
+ node.stub = stub.id;
1541
+ var prev2 = [];
1542
+ for (var prevId of node.prev) {
1543
+ if (prevId in node.noloop) {
1544
+ prev2.push(prevId);
1545
+ } else {
1546
+ stub.prev.push(prevId);
1547
+ var prev = nodes[prevId];
1548
+ redirectNode(nodes, prev, node.id, stub.id);
1549
+ }
1072
1550
  }
1551
+ node.prev = prev2;
1552
+ return stub;
1553
+ }
1073
1554
 
1074
- function rewriteTree(body, index, endId, output) {
1075
- while (index < body.length) {
1076
- var node = body[index]
1077
- index++
1078
- if (endId && node.id === endId) {
1079
- return index
1080
- }
1081
- if (node.type === "question") {
1082
- var transformed = rewriteQuestionTree(node, output)
1083
- if (endId) {
1084
- var breakYes = findLoopEnd(transformed.yes, endId)
1085
- var breakNo = findLoopEnd(transformed.no, endId)
1086
- if (breakYes || breakNo) {
1087
- var toBreak = []
1088
- findPlacesToBreak(transformed.yes, endId, toBreak)
1089
- findPlacesToBreak(transformed.no, endId, toBreak)
1090
- addBreaks(toBreak)
1091
- return index
1092
- }
1093
- }
1094
- } else if (node.type === "loopbegin") {
1095
- var body2 = []
1096
- index = rewriteTree(body, index, node.end, body2)
1097
- output.push({
1098
- id: node.id,
1099
- type: "loop",
1100
- content: node.content,
1101
- body: body2
1102
- })
1103
- } else {
1104
- output.push(node)
1105
- }
1106
- }
1555
+ function copySide(dst, src) {
1556
+ if (src.side) {
1557
+ dst.side = src.side;
1107
1558
  }
1559
+ }
1108
1560
 
1109
- function findPlacesToBreak(body, endId, output) {
1110
- if (body.length === 0) {
1111
- output.push(body)
1112
- return
1561
+ function rewriteTree(body, index, endId, output) {
1562
+ while (index < body.length) {
1563
+ var node = body[index];
1564
+ index++;
1565
+ if (endId && node.id === endId) {
1566
+ return index;
1567
+ }
1568
+ if (node.type === "question") {
1569
+ var transformed = rewriteQuestionTree(node, output);
1570
+ copySide(transformed, node);
1571
+ if (endId) {
1572
+ var breakYes = findLoopEnd(transformed.yes, endId);
1573
+ var breakNo = findLoopEnd(transformed.no, endId);
1574
+ if (breakYes || breakNo) {
1575
+ var toBreak = [];
1576
+ findPlacesToBreak(transformed.yes, endId, toBreak);
1577
+ findPlacesToBreak(transformed.no, endId, toBreak);
1578
+ addBreaks(toBreak);
1579
+ return index;
1580
+ }
1113
1581
  }
1114
- var last = body[body.length - 1]
1115
- if (last.id === endId) {
1116
- return
1117
- }
1118
- if (last.type === "question") {
1119
- var qends = []
1120
- findPlacesToBreak(last.yes, endId, qends)
1121
- findPlacesToBreak(last.no, endId, qends)
1122
- if (qends.length === 2
1123
- && qends[0] === last.yes
1124
- && qends[1] === last.no) {
1125
- output.push(body)
1126
- } else {
1127
- addRange(output, qends)
1128
- }
1129
- } else {
1130
- output.push(body)
1582
+ } else if (node.type === "loopbegin") {
1583
+ var body2 = [];
1584
+ index = rewriteTree(body, index, node.end, body2);
1585
+ output.push({
1586
+ id: node.id,
1587
+ type: "loop",
1588
+ content: node.content,
1589
+ body: body2,
1590
+ });
1591
+ } else if (node.type === "parbegin") {
1592
+ var copy = {
1593
+ id: node.id,
1594
+ type: node.type,
1595
+ procs: [],
1596
+ };
1597
+ for (var proc of node.procs) {
1598
+ var procCopy = {
1599
+ ordinal: proc.ordinal,
1600
+ body: [],
1601
+ };
1602
+ copy.procs.push(procCopy);
1603
+ rewriteTree(proc.body, 0, undefined, procCopy.body);
1131
1604
  }
1605
+ output.push(copy);
1606
+ } else {
1607
+ output.push(node);
1608
+ }
1132
1609
  }
1610
+ }
1133
1611
 
1134
- function findLoopEnd(body, endId) {
1135
- for (var i = 0; i < body.length; i++) {
1136
- var node = body[i]
1137
- if (node.id === endId) {
1138
- if (i === body.length - 1) {
1139
- return true
1140
- } else {
1141
- throw createError(
1142
- translate("An exit from the loop must lead to the point right after the loop end"),
1143
- filename,
1144
- node.id
1145
- );
1146
- }
1147
- }
1148
- if (node.type === "question") {
1149
- if (findLoopEnd(node.yes, endId)) {
1150
- return true
1151
- }
1152
- if (findLoopEnd(node.no, endId)) {
1153
- return true
1154
- }
1155
- }
1156
- }
1157
- return false
1612
+ function findPlacesToBreak(body, endId, output) {
1613
+ if (body.length === 0) {
1614
+ output.push(body);
1615
+ return;
1158
1616
  }
1617
+ var last = body[body.length - 1];
1618
+ if (last.id === endId) {
1619
+ return;
1620
+ }
1621
+ if (last.type === "question") {
1622
+ var qends = [];
1623
+ findPlacesToBreak(last.yes, endId, qends);
1624
+ findPlacesToBreak(last.no, endId, qends);
1625
+ if (qends.length === 2 && qends[0] === last.yes && qends[1] === last.no) {
1626
+ output.push(body);
1627
+ } else {
1628
+ addRange(output, qends);
1629
+ }
1630
+ } else {
1631
+ output.push(body);
1632
+ }
1633
+ }
1159
1634
 
1160
- function addBreaks(toBreak) {
1161
- for (var body of toBreak) {
1162
- body.push({
1163
- type: "break"
1164
- })
1635
+ function findLoopEnd(body, endId) {
1636
+ for (var i = 0; i < body.length; i++) {
1637
+ var node = body[i];
1638
+ if (node.id === endId) {
1639
+ if (i === body.length - 1) {
1640
+ return true;
1641
+ } else {
1642
+ throw createError(
1643
+ translate(
1644
+ "An exit from the loop must lead to the point right after the loop end",
1645
+ ),
1646
+ filename,
1647
+ node.id,
1648
+ );
1649
+ }
1650
+ }
1651
+ if (node.type === "question") {
1652
+ if (findLoopEnd(node.yes, endId)) {
1653
+ return true;
1165
1654
  }
1655
+ if (findLoopEnd(node.no, endId)) {
1656
+ return true;
1657
+ }
1658
+ }
1166
1659
  }
1660
+ return false;
1661
+ }
1167
1662
 
1168
- function rewriteQuestionTree(question, output) {
1169
- var yes = []
1170
- var no = []
1171
- rewriteTree(question.yes, 0, undefined, yes)
1172
- rewriteTree(question.no, 0, undefined, no)
1173
- var transformed = {
1174
- type: "question",
1175
- id: question.id,
1176
- content: question.content,
1177
- yes: yes,
1178
- no: no
1179
- }
1180
- output.push(transformed)
1181
- return transformed
1663
+ function addBreaks(toBreak) {
1664
+ for (var body of toBreak) {
1665
+ body.push({
1666
+ type: "break",
1667
+ });
1182
1668
  }
1183
-
1669
+ }
1184
1670
 
1185
- function structMain() {
1186
- rewireArrows(nodes, branches)
1187
- prepareQuestions(nodes)
1188
- var result = []
1189
- for (var branch of branches) {
1190
- flowGraph(nodes, branch.next, [])
1191
- }
1192
-
1193
- for (var branch of branches) {
1194
- var body = []
1195
- buildTree(nodes, branch.next, body, "<dummy id>")
1196
- var body2 = []
1197
- rewriteTree(body, 0, undefined, body2)
1198
- result.push({
1199
- name: branch.content,
1200
- branchId: branch.branchId,
1201
- start: branch.next,
1202
- refs: branch.prev.length,
1203
- body: optimizeTree(body2)
1204
- })
1205
- }
1671
+ function rewriteQuestionTree(question, output) {
1672
+ var yes = [];
1673
+ var no = [];
1674
+ rewriteTree(question.yes, 0, undefined, yes);
1675
+ rewriteTree(question.no, 0, undefined, no);
1676
+ var transformed = {
1677
+ type: "question",
1678
+ id: question.id,
1679
+ content: question.content,
1680
+ yes: yes,
1681
+ no: no,
1682
+ };
1683
+ output.push(transformed);
1684
+ return transformed;
1685
+ }
1686
+
1687
+ function structMain() {
1688
+ rewireArrows(nodes, branches);
1689
+ prepareQuestions(nodes);
1690
+ var result = [];
1691
+
1692
+ for (var branch of branches) {
1693
+ if (options.noLoop) {
1694
+ flow_no_loop(nodes, branch.next, []);
1695
+ } else {
1696
+ flowGraph(nodes, branch.next, []);
1697
+ }
1698
+ }
1206
1699
 
1207
- return sortByProperty(result, "branchId")
1700
+ for (var branch of branches) {
1701
+ var body = [];
1702
+ buildTree(nodes, branch.next, body, "<dummy id>");
1703
+ var body2 = [];
1704
+ rewriteTree(body, 0, undefined, body2);
1705
+ result.push({
1706
+ name: branch.content,
1707
+ branchId: branch.branchId,
1708
+ start: branch.next,
1709
+ refs: branch.prev.length,
1710
+ body: optimizeTree(body2),
1711
+ });
1208
1712
  }
1209
1713
 
1210
- return structMain()
1714
+ return sortByProperty(result, "branchId");
1715
+ }
1716
+
1717
+ return structMain();
1211
1718
  }
1212
1719
  module.exports = { structFlow, redirectNode };
1213
- },{"./technicalTree":7,"./tools":8,"./treeTools":10}],7:[function(require,module,exports){
1720
+
1721
+ },{"./noloop":6,"./technicalTree":9,"./tools":10,"./treeTools":12}],9:[function(require,module,exports){
1214
1722
  function buildTree(nodes, nodeId, body, stopId) {
1215
1723
  while (nodeId) {
1216
1724
  if (nodeId === stopId) {return;}
@@ -1250,6 +1758,21 @@ function buildTree(nodes, nodeId, body, stopId) {
1250
1758
  start: node.arrow
1251
1759
  };
1252
1760
 
1761
+ next = node.one;
1762
+ } else if (node.type === "parbegin") {
1763
+ transformed = {
1764
+ id: node.id,
1765
+ type: node.type,
1766
+ procs: []
1767
+ }
1768
+ for (var proc of node.procs) {
1769
+ var childProc = {
1770
+ ordinal: proc.ordinal,
1771
+ body: []
1772
+ }
1773
+ transformed.procs.push(childProc)
1774
+ buildTree(nodes, proc.start, childProc.body, undefined)
1775
+ }
1253
1776
  next = node.one;
1254
1777
  } else {
1255
1778
  transformed = {
@@ -1268,8 +1791,13 @@ function buildTree(nodes, nodeId, body, stopId) {
1268
1791
  ]
1269
1792
  )
1270
1793
  next = node.one;
1794
+ if (node.final) {
1795
+ next = undefined
1796
+ }
1797
+ }
1798
+ if (node.side) {
1799
+ transformed.side = node.side
1271
1800
  }
1272
-
1273
1801
  body.push(transformed);
1274
1802
  nodeId = next;
1275
1803
  }
@@ -1285,6 +1813,9 @@ function copyFields(dst, src, fields) {
1285
1813
  }
1286
1814
 
1287
1815
  function reserveNext(nodes, node) {
1816
+ if (!node.next) {
1817
+ return undefined
1818
+ }
1288
1819
  const target = nodes[node.next];
1289
1820
  if (target.targetTaken) {
1290
1821
  return undefined;
@@ -1296,7 +1827,7 @@ function reserveNext(nodes, node) {
1296
1827
 
1297
1828
  module.exports = {buildTree}
1298
1829
 
1299
- },{}],8:[function(require,module,exports){
1830
+ },{}],10:[function(require,module,exports){
1300
1831
 
1301
1832
  function createError(message, filename, nodeId) {
1302
1833
  var error = new Error(message)
@@ -1337,7 +1868,7 @@ function addRange(to, from) {
1337
1868
  }
1338
1869
  }
1339
1870
  module.exports = { createError, sortByProperty, addRange, remove }
1340
- },{}],9:[function(require,module,exports){
1871
+ },{}],11:[function(require,module,exports){
1341
1872
  var translationsRu = {
1342
1873
  "error": "ОШИБКА",
1343
1874
  "not": "не",
@@ -1366,7 +1897,13 @@ var translationsRu = {
1366
1897
  "Description": "Описание",
1367
1898
  "Algorithm": "Алгоритм",
1368
1899
  Remarks: "Замечания",
1369
- Parameters: "Параметры"
1900
+ Parameters: "Параметры",
1901
+ "Group of parallel processes": "Группа параллельных процессов",
1902
+ "Parallel process": "Параллельный процесс",
1903
+ "Start at": "Начать в",
1904
+ "Do for": "Делать в течение",
1905
+ "Pause": "Пауза",
1906
+ "Start timer": "Запустить таймер"
1370
1907
  }
1371
1908
 
1372
1909
  var translationsEn = {
@@ -1397,7 +1934,13 @@ var translationsEn = {
1397
1934
  Description: 'Description',
1398
1935
  Algorithm: 'Algorithm',
1399
1936
  Remarks: "Remarks",
1400
- Parameters: "Parameters"
1937
+ Parameters: "Parameters",
1938
+ "Group of parallel processes": "Group of parallel processes",
1939
+ "Parallel process": "Parallel process",
1940
+ "Start at": "Start at",
1941
+ "Do for": "Do for",
1942
+ "Pause": "Pause",
1943
+ "Start timer": "Start timer",
1401
1944
  }
1402
1945
 
1403
1946
  var translationsNo = {
@@ -1428,7 +1971,13 @@ var translationsNo = {
1428
1971
  Description: 'Beskrivelse',
1429
1972
  Algorithm: 'Algoritme',
1430
1973
  Remarks: "Bemerkninger",
1431
- Parameters: "Parametere"
1974
+ Parameters: "Parametere",
1975
+ "Group of parallel processes": "Gruppe av parallelle prosesser",
1976
+ "Parallel process": "Parallell prosess",
1977
+ "Start at": "Start ved",
1978
+ "Do for": "Gjør i",
1979
+ "Pause": "Pause",
1980
+ "Start timer": "Start tidtaker"
1432
1981
  };
1433
1982
 
1434
1983
 
@@ -1452,7 +2001,7 @@ function setUpLanguage(language) {
1452
2001
 
1453
2002
 
1454
2003
  module.exports = { setUpLanguage, translate };
1455
- },{}],10:[function(require,module,exports){
2004
+ },{}],12:[function(require,module,exports){
1456
2005
 
1457
2006
  function optimizeTree(steps) {
1458
2007
  var result = []
@@ -1463,6 +2012,8 @@ function optimizeTree(steps) {
1463
2012
  var copy
1464
2013
  if (step.type === "question") {
1465
2014
  copy = optimizeQuestion(step)
2015
+ } else if (step.type === "parbegin") {
2016
+ copy = optimizeParbegin(step)
1466
2017
  } else if (step.type === "loop") {
1467
2018
  copy = optimizeLoop(step)
1468
2019
  } else {
@@ -1474,6 +2025,22 @@ function optimizeTree(steps) {
1474
2025
  return result
1475
2026
  }
1476
2027
 
2028
+ function optimizeParbegin(step) {
2029
+ var procs = []
2030
+ for (var proc of step.procs) {
2031
+ var procCopy = {
2032
+ ordinal: proc.ordinal,
2033
+ body: optimizeTree(proc.body)
2034
+ }
2035
+ procs.push(procCopy)
2036
+ }
2037
+ return {
2038
+ id: step.id,
2039
+ type: step.type,
2040
+ procs: procs
2041
+ }
2042
+ }
2043
+
1477
2044
  function optimizeLoop(step) {
1478
2045
  return {
1479
2046
  id: step.id,
@@ -1488,6 +2055,7 @@ function optimizeQuestion(step) {
1488
2055
  var no = optimizeTree(step.no)
1489
2056
  if (yes.length === 0 && no.length === 0) {
1490
2057
  return {
2058
+ side: step.side,
1491
2059
  type: step.type,
1492
2060
  content: step.content,
1493
2061
  yes: [],
@@ -1496,6 +2064,7 @@ function optimizeQuestion(step) {
1496
2064
  }
1497
2065
  if (yes.length === 0) {
1498
2066
  return {
2067
+ side: step.side,
1499
2068
  type: step.type,
1500
2069
  content: {operator:"not",operand:step.content},
1501
2070
  yes: no,
@@ -1503,6 +2072,7 @@ function optimizeQuestion(step) {
1503
2072
  }
1504
2073
  }
1505
2074
  return {
2075
+ side: step.side,
1506
2076
  type: step.type,
1507
2077
  content: step.content,
1508
2078
  yes: yes,