drakongen 1.4.7 → 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 (142) hide show
  1. package/browser/drakongen.js +959 -469
  2. package/browsertest.html +20 -5
  3. package/buildexamples.js +41 -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/BugBadExit.drakon +69 -0
  8. package/examples/BugBadExit.txt +16 -0
  9. package/examples/Complex arrow-parallel.drakon +116 -0
  10. package/examples/Complex arrow-parallel.txt +40 -0
  11. package/examples/Complex arrow.txt +1 -0
  12. package/examples/How to tune PID on a quadcopter.txt +1 -0
  13. package/examples/NestedArrowLoop.drakon +38 -0
  14. package/examples/NestedArrowLoop.txt +20 -0
  15. package/examples/Workout foreach.drakon +7 -2
  16. package/examples/Workout foreach.txt +1 -0
  17. package/examples/all icons.drakon +227 -0
  18. package/examples/all icons.txt +68 -0
  19. package/examples/ar01-action.drakon +31 -0
  20. package/examples/ar01-action.txt +9 -0
  21. package/examples/ar10-parallel.drakon +70 -0
  22. package/examples/ar10-parallel.txt +20 -0
  23. package/examples/badShortCircuit.drakon +34 -0
  24. package/examples/badShortCircuit.txt +7 -0
  25. package/examples/getToken.txt +2 -0
  26. package/examples/monitorPaymentStatus.drakon +56 -0
  27. package/examples/monitorPaymentStatus.txt +25 -0
  28. package/examples/no-loop/00.Empty.txt +6 -0
  29. package/examples/no-loop/01. /320/221/320/265/320/273/321/213/320/271.txt" +5 -0
  30. package/examples/no-loop/02. /320/247/321/221/321/200/320/275/321/213/320/271.txt" +8 -0
  31. package/examples/no-loop/03. /320/241/320/265/321/200/321/213/320/271.txt" +7 -0
  32. package/examples/no-loop/04. /320/221/321/203/321/200/321/213/320/271.txt" +7 -0
  33. package/examples/no-loop/05. /320/226/321/221/320/273/321/202/321/213/320/271.txt" +7 -0
  34. package/examples/no-loop/06. /320/221/320/260/320/263/321/200/320/276/320/262/321/213/320/271.txt" +9 -0
  35. 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
  36. 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
  37. 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
  38. package/examples/no-loop/10. /320/240/320/276/320/267/320/276/320/262/321/213/320/271.txt" +13 -0
  39. package/examples/no-loop/11. /320/227/320/260/321/211/320/270/321/202/320/275/321/213/320/271.txt" +9 -0
  40. package/examples/no-loop/12. /320/221/320/276/320/273/320/276/321/202/320/275/321/213/320/271.txt" +9 -0
  41. 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
  42. package/examples/no-loop/14. /320/227/320/276/320/273/320/276/321/202/320/276/320/271.txt" +17 -0
  43. package/examples/no-loop/15. /320/241/320/270/320/275/320/270/320/271.txt" +20 -0
  44. package/examples/no-loop/16. /320/223/320/276/320/273/321/203/320/261/320/276/320/271.txt" +11 -0
  45. 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
  46. package/examples/no-loop/18. /320/241/321/202/320/260/320/273/321/214/320/275/320/276/320/271.txt" +15 -0
  47. package/examples/no-loop/19. Lilla.txt +11 -0
  48. 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" +17 -0
  49. 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" +16 -0
  50. package/examples/no-loop/Adaptive design.txt +31 -0
  51. package/examples/no-loop/And test.txt +9 -0
  52. package/examples/no-loop/Arrow - double exit.txt +13 -0
  53. package/examples/no-loop/BugBadExit.txt +16 -0
  54. package/examples/no-loop/Comments.txt +19 -0
  55. package/examples/no-loop/Complex arrow-parallel.txt +40 -0
  56. package/examples/no-loop/Complex arrow.txt +37 -0
  57. package/examples/no-loop/DoubleArrow.txt +17 -0
  58. package/examples/no-loop/Find pointing nodes.txt +24 -0
  59. package/examples/no-loop/How to tune PID on a quadcopter.txt +145 -0
  60. package/examples/no-loop/Logical_And.txt +9 -0
  61. package/examples/no-loop/Logical_And_Inv.txt +9 -0
  62. package/examples/no-loop/Logical_And_Inv_x2.txt +15 -0
  63. package/examples/no-loop/Logical_Or.txt +9 -0
  64. package/examples/no-loop/Logical_Or_Inv.txt +9 -0
  65. package/examples/no-loop/Mind one.txt +18 -0
  66. package/examples/no-loop/NestedArrowLoop.txt +20 -0
  67. package/examples/no-loop/Or test.txt +9 -0
  68. package/examples/no-loop/Silhouette test 1.txt +31 -0
  69. package/examples/no-loop/Work out action-check.txt +9 -0
  70. package/examples/no-loop/Workout foreach.txt +13 -0
  71. package/examples/no-loop/all icons.txt +68 -0
  72. package/examples/no-loop/ar01-action.txt +9 -0
  73. package/examples/no-loop/ar01.txt +8 -0
  74. package/examples/no-loop/ar02.txt +10 -0
  75. package/examples/no-loop/ar03.txt +8 -0
  76. package/examples/no-loop/ar04.txt +10 -0
  77. package/examples/no-loop/ar05.txt +11 -0
  78. package/examples/no-loop/ar06.txt +13 -0
  79. package/examples/no-loop/ar07.txt +13 -0
  80. package/examples/no-loop/ar08.txt +16 -0
  81. package/examples/no-loop/ar09.txt +16 -0
  82. package/examples/no-loop/ar10-parallel.txt +20 -0
  83. package/examples/no-loop/ar10.txt +17 -0
  84. package/examples/no-loop/ar11.txt +17 -0
  85. package/examples/no-loop/ar12.txt +10 -0
  86. package/examples/no-loop/badShortCircuit.txt +7 -0
  87. package/examples/no-loop/getToken.txt +34 -0
  88. package/examples/no-loop/monitorPaymentStatus.txt +25 -0
  89. package/examples/no-loop/parallel-3.txt +12 -0
  90. package/examples/no-loop/parallel-arrow-loop.txt +12 -0
  91. package/examples/no-loop/parallel-empty-nested.txt +11 -0
  92. package/examples/no-loop/parallel-empty.txt +8 -0
  93. package/examples/no-loop/parallel-nested.txt +16 -0
  94. package/examples/no-loop/sil2.txt +29 -0
  95. package/examples/no-loop/tmp.txt +35 -0
  96. 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
  97. package/examples/parallel-3.drakon +49 -0
  98. package/examples/parallel-3.txt +12 -0
  99. package/examples/parallel-arrow-loop.drakon +44 -0
  100. package/examples/parallel-arrow-loop.txt +12 -0
  101. package/examples/parallel-empty-nested.drakon +40 -0
  102. package/examples/parallel-empty-nested.txt +11 -0
  103. package/examples/parallel-empty.drakon +27 -0
  104. package/examples/parallel-empty.txt +8 -0
  105. package/examples/parallel-nested.drakon +67 -0
  106. package/examples/parallel-nested.txt +16 -0
  107. package/examples/sil2.txt +1 -0
  108. package/examples/tmp.txt +2 -0
  109. 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
  110. 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
  111. package/package.json +1 -1
  112. package/secondary/Secondary.drakon +72 -0
  113. package/src/browserTools.js +1 -1
  114. package/src/drakonToPromptStruct.js +41 -15
  115. package/src/drakonToStruct.js +198 -26
  116. package/src/drakongen.js +50 -19
  117. package/src/free.js +69 -0
  118. package/src/index.js +48 -15
  119. package/src/main.js +12 -3
  120. package/src/nodeTools.js +1 -1
  121. package/src/noloop/flow_no_loop.drakon +33 -0
  122. package/src/noloop/noloop.dtproj +6 -0
  123. package/src/noloop/private/decrement_arrow_count.drakon +1 -0
  124. package/src/noloop/private/decrement_if_count.drakon +33 -0
  125. package/src/noloop/private/group_stack_by_id.drakon +63 -0
  126. package/src/noloop/private/increment_if_count.drakon +33 -0
  127. package/src/noloop/private/is_in_map.drakon +1 -0
  128. package/src/noloop/private/merge_converging_branches.drakon +186 -0
  129. package/src/noloop/private/recurse_traversal.drakon +152 -0
  130. package/src/noloop/private/traverse_node.drakon +69 -0
  131. package/src/noloop.js +135 -0
  132. package/src/printPseudo.js +29 -3
  133. package/src/structFlow.js +155 -378
  134. package/src/technicalTree.js +60 -13
  135. package/src/translate.js +176 -4
  136. package/src/treeTools.js +59 -28
  137. package/test_secondary.js +14 -0
  138. package/.vscode/launch.json +0 -18
  139. package/exampleproject/.drakontech/history.json +0 -12
  140. package/exampleproject/.drakontech/opened.txt +0 -1
  141. package/examples/.drakontech/history.json +0 -124
  142. 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) {
@@ -80,7 +81,7 @@ function drakonToPseudocode(drakonJson, name, filename, htmlToString, translate)
80
81
  }
