mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Merge branch '0.14.0'
This commit is contained in:
@@ -34,17 +34,17 @@ var stubbedExpressApp = {
|
||||
get: function() {},
|
||||
post: function() {},
|
||||
put: function() {},
|
||||
delete: function(){}
|
||||
delete: function() {}
|
||||
}
|
||||
var adminApi = {
|
||||
library: {
|
||||
register: function(){}
|
||||
register: function() {}
|
||||
},
|
||||
auth: {
|
||||
needsPermission: function(){}
|
||||
needsPermission: function() {}
|
||||
},
|
||||
comms: {
|
||||
publish: function(){}
|
||||
publish: function() {}
|
||||
},
|
||||
adminApp: stubbedExpressApp,
|
||||
nodeApp: stubbedExpressApp,
|
||||
@@ -105,16 +105,11 @@ function start() {
|
||||
var nodeErrors = redNodes.getNodeList(function(n) { return n.err!=null;});
|
||||
var nodeMissing = redNodes.getNodeList(function(n) { return n.module && n.enabled && !n.loaded && !n.err;});
|
||||
if (nodeErrors.length > 0) {
|
||||
log.warn("------------------------------------------");
|
||||
if (settings.verbose) {
|
||||
for (i=0;i<nodeErrors.length;i+=1) {
|
||||
log.warn("["+nodeErrors[i].name+"] "+nodeErrors[i].err);
|
||||
}
|
||||
} else {
|
||||
log.warn(log._("server.errors",{count:nodeErrors.length}));
|
||||
log.warn(log._("server.errors-help"));
|
||||
log.warn("------------------------------------------------------");
|
||||
for (i=0;i<nodeErrors.length;i+=1) {
|
||||
log.warn("["+nodeErrors[i].name+"] "+nodeErrors[i].err);
|
||||
}
|
||||
log.warn("------------------------------------------");
|
||||
log.warn("------------------------------------------------------");
|
||||
}
|
||||
if (nodeMissing.length > 0) {
|
||||
log.warn(log._("server.missing-modules"));
|
||||
@@ -148,7 +143,7 @@ function start() {
|
||||
}).otherwise(function(err) {
|
||||
console.log(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function reportMetrics() {
|
||||
|
@@ -120,7 +120,7 @@
|
||||
},
|
||||
"localfilesystem": {
|
||||
"user-dir": "User directory : __path__",
|
||||
"flows-file": "Flows file : __path__",
|
||||
"flows-file": "Flows file : __path__",
|
||||
"create": "Creating new flow file",
|
||||
"empty": "Existing flow file is empty",
|
||||
"invalid": "Existing flow file is not valid json",
|
||||
|
@@ -72,7 +72,11 @@ var consoleLogger = function(msg) {
|
||||
if (msg.level == log.METRIC || msg.level == log.AUDIT) {
|
||||
util.log("["+levelNames[msg.level]+"] "+JSON.stringify(msg));
|
||||
} else {
|
||||
util.log("["+levelNames[msg.level]+"] "+(msg.type?"["+msg.type+":"+(msg.name||msg.id)+"] ":"")+msg.msg);
|
||||
var message = msg.msg;
|
||||
if (typeof message === 'object' && message.toString() === '[object Object]' && message.message) {
|
||||
message = message.message;
|
||||
}
|
||||
util.log("["+levelNames[msg.level]+"] "+(msg.type?"["+msg.type+":"+(msg.name||msg.id)+"] ":"")+message);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2014, 2015 IBM Corp.
|
||||
* Copyright 2014, 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -99,8 +99,15 @@ Node.prototype.close = function() {
|
||||
}
|
||||
}
|
||||
if (promises.length > 0) {
|
||||
return when.settle(promises);
|
||||
return when.settle(promises).then(function() {
|
||||
if (this._context) {
|
||||
context.delete(this._alias||this.id,this.z);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (this._context) {
|
||||
context.delete(this._alias||this.id,this.z);
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -157,16 +164,18 @@ Node.prototype.send = function(msg) {
|
||||
// for each msg to send eg. [[m1, m2, ...], ...]
|
||||
for (k = 0; k < msgs.length; k++) {
|
||||
var m = msgs[k];
|
||||
/* istanbul ignore else */
|
||||
if (!sentMessageId) {
|
||||
sentMessageId = m._msgid;
|
||||
}
|
||||
if (msgSent) {
|
||||
var clonedmsg = redUtil.cloneMessage(m);
|
||||
sendEvents.push({n:node,m:clonedmsg});
|
||||
} else {
|
||||
sendEvents.push({n:node,m:m});
|
||||
msgSent = true;
|
||||
if (m !== null && m !== undefined) {
|
||||
/* istanbul ignore else */
|
||||
if (!sentMessageId) {
|
||||
sentMessageId = m._msgid;
|
||||
}
|
||||
if (msgSent) {
|
||||
var clonedmsg = redUtil.cloneMessage(m);
|
||||
sendEvents.push({n:node,m:clonedmsg});
|
||||
} else {
|
||||
sendEvents.push({n:node,m:m});
|
||||
msgSent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2015 IBM Corp.
|
||||
* Copyright 2015, 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -76,5 +76,6 @@ module.exports = {
|
||||
globalContext = createContext("global",settings.functionGlobalContext || {});
|
||||
},
|
||||
get: getContext,
|
||||
delete: deleteContext,
|
||||
clean:clean
|
||||
};
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2015 IBM Corp.
|
||||
* Copyright 2015, 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,10 +36,32 @@ function Flow(global,flow) {
|
||||
var id;
|
||||
catchNodeMap = {};
|
||||
statusNodeMap = {};
|
||||
for (id in flow.configs) {
|
||||
if (flow.configs.hasOwnProperty(id)) {
|
||||
node = flow.configs[id];
|
||||
if (!activeNodes[id]) {
|
||||
|
||||
var configNodes = Object.keys(flow.configs);
|
||||
var configNodeAttempts = {};
|
||||
while(configNodes.length > 0) {
|
||||
id = configNodes.shift();
|
||||
node = flow.configs[id];
|
||||
if (!activeNodes[id]) {
|
||||
var readyToCreate = true;
|
||||
// This node doesn't exist.
|
||||
// Check it doesn't reference another non-existent config node
|
||||
for (var prop in node) {
|
||||
if (node.hasOwnProperty(prop) && prop !== 'id' && prop !== 'wires' && prop !== '_users' && flow.configs[node[prop]]) {
|
||||
if (!activeNodes[node[prop]]) {
|
||||
// References a non-existent config node
|
||||
// Add it to the back of the list to try again later
|
||||
configNodes.push(id);
|
||||
configNodeAttempts[id] = (configNodeAttempts[id]||0)+1;
|
||||
if (configNodeAttempts[id] === 100) {
|
||||
throw new Error("Circular config node dependency detected: "+id);
|
||||
}
|
||||
readyToCreate = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (readyToCreate) {
|
||||
newNode = createNode(node.type,node);
|
||||
if (newNode) {
|
||||
activeNodes[id] = newNode;
|
||||
@@ -47,6 +69,7 @@ function Flow(global,flow) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (diff && diff.rewired) {
|
||||
for (var j=0;j<diff.rewired.length;j++) {
|
||||
var rewireNode = activeNodes[diff.rewired[j]];
|
||||
@@ -224,6 +247,9 @@ function Flow(global,flow) {
|
||||
count: count
|
||||
}
|
||||
};
|
||||
if (logMessage.hasOwnProperty('stack')) {
|
||||
errorMessage.error.stack = logMessage.stack;
|
||||
}
|
||||
targetCatchNode.receive(errorMessage);
|
||||
handled = true;
|
||||
});
|
||||
@@ -254,7 +280,7 @@ function mapEnvVarProperties(obj,prop) {
|
||||
}
|
||||
} else {
|
||||
for (var p in obj[prop]) {
|
||||
if (obj[prop].hasOwnProperty) {
|
||||
if (obj[prop].hasOwnProperty(p)) {
|
||||
mapEnvVarProperties(obj[prop],p);
|
||||
}
|
||||
}
|
||||
|
@@ -457,7 +457,11 @@ function getFlow(id) {
|
||||
var nodeIds = Object.keys(flow.nodes);
|
||||
if (nodeIds.length > 0) {
|
||||
result.nodes = nodeIds.map(function(nodeId) {
|
||||
return clone(flow.nodes[nodeId]);
|
||||
var node = clone(flow.nodes[nodeId]);
|
||||
if (node.type === 'link out') {
|
||||
delete node.wires;
|
||||
}
|
||||
return node;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -67,7 +67,8 @@ module.exports = {
|
||||
flow.subflows[n.id].instances = [];
|
||||
}
|
||||
});
|
||||
|
||||
var linkWires = {};
|
||||
var linkOutNodes = [];
|
||||
config.forEach(function(n) {
|
||||
if (n.type !== 'subflow' && n.type !== 'tab') {
|
||||
var subflowDetails = subflowInstanceRE.exec(n.type);
|
||||
@@ -100,19 +101,56 @@ module.exports = {
|
||||
flow.configs[n.id]._users = [];
|
||||
}
|
||||
}
|
||||
if (n.type === 'link in' && n.links) {
|
||||
// Ensure wires are present in corresponding link out nodes
|
||||
n.links.forEach(function(id) {
|
||||
linkWires[id] = linkWires[id]||{};
|
||||
linkWires[id][n.id] = true;
|
||||
})
|
||||
} else if (n.type === 'link out' && n.links) {
|
||||
linkWires[n.id] = linkWires[n.id]||{};
|
||||
n.links.forEach(function(id) {
|
||||
linkWires[n.id][id] = true;
|
||||
})
|
||||
linkOutNodes.push(n);
|
||||
}
|
||||
}
|
||||
});
|
||||
linkOutNodes.forEach(function(n) {
|
||||
var links = linkWires[n.id];
|
||||
var targets = Object.keys(links);
|
||||
n.wires = [targets];
|
||||
});
|
||||
|
||||
|
||||
var addedTabs = {};
|
||||
config.forEach(function(n) {
|
||||
if (n.type !== 'subflow' && n.type !== 'tab') {
|
||||
for (var prop in n) {
|
||||
if (n.hasOwnProperty(prop) && prop !== 'id' && prop !== 'wires' && prop !== '_users' && flow.configs[n[prop]]) {
|
||||
if (n.hasOwnProperty(prop) && prop !== 'id' && prop !== 'wires' && prop !== 'type' && prop !== '_users' && flow.configs[n[prop]]) {
|
||||
// This property references a global config node
|
||||
flow.configs[n[prop]]._users.push(n.id)
|
||||
}
|
||||
}
|
||||
if (n.z && !flow.subflows[n.z]) {
|
||||
|
||||
if (!flow.flows[n.z]) {
|
||||
flow.flows[n.z] = {type:'tab',id:n.z};
|
||||
flow.flows[n.z].subflows = {};
|
||||
flow.flows[n.z].configs = {};
|
||||
flow.flows[n.z].nodes = {};
|
||||
addedTabs[n.z] = flow.flows[n.z];
|
||||
}
|
||||
if (addedTabs[n.z]) {
|
||||
if (n.hasOwnProperty('x') && n.hasOwnProperty('y')) {
|
||||
addedTabs[n.z].nodes[n.id] = n;
|
||||
} else {
|
||||
addedTabs[n.z].configs[n.id] = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return flow;
|
||||
},
|
||||
|
||||
@@ -230,22 +268,32 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
|
||||
for (id in newConfig.allNodes) {
|
||||
if (newConfig.allNodes.hasOwnProperty(id)) {
|
||||
node = newConfig.allNodes[id];
|
||||
for (var prop in node) {
|
||||
if (node.hasOwnProperty(prop) && prop != "z" && prop != "id" && prop != "wires") {
|
||||
// This node has a property that references a changed/removed node
|
||||
// Assume it is a config node change and mark this node as
|
||||
// changed.
|
||||
if (changed[node[prop]] || removed[node[prop]]) {
|
||||
if (!changed[node.id]) {
|
||||
changed[node.id] = node;
|
||||
if (newConfig.allNodes[node.z]) {
|
||||
changed[node.z] = newConfig.allNodes[node.z];
|
||||
if (changed[node.z].type === "subflow") {
|
||||
changedSubflows[node.z] = changed[node.z];
|
||||
delete changed[node.id];
|
||||
var madeChange;
|
||||
// Loop through the nodes looking for references to changed config nodes
|
||||
// Repeat the loop if anything is marked as changed as it may need to be
|
||||
// propagated to parent nodes.
|
||||
// TODO: looping through all nodes every time is a bit inefficient - could be more targeted
|
||||
do {
|
||||
madeChange = false;
|
||||
for (id in newConfig.allNodes) {
|
||||
if (newConfig.allNodes.hasOwnProperty(id)) {
|
||||
node = newConfig.allNodes[id];
|
||||
for (var prop in node) {
|
||||
if (node.hasOwnProperty(prop) && prop != "z" && prop != "id" && prop != "wires") {
|
||||
// This node has a property that references a changed/removed node
|
||||
// Assume it is a config node change and mark this node as
|
||||
// changed.
|
||||
if (changed[node[prop]] || removed[node[prop]]) {
|
||||
if (!changed[node.id]) {
|
||||
madeChange = true;
|
||||
changed[node.id] = node;
|
||||
// This node exists within subflow template
|
||||
// Mark the template as having changed
|
||||
if (newConfig.allNodes[node.z]) {
|
||||
changed[node.z] = newConfig.allNodes[node.z];
|
||||
if (changed[node.z].type === "subflow") {
|
||||
changedSubflows[node.z] = changed[node.z];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -253,8 +301,18 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while(madeChange===true)
|
||||
|
||||
// Find any nodes that exist on a subflow template and remove from changed
|
||||
// list as the parent subflow will now be marked as containing a change
|
||||
for (id in newConfig.allNodes) {
|
||||
if (newConfig.allNodes.hasOwnProperty(id)) {
|
||||
node = newConfig.allNodes[id];
|
||||
if (newConfig.allNodes[node.z] && newConfig.allNodes[node.z].type === "subflow") {
|
||||
delete changed[node.id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively mark all instances of changed subflows as changed
|
||||
var changedSubflowStack = Object.keys(changedSubflows);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013, 2015 IBM Corp.
|
||||
* Copyright 2013, 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -33,15 +33,25 @@ var settings;
|
||||
|
||||
/**
|
||||
* Registers a node constructor
|
||||
* @param nodeSet - the nodeSet providing the node (module/set)
|
||||
* @param type - the string type name
|
||||
* @param constructor - the constructor function for this node type
|
||||
* @param opts - optional additional options for the node
|
||||
*/
|
||||
function registerType(type,constructor,opts) {
|
||||
function registerType(nodeSet,type,constructor,opts) {
|
||||
if (typeof type !== "string") {
|
||||
// This is someone calling the api directly, rather than via the
|
||||
// RED object provided to a node. Log a warning
|
||||
log.warn("Deprecated call to RED.runtime.nodes.registerType - node-set name must be provided as first argument");
|
||||
opts = constructor;
|
||||
constructor = type;
|
||||
type = nodeSet;
|
||||
nodeSet = "";
|
||||
}
|
||||
if (opts && opts.credentials) {
|
||||
credentials.register(type,opts.credentials);
|
||||
}
|
||||
registry.registerType(type,constructor);
|
||||
registry.registerType(nodeSet,type,constructor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2015 IBM Corp.
|
||||
* Copyright 2015, 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -67,10 +67,14 @@ function createNodeApi(node) {
|
||||
nodes: {},
|
||||
log: {},
|
||||
settings: {},
|
||||
events: runtime.events,
|
||||
util: runtime.util,
|
||||
version: runtime.version,
|
||||
}
|
||||
copyObjectProperties(runtime.nodes,red.nodes,["createNode","getNode","eachNode","registerType","addCredentials","getCredentials","deleteCredentials" ]);
|
||||
copyObjectProperties(runtime.nodes,red.nodes,["createNode","getNode","eachNode","addCredentials","getCredentials","deleteCredentials" ]);
|
||||
red.nodes.registerType = function(type,constructor,opts) {
|
||||
runtime.nodes.registerType(node.id,type,constructor,opts);
|
||||
}
|
||||
copyObjectProperties(runtime.log,red.log,null,["init"]);
|
||||
copyObjectProperties(runtime.settings,red.settings,null,["init","load","reset"]);
|
||||
if (runtime.adminApi) {
|
||||
|
@@ -102,22 +102,26 @@ function scanDirForNodesModules(dir,moduleName) {
|
||||
var files = fs.readdirSync(dir);
|
||||
for (var i=0;i<files.length;i++) {
|
||||
var fn = files[i];
|
||||
if (!isExcluded(fn) && (!moduleName || fn == moduleName)) {
|
||||
var pkgfn = path.join(dir,fn,"package.json");
|
||||
try {
|
||||
var pkg = require(pkgfn);
|
||||
if (pkg['node-red']) {
|
||||
var moduleDir = path.join(dir,fn);
|
||||
results.push({dir:moduleDir,package:pkg});
|
||||
if (/^@/.test(fn)) {
|
||||
results = results.concat(scanDirForNodesModules(path.join(dir,fn),moduleName));
|
||||
} else {
|
||||
if (!isExcluded(fn) && (!moduleName || fn == moduleName)) {
|
||||
var pkgfn = path.join(dir,fn,"package.json");
|
||||
try {
|
||||
var pkg = require(pkgfn);
|
||||
if (pkg['node-red']) {
|
||||
var moduleDir = path.join(dir,fn);
|
||||
results.push({dir:moduleDir,package:pkg});
|
||||
}
|
||||
} catch(err) {
|
||||
if (err.code != "MODULE_NOT_FOUND") {
|
||||
// TODO: handle unexpected error
|
||||
}
|
||||
}
|
||||
} catch(err) {
|
||||
if (err.code != "MODULE_NOT_FOUND") {
|
||||
// TODO: handle unexpected error
|
||||
if (fn == moduleName) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fn == moduleName) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(err) {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2015 IBM Corp.
|
||||
* Copyright 2015, 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -67,11 +67,13 @@ function filterNodeInfo(n) {
|
||||
|
||||
|
||||
function getModule(id) {
|
||||
return id.split("/")[0];
|
||||
var parts = id.split("/");
|
||||
return parts.slice(0,parts.length-1).join("/");
|
||||
}
|
||||
|
||||
function getNode(id) {
|
||||
return id.split("/")[1];
|
||||
var parts = id.split("/");
|
||||
return parts[parts.length-1];
|
||||
}
|
||||
|
||||
function saveNodeList() {
|
||||
@@ -223,7 +225,7 @@ function removeModule(module) {
|
||||
|
||||
function getNodeInfo(typeOrId) {
|
||||
var id = typeOrId;
|
||||
if (nodeTypeToId[typeOrId]) {
|
||||
if (nodeTypeToId.hasOwnProperty(typeOrId)) {
|
||||
id = nodeTypeToId[typeOrId];
|
||||
}
|
||||
/* istanbul ignore else */
|
||||
@@ -248,7 +250,7 @@ function getFullNodeInfo(typeOrId) {
|
||||
// Used by index.enableNodeSet so that .file can be retrieved to pass
|
||||
// to loader.loadNodeSet
|
||||
var id = typeOrId;
|
||||
if (nodeTypeToId[typeOrId]) {
|
||||
if (nodeTypeToId.hasOwnProperty(typeOrId)) {
|
||||
id = nodeTypeToId[typeOrId];
|
||||
}
|
||||
/* istanbul ignore else */
|
||||
@@ -326,14 +328,49 @@ function getCaller(){
|
||||
return stack[0].getFileName();
|
||||
}
|
||||
|
||||
function registerNodeConstructor(type,constructor) {
|
||||
if (nodeConstructors[type]) {
|
||||
function inheritNode(constructor) {
|
||||
if(Object.getPrototypeOf(constructor.prototype) === Object.prototype) {
|
||||
util.inherits(constructor,Node);
|
||||
} else {
|
||||
var proto = constructor.prototype;
|
||||
while(Object.getPrototypeOf(proto) !== Object.prototype) {
|
||||
proto = Object.getPrototypeOf(proto);
|
||||
}
|
||||
//TODO: This is a partial implementation of util.inherits >= node v5.0.0
|
||||
// which should be changed when support for node < v5.0.0 is dropped
|
||||
// see: https://github.com/nodejs/node/pull/3455
|
||||
proto.constructor.super_ = Node;
|
||||
if(Object.setPrototypeOf) {
|
||||
Object.setPrototypeOf(proto, Node.prototype);
|
||||
} else {
|
||||
// hack for node v0.10
|
||||
proto.__proto__ = Node.prototype;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function registerNodeConstructor(nodeSet,type,constructor) {
|
||||
if (nodeConstructors.hasOwnProperty(type)) {
|
||||
throw new Error(type+" already registered");
|
||||
}
|
||||
//TODO: Ensure type is known - but doing so will break some tests
|
||||
// that don't have a way to register a node template ahead
|
||||
// of registering the constructor
|
||||
util.inherits(constructor,Node);
|
||||
if(!(constructor.prototype instanceof Node)) {
|
||||
inheritNode(constructor);
|
||||
}
|
||||
|
||||
var nodeSetInfo = getFullNodeInfo(nodeSet);
|
||||
if (nodeSetInfo) {
|
||||
if (nodeSetInfo.types.indexOf(type) === -1) {
|
||||
// A type is being registered for a known set, but for some reason
|
||||
// we didn't spot it when parsing the HTML file.
|
||||
// Registered a type is the definitive action - not the presence
|
||||
// of an edit template. Ensure it is on the list of known types.
|
||||
nodeSetInfo.types.push(type);
|
||||
}
|
||||
}
|
||||
|
||||
nodeConstructors[type] = constructor;
|
||||
events.emit("type-registered",type);
|
||||
}
|
||||
@@ -405,7 +442,11 @@ function clear() {
|
||||
}
|
||||
|
||||
function getTypeId(type) {
|
||||
return nodeTypeToId[type];
|
||||
if (nodeTypeToId.hasOwnProperty(type)) {
|
||||
return nodeTypeToId[type];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function enableNodeSet(typeOrId) {
|
||||
@@ -414,7 +455,7 @@ function enableNodeSet(typeOrId) {
|
||||
}
|
||||
|
||||
var id = typeOrId;
|
||||
if (nodeTypeToId[typeOrId]) {
|
||||
if (nodeTypeToId.hasOwnProperty(typeOrId)) {
|
||||
id = nodeTypeToId[typeOrId];
|
||||
}
|
||||
var config;
|
||||
@@ -436,7 +477,7 @@ function disableNodeSet(typeOrId) {
|
||||
throw new Error("Settings unavailable");
|
||||
}
|
||||
var id = typeOrId;
|
||||
if (nodeTypeToId[typeOrId]) {
|
||||
if (nodeTypeToId.hasOwnProperty(typeOrId)) {
|
||||
id = nodeTypeToId[typeOrId];
|
||||
}
|
||||
var config;
|
||||
|
@@ -127,14 +127,108 @@ function compareObjects(obj1,obj2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function normalisePropertyExpression(str) {
|
||||
var length = str.length;
|
||||
var parts = [];
|
||||
var start = 0;
|
||||
var inString = false;
|
||||
var inBox = false;
|
||||
var quoteChar;
|
||||
var v;
|
||||
for (var i=0;i<length;i++) {
|
||||
var c = str[i];
|
||||
if (!inString) {
|
||||
if (c === "'" || c === '"') {
|
||||
if (!inBox) {
|
||||
throw new Error("Invalid property expression: unexpected "+c+" at position "+i);
|
||||
}
|
||||
inString = true;
|
||||
quoteChar = c;
|
||||
start = i+1;
|
||||
} else if (c === '.') {
|
||||
if (i===0) {
|
||||
throw new Error("Invalid property expression: unexpected . at position 0");
|
||||
}
|
||||
if (start != i) {
|
||||
v = str.substring(start,i);
|
||||
if (/^\d+$/.test(v)) {
|
||||
parts.push(parseInt(v));
|
||||
} else {
|
||||
parts.push(v);
|
||||
}
|
||||
}
|
||||
if (i===length-1) {
|
||||
throw new Error("Invalid property expression: unterminated expression");
|
||||
}
|
||||
// Next char is a-z
|
||||
if (!/[a-z0-9]/i.test(str[i+1])) {
|
||||
throw new Error("Invalid property expression: unexpected "+str[i+1]+" at position "+(i+1));
|
||||
}
|
||||
start = i+1;
|
||||
} else if (c === '[') {
|
||||
if (i === 0) {
|
||||
throw new Error("Invalid property expression: unexpected "+c+" at position "+i);
|
||||
}
|
||||
if (start != i) {
|
||||
parts.push(str.substring(start,i));
|
||||
}
|
||||
if (i===length-1) {
|
||||
throw new Error("Invalid property expression: unterminated expression");
|
||||
}
|
||||
// Next char is either a quote or a number
|
||||
if (!/["'\d]/.test(str[i+1])) {
|
||||
throw new Error("Invalid property expression: unexpected "+str[i+1]+" at position "+(i+1));
|
||||
}
|
||||
start = i+1;
|
||||
inBox = true;
|
||||
} else if (c === ']') {
|
||||
if (!inBox) {
|
||||
throw new Error("Invalid property expression: unexpected "+c+" at position "+i);
|
||||
}
|
||||
if (start != i) {
|
||||
v = str.substring(start,i);
|
||||
if (/^\d+$/.test(v)) {
|
||||
parts.push(parseInt(v));
|
||||
} else {
|
||||
throw new Error("Invalid property expression: unexpected array expression at position "+start);
|
||||
}
|
||||
}
|
||||
start = i+1;
|
||||
inBox = false;
|
||||
} else if (c === ' ') {
|
||||
throw new Error("Invalid property expression: unexpected ' ' at position "+i);
|
||||
}
|
||||
} else {
|
||||
if (c === quoteChar) {
|
||||
parts.push(str.substring(start,i));
|
||||
// Next char must be a ]
|
||||
if (!/\]/.test(str[i+1])) {
|
||||
throw new Error("Invalid property expression: unexpected array expression at position "+start);
|
||||
}
|
||||
start = i+1;
|
||||
inString = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (inBox || inString) {
|
||||
throw new Error("Invalid property expression: unterminated expression");
|
||||
}
|
||||
if (start < length) {
|
||||
parts.push(str.substring(start));
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
function getMessageProperty(msg,expr) {
|
||||
var result = null;
|
||||
if (expr.indexOf('msg.')===0) {
|
||||
expr = expr.substring(4);
|
||||
}
|
||||
var msgPropParts = expr.split(".");
|
||||
msgPropParts.reduce(function(obj, i) {
|
||||
result = (typeof obj[i] !== "undefined" ? obj[i] : undefined);
|
||||
var msgPropParts = normalisePropertyExpression(expr);
|
||||
var m;
|
||||
msgPropParts.reduce(function(obj, key) {
|
||||
result = (typeof obj[key] !== "undefined" ? obj[key] : undefined);
|
||||
return result;
|
||||
}, msg);
|
||||
return result;
|
||||
@@ -147,30 +241,54 @@ function setMessageProperty(msg,prop,value,createMissing) {
|
||||
if (prop.indexOf('msg.')===0) {
|
||||
prop = prop.substring(4);
|
||||
}
|
||||
var msgPropParts = prop.split(".");
|
||||
var msgPropParts = normalisePropertyExpression(prop);
|
||||
var depth = 0;
|
||||
msgPropParts.reduce(function(obj, i) {
|
||||
if (obj === null) {
|
||||
return null;
|
||||
}
|
||||
depth++;
|
||||
if (depth === msgPropParts.length) {
|
||||
if (typeof value === "undefined") {
|
||||
delete obj[i];
|
||||
} else {
|
||||
obj[i] = value;
|
||||
}
|
||||
} else {
|
||||
if (!obj[i]) {
|
||||
if (createMissing) {
|
||||
obj[i] = {};
|
||||
var length = msgPropParts.length;
|
||||
var obj = msg;
|
||||
var key;
|
||||
for (var i=0;i<length-1;i++) {
|
||||
key = msgPropParts[i];
|
||||
if (typeof key === 'string' || (typeof key === 'number' && !Array.isArray(obj))) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
obj = obj[key];
|
||||
} else if (createMissing) {
|
||||
if (typeof msgPropParts[i+1] === 'string') {
|
||||
obj[key] = {};
|
||||
} else {
|
||||
return null;
|
||||
obj[key] = [];
|
||||
}
|
||||
obj = obj[key];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if (typeof key === 'number') {
|
||||
// obj is an array
|
||||
if (obj[key] === undefined) {
|
||||
if (createMissing) {
|
||||
if (typeof msgPropParts[i+1] === 'string') {
|
||||
obj[key] = {};
|
||||
} else {
|
||||
obj[key] = [];
|
||||
}
|
||||
obj = obj[key];
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
} else {
|
||||
obj = obj[key];
|
||||
}
|
||||
return obj[i];
|
||||
}
|
||||
}, msg);
|
||||
}
|
||||
key = msgPropParts[length-1];
|
||||
if (typeof value === "undefined") {
|
||||
if (typeof key === 'number' && Array.isArray(obj)) {
|
||||
obj.splice(key,1);
|
||||
} else {
|
||||
delete obj[key]
|
||||
}
|
||||
} else {
|
||||
obj[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
function evaluateNodeProperty(value, type, node, msg) {
|
||||
@@ -182,6 +300,8 @@ function evaluateNodeProperty(value, type, node, msg) {
|
||||
return JSON.parse(value);
|
||||
} else if (type === 're') {
|
||||
return new RegExp(value);
|
||||
} else if (type === 'date') {
|
||||
return Date.now();
|
||||
} else if (type === 'msg' && msg) {
|
||||
return getMessageProperty(msg,value);
|
||||
} else if (type === 'flow' && node) {
|
||||
@@ -203,5 +323,6 @@ module.exports = {
|
||||
generateId: generateId,
|
||||
getMessageProperty: getMessageProperty,
|
||||
setMessageProperty: setMessageProperty,
|
||||
evaluateNodeProperty: evaluateNodeProperty
|
||||
evaluateNodeProperty: evaluateNodeProperty,
|
||||
normalisePropertyExpression: normalisePropertyExpression
|
||||
};
|
||||
|
Reference in New Issue
Block a user