drakongen 1.4.6 → 1.4.7

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 (2) hide show
  1. package/browser/drakongen.js +399 -329
  2. package/package.json +1 -1
@@ -157,420 +157,490 @@ module.exports = { drakonToPseudocode, mindToTree };
157
157
  const { structFlow, redirectNode } = require("./structFlow");
158
158
  const { createError, remove } = require("./tools");
159
159
 
160
- var translate
160
+ var translate;
161
161
 
162
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)
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) {
196
181
  return {
197
- name: name,
198
- params: drakonGraph.params || "",
199
- description: drakonGraph.description || "",
200
- branches: branchTrees
201
- }
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) =>
193
+ checkBranchIsReferenced(branch, firstNodeId, filename),
194
+ );
195
+ rewireShortcircuit(nodes, filename);
196
+ 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
+ };
202
204
  }
203
205
 
204
206
  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)
231
-
232
- return {
233
- name: name,
234
- params: drakonGraph.params || "",
235
- description: drakonGraph.description || "",
236
- branches: branchTrees
237
- }
207
+ translate = translateFunction;
208
+ let drakonGraph;
209
+ try {
210
+ drakonGraph = JSON.parse(drakonJson);
211
+ } catch (error) {
212
+ var message = translate("Error parsing JSON") + ": " + error.message;
213
+ throw createError(message, filename);
214
+ }
215
+
216
+ const nodes = drakonGraph.items || {};
217
+
218
+ var branches = [];
219
+ var firstNodeId = findStartNode(nodes, filename, branches);
220
+
221
+ if (!firstNodeId) {
222
+ return undefined;
223
+ }
224
+
225
+ buildTwoWayConnections(nodes, firstNodeId);
226
+
227
+ rewireSelectsMarkLoops(nodes, filename);
228
+ rewireShortcircuit(nodes, filename);
229
+ branches.forEach((branch) =>
230
+ checkBranchIsReferenced(branch, firstNodeId, filename),
231
+ );
232
+ branches.forEach((branch) => cutOffBranch(nodes, branch));
233
+
234
+ var branchTrees = structFlow(nodes, branches, filename, translate);
235
+
236
+ return {
237
+ name: name,
238
+ params: drakonGraph.params || "",
239
+ description: drakonGraph.description || "",
240
+ branches: branchTrees,
241
+ };
238
242
  }
239
243
 
240
-
241
244
  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
- }
245
+ if (branch.id === firstNodeId) {
246
+ return;
247
+ }
248
+ if (branch.prev.length === 0) {
249
+ throw createError(
250
+ translate("A silhouette branch is not referenced"),
251
+ filename,
252
+ branch.id,
253
+ );
254
+ }
248
255
  }
249
256
 
250
257
  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))
258
+ var end = {
259
+ type: "end",
260
+ id: branch.id + "-end",
261
+ prev: [],
262
+ };
263
+ nodes[end.id] = end;
264
+ branch.next = branch.one;
265
+ var addresses = [];
266
+ traverseToHitBranch(nodes, branch.id, {}, (prev, node) =>
267
+ addFakeEnd(nodes, prev, node, end, addresses),
268
+ );
260
269
  }
261
270
 
262
271
  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
- }
272
+ if (!nodeId) {
273
+ return;
274
+ }
275
+ if (nodeId in visited) {
276
+ return;
277
+ }
278
+ visited[nodeId] = true;
279
+ var node = nodes[nodeId];
280
+ if (!node) {
281
+ return;
282
+ }
283
+ if (node.one) {
284
+ var one = nodes[node.one];
285
+ if (one.type === "branch") {
286
+ action(node, one);
287
+ } else {
288
+ traverseToHitBranch(nodes, node.one, visited, action);
275
289
  }
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
- }
290
+ }
291
+ if (node.two) {
292
+ var two = nodes[node.two];
293
+ if (two.type === "branch") {
294
+ action(node, two);
295
+ } else {
296
+ traverseToHitBranch(nodes, node.two, visited, action);
297
+ }
298
+ }
284
299
  }
285
300
 
286
- var idCounter = 1000
301
+ var idCounter = 1000;
287
302
  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)
