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.
- package/browser/drakongen.js +399 -329
- package/package.json +1 -1
package/browser/drakongen.js
CHANGED
|
@@ -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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
331
|
+
for (var id in nodes) {
|
|
332
|
+
var node = nodes[id];
|
|
333
|
+
node.id = id;
|
|
334
|
+
node.prev = [];
|
|
335
|
+
}
|
|
321
336
|
|
|
322
|
-
|
|
337
|
+
traverse(nodes, firstNodeId, {}, connectBack);
|
|
323
338
|
}
|
|
324
339
|
|
|
325
340
|
function findStartNode(nodes, filename, branches) {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
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
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
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
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
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
|
-
|
|
459
|
+
next.prev = next.prev.filter((prevId) => prevId !== node.id);
|
|
418
460
|
}
|
|
419
461
|
|
|
420
462
|
function redirectPrev(nodes, node, newTarget) {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
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
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
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
|
-
|
|
442
|
-
|
|
443
|
-
}
|
|
483
|
+
while (findShortcusts(nodes)) {}
|
|
444
484
|
}
|
|
445
485
|
|
|
446
486
|
function findShortcusts(nodes) {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
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
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
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
|
-
|
|
511
|
+
}
|
|
512
|
+
return undefined;
|
|
473
513
|
}
|
|
474
514
|
|
|
475
515
|
function findOrOperand(nodes, node) {
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
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
|
-
|
|
521
|
+
}
|
|
522
|
+
return undefined;
|
|
483
523
|
}
|
|
484
524
|
|
|
485
525
|
function writeAndShortcut(nodes, node, andOperand) {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
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
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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
|
|
518
|
-
|
|
519
|
-
|
|
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
|
-
|
|
523
|
-
|
|
524
|
-
|
|
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
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
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
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
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
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
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")
|