// PumpingStation editor — dynamic input bounds. // Sets HTML5 min/max attributes on every level and percent input based on // the current values of related inputs, so the up/down arrows stop at // values that respect the basin hierarchy: // // 0 < outflowLevel < dryRunLevel < startLevel ≤ inflowLevel // ≤ shiftLevel ≤ maxLevel ≤ overflowLevel ≤ basinHeight // // The user can still type out-of-range values via the keyboard (HTML5 // min/max only constrain the spinner). The validation ribbons in // basin-diagram.js and mode-preview.js catch typed violations and the // oneditsave handler blocks Deploy until they're resolved. (function () { const ns = window.PSEditor = window.PSEditor || {}; const fNum = (id) => ns.fNum(id); const EPS = 0.001; // smallest meaningful step (mm-precision) const setBounds = (id, min, max) => { const el = document.getElementById(`node-input-${id}`); if (!el) return; if (Number.isFinite(min)) el.setAttribute('min', String(min)); else el.removeAttribute('min'); if (Number.isFinite(max)) el.setAttribute('max', String(max)); else el.removeAttribute('max'); }; ns.bounds = { apply() { const basinHeight = fNum('basinHeight'); const outflow = fNum('outflowLevel'); const dryPct = fNum('dryRunThresholdPercent'); const start = fNum('startLevel'); const inlet = fNum('inflowLevel'); const max = fNum('maxLevel'); const overflow = fNum('overflowLevel'); const shiftEnabled = !!document.getElementById('node-input-enableShiftedRamp')?.checked; // Derived dryRunLevel (lower bound for startLevel). const dryRun = (Number.isFinite(outflow) && Number.isFinite(dryPct)) ? outflow * (1 + dryPct / 100) : null; // Geometry — basin envelope. setBounds('basinHeight', EPS, undefined); setBounds('basinVolume', EPS, undefined); // Levels (each capped by the next-higher level if defined). setBounds('outflowLevel', EPS, Number.isFinite(start) && Number.isFinite(dryPct) ? start / (1 + dryPct / 100) - EPS // keep dryRun < start : (start ?? inlet ?? max ?? overflow ?? basinHeight)); setBounds('startLevel', Number.isFinite(dryRun) ? dryRun + EPS : EPS, inlet ?? max ?? overflow ?? basinHeight); setBounds('inflowLevel', start ?? EPS, max ?? overflow ?? basinHeight); setBounds('maxLevel', inlet ?? start ?? EPS, overflow ?? basinHeight); setBounds('overflowLevel', max ?? inlet ?? start ?? EPS, basinHeight); // Shift inputs (only relevant when shifted ramp enabled). if (shiftEnabled) { setBounds('shiftLevel', Number.isFinite(start) ? start : EPS, max ?? overflow ?? basinHeight); setBounds('shiftArmPercent', 1, 100); } // Percentages. // dryRun% capped so dryRunLevel ≤ startLevel. let dryMax = 99; if (Number.isFinite(start) && Number.isFinite(outflow) && outflow > 0) { dryMax = Math.max(0, Math.min(99, ((start / outflow) - 1) * 100)); } setBounds('dryRunThresholdPercent', 0, dryMax); // highVol% bounded (1, 100). Equal to 100 means no margin to overflow. setBounds('highVolumeSafetyThresholdPercent', 1, 100); }, }; })();