303
+ var lastAddress = undefined;
304
+ if (addresses.length > 0) {
305
+ lastAddress = addresses[addresses.length - 1];
306
+ }
307
+ var address;
308
+ if (lastAddress && lastAddress.branch === node.id) {
309
+ address = lastAddress;
310
+ } else {
311
+ address = {
312
+ type: "address",
313
+ content: node.content,
314
+ id: "ad-" + idCounter,
315
+ branch: node.id,
316
+ one: end.id,
317
+ prev: [],
318
+ };
319
+ idCounter++;
320
+ nodes[address.id] = address;
321
+ end.prev.push(address.id);
322
+ addresses.push(address);
323
+ node.prev.push(address.id);
324
+ }
325
+ redirectNode(nodes, prev, node.id, address.id);
326
+ address.prev.push(prev.id);
327
+ node.prev = remove(node.prev, prev.id);
313
328
  }
314
329
 
315
330
  function buildTwoWayConnections(nodes, firstNodeId) {
316
- for (var id in nodes) {
317
- var node = nodes[id]
318
- node.id = id
319
- node.prev = []
320
- }
331
+ for (var id in nodes) {
332
+ var node = nodes[id];
333
+ node.id = id;
334
+ node.prev = [];
335
+ }
321
336
 
322
- traverse(nodes, firstNodeId, {}, connectBack)
337
+ traverse(nodes, firstNodeId, {}, connectBack);
323
338
  }
324
339
 
325
340
  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
341
+ var firstNodeId = undefined;
342
+ var minBranchId = 10000;
343
+ for (var id in nodes) {
344
+ var node = nodes[id];
345
+ if (node.type === "branch") {
346
+ if (node.branchId < minBranchId) {
347
+ firstNodeId = id;
348
+ minBranchId = node.branchId;
349
+ }
350
+ branches.push(node);
351
+ } else if (node.type === "select") {
352
+ if (!node.content) {
353
+ throw createError(
354
+ translate("A Select icon must have content"),
355
+ filename,
356
+ id,
357
+ );
358
+ }
359
+ node.cases = [];
360
+ } else if (node.type === "loopbegin") {
361
+ if (!node.content) {
362
+ throw createError(
363
+ translate("A Loop begin icon must have content"),
364
+ filename,
365
+ id,
366
+ );
367
+ }
368
+ } else if (node.type === "question") {
369
+ if (!node.content) {
370
+ throw createError(
371
+ translate("A Question icon must have content"),
372
+ filename,
373
+ id,
374
+ );
375
+ }
376
+ }
377
+ }
378
+
379
+ return firstNodeId;
353
380
  }
354
381
 
355
382
  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
- }
383
+ for (var id of Object.keys(nodes)) {
384
+ var node = nodes[id];
385
+ if (!node) {
386
+ continue;
387
+ }
388
+ if (node.type === "select") {
389
+ rewireSelect(nodes, node, filename);
390
+ } else if (node.type === "loopbegin") {
391
+ markLoopBody(nodes, node, filename);
364
392
  }
393
+ }
365
394
  }
366
395
 
367
396
  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)
397
+ var caseNodeId = selectNode.one;
398
+ while (caseNodeId) {
399
+ var caseNode = nodes[caseNodeId];
400
+ caseNodeId = caseNode.two;
401
+ if (caseNode.content) {
402
+ caseNode.type = "question";
403
+ caseNode.flag1 = 1;
404
+ caseNode.content = {
405
+ operator: "equal",
406
+ left: selectNode.content,
407
+ right: caseNode.content,
408
+ };
409
+ if (!caseNode.two) {
410
+ var errorId = caseNode.id + "-unexpected";
411
+ var errorAction = insertIcon(
412
+ nodes,
413
+ "error",
414
+ errorId,
415
+ selectNode.content,
416
+ );
417
+ errorAction.message = translate("Unexpected case value");
418
+
419
+ caseNode.two = errorId;
420
+ errorAction.prev.push(caseNode.id);
421
+ errorAction.one = caseNode.one;
422
+
423
+ var next = nodes[caseNode.one];
424
+ next.prev.push(errorId);
425
+ }
426
+ } else {
427
+ if (caseNode.two) {
428
+ throw createError(
429
+ translate("Only the rightmost Case icon can be empty"),
430
+ filename,
431
+ caseNode.id,
432
+ );
433
+ }
434
+ removeNodeOne(nodes, caseNode.id);
435
+ }
436
+ }
437
+ removeNodeOne(nodes, selectNode.id);
396
438
  }
