@node-red/editor-client 3.0.0-beta.1 → 3.0.0-beta.4

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,19 @@ 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
+ const createNewIds = options.generateIds;
5330
+ const reimport = (!createNewIds && !!options.reimport)
5331
+ const createMissingWorkspace = options.addFlow;
5257
5332
  var i;
5258
5333
  var n;
5259
5334
  var newNodes;
@@ -5554,7 +5629,8 @@ RED.nodes = (function() {
5554
5629
  }
5555
5630
  }
5556
5631
  } else {
5557
- if (n.z && !workspace_map[n.z] && !subflow_map[n.z]) {
5632
+ const keepNodesCurrentZ = reimport && n.z && RED.workspaces.contains(n.z)
5633
+ if (!keepNodesCurrentZ && n.z && !workspace_map[n.z] && !subflow_map[n.z]) {
5558
5634
  n.z = activeWorkspace;
5559
5635
  }
5560
5636
  }
@@ -5655,7 +5731,8 @@ RED.nodes = (function() {
5655
5731
  node.id = getID();
5656
5732
  } else {
5657
5733
  node.id = n.id;
5658
- if (node.z == null || (!workspace_map[node.z] && !subflow_map[node.z])) {
5734
+ const keepNodesCurrentZ = reimport && node.z && RED.workspaces.contains(node.z)
5735
+ if (!keepNodesCurrentZ && (node.z == null || (!workspace_map[node.z] && !subflow_map[node.z]))) {
5659
5736
  if (createMissingWorkspace) {
5660
5737
  if (missingWorkspace === null) {
5661
5738
  missingWorkspace = RED.workspaces.add(null,true);
@@ -6084,6 +6161,8 @@ RED.nodes = (function() {
6084
6161
  workspacesOrder = [];
6085
6162
  groups = {};
6086
6163
  groupsByZ = {};
6164
+ junctions = {};
6165
+ junctionsByZ = {};
6087
6166
 
6088
6167
  var subflowIds = Object.keys(subflows);
6089
6168
  subflowIds.forEach(function(id) {
@@ -6352,7 +6431,7 @@ RED.nodes = (function() {
6352
6431
  // Force the redraw to be synchronous so the view updates
6353
6432
  // *now* and removes the unknown node
6354
6433
  RED.view.redraw(true, true);
6355
- var result = importNodes(reimportList,{generateIds:false});
6434
+ var result = importNodes(reimportList,{generateIds:false, reimport: true});
6356
6435
  var newNodeMap = {};
6357
6436
  result.nodes.forEach(function(n) {
6358
6437
  newNodeMap[n.id] = n;
@@ -7351,6 +7430,14 @@ RED.history = (function() {
7351
7430
  var undoHistory = [];
7352
7431
  var redoHistory = [];
7353
7432
 
7433
+ function nodeOrJunction(id) {
7434
+ var node = RED.nodes.node(id);
7435
+ if (node) {
7436
+ return node;
7437
+ }
7438
+ return RED.nodes.junction(id);
7439
+ }
7440
+
7354
7441
  function undoEvent(ev) {
7355
7442
  var i;
7356
7443
  var len;
@@ -7843,6 +7930,7 @@ RED.history = (function() {
7843
7930
  var z = ev.activeWorkspace;
7844
7931
  var fullNodeList = RED.nodes.filterNodes({z:ev.subflow.subflow.id});
7845
7932
  fullNodeList = fullNodeList.concat(RED.nodes.groups(ev.subflow.subflow.id))
7933
+ fullNodeList = fullNodeList.concat(RED.nodes.junctions(ev.subflow.subflow.id))
7846
7934
  fullNodeList.forEach(function(n) {
7847
7935
  n.x += ev.subflow.offsetX;
7848
7936
  n.y += ev.subflow.offsetY;
@@ -7852,7 +7940,7 @@ RED.history = (function() {
7852
7940
  });
7853
7941
  inverseEv.subflows = [];
7854
7942
  for (i=0;i<ev.nodes.length;i++) {
7855
- inverseEv.subflows.push(RED.nodes.node(ev.nodes[i]));
7943
+ inverseEv.subflows.push(nodeOrJunction(ev.nodes[i]));
7856
7944
  RED.nodes.remove(ev.nodes[i]);
7857
7945
  }
7858
7946
  }
@@ -9196,6 +9284,8 @@ RED.utils = (function() {
9196
9284
  return "font-awesome/fa-circle-o"
9197
9285
  } else if (def.category === 'config') {
9198
9286
  return RED.settings.apiRootUrl+"icons/node-red/cog.svg"
9287
+ } else if ((node && /^_action_:/.test(node.type)) || /^_action_:/.test(def.type)) {
9288
+ return "font-awesome/fa-cogs"
9199
9289
  } else if (node && node.type === 'tab') {
9200
9290
  return "red-ui-icons/red-ui-icons-flow"
9201
9291
  // return RED.settings.apiRootUrl+"images/subflow_tab.svg"
@@ -10030,6 +10120,7 @@ RED.utils = (function() {
10030
10120
  * - multi : boolean - if true, .selected will return an array of results
10031
10121
  * otherwise, returns the first selected item
10032
10122
  * - sortable: boolean/string - TODO: see editableList
10123
+ * - selectable: boolean - default true - whether individual items can be selected
10033
10124
  * - rootSortable: boolean - if 'sortable' is set, then setting this to
10034
10125
  * false, prevents items being sorted to the
10035
10126
  * top level of the tree
@@ -10127,6 +10218,7 @@ RED.utils = (function() {
10127
10218
  switch(evt.keyCode) {
10128
10219
  case 32: // SPACE
10129
10220
  case 13: // ENTER
10221
+ if (!that.options.selectable) { return }
10130
10222
  if (evt.altKey || evt.ctrlKey || evt.metaKey || evt.shiftKey) {
10131
10223
  return
10132
10224
  }
@@ -11114,6 +11206,7 @@ RED.utils = (function() {
11114
11206
  RED.menu = (function() {
11115
11207
 
11116
11208
  var menuItems = {};
11209
+ let menuItemCount = 0
11117
11210
 
11118
11211
  function createMenuItem(opt) {
11119
11212
  var item;
@@ -11157,15 +11250,16 @@ RED.menu = (function() {
11157
11250
  item = $('<li class="red-ui-menu-divider"></li>');
11158
11251
  } else {
11159
11252
  item = $('<li></li>');
11160
-
11253
+ if (!opt.id) {
11254
+ opt.id = 'red-ui-menu-item-'+(menuItemCount++)
11255
+ }
11161
11256
  if (opt.group) {
11162
11257
  item.addClass("red-ui-menu-group-"+opt.group);
11163
-
11164
11258
  }
11165
11259
  var linkContent = '<a '+(opt.id?'id="'+opt.id+'" ':'')+'tabindex="-1" href="#">';
11166
11260
  if (opt.toggle) {
11167
- linkContent += '<i class="fa fa-square pull-left"></i>';
11168
- linkContent += '<i class="fa fa-check-square pull-left"></i>';
11261
+ linkContent += '<i class="fa fa-square'+(opt.direction!=='right'?" pull-left":"")+'"></i>';
11262
+ linkContent += '<i class="fa fa-check-square'+(opt.direction!=='right'?" pull-left":"")+'"></i>';
11169
11263
 
11170
11264
  }
11171
11265
  if (opt.icon !== undefined) {
@@ -11175,12 +11269,15 @@ RED.menu = (function() {
11175
11269
  linkContent += '<i class="'+(opt.icon?opt.icon:'" style="display: inline-block;"')+'"></i> ';
11176
11270
  }
11177
11271
  }
11178
-
11272
+ let label = opt.label
11273
+ if (!opt.label && typeof opt.onselect === 'string') {
11274
+ label = RED.actions.getLabel(opt.onselect)
11275
+ }
11179
11276
  if (opt.sublabel) {
11180
- linkContent += '<span class="red-ui-menu-label-container"><span class="red-ui-menu-label">'+opt.label+'</span>'+
11277
+ linkContent += '<span class="red-ui-menu-label-container"><span class="red-ui-menu-label">'+label+'</span>'+
11181
11278
  '<span class="red-ui-menu-sublabel">'+opt.sublabel+'</span></span>'
11182
11279
  } else {
11183
- linkContent += '<span class="red-ui-menu-label"><span>'+opt.label+'</span></span>'
11280
+ linkContent += '<span class="red-ui-menu-label"><span>'+label+'</span></span>'
11184
11281
  }
11185
11282
 
11186
11283
  linkContent += '</a>';
@@ -11224,19 +11321,45 @@ RED.menu = (function() {
11224
11321
  });
11225
11322
  }
11226
11323
  if (opt.options) {
11227
- item.addClass("red-ui-menu-dropdown-submenu pull-left");
11324
+ item.addClass("red-ui-menu-dropdown-submenu"+(opt.direction!=='right'?" pull-left":""));
11228
11325
  var submenu = $('<ul id="'+opt.id+'-submenu" class="red-ui-menu-dropdown"></ul>').appendTo(item);
11326
+ var hasIcons = false
11327
+ var hasSubmenus = false
11229
11328
 
11230
11329
  for (var i=0;i<opt.options.length;i++) {
11330
+
11331
+ if (opt.options[i]) {
11332
+ if (opt.onpreselect && opt.options[i].onpreselect === undefined) {
11333
+ opt.options[i].onpreselect = opt.onpreselect
11334
+ }
11335
+ if (opt.onpostselect && opt.options[i].onpostselect === undefined) {
11336
+ opt.options[i].onpostselect = opt.onpostselect
11337
+ }
11338
+ opt.options[i].direction = opt.direction
11339
+ hasIcons = hasIcons || (opt.options[i].icon);
11340
+ hasSubmenus = hasSubmenus || (opt.options[i].options);
11341
+ }
11342
+
11231
11343
  var li = createMenuItem(opt.options[i]);
11232
11344
  if (li) {
11233
11345
  li.appendTo(submenu);
11234
11346
  }
11235
11347
  }
11348
+ if (!hasIcons) {
11349
+ submenu.addClass("red-ui-menu-dropdown-noicons")
11350
+ }
11351
+ if (hasSubmenus) {
11352
+ submenu.addClass("red-ui-menu-dropdown-submenus")
11353
+ }
11354
+
11355
+
11236
11356
  }
11237
11357
  if (opt.disabled) {
11238
11358
  item.addClass("disabled");
11239
11359
  }
11360
+ if (opt.visible === false) {
11361
+ item.addClass("hide");
11362
+ }
11240
11363
  }
11241
11364
 
11242
11365
 
@@ -11245,7 +11368,9 @@ RED.menu = (function() {
11245
11368
  }
11246
11369
  function createMenu(options) {
11247
11370
  var topMenu = $("<ul/>",{class:"red-ui-menu red-ui-menu-dropdown pull-right"});
11248
-
11371
+ if (options.direction) {
11372
+ topMenu.addClass("red-ui-menu-dropdown-direction-"+options.direction)
11373
+ }
11249
11374
  if (options.id) {
11250
11375
  topMenu.attr({id:options.id+"-submenu"});
11251
11376
  var menuParent = $("#"+options.id);
@@ -11271,9 +11396,22 @@ RED.menu = (function() {
11271
11396
  }
11272
11397
 
11273
11398
  var lastAddedSeparator = false;
11399
+ var hasSubmenus = false;
11400
+ var hasIcons = false;
11274
11401
  for (var i=0;i<options.options.length;i++) {
11275
11402
  var opt = options.options[i];
11403
+ if (opt) {
11404
+ if (options.onpreselect && opt.onpreselect === undefined) {
11405
+ opt.onpreselect = options.onpreselect
11406
+ }
11407
+ if (options.onpostselect && opt.onpostselect === undefined) {
11408
+ opt.onpostselect = options.onpostselect
11409
+ }
11410
+ opt.direction = options.direction || 'left'
11411
+ }
11276
11412
  if (opt !== null || !lastAddedSeparator) {
11413
+ hasIcons = hasIcons || (opt && opt.icon);
11414
+ hasSubmenus = hasSubmenus || (opt && opt.options);
11277
11415
  var li = createMenuItem(opt);
11278
11416
  if (li) {
11279
11417
  li.appendTo(topMenu);
@@ -11281,13 +11419,21 @@ RED.menu = (function() {
11281
11419
  }
11282
11420
  }
11283
11421
  }
11284
-
11422
+ if (!hasIcons) {
11423
+ topMenu.addClass("red-ui-menu-dropdown-noicons")
11424
+ }
11425
+ if (hasSubmenus) {
11426
+ topMenu.addClass("red-ui-menu-dropdown-submenus")
11427
+ }
11285
11428
  return topMenu;
11286
11429
  }
11287
11430
 
11288
11431
  function triggerAction(id, args) {
11289
11432
  var opt = menuItems[id];
11290
11433
  var callback = opt.onselect;
11434
+ if (opt.onpreselect) {
11435
+ opt.onpreselect.call(opt,args)
11436
+ }
11291
11437
  if (typeof opt.onselect === 'string') {
11292
11438
  callback = RED.actions.get(opt.onselect);
11293
11439
  }
@@ -11296,6 +11442,9 @@ RED.menu = (function() {
11296
11442
  } else {
11297
11443
  console.log("No callback for",id,opt.onselect);
11298
11444
  }
11445
+ if (opt.onpostselect) {
11446
+ opt.onpostselect.call(opt,args)
11447
+ }
11299
11448
  }
11300
11449
 
11301
11450
  function isSelected(id) {
@@ -11347,6 +11496,14 @@ RED.menu = (function() {
11347
11496
  }
11348
11497
  }
11349
11498
 
11499
+ function setVisible(id,state) {
11500
+ if (!state) {
11501
+ $("#"+id).parent().addClass("hide");
11502
+ } else {
11503
+ $("#"+id).parent().removeClass("hide");
11504
+ }
11505
+ }
11506
+
11350
11507
  function addItem(id,opt) {
11351
11508
  var item = createMenuItem(opt);
11352
11509
  if (opt !== null && opt.group) {
@@ -11403,6 +11560,7 @@ RED.menu = (function() {
11403
11560
  isSelected: isSelected,
11404
11561
  toggleSelected: toggleSelected,
11405
11562
  setDisabled: setDisabled,
11563
+ setVisible: setVisible,
11406
11564
  addItem: addItem,
11407
11565
  removeItem: removeItem,
11408
11566
  setAction: setAction,
@@ -12152,10 +12310,13 @@ RED.popover = (function() {
12152
12310
  var target = options.target;
12153
12311
  var align = options.align || "right";
12154
12312
  var offset = options.offset || [0,0];
12313
+ var xPos = options.x;
12314
+ var yPos = options.y;
12315
+ var isAbsolutePosition = (xPos !== undefined && yPos !== undefined)
12155
12316
 
12156
- var pos = target.offset();
12157
- var targetWidth = target.width();
12158
- var targetHeight = target.outerHeight();
12317
+ var pos = isAbsolutePosition?{left:xPos, top: yPos}:target.offset();
12318
+ var targetWidth = isAbsolutePosition?0:target.width();
12319
+ var targetHeight = isAbsolutePosition?0:target.outerHeight();
12159
12320
  var panelHeight = panel.height();
12160
12321
  var panelWidth = panel.width();
12161
12322
 
@@ -13223,7 +13384,7 @@ RED.tabs = (function() {
13223
13384
  }
13224
13385
 
13225
13386
  // link.attr("title",tab.label);
13226
- RED.popover.tooltip(link,function() { return tab.label})
13387
+ RED.popover.tooltip(link,function() { return RED.utils.sanitize(tab.label); });
13227
13388
 
13228
13389
  if (options.onadd) {
13229
13390
  options.onadd(tab);
@@ -14615,10 +14776,7 @@ RED.stack = (function() {
14615
14776
  $(opt.icon).prependTo(this.selectLabel);
14616
14777
  }
14617
14778
  else if (opt.icon.indexOf("/") !== -1) {
14618
- image = new Image();
14619
- image.name = opt.icon;
14620
- image.src = mapDeprecatedIcon(opt.icon);
14621
- $('<img>',{src:mapDeprecatedIcon(opt.icon),style:"margin-right: 4px;height: 18px;"}).prependTo(this.selectLabel);
14779
+ $('<i>',{class:"red-ui-typedInput-icon", style:"mask-image: url("+opt.icon+"); -webkit-mask-image: url("+opt.icon+"); margin-right: 4px;height: 18px;width:13px"}).prependTo(this.selectLabel);
14622
14780
  }
14623
14781
  else {
14624
14782
  $('<i>',{class:"red-ui-typedInput-icon "+opt.icon,style:"min-width: 13px; margin-right: 4px;"}).prependTo(this.selectLabel);
@@ -15143,6 +15301,34 @@ RED.stack = (function() {
15143
15301
  function getAction(name) {
15144
15302
  return actions[name].handler;
15145
15303
  }
15304
+ function getActionLabel(name) {
15305
+ let def = actions[name]
15306
+ if (!def) {
15307
+ return ''
15308
+ }
15309
+ if (!def.label) {
15310
+ var options = def.options;
15311
+ var key = options ? options.label : undefined;
15312
+ if (!key) {
15313
+ key = "action-list." +name.replace(/^.*:/,"");
15314
+ }
15315
+ var label = RED._(key);
15316
+ if (label === key) {
15317
+ // no translation. convert `name` to description
15318
+ label = name.replace(/(^.+:([a-z]))|(-([a-z]))/g, function() {
15319
+ if (arguments[5] === 0) {
15320
+ return arguments[2].toUpperCase();
15321
+ } else {
15322
+ return " "+arguments[4].toUpperCase();
15323
+ }
15324
+ });
15325
+ }
15326
+ def.label = label;
15327
+ }
15328
+ return def.label
15329
+ }
15330
+
15331
+
15146
15332
  function invokeAction() {
15147
15333
  var args = Array.prototype.slice.call(arguments);
15148
15334
  var name = args.shift();
@@ -15153,7 +15339,7 @@ RED.stack = (function() {
15153
15339
  }
15154
15340
  function listActions() {
15155
15341
  var result = [];
15156
- var missing = [];
15342
+
15157
15343
  Object.keys(actions).forEach(function(action) {
15158
15344
  var def = actions[action];
15159
15345
  var shortcut = RED.keyboard.getShortcut(action);
@@ -15164,28 +15350,8 @@ RED.stack = (function() {
15164
15350
  isUser = !!RED.keyboard.getUserShortcut(action);
15165
15351
  }
15166
15352
  if (!def.label) {
15167
- var name = action;
15168
- var options = def.options;
15169
- var key = options ? options.label : undefined;
15170
- if (!key) {
15171
- key = "action-list." +name.replace(/^.*:/,"");
15172
- }
15173
- var label = RED._(key);
15174
- if (label === key) {
15175
- // no translation. convert `name` to description
15176
- label = name.replace(/(^.+:([a-z]))|(-([a-z]))/g, function() {
15177
- if (arguments[5] === 0) {
15178
- return arguments[2].toUpperCase();
15179
- } else {
15180
- return " "+arguments[4].toUpperCase();
15181
- }
15182
- });
15183
- missing.push(key);
15184
- }
15185
- def.label = label;
15353
+ def.label = getActionLabel(action)
15186
15354
  }
15187
- //console.log("; missing:", missing);
15188
-
15189
15355
  result.push({
15190
15356
  id:action,
15191
15357
  scope:shortcut?shortcut.scope:undefined,
@@ -15201,6 +15367,7 @@ RED.stack = (function() {
15201
15367
  add: addAction,
15202
15368
  remove: removeAction,
15203
15369
  get: getAction,
15370
+ getLabel: getActionLabel,
15204
15371
  invoke: invokeAction,
15205
15372
  list: listActions
15206
15373
  }
@@ -15270,16 +15437,18 @@ RED.deploy = (function() {
15270
15437
  '</a>'+
15271
15438
  '<a id="red-ui-header-button-deploy-options" class="red-ui-deploy-button" href="#"><i class="fa fa-caret-down"></i></a>'+
15272
15439
  '</span></li>').prependTo(".red-ui-header-toolbar");
15273
- RED.menu.init({id:"red-ui-header-button-deploy-options",
15274
- options: [
15275
- {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")}}},
15276
- {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")}}},
15277
- {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")}}},
15278
- null,
15279
- {id:"deploymenu-item-reload", icon:"red/images/deploy-reload.svg",label:RED._("deploy.restartFlows"),sublabel:RED._("deploy.restartFlowsDesc"),onselect:"core:restart-flows"},
15280
-
15281
- ]
15282
- });
15440
+ const mainMenuItems = [
15441
+ {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")}}},
15442
+ {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")}}},
15443
+ {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")}}},
15444
+ null
15445
+ ]
15446
+ if (RED.settings.runtimeState && RED.settings.runtimeState.ui === true) {
15447
+ mainMenuItems.push({id:"deploymenu-item-runtime-start", icon:"red/images/start.svg",label:"Start"/*RED._("deploy.startFlows")*/,sublabel:"Start Flows" /*RED._("deploy.startFlowsDesc")*/,onselect:"core:start-flows", visible:false})
15448
+ mainMenuItems.push({id:"deploymenu-item-runtime-stop", icon:"red/images/stop.svg",label:"Stop"/*RED._("deploy.startFlows")*/,sublabel:"Stop Flows" /*RED._("deploy.startFlowsDesc")*/,onselect:"core:stop-flows", visible:false})
15449
+ }
15450
+ mainMenuItems.push({id:"deploymenu-item-reload", icon:"red/images/deploy-reload.svg",label:RED._("deploy.restartFlows"),sublabel:RED._("deploy.restartFlowsDesc"),onselect:"core:restart-flows"})
15451
+ RED.menu.init({id:"red-ui-header-button-deploy-options", options: mainMenuItems });
15283
15452
  } else if (type == "simple") {
15284
15453
  var label = options.label || RED._("deploy.deploy");
15285
15454
  var icon = 'red/images/deploy-full-o.svg';
@@ -15307,6 +15476,10 @@ RED.deploy = (function() {
15307
15476
 
15308
15477
  RED.actions.add("core:deploy-flows",save);
15309
15478
  if (type === "default") {
15479
+ if (RED.settings.runtimeState && RED.settings.runtimeState.ui === true) {
15480
+ RED.actions.add("core:stop-flows",function() { stopStartFlows("stop") });
15481
+ RED.actions.add("core:start-flows",function() { stopStartFlows("start") });
15482
+ }
15310
15483
  RED.actions.add("core:restart-flows",restart);
15311
15484
  RED.actions.add("core:set-deploy-type-to-full",function() { RED.menu.setSelected("deploymenu-item-full",true);});
15312
15485
  RED.actions.add("core:set-deploy-type-to-modified-flows",function() { RED.menu.setSelected("deploymenu-item-flow",true); });
@@ -15477,18 +15650,73 @@ RED.deploy = (function() {
15477
15650
  function sanitize(html) {
15478
15651
  return html.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")
15479
15652
  }
15480
- function restart() {
15481
- var startTime = Date.now();
15482
- $(".red-ui-deploy-button-content").css('opacity',0);
15483
- $(".red-ui-deploy-button-spinner").show();
15484
- var deployWasEnabled = !$("#red-ui-header-button-deploy").hasClass("disabled");
15485
- $("#red-ui-header-button-deploy").addClass("disabled");
15486
- deployInflight = true;
15653
+
15654
+ function shadeShow() {
15487
15655
  $("#red-ui-header-shade").show();
15488
15656
  $("#red-ui-editor-shade").show();
15489
15657
  $("#red-ui-palette-shade").show();
15490
15658
  $("#red-ui-sidebar-shade").show();
15491
-
15659
+ }
15660
+ function shadeHide() {
15661
+ $("#red-ui-header-shade").hide();
15662
+ $("#red-ui-editor-shade").hide();
15663
+ $("#red-ui-palette-shade").hide();
15664
+ $("#red-ui-sidebar-shade").hide();
15665
+ }
15666
+ function deployButtonSetBusy(){
15667
+ $(".red-ui-deploy-button-content").css('opacity',0);
15668
+ $(".red-ui-deploy-button-spinner").show();
15669
+ $("#red-ui-header-button-deploy").addClass("disabled");
15670
+ }
15671
+ function deployButtonClearBusy(){
15672
+ $(".red-ui-deploy-button-content").css('opacity',1);
15673
+ $(".red-ui-deploy-button-spinner").hide();
15674
+ }
15675
+ function stopStartFlows(state) {
15676
+ const startTime = Date.now()
15677
+ const deployWasEnabled = !$("#red-ui-header-button-deploy").hasClass("disabled")
15678
+ deployInflight = true
15679
+ deployButtonSetBusy()
15680
+ shadeShow()
15681
+ $.ajax({
15682
+ url:"flows/state",
15683
+ type: "POST",
15684
+ data: {state: state}
15685
+ }).done(function(data,textStatus,xhr) {
15686
+ if (deployWasEnabled) {
15687
+ $("#red-ui-header-button-deploy").removeClass("disabled")
15688
+ }
15689
+ }).fail(function(xhr,textStatus,err) {
15690
+ if (deployWasEnabled) {
15691
+ $("#red-ui-header-button-deploy").removeClass("disabled")
15692
+ }
15693
+ if (xhr.status === 401) {
15694
+ RED.notify(RED._("notification.error", { message: RED._("user.notAuthorized") }), "error")
15695
+ } else if (xhr.responseText) {
15696
+ const errorDetail = { message: err ? (err + "") : "" }
15697
+ try {
15698
+ errorDetail.message = JSON.parse(xhr.responseText).message
15699
+ } finally {
15700
+ errorDetail.message = errorDetail.message || xhr.responseText
15701
+ }
15702
+ RED.notify(RED._("notification.error", errorDetail), "error")
15703
+ } else {
15704
+ RED.notify(RED._("notification.error", { message: RED._("deploy.errors.noResponse") }), "error")
15705
+ }
15706
+ }).always(function() {
15707
+ const delta = Math.max(0, 300 - (Date.now() - startTime))
15708
+ setTimeout(function () {
15709
+ deployButtonClearBusy()
15710
+ shadeHide()
15711
+ deployInflight = false
15712
+ }, delta);
15713
+ });
15714
+ }
15715
+ function restart() {
15716
+ var startTime = Date.now();
15717
+ var deployWasEnabled = !$("#red-ui-header-button-deploy").hasClass("disabled");
15718
+ deployInflight = true;
15719
+ deployButtonSetBusy();
15492
15720
  $.ajax({
15493
15721
  url:"flows",
15494
15722
  type: "POST",
@@ -15514,15 +15742,10 @@ RED.deploy = (function() {
15514
15742
  RED.notify(RED._("deploy.deployFailed",{message:RED._("deploy.errors.noResponse")}),"error");
15515
15743
  }
15516
15744
  }).always(function() {
15517
- deployInflight = false;
15518
15745
  var delta = Math.max(0,300-(Date.now()-startTime));
15519
15746
  setTimeout(function() {
15520
- $(".red-ui-deploy-button-content").css('opacity',1);
15521
- $(".red-ui-deploy-button-spinner").hide();
15522
- $("#red-ui-header-shade").hide();
15523
- $("#red-ui-editor-shade").hide();
15524
- $("#red-ui-palette-shade").hide();
15525
- $("#red-ui-sidebar-shade").hide();
15747
+ deployButtonClearBusy();
15748
+ deployInflight = false;
15526
15749
  },delta);
15527
15750
  });
15528
15751
  }
@@ -15657,21 +15880,14 @@ RED.deploy = (function() {
15657
15880
  const nns = RED.nodes.createCompleteNodeSet();
15658
15881
  const startTime = Date.now();
15659
15882
 
15660
- $(".red-ui-deploy-button-content").css('opacity', 0);
15661
- $(".red-ui-deploy-button-spinner").show();
15662
- $("#red-ui-header-button-deploy").addClass("disabled");
15663
-
15883
+ deployButtonSetBusy();
15664
15884
  const data = { flows: nns };
15665
-
15666
15885
  if (!force) {
15667
15886
  data.rev = RED.nodes.version();
15668
15887
  }
15669
15888
 
15670
15889
  deployInflight = true;
15671
- $("#red-ui-header-shade").show();
15672
- $("#red-ui-editor-shade").show();
15673
- $("#red-ui-palette-shade").show();
15674
- $("#red-ui-sidebar-shade").show();
15890
+ shadeShow();
15675
15891
  $.ajax({
15676
15892
  url: "flows",
15677
15893
  type: "POST",
@@ -15757,15 +15973,11 @@ RED.deploy = (function() {
15757
15973
  RED.notify(RED._("deploy.deployFailed", { message: RED._("deploy.errors.noResponse") }), "error");
15758
15974
  }
15759
15975
  }).always(function () {
15760
- deployInflight = false;
15761
15976
  const delta = Math.max(0, 300 - (Date.now() - startTime));
15762
15977
  setTimeout(function () {
15763
- $(".red-ui-deploy-button-content").css('opacity', 1);
15764
- $(".red-ui-deploy-button-spinner").hide();
15765
- $("#red-ui-header-shade").hide();
15766
- $("#red-ui-editor-shade").hide();
15767
- $("#red-ui-palette-shade").hide();
15768
- $("#red-ui-sidebar-shade").hide();
15978
+ deployInflight = false;
15979
+ deployButtonClearBusy()
15980
+ shadeHide()
15769
15981
  }, delta);
15770
15982
  });
15771
15983
  }
@@ -19054,9 +19266,22 @@ RED.workspaces = (function() {
19054
19266
  onselect: "core:show-last-hidden-flow"
19055
19267
  }
19056
19268
  ]
19057
- if (hideStack.length > 0) {
19269
+ let hiddenFlows = new Set()
19270
+ for (let i = 0; i < hideStack.length; i++) {
19271
+ let ids = hideStack[i]
19272
+ if (!Array.isArray(ids)) {
19273
+ ids = [ids]
19274
+ }
19275
+ ids.forEach(id => {
19276
+ if (RED.nodes.workspace(id)) {
19277
+ hiddenFlows.add(id)
19278
+ }
19279
+ })
19280
+ }
19281
+ const flowCount = hiddenFlows.size;
19282
+ if (flowCount > 0) {
19058
19283
  menuItems.unshift({
19059
- label: RED._("workspace.hiddenFlows",{count: hideStack.length}),
19284
+ label: RED._("workspace.hiddenFlows",{count: flowCount}),
19060
19285
  onselect: "core:list-hidden-flows"
19061
19286
  })
19062
19287
  }
@@ -19552,7 +19777,6 @@ RED.view = (function() {
19552
19777
  var selectNodesOptions;
19553
19778
 
19554
19779
  let flashingNodeId;
19555
- let flashingNodeTimer;
19556
19780
 
19557
19781
  var clipboard = "";
19558
19782
 
@@ -19632,7 +19856,8 @@ RED.view = (function() {
19632
19856
  length: function() { return set.length},
19633
19857
  get: function(i) { return set[i] },
19634
19858
  forEach: function(func) { set.forEach(func) },
19635
- nodes: function() { return set.map(function(n) { return n.n })}
19859
+ nodes: function() { return set.map(function(n) { return n.n })},
19860
+ has: function(node) { return setIds.has(node.id) }
19636
19861
  }
19637
19862
  return api;
19638
19863
  })();
@@ -19664,13 +19889,16 @@ RED.view = (function() {
19664
19889
 
19665
19890
  function init() {
19666
19891
 
19667
- // setTimeout(function() {
19668
- // function snap(p) { return RED.view.gridSize() * Math.round(p/RED.view.gridSize())}; for (var i = 0;i<10;i++) {
19669
- // RED.nodes.addJunction({_def:{defaults:{}}, type:'junction', z:"0ccdc1d81f2729cc",id:RED.nodes.id(),x:snap(Math.floor(Math.random()*600)),y:snap(Math.floor(Math.random()*600)), w:0,h:0})
19670
- // } ; RED.view.redraw(true)
19671
- // },2000)
19672
19892
  chart = $("#red-ui-workspace-chart");
19673
-
19893
+ chart.on('contextmenu', function(evt) {
19894
+ evt.preventDefault()
19895
+ evt.stopPropagation()
19896
+ RED.contextMenu.show({
19897
+ x:evt.clientX-5,
19898
+ y:evt.clientY-5
19899
+ })
19900
+ return false
19901
+ })
19674
19902
  outer = d3.select("#red-ui-workspace-chart")
19675
19903
  .append("svg:svg")
19676
19904
  .attr("width", space_width)
@@ -19694,6 +19922,7 @@ RED.view = (function() {
19694
19922
  .on("mousedown", canvasMouseDown)
19695
19923
  .on("mouseup", canvasMouseUp)
19696
19924
  .on("mouseenter", function() {
19925
+ d3.select(document).on('mouseup.red-ui-workspace-tracker', null)
19697
19926
  if (lasso) {
19698
19927
  if (d3.event.buttons !== 1) {
19699
19928
  lasso.remove();
@@ -19709,6 +19938,7 @@ RED.view = (function() {
19709
19938
  }
19710
19939
  }
19711
19940
  })
19941
+ .on("mouseleave", canvasMouseLeave)
19712
19942
  .on("touchend", function() {
19713
19943
  d3.event.preventDefault();
19714
19944
  clearTimeout(touchStartTime);
@@ -19849,6 +20079,9 @@ RED.view = (function() {
19849
20079
  drag_lines = [];
19850
20080
 
19851
20081
  RED.events.on("workspace:change",function(event) {
20082
+ // Just in case the mouse left the workspace whilst doing an action,
20083
+ // put us back into default mode so the refresh works
20084
+ mouse_mode = 0
19852
20085
  if (event.old !== 0) {
19853
20086
  workspaceScrollPositions[event.old] = {
19854
20087
  left:chart.scrollLeft(),
@@ -19990,6 +20223,23 @@ RED.view = (function() {
19990
20223
  nn.x = mousePos[0];
19991
20224
  nn.y = mousePos[1];
19992
20225
 
20226
+ var minX = nn.w/2 -5;
20227
+ if (nn.x < minX) {
20228
+ nn.x = minX;
20229
+ }
20230
+ var minY = nn.h/2 -5;
20231
+ if (nn.y < minY) {
20232
+ nn.y = minY;
20233
+ }
20234
+ var maxX = space_width -nn.w/2 +5;
20235
+ if (nn.x > maxX) {
20236
+ nn.x = maxX;
20237
+ }
20238
+ var maxY = space_height -nn.h +5;
20239
+ if (nn.y > maxY) {
20240
+ nn.y = maxY;
20241
+ }
20242
+
19993
20243
  if (snapGrid) {
19994
20244
  var gridOffset = RED.view.tools.calculateGridSnapOffsets(nn);
19995
20245
  nn.x -= gridOffset.x;
@@ -20419,8 +20669,10 @@ RED.view = (function() {
20419
20669
  }
20420
20670
 
20421
20671
  function canvasMouseDown() {
20422
- if (RED.view.DEBUG) { console.warn("canvasMouseDown", mouse_mode); }
20423
- var point;
20672
+ if (RED.view.DEBUG) {
20673
+ console.warn("canvasMouseDown", { mouse_mode, point: d3.mouse(this), event: d3.event });
20674
+ }
20675
+ RED.contextMenu.hide();
20424
20676
  if (mouse_mode === RED.state.SELECTING_NODE) {
20425
20677
  d3.event.stopPropagation();
20426
20678
  return;
@@ -20433,49 +20685,56 @@ RED.view = (function() {
20433
20685
  scroll_position = [chart.scrollLeft(),chart.scrollTop()];
20434
20686
  return;
20435
20687
  }
20436
- if (!mousedown_node && !mousedown_link && !mousedown_group) {
20688
+ if (d3.event.button === 2) {
20689
+ return
20690
+ }
20691
+ if (!mousedown_node && !mousedown_link && !mousedown_group && !d3.event.shiftKey) {
20437
20692
  selectedLinks.clear();
20438
20693
  updateSelection();
20439
20694
  }
20440
- if (mouse_mode === 0) {
20441
- if (lasso) {
20442
- lasso.remove();
20443
- lasso = null;
20444
- }
20695
+ if (mouse_mode === 0 && lasso) {
20696
+ lasso.remove();
20697
+ lasso = null;
20445
20698
  }
20446
- if ((mouse_mode === 0 || mouse_mode === RED.state.QUICK_JOINING) && (d3.event.touches || d3.event.button === 0) && (d3.event.metaKey || d3.event.ctrlKey)) {
20447
- // Trigger quick add dialog
20448
- d3.event.stopPropagation();
20449
- clearSelection();
20450
- point = d3.mouse(this);
20451
- var clickedGroup = getGroupAt(point[0],point[1]);
20452
- if (drag_lines.length > 0) {
20453
- clickedGroup = clickedGroup || RED.nodes.group(drag_lines[0].node.g)
20454
- }
20455
- showQuickAddDialog({position:point, group:clickedGroup});
20456
- } else if (mouse_mode === 0 && (d3.event.touches || d3.event.button === 0) && !(d3.event.metaKey || d3.event.ctrlKey)) {
20457
- // Tigger lasso
20458
- if (!touchStartTime) {
20459
- point = d3.mouse(this);
20460
- lasso = eventLayer.append("rect")
20461
- .attr("ox",point[0])
20462
- .attr("oy",point[1])
20463
- .attr("rx",1)
20464
- .attr("ry",1)
20465
- .attr("x",point[0])
20466
- .attr("y",point[1])
20467
- .attr("width",0)
20468
- .attr("height",0)
20469
- .attr("class","nr-ui-view-lasso");
20470
- d3.event.preventDefault();
20699
+ if (d3.event.touches || d3.event.button === 0) {
20700
+ if ((mouse_mode === 0 || mouse_mode === RED.state.QUICK_JOINING) && (d3.event.metaKey || d3.event.ctrlKey) && !(d3.event.altKey || d3.event.shiftKey)) {
20701
+ // Trigger quick add dialog
20702
+ d3.event.stopPropagation();
20703
+ clearSelection();
20704
+ const point = d3.mouse(this);
20705
+ var clickedGroup = getGroupAt(point[0], point[1]);
20706
+ if (drag_lines.length > 0) {
20707
+ clickedGroup = clickedGroup || RED.nodes.group(drag_lines[0].node.g)
20708
+ }
20709
+ showQuickAddDialog({ position: point, group: clickedGroup });
20710
+ } else if (mouse_mode === 0 && !(d3.event.metaKey || d3.event.ctrlKey)) {
20711
+ // CTRL not being held
20712
+ if (!d3.event.altKey) {
20713
+ // ALT not held (shift is allowed) Trigger lasso
20714
+ if (!touchStartTime) {
20715
+ const point = d3.mouse(this);
20716
+ lasso = eventLayer.append("rect")
20717
+ .attr("ox", point[0])
20718
+ .attr("oy", point[1])
20719
+ .attr("rx", 1)
20720
+ .attr("ry", 1)
20721
+ .attr("x", point[0])
20722
+ .attr("y", point[1])
20723
+ .attr("width", 0)
20724
+ .attr("height", 0)
20725
+ .attr("class", "nr-ui-view-lasso");
20726
+ d3.event.preventDefault();
20727
+ }
20728
+ } else if (d3.event.altKey) {
20729
+ //Alt [+shift] held - Begin slicing
20730
+ clearSelection();
20731
+ mouse_mode = (d3.event.shiftKey) ? RED.state.SLICING_JUNCTION : RED.state.SLICING;
20732
+ const point = d3.mouse(this);
20733
+ slicePath = eventLayer.append("path").attr("class", "nr-ui-view-slice").attr("d", `M${point[0]} ${point[1]}`)
20734
+ slicePathLast = point;
20735
+ RED.view.redraw();
20736
+ }
20471
20737
  }
20472
- } else if (mouse_mode === 0 && d3.event.button === 2 && (d3.event.metaKey || d3.event.ctrlKey || d3.event.shiftKey)) {
20473
- clearSelection();
20474
- mouse_mode = (d3.event.metaKey || d3.event.ctrlKey)?RED.state.SLICING : RED.state.SLICING_JUNCTION;
20475
- point = d3.mouse(this);
20476
- slicePath = eventLayer.append("path").attr("class","nr-ui-view-slice").attr("d",`M${point[0]} ${point[1]}`)
20477
- slicePathLast = point;
20478
- RED.view.redraw();
20479
20738
  }
20480
20739
  }
20481
20740
 
@@ -20483,6 +20742,7 @@ RED.view = (function() {
20483
20742
  options = options || {};
20484
20743
  var point = options.position || lastClickPosition;
20485
20744
  var spliceLink = options.splice;
20745
+ var spliceMultipleLinks = options.spliceMultiple
20486
20746
  var targetGroup = options.group;
20487
20747
  var touchTrigger = options.touchTrigger;
20488
20748
 
@@ -20495,6 +20755,10 @@ RED.view = (function() {
20495
20755
  var ox = point[0];
20496
20756
  var oy = point[1];
20497
20757
 
20758
+ const offset = $("#red-ui-workspace-chart").offset()
20759
+ var clientX = ox + offset.left - $("#red-ui-workspace-chart").scrollLeft()
20760
+ var clientY = oy + offset.top - $("#red-ui-workspace-chart").scrollTop()
20761
+
20498
20762
  if (RED.settings.get("editor").view['view-snap-grid']) {
20499
20763
  // eventLayer.append("circle").attr("cx",point[0]).attr("cy",point[1]).attr("r","2").attr('fill','red')
20500
20764
  point[0] = Math.round(point[0] / gridSize) * gridSize;
@@ -20546,8 +20810,12 @@ RED.view = (function() {
20546
20810
  }
20547
20811
  hideDragLines();
20548
20812
  }
20549
- if (spliceLink) {
20550
- filter = {input:true, output:true}
20813
+ if (spliceLink || spliceMultipleLinks) {
20814
+ filter = {
20815
+ input:true,
20816
+ output:true,
20817
+ spliceMultiple: spliceMultipleLinks
20818
+ }
20551
20819
  }
20552
20820
 
20553
20821
  var rebuildQuickAddLink = function() {
@@ -20572,8 +20840,8 @@ RED.view = (function() {
20572
20840
  var lastAddedWidth;
20573
20841
 
20574
20842
  RED.typeSearch.show({
20575
- x:d3.event.clientX-mainPos.left-node_width/2 - (ox-point[0]),
20576
- y:d3.event.clientY-mainPos.top+ node_height/2 + 5 - (oy-point[1]),
20843
+ x:clientX-mainPos.left-node_width/2 - (ox-point[0]),
20844
+ y:clientY-mainPos.top+ node_height/2 + 5 - (oy-point[1]),
20577
20845
  disableFocus: touchTrigger,
20578
20846
  filter: filter,
20579
20847
  move: function(dx,dy) {
@@ -20601,7 +20869,7 @@ RED.view = (function() {
20601
20869
  hideDragLines();
20602
20870
  redraw();
20603
20871
  },
20604
- add: function(type,keepAdding) {
20872
+ add: function(type, keepAdding) {
20605
20873
  if (touchTrigger) {
20606
20874
  keepAdding = false;
20607
20875
  resetMouseVars();
@@ -20609,7 +20877,13 @@ RED.view = (function() {
20609
20877
 
20610
20878
  var nn;
20611
20879
  var historyEvent;
20612
- if (type === 'junction') {
20880
+ if (/^_action_:/.test(type)) {
20881
+ const actionName = type.substring(9)
20882
+ quickAddActive = false;
20883
+ ghostNode.remove();
20884
+ RED.actions.invoke(actionName)
20885
+ return
20886
+ } else if (type === 'junction') {
20613
20887
  nn = {
20614
20888
  _def: {defaults:{}},
20615
20889
  type: 'junction',
@@ -21175,12 +21449,24 @@ RED.view = (function() {
21175
21449
  redraw();
21176
21450
  }
21177
21451
  }
21178
-
21452
+ function canvasMouseLeave() {
21453
+ if (mouse_mode !== 0 && d3.event.buttons !== 0) {
21454
+ d3.select(document).on('mouseup.red-ui-workspace-tracker', function() {
21455
+ d3.select(document).on('mouseup.red-ui-workspace-tracker', null)
21456
+ canvasMouseUp.call(this)
21457
+ })
21458
+ }
21459
+ }
21179
21460
  function canvasMouseUp() {
21180
21461
  lastClickPosition = [d3.event.offsetX/scaleFactor,d3.event.offsetY/scaleFactor];
21181
- if (RED.view.DEBUG) { console.warn("canvasMouseUp", mouse_mode); }
21462
+ if (RED.view.DEBUG) {
21463
+ console.warn("canvasMouseUp", { mouse_mode, point: d3.mouse(this), event: d3.event });
21464
+ }
21182
21465
  var i;
21183
21466
  var historyEvent;
21467
+ if (d3.event.button === 2) {
21468
+ return
21469
+ }
21184
21470
  if (mouse_mode === RED.state.PANNING) {
21185
21471
  resetMouseVars();
21186
21472
  return
@@ -21272,8 +21558,20 @@ RED.view = (function() {
21272
21558
  }
21273
21559
  }
21274
21560
  })
21275
-
21276
-
21561
+ activeLinks.forEach(function(link) {
21562
+ if (!link.selected) {
21563
+ var sourceY = link.source.y
21564
+ var targetY = link.target.y
21565
+ var sourceX = link.source.x+(link.source.w/2) + 10
21566
+ var targetX = link.target.x-(link.target.w/2) - 10
21567
+ if (
21568
+ sourceX > x && sourceX < x2 && sourceY > y && sourceY < y2 &&
21569
+ targetX > x && targetX < x2 && targetY > y && targetY < y2
21570
+ ) {
21571
+ selectedLinks.add(link);
21572
+ }
21573
+ }
21574
+ })
21277
21575
 
21278
21576
  // var selectionChanged = false;
21279
21577
  // do {
@@ -21321,86 +21619,118 @@ RED.view = (function() {
21321
21619
  slicePath = null;
21322
21620
  RED.view.redraw(true);
21323
21621
  } else if (mouse_mode == RED.state.SLICING_JUNCTION) {
21324
- var removedLinks = []
21325
- var addedLinks = []
21326
- var addedJunctions = []
21327
-
21328
- var groupedLinks = {}
21329
- selectedLinks.forEach(function(l) {
21330
- var sourceId = l.source.id+":"+l.sourcePort
21331
- groupedLinks[sourceId] = groupedLinks[sourceId] || []
21332
- groupedLinks[sourceId].push(l)
21333
- });
21334
- var linkGroups = Object.keys(groupedLinks)
21335
- linkGroups.forEach(function(gid) {
21336
- var links = groupedLinks[gid]
21337
- var junction = {
21338
- _def: {defaults:{}},
21339
- type: 'junction',
21340
- z: RED.workspaces.active(),
21341
- id: RED.nodes.id(),
21342
- x: 0,
21343
- y: 0,
21344
- w: 0, h: 0,
21345
- outputs: 1,
21346
- inputs: 1,
21347
- dirty: true
21348
- }
21349
- links.forEach(function(l) {
21350
- junction.x += l._sliceLocation.x
21351
- junction.y += l._sliceLocation.y
21352
- })
21353
- junction.x = Math.round(junction.x/links.length)
21354
- junction.y = Math.round(junction.y/links.length)
21355
- if (snapGrid) {
21356
- junction.x = (gridSize*Math.round(junction.x/gridSize));
21357
- junction.y = (gridSize*Math.round(junction.y/gridSize));
21358
- }
21359
-
21360
- var nodeGroups = new Set()
21361
-
21362
- RED.nodes.addJunction(junction)
21363
- addedJunctions.push(junction)
21364
- var newLink = {
21365
- source: links[0].source,
21366
- sourcePort: links[0].sourcePort,
21367
- target: junction
21368
- }
21369
- addedLinks.push(newLink)
21370
- RED.nodes.addLink(newLink)
21371
- links.forEach(function(l) {
21372
- removedLinks.push(l)
21373
- RED.nodes.removeLink(l)
21374
- var newLink = {
21375
- source: junction,
21376
- sourcePort: 0,
21377
- target: l.target
21378
- }
21379
- addedLinks.push(newLink)
21380
- RED.nodes.addLink(newLink)
21381
- nodeGroups.add(l.source.g || "__NONE__")
21382
- nodeGroups.add(l.target.g || "__NONE__")
21383
- })
21384
- if (nodeGroups.size === 1) {
21385
- var group = nodeGroups.values().next().value
21386
- if (group !== "__NONE__") {
21387
- RED.group.addToGroup(RED.nodes.group(group), junction)
21388
- }
21389
- }
21390
- })
21622
+ RED.actions.invoke("core:split-wires-with-junctions")
21391
21623
  slicePath.remove();
21392
21624
  slicePath = null;
21393
21625
 
21394
- if (addedJunctions.length > 0) {
21395
- RED.history.push({
21396
- t: 'add',
21397
- links: addedLinks,
21398
- junctions: addedJunctions,
21399
- removedLinks: removedLinks
21400
- })
21401
- RED.nodes.dirty(true)
21402
- }
21403
- RED.view.redraw(true);
21626
+ // var removedLinks = new Set()
21627
+ // var addedLinks = []
21628
+ // var addedJunctions = []
21629
+ //
21630
+ // var groupedLinks = {}
21631
+ // selectedLinks.forEach(function(l) {
21632
+ // var sourceId = l.source.id+":"+l.sourcePort
21633
+ // groupedLinks[sourceId] = groupedLinks[sourceId] || []
21634
+ // groupedLinks[sourceId].push(l)
21635
+ //
21636
+ // groupedLinks[l.target.id] = groupedLinks[l.target.id] || []
21637
+ // groupedLinks[l.target.id].push(l)
21638
+ // });
21639
+ // var linkGroups = Object.keys(groupedLinks)
21640
+ // linkGroups.sort(function(A,B) {
21641
+ // return groupedLinks[B].length - groupedLinks[A].length
21642
+ // })
21643
+ // linkGroups.forEach(function(gid) {
21644
+ // var links = groupedLinks[gid]
21645
+ // var junction = {
21646
+ // _def: {defaults:{}},
21647
+ // type: 'junction',
21648
+ // z: RED.workspaces.active(),
21649
+ // id: RED.nodes.id(),
21650
+ // x: 0,
21651
+ // y: 0,
21652
+ // w: 0, h: 0,
21653
+ // outputs: 1,
21654
+ // inputs: 1,
21655
+ // dirty: true
21656
+ // }
21657
+ // links = links.filter(function(l) { return !removedLinks.has(l) })
21658
+ // if (links.length === 0) {
21659
+ // return
21660
+ // }
21661
+ // links.forEach(function(l) {
21662
+ // junction.x += l._sliceLocation.x
21663
+ // junction.y += l._sliceLocation.y
21664
+ // })
21665
+ // junction.x = Math.round(junction.x/links.length)
21666
+ // junction.y = Math.round(junction.y/links.length)
21667
+ // if (snapGrid) {
21668
+ // junction.x = (gridSize*Math.round(junction.x/gridSize));
21669
+ // junction.y = (gridSize*Math.round(junction.y/gridSize));
21670
+ // }
21671
+ //
21672
+ // var nodeGroups = new Set()
21673
+ //
21674
+ // RED.nodes.addJunction(junction)
21675
+ // addedJunctions.push(junction)
21676
+ // let newLink
21677
+ // if (gid === links[0].source.id+":"+links[0].sourcePort) {
21678
+ // newLink = {
21679
+ // source: links[0].source,
21680
+ // sourcePort: links[0].sourcePort,
21681
+ // target: junction
21682
+ // }
21683
+ // } else {
21684
+ // newLink = {
21685
+ // source: junction,
21686
+ // sourcePort: 0,
21687
+ // target: links[0].target
21688
+ // }
21689
+ // }
21690
+ // addedLinks.push(newLink)
21691
+ // RED.nodes.addLink(newLink)
21692
+ // links.forEach(function(l) {
21693
+ // removedLinks.add(l)
21694
+ // RED.nodes.removeLink(l)
21695
+ // let newLink
21696
+ // if (gid === l.target.id) {
21697
+ // newLink = {
21698
+ // source: l.source,
21699
+ // sourcePort: l.sourcePort,
21700
+ // target: junction
21701
+ // }
21702
+ // } else {
21703
+ // newLink = {
21704
+ // source: junction,
21705
+ // sourcePort: 0,
21706
+ // target: l.target
21707
+ // }
21708
+ // }
21709
+ // addedLinks.push(newLink)
21710
+ // RED.nodes.addLink(newLink)
21711
+ // nodeGroups.add(l.source.g || "__NONE__")
21712
+ // nodeGroups.add(l.target.g || "__NONE__")
21713
+ // })
21714
+ // if (nodeGroups.size === 1) {
21715
+ // var group = nodeGroups.values().next().value
21716
+ // if (group !== "__NONE__") {
21717
+ // RED.group.addToGroup(RED.nodes.group(group), junction)
21718
+ // }
21719
+ // }
21720
+ // })
21721
+ // slicePath.remove();
21722
+ // slicePath = null;
21723
+ //
21724
+ // if (addedJunctions.length > 0) {
21725
+ // RED.history.push({
21726
+ // t: 'add',
21727
+ // links: addedLinks,
21728
+ // junctions: addedJunctions,
21729
+ // removedLinks: Array.from(removedLinks)
21730
+ // })
21731
+ // RED.nodes.dirty(true)
21732
+ // }
21733
+ // RED.view.redraw(true);
21404
21734
  }
21405
21735
  if (mouse_mode == RED.state.MOVING_ACTIVE) {
21406
21736
  if (movingSet.length() > 0) {
@@ -22261,6 +22591,7 @@ RED.view = (function() {
22261
22591
 
22262
22592
  function portMouseDown(d,portType,portIndex, evt) {
22263
22593
  if (RED.view.DEBUG) { console.warn("portMouseDown", mouse_mode,d,portType,portIndex); }
22594
+ RED.contextMenu.hide();
22264
22595
  evt = evt || d3.event;
22265
22596
  if (evt === 1) {
22266
22597
  return;
@@ -22377,7 +22708,7 @@ RED.view = (function() {
22377
22708
  } else if (drag_line.portType === PORT_TYPE_INPUT) {
22378
22709
  src = mouseup_node;
22379
22710
  dst = drag_line.node;
22380
- src_port = portIndex;
22711
+ src_port = portIndex || 0;
22381
22712
  }
22382
22713
  var link = {source: src, sourcePort:src_port, target: dst};
22383
22714
  if (drag_line.virtualLink) {
@@ -22668,52 +22999,16 @@ RED.view = (function() {
22668
22999
  port.classed("red-ui-flow-port-hovered",false);
22669
23000
  }
22670
23001
 
22671
- function junctionMouseOver(junction, d) {
22672
- junction.classed("red-ui-flow-junction-hovered",true);
23002
+ function junctionMouseOver(junction, d, portType) {
23003
+ var active = (portType === undefined) ||
23004
+ (mouse_mode !== RED.state.JOINING && mouse_mode !== RED.state.QUICK_JOINING) ||
23005
+ (drag_lines.length > 0 && drag_lines[0].portType !== portType && !drag_lines[0].virtualLink)
23006
+ junction.classed("red-ui-flow-junction-hovered", active);
22673
23007
  }
22674
23008
  function junctionMouseOut(junction, d) {
22675
23009
  junction.classed("red-ui-flow-junction-hovered",false);
22676
23010
  }
22677
23011
 
22678
- function junctionMouseDown(junction, d, evt) {
22679
- if (RED.view.DEBUG) { console.warn("junctionMouseDown", d); }
22680
- evt = evt || d3.event;
22681
- d3.event = evt
22682
- if (evt === 1) {
22683
- return;
22684
- }
22685
- if (mouse_mode === RED.state.SELECTING_NODE) {
22686
- evt.stopPropagation();
22687
- return;
22688
- }
22689
- if (mouse_mode == RED.state.QUICK_JOINING) {
22690
- d3.event.stopPropagation();
22691
- return;
22692
- }
22693
-
22694
- // mousedown_node = d;
22695
- // mousedown_port_type = portType;
22696
- // mousedown_port_index = portIndex || 0;
22697
- if (mouse_mode !== RED.state.QUICK_JOINING && (evt.ctrlKey || evt.metaKey)) {
22698
- mouse_mode = RED.state.QUICK_JOINING;
22699
- document.body.style.cursor = "crosshair";
22700
- showDragLines([{node:d,port:0,portType: PORT_TYPE_OUTPUT}]);
22701
- $(window).on('keyup',disableQuickJoinEventHandler);
22702
- } else if (event.button != 2) {
22703
- nodeMouseDown.call(junction[0][0],d)
22704
- // clearSelection();
22705
- // movingSet.add(d);
22706
- // mousedown_node = d;
22707
- // mouse_mode = RED.state.MOVING;
22708
- // var mouse = d3.touches(junction[0][0])[0]||d3.mouse(junction[0][0]);
22709
- // mouse[0] += d.x-d.w/2;
22710
- // mouse[1] += d.y-d.h/2;
22711
- // prepareDrag(mouse);
22712
- }
22713
- evt.stopPropagation();
22714
- evt.preventDefault();
22715
- }
22716
-
22717
23012
  function prepareDrag(mouse) {
22718
23013
  mouse_mode = RED.state.MOVING;
22719
23014
  // Called when movingSet should be prepared to be dragged
@@ -22805,6 +23100,7 @@ RED.view = (function() {
22805
23100
  function nodeMouseDown(d) {
22806
23101
  if (RED.view.DEBUG) { console.warn("nodeMouseDown", mouse_mode,d); }
22807
23102
  focusView();
23103
+ RED.contextMenu.hide();
22808
23104
  if (d3.event.button === 1) {
22809
23105
  return;
22810
23106
  }
@@ -22873,6 +23169,9 @@ RED.view = (function() {
22873
23169
  return;
22874
23170
  } else if (mouse_mode === RED.state.SELECTING_NODE) {
22875
23171
  d3.event.stopPropagation();
23172
+ if (d.type === 'junction') {
23173
+ return
23174
+ }
22876
23175
  if (selectNodesOptions.single) {
22877
23176
  selectNodesOptions.done(d);
22878
23177
  return;
@@ -22899,12 +23198,12 @@ RED.view = (function() {
22899
23198
  var now = Date.now();
22900
23199
  clickElapsed = now-clickTime;
22901
23200
  clickTime = now;
22902
- dblClickPrimed = (lastClickNode == mousedown_node &&
23201
+ dblClickPrimed = lastClickNode == mousedown_node &&
22903
23202
  (d3.event.touches || d3.event.button === 0) &&
22904
23203
  !d3.event.shiftKey && !d3.event.altKey &&
22905
- clickElapsed < dblClickInterval
22906
- )
22907
- lastClickNode = mousedown_node;
23204
+ clickElapsed < dblClickInterval &&
23205
+ d.type !== 'junction'
23206
+ lastClickNode = mousedown_node;
22908
23207
 
22909
23208
  if (!d.selected && d.g /*&& !RED.nodes.group(d.g).selected*/) {
22910
23209
  var nodeGroup = RED.nodes.group(d.g);
@@ -23030,9 +23329,9 @@ RED.view = (function() {
23030
23329
  clearSelection();
23031
23330
  }
23032
23331
  var clickPosition = (d3.event.offsetX/scaleFactor - mousedown_node.x)
23033
- var edgeDelta = (mousedown_node.w/2) - Math.abs(clickPosition);
23332
+ var edgeDelta = ((mousedown_node.w||10)/2) - Math.abs(clickPosition);
23034
23333
  var cnodes;
23035
- var targetEdgeDelta = mousedown_node.w > 30 ? 25 : 8;
23334
+ var targetEdgeDelta = mousedown_node.w > 30 ? 25 : (mousedown_node.w > 0 ? 8 : 3);
23036
23335
  if (edgeDelta < targetEdgeDelta) {
23037
23336
  if (clickPosition < 0) {
23038
23337
  cnodes = [mousedown_node].concat(RED.nodes.getAllUpstreamNodes(mousedown_node));
@@ -23177,12 +23476,14 @@ RED.view = (function() {
23177
23476
  function portMouseOverProxy(e) { portMouseOver(d3.select(this), this.__data__,this.__portType__,this.__portIndex__, e); }
23178
23477
  function portMouseOutProxy(e) { portMouseOut(d3.select(this), this.__data__,this.__portType__,this.__portIndex__, e); }
23179
23478
 
23180
- function junctionMouseOverProxy(e) { junctionMouseOver(d3.select(this), this.__data__) }
23479
+ function junctionMouseOverProxy(e) { junctionMouseOver(d3.select(this), this.__data__, this.__portType__) }
23181
23480
  function junctionMouseOutProxy(e) { junctionMouseOut(d3.select(this), this.__data__) }
23182
- function junctionMouseDownProxy(e) { junctionMouseDown(d3.select(this), this.__data__, e) }
23183
- function junctionMouseUpProxy(e) { junctionMouseUp(d3.select(this), this.__data__) }
23184
23481
 
23185
23482
  function linkMouseDown(d) {
23483
+ if (RED.view.DEBUG) {
23484
+ console.warn("linkMouseDown", { mouse_mode, point: d3.mouse(this), event: d3.event });
23485
+ }
23486
+ RED.contextMenu.hide();
23186
23487
  if (mouse_mode === RED.state.SELECTING_NODE) {
23187
23488
  d3.event.stopPropagation();
23188
23489
  return;
@@ -23242,6 +23543,9 @@ RED.view = (function() {
23242
23543
  }
23243
23544
 
23244
23545
  function groupMouseUp(g) {
23546
+ if (RED.view.DEBUG) {
23547
+ console.warn("groupMouseUp", { mouse_mode, event: d3.event });
23548
+ }
23245
23549
  if (dblClickPrimed && mousedown_group == g && clickElapsed > 0 && clickElapsed < dblClickInterval) {
23246
23550
  mouse_mode = RED.state.DEFAULT;
23247
23551
  RED.editor.editGroup(g);
@@ -23257,6 +23561,10 @@ RED.view = (function() {
23257
23561
  // return
23258
23562
  // }
23259
23563
 
23564
+ if (RED.view.DEBUG) {
23565
+ console.warn("groupMouseDown", { mouse_mode, point: mouse, event: d3.event });
23566
+ }
23567
+ RED.contextMenu.hide();
23260
23568
  focusView();
23261
23569
  if (d3.event.button === 1) {
23262
23570
  return;
@@ -24253,6 +24561,9 @@ RED.view = (function() {
24253
24561
  if (d._def.button) {
24254
24562
  var buttonEnabled = isButtonEnabled(d);
24255
24563
  this.__buttonGroup__.classList.toggle("red-ui-flow-node-button-disabled", !buttonEnabled);
24564
+ if (RED.runtime && Object.hasOwn(RED.runtime,'started')) {
24565
+ this.__buttonGroup__.classList.toggle("red-ui-flow-node-button-stopped", !RED.runtime.started);
24566
+ }
24256
24567
 
24257
24568
  var x = d._def.align == "right"?d.w-6:-25;
24258
24569
  if (d._def.button.toggle && !d[d._def.button.toggle]) {
@@ -24331,22 +24642,56 @@ RED.view = (function() {
24331
24642
  junctionBack.setAttribute("y",-5);
24332
24643
  junctionBack.setAttribute("width",10);
24333
24644
  junctionBack.setAttribute("height",10);
24334
- junctionBack.setAttribute("rx",5);
24335
- junctionBack.setAttribute("ry",5);
24645
+ junctionBack.setAttribute("rx",3);
24646
+ junctionBack.setAttribute("ry",3);
24336
24647
  junctionBack.__data__ = d;
24337
24648
  this.__junctionBack__ = junctionBack;
24338
24649
  contents.appendChild(junctionBack);
24339
24650
 
24651
+ var junctionInput = document.createElementNS("http://www.w3.org/2000/svg","rect");
24652
+ junctionInput.setAttribute("class","red-ui-flow-junction-port red-ui-flow-junction-port-input");
24653
+ junctionInput.setAttribute("x",-5);
24654
+ junctionInput.setAttribute("y",-5);
24655
+ junctionInput.setAttribute("width",10);
24656
+ junctionInput.setAttribute("height",10);
24657
+ junctionInput.setAttribute("rx",3);
24658
+ junctionInput.setAttribute("ry",3);
24659
+ junctionInput.__data__ = d;
24660
+ junctionInput.__portType__ = PORT_TYPE_INPUT;
24661
+ junctionInput.__portIndex__ = 0;
24662
+ this.__junctionInput__ = junctionOutput;
24663
+ contents.appendChild(junctionInput);
24664
+ junctionInput.addEventListener("mouseup", portMouseUpProxy);
24665
+ junctionInput.addEventListener("mousedown", portMouseDownProxy);
24666
+
24667
+
24668
+ this.__junctionInput__ = junctionInput;
24669
+ contents.appendChild(junctionInput);
24670
+ var junctionOutput = document.createElementNS("http://www.w3.org/2000/svg","rect");
24671
+ junctionOutput.setAttribute("class","red-ui-flow-junction-port red-ui-flow-junction-port-output");
24672
+ junctionOutput.setAttribute("x",-5);
24673
+ junctionOutput.setAttribute("y",-5);
24674
+ junctionOutput.setAttribute("width",10);
24675
+ junctionOutput.setAttribute("height",10);
24676
+ junctionOutput.setAttribute("rx",3);
24677
+ junctionOutput.setAttribute("ry",3);
24678
+ junctionOutput.__data__ = d;
24679
+ junctionOutput.__portType__ = PORT_TYPE_OUTPUT;
24680
+ junctionOutput.__portIndex__ = 0;
24681
+ this.__junctionOutput__ = junctionOutput;
24682
+ contents.appendChild(junctionOutput);
24683
+ junctionOutput.addEventListener("mouseup", portMouseUpProxy);
24684
+ junctionOutput.addEventListener("mousedown", portMouseDownProxy);
24685
+
24686
+ junctionOutput.addEventListener("mouseover", junctionMouseOverProxy);
24687
+ junctionOutput.addEventListener("mouseout", junctionMouseOutProxy);
24688
+ junctionInput.addEventListener("mouseover", junctionMouseOverProxy);
24689
+ junctionInput.addEventListener("mouseout", junctionMouseOutProxy);
24340
24690
  junctionBack.addEventListener("mouseover", junctionMouseOverProxy);
24341
24691
  junctionBack.addEventListener("mouseout", junctionMouseOutProxy);
24342
- junctionBack.addEventListener("mouseup", portMouseUpProxy);
24343
- junctionBack.addEventListener("mousedown", junctionMouseDownProxy);
24344
24692
 
24345
- // d3.select(junctionBack).on("mousedown", nodeMouseDown);
24346
-
24347
- this.__portType__ = PORT_TYPE_INPUT
24348
- this.__portIndex__ = 0
24349
- // function portMouseUpProxy(e) { portMouseUp(this.__data__,this.__portType__,this.__portIndex__, e); }
24693
+ // These handlers expect to be registered as d3 events
24694
+ d3.select(junctionBack).on("mousedown", nodeMouseDown).on("mouseup", nodeMouseUp);
24350
24695
 
24351
24696
  junction[0][0].appendChild(contents);
24352
24697
  })
@@ -24355,6 +24700,7 @@ RED.view = (function() {
24355
24700
  var junction = d3.select(this);
24356
24701
  this.setAttribute("transform", "translate(" + (d.x) + "," + (d.y) + ")");
24357
24702
  if (d.dirty) {
24703
+ junction.classed("red-ui-flow-junction-dragging", mouse_mode === RED.state.MOVING_ACTIVE && movingSet.has(d))
24358
24704
  junction.classed("selected", !!d.selected)
24359
24705
  dirtyNodes[d.id] = d;
24360
24706
 
@@ -24395,10 +24741,11 @@ RED.view = (function() {
24395
24741
  .on("touchstart",linkTouchStart)
24396
24742
  .on("mousemove", function(d) {
24397
24743
  if (mouse_mode === RED.state.SLICING) {
24744
+
24398
24745
  selectedLinks.add(d)
24399
24746
  l.classed("red-ui-flow-link-splice",true)
24400
24747
  redraw()
24401
- } else if (mouse_mode === RED.state.SLICING_JUNCTION) {
24748
+ } else if (mouse_mode === RED.state.SLICING_JUNCTION && !d.link) {
24402
24749
  if (!l.classed("red-ui-flow-link-splice")) {
24403
24750
  // Find intersection point
24404
24751
  var lineLength = pathLine.getTotalLength();
@@ -25144,7 +25491,12 @@ RED.view = (function() {
25144
25491
  node.dirty = true;
25145
25492
  node.dirtyStatus = true;
25146
25493
  node.changed = true;
25147
- RED.events.emit("nodes:change",node);
25494
+ if (node.type === "junction") {
25495
+ RED.events.emit("junctions:change",node);
25496
+ }
25497
+ else {
25498
+ RED.events.emit("nodes:change",node);
25499
+ }
25148
25500
  }
25149
25501
  }
25150
25502
  }
@@ -25207,6 +25559,7 @@ RED.view = (function() {
25207
25559
  * @private
25208
25560
  */
25209
25561
  function createNode(type, x, y, z) {
25562
+ const wasDirty = RED.nodes.dirty()
25210
25563
  var m = /^subflow:(.+)$/.exec(type);
25211
25564
  var activeSubflow = z ? RED.nodes.subflow(z) : null;
25212
25565
  if (activeSubflow && m) {
@@ -25265,7 +25618,7 @@ RED.view = (function() {
25265
25618
  var historyEvent = {
25266
25619
  t: "add",
25267
25620
  nodes: [nn.id],
25268
- dirty: RED.nodes.dirty()
25621
+ dirty: wasDirty
25269
25622
  }
25270
25623
  if (activeSubflow) {
25271
25624
  var subflowRefresh = RED.subflow.refresh(true);
@@ -25312,27 +25665,26 @@ RED.view = (function() {
25312
25665
  if(typeof node === "string") { node = RED.nodes.node(n); }
25313
25666
  if(!node) { return; }
25314
25667
 
25315
- const flashingNode = flashingNodeTimer && flashingNodeId && RED.nodes.node(flashingNodeId);
25668
+ const flashingNode = flashingNodeId && RED.nodes.node(flashingNodeId);
25316
25669
  if(flashingNode) {
25317
25670
  //cancel current flashing node before flashing new node
25318
- clearInterval(flashingNodeTimer);
25319
- flashingNodeTimer = null;
25671
+ clearInterval(flashingNode.__flashTimer);
25672
+ delete flashingNode.__flashTimer;
25320
25673
  flashingNode.dirty = true;
25321
25674
  flashingNode.highlighted = false;
25322
25675
  }
25323
-
25324
- flashingNodeTimer = setInterval(function(flashEndTime) {
25325
- node.dirty = true;
25676
+ node.__flashTimer = setInterval(function(flashEndTime, n) {
25677
+ n.dirty = true;
25326
25678
  if (flashEndTime >= Date.now()) {
25327
- node.highlighted = !node.highlighted;
25679
+ n.highlighted = !n.highlighted;
25328
25680
  } else {
25329
- clearInterval(flashingNodeTimer);
25330
- flashingNodeTimer = null;
25331
- node.highlighted = false;
25681
+ clearInterval(n.__flashTimer);
25682
+ delete n.__flashTimer;
25332
25683
  flashingNodeId = null;
25684
+ n.highlighted = false;
25333
25685
  }
25334
25686
  RED.view.redraw();
25335
- }, 100, Date.now() + 2200)
25687
+ }, 100, Date.now() + 2200, node)
25336
25688
  flashingNodeId = node.id;
25337
25689
  node.highlighted = true;
25338
25690
  RED.view.redraw();
@@ -26218,17 +26570,17 @@ RED.view.tools = (function() {
26218
26570
 
26219
26571
  }
26220
26572
 
26221
- function addNode() {
26222
- var selection = RED.view.selection();
26223
- if (selection.nodes && selection.nodes.length === 1 && selection.nodes[0].outputs > 0) {
26224
- var selectedNode = selection.nodes[0];
26225
- RED.view.showQuickAddDialog([
26226
- selectedNode.x + selectedNode.w + 50,selectedNode.y
26227
- ])
26228
- } else {
26229
- RED.view.showQuickAddDialog();
26230
- }
26231
- }
26573
+ // function addNode() {
26574
+ // var selection = RED.view.selection();
26575
+ // if (selection.nodes && selection.nodes.length === 1 && selection.nodes[0].outputs > 0) {
26576
+ // var selectedNode = selection.nodes[0];
26577
+ // RED.view.showQuickAddDialog([
26578
+ // selectedNode.x + selectedNode.w + 50,selectedNode.y
26579
+ // ])
26580
+ // } else {
26581
+ // RED.view.showQuickAddDialog();
26582
+ // }
26583
+ // }
26232
26584
 
26233
26585
 
26234
26586
  function gotoNearestNode(direction) {
@@ -26697,6 +27049,9 @@ RED.view.tools = (function() {
26697
27049
  */
26698
27050
  function splitWiresWithLinkNodes(wires) {
26699
27051
  let wiresToSplit = wires || RED.view.selection().links;
27052
+ if (!wiresToSplit) {
27053
+ return
27054
+ }
26700
27055
  if (!Array.isArray(wiresToSplit)) {
26701
27056
  wiresToSplit = [wiresToSplit];
26702
27057
  }
@@ -26858,13 +27213,14 @@ RED.view.tools = (function() {
26858
27213
  * - it uses `<paletteLabel> <N>` - where N is the next available integer that
26859
27214
  * doesn't clash with any existing nodes of that type
26860
27215
  * @param {Object} node The node to set the name of - if not provided, uses current selection
27216
+ * @param {{ renameBlank: boolean, renameClash: boolean, generateHistory: boolean }} options Possible options are `renameBlank`, `renameClash` and `generateHistory`
26861
27217
  */
26862
27218
  function generateNodeNames(node, options) {
26863
- options = options || {
27219
+ options = Object.assign({
26864
27220
  renameBlank: true,
26865
27221
  renameClash: true,
26866
27222
  generateHistory: true
26867
- }
27223
+ }, options)
26868
27224
  let nodes = node;
26869
27225
  if (node) {
26870
27226
  if (!Array.isArray(node)) {
@@ -26929,6 +27285,135 @@ RED.view.tools = (function() {
26929
27285
  }
26930
27286
  }
26931
27287
 
27288
+ function addJunctionsToWires(wires) {
27289
+ let wiresToSplit = wires || RED.view.selection().links;
27290
+ if (!wiresToSplit) {
27291
+ return
27292
+ }
27293
+ if (!Array.isArray(wiresToSplit)) {
27294
+ wiresToSplit = [wiresToSplit];
27295
+ }
27296
+ if (wiresToSplit.length === 0) {
27297
+ return;
27298
+ }
27299
+
27300
+ var removedLinks = new Set()
27301
+ var addedLinks = []
27302
+ var addedJunctions = []
27303
+
27304
+ var groupedLinks = {}
27305
+ wiresToSplit.forEach(function(l) {
27306
+ var sourceId = l.source.id+":"+l.sourcePort
27307
+ groupedLinks[sourceId] = groupedLinks[sourceId] || []
27308
+ groupedLinks[sourceId].push(l)
27309
+
27310
+ groupedLinks[l.target.id] = groupedLinks[l.target.id] || []
27311
+ groupedLinks[l.target.id].push(l)
27312
+ });
27313
+ var linkGroups = Object.keys(groupedLinks)
27314
+ linkGroups.sort(function(A,B) {
27315
+ return groupedLinks[B].length - groupedLinks[A].length
27316
+ })
27317
+ linkGroups.forEach(function(gid) {
27318
+ var links = groupedLinks[gid]
27319
+ var junction = {
27320
+ _def: {defaults:{}},
27321
+ type: 'junction',
27322
+ z: RED.workspaces.active(),
27323
+ id: RED.nodes.id(),
27324
+ x: 0,
27325
+ y: 0,
27326
+ w: 0, h: 0,
27327
+ outputs: 1,
27328
+ inputs: 1,
27329
+ dirty: true
27330
+ }
27331
+ links = links.filter(function(l) { return !removedLinks.has(l) })
27332
+ if (links.length === 0) {
27333
+ return
27334
+ }
27335
+ let pointCount = 0
27336
+ links.forEach(function(l) {
27337
+ if (l._sliceLocation) {
27338
+ junction.x += l._sliceLocation.x
27339
+ junction.y += l._sliceLocation.y
27340
+ delete l._sliceLocation
27341
+ pointCount++
27342
+ } else {
27343
+ junction.x += l.source.x + l.source.w/2 + l.target.x - l.target.w/2
27344
+ junction.y += l.source.y + l.target.y
27345
+ pointCount += 2
27346
+ }
27347
+ })
27348
+ junction.x = Math.round(junction.x/pointCount)
27349
+ junction.y = Math.round(junction.y/pointCount)
27350
+ if (RED.view.snapGrid) {
27351
+ let gridSize = RED.view.gridSize()
27352
+ junction.x = (gridSize*Math.round(junction.x/gridSize));
27353
+ junction.y = (gridSize*Math.round(junction.y/gridSize));
27354
+ }
27355
+
27356
+ var nodeGroups = new Set()
27357
+
27358
+ RED.nodes.addJunction(junction)
27359
+ addedJunctions.push(junction)
27360
+ let newLink
27361
+ if (gid === links[0].source.id+":"+links[0].sourcePort) {
27362
+ newLink = {
27363
+ source: links[0].source,
27364
+ sourcePort: links[0].sourcePort,
27365
+ target: junction
27366
+ }
27367
+ } else {
27368
+ newLink = {
27369
+ source: junction,
27370
+ sourcePort: 0,
27371
+ target: links[0].target
27372
+ }
27373
+ }
27374
+ addedLinks.push(newLink)
27375
+ RED.nodes.addLink(newLink)
27376
+ links.forEach(function(l) {
27377
+ removedLinks.add(l)
27378
+ RED.nodes.removeLink(l)
27379
+ let newLink
27380
+ if (gid === l.target.id) {
27381
+ newLink = {
27382
+ source: l.source,
27383
+ sourcePort: l.sourcePort,
27384
+ target: junction
27385
+ }
27386
+ } else {
27387
+ newLink = {
27388
+ source: junction,
27389
+ sourcePort: 0,
27390
+ target: l.target
27391
+ }
27392
+ }
27393
+ addedLinks.push(newLink)
27394
+ RED.nodes.addLink(newLink)
27395
+ nodeGroups.add(l.source.g || "__NONE__")
27396
+ nodeGroups.add(l.target.g || "__NONE__")
27397
+ })
27398
+ if (nodeGroups.size === 1) {
27399
+ var group = nodeGroups.values().next().value
27400
+ if (group !== "__NONE__") {
27401
+ RED.group.addToGroup(RED.nodes.group(group), junction)
27402
+ }
27403
+ }
27404
+ })
27405
+ if (addedJunctions.length > 0) {
27406
+ RED.history.push({
27407
+ t: 'add',
27408
+ links: addedLinks,
27409
+ junctions: addedJunctions,
27410
+ removedLinks: Array.from(removedLinks)
27411
+ })
27412
+ RED.nodes.dirty(true)
27413
+ }
27414
+ RED.view.redraw(true);
27415
+ }
27416
+
26932
27417
  return {
26933
27418
  init: function() {
26934
27419
  RED.actions.add("core:show-selected-node-labels", function() { setSelectedNodeLabelState(true); })
@@ -26991,6 +27476,7 @@ RED.view.tools = (function() {
26991
27476
  RED.actions.add("core:wire-node-to-multiple", function() { wireNodeToMultiple() })
26992
27477
 
26993
27478
  RED.actions.add("core:split-wire-with-link-nodes", function () { splitWiresWithLinkNodes() });
27479
+ RED.actions.add("core:split-wires-with-junctions", function () { addJunctionsToWires() });
26994
27480
 
26995
27481
  RED.actions.add("core:generate-node-names", generateNodeNames )
26996
27482
 
@@ -27508,7 +27994,7 @@ RED.palette = (function() {
27508
27994
  }
27509
27995
 
27510
27996
  function escapeCategory(category) {
27511
- return category.replace(/[ /.]/g,"_");
27997
+ return category.replace(/[\x00-\x2c\x2e-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]/g,"_");
27512
27998
  }
27513
27999
  function addNodeType(nt,def) {
27514
28000
  if (getPaletteNode(nt).length) {
@@ -29286,10 +29772,8 @@ RED.sidebar.help = (function() {
29286
29772
  var helpSection;
29287
29773
  var panels;
29288
29774
  var panelRatio;
29289
- var helpTopics = [];
29290
29775
  var treeList;
29291
29776
  var tocPanel;
29292
- var helpIndex = {};
29293
29777
 
29294
29778
  function resizeStack() {
29295
29779
  var h = $(content).parent().height() - toolbar.outerHeight();
@@ -29363,7 +29847,10 @@ RED.sidebar.help = (function() {
29363
29847
  var pendingContentLoad;
29364
29848
  treeList.on('treelistselect', function(e,item) {
29365
29849
  pendingContentLoad = item;
29366
- if (item.nodeType) {
29850
+ if (item.tour) {
29851
+ RED.tourGuide.run(item.tour);
29852
+ }
29853
+ else if (item.nodeType) {
29367
29854
  showNodeTypeHelp(item.nodeType);
29368
29855
  } else if (item.content) {
29369
29856
  helpSection.empty();
@@ -29455,7 +29942,6 @@ RED.sidebar.help = (function() {
29455
29942
  }
29456
29943
 
29457
29944
  function refreshHelpIndex() {
29458
- helpTopics = [];
29459
29945
  var modules = RED.nodes.registry.getModuleList();
29460
29946
  var moduleNames = Object.keys(modules);
29461
29947
  moduleNames.sort();
@@ -29464,15 +29950,32 @@ RED.sidebar.help = (function() {
29464
29950
  label: RED._("sidebar.help.nodeHelp"),
29465
29951
  children: [],
29466
29952
  expanded: true
29467
- }
29953
+ };
29954
+ var tours = RED.tourGuide.list().map(function (item) {
29955
+ return {
29956
+ icon: "fa fa-play-circle-o",
29957
+ label: item.label,
29958
+ tour: item.path,
29959
+ };
29960
+ });
29468
29961
  var helpData = [
29469
29962
  {
29470
- id: 'changelog',
29471
- label: "Node-RED v"+RED.settings.version,
29472
- content: getChangelog
29963
+ label: "Node-RED",
29964
+ children: [
29965
+ {
29966
+ id: 'changelog',
29967
+ label: RED._("sidebar.help.changeLog"),
29968
+ content: getChangelog
29969
+ },
29970
+ {
29971
+ label: RED._("tourGuide.welcomeTours"),
29972
+ children: tours
29973
+ }
29974
+
29975
+ ]
29473
29976
  },
29474
29977
  nodeHelp
29475
- ]
29978
+ ];
29476
29979
  var subflows = RED.nodes.registry.getNodeTypes().filter(function(t) {return /subflow/.test(t)});
29477
29980
  if (subflows.length > 0) {
29478
29981
  nodeHelp.children.push({
@@ -33818,8 +34321,7 @@ RED.editor = (function() {
33818
34321
  if (!node._def.defaults || !node._def.defaults.hasOwnProperty("icon")) {
33819
34322
  var icon = $("#red-ui-editor-node-icon").val()||"";
33820
34323
  if (!this.isDefaultIcon) {
33821
- if ((icon !== node.icon) &&
33822
- (icon !== "")) {
34324
+ if ((node.icon && icon !== node.icon) || (!node.icon && icon !== "")) {
33823
34325
  editState.changes.icon = node.icon;
33824
34326
  node.icon = icon;
33825
34327
  editState.changed = true;
@@ -34472,7 +34974,6 @@ RED.editor = (function() {
34472
34974
  this.tabflowEditor = RED.editor.createEditor({
34473
34975
  id: 'node-input-info',
34474
34976
  mode: 'ace/mode/markdown',
34475
- stateId: options.stateId,
34476
34977
  value: ""
34477
34978
  });
34478
34979
 
@@ -35126,7 +35627,7 @@ RED.editor = (function() {
35126
35627
 
35127
35628
  const MONACO = "monaco";
35128
35629
  const ACE = "ace";
35129
- const defaultEditor = ACE;
35630
+ const defaultEditor = MONACO;
35130
35631
  const DEFAULT_SETTINGS = { lib: defaultEditor, options: {} };
35131
35632
  var selectedCodeEditor = null;
35132
35633
  var initialised = false;
@@ -35153,12 +35654,12 @@ RED.editor = (function() {
35153
35654
  }
35154
35655
 
35155
35656
  function create(options) {
35156
- //TODO: (quandry - for consideration)
35657
+ //TODO: (quandry - for consideration)
35157
35658
  // Below, I had to create a hidden element if options.id || options.element is not in the DOM
35158
- // I have seen 1 node calling `this.editor = RED.editor.createEditor()` with an
35659
+ // I have seen 1 node calling `this.editor = RED.editor.createEditor()` with an
35159
35660
  // invalid (non existing html element selector) (e.g. node-red-contrib-components does this)
35160
- // This causes monaco to throw an error when attempting to hook up its events to the dom & the rest of the 'oneditperapre'
35161
- // code is thus skipped.
35661
+ // This causes monaco to throw an error when attempting to hook up its events to the dom & the rest of the 'oneditperapre'
35662
+ // code is thus skipped.
35162
35663
  // In ACE mode, creating an ACE editor (with an invalid ID) allows the editor to be created (but obviously there is no UI)
35163
35664
  // Because one (or more) contrib nodes have left this bad code in place, how would we handle this?
35164
35665
  // For compatibility, I have decided to create a hidden element so that at least an editor is created & errors do not occur.
@@ -35184,7 +35685,7 @@ RED.editor = (function() {
35184
35685
  return this.editor.create(options);//fallback to ACE
35185
35686
  }
35186
35687
  }
35187
-
35688
+
35188
35689
  return {
35189
35690
  init: init,
35190
35691
  /**
@@ -35196,7 +35697,7 @@ RED.editor = (function() {
35196
35697
  },
35197
35698
  /**
35198
35699
  * Get user selected code editor
35199
- * @return {string} Returns
35700
+ * @return {string} Returns
35200
35701
  * @memberof RED.editor.codeEditor
35201
35702
  */
35202
35703
  get editor() {
@@ -35209,7 +35710,8 @@ RED.editor = (function() {
35209
35710
  */
35210
35711
  create: create
35211
35712
  }
35212
- })();;RED.editor.colorPicker = RED.colorPicker = (function() {
35713
+ })();
35714
+ ;RED.editor.colorPicker = RED.colorPicker = (function() {
35213
35715
 
35214
35716
  function create(options) {
35215
35717
  var color = options.value;
@@ -35461,8 +35963,12 @@ RED.editor = (function() {
35461
35963
  style: "width:100%",
35462
35964
  class: "node-input-env-value",
35463
35965
  type: "text",
35464
- }).attr("autocomplete","disable").appendTo(envRow)
35465
- valueField.typedInput({default:'str',types:isTemplateNode?DEFAULT_ENV_TYPE_LIST:DEFAULT_ENV_TYPE_LIST_INC_CRED});
35966
+ }).attr("autocomplete","disable").appendTo(envRow);
35967
+ var types = (opt.ui && opt.ui.opts && opt.ui.opts.types);
35968
+ if (!types) {
35969
+ types = isTemplateNode ? DEFAULT_ENV_TYPE_LIST : DEFAULT_ENV_TYPE_LIST_INC_CRED;
35970
+ }
35971
+ valueField.typedInput({default:'str',types:types});
35466
35972
  valueField.typedInput('type', opt.type);
35467
35973
  if (opt.type === "cred") {
35468
35974
  if (opt.value) {
@@ -35514,6 +36020,11 @@ RED.editor = (function() {
35514
36020
  }
35515
36021
  opt.ui.label = opt.ui.label || {};
35516
36022
  opt.ui.type = opt.ui.type || "input";
36023
+ if ((opt.ui.type === "cred") &&
36024
+ opt.ui.opts &&
36025
+ opt.ui.opts.types) {
36026
+ opt.ui.type = "input";
36027
+ }
35517
36028
 
35518
36029
  var uiRow = $('<div/>').appendTo(container).hide();
35519
36030
  // save current info for reverting on cancel
@@ -36920,8 +37431,8 @@ RED.editor = (function() {
36920
37431
  types:[
36921
37432
  'str','num','bool',
36922
37433
  {value:"null",label:RED._("common.type.null"),hasValue:false},
36923
- {value:"array",label:RED._("common.type.array"),hasValue:false,icon:"red/images/typedInput/json.png"},
36924
- {value:"object",label:RED._("common.type.object"),hasValue:false,icon:"red/images/typedInput/json.png"}
37434
+ {value:"array",label:RED._("common.type.array"),hasValue:false,icon:"red/images/typedInput/json.svg"},
37435
+ {value:"object",label:RED._("common.type.object"),hasValue:false,icon:"red/images/typedInput/json.svg"}
36925
37436
  ],
36926
37437
  default: valType
36927
37438
  });
@@ -37143,6 +37654,7 @@ RED.editor = (function() {
37143
37654
  var container = $("#red-ui-editor-type-json-tab-ui-container").css({"height":"100%"});
37144
37655
  var filterDepth = Infinity;
37145
37656
  var list = $('<div class="red-ui-debug-msg-payload red-ui-editor-type-json-editor">').appendTo(container).treeList({
37657
+ selectable: false,
37146
37658
  rootSortable: false,
37147
37659
  sortable: ".red-ui-editor-type-json-editor-item-handle",
37148
37660
  }).on("treelistchangeparent", function(event, evt) {
@@ -40390,6 +40902,7 @@ RED.clipboard = (function() {
40390
40902
  } else if (type === 'flow') {
40391
40903
  var activeWorkspace = RED.workspaces.active();
40392
40904
  nodes = RED.nodes.groups(activeWorkspace);
40905
+ nodes = nodes.concat(RED.nodes.junctions(activeWorkspace));
40393
40906
  nodes = nodes.concat(RED.nodes.filterNodes({z:activeWorkspace}));
40394
40907
  RED.nodes.eachConfig(function(n) {
40395
40908
  if (n.z === RED.workspaces.active() && n._def.hasUsers === false) {
@@ -41359,106 +41872,112 @@ RED.library = (function() {
41359
41872
  options.onconfirm(item);
41360
41873
  }
41361
41874
  });
41362
- var itemTools = $("<div>").css({position: "absolute",bottom:"6px",right:"8px"});
41363
- var menuButton = $('<button class="red-ui-button red-ui-button-small" type="button"><i class="fa fa-ellipsis-h"></i></button>')
41364
- .on("click", function(evt) {
41365
- evt.preventDefault();
41366
- evt.stopPropagation();
41367
- var elementPos = menuButton.offset();
41368
-
41369
- var menuOptionMenu = RED.menu.init({id:"red-ui-library-browser-menu",
41370
- options: [
41371
- {id:"red-ui-library-browser-menu-addFolder",label:RED._("library.newFolder"), onselect: function() {
41372
- var defaultFolderName = "new-folder";
41373
- var defaultFolderNameMatches = {};
41374
-
41375
- var selected = dirList.treeList('selected');
41376
- if (!selected.children) {
41377
- selected = selected.parent;
41378
- }
41379
- var complete = function() {
41380
- selected.children.forEach(function(c) {
41381
- if (/^new-folder/.test(c.label)) {
41382
- defaultFolderNameMatches[c.label] = true
41383
- }
41384
- });
41385
- var folderIndex = 2;
41386
- while(defaultFolderNameMatches[defaultFolderName]) {
41387
- defaultFolderName = "new-folder-"+(folderIndex++)
41388
- }
41389
-
41390
- selected.treeList.expand();
41391
- var input = $('<input type="text" class="red-ui-treeList-input">').val(defaultFolderName);
41392
- var newItem = {
41393
- icon: "fa fa-folder-o",
41394
- children:[],
41395
- path: selected.path,
41396
- element: input
41397
- }
41398
- var confirmAdd = function() {
41399
- var val = input.val().trim();
41400
- if (val === "") {
41401
- cancelAdd();
41402
- return;
41403
- } else {
41404
- for (var i=0;i<selected.children.length;i++) {
41405
- if (selected.children[i].label === val) {
41406
- cancelAdd();
41407
- return;
41408
- }
41409
- }
41410
- }
41411
- newItem.treeList.remove();
41412
- var finalItem = {
41413
- library: selected.library,
41414
- type: selected.type,
41415
- icon: "fa fa-folder",
41416
- children:[],
41417
- label: val,
41418
- path: newItem.path+val+"/"
41419
- }
41420
- selected.treeList.addChild(finalItem,true);
41421
- }
41422
- var cancelAdd = function() {
41423
- newItem.treeList.remove();
41424
- }
41425
- input.on('keydown', function(evt) {
41426
- evt.stopPropagation();
41427
- if (evt.keyCode === 13) {
41428
- confirmAdd();
41429
- } else if (evt.keyCode === 27) {
41430
- cancelAdd();
41431
- }
41432
- })
41433
- input.on("blur", function() {
41434
- confirmAdd();
41435
- })
41436
- selected.treeList.addChild(newItem);
41437
- setTimeout(function() {
41438
- input.trigger("focus");
41439
- input.select();
41440
- },400);
41441
- }
41442
- selected.treeList.expand(complete);
41443
-
41444
- } },
41445
- // null,
41446
- // {id:"red-ui-library-browser-menu-rename",label:"Rename", onselect: function() {} },
41447
- // {id:"red-ui-library-browser-menu-delete",label:"Delete", onselect: function() {} }
41448
- ]
41449
- }).on('mouseleave', function(){ $(this).remove(); dirList.focus() })
41450
- .on('mouseup', function() { var self = $(this);self.hide(); dirList.focus(); setTimeout(function() { self.remove() },100)})
41451
- .appendTo("body");
41452
- menuOptionMenu.css({
41453
- position: "absolute",
41454
- top: elementPos.top+"px",
41455
- left: (elementPos.left - menuOptionMenu.width() + 20)+"px"
41456
- }).show();
41457
-
41458
- }).appendTo(itemTools);
41875
+ var itemTools = null;
41459
41876
  if (options.folderTools) {
41460
41877
  dirList.on('treelistselect', function(event, item) {
41461
41878
  if (item.writable !== false && item.treeList) {
41879
+ if (itemTools) {
41880
+ itemTools.remove();
41881
+ }
41882
+ itemTools = $("<div>").css({position: "absolute",bottom:"6px",right:"8px"});
41883
+ var menuButton = $('<button class="red-ui-button red-ui-button-small" type="button"><i class="fa fa-ellipsis-h"></i></button>')
41884
+ .on("click", function(evt) {
41885
+ evt.preventDefault();
41886
+ evt.stopPropagation();
41887
+ var elementPos = menuButton.offset();
41888
+
41889
+ var menuOptionMenu
41890
+ = RED.menu.init({id:"red-ui-library-browser-menu",
41891
+ options: [
41892
+ {id:"red-ui-library-browser-menu-addFolder",label:RED._("library.newFolder"), onselect: function() {
41893
+ var defaultFolderName = "new-folder";
41894
+ var defaultFolderNameMatches = {};
41895
+
41896
+ var selected = dirList.treeList('selected');
41897
+ if (!selected.children) {
41898
+ selected = selected.parent;
41899
+ }
41900
+ var complete = function() {
41901
+ selected.children.forEach(function(c) {
41902
+ if (/^new-folder/.test(c.label)) {
41903
+ defaultFolderNameMatches[c.label] = true
41904
+ }
41905
+ });
41906
+ var folderIndex = 2;
41907
+ while(defaultFolderNameMatches[defaultFolderName]) {
41908
+ defaultFolderName = "new-folder-"+(folderIndex++)
41909
+ }
41910
+
41911
+ selected.treeList.expand();
41912
+ var input = $('<input type="text" class="red-ui-treeList-input">').val(defaultFolderName);
41913
+ var newItem = {
41914
+ icon: "fa fa-folder-o",
41915
+ children:[],
41916
+ path: selected.path,
41917
+ element: input
41918
+ }
41919
+ var confirmAdd = function() {
41920
+ var val = input.val().trim();
41921
+ if (val === "") {
41922
+ cancelAdd();
41923
+ return;
41924
+ } else {
41925
+ for (var i=0;i<selected.children.length;i++) {
41926
+ if (selected.children[i].label === val) {
41927
+ cancelAdd();
41928
+ return;
41929
+ }
41930
+ }
41931
+ }
41932
+ newItem.treeList.remove();
41933
+ var finalItem = {
41934
+ library: selected.library,
41935
+ type: selected.type,
41936
+ icon: "fa fa-folder",
41937
+ children:[],
41938
+ label: val,
41939
+ path: newItem.path+val+"/"
41940
+ }
41941
+ selected.treeList.addChild(finalItem,true);
41942
+ }
41943
+ var cancelAdd = function() {
41944
+ newItem.treeList.remove();
41945
+ }
41946
+ input.on('keydown', function(evt) {
41947
+ evt.stopPropagation();
41948
+ if (evt.keyCode === 13) {
41949
+ confirmAdd();
41950
+ } else if (evt.keyCode === 27) {
41951
+ cancelAdd();
41952
+ }
41953
+ })
41954
+ input.on("blur", function() {
41955
+ confirmAdd();
41956
+ })
41957
+ selected.treeList.addChild(newItem);
41958
+ setTimeout(function() {
41959
+ input.trigger("focus");
41960
+ input.select();
41961
+ },400);
41962
+ }
41963
+ selected.treeList.expand(complete);
41964
+
41965
+ } },
41966
+ // null,
41967
+ // {id:"red-ui-library-browser-menu-rename",label:"Rename", onselect: function() {} },
41968
+ // {id:"red-ui-library-browser-menu-delete",label:"Delete", onselect: function() {} }
41969
+ ]
41970
+ }).on('mouseleave', function(){ $(this).remove(); dirList.focus() })
41971
+ .on('mouseup', function() { var self = $(this);self.hide(); dirList.focus(); setTimeout(function() { self.remove() },100)})
41972
+ .appendTo("body");
41973
+ menuOptionMenu.css({
41974
+ position: "absolute",
41975
+ top: elementPos.top+"px",
41976
+ left: (elementPos.left - menuOptionMenu.width() + 20)+"px"
41977
+ }).show();
41978
+
41979
+ }).appendTo(itemTools);
41980
+
41462
41981
  itemTools.appendTo(item.treeList.label);
41463
41982
  }
41464
41983
  });
@@ -42300,7 +42819,6 @@ RED.search = (function() {
42300
42819
 
42301
42820
  function search(val) {
42302
42821
  var results = [];
42303
- var keys = [];
42304
42822
  var typeFilter;
42305
42823
  var m = /(?:^| )type:([^ ]+)/.exec(val);
42306
42824
  if (m) {
@@ -42314,19 +42832,24 @@ RED.search = (function() {
42314
42832
  val = extractFlag(val,"subflow",flags);
42315
42833
  val = extractFlag(val,"hidden",flags);
42316
42834
  val = extractFlag(val,"modified",flags);
42317
- // uses:<node-id>
42318
- val = extractValue(val,"uses",flags);
42319
-
42320
- var hasFlags = Object.keys(flags).length > 0;
42321
-
42835
+ val = extractValue(val,"flow",flags);// flow:active or flow:<flow-id>
42836
+ val = extractValue(val,"uses",flags);// uses:<node-id>
42322
42837
  val = val.trim();
42323
-
42838
+ var hasFlags = Object.keys(flags).length > 0;
42839
+ if (flags.flow && flags.flow.indexOf("current") >= 0) {
42840
+ let idx = flags.flow.indexOf("current");
42841
+ flags.flow[idx] = RED.workspaces.active();//convert active to flow ID
42842
+ }
42843
+ if (flags.flow && flags.flow.length) {
42844
+ flags.flow = [ ...new Set(flags.flow) ]; //deduplicate
42845
+ }
42324
42846
  if (val.length > 0 || typeFilter || hasFlags) {
42325
42847
  val = val.toLowerCase();
42326
42848
  var i;
42327
42849
  var j;
42328
42850
  var list = [];
42329
42851
  var nodes = {};
42852
+ let keys = [];
42330
42853
  if (flags.uses) {
42331
42854
  keys = flags.uses;
42332
42855
  } else {
@@ -42336,7 +42859,7 @@ RED.search = (function() {
42336
42859
  var key = keys[i];
42337
42860
  var kpos = keys[i].indexOf(val);
42338
42861
  if (kpos > -1) {
42339
- var ids = Object.keys(index[key]);
42862
+ var ids = Object.keys(index[key]||{});
42340
42863
  for (j=0;j<ids.length;j++) {
42341
42864
  var node = index[key][ids[j]];
42342
42865
  var isConfigNode = node.node._def.category === "config" && node.node.type !== 'group';
@@ -42380,6 +42903,11 @@ RED.search = (function() {
42380
42903
  continue;
42381
42904
  }
42382
42905
  }
42906
+ if (flags.hasOwnProperty("flow")) {
42907
+ if (flags.flow.indexOf(node.node.z || node.node.id) < 0) {
42908
+ continue;
42909
+ }
42910
+ }
42383
42911
  if (!typeFilter || node.node.type === typeFilter) {
42384
42912
  nodes[node.node.id] = nodes[node.node.id] = {
42385
42913
  node: node.node,
@@ -42447,7 +42975,7 @@ RED.search = (function() {
42447
42975
  }
42448
42976
  currentResults = search(value);
42449
42977
  if (currentResults.length > 0) {
42450
- for (i=0;i<Math.min(currentResults.length,25);i++) {
42978
+ for (let i=0;i<Math.min(currentResults.length,25);i++) {
42451
42979
  searchResults.editableList('addItem',currentResults[i])
42452
42980
  }
42453
42981
  if (currentResults.length > 25) {
@@ -42784,8 +43312,8 @@ RED.search = (function() {
42784
43312
  {label:RED._("search.options.uknownNodes"), value: "type:unknown"},
42785
43313
  {label:RED._("search.options.unusedSubflows"), value:"is:subflow is:unused"},
42786
43314
  {label:RED._("search.options.hiddenFlows"), value:"is:hidden"},
43315
+ {label:RED._("search.options.thisFlow"), value:"flow:current"},
42787
43316
  ]
42788
-
42789
43317
  }
42790
43318
 
42791
43319
  function init() {
@@ -42847,6 +43375,182 @@ RED.search = (function() {
42847
43375
  };
42848
43376
 
42849
43377
  })();
43378
+ ;RED.contextMenu = (function() {
43379
+
43380
+ let menu;
43381
+ function createMenu() {
43382
+ // menu = RED.popover.menu({
43383
+ // options: [
43384
+ // {
43385
+ // label: 'delete selection',
43386
+ // onselect: function() {
43387
+ // RED.actions.invoke('core:delete-selection')
43388
+ // RED.view.focus()
43389
+ // }
43390
+ // },
43391
+ // { label: 'world' }
43392
+ // ],
43393
+ // width: 200,
43394
+ // })
43395
+
43396
+
43397
+
43398
+
43399
+ }
43400
+
43401
+ function disposeMenu() {
43402
+ $(document).off("mousedown.red-ui-workspace-context-menu");
43403
+ if (menu) {
43404
+ menu.remove();
43405
+ }
43406
+ menu = null;
43407
+ }
43408
+ function show(options) {
43409
+ if (menu) {
43410
+ menu.remove()
43411
+ }
43412
+
43413
+ const selection = RED.view.selection()
43414
+ const hasSelection = (selection.nodes && selection.nodes.length > 0);
43415
+ const hasMultipleSelection = hasSelection && selection.nodes.length > 1;
43416
+ const hasLinks = selection.links && selection.links.length > 0;
43417
+ const isSingleLink = !hasSelection && hasLinks && selection.links.length === 1
43418
+ const isMultipleLinks = !hasSelection && hasLinks && selection.links.length > 1
43419
+ const canDelete = hasSelection || hasLinks
43420
+ const isGroup = hasSelection && selection.nodes.length === 1 && selection.nodes[0].type === 'group'
43421
+
43422
+ const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g
43423
+
43424
+
43425
+ const menuItems = [
43426
+ { onselect: 'core:show-action-list', onpostselect: function() {} },
43427
+ {
43428
+ label: RED._("contextMenu.insert"),
43429
+ options: [
43430
+ {
43431
+ label: RED._("contextMenu.node"),
43432
+ onselect: function() {
43433
+ RED.view.showQuickAddDialog({
43434
+ position: [ options.x - offset.left, options.y - offset.top ],
43435
+ touchTrigger: true,
43436
+ splice: isSingleLink?selection.links[0]:undefined,
43437
+ // spliceMultiple: isMultipleLinks
43438
+ })
43439
+ }
43440
+ },
43441
+ {
43442
+ label: RED._("contextMenu.junction"),
43443
+ onselect: 'core:split-wires-with-junctions',
43444
+ disabled: hasSelection || !hasLinks
43445
+ },
43446
+ {
43447
+ label: RED._("contextMenu.linkNodes"),
43448
+ onselect: 'core:split-wire-with-link-nodes',
43449
+ disabled: hasSelection || !hasLinks
43450
+ }
43451
+ ]
43452
+
43453
+
43454
+
43455
+ }
43456
+ ]
43457
+ // menuItems.push(
43458
+ // {
43459
+ // label: (isSingleLink || isMultipleLinks)?'Insert into wire...':'Add node...',
43460
+ // onselect: function() {
43461
+ // RED.view.showQuickAddDialog({
43462
+ // position: [ options.x - offset.left, options.y - offset.top ],
43463
+ // touchTrigger: true,
43464
+ // splice: isSingleLink?selection.links[0]:undefined,
43465
+ // spliceMultiple: isMultipleLinks
43466
+ // })
43467
+ // }
43468
+ // },
43469
+ // )
43470
+ // if (hasLinks && !hasSelection) {
43471
+ // menuItems.push({ onselect: 'core:split-wires-with-junctions', label: 'Insert junction'})
43472
+ // }
43473
+ menuItems.push(
43474
+ null,
43475
+ { onselect: 'core:undo', disabled: RED.history.list().length === 0 },
43476
+ { onselect: 'core:redo', disabled: RED.history.listRedo().length === 0 },
43477
+ null,
43478
+ { onselect: 'core:cut-selection-to-internal-clipboard', label: RED._("keyboard.cutNode"), disabled: !hasSelection},
43479
+ { onselect: 'core:copy-selection-to-internal-clipboard', label: RED._("keyboard.copyNode"), disabled: !hasSelection },
43480
+ { onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !RED.view.clipboard() },
43481
+ { onselect: 'core:delete-selection', disabled: !canDelete },
43482
+ { onselect: 'core:show-export-dialog', label: RED._("menu.label.export") },
43483
+ { onselect: 'core:select-all-nodes' }
43484
+ )
43485
+
43486
+ if (hasSelection) {
43487
+ menuItems.push(
43488
+ null,
43489
+ isGroup ?
43490
+ { onselect: 'core:ungroup-selection', disabled: !isGroup }
43491
+ : { onselect: 'core:group-selection', disabled: !hasSelection }
43492
+ )
43493
+ if (canRemoveFromGroup) {
43494
+ menuItems.push({ onselect: 'core:remove-selection-from-group', label: RED._("menu.label.groupRemoveSelection") })
43495
+ }
43496
+
43497
+ }
43498
+ const offset = $("#red-ui-workspace-chart").offset()
43499
+ menu = RED.menu.init({
43500
+ direction: 'right',
43501
+ onpreselect: function() {
43502
+ disposeMenu()
43503
+ },
43504
+ onpostselect: function() {
43505
+ RED.view.focus()
43506
+ },
43507
+ options: menuItems
43508
+ });
43509
+
43510
+ menu.attr("id","red-ui-workspace-context-menu");
43511
+ menu.css({
43512
+ position: "absolute"
43513
+ })
43514
+ menu.appendTo("body");
43515
+
43516
+ // TODO: prevent the menu from overflowing the window.
43517
+
43518
+ var top = options.y
43519
+ var left = options.x
43520
+
43521
+ if (top+menu.height()-$(document).scrollTop() > $(window).height()) {
43522
+ top -= (top+menu.height())-$(window).height() + 22;
43523
+ }
43524
+ if (left+menu.width()-$(document).scrollLeft() > $(window).width()) {
43525
+ left -= (left+menu.width())-$(window).width() + 18;
43526
+ }
43527
+ menu.css({
43528
+ top: top+"px",
43529
+ left: left+"px"
43530
+ })
43531
+ $(".red-ui-menu.red-ui-menu-dropdown").hide();
43532
+ $(document).on("mousedown.red-ui-workspace-context-menu", function(evt) {
43533
+ if (menu && menu[0].contains(evt.target)) {
43534
+ return
43535
+ }
43536
+ disposeMenu()
43537
+ });
43538
+ menu.show();
43539
+
43540
+ // menu.show({
43541
+ // target: $('#red-ui-main-container'),
43542
+ // x: options.x,
43543
+ // y: options.y
43544
+ // })
43545
+
43546
+
43547
+ }
43548
+
43549
+ return {
43550
+ show: show,
43551
+ hide: disposeMenu
43552
+ }
43553
+ })()
42850
43554
  ;/**
42851
43555
  * Copyright JS Foundation and other contributors, http://js.foundation
42852
43556
  *
@@ -43185,7 +43889,9 @@ RED.actionList = (function() {
43185
43889
  var index = Math.max(0,selected);
43186
43890
  if (index < children.length) {
43187
43891
  var n = $(children[index]).find(".red-ui-editableList-item-content").data('data');
43188
- typesUsed[n.type] = Date.now();
43892
+ if (!/^_action_:/.test(n.type)) {
43893
+ typesUsed[n.type] = Date.now();
43894
+ }
43189
43895
  if (n.def.outputs === 0) {
43190
43896
  confirm(n);
43191
43897
  } else {
@@ -43254,6 +43960,8 @@ RED.actionList = (function() {
43254
43960
  var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).appendTo(div);
43255
43961
  if (object.type === "junction") {
43256
43962
  nodeDiv.addClass("red-ui-palette-icon-junction");
43963
+ } else if (/^_action_:/.test(object.type)) {
43964
+ nodeDiv.addClass("red-ui-palette-icon-junction")
43257
43965
  } else {
43258
43966
  var colour = RED.utils.getNodeColor(object.type,def);
43259
43967
  nodeDiv.css('backgroundColor',colour);
@@ -43263,11 +43971,14 @@ RED.actionList = (function() {
43263
43971
  var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
43264
43972
  RED.utils.createIconElement(icon_url, iconContainer, false);
43265
43973
 
43266
- if (object.type !== "junction" && def.inputs > 0) {
43267
- $('<div/>',{class:"red-ui-search-result-node-port"}).appendTo(nodeDiv);
43268
- }
43269
- if (object.type !== "junction" && def.outputs > 0) {
43270
- $('<div/>',{class:"red-ui-search-result-node-port red-ui-search-result-node-output"}).appendTo(nodeDiv);
43974
+
43975
+ if (!/^_action_:/.test(object.type) && object.type !== "junction") {
43976
+ if (def.inputs > 0) {
43977
+ $('<div/>',{class:"red-ui-search-result-node-port"}).appendTo(nodeDiv);
43978
+ }
43979
+ if (def.outputs > 0) {
43980
+ $('<div/>',{class:"red-ui-search-result-node-port red-ui-search-result-node-output"}).appendTo(nodeDiv);
43981
+ }
43271
43982
  }
43272
43983
 
43273
43984
  var contentDiv = $('<div>',{class:"red-ui-search-result-description"}).appendTo(div);
@@ -43288,7 +43999,9 @@ RED.actionList = (function() {
43288
43999
  }
43289
44000
  function confirm(def) {
43290
44001
  hide();
43291
- typesUsed[def.type] = Date.now();
44002
+ if (!/^_action_:/.test(def.type)) {
44003
+ typesUsed[def.type] = Date.now();
44004
+ }
43292
44005
  addCallback(def.type);
43293
44006
  }
43294
44007
 
@@ -43397,6 +44110,7 @@ RED.actionList = (function() {
43397
44110
  function applyFilter(filter,type,def) {
43398
44111
  return !filter ||
43399
44112
  (
44113
+ (!filter.spliceMultiple) &&
43400
44114
  (!filter.type || type === filter.type) &&
43401
44115
  (!filter.input || type === 'junction' || def.inputs > 0) &&
43402
44116
  (!filter.output || type === 'junction' || def.outputs > 0)
@@ -43411,6 +44125,13 @@ RED.actionList = (function() {
43411
44125
  'inject','debug','function','change','switch','junction'
43412
44126
  ].filter(function(t) { return applyFilter(opts.filter,t,RED.nodes.getType(t)); });
43413
44127
 
44128
+ // if (opts.filter && opts.filter.input && opts.filter.output && !opts.filter.type) {
44129
+ // if (opts.filter.spliceMultiple) {
44130
+ // common.push('_action_:core:split-wires-with-junctions')
44131
+ // }
44132
+ // common.push('_action_:core:split-wire-with-link-nodes')
44133
+ // }
44134
+
43414
44135
  var recentlyUsed = Object.keys(typesUsed);
43415
44136
  recentlyUsed.sort(function(a,b) {
43416
44137
  return typesUsed[b]-typesUsed[a];
@@ -43435,6 +44156,8 @@ RED.actionList = (function() {
43435
44156
  var itemDef = RED.nodes.getType(common[i]);
43436
44157
  if (common[i] === 'junction') {
43437
44158
  itemDef = { inputs:1, outputs: 1, label: 'junction', type: 'junction'}
44159
+ } else if (/^_action_:/.test(common[i]) ) {
44160
+ itemDef = { inputs:1, outputs: 1, label: common[i], type: common[i]}
43438
44161
  }
43439
44162
  if (itemDef) {
43440
44163
  item = {
@@ -44088,6 +44811,14 @@ RED.subflow = (function() {
44088
44811
  return x;
44089
44812
  }
44090
44813
 
44814
+ function nodeOrJunction(id) {
44815
+ var node = RED.nodes.node(id);
44816
+ if (node) {
44817
+ return node;
44818
+ }
44819
+ return RED.nodes.junction(id);
44820
+ }
44821
+
44091
44822
  function convertToSubflow() {
44092
44823
  var selection = RED.view.selection();
44093
44824
  if (!selection.nodes) {
@@ -44276,14 +45007,15 @@ RED.subflow = (function() {
44276
45007
 
44277
45008
  subflow.in.forEach(function(input) {
44278
45009
  input.wires.forEach(function(wire) {
44279
- var link = {source: input, sourcePort: 0, target: RED.nodes.node(wire.id) }
45010
+ var link = {source: input, sourcePort: 0, target: nodeOrJunction(wire.id) }
44280
45011
  new_links.push(link);
44281
45012
  RED.nodes.addLink(link);
44282
45013
  });
44283
45014
  });
45015
+
44284
45016
  subflow.out.forEach(function(output,i) {
44285
45017
  output.wires.forEach(function(wire) {
44286
- var link = {source: RED.nodes.node(wire.id), sourcePort: wire.port , target: output }
45018
+ var link = {source: nodeOrJunction(wire.id), sourcePort: wire.port , target: output }
44287
45019
  new_links.push(link);
44288
45020
  RED.nodes.addLink(link);
44289
45021
  });
@@ -44299,7 +45031,7 @@ RED.subflow = (function() {
44299
45031
  n.links = n.links.filter(function(id) {
44300
45032
  var isLocalLink = nodes.hasOwnProperty(id);
44301
45033
  if (!isLocalLink) {
44302
- var otherNode = RED.nodes.node(id);
45034
+ var otherNode = nodeOrJunction(id);
44303
45035
  if (otherNode && otherNode.links) {
44304
45036
  var i = otherNode.links.indexOf(n.id);
44305
45037
  if (i > -1) {
@@ -44315,7 +45047,6 @@ RED.subflow = (function() {
44315
45047
  RED.nodes.moveNodeToTab(n, subflow.id);
44316
45048
  }
44317
45049
 
44318
-
44319
45050
  var historyEvent = {
44320
45051
  t:'createSubflow',
44321
45052
  nodes:[subflowInstance.id],
@@ -45123,9 +45854,6 @@ RED.group = (function() {
45123
45854
  groups: [ ],
45124
45855
  dirty: RED.nodes.dirty()
45125
45856
  }
45126
- RED.history.push(historyEvent);
45127
-
45128
-
45129
45857
  groups.forEach(function(g) {
45130
45858
  newSelection = newSelection.concat(ungroup(g))
45131
45859
  historyEvent.groups.push(g);
@@ -52395,9 +53123,30 @@ RED.touch.radialMenu = (function() {
52395
53123
  })
52396
53124
  }
52397
53125
 
53126
+ function listTour() {
53127
+ return [
53128
+ {
53129
+ id: "3_0",
53130
+ label: "3.0.0-beta.4",
53131
+ path: "./tours/welcome.js"
53132
+ },
53133
+ {
53134
+ id: "2_2",
53135
+ label: "2.2.0",
53136
+ path: "./tours/2.2/welcome.js"
53137
+ },
53138
+ {
53139
+ id: "2_1",
53140
+ label: "2.1.0",
53141
+ path: "./tours/2.1/welcome.js"
53142
+ }
53143
+ ];
53144
+ }
53145
+
52398
53146
  return {
52399
53147
  load: loadTour,
52400
53148
  run: run,
53149
+ list: listTour,
52401
53150
  reset: function() {
52402
53151
  RED.settings.set("editor.tours.welcome",'');
52403
53152
  }