Files
avcf/blocks-proc.js
T

238 lines
8.0 KiB
JavaScript

// ── 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 =
`<input class="proc-param-name" placeholder="name" value="">` +
`<input class="proc-param-type" placeholder="type" value="scalar">` +
`<button class="proc-param-del" title="remove">&times;</button>`;
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];
}
}
}
}