397
439
 
398
440
  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
441
+ var node = {
442
+ type: type,
443
+ id: id,
444
+ content: content,
445
+ prev: [],
446
+ };
447
+ nodes[id] = node;
448
+ return node;
407
449
  }
408
450
 
409
451
  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]
452
+ var node = nodes[nodeId];
453
+ redirectPrev(nodes, node, node.one);
454
+ redirectNext(nodes, node, node.one);
455
+ delete nodes[nodeId];
414
456
  }
415
457
 
416
458
  function removeFromNext(node, next) {
417
- next.prev = next.prev.filter(prevId => prevId !== node.id)
459
+ next.prev = next.prev.filter((prevId) => prevId !== node.id);
418
460
  }
419
461
 
420
462
  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
- }
463
+ for (var prevId of node.prev) {
464
+ var prev = nodes[prevId];
465
+ if (prev.one === node.id) {
466
+ prev.one = newTarget;
429
467
  }
468
+ if (prev.two === node.id) {
469
+ prev.two = newTarget;
470
+ }
471
+ }
430
472
  }
431
473
 
432
474
  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
- }
475
+ var target = nodes[newTarget];
476
+ removeFromNext(node, target);
477
+ for (var prevId of node.prev) {
478
+ target.prev.push(prevId);
479
+ }
438
480
  }
439
481
 
440
482
  function rewireShortcircuit(nodes) {
441
- while (findShortcusts(nodes)) {
442
-
443
- }
483
+ while (findShortcusts(nodes)) {}
444
484
  }
445
485
 
446
486
  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
487
+ for (var id in nodes) {
488
+ var node = nodes[id];
489
+ if (node.type === "question") {
490
+ var andOperand = findAndOperand(nodes, node);
491
+ if (andOperand) {
492
+ writeAndShortcut(nodes, node, andOperand);
493
+ return true;
494
+ }
495
+ var orOperand = findOrOperand(nodes, node);
496
+ if (orOperand) {
497
+ writeOrShortcut(nodes, node, orOperand);
498
+ return true;
499
+ }
500
+ }
501
+ }
502
+ return false;
463
503
  }
464
504
 
465
505
  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
- }
506
+ var below = nodes[node.one];
507
+ if (below.type === "question") {
508
+ if (below.prev.length === 1 && below.two === node.two) {
509
+ return below;
471
510
  }
472
- return undefined
511
+ }
512
+ return undefined;
473
513
  }
474
514
 
475
515
  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
- }
516
+ var right = nodes[node.two];
517
+ if (right.type === "question") {
518
+ if (right.prev.length === 1 && right.one === node.one) {
519
+ return right;
481
520
  }
482
- return undefined
521
+ }
522
+ return undefined;
483
523
  }
484
524
 
485
525
  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]
526
+ var right = nodes[node.two];
527
+ var down = nodes[andOperand.one];
528
+ removeFromNext(andOperand, right);
529
+ removeFromNext(andOperand, down);
530
+ node.content = {
531
+ operator: "and",
532
+ left: normalizeContent(node),
533
+ right: normalizeContent(andOperand),
534
+ };
535
+ node.one = down.id;
536
+ node.flag1 = 1;
537
+ normalizeAnd(node);
538
+ down.prev.push(node.id);
539
+ delete nodes[andOperand.id];
499
540
  }
500
541
 
501
542
  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)
506
- 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]
543
+ var right = nodes[orOperand.two];
544
+ var down = nodes[orOperand.one];
545
+ removeFromNext(orOperand, right);
546
+ removeFromNext(orOperand, down);
547
+ node.content = {
548
+ operator: "or",
549
+ left: normalizeContent(node),
550
+ right: normalizeContent(orOperand),
551
+ };
552
+ node.two = right.id;
553
+ node.flag1 = 1;
554
+ normalizeOr(node);
555
+ right.prev.push(node.id);
556
+ delete nodes[orOperand.id];
515
557
  }
