Skip to content

Commit ec462d9

Browse files
committed
Isolate each node's editor script in an IIFE
The generated editor scripts declared their definition object and helper functions at the top level. Since Node-RED evaluates every node's editor script in the same global scope, the second node's `const def` threw "Identifier 'def' has already been declared", which aborted that script and left the node missing from the palette — only the first node registered. Wrap each init script in an IIFE so its declarations stay local.
1 parent 857e50f commit ec462d9

5 files changed

Lines changed: 252 additions & 241 deletions

File tree

README.md

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,17 @@ This node can be used to read one or multiple universes sent by sACN.
3030

3131
#### Parameters:
3232

33-
| Parameter | Description | Possible Values | Default Value | Mandatory |
34-
| ---------- | ----------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | ---------------------------- | --------- |
35-
| universe | The universe that is meant to be observed. | `\d+` (`1` to `63999`) | `1` | yes |
36-
| mode | Defines whether the node returns the values of every read sACN package (passthrough mode), or merged values using HTP or LTP. | `passthrough`, `htp`, `ltp` | `htp` | yes |
37-
| output | Defines whether the node sends only changed values or the whole universe. | `full`, `changes` | `full` | yes |
38-
| trigger | Controls when a message is emitted: only on change, on every received packet, or on change plus a cyclic keepalive re-emit. | `changes`, `always`, `interval` | `changes` | yes |
39-
| interval | Keepalive interval in milliseconds for the `interval` trigger; the full universe is re-emitted when no change arrives in time. | `\d+` | `1000` | no |
40-
| clearOnUniverseChange | When the observed universe is switched at runtime, emit a full universe of zeros until real data for the new universe arrives. | `true`, `false` | `false` | no |
41-
| values | Emit channel values as a percentage (0–100) or as raw DMX values (0–255). | `percent`, `absolute` | `percent` | yes |
42-
| IP-address | IP-Address of the network-interface that should be used for reading from sACN. | `\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\` (_any valid ip-address_) | _empty_ | no |
43-
| port | The network port which should be used for reading sACN. | `\d+` | _empty_ (defaults to `5568`) | no |
33+
| Parameter | Description | Possible Values | Default Value | Mandatory |
34+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------- | ---------------------------- | --------- |
35+
| universe | The universe that is meant to be observed. | `\d+` (`1` to `63999`) | `1` | yes |
36+
| mode | Defines whether the node returns the values of every read sACN package (passthrough mode), or merged values using HTP or LTP. | `passthrough`, `htp`, `ltp` | `htp` | yes |
37+
| output | Defines whether the node sends only changed values or the whole universe. | `full`, `changes` | `full` | yes |
38+
| trigger | Controls when a message is emitted: only on change, on every received packet, or on change plus a cyclic keepalive re-emit. | `changes`, `always`, `interval` | `changes` | yes |
39+
| interval | Keepalive interval in milliseconds for the `interval` trigger; the full universe is re-emitted when no change arrives in time. | `\d+` | `1000` | no |
40+
| clearOnUniverseChange | When the observed universe is switched at runtime, emit a full universe of zeros until real data for the new universe arrives. | `true`, `false` | `false` | no |
41+
| values | Emit channel values as a percentage (0–100) or as raw DMX values (0–255). | `percent`, `absolute` | `percent` | yes |
42+
| IP-address | IP-Address of the network-interface that should be used for reading from sACN. | `\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\` (_any valid ip-address_) | _empty_ | no |
43+
| port | The network port which should be used for reading sACN. | `\d+` | _empty_ (defaults to `5568`) | no |
4444

4545
#### Input:
4646

@@ -69,16 +69,16 @@ This node can be used to send one universe using sACN.
6969

7070
#### Parameters:
7171

72-
| Parameter | Description | Possible Values | Default Value | Mandatory |
73-
| ----------- | ------------------------------------------------------------------------- | -------------------------------------------------------------- | ---------------------------- | --------- |
74-
| universe | The universe that is meant to be observed. | `\d+` (`1` to `63999`) | `1` | yes |
75-
| source-name | The name for the sACN-sender that should be displayed within the network. | _any string below 50 characters_ | `Node-RED` | yes |
76-
| speed | Defines the frequency for sending sACN-packages | `once (0Hz)`, `24Hz`, `27Hz`, `30Hz`, `40Hz`, `44Hz` | `0Hz` | yes |
77-
| priority | The priority that should be used for the sACN-sender. | `\d+` (`1` to `200`) | `100` | yes |
78-
| values | Interpret the payload as a percentage (0–100) or as raw DMX values (0–255). | `percent`, `absolute` | `percent` | yes |
79-
| blank-on-stop | Send an all-zero frame before closing so receivers go dark on stop. | `true`, `false` | `false` | no |
80-
| IP-address | IP-Address of the network-interface that should be used for sending sACN. | `\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\` (_any valid ip-address_) | _empty_ | no |
81-
| port | The network port which should be used for reading sACN. | `\d+` | _empty_ (defaults to `5568`) | no |
72+
| Parameter | Description | Possible Values | Default Value | Mandatory |
73+
| ------------- | --------------------------------------------------------------------------- | -------------------------------------------------------------- | ---------------------------- | --------- |
74+
| universe | The universe that is meant to be observed. | `\d+` (`1` to `63999`) | `1` | yes |
75+
| source-name | The name for the sACN-sender that should be displayed within the network. | _any string below 50 characters_ | `Node-RED` | yes |
76+
| speed | Defines the frequency for sending sACN-packages | `once (0Hz)`, `24Hz`, `27Hz`, `30Hz`, `40Hz`, `44Hz` | `0Hz` | yes |
77+
| priority | The priority that should be used for the sACN-sender. | `\d+` (`1` to `200`) | `100` | yes |
78+
| values | Interpret the payload as a percentage (0–100) or as raw DMX values (0–255). | `percent`, `absolute` | `percent` | yes |
79+
| blank-on-stop | Send an all-zero frame before closing so receivers go dark on stop. | `true`, `false` | `false` | no |
80+
| IP-address | IP-Address of the network-interface that should be used for sending sACN. | `\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\` (_any valid ip-address_) | _empty_ | no |
81+
| port | The network port which should be used for reading sACN. | `\d+` | _empty_ (defaults to `5568`) | no |
8282

8383
#### Expected input:
8484

build.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,16 @@ function buildNodeForm(node, source, target) {
5656

5757
const initJSLines = initJS.split("\n");
5858
html.push('<script type="text/javascript">');
59+
// wrap in an IIFE so top-level declarations (e.g. `const def`, helper functions)
60+
// stay local; multiple node editor scripts share one global scope and would
61+
// otherwise collide ("Identifier 'def' has already been declared").
62+
html.push(" (function () {");
5963
initJSLines.forEach((line) => {
6064
if (line.length > 0) {
61-
html.push(` ${line}`);
65+
html.push(` ${line}`);
6266
}
6367
});
68+
html.push(" })();");
6469
html.push("</script>");
6570

6671
html.push("");

dist/nodes/sacn-in/sacn-in.html

Lines changed: 107 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -172,121 +172,123 @@ <h3>References</h3>
172172
</ul>
173173
</script>
174174
<script type="text/javascript">
175-
function populateInterfaceOptions() {
176-
const list = document.getElementById("node-input-interface-options");
177-
if (!list) {
178-
return;
179-
}
180-
$.getJSON("sacn/interfaces", (interfaces) => {
181-
list.replaceChildren();
182-
interfaces.forEach((iface) => {
183-
const option = document.createElement("option");
184-
option.value = iface.address;
185-
option.label = `${iface.name} (${iface.address})`;
186-
list.appendChild(option);
175+
(function () {
176+
function populateInterfaceOptions() {
177+
const list = document.getElementById("node-input-interface-options");
178+
if (!list) {
179+
return;
180+
}
181+
$.getJSON("sacn/interfaces", (interfaces) => {
182+
list.replaceChildren();
183+
interfaces.forEach((iface) => {
184+
const option = document.createElement("option");
185+
option.value = iface.address;
186+
option.label = `${iface.name} (${iface.address})`;
187+
list.appendChild(option);
188+
});
187189
});
188-
});
189-
}
190-
function toInt(value) {
191-
if (typeof value === "number") {
192-
return value;
193190
}
194-
if (typeof value === "string") {
195-
return parseInt(value, 10);
191+
function toInt(value) {
192+
if (typeof value === "number") {
193+
return value;
194+
}
195+
if (typeof value === "string") {
196+
return parseInt(value, 10);
197+
}
198+
return NaN;
196199
}
197-
return NaN;
198-
}
199-
function isValidInterface(value) {
200-
if (typeof value !== "string" || value === "") {
201-
return true;
200+
function isValidInterface(value) {
201+
if (typeof value !== "string" || value === "") {
202+
return true;
203+
}
204+
return /^(\d{1,3}\.){3}\d{1,3}$/.test(value);
202205
}
203-
return /^(\d{1,3}\.){3}\d{1,3}$/.test(value);
204-
}
205-
function isValidUniverse(value) {
206-
const n = toInt(value);
207-
return !isNaN(n) && n >= 1 && n <= 63999;
208-
}
209-
function isValidPort(value) {
210-
if (value === undefined || value === null || value === "") {
211-
return true;
206+
function isValidUniverse(value) {
207+
const n = toInt(value);
208+
return !isNaN(n) && n >= 1 && n <= 63999;
212209
}
213-
const n = toInt(value);
214-
return !isNaN(n) && n >= 1 && n <= 65535;
215-
}
216-
const def = {
217-
category: "sACN",
218-
color: "#dcc515",
219-
defaults: {
220-
name: {
221-
value: "",
222-
},
223-
universe: {
224-
value: 1,
225-
required: true,
226-
validate: function (v) {
227-
return isValidUniverse(v);
210+
function isValidPort(value) {
211+
if (value === undefined || value === null || value === "") {
212+
return true;
213+
}
214+
const n = toInt(value);
215+
return !isNaN(n) && n >= 1 && n <= 65535;
216+
}
217+
const def = {
218+
category: "sACN",
219+
color: "#dcc515",
220+
defaults: {
221+
name: {
222+
value: "",
228223
},
229-
},
230-
port: {
231-
value: undefined,
232-
required: false,
233-
validate: function (v) {
234-
return isValidPort(v);
224+
universe: {
225+
value: 1,
226+
required: true,
227+
validate: function (v) {
228+
return isValidUniverse(v);
229+
},
235230
},
236-
},
237-
interface: {
238-
value: "",
239-
required: false,
240-
validate: function (v) {
241-
return isValidInterface(v);
231+
port: {
232+
value: undefined,
233+
required: false,
234+
validate: function (v) {
235+
return isValidPort(v);
236+
},
237+
},
238+
interface: {
239+
value: "",
240+
required: false,
241+
validate: function (v) {
242+
return isValidInterface(v);
243+
},
244+
},
245+
mode: {
246+
value: "htp",
247+
required: true,
248+
},
249+
output: {
250+
value: "full",
251+
required: true,
252+
},
253+
trigger: {
254+
value: "changes",
255+
required: true,
256+
},
257+
interval: {
258+
value: 1000,
259+
required: true,
260+
},
261+
clearOnUniverseChange: {
262+
value: false,
263+
},
264+
values: {
265+
value: "percent",
266+
required: true,
242267
},
243268
},
244-
mode: {
245-
value: "htp",
246-
required: true,
247-
},
248-
output: {
249-
value: "full",
250-
required: true,
251-
},
252-
trigger: {
253-
value: "changes",
254-
required: true,
255-
},
256-
interval: {
257-
value: 1000,
258-
required: true,
269+
inputs: 1,
270+
outputs: 1,
271+
paletteLabel: "sACN in",
272+
icon: "sacn-in.svg",
273+
label: function () {
274+
return this.name || `sACN in · U${this.universe}`;
259275
},
260-
clearOnUniverseChange: {
261-
value: false,
276+
labelStyle: function () {
277+
return this.name ? "node_label_italic" : "";
262278
},
263-
values: {
264-
value: "percent",
265-
required: true,
279+
oneditprepare: function () {
280+
const select = document.getElementById("node-input-trigger");
281+
const row = document.getElementById("row-interval");
282+
const toggleInterval = () => {
283+
if (row) {
284+
row.style.display = select?.value === "interval" ? "" : "none";
285+
}
286+
};
287+
select?.addEventListener("change", toggleInterval);
288+
toggleInterval();
289+
populateInterfaceOptions();
266290
},
267-
},
268-
inputs: 1,
269-
outputs: 1,
270-
paletteLabel: "sACN in",
271-
icon: "sacn-in.svg",
272-
label: function () {
273-
return this.name || `sACN in · U${this.universe}`;
274-
},
275-
labelStyle: function () {
276-
return this.name ? "node_label_italic" : "";
277-
},
278-
oneditprepare: function () {
279-
const select = document.getElementById("node-input-trigger");
280-
const row = document.getElementById("row-interval");
281-
const toggleInterval = () => {
282-
if (row) {
283-
row.style.display = select?.value === "interval" ? "" : "none";
284-
}
285-
};
286-
select?.addEventListener("change", toggleInterval);
287-
toggleInterval();
288-
populateInterfaceOptions();
289-
},
290-
};
291-
RED.nodes.registerType("sacn-in", def);
291+
};
292+
RED.nodes.registerType("sacn-in", def);
293+
})();
292294
</script>

0 commit comments

Comments
 (0)