/** * Copyright 2014, 2015 IBM Corp. * * 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 util = require("util"); var when = require("when"); var whenNode = require('when/node'); var fs = require("fs"); var path = require("path"); var crypto = require("crypto"); var UglifyJS = require("uglify-js"); var events = require("../events"); var Node; var settings; function filterNodeInfo(n) { var r = { id: n.id, name: n.name, types: n.types, enabled: n.enabled }; if (n.hasOwnProperty("loaded")) { r.loaded = n.loaded; } if (n.hasOwnProperty("module")) { r.module = n.module; } if (n.hasOwnProperty("err")) { r.err = n.err.toString(); } return r; } function getModule(id) { return id.split("/")[0]; } function getNode(id) { return id.split("/")[1]; } var registry = (function() { var nodeConfigCache = null; var moduleConfigs = {}; var nodeList = []; var nodeConstructors = {}; var nodeTypeToId = {}; var moduleNodes = {}; function saveNodeList() { var moduleList = {}; for (var module in moduleConfigs) { if (moduleConfigs.hasOwnProperty(module)) { if (!moduleList[module]) { moduleList[module] = { name: module, version: moduleConfigs[module].version, nodes: {} }; } var nodes = moduleConfigs[module].nodes; for(var node in nodes) { if (nodes.hasOwnProperty(node)) { var config = nodes[node]; var n = filterNodeInfo(config); delete n.loaded; delete n.err; delete n.file; delete n.id; moduleList[module].nodes[node] = n; } } } } if (settings.available()) { return settings.set("nodes",moduleList); } else { return when.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) { 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; } } return { init: function() { if (settings.available()) { moduleConfigs = loadNodeConfigs(); } else { moduleConfigs = {}; } moduleNodes = {}; nodeTypeToId = {}; nodeConstructors = {}; nodeList = []; nodeConfigCache = null; }, addNodeSet: function(id,set,version) { if (!set.err) { set.types.forEach(function(t) { nodeTypeToId[t] = id; }); } moduleNodes[set.module] = moduleNodes[set.module]||[]; moduleNodes[set.module].push(set.name); if (!moduleConfigs[set.module]) { moduleConfigs[set.module] = { name: set.module, nodes: {} }; } if (version) { moduleConfigs[set.module].version = version; } moduleConfigs[set.module].nodes[set.name] = set; nodeList.push(id); nodeConfigCache = null; }, removeNode: function(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) { delete nodeConstructors[t]; delete nodeTypeToId[t]; }); config.enabled = false; config.loaded = false; nodeConfigCache = null; return filterNodeInfo(config); }, removeModule: function(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 0) { result += ''; } nodeConfigCache = result; } return nodeConfigCache; }, getNodeConfig: function(id) { var config = moduleConfigs[getModule(id)]; if (!config) { return null; } config = config.nodes[getNode(id)]; if (config) { var result = config.config; if (config.script) { result += ''; } return result; } else { return null; } }, getNodeConstructor: function(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; }, clear: function() { nodeConfigCache = null; moduleConfigs = {}; nodeList = []; nodeConstructors = {}; nodeTypeToId = {}; }, getTypeId: function(type) { return nodeTypeToId[type]; }, getNodeModuleInfo: function(module) { return moduleNodes[module]; }, enableNodeSet: function(typeOrId) { if (!settings.available()) { throw new Error("Settings unavailable"); } var id = typeOrId; if (nodeTypeToId[typeOrId]) { id = nodeTypeToId[typeOrId]; } var config; try { config = moduleConfigs[getModule(id)].nodes[getNode(id)]; delete config.err; config.enabled = true; if (!config.loaded) { // TODO: honour the promise this returns loadNodeModule(config); } nodeConfigCache = null; saveNodeList(); } catch (err) { throw new Error("Unrecognised id: "+typeOrId); } return filterNodeInfo(config); }, disableNodeSet: function(typeOrId) { if (!settings.available()) { throw new Error("Settings unavailable"); } var id = typeOrId; if (nodeTypeToId[typeOrId]) { id = nodeTypeToId[typeOrId]; } var config; try { config = moduleConfigs[getModule(id)].nodes[getNode(id)]; // TODO: persist setting config.enabled = false; nodeConfigCache = null; saveNodeList(); } catch (err) { throw new Error("Unrecognised id: "+id); } return filterNodeInfo(config); }, saveNodeList: saveNodeList, cleanModuleList: function() { var removed = false; for (var mod in moduleConfigs) { 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) { if (nodes.hasOwnProperty(node)) { var n = nodes[node]; if (n.enabled && !n.err && !n.loaded) { registry.removeNode(mod+"/"+node); removed = true; } } } } else if (moduleConfigs[mod] && !moduleNodes[mod]) { // For node modules, look for missing ones for (node in nodes) { if (nodes.hasOwnProperty(node)) { registry.removeNode(mod+"/"+node); removed = true; } } } } } if (removed) { saveNodeList(); } } }; })(); function init(_settings) { Node = require("./Node"); settings = _settings; registry.init(); } /** * Synchronously walks the directory looking for node files. * Emits 'node-icon-dir' events for an icon dirs found * @param dir the directory to search * @return an array of fully-qualified paths to .js files */ function getNodeFiles(dir) { var result = []; var files = []; try { files = fs.readdirSync(dir); } catch(err) { return result; } files.sort(); files.forEach(function(fn) { var stats = fs.statSync(path.join(dir,fn)); if (stats.isFile()) { if (/\.js$/.test(fn)) { var valid = true; if (settings.nodesExcludes) { for (var i=0;i]*)data-template-name=['"]([^'"]*)['"]/gi; var match = null; while((match = regExp.exec(content)) !== null) { types.push(match[2]); } node.types = types; node.config = content; // TODO: parse out the javascript portion of the template node.script = ""; for (var i=0;i