516
558
 
517
- function normalizeContent(question) {
518
- if (question.flag1 === 1) {
519
- return question.content
520
- }
559
+ function normalizeAnd(node) {
560
+ var op = node.content;
561
+ var left = op.left;
562
+ var right = op.right;
563
+ if (left.operator === "not" && right.operator === "not") {
564
+ node.content = {
565
+ operator: "or",
566
+ left: left.operand,
567
+ right: right.operand,
568
+ };
569
+ node.flag1 = 0;
570
+ }
571
+ }
521
572
 
522
- return {
523
- operator: "not",
524
- operand: question.content
525
- }
573
+ function normalizeOr(node) {
574
+ var op = node.content;
575
+ var left = op.left;
576
+ var right = op.right;
577
+ if (left.operator === "not" && right.operator === "not") {
578
+ node.content = {
579
+ operator: "and",
580
+ left: left.operand,
581
+ right: right.operand,
582
+ };
583
+ node.flag1 = 0;
584
+ }
526
585
  }
527
586
 
587
+ function normalizeContent(question) {
588
+ if (question.flag1 === 1) {
589
+ return question.content;
590
+ }
591
+
592
+ return {
593
+ operator: "not",
594
+ operand: question.content,
595
+ };
596
+ }
528
597
 
529
598
  function traverse(nodes, nodeId, visited, action) {
530
- if (!nodeId) {
531
- return
532
- }
533
-
534
- if (nodeId in visited) {
535
- return
536
- }
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)
599
+ if (!nodeId) {
600
+ return;
601
+ }
602
+
603
+ if (nodeId in visited) {
604
+ return;
605
+ }
606
+ visited[nodeId] = true;
607
+ var node = nodes[nodeId];
608
+ action(nodes, node);
609
+ traverse(nodes, node.one, visited, action);
610
+ traverse(nodes, node.two, visited, action);
542
611
  }
543
612
 
544
613
  function connectBack(nodes, node) {
545
- if (node.one) {
546
- var one = nodes[node.one]
547
- one.prev.push(node.id)
548
- }
549
- if (node.two) {
550
- var two = nodes[node.two]
551
- two.prev.push(node.id)
552
- }
614
+ if (node.one) {
615
+ var one = nodes[node.one];
616
+ one.prev.push(node.id);
617
+ }
618
+ if (node.two) {
619
+ var two = nodes[node.two];
620
+ two.prev.push(node.id);
621
+ }
553
622
  }
554
623
 
555
624
  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
568
- }
569
- }
570
- throw createError(translate("Loop end expected here"), filename, start.one)
625
+ var nextNodeId = start.one;
626
+ while (nextNodeId) {
627
+ var current = nodes[nextNodeId];
628
+ nextNodeId = current.one;
629
+ current.parentLoopId = start.id;
630
+ if (current.type === "loopbegin") {
631
+ nextNodeId = markLoopBody(nodes, current, filename);
632
+ } else if (current.type === "loopend") {
633
+ start.end = current.id;
634
+ start.next = current.one;
635
+ current.start = start.id;
636
+ return nextNodeId;
637
+ }
638
+ }
639
+ throw createError(translate("Loop end expected here"), filename, start.one);
571
640
  }
572
641
 
573
642
  module.exports = { drakonToStruct, drakonToGraph };
643
+
574
644
  },{"./structFlow":6,"./tools":8}],4:[function(require,module,exports){
575
645
  const { drakonToPseudocode, mindToTree } = require('./drakonToPromptStruct');
576
646
  const { htmlToString } = require("./browserTools")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drakongen",
3
- "version": "1.4.6",
3
+ "version": "1.4.7",
4
4
  "description": "Generates pseudocode and AST from drakon flowcharts built with DrakonWidget or DrakonHub.",
5
5
  "main": "src/index.js",
6
6
  "bin": {