drakongen 1.7.0 → 1.9.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 (65) hide show
  1. package/browser/drakongen.js +426 -506
  2. package/browsertest.html +11 -4
  3. package/buildexamples.js +0 -10
  4. package/examples/BugBadExit.drakon +69 -0
  5. package/examples/BugBadExit.txt +16 -0
  6. package/examples/Workout foreach.drakon +7 -2
  7. package/examples/Workout foreach.txt +1 -0
  8. 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" +3 -2
  9. 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" +3 -4
  10. package/examples/no-loop/Arrow - double exit.txt +13 -0
  11. package/examples/no-loop/BugBadExit.txt +16 -0
  12. package/examples/no-loop/Complex arrow-parallel.txt +40 -0
  13. package/examples/no-loop/Complex arrow.txt +37 -0
  14. package/examples/no-loop/DoubleArrow.txt +17 -0
  15. package/examples/no-loop/Find pointing nodes.txt +24 -0
  16. package/examples/no-loop/How to tune PID on a quadcopter.txt +145 -0
  17. package/examples/no-loop/NestedArrowLoop.txt +20 -0
  18. package/examples/no-loop/Work out action-check.txt +9 -0
  19. package/examples/no-loop/Workout foreach.txt +13 -0
  20. package/examples/no-loop/all icons.txt +68 -0
  21. package/examples/no-loop/ar01-action.txt +9 -0
  22. package/examples/no-loop/ar01.txt +8 -0
  23. package/examples/no-loop/ar02.txt +10 -0
  24. package/examples/no-loop/ar03.txt +8 -0
  25. package/examples/no-loop/ar04.txt +10 -0
  26. package/examples/no-loop/ar05.txt +11 -0
  27. package/examples/no-loop/ar06.txt +13 -0
  28. package/examples/no-loop/ar07.txt +13 -0
  29. package/examples/no-loop/ar08.txt +16 -0
  30. package/examples/no-loop/ar09.txt +16 -0
  31. package/examples/no-loop/ar10-parallel.txt +20 -0
  32. package/examples/no-loop/ar10.txt +17 -0
  33. package/examples/no-loop/ar11.txt +17 -0
  34. package/examples/no-loop/ar12.txt +10 -0
  35. package/examples/no-loop/parallel-3.txt +12 -0
  36. package/examples/no-loop/parallel-arrow-loop.txt +12 -0
  37. package/examples/no-loop/parallel-empty-nested.txt +11 -0
  38. package/examples/no-loop/parallel-empty.txt +8 -0
  39. package/examples/no-loop/parallel-nested.txt +16 -0
  40. package/package.json +1 -1
  41. package/src/drakonToPromptStruct.js +37 -12
  42. package/src/drakonToStruct.js +51 -34
  43. package/src/drakongen.js +7 -1
  44. package/src/index.js +8 -2
  45. package/src/main.js +8 -5
  46. package/src/noloop/flow_no_loop.drakon +33 -0
  47. package/src/noloop/noloop.dtproj +6 -0
  48. package/src/noloop/private/decrement_arrow_count.drakon +1 -0
  49. package/{prompts/noloop → src/noloop/private}/decrement_if_count.drakon +9 -10
  50. package/src/noloop/private/group_stack_by_id.drakon +63 -0
  51. package/{prompts/noloop → src/noloop/private}/increment_if_count.drakon +9 -10
  52. package/src/noloop/private/is_in_map.drakon +1 -0
  53. package/src/noloop/private/merge_converging_branches.drakon +186 -0
  54. package/src/noloop/private/recurse_traversal.drakon +152 -0
  55. package/src/noloop/private/traverse_node.drakon +69 -0
  56. package/src/noloop.js +78 -124
  57. package/src/structFlow.js +11 -281
  58. package/src/technicalTree.js +40 -16
  59. package/src/translate.js +155 -1
  60. package/src/treeTools.js +46 -36
  61. package/prompts/noloop/flow_no_loop.drakon +0 -26
  62. package/prompts/noloop/group_stack_by_id.drakon +0 -56
  63. package/prompts/noloop/merge_incoming_branches.drakon +0 -105
  64. package/prompts/noloop/recurse_traversal.drakon +0 -88
  65. package/prompts/noloop/traverse_node.drakon +0 -62
@@ -81,7 +81,7 @@ function drakonToPseudocode(drakonJson, name, filename, htmlToString, translate,
81
81
  }
82
82
 
83
83
 
