Compare commits
1 Commits
d6f8af4395
...
fb8d5c03e6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb8d5c03e6 |
@@ -74,44 +74,50 @@
|
||||
oneditprepare: function() {
|
||||
const node = this;
|
||||
|
||||
// === Mode selector — TOP-LEVEL hierarchy ===
|
||||
// The Input Mode drives whether analog-pipeline fields or the
|
||||
// digital channels editor are shown. Initialize the <select> from
|
||||
// the saved node value, fall back to 'analog' for legacy nodes
|
||||
// that were saved before the mode field existed.
|
||||
const modeSelect = document.getElementById('node-input-mode');
|
||||
const initialMode = (node.mode === 'digital' || node.mode === 'analog') ? node.mode : 'analog';
|
||||
modeSelect.value = initialMode;
|
||||
// === Asset / logger / position placeholders (dynamic menus) ===
|
||||
// Kick these off FIRST so that any error in the downstream mode
|
||||
// logic can never block the shared menus. Historical regression:
|
||||
// a ReferenceError in the mode block aborted oneditprepare and
|
||||
// stopped the asset menu from rendering at all.
|
||||
const waitForMenuData = () => {
|
||||
if (window.EVOLV?.nodes?.measurement?.initEditor) {
|
||||
window.EVOLV.nodes.measurement.initEditor(node);
|
||||
} else {
|
||||
setTimeout(waitForMenuData, 50);
|
||||
}
|
||||
};
|
||||
waitForMenuData();
|
||||
|
||||
// IMPORTANT: all DOM references are resolved up front so helper
|
||||
// functions called during initial applyMode() don't trip over the
|
||||
// Temporal Dead Zone on later `const` declarations.
|
||||
|
||||
const modeSelect = document.getElementById('node-input-mode');
|
||||
const analogBlock = document.getElementById('analog-only-fields');
|
||||
const digitalBlock = document.getElementById('digital-only-fields');
|
||||
const modeHint = document.getElementById('mode-hint');
|
||||
|
||||
function applyMode(mode) {
|
||||
const isDigital = mode === 'digital';
|
||||
analogBlock.style.display = isDigital ? 'none' : 'block';
|
||||
digitalBlock.style.display = isDigital ? 'block' : 'none';
|
||||
if (modeHint) {
|
||||
modeHint.textContent = isDigital
|
||||
? 'msg.payload must be an OBJECT, e.g. {"temperature": 22.5, "humidity": 45}. Define each key below.'
|
||||
: 'msg.payload must be a NUMBER (or numeric string). Configure scaling/smoothing below.';
|
||||
}
|
||||
validateChannelsJson();
|
||||
}
|
||||
|
||||
modeSelect.addEventListener('change', (e) => applyMode(e.target.value));
|
||||
applyMode(initialMode);
|
||||
|
||||
// === Channels JSON live validation (digital only) ===
|
||||
const channelsArea = document.getElementById('node-input-channels');
|
||||
const channelsHint = document.getElementById('channels-validation');
|
||||
|
||||
// Initialise the mode <select> from the saved node.mode. Legacy
|
||||
// nodes (saved before the mode field existed) fall back to
|
||||
// 'analog' so they keep behaving exactly like before.
|
||||
const initialMode = (node.mode === 'digital' || node.mode === 'analog') ? node.mode : 'analog';
|
||||
if (modeSelect) modeSelect.value = initialMode;
|
||||
|
||||
// Populate the channels textarea from the saved node.channels
|
||||
// (stored as a raw JSON string; parsing happens server-side).
|
||||
if (channelsArea && typeof node.channels === 'string') {
|
||||
channelsArea.value = node.channels;
|
||||
}
|
||||
|
||||
function validateChannelsJson() {
|
||||
if (!channelsHint) return;
|
||||
if (modeSelect.value !== 'digital') { channelsHint.textContent = ''; return; }
|
||||
const raw = (channelsArea.value || '').trim();
|
||||
if (!modeSelect || modeSelect.value !== 'digital') {
|
||||
channelsHint.textContent = '';
|
||||
return;
|
||||
}
|
||||
const raw = (channelsArea && channelsArea.value || '').trim();
|
||||
if (!raw || raw === '[]') {
|
||||
channelsHint.innerHTML = '<span style="color:#b45309;">Digital mode with no channels — no measurements will be emitted.</span>';
|
||||
return;
|
||||
@@ -120,7 +126,7 @@
|
||||
const parsed = JSON.parse(raw);
|
||||
if (!Array.isArray(parsed)) throw new Error('must be an array');
|
||||
const missing = parsed
|
||||
.map((c, i) => (c && c.key && c.type ? null : `entry ${i}: missing key or type`))
|
||||
.map((c, i) => (c && c.key && c.type ? null : 'entry ' + i + ': missing key or type'))
|
||||
.filter(Boolean);
|
||||
if (missing.length) {
|
||||
channelsHint.innerHTML = '<span style="color:#b45309;">' + missing.join('; ') + '</span>';
|
||||
@@ -131,17 +137,24 @@
|
||||
channelsHint.innerHTML = '<span style="color:#b91c1c;">Invalid JSON: ' + e.message + '</span>';
|
||||
}
|
||||
}
|
||||
if (channelsArea) channelsArea.addEventListener('input', validateChannelsJson);
|
||||
|
||||
// === Asset / logger / position placeholders (dynamic menus) ===
|
||||
const waitForMenuData = () => {
|
||||
if (window.EVOLV?.nodes?.measurement?.initEditor) {
|
||||
window.EVOLV.nodes.measurement.initEditor(node);
|
||||
} else {
|
||||
setTimeout(waitForMenuData, 50);
|
||||
function applyMode(mode) {
|
||||
const isDigital = mode === 'digital';
|
||||
if (analogBlock) analogBlock.style.display = isDigital ? 'none' : 'block';
|
||||
if (digitalBlock) digitalBlock.style.display = isDigital ? 'block' : 'none';
|
||||
if (modeHint) {
|
||||
modeHint.textContent = isDigital
|
||||
? 'msg.payload must be an OBJECT, e.g. {"temperature": 22.5, "humidity": 45}. Define each key below.'
|
||||
: 'msg.payload must be a NUMBER (or numeric string). Configure scaling/smoothing below.';
|
||||
}
|
||||
validateChannelsJson();
|
||||
}
|
||||
|
||||
if (modeSelect) modeSelect.addEventListener('change', (e) => applyMode(e.target.value));
|
||||
if (channelsArea) channelsArea.addEventListener('input', validateChannelsJson);
|
||||
try { applyMode(initialMode); } catch (e) {
|
||||
console.error('measurement: applyMode failed', e);
|
||||
}
|
||||
};
|
||||
waitForMenuData();
|
||||
|
||||
// === Smoothing method dropdown (analog only) ===
|
||||
const smoothMethodSelect = document.getElementById('node-input-smooth_method');
|
||||
|
||||
Reference in New Issue
Block a user