81
82
 
82
83
 
83
- function mindToTree(drakonJson, name, filename, htmlToString) {
84
+ function mindToTree(drakonJson, name, filename, htmlToString, outputToJson) {
84
85
  let drakonGraph;
85
86
  try {
86
87
  drakonJson = drakonJson || ""
@@ -93,14 +94,40 @@ function mindToTree(drakonJson, name, filename, htmlToString) {
93
94
  }
94
95
 
95
96
  const nodes = drakonGraph.items || {};
96
- var root = createMindNode("## " + name)
97
- nodes["root"] = root
98
- connectMindNodesToParent(nodes)
99
- sortMindChildren(nodes)
100
- var lines = []
101
- printMindNode(root, 0, lines, htmlToString, true)
102
- lines.push("")
103
- 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
+ }
104
131
  return {text:text}
105
132
  }
106
133
 
@@ -144,22 +171,29 @@ function printMindNode(node, depth, lines, htmlToString, first) {
144
171
 
145
172
  function createMindNode(name) {
146
173
  return {
147
- "type": "idea",
148
- "content": "<p>" + name + "</p>",
174
+ "type": "graf",
175
+ "content": name,
149
176
  "parent": undefined,
150
- "treeType": "treeview",
151
177
  "ordinal": 0
152
178
  }
153
179
  }
154
180
 
155
181
  module.exports = { drakonToPseudocode, mindToTree };
156
- },{"./drakonToStruct":3,"./printPseudo":5,"./tools":8}],3:[function(require,module,exports){
182
+ },{"./drakonToStruct":3,"./printPseudo":7,"./tools":10}],3:[function(require,module,exports){
157
183
  const { structFlow, redirectNode } = require("./structFlow");
158
184
  const { createError, remove } = require("./tools");
159
185
 
160
186
  var translate;
161
187
 
162
- function drakonToStruct(drakonJson, name, filename, translateFunction) {
188
+ function drakonToStruct(
189
+ drakonJson,
190
+ name,
191
+ filename,
192
+ translateFunction,
193
+ htmlToString,
194
+ options,
195
+ ) {
196
+ options = options || {};
163
197
  translate = translateFunction;
164
198
  let drakonGraph;
165
199
  try {
@@ -175,32 +209,122 @@ function drakonToStruct(drakonJson, name, filename, translateFunction) {
175
209
  const nodes = drakonGraph.items || {};
176
210
 
177
211
  var branches = [];
178
- 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
+ };
179
223
 
180
224
  if (!firstNodeId) {
181
- return {
182
- name: name,
183
- params: drakonGraph.params || "",
184
- description: drakonGraph.description || "",
185
- branches: [],
186
- };
225
+ return result
187
226
  }
188
227
 
228
+ handleParallel(nodes, undefined, firstNodeId, {}, undefined);
189
229
  buildTwoWayConnections(nodes, firstNodeId);
190
230
 
191
231
  rewireSelectsMarkLoops(nodes, filename);
192
232
  branches.forEach((branch) =>
193
- checkBranchIsReferenced(branch, firstNodeId, filename),
233
+ checkBranchIsReferenced(
234
+ branch,
235
+ firstNodeId,
236
+ filename,
237
+ options
238
+ ),
194
239
  );
195
240
  rewireShortcircuit(nodes, filename);
196
241
  branches.forEach((branch) => cutOffBranch(nodes, branch));
197
- var branchTrees = structFlow(nodes, branches, filename, translate);
198
- return {
199
- name: name,
200
- params: drakonGraph.params || "",
201
- description: drakonGraph.description || "",
202
- branches: branchTrees,
203
- };
242
+ var branchTrees = structFlow(nodes, branches, filename, translate, options);
243
+
244
+ result.branches = branchTrees
245
+ result.secondary = findSecondary(branchTrees, options)
246
+ return result
247
+ }
248
+
249
+ function findSecondary(branchTrees, options) {
250
+ if (!options || !options.secondary) {
251
+ return undefined;
252
+ }
253
+ var ordinal = 0;
254
+ for (var branch of branchTrees) {
255
+ var name = branch.name;
256
+ if (name === options.secondary) {
257
+ return ordinal;
258
+ }
259
+ ordinal++;
260
+ }
261
+ return undefined;
262
+ }
263
+
264
+ function handleParallel(nodes, prevNode, nodeId, visited, proc) {
265
+ if (!nodeId) {
266
+ return;
267
+ }
268
+ var node = nodes[nodeId];
269
+ if (node.type === "parend") {
270
+ if (!proc) {
271
+ throw new Error("handleParallel: no proc for parend");
272
+ }
273
+ var endId = proc.end;
274
+ var end;
275
+ if (endId) {
276
+ end = nodes[endId];
277
+ } else {
278
+ end = {
279
+ type: "end",
280
+ id: proc.id + "-" + proc.ordinal + "-end",
281
+ prev: [],
282
+ };
283
+ nodes[end.id] = end;
284
+ proc.end = end.id;
285
+ proc.next = node.one;
286
+ }
287
+ redirectNode(nodes, prevNode, nodeId, end.id);
288
+ return;
289
+ }
290
+ if (nodeId in visited) {
291
+ return;
292
+ }
293
+ visited[nodeId] = true;
294
+ if (node.type === "parbegin") {
295
+ node.procs = [];
296
+ var ordinal = 0;
297
+ var current = node;
298
+ while (true) {
299
+ var start = {
300
+ id: nodeId + "-" + ordinal + "-start",
301
+ type: "action",
302
+ prev: [],
303
+ one: current.one,
304
+ };
305
+ nodes[start.id] = start;
306
+ var childProc = {
307
+ id: nodeId,
308
+ ordinal: ordinal,
309
+ start: start.id,
310
+ };
311
+ var next = current.two;
312
+ node.procs.push(childProc);
313
+ handleParallel(nodes, start, start.one, {}, childProc);
314
+ delete current.one;
315
+ delete current.two;
316
+ if (!next) {
317
+ break;
318
+ }
319
+ current = nodes[next];
320
+ ordinal++;
321
+ }
322
+ node.one = node.procs[0].next;
323
+ handleParallel(nodes, node, node.one, visited, proc);
324
+ } else {
325
+ handleParallel(nodes, node, node.one, visited, proc);
326
+ handleParallel(nodes, node, node.two, visited, proc);
327
+ }
204
328
  }
205
329
 
206
330
  function drakonToGraph(drakonJson, name, filename, translateFunction) {
@@ -227,7 +351,13 @@ function drakonToGraph(drakonJson, name, filename, translateFunction) {
227
351
  rewireSelectsMarkLoops(nodes, filename);
228
352
  rewireShortcircuit(nodes, filename);
229
353
  branches.forEach((branch) =>
230
- checkBranchIsReferenced(branch, firstNodeId, filename),
354
+ checkBranchIsReferenced(
355
+ branch,
356
+ firstNodeId,
357
+ filename,
358
+ undefined,
359
+ undefined,
360
+ ),
231
361
  );
232
362
  branches.forEach((branch) => cutOffBranch(nodes, branch));
233
363
 
@@ -241,16 +371,34 @@ function drakonToGraph(drakonJson, name, filename, translateFunction) {
241
371
  };
242
372
  }
243
373
 
244
- function checkBranchIsReferenced(branch, firstNodeId, filename) {
374
+ function checkBranchIsReferenced(
375
+ branch,
376
+ firstNodeId,
377
+ filename,
378
+ options
379
+ ) {
245
380
  if (branch.id === firstNodeId) {
246
381
  return;
247
382
  }
248
- if (branch.prev.length === 0) {
249
- throw createError(
250
- translate("A silhouette branch is not referenced"),
251
- filename,
252
- branch.id,
253
- );
383
+ if (options) {
384
+ var branchName = branch.content;
385
+ if (branchName === options.secondary) {
386
+ if (branch.prev.length > 0) {
387
+ throw createError(
388
+ translate("A secondary branch is referenced"),
389
+ filename,
390
+ branch.id,
391
+ );
392
+ }
393
+ } else {
394
+ if (branch.prev.length === 0) {
395
+ throw createError(
396
+ translate("A silhouette branch is not referenced"),
397
+ filename,
398
+ branch.id,
399
+ );
400
+ }
401
+ }
254
402
  }
255
403
  }
256
404
 
@@ -334,14 +482,19 @@ function buildTwoWayConnections(nodes, firstNodeId) {
334
482
  node.prev = [];
335
483
  }
336
484
 
337
- traverse(nodes, firstNodeId, {}, connectBack);
485
+ var visitor = function (nodes, node) {
486
+ return connectBack(nodes, node);
487
+ };
488
+
489
+ traverse(nodes, firstNodeId, {}, visitor);
338
490
  }
339
491
 
340
- function findStartNode(nodes, filename, branches) {
492
+ function findStartNode(nodes, filename, branches, htmlToString) {
341
493
  var firstNodeId = undefined;
342
494
  var minBranchId = 10000;
343
495
  for (var id in nodes) {
344
496
  var node = nodes[id];
497
+ decodeNodeContent(node, htmlToString);
345
498
  if (node.type === "branch") {
346
499
  if (node.branchId < minBranchId) {
347
500
  firstNodeId = id;
@@ -373,12 +526,33 @@ function findStartNode(nodes, filename, branches) {
373
526
  id,
374
527
  );
375
528
  }
529
+ } else if (node.final) {
530
+ delete node.one
531
+ delete node.two
376
532
  }
377
533
  }
378
534
 
379
535
  return firstNodeId;
380
536
  }
381
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
+
382
556
  function rewireSelectsMarkLoops(nodes, filename) {
383
557
  for (var id of Object.keys(nodes)) {
384
558
  var node = nodes[id];
@@ -395,6 +569,7 @@ function rewireSelectsMarkLoops(nodes, filename) {
395
569
 
396
570
  function rewireSelect(nodes, selectNode, filename) {
397
571
  var caseNodeId = selectNode.one;
572
+ var caseNode0 = nodes[caseNodeId];
398
573
  while (caseNodeId) {
399
574
  var caseNode = nodes[caseNodeId];
400
575
  caseNodeId = caseNode.two;
@@ -434,6 +609,7 @@ function rewireSelect(nodes, selectNode, filename) {
434
609
  removeNodeOne(nodes, caseNode.id);
435
610
  }
436
611
  }
612
+ caseNode0.side = selectNode.side;
437
613
  removeNodeOne(nodes, selectNode.id);
438
614
  }
439
615
 
@@ -608,6 +784,11 @@ function traverse(nodes, nodeId, visited, action) {
608
784
  action(nodes, node);
609
785
  traverse(nodes, node.one, visited, action);
610
786
  traverse(nodes, node.two, visited, action);
787
+ if (node.procs) {
788
+ for (var proc of node.procs) {
789
+ traverse(nodes, proc.start, visited, action);
790
+ }
791
+ }
611
792
  }
612
793
 
613
794
  function connectBack(nodes, node) {
@@ -619,6 +800,23 @@ function connectBack(nodes, node) {
619
800
  var two = nodes[node.two];
620
801
  two.prev.push(node.id);
621
802
  }
803
+
804
+ if (node.side) {
805
+ var side = nodes[node.side].content;
806
+ if (side) {
807
+ node.side = decodeSide(side);
808
+ } else {
809
+ delete node.side;
810
+ }
811
+ }
812
+ }
813
+
814
+ function decodeSide(content) {
815
+ if (content.indexOf("=") === -1) {
816
+ return translate("Do for") + " " + content;
817
+ } else {
818
+ return translate("Start at") + " " + content;
819
+ }
622
820
  }
623
821
 
624
822
  function markLoopBody(nodes, start, filename) {
@@ -641,32 +839,270 @@ function markLoopBody(nodes, start, filename) {
641
839
 
642
840
  module.exports = { drakonToStruct, drakonToGraph };
643
841
 
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")
842
+ },{"./structFlow":8,"./tools":10}],4:[function(require,module,exports){
843
+ const { drakonToPseudocode, mindToTree } = require("./drakonToPromptStruct");
844
+ const { htmlToString } = require("./browserTools");
845
+ const { setUpLanguage, translate } = require("./translate");
648
846
  const { drakonToStruct } = require("./drakonToStruct");
649
-
847
+ const { freeDiagramToText } = require("./free");
650
848
 
651
849
  window.drakongen = {
652
- toPseudocode: function (drakonJson, name, filename, language) {
653
- setUpLanguage(language)
654
- return drakonToPseudocode(drakonJson, name, filename, htmlToString, translate).text
655
- },
850
+ toPseudocode: function (drakonJson, name, filename, language) {
851
+ setUpLanguage(language);
852
+ return drakonToPseudocode(
853
+ drakonJson,
854
+ name,
855
+ filename,
856
+ htmlToString,
857
+ translate,
858
+ ).text;
859
+ },
860
+
861
+ toMindTree: function (mindJson, name, filename, language) {
862
+ setUpLanguage(language);
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);
870
+ return result.text;
871
+ },
872
+
873
+ freeToText: function (freeJson, name, filename, language) {
874
+ setUpLanguage(language);
875
+ var result = freeDiagramToText(
876
+ freeJson,
877
+ name,
878
+ filename,
879
+ translate,
880
+ htmlToString,
881
+ );
882
+ return result.text;
883
+ },
884
+
885
+ toTree: function (drakonJson, name, filename, language, options) {
886
+ setUpLanguage(language);
887
+ var result = drakonToStruct(
888
+ drakonJson,
889
+ name,
890
+ filename,
891
+ translate,
892
+ htmlToString,
893
+ options,
894
+ );
895
+ return JSON.stringify(result, null, 4);
896
+ },
897
+ };
898
+
899
+ },{"./browserTools":1,"./drakonToPromptStruct":2,"./drakonToStruct":3,"./free":5,"./translate":11}],5:[function(require,module,exports){
900
+ var {addRange} = require("./tools")
901
+ const { createError } = require("./tools");
902
+
903
+ var translate
904
+
905
+ function compareVertically(box1, box2) {
906
+ if (box1.top + box1.height <= box2.top) return -1;
907
+ if (box2.top + box2.height <= box1.top) return 1;
908
+ return 0;
909
+ }
910
+
911
+ function compareHorizontally(box1, box2) {
912
+ if (box1.left + box1.width <= box2.left) return -1;
913
+ if (box2.left + box2.width <= box1.left) return 1;
914
+ return 0;
915
+ }
916
+
917
+ function byTopLeft(box1, box2) {
918
+ var vertical = compareVertically(box1, box2)
919
+ if (vertical == 0) {
920
+ return compareHorizontally(box1, box2)
921
+ }
922
+ return vertical
923
+ }
656
924
 
657
- toMindTree: function (mindJson, name, filename, language) {
658
- setUpLanguage(language)
659
- var result = mindToTree(mindJson, name, filename, htmlToString)
660
- return result.text
661
- },
925
+ function parseDiagram(freeJson, filename) {
926
+ let diagram;
927
+ try {
928
+ freeJson = freeJson || ""
929
+ freeJson = freeJson.trim()
930
+ freeJson = freeJson || "{}"
931
+ diagram = JSON.parse(freeJson);
932
+ } catch (error) {
933
+ var message = translate("Error parsing JSON") + ": " + error.message
934
+ throw createError(message, filename)
935
+ }
936
+ return diagram
937
+ }
662
938
 
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)
939
+ function sortedItems(diagram) {
940
+ var items = diagram.items || {};
941
+ var result = [];
942
+ for (var id in items) {
943
+ var item = items[id];
944
+ if (item.content && item.top && item.left && item.width && item.height) {
945
+ result.push(item);
946
+ }
667
947
  }
948
+ result.sort(byTopLeft);
949
+ return result;
668
950
  }
669
- },{"./browserTools":1,"./drakonToPromptStruct":2,"./drakonToStruct":3,"./translate":9}],5:[function(require,module,exports){
951
+
952
+ function freeDiagramToText(freeJson, name, filename, translateFunction, htmlToString) {
953
+ translate = translateFunction
954
+ var diagram = parseDiagram(freeJson, filename)
955
+ var sorted = sortedItems(diagram)
956
+ var lines = []
957
+ lines.push("## " + name)
958
+ lines.push("")
959
+ for (var item of sorted) {
960
+ var content = htmlToString(item.content)
961
+ addRange(lines, content)
962
+ lines.push("")
963
+ }
964
+ var text = lines.join("\n")
965
+ return {text:text}
966
+ }
967
+
968
+ module.exports = {freeDiagramToText}
969
+ },{"./tools":10}],6:[function(require,module,exports){
970
+ function decrement_arrow_count(context, node) {
971
+ var algonode;
972
+ algonode = context.nodes[node.arrow];
973
+ algonode.branching--;
974
+ }
975
+ function decrement_if_count(context, node) {
976
+ var _collection_12, if_id, if_node;
977
+ _collection_12 = node.stack;
978
+ for (if_id of _collection_12) {
979
+ if_node = context.nodes[if_id];
980
+ if_node.branching--;
981
+ }
982
+ }
983
+ function flow_no_loop(nodes, start_node_id) {
984
+ var context;
985
+ context = { nodes: nodes };
986
+ traverse_node(context, start_node_id, []);
987
+ }
988
+ function group_stack_by_id(stack) {
989
+ var counts_by_id, element, existing;
990
+ counts_by_id = {};
991
+ for (element of stack) {
992
+ if (element in counts_by_id) {
993
+ existing = counts_by_id[element];
994
+ } else {
995
+ existing = 0;
996
+ }
997
+ counts_by_id[element] = existing + 1;
998
+ }
999
+ return counts_by_id;
1000
+ }
1001
+ function increment_if_count(context, node) {
1002
+ var _collection_14, if_id, if_node;
1003
+ _collection_14 = node.stack;
1004
+ for (if_id of _collection_14) {
1005
+ if_node = context.nodes[if_id];
1006
+ if_node.branching++;
1007
+ }
1008
+ }
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;
1018
+ common = node.stack.concat(stack);
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
+ }
1033
+ processed_stack = [];
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);
1042
+ } else {
1043
+ algonode.next = node_id;
1044
+ }
1045
+ } else {
1046
+ processed_stack.push(algonode_id);
1047
+ }
1048
+ }
1049
+ }
1050
+ node.stack = processed_stack;
1051
+ }
1052
+ function recurse_traversal(context, node_id, node) {
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);
1057
+ stack1 = node.stack.slice();
1058
+ stack1.push(node_id);
1059
+ stack2 = node.stack.slice();
1060
+ stack2.push(node_id);
1061
+ traverse_node(context, node.two, stack2);
1062
+ traverse_node(context, node.one, stack1);
1063
+ } else {
1064
+ if (_selectValue_18 === 'arrow-loop') {
1065
+ stack1 = node.stack.slice();
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
+ }
1086
+ }
1087
+ }
1088
+ }
1089
+ function traverse_node(context, node_id, stack) {
1090
+ var node;
1091
+ if (node_id) {
1092
+ node = context.nodes[node_id];
1093
+ if (!node.stack) {
1094
+ node.stack = [];
1095
+ node.refs = node.prev.length;
1096
+ }
1097
+ node.refs--;
1098
+ merge_converging_branches(context, node_id, node, stack);
1099
+ if (!(node.refs > 0)) {
1100
+ recurse_traversal(context, node_id, node);
1101
+ }
1102
+ }
1103
+ }
1104
+ module.exports = { flow_no_loop };
1105
+ },{}],7:[function(require,module,exports){
670
1106
  var {addRange} = require("./tools")
671
1107
 
672
1108
  function makeIndent(depth) {
@@ -679,8 +1115,11 @@ function printWithIndent(lines, indent, output) {
679
1115
  }
680
1116
 
681
1117
  function printPseudo(algorithm, translate, output, htmlToString) {
682
- function printStructuredContent(content, indent, output) {
1118
+ function printStructuredContent(content, indent, output, side) {
683
1119
  var lines = printStructuredContentNoIdent(content)
1120
+ if (side) {
1121
+ lines[0] = side + ": " + lines[0]
1122
+ }
684
1123
  printWithIndent(lines, indent, output)
685
1124
  }
686
1125
 
@@ -744,19 +1183,39 @@ function printPseudo(algorithm, translate, output, htmlToString) {
744
1183
  printError(step, indent, output)
745
1184
  } else if (step.type === "break") {
746
1185
  output.push(indent + translate("break"))
1186
+ } else if (step.type === "parbegin") {
1187
+ printParbegin(step, depth, output)
747
1188
  } else {
748
1189
  printOther(step, indent, output)
749
1190
  }
750
1191
  }
751
1192
  }
1193
+
1194
+ function printParbegin(step, depth, output) {
1195
+ const indent2 = makeIndent(depth + 1)
1196
+ const indent = makeIndent(depth)
1197
+ printWithIndent([translate("Group of parallel processes")], indent, output)
1198
+ for (var proc of step.procs) {
1199
+ printWithIndent([translate("Parallel process") + " " + (proc.ordinal + 1)], indent2, output)
1200
+ printSteps(proc.body, depth + 2, output)
1201
+ }
1202
+ }
752
1203
 
753
1204
  function printOther(step, indent, output) {
1205
+ var side = step.side
754
1206
  if (!step.content && !step.secondary) {return}
755
1207
  if (step.secondary) {
756
- printStructuredContent(step.secondary, indent, output)
1208
+ printStructuredContent(step.secondary, indent, output, side)
1209
+ side = undefined
757
1210
  }
758
1211
  if (step.content) {
759
- printStructuredContent(step.content, indent, output)
1212
+ var content = step.content
1213
+ if (step.type === "pause") {
1214
+ content = translate("Pause") + " " + htmlToString(content).join(" ")
1215
+ } else if (step.type === "timer") {
1216
+ content = translate("Start timer") + " " + htmlToString(content).join(" ")
1217
+ }
1218
+ printStructuredContent(content, indent, output, side)
760
1219
  }
761
1220
  }
762
1221
 
@@ -797,6 +1256,9 @@ function printPseudo(algorithm, translate, output, htmlToString) {
797
1256
  var content = step.content
798
1257
  var lines = printStructuredContentNoIdent(content)
799
1258
  lines[0] = translate("if") + " " + lines[0]
1259
+ if (step.side) {
1260
+ lines[0] = step.side + ": " + lines[0]
1261
+ }
800
1262
  printWithIndent(lines, indent, output)
801
1263
  addRange(output, yesBody)
802
1264
  if (!empty(noBody)) {
@@ -825,402 +1287,186 @@ function printPseudo(algorithm, translate, output, htmlToString) {
825
1287
  }
826
1288
 
827
1289
  module.exports = {printPseudo, printWithIndent, makeIndent}
828
- },{"./tools":8}],6:[function(require,module,exports){
829
- var {buildTree} = require("./technicalTree")
1290
+ },{"./tools":10}],8:[function(require,module,exports){
1291
+ var { buildTree } = require("./technicalTree");
830
1292
  const { createError, sortByProperty } = require("./tools");
831
- const { optimizeTree } = require("./treeTools")
1293
+ const { optimizeTree } = require("./treeTools");
1294
+ const { flow_no_loop } = require("./noloop")
832
1295
 
833
1296
  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
- }
1297
+ if (node.one === from) {
1298
+ node.one = to;
1299
+ }
1300
+ if (node.two === from) {
1301
+ node.two = to;
1302
+ }
1303
+ if (node.next === from) {
1304
+ node.next = to;
1305
+ }
1306
+ if (node.start && node.type === "loopend") {
1307
+ start = nodes[node.start];
1308
+ if (start.next === from) {
1309
+ start.next = to;
848
1310
  }
1311
+ }
849
1312
  }
850
1313
 
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];
859
-
860
- if (!node.stack) {
861
- node.stack = [];
862
- node.remaining = node.prev.length;
863
- }
864
- node.remaining--;
865
-
866
- mergeBranchingStack(nodes, node, branchingStack);
867
- if (node.remaining > 0) {return;}
868
-
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
- }
875
-
876
- const stackOne = node.stack.slice();
877
- const stackTwo = node.stack.slice();
878
- stackOne.push(nodeId);
879
- stackTwo.push(nodeId);
880
-
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
- }
892
- }
893
-
894
- function decrementBranchingForArrow(nodes, node) {
895
- var algonode = nodes[node.arrow]
896
- algonode.branching--
897
- }
898
-
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
- }
908
- }
909
- return stub
910
- }
911
-
912
-
913
-
914
- function mergeBranchingStack(nodes, node, branchingStack) {
915
- // Append all elements of the branching stack to node.stack
916
- addRange(node.stack, branchingStack)
917
-
918
- // Build a dictionary of occurrences
919
- const dictionary = buildDictionaryOfOccurences(node);
1314
+ function structFlow(nodes, branches, filename, translate, options) {
920
1315
 
921
- // Merge all nodes
922
- mergeAll(nodes, node, dictionary);
923
-
924
- // Rebuild the stack
925
- node.stack = buildStackFromDictionary(dictionary);
926
- }
927
-
928
- function addRange(dst, src) {
929
- for (let i = 0; i < src.length; i++) {
930
- dst.push(src[i]);
931
- }
1316
+ function prepareQuestions(nodes) {
1317
+ for (const nodeId in nodes) {
1318
+ const node = nodes[nodeId];
1319
+ if (node.type === "question") {
1320
+ node.branching = 2;
1321
+ } else if (node.type === "arrow-loop") {
1322
+ node.branching = 1;
1323
+ }
932
1324
  }
1325
+ }
933
1326
 
934
- function buildStackFromDictionary(dictionary) {
935
- const rebuiltStack = [];
936
- for (const id in dictionary) {
937
- if (dictionary[id] > 0) {
938
- rebuiltStack.push(id);
939
- }
940
- }
941
- return rebuiltStack;
1327
+ function rewireArrows(nodes, branches) {
1328
+ branches.forEach((branch) =>
1329
+ rewireArrowsInBranch(nodes, branch.id, branch.next, []),
1330
+ );
1331
+ for (var id in nodes) {
1332
+ var node = nodes[id];
1333
+ if (node.type === "arrow-loop") {
1334
+ var stub = insertArrowStub(nodes, node);
1335
+ var visited = {};
1336
+ fillAStack(nodes, stub, stub.arrow, visited);
1337
+ }
942
1338
  }
1339
+ }
943
1340
 
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;
949
- }
950
- return dictionary;
1341
+ function fillAStack(nodes, node, arrowId, visited) {
1342
+ if (node.id in visited) {
1343
+ return;
951
1344
  }
952
-
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
- }
1345
+ visited[node.id] = true;
1346
+ if (!node.astack) {
1347
+ node.astack = {};
982
1348
  }
983
- function isInMap(map, key) {
984
- if (!map) { return false }
985
- return key in map
1349
+ node.astack[arrowId] = true;
1350
+ if (node.id === arrowId) {
1351
+ return;
986
1352
  }
987
-
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
- }
1353
+ for (var prevId of node.prev) {
1354
+ var prev = nodes[prevId];
1355
+ fillAStack(nodes, prev, arrowId, visited);
997
1356
  }
1357
+ }
998
1358
 
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
- }
1359
+ function rewireArrowsInBranch(nodes, prevNodeId, nodeId, arrowStack) {
1360
+ if (!nodeId) {
1361
+ return;
1008
1362
  }
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
- }
1022
- }
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
- }
1363
+ var node = nodes[nodeId];
1364
+ if (node.type === "branch") {
1365
+ return;
1049
1366
  }
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
1367
+ if (node.type === "arrow-loop") {
1368
+ if (!node.noloop) {
1369
+ node.noloop = {};
1370
+ }
1371
+ if (arrowStack.includes(nodeId)) {
1372
+ return;
1373
+ }
1374
+ node.noloop[prevNodeId] = true;
1375
+ arrowStack = arrowStack.slice();
1376
+ arrowStack.push(nodeId);
1377
+ rewireArrowsInBranch(nodes, nodeId, node.one, arrowStack);
1378
+ } else if (node.type === "question") {
1379
+ var left = arrowStack.slice();
1380
+ var right = arrowStack.slice();
1381
+ rewireArrowsInBranch(nodes, nodeId, node.one, left);
1382
+ rewireArrowsInBranch(nodes, nodeId, node.two, right);
1383
+ } else if (node.type === "parbegin") {
1384
+ for (var proc of node.procs) {
1385
+ rewireArrowsInBranch(nodes, undefined, proc.start, []);
1386
+ }
1387
+ rewireArrowsInBranch(nodes, nodeId, node.one, arrowStack);
1388
+ } else {
1389
+ rewireArrowsInBranch(nodes, nodeId, node.one, arrowStack);
1072
1390
  }
