mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Merge branch 'dev' into edit-panes-2
This commit is contained in:
		
							
								
								
									
										14
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								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 | ||||
|   | ||||
							
								
								
									
										26
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								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", | ||||
| @@ -73,9 +73,9 @@ | ||||
|         "passport-oauth2-client-password": "0.1.2", | ||||
|         "raw-body": "2.4.1", | ||||
|         "semver": "7.3.5", | ||||
|         "tar": "6.1.2", | ||||
|         "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" | ||||
|   | ||||
| @@ -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 }), | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -116,7 +116,20 @@ | ||||
|             "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", | ||||
|             "moveToBack":"Move to back", | ||||
|             "moveToFront":"Move to front", | ||||
|             "moveBackwards":"Move backwards", | ||||
|             "moveForwards":"Move forwards" | ||||
|         } | ||||
|     }, | ||||
|     "actions": { | ||||
| @@ -450,8 +463,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 +478,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" | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@node-red/editor-client", | ||||
|     "version": "2.0.5", | ||||
|     "version": "2.0.6", | ||||
|     "license": "Apache-2.0", | ||||
|     "repository": { | ||||
|         "type": "git", | ||||
|   | ||||
| @@ -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 = { | ||||
| @@ -658,6 +669,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 +678,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 +687,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 +698,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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -77,6 +77,15 @@ | ||||
|         "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 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() { | ||||
|  | ||||
|     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 = []; | ||||
| @@ -210,6 +209,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<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() { | ||||
|         var bytes = []; | ||||
|         for (var i=0;i<8;i++) { | ||||
| @@ -295,15 +567,10 @@ RED.nodes = (function() { | ||||
|                 }); | ||||
|                 n.i = nextId+1; | ||||
|             } | ||||
|             nodes[n.id] = n; | ||||
|             allNodes.addNode(n); | ||||
|             if (!nodeLinks[n.id]) { | ||||
|                 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); | ||||
|     } | ||||
| @@ -331,10 +598,8 @@ RED.nodes = (function() { | ||||
|     function getNode(id) { | ||||
|         if (id in configNodes) { | ||||
|             return configNodes[id]; | ||||
|         } else if (id in nodes) { | ||||
|             return nodes[id]; | ||||
|         } | ||||
|         return null; | ||||
|         return allNodes.getNode(id); | ||||
|     } | ||||
|  | ||||
|     function removeNode(id) { | ||||
| @@ -346,13 +611,10 @@ RED.nodes = (function() { | ||||
|             delete configNodes[id]; | ||||
|             RED.events.emit('nodes:remove',node); | ||||
|             RED.workspaces.refresh(); | ||||
|         } else if (id in nodes) { | ||||
|             node = nodes[id]; | ||||
|             delete nodes[id] | ||||
|         } else if (allNodes.hasNode(id)) { | ||||
|             node = allNodes.getNode(id); | ||||
|             allNodes.removeNode(node); | ||||
|             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.forEach(removeLink); | ||||
|             var updatedConfigNode = false; | ||||
| @@ -410,18 +672,32 @@ RED.nodes = (function() { | ||||
|         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) { | ||||
|         if (node.type === "group") { | ||||
|             moveGroupToTab(node,z); | ||||
|             return; | ||||
|         } | ||||
|         if (nodeTabMap[node.z]) { | ||||
|             delete nodeTabMap[node.z][node.id]; | ||||
|         } | ||||
|         if (!nodeTabMap[z]) { | ||||
|             nodeTabMap[z] = {}; | ||||
|         } | ||||
|         nodeTabMap[z][node.id] = node; | ||||
|         allNodes.moveNode(node,z); | ||||
|         var nl = nodeLinks[node.id]; | ||||
|         if (nl) { | ||||
|             nl.in.forEach(function(l) { | ||||
| @@ -483,7 +759,7 @@ RED.nodes = (function() { | ||||
|  | ||||
|     function addWorkspace(ws,targetIndex) { | ||||
|         workspaces[ws.id] = ws; | ||||
|         nodeTabMap[ws.id] = {}; | ||||
|         allNodes.addTab(ws.id); | ||||
|         linkTabMap[ws.id] = []; | ||||
|  | ||||
|         ws._def = RED.nodes.getType('tab'); | ||||
| @@ -507,21 +783,16 @@ RED.nodes = (function() { | ||||
|         var removedGroups = []; | ||||
|         if (ws) { | ||||
|             delete workspaces[id]; | ||||
|             delete nodeTabMap[id]; | ||||
|             allNodes.removeTab(id); | ||||
|             delete linkTabMap[id]; | ||||
|             workspacesOrder.splice(workspacesOrder.indexOf(id),1); | ||||
|             var i; | ||||
|             var node; | ||||
|             // TODO: this should use nodeTabMap | ||||
|             for (i in nodes) { | ||||
|                 if (nodes.hasOwnProperty(i)) { | ||||
|                     node = nodes[i]; | ||||
|                     if (node.z == id) { | ||||
|                         removedNodes.push(node); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|             if (allNodes.hasTab(id)) { | ||||
|                 removedNodes = allNodes.getNodes(id).slice() | ||||
|             } | ||||
|             for(i in configNodes) { | ||||
|             for (i in configNodes) { | ||||
|                 if (configNodes.hasOwnProperty(i)) { | ||||
|                     node = configNodes[i]; | ||||
|                     if (node.z == id) { | ||||
| @@ -573,7 +844,7 @@ RED.nodes = (function() { | ||||
|             sf.name = subflowName; | ||||
|         } | ||||
|         subflows[sf.id] = sf; | ||||
|         nodeTabMap[sf.id] = {}; | ||||
|         allNodes.addTab(sf.id); | ||||
|         linkTabMap[sf.id] = []; | ||||
|  | ||||
|         RED.nodes.registerType("subflow:"+sf.id, { | ||||
| @@ -619,27 +890,24 @@ RED.nodes = (function() { | ||||
|     function removeSubflow(sf) { | ||||
|         if (subflows[sf.id]) { | ||||
|             delete subflows[sf.id]; | ||||
|             delete nodeTabMap[sf.id]; | ||||
|             allNodes.removeTab(sf.id); | ||||
|             registry.removeNodeType("subflow:"+sf.id); | ||||
|             RED.events.emit("subflows:remove",sf); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function subflowContains(sfid,nodeid) { | ||||
|         for (var i in nodes) { | ||||
|             if (nodes.hasOwnProperty(i)) { | ||||
|                 var node = nodes[i]; | ||||
|                 if (node.z === sfid) { | ||||
|                     var m = /^subflow:(.+)$/.exec(node.type); | ||||
|                     if (m) { | ||||
|                         if (m[1] === nodeid) { | ||||
|                             return true; | ||||
|                         } else { | ||||
|                             var result = subflowContains(m[1],nodeid); | ||||
|                             if (result) { | ||||
|                                 return true; | ||||
|                             } | ||||
|                         } | ||||
|         var sfNodes = allNodes.getNodes(sfid); | ||||
|         for (var i = 0; i<sfNodes.length; i++) { | ||||
|             var node = sfNodes[i]; | ||||
|             var m = /^subflow:(.+)$/.exec(node.type); | ||||
|             if (m) { | ||||
|                 if (m[1] === nodeid) { | ||||
|                     return true; | ||||
|                 } else { | ||||
|                     var result = subflowContains(m[1],nodeid); | ||||
|                     if (result) { | ||||
|                         return true; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| @@ -960,11 +1228,15 @@ RED.nodes = (function() { | ||||
|  | ||||
|     function createExportableSubflow(id) { | ||||
|         var sf = getSubflow(id); | ||||
|         var nodeSet = [sf]; | ||||
|         var sfNodeIds = Object.keys(nodeTabMap[sf.id]||{}); | ||||
|         for (var i=0, l=sfNodeIds.length; i<l; i++) { | ||||
|             nodeSet.push(nodeTabMap[sf.id][sfNodeIds[i]]); | ||||
|         var nodeSet; | ||||
|         var sfNodes = allNodes.getNodes(sf.id); | ||||
|         if (sfNodes) { | ||||
|             nodeSet = sfNodes.slice(); | ||||
|             nodeSet.unshift(sf); | ||||
|         } else { | ||||
|             nodeSet = [sf]; | ||||
|         } | ||||
|         console.log(nodeSet); | ||||
|         return createExportableNodeSet(nodeSet); | ||||
|     } | ||||
|     /** | ||||
| @@ -991,12 +1263,9 @@ RED.nodes = (function() { | ||||
|                 if (!exportedSubflows[subflowId]) { | ||||
|                     exportedSubflows[subflowId] = true; | ||||
|                     var subflow = getSubflow(subflowId); | ||||
|                     var subflowSet = [subflow]; | ||||
|                     RED.nodes.eachNode(function(n) { | ||||
|                         if (n.z == subflowId) { | ||||
|                             subflowSet.push(n); | ||||
|                         } | ||||
|                     }); | ||||
|                     var subflowSet = allNodes.getNodes(subflowId).slice(); | ||||
|                     subflowSet.unshift(subflow); | ||||
|  | ||||
|                     RED.nodes.eachConfig(function(n) { | ||||
|                         if (n.z == subflowId) { | ||||
|                             subflowSet.push(n); | ||||
| @@ -1074,11 +1343,9 @@ RED.nodes = (function() { | ||||
|                 nns.push(convertNode(configNodes[i], opts)); | ||||
|             } | ||||
|         } | ||||
|         for (i in nodes) { | ||||
|             if (nodes.hasOwnProperty(i)) { | ||||
|                 nns.push(convertNode(nodes[i], opts)); | ||||
|             } | ||||
|         } | ||||
|         RED.nodes.eachNode(function(n) { | ||||
|             nns.push(convertNode(n, opts)); | ||||
|         }) | ||||
|         return nns; | ||||
|     } | ||||
|  | ||||
| @@ -1175,7 +1442,7 @@ RED.nodes = (function() { | ||||
|             var nodeZ = n.z || "__global__"; | ||||
|             imported.zMap[nodeZ] = imported.zMap[nodeZ] || []; | ||||
|             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; | ||||
|             } | ||||
|         }) | ||||
| @@ -1183,7 +1450,6 @@ RED.nodes = (function() { | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Replace the provided nodes. | ||||
|      * This must contain complete Subflow defs or complete Flow Tabs. | ||||
| @@ -1342,7 +1608,7 @@ RED.nodes = (function() { | ||||
|             if (!options.generateIds) { | ||||
|                 if (!options.importMap[id]) { | ||||
|                     // 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) { | ||||
|                         existingNodes.push({existing:existing, imported:n}); | ||||
|                     } | ||||
| @@ -2006,32 +2272,9 @@ RED.nodes = (function() { | ||||
|  | ||||
|     // TODO: supports filter.z|type | ||||
|     function filterNodes(filter) { | ||||
|         var result = []; | ||||
|         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; | ||||
|         return allNodes.filterNodes(filter); | ||||
|     } | ||||
|  | ||||
|     function filterLinks(filter) { | ||||
|         var result = []; | ||||
|         var candidateLinks = []; | ||||
| @@ -2119,9 +2362,8 @@ RED.nodes = (function() { | ||||
|     } | ||||
|  | ||||
|     function clear() { | ||||
|         nodes = {}; | ||||
|         allNodes.clear(); | ||||
|         links = []; | ||||
|         nodeTabMap = {}; | ||||
|         linkTabMap = {}; | ||||
|         nodeLinks = {}; | ||||
|         configNodes = {}; | ||||
| @@ -2213,10 +2455,7 @@ RED.nodes = (function() { | ||||
|                         if (configNodes.hasOwnProperty(n.id)) { | ||||
|                             delete configNodes[n.id]; | ||||
|                         } else { | ||||
|                             delete nodes[n.id]; | ||||
|                             if (nodeTabMap[n.z]) { | ||||
|                                 delete nodeTabMap[n.z][n.id]; | ||||
|                             } | ||||
|                             allNodes.removeNode(n); | ||||
|                         } | ||||
|                         reimportList.push(convertNode(n)); | ||||
|                         RED.events.emit('nodes:remove',n); | ||||
| @@ -2273,6 +2512,13 @@ RED.nodes = (function() { | ||||
|         remove: removeNode, | ||||
|         clear: clear, | ||||
|  | ||||
|         moveNodesForwards: moveNodesForwards, | ||||
|         moveNodesBackwards: moveNodesBackwards, | ||||
|         moveNodesToFront: moveNodesToFront, | ||||
|         moveNodesToBack: moveNodesToBack, | ||||
|         getNodeOrder: getNodeOrder, | ||||
|         setNodeOrder: setNodeOrder, | ||||
|  | ||||
|         moveNodeToTab: moveNodeToTab, | ||||
|  | ||||
|         addLink: addLink, | ||||
| @@ -2292,16 +2538,10 @@ RED.nodes = (function() { | ||||
|         addGroup: addGroup, | ||||
|         removeGroup: removeGroup, | ||||
|         group: function(id) { return groups[id] }, | ||||
|         groups: function(z) { return groupsByZ[z]||[] }, | ||||
|         groups: function(z) { return groupsByZ[z]?groupsByZ[z].slice():[] }, | ||||
|  | ||||
|         eachNode: function(cb) { | ||||
|             for (var id in nodes) { | ||||
|                 if (nodes.hasOwnProperty(id)) { | ||||
|                     if (cb(nodes[id]) === false) { | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             allNodes.eachNode(cb); | ||||
|         }, | ||||
|         eachLink: function(cb) { | ||||
|             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"} | ||||
|             ]}); | ||||
|         } | ||||
|         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,25 @@ 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-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); | ||||
|         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 +661,6 @@ var RED = (function() { | ||||
|         RED.user.init(); | ||||
|         RED.notifications.init(); | ||||
|         RED.library.init(); | ||||
|         RED.keyboard.init(); | ||||
|         RED.palette.init(); | ||||
|         RED.eventLog.init(); | ||||
|  | ||||
| @@ -655,7 +689,7 @@ var RED = (function() { | ||||
|  | ||||
|         RED.deploy.init(RED.settings.theme("deployButton",null)); | ||||
|  | ||||
|         buildMainMenu(); | ||||
|         RED.keyboard.init(buildMainMenu); | ||||
|  | ||||
|         RED.nodes.init(); | ||||
|         RED.comms.connect(); | ||||
|   | ||||
| @@ -88,6 +88,13 @@ RED.menu = (function() { | ||||
|             linkContent += '</a>'; | ||||
|  | ||||
|             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; | ||||
|  | ||||
| @@ -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 { | ||||
|         init: createMenu, | ||||
|         setSelected: setSelected, | ||||
| @@ -284,6 +307,7 @@ RED.menu = (function() { | ||||
|         setDisabled: setDisabled, | ||||
|         addItem: addItem, | ||||
|         removeItem: removeItem, | ||||
|         setAction: setAction | ||||
|         setAction: setAction, | ||||
|         refreshShortcuts: refreshShortcuts | ||||
|     } | ||||
| })(); | ||||
|   | ||||
| @@ -184,7 +184,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; | ||||
| @@ -201,6 +203,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() }) | ||||
| @@ -253,6 +257,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() { | ||||
|   | ||||
| @@ -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(); | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
|   | ||||
| @@ -311,6 +311,7 @@ RED.sidebar.info.outliner = (function() { | ||||
|         RED.events.on("nodes:add",onNodeAdd); | ||||
|         RED.events.on("nodes:remove",onObjectRemove); | ||||
|         RED.events.on("nodes:change",onNodeChange); | ||||
|         // RED.events.on("nodes:reorder",onNodesReorder); | ||||
|  | ||||
|         RED.events.on("groups:add",onNodeAdd); | ||||
|         RED.events.on("groups:remove",onObjectRemove); | ||||
| @@ -369,6 +370,21 @@ RED.sidebar.info.outliner = (function() { | ||||
|             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) { | ||||
|         objects[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 { | ||||
|         init: 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());}); | ||||
| @@ -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-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());}); | ||||
| @@ -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-above", function() { gotoNearestNode('up') }) | ||||
|             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() }) | ||||
|         }, | ||||
|         /** | ||||
|   | ||||
| @@ -501,6 +501,28 @@ 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); | ||||
|             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:edit-selected-node",editSelection); | ||||
|         RED.actions.add("core:go-to-selection",function() { | ||||
| @@ -655,14 +677,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; | ||||
| @@ -695,7 +720,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; | ||||
|             } | ||||
|         }); | ||||
|  | ||||
| @@ -704,7 +730,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); | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
| @@ -2332,6 +2359,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) { | ||||
| @@ -3807,7 +3835,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__ = []; | ||||
| @@ -3953,7 +3980,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); | ||||
| @@ -4261,6 +4293,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) { | ||||
| @@ -4499,7 +4538,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; | ||||
|                     } | ||||
|                 }) | ||||
|             } | ||||
|   | ||||
| @@ -141,7 +141,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); | ||||
|             }, | ||||
|   | ||||
| @@ -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) { | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
|             } | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -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.2", | ||||
|         "uglify-js": "3.13.10" | ||||
|         "tar": "6.1.11", | ||||
|         "uglify-js": "3.14.1" | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -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" | ||||
|     } | ||||
|   | ||||
							
								
								
									
										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", | ||||
|     "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", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user