@node-red/editor-client 2.2.0 → 2.2.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
@@ -343,8 +343,21 @@ var RED = (function() {
343
343
  if (/^#flow\/.+$/.test(currentHash)) {
344
344
  RED.workspaces.show(currentHash.substring(6),true);
345
345
  }
346
- if (RED.workspaces.active() === 0 && RED.workspaces.count() > 0) {
347
- RED.workspaces.show(RED.nodes.getWorkspaceOrder()[0])
346
+ if (RED.workspaces.count() > 0) {
347
+ const hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
348
+ const workspaces = RED.nodes.getWorkspaceOrder();
349
+ if (RED.workspaces.active() === 0) {
350
+ for (let index = 0; index < workspaces.length; index++) {
351
+ const ws = workspaces[index];
352
+ if (!hiddenTabs[ws]) {
353
+ RED.workspaces.show(ws);
354
+ break;
355
+ }
356
+ }
357
+ }
358
+ if (RED.workspaces.active() === 0) {
359
+ RED.workspaces.show(workspaces[0]);
360
+ }
348
361
  }
349
362
  } catch(err) {
350
363
  console.warn(err);
@@ -552,7 +565,7 @@ var RED = (function() {
552
565
  var parts = topic.split("/");
553
566
  var node = RED.nodes.node(parts[1]);
554
567
  if (node) {
555
- if (msg.hasOwnProperty("text") && msg.text !== null && /^[a-zA-Z]/.test(msg.text)) {
568
+ if (msg.hasOwnProperty("text") && msg.text !== null && /^[@a-zA-Z]/.test(msg.text)) {
556
569
  msg.text = node._(msg.text.toString(),{defaultValue:msg.text.toString()});
557
570
  }
558
571
  node.status = msg;
@@ -4190,6 +4203,14 @@ RED.nodes = (function() {
4190
4203
  RED.events.emit('nodes:add',n);
4191
4204
  }
4192
4205
  function addLink(l) {
4206
+ if (nodeLinks[l.source.id]) {
4207
+ const isUnique = nodeLinks[l.source.id].out.every(function(link) {
4208
+ return link.sourcePort !== l.sourcePort || link.target.id !== l.target.id
4209
+ })
4210
+ if (!isUnique) {
4211
+ return
4212
+ }
4213
+ }
4193
4214
  links.push(l);
4194
4215
  if (l.source) {
4195
4216
  // Possible the node hasn't been added yet
@@ -5717,7 +5738,7 @@ RED.nodes = (function() {
5717
5738
  n = new_nodes[i];
5718
5739
  if (n.wires) {
5719
5740
  for (var w1=0;w1<n.wires.length;w1++) {
5720
- var wires = (n.wires[w1] instanceof Array)?n.wires[w1]:[n.wires[w1]];
5741
+ var wires = (Array.isArray(n.wires[w1]))?n.wires[w1]:[n.wires[w1]];
5721
5742
  for (var w2=0;w2<wires.length;w2++) {
5722
5743
  if (node_map.hasOwnProperty(wires[w2])) {
5723
5744
  if (n.z === node_map[wires[w2]].z) {
@@ -11654,6 +11675,15 @@ RED.popover = (function() {
11654
11675
  }
11655
11676
  }
11656
11677
 
11678
+ target.on("remove", function (ev) {
11679
+ if (timer) {
11680
+ clearTimeout(timer);
11681
+ }
11682
+ if (active) {
11683
+ active = false;
11684
+ setTimeout(closePopup,delay.hide);
11685
+ }
11686
+ });
11657
11687
  if (trigger === 'hover') {
11658
11688
  target.on('mouseenter',function(e) {
11659
11689
  clearTimeout(timer);
@@ -12970,7 +13000,7 @@ RED.tabs = (function() {
12970
13000
  }
12971
13001
 
12972
13002
  // link.attr("title",tab.label);
12973
- RED.popover.tooltip(link,function() { return tab.label})
13003
+ RED.popover.tooltip(link,function() { return RED.utils.sanitize(tab.label); });
12974
13004
 
12975
13005
  if (options.onadd) {
12976
13006
  options.onadd(tab);
@@ -15223,202 +15253,208 @@ RED.deploy = (function() {
15223
15253
  },delta);
15224
15254
  });
15225
15255
  }
15226
- function save(skipValidation,force) {
15227
- if (!$("#red-ui-header-button-deploy").hasClass("disabled")) {
15228
- if (!RED.user.hasPermission("flows.write")) {
15229
- RED.notify(RED._("user.errors.deploy"),"error");
15230
- return;
15231
- }
15232
- if (!skipValidation) {
15233
- var hasUnknown = false;
15234
- var hasInvalid = false;
15235
- var hasUnusedConfig = false;
15236
-
15237
- var unknownNodes = [];
15238
- var invalidNodes = [];
15256
+ function save(skipValidation, force) {
15257
+ if ($("#red-ui-header-button-deploy").hasClass("disabled")) {
15258
+ return; //deploy is disabled
15259
+ }
15260
+ if ($("#red-ui-header-shade").is(":visible")) {
15261
+ return; //deploy is shaded
15262
+ }
15263
+ if (!RED.user.hasPermission("flows.write")) {
15264
+ RED.notify(RED._("user.errors.deploy"), "error");
15265
+ return;
15266
+ }
15267
+ let hasUnusedConfig = false;
15268
+ if (!skipValidation) {
15269
+ let hasUnknown = false;
15270
+ let hasInvalid = false;
15271
+ const unknownNodes = [];
15272
+ const invalidNodes = [];
15239
15273
 
15240
- RED.nodes.eachConfig(function(node) {
15241
- if (!node.valid && !node.d) {
15242
- invalidNodes.push(getNodeInfo(node));
15243
- }
15244
- if (node.type === "unknown") {
15245
- if (unknownNodes.indexOf(node.name) == -1) {
15246
- unknownNodes.push(node.name);
15247
- }
15248
- }
15249
- });
15250
- RED.nodes.eachNode(function(node) {
15251
- if (!node.valid && !node.d) {
15252
- invalidNodes.push(getNodeInfo(node));
15274
+ RED.nodes.eachConfig(function (node) {
15275
+ if (node.valid === undefined) {
15276
+ RED.editor.validateNode(node);
15277
+ }
15278
+ if (!node.valid && !node.d) {
15279
+ invalidNodes.push(getNodeInfo(node));
15280
+ }
15281
+ if (node.type === "unknown") {
15282
+ if (unknownNodes.indexOf(node.name) == -1) {
15283
+ unknownNodes.push(node.name);
15253
15284
  }
15254
- if (node.type === "unknown") {
15255
- if (unknownNodes.indexOf(node.name) == -1) {
15256
- unknownNodes.push(node.name);
15257
- }
15285
+ }
15286
+ });
15287
+ RED.nodes.eachNode(function (node) {
15288
+ if (!node.valid && !node.d) {
15289
+ invalidNodes.push(getNodeInfo(node));
15290
+ }
15291
+ if (node.type === "unknown") {
15292
+ if (unknownNodes.indexOf(node.name) == -1) {
15293
+ unknownNodes.push(node.name);
15258
15294
  }
15259
- });
15260
- hasUnknown = unknownNodes.length > 0;
15261
- hasInvalid = invalidNodes.length > 0;
15295
+ }
15296
+ });
15297
+ hasUnknown = unknownNodes.length > 0;
15298
+ hasInvalid = invalidNodes.length > 0;
15262
15299
 
15263
- var unusedConfigNodes = [];
15264
- RED.nodes.eachConfig(function(node) {
15265
- if ((node._def.hasUsers !== false) && (node.users.length === 0)) {
15266
- unusedConfigNodes.push(getNodeInfo(node));
15267
- hasUnusedConfig = true;
15268
- }
15269
- });
15300
+ const unusedConfigNodes = [];
15301
+ RED.nodes.eachConfig(function (node) {
15302
+ if ((node._def.hasUsers !== false) && (node.users.length === 0)) {
15303
+ unusedConfigNodes.push(getNodeInfo(node));
15304
+ hasUnusedConfig = true;
15305
+ }
15306
+ });
15270
15307
 
15271
- var showWarning = false;
15272
- var notificationMessage;
15273
- var notificationButtons = [];
15274
- var notification;
15275
- if (hasUnknown && !ignoreDeployWarnings.unknown) {
15276
- showWarning = true;
15277
- notificationMessage = "<p>"+RED._('deploy.confirm.unknown')+"</p>"+
15278
- '<ul class="red-ui-deploy-dialog-confirm-list"><li>'+cropList(unknownNodes).map(function(n) { return sanitize(n) }).join("</li><li>")+"</li></ul><p>"+
15279
- RED._('deploy.confirm.confirm')+
15280
- "</p>";
15281
-
15282
- notificationButtons= [
15283
- {
15284
- id: "red-ui-deploy-dialog-confirm-deploy-deploy",
15285
- text: RED._("deploy.confirm.button.confirm"),
15286
- class: "primary",
15287
- click: function() {
15288
- save(true);
15289
- notification.close();
15290
- }
15308
+ let showWarning = false;
15309
+ let notificationMessage;
15310
+ let notificationButtons = [];
15311
+ let notification;
15312
+ if (hasUnknown && !ignoreDeployWarnings.unknown) {
15313
+ showWarning = true;
15314
+ notificationMessage = "<p>" + RED._('deploy.confirm.unknown') + "</p>" +
15315
+ '<ul class="red-ui-deploy-dialog-confirm-list"><li>' + cropList(unknownNodes).map(function (n) { return sanitize(n) }).join("</li><li>") + "</li></ul><p>" +
15316
+ RED._('deploy.confirm.confirm') +
15317
+ "</p>";
15318
+
15319
+ notificationButtons = [
15320
+ {
15321
+ id: "red-ui-deploy-dialog-confirm-deploy-deploy",
15322
+ text: RED._("deploy.confirm.button.confirm"),
15323
+ class: "primary",
15324
+ click: function () {
15325
+ save(true);
15326
+ notification.close();
15291
15327
  }
15292
- ];
15293
- } else if (hasInvalid && !ignoreDeployWarnings.invalid) {
15294
- showWarning = true;
15295
- invalidNodes.sort(sortNodeInfo);
15296
-
15297
- notificationMessage = "<p>"+RED._('deploy.confirm.improperlyConfigured')+"</p>"+
15298
- '<ul class="red-ui-deploy-dialog-confirm-list"><li>'+cropList(invalidNodes.map(function(A) { return sanitize( (A.tab?"["+A.tab+"] ":"")+A.label+" ("+A.type+")")})).join("</li><li>")+"</li></ul><p>"+
15299
- RED._('deploy.confirm.confirm')+
15300
- "</p>";
15301
- notificationButtons= [
15302
- {
15303
- id: "red-ui-deploy-dialog-confirm-deploy-deploy",
15304
- text: RED._("deploy.confirm.button.confirm"),
15305
- class: "primary",
15306
- click: function() {
15307
- save(true);
15308
- notification.close();
15309
- }
15328
+ }
15329
+ ];
15330
+ } else if (hasInvalid && !ignoreDeployWarnings.invalid) {
15331
+ showWarning = true;
15332
+ invalidNodes.sort(sortNodeInfo);
15333
+
15334
+ notificationMessage = "<p>" + RED._('deploy.confirm.improperlyConfigured') + "</p>" +
15335
+ '<ul class="red-ui-deploy-dialog-confirm-list"><li>' + cropList(invalidNodes.map(function (A) { return sanitize((A.tab ? "[" + A.tab + "] " : "") + A.label + " (" + A.type + ")") })).join("</li><li>") + "</li></ul><p>" +
15336
+ RED._('deploy.confirm.confirm') +
15337
+ "</p>";
15338
+ notificationButtons = [
15339
+ {
15340
+ id: "red-ui-deploy-dialog-confirm-deploy-deploy",
15341
+ text: RED._("deploy.confirm.button.confirm"),
15342
+ class: "primary",
15343
+ click: function () {
15344
+ save(true);
15345
+ notification.close();
15310
15346
  }
15311
- ];
15312
- }
15313
- if (showWarning) {
15314
- notificationButtons.unshift(
15315
- {
15316
- text: RED._("common.label.cancel"),
15317
- click: function() {
15318
- notification.close();
15319
- }
15347
+ }
15348
+ ];
15349
+ }
15350
+ if (showWarning) {
15351
+ notificationButtons.unshift(
15352
+ {
15353
+ text: RED._("common.label.cancel"),
15354
+ click: function () {
15355
+ notification.close();
15320
15356
  }
15321
- );
15322
- notification = RED.notify(notificationMessage,{
15323
- modal: true,
15324
- fixed: true,
15325
- buttons:notificationButtons
15326
- });
15327
- return;
15328
- }
15357
+ }
15358
+ );
15359
+ notification = RED.notify(notificationMessage, {
15360
+ modal: true,
15361
+ fixed: true,
15362
+ buttons: notificationButtons
15363
+ });
15364
+ return;
15329
15365
  }
15366
+ }
15330
15367
 
15331
- var nns = RED.nodes.createCompleteNodeSet();
15368
+ const nns = RED.nodes.createCompleteNodeSet();
15369
+ const startTime = Date.now();
15332
15370
 
15333
- var startTime = Date.now();
15334
- $(".red-ui-deploy-button-content").css('opacity',0);
15335
- $(".red-ui-deploy-button-spinner").show();
15336
- $("#red-ui-header-button-deploy").addClass("disabled");
15371
+ $(".red-ui-deploy-button-content").css('opacity', 0);
15372
+ $(".red-ui-deploy-button-spinner").show();
15373
+ $("#red-ui-header-button-deploy").addClass("disabled");
15337
15374
 
15338
- var data = {flows:nns};
15375
+ const data = { flows: nns };
15339
15376
 
15340
- if (!force) {
15341
- data.rev = RED.nodes.version();
15342
- }
15377
+ if (!force) {
15378
+ data.rev = RED.nodes.version();
15379
+ }
15343
15380
 
15344
- deployInflight = true;
15345
- $("#red-ui-header-shade").show();
15346
- $("#red-ui-editor-shade").show();
15347
- $("#red-ui-palette-shade").show();
15348
- $("#red-ui-sidebar-shade").show();
15349
- $.ajax({
15350
- url:"flows",
15351
- type: "POST",
15352
- data: JSON.stringify(data),
15353
- contentType: "application/json; charset=utf-8",
15354
- headers: {
15355
- "Node-RED-Deployment-Type":deploymentType
15381
+ deployInflight = true;
15382
+ $("#red-ui-header-shade").show();
15383
+ $("#red-ui-editor-shade").show();
15384
+ $("#red-ui-palette-shade").show();
15385
+ $("#red-ui-sidebar-shade").show();
15386
+ $.ajax({
15387
+ url: "flows",
15388
+ type: "POST",
15389
+ data: JSON.stringify(data),
15390
+ contentType: "application/json; charset=utf-8",
15391
+ headers: {
15392
+ "Node-RED-Deployment-Type": deploymentType
15393
+ }
15394
+ }).done(function (data, textStatus, xhr) {
15395
+ RED.nodes.dirty(false);
15396
+ RED.nodes.version(data.rev);
15397
+ RED.nodes.originalFlow(nns);
15398
+ if (hasUnusedConfig) {
15399
+ RED.notify(
15400
+ '<p>' + RED._("deploy.successfulDeploy") + '</p>' +
15401
+ '<p>' + RED._("deploy.unusedConfigNodes") + ' <a href="#" onclick="RED.sidebar.config.show(true); return false;">' + RED._("deploy.unusedConfigNodesLink") + '</a></p>', "success", false, 6000);
15402
+ } else {
15403
+ RED.notify('<p>' + RED._("deploy.successfulDeploy") + '</p>', "success");
15404
+ }
15405
+ RED.nodes.eachNode(function (node) {
15406
+ if (node.changed) {
15407
+ node.dirty = true;
15408
+ node.changed = false;
15356
15409
  }
15357
- }).done(function(data,textStatus,xhr) {
15358
- RED.nodes.dirty(false);
15359
- RED.nodes.version(data.rev);
15360
- RED.nodes.originalFlow(nns);
15361
- if (hasUnusedConfig) {
15362
- RED.notify(
15363
- '<p>'+RED._("deploy.successfulDeploy")+'</p>'+
15364
- '<p>'+RED._("deploy.unusedConfigNodes")+' <a href="#" onclick="RED.sidebar.config.show(true); return false;">'+RED._("deploy.unusedConfigNodesLink")+'</a></p>',"success",false,6000);
15365
- } else {
15366
- RED.notify('<p>'+RED._("deploy.successfulDeploy")+'</p>',"success");
15410
+ if (node.moved) {
15411
+ node.dirty = true;
15412
+ node.moved = false;
15367
15413
  }
15368
- RED.nodes.eachNode(function(node) {
15369
- if (node.changed) {
15370
- node.dirty = true;
15371
- node.changed = false;
15372
- }
15373
- if (node.moved) {
15374
- node.dirty = true;
15375
- node.moved = false;
15376
- }
15377
- if(node.credentials) {
15378
- delete node.credentials;
15379
- }
15380
- });
15381
- RED.nodes.eachConfig(function (confNode) {
15382
- confNode.changed = false;
15383
- if (confNode.credentials) {
15384
- delete confNode.credentials;
15385
- }
15386
- });
15387
- RED.nodes.eachSubflow(function(subflow) {
15388
- subflow.changed = false;
15389
- });
15390
- RED.nodes.eachWorkspace(function(ws) {
15391
- ws.changed = false;
15392
- });
15393
- // Once deployed, cannot undo back to a clean state
15394
- RED.history.markAllDirty();
15395
- RED.view.redraw();
15396
- RED.events.emit("deploy");
15397
- }).fail(function(xhr,textStatus,err) {
15398
- RED.nodes.dirty(true);
15399
- $("#red-ui-header-button-deploy").removeClass("disabled");
15400
- if (xhr.status === 401) {
15401
- RED.notify(RED._("deploy.deployFailed",{message:RED._("user.notAuthorized")}),"error");
15402
- } else if (xhr.status === 409) {
15403
- resolveConflict(nns, true);
15404
- } else if (xhr.responseText) {
15405
- RED.notify(RED._("deploy.deployFailed",{message:xhr.responseText}),"error");
15406
- } else {
15407
- RED.notify(RED._("deploy.deployFailed",{message:RED._("deploy.errors.noResponse")}),"error");
15414
+ if (node.credentials) {
15415
+ delete node.credentials;
15408
15416
  }
15409
- }).always(function() {
15410
- deployInflight = false;
15411
- var delta = Math.max(0,300-(Date.now()-startTime));
15412
- setTimeout(function() {
15413
- $(".red-ui-deploy-button-content").css('opacity',1);
15414
- $(".red-ui-deploy-button-spinner").hide();
15415
- $("#red-ui-header-shade").hide();
15416
- $("#red-ui-editor-shade").hide();
15417
- $("#red-ui-palette-shade").hide();
15418
- $("#red-ui-sidebar-shade").hide();
15419
- },delta);
15420
15417
  });
15421
- }
15418
+ RED.nodes.eachConfig(function (confNode) {
15419
+ confNode.changed = false;
15420
+ if (confNode.credentials) {
15421
+ delete confNode.credentials;
15422
+ }
15423
+ });
15424
+ RED.nodes.eachSubflow(function (subflow) {
15425
+ subflow.changed = false;
15426
+ });
15427
+ RED.nodes.eachWorkspace(function (ws) {
15428
+ ws.changed = false;
15429
+ });
15430
+ // Once deployed, cannot undo back to a clean state
15431
+ RED.history.markAllDirty();
15432
+ RED.view.redraw();
15433
+ RED.events.emit("deploy");
15434
+ }).fail(function (xhr, textStatus, err) {
15435
+ RED.nodes.dirty(true);
15436
+ $("#red-ui-header-button-deploy").removeClass("disabled");
15437
+ if (xhr.status === 401) {
15438
+ RED.notify(RED._("deploy.deployFailed", { message: RED._("user.notAuthorized") }), "error");
15439
+ } else if (xhr.status === 409) {
15440
+ resolveConflict(nns, true);
15441
+ } else if (xhr.responseText) {
15442
+ RED.notify(RED._("deploy.deployFailed", { message: xhr.responseText }), "error");
15443
+ } else {
15444
+ RED.notify(RED._("deploy.deployFailed", { message: RED._("deploy.errors.noResponse") }), "error");
15445
+ }
15446
+ }).always(function () {
15447
+ deployInflight = false;
15448
+ const delta = Math.max(0, 300 - (Date.now() - startTime));
15449
+ setTimeout(function () {
15450
+ $(".red-ui-deploy-button-content").css('opacity', 1);
15451
+ $(".red-ui-deploy-button-spinner").hide();
15452
+ $("#red-ui-header-shade").hide();
15453
+ $("#red-ui-editor-shade").hide();
15454
+ $("#red-ui-palette-shade").hide();
15455
+ $("#red-ui-sidebar-shade").hide();
15456
+ }, delta);
15457
+ });
15422
15458
  }
15423
15459
  return {
15424
15460
  init: init,
@@ -18292,7 +18328,7 @@ RED.keyboard = (function() {
18292
18328
  pane.find("#red-ui-settings-tab-keyboard-filter").searchBox({
18293
18329
  delay: 100,
18294
18330
  change: function() {
18295
- var filterValue = $(this).val().trim();
18331
+ var filterValue = $(this).val().trim().toLowerCase();
18296
18332
  if (filterValue === "") {
18297
18333
  shortcutList.editableList('filter', null);
18298
18334
  } else {
@@ -18641,9 +18677,22 @@ RED.workspaces = (function() {
18641
18677
  onselect: "core:show-last-hidden-flow"
18642
18678
  }
18643
18679
  ]
18644
- if (hideStack.length > 0) {
18680
+ let hiddenFlows = new Set()
18681
+ for (let i = 0; i < hideStack.length; i++) {
18682
+ let ids = hideStack[i]
18683
+ if (!Array.isArray(ids)) {
18684
+ ids = [ids]
18685
+ }
18686
+ ids.forEach(id => {
18687
+ if (RED.nodes.workspace(id)) {
18688
+ hiddenFlows.add(id)
18689
+ }
18690
+ })
18691
+ }
18692
+ const flowCount = hiddenFlows.size;
18693
+ if (flowCount > 0) {
18645
18694
  menuItems.unshift({
18646
- label: RED._("workspace.hiddenFlows",{count: hideStack.length}),
18695
+ label: RED._("workspace.hiddenFlows",{count: flowCount}),
18647
18696
  onselect: "core:list-hidden-flows"
18648
18697
  })
18649
18698
  }
@@ -19238,6 +19287,7 @@ RED.view = (function() {
19238
19287
  .on("mousedown", canvasMouseDown)
19239
19288
  .on("mouseup", canvasMouseUp)
19240
19289
  .on("mouseenter", function() {
19290
+ d3.select(document).on('mouseup.red-ui-workspace-tracker', null)
19241
19291
  if (lasso) {
19242
19292
  if (d3.event.buttons !== 1) {
19243
19293
  lasso.remove();
@@ -19253,6 +19303,7 @@ RED.view = (function() {
19253
19303
  }
19254
19304
  }
19255
19305
  })
19306
+ .on("mouseleave", canvasMouseLeave)
19256
19307
  .on("touchend", function() {
19257
19308
  d3.event.preventDefault();
19258
19309
  clearTimeout(touchStartTime);
@@ -19392,6 +19443,9 @@ RED.view = (function() {
19392
19443
  drag_lines = [];
19393
19444
 
19394
19445
  RED.events.on("workspace:change",function(event) {
19446
+ // Just in case the mouse left the workspace whilst doing an action,
19447
+ // put us back into default mode so the refresh works
19448
+ mouse_mode = 0
19395
19449
  if (event.old !== 0) {
19396
19450
  workspaceScrollPositions[event.old] = {
19397
19451
  left:chart.scrollLeft(),
@@ -20755,10 +20809,19 @@ RED.view = (function() {
20755
20809
  redraw();
20756
20810
  }
20757
20811
  }
20758
-
20812
+ function canvasMouseLeave() {
20813
+ if (mouse_mode !== 0 && d3.event.buttons !== 0) {
20814
+ d3.select(document).on('mouseup.red-ui-workspace-tracker', function() {
20815
+ d3.select(document).on('mouseup.red-ui-workspace-tracker', null)
20816
+ canvasMouseUp.call(this)
20817
+ })
20818
+ }
20819
+ }
20759
20820
  function canvasMouseUp() {
20760
20821
  lastClickPosition = [d3.event.offsetX/scaleFactor,d3.event.offsetY/scaleFactor];
20761
- if (RED.view.DEBUG) { console.warn("canvasMouseUp", mouse_mode); }
20822
+ if (RED.view.DEBUG) {
20823
+ console.warn("canvasMouseUp", { mouse_mode, point: d3.mouse(this), event: d3.event });
20824
+ }
20762
20825
  var i;
20763
20826
  var historyEvent;
20764
20827
  if (mouse_mode === RED.state.PANNING) {
@@ -21352,14 +21415,21 @@ RED.view = (function() {
21352
21415
  var removedSubflowStatus;
21353
21416
  var subflowInstances = [];
21354
21417
  var historyEvents = [];
21355
-
21418
+ var addToRemovedLinks = function(links) {
21419
+ if(!links) { return; }
21420
+ var _links = Array.isArray(links) ? links : [links];
21421
+ _links.forEach(function(l) {
21422
+ removedLinks.push(l);
21423
+ selectedLinks.remove(l);
21424
+ })
21425
+ }
21356
21426
  if (reconnectWires) {
21357
21427
  var reconnectResult = RED.nodes.detachNodes(movingSet.nodes())
21358
21428
  var addedLinks = reconnectResult.newLinks;
21359
21429
  if (addedLinks.length > 0) {
21360
21430
  historyEvents.push({ t:'add', links: addedLinks })
21361
21431
  }
21362
- removedLinks = removedLinks.concat(reconnectResult.removedLinks)
21432
+ addToRemovedLinks(reconnectResult.removedLinks)
21363
21433
  }
21364
21434
 
21365
21435
  var startDirty = RED.nodes.dirty();
@@ -21391,7 +21461,7 @@ RED.view = (function() {
21391
21461
  var removedEntities = RED.nodes.remove(node.id);
21392
21462
  removedNodes.push(node);
21393
21463
  removedNodes = removedNodes.concat(removedEntities.nodes);
21394
- removedLinks = removedLinks.concat(removedEntities.links);
21464
+ addToRemovedLinks(removedEntities.links);
21395
21465
  if (node.g) {
21396
21466
  var group = RED.nodes.group(node.g);
21397
21467
  if (selectedGroups.indexOf(group) === -1) {
@@ -21424,20 +21494,20 @@ RED.view = (function() {
21424
21494
  if (removedSubflowOutputs.length > 0) {
21425
21495
  result = RED.subflow.removeOutput(removedSubflowOutputs);
21426
21496
  if (result) {
21427
- removedLinks = removedLinks.concat(result.links);
21497
+ addToRemovedLinks(result.links);
21428
21498
  }
21429
21499
  }
21430
21500
  // Assume 0/1 inputs
21431
21501
  if (removedSubflowInputs.length == 1) {
21432
21502
  result = RED.subflow.removeInput();
21433
21503
  if (result) {
21434
- removedLinks = removedLinks.concat(result.links);
21504
+ addToRemovedLinks(result.links);
21435
21505
  }
21436
21506
  }
21437
21507
  if (removedSubflowStatus) {
21438
21508
  result = RED.subflow.removeStatus();
21439
21509
  if (result) {
21440
- removedLinks = removedLinks.concat(result.links);
21510
+ addToRemovedLinks(result.links);
21441
21511
  }
21442
21512
  }
21443
21513
 
@@ -22595,6 +22665,9 @@ RED.view = (function() {
22595
22665
  function portMouseOutProxy(e) { portMouseOut(d3.select(this), this.__data__,this.__portType__,this.__portIndex__, e); }
22596
22666
 
22597
22667
  function linkMouseDown(d) {
22668
+ if (RED.view.DEBUG) {
22669
+ console.warn("linkMouseDown", { mouse_mode, point: d3.mouse(this), event: d3.event });
22670
+ }
22598
22671
  if (mouse_mode === RED.state.SELECTING_NODE) {
22599
22672
  d3.event.stopPropagation();
22600
22673
  return;
@@ -24628,7 +24701,7 @@ RED.view = (function() {
24628
24701
  node.dirty = true;
24629
24702
  RED.workspaces.show(node.z);
24630
24703
 
24631
- var screenSize = [chart.width()/scaleFactor,chart.height()/scaleFactor];
24704
+ var screenSize = [chart[0].clientWidth/scaleFactor,chart[0].clientHeight/scaleFactor];
24632
24705
  var scrollPos = [chart.scrollLeft()/scaleFactor,chart.scrollTop()/scaleFactor];
24633
24706
  var cx = node.x;
24634
24707
  var cy = node.y;
@@ -26469,7 +26542,7 @@ RED.palette = (function() {
26469
26542
  }
26470
26543
 
26471
26544
  function escapeCategory(category) {
26472
- return category.replace(/[ /.]/g,"_");
26545
+ return category.replace(/[\x00-\x2c\x2e-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]/g,"_");
26473
26546
  }
26474
26547
  function addNodeType(nt,def) {
26475
26548
  if (getPaletteNode(nt).length) {
@@ -29045,7 +29118,7 @@ RED.sidebar.config = (function() {
29045
29118
  refreshConfigNodeList();
29046
29119
  }
29047
29120
  });
29048
- RED.popover.tooltip($('#red-ui-sidebar-config-filter-all'), RED._("sidebar.config.showAllUnusedConfigNodes"));
29121
+ RED.popover.tooltip($('#red-ui-sidebar-config-filter-all'), RED._("sidebar.config.showAllConfigNodes"));
29049
29122
  RED.popover.tooltip($('#red-ui-sidebar-config-filter-unused'), RED._("sidebar.config.showAllUnusedConfigNodes"));
29050
29123
 
29051
29124
  }
@@ -32646,8 +32719,7 @@ RED.editor = (function() {
32646
32719
  if (!node._def.defaults || !node._def.defaults.hasOwnProperty("icon")) {
32647
32720
  var icon = $("#red-ui-editor-node-icon").val()||"";
32648
32721
  if (!this.isDefaultIcon) {
32649
- if ((icon !== node.icon) &&
32650
- (icon !== "")) {
32722
+ if ((node.icon && icon !== node.icon) || (!node.icon && icon !== "")) {
32651
32723
  editState.changes.icon = node.icon;
32652
32724
  node.icon = icon;
32653
32725
  editState.changed = true;
@@ -33451,7 +33523,7 @@ RED.editor = (function() {
33451
33523
  newValue = "";
33452
33524
  }
33453
33525
  }
33454
- if (node[d] != newValue) {
33526
+ if (!isEqual(node[d], newValue)) {
33455
33527
  if (node._def.defaults[d].type) {
33456
33528
  // Change to a related config node
33457
33529
  var configNode = RED.nodes.node(node[d]);
@@ -33483,6 +33555,23 @@ RED.editor = (function() {
33483
33555
  }
33484
33556
  });
33485
33557
 
33558
+ /**
33559
+ * Compares `newValue` with `originalValue` for equality.
33560
+ * @param {*} originalValue Original value
33561
+ * @param {*} newValue New value
33562
+ * @returns {boolean} true if originalValue equals newValue, otherwise false
33563
+ */
33564
+ function isEqual(originalValue, newValue) {
33565
+ try {
33566
+ if(originalValue == newValue) {
33567
+ return true;
33568
+ }
33569
+ return JSON.stringify(originalValue) === JSON.stringify(newValue);
33570
+ } catch (err) {
33571
+ return false;
33572
+ }
33573
+ }
33574
+
33486
33575
  /**
33487
33576
  * Update the node credentials from the edit form
33488
33577
  * @param node - the node containing the credentials
@@ -34269,8 +34358,12 @@ RED.editor = (function() {
34269
34358
  style: "width:100%",
34270
34359
  class: "node-input-env-value",
34271
34360
  type: "text",
34272
- }).attr("autocomplete","disable").appendTo(envRow)
34273
- valueField.typedInput({default:'str',types:isTemplateNode?DEFAULT_ENV_TYPE_LIST:DEFAULT_ENV_TYPE_LIST_INC_CRED});
34361
+ }).attr("autocomplete","disable").appendTo(envRow);
34362
+ var types = (opt.ui && opt.ui.opts && opt.ui.opts.types);
34363
+ if (!types) {
34364
+ types = isTemplateNode ? DEFAULT_ENV_TYPE_LIST : DEFAULT_ENV_TYPE_LIST_INC_CRED;
34365
+ }
34366
+ valueField.typedInput({default:'str',types:types});
34274
34367
  valueField.typedInput('type', opt.type);
34275
34368
  if (opt.type === "cred") {
34276
34369
  if (opt.value) {
@@ -34322,6 +34415,11 @@ RED.editor = (function() {
34322
34415
  }
34323
34416
  opt.ui.label = opt.ui.label || {};
34324
34417
  opt.ui.type = opt.ui.type || "input";
34418
+ if ((opt.ui.type === "cred") &&
34419
+ opt.ui.opts &&
34420
+ opt.ui.opts.types) {
34421
+ opt.ui.type = "input";
34422
+ }
34325
34423
 
34326
34424
  var uiRow = $('<div/>').appendTo(container).hide();
34327
34425
  // save current info for reverting on cancel
@@ -36191,7 +36289,7 @@ RED.editor = (function() {
36191
36289
  'b': { before:"**", after: "**", tooltip: RED._("markdownEditor.bold")},
36192
36290
  'i': { before:"_", after: "_", tooltip: RED._("markdownEditor.italic")},
36193
36291
  'code': { before:"`", after: "`", tooltip: RED._("markdownEditor.code")},
36194
- 'ol': { before:" * ", newline: true, tooltip: RED._("markdownEditor.ordered-list")},
36292
+ 'ol': { before:" 1. ", newline: true, tooltip: RED._("markdownEditor.ordered-list")},
36195
36293
  'ul': { before:" - ", newline: true, tooltip: RED._("markdownEditor.unordered-list")},
36196
36294
  'bq': { before:"> ", newline: true, tooltip: RED._("markdownEditor.quote")},
36197
36295
  'link': { before:"[", after: "]()", tooltip: RED._("markdownEditor.link")},
@@ -37058,7 +37156,7 @@ RED.editor.codeEditor.monaco = (function() {
37058
37156
  createMonacoCompletionItem("set (flow context)", 'flow.set("${1:name}", ${1:value});','Set a value in flow context',range),
37059
37157
  createMonacoCompletionItem("get (global context)", 'global.get("${1:name}");','Get a value from global context',range),
37060
37158
  createMonacoCompletionItem("set (global context)", 'global.set("${1:name}", ${1:value});','Set a value in global context',range),
37061
- createMonacoCompletionItem("get (env)", 'env.get("${1:name}");','Get env variable value',range),
37159
+ createMonacoCompletionItem("get (env)", 'env.get("${1|NR_NODE_ID,NR_NODE_NAME,NR_NODE_PATH,NR_GROUP_ID,NR_GROUP_NAME,NR_FLOW_ID,NR_FLOW_NAME|}");','Get env variable value',range),
37062
37160
  createMonacoCompletionItem("cloneMessage (RED.util)", 'RED.util.cloneMessage(${1:msg});',
37063
37161
  ["```typescript",
37064
37162
  "RED.util.cloneMessage<T extends registry.NodeMessage>(msg: T): T",
@@ -40006,106 +40104,112 @@ RED.library = (function() {
40006
40104
  options.onconfirm(item);
40007
40105
  }
40008
40106
  });
40009
- var itemTools = $("<div>").css({position: "absolute",bottom:"6px",right:"8px"});
40010
- var menuButton = $('<button class="red-ui-button red-ui-button-small" type="button"><i class="fa fa-ellipsis-h"></i></button>')
40011
- .on("click", function(evt) {
40012
- evt.preventDefault();
40013
- evt.stopPropagation();
40014
- var elementPos = menuButton.offset();
40015
-
40016
- var menuOptionMenu = RED.menu.init({id:"red-ui-library-browser-menu",
40017
- options: [
40018
- {id:"red-ui-library-browser-menu-addFolder",label:RED._("library.newFolder"), onselect: function() {
40019
- var defaultFolderName = "new-folder";
40020
- var defaultFolderNameMatches = {};
40021
-
40022
- var selected = dirList.treeList('selected');
40023
- if (!selected.children) {
40024
- selected = selected.parent;
40025
- }
40026
- var complete = function() {
40027
- selected.children.forEach(function(c) {
40028
- if (/^new-folder/.test(c.label)) {
40029
- defaultFolderNameMatches[c.label] = true
40030
- }
40031
- });
40032
- var folderIndex = 2;
40033
- while(defaultFolderNameMatches[defaultFolderName]) {
40034
- defaultFolderName = "new-folder-"+(folderIndex++)
40035
- }
40036
-
40037
- selected.treeList.expand();
40038
- var input = $('<input type="text" class="red-ui-treeList-input">').val(defaultFolderName);
40039
- var newItem = {
40040
- icon: "fa fa-folder-o",
40041
- children:[],
40042
- path: selected.path,
40043
- element: input
40044
- }
40045
- var confirmAdd = function() {
40046
- var val = input.val().trim();
40047
- if (val === "") {
40048
- cancelAdd();
40049
- return;
40050
- } else {
40051
- for (var i=0;i<selected.children.length;i++) {
40052
- if (selected.children[i].label === val) {
40053
- cancelAdd();
40054
- return;
40055
- }
40056
- }
40057
- }
40058
- newItem.treeList.remove();
40059
- var finalItem = {
40060
- library: selected.library,
40061
- type: selected.type,
40062
- icon: "fa fa-folder",
40063
- children:[],
40064
- label: val,
40065
- path: newItem.path+val+"/"
40066
- }
40067
- selected.treeList.addChild(finalItem,true);
40068
- }
40069
- var cancelAdd = function() {
40070
- newItem.treeList.remove();
40071
- }
40072
- input.on('keydown', function(evt) {
40073
- evt.stopPropagation();
40074
- if (evt.keyCode === 13) {
40075
- confirmAdd();
40076
- } else if (evt.keyCode === 27) {
40077
- cancelAdd();
40078
- }
40079
- })
40080
- input.on("blur", function() {
40081
- confirmAdd();
40082
- })
40083
- selected.treeList.addChild(newItem);
40084
- setTimeout(function() {
40085
- input.trigger("focus");
40086
- input.select();
40087
- },400);
40088
- }
40089
- selected.treeList.expand(complete);
40090
-
40091
- } },
40092
- // null,
40093
- // {id:"red-ui-library-browser-menu-rename",label:"Rename", onselect: function() {} },
40094
- // {id:"red-ui-library-browser-menu-delete",label:"Delete", onselect: function() {} }
40095
- ]
40096
- }).on('mouseleave', function(){ $(this).remove(); dirList.focus() })
40097
- .on('mouseup', function() { var self = $(this);self.hide(); dirList.focus(); setTimeout(function() { self.remove() },100)})
40098
- .appendTo("body");
40099
- menuOptionMenu.css({
40100
- position: "absolute",
40101
- top: elementPos.top+"px",
40102
- left: (elementPos.left - menuOptionMenu.width() + 20)+"px"
40103
- }).show();
40104
-
40105
- }).appendTo(itemTools);
40107
+ var itemTools = null;
40106
40108
  if (options.folderTools) {
40107
40109
  dirList.on('treelistselect', function(event, item) {
40108
40110
  if (item.writable !== false && item.treeList) {
40111
+ if (itemTools) {
40112
+ itemTools.remove();
40113
+ }
40114
+ itemTools = $("<div>").css({position: "absolute",bottom:"6px",right:"8px"});
40115
+ var menuButton = $('<button class="red-ui-button red-ui-button-small" type="button"><i class="fa fa-ellipsis-h"></i></button>')
40116
+ .on("click", function(evt) {
40117
+ evt.preventDefault();
40118
+ evt.stopPropagation();
40119
+ var elementPos = menuButton.offset();
40120
+
40121
+ var menuOptionMenu
40122
+ = RED.menu.init({id:"red-ui-library-browser-menu",
40123
+ options: [
40124
+ {id:"red-ui-library-browser-menu-addFolder",label:RED._("library.newFolder"), onselect: function() {
40125
+ var defaultFolderName = "new-folder";
40126
+ var defaultFolderNameMatches = {};
40127
+
40128
+ var selected = dirList.treeList('selected');
40129
+ if (!selected.children) {
40130
+ selected = selected.parent;
40131
+ }
40132
+ var complete = function() {
40133
+ selected.children.forEach(function(c) {
40134
+ if (/^new-folder/.test(c.label)) {
40135
+ defaultFolderNameMatches[c.label] = true
40136
+ }
40137
+ });
40138
+ var folderIndex = 2;
40139
+ while(defaultFolderNameMatches[defaultFolderName]) {
40140
+ defaultFolderName = "new-folder-"+(folderIndex++)
40141
+ }
40142
+
40143
+ selected.treeList.expand();
40144
+ var input = $('<input type="text" class="red-ui-treeList-input">').val(defaultFolderName);
40145
+ var newItem = {
40146
+ icon: "fa fa-folder-o",
40147
+ children:[],
40148
+ path: selected.path,
40149
+ element: input
40150
+ }
40151
+ var confirmAdd = function() {
40152
+ var val = input.val().trim();
40153
+ if (val === "") {
40154
+ cancelAdd();
40155
+ return;
40156
+ } else {
40157
+ for (var i=0;i<selected.children.length;i++) {
40158
+ if (selected.children[i].label === val) {
40159
+ cancelAdd();
40160
+ return;
40161
+ }
40162
+ }
40163
+ }
40164
+ newItem.treeList.remove();
40165
+ var finalItem = {
40166
+ library: selected.library,
40167
+ type: selected.type,
40168
+ icon: "fa fa-folder",
40169
+ children:[],
40170
+ label: val,
40171
+ path: newItem.path+val+"/"
40172
+ }
40173
+ selected.treeList.addChild(finalItem,true);
40174
+ }
40175
+ var cancelAdd = function() {
40176
+ newItem.treeList.remove();
40177
+ }
40178
+ input.on('keydown', function(evt) {
40179
+ evt.stopPropagation();
40180
+ if (evt.keyCode === 13) {
40181
+ confirmAdd();
40182
+ } else if (evt.keyCode === 27) {
40183
+ cancelAdd();
40184
+ }
40185
+ })
40186
+ input.on("blur", function() {
40187
+ confirmAdd();
40188
+ })
40189
+ selected.treeList.addChild(newItem);
40190
+ setTimeout(function() {
40191
+ input.trigger("focus");
40192
+ input.select();
40193
+ },400);
40194
+ }
40195
+ selected.treeList.expand(complete);
40196
+
40197
+ } },
40198
+ // null,
40199
+ // {id:"red-ui-library-browser-menu-rename",label:"Rename", onselect: function() {} },
40200
+ // {id:"red-ui-library-browser-menu-delete",label:"Delete", onselect: function() {} }
40201
+ ]
40202
+ }).on('mouseleave', function(){ $(this).remove(); dirList.focus() })
40203
+ .on('mouseup', function() { var self = $(this);self.hide(); dirList.focus(); setTimeout(function() { self.remove() },100)})
40204
+ .appendTo("body");
40205
+ menuOptionMenu.css({
40206
+ position: "absolute",
40207
+ top: elementPos.top+"px",
40208
+ left: (elementPos.left - menuOptionMenu.width() + 20)+"px"
40209
+ }).show();
40210
+
40211
+ }).appendTo(itemTools);
40212
+
40109
40213
  itemTools.appendTo(item.treeList.label);
40110
40214
  }
40111
40215
  });
@@ -40980,7 +41084,7 @@ RED.search = (function() {
40980
41084
  var key = keys[i];
40981
41085
  var kpos = keys[i].indexOf(val);
40982
41086
  if (kpos > -1) {
40983
- var ids = Object.keys(index[key]);
41087
+ var ids = Object.keys(index[key]||{});
40984
41088
  for (j=0;j<ids.length;j++) {
40985
41089
  var node = index[key][ids[j]];
40986
41090
  var isConfigNode = node.node._def.category === "config" && node.node.type !== 'group';
@@ -41086,7 +41190,7 @@ RED.search = (function() {
41086
41190
  }
41087
41191
  currentResults = search(value);
41088
41192
  if (currentResults.length > 0) {
41089
- for (i=0;i<Math.min(currentResults.length,25);i++) {
41193
+ for (let i=0;i<Math.min(currentResults.length,25);i++) {
41090
41194
  searchResults.editableList('addItem',currentResults[i])
41091
41195
  }
41092
41196
  if (currentResults.length > 25) {
@@ -42997,6 +43101,17 @@ RED.subflow = (function() {
42997
43101
  default: inputType
42998
43102
  })
42999
43103
  input.typedInput('value',val.value)
43104
+ if (inputType === 'cred') {
43105
+ if (node.credentials) {
43106
+ if (node.credentials[tenv.name]) {
43107
+ input.typedInput('value', node.credentials[tenv.name]);
43108
+ } else if (node.credentials['has_'+tenv.name]) {
43109
+ input.typedInput('value', "__PWRD__")
43110
+ } else {
43111
+ input.typedInput('value', "");
43112
+ }
43113
+ }
43114
+ }
43000
43115
  } else {
43001
43116
  input.val(val.value)
43002
43117
  }
@@ -43653,9 +43768,6 @@ RED.group = (function() {
43653
43768
  groups: [ ],
43654
43769
  dirty: RED.nodes.dirty()
43655
43770
  }
43656
- RED.history.push(historyEvent);
43657
-
43658
-
43659
43771
  groups.forEach(function(g) {
43660
43772
  newSelection = newSelection.concat(ungroup(g))
43661
43773
  historyEvent.groups.push(g);