drakongen 1.4.6 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/browser/drakongen.js +1362 -722
  2. package/browsertest.html +9 -1
  3. package/buildexamples.js +51 -4
  4. package/exampleproject/foo.txt +2 -0
  5. package/examples/20. /320/227/320/276/320/273/320/276/321/202/320/276/320/271 - /320/273/320/265/320/262/321/213/320/271 final.drakon" +66 -0
  6. package/examples/21. /320/227/320/276/320/273/320/276/321/202/320/276/320/271 - /320/277/321/200/320/260/320/262/321/213/320/271 final.drakon" +66 -0
  7. package/examples/Complex arrow-parallel.drakon +116 -0
  8. package/examples/Complex arrow-parallel.txt +40 -0
  9. package/examples/Complex arrow.txt +1 -0
  10. package/examples/How to tune PID on a quadcopter.txt +1 -0
  11. package/examples/NestedArrowLoop.drakon +38 -0
  12. package/examples/NestedArrowLoop.txt +20 -0
  13. package/examples/all icons.drakon +227 -0
  14. package/examples/all icons.txt +68 -0
  15. package/examples/ar01-action.drakon +31 -0
  16. package/examples/ar01-action.txt +9 -0
  17. package/examples/ar10-parallel.drakon +70 -0
  18. package/examples/ar10-parallel.txt +20 -0
  19. package/examples/badShortCircuit.drakon +34 -0
  20. package/examples/badShortCircuit.txt +7 -0
  21. package/examples/getToken.txt +2 -0
  22. package/examples/monitorPaymentStatus.drakon +56 -0
  23. package/examples/monitorPaymentStatus.txt +25 -0
  24. package/examples/no-loop/00.Empty.txt +6 -0
  25. package/examples/no-loop/01. /320/221/320/265/320/273/321/213/320/271.txt" +5 -0
  26. package/examples/no-loop/02. /320/247/321/221/321/200/320/275/321/213/320/271.txt" +8 -0
  27. package/examples/no-loop/03. /320/241/320/265/321/200/321/213/320/271.txt" +7 -0
  28. package/examples/no-loop/04. /320/221/321/203/321/200/321/213/320/271.txt" +7 -0
  29. package/examples/no-loop/05. /320/226/321/221/320/273/321/202/321/213/320/271.txt" +7 -0
  30. package/examples/no-loop/06. /320/221/320/260/320/263/321/200/320/276/320/262/321/213/320/271.txt" +9 -0
  31. package/examples/no-loop/07. /320/244/320/270/320/276/320/273/320/265/321/202/320/276/320/262/321/213/320/271.txt" +12 -0
  32. package/examples/no-loop/08. /320/221/320/270/321/200/321/216/320/267/320/276/320/262/321/213/320/271.txt" +7 -0
  33. package/examples/no-loop/09. /320/236/321/200/320/260/320/275/320/266/320/265/320/262/321/213/320/271.txt" +9 -0
  34. package/examples/no-loop/10. /320/240/320/276/320/267/320/276/320/262/321/213/320/271.txt" +13 -0
  35. package/examples/no-loop/11. /320/227/320/260/321/211/320/270/321/202/320/275/321/213/320/271.txt" +9 -0
  36. package/examples/no-loop/12. /320/221/320/276/320/273/320/276/321/202/320/275/321/213/320/271.txt" +9 -0
  37. package/examples/no-loop/13. /320/241/320/260/320/273/320/260/321/202/320/276/320/262/321/213/320/271.txt" +15 -0
  38. package/examples/no-loop/14. /320/227/320/276/320/273/320/276/321/202/320/276/320/271.txt" +17 -0
  39. package/examples/no-loop/15. /320/241/320/270/320/275/320/270/320/271.txt" +20 -0
  40. package/examples/no-loop/16. /320/223/320/276/320/273/321/203/320/261/320/276/320/271.txt" +11 -0
  41. package/examples/no-loop/17. /320/241/320/260/320/273/320/260/321/202/320/276/320/262/321/213/320/271.txt" +10 -0
  42. package/examples/no-loop/18. /320/241/321/202/320/260/320/273/321/214/320/275/320/276/320/271.txt" +15 -0
  43. package/examples/no-loop/19. Lilla.txt +11 -0
  44. package/examples/no-loop/20. /320/227/320/276/320/273/320/276/321/202/320/276/320/271 - /320/273/320/265/320/262/321/213/320/271 final.txt" +16 -0
  45. package/examples/no-loop/21. /320/227/320/276/320/273/320/276/321/202/320/276/320/271 - /320/277/321/200/320/260/320/262/321/213/320/271 final.txt" +17 -0
  46. package/examples/no-loop/Adaptive design.txt +31 -0
  47. package/examples/no-loop/And test.txt +9 -0
  48. package/examples/no-loop/Comments.txt +19 -0
  49. package/examples/no-loop/Logical_And.txt +9 -0
  50. package/examples/no-loop/Logical_And_Inv.txt +9 -0
  51. package/examples/no-loop/Logical_And_Inv_x2.txt +15 -0
  52. package/examples/no-loop/Logical_Or.txt +9 -0
  53. package/examples/no-loop/Logical_Or_Inv.txt +9 -0
  54. package/examples/no-loop/Mind one.txt +18 -0
  55. package/examples/no-loop/Or test.txt +9 -0
  56. package/examples/no-loop/Silhouette test 1.txt +31 -0
  57. package/examples/no-loop/badShortCircuit.txt +7 -0
  58. package/examples/no-loop/getToken.txt +34 -0
  59. package/examples/no-loop/monitorPaymentStatus.txt +25 -0
  60. package/examples/no-loop/sil2.txt +29 -0
  61. package/examples/no-loop/tmp.txt +35 -0
  62. package/examples/no-loop//320/241/320/265/320/274/320/260/320/275/321/202/320/270/321/207/320/265/321/201/320/272/320/276/320/265 /321/217/320/264/321/200/320/276.txt" +36 -0
  63. package/examples/parallel-3.drakon +49 -0
  64. package/examples/parallel-3.txt +12 -0
  65. package/examples/parallel-arrow-loop.drakon +44 -0
  66. package/examples/parallel-arrow-loop.txt +12 -0
  67. package/examples/parallel-empty-nested.drakon +40 -0
  68. package/examples/parallel-empty-nested.txt +11 -0
  69. package/examples/parallel-empty.drakon +27 -0
  70. package/examples/parallel-empty.txt +8 -0
  71. package/examples/parallel-nested.drakon +67 -0
  72. package/examples/parallel-nested.txt +16 -0
  73. package/examples/sil2.txt +1 -0
  74. package/examples/tmp.txt +2 -0
  75. package/examples//320/241/320/265/320/274/320/260/320/275/321/202/320/270/321/207/320/265/321/201/320/272/320/276/320/265 /321/217/320/264/321/200/320/276.free" +130 -0
  76. package/examples//320/241/320/265/320/274/320/260/320/275/321/202/320/270/321/207/320/265/321/201/320/272/320/276/320/265 /321/217/320/264/321/200/320/276.txt" +36 -0
  77. package/package.json +1 -1
  78. package/prompts/noloop/decrement_if_count.drakon +34 -0
  79. package/prompts/noloop/flow_no_loop.drakon +26 -0
  80. package/prompts/noloop/group_stack_by_id.drakon +56 -0
  81. package/prompts/noloop/increment_if_count.drakon +34 -0
  82. package/prompts/noloop/merge_incoming_branches.drakon +105 -0
  83. package/prompts/noloop/recurse_traversal.drakon +88 -0
  84. package/prompts/noloop/traverse_node.drakon +62 -0
  85. package/secondary/Secondary.drakon +72 -0
  86. package/src/browserTools.js +1 -1
  87. package/src/drakonToPromptStruct.js +4 -3
  88. package/src/drakonToStruct.js +170 -15
  89. package/src/drakongen.js +44 -19
  90. package/src/free.js +69 -0
  91. package/src/index.js +42 -15
  92. package/src/main.js +10 -4
  93. package/src/nodeTools.js +1 -1
  94. package/src/noloop.js +181 -0
  95. package/src/printPseudo.js +29 -3
  96. package/src/structFlow.js +381 -334
  97. package/src/technicalTree.js +24 -1
  98. package/src/translate.js +21 -3
  99. package/src/treeTools.js +21 -0
  100. package/test_secondary.js +14 -0
  101. package/.vscode/launch.json +0 -18
  102. package/exampleproject/.drakontech/history.json +0 -12
  103. package/exampleproject/.drakontech/opened.txt +0 -1
  104. package/examples/.drakontech/history.json +0 -124
  105. package/examples/.drakontech/opened.txt +0 -1
