drakongen 1.4.4 → 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 +403 -329
- package/exampleproject/.drakontech/history.json +12 -0
- package/exampleproject/.drakontech/opened.txt +1 -0
- package/exampleproject/foo.drakon +23 -10
- package/exampleproject/foo.txt +13 -6
- package/exampleproject/math/approximately equal.drakon +15 -7
- package/exampleproject/solution.json +3 -0
- package/examples/.drakontech/history.json +124 -0
- package/examples/.drakontech/opened.txt +1 -0
- package/examples/01. /320/221/320/265/320/273/321/213/320/271.txt" +1 -1
- package/examples/02. /320/247/321/221/321/200/320/275/321/213/320/271.txt" +1 -1
- package/examples/03. /320/241/320/265/321/200/321/213/320/271.txt" +1 -1
- package/examples/04. /320/221/321/203/321/200/321/213/320/271.txt" +1 -1
- package/examples/05. /320/226/321/221/320/273/321/202/321/213/320/271.txt" +1 -1
- package/examples/06. /320/221/320/260/320/263/321/200/320/276/320/262/321/213/320/271.txt" +1 -1
- package/examples/07. /320/244/320/270/320/276/320/273/320/265/321/202/320/276/320/262/321/213/320/271.txt" +1 -1
- package/examples/08. /320/221/320/270/321/200/321/216/320/267/320/276/320/262/321/213/320/271.txt" +1 -1
- package/examples/09. /320/236/321/200/320/260/320/275/320/266/320/265/320/262/321/213/320/271.txt" +1 -1
- package/examples/10. /320/240/320/276/320/267/320/276/320/262/321/213/320/271.txt" +1 -1
- package/examples/11. /320/227/320/260/321/211/320/270/321/202/320/275/321/213/320/271.txt" +1 -1
- package/examples/12. /320/221/320/276/320/273/320/276/321/202/320/275/321/213/320/271.txt" +1 -1
- package/examples/13. /320/241/320/260/320/273/320/260/321/202/320/276/320/262/321/213/320/271.txt" +1 -1
- package/examples/14. /320/227/320/276/320/273/320/276/321/202/320/276/320/271.txt" +1 -1
- package/examples/15. /320/241/320/270/320/275/320/270/320/271.txt" +1 -1
- package/examples/16. /320/223/320/276/320/273/321/203/320/261/320/276/320/271.txt" +1 -1
- package/examples/17. /320/241/320/260/320/273/320/260/321/202/320/276/320/262/321/213/320/271.txt" +1 -1
- package/examples/18. /320/241/321/202/320/260/320/273/321/214/320/275/320/276/320/271.txt" +1 -1
- package/examples/How to tune PID on a quadcopter.txt +12 -12
- package/examples/Logical_And.drakon +37 -0
- package/examples/Logical_And.txt +9 -0
- package/examples/Logical_And_Inv.drakon +37 -0
- package/examples/Logical_And_Inv.txt +9 -0
- package/examples/Logical_And_Inv_x2.drakon +91 -0
- package/examples/Logical_And_Inv_x2.txt +15 -0
- package/examples/Logical_Or.drakon +37 -0
- package/examples/Logical_Or.txt +9 -0
- package/examples/Logical_Or_Inv.drakon +37 -0
- package/examples/Logical_Or_Inv.txt +9 -0
- package/package.json +1 -1
- package/src/drakonToStruct.js +394 -325
- package/src/translate.js +3 -0
- package/src/treeTools.js +1 -0
- /package/examples/01. /320/221/320/265/320/273/321/213/320/{270/314/206.drakon" → 271.drakon"} +0 -0
- /package/examples/02. /320/247/{320/265/314/210/321/200/320/275/321/213/320/270/314/206.drakon" → 321/221/321/200/320/275/321/213/320/271.drakon"} +0 -0
- /package/examples/03. /320/241/320/265/321/200/321/213/320/{270/314/206.drakon" → 271.drakon"} +0 -0
- /package/examples/04. /320/221/321/203/321/200/321/213/320/{270/314/206.drakon" → 271.drakon"} +0 -0
- /package/examples/05. /320/226/{320/265/314/210/320/273/321/202/321/213/320/270/314/206.drakon" → 321/221/320/273/321/202/321/213/320/271.drakon"} +0 -0
- /package/examples/06. /320/221/320/260/320/263/321/200/320/276/320/262/321/213/320/{270/314/206.drakon" → 271.drakon"} +0 -0
- /package/examples/07. /320/244/320/270/320/276/320/273/320/265/321/202/320/276/320/262/321/213/320/{270/314/206.drakon" → 271.drakon"} +0 -0
- /package/examples/08. /320/221/320/270/321/200/321/216/320/267/320/276/320/262/321/213/320/{270/314/206.drakon" → 271.drakon"} +0 -0
- /package/examples/09. /320/236/321/200/320/260/320/275/320/266/320/265/320/262/321/213/320/{270/314/206.drakon" → 271.drakon"} +0 -0
- /package/examples/10. /320/240/320/276/320/267/320/276/320/262/321/213/320/{270/314/206.drakon" → 271.drakon"} +0 -0
- /package/examples/11. /320/227/320/260/321/211/320/270/321/202/320/275/321/213/320/{270/314/206.drakon" → 271.drakon"} +0 -0
- /package/examples/12. /320/221/320/276/320/273/320/276/321/202/320/275/321/213/320/{270/314/206.drakon" → 271.drakon"} +0 -0
- /package/examples/13. /320/241/320/260/320/273/320/260/321/202/320/276/320/262/321/213/320/{270/314/206.drakon" → 271.drakon"} +0 -0
- /package/examples/14. /320/227/320/276/320/273/320/276/321/202/320/276/320/{270/314/206.drakon" → 271.drakon"} +0 -0
- /package/examples/15. /320/241/320/270/320/275/320/270/320/{270/314/206.drakon" → 271.drakon"} +0 -0
- /package/examples/16. /320/223/320/276/320/273/321/203/320/261/320/276/320/{270/314/206.drakon" → 271.drakon"} +0 -0
- /package/examples/17. /320/241/320/260/320/273/320/260/321/202/320/276/320/262/321/213/320/{270/314/206.drakon" → 271.drakon"} +0 -0
- /package/examples/18. /320/241/321/202/320/260/320/273/321/214/320/275/320/276/320/{270/314/206.drakon" → 271.drakon"} +0 -0
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")
|
|
@@ -1284,6 +1354,7 @@ var translationsRu = {
|
|
|
1284
1354
|
"A Loop begin icon must have content": "Икона начала цикла ДЛЯ должна содержать данные",
|
|
1285
1355
|
"A Question icon must have content": "Икона Вопрос должна содержать данные",
|
|
1286
1356
|
"A Select icon must have content": "Икона Выбор должна содержать данные",
|
|
1357
|
+
"Unexpected case value": "Неожиданное значение иконы Вариант",
|
|
1287
1358
|
"Loop end expected here": "Здесь ожидается конец цикла",
|
|
1288
1359
|
"An exit from the loop must lead to the point right after the loop end": "Выход из цикла должен вести в точку сразу за его концом",
|
|
1289
1360
|
"A silhouette branch is not referenced": "Нет ссылок на ветку силуэта",
|
|
@@ -1314,6 +1385,7 @@ var translationsEn = {
|
|
|
1314
1385
|
'A Loop begin icon must have content': 'A Loop begin icon must have content',
|
|
1315
1386
|
'A Question icon must have content': 'A Question icon must have content',
|
|
1316
1387
|
'A Select icon must have content': 'A Select icon must have content',
|
|
1388
|
+
"Unexpected case value": "Unexpected case value",
|
|
1317
1389
|
'Loop end expected here': 'Loop end expected here',
|
|
1318
1390
|
'An exit from the loop must lead to the point right after the loop end': 'An exit from the loop must lead to the point right after the loop end',
|
|
1319
1391
|
'A silhouette branch is not referenced': 'A silhouette branch is not referenced',
|
|
@@ -1344,6 +1416,7 @@ var translationsNo = {
|
|
|
1344
1416
|
'A Loop begin icon must have content': 'Et Loop-startikon må ha innhold',
|
|
1345
1417
|
'A Question icon must have content': 'Et Spørsmål-ikon må ha innhold',
|
|
1346
1418
|
'A Select icon must have content': 'Et Velg-ikon må ha innhold',
|
|
1419
|
+
"Unexpected case value": "Uventet tilfelle verdi",
|
|
1347
1420
|
'Loop end expected here': 'Slutt på løkke forventet her',
|
|
1348
1421
|
'An exit from the loop must lead to the point right after the loop end': 'En utgang fra løkken må føre til punktet rett etter løkkens slutt',
|
|
1349
1422
|
'A silhouette branch is not referenced': 'En silhuettgren er ikke referert',
|
|
@@ -1403,6 +1476,7 @@ function optimizeTree(steps) {
|
|
|
1403
1476
|
|
|
1404
1477
|
function optimizeLoop(step) {
|
|
1405
1478
|
return {
|
|
1479
|
+
id: step.id,
|
|
1406
1480
|
type: step.type,
|
|
1407
1481
|
content: step.content,
|
|
1408
1482
|
body: optimizeTree(step.body)
|