84
- function mindToTree(drakonJson, name, filename, htmlToString) {
84
+ function mindToTree(drakonJson, name, filename, htmlToString, outputToJson) {
85
85
  let drakonGraph;
86
86
  try {
87
87
  drakonJson = drakonJson || ""
@@ -94,14 +94,40 @@ function mindToTree(drakonJson, name, filename, htmlToString) {
94
94
  }
95
95
 
96
96
  const nodes = drakonGraph.items || {};
97
- var root = createMindNode("## " + name)
98
- nodes["root"] = root
99
- connectMindNodesToParent(nodes)
100
- sortMindChildren(nodes)
101
- var lines = []
102
- printMindNode(root, 0, lines, htmlToString, true)
103
- lines.push("")
104
- var text = lines.join("\n")
97
+ for (var id in nodes) {
98
+ var node = nodes[id]
99
+ node.id = id
100
+ delete node.type
101
+ delete node.treeType
102
+ if (node.content) {
103
+ node.content = htmlToString(node.content).join("\n")
104
+ }
105
+ }
106
+
107
+ var text;
108
+ if (outputToJson) {
109
+ var root = createMindNode(name)
110
+ root.name = root.content
111
+ delete root.content
112
+ nodes["root"] = root
113
+ connectMindNodesToParent(nodes)
114
+ sortMindChildren(nodes)
115
+ for (var id in nodes) {
116
+ var node = nodes[id]
117
+ delete node.parent
118
+ delete node.ordinal
119
+ }
120
+ text = JSON.stringify(root, null, 4)
121
+ } else {
122
+ var root = createMindNode("## " + name)
123
+ nodes["root"] = root
124
+ connectMindNodesToParent(nodes)
125
+ sortMindChildren(nodes)
126
+ var lines = []
127
+ printMindNode(root, 0, lines, htmlToString, true)
128
+ lines.push("")
129
+ text = lines.join("\n")
130
+ }
105
131
  return {text:text}
106
132
  }
107
133
 
@@ -145,10 +171,9 @@ function printMindNode(node, depth, lines, htmlToString, first) {
145
171
 
146
172
  function createMindNode(name) {
147
173
  return {
148
- "type": "idea",
149
- "content": "<p>" + name + "</p>",
174
+ "type": "graf",
175
+ "content": name,
150
176
  "parent": undefined,
151
- "treeType": "treeview",
152
177
  "ordinal": 0
153
178
  }
154
179
  }
@@ -184,19 +209,24 @@ function drakonToStruct(
184
209
  const nodes = drakonGraph.items || {};
185
210
 
186
211
  var branches = [];
187
- var firstNodeId = findStartNode(nodes, filename, branches);
212
+ var firstNodeId = findStartNode(nodes, filename, branches, htmlToString);
213
+ var params = decodeContent(drakonGraph.params, htmlToString);
214
+ var description = decodeContent(drakonGraph.description, htmlToString);
215
+
216
+ var result = {
217
+ name: name,
218
+ type: "drakon",
219
+ params: params,
220
+ description: description,
221
+ branches: []
222
+ };
188
223
 
189
224
  if (!firstNodeId) {
190
- return {
191
- name: name,
192
- params: drakonGraph.params || "",
193
- description: drakonGraph.description || "",
194
- branches: [],
195
- };
225
+ return result
196
226
  }
197
227
 
198
228
  handleParallel(nodes, undefined, firstNodeId, {}, undefined);
199
- buildTwoWayConnections(nodes, firstNodeId, htmlToString);
229
+ buildTwoWayConnections(nodes, firstNodeId);
200
230
 
201
231
  rewireSelectsMarkLoops(nodes, filename);
202
232
  branches.forEach((branch) =>
@@ -204,29 +234,25 @@ function drakonToStruct(
204
234
  branch,
205
235
  firstNodeId,
206
236
  filename,
207
- options,
208
- htmlToString,
237
+ options
209
238
  ),
210
239
  );
211
240
  rewireShortcircuit(nodes, filename);
212
241
  branches.forEach((branch) => cutOffBranch(nodes, branch));
213
242
  var branchTrees = structFlow(nodes, branches, filename, translate, options);
214
- return {
215
- name: name,
216
- params: drakonGraph.params || "",
217
- description: drakonGraph.description || "",
218
- branches: branchTrees,
219
- secondary: findSecondary(branchTrees, options, htmlToString),
220
- };
243
+
244
+ result.branches = branchTrees
245
+ result.secondary = findSecondary(branchTrees, options)
246
+ return result
221
247
  }
222
248
 
223
- function findSecondary(branchTrees, options, htmlToString) {
249
+ function findSecondary(branchTrees, options) {
224
250
  if (!options || !options.secondary) {
225
251
  return undefined;
226
252
  }
227
253
  var ordinal = 0;
228
254
  for (var branch of branchTrees) {
229
- var name = htmlToString(branch.name)[0];
255
+ var name = branch.name;
230
256
  if (name === options.secondary) {
231
257
  return ordinal;
232
258
  }
@@ -349,14 +375,13 @@ function checkBranchIsReferenced(
349
375
  branch,
350
376
  firstNodeId,
351
377
  filename,
352
- options,
353
- htmlToString,
378
+ options
354
379
  ) {
355
380
  if (branch.id === firstNodeId) {
356
381
  return;
357
382
  }
358
- if (options && htmlToString) {
359
- var branchName = htmlToString(branch.content)[0];
383
+ if (options) {
384
+ var branchName = branch.content;
360
385
  if (branchName === options.secondary) {
361
386
  if (branch.prev.length > 0) {
362
387
  throw createError(
@@ -450,7 +475,7 @@ function addFakeEnd(nodes, prev, node, end, addresses) {
450
475
  node.prev = remove(node.prev, prev.id);
451
476
  }
452
477
 
453
- function buildTwoWayConnections(nodes, firstNodeId, htmlToString) {
478
+ function buildTwoWayConnections(nodes, firstNodeId) {
454
479
  for (var id in nodes) {
455
480
  var node = nodes[id];
456
481
  node.id = id;
@@ -458,17 +483,18 @@ function buildTwoWayConnections(nodes, firstNodeId, htmlToString) {
458
483
  }
459
484
 
460
485
  var visitor = function (nodes, node) {
461
- return connectBack(nodes, node, htmlToString);
486
+ return connectBack(nodes, node);
462
487
  };
463
488
 
464
489
  traverse(nodes, firstNodeId, {}, visitor);
465
490
  }
466
491
 
467
- function findStartNode(nodes, filename, branches) {
492
+ function findStartNode(nodes, filename, branches, htmlToString) {
468
493
  var firstNodeId = undefined;
469
494
  var minBranchId = 10000;
470
495
  for (var id in nodes) {
471
496
  var node = nodes[id];
497
+ decodeNodeContent(node, htmlToString);
472
498
  if (node.type === "branch") {
473
499
  if (node.branchId < minBranchId) {
474
500
  firstNodeId = id;
@@ -509,6 +535,24 @@ function findStartNode(nodes, filename, branches) {
509
535
  return firstNodeId;
510
536
  }
511
537
 
538
+ function decodeNodeContent(node, htmlToString) {
539
+ if (node.content && typeof node.content === "string") {
540
+ node.content = decodeContent(node.content, htmlToString);
541
+ }
542
+
543
+ if (node.secondary && typeof node.secondary === "string") {
544
+ node.secondary = decodeContent(node.secondary, htmlToString);
545
+ }
546
+ }
547
+
548
+ function decodeContent(content, htmlToString) {
549
+ if (!content) {
550
+ return ""
551
+ }
552
+ var lines = htmlToString(content);
553
+ return lines.join("\n");
554
+ }
555
+
512
556
  function rewireSelectsMarkLoops(nodes, filename) {
513
557
  for (var id of Object.keys(nodes)) {
514
558
  var node = nodes[id];
@@ -747,7 +791,7 @@ function traverse(nodes, nodeId, visited, action) {
747
791
  }
748
792
  }
749
793
 
750
- function connectBack(nodes, node, htmlToString) {
794
+ function connectBack(nodes, node) {
751
795
  if (node.one) {
752
796
  var one = nodes[node.one];
753
797
  one.prev.push(node.id);
@@ -760,20 +804,18 @@ function connectBack(nodes, node, htmlToString) {
760
804
  if (node.side) {
761
805
  var side = nodes[node.side].content;
762
806
  if (side) {
763
- node.side = decodeSide(side, htmlToString);
807
+ node.side = decodeSide(side);
764
808
  } else {
765
809
  delete node.side;
766
810
  }
767
811
  }
768
812
  }
769
813
 
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;
814
+ function decodeSide(content) {
815
+ if (content.indexOf("=") === -1) {
816
+ return translate("Do for") + " " + content;
775
817
  } else {
776
- return translate("Start at") + " " + oneLine;
818
+ return translate("Start at") + " " + content;
777
819
  }
778
820
  }
779
821
 
@@ -818,7 +860,13 @@ window.drakongen = {
818
860
 
819
861
  toMindTree: function (mindJson, name, filename, language) {
820
862
  setUpLanguage(language);
821
- var result = mindToTree(mindJson, name, filename, htmlToString);
863
+ var result = mindToTree(mindJson, name, filename, htmlToString, false);
864
+ return result.text;
865
+ },
866
+
867
+ toMindTreeJson: function (mindJson, name, filename, language) {
868
+ setUpLanguage(language);
869
+ var result = mindToTree(mindJson, name, filename, htmlToString, true);
822
870
  return result.text;
823
871
  },
824
872
 
@@ -919,187 +967,141 @@ function freeDiagramToText(freeJson, name, filename, translateFunction, htmlToSt
919
967
 
920
968
  module.exports = {freeDiagramToText}
921
969
  },{"./tools":10}],6:[function(require,module,exports){
922
- "use strict";
923
-
970
+ function decrement_arrow_count(context, node) {
971
+ var algonode;
972
+ algonode = context.nodes[node.arrow];
973
+ algonode.branching--;
974
+ }
924
975
  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];
976
+ var _collection_12, if_id, if_node;
977
+ _collection_12 = node.stack;
978
+ for (if_id of _collection_12) {
931
979
  if_node = context.nodes[if_id];
932
980
  if_node.branching--;
933
981
  }
934
982
  }
935
-
936
983
  function flow_no_loop(nodes, start_node_id) {
937
984
  var context;
938
-
939
- context = {
940
- nodes: nodes
941
- };
942
-
943
- traverse_node(
944
- context,
945
- start_node_id,
946
- []
947
- );
985
+ context = { nodes: nodes };
986
+ traverse_node(context, start_node_id, []);
948
987
  }
949
-
950
988
  function group_stack_by_id(stack) {
951
- var counts_by_id;
952
- var i;
953
- var element;
954
- var existing;
955
-
989
+ var counts_by_id, element, existing;
956
990
  counts_by_id = {};
957
-
958
- for (i = 0; i < stack.length; i++) {
959
- element = stack[i];
960
-
991
+ for (element of stack) {
961
992
  if (element in counts_by_id) {
962
993
  existing = counts_by_id[element];
963
994
  } else {
964
995
  existing = 0;
965
996
  }
966
-
967
997
  counts_by_id[element] = existing + 1;
968
998
  }
969
-
970
999
  return counts_by_id;
971
1000
  }
972
-
973
1001
  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];
1002
+ var _collection_14, if_id, if_node;
1003
+ _collection_14 = node.stack;
1004
+ for (if_id of _collection_14) {
980
1005
  if_node = context.nodes[if_id];
981
1006
  if_node.branching++;
982
1007
  }
983
1008
  }
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
-
1009
+ function is_in_map(map, key) {
1010
+ if (map) {
1011
+ return key in map;
1012
+ } else {
1013
+ return false;
1014
+ }
1015
+ }
1016
+ function merge_converging_branches(context, node_id, node, stack) {
1017
+ var algonode, algonode_id, common, count, counts_by_id, processed_stack, stub;
995
1018
  common = node.stack.concat(stack);
996
1019
  counts_by_id = group_stack_by_id(common);
1020
+ for (algonode_id in counts_by_id) {
1021
+ count = counts_by_id[algonode_id];
1022
+ algonode = context.nodes[algonode_id];
1023
+ if (!algonode.next && algonode.type == 'arrow-loop') {
1024
+ algonode.branching -= count - 1;
1025
+ if (!(algonode.branching > 1 || is_in_map(node.astack, algonode_id))) {
1026
+ stub = context.nodes[algonode.stub];
1027
+ decrement_if_count(context, stub);
1028
+ stub.one = node_id;
1029
+ algonode.next = node_id;
1030
+ }
1031
+ }
1032
+ }
997
1033
  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;
1034
+ for (algonode_id in counts_by_id) {
1035
+ count = counts_by_id[algonode_id];
1036
+ algonode = context.nodes[algonode_id];
1037
+ if (!algonode.next) {
1038
+ if (algonode.type == 'question') {
1039
+ algonode.branching -= count - 1;
1040
+ if (algonode.branching > 1) {
1041
+ processed_stack.push(algonode_id);
1015
1042
  } else {
1016
- processed_stack.push(id);
1043
+ algonode.next = node_id;
1017
1044
  }
1045
+ } else {
1046
+ processed_stack.push(algonode_id);
1018
1047
  }
1019
1048
  }
1020
1049
  }
1021
-
1022
1050
  node.stack = processed_stack;
1023
1051
  }
1024
-
1025
1052
  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
-
1053
+ var _collection_20, _selectValue_18, proc, stack1, stack2;
1054
+ _selectValue_18 = node.type;
1055
+ if (_selectValue_18 === 'question') {
1056
+ increment_if_count(context, node);
1035
1057
  stack1 = node.stack.slice();
1036
1058
  stack1.push(node_id);
1037
-
1038
1059
  stack2 = node.stack.slice();
1039
1060
  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
- );
1061
+ traverse_node(context, node.two, stack2);
1062
+ traverse_node(context, node.one, stack1);
1052
1063
  } else {
1053
- if (node.final) {
1054
- decrement_if_count(
1055
- context,
1056
- node
1057
- );
1058
- } else {
1064
+ if (_selectValue_18 === 'arrow-loop') {
1059
1065
  stack1 = node.stack.slice();
1060
-
1061
- traverse_node(
1062
- context,
1063
- node.one,
1064
- stack1
1065
- );
1066
+ stack1.push(node_id);
1067
+ traverse_node(context, node.one, stack1);
1068
+ } else {
1069
+ if (_selectValue_18 === 'arrow-stub') {
1070
+ decrement_arrow_count(context, node);
1071
+ } else {
1072
+ if (_selectValue_18 === 'parbegin') {
1073
+ _collection_20 = node.procs;
1074
+ for (proc of _collection_20) {
1075
+ flow_no_loop(context.nodes, proc.start);
1076
+ }
1077
+ } else {
1078
+ if (node.final) {
1079
+ decrement_if_count(context, node);
1080
+ } else {
1081
+ stack1 = node.stack.slice();
1082
+ traverse_node(context, node.one, stack1);
1083
+ }
1084
+ }
1085
+ }
1066
1086
  }
1067
1087
  }
1068
1088
  }
1069
-
1070
1089
  function traverse_node(context, node_id, stack) {
1071
1090
  var node;
1072
-
1073
1091
  if (node_id) {
1074
1092
  node = context.nodes[node_id];
1075
-
1076
1093
  if (!node.stack) {
1077
1094
  node.stack = [];
1078
1095
  node.refs = node.prev.length;
1079
1096
  }
1080
-
1081
1097
  node.refs--;
1082
-
1083
- merge_incoming_branches(
1084
- context,
1085
- node_id,
1086
- node,
1087
- stack
1088
- );
1089
-
1098
+ merge_converging_branches(context, node_id, node, stack);
1090
1099
  if (!(node.refs > 0)) {
1091
- recurse_traversal(
1092
- context,
1093
- node_id,
1094
- node
1095
- );
1100
+ recurse_traversal(context, node_id, node);
1096
1101
  }
1097
1102
  }
1098
1103
  }
1099
-
1100
- module.exports = {
1101
- flow_no_loop: flow_no_loop
1102
- };
1104
+ module.exports = { flow_no_loop };
1103
1105
  },{}],7:[function(require,module,exports){
1104
1106
  var {addRange} = require("./tools")
1105
1107
 
@@ -1310,147 +1312,6 @@ function redirectNode(nodes, node, from, to) {
1310
1312
  }
1311
1313
 
1312
1314
  function structFlow(nodes, branches, filename, translate, options) {
1313
- function flowGraph(nodes, nodeId, branchingStack) {
1314
- if (!nodeId) {
1315
- return;
1316
- }
1317
-
1318
- const node = nodes[nodeId];
1319
-
1320
- if (!node.stack) {
1321
- node.stack = [];
1322
- node.remaining = node.prev.length;
1323
- }
1324
- node.remaining--;
1325
-
1326
- mergeBranchingStack(nodes, node, branchingStack);
1327
- if (node.remaining > 0) {
1328
- return;
1329
- }
1330
-
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
- }
1337
-
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);
1358
- }
1359
- }
1360
-
1361
- function decrementBranchingForArrow(nodes, node) {
1362
- var algonode = nodes[node.arrow];
1363
- algonode.branching--;
1364
- }
1365
-
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--;
1373
- }
1374
- }
1375
- }
1376
- return stub;
1377
- }
1378
-
1379
- function mergeBranchingStack(nodes, node, branchingStack) {
1380
- // Append all elements of the branching stack to node.stack
1381
- addRange(node.stack, branchingStack);
1382
-
1383
- // Build a dictionary of occurrences
1384
- const dictionary = buildDictionaryOfOccurences(node);
1385
-
1386
- // Merge all nodes
1387
- mergeAll(nodes, node, dictionary);
1388
-
1389
- // Rebuild the stack
1390
- node.stack = buildStackFromDictionary(dictionary);
1391
- }
1392
-
1393
- function addRange(dst, src) {
1394
- for (let i = 0; i < src.length; i++) {
1395
- dst.push(src[i]);
1396
- }
1397
- }
1398
-
1399
- function buildStackFromDictionary(dictionary) {
1400
- const rebuiltStack = [];
1401
- for (const id in dictionary) {
1402
- if (dictionary[id] > 0) {
1403
- rebuiltStack.push(id);
1404
- }
1405
- }
1406
- return rebuiltStack;
1407
- }
1408
-
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;
1414
- }
1415
- return dictionary;
1416
- }
1417
-
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
- }
1434
- }
1435
- }
1436
- }
1437
-
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;
1444
- }
1445
- }
1446
- }
1447
- }
1448
- function isInMap(map, key) {
1449
- if (!map) {
1450
- return false;
1451
- }
1452
- return key in map;
1453
- }
1454
1315
 
1455
1316
  function prepareQuestions(nodes) {
1456
1317
  for (const nodeId in nodes) {
@@ -1552,136 +1413,12 @@ function structFlow(nodes, branches, filename, translate, options) {
1552
1413
  return stub;
1553
1414
  }
1554
1415
 
1555
- function copySide(dst, src) {
1556
- if (src.side) {
1557
- dst.side = src.side;
1558
- }
1559
- }
1560
-
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
- }
1581
- }
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);
1604
- }
1605
- output.push(copy);
1606
- } else {
1607
- output.push(node);
1608
- }
1609
- }
1610
- }
1611
-
1612
- function findPlacesToBreak(body, endId, output) {
1613
- if (body.length === 0) {
1614
- output.push(body);
1615
- return;
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
- }
1634
-
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;
1654
- }
1655
- if (findLoopEnd(node.no, endId)) {
1656
- return true;
1657
- }
1658
- }
1659
- }
1660
- return false;
1661
- }
1662
-
1663
- function addBreaks(toBreak) {
1664
- for (var body of toBreak) {
1665
- body.push({
1666
- type: "break",
1667
- });
1668
- }
1669
- }
1670
-
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;
1416
+ function onError(message, nodeId) {
1417
+ throw createError(
1418
+ translate(message),
1419
+ filename,
1420
+ nodeId
1421
+ );
1685
1422
  }