@@ -14,7 +14,7 @@ function htmlToString(html) {
14
14
 
15
15
  root.childNodes.forEach((node) => {
16
16
  if (node.tagName === 'P') {
17
- output.push(node.textContent.trim());
17
+ output.push(node.textContent);
18
18
  } else if (node.tagName === 'UL') {
19
19
  node.childNodes.forEach((item) => {
20
20
  if (item.tagName === 'LI') {
@@ -39,16 +39,17 @@ const {drakonToStruct} = require("./drakonToStruct");
39
39
  const {printPseudo, printWithIndent, makeIndent} = require('./printPseudo');
40
40
  const {addRange, sortByProperty} = require("./tools")
41
41
 
42
- function drakonToPseudocode(drakonJson, name, filename, htmlToString, translate) {
43
- var diagram = drakonToStruct(drakonJson, name, filename, translate)
42
+ function drakonToPseudocode(drakonJson, name, filename, htmlToString, translate, options) {
43
+ var diagram = drakonToStruct(drakonJson, name, filename, translate, htmlToString, options)
44
44
  var lines = []
45
45
 
46
46
  lines.push("## " + translate("Procedure") + " \"" + diagram.name + "\"")
47
+ lines.push("")
47
48
  if (diagram.params) {
48
49
  lines.push(translate("Parameters") + ":")
49
50
  addRange(lines, htmlToString(diagram.params))
51
+ lines.push("")
50
52
  }
51
- lines.push("")
52
53
  lines.push(translate("Algorithm") + ":")
53
54
 
54
55
  if (diagram.branches.length === 0) {
@@ -153,450 +154,953 @@ function createMindNode(name) {
153
154
  }
154
155
 
155
156
  module.exports = { drakonToPseudocode, mindToTree };
156
- },{"./drakonToStruct":3,"./printPseudo":5,"./tools":8}],3:[function(require,module,exports){
157
+ },{"./drakonToStruct":3,"./printPseudo":7,"./tools":10}],3:[function(require,module,exports){
157
158
  const { structFlow, redirectNode } = require("./structFlow");
158
159
  const { createError, remove } = require("./tools");
159
160
 
160
- var translate
161
-
162
- function drakonToStruct(drakonJson, name, filename, translateFunction) {
163
- translate = translateFunction
164
- let drakonGraph;
165
- try {
166
- drakonJson = drakonJson || ""
167
- drakonJson = drakonJson.trim()
168
- drakonJson = drakonJson || "{}"
169
- drakonGraph = JSON.parse(drakonJson);
170
- } catch (error) {
171
- var message = translate("Error parsing JSON") + ": " + error.message
172
- throw createError(message, filename)
173
- }
174
-
175
- const nodes = drakonGraph.items || {};
176
-
177
- var branches = []
178
- var firstNodeId = findStartNode(nodes, filename, branches)
179
-
180
- if (!firstNodeId) {
181
- return {
182
- name: name,
183
- params: drakonGraph.params || "",
184
- description: drakonGraph.description || "",
185
- branches: []
186
- }
187
- }
188
-
189
- buildTwoWayConnections(nodes, firstNodeId)
190
-
191
- rewireSelectsMarkLoops(nodes, filename)
192
- branches.forEach(branch => checkBranchIsReferenced(branch, firstNodeId, filename))
193
- rewireShortcircuit(nodes, filename)
194
- branches.forEach(branch => cutOffBranch(nodes, branch))
195
- var branchTrees = structFlow(nodes, branches, filename, translate)
161
+ var translate;
162
+
163
+ function drakonToStruct(
164
+ drakonJson,
165
+ name,
166
+ filename,
167
+ translateFunction,
168
+ htmlToString,
169
+ options,
170
+ ) {
171
+ options = options || {};
172
+ translate = translateFunction;
173
+ let drakonGraph;
174
+ try {
175
+ drakonJson = drakonJson || "";
176
+ drakonJson = drakonJson.trim();
177
+ drakonJson = drakonJson || "{}";
178
+ drakonGraph = JSON.parse(drakonJson);
179
+ } catch (error) {
180
+ var message = translate("Error parsing JSON") + ": " + error.message;
181
+ throw createError(message, filename);
182
+ }
183
+
184
+ const nodes = drakonGraph.items || {};
185
+
186
+ var branches = [];
187
+ var firstNodeId = findStartNode(nodes, filename, branches);
188
+
189
+ if (!firstNodeId) {
196
190
  return {
197
- name: name,
198
- params: drakonGraph.params || "",
199
- description: drakonGraph.description || "",
200
- branches: branchTrees
201
- }
191
+ name: name,
192
+ params: drakonGraph.params || "",
193
+ description: drakonGraph.description || "",
194
+ branches: [],
195
+ };
196
+ }
197
+
198
+ handleParallel(nodes, undefined, firstNodeId, {}, undefined);
199
+ buildTwoWayConnections(nodes, firstNodeId, htmlToString);
200
+
201
+ rewireSelectsMarkLoops(nodes, filename);
202
+ branches.forEach((branch) =>
203
+ checkBranchIsReferenced(
204
+ branch,
205
+ firstNodeId,
206
+ filename,
207
+ options,
208
+ htmlToString,
209
+ ),
210
+ );
211
+ rewireShortcircuit(nodes, filename);
212
+ branches.forEach((branch) => cutOffBranch(nodes, branch));
213
+ 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
+ };
202
221
  }
203
222
 
204
- function drakonToGraph(drakonJson, name, filename, translateFunction) {
205
- translate = translateFunction
206
- let drakonGraph;
207
- try {
208
- drakonGraph = JSON.parse(drakonJson);
209
- } catch (error) {
210
- var message = translate("Error parsing JSON") + ": " + error.message
211
- throw createError(message, filename)
212
- }
213
-
214
- const nodes = drakonGraph.items || {};
215
-
216
- var branches = []
217
- var firstNodeId = findStartNode(nodes, filename, branches)
218
-
219
- if (!firstNodeId) {
220
- return undefined
221
- }
222
-
223
- buildTwoWayConnections(nodes, firstNodeId)
224
-
225
- rewireSelectsMarkLoops(nodes, filename)
226
- rewireShortcircuit(nodes, filename)
227
- branches.forEach(branch => checkBranchIsReferenced(branch, firstNodeId, filename))
228
- branches.forEach(branch => cutOffBranch(nodes, branch))
229
-
230
- var branchTrees = structFlow(nodes, branches, filename, translate)
223
+ function findSecondary(branchTrees, options, htmlToString) {
224
+ if (!options || !options.secondary) {
225
+ return undefined;
226
+ }
227
+ var ordinal = 0;
228
+ for (var branch of branchTrees) {
229
+ var name = htmlToString(branch.name)[0];
230
+ if (name === options.secondary) {
231
+ return ordinal;
232
+ }
233
+ ordinal++;
234
+ }
235
+ return undefined;
236
+ }
231
237
 
232
- return {
233
- name: name,
234
- params: drakonGraph.params || "",
235
- description: drakonGraph.description || "",
236
- branches: branchTrees
237
- }
238
+ function handleParallel(nodes, prevNode, nodeId, visited, proc) {
239
+ if (!nodeId) {
240
+ return;
241
+ }
242
+ var node = nodes[nodeId];
243
+ if (node.type === "parend") {
244
+ if (!proc) {
245
+ throw new Error("handleParallel: no proc for parend");
246
+ }
247
+ var endId = proc.end;
248
+ var end;
249
+ if (endId) {
250
+ end = nodes[endId];
251
+ } else {
252
+ end = {
253
+ type: "end",
254
+ id: proc.id + "-" + proc.ordinal + "-end",
255
+ prev: [],
256
+ };
257
+ nodes[end.id] = end;
258
+ proc.end = end.id;
259
+ proc.next = node.one;
260
+ }
261
+ redirectNode(nodes, prevNode, nodeId, end.id);
262
+ return;
263
+ }
264
+ if (nodeId in visited) {
265
+ return;
266
+ }
267
+ visited[nodeId] = true;
268
+ if (node.type === "parbegin") {
269
+ node.procs = [];
270
+ var ordinal = 0;
271
+ var current = node;
272
+ while (true) {
273
+ var start = {
274
+ id: nodeId + "-" + ordinal + "-start",
275
+ type: "action",
276
+ prev: [],
277
+ one: current.one,
278
+ };
279
+ nodes[start.id] = start;
280
+ var childProc = {
281
+ id: nodeId,
282
+ ordinal: ordinal,
283
+ start: start.id,
284
+ };
285
+ var next = current.two;
286
+ node.procs.push(childProc);
287
+ handleParallel(nodes, start, start.one, {}, childProc);
288
+ delete current.one;
289
+ delete current.two;
290
+ if (!next) {
291
+ break;
292
+ }
293
+ current = nodes[next];
294
+ ordinal++;
295
+ }
296
+ node.one = node.procs[0].next;
297
+ handleParallel(nodes, node, node.one, visited, proc);
298
+ } else {
299
+ handleParallel(nodes, node, node.one, visited, proc);
300
+ handleParallel(nodes, node, node.two, visited, proc);
301
+ }
238
302
  }
239
303
 
304
+ function drakonToGraph(drakonJson, name, filename, translateFunction) {
305
+ translate = translateFunction;
306
+ let drakonGraph;
307
+ try {
308
+ drakonGraph = JSON.parse(drakonJson);
309
+ } catch (error) {
310
+ var message = translate("Error parsing JSON") + ": " + error.message;
311
+ throw createError(message, filename);
312
+ }
313
+
314
+ const nodes = drakonGraph.items || {};
315
+
316
+ var branches = [];
317
+ var firstNodeId = findStartNode(nodes, filename, branches);
318
+
319
+ if (!firstNodeId) {
320
+ return undefined;
321
+ }
322
+
323
+ buildTwoWayConnections(nodes, firstNodeId);
324
+
325
+ rewireSelectsMarkLoops(nodes, filename);
326
+ rewireShortcircuit(nodes, filename);
327
+ branches.forEach((branch) =>
328
+ checkBranchIsReferenced(
329
+ branch,
330
+ firstNodeId,
331
+ filename,
332
+ undefined,
333
+ undefined,
334
+ ),
335
+ );
336
+ branches.forEach((branch) => cutOffBranch(nodes, branch));
337
+
338
+ var branchTrees = structFlow(nodes, branches, filename, translate);
339
+
340
+ return {
341
+ name: name,
342
+ params: drakonGraph.params || "",
343
+ description: drakonGraph.description || "",
344
+ branches: branchTrees,
345
+ };
346
+ }
240
347
 
241
- function checkBranchIsReferenced(branch, firstNodeId, filename) {
242
- if (branch.id === firstNodeId) {
243
- return
244
- }
245
- if (branch.prev.length === 0) {
246
- throw createError(translate("A silhouette branch is not referenced"), filename, branch.id)
247
- }
348
+ function checkBranchIsReferenced(
349
+ branch,
350
+ firstNodeId,
351
+ filename,
352
+ options,
353
+ htmlToString,
354
+ ) {
355
+ if (branch.id === firstNodeId) {
356
+ return;
357
+ }
358
+ if (options && htmlToString) {
359
+ var branchName = htmlToString(branch.content)[0];
360
+ if (branchName === options.secondary) {
361
+ if (branch.prev.length > 0) {
362
+ throw createError(
363
+ translate("A secondary branch is referenced"),
364
+ filename,
365
+ branch.id,
366
+ );
367
+ }
368
+ } else {
369
+ if (branch.prev.length === 0) {
370
+ throw createError(
371
+ translate("A silhouette branch is not referenced"),
372
+ filename,
373
+ branch.id,
374
+ );
375
+ }
376
+ }
377
+ }
248
378
  }
249
379
 
250
380
  function cutOffBranch(nodes, branch) {
251
- var end = {
252
- type: "end",
253
- id: branch.id + "-end",
254
- prev: []
255
- }
256
- nodes[end.id] = end
257
- branch.next = branch.one
258
- var addresses = []
259
- traverseToHitBranch(nodes, branch.id, {}, (prev, node) => addFakeEnd(nodes, prev, node, end, addresses))
381
+ var end = {
382
+ type: "end",
383
+ id: branch.id + "-end",
384
+ prev: [],
385
+ };
386
+ nodes[end.id] = end;
387
+ branch.next = branch.one;
388
+ var addresses = [];
389
+ traverseToHitBranch(nodes, branch.id, {}, (prev, node) =>
390
+ addFakeEnd(nodes, prev, node, end, addresses),
391
+ );
260
392
  }
261
393
 
262
394
  function traverseToHitBranch(nodes, nodeId, visited, action) {
263
- if (!nodeId) {return}
264
- if (nodeId in visited) {return}
265
- visited[nodeId] = true
266
- var node = nodes[nodeId]
267
- if (!node) {return}
268
- if (node.one) {
269
- var one = nodes[node.one]
270
- if (one.type === "branch") {
271
- action(node, one)
272
- } else {
273
- traverseToHitBranch(nodes, node.one, visited, action)
274
- }
395
+ if (!nodeId) {
396
+ return;
397
+ }
398
+ if (nodeId in visited) {
399
+ return;
400
+ }
401
+ visited[nodeId] = true;
402
+ var node = nodes[nodeId];
403
+ if (!node) {
404
+ return;
405
+ }
406
+ if (node.one) {
407
+ var one = nodes[node.one];
408
+ if (one.type === "branch") {
409
+ action(node, one);
410
+ } else {
411
+ traverseToHitBranch(nodes, node.one, visited, action);
275
412
  }
276
- if (node.two) {
277
- var two = nodes[node.two]
278
- if (two.type === "branch") {
279
- action(node, two)
280
- } else {
281
- traverseToHitBranch(nodes, node.two, visited, action)
282
- }
283
- }
413
+ }
414
+ if (node.two) {
415
+ var two = nodes[node.two];
416
+ if (two.type === "branch") {
417
+ action(node, two);
418
+ } else {
419
+ traverseToHitBranch(nodes, node.two, visited, action);
420
+ }
421
+ }
284
422
  }
285
423
 
286
- var idCounter = 1000
424
+ var idCounter = 1000;
287
425
  function addFakeEnd(nodes, prev, node, end, addresses) {
288
- var lastAddress = undefined
289
- if (addresses.length > 0) {
290
- lastAddress = addresses[addresses.length - 1]
291
- }
292
- var address
293
- if (lastAddress && lastAddress.branch === node.id) {
294
- address = lastAddress
295
- } else {
296
- address = {
297
- type: "address",
298
- content: node.content,
299
- id: "ad-" + idCounter,
300
- branch: node.id,
301
- one: end.id,
302
- prev: []
303
- }
304
- idCounter++
305
- nodes[address.id] = address
306
- end.prev.push(address.id)
307
- addresses.push(address)
308
- node.prev.push(address.id)
309
- }
310
- redirectNode(nodes, prev, node.id, address.id)
311
- address.prev.push(prev.id)
312
- node.prev = remove(node.prev, prev.id)
313
- }
314
-
315
- function buildTwoWayConnections(nodes, firstNodeId) {
316
- for (var id in nodes) {
317
- var node = nodes[id]
318
- node.id = id
319
- node.prev = []
320
- }
426
+ var lastAddress = undefined;
427
+ if (addresses.length > 0) {
428
+ lastAddress = addresses[addresses.length - 1];
429
+ }
430
+ var address;
431
+ if (lastAddress && lastAddress.branch === node.id) {
432
+ address = lastAddress;
433
+ } else {
434
+ address = {
435
+ type: "address",
436
+ content: node.content,
437
+ id: "ad-" + idCounter,
438
+ branch: node.id,
439
+ one: end.id,
440
+ prev: [],
441
+ };
442
+ idCounter++;
443
+ nodes[address.id] = address;
444
+ end.prev.push(address.id);
445
+ addresses.push(address);
446
+ node.prev.push(address.id);
447
+ }
448
+ redirectNode(nodes, prev, node.id, address.id);
449
+ address.prev.push(prev.id);
450
+ node.prev = remove(node.prev, prev.id);
451
+ }
452
+
453
+ function buildTwoWayConnections(nodes, firstNodeId, htmlToString) {
454
+ for (var id in nodes) {
455
+ var node = nodes[id];
456
+ node.id = id;
457
+ node.prev = [];
458
+ }
459
+
460
+ var visitor = function (nodes, node) {
461
+ return connectBack(nodes, node, htmlToString);
462
+ };
321
463
 
322
- traverse(nodes, firstNodeId, {}, connectBack)
464
+ traverse(nodes, firstNodeId, {}, visitor);
323
465
  }
324
466
 
325
467
  function findStartNode(nodes, filename, branches) {
326
- var firstNodeId = undefined
327
- var minBranchId = 10000
328
- for (var id in nodes) {
329
- var node = nodes[id]
330
- if (node.type === "branch") {
331
- if (node.branchId < minBranchId) {
332
- firstNodeId = id
333
- minBranchId = node.branchId
334
- }
335
- branches.push(node)
336
- } else if (node.type === "select") {
337
- if (!node.content) {
338
- throw createError(translate("A Select icon must have content"), filename, id)
339
- }
340
- node.cases = [];
341
- } else if (node.type === "loopbegin") {
342
- if (!node.content) {
343
- throw createError(translate("A Loop begin icon must have content"), filename, id)
344
- }
345
- } else if (node.type === "question") {
346
- if (!node.content) {
347
- throw createError(translate("A Question icon must have content"), filename, id)
348
- }
349
- }
350
- }
351
-
352
- return firstNodeId
468
+ var firstNodeId = undefined;
469
+ var minBranchId = 10000;
470
+ for (var id in nodes) {
471
+ var node = nodes[id];
472
+ if (node.type === "branch") {
473
+ if (node.branchId < minBranchId) {
474
+ firstNodeId = id;
475
+ minBranchId = node.branchId;
476
+ }
477
+ branches.push(node);
478
+ } else if (node.type === "select") {
479
+ if (!node.content) {
480
+ throw createError(
481
+ translate("A Select icon must have content"),
482
+ filename,
483
+ id,
484
+ );
485
+ }
486
+ node.cases = [];
487
+ } else if (node.type === "loopbegin") {
488
+ if (!node.content) {
489
+ throw createError(
490
+ translate("A Loop begin icon must have content"),
491
+ filename,
492
+ id,
493
+ );
494
+ }
495
+ } else if (node.type === "question") {
496
+ if (!node.content) {
497
+ throw createError(
498
+ translate("A Question icon must have content"),
499
+ filename,
500
+ id,
501
+ );
502
+ }
503
+ } else if (node.final) {
504
+ delete node.one
505
+ delete node.two
506
+ }
507
+ }
508
+
509
+ return firstNodeId;
353
510
  }
354
511
 
355
512
  function rewireSelectsMarkLoops(nodes, filename) {
356
- for (var id of Object.keys(nodes)) {
357
- var node = nodes[id]
358
- if (!node) { continue }
359
- if (node.type === "select") {
360
- rewireSelect(nodes, node, filename)
361
- } else if (node.type === "loopbegin") {
362
- markLoopBody(nodes, node, filename)
363
- }
513
+ for (var id of Object.keys(nodes)) {
514
+ var node = nodes[id];
515
+ if (!node) {
516
+ continue;
517
+ }
518
+ if (node.type === "select") {
519
+ rewireSelect(nodes, node, filename);
520
+ } else if (node.type === "loopbegin") {
521
+ markLoopBody(nodes, node, filename);
364
522
  }
523
+ }
365
524
  }
366
525
 
367
526
  function rewireSelect(nodes, selectNode, filename) {
368
- var caseNodeId = selectNode.one
369
- while (caseNodeId) {
370
- var caseNode = nodes[caseNodeId]
371
- caseNodeId = caseNode.two
372
- if (caseNode.content) {
373
- caseNode.type = "question"
374
- caseNode.flag1 = 1
375
- caseNode.content = {operator: "equal", left:selectNode.content, right:caseNode.content}
376
- if (!caseNode.two) {
377
- var errorId = caseNode.id + "-unexpected"
378
- var errorAction = insertIcon(nodes, "error", errorId, selectNode.content)
379
- errorAction.message = translate("Unexpected case value")
380
-
381
- caseNode.two = errorId
382
- errorAction.prev.push(caseNode.id)
383
- errorAction.one = caseNode.one
384
-
385
- var next = nodes[caseNode.one]
386
- next.prev.push(errorId)
387
- }
388
- } else {
389
- if (caseNode.two) {
390
- throw createError(translate("Only the rightmost Case icon can be empty"), filename, caseNode.id)
391
- }
392
- removeNodeOne(nodes, caseNode.id)
393
- }
394
- }
395
- removeNodeOne(nodes, selectNode.id)
527
+ var caseNodeId = selectNode.one;
528
+ var caseNode0 = nodes[caseNodeId];
529
+ while (caseNodeId) {
530
+ var caseNode = nodes[caseNodeId];
531
+ caseNodeId = caseNode.two;
532
+ if (caseNode.content) {
533
+ caseNode.type = "question";
534
+ caseNode.flag1 = 1;
535
+ caseNode.content = {
536
+ operator: "equal",
537
+ left: selectNode.content,
538
+ right: caseNode.content,
539
+ };
540
+ if (!caseNode.two) {
541
+ var errorId = caseNode.id + "-unexpected";
542
+ var errorAction = insertIcon(
543
+ nodes,
544
+ "error",
545
+ errorId,
546
+ selectNode.content,
547
+ );
548
+ errorAction.message = translate("Unexpected case value");
549
+
550
+ caseNode.two = errorId;
551
+ errorAction.prev.push(caseNode.id);
552
+ errorAction.one = caseNode.one;
553
+
554
+ var next = nodes[caseNode.one];
555
+ next.prev.push(errorId);
556
+ }
557
+ } else {
558
+ if (caseNode.two) {
559
+ throw createError(
560
+ translate("Only the rightmost Case icon can be empty"),
561
+ filename,
562
+ caseNode.id,
563
+ );
564
+ }
565
+ removeNodeOne(nodes, caseNode.id);
566
+ }
567
+ }
568
+ caseNode0.side = selectNode.side;
569
+ removeNodeOne(nodes, selectNode.id);
396
570
  }
397
571
 
398
572
  function insertIcon(nodes, type, id, content) {
399
- var node = {
400
- type: type,
401
- id: id,
402
- content: content,
403
- prev: []
404
- }
405
- nodes[id] = node
406
- return node
573
+ var node = {
574
+ type: type,
575
+ id: id,
576
+ content: content,
577
+ prev: [],
578
+ };
579
+ nodes[id] = node;
580
+ return node;
407
581
  }
408
582
 
409
583
  function removeNodeOne(nodes, nodeId) {
410
- var node = nodes[nodeId]
411
- redirectPrev(nodes, node, node.one)
412
- redirectNext(nodes, node, node.one)
413
- delete nodes[nodeId]
584
+ var node = nodes[nodeId];
585
+ redirectPrev(nodes, node, node.one);
586
+ redirectNext(nodes, node, node.one);
587
+ delete nodes[nodeId];
414
588
  }
415
589
 
416
590
  function removeFromNext(node, next) {
417
- next.prev = next.prev.filter(prevId => prevId !== node.id)
591
+ next.prev = next.prev.filter((prevId) => prevId !== node.id);
418
592
  }
419
593
 
420
594
  function redirectPrev(nodes, node, newTarget) {
421
- for (var prevId of node.prev) {
422
- var prev = nodes[prevId]
423
- if (prev.one === node.id) {
424
- prev.one = newTarget
425
- }
426
- if (prev.two === node.id) {
427
- prev.two = newTarget
428
- }
595
+ for (var prevId of node.prev) {
596
+ var prev = nodes[prevId];
597
+ if (prev.one === node.id) {
598
+ prev.one = newTarget;
599
+ }
600
+ if (prev.two === node.id) {
601
+ prev.two = newTarget;
429
602
  }
603
+ }
430
604
  }
431
605
 
432
606
  function redirectNext(nodes, node, newTarget) {
433
- var target = nodes[newTarget]
434
- removeFromNext(node, target)
435
- for (var prevId of node.prev) {
436
- target.prev.push(prevId)
437
- }
607
+ var target = nodes[newTarget];
608
+ removeFromNext(node, target);
609
+ for (var prevId of node.prev) {
610
+ target.prev.push(prevId);
611
+ }
438
612
  }
439
613
 
440
614
  function rewireShortcircuit(nodes) {
441
- while (findShortcusts(nodes)) {
442
-
443
- }
615
+ while (findShortcusts(nodes)) {}
444
616
  }
445
617
 
446
618
  function findShortcusts(nodes) {
447
- for (var id in nodes) {
448
- var node = nodes[id]
449
- if (node.type === "question") {
450
- var andOperand = findAndOperand(nodes, node)
451
- if (andOperand) {
452
- writeAndShortcut(nodes, node, andOperand)
453
- return true
454
- }
455
- var orOperand = findOrOperand(nodes, node)
456
- if (orOperand) {
457
- writeOrShortcut(nodes, node, orOperand)
458
- return true
459
- }
460
- }
461
- }
462
- return false
619
+ for (var id in nodes) {
620
+ var node = nodes[id];
621
+ if (node.type === "question") {
622
+ var andOperand = findAndOperand(nodes, node);
623
+ if (andOperand) {
624
+ writeAndShortcut(nodes, node, andOperand);
625
+ return true;
626
+ }
627
+ var orOperand = findOrOperand(nodes, node);
628
+ if (orOperand) {
629
+ writeOrShortcut(nodes, node, orOperand);
630
+ return true;
631
+ }
632
+ }
633
+ }
634
+ return false;
463
635
  }
464
636
 
465
637
  function findAndOperand(nodes, node) {
466
- var below = nodes[node.one]
467
- if (below.type === "question") {
468
- if (below.prev.length === 1 && below.two === node.two) {
469
- return below
470
- }
638
+ var below = nodes[node.one];
639
+ if (below.type === "question") {
640
+ if (below.prev.length === 1 && below.two === node.two) {
641
+ return below;
471
642
  }
472
- return undefined
643
+ }
644
+ return undefined;
473
645
  }
474
646
 
475
647
  function findOrOperand(nodes, node) {
476
- var right = nodes[node.two]
477
- if (right.type === "question") {
478
- if (right.prev.length === 1 && right.one === node.one) {
479
- return right
480
- }
648
+ var right = nodes[node.two];
649
+ if (right.type === "question") {
650
+ if (right.prev.length === 1 && right.one === node.one) {
651
+ return right;
481
652
  }
482
- return undefined
653
+ }
654
+ return undefined;
483
655
  }
484
656
 
485
657
  function writeAndShortcut(nodes, node, andOperand) {
486
- var right = nodes[node.two]
487
- var down = nodes[andOperand.one]
488
- removeFromNext(andOperand, right)
489
- removeFromNext(andOperand, down)
490
- node.content = {
491
- operator: "and",
492
- left: normalizeContent(node),
493
- right: normalizeContent(andOperand)
494
- }
495
- node.one = down.id
496
- node.flag1 = 1
497
- down.prev.push(node.id)
498
- delete nodes[andOperand.id]
658
+ var right = nodes[node.two];
659
+ var down = nodes[andOperand.one];
660
+ removeFromNext(andOperand, right);
661
+ removeFromNext(andOperand, down);
662
+ node.content = {
663
+ operator: "and",
664
+ left: normalizeContent(node),
665
+ right: normalizeContent(andOperand),
666
+ };
667
+ node.one = down.id;
668
+ node.flag1 = 1;
669
+ normalizeAnd(node);
670
+ down.prev.push(node.id);
671
+ delete nodes[andOperand.id];
499
672
  }
500
673
 
501
674
  function writeOrShortcut(nodes, node, orOperand) {
502
- var right = nodes[orOperand.two]
503
- var down = nodes[orOperand.one]
504
- removeFromNext(orOperand, right)
505
- removeFromNext(orOperand, down)
675
+ var right = nodes[orOperand.two];
676
+ var down = nodes[orOperand.one];
677
+ removeFromNext(orOperand, right);
678
+ removeFromNext(orOperand, down);
679
+ node.content = {
680
+ operator: "or",
681
+ left: normalizeContent(node),
682
+ right: normalizeContent(orOperand),
683
+ };
684
+ node.two = right.id;
685
+ node.flag1 = 1;
686
+ normalizeOr(node);
687
+ right.prev.push(node.id);
688
+ delete nodes[orOperand.id];
689
+ }
690
+
691
+ function normalizeAnd(node) {
692
+ var op = node.content;
693
+ var left = op.left;
694
+ var right = op.right;
695
+ if (left.operator === "not" && right.operator === "not") {
506
696
  node.content = {
507
- operator: "or",
508
- left: normalizeContent(node),
509
- right: normalizeContent(orOperand)
510
- }
511
- node.two = right.id
512
- node.flag1 = 1
513
- right.prev.push(node.id)
514
- delete nodes[orOperand.id]
697
+ operator: "or",
698
+ left: left.operand,
699
+ right: right.operand,
700
+ };
701
+ node.flag1 = 0;
702
+ }
703
+ }
704
+
705
+ function normalizeOr(node) {
706
+ var op = node.content;
707
+ var left = op.left;
708
+ var right = op.right;
709
+ if (left.operator === "not" && right.operator === "not") {
710
+ node.content = {
711
+ operator: "and",
712
+ left: left.operand,
713
+ right: right.operand,
714
+ };
715
+ node.flag1 = 0;
716
+ }
515
717
  }
516
718
 
517
719
  function normalizeContent(question) {
518
- if (question.flag1 === 1) {
519
- return question.content
720
+ if (question.flag1 === 1) {
721
+ return question.content;
722
+ }
723
+
724
+ return {
725
+ operator: "not",
726
+ operand: question.content,
727
+ };
728
+ }
729
+
730
+ function traverse(nodes, nodeId, visited, action) {
731
+ if (!nodeId) {
732
+ return;
733
+ }
734
+
735
+ if (nodeId in visited) {
736
+ return;
737
+ }
738
+ visited[nodeId] = true;
739
+ var node = nodes[nodeId];
740
+ action(nodes, node);
741
+ traverse(nodes, node.one, visited, action);
742
+ traverse(nodes, node.two, visited, action);
743
+ if (node.procs) {
744
+ for (var proc of node.procs) {
745
+ traverse(nodes, proc.start, visited, action);
746
+ }
747
+ }
748
+ }
749
+
750
+ function connectBack(nodes, node, htmlToString) {
751
+ if (node.one) {
752
+ var one = nodes[node.one];
753
+ one.prev.push(node.id);
754
+ }
755
+ if (node.two) {
756
+ var two = nodes[node.two];
757
+ two.prev.push(node.id);
758
+ }
759
+
760
+ if (node.side) {
761
+ var side = nodes[node.side].content;
762
+ if (side) {
763
+ node.side = decodeSide(side, htmlToString);
764
+ } else {
765
+ delete node.side;
520
766
  }
767
+ }
768
+ }
521
769
 
522
- return {
523
- operator: "not",
524
- operand: question.content
770
+ function decodeSide(content, htmlToString) {
771
+ var text = htmlToString(content);
772
+ var oneLine = text.join(" ");
773
+ if (oneLine.indexOf("=") === -1) {
774
+ return translate("Do for") + " " + oneLine;
775
+ } else {
776
+ return translate("Start at") + " " + oneLine;
777
+ }
778
+ }
779
+
780
+ function markLoopBody(nodes, start, filename) {
781
+ var nextNodeId = start.one;
782
+ while (nextNodeId) {
783
+ var current = nodes[nextNodeId];
784
+ nextNodeId = current.one;
785
+ current.parentLoopId = start.id;
786
+ if (current.type === "loopbegin") {
787
+ nextNodeId = markLoopBody(nodes, current, filename);
788
+ } else if (current.type === "loopend") {
789
+ start.end = current.id;
790
+ start.next = current.one;
791
+ current.start = start.id;
792
+ return nextNodeId;
793
+ }
794
+ }
795
+ throw createError(translate("Loop end expected here"), filename, start.one);
796
+ }
797
+
798
+ module.exports = { drakonToStruct, drakonToGraph };
799
+
800
+ },{"./structFlow":8,"./tools":10}],4:[function(require,module,exports){
801
+ const { drakonToPseudocode, mindToTree } = require("./drakonToPromptStruct");
802
+ const { htmlToString } = require("./browserTools");
803
+ const { setUpLanguage, translate } = require("./translate");
804
+ const { drakonToStruct } = require("./drakonToStruct");
805
+ const { freeDiagramToText } = require("./free");
806
+
807
+ window.drakongen = {
808
+ toPseudocode: function (drakonJson, name, filename, language) {
809
+ setUpLanguage(language);
810
+ return drakonToPseudocode(
811
+ drakonJson,
812
+ name,
813
+ filename,
814
+ htmlToString,
815
+ translate,
816
+ ).text;
817
+ },
818
+
819
+ toMindTree: function (mindJson, name, filename, language) {
820
+ setUpLanguage(language);
821
+ var result = mindToTree(mindJson, name, filename, htmlToString);
822
+ return result.text;
823
+ },
824
+
825
+ freeToText: function (freeJson, name, filename, language) {
826
+ setUpLanguage(language);
827
+ var result = freeDiagramToText(
828
+ freeJson,
829
+ name,
830
+ filename,
831
+ translate,
832
+ htmlToString,
833
+ );
834
+ return result.text;
835
+ },
836
+
837
+ toTree: function (drakonJson, name, filename, language, options) {
838
+ setUpLanguage(language);
839
+ var result = drakonToStruct(
840
+ drakonJson,
841
+ name,
842
+ filename,
843
+ translate,
844
+ htmlToString,
845
+ options,
846
+ );
847
+ return JSON.stringify(result, null, 4);
848
+ },
849
+ };
850
+
851
+ },{"./browserTools":1,"./drakonToPromptStruct":2,"./drakonToStruct":3,"./free":5,"./translate":11}],5:[function(require,module,exports){
852
+ var {addRange} = require("./tools")
853
+ const { createError } = require("./tools");
854
+
855
+ var translate
856
+
857
+ function compareVertically(box1, box2) {
858
+ if (box1.top + box1.height <= box2.top) return -1;
859
+ if (box2.top + box2.height <= box1.top) return 1;
860
+ return 0;
861
+ }
862
+
863
+ function compareHorizontally(box1, box2) {
864
+ if (box1.left + box1.width <= box2.left) return -1;
865
+ if (box2.left + box2.width <= box1.left) return 1;
866
+ return 0;
867
+ }
868
+
869
+ function byTopLeft(box1, box2) {
870
+ var vertical = compareVertically(box1, box2)
871
+ if (vertical == 0) {
872
+ return compareHorizontally(box1, box2)
525
873
  }
874
+ return vertical
526
875
  }
527
876
 
877
+ function parseDiagram(freeJson, filename) {
878
+ let diagram;
879
+ try {
880
+ freeJson = freeJson || ""
881
+ freeJson = freeJson.trim()
882
+ freeJson = freeJson || "{}"
883
+ diagram = JSON.parse(freeJson);
884
+ } catch (error) {
885
+ var message = translate("Error parsing JSON") + ": " + error.message
886
+ throw createError(message, filename)
887
+ }
888
+ return diagram
889
+ }
528
890
 
529
- function traverse(nodes, nodeId, visited, action) {
530
- if (!nodeId) {
531
- return
891
+ function sortedItems(diagram) {
892
+ var items = diagram.items || {};
893
+ var result = [];
894
+ for (var id in items) {
895
+ var item = items[id];
896
+ if (item.content && item.top && item.left && item.width && item.height) {
897
+ result.push(item);
898
+ }
532
899
  }
900
+ result.sort(byTopLeft);
901
+ return result;
902
+ }
533
903
 
534
- if (nodeId in visited) {
535
- return
904
+ function freeDiagramToText(freeJson, name, filename, translateFunction, htmlToString) {
905
+ translate = translateFunction
906
+ var diagram = parseDiagram(freeJson, filename)
907
+ var sorted = sortedItems(diagram)
908
+ var lines = []
909
+ lines.push("## " + name)
910
+ lines.push("")
911
+ for (var item of sorted) {
912
+ var content = htmlToString(item.content)
913
+ addRange(lines, content)
914
+ lines.push("")
536
915
  }
537
- visited[nodeId] = true
538
- var node = nodes[nodeId]
539
- action(nodes, node)
540
- traverse(nodes, node.one, visited, action)
541
- traverse(nodes, node.two, visited, action)
916
+ var text = lines.join("\n")
917
+ return {text:text}
542
918
  }
543
919
 
544
- function connectBack(nodes, node) {
545
- if (node.one) {
546
- var one = nodes[node.one]
547
- one.prev.push(node.id)
920
+ module.exports = {freeDiagramToText}
921
+ },{"./tools":10}],6:[function(require,module,exports){
922
+ "use strict";
923
+
924
+ function decrement_if_count(context, node) {
925
+ var i;
926
+ var if_id;
927
+ var if_node;
928
+
929
+ for (i = 0; i < node.stack.length; i++) {
930
+ if_id = node.stack[i];
931
+ if_node = context.nodes[if_id];
932
+ if_node.branching--;
548
933
  }
549
- if (node.two) {
550
- var two = nodes[node.two]
551
- two.prev.push(node.id)
552
- }
553
934
  }
554
935
 
555
- function markLoopBody(nodes, start, filename) {
556
- var nextNodeId = start.one
557
- while (nextNodeId) {
558
- var current = nodes[nextNodeId]
559
- nextNodeId = current.one
560
- current.parentLoopId = start.id
561
- if (current.type === "loopbegin") {
562
- nextNodeId = markLoopBody(nodes, current, filename)
563
- } else if (current.type === "loopend") {
564
- start.end = current.id
565
- start.next = current.one
566
- current.start = start.id
567
- return nextNodeId
936
+ function flow_no_loop(nodes, start_node_id) {
937
+ var context;
938
+
939
+ context = {
940
+ nodes: nodes
941
+ };
942
+
943
+ traverse_node(
944
+ context,
945
+ start_node_id,
946
+ []
947
+ );
948
+ }
949
+
950
+ function group_stack_by_id(stack) {
951
+ var counts_by_id;
952
+ var i;
953
+ var element;
954
+ var existing;
955
+
956
+ counts_by_id = {};
957
+
958
+ for (i = 0; i < stack.length; i++) {
959
+ element = stack[i];
960
+
961
+ if (element in counts_by_id) {
962
+ existing = counts_by_id[element];
963
+ } else {
964
+ existing = 0;
568
965
  }
966
+
967
+ counts_by_id[element] = existing + 1;
569
968
  }
570
- throw createError(translate("Loop end expected here"), filename, start.one)
969
+
970
+ return counts_by_id;
571
971
  }
572
972
 
573
- module.exports = { drakonToStruct, drakonToGraph };
574
- },{"./structFlow":6,"./tools":8}],4:[function(require,module,exports){
575
- const { drakonToPseudocode, mindToTree } = require('./drakonToPromptStruct');
576
- const { htmlToString } = require("./browserTools")
577
- const { setUpLanguage, translate } = require("./translate")
578
- const { drakonToStruct } = require("./drakonToStruct");
973
+ function increment_if_count(context, node) {
974
+ var i;
975
+ var if_id;
976
+ var if_node;
977
+
978
+ for (i = 0; i < node.stack.length; i++) {
979
+ if_id = node.stack[i];
980
+ if_node = context.nodes[if_id];
981
+ if_node.branching++;
982
+ }
983
+ }
579
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;
580
994
 
581
- window.drakongen = {
582
- toPseudocode: function (drakonJson, name, filename, language) {
583
- setUpLanguage(language)
584
- return drakonToPseudocode(drakonJson, name, filename, htmlToString, translate).text
585
- },
995
+ common = node.stack.concat(stack);
996
+ counts_by_id = group_stack_by_id(common);
997
+ processed_stack = [];
998
+
999
+ ids = Object.keys(counts_by_id);
1000
+
1001
+ for (i = 0; i < ids.length; i++) {
1002
+ id = ids[i];
1003
+ count = counts_by_id[id];
1004
+
1005
+ if_node = context.nodes[id];
1006
+
1007
+ if (!if_node.next) {
1008
+ if (if_node.branching === 1) {
1009
+ if_node.next = node_id;
1010
+ } else {
1011
+ if_node.branching -= count - 1;
1012
+
1013
+ if (if_node.branching === 1) {
1014
+ if_node.next = node_id;
1015
+ } else {
1016
+ processed_stack.push(id);
1017
+ }
1018
+ }
1019
+ }
1020
+ }
586
1021
 
587
- toMindTree: function (mindJson, name, filename, language) {
588
- setUpLanguage(language)
589
- var result = mindToTree(mindJson, name, filename, htmlToString)
590
- return result.text
591
- },
1022
+ node.stack = processed_stack;
1023
+ }
592
1024
 
593
- toTree: function (drakonJson, name, filename, language) {
594
- setUpLanguage(language)
595
- var result = drakonToStruct(drakonJson, name, filename, translate)
596
- return JSON.stringify(result, null, 4)
1025
+ function recurse_traversal(context, node_id, node) {
1026
+ var stack1;
1027
+ var stack2;
1028
+
1029
+ if (node.type === "question") {
1030
+ increment_if_count(
1031
+ context,
1032
+ node
1033
+ );
1034
+
1035
+ stack1 = node.stack.slice();
1036
+ stack1.push(node_id);
1037
+
1038
+ stack2 = node.stack.slice();
1039
+ stack2.push(node_id);
1040
+
1041
+ traverse_node(
1042
+ context,
1043
+ node.one,
1044
+ stack1
1045
+ );
1046
+
1047
+ traverse_node(
1048
+ context,
1049
+ node.two,
1050
+ stack2
1051
+ );
1052
+ } else {
1053
+ if (node.final) {
1054
+ decrement_if_count(
1055
+ context,
1056
+ node
1057
+ );
1058
+ } else {
1059
+ stack1 = node.stack.slice();
1060
+
1061
+ traverse_node(
1062
+ context,
1063
+ node.one,
1064
+ stack1
1065
+ );
1066
+ }
597
1067
  }
598
1068
  }
599
- },{"./browserTools":1,"./drakonToPromptStruct":2,"./drakonToStruct":3,"./translate":9}],5:[function(require,module,exports){
1069
+
1070
+ function traverse_node(context, node_id, stack) {
1071
+ var node;
1072
+
1073
+ if (node_id) {
1074
+ node = context.nodes[node_id];
1075
+
1076
+ if (!node.stack) {
1077
+ node.stack = [];
1078
+ node.refs = node.prev.length;
1079
+ }
1080
+
1081
+ node.refs--;
1082
+
1083
+ merge_incoming_branches(
1084
+ context,
1085
+ node_id,
1086
+ node,
1087
+ stack
1088
+ );
1089
+
1090
+ if (!(node.refs > 0)) {
1091
+ recurse_traversal(
1092
+ context,
1093
+ node_id,
1094
+ node
1095
+ );
1096
+ }
1097
+ }
1098
+ }
1099
+
1100
+ module.exports = {
1101
+ flow_no_loop: flow_no_loop
1102
+ };
1103
+ },{}],7:[function(require,module,exports){
600
1104
  var {addRange} = require("./tools")
601
1105
 
602
1106
  function makeIndent(depth) {
@@ -609,8 +1113,11 @@ function printWithIndent(lines, indent, output) {
609
1113
  }
610
1114
 
611
1115
  function printPseudo(algorithm, translate, output, htmlToString) {
612
- function printStructuredContent(content, indent, output) {
1116
+ function printStructuredContent(content, indent, output, side) {
613
1117
  var lines = printStructuredContentNoIdent(content)
1118
+ if (side) {
1119
+ lines[0] = side + ": " + lines[0]
1120
+ }
614
1121
  printWithIndent(lines, indent, output)
615
1122
  }
616
1123
 
@@ -674,19 +1181,39 @@ function printPseudo(algorithm, translate, output, htmlToString) {
674
1181
  printError(step, indent, output)
675
1182
  } else if (step.type === "break") {
676
1183
  output.push(indent + translate("break"))
1184
+ } else if (step.type === "parbegin") {
1185
+ printParbegin(step, depth, output)
677
1186
  } else {
678
1187
  printOther(step, indent, output)
679
1188
  }
680
1189
  }
681
1190
  }
1191
+
1192
+ function printParbegin(step, depth, output) {
1193
+ const indent2 = makeIndent(depth + 1)
1194
+ const indent = makeIndent(depth)
1195
+ printWithIndent([translate("Group of parallel processes")], indent, output)
1196
+ for (var proc of step.procs) {
1197
+ printWithIndent([translate("Parallel process") + " " + (proc.ordinal + 1)], indent2, output)
1198
+ printSteps(proc.body, depth + 2, output)
1199
+ }
1200
+ }
682
1201
 
683
1202
  function printOther(step, indent, output) {
1203
+ var side = step.side
684
1204
  if (!step.content && !step.secondary) {return}
685
1205
  if (step.secondary) {
686
- printStructuredContent(step.secondary, indent, output)
1206
+ printStructuredContent(step.secondary, indent, output, side)
1207
+ side = undefined
687
1208
  }
688
1209
  if (step.content) {
689
- printStructuredContent(step.content, indent, output)
1210
+ var content = step.content
1211
+ if (step.type === "pause") {
1212
+ content = translate("Pause") + " " + htmlToString(content).join(" ")
1213
+ } else if (step.type === "timer") {
1214
+ content = translate("Start timer") + " " + htmlToString(content).join(" ")
1215
+ }
1216
+ printStructuredContent(content, indent, output, side)
690
1217
  }
691
1218
  }
692
1219
 
@@ -727,6 +1254,9 @@ function printPseudo(algorithm, translate, output, htmlToString) {
727
1254
  var content = step.content
728
1255
  var lines = printStructuredContentNoIdent(content)
729
1256
  lines[0] = translate("if") + " " + lines[0]
1257
+ if (step.side) {
1258
+ lines[0] = step.side + ": " + lines[0]
1259
+ }
730
1260
  printWithIndent(lines, indent, output)
731
1261
  addRange(output, yesBody)
732
1262
  if (!empty(noBody)) {
@@ -755,392 +1285,440 @@ function printPseudo(algorithm, translate, output, htmlToString) {
755
1285
  }
756
1286
 
757
1287
  module.exports = {printPseudo, printWithIndent, makeIndent}
758
- },{"./tools":8}],6:[function(require,module,exports){
759
- var {buildTree} = require("./technicalTree")
1288
+ },{"./tools":10}],8:[function(require,module,exports){
1289
+ var { buildTree } = require("./technicalTree");
760
1290
  const { createError, sortByProperty } = require("./tools");
761
- const { optimizeTree } = require("./treeTools")
1291
+ const { optimizeTree } = require("./treeTools");
1292
+ const { flow_no_loop } = require("./noloop")
762
1293
 
763
1294
  function redirectNode(nodes, node, from, to) {
764
- if (node.one === from) {
765
- node.one = to;
766
- }
767
- if (node.two === from) {
768
- node.two = to;
769
- }
770
- if (node.next === from) {
771
- node.next = to
772
- }
773
- if (node.start && node.type === "loopend") {
774
- start = nodes[node.start]
775
- if (start.next === from) {
776
- start.next = to
777
- }
778
- }
1295
+ if (node.one === from) {
1296
+ node.one = to;
1297
+ }
1298
+ if (node.two === from) {
1299
+ node.two = to;
1300
+ }
1301
+ if (node.next === from) {
1302
+ node.next = to;
1303
+ }
1304
+ if (node.start && node.type === "loopend") {
1305
+ start = nodes[node.start];
1306
+ if (start.next === from) {
1307
+ start.next = to;
1308
+ }
1309
+ }
779
1310
  }
780
1311
 
781
- function structFlow(nodes, branches, filename, translate) {
782
-
783
-
784
-
785
- function flowGraph(nodes, nodeId, branchingStack) {
786
- if (!nodeId) {return;}
787
-
788
- const node = nodes[nodeId];
789
-
790
- if (!node.stack) {
791
- node.stack = [];
792
- node.remaining = node.prev.length;
1312
+ 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--;
793
1373
  }
794
- node.remaining--;
795
-
796
- mergeBranchingStack(nodes, node, branchingStack);
797
- if (node.remaining > 0) {return;}
798
-
799
- if (node.type === "question") {
800
- for (let i = 0; i < node.stack.length; i++) {
801
- const questionId = node.stack[i];
802
- const question = nodes[questionId];
803
- question.branching++;
804
- }
805
-
806
- const stackOne = node.stack.slice();
807
- const stackTwo = node.stack.slice();
808
- stackOne.push(nodeId);
809
- stackTwo.push(nodeId);
810
-
811
- flowGraph(nodes, node.two, stackTwo);
812
- flowGraph(nodes, node.one, stackOne);
813
- } else if (node.type === "arrow-loop") {
814
- const stackOne = node.stack.slice();
815
- stackOne.push(nodeId);
816
- flowGraph(nodes, node.one, stackOne);
817
- } else if (node.type === "arrow-stub") {
818
- decrementBranchingForArrow(nodes, node)
819
- } else {
820
- flowGraph(nodes, node.one, node.stack);
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
+ }
821
1434
  }
1435
+ }
822
1436
  }
823
1437
 
824
- function decrementBranchingForArrow(nodes, node) {
825
- var algonode = nodes[node.arrow]
826
- algonode.branching--
827
- }
828
-
829
- function decrementQuestions(nodes, algonode, dictionary) {
830
- var stub = nodes[algonode.stub]
831
- for (var id of stub.stack) {
832
- var snode = nodes[id]
833
- if (id != algonode) {
834
- if (id in dictionary) {
835
- snode.branching--
836
- }
837
- }
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;
838
1444
  }
839
- return stub
840
- }
841
-
842
-
843
-
844
- function mergeBranchingStack(nodes, node, branchingStack) {
845
- // Append all elements of the branching stack to node.stack
846
- addRange(node.stack, branchingStack)
847
-
848
- // Build a dictionary of occurrences
849
- const dictionary = buildDictionaryOfOccurences(node);
850
-
851
- // Merge all nodes
852
- mergeAll(nodes, node, dictionary);
853
-
854
- // Rebuild the stack
855
- node.stack = buildStackFromDictionary(dictionary);
1445
+ }
1446
+ }
1447
+ }
1448
+ function isInMap(map, key) {
1449
+ if (!map) {
1450
+ return false;
1451
+ }
1452
+ return key in map;
1453
+ }
1454
+
1455
+ function prepareQuestions(nodes) {
1456
+ for (const nodeId in nodes) {
1457
+ const node = nodes[nodeId];
1458
+ if (node.type === "question") {
1459
+ node.branching = 2;
1460
+ } else if (node.type === "arrow-loop") {
1461
+ node.branching = 1;
1462
+ }
1463
+ }
1464
+ }
1465
+
1466
+ function rewireArrows(nodes, branches) {
1467
+ branches.forEach((branch) =>
1468
+ rewireArrowsInBranch(nodes, branch.id, branch.next, []),
1469
+ );
1470
+ for (var id in nodes) {
1471
+ var node = nodes[id];
1472
+ if (node.type === "arrow-loop") {
1473
+ var stub = insertArrowStub(nodes, node);
1474
+ var visited = {};
1475
+ fillAStack(nodes, stub, stub.arrow, visited);
1476
+ }
856
1477
  }
1478
+ }
857
1479
 
858
- function addRange(dst, src) {
859
- for (let i = 0; i < src.length; i++) {
860
- dst.push(src[i]);
861
- }
1480
+ function fillAStack(nodes, node, arrowId, visited) {
1481
+ if (node.id in visited) {
1482
+ return;
862
1483
  }
863
-
864
- function buildStackFromDictionary(dictionary) {
865
- const rebuiltStack = [];
866
- for (const id in dictionary) {
867
- if (dictionary[id] > 0) {
868
- rebuiltStack.push(id);
869
- }
870
- }
871
- return rebuiltStack;
1484
+ visited[node.id] = true;
1485
+ if (!node.astack) {
1486
+ node.astack = {};
872
1487
  }
873
-
874
- function buildDictionaryOfOccurences(node) {
875
- const dictionary = {};
876
- for (let i = 0; i < node.stack.length; i++) {
877
- const id = node.stack[i];
878
- dictionary[id] = (dictionary[id] || 0) + 1;
879
- }
880
- return dictionary;
1488
+ node.astack[arrowId] = true;
1489
+ if (node.id === arrowId) {
1490
+ return;
881
1491
  }
882
-
883
- function mergeAll(nodes, node, dictionary) {
884
- for (const id in dictionary) {
885
- const occurrences = dictionary[id];
886
- const algonode = nodes[id];
887
- if (occurrences > 1) {
888
- algonode.branching--;
889
- dictionary[id] = occurrences - 1;
890
- }
891
- if (algonode.branching === 1) {
892
- if (algonode.type === "arrow-loop" && !algonode.next) {
893
- if (!isInMap(node.astack, id)) {
894
- algonode.next = node.id
895
- dictionary[algonode.id] = 0;
896
- var stub = decrementQuestions(nodes, algonode, dictionary)
897
- stub.one = node.id
898
- }
899
- }
900
- }
901
- }
902
-
903
- for (const id in dictionary) {
904
- const algonode = nodes[id];
905
- if (algonode.branching === 1) {
906
- if (algonode.type === "question") {
907
- algonode.next = node.id;
908
- dictionary[algonode.id] = 0;
909
- }
910
- }
911
- }
912
- }
913
- function isInMap(map, key) {
914
- if (!map) { return false }
915
- return key in map
916
- }
917
-
918
- function prepareQuestions(nodes) {
919
- for (const nodeId in nodes) {
920
- const node = nodes[nodeId];
921
- if (node.type === "question") {
922
- node.branching = 2;
923
- } else if (node.type === "arrow-loop") {
924
- node.branching = 1;
925
- }
926
- }
1492
+ for (var prevId of node.prev) {
1493
+ var prev = nodes[prevId];
1494
+ fillAStack(nodes, prev, arrowId, visited);
927
1495
  }
1496
+ }
928
1497
 
929
- function rewireArrows(nodes, branches) {
930
- branches.forEach(branch => rewireArrowsInBranch(nodes, branch.id, branch.next, []))
931
- for (var id in nodes) {
932
- var node = nodes[id]
933
- if (node.type === "arrow-loop") {
934
- var stub = insertArrowStub(nodes, node)
935
- fillAStack(nodes, stub, stub.arrow)
936
- }
937
- }
938
- }
939
-
940
- function fillAStack(nodes, node, arrowId) {
941
- if (!node.astack) {
942
- node.astack = {}
943
- }
944
- node.astack[arrowId] = true
945
- if (node.id === arrowId) {
946
- return
947
- }
948
- for (var prevId of node.prev) {
949
- var prev = nodes[prevId]
950
- fillAStack(nodes, prev, arrowId)
951
- }
952
- }
953
-
954
- function rewireArrowsInBranch(nodes, prevNodeId, nodeId, arrowStack) {
955
- if (!nodeId) {return}
956
- var node = nodes[nodeId]
957
- if (node.type === "branch") {
958
- return
959
- }
960
- if (node.type === "arrow-loop") {
961
- if (!node.noloop) {
962
- node.noloop = {}
963
- }
964
- if (arrowStack.includes(nodeId)) {
965
- return
966
- }
967
- node.noloop[prevNodeId] = true
968
- arrowStack = arrowStack.slice()
969
- arrowStack.push(nodeId)
970
- rewireArrowsInBranch(nodes, nodeId, node.one, arrowStack)
971
- } else if (node.type === "question") {
972
- var left = arrowStack.slice()
973
- var right = arrowStack.slice()
974
- rewireArrowsInBranch(nodes, nodeId, node.one, left)
975
- rewireArrowsInBranch(nodes, nodeId, node.two, right)
976
- } else {
977
- rewireArrowsInBranch(nodes, nodeId, node.one, arrowStack)
1498
+ function rewireArrowsInBranch(nodes, prevNodeId, nodeId, arrowStack) {
1499
+ if (!nodeId) {
1500
+ return;
1501
+ }
1502
+ var node = nodes[nodeId];
1503
+ if (node.type === "branch") {
1504
+ return;
1505
+ }
1506
+ if (node.type === "arrow-loop") {
1507
+ if (!node.noloop) {
1508
+ node.noloop = {};
1509
+ }
1510
+ if (arrowStack.includes(nodeId)) {
1511
+ return;
1512
+ }
1513
+ node.noloop[prevNodeId] = true;
1514
+ arrowStack = arrowStack.slice();
1515
+ arrowStack.push(nodeId);
1516
+ rewireArrowsInBranch(nodes, nodeId, node.one, arrowStack);
1517
+ } else if (node.type === "question") {
1518
+ var left = arrowStack.slice();
1519
+ var right = arrowStack.slice();
1520
+ rewireArrowsInBranch(nodes, nodeId, node.one, left);
1521
+ rewireArrowsInBranch(nodes, nodeId, node.two, right);
1522
+ } else if (node.type === "parbegin") {
1523
+ for (var proc of node.procs) {
1524
+ rewireArrowsInBranch(nodes, undefined, proc.start, []);
1525
+ }
1526
+ rewireArrowsInBranch(nodes, nodeId, node.one, arrowStack);
1527
+ } else {
1528
+ rewireArrowsInBranch(nodes, nodeId, node.one, arrowStack);
1529
+ }
1530
+ }
1531
+
1532
+ function insertArrowStub(nodes, node) {
1533
+ var stub = {
1534
+ type: "arrow-stub",
1535
+ id: "arrow-stub-" + node.id,
1536
+ arrow: node.id,
1537
+ prev: [],
1538
+ };
1539
+ nodes[stub.id] = stub;
1540
+ node.stub = stub.id;
1541
+ var prev2 = [];
1542
+ for (var prevId of node.prev) {
1543
+ if (prevId in node.noloop) {
1544
+ prev2.push(prevId);
1545
+ } else {
1546
+ stub.prev.push(prevId);
1547
+ var prev = nodes[prevId];
1548
+ redirectNode(nodes, prev, node.id, stub.id);
1549
+ }
1550
+ }
1551
+ node.prev = prev2;
1552
+ return stub;
1553
+ }
1554
+
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
+ }
978
1581
  }
979
- }
980
-
981
- function insertArrowStub(nodes, node) {
982
- var stub = {
983
- type: "arrow-stub",
984
- id: "arrow-stub-" + node.id,
985
- arrow: node.id,
986
- prev: []
987
- }
988
- nodes[stub.id] = stub
989
- node.stub = stub.id
990
- var prev2 = []
991
- for (var prevId of node.prev) {
992
- if (prevId in node.noloop) {
993
- prev2.push(prevId)
994
- } else {
995
- stub.prev.push(prevId)
996
- var prev = nodes[prevId]
997
- redirectNode(nodes, prev, node.id, stub.id)
998
- }
999
- }
1000
- node.prev = prev2
1001
- return stub
1002
- }
1003
-
1004
- function rewriteTree(body, index, endId, output) {
1005
- while (index < body.length) {
1006
- var node = body[index]
1007
- index++
1008
- if (endId && node.id === endId) {
1009
- return index
1010
- }
1011
- if (node.type === "question") {
1012
- var transformed = rewriteQuestionTree(node, output)
1013
- if (endId) {
1014
- var breakYes = findLoopEnd(transformed.yes, endId)
1015
- var breakNo = findLoopEnd(transformed.no, endId)
1016
- if (breakYes || breakNo) {
1017
- var toBreak = []
1018
- findPlacesToBreak(transformed.yes, endId, toBreak)
1019
- findPlacesToBreak(transformed.no, endId, toBreak)
1020
- addBreaks(toBreak)
1021
- return index
1022
- }
1023
- }
1024
- } else if (node.type === "loopbegin") {
1025
- var body2 = []
1026
- index = rewriteTree(body, index, node.end, body2)
1027
- output.push({
1028
- id: node.id,
1029
- type: "loop",
1030
- content: node.content,
1031
- body: body2
1032
- })
1033
- } else {
1034
- output.push(node)
1035
- }
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);
1036
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);
1037
1632
  }
1633
+ }
1038
1634
 
1039
- function findPlacesToBreak(body, endId, output) {
1040
- if (body.length === 0) {
1041
- output.push(body)
1042
- return
1043
- }
1044
- var last = body[body.length - 1]
1045
- if (last.id === endId) {
1046
- return
1047
- }
1048
- if (last.type === "question") {
1049
- var qends = []
1050
- findPlacesToBreak(last.yes, endId, qends)
1051
- findPlacesToBreak(last.no, endId, qends)
1052
- if (qends.length === 2
1053
- && qends[0] === last.yes
1054
- && qends[1] === last.no) {
1055
- output.push(body)
1056
- } else {
1057
- addRange(output, qends)
1058
- }
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;
1059
1641
  } else {
1060
- output.push(body)
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
+ );
1061
1649
  }
1062
- }
1063
-
1064
- function findLoopEnd(body, endId) {
1065
- for (var i = 0; i < body.length; i++) {
1066
- var node = body[i]
1067
- if (node.id === endId) {
1068
- if (i === body.length - 1) {
1069
- return true
1070
- } else {
1071
- throw createError(
1072
- translate("An exit from the loop must lead to the point right after the loop end"),
1073
- filename,
1074
- node.id
1075
- );
1076
- }
1077
- }
1078
- if (node.type === "question") {
1079
- if (findLoopEnd(node.yes, endId)) {
1080
- return true
1081
- }
1082
- if (findLoopEnd(node.no, endId)) {
1083
- return true
1084
- }
1085
- }
1650
+ }
1651
+ if (node.type === "question") {
1652
+ if (findLoopEnd(node.yes, endId)) {
1653
+ return true;
1086
1654
  }
1087
- return false
1088
- }
1089
-
1090
- function addBreaks(toBreak) {
1091
- for (var body of toBreak) {
1092
- body.push({
1093
- type: "break"
1094
- })
1095
- }
1096
- }
1097
-
1098
- function rewriteQuestionTree(question, output) {
1099
- var yes = []
1100
- var no = []
1101
- rewriteTree(question.yes, 0, undefined, yes)
1102
- rewriteTree(question.no, 0, undefined, no)
1103
- var transformed = {
1104
- type: "question",
1105
- id: question.id,
1106
- content: question.content,
1107
- yes: yes,
1108
- no: no
1655
+ if (findLoopEnd(node.no, endId)) {
1656
+ return true;
1109
1657
  }
1110
- output.push(transformed)
1111
- return transformed
1112
- }
1113
-
1114
-
1115
- function structMain() {
1116
- rewireArrows(nodes, branches)
1117
- prepareQuestions(nodes)
1118
- var result = []
1119
- for (var branch of branches) {
1120
- flowGraph(nodes, branch.next, [])
1121
- }
1122
-
1123
- for (var branch of branches) {
1124
- var body = []
1125
- buildTree(nodes, branch.next, body, "<dummy id>")
1126
- var body2 = []
1127
- rewriteTree(body, 0, undefined, body2)
1128
- result.push({
1129
- name: branch.content,
1130
- branchId: branch.branchId,
1131
- start: branch.next,
1132
- refs: branch.prev.length,
1133
- body: optimizeTree(body2)
1134
- })
1135
- }
1136
-
1137
- return sortByProperty(result, "branchId")
1138
- }
1139
-
1140
- return structMain()
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;
1685
+ }
1686
+
1687
+ function structMain() {
1688
+ rewireArrows(nodes, branches);
1689
+ prepareQuestions(nodes);
1690
+ var result = [];
1691
+
1692
+ for (var branch of branches) {
1693
+ if (options.noLoop) {
1694
+ flow_no_loop(nodes, branch.next, []);
1695
+ } else {
1696
+ flowGraph(nodes, branch.next, []);
1697
+ }
1698
+ }
1699
+
1700
+ for (var branch of branches) {
1701
+ var body = [];
1702
+ buildTree(nodes, branch.next, body, "<dummy id>");
1703
+ var body2 = [];
1704
+ rewriteTree(body, 0, undefined, body2);
1705
+ result.push({
1706
+ name: branch.content,
1707
+ branchId: branch.branchId,
1708
+ start: branch.next,
1709
+ refs: branch.prev.length,
1710
+ body: optimizeTree(body2),
1711
+ });
1712
+ }
1713
+
1714
+ return sortByProperty(result, "branchId");
1715
+ }
1716
+
1717
+ return structMain();
1141
1718
  }
1142
1719
  module.exports = { structFlow, redirectNode };
1143
- },{"./technicalTree":7,"./tools":8,"./treeTools":10}],7:[function(require,module,exports){
1720
+
1721
+ },{"./noloop":6,"./technicalTree":9,"./tools":10,"./treeTools":12}],9:[function(require,module,exports){
1144
1722
  function buildTree(nodes, nodeId, body, stopId) {
1145
1723
  while (nodeId) {
1146
1724
  if (nodeId === stopId) {return;}
@@ -1180,6 +1758,21 @@ function buildTree(nodes, nodeId, body, stopId) {
1180
1758
  start: node.arrow
1181
1759
  };
1182
1760
 
1761
+ next = node.one;
1762
+ } else if (node.type === "parbegin") {
1763
+ transformed = {
1764
+ id: node.id,
1765
+ type: node.type,
1766
+ procs: []
1767
+ }
1768
+ for (var proc of node.procs) {
1769
+ var childProc = {
1770
+ ordinal: proc.ordinal,
1771
+ body: []
1772
+ }
1773
+ transformed.procs.push(childProc)
1774
+ buildTree(nodes, proc.start, childProc.body, undefined)
1775
+ }
1183
1776
  next = node.one;
1184
1777
  } else {
1185
1778
  transformed = {
@@ -1198,8 +1791,13 @@ function buildTree(nodes, nodeId, body, stopId) {
1198
1791
  ]
1199
1792
  )
1200
1793
  next = node.one;
1794
+ if (node.final) {
1795
+ next = undefined
1796
+ }
1797
+ }
1798
+ if (node.side) {
1799
+ transformed.side = node.side
1201
1800
  }
1202
-
1203
1801
  body.push(transformed);
1204
1802
  nodeId = next;
1205
1803
  }
@@ -1215,6 +1813,9 @@ function copyFields(dst, src, fields) {
1215
1813
  }
1216
1814
 
1217
1815
  function reserveNext(nodes, node) {
1816
+ if (!node.next) {
1817
+ return undefined
1818
+ }
1218
1819
  const target = nodes[node.next];
1219
1820
  if (target.targetTaken) {
1220
1821
  return undefined;
@@ -1226,7 +1827,7 @@ function reserveNext(nodes, node) {
1226
1827
 
1227
1828
  module.exports = {buildTree}
1228
1829
 
1229
- },{}],8:[function(require,module,exports){
1830
+ },{}],10:[function(require,module,exports){
1230
1831
 
1231
1832
  function createError(message, filename, nodeId) {
1232
1833
  var error = new Error(message)
@@ -1267,7 +1868,7 @@ function addRange(to, from) {
1267
1868
  }
1268
1869
  }
1269
1870
  module.exports = { createError, sortByProperty, addRange, remove }
1270
- },{}],9:[function(require,module,exports){
1871
+ },{}],11:[function(require,module,exports){
1271
1872
  var translationsRu = {
1272
1873
  "error": "ОШИБКА",
1273
1874
  "not": "не",
@@ -1296,7 +1897,13 @@ var translationsRu = {
1296
1897
  "Description": "Описание",
1297
1898
  "Algorithm": "Алгоритм",
1298
1899
  Remarks: "Замечания",
1299
- Parameters: "Параметры"
1900
+ Parameters: "Параметры",
1901
+ "Group of parallel processes": "Группа параллельных процессов",
1902
+ "Parallel process": "Параллельный процесс",
1903
+ "Start at": "Начать в",
1904
+ "Do for": "Делать в течение",
1905
+ "Pause": "Пауза",
1906
+ "Start timer": "Запустить таймер"
1300
1907
  }
1301
1908
 
1302
1909
  var translationsEn = {
@@ -1327,7 +1934,13 @@ var translationsEn = {
1327
1934
  Description: 'Description',
1328
1935
  Algorithm: 'Algorithm',
1329
1936
  Remarks: "Remarks",
1330
- Parameters: "Parameters"
1937
+ Parameters: "Parameters",
1938
+ "Group of parallel processes": "Group of parallel processes",
1939
+ "Parallel process": "Parallel process",
1940
+ "Start at": "Start at",
1941
+ "Do for": "Do for",
1942
+ "Pause": "Pause",
1943
+ "Start timer": "Start timer",
1331
1944
  }
1332
1945
 
1333
1946
  var translationsNo = {
@@ -1358,7 +1971,13 @@ var translationsNo = {
1358
1971
  Description: 'Beskrivelse',
1359
1972
  Algorithm: 'Algoritme',
1360
1973
  Remarks: "Bemerkninger",
1361
- Parameters: "Parametere"
1974
+ Parameters: "Parametere",
1975
+ "Group of parallel processes": "Gruppe av parallelle prosesser",
1976
+ "Parallel process": "Parallell prosess",
1977
+ "Start at": "Start ved",
1978
+ "Do for": "Gjør i",
1979
+ "Pause": "Pause",
1980
+ "Start timer": "Start tidtaker"
1362
1981
  };
1363
1982
 
1364
1983
 
@@ -1382,7 +2001,7 @@ function setUpLanguage(language) {
1382
2001
 
1383
2002
 
1384
2003
  module.exports = { setUpLanguage, translate };
1385
- },{}],10:[function(require,module,exports){
2004
+ },{}],12:[function(require,module,exports){
1386
2005
 
1387
2006
  function optimizeTree(steps) {
1388
2007
  var result = []
@@ -1393,6 +2012,8 @@ function optimizeTree(steps) {
1393
2012
  var copy
1394
2013
  if (step.type === "question") {
1395
2014
  copy = optimizeQuestion(step)
2015
+ } else if (step.type === "parbegin") {
2016
+ copy = optimizeParbegin(step)
1396
2017
  } else if (step.type === "loop") {
1397
2018
  copy = optimizeLoop(step)
1398
2019
  } else {
@@ -1404,6 +2025,22 @@ function optimizeTree(steps) {
1404
2025
  return result
1405
2026
  }
1406
2027
 
2028
+ function optimizeParbegin(step) {
2029
+ var procs = []
2030
+ for (var proc of step.procs) {
2031
+ var procCopy = {
2032
+ ordinal: proc.ordinal,
2033
+ body: optimizeTree(proc.body)
2034
+ }
2035
+ procs.push(procCopy)
2036
+ }
2037
+ return {
2038
+ id: step.id,
2039
+ type: step.type,
2040
+ procs: procs
2041
+ }
2042
+ }
2043
+
1407
2044
  function optimizeLoop(step) {
1408
2045
  return {
1409
2046
  id: step.id,
@@ -1418,6 +2055,7 @@ function optimizeQuestion(step) {
1418
2055
  var no = optimizeTree(step.no)
1419
2056
  if (yes.length === 0 && no.length === 0) {
1420
2057
  return {
2058
+ side: step.side,
1421
2059
  type: step.type,
1422
2060
  content: step.content,
1423
2061
  yes: [],
@@ -1426,6 +2064,7 @@ function optimizeQuestion(step) {
1426
2064
  }
1427
2065
  if (yes.length === 0) {
1428
2066
  return {
2067
+ side: step.side,
1429
2068
  type: step.type,
1430
2069
  content: {operator:"not",operand:step.content},
1431
2070
  yes: no,
@@ -1433,6 +2072,7 @@ function optimizeQuestion(step) {
1433
2072
  }
1434
2073
  }
1435
2074
  return {
2075
+ side: step.side,
1436
2076
  type: step.type,
1437
2077
  content: step.content,
1438
2078
  yes: yes,