const AssetMenu = require('./asset.js'); const { TagcodeApp, DynamicAssetMenu } = require('./tagcodeApp.js'); const LoggerMenu = require('./logger.js'); const PhysicalPositionMenu = require('./physicalPosition.js'); const ConfigManager = require('../configs'); class MenuManager { constructor() { this.registeredMenus = new Map(); this.configManager = new ConfigManager('../configs'); // Register factory functions this.registerMenu('asset', (nodeName) => new AssetMenu({ softwareType: this._getSoftwareType(nodeName) })); // static menu to be replaced by dynamic one but later //this.registerMenu('asset', (nodeName) => new DynamicAssetMenu(nodeName, new TagcodeApp())); this.registerMenu('logger', () => new LoggerMenu()); this.registerMenu('position', () => new PhysicalPositionMenu()); } /** * Register a menu type with its handler factory function * @param {string} menuType - The type of menu (e.g., 'asset', 'logging') * @param {function} menuFactory - The menu factory function */ registerMenu(menuType, menuFactory) { this.registeredMenus.set(menuType, menuFactory); } _getSoftwareType(nodeName) { if (!nodeName) { return null; } try { const config = this.configManager.getConfig(nodeName); return config?.functionality?.softwareType || nodeName; } catch (error) { console.warn(`Unable to determine softwareType for ${nodeName}: ${error.message}`); return nodeName; } } /** * Create a complete endpoint script with data and initialization functions * @param {string} nodeName - The name of the node type * @param {Array} menuTypes - Array of menu types to include * @returns {string} Complete JavaScript code to serve */ createEndpoint(nodeName, menuTypes) { try { // ✅ Create instances using factory functions with proper error handling const instantiatedMenus = new Map(); menuTypes.forEach(menuType => { try { const factory = this.registeredMenus.get(menuType); if (typeof factory === 'function') { const instance = factory(nodeName); instantiatedMenus.set(menuType, instance); } else { console.warn(`No factory function found for menu type: ${menuType}`); } } catch (error) { console.error(`Error creating instance for ${menuType}:`, error); } }); // ✅ Collect all menu data with error handling const menuData = {}; menuTypes.forEach(menuType => { try { const handler = instantiatedMenus.get(menuType); if (handler && typeof handler.getAllMenuData === 'function') { menuData[menuType] = handler.getAllMenuData(nodeName); } else { // Provide default empty data if method doesn't exist menuData[menuType] = {}; } } catch (error) { console.error(`Error getting menu data for ${menuType}:`, error); menuData[menuType] = {}; } }); // ✅ Generate HTML injection code with error handling const htmlInjections = menuTypes.map(type => { try { const menu = instantiatedMenus.get(type); if (menu && typeof menu.getHtmlInjectionCode === 'function') { return menu.getHtmlInjectionCode(nodeName); } return ''; } catch (error) { console.error(`Error generating HTML injection for ${type}:`, error); return `// Error generating HTML injection for ${type}: ${error.message}`; } }).join('\n'); // ✅ Collect all client initialization code with error handling const initFunctions = []; menuTypes.forEach(menuType => { try { const handler = instantiatedMenus.get(menuType); if (handler && typeof handler.getClientInitCode === 'function') { initFunctions.push(handler.getClientInitCode(nodeName)); } } catch (error) { console.error(`Error generating init code for ${menuType}:`, error); initFunctions.push(`// Error in ${menuType} initialization: ${error.message}`); } }); // Convert menu data to JSON const menuDataJSON = JSON.stringify(menuData, null, 2); // ✅ Assemble the complete script with comprehensive error handling return ` try { // Create the namespace structure with safety checks window.EVOLV = window.EVOLV || {}; window.EVOLV.nodes = window.EVOLV.nodes || {}; window.EVOLV.nodes.${nodeName} = window.EVOLV.nodes.${nodeName} || {}; // Initialize menu namespaces ${menuTypes.map(type => `window.EVOLV.nodes.${nodeName}.${type}Menu = window.EVOLV.nodes.${nodeName}.${type}Menu || {};`).join('\n ')} // Inject the pre-loaded menu data directly into the namespace window.EVOLV.nodes.${nodeName}.menuData = ${menuDataJSON}; // HTML injections with error handling try { ${htmlInjections} } catch (htmlError) { console.error('Error in HTML injections for ${nodeName}:', htmlError); } // Initialize functions with error handling try { ${initFunctions.join('\n\n ')} } catch (initError) { console.error('Error in initialization functions for ${nodeName}:', initError); } // Main initialization function that calls all menu initializers window.EVOLV.nodes.${nodeName}.initEditor = function(node) { try { ${menuTypes.map(type => ` try { if (window.EVOLV.nodes.${nodeName}.${type}Menu && window.EVOLV.nodes.${nodeName}.${type}Menu.initEditor) { window.EVOLV.nodes.${nodeName}.${type}Menu.initEditor(node); } } catch (${type}Error) { console.error('Error initializing ${type} menu for ${nodeName}:', ${type}Error); }`).join('')} } catch (editorError) { console.error('Error in main editor initialization for ${nodeName}:', editorError); } }; console.log('${nodeName} menu data and initializers loaded for: ${menuTypes.join(', ')}'); } catch (globalError) { console.error('Critical error in ${nodeName} menu initialization:', globalError); // Fallback initialization window.EVOLV = window.EVOLV || {}; window.EVOLV.nodes = window.EVOLV.nodes || {}; window.EVOLV.nodes.${nodeName} = window.EVOLV.nodes.${nodeName} || {}; window.EVOLV.nodes.${nodeName}.initEditor = function(node) { console.warn('Using fallback editor initialization for ${nodeName}'); }; } `; } catch (error) { console.error(`Critical error creating endpoint for ${nodeName}:`, error); // Return minimal fallback script return ` window.EVOLV = window.EVOLV || {}; window.EVOLV.nodes = window.EVOLV.nodes || {}; window.EVOLV.nodes.${nodeName} = window.EVOLV.nodes.${nodeName} || {}; window.EVOLV.nodes.${nodeName}.initEditor = function(node) { console.error('Menu system failed to initialize for ${nodeName}'); }; console.error('Menu system failed for ${nodeName}:', '${error.message}'); `; } } } module.exports = MenuManager;