From 9e5218f6b4b14a3b95d7091808bd68ecc544f614 Mon Sep 17 00:00:00 2001 From: Mauricio Bonani Date: Tue, 10 Aug 2021 14:21:54 -0400 Subject: [PATCH 01/15] Place close tab link in front of fade --- .../@node-red/editor-client/src/js/ui/common/tabs.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js index 0dfb72ffd..41c6e704c 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js @@ -664,6 +664,8 @@ RED.tabs = (function() { link.on("dblclick", function(evt) { evt.stopPropagation(); evt.preventDefault(); }) + $('').appendTo(li); + if (tab.closeable) { li.addClass("red-ui-tabs-closeable") var closeLink = $("",{href:"#",class:"red-ui-tab-close"}).appendTo(li); @@ -674,8 +676,6 @@ RED.tabs = (function() { }); } - $('').appendTo(li); - var badges = $('').appendTo(li); if (options.onselect) { $('').appendTo(badges); From e93734b209a8986d44daeaf30e89226b68c57dfb Mon Sep 17 00:00:00 2001 From: Mauricio Bonani Date: Tue, 10 Aug 2021 14:26:41 -0400 Subject: [PATCH 02/15] Change fade color when hovering an inactive tab --- .../node_modules/@node-red/editor-client/src/sass/tabs.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/node_modules/@node-red/editor-client/src/sass/tabs.scss b/packages/node_modules/@node-red/editor-client/src/sass/tabs.scss index 662aaa6ae..0b038932a 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/tabs.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/tabs.scss @@ -131,6 +131,9 @@ &:not(.active) a:hover { color: $workspace-button-color-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); + } } } } From 7f2627dbc8e999c987c536205868af56b587b5e8 Mon Sep 17 00:00:00 2001 From: bartbutenaers Date: Sat, 21 Aug 2021 22:21:58 +0200 Subject: [PATCH 03/15] Key and certificate as string or buffer --- packages/node_modules/node-red/red.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/node-red/red.js b/packages/node_modules/node-red/red.js index 2b934fb56..5c40d7794 100755 --- a/packages/node_modules/node-red/red.js +++ b/packages/node_modules/node-red/red.js @@ -234,8 +234,13 @@ httpsPromise.then(function(startupHttps) { // Get the result of the function, because createServer doesn't accept functions as input Promise.resolve(settings.https()).then(function(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 - if(!server.key || !server.cert || !server.key.equals(refreshedHttps.key) || !server.cert.equals(refreshedHttps.cert)) { + if(updateKey || updateCert) { server.setSecureContext(refreshedHttps); RED.log.info(RED.log._("server.https.settings-refreshed")); } From 630d2ca926bbd5fab3b4cea79e5030d010711129 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Mon, 23 Aug 2021 15:52:49 +0200 Subject: [PATCH 04/15] Give passport verfiy callback wrapper the same arity as the original callback passed in via options --- .../@node-red/editor-api/lib/auth/index.js | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/packages/node_modules/@node-red/editor-api/lib/auth/index.js b/packages/node_modules/@node-red/editor-api/lib/auth/index.js index fb95ede7b..5552301d0 100644 --- a/packages/node_modules/@node-red/editor-api/lib/auth/index.js +++ b/packages/node_modules/@node-red/editor-api/lib/auth/index.js @@ -173,27 +173,30 @@ function genericStrategy(adminApp,strategy) { adminApp.use(passport.session()); var options = strategy.options; + var verify = function() { + var originalDone = arguments[arguments.length-1]; + if (options.verify) { + var args = Array.from(arguments); + args[args.length-1] = function(err,profile) { + if (err) { + return originalDone(err); + } else { + return completeVerify(profile,originalDone); + } + }; - passport.use(new strategy.strategy(options, - function() { - var originalDone = arguments[arguments.length-1]; - if (options.verify) { - var args = Array.from(arguments); - args[args.length-1] = function(err,profile) { - if (err) { - return originalDone(err); - } else { - return completeVerify(profile,originalDone); - } - }; - options.verify.apply(null,args); - } else { - var profile = arguments[arguments.length - 2]; - return completeVerify(profile,originalDone); - } - + options.verify.apply(null,args); + } else { + var profile = arguments[arguments.length - 2]; + 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', passport.authenticate(strategy.name, {session:false, failureRedirect: settings.httpAdminRoot }), From e13133fd2b4ff1e0cfffbf8f153435f767430956 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 16 Aug 2021 21:21:19 +0100 Subject: [PATCH 05/15] Add align actions to editor --- .../editor-client/src/js/keymap.json | 8 +- .../editor-client/src/js/ui/view-tools.js | 109 ++++++++++++++++++ 2 files changed, 116 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/keymap.json b/packages/node_modules/@node-red/editor-client/src/js/keymap.json index c323b3e64..b9459e681 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/keymap.json +++ b/packages/node_modules/@node-red/editor-client/src/js/keymap.json @@ -77,6 +77,12 @@ "right": "core:go-to-nearest-node-on-right", "left": "core:go-to-nearest-node-on-left", "up": "core:go-to-nearest-node-above", - "down": "core:go-to-nearest-node-below" + "down": "core:go-to-nearest-node-below", + "alt-a l": "core:align-nodes-to-left", + "alt-a r": "core:align-nodes-to-right", + "alt-a t": "core:align-nodes-to-top", + "alt-a b": "core:align-nodes-to-bottom", + "alt-a m": "core:align-nodes-to-middle", + "alt-a c": "core:align-nodes-to-center" } } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js index 6c3889832..001155519 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js @@ -427,8 +427,107 @@ 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); + } + } } @@ -474,6 +573,16 @@ 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-above", function() { gotoNearestNode('up') }) RED.actions.add("core:go-to-nearest-node-below", function() { gotoNearestNode('down') }) + + RED.actions.add("core:align-nodes-to-left", function() { alignSelectionToEdge('left') }) + RED.actions.add("core:align-nodes-to-right", function() { alignSelectionToEdge('right') }) + RED.actions.add("core:align-nodes-to-top", function() { alignSelectionToEdge('top') }) + RED.actions.add("core:align-nodes-to-bottom", function() { alignSelectionToEdge('bottom') }) + RED.actions.add("core:align-nodes-to-middle", function() { alignSelectionToEdge('middle') }) + RED.actions.add("core:align-nodes-to-center", function() { alignSelectionToEdge('center') }) + + + // RED.actions.add("core:add-node", function() { addNode() }) }, /** From 56121203bfdacf8f39f568b24b54b8105ca118c5 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 16 Aug 2021 21:34:30 +0100 Subject: [PATCH 06/15] Rename align actions to match existing align-selection-to-grid --- .../@node-red/editor-client/src/js/keymap.json | 13 +++++++------ .../editor-client/src/js/ui/view-tools.js | 15 +++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/keymap.json b/packages/node_modules/@node-red/editor-client/src/js/keymap.json index b9459e681..621fdaf41 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/keymap.json +++ b/packages/node_modules/@node-red/editor-client/src/js/keymap.json @@ -78,11 +78,12 @@ "left": "core:go-to-nearest-node-on-left", "up": "core:go-to-nearest-node-above", "down": "core:go-to-nearest-node-below", - "alt-a l": "core:align-nodes-to-left", - "alt-a r": "core:align-nodes-to-right", - "alt-a t": "core:align-nodes-to-top", - "alt-a b": "core:align-nodes-to-bottom", - "alt-a m": "core:align-nodes-to-middle", - "alt-a c": "core:align-nodes-to-center" + "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" } } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js index 001155519..367e173fd 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js @@ -536,8 +536,6 @@ RED.view.tools = (function() { 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: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-right", function() { RED.view.scroll(RED.view.gridSize(),0);}); RED.actions.add("core:scroll-view-down", function() { RED.view.scroll(0,RED.view.gridSize());}); @@ -574,12 +572,13 @@ RED.view.tools = (function() { 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:align-nodes-to-left", function() { alignSelectionToEdge('left') }) - RED.actions.add("core:align-nodes-to-right", function() { alignSelectionToEdge('right') }) - RED.actions.add("core:align-nodes-to-top", function() { alignSelectionToEdge('top') }) - RED.actions.add("core:align-nodes-to-bottom", function() { alignSelectionToEdge('bottom') }) - RED.actions.add("core:align-nodes-to-middle", function() { alignSelectionToEdge('middle') }) - RED.actions.add("core:align-nodes-to-center", function() { alignSelectionToEdge('center') }) + 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') }) From d2cdc67ec7317e21042f952aa084f5ca4a31d8b5 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 23 Aug 2021 21:29:21 +0100 Subject: [PATCH 07/15] Add distribute-horizontally/vertically actions to editor --- .../editor-client/src/js/keymap.json | 4 +- .../editor-client/src/js/ui/view-tools.js | 161 ++++++++++++++++++ 2 files changed, 164 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/keymap.json b/packages/node_modules/@node-red/editor-client/src/js/keymap.json index 621fdaf41..c50508389 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/keymap.json +++ b/packages/node_modules/@node-red/editor-client/src/js/keymap.json @@ -84,6 +84,8 @@ "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 c": "core:align-selection-to-center", + "alt-a h": "core:distribute-selection-horizontally", + "alt-a v": "core:distribute-selection-vertically" } } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js index 367e173fd..ede8d3442 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js @@ -531,6 +531,165 @@ RED.view.tools = (function() { } + 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); + } + } + } + + return { init: function() { RED.actions.add("core:show-selected-node-labels", function() { setSelectedNodeLabelState(true); }) @@ -580,6 +739,8 @@ RED.view.tools = (function() { 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() }) From 8e89b1bdf2db6365b6dca4d66fd3c65b455823db Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 27 Aug 2021 16:22:58 +0100 Subject: [PATCH 08/15] Fix typo in ko editor.json Fixes #3119 --- .../node_modules/@node-red/editor-client/locales/ko/editor.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/locales/ko/editor.json b/packages/node_modules/@node-red/editor-client/locales/ko/editor.json index 3c12e116a..9e0c74ea8 100755 --- a/packages/node_modules/@node-red/editor-client/locales/ko/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/ko/editor.json @@ -56,7 +56,7 @@ "displayConfig": "설정노드 보기", "import": "가져오기", "export": "내보내기", - "search": "플로우 겅색", + "search": "플로우 검색", "searchInput": "플로우 검색", "subflows": "보조 플로우", "createSubflow": "보조 플로우 생성", From 26d83bb9eaa762165c14970eb8bfb1fbc04dab73 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 27 Aug 2021 16:32:43 +0100 Subject: [PATCH 09/15] Ensure treeList row has suitable min-height when no content Fixes #3109 --- .../@node-red/editor-client/src/sass/ui/common/treeList.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/node_modules/@node-red/editor-client/src/sass/ui/common/treeList.scss b/packages/node_modules/@node-red/editor-client/src/sass/ui/common/treeList.scss index 54e37b57c..a89fe1b84 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/ui/common/treeList.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/ui/common/treeList.scss @@ -101,6 +101,9 @@ } .red-ui-treeList-label-text { margin-left: 4px; + &:empty { + min-height: 20px; + } } .red-ui-treeList-sublabel-text { top: 0; From 78899378c2f9e13b37c5f555ed7b5b97bc5d1e9b Mon Sep 17 00:00:00 2001 From: Amit Kumar Swami Date: Thu, 2 Sep 2021 13:19:40 +0800 Subject: [PATCH 10/15] Update tar to 6.1.9 to resolve vulnerability issue --- package.json | 2 +- packages/node_modules/@node-red/registry/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 2296ad220..33dd27ca7 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "passport-oauth2-client-password": "0.1.2", "raw-body": "2.4.1", "semver": "7.3.5", - "tar": "6.1.2", + "tar": "6.1.9", "tough-cookie": "4.0.0", "uglify-js": "3.13.10", "uuid": "8.3.2", diff --git a/packages/node_modules/@node-red/registry/package.json b/packages/node_modules/@node-red/registry/package.json index 999352647..2513b8f33 100644 --- a/packages/node_modules/@node-red/registry/package.json +++ b/packages/node_modules/@node-red/registry/package.json @@ -20,7 +20,7 @@ "clone": "2.1.2", "fs-extra": "10.0.0", "semver": "7.3.5", - "tar": "6.1.2", + "tar": "6.1.9", "uglify-js": "3.13.10" } } From ee15e9acc54d7ebef65dc4d20a17d35c328cff43 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 2 Sep 2021 08:21:10 +0100 Subject: [PATCH 11/15] Update tar to latest --- package.json | 2 +- packages/node_modules/@node-red/registry/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 33dd27ca7..b133d1f06 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "passport-oauth2-client-password": "0.1.2", "raw-body": "2.4.1", "semver": "7.3.5", - "tar": "6.1.9", + "tar": "6.1.11", "tough-cookie": "4.0.0", "uglify-js": "3.13.10", "uuid": "8.3.2", diff --git a/packages/node_modules/@node-red/registry/package.json b/packages/node_modules/@node-red/registry/package.json index 2513b8f33..6e3162b65 100644 --- a/packages/node_modules/@node-red/registry/package.json +++ b/packages/node_modules/@node-red/registry/package.json @@ -20,7 +20,7 @@ "clone": "2.1.2", "fs-extra": "10.0.0", "semver": "7.3.5", - "tar": "6.1.9", + "tar": "6.1.11", "uglify-js": "3.13.10" } } From 3b460fb8faedbacb588efb9b312c6c7bcffe708e Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 2 Sep 2021 09:00:54 +0100 Subject: [PATCH 12/15] Bump for 2.0.6 --- CHANGELOG.md | 14 +++++++++++ package.json | 24 +++++++++---------- .../@node-red/editor-api/package.json | 8 +++---- .../@node-red/editor-client/package.json | 2 +- .../node_modules/@node-red/nodes/package.json | 6 ++--- .../@node-red/registry/package.json | 6 ++--- .../@node-red/runtime/package.json | 8 +++---- .../node_modules/@node-red/util/package.json | 4 ++-- packages/node_modules/node-red/package.json | 10 ++++---- 9 files changed, 48 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54e98ba74..d442405b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +#### 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 diff --git a/package.json b/package.json index b133d1f06..d52055bc4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red", - "version": "2.0.5", + "version": "2.0.6", "description": "Low-code programming for event-driven applications", "homepage": "http://nodered.org", "license": "Apache-2.0", @@ -28,8 +28,8 @@ "dependencies": { "acorn": "8.4.1", "acorn-walk": "8.1.1", - "ajv": "8.6.0", - "async-mutex": "0.3.1", + "ajv": "8.6.2", + "async-mutex": "0.3.2", "basic-auth": "2.0.1", "bcryptjs": "2.4.3", "body-parser": "1.19.0", @@ -55,14 +55,14 @@ "is-utf8": "0.2.1", "js-yaml": "3.14.1", "json-stringify-safe": "5.0.1", - "jsonata": "1.8.4", + "jsonata": "1.8.5", "lodash.clonedeep": "^4.5.0", "media-typer": "1.1.0", "memorystore": "1.6.6", "mime": "2.5.2", "moment-timezone": "0.5.33", "mqtt": "4.2.8", - "multer": "1.4.2", + "multer": "1.4.3", "mustache": "4.2.0", "node-red-admin": "^2.2.0", "nopt": "5.0.0", @@ -75,7 +75,7 @@ "semver": "7.3.5", "tar": "6.1.11", "tough-cookie": "4.0.0", - "uglify-js": "3.13.10", + "uglify-js": "3.14.1", "uuid": "8.3.2", "ws": "7.5.1", "xml2js": "0.4.23" @@ -84,7 +84,7 @@ "bcrypt": "5.0.1" }, "devDependencies": { - "dompurify": "2.2.9", + "dompurify": "2.3.1", "grunt": "1.4.1", "grunt-chmod": "~1.1.1", "grunt-cli": "~1.4.3", @@ -109,15 +109,15 @@ "jsdoc-nr-template": "github:node-red/jsdoc-nr-template", "marked": "2.1.3", "minami": "1.2.3", - "mocha": "9.0.1", + "mocha": "9.1.1", "node-red-node-test-helper": "^0.2.7", - "nodemon": "2.0.8", + "nodemon": "2.0.12", "proxy": "^1.0.2", - "sass": "1.35.1", + "sass": "1.39.0", "should": "13.2.3", - "sinon": "11.1.1", + "sinon": "11.1.2", "stoppable": "^1.1.0", - "supertest": "6.1.3" + "supertest": "6.1.6" }, "engines": { "node": ">=12" diff --git a/packages/node_modules/@node-red/editor-api/package.json b/packages/node_modules/@node-red/editor-api/package.json index d3c342cba..73f512538 100644 --- a/packages/node_modules/@node-red/editor-api/package.json +++ b/packages/node_modules/@node-red/editor-api/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/editor-api", - "version": "2.0.5", + "version": "2.0.6", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,8 +16,8 @@ } ], "dependencies": { - "@node-red/util": "2.0.5", - "@node-red/editor-client": "2.0.5", + "@node-red/util": "2.0.6", + "@node-red/editor-client": "2.0.6", "bcryptjs": "2.4.3", "body-parser": "1.19.0", "clone": "2.1.2", @@ -26,7 +26,7 @@ "express": "4.17.1", "memorystore": "1.6.6", "mime": "2.5.2", - "multer": "1.4.2", + "multer": "1.4.3", "mustache": "4.2.0", "oauth2orize": "1.11.0", "passport-http-bearer": "1.0.1", diff --git a/packages/node_modules/@node-red/editor-client/package.json b/packages/node_modules/@node-red/editor-client/package.json index ee3f1d8ad..fc315b61d 100644 --- a/packages/node_modules/@node-red/editor-client/package.json +++ b/packages/node_modules/@node-red/editor-client/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/editor-client", - "version": "2.0.5", + "version": "2.0.6", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/@node-red/nodes/package.json b/packages/node_modules/@node-red/nodes/package.json index 540c97734..e082f3c73 100644 --- a/packages/node_modules/@node-red/nodes/package.json +++ b/packages/node_modules/@node-red/nodes/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/nodes", - "version": "2.0.5", + "version": "2.0.6", "license": "Apache-2.0", "repository": { "type": "git", @@ -17,7 +17,7 @@ "dependencies": { "acorn": "8.4.1", "acorn-walk": "8.1.1", - "ajv": "8.6.0", + "ajv": "8.6.2", "body-parser": "1.19.0", "cheerio": "1.0.0-rc.10", "content-type": "1.0.4", @@ -37,7 +37,7 @@ "js-yaml": "3.14.1", "media-typer": "1.1.0", "mqtt": "4.2.8", - "multer": "1.4.2", + "multer": "1.4.3", "mustache": "4.2.0", "on-headers": "1.0.2", "raw-body": "2.4.1", diff --git a/packages/node_modules/@node-red/registry/package.json b/packages/node_modules/@node-red/registry/package.json index 6e3162b65..d42cfd4ae 100644 --- a/packages/node_modules/@node-red/registry/package.json +++ b/packages/node_modules/@node-red/registry/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/registry", - "version": "2.0.5", + "version": "2.0.6", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,11 +16,11 @@ } ], "dependencies": { - "@node-red/util": "2.0.5", + "@node-red/util": "2.0.6", "clone": "2.1.2", "fs-extra": "10.0.0", "semver": "7.3.5", "tar": "6.1.11", - "uglify-js": "3.13.10" + "uglify-js": "3.14.1" } } diff --git a/packages/node_modules/@node-red/runtime/package.json b/packages/node_modules/@node-red/runtime/package.json index 4925ebb8a..e2a288862 100644 --- a/packages/node_modules/@node-red/runtime/package.json +++ b/packages/node_modules/@node-red/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/runtime", - "version": "2.0.5", + "version": "2.0.6", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,9 +16,9 @@ } ], "dependencies": { - "@node-red/registry": "2.0.5", - "@node-red/util": "2.0.5", - "async-mutex": "0.3.1", + "@node-red/registry": "2.0.6", + "@node-red/util": "2.0.6", + "async-mutex": "0.3.2", "clone": "2.1.2", "express": "4.17.1", "fs-extra": "10.0.0", diff --git a/packages/node_modules/@node-red/util/package.json b/packages/node_modules/@node-red/util/package.json index 8a569238c..d76c9f450 100644 --- a/packages/node_modules/@node-red/util/package.json +++ b/packages/node_modules/@node-red/util/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/util", - "version": "2.0.5", + "version": "2.0.6", "license": "Apache-2.0", "repository": { "type": "git", @@ -18,7 +18,7 @@ "fs-extra": "10.0.0", "i18next": "20.3.2", "json-stringify-safe": "5.0.1", - "jsonata": "1.8.4", + "jsonata": "1.8.5", "lodash.clonedeep": "^4.5.0", "moment-timezone": "0.5.33" } diff --git a/packages/node_modules/node-red/package.json b/packages/node_modules/node-red/package.json index c70c08451..2543d896f 100644 --- a/packages/node_modules/node-red/package.json +++ b/packages/node_modules/node-red/package.json @@ -1,6 +1,6 @@ { "name": "node-red", - "version": "2.0.5", + "version": "2.0.6", "description": "Low-code programming for event-driven applications", "homepage": "http://nodered.org", "license": "Apache-2.0", @@ -31,10 +31,10 @@ "flow" ], "dependencies": { - "@node-red/editor-api": "2.0.5", - "@node-red/runtime": "2.0.5", - "@node-red/util": "2.0.5", - "@node-red/nodes": "2.0.5", + "@node-red/editor-api": "2.0.6", + "@node-red/runtime": "2.0.6", + "@node-red/util": "2.0.6", + "@node-red/nodes": "2.0.6", "basic-auth": "2.0.1", "bcryptjs": "2.4.3", "express": "4.17.1", From 192b542fe4223f0fd0d7ecf3eb558bcc3084827c Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 7 Sep 2021 11:47:04 +0100 Subject: [PATCH 13/15] Add menu options for align tools and show shortcuts on menu --- .../editor-client/locales/en-US/editor.json | 21 +++++++++--- .../@node-red/editor-client/src/js/history.js | 8 +++++ .../@node-red/editor-client/src/js/red.js | 33 +++++++++++++++++-- .../editor-client/src/js/ui/common/menu.js | 26 ++++++++++++++- .../editor-client/src/js/ui/group.js | 5 +++ .../editor-client/src/js/ui/keyboard.js | 6 +++- .../@node-red/editor-client/src/js/ui/view.js | 9 +++++ .../editor-client/src/sass/dropdownMenu.scss | 14 +++++++- .../editor-client/src/sass/header.scss | 7 ++-- 9 files changed, 118 insertions(+), 11 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json index 3f1e32588..adeae0c2d 100755 --- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json @@ -116,7 +116,16 @@ "groupSelection": "Group selection", "ungroupSelection": "Ungroup 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" } }, "actions": { @@ -450,8 +459,9 @@ "unassigned": "Unassigned", "global": "global", "workspace": "workspace", - "selectAll": "Select all nodes", - "selectAllConnected": "Select all connected nodes", + "selectAll": "Select all", + "selectNone": "Select none", + "selectAllConnected": "Select connected", "addRemoveNode": "Add/remove node from selection", "editSelected": "Edit selected node", "deleteSelected": "Delete selected nodes or link", @@ -464,7 +474,10 @@ "copyNode": "Copy selected nodes", "cutNode": "Cut selected 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", "managePalette": "Manage palette", "actionList":"Action list" diff --git a/packages/node_modules/@node-red/editor-client/src/js/history.js b/packages/node_modules/@node-red/editor-client/src/js/history.js index 338d955e1..21823ae26 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/history.js +++ b/packages/node_modules/@node-red/editor-client/src/js/history.js @@ -658,6 +658,8 @@ RED.history = (function() { push: function(ev) { undoHistory.push(ev); redoHistory = []; + RED.menu.setDisabled("menu-item-edit-undo", false); + RED.menu.setDisabled("menu-item-edit-redo", true); }, pop: function() { var ev = undoHistory.pop(); @@ -665,6 +667,8 @@ RED.history = (function() { if (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() { return undoHistory[undoHistory.length-1]; @@ -672,6 +676,8 @@ RED.history = (function() { clear: function() { undoHistory = []; redoHistory = []; + RED.menu.setDisabled("menu-item-edit-undo", true); + RED.menu.setDisabled("menu-item-edit-redo", true); }, redo: function() { var ev = redoHistory.pop(); @@ -681,6 +687,8 @@ RED.history = (function() { undoHistory.push(uev); } } + RED.menu.setDisabled("menu-item-edit-undo", undoHistory.length === 0); + RED.menu.setDisabled("menu-item-edit-redo", redoHistory.length === 0); } } diff --git a/packages/node_modules/@node-red/editor-client/src/js/red.js b/packages/node_modules/@node-red/editor-client/src/js/red.js index 1a7d9b08f..171e3124b 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/red.js +++ b/packages/node_modules/@node-red/editor-client/src/js/red.js @@ -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"} ]}); } + 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:[ {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}, @@ -566,6 +582,20 @@ var RED = (function() { {id:"menu-item-action-list",label:RED._("keyboard.actionList"),onselect:"core:show-action-list"}, null ]}); + + menuOptions.push({id:"menu-item-arrange-menu", label:RED._("menu.label.arrange"), options: [ + {id: "menu-item-view-tools-align-left", label:RED._("menu.label.alignLeft"), onselect: "core:align-selection-to-left"}, + {id: "menu-item-view-tools-align-center", label:RED._("menu.label.alignCenter"), onselect: "core:align-selection-to-center"}, + {id: "menu-item-view-tools-align-right", label:RED._("menu.label.alignRight"), onselect: "core:align-selection-to-right"}, + null, + {id: "menu-item-view-tools-align-top", label:RED._("menu.label.alignTop"), onselect: "core:align-selection-to-top"}, + {id: "menu-item-view-tools-align-middle", label:RED._("menu.label.alignMiddle"), onselect: "core:align-selection-to-middle"}, + {id: "menu-item-view-tools-align-bottom", label:RED._("menu.label.alignBottom"), onselect: "core:align-selection-to-bottom"}, + null, + {id: "menu-item-view-tools-distribute-horizontally", label:RED._("menu.label.distributeHorizontally"), onselect: "core:distribute-selection-horizontally"}, + {id: "menu-item-view-tools-distribute-veritcally", label:RED._("menu.label.distributeVertically"), onselect: "core:distribute-selection-vertically"} + ]}); + menuOptions.push(null); 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"}); @@ -626,7 +656,6 @@ var RED = (function() { RED.user.init(); RED.notifications.init(); RED.library.init(); - RED.keyboard.init(); RED.palette.init(); RED.eventLog.init(); @@ -655,7 +684,7 @@ var RED = (function() { RED.deploy.init(RED.settings.theme("deployButton",null)); - buildMainMenu(); + RED.keyboard.init(buildMainMenu); RED.nodes.init(); RED.comms.connect(); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/menu.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/menu.js index 7ebe659c9..e8c678333 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/menu.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/menu.js @@ -88,6 +88,13 @@ RED.menu = (function() { linkContent += ''; 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 = $(''+RED.keyboard.formatKey(shortcut.key, true)+'').appendTo(link); + } + } 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 = $(''+RED.keyboard.formatKey(shortcut.key, true)+'').appendTo(opt.link); + } + } + } + } + } + return { init: createMenu, setSelected: setSelected, @@ -284,6 +307,7 @@ RED.menu = (function() { setDisabled: setDisabled, addItem: addItem, removeItem: removeItem, - setAction: setAction + setAction: setAction, + refreshShortcuts: refreshShortcuts } })(); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/group.js b/packages/node_modules/@node-red/editor-client/src/js/ui/group.js index 1c86eb631..6e4ff98f5 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/group.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/group.js @@ -183,7 +183,9 @@ RED.group = (function() { var activateUngroup = false; var activateMerge = false; var activateRemove = false; + var singleGroupSelected = false; if (activateGroup) { + singleGroupSelected = selection.nodes.length === 1 && selection.nodes[0].type === 'group'; selection.nodes.forEach(function (n) { if (n.type === "group") { activateUngroup = true; @@ -200,6 +202,8 @@ RED.group = (function() { RED.menu.setDisabled("menu-item-group-ungroup", !activateUngroup); RED.menu.setDisabled("menu-item-group-merge", !activateMerge); 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() }) @@ -252,6 +256,7 @@ RED.group = (function() { if (selection.nodes && selection.nodes.length === 1 && selection.nodes[0].type === 'group') { groupStyleClipboard = JSON.parse(JSON.stringify(selection.nodes[0].style)); RED.notify(RED._("clipboard.groupStyleCopied"),{id:"clipboard"}) + RED.menu.setDisabled("menu-item-edit-paste-group-style", false) } } function pasteGroupStyle() { diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/keyboard.js b/packages/node_modules/@node-red/editor-client/src/js/ui/keyboard.js index 9e976e4fc..7a38a290d 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/keyboard.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/keyboard.js @@ -131,7 +131,7 @@ RED.keyboard = (function() { return mergedKeymap; } - function init() { + function init(done) { // Migrate from pre-0.18 migrateOldKeymap(); @@ -164,6 +164,7 @@ RED.keyboard = (function() { } } } + done(); }); RED.userSettings.add({ @@ -174,6 +175,9 @@ RED.keyboard = (function() { setTimeout(function() { $("#red-ui-settings-tab-keyboard-filter").trigger("focus"); },200); + }, + close: function() { + RED.menu.refreshShortcuts(); } }) } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index afbf5f5f7..51f4428f6 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -501,6 +501,14 @@ RED.view = (function() { 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: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); + 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.actions.add("core:delete-selection",deleteSelection); RED.actions.add("core:edit-selected-node",editSelection); RED.actions.add("core:go-to-selection",function() { @@ -2332,6 +2340,7 @@ RED.view = (function() { } } clipboard = JSON.stringify(nns); + RED.menu.setDisabled("menu-item-edit-paste", false); if (nodeCount > 0) { RED.notify(RED._("clipboard.nodeCopied",{count:nodeCount}),{id:"clipboard"}); } else if (groupCount > 0) { diff --git a/packages/node_modules/@node-red/editor-client/src/sass/dropdownMenu.scss b/packages/node_modules/@node-red/editor-client/src/sass/dropdownMenu.scss index bb91846bb..ca19afc73 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/dropdownMenu.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/dropdownMenu.scss @@ -68,6 +68,10 @@ & > .disabled > a:hover, & > .disabled > a:focus { color: $menuDisabledColor; + .red-ui-popover-key { + color: $menuDisabledColor; + border-color: $menuDisabledColor; + } } & > .disabled > a:hover, @@ -102,6 +106,14 @@ display: none; } } + .red-ui-popover-key { + border: none; + padding: 0; + font-size: 13px; + float: right; + color: $menuColor; + border-color: $menuColor; + } } } @@ -209,4 +221,4 @@ ul.red-ui-menu:not(.red-ui-menu-dropdown) { } } -} \ No newline at end of file +} diff --git a/packages/node_modules/@node-red/editor-client/src/sass/header.scss b/packages/node_modules/@node-red/editor-client/src/sass/header.scss index 4093e9bca..02528a446 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/header.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/header.scss @@ -191,14 +191,17 @@ margin-top: 0; li a { color: $header-menu-color; - padding: 3px 40px; + padding: 3px 10px 3px 40px; img { max-width: 100%; margin-right: 10px; padding: 4px; border: 3px solid transparent; } - + .red-ui-popover-key { + color: $header-menu-color-disabled !important; + border-color: $header-menu-color-disabled !important; + } &.active img { border: 3px solid $header-menu-item-border-active; } From 4132fb79a661aaed78560001415959c285bc4d3c Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 26 Aug 2021 21:16:40 +0100 Subject: [PATCH 14/15] Allow nodes to be raised/lowered in the workspace --- .../@node-red/editor-client/src/js/history.js | 17 +- .../@node-red/editor-client/src/js/nodes.js | 456 +++++++++++++----- .../src/js/ui/tab-info-outliner.js | 16 + .../editor-client/src/js/ui/view-tools.js | 42 ++ .../@node-red/editor-client/src/js/ui/view.js | 28 +- .../editor-client/src/js/ui/workspaces.js | 9 +- 6 files changed, 450 insertions(+), 118 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/history.js b/packages/node_modules/@node-red/editor-client/src/js/history.js index 21823ae26..256500200 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/history.js +++ b/packages/node_modules/@node-red/editor-client/src/js/history.js @@ -558,11 +558,22 @@ RED.history = (function() { } else if (ev.t == "reorder") { inverseEv = { t: 'reorder', - order: RED.nodes.getWorkspaceOrder(), dirty: RED.nodes.dirty() }; - if (ev.order) { - RED.workspaces.order(ev.order); + if (ev.workspaces) { + 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") { inverseEv = { diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index ee3542eee..df95c6392 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -16,8 +16,6 @@ RED.nodes = (function() { var node_defs = {}; - var nodes = {}; - var nodeTabMap = {}; var linkTabMap = {}; var configNodes = {}; @@ -41,6 +39,7 @@ RED.nodes = (function() { RED.events.emit("workspace:dirty",{dirty:dirty}); } + // The registry holds information about all node types. var registry = (function() { var moduleList = {}; var nodeList = []; @@ -209,6 +208,279 @@ RED.nodes = (function() { 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 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.moveNodesBacks(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 { init: function() { @@ -710,6 +745,12 @@ RED.view.tools = (function() { 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-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-right", function() { moveSelection(RED.view.gridSize(),0);}); RED.actions.add("core:step-selection-down", function() { moveSelection(0,RED.view.gridSize());}); @@ -743,6 +784,7 @@ RED.view.tools = (function() { RED.actions.add("core:distribute-selection-vertically", function() { distributeSelection('v') }) + // RED.actions.add("core:add-node", function() { addNode() }) }, /** diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index 51f4428f6..40d8af2e7 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -663,14 +663,17 @@ RED.view = (function() { var activeWorkspace = RED.workspaces.active(); activeNodes = RED.nodes.filterNodes({z:activeWorkspace}); - + activeNodes.forEach(function(n,i) { + n._index = i; + }) activeLinks = RED.nodes.filterLinks({ source:{z:activeWorkspace}, target:{z:activeWorkspace} }); activeGroups = RED.nodes.groups(activeWorkspace)||[]; - activeGroups.forEach(function(g) { + activeGroups.forEach(function(g,i) { + g._index = i; if (g.g) { g._root = g.g; g._depth = 1; @@ -703,7 +706,8 @@ RED.view = (function() { if (a._root === b._root) { return a._depth - b._depth; } else { - return a._root.localeCompare(b._root); + // return a._root.localeCompare(b._root); + return a._index - b._index; } }); @@ -712,7 +716,8 @@ RED.view = (function() { if (a._root === b._root) { return a._depth - b._depth; } else { - return a._root.localeCompare(b._root); + return a._index - b._index; + // return a._root.localeCompare(b._root); } }) } @@ -3816,7 +3821,6 @@ RED.view = (function() { .attr("class", "red-ui-flow-node red-ui-flow-node-group") .classed("red-ui-flow-subflow", activeSubflow != null); - nodeEnter.each(function(d,i) { this.__outputs__ = []; this.__inputs__ = []; @@ -3962,7 +3966,12 @@ RED.view = (function() { RED.hooks.trigger("viewAddNode",{node:d,el:this}) }); + var nodesReordered = false; node.each(function(d,i) { + if (d._reordered) { + nodesReordered = true; + delete d._reordered; + } if (d.dirty) { var self = this; var thisNode = d3.select(this); @@ -4270,6 +4279,13 @@ RED.view = (function() { 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( activeLinks, function(d) { @@ -4508,7 +4524,7 @@ RED.view = (function() { if (a._root === b._root) { return a._depth - b._depth; } else { - return a._root.localeCompare(b._root); + return a._index - b._index; } }) } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js index a7a4e024f..336b243c5 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js @@ -293,7 +293,14 @@ RED.workspaces = (function() { } }, 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); setWorkspaceOrder(newOrder); }, From 9dc5ae21c4a8da55cfa77b565a6d7eb18c868280 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 7 Sep 2021 12:04:58 +0100 Subject: [PATCH 15/15] Add menu options for raise/lower actions --- .../editor-client/locales/en-US/editor.json | 6 +++++- .../@node-red/editor-client/src/js/red.js | 21 ++++++++++++------- .../editor-client/src/js/ui/view-tools.js | 2 +- .../@node-red/editor-client/src/js/ui/view.js | 14 +++++++++++++ 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json index adeae0c2d..63ea0efe9 100755 --- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json @@ -125,7 +125,11 @@ "alignMiddle":"Align to middle", "alignBottom":"Align to bottom", "distributeHorizontally":"Distribute horizontally", - "distributeVertically":"Distribute vertically" + "distributeVertically":"Distribute vertically", + "moveToBack":"Move to back", + "moveToFront":"Move to front", + "moveBackwards":"Move backwards", + "moveForwards":"Move forwards" } }, "actions": { diff --git a/packages/node_modules/@node-red/editor-client/src/js/red.js b/packages/node_modules/@node-red/editor-client/src/js/red.js index 171e3124b..ad68ea612 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/red.js +++ b/packages/node_modules/@node-red/editor-client/src/js/red.js @@ -584,16 +584,21 @@ var RED = (function() { ]}); menuOptions.push({id:"menu-item-arrange-menu", label:RED._("menu.label.arrange"), options: [ - {id: "menu-item-view-tools-align-left", label:RED._("menu.label.alignLeft"), onselect: "core:align-selection-to-left"}, - {id: "menu-item-view-tools-align-center", label:RED._("menu.label.alignCenter"), onselect: "core:align-selection-to-center"}, - {id: "menu-item-view-tools-align-right", label:RED._("menu.label.alignRight"), onselect: "core:align-selection-to-right"}, + {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-top", label:RED._("menu.label.alignTop"), onselect: "core:align-selection-to-top"}, - {id: "menu-item-view-tools-align-middle", label:RED._("menu.label.alignMiddle"), onselect: "core:align-selection-to-middle"}, - {id: "menu-item-view-tools-align-bottom", label:RED._("menu.label.alignBottom"), onselect: "core:align-selection-to-bottom"}, + {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-distribute-horizontally", label:RED._("menu.label.distributeHorizontally"), onselect: "core:distribute-selection-horizontally"}, - {id: "menu-item-view-tools-distribute-veritcally", label:RED._("menu.label.distributeVertically"), onselect: "core:distribute-selection-vertically"} + {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); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js index 2c59fc352..2fba37e88 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js @@ -709,7 +709,7 @@ RED.view.tools = (function() { if (dir === "forwards") { movedNodes = RED.nodes.moveNodesForwards(nodesToMove); } else if (dir === "backwards") { - movedNodes = RED.nodes.moveNodesBacks(nodesToMove); + movedNodes = RED.nodes.moveNodesBackwards(nodesToMove); } else if (dir === "front") { movedNodes = RED.nodes.moveNodesToFront(nodesToMove); } else if (dir === "back") { diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index 40d8af2e7..d154fe865 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -504,9 +504,23 @@ RED.view = (function() { 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);