mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Compare commits
	
		
			45 Commits
		
	
	
		
			5004-icon-
			...
			master
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 2feb290ae3 | ||
|  | 45bcb74dae | ||
|  | 6da890bf88 | ||
|  | b8eeef182c | ||
|  | 54e0de64a7 | ||
|  | 3bd1b58217 | ||
|  | 71f8de94b0 | ||
|  | 4723378f2f | ||
|  | 8837597ff5 | ||
|  | 473b93f497 | ||
|  | d9c5144fe2 | ||
|  | cbc91a9ac8 | ||
|  | 88c946d401 | ||
|  | ff565bacb4 | ||
|  | e55301c073 | ||
|  | dc69226944 | ||
|  | bf6b18b8a6 | ||
|  | e1b591d761 | ||
|  | 27463197cd | ||
|  | 854460db56 | ||
|  | 4984af48f1 | ||
|  | e9dab46de8 | ||
|  | 4605f01c5d | ||
|  | a0ddf96e03 | ||
|  | 16d25b9d41 | ||
|  | 77c4ccf8fb | ||
|  | 7d9e09f5a7 | ||
|  | f4c184af4d | ||
|  | 9694c8bdfa | ||
|  | ca61efc986 | ||
|  | ffdbd94927 | ||
|  | 43df2318d4 | ||
|  | 21612a5215 | ||
|  | 756485e308 | ||
|  | efbe38f509 | ||
|  | daa76e6e5f | ||
|  | 48d2d269a5 | ||
|  | 13cac1b5ef | ||
|  | 479b7e756d | ||
|  | 503ef62cf5 | ||
|  | b355a37378 | ||
|  | 1acc16c9ef | ||
|  | 4cbf672b26 | ||
|  | 272355a48e | ||
|  | 953b7584a3 | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -28,3 +28,4 @@ docs | ||||
| .nyc_output | ||||
| sync.ffs_db | ||||
| package-lock.json | ||||
| .editorconfig | ||||
|   | ||||
							
								
								
									
										26
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,3 +1,29 @@ | ||||
| #### 4.0.9: Maintenance Release | ||||
|  | ||||
|  Editor | ||||
|   | ||||
|  - Add details for the dynamic subscription to match the English docs (#5050) @aikitori | ||||
|  - Fix tooltip snapping based on `typedInput` type (#5051) @GogoVega | ||||
|  - Prevent symbol usage warning in monaco (#5049) @Steve-Mcl | ||||
|  - Show subflow flow context under node section of sidebar (#5025) @knolleary | ||||
|  - feat: Add custom label for default deploy button in settings.editorTheme (#5030) @matiseni51 | ||||
|  - Handle long auto-complete suggests (#5042) @knolleary | ||||
|  - Handle undefined username when generating user icon (#5043) @knolleary | ||||
|  - Handle dragging node into group and splicing link at same time (#5027) @knolleary | ||||
|  - Remember context sidebar tree state when refreshing (#5021) @knolleary | ||||
|  - Update sf instance env vars when removed from template (#5023) @knolleary | ||||
|  - Do not select group when triggering quick-add within it (#5022) @knolleary | ||||
|  - Fix library icon handling within library browser component (#5017) @knolleary | ||||
|   | ||||
| Runtime | ||||
|  - Allow env var access to context (#5016) @knolleary | ||||
|  - fix debug status reporting if null (#5018) @dceejay | ||||
|  - Fix grunt dev via better ndoemon ignore rules (#5015) @knolleary | ||||
|  - Fix typo in CHANGELOG (4.0.7-->4.0.8) (#5007) @natcl | ||||
|  | ||||
| Nodes | ||||
|  - Switch: Avoid exceeding call stack when draining message group in Switch (#5014) @knolleary | ||||
|  | ||||
| #### 4.0.8: Maintenance Release | ||||
|  | ||||
| Editor | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "node-red", | ||||
|     "version": "4.0.8", | ||||
|     "version": "4.0.9", | ||||
|     "description": "Low-code programming for event-driven applications", | ||||
|     "homepage": "https://nodered.org", | ||||
|     "license": "Apache-2.0", | ||||
|   | ||||
| @@ -185,13 +185,12 @@ module.exports = { | ||||
|         } | ||||
|  | ||||
|         if (theme.deployButton) { | ||||
|             themeSettings.deployButton = {}; | ||||
|             if (theme.deployButton.label) { | ||||
|                 themeSettings.deployButton.label = theme.deployButton.label; | ||||
|             } | ||||
|             if (theme.deployButton.type == "simple") { | ||||
|                 themeSettings.deployButton = { | ||||
|                     type: "simple" | ||||
|                 } | ||||
|                 if (theme.deployButton.label) { | ||||
|                     themeSettings.deployButton.label = theme.deployButton.label; | ||||
|                 } | ||||
|                 themeSettings.deployButton.type = theme.deployButton.type; | ||||
|                 if (theme.deployButton.icon) { | ||||
|                     url = serveFile(themeApp,"/deploy/",theme.deployButton.icon); | ||||
|                     if (url) { | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@node-red/editor-api", | ||||
|     "version": "4.0.8", | ||||
|     "version": "4.0.9", | ||||
|     "license": "Apache-2.0", | ||||
|     "main": "./lib/index.js", | ||||
|     "repository": { | ||||
| @@ -16,8 +16,8 @@ | ||||
|         } | ||||
|     ], | ||||
|     "dependencies": { | ||||
|         "@node-red/util": "4.0.8", | ||||
|         "@node-red/editor-client": "4.0.8", | ||||
|         "@node-red/util": "4.0.9", | ||||
|         "@node-red/editor-client": "4.0.9", | ||||
|         "bcryptjs": "2.4.3", | ||||
|         "body-parser": "1.20.3", | ||||
|         "clone": "2.1.2", | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@node-red/editor-client", | ||||
|     "version": "4.0.8", | ||||
|     "version": "4.0.9", | ||||
|     "license": "Apache-2.0", | ||||
|     "repository": { | ||||
|         "type": "git", | ||||
|   | ||||
| @@ -398,14 +398,13 @@ RED.multiplayer = (function () { | ||||
|             anonIconBody.setAttribute("d",`M ${radius/2} ${radius/2 + 5} h -2.5 c -2 1 -2 -5 0.5 -4.5 c 2 1 2 1 4 0 c 2.5 -0.5  2.5 5.5  0 4.5  z`); | ||||
|             group.appendChild(anonIconBody) | ||||
|         } else { | ||||
|             const labelText = user.username ? user.username.substring(0,2) : user | ||||
|             const label = document.createElementNS("http://www.w3.org/2000/svg","text"); | ||||
|             if (user.username) { | ||||
|             if (user.username || user.email) { | ||||
|                 label.setAttribute("class","red-ui-multiplayer-annotation-label"); | ||||
|                 label.textContent = user.username.substring(0,2) | ||||
|                 label.textContent = (user.username || user.email).substring(0,2) | ||||
|             } else { | ||||
|                 label.setAttribute("class","red-ui-multiplayer-annotation-label red-ui-multiplayer-user-count") | ||||
|                 label.textContent = user | ||||
|                 label.textContent = 'nr' | ||||
|             } | ||||
|             label.setAttribute("text-anchor", "middle") | ||||
|             label.setAttribute("x",radius/2); | ||||
|   | ||||
| @@ -61,7 +61,7 @@ | ||||
|             } | ||||
|             this.menu = RED.popover.menu({ | ||||
|                 tabSelect: true, | ||||
|                 width: 300, | ||||
|                 width: Math.max(300, this.element.width()), | ||||
|                 maxHeight: 200, | ||||
|                 class: "red-ui-autoComplete-container", | ||||
|                 options: completions, | ||||
|   | ||||
| @@ -63,6 +63,7 @@ | ||||
|             pre: value.substring(0,idx), | ||||
|             match: value.substring(idx,idx+len), | ||||
|             post: value.substring(idx+len), | ||||
|             exact: idx === 0 && value.length === searchValue.length | ||||
|         } | ||||
|     } | ||||
|     function generateSpans(match) { | ||||
| @@ -83,7 +84,7 @@ | ||||
|                 const srcMatch = getMatch(optSrc, val); | ||||
|                 if (valMatch.found || srcMatch.found) { | ||||
|                     const element = $('<div>',{style: "display: flex"}); | ||||
|                     const valEl = $('<div/>',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"}); | ||||
|                     const valEl = $('<div/>',{ class: "red-ui-autoComplete-completion" }); | ||||
|                     valEl.append(generateSpans(valMatch)); | ||||
|                     valEl.appendTo(element); | ||||
|                     if (optSrc) { | ||||
| @@ -159,7 +160,7 @@ | ||||
|             if (valMatch.found) { | ||||
|                 const optSrc = envVarsMap[v] | ||||
|                 const element = $('<div>',{style: "display: flex"}); | ||||
|                 const valEl = $('<div/>',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"}); | ||||
|                 const valEl = $('<div/>',{ class: "red-ui-autoComplete-completion" }); | ||||
|                 valEl.append(generateSpans(valMatch)) | ||||
|                 valEl.appendTo(element) | ||||
|  | ||||
| @@ -201,7 +202,7 @@ | ||||
|         const that = this | ||||
|         const getContextKeysFromRuntime = function(scope, store, searchKey, done) { | ||||
|             contextKnownKeys[scope] = contextKnownKeys[scope] || {} | ||||
|             contextKnownKeys[scope][store] = contextKnownKeys[scope][store] || new Set() | ||||
|             contextKnownKeys[scope][store] = contextKnownKeys[scope][store] || new Map() | ||||
|             if (searchKey.length > 0) { | ||||
|                 try { | ||||
|                     RED.utils.normalisePropertyExpression(searchKey) | ||||
| @@ -223,11 +224,12 @@ | ||||
|                     const result = data[store] || {} | ||||
|                     const keys = result.keys || [] | ||||
|                     const keyPrefix = searchKey + (searchKey.length > 0 ? '.' : '') | ||||
|                     keys.forEach(key => { | ||||
|                     keys.forEach(keyInfo => { | ||||
|                         const key = keyInfo.key | ||||
|                         if (/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(key)) { | ||||
|                             contextKnownKeys[scope][store].add(keyPrefix + key) | ||||
|                             contextKnownKeys[scope][store].set(keyPrefix + key, keyInfo) | ||||
|                         } else { | ||||
|                             contextKnownKeys[scope][store].add(searchKey + "[\""+key.replace(/"/,"\\\"")+"\"]") | ||||
|                             contextKnownKeys[scope][store].set(searchKey + "[\""+key.replace(/"/,"\\\"")+"\"]", keyInfo) | ||||
|                         }                         | ||||
|                     }) | ||||
|                     done() | ||||
| @@ -242,14 +244,14 @@ | ||||
|                 // Get the flow id of the node we're editing | ||||
|                 const editStack = RED.editor.getEditStack() | ||||
|                 if (editStack.length === 0) { | ||||
|                     done([]) | ||||
|                     done(new Map()) | ||||
|                     return | ||||
|                 } | ||||
|                 const editingNode = editStack.pop() | ||||
|                 if (editingNode.z) { | ||||
|                     scope = `${scope}/${editingNode.z}` | ||||
|                 } else { | ||||
|                     done([]) | ||||
|                     done(new Map()) | ||||
|                     return | ||||
|                 } | ||||
|             } | ||||
| @@ -269,17 +271,29 @@ | ||||
|         return function(val, done) { | ||||
|             getContextKeys(val, function (keys) { | ||||
|                 const matches = [] | ||||
|                 keys.forEach(v => { | ||||
|                 keys.forEach((keyInfo, v) => { | ||||
|                     let optVal = v | ||||
|                     let valMatch = getMatch(optVal, val); | ||||
|                     if (!valMatch.found && val.length > 0 && val.endsWith('.')) { | ||||
|                         // Search key ends in '.' - but doesn't match. Check again | ||||
|                         // with [" at the end instead so we match bracket notation | ||||
|                         valMatch = getMatch(optVal, val.substring(0, val.length - 1) + '["') | ||||
|                     if (!valMatch.found && val.length > 0) { | ||||
|                         if (val.endsWith('.')) { | ||||
|                             // Search key ends in '.' - but doesn't match. Check again | ||||
|                             // with [" at the end instead so we match bracket notation | ||||
|                             valMatch = getMatch(optVal, val.substring(0, val.length - 1) + '["') | ||||
|                         // } else if (val.endsWith('[') && /^array/.test(keyInfo.format)) { | ||||
|                         //     console.log('this case') | ||||
|                         } | ||||
|                     } | ||||
|                     if (valMatch.found) { | ||||
|                         const element = $('<div>',{style: "display: flex"}); | ||||
|                         const valEl = $('<div/>',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"}); | ||||
|                         const valEl = $('<div/>',{ class: "red-ui-autoComplete-completion" }); | ||||
|                         // if (keyInfo.format) { | ||||
|                         //     valMatch.post += ' ' + keyInfo.format | ||||
|                         // } | ||||
|                         if (valMatch.exact && /^array/.test(keyInfo.format)) { | ||||
|                             valMatch.post += `[0-${keyInfo.length}]` | ||||
|                             optVal +=  '[' | ||||
|  | ||||
|                         } | ||||
|                         valEl.append(generateSpans(valMatch)) | ||||
|                         valEl.appendTo(element) | ||||
|                         matches.push({ | ||||
| @@ -1567,7 +1581,8 @@ | ||||
|                     if (tooltip) { | ||||
|                         tooltip.setContent(valid); | ||||
|                     } else { | ||||
|                         tooltip = RED.popover.tooltip(this.elementDiv, valid); | ||||
|                         const target = this.typeMap[type]?.options ? this.optionSelectLabel : this.elementDiv; | ||||
|                         tooltip = RED.popover.tooltip(target, valid); | ||||
|                         this.element.data("tooltip", tooltip); | ||||
|                     } | ||||
|                 } | ||||
|   | ||||
| @@ -44,6 +44,7 @@ RED.deploy = (function() { | ||||
|     /** | ||||
|      * options: | ||||
|      *   type: "default" - Button with drop-down options - no further customisation available | ||||
|      *      label: the text to display - default: "Deploy" | ||||
|      *   type: "simple"  - Button without dropdown. Customisations: | ||||
|      *      label: the text to display - default: "Deploy" | ||||
|      *      icon : the icon to use. Null removes the icon. default: "red/images/deploy-full-o.svg" | ||||
| @@ -51,13 +52,14 @@ RED.deploy = (function() { | ||||
|     function init(options) { | ||||
|         options = options || {}; | ||||
|         var type = options.type || "default"; | ||||
|         var label = options.label || RED._("deploy.deploy"); | ||||
|  | ||||
|         if (type == "default") { | ||||
|             $('<li><span class="red-ui-deploy-button-group button-group">'+ | ||||
|               '<a id="red-ui-header-button-deploy" class="red-ui-deploy-button disabled" href="#">'+ | ||||
|                 '<span class="red-ui-deploy-button-content">'+ | ||||
|                  '<img id="red-ui-header-button-deploy-icon" src="red/images/deploy-full-o.svg"> '+ | ||||
|                  '<span>'+RED._("deploy.deploy")+'</span>'+ | ||||
|                  '<span>'+label+'</span>'+ | ||||
|                 '</span>'+ | ||||
|                 '<span class="red-ui-deploy-button-spinner hide">'+ | ||||
|                  '<img src="red/images/spin.svg"/>'+ | ||||
| @@ -78,7 +80,6 @@ RED.deploy = (function() { | ||||
|             mainMenuItems.push({id:"deploymenu-item-reload", icon:"red/images/deploy-reload.svg",label:RED._("deploy.restartFlows"),sublabel:RED._("deploy.restartFlowsDesc"),onselect:"core:restart-flows"}) | ||||
|             RED.menu.init({id:"red-ui-header-button-deploy-options", options: mainMenuItems }); | ||||
|         } else if (type == "simple") { | ||||
|             var label = options.label || RED._("deploy.deploy"); | ||||
|             var icon = 'red/images/deploy-full-o.svg'; | ||||
|             if (options.hasOwnProperty('icon')) { | ||||
|                 icon = options.icon; | ||||
| @@ -424,11 +425,15 @@ RED.deploy = (function() { | ||||
|             const unknownNodes = []; | ||||
|             const invalidNodes = []; | ||||
|  | ||||
|             const isDisabled = function (node) { | ||||
|                 return (node.d || RED.nodes.workspace(node.z)?.disabled); | ||||
|             }; | ||||
|  | ||||
|             RED.nodes.eachConfig(function (node) { | ||||
|                 if (node.valid === undefined) { | ||||
|                     RED.editor.validateNode(node); | ||||
|                 } | ||||
|                 if (!node.valid && !node.d) { | ||||
|                 if (!node.valid && !isDisabled(node)) { | ||||
|                     invalidNodes.push(getNodeInfo(node)); | ||||
|                 } | ||||
|                 if (node.type === "unknown") { | ||||
| @@ -438,7 +443,7 @@ RED.deploy = (function() { | ||||
|                 } | ||||
|             }); | ||||
|             RED.nodes.eachNode(function (node) { | ||||
|                 if (!node.valid && !node.d) { | ||||
|                 if (!node.valid && !isDisabled(node)) { | ||||
|                     invalidNodes.push(getNodeInfo(node)); | ||||
|                 } | ||||
|                 if (node.type === "unknown") { | ||||
| @@ -452,7 +457,7 @@ RED.deploy = (function() { | ||||
|  | ||||
|             const unusedConfigNodes = []; | ||||
|             RED.nodes.eachConfig(function (node) { | ||||
|                 if ((node._def.hasUsers !== false) && (node.users.length === 0)) { | ||||
|                 if ((node._def.hasUsers !== false) && (node.users.length === 0) && !isDisabled(node)) { | ||||
|                     unusedConfigNodes.push(getNodeInfo(node)); | ||||
|                     hasUnusedConfig = true; | ||||
|                 } | ||||
|   | ||||
| @@ -1836,8 +1836,18 @@ RED.editor = (function() { | ||||
|                                 } | ||||
|                             }); | ||||
|                         } | ||||
|  | ||||
|                         let envToRemove = new Set() | ||||
|                         if (!isSameObj(old_env, new_env)) { | ||||
|                             // Get a list of env properties that have been removed | ||||
|                             // by comparing old_env and new_env | ||||
|                             if (old_env) { | ||||
|                                 old_env.forEach(env => { envToRemove.add(env.name) }) | ||||
|                             } | ||||
|                             if (new_env) { | ||||
|                                 new_env.forEach(env => { | ||||
|                                     envToRemove.delete(env.name) | ||||
|                                 }) | ||||
|                             } | ||||
|                             editState.changes.env = editing_node.env; | ||||
|                             editing_node.env = new_env; | ||||
|                             editState.changed = true; | ||||
| @@ -1846,10 +1856,11 @@ RED.editor = (function() { | ||||
|  | ||||
|  | ||||
|                         if (editState.changed) { | ||||
|                             var wasChanged = editing_node.changed; | ||||
|                             let wasChanged = editing_node.changed; | ||||
|                             editing_node.changed = true; | ||||
|                             validateNode(editing_node); | ||||
|                             var subflowInstances = []; | ||||
|                             let subflowInstances = []; | ||||
|                             let instanceHistoryEvents = [] | ||||
|                             RED.nodes.eachNode(function(n) { | ||||
|                                 if (n.type == "subflow:"+editing_node.id) { | ||||
|                                     subflowInstances.push({ | ||||
| @@ -1859,13 +1870,35 @@ RED.editor = (function() { | ||||
|                                     n._def.color = editing_node.color; | ||||
|                                     n.changed = true; | ||||
|                                     n.dirty = true; | ||||
|                                     if (n.env) { | ||||
|                                         const oldEnv = n.env | ||||
|                                         const newEnv = [] | ||||
|                                         let envChanged = false | ||||
|                                         n.env.forEach((env, index) => { | ||||
|                                             if (envToRemove.has(env.name)) { | ||||
|                                                 envChanged = true | ||||
|                                             } else { | ||||
|                                                 newEnv.push(env) | ||||
|                                             } | ||||
|                                         }) | ||||
|                                         if (envChanged) { | ||||
|                                             instanceHistoryEvents.push({ | ||||
|                                                 t: 'edit', | ||||
|                                                 node: n, | ||||
|                                                 changes: { env: oldEnv }, | ||||
|                                                 dirty: n.dirty, | ||||
|                                                 changed: n.changed | ||||
|                                             }) | ||||
|                                             n.env = newEnv | ||||
|                                         } | ||||
|                                     } | ||||
|                                     updateNodeProperties(n); | ||||
|                                     validateNode(n); | ||||
|                                 } | ||||
|                             }); | ||||
|                             RED.events.emit("subflows:change",editing_node); | ||||
|                             RED.nodes.dirty(true); | ||||
|                             var historyEvent = { | ||||
|                             let historyEvent = { | ||||
|                                 t:'edit', | ||||
|                                 node:editing_node, | ||||
|                                 changes:editState.changes, | ||||
| @@ -1875,7 +1908,13 @@ RED.editor = (function() { | ||||
|                                     instances:subflowInstances | ||||
|                                 } | ||||
|                             }; | ||||
|  | ||||
|                             if (instanceHistoryEvents.length > 0) { | ||||
|                                 historyEvent = { | ||||
|                                     t: 'multi', | ||||
|                                     events: [ historyEvent, ...instanceHistoryEvents ], | ||||
|                                     dirty: wasDirty | ||||
|                                 } | ||||
|                             } | ||||
|                             RED.history.push(historyEvent); | ||||
|                         } | ||||
|                         editing_node.dirty = true; | ||||
|   | ||||
| @@ -691,6 +691,7 @@ RED.editor.codeEditor.monaco = (function() { | ||||
|                         2322,  //Type 'unknown' is not assignable to type 'string' | ||||
|                         2339,  //property does not exist on | ||||
|                         2345,  //Argument of type xxx is not assignable to parameter of type 'DateTimeFormatOptions' | ||||
|                         2538,  //Ignore symbols as index property error.  | ||||
|                         7043,  //i forget what this one is, | ||||
|                         80001, //Convert to ES6 module | ||||
|                         80004, //JSDoc types may be moved to TypeScript types. | ||||
|   | ||||
| @@ -27,6 +27,12 @@ | ||||
|         reader.readAsDataURL(file); | ||||
|     } | ||||
|  | ||||
|     function file2Text(file,cb) { | ||||
|         file.arrayBuffer().then(d => { | ||||
|             cb( new TextDecoder().decode(d) ) | ||||
|         }).catch(ex => { cb(`error: ${ex}`) }) | ||||
|     } | ||||
|  | ||||
|     var initialized = false; | ||||
|     var currentEditor = null; | ||||
|     /** | ||||
| @@ -52,7 +58,8 @@ | ||||
|                     if (files.length === 1) { | ||||
|                         var file = files[0]; | ||||
|                         var name = file.name.toLowerCase(); | ||||
|  | ||||
|                         var fileType = file.type.toLowerCase(); | ||||
|                          | ||||
|                         if (name.match(/\.(apng|avif|gif|jpeg|png|svg|webp)$/)) { | ||||
|                             file2base64Image(file, function (image) { | ||||
|                                 var session = currentEditor.getSession(); | ||||
| @@ -63,7 +70,30 @@ | ||||
|                             }); | ||||
|                             return; | ||||
|                         } | ||||
|  | ||||
|                         if ( fileType.startsWith("text/") ) { | ||||
|                             file2Text(file, function (txt) { | ||||
|                                 var session = currentEditor.getSession(); | ||||
|                                 var pos = session.getCursorPosition(); | ||||
|                                 session.insert(pos, txt); | ||||
|                                 $("#red-ui-image-drop-target").hide(); | ||||
|                             }); | ||||
|                             return; | ||||
|                         } | ||||
|                          | ||||
|                     } | ||||
|                 } else if ($.inArray("text/plain", ev.originalEvent.dataTransfer.types) != -1) { | ||||
|                     let item = Object.values(ev.originalEvent.dataTransfer.items).filter(d => d.type == "text/plain")[0] | ||||
|  | ||||
|                     if (item) { | ||||
|                          item.getAsString(txt => { | ||||
|                             var session = currentEditor.getSession(); | ||||
|                             var pos = session.getCursorPosition(); | ||||
|                             session.insert(pos, txt); | ||||
|                             $("#red-ui-image-drop-target").hide(); | ||||
|                          }) | ||||
|                          return | ||||
|                     }                     | ||||
|                 } | ||||
|                 $("#red-ui-image-drop-target").hide(); | ||||
|             }); | ||||
|   | ||||
| @@ -56,7 +56,16 @@ RED.sidebar.config = (function() { | ||||
|             } else { | ||||
|                 $('<span class="red-ui-palette-node-config-label" data-i18n="sidebar.config.'+name+'">').appendTo(header); | ||||
|             } | ||||
|  | ||||
|             $('<span class="red-ui-sidebar-node-config-filter-info"></span>').appendTo(header); | ||||
|  | ||||
|             const changeBadgeContainer = $('<svg class="red-ui-sidebar-config-category-changed red-ui-flow-node-changed" width="10" height="10" viewBox="-1 -1 12 12"></svg>').appendTo(header); | ||||
|             const changeBadge = document.createElementNS("http://www.w3.org/2000/svg", "circle"); | ||||
|             changeBadge.setAttribute("cx", "5"); | ||||
|             changeBadge.setAttribute("cy", "5"); | ||||
|             changeBadge.setAttribute("r", "5"); | ||||
|             changeBadgeContainer.append(changeBadge); | ||||
|  | ||||
|             category = $('<ul class="red-ui-palette-content red-ui-sidebar-node-config-list"></ul>').appendTo(container); | ||||
|             category.on("click", function(e) { | ||||
|                 $(content).find(".red-ui-palette-node").removeClass("selected"); | ||||
| @@ -150,9 +159,6 @@ RED.sidebar.config = (function() { | ||||
|                     $('<li class="red-ui-palette-node-config-type">'+node.type+'</li>').appendTo(list); | ||||
|                     currentType = node.type; | ||||
|                 } | ||||
|                 if (node.changed) { | ||||
|                     labelText += "!!" | ||||
|                 } | ||||
|                 var entry = $('<li class="red-ui-palette-node_id_'+node.id.replace(/\./g,"-")+'"></li>').appendTo(list); | ||||
|                 var nodeDiv = $('<div class="red-ui-palette-node-config red-ui-palette-node"></div>').appendTo(entry); | ||||
|                 entry.data('node',node.id); | ||||
| @@ -181,15 +187,29 @@ RED.sidebar.config = (function() { | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 if (node.changed) { | ||||
|                     const nodeDivAnnotations = $('<svg class="red-ui-palette-node-annotations red-ui-flow-node-changed" width="10" height="10" viewBox="-1 -1 12 12"></svg>').appendTo(nodeDiv); | ||||
|                     const changeBadge = document.createElementNS("http://www.w3.org/2000/svg", "circle"); | ||||
|                     changeBadge.setAttribute("cx", "5"); | ||||
|                     changeBadge.setAttribute("cy", "5"); | ||||
|                     changeBadge.setAttribute("r", "5"); | ||||
|                     nodeDivAnnotations.append($(changeBadge)); | ||||
|  | ||||
|                     const categoryHeader = list.parent().find(".red-ui-sidebar-config-tray-header.red-ui-palette-header"); | ||||
|                     categoryHeader.addClass("red-ui-sidebar-config-changed"); | ||||
|                     nodeDiv.addClass("red-ui-palette-node-config-changed"); | ||||
|                 } | ||||
|  | ||||
|                 if (!node.valid) { | ||||
|                     nodeDiv.addClass("red-ui-palette-node-config-invalid") | ||||
|                     const nodeDivAnnotations = $('<svg class="red-ui-palette-node-annotations red-ui-flow-node-error" width="10" height="10"></svg>').appendTo(nodeDiv) | ||||
|                     const errorBadge = document.createElementNS("http://www.w3.org/2000/svg","path"); | ||||
|                     errorBadge.setAttribute("d","M 0,9 l 10,0 -5,-8 z"); | ||||
|                     nodeDivAnnotations.append($(errorBadge)) | ||||
|                     const nodeDivAnnotations = $('<svg class="red-ui-palette-node-annotations red-ui-flow-node-error" width="10" height="10"></svg>').appendTo(nodeDiv); | ||||
|                     const errorBadge = document.createElementNS("http://www.w3.org/2000/svg", "path"); | ||||
|                     errorBadge.setAttribute("d", "M 0,9 l 10,0 -5,-8 z"); | ||||
|                     nodeDivAnnotations.append($(errorBadge)); | ||||
|  | ||||
|                     nodeDiv.addClass("red-ui-palette-node-config-invalid"); | ||||
|                     RED.popover.tooltip(nodeDivAnnotations, function () { | ||||
|                         if (node.validationErrors && node.validationErrors.length > 0) { | ||||
|                             return RED._("editor.errors.invalidProperties")+"<br>  - "+node.validationErrors.join("<br>  - ") | ||||
|                             return RED._("editor.errors.invalidProperties") + "<br>  - " + node.validationErrors.join("<br>  - "); | ||||
|                         } | ||||
|                     }) | ||||
|                 } | ||||
| @@ -252,6 +272,10 @@ RED.sidebar.config = (function() { | ||||
|                 $(this).remove(); | ||||
|                 delete categories[id]; | ||||
|             } | ||||
|  | ||||
|             // Remove the `changed` badge from the category header | ||||
|             const categoryHeader = $(this).find(".red-ui-sidebar-config-tray-header.red-ui-palette-header"); | ||||
|             categoryHeader.removeClass("red-ui-sidebar-config-changed"); | ||||
|         }) | ||||
|         var globalConfigNodes = []; | ||||
|         var configList = {}; | ||||
|   | ||||
| @@ -18,8 +18,6 @@ RED.sidebar.context = (function() { | ||||
|     var content; | ||||
|     var sections; | ||||
|  | ||||
|     var localCache = {}; | ||||
|  | ||||
|     var flowAutoRefresh; | ||||
|     var nodeAutoRefresh; | ||||
|     var nodeSection; | ||||
| @@ -27,6 +25,8 @@ RED.sidebar.context = (function() { | ||||
|     var flowSection; | ||||
|     var globalSection; | ||||
|  | ||||
|     const expandedPaths = {} | ||||
|  | ||||
|     var currentNode; | ||||
|     var currentFlow; | ||||
|  | ||||
| @@ -212,14 +212,41 @@ RED.sidebar.context = (function() { | ||||
|             var l = keys.length; | ||||
|             for (var i = 0; i < l; i++) { | ||||
|                 sortedData[keys[i]].forEach(function(v) { | ||||
|                     var k = keys[i]; | ||||
|                     var l2 = sortedData[k].length; | ||||
|                     var propRow = $('<tr class="red-ui-help-info-row"><td class="red-ui-sidebar-context-property"></td><td></td></tr>').appendTo(container); | ||||
|                     var obj = $(propRow.children()[0]); | ||||
|                     const k = keys[i]; | ||||
|                     let payload = v.msg; | ||||
|                     let format = v.format; | ||||
|                     const tools = $('<span class="button-group"></span>'); | ||||
|                     expandedPaths[id + "." + k] = expandedPaths[id + "." + k] || new Set() | ||||
|                     const objectElementOptions = { | ||||
|                         typeHint: format, | ||||
|                         sourceId: id + "." + k, | ||||
|                         tools, | ||||
|                         path: k, | ||||
|                         rootPath: k, | ||||
|                         exposeApi: true, | ||||
|                         ontoggle: function(path,state) { | ||||
|                             path = path.substring(k.length+1) | ||||
|                             if (state) { | ||||
|                                 expandedPaths[id+"."+k].add(path) | ||||
|                             } else { | ||||
|                                 // if 'a' has been collapsed, we want to remove 'a.b' and 'a[0]...' from the set | ||||
|                                 // of collapsed paths | ||||
|                                 for (let expandedPath of expandedPaths[id+"."+k]) { | ||||
|                                     if (expandedPath.startsWith(path+".") || expandedPath.startsWith(path+"[")) { | ||||
|                                         expandedPaths[id+"."+k].delete(expandedPath) | ||||
|                                     } | ||||
|                                 } | ||||
|                                 expandedPaths[id+"."+k].delete(path) | ||||
|                             } | ||||
|                         }, | ||||
|                         expandPaths: [ ...expandedPaths[id+"."+k] ].sort(), | ||||
|                         expandLeafNodes: true | ||||
|                     } | ||||
|                     const propRow = $('<tr class="red-ui-help-info-row"><td class="red-ui-sidebar-context-property"></td><td></td></tr>').appendTo(container); | ||||
|                     const obj = $(propRow.children()[0]); | ||||
|                     obj.text(k); | ||||
|                     var tools = $('<span class="button-group"></span>'); | ||||
|                     const urlSafeK = encodeURIComponent(k) | ||||
|                     var refreshItem = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-refresh"></i></button>').appendTo(tools).on("click", function(e) { | ||||
|                     const refreshItem = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-refresh"></i></button>').appendTo(tools).on("click", function(e) { | ||||
|                         e.preventDefault(); | ||||
|                         e.stopPropagation(); | ||||
|                         $.getJSON(baseUrl+"/"+urlSafeK+"?store="+v.store, function(data) { | ||||
| @@ -229,16 +256,14 @@ RED.sidebar.context = (function() { | ||||
|                                 tools.detach(); | ||||
|                                 $(propRow.children()[1]).empty(); | ||||
|                                 RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), { | ||||
|                                     ...objectElementOptions, | ||||
|                                     typeHint: data.format, | ||||
|                                     sourceId: id+"."+k, | ||||
|                                     tools: tools, | ||||
|                                     path: k | ||||
|                                 }).appendTo(propRow.children()[1]); | ||||
|                             } | ||||
|                         }) | ||||
|                     }); | ||||
|                     RED.popover.tooltip(refreshItem,RED._("sidebar.context.refrsh")); | ||||
|                     var deleteItem = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-trash"></i></button>').appendTo(tools).on("click", function(e) { | ||||
|                     const deleteItem = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-trash"></i></button>').appendTo(tools).on("click", function(e) { | ||||
|                         e.preventDefault(); | ||||
|                         e.stopPropagation(); | ||||
|                         var popover = RED.popover.create({ | ||||
| @@ -246,7 +271,7 @@ RED.sidebar.context = (function() { | ||||
|                             target: propRow, | ||||
|                             direction: "left", | ||||
|                             content: function() { | ||||
|                                 var content = $('<div>'); | ||||
|                                 const content = $('<div>'); | ||||
|                                 $('<p data-i18n="sidebar.context.deleteConfirm"></p>').appendTo(content); | ||||
|                                 var row = $('<p>').appendTo(content); | ||||
|                                 var bg = $('<span class="button-group"></span>').appendTo(row); | ||||
| @@ -269,16 +294,15 @@ RED.sidebar.context = (function() { | ||||
|                                                 if (container.children().length === 0) { | ||||
|                                                     $('<tr class="red-ui-help-info-row red-ui-search-empty blank" colspan="2"><td data-i18n="sidebar.context.empty"></td></tr>').appendTo(container).i18n(); | ||||
|                                                 } | ||||
|                                                 delete expandedPaths[id + "." + k] | ||||
|                                             } else { | ||||
|                                                 payload = data.msg; | ||||
|                                                 format = data.format; | ||||
|                                                 tools.detach(); | ||||
|                                                 $(propRow.children()[1]).empty(); | ||||
|                                                 RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), { | ||||
|                                                     typeHint: data.format, | ||||
|                                                     sourceId: id+"."+k, | ||||
|                                                     tools: tools, | ||||
|                                                     path: k | ||||
|                                                     ...objectElementOptions, | ||||
|                                                     typeHint: data.format | ||||
|                                                 }).appendTo(propRow.children()[1]); | ||||
|                                             } | ||||
|                                         }); | ||||
| @@ -293,14 +317,7 @@ RED.sidebar.context = (function() { | ||||
|  | ||||
|                     }); | ||||
|                     RED.popover.tooltip(deleteItem,RED._("sidebar.context.delete")); | ||||
|                     var payload = v.msg; | ||||
|                     var format = v.format; | ||||
|                     RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), { | ||||
|                         typeHint: v.format, | ||||
|                         sourceId: id+"."+k, | ||||
|                         tools: tools, | ||||
|                         path: k | ||||
|                     }).appendTo(propRow.children()[1]); | ||||
|                     RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), objectElementOptions).appendTo(propRow.children()[1]); | ||||
|                     if (contextStores.length > 1) { | ||||
|                         $("<span>",{class:"red-ui-sidebar-context-property-storename"}).text(v.store).appendTo($(propRow.children()[0])) | ||||
|                     } | ||||
|   | ||||
| @@ -230,7 +230,7 @@ RED.utils = (function() { | ||||
|     var pinnedPaths = {}; | ||||
|     var formattedPaths = {}; | ||||
|  | ||||
|     function addMessageControls(obj,sourceId,key,msg,rootPath,strippedKey,extraTools) { | ||||
|     function addMessageControls(obj,sourceId,key,msg,rootPath,strippedKey,extraTools,enablePinning) { | ||||
|         if (!pinnedPaths.hasOwnProperty(sourceId)) { | ||||
|             pinnedPaths[sourceId] = {} | ||||
|         } | ||||
| @@ -250,7 +250,7 @@ RED.utils = (function() { | ||||
|             RED.clipboard.copyText(msg,copyPayload,"clipboard.copyMessageValue"); | ||||
|         }) | ||||
|         RED.popover.tooltip(copyPayload,RED._("node-red:debug.sidebar.copyPayload")); | ||||
|         if (strippedKey !== undefined && strippedKey !== '') { | ||||
|         if (enablePinning && strippedKey !== undefined && strippedKey !== '') { | ||||
|             var isPinned = pinnedPaths[sourceId].hasOwnProperty(strippedKey); | ||||
|  | ||||
|             var pinPath = $('<button class="red-ui-button red-ui-button-small red-ui-debug-msg-tools-pin"><i class="fa fa-map-pin"></i></button>').appendTo(tools).on("click", function(e) { | ||||
| @@ -281,13 +281,16 @@ RED.utils = (function() { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     function checkExpanded(strippedKey,expandPaths,minRange,maxRange) { | ||||
|     function checkExpanded(strippedKey, expandPaths, { minRange, maxRange, expandLeafNodes }) { | ||||
|         if (expandPaths && expandPaths.length > 0) { | ||||
|             if (strippedKey === '' && minRange === undefined) { | ||||
|                 return true; | ||||
|             } | ||||
|             for (var i=0;i<expandPaths.length;i++) { | ||||
|                 var p = expandPaths[i]; | ||||
|                 if (expandLeafNodes && p === strippedKey) { | ||||
|                     return true | ||||
|                 } | ||||
|                 if (p.indexOf(strippedKey) === 0 && (p[strippedKey.length] === "." ||  p[strippedKey.length] === "[") ) { | ||||
|  | ||||
|                     if (minRange !== undefined && p[strippedKey.length] === "[") { | ||||
| @@ -394,6 +397,8 @@ RED.utils = (function() { | ||||
|         var sourceId = options.sourceId; | ||||
|         var rootPath = options.rootPath; | ||||
|         var expandPaths = options.expandPaths; | ||||
|         const enablePinning = options.enablePinning | ||||
|         const expandLeafNodes = options.expandLeafNodes; | ||||
|         var ontoggle = options.ontoggle; | ||||
|         var exposeApi = options.exposeApi; | ||||
|         var tools = options.tools; | ||||
| @@ -416,11 +421,11 @@ RED.utils = (function() { | ||||
|         } | ||||
|         header = $('<span class="red-ui-debug-msg-row"></span>').appendTo(element); | ||||
|         if (sourceId) { | ||||
|             addMessageControls(header,sourceId,path,obj,rootPath,strippedKey,tools); | ||||
|             addMessageControls(header,sourceId,path,obj,rootPath,strippedKey,tools, enablePinning); | ||||
|         } | ||||
|         if (!key) { | ||||
|             element.addClass("red-ui-debug-msg-top-level"); | ||||
|             if (sourceId) { | ||||
|             if (sourceId && !expandPaths) { | ||||
|                 var pinned = pinnedPaths[sourceId]; | ||||
|                 expandPaths = []; | ||||
|                 if (pinned) { | ||||
| @@ -476,7 +481,7 @@ RED.utils = (function() { | ||||
|                     $('<span class="red-ui-debug-msg-type-meta red-ui-debug-msg-object-type-header"></span>').text(typeHint||'string').appendTo(header); | ||||
|                     var row = $('<div class="red-ui-debug-msg-object-entry collapsed"></div>').appendTo(element); | ||||
|                     $('<pre class="red-ui-debug-msg-type-string"></pre>').text(obj).appendTo(row); | ||||
|                 },function(state) {if (ontoggle) { ontoggle(path,state);}}, checkExpanded(strippedKey,expandPaths)); | ||||
|                 },function(state) {if (ontoggle) { ontoggle(path,state);}}, checkExpanded(strippedKey, expandPaths, { expandLeafNodes })); | ||||
|             } | ||||
|             e = $('<span class="red-ui-debug-msg-type-string red-ui-debug-msg-object-header"></span>').html('"'+formatString(sanitize(obj))+'"').appendTo(entryObj); | ||||
|             if (/^#[0-9a-f]{6}$/i.test(obj)) { | ||||
| @@ -592,14 +597,16 @@ RED.utils = (function() { | ||||
|                                     typeHint: type==='buffer'?'hex':false, | ||||
|                                     hideKey: false, | ||||
|                                     path: path+"["+i+"]", | ||||
|                                     sourceId: sourceId, | ||||
|                                     rootPath: rootPath, | ||||
|                                     expandPaths: expandPaths, | ||||
|                                     ontoggle: ontoggle, | ||||
|                                     exposeApi: exposeApi, | ||||
|                                     sourceId, | ||||
|                                     rootPath, | ||||
|                                     expandPaths, | ||||
|                                     expandLeafNodes, | ||||
|                                     ontoggle, | ||||
|                                     exposeApi, | ||||
|                                     // tools: tools // Do not pass tools down as we | ||||
|                                                     // keep them attached to the top-level header | ||||
|                                     nodeSelector: options.nodeSelector, | ||||
|                                     enablePinning | ||||
|                                 } | ||||
|                             ).appendTo(row); | ||||
|                         } | ||||
| @@ -623,21 +630,23 @@ RED.utils = (function() { | ||||
|                                                 typeHint: type==='buffer'?'hex':false, | ||||
|                                                 hideKey: false, | ||||
|                                                 path: path+"["+i+"]", | ||||
|                                                 sourceId: sourceId, | ||||
|                                                 rootPath: rootPath, | ||||
|                                                 expandPaths: expandPaths, | ||||
|                                                 ontoggle: ontoggle, | ||||
|                                                 exposeApi: exposeApi, | ||||
|                                                 sourceId, | ||||
|                                                 rootPath, | ||||
|                                                 expandPaths, | ||||
|                                                 expandLeafNodes, | ||||
|                                                 ontoggle, | ||||
|                                                 exposeApi, | ||||
|                                                 // tools: tools // Do not pass tools down as we | ||||
|                                                                 // keep them attached to the top-level header | ||||
|                                                 nodeSelector: options.nodeSelector, | ||||
|                                                 enablePinning | ||||
|                                             } | ||||
|                                         ).appendTo(row); | ||||
|                                     } | ||||
|                                 } | ||||
|                             })(), | ||||
|                             (function() { var path = path+"["+i+"]"; return function(state) {if (ontoggle) { ontoggle(path,state);}}})(), | ||||
|                             checkExpanded(strippedKey,expandPaths,minRange,Math.min(fullLength-1,(minRange+9)))); | ||||
|                             checkExpanded(strippedKey,expandPaths,{ minRange, maxRange: Math.min(fullLength-1,(minRange+9)), expandLeafNodes})); | ||||
|                             $('<span class="red-ui-debug-msg-object-key"></span>').html("["+minRange+" … "+Math.min(fullLength-1,(minRange+9))+"]").appendTo(header); | ||||
|                         } | ||||
|                         if (fullLength < originalLength) { | ||||
| @@ -646,7 +655,7 @@ RED.utils = (function() { | ||||
|                     } | ||||
|                 }, | ||||
|                 function(state) {if (ontoggle) { ontoggle(path,state);}}, | ||||
|                 checkExpanded(strippedKey,expandPaths)); | ||||
|                 checkExpanded(strippedKey, expandPaths, { expandLeafNodes })); | ||||
|             } | ||||
|         } else if (typeof obj === 'object') { | ||||
|             element.addClass('collapsed'); | ||||
| @@ -680,14 +689,16 @@ RED.utils = (function() { | ||||
|                                 typeHint: false, | ||||
|                                 hideKey: false, | ||||
|                                 path: newPath, | ||||
|                                 sourceId: sourceId, | ||||
|                                 rootPath: rootPath, | ||||
|                                 expandPaths: expandPaths, | ||||
|                                 ontoggle: ontoggle, | ||||
|                                 exposeApi: exposeApi, | ||||
|                                 sourceId, | ||||
|                                 rootPath, | ||||
|                                 expandPaths, | ||||
|                                 expandLeafNodes, | ||||
|                                 ontoggle, | ||||
|                                 exposeApi, | ||||
|                                 // tools: tools // Do not pass tools down as we | ||||
|                                                 // keep them attached to the top-level header | ||||
|                                 nodeSelector: options.nodeSelector, | ||||
|                                 enablePinning | ||||
|                             } | ||||
|                         ).appendTo(row); | ||||
|                     } | ||||
| @@ -696,7 +707,7 @@ RED.utils = (function() { | ||||
|                     } | ||||
|                 }, | ||||
|                 function(state) {if (ontoggle) { ontoggle(path,state);}}, | ||||
|                 checkExpanded(strippedKey,expandPaths)); | ||||
|                 checkExpanded(strippedKey, expandPaths, { expandLeafNodes })); | ||||
|             } | ||||
|             if (key) { | ||||
|                 $('<span class="red-ui-debug-msg-type-meta"></span>').text(type).appendTo(entryObj); | ||||
|   | ||||
| @@ -1265,11 +1265,6 @@ RED.view = (function() { | ||||
|         var targetGroup = options.group; | ||||
|         var touchTrigger = options.touchTrigger; | ||||
|  | ||||
|         if (targetGroup) { | ||||
|             selectedGroups.add(targetGroup,false); | ||||
|             RED.view.redraw(); | ||||
|         } | ||||
|  | ||||
|         // `point` is the place in the workspace the mouse has clicked. | ||||
|         //  This takes into account scrolling and scaling of the workspace. | ||||
|         var ox = point[0]; | ||||
| @@ -1591,9 +1586,6 @@ RED.view = (function() { | ||||
|                 // auto select dropped node - so info shows (if visible) | ||||
|                 clearSelection(); | ||||
|                 nn.selected = true; | ||||
|                 if (targetGroup) { | ||||
|                     selectedGroups.add(targetGroup,false); | ||||
|                 } | ||||
|                 movingSet.add(nn); | ||||
|                 updateActiveNodes(); | ||||
|                 updateSelection(); | ||||
| @@ -2178,19 +2170,24 @@ RED.view = (function() { | ||||
|                         n.n.moved = true; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 // Check to see if we need to splice a link | ||||
|                 // If a node has moved and ends up being spliced into a link, keep | ||||
|                 // track of which historyEvent to add the splice info to | ||||
|                 let targetSpliceEvent = null | ||||
|                 if (moveEvent.nodes.length > 0) { | ||||
|                     historyEvent.events.push(moveEvent) | ||||
|                     if (activeSpliceLink) { | ||||
|                         var linkToSplice = d3.select(activeSpliceLink).data()[0]; | ||||
|                         spliceLink(linkToSplice, movingSet.get(0).n, moveEvent) | ||||
|                     } | ||||
|                     targetSpliceEvent = moveEvent | ||||
|                 } | ||||
|                 if (moveAndChangedGroupEvent.nodes.length > 0) { | ||||
|                     historyEvent.events.push(moveAndChangedGroupEvent) | ||||
|                     targetSpliceEvent = moveAndChangedGroupEvent | ||||
|                 } | ||||
|                  | ||||
|                 // activeSpliceLink will only be set if the movingSet has a single | ||||
|                 // node that is able to splice. | ||||
|                 if (targetSpliceEvent && activeSpliceLink) { | ||||
|                     var linkToSplice = d3.select(activeSpliceLink).data()[0]; | ||||
|                     spliceLink(linkToSplice, movingSet.get(0).n, targetSpliceEvent) | ||||
|                 } | ||||
|  | ||||
|                 // Only continue if something has moved | ||||
|                 if (historyEvent.events.length > 0) { | ||||
|                     RED.nodes.dirty(true); | ||||
|   | ||||
| @@ -351,10 +351,10 @@ RED.user = (function() { | ||||
|             userIcon.css({ | ||||
|                 backgroundImage: "url("+user.image+")", | ||||
|             }) | ||||
|         } else if (user.anonymous) { | ||||
|         } else if (user.anonymous || (!user.username && !user.email)) { | ||||
|             $('<i class="fa fa-user"></i>').appendTo(userIcon); | ||||
|         } else { | ||||
|             $('<span>').text(user.username.substring(0,2)).appendTo(userIcon); | ||||
|             $('<span>').text((user.username || user.email).substring(0,2)).appendTo(userIcon); | ||||
|         } | ||||
|         if (user.profileColor !== undefined) { | ||||
|             userIcon.addClass('red-ui-user-profile-color-' + user.profileColor) | ||||
|   | ||||
| @@ -84,6 +84,11 @@ ul.red-ui-sidebar-node-config-list { | ||||
|     background: var(--red-ui-node-config-background); | ||||
|     color: var(--red-ui-primary-text-color); | ||||
|     cursor: pointer; | ||||
|     &.red-ui-palette-node-config-invalid.red-ui-palette-node-config-changed { | ||||
|         .red-ui-palette-node-annotations.red-ui-flow-node-error { | ||||
|             left: calc(100% - 28px); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| ul.red-ui-sidebar-node-config-list li.red-ui-palette-node-config-type { | ||||
|     color: var(--red-ui-secondary-text-color); | ||||
| @@ -115,6 +120,15 @@ ul.red-ui-sidebar-node-config-list li.red-ui-palette-node-config-type { | ||||
| .red-ui-palette-node-config-invalid { | ||||
|     border-color: var(--red-ui-form-input-border-error-color) | ||||
| } | ||||
| .red-ui-sidebar-config-tray-header.red-ui-palette-header:not(.red-ui-sidebar-config-changed) .red-ui-flow-node-changed { | ||||
|     display: none; | ||||
| } | ||||
| .red-ui-sidebar-config-tray-header.red-ui-palette-header.red-ui-sidebar-config-changed .red-ui-flow-node-changed { | ||||
|     display: inline-block; | ||||
|     position: absolute; | ||||
|     top: 1px; | ||||
|     right: 1px; | ||||
| } | ||||
| .red-ui-palette-node-annotations { | ||||
|     position: absolute; | ||||
|     left: calc(100% - 15px); | ||||
|   | ||||
| @@ -2,4 +2,15 @@ | ||||
|     &.red-ui-popover-panel { | ||||
|         border-top: none; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| .red-ui-autoComplete-completion { | ||||
|     font-family: var(--red-ui-monospace-font); | ||||
|     white-space: nowrap; | ||||
|     overflow: hidden; | ||||
|     flex-grow: 1; | ||||
|     text-overflow: ellipsis; | ||||
|     direction: rtl; | ||||
|     text-align: left; | ||||
| } | ||||
|   | ||||
| @@ -148,7 +148,7 @@ module.exports = function(RED) { | ||||
|                         var st = (typeof output === 'string') ? output : util.inspect(output); | ||||
|                         var fill = "grey"; | ||||
|                         var shape = "dot"; | ||||
|                         if (typeof output === 'object' && hasOwnProperty.call(output, "fill") && hasOwnProperty.call(output, "shape") && hasOwnProperty.call(output, "text")) { | ||||
|                         if (typeof output === 'object' && output?.fill && output?.shape && output?.text) { | ||||
|                             fill = output.fill; | ||||
|                             shape = output.shape; | ||||
|                             st = output.text; | ||||
|   | ||||
| @@ -511,9 +511,10 @@ RED.debug = (function() { | ||||
|             typeHint: format, | ||||
|             hideKey: false, | ||||
|             path: path, | ||||
|             sourceId: sourceNode&&sourceNode.id, | ||||
|             sourceId: sourceNode && sourceNode.id, | ||||
|             rootPath: path, | ||||
|             nodeSelector: config.messageSourceClick, | ||||
|             enablePinning: true | ||||
|         }); | ||||
|         // Do this in a separate step so the element functions aren't stripped | ||||
|         debugMessage.appendTo(el); | ||||
|   | ||||
| @@ -39,10 +39,36 @@ | ||||
|         <dd><b>MQTTv5</b>: Ablaufzeit der Nachricht in Sekunden.</dd> | ||||
|     </dl> | ||||
|     <h3>Details</h3> | ||||
|     <p>Das abonnierte Topic darf  MQTT-Platzhalterzeichen (wildcards) enthalten (+ für eine Ebene und # für mehrere Ebenen).</p> | ||||
|     <p>Dieser Node erfordert eine Verbindung zu einem MQTT-Broker, der über die Auswahlliste selektiert werden kann. | ||||
|        Eine neue Verbindung wird durch Klicken auf das Stiftsymbol erstellt.</p> | ||||
|     <p>Das abonnierte Topic darf MQTT-Platzhalterzeichen (wildcards) enthalten (+ für eine Ebene und # für mehrere Ebenen).</p> | ||||
|     <p>Diese Node erfordert eine Verbindung zu einem MQTT-Broker, der über die Auswahlliste selektiert werden kann. Eine neue Verbindung wird durch Klicken auf das Stiftsymbol erstellt.</p> | ||||
|     <p>Mehrere MQTT-Nodes (in oder out) können bei Bedarf dieselbe Broker-Verbindung nutzen.</p> | ||||
|     <h4>Dynamische Steuerung</h4> | ||||
|     Die von der Node genutzte Verbindung kann dynamisch gesteuert werden, wenn die MQTT-Node eine der folgenden Nachrichten erhält. Die Payload dieser Nachrichten werden nicht veröffentlicht. | ||||
|     <h4>Eingangsdaten</h4> | ||||
|     <p>Nur Verfügbar, wenn die Node für dynamische Abonnements konfiguriert wurde.</p> | ||||
|     <dl class="message-properties"> | ||||
|        <dt>action <span class="property-type">string</span></dt> | ||||
|        <dd>Der Name der Aktion, die die MQTT-Node ausführen soll. Verfügbare Aktionen sind: <code>"connect"</code>, <code>"disconnect"</code>, <code>"getSubscriptions"</code>, <code>"subscribe"</code> und <code>"unsubscribe"</code>.</dd> | ||||
|        <dt class="optional">topic <span class="property-type">string|object|array</span></dt> | ||||
|         <dd>Bei den Aktionen <code>"subscribe"</code> und <code>"unsubscribe"</code> gibt diese Eigenschaft die MQTT-Topic an. Dabei kann es sich um Folgendes handeln:   | ||||
|           <ul> | ||||
|               <li>eine Zeichenfolge, die den Topic-Filter enthält</li> | ||||
|               <li>ein Objekt mit den Eigenschaften <code>topic</code> und <code>qos</code></li> | ||||
|               <li>ein Array aus Zeichenfolgen oder Objekten, um mehrere Topics gleichzeitig zu verwalten</li> | ||||
|           </ul> | ||||
|         </dd> | ||||
|        <dt class="optional">broker <span class="property-type">broker</span> </dt> | ||||
|        <dd>Für die Aktion <code>"connect"</code> kann diese Eigenschaft jede der einzelnen Broker-Konfigurationseinstellungen überschreiben, einschließlich: <ul> | ||||
|                <li><code>broker</code></li> | ||||
|                <li><code>port</code></li> | ||||
|                <li><code>url</code> - überschreibt Broker/Port, um eine vollständige Verbindungs-URL bereitzustellen</li> | ||||
|                <li><code>username</code></li> | ||||
|                <li><code>password</code></li> | ||||
|            </ul> | ||||
|            <p>Wenn diese Eigenschaft gesetzt ist und der Broker bereits verbunden ist, wird ein Fehler protokolliert, es sei denn, die Eigenschaft <code>force</code> gesetzt - in diesem Fall wird die Verbindung zum Broker getrennt, die neuen Einstellungen angewendet und erneut verbunden.</p> | ||||
|        </dd> | ||||
|     </dl> | ||||
|    | ||||
| </script> | ||||
|  | ||||
| <script type="text/html" data-help-name="mqtt out"> | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@node-red/nodes", | ||||
|     "version": "4.0.8", | ||||
|     "version": "4.0.9", | ||||
|     "license": "Apache-2.0", | ||||
|     "repository": { | ||||
|         "type": "git", | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@node-red/registry", | ||||
|     "version": "4.0.8", | ||||
|     "version": "4.0.9", | ||||
|     "license": "Apache-2.0", | ||||
|     "main": "./lib/index.js", | ||||
|     "repository": { | ||||
| @@ -16,7 +16,7 @@ | ||||
|         } | ||||
|     ], | ||||
|     "dependencies": { | ||||
|         "@node-red/util": "4.0.8", | ||||
|         "@node-red/util": "4.0.9", | ||||
|         "clone": "2.1.2", | ||||
|         "fs-extra": "11.2.0", | ||||
|         "semver": "7.6.3", | ||||
|   | ||||
| @@ -96,7 +96,11 @@ var api = module.exports = { | ||||
|             } else if (scope === 'node') { | ||||
|                 var node = runtime.nodes.getNode(id); | ||||
|                 if (node) { | ||||
|                     ctx = node.context(); | ||||
|                     if (/^subflow:/.test(node.type)) { | ||||
|                         ctx = runtime.nodes.getContext(node.id); | ||||
|                     } else { | ||||
|                         ctx = node.context(); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             if (ctx) { | ||||
| @@ -104,13 +108,25 @@ var api = module.exports = { | ||||
|                     store = store || availableStores.default; | ||||
|                     ctx.get(key,store,function(err, v) { | ||||
|                         if (opts.keysOnly) { | ||||
|                             const result = {} | ||||
|                             if (Array.isArray(v)) { | ||||
|                                 resolve({ [store]: { format: `array[${v.length}]`}}) | ||||
|                                 result.format = `array[${v.length}]` | ||||
|                             } else if (typeof v === 'object') { | ||||
|                                 resolve({ [store]: { keys: Object.keys(v), format: 'Object' } }) | ||||
|                                 result.keys = Object.keys(v).map(k => { | ||||
|                                     if (Array.isArray(v[k])) { | ||||
|                                         return { key: k, format: `array[${v[k].length}]`, length: v[k].length } | ||||
|                                     } else if (typeof v[k] === 'object') { | ||||
|                                         return { key: k, format: 'object' } | ||||
|                                     } else { | ||||
|                                         return { key: k } | ||||
|                                     } | ||||
|                                 }) | ||||
|                                 result.format = 'object' | ||||
|                             } else { | ||||
|                                 resolve({ [store]: { keys: [] }}) | ||||
|                                 result.keys = [] | ||||
|                             } | ||||
|                             resolve({ [store]: result }) | ||||
|                             return | ||||
|                         } | ||||
|                         var encoded = util.encodeObject({msg:v}); | ||||
|                         if (store !== availableStores.default) { | ||||
| @@ -147,7 +163,7 @@ var api = module.exports = { | ||||
|                                     } | ||||
|                                     return | ||||
|                                 } | ||||
|                                 result[store] = { keys } | ||||
|                                 result[store] = { keys: keys.map(key => { return { key }}) } | ||||
|                                 c--; | ||||
|                                 if (c === 0) { | ||||
|                                     if (!errorReported) { | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@node-red/runtime", | ||||
|     "version": "4.0.8", | ||||
|     "version": "4.0.9", | ||||
|     "license": "Apache-2.0", | ||||
|     "main": "./lib/index.js", | ||||
|     "repository": { | ||||
| @@ -16,8 +16,8 @@ | ||||
|         } | ||||
|     ], | ||||
|     "dependencies": { | ||||
|         "@node-red/registry": "4.0.8", | ||||
|         "@node-red/util": "4.0.8", | ||||
|         "@node-red/registry": "4.0.9", | ||||
|         "@node-red/util": "4.0.9", | ||||
|         "async-mutex": "0.5.0", | ||||
|         "clone": "2.1.2", | ||||
|         "express": "4.21.2", | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@node-red/util", | ||||
|     "version": "4.0.8", | ||||
|     "version": "4.0.9", | ||||
|     "license": "Apache-2.0", | ||||
|     "repository": { | ||||
|         "type": "git", | ||||
|   | ||||
							
								
								
									
										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": "4.0.8", | ||||
|     "version": "4.0.9", | ||||
|     "description": "Low-code programming for event-driven applications", | ||||
|     "homepage": "https://nodered.org", | ||||
|     "license": "Apache-2.0", | ||||
| @@ -31,10 +31,10 @@ | ||||
|         "flow" | ||||
|     ], | ||||
|     "dependencies": { | ||||
|         "@node-red/editor-api": "4.0.8", | ||||
|         "@node-red/runtime": "4.0.8", | ||||
|         "@node-red/util": "4.0.8", | ||||
|         "@node-red/nodes": "4.0.8", | ||||
|         "@node-red/editor-api": "4.0.9", | ||||
|         "@node-red/runtime": "4.0.9", | ||||
|         "@node-red/util": "4.0.9", | ||||
|         "@node-red/nodes": "4.0.9", | ||||
|         "basic-auth": "2.0.1", | ||||
|         "bcryptjs": "2.4.3", | ||||
|         "cors": "2.8.5", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user