1391
+ }
1073
1392
 
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
- }
1393
+ function insertArrowStub(nodes, node) {
1394
+ var stub = {
1395
+ type: "arrow-stub",
1396
+ id: "arrow-stub-" + node.id,
1397
+ arrow: node.id,
1398
+ prev: [],
1399
+ };
1400
+ nodes[stub.id] = stub;
1401
+ node.stub = stub.id;
1402
+ var prev2 = [];
1403
+ for (var prevId of node.prev) {
1404
+ if (prevId in node.noloop) {
1405
+ prev2.push(prevId);
1406
+ } else {
1407
+ stub.prev.push(prevId);
1408
+ var prev = nodes[prevId];
1409
+ redirectNode(nodes, prev, node.id, stub.id);
1410
+ }
1107
1411
  }
1412
+ node.prev = prev2;
1413
+ return stub;
1414
+ }
1108
1415
 
1109
- function findPlacesToBreak(body, endId, output) {
1110
- if (body.length === 0) {
1111
- output.push(body)
1112
- return
1113
- }
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)
1131
- }
1132
- }
1416
+ function onError(message, nodeId) {
1417
+ throw createError(
1418
+ translate(message),
1419
+ filename,
1420
+ nodeId
1421
+ );
1422
+ }
1133
1423
 
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
1158
- }
1424
+ function structMain() {
1425
+ rewireArrows(nodes, branches);
1426
+ prepareQuestions(nodes);
1427
+ var result = [];
1159
1428
 
1160
- function addBreaks(toBreak) {
1161
- for (var body of toBreak) {
1162
- body.push({
1163
- type: "break"
1164
- })
1165
- }
1429
+ for (var branch of branches) {
1430
+ flow_no_loop(nodes, branch.next, []);
1166
1431
  }
1167
1432
 
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
1433
+ for (var branch of branches) {
1434
+ var body = [];
1435
+ buildTree(nodes, branch.next, body, "<dummy id>", undefined, onError);
1436
+
1437
+ result.push({
1438
+ name: branch.content,
1439
+ branchId: branch.branchId,
1440
+ id: branch.id,
1441
+ refs: branch.prev.length,
1442
+ body: optimizeTree(body),
1443
+ });
1182
1444
  }
1183
-
1184
1445
 
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
- }
1206
-
1207
- return sortByProperty(result, "branchId")
1208
- }
1446
+ return sortByProperty(result, "branchId");
1447
+ }
1209
1448
 
