@node-red/editor-client 3.1.0-beta.3 → 3.1.0

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
@@ -822,7 +822,7 @@ var RED = (function() {
822
822
  }
823
823
  menuOptions.push({id:"menu-item-help",
824
824
  label: RED.settings.theme("menu.menu-item-help.label",RED._("menu.label.help")),
825
- href: RED.settings.theme("menu.menu-item-help.url","http://nodered.org/docs")
825
+ href: RED.settings.theme("menu.menu-item-help.url","https://nodered.org/docs")
826
826
  });
827
827
  menuOptions.push({id:"menu-item-node-red-version", label:"v"+RED.settings.version, onselect: "core:show-about" });
828
828
 
@@ -4493,8 +4493,8 @@ RED.nodes = (function() {
4493
4493
 
4494
4494
  if (node && node._def.onremove) {
4495
4495
  // Deprecated: never documented but used by some early nodes
4496
- console.log("Deprecated API warning: node type ",node.type," has an onremove function - should be oneditremove - please report");
4497
- node._def.onremove.call(n);
4496
+ console.log("Deprecated API warning: node type ",node.type," has an onremove function - should be oneditdelete - please report");
4497
+ node._def.onremove.call(node);
4498
4498
  }
4499
4499
  return {links:removedLinks,nodes:removedNodes};
4500
4500
  }
@@ -17426,9 +17426,10 @@ RED.diagnostics = (function () {
17426
17426
  }
17427
17427
  if (localNode && remoteNode && typeof localNode[d] === "string") {
17428
17428
  if (/\n/.test(localNode[d]) || /\n/.test(remoteNode[d])) {
17429
- $('<button class="red-ui-button red-ui-button-small red-ui-diff-text-diff-button"><i class="fa fa-file-o"> <i class="fa fa-caret-left"></i> <i class="fa fa-caret-right"></i> <i class="fa fa-file-o"></i></button>').on("click", function() {
17429
+ var textDiff = $('<button class="red-ui-button red-ui-button-small red-ui-diff-text-diff-button"><i class="fa fa-file-o"> <i class="fa fa-caret-left"></i> <i class="fa fa-caret-right"></i> <i class="fa fa-file-o"></i></button>').on("click", function() {
17430
17430
  showTextDiff(localNode[d],remoteNode[d]);
17431
17431
  }).appendTo(propertyNameCell);
17432
+ RED.popover.tooltip(textDiff, RED._("diff.compareChanges"));
17432
17433
  }
17433
17434
  }
17434
17435
 
@@ -18923,7 +18924,10 @@ RED.keyboard = (function() {
18923
18924
  // One exception is shortcuts that include both Cmd and Ctrl. We don't
18924
18925
  // support them - but we need to make sure we don't block browser-specific
18925
18926
  // shortcuts (such as Cmd-Ctrl-F for fullscreen).
18926
- if ((evt.ctrlKey || evt.metaKey) && (evt.ctrlKey !== evt.metaKey)) {
18927
+ if (evt.ctrlKey && evt.metaKey) {
18928
+ return null; // dont handle both cmd+ctrl - let browser handle this
18929
+ }
18930
+ if (evt.ctrlKey || evt.metaKey) {
18927
18931
  slot = slot.ctrl;
18928
18932
  }
18929
18933
  if (slot && evt.shiftKey) {
@@ -20620,7 +20624,7 @@ RED.view = (function() {
20620
20624
 
20621
20625
  // Note: these are the permitted status colour aliases. The actual RGB values
20622
20626
  // are set in the CSS - flow.scss/colors.scss
20623
- var status_colours = {
20627
+ const status_colours = {
20624
20628
  "red": "#c00",
20625
20629
  "green": "#5a8",
20626
20630
  "yellow": "#F9DF31",
@@ -20629,19 +20633,32 @@ RED.view = (function() {
20629
20633
  "gray": "#d3d3d3"
20630
20634
  }
20631
20635
 
20632
- var PORT_TYPE_INPUT = 1;
20633
- var PORT_TYPE_OUTPUT = 0;
20636
+ const PORT_TYPE_INPUT = 1;
20637
+ const PORT_TYPE_OUTPUT = 0;
20634
20638
 
20635
- var chart;
20636
- var outer;
20639
+ /**
20640
+ * The jQuery object for the workspace chart `#red-ui-workspace-chart` div element
20641
+ * @type {JQuery<HTMLElement>} #red-ui-workspace-chart HTML Element
20642
+ */
20643
+ let chart;
20644
+ /**
20645
+ * The d3 object `#red-ui-workspace-chart` svg element
20646
+ * @type {d3.Selection<HTMLElement, Any, Any, Any>}
20647
+ */
20648
+ let outer;
20649
+ /**
20650
+ * The d3 object `#red-ui-workspace-chart` svg element (specifically for events)
20651
+ * @type {d3.Selection<d3.BaseType, any, any, any>}
20652
+ */
20637
20653
  var eventLayer;
20638
- var gridLayer;
20639
- var linkLayer;
20640
- var junctionLayer;
20641
- var dragGroupLayer;
20642
- var groupSelectLayer;
20643
- var nodeLayer;
20644
- var groupLayer;
20654
+
20655
+ /** @type {SVGGElement} */ let gridLayer;
20656
+ /** @type {SVGGElement} */ let linkLayer;
20657
+ /** @type {SVGGElement} */ let junctionLayer;
20658
+ /** @type {SVGGElement} */ let dragGroupLayer;
20659
+ /** @type {SVGGElement} */ let groupSelectLayer;
20660
+ /** @type {SVGGElement} */ let nodeLayer;
20661
+ /** @type {SVGGElement} */ let groupLayer;
20645
20662
  var drag_lines;
20646
20663
 
20647
20664
  const movingSet = (function() {
@@ -20808,11 +20825,21 @@ RED.view = (function() {
20808
20825
  return api
20809
20826
  })()
20810
20827
 
20828
+ const isMac = RED.utils.getBrowserInfo().os === 'mac'
20829
+ // 'Control' is the main modifier key for mouse actions. On Windows,
20830
+ // that is the standard Ctrl key. On Mac that is the Cmd key.
20831
+ function isControlPressed (event) {
20832
+ return (isMac && event.metaKey) || (!isMac && event.ctrlKey)
20833
+ }
20811
20834
 
20812
20835
  function init() {
20813
20836
 
20814
20837
  chart = $("#red-ui-workspace-chart");
20815
20838
  chart.on('contextmenu', function(evt) {
20839
+ if (RED.view.DEBUG) {
20840
+ console.warn("contextmenu", { mouse_mode, event: d3.event });
20841
+ }
20842
+ mouse_mode = RED.state.DEFAULT
20816
20843
  evt.preventDefault()
20817
20844
  evt.stopPropagation()
20818
20845
  RED.contextMenu.show({
@@ -20910,16 +20937,6 @@ RED.view = (function() {
20910
20937
  touchStartTime = setTimeout(function() {
20911
20938
  touchStartTime = null;
20912
20939
  showTouchMenu(obj,pos);
20913
- //lasso = eventLayer.append("rect")
20914
- // .attr("ox",point[0])
20915
- // .attr("oy",point[1])
20916
- // .attr("rx",2)
20917
- // .attr("ry",2)
20918
- // .attr("x",point[0])
20919
- // .attr("y",point[1])
20920
- // .attr("width",0)
20921
- // .attr("height",0)
20922
- // .attr("class","nr-ui-view-lasso");
20923
20940
  },touchLongPressTimeout);
20924
20941
  }
20925
20942
  d3.event.preventDefault();
@@ -21552,7 +21569,7 @@ RED.view = (function() {
21552
21569
  })
21553
21570
  }
21554
21571
 
21555
- function generateLinkPath(origX,origY, destX, destY, sc) {
21572
+ function generateLinkPath(origX,origY, destX, destY, sc, hasStatus = false) {
21556
21573
  var dy = destY-origY;
21557
21574
  var dx = destX-origX;
21558
21575
  var delta = Math.sqrt(dy*dy+dx*dx);
@@ -21569,62 +21586,110 @@ RED.view = (function() {
21569
21586
  } else {
21570
21587
  scale = 0.4-0.2*(Math.max(0,(node_width-Math.min(Math.abs(dx),Math.abs(dy)))/node_width));
21571
21588
  }
21589
+ function genCP(cp) {
21590
+ return ` M ${cp[0]-5} ${cp[1]} h 10 M ${cp[0]} ${cp[1]-5} v 10 `
21591
+ }
21572
21592
  if (dx*sc > 0) {
21573
- return "M "+origX+" "+origY+
21574
- " C "+(origX+sc*(node_width*scale))+" "+(origY+scaleY*node_height)+" "+
21575
- (destX-sc*(scale)*node_width)+" "+(destY-scaleY*node_height)+" "+
21576
- destX+" "+destY
21593
+ let cp = [
21594
+ [(origX+sc*(node_width*scale)), (origY+scaleY*node_height)],
21595
+ [(destX-sc*(scale)*node_width), (destY-scaleY*node_height)]
21596
+ ]
21597
+ return `M ${origX} ${origY} C ${cp[0][0]} ${cp[0][1]} ${cp[1][0]} ${cp[1][1]} ${destX} ${destY}`
21598
+ // + ` ${genCP(cp[0])} ${genCP(cp[1])}`
21577
21599
  } else {
21600
+ let topX, topY, bottomX, bottomY
21601
+ let cp
21602
+ let midX = Math.floor(destX-dx/2);
21603
+ let midY = Math.floor(destY-dy/2);
21604
+ if (Math.abs(dy) < 10) {
21605
+ bottomY = Math.max(origY, destY) + (hasStatus?35:25)
21606
+ let startCurveHeight = bottomY - origY
21607
+ let endCurveHeight = bottomY - destY
21608
+ cp = [
21609
+ [ origX + sc*15 , origY ],
21610
+ [ origX + sc*25 , origY + 5 ],
21611
+ [ origX + sc*25 , origY + startCurveHeight/2 ],
21612
+
21613
+ [ origX + sc*25 , origY + startCurveHeight - 5 ],
21614
+ [ origX + sc*15 , origY + startCurveHeight ],
21615
+ [ origX , origY + startCurveHeight ],
21616
+
21617
+ [ destX - sc*15, origY + startCurveHeight ],
21618
+ [ destX - sc*25, origY + startCurveHeight - 5 ],
21619
+ [ destX - sc*25, destY + endCurveHeight/2 ],
21620
+
21621
+ [ destX - sc*25, destY + 5 ],
21622
+ [ destX - sc*15, destY ],
21623
+ [ destX, destY ],
21624
+ ]
21578
21625
 
21579
- var midX = Math.floor(destX-dx/2);
21580
- var midY = Math.floor(destY-dy/2);
21581
- //
21582
- if (dy === 0) {
21583
- midY = destY + node_height;
21584
- }
21585
- var cp_height = node_height/2;
21586
- var y1 = (destY + midY)/2
21587
- var topX =origX + sc*node_width*scale;
21588
- var topY = dy>0?Math.min(y1 - dy/2 , origY+cp_height):Math.max(y1 - dy/2 , origY-cp_height);
21589
- var bottomX = destX - sc*node_width*scale;
21590
- var bottomY = dy>0?Math.max(y1, destY-cp_height):Math.min(y1, destY+cp_height);
21591
- var x1 = (origX+topX)/2;
21592
- var scy = dy>0?1:-1;
21593
- var cp = [
21594
- // Orig -> Top
21595
- [x1,origY],
21596
- [topX,dy>0?Math.max(origY, topY-cp_height):Math.min(origY, topY+cp_height)],
21597
- // Top -> Mid
21598
- // [Mirror previous cp]
21599
- [x1,dy>0?Math.min(midY, topY+cp_height):Math.max(midY, topY-cp_height)],
21600
- // Mid -> Bottom
21601
- // [Mirror previous cp]
21602
- [bottomX,dy>0?Math.max(midY, bottomY-cp_height):Math.min(midY, bottomY+cp_height)],
21603
- // Bottom -> Dest
21604
- // [Mirror previous cp]
21605
- [(destX+bottomX)/2,destY]
21606
- ];
21607
- if (cp[2][1] === topY+scy*cp_height) {
21608
- if (Math.abs(dy) < cp_height*10) {
21609
- cp[1][1] = topY-scy*cp_height/2;
21610
- cp[3][1] = bottomY-scy*cp_height/2;
21611
- }
21612
- cp[2][0] = topX;
21613
- }
21614
- return "M "+origX+" "+origY+
21615
- " C "+
21616
- cp[0][0]+" "+cp[0][1]+" "+
21617
- cp[1][0]+" "+cp[1][1]+" "+
21618
- topX+" "+topY+
21619
- " S "+
21620
- cp[2][0]+" "+cp[2][1]+" "+
21621
- midX+" "+midY+
21622
- " S "+
21623
- cp[3][0]+" "+cp[3][1]+" "+
21624
- bottomX+" "+bottomY+
21625
- " S "+
21626
+ return "M "+origX+" "+origY+
21627
+ " C "+
21628
+ cp[0][0]+" "+cp[0][1]+" "+
21629
+ cp[1][0]+" "+cp[1][1]+" "+
21630
+ cp[2][0]+" "+cp[2][1]+" "+
21631
+ " C " +
21632
+ cp[3][0]+" "+cp[3][1]+" "+
21626
21633
  cp[4][0]+" "+cp[4][1]+" "+
21627
- destX+" "+destY
21634
+ cp[5][0]+" "+cp[5][1]+" "+
21635
+ " h "+dx+
21636
+ " C "+
21637
+ cp[6][0]+" "+cp[6][1]+" "+
21638
+ cp[7][0]+" "+cp[7][1]+" "+
21639
+ cp[8][0]+" "+cp[8][1]+" "+
21640
+ " C " +
21641
+ cp[9][0]+" "+cp[9][1]+" "+
21642
+ cp[10][0]+" "+cp[10][1]+" "+
21643
+ cp[11][0]+" "+cp[11][1]+" "
21644
+ // +genCP(cp[0])+genCP(cp[1])+genCP(cp[2])+genCP(cp[3])+genCP(cp[4])
21645
+ // +genCP(cp[5])+genCP(cp[6])+genCP(cp[7])+genCP(cp[8])+genCP(cp[9])+genCP(cp[10])
21646
+ } else {
21647
+ var cp_height = node_height/2;
21648
+ var y1 = (destY + midY)/2
21649
+ topX = origX + sc*node_width*scale;
21650
+ topY = dy>0?Math.min(y1 - dy/2 , origY+cp_height):Math.max(y1 - dy/2 , origY-cp_height);
21651
+ bottomX = destX - sc*node_width*scale;
21652
+ bottomY = dy>0?Math.max(y1, destY-cp_height):Math.min(y1, destY+cp_height);
21653
+ var x1 = (origX+topX)/2;
21654
+ var scy = dy>0?1:-1;
21655
+ cp = [
21656
+ // Orig -> Top
21657
+ [x1,origY],
21658
+ [topX,dy>0?Math.max(origY, topY-cp_height):Math.min(origY, topY+cp_height)],
21659
+ // Top -> Mid
21660
+ // [Mirror previous cp]
21661
+ [x1,dy>0?Math.min(midY, topY+cp_height):Math.max(midY, topY-cp_height)],
21662
+ // Mid -> Bottom
21663
+ // [Mirror previous cp]
21664
+ [bottomX,dy>0?Math.max(midY, bottomY-cp_height):Math.min(midY, bottomY+cp_height)],
21665
+ // Bottom -> Dest
21666
+ // [Mirror previous cp]
21667
+ [(destX+bottomX)/2,destY]
21668
+ ];
21669
+ if (cp[2][1] === topY+scy*cp_height) {
21670
+ if (Math.abs(dy) < cp_height*10) {
21671
+ cp[1][1] = topY-scy*cp_height/2;
21672
+ cp[3][1] = bottomY-scy*cp_height/2;
21673
+ }
21674
+ cp[2][0] = topX;
21675
+ }
21676
+ return "M "+origX+" "+origY+
21677
+ " C "+
21678
+ cp[0][0]+" "+cp[0][1]+" "+
21679
+ cp[1][0]+" "+cp[1][1]+" "+
21680
+ topX+" "+topY+
21681
+ " S "+
21682
+ cp[2][0]+" "+cp[2][1]+" "+
21683
+ midX+" "+midY+
21684
+ " S "+
21685
+ cp[3][0]+" "+cp[3][1]+" "+
21686
+ bottomX+" "+bottomY+
21687
+ " S "+
21688
+ cp[4][0]+" "+cp[4][1]+" "+
21689
+ destX+" "+destY
21690
+
21691
+ // +genCP(cp[0])+genCP(cp[1])+genCP(cp[2])+genCP(cp[3])+genCP(cp[4])
21692
+ }
21628
21693
  }
21629
21694
  }
21630
21695
 
@@ -21658,7 +21723,7 @@ RED.view = (function() {
21658
21723
  lasso = null;
21659
21724
  }
21660
21725
  if (d3.event.touches || d3.event.button === 0) {
21661
- if ((mouse_mode === 0 || mouse_mode === RED.state.QUICK_JOINING) && (d3.event.metaKey || d3.event.ctrlKey) && !(d3.event.altKey || d3.event.shiftKey)) {
21726
+ if ((mouse_mode === 0 || mouse_mode === RED.state.QUICK_JOINING) && isControlPressed(d3.event) && !(d3.event.altKey || d3.event.shiftKey)) {
21662
21727
  // Trigger quick add dialog
21663
21728
  d3.event.stopPropagation();
21664
21729
  clearSelection();
@@ -21668,7 +21733,7 @@ RED.view = (function() {
21668
21733
  clickedGroup = clickedGroup || RED.nodes.group(drag_lines[0].node.g)
21669
21734
  }
21670
21735
  showQuickAddDialog({ position: point, group: clickedGroup });
21671
- } else if (mouse_mode === 0 && !(d3.event.metaKey || d3.event.ctrlKey)) {
21736
+ } else if (mouse_mode === 0 && !isControlPressed(d3.event)) {
21672
21737
  // CTRL not being held
21673
21738
  if (!d3.event.altKey) {
21674
21739
  // ALT not held (shift is allowed) Trigger lasso
@@ -21866,6 +21931,7 @@ RED.view = (function() {
21866
21931
  }
21867
21932
  historyEvent = {
21868
21933
  t:'add',
21934
+ dirty: RED.nodes.dirty(),
21869
21935
  junctions:[nn]
21870
21936
  }
21871
21937
  } else {
@@ -22241,7 +22307,7 @@ RED.view = (function() {
22241
22307
  var portY = -((numOutputs-1)/2)*13 +13*sourcePort;
22242
22308
 
22243
22309
  var sc = (drag_line.portType === PORT_TYPE_OUTPUT)?1:-1;
22244
- drag_line.el.attr("d",generateLinkPath(drag_line.node.x+sc*drag_line.node.w/2,drag_line.node.y+portY,mousePos[0],mousePos[1],sc));
22310
+ drag_line.el.attr("d",generateLinkPath(drag_line.node.x+sc*drag_line.node.w/2,drag_line.node.y+portY,mousePos[0],mousePos[1],sc, !!drag_line.node.status));
22245
22311
  }
22246
22312
  d3.event.preventDefault();
22247
22313
  } else if (mouse_mode == RED.state.MOVING) {
@@ -23565,22 +23631,38 @@ RED.view = (function() {
23565
23631
  }
23566
23632
  }
23567
23633
  document.body.style.cursor = "";
23634
+
23568
23635
  if (mouse_mode == RED.state.JOINING || mouse_mode == RED.state.QUICK_JOINING) {
23569
23636
  if (typeof TouchEvent != "undefined" && evt instanceof TouchEvent) {
23570
- var found = false;
23571
- RED.nodes.eachNode(function(n) {
23572
- if (n.z == RED.workspaces.active()) {
23573
- var hw = n.w/2;
23574
- var hh = n.h/2;
23575
- if (n.x-hw<mouse_position[0] && n.x+hw> mouse_position[0] &&
23576
- n.y-hh<mouse_position[1] && n.y+hh>mouse_position[1]) {
23577
- found = true;
23578
- mouseup_node = n;
23579
- portType = mouseup_node.inputs>0?PORT_TYPE_INPUT:PORT_TYPE_OUTPUT;
23580
- portIndex = 0;
23637
+ if (RED.view.DEBUG) { console.warn("portMouseUp: TouchEvent", mouse_mode,d,portType,portIndex); }
23638
+ const direction = drag_lines[0].portType === PORT_TYPE_INPUT ? PORT_TYPE_OUTPUT : PORT_TYPE_INPUT
23639
+ let found = false;
23640
+ for (let nodeIdx = 0; nodeIdx < activeNodes.length; nodeIdx++) {
23641
+ const n = activeNodes[nodeIdx];
23642
+ if (RED.view.tools.isPointInNode(n, mouse_position)) {
23643
+ found = true;
23644
+ mouseup_node = n;
23645
+ // portType = mouseup_node.inputs > 0 ? PORT_TYPE_INPUT : PORT_TYPE_OUTPUT;
23646
+ portType = direction;
23647
+ portIndex = 0;
23648
+ break
23649
+ }
23650
+ }
23651
+
23652
+ if (!found && drag_lines.length > 0 && !drag_lines[0].virtualLink) {
23653
+ for (let juncIdx = 0; juncIdx < activeJunctions.length; juncIdx++) {
23654
+ // NOTE: a junction is 10px x 10px but the target area is expanded to 30wx20h by adding padding to the bounding box
23655
+ const jNode = activeJunctions[juncIdx];
23656
+ if (RED.view.tools.isPointInNode(jNode, mouse_position, 20, 10)) {
23657
+ found = true;
23658
+ mouseup_node = jNode;
23659
+ portType = direction;
23660
+ portIndex = 0;
23661
+ break
23581
23662
  }
23582
23663
  }
23583
- });
23664
+ }
23665
+
23584
23666
  if (!found && activeSubflow) {
23585
23667
  var subflowPorts = [];
23586
23668
  if (activeSubflow.status) {
@@ -23592,16 +23674,13 @@ RED.view = (function() {
23592
23674
  if (activeSubflow.out) {
23593
23675
  subflowPorts = subflowPorts.concat(activeSubflow.out)
23594
23676
  }
23595
- for (var i=0;i<subflowPorts.length;i++) {
23596
- var n = subflowPorts[i];
23597
- var hw = n.w/2;
23598
- var hh = n.h/2;
23599
- if (n.x-hw<mouse_position[0] && n.x+hw> mouse_position[0] &&
23600
- n.y-hh<mouse_position[1] && n.y+hh>mouse_position[1]) {
23601
- found = true;
23602
- mouseup_node = n;
23603
- portType = mouseup_node.direction === "in"?PORT_TYPE_OUTPUT:PORT_TYPE_INPUT;
23604
- portIndex = 0;
23677
+ for (var i = 0; i < subflowPorts.length; i++) {
23678
+ const sf = subflowPorts[i];
23679
+ if (RED.view.tools.isPointInNode(sf, mouse_position)) {
23680
+ found = true;
23681
+ mouseup_node = sf;
23682
+ portType = mouseup_node.direction === "in" ? PORT_TYPE_OUTPUT : PORT_TYPE_INPUT;
23683
+ portIndex = 0;
23605
23684
  break;
23606
23685
  }
23607
23686
  }
@@ -23994,7 +24073,7 @@ RED.view = (function() {
23994
24073
  d3.event.preventDefault()
23995
24074
  document.getSelection().removeAllRanges()
23996
24075
  if (d.type != "subflow") {
23997
- if (/^subflow:/.test(d.type) && (d3.event.ctrlKey || d3.event.metaKey)) {
24076
+ if (/^subflow:/.test(d.type) && isControlPressed(d3.event)) {
23998
24077
  RED.workspaces.show(d.type.substring(8));
23999
24078
  } else {
24000
24079
  RED.editor.edit(d);
@@ -24158,12 +24237,12 @@ RED.view = (function() {
24158
24237
  d.type !== 'junction'
24159
24238
  lastClickNode = mousedown_node;
24160
24239
 
24161
- if (d.selected && (d3.event.ctrlKey||d3.event.metaKey)) {
24240
+ if (d.selected && isControlPressed(d3.event)) {
24162
24241
  mousedown_node.selected = false;
24163
24242
  movingSet.remove(mousedown_node);
24164
24243
  } else {
24165
24244
  if (d3.event.shiftKey) {
24166
- if (!(d3.event.ctrlKey||d3.event.metaKey)) {
24245
+ if (!isControlPressed(d3.event)) {
24167
24246
  clearSelection();
24168
24247
  }
24169
24248
  var clickPosition = (d3.event.offsetX/scaleFactor - mousedown_node.x)
@@ -24332,10 +24411,10 @@ RED.view = (function() {
24332
24411
  }
24333
24412
  mousedown_link = d;
24334
24413
 
24335
- if (!(d3.event.metaKey || d3.event.ctrlKey)) {
24414
+ if (!isControlPressed(d3.event)) {
24336
24415
  clearSelection();
24337
24416
  }
24338
- if (d3.event.metaKey || d3.event.ctrlKey) {
24417
+ if (isControlPressed(d3.event)) {
24339
24418
  if (!selectedLinks.has(mousedown_link)) {
24340
24419
  selectedLinks.add(mousedown_link);
24341
24420
  } else {
@@ -24350,7 +24429,7 @@ RED.view = (function() {
24350
24429
  redraw();
24351
24430
  focusView();
24352
24431
  d3.event.stopPropagation();
24353
- if (!mousedown_link.link && movingSet.length() === 0 && (d3.event.touches || d3.event.button === 0) && selectedLinks.length() === 1 && selectedLinks.has(mousedown_link) && (d3.event.metaKey || d3.event.ctrlKey)) {
24432
+ if (!mousedown_link.link && movingSet.length() === 0 && (d3.event.touches || d3.event.button === 0) && selectedLinks.length() === 1 && selectedLinks.has(mousedown_link) && isControlPressed(d3.event)) {
24354
24433
  d3.select(this).classed("red-ui-flow-link-splice",true);
24355
24434
  var point = d3.mouse(this);
24356
24435
  var clickedGroup = getGroupAt(point[0],point[1]);
@@ -24431,7 +24510,7 @@ RED.view = (function() {
24431
24510
  );
24432
24511
  lastClickNode = g;
24433
24512
 
24434
- if (g.selected && (d3.event.ctrlKey||d3.event.metaKey)) {
24513
+ if (g.selected && isControlPressed(d3.event)) {
24435
24514
  selectedGroups.remove(g);
24436
24515
  d3.event.stopPropagation();
24437
24516
  } else {
@@ -24631,21 +24710,27 @@ RED.view = (function() {
24631
24710
  nodeEl.__statusGroup__.style.display = "none";
24632
24711
  } else {
24633
24712
  nodeEl.__statusGroup__.style.display = "inline";
24713
+ let backgroundWidth = 12
24634
24714
  var fill = status_colours[d.status.fill]; // Only allow our colours for now
24635
24715
  if (d.status.shape == null && fill == null) {
24716
+ backgroundWidth = 0
24636
24717
  nodeEl.__statusShape__.style.display = "none";
24718
+ nodeEl.__statusBackground__.setAttribute("x", 17)
24637
24719
  nodeEl.__statusGroup__.setAttribute("transform","translate(-14,"+(d.h+3)+")");
24638
24720
  } else {
24639
24721
  nodeEl.__statusGroup__.setAttribute("transform","translate(3,"+(d.h+3)+")");
24640
24722
  var statusClass = "red-ui-flow-node-status-"+(d.status.shape||"dot")+"-"+d.status.fill;
24641
24723
  nodeEl.__statusShape__.style.display = "inline";
24642
24724
  nodeEl.__statusShape__.setAttribute("class","red-ui-flow-node-status "+statusClass);
24725
+ nodeEl.__statusBackground__.setAttribute("x", 3)
24643
24726
  }
24644
24727
  if (d.status.hasOwnProperty('text')) {
24645
24728
  nodeEl.__statusLabel__.textContent = d.status.text;
24646
24729
  } else {
24647
24730
  nodeEl.__statusLabel__.textContent = "";
24648
24731
  }
24732
+ const textSize = nodeEl.__statusLabel__.getBBox()
24733
+ nodeEl.__statusBackground__.setAttribute('width', backgroundWidth + textSize.width + 6)
24649
24734
  }
24650
24735
  delete d.dirtyStatus;
24651
24736
  }
@@ -25051,17 +25136,30 @@ RED.view = (function() {
25051
25136
  statusEl.style.display = "none";
25052
25137
  node[0][0].__statusGroup__ = statusEl;
25053
25138
 
25054
- var statusRect = document.createElementNS("http://www.w3.org/2000/svg","rect");
25055
- statusRect.setAttribute("class","red-ui-flow-node-status");
25056
- statusRect.setAttribute("x",6);
25057
- statusRect.setAttribute("y",1);
25058
- statusRect.setAttribute("width",9);
25059
- statusRect.setAttribute("height",9);
25060
- statusRect.setAttribute("rx",2);
25061
- statusRect.setAttribute("ry",2);
25062
- statusRect.setAttribute("stroke-width","3");
25063
- statusEl.appendChild(statusRect);
25064
- node[0][0].__statusShape__ = statusRect;
25139
+ var statusBackground = document.createElementNS("http://www.w3.org/2000/svg","rect");
25140
+ statusBackground.setAttribute("class","red-ui-flow-node-status-background");
25141
+ statusBackground.setAttribute("x",3);
25142
+ statusBackground.setAttribute("y",-1);
25143
+ statusBackground.setAttribute("width",200);
25144
+ statusBackground.setAttribute("height",13);
25145
+ statusBackground.setAttribute("rx",1);
25146
+ statusBackground.setAttribute("ry",1);
25147
+
25148
+ statusEl.appendChild(statusBackground);
25149
+ node[0][0].__statusBackground__ = statusBackground;
25150
+
25151
+
25152
+ var statusIcon = document.createElementNS("http://www.w3.org/2000/svg","rect");
25153
+ statusIcon.setAttribute("class","red-ui-flow-node-status");
25154
+ statusIcon.setAttribute("x",6);
25155
+ statusIcon.setAttribute("y",1);
25156
+ statusIcon.setAttribute("width",9);
25157
+ statusIcon.setAttribute("height",9);
25158
+ statusIcon.setAttribute("rx",2);
25159
+ statusIcon.setAttribute("ry",2);
25160
+ statusIcon.setAttribute("stroke-width","3");
25161
+ statusEl.appendChild(statusIcon);
25162
+ node[0][0].__statusShape__ = statusIcon;
25065
25163
 
25066
25164
  var statusLabel = document.createElementNS("http://www.w3.org/2000/svg","text");
25067
25165
  statusLabel.setAttribute("class","red-ui-flow-node-status-label");
@@ -25467,16 +25565,25 @@ RED.view = (function() {
25467
25565
  contents.appendChild(junctionOutput);
25468
25566
  junctionOutput.addEventListener("mouseup", portMouseUpProxy);
25469
25567
  junctionOutput.addEventListener("mousedown", portMouseDownProxy);
25470
-
25471
25568
  junctionOutput.addEventListener("mouseover", junctionMouseOverProxy);
25472
25569
  junctionOutput.addEventListener("mouseout", junctionMouseOutProxy);
25570
+ junctionOutput.addEventListener("touchmove", junctionMouseOverProxy);
25571
+ junctionOutput.addEventListener("touchend", portMouseUpProxy);
25572
+ junctionOutput.addEventListener("touchstart", portMouseDownProxy);
25573
+
25473
25574
  junctionInput.addEventListener("mouseover", junctionMouseOverProxy);
25474
25575
  junctionInput.addEventListener("mouseout", junctionMouseOutProxy);
25576
+ junctionInput.addEventListener("touchmove", junctionMouseOverProxy);
25577
+ junctionInput.addEventListener("touchend", portMouseUpProxy);
25578
+ junctionInput.addEventListener("touchstart", portMouseDownProxy);
25579
+
25475
25580
  junctionBack.addEventListener("mouseover", junctionMouseOverProxy);
25476
25581
  junctionBack.addEventListener("mouseout", junctionMouseOutProxy);
25582
+ junctionBack.addEventListener("touchmove", junctionMouseOverProxy);
25477
25583
 
25478
25584
  // These handlers expect to be registered as d3 events
25479
25585
  d3.select(junctionBack).on("mousedown", nodeMouseDown).on("mouseup", nodeMouseUp);
25586
+ d3.select(junctionBack).on("touchstart", nodeMouseDown).on("touchend", nodeMouseUp);
25480
25587
 
25481
25588
  junction[0][0].appendChild(contents);
25482
25589
  })
@@ -25586,7 +25693,7 @@ RED.view = (function() {
25586
25693
  // " C "+(d.x1+scale*node_width)+" "+(d.y1+scaleY*node_height)+" "+
25587
25694
  // (d.x2-scale*node_width)+" "+(d.y2-scaleY*node_height)+" "+
25588
25695
  // d.x2+" "+d.y2;
25589
- var path = generateLinkPath(d.x1,d.y1,d.x2,d.y2,1);
25696
+ var path = generateLinkPath(d.x1,d.y1,d.x2,d.y2,1, !!(d.source.status || d.target.status));
25590
25697
  if (/NaN/.test(path)) {
25591
25698
  path = ""
25592
25699
  }
@@ -28467,6 +28574,39 @@ RED.view.tools = (function() {
28467
28574
  }
28468
28575
  }
28469
28576
 
28577
+ /**
28578
+ * Determine if a point is within a node
28579
+ * @param {*} node - A Node or Junction node
28580
+ * @param {[Number,Number]} mouse_position The x,y position of the mouse
28581
+ * @param {Number} [marginX=0] - A margin to add or deduct from the x position (to increase the hit area)
28582
+ * @param {Number} [marginY=0] - A margin to add or deduct from the y position (to increase the hit area)
28583
+ * @returns
28584
+ */
28585
+ function isPointInNode (node, [x, y], marginX, marginY) {
28586
+ marginX = marginX || 0
28587
+ marginY = marginY || 0
28588
+
28589
+ let w = node.w || 10 // junctions dont have any w or h value
28590
+ let h = node.h || 10
28591
+ let x1, x2, y1, y2
28592
+
28593
+ if (node.type === "junction" || node.type === "group") {
28594
+ // x/y is the top left of the node
28595
+ x1 = node.x
28596
+ y1 = node.y
28597
+ x2 = node.x + w
28598
+ y2 = node.y + h
28599
+ } else {
28600
+ // x/y is the center of the node
28601
+ const [xMid, yMid] = [w/2, h/2]
28602
+ x1 = node.x - xMid
28603
+ y1 = node.y - yMid
28604
+ x2 = node.x + xMid
28605
+ y2 = node.y + yMid
28606
+ }
28607
+ return (x >= (x1 - marginX) && x <= (x2 + marginX) && y >= (y1 - marginY) && y <= (y2 + marginY))
28608
+ }
28609
+
28470
28610
  return {
28471
28611
  init: function() {
28472
28612
  RED.actions.add("core:show-selected-node-labels", function() { setSelectedNodeLabelState(true); })
@@ -28549,7 +28689,8 @@ RED.view.tools = (function() {
28549
28689
  * @param {Number} dy
28550
28690
  */
28551
28691
  moveSelection: moveSelection,
28552
- calculateGridSnapOffsets: calculateGridSnapOffsets
28692
+ calculateGridSnapOffsets: calculateGridSnapOffsets,
28693
+ isPointInNode: isPointInNode
28553
28694
  }
28554
28695
 
28555
28696
  })();
@@ -32010,11 +32151,11 @@ RED.sidebar.context = (function() {
32010
32151
  var obj = $(propRow.children()[0]);
32011
32152
  obj.text(k);
32012
32153
  var tools = $('<span class="button-group"></span>');
32013
-
32154
+ const urlSafeK = encodeURIComponent(k)
32014
32155
  var refreshItem = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-refresh"></i></button>').appendTo(tools).on("click", function(e) {
32015
32156
  e.preventDefault();
32016
32157
  e.stopPropagation();
32017
- $.getJSON(baseUrl+"/"+k+"?store="+v.store, function(data) {
32158
+ $.getJSON(baseUrl+"/"+urlSafeK+"?store="+v.store, function(data) {
32018
32159
  if (data.msg !== payload || data.format !== format) {
32019
32160
  payload = data.msg;
32020
32161
  format = data.format;
@@ -32050,11 +32191,12 @@ RED.sidebar.context = (function() {
32050
32191
  $('<button class="red-ui-button primary" data-i18n="common.label.delete"></button>').appendTo(bg).on("click", function(e) {
32051
32192
  e.preventDefault();
32052
32193
  popover.close();
32194
+ const urlSafeK = encodeURIComponent(k)
32053
32195
  $.ajax({
32054
- url: baseUrl+"/"+k+"?store="+v.store,
32196
+ url: baseUrl+"/"+urlSafeK+"?store="+v.store,
32055
32197
  type: "DELETE"
32056
32198
  }).done(function(data,textStatus,xhr) {
32057
- $.getJSON(baseUrl+"/"+k+"?store="+v.store, function(data) {
32199
+ $.getJSON(baseUrl+"/"+urlSafeK+"?store="+v.store, function(data) {
32058
32200
  if (data.format === 'undefined') {
32059
32201
  propRow.remove();
32060
32202
  if (container.children().length === 0) {
@@ -32140,15 +32282,17 @@ RED.sidebar.context = (function() {
32140
32282
  RED.palette.editor = (function() {
32141
32283
 
32142
32284
  var disabled = false;
32143
-
32285
+ let catalogues = []
32286
+ const loadedCatalogs = []
32144
32287
  var editorTabs;
32145
- var filterInput;
32146
- var searchInput;
32147
- var nodeList;
32148
- var packageList;
32149
- var loadedList = [];
32150
- var filteredList = [];
32151
- var loadedIndex = {};
32288
+ let filterInput;
32289
+ let searchInput;
32290
+ let nodeList;
32291
+ let packageList;
32292
+ let fullList = []
32293
+ let loadedList = [];
32294
+ let filteredList = [];
32295
+ let loadedIndex = {};
32152
32296
 
32153
32297
  var typesInUse = {};
32154
32298
  var nodeEntries = {};
@@ -32286,7 +32430,6 @@ RED.palette.editor = (function() {
32286
32430
  }
32287
32431
  }
32288
32432
 
32289
-
32290
32433
  function getContrastingBorder(rgbColor){
32291
32434
  var parts = /^rgba?\(\s*(\d+),\s*(\d+),\s*(\d+)[,)]/.exec(rgbColor);
32292
32435
  if (parts) {
@@ -32493,10 +32636,10 @@ RED.palette.editor = (function() {
32493
32636
  var activeSort = sortModulesRelevance;
32494
32637
 
32495
32638
  function handleCatalogResponse(err,catalog,index,v) {
32639
+ const url = catalog.url
32496
32640
  catalogueLoadStatus.push(err||v);
32497
32641
  if (!err) {
32498
32642
  if (v.modules) {
32499
- var a = false;
32500
32643
  v.modules = v.modules.filter(function(m) {
32501
32644
  if (RED.utils.checkModuleAllowed(m.id,m.version,installAllowList,installDenyList)) {
32502
32645
  loadedIndex[m.id] = m;
@@ -32513,13 +32656,14 @@ RED.palette.editor = (function() {
32513
32656
  m.timestamp = 0;
32514
32657
  }
32515
32658
  m.index = m.index.join(",").toLowerCase();
32659
+ m.catalog = catalog;
32660
+ m.catalogIndex = index;
32516
32661
  return true;
32517
32662
  }
32518
32663
  return false;
32519
32664
  })
32520
32665
  loadedList = loadedList.concat(v.modules);
32521
32666
  }
32522
- searchInput.searchBox('count',loadedList.length);
32523
32667
  } else {
32524
32668
  catalogueLoadErrors = true;
32525
32669
  }
@@ -32528,7 +32672,7 @@ RED.palette.editor = (function() {
32528
32672
  }
32529
32673
  if (catalogueLoadStatus.length === catalogueCount) {
32530
32674
  if (catalogueLoadErrors) {
32531
- RED.notify(RED._('palette.editor.errors.catalogLoadFailed',{url: catalog}),"error",false,8000);
32675
+ RED.notify(RED._('palette.editor.errors.catalogLoadFailed',{url: url}),"error",false,8000);
32532
32676
  }
32533
32677
  var delta = 250-(Date.now() - catalogueLoadStart);
32534
32678
  setTimeout(function() {
@@ -32540,12 +32684,13 @@ RED.palette.editor = (function() {
32540
32684
 
32541
32685
  function initInstallTab() {
32542
32686
  if (loadedList.length === 0) {
32687
+ fullList = [];
32543
32688
  loadedList = [];
32544
32689
  loadedIndex = {};
32545
32690
  packageList.editableList('empty');
32546
32691
 
32547
32692
  $(".red-ui-palette-module-shade-status").text(RED._('palette.editor.loading'));
32548
- var catalogues = RED.settings.theme('palette.catalogues')||['https://catalogue.nodered.org/catalogue.json'];
32693
+
32549
32694
  catalogueLoadStatus = [];
32550
32695
  catalogueLoadErrors = false;
32551
32696
  catalogueCount = catalogues.length;
@@ -32555,27 +32700,97 @@ RED.palette.editor = (function() {
32555
32700
  $("#red-ui-palette-module-install-shade").show();
32556
32701
  catalogueLoadStart = Date.now();
32557
32702
  var handled = 0;
32558
- catalogues.forEach(function(catalog,index) {
32559
- $.getJSON(catalog, {_: new Date().getTime()},function(v) {
32560
- handleCatalogResponse(null,catalog,index,v);
32703
+ loadedCatalogs.length = 0; // clear the loadedCatalogs array
32704
+ for (let index = 0; index < catalogues.length; index++) {
32705
+ const url = catalogues[index];
32706
+ $.getJSON(url, {_: new Date().getTime()},function(v) {
32707
+ loadedCatalogs.push({ index: index, url: url, name: v.name, updated_at: v.updated_at, modules_count: (v.modules || []).length })
32708
+ handleCatalogResponse(null,{ url: url, name: v.name},index,v);
32561
32709
  refreshNodeModuleList();
32562
32710
  }).fail(function(jqxhr, textStatus, error) {
32563
- console.warn("Error loading catalog",catalog,":",error);
32564
- handleCatalogResponse(jqxhr,catalog,index);
32711
+ console.warn("Error loading catalog",url,":",error);
32712
+ handleCatalogResponse(jqxhr,url,index);
32565
32713
  }).always(function() {
32566
32714
  handled++;
32567
32715
  if (handled === catalogueCount) {
32568
- searchInput.searchBox('change');
32716
+ //sort loadedCatalogs by e.index ascending
32717
+ loadedCatalogs.sort((a, b) => a.index - b.index)
32718
+ updateCatalogFilter(loadedCatalogs)
32569
32719
  }
32570
32720
  })
32571
- });
32721
+ }
32722
+ }
32723
+ }
32724
+
32725
+ /**
32726
+ * Refreshes the catalog filter dropdown and updates local variables
32727
+ * @param {[{url:String, name:String, updated_at:String, modules_count:Number}]} catalogEntries
32728
+ */
32729
+ function updateCatalogFilter(catalogEntries, maxRetry = 3) {
32730
+ // clean up existing filters
32731
+ const catalogSelection = $('#red-catalogue-filter-select')
32732
+ if (catalogSelection.length === 0) {
32733
+ // sidebar not yet loaded (red-catalogue-filter-select is not in dom)
32734
+ if (maxRetry > 0) {
32735
+ // console.log("updateCatalogFilter: sidebar not yet loaded, retrying in 100ms")
32736
+ // try again in 100ms
32737
+ setTimeout(() => {
32738
+ updateCatalogFilter(catalogEntries, maxRetry - 1)
32739
+ }, 100);
32740
+ return;
32741
+ }
32742
+ return; // give up
32743
+ }
32744
+ catalogSelection.off("change") // remove any existing event handlers
32745
+ catalogSelection.attr('disabled', 'disabled')
32746
+ catalogSelection.empty()
32747
+ catalogSelection.append($('<option>', { value: "loading", text: RED._('palette.editor.loading'), disabled: true, selected: true }));
32748
+
32749
+ fullList = loadedList.slice()
32750
+ catalogSelection.empty() // clear the select list
32751
+
32752
+ // loop through catalogTypes, and an option entry per catalog
32753
+ for (let index = 0; index < catalogEntries.length; index++) {
32754
+ const catalog = catalogEntries[index];
32755
+ catalogSelection.append(`<option value="${catalog.name}">${catalog.name}</option>`)
32756
+ }
32757
+ // select the 1st option in the select list
32758
+ catalogSelection.val(catalogSelection.find('option:first').val())
32759
+
32760
+ // if there is only 1 catalog, hide the select
32761
+ if (catalogEntries.length > 1) {
32762
+ catalogSelection.prepend(`<option value="all">${RED._('palette.editor.allCatalogs')}</option>`)
32763
+ catalogSelection.val('all')
32764
+ catalogSelection.removeAttr('disabled') // permit the user to select a catalog
32765
+ }
32766
+ // refresh the searchInput counter and trigger a change
32767
+ filterByCatalog(catalogSelection.val())
32768
+ searchInput.searchBox('change');
32769
+
32770
+ // hook up the change event handler
32771
+ catalogSelection.on("change", function() {
32772
+ const selectedCatalog = $(this).val();
32773
+ filterByCatalog(selectedCatalog);
32774
+ searchInput.searchBox('change');
32775
+ })
32776
+ }
32777
+
32778
+ function filterByCatalog(selectedCatalog) {
32779
+ if (loadedCatalogs.length <= 1 || selectedCatalog === "all") {
32780
+ loadedList = fullList.slice();
32781
+ } else {
32782
+ loadedList = fullList.filter(function(m) {
32783
+ return (m.catalog.name === selectedCatalog);
32784
+ })
32572
32785
  }
32786
+ refreshFilteredItems();
32787
+ searchInput.searchBox('count',filteredList.length+" / "+loadedList.length);
32573
32788
  }
32574
32789
 
32575
32790
  function refreshFilteredItems() {
32576
32791
  packageList.editableList('empty');
32577
32792
  var currentFilter = searchInput.searchBox('value').trim();
32578
- if (currentFilter === ""){
32793
+ if (currentFilter === "" && loadedList.length > 20){
32579
32794
  packageList.editableList('addItem',{count:loadedList.length})
32580
32795
  return;
32581
32796
  }
@@ -32586,7 +32801,6 @@ RED.palette.editor = (function() {
32586
32801
  if (filteredList.length === 0) {
32587
32802
  packageList.editableList('addItem',{});
32588
32803
  }
32589
-
32590
32804
  if (filteredList.length > 10) {
32591
32805
  packageList.editableList('addItem',{start:10,more:filteredList.length-10})
32592
32806
  }
@@ -32616,6 +32830,7 @@ RED.palette.editor = (function() {
32616
32830
  var updateDenyList = [];
32617
32831
 
32618
32832
  function init() {
32833
+ catalogues = RED.settings.theme('palette.catalogues')||['https://catalogue.nodered.org/catalogue.json']
32619
32834
  if (RED.settings.get('externalModules.palette.allowInstall', true) === false) {
32620
32835
  return;
32621
32836
  }
@@ -32793,7 +33008,8 @@ RED.palette.editor = (function() {
32793
33008
  });
32794
33009
 
32795
33010
 
32796
- nodeList = $('<ol>',{id:"red-ui-palette-module-list", style:"position: absolute;top: 35px;bottom: 0;left: 0;right: 0px;"}).appendTo(modulesTab).editableList({
33011
+ nodeList = $('<ol>',{id:"red-ui-palette-module-list"}).appendTo(modulesTab).editableList({
33012
+ class: "scrollable",
32797
33013
  addButton: false,
32798
33014
  scrollOnAdd: false,
32799
33015
  sort: function(A,B) {
@@ -32924,28 +33140,27 @@ RED.palette.editor = (function() {
32924
33140
  $('<div>',{class:"red-ui-search-empty"}).text(RED._('search.empty')).appendTo(container);
32925
33141
  }
32926
33142
  }
32927
- });
33143
+ })
32928
33144
  }
32929
33145
 
32930
33146
  function createInstallTab(content) {
32931
- var installTab = $('<div>',{class:"red-ui-palette-editor-tab hide"}).appendTo(content);
32932
-
33147
+ const installTab = $('<div>',{class:"red-ui-palette-editor-tab", style: "display: none;"}).appendTo(content);
32933
33148
  editorTabs.addTab({
32934
33149
  id: 'install',
32935
33150
  label: RED._('palette.editor.tab-install'),
32936
33151
  content: installTab
32937
33152
  })
32938
33153
 
32939
- var toolBar = $('<div>',{class:"red-ui-palette-editor-toolbar"}).appendTo(installTab);
32940
-
32941
- var searchDiv = $('<div>',{class:"red-ui-palette-search"}).appendTo(installTab);
33154
+ const toolBar = $('<div>',{class:"red-ui-palette-editor-toolbar"}).appendTo(installTab);
33155
+
33156
+ const searchDiv = $('<div>',{class:"red-ui-palette-search"}).appendTo(installTab);
32942
33157
  searchInput = $('<input type="text" data-i18n="[placeholder]palette.search"></input>')
32943
33158
  .appendTo(searchDiv)
32944
33159
  .searchBox({
32945
33160
  delay: 300,
32946
33161
  change: function() {
32947
33162
  var searchTerm = $(this).val().trim().toLowerCase();
32948
- if (searchTerm.length > 0) {
33163
+ if (searchTerm.length > 0 || loadedList.length < 20) {
32949
33164
  filteredList = loadedList.filter(function(m) {
32950
33165
  return (m.index.indexOf(searchTerm) > -1);
32951
33166
  }).map(function(f) { return {info:f}});
@@ -32955,19 +33170,26 @@ RED.palette.editor = (function() {
32955
33170
  searchInput.searchBox('count',loadedList.length);
32956
33171
  packageList.editableList('empty');
32957
33172
  packageList.editableList('addItem',{count:loadedList.length});
32958
-
32959
33173
  }
32960
33174
  }
32961
33175
  });
32962
33176
 
32963
- $('<span>').text(RED._("palette.editor.sort")+' ').appendTo(toolBar);
32964
- var sortGroup = $('<span class="button-group"></span>').appendTo(toolBar);
32965
- var sortRelevance = $('<a href="#" class="red-ui-palette-editor-install-sort-option red-ui-sidebar-header-button-toggle selected"><i class="fa fa-sort-amount-desc"></i></a>').appendTo(sortGroup);
32966
- var sortAZ = $('<a href="#" class="red-ui-palette-editor-install-sort-option red-ui-sidebar-header-button-toggle" data-i18n="palette.editor.sortAZ"></a>').appendTo(sortGroup);
32967
- var sortRecent = $('<a href="#" class="red-ui-palette-editor-install-sort-option red-ui-sidebar-header-button-toggle" data-i18n="palette.editor.sortRecent"></a>').appendTo(sortGroup);
33177
+ const catalogSelection = $('<select id="red-catalogue-filter-select">').appendTo(toolBar);
33178
+ catalogSelection.addClass('red-ui-palette-editor-catalogue-filter');
33179
+
33180
+ const toolBarActions = $('<div>',{class:"red-ui-palette-editor-toolbar-actions"}).appendTo(toolBar);
32968
33181
 
33182
+ $('<span>').text(RED._("palette.editor.sort")+' ').appendTo(toolBarActions);
33183
+ const sortGroup = $('<span class="button-group"></span>').appendTo(toolBarActions);
33184
+ const sortRelevance = $('<a href="#" class="red-ui-palette-editor-install-sort-option red-ui-sidebar-header-button-toggle selected"><i class="fa fa-sort-amount-desc"></i></a>').appendTo(sortGroup);
33185
+ const sortAZ = $('<a href="#" class="red-ui-palette-editor-install-sort-option red-ui-sidebar-header-button-toggle"><i class="fa fa-sort-alpha-asc"></i></a>').appendTo(sortGroup);
33186
+ const sortRecent = $('<a href="#" class="red-ui-palette-editor-install-sort-option red-ui-sidebar-header-button-toggle"><i class="fa fa-calendar"></i></a>').appendTo(sortGroup);
33187
+ RED.popover.tooltip(sortRelevance,RED._("palette.editor.sortRelevance"));
33188
+ RED.popover.tooltip(sortAZ,RED._("palette.editor.sortAZ"));
33189
+ RED.popover.tooltip(sortRecent,RED._("palette.editor.sortRecent"));
32969
33190
 
32970
- var sortOpts = [
33191
+
33192
+ const sortOpts = [
32971
33193
  {button: sortRelevance, func: sortModulesRelevance},
32972
33194
  {button: sortAZ, func: sortModulesAZ},
32973
33195
  {button: sortRecent, func: sortModulesRecent}
@@ -32985,7 +33207,7 @@ RED.palette.editor = (function() {
32985
33207
  });
32986
33208
  });
32987
33209
 
32988
- var refreshSpan = $('<span>').appendTo(toolBar);
33210
+ var refreshSpan = $('<span>').appendTo(toolBarActions);
32989
33211
  var refreshButton = $('<a href="#" class="red-ui-sidebar-header-button"><i class="fa fa-refresh"></i></a>').appendTo(refreshSpan);
32990
33212
  refreshButton.on("click", function(e) {
32991
33213
  e.preventDefault();
@@ -32995,7 +33217,8 @@ RED.palette.editor = (function() {
32995
33217
  })
32996
33218
  RED.popover.tooltip(refreshButton,RED._("palette.editor.refresh"));
32997
33219
 
32998
- packageList = $('<ol>',{style:"position: absolute;top: 79px;bottom: 0;left: 0;right: 0px;"}).appendTo(installTab).editableList({
33220
+ packageList = $('<ol>').appendTo(installTab).editableList({
33221
+ class: "scrollable",
32999
33222
  addButton: false,
33000
33223
  scrollOnAdd: false,
33001
33224
  addItem: function(container,i,object) {
@@ -33030,6 +33253,9 @@ RED.palette.editor = (function() {
33030
33253
  var metaRow = $('<div class="red-ui-palette-module-meta"></div>').appendTo(headerRow);
33031
33254
  $('<span class="red-ui-palette-module-version"><i class="fa fa-tag"></i> '+entry.version+'</span>').appendTo(metaRow);
33032
33255
  $('<span class="red-ui-palette-module-updated"><i class="fa fa-calendar"></i> '+formatUpdatedAt(entry.updated_at)+'</span>').appendTo(metaRow);
33256
+ if (loadedCatalogs.length > 1) {
33257
+ $('<span class="red-ui-palette-module-updated"><i class="fa fa-cubes"></i>' + (entry.catalog.name || entry.catalog.url) + '</span>').appendTo(metaRow);
33258
+ }
33033
33259
 
33034
33260
  var duplicateType = false;
33035
33261
  if (entry.types && entry.types.length > 0) {
@@ -33076,9 +33302,10 @@ RED.palette.editor = (function() {
33076
33302
  }
33077
33303
  }
33078
33304
  });
33305
+
33079
33306
 
33080
33307
  if (RED.settings.get('externalModules.palette.allowUpload', true) !== false) {
33081
- var uploadSpan = $('<span class="button-group">').prependTo(toolBar);
33308
+ var uploadSpan = $('<span class="button-group">').prependTo(toolBarActions);
33082
33309
  var uploadButton = $('<button type="button" class="red-ui-sidebar-header-button red-ui-palette-editor-upload-button"><label><i class="fa fa-upload"></i><form id="red-ui-palette-editor-upload-form" enctype="multipart/form-data"><input name="tarball" type="file" accept=".tgz"></label></button>').appendTo(uploadSpan);
33083
33310
 
33084
33311
  var uploadInput = uploadButton.find('input[type="file"]');
@@ -34074,7 +34301,10 @@ RED.editor = (function() {
34074
34301
  if (typeof editing_node[d] === "string" || typeof editing_node[d] === "number") {
34075
34302
  oldValues[d] = editing_node[d];
34076
34303
  } else {
34077
- oldValues[d] = $.extend(true,{},{v:editing_node[d]}).v;
34304
+ // Dont clone the group node `nodes` array
34305
+ if (editing_node.type !== 'group' || d !== "nodes") {
34306
+ oldValues[d] = $.extend(true,{},{v:editing_node[d]}).v;
34307
+ }
34078
34308
  }
34079
34309
  }
34080
34310
  }
@@ -35666,7 +35896,7 @@ RED.editor = (function() {
35666
35896
  }
35667
35897
 
35668
35898
  $('<div class="form-row">'+
35669
- '<label for="node-input-show-label-btn" data-i18n="editor.label"></label>'+
35899
+ '<label for="node-input-show-label" data-i18n="editor.label"></label>'+
35670
35900
  '<span style="margin-right: 2px;"/>'+
35671
35901
  '<input type="checkbox" id="node-input-show-label"/>'+
35672
35902
  '</div>').appendTo(dialogForm);
@@ -45551,6 +45781,7 @@ RED.actionList = (function() {
45551
45781
  items.push({type:t,def: def, label:getTypeLabel(t,def)});
45552
45782
  }
45553
45783
  });
45784
+ items.push({ type: 'junction', def: { inputs:1, outputs: 1, label: 'junction', type: 'junction'}, label: 'junction' })
45554
45785
  items.sort(sortTypeLabels);
45555
45786
 
45556
45787
  var commonCount = 0;
@@ -46278,7 +46509,7 @@ RED.subflow = (function() {
46278
46509
  for (i=0; i<nodeList.length;i++) {
46279
46510
  if (nodeList[i].g && !includedGroups.has(nodeList[i].g)) {
46280
46511
  if (containingGroup !== nodeList[i].g) {
46281
- RED.notify("Cannot create subflow across multiple groups","error");
46512
+ RED.notify(RED._("subflow.errors.acrossMultipleGroups"), "error");
46282
46513
  return;
46283
46514
  }
46284
46515
  }