// ── Procedure block definitions ──────────────────────────────────────────── // // Blocks that define and call procedures: // WCProcedure — procedure definition (lives in Procedures tab) // WCProcCall — procedure call block (lives in Algorithm tab) // // Each extends WCBlock (from wcblock.js). // // Load order dependencies: // wcblock.js → WCBlock base class // helpers.js → refreshProcOptions, findDataSource, resolveSourceName // dom.js → $id // draw.js → deleteLink // ── WCProcedure — procedure definition ───────────────────────────────────── class WCProcedure extends WCBlock { connectedCallback() { super.connectedCallback(); this._el.name = this.querySelector(".proc-name"); this._el.inputs = this.querySelector(".proc-inputs"); this._el.outputs = this.querySelector(".proc-outputs"); this.querySelector(".proc-add-input").addEventListener("click", () => { this._addParamRow(this._el.inputs, "in"); }); this.querySelector(".proc-add-output").addEventListener("click", () => { this._addParamRow(this._el.outputs, "out"); }); this._el.name.addEventListener("input", () => refreshProcOptions()); } _addParamRow(container, dir) { const row = document.createElement("div"); row.className = "proc-param-row"; row.innerHTML = `` + `` + ``; row.querySelector(".proc-param-del").addEventListener("click", () => { row.remove(); refreshProcOptions(); }); for (const inp of row.querySelectorAll("input")) { inp.addEventListener("input", () => refreshProcOptions()); } container.appendChild(row); refreshProcOptions(); } getLibrary() { const pane = this.closest(".lib-pane"); return pane ? pane.dataset.libName : "unknown"; } getQualifiedName() { return this.getLibrary() + "/" + (this._el.name.value || "unnamed"); } getInputParams() { return this._getParams(this._el.inputs); } getOutputParams() { return this._getParams(this._el.outputs); } _getParams(container) { return Array.from(container.querySelectorAll(".proc-param-row")).map(row => ({ name: row.querySelector(".proc-param-name").value || "arg", type: row.querySelector(".proc-param-type").value || "scalar" })); } saveData() { return { library: this.getLibrary(), name: this._el.name.value, inputs: this.getInputParams(), outputs: this.getOutputParams() }; } restoreData(d) { this._el.name.value = d.name || "proc"; for (const p of (d.inputs || [])) { this._addParamRow(this._el.inputs, "in"); const rows = this._el.inputs.querySelectorAll(".proc-param-row"); const last = rows[rows.length - 1]; last.querySelector(".proc-param-name").value = p.name || "arg"; last.querySelector(".proc-param-type").value = p.type || "scalar"; } for (const p of (d.outputs || [])) { this._addParamRow(this._el.outputs, "out"); const rows = this._el.outputs.querySelectorAll(".proc-param-row"); const last = rows[rows.length - 1]; last.querySelector(".proc-param-name").value = p.name || "arg"; last.querySelector(".proc-param-type").value = p.type || "scalar"; } } generateOwnCode() { return ""; } } // ── WCProcCall — procedure call block ────────────────────────────────────── class WCProcCall extends WCBlock { connectedCallback() { super.connectedCallback(); this._el.select = this.querySelector(".proccall-select"); this._el.nameInput = this.querySelector(".proccall-name"); this._el.inputs = this.querySelector(".proccall-inputs"); this._el.outputs = this.querySelector(".proccall-outputs"); this._el.select.addEventListener("change", () => this._rebuildParams()); this._initTimer = setTimeout(() => this._rebuildParams(), 0); } _rebuildParams() { const qname = this._el.select.dataset.restoredValue || this._el.select.value; for (const container of [this._el.inputs, this._el.outputs]) { for (const hook of container.querySelectorAll(".hook")) { const paths = [...document.querySelectorAll("path.flow, path.flow-data")]; for (const path of paths) { if (path.fromHook === hook || path.toHook === hook) { deleteLink(path); } } } } this._el.inputs.innerHTML = ""; this._el.outputs.innerHTML = ""; if (!qname) return; for (const proc of document.querySelectorAll("wc-procedure")) { if (proc.getQualifiedName() === qname) { for (const p of proc.getInputParams()) { this._createArgField(this._el.inputs, p, "in"); } for (const p of proc.getOutputParams()) { this._createArgField(this._el.outputs, p, "out"); } break; } } } _createArgField(container, param, dir) { const row = document.createElement("div"); row.className = "proccall-arg-row"; const label = document.createElement("span"); label.className = "proccall-arg-label"; label.textContent = param.name + ":"; const input = document.createElement("input"); input.className = "proccall-arg-input"; input.placeholder = param.name; input.dataset.dir = dir; input.dataset.paramName = param.name; input.dataset.paramType = param.type; const hook = document.createElement("button"); hook.className = "hook"; hook.dataset.direction = dir === "in" ? "in" : "out"; hook.dataset.linkType = "data"; hook.dataset.type = param.type; hook.dataset.exit = dir === "in" ? "left" : "right"; hook.title = (dir === "in" ? "expects " : "") + param.type; if (dir === "in") { row.appendChild(hook); row.appendChild(label); row.appendChild(input); } else { row.appendChild(label); row.appendChild(input); row.appendChild(hook); } container.appendChild(row); this._setupHook(hook); } _sourceName(block) { const name = resolveSourceName(block); if (!name) return ""; return block.dataType === "string" ? name + ".data" : name; } generateOwnCode() { const qname = this._el.select.value; if (!qname) return ""; const parts = qname.split("/"); const procName = parts[1] || qname; const inputs = []; const outputs = []; for (const row of this._el.inputs.querySelectorAll(".proccall-arg-row")) { const hook = row.querySelector(".hook"); const inp = row.querySelector(".proccall-arg-input"); const source = hook ? findDataSource(hook) : null; inputs.push(source ? this._sourceName(source) : (inp.value || inp.placeholder)); } for (const row of this._el.outputs.querySelectorAll(".proccall-arg-row")) { const inp = row.querySelector(".proccall-arg-input"); outputs.push("&" + (inp.value || inp.placeholder)); } const args = [...inputs, ...outputs].join(", "); return `\n${procName}(${args});\n`; } saveData() { const args = {}; for (const inp of this.querySelectorAll(".proccall-arg-input")) { args[inp.dataset.dir + ":" + inp.dataset.paramName] = inp.value; } return { selected: this._el.select.value, name: this._el.nameInput.value, args }; } restoreData(d) { if (this._initTimer) { clearTimeout(this._initTimer); this._initTimer = null; } this._el.select.dataset.restoredValue = d.selected || ""; this._el.select.value = d.selected || ""; this._el.nameInput.value = d.name || ""; this._rebuildParams(); if (d.args) { for (const inp of this.querySelectorAll(".proccall-arg-input")) { const key = inp.dataset.dir + ":" + inp.dataset.paramName; if (d.args[key] !== undefined) inp.value = d.args[key]; } } } }