Spaces:
Running
Running
Update index.html
Browse files- index.html +450 -19
index.html
CHANGED
@@ -1,19 +1,450 @@
|
|
1 |
-
<!
|
2 |
-
<html>
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<title>Visual Programming Language</title>
|
7 |
+
<style>
|
8 |
+
body { margin: 0; font-family: Arial, sans-serif; }
|
9 |
+
#canvas {
|
10 |
+
width: 100vw;
|
11 |
+
height: 100vh;
|
12 |
+
background: #f0f0f0;
|
13 |
+
position: relative;
|
14 |
+
overflow: auto;
|
15 |
+
}
|
16 |
+
.node {
|
17 |
+
position: absolute;
|
18 |
+
width: 250px;
|
19 |
+
background: #fff;
|
20 |
+
border: 2px solid #333;
|
21 |
+
border-radius: 5px;
|
22 |
+
padding: 10px;
|
23 |
+
cursor: move;
|
24 |
+
user-select: none;
|
25 |
+
z-index: 1;
|
26 |
+
}
|
27 |
+
.node-header { font-weight: bold; margin-bottom: 5px; }
|
28 |
+
.port {
|
29 |
+
width: 10px;
|
30 |
+
height: 10px;
|
31 |
+
background: #666;
|
32 |
+
border-radius: 50%;
|
33 |
+
position: absolute;
|
34 |
+
cursor: pointer;
|
35 |
+
}
|
36 |
+
.port-input { left: -15px; top: 50%; transform: translateY(-50%); }
|
37 |
+
.port-output { right: -15px; }
|
38 |
+
.port-output-true { right: -15px; top: 30%; }
|
39 |
+
.port-output-false { right: -15px; top: 70%; }
|
40 |
+
#toolbar {
|
41 |
+
position: fixed;
|
42 |
+
top: 10px;
|
43 |
+
left: 10px;
|
44 |
+
background: #ddd;
|
45 |
+
padding: 10px;
|
46 |
+
border-radius: 5px;
|
47 |
+
z-index: 10;
|
48 |
+
}
|
49 |
+
button { margin: 2px; }
|
50 |
+
#connections {
|
51 |
+
position: absolute;
|
52 |
+
top: 0;
|
53 |
+
left: 0;
|
54 |
+
width: 100%;
|
55 |
+
height: 100%;
|
56 |
+
pointer-events: none;
|
57 |
+
z-index: 0;
|
58 |
+
}
|
59 |
+
input { width: 80%; margin: 5px 0; }
|
60 |
+
.case-container { margin: 5px 0; display: flex; align-items: center; }
|
61 |
+
.label { font-size: 12px; color: #555; position: absolute; right: -50px; }
|
62 |
+
.arg-container { margin: 5px 0; }
|
63 |
+
</style>
|
64 |
+
</head>
|
65 |
+
<body>
|
66 |
+
<div id="toolbar">
|
67 |
+
<button onclick="addNode('Variable')">Variable</button>
|
68 |
+
<button onclick="addNode('Assign')">Assign</button>
|
69 |
+
<button onclick="addNode('Print')">Print</button>
|
70 |
+
<button onclick="addNode('Function')">Function</button>
|
71 |
+
<button onclick="addNode('Return')">Return</button>
|
72 |
+
<button onclick="addNode('Call')">Call</button>
|
73 |
+
<button onclick="addNode('If')">If</button>
|
74 |
+
<button onclick="addNode('WhileLoop')">While Loop</button>
|
75 |
+
<button onclick="addNode('Switch')">Switch</button>
|
76 |
+
<button onclick="addNode('Break')">Break</button>
|
77 |
+
<button onclick="addNode('Continue')">Continue</button>
|
78 |
+
<button onclick="runWorkflow()">Run Workflow</button>
|
79 |
+
</div>
|
80 |
+
<div id="canvas">
|
81 |
+
<svg id="connections"></svg>
|
82 |
+
</div>
|
83 |
+
|
84 |
+
<script>
|
85 |
+
const canvas = document.getElementById('canvas');
|
86 |
+
const connectionsSvg = document.getElementById('connections');
|
87 |
+
let nodes = [];
|
88 |
+
let connections = [];
|
89 |
+
let draggingNode = null;
|
90 |
+
let draggingPort = null;
|
91 |
+
let offsetX, offsetY;
|
92 |
+
let variables = {};
|
93 |
+
let functionReturns = {};
|
94 |
+
|
95 |
+
// Add a new node
|
96 |
+
function addNode(type) {
|
97 |
+
const node = document.createElement('div');
|
98 |
+
node.className = 'node';
|
99 |
+
node.style.left = `${Math.random() * 300 + 50}px`;
|
100 |
+
node.style.top = `${Math.random() * 300 + 50}px`;
|
101 |
+
node.dataset.type = type;
|
102 |
+
|
103 |
+
let html = `<div class="node-header">${type}</div>`;
|
104 |
+
if (type === 'Variable') {
|
105 |
+
html += `
|
106 |
+
<input type="text" class="var-name" placeholder="Variable Name">
|
107 |
+
<input type="text" class="var-value" placeholder="Value">
|
108 |
+
<div class="port port-output" data-type="output"></div>
|
109 |
+
`;
|
110 |
+
} else if (type === 'Assign') {
|
111 |
+
html += `
|
112 |
+
<input type="text" class="assign-expr" placeholder="Assignment (e.g., i = i + 1)">
|
113 |
+
<div class="port port-input" data-type="input"></div>
|
114 |
+
<div class="port port-output" data-type="output"></div>
|
115 |
+
`;
|
116 |
+
} else if (type === 'Print') {
|
117 |
+
html += `
|
118 |
+
<input type="text" class="print-value" placeholder="Variable or Text">
|
119 |
+
<div class="port port-input" data-type="input"></div>
|
120 |
+
`;
|
121 |
+
} else if (type === 'Function') {
|
122 |
+
html += `
|
123 |
+
<input type="text" class="func-name" placeholder="Function Name">
|
124 |
+
<div class="arg-container">
|
125 |
+
<input type="text" class="arg-name" placeholder="Arg Name (e.g., a)">
|
126 |
+
</div>
|
127 |
+
<button onclick="addArg(this.parentElement)">Add Arg</button>
|
128 |
+
<div class="port port-input" data-type="input"></div>
|
129 |
+
<div class="port port-output" data-type="output"></div>
|
130 |
+
`;
|
131 |
+
} else if (type === 'Return') {
|
132 |
+
html += `
|
133 |
+
<input type="text" class="return-value" placeholder="Return Value (e.g., x + 1)">
|
134 |
+
<div class="port port-input" data-type="input"></div>
|
135 |
+
`;
|
136 |
+
} else if (type === 'Call') {
|
137 |
+
html += `
|
138 |
+
<input type="text" class="call-name" placeholder="Function Name (e.g., add)">
|
139 |
+
<div class="arg-container">
|
140 |
+
<input type="text" class="call-arg" placeholder="Arg Value (e.g., 3)">
|
141 |
+
</div>
|
142 |
+
<button onclick="addCallArg(this.parentElement)">Add Arg</button>
|
143 |
+
<input type="text" class="call-result" placeholder="Result Var (e.g., z)">
|
144 |
+
<div class="port port-input" data-type="input"></div>
|
145 |
+
<div class="port port-output" data-type="output"></div>
|
146 |
+
`;
|
147 |
+
} else if (type === 'If') {
|
148 |
+
html += `
|
149 |
+
<input type="text" class="condition" placeholder="Condition (e.g., x > 5)">
|
150 |
+
<div class="port port-input" data-type="input"></div>
|
151 |
+
<div class="port port-output-true" data-type="output" data-branch="true"><span class="label">True</span></div>
|
152 |
+
<div class="port port-output-false" data-type="output" data-branch="false"><span class="label">False</span></div>
|
153 |
+
`;
|
154 |
+
} else if (type === 'WhileLoop') {
|
155 |
+
html += `
|
156 |
+
<input type="text" class="condition" placeholder="Condition (e.g., i < 5)">
|
157 |
+
<input type="text" class="update" placeholder="Update (e.g., i = i + 1)">
|
158 |
+
<div class="port port-input" data-type="input"></div>
|
159 |
+
<div class="port port-output" data-type="output"></div>
|
160 |
+
`;
|
161 |
+
} else if (type === 'Switch') {
|
162 |
+
html += `
|
163 |
+
<input type="text" class="switch-var" placeholder="Variable to Switch">
|
164 |
+
<div class="case-container">
|
165 |
+
<input type="text" class="case-value" placeholder="Case Value (e.g., 1)">
|
166 |
+
<input type="checkbox" class="case-break" title="Break after this case">
|
167 |
+
<div class="port port-output" data-type="output" data-case="0"></div>
|
168 |
+
</div>
|
169 |
+
<button onclick="addCase(this.parentElement)">Add Case</button>
|
170 |
+
<div class="port port-input" data-type="input"></div>
|
171 |
+
`;
|
172 |
+
} else if (type === 'Break' || type === 'Continue') {
|
173 |
+
html += `
|
174 |
+
<div class="port port-input" data-type="input"></div>
|
175 |
+
`;
|
176 |
+
}
|
177 |
+
node.innerHTML = html;
|
178 |
+
canvas.appendChild(node);
|
179 |
+
nodes.push(node);
|
180 |
+
|
181 |
+
// Dragging node
|
182 |
+
node.addEventListener('mousedown', (e) => {
|
183 |
+
if (!e.target.classList.contains('port') && e.target.tagName !== 'INPUT' && e.target.tagName !== 'BUTTON') {
|
184 |
+
draggingNode = node;
|
185 |
+
const rect = node.getBoundingClientRect();
|
186 |
+
offsetX = e.clientX - rect.left;
|
187 |
+
offsetY = e.clientY - rect.top;
|
188 |
+
}
|
189 |
+
});
|
190 |
+
|
191 |
+
// Connecting ports
|
192 |
+
const ports = node.querySelectorAll('.port');
|
193 |
+
ports.forEach(port => {
|
194 |
+
port.addEventListener('mousedown', (e) => {
|
195 |
+
e.stopPropagation();
|
196 |
+
draggingPort = port;
|
197 |
+
});
|
198 |
+
});
|
199 |
+
}
|
200 |
+
|
201 |
+
// Add a new argument to Function node
|
202 |
+
function addArg(funcNode) {
|
203 |
+
const argCount = funcNode.querySelectorAll('.arg-container').length;
|
204 |
+
const argDiv = document.createElement('div');
|
205 |
+
argDiv.className = 'arg-container';
|
206 |
+
argDiv.innerHTML = `<input type="text" class="arg-name" placeholder="Arg Name (e.g., arg${argCount + 1})">`;
|
207 |
+
funcNode.insertBefore(argDiv, funcNode.querySelector('button'));
|
208 |
+
}
|
209 |
+
|
210 |
+
// Add a new argument to Call node
|
211 |
+
function addCallArg(callNode) {
|
212 |
+
const argCount = callNode.querySelectorAll('.arg-container').length;
|
213 |
+
const argDiv = document.createElement('div');
|
214 |
+
argDiv.className = 'arg-container';
|
215 |
+
argDiv.innerHTML = `<input type="text" class="call-arg" placeholder="Arg Value (e.g., ${argCount + 1})">`;
|
216 |
+
callNode.insertBefore(argDiv, callNode.querySelector('button'));
|
217 |
+
}
|
218 |
+
|
219 |
+
// Add a new case to Switch node
|
220 |
+
function addCase(switchNode) {
|
221 |
+
const caseCount = switchNode.querySelectorAll('.case-container').length;
|
222 |
+
const caseDiv = document.createElement('div');
|
223 |
+
caseDiv.className = 'case-container';
|
224 |
+
caseDiv.innerHTML = `
|
225 |
+
<input type="text" class="case-value" placeholder="Case Value (e.g., ${caseCount + 1})">
|
226 |
+
<input type="checkbox" class="case-break" title="Break after this case">
|
227 |
+
<div class="port port-output" data-type="output" data-case="${caseCount}"></div>
|
228 |
+
`;
|
229 |
+
switchNode.insertBefore(caseDiv, switchNode.querySelector('button'));
|
230 |
+
const newPort = caseDiv.querySelector('.port');
|
231 |
+
newPort.addEventListener('mousedown', (e) => {
|
232 |
+
e.stopPropagation();
|
233 |
+
draggingPort = newPort;
|
234 |
+
});
|
235 |
+
}
|
236 |
+
|
237 |
+
// Handle dragging
|
238 |
+
document.addEventListener('mousemove', (e) => {
|
239 |
+
if (draggingNode) {
|
240 |
+
const canvasRect = canvas.getBoundingClientRect();
|
241 |
+
const x = e.clientX - offsetX - canvasRect.left;
|
242 |
+
const y = e.clientY - offsetY - canvasRect.top;
|
243 |
+
draggingNode.style.left = `${x}px`;
|
244 |
+
draggingNode.style.top = `${y}px`;
|
245 |
+
updateConnections();
|
246 |
+
}
|
247 |
+
});
|
248 |
+
|
249 |
+
// Handle mouse up
|
250 |
+
document.addEventListener('mouseup', (e) => {
|
251 |
+
if (draggingNode) draggingNode = null;
|
252 |
+
if (draggingPort) {
|
253 |
+
const targetPort = e.target.closest('.port');
|
254 |
+
if (targetPort && targetPort !== draggingPort &&
|
255 |
+
draggingPort.dataset.type !== targetPort.dataset.type) {
|
256 |
+
connections.push({ from: draggingPort, to: targetPort });
|
257 |
+
updateConnections();
|
258 |
+
}
|
259 |
+
draggingPort = null;
|
260 |
+
}
|
261 |
+
});
|
262 |
+
|
263 |
+
// Update connection lines
|
264 |
+
function updateConnections() {
|
265 |
+
connectionsSvg.innerHTML = '';
|
266 |
+
const canvasRect = canvas.getBoundingClientRect();
|
267 |
+
connections.forEach(conn => {
|
268 |
+
const fromRect = conn.from.getBoundingClientRect();
|
269 |
+
const toRect = conn.to.getBoundingClientRect();
|
270 |
+
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
271 |
+
line.setAttribute('x1', fromRect.left + fromRect.width / 2 - canvasRect.left);
|
272 |
+
line.setAttribute('y1', fromRect.top + fromRect.height / 2 - canvasRect.top);
|
273 |
+
line.setAttribute('x2', toRect.left + toRect.width / 2 - canvasRect.left);
|
274 |
+
line.setAttribute('y2', toRect.top + toRect.height / 2 - canvasRect.top);
|
275 |
+
line.setAttribute('stroke', '#333');
|
276 |
+
line.setAttribute('stroke-width', '2');
|
277 |
+
connectionsSvg.appendChild(line);
|
278 |
+
});
|
279 |
+
}
|
280 |
+
|
281 |
+
// Evaluate an expression
|
282 |
+
function evaluateExpression(expr) {
|
283 |
+
if (!expr) return null;
|
284 |
+
try {
|
285 |
+
let evalExpr = expr;
|
286 |
+
for (const [varName, varValue] of Object.entries(variables)) {
|
287 |
+
evalExpr = evalExpr.replace(new RegExp(`\\b${varName}\\b`, 'g'), varValue);
|
288 |
+
}
|
289 |
+
if (evalExpr.includes('=')) {
|
290 |
+
const [varName, value] = evalExpr.split('=').map(s => s.trim());
|
291 |
+
variables[varName] = eval(value);
|
292 |
+
return null;
|
293 |
+
}
|
294 |
+
return eval(evalExpr);
|
295 |
+
} catch (e) {
|
296 |
+
return `Error in expression: ${e.message}`;
|
297 |
+
}
|
298 |
+
}
|
299 |
+
|
300 |
+
// Run the workflow
|
301 |
+
function runWorkflow() {
|
302 |
+
let output = '';
|
303 |
+
variables = {};
|
304 |
+
functionReturns = {};
|
305 |
+
nodes.forEach(n => delete n.dataset.executed);
|
306 |
+
|
307 |
+
function executeNode(node, loopControl = { break: false, continue: false }, funcContext = null) {
|
308 |
+
if (node.dataset.executed || loopControl.break) return;
|
309 |
+
node.dataset.executed = true;
|
310 |
+
|
311 |
+
const type = node.dataset.type;
|
312 |
+
if (type === 'Variable') {
|
313 |
+
const name = node.querySelector('.var-name').value;
|
314 |
+
const value = node.querySelector('.var-value').value;
|
315 |
+
variables[name] = isNaN(value) ? value : Number(value);
|
316 |
+
executeNext(node, 'output', loopControl, funcContext);
|
317 |
+
} else if (type === 'Assign') {
|
318 |
+
const expr = node.querySelector('.assign-expr').value;
|
319 |
+
const error = evaluateExpression(expr);
|
320 |
+
if (error) output += `${error}\n`;
|
321 |
+
executeNext(node, 'output', loopControl, funcContext);
|
322 |
+
} else if (type === 'Print') {
|
323 |
+
const value = node.querySelector('.print-value').value;
|
324 |
+
const printVal = variables[value] !== undefined ? variables[value] : value;
|
325 |
+
output += `${printVal}\n`;
|
326 |
+
} else if (type === 'Function') {
|
327 |
+
const funcName = node.querySelector('.func-name').value;
|
328 |
+
const args = Array.from(node.querySelectorAll('.arg-name')).map(input => input.value);
|
329 |
+
const prevVars = { ...variables };
|
330 |
+
funcContext = { name: funcName, args: {}, returned: false };
|
331 |
+
executeNext(node, 'output', loopControl, funcContext);
|
332 |
+
variables = prevVars;
|
333 |
+
if (!funcContext.returned) functionReturns[funcName] = undefined;
|
334 |
+
} else if (type === 'Return') {
|
335 |
+
if (funcContext) {
|
336 |
+
const returnValue = node.querySelector('.return-value').value;
|
337 |
+
const result = evaluateExpression(returnValue);
|
338 |
+
if (result && !result.startsWith('Error')) {
|
339 |
+
functionReturns[funcContext.name] = result;
|
340 |
+
} else if (result) {
|
341 |
+
output += `${result}\n`;
|
342 |
+
}
|
343 |
+
funcContext.returned = true;
|
344 |
+
}
|
345 |
+
} else if (type === 'Call') {
|
346 |
+
const callName = node.querySelector('.call-name').value;
|
347 |
+
const callArgs = Array.from(node.querySelectorAll('.call-arg')).map(input => evaluateExpression(input.value) || input.value);
|
348 |
+
const resultVar = node.querySelector('.call-result').value;
|
349 |
+
const targetFunc = nodes.find(n => n.dataset.type === 'Function' && n.querySelector('.func-name').value === callName);
|
350 |
+
if (targetFunc) {
|
351 |
+
const funcArgs = Array.from(targetFunc.querySelectorAll('.arg-name')).map(input => input.value);
|
352 |
+
const prevVars = { ...variables };
|
353 |
+
funcArgs.forEach((argName, index) => {
|
354 |
+
variables[argName] = callArgs[index] !== undefined ? callArgs[index] : undefined;
|
355 |
+
});
|
356 |
+
const funcContext = { name: callName, args: {}, returned: false };
|
357 |
+
executeNode(targetFunc, { break: false, continue: false }, funcContext);
|
358 |
+
variables = prevVars;
|
359 |
+
if (resultVar && functionReturns[callName] !== undefined) {
|
360 |
+
variables[resultVar] = functionReturns[callName];
|
361 |
+
}
|
362 |
+
} else {
|
363 |
+
output += `Function ${callName} not found!\n`;
|
364 |
+
}
|
365 |
+
executeNext(node, 'output', loopControl, funcContext);
|
366 |
+
} else if (type === 'If') {
|
367 |
+
const condition = node.querySelector('.condition').value;
|
368 |
+
let result = false;
|
369 |
+
try {
|
370 |
+
let evalCondition = condition;
|
371 |
+
for (const [varName, varValue] of Object.entries(variables)) {
|
372 |
+
evalCondition = evalCondition.replace(new RegExp(`\\b${varName}\\b`, 'g'), varValue);
|
373 |
+
}
|
374 |
+
result = eval(evalCondition);
|
375 |
+
} catch (e) {
|
376 |
+
output += `Error in If condition: ${e.message}\n`;
|
377 |
+
}
|
378 |
+
executeNext(node, result ? 'true' : 'false', loopControl, funcContext);
|
379 |
+
} else if (type === 'WhileLoop') {
|
380 |
+
const condition = node.querySelector('.condition').value;
|
381 |
+
const update = node.querySelector('.update').value;
|
382 |
+
let result = true;
|
383 |
+
while (result && !loopControl.break) {
|
384 |
+
try {
|
385 |
+
let evalCondition = condition;
|
386 |
+
for (const [varName, varValue] of Object.entries(variables)) {
|
387 |
+
evalCondition = evalCondition.replace(new RegExp(`\\b${varName}\\b`, 'g'), varValue);
|
388 |
+
}
|
389 |
+
result = eval(evalCondition);
|
390 |
+
if (result) {
|
391 |
+
let innerControl = { break: false, continue: false };
|
392 |
+
executeNext(node, 'output', innerControl, funcContext);
|
393 |
+
if (innerControl.continue) continue;
|
394 |
+
if (innerControl.break) break;
|
395 |
+
const error = evaluateExpression(update);
|
396 |
+
if (error) output += `${error}\n`;
|
397 |
+
}
|
398 |
+
} catch (e) {
|
399 |
+
output += `Error in WhileLoop condition: ${e.message}\n`;
|
400 |
+
break;
|
401 |
+
}
|
402 |
+
}
|
403 |
+
} else if (type === 'Switch') {
|
404 |
+
const switchVar = node.querySelector('.switch-var').value;
|
405 |
+
const caseInputs = node.querySelectorAll('.case-container');
|
406 |
+
const value = variables[switchVar];
|
407 |
+
let matched = false;
|
408 |
+
for (let [index, container] of caseInputs.entries()) {
|
409 |
+
const caseValue = container.querySelector('.case-value').value;
|
410 |
+
const shouldBreak = container.querySelector('.case-break').checked;
|
411 |
+
if (caseValue == value || matched) {
|
412 |
+
matched = true;
|
413 |
+
executeNext(node, index.toString(), loopControl, funcContext);
|
414 |
+
if (shouldBreak) break;
|
415 |
+
}
|
416 |
+
}
|
417 |
+
if (!matched) output += `No matching case for ${switchVar}=${value}\n`;
|
418 |
+
} else if (type === 'Break') {
|
419 |
+
loopControl.break = true;
|
420 |
+
} else if (type === 'Continue') {
|
421 |
+
loopControl.continue = true;
|
422 |
+
}
|
423 |
+
}
|
424 |
+
|
425 |
+
function executeNext(node, branch, loopControl, funcContext) {
|
426 |
+
let portClass;
|
427 |
+
if (node.dataset.type === 'Switch') {
|
428 |
+
portClass = `.port-output[data-case="${branch}"]`;
|
429 |
+
} else {
|
430 |
+
portClass = branch === 'true' ? '.port-output-true' : branch === 'false' ? '.port-output-false' : '.port-output';
|
431 |
+
}
|
432 |
+
const nextConn = connections.find(c => c.from === node.querySelector(portClass));
|
433 |
+
if (nextConn && (!funcContext || !funcContext.returned)) {
|
434 |
+
const nextNode = nextConn.to.parentElement;
|
435 |
+
executeNode(nextNode, loopControl, funcContext);
|
436 |
+
}
|
437 |
+
}
|
438 |
+
|
439 |
+
const startNodes = nodes.filter(n => !connections.some(c => c.to.parentElement === n));
|
440 |
+
if (startNodes.length > 0) {
|
441 |
+
startNodes.forEach(node => executeNode(node));
|
442 |
+
} else {
|
443 |
+
nodes.forEach(node => executeNode(node));
|
444 |
+
}
|
445 |
+
|
446 |
+
alert(output || 'No output generated! Check your connections and node settings.');
|
447 |
+
}
|
448 |
+
</script>
|
449 |
+
</body>
|
450 |
+
</html>
|