// ── WCProgram — root container + block registration ────────────────────────── // // WCProgram is the root custom element that owns the flowchart workspace. // Unlike other blocks, WCProgram extends HTMLElement directly — not WCBlock — // because the program container itself is not draggable or linkable. // // This file also contains the block registry and registration calls. // Must be loaded AFTER all block class definitions exist in the page. // // Load order dependencies: // All block class files must be loaded first: // wcblock.js → blocks-algo.js → blocks-data.js → blocks-proc.js // helpers.js → toCName // dom.js → $id // // This file is loaded last among the block group, before persist.js and app.js. // ═══════════════════════════════════════════════════════════════════════════════ // WCProgram — root container // ═══════════════════════════════════════════════════════════════════════════════ class WCProgram extends HTMLElement { constructor() { super(); this.counter = 0; } build() { const startFrag = document.importNode( $id("wc-start").content, true ); const endFrag = document.importNode( $id("wc-end").content, true ); startFrag.firstElementChild.id = "main-start"; endFrag.firstElementChild.id = "main-end"; this.appendChild(startFrag); this.appendChild(endFrag); } makeNode(tag) { const info = BLOCK_REGISTRY[tag]; if (!info) return null; this.counter++; const frag = document.importNode(document.querySelector(info.template).content, true); const el = frag.firstElementChild; el.id = info.prefix + this.counter; return el; } newNode(tag, parent) { const info = BLOCK_REGISTRY[tag]; if (!info) return null; const el = this.makeNode(tag); if (!el) return null; (parent || (info.getParent ? info.getParent() : this)).appendChild(el); return el; } newDecisionNode() { return this.newNode("wc-decision"); } newExpressionNode() { return this.newNode("wc-expression"); } newVarRefNode() { return this.newNode("wc-varref"); } newOutputNode() { return this.newNode("wc-output"); } newProcCallNode() { const el = this.newNode("wc-proccall"); if (el) refreshProcOptions(); return el; } newUnitNode() { const el = this.newNode("wc-unit", $id("data-area")); if (el) refreshAll(); return el; } newQuantityNode() { const el = this.newNode("wc-quantity", $id("data-area")); if (el) { refreshQuantityOptions(); refreshDefaultUnitOptions(); } return el; } newBooleanNode() { const el = this.newNode("wc-boolean", $id("variable-area")); if (el) refreshVarRefOptions(); return el; } newScalarNode() { const el = this.newNode("wc-scalar", $id("variable-area")); if (el) { refreshUnitOptions(); refreshVarRefOptions(); } return el; } newStringNode() { const el = this.newNode("wc-string", $id("variable-area")); if (el) refreshVarRefOptions(); return el; } newArrayNode() { const el = this.newNode("wc-array", $id("variable-area")); if (el) refreshVarRefOptions(); return el; } newProcedureNode(libName) { const libPane = libName ? document.querySelector(`.lib-pane[data-lib-name="${libName}"]`) : document.querySelector(".lib-pane"); if (!libPane) return null; const el = this.newNode("wc-procedure", libPane.querySelector(".procedures-area")); if (el) refreshProcOptions(); return el; } _gatherUsedTypes() { const usedQuantities = new Set(), usedUnits = new Set(); for (const d of $id("variable-area").querySelectorAll("wc-scalar")) { const t = d.querySelector(".var-type").value; if (t) usedQuantities.add(t); const u = d.querySelector(".var-unit").value; if (u) usedUnits.add(u); } return { usedQuantities, usedUnits }; } _generateTypeDefs(usedQuantities) { return Array.from( $id("data-area").querySelectorAll("wc-quantity") ).filter(q => usedQuantities.has(q.querySelector(".quantity-name").value || "unnamed")) .map(q => { const name = q.querySelector(".quantity-name").value || "unnamed"; return `typedef double ${name};`; }).join("\n"); } _generateUnitDefs(usedUnits) { return Array.from( $id("data-area").querySelectorAll("wc-unit") ).filter(u => usedUnits.has(u.querySelector(".unit-suffix").value)) .map(u => { const type = u.querySelector(".unit-type").value || "unknown"; const suffix = u.querySelector(".unit-suffix").value || ""; const fullname = u.querySelector(".unit-fullname").value || ""; const si = u.querySelector(".unit-si").value || "1"; return `// unit: ${fullname} (${suffix}) = 1 ${type} * ${si}`; }).join("\n"); } _generateVariableDeclarations() { const boolDefs = Array.from($id("variable-area").querySelectorAll("wc-boolean")).map(d => d.generateCode()).join("\n"); const scalarDefs = Array.from($id("variable-area").querySelectorAll("wc-scalar")).map(d => d.generateCode()).join("\n"); const stringDefs = Array.from($id("variable-area").querySelectorAll("wc-string")).map(d => d.generateCode()).join("\n"); const arrayDefs = Array.from($id("variable-area").querySelectorAll("wc-array")).map(d => d.generateCode()).join("\n"); const exprDefs = Array.from(this.querySelectorAll("wc-expression")).map(d => d.generateCode()).join("\n"); return { boolDefs, scalarDefs, stringDefs, arrayDefs, exprDefs }; } _generateIncludes(proccalls, hasStrings) { const libNames = new Set(); for (const p of proccalls) { const qname = p.querySelector(".proccall-select").value; if (qname) { const lib = qname.split("/")[0]; if (lib) libNames.add(lib); } } let includes = Array.from(libNames).map(l => `#include <${l}.h>`).join("\n"); if (hasStrings) { const hdr = "#include \n#include "; includes = includes ? includes + "\n" + hdr : hdr; } return includes; } generateCode() { const { usedQuantities, usedUnits } = this._gatherUsedTypes(); const typeDefs = this._generateTypeDefs(usedQuantities); const unitDefs = this._generateUnitDefs(usedUnits); const { boolDefs, scalarDefs, stringDefs, arrayDefs, exprDefs } = this._generateVariableDeclarations(); const hasStrings = $id("variable-area").querySelectorAll("wc-string").length > 0; const stringStruct = hasStrings ? "typedef struct { char *data; long int length; } string;\n\n" : ""; const body = this.firstElementChild.generateCode(new Set()); const proccalls = this.querySelectorAll("wc-proccall"); const includes = this._generateIncludes(proccalls, hasStrings); const stringNames = Array.from($id("variable-area").querySelectorAll("wc-string")).map( d => toCName(d.querySelector(".var-name").value) || "s" ); const freeSection = hasStrings ? "\n" + stringNames.map(n => `free(${n}.data);`).join("\n") + "\n" : ""; const dataSection = (boolDefs || scalarDefs || stringDefs || arrayDefs || exprDefs) ? [boolDefs, scalarDefs, stringDefs, arrayDefs, exprDefs] .filter(Boolean).map(s => s + "\n").join("") : ""; $id("c-code").value = "// --- WiseCode\u00ae ---\n" + (includes ? includes + "\n\n" : "") + "int main() {\n" + (typeDefs ? typeDefs + "\n\n" : "") + (unitDefs ? unitDefs + "\n\n" : "") + stringStruct + dataSection + body + freeSection + "return 0;\n" + "}\n// --- END ---"; } } // ═══════════════════════════════════════════════════════════════════════════════ // Block registry // ═══════════════════════════════════════════════════════════════════════════════ const BLOCK_REGISTRY = {}; const BLOCK_ACTIONS = {}; function registerBlock(tag, cls, prefix, creatable, getParent) { customElements.define(tag, cls); BLOCK_REGISTRY[tag] = { cls, template: "#" + tag, prefix: prefix || tag.replace("wc-", ""), getParent }; if (creatable) BLOCK_ACTIONS[creatable] = tag; } registerBlock("wc-start", WCStart, "start", null, null); registerBlock("wc-end", WCEnd, "end", null, null); registerBlock("wc-program", WCProgram, "program", null, null); registerBlock("wc-output", WCOutput, "output", "output", null); registerBlock("wc-decision", WCDecision, "decision", "decision", null); registerBlock("wc-boolean", WCBoolean, "bool", "boolean", () => $id("variable-area")); registerBlock("wc-scalar", WCScalar, "scalar", "scalar", () => $id("variable-area")); registerBlock("wc-string", WCString, "str", "string", () => $id("variable-area")); registerBlock("wc-array", WCArray, "arr", "array", () => $id("variable-area")); registerBlock("wc-varref", WCVarRef, "varref", "varref", null); registerBlock("wc-expression", WCExpression, "expr", "expression", null); registerBlock("wc-quantity", WCQuantity, "quantity", "quantity", () => $id("data-area")); registerBlock("wc-unit", WCUnit, "unit", "unit", () => $id("data-area")); registerBlock("wc-procedure", WCProcedure, "proc", null, null); registerBlock("wc-proccall", WCProcCall, "proccall", "proccall", null); registerBlock("wc-assign", WCAssign, "assign", "assign", null); const _connObserver = new MutationObserver(() => { for (const el of document.querySelectorAll("wc-assign")) el._updateValueVisibility(); }); const _connSvg = document.getElementById("connections"); if (_connSvg) _connObserver.observe(_connSvg, { childList: true, subtree: true });