mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Merge branch 'dev' into pr_2042
This commit is contained in:
commit
5110eaff96
26
CHANGELOG.md
26
CHANGELOG.md
@ -2,7 +2,31 @@
|
|||||||
|
|
||||||
Runtime
|
Runtime
|
||||||
|
|
||||||
- Bump JSONata to 1.6.5
|
- Bump JSONata to 1.6.4
|
||||||
|
- Add Flow.getSetting for resolving env-var properties
|
||||||
|
- Refactor Subflow logic into own class
|
||||||
|
- Restore RED.auth to node-red module api
|
||||||
|
- Tidy up when usage in Flow and Node
|
||||||
|
|
||||||
|
Editor
|
||||||
|
|
||||||
|
- German translation
|
||||||
|
- Change default dropdown appearance and sidebar tab menu handling
|
||||||
|
- Handle multiple-select box when nothing selected Fixes #2021
|
||||||
|
- Handle i18n properly when key is a valid sub-identifier Fixes #2028
|
||||||
|
- Avoid duplicate links when missing node type installed Fixes #2032
|
||||||
|
- Add View Tools
|
||||||
|
- Don't collapse version control header when clicking refresh
|
||||||
|
- Add fast entry via keyboard for string of nodes
|
||||||
|
- Check for undeployed change before showing open project dialog
|
||||||
|
- Add placeholder node when in quick-add mode
|
||||||
|
- Move nodes to top-left corner when converting to subflow
|
||||||
|
|
||||||
|
Nodes
|
||||||
|
|
||||||
|
- Debug: Allow debug edit expression to be sent to status
|
||||||
|
- WebSocket: Fix missing translated help
|
||||||
|
|
||||||
|
|
||||||
#### 0.20.0-beta.3: Beta Release
|
#### 0.20.0-beta.3: Beta Release
|
||||||
|
|
||||||
|
14
Gruntfile.js
14
Gruntfile.js
@ -15,6 +15,7 @@
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
var path = require("path");
|
var path = require("path");
|
||||||
|
var fs = require("fs-extra");
|
||||||
|
|
||||||
module.exports = function(grunt) {
|
module.exports = function(grunt) {
|
||||||
|
|
||||||
@ -442,7 +443,9 @@ module.exports = function(grunt) {
|
|||||||
'packages/node_modules/@node-red/runtime/lib/api/*.js',
|
'packages/node_modules/@node-red/runtime/lib/api/*.js',
|
||||||
'packages/node_modules/@node-red/runtime/lib/events.js',
|
'packages/node_modules/@node-red/runtime/lib/events.js',
|
||||||
'packages/node_modules/@node-red/util/**/*.js',
|
'packages/node_modules/@node-red/util/**/*.js',
|
||||||
],
|
'packages/node_modules/@node-red/editor-api/lib/index.js',
|
||||||
|
'packages/node_modules/@node-red/editor-api/lib/auth/index.js'
|
||||||
|
],
|
||||||
options: {
|
options: {
|
||||||
destination: 'docs',
|
destination: 'docs',
|
||||||
configure: './jsdoc.json'
|
configure: './jsdoc.json'
|
||||||
@ -553,6 +556,13 @@ module.exports = function(grunt) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
grunt.registerTask('verifyUiTestDependencies', function() {
|
||||||
|
if (!fs.existsSync(path.join("node_modules", "chromedriver"))) {
|
||||||
|
grunt.fail.fatal('You need to run "npm install chromedriver@2" before running UI test.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
grunt.registerTask('setDevEnv',
|
grunt.registerTask('setDevEnv',
|
||||||
'Sets NODE_ENV=development so non-minified assets are used',
|
'Sets NODE_ENV=development so non-minified assets are used',
|
||||||
function () {
|
function () {
|
||||||
@ -573,7 +583,7 @@ module.exports = function(grunt) {
|
|||||||
|
|
||||||
grunt.registerTask('test-ui',
|
grunt.registerTask('test-ui',
|
||||||
'Builds editor content then runs unit tests on editor ui',
|
'Builds editor content then runs unit tests on editor ui',
|
||||||
['build','jshint:editor','webdriver:all']);
|
['verifyUiTestDependencies','build','jshint:editor','webdriver:all']);
|
||||||
|
|
||||||
grunt.registerTask('test-nodes',
|
grunt.registerTask('test-nodes',
|
||||||
'Runs unit tests on core nodes',
|
'Runs unit tests on core nodes',
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
},
|
},
|
||||||
"templates": {
|
"templates": {
|
||||||
"systemName": "Node-RED Runtime API",
|
"systemName": "Node-RED Runtime API",
|
||||||
"theme":"yeti",
|
|
||||||
"footer": "",
|
"footer": "",
|
||||||
"copyright": "Released under the Apache License v2.0",
|
"copyright": "Released under the Apache License v2.0",
|
||||||
"default": {
|
"default": {
|
||||||
|
12
package.json
12
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "node-red",
|
"name": "node-red",
|
||||||
"version": "0.20.0-beta.3",
|
"version": "0.20.0-beta.4",
|
||||||
"description": "A visual tool for wiring the Internet of Things",
|
"description": "A visual tool for wiring the Internet of Things",
|
||||||
"homepage": "http://nodered.org",
|
"homepage": "http://nodered.org",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
@ -24,7 +24,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ajv": "6.6.2",
|
"ajv": "6.7.0",
|
||||||
"basic-auth": "2.0.1",
|
"basic-auth": "2.0.1",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"body-parser": "1.18.3",
|
"body-parser": "1.18.3",
|
||||||
@ -56,7 +56,7 @@
|
|||||||
"node-red-node-feedparser": "^0.1.14",
|
"node-red-node-feedparser": "^0.1.14",
|
||||||
"node-red-node-rbe": "0.2.*",
|
"node-red-node-rbe": "0.2.*",
|
||||||
"node-red-node-sentiment": "^0.1.0",
|
"node-red-node-sentiment": "^0.1.0",
|
||||||
"node-red-node-tail": "^0.0.1",
|
"node-red-node-tail": "^0.0.2",
|
||||||
"node-red-node-twitter": "^1.1.0",
|
"node-red-node-twitter": "^1.1.0",
|
||||||
"nopt": "4.0.1",
|
"nopt": "4.0.1",
|
||||||
"oauth2orize": "1.11.0",
|
"oauth2orize": "1.11.0",
|
||||||
@ -67,17 +67,15 @@
|
|||||||
"raw-body": "2.3.3",
|
"raw-body": "2.3.3",
|
||||||
"request": "2.88.0",
|
"request": "2.88.0",
|
||||||
"semver": "5.6.0",
|
"semver": "5.6.0",
|
||||||
"sentiment": "2.1.0",
|
|
||||||
"uglify-js": "3.4.9",
|
"uglify-js": "3.4.9",
|
||||||
"when": "3.7.8",
|
"when": "3.7.8",
|
||||||
"ws": "6.1.2",
|
"ws": "6.1.3",
|
||||||
"xml2js": "0.4.19"
|
"xml2js": "0.4.19"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"bcrypt": "~2.0.0"
|
"bcrypt": "~2.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"chromedriver": "2.45.0",
|
|
||||||
"grunt": "~1.0.3",
|
"grunt": "~1.0.3",
|
||||||
"grunt-chmod": "~1.1.1",
|
"grunt-chmod": "~1.1.1",
|
||||||
"grunt-cli": "~1.3.2",
|
"grunt-cli": "~1.3.2",
|
||||||
@ -107,7 +105,7 @@
|
|||||||
"should": "^8.4.0",
|
"should": "^8.4.0",
|
||||||
"sinon": "1.17.7",
|
"sinon": "1.17.7",
|
||||||
"stoppable": "^1.1.0",
|
"stoppable": "^1.1.0",
|
||||||
"supertest": "3.3.0",
|
"supertest": "3.4.2",
|
||||||
"wdio-chromedriver-service": "^0.1.5",
|
"wdio-chromedriver-service": "^0.1.5",
|
||||||
"wdio-mocha-framework": "^0.6.4",
|
"wdio-mocha-framework": "^0.6.4",
|
||||||
"wdio-spec-reporter": "^0.1.5",
|
"wdio-spec-reporter": "^0.1.5",
|
||||||
|
@ -14,6 +14,11 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @mixin @node-red/editor-api_auth
|
||||||
|
*/
|
||||||
|
|
||||||
var passport = require("passport");
|
var passport = require("passport");
|
||||||
var oauth2orize = require("oauth2orize");
|
var oauth2orize = require("oauth2orize");
|
||||||
|
|
||||||
@ -44,7 +49,14 @@ function init(_settings,storage) {
|
|||||||
Tokens.init(mergedAdminAuth,storage);
|
Tokens.init(mergedAdminAuth,storage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Returns an Express middleware function that ensures the user making a request
|
||||||
|
* has the necessary permission.
|
||||||
|
*
|
||||||
|
* @param {String} permission - the permission required for the request, such as `flows.write`
|
||||||
|
* @return {Function} - an Express middleware
|
||||||
|
* @memberof @node-red/editor-api_auth
|
||||||
|
*/
|
||||||
function needsPermission(permission) {
|
function needsPermission(permission) {
|
||||||
return function(req,res,next) {
|
return function(req,res,next) {
|
||||||
if (settings && settings.adminAuth) {
|
if (settings && settings.adminAuth) {
|
||||||
|
@ -14,6 +14,16 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This module provides an Express application to serve the Node-RED editor.
|
||||||
|
*
|
||||||
|
* It implements the Node-RED HTTP Admin API the Editor uses to interact
|
||||||
|
* with the Node-RED runtime.
|
||||||
|
*
|
||||||
|
* @namespace @node-red/editor-api
|
||||||
|
*/
|
||||||
|
|
||||||
var express = require("express");
|
var express = require("express");
|
||||||
var bodyParser = require("body-parser");
|
var bodyParser = require("body-parser");
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
@ -28,6 +38,15 @@ var adminApp;
|
|||||||
var server;
|
var server;
|
||||||
var editor;
|
var editor;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise the module.
|
||||||
|
* @param {Object} settings The runtime settings
|
||||||
|
* @param {HTTPServer} server An instance of HTTP Server
|
||||||
|
* @param {Storage} storage An instance of Node-RED Storage
|
||||||
|
* @param {Runtime} runtimeAPI An instance of Node-RED Runtime
|
||||||
|
* @memberof @node-red/editor-api
|
||||||
|
*/
|
||||||
function init(settings,_server,storage,runtimeAPI) {
|
function init(settings,_server,storage,runtimeAPI) {
|
||||||
server = _server;
|
server = _server;
|
||||||
if (settings.httpAdminRoot !== false) {
|
if (settings.httpAdminRoot !== false) {
|
||||||
@ -80,6 +99,12 @@ function init(settings,_server,storage,runtimeAPI) {
|
|||||||
adminApp = null;
|
adminApp = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the module.
|
||||||
|
* @return {Promise} resolves when the application is ready to handle requests
|
||||||
|
* @memberof @node-red/editor-api
|
||||||
|
*/
|
||||||
function start() {
|
function start() {
|
||||||
if (editor) {
|
if (editor) {
|
||||||
return editor.start();
|
return editor.start();
|
||||||
@ -87,6 +112,12 @@ function start() {
|
|||||||
return when.resolve();
|
return when.resolve();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the module.
|
||||||
|
* @return {Promise} resolves when the application is stopped
|
||||||
|
* @memberof @node-red/editor-api
|
||||||
|
*/
|
||||||
function stop() {
|
function stop() {
|
||||||
if (editor) {
|
if (editor) {
|
||||||
editor.stop();
|
editor.stop();
|
||||||
@ -97,8 +128,18 @@ module.exports = {
|
|||||||
init: init,
|
init: init,
|
||||||
start: start,
|
start: start,
|
||||||
stop: stop,
|
stop: stop,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @memberof @node-red/editor-api
|
||||||
|
* @mixes @node-red/editor-api_auth
|
||||||
|
*/
|
||||||
auth: {
|
auth: {
|
||||||
needsPermission: auth.needsPermission
|
needsPermission: auth.needsPermission
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* The Express app used to serve the Node-RED Editor
|
||||||
|
* @type ExpressApplication
|
||||||
|
* @memberof @node-red/editor-api
|
||||||
|
*/
|
||||||
get httpAdmin() { return adminApp; }
|
get httpAdmin() { return adminApp; }
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/editor-api",
|
"name": "@node-red/editor-api",
|
||||||
"version": "0.20.0-beta.3",
|
"version": "0.20.0-beta.4",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@ -16,8 +16,8 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@node-red/util": "0.20.0-beta.3",
|
"@node-red/util": "0.20.0-beta.4",
|
||||||
"@node-red/editor-client": "0.20.0-beta.3",
|
"@node-red/editor-client": "0.20.0-beta.4",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"body-parser": "1.18.3",
|
"body-parser": "1.18.3",
|
||||||
"clone": "2.1.2",
|
"clone": "2.1.2",
|
||||||
@ -32,6 +32,6 @@
|
|||||||
"passport-oauth2-client-password": "0.1.2",
|
"passport-oauth2-client-password": "0.1.2",
|
||||||
"passport": "0.4.0",
|
"passport": "0.4.0",
|
||||||
"when": "3.7.8",
|
"when": "3.7.8",
|
||||||
"ws": "6.1.2"
|
"ws": "6.1.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -273,6 +273,7 @@
|
|||||||
"editSubflowProperties": "edit properties",
|
"editSubflowProperties": "edit properties",
|
||||||
"input": "inputs:",
|
"input": "inputs:",
|
||||||
"output": "outputs:",
|
"output": "outputs:",
|
||||||
|
"status": "status node",
|
||||||
"deleteSubflow": "delete subflow",
|
"deleteSubflow": "delete subflow",
|
||||||
"info": "Description",
|
"info": "Description",
|
||||||
"category": "Category",
|
"category": "Category",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/editor-client",
|
"name": "@node-red/editor-client",
|
||||||
"version": "0.20.0-beta.3",
|
"version": "0.20.0-beta.4",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -125,14 +125,20 @@ RED.history = (function() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ev.subflow && ev.subflow.hasOwnProperty('instances')) {
|
if (ev.subflow) {
|
||||||
ev.subflow.instances.forEach(function(n) {
|
if (ev.subflow.hasOwnProperty('instances')) {
|
||||||
var node = RED.nodes.node(n.id);
|
ev.subflow.instances.forEach(function(n) {
|
||||||
if (node) {
|
var node = RED.nodes.node(n.id);
|
||||||
node.changed = n.changed;
|
if (node) {
|
||||||
node.dirty = true;
|
node.changed = n.changed;
|
||||||
}
|
node.dirty = true;
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (ev.subflow.hasOwnProperty('status')) {
|
||||||
|
subflow = RED.nodes.subflow(ev.subflow.id);
|
||||||
|
subflow.status = ev.subflow.status;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (subflow) {
|
if (subflow) {
|
||||||
RED.nodes.filterNodes({type:"subflow:"+subflow.id}).forEach(function(n) {
|
RED.nodes.filterNodes({type:"subflow:"+subflow.id}).forEach(function(n) {
|
||||||
@ -232,6 +238,11 @@ RED.history = (function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (ev.subflow.hasOwnProperty('status')) {
|
||||||
|
if (ev.subflow.status) {
|
||||||
|
delete ev.node.status;
|
||||||
|
}
|
||||||
|
}
|
||||||
RED.editor.validateNode(ev.node);
|
RED.editor.validateNode(ev.node);
|
||||||
RED.nodes.filterNodes({type:"subflow:"+ev.node.id}).forEach(function(n) {
|
RED.nodes.filterNodes({type:"subflow:"+ev.node.id}).forEach(function(n) {
|
||||||
n.inputs = ev.node.in.length;
|
n.inputs = ev.node.in.length;
|
||||||
@ -262,6 +273,8 @@ RED.history = (function() {
|
|||||||
} else if (ev.t == "createSubflow") {
|
} else if (ev.t == "createSubflow") {
|
||||||
if (ev.nodes) {
|
if (ev.nodes) {
|
||||||
RED.nodes.filterNodes({z:ev.subflow.subflow.id}).forEach(function(n) {
|
RED.nodes.filterNodes({z:ev.subflow.subflow.id}).forEach(function(n) {
|
||||||
|
n.x += ev.subflow.offsetX;
|
||||||
|
n.y += ev.subflow.offsetY;
|
||||||
n.z = ev.activeWorkspace;
|
n.z = ev.activeWorkspace;
|
||||||
n.dirty = true;
|
n.dirty = true;
|
||||||
});
|
});
|
||||||
@ -288,6 +301,7 @@ RED.history = (function() {
|
|||||||
RED.workspaces.order(ev.order);
|
RED.workspaces.order(ev.order);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(modifiedTabs).forEach(function(id) {
|
Object.keys(modifiedTabs).forEach(function(id) {
|
||||||
var subflow = RED.nodes.subflow(id);
|
var subflow = RED.nodes.subflow(id);
|
||||||
if (subflow) {
|
if (subflow) {
|
||||||
@ -301,6 +315,7 @@ RED.history = (function() {
|
|||||||
RED.palette.refresh();
|
RED.palette.refresh();
|
||||||
RED.workspaces.refresh();
|
RED.workspaces.refresh();
|
||||||
RED.sidebar.config.refresh();
|
RED.sidebar.config.refresh();
|
||||||
|
RED.subflow.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -575,6 +575,18 @@ RED.nodes = (function() {
|
|||||||
node.icon = n.icon;
|
node.icon = n.icon;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (n.status) {
|
||||||
|
node.status = {x: n.status.x, y: n.status.y, wires:[]};
|
||||||
|
links.forEach(function(d) {
|
||||||
|
if (d.target === n.status) {
|
||||||
|
if (d.source.type != "subflow") {
|
||||||
|
node.status.wires.push({id:d.source.id, port:d.sourcePort})
|
||||||
|
} else {
|
||||||
|
node.status.wires.push({id:n.id, port:0})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@ -855,6 +867,12 @@ RED.nodes = (function() {
|
|||||||
output.i = i;
|
output.i = i;
|
||||||
output.id = getID();
|
output.id = getID();
|
||||||
});
|
});
|
||||||
|
if (n.status) {
|
||||||
|
n.status.type = "subflow";
|
||||||
|
n.status.direction = "status";
|
||||||
|
n.status.z = n.id;
|
||||||
|
n.status.id = getID();
|
||||||
|
}
|
||||||
new_subflows.push(n);
|
new_subflows.push(n);
|
||||||
addSubflow(n,createNewIds);
|
addSubflow(n,createNewIds);
|
||||||
}
|
}
|
||||||
@ -1194,6 +1212,19 @@ RED.nodes = (function() {
|
|||||||
});
|
});
|
||||||
delete output.wires;
|
delete output.wires;
|
||||||
});
|
});
|
||||||
|
if (n.status) {
|
||||||
|
n.status.wires.forEach(function(wire) {
|
||||||
|
var link;
|
||||||
|
if (subflow_map[wire.id] && subflow_map[wire.id].id == n.id) {
|
||||||
|
link = {source:n.in[wire.port], sourcePort:wire.port,target:n.status};
|
||||||
|
} else {
|
||||||
|
link = {source:node_map[wire.id]||subflow_map[wire.id], sourcePort:wire.port,target:n.status};
|
||||||
|
}
|
||||||
|
addLink(link);
|
||||||
|
new_links.push(link);
|
||||||
|
});
|
||||||
|
delete n.status.wires;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RED.workspaces.refresh();
|
RED.workspaces.refresh();
|
||||||
|
@ -687,7 +687,7 @@ RED.diff = (function() {
|
|||||||
diff: remoteDiff
|
diff: remoteDiff
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var selectState = "";
|
var selectState = "";
|
||||||
|
|
||||||
if (conflicted) {
|
if (conflicted) {
|
||||||
@ -1158,19 +1158,19 @@ RED.diff = (function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
var diff = {
|
||||||
currentConfig: currentConfig,
|
currentConfig: currentConfig,
|
||||||
newConfig: newConfig,
|
newConfig: newConfig,
|
||||||
added: added,
|
added: added,
|
||||||
deleted: deleted,
|
deleted: deleted,
|
||||||
changed: changed,
|
changed: changed,
|
||||||
moved: moved
|
moved: moved
|
||||||
}
|
};
|
||||||
|
return diff;
|
||||||
}
|
}
|
||||||
function resolveDiffs(localDiff,remoteDiff) {
|
function resolveDiffs(localDiff,remoteDiff) {
|
||||||
var conflicted = {};
|
var conflicted = {};
|
||||||
var resolutions = {};
|
var resolutions = {};
|
||||||
|
|
||||||
var diff = {
|
var diff = {
|
||||||
localDiff: localDiff,
|
localDiff: localDiff,
|
||||||
remoteDiff: remoteDiff,
|
remoteDiff: remoteDiff,
|
||||||
@ -1348,7 +1348,7 @@ RED.diff = (function() {
|
|||||||
if (node) {
|
if (node) {
|
||||||
nodeChangedStates[id] = node.changed;
|
nodeChangedStates[id] = node.changed;
|
||||||
}
|
}
|
||||||
localChangedStates[id] = true;
|
localChangedStates[id] = 1;
|
||||||
newConfig.push(remoteDiff.newConfig.all[id]);
|
newConfig.push(remoteDiff.newConfig.all[id]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1363,7 +1363,7 @@ RED.diff = (function() {
|
|||||||
nodeChangedStates[id] = node.changed;
|
nodeChangedStates[id] = node.changed;
|
||||||
}
|
}
|
||||||
if (!localDiff.added.hasOwnProperty(id)) {
|
if (!localDiff.added.hasOwnProperty(id)) {
|
||||||
localChangedStates[id] = true;
|
localChangedStates[id] = 2;
|
||||||
newConfig.push(remoteDiff.newConfig.all[id]);
|
newConfig.push(remoteDiff.newConfig.all[id]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1376,24 +1376,42 @@ RED.diff = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function mergeDiff(diff) {
|
function mergeDiff(diff) {
|
||||||
|
//console.log(diff);
|
||||||
var appliedDiff = applyDiff(diff);
|
var appliedDiff = applyDiff(diff);
|
||||||
|
|
||||||
var newConfig = appliedDiff.config;
|
var newConfig = appliedDiff.config;
|
||||||
var nodeChangedStates = appliedDiff.nodeChangedStates;
|
var nodeChangedStates = appliedDiff.nodeChangedStates;
|
||||||
var localChangedStates = appliedDiff.localChangedStates;
|
var localChangedStates = appliedDiff.localChangedStates;
|
||||||
|
|
||||||
|
var isDirty = RED.nodes.dirty();
|
||||||
|
|
||||||
var historyEvent = {
|
var historyEvent = {
|
||||||
t:"replace",
|
t:"replace",
|
||||||
config: RED.nodes.createCompleteNodeSet(),
|
config: RED.nodes.createCompleteNodeSet(),
|
||||||
changed: nodeChangedStates,
|
changed: nodeChangedStates,
|
||||||
dirty: RED.nodes.dirty(),
|
dirty: isDirty,
|
||||||
rev: RED.nodes.version()
|
rev: RED.nodes.version()
|
||||||
}
|
}
|
||||||
|
|
||||||
RED.history.push(historyEvent);
|
RED.history.push(historyEvent);
|
||||||
|
|
||||||
|
var originalFlow = RED.nodes.originalFlow();
|
||||||
|
// originalFlow is what the editor things it loaded
|
||||||
|
// - add any newly added nodes from remote diff as they are now part of the record
|
||||||
|
for (var id in diff.remoteDiff.added) {
|
||||||
|
if (diff.remoteDiff.added.hasOwnProperty(id)) {
|
||||||
|
if (diff.remoteDiff.newConfig.all.hasOwnProperty(id)) {
|
||||||
|
originalFlow.push(JSON.parse(JSON.stringify(diff.remoteDiff.newConfig.all[id])));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RED.nodes.clear();
|
RED.nodes.clear();
|
||||||
var imported = RED.nodes.import(newConfig);
|
var imported = RED.nodes.import(newConfig);
|
||||||
|
|
||||||
|
// Restore the original flow so subsequent merge resolutions can properly
|
||||||
|
// identify new-vs-old
|
||||||
|
RED.nodes.originalFlow(originalFlow);
|
||||||
imported[0].forEach(function(n) {
|
imported[0].forEach(function(n) {
|
||||||
if (nodeChangedStates[n.id] || localChangedStates[n.id]) {
|
if (nodeChangedStates[n.id] || localChangedStates[n.id]) {
|
||||||
n.changed = true;
|
n.changed = true;
|
||||||
@ -1402,11 +1420,16 @@ RED.diff = (function() {
|
|||||||
|
|
||||||
RED.nodes.version(diff.remoteDiff.rev);
|
RED.nodes.version(diff.remoteDiff.rev);
|
||||||
|
|
||||||
|
if (isDirty) {
|
||||||
|
RED.nodes.dirty(true);
|
||||||
|
}
|
||||||
|
|
||||||
RED.view.redraw(true);
|
RED.view.redraw(true);
|
||||||
RED.palette.refresh();
|
RED.palette.refresh();
|
||||||
RED.workspaces.refresh();
|
RED.workspaces.refresh();
|
||||||
RED.sidebar.config.refresh();
|
RED.sidebar.config.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
function showTestFlowDiff(index) {
|
function showTestFlowDiff(index) {
|
||||||
if (index === 1) {
|
if (index === 1) {
|
||||||
var localFlow = RED.nodes.createCompleteNodeSet();
|
var localFlow = RED.nodes.createCompleteNodeSet();
|
||||||
|
@ -16,12 +16,12 @@
|
|||||||
|
|
||||||
RED.subflow = (function() {
|
RED.subflow = (function() {
|
||||||
|
|
||||||
|
|
||||||
var _subflowEditTemplate = '<script type="text/x-red" data-template-name="subflow">'+
|
var _subflowEditTemplate = '<script type="text/x-red" data-template-name="subflow">'+
|
||||||
'<div class="form-row"><label for="node-input-name" data-i18n="[append]editor:common.label.name"><i class="fa fa-tag"></i> </label><input type="text" id="node-input-name"></div>'+
|
'<div class="form-row"><label for="node-input-name" data-i18n="[append]editor:common.label.name"><i class="fa fa-tag"></i> </label><input type="text" id="node-input-name"></div>'+
|
||||||
'<div class="form-row" style="margin-bottom: 0px;"><label style="width: auto;" data-i18n="[append]editor:editor-tab.env"><i class="fa fa-th-list"></i> </label></div>'+
|
'<div class="form-row" style="margin-bottom: 0px;"><label style="width: auto;" data-i18n="[append]editor:editor-tab.env"><i class="fa fa-th-list"></i> </label></div>'+
|
||||||
'<div class="form-row node-input-env-container-row"><ol id="node-input-env-container"></ol></div>'+
|
'<div class="form-row node-input-env-container-row"><ol id="node-input-env-container"></ol></div>'+
|
||||||
'</script>';
|
'</script>';
|
||||||
|
|
||||||
var _subflowTemplateEditTemplate = '<script type="text/x-red" data-template-name="subflow-template">'+
|
var _subflowTemplateEditTemplate = '<script type="text/x-red" data-template-name="subflow-template">'+
|
||||||
'<div class="form-row"><i class="fa fa-tag"></i> <label for="subflow-input-name" data-i18n="common.label.name"></label><input type="text" id="subflow-input-name"></div>'+
|
'<div class="form-row"><i class="fa fa-tag"></i> <label for="subflow-input-name" data-i18n="common.label.name"></label><input type="text" id="subflow-input-name"></div>'+
|
||||||
'<div class="form-row"><i class="fa fa-folder-o"></i> <label for="subflow-input-category" data-i18n="editor:subflow.category"></label><select style="width: 250px;" id="subflow-input-category"></select><input style="display:none; margin-left: 10px; width:calc(100% - 250px)" type="text" id="subflow-input-custom-category"></div>'+
|
'<div class="form-row"><i class="fa fa-folder-o"></i> <label for="subflow-input-category" data-i18n="editor:subflow.category"></label><select style="width: 250px;" id="subflow-input-category"></select><input style="display:none; margin-left: 10px; width:calc(100% - 250px)" type="text" id="subflow-input-custom-category"></div>'+
|
||||||
@ -30,26 +30,22 @@ RED.subflow = (function() {
|
|||||||
'<div class="form-row form-tips" id="subflow-dialog-user-count"></div>'+
|
'<div class="form-row form-tips" id="subflow-dialog-user-count"></div>'+
|
||||||
'</script>';
|
'</script>';
|
||||||
|
|
||||||
|
|
||||||
function getSubflow() {
|
|
||||||
return RED.nodes.subflow(RED.workspaces.active());
|
|
||||||
}
|
|
||||||
|
|
||||||
function findAvailableSubflowIOPosition(subflow,isInput) {
|
function findAvailableSubflowIOPosition(subflow,isInput) {
|
||||||
var pos = {x:50,y:30};
|
var pos = {x:50,y:30};
|
||||||
if (!isInput) {
|
if (!isInput) {
|
||||||
pos.x += 110;
|
pos.x += 110;
|
||||||
}
|
}
|
||||||
for (var i=0;i<subflow.out.length+subflow.in.length;i++) {
|
var ports = [].concat(subflow.out).concat(subflow.in);
|
||||||
var port;
|
if (subflow.status) {
|
||||||
if (i < subflow.out.length) {
|
ports.push(subflow.status);
|
||||||
port = subflow.out[i];
|
}
|
||||||
} else {
|
ports.sort(function(A,B) {
|
||||||
port = subflow.in[i-subflow.out.length];
|
return A.x-B.x;
|
||||||
}
|
});
|
||||||
|
for (var i=0; i<ports.length; i++) {
|
||||||
|
var port = ports[i];
|
||||||
if (port.x == pos.x && port.y == pos.y) {
|
if (port.x == pos.x && port.y == pos.y) {
|
||||||
pos.x += 55;
|
pos.x += 55;
|
||||||
i=0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pos;
|
return pos;
|
||||||
@ -197,6 +193,61 @@ RED.subflow = (function() {
|
|||||||
return {subflowOutputs: removedSubflowOutputs, links: removedLinks}
|
return {subflowOutputs: removedSubflowOutputs, links: removedLinks}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addSubflowStatus() {
|
||||||
|
var subflow = RED.nodes.subflow(RED.workspaces.active());
|
||||||
|
if (subflow.status) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var position = findAvailableSubflowIOPosition(subflow,false);
|
||||||
|
var statusNode = {
|
||||||
|
type:"subflow",
|
||||||
|
direction:"status",
|
||||||
|
z:subflow.id,
|
||||||
|
x:position.x,
|
||||||
|
y:position.y,
|
||||||
|
id:RED.nodes.id()
|
||||||
|
};
|
||||||
|
subflow.status = statusNode;
|
||||||
|
subflow.dirty = true;
|
||||||
|
var wasDirty = RED.nodes.dirty();
|
||||||
|
var wasChanged = subflow.changed;
|
||||||
|
subflow.changed = true;
|
||||||
|
var result = refresh(true);
|
||||||
|
var historyEvent = {
|
||||||
|
t:'edit',
|
||||||
|
node:subflow,
|
||||||
|
dirty:wasDirty,
|
||||||
|
changed:wasChanged,
|
||||||
|
subflow: { status: true }
|
||||||
|
};
|
||||||
|
RED.history.push(historyEvent);
|
||||||
|
RED.view.select();
|
||||||
|
RED.nodes.dirty(true);
|
||||||
|
RED.view.redraw();
|
||||||
|
$("#workspace-subflow-status").prop("checked",!!subflow.status);
|
||||||
|
$("#workspace-subflow-status").parent().parent().toggleClass("active",!!subflow.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeSubflowStatus() {
|
||||||
|
var subflow = RED.nodes.subflow(RED.workspaces.active());
|
||||||
|
if (!subflow.status) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var subflowRemovedLinks = [];
|
||||||
|
RED.nodes.eachLink(function(l) {
|
||||||
|
if (l.target.type == "subflow" && l.target.z == subflow.id && l.target.direction == "status") {
|
||||||
|
subflowRemovedLinks.push(l);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
subflowRemovedLinks.forEach(function(l) { RED.nodes.removeLink(l)});
|
||||||
|
delete subflow.status;
|
||||||
|
|
||||||
|
$("#workspace-subflow-status").prop("checked",!!subflow.status);
|
||||||
|
$("#workspace-subflow-status").parent().parent().toggleClass("active",!!subflow.status);
|
||||||
|
|
||||||
|
return { links: subflowRemovedLinks }
|
||||||
|
}
|
||||||
|
|
||||||
function refresh(markChange) {
|
function refresh(markChange) {
|
||||||
var activeSubflow = RED.nodes.subflow(RED.workspaces.active());
|
var activeSubflow = RED.nodes.subflow(RED.workspaces.active());
|
||||||
refreshToolbar(activeSubflow);
|
refreshToolbar(activeSubflow);
|
||||||
@ -225,12 +276,17 @@ RED.subflow = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshToolbar(activeSubflow) {
|
function refreshToolbar(activeSubflow) {
|
||||||
if (activeSubflow) {
|
if (activeSubflow) {
|
||||||
$("#workspace-subflow-input-add").toggleClass("active", activeSubflow.in.length !== 0);
|
$("#workspace-subflow-input-add").toggleClass("active", activeSubflow.in.length !== 0);
|
||||||
$("#workspace-subflow-input-remove").toggleClass("active",activeSubflow.in.length === 0);
|
$("#workspace-subflow-input-remove").toggleClass("active",activeSubflow.in.length === 0);
|
||||||
|
|
||||||
$("#workspace-subflow-output .spinner-value").text(activeSubflow.out.length);
|
$("#workspace-subflow-output .spinner-value").text(activeSubflow.out.length);
|
||||||
|
|
||||||
|
$("#workspace-subflow-status").prop("checked",!!activeSubflow.status);
|
||||||
|
$("#workspace-subflow-status").parent().parent().toggleClass("active",!!activeSubflow.status);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,22 +294,32 @@ RED.subflow = (function() {
|
|||||||
var toolbar = $("#workspace-toolbar");
|
var toolbar = $("#workspace-toolbar");
|
||||||
toolbar.empty();
|
toolbar.empty();
|
||||||
|
|
||||||
|
// Edit properties
|
||||||
$('<a class="button" id="workspace-subflow-edit" href="#" data-i18n="[append]subflow.editSubflowProperties"><i class="fa fa-pencil"></i> </a>').appendTo(toolbar);
|
$('<a class="button" id="workspace-subflow-edit" href="#" data-i18n="[append]subflow.editSubflowProperties"><i class="fa fa-pencil"></i> </a>').appendTo(toolbar);
|
||||||
|
|
||||||
|
// Inputs
|
||||||
$('<span style="margin-left: 5px;" data-i18n="subflow.input"></span> '+
|
$('<span style="margin-left: 5px;" data-i18n="subflow.input"></span> '+
|
||||||
'<div style="display: inline-block;" class="button-group">'+
|
'<div style="display: inline-block;" class="button-group">'+
|
||||||
'<a id="workspace-subflow-input-remove" class="button active" href="#">0</a>'+
|
'<a id="workspace-subflow-input-remove" class="button active" href="#">0</a>'+
|
||||||
'<a id="workspace-subflow-input-add" class="button" href="#">1</a>'+
|
'<a id="workspace-subflow-input-add" class="button" href="#">1</a>'+
|
||||||
'</div>').appendTo(toolbar);
|
'</div>').appendTo(toolbar);
|
||||||
|
|
||||||
|
// Outputs
|
||||||
$('<span style="margin-left: 5px;" data-i18n="subflow.output"></span> <div id="workspace-subflow-output" style="display: inline-block;" class="button-group spinner-group">'+
|
$('<span style="margin-left: 5px;" data-i18n="subflow.output"></span> <div id="workspace-subflow-output" style="display: inline-block;" class="button-group spinner-group">'+
|
||||||
'<a id="workspace-subflow-output-remove" class="button" href="#"><i class="fa fa-minus"></i></a>'+
|
'<a id="workspace-subflow-output-remove" class="button" href="#"><i class="fa fa-minus"></i></a>'+
|
||||||
'<div class="spinner-value">3</div>'+
|
'<div class="spinner-value">3</div>'+
|
||||||
'<a id="workspace-subflow-output-add" class="button" href="#"><i class="fa fa-plus"></i></a>'+
|
'<a id="workspace-subflow-output-add" class="button" href="#"><i class="fa fa-plus"></i></a>'+
|
||||||
'</div>').appendTo(toolbar);
|
'</div>').appendTo(toolbar);
|
||||||
|
|
||||||
|
// Status
|
||||||
|
$('<span class="button-group"><span class="button" style="padding:0"><label for="workspace-subflow-status"><input id="workspace-subflow-status" type="checkbox"> <span data-i18n="subflow.status"></span></label></span></span>').appendTo(toolbar);
|
||||||
|
|
||||||
// $('<a class="button disabled" id="workspace-subflow-add-input" href="#" data-i18n="[append]subflow.input"><i class="fa fa-plus"></i> </a>').appendTo(toolbar);
|
// $('<a class="button disabled" id="workspace-subflow-add-input" href="#" data-i18n="[append]subflow.input"><i class="fa fa-plus"></i> </a>').appendTo(toolbar);
|
||||||
// $('<a class="button" id="workspace-subflow-add-output" href="#" data-i18n="[append]subflow.output"><i class="fa fa-plus"></i> </a>').appendTo(toolbar);
|
// $('<a class="button" id="workspace-subflow-add-output" href="#" data-i18n="[append]subflow.output"><i class="fa fa-plus"></i> </a>').appendTo(toolbar);
|
||||||
|
|
||||||
|
// Delete
|
||||||
$('<a class="button" id="workspace-subflow-delete" href="#" data-i18n="[append]subflow.deleteSubflow"><i class="fa fa-trash"></i> </a>').appendTo(toolbar);
|
$('<a class="button" id="workspace-subflow-delete" href="#" data-i18n="[append]subflow.deleteSubflow"><i class="fa fa-trash"></i> </a>').appendTo(toolbar);
|
||||||
|
|
||||||
toolbar.i18n();
|
toolbar.i18n();
|
||||||
|
|
||||||
|
|
||||||
@ -280,6 +346,7 @@ RED.subflow = (function() {
|
|||||||
RED.view.redraw(true);
|
RED.view.redraw(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#workspace-subflow-output-add").click(function(event) {
|
$("#workspace-subflow-output-add").click(function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
addSubflowOutput();
|
addSubflowOutput();
|
||||||
@ -289,6 +356,7 @@ RED.subflow = (function() {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
addSubflowInput();
|
addSubflowInput();
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#workspace-subflow-input-remove").click(function(event) {
|
$("#workspace-subflow-input-remove").click(function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
var wasDirty = RED.nodes.dirty();
|
var wasDirty = RED.nodes.dirty();
|
||||||
@ -313,6 +381,33 @@ RED.subflow = (function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#workspace-subflow-status").change(function(evt) {
|
||||||
|
if (this.checked) {
|
||||||
|
addSubflowStatus();
|
||||||
|
} else {
|
||||||
|
var currentStatus = activeSubflow.status;
|
||||||
|
var wasChanged = activeSubflow.changed;
|
||||||
|
var result = removeSubflowStatus();
|
||||||
|
if (result) {
|
||||||
|
activeSubflow.changed = true;
|
||||||
|
var wasDirty = RED.nodes.dirty();
|
||||||
|
RED.history.push({
|
||||||
|
t:'delete',
|
||||||
|
links:result.links,
|
||||||
|
changed: wasChanged,
|
||||||
|
dirty:wasDirty,
|
||||||
|
subflow: {
|
||||||
|
id: activeSubflow.id,
|
||||||
|
status: currentStatus
|
||||||
|
}
|
||||||
|
});
|
||||||
|
RED.view.select();
|
||||||
|
RED.nodes.dirty(true);
|
||||||
|
RED.view.redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
$("#workspace-subflow-edit").click(function(event) {
|
$("#workspace-subflow-edit").click(function(event) {
|
||||||
RED.editor.editSubflow(RED.nodes.subflow(RED.workspaces.active()));
|
RED.editor.editSubflow(RED.nodes.subflow(RED.workspaces.active()));
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -334,6 +429,7 @@ RED.subflow = (function() {
|
|||||||
$("#chart").css({"margin-top": "40px"});
|
$("#chart").css({"margin-top": "40px"});
|
||||||
$("#workspace-toolbar").show();
|
$("#workspace-toolbar").show();
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideWorkspaceToolbar() {
|
function hideWorkspaceToolbar() {
|
||||||
$("#workspace-toolbar").hide().empty();
|
$("#workspace-toolbar").hide().empty();
|
||||||
$("#chart").css({"margin-top": "0"});
|
$("#chart").css({"margin-top": "0"});
|
||||||
@ -379,6 +475,7 @@ RED.subflow = (function() {
|
|||||||
subflows: [activeSubflow]
|
subflows: [activeSubflow]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
RED.events.on("workspace:change",function(event) {
|
RED.events.on("workspace:change",function(event) {
|
||||||
var activeSubflow = RED.nodes.subflow(event.workspace);
|
var activeSubflow = RED.nodes.subflow(event.workspace);
|
||||||
@ -436,6 +533,13 @@ RED.subflow = (function() {
|
|||||||
RED.nodes.dirty(true);
|
RED.nodes.dirty(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function snapToGrid(x) {
|
||||||
|
if (RED.settings.get("editor").view['view-snap-grid']) {
|
||||||
|
x = Math.round(x / RED.view.gridSize()) * RED.view.gridSize();
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
function convertToSubflow() {
|
function convertToSubflow() {
|
||||||
var selection = RED.view.selection();
|
var selection = RED.view.selection();
|
||||||
if (!selection.nodes) {
|
if (!selection.nodes) {
|
||||||
@ -451,7 +555,6 @@ RED.subflow = (function() {
|
|||||||
var candidateOutputs = [];
|
var candidateOutputs = [];
|
||||||
var candidateInputNodes = {};
|
var candidateInputNodes = {};
|
||||||
|
|
||||||
|
|
||||||
var boundingBox = [selection.nodes[0].x,
|
var boundingBox = [selection.nodes[0].x,
|
||||||
selection.nodes[0].y,
|
selection.nodes[0].y,
|
||||||
selection.nodes[0].x,
|
selection.nodes[0].x,
|
||||||
@ -467,8 +570,14 @@ RED.subflow = (function() {
|
|||||||
Math.max(boundingBox[3],n.y)
|
Math.max(boundingBox[3],n.y)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
var offsetX = snapToGrid(boundingBox[0] - 200);
|
||||||
|
var offsetY = snapToGrid(boundingBox[1] - 80);
|
||||||
|
|
||||||
var center = [(boundingBox[2]+boundingBox[0]) / 2,(boundingBox[3]+boundingBox[1]) / 2];
|
|
||||||
|
var center = [
|
||||||
|
snapToGrid((boundingBox[2]+boundingBox[0]) / 2),
|
||||||
|
snapToGrid((boundingBox[3]+boundingBox[1]) / 2)
|
||||||
|
];
|
||||||
|
|
||||||
RED.nodes.eachLink(function(link) {
|
RED.nodes.eachLink(function(link) {
|
||||||
if (nodes[link.source.id] && nodes[link.target.id]) {
|
if (nodes[link.source.id] && nodes[link.target.id]) {
|
||||||
@ -525,8 +634,8 @@ RED.subflow = (function() {
|
|||||||
in: Object.keys(candidateInputNodes).map(function(v,i) { var index = i; return {
|
in: Object.keys(candidateInputNodes).map(function(v,i) { var index = i; return {
|
||||||
type:"subflow",
|
type:"subflow",
|
||||||
direction:"in",
|
direction:"in",
|
||||||
x:candidateInputNodes[v].x-(candidateInputNodes[v].w/2)-80,
|
x:snapToGrid(candidateInputNodes[v].x-(candidateInputNodes[v].w/2)-80 - offsetX),
|
||||||
y:candidateInputNodes[v].y,
|
y:snapToGrid(candidateInputNodes[v].y - offsetY),
|
||||||
z:subflowId,
|
z:subflowId,
|
||||||
i:index,
|
i:index,
|
||||||
id:RED.nodes.id(),
|
id:RED.nodes.id(),
|
||||||
@ -535,8 +644,8 @@ RED.subflow = (function() {
|
|||||||
out: candidateOutputs.map(function(v,i) { var index = i; return {
|
out: candidateOutputs.map(function(v,i) { var index = i; return {
|
||||||
type:"subflow",
|
type:"subflow",
|
||||||
direction:"in",
|
direction:"in",
|
||||||
x:v.source.x+(v.source.w/2)+80,
|
x:snapToGrid(v.source.x+(v.source.w/2)+80 - offsetX),
|
||||||
y:v.source.y,
|
y:snapToGrid(v.source.y - offsetY),
|
||||||
z:subflowId,
|
z:subflowId,
|
||||||
i:index,
|
i:index,
|
||||||
id:RED.nodes.id(),
|
id:RED.nodes.id(),
|
||||||
@ -611,6 +720,8 @@ RED.subflow = (function() {
|
|||||||
return isLocalLink;
|
return isLocalLink;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
n.x -= offsetX;
|
||||||
|
n.y -= offsetY;
|
||||||
n.z = subflow.id;
|
n.z = subflow.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -619,7 +730,9 @@ RED.subflow = (function() {
|
|||||||
nodes:[subflowInstance.id],
|
nodes:[subflowInstance.id],
|
||||||
links:new_links,
|
links:new_links,
|
||||||
subflow: {
|
subflow: {
|
||||||
subflow: subflow
|
subflow: subflow,
|
||||||
|
offsetX: offsetX,
|
||||||
|
offsetY: offsetY
|
||||||
},
|
},
|
||||||
|
|
||||||
activeWorkspace: RED.workspaces.active(),
|
activeWorkspace: RED.workspaces.active(),
|
||||||
@ -633,8 +746,6 @@ RED.subflow = (function() {
|
|||||||
RED.view.redraw(true);
|
RED.view.redraw(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
init: init,
|
init: init,
|
||||||
createSubflow: createSubflow,
|
createSubflow: createSubflow,
|
||||||
@ -642,6 +753,7 @@ RED.subflow = (function() {
|
|||||||
removeSubflow: removeSubflow,
|
removeSubflow: removeSubflow,
|
||||||
refresh: refresh,
|
refresh: refresh,
|
||||||
removeInput: removeSubflowInput,
|
removeInput: removeSubflowInput,
|
||||||
removeOutput: removeSubflowOutput
|
removeOutput: removeSubflowOutput,
|
||||||
|
removeStatus: removeSubflowStatus
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
@ -1261,6 +1261,13 @@ RED.view = (function() {
|
|||||||
moving_set.push({n:n});
|
moving_set.push({n:n});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (activeSubflow.status) {
|
||||||
|
activeSubflow.status.selected = (activeSubflow.status.x > x && activeSubflow.status.x < x2 && activeSubflow.status.y > y && activeSubflow.status.y < y2);
|
||||||
|
if (activeSubflow.status.selected) {
|
||||||
|
activeSubflow.status.dirty = true;
|
||||||
|
moving_set.push({n:activeSubflow.status});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
updateSelection();
|
updateSelection();
|
||||||
lasso.remove();
|
lasso.remove();
|
||||||
@ -1367,6 +1374,13 @@ RED.view = (function() {
|
|||||||
moving_set.push({n:n});
|
moving_set.push({n:n});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (activeSubflow.status) {
|
||||||
|
if (!activeSubflow.status.selected) {
|
||||||
|
activeSubflow.status.selected = true;
|
||||||
|
activeSubflow.status.dirty = true;
|
||||||
|
moving_set.push({n:activeSubflow.status});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selected_link = null;
|
selected_link = null;
|
||||||
@ -1552,6 +1566,7 @@ RED.view = (function() {
|
|||||||
var removedLinks = [];
|
var removedLinks = [];
|
||||||
var removedSubflowOutputs = [];
|
var removedSubflowOutputs = [];
|
||||||
var removedSubflowInputs = [];
|
var removedSubflowInputs = [];
|
||||||
|
var removedSubflowStatus = undefined;
|
||||||
var subflowInstances = [];
|
var subflowInstances = [];
|
||||||
|
|
||||||
var startDirty = RED.nodes.dirty();
|
var startDirty = RED.nodes.dirty();
|
||||||
@ -1573,6 +1588,8 @@ RED.view = (function() {
|
|||||||
removedSubflowOutputs.push(node);
|
removedSubflowOutputs.push(node);
|
||||||
} else if (node.direction === "in") {
|
} else if (node.direction === "in") {
|
||||||
removedSubflowInputs.push(node);
|
removedSubflowInputs.push(node);
|
||||||
|
} else if (node.direction === "status") {
|
||||||
|
removedSubflowStatus = node;
|
||||||
}
|
}
|
||||||
node.dirty = true;
|
node.dirty = true;
|
||||||
}
|
}
|
||||||
@ -1590,12 +1607,19 @@ RED.view = (function() {
|
|||||||
removedLinks = removedLinks.concat(result.links);
|
removedLinks = removedLinks.concat(result.links);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (removedSubflowStatus) {
|
||||||
|
result = RED.subflow.removeStatus();
|
||||||
|
if (result) {
|
||||||
|
removedLinks = removedLinks.concat(result.links);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var instances = RED.subflow.refresh(true);
|
var instances = RED.subflow.refresh(true);
|
||||||
if (instances) {
|
if (instances) {
|
||||||
subflowInstances = instances.instances;
|
subflowInstances = instances.instances;
|
||||||
}
|
}
|
||||||
moving_set = [];
|
moving_set = [];
|
||||||
if (removedNodes.length > 0 || removedSubflowOutputs.length > 0 || removedSubflowInputs.length > 0) {
|
if (removedNodes.length > 0 || removedSubflowOutputs.length > 0 || removedSubflowInputs.length > 0 || removedSubflowStatus) {
|
||||||
RED.nodes.dirty(true);
|
RED.nodes.dirty(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1651,10 +1675,14 @@ RED.view = (function() {
|
|||||||
subflowOutputs:removedSubflowOutputs,
|
subflowOutputs:removedSubflowOutputs,
|
||||||
subflowInputs:removedSubflowInputs,
|
subflowInputs:removedSubflowInputs,
|
||||||
subflow: {
|
subflow: {
|
||||||
|
id: activeSubflow?activeSubflow.id:undefined,
|
||||||
instances: subflowInstances
|
instances: subflowInstances
|
||||||
},
|
},
|
||||||
dirty:startDirty
|
dirty:startDirty
|
||||||
};
|
};
|
||||||
|
if (removedSubflowStatus) {
|
||||||
|
historyEvent.subflow.status = removedSubflowStatus;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
RED.history.push(historyEvent);
|
RED.history.push(historyEvent);
|
||||||
|
|
||||||
@ -2420,6 +2448,49 @@ RED.view = (function() {
|
|||||||
|
|
||||||
inGroup.append("svg:text").attr("class","port_label").attr("x",18).attr("y",20).style("font-size","10px").text("input");
|
inGroup.append("svg:text").attr("class","port_label").attr("x",18).attr("y",20).style("font-size","10px").text("input");
|
||||||
|
|
||||||
|
var subflowStatus = nodeLayer.selectAll(".subflowstatus").data(activeSubflow.status?[activeSubflow.status]:[],function(d,i){ return d.id;});
|
||||||
|
subflowStatus.exit().remove();
|
||||||
|
|
||||||
|
var statusGroup = subflowStatus.enter().insert("svg:g").attr("class","node subflowstatus").attr("transform",function(d) { return "translate("+(d.x-20)+","+(d.y-20)+")"});
|
||||||
|
statusGroup.each(function(d,i) {
|
||||||
|
d.w=40;
|
||||||
|
d.h=40;
|
||||||
|
});
|
||||||
|
statusGroup.append("rect").attr("class","subflowport").attr("rx",8).attr("ry",8).attr("width",40).attr("height",40)
|
||||||
|
// TODO: This is exactly the same set of handlers used for regular nodes - DRY
|
||||||
|
.on("mouseup",nodeMouseUp)
|
||||||
|
.on("mousedown",nodeMouseDown)
|
||||||
|
.on("touchstart",function(d) {
|
||||||
|
var obj = d3.select(this);
|
||||||
|
var touch0 = d3.event.touches.item(0);
|
||||||
|
var pos = [touch0.pageX,touch0.pageY];
|
||||||
|
startTouchCenter = [touch0.pageX,touch0.pageY];
|
||||||
|
startTouchDistance = 0;
|
||||||
|
touchStartTime = setTimeout(function() {
|
||||||
|
showTouchMenu(obj,pos);
|
||||||
|
},touchLongPressTimeout);
|
||||||
|
nodeMouseDown.call(this,d)
|
||||||
|
})
|
||||||
|
.on("touchend", function(d) {
|
||||||
|
clearTimeout(touchStartTime);
|
||||||
|
touchStartTime = null;
|
||||||
|
if (RED.touch.radialMenu.active()) {
|
||||||
|
d3.event.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nodeMouseUp.call(this,d);
|
||||||
|
});
|
||||||
|
|
||||||
|
statusGroup.append("g").attr('transform','translate(-5,15)').append("rect").attr("class","port").attr("rx",3).attr("ry",3).attr("width",10).attr("height",10)
|
||||||
|
.on("mousedown", function(d,i){portMouseDown(d,PORT_TYPE_INPUT,0);} )
|
||||||
|
.on("touchstart", function(d,i){portMouseDown(d,PORT_TYPE_INPUT,0);} )
|
||||||
|
.on("mouseup", function(d,i){portMouseUp(d,PORT_TYPE_INPUT,0);})
|
||||||
|
.on("touchend",function(d,i){portMouseUp(d,PORT_TYPE_INPUT,0);} )
|
||||||
|
.on("mouseover",function(d){portMouseOver(d3.select(this),d,PORT_TYPE_INPUT,0);})
|
||||||
|
.on("mouseout",function(d){portMouseOut(d3.select(this),d,PORT_TYPE_INPUT,0);});
|
||||||
|
|
||||||
|
statusGroup.append("svg:text").attr("class","port_label").attr("x",22).attr("y",20).style("font-size","10px").text("status");
|
||||||
|
|
||||||
subflowOutputs.each(function(d,i) {
|
subflowOutputs.each(function(d,i) {
|
||||||
if (d.dirty) {
|
if (d.dirty) {
|
||||||
var output = d3.select(this);
|
var output = d3.select(this);
|
||||||
@ -2439,9 +2510,22 @@ RED.view = (function() {
|
|||||||
d.dirty = false;
|
d.dirty = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
subflowStatus.each(function(d,i) {
|
||||||
|
if (d.dirty) {
|
||||||
|
var output = d3.select(this);
|
||||||
|
output.selectAll(".subflowport").classed("node_selected",function(d) { return d.selected; })
|
||||||
|
output.selectAll(".port_index").text(function(d){ return d.i+1});
|
||||||
|
output.attr("transform", function(d) { return "translate(" + (d.x-d.w/2) + "," + (d.y-d.h/2) + ")"; });
|
||||||
|
dirtyNodes[d.id] = d;
|
||||||
|
d.dirty = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
nodeLayer.selectAll(".subflowoutput").remove();
|
nodeLayer.selectAll(".subflowoutput").remove();
|
||||||
nodeLayer.selectAll(".subflowinput").remove();
|
nodeLayer.selectAll(".subflowinput").remove();
|
||||||
|
nodeLayer.selectAll(".subflowstatus").remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
var node = nodeLayer.selectAll(".nodegroup").data(activeNodes,function(d){return d.id});
|
var node = nodeLayer.selectAll(".nodegroup").data(activeNodes,function(d){return d.id});
|
||||||
@ -2703,6 +2787,8 @@ RED.view = (function() {
|
|||||||
d.resize = false;
|
d.resize = false;
|
||||||
}
|
}
|
||||||
var thisNode = d3.select(this);
|
var thisNode = d3.select(this);
|
||||||
|
thisNode.classed("node_subflow",function(d) { return activeSubflow != null; })
|
||||||
|
|
||||||
//thisNode.selectAll(".centerDot").attr({"cx":function(d) { return d.w/2;},"cy":function(d){return d.h/2}});
|
//thisNode.selectAll(".centerDot").attr({"cx":function(d) { return d.w/2;},"cy":function(d){return d.h/2}});
|
||||||
thisNode.attr("transform", function(d) { return "translate(" + (d.x-d.w/2) + "," + (d.y-d.h/2) + ")"; });
|
thisNode.attr("transform", function(d) { return "translate(" + (d.x-d.w/2) + "," + (d.y-d.h/2) + ")"; });
|
||||||
if (mouse_mode != RED.state.MOVING_ACTIVE) {
|
if (mouse_mode != RED.state.MOVING_ACTIVE) {
|
||||||
@ -3004,6 +3090,9 @@ RED.view = (function() {
|
|||||||
links.each(function(d) {
|
links.each(function(d) {
|
||||||
var link = d3.select(this);
|
var link = d3.select(this);
|
||||||
if (d.added || d===selected_link || d.selected || dirtyNodes[d.source.id] || dirtyNodes[d.target.id]) {
|
if (d.added || d===selected_link || d.selected || dirtyNodes[d.source.id] || dirtyNodes[d.target.id]) {
|
||||||
|
if (/link_line/.test(link.attr('class'))) {
|
||||||
|
link.classed("link_subflow", function(d) { return !d.link && activeSubflow });
|
||||||
|
}
|
||||||
link.attr("d",function(d){
|
link.attr("d",function(d){
|
||||||
var numOutputs = d.source.outputs || 1;
|
var numOutputs = d.source.outputs || 1;
|
||||||
var sourcePort = d.sourcePort || 0;
|
var sourcePort = d.sourcePort || 0;
|
||||||
@ -3017,8 +3106,11 @@ RED.view = (function() {
|
|||||||
// " C "+(d.x1+scale*node_width)+" "+(d.y1+scaleY*node_height)+" "+
|
// " C "+(d.x1+scale*node_width)+" "+(d.y1+scaleY*node_height)+" "+
|
||||||
// (d.x2-scale*node_width)+" "+(d.y2-scaleY*node_height)+" "+
|
// (d.x2-scale*node_width)+" "+(d.y2-scaleY*node_height)+" "+
|
||||||
// d.x2+" "+d.y2;
|
// d.x2+" "+d.y2;
|
||||||
|
var path = generateLinkPath(d.x1,d.y1,d.x2,d.y2,1);
|
||||||
return generateLinkPath(d.x1,d.y1,d.x2,d.y2,1);
|
if (/NaN/.test(path)) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return path;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -33,6 +33,15 @@
|
|||||||
transition: right 0.2s ease;
|
transition: right 0.2s ease;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
label {
|
||||||
|
padding: 1px 8px;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
input[type="checkbox"] {
|
||||||
|
margin: 0 3px 0 0 ;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
.button {
|
.button {
|
||||||
@include workspace-button;
|
@include workspace-button;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
<!--
|
|
||||||
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.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<script type="text/x-red" data-help-name="sentiment">
|
|
||||||
<p> Analysiert die ausgewählte Eigenschaft aus dem <code>msg</code> Objekt und fügt ein <code>sentiment</code> Objekt hinzu. </p>
|
|
||||||
<h3> Ausgaben </h3>
|
|
||||||
<dl class="message-properties">
|
|
||||||
<dt> sentiment <span class="property-type"> Objekt </span> </dt>
|
|
||||||
<dd> enthält die resultierende Stimmungslage AFINN-111. </dd>
|
|
||||||
<dt> sentiment.score <span class="property-type"> Zahl </span> </dt>
|
|
||||||
<dd> die Sentiment-Bewertung. </dd>
|
|
||||||
</dl>
|
|
||||||
<h3> Eingaben </h3>
|
|
||||||
<dl class="message-properties">
|
|
||||||
<dt> überschreibt <span class="property-type"> Objekt </span> </dt>
|
|
||||||
<dd> Ein Objekt mit Wort-Überschreibungen kann angegeben werden- <code> { word:score, ... } </code>. </dd>
|
|
||||||
</dl>
|
|
||||||
<h3> Details </h3>
|
|
||||||
<p> Eine Bewertung größer als Null ist positiv und kleiner als null ist negativ. </p>
|
|
||||||
<p> Die Bewertung liegt in der Regel im Bereich von -5 bis +5, kann jedoch höher und niedriger sein. </p>
|
|
||||||
<p>See <a href="https://github.com/thisandagain/sentiment/blob/master/README.md" target="_blank">the Sentiment docs here</a>.</p>
|
|
||||||
</script>
|
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/nodes",
|
"name": "@node-red/nodes",
|
||||||
"version": "0.20.0-beta.3",
|
"version": "0.20.0-beta.4",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -15,7 +15,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ajv": "6.6.2",
|
"ajv": "6.7.0",
|
||||||
"body-parser": "1.18.3",
|
"body-parser": "1.18.3",
|
||||||
"cheerio": "0.22.0",
|
"cheerio": "0.22.0",
|
||||||
"cookie-parser": "1.4.3",
|
"cookie-parser": "1.4.3",
|
||||||
@ -36,8 +36,7 @@
|
|||||||
"on-headers": "1.0.1",
|
"on-headers": "1.0.1",
|
||||||
"raw-body": "2.3.3",
|
"raw-body": "2.3.3",
|
||||||
"request": "2.88.0",
|
"request": "2.88.0",
|
||||||
"sentiment": "2.1.0",
|
"ws": "6.1.3",
|
||||||
"ws": "6.1.2",
|
|
||||||
"xml2js": "0.4.19"
|
"xml2js": "0.4.19"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,16 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This module provides the node registry for the Node-RED runtime.
|
||||||
|
*
|
||||||
|
* It is responsible for loading node modules and making them available
|
||||||
|
* to the runtime.
|
||||||
|
*
|
||||||
|
* @namespace @node-red/registry
|
||||||
|
*/
|
||||||
|
|
||||||
var registry = require("./registry");
|
var registry = require("./registry");
|
||||||
var loader = require("./loader");
|
var loader = require("./loader");
|
||||||
var installer = require("./installer");
|
var installer = require("./installer");
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/registry",
|
"name": "@node-red/registry",
|
||||||
"version": "0.20.0-beta.3",
|
"version": "0.20.0-beta.4",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@ -16,7 +16,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@node-red/util": "0.20.0-beta.3",
|
"@node-red/util": "0.20.0-beta.4",
|
||||||
"semver": "5.6.0",
|
"semver": "5.6.0",
|
||||||
"uglify-js": "3.4.9",
|
"uglify-js": "3.4.9",
|
||||||
"when": "3.7.8"
|
"when": "3.7.8"
|
||||||
|
@ -38,7 +38,11 @@ function Node(n) {
|
|||||||
// Make this a non-enumerable property as it may cause
|
// Make this a non-enumerable property as it may cause
|
||||||
// circular references. Any existing code that tries to JSON serialise
|
// circular references. Any existing code that tries to JSON serialise
|
||||||
// the object (such as dashboard) will not like circular refs
|
// the object (such as dashboard) will not like circular refs
|
||||||
Object.defineProperty(this,'_flow', {value: n._flow, })
|
// The value must still be writable in the case that a node does:
|
||||||
|
// Object.assign(this,config)
|
||||||
|
// as part of its constructure - config._flow will overwrite this._flow
|
||||||
|
// which we can tolerate as they are the same object.
|
||||||
|
Object.defineProperty(this,'_flow', {value: n._flow, enumerable: false, writable: true })
|
||||||
}
|
}
|
||||||
this.updateWires(n.wires);
|
this.updateWires(n.wires);
|
||||||
}
|
}
|
||||||
|
@ -265,7 +265,6 @@ class Flow {
|
|||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the flow definition. This doesn't change anything that is running.
|
* Update the flow definition. This doesn't change anything that is running.
|
||||||
* This should be called after `stop` and before `start`.
|
* This should be called after `stop` and before `start`.
|
||||||
@ -281,11 +280,13 @@ class Flow {
|
|||||||
/**
|
/**
|
||||||
* Get a node instance from this flow. If the node is not known to this
|
* Get a node instance from this flow. If the node is not known to this
|
||||||
* flow, pass the request up to the parent.
|
* flow, pass the request up to the parent.
|
||||||
* @param {[type]} id [description]
|
* @param {String} id [description]
|
||||||
|
* @param {Boolean} cancelBubble if true, prevents the flow from passing the request to the parent
|
||||||
|
* This stops infinite loops when the parent asked this Flow for the
|
||||||
|
* node to begin with.
|
||||||
* @return {[type]} [description]
|
* @return {[type]} [description]
|
||||||
*/
|
*/
|
||||||
getNode(id) {
|
getNode(id, cancelBubble) {
|
||||||
// console.log('getNode',id,!!this.activeNodes[id])
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -298,7 +299,10 @@ class Flow {
|
|||||||
// TEMP: this is a subflow internal node within this flow
|
// TEMP: this is a subflow internal node within this flow
|
||||||
return this.activeNodes[id];
|
return this.activeNodes[id];
|
||||||
}
|
}
|
||||||
return this.parent.getNode(id);
|
if (!cancelBubble) {
|
||||||
|
return this.parent.getNode(id);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
const clone = require("clone");
|
const clone = require("clone");
|
||||||
const Flow = require('./Flow').Flow;
|
const Flow = require('./Flow').Flow;
|
||||||
|
|
||||||
|
const util = require("util");
|
||||||
|
|
||||||
const redUtil = require("@node-red/util").util;
|
const redUtil = require("@node-red/util").util;
|
||||||
const flowUtil = require("./util");
|
const flowUtil = require("./util");
|
||||||
|
|
||||||
@ -113,6 +115,40 @@ class Subflow extends Flow {
|
|||||||
var self = this;
|
var self = this;
|
||||||
// Create a subflow node to accept inbound messages and route appropriately
|
// Create a subflow node to accept inbound messages and route appropriately
|
||||||
var Node = require("../Node");
|
var Node = require("../Node");
|
||||||
|
|
||||||
|
if (this.subflowDef.status) {
|
||||||
|
var subflowStatusConfig = {
|
||||||
|
id: this.subflowInstance.id+":status",
|
||||||
|
type: "subflow-status",
|
||||||
|
z: this.subflowInstance.id,
|
||||||
|
_flow: this.parent
|
||||||
|
}
|
||||||
|
this.statusNode = new Node(subflowStatusConfig);
|
||||||
|
this.statusNode.on("input", function(msg) {
|
||||||
|
if (msg.payload !== undefined) {
|
||||||
|
if (typeof msg.payload === "string") {
|
||||||
|
// if msg.payload is a String, use it as status text
|
||||||
|
self.node.status({text:msg.payload})
|
||||||
|
return;
|
||||||
|
} else if (Object.prototype.toString.call(msg.payload) === "[object Object]") {
|
||||||
|
if (msg.payload.hasOwnProperty('text') || msg.payload.hasOwnProperty('fill') || msg.payload.hasOwnProperty('shape') || Object.keys(msg.payload).length === 0) {
|
||||||
|
// msg.payload is an object that looks like a status object
|
||||||
|
self.node.status(msg.payload);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Anything else - inspect it and use as status text
|
||||||
|
var text = util.inspect(msg.payload);
|
||||||
|
if (text.length > 32) { text = text.substr(0,32) + "..."; }
|
||||||
|
self.node.status({text:text});
|
||||||
|
} else if (msg.status !== undefined) {
|
||||||
|
// if msg.status exists
|
||||||
|
self.node.status(msg.status)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var subflowInstanceConfig = {
|
var subflowInstanceConfig = {
|
||||||
id: this.subflowInstance.id,
|
id: this.subflowInstance.id,
|
||||||
type: this.subflowInstance.type,
|
type: this.subflowInstance.type,
|
||||||
@ -177,7 +213,6 @@ class Subflow extends Flow {
|
|||||||
|
|
||||||
// Wire the subflow outputs
|
// Wire the subflow outputs
|
||||||
if (this.subflowDef.out) {
|
if (this.subflowDef.out) {
|
||||||
var modifiedNodes = {};
|
|
||||||
for (var i=0;i<this.subflowDef.out.length;i++) {
|
for (var i=0;i<this.subflowDef.out.length;i++) {
|
||||||
// i: the output index
|
// i: the output index
|
||||||
// This is what this Output is wired to
|
// This is what this Output is wired to
|
||||||
@ -189,7 +224,6 @@ class Subflow extends Flow {
|
|||||||
this.node._updateWires(subflowInstanceConfig.wires);
|
this.node._updateWires(subflowInstanceConfig.wires);
|
||||||
} else {
|
} else {
|
||||||
var node = self.node_map[wires[j].id];
|
var node = self.node_map[wires[j].id];
|
||||||
modifiedNodes[node.id] = node;
|
|
||||||
if (!node._originalWires) {
|
if (!node._originalWires) {
|
||||||
node._originalWires = clone(node.wires);
|
node._originalWires = clone(node.wires);
|
||||||
}
|
}
|
||||||
@ -198,6 +232,26 @@ class Subflow extends Flow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.subflowDef.status) {
|
||||||
|
var subflowStatusId = this.statusNode.id;
|
||||||
|
wires = this.subflowDef.status.wires;
|
||||||
|
for (var j=0;j<wires.length;j++) {
|
||||||
|
if (wires[j].id === this.subflowDef.id) {
|
||||||
|
// A subflow input wired straight to a subflow output
|
||||||
|
subflowInstanceConfig.wires[wires[j].port].push(subflowStatusId);
|
||||||
|
this.node._updateWires(subflowInstanceConfig.wires);
|
||||||
|
} else {
|
||||||
|
var node = self.node_map[wires[j].id];
|
||||||
|
if (!node._originalWires) {
|
||||||
|
node._originalWires = clone(node.wires);
|
||||||
|
}
|
||||||
|
node.wires[wires[j].port] = (node.wires[wires[j].port]||[]);
|
||||||
|
node.wires[wires[j].port].push(subflowStatusId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
super.start(diff);
|
super.start(diff);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,6 +281,23 @@ class Subflow extends Flow {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a node instance from this subflow.
|
||||||
|
* If the subflow has a status node, check for that, otherwise use
|
||||||
|
* the super-class function
|
||||||
|
* @param {String} id [description]
|
||||||
|
* @param {Boolean} cancelBubble if true, prevents the flow from passing the request to the parent
|
||||||
|
* This stops infinite loops when the parent asked this Flow for the
|
||||||
|
* node to begin with.
|
||||||
|
* @return {[type]} [description]
|
||||||
|
*/
|
||||||
|
getNode(id, cancelBubble) {
|
||||||
|
if (this.statusNode && this.statusNode.id === id) {
|
||||||
|
return this.statusNode;
|
||||||
|
}
|
||||||
|
return super.getNode(id,cancelBubble);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a status event from a node within this flow.
|
* Handle a status event from a node within this flow.
|
||||||
* @param {Node} node The original node that triggered the event
|
* @param {Node} node The original node that triggered the event
|
||||||
@ -240,10 +311,14 @@ class Subflow extends Flow {
|
|||||||
handleStatus(node,statusMessage,reportingNode,muteStatus) {
|
handleStatus(node,statusMessage,reportingNode,muteStatus) {
|
||||||
let handled = super.handleStatus(node,statusMessage,reportingNode,muteStatus);
|
let handled = super.handleStatus(node,statusMessage,reportingNode,muteStatus);
|
||||||
if (!handled) {
|
if (!handled) {
|
||||||
// No status node on this subflow caught the status message.
|
if (!this.statusNode || node === this.node) {
|
||||||
// Pass up to the parent with this subflow's instance as the
|
// No status node on this subflow caught the status message.
|
||||||
// reporting node
|
// AND there is no Subflow Status node - so the user isn't
|
||||||
handled = this.parent.handleStatus(node,statusMessage,this.node,true);
|
// wanting to manage status messages themselves
|
||||||
|
// Pass up to the parent with this subflow's instance as the
|
||||||
|
// reporting node
|
||||||
|
handled = this.parent.handleStatus(node,statusMessage,this.node,true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return handled;
|
return handled;
|
||||||
|
|
||||||
|
@ -207,11 +207,11 @@ function setFlows(_config,type,muteLog,forceStart) {
|
|||||||
function getNode(id) {
|
function getNode(id) {
|
||||||
var node;
|
var node;
|
||||||
if (activeNodesToFlow[id] && activeFlows[activeNodesToFlow[id]]) {
|
if (activeNodesToFlow[id] && activeFlows[activeNodesToFlow[id]]) {
|
||||||
return activeFlows[activeNodesToFlow[id]].getNode(id);
|
return activeFlows[activeNodesToFlow[id]].getNode(id,true);
|
||||||
}
|
}
|
||||||
for (var flowId in activeFlows) {
|
for (var flowId in activeFlows) {
|
||||||
if (activeFlows.hasOwnProperty(flowId)) {
|
if (activeFlows.hasOwnProperty(flowId)) {
|
||||||
node = activeFlows[flowId].getNode(id);
|
node = activeFlows[flowId].getNode(id,true);
|
||||||
if (node) {
|
if (node) {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/runtime",
|
"name": "@node-red/runtime",
|
||||||
"version": "0.20.0-beta.3",
|
"version": "0.20.0-beta.4",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@ -16,8 +16,8 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@node-red/registry": "0.20.0-beta.3",
|
"@node-red/registry": "0.20.0-beta.4",
|
||||||
"@node-red/util": "0.20.0-beta.3",
|
"@node-red/util": "0.20.0-beta.4",
|
||||||
"clone": "2.1.2",
|
"clone": "2.1.2",
|
||||||
"express": "4.16.4",
|
"express": "4.16.4",
|
||||||
"fs-extra": "7.0.1",
|
"fs-extra": "7.0.1",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/util",
|
"name": "@node-red/util",
|
||||||
"version": "0.20.0-beta.3",
|
"version": "0.20.0-beta.4",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
17
packages/node_modules/node-red/lib/red.js
vendored
17
packages/node_modules/node-red/lib/red.js
vendored
@ -122,6 +122,13 @@ module.exports = {
|
|||||||
*/
|
*/
|
||||||
util: redUtil.util,
|
util: redUtil.util,
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This provides access to the internal nodes module of the
|
||||||
|
* runtime.
|
||||||
|
*
|
||||||
|
* @memberof node-red
|
||||||
|
*/
|
||||||
get nodes() { return runtime._.nodes },
|
get nodes() { return runtime._.nodes },
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -131,6 +138,12 @@ module.exports = {
|
|||||||
*/
|
*/
|
||||||
events: runtime.events,
|
events: runtime.events,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This provides access to the internal settings module of the
|
||||||
|
* runtime.
|
||||||
|
*
|
||||||
|
* @memberof node-red
|
||||||
|
*/
|
||||||
get settings() { return runtime._.settings },
|
get settings() { return runtime._.settings },
|
||||||
|
|
||||||
|
|
||||||
@ -145,18 +158,21 @@ module.exports = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The express application for the Editor Admin API
|
* The express application for the Editor Admin API
|
||||||
|
* @type ExpressApplication
|
||||||
* @memberof node-red
|
* @memberof node-red
|
||||||
*/
|
*/
|
||||||
get httpAdmin() { return api.httpAdmin },
|
get httpAdmin() { return api.httpAdmin },
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The express application for HTTP Nodes
|
* The express application for HTTP Nodes
|
||||||
|
* @type ExpressApplication
|
||||||
* @memberof node-red
|
* @memberof node-red
|
||||||
*/
|
*/
|
||||||
get httpNode() { return runtime.httpNode },
|
get httpNode() { return runtime.httpNode },
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The HTTP Server used by the runtime
|
* The HTTP Server used by the runtime
|
||||||
|
* @type HTTPServer
|
||||||
* @memberof node-red
|
* @memberof node-red
|
||||||
*/
|
*/
|
||||||
get server() { return server },
|
get server() { return server },
|
||||||
@ -170,6 +186,7 @@ module.exports = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor authentication api.
|
* The editor authentication api.
|
||||||
|
* @see @node-red/editor-api_auth
|
||||||
* @memberof node-red
|
* @memberof node-red
|
||||||
*/
|
*/
|
||||||
auth: api.auth
|
auth: api.auth
|
||||||
|
12
packages/node_modules/node-red/package.json
vendored
12
packages/node_modules/node-red/package.json
vendored
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "node-red",
|
"name": "node-red",
|
||||||
"version": "0.20.0-beta.3",
|
"version": "0.20.0-beta.4",
|
||||||
"description": "A visual tool for wiring the Internet of Things",
|
"description": "A visual tool for wiring the Internet of Things",
|
||||||
"homepage": "http://nodered.org",
|
"homepage": "http://nodered.org",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
@ -31,10 +31,10 @@
|
|||||||
"flow"
|
"flow"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@node-red/editor-api": "0.20.0-beta.3",
|
"@node-red/editor-api": "0.20.0-beta.4",
|
||||||
"@node-red/runtime": "0.20.0-beta.3",
|
"@node-red/runtime": "0.20.0-beta.4",
|
||||||
"@node-red/util": "0.20.0-beta.3",
|
"@node-red/util": "0.20.0-beta.4",
|
||||||
"@node-red/nodes": "0.20.0-beta.3",
|
"@node-red/nodes": "0.20.0-beta.4",
|
||||||
"basic-auth": "2.0.1",
|
"basic-auth": "2.0.1",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"express": "4.16.4",
|
"express": "4.16.4",
|
||||||
@ -43,7 +43,7 @@
|
|||||||
"node-red-node-feedparser": "^0.1.14",
|
"node-red-node-feedparser": "^0.1.14",
|
||||||
"node-red-node-rbe": "0.2.*",
|
"node-red-node-rbe": "0.2.*",
|
||||||
"node-red-node-sentiment": "^0.1.0",
|
"node-red-node-sentiment": "^0.1.0",
|
||||||
"node-red-node-tail": "^0.0.1",
|
"node-red-node-tail": "^0.0.2",
|
||||||
"node-red-node-twitter": "^1.1.0",
|
"node-red-node-twitter": "^1.1.0",
|
||||||
"nopt": "4.0.1",
|
"nopt": "4.0.1",
|
||||||
"semver": "5.6.0"
|
"semver": "5.6.0"
|
||||||
|
@ -413,6 +413,69 @@ describe('Flow', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
describe('#getNode',function() {
|
||||||
|
it("gets a node known to the flow",function(done) {
|
||||||
|
var config = flowUtils.parseConfig([
|
||||||
|
{id:"t1",type:"tab"},
|
||||||
|
{id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]},
|
||||||
|
{id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]},
|
||||||
|
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
|
||||||
|
{id:"4",z:"t1",type:"test",foo:"a"}
|
||||||
|
]);
|
||||||
|
var flow = Flow.create({},config,config.flows["t1"]);
|
||||||
|
flow.start();
|
||||||
|
|
||||||
|
Object.keys(flow.getActiveNodes()).should.have.length(4);
|
||||||
|
|
||||||
|
flow.getNode('1').should.have.a.property('id','1');
|
||||||
|
|
||||||
|
flow.stop().then(() => { done() });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes to parent if node not known locally",function(done) {
|
||||||
|
var config = flowUtils.parseConfig([
|
||||||
|
{id:"t1",type:"tab"},
|
||||||
|
{id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]},
|
||||||
|
{id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]},
|
||||||
|
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
|
||||||
|
{id:"4",z:"t1",type:"test",foo:"a"}
|
||||||
|
]);
|
||||||
|
var flow = Flow.create({
|
||||||
|
getNode: id => { return {id:id}}
|
||||||
|
},config,config.flows["t1"]);
|
||||||
|
flow.start();
|
||||||
|
|
||||||
|
Object.keys(flow.getActiveNodes()).should.have.length(4);
|
||||||
|
|
||||||
|
flow.getNode('1').should.have.a.property('id','1');
|
||||||
|
|
||||||
|
flow.getNode('parentNode').should.have.a.property('id','parentNode');
|
||||||
|
|
||||||
|
|
||||||
|
flow.stop().then(() => { done() });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not pass to parent if cancelBubble set",function(done) {
|
||||||
|
var config = flowUtils.parseConfig([
|
||||||
|
{id:"t1",type:"tab"},
|
||||||
|
{id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]},
|
||||||
|
{id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]},
|
||||||
|
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
|
||||||
|
{id:"4",z:"t1",type:"test",foo:"a"}
|
||||||
|
]);
|
||||||
|
var flow = Flow.create({
|
||||||
|
getNode: id => { return {id:id}}
|
||||||
|
},config,config.flows["t1"]);
|
||||||
|
flow.start();
|
||||||
|
|
||||||
|
Object.keys(flow.getActiveNodes()).should.have.length(4);
|
||||||
|
|
||||||
|
flow.getNode('1').should.have.a.property('id','1');
|
||||||
|
|
||||||
|
should.not.exist(flow.getNode('parentNode',true));
|
||||||
|
flow.stop().then(() => { done() });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("#handleStatus",function() {
|
describe("#handleStatus",function() {
|
||||||
it("passes a status event to the adjacent status node",function(done) {
|
it("passes a status event to the adjacent status node",function(done) {
|
||||||
|
@ -298,7 +298,6 @@ describe('Subflow', function() {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("instantiates a subflow inside a subflow and stops it",function(done) {
|
it("instantiates a subflow inside a subflow and stops it",function(done) {
|
||||||
var config = flowUtils.parseConfig([
|
var config = flowUtils.parseConfig([
|
||||||
{id:"t1",type:"tab"},
|
{id:"t1",type:"tab"},
|
||||||
@ -452,7 +451,6 @@ describe('Subflow', function() {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("passes a status event to the subflow's parent tab status node - targetted scope",function(done) {
|
it("passes a status event to the subflow's parent tab status node - targetted scope",function(done) {
|
||||||
var config = flowUtils.parseConfig([
|
var config = flowUtils.parseConfig([
|
||||||
{id:"t1",type:"tab"},
|
{id:"t1",type:"tab"},
|
||||||
@ -490,9 +488,164 @@ describe('Subflow', function() {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("status node", function() {
|
||||||
|
it("emits a status event when a message is passed to a subflow-status node - msg.payload as string", function(done) {
|
||||||
|
var config = flowUtils.parseConfig([
|
||||||
|
{id:"t1",type:"tab"},
|
||||||
|
{id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
|
||||||
|
{id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]},
|
||||||
|
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
|
||||||
|
{
|
||||||
|
id:"sf1",
|
||||||
|
type:"subflow",
|
||||||
|
name:"Subflow 2",
|
||||||
|
info:"",
|
||||||
|
in:[{wires:[{id:"sf1-1"}]}],
|
||||||
|
out:[{wires:[{id:"sf1-1",port:0}]}],
|
||||||
|
status:{wires:[{id:"sf1-1", port:0}]}
|
||||||
|
},
|
||||||
|
{id:"sf1-1",type:"test",name:"test","z":"sf1",x:166,y:99,"wires":[[]]},
|
||||||
|
{id:"sn",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]}
|
||||||
|
]);
|
||||||
|
var flow = Flow.create({},config,config.flows["t1"]);
|
||||||
|
|
||||||
|
flow.start();
|
||||||
|
|
||||||
|
var activeNodes = flow.getActiveNodes();
|
||||||
|
|
||||||
|
activeNodes["1"].receive({payload:"test-payload"});
|
||||||
|
|
||||||
|
currentNodes["sn"].should.have.a.property("handled",1);
|
||||||
|
var statusMessage = currentNodes["sn"].messages[0];
|
||||||
|
|
||||||
|
statusMessage.should.have.a.property("status");
|
||||||
|
statusMessage.status.should.have.a.property("text","test-payload");
|
||||||
|
statusMessage.status.should.have.a.property("source");
|
||||||
|
statusMessage.status.source.should.have.a.property("id","2");
|
||||||
|
statusMessage.status.source.should.have.a.property("type","subflow:sf1");
|
||||||
|
|
||||||
|
flow.stop().then(function() {
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("emits a status event when a message is passed to a subflow-status node - msg.payload as status obj", function(done) {
|
||||||
|
var config = flowUtils.parseConfig([
|
||||||
|
{id:"t1",type:"tab"},
|
||||||
|
{id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
|
||||||
|
{id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]},
|
||||||
|
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
|
||||||
|
{
|
||||||
|
id:"sf1",
|
||||||
|
type:"subflow",
|
||||||
|
name:"Subflow 2",
|
||||||
|
info:"",
|
||||||
|
in:[{wires:[{id:"sf1-1"}]}],
|
||||||
|
out:[{wires:[{id:"sf1-1",port:0}]}],
|
||||||
|
status:{wires:[{id:"sf1-1", port:0}]}
|
||||||
|
},
|
||||||
|
{id:"sf1-1",type:"test",name:"test","z":"sf1",x:166,y:99,"wires":[[]]},
|
||||||
|
{id:"sn",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]}
|
||||||
|
]);
|
||||||
|
var flow = Flow.create({},config,config.flows["t1"]);
|
||||||
|
|
||||||
|
flow.start();
|
||||||
|
|
||||||
|
var activeNodes = flow.getActiveNodes();
|
||||||
|
|
||||||
|
activeNodes["1"].receive({payload:{text:"payload-obj"}});
|
||||||
|
|
||||||
|
currentNodes["sn"].should.have.a.property("handled",1);
|
||||||
|
var statusMessage = currentNodes["sn"].messages[0];
|
||||||
|
|
||||||
|
statusMessage.should.have.a.property("status");
|
||||||
|
statusMessage.status.should.have.a.property("text","payload-obj");
|
||||||
|
statusMessage.status.should.have.a.property("source");
|
||||||
|
statusMessage.status.source.should.have.a.property("id","2");
|
||||||
|
statusMessage.status.source.should.have.a.property("type","subflow:sf1");
|
||||||
|
|
||||||
|
flow.stop().then(function() {
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("emits a status event when a message is passed to a subflow-status node - msg.status", function(done) {
|
||||||
|
var config = flowUtils.parseConfig([
|
||||||
|
{id:"t1",type:"tab"},
|
||||||
|
{id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
|
||||||
|
{id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]},
|
||||||
|
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
|
||||||
|
{
|
||||||
|
id:"sf1",
|
||||||
|
type:"subflow",
|
||||||
|
name:"Subflow 2",
|
||||||
|
info:"",
|
||||||
|
in:[{wires:[{id:"sf1-1"}]}],
|
||||||
|
out:[{wires:[{id:"sf1-1",port:0}]}],
|
||||||
|
status:{wires:[{id:"sf1-1", port:0}]}
|
||||||
|
},
|
||||||
|
{id:"sf1-1",type:"test",name:"test","z":"sf1",x:166,y:99,"wires":[[]]},
|
||||||
|
{id:"sn",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]}
|
||||||
|
]);
|
||||||
|
var flow = Flow.create({},config,config.flows["t1"]);
|
||||||
|
|
||||||
|
flow.start();
|
||||||
|
|
||||||
|
var activeNodes = flow.getActiveNodes();
|
||||||
|
|
||||||
|
activeNodes["1"].receive({status:{text:"status-obj"}});
|
||||||
|
|
||||||
|
currentNodes["sn"].should.have.a.property("handled",1);
|
||||||
|
var statusMessage = currentNodes["sn"].messages[0];
|
||||||
|
|
||||||
|
statusMessage.should.have.a.property("status");
|
||||||
|
statusMessage.status.should.have.a.property("text","status-obj");
|
||||||
|
statusMessage.status.should.have.a.property("source");
|
||||||
|
statusMessage.status.source.should.have.a.property("id","2");
|
||||||
|
statusMessage.status.source.should.have.a.property("type","subflow:sf1");
|
||||||
|
|
||||||
|
flow.stop().then(function() {
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("does not emit a regular status event if it contains a subflow-status node", function(done) {
|
||||||
|
var config = flowUtils.parseConfig([
|
||||||
|
{id:"t1",type:"tab"},
|
||||||
|
{id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]},
|
||||||
|
{id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]},
|
||||||
|
{id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]},
|
||||||
|
{
|
||||||
|
id:"sf1",
|
||||||
|
type:"subflow",
|
||||||
|
name:"Subflow 2",
|
||||||
|
info:"",
|
||||||
|
in:[{wires:[{id:"sf1-1"}]}],
|
||||||
|
out:[{wires:[{id:"sf1-1",port:0}]}],
|
||||||
|
status:{wires:[]}
|
||||||
|
},
|
||||||
|
{id:"sf1-1",type:"testStatus",name:"test-status-node","z":"sf1",x:166,y:99,"wires":[[]]},
|
||||||
|
{id:"sn",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]}
|
||||||
|
]);
|
||||||
|
var flow = Flow.create({},config,config.flows["t1"]);
|
||||||
|
|
||||||
|
flow.start();
|
||||||
|
|
||||||
|
var activeNodes = flow.getActiveNodes();
|
||||||
|
|
||||||
|
activeNodes["1"].receive({payload:"test-payload"});
|
||||||
|
|
||||||
|
currentNodes["sn"].should.have.a.property("handled",0);
|
||||||
|
|
||||||
|
flow.stop().then(function() {
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
describe("#handleError",function() {
|
describe("#handleError",function() {
|
||||||
it("passes an error event to the subflow's parent tab catch node - all scope",function(done) {
|
it("passes an error event to the subflow's parent tab catch node - all scope",function(done) {
|
||||||
var config = flowUtils.parseConfig([
|
var config = flowUtils.parseConfig([
|
||||||
@ -526,7 +679,6 @@ describe('Subflow', function() {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("passes an error event to the subflow's parent tab catch node - targetted scope",function(done) {
|
it("passes an error event to the subflow's parent tab catch node - targetted scope",function(done) {
|
||||||
var config = flowUtils.parseConfig([
|
var config = flowUtils.parseConfig([
|
||||||
{id:"t1",type:"tab"},
|
{id:"t1",type:"tab"},
|
||||||
@ -563,7 +715,6 @@ describe('Subflow', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#env var", function() {
|
describe("#env var", function() {
|
||||||
|
Loading…
Reference in New Issue
Block a user