@node-red/editor-client 4.0.2 → 4.0.3

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
@@ -2386,16 +2386,36 @@ RED.comms = (function() {
2386
2386
  break
2387
2387
  }
2388
2388
  }
2389
+ if (isInWorkspace) {
2390
+ const chart = $('#red-ui-workspace-chart')
2391
+ const chartOffset = chart.offset()
2392
+ const scaleFactor = RED.view.scale()
2393
+ location.cursor = {
2394
+ x: (lastPosition[0] - chartOffset.left + chart.scrollLeft()) / scaleFactor,
2395
+ y: (lastPosition[1] - chartOffset.top + chart.scrollTop()) / scaleFactor
2396
+ }
2397
+ }
2389
2398
  return location
2390
2399
  }
2400
+
2401
+ let publishLocationTimeout
2402
+ let lastPosition = [0,0]
2403
+ let isInWorkspace = false
2404
+
2391
2405
  function publishLocation () {
2392
- const location = getLocation()
2393
- if (location.workspace !== 0) {
2394
- log('send', 'multiplayer/location', location)
2395
- RED.comms.send('multiplayer/location', location)
2406
+ if (!publishLocationTimeout) {
2407
+ publishLocationTimeout = setTimeout(() => {
2408
+ const location = getLocation()
2409
+ if (location.workspace !== 0) {
2410
+ log('send', 'multiplayer/location', location)
2411
+ RED.comms.send('multiplayer/location', location)
2412
+ }
2413
+ publishLocationTimeout = null
2414
+ }, 100)
2396
2415
  }
2397
2416
  }
2398
2417
 
2418
+
2399
2419
  function revealUser(location, skipWorkspace) {
2400
2420
  if (location.node) {
2401
2421
  // Need to check if this is a known node, so we can fall back to revealing
@@ -2557,7 +2577,16 @@ RED.comms = (function() {
2557
2577
 
2558
2578
  function removeUserLocation (sessionId) {
2559
2579
  updateUserLocation(sessionId, {})
2580
+ removeUserCursor(sessionId)
2560
2581
  }
2582
+ function removeUserCursor (sessionId) {
2583
+ // return
2584
+ if (sessions[sessionId]?.cursor) {
2585
+ sessions[sessionId].cursor.parentNode.removeChild(sessions[sessionId].cursor)
2586
+ delete sessions[sessionId].cursor
2587
+ }
2588
+ }
2589
+
2561
2590
  function updateUserLocation (sessionId, location) {
2562
2591
  let viewTouched = false
2563
2592
  const oldLocation = sessions[sessionId].location
@@ -2577,6 +2606,28 @@ RED.comms = (function() {
2577
2606
  // console.log(`updateUserLocation sessionId:${sessionId} oldWS:${oldLocation?.workspace} newWS:${location.workspace}`)
2578
2607
  if (location.workspace) {
2579
2608
  getWorkspaceTray(location.workspace).addUser(sessionId)
2609
+ if (location.cursor && location.workspace === RED.workspaces.active()) {
2610
+ if (!sessions[sessionId].cursor) {
2611
+ const user = sessions[sessionId].user
2612
+ const cursorIcon = document.createElementNS("http://www.w3.org/2000/svg","g");
2613
+ cursorIcon.setAttribute("class", "red-ui-multiplayer-annotation")
2614
+ cursorIcon.appendChild(createAnnotationUser(user, true))
2615
+ $(cursorIcon).css({
2616
+ transform: `translate( ${location.cursor.x}px, ${location.cursor.y}px)`,
2617
+ transition: 'transform 0.1s linear'
2618
+ })
2619
+ $("#red-ui-workspace-chart svg").append(cursorIcon)
2620
+ sessions[sessionId].cursor = cursorIcon
2621
+ } else {
2622
+ const cursorIcon = sessions[sessionId].cursor
2623
+ $(cursorIcon).css({
2624
+ transform: `translate( ${location.cursor.x}px, ${location.cursor.y}px)`
2625
+ })
2626
+
2627
+ }
2628
+ } else if (sessions[sessionId].cursor) {
2629
+ removeUserCursor(sessionId)
2630
+ }
2580
2631
  }
2581
2632
  if (location.node) {
2582
2633
  addUserToNode(sessionId, location.node)
@@ -2595,67 +2646,69 @@ RED.comms = (function() {
2595
2646
  // }
2596
2647
  // }
2597
2648
 
2598
- return {
2599
- init: function () {
2600
2649
 
2601
- function createAnnotationUser(user) {
2602
-
2603
- const group = document.createElementNS("http://www.w3.org/2000/svg","g");
2604
- const badge = document.createElementNS("http://www.w3.org/2000/svg","circle");
2605
- const radius = 20
2606
- badge.setAttribute("cx",radius/2);
2607
- badge.setAttribute("cy",radius/2);
2608
- badge.setAttribute("r",radius/2);
2609
- badge.setAttribute("class", "red-ui-multiplayer-annotation-background")
2610
- group.appendChild(badge)
2611
- if (user && user.profileColor !== undefined) {
2612
- badge.setAttribute("class", "red-ui-multiplayer-annotation-background red-ui-user-profile-color-" + user.profileColor)
2613
- }
2614
- if (user && user.image) {
2615
- const image = document.createElementNS("http://www.w3.org/2000/svg","image");
2616
- image.setAttribute("width", radius)
2617
- image.setAttribute("height", radius)
2618
- image.setAttribute("href", user.image)
2619
- image.setAttribute("clip-path", "circle("+Math.floor(radius/2)+")")
2620
- group.appendChild(image)
2621
- } else if (user && user.anonymous) {
2622
- const anonIconHead = document.createElementNS("http://www.w3.org/2000/svg","circle");
2623
- anonIconHead.setAttribute("cx", radius/2)
2624
- anonIconHead.setAttribute("cy", radius/2 - 2)
2625
- anonIconHead.setAttribute("r", 2.4)
2626
- anonIconHead.setAttribute("class","red-ui-multiplayer-annotation-anon-label");
2627
- group.appendChild(anonIconHead)
2628
- const anonIconBody = document.createElementNS("http://www.w3.org/2000/svg","path");
2629
- anonIconBody.setAttribute("class","red-ui-multiplayer-annotation-anon-label");
2630
- // anonIconBody.setAttribute("d",`M ${radius/2 - 4} ${radius/2 + 1} h 8 v4 h -8 z`);
2631
- 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`);
2632
- group.appendChild(anonIconBody)
2633
- } else {
2634
- const labelText = user.username ? user.username.substring(0,2) : user
2635
- const label = document.createElementNS("http://www.w3.org/2000/svg","text");
2636
- if (user.username) {
2637
- label.setAttribute("class","red-ui-multiplayer-annotation-label");
2638
- label.textContent = user.username.substring(0,2)
2639
- } else {
2640
- label.setAttribute("class","red-ui-multiplayer-annotation-label red-ui-multiplayer-user-count")
2641
- label.textContent = user
2642
- }
2643
- label.setAttribute("text-anchor", "middle")
2644
- label.setAttribute("x",radius/2);
2645
- label.setAttribute("y",radius/2 + 3);
2646
- group.appendChild(label)
2647
- }
2648
- const border = document.createElementNS("http://www.w3.org/2000/svg","circle");
2649
- border.setAttribute("cx",radius/2);
2650
- border.setAttribute("cy",radius/2);
2651
- border.setAttribute("r",radius/2);
2652
- border.setAttribute("class", "red-ui-multiplayer-annotation-border")
2653
- group.appendChild(border)
2654
-
2650
+ function createAnnotationUser(user, pointer = false) {
2651
+ const radius = 20
2652
+ const halfRadius = radius/2
2653
+ const group = document.createElementNS("http://www.w3.org/2000/svg","g");
2654
+ const badge = document.createElementNS("http://www.w3.org/2000/svg","path");
2655
+ let shapePath
2656
+ if (!pointer) {
2657
+ shapePath = `M 0 ${halfRadius} a ${halfRadius} ${halfRadius} 0 1 1 ${radius} 0 a ${halfRadius} ${halfRadius} 0 1 1 -${radius} 0 z`
2658
+ } else {
2659
+ shapePath = `M 0 0 h ${halfRadius} a ${halfRadius} ${halfRadius} 0 1 1 -${halfRadius} ${halfRadius} z`
2660
+ }
2661
+ badge.setAttribute('d', shapePath)
2662
+ badge.setAttribute("class", "red-ui-multiplayer-annotation-background")
2663
+ group.appendChild(badge)
2664
+ if (user && user.profileColor !== undefined) {
2665
+ badge.setAttribute("class", "red-ui-multiplayer-annotation-background red-ui-user-profile-color-" + user.profileColor)
2666
+ }
2667
+ if (user && user.image) {
2668
+ const image = document.createElementNS("http://www.w3.org/2000/svg","image");
2669
+ image.setAttribute("width", radius)
2670
+ image.setAttribute("height", radius)
2671
+ image.setAttribute("href", user.image)
2672
+ image.setAttribute("clip-path", "circle("+Math.floor(radius/2)+")")
2673
+ group.appendChild(image)
2674
+ } else if (user && user.anonymous) {
2675
+ const anonIconHead = document.createElementNS("http://www.w3.org/2000/svg","circle");
2676
+ anonIconHead.setAttribute("cx", radius/2)
2677
+ anonIconHead.setAttribute("cy", radius/2 - 2)
2678
+ anonIconHead.setAttribute("r", 2.4)
2679
+ anonIconHead.setAttribute("class","red-ui-multiplayer-annotation-anon-label");
2680
+ group.appendChild(anonIconHead)
2681
+ const anonIconBody = document.createElementNS("http://www.w3.org/2000/svg","path");
2682
+ anonIconBody.setAttribute("class","red-ui-multiplayer-annotation-anon-label");
2683
+ // anonIconBody.setAttribute("d",`M ${radius/2 - 4} ${radius/2 + 1} h 8 v4 h -8 z`);
2684
+ 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`);
2685
+ group.appendChild(anonIconBody)
2686
+ } else {
2687
+ const labelText = user.username ? user.username.substring(0,2) : user
2688
+ const label = document.createElementNS("http://www.w3.org/2000/svg","text");
2689
+ if (user.username) {
2690
+ label.setAttribute("class","red-ui-multiplayer-annotation-label");
2691
+ label.textContent = user.username.substring(0,2)
2692
+ } else {
2693
+ label.setAttribute("class","red-ui-multiplayer-annotation-label red-ui-multiplayer-user-count")
2694
+ label.textContent = user
2695
+ }
2696
+ label.setAttribute("text-anchor", "middle")
2697
+ label.setAttribute("x",radius/2);
2698
+ label.setAttribute("y",radius/2 + 3);
2699
+ group.appendChild(label)
2700
+ }
2701
+ const border = document.createElementNS("http://www.w3.org/2000/svg","path");
2702
+ border.setAttribute('d', shapePath)
2703
+ border.setAttribute("class", "red-ui-multiplayer-annotation-border")
2704
+ group.appendChild(border)
2705
+ return group
2706
+ }
2655
2707
 
2708
+ return {
2709
+ init: function () {
2656
2710
 
2657
- return group
2658
- }
2711
+
2659
2712
 
2660
2713
  RED.view.annotations.register("red-ui-multiplayer",{
2661
2714
  type: 'badge',
@@ -2765,6 +2818,24 @@ RED.comms = (function() {
2765
2818
  RED.comms.send('multiplayer/disconnect', disconnectInfo)
2766
2819
  RED.settings.removeLocal('multiplayer:sessionId')
2767
2820
  })
2821
+
2822
+ const chart = $('#red-ui-workspace-chart')
2823
+ chart.on('mousemove', function (evt) {
2824
+ lastPosition[0] = evt.clientX
2825
+ lastPosition[1] = evt.clientY
2826
+ publishLocation()
2827
+ })
2828
+ chart.on('scroll', function (evt) {
2829
+ publishLocation()
2830
+ })
2831
+ chart.on('mouseenter', function () {
2832
+ isInWorkspace = true
2833
+ publishLocation()
2834
+ })
2835
+ chart.on('mouseleave', function () {
2836
+ isInWorkspace = false
2837
+ publishLocation()
2838
+ })
2768
2839
  }
2769
2840
  }
2770
2841
 
@@ -6757,11 +6828,24 @@ RED.nodes = (function() {
6757
6828
  } else {
6758
6829
  delete n.g
6759
6830
  }
6760
- // If importing into a subflow, ensure an outbound-link doesn't get added
6761
- if (activeSubflow && /^link /.test(n.type) && n.links) {
6831
+ // If importing a link node, ensure both ends of each link are either:
6832
+ // - not in a subflow
6833
+ // - both in the same subflow
6834
+ if (/^link /.test(n.type) && n.links) {
6762
6835
  n.links = n.links.filter(function(id) {
6763
6836
  const otherNode = node_map[id] || RED.nodes.node(id);
6764
- return (otherNode && otherNode.z === activeWorkspace);
6837
+ if (!otherNode) {
6838
+ // Cannot find other end - remove the link
6839
+ return false
6840
+ }
6841
+ if (otherNode.z === n.z) {
6842
+ // Both ends in the same flow/subflow
6843
+ return true
6844
+ } else if (!!getSubflow(n.z) || !!getSubflow(otherNode.z)) {
6845
+ // One end is in a subflow - remove the link
6846
+ return false
6847
+ }
6848
+ return true
6765
6849
  });
6766
6850
  }
6767
6851
  for (var d3 in n._def.defaults) {
@@ -18099,7 +18183,7 @@ RED.diagnostics = (function () {
18099
18183
  }
18100
18184
  })
18101
18185
  if (c === 0) {
18102
- result.text("none");
18186
+ result.text(RED._("diff.type.none"));
18103
18187
  } else {
18104
18188
  list.appendTo(result);
18105
18189
  }
@@ -18423,7 +18507,7 @@ RED.diagnostics = (function () {
18423
18507
  conflict = true;
18424
18508
  }
18425
18509
  row = $("<tr>").appendTo(nodePropertiesTableBody);
18426
- $("<td>",{class:"red-ui-diff-list-cell-label"}).text("position").appendTo(row);
18510
+ $("<td>",{class:"red-ui-diff-list-cell-label"}).text(RED._("diff.type.position")).appendTo(row);
18427
18511
  localCell = $("<td>",{class:"red-ui-diff-list-cell red-ui-diff-list-node-local"}).appendTo(row);
18428
18512
  if (localNode) {
18429
18513
  localCell.addClass("red-ui-diff-status-"+(localChanged?"moved":"unchanged"));
@@ -18501,7 +18585,7 @@ RED.diagnostics = (function () {
18501
18585
  conflict = true;
18502
18586
  }
18503
18587
  row = $("<tr>").appendTo(nodePropertiesTableBody);
18504
- $("<td>",{class:"red-ui-diff-list-cell-label"}).text("wires").appendTo(row);
18588
+ $("<td>",{class:"red-ui-diff-list-cell-label"}).text(RED._("diff.type.wires")).appendTo(row);
18505
18589
  localCell = $("<td>",{class:"red-ui-diff-list-cell red-ui-diff-list-node-local"}).appendTo(row);
18506
18590
  if (localNode) {
18507
18591
  if (!conflict) {
@@ -19631,15 +19715,14 @@ RED.diagnostics = (function () {
19631
19715
  if (!isSeparator) {
19632
19716
  var isOurs = /^..<<<<<<</.test(lineText);
19633
19717
  if (isOurs) {
19634
- $('<span>').text("<<<<<<< Local Changes").appendTo(line);
19718
+ $('<span>').text("<<<<<<< " + RED._("diff.localChanges")).appendTo(line);
19635
19719
  hunk.localChangeStart = actualLineNumber;
19636
19720
  } else {
19637
19721
  hunk.remoteChangeEnd = actualLineNumber;
19638
- $('<span>').text(">>>>>>> Remote Changes").appendTo(line);
19639
-
19722
+ $('<span>').text(">>>>>>> " + RED._("diff.remoteChanges")).appendTo(line);
19640
19723
  }
19641
19724
  diffRow.addClass("mergeHeader-"+(isOurs?"ours":"theirs"));
19642
- $('<button class="red-ui-button red-ui-button-small" style="float: right; margin-right: 20px;"><i class="fa fa-angle-double-'+(isOurs?"down":"up")+'"></i> use '+(isOurs?"local":"remote")+' changes</button>')
19725
+ $('<button class="red-ui-button red-ui-button-small" style="float: right; margin-right: 20px;"><i class="fa fa-angle-double-'+(isOurs?"down":"up")+'"></i> '+RED._(isOurs?"diff.useLocalChanges":"diff.useRemoteChanges")+'</button>')
19643
19726
  .appendTo(line)
19644
19727
  .on("click", function(evt) {
19645
19728
  evt.preventDefault();
@@ -19721,7 +19804,7 @@ RED.diagnostics = (function () {
19721
19804
  $("<h3>").text(commit.title).appendTo(content);
19722
19805
  $('<div class="commit-body"></div>').text(commit.comment).appendTo(content);
19723
19806
  var summary = $('<div class="commit-summary"></div>').appendTo(content);
19724
- $('<div style="float: right">').text("Commit "+commit.sha).appendTo(summary);
19807
+ $('<div style="float: right">').text(RED._('diff.commit')+" "+commit.sha).appendTo(summary);
19725
19808
  $('<div>').text((commit.authorName||commit.author)+" - "+options.date).appendTo(summary);
19726
19809
 
19727
19810
  if (commit.files) {
@@ -21013,25 +21096,29 @@ RED.workspaces = (function() {
21013
21096
  },
21014
21097
  null)
21015
21098
  }
21016
- menuItems.push(
21017
- {
21018
- id:"red-ui-tabs-menu-option-add-flow",
21019
- label: RED._("workspace.addFlow"),
21020
- onselect: "core:add-flow"
21021
- }
21022
- )
21023
- if (isMenuButton || !!tab) {
21099
+ if (RED.settings.theme("menu.menu-item-workspace-add", true)) {
21024
21100
  menuItems.push(
21025
21101
  {
21026
- id:"red-ui-tabs-menu-option-add-flow-right",
21027
- label: RED._("workspace.addFlowToRight"),
21028
- shortcut: RED.keyboard.getShortcut("core:add-flow-to-right"),
21029
- onselect: function() {
21030
- RED.actions.invoke("core:add-flow-to-right", tab)
21031
- }
21032
- },
21033
- null
21102
+ id:"red-ui-tabs-menu-option-add-flow",
21103
+ label: RED._("workspace.addFlow"),
21104
+ onselect: "core:add-flow"
21105
+ }
21034
21106
  )
21107
+ }
21108
+ if (isMenuButton || !!tab) {
21109
+ if (RED.settings.theme("menu.menu-item-workspace-add", true)) {
21110
+ menuItems.push(
21111
+ {
21112
+ id:"red-ui-tabs-menu-option-add-flow-right",
21113
+ label: RED._("workspace.addFlowToRight"),
21114
+ shortcut: RED.keyboard.getShortcut("core:add-flow-to-right"),
21115
+ onselect: function() {
21116
+ RED.actions.invoke("core:add-flow-to-right", tab)
21117
+ }
21118
+ },
21119
+ null
21120
+ )
21121
+ }
21035
21122
  if (activeWorkspace && activeWorkspace.type === 'tab') {
21036
21123
  menuItems.push(
21037
21124
  isFlowDisabled ? {
@@ -21085,7 +21172,9 @@ RED.workspaces = (function() {
21085
21172
  }
21086
21173
  )
21087
21174
  }
21088
- menuItems.push(null)
21175
+ if (menuItems.length > 0) {
21176
+ menuItems.push(null)
21177
+ }
21089
21178
  if (isMenuButton || !!tab) {
21090
21179
  menuItems.push(
21091
21180
  {
@@ -21129,19 +21218,24 @@ RED.workspaces = (function() {
21129
21218
  }
21130
21219
  )
21131
21220
  if (tab) {
21221
+ menuItems.push(null)
21222
+
21223
+ if (RED.settings.theme("menu.menu-item-workspace-delete", true)) {
21224
+ menuItems.push(
21225
+ {
21226
+ label: RED._("common.label.delete"),
21227
+ onselect: function() {
21228
+ if (tab.type === 'tab') {
21229
+ RED.workspaces.delete(tab)
21230
+ } else if (tab.type === 'subflow') {
21231
+ RED.subflow.delete(tab.id)
21232
+ }
21233
+ },
21234
+ disabled: isCurrentLocked || (workspaceTabCount === 1)
21235
+ }
21236
+ )
21237
+ }
21132
21238
  menuItems.push(
21133
- null,
21134
- {
21135
- label: RED._("common.label.delete"),
21136
- onselect: function() {
21137
- if (tab.type === 'tab') {
21138
- RED.workspaces.delete(tab)
21139
- } else if (tab.type === 'subflow') {
21140
- RED.subflow.delete(tab.id)
21141
- }
21142
- },
21143
- disabled: isCurrentLocked || (workspaceTabCount === 1)
21144
- },
21145
21239
  {
21146
21240
  label: RED._("menu.label.export"),
21147
21241
  shortcut: RED.keyboard.getShortcut("core:show-export-dialog"),
@@ -21298,7 +21392,7 @@ RED.workspaces = (function() {
21298
21392
  },
21299
21393
  minimumActiveTabWidth: 150,
21300
21394
  scrollable: true,
21301
- addButton: "core:add-flow",
21395
+ addButton: RED.settings.theme("menu.menu-item-workspace-add", true) ? "core:add-flow" : undefined,
21302
21396
  addButtonCaption: RED._("workspace.addFlow"),
21303
21397
  menu: function() { return getMenuItems(true) },
21304
21398
  contextmenu: function(tab) { return getMenuItems(false, tab) }
@@ -21355,19 +21449,24 @@ RED.workspaces = (function() {
21355
21449
  $(window).on("resize", function() {
21356
21450
  workspace_tabs.resize();
21357
21451
  });
21358
-
21359
- RED.actions.add("core:add-flow",function(opts) { addWorkspace(undefined,undefined,opts?opts.index:undefined)});
21360
- RED.actions.add("core:add-flow-to-right",function(workspace) {
21361
- let index
21362
- if (workspace) {
21363
- index = workspace_tabs.getTabIndex(workspace.id)+1
21364
- } else {
21365
- index = workspace_tabs.activeIndex()+1
21366
- }
21367
- addWorkspace(undefined,undefined,index)
21368
- });
21369
- RED.actions.add("core:edit-flow",editWorkspace);
21370
- RED.actions.add("core:remove-flow",removeWorkspace);
21452
+ if (RED.settings.theme("menu.menu-item-workspace-add", true)) {
21453
+ RED.actions.add("core:add-flow",function(opts) { addWorkspace(undefined,undefined,opts?opts.index:undefined)});
21454
+ RED.actions.add("core:add-flow-to-right",function(workspace) {
21455
+ let index
21456
+ if (workspace) {
21457
+ index = workspace_tabs.getTabIndex(workspace.id)+1
21458
+ } else {
21459
+ index = workspace_tabs.activeIndex()+1
21460
+ }
21461
+ addWorkspace(undefined,undefined,index)
21462
+ });
21463
+ }
21464
+ if (RED.settings.theme("menu.menu-item-workspace-edit", true)) {
21465
+ RED.actions.add("core:edit-flow",editWorkspace);
21466
+ }
21467
+ if (RED.settings.theme("menu.menu-item-workspace-delete", true)) {
21468
+ RED.actions.add("core:remove-flow",removeWorkspace);
21469
+ }
21371
21470
  RED.actions.add("core:enable-flow",enableWorkspace);
21372
21471
  RED.actions.add("core:disable-flow",disableWorkspace);
21373
21472
  RED.actions.add("core:lock-flow",lockWorkspace);
@@ -21734,6 +21833,17 @@ RED.workspaces = (function() {
21734
21833
  }
21735
21834
  },
21736
21835
  refresh: function() {
21836
+ var workspace = RED.nodes.workspace(RED.workspaces.active());
21837
+ if (workspace) {
21838
+ document.title = `${documentTitle} : ${workspace.label}`;
21839
+ } else {
21840
+ var subflow = RED.nodes.subflow(RED.workspaces.active());
21841
+ if (subflow) {
21842
+ document.title = `${documentTitle} : ${subflow.name}`;
21843
+ } else {
21844
+ document.title = documentTitle
21845
+ }
21846
+ }
21737
21847
  RED.nodes.eachWorkspace(function(ws) {
21738
21848
  workspace_tabs.renameTab(ws.id,ws.label);
21739
21849
  $("#red-ui-tab-"+(ws.id.replace(".","-"))).attr("flowname",ws.label)
@@ -23016,7 +23126,10 @@ RED.view = (function() {
23016
23126
  lasso = null;
23017
23127
  }
23018
23128
  if (d3.event.touches || d3.event.button === 0) {
23019
- if ((mouse_mode === 0 || mouse_mode === RED.state.QUICK_JOINING) && isControlPressed(d3.event) && !(d3.event.altKey || d3.event.shiftKey)) {
23129
+ if (
23130
+ (mouse_mode === 0 && isControlPressed(d3.event) && !(d3.event.altKey || d3.event.shiftKey)) ||
23131
+ mouse_mode === RED.state.QUICK_JOINING
23132
+ ) {
23020
23133
  // Trigger quick add dialog
23021
23134
  d3.event.stopPropagation();
23022
23135
  clearSelection();
@@ -23092,7 +23205,6 @@ RED.view = (function() {
23092
23205
  }
23093
23206
 
23094
23207
  var mainPos = $("#red-ui-main-container").position();
23095
-
23096
23208
  if (mouse_mode !== RED.state.QUICK_JOINING) {
23097
23209
  mouse_mode = RED.state.QUICK_JOINING;
23098
23210
  $(window).on('keyup',disableQuickJoinEventHandler);
@@ -24864,8 +24976,8 @@ RED.view = (function() {
24864
24976
  }
24865
24977
 
24866
24978
  function disableQuickJoinEventHandler(evt) {
24867
- // Check for ctrl (all browsers), "Meta" (Chrome/FF), keyCode 91 (Safari)
24868
- if (evt.keyCode === 17 || evt.key === "Meta" || evt.keyCode === 91) {
24979
+ // Check for ctrl (all browsers), "Meta" (Chrome/FF), keyCode 91 (Safari), or Escape
24980
+ if (evt.keyCode === 17 || evt.key === "Meta" || evt.keyCode === 91 || evt.keyCode === 27) {
24869
24981
  resetMouseVars();
24870
24982
  hideDragLines();
24871
24983
  redraw();
@@ -24996,27 +25108,59 @@ RED.view = (function() {
24996
25108
 
24997
25109
  for (i=0;i<drag_lines.length;i++) {
24998
25110
  if (portType != drag_lines[i].portType && mouseup_node !== drag_lines[i].node) {
24999
- var drag_line = drag_lines[i];
25000
- var src,dst,src_port;
25111
+ let drag_line = drag_lines[i];
25112
+ let src,dst,src_port;
25113
+ let oldDst;
25114
+ let oldSrc;
25001
25115
  if (drag_line.portType === PORT_TYPE_OUTPUT) {
25002
25116
  src = drag_line.node;
25003
25117
  src_port = drag_line.port;
25004
25118
  dst = mouseup_node;
25119
+ oldSrc = src;
25120
+ if (drag_line.link) {
25121
+ oldDst = drag_line.link.target;
25122
+ }
25005
25123
  } else if (drag_line.portType === PORT_TYPE_INPUT) {
25006
25124
  src = mouseup_node;
25007
25125
  dst = drag_line.node;
25008
25126
  src_port = portIndex || 0;
25127
+ oldSrc = dst;
25128
+ if (drag_line.link) {
25129
+ oldDst = drag_line.link.source
25130
+ }
25009
25131
  }
25010
25132
  var link = {source: src, sourcePort:src_port, target: dst};
25011
25133
  if (drag_line.virtualLink) {
25012
25134
  if (/^link (in|out)$/.test(src.type) && /^link (in|out)$/.test(dst.type) && src.type !== dst.type) {
25013
25135
  if (src.links.indexOf(dst.id) === -1 && dst.links.indexOf(src.id) === -1) {
25014
- var oldSrcLinks = $.extend(true,{},{v:src.links}).v
25015
- var oldDstLinks = $.extend(true,{},{v:dst.links}).v
25136
+ var oldSrcLinks = [...src.links]
25137
+ var oldDstLinks = [...dst.links]
25138
+
25016
25139
  src.links.push(dst.id);
25017
25140
  dst.links.push(src.id);
25141
+
25142
+ if (oldDst) {
25143
+ src.links = src.links.filter(id => id !== oldDst.id)
25144
+ dst.links = dst.links.filter(id => id !== oldDst.id)
25145
+ var oldOldDstLinks = [...oldDst.links]
25146
+ oldDst.links = oldDst.links.filter(id => id !== oldSrc.id)
25147
+ oldDst.dirty = true;
25148
+ modifiedNodes.push(oldDst);
25149
+ linkEditEvents.push({
25150
+ t:'edit',
25151
+ node: oldDst,
25152
+ dirty: RED.nodes.dirty(),
25153
+ changed: oldDst.changed,
25154
+ changes: {
25155
+ links:oldOldDstLinks
25156
+ }
25157
+ });
25158
+ oldDst.changed = true;
25159
+ }
25160
+
25018
25161
  src.dirty = true;
25019
25162
  dst.dirty = true;
25163
+
25020
25164
  modifiedNodes.push(src);
25021
25165
  modifiedNodes.push(dst);
25022
25166
 
@@ -25044,6 +25188,7 @@ RED.view = (function() {
25044
25188
  links:oldDstLinks
25045
25189
  }
25046
25190
  });
25191
+
25047
25192
  src.changed = true;
25048
25193
  dst.changed = true;
25049
25194
  }
@@ -31318,7 +31463,7 @@ RED.sidebar.info = (function() {
31318
31463
 
31319
31464
  propertiesPanelHeaderIcon.empty();
31320
31465
  RED.utils.createNodeIcon({type:"_selection_"}).appendTo(propertiesPanelHeaderIcon);
31321
- propertiesPanelHeaderLabel.text("Selection");
31466
+ propertiesPanelHeaderLabel.text(RED._("sidebar.info.selection"));
31322
31467
  propertiesPanelHeaderReveal.hide();
31323
31468
  propertiesPanelHeaderHelp.hide();
31324
31469
  propertiesPanelHeaderCopyLink.hide();
@@ -36089,7 +36234,7 @@ RED.editor = (function() {
36089
36234
  const labelText = RED.editor.envVarList.lookupLabel(labels, labels["en-US"] || tenv.name, locale)
36090
36235
  const config = {
36091
36236
  env: tenv,
36092
- id: '${' + parentEnv[0].name + '}',
36237
+ id: '${' + tenv.name + '}',
36093
36238
  type: type,
36094
36239
  label: labelText,
36095
36240
  __label__: `[env] ${labelText}`
@@ -45814,10 +45959,10 @@ RED.library = (function() {
45814
45959
  if (file && file.label && !file.children) {
45815
45960
  $.get("library/"+file.library+"/"+file.type+"/"+file.path, function(data) {
45816
45961
  //TODO: nls + sanitize
45817
- var propRow = $('<tr class="red-ui-help-info-row"><td>Type</td><td></td></tr>').appendTo(table);
45962
+ var propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("library.type")+'</td><td></td></tr>').appendTo(table);
45818
45963
  $(propRow.children()[1]).text(activeLibrary.type);
45819
45964
  if (file.props.hasOwnProperty('name')) {
45820
- propRow = $('<tr class="red-ui-help-info-row"><td>Name</td><td>'+file.props.name+'</td></tr>').appendTo(table);
45965
+ propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("library.name")+'</td><td>'+file.props.name+'</td></tr>').appendTo(table);
45821
45966
  $(propRow.children()[1]).text(file.props.name);
45822
45967
  }
45823
45968
  for (var p in file.props) {
@@ -46901,24 +47046,28 @@ RED.search = (function() {
46901
47046
  const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g
46902
47047
  let hasGroup, isAllGroups = true, hasDisabledNode, hasEnabledNode, hasLabeledNode, hasUnlabeledNode;
46903
47048
  if (hasSelection) {
46904
- selection.nodes.forEach(n => {
47049
+ const nodes = selection.nodes.slice();
47050
+ while (nodes.length) {
47051
+ const n = nodes.shift();
46905
47052
  if (n.type === 'group') {
46906
47053
  hasGroup = true;
47054
+ nodes.push(...n.nodes);
46907
47055
  } else {
46908
47056
  isAllGroups = false;
46909
- }
46910
- if (n.d) {
46911
- hasDisabledNode = true;
46912
- } else {
46913
- hasEnabledNode = true;
47057
+ if (n.d) {
47058
+ hasDisabledNode = true;
47059
+ } else {
47060
+ hasEnabledNode = true;
47061
+ }
46914
47062
  }
46915
47063
  if (n.l === undefined || n.l) {
46916
47064
  hasLabeledNode = true;
46917
47065
  } else {
46918
47066
  hasUnlabeledNode = true;
46919
47067
  }
46920
- });
47068
+ }
46921
47069
  }
47070
+
46922
47071
  const offset = $("#red-ui-workspace-chart").offset()
46923
47072
 
46924
47073
  let addX = options.x - offset.left + $("#red-ui-workspace-chart").scrollLeft()
@@ -46930,10 +47079,11 @@ RED.search = (function() {
46930
47079
  addY = gridSize * Math.floor(addY / gridSize)
46931
47080
  }
46932
47081
 
46933
- menuItems.push(
46934
- { onselect: 'core:show-action-list', label: RED._("contextMenu.showActionList"), onpostselect: function () { } }
46935
- )
46936
-
47082
+ if (RED.settings.theme("menu.menu-item-action-list", true)) {
47083
+ menuItems.push(
47084
+ { onselect: 'core:show-action-list', label: RED._("contextMenu.showActionList"), onpostselect: function () { } }
47085
+ )
47086
+ }
46937
47087
  const insertOptions = []
46938
47088
  menuItems.push({ label: RED._("contextMenu.insert"), options: insertOptions })
46939
47089
  insertOptions.push(
@@ -47350,7 +47500,9 @@ RED.actionList = (function() {
47350
47500
  }
47351
47501
 
47352
47502
  function init() {
47353
- RED.actions.add("core:show-action-list",show);
47503
+ if (RED.settings.theme("menu.menu-item-action-list", true)) {
47504
+ RED.actions.add("core:show-action-list",show);
47505
+ }
47354
47506
 
47355
47507
  RED.events.on("editor:open",function() { disabled = true; });
47356
47508
  RED.events.on("editor:close",function() { disabled = false; });
@@ -47656,6 +47808,11 @@ RED.actionList = (function() {
47656
47808
  if ($("#red-ui-main-container").height() - opts.y - 195 < 0) {
47657
47809
  opts.y = opts.y - 275;
47658
47810
  }
47811
+ const dialogWidth = dialog.width() || 300 // default is 300 (defined in class .red-ui-search)
47812
+ const workspaceWidth = $('#red-ui-workspace').width()
47813
+ if (workspaceWidth > dialogWidth && workspaceWidth - opts.x - dialogWidth < 0) {
47814
+ opts.x = opts.x - (dialogWidth - RED.view.node_width)
47815
+ }
47659
47816
  dialog.css({left:opts.x+"px",top:opts.y+"px"}).show();
47660
47817
  searchResultsDiv.slideDown(300);
47661
47818
  setTimeout(function() {
@@ -47707,13 +47864,25 @@ RED.actionList = (function() {
47707
47864
  }
47708
47865
  }
47709
47866
  function applyFilter(filter,type,def) {
47710
- return !def || !filter ||
47711
- (
47712
- (!filter.spliceMultiple) &&
47713
- (!filter.type || type === filter.type) &&
47714
- (!filter.input || type === 'junction' || def.inputs > 0) &&
47715
- (!filter.output || type === 'junction' || def.outputs > 0)
47716
- )
47867
+ if (!filter) {
47868
+ // No filter; allow everything
47869
+ return true
47870
+ }
47871
+ if (type === 'junction') {
47872
+ // Only allow Junction is there's no specific type filter
47873
+ return !filter.type
47874
+ }
47875
+ if (filter.type) {
47876
+ // Handle explicit type filter
47877
+ return filter.type === type
47878
+ }
47879
+ if (!def) {
47880
+ // No node definition available - allow it
47881
+ return true
47882
+ }
47883
+ // Check if the filter is for input/outputs and apply
47884
+ return (!filter.input || def.inputs > 0) &&
47885
+ (!filter.output || def.outputs > 0)
47717
47886
  }
47718
47887
  function refreshTypeList(opts) {
47719
47888
  var i;
@@ -53044,7 +53213,7 @@ RED.projects.settings = (function() {
53044
53213
  if (activeProject.dependencies) {
53045
53214
  for (var m in activeProject.dependencies) {
53046
53215
  if (activeProject.dependencies.hasOwnProperty(m)) {
53047
- var installed = !!RED.nodes.registry.getModule(m) && activeProject.dependencies[m] === modulesInUse[m].version;
53216
+ var installed = !!RED.nodes.registry.getModule(m) && activeProject.dependencies[m] === modulesInUse[m]?.version;
53048
53217
  depsList.editableList('addItem',{
53049
53218
  id: m,
53050
53219
  version: activeProject.dependencies[m], //RED.nodes.registry.getModule(module).version,
@@ -53992,7 +54161,7 @@ RED.projects.settings = (function() {
53992
54161
  notification.close();
53993
54162
  }
53994
54163
  },{
53995
- text: 'Delete branch',
54164
+ text: RED._("sidebar.project.projectSettings.deleteBranch"),
53996
54165
  click: function() {
53997
54166
  notification.close();
53998
54167
  var url = "projects/"+activeProject.name+"/branches/"+entry.name;