1686
1423
 
1687
1424
  function structMain() {
@@ -1690,24 +1427,19 @@ function structFlow(nodes, branches, filename, translate, options) {
1690
1427
  var result = [];
1691
1428
 
1692
1429
  for (var branch of branches) {
1693
- if (options.noLoop) {
1694
- flow_no_loop(nodes, branch.next, []);
1695
- } else {
1696
- flowGraph(nodes, branch.next, []);
1697
- }
1430
+ flow_no_loop(nodes, branch.next, []);
1698
1431
  }
1699
1432
 
1700
1433
  for (var branch of branches) {
1701
1434
  var body = [];
1702
- buildTree(nodes, branch.next, body, "<dummy id>");
1703
- var body2 = [];
1704
- rewriteTree(body, 0, undefined, body2);
1435
+ buildTree(nodes, branch.next, body, "<dummy id>", undefined, onError);
1436
+
1705
1437
  result.push({
1706
1438
  name: branch.content,
1707
1439
  branchId: branch.branchId,
1708
- start: branch.next,
1440
+ id: branch.id,
1709
1441
  refs: branch.prev.length,
1710
- body: optimizeTree(body2),
1442
+ body: optimizeTree(body),
1711
1443
  });
1712
1444
  }
1713
1445
 
@@ -1719,16 +1451,22 @@ function structFlow(nodes, branches, filename, translate, options) {
1719
1451
  module.exports = { structFlow, redirectNode };
1720
1452
 
1721
1453
  },{"./noloop":6,"./technicalTree":9,"./tools":10,"./treeTools":12}],9:[function(require,module,exports){
1722
- function buildTree(nodes, nodeId, body, stopId) {
1454
+ function buildTree(nodes, nodeId, body, stopId, afterLoop, onError) {
1723
1455
  while (nodeId) {
1724
- if (nodeId === stopId) {return;}
1456
+ if (nodeId === afterLoop) {
1457
+ body.push({type: "break"})
1458
+ return
1459
+ }
1460
+ if (nodeId === stopId) {
1461
+ return;
1462
+ }
1725
1463
  const node = nodes[nodeId];
1726
1464
  let transformed;
1727
1465
  let next;
1728
1466
 
1729
1467
  if (node.type === "question") {
1730
1468
  next = reserveNext(nodes, node)
1731
-
1469
+
1732
1470
  transformed = {
1733
1471
  id: node.id,
1734
1472
  type: "question",
@@ -1740,25 +1478,43 @@ function buildTree(nodes, nodeId, body, stopId) {
1740
1478
  const yesNodeId = node.flag1 === 1 ? node.one : node.two;
1741
1479
  const noNodeId = node.flag1 === 1 ? node.two : node.one;
1742
1480
 
1743
- buildTree(nodes, yesNodeId, transformed.yes, node.next);
1744
- buildTree(nodes, noNodeId, transformed.no, node.next);
1745
- } else if (node.type === "arrow-loop") {
1481
+ buildTree(nodes, yesNodeId, transformed.yes, node.next, afterLoop, onError);
1482
+ buildTree(nodes, noNodeId, transformed.no, node.next, afterLoop, onError);
1483
+ if (next === afterLoop) {
1484
+ next = undefined
1485
+ }
1486
+ } else if (node.type == "loopbegin") {
1746
1487
  transformed = {
1747
1488
  id: node.id,
1748
1489
  type: "loopbegin",
1749
- content: "",
1750
- end: node.stub
1490
+ content: node.content,
1491
+ end: node.end,
1492
+ body: []
1751
1493
  };
1752
-
1753
- next = node.one;
1754
- } else if (node.type === "arrow-stub") {
1494
+ var end = nodes[node.end]
1495
+ buildTree(nodes, node.one, transformed.body, node.end, end.one, onError)
1496
+ next = node.next;
1497
+ } else if (node.type == "loopend") {
1498
+ if (stopId !== afterLoop) {
1499
+ onError(
1500
+ "An exit from the loop must lead to the point right after the loop end",
1501
+ node.id
1502
+ )
1503
+ }
1504
+ return
1505
+ } else if (node.type === "arrow-loop") {
1755
1506
  transformed = {
1756
1507
  id: node.id,
1757
- type: "loopend",
1758
- start: node.arrow
1508
+ type: "loopbegin",
1509
+ content: "",
1510
+ end: node.stub,
1511
+ body: []
1759
1512
  };
1760
-
1761
- next = node.one;
1513
+ var end = nodes[node.stub]
1514
+ buildTree(nodes, node.one, transformed.body, node.stub, end.one, onError)
1515
+ next = node.next;
1516
+ } else if (node.type === "arrow-stub") {
1517
+ return
1762
1518
  } else if (node.type === "parbegin") {
1763
1519
  transformed = {
1764
1520
  id: node.id,
@@ -1771,7 +1527,7 @@ function buildTree(nodes, nodeId, body, stopId) {
1771
1527
  body: []
1772
1528
  }
1773
1529
  transformed.procs.push(childProc)
1774
- buildTree(nodes, proc.start, childProc.body, undefined)
1530
+ buildTree(nodes, proc.start, childProc.body, undefined, undefined, buildTree)
1775
1531
  }
1776
1532
  next = node.one;
1777
1533
  } else {
@@ -1980,6 +1736,153 @@ var translationsNo = {
1980
1736
  "Start timer": "Start tidtaker"
1981
1737
  };
1982
1738
 
1739
+ var translationsFr = {
1740
+ error: 'Erreur',
1741
+ not: 'non',
1742
+ break: 'quitter la boucle',
1743
+ and: 'et',
1744
+ or: 'ou',
1745
+ if: 'Si',
1746
+ else: 'Sinon',
1747
+ empty: 'Vide',
1748
+ 'loop forever': 'Boucler indéfiniment',
1749
+ pass: 'Ignorer',
1750
+ 'Only the rightmost Case icon can be empty': "Seule l'icône Case la plus à droite peut être vide",
1751
+ 'Error parsing JSON': "Erreur lors de l'analyse du JSON",
1752
+ 'A Loop begin icon must have content': "Une icône de début de boucle doit avoir un contenu",
1753
+ 'A Question icon must have content': "Une icône Question doit avoir un contenu",
1754
+ 'A Select icon must have content': "Une icône Select doit avoir un contenu",
1755
+ 'Unexpected case value': 'Valeur de cas inattendue',
1756
+ 'Loop end expected here': 'Fin de boucle attendue ici',
1757
+ 'An exit from the loop must lead to the point right after the loop end': 'Une sortie de boucle doit mener au point situé juste après la fin de la boucle',
1758
+ 'A silhouette branch is not referenced': "Une branche de silhouette n'est pas référencée",
1759
+ 'Call subroutine': 'Appeler la sous-routine',
1760
+ Procedure: 'Procédure',
1761
+ 'End of procedure': 'Fin de la procédure',
1762
+ Subroutine: 'Sous-routine',
1763
+ 'End of subroutine': 'Fin de la sous-routine',
1764
+ Description: 'Description',
1765
+ Algorithm: 'Algorithme',
1766
+ Remarks: 'Remarques',
1767
+ Parameters: 'Paramètres',
1768
+ 'Group of parallel processes': 'Groupe de processus parallèles',
1769
+ 'Parallel process': 'Processus parallèle',
1770
+ 'Start at': 'Commencer à',
1771
+ 'Do for': 'Exécuter pendant',
1772
+ 'Pause': 'Pause',
1773
+ 'Start timer': 'Démarrer le minuteur'
1774
+ };
1775
+
1776
+ var translationsDe = {
1777
+ error: 'Fehler',
1778
+ not: 'nicht',
1779
+ break: 'Schleife beenden',
1780
+ and: 'und',
1781
+ or: 'oder',
1782
+ if: 'Wenn',
1783
+ else: 'Sonst',
1784
+ empty: 'Leer',
1785
+ 'loop forever': 'Endlos wiederholen',
1786
+ pass: 'Überspringen',
1787
+ 'Only the rightmost Case icon can be empty': 'Nur das äußerste rechte Case-Symbol darf leer sein',
1788
+ 'Error parsing JSON': 'Fehler beim Parsen von JSON',
1789
+ 'A Loop begin icon must have content': 'Ein Schleifenstart-Symbol muss Inhalt haben',
1790
+ 'A Question icon must have content': 'Ein Frage-Symbol muss Inhalt haben',
1791
+ 'A Select icon must have content': 'Ein Auswahl-Symbol muss Inhalt haben',
1792
+ 'Unexpected case value': 'Unerwarteter Fallwert',
1793
+ 'Loop end expected here': 'Schleifenende wird hier erwartet',
1794
+ 'An exit from the loop must lead to the point right after the loop end': 'Ein Ausgang aus der Schleife muss direkt hinter das Schleifenende führen',
1795
+ 'A silhouette branch is not referenced': 'Ein Silhouettenzweig wird nicht referenziert',
1796
+ 'Call subroutine': 'Unterprogramm aufrufen',
1797
+ Procedure: 'Prozedur',
1798
+ 'End of procedure': 'Ende der Prozedur',
1799
+ Subroutine: 'Unterprogramm',
1800
+ 'End of subroutine': 'Ende des Unterprogramms',
1801
+ Description: 'Beschreibung',
1802
+ Algorithm: 'Algorithmus',
1803
+ Remarks: 'Bemerkungen',
1804
+ Parameters: 'Parameter',
1805
+ 'Group of parallel processes': 'Gruppe paralleler Prozesse',
1806
+ 'Parallel process': 'Paralleler Prozess',
1807
+ 'Start at': 'Starten bei',
1808
+ 'Do for': 'Ausführen für',
1809
+ 'Pause': 'Pause',
1810
+ 'Start timer': 'Timer starten'
1811
+ };
1812
+
1813
+ var translationsEs = {
1814
+ error: 'Error',
1815
+ not: 'no',
1816
+ break: 'salir del bucle',
1817
+ and: 'y',
1818
+ or: 'o',
1819
+ if: 'Si',
1820
+ else: 'De lo contrario',
1821
+ empty: 'Vacío',
1822
+ 'loop forever': 'Repetir para siempre',
1823
+ pass: 'Omitir',
1824
+ 'Only the rightmost Case icon can be empty': 'Solo el icono Case más a la derecha puede estar vacío',
1825
+ 'Error parsing JSON': 'Error al analizar JSON',
1826
+ 'A Loop begin icon must have content': 'Un icono de inicio de bucle debe tener contenido',
1827
+ 'A Question icon must have content': 'Un icono de Pregunta debe tener contenido',
1828
+ 'A Select icon must have content': 'Un icono de Selección debe tener contenido',
1829
+ 'Unexpected case value': 'Valor de caso inesperado',
1830
+ 'Loop end expected here': 'Se esperaba el final del bucle aquí',
1831
+ 'An exit from the loop must lead to the point right after the loop end': 'Una salida del bucle debe conducir al punto inmediatamente después del final del bucle',
1832
+ 'A silhouette branch is not referenced': 'Una rama de silueta no está referenciada',
1833
+ 'Call subroutine': 'Llamar subrutina',
1834
+ Procedure: 'Procedimiento',
1835
+ 'End of procedure': 'Fin del procedimiento',
1836
+ Subroutine: 'Subrutina',
1837
+ 'End of subroutine': 'Fin de la subrutina',
1838
+ Description: 'Descripción',
1839
+ Algorithm: 'Algoritmo',
1840
+ Remarks: 'Observaciones',
1841
+ Parameters: 'Parámetros',
1842
+ 'Group of parallel processes': 'Grupo de procesos paralelos',
1843
+ 'Parallel process': 'Proceso paralelo',
1844
+ 'Start at': 'Comenzar en',
1845
+ 'Do for': 'Ejecutar durante',
1846
+ 'Pause': 'Pausa',
1847
+ 'Start timer': 'Iniciar temporizador'
1848
+ };
1849
+
1850
+ var translationsLt = {
1851
+ error: 'Klaida',
1852
+ not: 'ne',
1853
+ break: 'nutraukti ciklą',
1854
+ and: 'ir',
1855
+ or: 'arba',
1856
+ if: 'Jei',
1857
+ else: 'Kitaip',
1858
+ empty: 'Tuščias',
1859
+ 'loop forever': 'Kartoti amžinai',
1860
+ pass: 'Praleisti',
1861
+ 'Only the rightmost Case icon can be empty': 'Tik dešiniausia Case piktograma gali būti tuščia',
1862
+ 'Error parsing JSON': 'Klaida analizuojant JSON',
1863
+ 'A Loop begin icon must have content': 'Ciklo pradžios piktograma turi turėti turinį',
1864
+ 'A Question icon must have content': 'Klausimo piktograma turi turėti turinį',
1865
+ 'A Select icon must have content': 'Pasirinkimo piktograma turi turėti turinį',
1866
+ 'Unexpected case value': 'Netikėta atvejo reikšmė',
1867
+ 'Loop end expected here': 'Čia tikimasi ciklo pabaigos',
1868
+ 'An exit from the loop must lead to the point right after the loop end': 'Išėjimas iš ciklo turi vesti į tašką iškart po ciklo pabaigos',
1869
+ 'A silhouette branch is not referenced': 'Silueto šaka nėra susieta',
1870
+ 'Call subroutine': 'Kviesti paprogramę',
1871
+ Procedure: 'Procedūra',
1872
+ 'End of procedure': 'Procedūros pabaiga',
1873
+ Subroutine: 'Paprogramė',
1874
+ 'End of subroutine': 'Paprogramės pabaiga',
1875
+ Description: 'Aprašymas',
1876
+ Algorithm: 'Algoritmas',
1877
+ Remarks: 'Pastabos',
1878
+ Parameters: 'Parametrai',
1879
+ 'Group of parallel processes': 'Lygiagrečių procesų grupė',
1880
+ 'Parallel process': 'Lygiagretus procesas',
1881
+ 'Start at': 'Pradėti nuo',
1882
+ 'Do for': 'Vykdyti',
1883
+ 'Pause': 'Pauzė',
1884
+ 'Start timer': 'Paleisti laikmatį'
1885
+ };
1983
1886
 
1984
1887
  var translations = translationsEn
1985
1888
 
@@ -1994,12 +1897,19 @@ function setUpLanguage(language) {
1994
1897
  translations = translationsNo
1995
1898
  } else if (language === "en") {
1996
1899
  translations = translationsEn
1900
+ } else if (language === "fr") {
1901
+ translations = translationsFr
1902
+ } else if (language === "de") {
1903
+ translations = translationsDe
1904
+ } else if (language === "es") {
1905
+ translations = translationsEs
1906
+ } else if (language === "lt") {
1907
+ translations = translationsLt
1997
1908
  } else {
1998
1909
  translations = {}
1999
1910
  }
2000
1911
  }
2001
1912
 
2002
-
2003
1913
  module.exports = { setUpLanguage, translate };
2004
1914
  },{}],12:[function(require,module,exports){
2005
1915
 
@@ -2009,23 +1919,21 @@ function optimizeTree(steps) {
2009
1919
  for (var step of steps) {
2010
1920
  if (step.type === "end" || step.type === "branch" || step.type === "loopend") { continue }
2011
1921
  if ((step.type === "action" || step.type === "comment") && !step.content) { continue }
2012
- var copy
2013
1922
  if (step.type === "question") {
2014
- copy = optimizeQuestion(step)
1923
+ optimizeQuestion(step, result)
2015
1924
  } else if (step.type === "parbegin") {
2016
- copy = optimizeParbegin(step)
2017
- } else if (step.type === "loop") {
2018
- copy = optimizeLoop(step)
1925
+ optimizeParbegin(step, result)
1926
+ } else if (step.type === "loop" || step.type === "loopbegin") {
1927
+ optimizeLoop(step, result)
2019
1928
  } else {
2020
- copy = step
1929
+ result.push(step)
2021
1930
  }
2022
- result.push(copy)
2023
1931
  }
2024
1932
 
2025
1933
  return result
2026
1934
  }
2027
1935
 
2028
- function optimizeParbegin(step) {
1936
+ function optimizeParbegin(step, output) {
2029
1937
  var procs = []
2030
1938
  for (var proc of step.procs) {
2031
1939
  var procCopy = {
@@ -2034,50 +1942,62 @@ function optimizeParbegin(step) {
2034
1942
  }
2035
1943
  procs.push(procCopy)
2036
1944
  }
2037
- return {
1945
+ output.push({
2038
1946
  id: step.id,
2039
1947
  type: step.type,
2040
1948
  procs: procs
2041
- }
1949
+ })
2042
1950
  }
2043
1951
 
2044
- function optimizeLoop(step) {
2045
- return {
1952
+ function optimizeLoop(step, output) {
1953
+ output.push({
2046
1954
  id: step.id,
2047
- type: step.type,
1955
+ type: "loop",
2048
1956
  content: step.content,
2049
1957
  body: optimizeTree(step.body)
1958
+ })
1959
+ }
1960
+
1961
+ function endsWithBreak(body) {
1962
+ if (body.length === 0) {
1963
+ return false
2050
1964
  }
1965
+ var lastId = body.length - 1
1966
+ return body[lastId].type === "break"
2051
1967
  }
2052
1968
 
2053
- function optimizeQuestion(step) {
1969
+ function optimizeQuestion(step, output) {
2054
1970
  var yes = optimizeTree(step.yes)
2055
1971
  var no = optimizeTree(step.no)
2056
- if (yes.length === 0 && no.length === 0) {
2057
- return {
2058
- side: step.side,
2059
- type: step.type,
2060
- content: step.content,
2061
- yes: [],
2062
- no: []
2063
- }
2064
- }
2065
- if (yes.length === 0) {
2066
- return {
2067
- side: step.side,
2068
- type: step.type,
2069
- content: {operator:"not",operand:step.content},
2070
- yes: no,
2071
- no: []
2072
- }
2073
- }
2074
- return {
1972
+ var breakYes = endsWithBreak(yes)
1973
+ var breakNo = endsWithBreak(no)
1974
+
1975
+ var result = {
1976
+ id: step.id,
2075
1977
  side: step.side,
2076
- type: step.type,
2077
- content: step.content,
2078
- yes: yes,
2079
- no: no
1978
+ type: step.type
1979
+ }
1980
+ if (breakYes && breakNo) {
1981
+ yes.pop()
1982
+ no.pop()
2080
1983
  }
1984
+ if (yes.length === 0 && no.length === 0) {
1985
+ result.content = step.content
1986
+ result.yes = []
1987
+ result.no = []
1988
+ } else if (yes.length === 0) {
1989
+ result.content = {operator:"not",operand:step.content}
1990
+ result.yes = no
1991
+ result.no = []
1992
+ } else {
1993
+ result.content = step.content,
1994
+ result.yes = yes,
1995
+ result.no = no
1996
+ }
1997
+ output.push(result)
1998
+ if (breakYes && breakNo) {
1999
+ output.push({type: "break"})
2000
+ }
2081
2001
  }
2082
2002
 
2083
2003