1210
- return structMain()
1449
+ return structMain();
1211
1450
  }
1212
1451
  module.exports = { structFlow, redirectNode };
1213
- },{"./technicalTree":7,"./tools":8,"./treeTools":10}],7:[function(require,module,exports){
1214
- function buildTree(nodes, nodeId, body, stopId) {
1452
+
1453
+ },{"./noloop":6,"./technicalTree":9,"./tools":10,"./treeTools":12}],9:[function(require,module,exports){
1454
+ function buildTree(nodes, nodeId, body, stopId, afterLoop, onError) {
1215
1455
  while (nodeId) {
1216
- if (nodeId === stopId) {return;}
1456
+ if (nodeId === afterLoop) {
1457
+ body.push({type: "break"})
1458
+ return
1459
+ }
1460
+ if (nodeId === stopId) {
1461
+ return;
1462
+ }
1217
1463
  const node = nodes[nodeId];
1218
1464
  let transformed;
1219
1465
  let next;
1220
1466
 
1221
1467
  if (node.type === "question") {
1222
1468
  next = reserveNext(nodes, node)
1223
-
1469
+
1224
1470
  transformed = {
1225
1471
  id: node.id,
1226
1472
  type: "question",
@@ -1232,24 +1478,57 @@ function buildTree(nodes, nodeId, body, stopId) {
1232
1478
  const yesNodeId = node.flag1 === 1 ? node.one : node.two;
1233
1479
  const noNodeId = node.flag1 === 1 ? node.two : node.one;
1234
1480
 
1235
- buildTree(nodes, yesNodeId, transformed.yes, node.next);
1236
- buildTree(nodes, noNodeId, transformed.no, node.next);
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") {
1487
+ transformed = {
1488
+ id: node.id,
1489
+ type: "loopbegin",
1490
+ content: node.content,
1491
+ end: node.end,
1492
+ body: []
1493
+ };
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
1237
1505
  } else if (node.type === "arrow-loop") {
1238
1506
  transformed = {
1239
1507
  id: node.id,
1240
1508
  type: "loopbegin",
1241
1509
  content: "",
1242
- end: node.stub
1510
+ end: node.stub,
1511
+ body: []
1243
1512
  };
1244
-
1245
- 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;
1246
1516
  } else if (node.type === "arrow-stub") {
1517
+ return
1518
+ } else if (node.type === "parbegin") {
1247
1519
  transformed = {
1248
1520
  id: node.id,
1249
- type: "loopend",
1250
- start: node.arrow
1251
- };
1252
-
1521
+ type: node.type,
1522
+ procs: []
1523
+ }
1524
+ for (var proc of node.procs) {
1525
+ var childProc = {
1526
+ ordinal: proc.ordinal,
1527
+ body: []
1528
+ }
1529
+ transformed.procs.push(childProc)
1530
+ buildTree(nodes, proc.start, childProc.body, undefined, undefined, buildTree)
1531
+ }
1253
1532
  next = node.one;
1254
1533
  } else {
1255
1534
  transformed = {
@@ -1268,8 +1547,13 @@ function buildTree(nodes, nodeId, body, stopId) {
1268
1547
  ]
1269
1548
  )
1270
1549
  next = node.one;
1550
+ if (node.final) {
1551
+ next = undefined
1552
+ }
1553
+ }
1554
+ if (node.side) {
1555
+ transformed.side = node.side
1271
1556
  }
1272
-
1273
1557
  body.push(transformed);
1274
1558
  nodeId = next;
1275
1559
  }
@@ -1285,6 +1569,9 @@ function copyFields(dst, src, fields) {
1285
1569
  }
1286
1570
 
1287
1571
  function reserveNext(nodes, node) {
1572
+ if (!node.next) {
1573
+ return undefined
1574
+ }
1288
1575
  const target = nodes[node.next];
1289
1576
  if (target.targetTaken) {
1290
1577
  return undefined;
@@ -1296,7 +1583,7 @@ function reserveNext(nodes, node) {
1296
1583
 
1297
1584
  module.exports = {buildTree}
1298
1585
 
1299
- },{}],8:[function(require,module,exports){
1586
+ },{}],10:[function(require,module,exports){
1300
1587
 
1301
1588
  function createError(message, filename, nodeId) {
1302
1589
  var error = new Error(message)
@@ -1337,7 +1624,7 @@ function addRange(to, from) {
1337
1624
  }
1338
1625
  }
1339
1626
  module.exports = { createError, sortByProperty, addRange, remove }
1340
- },{}],9:[function(require,module,exports){
1627
+ },{}],11:[function(require,module,exports){
1341
1628
  var translationsRu = {
1342
1629
  "error": "ОШИБКА",
1343
1630
  "not": "не",
@@ -1366,7 +1653,13 @@ var translationsRu = {
1366
1653
  "Description": "Описание",
1367
1654
  "Algorithm": "Алгоритм",
1368
1655
  Remarks: "Замечания",
1369
- Parameters: "Параметры"
1656
+ Parameters: "Параметры",
1657
+ "Group of parallel processes": "Группа параллельных процессов",
1658
+ "Parallel process": "Параллельный процесс",
1659
+ "Start at": "Начать в",
1660
+ "Do for": "Делать в течение",
1661
+ "Pause": "Пауза",
1662
+ "Start timer": "Запустить таймер"
1370
1663
  }
1371
1664
 
1372
1665
  var translationsEn = {
@@ -1397,7 +1690,13 @@ var translationsEn = {
1397
1690
  Description: 'Description',
1398
1691
  Algorithm: 'Algorithm',
1399
1692
  Remarks: "Remarks",
1400
- Parameters: "Parameters"
1693
+ Parameters: "Parameters",
1694
+ "Group of parallel processes": "Group of parallel processes",
1695
+ "Parallel process": "Parallel process",
1696
+ "Start at": "Start at",
1697
+ "Do for": "Do for",
1698
+ "Pause": "Pause",
1699
+ "Start timer": "Start timer",
1401
1700
  }
1402
1701
 
1403
1702
  var translationsNo = {
@@ -1428,9 +1727,162 @@ var translationsNo = {
1428
1727
  Description: 'Beskrivelse',
1429
1728
  Algorithm: 'Algoritme',
1430
1729
  Remarks: "Bemerkninger",
1431
- Parameters: "Parametere"
1730
+ Parameters: "Parametere",
1731
+ "Group of parallel processes": "Gruppe av parallelle prosesser",
1732
+ "Parallel process": "Parallell prosess",
1733
+ "Start at": "Start ved",
1734
+ "Do for": "Gjør i",
1735
+ "Pause": "Pause",
1736
+ "Start timer": "Start tidtaker"
1737
+ };
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'
1432
1774
  };
1433
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
+ };
1434
1886
 
1435
1887
  var translations = translationsEn
1436
1888
 
@@ -1445,14 +1897,21 @@ function setUpLanguage(language) {
1445
1897
  translations = translationsNo
1446
1898
  } else if (language === "en") {
1447
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
1448
1908
  } else {
1449
1909
  translations = {}
1450
1910
  }
1451
1911
  }
1452
1912
 
1453
-
1454
1913
  module.exports = { setUpLanguage, translate };
1455
- },{}],10:[function(require,module,exports){
1914
+ },{}],12:[function(require,module,exports){
1456
1915
 
1457
1916
  function optimizeTree(steps) {
1458
1917
  var result = []
@@ -1460,54 +1919,85 @@ function optimizeTree(steps) {
1460
1919
  for (var step of steps) {
1461
1920
  if (step.type === "end" || step.type === "branch" || step.type === "loopend") { continue }
1462
1921
  if ((step.type === "action" || step.type === "comment") && !step.content) { continue }
1463
- var copy
1464
1922
  if (step.type === "question") {
1465
- copy = optimizeQuestion(step)
1466
- } else if (step.type === "loop") {
1467
- copy = optimizeLoop(step)
1923
+ optimizeQuestion(step, result)
1924
+ } else if (step.type === "parbegin") {
1925
+ optimizeParbegin(step, result)
1926
+ } else if (step.type === "loop" || step.type === "loopbegin") {
1927
+ optimizeLoop(step, result)
1468
1928
  } else {
1469
- copy = step
1929
+ result.push(step)
1470
1930
  }
1471
- result.push(copy)
1472
1931
  }
1473
1932
 
1474
1933
  return result
1475
1934
  }
1476
1935
 
1477
- function optimizeLoop(step) {
1478
- return {
1936
+ function optimizeParbegin(step, output) {
1937
+ var procs = []
1938
+ for (var proc of step.procs) {
1939
+ var procCopy = {
1940
+ ordinal: proc.ordinal,
1941
+ body: optimizeTree(proc.body)
1942
+ }
1943
+ procs.push(procCopy)
1944
+ }
1945
+ output.push({
1479
1946
  id: step.id,
1480
1947
  type: step.type,
1948
+ procs: procs
1949
+ })
1950
+ }
1951
+
1952
+ function optimizeLoop(step, output) {
1953
+ output.push({
1954
+ id: step.id,
1955
+ type: "loop",
1481
1956
  content: step.content,
1482
1957
  body: optimizeTree(step.body)
1958
+ })
1959
+ }
1960
+
1961
+ function endsWithBreak(body) {
1962
+ if (body.length === 0) {
1963
+ return false
1483
1964
  }
1965
+ var lastId = body.length - 1
1966
+ return body[lastId].type === "break"
1484
1967
  }
1485
1968
 
1486
- function optimizeQuestion(step) {
1969
+ function optimizeQuestion(step, output) {
1487
1970
  var yes = optimizeTree(step.yes)
1488
1971
  var no = optimizeTree(step.no)
1489
- if (yes.length === 0 && no.length === 0) {
1490
- return {
1491
- type: step.type,
1492
- content: step.content,
1493
- yes: [],
1494
- no: []
1495
- }
1496
- }
1497
- if (yes.length === 0) {
1498
- return {
1499
- type: step.type,
1500
- content: {operator:"not",operand:step.content},
1501
- yes: no,
1502
- no: []
1503
- }
1972
+ var breakYes = endsWithBreak(yes)
1973
+ var breakNo = endsWithBreak(no)
1974
+
1975
+ var result = {
1976
+ id: step.id,
1977
+ side: step.side,
1978
+ type: step.type
1504
1979
  }
1505
- return {
1506
- type: step.type,
1507
- content: step.content,
1508
- yes: yes,
1509
- no: no
1980
+ if (breakYes && breakNo) {
1981
+ yes.pop()
1982
+ no.pop()
1510
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
+ }
1511
2001
  }
1512
2002
 
1513
2003