@node-red/editor-client 3.0.0-beta.2 → 3.0.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
@@ -388,6 +388,10 @@ var RED = (function() {
388
388
  // handled below
389
389
  return;
390
390
  }
391
+ if (notificationId === "flows-run-state") {
392
+ // handled in editor-client/src/js/runtime.js
393
+ return;
394
+ }
391
395
  if (notificationId === "project-update") {
392
396
  loader.start(RED._("event.loadingProject"), 0);
393
397
  RED.nodes.clear();
@@ -423,7 +427,6 @@ var RED = (function() {
423
427
  id: notificationId
424
428
  }
425
429
  if (notificationId === "runtime-state") {
426
- RED.events.emit("runtime-state",msg);
427
430
  if (msg.error === "safe-mode") {
428
431
  options.buttons = [
429
432
  {
@@ -564,9 +567,9 @@ var RED = (function() {
564
567
  } else if (persistentNotifications.hasOwnProperty(notificationId)) {
565
568
  persistentNotifications[notificationId].close();
566
569
  delete persistentNotifications[notificationId];
567
- if (notificationId === 'runtime-state') {
568
- RED.events.emit("runtime-state",msg);
569
- }
570
+ }
571
+ if (notificationId === 'runtime-state') {
572
+ RED.events.emit("runtime-state",msg);
570
573
  }
571
574
  });
572
575
  RED.comms.subscribe("status/#",function(topic,msg) {
@@ -838,6 +841,7 @@ var RED = (function() {
838
841
  RED.keyboard.init(buildMainMenu);
839
842
 
840
843
  RED.nodes.init();
844
+ RED.runtime.init()
841
845
  RED.comms.connect();
842
846
 
843
847
  $("#red-ui-main-container").show();
@@ -2080,6 +2084,42 @@ RED.comms = (function() {
2080
2084
  unsubscribe:unsubscribe
2081
2085
  }
2082
2086
  })();
2087
+ ;RED.runtime = (function() {
2088
+ let state = ""
2089
+ let settings = { ui: false, enabled: false };
2090
+ const STOPPED = "stop"
2091
+ const STARTED = "start"
2092
+ const SAFE = "safe"
2093
+
2094
+ return {
2095
+ init: function() {
2096
+ // refresh the current runtime status from server
2097
+ settings = Object.assign({}, settings, RED.settings.runtimeState);
2098
+ RED.events.on("runtime-state", function(msg) {
2099
+ if (msg.state) {
2100
+ const currentState = state
2101
+ state = msg.state
2102
+ $(".red-ui-flow-node-button").toggleClass("red-ui-flow-node-button-stopped", state !== STARTED)
2103
+ if(settings.enabled === true && settings.ui === true) {
2104
+ RED.menu.setVisible("deploymenu-item-runtime-stop", state === STARTED)
2105
+ RED.menu.setVisible("deploymenu-item-runtime-start", state !== STARTED)
2106
+ }
2107
+ // Do not notify the user about this event if:
2108
+ // - This is the very first event we've received after loading the editor (currentState = '')
2109
+ // - The state matches what we already thought was the case (state === currentState)
2110
+ // - The event was triggered by a deploy (msg.deploy === true)
2111
+ // - The event is a safe mode event - that gets notified separately
2112
+ if (currentState !== '' && state !== currentState && !msg.deploy && state !== SAFE) {
2113
+ RED.notify(RED._("notification.state.flows"+(state === STOPPED?'Stopped':'Started'), msg), "success")
2114
+ }
2115
+ }
2116
+ });
2117
+ },
2118
+ get started() {
2119
+ return state === STARTED
2120
+ }
2121
+ }
2122
+ })()
2083
2123
  ;/**
2084
2124
  * Copyright JS Foundation and other contributors, http://js.foundation
2085
2125
  *
@@ -3636,7 +3676,7 @@ RED.state = {
3636
3676
  * limitations under the License.
3637
3677
  **/
3638
3678
 
3639
- /**
3679
+ /**
3640
3680
  * An Interface to nodes and utility functions for creating/adding/deleting nodes and links
3641
3681
  * @namespace RED.nodes
3642
3682
  */
@@ -4360,6 +4400,10 @@ RED.nodes = (function() {
4360
4400
  moveGroupToTab(node,z);
4361
4401
  return;
4362
4402
  }
4403
+ if (node.type === "junction") {
4404
+ moveJunctionToTab(node,z);
4405
+ return;
4406
+ }
4363
4407
  var oldZ = node.z;
4364
4408
  allNodes.moveNode(node,z);
4365
4409
  var nl = nodeLinks[node.id];
@@ -4394,6 +4438,39 @@ RED.nodes = (function() {
4394
4438
  RED.events.emit("groups:change",group);
4395
4439
  }
4396
4440
 
4441
+ function moveJunctionToTab(junction, z) {
4442
+ var index = junctionsByZ[junction.z].indexOf(junction);
4443
+ junctionsByZ[junction.z].splice(index,1);
4444
+ junctionsByZ[z] = junctionsByZ[z] || [];
4445
+ junctionsByZ[z].push(junction);
4446
+
4447
+ var oldZ = junction.z;
4448
+ junction.z = z;
4449
+
4450
+ var nl = nodeLinks[junction.id];
4451
+ if (nl) {
4452
+ nl.in.forEach(function(l) {
4453
+ var idx = linkTabMap[oldZ].indexOf(l);
4454
+ if (idx != -1) {
4455
+ linkTabMap[oldZ].splice(idx, 1);
4456
+ }
4457
+ if ((l.source.z === z) && linkTabMap[z]) {
4458
+ linkTabMap[z].push(l);
4459
+ }
4460
+ });
4461
+ nl.out.forEach(function(l) {
4462
+ var idx = linkTabMap[oldZ].indexOf(l);
4463
+ if (idx != -1) {
4464
+ linkTabMap[oldZ].splice(idx, 1);
4465
+ }
4466
+ if ((l.target.z === z) && linkTabMap[z]) {
4467
+ linkTabMap[z].push(l);
4468
+ }
4469
+ });
4470
+ }
4471
+ RED.events.emit("junctions:change",junction);
4472
+ }
4473
+
4397
4474
  function removeLink(l) {
4398
4475
  var index = links.indexOf(l);
4399
4476
  if (index != -1) {
@@ -5239,21 +5316,20 @@ RED.nodes = (function() {
5239
5316
  * Options:
5240
5317
  * - generateIds - whether to replace all node ids
5241
5318
  * - addFlow - whether to import nodes to a new tab
5242
- * - importToCurrent
5319
+ * - reimport - if node has a .z property, dont overwrite it
5320
+ * Only applicible when `generateIds` is false
5243
5321
  * - importMap - how to resolve any conflicts.
5244
5322
  * - id:import - import as-is
5245
5323
  * - id:copy - import with new id
5246
5324
  * - id:replace - import over the top of existing
5247
5325
  */
5248
5326
  function importNodes(newNodesObj,options) { // createNewIds,createMissingWorkspace) {
5249
- options = options || {
5250
- generateIds: false,
5251
- addFlow: false,
5252
- }
5253
- options.importMap = options.importMap || {};
5254
-
5255
- var createNewIds = options.generateIds;
5256
- var createMissingWorkspace = options.addFlow;
5327
+ const defOpts = { generateIds: false, addFlow: false, reimport: false, importMap: {} }
5328
+ options = Object.assign({}, defOpts, options)
5329
+ options.importMap = options.importMap || {}
5330
+ const createNewIds = options.generateIds;
5331
+ const reimport = (!createNewIds && !!options.reimport)
5332
+ const createMissingWorkspace = options.addFlow;
5257
5333
  var i;
5258
5334
  var n;
5259
5335
  var newNodes;
@@ -5554,7 +5630,8 @@ RED.nodes = (function() {
5554
5630
  }
5555
5631
  }
5556
5632
  } else {
5557
- if (n.z && !workspace_map[n.z] && !subflow_map[n.z]) {
5633
+ const keepNodesCurrentZ = reimport && n.z && RED.workspaces.contains(n.z)
5634
+ if (!keepNodesCurrentZ && n.z && !workspace_map[n.z] && !subflow_map[n.z]) {
5558
5635
  n.z = activeWorkspace;
5559
5636
  }
5560
5637
  }
@@ -5655,7 +5732,8 @@ RED.nodes = (function() {
5655
5732
  node.id = getID();
5656
5733
  } else {
5657
5734
  node.id = n.id;
5658
- if (node.z == null || (!workspace_map[node.z] && !subflow_map[node.z])) {
5735
+ const keepNodesCurrentZ = reimport && node.z && RED.workspaces.contains(node.z)
5736
+ if (!keepNodesCurrentZ && (node.z == null || (!workspace_map[node.z] && !subflow_map[node.z]))) {
5659
5737
  if (createMissingWorkspace) {
5660
5738
  if (missingWorkspace === null) {
5661
5739
  missingWorkspace = RED.workspaces.add(null,true);
@@ -6354,7 +6432,7 @@ RED.nodes = (function() {
6354
6432
  // Force the redraw to be synchronous so the view updates
6355
6433
  // *now* and removes the unknown node
6356
6434
  RED.view.redraw(true, true);
6357
- var result = importNodes(reimportList,{generateIds:false});
6435
+ var result = importNodes(reimportList,{generateIds:false, reimport: true});
6358
6436
  var newNodeMap = {};
6359
6437
  result.nodes.forEach(function(n) {
6360
6438
  newNodeMap[n.id] = n;
@@ -7353,6 +7431,14 @@ RED.history = (function() {
7353
7431
  var undoHistory = [];
7354
7432
  var redoHistory = [];
7355
7433
 
7434
+ function nodeOrJunction(id) {
7435
+ var node = RED.nodes.node(id);
7436
+ if (node) {
7437
+ return node;
7438
+ }
7439
+ return RED.nodes.junction(id);
7440
+ }
7441
+
7356
7442
  function undoEvent(ev) {
7357
7443
  var i;
7358
7444
  var len;
@@ -7845,6 +7931,7 @@ RED.history = (function() {
7845
7931
  var z = ev.activeWorkspace;
7846
7932
  var fullNodeList = RED.nodes.filterNodes({z:ev.subflow.subflow.id});
7847
7933
  fullNodeList = fullNodeList.concat(RED.nodes.groups(ev.subflow.subflow.id))
7934
+ fullNodeList = fullNodeList.concat(RED.nodes.junctions(ev.subflow.subflow.id))
7848
7935
  fullNodeList.forEach(function(n) {
7849
7936
  n.x += ev.subflow.offsetX;
7850
7937
  n.y += ev.subflow.offsetY;
@@ -7854,7 +7941,7 @@ RED.history = (function() {
7854
7941
  });
7855
7942
  inverseEv.subflows = [];
7856
7943
  for (i=0;i<ev.nodes.length;i++) {
7857
- inverseEv.subflows.push(RED.nodes.node(ev.nodes[i]));
7944
+ inverseEv.subflows.push(nodeOrJunction(ev.nodes[i]));
7858
7945
  RED.nodes.remove(ev.nodes[i]);
7859
7946
  }
7860
7947
  }
@@ -9198,6 +9285,8 @@ RED.utils = (function() {
9198
9285
  return "font-awesome/fa-circle-o"
9199
9286
  } else if (def.category === 'config') {
9200
9287
  return RED.settings.apiRootUrl+"icons/node-red/cog.svg"
9288
+ } else if ((node && /^_action_:/.test(node.type)) || /^_action_:/.test(def.type)) {
9289
+ return "font-awesome/fa-cogs"
9201
9290
  } else if (node && node.type === 'tab') {
9202
9291
  return "red-ui-icons/red-ui-icons-flow"
9203
9292
  // return RED.settings.apiRootUrl+"images/subflow_tab.svg"
@@ -10032,6 +10121,7 @@ RED.utils = (function() {
10032
10121
  * - multi : boolean - if true, .selected will return an array of results
10033
10122
  * otherwise, returns the first selected item
10034
10123
  * - sortable: boolean/string - TODO: see editableList
10124
+ * - selectable: boolean - default true - whether individual items can be selected
10035
10125
  * - rootSortable: boolean - if 'sortable' is set, then setting this to
10036
10126
  * false, prevents items being sorted to the
10037
10127
  * top level of the tree
@@ -10129,6 +10219,7 @@ RED.utils = (function() {
10129
10219
  switch(evt.keyCode) {
10130
10220
  case 32: // SPACE
10131
10221
  case 13: // ENTER
10222
+ if (!that.options.selectable) { return }
10132
10223
  if (evt.altKey || evt.ctrlKey || evt.metaKey || evt.shiftKey) {
10133
10224
  return
10134
10225
  }
@@ -11116,6 +11207,7 @@ RED.utils = (function() {
11116
11207
  RED.menu = (function() {
11117
11208
 
11118
11209
  var menuItems = {};
11210
+ let menuItemCount = 0
11119
11211
 
11120
11212
  function createMenuItem(opt) {
11121
11213
  var item;
@@ -11159,15 +11251,16 @@ RED.menu = (function() {
11159
11251
  item = $('<li class="red-ui-menu-divider"></li>');
11160
11252
  } else {
11161
11253
  item = $('<li></li>');
11162
-
11254
+ if (!opt.id) {
11255
+ opt.id = 'red-ui-menu-item-'+(menuItemCount++)
11256
+ }
11163
11257
  if (opt.group) {
11164
11258
  item.addClass("red-ui-menu-group-"+opt.group);
11165
-
11166
11259
  }
11167
11260
  var linkContent = '<a '+(opt.id?'id="'+opt.id+'" ':'')+'tabindex="-1" href="#">';
11168
11261
  if (opt.toggle) {
11169
- linkContent += '<i class="fa fa-square pull-left"></i>';
11170
- linkContent += '<i class="fa fa-check-square pull-left"></i>';
11262
+ linkContent += '<i class="fa fa-square'+(opt.direction!=='right'?" pull-left":"")+'"></i>';
11263
+ linkContent += '<i class="fa fa-check-square'+(opt.direction!=='right'?" pull-left":"")+'"></i>';
11171
11264
 
11172
11265
  }
11173
11266
  if (opt.icon !== undefined) {
@@ -11177,12 +11270,15 @@ RED.menu = (function() {
11177
11270
  linkContent += '<i class="'+(opt.icon?opt.icon:'" style="display: inline-block;"')+'"></i> ';
11178
11271
  }
11179
11272
  }
11180
-
11273
+ let label = opt.label
11274
+ if (!opt.label && typeof opt.onselect === 'string') {
11275
+ label = RED.actions.getLabel(opt.onselect)
11276
+ }
11181
11277
  if (opt.sublabel) {
11182
- linkContent += '<span class="red-ui-menu-label-container"><span class="red-ui-menu-label">'+opt.label+'</span>'+
11278
+ linkContent += '<span class="red-ui-menu-label-container"><span class="red-ui-menu-label">'+label+'</span>'+
11183
11279
  '<span class="red-ui-menu-sublabel">'+opt.sublabel+'</span></span>'
11184
11280
  } else {
11185
- linkContent += '<span class="red-ui-menu-label"><span>'+opt.label+'</span></span>'
11281
+ linkContent += '<span class="red-ui-menu-label"><span>'+label+'</span></span>'
11186
11282
  }
11187
11283
 
11188
11284
  linkContent += '</a>';
@@ -11226,19 +11322,45 @@ RED.menu = (function() {
11226
11322
  });
11227
11323
  }
11228
11324
  if (opt.options) {
11229
- item.addClass("red-ui-menu-dropdown-submenu pull-left");
11325
+ item.addClass("red-ui-menu-dropdown-submenu"+(opt.direction!=='right'?" pull-left":""));
11230
11326
  var submenu = $('<ul id="'+opt.id+'-submenu" class="red-ui-menu-dropdown"></ul>').appendTo(item);
11327
+ var hasIcons = false
11328
+ var hasSubmenus = false
11231
11329
 
11232
11330
  for (var i=0;i<opt.options.length;i++) {
11331
+
11332
+ if (opt.options[i]) {
11333
+ if (opt.onpreselect && opt.options[i].onpreselect === undefined) {
11334
+ opt.options[i].onpreselect = opt.onpreselect
11335
+ }
11336
+ if (opt.onpostselect && opt.options[i].onpostselect === undefined) {
11337
+ opt.options[i].onpostselect = opt.onpostselect
11338
+ }
11339
+ opt.options[i].direction = opt.direction
11340
+ hasIcons = hasIcons || (opt.options[i].icon);
11341
+ hasSubmenus = hasSubmenus || (opt.options[i].options);
11342
+ }
11343
+
11233
11344
  var li = createMenuItem(opt.options[i]);
11234
11345
  if (li) {
11235
11346
  li.appendTo(submenu);
11236
11347
  }
11237
11348
  }
11349
+ if (!hasIcons) {
11350
+ submenu.addClass("red-ui-menu-dropdown-noicons")
11351
+ }
11352
+ if (hasSubmenus) {
11353
+ submenu.addClass("red-ui-menu-dropdown-submenus")
11354
+ }
11355
+
11356
+
11238
11357
  }
11239
11358
  if (opt.disabled) {
11240
11359
  item.addClass("disabled");
11241
11360
  }
11361
+ if (opt.visible === false) {
11362
+ item.addClass("hide");
11363
+ }
11242
11364
  }
11243
11365
 
11244
11366
 
@@ -11247,7 +11369,9 @@ RED.menu = (function() {
11247
11369
  }
11248
11370
  function createMenu(options) {
11249
11371
  var topMenu = $("<ul/>",{class:"red-ui-menu red-ui-menu-dropdown pull-right"});
11250
-
11372
+ if (options.direction) {
11373
+ topMenu.addClass("red-ui-menu-dropdown-direction-"+options.direction)
11374
+ }
11251
11375
  if (options.id) {
11252
11376
  topMenu.attr({id:options.id+"-submenu"});
11253
11377
  var menuParent = $("#"+options.id);
@@ -11273,9 +11397,22 @@ RED.menu = (function() {
11273
11397
  }
11274
11398
 
11275
11399
  var lastAddedSeparator = false;
11400
+ var hasSubmenus = false;
11401
+ var hasIcons = false;
11276
11402
  for (var i=0;i<options.options.length;i++) {
11277
11403
  var opt = options.options[i];
11404
+ if (opt) {
11405
+ if (options.onpreselect && opt.onpreselect === undefined) {
11406
+ opt.onpreselect = options.onpreselect
11407
+ }
11408
+ if (options.onpostselect && opt.onpostselect === undefined) {
11409
+ opt.onpostselect = options.onpostselect
11410
+ }
11411
+ opt.direction = options.direction || 'left'
11412
+ }
11278
11413
  if (opt !== null || !lastAddedSeparator) {
11414
+ hasIcons = hasIcons || (opt && opt.icon);
11415
+ hasSubmenus = hasSubmenus || (opt && opt.options);
11279
11416
  var li = createMenuItem(opt);
11280
11417
  if (li) {
11281
11418
  li.appendTo(topMenu);
@@ -11283,13 +11420,21 @@ RED.menu = (function() {
11283
11420
  }
11284
11421
  }
11285
11422
  }
11286
-
11423
+ if (!hasIcons) {
11424
+ topMenu.addClass("red-ui-menu-dropdown-noicons")
11425
+ }
11426
+ if (hasSubmenus) {
11427
+ topMenu.addClass("red-ui-menu-dropdown-submenus")
11428
+ }
11287
11429
  return topMenu;
11288
11430
  }
11289
11431
 
11290
11432
  function triggerAction(id, args) {
11291
11433
  var opt = menuItems[id];
11292
11434
  var callback = opt.onselect;
11435
+ if (opt.onpreselect) {
11436
+ opt.onpreselect.call(opt,args)
11437
+ }
11293
11438
  if (typeof opt.onselect === 'string') {
11294
11439
  callback = RED.actions.get(opt.onselect);
11295
11440
  }
@@ -11298,6 +11443,9 @@ RED.menu = (function() {
11298
11443
  } else {
11299
11444
  console.log("No callback for",id,opt.onselect);
11300
11445
  }
11446
+ if (opt.onpostselect) {
11447
+ opt.onpostselect.call(opt,args)
11448
+ }
11301
11449
  }
11302
11450
 
11303
11451
  function isSelected(id) {
@@ -11349,6 +11497,14 @@ RED.menu = (function() {
11349
11497
  }
11350
11498
  }
11351
11499
 
11500
+ function setVisible(id,state) {
11501
+ if (!state) {
11502
+ $("#"+id).parent().addClass("hide");
11503
+ } else {
11504
+ $("#"+id).parent().removeClass("hide");
11505
+ }
11506
+ }
11507
+
11352
11508
  function addItem(id,opt) {
11353
11509
  var item = createMenuItem(opt);
11354
11510
  if (opt !== null && opt.group) {
@@ -11405,6 +11561,7 @@ RED.menu = (function() {
11405
11561
  isSelected: isSelected,
11406
11562
  toggleSelected: toggleSelected,
11407
11563
  setDisabled: setDisabled,
11564
+ setVisible: setVisible,
11408
11565
  addItem: addItem,
11409
11566
  removeItem: removeItem,
11410
11567
  setAction: setAction,
@@ -12154,10 +12311,13 @@ RED.popover = (function() {
12154
12311
  var target = options.target;
12155
12312
  var align = options.align || "right";
12156
12313
  var offset = options.offset || [0,0];
12314
+ var xPos = options.x;
12315
+ var yPos = options.y;
12316
+ var isAbsolutePosition = (xPos !== undefined && yPos !== undefined)
12157
12317
 
12158
- var pos = target.offset();
12159
- var targetWidth = target.width();
12160
- var targetHeight = target.outerHeight();
12318
+ var pos = isAbsolutePosition?{left:xPos, top: yPos}:target.offset();
12319
+ var targetWidth = isAbsolutePosition?0:target.width();
12320
+ var targetHeight = isAbsolutePosition?0:target.outerHeight();
12161
12321
  var panelHeight = panel.height();
12162
12322
  var panelWidth = panel.width();
12163
12323
 
@@ -13225,7 +13385,7 @@ RED.tabs = (function() {
13225
13385
  }
13226
13386
 
13227
13387
  // link.attr("title",tab.label);
13228
- RED.popover.tooltip(link,function() { return tab.label})
13388
+ RED.popover.tooltip(link,function() { return RED.utils.sanitize(tab.label); });
13229
13389
 
13230
13390
  if (options.onadd) {
13231
13391
  options.onadd(tab);
@@ -15142,6 +15302,34 @@ RED.stack = (function() {
15142
15302
  function getAction(name) {
15143
15303
  return actions[name].handler;
15144
15304
  }
15305
+ function getActionLabel(name) {
15306
+ let def = actions[name]
15307
+ if (!def) {
15308
+ return ''
15309
+ }
15310
+ if (!def.label) {
15311
+ var options = def.options;
15312
+ var key = options ? options.label : undefined;
15313
+ if (!key) {
15314
+ key = "action-list." +name.replace(/^.*:/,"");
15315
+ }
15316
+ var label = RED._(key);
15317
+ if (label === key) {
15318
+ // no translation. convert `name` to description
15319
+ label = name.replace(/(^.+:([a-z]))|(-([a-z]))/g, function() {
15320
+ if (arguments[5] === 0) {
15321
+ return arguments[2].toUpperCase();
15322
+ } else {
15323
+ return " "+arguments[4].toUpperCase();
15324
+ }
15325
+ });
15326
+ }
15327
+ def.label = label;
15328
+ }
15329
+ return def.label
15330
+ }
15331
+
15332
+
15145
15333
  function invokeAction() {
15146
15334
  var args = Array.prototype.slice.call(arguments);
15147
15335
  var name = args.shift();
@@ -15152,7 +15340,7 @@ RED.stack = (function() {
15152
15340
  }
15153
15341
  function listActions() {
15154
15342
  var result = [];
15155
- var missing = [];
15343
+
15156
15344
  Object.keys(actions).forEach(function(action) {
15157
15345
  var def = actions[action];
15158
15346
  var shortcut = RED.keyboard.getShortcut(action);
@@ -15163,28 +15351,8 @@ RED.stack = (function() {
15163
15351
  isUser = !!RED.keyboard.getUserShortcut(action);
15164
15352
  }
15165
15353
  if (!def.label) {
15166
- var name = action;
15167
- var options = def.options;
15168
- var key = options ? options.label : undefined;
15169
- if (!key) {
15170
- key = "action-list." +name.replace(/^.*:/,"");
15171
- }
15172
- var label = RED._(key);
15173
- if (label === key) {
15174
- // no translation. convert `name` to description
15175
- label = name.replace(/(^.+:([a-z]))|(-([a-z]))/g, function() {
15176
- if (arguments[5] === 0) {
15177
- return arguments[2].toUpperCase();
15178
- } else {
15179
- return " "+arguments[4].toUpperCase();
15180
- }
15181
- });
15182
- missing.push(key);
15183
- }
15184
- def.label = label;
15354
+ def.label = getActionLabel(action)
15185
15355
  }
15186
- //console.log("; missing:", missing);
15187
-
15188
15356
  result.push({
15189
15357
  id:action,
15190
15358
  scope:shortcut?shortcut.scope:undefined,
@@ -15200,6 +15368,7 @@ RED.stack = (function() {
15200
15368
  add: addAction,
15201
15369
  remove: removeAction,
15202
15370
  get: getAction,
15371
+ getLabel: getActionLabel,
15203
15372
  invoke: invokeAction,
15204
15373
  list: listActions
15205
15374
  }
@@ -15269,16 +15438,18 @@ RED.deploy = (function() {
15269
15438
  '</a>'+
15270
15439
  '<a id="red-ui-header-button-deploy-options" class="red-ui-deploy-button" href="#"><i class="fa fa-caret-down"></i></a>'+
15271
15440
  '</span></li>').prependTo(".red-ui-header-toolbar");
15272
- RED.menu.init({id:"red-ui-header-button-deploy-options",
15273
- options: [
15274
- {id:"deploymenu-item-full",toggle:"deploy-type",icon:"red/images/deploy-full.svg",label:RED._("deploy.full"),sublabel:RED._("deploy.fullDesc"),selected: true, onselect:function(s) { if(s){changeDeploymentType("full")}}},
15275
- {id:"deploymenu-item-flow",toggle:"deploy-type",icon:"red/images/deploy-flows.svg",label:RED._("deploy.modifiedFlows"),sublabel:RED._("deploy.modifiedFlowsDesc"), onselect:function(s) {if(s){changeDeploymentType("flows")}}},
15276
- {id:"deploymenu-item-node",toggle:"deploy-type",icon:"red/images/deploy-nodes.svg",label:RED._("deploy.modifiedNodes"),sublabel:RED._("deploy.modifiedNodesDesc"),onselect:function(s) { if(s){changeDeploymentType("nodes")}}},
15277
- null,
15278
- {id:"deploymenu-item-reload", icon:"red/images/deploy-reload.svg",label:RED._("deploy.restartFlows"),sublabel:RED._("deploy.restartFlowsDesc"),onselect:"core:restart-flows"},
15279
-
15280
- ]
15281
- });
15441
+ const mainMenuItems = [
15442
+ {id:"deploymenu-item-full",toggle:"deploy-type",icon:"red/images/deploy-full.svg",label:RED._("deploy.full"),sublabel:RED._("deploy.fullDesc"),selected: true, onselect:function(s) { if(s){changeDeploymentType("full")}}},
15443
+ {id:"deploymenu-item-flow",toggle:"deploy-type",icon:"red/images/deploy-flows.svg",label:RED._("deploy.modifiedFlows"),sublabel:RED._("deploy.modifiedFlowsDesc"), onselect:function(s) {if(s){changeDeploymentType("flows")}}},
15444
+ {id:"deploymenu-item-node",toggle:"deploy-type",icon:"red/images/deploy-nodes.svg",label:RED._("deploy.modifiedNodes"),sublabel:RED._("deploy.modifiedNodesDesc"),onselect:function(s) { if(s){changeDeploymentType("nodes")}}},
15445
+ null
15446
+ ]
15447
+ if (RED.settings.runtimeState && RED.settings.runtimeState.ui === true) {
15448
+ mainMenuItems.push({id:"deploymenu-item-runtime-start", icon:"red/images/start.svg",label:RED._("deploy.startFlows"),sublabel:RED._("deploy.startFlowsDesc"),onselect:"core:start-flows", visible:false})
15449
+ mainMenuItems.push({id:"deploymenu-item-runtime-stop", icon:"red/images/stop.svg",label:RED._("deploy.stopFlows"),sublabel:RED._("deploy.stopFlowsDesc"),onselect:"core:stop-flows", visible:false})
15450
+ }
15451
+ mainMenuItems.push({id:"deploymenu-item-reload", icon:"red/images/deploy-reload.svg",label:RED._("deploy.restartFlows"),sublabel:RED._("deploy.restartFlowsDesc"),onselect:"core:restart-flows"})
15452
+ RED.menu.init({id:"red-ui-header-button-deploy-options", options: mainMenuItems });
15282
15453
  } else if (type == "simple") {
15283
15454
  var label = options.label || RED._("deploy.deploy");
15284
15455
  var icon = 'red/images/deploy-full-o.svg';
@@ -15306,6 +15477,10 @@ RED.deploy = (function() {
15306
15477
 
15307
15478
  RED.actions.add("core:deploy-flows",save);
15308
15479
  if (type === "default") {
15480
+ if (RED.settings.runtimeState && RED.settings.runtimeState.ui === true) {
15481
+ RED.actions.add("core:stop-flows",function() { stopStartFlows("stop") });
15482
+ RED.actions.add("core:start-flows",function() { stopStartFlows("start") });
15483
+ }
15309
15484
  RED.actions.add("core:restart-flows",restart);
15310
15485
  RED.actions.add("core:set-deploy-type-to-full",function() { RED.menu.setSelected("deploymenu-item-full",true);});
15311
15486
  RED.actions.add("core:set-deploy-type-to-modified-flows",function() { RED.menu.setSelected("deploymenu-item-flow",true); });
@@ -15476,18 +15651,73 @@ RED.deploy = (function() {
15476
15651
  function sanitize(html) {
15477
15652
  return html.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")
15478
15653
  }
15479
- function restart() {
15480
- var startTime = Date.now();
15481
- $(".red-ui-deploy-button-content").css('opacity',0);
15482
- $(".red-ui-deploy-button-spinner").show();
15483
- var deployWasEnabled = !$("#red-ui-header-button-deploy").hasClass("disabled");
15484
- $("#red-ui-header-button-deploy").addClass("disabled");
15485
- deployInflight = true;
15654
+
15655
+ function shadeShow() {
15486
15656
  $("#red-ui-header-shade").show();
15487
15657
  $("#red-ui-editor-shade").show();
15488
15658
  $("#red-ui-palette-shade").show();
15489
15659
  $("#red-ui-sidebar-shade").show();
15490
-
15660
+ }
15661
+ function shadeHide() {
15662
+ $("#red-ui-header-shade").hide();
15663
+ $("#red-ui-editor-shade").hide();
15664
+ $("#red-ui-palette-shade").hide();
15665
+ $("#red-ui-sidebar-shade").hide();
15666
+ }
15667
+ function deployButtonSetBusy(){
15668
+ $(".red-ui-deploy-button-content").css('opacity',0);
15669
+ $(".red-ui-deploy-button-spinner").show();
15670
+ $("#red-ui-header-button-deploy").addClass("disabled");
15671
+ }
15672
+ function deployButtonClearBusy(){
15673
+ $(".red-ui-deploy-button-content").css('opacity',1);
15674
+ $(".red-ui-deploy-button-spinner").hide();
15675
+ }
15676
+ function stopStartFlows(state) {
15677
+ const startTime = Date.now()
15678
+ const deployWasEnabled = !$("#red-ui-header-button-deploy").hasClass("disabled")
15679
+ deployInflight = true
15680
+ deployButtonSetBusy()
15681
+ shadeShow()
15682
+ $.ajax({
15683
+ url:"flows/state",
15684
+ type: "POST",
15685
+ data: {state: state}
15686
+ }).done(function(data,textStatus,xhr) {
15687
+ if (deployWasEnabled) {
15688
+ $("#red-ui-header-button-deploy").removeClass("disabled")
15689
+ }
15690
+ }).fail(function(xhr,textStatus,err) {
15691
+ if (deployWasEnabled) {
15692
+ $("#red-ui-header-button-deploy").removeClass("disabled")
15693
+ }
15694
+ if (xhr.status === 401) {
15695
+ RED.notify(RED._("notification.error", { message: RED._("user.notAuthorized") }), "error")
15696
+ } else if (xhr.responseText) {
15697
+ const errorDetail = { message: err ? (err + "") : "" }
15698
+ try {
15699
+ errorDetail.message = JSON.parse(xhr.responseText).message
15700
+ } finally {
15701
+ errorDetail.message = errorDetail.message || xhr.responseText
15702
+ }
15703
+ RED.notify(RED._("notification.error", errorDetail), "error")
15704
+ } else {
15705
+ RED.notify(RED._("notification.error", { message: RED._("deploy.errors.noResponse") }), "error")
15706
+ }
15707
+ }).always(function() {
15708
+ const delta = Math.max(0, 300 - (Date.now() - startTime))
15709
+ setTimeout(function () {
15710
+ deployButtonClearBusy()
15711
+ shadeHide()
15712
+ deployInflight = false
15713
+ }, delta);
15714
+ });
15715
+ }
15716
+ function restart() {
15717
+ var startTime = Date.now();
15718
+ var deployWasEnabled = !$("#red-ui-header-button-deploy").hasClass("disabled");
15719
+ deployInflight = true;
15720
+ deployButtonSetBusy();
15491
15721
  $.ajax({
15492
15722
  url:"flows",
15493
15723
  type: "POST",
@@ -15513,15 +15743,10 @@ RED.deploy = (function() {
15513
15743
  RED.notify(RED._("deploy.deployFailed",{message:RED._("deploy.errors.noResponse")}),"error");
15514
15744
  }
15515
15745
  }).always(function() {
15516
- deployInflight = false;
15517
15746
  var delta = Math.max(0,300-(Date.now()-startTime));
15518
15747
  setTimeout(function() {
15519
- $(".red-ui-deploy-button-content").css('opacity',1);
15520
- $(".red-ui-deploy-button-spinner").hide();
15521
- $("#red-ui-header-shade").hide();
15522
- $("#red-ui-editor-shade").hide();
15523
- $("#red-ui-palette-shade").hide();
15524
- $("#red-ui-sidebar-shade").hide();
15748
+ deployButtonClearBusy();
15749
+ deployInflight = false;
15525
15750
  },delta);
15526
15751
  });
15527
15752
  }
@@ -15656,21 +15881,14 @@ RED.deploy = (function() {
15656
15881
  const nns = RED.nodes.createCompleteNodeSet();
15657
15882
  const startTime = Date.now();
15658
15883
 
15659
- $(".red-ui-deploy-button-content").css('opacity', 0);
15660
- $(".red-ui-deploy-button-spinner").show();
15661
- $("#red-ui-header-button-deploy").addClass("disabled");
15662
-
15884
+ deployButtonSetBusy();
15663
15885
  const data = { flows: nns };
15664
-
15665
15886
  if (!force) {
15666
15887
  data.rev = RED.nodes.version();
15667
15888
  }
15668
15889
 
15669
15890
  deployInflight = true;
15670
- $("#red-ui-header-shade").show();
15671
- $("#red-ui-editor-shade").show();
15672
- $("#red-ui-palette-shade").show();
15673
- $("#red-ui-sidebar-shade").show();
15891
+ shadeShow();
15674
15892
  $.ajax({
15675
15893
  url: "flows",
15676
15894
  type: "POST",
@@ -15756,15 +15974,11 @@ RED.deploy = (function() {
15756
15974
  RED.notify(RED._("deploy.deployFailed", { message: RED._("deploy.errors.noResponse") }), "error");
15757
15975
  }
15758
15976
  }).always(function () {
15759
- deployInflight = false;
15760
15977
  const delta = Math.max(0, 300 - (Date.now() - startTime));
15761
15978
  setTimeout(function () {
15762
- $(".red-ui-deploy-button-content").css('opacity', 1);
15763
- $(".red-ui-deploy-button-spinner").hide();
15764
- $("#red-ui-header-shade").hide();
15765
- $("#red-ui-editor-shade").hide();
15766
- $("#red-ui-palette-shade").hide();
15767
- $("#red-ui-sidebar-shade").hide();
15979
+ deployInflight = false;
15980
+ deployButtonClearBusy()
15981
+ shadeHide()
15768
15982
  }, delta);
15769
15983
  });
15770
15984
  }
@@ -18341,13 +18555,18 @@ RED.keyboard = (function() {
18341
18555
  if (partialState) {
18342
18556
  partialState = null;
18343
18557
  return resolveKeyEvent(evt);
18344
- } else if (Object.keys(handler).length > 0) {
18345
- partialState = handler;
18346
- evt.preventDefault();
18347
- return null;
18348
- } else {
18349
- return null;
18350
18558
  }
18559
+ if (Object.keys(handler).length > 0) {
18560
+ // check if there's a potential combined handler initiated by this keyCode
18561
+ for (let h in handler) {
18562
+ if (matchHandlerToEvent(evt,handler[h]) > -1) {
18563
+ partialState = handler;
18564
+ evt.preventDefault();
18565
+ break;
18566
+ }
18567
+ }
18568
+ }
18569
+ return null;
18351
18570
  } else {
18352
18571
  var depth = Infinity;
18353
18572
  var matchedHandler;
@@ -19053,9 +19272,22 @@ RED.workspaces = (function() {
19053
19272
  onselect: "core:show-last-hidden-flow"
19054
19273
  }
19055
19274
  ]
19056
- if (hideStack.length > 0) {
19275
+ let hiddenFlows = new Set()
19276
+ for (let i = 0; i < hideStack.length; i++) {
19277
+ let ids = hideStack[i]
19278
+ if (!Array.isArray(ids)) {
19279
+ ids = [ids]
19280
+ }
19281
+ ids.forEach(id => {
19282
+ if (RED.nodes.workspace(id)) {
19283
+ hiddenFlows.add(id)
19284
+ }
19285
+ })
19286
+ }
19287
+ const flowCount = hiddenFlows.size;
19288
+ if (flowCount > 0) {
19057
19289
  menuItems.unshift({
19058
- label: RED._("workspace.hiddenFlows",{count: hideStack.length}),
19290
+ label: RED._("workspace.hiddenFlows",{count: flowCount}),
19059
19291
  onselect: "core:list-hidden-flows"
19060
19292
  })
19061
19293
  }
@@ -19553,6 +19785,7 @@ RED.view = (function() {
19553
19785
  let flashingNodeId;
19554
19786
 
19555
19787
  var clipboard = "";
19788
+ let clipboardSource
19556
19789
 
19557
19790
  // Note: these are the permitted status colour aliases. The actual RGB values
19558
19791
  // are set in the CSS - flow.scss/colors.scss
@@ -19664,7 +19897,15 @@ RED.view = (function() {
19664
19897
  function init() {
19665
19898
 
19666
19899
  chart = $("#red-ui-workspace-chart");
19667
-
19900
+ chart.on('contextmenu', function(evt) {
19901
+ evt.preventDefault()
19902
+ evt.stopPropagation()
19903
+ RED.contextMenu.show({
19904
+ x:evt.clientX-5,
19905
+ y:evt.clientY-5
19906
+ })
19907
+ return false
19908
+ })
19668
19909
  outer = d3.select("#red-ui-workspace-chart")
19669
19910
  .append("svg:svg")
19670
19911
  .attr("width", space_width)
@@ -19688,6 +19929,7 @@ RED.view = (function() {
19688
19929
  .on("mousedown", canvasMouseDown)
19689
19930
  .on("mouseup", canvasMouseUp)
19690
19931
  .on("mouseenter", function() {
19932
+ d3.select(document).on('mouseup.red-ui-workspace-tracker', null)
19691
19933
  if (lasso) {
19692
19934
  if (d3.event.buttons !== 1) {
19693
19935
  lasso.remove();
@@ -19703,6 +19945,7 @@ RED.view = (function() {
19703
19945
  }
19704
19946
  }
19705
19947
  })
19948
+ .on("mouseleave", canvasMouseLeave)
19706
19949
  .on("touchend", function() {
19707
19950
  d3.event.preventDefault();
19708
19951
  clearTimeout(touchStartTime);
@@ -19843,6 +20086,9 @@ RED.view = (function() {
19843
20086
  drag_lines = [];
19844
20087
 
19845
20088
  RED.events.on("workspace:change",function(event) {
20089
+ // Just in case the mouse left the workspace whilst doing an action,
20090
+ // put us back into default mode so the refresh works
20091
+ mouse_mode = 0
19846
20092
  if (event.old !== 0) {
19847
20093
  workspaceScrollPositions[event.old] = {
19848
20094
  left:chart.scrollLeft(),
@@ -19984,6 +20230,23 @@ RED.view = (function() {
19984
20230
  nn.x = mousePos[0];
19985
20231
  nn.y = mousePos[1];
19986
20232
 
20233
+ var minX = nn.w/2 -5;
20234
+ if (nn.x < minX) {
20235
+ nn.x = minX;
20236
+ }
20237
+ var minY = nn.h/2 -5;
20238
+ if (nn.y < minY) {
20239
+ nn.y = minY;
20240
+ }
20241
+ var maxX = space_width -nn.w/2 +5;
20242
+ if (nn.x > maxX) {
20243
+ nn.x = maxX;
20244
+ }
20245
+ var maxY = space_height -nn.h +5;
20246
+ if (nn.y > maxY) {
20247
+ nn.y = maxY;
20248
+ }
20249
+
19987
20250
  if (snapGrid) {
19988
20251
  var gridOffset = RED.view.tools.calculateGridSnapOffsets(nn);
19989
20252
  nn.x -= gridOffset.x;
@@ -20056,8 +20319,10 @@ RED.view = (function() {
20056
20319
  });
20057
20320
 
20058
20321
  RED.actions.add("core:copy-selection-to-internal-clipboard",copySelection);
20059
- RED.actions.add("core:cut-selection-to-internal-clipboard",function(){copySelection();deleteSelection();});
20060
- RED.actions.add("core:paste-from-internal-clipboard",function(){importNodes(clipboard,{generateIds: true, generateDefaultNames: true});});
20322
+ RED.actions.add("core:cut-selection-to-internal-clipboard",function(){copySelection(true);deleteSelection();});
20323
+ RED.actions.add("core:paste-from-internal-clipboard",function(){
20324
+ importNodes(clipboard,{generateIds: clipboardSource === 'copy', generateDefaultNames: clipboardSource === 'copy'});
20325
+ });
20061
20326
 
20062
20327
  RED.actions.add("core:detach-selected-nodes", function() { detachSelectedNodes() })
20063
20328
 
@@ -20413,9 +20678,10 @@ RED.view = (function() {
20413
20678
  }
20414
20679
 
20415
20680
  function canvasMouseDown() {
20416
- if (RED.view.DEBUG) {
20681
+ if (RED.view.DEBUG) {
20417
20682
  console.warn("canvasMouseDown", { mouse_mode, point: d3.mouse(this), event: d3.event });
20418
20683
  }
20684
+ RED.contextMenu.hide();
20419
20685
  if (mouse_mode === RED.state.SELECTING_NODE) {
20420
20686
  d3.event.stopPropagation();
20421
20687
  return;
@@ -20428,7 +20694,10 @@ RED.view = (function() {
20428
20694
  scroll_position = [chart.scrollLeft(),chart.scrollTop()];
20429
20695
  return;
20430
20696
  }
20431
- if (!mousedown_node && !mousedown_link && !mousedown_group) {
20697
+ if (d3.event.button === 2) {
20698
+ return
20699
+ }
20700
+ if (!mousedown_node && !mousedown_link && !mousedown_group && !d3.event.shiftKey) {
20432
20701
  selectedLinks.clear();
20433
20702
  updateSelection();
20434
20703
  }
@@ -20482,6 +20751,7 @@ RED.view = (function() {
20482
20751
  options = options || {};
20483
20752
  var point = options.position || lastClickPosition;
20484
20753
  var spliceLink = options.splice;
20754
+ var spliceMultipleLinks = options.spliceMultiple
20485
20755
  var targetGroup = options.group;
20486
20756
  var touchTrigger = options.touchTrigger;
20487
20757
 
@@ -20494,6 +20764,10 @@ RED.view = (function() {
20494
20764
  var ox = point[0];
20495
20765
  var oy = point[1];
20496
20766
 
20767
+ const offset = $("#red-ui-workspace-chart").offset()
20768
+ var clientX = ox + offset.left - $("#red-ui-workspace-chart").scrollLeft()
20769
+ var clientY = oy + offset.top - $("#red-ui-workspace-chart").scrollTop()
20770
+
20497
20771
  if (RED.settings.get("editor").view['view-snap-grid']) {
20498
20772
  // eventLayer.append("circle").attr("cx",point[0]).attr("cy",point[1]).attr("r","2").attr('fill','red')
20499
20773
  point[0] = Math.round(point[0] / gridSize) * gridSize;
@@ -20545,8 +20819,12 @@ RED.view = (function() {
20545
20819
  }
20546
20820
  hideDragLines();
20547
20821
  }
20548
- if (spliceLink) {
20549
- filter = {input:true, output:true}
20822
+ if (spliceLink || spliceMultipleLinks) {
20823
+ filter = {
20824
+ input:true,
20825
+ output:true,
20826
+ spliceMultiple: spliceMultipleLinks
20827
+ }
20550
20828
  }
20551
20829
 
20552
20830
  var rebuildQuickAddLink = function() {
@@ -20571,8 +20849,8 @@ RED.view = (function() {
20571
20849
  var lastAddedWidth;
20572
20850
 
20573
20851
  RED.typeSearch.show({
20574
- x:d3.event.clientX-mainPos.left-node_width/2 - (ox-point[0]),
20575
- y:d3.event.clientY-mainPos.top+ node_height/2 + 5 - (oy-point[1]),
20852
+ x:clientX-mainPos.left-node_width/2 - (ox-point[0]),
20853
+ y:clientY-mainPos.top+ node_height/2 + 5 - (oy-point[1]),
20576
20854
  disableFocus: touchTrigger,
20577
20855
  filter: filter,
20578
20856
  move: function(dx,dy) {
@@ -20600,7 +20878,7 @@ RED.view = (function() {
20600
20878
  hideDragLines();
20601
20879
  redraw();
20602
20880
  },
20603
- add: function(type,keepAdding) {
20881
+ add: function(type, keepAdding) {
20604
20882
  if (touchTrigger) {
20605
20883
  keepAdding = false;
20606
20884
  resetMouseVars();
@@ -20608,7 +20886,13 @@ RED.view = (function() {
20608
20886
 
20609
20887
  var nn;
20610
20888
  var historyEvent;
20611
- if (type === 'junction') {
20889
+ if (/^_action_:/.test(type)) {
20890
+ const actionName = type.substring(9)
20891
+ quickAddActive = false;
20892
+ ghostNode.remove();
20893
+ RED.actions.invoke(actionName)
20894
+ return
20895
+ } else if (type === 'junction') {
20612
20896
  nn = {
20613
20897
  _def: {defaults:{}},
20614
20898
  type: 'junction',
@@ -21174,14 +21458,24 @@ RED.view = (function() {
21174
21458
  redraw();
21175
21459
  }
21176
21460
  }
21177
-
21461
+ function canvasMouseLeave() {
21462
+ if (mouse_mode !== 0 && d3.event.buttons !== 0) {
21463
+ d3.select(document).on('mouseup.red-ui-workspace-tracker', function() {
21464
+ d3.select(document).on('mouseup.red-ui-workspace-tracker', null)
21465
+ canvasMouseUp.call(this)
21466
+ })
21467
+ }
21468
+ }
21178
21469
  function canvasMouseUp() {
21179
21470
  lastClickPosition = [d3.event.offsetX/scaleFactor,d3.event.offsetY/scaleFactor];
21180
- if (RED.view.DEBUG) {
21471
+ if (RED.view.DEBUG) {
21181
21472
  console.warn("canvasMouseUp", { mouse_mode, point: d3.mouse(this), event: d3.event });
21182
21473
  }
21183
21474
  var i;
21184
21475
  var historyEvent;
21476
+ if (d3.event.button === 2) {
21477
+ return
21478
+ }
21185
21479
  if (mouse_mode === RED.state.PANNING) {
21186
21480
  resetMouseVars();
21187
21481
  return
@@ -21273,8 +21567,20 @@ RED.view = (function() {
21273
21567
  }
21274
21568
  }
21275
21569
  })
21276
-
21277
-
21570
+ activeLinks.forEach(function(link) {
21571
+ if (!link.selected) {
21572
+ var sourceY = link.source.y
21573
+ var targetY = link.target.y
21574
+ var sourceX = link.source.x+(link.source.w/2) + 10
21575
+ var targetX = link.target.x-(link.target.w/2) - 10
21576
+ if (
21577
+ sourceX > x && sourceX < x2 && sourceY > y && sourceY < y2 &&
21578
+ targetX > x && targetX < x2 && targetY > y && targetY < y2
21579
+ ) {
21580
+ selectedLinks.add(link);
21581
+ }
21582
+ }
21583
+ })
21278
21584
 
21279
21585
  // var selectionChanged = false;
21280
21586
  // do {
@@ -21322,114 +21628,118 @@ RED.view = (function() {
21322
21628
  slicePath = null;
21323
21629
  RED.view.redraw(true);
21324
21630
  } else if (mouse_mode == RED.state.SLICING_JUNCTION) {
21325
- var removedLinks = new Set()
21326
- var addedLinks = []
21327
- var addedJunctions = []
21328
-
21329
- var groupedLinks = {}
21330
- selectedLinks.forEach(function(l) {
21331
- var sourceId = l.source.id+":"+l.sourcePort
21332
- groupedLinks[sourceId] = groupedLinks[sourceId] || []
21333
- groupedLinks[sourceId].push(l)
21334
-
21335
- groupedLinks[l.target.id] = groupedLinks[l.target.id] || []
21336
- groupedLinks[l.target.id].push(l)
21337
- });
21338
- var linkGroups = Object.keys(groupedLinks)
21339
- linkGroups.sort(function(A,B) {
21340
- return groupedLinks[B].length - groupedLinks[A].length
21341
- })
21342
- linkGroups.forEach(function(gid) {
21343
- var links = groupedLinks[gid]
21344
- var junction = {
21345
- _def: {defaults:{}},
21346
- type: 'junction',
21347
- z: RED.workspaces.active(),
21348
- id: RED.nodes.id(),
21349
- x: 0,
21350
- y: 0,
21351
- w: 0, h: 0,
21352
- outputs: 1,
21353
- inputs: 1,
21354
- dirty: true
21355
- }
21356
- links = links.filter(function(l) { return !removedLinks.has(l) })
21357
- if (links.length === 0) {
21358
- return
21359
- }
21360
- links.forEach(function(l) {
21361
- junction.x += l._sliceLocation.x
21362
- junction.y += l._sliceLocation.y
21363
- })
21364
- junction.x = Math.round(junction.x/links.length)
21365
- junction.y = Math.round(junction.y/links.length)
21366
- if (snapGrid) {
21367
- junction.x = (gridSize*Math.round(junction.x/gridSize));
21368
- junction.y = (gridSize*Math.round(junction.y/gridSize));
21369
- }
21370
-
21371
- var nodeGroups = new Set()
21372
-
21373
- RED.nodes.addJunction(junction)
21374
- addedJunctions.push(junction)
21375
- let newLink
21376
- if (gid === links[0].source.id+":"+links[0].sourcePort) {
21377
- newLink = {
21378
- source: links[0].source,
21379
- sourcePort: links[0].sourcePort,
21380
- target: junction
21381
- }
21382
- } else {
21383
- newLink = {
21384
- source: junction,
21385
- sourcePort: 0,
21386
- target: links[0].target
21387
- }
21388
- }
21389
- addedLinks.push(newLink)
21390
- RED.nodes.addLink(newLink)
21391
- links.forEach(function(l) {
21392
- removedLinks.add(l)
21393
- RED.nodes.removeLink(l)
21394
- let newLink
21395
- if (gid === l.target.id) {
21396
- newLink = {
21397
- source: l.source,
21398
- sourcePort: l.sourcePort,
21399
- target: junction
21400
- }
21401
- } else {
21402
- newLink = {
21403
- source: junction,
21404
- sourcePort: 0,
21405
- target: l.target
21406
- }
21407
- }
21408
- addedLinks.push(newLink)
21409
- RED.nodes.addLink(newLink)
21410
- nodeGroups.add(l.source.g || "__NONE__")
21411
- nodeGroups.add(l.target.g || "__NONE__")
21412
- })
21413
- if (nodeGroups.size === 1) {
21414
- var group = nodeGroups.values().next().value
21415
- if (group !== "__NONE__") {
21416
- RED.group.addToGroup(RED.nodes.group(group), junction)
21417
- }
21418
- }
21419
- })
21631
+ RED.actions.invoke("core:split-wires-with-junctions")
21420
21632
  slicePath.remove();
21421
21633
  slicePath = null;
21422
21634
 
21423
- if (addedJunctions.length > 0) {
21424
- RED.history.push({
21425
- t: 'add',
21426
- links: addedLinks,
21427
- junctions: addedJunctions,
21428
- removedLinks: Array.from(removedLinks)
21429
- })
21430
- RED.nodes.dirty(true)
21431
- }
21432
- RED.view.redraw(true);
21635
+ // var removedLinks = new Set()
21636
+ // var addedLinks = []
21637
+ // var addedJunctions = []
21638
+ //
21639
+ // var groupedLinks = {}
21640
+ // selectedLinks.forEach(function(l) {
21641
+ // var sourceId = l.source.id+":"+l.sourcePort
21642
+ // groupedLinks[sourceId] = groupedLinks[sourceId] || []
21643
+ // groupedLinks[sourceId].push(l)
21644
+ //
21645
+ // groupedLinks[l.target.id] = groupedLinks[l.target.id] || []
21646
+ // groupedLinks[l.target.id].push(l)
21647
+ // });
21648
+ // var linkGroups = Object.keys(groupedLinks)
21649
+ // linkGroups.sort(function(A,B) {
21650
+ // return groupedLinks[B].length - groupedLinks[A].length
21651
+ // })
21652
+ // linkGroups.forEach(function(gid) {
21653
+ // var links = groupedLinks[gid]
21654
+ // var junction = {
21655
+ // _def: {defaults:{}},
21656
+ // type: 'junction',
21657
+ // z: RED.workspaces.active(),
21658
+ // id: RED.nodes.id(),
21659
+ // x: 0,
21660
+ // y: 0,
21661
+ // w: 0, h: 0,
21662
+ // outputs: 1,
21663
+ // inputs: 1,
21664
+ // dirty: true
21665
+ // }
21666
+ // links = links.filter(function(l) { return !removedLinks.has(l) })
21667
+ // if (links.length === 0) {
21668
+ // return
21669
+ // }
21670
+ // links.forEach(function(l) {
21671
+ // junction.x += l._sliceLocation.x
21672
+ // junction.y += l._sliceLocation.y
21673
+ // })
21674
+ // junction.x = Math.round(junction.x/links.length)
21675
+ // junction.y = Math.round(junction.y/links.length)
21676
+ // if (snapGrid) {
21677
+ // junction.x = (gridSize*Math.round(junction.x/gridSize));
21678
+ // junction.y = (gridSize*Math.round(junction.y/gridSize));
21679
+ // }
21680
+ //
21681
+ // var nodeGroups = new Set()
21682
+ //
21683
+ // RED.nodes.addJunction(junction)
21684
+ // addedJunctions.push(junction)
21685
+ // let newLink
21686
+ // if (gid === links[0].source.id+":"+links[0].sourcePort) {
21687
+ // newLink = {
21688
+ // source: links[0].source,
21689
+ // sourcePort: links[0].sourcePort,
21690
+ // target: junction
21691
+ // }
21692
+ // } else {
21693
+ // newLink = {
21694
+ // source: junction,
21695
+ // sourcePort: 0,
21696
+ // target: links[0].target
21697
+ // }
21698
+ // }
21699
+ // addedLinks.push(newLink)
21700
+ // RED.nodes.addLink(newLink)
21701
+ // links.forEach(function(l) {
21702
+ // removedLinks.add(l)
21703
+ // RED.nodes.removeLink(l)
21704
+ // let newLink
21705
+ // if (gid === l.target.id) {
21706
+ // newLink = {
21707
+ // source: l.source,
21708
+ // sourcePort: l.sourcePort,
21709
+ // target: junction
21710
+ // }
21711
+ // } else {
21712
+ // newLink = {
21713
+ // source: junction,
21714
+ // sourcePort: 0,
21715
+ // target: l.target
21716
+ // }
21717
+ // }
21718
+ // addedLinks.push(newLink)
21719
+ // RED.nodes.addLink(newLink)
21720
+ // nodeGroups.add(l.source.g || "__NONE__")
21721
+ // nodeGroups.add(l.target.g || "__NONE__")
21722
+ // })
21723
+ // if (nodeGroups.size === 1) {
21724
+ // var group = nodeGroups.values().next().value
21725
+ // if (group !== "__NONE__") {
21726
+ // RED.group.addToGroup(RED.nodes.group(group), junction)
21727
+ // }
21728
+ // }
21729
+ // })
21730
+ // slicePath.remove();
21731
+ // slicePath = null;
21732
+ //
21733
+ // if (addedJunctions.length > 0) {
21734
+ // RED.history.push({
21735
+ // t: 'add',
21736
+ // links: addedLinks,
21737
+ // junctions: addedJunctions,
21738
+ // removedLinks: Array.from(removedLinks)
21739
+ // })
21740
+ // RED.nodes.dirty(true)
21741
+ // }
21742
+ // RED.view.redraw(true);
21433
21743
  }
21434
21744
  if (mouse_mode == RED.state.MOVING_ACTIVE) {
21435
21745
  if (movingSet.length() > 0) {
@@ -21532,6 +21842,9 @@ RED.view = (function() {
21532
21842
  }
21533
21843
  }
21534
21844
  if (mouse_mode == RED.state.IMPORT_DRAGGING) {
21845
+ if (clipboardSource === 'cut') {
21846
+ clipboardSource = 'copy'
21847
+ }
21535
21848
  updateActiveNodes();
21536
21849
  RED.nodes.dirty(true);
21537
21850
  }
@@ -22086,7 +22399,7 @@ RED.view = (function() {
22086
22399
  }
22087
22400
  }
22088
22401
 
22089
- function copySelection() {
22402
+ function copySelection(isCut) {
22090
22403
  if (mouse_mode === RED.state.SELECTING_NODE) {
22091
22404
  return;
22092
22405
  }
@@ -22150,6 +22463,7 @@ RED.view = (function() {
22150
22463
  }
22151
22464
  }
22152
22465
  clipboard = JSON.stringify(nns);
22466
+ clipboardSource = isCut ? 'cut' : 'copy'
22153
22467
  RED.menu.setDisabled("menu-item-edit-paste", false);
22154
22468
  if (nodeCount > 0) {
22155
22469
  RED.notify(RED._("clipboard.nodeCopied",{count:nodeCount}),{id:"clipboard"});
@@ -22290,6 +22604,7 @@ RED.view = (function() {
22290
22604
 
22291
22605
  function portMouseDown(d,portType,portIndex, evt) {
22292
22606
  if (RED.view.DEBUG) { console.warn("portMouseDown", mouse_mode,d,portType,portIndex); }
22607
+ RED.contextMenu.hide();
22293
22608
  evt = evt || d3.event;
22294
22609
  if (evt === 1) {
22295
22610
  return;
@@ -22668,11 +22983,17 @@ RED.view = (function() {
22668
22983
 
22669
22984
  if (active && ((portType === PORT_TYPE_INPUT && ((d._def && d._def.inputLabels)||d.inputLabels)) || (portType === PORT_TYPE_OUTPUT && ((d._def && d._def.outputLabels)||d.outputLabels)))) {
22670
22985
  portLabelHoverTimeout = setTimeout(function() {
22986
+ const n = port && port.node()
22987
+ const nId = n && n.__data__ && n.__data__.id
22988
+ //check see if node has been deleted since timeout started
22989
+ if(!n || !n.parentNode || !RED.nodes.node(n.__data__.id)) {
22990
+ return; //node is gone!
22991
+ }
22671
22992
  var tooltip = getPortLabel(d,portType,portIndex);
22672
22993
  if (!tooltip) {
22673
22994
  return;
22674
22995
  }
22675
- var pos = getElementPosition(port.node());
22996
+ var pos = getElementPosition(n);
22676
22997
  portLabelHoverTimeout = null;
22677
22998
  portLabelHover = showTooltip(
22678
22999
  (pos[0]+(portType===PORT_TYPE_INPUT?-2:12)),
@@ -22798,6 +23119,7 @@ RED.view = (function() {
22798
23119
  function nodeMouseDown(d) {
22799
23120
  if (RED.view.DEBUG) { console.warn("nodeMouseDown", mouse_mode,d); }
22800
23121
  focusView();
23122
+ RED.contextMenu.hide();
22801
23123
  if (d3.event.button === 1) {
22802
23124
  return;
22803
23125
  }
@@ -22858,6 +23180,9 @@ RED.view = (function() {
22858
23180
  updateSelection();
22859
23181
  RED.nodes.dirty(true);
22860
23182
  redraw();
23183
+ if (clipboardSource === 'cut') {
23184
+ clipboardSource = 'copy'
23185
+ }
22861
23186
  resetMouseVars();
22862
23187
  d3.event.stopPropagation();
22863
23188
  return;
@@ -23105,6 +23430,10 @@ RED.view = (function() {
23105
23430
  if (d.hasOwnProperty('l')?!d.l : (d.type === "link in" || d.type === "link out")) {
23106
23431
  var parentNode = this.parentNode;
23107
23432
  portLabelHoverTimeout = setTimeout(function() {
23433
+ //check see if node has been deleted since timeout started
23434
+ if(!parentNode || !parentNode.parentNode || !RED.nodes.node(parentNode.id)) {
23435
+ return; //node is gone!
23436
+ }
23108
23437
  var tooltip;
23109
23438
  if (d._def.label) {
23110
23439
  tooltip = d._def.label;
@@ -23177,9 +23506,10 @@ RED.view = (function() {
23177
23506
  function junctionMouseOutProxy(e) { junctionMouseOut(d3.select(this), this.__data__) }
23178
23507
 
23179
23508
  function linkMouseDown(d) {
23180
- if (RED.view.DEBUG) {
23509
+ if (RED.view.DEBUG) {
23181
23510
  console.warn("linkMouseDown", { mouse_mode, point: d3.mouse(this), event: d3.event });
23182
23511
  }
23512
+ RED.contextMenu.hide();
23183
23513
  if (mouse_mode === RED.state.SELECTING_NODE) {
23184
23514
  d3.event.stopPropagation();
23185
23515
  return;
@@ -23239,6 +23569,9 @@ RED.view = (function() {
23239
23569
  }
23240
23570
 
23241
23571
  function groupMouseUp(g) {
23572
+ if (RED.view.DEBUG) {
23573
+ console.warn("groupMouseUp", { mouse_mode, event: d3.event });
23574
+ }
23242
23575
  if (dblClickPrimed && mousedown_group == g && clickElapsed > 0 && clickElapsed < dblClickInterval) {
23243
23576
  mouse_mode = RED.state.DEFAULT;
23244
23577
  RED.editor.editGroup(g);
@@ -23254,6 +23587,10 @@ RED.view = (function() {
23254
23587
  // return
23255
23588
  // }
23256
23589
 
23590
+ if (RED.view.DEBUG) {
23591
+ console.warn("groupMouseDown", { mouse_mode, point: mouse, event: d3.event });
23592
+ }
23593
+ RED.contextMenu.hide();
23257
23594
  focusView();
23258
23595
  if (d3.event.button === 1) {
23259
23596
  return;
@@ -23459,7 +23796,7 @@ RED.view = (function() {
23459
23796
  var mdn = mousedown_node;
23460
23797
  var options = [];
23461
23798
  options.push({name:"delete",disabled:(movingSet.length()===0 && selectedLinks.length() === 0),onselect:function() {deleteSelection();}});
23462
- options.push({name:"cut",disabled:(movingSet.length()===0),onselect:function() {copySelection();deleteSelection();}});
23799
+ options.push({name:"cut",disabled:(movingSet.length()===0),onselect:function() {copySelection(true);deleteSelection();}});
23463
23800
  options.push({name:"copy",disabled:(movingSet.length()===0),onselect:function() {copySelection();}});
23464
23801
  options.push({name:"paste",disabled:(clipboard.length===0),onselect:function() {importNodes(clipboard, {generateIds: true, touchImport: true});}});
23465
23802
  options.push({name:"edit",disabled:(movingSet.length() != 1),onselect:function() { RED.editor.edit(mdn);}});
@@ -23933,12 +24270,10 @@ RED.view = (function() {
23933
24270
  icon_groupEl.setAttribute("y",0);
23934
24271
  icon_groupEl.style["pointer-events"] = "none";
23935
24272
  node[0][0].__iconGroup__ = icon_groupEl;
23936
- var icon_shade = document.createElementNS("http://www.w3.org/2000/svg","rect");
24273
+ var icon_shade = document.createElementNS("http://www.w3.org/2000/svg","path");
23937
24274
  icon_shade.setAttribute("x",0);
23938
24275
  icon_shade.setAttribute("y",0);
23939
24276
  icon_shade.setAttribute("class","red-ui-flow-node-icon-shade")
23940
- icon_shade.setAttribute("width",30);
23941
- icon_shade.setAttribute("height",Math.min(50,d.h-4));
23942
24277
  icon_groupEl.appendChild(icon_shade);
23943
24278
  node[0][0].__iconShade__ = icon_shade;
23944
24279
 
@@ -24231,9 +24566,20 @@ RED.view = (function() {
24231
24566
  }
24232
24567
 
24233
24568
  icon.attr("y",function(){return (d.h-d3.select(this).attr("height"))/2;});
24234
- this.__iconShade__.setAttribute("height", d.h );
24569
+
24570
+
24571
+ const iconShadeHeight = d.h
24572
+ const iconShadeWidth = 30
24573
+ this.__iconShade__.setAttribute("d", hideLabel ?
24574
+ `M5 0 h${iconShadeWidth-10} a 5 5 0 0 1 5 5 v${iconShadeHeight-10} a 5 5 0 0 1 -5 5 h-${iconShadeWidth-10} a 5 5 0 0 1 -5 -5 v-${iconShadeHeight-10} a 5 5 0 0 1 5 -5` : (
24575
+ "right" === d._def.align ?
24576
+ `M 0 0 h${iconShadeWidth-5} a 5 5 0 0 1 5 5 v${iconShadeHeight-10} a 5 5 0 0 1 -5 5 h-${iconShadeWidth-5} v-${iconShadeHeight}` :
24577
+ `M5 0 h${iconShadeWidth-5} v${iconShadeHeight} h-${iconShadeWidth-5} a 5 5 0 0 1 -5 -5 v-${iconShadeHeight-10} a 5 5 0 0 1 5 -5`
24578
+ )
24579
+ )
24580
+ this.__iconShadeBorder__.style.display = hideLabel?'none':''
24235
24581
  this.__iconShadeBorder__.setAttribute("d",
24236
- "M " + (((!d._def.align && d.inputs !== 0 && d.outputs === 0) || "right" === d._def.align) ? 0 : 30) + " 1 l 0 " + (d.h - 2)
24582
+ "M " + (((!d._def.align && d.inputs !== 0 && d.outputs === 0) || "right" === d._def.align) ? 0.5 : 29.5) + " "+(d.selected?1:0.5)+" l 0 " + (d.h - (d.selected?2:1))
24237
24583
  );
24238
24584
  faIcon.attr("y",(d.h+13)/2);
24239
24585
  }
@@ -24250,6 +24596,9 @@ RED.view = (function() {
24250
24596
  if (d._def.button) {
24251
24597
  var buttonEnabled = isButtonEnabled(d);
24252
24598
  this.__buttonGroup__.classList.toggle("red-ui-flow-node-button-disabled", !buttonEnabled);
24599
+ if (RED.runtime && Object.hasOwn(RED.runtime,'started')) {
24600
+ this.__buttonGroup__.classList.toggle("red-ui-flow-node-button-stopped", !RED.runtime.started);
24601
+ }
24253
24602
 
24254
24603
  var x = d._def.align == "right"?d.w-6:-25;
24255
24604
  if (d._def.button.toggle && !d[d._def.button.toggle]) {
@@ -25177,7 +25526,12 @@ RED.view = (function() {
25177
25526
  node.dirty = true;
25178
25527
  node.dirtyStatus = true;
25179
25528
  node.changed = true;
25180
- RED.events.emit("nodes:change",node);
25529
+ if (node.type === "junction") {
25530
+ RED.events.emit("junctions:change",node);
25531
+ }
25532
+ else {
25533
+ RED.events.emit("nodes:change",node);
25534
+ }
25181
25535
  }
25182
25536
  }
25183
25537
  }
@@ -25240,6 +25594,7 @@ RED.view = (function() {
25240
25594
  * @private
25241
25595
  */
25242
25596
  function createNode(type, x, y, z) {
25597
+ const wasDirty = RED.nodes.dirty()
25243
25598
  var m = /^subflow:(.+)$/.exec(type);
25244
25599
  var activeSubflow = z ? RED.nodes.subflow(z) : null;
25245
25600
  if (activeSubflow && m) {
@@ -25298,7 +25653,7 @@ RED.view = (function() {
25298
25653
  var historyEvent = {
25299
25654
  t: "add",
25300
25655
  nodes: [nn.id],
25301
- dirty: RED.nodes.dirty()
25656
+ dirty: wasDirty
25302
25657
  }
25303
25658
  if (activeSubflow) {
25304
25659
  var subflowRefresh = RED.subflow.refresh(true);
@@ -25600,7 +25955,13 @@ RED.view = (function() {
25600
25955
  showQuickAddDialog:showQuickAddDialog,
25601
25956
  calculateNodeDimensions: calculateNodeDimensions,
25602
25957
  getElementPosition:getElementPosition,
25603
- showTooltip:showTooltip
25958
+ showTooltip:showTooltip,
25959
+ dimensions: function() {
25960
+ return {
25961
+ width: space_width,
25962
+ height: space_height
25963
+ };
25964
+ }
25604
25965
  };
25605
25966
  })();
25606
25967
  ;RED.view.annotations = (function() {
@@ -26019,6 +26380,9 @@ RED.view.tools = (function() {
26019
26380
  $(document).one('keyup',endKeyboardMove);
26020
26381
  endMoveSet = true;
26021
26382
  }
26383
+ var dim = RED.view.dimensions();
26384
+ var space_width = dim.width;
26385
+ var space_height = dim.height;
26022
26386
  var minX = 0;
26023
26387
  var minY = 0;
26024
26388
  var node;
@@ -26034,6 +26398,12 @@ RED.view.tools = (function() {
26034
26398
  node.n.dirty = true;
26035
26399
  node.n.x += dx;
26036
26400
  node.n.y += dy;
26401
+ if ((node.n.x +node.n.w/2) >= space_width) {
26402
+ node.n.x = space_width -node.n.w/2;
26403
+ }
26404
+ if ((node.n.y +node.n.h/2) >= space_height) {
26405
+ node.n.y = space_height -node.n.h/2;
26406
+ }
26037
26407
  node.n.dirty = true;
26038
26408
  if (node.n.type === "group") {
26039
26409
  RED.group.markDirty(node.n);
@@ -26250,17 +26620,17 @@ RED.view.tools = (function() {
26250
26620
 
26251
26621
  }
26252
26622
 
26253
- function addNode() {
26254
- var selection = RED.view.selection();
26255
- if (selection.nodes && selection.nodes.length === 1 && selection.nodes[0].outputs > 0) {
26256
- var selectedNode = selection.nodes[0];
26257
- RED.view.showQuickAddDialog([
26258
- selectedNode.x + selectedNode.w + 50,selectedNode.y
26259
- ])
26260
- } else {
26261
- RED.view.showQuickAddDialog();
26262
- }
26263
- }
26623
+ // function addNode() {
26624
+ // var selection = RED.view.selection();
26625
+ // if (selection.nodes && selection.nodes.length === 1 && selection.nodes[0].outputs > 0) {
26626
+ // var selectedNode = selection.nodes[0];
26627
+ // RED.view.showQuickAddDialog([
26628
+ // selectedNode.x + selectedNode.w + 50,selectedNode.y
26629
+ // ])
26630
+ // } else {
26631
+ // RED.view.showQuickAddDialog();
26632
+ // }
26633
+ // }
26264
26634
 
26265
26635
 
26266
26636
  function gotoNearestNode(direction) {
@@ -26728,7 +27098,10 @@ RED.view.tools = (function() {
26728
27098
  * @param {Object || Object[]} wires The wire(s) to split and replace with link-out, link-in nodes.
26729
27099
  */
26730
27100
  function splitWiresWithLinkNodes(wires) {
26731
- let wiresToSplit = wires || RED.view.selection().links;
27101
+ let wiresToSplit = wires || (RED.view.selection().links && RED.view.selection().links.filter(e => !e.link));
27102
+ if (!wiresToSplit) {
27103
+ return
27104
+ }
26732
27105
  if (!Array.isArray(wiresToSplit)) {
26733
27106
  wiresToSplit = [wiresToSplit];
26734
27107
  }
@@ -26890,13 +27263,14 @@ RED.view.tools = (function() {
26890
27263
  * - it uses `<paletteLabel> <N>` - where N is the next available integer that
26891
27264
  * doesn't clash with any existing nodes of that type
26892
27265
  * @param {Object} node The node to set the name of - if not provided, uses current selection
27266
+ * @param {{ renameBlank: boolean, renameClash: boolean, generateHistory: boolean }} options Possible options are `renameBlank`, `renameClash` and `generateHistory`
26893
27267
  */
26894
27268
  function generateNodeNames(node, options) {
26895
- options = options || {
27269
+ options = Object.assign({
26896
27270
  renameBlank: true,
26897
27271
  renameClash: true,
26898
27272
  generateHistory: true
26899
- }
27273
+ }, options)
26900
27274
  let nodes = node;
26901
27275
  if (node) {
26902
27276
  if (!Array.isArray(node)) {
@@ -26961,6 +27335,138 @@ RED.view.tools = (function() {
26961
27335
  }
26962
27336
  }
26963
27337
 
27338
+ function addJunctionsToWires(wires) {
27339
+ let wiresToSplit = wires || (RED.view.selection().links && RED.view.selection().links.filter(e => !e.link));
27340
+ if (!wiresToSplit) {
27341
+ return
27342
+ }
27343
+ if (!Array.isArray(wiresToSplit)) {
27344
+ wiresToSplit = [wiresToSplit];
27345
+ }
27346
+ if (wiresToSplit.length === 0) {
27347
+ return;
27348
+ }
27349
+
27350
+ var removedLinks = new Set()
27351
+ var addedLinks = []
27352
+ var addedJunctions = []
27353
+
27354
+ var groupedLinks = {}
27355
+ wiresToSplit.forEach(function(l) {
27356
+ var sourceId = l.source.id+":"+l.sourcePort
27357
+ groupedLinks[sourceId] = groupedLinks[sourceId] || []
27358
+ groupedLinks[sourceId].push(l)
27359
+
27360
+ groupedLinks[l.target.id] = groupedLinks[l.target.id] || []
27361
+ groupedLinks[l.target.id].push(l)
27362
+ });
27363
+ var linkGroups = Object.keys(groupedLinks)
27364
+ linkGroups.sort(function(A,B) {
27365
+ return groupedLinks[B].length - groupedLinks[A].length
27366
+ })
27367
+ const wasDirty = RED.nodes.dirty()
27368
+ linkGroups.forEach(function(gid) {
27369
+ var links = groupedLinks[gid]
27370
+ var junction = {
27371
+ _def: {defaults:{}},
27372
+ type: 'junction',
27373
+ z: RED.workspaces.active(),
27374
+ id: RED.nodes.id(),
27375
+ x: 0,
27376
+ y: 0,
27377
+ w: 0, h: 0,
27378
+ outputs: 1,
27379
+ inputs: 1,
27380
+ dirty: true
27381
+ }
27382
+ links = links.filter(function(l) { return !removedLinks.has(l) })
27383
+ if (links.length === 0) {
27384
+ return
27385
+ }
27386
+ let pointCount = 0
27387
+ links.forEach(function(l) {
27388
+ if (l._sliceLocation) {
27389
+ junction.x += l._sliceLocation.x
27390
+ junction.y += l._sliceLocation.y
27391
+ delete l._sliceLocation
27392
+ pointCount++
27393
+ } else {
27394
+ junction.x += l.source.x + l.source.w/2 + l.target.x - l.target.w/2
27395
+ junction.y += l.source.y + l.target.y
27396
+ pointCount += 2
27397
+ }
27398
+ })
27399
+ junction.x = Math.round(junction.x/pointCount)
27400
+ junction.y = Math.round(junction.y/pointCount)
27401
+ if (RED.view.snapGrid) {
27402
+ let gridSize = RED.view.gridSize()
27403
+ junction.x = (gridSize*Math.round(junction.x/gridSize));
27404
+ junction.y = (gridSize*Math.round(junction.y/gridSize));
27405
+ }
27406
+
27407
+ var nodeGroups = new Set()
27408
+
27409
+ RED.nodes.addJunction(junction)
27410
+ addedJunctions.push(junction)
27411
+ let newLink
27412
+ if (gid === links[0].source.id+":"+links[0].sourcePort) {
27413
+ newLink = {
27414
+ source: links[0].source,
27415
+ sourcePort: links[0].sourcePort,
27416
+ target: junction
27417
+ }
27418
+ } else {
27419
+ newLink = {
27420
+ source: junction,
27421
+ sourcePort: 0,
27422
+ target: links[0].target
27423
+ }
27424
+ }
27425
+ addedLinks.push(newLink)
27426
+ RED.nodes.addLink(newLink)
27427
+ links.forEach(function(l) {
27428
+ removedLinks.add(l)
27429
+ RED.nodes.removeLink(l)
27430
+ let newLink
27431
+ if (gid === l.target.id) {
27432
+ newLink = {
27433
+ source: l.source,
27434
+ sourcePort: l.sourcePort,
27435
+ target: junction
27436
+ }
27437
+ } else {
27438
+ newLink = {
27439
+ source: junction,
27440
+ sourcePort: 0,
27441
+ target: l.target
27442
+ }
27443
+ }
27444
+ addedLinks.push(newLink)
27445
+ RED.nodes.addLink(newLink)
27446
+ nodeGroups.add(l.source.g || "__NONE__")
27447
+ nodeGroups.add(l.target.g || "__NONE__")
27448
+ })
27449
+ if (nodeGroups.size === 1) {
27450
+ var group = nodeGroups.values().next().value
27451
+ if (group !== "__NONE__") {
27452
+ RED.group.addToGroup(RED.nodes.group(group), junction)
27453
+ }
27454
+ }
27455
+ })
27456
+ if (addedJunctions.length > 0) {
27457
+ RED.history.push({
27458
+ dirty: wasDirty,
27459
+ t: 'add',
27460
+ links: addedLinks,
27461
+ junctions: addedJunctions,
27462
+ removedLinks: Array.from(removedLinks)
27463
+ })
27464
+ RED.nodes.dirty(true)
27465
+ RED.view.select({nodes: addedJunctions });
27466
+ }
27467
+ RED.view.redraw(true);
27468
+ }
27469
+
26964
27470
  return {
26965
27471
  init: function() {
26966
27472
  RED.actions.add("core:show-selected-node-labels", function() { setSelectedNodeLabelState(true); })
@@ -27023,6 +27529,7 @@ RED.view.tools = (function() {
27023
27529
  RED.actions.add("core:wire-node-to-multiple", function() { wireNodeToMultiple() })
27024
27530
 
27025
27531
  RED.actions.add("core:split-wire-with-link-nodes", function () { splitWiresWithLinkNodes() });
27532
+ RED.actions.add("core:split-wires-with-junctions", function () { addJunctionsToWires() });
27026
27533
 
27027
27534
  RED.actions.add("core:generate-node-names", generateNodeNames )
27028
27535
 
@@ -27540,7 +28047,7 @@ RED.palette = (function() {
27540
28047
  }
27541
28048
 
27542
28049
  function escapeCategory(category) {
27543
- return category.replace(/[ /.]/g,"_");
28050
+ return category.replace(/[\x00-\x2c\x2e-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]/g,"_");
27544
28051
  }
27545
28052
  function addNodeType(nt,def) {
27546
28053
  if (getPaletteNode(nt).length) {
@@ -29318,10 +29825,8 @@ RED.sidebar.help = (function() {
29318
29825
  var helpSection;
29319
29826
  var panels;
29320
29827
  var panelRatio;
29321
- var helpTopics = [];
29322
29828
  var treeList;
29323
29829
  var tocPanel;
29324
- var helpIndex = {};
29325
29830
 
29326
29831
  function resizeStack() {
29327
29832
  var h = $(content).parent().height() - toolbar.outerHeight();
@@ -29395,7 +29900,10 @@ RED.sidebar.help = (function() {
29395
29900
  var pendingContentLoad;
29396
29901
  treeList.on('treelistselect', function(e,item) {
29397
29902
  pendingContentLoad = item;
29398
- if (item.nodeType) {
29903
+ if (item.tour) {
29904
+ RED.tourGuide.run(item.tour);
29905
+ }
29906
+ else if (item.nodeType) {
29399
29907
  showNodeTypeHelp(item.nodeType);
29400
29908
  } else if (item.content) {
29401
29909
  helpSection.empty();
@@ -29487,7 +29995,6 @@ RED.sidebar.help = (function() {
29487
29995
  }
29488
29996
 
29489
29997
  function refreshHelpIndex() {
29490
- helpTopics = [];
29491
29998
  var modules = RED.nodes.registry.getModuleList();
29492
29999
  var moduleNames = Object.keys(modules);
29493
30000
  moduleNames.sort();
@@ -29496,15 +30003,32 @@ RED.sidebar.help = (function() {
29496
30003
  label: RED._("sidebar.help.nodeHelp"),
29497
30004
  children: [],
29498
30005
  expanded: true
29499
- }
30006
+ };
30007
+ var tours = RED.tourGuide.list().map(function (item) {
30008
+ return {
30009
+ icon: "fa fa-play-circle-o",
30010
+ label: item.label,
30011
+ tour: item.path,
30012
+ };
30013
+ });
29500
30014
  var helpData = [
29501
30015
  {
29502
- id: 'changelog',
29503
- label: "Node-RED v"+RED.settings.version,
29504
- content: getChangelog
30016
+ label: "Node-RED",
30017
+ children: [
30018
+ {
30019
+ id: 'changelog',
30020
+ label: RED._("sidebar.help.changeLog"),
30021
+ content: getChangelog
30022
+ },
30023
+ {
30024
+ label: RED._("tourGuide.welcomeTours"),
30025
+ children: tours
30026
+ }
30027
+
30028
+ ]
29505
30029
  },
29506
30030
  nodeHelp
29507
- ]
30031
+ ];
29508
30032
  var subflows = RED.nodes.registry.getNodeTypes().filter(function(t) {return /subflow/.test(t)});
29509
30033
  if (subflows.length > 0) {
29510
30034
  nodeHelp.children.push({
@@ -33850,8 +34374,7 @@ RED.editor = (function() {
33850
34374
  if (!node._def.defaults || !node._def.defaults.hasOwnProperty("icon")) {
33851
34375
  var icon = $("#red-ui-editor-node-icon").val()||"";
33852
34376
  if (!this.isDefaultIcon) {
33853
- if ((icon !== node.icon) &&
33854
- (icon !== "")) {
34377
+ if ((node.icon && icon !== node.icon) || (!node.icon && icon !== "")) {
33855
34378
  editState.changes.icon = node.icon;
33856
34379
  node.icon = icon;
33857
34380
  editState.changed = true;
@@ -35157,7 +35680,7 @@ RED.editor = (function() {
35157
35680
 
35158
35681
  const MONACO = "monaco";
35159
35682
  const ACE = "ace";
35160
- const defaultEditor = ACE;
35683
+ const defaultEditor = MONACO;
35161
35684
  const DEFAULT_SETTINGS = { lib: defaultEditor, options: {} };
35162
35685
  var selectedCodeEditor = null;
35163
35686
  var initialised = false;
@@ -35184,12 +35707,12 @@ RED.editor = (function() {
35184
35707
  }
35185
35708
 
35186
35709
  function create(options) {
35187
- //TODO: (quandry - for consideration)
35710
+ //TODO: (quandry - for consideration)
35188
35711
  // Below, I had to create a hidden element if options.id || options.element is not in the DOM
35189
- // I have seen 1 node calling `this.editor = RED.editor.createEditor()` with an
35712
+ // I have seen 1 node calling `this.editor = RED.editor.createEditor()` with an
35190
35713
  // invalid (non existing html element selector) (e.g. node-red-contrib-components does this)
35191
- // This causes monaco to throw an error when attempting to hook up its events to the dom & the rest of the 'oneditperapre'
35192
- // code is thus skipped.
35714
+ // This causes monaco to throw an error when attempting to hook up its events to the dom & the rest of the 'oneditperapre'
35715
+ // code is thus skipped.
35193
35716
  // In ACE mode, creating an ACE editor (with an invalid ID) allows the editor to be created (but obviously there is no UI)
35194
35717
  // Because one (or more) contrib nodes have left this bad code in place, how would we handle this?
35195
35718
  // For compatibility, I have decided to create a hidden element so that at least an editor is created & errors do not occur.
@@ -35215,7 +35738,7 @@ RED.editor = (function() {
35215
35738
  return this.editor.create(options);//fallback to ACE
35216
35739
  }
35217
35740
  }
35218
-
35741
+
35219
35742
  return {
35220
35743
  init: init,
35221
35744
  /**
@@ -35227,7 +35750,7 @@ RED.editor = (function() {
35227
35750
  },
35228
35751
  /**
35229
35752
  * Get user selected code editor
35230
- * @return {string} Returns
35753
+ * @return {string} Returns
35231
35754
  * @memberof RED.editor.codeEditor
35232
35755
  */
35233
35756
  get editor() {
@@ -35240,7 +35763,8 @@ RED.editor = (function() {
35240
35763
  */
35241
35764
  create: create
35242
35765
  }
35243
- })();;RED.editor.colorPicker = RED.colorPicker = (function() {
35766
+ })();
35767
+ ;RED.editor.colorPicker = RED.colorPicker = (function() {
35244
35768
 
35245
35769
  function create(options) {
35246
35770
  var color = options.value;
@@ -35492,8 +36016,12 @@ RED.editor = (function() {
35492
36016
  style: "width:100%",
35493
36017
  class: "node-input-env-value",
35494
36018
  type: "text",
35495
- }).attr("autocomplete","disable").appendTo(envRow)
35496
- valueField.typedInput({default:'str',types:isTemplateNode?DEFAULT_ENV_TYPE_LIST:DEFAULT_ENV_TYPE_LIST_INC_CRED});
36019
+ }).attr("autocomplete","disable").appendTo(envRow);
36020
+ var types = (opt.ui && opt.ui.opts && opt.ui.opts.types);
36021
+ if (!types) {
36022
+ types = isTemplateNode ? DEFAULT_ENV_TYPE_LIST : DEFAULT_ENV_TYPE_LIST_INC_CRED;
36023
+ }
36024
+ valueField.typedInput({default:'str',types:types});
35497
36025
  valueField.typedInput('type', opt.type);
35498
36026
  if (opt.type === "cred") {
35499
36027
  if (opt.value) {
@@ -35545,6 +36073,11 @@ RED.editor = (function() {
35545
36073
  }
35546
36074
  opt.ui.label = opt.ui.label || {};
35547
36075
  opt.ui.type = opt.ui.type || "input";
36076
+ if ((opt.ui.type === "cred") &&
36077
+ opt.ui.opts &&
36078
+ opt.ui.opts.types) {
36079
+ opt.ui.type = "input";
36080
+ }
35548
36081
 
35549
36082
  var uiRow = $('<div/>').appendTo(container).hide();
35550
36083
  // save current info for reverting on cancel
@@ -37174,6 +37707,7 @@ RED.editor = (function() {
37174
37707
  var container = $("#red-ui-editor-type-json-tab-ui-container").css({"height":"100%"});
37175
37708
  var filterDepth = Infinity;
37176
37709
  var list = $('<div class="red-ui-debug-msg-payload red-ui-editor-type-json-editor">').appendTo(container).treeList({
37710
+ selectable: false,
37177
37711
  rootSortable: false,
37178
37712
  sortable: ".red-ui-editor-type-json-editor-item-handle",
37179
37713
  }).on("treelistchangeparent", function(event, evt) {
@@ -40421,6 +40955,7 @@ RED.clipboard = (function() {
40421
40955
  } else if (type === 'flow') {
40422
40956
  var activeWorkspace = RED.workspaces.active();
40423
40957
  nodes = RED.nodes.groups(activeWorkspace);
40958
+ nodes = nodes.concat(RED.nodes.junctions(activeWorkspace));
40424
40959
  nodes = nodes.concat(RED.nodes.filterNodes({z:activeWorkspace}));
40425
40960
  RED.nodes.eachConfig(function(n) {
40426
40961
  if (n.z === RED.workspaces.active() && n._def.hasUsers === false) {
@@ -41390,106 +41925,112 @@ RED.library = (function() {
41390
41925
  options.onconfirm(item);
41391
41926
  }
41392
41927
  });
41393
- var itemTools = $("<div>").css({position: "absolute",bottom:"6px",right:"8px"});
41394
- var menuButton = $('<button class="red-ui-button red-ui-button-small" type="button"><i class="fa fa-ellipsis-h"></i></button>')
41395
- .on("click", function(evt) {
41396
- evt.preventDefault();
41397
- evt.stopPropagation();
41398
- var elementPos = menuButton.offset();
41399
-
41400
- var menuOptionMenu = RED.menu.init({id:"red-ui-library-browser-menu",
41401
- options: [
41402
- {id:"red-ui-library-browser-menu-addFolder",label:RED._("library.newFolder"), onselect: function() {
41403
- var defaultFolderName = "new-folder";
41404
- var defaultFolderNameMatches = {};
41405
-
41406
- var selected = dirList.treeList('selected');
41407
- if (!selected.children) {
41408
- selected = selected.parent;
41409
- }
41410
- var complete = function() {
41411
- selected.children.forEach(function(c) {
41412
- if (/^new-folder/.test(c.label)) {
41413
- defaultFolderNameMatches[c.label] = true
41414
- }
41415
- });
41416
- var folderIndex = 2;
41417
- while(defaultFolderNameMatches[defaultFolderName]) {
41418
- defaultFolderName = "new-folder-"+(folderIndex++)
41419
- }
41420
-
41421
- selected.treeList.expand();
41422
- var input = $('<input type="text" class="red-ui-treeList-input">').val(defaultFolderName);
41423
- var newItem = {
41424
- icon: "fa fa-folder-o",
41425
- children:[],
41426
- path: selected.path,
41427
- element: input
41428
- }
41429
- var confirmAdd = function() {
41430
- var val = input.val().trim();
41431
- if (val === "") {
41432
- cancelAdd();
41433
- return;
41434
- } else {
41435
- for (var i=0;i<selected.children.length;i++) {
41436
- if (selected.children[i].label === val) {
41437
- cancelAdd();
41438
- return;
41439
- }
41440
- }
41441
- }
41442
- newItem.treeList.remove();
41443
- var finalItem = {
41444
- library: selected.library,
41445
- type: selected.type,
41446
- icon: "fa fa-folder",
41447
- children:[],
41448
- label: val,
41449
- path: newItem.path+val+"/"
41450
- }
41451
- selected.treeList.addChild(finalItem,true);
41452
- }
41453
- var cancelAdd = function() {
41454
- newItem.treeList.remove();
41455
- }
41456
- input.on('keydown', function(evt) {
41457
- evt.stopPropagation();
41458
- if (evt.keyCode === 13) {
41459
- confirmAdd();
41460
- } else if (evt.keyCode === 27) {
41461
- cancelAdd();
41462
- }
41463
- })
41464
- input.on("blur", function() {
41465
- confirmAdd();
41466
- })
41467
- selected.treeList.addChild(newItem);
41468
- setTimeout(function() {
41469
- input.trigger("focus");
41470
- input.select();
41471
- },400);
41472
- }
41473
- selected.treeList.expand(complete);
41474
-
41475
- } },
41476
- // null,
41477
- // {id:"red-ui-library-browser-menu-rename",label:"Rename", onselect: function() {} },
41478
- // {id:"red-ui-library-browser-menu-delete",label:"Delete", onselect: function() {} }
41479
- ]
41480
- }).on('mouseleave', function(){ $(this).remove(); dirList.focus() })
41481
- .on('mouseup', function() { var self = $(this);self.hide(); dirList.focus(); setTimeout(function() { self.remove() },100)})
41482
- .appendTo("body");
41483
- menuOptionMenu.css({
41484
- position: "absolute",
41485
- top: elementPos.top+"px",
41486
- left: (elementPos.left - menuOptionMenu.width() + 20)+"px"
41487
- }).show();
41488
-
41489
- }).appendTo(itemTools);
41928
+ var itemTools = null;
41490
41929
  if (options.folderTools) {
41491
41930
  dirList.on('treelistselect', function(event, item) {
41492
41931
  if (item.writable !== false && item.treeList) {
41932
+ if (itemTools) {
41933
+ itemTools.remove();
41934
+ }
41935
+ itemTools = $("<div>").css({position: "absolute",bottom:"6px",right:"8px"});
41936
+ var menuButton = $('<button class="red-ui-button red-ui-button-small" type="button"><i class="fa fa-ellipsis-h"></i></button>')
41937
+ .on("click", function(evt) {
41938
+ evt.preventDefault();
41939
+ evt.stopPropagation();
41940
+ var elementPos = menuButton.offset();
41941
+
41942
+ var menuOptionMenu
41943
+ = RED.menu.init({id:"red-ui-library-browser-menu",
41944
+ options: [
41945
+ {id:"red-ui-library-browser-menu-addFolder",label:RED._("library.newFolder"), onselect: function() {
41946
+ var defaultFolderName = "new-folder";
41947
+ var defaultFolderNameMatches = {};
41948
+
41949
+ var selected = dirList.treeList('selected');
41950
+ if (!selected.children) {
41951
+ selected = selected.parent;
41952
+ }
41953
+ var complete = function() {
41954
+ selected.children.forEach(function(c) {
41955
+ if (/^new-folder/.test(c.label)) {
41956
+ defaultFolderNameMatches[c.label] = true
41957
+ }
41958
+ });
41959
+ var folderIndex = 2;
41960
+ while(defaultFolderNameMatches[defaultFolderName]) {
41961
+ defaultFolderName = "new-folder-"+(folderIndex++)
41962
+ }
41963
+
41964
+ selected.treeList.expand();
41965
+ var input = $('<input type="text" class="red-ui-treeList-input">').val(defaultFolderName);
41966
+ var newItem = {
41967
+ icon: "fa fa-folder-o",
41968
+ children:[],
41969
+ path: selected.path,
41970
+ element: input
41971
+ }
41972
+ var confirmAdd = function() {
41973
+ var val = input.val().trim();
41974
+ if (val === "") {
41975
+ cancelAdd();
41976
+ return;
41977
+ } else {
41978
+ for (var i=0;i<selected.children.length;i++) {
41979
+ if (selected.children[i].label === val) {
41980
+ cancelAdd();
41981
+ return;
41982
+ }
41983
+ }
41984
+ }
41985
+ newItem.treeList.remove();
41986
+ var finalItem = {
41987
+ library: selected.library,
41988
+ type: selected.type,
41989
+ icon: "fa fa-folder",
41990
+ children:[],
41991
+ label: val,
41992
+ path: newItem.path+val+"/"
41993
+ }
41994
+ selected.treeList.addChild(finalItem,true);
41995
+ }
41996
+ var cancelAdd = function() {
41997
+ newItem.treeList.remove();
41998
+ }
41999
+ input.on('keydown', function(evt) {
42000
+ evt.stopPropagation();
42001
+ if (evt.keyCode === 13) {
42002
+ confirmAdd();
42003
+ } else if (evt.keyCode === 27) {
42004
+ cancelAdd();
42005
+ }
42006
+ })
42007
+ input.on("blur", function() {
42008
+ confirmAdd();
42009
+ })
42010
+ selected.treeList.addChild(newItem);
42011
+ setTimeout(function() {
42012
+ input.trigger("focus");
42013
+ input.select();
42014
+ },400);
42015
+ }
42016
+ selected.treeList.expand(complete);
42017
+
42018
+ } },
42019
+ // null,
42020
+ // {id:"red-ui-library-browser-menu-rename",label:"Rename", onselect: function() {} },
42021
+ // {id:"red-ui-library-browser-menu-delete",label:"Delete", onselect: function() {} }
42022
+ ]
42023
+ }).on('mouseleave', function(){ $(this).remove(); dirList.focus() })
42024
+ .on('mouseup', function() { var self = $(this);self.hide(); dirList.focus(); setTimeout(function() { self.remove() },100)})
42025
+ .appendTo("body");
42026
+ menuOptionMenu.css({
42027
+ position: "absolute",
42028
+ top: elementPos.top+"px",
42029
+ left: (elementPos.left - menuOptionMenu.width() + 20)+"px"
42030
+ }).show();
42031
+
42032
+ }).appendTo(itemTools);
42033
+
41493
42034
  itemTools.appendTo(item.treeList.label);
41494
42035
  }
41495
42036
  });
@@ -42887,6 +43428,209 @@ RED.search = (function() {
42887
43428
  };
42888
43429
 
42889
43430
  })();
43431
+ ;RED.contextMenu = (function () {
43432
+
43433
+ let menu;
43434
+ function createMenu() {
43435
+ // menu = RED.popover.menu({
43436
+ // options: [
43437
+ // {
43438
+ // label: 'delete selection',
43439
+ // onselect: function() {
43440
+ // RED.actions.invoke('core:delete-selection')
43441
+ // RED.view.focus()
43442
+ // }
43443
+ // },
43444
+ // { label: 'world' }
43445
+ // ],
43446
+ // width: 200,
43447
+ // })
43448
+ }
43449
+
43450
+ function disposeMenu() {
43451
+ $(document).off("mousedown.red-ui-workspace-context-menu");
43452
+ if (menu) {
43453
+ menu.remove();
43454
+ }
43455
+ menu = null;
43456
+ }
43457
+ function show(options) {
43458
+ if (menu) {
43459
+ menu.remove()
43460
+ }
43461
+
43462
+ const selection = RED.view.selection()
43463
+ const noSelection = !selection || Object.keys(selection).length === 0
43464
+ const hasSelection = (selection.nodes && selection.nodes.length > 0);
43465
+ const hasMultipleSelection = hasSelection && selection.nodes.length > 1;
43466
+ const virtulLinks = (selection.links && selection.links.filter(e => !!e.link)) || [];
43467
+ const wireLinks = (selection.links && selection.links.filter(e => !e.link)) || [];
43468
+ const hasLinks = wireLinks.length > 0;
43469
+ const isSingleLink = !hasSelection && hasLinks && wireLinks.length === 1
43470
+ const isMultipleLinks = !hasSelection && hasLinks && wireLinks.length > 1
43471
+ const canDelete = hasSelection || hasLinks
43472
+ const isGroup = hasSelection && selection.nodes.length === 1 && selection.nodes[0].type === 'group'
43473
+
43474
+ const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g
43475
+ const offset = $("#red-ui-workspace-chart").offset()
43476
+
43477
+ let addX = options.x - offset.left + $("#red-ui-workspace-chart").scrollLeft()
43478
+ let addY = options.y - offset.top + $("#red-ui-workspace-chart").scrollTop()
43479
+
43480
+ if (RED.view.snapGrid) {
43481
+ const gridSize = RED.view.gridSize()
43482
+ addX = gridSize * Math.floor(addX / gridSize)
43483
+ addY = gridSize * Math.floor(addY / gridSize)
43484
+ }
43485
+
43486
+ const menuItems = [
43487
+ { onselect: 'core:show-action-list', onpostselect: function () { } },
43488
+ {
43489
+ label: RED._("contextMenu.insert"),
43490
+ options: [
43491
+ {
43492
+ label: RED._("contextMenu.node"),
43493
+ onselect: function () {
43494
+ RED.view.showQuickAddDialog({
43495
+ position: [addX, addY],
43496
+ touchTrigger: true,
43497
+ splice: isSingleLink ? selection.links[0] : undefined,
43498
+ // spliceMultiple: isMultipleLinks
43499
+ })
43500
+ }
43501
+ },
43502
+ (hasLinks) ? { // has least 1 wire selected
43503
+ label: RED._("contextMenu.junction"),
43504
+ onselect: 'core:split-wires-with-junctions',
43505
+ disabled: !hasLinks
43506
+ } : {
43507
+ label: RED._("contextMenu.junction"),
43508
+ onselect: function () {
43509
+ const nn = {
43510
+ _def: { defaults: {} },
43511
+ type: 'junction',
43512
+ z: RED.workspaces.active(),
43513
+ id: RED.nodes.id(),
43514
+ x: addX,
43515
+ y: addY,
43516
+ w: 0, h: 0,
43517
+ outputs: 1,
43518
+ inputs: 1,
43519
+ dirty: true
43520
+ }
43521
+ const historyEvent = {
43522
+ dirty: RED.nodes.dirty(),
43523
+ t: 'add',
43524
+ junctions: [nn]
43525
+ }
43526
+ RED.nodes.addJunction(nn);
43527
+ RED.history.push(historyEvent);
43528
+ RED.nodes.dirty(true);
43529
+ RED.view.select({nodes: [nn] });
43530
+ RED.view.redraw(true)
43531
+ }
43532
+ },
43533
+ {
43534
+ label: RED._("contextMenu.linkNodes"),
43535
+ onselect: 'core:split-wire-with-link-nodes',
43536
+ disabled: !hasLinks
43537
+ }
43538
+ ]
43539
+
43540
+
43541
+
43542
+ }
43543
+ ]
43544
+
43545
+ menuItems.push(
43546
+ null,
43547
+ { onselect: 'core:undo', disabled: RED.history.list().length === 0 },
43548
+ { onselect: 'core:redo', disabled: RED.history.listRedo().length === 0 },
43549
+ null,
43550
+ { onselect: 'core:cut-selection-to-internal-clipboard', label: RED._("keyboard.cutNode"), disabled: !hasSelection },
43551
+ { onselect: 'core:copy-selection-to-internal-clipboard', label: RED._("keyboard.copyNode"), disabled: !hasSelection },
43552
+ { onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !RED.view.clipboard() },
43553
+ { onselect: 'core:delete-selection', disabled: !canDelete },
43554
+ { onselect: 'core:show-export-dialog', label: RED._("menu.label.export") },
43555
+ { onselect: 'core:select-all-nodes' }
43556
+ )
43557
+
43558
+ if (hasSelection) {
43559
+ menuItems.push(
43560
+ null,
43561
+ isGroup ?
43562
+ { onselect: 'core:ungroup-selection', disabled: !isGroup }
43563
+ : { onselect: 'core:group-selection', disabled: !hasSelection }
43564
+ )
43565
+ if (canRemoveFromGroup) {
43566
+ menuItems.push({ onselect: 'core:remove-selection-from-group', label: RED._("menu.label.groupRemoveSelection") })
43567
+ }
43568
+
43569
+ }
43570
+
43571
+ var direction = "right";
43572
+ var MENU_WIDTH = 500; // can not use menu width here
43573
+ if ((options.x -$(document).scrollLeft()) >
43574
+ ($(window).width() -MENU_WIDTH)) {
43575
+ direction = "left";
43576
+ }
43577
+
43578
+ menu = RED.menu.init({
43579
+ direction: direction,
43580
+ onpreselect: function() {
43581
+ disposeMenu()
43582
+ },
43583
+ onpostselect: function () {
43584
+ RED.view.focus()
43585
+ },
43586
+ options: menuItems
43587
+ });
43588
+
43589
+ menu.attr("id", "red-ui-workspace-context-menu");
43590
+ menu.css({
43591
+ position: "absolute"
43592
+ })
43593
+ menu.appendTo("body");
43594
+
43595
+ // TODO: prevent the menu from overflowing the window.
43596
+
43597
+ var top = options.y
43598
+ var left = options.x
43599
+
43600
+ if (top + menu.height() - $(document).scrollTop() > $(window).height()) {
43601
+ top -= (top + menu.height()) - $(window).height() + 22;
43602
+ }
43603
+ if (left + menu.width() - $(document).scrollLeft() > $(window).width()) {
43604
+ left -= (left + menu.width()) - $(window).width() + 18;
43605
+ }
43606
+ menu.css({
43607
+ top: top + "px",
43608
+ left: left + "px"
43609
+ })
43610
+ $(".red-ui-menu.red-ui-menu-dropdown").hide();
43611
+ $(document).on("mousedown.red-ui-workspace-context-menu", function (evt) {
43612
+ if (menu && menu[0].contains(evt.target)) {
43613
+ return
43614
+ }
43615
+ disposeMenu()
43616
+ });
43617
+ menu.show();
43618
+ // set focus to first item so that pressing escape key closes the menu
43619
+ $("#red-ui-workspace-context-menu :first(ul) > a").trigger("focus")
43620
+
43621
+ }
43622
+ // Allow escape key hook and other editor events to close context menu
43623
+ RED.keyboard.add("red-ui-workspace-context-menu", "escape", function () { RED.contextMenu.hide() })
43624
+ RED.events.on("editor:open", function () { RED.contextMenu.hide() });
43625
+ RED.events.on("search:open", function () { RED.contextMenu.hide() });
43626
+ RED.events.on("type-search:open", function () { RED.contextMenu.hide() });
43627
+ RED.events.on("actionList:open", function () { RED.contextMenu.hide() });
43628
+ RED.events.on("view:selection-changed", function () { RED.contextMenu.hide() });
43629
+ return {
43630
+ show: show,
43631
+ hide: disposeMenu
43632
+ }
43633
+ })()
42890
43634
  ;/**
42891
43635
  * Copyright JS Foundation and other contributors, http://js.foundation
42892
43636
  *
@@ -43225,7 +43969,9 @@ RED.actionList = (function() {
43225
43969
  var index = Math.max(0,selected);
43226
43970
  if (index < children.length) {
43227
43971
  var n = $(children[index]).find(".red-ui-editableList-item-content").data('data');
43228
- typesUsed[n.type] = Date.now();
43972
+ if (!/^_action_:/.test(n.type)) {
43973
+ typesUsed[n.type] = Date.now();
43974
+ }
43229
43975
  if (n.def.outputs === 0) {
43230
43976
  confirm(n);
43231
43977
  } else {
@@ -43294,6 +44040,8 @@ RED.actionList = (function() {
43294
44040
  var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).appendTo(div);
43295
44041
  if (object.type === "junction") {
43296
44042
  nodeDiv.addClass("red-ui-palette-icon-junction");
44043
+ } else if (/^_action_:/.test(object.type)) {
44044
+ nodeDiv.addClass("red-ui-palette-icon-junction")
43297
44045
  } else {
43298
44046
  var colour = RED.utils.getNodeColor(object.type,def);
43299
44047
  nodeDiv.css('backgroundColor',colour);
@@ -43303,11 +44051,14 @@ RED.actionList = (function() {
43303
44051
  var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
43304
44052
  RED.utils.createIconElement(icon_url, iconContainer, false);
43305
44053
 
43306
- if (object.type !== "junction" && def.inputs > 0) {
43307
- $('<div/>',{class:"red-ui-search-result-node-port"}).appendTo(nodeDiv);
43308
- }
43309
- if (object.type !== "junction" && def.outputs > 0) {
43310
- $('<div/>',{class:"red-ui-search-result-node-port red-ui-search-result-node-output"}).appendTo(nodeDiv);
44054
+
44055
+ if (!/^_action_:/.test(object.type) && object.type !== "junction") {
44056
+ if (def.inputs > 0) {
44057
+ $('<div/>',{class:"red-ui-search-result-node-port"}).appendTo(nodeDiv);
44058
+ }
44059
+ if (def.outputs > 0) {
44060
+ $('<div/>',{class:"red-ui-search-result-node-port red-ui-search-result-node-output"}).appendTo(nodeDiv);
44061
+ }
43311
44062
  }
43312
44063
 
43313
44064
  var contentDiv = $('<div>',{class:"red-ui-search-result-description"}).appendTo(div);
@@ -43328,7 +44079,9 @@ RED.actionList = (function() {
43328
44079
  }
43329
44080
  function confirm(def) {
43330
44081
  hide();
43331
- typesUsed[def.type] = Date.now();
44082
+ if (!/^_action_:/.test(def.type)) {
44083
+ typesUsed[def.type] = Date.now();
44084
+ }
43332
44085
  addCallback(def.type);
43333
44086
  }
43334
44087
 
@@ -43437,6 +44190,7 @@ RED.actionList = (function() {
43437
44190
  function applyFilter(filter,type,def) {
43438
44191
  return !filter ||
43439
44192
  (
44193
+ (!filter.spliceMultiple) &&
43440
44194
  (!filter.type || type === filter.type) &&
43441
44195
  (!filter.input || type === 'junction' || def.inputs > 0) &&
43442
44196
  (!filter.output || type === 'junction' || def.outputs > 0)
@@ -43451,6 +44205,13 @@ RED.actionList = (function() {
43451
44205
  'inject','debug','function','change','switch','junction'
43452
44206
  ].filter(function(t) { return applyFilter(opts.filter,t,RED.nodes.getType(t)); });
43453
44207
 
44208
+ // if (opts.filter && opts.filter.input && opts.filter.output && !opts.filter.type) {
44209
+ // if (opts.filter.spliceMultiple) {
44210
+ // common.push('_action_:core:split-wires-with-junctions')
44211
+ // }
44212
+ // common.push('_action_:core:split-wire-with-link-nodes')
44213
+ // }
44214
+
43454
44215
  var recentlyUsed = Object.keys(typesUsed);
43455
44216
  recentlyUsed.sort(function(a,b) {
43456
44217
  return typesUsed[b]-typesUsed[a];
@@ -43475,6 +44236,8 @@ RED.actionList = (function() {
43475
44236
  var itemDef = RED.nodes.getType(common[i]);
43476
44237
  if (common[i] === 'junction') {
43477
44238
  itemDef = { inputs:1, outputs: 1, label: 'junction', type: 'junction'}
44239
+ } else if (/^_action_:/.test(common[i]) ) {
44240
+ itemDef = { inputs:1, outputs: 1, label: common[i], type: common[i]}
43478
44241
  }
43479
44242
  if (itemDef) {
43480
44243
  item = {
@@ -44128,6 +44891,14 @@ RED.subflow = (function() {
44128
44891
  return x;
44129
44892
  }
44130
44893
 
44894
+ function nodeOrJunction(id) {
44895
+ var node = RED.nodes.node(id);
44896
+ if (node) {
44897
+ return node;
44898
+ }
44899
+ return RED.nodes.junction(id);
44900
+ }
44901
+
44131
44902
  function convertToSubflow() {
44132
44903
  var selection = RED.view.selection();
44133
44904
  if (!selection.nodes) {
@@ -44316,14 +45087,15 @@ RED.subflow = (function() {
44316
45087
 
44317
45088
  subflow.in.forEach(function(input) {
44318
45089
  input.wires.forEach(function(wire) {
44319
- var link = {source: input, sourcePort: 0, target: RED.nodes.node(wire.id) }
45090
+ var link = {source: input, sourcePort: 0, target: nodeOrJunction(wire.id) }
44320
45091
  new_links.push(link);
44321
45092
  RED.nodes.addLink(link);
44322
45093
  });
44323
45094
  });
45095
+
44324
45096
  subflow.out.forEach(function(output,i) {
44325
45097
  output.wires.forEach(function(wire) {
44326
- var link = {source: RED.nodes.node(wire.id), sourcePort: wire.port , target: output }
45098
+ var link = {source: nodeOrJunction(wire.id), sourcePort: wire.port , target: output }
44327
45099
  new_links.push(link);
44328
45100
  RED.nodes.addLink(link);
44329
45101
  });
@@ -44339,7 +45111,7 @@ RED.subflow = (function() {
44339
45111
  n.links = n.links.filter(function(id) {
44340
45112
  var isLocalLink = nodes.hasOwnProperty(id);
44341
45113
  if (!isLocalLink) {
44342
- var otherNode = RED.nodes.node(id);
45114
+ var otherNode = nodeOrJunction(id);
44343
45115
  if (otherNode && otherNode.links) {
44344
45116
  var i = otherNode.links.indexOf(n.id);
44345
45117
  if (i > -1) {
@@ -44355,7 +45127,6 @@ RED.subflow = (function() {
44355
45127
  RED.nodes.moveNodeToTab(n, subflow.id);
44356
45128
  }
44357
45129
 
44358
-
44359
45130
  var historyEvent = {
44360
45131
  t:'createSubflow',
44361
45132
  nodes:[subflowInstance.id],
@@ -44393,6 +45164,7 @@ RED.subflow = (function() {
44393
45164
  RED.nodes.dirty(true);
44394
45165
  RED.view.updateActive();
44395
45166
  RED.view.select(null);
45167
+ RED.view.focus();
44396
45168
  }
44397
45169
 
44398
45170
 
@@ -44447,6 +45219,7 @@ RED.subflow = (function() {
44447
45219
 
44448
45220
 
44449
45221
  function buildEnvUIRow(row, tenv, ui, node) {
45222
+ console.log(tenv, ui)
44450
45223
  ui.label = ui.label||{};
44451
45224
  if ((tenv.type === "cred" || (tenv.parent && tenv.parent.type === "cred")) && !ui.type) {
44452
45225
  ui.type = "cred";
@@ -44507,6 +45280,17 @@ RED.subflow = (function() {
44507
45280
  default: inputType
44508
45281
  })
44509
45282
  input.typedInput('value',val.value)
45283
+ if (inputType === 'cred') {
45284
+ if (node.credentials) {
45285
+ if (node.credentials[tenv.name]) {
45286
+ input.typedInput('value', node.credentials[tenv.name]);
45287
+ } else if (node.credentials['has_'+tenv.name]) {
45288
+ input.typedInput('value', "__PWRD__")
45289
+ } else {
45290
+ input.typedInput('value', "");
45291
+ }
45292
+ }
45293
+ }
44510
45294
  } else {
44511
45295
  input.val(val.value)
44512
45296
  }
@@ -45148,6 +45932,7 @@ RED.group = (function() {
45148
45932
  RED.history.push(historyEvent);
45149
45933
  RED.view.select({nodes:[group]});
45150
45934
  RED.nodes.dirty(true);
45935
+ RED.view.focus();
45151
45936
  }
45152
45937
  }
45153
45938
  }
@@ -45170,6 +45955,7 @@ RED.group = (function() {
45170
45955
  RED.history.push(historyEvent);
45171
45956
  RED.view.select({nodes:newSelection})
45172
45957
  RED.nodes.dirty(true);
45958
+ RED.view.focus();
45173
45959
  }
45174
45960
  }
45175
45961
 
@@ -45264,6 +46050,7 @@ RED.group = (function() {
45264
46050
  });
45265
46051
  RED.history.push(historyEvent);
45266
46052
  RED.nodes.dirty(true);
46053
+ RED.view.focus();
45267
46054
  }
45268
46055
  }
45269
46056
 
@@ -45291,6 +46078,7 @@ RED.group = (function() {
45291
46078
  }
45292
46079
  }
45293
46080
  RED.view.select({nodes:selection.nodes})
46081
+ RED.view.focus();
45294
46082
  }
45295
46083
  }
45296
46084
  function createGroup(nodes) {
@@ -52432,9 +53220,30 @@ RED.touch.radialMenu = (function() {
52432
53220
  })
52433
53221
  }
52434
53222
 
53223
+ function listTour() {
53224
+ return [
53225
+ {
53226
+ id: "3_0",
53227
+ label: "3.0",
53228
+ path: "./tours/welcome.js"
53229
+ },
53230
+ {
53231
+ id: "2_2",
53232
+ label: "2.2",
53233
+ path: "./tours/2.2/welcome.js"
53234
+ },
53235
+ {
53236
+ id: "2_1",
53237
+ label: "2.1",
53238
+ path: "./tours/2.1/welcome.js"
53239
+ }
53240
+ ];
53241
+ }
53242
+
52435
53243
  return {
52436
53244
  load: loadTour,
52437
53245
  run: run,
53246
+ list: listTour,
52438
53247
  reset: function() {
52439
53248
  RED.settings.set("editor.tours.welcome",'');
52440
53249
  }