@node-red/editor-client 4.0.8 → 4.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/public/red/red.js CHANGED
@@ -2017,10 +2017,10 @@ RED.user = (function() {
2017
2017
  userIcon.css({
2018
2018
  backgroundImage: "url("+user.image+")",
2019
2019
  })
2020
- } else if (user.anonymous) {
2020
+ } else if (user.anonymous || (!user.username && !user.email)) {
2021
2021
  $('<i class="fa fa-user"></i>').appendTo(userIcon);
2022
2022
  } else {
2023
- $('<span>').text(user.username.substring(0,2)).appendTo(userIcon);
2023
+ $('<span>').text((user.username || user.email).substring(0,2)).appendTo(userIcon);
2024
2024
  }
2025
2025
  if (user.profileColor !== undefined) {
2026
2026
  userIcon.addClass('red-ui-user-profile-color-' + user.profileColor)
@@ -2715,14 +2715,13 @@ RED.comms = (function() {
2715
2715
  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`);
2716
2716
  group.appendChild(anonIconBody)
2717
2717
  } else {
2718
- const labelText = user.username ? user.username.substring(0,2) : user
2719
2718
  const label = document.createElementNS("http://www.w3.org/2000/svg","text");
2720
- if (user.username) {
2719
+ if (user.username || user.email) {
2721
2720
  label.setAttribute("class","red-ui-multiplayer-annotation-label");
2722
- label.textContent = user.username.substring(0,2)
2721
+ label.textContent = (user.username || user.email).substring(0,2)
2723
2722
  } else {
2724
2723
  label.setAttribute("class","red-ui-multiplayer-annotation-label red-ui-multiplayer-user-count")
2725
- label.textContent = user
2724
+ label.textContent = 'nr'
2726
2725
  }
2727
2726
  label.setAttribute("text-anchor", "middle")
2728
2727
  label.setAttribute("x",radius/2);
@@ -9795,7 +9794,7 @@ RED.utils = (function() {
9795
9794
  var pinnedPaths = {};
9796
9795
  var formattedPaths = {};
9797
9796
 
9798
- function addMessageControls(obj,sourceId,key,msg,rootPath,strippedKey,extraTools) {
9797
+ function addMessageControls(obj,sourceId,key,msg,rootPath,strippedKey,extraTools,enablePinning) {
9799
9798
  if (!pinnedPaths.hasOwnProperty(sourceId)) {
9800
9799
  pinnedPaths[sourceId] = {}
9801
9800
  }
@@ -9815,7 +9814,7 @@ RED.utils = (function() {
9815
9814
  RED.clipboard.copyText(msg,copyPayload,"clipboard.copyMessageValue");
9816
9815
  })
9817
9816
  RED.popover.tooltip(copyPayload,RED._("node-red:debug.sidebar.copyPayload"));
9818
- if (strippedKey !== undefined && strippedKey !== '') {
9817
+ if (enablePinning && strippedKey !== undefined && strippedKey !== '') {
9819
9818
  var isPinned = pinnedPaths[sourceId].hasOwnProperty(strippedKey);
9820
9819
 
9821
9820
  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) {
@@ -9846,13 +9845,16 @@ RED.utils = (function() {
9846
9845
  }
9847
9846
  }
9848
9847
  }
9849
- function checkExpanded(strippedKey,expandPaths,minRange,maxRange) {
9848
+ function checkExpanded(strippedKey, expandPaths, { minRange, maxRange, expandLeafNodes }) {
9850
9849
  if (expandPaths && expandPaths.length > 0) {
9851
9850
  if (strippedKey === '' && minRange === undefined) {
9852
9851
  return true;
9853
9852
  }
9854
9853
  for (var i=0;i<expandPaths.length;i++) {
9855
9854
  var p = expandPaths[i];
9855
+ if (expandLeafNodes && p === strippedKey) {
9856
+ return true
9857
+ }
9856
9858
  if (p.indexOf(strippedKey) === 0 && (p[strippedKey.length] === "." || p[strippedKey.length] === "[") ) {
9857
9859
 
9858
9860
  if (minRange !== undefined && p[strippedKey.length] === "[") {
@@ -9959,6 +9961,8 @@ RED.utils = (function() {
9959
9961
  var sourceId = options.sourceId;
9960
9962
  var rootPath = options.rootPath;
9961
9963
  var expandPaths = options.expandPaths;
9964
+ const enablePinning = options.enablePinning
9965
+ const expandLeafNodes = options.expandLeafNodes;
9962
9966
  var ontoggle = options.ontoggle;
9963
9967
  var exposeApi = options.exposeApi;
9964
9968
  var tools = options.tools;
@@ -9981,11 +9985,11 @@ RED.utils = (function() {
9981
9985
  }
9982
9986
  header = $('<span class="red-ui-debug-msg-row"></span>').appendTo(element);
9983
9987
  if (sourceId) {
9984
- addMessageControls(header,sourceId,path,obj,rootPath,strippedKey,tools);
9988
+ addMessageControls(header,sourceId,path,obj,rootPath,strippedKey,tools, enablePinning);
9985
9989
  }
9986
9990
  if (!key) {
9987
9991
  element.addClass("red-ui-debug-msg-top-level");
9988
- if (sourceId) {
9992
+ if (sourceId && !expandPaths) {
9989
9993
  var pinned = pinnedPaths[sourceId];
9990
9994
  expandPaths = [];
9991
9995
  if (pinned) {
@@ -10041,7 +10045,7 @@ RED.utils = (function() {
10041
10045
  $('<span class="red-ui-debug-msg-type-meta red-ui-debug-msg-object-type-header"></span>').text(typeHint||'string').appendTo(header);
10042
10046
  var row = $('<div class="red-ui-debug-msg-object-entry collapsed"></div>').appendTo(element);
10043
10047
  $('<pre class="red-ui-debug-msg-type-string"></pre>').text(obj).appendTo(row);
10044
- },function(state) {if (ontoggle) { ontoggle(path,state);}}, checkExpanded(strippedKey,expandPaths));
10048
+ },function(state) {if (ontoggle) { ontoggle(path,state);}}, checkExpanded(strippedKey, expandPaths, { expandLeafNodes }));
10045
10049
  }
10046
10050
  e = $('<span class="red-ui-debug-msg-type-string red-ui-debug-msg-object-header"></span>').html('"'+formatString(sanitize(obj))+'"').appendTo(entryObj);
10047
10051
  if (/^#[0-9a-f]{6}$/i.test(obj)) {
@@ -10157,14 +10161,16 @@ RED.utils = (function() {
10157
10161
  typeHint: type==='buffer'?'hex':false,
10158
10162
  hideKey: false,
10159
10163
  path: path+"["+i+"]",
10160
- sourceId: sourceId,
10161
- rootPath: rootPath,
10162
- expandPaths: expandPaths,
10163
- ontoggle: ontoggle,
10164
- exposeApi: exposeApi,
10164
+ sourceId,
10165
+ rootPath,
10166
+ expandPaths,
10167
+ expandLeafNodes,
10168
+ ontoggle,
10169
+ exposeApi,
10165
10170
  // tools: tools // Do not pass tools down as we
10166
10171
  // keep them attached to the top-level header
10167
10172
  nodeSelector: options.nodeSelector,
10173
+ enablePinning
10168
10174
  }
10169
10175
  ).appendTo(row);
10170
10176
  }
@@ -10188,21 +10194,23 @@ RED.utils = (function() {
10188
10194
  typeHint: type==='buffer'?'hex':false,
10189
10195
  hideKey: false,
10190
10196
  path: path+"["+i+"]",
10191
- sourceId: sourceId,
10192
- rootPath: rootPath,
10193
- expandPaths: expandPaths,
10194
- ontoggle: ontoggle,
10195
- exposeApi: exposeApi,
10197
+ sourceId,
10198
+ rootPath,
10199
+ expandPaths,
10200
+ expandLeafNodes,
10201
+ ontoggle,
10202
+ exposeApi,
10196
10203
  // tools: tools // Do not pass tools down as we
10197
10204
  // keep them attached to the top-level header
10198
10205
  nodeSelector: options.nodeSelector,
10206
+ enablePinning
10199
10207
  }
10200
10208
  ).appendTo(row);
10201
10209
  }
10202
10210
  }
10203
10211
  })(),
10204
10212
  (function() { var path = path+"["+i+"]"; return function(state) {if (ontoggle) { ontoggle(path,state);}}})(),
10205
- checkExpanded(strippedKey,expandPaths,minRange,Math.min(fullLength-1,(minRange+9))));
10213
+ checkExpanded(strippedKey,expandPaths,{ minRange, maxRange: Math.min(fullLength-1,(minRange+9)), expandLeafNodes}));
10206
10214
  $('<span class="red-ui-debug-msg-object-key"></span>').html("["+minRange+" &hellip; "+Math.min(fullLength-1,(minRange+9))+"]").appendTo(header);
10207
10215
  }
10208
10216
  if (fullLength < originalLength) {
@@ -10211,7 +10219,7 @@ RED.utils = (function() {
10211
10219
  }
10212
10220
  },
10213
10221
  function(state) {if (ontoggle) { ontoggle(path,state);}},
10214
- checkExpanded(strippedKey,expandPaths));
10222
+ checkExpanded(strippedKey, expandPaths, { expandLeafNodes }));
10215
10223
  }
10216
10224
  } else if (typeof obj === 'object') {
10217
10225
  element.addClass('collapsed');
@@ -10245,14 +10253,16 @@ RED.utils = (function() {
10245
10253
  typeHint: false,
10246
10254
  hideKey: false,
10247
10255
  path: newPath,
10248
- sourceId: sourceId,
10249
- rootPath: rootPath,
10250
- expandPaths: expandPaths,
10251
- ontoggle: ontoggle,
10252
- exposeApi: exposeApi,
10256
+ sourceId,
10257
+ rootPath,
10258
+ expandPaths,
10259
+ expandLeafNodes,
10260
+ ontoggle,
10261
+ exposeApi,
10253
10262
  // tools: tools // Do not pass tools down as we
10254
10263
  // keep them attached to the top-level header
10255
10264
  nodeSelector: options.nodeSelector,
10265
+ enablePinning
10256
10266
  }
10257
10267
  ).appendTo(row);
10258
10268
  }
@@ -10261,7 +10271,7 @@ RED.utils = (function() {
10261
10271
  }
10262
10272
  },
10263
10273
  function(state) {if (ontoggle) { ontoggle(path,state);}},
10264
- checkExpanded(strippedKey,expandPaths));
10274
+ checkExpanded(strippedKey, expandPaths, { expandLeafNodes }));
10265
10275
  }
10266
10276
  if (key) {
10267
10277
  $('<span class="red-ui-debug-msg-type-meta"></span>').text(type).appendTo(entryObj);
@@ -15289,6 +15299,7 @@ RED.stack = (function() {
15289
15299
  pre: value.substring(0,idx),
15290
15300
  match: value.substring(idx,idx+len),
15291
15301
  post: value.substring(idx+len),
15302
+ exact: idx === 0 && value.length === searchValue.length
15292
15303
  }
15293
15304
  }
15294
15305
  function generateSpans(match) {
@@ -15309,7 +15320,7 @@ RED.stack = (function() {
15309
15320
  const srcMatch = getMatch(optSrc, val);
15310
15321
  if (valMatch.found || srcMatch.found) {
15311
15322
  const element = $('<div>',{style: "display: flex"});
15312
- const valEl = $('<div/>',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"});
15323
+ const valEl = $('<div/>',{ class: "red-ui-autoComplete-completion" });
15313
15324
  valEl.append(generateSpans(valMatch));
15314
15325
  valEl.appendTo(element);
15315
15326
  if (optSrc) {
@@ -15385,7 +15396,7 @@ RED.stack = (function() {
15385
15396
  if (valMatch.found) {
15386
15397
  const optSrc = envVarsMap[v]
15387
15398
  const element = $('<div>',{style: "display: flex"});
15388
- const valEl = $('<div/>',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"});
15399
+ const valEl = $('<div/>',{ class: "red-ui-autoComplete-completion" });
15389
15400
  valEl.append(generateSpans(valMatch))
15390
15401
  valEl.appendTo(element)
15391
15402
 
@@ -15427,7 +15438,7 @@ RED.stack = (function() {
15427
15438
  const that = this
15428
15439
  const getContextKeysFromRuntime = function(scope, store, searchKey, done) {
15429
15440
  contextKnownKeys[scope] = contextKnownKeys[scope] || {}
15430
- contextKnownKeys[scope][store] = contextKnownKeys[scope][store] || new Set()
15441
+ contextKnownKeys[scope][store] = contextKnownKeys[scope][store] || new Map()
15431
15442
  if (searchKey.length > 0) {
15432
15443
  try {
15433
15444
  RED.utils.normalisePropertyExpression(searchKey)
@@ -15449,11 +15460,12 @@ RED.stack = (function() {
15449
15460
  const result = data[store] || {}
15450
15461
  const keys = result.keys || []
15451
15462
  const keyPrefix = searchKey + (searchKey.length > 0 ? '.' : '')
15452
- keys.forEach(key => {
15463
+ keys.forEach(keyInfo => {
15464
+ const key = keyInfo.key
15453
15465
  if (/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(key)) {
15454
- contextKnownKeys[scope][store].add(keyPrefix + key)
15466
+ contextKnownKeys[scope][store].set(keyPrefix + key, keyInfo)
15455
15467
  } else {
15456
- contextKnownKeys[scope][store].add(searchKey + "[\""+key.replace(/"/,"\\\"")+"\"]")
15468
+ contextKnownKeys[scope][store].set(searchKey + "[\""+key.replace(/"/,"\\\"")+"\"]", keyInfo)
15457
15469
  }
15458
15470
  })
15459
15471
  done()
@@ -15468,14 +15480,14 @@ RED.stack = (function() {
15468
15480
  // Get the flow id of the node we're editing
15469
15481
  const editStack = RED.editor.getEditStack()
15470
15482
  if (editStack.length === 0) {
15471
- done([])
15483
+ done(new Map())
15472
15484
  return
15473
15485
  }
15474
15486
  const editingNode = editStack.pop()
15475
15487
  if (editingNode.z) {
15476
15488
  scope = `${scope}/${editingNode.z}`
15477
15489
  } else {
15478
- done([])
15490
+ done(new Map())
15479
15491
  return
15480
15492
  }
15481
15493
  }
@@ -15495,17 +15507,29 @@ RED.stack = (function() {
15495
15507
  return function(val, done) {
15496
15508
  getContextKeys(val, function (keys) {
15497
15509
  const matches = []
15498
- keys.forEach(v => {
15510
+ keys.forEach((keyInfo, v) => {
15499
15511
  let optVal = v
15500
15512
  let valMatch = getMatch(optVal, val);
15501
- if (!valMatch.found && val.length > 0 && val.endsWith('.')) {
15502
- // Search key ends in '.' - but doesn't match. Check again
15503
- // with [" at the end instead so we match bracket notation
15504
- valMatch = getMatch(optVal, val.substring(0, val.length - 1) + '["')
15513
+ if (!valMatch.found && val.length > 0) {
15514
+ if (val.endsWith('.')) {
15515
+ // Search key ends in '.' - but doesn't match. Check again
15516
+ // with [" at the end instead so we match bracket notation
15517
+ valMatch = getMatch(optVal, val.substring(0, val.length - 1) + '["')
15518
+ // } else if (val.endsWith('[') && /^array/.test(keyInfo.format)) {
15519
+ // console.log('this case')
15520
+ }
15505
15521
  }
15506
15522
  if (valMatch.found) {
15507
15523
  const element = $('<div>',{style: "display: flex"});
15508
- const valEl = $('<div/>',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"});
15524
+ const valEl = $('<div/>',{ class: "red-ui-autoComplete-completion" });
15525
+ // if (keyInfo.format) {
15526
+ // valMatch.post += ' ' + keyInfo.format
15527
+ // }
15528
+ if (valMatch.exact && /^array/.test(keyInfo.format)) {
15529
+ valMatch.post += `[0-${keyInfo.length}]`
15530
+ optVal += '['
15531
+
15532
+ }
15509
15533
  valEl.append(generateSpans(valMatch))
15510
15534
  valEl.appendTo(element)
15511
15535
  matches.push({
@@ -16793,7 +16817,8 @@ RED.stack = (function() {
16793
16817
  if (tooltip) {
16794
16818
  tooltip.setContent(valid);
16795
16819
  } else {
16796
- tooltip = RED.popover.tooltip(this.elementDiv, valid);
16820
+ const target = this.typeMap[type]?.options ? this.optionSelectLabel : this.elementDiv;
16821
+ tooltip = RED.popover.tooltip(target, valid);
16797
16822
  this.element.data("tooltip", tooltip);
16798
16823
  }
16799
16824
  }
@@ -17025,7 +17050,7 @@ RED.stack = (function() {
17025
17050
  }
17026
17051
  this.menu = RED.popover.menu({
17027
17052
  tabSelect: true,
17028
- width: 300,
17053
+ width: Math.max(300, this.element.width()),
17029
17054
  maxHeight: 200,
17030
17055
  class: "red-ui-autoComplete-container",
17031
17056
  options: completions,
@@ -17228,6 +17253,7 @@ RED.deploy = (function() {
17228
17253
  /**
17229
17254
  * options:
17230
17255
  * type: "default" - Button with drop-down options - no further customisation available
17256
+ * label: the text to display - default: "Deploy"
17231
17257
  * type: "simple" - Button without dropdown. Customisations:
17232
17258
  * label: the text to display - default: "Deploy"
17233
17259
  * icon : the icon to use. Null removes the icon. default: "red/images/deploy-full-o.svg"
@@ -17235,13 +17261,14 @@ RED.deploy = (function() {
17235
17261
  function init(options) {
17236
17262
  options = options || {};
17237
17263
  var type = options.type || "default";
17264
+ var label = options.label || RED._("deploy.deploy");
17238
17265
 
17239
17266
  if (type == "default") {
17240
17267
  $('<li><span class="red-ui-deploy-button-group button-group">'+
17241
17268
  '<a id="red-ui-header-button-deploy" class="red-ui-deploy-button disabled" href="#">'+
17242
17269
  '<span class="red-ui-deploy-button-content">'+
17243
17270
  '<img id="red-ui-header-button-deploy-icon" src="red/images/deploy-full-o.svg"> '+
17244
- '<span>'+RED._("deploy.deploy")+'</span>'+
17271
+ '<span>'+label+'</span>'+
17245
17272
  '</span>'+
17246
17273
  '<span class="red-ui-deploy-button-spinner hide">'+
17247
17274
  '<img src="red/images/spin.svg"/>'+
@@ -17262,7 +17289,6 @@ RED.deploy = (function() {
17262
17289
  mainMenuItems.push({id:"deploymenu-item-reload", icon:"red/images/deploy-reload.svg",label:RED._("deploy.restartFlows"),sublabel:RED._("deploy.restartFlowsDesc"),onselect:"core:restart-flows"})
17263
17290
  RED.menu.init({id:"red-ui-header-button-deploy-options", options: mainMenuItems });
17264
17291
  } else if (type == "simple") {
17265
- var label = options.label || RED._("deploy.deploy");
17266
17292
  var icon = 'red/images/deploy-full-o.svg';
17267
17293
  if (options.hasOwnProperty('icon')) {
17268
17294
  icon = options.icon;
@@ -23445,11 +23471,6 @@ RED.view = (function() {
23445
23471
  var targetGroup = options.group;
23446
23472
  var touchTrigger = options.touchTrigger;
23447
23473
 
23448
- if (targetGroup) {
23449
- selectedGroups.add(targetGroup,false);
23450
- RED.view.redraw();
23451
- }
23452
-
23453
23474
  // `point` is the place in the workspace the mouse has clicked.
23454
23475
  // This takes into account scrolling and scaling of the workspace.
23455
23476
  var ox = point[0];
@@ -23771,9 +23792,6 @@ RED.view = (function() {
23771
23792
  // auto select dropped node - so info shows (if visible)
23772
23793
  clearSelection();
23773
23794
  nn.selected = true;
23774
- if (targetGroup) {
23775
- selectedGroups.add(targetGroup,false);
23776
- }
23777
23795
  movingSet.add(nn);
23778
23796
  updateActiveNodes();
23779
23797
  updateSelection();
@@ -24358,19 +24376,24 @@ RED.view = (function() {
24358
24376
  n.n.moved = true;
24359
24377
  }
24360
24378
  }
24361
-
24362
- // Check to see if we need to splice a link
24379
+ // If a node has moved and ends up being spliced into a link, keep
24380
+ // track of which historyEvent to add the splice info to
24381
+ let targetSpliceEvent = null
24363
24382
  if (moveEvent.nodes.length > 0) {
24364
24383
  historyEvent.events.push(moveEvent)
24365
- if (activeSpliceLink) {
24366
- var linkToSplice = d3.select(activeSpliceLink).data()[0];
24367
- spliceLink(linkToSplice, movingSet.get(0).n, moveEvent)
24368
- }
24384
+ targetSpliceEvent = moveEvent
24369
24385
  }
24370
24386
  if (moveAndChangedGroupEvent.nodes.length > 0) {
24371
24387
  historyEvent.events.push(moveAndChangedGroupEvent)
24388
+ targetSpliceEvent = moveAndChangedGroupEvent
24372
24389
  }
24373
-
24390
+ // activeSpliceLink will only be set if the movingSet has a single
24391
+ // node that is able to splice.
24392
+ if (targetSpliceEvent && activeSpliceLink) {
24393
+ var linkToSplice = d3.select(activeSpliceLink).data()[0];
24394
+ spliceLink(linkToSplice, movingSet.get(0).n, targetSpliceEvent)
24395
+ }
24396
+
24374
24397
  // Only continue if something has moved
24375
24398
  if (historyEvent.events.length > 0) {
24376
24399
  RED.nodes.dirty(true);
@@ -33829,8 +33852,6 @@ RED.sidebar.context = (function() {
33829
33852
  var content;
33830
33853
  var sections;
33831
33854
 
33832
- var localCache = {};
33833
-
33834
33855
  var flowAutoRefresh;
33835
33856
  var nodeAutoRefresh;
33836
33857
  var nodeSection;
@@ -33838,6 +33859,8 @@ RED.sidebar.context = (function() {
33838
33859
  var flowSection;
33839
33860
  var globalSection;
33840
33861
 
33862
+ const expandedPaths = {}
33863
+
33841
33864
  var currentNode;
33842
33865
  var currentFlow;
33843
33866
 
@@ -34023,14 +34046,41 @@ RED.sidebar.context = (function() {
34023
34046
  var l = keys.length;
34024
34047
  for (var i = 0; i < l; i++) {
34025
34048
  sortedData[keys[i]].forEach(function(v) {
34026
- var k = keys[i];
34027
- var l2 = sortedData[k].length;
34028
- var propRow = $('<tr class="red-ui-help-info-row"><td class="red-ui-sidebar-context-property"></td><td></td></tr>').appendTo(container);
34029
- var obj = $(propRow.children()[0]);
34049
+ const k = keys[i];
34050
+ let payload = v.msg;
34051
+ let format = v.format;
34052
+ const tools = $('<span class="button-group"></span>');
34053
+ expandedPaths[id + "." + k] = expandedPaths[id + "." + k] || new Set()
34054
+ const objectElementOptions = {
34055
+ typeHint: format,
34056
+ sourceId: id + "." + k,
34057
+ tools,
34058
+ path: k,
34059
+ rootPath: k,
34060
+ exposeApi: true,
34061
+ ontoggle: function(path,state) {
34062
+ path = path.substring(k.length+1)
34063
+ if (state) {
34064
+ expandedPaths[id+"."+k].add(path)
34065
+ } else {
34066
+ // if 'a' has been collapsed, we want to remove 'a.b' and 'a[0]...' from the set
34067
+ // of collapsed paths
34068
+ for (let expandedPath of expandedPaths[id+"."+k]) {
34069
+ if (expandedPath.startsWith(path+".") || expandedPath.startsWith(path+"[")) {
34070
+ expandedPaths[id+"."+k].delete(expandedPath)
34071
+ }
34072
+ }
34073
+ expandedPaths[id+"."+k].delete(path)
34074
+ }
34075
+ },
34076
+ expandPaths: [ ...expandedPaths[id+"."+k] ].sort(),
34077
+ expandLeafNodes: true
34078
+ }
34079
+ const propRow = $('<tr class="red-ui-help-info-row"><td class="red-ui-sidebar-context-property"></td><td></td></tr>').appendTo(container);
34080
+ const obj = $(propRow.children()[0]);
34030
34081
  obj.text(k);
34031
- var tools = $('<span class="button-group"></span>');
34032
34082
  const urlSafeK = encodeURIComponent(k)
34033
- var refreshItem = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-refresh"></i></button>').appendTo(tools).on("click", function(e) {
34083
+ const refreshItem = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-refresh"></i></button>').appendTo(tools).on("click", function(e) {
34034
34084
  e.preventDefault();
34035
34085
  e.stopPropagation();
34036
34086
  $.getJSON(baseUrl+"/"+urlSafeK+"?store="+v.store, function(data) {
@@ -34040,16 +34090,14 @@ RED.sidebar.context = (function() {
34040
34090
  tools.detach();
34041
34091
  $(propRow.children()[1]).empty();
34042
34092
  RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), {
34093
+ ...objectElementOptions,
34043
34094
  typeHint: data.format,
34044
- sourceId: id+"."+k,
34045
- tools: tools,
34046
- path: k
34047
34095
  }).appendTo(propRow.children()[1]);
34048
34096
  }
34049
34097
  })
34050
34098
  });
34051
34099
  RED.popover.tooltip(refreshItem,RED._("sidebar.context.refrsh"));
34052
- var deleteItem = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-trash"></i></button>').appendTo(tools).on("click", function(e) {
34100
+ const deleteItem = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-trash"></i></button>').appendTo(tools).on("click", function(e) {
34053
34101
  e.preventDefault();
34054
34102
  e.stopPropagation();
34055
34103
  var popover = RED.popover.create({
@@ -34057,7 +34105,7 @@ RED.sidebar.context = (function() {
34057
34105
  target: propRow,
34058
34106
  direction: "left",
34059
34107
  content: function() {
34060
- var content = $('<div>');
34108
+ const content = $('<div>');
34061
34109
  $('<p data-i18n="sidebar.context.deleteConfirm"></p>').appendTo(content);
34062
34110
  var row = $('<p>').appendTo(content);
34063
34111
  var bg = $('<span class="button-group"></span>').appendTo(row);
@@ -34080,16 +34128,15 @@ RED.sidebar.context = (function() {
34080
34128
  if (container.children().length === 0) {
34081
34129
  $('<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();
34082
34130
  }
34131
+ delete expandedPaths[id + "." + k]
34083
34132
  } else {
34084
34133
  payload = data.msg;
34085
34134
  format = data.format;
34086
34135
  tools.detach();
34087
34136
  $(propRow.children()[1]).empty();
34088
34137
  RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), {
34089
- typeHint: data.format,
34090
- sourceId: id+"."+k,
34091
- tools: tools,
34092
- path: k
34138
+ ...objectElementOptions,
34139
+ typeHint: data.format
34093
34140
  }).appendTo(propRow.children()[1]);
34094
34141
  }
34095
34142
  });
@@ -34104,14 +34151,7 @@ RED.sidebar.context = (function() {
34104
34151
 
34105
34152
  });
34106
34153
  RED.popover.tooltip(deleteItem,RED._("sidebar.context.delete"));
34107
- var payload = v.msg;
34108
- var format = v.format;
34109
- RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), {
34110
- typeHint: v.format,
34111
- sourceId: id+"."+k,
34112
- tools: tools,
34113
- path: k
34114
- }).appendTo(propRow.children()[1]);
34154
+ RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), objectElementOptions).appendTo(propRow.children()[1]);
34115
34155
  if (contextStores.length > 1) {
34116
34156
  $("<span>",{class:"red-ui-sidebar-context-property-storename"}).text(v.store).appendTo($(propRow.children()[0]))
34117
34157
  }
@@ -37449,8 +37489,18 @@ RED.editor = (function() {
37449
37489
  }
37450
37490
  });
37451
37491
  }
37452
-
37492
+ let envToRemove = new Set()
37453
37493
  if (!isSameObj(old_env, new_env)) {
37494
+ // Get a list of env properties that have been removed
37495
+ // by comparing old_env and new_env
37496
+ if (old_env) {
37497
+ old_env.forEach(env => { envToRemove.add(env.name) })
37498
+ }
37499
+ if (new_env) {
37500
+ new_env.forEach(env => {
37501
+ envToRemove.delete(env.name)
37502
+ })
37503
+ }
37454
37504
  editState.changes.env = editing_node.env;
37455
37505
  editing_node.env = new_env;
37456
37506
  editState.changed = true;
@@ -37459,10 +37509,11 @@ RED.editor = (function() {
37459
37509
 
37460
37510
 
37461
37511
  if (editState.changed) {
37462
- var wasChanged = editing_node.changed;
37512
+ let wasChanged = editing_node.changed;
37463
37513
  editing_node.changed = true;
37464
37514
  validateNode(editing_node);
37465
- var subflowInstances = [];
37515
+ let subflowInstances = [];
37516
+ let instanceHistoryEvents = []
37466
37517
  RED.nodes.eachNode(function(n) {
37467
37518
  if (n.type == "subflow:"+editing_node.id) {
37468
37519
  subflowInstances.push({
@@ -37472,13 +37523,35 @@ RED.editor = (function() {
37472
37523
  n._def.color = editing_node.color;
37473
37524
  n.changed = true;
37474
37525
  n.dirty = true;
37526
+ if (n.env) {
37527
+ const oldEnv = n.env
37528
+ const newEnv = []
37529
+ let envChanged = false
37530
+ n.env.forEach((env, index) => {
37531
+ if (envToRemove.has(env.name)) {
37532
+ envChanged = true
37533
+ } else {
37534
+ newEnv.push(env)
37535
+ }
37536
+ })
37537
+ if (envChanged) {
37538
+ instanceHistoryEvents.push({
37539
+ t: 'edit',
37540
+ node: n,
37541
+ changes: { env: oldEnv },
37542
+ dirty: n.dirty,
37543
+ changed: n.changed
37544
+ })
37545
+ n.env = newEnv
37546
+ }
37547
+ }
37475
37548
  updateNodeProperties(n);
37476
37549
  validateNode(n);
37477
37550
  }
37478
37551
  });
37479
37552
  RED.events.emit("subflows:change",editing_node);
37480
37553
  RED.nodes.dirty(true);
37481
- var historyEvent = {
37554
+ let historyEvent = {
37482
37555
  t:'edit',
37483
37556
  node:editing_node,
37484
37557
  changes:editState.changes,
@@ -37488,7 +37561,13 @@ RED.editor = (function() {
37488
37561
  instances:subflowInstances
37489
37562
  }
37490
37563
  };
37491
-
37564
+ if (instanceHistoryEvents.length > 0) {
37565
+ historyEvent = {
37566
+ t: 'multi',
37567
+ events: [ historyEvent, ...instanceHistoryEvents ],
37568
+ dirty: wasDirty
37569
+ }
37570
+ }
37492
37571
  RED.history.push(historyEvent);
37493
37572
  }
37494
37573
  editing_node.dirty = true;
@@ -42934,6 +43013,7 @@ RED.editor.codeEditor.monaco = (function() {
42934
43013
  2322, //Type 'unknown' is not assignable to type 'string'
42935
43014
  2339, //property does not exist on
42936
43015
  2345, //Argument of type xxx is not assignable to parameter of type 'DateTimeFormatOptions'
43016
+ 2538, //Ignore symbols as index property error.
42937
43017
  7043, //i forget what this one is,
42938
43018
  80001, //Convert to ES6 module
42939
43019
  80004, //JSDoc types may be moved to TypeScript types.
@@ -45783,10 +45863,15 @@ RED.library = (function() {
45783
45863
  if (lib.types && lib.types.indexOf(options.url) === -1) {
45784
45864
  return;
45785
45865
  }
45866
+ let icon = 'fa fa-hdd-o';
45867
+ if (lib.icon) {
45868
+ const fullIcon = RED.utils.separateIconPath(lib.icon);
45869
+ icon = (fullIcon.module==="font-awesome"?"fa ":"")+fullIcon.file;
45870
+ }
45786
45871
  listing.push({
45787
45872
  library: lib.id,
45788
45873
  type: options.url,
45789
- icon: lib.icon || 'fa fa-hdd-o',
45874
+ icon,
45790
45875
  label: RED._(lib.label||lib.id),
45791
45876
  path: "",
45792
45877
  expanded: true,
@@ -45841,10 +45926,15 @@ RED.library = (function() {
45841
45926
  if (lib.types && lib.types.indexOf(options.url) === -1) {
45842
45927
  return;
45843
45928
  }
45929
+ let icon = 'fa fa-hdd-o';
45930
+ if (lib.icon) {
45931
+ const fullIcon = RED.utils.separateIconPath(lib.icon);
45932
+ icon = (fullIcon.module==="font-awesome"?"fa ":"")+fullIcon.file;
45933
+ }
45844
45934
  listing.push({
45845
45935
  library: lib.id,
45846
45936
  type: options.url,
45847
- icon: lib.icon || 'fa fa-hdd-o',
45937
+ icon,
45848
45938
  label: RED._(lib.label||lib.id),
45849
45939
  path: "",
45850
45940
  expanded: true,