/** * Copyright 2014 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 cheerio = require("cheerio"); var UglifyJS = require("uglify-js"); var events = require("../events"); var Node; var settings; var node_types = {}; var node_configs = []; //TODO: clear this cache whenever a node type is added/removed var node_config_cache = null; /** * 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 = fs.readdirSync(dir); files.sort(); files.forEach(function(fn) { var stats = fs.statSync(path.join(dir,fn)); if (stats.isFile()) { if (/\.js$/.test(fn)) { result.push(path.join(dir,fn)); } } else if (stats.isDirectory()) { // Ignore /.dirs/, /lib/ /node_modules/ if (!/^(\..*|lib|icons|node_modules|test)$/.test(fn)) { result = result.concat(getNodeFiles(path.join(dir,fn))); } else if (fn === "icons") { events.emit("node-icon-dir",path.join(dir,fn)); } } }); return result; } /** * Scans the node_modules path for nodes * @return a list of node modules: {dir,package} */ function scanTreeForNodesModules(moduleName) { var dir = __dirname+"/../../nodes"; var results = []; var up = path.resolve(path.join(dir,"..")); while (up !== dir) { var pm = path.join(dir,"node_modules"); try { var files = fs.readdirSync(pm); files.forEach(function(fn) { if (!moduleName || fn == moduleName) { var pkgfn = path.join(pm,fn,"package.json"); try { var pkg = require(pkgfn); if (pkg['node-red']) { var moduleDir = path.join(pm,fn); results.push({dir:moduleDir,package:pkg}); } } catch(err) { if (err.code != "MODULE_NOT_FOUND") { // TODO: handle unexpected error } } } }); } catch(err) { } dir = up; up = path.resolve(path.join(dir,"..")); } return results; } /** * Loads the nodes provided in an npm package. * @param moduleDir the root directory of the package * @param pkg the module's package.json object * @return an array of promises returned by loadNode */ function loadNodesFromModule(moduleDir,pkg) { var nodes = pkg['node-red'].nodes||{}; var promises = []; var iconDirs = []; for (var n in nodes) { if (nodes.hasOwnProperty(n)) { promises.push(loadNode(path.join(moduleDir,nodes[n]),pkg.name,n)); var iconDir = path.join(moduleDir,path.dirname(nodes[n]),"icons"); if (iconDirs.indexOf(iconDir) == -1) { if (fs.existsSync(iconDir)) { events.emit("node-icon-dir",iconDir); iconDirs.push(iconDir); } } } } return promises; } /** * Loads the specified node into the registry. * @param nodeFile the fully qualified path of the node's .js file * @param nodeModule the name of the module (npm nodes only) * @param nodeName the name of the node (npm nodes only) * @return a promise that resolves to a node info object * { * name: the name of the node file, or label from the npm module * module: the name of the node npm module (npm nodes only) * path: the fully qualified path to the node's .js file * template: the fully qualified path to the node's .html file * config: the non-script parts of the node's .html file * script: the script part of the node's .html file * err: any error encountered whilst loading the node * } * The node info object must be added to the node_config array by the caller. * This allows nodes to be added in a defined order, regardless of how async * their loading becomes. */ function loadNode(nodeFile, nodeModule, nodeName) { var nodeDir = path.dirname(nodeFile); var nodeFn = path.basename(nodeFile); if (settings.nodesExcludes) { for (var i=0;i"; if (el.attribs) { for (var j in el.attribs) { if (el.attribs.hasOwnProperty(j)) { openTag += " "+j+'="'+el.attribs[j]+'"'; } } } openTag += ">"; template += openTag+$(el).text()+closeTag; } }); nodeInfo.template = templateFilename; nodeInfo.config = template; nodeInfo.script = script; return nodeInfo; } function init(_settings) { Node = require("./Node"); settings = _settings; } /** * Loads all palette nodes * @return a promise that resolves to a list of any errors encountered loading nodes */ function load() { return when.promise(function(resolve,reject) { // Find all of the nodes to load var nodeFiles = getNodeFiles(__dirname+"/../../nodes"); if (settings.nodesDir) { var dir = settings.nodesDir; if (typeof settings.nodesDir == "string") { dir = [dir]; } for (var i=0;i