mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Merge branch 'dev' into theme-monaco-theme
This commit is contained in:
commit
5825da9c76
46
CHANGELOG.md
46
CHANGELOG.md
@ -1,3 +1,49 @@
|
|||||||
|
#### 2.0.6: Maintenance Release
|
||||||
|
|
||||||
|
Editor
|
||||||
|
|
||||||
|
- Fix typo in ko editor.json Fixes #3119
|
||||||
|
- Change fade color when hovering an inactive tab (#3106) @bonanitech
|
||||||
|
- Ensure treeList row has suitable min-height when no content Fixes #3109
|
||||||
|
|
||||||
|
Runtime
|
||||||
|
|
||||||
|
- Update tar to latest (#3128) @aksswami
|
||||||
|
- Give passport verify callback the same arity as the original callback (#3117) @dschmidt
|
||||||
|
- Handle HTTPS Key and certificate as string or buffer (#3115) @bartbutenaers
|
||||||
|
|
||||||
|
#### 2.0.5: Maintenance Release
|
||||||
|
|
||||||
|
Editor
|
||||||
|
|
||||||
|
- Remove default ctrl-enter keybinding from monaco editor Fixes #3093
|
||||||
|
|
||||||
|
Runtime
|
||||||
|
|
||||||
|
- Update tar dependency
|
||||||
|
- Add support for maintenance streams in generate-publish-script
|
||||||
|
|
||||||
|
|
||||||
|
Nodes
|
||||||
|
|
||||||
|
- Fix regression in Join node when manual joining array with msg.parts present Fixes #3096
|
||||||
|
|
||||||
|
#### 2.0.4: Maintenance Release
|
||||||
|
|
||||||
|
Editor
|
||||||
|
|
||||||
|
- Fix tab fade CSS for when using themes (#3085) @bonanitech
|
||||||
|
- Handle just-copied-but-not-deployed node with credentials in editor Fixes #3090
|
||||||
|
|
||||||
|
Nodes
|
||||||
|
|
||||||
|
- Filter: Fix RBE node handling of default topi property Fixes #3087
|
||||||
|
- HTTP Request: Handle partially encoded url query strings in request node
|
||||||
|
- HTTP Request: Fix support for supplied CA certs (#3089) @hardillb
|
||||||
|
- HTTP Request: Ensure TLS Cert is used (#3092) @hardillb
|
||||||
|
- Inject: Fix inject now button unable to send empty props
|
||||||
|
- Inject: Inject now button success notification should use label with updated props
|
||||||
|
|
||||||
#### 2.0.3: Maintenance Release
|
#### 2.0.3: Maintenance Release
|
||||||
|
|
||||||
Nodes
|
Nodes
|
||||||
|
@ -162,7 +162,6 @@ module.exports = function(grunt) {
|
|||||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/stack.js",
|
"packages/node_modules/@node-red/editor-client/src/js/ui/common/stack.js",
|
||||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js",
|
"packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js",
|
||||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/toggleButton.js",
|
"packages/node_modules/@node-red/editor-client/src/js/ui/common/toggleButton.js",
|
||||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/colorPicker.js",
|
|
||||||
"packages/node_modules/@node-red/editor-client/src/js/ui/actions.js",
|
"packages/node_modules/@node-red/editor-client/src/js/ui/actions.js",
|
||||||
"packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js",
|
"packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js",
|
||||||
"packages/node_modules/@node-red/editor-client/src/js/ui/diff.js",
|
"packages/node_modules/@node-red/editor-client/src/js/ui/diff.js",
|
||||||
@ -182,6 +181,7 @@ module.exports = function(grunt) {
|
|||||||
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js",
|
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js",
|
||||||
"packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js",
|
"packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js",
|
||||||
"packages/node_modules/@node-red/editor-client/src/js/ui/editor.js",
|
"packages/node_modules/@node-red/editor-client/src/js/ui/editor.js",
|
||||||
|
"packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/*.js",
|
||||||
"packages/node_modules/@node-red/editor-client/src/js/ui/editors/*.js",
|
"packages/node_modules/@node-red/editor-client/src/js/ui/editors/*.js",
|
||||||
"packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/*.js",
|
"packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/*.js",
|
||||||
"packages/node_modules/@node-red/editor-client/src/js/ui/event-log.js",
|
"packages/node_modules/@node-red/editor-client/src/js/ui/event-log.js",
|
||||||
|
26
package.json
26
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "node-red",
|
"name": "node-red",
|
||||||
"version": "2.0.3",
|
"version": "2.0.6",
|
||||||
"description": "Low-code programming for event-driven applications",
|
"description": "Low-code programming for event-driven applications",
|
||||||
"homepage": "http://nodered.org",
|
"homepage": "http://nodered.org",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
@ -28,8 +28,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": "8.4.1",
|
"acorn": "8.4.1",
|
||||||
"acorn-walk": "8.1.1",
|
"acorn-walk": "8.1.1",
|
||||||
"ajv": "8.6.0",
|
"ajv": "8.6.2",
|
||||||
"async-mutex": "0.3.1",
|
"async-mutex": "0.3.2",
|
||||||
"basic-auth": "2.0.1",
|
"basic-auth": "2.0.1",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"body-parser": "1.19.0",
|
"body-parser": "1.19.0",
|
||||||
@ -55,14 +55,14 @@
|
|||||||
"is-utf8": "0.2.1",
|
"is-utf8": "0.2.1",
|
||||||
"js-yaml": "3.14.1",
|
"js-yaml": "3.14.1",
|
||||||
"json-stringify-safe": "5.0.1",
|
"json-stringify-safe": "5.0.1",
|
||||||
"jsonata": "1.8.4",
|
"jsonata": "1.8.5",
|
||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
"media-typer": "1.1.0",
|
"media-typer": "1.1.0",
|
||||||
"memorystore": "1.6.6",
|
"memorystore": "1.6.6",
|
||||||
"mime": "2.5.2",
|
"mime": "2.5.2",
|
||||||
"moment-timezone": "0.5.33",
|
"moment-timezone": "0.5.33",
|
||||||
"mqtt": "4.2.8",
|
"mqtt": "4.2.8",
|
||||||
"multer": "1.4.2",
|
"multer": "1.4.3",
|
||||||
"mustache": "4.2.0",
|
"mustache": "4.2.0",
|
||||||
"node-red-admin": "^2.2.0",
|
"node-red-admin": "^2.2.0",
|
||||||
"nopt": "5.0.0",
|
"nopt": "5.0.0",
|
||||||
@ -73,9 +73,9 @@
|
|||||||
"passport-oauth2-client-password": "0.1.2",
|
"passport-oauth2-client-password": "0.1.2",
|
||||||
"raw-body": "2.4.1",
|
"raw-body": "2.4.1",
|
||||||
"semver": "7.3.5",
|
"semver": "7.3.5",
|
||||||
"tar": "6.1.0",
|
"tar": "6.1.11",
|
||||||
"tough-cookie": "4.0.0",
|
"tough-cookie": "4.0.0",
|
||||||
"uglify-js": "3.13.10",
|
"uglify-js": "3.14.1",
|
||||||
"uuid": "8.3.2",
|
"uuid": "8.3.2",
|
||||||
"ws": "7.5.1",
|
"ws": "7.5.1",
|
||||||
"xml2js": "0.4.23"
|
"xml2js": "0.4.23"
|
||||||
@ -84,7 +84,7 @@
|
|||||||
"bcrypt": "5.0.1"
|
"bcrypt": "5.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"dompurify": "2.2.9",
|
"dompurify": "2.3.1",
|
||||||
"grunt": "1.4.1",
|
"grunt": "1.4.1",
|
||||||
"grunt-chmod": "~1.1.1",
|
"grunt-chmod": "~1.1.1",
|
||||||
"grunt-cli": "~1.4.3",
|
"grunt-cli": "~1.4.3",
|
||||||
@ -109,15 +109,15 @@
|
|||||||
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
|
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
|
||||||
"marked": "2.1.3",
|
"marked": "2.1.3",
|
||||||
"minami": "1.2.3",
|
"minami": "1.2.3",
|
||||||
"mocha": "9.0.1",
|
"mocha": "9.1.1",
|
||||||
"node-red-node-test-helper": "^0.2.7",
|
"node-red-node-test-helper": "^0.2.7",
|
||||||
"nodemon": "2.0.8",
|
"nodemon": "2.0.12",
|
||||||
"proxy": "^1.0.2",
|
"proxy": "^1.0.2",
|
||||||
"sass": "1.35.1",
|
"sass": "1.39.0",
|
||||||
"should": "13.2.3",
|
"should": "13.2.3",
|
||||||
"sinon": "11.1.1",
|
"sinon": "11.1.2",
|
||||||
"stoppable": "^1.1.0",
|
"stoppable": "^1.1.0",
|
||||||
"supertest": "6.1.3"
|
"supertest": "6.1.6"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
|
@ -173,9 +173,7 @@ function genericStrategy(adminApp,strategy) {
|
|||||||
adminApp.use(passport.session());
|
adminApp.use(passport.session());
|
||||||
|
|
||||||
var options = strategy.options;
|
var options = strategy.options;
|
||||||
|
var verify = function() {
|
||||||
passport.use(new strategy.strategy(options,
|
|
||||||
function() {
|
|
||||||
var originalDone = arguments[arguments.length-1];
|
var originalDone = arguments[arguments.length-1];
|
||||||
if (options.verify) {
|
if (options.verify) {
|
||||||
var args = Array.from(arguments);
|
var args = Array.from(arguments);
|
||||||
@ -186,14 +184,19 @@ function genericStrategy(adminApp,strategy) {
|
|||||||
return completeVerify(profile,originalDone);
|
return completeVerify(profile,originalDone);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
options.verify.apply(null,args);
|
options.verify.apply(null,args);
|
||||||
} else {
|
} else {
|
||||||
var profile = arguments[arguments.length - 2];
|
var profile = arguments[arguments.length - 2];
|
||||||
return completeVerify(profile,originalDone);
|
return completeVerify(profile,originalDone);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
// Give our callback the same arity as the original one from options
|
||||||
|
if (options.verify) {
|
||||||
|
Object.defineProperty(verify, "length", { value: options.verify.length })
|
||||||
}
|
}
|
||||||
));
|
|
||||||
|
passport.use(new strategy.strategy(options, verify));
|
||||||
|
|
||||||
adminApp.get('/auth/strategy',
|
adminApp.get('/auth/strategy',
|
||||||
passport.authenticate(strategy.name, {session:false, failureRedirect: settings.httpAdminRoot }),
|
passport.authenticate(strategy.name, {session:false, failureRedirect: settings.httpAdminRoot }),
|
||||||
|
@ -24,7 +24,10 @@ var defaultContext = {
|
|||||||
page: {
|
page: {
|
||||||
title: "Node-RED",
|
title: "Node-RED",
|
||||||
favicon: "favicon.ico",
|
favicon: "favicon.ico",
|
||||||
tabicon: "red/images/node-red-icon-black.svg"
|
tabicon: {
|
||||||
|
icon: "red/images/node-red-icon-black.svg",
|
||||||
|
colour: "#8f0000"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
title: "Node-RED",
|
title: "Node-RED",
|
||||||
@ -123,9 +126,13 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (theme.page.tabicon) {
|
if (theme.page.tabicon) {
|
||||||
url = serveFile(themeApp,"/tabicon/",theme.page.tabicon)
|
let icon = theme.page.tabicon.icon || theme.page.tabicon
|
||||||
|
url = serveFile(themeApp,"/tabicon/", icon)
|
||||||
if (url) {
|
if (url) {
|
||||||
themeContext.page.tabicon = url;
|
themeContext.page.tabicon.icon = url;
|
||||||
|
}
|
||||||
|
if (theme.page.tabicon.colour) {
|
||||||
|
themeContext.page.tabicon.colour = theme.page.tabicon.colour
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/editor-api",
|
"name": "@node-red/editor-api",
|
||||||
"version": "2.0.3",
|
"version": "2.0.6",
|
||||||
"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": "2.0.3",
|
"@node-red/util": "2.0.6",
|
||||||
"@node-red/editor-client": "2.0.3",
|
"@node-red/editor-client": "2.0.6",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"body-parser": "1.19.0",
|
"body-parser": "1.19.0",
|
||||||
"clone": "2.1.2",
|
"clone": "2.1.2",
|
||||||
@ -26,7 +26,7 @@
|
|||||||
"express": "4.17.1",
|
"express": "4.17.1",
|
||||||
"memorystore": "1.6.6",
|
"memorystore": "1.6.6",
|
||||||
"mime": "2.5.2",
|
"mime": "2.5.2",
|
||||||
"multer": "1.4.2",
|
"multer": "1.4.3",
|
||||||
"mustache": "4.2.0",
|
"mustache": "4.2.0",
|
||||||
"oauth2orize": "1.11.0",
|
"oauth2orize": "1.11.0",
|
||||||
"passport-http-bearer": "1.0.1",
|
"passport-http-bearer": "1.0.1",
|
||||||
|
@ -116,7 +116,20 @@
|
|||||||
"groupSelection": "Group selection",
|
"groupSelection": "Group selection",
|
||||||
"ungroupSelection": "Ungroup selection",
|
"ungroupSelection": "Ungroup selection",
|
||||||
"groupMergeSelection": "Merge selection",
|
"groupMergeSelection": "Merge selection",
|
||||||
"groupRemoveSelection": "Remove from group"
|
"groupRemoveSelection": "Remove from group",
|
||||||
|
"arrange":"Arrange",
|
||||||
|
"alignLeft":"Align to left",
|
||||||
|
"alignCenter":"Align to center",
|
||||||
|
"alignRight":"Align to right",
|
||||||
|
"alignTop":"Align to top",
|
||||||
|
"alignMiddle":"Align to middle",
|
||||||
|
"alignBottom":"Align to bottom",
|
||||||
|
"distributeHorizontally":"Distribute horizontally",
|
||||||
|
"distributeVertically":"Distribute vertically",
|
||||||
|
"moveToBack":"Move to back",
|
||||||
|
"moveToFront":"Move to front",
|
||||||
|
"moveBackwards":"Move backwards",
|
||||||
|
"moveForwards":"Move forwards"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
@ -450,8 +463,9 @@
|
|||||||
"unassigned": "Unassigned",
|
"unassigned": "Unassigned",
|
||||||
"global": "global",
|
"global": "global",
|
||||||
"workspace": "workspace",
|
"workspace": "workspace",
|
||||||
"selectAll": "Select all nodes",
|
"selectAll": "Select all",
|
||||||
"selectAllConnected": "Select all connected nodes",
|
"selectNone": "Select none",
|
||||||
|
"selectAllConnected": "Select connected",
|
||||||
"addRemoveNode": "Add/remove node from selection",
|
"addRemoveNode": "Add/remove node from selection",
|
||||||
"editSelected": "Edit selected node",
|
"editSelected": "Edit selected node",
|
||||||
"deleteSelected": "Delete selected nodes or link",
|
"deleteSelected": "Delete selected nodes or link",
|
||||||
@ -464,7 +478,10 @@
|
|||||||
"copyNode": "Copy selected nodes",
|
"copyNode": "Copy selected nodes",
|
||||||
"cutNode": "Cut selected nodes",
|
"cutNode": "Cut selected nodes",
|
||||||
"pasteNode": "Paste nodes",
|
"pasteNode": "Paste nodes",
|
||||||
"undoChange": "Undo the last change performed",
|
"copyGroupStyle": "Copy group style",
|
||||||
|
"pasteGroupStyle": "Paste group style",
|
||||||
|
"undoChange": "Undo",
|
||||||
|
"redoChange": "Redo",
|
||||||
"searchBox": "Open search box",
|
"searchBox": "Open search box",
|
||||||
"managePalette": "Manage palette",
|
"managePalette": "Manage palette",
|
||||||
"actionList":"Action list"
|
"actionList":"Action list"
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
"displayConfig": "설정노드 보기",
|
"displayConfig": "설정노드 보기",
|
||||||
"import": "가져오기",
|
"import": "가져오기",
|
||||||
"export": "내보내기",
|
"export": "내보내기",
|
||||||
"search": "플로우 겅색",
|
"search": "플로우 검색",
|
||||||
"searchInput": "플로우 검색",
|
"searchInput": "플로우 검색",
|
||||||
"subflows": "보조 플로우",
|
"subflows": "보조 플로우",
|
||||||
"createSubflow": "보조 플로우 생성",
|
"createSubflow": "보조 플로우 생성",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/editor-client",
|
"name": "@node-red/editor-client",
|
||||||
"version": "2.0.3",
|
"version": "2.0.6",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -558,11 +558,22 @@ RED.history = (function() {
|
|||||||
} else if (ev.t == "reorder") {
|
} else if (ev.t == "reorder") {
|
||||||
inverseEv = {
|
inverseEv = {
|
||||||
t: 'reorder',
|
t: 'reorder',
|
||||||
order: RED.nodes.getWorkspaceOrder(),
|
|
||||||
dirty: RED.nodes.dirty()
|
dirty: RED.nodes.dirty()
|
||||||
};
|
};
|
||||||
if (ev.order) {
|
if (ev.workspaces) {
|
||||||
RED.workspaces.order(ev.order);
|
inverseEv.workspaces = {
|
||||||
|
from: ev.workspaces.to,
|
||||||
|
to: ev.workspaces.from
|
||||||
|
}
|
||||||
|
RED.workspaces.order(ev.workspaces.from);
|
||||||
|
}
|
||||||
|
if (ev.nodes) {
|
||||||
|
inverseEv.nodes = {
|
||||||
|
z: ev.nodes.z,
|
||||||
|
from: ev.nodes.to,
|
||||||
|
to: ev.nodes.from
|
||||||
|
}
|
||||||
|
RED.nodes.setNodeOrder(ev.nodes.z,ev.nodes.from);
|
||||||
}
|
}
|
||||||
} else if (ev.t == "createGroup") {
|
} else if (ev.t == "createGroup") {
|
||||||
inverseEv = {
|
inverseEv = {
|
||||||
@ -658,6 +669,8 @@ RED.history = (function() {
|
|||||||
push: function(ev) {
|
push: function(ev) {
|
||||||
undoHistory.push(ev);
|
undoHistory.push(ev);
|
||||||
redoHistory = [];
|
redoHistory = [];
|
||||||
|
RED.menu.setDisabled("menu-item-edit-undo", false);
|
||||||
|
RED.menu.setDisabled("menu-item-edit-redo", true);
|
||||||
},
|
},
|
||||||
pop: function() {
|
pop: function() {
|
||||||
var ev = undoHistory.pop();
|
var ev = undoHistory.pop();
|
||||||
@ -665,6 +678,8 @@ RED.history = (function() {
|
|||||||
if (rev) {
|
if (rev) {
|
||||||
redoHistory.push(rev);
|
redoHistory.push(rev);
|
||||||
}
|
}
|
||||||
|
RED.menu.setDisabled("menu-item-edit-undo", undoHistory.length === 0);
|
||||||
|
RED.menu.setDisabled("menu-item-edit-redo", redoHistory.length === 0);
|
||||||
},
|
},
|
||||||
peek: function() {
|
peek: function() {
|
||||||
return undoHistory[undoHistory.length-1];
|
return undoHistory[undoHistory.length-1];
|
||||||
@ -672,6 +687,8 @@ RED.history = (function() {
|
|||||||
clear: function() {
|
clear: function() {
|
||||||
undoHistory = [];
|
undoHistory = [];
|
||||||
redoHistory = [];
|
redoHistory = [];
|
||||||
|
RED.menu.setDisabled("menu-item-edit-undo", true);
|
||||||
|
RED.menu.setDisabled("menu-item-edit-redo", true);
|
||||||
},
|
},
|
||||||
redo: function() {
|
redo: function() {
|
||||||
var ev = redoHistory.pop();
|
var ev = redoHistory.pop();
|
||||||
@ -681,6 +698,8 @@ RED.history = (function() {
|
|||||||
undoHistory.push(uev);
|
undoHistory.push(uev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RED.menu.setDisabled("menu-item-edit-undo", undoHistory.length === 0);
|
||||||
|
RED.menu.setDisabled("menu-item-edit-redo", redoHistory.length === 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +77,15 @@
|
|||||||
"right": "core:go-to-nearest-node-on-right",
|
"right": "core:go-to-nearest-node-on-right",
|
||||||
"left": "core:go-to-nearest-node-on-left",
|
"left": "core:go-to-nearest-node-on-left",
|
||||||
"up": "core:go-to-nearest-node-above",
|
"up": "core:go-to-nearest-node-above",
|
||||||
"down": "core:go-to-nearest-node-below"
|
"down": "core:go-to-nearest-node-below",
|
||||||
|
"alt-a g": "core:align-selection-to-grid",
|
||||||
|
"alt-a l": "core:align-selection-to-left",
|
||||||
|
"alt-a r": "core:align-selection-to-right",
|
||||||
|
"alt-a t": "core:align-selection-to-top",
|
||||||
|
"alt-a b": "core:align-selection-to-bottom",
|
||||||
|
"alt-a m": "core:align-selection-to-middle",
|
||||||
|
"alt-a c": "core:align-selection-to-center",
|
||||||
|
"alt-a h": "core:distribute-selection-horizontally",
|
||||||
|
"alt-a v": "core:distribute-selection-vertically"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,6 @@
|
|||||||
RED.nodes = (function() {
|
RED.nodes = (function() {
|
||||||
|
|
||||||
var node_defs = {};
|
var node_defs = {};
|
||||||
var nodes = {};
|
|
||||||
var nodeTabMap = {};
|
|
||||||
var linkTabMap = {};
|
var linkTabMap = {};
|
||||||
|
|
||||||
var configNodes = {};
|
var configNodes = {};
|
||||||
@ -41,6 +39,7 @@ RED.nodes = (function() {
|
|||||||
RED.events.emit("workspace:dirty",{dirty:dirty});
|
RED.events.emit("workspace:dirty",{dirty:dirty});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The registry holds information about all node types.
|
||||||
var registry = (function() {
|
var registry = (function() {
|
||||||
var moduleList = {};
|
var moduleList = {};
|
||||||
var nodeList = [];
|
var nodeList = [];
|
||||||
@ -53,7 +52,8 @@ RED.nodes = (function() {
|
|||||||
defaults: {
|
defaults: {
|
||||||
label: {value:""},
|
label: {value:""},
|
||||||
disabled: {value: false},
|
disabled: {value: false},
|
||||||
info: {value: ""}
|
info: {value: ""},
|
||||||
|
env: {value: []}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -209,6 +209,279 @@ RED.nodes = (function() {
|
|||||||
return exports;
|
return exports;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
// allNodes holds information about the Flow nodes.
|
||||||
|
var allNodes = (function() {
|
||||||
|
var nodes = {};
|
||||||
|
var tabMap = {};
|
||||||
|
var api = {
|
||||||
|
addTab: function(id) {
|
||||||
|
tabMap[id] = [];
|
||||||
|
},
|
||||||
|
hasTab: function(z) {
|
||||||
|
return tabMap.hasOwnProperty(z)
|
||||||
|
},
|
||||||
|
removeTab: function(id) {
|
||||||
|
delete tabMap[id];
|
||||||
|
},
|
||||||
|
addNode: function(n) {
|
||||||
|
nodes[n.id] = n;
|
||||||
|
if (tabMap.hasOwnProperty(n.z)) {
|
||||||
|
tabMap[n.z].push(n);
|
||||||
|
} else {
|
||||||
|
console.warn("Node added to unknown tab/subflow:",n);
|
||||||
|
tabMap["_"] = tabMap["_"] || [];
|
||||||
|
tabMap["_"].push(n);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeNode: function(n) {
|
||||||
|
delete nodes[n.id]
|
||||||
|
if (tabMap.hasOwnProperty(n.z)) {
|
||||||
|
var i = tabMap[n.z].indexOf(n);
|
||||||
|
if (i > -1) {
|
||||||
|
tabMap[n.z].splice(i,1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hasNode: function(id) {
|
||||||
|
return nodes.hasOwnProperty(id);
|
||||||
|
},
|
||||||
|
getNode: function(id) {
|
||||||
|
return nodes[id]
|
||||||
|
},
|
||||||
|
moveNode: function(n, newZ) {
|
||||||
|
api.removeNode(n);
|
||||||
|
tabMap[newZ] = tabMap[newZ] || [];
|
||||||
|
tabMap[newZ].push(n);
|
||||||
|
},
|
||||||
|
moveNodesForwards: function(nodes) {
|
||||||
|
var result = [];
|
||||||
|
if (!Array.isArray(nodes)) {
|
||||||
|
nodes = [nodes]
|
||||||
|
}
|
||||||
|
// Can only do this for nodes on the same tab.
|
||||||
|
// Use nodes[0] to get the z
|
||||||
|
var tabNodes = tabMap[nodes[0].z];
|
||||||
|
var toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
|
||||||
|
var moved = new Set();
|
||||||
|
for (var i = tabNodes.length-1; i >= 0; i--) {
|
||||||
|
if (toMove.size === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var n = tabNodes[i];
|
||||||
|
if (toMove.has(n)) {
|
||||||
|
// This is a node to move.
|
||||||
|
if (i < tabNodes.length-1 && !moved.has(tabNodes[i+1])) {
|
||||||
|
// Remove from current position
|
||||||
|
tabNodes.splice(i,1);
|
||||||
|
// Add it back one position higher
|
||||||
|
tabNodes.splice(i+1,0,n);
|
||||||
|
n._reordered = true;
|
||||||
|
result.push(n);
|
||||||
|
}
|
||||||
|
toMove.delete(n);
|
||||||
|
moved.add(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result.length > 0) {
|
||||||
|
RED.events.emit('nodes:reorder',{
|
||||||
|
z: nodes[0].z,
|
||||||
|
nodes: result
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
moveNodesBackwards: function(nodes) {
|
||||||
|
var result = [];
|
||||||
|
if (!Array.isArray(nodes)) {
|
||||||
|
nodes = [nodes]
|
||||||
|
}
|
||||||
|
// Can only do this for nodes on the same tab.
|
||||||
|
// Use nodes[0] to get the z
|
||||||
|
var tabNodes = tabMap[nodes[0].z];
|
||||||
|
var toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
|
||||||
|
var moved = new Set();
|
||||||
|
for (var i = 0; i < tabNodes.length; i++) {
|
||||||
|
if (toMove.size === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var n = tabNodes[i];
|
||||||
|
if (toMove.has(n)) {
|
||||||
|
// This is a node to move.
|
||||||
|
if (i > 0 && !moved.has(tabNodes[i-1])) {
|
||||||
|
// Remove from current position
|
||||||
|
tabNodes.splice(i,1);
|
||||||
|
// Add it back one position lower
|
||||||
|
tabNodes.splice(i-1,0,n);
|
||||||
|
n._reordered = true;
|
||||||
|
result.push(n);
|
||||||
|
}
|
||||||
|
toMove.delete(n);
|
||||||
|
moved.add(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result.length > 0) {
|
||||||
|
RED.events.emit('nodes:reorder',{
|
||||||
|
z: nodes[0].z,
|
||||||
|
nodes: result
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
moveNodesToFront: function(nodes) {
|
||||||
|
var result = [];
|
||||||
|
if (!Array.isArray(nodes)) {
|
||||||
|
nodes = [nodes]
|
||||||
|
}
|
||||||
|
// Can only do this for nodes on the same tab.
|
||||||
|
// Use nodes[0] to get the z
|
||||||
|
var tabNodes = tabMap[nodes[0].z];
|
||||||
|
var toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
|
||||||
|
var target = tabNodes.length-1;
|
||||||
|
for (var i = tabNodes.length-1; i >= 0; i--) {
|
||||||
|
if (toMove.size === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var n = tabNodes[i];
|
||||||
|
if (toMove.has(n)) {
|
||||||
|
// This is a node to move.
|
||||||
|
if (i < target) {
|
||||||
|
// Remove from current position
|
||||||
|
tabNodes.splice(i,1);
|
||||||
|
tabNodes.splice(target,0,n);
|
||||||
|
n._reordered = true;
|
||||||
|
result.push(n);
|
||||||
|
}
|
||||||
|
target--;
|
||||||
|
toMove.delete(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result.length > 0) {
|
||||||
|
RED.events.emit('nodes:reorder',{
|
||||||
|
z: nodes[0].z,
|
||||||
|
nodes: result
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
moveNodesToBack: function(nodes) {
|
||||||
|
var result = [];
|
||||||
|
if (!Array.isArray(nodes)) {
|
||||||
|
nodes = [nodes]
|
||||||
|
}
|
||||||
|
// Can only do this for nodes on the same tab.
|
||||||
|
// Use nodes[0] to get the z
|
||||||
|
var tabNodes = tabMap[nodes[0].z];
|
||||||
|
var toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
|
||||||
|
var target = 0;
|
||||||
|
for (var i = 0; i < tabNodes.length; i++) {
|
||||||
|
if (toMove.size === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var n = tabNodes[i];
|
||||||
|
if (toMove.has(n)) {
|
||||||
|
// This is a node to move.
|
||||||
|
if (i > target) {
|
||||||
|
// Remove from current position
|
||||||
|
tabNodes.splice(i,1);
|
||||||
|
// Add it back one position lower
|
||||||
|
tabNodes.splice(target,0,n);
|
||||||
|
n._reordered = true;
|
||||||
|
result.push(n);
|
||||||
|
}
|
||||||
|
target++;
|
||||||
|
toMove.delete(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result.length > 0) {
|
||||||
|
RED.events.emit('nodes:reorder',{
|
||||||
|
z: nodes[0].z,
|
||||||
|
nodes: result
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
getNodes: function(z) {
|
||||||
|
return tabMap[z];
|
||||||
|
},
|
||||||
|
clear: function() {
|
||||||
|
nodes = {};
|
||||||
|
tabMap = {};
|
||||||
|
},
|
||||||
|
eachNode: function(cb) {
|
||||||
|
var nodeList,i,j;
|
||||||
|
for (i in subflows) {
|
||||||
|
if (subflows.hasOwnProperty(i)) {
|
||||||
|
nodeList = tabMap[i];
|
||||||
|
for (j = 0; j < nodeList.length; j++) {
|
||||||
|
if (cb(nodeList[j]) === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < workspacesOrder.length; i++) {
|
||||||
|
nodeList = tabMap[workspacesOrder[i]];
|
||||||
|
for (j = 0; j < nodeList.length; j++) {
|
||||||
|
if (cb(nodeList[j]) === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Flow nodes that do not have a valid tab/subflow
|
||||||
|
if (tabMap["_"]) {
|
||||||
|
nodeList = tabMap["_"];
|
||||||
|
for (j = 0; j < nodeList.length; j++) {
|
||||||
|
if (cb(nodeList[j]) === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
filterNodes: function(filter) {
|
||||||
|
var result = [];
|
||||||
|
var searchSet = null;
|
||||||
|
var doZFilter = false;
|
||||||
|
if (filter.hasOwnProperty("z")) {
|
||||||
|
if (tabMap.hasOwnProperty(filter.z)) {
|
||||||
|
searchSet = tabMap[filter.z];
|
||||||
|
} else {
|
||||||
|
doZFilter = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (searchSet === null) {
|
||||||
|
searchSet = nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var n=0;n<searchSet.length;n++) {
|
||||||
|
var node = searchSet[n];
|
||||||
|
if (filter.hasOwnProperty("type") && node.type !== filter.type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (doZFilter && node.z !== filter.z) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result.push(node);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
getNodeOrder: function(z) {
|
||||||
|
return tabMap[z].map(function(n) { return n.id })
|
||||||
|
},
|
||||||
|
setNodeOrder: function(z, order) {
|
||||||
|
var orderMap = {};
|
||||||
|
order.forEach(function(id,i) {
|
||||||
|
orderMap[id] = i;
|
||||||
|
})
|
||||||
|
tabMap[z].sort(function(A,B) {
|
||||||
|
A._reordered = true;
|
||||||
|
B._reordered = true;
|
||||||
|
return orderMap[A.id] - orderMap[B.id];
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return api;
|
||||||
|
})()
|
||||||
|
|
||||||
function getID() {
|
function getID() {
|
||||||
var bytes = [];
|
var bytes = [];
|
||||||
for (var i=0;i<8;i++) {
|
for (var i=0;i<8;i++) {
|
||||||
@ -294,15 +567,10 @@ RED.nodes = (function() {
|
|||||||
});
|
});
|
||||||
n.i = nextId+1;
|
n.i = nextId+1;
|
||||||
}
|
}
|
||||||
nodes[n.id] = n;
|
allNodes.addNode(n);
|
||||||
if (!nodeLinks[n.id]) {
|
if (!nodeLinks[n.id]) {
|
||||||
nodeLinks[n.id] = {in:[],out:[]};
|
nodeLinks[n.id] = {in:[],out:[]};
|
||||||
}
|
}
|
||||||
if (nodeTabMap[n.z]) {
|
|
||||||
nodeTabMap[n.z][n.id] = n;
|
|
||||||
} else {
|
|
||||||
console.warn("Node added to unknown tab/subflow:",n);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
RED.events.emit('nodes:add',n);
|
RED.events.emit('nodes:add',n);
|
||||||
}
|
}
|
||||||
@ -330,10 +598,8 @@ RED.nodes = (function() {
|
|||||||
function getNode(id) {
|
function getNode(id) {
|
||||||
if (id in configNodes) {
|
if (id in configNodes) {
|
||||||
return configNodes[id];
|
return configNodes[id];
|
||||||
} else if (id in nodes) {
|
|
||||||
return nodes[id];
|
|
||||||
}
|
}
|
||||||
return null;
|
return allNodes.getNode(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeNode(id) {
|
function removeNode(id) {
|
||||||
@ -345,13 +611,10 @@ RED.nodes = (function() {
|
|||||||
delete configNodes[id];
|
delete configNodes[id];
|
||||||
RED.events.emit('nodes:remove',node);
|
RED.events.emit('nodes:remove',node);
|
||||||
RED.workspaces.refresh();
|
RED.workspaces.refresh();
|
||||||
} else if (id in nodes) {
|
} else if (allNodes.hasNode(id)) {
|
||||||
node = nodes[id];
|
node = allNodes.getNode(id);
|
||||||
delete nodes[id]
|
allNodes.removeNode(node);
|
||||||
delete nodeLinks[id];
|
delete nodeLinks[id];
|
||||||
if (nodeTabMap[node.z]) {
|
|
||||||
delete nodeTabMap[node.z][node.id];
|
|
||||||
}
|
|
||||||
removedLinks = links.filter(function(l) { return (l.source === node) || (l.target === node); });
|
removedLinks = links.filter(function(l) { return (l.source === node) || (l.target === node); });
|
||||||
removedLinks.forEach(removeLink);
|
removedLinks.forEach(removeLink);
|
||||||
var updatedConfigNode = false;
|
var updatedConfigNode = false;
|
||||||
@ -409,18 +672,32 @@ RED.nodes = (function() {
|
|||||||
return {links:removedLinks,nodes:removedNodes};
|
return {links:removedLinks,nodes:removedNodes};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function moveNodesForwards(nodes) {
|
||||||
|
return allNodes.moveNodesForwards(nodes);
|
||||||
|
}
|
||||||
|
function moveNodesBackwards(nodes) {
|
||||||
|
return allNodes.moveNodesBackwards(nodes);
|
||||||
|
}
|
||||||
|
function moveNodesToFront(nodes) {
|
||||||
|
return allNodes.moveNodesToFront(nodes);
|
||||||
|
}
|
||||||
|
function moveNodesToBack(nodes) {
|
||||||
|
return allNodes.moveNodesToBack(nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNodeOrder(z) {
|
||||||
|
return allNodes.getNodeOrder(z);
|
||||||
|
}
|
||||||
|
function setNodeOrder(z, order) {
|
||||||
|
allNodes.setNodeOrder(z,order);
|
||||||
|
}
|
||||||
|
|
||||||
function moveNodeToTab(node, z) {
|
function moveNodeToTab(node, z) {
|
||||||
if (node.type === "group") {
|
if (node.type === "group") {
|
||||||
moveGroupToTab(node,z);
|
moveGroupToTab(node,z);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (nodeTabMap[node.z]) {
|
allNodes.moveNode(node,z);
|
||||||
delete nodeTabMap[node.z][node.id];
|
|
||||||
}
|
|
||||||
if (!nodeTabMap[z]) {
|
|
||||||
nodeTabMap[z] = {};
|
|
||||||
}
|
|
||||||
nodeTabMap[z][node.id] = node;
|
|
||||||
var nl = nodeLinks[node.id];
|
var nl = nodeLinks[node.id];
|
||||||
if (nl) {
|
if (nl) {
|
||||||
nl.in.forEach(function(l) {
|
nl.in.forEach(function(l) {
|
||||||
@ -482,7 +759,7 @@ RED.nodes = (function() {
|
|||||||
|
|
||||||
function addWorkspace(ws,targetIndex) {
|
function addWorkspace(ws,targetIndex) {
|
||||||
workspaces[ws.id] = ws;
|
workspaces[ws.id] = ws;
|
||||||
nodeTabMap[ws.id] = {};
|
allNodes.addTab(ws.id);
|
||||||
linkTabMap[ws.id] = [];
|
linkTabMap[ws.id] = [];
|
||||||
|
|
||||||
ws._def = RED.nodes.getType('tab');
|
ws._def = RED.nodes.getType('tab');
|
||||||
@ -506,19 +783,14 @@ RED.nodes = (function() {
|
|||||||
var removedGroups = [];
|
var removedGroups = [];
|
||||||
if (ws) {
|
if (ws) {
|
||||||
delete workspaces[id];
|
delete workspaces[id];
|
||||||
delete nodeTabMap[id];
|
allNodes.removeTab(id);
|
||||||
delete linkTabMap[id];
|
delete linkTabMap[id];
|
||||||
workspacesOrder.splice(workspacesOrder.indexOf(id),1);
|
workspacesOrder.splice(workspacesOrder.indexOf(id),1);
|
||||||
var i;
|
var i;
|
||||||
var node;
|
var node;
|
||||||
// TODO: this should use nodeTabMap
|
|
||||||
for (i in nodes) {
|
if (allNodes.hasTab(id)) {
|
||||||
if (nodes.hasOwnProperty(i)) {
|
removedNodes = allNodes.getNodes(id).slice()
|
||||||
node = nodes[i];
|
|
||||||
if (node.z == id) {
|
|
||||||
removedNodes.push(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (i in configNodes) {
|
for (i in configNodes) {
|
||||||
if (configNodes.hasOwnProperty(i)) {
|
if (configNodes.hasOwnProperty(i)) {
|
||||||
@ -572,7 +844,7 @@ RED.nodes = (function() {
|
|||||||
sf.name = subflowName;
|
sf.name = subflowName;
|
||||||
}
|
}
|
||||||
subflows[sf.id] = sf;
|
subflows[sf.id] = sf;
|
||||||
nodeTabMap[sf.id] = {};
|
allNodes.addTab(sf.id);
|
||||||
linkTabMap[sf.id] = [];
|
linkTabMap[sf.id] = [];
|
||||||
|
|
||||||
RED.nodes.registerType("subflow:"+sf.id, {
|
RED.nodes.registerType("subflow:"+sf.id, {
|
||||||
@ -591,18 +863,18 @@ RED.nodes = (function() {
|
|||||||
inputLabels: function(i) { return sf.inputLabels?sf.inputLabels[i]:null },
|
inputLabels: function(i) { return sf.inputLabels?sf.inputLabels[i]:null },
|
||||||
outputLabels: function(i) { return sf.outputLabels?sf.outputLabels[i]:null },
|
outputLabels: function(i) { return sf.outputLabels?sf.outputLabels[i]:null },
|
||||||
oneditprepare: function() {
|
oneditprepare: function() {
|
||||||
|
if (this.type !== 'subflow') {
|
||||||
|
// A subflow instance node
|
||||||
RED.subflow.buildEditForm("subflow",this);
|
RED.subflow.buildEditForm("subflow",this);
|
||||||
RED.subflow.buildPropertiesForm(this);
|
} else {
|
||||||
|
// A subflow template node
|
||||||
|
RED.subflow.buildEditForm("subflow-template", this);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
oneditresize: function(size) {
|
oneditresize: function(size) {
|
||||||
// var rows = $(".dialog-form>div:not(.node-input-env-container-row)");
|
if (this.type === 'subflow') {
|
||||||
var height = size.height;
|
$("#node-input-env-container").editableList('height',size.height - 80);
|
||||||
// for (var i=0; i<rows.size(); i++) {
|
}
|
||||||
// height -= $(rows[i]).outerHeight(true);
|
|
||||||
// }
|
|
||||||
// var editorRow = $("#dialog-form>div.node-input-env-container-row");
|
|
||||||
// height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
|
||||||
$("ol.red-ui-editor-subflow-env-list").editableList('height',height);
|
|
||||||
},
|
},
|
||||||
set:{
|
set:{
|
||||||
module: "node-red"
|
module: "node-red"
|
||||||
@ -618,17 +890,16 @@ RED.nodes = (function() {
|
|||||||
function removeSubflow(sf) {
|
function removeSubflow(sf) {
|
||||||
if (subflows[sf.id]) {
|
if (subflows[sf.id]) {
|
||||||
delete subflows[sf.id];
|
delete subflows[sf.id];
|
||||||
delete nodeTabMap[sf.id];
|
allNodes.removeTab(sf.id);
|
||||||
registry.removeNodeType("subflow:"+sf.id);
|
registry.removeNodeType("subflow:"+sf.id);
|
||||||
RED.events.emit("subflows:remove",sf);
|
RED.events.emit("subflows:remove",sf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function subflowContains(sfid,nodeid) {
|
function subflowContains(sfid,nodeid) {
|
||||||
for (var i in nodes) {
|
var sfNodes = allNodes.getNodes(sfid);
|
||||||
if (nodes.hasOwnProperty(i)) {
|
for (var i = 0; i<sfNodes.length; i++) {
|
||||||
var node = nodes[i];
|
var node = sfNodes[i];
|
||||||
if (node.z === sfid) {
|
|
||||||
var m = /^subflow:(.+)$/.exec(node.type);
|
var m = /^subflow:(.+)$/.exec(node.type);
|
||||||
if (m) {
|
if (m) {
|
||||||
if (m[1] === nodeid) {
|
if (m[1] === nodeid) {
|
||||||
@ -641,8 +912,6 @@ RED.nodes = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -681,7 +950,13 @@ RED.nodes = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function convertWorkspace(n) {
|
function convertWorkspace(n,opts) {
|
||||||
|
var exportCreds = true;
|
||||||
|
if (opts) {
|
||||||
|
if (opts.hasOwnProperty("credentials")) {
|
||||||
|
exportCreds = opts.credentials;
|
||||||
|
}
|
||||||
|
}
|
||||||
var node = {};
|
var node = {};
|
||||||
node.id = n.id;
|
node.id = n.id;
|
||||||
node.type = n.type;
|
node.type = n.type;
|
||||||
@ -690,6 +965,23 @@ RED.nodes = (function() {
|
|||||||
node[d] = n[d];
|
node[d] = n[d];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (exportCreds) {
|
||||||
|
var credentialSet = {};
|
||||||
|
if (n.credentials) {
|
||||||
|
for (var tabCred in n.credentials) {
|
||||||
|
if (n.credentials.hasOwnProperty(tabCred)) {
|
||||||
|
if (!n.credentials._ ||
|
||||||
|
n.credentials["has_"+tabCred] != n.credentials._["has_"+tabCred] ||
|
||||||
|
(n.credentials["has_"+tabCred] && n.credentials[tabCred])) {
|
||||||
|
credentialSet[tabCred] = n.credentials[tabCred];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.keys(credentialSet).length > 0) {
|
||||||
|
node.credentials = credentialSet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -710,7 +1002,7 @@ RED.nodes = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (n.type === 'tab') {
|
if (n.type === 'tab') {
|
||||||
return convertWorkspace(n);
|
return convertWorkspace(n, { credentials: exportCreds });
|
||||||
}
|
}
|
||||||
var node = {};
|
var node = {};
|
||||||
node.id = n.id;
|
node.id = n.id;
|
||||||
@ -739,8 +1031,10 @@ RED.nodes = (function() {
|
|||||||
}
|
}
|
||||||
if (exportCreds) {
|
if (exportCreds) {
|
||||||
var credentialSet = {};
|
var credentialSet = {};
|
||||||
if (/^subflow:/.test(node.type) && n.credentials) {
|
if ((/^subflow:/.test(node.type) ||
|
||||||
// A subflow instance node can have arbitrary creds
|
(node.type === "group")) &&
|
||||||
|
n.credentials) {
|
||||||
|
// A subflow instance/group node can have arbitrary creds
|
||||||
for (var sfCred in n.credentials) {
|
for (var sfCred in n.credentials) {
|
||||||
if (n.credentials.hasOwnProperty(sfCred)) {
|
if (n.credentials.hasOwnProperty(sfCred)) {
|
||||||
if (!n.credentials._ ||
|
if (!n.credentials._ ||
|
||||||
@ -934,11 +1228,15 @@ RED.nodes = (function() {
|
|||||||
|
|
||||||
function createExportableSubflow(id) {
|
function createExportableSubflow(id) {
|
||||||
var sf = getSubflow(id);
|
var sf = getSubflow(id);
|
||||||
var nodeSet = [sf];
|
var nodeSet;
|
||||||
var sfNodeIds = Object.keys(nodeTabMap[sf.id]||{});
|
var sfNodes = allNodes.getNodes(sf.id);
|
||||||
for (var i=0, l=sfNodeIds.length; i<l; i++) {
|
if (sfNodes) {
|
||||||
nodeSet.push(nodeTabMap[sf.id][sfNodeIds[i]]);
|
nodeSet = sfNodes.slice();
|
||||||
|
nodeSet.unshift(sf);
|
||||||
|
} else {
|
||||||
|
nodeSet = [sf];
|
||||||
}
|
}
|
||||||
|
console.log(nodeSet);
|
||||||
return createExportableNodeSet(nodeSet);
|
return createExportableNodeSet(nodeSet);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -965,12 +1263,9 @@ RED.nodes = (function() {
|
|||||||
if (!exportedSubflows[subflowId]) {
|
if (!exportedSubflows[subflowId]) {
|
||||||
exportedSubflows[subflowId] = true;
|
exportedSubflows[subflowId] = true;
|
||||||
var subflow = getSubflow(subflowId);
|
var subflow = getSubflow(subflowId);
|
||||||
var subflowSet = [subflow];
|
var subflowSet = allNodes.getNodes(subflowId).slice();
|
||||||
RED.nodes.eachNode(function(n) {
|
subflowSet.unshift(subflow);
|
||||||
if (n.z == subflowId) {
|
|
||||||
subflowSet.push(n);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
RED.nodes.eachConfig(function(n) {
|
RED.nodes.eachConfig(function(n) {
|
||||||
if (n.z == subflowId) {
|
if (n.z == subflowId) {
|
||||||
subflowSet.push(n);
|
subflowSet.push(n);
|
||||||
@ -1030,7 +1325,7 @@ RED.nodes = (function() {
|
|||||||
var i;
|
var i;
|
||||||
for (i=0;i<workspacesOrder.length;i++) {
|
for (i=0;i<workspacesOrder.length;i++) {
|
||||||
if (workspaces[workspacesOrder[i]].type == "tab") {
|
if (workspaces[workspacesOrder[i]].type == "tab") {
|
||||||
nns.push(convertWorkspace(workspaces[workspacesOrder[i]]));
|
nns.push(convertWorkspace(workspaces[workspacesOrder[i]], opts));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (i in subflows) {
|
for (i in subflows) {
|
||||||
@ -1048,11 +1343,9 @@ RED.nodes = (function() {
|
|||||||
nns.push(convertNode(configNodes[i], opts));
|
nns.push(convertNode(configNodes[i], opts));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (i in nodes) {
|
RED.nodes.eachNode(function(n) {
|
||||||
if (nodes.hasOwnProperty(i)) {
|
nns.push(convertNode(n, opts));
|
||||||
nns.push(convertNode(nodes[i], opts));
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
return nns;
|
return nns;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1149,7 +1442,7 @@ RED.nodes = (function() {
|
|||||||
var nodeZ = n.z || "__global__";
|
var nodeZ = n.z || "__global__";
|
||||||
imported.zMap[nodeZ] = imported.zMap[nodeZ] || [];
|
imported.zMap[nodeZ] = imported.zMap[nodeZ] || [];
|
||||||
imported.zMap[nodeZ].push(n)
|
imported.zMap[nodeZ].push(n)
|
||||||
if (nodes[n.id] || configNodes[n.id] || workspaces[n.id] || subflows[n.id] || groups[n.id]) {
|
if (allNodes.hasNode(n.id) || configNodes[n.id] || workspaces[n.id] || subflows[n.id] || groups[n.id]) {
|
||||||
imported.conflicted[n.id] = n;
|
imported.conflicted[n.id] = n;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -1157,7 +1450,6 @@ RED.nodes = (function() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace the provided nodes.
|
* Replace the provided nodes.
|
||||||
* This must contain complete Subflow defs or complete Flow Tabs.
|
* This must contain complete Subflow defs or complete Flow Tabs.
|
||||||
@ -1316,7 +1608,7 @@ RED.nodes = (function() {
|
|||||||
if (!options.generateIds) {
|
if (!options.generateIds) {
|
||||||
if (!options.importMap[id]) {
|
if (!options.importMap[id]) {
|
||||||
// No conflict resolution for this node
|
// No conflict resolution for this node
|
||||||
var existing = nodes[id] || configNodes[id] || workspaces[id] || subflows[id] || groups[id];
|
var existing = allNodes.getNode(id) || configNodes[id] || workspaces[id] || subflows[id] || groups[id];
|
||||||
if (existing) {
|
if (existing) {
|
||||||
existingNodes.push({existing:existing, imported:n});
|
existingNodes.push({existing:existing, imported:n});
|
||||||
}
|
}
|
||||||
@ -1387,7 +1679,8 @@ RED.nodes = (function() {
|
|||||||
type: "tab",
|
type: "tab",
|
||||||
disabled: false,
|
disabled: false,
|
||||||
label: RED._("clipboard.recoveredNodes"),
|
label: RED._("clipboard.recoveredNodes"),
|
||||||
info: RED._("clipboard.recoveredNodesInfo")
|
info: RED._("clipboard.recoveredNodesInfo"),
|
||||||
|
env: []
|
||||||
}
|
}
|
||||||
addWorkspace(recoveryWorkspace);
|
addWorkspace(recoveryWorkspace);
|
||||||
RED.workspaces.add(recoveryWorkspace);
|
RED.workspaces.add(recoveryWorkspace);
|
||||||
@ -1518,7 +1811,7 @@ RED.nodes = (function() {
|
|||||||
|
|
||||||
// Add a tab if there isn't one there already
|
// Add a tab if there isn't one there already
|
||||||
if (defaultWorkspace == null) {
|
if (defaultWorkspace == null) {
|
||||||
defaultWorkspace = { type:"tab", id:getID(), disabled: false, info:"", label:RED._('workspace.defaultName',{number:1})};
|
defaultWorkspace = { type:"tab", id:getID(), disabled: false, info:"", label:RED._('workspace.defaultName',{number:1}), env:[]};
|
||||||
addWorkspace(defaultWorkspace);
|
addWorkspace(defaultWorkspace);
|
||||||
RED.workspaces.add(defaultWorkspace);
|
RED.workspaces.add(defaultWorkspace);
|
||||||
new_workspaces.push(defaultWorkspace);
|
new_workspaces.push(defaultWorkspace);
|
||||||
@ -1979,32 +2272,9 @@ RED.nodes = (function() {
|
|||||||
|
|
||||||
// TODO: supports filter.z|type
|
// TODO: supports filter.z|type
|
||||||
function filterNodes(filter) {
|
function filterNodes(filter) {
|
||||||
var result = [];
|
return allNodes.filterNodes(filter);
|
||||||
var searchSet = null;
|
|
||||||
var doZFilter = false;
|
|
||||||
if (filter.hasOwnProperty("z")) {
|
|
||||||
if (nodeTabMap.hasOwnProperty(filter.z)) {
|
|
||||||
searchSet = Object.keys(nodeTabMap[filter.z]);
|
|
||||||
} else {
|
|
||||||
doZFilter = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (searchSet === null) {
|
|
||||||
searchSet = Object.keys(nodes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var n=0;n<searchSet.length;n++) {
|
|
||||||
var node = nodes[searchSet[n]];
|
|
||||||
if (filter.hasOwnProperty("type") && node.type !== filter.type) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (doZFilter && node.z !== filter.z) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result.push(node);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
function filterLinks(filter) {
|
function filterLinks(filter) {
|
||||||
var result = [];
|
var result = [];
|
||||||
var candidateLinks = [];
|
var candidateLinks = [];
|
||||||
@ -2092,9 +2362,8 @@ RED.nodes = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function clear() {
|
function clear() {
|
||||||
nodes = {};
|
allNodes.clear();
|
||||||
links = [];
|
links = [];
|
||||||
nodeTabMap = {};
|
|
||||||
linkTabMap = {};
|
linkTabMap = {};
|
||||||
nodeLinks = {};
|
nodeLinks = {};
|
||||||
configNodes = {};
|
configNodes = {};
|
||||||
@ -2186,10 +2455,7 @@ RED.nodes = (function() {
|
|||||||
if (configNodes.hasOwnProperty(n.id)) {
|
if (configNodes.hasOwnProperty(n.id)) {
|
||||||
delete configNodes[n.id];
|
delete configNodes[n.id];
|
||||||
} else {
|
} else {
|
||||||
delete nodes[n.id];
|
allNodes.removeNode(n);
|
||||||
if (nodeTabMap[n.z]) {
|
|
||||||
delete nodeTabMap[n.z][n.id];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
reimportList.push(convertNode(n));
|
reimportList.push(convertNode(n));
|
||||||
RED.events.emit('nodes:remove',n);
|
RED.events.emit('nodes:remove',n);
|
||||||
@ -2246,6 +2512,13 @@ RED.nodes = (function() {
|
|||||||
remove: removeNode,
|
remove: removeNode,
|
||||||
clear: clear,
|
clear: clear,
|
||||||
|
|
||||||
|
moveNodesForwards: moveNodesForwards,
|
||||||
|
moveNodesBackwards: moveNodesBackwards,
|
||||||
|
moveNodesToFront: moveNodesToFront,
|
||||||
|
moveNodesToBack: moveNodesToBack,
|
||||||
|
getNodeOrder: getNodeOrder,
|
||||||
|
setNodeOrder: setNodeOrder,
|
||||||
|
|
||||||
moveNodeToTab: moveNodeToTab,
|
moveNodeToTab: moveNodeToTab,
|
||||||
|
|
||||||
addLink: addLink,
|
addLink: addLink,
|
||||||
@ -2265,16 +2538,10 @@ RED.nodes = (function() {
|
|||||||
addGroup: addGroup,
|
addGroup: addGroup,
|
||||||
removeGroup: removeGroup,
|
removeGroup: removeGroup,
|
||||||
group: function(id) { return groups[id] },
|
group: function(id) { return groups[id] },
|
||||||
groups: function(z) { return groupsByZ[z]||[] },
|
groups: function(z) { return groupsByZ[z]?groupsByZ[z].slice():[] },
|
||||||
|
|
||||||
eachNode: function(cb) {
|
eachNode: function(cb) {
|
||||||
for (var id in nodes) {
|
allNodes.eachNode(cb);
|
||||||
if (nodes.hasOwnProperty(id)) {
|
|
||||||
if (cb(nodes[id]) === false) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
eachLink: function(cb) {
|
eachLink: function(cb) {
|
||||||
for (var l=0;l<links.length;l++) {
|
for (var l=0;l<links.length;l++) {
|
||||||
|
@ -559,6 +559,22 @@ var RED = (function() {
|
|||||||
{id:"menu-item-projects-settings",label:RED._("menu.label.projects-settings"),disabled:false,onselect:"core:show-project-settings"}
|
{id:"menu-item-projects-settings",label:RED._("menu.label.projects-settings"),disabled:false,onselect:"core:show-project-settings"}
|
||||||
]});
|
]});
|
||||||
}
|
}
|
||||||
|
menuOptions.push({id:"menu-item-edit-menu", label:"Edit", options: [
|
||||||
|
{id: "menu-item-edit-undo", label:RED._("keyboard.undoChange"), disabled: true, onselect: "core:undo"},
|
||||||
|
{id: "menu-item-edit-redo", label:RED._("keyboard.redoChange"), disabled: true, onselect: "core:redo"},
|
||||||
|
null,
|
||||||
|
{id: "menu-item-edit-cut", label:RED._("keyboard.cutNode"), onselect: "core:cut-selection-to-internal-clipboard"},
|
||||||
|
{id: "menu-item-edit-copy", label:RED._("keyboard.copyNode"), onselect: "core:copy-selection-to-internal-clipboard"},
|
||||||
|
{id: "menu-item-edit-paste", label:RED._("keyboard.pasteNode"), disabled: true, onselect: "core:paste-from-internal-clipboard"},
|
||||||
|
null,
|
||||||
|
{id: "menu-item-edit-copy-group-style", label:RED._("keyboard.copyGroupStyle"), onselect: "core:copy-group-style"},
|
||||||
|
{id: "menu-item-edit-paste-group-style", label:RED._("keyboard.pasteGroupStyle"), disabled: true, onselect: "core:paste-group-style"},
|
||||||
|
null,
|
||||||
|
{id: "menu-item-edit-select-all", label:RED._("keyboard.selectAll"), onselect: "core:select-all-nodes"},
|
||||||
|
{id: "menu-item-edit-select-connected", label:RED._("keyboard.selectAllConnected"), onselect: "core:select-connected-nodes"},
|
||||||
|
{id: "menu-item-edit-select-none", label:RED._("keyboard.selectNone"), onselect: "core:select-none"}
|
||||||
|
]});
|
||||||
|
|
||||||
menuOptions.push({id:"menu-item-view-menu",label:RED._("menu.label.view.view"),options:[
|
menuOptions.push({id:"menu-item-view-menu",label:RED._("menu.label.view.view"),options:[
|
||||||
{id:"menu-item-palette",label:RED._("menu.label.palette.show"),toggle:true,onselect:"core:toggle-palette", selected: true},
|
{id:"menu-item-palette",label:RED._("menu.label.palette.show"),toggle:true,onselect:"core:toggle-palette", selected: true},
|
||||||
{id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:"core:toggle-sidebar", selected: true},
|
{id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:"core:toggle-sidebar", selected: true},
|
||||||
@ -566,6 +582,25 @@ var RED = (function() {
|
|||||||
{id:"menu-item-action-list",label:RED._("keyboard.actionList"),onselect:"core:show-action-list"},
|
{id:"menu-item-action-list",label:RED._("keyboard.actionList"),onselect:"core:show-action-list"},
|
||||||
null
|
null
|
||||||
]});
|
]});
|
||||||
|
|
||||||
|
menuOptions.push({id:"menu-item-arrange-menu", label:RED._("menu.label.arrange"), options: [
|
||||||
|
{id: "menu-item-view-tools-move-to-back", label:RED._("menu.label.moveToBack"), disabled: true, onselect: "core:move-selection-to-back"},
|
||||||
|
{id: "menu-item-view-tools-move-to-front", label:RED._("menu.label.moveToFront"), disabled: true, onselect: "core:move-selection-to-front"},
|
||||||
|
{id: "menu-item-view-tools-move-backwards", label:RED._("menu.label.moveBackwards"), disabled: true, onselect: "core:move-selection-backwards"},
|
||||||
|
{id: "menu-item-view-tools-move-forwards", label:RED._("menu.label.moveForwards"), disabled: true, onselect: "core:move-selection-forwards"},
|
||||||
|
null,
|
||||||
|
{id: "menu-item-view-tools-align-left", label:RED._("menu.label.alignLeft"), disabled: true, onselect: "core:align-selection-to-left"},
|
||||||
|
{id: "menu-item-view-tools-align-center", label:RED._("menu.label.alignCenter"), disabled: true, onselect: "core:align-selection-to-center"},
|
||||||
|
{id: "menu-item-view-tools-align-right", label:RED._("menu.label.alignRight"), disabled: true, onselect: "core:align-selection-to-right"},
|
||||||
|
null,
|
||||||
|
{id: "menu-item-view-tools-align-top", label:RED._("menu.label.alignTop"), disabled: true, onselect: "core:align-selection-to-top"},
|
||||||
|
{id: "menu-item-view-tools-align-middle", label:RED._("menu.label.alignMiddle"), disabled: true, onselect: "core:align-selection-to-middle"},
|
||||||
|
{id: "menu-item-view-tools-align-bottom", label:RED._("menu.label.alignBottom"), disabled: true, onselect: "core:align-selection-to-bottom"},
|
||||||
|
null,
|
||||||
|
{id: "menu-item-view-tools-distribute-horizontally", label:RED._("menu.label.distributeHorizontally"), disabled: true, onselect: "core:distribute-selection-horizontally"},
|
||||||
|
{id: "menu-item-view-tools-distribute-veritcally", label:RED._("menu.label.distributeVertically"), disabled: true, onselect: "core:distribute-selection-vertically"}
|
||||||
|
]});
|
||||||
|
|
||||||
menuOptions.push(null);
|
menuOptions.push(null);
|
||||||
if (RED.settings.theme("menu.menu-item-import-library", true)) {
|
if (RED.settings.theme("menu.menu-item-import-library", true)) {
|
||||||
menuOptions.push({id: "menu-item-import", label: RED._("menu.label.import"), onselect: "core:show-import-dialog"});
|
menuOptions.push({id: "menu-item-import", label: RED._("menu.label.import"), onselect: "core:show-import-dialog"});
|
||||||
@ -626,7 +661,6 @@ var RED = (function() {
|
|||||||
RED.user.init();
|
RED.user.init();
|
||||||
RED.notifications.init();
|
RED.notifications.init();
|
||||||
RED.library.init();
|
RED.library.init();
|
||||||
RED.keyboard.init();
|
|
||||||
RED.palette.init();
|
RED.palette.init();
|
||||||
RED.eventLog.init();
|
RED.eventLog.init();
|
||||||
|
|
||||||
@ -655,7 +689,7 @@ var RED = (function() {
|
|||||||
|
|
||||||
RED.deploy.init(RED.settings.theme("deployButton",null));
|
RED.deploy.init(RED.settings.theme("deployButton",null));
|
||||||
|
|
||||||
buildMainMenu();
|
RED.keyboard.init(buildMainMenu);
|
||||||
|
|
||||||
RED.nodes.init();
|
RED.nodes.init();
|
||||||
RED.comms.connect();
|
RED.comms.connect();
|
||||||
|
@ -88,6 +88,13 @@ RED.menu = (function() {
|
|||||||
linkContent += '</a>';
|
linkContent += '</a>';
|
||||||
|
|
||||||
var link = $(linkContent).appendTo(item);
|
var link = $(linkContent).appendTo(item);
|
||||||
|
opt.link = link;
|
||||||
|
if (typeof opt.onselect === 'string') {
|
||||||
|
var shortcut = RED.keyboard.getShortcut(opt.onselect);
|
||||||
|
if (shortcut && shortcut.key) {
|
||||||
|
opt.shortcutSpan = $('<span class="red-ui-popover-key">'+RED.keyboard.formatKey(shortcut.key, true)+'</span>').appendTo(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
menuItems[opt.id] = opt;
|
menuItems[opt.id] = opt;
|
||||||
|
|
||||||
@ -276,6 +283,22 @@ RED.menu = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function refreshShortcuts() {
|
||||||
|
for (var id in menuItems) {
|
||||||
|
if (menuItems.hasOwnProperty(id)) {
|
||||||
|
var opt = menuItems[id];
|
||||||
|
if (typeof opt.onselect === "string" && opt.shortcutSpan) {
|
||||||
|
opt.shortcutSpan.remove();
|
||||||
|
delete opt.shortcutSpan;
|
||||||
|
var shortcut = RED.keyboard.getShortcut(opt.onselect);
|
||||||
|
if (shortcut && shortcut.key) {
|
||||||
|
opt.shortcutSpan = $('<span class="red-ui-popover-key">'+RED.keyboard.formatKey(shortcut.key, true)+'</span>').appendTo(opt.link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
init: createMenu,
|
init: createMenu,
|
||||||
setSelected: setSelected,
|
setSelected: setSelected,
|
||||||
@ -284,6 +307,7 @@ RED.menu = (function() {
|
|||||||
setDisabled: setDisabled,
|
setDisabled: setDisabled,
|
||||||
addItem: addItem,
|
addItem: addItem,
|
||||||
removeItem: removeItem,
|
removeItem: removeItem,
|
||||||
setAction: setAction
|
setAction: setAction,
|
||||||
|
refreshShortcuts: refreshShortcuts
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
@ -664,6 +664,8 @@ RED.tabs = (function() {
|
|||||||
link.on("dblclick", function(evt) { evt.stopPropagation(); evt.preventDefault(); })
|
link.on("dblclick", function(evt) { evt.stopPropagation(); evt.preventDefault(); })
|
||||||
|
|
||||||
|
|
||||||
|
$('<span class="red-ui-tabs-fade"></span>').appendTo(li);
|
||||||
|
|
||||||
if (tab.closeable) {
|
if (tab.closeable) {
|
||||||
li.addClass("red-ui-tabs-closeable")
|
li.addClass("red-ui-tabs-closeable")
|
||||||
var closeLink = $("<a/>",{href:"#",class:"red-ui-tab-close"}).appendTo(li);
|
var closeLink = $("<a/>",{href:"#",class:"red-ui-tab-close"}).appendTo(li);
|
||||||
@ -674,8 +676,6 @@ RED.tabs = (function() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$('<span class="red-ui-tabs-fade"></span>').appendTo(li);
|
|
||||||
|
|
||||||
var badges = $('<span class="red-ui-tabs-badges"></span>').appendTo(li);
|
var badges = $('<span class="red-ui-tabs-badges"></span>').appendTo(li);
|
||||||
if (options.onselect) {
|
if (options.onselect) {
|
||||||
$('<i class="red-ui-tabs-badge-changed fa fa-circle"></i>').appendTo(badges);
|
$('<i class="red-ui-tabs-badge-changed fa fa-circle"></i>').appendTo(badges);
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -942,6 +942,15 @@ RED.editor.codeEditor.monaco = (function() {
|
|||||||
/*********** Create the monaco editor ***************/
|
/*********** Create the monaco editor ***************/
|
||||||
var ed = monaco.editor.create(el, editorOptions);
|
var ed = monaco.editor.create(el, editorOptions);
|
||||||
|
|
||||||
|
//Unbind ctrl-Enter (default action is to insert a newline in editor) This permits the shortcut to close the tray.
|
||||||
|
try {
|
||||||
|
ed._standaloneKeybindingService.addDynamicKeybinding(
|
||||||
|
'-editor.action.insertLineAfter', // command ID prefixed by '-'
|
||||||
|
null, // keybinding
|
||||||
|
() => {} // need to pass an empty handler
|
||||||
|
);
|
||||||
|
} catch (error) { }
|
||||||
|
|
||||||
ed.nodered = {
|
ed.nodered = {
|
||||||
refreshModuleLibs: refreshModuleLibs //expose this for function node externalModules refresh
|
refreshModuleLibs: refreshModuleLibs //expose this for function node externalModules refresh
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
RED.colorPicker = (function() {
|
RED.editor.colorPicker = RED.colorPicker = (function() {
|
||||||
|
|
||||||
function create(options) {
|
function create(options) {
|
||||||
var color = options.value;
|
var color = options.value;
|
616
packages/node_modules/@node-red/editor-client/src/js/ui/editors/envVarList.js
vendored
Normal file
616
packages/node_modules/@node-red/editor-client/src/js/ui/editors/envVarList.js
vendored
Normal file
@ -0,0 +1,616 @@
|
|||||||
|
RED.editor.envVarList = (function() {
|
||||||
|
|
||||||
|
var currentLocale = 'en-US';
|
||||||
|
var DEFAULT_ENV_TYPE_LIST = ['str','num','bool','json','bin','env'];
|
||||||
|
var DEFAULT_ENV_TYPE_LIST_INC_CRED = ['str','num','bool','json','bin','env','cred'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create env var edit interface
|
||||||
|
* @param container - container
|
||||||
|
* @param node - subflow node
|
||||||
|
*/
|
||||||
|
function buildPropertiesList(envContainer, node) {
|
||||||
|
|
||||||
|
var isTemplateNode = (node.type === "subflow");
|
||||||
|
|
||||||
|
envContainer
|
||||||
|
.css({
|
||||||
|
'min-height':'150px',
|
||||||
|
'min-width':'450px'
|
||||||
|
})
|
||||||
|
.editableList({
|
||||||
|
header: isTemplateNode?$('<div><div><div></div><div data-i18n="common.label.name"></div><div data-i18n="editor-tab.defaultValue"></div><div></div></div></div>'):undefined,
|
||||||
|
addItem: function(container, i, opt) {
|
||||||
|
// If this is an instance node, these are properties unique to
|
||||||
|
// this instance - ie opt.parent will not be defined.
|
||||||
|
|
||||||
|
if (isTemplateNode) {
|
||||||
|
container.addClass("red-ui-editor-subflow-env-editable")
|
||||||
|
}
|
||||||
|
|
||||||
|
var envRow = $('<div/>').appendTo(container);
|
||||||
|
var nameField = null;
|
||||||
|
var valueField = null;
|
||||||
|
|
||||||
|
nameField = $('<input/>', {
|
||||||
|
class: "node-input-env-name",
|
||||||
|
type: "text",
|
||||||
|
placeholder: RED._("common.label.name")
|
||||||
|
}).attr("autocomplete","disable").appendTo(envRow).val(opt.name);
|
||||||
|
valueField = $('<input/>',{
|
||||||
|
style: "width:100%",
|
||||||
|
class: "node-input-env-value",
|
||||||
|
type: "text",
|
||||||
|
}).attr("autocomplete","disable").appendTo(envRow)
|
||||||
|
valueField.typedInput({default:'str',types:isTemplateNode?DEFAULT_ENV_TYPE_LIST:DEFAULT_ENV_TYPE_LIST_INC_CRED});
|
||||||
|
valueField.typedInput('type', opt.type);
|
||||||
|
if (opt.type === "cred") {
|
||||||
|
if (opt.value) {
|
||||||
|
valueField.typedInput('value', opt.value);
|
||||||
|
} else if (node.credentials && node.credentials[opt.name]) {
|
||||||
|
valueField.typedInput('value', node.credentials[opt.name]);
|
||||||
|
} else if (node.credentials && node.credentials['has_'+opt.name]) {
|
||||||
|
valueField.typedInput('value', "__PWRD__");
|
||||||
|
} else {
|
||||||
|
valueField.typedInput('value', "");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
valueField.typedInput('value', opt.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
opt.nameField = nameField;
|
||||||
|
opt.valueField = valueField;
|
||||||
|
|
||||||
|
var actionButton = $('<a/>',{href:"#",class:"red-ui-editableList-item-remove red-ui-button red-ui-button-small"}).appendTo(envRow);
|
||||||
|
$('<i/>',{class:"fa "+(opt.parent?"fa-reply":"fa-remove")}).appendTo(actionButton);
|
||||||
|
var removeTip = RED.popover.tooltip(actionButton,RED._("subflow.env.remove"));
|
||||||
|
actionButton.on("click", function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
removeTip.close();
|
||||||
|
container.parent().addClass("red-ui-editableList-item-deleting")
|
||||||
|
container.fadeOut(300, function() {
|
||||||
|
envContainer.editableList('removeItem',opt);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isTemplateNode) {
|
||||||
|
// Add the UI customisation row
|
||||||
|
// if `opt.ui` does not exist, then apply defaults. If these
|
||||||
|
// defaults do not change then they will get stripped off
|
||||||
|
// before saving.
|
||||||
|
if (opt.type === 'cred') {
|
||||||
|
opt.ui = opt.ui || {
|
||||||
|
icon: "",
|
||||||
|
type: "cred"
|
||||||
|
}
|
||||||
|
opt.ui.type = "cred";
|
||||||
|
} else {
|
||||||
|
opt.ui = opt.ui || {
|
||||||
|
icon: "",
|
||||||
|
type: "input",
|
||||||
|
opts: {types:DEFAULT_ENV_TYPE_LIST}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opt.ui.label = opt.ui.label || {};
|
||||||
|
opt.ui.type = opt.ui.type || "input";
|
||||||
|
|
||||||
|
var uiRow = $('<div/>').appendTo(container).hide();
|
||||||
|
// save current info for reverting on cancel
|
||||||
|
// var copy = $.extend(true, {}, ui);
|
||||||
|
|
||||||
|
$('<a href="#"><i class="fa fa-angle-right"></a>').prependTo(envRow).on("click", function (evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
if ($(this).hasClass('expanded')) {
|
||||||
|
uiRow.slideUp();
|
||||||
|
$(this).removeClass('expanded');
|
||||||
|
} else {
|
||||||
|
uiRow.slideDown();
|
||||||
|
$(this).addClass('expanded');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
buildEnvEditRow(uiRow, opt.ui, nameField, valueField);
|
||||||
|
nameField.trigger('change');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sortable: ".red-ui-editableList-item-handle",
|
||||||
|
removable: false
|
||||||
|
});
|
||||||
|
var parentEnv = {};
|
||||||
|
var envList = [];
|
||||||
|
if (/^subflow:/.test(node.type)) {
|
||||||
|
var subflowDef = RED.nodes.subflow(node.type.substring(8));
|
||||||
|
if (subflowDef.env) {
|
||||||
|
subflowDef.env.forEach(function(env) {
|
||||||
|
var item = {
|
||||||
|
name:env.name,
|
||||||
|
parent: {
|
||||||
|
type: env.type,
|
||||||
|
value: env.value,
|
||||||
|
ui: env.ui
|
||||||
|
}
|
||||||
|
}
|
||||||
|
envList.push(item);
|
||||||
|
parentEnv[env.name] = item;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.env) {
|
||||||
|
for (var i = 0; i < node.env.length; i++) {
|
||||||
|
var env = node.env[i];
|
||||||
|
if (parentEnv.hasOwnProperty(env.name)) {
|
||||||
|
parentEnv[env.name].type = env.type;
|
||||||
|
parentEnv[env.name].value = env.value;
|
||||||
|
} else {
|
||||||
|
envList.push({
|
||||||
|
name: env.name,
|
||||||
|
type: env.type,
|
||||||
|
value: env.value,
|
||||||
|
ui: env.ui
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
envList.forEach(function(env) {
|
||||||
|
if (env.parent && env.parent.ui && env.parent.ui.type === 'hide') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isTemplateNode && env.parent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
envContainer.editableList('addItem', JSON.parse(JSON.stringify(env)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create UI edit interface for environment variable
|
||||||
|
* @param container - container
|
||||||
|
* @param env - env var definition
|
||||||
|
* @param nameField - name field of env var
|
||||||
|
* @param valueField - value field of env var
|
||||||
|
*/
|
||||||
|
function buildEnvEditRow(container, ui, nameField, valueField) {
|
||||||
|
container.addClass("red-ui-editor-subflow-env-ui-row")
|
||||||
|
var topRow = $('<div></div>').appendTo(container);
|
||||||
|
$('<div></div>').appendTo(topRow);
|
||||||
|
$('<div>').text(RED._("editor.icon")).appendTo(topRow);
|
||||||
|
$('<div>').text(RED._("editor.label")).appendTo(topRow);
|
||||||
|
$('<div>').text(RED._("editor.inputType")).appendTo(topRow);
|
||||||
|
|
||||||
|
var row = $('<div></div>').appendTo(container);
|
||||||
|
$('<div><i class="red-ui-editableList-item-handle fa fa-bars"></i></div>').appendTo(row);
|
||||||
|
var typeOptions = {
|
||||||
|
'input': {types:DEFAULT_ENV_TYPE_LIST},
|
||||||
|
'select': {opts:[]},
|
||||||
|
'spinner': {},
|
||||||
|
'cred': {}
|
||||||
|
};
|
||||||
|
if (ui.opts) {
|
||||||
|
typeOptions[ui.type] = ui.opts;
|
||||||
|
} else {
|
||||||
|
// Pick up the default values if not otherwise provided
|
||||||
|
ui.opts = typeOptions[ui.type];
|
||||||
|
}
|
||||||
|
var iconCell = $('<div></div>').appendTo(row);
|
||||||
|
|
||||||
|
var iconButton = $('<a href="#"></a>').appendTo(iconCell);
|
||||||
|
iconButton.on("click", function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
var icon = ui.icon || "";
|
||||||
|
var iconPath = (icon ? RED.utils.separateIconPath(icon) : {});
|
||||||
|
RED.editor.iconPicker.show(iconButton, null, iconPath, true, function (newIcon) {
|
||||||
|
iconButton.empty();
|
||||||
|
var path = newIcon || "";
|
||||||
|
var newPath = RED.utils.separateIconPath(path);
|
||||||
|
if (newPath) {
|
||||||
|
$('<i class="fa"></i>').addClass(newPath.file).appendTo(iconButton);
|
||||||
|
}
|
||||||
|
ui.icon = path;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
if (ui.icon) {
|
||||||
|
var newPath = RED.utils.separateIconPath(ui.icon);
|
||||||
|
$('<i class="fa '+newPath.file+'"></i>').appendTo(iconButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
var labelCell = $('<div></div>').appendTo(row);
|
||||||
|
|
||||||
|
var label = ui.label && ui.label[currentLocale] || "";
|
||||||
|
var labelInput = $('<input type="text">').val(label).appendTo(labelCell);
|
||||||
|
ui.labelField = labelInput;
|
||||||
|
labelInput.on('change', function(evt) {
|
||||||
|
ui.label = ui.label || {};
|
||||||
|
var val = $(this).val().trim();
|
||||||
|
if (val === "") {
|
||||||
|
delete ui.label[currentLocale];
|
||||||
|
} else {
|
||||||
|
ui.label[currentLocale] = val;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
var labelIcon = $('<span class="red-ui-editor-subflow-env-lang-icon"><i class="fa fa-language"></i></span>').appendTo(labelCell);
|
||||||
|
RED.popover.tooltip(labelIcon,function() {
|
||||||
|
var langs = Object.keys(ui.label);
|
||||||
|
var content = $("<div>");
|
||||||
|
if (langs.indexOf(currentLocale) === -1) {
|
||||||
|
langs.push(currentLocale);
|
||||||
|
langs.sort();
|
||||||
|
}
|
||||||
|
langs.forEach(function(l) {
|
||||||
|
var row = $('<div>').appendTo(content);
|
||||||
|
$('<span>').css({display:"inline-block",width:"120px"}).text(RED._("languages."+l)+(l===currentLocale?"*":"")).appendTo(row);
|
||||||
|
$('<span>').text(ui.label[l]||"").appendTo(row);
|
||||||
|
});
|
||||||
|
return content;
|
||||||
|
})
|
||||||
|
|
||||||
|
nameField.on('change',function(evt) {
|
||||||
|
labelInput.attr("placeholder",$(this).val())
|
||||||
|
});
|
||||||
|
|
||||||
|
var inputCell = $('<div></div>').appendTo(row);
|
||||||
|
var inputCellInput = $('<input type="text">').css("width","100%").appendTo(inputCell);
|
||||||
|
if (ui.type === "input") {
|
||||||
|
inputCellInput.val(ui.opts.types.join(","));
|
||||||
|
}
|
||||||
|
var checkbox;
|
||||||
|
var selectBox;
|
||||||
|
|
||||||
|
inputCellInput.typedInput({
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
value:"input",
|
||||||
|
label:RED._("editor.inputs.input"), icon:"fa fa-i-cursor",showLabel:false,multiple:true,options:[
|
||||||
|
{value:"str",label:RED._("editor.types.str"),icon:"red/images/typedInput/az.svg"},
|
||||||
|
{value:"num",label:RED._("editor.types.num"),icon:"red/images/typedInput/09.svg"},
|
||||||
|
{value:"bool",label:RED._("editor.types.bool"),icon:"red/images/typedInput/bool.svg"},
|
||||||
|
{value:"json",label:RED._("editor.types.json"),icon:"red/images/typedInput/json.svg"},
|
||||||
|
{value: "bin",label: RED._("editor.types.bin"),icon: "red/images/typedInput/bin.svg"},
|
||||||
|
{value: "env",label: RED._("editor.types.env"),icon: "red/images/typedInput/env.svg"},
|
||||||
|
{value: "cred",label: RED._("editor.types.cred"),icon: "fa fa-lock"}
|
||||||
|
],
|
||||||
|
default: DEFAULT_ENV_TYPE_LIST,
|
||||||
|
valueLabel: function(container,value) {
|
||||||
|
container.css("padding",0);
|
||||||
|
var innerContainer = $('<div class="red-ui-editor-subflow-env-input-type"></div>').appendTo(container);
|
||||||
|
|
||||||
|
var input = $('<div class="placeholder-input">').appendTo(innerContainer);
|
||||||
|
$('<span><i class="fa fa-i-cursor"></i></span>').appendTo(input);
|
||||||
|
if (value.length) {
|
||||||
|
value.forEach(function(v) {
|
||||||
|
if (!/^fa /.test(v.icon)) {
|
||||||
|
$('<img>',{src:v.icon,style:"max-width:14px; padding: 0 3px; margin-top:-4px; margin-left: 1px"}).appendTo(input);
|
||||||
|
} else {
|
||||||
|
var s = $('<span>',{style:"max-width:14px; padding: 0 3px; margin-top:-4px; margin-left: 1px"}).appendTo(input);
|
||||||
|
$("<i>",{class: v.icon}).appendTo(s);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
$('<span class="red-ui-editor-subflow-env-input-type-placeholder"></span>').text(RED._("editor.selectType")).appendTo(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "cred",
|
||||||
|
label: RED._("typedInput.type.cred"), icon:"fa fa-lock", showLabel: false,
|
||||||
|
valueLabel: function(container,value) {
|
||||||
|
container.css("padding",0);
|
||||||
|
var innerContainer = $('<div class="red-ui-editor-subflow-env-input-type">').css({
|
||||||
|
"border-top-right-radius": "4px",
|
||||||
|
"border-bottom-right-radius": "4px"
|
||||||
|
}).appendTo(container);
|
||||||
|
$('<div class="placeholder-input">').html("••••••••").appendTo(innerContainer);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value:"select",
|
||||||
|
label:RED._("editor.inputs.select"), icon:"fa fa-tasks",showLabel:false,
|
||||||
|
valueLabel: function(container,value) {
|
||||||
|
container.css("padding","0");
|
||||||
|
|
||||||
|
selectBox = $('<select></select>').appendTo(container);
|
||||||
|
if (ui.opts && Array.isArray(ui.opts.opts)) {
|
||||||
|
ui.opts.opts.forEach(function(o) {
|
||||||
|
var label = lookupLabel(o.l, o.l["en-US"]||o.v, currentLocale);
|
||||||
|
// $('<option>').val((o.t||'str')+":"+o.v).text(label).appendTo(selectBox);
|
||||||
|
$('<option>').val(o.v).text(label).appendTo(selectBox);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
selectBox.on('change', function(evt) {
|
||||||
|
var v = selectBox.val();
|
||||||
|
// var parts = v.split(":");
|
||||||
|
// var t = parts.shift();
|
||||||
|
// v = parts.join(":");
|
||||||
|
//
|
||||||
|
// valueField.typedInput("type",'str')
|
||||||
|
valueField.typedInput("value",v)
|
||||||
|
});
|
||||||
|
selectBox.val(valueField.typedInput("value"));
|
||||||
|
// selectBox.val(valueField.typedInput('type')+":"+valueField.typedInput("value"));
|
||||||
|
},
|
||||||
|
expand: {
|
||||||
|
icon: "fa-caret-down",
|
||||||
|
minWidth: 400,
|
||||||
|
content: function(container) {
|
||||||
|
var content = $('<div class="red-ui-editor-subflow-ui-edit-panel">').appendTo(container);
|
||||||
|
var optList = $('<ol>').appendTo(content).editableList({
|
||||||
|
header:$("<div><div>"+RED._("editor.select.label")+"</div><div>"+RED._("editor.select.value")+"</div></div>"),
|
||||||
|
addItem: function(row,index,itemData) {
|
||||||
|
var labelDiv = $('<div>').appendTo(row);
|
||||||
|
var label = lookupLabel(itemData.l, "", currentLocale);
|
||||||
|
itemData.label = $('<input type="text">').val(label).appendTo(labelDiv);
|
||||||
|
itemData.label.on('keydown', function(evt) {
|
||||||
|
if (evt.keyCode === 13) {
|
||||||
|
itemData.input.focus();
|
||||||
|
evt.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var labelIcon = $('<span class="red-ui-editor-subflow-env-lang-icon"><i class="fa fa-language"></i></span>').appendTo(labelDiv);
|
||||||
|
RED.popover.tooltip(labelIcon,function() {
|
||||||
|
return currentLocale;
|
||||||
|
})
|
||||||
|
itemData.input = $('<input type="text">').val(itemData.v).appendTo(row);
|
||||||
|
|
||||||
|
// Problem using a TI here:
|
||||||
|
// - this is in a popout panel
|
||||||
|
// - clicking the expand button in the TI will close the parent edit tray
|
||||||
|
// and open the type editor.
|
||||||
|
// - but it leaves the popout panel over the top.
|
||||||
|
// - there is no way to get back to the popout panel after closing the type editor
|
||||||
|
//.typedInput({default:itemData.t||'str', types:DEFAULT_ENV_TYPE_LIST});
|
||||||
|
itemData.input.on('keydown', function(evt) {
|
||||||
|
if (evt.keyCode === 13) {
|
||||||
|
// Enter or Tab
|
||||||
|
var index = optList.editableList('indexOf',itemData);
|
||||||
|
var length = optList.editableList('length');
|
||||||
|
if (index + 1 === length) {
|
||||||
|
var newItem = {};
|
||||||
|
optList.editableList('addItem',newItem);
|
||||||
|
setTimeout(function() {
|
||||||
|
if (newItem.label) {
|
||||||
|
newItem.label.focus();
|
||||||
|
}
|
||||||
|
},100)
|
||||||
|
} else {
|
||||||
|
var nextItem = optList.editableList('getItemAt',index+1);
|
||||||
|
if (nextItem.label) {
|
||||||
|
nextItem.label.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
evt.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
sortable: true,
|
||||||
|
removable: true,
|
||||||
|
height: 160
|
||||||
|
})
|
||||||
|
if (ui.opts.opts.length > 0) {
|
||||||
|
ui.opts.opts.forEach(function(o) {
|
||||||
|
optList.editableList('addItem',$.extend(true,{},o))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
optList.editableList('addItem',{})
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
onclose: function() {
|
||||||
|
var items = optList.editableList('items');
|
||||||
|
var vals = [];
|
||||||
|
items.each(function (i,el) {
|
||||||
|
var data = el.data('data');
|
||||||
|
var l = data.label.val().trim();
|
||||||
|
var v = data.input.val();
|
||||||
|
// var t = data.input.typedInput('type');
|
||||||
|
// var v = data.input.typedInput('value');
|
||||||
|
if (l.length > 0) {
|
||||||
|
data.l = data.l || {};
|
||||||
|
data.l[currentLocale] = l;
|
||||||
|
}
|
||||||
|
data.v = v;
|
||||||
|
|
||||||
|
if (l.length > 0 || v.length > 0) {
|
||||||
|
var val = {l:data.l,v:data.v};
|
||||||
|
// if (t !== 'str') {
|
||||||
|
// val.t = t;
|
||||||
|
// }
|
||||||
|
vals.push(val);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ui.opts.opts = vals;
|
||||||
|
inputCellInput.typedInput('value',Date.now())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value:"checkbox",
|
||||||
|
label:RED._("editor.inputs.checkbox"), icon:"fa fa-check-square-o",showLabel:false,
|
||||||
|
valueLabel: function(container,value) {
|
||||||
|
container.css("padding",0);
|
||||||
|
checkbox = $('<input type="checkbox">').appendTo(container);
|
||||||
|
checkbox.on('change', function(evt) {
|
||||||
|
valueField.typedInput('value',$(this).prop('checked')?"true":"false");
|
||||||
|
})
|
||||||
|
checkbox.prop('checked',valueField.typedInput('value')==="true");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value:"spinner",
|
||||||
|
label:RED._("editor.inputs.spinner"), icon:"fa fa-sort-numeric-asc", showLabel:false,
|
||||||
|
valueLabel: function(container,value) {
|
||||||
|
container.css("padding",0);
|
||||||
|
var innerContainer = $('<div class="red-ui-editor-subflow-env-input-type"></div>').appendTo(container);
|
||||||
|
|
||||||
|
var input = $('<div class="placeholder-input">').appendTo(innerContainer);
|
||||||
|
$('<span><i class="fa fa-sort-numeric-asc"></i></span>').appendTo(input);
|
||||||
|
|
||||||
|
var min = ui.opts && ui.opts.min;
|
||||||
|
var max = ui.opts && ui.opts.max;
|
||||||
|
var label = "";
|
||||||
|
if (min !== undefined && max !== undefined) {
|
||||||
|
label = Math.min(min,max)+" - "+Math.max(min,max);
|
||||||
|
} else if (min !== undefined) {
|
||||||
|
label = "> "+min;
|
||||||
|
} else if (max !== undefined) {
|
||||||
|
label = "< "+max;
|
||||||
|
}
|
||||||
|
$('<span>').css("margin-left","15px").text(label).appendTo(input);
|
||||||
|
},
|
||||||
|
expand: {
|
||||||
|
icon: "fa-caret-down",
|
||||||
|
content: function(container) {
|
||||||
|
var content = $('<div class="red-ui-editor-subflow-ui-edit-panel">').appendTo(container);
|
||||||
|
content.css("padding","8px 5px")
|
||||||
|
var min = ui.opts.min;
|
||||||
|
var max = ui.opts.max;
|
||||||
|
var minInput = $('<input type="number" style="margin-bottom:0; width:60px">');
|
||||||
|
minInput.val(min);
|
||||||
|
var maxInput = $('<input type="number" style="margin-bottom:0; width:60px">');
|
||||||
|
maxInput.val(max);
|
||||||
|
$('<div class="form-row" style="margin-bottom:3px"><label>'+RED._("editor.spinner.min")+'</label></div>').append(minInput).appendTo(content);
|
||||||
|
$('<div class="form-row" style="margin-bottom:0"><label>'+RED._("editor.spinner.max")+'</label></div>').append(maxInput).appendTo(content);
|
||||||
|
return {
|
||||||
|
onclose: function() {
|
||||||
|
var min = minInput.val().trim();
|
||||||
|
var max = maxInput.val().trim();
|
||||||
|
if (min !== "") {
|
||||||
|
ui.opts.min = parseInt(min);
|
||||||
|
} else {
|
||||||
|
delete ui.opts.min;
|
||||||
|
}
|
||||||
|
if (max !== "") {
|
||||||
|
ui.opts.max = parseInt(max);
|
||||||
|
} else {
|
||||||
|
delete ui.opts.max;
|
||||||
|
}
|
||||||
|
inputCellInput.typedInput('value',Date.now())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value:"none",
|
||||||
|
label:RED._("editor.inputs.none"), icon:"fa fa-times",hasValue:false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value:"hide",
|
||||||
|
label:RED._("editor.inputs.hidden"), icon:"fa fa-ban",hasValue:false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
default: 'none'
|
||||||
|
}).on("typedinputtypechange", function(evt,type) {
|
||||||
|
ui.type = $(this).typedInput("type");
|
||||||
|
ui.opts = typeOptions[ui.type];
|
||||||
|
if (ui.type === 'input') {
|
||||||
|
// In the case of 'input' type, the typedInput uses the multiple-option
|
||||||
|
// mode. Its value needs to be set to a comma-separately list of the
|
||||||
|
// selected options.
|
||||||
|
inputCellInput.typedInput('value',ui.opts.types.join(","))
|
||||||
|
} else {
|
||||||
|
// No other type cares about `value`, but doing this will
|
||||||
|
// force a refresh of the label now that `ui.opts` has
|
||||||
|
// been updated.
|
||||||
|
inputCellInput.typedInput('value',Date.now())
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ui.type) {
|
||||||
|
case 'input':
|
||||||
|
valueField.typedInput('types',ui.opts.types);
|
||||||
|
break;
|
||||||
|
case 'select':
|
||||||
|
valueField.typedInput('types',['str']);
|
||||||
|
break;
|
||||||
|
case 'checkbox':
|
||||||
|
valueField.typedInput('types',['bool']);
|
||||||
|
break;
|
||||||
|
case 'spinner':
|
||||||
|
valueField.typedInput('types',['num']);
|
||||||
|
break;
|
||||||
|
case 'cred':
|
||||||
|
valueField.typedInput('types',['cred']);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
valueField.typedInput('types',DEFAULT_ENV_TYPE_LIST)
|
||||||
|
}
|
||||||
|
if (ui.type === 'checkbox') {
|
||||||
|
valueField.typedInput('type','bool');
|
||||||
|
} else if (ui.type === 'spinner') {
|
||||||
|
valueField.typedInput('type','num');
|
||||||
|
}
|
||||||
|
if (ui.type !== 'checkbox') {
|
||||||
|
checkbox = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}).on("change", function(evt,type) {
|
||||||
|
if (ui.type === 'input') {
|
||||||
|
var types = inputCellInput.typedInput('value');
|
||||||
|
ui.opts.types = (types === "") ? ["str"] : types.split(",");
|
||||||
|
valueField.typedInput('types',ui.opts.types);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
valueField.on("change", function(evt) {
|
||||||
|
if (checkbox) {
|
||||||
|
checkbox.prop('checked',$(this).typedInput('value')==="true")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// Set the input to the right type. This will trigger the 'typedinputtypechange'
|
||||||
|
// event handler (just above ^^) to update the value if needed
|
||||||
|
inputCellInput.typedInput('type',ui.type)
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLocale(l, list) {
|
||||||
|
currentLocale = l;
|
||||||
|
if (list) {
|
||||||
|
var items = list.editableList("items");
|
||||||
|
items.each(function (i, item) {
|
||||||
|
var entry = $(this).data('data');
|
||||||
|
var labelField = entry.ui.labelField;
|
||||||
|
labelField.val(lookupLabel(entry.ui.label, "", currentLocale));
|
||||||
|
if (labelField.timeout) {
|
||||||
|
clearTimeout(labelField.timeout);
|
||||||
|
delete labelField.timeout;
|
||||||
|
}
|
||||||
|
labelField.addClass("input-updated");
|
||||||
|
labelField.timeout = setTimeout(function() {
|
||||||
|
delete labelField.timeout
|
||||||
|
labelField.removeClass("input-updated");
|
||||||
|
},3000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup text for specific locale
|
||||||
|
* @param labels - dict of labels
|
||||||
|
* @param defaultLabel - fallback label if not found
|
||||||
|
* @param locale - target locale
|
||||||
|
* @returns {string} text for specified locale
|
||||||
|
*/
|
||||||
|
function lookupLabel(labels, defaultLabel, locale) {
|
||||||
|
if (labels) {
|
||||||
|
if (labels[locale]) {
|
||||||
|
return labels[locale];
|
||||||
|
}
|
||||||
|
if (locale) {
|
||||||
|
var lang = locale.substring(0, 2);
|
||||||
|
if (labels[lang]) {
|
||||||
|
return labels[lang];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
create: buildPropertiesList,
|
||||||
|
setLocale: setLocale,
|
||||||
|
lookupLabel: lookupLabel,
|
||||||
|
DEFAULT_ENV_TYPE_LIST: DEFAULT_ENV_TYPE_LIST,
|
||||||
|
DEFAULT_ENV_TYPE_LIST_INC_CRED: DEFAULT_ENV_TYPE_LIST_INC_CRED
|
||||||
|
}
|
||||||
|
})();
|
99
packages/node_modules/@node-red/editor-client/src/js/ui/editors/iconPicker.js
vendored
Normal file
99
packages/node_modules/@node-red/editor-client/src/js/ui/editors/iconPicker.js
vendored
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
RED.editor.iconPicker = (function() {
|
||||||
|
function showIconPicker(container, backgroundColor, iconPath, faOnly, done) {
|
||||||
|
var picker = $('<div class="red-ui-icon-picker">');
|
||||||
|
var searchDiv = $("<div>",{class:"red-ui-search-container"}).appendTo(picker);
|
||||||
|
searchInput = $('<input type="text">').attr("placeholder",RED._("editor.searchIcons")).appendTo(searchDiv).searchBox({
|
||||||
|
delay: 50,
|
||||||
|
change: function() {
|
||||||
|
var searchTerm = $(this).val().trim();
|
||||||
|
if (searchTerm === "") {
|
||||||
|
iconList.find(".red-ui-icon-list-module").show();
|
||||||
|
iconList.find(".red-ui-icon-list-icon").show();
|
||||||
|
} else {
|
||||||
|
iconList.find(".red-ui-icon-list-module").hide();
|
||||||
|
iconList.find(".red-ui-icon-list-icon").each(function(i,n) {
|
||||||
|
if ($(n).data('icon').indexOf(searchTerm) === -1) {
|
||||||
|
$(n).hide();
|
||||||
|
} else {
|
||||||
|
$(n).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var row = $('<div>').appendTo(picker);
|
||||||
|
var iconList = $('<div class="red-ui-icon-list">').appendTo(picker);
|
||||||
|
var metaRow = $('<div class="red-ui-icon-meta"></div>').appendTo(picker);
|
||||||
|
var summary = $('<span>').appendTo(metaRow);
|
||||||
|
var resetButton = $('<button type="button" class="red-ui-button red-ui-button-small">'+RED._("editor.useDefault")+'</button>').appendTo(metaRow).on("click", function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
iconPanel.hide();
|
||||||
|
done(null);
|
||||||
|
});
|
||||||
|
if (!backgroundColor && faOnly) {
|
||||||
|
iconList.addClass("red-ui-icon-list-dark");
|
||||||
|
}
|
||||||
|
setTimeout(function() {
|
||||||
|
var iconSets = RED.nodes.getIconSets();
|
||||||
|
Object.keys(iconSets).forEach(function(moduleName) {
|
||||||
|
if (faOnly && (moduleName !== "font-awesome")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var icons = iconSets[moduleName];
|
||||||
|
if (icons.length > 0) {
|
||||||
|
// selectIconModule.append($("<option></option>").val(moduleName).text(moduleName));
|
||||||
|
var header = $('<div class="red-ui-icon-list-module"></div>').text(moduleName).appendTo(iconList);
|
||||||
|
$('<i class="fa fa-cube"></i>').prependTo(header);
|
||||||
|
icons.forEach(function(icon) {
|
||||||
|
var iconDiv = $('<div>',{class:"red-ui-icon-list-icon"}).appendTo(iconList);
|
||||||
|
var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).appendTo(iconDiv);
|
||||||
|
var icon_url = RED.settings.apiRootUrl+"icons/"+moduleName+"/"+icon;
|
||||||
|
iconDiv.data('icon',icon_url);
|
||||||
|
if (backgroundColor) {
|
||||||
|
nodeDiv.css({
|
||||||
|
'backgroundColor': backgroundColor
|
||||||
|
});
|
||||||
|
var borderColor = RED.utils.getDarkerColor(backgroundColor);
|
||||||
|
if (borderColor !== backgroundColor) {
|
||||||
|
nodeDiv.css('border-color',borderColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
|
||||||
|
RED.utils.createIconElement(icon_url, iconContainer, true);
|
||||||
|
|
||||||
|
if (iconPath.module === moduleName && iconPath.file === icon) {
|
||||||
|
iconDiv.addClass("selected");
|
||||||
|
}
|
||||||
|
iconDiv.on("mouseover", function() {
|
||||||
|
summary.text(icon);
|
||||||
|
})
|
||||||
|
iconDiv.on("mouseout", function() {
|
||||||
|
summary.html(" ");
|
||||||
|
})
|
||||||
|
iconDiv.on("click", function() {
|
||||||
|
iconPanel.hide();
|
||||||
|
done(moduleName+"/"+icon);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setTimeout(function() {
|
||||||
|
spinner.remove();
|
||||||
|
},50);
|
||||||
|
},300);
|
||||||
|
var spinner = RED.utils.addSpinnerOverlay(iconList,true);
|
||||||
|
var iconPanel = RED.popover.panel(picker);
|
||||||
|
iconPanel.show({
|
||||||
|
target: container
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
picker.slideDown(100);
|
||||||
|
searchInput.trigger("focus");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
show: showIconPicker
|
||||||
|
}
|
||||||
|
})();
|
514
packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/appearance.js
vendored
Normal file
514
packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/appearance.js
vendored
Normal file
@ -0,0 +1,514 @@
|
|||||||
|
;(function() {
|
||||||
|
|
||||||
|
RED.editor.registerEditPane("editor-tab-appearance", function(node) {
|
||||||
|
return {
|
||||||
|
label: RED._("editor-tab.appearance"),
|
||||||
|
name: RED._("editor-tab.appearance"),
|
||||||
|
iconClass: "fa fa-object-group",
|
||||||
|
create: function(container) {
|
||||||
|
this.content = container;
|
||||||
|
buildAppearanceForm(this.content,node);
|
||||||
|
|
||||||
|
if (node.type === 'subflow') {
|
||||||
|
this.defaultIcon = "node-red/subflow.svg";
|
||||||
|
} else {
|
||||||
|
var iconPath = RED.utils.getDefaultNodeIcon(node._def,node);
|
||||||
|
this.defaultIcon = iconPath.module+"/"+iconPath.file;
|
||||||
|
if (node.icon && node.icon !== this.defaultIcon) {
|
||||||
|
this.isDefaultIcon = false;
|
||||||
|
} else {
|
||||||
|
this.isDefaultIcon = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resize: function(size) {
|
||||||
|
|
||||||
|
},
|
||||||
|
close: function() {
|
||||||
|
|
||||||
|
},
|
||||||
|
show: function() {
|
||||||
|
refreshLabelForm(this.content, node);
|
||||||
|
},
|
||||||
|
apply: function(editState) {
|
||||||
|
if (updateLabels(node, editState.changes, editState.outputMap)) {
|
||||||
|
editState.changed = true;
|
||||||
|
}
|
||||||
|
if (!node._def.defaults || !node._def.defaults.hasOwnProperty("icon")) {
|
||||||
|
var icon = $("#red-ui-editor-node-icon").val()||""
|
||||||
|
if (!this.isDefaultIcon) {
|
||||||
|
if (icon !== node.icon) {
|
||||||
|
editState.changes.icon = node.icon;
|
||||||
|
node.icon = icon;
|
||||||
|
editState.changed = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (icon !== "" && icon !== this.defaultIcon) {
|
||||||
|
editState.changes.icon = node.icon;
|
||||||
|
node.icon = icon;
|
||||||
|
editState.changed = true;
|
||||||
|
} else {
|
||||||
|
var iconPath = RED.utils.getDefaultNodeIcon(node._def, node);
|
||||||
|
var currentDefaultIcon = iconPath.module+"/"+iconPath.file;
|
||||||
|
if (this.defaultIcon !== currentDefaultIcon) {
|
||||||
|
editState.changes.icon = node.icon;
|
||||||
|
node.icon = currentDefaultIcon;
|
||||||
|
editState.changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (node.type === "subflow") {
|
||||||
|
var newCategory = $("#subflow-appearance-input-category").val().trim();
|
||||||
|
if (newCategory === "_custom_") {
|
||||||
|
newCategory = $("#subflow-appearance-input-custom-category").val().trim();
|
||||||
|
if (newCategory === "") {
|
||||||
|
newCategory = node.category;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newCategory === 'subflows') {
|
||||||
|
newCategory = '';
|
||||||
|
}
|
||||||
|
if (newCategory != node.category) {
|
||||||
|
editState.changes['category'] = node.category;
|
||||||
|
node.category = newCategory;
|
||||||
|
editState.changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldColor = node.color;
|
||||||
|
var newColor = $("#red-ui-editor-node-color").val();
|
||||||
|
if (oldColor !== newColor) {
|
||||||
|
editState.changes.color = node.color;
|
||||||
|
node.color = newColor;
|
||||||
|
editState.changed = true;
|
||||||
|
RED.utils.clearNodeColorCache();
|
||||||
|
if (node.type === "subflow") {
|
||||||
|
var nodeDefinition = RED.nodes.getType(
|
||||||
|
"subflow:" + node.id
|
||||||
|
);
|
||||||
|
nodeDefinition["color"] = newColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
if (!$("#node-input-show-label").prop('checked')) {
|
||||||
|
// Not checked - hide label
|
||||||
|
if (!/^link (in|out)$/.test(node.type)) {
|
||||||
|
// Not a link node - default state is true
|
||||||
|
if (node.l !== false) {
|
||||||
|
editState.changes.l = node.l
|
||||||
|
editState.changed = true;
|
||||||
|
}
|
||||||
|
node.l = false;
|
||||||
|
} else {
|
||||||
|
// A link node - default state is false
|
||||||
|
if (node.hasOwnProperty('l') && node.l) {
|
||||||
|
editState.changes.l = node.l
|
||||||
|
editState.changed = true;
|
||||||
|
}
|
||||||
|
delete node.l;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Checked - show label
|
||||||
|
if (!/^link (in|out)$/.test(node.type)) {
|
||||||
|
// Not a link node - default state is true
|
||||||
|
if (node.hasOwnProperty('l') && !node.l) {
|
||||||
|
editState.changes.l = node.l
|
||||||
|
editState.changed = true;
|
||||||
|
}
|
||||||
|
delete node.l;
|
||||||
|
} else {
|
||||||
|
if (!node.l) {
|
||||||
|
editState.changes.l = node.l
|
||||||
|
editState.changed = true;
|
||||||
|
}
|
||||||
|
node.l = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function buildAppearanceForm(container,node) {
|
||||||
|
var dialogForm = $('<form class="dialog-form form-horizontal" autocomplete="off"></form>').appendTo(container);
|
||||||
|
|
||||||
|
var i,row;
|
||||||
|
|
||||||
|
if (node.type === "subflow") {
|
||||||
|
var categoryRow = $("<div/>", {
|
||||||
|
class: "form-row"
|
||||||
|
}).appendTo(dialogForm);
|
||||||
|
$("<label/>", {
|
||||||
|
for: "subflow-appearance-input-category",
|
||||||
|
"data-i18n": "editor:subflow.category"
|
||||||
|
}).appendTo(categoryRow);
|
||||||
|
var categorySelector = $("<select/>", {
|
||||||
|
id: "subflow-appearance-input-category"
|
||||||
|
}).css({
|
||||||
|
width: "250px"
|
||||||
|
}).appendTo(categoryRow);
|
||||||
|
$("<input/>", {
|
||||||
|
type: "text",
|
||||||
|
id: "subflow-appearance-input-custom-category"
|
||||||
|
}).css({
|
||||||
|
display: "none",
|
||||||
|
"margin-left": "10px",
|
||||||
|
width: "calc(100% - 250px)"
|
||||||
|
}).appendTo(categoryRow);
|
||||||
|
|
||||||
|
var categories = RED.palette.getCategories();
|
||||||
|
categories.sort(function(A,B) {
|
||||||
|
return A.label.localeCompare(B.label);
|
||||||
|
})
|
||||||
|
categories.forEach(function(cat) {
|
||||||
|
categorySelector.append($("<option/>").val(cat.id).text(cat.label));
|
||||||
|
})
|
||||||
|
categorySelector.append($("<option/>").attr('disabled',true).text("---"));
|
||||||
|
categorySelector.append($("<option/>").val("_custom_").text(RED._("palette.addCategory")));
|
||||||
|
|
||||||
|
$("#subflow-appearance-input-category").on("change", function() {
|
||||||
|
var val = $(this).val();
|
||||||
|
if (val === "_custom_") {
|
||||||
|
$("#subflow-appearance-input-category").width(120);
|
||||||
|
$("#subflow-appearance-input-custom-category").show();
|
||||||
|
} else {
|
||||||
|
$("#subflow-appearance-input-category").width(250);
|
||||||
|
$("#subflow-appearance-input-custom-category").hide();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$("#subflow-appearance-input-category").val(node.category||"subflows");
|
||||||
|
var userCount = 0;
|
||||||
|
var subflowType = "subflow:"+node.id;
|
||||||
|
|
||||||
|
// RED.nodes.eachNode(function(n) {
|
||||||
|
// if (n.type === subflowType) {
|
||||||
|
// userCount++;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
$("#red-ui-editor-subflow-user-count")
|
||||||
|
.text(RED._("subflow.subflowInstances", {count:node.instances.length})).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('<div class="form-row">'+
|
||||||
|
'<label for="node-input-show-label-btn" data-i18n="editor.label"></label>'+
|
||||||
|
'<span style="margin-right: 2px;"/>'+
|
||||||
|
'<input type="checkbox" id="node-input-show-label"/>'+
|
||||||
|
'</div>').appendTo(dialogForm);
|
||||||
|
|
||||||
|
$("#node-input-show-label").toggleButton({
|
||||||
|
enabledLabel: RED._("editor.show"),
|
||||||
|
disabledLabel: RED._("editor.hide")
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!node.hasOwnProperty("l")) {
|
||||||
|
// Show label if type not link
|
||||||
|
node.l = !/^link (in|out)$/.test(node._def.type);
|
||||||
|
}
|
||||||
|
$("#node-input-show-label").prop("checked",node.l).trigger("change");
|
||||||
|
|
||||||
|
if (node.type === "subflow") {
|
||||||
|
// subflow template can select its color
|
||||||
|
var color = node.color ? node.color : "#DDAA99";
|
||||||
|
var colorRow = $("<div/>", {
|
||||||
|
class: "form-row"
|
||||||
|
}).appendTo(dialogForm);
|
||||||
|
$("<label/>").text(RED._("editor.color")).appendTo(colorRow);
|
||||||
|
|
||||||
|
var recommendedColors = [
|
||||||
|
"#DDAA99",
|
||||||
|
"#3FADB5", "#87A980", "#A6BBCF",
|
||||||
|
"#AAAA66", "#C0C0C0", "#C0DEED",
|
||||||
|
"#C7E9C0", "#D7D7A0", "#D8BFD8",
|
||||||
|
"#DAC4B4", "#DEB887", "#DEBD5C",
|
||||||
|
"#E2D96E", "#E6E0F8", "#E7E7AE",
|
||||||
|
"#E9967A", "#F3B567", "#FDD0A2",
|
||||||
|
"#FDF0C2", "#FFAAAA", "#FFCC66",
|
||||||
|
"#FFF0F0", "#FFFFFF"
|
||||||
|
]
|
||||||
|
|
||||||
|
RED.editor.colorPicker.create({
|
||||||
|
id: "red-ui-editor-node-color",
|
||||||
|
value: color,
|
||||||
|
palette: recommendedColors,
|
||||||
|
sortPalette: function (a, b) {return a.l - b.l;}
|
||||||
|
}).appendTo(colorRow);
|
||||||
|
|
||||||
|
$("#red-ui-editor-node-color").on('change', function(ev) {
|
||||||
|
// Horribly out of scope...
|
||||||
|
var colour = $(this).val();
|
||||||
|
nodeDiv.css('backgroundColor',colour);
|
||||||
|
var borderColor = RED.utils.getDarkerColor(colour);
|
||||||
|
if (borderColor !== colour) {
|
||||||
|
nodeDiv.css('border-color',borderColor)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// If a node has icon property in defaults, the icon of the node cannot be modified. (e.g, ui_button node in dashboard)
|
||||||
|
if ((!node._def.defaults || !node._def.defaults.hasOwnProperty("icon"))) {
|
||||||
|
var iconRow = $('<div class="form-row"></div>').appendTo(dialogForm);
|
||||||
|
$('<label data-i18n="editor.settingIcon">').appendTo(iconRow);
|
||||||
|
|
||||||
|
var iconButton = $('<button type="button" class="red-ui-button red-ui-editor-node-appearance-button">').appendTo(iconRow);
|
||||||
|
$('<i class="fa fa-caret-down"></i>').appendTo(iconButton);
|
||||||
|
var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).appendTo(iconButton);
|
||||||
|
var colour = RED.utils.getNodeColor(node.type, node._def);
|
||||||
|
var icon_url = RED.utils.getNodeIcon(node._def,node);
|
||||||
|
nodeDiv.css('backgroundColor',colour);
|
||||||
|
var borderColor = RED.utils.getDarkerColor(colour);
|
||||||
|
if (borderColor !== colour) {
|
||||||
|
nodeDiv.css('border-color',borderColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
|
||||||
|
RED.utils.createIconElement(icon_url, iconContainer, true);
|
||||||
|
|
||||||
|
iconButton.on("click", function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var iconPath;
|
||||||
|
var icon = $("#red-ui-editor-node-icon").val()||"";
|
||||||
|
if (icon) {
|
||||||
|
iconPath = RED.utils.separateIconPath(icon);
|
||||||
|
} else {
|
||||||
|
iconPath = RED.utils.getDefaultNodeIcon(node._def, node);
|
||||||
|
}
|
||||||
|
var backgroundColor = RED.utils.getNodeColor(node.type, node._def);
|
||||||
|
if (node.type === "subflow") {
|
||||||
|
backgroundColor = $("#red-ui-editor-node-color").val();
|
||||||
|
}
|
||||||
|
RED.editor.iconPicker.show(iconButton,backgroundColor,iconPath,false,function(newIcon) {
|
||||||
|
$("#red-ui-editor-node-icon").val(newIcon||"");
|
||||||
|
var icon_url = RED.utils.getNodeIcon(node._def,{type:node.type,icon:newIcon});
|
||||||
|
RED.utils.createIconElement(icon_url, iconContainer, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
RED.popover.tooltip(iconButton, function() {
|
||||||
|
return $("#red-ui-editor-node-icon").val() || RED._("editor.default");
|
||||||
|
})
|
||||||
|
$('<input type="hidden" id="red-ui-editor-node-icon">').val(node.icon).appendTo(iconRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$('<div class="form-row"><span data-i18n="editor.portLabels"></span></div>').appendTo(dialogForm);
|
||||||
|
|
||||||
|
var inputCount = node.inputs || node._def.inputs || 0;
|
||||||
|
var outputCount = node.outputs || node._def.outputs || 0;
|
||||||
|
if (node.type === 'subflow') {
|
||||||
|
inputCount = node.in.length;
|
||||||
|
outputCount = node.out.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
var inputLabels = node.inputLabels || [];
|
||||||
|
var outputLabels = node.outputLabels || [];
|
||||||
|
|
||||||
|
var inputPlaceholder = node._def.inputLabels?RED._("editor.defaultLabel"):RED._("editor.noDefaultLabel");
|
||||||
|
var outputPlaceholder = node._def.outputLabels?RED._("editor.defaultLabel"):RED._("editor.noDefaultLabel");
|
||||||
|
|
||||||
|
$('<div class="form-row"><span style="margin-left: 50px;" data-i18n="editor.labelInputs"></span><div id="red-ui-editor-node-label-form-inputs"></div></div>').appendTo(dialogForm);
|
||||||
|
var inputsDiv = $("#red-ui-editor-node-label-form-inputs");
|
||||||
|
if (inputCount > 0) {
|
||||||
|
for (i=0;i<inputCount;i++) {
|
||||||
|
buildLabelRow("input",i,inputLabels[i],inputPlaceholder).appendTo(inputsDiv);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buildLabelRow().appendTo(inputsDiv);
|
||||||
|
}
|
||||||
|
$('<div class="form-row"><span style="margin-left: 50px;" data-i18n="editor.labelOutputs"></span><div id="red-ui-editor-node-label-form-outputs"></div></div>').appendTo(dialogForm);
|
||||||
|
var outputsDiv = $("#red-ui-editor-node-label-form-outputs");
|
||||||
|
if (outputCount > 0) {
|
||||||
|
for (i=0;i<outputCount;i++) {
|
||||||
|
buildLabelRow("output",i,outputLabels[i],outputPlaceholder).appendTo(outputsDiv);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buildLabelRow().appendTo(outputsDiv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshLabelForm(container,node) {
|
||||||
|
|
||||||
|
var inputPlaceholder = node._def.inputLabels?RED._("editor.defaultLabel"):RED._("editor.noDefaultLabel");
|
||||||
|
var outputPlaceholder = node._def.outputLabels?RED._("editor.defaultLabel"):RED._("editor.noDefaultLabel");
|
||||||
|
|
||||||
|
var inputsDiv = $("#red-ui-editor-node-label-form-inputs");
|
||||||
|
var outputsDiv = $("#red-ui-editor-node-label-form-outputs");
|
||||||
|
|
||||||
|
var inputCount;
|
||||||
|
var formInputs = $("#node-input-inputs").val();
|
||||||
|
if (formInputs === undefined) {
|
||||||
|
if (node.type === 'subflow') {
|
||||||
|
inputCount = node.in.length;
|
||||||
|
} else {
|
||||||
|
inputCount = node.inputs || node._def.inputs || 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
inputCount = Math.min(1,Math.max(0,parseInt(formInputs)));
|
||||||
|
if (isNaN(inputCount)) {
|
||||||
|
inputCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var children = inputsDiv.children();
|
||||||
|
var childCount = children.length;
|
||||||
|
if (childCount === 1 && $(children[0]).hasClass('red-ui-editor-node-label-form-none')) {
|
||||||
|
childCount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childCount < inputCount) {
|
||||||
|
if (childCount === 0) {
|
||||||
|
// remove the 'none' placeholder
|
||||||
|
$(children[0]).remove();
|
||||||
|
}
|
||||||
|
for (i = childCount;i<inputCount;i++) {
|
||||||
|
buildLabelRow("input",i,"",inputPlaceholder).appendTo(inputsDiv);
|
||||||
|
}
|
||||||
|
} else if (childCount > inputCount) {
|
||||||
|
for (i=inputCount;i<childCount;i++) {
|
||||||
|
$(children[i]).remove();
|
||||||
|
}
|
||||||
|
if (inputCount === 0) {
|
||||||
|
buildLabelRow().appendTo(inputsDiv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var outputCount;
|
||||||
|
var i;
|
||||||
|
var formOutputs = $("#node-input-outputs").val();
|
||||||
|
|
||||||
|
if (formOutputs === undefined) {
|
||||||
|
if (node.type === 'subflow') {
|
||||||
|
outputCount = node.out.length;
|
||||||
|
} else {
|
||||||
|
inputCount = node.outputs || node._def.outputs || 0;
|
||||||
|
}
|
||||||
|
} else if (isNaN(formOutputs)) {
|
||||||
|
var outputMap = JSON.parse(formOutputs);
|
||||||
|
var keys = Object.keys(outputMap);
|
||||||
|
children = outputsDiv.children();
|
||||||
|
childCount = children.length;
|
||||||
|
if (childCount === 1 && $(children[0]).hasClass('red-ui-editor-node-label-form-none')) {
|
||||||
|
childCount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputCount = 0;
|
||||||
|
var rows = [];
|
||||||
|
keys.forEach(function(p) {
|
||||||
|
var row = $("#red-ui-editor-node-label-form-output-"+p).parent();
|
||||||
|
if (row.length === 0 && outputMap[p] !== -1) {
|
||||||
|
if (childCount === 0) {
|
||||||
|
$(children[0]).remove();
|
||||||
|
childCount = -1;
|
||||||
|
}
|
||||||
|
row = buildLabelRow("output",p,"",outputPlaceholder);
|
||||||
|
} else {
|
||||||
|
row.detach();
|
||||||
|
}
|
||||||
|
if (outputMap[p] !== -1) {
|
||||||
|
outputCount++;
|
||||||
|
rows.push({i:parseInt(outputMap[p]),r:row});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
rows.sort(function(A,B) {
|
||||||
|
return A.i-B.i;
|
||||||
|
})
|
||||||
|
rows.forEach(function(r,i) {
|
||||||
|
r.r.find("label").text((i+1)+".");
|
||||||
|
r.r.appendTo(outputsDiv);
|
||||||
|
})
|
||||||
|
if (rows.length === 0) {
|
||||||
|
buildLabelRow("output",i,"").appendTo(outputsDiv);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
outputCount = Math.max(0,parseInt(formOutputs));
|
||||||
|
}
|
||||||
|
children = outputsDiv.children();
|
||||||
|
childCount = children.length;
|
||||||
|
if (childCount === 1 && $(children[0]).hasClass('red-ui-editor-node-label-form-none')) {
|
||||||
|
childCount--;
|
||||||
|
}
|
||||||
|
if (childCount < outputCount) {
|
||||||
|
if (childCount === 0) {
|
||||||
|
// remove the 'none' placeholder
|
||||||
|
$(children[0]).remove();
|
||||||
|
}
|
||||||
|
for (i = childCount;i<outputCount;i++) {
|
||||||
|
buildLabelRow("output",i,"").appendTo(outputsDiv);
|
||||||
|
}
|
||||||
|
} else if (childCount > outputCount) {
|
||||||
|
for (i=outputCount;i<childCount;i++) {
|
||||||
|
$(children[i]).remove();
|
||||||
|
}
|
||||||
|
if (outputCount === 0) {
|
||||||
|
buildLabelRow().appendTo(outputsDiv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildLabelRow(type, index, value, placeHolder) {
|
||||||
|
var result = $('<div>',{class:"red-ui-editor-node-label-form-row"});
|
||||||
|
if (type === undefined) {
|
||||||
|
$('<span>').text(RED._("editor.noDefaultLabel")).appendTo(result);
|
||||||
|
result.addClass("red-ui-editor-node-label-form-none");
|
||||||
|
} else {
|
||||||
|
result.addClass("");
|
||||||
|
var id = "red-ui-editor-node-label-form-"+type+"-"+index;
|
||||||
|
$('<label>',{for:id}).text((index+1)+".").appendTo(result);
|
||||||
|
var input = $('<input>',{type:"text",id:id, placeholder: placeHolder}).val(value).appendTo(result);
|
||||||
|
var clear = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-times"></i></button>').appendTo(result);
|
||||||
|
clear.on("click", function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
input.val("");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLabels(node, changes, outputMap) {
|
||||||
|
var inputLabels = $("#red-ui-editor-node-label-form-inputs").children().find("input");
|
||||||
|
var outputLabels = $("#red-ui-editor-node-label-form-outputs").children().find("input");
|
||||||
|
|
||||||
|
var hasNonBlankLabel = false;
|
||||||
|
var changed = false;
|
||||||
|
var newValue = inputLabels.map(function() {
|
||||||
|
var v = $(this).val();
|
||||||
|
hasNonBlankLabel = hasNonBlankLabel || v!== "";
|
||||||
|
return v;
|
||||||
|
}).toArray().slice(0,node.inputs);
|
||||||
|
if ((node.inputLabels === undefined && hasNonBlankLabel) ||
|
||||||
|
(node.inputLabels !== undefined && JSON.stringify(newValue) !== JSON.stringify(node.inputLabels))) {
|
||||||
|
changes.inputLabels = node.inputLabels;
|
||||||
|
node.inputLabels = newValue;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
hasNonBlankLabel = false;
|
||||||
|
newValue = new Array(node.outputs);
|
||||||
|
outputLabels.each(function() {
|
||||||
|
var index = $(this).attr('id').substring("red-ui-editor-node-label-form-output-".length);
|
||||||
|
if (outputMap && outputMap.hasOwnProperty(index)) {
|
||||||
|
index = parseInt(outputMap[index]);
|
||||||
|
if (index === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var v = $(this).val();
|
||||||
|
hasNonBlankLabel = hasNonBlankLabel || v!== "";
|
||||||
|
newValue[index] = v;
|
||||||
|
});
|
||||||
|
|
||||||
|
if ((node.outputLabels === undefined && hasNonBlankLabel) ||
|
||||||
|
(node.outputLabels !== undefined && JSON.stringify(newValue) !== JSON.stringify(node.outputLabels))) {
|
||||||
|
changes.outputLabels = node.outputLabels;
|
||||||
|
node.outputLabels = newValue;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
})();
|
70
packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/description.js
vendored
Normal file
70
packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/description.js
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
;(function() {
|
||||||
|
|
||||||
|
RED.editor.registerEditPane("editor-tab-description", function(node) {
|
||||||
|
return {
|
||||||
|
label: RED._("editor-tab.description"),
|
||||||
|
name: RED._("editor-tab.description"),
|
||||||
|
iconClass: "fa fa-file-text-o",
|
||||||
|
|
||||||
|
create: function(container) {
|
||||||
|
this.editor = buildDescriptionForm(container,node);
|
||||||
|
RED.e = this.editor;
|
||||||
|
},
|
||||||
|
resize: function(size) {
|
||||||
|
this.editor.resize();
|
||||||
|
},
|
||||||
|
close: function() {
|
||||||
|
this.editor.destroy();
|
||||||
|
this.editor = null;
|
||||||
|
},
|
||||||
|
show: function() {
|
||||||
|
this.editor.focus();
|
||||||
|
},
|
||||||
|
apply: function(editState) {
|
||||||
|
var oldInfo = node.info;
|
||||||
|
var newInfo = this.editor.getValue();
|
||||||
|
if (!!oldInfo) {
|
||||||
|
// Has existing info property
|
||||||
|
if (newInfo.trim() === "") {
|
||||||
|
// New value is blank - remove the property
|
||||||
|
editState.changed = true;
|
||||||
|
editState.changes.info = oldInfo;
|
||||||
|
delete node.info;
|
||||||
|
} else if (newInfo !== oldInfo) {
|
||||||
|
// New value is different
|
||||||
|
editState.changed = true;
|
||||||
|
editState.changes.info = oldInfo;
|
||||||
|
node.info = newInfo;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No existing info
|
||||||
|
if (newInfo.trim() !== "") {
|
||||||
|
// New value is not blank
|
||||||
|
editState.changed = true;
|
||||||
|
editState.changes.info = undefined;
|
||||||
|
node.info = newInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function buildDescriptionForm(container,node) {
|
||||||
|
var dialogForm = $('<form class="dialog-form form-horizontal" autocomplete="off"></form>').appendTo(container);
|
||||||
|
var toolbarRow = $('<div></div>').appendTo(dialogForm);
|
||||||
|
var row = $('<div class="form-row node-text-editor-row" style="position:relative; padding-top: 4px; height: 100%"></div>').appendTo(dialogForm);
|
||||||
|
var editorId = "node-info-input-info-editor-"+Math.floor(1000*Math.random());
|
||||||
|
$('<div style="height: 100%" class="node-text-editor" id="'+editorId+'" ></div>').appendTo(row);
|
||||||
|
var nodeInfoEditor = RED.editor.createEditor({
|
||||||
|
id: editorId,
|
||||||
|
mode: 'ace/mode/markdown',
|
||||||
|
value: ""
|
||||||
|
});
|
||||||
|
if (node.info) {
|
||||||
|
nodeInfoEditor.getSession().setValue(node.info, -1);
|
||||||
|
}
|
||||||
|
node.infoEditor = nodeInfoEditor;
|
||||||
|
return nodeInfoEditor;
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
69
packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/envVarProperties.js
vendored
Normal file
69
packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/envVarProperties.js
vendored
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
;(function() {
|
||||||
|
|
||||||
|
RED.editor.registerEditPane("editor-tab-envProperties", function(node) {
|
||||||
|
return {
|
||||||
|
label: RED._("editor-tab.envProperties"),
|
||||||
|
name: RED._("editor-tab.envProperties"),
|
||||||
|
iconClass: "fa fa-list",
|
||||||
|
create: function(container) {
|
||||||
|
var form = $('<form class="dialog-form form-horizontal"></form>').appendTo(container);
|
||||||
|
var listContainer = $('<div class="form-row node-input-env-container-row"></div>').appendTo(form);
|
||||||
|
this.list = $('<ol></ol>').appendTo(listContainer);
|
||||||
|
RED.editor.envVarList.create(this.list, node);
|
||||||
|
},
|
||||||
|
resize: function(size) {
|
||||||
|
this.list.editableList('height',size.height);
|
||||||
|
},
|
||||||
|
close: function() {
|
||||||
|
|
||||||
|
},
|
||||||
|
apply: function(editState) {
|
||||||
|
var old_env = node.env;
|
||||||
|
var new_env = [];
|
||||||
|
if (/^subflow:/.test(node.type)) {
|
||||||
|
new_env = RED.subflow.exportSubflowInstanceEnv(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the values from the Properties table tab
|
||||||
|
var items = this.list.editableList('items');
|
||||||
|
items.each(function (i,el) {
|
||||||
|
var data = el.data('data');
|
||||||
|
var item;
|
||||||
|
if (data.nameField && data.valueField) {
|
||||||
|
item = {
|
||||||
|
name: data.nameField.val(),
|
||||||
|
value: data.valueField.typedInput("value"),
|
||||||
|
type: data.valueField.typedInput("type")
|
||||||
|
}
|
||||||
|
if (item.name.trim() !== "") {
|
||||||
|
new_env.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (new_env && new_env.length > 0) {
|
||||||
|
new_env.forEach(function(prop) {
|
||||||
|
if (prop.type === "cred") {
|
||||||
|
node.credentials = node.credentials || {_:{}};
|
||||||
|
node.credentials[prop.name] = prop.value;
|
||||||
|
node.credentials['has_'+prop.name] = (prop.value !== "");
|
||||||
|
if (prop.value !== '__PWRD__') {
|
||||||
|
editState.changed = true;
|
||||||
|
}
|
||||||
|
delete prop.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!isSameObj(old_env, new_env)) {
|
||||||
|
node.env = new_env;
|
||||||
|
editState.changes.env = node.env;
|
||||||
|
editState.changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
function isSameObj(env0, env1) {
|
||||||
|
return (JSON.stringify(env0) === JSON.stringify(env1));
|
||||||
|
}
|
||||||
|
})();
|
60
packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/flowProperties.js
vendored
Normal file
60
packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/flowProperties.js
vendored
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
;(function() {
|
||||||
|
|
||||||
|
RED.editor.registerEditPane("editor-tab-flow-properties", function(node) {
|
||||||
|
return {
|
||||||
|
label: RED._("editor-tab.properties"),
|
||||||
|
name: RED._("editor-tab.properties"),
|
||||||
|
iconClass: "fa fa-cog",
|
||||||
|
create: function(container) {
|
||||||
|
var dialogForm = $('<form id="dialog-form" class="form-horizontal"></form>').appendTo(container);
|
||||||
|
$('<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" data-i18n="[placeholder]common.label.name">'+
|
||||||
|
'</div>').appendTo(dialogForm);
|
||||||
|
|
||||||
|
var row = $('<div class="form-row node-text-editor-row">'+
|
||||||
|
'<label for="node-input-info" data-i18n="editor:workspace.info" style="width:300px;"></label>'+
|
||||||
|
'<div style="min-height:150px;" class="node-text-editor" id="node-input-info"></div>'+
|
||||||
|
'</div>').appendTo(dialogForm);
|
||||||
|
this.tabflowEditor = RED.editor.createEditor({
|
||||||
|
id: 'node-input-info',
|
||||||
|
mode: 'ace/mode/markdown',
|
||||||
|
value: ""
|
||||||
|
});
|
||||||
|
|
||||||
|
$('<input type="text" style="display: none;" />').prependTo(dialogForm);
|
||||||
|
dialogForm.on("submit", function(e) { e.preventDefault();});
|
||||||
|
|
||||||
|
$("#node-input-name").val(node.label);
|
||||||
|
RED.text.bidi.prepareInput($("#node-input-name"));
|
||||||
|
this.tabflowEditor.getSession().setValue(node.info || "", -1);
|
||||||
|
},
|
||||||
|
resize: function(size) {
|
||||||
|
$("#node-input-info").css("height", (size.height-70)+"px");
|
||||||
|
this.tabflowEditor.resize();
|
||||||
|
},
|
||||||
|
close: function() {
|
||||||
|
this.tabflowEditor.destroy();
|
||||||
|
},
|
||||||
|
apply: function(editState) {
|
||||||
|
var label = $( "#node-input-name" ).val();
|
||||||
|
|
||||||
|
if (node.label != label) {
|
||||||
|
editState.changes.label = node.label;
|
||||||
|
editState.changed = true;
|
||||||
|
node.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
var info = this.tabflowEditor.getValue();
|
||||||
|
if (node.info !== info) {
|
||||||
|
editState.changes.info = node.info;
|
||||||
|
editState.changed = true;
|
||||||
|
node.info = info;
|
||||||
|
}
|
||||||
|
$("#red-ui-tab-"+(node.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!node.disabled);
|
||||||
|
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!node.disabled);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
181
packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/properties.js
vendored
Normal file
181
packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/properties.js
vendored
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
;(function() {
|
||||||
|
|
||||||
|
RED.editor.registerEditPane("editor-tab-properties", function(node) {
|
||||||
|
return {
|
||||||
|
label: RED._("editor-tab.properties"),
|
||||||
|
name: RED._("editor-tab.properties"),
|
||||||
|
iconClass: "fa fa-cog",
|
||||||
|
create: function(container) {
|
||||||
|
|
||||||
|
var nodeType = node.type;
|
||||||
|
if (node.type === "subflow") {
|
||||||
|
nodeType = "subflow-template";
|
||||||
|
} else if (node.type.substring(0,8) == "subflow:") {
|
||||||
|
nodeType = "subflow";
|
||||||
|
}
|
||||||
|
|
||||||
|
var i18nNamespace;
|
||||||
|
if (node._def.set.module === "node-red") {
|
||||||
|
i18nNamespace = "node-red";
|
||||||
|
} else {
|
||||||
|
i18nNamespace = node._def.set.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
var formStyle = "dialog-form";
|
||||||
|
this.inputClass = "node-input";
|
||||||
|
if (node._def.category === "config" && nodeType !== "group") {
|
||||||
|
this.inputClass = "node-config-input";
|
||||||
|
formStyle = "node-config-dialog-edit-form";
|
||||||
|
}
|
||||||
|
RED.editor.buildEditForm(container,formStyle,nodeType,i18nNamespace,node);
|
||||||
|
},
|
||||||
|
resize: function(size) {
|
||||||
|
if (node && node._def.oneditresize) {
|
||||||
|
try {
|
||||||
|
node._def.oneditresize.call(node,size);
|
||||||
|
} catch(err) {
|
||||||
|
console.log("oneditresize",node.id,node.type,err.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
close: function() {
|
||||||
|
|
||||||
|
},
|
||||||
|
apply: function(editState) {
|
||||||
|
var newValue;
|
||||||
|
var d;
|
||||||
|
if (node._def.defaults) {
|
||||||
|
for (d in node._def.defaults) {
|
||||||
|
if (node._def.defaults.hasOwnProperty(d)) {
|
||||||
|
var input = $("#"+this.inputClass+"-"+d);
|
||||||
|
if (input.attr('type') === "checkbox") {
|
||||||
|
newValue = input.prop('checked');
|
||||||
|
} else if (input.prop("nodeName") === "select" && input.attr("multiple") === "multiple") {
|
||||||
|
// An empty select-multiple box returns null.
|
||||||
|
// Need to treat that as an empty array.
|
||||||
|
newValue = input.val();
|
||||||
|
if (newValue == null) {
|
||||||
|
newValue = [];
|
||||||
|
}
|
||||||
|
} else if ("format" in node._def.defaults[d] && node._def.defaults[d].format !== "" && input[0].nodeName === "DIV") {
|
||||||
|
newValue = input.text();
|
||||||
|
} else {
|
||||||
|
newValue = input.val();
|
||||||
|
}
|
||||||
|
if (newValue != null) {
|
||||||
|
if (d === "outputs") {
|
||||||
|
if (newValue.trim() === "") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (isNaN(newValue)) {
|
||||||
|
editState.outputMap = JSON.parse(newValue);
|
||||||
|
var outputCount = 0;
|
||||||
|
var outputsChanged = false;
|
||||||
|
var keys = Object.keys(editState.outputMap);
|
||||||
|
keys.forEach(function(p) {
|
||||||
|
if (isNaN(p)) {
|
||||||
|
// New output;
|
||||||
|
outputCount ++;
|
||||||
|
delete editState.outputMap[p];
|
||||||
|
} else {
|
||||||
|
editState.outputMap[p] = editState.outputMap[p]+"";
|
||||||
|
if (editState.outputMap[p] !== "-1") {
|
||||||
|
outputCount++;
|
||||||
|
if (editState.outputMap[p] !== p) {
|
||||||
|
// Output moved
|
||||||
|
outputsChanged = true;
|
||||||
|
} else {
|
||||||
|
delete editState.outputMap[p];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Output removed
|
||||||
|
outputsChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
newValue = outputCount;
|
||||||
|
if (outputsChanged) {
|
||||||
|
editState.changed = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newValue = parseInt(newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (node._def.defaults[d].type) {
|
||||||
|
if (newValue == "_ADD_") {
|
||||||
|
newValue = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (node[d] != newValue) {
|
||||||
|
if (node._def.defaults[d].type) {
|
||||||
|
// Change to a related config node
|
||||||
|
var configNode = RED.nodes.node(node[d]);
|
||||||
|
if (configNode) {
|
||||||
|
var users = configNode.users;
|
||||||
|
users.splice(users.indexOf(node),1);
|
||||||
|
RED.events.emit("nodes:change",configNode);
|
||||||
|
}
|
||||||
|
configNode = RED.nodes.node(newValue);
|
||||||
|
if (configNode) {
|
||||||
|
configNode.users.push(node);
|
||||||
|
RED.events.emit("nodes:change",configNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
editState.changes[d] = node[d];
|
||||||
|
node[d] = newValue;
|
||||||
|
editState.changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (node._def.credentials) {
|
||||||
|
var credDefinition = node._def.credentials;
|
||||||
|
var credsChanged = updateNodeCredentials(node,credDefinition,this.inputClass);
|
||||||
|
editState.changed = editState.changed || credsChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the node credentials from the edit form
|
||||||
|
* @param node - the node containing the credentials
|
||||||
|
* @param credDefinition - definition of the credentials
|
||||||
|
* @param prefix - prefix of the input fields
|
||||||
|
* @return {boolean} whether anything has changed
|
||||||
|
*/
|
||||||
|
function updateNodeCredentials(node, credDefinition, prefix) {
|
||||||
|
var changed = false;
|
||||||
|
if (!node.credentials) {
|
||||||
|
node.credentials = {_:{}};
|
||||||
|
} else if (!node.credentials._) {
|
||||||
|
node.credentials._ = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var cred in credDefinition) {
|
||||||
|
if (credDefinition.hasOwnProperty(cred)) {
|
||||||
|
var input = $("#" + prefix + '-' + cred);
|
||||||
|
if (input.length > 0) {
|
||||||
|
var value = input.val();
|
||||||
|
if (credDefinition[cred].type == 'password') {
|
||||||
|
node.credentials['has_' + cred] = (value !== "");
|
||||||
|
if (value == '__PWRD__') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
changed = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
node.credentials[cred] = value;
|
||||||
|
if (value != node.credentials._[cred]) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
})();
|
179
packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/subflowModule.js
vendored
Normal file
179
packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/subflowModule.js
vendored
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
(function() {
|
||||||
|
var _subflowModulePaneTemplate = '<form class="dialog-form form-horizontal" autocomplete="off">'+
|
||||||
|
'<div class="form-row">'+
|
||||||
|
'<label for="subflow-input-module-module" data-i18n="[append]editor:subflow.module"><i class="fa fa-cube"></i> </label>'+
|
||||||
|
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-module" data-i18n="[placeholder]common.label.name">'+
|
||||||
|
'</div>'+
|
||||||
|
'<div class="form-row">'+
|
||||||
|
'<label for="subflow-input-module-type" data-i18n="[append]editor:subflow.type"> </label>'+
|
||||||
|
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-type">'+
|
||||||
|
'</div>'+
|
||||||
|
'<div class="form-row">'+
|
||||||
|
'<label for="subflow-input-module-version" data-i18n="[append]editor:subflow.version"></label>'+
|
||||||
|
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-version" data-i18n="[placeholder]editor:subflow.versionPlaceholder">'+
|
||||||
|
'</div>'+
|
||||||
|
'<div class="form-row">'+
|
||||||
|
'<label for="subflow-input-module-desc" data-i18n="[append]editor:subflow.desc"></label>'+
|
||||||
|
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-desc">'+
|
||||||
|
'</div>'+
|
||||||
|
'<div class="form-row">'+
|
||||||
|
'<label for="subflow-input-module-license" data-i18n="[append]editor:subflow.license"></label>'+
|
||||||
|
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-license">'+
|
||||||
|
'</div>'+
|
||||||
|
'<div class="form-row">'+
|
||||||
|
'<label for="subflow-input-module-author" data-i18n="[append]editor:subflow.author"></label>'+
|
||||||
|
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-author" data-i18n="[placeholder]editor:subflow.authorPlaceholder">'+
|
||||||
|
'</div>'+
|
||||||
|
'<div class="form-row">'+
|
||||||
|
'<label for="subflow-input-module-keywords" data-i18n="[append]editor:subflow.keys"></label>'+
|
||||||
|
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-keywords" data-i18n="[placeholder]editor:subflow.keysPlaceholder">'+
|
||||||
|
'</div>'+
|
||||||
|
'</form>';
|
||||||
|
|
||||||
|
RED.editor.registerEditPane("editor-tab-subflow-module", function(node) {
|
||||||
|
return {
|
||||||
|
label: RED._("editor-tab.module"),
|
||||||
|
name: RED._("editor-tab.module"),
|
||||||
|
iconClass: "fa fa-cube",
|
||||||
|
create: function(container) {
|
||||||
|
buildModuleForm(container, node);
|
||||||
|
},
|
||||||
|
resize: function(size) {
|
||||||
|
},
|
||||||
|
close: function() {
|
||||||
|
|
||||||
|
},
|
||||||
|
apply: function(editState) {
|
||||||
|
var newMeta = exportSubflowModuleProperties(node);
|
||||||
|
if (!isSameObj(node.meta,newMeta)) {
|
||||||
|
editState.changes.meta = node.meta;
|
||||||
|
node.meta = newMeta;
|
||||||
|
editState.changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function isSameObj(env0, env1) {
|
||||||
|
return (JSON.stringify(env0) === JSON.stringify(env1));
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupInputValidation(input,validator) {
|
||||||
|
var errorTip;
|
||||||
|
var validateTimeout;
|
||||||
|
|
||||||
|
var validateFunction = function() {
|
||||||
|
if (validateTimeout) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
validateTimeout = setTimeout(function() {
|
||||||
|
var error = validator(input.val());
|
||||||
|
// if (!error && errorTip) {
|
||||||
|
// errorTip.close();
|
||||||
|
// errorTip = null;
|
||||||
|
// } else if (error && !errorTip) {
|
||||||
|
// errorTip = RED.popover.create({
|
||||||
|
// tooltip: true,
|
||||||
|
// target:input,
|
||||||
|
// size: "small",
|
||||||
|
// direction: "bottom",
|
||||||
|
// content: error,
|
||||||
|
// }).open();
|
||||||
|
// }
|
||||||
|
input.toggleClass("input-error",!!error);
|
||||||
|
validateTimeout = null;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
input.on("change keyup paste", validateFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildModuleForm(container, node) {
|
||||||
|
$(_subflowModulePaneTemplate).appendTo(container);
|
||||||
|
var moduleProps = node.meta || {};
|
||||||
|
[
|
||||||
|
'module',
|
||||||
|
'type',
|
||||||
|
'version',
|
||||||
|
'author',
|
||||||
|
'desc',
|
||||||
|
'keywords',
|
||||||
|
'license'
|
||||||
|
].forEach(function(property) {
|
||||||
|
$("#subflow-input-module-"+property).val(moduleProps[property]||"")
|
||||||
|
})
|
||||||
|
$("#subflow-input-module-type").attr("placeholder",node.id);
|
||||||
|
|
||||||
|
setupInputValidation($("#subflow-input-module-module"), function(newValue) {
|
||||||
|
newValue = newValue.trim();
|
||||||
|
var isValid = newValue.length < 215;
|
||||||
|
isValid = isValid && !/^[._]/.test(newValue);
|
||||||
|
isValid = isValid && !/[A-Z]/.test(newValue);
|
||||||
|
if (newValue !== encodeURIComponent(newValue)) {
|
||||||
|
var m = /^@([^\/]+)\/([^\/]+)$/.exec(newValue);
|
||||||
|
if (m) {
|
||||||
|
isValid = isValid && (m[1] === encodeURIComponent(m[1]) && m[2] === encodeURIComponent(m[2]))
|
||||||
|
} else {
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isValid?"":"Invalid module name"
|
||||||
|
})
|
||||||
|
setupInputValidation($("#subflow-input-module-version"), function(newValue) {
|
||||||
|
newValue = newValue.trim();
|
||||||
|
var isValid = newValue === "" ||
|
||||||
|
/^(\d|[1-9]\d*)\.(\d|[1-9]\d*)\.(\d|[1-9]\d*)(-(0|[1-9A-Za-z-][0-9A-Za-z-]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\.(0|[1-9A-Za-z-][0-9A-Za-z-]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$/.test(newValue);
|
||||||
|
return isValid?"":"Invalid version number"
|
||||||
|
})
|
||||||
|
|
||||||
|
var licenses = ["none", "Apache-2.0", "BSD-3-Clause", "BSD-2-Clause", "GPL-2.0", "GPL-3.0", "MIT", "MPL-2.0", "CDDL-1.0", "EPL-2.0"];
|
||||||
|
var typedLicenses = {
|
||||||
|
types: licenses.map(function(l) {
|
||||||
|
return {
|
||||||
|
value: l,
|
||||||
|
label: l === "none" ? RED._("editor:subflow.licenseNone") : l,
|
||||||
|
hasValue: false
|
||||||
|
};
|
||||||
|
})
|
||||||
|
}
|
||||||
|
typedLicenses.types.push({
|
||||||
|
value:"_custom_", label:RED._("editor:subflow.licenseOther"), icon:"red/images/typedInput/az.svg"
|
||||||
|
})
|
||||||
|
if (!moduleProps.license) {
|
||||||
|
typedLicenses.default = "none";
|
||||||
|
} else if (licenses.indexOf(moduleProps.license) > -1) {
|
||||||
|
typedLicenses.default = moduleProps.license;
|
||||||
|
} else {
|
||||||
|
typedLicenses.default = "_custom_";
|
||||||
|
}
|
||||||
|
$("#subflow-input-module-license").typedInput(typedLicenses)
|
||||||
|
}
|
||||||
|
function exportSubflowModuleProperties(node) {
|
||||||
|
var value;
|
||||||
|
var moduleProps = {};
|
||||||
|
[
|
||||||
|
'module',
|
||||||
|
'type',
|
||||||
|
'version',
|
||||||
|
'author',
|
||||||
|
'desc',
|
||||||
|
'keywords'
|
||||||
|
].forEach(function(property) {
|
||||||
|
value = $("#subflow-input-module-"+property).val().trim();
|
||||||
|
if (value) {
|
||||||
|
moduleProps[property] = value;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
var selectedLicenseType = $("#subflow-input-module-license").typedInput("type");
|
||||||
|
|
||||||
|
if (selectedLicenseType === '_custom_') {
|
||||||
|
value = $("#subflow-input-module-license").val();
|
||||||
|
if (value) {
|
||||||
|
moduleProps.license = value;
|
||||||
|
}
|
||||||
|
} else if (selectedLicenseType !== "none") {
|
||||||
|
moduleProps.license = selectedLicenseType;
|
||||||
|
}
|
||||||
|
return moduleProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
@ -87,16 +87,18 @@ RED.group = (function() {
|
|||||||
"label-position": "nw"
|
"label-position": "nw"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var groupDef = {
|
var groupDef = {
|
||||||
defaults:{
|
defaults:{
|
||||||
name:{value:""},
|
name:{value:""},
|
||||||
style:{value:{label:true}},
|
style:{value:{label:true}},
|
||||||
nodes:{value:[]}
|
nodes:{value:[]},
|
||||||
|
env: {value:[]},
|
||||||
},
|
},
|
||||||
category: "config",
|
category: "config",
|
||||||
oneditprepare: function() {
|
oneditprepare: function() {
|
||||||
var style = this.style || {};
|
var style = this.style || {};
|
||||||
RED.colorPicker.create({
|
RED.editor.colorPicker.create({
|
||||||
id:"node-input-style-stroke",
|
id:"node-input-style-stroke",
|
||||||
value: style.stroke || defaultGroupStyle.stroke || "#a4a4a4",
|
value: style.stroke || defaultGroupStyle.stroke || "#a4a4a4",
|
||||||
palette: colorPalette,
|
palette: colorPalette,
|
||||||
@ -107,7 +109,7 @@ RED.group = (function() {
|
|||||||
none: true,
|
none: true,
|
||||||
opacity: style.hasOwnProperty('stroke-opacity')?style['stroke-opacity']:(defaultGroupStyle.hasOwnProperty('stroke-opacity')?defaultGroupStyle['stroke-opacity']:1.0)
|
opacity: style.hasOwnProperty('stroke-opacity')?style['stroke-opacity']:(defaultGroupStyle.hasOwnProperty('stroke-opacity')?defaultGroupStyle['stroke-opacity']:1.0)
|
||||||
}).appendTo("#node-input-row-style-stroke");
|
}).appendTo("#node-input-row-style-stroke");
|
||||||
RED.colorPicker.create({
|
RED.editor.colorPicker.create({
|
||||||
id:"node-input-style-fill",
|
id:"node-input-style-fill",
|
||||||
value: style.fill || defaultGroupStyle.fill ||"none",
|
value: style.fill || defaultGroupStyle.fill ||"none",
|
||||||
palette: colorPalette,
|
palette: colorPalette,
|
||||||
@ -124,7 +126,7 @@ RED.group = (function() {
|
|||||||
value:style["label-position"] || "nw"
|
value:style["label-position"] || "nw"
|
||||||
}).appendTo("#node-input-row-style-label-position");
|
}).appendTo("#node-input-row-style-label-position");
|
||||||
|
|
||||||
RED.colorPicker.create({
|
RED.editor.colorPicker.create({
|
||||||
id:"node-input-style-color",
|
id:"node-input-style-color",
|
||||||
value: style.color || defaultGroupStyle.color ||"#a4a4a4",
|
value: style.color || defaultGroupStyle.color ||"#a4a4a4",
|
||||||
palette: colorPalette,
|
palette: colorPalette,
|
||||||
@ -144,7 +146,6 @@ RED.group = (function() {
|
|||||||
})
|
})
|
||||||
$("#node-input-style-label").prop("checked", this.style.label)
|
$("#node-input-style-label").prop("checked", this.style.label)
|
||||||
$("#node-input-style-label").trigger("change");
|
$("#node-input-style-label").trigger("change");
|
||||||
|
|
||||||
},
|
},
|
||||||
oneditresize: function(size) {
|
oneditresize: function(size) {
|
||||||
},
|
},
|
||||||
@ -183,7 +184,9 @@ RED.group = (function() {
|
|||||||
var activateUngroup = false;
|
var activateUngroup = false;
|
||||||
var activateMerge = false;
|
var activateMerge = false;
|
||||||
var activateRemove = false;
|
var activateRemove = false;
|
||||||
|
var singleGroupSelected = false;
|
||||||
if (activateGroup) {
|
if (activateGroup) {
|
||||||
|
singleGroupSelected = selection.nodes.length === 1 && selection.nodes[0].type === 'group';
|
||||||
selection.nodes.forEach(function (n) {
|
selection.nodes.forEach(function (n) {
|
||||||
if (n.type === "group") {
|
if (n.type === "group") {
|
||||||
activateUngroup = true;
|
activateUngroup = true;
|
||||||
@ -200,6 +203,8 @@ RED.group = (function() {
|
|||||||
RED.menu.setDisabled("menu-item-group-ungroup", !activateUngroup);
|
RED.menu.setDisabled("menu-item-group-ungroup", !activateUngroup);
|
||||||
RED.menu.setDisabled("menu-item-group-merge", !activateMerge);
|
RED.menu.setDisabled("menu-item-group-merge", !activateMerge);
|
||||||
RED.menu.setDisabled("menu-item-group-remove", !activateRemove);
|
RED.menu.setDisabled("menu-item-group-remove", !activateRemove);
|
||||||
|
RED.menu.setDisabled("menu-item-edit-copy-group-style", !singleGroupSelected);
|
||||||
|
RED.menu.setDisabled("menu-item-edit-paste-group-style", !activateUngroup);
|
||||||
});
|
});
|
||||||
|
|
||||||
RED.actions.add("core:group-selection", function() { groupSelection() })
|
RED.actions.add("core:group-selection", function() { groupSelection() })
|
||||||
@ -252,6 +257,7 @@ RED.group = (function() {
|
|||||||
if (selection.nodes && selection.nodes.length === 1 && selection.nodes[0].type === 'group') {
|
if (selection.nodes && selection.nodes.length === 1 && selection.nodes[0].type === 'group') {
|
||||||
groupStyleClipboard = JSON.parse(JSON.stringify(selection.nodes[0].style));
|
groupStyleClipboard = JSON.parse(JSON.stringify(selection.nodes[0].style));
|
||||||
RED.notify(RED._("clipboard.groupStyleCopied"),{id:"clipboard"})
|
RED.notify(RED._("clipboard.groupStyleCopied"),{id:"clipboard"})
|
||||||
|
RED.menu.setDisabled("menu-item-edit-paste-group-style", false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function pasteGroupStyle() {
|
function pasteGroupStyle() {
|
||||||
|
@ -131,7 +131,7 @@ RED.keyboard = (function() {
|
|||||||
return mergedKeymap;
|
return mergedKeymap;
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function init(done) {
|
||||||
// Migrate from pre-0.18
|
// Migrate from pre-0.18
|
||||||
migrateOldKeymap();
|
migrateOldKeymap();
|
||||||
|
|
||||||
@ -164,6 +164,7 @@ RED.keyboard = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
RED.userSettings.add({
|
RED.userSettings.add({
|
||||||
@ -174,6 +175,9 @@ RED.keyboard = (function() {
|
|||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
$("#red-ui-settings-tab-keyboard-filter").trigger("focus");
|
$("#red-ui-settings-tab-keyboard-filter").trigger("focus");
|
||||||
},200);
|
},200);
|
||||||
|
},
|
||||||
|
close: function() {
|
||||||
|
RED.menu.refreshShortcuts();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -536,7 +536,7 @@ RED.library = (function() {
|
|||||||
// evt.preventDefault();
|
// evt.preventDefault();
|
||||||
// var icon = libraryFields['icon'].input.val() || "";
|
// var icon = libraryFields['icon'].input.val() || "";
|
||||||
// var iconPath = (icon ? RED.utils.separateIconPath(icon) : {});
|
// var iconPath = (icon ? RED.utils.separateIconPath(icon) : {});
|
||||||
// RED.editor.showIconPicker(iconButton, null, iconPath, true, function (newIcon) {
|
// RED.editor.iconPicker.show(iconButton, null, iconPath, true, function (newIcon) {
|
||||||
// iconButton.empty();
|
// iconButton.empty();
|
||||||
// var path = newIcon || "";
|
// var path = newIcon || "";
|
||||||
// var newPath = RED.utils.separateIconPath(path);
|
// var newPath = RED.utils.separateIconPath(path);
|
||||||
|
@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
RED.subflow = (function() {
|
RED.subflow = (function() {
|
||||||
|
|
||||||
var currentLocale = "en-US";
|
|
||||||
|
|
||||||
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">'+
|
'<div class="form-row">'+
|
||||||
'<label for="node-input-name" data-i18n="[append]editor:common.label.name"><i class="fa fa-tag"></i> </label>'+
|
'<label for="node-input-name" data-i18n="[append]editor:common.label.name"><i class="fa fa-tag"></i> </label>'+
|
||||||
@ -37,7 +35,7 @@ RED.subflow = (function() {
|
|||||||
'<div id="subflow-env-tabs-content">'+
|
'<div id="subflow-env-tabs-content">'+
|
||||||
'<div id="subflow-env-tab-edit">'+
|
'<div id="subflow-env-tab-edit">'+
|
||||||
'<div class="form-row node-input-env-container-row" id="subflow-input-edit-ui">'+
|
'<div class="form-row node-input-env-container-row" id="subflow-input-edit-ui">'+
|
||||||
'<ol class="red-ui-editor-subflow-env-list" id="node-input-env-container"></ol>'+
|
'<ol id="node-input-env-container"></ol>'+
|
||||||
'<div class="node-input-env-locales-row"><i class="fa fa-language"></i> <select id="subflow-input-env-locale"></select></div>'+
|
'<div class="node-input-env-locales-row"><i class="fa fa-language"></i> <select id="subflow-input-env-locale"></select></div>'+
|
||||||
'</div>'+
|
'</div>'+
|
||||||
'</div>'+
|
'</div>'+
|
||||||
@ -47,37 +45,6 @@ RED.subflow = (function() {
|
|||||||
'</div>'+
|
'</div>'+
|
||||||
'</script>';
|
'</script>';
|
||||||
|
|
||||||
var _subflowModulePaneTemplate = '<form class="dialog-form form-horizontal" autocomplete="off">'+
|
|
||||||
'<div class="form-row">'+
|
|
||||||
'<label for="subflow-input-module-module" data-i18n="[append]editor:subflow.module"><i class="fa fa-cube"></i> </label>'+
|
|
||||||
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-module" data-i18n="[placeholder]common.label.name">'+
|
|
||||||
'</div>'+
|
|
||||||
'<div class="form-row">'+
|
|
||||||
'<label for="subflow-input-module-type" data-i18n="[append]editor:subflow.type"> </label>'+
|
|
||||||
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-type">'+
|
|
||||||
'</div>'+
|
|
||||||
'<div class="form-row">'+
|
|
||||||
'<label for="subflow-input-module-version" data-i18n="[append]editor:subflow.version"></label>'+
|
|
||||||
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-version" data-i18n="[placeholder]editor:subflow.versionPlaceholder">'+
|
|
||||||
'</div>'+
|
|
||||||
'<div class="form-row">'+
|
|
||||||
'<label for="subflow-input-module-desc" data-i18n="[append]editor:subflow.desc"></label>'+
|
|
||||||
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-desc">'+
|
|
||||||
'</div>'+
|
|
||||||
'<div class="form-row">'+
|
|
||||||
'<label for="subflow-input-module-license" data-i18n="[append]editor:subflow.license"></label>'+
|
|
||||||
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-license">'+
|
|
||||||
'</div>'+
|
|
||||||
'<div class="form-row">'+
|
|
||||||
'<label for="subflow-input-module-author" data-i18n="[append]editor:subflow.author"></label>'+
|
|
||||||
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-author" data-i18n="[placeholder]editor:subflow.authorPlaceholder">'+
|
|
||||||
'</div>'+
|
|
||||||
'<div class="form-row">'+
|
|
||||||
'<label for="subflow-input-module-keywords" data-i18n="[append]editor:subflow.keys"></label>'+
|
|
||||||
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-keywords" data-i18n="[placeholder]editor:subflow.keysPlaceholder">'+
|
|
||||||
'</div>'+
|
|
||||||
'</form>';
|
|
||||||
|
|
||||||
function findAvailableSubflowIOPosition(subflow,isInput) {
|
function findAvailableSubflowIOPosition(subflow,isInput) {
|
||||||
var pos = {x:50,y:30};
|
var pos = {x:50,y:30};
|
||||||
if (!isInput) {
|
if (!isInput) {
|
||||||
@ -909,7 +876,6 @@ RED.subflow = (function() {
|
|||||||
* Create interface for controlling env var UI definition
|
* Create interface for controlling env var UI definition
|
||||||
*/
|
*/
|
||||||
function buildEnvControl(envList,node) {
|
function buildEnvControl(envList,node) {
|
||||||
|
|
||||||
var tabs = RED.tabs.create({
|
var tabs = RED.tabs.create({
|
||||||
id: "subflow-env-tabs",
|
id: "subflow-env-tabs",
|
||||||
onchange: function(tab) {
|
onchange: function(tab) {
|
||||||
@ -950,588 +916,11 @@ RED.subflow = (function() {
|
|||||||
locales.val(locale);
|
locales.val(locale);
|
||||||
|
|
||||||
locales.on("change", function() {
|
locales.on("change", function() {
|
||||||
currentLocale = $(this).val();
|
RED.editor.envVarList.setLocale($(this).val(), $("#node-input-env-container"));
|
||||||
var items = $("#node-input-env-container").editableList("items");
|
|
||||||
items.each(function (i, item) {
|
|
||||||
var entry = $(this).data('data');
|
|
||||||
var labelField = entry.ui.labelField;
|
|
||||||
labelField.val(lookupLabel(entry.ui.label, "", currentLocale));
|
|
||||||
if (labelField.timeout) {
|
|
||||||
clearTimeout(labelField.timeout);
|
|
||||||
delete labelField.timeout;
|
|
||||||
}
|
|
||||||
labelField.addClass("input-updated");
|
|
||||||
labelField.timeout = setTimeout(function() {
|
|
||||||
delete labelField.timeout
|
|
||||||
labelField.removeClass("input-updated");
|
|
||||||
},3000);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
RED.editor.envVarList.setLocale(locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
var DEFAULT_ENV_TYPE_LIST = ['str','num','bool','json','bin','env'];
|
|
||||||
var DEFAULT_ENV_TYPE_LIST_INC_CRED = ['str','num','bool','json','bin','env','cred'];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create env var edit interface
|
|
||||||
* @param container - container
|
|
||||||
* @param node - subflow node
|
|
||||||
*/
|
|
||||||
function buildPropertiesList(envContainer, node) {
|
|
||||||
|
|
||||||
var isTemplateNode = (node.type === "subflow");
|
|
||||||
|
|
||||||
if (isTemplateNode) {
|
|
||||||
buildEnvControl(envContainer, node);
|
|
||||||
}
|
|
||||||
envContainer
|
|
||||||
.css({
|
|
||||||
'min-height':'150px',
|
|
||||||
'min-width':'450px'
|
|
||||||
})
|
|
||||||
.editableList({
|
|
||||||
header: isTemplateNode?$('<div><div><div></div><div data-i18n="common.label.name"></div><div data-i18n="editor-tab.defaultValue"></div><div></div></div></div>'):undefined,
|
|
||||||
addItem: function(container, i, opt) {
|
|
||||||
// If this is an instance node, these are properties unique to
|
|
||||||
// this instance - ie opt.parent will not be defined.
|
|
||||||
|
|
||||||
if (isTemplateNode) {
|
|
||||||
container.addClass("red-ui-editor-subflow-env-editable")
|
|
||||||
}
|
|
||||||
|
|
||||||
var envRow = $('<div/>').appendTo(container);
|
|
||||||
var nameField = null;
|
|
||||||
var valueField = null;
|
|
||||||
|
|
||||||
nameField = $('<input/>', {
|
|
||||||
class: "node-input-env-name",
|
|
||||||
type: "text",
|
|
||||||
placeholder: RED._("common.label.name")
|
|
||||||
}).attr("autocomplete","disable").appendTo(envRow).val(opt.name);
|
|
||||||
valueField = $('<input/>',{
|
|
||||||
style: "width:100%",
|
|
||||||
class: "node-input-env-value",
|
|
||||||
type: "text",
|
|
||||||
}).attr("autocomplete","disable").appendTo(envRow)
|
|
||||||
valueField.typedInput({default:'str',types:isTemplateNode?DEFAULT_ENV_TYPE_LIST:DEFAULT_ENV_TYPE_LIST_INC_CRED});
|
|
||||||
valueField.typedInput('type', opt.type);
|
|
||||||
if (opt.type === "cred") {
|
|
||||||
if (opt.value) {
|
|
||||||
valueField.typedInput('value', opt.value);
|
|
||||||
} else if (node.credentials && node.credentials[opt.name]) {
|
|
||||||
valueField.typedInput('value', node.credentials[opt.name]);
|
|
||||||
} else if (node.credentials && node.credentials['has_'+opt.name]) {
|
|
||||||
valueField.typedInput('value', "__PWRD__");
|
|
||||||
} else {
|
|
||||||
valueField.typedInput('value', "");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
valueField.typedInput('value', opt.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
opt.nameField = nameField;
|
|
||||||
opt.valueField = valueField;
|
|
||||||
|
|
||||||
var actionButton = $('<a/>',{href:"#",class:"red-ui-editableList-item-remove red-ui-button red-ui-button-small"}).appendTo(envRow);
|
|
||||||
$('<i/>',{class:"fa "+(opt.parent?"fa-reply":"fa-remove")}).appendTo(actionButton);
|
|
||||||
var removeTip = RED.popover.tooltip(actionButton,RED._("subflow.env.remove"));
|
|
||||||
actionButton.on("click", function(evt) {
|
|
||||||
evt.preventDefault();
|
|
||||||
removeTip.close();
|
|
||||||
container.parent().addClass("red-ui-editableList-item-deleting")
|
|
||||||
container.fadeOut(300, function() {
|
|
||||||
envContainer.editableList('removeItem',opt);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isTemplateNode) {
|
|
||||||
// Add the UI customisation row
|
|
||||||
// if `opt.ui` does not exist, then apply defaults. If these
|
|
||||||
// defaults do not change then they will get stripped off
|
|
||||||
// before saving.
|
|
||||||
if (opt.type === 'cred') {
|
|
||||||
opt.ui = opt.ui || {
|
|
||||||
icon: "",
|
|
||||||
type: "cred"
|
|
||||||
}
|
|
||||||
opt.ui.type = "cred";
|
|
||||||
} else {
|
|
||||||
opt.ui = opt.ui || {
|
|
||||||
icon: "",
|
|
||||||
type: "input",
|
|
||||||
opts: {types:DEFAULT_ENV_TYPE_LIST}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
opt.ui.label = opt.ui.label || {};
|
|
||||||
opt.ui.type = opt.ui.type || "input";
|
|
||||||
|
|
||||||
var uiRow = $('<div/>').appendTo(container).hide();
|
|
||||||
// save current info for reverting on cancel
|
|
||||||
// var copy = $.extend(true, {}, ui);
|
|
||||||
|
|
||||||
$('<a href="#"><i class="fa fa-angle-right"></a>').prependTo(envRow).on("click", function (evt) {
|
|
||||||
evt.preventDefault();
|
|
||||||
if ($(this).hasClass('expanded')) {
|
|
||||||
uiRow.slideUp();
|
|
||||||
$(this).removeClass('expanded');
|
|
||||||
} else {
|
|
||||||
uiRow.slideDown();
|
|
||||||
$(this).addClass('expanded');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
buildEnvEditRow(uiRow, opt.ui, nameField, valueField);
|
|
||||||
nameField.trigger('change');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sortable: ".red-ui-editableList-item-handle",
|
|
||||||
removable: false
|
|
||||||
});
|
|
||||||
var parentEnv = {};
|
|
||||||
var envList = [];
|
|
||||||
if (/^subflow:/.test(node.type)) {
|
|
||||||
var subflowDef = RED.nodes.subflow(node.type.substring(8));
|
|
||||||
if (subflowDef.env) {
|
|
||||||
subflowDef.env.forEach(function(env) {
|
|
||||||
var item = {
|
|
||||||
name:env.name,
|
|
||||||
parent: {
|
|
||||||
type: env.type,
|
|
||||||
value: env.value,
|
|
||||||
ui: env.ui
|
|
||||||
}
|
|
||||||
}
|
|
||||||
envList.push(item);
|
|
||||||
parentEnv[env.name] = item;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.env) {
|
|
||||||
for (var i = 0; i < node.env.length; i++) {
|
|
||||||
var env = node.env[i];
|
|
||||||
if (parentEnv.hasOwnProperty(env.name)) {
|
|
||||||
parentEnv[env.name].type = env.type;
|
|
||||||
parentEnv[env.name].value = env.value;
|
|
||||||
} else {
|
|
||||||
envList.push({
|
|
||||||
name: env.name,
|
|
||||||
type: env.type,
|
|
||||||
value: env.value,
|
|
||||||
ui: env.ui
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
envList.forEach(function(env) {
|
|
||||||
if (env.parent && env.parent.ui && env.parent.ui.type === 'hide') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!isTemplateNode && env.parent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
envContainer.editableList('addItem', JSON.parse(JSON.stringify(env)));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create UI edit interface for environment variable
|
|
||||||
* @param container - container
|
|
||||||
* @param env - env var definition
|
|
||||||
* @param nameField - name field of env var
|
|
||||||
* @param valueField - value field of env var
|
|
||||||
*/
|
|
||||||
function buildEnvEditRow(container, ui, nameField, valueField) {
|
|
||||||
container.addClass("red-ui-editor-subflow-env-ui-row")
|
|
||||||
var topRow = $('<div></div>').appendTo(container);
|
|
||||||
$('<div></div>').appendTo(topRow);
|
|
||||||
$('<div>').text(RED._("editor.icon")).appendTo(topRow);
|
|
||||||
$('<div>').text(RED._("editor.label")).appendTo(topRow);
|
|
||||||
$('<div>').text(RED._("editor.inputType")).appendTo(topRow);
|
|
||||||
|
|
||||||
var row = $('<div></div>').appendTo(container);
|
|
||||||
$('<div><i class="red-ui-editableList-item-handle fa fa-bars"></i></div>').appendTo(row);
|
|
||||||
var typeOptions = {
|
|
||||||
'input': {types:DEFAULT_ENV_TYPE_LIST},
|
|
||||||
'select': {opts:[]},
|
|
||||||
'spinner': {},
|
|
||||||
'cred': {}
|
|
||||||
};
|
|
||||||
if (ui.opts) {
|
|
||||||
typeOptions[ui.type] = ui.opts;
|
|
||||||
} else {
|
|
||||||
// Pick up the default values if not otherwise provided
|
|
||||||
ui.opts = typeOptions[ui.type];
|
|
||||||
}
|
|
||||||
var iconCell = $('<div></div>').appendTo(row);
|
|
||||||
|
|
||||||
var iconButton = $('<a href="#"></a>').appendTo(iconCell);
|
|
||||||
iconButton.on("click", function(evt) {
|
|
||||||
evt.preventDefault();
|
|
||||||
var icon = ui.icon || "";
|
|
||||||
var iconPath = (icon ? RED.utils.separateIconPath(icon) : {});
|
|
||||||
RED.editor.showIconPicker(iconButton, null, iconPath, true, function (newIcon) {
|
|
||||||
iconButton.empty();
|
|
||||||
var path = newIcon || "";
|
|
||||||
var newPath = RED.utils.separateIconPath(path);
|
|
||||||
if (newPath) {
|
|
||||||
$('<i class="fa"></i>').addClass(newPath.file).appendTo(iconButton);
|
|
||||||
}
|
|
||||||
ui.icon = path;
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
if (ui.icon) {
|
|
||||||
var newPath = RED.utils.separateIconPath(ui.icon);
|
|
||||||
$('<i class="fa '+newPath.file+'"></i>').appendTo(iconButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
var labelCell = $('<div></div>').appendTo(row);
|
|
||||||
|
|
||||||
var label = ui.label && ui.label[currentLocale] || "";
|
|
||||||
var labelInput = $('<input type="text">').val(label).appendTo(labelCell);
|
|
||||||
ui.labelField = labelInput;
|
|
||||||
labelInput.on('change', function(evt) {
|
|
||||||
ui.label = ui.label || {};
|
|
||||||
var val = $(this).val().trim();
|
|
||||||
if (val === "") {
|
|
||||||
delete ui.label[currentLocale];
|
|
||||||
} else {
|
|
||||||
ui.label[currentLocale] = val;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
var labelIcon = $('<span class="red-ui-editor-subflow-env-lang-icon"><i class="fa fa-language"></i></span>').appendTo(labelCell);
|
|
||||||
RED.popover.tooltip(labelIcon,function() {
|
|
||||||
var langs = Object.keys(ui.label);
|
|
||||||
var content = $("<div>");
|
|
||||||
if (langs.indexOf(currentLocale) === -1) {
|
|
||||||
langs.push(currentLocale);
|
|
||||||
langs.sort();
|
|
||||||
}
|
|
||||||
langs.forEach(function(l) {
|
|
||||||
var row = $('<div>').appendTo(content);
|
|
||||||
$('<span>').css({display:"inline-block",width:"120px"}).text(RED._("languages."+l)+(l===currentLocale?"*":"")).appendTo(row);
|
|
||||||
$('<span>').text(ui.label[l]||"").appendTo(row);
|
|
||||||
});
|
|
||||||
return content;
|
|
||||||
})
|
|
||||||
|
|
||||||
nameField.on('change',function(evt) {
|
|
||||||
labelInput.attr("placeholder",$(this).val())
|
|
||||||
});
|
|
||||||
|
|
||||||
var inputCell = $('<div></div>').appendTo(row);
|
|
||||||
var inputCellInput = $('<input type="text">').css("width","100%").appendTo(inputCell);
|
|
||||||
if (ui.type === "input") {
|
|
||||||
inputCellInput.val(ui.opts.types.join(","));
|
|
||||||
}
|
|
||||||
var checkbox;
|
|
||||||
var selectBox;
|
|
||||||
|
|
||||||
inputCellInput.typedInput({
|
|
||||||
types: [
|
|
||||||
{
|
|
||||||
value:"input",
|
|
||||||
label:RED._("editor.inputs.input"), icon:"fa fa-i-cursor",showLabel:false,multiple:true,options:[
|
|
||||||
{value:"str",label:RED._("editor.types.str"),icon:"red/images/typedInput/az.svg"},
|
|
||||||
{value:"num",label:RED._("editor.types.num"),icon:"red/images/typedInput/09.svg"},
|
|
||||||
{value:"bool",label:RED._("editor.types.bool"),icon:"red/images/typedInput/bool.svg"},
|
|
||||||
{value:"json",label:RED._("editor.types.json"),icon:"red/images/typedInput/json.svg"},
|
|
||||||
{value: "bin",label: RED._("editor.types.bin"),icon: "red/images/typedInput/bin.svg"},
|
|
||||||
{value: "env",label: RED._("editor.types.env"),icon: "red/images/typedInput/env.svg"},
|
|
||||||
{value: "cred",label: RED._("editor.types.cred"),icon: "fa fa-lock"}
|
|
||||||
],
|
|
||||||
default: DEFAULT_ENV_TYPE_LIST,
|
|
||||||
valueLabel: function(container,value) {
|
|
||||||
container.css("padding",0);
|
|
||||||
var innerContainer = $('<div class="red-ui-editor-subflow-env-input-type"></div>').appendTo(container);
|
|
||||||
|
|
||||||
var input = $('<div class="placeholder-input">').appendTo(innerContainer);
|
|
||||||
$('<span><i class="fa fa-i-cursor"></i></span>').appendTo(input);
|
|
||||||
if (value.length) {
|
|
||||||
value.forEach(function(v) {
|
|
||||||
if (!/^fa /.test(v.icon)) {
|
|
||||||
$('<img>',{src:v.icon,style:"max-width:14px; padding: 0 3px; margin-top:-4px; margin-left: 1px"}).appendTo(input);
|
|
||||||
} else {
|
|
||||||
var s = $('<span>',{style:"max-width:14px; padding: 0 3px; margin-top:-4px; margin-left: 1px"}).appendTo(input);
|
|
||||||
$("<i>",{class: v.icon}).appendTo(s);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
$('<span class="red-ui-editor-subflow-env-input-type-placeholder"></span>').text(RED._("editor.selectType")).appendTo(input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "cred",
|
|
||||||
label: RED._("typedInput.type.cred"), icon:"fa fa-lock", showLabel: false,
|
|
||||||
valueLabel: function(container,value) {
|
|
||||||
container.css("padding",0);
|
|
||||||
var innerContainer = $('<div class="red-ui-editor-subflow-env-input-type">').css({
|
|
||||||
"border-top-right-radius": "4px",
|
|
||||||
"border-bottom-right-radius": "4px"
|
|
||||||
}).appendTo(container);
|
|
||||||
$('<div class="placeholder-input">').html("••••••••").appendTo(innerContainer);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value:"select",
|
|
||||||
label:RED._("editor.inputs.select"), icon:"fa fa-tasks",showLabel:false,
|
|
||||||
valueLabel: function(container,value) {
|
|
||||||
container.css("padding","0");
|
|
||||||
|
|
||||||
selectBox = $('<select></select>').appendTo(container);
|
|
||||||
if (ui.opts && Array.isArray(ui.opts.opts)) {
|
|
||||||
ui.opts.opts.forEach(function(o) {
|
|
||||||
var label = lookupLabel(o.l, o.l["en-US"]||o.v, currentLocale);
|
|
||||||
// $('<option>').val((o.t||'str')+":"+o.v).text(label).appendTo(selectBox);
|
|
||||||
$('<option>').val(o.v).text(label).appendTo(selectBox);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
selectBox.on('change', function(evt) {
|
|
||||||
var v = selectBox.val();
|
|
||||||
// var parts = v.split(":");
|
|
||||||
// var t = parts.shift();
|
|
||||||
// v = parts.join(":");
|
|
||||||
//
|
|
||||||
// valueField.typedInput("type",'str')
|
|
||||||
valueField.typedInput("value",v)
|
|
||||||
});
|
|
||||||
selectBox.val(valueField.typedInput("value"));
|
|
||||||
// selectBox.val(valueField.typedInput('type')+":"+valueField.typedInput("value"));
|
|
||||||
},
|
|
||||||
expand: {
|
|
||||||
icon: "fa-caret-down",
|
|
||||||
minWidth: 400,
|
|
||||||
content: function(container) {
|
|
||||||
var content = $('<div class="red-ui-editor-subflow-ui-edit-panel">').appendTo(container);
|
|
||||||
var optList = $('<ol>').appendTo(content).editableList({
|
|
||||||
header:$("<div><div>"+RED._("editor.select.label")+"</div><div>"+RED._("editor.select.value")+"</div></div>"),
|
|
||||||
addItem: function(row,index,itemData) {
|
|
||||||
var labelDiv = $('<div>').appendTo(row);
|
|
||||||
var label = lookupLabel(itemData.l, "", currentLocale);
|
|
||||||
itemData.label = $('<input type="text">').val(label).appendTo(labelDiv);
|
|
||||||
itemData.label.on('keydown', function(evt) {
|
|
||||||
if (evt.keyCode === 13) {
|
|
||||||
itemData.input.focus();
|
|
||||||
evt.preventDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var labelIcon = $('<span class="red-ui-editor-subflow-env-lang-icon"><i class="fa fa-language"></i></span>').appendTo(labelDiv);
|
|
||||||
RED.popover.tooltip(labelIcon,function() {
|
|
||||||
return currentLocale;
|
|
||||||
})
|
|
||||||
itemData.input = $('<input type="text">').val(itemData.v).appendTo(row);
|
|
||||||
|
|
||||||
// Problem using a TI here:
|
|
||||||
// - this is in a popout panel
|
|
||||||
// - clicking the expand button in the TI will close the parent edit tray
|
|
||||||
// and open the type editor.
|
|
||||||
// - but it leaves the popout panel over the top.
|
|
||||||
// - there is no way to get back to the popout panel after closing the type editor
|
|
||||||
//.typedInput({default:itemData.t||'str', types:DEFAULT_ENV_TYPE_LIST});
|
|
||||||
itemData.input.on('keydown', function(evt) {
|
|
||||||
if (evt.keyCode === 13) {
|
|
||||||
// Enter or Tab
|
|
||||||
var index = optList.editableList('indexOf',itemData);
|
|
||||||
var length = optList.editableList('length');
|
|
||||||
if (index + 1 === length) {
|
|
||||||
var newItem = {};
|
|
||||||
optList.editableList('addItem',newItem);
|
|
||||||
setTimeout(function() {
|
|
||||||
if (newItem.label) {
|
|
||||||
newItem.label.focus();
|
|
||||||
}
|
|
||||||
},100)
|
|
||||||
} else {
|
|
||||||
var nextItem = optList.editableList('getItemAt',index+1);
|
|
||||||
if (nextItem.label) {
|
|
||||||
nextItem.label.focus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
evt.preventDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
sortable: true,
|
|
||||||
removable: true,
|
|
||||||
height: 160
|
|
||||||
})
|
|
||||||
if (ui.opts.opts.length > 0) {
|
|
||||||
ui.opts.opts.forEach(function(o) {
|
|
||||||
optList.editableList('addItem',$.extend(true,{},o))
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
optList.editableList('addItem',{})
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
onclose: function() {
|
|
||||||
var items = optList.editableList('items');
|
|
||||||
var vals = [];
|
|
||||||
items.each(function (i,el) {
|
|
||||||
var data = el.data('data');
|
|
||||||
var l = data.label.val().trim();
|
|
||||||
var v = data.input.val();
|
|
||||||
// var t = data.input.typedInput('type');
|
|
||||||
// var v = data.input.typedInput('value');
|
|
||||||
if (l.length > 0) {
|
|
||||||
data.l = data.l || {};
|
|
||||||
data.l[currentLocale] = l;
|
|
||||||
}
|
|
||||||
data.v = v;
|
|
||||||
|
|
||||||
if (l.length > 0 || v.length > 0) {
|
|
||||||
var val = {l:data.l,v:data.v};
|
|
||||||
// if (t !== 'str') {
|
|
||||||
// val.t = t;
|
|
||||||
// }
|
|
||||||
vals.push(val);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ui.opts.opts = vals;
|
|
||||||
inputCellInput.typedInput('value',Date.now())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value:"checkbox",
|
|
||||||
label:RED._("editor.inputs.checkbox"), icon:"fa fa-check-square-o",showLabel:false,
|
|
||||||
valueLabel: function(container,value) {
|
|
||||||
container.css("padding",0);
|
|
||||||
checkbox = $('<input type="checkbox">').appendTo(container);
|
|
||||||
checkbox.on('change', function(evt) {
|
|
||||||
valueField.typedInput('value',$(this).prop('checked')?"true":"false");
|
|
||||||
})
|
|
||||||
checkbox.prop('checked',valueField.typedInput('value')==="true");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value:"spinner",
|
|
||||||
label:RED._("editor.inputs.spinner"), icon:"fa fa-sort-numeric-asc", showLabel:false,
|
|
||||||
valueLabel: function(container,value) {
|
|
||||||
container.css("padding",0);
|
|
||||||
var innerContainer = $('<div class="red-ui-editor-subflow-env-input-type"></div>').appendTo(container);
|
|
||||||
|
|
||||||
var input = $('<div class="placeholder-input">').appendTo(innerContainer);
|
|
||||||
$('<span><i class="fa fa-sort-numeric-asc"></i></span>').appendTo(input);
|
|
||||||
|
|
||||||
var min = ui.opts && ui.opts.min;
|
|
||||||
var max = ui.opts && ui.opts.max;
|
|
||||||
var label = "";
|
|
||||||
if (min !== undefined && max !== undefined) {
|
|
||||||
label = Math.min(min,max)+" - "+Math.max(min,max);
|
|
||||||
} else if (min !== undefined) {
|
|
||||||
label = "> "+min;
|
|
||||||
} else if (max !== undefined) {
|
|
||||||
label = "< "+max;
|
|
||||||
}
|
|
||||||
$('<span>').css("margin-left","15px").text(label).appendTo(input);
|
|
||||||
},
|
|
||||||
expand: {
|
|
||||||
icon: "fa-caret-down",
|
|
||||||
content: function(container) {
|
|
||||||
var content = $('<div class="red-ui-editor-subflow-ui-edit-panel">').appendTo(container);
|
|
||||||
content.css("padding","8px 5px")
|
|
||||||
var min = ui.opts.min;
|
|
||||||
var max = ui.opts.max;
|
|
||||||
var minInput = $('<input type="number" style="margin-bottom:0; width:60px">');
|
|
||||||
minInput.val(min);
|
|
||||||
var maxInput = $('<input type="number" style="margin-bottom:0; width:60px">');
|
|
||||||
maxInput.val(max);
|
|
||||||
$('<div class="form-row" style="margin-bottom:3px"><label>'+RED._("editor.spinner.min")+'</label></div>').append(minInput).appendTo(content);
|
|
||||||
$('<div class="form-row" style="margin-bottom:0"><label>'+RED._("editor.spinner.max")+'</label></div>').append(maxInput).appendTo(content);
|
|
||||||
return {
|
|
||||||
onclose: function() {
|
|
||||||
var min = minInput.val().trim();
|
|
||||||
var max = maxInput.val().trim();
|
|
||||||
if (min !== "") {
|
|
||||||
ui.opts.min = parseInt(min);
|
|
||||||
} else {
|
|
||||||
delete ui.opts.min;
|
|
||||||
}
|
|
||||||
if (max !== "") {
|
|
||||||
ui.opts.max = parseInt(max);
|
|
||||||
} else {
|
|
||||||
delete ui.opts.max;
|
|
||||||
}
|
|
||||||
inputCellInput.typedInput('value',Date.now())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value:"none",
|
|
||||||
label:RED._("editor.inputs.none"), icon:"fa fa-times",hasValue:false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value:"hide",
|
|
||||||
label:RED._("editor.inputs.hidden"), icon:"fa fa-ban",hasValue:false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
default: 'none'
|
|
||||||
}).on("typedinputtypechange", function(evt,type) {
|
|
||||||
ui.type = $(this).typedInput("type");
|
|
||||||
ui.opts = typeOptions[ui.type];
|
|
||||||
if (ui.type === 'input') {
|
|
||||||
// In the case of 'input' type, the typedInput uses the multiple-option
|
|
||||||
// mode. Its value needs to be set to a comma-separately list of the
|
|
||||||
// selected options.
|
|
||||||
inputCellInput.typedInput('value',ui.opts.types.join(","))
|
|
||||||
} else {
|
|
||||||
// No other type cares about `value`, but doing this will
|
|
||||||
// force a refresh of the label now that `ui.opts` has
|
|
||||||
// been updated.
|
|
||||||
inputCellInput.typedInput('value',Date.now())
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (ui.type) {
|
|
||||||
case 'input':
|
|
||||||
valueField.typedInput('types',ui.opts.types);
|
|
||||||
break;
|
|
||||||
case 'select':
|
|
||||||
valueField.typedInput('types',['str']);
|
|
||||||
break;
|
|
||||||
case 'checkbox':
|
|
||||||
valueField.typedInput('types',['bool']);
|
|
||||||
break;
|
|
||||||
case 'spinner':
|
|
||||||
valueField.typedInput('types',['num']);
|
|
||||||
break;
|
|
||||||
case 'cred':
|
|
||||||
valueField.typedInput('types',['cred']);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
valueField.typedInput('types',DEFAULT_ENV_TYPE_LIST)
|
|
||||||
}
|
|
||||||
if (ui.type === 'checkbox') {
|
|
||||||
valueField.typedInput('type','bool');
|
|
||||||
} else if (ui.type === 'spinner') {
|
|
||||||
valueField.typedInput('type','num');
|
|
||||||
}
|
|
||||||
if (ui.type !== 'checkbox') {
|
|
||||||
checkbox = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}).on("change", function(evt,type) {
|
|
||||||
if (ui.type === 'input') {
|
|
||||||
var types = inputCellInput.typedInput('value');
|
|
||||||
ui.opts.types = (types === "") ? ["str"] : types.split(",");
|
|
||||||
valueField.typedInput('types',ui.opts.types);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
valueField.on("change", function(evt) {
|
|
||||||
if (checkbox) {
|
|
||||||
checkbox.prop('checked',$(this).typedInput('value')==="true")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// Set the input to the right type. This will trigger the 'typedinputtypechange'
|
|
||||||
// event handler (just above ^^) to update the value if needed
|
|
||||||
inputCellInput.typedInput('type',ui.type)
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildEnvUIRow(row, tenv, ui, node) {
|
function buildEnvUIRow(row, tenv, ui, node) {
|
||||||
ui.label = ui.label||{};
|
ui.label = ui.label||{};
|
||||||
@ -1540,7 +929,7 @@ RED.subflow = (function() {
|
|||||||
ui.opts = {};
|
ui.opts = {};
|
||||||
} else if (!ui.type) {
|
} else if (!ui.type) {
|
||||||
ui.type = "input";
|
ui.type = "input";
|
||||||
ui.opts = {types:DEFAULT_ENV_TYPE_LIST}
|
ui.opts = { types: RED.editor.envVarList.DEFAULT_ENV_TYPE_LIST }
|
||||||
} else {
|
} else {
|
||||||
if (!ui.opts) {
|
if (!ui.opts) {
|
||||||
ui.opts = (ui.type === "select") ? {opts:[]} : {};
|
ui.opts = (ui.type === "select") ? {opts:[]} : {};
|
||||||
@ -1549,7 +938,7 @@ RED.subflow = (function() {
|
|||||||
|
|
||||||
var labels = ui.label || {};
|
var labels = ui.label || {};
|
||||||
var locale = RED.i18n.lang();
|
var locale = RED.i18n.lang();
|
||||||
var labelText = lookupLabel(labels, labels["en-US"]||tenv.name, locale);
|
var labelText = RED.editor.envVarList.lookupLabel(labels, labels["en-US"]||tenv.name, locale);
|
||||||
var label = $('<label>').appendTo(row);
|
var label = $('<label>').appendTo(row);
|
||||||
$('<span> </span>').appendTo(row);
|
$('<span> </span>').appendTo(row);
|
||||||
var labelContainer = $('<span></span>').appendTo(label);
|
var labelContainer = $('<span></span>').appendTo(label);
|
||||||
@ -1602,7 +991,7 @@ RED.subflow = (function() {
|
|||||||
input = $('<select>').css('width','70%').appendTo(row);
|
input = $('<select>').css('width','70%').appendTo(row);
|
||||||
if (ui.opts.opts) {
|
if (ui.opts.opts) {
|
||||||
ui.opts.opts.forEach(function(o) {
|
ui.opts.opts.forEach(function(o) {
|
||||||
$('<option>').val(o.v).text(lookupLabel(o.l, o.l['en-US']||o.v, locale)).appendTo(input);
|
$('<option>').val(o.v).text(RED.editor.envVarList.lookupLabel(o.l, o.l['en-US']||o.v, locale)).appendTo(input);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
input.val(val.value);
|
input.val(val.value);
|
||||||
@ -1670,7 +1059,6 @@ RED.subflow = (function() {
|
|||||||
*/
|
*/
|
||||||
function buildEnvUI(uiContainer, envList, node) {
|
function buildEnvUI(uiContainer, envList, node) {
|
||||||
uiContainer.empty();
|
uiContainer.empty();
|
||||||
var elementID = 0;
|
|
||||||
for (var i = 0; i < envList.length; i++) {
|
for (var i = 0; i < envList.length; i++) {
|
||||||
var tenv = envList[i];
|
var tenv = envList[i];
|
||||||
if (tenv.ui && tenv.ui.type === 'hide') {
|
if (tenv.ui && tenv.ui.type === 'hide') {
|
||||||
@ -1678,8 +1066,6 @@ RED.subflow = (function() {
|
|||||||
}
|
}
|
||||||
var row = $("<div/>", { class: "form-row" }).appendTo(uiContainer);
|
var row = $("<div/>", { class: "form-row" }).appendTo(uiContainer);
|
||||||
buildEnvUIRow(row,tenv, tenv.ui || {}, node);
|
buildEnvUIRow(row,tenv, tenv.ui || {}, node);
|
||||||
|
|
||||||
// console.log(ui);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// buildEnvUI
|
// buildEnvUI
|
||||||
@ -1715,7 +1101,7 @@ RED.subflow = (function() {
|
|||||||
// icon: "",
|
// icon: "",
|
||||||
// label: {},
|
// label: {},
|
||||||
// type: "input",
|
// type: "input",
|
||||||
// opts: {types:DEFAULT_ENV_TYPE_LIST}
|
// opts: {types:RED.editor.envVarList.DEFAULT_ENV_TYPE_LIST}
|
||||||
// }
|
// }
|
||||||
if (!ui.icon) {
|
if (!ui.icon) {
|
||||||
delete ui.icon;
|
delete ui.icon;
|
||||||
@ -1725,7 +1111,7 @@ RED.subflow = (function() {
|
|||||||
}
|
}
|
||||||
switch (ui.type) {
|
switch (ui.type) {
|
||||||
case "input":
|
case "input":
|
||||||
if (JSON.stringify(ui.opts) === JSON.stringify({types:DEFAULT_ENV_TYPE_LIST})) {
|
if (JSON.stringify(ui.opts) === JSON.stringify({types:RED.editor.envVarList.DEFAULT_ENV_TYPE_LIST})) {
|
||||||
// This is the default input config. Delete it as it will
|
// This is the default input config. Delete it as it will
|
||||||
// be applied automatically
|
// be applied automatically
|
||||||
delete ui.type;
|
delete ui.type;
|
||||||
@ -1841,7 +1227,6 @@ RED.subflow = (function() {
|
|||||||
|
|
||||||
function exportSubflowInstanceEnv(node) {
|
function exportSubflowInstanceEnv(node) {
|
||||||
var env = [];
|
var env = [];
|
||||||
|
|
||||||
// First, get the values for the SubflowTemplate defined properties
|
// First, get the values for the SubflowTemplate defined properties
|
||||||
// - these are the ones with custom UI elements
|
// - these are the ones with custom UI elements
|
||||||
var parentEnv = getSubflowInstanceParentEnv(node);
|
var parentEnv = getSubflowInstanceParentEnv(node);
|
||||||
@ -1853,7 +1238,7 @@ RED.subflow = (function() {
|
|||||||
ui.type = "cred";
|
ui.type = "cred";
|
||||||
} else {
|
} else {
|
||||||
ui.type = "input";
|
ui.type = "input";
|
||||||
ui.opts = {types:DEFAULT_ENV_TYPE_LIST}
|
ui.opts = {types:RED.editor.envVarList.DEFAULT_ENV_TYPE_LIST}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ui.opts = ui.opts || {};
|
ui.opts = ui.opts || {};
|
||||||
@ -1893,22 +1278,6 @@ RED.subflow = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// Second, get the values from the Properties table tab
|
|
||||||
var items = $('#red-ui-editor-subflow-env-list').editableList('items');
|
|
||||||
items.each(function (i,el) {
|
|
||||||
var data = el.data('data');
|
|
||||||
var item;
|
|
||||||
if (data.nameField && data.valueField) {
|
|
||||||
item = {
|
|
||||||
name: data.nameField.val(),
|
|
||||||
value: data.valueField.typedInput("value"),
|
|
||||||
type: data.valueField.typedInput("type")
|
|
||||||
}
|
|
||||||
if (item.name.trim() !== "") {
|
|
||||||
env.push(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1916,164 +1285,18 @@ RED.subflow = (function() {
|
|||||||
return 'node-input-subflow-env-'+name.replace(/[^a-z0-9-_]/ig,"_");
|
return 'node-input-subflow-env-'+name.replace(/[^a-z0-9-_]/ig,"_");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Called by subflow.oneditprepare for both instances and templates
|
||||||
* Lookup text for specific locale
|
|
||||||
* @param labels - dict of labels
|
|
||||||
* @param defaultLabel - fallback label if not found
|
|
||||||
* @param locale - target locale
|
|
||||||
* @returns {string} text for specified locale
|
|
||||||
*/
|
|
||||||
function lookupLabel(labels, defaultLabel, locale) {
|
|
||||||
if (labels) {
|
|
||||||
if (labels[locale]) {
|
|
||||||
return labels[locale];
|
|
||||||
}
|
|
||||||
if (locale) {
|
|
||||||
var lang = locale.substring(0, 2);
|
|
||||||
if (labels[lang]) {
|
|
||||||
return labels[lang];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultLabel;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildEditForm(type,node) {
|
function buildEditForm(type,node) {
|
||||||
if (type === "subflow-template") {
|
if (type === "subflow-template") {
|
||||||
buildPropertiesList($('#node-input-env-container'), node);
|
// This is the tabbed UI that offers the env list - with UI options
|
||||||
|
// plus the preview tab
|
||||||
|
buildEnvControl($('#node-input-env-container'), node);
|
||||||
|
RED.editor.envVarList.create($('#node-input-env-container'), node);
|
||||||
} else if (type === "subflow") {
|
} else if (type === "subflow") {
|
||||||
// This gets called by the subflow type `oneditprepare` function
|
// This is the rendered version of the subflow env var list
|
||||||
// registered in nodes.js#addSubflow()
|
|
||||||
buildEnvUI($("#subflow-input-ui"), getSubflowInstanceParentEnv(node), node);
|
buildEnvUI($("#subflow-input-ui"), getSubflowInstanceParentEnv(node), node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function buildPropertiesForm(node) {
|
|
||||||
var container = $('#editor-subflow-envProperties-content');
|
|
||||||
var form = $('<form class="dialog-form form-horizontal"></form>').appendTo(container);
|
|
||||||
var listContainer = $('<div class="form-row node-input-env-container-row"></div>').appendTo(form);
|
|
||||||
var list = $('<ol id="red-ui-editor-subflow-env-list" class="red-ui-editor-subflow-env-list"></ol>').appendTo(listContainer);
|
|
||||||
buildPropertiesList(list, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupInputValidation(input,validator) {
|
|
||||||
var errorTip;
|
|
||||||
var validateTimeout;
|
|
||||||
|
|
||||||
var validateFunction = function() {
|
|
||||||
if (validateTimeout) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
validateTimeout = setTimeout(function() {
|
|
||||||
var error = validator(input.val());
|
|
||||||
// if (!error && errorTip) {
|
|
||||||
// errorTip.close();
|
|
||||||
// errorTip = null;
|
|
||||||
// } else if (error && !errorTip) {
|
|
||||||
// errorTip = RED.popover.create({
|
|
||||||
// tooltip: true,
|
|
||||||
// target:input,
|
|
||||||
// size: "small",
|
|
||||||
// direction: "bottom",
|
|
||||||
// content: error,
|
|
||||||
// }).open();
|
|
||||||
// }
|
|
||||||
input.toggleClass("input-error",!!error);
|
|
||||||
validateTimeout = null;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
input.on("change keyup paste", validateFunction);
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildModuleForm(container, node) {
|
|
||||||
$(_subflowModulePaneTemplate).appendTo(container);
|
|
||||||
var moduleProps = node.meta || {};
|
|
||||||
[
|
|
||||||
'module',
|
|
||||||
'type',
|
|
||||||
'version',
|
|
||||||
'author',
|
|
||||||
'desc',
|
|
||||||
'keywords',
|
|
||||||
'license'
|
|
||||||
].forEach(function(property) {
|
|
||||||
$("#subflow-input-module-"+property).val(moduleProps[property]||"")
|
|
||||||
})
|
|
||||||
$("#subflow-input-module-type").attr("placeholder",node.id);
|
|
||||||
|
|
||||||
setupInputValidation($("#subflow-input-module-module"), function(newValue) {
|
|
||||||
newValue = newValue.trim();
|
|
||||||
var isValid = newValue.length < 215;
|
|
||||||
isValid = isValid && !/^[._]/.test(newValue);
|
|
||||||
isValid = isValid && !/[A-Z]/.test(newValue);
|
|
||||||
if (newValue !== encodeURIComponent(newValue)) {
|
|
||||||
var m = /^@([^\/]+)\/([^\/]+)$/.exec(newValue);
|
|
||||||
if (m) {
|
|
||||||
isValid = isValid && (m[1] === encodeURIComponent(m[1]) && m[2] === encodeURIComponent(m[2]))
|
|
||||||
} else {
|
|
||||||
isValid = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return isValid?"":"Invalid module name"
|
|
||||||
})
|
|
||||||
setupInputValidation($("#subflow-input-module-version"), function(newValue) {
|
|
||||||
newValue = newValue.trim();
|
|
||||||
var isValid = newValue === "" ||
|
|
||||||
/^(\d|[1-9]\d*)\.(\d|[1-9]\d*)\.(\d|[1-9]\d*)(-(0|[1-9A-Za-z-][0-9A-Za-z-]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\.(0|[1-9A-Za-z-][0-9A-Za-z-]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$/.test(newValue);
|
|
||||||
return isValid?"":"Invalid version number"
|
|
||||||
})
|
|
||||||
|
|
||||||
var licenses = ["none", "Apache-2.0", "BSD-3-Clause", "BSD-2-Clause", "GPL-2.0", "GPL-3.0", "MIT", "MPL-2.0", "CDDL-1.0", "EPL-2.0"];
|
|
||||||
var typedLicenses = {
|
|
||||||
types: licenses.map(function(l) {
|
|
||||||
return {
|
|
||||||
value: l,
|
|
||||||
label: l === "none" ? RED._("editor:subflow.licenseNone") : l,
|
|
||||||
hasValue: false
|
|
||||||
};
|
|
||||||
})
|
|
||||||
}
|
|
||||||
typedLicenses.types.push({
|
|
||||||
value:"_custom_", label:RED._("editor:subflow.licenseOther"), icon:"red/images/typedInput/az.svg"
|
|
||||||
})
|
|
||||||
if (!moduleProps.license) {
|
|
||||||
typedLicenses.default = "none";
|
|
||||||
} else if (licenses.indexOf(moduleProps.license) > -1) {
|
|
||||||
typedLicenses.default = moduleProps.license;
|
|
||||||
} else {
|
|
||||||
typedLicenses.default = "_custom_";
|
|
||||||
}
|
|
||||||
$("#subflow-input-module-license").typedInput(typedLicenses)
|
|
||||||
}
|
|
||||||
|
|
||||||
function exportSubflowModuleProperties(node) {
|
|
||||||
var value;
|
|
||||||
var moduleProps = {};
|
|
||||||
[
|
|
||||||
'module',
|
|
||||||
'type',
|
|
||||||
'version',
|
|
||||||
'author',
|
|
||||||
'desc',
|
|
||||||
'keywords'
|
|
||||||
].forEach(function(property) {
|
|
||||||
value = $("#subflow-input-module-"+property).val().trim();
|
|
||||||
if (value) {
|
|
||||||
moduleProps[property] = value;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
var selectedLicenseType = $("#subflow-input-module-license").typedInput("type");
|
|
||||||
|
|
||||||
if (selectedLicenseType === '_custom_') {
|
|
||||||
value = $("#subflow-input-module-license").val();
|
|
||||||
if (value) {
|
|
||||||
moduleProps.license = value;
|
|
||||||
}
|
|
||||||
} else if (selectedLicenseType !== "none") {
|
|
||||||
moduleProps.license = selectedLicenseType;
|
|
||||||
}
|
|
||||||
return moduleProps;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
init: init,
|
init: init,
|
||||||
@ -2085,14 +1308,9 @@ RED.subflow = (function() {
|
|||||||
removeOutput: removeSubflowOutput,
|
removeOutput: removeSubflowOutput,
|
||||||
removeStatus: removeSubflowStatus,
|
removeStatus: removeSubflowStatus,
|
||||||
|
|
||||||
|
|
||||||
buildEditForm: buildEditForm,
|
buildEditForm: buildEditForm,
|
||||||
buildPropertiesForm: buildPropertiesForm,
|
|
||||||
buildModuleForm: buildModuleForm,
|
|
||||||
|
|
||||||
exportSubflowTemplateEnv: exportEnvList,
|
exportSubflowTemplateEnv: exportEnvList,
|
||||||
exportSubflowInstanceEnv: exportSubflowInstanceEnv,
|
exportSubflowInstanceEnv: exportSubflowInstanceEnv
|
||||||
exportSubflowModuleProperties: exportSubflowModuleProperties
|
|
||||||
|
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
@ -311,6 +311,7 @@ RED.sidebar.info.outliner = (function() {
|
|||||||
RED.events.on("nodes:add",onNodeAdd);
|
RED.events.on("nodes:add",onNodeAdd);
|
||||||
RED.events.on("nodes:remove",onObjectRemove);
|
RED.events.on("nodes:remove",onObjectRemove);
|
||||||
RED.events.on("nodes:change",onNodeChange);
|
RED.events.on("nodes:change",onNodeChange);
|
||||||
|
// RED.events.on("nodes:reorder",onNodesReorder);
|
||||||
|
|
||||||
RED.events.on("groups:add",onNodeAdd);
|
RED.events.on("groups:add",onNodeAdd);
|
||||||
RED.events.on("groups:remove",onObjectRemove);
|
RED.events.on("groups:remove",onObjectRemove);
|
||||||
@ -369,6 +370,21 @@ RED.sidebar.info.outliner = (function() {
|
|||||||
return indexMap[A.id] - indexMap[B.id]
|
return indexMap[A.id] - indexMap[B.id]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// function onNodesReorder(event) {
|
||||||
|
// //
|
||||||
|
// var nodes = RED.nodes.getNodeOrder(event.z);
|
||||||
|
// var indexMap = {};
|
||||||
|
// nodes.forEach(function(id,index) {
|
||||||
|
// indexMap[id] = index;
|
||||||
|
// })
|
||||||
|
// var existingObject = objects[event.z];
|
||||||
|
// existingObject.treeList.sortChildren(function(A,B) {
|
||||||
|
// if (A.children && !B.children) { return -1 }
|
||||||
|
// if (!A.children && B.children) { return 1 }
|
||||||
|
// if (A.children && B.children) { return -1 }
|
||||||
|
// return indexMap[A.id] - indexMap[B.id]
|
||||||
|
// })
|
||||||
|
// }
|
||||||
function onSubflowAdd(sf) {
|
function onSubflowAdd(sf) {
|
||||||
objects[sf.id] = {
|
objects[sf.id] = {
|
||||||
id: sf.id,
|
id: sf.id,
|
||||||
|
@ -427,18 +427,309 @@ RED.view.tools = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function alignSelectionToEdge(direction) {
|
||||||
|
var selection = RED.view.selection();
|
||||||
|
|
||||||
|
if (selection.nodes && selection.nodes.length > 1) {
|
||||||
|
var changedNodes = [];
|
||||||
|
var bounds = {
|
||||||
|
minX: Number.MAX_SAFE_INTEGER,
|
||||||
|
minY: Number.MAX_SAFE_INTEGER,
|
||||||
|
maxX: Number.MIN_SAFE_INTEGER,
|
||||||
|
maxY: Number.MIN_SAFE_INTEGER
|
||||||
|
}
|
||||||
|
selection.nodes.forEach(function(n) {
|
||||||
|
if (n.type === "group") {
|
||||||
|
bounds.minX = Math.min(bounds.minX, n.x);
|
||||||
|
bounds.minY = Math.min(bounds.minY, n.y);
|
||||||
|
bounds.maxX = Math.max(bounds.maxX, n.x + n.w);
|
||||||
|
bounds.maxY = Math.max(bounds.maxY, n.y + n.h);
|
||||||
|
} else {
|
||||||
|
bounds.minX = Math.min(bounds.minX, n.x - n.w/2);
|
||||||
|
bounds.minY = Math.min(bounds.minY, n.y - n.h/2);
|
||||||
|
bounds.maxX = Math.max(bounds.maxX, n.x + n.w/2);
|
||||||
|
bounds.maxY = Math.max(bounds.maxY, n.y + n.h/2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
bounds.midX = bounds.minX + (bounds.maxX - bounds.minX)/2;
|
||||||
|
bounds.midY = bounds.minY + (bounds.maxY - bounds.minY)/2;
|
||||||
|
|
||||||
|
selection.nodes.forEach(function(n) {
|
||||||
|
var targetX;
|
||||||
|
var targetY;
|
||||||
|
var isGroup = n.type==="group";
|
||||||
|
switch(direction) {
|
||||||
|
case 'top':
|
||||||
|
targetX = n.x;
|
||||||
|
targetY = bounds.minY + (isGroup?0:(n.h/2));
|
||||||
|
break;
|
||||||
|
case 'bottom':
|
||||||
|
targetX = n.x;
|
||||||
|
targetY = bounds.maxY - (isGroup?n.h:(n.h/2));
|
||||||
|
break;
|
||||||
|
case 'left':
|
||||||
|
targetX = bounds.minX + (isGroup?0:(n.w/2));
|
||||||
|
targetY = n.y;
|
||||||
|
break;
|
||||||
|
case 'right':
|
||||||
|
targetX = bounds.maxX - (isGroup?n.w:(n.w/2));
|
||||||
|
targetY = n.y;
|
||||||
|
break;
|
||||||
|
case 'middle':
|
||||||
|
targetX = n.x;
|
||||||
|
targetY = bounds.midY - (isGroup?n.h/2:0)
|
||||||
|
break;
|
||||||
|
case 'center':
|
||||||
|
targetX = bounds.midX - (isGroup?n.w/2:0)
|
||||||
|
targetY = n.y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n.x !== targetX || n.y !== targetY) {
|
||||||
|
if (!isGroup) {
|
||||||
|
changedNodes.push({
|
||||||
|
n:n,
|
||||||
|
ox: n.x,
|
||||||
|
oy: n.y,
|
||||||
|
moved: n.moved
|
||||||
|
});
|
||||||
|
n.x = targetX;
|
||||||
|
n.y = targetY;
|
||||||
|
n.dirty = true;
|
||||||
|
n.moved = true;
|
||||||
|
} else {
|
||||||
|
var groupNodes = RED.group.getNodes(n, true);
|
||||||
|
var deltaX = n.x - targetX;
|
||||||
|
var deltaY = n.y - targetY;
|
||||||
|
groupNodes.forEach(function(gn) {
|
||||||
|
if (gn.type !== "group" ) {
|
||||||
|
changedNodes.push({
|
||||||
|
n:gn,
|
||||||
|
ox: gn.x,
|
||||||
|
oy: gn.y,
|
||||||
|
moved: gn.moved
|
||||||
|
});
|
||||||
|
gn.x = gn.x - deltaX;
|
||||||
|
gn.y = gn.y - deltaY;
|
||||||
|
gn.dirty = true;
|
||||||
|
gn.moved = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (changedNodes.length > 0) {
|
||||||
|
RED.history.push({t:"move",nodes:changedNodes,dirty:RED.nodes.dirty()});
|
||||||
|
RED.nodes.dirty(true);
|
||||||
|
RED.view.redraw(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function distributeSelection(direction) {
|
||||||
|
var selection = RED.view.selection();
|
||||||
|
|
||||||
|
if (selection.nodes && selection.nodes.length > 2) {
|
||||||
|
var changedNodes = [];
|
||||||
|
var bounds = {
|
||||||
|
minX: Number.MAX_SAFE_INTEGER,
|
||||||
|
minY: Number.MAX_SAFE_INTEGER,
|
||||||
|
maxX: Number.MIN_SAFE_INTEGER,
|
||||||
|
maxY: Number.MIN_SAFE_INTEGER
|
||||||
|
}
|
||||||
|
var startAnchors = [];
|
||||||
|
var endAnchors = [];
|
||||||
|
|
||||||
|
selection.nodes.forEach(function(n) {
|
||||||
|
var nx,ny;
|
||||||
|
if (n.type === "group") {
|
||||||
|
nx = n.x + n.w/2;
|
||||||
|
ny = n.y + n.h/2;
|
||||||
|
} else {
|
||||||
|
nx = n.x;
|
||||||
|
ny = n.y;
|
||||||
|
}
|
||||||
|
if (direction === "h") {
|
||||||
|
if (nx < bounds.minX) {
|
||||||
|
startAnchors = [];
|
||||||
|
bounds.minX = nx;
|
||||||
|
}
|
||||||
|
if (nx === bounds.minX) {
|
||||||
|
startAnchors.push(n);
|
||||||
|
}
|
||||||
|
if (nx > bounds.maxX) {
|
||||||
|
endAnchors = [];
|
||||||
|
bounds.maxX = nx;
|
||||||
|
}
|
||||||
|
if (nx === bounds.maxX) {
|
||||||
|
endAnchors.push(n);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ny < bounds.minY) {
|
||||||
|
startAnchors = [];
|
||||||
|
bounds.minY = ny;
|
||||||
|
}
|
||||||
|
if (ny === bounds.minY) {
|
||||||
|
startAnchors.push(n);
|
||||||
|
}
|
||||||
|
if (ny > bounds.maxY) {
|
||||||
|
endAnchors = [];
|
||||||
|
bounds.maxY = ny;
|
||||||
|
}
|
||||||
|
if (ny === bounds.maxY) {
|
||||||
|
endAnchors.push(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var startAnchor = startAnchors[0];
|
||||||
|
var endAnchor = endAnchors[0];
|
||||||
|
|
||||||
|
var nodeSpace = 0;
|
||||||
|
var nodesToMove = selection.nodes.filter(function(n) {
|
||||||
|
if (n.id !== startAnchor.id && n.id !== endAnchor.id) {
|
||||||
|
nodeSpace += direction === 'h'?n.w:n.h;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}).sort(function(A,B) {
|
||||||
|
if (direction === 'h') {
|
||||||
|
return A.x - B.x
|
||||||
|
} else {
|
||||||
|
return A.y - B.y
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
var saX = startAnchor.x + startAnchor.w/2;
|
||||||
|
var saY = startAnchor.y + startAnchor.h/2;
|
||||||
|
if (startAnchor.type === "group") {
|
||||||
|
saX = startAnchor.x + startAnchor.w;
|
||||||
|
saY = startAnchor.y + startAnchor.h;
|
||||||
|
}
|
||||||
|
var eaX = endAnchor.x;
|
||||||
|
var eaY = endAnchor.y;
|
||||||
|
if (endAnchor.type !== "group") {
|
||||||
|
eaX -= endAnchor.w/2;
|
||||||
|
eaY -= endAnchor.h/2;
|
||||||
|
}
|
||||||
|
var spaceToFill = direction === 'h'?(eaX - saX - nodeSpace): (eaY - saY - nodeSpace);
|
||||||
|
var spaceBetweenNodes = spaceToFill / (nodesToMove.length + 1);
|
||||||
|
|
||||||
|
var tx = saX;
|
||||||
|
var ty = saY;
|
||||||
|
while(nodesToMove.length > 0) {
|
||||||
|
if (direction === 'h') {
|
||||||
|
tx += spaceBetweenNodes;
|
||||||
|
} else {
|
||||||
|
ty += spaceBetweenNodes;
|
||||||
|
}
|
||||||
|
var nextNode = nodesToMove.shift();
|
||||||
|
var isGroup = nextNode.type==="group";
|
||||||
|
|
||||||
|
var nx = nextNode.x;
|
||||||
|
var ny = nextNode.y;
|
||||||
|
if (!isGroup) {
|
||||||
|
tx += nextNode.w/2;
|
||||||
|
ty += nextNode.h/2;
|
||||||
|
}
|
||||||
|
if ((direction === 'h' && nx !== tx) || (direction === 'v' && ny !== ty)) {
|
||||||
|
if (!isGroup) {
|
||||||
|
changedNodes.push({
|
||||||
|
n:nextNode,
|
||||||
|
ox: nextNode.x,
|
||||||
|
oy: nextNode.y,
|
||||||
|
moved: nextNode.moved
|
||||||
|
});
|
||||||
|
if (direction === 'h') {
|
||||||
|
nextNode.x = tx;
|
||||||
|
} else {
|
||||||
|
nextNode.y = ty;
|
||||||
|
}
|
||||||
|
nextNode.dirty = true;
|
||||||
|
nextNode.moved = true;
|
||||||
|
} else {
|
||||||
|
var groupNodes = RED.group.getNodes(nextNode, true);
|
||||||
|
var deltaX = direction === 'h'? nx - tx : 0;
|
||||||
|
var deltaY = direction === 'v'? ny - ty : 0;
|
||||||
|
groupNodes.forEach(function(gn) {
|
||||||
|
if (gn.type !== "group" ) {
|
||||||
|
changedNodes.push({
|
||||||
|
n:gn,
|
||||||
|
ox: gn.x,
|
||||||
|
oy: gn.y,
|
||||||
|
moved: gn.moved
|
||||||
|
});
|
||||||
|
gn.x = gn.x - deltaX;
|
||||||
|
gn.y = gn.y - deltaY;
|
||||||
|
gn.dirty = true;
|
||||||
|
gn.moved = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isGroup) {
|
||||||
|
tx += nextNode.w;
|
||||||
|
ty += nextNode.h;
|
||||||
|
} else {
|
||||||
|
tx += nextNode.w/2;
|
||||||
|
ty += nextNode.h/2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changedNodes.length > 0) {
|
||||||
|
RED.history.push({t:"move",nodes:changedNodes,dirty:RED.nodes.dirty()});
|
||||||
|
RED.nodes.dirty(true);
|
||||||
|
RED.view.redraw(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reorderSelection(dir) {
|
||||||
|
var selection = RED.view.selection();
|
||||||
|
if (selection.nodes) {
|
||||||
|
var nodesToMove = [];
|
||||||
|
selection.nodes.forEach(function(n) {
|
||||||
|
if (n.type === "group") {
|
||||||
|
nodesToMove = nodesToMove.concat(RED.group.getNodes(n, true).filter(function(n) {
|
||||||
|
return n.type !== "group";
|
||||||
|
}))
|
||||||
|
} else if (n.type !== "subflow"){
|
||||||
|
nodesToMove.push(n);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (nodesToMove.length > 0) {
|
||||||
|
var z = nodesToMove[0].z;
|
||||||
|
var existingOrder = RED.nodes.getNodeOrder(z);
|
||||||
|
var movedNodes;
|
||||||
|
if (dir === "forwards") {
|
||||||
|
movedNodes = RED.nodes.moveNodesForwards(nodesToMove);
|
||||||
|
} else if (dir === "backwards") {
|
||||||
|
movedNodes = RED.nodes.moveNodesBackwards(nodesToMove);
|
||||||
|
} else if (dir === "front") {
|
||||||
|
movedNodes = RED.nodes.moveNodesToFront(nodesToMove);
|
||||||
|
} else if (dir === "back") {
|
||||||
|
movedNodes = RED.nodes.moveNodesToBack(nodesToMove);
|
||||||
|
}
|
||||||
|
if (movedNodes.length > 0) {
|
||||||
|
var newOrder = RED.nodes.getNodeOrder(z);
|
||||||
|
RED.history.push({t:"reorder",nodes:{z:z,from:existingOrder,to:newOrder},dirty:RED.nodes.dirty()});
|
||||||
|
RED.nodes.dirty(true);
|
||||||
|
RED.view.redraw(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
init: function() {
|
init: function() {
|
||||||
RED.actions.add("core:show-selected-node-labels", function() { setSelectedNodeLabelState(true); })
|
RED.actions.add("core:show-selected-node-labels", function() { setSelectedNodeLabelState(true); })
|
||||||
RED.actions.add("core:hide-selected-node-labels", function() { setSelectedNodeLabelState(false); })
|
RED.actions.add("core:hide-selected-node-labels", function() { setSelectedNodeLabelState(false); })
|
||||||
|
|
||||||
RED.actions.add("core:align-selection-to-grid", alignToGrid);
|
|
||||||
|
|
||||||
RED.actions.add("core:scroll-view-up", function() { RED.view.scroll(0,-RED.view.gridSize());});
|
RED.actions.add("core:scroll-view-up", function() { RED.view.scroll(0,-RED.view.gridSize());});
|
||||||
RED.actions.add("core:scroll-view-right", function() { RED.view.scroll(RED.view.gridSize(),0);});
|
RED.actions.add("core:scroll-view-right", function() { RED.view.scroll(RED.view.gridSize(),0);});
|
||||||
RED.actions.add("core:scroll-view-down", function() { RED.view.scroll(0,RED.view.gridSize());});
|
RED.actions.add("core:scroll-view-down", function() { RED.view.scroll(0,RED.view.gridSize());});
|
||||||
@ -454,6 +745,12 @@ RED.view.tools = (function() {
|
|||||||
RED.actions.add("core:move-selection-down", function() { moveSelection(0,1);});
|
RED.actions.add("core:move-selection-down", function() { moveSelection(0,1);});
|
||||||
RED.actions.add("core:move-selection-left", function() { moveSelection(-1,0);});
|
RED.actions.add("core:move-selection-left", function() { moveSelection(-1,0);});
|
||||||
|
|
||||||
|
RED.actions.add("core:move-selection-forwards", function() { reorderSelection('forwards') })
|
||||||
|
RED.actions.add("core:move-selection-backwards", function() { reorderSelection('backwards') })
|
||||||
|
RED.actions.add("core:move-selection-to-front", function() { reorderSelection('front') })
|
||||||
|
RED.actions.add("core:move-selection-to-back", function() { reorderSelection('back') })
|
||||||
|
|
||||||
|
|
||||||
RED.actions.add("core:step-selection-up", function() { moveSelection(0,-RED.view.gridSize());});
|
RED.actions.add("core:step-selection-up", function() { moveSelection(0,-RED.view.gridSize());});
|
||||||
RED.actions.add("core:step-selection-right", function() { moveSelection(RED.view.gridSize(),0);});
|
RED.actions.add("core:step-selection-right", function() { moveSelection(RED.view.gridSize(),0);});
|
||||||
RED.actions.add("core:step-selection-down", function() { moveSelection(0,RED.view.gridSize());});
|
RED.actions.add("core:step-selection-down", function() { moveSelection(0,RED.view.gridSize());});
|
||||||
@ -474,6 +771,20 @@ RED.view.tools = (function() {
|
|||||||
RED.actions.add("core:go-to-nearest-node-on-right", function() { gotoNearestNode('right')})
|
RED.actions.add("core:go-to-nearest-node-on-right", function() { gotoNearestNode('right')})
|
||||||
RED.actions.add("core:go-to-nearest-node-above", function() { gotoNearestNode('up') })
|
RED.actions.add("core:go-to-nearest-node-above", function() { gotoNearestNode('up') })
|
||||||
RED.actions.add("core:go-to-nearest-node-below", function() { gotoNearestNode('down') })
|
RED.actions.add("core:go-to-nearest-node-below", function() { gotoNearestNode('down') })
|
||||||
|
|
||||||
|
RED.actions.add("core:align-selection-to-grid", alignToGrid);
|
||||||
|
RED.actions.add("core:align-selection-to-left", function() { alignSelectionToEdge('left') })
|
||||||
|
RED.actions.add("core:align-selection-to-right", function() { alignSelectionToEdge('right') })
|
||||||
|
RED.actions.add("core:align-selection-to-top", function() { alignSelectionToEdge('top') })
|
||||||
|
RED.actions.add("core:align-selection-to-bottom", function() { alignSelectionToEdge('bottom') })
|
||||||
|
RED.actions.add("core:align-selection-to-middle", function() { alignSelectionToEdge('middle') })
|
||||||
|
RED.actions.add("core:align-selection-to-center", function() { alignSelectionToEdge('center') })
|
||||||
|
|
||||||
|
RED.actions.add("core:distribute-selection-horizontally", function() { distributeSelection('h') })
|
||||||
|
RED.actions.add("core:distribute-selection-vertically", function() { distributeSelection('v') })
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// RED.actions.add("core:add-node", function() { addNode() })
|
// RED.actions.add("core:add-node", function() { addNode() })
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
@ -501,6 +501,28 @@ RED.view = (function() {
|
|||||||
RED.actions.add("core:copy-selection-to-internal-clipboard",copySelection);
|
RED.actions.add("core:copy-selection-to-internal-clipboard",copySelection);
|
||||||
RED.actions.add("core:cut-selection-to-internal-clipboard",function(){copySelection();deleteSelection();});
|
RED.actions.add("core:cut-selection-to-internal-clipboard",function(){copySelection();deleteSelection();});
|
||||||
RED.actions.add("core:paste-from-internal-clipboard",function(){importNodes(clipboard,{generateIds: true});});
|
RED.actions.add("core:paste-from-internal-clipboard",function(){importNodes(clipboard,{generateIds: true});});
|
||||||
|
|
||||||
|
RED.events.on("view:selection-changed", function(selection) {
|
||||||
|
var hasSelection = (selection.nodes && selection.nodes.length > 0);
|
||||||
|
var hasMultipleSelection = hasSelection && selection.nodes.length > 1;
|
||||||
|
RED.menu.setDisabled("menu-item-edit-cut",!hasSelection);
|
||||||
|
RED.menu.setDisabled("menu-item-edit-copy",!hasSelection);
|
||||||
|
RED.menu.setDisabled("menu-item-edit-select-connected",!hasSelection);
|
||||||
|
RED.menu.setDisabled("menu-item-view-tools-move-to-back",!hasSelection);
|
||||||
|
RED.menu.setDisabled("menu-item-view-tools-move-to-front",!hasSelection);
|
||||||
|
RED.menu.setDisabled("menu-item-view-tools-move-backwards",!hasSelection);
|
||||||
|
RED.menu.setDisabled("menu-item-view-tools-move-forwards",!hasSelection);
|
||||||
|
|
||||||
|
RED.menu.setDisabled("menu-item-view-tools-align-left",!hasMultipleSelection);
|
||||||
|
RED.menu.setDisabled("menu-item-view-tools-align-center",!hasMultipleSelection);
|
||||||
|
RED.menu.setDisabled("menu-item-view-tools-align-right",!hasMultipleSelection);
|
||||||
|
RED.menu.setDisabled("menu-item-view-tools-align-top",!hasMultipleSelection);
|
||||||
|
RED.menu.setDisabled("menu-item-view-tools-align-middle",!hasMultipleSelection);
|
||||||
|
RED.menu.setDisabled("menu-item-view-tools-align-bottom",!hasMultipleSelection);
|
||||||
|
RED.menu.setDisabled("menu-item-view-tools-distribute-horizontally",!hasMultipleSelection);
|
||||||
|
RED.menu.setDisabled("menu-item-view-tools-distribute-veritcally",!hasMultipleSelection);
|
||||||
|
})
|
||||||
|
|
||||||
RED.actions.add("core:delete-selection",deleteSelection);
|
RED.actions.add("core:delete-selection",deleteSelection);
|
||||||
RED.actions.add("core:edit-selected-node",editSelection);
|
RED.actions.add("core:edit-selected-node",editSelection);
|
||||||
RED.actions.add("core:go-to-selection",function() {
|
RED.actions.add("core:go-to-selection",function() {
|
||||||
@ -655,14 +677,17 @@ RED.view = (function() {
|
|||||||
var activeWorkspace = RED.workspaces.active();
|
var activeWorkspace = RED.workspaces.active();
|
||||||
|
|
||||||
activeNodes = RED.nodes.filterNodes({z:activeWorkspace});
|
activeNodes = RED.nodes.filterNodes({z:activeWorkspace});
|
||||||
|
activeNodes.forEach(function(n,i) {
|
||||||
|
n._index = i;
|
||||||
|
})
|
||||||
activeLinks = RED.nodes.filterLinks({
|
activeLinks = RED.nodes.filterLinks({
|
||||||
source:{z:activeWorkspace},
|
source:{z:activeWorkspace},
|
||||||
target:{z:activeWorkspace}
|
target:{z:activeWorkspace}
|
||||||
});
|
});
|
||||||
|
|
||||||
activeGroups = RED.nodes.groups(activeWorkspace)||[];
|
activeGroups = RED.nodes.groups(activeWorkspace)||[];
|
||||||
activeGroups.forEach(function(g) {
|
activeGroups.forEach(function(g,i) {
|
||||||
|
g._index = i;
|
||||||
if (g.g) {
|
if (g.g) {
|
||||||
g._root = g.g;
|
g._root = g.g;
|
||||||
g._depth = 1;
|
g._depth = 1;
|
||||||
@ -695,7 +720,8 @@ RED.view = (function() {
|
|||||||
if (a._root === b._root) {
|
if (a._root === b._root) {
|
||||||
return a._depth - b._depth;
|
return a._depth - b._depth;
|
||||||
} else {
|
} else {
|
||||||
return a._root.localeCompare(b._root);
|
// return a._root.localeCompare(b._root);
|
||||||
|
return a._index - b._index;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -704,7 +730,8 @@ RED.view = (function() {
|
|||||||
if (a._root === b._root) {
|
if (a._root === b._root) {
|
||||||
return a._depth - b._depth;
|
return a._depth - b._depth;
|
||||||
} else {
|
} else {
|
||||||
return a._root.localeCompare(b._root);
|
return a._index - b._index;
|
||||||
|
// return a._root.localeCompare(b._root);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -2332,6 +2359,7 @@ RED.view = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
clipboard = JSON.stringify(nns);
|
clipboard = JSON.stringify(nns);
|
||||||
|
RED.menu.setDisabled("menu-item-edit-paste", false);
|
||||||
if (nodeCount > 0) {
|
if (nodeCount > 0) {
|
||||||
RED.notify(RED._("clipboard.nodeCopied",{count:nodeCount}),{id:"clipboard"});
|
RED.notify(RED._("clipboard.nodeCopied",{count:nodeCount}),{id:"clipboard"});
|
||||||
} else if (groupCount > 0) {
|
} else if (groupCount > 0) {
|
||||||
@ -3807,7 +3835,6 @@ RED.view = (function() {
|
|||||||
.attr("class", "red-ui-flow-node red-ui-flow-node-group")
|
.attr("class", "red-ui-flow-node red-ui-flow-node-group")
|
||||||
.classed("red-ui-flow-subflow", activeSubflow != null);
|
.classed("red-ui-flow-subflow", activeSubflow != null);
|
||||||
|
|
||||||
|
|
||||||
nodeEnter.each(function(d,i) {
|
nodeEnter.each(function(d,i) {
|
||||||
this.__outputs__ = [];
|
this.__outputs__ = [];
|
||||||
this.__inputs__ = [];
|
this.__inputs__ = [];
|
||||||
@ -3953,7 +3980,12 @@ RED.view = (function() {
|
|||||||
RED.hooks.trigger("viewAddNode",{node:d,el:this})
|
RED.hooks.trigger("viewAddNode",{node:d,el:this})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var nodesReordered = false;
|
||||||
node.each(function(d,i) {
|
node.each(function(d,i) {
|
||||||
|
if (d._reordered) {
|
||||||
|
nodesReordered = true;
|
||||||
|
delete d._reordered;
|
||||||
|
}
|
||||||
if (d.dirty) {
|
if (d.dirty) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var thisNode = d3.select(this);
|
var thisNode = d3.select(this);
|
||||||
@ -4261,6 +4293,13 @@ RED.view = (function() {
|
|||||||
|
|
||||||
RED.hooks.trigger("viewRedrawNode",{node:d,el:this})
|
RED.hooks.trigger("viewRedrawNode",{node:d,el:this})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (nodesReordered) {
|
||||||
|
node.sort(function(a,b) {
|
||||||
|
return a._index - b._index;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
var link = linkLayer.selectAll(".red-ui-flow-link").data(
|
var link = linkLayer.selectAll(".red-ui-flow-link").data(
|
||||||
activeLinks,
|
activeLinks,
|
||||||
function(d) {
|
function(d) {
|
||||||
@ -4499,7 +4538,7 @@ RED.view = (function() {
|
|||||||
if (a._root === b._root) {
|
if (a._root === b._root) {
|
||||||
return a._depth - b._depth;
|
return a._depth - b._depth;
|
||||||
} else {
|
} else {
|
||||||
return a._root.localeCompare(b._root);
|
return a._index - b._index;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,9 @@ RED.workspaces = (function() {
|
|||||||
var viewStack = [];
|
var viewStack = [];
|
||||||
var viewStackPos = 0;
|
var viewStackPos = 0;
|
||||||
|
|
||||||
|
function isSameObj(env0, env1) {
|
||||||
|
return (JSON.stringify(env0) === JSON.stringify(env1));
|
||||||
|
}
|
||||||
|
|
||||||
function addToViewStack(id) {
|
function addToViewStack(id) {
|
||||||
if (viewStackPos !== viewStack.length) {
|
if (viewStackPos !== viewStack.length) {
|
||||||
@ -43,7 +46,7 @@ RED.workspaces = (function() {
|
|||||||
workspaceIndex += 1;
|
workspaceIndex += 1;
|
||||||
} while ($("#red-ui-workspace-tabs a[title='"+RED._('workspace.defaultName',{number:workspaceIndex})+"']").size() !== 0);
|
} while ($("#red-ui-workspace-tabs a[title='"+RED._('workspace.defaultName',{number:workspaceIndex})+"']").size() !== 0);
|
||||||
|
|
||||||
ws = {type:"tab",id:tabId,disabled: false,info:"",label:RED._('workspace.defaultName',{number:workspaceIndex})};
|
ws = {type:"tab",id:tabId,disabled: false,info:"",label:RED._('workspace.defaultName',{number:workspaceIndex}),env:[]};
|
||||||
RED.nodes.addWorkspace(ws,targetIndex);
|
RED.nodes.addWorkspace(ws,targetIndex);
|
||||||
workspace_tabs.addTab(ws,targetIndex);
|
workspace_tabs.addTab(ws,targetIndex);
|
||||||
workspace_tabs.activateTab(tabId);
|
workspace_tabs.activateTab(tabId);
|
||||||
@ -55,6 +58,7 @@ RED.workspaces = (function() {
|
|||||||
RED.view.focus();
|
RED.view.focus();
|
||||||
return ws;
|
return ws;
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteWorkspace(ws) {
|
function deleteWorkspace(ws) {
|
||||||
if (workspaceTabCount === 1) {
|
if (workspaceTabCount === 1) {
|
||||||
return;
|
return;
|
||||||
@ -78,165 +82,9 @@ RED.workspaces = (function() {
|
|||||||
if (subflow) {
|
if (subflow) {
|
||||||
RED.editor.editSubflow(subflow);
|
RED.editor.editSubflow(subflow);
|
||||||
}
|
}
|
||||||
return;
|
} else {
|
||||||
|
RED.editor.editFlow(workspace);
|
||||||
}
|
}
|
||||||
RED.view.state(RED.state.EDITING);
|
|
||||||
var tabflowEditor;
|
|
||||||
var trayOptions = {
|
|
||||||
title: RED._("workspace.editFlow",{name:RED.utils.sanitize(workspace.label)}),
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
id: "node-dialog-delete",
|
|
||||||
class: 'leftButton'+((workspaceTabCount === 1)?" disabled":""),
|
|
||||||
text: RED._("common.label.delete"), //'<i class="fa fa-trash"></i>',
|
|
||||||
click: function() {
|
|
||||||
deleteWorkspace(workspace);
|
|
||||||
RED.tray.close();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "node-dialog-cancel",
|
|
||||||
text: RED._("common.label.cancel"),
|
|
||||||
click: function() {
|
|
||||||
RED.tray.close();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "node-dialog-ok",
|
|
||||||
class: "primary",
|
|
||||||
text: RED._("common.label.done"),
|
|
||||||
click: function() {
|
|
||||||
var label = $( "#node-input-name" ).val();
|
|
||||||
var changed = false;
|
|
||||||
var changes = {};
|
|
||||||
if (workspace.label != label) {
|
|
||||||
changes.label = workspace.label;
|
|
||||||
changed = true;
|
|
||||||
workspace.label = label;
|
|
||||||
workspace_tabs.renameTab(workspace.id,label);
|
|
||||||
}
|
|
||||||
var disabled = $("#node-input-disabled").prop("checked");
|
|
||||||
if (workspace.disabled !== disabled) {
|
|
||||||
changes.disabled = workspace.disabled;
|
|
||||||
changed = true;
|
|
||||||
workspace.disabled = disabled;
|
|
||||||
}
|
|
||||||
var info = tabflowEditor.getValue();
|
|
||||||
if (workspace.info !== info) {
|
|
||||||
changes.info = workspace.info;
|
|
||||||
changed = true;
|
|
||||||
workspace.info = info;
|
|
||||||
}
|
|
||||||
$("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!workspace.disabled);
|
|
||||||
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!workspace.disabled);
|
|
||||||
|
|
||||||
if (changed) {
|
|
||||||
var historyEvent = {
|
|
||||||
t: "edit",
|
|
||||||
changes:changes,
|
|
||||||
node: workspace,
|
|
||||||
dirty: RED.nodes.dirty()
|
|
||||||
}
|
|
||||||
workspace.changed = true;
|
|
||||||
RED.history.push(historyEvent);
|
|
||||||
RED.nodes.dirty(true);
|
|
||||||
RED.sidebar.config.refresh();
|
|
||||||
if (changes.hasOwnProperty('disabled')) {
|
|
||||||
RED.nodes.eachNode(function(n) {
|
|
||||||
if (n.z === workspace.id) {
|
|
||||||
n.dirty = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
RED.view.redraw();
|
|
||||||
}
|
|
||||||
RED.events.emit("flows:change",workspace);
|
|
||||||
}
|
|
||||||
RED.tray.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
resize: function(dimensions) {
|
|
||||||
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
|
|
||||||
var editorRow = $("#dialog-form>div.node-text-editor-row");
|
|
||||||
var height = $("#dialog-form").height();
|
|
||||||
for (var i=0; i<rows.size(); i++) {
|
|
||||||
height -= $(rows[i]).outerHeight(true);
|
|
||||||
}
|
|
||||||
height -= (parseInt($("#dialog-form").css("marginTop"))+parseInt($("#dialog-form").css("marginBottom")));
|
|
||||||
$(".node-text-editor").css("height",height+"px");
|
|
||||||
tabflowEditor.resize();
|
|
||||||
},
|
|
||||||
open: function(tray) {
|
|
||||||
var trayFooter = tray.find(".red-ui-tray-footer");
|
|
||||||
var trayBody = tray.find('.red-ui-tray-body');
|
|
||||||
var trayFooterLeft = $('<div class="red-ui-tray-footer-left"></div>').appendTo(trayFooter)
|
|
||||||
|
|
||||||
var dialogForm = $('<form id="dialog-form" class="form-horizontal"></form>').appendTo(trayBody);
|
|
||||||
$('<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" data-i18n="[placeholder]common.label.name">'+
|
|
||||||
'</div>').appendTo(dialogForm);
|
|
||||||
|
|
||||||
|
|
||||||
if (!workspace.hasOwnProperty("disabled")) {
|
|
||||||
workspace.disabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$('<input id="node-input-disabled" type="checkbox">').prop("checked",workspace.disabled).appendTo(trayFooterLeft).toggleButton({
|
|
||||||
enabledIcon: "fa-circle-thin",
|
|
||||||
disabledIcon: "fa-ban",
|
|
||||||
invertState: true
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
var row = $('<div class="form-row node-text-editor-row">'+
|
|
||||||
'<label for="node-input-info" data-i18n="editor:workspace.info" style="width:300px;"></label>'+
|
|
||||||
'<div style="min-height:250px;" class="node-text-editor" id="node-input-info"></div>'+
|
|
||||||
'</div>').appendTo(dialogForm);
|
|
||||||
tabflowEditor = RED.editor.createEditor({
|
|
||||||
id: 'node-input-info',
|
|
||||||
mode: 'ace/mode/markdown',
|
|
||||||
value: ""
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#node-info-input-info-expand').on("click", function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
var value = tabflowEditor.getValue();
|
|
||||||
RED.editor.editMarkdown({
|
|
||||||
value: value,
|
|
||||||
width: "Infinity",
|
|
||||||
cursor: tabflowEditor.getCursorPosition(),
|
|
||||||
complete: function(v,cursor) {
|
|
||||||
tabflowEditor.setValue(v, -1);
|
|
||||||
tabflowEditor.gotoLine(cursor.row+1,cursor.column,false);
|
|
||||||
setTimeout(function() {
|
|
||||||
tabflowEditor.focus();
|
|
||||||
},300);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$('<input type="text" style="display: none;" />').prependTo(dialogForm);
|
|
||||||
dialogForm.on("submit", function(e) { e.preventDefault();});
|
|
||||||
$("#node-input-name").val(workspace.label);
|
|
||||||
RED.text.bidi.prepareInput($("#node-input-name"));
|
|
||||||
tabflowEditor.getSession().setValue(workspace.info || "", -1);
|
|
||||||
dialogForm.i18n();
|
|
||||||
},
|
|
||||||
close: function() {
|
|
||||||
if (RED.view.state() != RED.state.IMPORT_DRAGGING) {
|
|
||||||
RED.view.state(RED.state.DEFAULT);
|
|
||||||
}
|
|
||||||
var selection = RED.view.selection();
|
|
||||||
if (!selection.nodes && !selection.links && workspace.id === activeWorkspace) {
|
|
||||||
RED.sidebar.info.refresh(workspace);
|
|
||||||
}
|
|
||||||
tabflowEditor.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RED.tray.show(trayOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -293,7 +141,14 @@ RED.workspaces = (function() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onreorder: function(oldOrder, newOrder) {
|
onreorder: function(oldOrder, newOrder) {
|
||||||
RED.history.push({t:'reorder',order:oldOrder,dirty:RED.nodes.dirty()});
|
RED.history.push({
|
||||||
|
t:'reorder',
|
||||||
|
workspaces: {
|
||||||
|
from:oldOrder,
|
||||||
|
to:newOrder
|
||||||
|
},
|
||||||
|
dirty:RED.nodes.dirty()
|
||||||
|
});
|
||||||
RED.nodes.dirty(true);
|
RED.nodes.dirty(true);
|
||||||
setWorkspaceOrder(newOrder);
|
setWorkspaceOrder(newOrder);
|
||||||
},
|
},
|
||||||
@ -445,7 +300,6 @@ RED.workspaces = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function removeWorkspace(ws) {
|
function removeWorkspace(ws) {
|
||||||
if (!ws) {
|
if (!ws) {
|
||||||
deleteWorkspace(RED.nodes.workspace(activeWorkspace));
|
deleteWorkspace(RED.nodes.workspace(activeWorkspace));
|
||||||
@ -474,7 +328,10 @@ RED.workspaces = (function() {
|
|||||||
return {
|
return {
|
||||||
init: init,
|
init: init,
|
||||||
add: addWorkspace,
|
add: addWorkspace,
|
||||||
|
// remove: remove workspace without editor history etc
|
||||||
remove: removeWorkspace,
|
remove: removeWorkspace,
|
||||||
|
// delete: remove workspace and update editor history
|
||||||
|
delete: deleteWorkspace,
|
||||||
order: setWorkspaceOrder,
|
order: setWorkspaceOrder,
|
||||||
edit: editWorkspace,
|
edit: editWorkspace,
|
||||||
contains: function(id) {
|
contains: function(id) {
|
||||||
|
@ -68,6 +68,10 @@
|
|||||||
& > .disabled > a:hover,
|
& > .disabled > a:hover,
|
||||||
& > .disabled > a:focus {
|
& > .disabled > a:focus {
|
||||||
color: $menuDisabledColor;
|
color: $menuDisabledColor;
|
||||||
|
.red-ui-popover-key {
|
||||||
|
color: $menuDisabledColor;
|
||||||
|
border-color: $menuDisabledColor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& > .disabled > a:hover,
|
& > .disabled > a:hover,
|
||||||
@ -102,6 +106,14 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.red-ui-popover-key {
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 13px;
|
||||||
|
float: right;
|
||||||
|
color: $menuColor;
|
||||||
|
border-color: $menuColor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -191,14 +191,17 @@
|
|||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
li a {
|
li a {
|
||||||
color: $header-menu-color;
|
color: $header-menu-color;
|
||||||
padding: 3px 40px;
|
padding: 3px 10px 3px 40px;
|
||||||
img {
|
img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
border: 3px solid transparent;
|
border: 3px solid transparent;
|
||||||
}
|
}
|
||||||
|
.red-ui-popover-key {
|
||||||
|
color: $header-menu-color-disabled !important;
|
||||||
|
border-color: $header-menu-color-disabled !important;
|
||||||
|
}
|
||||||
&.active img {
|
&.active img {
|
||||||
border: 3px solid $header-menu-item-border-active;
|
border: 3px solid $header-menu-item-border-active;
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.red-ui-tabs-fade {
|
.red-ui-tabs-fade {
|
||||||
background-image: linear-gradient(to right, rgba(255,255,255,0.001), $tab-background-active);
|
background-image: linear-gradient(to right, change-color($tab-background-active, $alpha: 0.001), $tab-background-active);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -112,7 +112,7 @@
|
|||||||
&:not(.active) {
|
&:not(.active) {
|
||||||
background: $tab-background-selected;
|
background: $tab-background-selected;
|
||||||
.red-ui-tabs-fade {
|
.red-ui-tabs-fade {
|
||||||
background-image: linear-gradient(to right, rgba(255,255,255,0.001), $tab-background-selected);
|
background-image: linear-gradient(to right, change-color($tab-background-selected, $alpha: 0.001), $tab-background-selected);
|
||||||
}
|
}
|
||||||
.red-ui-tabs-badge-selected {
|
.red-ui-tabs-badge-selected {
|
||||||
background: $tab-background-selected;
|
background: $tab-background-selected;
|
||||||
@ -131,6 +131,9 @@
|
|||||||
&:not(.active) a:hover {
|
&:not(.active) a:hover {
|
||||||
color: $workspace-button-color-hover;
|
color: $workspace-button-color-hover;
|
||||||
background: $tab-background-hover;
|
background: $tab-background-hover;
|
||||||
|
&+.red-ui-tabs-fade {
|
||||||
|
background-image: linear-gradient(to right, change-color($tab-background-hover, $alpha: 0.001), $tab-background-hover);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -308,7 +311,7 @@
|
|||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
width: 15px;
|
width: 15px;
|
||||||
background-image: linear-gradient(to right, rgba(255,255,255,0.001), $tab-background-inactive);
|
background-image: linear-gradient(to right, change-color($tab-background-inactive, $alpha: 0.001), $tab-background-inactive);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,6 +101,9 @@
|
|||||||
}
|
}
|
||||||
.red-ui-treeList-label-text {
|
.red-ui-treeList-label-text {
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
|
&:empty {
|
||||||
|
min-height: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.red-ui-treeList-sublabel-text {
|
.red-ui-treeList-sublabel-text {
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
-->
|
-->
|
||||||
<title>{{ page.title }}</title>
|
<title>{{ page.title }}</title>
|
||||||
<link rel="icon" type="image/png" href="{{ page.favicon }}">
|
<link rel="icon" type="image/png" href="{{ page.favicon }}">
|
||||||
<link rel="mask-icon" href="{{ page.tabicon }}" color="#8f0000">
|
<link rel="mask-icon" href="{{ page.tabicon.icon }}" color="{{ page.tabicon.colour }}">
|
||||||
<link rel="stylesheet" href="vendor/jquery/css/base/jquery-ui.min.css">
|
<link rel="stylesheet" href="vendor/jquery/css/base/jquery-ui.min.css">
|
||||||
<link rel="stylesheet" href="vendor/font-awesome/css/font-awesome.min.css">
|
<link rel="stylesheet" href="vendor/font-awesome/css/font-awesome.min.css">
|
||||||
<link rel="stylesheet" href="red/style.min.css">
|
<link rel="stylesheet" href="red/style.min.css">
|
||||||
|
@ -193,7 +193,7 @@
|
|||||||
}
|
}
|
||||||
/** Perform inject, optionally sending a custom msg (refactored for re-use in the form inject button)*/
|
/** Perform inject, optionally sending a custom msg (refactored for re-use in the form inject button)*/
|
||||||
function doInject(node, customMsg) {
|
function doInject(node, customMsg) {
|
||||||
var label = node._def.label.call(node);
|
var label = node._def.label.call(node,customMsg?customMsg.__user_inject_props__:undefined);
|
||||||
if (label.length > 30) {
|
if (label.length > 30) {
|
||||||
label = label.substring(0, 50) + "...";
|
label = label.substring(0, 50) + "...";
|
||||||
}
|
}
|
||||||
@ -201,7 +201,8 @@
|
|||||||
$.ajax({
|
$.ajax({
|
||||||
url: "inject/" + node.id,
|
url: "inject/" + node.id,
|
||||||
type: "POST",
|
type: "POST",
|
||||||
data: customMsg,
|
data: JSON.stringify(customMsg||{}),
|
||||||
|
contentType: "application/json; charset=utf-8",
|
||||||
success: function (resp) {
|
success: function (resp) {
|
||||||
RED.notify(node._("inject.success", { label: label }), { type: "success", id: "inject", timeout: 2000 });
|
RED.notify(node._("inject.success", { label: label }), { type: "success", id: "inject", timeout: 2000 });
|
||||||
},
|
},
|
||||||
@ -291,7 +292,7 @@
|
|||||||
}
|
}
|
||||||
return lab;
|
return lab;
|
||||||
},
|
},
|
||||||
label: function() {
|
label: function(customProps) {
|
||||||
var suffix = "";
|
var suffix = "";
|
||||||
// if fire once then add small indication
|
// if fire once then add small indication
|
||||||
if (this.once) {
|
if (this.once) {
|
||||||
@ -304,11 +305,23 @@
|
|||||||
if (this.name) {
|
if (this.name) {
|
||||||
return this.name+suffix;
|
return this.name+suffix;
|
||||||
}
|
}
|
||||||
|
var payload = "";
|
||||||
var payload = this.payload || "";
|
var payloadType = "str";
|
||||||
var payloadType = this.payloadType || "str";
|
var topic = "";
|
||||||
var topic = this.topic || "";
|
if (customProps) {
|
||||||
|
for (var i=0;i<customProps.length;i++) {
|
||||||
|
if (customProps[i].p === "payload") {
|
||||||
|
payload = customProps[i].v;
|
||||||
|
payloadType = customProps[i].vt;
|
||||||
|
} else if (customProps[i].p === "topic") {
|
||||||
|
topic = customProps[i].v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
payload = this.payload || "";
|
||||||
|
payloadType = this.payloadType || "str";
|
||||||
|
topic = this.topic || "";
|
||||||
|
}
|
||||||
if (payloadType === "string" ||
|
if (payloadType === "string" ||
|
||||||
payloadType === "str" ||
|
payloadType === "str" ||
|
||||||
payloadType === "num" ||
|
payloadType === "num" ||
|
||||||
@ -496,11 +509,8 @@
|
|||||||
label: node._("inject.injectNow"),
|
label: node._("inject.injectNow"),
|
||||||
click: function(e) {
|
click: function(e) {
|
||||||
var items = eList.editableList('items');
|
var items = eList.editableList('items');
|
||||||
var result = getProps(items);
|
var props = getProps(items);
|
||||||
var m = {__user_inject_props__: []};
|
var m = {__user_inject_props__: props.props};
|
||||||
if (result && result.props && result.props.length) {
|
|
||||||
m.__user_inject_props__ = result.props;
|
|
||||||
}
|
|
||||||
doInject(node, m);
|
doInject(node, m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,10 +27,14 @@
|
|||||||
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="node-red:common.label.property"></span></label>
|
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="node-red:common.label.property"></span></label>
|
||||||
<input type="text" id="node-input-property" style="width:70%;"/>
|
<input type="text" id="node-input-property" style="width:70%;"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row" style="margin-bottom: 0px;">
|
||||||
<label> </label>
|
<label> </label>
|
||||||
<input type="checkbox" id="node-input-septopics" style="display:inline-block; width:20px; vertical-align:baseline;">
|
<input type="checkbox" id="node-input-septopics" style="display:inline-block; width:20px; vertical-align:baseline;">
|
||||||
<span data-i18n="rbe.label.septopics"></span> <input type="text" id="node-input-topi" style="width:27%;"/>
|
<label style="width: auto" for="node-input-septopics" data-i18n="rbe.label.septopics"></label>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label> </label>
|
||||||
|
<input type="text" id="node-input-topi" style="width:70%;"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="rbe.label.name"></span></label>
|
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="rbe.label.name"></span></label>
|
||||||
@ -70,6 +74,10 @@
|
|||||||
if (this.septopics === undefined) {
|
if (this.septopics === undefined) {
|
||||||
$("#node-input-septopics").prop('checked', true);
|
$("#node-input-septopics").prop('checked', true);
|
||||||
}
|
}
|
||||||
|
if (this.topi === undefined) {
|
||||||
|
$("#node-input-topi").val("topic");
|
||||||
|
}
|
||||||
|
|
||||||
$("#node-input-property").typedInput({default:'msg',types:['msg']});
|
$("#node-input-property").typedInput({default:'msg',types:['msg']});
|
||||||
$("#node-input-topi").typedInput({default:'msg',types:['msg']});
|
$("#node-input-topi").typedInput({default:'msg',types:['msg']});
|
||||||
//$( "#node-input-gap" ).spinner({min:0});
|
//$( "#node-input-gap" ).spinner({min:0});
|
||||||
@ -88,6 +96,11 @@
|
|||||||
$("#node-startvalue").hide();
|
$("#node-startvalue").hide();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
$("#node-input-septopics").on("change", function() {
|
||||||
|
$("#node-input-topi").typedInput("disable",!this.checked);
|
||||||
|
})
|
||||||
|
$("#node-input-topi").typedInput("disable",!!!this.septopics);
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -134,7 +134,29 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
|||||||
url = "http://"+url;
|
url = "http://"+url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
url = encodeURI(url);
|
|
||||||
|
// The Request module used in Node-RED 1.x was tolerant of query strings that
|
||||||
|
// were partially encoded. For example - "?a=hello%20there&b=20%"
|
||||||
|
// The GOT module doesn't like that.
|
||||||
|
// The following is an attempt to normalise the url to ensure it is properly
|
||||||
|
// encoded. We cannot just encode it directly as we don't want any valid
|
||||||
|
// encoded entity to end up doubly encoded.
|
||||||
|
if (url.indexOf("?") > -1) {
|
||||||
|
// Only do this if there is a query string to deal with
|
||||||
|
const [hostPath, ...queryString] = url.split("?")
|
||||||
|
const query = queryString.join("?");
|
||||||
|
if (query) {
|
||||||
|
// Look for any instance of % not followed by two hex chars.
|
||||||
|
// Replace any we find with %25.
|
||||||
|
const escapedQueryString = query.replace(/(%.?.?)/g, function(v) {
|
||||||
|
if (/^%[a-f0-9]{2}/i.test(v)) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
return v.replace(/%/,"%25")
|
||||||
|
})
|
||||||
|
url = hostPath+"?"+escapedQueryString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var method = nodeMethod.toUpperCase() || "GET";
|
var method = nodeMethod.toUpperCase() || "GET";
|
||||||
if (msg.method && n.method && (n.method !== "use")) { // warn if override option not set
|
if (msg.method && n.method && (n.method !== "use")) { // warn if override option not set
|
||||||
@ -449,6 +471,14 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
|||||||
if (tlsNode) {
|
if (tlsNode) {
|
||||||
opts.https = {};
|
opts.https = {};
|
||||||
tlsNode.addTLSOptions(opts.https);
|
tlsNode.addTLSOptions(opts.https);
|
||||||
|
if (opts.https.ca) {
|
||||||
|
opts.https.certificateAuthority = opts.https.ca;
|
||||||
|
delete opts.https.ca;
|
||||||
|
}
|
||||||
|
if (opts.https.cert) {
|
||||||
|
opts.https.certificate = opts.https.cert;
|
||||||
|
delete opts.https.cert;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (msg.hasOwnProperty('rejectUnauthorized')) {
|
if (msg.hasOwnProperty('rejectUnauthorized')) {
|
||||||
opts.https = { rejectUnauthorized: msg.rejectUnauthorized };
|
opts.https = { rejectUnauthorized: msg.rejectUnauthorized };
|
||||||
|
@ -629,9 +629,6 @@ module.exports = function(RED) {
|
|||||||
if (node.build === 'object') {
|
if (node.build === 'object') {
|
||||||
propertyKey = RED.util.getMessageProperty(msg,node.key);
|
propertyKey = RED.util.getMessageProperty(msg,node.key);
|
||||||
}
|
}
|
||||||
if (msg.hasOwnProperty("parts")) {
|
|
||||||
propertyIndex = msg.parts.index;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.hasOwnProperty("reset")) {
|
if (msg.hasOwnProperty("reset")) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/nodes",
|
"name": "@node-red/nodes",
|
||||||
"version": "2.0.3",
|
"version": "2.0.6",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -17,7 +17,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": "8.4.1",
|
"acorn": "8.4.1",
|
||||||
"acorn-walk": "8.1.1",
|
"acorn-walk": "8.1.1",
|
||||||
"ajv": "8.6.0",
|
"ajv": "8.6.2",
|
||||||
"body-parser": "1.19.0",
|
"body-parser": "1.19.0",
|
||||||
"cheerio": "1.0.0-rc.10",
|
"cheerio": "1.0.0-rc.10",
|
||||||
"content-type": "1.0.4",
|
"content-type": "1.0.4",
|
||||||
@ -37,7 +37,7 @@
|
|||||||
"js-yaml": "3.14.1",
|
"js-yaml": "3.14.1",
|
||||||
"media-typer": "1.1.0",
|
"media-typer": "1.1.0",
|
||||||
"mqtt": "4.2.8",
|
"mqtt": "4.2.8",
|
||||||
"multer": "1.4.2",
|
"multer": "1.4.3",
|
||||||
"mustache": "4.2.0",
|
"mustache": "4.2.0",
|
||||||
"on-headers": "1.0.2",
|
"on-headers": "1.0.2",
|
||||||
"raw-body": "2.4.1",
|
"raw-body": "2.4.1",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/registry",
|
"name": "@node-red/registry",
|
||||||
"version": "2.0.3",
|
"version": "2.0.6",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@ -16,11 +16,11 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@node-red/util": "2.0.3",
|
"@node-red/util": "2.0.6",
|
||||||
"clone": "2.1.2",
|
"clone": "2.1.2",
|
||||||
"fs-extra": "10.0.0",
|
"fs-extra": "10.0.0",
|
||||||
"semver": "7.3.5",
|
"semver": "7.3.5",
|
||||||
"tar": "6.1.0",
|
"tar": "6.1.11",
|
||||||
"uglify-js": "3.13.10"
|
"uglify-js": "3.14.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,9 @@ var api = module.exports = {
|
|||||||
}
|
}
|
||||||
var sendCredentials = {};
|
var sendCredentials = {};
|
||||||
var cred;
|
var cred;
|
||||||
if (/^subflow(:|$)/.test(opts.type)) {
|
if (/^subflow(:|$)/.test(opts.type) ||
|
||||||
|
(opts.type === "tab") ||
|
||||||
|
(opts.type === "group")) {
|
||||||
for (cred in credentials) {
|
for (cred in credentials) {
|
||||||
if (credentials.hasOwnProperty(cred)) {
|
if (credentials.hasOwnProperty(cred)) {
|
||||||
sendCredentials['has_'+cred] = credentials[cred] != null && credentials[cred] !== '';
|
sendCredentials['has_'+cred] = credentials[cred] != null && credentials[cred] !== '';
|
||||||
|
@ -20,6 +20,7 @@ const events = require("@node-red/util").events;
|
|||||||
var flowUtil = require("./util");
|
var flowUtil = require("./util");
|
||||||
const context = require('../nodes/context');
|
const context = require('../nodes/context');
|
||||||
const hooks = require("@node-red/util").hooks;
|
const hooks = require("@node-red/util").hooks;
|
||||||
|
const credentials = require("../nodes/credentials");
|
||||||
|
|
||||||
var Subflow;
|
var Subflow;
|
||||||
var Log;
|
var Log;
|
||||||
@ -396,6 +397,17 @@ class Flow {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a group node instance
|
||||||
|
* @param {String} id
|
||||||
|
* @return {Node} group node
|
||||||
|
*/
|
||||||
|
getGroupNode(id) {
|
||||||
|
const groups = this.global.groups;
|
||||||
|
return groups[id];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all of the nodes instantiated within this flow
|
* Get all of the nodes instantiated within this flow
|
||||||
* @return {[type]} [description]
|
* @return {[type]} [description]
|
||||||
@ -404,6 +416,82 @@ class Flow {
|
|||||||
return this.activeNodes;
|
return this.activeNodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Get value of environment variable defined in group node.
|
||||||
|
* @param {String} group - group node
|
||||||
|
* @param {String} name - name of variable
|
||||||
|
* @return {Object} object containing the value in val property or null if not defined
|
||||||
|
*/
|
||||||
|
getGroupEnvSetting(node, group, name) {
|
||||||
|
if (group) {
|
||||||
|
if (group.credentials === undefined) {
|
||||||
|
group.credentials = credentials.get(group.id) || {};
|
||||||
|
}
|
||||||
|
if (!name.startsWith("$parent.")) {
|
||||||
|
if (group.env) {
|
||||||
|
if (!group._env) {
|
||||||
|
const envs = group.env;
|
||||||
|
const entries = envs.map((env) => {
|
||||||
|
if (env.type === "cred") {
|
||||||
|
const cred = group.credentials;
|
||||||
|
if (cred.hasOwnProperty(env.name)) {
|
||||||
|
env.value = cred[env.name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [env.name, env];
|
||||||
|
|
||||||
|
return [env.name, env];
|
||||||
|
});
|
||||||
|
group._env = Object.fromEntries(entries);
|
||||||
|
}
|
||||||
|
const env = group._env[name];
|
||||||
|
if (env) {
|
||||||
|
let value = env.value;
|
||||||
|
const type = env.type;
|
||||||
|
if ((type !== "env") ||
|
||||||
|
(value !== name)) {
|
||||||
|
if (type === "env") {
|
||||||
|
value = value.replace(new RegExp("\\${"+name+"}","g"),"${$parent."+name+"}");
|
||||||
|
}
|
||||||
|
if (type === "bool") {
|
||||||
|
const val
|
||||||
|
= ((value === "true") ||
|
||||||
|
(value === true));
|
||||||
|
return {
|
||||||
|
val: val
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (type === "cred") {
|
||||||
|
return {
|
||||||
|
val: value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
var val = redUtil.evaluateNodeProperty(value, type, node, null, null);
|
||||||
|
return {
|
||||||
|
val: val
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
this.error(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
name = name.substring(8);
|
||||||
|
}
|
||||||
|
if (group.g) {
|
||||||
|
const parent = this.getGroupNode(group.g);
|
||||||
|
return this.getGroupEnvSetting(node, parent, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a flow setting value. This currently automatically defers to the parent
|
* Get a flow setting value. This currently automatically defers to the parent
|
||||||
* flow which, as defined in ./index.js returns `process.env[key]`.
|
* flow which, as defined in ./index.js returns `process.env[key]`.
|
||||||
@ -412,6 +500,52 @@ class Flow {
|
|||||||
* @return {[type]} [description]
|
* @return {[type]} [description]
|
||||||
*/
|
*/
|
||||||
getSetting(key) {
|
getSetting(key) {
|
||||||
|
const flow = this.flow;
|
||||||
|
if (flow.credentials === undefined) {
|
||||||
|
flow.credentials = credentials.get(flow.id) || {};
|
||||||
|
}
|
||||||
|
if (flow.env) {
|
||||||
|
if (!key.startsWith("$parent.")) {
|
||||||
|
if (!flow._env) {
|
||||||
|
const envs = flow.env;
|
||||||
|
const entries = envs.map((env) => {
|
||||||
|
if (env.type === "cred") {
|
||||||
|
const cred = flow.credentials;
|
||||||
|
if (cred.hasOwnProperty(env.name)) {
|
||||||
|
env.value = cred[env.name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [env.name, env]
|
||||||
|
});
|
||||||
|
flow._env = Object.fromEntries(entries);
|
||||||
|
}
|
||||||
|
const env = flow._env[key];
|
||||||
|
if (env) {
|
||||||
|
let value = env.value;
|
||||||
|
const type = env.type;
|
||||||
|
if ((type !== "env") || (value !== key)) {
|
||||||
|
if (type === "env") {
|
||||||
|
value = value.replace(new RegExp("\\${"+key+"}","g"),"${$parent."+key+"}");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (type === "bool") {
|
||||||
|
const val = ((value === "true") ||
|
||||||
|
(value === true));
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
if (type === "cred") {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
var val = redUtil.evaluateNodeProperty(value, type, null, null, null);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
this.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return this.parent.getSetting(key);
|
return this.parent.getSetting(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,6 +370,16 @@ class Subflow extends Flow {
|
|||||||
// name starts $parent. ... so delegate to parent automatically
|
// name starts $parent. ... so delegate to parent automatically
|
||||||
name = name.substring(8);
|
name = name.substring(8);
|
||||||
}
|
}
|
||||||
|
const node = this.subflowInstance;
|
||||||
|
if (node.g) {
|
||||||
|
const group = this.getGroupNode(node.g);
|
||||||
|
const result = this.getGroupEnvSetting(node, group, name);
|
||||||
|
if (result) {
|
||||||
|
return result.val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var parent = this.parent;
|
var parent = this.parent;
|
||||||
if (parent) {
|
if (parent) {
|
||||||
var val = parent.getSetting(name);
|
var val = parent.getSetting(name);
|
||||||
|
@ -539,6 +539,9 @@ async function addFlow(flow, user) {
|
|||||||
if (flow.hasOwnProperty('disabled')) {
|
if (flow.hasOwnProperty('disabled')) {
|
||||||
tabNode.disabled = flow.disabled;
|
tabNode.disabled = flow.disabled;
|
||||||
}
|
}
|
||||||
|
if (flow.hasOwnProperty('env')) {
|
||||||
|
tabNode.env = flow.env;
|
||||||
|
}
|
||||||
|
|
||||||
var nodes = [tabNode];
|
var nodes = [tabNode];
|
||||||
|
|
||||||
@ -599,6 +602,9 @@ function getFlow(id) {
|
|||||||
if (flow.hasOwnProperty('info')) {
|
if (flow.hasOwnProperty('info')) {
|
||||||
result.info = flow.info;
|
result.info = flow.info;
|
||||||
}
|
}
|
||||||
|
if (flow.hasOwnProperty('env')) {
|
||||||
|
result.env = flow.env;
|
||||||
|
}
|
||||||
if (id !== 'global') {
|
if (id !== 'global') {
|
||||||
result.nodes = [];
|
result.nodes = [];
|
||||||
}
|
}
|
||||||
@ -694,6 +700,12 @@ async function updateFlow(id,newFlow, user) {
|
|||||||
if (newFlow.hasOwnProperty('disabled')) {
|
if (newFlow.hasOwnProperty('disabled')) {
|
||||||
tabNode.disabled = newFlow.disabled;
|
tabNode.disabled = newFlow.disabled;
|
||||||
}
|
}
|
||||||
|
if (newFlow.hasOwnProperty('env')) {
|
||||||
|
tabNode.env = newFlow.env;
|
||||||
|
}
|
||||||
|
if (newFlow.hasOwnProperty('credentials')) {
|
||||||
|
tabNode.credentials = newFlow.credentials;
|
||||||
|
}
|
||||||
|
|
||||||
nodes = [tabNode].concat(newFlow.nodes||[]).concat(newFlow.configs||[]);
|
nodes = [tabNode].concat(newFlow.nodes||[]).concat(newFlow.configs||[]);
|
||||||
nodes.forEach(function(n) {
|
nodes.forEach(function(n) {
|
||||||
|
@ -44,24 +44,25 @@ function diffNodes(oldNode,newNode) {
|
|||||||
var EnvVarPropertyRE_old = /^\$\((\S+)\)$/;
|
var EnvVarPropertyRE_old = /^\$\((\S+)\)$/;
|
||||||
var EnvVarPropertyRE = /^\${(\S+)}$/;
|
var EnvVarPropertyRE = /^\${(\S+)}$/;
|
||||||
|
|
||||||
function mapEnvVarProperties(obj,prop,flow) {
|
|
||||||
|
function mapEnvVarProperties(obj,prop,flow,config) {
|
||||||
var v = obj[prop];
|
var v = obj[prop];
|
||||||
if (Buffer.isBuffer(v)) {
|
if (Buffer.isBuffer(v)) {
|
||||||
return;
|
return;
|
||||||
} else if (Array.isArray(v)) {
|
} else if (Array.isArray(v)) {
|
||||||
for (var i=0;i<v.length;i++) {
|
for (var i=0;i<v.length;i++) {
|
||||||
mapEnvVarProperties(v,i,flow);
|
mapEnvVarProperties(v,i,flow,config);
|
||||||
}
|
}
|
||||||
} else if (typeof obj[prop] === 'string') {
|
} else if (typeof obj[prop] === 'string') {
|
||||||
if (obj[prop][0] === "$" && (EnvVarPropertyRE_old.test(v) || EnvVarPropertyRE.test(v)) ) {
|
if (obj[prop][0] === "$" && (EnvVarPropertyRE_old.test(v) || EnvVarPropertyRE.test(v)) ) {
|
||||||
var envVar = v.substring(2,v.length-1);
|
var envVar = v.substring(2,v.length-1);
|
||||||
var r = flow.getSetting(envVar);
|
var r = redUtil.getSetting(config, envVar, flow);
|
||||||
obj[prop] = r!==undefined?r:obj[prop];
|
obj[prop] = r ? r : obj[prop];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (var p in v) {
|
for (var p in v) {
|
||||||
if (v.hasOwnProperty(p)) {
|
if (v.hasOwnProperty(p)) {
|
||||||
mapEnvVarProperties(v,p,flow);
|
mapEnvVarProperties(v,p,flow,config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -78,7 +79,7 @@ function createNode(flow,config) {
|
|||||||
delete conf.credentials;
|
delete conf.credentials;
|
||||||
for (var p in conf) {
|
for (var p in conf) {
|
||||||
if (conf.hasOwnProperty(p)) {
|
if (conf.hasOwnProperty(p)) {
|
||||||
mapEnvVarProperties(conf,p,flow);
|
mapEnvVarProperties(conf,p,flow,conf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -38,6 +38,7 @@ function Node(n) {
|
|||||||
this.id = n.id;
|
this.id = n.id;
|
||||||
this.type = n.type;
|
this.type = n.type;
|
||||||
this.z = n.z;
|
this.z = n.z;
|
||||||
|
this.g = n.g;
|
||||||
this._closeCallbacks = [];
|
this._closeCallbacks = [];
|
||||||
this._inputCallback = null;
|
this._inputCallback = null;
|
||||||
this._inputCallbacks = null;
|
this._inputCallbacks = null;
|
||||||
|
@ -103,7 +103,7 @@ function createNode(node,def) {
|
|||||||
// allow $(foo) syntax to substitute env variables for credentials also...
|
// allow $(foo) syntax to substitute env variables for credentials also...
|
||||||
for (var p in creds) {
|
for (var p in creds) {
|
||||||
if (creds.hasOwnProperty(p)) {
|
if (creds.hasOwnProperty(p)) {
|
||||||
flowUtil.mapEnvVarProperties(creds,p,node._flow);
|
flowUtil.mapEnvVarProperties(creds,p,node._flow,node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
node.credentials = creds;
|
node.credentials = creds;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/runtime",
|
"name": "@node-red/runtime",
|
||||||
"version": "2.0.3",
|
"version": "2.0.6",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@ -16,9 +16,9 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@node-red/registry": "2.0.3",
|
"@node-red/registry": "2.0.6",
|
||||||
"@node-red/util": "2.0.3",
|
"@node-red/util": "2.0.6",
|
||||||
"async-mutex": "0.3.1",
|
"async-mutex": "0.3.2",
|
||||||
"clone": "2.1.2",
|
"clone": "2.1.2",
|
||||||
"express": "4.17.1",
|
"express": "4.17.1",
|
||||||
"fs-extra": "10.0.0",
|
"fs-extra": "10.0.0",
|
||||||
|
25
packages/node_modules/@node-red/util/lib/util.js
vendored
25
packages/node_modules/@node-red/util/lib/util.js
vendored
@ -521,13 +521,18 @@ function setObjectProperty(msg,prop,value,createMissing) {
|
|||||||
* @param {String} name - name of variable
|
* @param {String} name - name of variable
|
||||||
* @return {String} value of env var
|
* @return {String} value of env var
|
||||||
*/
|
*/
|
||||||
function getSetting(node, name) {
|
function getSetting(node, name, flow_) {
|
||||||
if (node && node._flow) {
|
var flow = (flow_ ? flow_ : (node ? node._flow : null));
|
||||||
var flow = node._flow;
|
|
||||||
if (flow) {
|
if (flow) {
|
||||||
return flow.getSetting(name);
|
if (node && node.g) {
|
||||||
|
const group = flow.getGroupNode(node.g);
|
||||||
|
const result = flow.getGroupEnvSetting(node, group, name);
|
||||||
|
if (result) {
|
||||||
|
return result.val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return flow.getSetting(name);
|
||||||
|
}
|
||||||
return process.env[name];
|
return process.env[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -544,18 +549,19 @@ function getSetting(node, name) {
|
|||||||
* @memberof @node-red/util_util
|
* @memberof @node-red/util_util
|
||||||
*/
|
*/
|
||||||
function evaluateEnvProperty(value, node) {
|
function evaluateEnvProperty(value, node) {
|
||||||
|
var flow = (node && node.hasOwnProperty("_flow")) ? node._flow : null;
|
||||||
var result;
|
var result;
|
||||||
if (/^\${[^}]+}$/.test(value)) {
|
if (/^\${[^}]+}$/.test(value)) {
|
||||||
// ${ENV_VAR}
|
// ${ENV_VAR}
|
||||||
var name = value.substring(2,value.length-1);
|
var name = value.substring(2,value.length-1);
|
||||||
result = getSetting(node, name);
|
result = getSetting(node, name, flow);
|
||||||
} else if (!/\${\S+}/.test(value)) {
|
} else if (!/\${\S+}/.test(value)) {
|
||||||
// ENV_VAR
|
// ENV_VAR
|
||||||
result = getSetting(node, value);
|
result = getSetting(node, value, flow);
|
||||||
} else {
|
} else {
|
||||||
// FOO${ENV_VAR}BAR
|
// FOO${ENV_VAR}BAR
|
||||||
return value.replace(/\${([^}]+)}/g, function(match, name) {
|
return value.replace(/\${([^}]+)}/g, function(match, name) {
|
||||||
var val = getSetting(node, name);
|
var val = getSetting(node, name, flow);
|
||||||
return (val === undefined)?"":val;
|
return (val === undefined)?"":val;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -668,7 +674,7 @@ function prepareJSONataExpression(value,node) {
|
|||||||
return node.context().global.get(val, store);
|
return node.context().global.get(val, store);
|
||||||
});
|
});
|
||||||
expr.assign('env', function(name) {
|
expr.assign('env', function(name) {
|
||||||
var val = getSetting(node, name);
|
var val = getSetting(node, name, node._flow);
|
||||||
if (typeof val !== 'undefined') {
|
if (typeof val !== 'undefined') {
|
||||||
return val;
|
return val;
|
||||||
} else {
|
} else {
|
||||||
@ -976,5 +982,6 @@ module.exports = {
|
|||||||
normaliseNodeTypeName: normaliseNodeTypeName,
|
normaliseNodeTypeName: normaliseNodeTypeName,
|
||||||
prepareJSONataExpression: prepareJSONataExpression,
|
prepareJSONataExpression: prepareJSONataExpression,
|
||||||
evaluateJSONataExpression: evaluateJSONataExpression,
|
evaluateJSONataExpression: evaluateJSONataExpression,
|
||||||
parseContextStore: parseContextStore
|
parseContextStore: parseContextStore,
|
||||||
|
getSetting: getSetting
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/util",
|
"name": "@node-red/util",
|
||||||
"version": "2.0.3",
|
"version": "2.0.6",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -18,7 +18,7 @@
|
|||||||
"fs-extra": "10.0.0",
|
"fs-extra": "10.0.0",
|
||||||
"i18next": "20.3.2",
|
"i18next": "20.3.2",
|
||||||
"json-stringify-safe": "5.0.1",
|
"json-stringify-safe": "5.0.1",
|
||||||
"jsonata": "1.8.4",
|
"jsonata": "1.8.5",
|
||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
"moment-timezone": "0.5.33"
|
"moment-timezone": "0.5.33"
|
||||||
}
|
}
|
||||||
|
10
packages/node_modules/node-red/package.json
vendored
10
packages/node_modules/node-red/package.json
vendored
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "node-red",
|
"name": "node-red",
|
||||||
"version": "2.0.3",
|
"version": "2.0.6",
|
||||||
"description": "Low-code programming for event-driven applications",
|
"description": "Low-code programming for event-driven applications",
|
||||||
"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": "2.0.3",
|
"@node-red/editor-api": "2.0.6",
|
||||||
"@node-red/runtime": "2.0.3",
|
"@node-red/runtime": "2.0.6",
|
||||||
"@node-red/util": "2.0.3",
|
"@node-red/util": "2.0.6",
|
||||||
"@node-red/nodes": "2.0.3",
|
"@node-red/nodes": "2.0.6",
|
||||||
"basic-auth": "2.0.1",
|
"basic-auth": "2.0.1",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"express": "4.17.1",
|
"express": "4.17.1",
|
||||||
|
7
packages/node_modules/node-red/red.js
vendored
7
packages/node_modules/node-red/red.js
vendored
@ -234,8 +234,13 @@ httpsPromise.then(function(startupHttps) {
|
|||||||
// Get the result of the function, because createServer doesn't accept functions as input
|
// Get the result of the function, because createServer doesn't accept functions as input
|
||||||
Promise.resolve(settings.https()).then(function(refreshedHttps) {
|
Promise.resolve(settings.https()).then(function(refreshedHttps) {
|
||||||
if (refreshedHttps) {
|
if (refreshedHttps) {
|
||||||
|
// The key/cert needs to be updated in the NodeJs http(s) server, when no key/cert is yet available or when the key/cert has changed.
|
||||||
|
// Note that the refreshed key/cert can be supplied as a string or a buffer.
|
||||||
|
var updateKey = (server.key == undefined || (Buffer.isBuffer(server.key) && !server.key.equals(refreshedHttps.key)) || (typeof server.key == "string" && server.key != refreshedHttps.key));
|
||||||
|
var updateCert = (server.cert == undefined || (Buffer.isBuffer(server.cert) && !server.cert.equals(refreshedHttps.cert)) || (typeof server.cert == "string" && server.cert != refreshedHttps.cert));
|
||||||
|
|
||||||
// Only update the credentials in the server when key or cert has changed
|
// Only update the credentials in the server when key or cert has changed
|
||||||
if(!server.key || !server.cert || !server.key.equals(refreshedHttps.key) || !server.cert.equals(refreshedHttps.cert)) {
|
if(updateKey || updateCert) {
|
||||||
server.setSecureContext(refreshedHttps);
|
server.setSecureContext(refreshedHttps);
|
||||||
RED.log.info(RED.log._("server.https.settings-refreshed"));
|
RED.log.info(RED.log._("server.https.settings-refreshed"));
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ const path = require("path");
|
|||||||
const fs = require("fs-extra");
|
const fs = require("fs-extra");
|
||||||
const should = require("should");
|
const should = require("should");
|
||||||
|
|
||||||
|
const LATEST = "2";
|
||||||
|
|
||||||
function generateScript() {
|
function generateScript() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const packages = [
|
const packages = [
|
||||||
@ -18,7 +20,13 @@ function generateScript() {
|
|||||||
const rootPackage = require(path.join(__dirname,"..","package.json"));
|
const rootPackage = require(path.join(__dirname,"..","package.json"));
|
||||||
const version = rootPackage.version;
|
const version = rootPackage.version;
|
||||||
|
|
||||||
const tagArg = /-/.test(version) ? "--tag next" : ""
|
const versionParts = version.split(".");
|
||||||
|
let tagArg = "";
|
||||||
|
if (versionParts[0] !== LATEST) {
|
||||||
|
tagArg = `--tag v${versionParts[0]}-maintenance`
|
||||||
|
} else if (/-/.test(version)) {
|
||||||
|
tagArg = "--tag next"
|
||||||
|
}
|
||||||
|
|
||||||
const lines = [];
|
const lines = [];
|
||||||
|
|
||||||
|
@ -542,6 +542,77 @@ describe('change Node', function() {
|
|||||||
changeNode1.receive({payload:"123",topic:"ABC"});
|
changeNode1.receive({payload:"123",topic:"ABC"});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sets the value using env property from tab', function(done) {
|
||||||
|
var flow = [
|
||||||
|
{"id":"tab1","type":"tab","env":[
|
||||||
|
{"name":"NR_TEST_A", "value":"bar", "type": "str"}
|
||||||
|
]},
|
||||||
|
{"id":"changeNode1","type":"change","z":"tab1",rules:[{"t":"set","p":"payload","pt":"msg","to":"NR_TEST_A","tot":"env"}],"name":"changeNode","wires":[["helperNode1"]]},
|
||||||
|
{id:"helperNode1", type:"helper", wires:[]}
|
||||||
|
];
|
||||||
|
helper.load(changeNode, flow, function() {
|
||||||
|
var changeNode1 = helper.getNode("changeNode1");
|
||||||
|
var helperNode1 = helper.getNode("helperNode1");
|
||||||
|
helperNode1.on("input", function(msg) {
|
||||||
|
try {
|
||||||
|
msg.payload.should.equal("bar");
|
||||||
|
done();
|
||||||
|
} catch(err) {
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
changeNode1.receive({payload:"123",topic:"ABC"});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the value using env property from group', function(done) {
|
||||||
|
var flow = [
|
||||||
|
{"id":"group1","type":"group","env":[
|
||||||
|
{"name":"NR_TEST_A", "value":"bar", "type": "str"}
|
||||||
|
]},
|
||||||
|
{"id":"changeNode1","type":"change","g":"group1",rules:[{"t":"set","p":"payload","pt":"msg","to":"NR_TEST_A","tot":"env"}],"name":"changeNode","wires":[["helperNode1"]]},
|
||||||
|
{id:"helperNode1", type:"helper", wires:[]}
|
||||||
|
];
|
||||||
|
helper.load(changeNode, flow, function() {
|
||||||
|
var changeNode1 = helper.getNode("changeNode1");
|
||||||
|
var helperNode1 = helper.getNode("helperNode1");
|
||||||
|
helperNode1.on("input", function(msg) {
|
||||||
|
try {
|
||||||
|
msg.payload.should.equal("bar");
|
||||||
|
done();
|
||||||
|
} catch(err) {
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
changeNode1.receive({payload:"123",topic:"ABC"});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the value using env property from nested group', function(done) {
|
||||||
|
var flow = [
|
||||||
|
{"id":"group1","type":"group","env":[
|
||||||
|
{"name":"NR_TEST_A", "value":"bar", "type": "str"}
|
||||||
|
]},
|
||||||
|
{"id":"group2","type":"group","g":"group1","env":[]},
|
||||||
|
{"id":"changeNode1","type":"change","g":"group2",rules:[{"t":"set","p":"payload","pt":"msg","to":"NR_TEST_A","tot":"env"}],"name":"changeNode","wires":[["helperNode1"]]},
|
||||||
|
{id:"helperNode1", type:"helper", wires:[]}
|
||||||
|
];
|
||||||
|
helper.load(changeNode, flow, function() {
|
||||||
|
var changeNode1 = helper.getNode("changeNode1");
|
||||||
|
var helperNode1 = helper.getNode("helperNode1");
|
||||||
|
helperNode1.on("input", function(msg) {
|
||||||
|
try {
|
||||||
|
msg.payload.should.equal("bar");
|
||||||
|
done();
|
||||||
|
} catch(err) {
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
changeNode1.receive({payload:"123",topic:"ABC"});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,6 +42,8 @@ describe('HTTP Request Node', function() {
|
|||||||
var testProxyPort = 10444;
|
var testProxyPort = 10444;
|
||||||
var testProxyServerAuth;
|
var testProxyServerAuth;
|
||||||
var testProxyAuthPort = 10554;
|
var testProxyAuthPort = 10554;
|
||||||
|
var testSslClientServer;
|
||||||
|
var testSslClientPort = 10664;
|
||||||
|
|
||||||
//save environment variables
|
//save environment variables
|
||||||
var preEnvHttpProxyLowerCase;
|
var preEnvHttpProxyLowerCase;
|
||||||
@ -57,6 +59,7 @@ describe('HTTP Request Node', function() {
|
|||||||
testServer = stoppable(http.createServer(testApp));
|
testServer = stoppable(http.createServer(testApp));
|
||||||
testServer.listen(testPort,function(err) {
|
testServer.listen(testPort,function(err) {
|
||||||
testSslPort += 1;
|
testSslPort += 1;
|
||||||
|
console.log("ssl port", testSslPort);
|
||||||
var sslOptions = {
|
var sslOptions = {
|
||||||
key: fs.readFileSync('test/resources/ssl/server.key'),
|
key: fs.readFileSync('test/resources/ssl/server.key'),
|
||||||
cert: fs.readFileSync('test/resources/ssl/server.crt')
|
cert: fs.readFileSync('test/resources/ssl/server.crt')
|
||||||
@ -75,7 +78,25 @@ describe('HTTP Request Node', function() {
|
|||||||
*/
|
*/
|
||||||
};
|
};
|
||||||
testSslServer = stoppable(https.createServer(sslOptions,testApp));
|
testSslServer = stoppable(https.createServer(sslOptions,testApp));
|
||||||
testSslServer.listen(testSslPort);
|
testSslServer.listen(testSslPort, function(err){
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
} else {
|
||||||
|
console.log("started testSslServer");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
testSslClientPort += 1;
|
||||||
|
var sslClientOptions = {
|
||||||
|
key: fs.readFileSync('test/resources/ssl/server.key'),
|
||||||
|
cert: fs.readFileSync('test/resources/ssl/server.crt'),
|
||||||
|
ca: fs.readFileSync('test/resources/ssl/server.crt'),
|
||||||
|
requestCert: true
|
||||||
|
};
|
||||||
|
testSslClientServer = stoppable(https.createServer(sslClientOptions, testApp));
|
||||||
|
testSslClientServer.listen(testSslClientPort, function(err){
|
||||||
|
console.log("ssl-client", err)
|
||||||
|
});
|
||||||
|
|
||||||
testProxyPort += 1;
|
testProxyPort += 1;
|
||||||
testProxyServer = stoppable(httpProxy(http.createServer()))
|
testProxyServer = stoppable(httpProxy(http.createServer()))
|
||||||
@ -121,6 +142,10 @@ describe('HTTP Request Node', function() {
|
|||||||
return "https://localhost:"+testSslPort+url;
|
return "https://localhost:"+testSslPort+url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSslClientTestURL(url) {
|
||||||
|
return "https://localhost:"+testSslClientPort+url;
|
||||||
|
}
|
||||||
|
|
||||||
function getDifferentTestURL(url) {
|
function getDifferentTestURL(url) {
|
||||||
return "http://127.0.0.1:"+testPort+url;
|
return "http://127.0.0.1:"+testPort+url;
|
||||||
}
|
}
|
||||||
@ -280,6 +305,14 @@ describe('HTTP Request Node', function() {
|
|||||||
headers:result
|
headers:result
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
testApp.get('/getClientCert', function(req,res) {
|
||||||
|
if (req.client.authorized) {
|
||||||
|
res.send('hello');
|
||||||
|
} else {
|
||||||
|
res.status(401).send();
|
||||||
|
}
|
||||||
|
})
|
||||||
startServer(function(err) {
|
startServer(function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
done(err);
|
done(err);
|
||||||
@ -293,7 +326,9 @@ describe('HTTP Request Node', function() {
|
|||||||
testProxyServer.stop(() => {
|
testProxyServer.stop(() => {
|
||||||
testProxyServerAuth.stop(() => {
|
testProxyServerAuth.stop(() => {
|
||||||
testSslServer.stop(() => {
|
testSslServer.stop(() => {
|
||||||
|
testSslClientServer.stop(() => {
|
||||||
helper.stopServer(done);
|
helper.stopServer(done);
|
||||||
|
})
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -1145,7 +1180,7 @@ describe('HTTP Request Node', function() {
|
|||||||
try {
|
try {
|
||||||
msg.should.have.property('payload',{
|
msg.should.have.property('payload',{
|
||||||
query:{ a: 'b', c:[ 'T24,0°|H80%|W S8,3m/s' ] },
|
query:{ a: 'b', c:[ 'T24,0°|H80%|W S8,3m/s' ] },
|
||||||
url: '/getQueryParams?a=b&c%5B0%5D.Text=T24,0%C2%B0%7CH80%25%7CW%20S8,3m/s'
|
url: '/getQueryParams?a=b&c[0].Text=T24,0%C2%B0|H80%25|W%20S8,3m/s'
|
||||||
});
|
});
|
||||||
msg.should.have.property('statusCode',200);
|
msg.should.have.property('statusCode',200);
|
||||||
msg.should.have.property('headers');
|
msg.should.have.property('headers');
|
||||||
@ -1154,7 +1189,7 @@ describe('HTTP Request Node', function() {
|
|||||||
done(err);
|
done(err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
n1.receive({url: getTestURL('/getQueryParams')+"?a=b&c[0].Text=T24,0°|H80%|W S8,3m/s"});
|
n1.receive({url: getTestURL('/getQueryParams')+"?a=b&c[0].Text=T24,0°|H80%|W%20S8,3m/s"});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@ -1518,6 +1553,60 @@ describe('HTTP Request Node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should use tls-config and verify serverCert', function(done) {
|
||||||
|
var flow = [
|
||||||
|
{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getSslTestURL('/text'),tls:"n3"},
|
||||||
|
{id:"n2", type:"helper"},
|
||||||
|
{id:"n3", type:"tls-config", cert:"test/resources/ssl/server.crt", key:"test/resources/ssl/server.key", ca:"test/resources/ssl/server.crt", verifyservercert:true}];
|
||||||
|
var testNodes = [httpRequestNode, tlsNode];
|
||||||
|
helper.load(testNodes, flow, function() {
|
||||||
|
var n3 = helper.getNode("n3");
|
||||||
|
var n2 = helper.getNode("n2");
|
||||||
|
var n1 = helper.getNode("n1");
|
||||||
|
n2.on("input", function(msg) {
|
||||||
|
try {
|
||||||
|
msg.should.have.property('payload','hello');
|
||||||
|
msg.should.have.property('statusCode',200);
|
||||||
|
msg.should.have.property('headers');
|
||||||
|
msg.headers.should.have.property('content-length',''+('hello'.length));
|
||||||
|
msg.headers.should.have.property('content-type').which.startWith('text/html');
|
||||||
|
msg.should.have.property('responseUrl').which.startWith('https://');
|
||||||
|
done();
|
||||||
|
} catch(err) {
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
n1.receive({payload:"foo"});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use tls-config and send client cert', function(done) {
|
||||||
|
var flow = [
|
||||||
|
{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getSslClientTestURL('/getClientCert'),tls:"n3"},
|
||||||
|
{id:"n2", type:"helper"},
|
||||||
|
{id:"n3", type:"tls-config", cert:"test/resources/ssl/server.crt", key:"test/resources/ssl/server.key", ca:"test/resources/ssl/server.crt", verifyservercert:false}];
|
||||||
|
var testNodes = [httpRequestNode,tlsNode];
|
||||||
|
helper.load(testNodes, flow, function() {
|
||||||
|
var n3 = helper.getNode("n3");
|
||||||
|
var n2 = helper.getNode("n2");
|
||||||
|
var n1 = helper.getNode("n1");
|
||||||
|
n2.on("input", function(msg) {
|
||||||
|
try {
|
||||||
|
msg.should.have.property('payload','hello');
|
||||||
|
msg.should.have.property('statusCode',200);
|
||||||
|
msg.should.have.property('headers');
|
||||||
|
msg.headers.should.have.property('content-length',''+('hello'.length));
|
||||||
|
msg.headers.should.have.property('content-type').which.startWith('text/html');
|
||||||
|
msg.should.have.property('responseUrl').which.startWith('https://');
|
||||||
|
done();
|
||||||
|
} catch(err) {
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
n1.receive({payload:"foo"});
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
//Removing HTTP Proxy testcases as GOT + Proxy_Agent doesn't work with mock'd proxy
|
//Removing HTTP Proxy testcases as GOT + Proxy_Agent doesn't work with mock'd proxy
|
||||||
/* */
|
/* */
|
||||||
it('should use http_proxy', function(done) {
|
it('should use http_proxy', function(done) {
|
||||||
|
@ -516,6 +516,28 @@ describe('JOIN node', function() {
|
|||||||
n1.receive({payload:{a:1}});
|
n1.receive({payload:{a:1}});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('should join things into an array ignoring msg.parts.index in manual mode', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"join", wires:[["n2"]], count:3, joiner:",",mode:"custom"},
|
||||||
|
{id:"n2", type:"helper"}];
|
||||||
|
helper.load(joinNode, flow, function() {
|
||||||
|
var n1 = helper.getNode("n1");
|
||||||
|
var n2 = helper.getNode("n2");
|
||||||
|
n2.on("input", function(msg) {
|
||||||
|
try {
|
||||||
|
msg.should.have.property("payload");
|
||||||
|
msg.payload.should.be.an.Array();
|
||||||
|
msg.payload[0].should.equal(1);
|
||||||
|
msg.payload[1].should.equal(true);
|
||||||
|
//msg.payload[2].a.should.equal(1);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
catch(e) {done(e);}
|
||||||
|
});
|
||||||
|
n1.receive({payload:1, parts: {index: 3}});
|
||||||
|
n1.receive({payload:true, parts: {index: 0}});
|
||||||
|
n1.receive({payload:{a:1}, parts: {index: 9}});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should join things into an array after a count with a buffer join set', function(done) {
|
it('should join things into an array after a count with a buffer join set', function(done) {
|
||||||
var flow = [{id:"n1", type:"join", wires:[["n2"]], count:3, joinerType:"bin", joiner:"" ,mode:"custom"},
|
var flow = [{id:"n1", type:"join", wires:[["n2"]], count:3, joinerType:"bin", joiner:"" ,mode:"custom"},
|
||||||
@ -1646,7 +1668,7 @@ describe('JOIN node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle join an array when using msg.parts and duplicate indexed parts arrive', function (done) {
|
it('should handle join an array when mode is auto and duplicate indexed parts arrive', function (done) {
|
||||||
var flow = [{ id: "n1", type: "join", wires: [["n2"]], joiner: "[44]", joinerType: "bin", build: "array", mode: "auto" },
|
var flow = [{ id: "n1", type: "join", wires: [["n2"]], joiner: "[44]", joinerType: "bin", build: "array", mode: "auto" },
|
||||||
{ id: "n2", type: "helper" }];
|
{ id: "n2", type: "helper" }];
|
||||||
helper.load(joinNode, flow, function () {
|
helper.load(joinNode, flow, function () {
|
||||||
|
@ -447,4 +447,124 @@ describe('subflow', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should access env var of tab', function(done) {
|
||||||
|
var flow = [
|
||||||
|
{id:"t0", type:"tab", label:"", disabled:false, info:"", env: [
|
||||||
|
{name: "K", type: "str", value: "V"}
|
||||||
|
]},
|
||||||
|
{id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", wires:[["n2"]]},
|
||||||
|
{id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]},
|
||||||
|
// Subflow
|
||||||
|
{id:"s1", type:"subflow", name:"Subflow", info:"", env: [],
|
||||||
|
in:[{
|
||||||
|
x:10, y:10,
|
||||||
|
wires:[ {id:"s1-n1"} ]
|
||||||
|
}],
|
||||||
|
out:[{
|
||||||
|
x:10, y:10,
|
||||||
|
wires:[ {id:"s1-n1", port:0} ]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{id:"s1-n1", x:10, y:10, z:"s1", type:"function",
|
||||||
|
func:"msg.V = env.get('K'); return msg;",
|
||||||
|
wires:[]}
|
||||||
|
];
|
||||||
|
helper.load(functionNode, flow, function() {
|
||||||
|
var n1 = helper.getNode("n1");
|
||||||
|
var n2 = helper.getNode("n2");
|
||||||
|
n2.on("input", function(msg) {
|
||||||
|
try {
|
||||||
|
msg.should.have.property("V", "V");
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
done(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
n1.receive({payload:"foo"});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should access env var of group', function(done) {
|
||||||
|
var flow = [
|
||||||
|
{id:"t0", type:"tab", label:"", disabled:false, info:""},
|
||||||
|
{id:"g1", z:"t0", type:"group", env:[
|
||||||
|
{name: "K", type: "str", value: "V"}
|
||||||
|
]},
|
||||||
|
{id:"n1", x:10, y:10, z:"t0", g:"g1", type:"subflow:s1", wires:[["n2"]]},
|
||||||
|
{id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]},
|
||||||
|
// Subflow
|
||||||
|
{id:"s1", type:"subflow", name:"Subflow", info:"", env: [],
|
||||||
|
in:[{
|
||||||
|
x:10, y:10,
|
||||||
|
wires:[ {id:"s1-n1"} ]
|
||||||
|
}],
|
||||||
|
out:[{
|
||||||
|
x:10, y:10,
|
||||||
|
wires:[ {id:"s1-n1", port:0} ]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{id:"s1-n1", x:10, y:10, z:"s1", type:"function",
|
||||||
|
func:"msg.V = env.get('K'); return msg;",
|
||||||
|
wires:[]}
|
||||||
|
];
|
||||||
|
helper.load(functionNode, flow, function() {
|
||||||
|
var n1 = helper.getNode("n1");
|
||||||
|
var n2 = helper.getNode("n2");
|
||||||
|
n2.on("input", function(msg) {
|
||||||
|
try {
|
||||||
|
msg.should.have.property("V", "V");
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
done(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
n1.receive({payload:"foo"});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should access env var of nested group', function(done) {
|
||||||
|
var flow = [
|
||||||
|
{id:"t0", type:"tab", label:"", disabled:false, info:""},
|
||||||
|
{id:"g1", z:"t0", type:"group", env:[
|
||||||
|
{name: "K", type: "str", value: "V"}
|
||||||
|
]},
|
||||||
|
{id:"g2", z:"t0", g:"g1", type:"group", env:[]},
|
||||||
|
{id:"n1", x:10, y:10, z:"t0", g:"g2", type:"subflow:s1", wires:[["n2"]]},
|
||||||
|
{id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]},
|
||||||
|
// Subflow
|
||||||
|
{id:"s1", type:"subflow", name:"Subflow", info:"", env: [],
|
||||||
|
in:[{
|
||||||
|
x:10, y:10,
|
||||||
|
wires:[ {id:"s1-n1"} ]
|
||||||
|
}],
|
||||||
|
out:[{
|
||||||
|
x:10, y:10,
|
||||||
|
wires:[ {id:"s1-n1", port:0} ]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{id:"s1-n1", x:10, y:10, z:"s1", type:"function",
|
||||||
|
func:"msg.V = env.get('K'); return msg;",
|
||||||
|
wires:[]}
|
||||||
|
];
|
||||||
|
helper.load(functionNode, flow, function() {
|
||||||
|
var n1 = helper.getNode("n1");
|
||||||
|
var n2 = helper.getNode("n2");
|
||||||
|
n2.on("input", function(msg) {
|
||||||
|
try {
|
||||||
|
msg.should.have.property("V", "V");
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
done(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
n1.receive({payload:"foo"});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -41,7 +41,9 @@ describe("api/editor/theme", function () {
|
|||||||
context.should.have.a.property("page");
|
context.should.have.a.property("page");
|
||||||
context.page.should.have.a.property("title", "Node-RED");
|
context.page.should.have.a.property("title", "Node-RED");
|
||||||
context.page.should.have.a.property("favicon", "favicon.ico");
|
context.page.should.have.a.property("favicon", "favicon.ico");
|
||||||
context.page.should.have.a.property("tabicon", "red/images/node-red-icon-black.svg");
|
context.page.should.have.a.property("tabicon");
|
||||||
|
context.page.tabicon.should.have.a.property("icon", "red/images/node-red-icon-black.svg");
|
||||||
|
context.page.tabicon.should.have.a.property("colour", "#8f0000");
|
||||||
context.should.have.a.property("header");
|
context.should.have.a.property("header");
|
||||||
context.header.should.have.a.property("title", "Node-RED");
|
context.header.should.have.a.property("title", "Node-RED");
|
||||||
context.header.should.have.a.property("image", "red/images/node-red.svg");
|
context.header.should.have.a.property("image", "red/images/node-red.svg");
|
||||||
@ -58,7 +60,10 @@ describe("api/editor/theme", function () {
|
|||||||
page: {
|
page: {
|
||||||
title: "Test Page Title",
|
title: "Test Page Title",
|
||||||
favicon: "/absolute/path/to/theme/favicon",
|
favicon: "/absolute/path/to/theme/favicon",
|
||||||
tabicon: "/absolute/path/to/theme/tabicon",
|
tabicon: {
|
||||||
|
icon: "/absolute/path/to/theme/tabicon",
|
||||||
|
colour: "#8f008f"
|
||||||
|
},
|
||||||
css: "/absolute/path/to/custom/css/file.css",
|
css: "/absolute/path/to/custom/css/file.css",
|
||||||
scripts: "/absolute/path/to/script.js"
|
scripts: "/absolute/path/to/script.js"
|
||||||
},
|
},
|
||||||
@ -108,7 +113,9 @@ describe("api/editor/theme", function () {
|
|||||||
context.should.have.a.property("page");
|
context.should.have.a.property("page");
|
||||||
context.page.should.have.a.property("title", "Test Page Title");
|
context.page.should.have.a.property("title", "Test Page Title");
|
||||||
context.page.should.have.a.property("favicon", "theme/favicon/favicon");
|
context.page.should.have.a.property("favicon", "theme/favicon/favicon");
|
||||||
context.page.should.have.a.property("tabicon", "theme/tabicon/tabicon");
|
context.page.should.have.a.property("tabicon")
|
||||||
|
context.page.tabicon.should.have.a.property("icon", "theme/tabicon/tabicon");
|
||||||
|
context.page.tabicon.should.have.a.property("colour", "#8f008f")
|
||||||
context.should.have.a.property("header");
|
context.should.have.a.property("header");
|
||||||
context.header.should.have.a.property("title", "Test Header Title");
|
context.header.should.have.a.property("title", "Test Header Title");
|
||||||
context.header.should.have.a.property("url", "http://nodered.org");
|
context.header.should.have.a.property("url", "http://nodered.org");
|
||||||
@ -142,6 +149,27 @@ describe("api/editor/theme", function () {
|
|||||||
settings.projects.should.have.a.property("enabled", false);
|
settings.projects.should.have.a.property("enabled", false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("picks up backwards compatible tabicon setting", async function () {
|
||||||
|
theme.init({
|
||||||
|
editorTheme: {
|
||||||
|
page: {
|
||||||
|
tabicon: "/absolute/path/to/theme/tabicon",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
theme.app();
|
||||||
|
|
||||||
|
var context = await theme.context();
|
||||||
|
context.should.have.a.property("page");
|
||||||
|
context.page.should.have.a.property("tabicon");
|
||||||
|
context.page.tabicon.should.have.a.property("icon", "theme/tabicon/tabicon");
|
||||||
|
// The colour property should remain as default in this case as the
|
||||||
|
// legacy format for defining tabicon doesn't allow specifying a colour
|
||||||
|
context.page.tabicon.should.have.a.property("colour", "#8f0000");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
it("test explicit userMenu set to true in theme setting", function () {
|
it("test explicit userMenu set to true in theme setting", function () {
|
||||||
theme.init({
|
theme.init({
|
||||||
editorTheme: {
|
editorTheme: {
|
||||||
|
@ -1228,4 +1228,54 @@ describe('Flow', function() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("#env", function () {
|
||||||
|
it("can instantiate a node with environment variable property values of group and tab", function (done) {
|
||||||
|
try {
|
||||||
|
after(function() {
|
||||||
|
delete process.env.V0;
|
||||||
|
delete process.env.V1;
|
||||||
|
})
|
||||||
|
process.env.V0 = "gv0";
|
||||||
|
process.env.V1 = "gv1";
|
||||||
|
var config = flowUtils.parseConfig([
|
||||||
|
{id:"t1",type:"tab",env:[
|
||||||
|
{"name": "V0", value: "v0", type: "str"}
|
||||||
|
]},
|
||||||
|
{id:"g1",type:"group",z:"t1",env:[
|
||||||
|
{"name": "V0", value: "v1", type: "str"},
|
||||||
|
{"name": "V1", value: "v2", type: "str"}
|
||||||
|
]},
|
||||||
|
{id:"g2",type:"group",z:"t1",g:"g1",env:[
|
||||||
|
{"name": "V1", value: "v3", type: "str"}
|
||||||
|
]},
|
||||||
|
{id:"1",x:10,y:10,z:"t1",type:"test",foo:"$(V0)",wires:[]},
|
||||||
|
{id:"2",x:10,y:10,z:"t1",g:"g1",type:"test",foo:"$(V0)",wires:[]},
|
||||||
|
{id:"3",x:10,y:10,z:"t1",g:"g1",type:"test",foo:"$(V1)",wires:[]},
|
||||||
|
{id:"4",x:10,y:10,z:"t1",g:"g2",type:"test",foo:"$(V1)",wires:[]},
|
||||||
|
{id:"5",x:10,y:10,z:"t1",type:"test",foo:"$(V1)",wires:[]},
|
||||||
|
]);
|
||||||
|
var flow = Flow.create({getSetting:v=>process.env[v]},config,config.flows["t1"]);
|
||||||
|
flow.start();
|
||||||
|
|
||||||
|
var activeNodes = flow.getActiveNodes();
|
||||||
|
|
||||||
|
activeNodes["1"].foo.should.equal("v0");
|
||||||
|
activeNodes["2"].foo.should.equal("v1");
|
||||||
|
activeNodes["3"].foo.should.equal("v2");
|
||||||
|
activeNodes["4"].foo.should.equal("v3");
|
||||||
|
activeNodes["5"].foo.should.equal("gv1");
|
||||||
|
|
||||||
|
flow.stop().then(function() {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.log(e.stack);
|
||||||
|
done(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user