Added helper functionality to abort movements in state class and safeguards to NOT be able to abort in protected states. some caps removal
275 lines
11 KiB
JavaScript
275 lines
11 KiB
JavaScript
|
|
class PhysicalPositionMenu {
|
|
|
|
// 1) Server-side: provide the option groups
|
|
getAllMenuData() {
|
|
return {
|
|
positionGroups: [
|
|
{ group: 'Positional', options: [
|
|
{ value: 'upstream', label: '← Upstream', icon: '←'},
|
|
{ value: 'atEquipment', label: '⊥ in place' , icon: '⊥' },
|
|
{ value: 'downstream', label: '→ Downstream' , icon: '→' }
|
|
]
|
|
}
|
|
],
|
|
// Distance contexts for each position
|
|
distanceContexts: {
|
|
upstream: {
|
|
description: 'Distance from parent inlet',
|
|
placeholder: 'e.g., 2.5 (meters before parent)',
|
|
helpText: 'How far upstream from the parent equipment'
|
|
},
|
|
downstream: {
|
|
description: 'Distance from parent outlet',
|
|
placeholder: 'e.g., 3.0 (meters after parent)',
|
|
helpText: 'How far downstream from the parent equipment'
|
|
},
|
|
atEquipment: {
|
|
description: 'Distance from parent start',
|
|
placeholder: 'e.g., 1.2 (meters from start)',
|
|
helpText: 'Position within the parent equipment boundaries'
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
// 2) HTML template (pure markup)
|
|
getHtmlTemplate() {
|
|
return `
|
|
<hr />
|
|
<h3>Physical Position vs parent</h3>
|
|
<div class="form-row">
|
|
<label for="node-input-positionVsParent"><i class="fa fa-map-marker"></i>Position</label>
|
|
<select id="node-input-positionVsParent" style="width:70%;">
|
|
<!-- optgroups will be injected -->
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Distance section -->
|
|
<div class="form-row">
|
|
<label> </label>
|
|
<input type="checkbox" id="node-input-hasDistance" style="display:inline-block; width:auto; margin-right:5px;">
|
|
<label for="node-input-hasDistance" style="width:auto;">Specify 1D Distance</label>
|
|
</div>
|
|
|
|
<div id="distance-section" class="form-row" style="display:none;">
|
|
<label for="node-input-distance"><i class="fa fa-ruler"></i>Distance</label>
|
|
<div style="display:flex; align-items:center; width:70%;">
|
|
<input type="number" id="node-input-distance" step="0.1" min="0" style="width:60%;" placeholder="0.0">
|
|
<span style="margin-left:5px; margin-right:5px;">meters</span>
|
|
</div>
|
|
<div id="distance-help" class="form-tips" style="margin-left:105px; font-size:11px; color:#666;">
|
|
Select a position to see distance context
|
|
</div>
|
|
</div>
|
|
<hr />
|
|
`;
|
|
}
|
|
|
|
// 3) HTML injector
|
|
getHtmlInjectionCode(nodeName) {
|
|
const tpl = this.getHtmlTemplate()
|
|
.replace(/`/g,'\\`').replace(/\$/g,'\\$');
|
|
return `
|
|
// PhysicalPosition HTML injection for ${nodeName}
|
|
window.EVOLV.nodes.${nodeName}.positionMenu.injectHtml = function() {
|
|
const ph = document.getElementById('position-fields-placeholder');
|
|
if (ph && !ph.hasChildNodes()) {
|
|
ph.innerHTML = \`${tpl}\`;
|
|
}
|
|
};
|
|
`;
|
|
}
|
|
|
|
// 4) Data-loader injector
|
|
getDataInjectionCode(nodeName) {
|
|
return `
|
|
// PhysicalPosition data loader for ${nodeName}
|
|
window.EVOLV.nodes.${nodeName}.positionMenu.loadData = function(node) {
|
|
const data = window.EVOLV.nodes.${nodeName}.menuData.position;
|
|
const sel = document.getElementById('node-input-positionVsParent');
|
|
const hasDistanceCheck = document.getElementById('node-input-hasDistance');
|
|
const distanceInput = document.getElementById('node-input-distance');
|
|
const distanceSection = document.getElementById('distance-section');
|
|
|
|
//Load position options
|
|
if (sel) {
|
|
sel.innerHTML = '';
|
|
(data.positionGroups||[]).forEach(grp => {
|
|
const optg = document.createElement('optgroup');
|
|
optg.label = grp.group;
|
|
grp.options.forEach(o=>{
|
|
const opt = document.createElement('option');
|
|
opt.value = o.value;
|
|
opt.textContent = o.label;
|
|
opt.setAttribute('data-icon', o.icon);
|
|
optg.appendChild(opt);
|
|
});
|
|
sel.appendChild(optg);
|
|
});
|
|
sel.value = node.positionVsParent || 'atEquipment';
|
|
}
|
|
|
|
//Load distance values
|
|
if (hasDistanceCheck) {
|
|
hasDistanceCheck.checked = node.hasDistance || false;
|
|
distanceSection.style.display = hasDistanceCheck.checked ? 'block' : 'none';
|
|
}
|
|
|
|
if (distanceInput) {
|
|
distanceInput.value = node.distance || '';
|
|
}
|
|
|
|
// Update distance context for current position
|
|
this.updateDistanceContext(node.positionVsParent || 'atEquipment', data.distanceContexts);
|
|
};
|
|
`;
|
|
}
|
|
|
|
// 5) (no special events needed, but stub for symmetry)
|
|
getEventInjectionCode(nodeName) {
|
|
return `
|
|
// PhysicalPosition events for ${nodeName}
|
|
window.EVOLV.nodes.${nodeName}.positionMenu.wireEvents = function(node) {
|
|
const positionSel = document.getElementById('node-input-positionVsParent');
|
|
const hasDistanceCheck = document.getElementById('node-input-hasDistance');
|
|
const distanceSection = document.getElementById('distance-section');
|
|
const data = window.EVOLV.nodes.${nodeName}.menuData.position;
|
|
|
|
// Toggle distance section visibility
|
|
if (hasDistanceCheck && distanceSection) {
|
|
hasDistanceCheck.addEventListener('change', function() {
|
|
distanceSection.style.display = this.checked ? 'block' : 'none';
|
|
|
|
// Clear distance if unchecked
|
|
if (!this.checked) {
|
|
const distanceInput = document.getElementById('node-input-distance');
|
|
if (distanceInput) {
|
|
distanceInput.value = '';
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Update distance context when position changes
|
|
if (positionSel) {
|
|
positionSel.addEventListener('change', function() {
|
|
const position = this.value;
|
|
window.EVOLV.nodes.${nodeName}.positionMenu.updateDistanceContext(position, data.distanceContexts);
|
|
});
|
|
}
|
|
};
|
|
|
|
// Helper function to update distance context
|
|
window.EVOLV.nodes.${nodeName}.positionMenu.updateDistanceContext = function(position, contexts) {
|
|
const distanceInput = document.getElementById('node-input-distance');
|
|
const distanceHelp = document.getElementById('distance-help');
|
|
|
|
const context = contexts && contexts[position];
|
|
|
|
if (context && distanceInput && distanceHelp) {
|
|
distanceInput.placeholder = context.placeholder || '0.0';
|
|
distanceHelp.textContent = context.helpText || 'Enter distance in meters';
|
|
}
|
|
};
|
|
`;
|
|
}
|
|
|
|
// 6) Save-logic injector
|
|
getSaveInjectionCode(nodeName) {
|
|
return `
|
|
// PhysicalPosition Save injection for ${nodeName}
|
|
window.EVOLV.nodes.${nodeName}.positionMenu.saveEditor = function(node) {
|
|
console.log("=== PhysicalPosition Save Debug ===");
|
|
|
|
const sel = document.getElementById('node-input-positionVsParent');
|
|
const hasDistanceCheck = document.getElementById('node-input-hasDistance');
|
|
const distanceInput = document.getElementById('node-input-distance');
|
|
|
|
console.log("→ sel element found:", !!sel);
|
|
console.log("→ sel:", sel);
|
|
console.log("→ sel.value:", sel ? sel.value : "NO ELEMENT");
|
|
console.log("→ sel.selectedIndex:", sel ? sel.selectedIndex : "NO ELEMENT");
|
|
console.log("→ sel.options:", sel ? Array.from(sel.options).map(o => ({value: o.value, text: o.textContent})) : "NO OPTIONS");
|
|
|
|
if (!sel) {
|
|
console.error("→ positionMenu.saveEditor FAILED: select element not found!");
|
|
return false;
|
|
}
|
|
|
|
// Save existing position data
|
|
const positionValue = sel.value;
|
|
const selectedOption = sel.options[sel.selectedIndex];
|
|
|
|
console.log("→ positionValue:", positionValue);
|
|
console.log("→ selectedOption:", selectedOption);
|
|
console.log("→ selectedOption.textContent:", selectedOption ? selectedOption.textContent : "NO OPTION");
|
|
console.log("→ selectedOption data-icon:", selectedOption ? selectedOption.getAttribute('data-icon') : "NO ICON");
|
|
|
|
node.positionVsParent = positionValue || 'atEquipment';
|
|
node.positionLabel = selectedOption ? selectedOption.textContent : 'At Equipment';
|
|
node.positionIcon = selectedOption ? selectedOption.getAttribute('data-icon') : 'fa fa-cog';
|
|
|
|
console.log("→ node.positionVsParent set to:", node.positionVsParent);
|
|
console.log("→ node.positionLabel set to:", node.positionLabel);
|
|
|
|
// Save distance data
|
|
console.log("→ hasDistanceCheck found:", !!hasDistanceCheck);
|
|
console.log("→ hasDistanceCheck.checked:", hasDistanceCheck ? hasDistanceCheck.checked : "NO ELEMENT");
|
|
|
|
node.hasDistance = hasDistanceCheck ? hasDistanceCheck.checked : false;
|
|
|
|
if (node.hasDistance && distanceInput && distanceInput.value) {
|
|
console.log("→ distanceInput.value:", distanceInput.value);
|
|
node.distance = parseFloat(distanceInput.value) || 0;
|
|
node.distanceUnit = 'm'; // Fixed to meters for now
|
|
|
|
// Generate distance description based on position
|
|
const contexts = window.EVOLV.nodes.${nodeName}.menuData.position.distanceContexts;
|
|
const context = contexts && contexts[node.positionVsParent];
|
|
node.distanceDescription = context ? context.description : 'Distance from parent';
|
|
|
|
console.log("→ distance set to:", node.distance);
|
|
} else {
|
|
console.log("→ clearing distance data");
|
|
delete node.distance;
|
|
delete node.distanceUnit;
|
|
delete node.distanceDescription;
|
|
}
|
|
|
|
console.log("→ positionMenu.saveEditor result: SUCCESS");
|
|
console.log("→ final node.positionVsParent:", node.positionVsParent);
|
|
|
|
return true;
|
|
};
|
|
`;
|
|
}
|
|
|
|
// 7) Compose everything into one client bundle
|
|
getClientInitCode(nodeName) {
|
|
const htmlCode = this.getHtmlInjectionCode(nodeName);
|
|
const dataCode = this.getDataInjectionCode(nodeName);
|
|
const eventCode = this.getEventInjectionCode(nodeName);
|
|
const saveCode = this.getSaveInjectionCode(nodeName);
|
|
|
|
return `
|
|
// --- PhysicalPositionMenu for ${nodeName} ---
|
|
window.EVOLV.nodes.${nodeName}.positionMenu =
|
|
window.EVOLV.nodes.${nodeName}.positionMenu || {};
|
|
|
|
${htmlCode}
|
|
${dataCode}
|
|
${eventCode}
|
|
${saveCode}
|
|
|
|
// hook into oneditprepare
|
|
window.EVOLV.nodes.${nodeName}.positionMenu.initEditor = function(node) {
|
|
this.injectHtml();
|
|
this.loadData(node);
|
|
this.wireEvents(node);
|
|
};
|
|
`;
|
|
}
|
|
}
|
|
|
|
module.exports = PhysicalPositionMenu; |