/** * Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. **/ //var UglifyJS = require("uglify-js"); var path = require("path"); var fs = require("fs"); var library = require("./library"); var events; var settings; var loader; var nodeConfigCache = null; var moduleConfigs = {}; var nodeList = []; var nodeConstructors = {}; var nodeTypeToId = {}; var moduleNodes = {}; function init(_settings,_loader, _events) { settings = _settings; loader = _loader; events = _events; moduleNodes = {}; nodeTypeToId = {}; nodeConstructors = {}; nodeList = []; nodeConfigCache = null; } function load() { if (settings.available()) { moduleConfigs = loadNodeConfigs(); } else { moduleConfigs = {}; } } function filterNodeInfo(n) { var r = { id: n.id||n.module+"/"+n.name, name: n.name, types: n.types, enabled: n.enabled, local: n.local||false }; if (n.hasOwnProperty("module")) { r.module = n.module; } if (n.hasOwnProperty("err")) { r.err = n.err; } return r; } function getModule(id) { var parts = id.split("/"); return parts.slice(0,parts.length-1).join("/"); } function getNode(id) { var parts = id.split("/"); return parts[parts.length-1]; } function saveNodeList() { var moduleList = {}; var hadPending = false; var hasPending = false; for (var module in moduleConfigs) { /* istanbul ignore else */ if (moduleConfigs.hasOwnProperty(module)) { if (Object.keys(moduleConfigs[module].nodes).length > 0) { if (!moduleList[module]) { moduleList[module] = { name: module, version: moduleConfigs[module].version, local: moduleConfigs[module].local||false, nodes: {} }; if (moduleConfigs[module].hasOwnProperty('pending_version')) { hadPending = true; if (moduleConfigs[module].pending_version !== moduleConfigs[module].version) { moduleList[module].pending_version = moduleConfigs[module].pending_version; hasPending = true; } else { delete moduleConfigs[module].pending_version; } } } var nodes = moduleConfigs[module].nodes; for(var node in nodes) { /* istanbul ignore else */ if (nodes.hasOwnProperty(node)) { var config = nodes[node]; var n = filterNodeInfo(config); delete n.err; delete n.file; delete n.id; n.file = config.file; moduleList[module].nodes[node] = n; } } } } } if (hadPending && !hasPending) { events.emit("runtime-event",{id:"restart-required",retain: true}); } if (settings.available()) { return settings.set("nodes",moduleList); } else { return Promise.reject("Settings unavailable"); } } function loadNodeConfigs() { var configs = settings.get("nodes"); if (!configs) { return {}; } else if (configs['node-red']) { return configs; } else { // Migrate from the 0.9.1 format of settings var newConfigs = {}; for (var id in configs) { /* istanbul ignore else */ if (configs.hasOwnProperty(id)) { var nodeConfig = configs[id]; var moduleName; var nodeSetName; if (nodeConfig.module) { moduleName = nodeConfig.module; nodeSetName = nodeConfig.name.split(":")[1]; } else { moduleName = "node-red"; nodeSetName = nodeConfig.name.replace(/^\d+-/,"").replace(/\.js$/,""); } if (!newConfigs[moduleName]) { newConfigs[moduleName] = { name: moduleName, nodes:{} }; } newConfigs[moduleName].nodes[nodeSetName] = { name: nodeSetName, types: nodeConfig.types, enabled: nodeConfig.enabled, module: moduleName }; } } settings.set("nodes",newConfigs); return newConfigs; } } function addModule(module) { moduleNodes[module.name] = []; moduleConfigs[module.name] = module; for (var setName in module.nodes) { if (module.nodes.hasOwnProperty(setName)) { var set = module.nodes[setName]; moduleNodes[module.name].push(set.name); nodeList.push(set.id); if (!set.err) { set.types.forEach(function(t) { if (nodeTypeToId.hasOwnProperty(t)) { set.err = "Type already registered"; set.err.code = "type_already_registered"; set.err.details = { type: t, moduleA: getNodeInfo(t).module, moduleB: set.module } } }); if (!set.err) { set.types.forEach(function(t) { nodeTypeToId[t] = set.id; }); } } } } if (module.icons) { icon_paths[module.name] = icon_paths[module.name] || []; module.icons.forEach(icon=>icon_paths[module.name].push(path.resolve(icon.path)) ) } if (module.examples) { library.addExamplesDir(module.name,module.examples.path); } nodeConfigCache = null; } function removeNode(id) { var config = moduleConfigs[getModule(id)].nodes[getNode(id)]; if (!config) { throw new Error("Unrecognised id: "+id); } delete moduleConfigs[getModule(id)].nodes[getNode(id)]; var i = nodeList.indexOf(id); if (i > -1) { nodeList.splice(i,1); } config.types.forEach(function(t) { var typeId = nodeTypeToId[t]; if (typeId === id) { delete nodeConstructors[t]; delete nodeTypeToId[t]; } }); config.enabled = false; config.loaded = false; nodeConfigCache = null; return filterNodeInfo(config); } function removeModule(module) { if (!settings.available()) { throw new Error("Settings unavailable"); } var nodes = moduleNodes[module]; if (!nodes) { throw new Error("Unrecognised module: "+module); } var infoList = []; for (var i=0;i\n"; result += config.config; result += loader.getNodeHelp(config,lang||"en-US")||""; //script += config.script; } } //if (script.length > 0) { // result += ''; //} nodeConfigCache = result; } return nodeConfigCache; } function getNodeConfig(id,lang) { var config = moduleConfigs[getModule(id)]; if (!config) { return null; } config = config.nodes[getNode(id)]; if (config) { var result = "\n"+config.config; result += loader.getNodeHelp(config,lang||"en-US") //if (config.script) { // result += ''; //} return result; } else { return null; } } function getNodeConstructor(type) { var id = nodeTypeToId[type]; var config; if (typeof id === "undefined") { config = undefined; } else { config = moduleConfigs[getModule(id)].nodes[getNode(id)]; } if (!config || (config.enabled && !config.err)) { return nodeConstructors[type]; } return null; } function clear() { nodeConfigCache = null; moduleConfigs = {}; nodeList = []; nodeConstructors = {}; nodeTypeToId = {}; } function getTypeId(type) { if (nodeTypeToId.hasOwnProperty(type)) { return nodeTypeToId[type]; } else { return null; } } function enableNodeSet(typeOrId) { if (!settings.available()) { throw new Error("Settings unavailable"); } var id = typeOrId; if (nodeTypeToId.hasOwnProperty(typeOrId)) { id = nodeTypeToId[typeOrId]; } var config; try { config = moduleConfigs[getModule(id)].nodes[getNode(id)]; delete config.err; config.enabled = true; nodeConfigCache = null; settings.enableNodeSettings(config.types); return saveNodeList().then(function() { return filterNodeInfo(config); }); } catch (err) { throw new Error("Unrecognised id: "+typeOrId); } } function disableNodeSet(typeOrId) { if (!settings.available()) { throw new Error("Settings unavailable"); } var id = typeOrId; if (nodeTypeToId.hasOwnProperty(typeOrId)) { id = nodeTypeToId[typeOrId]; } var config; try { config = moduleConfigs[getModule(id)].nodes[getNode(id)]; // TODO: persist setting config.enabled = false; nodeConfigCache = null; settings.disableNodeSettings(config.types); return saveNodeList().then(function() { return filterNodeInfo(config); }); } catch (err) { throw new Error("Unrecognised id: "+id); } } function cleanModuleList() { var removed = false; for (var mod in moduleConfigs) { /* istanbul ignore else */ if (moduleConfigs.hasOwnProperty(mod)) { var nodes = moduleConfigs[mod].nodes; var node; if (mod == "node-red") { // For core nodes, look for nodes that are enabled, !loaded and !errored for (node in nodes) { /* istanbul ignore else */ if (nodes.hasOwnProperty(node)) { var n = nodes[node]; if (n.enabled && !n.err && !n.loaded) { removeNode(mod+"/"+node); removed = true; } } } } else { if (moduleConfigs[mod] && !moduleNodes[mod]) { // For node modules, look for missing ones for (node in nodes) { /* istanbul ignore else */ if (nodes.hasOwnProperty(node)) { removeNode(mod+"/"+node); removed = true; } } delete moduleConfigs[mod]; } } } } if (removed) { saveNodeList(); } } function setModulePendingUpdated(module,version) { moduleConfigs[module].pending_version = version; return saveNodeList().then(function() { return getModuleInfo(module); }); } var icon_paths = { }; var iconCache = {}; function getNodeIconPath(module,icon) { if (/\.\./.test(icon)) { throw new Error(); } var iconName = module+"/"+icon; if (iconCache[iconName]) { return iconCache[iconName]; } else { var paths = icon_paths[module]; if (paths) { for (var p=0;p{ iconList[module] = iconList[module].concat(icon.icons)}) } } } return iconList; } var registry = module.exports = { init: init, load: load, clear: clear, registerNodeConstructor: registerNodeConstructor, getNodeConstructor: getNodeConstructor, addModule: addModule, enableNodeSet: enableNodeSet, disableNodeSet: disableNodeSet, setModulePendingUpdated: setModulePendingUpdated, removeModule: removeModule, getNodeInfo: getNodeInfo, getFullNodeInfo: getFullNodeInfo, getNodeList: getNodeList, getModuleList: getModuleList, getModuleInfo: getModuleInfo, getNodeIconPath: getNodeIconPath, getNodeIcons: getNodeIcons, /** * Gets all of the node template configs * @return all of the node templates in a single string */ getAllNodeConfigs: getAllNodeConfigs, getNodeConfig: getNodeConfig, getTypeId: getTypeId, saveNodeList: saveNodeList, cleanModuleList: cleanModuleList };