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

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.
Files changed (145) hide show
  1. package/locales/de/editor.json +14 -11
  2. package/locales/en-US/editor.json +44 -14
  3. package/locales/ja/editor.json +50 -17
  4. package/locales/ko/editor.json +5 -2
  5. package/locales/ru/editor.json +14 -11
  6. package/locales/zh-CN/editor.json +14 -11
  7. package/locales/zh-TW/editor.json +14 -11
  8. package/package.json +1 -1
  9. package/public/red/about +109 -0
  10. package/public/red/images/grip-horizontal.svg +1 -0
  11. package/public/red/images/grip.svg +1 -0
  12. package/public/red/keymap.json +5 -1
  13. package/public/red/red.js +2091 -614
  14. package/public/red/red.min.js +2 -2
  15. package/public/red/style.min.css +2 -2
  16. package/public/red/tours/images/continuous-search.png +0 -0
  17. package/public/red/tours/images/debug-path-tooltip.png +0 -0
  18. package/public/red/tours/images/junction-quick-add.png +0 -0
  19. package/public/red/tours/images/junction-slice.gif +0 -0
  20. package/public/red/tours/images/split-wire-with-links.gif +0 -0
  21. package/public/red/tours/welcome.js +65 -99
  22. package/public/types/node/assert.d.ts +5 -1
  23. package/public/types/node/async_hooks.d.ts +4 -1
  24. package/public/types/node/buffer.d.ts +68 -1
  25. package/public/types/node/child_process.d.ts +4 -1
  26. package/public/types/node/cluster.d.ts +4 -1
  27. package/public/types/node/console.d.ts +10 -1
  28. package/public/types/node/crypto.d.ts +6 -3
  29. package/public/types/node/dgram.d.ts +12 -9
  30. package/public/types/node/dns.d.ts +8 -1
  31. package/public/types/node/domain.d.ts +4 -1
  32. package/public/types/node/events.d.ts +5 -1
  33. package/public/types/node/fs.d.ts +10 -7
  34. package/public/types/node/globals.d.ts +140 -7
  35. package/public/types/node/http.d.ts +10 -4
  36. package/public/types/node/http2.d.ts +8 -5
  37. package/public/types/node/https.d.ts +4 -1
  38. package/public/types/node/module.d.ts +5 -1
  39. package/public/types/node/net.d.ts +7 -4
  40. package/public/types/node/os.d.ts +4 -1
  41. package/public/types/node/path.d.ts +5 -1
  42. package/public/types/node/perf_hooks.d.ts +4 -1
  43. package/public/types/node/process.d.ts +6 -2
  44. package/public/types/node/querystring.d.ts +4 -1
  45. package/public/types/node/readline.d.ts +5 -1
  46. package/public/types/node/stream.d.ts +13 -9
  47. package/public/types/node/string_decoder.d.ts +4 -1
  48. package/public/types/node/timers.d.ts +4 -1
  49. package/public/types/node/tls.d.ts +6 -2
  50. package/public/types/node/trace_events.d.ts +4 -1
  51. package/public/types/node/tty.d.ts +4 -1
  52. package/public/types/node/url.d.ts +35 -2
  53. package/public/types/node/util.d.ts +11 -2
  54. package/public/types/node/v8.d.ts +4 -1
  55. package/public/types/node/vm.d.ts +4 -1
  56. package/public/types/node/wasi.d.ts +4 -1
  57. package/public/types/node/worker_threads.d.ts +4 -1
  58. package/public/types/node/zlib.d.ts +4 -1
  59. package/public/types/node-red/func.d.ts +16 -2
  60. package/public/vendor/ace/worker-jsonata.js +1 -1
  61. package/public/vendor/monaco/dist/ThirdPartyNotices.txt +382 -217
  62. package/public/vendor/monaco/dist/ade705761eb7e702770d.ttf +0 -0
  63. package/public/vendor/monaco/dist/css.worker.js +2 -1
  64. package/public/vendor/monaco/dist/css.worker.js.LICENSE.txt +6 -0
  65. package/public/vendor/monaco/dist/editor.js +2 -5
  66. package/public/vendor/monaco/dist/editor.js.LICENSE.txt +8 -0
  67. package/public/vendor/monaco/dist/editor.worker.js +1 -1
  68. package/public/vendor/monaco/dist/html.worker.js +2 -1
  69. package/public/vendor/monaco/dist/html.worker.js.LICENSE.txt +6 -0
  70. package/public/vendor/monaco/dist/json.worker.js +2 -1
  71. package/public/vendor/monaco/dist/json.worker.js.LICENSE.txt +6 -0
  72. package/public/vendor/monaco/dist/locale/cs.js +1550 -1445
  73. package/public/vendor/monaco/dist/locale/de.js +1550 -1445
  74. package/public/vendor/monaco/dist/locale/es.js +1550 -1445
  75. package/public/vendor/monaco/dist/locale/fr.js +1550 -1445
  76. package/public/vendor/monaco/dist/locale/it.js +1550 -1445
  77. package/public/vendor/monaco/dist/locale/ja.js +1550 -1445
  78. package/public/vendor/monaco/dist/locale/ko.js +1550 -1445
  79. package/public/vendor/monaco/dist/locale/pl.js +1550 -1445
  80. package/public/vendor/monaco/dist/locale/pt-br.js +1550 -1445
  81. package/public/vendor/monaco/dist/locale/qps-ploc.js +1550 -1445
  82. package/public/vendor/monaco/dist/locale/ru.js +1550 -1445
  83. package/public/vendor/monaco/dist/locale/tr.js +1550 -1445
  84. package/public/vendor/monaco/dist/locale/zh-hans.js +1550 -1445
  85. package/public/vendor/monaco/dist/locale/zh-hant.js +1550 -1445
  86. package/public/vendor/monaco/dist/theme/ace.json +197 -0
  87. package/public/vendor/monaco/dist/theme/active4d.json +4 -0
  88. package/public/vendor/monaco/dist/theme/all-hallows-eve.json +4 -0
  89. package/public/vendor/monaco/dist/theme/amy.json +4 -0
  90. package/public/vendor/monaco/dist/theme/birds-of-paradise.json +4 -0
  91. package/public/vendor/monaco/dist/theme/blackboard.json +4 -0
  92. package/public/vendor/monaco/dist/theme/brilliance-black.json +4 -0
  93. package/public/vendor/monaco/dist/theme/brilliance-dull.json +4 -0
  94. package/public/vendor/monaco/dist/theme/chrome-devtools.json +4 -0
  95. package/public/vendor/monaco/dist/theme/clouds-midnight.json +4 -0
  96. package/public/vendor/monaco/dist/theme/clouds.json +4 -0
  97. package/public/vendor/monaco/dist/theme/cobalt.json +4 -0
  98. package/public/vendor/monaco/dist/theme/cobalt2.json +859 -0
  99. package/public/vendor/monaco/dist/theme/dawn.json +4 -0
  100. package/public/vendor/monaco/dist/theme/dracula.json +208 -0
  101. package/public/vendor/monaco/dist/theme/dreamweaver.json +4 -0
  102. package/public/vendor/monaco/dist/theme/eiffel.json +4 -0
  103. package/public/vendor/monaco/dist/theme/espresso-libre.json +4 -0
  104. package/public/vendor/monaco/dist/theme/github.json +4 -0
  105. package/public/vendor/monaco/dist/theme/idle.json +4 -0
  106. package/public/vendor/monaco/dist/theme/idlefingers.json +4 -0
  107. package/public/vendor/monaco/dist/theme/iplastic.json +4 -0
  108. package/public/vendor/monaco/dist/theme/katzenmilch.json +4 -0
  109. package/public/vendor/monaco/dist/theme/krtheme.json +4 -0
  110. package/public/vendor/monaco/dist/theme/kuroir-theme.json +4 -0
  111. package/public/vendor/monaco/dist/theme/lazy.json +4 -0
  112. package/public/vendor/monaco/dist/theme/magicwb-amiga.json +4 -0
  113. package/public/vendor/monaco/dist/theme/merbivore-soft.json +4 -0
  114. package/public/vendor/monaco/dist/theme/merbivore.json +4 -0
  115. package/public/vendor/monaco/dist/theme/monoindustrial.json +4 -0
  116. package/public/vendor/monaco/dist/theme/monokai-bright.json +4 -0
  117. package/public/vendor/monaco/dist/theme/monokai.json +4 -0
  118. package/public/vendor/monaco/dist/theme/night-owl.json +4 -0
  119. package/public/vendor/monaco/dist/theme/oceanic-next.json +4 -0
  120. package/public/vendor/monaco/dist/theme/pastels-on-dark.json +4 -0
  121. package/public/vendor/monaco/dist/theme/slush-and-poppies.json +4 -0
  122. package/public/vendor/monaco/dist/theme/solarized-dark.json +4 -0
  123. package/public/vendor/monaco/dist/theme/solarized-light.json +4 -0
  124. package/public/vendor/monaco/dist/theme/spacecadet.json +4 -0
  125. package/public/vendor/monaco/dist/theme/sunburst.json +4 -0
  126. package/public/vendor/monaco/dist/theme/textmate-mac-classic.json +4 -0
  127. package/public/vendor/monaco/dist/theme/tomorrow-night-blue.json +4 -0
  128. package/public/vendor/monaco/dist/theme/tomorrow-night-bright.json +4 -0
  129. package/public/vendor/monaco/dist/theme/tomorrow-night-eighties.json +4 -0
  130. package/public/vendor/monaco/dist/theme/tomorrow-night.json +4 -0
  131. package/public/vendor/monaco/dist/theme/tomorrow.json +4 -0
  132. package/public/vendor/monaco/dist/theme/twilight.json +4 -0
  133. package/public/vendor/monaco/dist/theme/upstream-sunburst.json +4 -0
  134. package/public/vendor/monaco/dist/theme/vibrant-ink.json +4 -0
  135. package/public/vendor/monaco/dist/theme/xcode_default.json +4 -0
  136. package/public/vendor/monaco/dist/theme/zenburnesque.json +4 -0
  137. package/public/vendor/monaco/dist/ts.worker.js +2 -16
  138. package/public/vendor/monaco/dist/ts.worker.js.LICENSE.txt +6 -0
  139. package/public/vendor/vendor.js +4 -4
  140. package/public/red/images/grip-horizontal.png +0 -0
  141. package/public/red/images/grip.png +0 -0
  142. package/public/red/tours/images/delete-repair.gif +0 -0
  143. package/public/red/tours/images/detach-repair.gif +0 -0
  144. package/public/red/tours/images/slice.gif +0 -0
  145. package/public/red/tours/images/subflow-labels.png +0 -0
package/public/red/red.js CHANGED
@@ -343,8 +343,21 @@ var RED = (function() {
343
343
  if (/^#flow\/.+$/.test(currentHash)) {
344
344
  RED.workspaces.show(currentHash.substring(6),true);
345
345
  }
346
- if (RED.workspaces.active() === 0 && RED.workspaces.count() > 0) {
347
- RED.workspaces.show(RED.nodes.getWorkspaceOrder()[0])
346
+ if (RED.workspaces.count() > 0) {
347
+ const hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
348
+ const workspaces = RED.nodes.getWorkspaceOrder();
349
+ if (RED.workspaces.active() === 0) {
350
+ for (let index = 0; index < workspaces.length; index++) {
351
+ const ws = workspaces[index];
352
+ if (!hiddenTabs[ws]) {
353
+ RED.workspaces.show(ws);
354
+ break;
355
+ }
356
+ }
357
+ }
358
+ if (RED.workspaces.active() === 0) {
359
+ RED.workspaces.show(workspaces[0]);
360
+ }
348
361
  }
349
362
  } catch(err) {
350
363
  console.warn(err);
@@ -436,6 +449,14 @@ var RED = (function() {
436
449
  } else {
437
450
  options.buttons = [
438
451
  {
452
+ text: RED._("notification.label.unknownNodesButton"),
453
+ class: "pull-left",
454
+ click: function() {
455
+ RED.actions.invoke("core:search", "type:unknown ");
456
+ }
457
+ },
458
+ {
459
+ class: "primary",
439
460
  text: RED._("common.label.close"),
440
461
  click: function() {
441
462
  persistentNotifications[notificationId].hideNotification();
@@ -552,7 +573,7 @@ var RED = (function() {
552
573
  var parts = topic.split("/");
553
574
  var node = RED.nodes.node(parts[1]);
554
575
  if (node) {
555
- if (msg.hasOwnProperty("text") && msg.text !== null && /^[a-zA-Z]/.test(msg.text)) {
576
+ if (msg.hasOwnProperty("text") && msg.text !== null && /^[@a-zA-Z]/.test(msg.text)) {
556
577
  msg.text = node._(msg.text.toString(),{defaultValue:msg.text.toString()});
557
578
  }
558
579
  node.status = msg;
@@ -693,7 +714,10 @@ var RED = (function() {
693
714
  null,
694
715
  {id: "menu-item-edit-select-all", label:RED._("keyboard.selectAll"), onselect: "core:select-all-nodes"},
695
716
  {id: "menu-item-edit-select-connected", label:RED._("keyboard.selectAllConnected"), onselect: "core:select-connected-nodes"},
696
- {id: "menu-item-edit-select-none", label:RED._("keyboard.selectNone"), onselect: "core:select-none"}
717
+ {id: "menu-item-edit-select-none", label:RED._("keyboard.selectNone"), onselect: "core:select-none"},
718
+ null,
719
+ {id: "menu-item-edit-split-wire-with-links", label:RED._("keyboard.splitWireWithLinks"), onselect: "core:split-wire-with-link-nodes"},
720
+
697
721
  ]});
698
722
 
699
723
  menuOptions.push({id:"menu-item-view-menu",label:RED._("menu.label.view.view"),options:[
@@ -805,6 +829,7 @@ var RED = (function() {
805
829
  RED.search.init();
806
830
  RED.actionList.init();
807
831
  RED.editor.init();
832
+ RED.diagnostics.init();
808
833
  RED.diff.init();
809
834
 
810
835
 
@@ -1659,20 +1684,26 @@ RED.user = (function() {
1659
1684
  });
1660
1685
 
1661
1686
  } else if (data.type == "strategy") {
1687
+ var sessionMessage = /[?&]session_message=(.*?)(?:$|&)/.exec(window.location.search);
1688
+ RED.sessionMessages = RED.sessionMessages || [];
1689
+ if (sessionMessage) {
1690
+ RED.sessionMessages.push(decodeURIComponent(sessionMessage[1]));
1691
+ if (history.pushState) {
1692
+ var newurl = window.location.protocol+"//"+window.location.host+window.location.pathname
1693
+ window.history.replaceState({ path: newurl }, "", newurl);
1694
+ } else {
1695
+ window.location.search = "";
1696
+ }
1697
+ }
1698
+
1699
+ if (RED.sessionMessages.length === 0 && data.autoLogin) {
1700
+ document.location = data.loginRedirect
1701
+ return
1702
+ }
1703
+
1662
1704
  i = 0;
1663
1705
  for (;i<data.prompts.length;i++) {
1664
1706
  var field = data.prompts[i];
1665
- var sessionMessage = /[?&]session_message=(.*?)(?:$|&)/.exec(window.location.search);
1666
- if (sessionMessage) {
1667
- RED.sessionMessages = RED.sessionMessages || [];
1668
- RED.sessionMessages.push(sessionMessage[1]);
1669
- if (history.pushState) {
1670
- var newurl = window.location.protocol+"//"+window.location.host+window.location.pathname
1671
- window.history.replaceState({ path: newurl }, "", newurl);
1672
- } else {
1673
- window.location.search = "";
1674
- }
1675
- }
1676
1707
  if (RED.sessionMessages) {
1677
1708
  var sessionMessages = $("<div/>",{class:"form-row",style:"text-align: center"}).appendTo("#node-dialog-login-fields");
1678
1709
  RED.sessionMessages.forEach(function (msg) {
@@ -3540,7 +3571,8 @@ RED.state = {
3540
3571
  GROUP_DRAGGING: 12,
3541
3572
  GROUP_RESIZE: 13,
3542
3573
  DETACHED_DRAGGING: 14,
3543
- SLICING: 15
3574
+ SLICING: 15,
3575
+ SLICING_JUNCTION: 16
3544
3576
  }
3545
3577
  ;RED.plugins = (function() {
3546
3578
  var plugins = {};
@@ -3603,6 +3635,11 @@ RED.state = {
3603
3635
  * See the License for the specific language governing permissions and
3604
3636
  * limitations under the License.
3605
3637
  **/
3638
+
3639
+ /**
3640
+ * An Interface to nodes and utility functions for creating/adding/deleting nodes and links
3641
+ * @namespace RED.nodes
3642
+ */
3606
3643
  RED.nodes = (function() {
3607
3644
 
3608
3645
  var PORT_TYPE_INPUT = 1;
@@ -3623,6 +3660,9 @@ RED.nodes = (function() {
3623
3660
  var groups = {};
3624
3661
  var groupsByZ = {};
3625
3662
 
3663
+ var junctions = {};
3664
+ var junctionsByZ = {};
3665
+
3626
3666
  var initialLoad;
3627
3667
 
3628
3668
  var dirty = false;
@@ -4190,6 +4230,14 @@ RED.nodes = (function() {
4190
4230
  RED.events.emit('nodes:add',n);
4191
4231
  }
4192
4232
  function addLink(l) {
4233
+ if (nodeLinks[l.source.id]) {
4234
+ const isUnique = nodeLinks[l.source.id].out.every(function(link) {
4235
+ return link.sourcePort !== l.sourcePort || link.target.id !== l.target.id
4236
+ })
4237
+ if (!isUnique) {
4238
+ return
4239
+ }
4240
+ }
4193
4241
  links.push(l);
4194
4242
  if (l.source) {
4195
4243
  // Possible the node hasn't been added yet
@@ -4396,6 +4444,7 @@ RED.nodes = (function() {
4396
4444
  var removedNodes = [];
4397
4445
  var removedLinks = [];
4398
4446
  var removedGroups = [];
4447
+ var removedJunctions = [];
4399
4448
  if (ws) {
4400
4449
  delete workspaces[id];
4401
4450
  delete linkTabMap[id];
@@ -4404,7 +4453,14 @@ RED.nodes = (function() {
4404
4453
  var node;
4405
4454
 
4406
4455
  if (allNodes.hasTab(id)) {
4407
- removedNodes = allNodes.getNodes(id).slice()
4456
+ removedNodes = allNodes.getNodes(id).filter(n => {
4457
+ if (n.type === 'junction') {
4458
+ removedJunctions.push(n)
4459
+ return false
4460
+ } else {
4461
+ return true
4462
+ }
4463
+ })
4408
4464
  }
4409
4465
  for (i in configNodes) {
4410
4466
  if (configNodes.hasOwnProperty(i)) {
@@ -4419,6 +4475,10 @@ RED.nodes = (function() {
4419
4475
  var result = removeNode(removedNodes[i].id);
4420
4476
  removedLinks = removedLinks.concat(result.links);
4421
4477
  }
4478
+ for (i=0;i<removedJunctions.length;i++) {
4479
+ var result = removeJunction(removedJunctions[i])
4480
+ removedLinks = removedLinks.concat(result.links)
4481
+ }
4422
4482
 
4423
4483
  // Must get 'removedGroups' in the right order.
4424
4484
  // - start with the top-most groups
@@ -4438,7 +4498,7 @@ RED.nodes = (function() {
4438
4498
  allNodes.removeTab(id);
4439
4499
  RED.events.emit('flows:remove',ws);
4440
4500
  }
4441
- return {nodes:removedNodes,links:removedLinks, groups: removedGroups};
4501
+ return {nodes:removedNodes,links:removedLinks, groups: removedGroups, junctions: removedJunctions};
4442
4502
  }
4443
4503
 
4444
4504
  function addSubflow(sf, createNewIds) {
@@ -4695,7 +4755,7 @@ RED.nodes = (function() {
4695
4755
  delete node.env;
4696
4756
  }
4697
4757
  }
4698
- if (n._def.category != "config") {
4758
+ if (n._def.category != "config" || n.type === 'junction') {
4699
4759
  node.x = n.x;
4700
4760
  node.y = n.y;
4701
4761
  if (exportDimensions) {
@@ -4958,6 +5018,11 @@ RED.nodes = (function() {
4958
5018
  nns.push(convertNode(groups[i], opts));
4959
5019
  }
4960
5020
  }
5021
+ for (i in junctions) {
5022
+ if (junctions.hasOwnProperty(i)) {
5023
+ nns.push(convertNode(junctions[i], opts));
5024
+ }
5025
+ }
4961
5026
  for (i in configNodes) {
4962
5027
  if (configNodes.hasOwnProperty(i)) {
4963
5028
  nns.push(convertNode(configNodes[i], opts));
@@ -5039,6 +5104,7 @@ RED.nodes = (function() {
5039
5104
  tabs: {},
5040
5105
  subflows: {},
5041
5106
  groups: {},
5107
+ junctions: {},
5042
5108
  configs: {},
5043
5109
  nodes: {},
5044
5110
  all: [],
@@ -5054,6 +5120,8 @@ RED.nodes = (function() {
5054
5120
  imported.subflows[n.id] = n;
5055
5121
  } else if (n.type === "group") {
5056
5122
  imported.groups[n.id] = n;
5123
+ } else if (n.type === "junction") {
5124
+ imported.junctions[n.id] = n;
5057
5125
  } else if (n.hasOwnProperty("x") && n.hasOwnProperty("y")) {
5058
5126
  imported.nodes[n.id] = n;
5059
5127
  } else {
@@ -5062,7 +5130,7 @@ RED.nodes = (function() {
5062
5130
  var nodeZ = n.z || "__global__";
5063
5131
  imported.zMap[nodeZ] = imported.zMap[nodeZ] || [];
5064
5132
  imported.zMap[nodeZ].push(n)
5065
- if (allNodes.hasNode(n.id) || configNodes[n.id] || workspaces[n.id] || subflows[n.id] || groups[n.id]) {
5133
+ if (allNodes.hasNode(n.id) || configNodes[n.id] || workspaces[n.id] || subflows[n.id] || groups[n.id] || junctions[n.id]) {
5066
5134
  imported.conflicted[n.id] = n;
5067
5135
  }
5068
5136
  })
@@ -5228,7 +5296,7 @@ RED.nodes = (function() {
5228
5296
  if (!options.generateIds) {
5229
5297
  if (!options.importMap[id]) {
5230
5298
  // No conflict resolution for this node
5231
- var existing = allNodes.getNode(id) || configNodes[id] || workspaces[id] || subflows[id] || groups[id];
5299
+ var existing = allNodes.getNode(id) || configNodes[id] || workspaces[id] || subflows[id] || groups[id] || junctions[id];
5232
5300
  if (existing) {
5233
5301
  existingNodes.push({existing:existing, imported:n});
5234
5302
  }
@@ -5282,6 +5350,7 @@ RED.nodes = (function() {
5282
5350
  n.type != "tab" &&
5283
5351
  n.type != "subflow" &&
5284
5352
  n.type != "group" &&
5353
+ n.type != 'junction' &&
5285
5354
  !registry.getNodeType(n.type) &&
5286
5355
  n.type.substring(0,8) != "subflow:" &&
5287
5356
  unknownTypes.indexOf(n.type)==-1) {
@@ -5354,6 +5423,7 @@ RED.nodes = (function() {
5354
5423
  var new_nodes = [];
5355
5424
  var new_links = [];
5356
5425
  var new_groups = [];
5426
+ var new_junctions = [];
5357
5427
  var new_group_set = new Set();
5358
5428
  var nid;
5359
5429
  var def;
@@ -5545,12 +5615,15 @@ RED.nodes = (function() {
5545
5615
  changed:false,
5546
5616
  _config:{}
5547
5617
  }
5548
- if (n.type !== "group") {
5618
+ if (n.type !== "group" && n.type !== 'junction') {
5549
5619
  node.wires = n.wires||[];
5550
5620
  node.inputLabels = n.inputLabels;
5551
5621
  node.outputLabels = n.outputLabels;
5552
5622
  node.icon = n.icon;
5553
5623
  }
5624
+ if (n.type === 'junction') {
5625
+ node.wires = n.wires||[];
5626
+ }
5554
5627
  if (n.hasOwnProperty('l')) {
5555
5628
  node.l = n.l;
5556
5629
  }
@@ -5619,6 +5692,15 @@ RED.nodes = (function() {
5619
5692
  node.outputs = subflow.out.length;
5620
5693
  node.inputs = subflow.in.length;
5621
5694
  node.env = n.env;
5695
+ } else if (n.type === 'junction') {
5696
+ node._def = {defaults:{}}
5697
+ node._config.x = node.x
5698
+ node._config.y = node.y
5699
+ node.inputs = 1
5700
+ node.outputs = 1
5701
+ node.w = 0;
5702
+ node.h = 0;
5703
+
5622
5704
  } else {
5623
5705
  if (!node._def) {
5624
5706
  if (node.x && node.y) {
@@ -5702,7 +5784,9 @@ RED.nodes = (function() {
5702
5784
  node_map[n.id] = node;
5703
5785
  // If an 'unknown' config node, it will not have been caught by the
5704
5786
  // proper config node handling, so needs adding to new_nodes here
5705
- if (node.type === "unknown" || node._def.category !== "config") {
5787
+ if (node.type === 'junction') {
5788
+ new_junctions.push(node)
5789
+ } else if (node.type === "unknown" || node._def.category !== "config") {
5706
5790
  new_nodes.push(node);
5707
5791
  } else if (node.type === "group") {
5708
5792
  new_groups.push(node);
@@ -5713,11 +5797,15 @@ RED.nodes = (function() {
5713
5797
  }
5714
5798
 
5715
5799
  // Remap all wires and config node references
5716
- for (i=0;i<new_nodes.length;i++) {
5717
- n = new_nodes[i];
5800
+ for (i=0;i<new_nodes.length+new_junctions.length;i++) {
5801
+ if (i<new_nodes.length) {
5802
+ n = new_nodes[i];
5803
+ } else {
5804
+ n = new_junctions[i - new_nodes.length]
5805
+ }
5718
5806
  if (n.wires) {
5719
5807
  for (var w1=0;w1<n.wires.length;w1++) {
5720
- var wires = (n.wires[w1] instanceof Array)?n.wires[w1]:[n.wires[w1]];
5808
+ var wires = (Array.isArray(n.wires[w1]))?n.wires[w1]:[n.wires[w1]];
5721
5809
  for (var w2=0;w2<wires.length;w2++) {
5722
5810
  if (node_map.hasOwnProperty(wires[w2])) {
5723
5811
  if (n.z === node_map[wires[w2]].z) {
@@ -5853,6 +5941,12 @@ RED.nodes = (function() {
5853
5941
  addGroup(n);
5854
5942
  }
5855
5943
 
5944
+ for (i=0;i<new_junctions.length;i++) {
5945
+ var junction = new_junctions[i];
5946
+ addJunction(junction);
5947
+ }
5948
+
5949
+
5856
5950
  // Now the nodes have been fully updated, add them.
5857
5951
  for (i=0;i<new_nodes.length;i++) {
5858
5952
  var node = new_nodes[i];
@@ -5883,6 +5977,7 @@ RED.nodes = (function() {
5883
5977
  nodes:new_nodes,
5884
5978
  links:new_links,
5885
5979
  groups:new_groups,
5980
+ junctions: new_junctions,
5886
5981
  workspaces:new_workspaces,
5887
5982
  subflows:new_subflows,
5888
5983
  missingWorkspace: missingWorkspace,
@@ -6038,6 +6133,30 @@ RED.nodes = (function() {
6038
6133
  RED.events.emit("groups:remove",group);
6039
6134
  }
6040
6135
 
6136
+ function addJunction(junction) {
6137
+ junctionsByZ[junction.z] = junctionsByZ[junction.z] || []
6138
+ junctionsByZ[junction.z].push(junction)
6139
+ junctions[junction.id] = junction;
6140
+ if (!nodeLinks[junction.id]) {
6141
+ nodeLinks[junction.id] = {in:[],out:[]};
6142
+ }
6143
+ RED.events.emit("junctions:add", junction)
6144
+ }
6145
+ function removeJunction(junction) {
6146
+ var i = junctionsByZ[junction.z].indexOf(junction)
6147
+ junctionsByZ[junction.z].splice(i, 1)
6148
+ if (junctionsByZ[junction.z].length === 0) {
6149
+ delete junctionsByZ[junction.z]
6150
+ }
6151
+ delete junctions[junction.id]
6152
+ delete nodeLinks[junction.id];
6153
+ RED.events.emit("junctions:remove", junction)
6154
+
6155
+ var removedLinks = links.filter(function(l) { return (l.source === junction) || (l.target === junction); });
6156
+ removedLinks.forEach(removeLink);
6157
+ return { links: removedLinks }
6158
+ }
6159
+
6041
6160
  function getNodeHelp(type) {
6042
6161
  var helpContent = "";
6043
6162
  var helpElement = $("script[data-help-name='"+type+"']");
@@ -6266,7 +6385,6 @@ RED.nodes = (function() {
6266
6385
  getType: registry.getNodeType,
6267
6386
  getNodeHelp: getNodeHelp,
6268
6387
  convertNode: convertNode,
6269
-
6270
6388
  add: addNode,
6271
6389
  remove: removeNode,
6272
6390
  clear: clear,
@@ -6312,6 +6430,11 @@ RED.nodes = (function() {
6312
6430
  group: function(id) { return groups[id] },
6313
6431
  groups: function(z) { return groupsByZ[z]?groupsByZ[z].slice():[] },
6314
6432
 
6433
+ addJunction: addJunction,
6434
+ removeJunction: removeJunction,
6435
+ junction: function(id) { return junctions[id] },
6436
+ junctions: function(z) { return junctionsByZ[z]?junctionsByZ[z].slice():[] },
6437
+
6315
6438
  eachNode: function(cb) {
6316
6439
  allNodes.eachNode(cb);
6317
6440
  },
@@ -7219,6 +7342,11 @@ RED.nodes.fontAwesome = (function() {
7219
7342
  * See the License for the specific language governing permissions and
7220
7343
  * limitations under the License.
7221
7344
  **/
7345
+
7346
+ /**
7347
+ * An API for undo / redo history buffer
7348
+ * @namespace RED.history
7349
+ */
7222
7350
  RED.history = (function() {
7223
7351
  var undoHistory = [];
7224
7352
  var redoHistory = [];
@@ -7307,6 +7435,23 @@ RED.history = (function() {
7307
7435
  RED.nodes.removeLink(ev.links[i]);
7308
7436
  }
7309
7437
  }
7438
+ if (ev.junctions) {
7439
+ inverseEv.junctions = [];
7440
+ for (i=0;i<ev.junctions.length;i++) {
7441
+ inverseEv.junctions.push(ev.junctions[i]);
7442
+ RED.nodes.removeJunction(ev.junctions[i]);
7443
+ if (ev.junctions[i].g) {
7444
+ var group = RED.nodes.group(ev.junctions[i].g);
7445
+ var index = group.nodes.indexOf(ev.junctions[i]);
7446
+ if (index !== -1) {
7447
+ group.nodes.splice(index,1);
7448
+ RED.group.markDirty(group);
7449
+ }
7450
+ }
7451
+
7452
+
7453
+ }
7454
+ }
7310
7455
  if (ev.groups) {
7311
7456
  inverseEv.groups = [];
7312
7457
  for (i = ev.groups.length - 1;i>=0;i--) {
@@ -7473,6 +7618,21 @@ RED.history = (function() {
7473
7618
  }
7474
7619
  }
7475
7620
  }
7621
+ if (ev.junctions) {
7622
+ inverseEv.junctions = [];
7623
+ for (i=0;i<ev.junctions.length;i++) {
7624
+ inverseEv.junctions.push(ev.junctions[i]);
7625
+ RED.nodes.addJunction(ev.junctions[i]);
7626
+ if (ev.junctions[i].g) {
7627
+ group = RED.nodes.group(ev.junctions[i].g);
7628
+ if (group.nodes.indexOf(ev.junctions[i]) === -1) {
7629
+ group.nodes.push(ev.junctions[i]);
7630
+ }
7631
+ RED.group.markDirty(group)
7632
+ }
7633
+
7634
+ }
7635
+ }
7476
7636
  if (ev.links) {
7477
7637
  inverseEv.links = [];
7478
7638
  for (i=0;i<ev.links.length;i++) {
@@ -7933,24 +8093,74 @@ RED.history = (function() {
7933
8093
  * limitations under the License.
7934
8094
  **/
7935
8095
  RED.validators = {
7936
- number: function(blankAllowed){return function(v) { return (blankAllowed&&(v===''||v===undefined)) || (v!=='' && !isNaN(v));}},
7937
- regex: function(re){return function(v) { return re.test(v);}},
7938
- typedInput: function(ptypeName,isConfig) { return function(v) {
7939
- var ptype = $("#node-"+(isConfig?"config-":"")+"input-"+ptypeName).val() || this[ptypeName];
7940
- if (ptype === 'json') {
7941
- try {
7942
- JSON.parse(v);
8096
+ number: function(blankAllowed,mopt){
8097
+ return function(v, opt) {
8098
+ if ((blankAllowed&&(v===''||v===undefined)) || (v!=='' && !isNaN(v))) {
7943
8099
  return true;
7944
- } catch(err) {
7945
- return false;
7946
8100
  }
7947
- } else if (ptype === 'msg' || ptype === 'flow' || ptype === 'global' ) {
7948
- return RED.utils.validatePropertyExpression(v);
7949
- } else if (ptype === 'num') {
7950
- return /^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/.test(v);
7951
- }
7952
- return true;
7953
- }}
8101
+ if (opt && opt.label) {
8102
+ return RED._("validator.errors.invalid-num-prop", {
8103
+ prop: opt.label
8104
+ });
8105
+ }
8106
+ return opt ? RED._("validator.errors.invalid-num") : false;
8107
+ };
8108
+ },
8109
+ regex: function(re, mopt) {
8110
+ return function(v, opt) {
8111
+ if (re.test(v)) {
8112
+ return true;
8113
+ }
8114
+ if (opt && opt.label) {
8115
+ return RED._("validator.errors.invalid-regex-prop", {
8116
+ prop: opt.label
8117
+ });
8118
+ }
8119
+ return opt ? RED._("validator.errors.invalid-regexp") : false;
8120
+ };
8121
+ },
8122
+ typedInput: function(ptypeName,isConfig,mopt) {
8123
+ return function(v, opt) {
8124
+ var ptype = $("#node-"+(isConfig?"config-":"")+"input-"+ptypeName).val() || this[ptypeName];
8125
+ if (ptype === 'json') {
8126
+ try {
8127
+ JSON.parse(v);
8128
+ return true;
8129
+ } catch(err) {
8130
+ if (opt && opt.label) {
8131
+ return RED._("validator.errors.invalid-json-prop", {
8132
+ error: err.message,
8133
+ prop: opt.label,
8134
+ });
8135
+ }
8136
+ return opt ? RED._("validator.errors.invalid-json", {
8137
+ error: err.message
8138
+ }) : false;
8139
+ }
8140
+ } else if (ptype === 'msg' || ptype === 'flow' || ptype === 'global' ) {
8141
+ if (RED.utils.validatePropertyExpression(v)) {
8142
+ return true;
8143
+ }
8144
+ if (opt && opt.label) {
8145
+ return RED._("validator.errors.invalid-prop-prop", {
8146
+ prop: opt.label
8147
+ });
8148
+ }
8149
+ return opt ? RED._("validator.errors.invalid-prop") : false;
8150
+ } else if (ptype === 'num') {
8151
+ if (/^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/.test(v)) {
8152
+ return true;
8153
+ }
8154
+ if (opt && opt.label) {
8155
+ return RED._("validator.errors.invalid-num-prop", {
8156
+ prop: opt.label
8157
+ });
8158
+ }
8159
+ return opt ? RED._("validator.errors.invalid-num") : false;
8160
+ }
8161
+ return true;
8162
+ };
8163
+ }
7954
8164
  };
7955
8165
  ;/**
7956
8166
  * Copyright JS Foundation and other contributors, http://js.foundation
@@ -8319,7 +8529,16 @@ RED.utils = (function() {
8319
8529
  }
8320
8530
  }
8321
8531
 
8322
- function buildMessageElement(obj,options) {
8532
+ /**
8533
+ * Create a DOM element representation of obj - as used by Debug sidebar etc
8534
+ *
8535
+ * @params obj - the data to display
8536
+ * @params options - a bag of options
8537
+ *
8538
+ * - If you want the Copy Value button, then set `sourceId`
8539
+ * - If you want the Copy Path button, also set `path` to the value to be copied
8540
+ */
8541
+ function createObjectElement(obj,options) {
8323
8542
  options = options || {};
8324
8543
  var key = options.key;
8325
8544
  var typeHint = options.typeHint;
@@ -8509,7 +8728,7 @@ RED.utils = (function() {
8509
8728
  if (fullLength <= 10) {
8510
8729
  for (i=0;i<fullLength;i++) {
8511
8730
  row = $('<div class="red-ui-debug-msg-object-entry collapsed"></div>').appendTo(arrayRows);
8512
- subElements[path+"["+i+"]"] = buildMessageElement(
8731
+ subElements[path+"["+i+"]"] = createObjectElement(
8513
8732
  data[i],
8514
8733
  {
8515
8734
  key: ""+i,
@@ -8539,7 +8758,7 @@ RED.utils = (function() {
8539
8758
  return function() {
8540
8759
  for (var i=min;i<=max;i++) {
8541
8760
  var row = $('<div class="red-ui-debug-msg-object-entry collapsed"></div>').appendTo(parent);
8542
- subElements[path+"["+i+"]"] = buildMessageElement(
8761
+ subElements[path+"["+i+"]"] = createObjectElement(
8543
8762
  data[i],
8544
8763
  {
8545
8764
  key: ""+i,
@@ -8595,7 +8814,7 @@ RED.utils = (function() {
8595
8814
  newPath += "[\""+keys[i].replace(/"/,"\\\"")+"\"]"
8596
8815
  }
8597
8816
  }
8598
- subElements[newPath] = buildMessageElement(
8817
+ subElements[newPath] = createObjectElement(
8599
8818
  data[keys[i]],
8600
8819
  {
8601
8820
  key: keys[i],
@@ -8973,6 +9192,8 @@ RED.utils = (function() {
8973
9192
  return "font-awesome/fa-object-ungroup";
8974
9193
  } else if (node && node.type === 'group') {
8975
9194
  return "font-awesome/fa-object-group"
9195
+ } else if ((node && node.type === 'junction') || (def.type === "junction") ) {
9196
+ return "font-awesome/fa-circle-o"
8976
9197
  } else if (def.category === 'config') {
8977
9198
  return RED.settings.apiRootUrl+"icons/node-red/cog.svg"
8978
9199
  } else if (node && node.type === 'tab') {
@@ -9038,6 +9259,8 @@ RED.utils = (function() {
9038
9259
  l = node.label || defaultLabel
9039
9260
  } else if (node.type === 'group') {
9040
9261
  l = node.name || defaultLabel
9262
+ } else if (node.type === 'junction') {
9263
+ l = 'junction'
9041
9264
  } else {
9042
9265
  l = node._def.label;
9043
9266
  try {
@@ -9050,6 +9273,18 @@ RED.utils = (function() {
9050
9273
  return RED.text.bidi.enforceTextDirectionWithUCC(l);
9051
9274
  }
9052
9275
 
9276
+ function getPaletteLabel(nodeType, def) {
9277
+ var label = nodeType;
9278
+ if (typeof def.paletteLabel !== "undefined") {
9279
+ try {
9280
+ label = (typeof def.paletteLabel === "function" ? def.paletteLabel.call(def) : def.paletteLabel)||"";
9281
+ } catch(err) {
9282
+ console.log("Definition error: "+nodeType+".paletteLabel",err);
9283
+ }
9284
+ }
9285
+ return label
9286
+ }
9287
+
9053
9288
  var nodeColorCache = {};
9054
9289
  function clearNodeColorCache() {
9055
9290
  nodeColorCache = {};
@@ -9192,6 +9427,8 @@ RED.utils = (function() {
9192
9427
  nodeDiv.addClass("red-ui-palette-icon-selection");
9193
9428
  } else if (node.type === "group") {
9194
9429
  nodeDiv.addClass("red-ui-palette-icon-group");
9430
+ } else if (node.type === "junction") {
9431
+ nodeDiv.addClass("red-ui-palette-icon-junction");
9195
9432
  } else if (node.type === 'tab') {
9196
9433
  nodeDiv.addClass("red-ui-palette-icon-flow");
9197
9434
  } else {
@@ -9323,7 +9560,7 @@ RED.utils = (function() {
9323
9560
  }
9324
9561
 
9325
9562
  return {
9326
- createObjectElement: buildMessageElement,
9563
+ createObjectElement: createObjectElement,
9327
9564
  getMessageProperty: getMessageProperty,
9328
9565
  setMessageProperty: setMessageProperty,
9329
9566
  normalisePropertyExpression: normalisePropertyExpression,
@@ -9333,6 +9570,7 @@ RED.utils = (function() {
9333
9570
  getNodeIcon: getNodeIcon,
9334
9571
  getNodeLabel: getNodeLabel,
9335
9572
  getNodeColor: getNodeColor,
9573
+ getPaletteLabel: getPaletteLabel,
9336
9574
  clearNodeColorCache: clearNodeColorCache,
9337
9575
  addSpinnerOverlay: addSpinnerOverlay,
9338
9576
  decodeObject: decodeObject,
@@ -11654,6 +11892,16 @@ RED.popover = (function() {
11654
11892
  }
11655
11893
  }
11656
11894
 
11895
+ target.on("remove", function (ev) {
11896
+ if (timer) {
11897
+ clearTimeout(timer);
11898
+ }
11899
+ if (active) {
11900
+ active = false;
11901
+ setTimeout(closePopup,delay.hide);
11902
+ }
11903
+ });
11904
+
11657
11905
  if (trigger === 'hover') {
11658
11906
  target.on('mouseenter',function(e) {
11659
11907
  clearTimeout(timer);
@@ -11765,6 +12013,11 @@ RED.popover = (function() {
11765
12013
  popover.setAction = function(newAction) {
11766
12014
  action = newAction;
11767
12015
  }
12016
+ popover.delete = function() {
12017
+ popover.close(true)
12018
+ target.off("mouseenter");
12019
+ target.off("mouseleave");
12020
+ };
11768
12021
  return popover;
11769
12022
 
11770
12023
  },
@@ -12066,8 +12319,8 @@ RED.popover = (function() {
12066
12319
  }
12067
12320
  });
12068
12321
  this.element.on("keydown",function(e) {
12069
- if (!menuShown && e.keyCode === 40) {
12070
- //DOWN
12322
+ if (!menuShown && e.keyCode === 40 && $(this).val() === '') {
12323
+ //DOWN (only show menu if search field is emty)
12071
12324
  showMenu();
12072
12325
  }
12073
12326
  });
@@ -12812,7 +13065,7 @@ RED.tabs = (function() {
12812
13065
  }
12813
13066
  var link = $("<a/>",{href:"#"+tab.id, class:"red-ui-tab-label"}).appendTo(li);
12814
13067
  if (tab.icon) {
12815
- $('<img src="'+tab.icon+'" class="red-ui-tab-icon"/>').appendTo(link);
13068
+ $('<i>',{class:"red-ui-tab-icon", style:"mask-image: url("+tab.icon+"); -webkit-mask-image: url("+tab.icon+");"}).appendTo(link);
12816
13069
  } else if (tab.iconClass) {
12817
13070
  $('<i>',{class:"red-ui-tab-icon "+tab.iconClass}).appendTo(link);
12818
13071
  }
@@ -13391,34 +13644,46 @@ RED.stack = (function() {
13391
13644
  }
13392
13645
 
13393
13646
  var autoComplete = function(options) {
13647
+ function getMatch(value, searchValue) {
13648
+ const idx = value.toLowerCase().indexOf(searchValue.toLowerCase());
13649
+ const len = idx > -1 ? searchValue.length : 0;
13650
+ return {
13651
+ index: idx,
13652
+ found: idx > -1,
13653
+ pre: value.substring(0,idx),
13654
+ match: value.substring(idx,idx+len),
13655
+ post: value.substring(idx+len),
13656
+ }
13657
+ }
13658
+ function generateSpans(match) {
13659
+ const els = [];
13660
+ if(match.pre) { els.push($('<span/>').text(match.pre)); }
13661
+ if(match.match) { els.push($('<span/>',{style:"font-weight: bold; color: var(--red-ui-text-color-link);"}).text(match.match)); }
13662
+ if(match.post) { els.push($('<span/>').text(match.post)); }
13663
+ return els;
13664
+ }
13394
13665
  return function(val) {
13395
13666
  var matches = [];
13396
13667
  options.forEach(opt => {
13397
- let v = opt.value;
13398
- var i = v.toLowerCase().indexOf(val.toLowerCase());
13399
- if (i > -1) {
13400
- var pre = v.substring(0,i);
13401
- var matchedVal = v.substring(i,i+val.length);
13402
- var post = v.substring(i+val.length)
13403
-
13404
- var el = $('<div/>',{style:"white-space:nowrap; overflow: hidden; flex-grow:1"});
13405
- $('<span/>').text(pre).appendTo(el);
13406
- $('<span/>',{style:"font-weight: bold"}).text(matchedVal).appendTo(el);
13407
- $('<span/>').text(post).appendTo(el);
13408
-
13409
- var element = $('<div>',{style: "display: flex"});
13410
- el.appendTo(element);
13411
- if (opt.source) {
13412
- $('<div>').css({
13413
- "font-size": "0.8em"
13414
- }).text(opt.source.join(",")).appendTo(element);
13415
- }
13416
-
13417
- matches.push({
13418
- value: v,
13419
- label: element,
13420
- i:i
13421
- })
13668
+ const optVal = opt.value;
13669
+ const optSrc = (opt.source||[]).join(",");
13670
+ const valMatch = getMatch(optVal, val);
13671
+ const srcMatch = getMatch(optSrc, val);
13672
+ if (valMatch.found || srcMatch.found) {
13673
+ const element = $('<div>',{style: "display: flex"});
13674
+ const valEl = $('<div/>',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"});
13675
+ valEl.append(generateSpans(valMatch));
13676
+ valEl.appendTo(element);
13677
+ if (optSrc) {
13678
+ const optEl = $('<div>').css({ "font-size": "0.8em" });
13679
+ optEl.append(generateSpans(srcMatch));
13680
+ optEl.appendTo(element);
13681
+ }
13682
+ matches.push({
13683
+ value: optVal,
13684
+ label: element,
13685
+ i: (valMatch.found ? valMatch.index : srcMatch.index)
13686
+ });
13422
13687
  }
13423
13688
  })
13424
13689
  matches.sort(function(A,B){return A.i-B.i})
@@ -13429,45 +13694,65 @@ RED.stack = (function() {
13429
13694
  // This is a hand-generated list of completions for the core nodes (based on the node help html).
13430
13695
  var msgCompletions = [
13431
13696
  { value: "payload" },
13432
- { value: "req", source: ["http in"]},
13433
- { value: "req.body", source: ["http in"]},
13434
- { value: "req.headers", source: ["http in"]},
13435
- { value: "req.query", source: ["http in"]},
13436
- { value: "req.params", source: ["http in"]},
13437
- { value: "req.cookies", source: ["http in"]},
13438
- { value: "req.files", source: ["http in"]},
13697
+ { value: "topic", source: ["mqtt","inject","rbe"] },
13698
+ { value: "action", source: ["mqtt"] },
13439
13699
  { value: "complete", source: ["join"] },
13440
13700
  { value: "contentType", source: ["mqtt"] },
13441
- { value: "cookies", source: ["http in","http request"] },
13701
+ { value: "cookies", source: ["http request","http response"] },
13442
13702
  { value: "correlationData", source: ["mqtt"] },
13443
13703
  { value: "delay", source: ["delay","trigger"] },
13444
13704
  { value: "encoding", source: ["file"] },
13445
13705
  { value: "error", source: ["catch"] },
13706
+ { value: "error.message", source: ["catch"] },
13707
+ { value: "error.source", source: ["catch"] },
13708
+ { value: "error.source.id", source: ["catch"] },
13709
+ { value: "error.source.type", source: ["catch"] },
13710
+ { value: "error.source.name", source: ["catch"] },
13446
13711
  { value: "filename", source: ["file","file in"] },
13447
13712
  { value: "flush", source: ["delay"] },
13448
13713
  { value: "followRedirects", source: ["http request"] },
13449
- { value: "headers", source: ["http in"," http request"] },
13714
+ { value: "headers", source: ["http response","http request"] },
13715
+ { value: "host", source: ["tcp request","http request"] },
13716
+ { value: "ip", source: ["udp out"] },
13450
13717
  { value: "kill", source: ["exec"] },
13451
13718
  { value: "messageExpiryInterval", source: ["mqtt"] },
13452
- { value: "method", source: ["http-request"] },
13719
+ { value: "method", source: ["http request"] },
13453
13720
  { value: "options", source: ["xml"] },
13454
- { value: "parts", source: ["split","join"] },
13721
+ { value: "parts", source: ["split","join","batch","sort"] },
13455
13722
  { value: "pid", source: ["exec"] },
13723
+ { value: "port", source: ["tcp request"," udp out"] },
13456
13724
  { value: "qos", source: ["mqtt"] },
13457
13725
  { value: "rate", source: ["delay"] },
13458
13726
  { value: "rejectUnauthorized", source: ["http request"] },
13727
+ { value: "req", source: ["http in"]},
13728
+ { value: "req.body", source: ["http in"]},
13729
+ { value: "req.headers", source: ["http in"]},
13730
+ { value: "req.query", source: ["http in"]},
13731
+ { value: "req.params", source: ["http in"]},
13732
+ { value: "req.cookies", source: ["http in"]},
13733
+ { value: "req.files", source: ["http in"]},
13459
13734
  { value: "requestTimeout", source: ["http request"] },
13460
13735
  { value: "reset", source: ["delay","trigger","join","rbe"] },
13736
+ { value: "responseCookies", source: ["http request"] },
13461
13737
  { value: "responseTopic", source: ["mqtt"] },
13738
+ { value: "responseURL", source: ["http request"] },
13462
13739
  { value: "restartTimeout", source: ["join"] },
13463
13740
  { value: "retain", source: ["mqtt"] },
13741
+ { value: "schema", source: ["json"] },
13464
13742
  { value: "select", source: ["html"] },
13465
- { value: "statusCode", source: ["http in"] },
13743
+ { value: "statusCode", source: ["http response","http request"] },
13744
+ { value: "status", source: ["status"] },
13745
+ { value: "status.text", source: ["status"] },
13746
+ { value: "status.source", source: ["status"] },
13747
+ { value: "status.source.type", source: ["status"] },
13748
+ { value: "status.source.id", source: ["status"] },
13749
+ { value: "status.source.name", source: ["status"] },
13750
+ { value: "target", source: ["link call"] },
13466
13751
  { value: "template", source: ["template"] },
13467
13752
  { value: "toFront", source: ["delay"] },
13468
- { value: "topic", source: ["inject","mqtt","rbe"] },
13469
13753
  { value: "url", source: ["http request"] },
13470
- { value: "userProperties", source: ["mqtt"] }
13754
+ { value: "userProperties", source: ["mqtt"] },
13755
+ { value: "_session", source: ["websocket out","tcp out"] },
13471
13756
  ]
13472
13757
  var allOptions = {
13473
13758
  msg: {value:"msg",label:"msg.",validate:RED.utils.validatePropertyExpression, autoComplete: autoComplete(msgCompletions)},
@@ -13502,6 +13787,8 @@ RED.stack = (function() {
13502
13787
  }
13503
13788
  RED.editor.editJSON({
13504
13789
  value: value,
13790
+ stateId: RED.editor.generateViewStateId("typedInput", that, "json"),
13791
+ focus: true,
13505
13792
  complete: function(v) {
13506
13793
  var value = v;
13507
13794
  try {
@@ -13524,6 +13811,8 @@ RED.stack = (function() {
13524
13811
  var that = this;
13525
13812
  RED.editor.editExpression({
13526
13813
  value: this.value().replace(/\t/g,"\n"),
13814
+ stateId: RED.editor.generateViewStateId("typedInput", that, "jsonata"),
13815
+ focus: true,
13527
13816
  complete: function(v) {
13528
13817
  that.value(v.replace(/\n/g,"\t"));
13529
13818
  }
@@ -13538,6 +13827,8 @@ RED.stack = (function() {
13538
13827
  var that = this;
13539
13828
  RED.editor.editBuffer({
13540
13829
  value: this.value(),
13830
+ stateId: RED.editor.generateViewStateId("typedInput", that, "bin"),
13831
+ focus: true,
13541
13832
  complete: function(v) {
13542
13833
  that.value(v);
13543
13834
  }
@@ -13973,7 +14264,7 @@ RED.stack = (function() {
13973
14264
  if (opt.icon.indexOf("<") === 0) {
13974
14265
  $(opt.icon).prependTo(op);
13975
14266
  } else if (opt.icon.indexOf("/") !== -1) {
13976
- $('<img>',{src:mapDeprecatedIcon(opt.icon),style:"margin-right: 4px; height: 18px;"}).prependTo(op);
14267
+ $('<i>',{class:"red-ui-typedInput-icon", style:"mask-image: url("+opt.icon+"); -webkit-mask-image: url("+opt.icon+");"}).prependTo(op);
13977
14268
  } else {
13978
14269
  $('<i>',{class:"red-ui-typedInput-icon "+opt.icon}).prependTo(op);
13979
14270
  }
@@ -14483,7 +14774,8 @@ RED.stack = (function() {
14483
14774
  this.elementDiv.show();
14484
14775
  if (opt.autoComplete) {
14485
14776
  this.input.autoComplete({
14486
- search: opt.autoComplete
14777
+ search: opt.autoComplete,
14778
+ minLength: 0
14487
14779
  })
14488
14780
  }
14489
14781
  }
@@ -14713,12 +15005,14 @@ RED.stack = (function() {
14713
15005
  *
14714
15006
  * options:
14715
15007
  *
14716
- * search : function(value, [done])
14717
- * A function that is passed the current contents of the input whenever
14718
- * it changes.
14719
- * The function must either return auto-complete options, or pass them
14720
- * to the optional 'done' parameter.
14721
- * If the function signature includes 'done', it must be used
15008
+ * search: function(value, [done])
15009
+ * A function that is passed the current contents of the input whenever
15010
+ * it changes.
15011
+ * The function must either return auto-complete options, or pass them
15012
+ * to the optional 'done' parameter.
15013
+ * If the function signature includes 'done', it must be used
15014
+ * minLength: number
15015
+ * If `minLength` is 0, pressing down arrow will show the list
14722
15016
  *
14723
15017
  * The auto-complete options should be an array of objects in the form:
14724
15018
  * {
@@ -14730,10 +15024,11 @@ RED.stack = (function() {
14730
15024
 
14731
15025
  $.widget( "nodered.autoComplete", {
14732
15026
  _create: function() {
14733
- var that = this;
15027
+ const that = this;
14734
15028
  this.completionMenuShown = false;
14735
- this.options.search = this.options.search || function() { return [] }
14736
- this.element.addClass("red-ui-autoComplete")
15029
+ this.options.minLength = parseInteger(this.options.minLength, 1, 0);
15030
+ this.options.search = this.options.search || function() { return [] };
15031
+ this.element.addClass("red-ui-autoComplete");
14737
15032
  this.element.on("keydown.red-ui-autoComplete", function(evt) {
14738
15033
  if ((evt.keyCode === 13 || evt.keyCode === 9) && that.completionMenuShown) {
14739
15034
  var opts = that.menu.options();
@@ -14775,8 +15070,8 @@ RED.stack = (function() {
14775
15070
  this.completionMenuShown = true;
14776
15071
  },
14777
15072
  _updateCompletions: function(val) {
14778
- var that = this;
14779
- if (val.trim() === "") {
15073
+ const that = this;
15074
+ if (val.trim().length < this.options.minLength) {
14780
15075
  if (this.completionMenuShown) {
14781
15076
  this.menu.hide();
14782
15077
  }
@@ -14800,7 +15095,7 @@ RED.stack = (function() {
14800
15095
  }
14801
15096
  }
14802
15097
  if (this.options.search.length === 2) {
14803
- var requestId = 1+Math.floor(Math.random()*10000);
15098
+ const requestId = 1+Math.floor(Math.random()*10000);
14804
15099
  this.pendingRequest = requestId;
14805
15100
  this.options.search(val,function(completions) { displayResults(completions,requestId);})
14806
15101
  } else {
@@ -14816,6 +15111,14 @@ RED.stack = (function() {
14816
15111
  }
14817
15112
  }
14818
15113
  });
15114
+ function parseInteger(input, def, min, max) {
15115
+ if(input == null) { return (def || 0); }
15116
+ min = min == null ? Number.NEGATIVE_INFINITY : min;
15117
+ max = max == null ? Number.POSITIVE_INFINITY : max;
15118
+ let n = parseInt(input);
15119
+ if(isNaN(n) || n < min || n > max) { n = def || 0; }
15120
+ return n;
15121
+ }
14819
15122
  })(jQuery);
14820
15123
  ;RED.actions = (function() {
14821
15124
  var actions = {
@@ -15223,202 +15526,248 @@ RED.deploy = (function() {
15223
15526
  },delta);
15224
15527
  });
15225
15528
  }
15226
- function save(skipValidation,force) {
15227
- if (!$("#red-ui-header-button-deploy").hasClass("disabled")) {
15228
- if (!RED.user.hasPermission("flows.write")) {
15229
- RED.notify(RED._("user.errors.deploy"),"error");
15230
- return;
15231
- }
15232
- if (!skipValidation) {
15233
- var hasUnknown = false;
15234
- var hasInvalid = false;
15235
- var hasUnusedConfig = false;
15529
+ function save(skipValidation, force) {
15530
+ if ($("#red-ui-header-button-deploy").hasClass("disabled")) {
15531
+ return; //deploy is disabled
15532
+ }
15533
+ if ($("#red-ui-header-shade").is(":visible")) {
15534
+ return; //deploy is shaded
15535
+ }
15536
+ if (!RED.user.hasPermission("flows.write")) {
15537
+ RED.notify(RED._("user.errors.deploy"), "error");
15538
+ return;
15539
+ }
15540
+ let hasUnusedConfig = false;
15541
+ if (!skipValidation) {
15542
+ let hasUnknown = false;
15543
+ let hasInvalid = false;
15544
+ const unknownNodes = [];
15545
+ const invalidNodes = [];
15236
15546
 
15237
- var unknownNodes = [];
15238
- var invalidNodes = [];
15547
+ RED.nodes.eachConfig(function (node) {
15548
+ if (node.valid === undefined) {
15549
+ RED.editor.validateNode(node);
15550
+ }
15551
+ if (!node.valid && !node.d) {
15552
+ invalidNodes.push(getNodeInfo(node));
15553
+ }
15554
+ if (node.type === "unknown") {
15555
+ if (unknownNodes.indexOf(node.name) == -1) {
15556
+ unknownNodes.push(node.name);
15557
+ }
15558
+ }
15559
+ });
15560
+ RED.nodes.eachNode(function (node) {
15561
+ if (!node.valid && !node.d) {
15562
+ invalidNodes.push(getNodeInfo(node));
15563
+ }
15564
+ if (node.type === "unknown") {
15565
+ if (unknownNodes.indexOf(node.name) == -1) {
15566
+ unknownNodes.push(node.name);
15567
+ }
15568
+ }
15569
+ });
15570
+ hasUnknown = unknownNodes.length > 0;
15571
+ hasInvalid = invalidNodes.length > 0;
15572
+
15573
+ const unusedConfigNodes = [];
15574
+ RED.nodes.eachConfig(function (node) {
15575
+ if ((node._def.hasUsers !== false) && (node.users.length === 0)) {
15576
+ unusedConfigNodes.push(getNodeInfo(node));
15577
+ hasUnusedConfig = true;
15578
+ }
15579
+ });
15239
15580
 
15240
- RED.nodes.eachConfig(function(node) {
15241
- if (!node.valid && !node.d) {
15242
- invalidNodes.push(getNodeInfo(node));
15581
+ let showWarning = false;
15582
+ let notificationMessage;
15583
+ let notificationButtons = [];
15584
+ let notification;
15585
+ if (hasUnknown && !ignoreDeployWarnings.unknown) {
15586
+ showWarning = true;
15587
+ notificationMessage = "<p>" + RED._('deploy.confirm.unknown') + "</p>" +
15588
+ '<ul class="red-ui-deploy-dialog-confirm-list"><li>' + cropList(unknownNodes).map(function (n) { return sanitize(n) }).join("</li><li>") + "</li></ul><p>" +
15589
+ RED._('deploy.confirm.confirm') +
15590
+ "</p>";
15591
+
15592
+ notificationButtons = [
15593
+ {
15594
+ text: RED._("deploy.unknownNodesButton"),
15595
+ class: "pull-left",
15596
+ click: function() {
15597
+ notification.close();
15598
+ RED.actions.invoke("core:search","type:unknown ");
15599
+ }
15600
+ },
15601
+ {
15602
+ id: "red-ui-deploy-dialog-confirm-deploy-deploy",
15603
+ text: RED._("deploy.confirm.button.confirm"),
15604
+ class: "primary",
15605
+ click: function () {
15606
+ save(true);
15607
+ notification.close();
15608
+ }
15243
15609
  }
15244
- if (node.type === "unknown") {
15245
- if (unknownNodes.indexOf(node.name) == -1) {
15246
- unknownNodes.push(node.name);
15610
+ ];
15611
+ } else if (hasInvalid && !ignoreDeployWarnings.invalid) {
15612
+ showWarning = true;
15613
+ invalidNodes.sort(sortNodeInfo);
15614
+
15615
+ notificationMessage = "<p>" + RED._('deploy.confirm.improperlyConfigured') + "</p>" +
15616
+ '<ul class="red-ui-deploy-dialog-confirm-list"><li>' + cropList(invalidNodes.map(function (A) { return sanitize((A.tab ? "[" + A.tab + "] " : "") + A.label + " (" + A.type + ")") })).join("</li><li>") + "</li></ul><p>" +
15617
+ RED._('deploy.confirm.confirm') +
15618
+ "</p>";
15619
+ notificationButtons = [
15620
+ {
15621
+ text: RED._("deploy.invalidNodesButton"),
15622
+ class: "pull-left",
15623
+ click: function() {
15624
+ notification.close();
15625
+ RED.actions.invoke("core:search","is:invalid ");
15626
+ }
15627
+ },
15628
+ {
15629
+ id: "red-ui-deploy-dialog-confirm-deploy-deploy",
15630
+ text: RED._("deploy.confirm.button.confirm"),
15631
+ class: "primary",
15632
+ click: function () {
15633
+ save(true);
15634
+ notification.close();
15247
15635
  }
15248
- }
15249
- });
15250
- RED.nodes.eachNode(function(node) {
15251
- if (!node.valid && !node.d) {
15252
- invalidNodes.push(getNodeInfo(node));
15253
15636
  }
15254
- if (node.type === "unknown") {
15255
- if (unknownNodes.indexOf(node.name) == -1) {
15256
- unknownNodes.push(node.name);
15637
+ ];
15638
+ }
15639
+ if (showWarning) {
15640
+ notificationButtons.unshift(
15641
+ {
15642
+ text: RED._("common.label.cancel"),
15643
+ click: function () {
15644
+ notification.close();
15257
15645
  }
15258
15646
  }
15647
+ );
15648
+ notification = RED.notify(notificationMessage, {
15649
+ modal: true,
15650
+ fixed: true,
15651
+ buttons: notificationButtons
15259
15652
  });
15260
- hasUnknown = unknownNodes.length > 0;
15261
- hasInvalid = invalidNodes.length > 0;
15653
+ return;
15654
+ }
15655
+ }
15262
15656
 
15263
- var unusedConfigNodes = [];
15264
- RED.nodes.eachConfig(function(node) {
15265
- if ((node._def.hasUsers !== false) && (node.users.length === 0)) {
15266
- unusedConfigNodes.push(getNodeInfo(node));
15267
- hasUnusedConfig = true;
15268
- }
15269
- });
15657
+ const nns = RED.nodes.createCompleteNodeSet();
15658
+ const startTime = Date.now();
15270
15659
 
15271
- var showWarning = false;
15272
- var notificationMessage;
15273
- var notificationButtons = [];
15274
- var notification;
15275
- if (hasUnknown && !ignoreDeployWarnings.unknown) {
15276
- showWarning = true;
15277
- notificationMessage = "<p>"+RED._('deploy.confirm.unknown')+"</p>"+
15278
- '<ul class="red-ui-deploy-dialog-confirm-list"><li>'+cropList(unknownNodes).map(function(n) { return sanitize(n) }).join("</li><li>")+"</li></ul><p>"+
15279
- RED._('deploy.confirm.confirm')+
15280
- "</p>";
15281
-
15282
- notificationButtons= [
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
+
15664
+ const data = { flows: nns };
15665
+
15666
+ if (!force) {
15667
+ data.rev = RED.nodes.version();
15668
+ }
15669
+
15670
+ 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();
15675
+ $.ajax({
15676
+ url: "flows",
15677
+ type: "POST",
15678
+ data: JSON.stringify(data),
15679
+ contentType: "application/json; charset=utf-8",
15680
+ headers: {
15681
+ "Node-RED-Deployment-Type": deploymentType
15682
+ }
15683
+ }).done(function (data, textStatus, xhr) {
15684
+ RED.nodes.dirty(false);
15685
+ RED.nodes.version(data.rev);
15686
+ RED.nodes.originalFlow(nns);
15687
+ if (hasUnusedConfig) {
15688
+ let notification;
15689
+ const opts = {
15690
+ type: "success",
15691
+ fixed: false,
15692
+ timeout: 6000,
15693
+ buttons: [
15283
15694
  {
15284
- id: "red-ui-deploy-dialog-confirm-deploy-deploy",
15285
- text: RED._("deploy.confirm.button.confirm"),
15286
- class: "primary",
15695
+ text: RED._("deploy.unusedConfigNodesButton"),
15696
+ class: "pull-left",
15287
15697
  click: function() {
15288
- save(true);
15289
15698
  notification.close();
15699
+ RED.actions.invoke("core:search","is:config is:unused ");
15290
15700
  }
15291
- }
15292
- ];
15293
- } else if (hasInvalid && !ignoreDeployWarnings.invalid) {
15294
- showWarning = true;
15295
- invalidNodes.sort(sortNodeInfo);
15296
-
15297
- notificationMessage = "<p>"+RED._('deploy.confirm.improperlyConfigured')+"</p>"+
15298
- '<ul class="red-ui-deploy-dialog-confirm-list"><li>'+cropList(invalidNodes.map(function(A) { return sanitize( (A.tab?"["+A.tab+"] ":"")+A.label+" ("+A.type+")")})).join("</li><li>")+"</li></ul><p>"+
15299
- RED._('deploy.confirm.confirm')+
15300
- "</p>";
15301
- notificationButtons= [
15701
+ },
15302
15702
  {
15303
- id: "red-ui-deploy-dialog-confirm-deploy-deploy",
15304
- text: RED._("deploy.confirm.button.confirm"),
15703
+ text: RED._("common.label.close"),
15305
15704
  class: "primary",
15306
- click: function() {
15705
+ click: function () {
15307
15706
  save(true);
15308
15707
  notification.close();
15309
15708
  }
15310
15709
  }
15311
- ];
15312
- }
15313
- if (showWarning) {
15314
- notificationButtons.unshift(
15315
- {
15316
- text: RED._("common.label.cancel"),
15317
- click: function() {
15318
- notification.close();
15319
- }
15320
- }
15321
- );
15322
- notification = RED.notify(notificationMessage,{
15323
- modal: true,
15324
- fixed: true,
15325
- buttons:notificationButtons
15326
- });
15327
- return;
15710
+ ]
15328
15711
  }
15712
+ notification = RED.notify(
15713
+ '<p>' + RED._("deploy.successfulDeploy") + '</p>' +
15714
+ '<p>' + RED._("deploy.unusedConfigNodes") + '</p>', opts);
15715
+ } else {
15716
+ RED.notify('<p>' + RED._("deploy.successfulDeploy") + '</p>', "success");
15329
15717
  }
15330
-
15331
- var nns = RED.nodes.createCompleteNodeSet();
15332
-
15333
- var startTime = Date.now();
15334
- $(".red-ui-deploy-button-content").css('opacity',0);
15335
- $(".red-ui-deploy-button-spinner").show();
15336
- $("#red-ui-header-button-deploy").addClass("disabled");
15337
-
15338
- var data = {flows:nns};
15339
-
15340
- if (!force) {
15341
- data.rev = RED.nodes.version();
15342
- }
15343
-
15344
- deployInflight = true;
15345
- $("#red-ui-header-shade").show();
15346
- $("#red-ui-editor-shade").show();
15347
- $("#red-ui-palette-shade").show();
15348
- $("#red-ui-sidebar-shade").show();
15349
- $.ajax({
15350
- url:"flows",
15351
- type: "POST",
15352
- data: JSON.stringify(data),
15353
- contentType: "application/json; charset=utf-8",
15354
- headers: {
15355
- "Node-RED-Deployment-Type":deploymentType
15718
+ RED.nodes.eachNode(function (node) {
15719
+ if (node.changed) {
15720
+ node.dirty = true;
15721
+ node.changed = false;
15356
15722
  }
15357
- }).done(function(data,textStatus,xhr) {
15358
- RED.nodes.dirty(false);
15359
- RED.nodes.version(data.rev);
15360
- RED.nodes.originalFlow(nns);
15361
- if (hasUnusedConfig) {
15362
- RED.notify(
15363
- '<p>'+RED._("deploy.successfulDeploy")+'</p>'+
15364
- '<p>'+RED._("deploy.unusedConfigNodes")+' <a href="#" onclick="RED.sidebar.config.show(true); return false;">'+RED._("deploy.unusedConfigNodesLink")+'</a></p>',"success",false,6000);
15365
- } else {
15366
- RED.notify('<p>'+RED._("deploy.successfulDeploy")+'</p>',"success");
15723
+ if (node.moved) {
15724
+ node.dirty = true;
15725
+ node.moved = false;
15367
15726
  }
15368
- RED.nodes.eachNode(function(node) {
15369
- if (node.changed) {
15370
- node.dirty = true;
15371
- node.changed = false;
15372
- }
15373
- if (node.moved) {
15374
- node.dirty = true;
15375
- node.moved = false;
15376
- }
15377
- if(node.credentials) {
15378
- delete node.credentials;
15379
- }
15380
- });
15381
- RED.nodes.eachConfig(function (confNode) {
15382
- confNode.changed = false;
15383
- if (confNode.credentials) {
15384
- delete confNode.credentials;
15385
- }
15386
- });
15387
- RED.nodes.eachSubflow(function(subflow) {
15388
- subflow.changed = false;
15389
- });
15390
- RED.nodes.eachWorkspace(function(ws) {
15391
- ws.changed = false;
15392
- });
15393
- // Once deployed, cannot undo back to a clean state
15394
- RED.history.markAllDirty();
15395
- RED.view.redraw();
15396
- RED.events.emit("deploy");
15397
- }).fail(function(xhr,textStatus,err) {
15398
- RED.nodes.dirty(true);
15399
- $("#red-ui-header-button-deploy").removeClass("disabled");
15400
- if (xhr.status === 401) {
15401
- RED.notify(RED._("deploy.deployFailed",{message:RED._("user.notAuthorized")}),"error");
15402
- } else if (xhr.status === 409) {
15403
- resolveConflict(nns, true);
15404
- } else if (xhr.responseText) {
15405
- RED.notify(RED._("deploy.deployFailed",{message:xhr.responseText}),"error");
15406
- } else {
15407
- RED.notify(RED._("deploy.deployFailed",{message:RED._("deploy.errors.noResponse")}),"error");
15727
+ if (node.credentials) {
15728
+ delete node.credentials;
15408
15729
  }
15409
- }).always(function() {
15410
- deployInflight = false;
15411
- var delta = Math.max(0,300-(Date.now()-startTime));
15412
- setTimeout(function() {
15413
- $(".red-ui-deploy-button-content").css('opacity',1);
15414
- $(".red-ui-deploy-button-spinner").hide();
15415
- $("#red-ui-header-shade").hide();
15416
- $("#red-ui-editor-shade").hide();
15417
- $("#red-ui-palette-shade").hide();
15418
- $("#red-ui-sidebar-shade").hide();
15419
- },delta);
15420
15730
  });
15421
- }
15731
+ RED.nodes.eachConfig(function (confNode) {
15732
+ confNode.changed = false;
15733
+ if (confNode.credentials) {
15734
+ delete confNode.credentials;
15735
+ }
15736
+ });
15737
+ RED.nodes.eachSubflow(function (subflow) {
15738
+ subflow.changed = false;
15739
+ });
15740
+ RED.nodes.eachWorkspace(function (ws) {
15741
+ ws.changed = false;
15742
+ });
15743
+ // Once deployed, cannot undo back to a clean state
15744
+ RED.history.markAllDirty();
15745
+ RED.view.redraw();
15746
+ RED.events.emit("deploy");
15747
+ }).fail(function (xhr, textStatus, err) {
15748
+ RED.nodes.dirty(true);
15749
+ $("#red-ui-header-button-deploy").removeClass("disabled");
15750
+ if (xhr.status === 401) {
15751
+ RED.notify(RED._("deploy.deployFailed", { message: RED._("user.notAuthorized") }), "error");
15752
+ } else if (xhr.status === 409) {
15753
+ resolveConflict(nns, true);
15754
+ } else if (xhr.responseText) {
15755
+ RED.notify(RED._("deploy.deployFailed", { message: xhr.responseText }), "error");
15756
+ } else {
15757
+ RED.notify(RED._("deploy.deployFailed", { message: RED._("deploy.errors.noResponse") }), "error");
15758
+ }
15759
+ }).always(function () {
15760
+ deployInflight = false;
15761
+ const delta = Math.max(0, 300 - (Date.now() - startTime));
15762
+ 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();
15769
+ }, delta);
15770
+ });
15422
15771
  }
15423
15772
  return {
15424
15773
  init: init,
@@ -15428,6 +15777,67 @@ RED.deploy = (function() {
15428
15777
 
15429
15778
  }
15430
15779
  })();
15780
+ ;
15781
+ RED.diagnostics = (function () {
15782
+
15783
+ function init() {
15784
+ if (RED.settings.get('diagnostics.ui', true) === false) {
15785
+ return;
15786
+ }
15787
+ RED.actions.add("core:show-system-info", function () { show(); });
15788
+ }
15789
+
15790
+ function show() {
15791
+ $.ajax({
15792
+ headers: {
15793
+ "Accept": "application/json"
15794
+ },
15795
+ cache: false,
15796
+ url: 'diagnostics',
15797
+ success: function (data) {
15798
+ var json = JSON.stringify(data || {}, "", 4);
15799
+ if (json === "{}") {
15800
+ json = "{\n\n}";
15801
+ }
15802
+ RED.editor.editJSON({
15803
+ title: RED._('diagnostics.title'),
15804
+ value: json,
15805
+ requireValid: true,
15806
+ readOnly: true,
15807
+ toolbarButtons: [
15808
+ {
15809
+ text: RED._('clipboard.export.copy'),
15810
+ icon: 'fa fa-copy',
15811
+ click: function () {
15812
+ RED.clipboard.copyText(json, $(this), RED._('clipboard.copyMessageValue'))
15813
+ }
15814
+ },
15815
+ {
15816
+ text: RED._('clipboard.download'),
15817
+ icon: 'fa fa-download',
15818
+ click: function () {
15819
+ var element = document.createElement('a');
15820
+ element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(json));
15821
+ element.setAttribute('download', "system-info.json");
15822
+ element.style.display = 'none';
15823
+ document.body.appendChild(element);
15824
+ element.click();
15825
+ document.body.removeChild(element);
15826
+ }
15827
+ },
15828
+ ]
15829
+ });
15830
+ },
15831
+ error: function (jqXHR, textStatus, errorThrown) {
15832
+ console.log("Unexpected error loading system info:", jqXHR.status, textStatus, errorThrown);
15833
+ }
15834
+ });
15835
+ }
15836
+
15837
+ return {
15838
+ init: init,
15839
+ };
15840
+ })();
15431
15841
  ;RED.diff = (function() {
15432
15842
 
15433
15843
  var currentDiff = {};
@@ -18292,7 +18702,7 @@ RED.keyboard = (function() {
18292
18702
  pane.find("#red-ui-settings-tab-keyboard-filter").searchBox({
18293
18703
  delay: 100,
18294
18704
  change: function() {
18295
- var filterValue = $(this).val().trim();
18705
+ var filterValue = $(this).val().trim().toLowerCase();
18296
18706
  if (filterValue === "") {
18297
18707
  shortcutList.editableList('filter', null);
18298
18708
  } else {
@@ -18384,6 +18794,9 @@ RED.workspaces = (function() {
18384
18794
  var hideStack = [];
18385
18795
  var viewStackPos = 0;
18386
18796
 
18797
+ let flashingTab;
18798
+ let flashingTabTimer;
18799
+
18387
18800
  function addToViewStack(id) {
18388
18801
  if (viewStackPos !== viewStack.length) {
18389
18802
  viewStack.splice(viewStackPos);
@@ -18788,6 +19201,9 @@ RED.workspaces = (function() {
18788
19201
  }
18789
19202
  }
18790
19203
  })
19204
+ RED.actions.add("core:list-modified-nodes",function() {
19205
+ RED.actions.invoke("core:search","is:modified ");
19206
+ })
18791
19207
  RED.actions.add("core:list-hidden-flows",function() {
18792
19208
  RED.actions.invoke("core:search","is:hidden ");
18793
19209
  })
@@ -18888,6 +19304,31 @@ RED.workspaces = (function() {
18888
19304
  workspace_tabs.order(order);
18889
19305
  }
18890
19306
 
19307
+ function flashTab(tabId) {
19308
+ if(flashingTab && flashingTab.length) {
19309
+ //cancel current flashing node before flashing new node
19310
+ clearInterval(flashingTabTimer);
19311
+ flashingTabTimer = null;
19312
+ flashingTab.removeClass('highlighted');
19313
+ flashingTab = null;
19314
+ }
19315
+ let tab = $("#red-ui-tab-" + tabId);
19316
+ if(!tab || !tab.length) { return; }
19317
+
19318
+ flashingTabTimer = setInterval(function(flashEndTime) {
19319
+ if (flashEndTime >= Date.now()) {
19320
+ const highlighted = tab.hasClass("highlighted");
19321
+ tab.toggleClass('highlighted', !highlighted)
19322
+ } else {
19323
+ clearInterval(flashingTabTimer);
19324
+ flashingTabTimer = null;
19325
+ flashingTab = null;
19326
+ tab.removeClass('highlighted');
19327
+ }
19328
+ }, 100, Date.now() + 2200);
19329
+ flashingTab = tab;
19330
+ tab.addClass('highlighted');
19331
+ }
18891
19332
  return {
18892
19333
  init: init,
18893
19334
  add: addWorkspace,
@@ -18920,7 +19361,7 @@ RED.workspaces = (function() {
18920
19361
  isHidden: function(id) {
18921
19362
  return hideStack.includes(id)
18922
19363
  },
18923
- show: function(id,skipStack,unhideOnly) {
19364
+ show: function(id,skipStack,unhideOnly,flash) {
18924
19365
  if (!workspace_tabs.contains(id)) {
18925
19366
  var sf = RED.nodes.subflow(id);
18926
19367
  if (sf) {
@@ -18942,6 +19383,9 @@ RED.workspaces = (function() {
18942
19383
  }
18943
19384
  workspace_tabs.activateTab(id);
18944
19385
  }
19386
+ if(flash) {
19387
+ flashTab(id.replace(".","-"))
19388
+ }
18945
19389
  },
18946
19390
  refresh: function() {
18947
19391
  RED.nodes.eachWorkspace(function(ws) {
@@ -18995,6 +19439,7 @@ RED.statusBar = (function() {
18995
19439
  function addWidget(options) {
18996
19440
  widgets[options.id] = options;
18997
19441
  var el = $('<span class="red-ui-statusbar-widget"></span>');
19442
+ el.prop('id', options.id);
18998
19443
  options.element.appendTo(el);
18999
19444
  if (options.align === 'left') {
19000
19445
  leftBucket.append(el);
@@ -19038,6 +19483,7 @@ RED.statusBar = (function() {
19038
19483
  * |- <g> "groupLayer"
19039
19484
  * |- <g> "groupSelectLayer"
19040
19485
  * |- <g> "linkLayer"
19486
+ * |- <g> "junctionLayer"
19041
19487
  * |- <g> "dragGroupLayer"
19042
19488
  * |- <g> "nodeLayer"
19043
19489
  */
@@ -19070,6 +19516,7 @@ RED.view = (function() {
19070
19516
  var activeSubflow = null;
19071
19517
  var activeNodes = [];
19072
19518
  var activeLinks = [];
19519
+ var activeJunctions = [];
19073
19520
  var activeFlowLinks = [];
19074
19521
  var activeLinkNodes = {};
19075
19522
  var activeGroup = null;
@@ -19104,6 +19551,9 @@ RED.view = (function() {
19104
19551
  var lastClickPosition = [];
19105
19552
  var selectNodesOptions;
19106
19553
 
19554
+ let flashingNodeId;
19555
+ let flashingNodeTimer;
19556
+
19107
19557
  var clipboard = "";
19108
19558
 
19109
19559
  // Note: these are the permitted status colour aliases. The actual RGB values
@@ -19125,6 +19575,7 @@ RED.view = (function() {
19125
19575
  var eventLayer;
19126
19576
  var gridLayer;
19127
19577
  var linkLayer;
19578
+ var junctionLayer;
19128
19579
  var dragGroupLayer;
19129
19580
  var groupSelectLayer;
19130
19581
  var nodeLayer;
@@ -19213,6 +19664,11 @@ RED.view = (function() {
19213
19664
 
19214
19665
  function init() {
19215
19666
 
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)
19216
19672
  chart = $("#red-ui-workspace-chart");
19217
19673
 
19218
19674
  outer = d3.select("#red-ui-workspace-chart")
@@ -19387,6 +19843,7 @@ RED.view = (function() {
19387
19843
  groupSelectLayer = eventLayer.append("g");
19388
19844
  linkLayer = eventLayer.append("g");
19389
19845
  dragGroupLayer = eventLayer.append("g");
19846
+ junctionLayer = eventLayer.append("g");
19390
19847
  nodeLayer = eventLayer.append("g");
19391
19848
 
19392
19849
  drag_lines = [];
@@ -19457,13 +19914,40 @@ RED.view = (function() {
19457
19914
  }
19458
19915
  });
19459
19916
 
19917
+ //add search to status-toolbar
19918
+ RED.statusBar.add({
19919
+ id: "view-search-tools",
19920
+ align: "left",
19921
+ hidden: false,
19922
+ element: $('<span class="button-group">'+
19923
+ '<button class="red-ui-footer-button" id="red-ui-view-searchtools-search"><i class="fa fa-search"></i></button>' +
19924
+ '</span>' +
19925
+ '<span class="button-group search-counter">' +
19926
+ '<span class="red-ui-footer-button" id="red-ui-view-searchtools-counter">? of ?</span>' +
19927
+ '</span>' +
19928
+ '<span class="button-group">' +
19929
+ '<button class="red-ui-footer-button" id="red-ui-view-searchtools-prev"><i class="fa fa-chevron-left"></i></button>' +
19930
+ '<button class="red-ui-footer-button" id="red-ui-view-searchtools-next"><i class="fa fa-chevron-right"></i></button>' +
19931
+ '</span>' +
19932
+ '<span class="button-group">' +
19933
+ '<button class="red-ui-footer-button" id="red-ui-view-searchtools-close"><i class="fa fa-close"></i></button>' +
19934
+ '</span>')
19935
+ })
19936
+ $("#red-ui-view-searchtools-search").on("click", searchFlows);
19937
+ RED.popover.tooltip($("#red-ui-view-searchtools-search"),RED._('actions.search-flows'),'core:search');
19938
+ $("#red-ui-view-searchtools-prev").on("click", searchPrev);
19939
+ RED.popover.tooltip($("#red-ui-view-searchtools-prev"),RED._('actions.search-prev'),'core:search-previous');
19940
+ $("#red-ui-view-searchtools-next").on("click", searchNext);
19941
+ RED.popover.tooltip($("#red-ui-view-searchtools-next"),RED._('actions.search-next'),'core:search-next');
19942
+ RED.popover.tooltip($("#red-ui-view-searchtools-close"),RED._('common.label.close'));
19943
+
19460
19944
  // Handle nodes dragged from the palette
19461
19945
  chart.droppable({
19462
19946
  accept:".red-ui-palette-node",
19463
19947
  drop: function( event, ui ) {
19464
19948
  d3.event = event;
19465
19949
  var selected_tool = $(ui.draggable[0]).attr("data-palette-type");
19466
- var result = addNode(selected_tool);
19950
+ var result = createNode(selected_tool);
19467
19951
  if (!result) {
19468
19952
  return;
19469
19953
  }
@@ -19507,17 +19991,9 @@ RED.view = (function() {
19507
19991
  nn.y = mousePos[1];
19508
19992
 
19509
19993
  if (snapGrid) {
19510
- var gridOffset = [0,0];
19511
- var offsetLeft = nn.x-(gridSize*Math.round((nn.x-nn.w/2)/gridSize)+nn.w/2);
19512
- var offsetRight = nn.x-(gridSize*Math.round((nn.x+nn.w/2)/gridSize)-nn.w/2);
19513
- if (Math.abs(offsetLeft) < Math.abs(offsetRight)) {
19514
- gridOffset[0] = offsetLeft
19515
- } else {
19516
- gridOffset[0] = offsetRight
19517
- }
19518
- gridOffset[1] = nn.y-(gridSize*Math.round(nn.y/gridSize));
19519
- nn.x -= gridOffset[0];
19520
- nn.y -= gridOffset[1];
19994
+ var gridOffset = RED.view.tools.calculateGridSnapOffsets(nn);
19995
+ nn.x -= gridOffset.x;
19996
+ nn.y -= gridOffset.y;
19521
19997
  }
19522
19998
 
19523
19999
  var spliceLink = $(ui.helper).data("splice");
@@ -19587,7 +20063,7 @@ RED.view = (function() {
19587
20063
 
19588
20064
  RED.actions.add("core:copy-selection-to-internal-clipboard",copySelection);
19589
20065
  RED.actions.add("core:cut-selection-to-internal-clipboard",function(){copySelection();deleteSelection();});
19590
- RED.actions.add("core:paste-from-internal-clipboard",function(){importNodes(clipboard,{generateIds: true});});
20066
+ RED.actions.add("core:paste-from-internal-clipboard",function(){importNodes(clipboard,{generateIds: true, generateDefaultNames: true});});
19591
20067
 
19592
20068
  RED.actions.add("core:detach-selected-nodes", function() { detachSelectedNodes() })
19593
20069
 
@@ -19807,7 +20283,7 @@ RED.view = (function() {
19807
20283
  source:{z:activeWorkspace},
19808
20284
  target:{z:activeWorkspace}
19809
20285
  });
19810
-
20286
+ activeJunctions = RED.nodes.junctions(activeWorkspace) || [];
19811
20287
  activeGroups = RED.nodes.groups(activeWorkspace)||[];
19812
20288
  activeGroups.forEach(function(g, i) {
19813
20289
  g._index = i;
@@ -19822,6 +20298,7 @@ RED.view = (function() {
19822
20298
  } else {
19823
20299
  activeNodes = [];
19824
20300
  activeLinks = [];
20301
+ activeJunctions = [];
19825
20302
  activeGroups = [];
19826
20303
  }
19827
20304
 
@@ -19941,81 +20418,6 @@ RED.view = (function() {
19941
20418
  }
19942
20419
  }
19943
20420
 
19944
- function addNode(type,x,y) {
19945
- var m = /^subflow:(.+)$/.exec(type);
19946
-
19947
- if (activeSubflow && m) {
19948
- var subflowId = m[1];
19949
- if (subflowId === activeSubflow.id) {
19950
- RED.notify(RED._("notification.error",{message: RED._("notification.errors.cannotAddSubflowToItself")}),"error");
19951
- return;
19952
- }
19953
- if (RED.nodes.subflowContains(m[1],activeSubflow.id)) {
19954
- RED.notify(RED._("notification.error",{message: RED._("notification.errors.cannotAddCircularReference")}),"error");
19955
- return;
19956
- }
19957
- }
19958
-
19959
- var nn = { id:RED.nodes.id(),z:RED.workspaces.active()};
19960
-
19961
- nn.type = type;
19962
- nn._def = RED.nodes.getType(nn.type);
19963
-
19964
- if (!m) {
19965
- nn.inputs = nn._def.inputs || 0;
19966
- nn.outputs = nn._def.outputs;
19967
-
19968
- for (var d in nn._def.defaults) {
19969
- if (nn._def.defaults.hasOwnProperty(d)) {
19970
- if (nn._def.defaults[d].value !== undefined) {
19971
- nn[d] = JSON.parse(JSON.stringify(nn._def.defaults[d].value));
19972
- }
19973
- }
19974
- }
19975
-
19976
- if (nn._def.onadd) {
19977
- try {
19978
- nn._def.onadd.call(nn);
19979
- } catch(err) {
19980
- console.log("Definition error: "+nn.type+".onadd:",err);
19981
- }
19982
- }
19983
- } else {
19984
- var subflow = RED.nodes.subflow(m[1]);
19985
- nn.name = "";
19986
- nn.inputs = subflow.in.length;
19987
- nn.outputs = subflow.out.length;
19988
- }
19989
-
19990
- nn.changed = true;
19991
- nn.moved = true;
19992
-
19993
- nn.w = node_width;
19994
- nn.h = Math.max(node_height,(nn.outputs||0) * 15);
19995
- nn.resize = true;
19996
-
19997
- var historyEvent = {
19998
- t:"add",
19999
- nodes:[nn.id],
20000
- dirty:RED.nodes.dirty()
20001
- }
20002
- if (activeSubflow) {
20003
- var subflowRefresh = RED.subflow.refresh(true);
20004
- if (subflowRefresh) {
20005
- historyEvent.subflow = {
20006
- id:activeSubflow.id,
20007
- changed: activeSubflow.changed,
20008
- instances: subflowRefresh.instances
20009
- }
20010
- }
20011
- }
20012
- return {
20013
- node: nn,
20014
- historyEvent: historyEvent
20015
- }
20016
-
20017
- }
20018
-
20019
20421
  function canvasMouseDown() {
20020
20422
  if (RED.view.DEBUG) { console.warn("canvasMouseDown", mouse_mode); }
20021
20423
  var point;
@@ -20067,9 +20469,9 @@ RED.view = (function() {
20067
20469
  .attr("class","nr-ui-view-lasso");
20068
20470
  d3.event.preventDefault();
20069
20471
  }
20070
- } else if (mouse_mode === 0 && d3.event.button === 2 && (d3.event.metaKey || d3.event.ctrlKey)) {
20472
+ } else if (mouse_mode === 0 && d3.event.button === 2 && (d3.event.metaKey || d3.event.ctrlKey || d3.event.shiftKey)) {
20071
20473
  clearSelection();
20072
- mouse_mode = RED.state.SLICING;
20474
+ mouse_mode = (d3.event.metaKey || d3.event.ctrlKey)?RED.state.SLICING : RED.state.SLICING_JUNCTION;
20073
20475
  point = d3.mouse(this);
20074
20476
  slicePath = eventLayer.append("path").attr("class","nr-ui-view-slice").attr("d",`M${point[0]} ${point[1]}`)
20075
20477
  slicePathLast = point;
@@ -20204,16 +20606,38 @@ RED.view = (function() {
20204
20606
  keepAdding = false;
20205
20607
  resetMouseVars();
20206
20608
  }
20207
- var result = addNode(type);
20208
- if (!result) {
20209
- return;
20609
+
20610
+ var nn;
20611
+ var historyEvent;
20612
+ if (type === 'junction') {
20613
+ nn = {
20614
+ _def: {defaults:{}},
20615
+ type: 'junction',
20616
+ z: RED.workspaces.active(),
20617
+ id: RED.nodes.id(),
20618
+ x: 0,
20619
+ y: 0,
20620
+ w: 0, h: 0,
20621
+ outputs: 1,
20622
+ inputs: 1,
20623
+ dirty: true
20624
+ }
20625
+ historyEvent = {
20626
+ t:'add',
20627
+ junctions:[nn]
20628
+ }
20629
+ } else {
20630
+ var result = createNode(type);
20631
+ if (!result) {
20632
+ return;
20633
+ }
20634
+ nn = result.node;
20635
+ historyEvent = result.historyEvent;
20210
20636
  }
20211
20637
  if (keepAdding) {
20212
20638
  mouse_mode = RED.state.QUICK_JOINING;
20213
20639
  }
20214
20640
 
20215
- var nn = result.node;
20216
- var historyEvent = result.historyEvent;
20217
20641
  nn.x = point[0];
20218
20642
  nn.y = point[1];
20219
20643
  var showLabel = RED.utils.getMessageProperty(RED.settings.get('editor'),"view.view-node-show-label");
@@ -20322,8 +20746,11 @@ RED.view = (function() {
20322
20746
  }
20323
20747
  }
20324
20748
  }
20325
-
20326
- RED.nodes.add(nn);
20749
+ if (nn.type === 'junction') {
20750
+ RED.nodes.addJunction(nn);
20751
+ } else {
20752
+ RED.nodes.add(nn);
20753
+ }
20327
20754
  RED.editor.validateNode(nn);
20328
20755
 
20329
20756
  if (targetGroup) {
@@ -20470,7 +20897,7 @@ RED.view = (function() {
20470
20897
  .attr("height",h)
20471
20898
  ;
20472
20899
  return;
20473
- } else if (mouse_mode === RED.state.SLICING) {
20900
+ } else if (mouse_mode === RED.state.SLICING || mouse_mode === RED.state.SLICING_JUNCTION) {
20474
20901
  if (slicePath) {
20475
20902
  var delta = Math.max(1,Math.abs(slicePathLast[0]-mouse_position[0]))*Math.max(1,Math.abs(slicePathLast[1]-mouse_position[1]))
20476
20903
  if (delta > 20) {
@@ -20657,16 +21084,9 @@ RED.view = (function() {
20657
21084
  gridOffset[0] = node.n.x-(gridSize*Math.floor(node.n.x/gridSize))-gridSize/2;
20658
21085
  gridOffset[1] = node.n.y-(gridSize*Math.floor(node.n.y/gridSize))-gridSize/2;
20659
21086
  } else {
20660
- var offsetLeft = node.n.x-(gridSize*Math.round((node.n.x-node.n.w/2)/gridSize)+node.n.w/2);
20661
- var offsetRight = node.n.x-(gridSize*Math.round((node.n.x+node.n.w/2)/gridSize)-node.n.w/2);
20662
- // gridOffset[0] = node.n.x-(gridSize*Math.floor((node.n.x-node.n.w/2)/gridSize)+node.n.w/2);
20663
- if (Math.abs(offsetLeft) < Math.abs(offsetRight)) {
20664
- gridOffset[0] = offsetLeft
20665
- } else {
20666
- gridOffset[0] = offsetRight
20667
- }
20668
- gridOffset[1] = node.n.y-(gridSize*Math.round(node.n.y/gridSize));
20669
- // console.log(offsetLeft, offsetRight);
21087
+ const snapOffsets = RED.view.tools.calculateGridSnapOffsets(node.n);
21088
+ gridOffset[0] = snapOffsets.x;
21089
+ gridOffset[1] = snapOffsets.y;
20670
21090
  }
20671
21091
  if (gridOffset[0] !== 0 || gridOffset[1] !== 0) {
20672
21092
  for (i = 0; i<movingSet.length(); i++) {
@@ -20843,6 +21263,15 @@ RED.view = (function() {
20843
21263
  }
20844
21264
  }
20845
21265
  });
21266
+ activeJunctions.forEach(function(n) {
21267
+ if (!n.selected) {
21268
+ if (n.x > x && n.x < x2 && n.y > y && n.y < y2) {
21269
+ n.selected = true;
21270
+ n.dirty = true;
21271
+ movingSet.add(n);
21272
+ }
21273
+ }
21274
+ })
20846
21275
 
20847
21276
 
20848
21277
 
@@ -20886,11 +21315,92 @@ RED.view = (function() {
20886
21315
  } else if (mouse_mode == RED.state.DEFAULT && mousedown_link == null && !d3.event.ctrlKey && !d3.event.metaKey ) {
20887
21316
  clearSelection();
20888
21317
  updateSelection();
20889
- } else if (slicePath) {
21318
+ } else if (mouse_mode == RED.state.SLICING) {
20890
21319
  deleteSelection();
20891
21320
  slicePath.remove();
20892
21321
  slicePath = null;
20893
21322
  RED.view.redraw(true);
21323
+ } 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
+ })
21391
+ slicePath.remove();
21392
+ slicePath = null;
21393
+
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);
20894
21404
  }
20895
21405
  if (mouse_mode == RED.state.MOVING_ACTIVE) {
20896
21406
  if (movingSet.length() > 0) {
@@ -21011,6 +21521,9 @@ RED.view = (function() {
21011
21521
  }
21012
21522
  }
21013
21523
  function zoomZero() { zoomView(1); }
21524
+ function searchFlows() { RED.actions.invoke("core:search", $(this).data("term")); }
21525
+ function searchPrev() { RED.actions.invoke("core:search-previous"); }
21526
+ function searchNext() { RED.actions.invoke("core:search-next"); }
21014
21527
 
21015
21528
 
21016
21529
  function zoomView(factor) {
@@ -21047,7 +21560,7 @@ RED.view = (function() {
21047
21560
  clearSelection();
21048
21561
  RED.history.pop();
21049
21562
  mouse_mode = 0;
21050
- } else if (mouse_mode === RED.state.SLICING) {
21563
+ } else if (mouse_mode === RED.state.SLICING || mouse_mode === RED.state.SLICING_JUNCTION) {
21051
21564
  if (slicePath) {
21052
21565
  slicePath.remove();
21053
21566
  slicePath = null;
@@ -21116,6 +21629,14 @@ RED.view = (function() {
21116
21629
  }
21117
21630
  });
21118
21631
 
21632
+ activeJunctions.forEach(function(n) {
21633
+ if (!n.selected) {
21634
+ n.selected = true;
21635
+ n.dirty = true;
21636
+ movingSet.add(n);
21637
+ }
21638
+ })
21639
+
21119
21640
  if (mouse_mode !== RED.state.SELECTING_NODE && activeSubflow) {
21120
21641
  activeSubflow.in.forEach(function(n) {
21121
21642
  if (!n.selected) {
@@ -21315,6 +21836,7 @@ RED.view = (function() {
21315
21836
  nodes: [],
21316
21837
  links: [],
21317
21838
  groups: [],
21839
+ junctions: [],
21318
21840
  workspaces: [],
21319
21841
  subflows: []
21320
21842
  }
@@ -21335,6 +21857,7 @@ RED.view = (function() {
21335
21857
  historyEvent.nodes = historyEvent.nodes.concat(subEvent.nodes);
21336
21858
  historyEvent.links = historyEvent.links.concat(subEvent.links);
21337
21859
  historyEvent.groups = historyEvent.groups.concat(subEvent.groups);
21860
+ historyEvent.junctions = historyEvent.junctions.concat(subEvent.junctions);
21338
21861
  }
21339
21862
  RED.history.push(historyEvent);
21340
21863
  RED.nodes.dirty(true);
@@ -21347,19 +21870,27 @@ RED.view = (function() {
21347
21870
  var removedNodes = [];
21348
21871
  var removedLinks = [];
21349
21872
  var removedGroups = [];
21873
+ var removedJunctions = [];
21350
21874
  var removedSubflowOutputs = [];
21351
21875
  var removedSubflowInputs = [];
21352
21876
  var removedSubflowStatus;
21353
21877
  var subflowInstances = [];
21354
21878
  var historyEvents = [];
21355
-
21879
+ var addToRemovedLinks = function(links) {
21880
+ if(!links) { return; }
21881
+ var _links = Array.isArray(links) ? links : [links];
21882
+ _links.forEach(function(l) {
21883
+ removedLinks.push(l);
21884
+ selectedLinks.remove(l);
21885
+ })
21886
+ }
21356
21887
  if (reconnectWires) {
21357
21888
  var reconnectResult = RED.nodes.detachNodes(movingSet.nodes())
21358
21889
  var addedLinks = reconnectResult.newLinks;
21359
21890
  if (addedLinks.length > 0) {
21360
21891
  historyEvents.push({ t:'add', links: addedLinks })
21361
21892
  }
21362
- removedLinks = removedLinks.concat(reconnectResult.removedLinks)
21893
+ addToRemovedLinks(reconnectResult.removedLinks)
21363
21894
  }
21364
21895
 
21365
21896
  var startDirty = RED.nodes.dirty();
@@ -21384,14 +21915,14 @@ RED.view = (function() {
21384
21915
  for (var i=0;i<movingSet.length();i++) {
21385
21916
  node = movingSet.get(i).n;
21386
21917
  node.selected = false;
21387
- if (node.type !== "group" && node.type !== "subflow") {
21918
+ if (node.type !== "group" && node.type !== "subflow" && node.type !== 'junction') {
21388
21919
  if (node.x < 0) {
21389
21920
  node.x = 25
21390
21921
  }
21391
21922
  var removedEntities = RED.nodes.remove(node.id);
21392
21923
  removedNodes.push(node);
21393
21924
  removedNodes = removedNodes.concat(removedEntities.nodes);
21394
- removedLinks = removedLinks.concat(removedEntities.links);
21925
+ addToRemovedLinks(removedEntities.links);
21395
21926
  if (node.g) {
21396
21927
  var group = RED.nodes.group(node.g);
21397
21928
  if (selectedGroups.indexOf(group) === -1) {
@@ -21402,6 +21933,10 @@ RED.view = (function() {
21402
21933
  RED.group.markDirty(group);
21403
21934
  }
21404
21935
  }
21936
+ } else if (node.type === 'junction') {
21937
+ var result = RED.nodes.removeJunction(node)
21938
+ removedJunctions.push(node);
21939
+ removedLinks = removedLinks.concat(result.links);
21405
21940
  } else {
21406
21941
  if (node.direction === "out") {
21407
21942
  removedSubflowOutputs.push(node);
@@ -21424,20 +21959,20 @@ RED.view = (function() {
21424
21959
  if (removedSubflowOutputs.length > 0) {
21425
21960
  result = RED.subflow.removeOutput(removedSubflowOutputs);
21426
21961
  if (result) {
21427
- removedLinks = removedLinks.concat(result.links);
21962
+ addToRemovedLinks(result.links);
21428
21963
  }
21429
21964
  }
21430
21965
  // Assume 0/1 inputs
21431
21966
  if (removedSubflowInputs.length == 1) {
21432
21967
  result = RED.subflow.removeInput();
21433
21968
  if (result) {
21434
- removedLinks = removedLinks.concat(result.links);
21969
+ addToRemovedLinks(result.links);
21435
21970
  }
21436
21971
  }
21437
21972
  if (removedSubflowStatus) {
21438
21973
  result = RED.subflow.removeStatus();
21439
21974
  if (result) {
21440
- removedLinks = removedLinks.concat(result.links);
21975
+ addToRemovedLinks(result.links);
21441
21976
  }
21442
21977
  }
21443
21978
 
@@ -21446,7 +21981,7 @@ RED.view = (function() {
21446
21981
  subflowInstances = instances.instances;
21447
21982
  }
21448
21983
  movingSet.clear();
21449
- if (removedNodes.length > 0 || removedSubflowOutputs.length > 0 || removedSubflowInputs.length > 0 || removedSubflowStatus || removedGroups.length > 0) {
21984
+ if (removedNodes.length > 0 || removedSubflowOutputs.length > 0 || removedSubflowInputs.length > 0 || removedSubflowStatus || removedGroups.length > 0 || removedJunctions.length > 0) {
21450
21985
  RED.nodes.dirty(true);
21451
21986
  }
21452
21987
  }
@@ -21493,6 +22028,7 @@ RED.view = (function() {
21493
22028
  nodes:removedNodes,
21494
22029
  links:removedLinks,
21495
22030
  groups: removedGroups,
22031
+ junctions: removedJunctions,
21496
22032
  subflowOutputs:removedSubflowOutputs,
21497
22033
  subflowInputs:removedSubflowInputs,
21498
22034
  subflow: {
@@ -21552,6 +22088,7 @@ RED.view = (function() {
21552
22088
  var nns = [];
21553
22089
  var nodeCount = 0;
21554
22090
  var groupCount = 0;
22091
+ var junctionCount = 0;
21555
22092
  var handled = {};
21556
22093
  for (var n=0;n<nodes.length;n++) {
21557
22094
  var node = nodes[n];
@@ -21564,6 +22101,8 @@ RED.view = (function() {
21564
22101
  if (node.type != "subflow") {
21565
22102
  if (node.type === "group") {
21566
22103
  groupCount++;
22104
+ } else if (node.type === 'junction') {
22105
+ junctionCount++;
21567
22106
  } else {
21568
22107
  nodeCount++;
21569
22108
  }
@@ -21746,6 +22285,7 @@ RED.view = (function() {
21746
22285
  evt.preventDefault();
21747
22286
  }
21748
22287
 
22288
+
21749
22289
  function portMouseUp(d,portType,portIndex,evt) {
21750
22290
  if (RED.view.DEBUG) { console.warn("portMouseUp", mouse_mode,d,portType,portIndex); }
21751
22291
  evt = evt || d3.event;
@@ -22128,6 +22668,52 @@ RED.view = (function() {
22128
22668
  port.classed("red-ui-flow-port-hovered",false);
22129
22669
  }
22130
22670
 
22671
+ function junctionMouseOver(junction, d) {
22672
+ junction.classed("red-ui-flow-junction-hovered",true);
22673
+ }
22674
+ function junctionMouseOut(junction, d) {
22675
+ junction.classed("red-ui-flow-junction-hovered",false);
22676
+ }
22677
+
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
+
22131
22717
  function prepareDrag(mouse) {
22132
22718
  mouse_mode = RED.state.MOVING;
22133
22719
  // Called when movingSet should be prepared to be dragged
@@ -22320,8 +22906,6 @@ RED.view = (function() {
22320
22906
  )
22321
22907
  lastClickNode = mousedown_node;
22322
22908
 
22323
- var i;
22324
-
22325
22909
  if (!d.selected && d.g /*&& !RED.nodes.group(d.g).selected*/) {
22326
22910
  var nodeGroup = RED.nodes.group(d.g);
22327
22911
 
@@ -22355,7 +22939,6 @@ RED.view = (function() {
22355
22939
  enterActiveGroup(ag);
22356
22940
  activeGroup.selected = true;
22357
22941
  }
22358
- console.log(d3.event);
22359
22942
  var cnodes = RED.nodes.getAllFlowNodes(mousedown_node);
22360
22943
  for (var n=0;n<cnodes.length;n++) {
22361
22944
  if (!cnodes[n].selected) {
@@ -22594,6 +23177,11 @@ RED.view = (function() {
22594
23177
  function portMouseOverProxy(e) { portMouseOver(d3.select(this), this.__data__,this.__portType__,this.__portIndex__, e); }
22595
23178
  function portMouseOutProxy(e) { portMouseOut(d3.select(this), this.__data__,this.__portType__,this.__portIndex__, e); }
22596
23179
 
23180
+ function junctionMouseOverProxy(e) { junctionMouseOver(d3.select(this), this.__data__) }
23181
+ 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
+
22597
23185
  function linkMouseDown(d) {
22598
23186
  if (mouse_mode === RED.state.SELECTING_NODE) {
22599
23187
  d3.event.stopPropagation();
@@ -23728,6 +24316,62 @@ RED.view = (function() {
23728
24316
  })
23729
24317
  }
23730
24318
 
24319
+ var junction = junctionLayer.selectAll(".red-ui-flow-junction").data(
24320
+ activeJunctions,
24321
+ d => d.id
24322
+ )
24323
+ var junctionEnter = junction.enter().insert("svg:g").attr("class","red-ui-flow-junction")
24324
+ junctionEnter.each(function(d,i) {
24325
+ var junction = d3.select(this);
24326
+ var contents = document.createDocumentFragment();
24327
+ // d.added = true;
24328
+ var junctionBack = document.createElementNS("http://www.w3.org/2000/svg","rect");
24329
+ junctionBack.setAttribute("class","red-ui-flow-junction-background");
24330
+ junctionBack.setAttribute("x",-5);
24331
+ junctionBack.setAttribute("y",-5);
24332
+ junctionBack.setAttribute("width",10);
24333
+ junctionBack.setAttribute("height",10);
24334
+ junctionBack.setAttribute("rx",5);
24335
+ junctionBack.setAttribute("ry",5);
24336
+ junctionBack.__data__ = d;
24337
+ this.__junctionBack__ = junctionBack;
24338
+ contents.appendChild(junctionBack);
24339
+
24340
+ junctionBack.addEventListener("mouseover", junctionMouseOverProxy);
24341
+ junctionBack.addEventListener("mouseout", junctionMouseOutProxy);
24342
+ junctionBack.addEventListener("mouseup", portMouseUpProxy);
24343
+ junctionBack.addEventListener("mousedown", junctionMouseDownProxy);
24344
+
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); }
24350
+
24351
+ junction[0][0].appendChild(contents);
24352
+ })
24353
+ junction.exit().remove();
24354
+ junction.each(function(d) {
24355
+ var junction = d3.select(this);
24356
+ this.setAttribute("transform", "translate(" + (d.x) + "," + (d.y) + ")");
24357
+ if (d.dirty) {
24358
+ junction.classed("selected", !!d.selected)
24359
+ dirtyNodes[d.id] = d;
24360
+
24361
+ if (d.g) {
24362
+ if (!dirtyGroups[d.g]) {
24363
+ var gg = d.g;
24364
+ while (gg && !dirtyGroups[gg]) {
24365
+ dirtyGroups[gg] = RED.nodes.group(gg);
24366
+ gg = dirtyGroups[gg].g;
24367
+ }
24368
+ }
24369
+ }
24370
+
24371
+ }
24372
+
24373
+ })
24374
+
23731
24375
  var link = linkLayer.selectAll(".red-ui-flow-link").data(
23732
24376
  activeLinks,
23733
24377
  function(d) {
@@ -23754,6 +24398,27 @@ RED.view = (function() {
23754
24398
  selectedLinks.add(d)
23755
24399
  l.classed("red-ui-flow-link-splice",true)
23756
24400
  redraw()
24401
+ } else if (mouse_mode === RED.state.SLICING_JUNCTION) {
24402
+ if (!l.classed("red-ui-flow-link-splice")) {
24403
+ // Find intersection point
24404
+ var lineLength = pathLine.getTotalLength();
24405
+ var pos;
24406
+ var delta = Infinity;
24407
+ for (var i = 0; i < lineLength; i++) {
24408
+ var linePos = pathLine.getPointAtLength(i);
24409
+ var posDeltaX = Math.abs(linePos.x-d3.event.offsetX)
24410
+ var posDeltaY = Math.abs(linePos.y-d3.event.offsetY)
24411
+ var posDelta = posDeltaX*posDeltaX + posDeltaY*posDeltaY
24412
+ if (posDelta < delta) {
24413
+ pos = linePos
24414
+ delta = posDelta
24415
+ }
24416
+ }
24417
+ d._sliceLocation = pos
24418
+ selectedLinks.add(d)
24419
+ l.classed("red-ui-flow-link-splice",true)
24420
+ redraw()
24421
+ }
23757
24422
  }
23758
24423
  })
23759
24424
 
@@ -23780,9 +24445,9 @@ RED.view = (function() {
23780
24445
  var numOutputs = d.source.outputs || 1;
23781
24446
  var sourcePort = d.sourcePort || 0;
23782
24447
  var y = -((numOutputs-1)/2)*13 +13*sourcePort;
23783
- d.x1 = d.source.x+d.source.w/2;
24448
+ d.x1 = d.source.x+(d.source.w/2||0);
23784
24449
  d.y1 = d.source.y+y;
23785
- d.x2 = d.target.x-d.target.w/2;
24450
+ d.x2 = d.target.x-(d.target.w/2||0);
23786
24451
  d.y2 = d.target.y;
23787
24452
 
23788
24453
  // return "M "+d.x1+" "+d.y1+
@@ -24196,12 +24861,16 @@ RED.view = (function() {
24196
24861
  * - addFlow - whether to import nodes to a new tab
24197
24862
  * - touchImport - whether this is a touch import. If not, imported nodes are
24198
24863
  * attachedto mouse for placing - "IMPORT_DRAGGING" state
24864
+ * - generateIds - whether to automatically generate new ids for all imported nodes
24865
+ * - generateDefaultNames - whether to automatically update any nodes with clashing
24866
+ * default names
24199
24867
  */
24200
24868
  function importNodes(newNodesObj,options) {
24201
24869
  options = options || {
24202
24870
  addFlow: false,
24203
24871
  touchImport: false,
24204
- generateIds: false
24872
+ generateIds: false,
24873
+ generateDefaultNames: false
24205
24874
  }
24206
24875
  var addNewFlow = options.addFlow
24207
24876
  var touchImport = options.touchImport;
@@ -24229,7 +24898,13 @@ RED.view = (function() {
24229
24898
  if (!$.isArray(nodesToImport)) {
24230
24899
  nodesToImport = [nodesToImport];
24231
24900
  }
24232
-
24901
+ if (options.generateDefaultNames) {
24902
+ RED.actions.invoke("core:generate-node-names", nodesToImport, {
24903
+ renameBlank: false,
24904
+ renameClash: true,
24905
+ generateHistory: false
24906
+ })
24907
+ }
24233
24908
 
24234
24909
  try {
24235
24910
  var activeSubflowChanged;
@@ -24241,6 +24916,7 @@ RED.view = (function() {
24241
24916
  var new_nodes = result.nodes;
24242
24917
  var new_links = result.links;
24243
24918
  var new_groups = result.groups;
24919
+ var new_junctions = result.junctions;
24244
24920
  var new_workspaces = result.workspaces;
24245
24921
  var new_subflows = result.subflows;
24246
24922
  var removedNodes = result.removedNodes;
@@ -24250,6 +24926,7 @@ RED.view = (function() {
24250
24926
  }
24251
24927
  var new_ms = new_nodes.filter(function(n) { return n.hasOwnProperty("x") && n.hasOwnProperty("y") && n.z == RED.workspaces.active() });
24252
24928
  new_ms = new_ms.concat(new_groups.filter(function(g) { return g.z === RED.workspaces.active()}))
24929
+ new_ms = new_ms.concat(new_junctions.filter(function(j) { return j.z === RED.workspaces.active()}))
24253
24930
  var new_node_ids = new_nodes.map(function(n){ n.changed = true; return n.id; });
24254
24931
 
24255
24932
  clearSelection();
@@ -24283,9 +24960,11 @@ RED.view = (function() {
24283
24960
  node.n.moved = true;
24284
24961
  node.n.x -= dx - mouse_position[0];
24285
24962
  node.n.y -= dy - mouse_position[1];
24286
- node.n.w = node_width;
24287
- node.n.h = node_height;
24288
- node.n.resize = true;
24963
+ if (node.n.type !== 'junction') {
24964
+ node.n.w = node_width;
24965
+ node.n.h = node_height;
24966
+ node.n.resize = true;
24967
+ }
24289
24968
  node.dx = node.n.x - mouse_position[0];
24290
24969
  node.dy = node.n.y - mouse_position[1];
24291
24970
  if (node.n.type === "group") {
@@ -24332,6 +25011,7 @@ RED.view = (function() {
24332
25011
  nodes:new_node_ids,
24333
25012
  links:new_links,
24334
25013
  groups:new_groups,
25014
+ junctions: new_junctions,
24335
25015
  workspaces:new_workspaces,
24336
25016
  subflows:new_subflows,
24337
25017
  dirty:RED.nodes.dirty()
@@ -24379,6 +25059,7 @@ RED.view = (function() {
24379
25059
  }
24380
25060
  })
24381
25061
  var newGroupCount = new_groups.length;
25062
+ var newJunctionCount = new_junctions.length;
24382
25063
  if (new_workspaces.length > 0) {
24383
25064
  counts.push(RED._("clipboard.flow",{count:new_workspaces.length}));
24384
25065
  }
@@ -24515,6 +25196,93 @@ RED.view = (function() {
24515
25196
  return selection;
24516
25197
  }
24517
25198
 
25199
+ /**
25200
+ * Create a node from a type string.
25201
+ * **NOTE:** Can throw on error - use `try` `catch` block when calling
25202
+ * @param {string} type The node type to create
25203
+ * @param {number} [x] (optional) The horizontal position on the workspace
25204
+ * @param {number} [y] (optional)The vertical on the workspace
25205
+ * @param {string} [z] (optional) The flow tab this node will belong to. Defaults to active workspace.
25206
+ * @returns An object containing the `node` and a `historyEvent`
25207
+ * @private
25208
+ */
25209
+ function createNode(type, x, y, z) {
25210
+ var m = /^subflow:(.+)$/.exec(type);
25211
+ var activeSubflow = z ? RED.nodes.subflow(z) : null;
25212
+ if (activeSubflow && m) {
25213
+ var subflowId = m[1];
25214
+ if (subflowId === activeSubflow.id) {
25215
+ throw new Error(RED._("notification.error", { message: RED._("notification.errors.cannotAddSubflowToItself") }))
25216
+ }
25217
+ if (RED.nodes.subflowContains(m[1], activeSubflow.id)) {
25218
+ throw new Error(RED._("notification.error", { message: RED._("notification.errors.cannotAddCircularReference") }))
25219
+ }
25220
+ }
25221
+
25222
+ var nn = { id: RED.nodes.id(), z: z || RED.workspaces.active() };
25223
+
25224
+ nn.type = type;
25225
+ nn._def = RED.nodes.getType(nn.type);
25226
+
25227
+ if (!m) {
25228
+ nn.inputs = nn._def.inputs || 0;
25229
+ nn.outputs = nn._def.outputs;
25230
+
25231
+ for (var d in nn._def.defaults) {
25232
+ if (nn._def.defaults.hasOwnProperty(d)) {
25233
+ if (nn._def.defaults[d].value !== undefined) {
25234
+ nn[d] = JSON.parse(JSON.stringify(nn._def.defaults[d].value));
25235
+ }
25236
+ }
25237
+ }
25238
+
25239
+ if (nn._def.onadd) {
25240
+ try {
25241
+ nn._def.onadd.call(nn);
25242
+ } catch (err) {
25243
+ console.log("Definition error: " + nn.type + ".onadd:", err);
25244
+ }
25245
+ }
25246
+ } else {
25247
+ var subflow = RED.nodes.subflow(m[1]);
25248
+ nn.name = "";
25249
+ nn.inputs = subflow.in.length;
25250
+ nn.outputs = subflow.out.length;
25251
+ }
25252
+
25253
+ nn.changed = true;
25254
+ nn.moved = true;
25255
+
25256
+ nn.w = RED.view.node_width;
25257
+ nn.h = Math.max(RED.view.node_height, (nn.outputs || 0) * 15);
25258
+ nn.resize = true;
25259
+ if (x != null && typeof x == "number" && x >= 0) {
25260
+ nn.x = x;
25261
+ }
25262
+ if (y != null && typeof y == "number" && y >= 0) {
25263
+ nn.y = y;
25264
+ }
25265
+ var historyEvent = {
25266
+ t: "add",
25267
+ nodes: [nn.id],
25268
+ dirty: RED.nodes.dirty()
25269
+ }
25270
+ if (activeSubflow) {
25271
+ var subflowRefresh = RED.subflow.refresh(true);
25272
+ if (subflowRefresh) {
25273
+ historyEvent.subflow = {
25274
+ id: activeSubflow.id,
25275
+ changed: activeSubflow.changed,
25276
+ instances: subflowRefresh.instances
25277
+ }
25278
+ }
25279
+ }
25280
+ return {
25281
+ node: nn,
25282
+ historyEvent: historyEvent
25283
+ }
25284
+ }
25285
+
24518
25286
  function calculateNodeDimensions(node) {
24519
25287
  var result = [node_width,node_height];
24520
25288
  try {
@@ -24538,6 +25306,37 @@ RED.view = (function() {
24538
25306
  return result;
24539
25307
  }
24540
25308
 
25309
+
25310
+ function flashNode(n) {
25311
+ let node = n;
25312
+ if(typeof node === "string") { node = RED.nodes.node(n); }
25313
+ if(!node) { return; }
25314
+
25315
+ const flashingNode = flashingNodeTimer && flashingNodeId && RED.nodes.node(flashingNodeId);
25316
+ if(flashingNode) {
25317
+ //cancel current flashing node before flashing new node
25318
+ clearInterval(flashingNodeTimer);
25319
+ flashingNodeTimer = null;
25320
+ flashingNode.dirty = true;
25321
+ flashingNode.highlighted = false;
25322
+ }
25323
+
25324
+ flashingNodeTimer = setInterval(function(flashEndTime) {
25325
+ node.dirty = true;
25326
+ if (flashEndTime >= Date.now()) {
25327
+ node.highlighted = !node.highlighted;
25328
+ } else {
25329
+ clearInterval(flashingNodeTimer);
25330
+ flashingNodeTimer = null;
25331
+ node.highlighted = false;
25332
+ flashingNodeId = null;
25333
+ }
25334
+ RED.view.redraw();
25335
+ }, 100, Date.now() + 2200)
25336
+ flashingNodeId = node.id;
25337
+ node.highlighted = true;
25338
+ RED.view.redraw();
25339
+ }
24541
25340
  return {
24542
25341
  init: init,
24543
25342
  state:function(state) {
@@ -24598,7 +25397,21 @@ RED.view = (function() {
24598
25397
  redraw(true);
24599
25398
  },
24600
25399
  selection: getSelection,
24601
-
25400
+ clearSelection: clearSelection,
25401
+ createNode: createNode,
25402
+ /** default node width */
25403
+ get node_width() {
25404
+ return node_width;
25405
+ },
25406
+ /** default node height */
25407
+ get node_height() {
25408
+ return node_height;
25409
+ },
25410
+ /** snap to grid option state */
25411
+ get snapGrid() {
25412
+ return snapGrid;
25413
+ },
25414
+ /** gets the current scale factor */
24602
25415
  scale: function() {
24603
25416
  return scaleFactor;
24604
25417
  },
@@ -24620,7 +25433,7 @@ RED.view = (function() {
24620
25433
  getActiveGroup: function() { return activeGroup },
24621
25434
  reveal: function(id,triggerHighlight) {
24622
25435
  if (RED.nodes.workspace(id) || RED.nodes.subflow(id)) {
24623
- RED.workspaces.show(id);
25436
+ RED.workspaces.show(id, null, null, true);
24624
25437
  } else {
24625
25438
  var node = RED.nodes.node(id) || RED.nodes.group(id);
24626
25439
  if (node) {
@@ -24628,7 +25441,7 @@ RED.view = (function() {
24628
25441
  node.dirty = true;
24629
25442
  RED.workspaces.show(node.z);
24630
25443
 
24631
- var screenSize = [chart.width()/scaleFactor,chart.height()/scaleFactor];
25444
+ var screenSize = [chart[0].clientWidth/scaleFactor,chart[0].clientHeight/scaleFactor];
24632
25445
  var scrollPos = [chart.scrollLeft()/scaleFactor,chart.scrollTop()/scaleFactor];
24633
25446
  var cx = node.x;
24634
25447
  var cy = node.y;
@@ -24645,24 +25458,7 @@ RED.view = (function() {
24645
25458
  },200);
24646
25459
  }
24647
25460
  if (triggerHighlight !== false) {
24648
- node.highlighted = true;
24649
- if (!node._flashing) {
24650
- node._flashing = true;
24651
- var flash = 22;
24652
- var flashFunc = function() {
24653
- flash--;
24654
- node.dirty = true;
24655
- if (flash >= 0) {
24656
- node.highlighted = !node.highlighted;
24657
- setTimeout(flashFunc,100);
24658
- } else {
24659
- node.highlighted = false;
24660
- delete node._flashing;
24661
- }
24662
- RED.view.redraw();
24663
- }
24664
- flashFunc();
24665
- }
25461
+ flashNode(node);
24666
25462
  }
24667
25463
  } else if (node._def.category === 'config') {
24668
25464
  RED.sidebar.config.show(id);
@@ -25895,6 +26691,244 @@ RED.view.tools = (function() {
25895
26691
  }
25896
26692
  }
25897
26693
 
26694
+ /**
26695
+ * Splits selected wires and re-joins them with link-out+link-in
26696
+ * @param {Object || Object[]} wires The wire(s) to split and replace with link-out, link-in nodes.
26697
+ */
26698
+ function splitWiresWithLinkNodes(wires) {
26699
+ let wiresToSplit = wires || RED.view.selection().links;
26700
+ if (!Array.isArray(wiresToSplit)) {
26701
+ wiresToSplit = [wiresToSplit];
26702
+ }
26703
+ if (wiresToSplit.length < 1) {
26704
+ return; //nothing selected
26705
+ }
26706
+
26707
+ const history = {
26708
+ t: 'multi',
26709
+ events: [],
26710
+ dirty: RED.nodes.dirty()
26711
+ }
26712
+ const nodeSrcMap = {};
26713
+ const nodeTrgMap = {};
26714
+ const _gridSize = RED.view.gridSize();
26715
+
26716
+ for (let wireIdx = 0; wireIdx < wiresToSplit.length; wireIdx++) {
26717
+ const wire = wiresToSplit[wireIdx];
26718
+
26719
+ //get source and target nodes of this wire link
26720
+ const nSrc = wire.source;
26721
+ const nTrg = wire.target;
26722
+
26723
+ var updateNewNodePosXY = function (origNode, newNode, alignLeft, snap, yOffset) {
26724
+ const nnSize = RED.view.calculateNodeDimensions(newNode);
26725
+ newNode.w = nnSize[0];
26726
+ newNode.h = nnSize[1];
26727
+ const coords = { x: origNode.x || 0, y: origNode.y || 0, w: origNode.w || RED.view.node_width, h: origNode.h || RED.view.node_height };
26728
+ const x = coords.x - (coords.w/2.0);
26729
+ if (alignLeft) {
26730
+ coords.x = x - _gridSize - (newNode.w/2.0);
26731
+ } else {
26732
+ coords.x = x + coords.w + _gridSize + (newNode.w/2.0);
26733
+ }
26734
+ newNode.x = coords.x;
26735
+ newNode.y = coords.y;
26736
+ if (snap !== false) {
26737
+ const offsets = RED.view.tools.calculateGridSnapOffsets(newNode);
26738
+ newNode.x -= offsets.x;
26739
+ newNode.y -= offsets.y;
26740
+ }
26741
+ newNode.y += (yOffset || 0);
26742
+ }
26743
+ const srcPort = (wire.sourcePort || 0);
26744
+ let linkOutMapId = nSrc.id + ':' + srcPort;
26745
+ let nnLinkOut = nodeSrcMap[linkOutMapId];
26746
+ //Create a Link Out if one is not already present
26747
+ if(!nnLinkOut) {
26748
+ const nLinkOut = RED.view.createNode("link out"); //create link node
26749
+ nnLinkOut = nLinkOut.node;
26750
+ nodeSrcMap[linkOutMapId] = nnLinkOut;
26751
+ let yOffset = 0;
26752
+ if(nSrc.outputs > 1) {
26753
+
26754
+ const CENTER_PORT = (((nSrc.outputs-1) / 2) + 1);
26755
+ const offsetCount = Math.abs(CENTER_PORT - (srcPort + 1));
26756
+ yOffset = (_gridSize * 2 * offsetCount);
26757
+ if((srcPort + 1) < CENTER_PORT) {
26758
+ yOffset = -yOffset;
26759
+ }
26760
+ updateNewNodePosXY(nSrc, nnLinkOut, false, false, yOffset);
26761
+ } else {
26762
+ updateNewNodePosXY(nSrc, nnLinkOut, false, RED.view.snapGrid, yOffset);
26763
+ }
26764
+ //add created node
26765
+ RED.nodes.add(nnLinkOut);
26766
+ RED.editor.validateNode(nnLinkOut);
26767
+ history.events.push(nLinkOut.historyEvent);
26768
+ //connect node to link node
26769
+ const link = {
26770
+ source: nSrc,
26771
+ sourcePort: wire.sourcePort || 0,
26772
+ target: nnLinkOut
26773
+ };
26774
+ RED.nodes.addLink(link);
26775
+ history.events.push({
26776
+ t: 'add',
26777
+ links: [link],
26778
+ });
26779
+ }
26780
+
26781
+ let nnLinkIn = nodeTrgMap[nTrg.id];
26782
+ //Create a Link In if one is not already present
26783
+ if(!nnLinkIn) {
26784
+ const nLinkIn = RED.view.createNode("link in"); //create link node
26785
+ nnLinkIn = nLinkIn.node;
26786
+ nodeTrgMap[nTrg.id] = nnLinkIn;
26787
+ updateNewNodePosXY(nTrg, nnLinkIn, true, RED.view.snapGrid, 0);
26788
+ //add created node
26789
+ RED.nodes.add(nnLinkIn);
26790
+ RED.editor.validateNode(nnLinkIn);
26791
+ history.events.push(nLinkIn.historyEvent);
26792
+ //connect node to link node
26793
+ const link = {
26794
+ source: nnLinkIn,
26795
+ sourcePort: 0,
26796
+ target: nTrg
26797
+ };
26798
+ RED.nodes.addLink(link);
26799
+ history.events.push({
26800
+ t: 'add',
26801
+ links: [link],
26802
+ });
26803
+ }
26804
+
26805
+ //connect the link out/link in virtual wires
26806
+ if(nnLinkIn.links.indexOf(nnLinkOut.id) == -1) {
26807
+ nnLinkIn.links.push(nnLinkOut.id);
26808
+ }
26809
+ if(nnLinkOut.links.indexOf(nnLinkIn.id) == -1) {
26810
+ nnLinkOut.links.push(nnLinkIn.id);
26811
+ }
26812
+
26813
+ //delete the original wire
26814
+ RED.nodes.removeLink(wire);
26815
+ history.events.push({
26816
+ t: "delete",
26817
+ links: [wire]
26818
+ });
26819
+ }
26820
+ //add all history events to stack
26821
+ RED.history.push(history);
26822
+
26823
+ //select all downstream of new link-in nodes so user can drag to new location
26824
+ RED.view.clearSelection();
26825
+ RED.view.select({nodes: Object.values(nodeTrgMap) });
26826
+ selectConnected("down");
26827
+
26828
+ //update the view
26829
+ RED.nodes.dirty(true);
26830
+ RED.view.redraw(true);
26831
+ }
26832
+
26833
+ /**
26834
+ * Calculate the required offsets to snap a node
26835
+ * @param {Object} node The node to calculate grid snap offsets for
26836
+ * @param {Object} [options] Options: `align` can be "nearest", "left" or "right"
26837
+ * @returns `{x:number, y:number}` as the offsets to deduct from `x` and `y`
26838
+ */
26839
+ function calculateGridSnapOffsets(node, options) {
26840
+ options = options || { align: "nearest" };
26841
+ const gridOffset = { x: 0, y: 0 };
26842
+ const gridSize = RED.view.gridSize();
26843
+ const offsetLeft = node.x - (gridSize * Math.round((node.x - node.w / 2) / gridSize) + node.w / 2);
26844
+ const offsetRight = node.x - (gridSize * Math.round((node.x + node.w / 2) / gridSize) - node.w / 2);
26845
+ gridOffset.x = offsetRight;
26846
+ if (options.align === "right") {
26847
+ //skip - already set to right
26848
+ } else if (options.align === "left" || Math.abs(offsetLeft) < Math.abs(offsetRight)) {
26849
+ gridOffset.x = offsetLeft;
26850
+ }
26851
+ gridOffset.y = node.y - (gridSize * Math.round(node.y / gridSize));
26852
+ return gridOffset;
26853
+ }
26854
+
26855
+ /**
26856
+ * Generate names for the select nodes.
26857
+ * - it only sets the name if it is currently blank
26858
+ * - it uses `<paletteLabel> <N>` - where N is the next available integer that
26859
+ * doesn't clash with any existing nodes of that type
26860
+ * @param {Object} node The node to set the name of - if not provided, uses current selection
26861
+ */
26862
+ function generateNodeNames(node, options) {
26863
+ options = options || {
26864
+ renameBlank: true,
26865
+ renameClash: true,
26866
+ generateHistory: true
26867
+ }
26868
+ let nodes = node;
26869
+ if (node) {
26870
+ if (!Array.isArray(node)) {
26871
+ nodes = [ node ]
26872
+ }
26873
+ } else {
26874
+ nodes = RED.view.selection().nodes;
26875
+ }
26876
+ if (nodes && nodes.length > 0) {
26877
+ // Generate history event if using the workspace selection,
26878
+ // or if the provided node already exists
26879
+ const generateHistory = options.generateHistory && (!node || !!RED.nodes.node(node.id))
26880
+ const historyEvents = []
26881
+ const typeIndex = {}
26882
+ let changed = false;
26883
+ nodes.forEach(n => {
26884
+ const nodeDef = n._def || RED.nodes.getType(n.type)
26885
+ if (nodeDef && nodeDef.defaults && nodeDef.defaults.name) {
26886
+ const paletteLabel = RED.utils.getPaletteLabel(n.type, nodeDef)
26887
+ const defaultNodeNameRE = new RegExp('^'+paletteLabel+' (\\d+)$')
26888
+ if (!typeIndex.hasOwnProperty(n.type)) {
26889
+ const existingNodes = RED.nodes.filterNodes({type: n.type})
26890
+ let maxNameNumber = 0;
26891
+ existingNodes.forEach(n => {
26892
+ let match = defaultNodeNameRE.exec(n.name)
26893
+ if (match) {
26894
+ let nodeNumber = parseInt(match[1])
26895
+ if (nodeNumber > maxNameNumber) {
26896
+ maxNameNumber = nodeNumber
26897
+ }
26898
+ }
26899
+ })
26900
+ typeIndex[n.type] = maxNameNumber + 1
26901
+ }
26902
+ if ((options.renameBlank && n.name === '') || (options.renameClash && defaultNodeNameRE.test(n.name))) {
26903
+ if (generateHistory) {
26904
+ historyEvents.push({
26905
+ t:'edit',
26906
+ node: n,
26907
+ changes: { name: n.name },
26908
+ dirty: RED.nodes.dirty(),
26909
+ changed: n.changed
26910
+ })
26911
+ }
26912
+ n.name = paletteLabel+" "+typeIndex[n.type]
26913
+ n.dirty = true
26914
+ typeIndex[n.type]++
26915
+ changed = true
26916
+ }
26917
+ }
26918
+ })
26919
+ if (changed) {
26920
+ if (historyEvents.length > 0) {
26921
+ RED.history.push({
26922
+ t: 'multi',
26923
+ events: historyEvents
26924
+ })
26925
+ }
26926
+ RED.nodes.dirty(true)
26927
+ RED.view.redraw()
26928
+ }
26929
+ }
26930
+ }
26931
+
25898
26932
  return {
25899
26933
  init: function() {
25900
26934
  RED.actions.add("core:show-selected-node-labels", function() { setSelectedNodeLabelState(true); })
@@ -25956,6 +26990,10 @@ RED.view.tools = (function() {
25956
26990
  RED.actions.add("core:wire-series-of-nodes", function() { wireSeriesOfNodes() })
25957
26991
  RED.actions.add("core:wire-node-to-multiple", function() { wireNodeToMultiple() })
25958
26992
 
26993
+ RED.actions.add("core:split-wire-with-link-nodes", function () { splitWiresWithLinkNodes() });
26994
+
26995
+ RED.actions.add("core:generate-node-names", generateNodeNames )
26996
+
25959
26997
  // RED.actions.add("core:add-node", function() { addNode() })
25960
26998
  },
25961
26999
  /**
@@ -25967,7 +27005,8 @@ RED.view.tools = (function() {
25967
27005
  * @param {Number} dx
25968
27006
  * @param {Number} dy
25969
27007
  */
25970
- moveSelection: moveSelection
27008
+ moveSelection: moveSelection,
27009
+ calculateGridSnapOffsets: calculateGridSnapOffsets
25971
27010
  }
25972
27011
 
25973
27012
  })();
@@ -26485,14 +27524,7 @@ RED.palette = (function() {
26485
27524
 
26486
27525
  var d = $('<div>',{class:"red-ui-palette-node"}).attr("data-palette-type",nt).data('category',rootCategory);
26487
27526
 
26488
- var label = nt;///^(.*?)([ -]in|[ -]out)?$/.exec(nt)[1];
26489
- if (typeof def.paletteLabel !== "undefined") {
26490
- try {
26491
- label = (typeof def.paletteLabel === "function" ? def.paletteLabel.call(def) : def.paletteLabel)||"";
26492
- } catch(err) {
26493
- console.log("Definition error: "+nt+".paletteLabel",err);
26494
- }
26495
- }
27527
+ var label = RED.utils.getPaletteLabel(nt, def);///^(.*?)([ -]in|[ -]out)?$/.exec(nt)[1];
26496
27528
 
26497
27529
  $('<div/>', {
26498
27530
  class: "red-ui-palette-label"+(((!def.align && def.inputs !== 0 && def.outputs === 0) || "right" === def.align) ? " red-ui-palette-label-right" : "")
@@ -27122,6 +28154,7 @@ RED.sidebar.info = (function() {
27122
28154
  });
27123
28155
  return el;
27124
28156
  }
28157
+
27125
28158
  function refresh(node) {
27126
28159
  if (node === undefined) {
27127
28160
  refreshSelection();
@@ -27230,7 +28263,7 @@ RED.sidebar.info = (function() {
27230
28263
  objectType = "group";
27231
28264
  }
27232
28265
  $(propRow.children()[0]).text(RED._("sidebar.info."+objectType))
27233
- RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]);
28266
+ RED.utils.createObjectElement(node.id,{sourceId: node.id}).appendTo(propRow.children()[1]);
27234
28267
 
27235
28268
  if (node.type === "tab" || node.type === "subflow") {
27236
28269
  // If nothing is selected, but we're on a flow or subflow tab.
@@ -27260,8 +28293,8 @@ RED.sidebar.info = (function() {
27260
28293
  if (typeCounts.groups > 0) {
27261
28294
  $('<div>').text(RED._("clipboard.group",{count:typeCounts.groups})).appendTo(counts);
27262
28295
  }
27263
-
27264
-
28296
+ } else if (node.type === 'junction') {
28297
+ propertiesPanelHeaderHelp.hide();
27265
28298
  } else {
27266
28299
  propertiesPanelHeaderHelp.show();
27267
28300
 
@@ -27324,7 +28357,7 @@ RED.sidebar.info = (function() {
27324
28357
 
27325
28358
  }
27326
28359
  } else {
27327
- RED.utils.createObjectElement(val).appendTo(propRow.children()[1]);
28360
+ RED.utils.createObjectElement(val,{sourceId: node.id}).appendTo(propRow.children()[1]);
27328
28361
  }
27329
28362
  }
27330
28363
  }
@@ -27390,6 +28423,7 @@ RED.sidebar.info = (function() {
27390
28423
 
27391
28424
 
27392
28425
  }
28426
+
27393
28427
  function setInfoText(infoText,target) {
27394
28428
  var info = addTargetToExternalLinks($('<div class="red-ui-help"><span class="red-ui-text-bidi-aware" dir=\"'+RED.text.bidi.resolveBaseTextDir(infoText)+'">'+infoText+'</span></div>')).appendTo(target);
27395
28429
  info.find(".red-ui-text-bidi-aware").contents().filter(function() { return this.nodeType === 3 && this.textContent.trim() !== "" }).wrap( "<span></span>" );
@@ -27406,6 +28440,7 @@ RED.sidebar.info = (function() {
27406
28440
  $(this).toggleClass('expanded',!isExpanded);
27407
28441
  })
27408
28442
  }
28443
+
27409
28444
  var tips = (function() {
27410
28445
  var enabled = true;
27411
28446
  var startDelay = 1000;
@@ -27815,14 +28850,7 @@ RED.sidebar.info = (function() {
27815
28850
 
27816
28851
  }
27817
28852
  },
27818
- options: [
27819
- {label:RED._("sidebar.info.search.configNodes"), value:"is:config"},
27820
- {label:RED._("sidebar.info.search.unusedConfigNodes"), value:"is:config is:unused"},
27821
- {label:RED._("sidebar.info.search.invalidNodes"), value: "is:invalid"},
27822
- {label:RED._("sidebar.info.search.uknownNodes"), value: "type:unknown"},
27823
- {label:RED._("sidebar.info.search.unusedSubflows"), value:"is:subflow is:unused"},
27824
- {label:RED._("sidebar.info.search.hiddenFlows"), value:"is:hidden"},
27825
- ]
28853
+ options: RED.search.getSearchOptions()
27826
28854
  });
27827
28855
 
27828
28856
  projectInfo = $('<div class="red-ui-treeList-label red-ui-info-outline-project"><span class="red-ui-treeList-icon"><i class="fa fa-archive"></i></span></div>').hide().appendTo(container)
@@ -27834,15 +28862,18 @@ RED.sidebar.info = (function() {
27834
28862
  data:getFlowData()
27835
28863
  })
27836
28864
  treeList.on('treelistselect', function(e,item) {
27837
- var node = RED.nodes.node(item.id) || RED.nodes.group(item.id);
28865
+ var node = RED.nodes.node(item.id) || RED.nodes.group(item.id) || RED.nodes.workspace(item.id) || RED.nodes.subflow(item.id);
27838
28866
  if (node) {
27839
- if (node.type === 'group' || node._def.category !== "config") {
27840
- // RED.view.select({nodes:[node]})
27841
- } else if (node._def.category === "config") {
27842
- RED.sidebar.info.refresh(node);
27843
- } else {
27844
- // RED.view.select({nodes:[]})
27845
- }
28867
+ RED.sidebar.info.refresh(node);
28868
+ // if (node.type === 'group' || node._def.category !== "config") {
28869
+ // // RED.view.select({nodes:[node]})
28870
+ // } else if (node._def.category === "config") {
28871
+ // RED.sidebar.info.refresh(node);
28872
+ // } else {
28873
+ // // RED.view.select({nodes:[]})
28874
+ // }
28875
+ } else {
28876
+ RED.sidebar.info.refresh(null);
27846
28877
  }
27847
28878
  })
27848
28879
  treeList.on('treelistconfirm', function(e,item) {
@@ -28605,7 +29636,7 @@ RED.sidebar.help = (function() {
28605
29636
  var node = selection.nodes[0];
28606
29637
  if (node.type === "subflow" && node.direction) {
28607
29638
  // ignore subflow virtual ports
28608
- } else if (node.type !== 'group'){
29639
+ } else if (node.type !== 'group' && node.type !== 'junction'){
28609
29640
  showNodeTypeHelp(node.type);
28610
29641
  }
28611
29642
  }
@@ -28714,6 +29745,8 @@ RED.sidebar.help = (function() {
28714
29745
  **/
28715
29746
  RED.sidebar.config = (function() {
28716
29747
 
29748
+ let flashingConfigNode;
29749
+ let flashingConfigNodeTimer;
28717
29750
 
28718
29751
  var content = document.createElement("div");
28719
29752
  content.className = "red-ui-sidebar-node-config";
@@ -28844,6 +29877,7 @@ RED.sidebar.config = (function() {
28844
29877
  var entry = $('<li class="red-ui-palette-node_id_'+node.id.replace(/\./g,"-")+'"></li>').appendTo(list);
28845
29878
  var nodeDiv = $('<div class="red-ui-palette-node-config red-ui-palette-node"></div>').appendTo(entry);
28846
29879
  entry.data('node',node.id);
29880
+ nodeDiv.data('node',node.id);
28847
29881
  var label = $('<div class="red-ui-palette-label"></div>').text(label).appendTo(nodeDiv);
28848
29882
  if (node.d) {
28849
29883
  nodeDiv.addClass("red-ui-palette-node-config-disabled");
@@ -29045,10 +30079,36 @@ RED.sidebar.config = (function() {
29045
30079
  refreshConfigNodeList();
29046
30080
  }
29047
30081
  });
29048
- RED.popover.tooltip($('#red-ui-sidebar-config-filter-all'), RED._("sidebar.config.showAllUnusedConfigNodes"));
30082
+ RED.popover.tooltip($('#red-ui-sidebar-config-filter-all'), RED._("sidebar.config.showAllConfigNodes"));
29049
30083
  RED.popover.tooltip($('#red-ui-sidebar-config-filter-unused'), RED._("sidebar.config.showAllUnusedConfigNodes"));
29050
30084
 
29051
30085
  }
30086
+
30087
+ function flashConfigNode(el) {
30088
+ if(flashingConfigNode && flashingConfigNode.length) {
30089
+ //cancel current flashing node before flashing new node
30090
+ clearInterval(flashingConfigNodeTimer);
30091
+ flashingConfigNodeTimer = null;
30092
+ flashingConfigNode.children("div").removeClass('highlighted');
30093
+ flashingConfigNode = null;
30094
+ }
30095
+ if(!el || !el.children("div").length) { return; }
30096
+
30097
+ flashingConfigNodeTimer = setInterval(function(flashEndTime) {
30098
+ if (flashEndTime >= Date.now()) {
30099
+ const highlighted = el.children("div").hasClass("highlighted");
30100
+ el.children("div").toggleClass('highlighted', !highlighted)
30101
+ } else {
30102
+ clearInterval(flashingConfigNodeTimer);
30103
+ flashingConfigNodeTimer = null;
30104
+ flashingConfigNode = null;
30105
+ el.children("div").removeClass('highlighted');
30106
+ }
30107
+ }, 100, Date.now() + 2200);
30108
+ flashingConfigNode = el;
30109
+ el.children("div").addClass('highlighted');
30110
+ }
30111
+
29052
30112
  function show(id) {
29053
30113
  if (typeof id === 'boolean') {
29054
30114
  if (id) {
@@ -29073,19 +30133,7 @@ RED.sidebar.config = (function() {
29073
30133
  } else if (y<0) {
29074
30134
  scrollWindow.animate({scrollTop: '+='+(y-10)},150);
29075
30135
  }
29076
- var flash = 21;
29077
- var flashFunc = function() {
29078
- if ((flash%2)===0) {
29079
- node.removeClass('node_highlighted');
29080
- } else {
29081
- node.addClass('node_highlighted');
29082
- }
29083
- flash--;
29084
- if (flash >= 0) {
29085
- setTimeout(flashFunc,100);
29086
- }
29087
- }
29088
- flashFunc();
30136
+ flashConfigNode(node, id);
29089
30137
  },100);
29090
30138
  }
29091
30139
  RED.sidebar.show("config");
@@ -30770,7 +31818,11 @@ RED.editor = (function() {
30770
31818
  var result = [];
30771
31819
  for (var prop in definition) {
30772
31820
  if (definition.hasOwnProperty(prop)) {
30773
- if (!validateNodeProperty(node, definition, prop, properties[prop])) {
31821
+ var valid = validateNodeProperty(node, definition, prop, properties[prop]);
31822
+ if ((typeof valid) === "string") {
31823
+ result.push(valid);
31824
+ }
31825
+ else if(!valid) {
30774
31826
  result.push(prop);
30775
31827
  }
30776
31828
  }
@@ -30784,7 +31836,7 @@ RED.editor = (function() {
30784
31836
  * @param definition - the node property definitions (either def.defaults or def.creds)
30785
31837
  * @param property - the property name being validated
30786
31838
  * @param value - the property value being validated
30787
- * @returns {boolean} whether the node proprty is valid
31839
+ * @returns {boolean|string} whether the node proprty is valid. `true`: valid `false|String`: invalid
30788
31840
  */
30789
31841
  function validateNodeProperty(node,definition,property,value) {
30790
31842
  var valid = true;
@@ -30796,22 +31848,74 @@ RED.editor = (function() {
30796
31848
  if (/^\$\{[a-zA-Z_][a-zA-Z0-9_]*\}$/.test(value)) {
30797
31849
  return true;
30798
31850
  }
31851
+ var label = null;
31852
+ if (("label" in definition[property]) &&
31853
+ ((typeof definition[property].label) == "string")) {
31854
+ label = definition[property].label;
31855
+ }
30799
31856
  if ("required" in definition[property] && definition[property].required) {
30800
31857
  valid = value !== "";
31858
+ if (!valid && label) {
31859
+ return RED._("validator.errors.missing-required-prop", {
31860
+ prop: label
31861
+ });
31862
+ }
30801
31863
  }
30802
31864
  if (valid && "validate" in definition[property]) {
30803
31865
  try {
30804
- valid = definition[property].validate.call(node,value);
31866
+ var opt = {};
31867
+ if (label) {
31868
+ opt.label = label;
31869
+ }
31870
+ valid = definition[property].validate.call(node,value, opt);
31871
+ // If the validator takes two arguments, it is a 3.x validator that
31872
+ // can return a String to mean 'invalid' and provide a reason
31873
+ if ((definition[property].validate.length === 2) &&
31874
+ ((typeof valid) === "string")) {
31875
+ return valid;
31876
+ } else {
31877
+ // Otherwise, a 2.x returns a truth-like/false-like value that
31878
+ // we should cooerce to a boolean.
31879
+ valid = !!valid
31880
+ }
30805
31881
  } catch(err) {
30806
31882
  console.log("Validation error:",node.type,node.id,"property: "+property,"value:",value,err);
31883
+ return RED._("validator.errors.validation-error", {
31884
+ prop: property,
31885
+ node: node.type,
31886
+ id: node.id,
31887
+ error: err.message
31888
+ });
30807
31889
  }
30808
31890
  }
30809
31891
  if (valid && definition[property].type && RED.nodes.getType(definition[property].type) && !("validate" in definition[property])) {
30810
31892
  if (!value || value == "_ADD_") {
30811
31893
  valid = definition[property].hasOwnProperty("required") && !definition[property].required;
31894
+ if (!valid && label) {
31895
+ return RED._("validator.errors.missing-required-prop", {
31896
+ prop: label
31897
+ });
31898
+ }
30812
31899
  } else {
30813
31900
  var configNode = RED.nodes.node(value);
30814
- valid = (configNode && (configNode.valid == null || configNode.valid));
31901
+ if (configNode) {
31902
+ if ((configNode.valid == null) || configNode.valid) {
31903
+ return true;
31904
+ }
31905
+ if (label) {
31906
+ return RED._("validator.errors.invalid-config", {
31907
+ prop: label
31908
+ });
31909
+ }
31910
+ }
31911
+ else {
31912
+ if (label) {
31913
+ return RED._("validator.errors.missing-config", {
31914
+ prop: label
31915
+ });
31916
+ }
31917
+ }
31918
+ return false;
30815
31919
  }
30816
31920
  }
30817
31921
  return valid;
@@ -30839,10 +31943,26 @@ RED.editor = (function() {
30839
31943
  if (defaults[property].hasOwnProperty("format") && defaults[property].format !== "" && input[0].nodeName === "DIV") {
30840
31944
  value = input.text();
30841
31945
  }
30842
- if (!validateNodeProperty(node, defaults, property,value)) {
31946
+ var valid = validateNodeProperty(node, defaults, property,value);
31947
+ if (((typeof valid) === "string") || !valid) {
30843
31948
  input.addClass("input-error");
31949
+ if ((typeof valid) === "string") {
31950
+ var tooltip = input.data("tooltip");
31951
+ if (tooltip) {
31952
+ tooltip.setContent(valid);
31953
+ }
31954
+ else {
31955
+ tooltip = RED.popover.tooltip(input, valid);
31956
+ input.data("tooltip", tooltip);
31957
+ }
31958
+ }
30844
31959
  } else {
30845
31960
  input.removeClass("input-error");
31961
+ var tooltip = input.data("tooltip");
31962
+ if (tooltip) {
31963
+ input.data("tooltip", null);
31964
+ tooltip.delete();
31965
+ }
30846
31966
  }
30847
31967
  }
30848
31968
  }
@@ -31009,20 +32129,11 @@ RED.editor = (function() {
31009
32129
  * @param prefix - the prefix to use in the input element ids (node-input|node-config-input)
31010
32130
  */
31011
32131
  function attachPropertyChangeHandler(node,definition,property,prefix) {
31012
- var input = $("#"+prefix+"-"+property);
31013
- if (definition !== undefined && "format" in definition[property] && definition[property].format !== "" && input[0].nodeName === "DIV") {
31014
- $("#"+prefix+"-"+property).on('change keyup', function(event) {
31015
- if (!$(this).attr("skipValidation")) {
31016
- validateNodeEditor(node,prefix);
31017
- }
31018
- });
31019
- } else {
31020
- $("#"+prefix+"-"+property).on("change", function(event) {
31021
- if (!$(this).attr("skipValidation")) {
31022
- validateNodeEditor(node,prefix);
31023
- }
31024
- });
31025
- }
32132
+ $("#"+prefix+"-"+property).on("change keyup paste", function(event) {
32133
+ if (!$(this).attr("skipValidation")) {
32134
+ validateNodeEditor(node,prefix);
32135
+ }
32136
+ });
31026
32137
  }
31027
32138
 
31028
32139
  /**
@@ -31456,6 +32567,7 @@ RED.editor = (function() {
31456
32567
  if (buildingEditDialog) { return }
31457
32568
  buildingEditDialog = true;
31458
32569
  var editing_node = node;
32570
+ var removeInfoEditorOnClose = false;
31459
32571
  var skipInfoRefreshOnClose = false;
31460
32572
  var activeEditPanes = [];
31461
32573
 
@@ -31651,6 +32763,14 @@ RED.editor = (function() {
31651
32763
  }
31652
32764
  if (!node._def.defaults || !node._def.defaults.hasOwnProperty('info')) {
31653
32765
  nodeEditPanes.push('editor-tab-description');
32766
+ removeInfoEditorOnClose = true;
32767
+ if(node.infoEditor) {
32768
+ //As 'editor-tab-description' adds `node.infoEditor` store original & set a
32769
+ //flag to NOT remove this property
32770
+ node.infoEditor__orig = node.infoEditor;
32771
+ delete node.infoEditor;
32772
+ removeInfoEditorOnClose = false;
32773
+ }
31654
32774
  }
31655
32775
  nodeEditPanes.push("editor-tab-appearance");
31656
32776
 
@@ -31666,8 +32786,17 @@ RED.editor = (function() {
31666
32786
  if (RED.view.state() != RED.state.IMPORT_DRAGGING) {
31667
32787
  RED.view.state(RED.state.DEFAULT);
31668
32788
  }
31669
- if (editing_node && !skipInfoRefreshOnClose) {
31670
- RED.sidebar.info.refresh(editing_node);
32789
+ if (editing_node) {
32790
+ if (editing_node.infoEditor__orig) {
32791
+ editing_node.infoEditor = editing_node.infoEditor__orig;
32792
+ delete editing_node.infoEditor__orig;
32793
+ }
32794
+ if (removeInfoEditorOnClose) {
32795
+ delete editing_node.infoEditor;
32796
+ }
32797
+ if (!skipInfoRefreshOnClose) {
32798
+ RED.sidebar.info.refresh(editing_node);
32799
+ }
31671
32800
  }
31672
32801
  RED.workspaces.refresh();
31673
32802
 
@@ -32527,6 +33656,48 @@ RED.editor = (function() {
32527
33656
  }
32528
33657
  }
32529
33658
 
33659
+ /** Genrate a consistent but unique ID for saving and restoring the code editors view state */
33660
+ function generateViewStateId(source, thing, suffix) {
33661
+ try {
33662
+ thing = thing || {};
33663
+ const thingOptions = typeof thing.options === "object" ? thing.options : {};
33664
+ let stateId;
33665
+ if (thing.hasOwnProperty("stateId")) {
33666
+ stateId = thing.stateId
33667
+ } else if (thingOptions.hasOwnProperty("stateId")) {
33668
+ stateId = thing.stateId
33669
+ }
33670
+ if (stateId === false) { return false; }
33671
+ if (!stateId) {
33672
+ let id;
33673
+ const selection = RED.view.selection();
33674
+ if (source === "node" && thing.id) {
33675
+ id = thing.id;
33676
+ } else if (selection.nodes && selection.nodes.length) {
33677
+ id = selection.nodes[0].id;
33678
+ } else {
33679
+ return false; //cant obtain Id.
33680
+ }
33681
+ //Use a string builder to build an ID
33682
+ const sb = [id];
33683
+ //get the index of the el - there may be more than one editor.
33684
+ const el = $(thing.element || thingOptions.element);
33685
+ if(el.length) {
33686
+ sb.push(el.closest(".form-row").index());
33687
+ sb.push(el.index());
33688
+ }
33689
+ if (source == "typedInput") {
33690
+ sb.push(el.closest("li").index());//for when embeded in editable list
33691
+ if (!suffix && thing.propertyType) { suffix = thing.propertyType }
33692
+ }
33693
+ stateId = sb.join("/");
33694
+ }
33695
+ if (stateId && suffix) { stateId += "/" + suffix; }
33696
+ return stateId;
33697
+ } catch (error) {
33698
+ return false;
33699
+ }
33700
+ }
32530
33701
  return {
32531
33702
  init: function() {
32532
33703
  if(window.ace) { window.ace.config.set('basePath', 'vendor/ace'); }
@@ -32543,6 +33714,7 @@ RED.editor = (function() {
32543
33714
  });
32544
33715
  RED.editor.codeEditor.init();
32545
33716
  },
33717
+ generateViewStateId: generateViewStateId,
32546
33718
  edit: showEditDialog,
32547
33719
  editConfig: showEditConfigNodeDialog,
32548
33720
  editFlow: showEditFlowDialog,
@@ -33147,7 +34319,6 @@ RED.editor = (function() {
33147
34319
 
33148
34320
  create: function(container) {
33149
34321
  this.editor = buildDescriptionForm(container,node);
33150
- RED.e = this.editor;
33151
34322
  },
33152
34323
  resize: function(size) {
33153
34324
  this.editor.resize();
@@ -33197,11 +34368,9 @@ RED.editor = (function() {
33197
34368
  var nodeInfoEditor = RED.editor.createEditor({
33198
34369
  id: editorId,
33199
34370
  mode: 'ace/mode/markdown',
33200
- value: ""
34371
+ stateId: RED.editor.generateViewStateId("node", node, "nodeinfo"),
34372
+ value: node.info || ""
33201
34373
  });
33202
- if (node.info) {
33203
- nodeInfoEditor.getSession().setValue(node.info, -1);
33204
- }
33205
34374
  node.infoEditor = nodeInfoEditor;
33206
34375
  return nodeInfoEditor;
33207
34376
  }
@@ -33303,6 +34472,7 @@ RED.editor = (function() {
33303
34472
  this.tabflowEditor = RED.editor.createEditor({
33304
34473
  id: 'node-input-info',
33305
34474
  mode: 'ace/mode/markdown',
34475
+ stateId: options.stateId,
33306
34476
  value: ""
33307
34477
  });
33308
34478
 
@@ -33451,7 +34621,7 @@ RED.editor = (function() {
33451
34621
  newValue = "";
33452
34622
  }
33453
34623
  }
33454
- if (node[d] != newValue) {
34624
+ if (!isEqual(node[d], newValue)) {
33455
34625
  if (node._def.defaults[d].type) {
33456
34626
  // Change to a related config node
33457
34627
  var configNode = RED.nodes.node(node[d]);
@@ -33483,6 +34653,23 @@ RED.editor = (function() {
33483
34653
  }
33484
34654
  });
33485
34655
 
34656
+ /**
34657
+ * Compares `newValue` with `originalValue` for equality.
34658
+ * @param {*} originalValue Original value
34659
+ * @param {*} newValue New value
34660
+ * @returns {boolean} true if originalValue equals newValue, otherwise false
34661
+ */
34662
+ function isEqual(originalValue, newValue) {
34663
+ try {
34664
+ if(originalValue == newValue) {
34665
+ return true;
34666
+ }
34667
+ return JSON.stringify(originalValue) === JSON.stringify(newValue);
34668
+ } catch (err) {
34669
+ return false;
34670
+ }
34671
+ }
34672
+
33486
34673
  /**
33487
34674
  * Update the node credentials from the edit form
33488
34675
  * @param node - the node containing the credentials
@@ -33751,6 +34938,7 @@ RED.editor = (function() {
33751
34938
  var definition = {
33752
34939
  show: function(options) {
33753
34940
  var value = options.value;
34941
+ var onCancel = options.cancel;
33754
34942
  var onComplete = options.complete;
33755
34943
  var type = "_buffer"
33756
34944
  if ($("script[data-template-name='"+type+"']").length === 0) {
@@ -33764,12 +34952,14 @@ RED.editor = (function() {
33764
34952
 
33765
34953
  var trayOptions = {
33766
34954
  title: options.title,
34955
+ focusElement: options.focusElement,
33767
34956
  width: "inherit",
33768
34957
  buttons: [
33769
34958
  {
33770
34959
  id: "node-dialog-cancel",
33771
34960
  text: RED._("common.label.cancel"),
33772
34961
  click: function() {
34962
+ if (onCancel) { onCancel(); }
33773
34963
  RED.tray.close();
33774
34964
  }
33775
34965
  },
@@ -33778,7 +34968,8 @@ RED.editor = (function() {
33778
34968
  text: RED._("common.label.done"),
33779
34969
  class: "primary",
33780
34970
  click: function() {
33781
- onComplete(JSON.stringify(bufferBinValue));
34971
+ bufferStringEditor.saveView();
34972
+ if (onComplete) { onComplete(JSON.stringify(bufferBinValue),null,bufferStringEditor); }
33782
34973
  RED.tray.close();
33783
34974
  }
33784
34975
  }
@@ -33790,19 +34981,20 @@ RED.editor = (function() {
33790
34981
  }
33791
34982
  },
33792
34983
  open: function(tray) {
33793
- var trayBody = tray.find('.red-ui-tray-body');
33794
34984
  var dialogForm = RED.editor.buildEditForm(tray.find('.red-ui-tray-body'),'dialog-form',type,'editor');
33795
-
33796
34985
  bufferStringEditor = RED.editor.createEditor({
33797
34986
  id: 'red-ui-editor-type-buffer-str',
33798
- value: "",
34987
+ value: value||"",
34988
+ stateId: RED.editor.generateViewStateId("buffer", options, ""),
34989
+ focus: true,
33799
34990
  mode:"ace/mode/text"
33800
34991
  });
33801
- bufferStringEditor.getSession().setValue(value||"",-1);
33802
34992
 
33803
34993
  bufferBinEditor = RED.editor.createEditor({
33804
34994
  id: 'red-ui-editor-type-buffer-bin',
33805
34995
  value: "",
34996
+ stateId: false,
34997
+ focus: false,
33806
34998
  mode:"ace/mode/text",
33807
34999
  readOnly: true
33808
35000
  });
@@ -34894,6 +36086,7 @@ RED.editor = (function() {
34894
36086
  show: function(options) {
34895
36087
  var expressionTestCacheId = options.parent||"_";
34896
36088
  var value = options.value;
36089
+ var onCancel = options.cancel;
34897
36090
  var onComplete = options.complete;
34898
36091
  var type = "_expression"
34899
36092
  if ($("script[data-template-name='"+type+"']").length === 0) {
@@ -34907,12 +36100,14 @@ RED.editor = (function() {
34907
36100
 
34908
36101
  var trayOptions = {
34909
36102
  title: options.title,
36103
+ focusElement: options.focusElement,
34910
36104
  width: "inherit",
34911
36105
  buttons: [
34912
36106
  {
34913
36107
  id: "node-dialog-cancel",
34914
36108
  text: RED._("common.label.cancel"),
34915
36109
  click: function() {
36110
+ if(onCancel) { onCancel(); }
34916
36111
  RED.tray.close();
34917
36112
  }
34918
36113
  },
@@ -34922,7 +36117,8 @@ RED.editor = (function() {
34922
36117
  class: "primary",
34923
36118
  click: function() {
34924
36119
  $("#red-ui-editor-type-expression-help").text("");
34925
- onComplete(expressionEditor.getValue());
36120
+ expressionEditor.saveView();
36121
+ if (onComplete) { onComplete(expressionEditor.getValue(),expressionEditor.getCursorPosition(),expressionEditor); }
34926
36122
  RED.tray.close();
34927
36123
  }
34928
36124
  }
@@ -34954,6 +36150,8 @@ RED.editor = (function() {
34954
36150
  id: 'red-ui-editor-type-expression',
34955
36151
  value: "",
34956
36152
  mode:"ace/mode/jsonata",
36153
+ stateId: options.stateId,
36154
+ focus: true,
34957
36155
  options: {
34958
36156
  enableBasicAutocompletion:true,
34959
36157
  enableSnippets:true,
@@ -35077,6 +36275,8 @@ RED.editor = (function() {
35077
36275
  testDataEditor = RED.editor.createEditor({
35078
36276
  id: 'red-ui-editor-type-expression-test-data',
35079
36277
  value: expressionTestCache[expressionTestCacheId] || '{\n "payload": "hello world"\n}',
36278
+ stateId: false,
36279
+ focus: false,
35080
36280
  mode:"ace/mode/json",
35081
36281
  lineNumbers: false
35082
36282
  });
@@ -35146,6 +36346,8 @@ RED.editor = (function() {
35146
36346
  testResultEditor = RED.editor.createEditor({
35147
36347
  id: 'red-ui-editor-type-expression-test-result',
35148
36348
  value: "",
36349
+ stateId: false,
36350
+ focus: false,
35149
36351
  mode:"ace/mode/json",
35150
36352
  lineNumbers: false,
35151
36353
  readOnly: true
@@ -35320,6 +36522,7 @@ RED.editor = (function() {
35320
36522
  var definition = {
35321
36523
  show: function(options) {
35322
36524
  var value = options.value;
36525
+ var onCancel = options.cancel;
35323
36526
  var onComplete = options.complete;
35324
36527
  var type = "_js"
35325
36528
  if ($("script[data-template-name='"+type+"']").length === 0) {
@@ -35327,16 +36530,16 @@ RED.editor = (function() {
35327
36530
  }
35328
36531
  RED.view.state(RED.state.EDITING);
35329
36532
  var expressionEditor;
35330
- var changeTimer;
35331
-
35332
36533
  var trayOptions = {
35333
36534
  title: options.title,
36535
+ focusElement: options.focusElement,
35334
36536
  width: options.width||"inherit",
35335
36537
  buttons: [
35336
36538
  {
35337
36539
  id: "node-dialog-cancel",
35338
36540
  text: RED._("common.label.cancel"),
35339
36541
  click: function() {
36542
+ if (onCancel) { onCancel(); }
35340
36543
  RED.tray.close();
35341
36544
  }
35342
36545
  },
@@ -35345,7 +36548,8 @@ RED.editor = (function() {
35345
36548
  text: RED._("common.label.done"),
35346
36549
  class: "primary",
35347
36550
  click: function() {
35348
- onComplete(expressionEditor.getValue(),expressionEditor.getCursorPosition());
36551
+ expressionEditor.saveView();
36552
+ if (onComplete) { onComplete(expressionEditor.getValue(), expressionEditor.getCursorPosition(), expressionEditor); }
35349
36553
  RED.tray.close();
35350
36554
  }
35351
36555
  }
@@ -35361,11 +36565,12 @@ RED.editor = (function() {
35361
36565
  expressionEditor.resize();
35362
36566
  },
35363
36567
  open: function(tray) {
35364
- var trayBody = tray.find('.red-ui-tray-body');
35365
36568
  var dialogForm = RED.editor.buildEditForm(tray.find('.red-ui-tray-body'),'dialog-form',type,'editor');
35366
36569
  expressionEditor = RED.editor.createEditor({
35367
36570
  id: 'node-input-js',
35368
36571
  mode: options.mode || 'ace/mode/javascript',
36572
+ stateId: options.stateId,
36573
+ focus: true,
35369
36574
  value: value,
35370
36575
  globals: {
35371
36576
  msg:true,
@@ -35383,19 +36588,16 @@ RED.editor = (function() {
35383
36588
  },
35384
36589
  extraLibs: options.extraLibs
35385
36590
  });
35386
- if (options.cursor) {
36591
+ if (options.cursor && !expressionEditor._initState) {
35387
36592
  expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false);
35388
36593
  }
35389
36594
  dialogForm.i18n();
35390
- setTimeout(function() {
35391
- expressionEditor.focus();
35392
- },300);
35393
36595
  },
35394
36596
  close: function() {
35395
- expressionEditor.destroy();
35396
36597
  if (options.onclose) {
35397
36598
  options.onclose();
35398
36599
  }
36600
+ expressionEditor.destroy();
35399
36601
  },
35400
36602
  show: function() {}
35401
36603
  }
@@ -35428,7 +36630,9 @@ RED.editor = (function() {
35428
36630
  '<ul id="red-ui-editor-type-json-tabs"></ul>'+
35429
36631
  '<div id="red-ui-editor-type-json-tab-raw" class="red-ui-editor-type-json-tab-content hide">'+
35430
36632
  '<div class="form-row" style="margin-bottom: 3px; text-align: right;">'+
35431
- '<button id="node-input-json-reformat" class="red-ui-button red-ui-button-small"><span data-i18n="jsonEditor.format"></span></button>'+
36633
+ '<span class="button-group">'+
36634
+ '<button id="node-input-json-reformat" class="red-ui-button red-ui-button-small"><span data-i18n="jsonEditor.format"></span></button>'+
36635
+ '<span class="button-group">'+
35432
36636
  '</div>'+
35433
36637
  '<div class="form-row node-text-editor-row">'+
35434
36638
  '<div style="height: 200px;min-height: 150px;" class="node-text-editor" id="node-input-json"></div>'+
@@ -35441,7 +36645,7 @@ RED.editor = (function() {
35441
36645
 
35442
36646
  var activeTab;
35443
36647
 
35444
- function insertNewItem(parent,index,copyIndex) {
36648
+ function insertNewItem(parent,index,copyIndex,readOnly) {
35445
36649
  var newValue = "";
35446
36650
 
35447
36651
  if (parent.children.length > 0) {
@@ -35467,26 +36671,26 @@ RED.editor = (function() {
35467
36671
  newKey = keyRoot+"-"+(keySuffix++);
35468
36672
  }
35469
36673
  }
35470
- var newItem = handleItem(newKey,newValue,parent.depth+1,parent);
36674
+ var newItem = handleItem(newKey,newValue,parent.depth+1,parent,readOnly);
35471
36675
  parent.treeList.insertChildAt(newItem, index, true);
35472
36676
  parent.treeList.expand();
35473
36677
  }
35474
- function showObjectMenu(button,item) {
36678
+ function showObjectMenu(button,item,readOnly) {
35475
36679
  var elementPos = button.offset();
35476
36680
  var options = [];
35477
36681
  if (item.parent) {
35478
36682
  options.push({id:"red-ui-editor-type-json-menu-insert-above", icon:"fa fa-toggle-up", label:RED._('jsonEditor.insertAbove'),onselect:function(){
35479
36683
  var index = item.parent.children.indexOf(item);
35480
- insertNewItem(item.parent,index,index);
36684
+ insertNewItem(item.parent,index,index,readOnly);
35481
36685
  }});
35482
36686
  options.push({id:"red-ui-editor-type-json-menu-insert-below", icon:"fa fa-toggle-down", label:RED._('jsonEditor.insertBelow'),onselect:function(){
35483
36687
  var index = item.parent.children.indexOf(item)+1;
35484
- insertNewItem(item.parent,index,index-1);
36688
+ insertNewItem(item.parent,index,index-1,readOnly);
35485
36689
  }});
35486
36690
  }
35487
36691
  if (item.type === 'array' || item.type === 'object') {
35488
36692
  options.push({id:"red-ui-editor-type-json-menu-add-child", icon:"fa fa-plus", label:RED._('jsonEditor.addItem'),onselect:function(){
35489
- insertNewItem(item,item.children.length,item.children.length-1);
36693
+ insertNewItem(item,item.children.length,item.children.length-1,readOnly);
35490
36694
  }});
35491
36695
  }
35492
36696
  if (item.parent) {
@@ -35528,7 +36732,7 @@ RED.editor = (function() {
35528
36732
  newKey = keyRoot+"-"+(keySuffix++);
35529
36733
  }
35530
36734
  }
35531
- var newItem = handleItem(newKey,convertToObject(item),item.parent.depth+1,item.parent);
36735
+ var newItem = handleItem(newKey,convertToObject(item),item.parent.depth+1,item.parent,readOnly);
35532
36736
  var index = item.parent.children.indexOf(item)+1;
35533
36737
 
35534
36738
  item.parent.treeList.insertChildAt(newItem, index, true);
@@ -35578,24 +36782,24 @@ RED.editor = (function() {
35578
36782
  menuOptionMenu.show();
35579
36783
  }
35580
36784
 
35581
- function parseObject(obj,depth,parent) {
36785
+ function parseObject(obj,depth,parent,readOnly) {
35582
36786
  var result = [];
35583
36787
  for (var prop in obj) {
35584
36788
  if (obj.hasOwnProperty(prop)) {
35585
- result.push(handleItem(prop,obj[prop],depth,parent));
36789
+ result.push(handleItem(prop,obj[prop],depth,parent,readOnly));
35586
36790
  }
35587
36791
  }
35588
36792
  return result;
35589
36793
  }
35590
- function parseArray(obj,depth,parent) {
36794
+ function parseArray(obj,depth,parent,readOnly) {
35591
36795
  var result = [];
35592
36796
  var l = obj.length;
35593
36797
  for (var i=0;i<l;i++) {
35594
- result.push(handleItem(i,obj[i],depth,parent));
36798
+ result.push(handleItem(i,obj[i],depth,parent,readOnly));
35595
36799
  }
35596
36800
  return result;
35597
36801
  }
35598
- function handleItem(key,val,depth,parent) {
36802
+ function handleItem(key,val,depth,parent,readOnly) {
35599
36803
  var item = {depth:depth, type: typeof val};
35600
36804
  var container = $('<span class="red-ui-editor-type-json-editor-label">');
35601
36805
  if (key != null) {
@@ -35611,11 +36815,14 @@ RED.editor = (function() {
35611
36815
  if (parent && parent.type === "array") {
35612
36816
  keyLabel.addClass("red-ui-editor-type-json-editor-label-array-key")
35613
36817
  }
35614
-
36818
+ if(readOnly) {
36819
+ keyLabel.addClass("readonly")
36820
+ }
35615
36821
  keyLabel.on("click", function(evt) {
35616
36822
  if (item.parent.type === 'array') {
35617
36823
  return;
35618
36824
  }
36825
+ if (readOnly) { return; }
35619
36826
  evt.preventDefault();
35620
36827
  evt.stopPropagation();
35621
36828
  var w = Math.max(150,keyLabel.width());
@@ -35660,10 +36867,10 @@ RED.editor = (function() {
35660
36867
  item.expanded = depth < 2;
35661
36868
  item.type = "array";
35662
36869
  item.deferBuild = depth >= 2;
35663
- item.children = parseArray(val,depth+1,item);
36870
+ item.children = parseArray(val,depth+1,item,readOnly);
35664
36871
  } else if (val !== null && item.type === "object") {
35665
36872
  item.expanded = depth < 2;
35666
- item.children = parseObject(val,depth+1,item);
36873
+ item.children = parseObject(val,depth+1,item,readOnly);
35667
36874
  item.deferBuild = depth >= 2;
35668
36875
  } else {
35669
36876
  item.value = val;
@@ -35694,7 +36901,11 @@ RED.editor = (function() {
35694
36901
  //
35695
36902
  var orphanedChildren;
35696
36903
  var valueLabel = $('<span class="red-ui-editor-type-json-editor-label-value">').addClass(valClass).text(valValue).appendTo(container);
36904
+ if (readOnly) {
36905
+ valueLabel.addClass("readonly")
36906
+ }
35697
36907
  valueLabel.on("click", function(evt) {
36908
+ if (readOnly) { return; }
35698
36909
  evt.preventDefault();
35699
36910
  evt.stopPropagation();
35700
36911
  if (valType === 'str') {
@@ -35802,17 +37013,19 @@ RED.editor = (function() {
35802
37013
  valueLabel.hide();
35803
37014
  })
35804
37015
  item.gutter = $('<span class="red-ui-editor-type-json-editor-item-gutter"></span>');
35805
-
35806
- if (parent) {//red-ui-editor-type-json-editor-item-handle
35807
- $('<span class="red-ui-editor-type-json-editor-item-handle"><i class="fa fa-bars"></span>').appendTo(item.gutter);
35808
- } else {
35809
- $('<span></span>').appendTo(item.gutter);
37016
+ if(!readOnly) {
37017
+ if (parent) {
37018
+ $('<span class="red-ui-editor-type-json-editor-item-handle"><i class="fa fa-bars"></span>').appendTo(item.gutter);
37019
+ } else {
37020
+ $('<span></span>').appendTo(item.gutter);
37021
+ }
37022
+ $('<button type="button" class="editor-button editor-button-small"><i class="fa fa-caret-down"></button>').appendTo(item.gutter).on("click", function(evt) {
37023
+ evt.preventDefault();
37024
+ evt.stopPropagation();
37025
+ showObjectMenu($(this), item, readOnly);
37026
+ });
35810
37027
  }
35811
- $('<button type="button" class="editor-button editor-button-small"><i class="fa fa-caret-down"></button>').appendTo(item.gutter).on("click", function(evt) {
35812
- evt.preventDefault();
35813
- evt.stopPropagation();
35814
- showObjectMenu($(this), item);
35815
- });
37028
+
35816
37029
  item.element = container;
35817
37030
  return item;
35818
37031
  }
@@ -35841,6 +37054,7 @@ RED.editor = (function() {
35841
37054
  var definition = {
35842
37055
  show: function(options) {
35843
37056
  var value = options.value;
37057
+ var onCancel = options.cancel;
35844
37058
  var onComplete = options.complete;
35845
37059
  var type = "_json"
35846
37060
  if ($("script[data-template-name='"+type+"']").length === 0) {
@@ -35862,15 +37076,16 @@ RED.editor = (function() {
35862
37076
  }
35863
37077
  }
35864
37078
  var rootNode;
35865
-
35866
37079
  var trayOptions = {
35867
37080
  title: options.title,
37081
+ focusElement: options.focusElement,
35868
37082
  width: options.width||700,
35869
37083
  buttons: [
35870
37084
  {
35871
37085
  id: "node-dialog-cancel",
35872
37086
  text: RED._("common.label.cancel"),
35873
37087
  click: function() {
37088
+ if (onCancel) { onCancel(); }
35874
37089
  RED.tray.close();
35875
37090
  }
35876
37091
  },
@@ -35892,7 +37107,8 @@ RED.editor = (function() {
35892
37107
  } else if (activeTab === "json-raw") {
35893
37108
  result = expressionEditor.getValue();
35894
37109
  }
35895
- if (onComplete) { onComplete(result) }
37110
+ expressionEditor.saveView();
37111
+ if (onComplete) { onComplete(result,null,expressionEditor) }
35896
37112
  RED.tray.close();
35897
37113
  }
35898
37114
  }
@@ -35905,7 +37121,25 @@ RED.editor = (function() {
35905
37121
  open: function(tray) {
35906
37122
  var trayBody = tray.find('.red-ui-tray-body');
35907
37123
  var dialogForm = RED.editor.buildEditForm(tray.find('.red-ui-tray-body'),'dialog-form',type,'editor');
35908
-
37124
+ var toolbarButtons = options.toolbarButtons || [];
37125
+ if (toolbarButtons.length) {
37126
+ toolbarButtons.forEach(function (button) {
37127
+ var element = $('<button type="button" class="red-ui-button red-ui-button-small"> </button>')
37128
+ .insertBefore("#node-input-json-reformat")
37129
+ .on("click", function (evt) {
37130
+ evt.preventDefault();
37131
+ if (button.click !== undefined) {
37132
+ button.click.call(element, evt);
37133
+ }
37134
+ });
37135
+ if (button.id) { element.attr("id", button.id); }
37136
+ if (button.title) { element.attr("title", button.title); }
37137
+ if (button.icon) { element.append($("<i></i>").attr("class", button.icon)); }
37138
+ if (button.label || button.text) {
37139
+ element.append($("<span></span>").text(" " + (button.label || button.text)));
37140
+ }
37141
+ });
37142
+ }
35909
37143
  var container = $("#red-ui-editor-type-json-tab-ui-container").css({"height":"100%"});
35910
37144
  var filterDepth = Infinity;
35911
37145
  var list = $('<div class="red-ui-debug-msg-payload red-ui-editor-type-json-editor">').appendTo(container).treeList({
@@ -35935,13 +37169,15 @@ RED.editor = (function() {
35935
37169
  })
35936
37170
  });
35937
37171
 
35938
-
35939
37172
  expressionEditor = RED.editor.createEditor({
35940
37173
  id: 'node-input-json',
35941
- value: "",
35942
- mode:"ace/mode/json"
37174
+ value: value||"",
37175
+ mode:"ace/mode/json",
37176
+ readOnly: !!options.readOnly,
37177
+ stateId: options.stateId,
37178
+ focus: true
35943
37179
  });
35944
- expressionEditor.getSession().setValue(value||"",-1);
37180
+
35945
37181
  if (options.requireValid) {
35946
37182
  expressionEditor.getSession().on('change', function() {
35947
37183
  clearTimeout(changeTimer);
@@ -35978,7 +37214,7 @@ RED.editor = (function() {
35978
37214
  var raw = expressionEditor.getValue().trim() ||"{}";
35979
37215
  try {
35980
37216
  var parsed = JSON.parse(raw);
35981
- rootNode = handleItem(null,parsed,0,null);
37217
+ rootNode = handleItem(null,parsed,0,null,options.readOnly);
35982
37218
  rootNode.class = "red-ui-editor-type-json-root-node"
35983
37219
  list.treeList('data',[rootNode]);
35984
37220
  } catch(err) {
@@ -35996,17 +37232,15 @@ RED.editor = (function() {
35996
37232
 
35997
37233
  tabs.addTab({
35998
37234
  id: 'json-raw',
35999
- label: RED._('jsonEditor.rawMode'),
37235
+ label: options.readOnly ? RED._('jsonEditor.rawMode-readonly') : RED._('jsonEditor.rawMode'),
36000
37236
  content: $("#red-ui-editor-type-json-tab-raw")
36001
37237
  });
36002
37238
  tabs.addTab({
36003
37239
  id: 'json-ui',
36004
- label: RED._('jsonEditor.uiMode'),
37240
+ label: options.readOnly ? RED._('jsonEditor.uiMode-readonly') : RED._('jsonEditor.uiMode'),
36005
37241
  content: $("#red-ui-editor-type-json-tab-ui")
36006
37242
  });
36007
37243
  finishedBuild = true;
36008
-
36009
-
36010
37244
  },
36011
37245
  close: function() {
36012
37246
  if (options.onclose) {
@@ -36077,24 +37311,26 @@ RED.editor = (function() {
36077
37311
  var definition = {
36078
37312
  show: function(options) {
36079
37313
  var value = options.value;
37314
+ var onCancel = options.cancel;
36080
37315
  var onComplete = options.complete;
36081
37316
  var type = "_markdown"
36082
37317
  if ($("script[data-template-name='"+type+"']").length === 0) {
36083
37318
  $(template).appendTo("#red-ui-editor-node-configs");
36084
37319
  }
36085
37320
 
36086
-
36087
37321
  RED.view.state(RED.state.EDITING);
36088
37322
  var expressionEditor;
36089
37323
 
36090
37324
  var trayOptions = {
36091
37325
  title: options.title,
37326
+ focusElement: options.focusElement,
36092
37327
  width: options.width||Infinity,
36093
37328
  buttons: [
36094
37329
  {
36095
37330
  id: "node-dialog-cancel",
36096
37331
  text: RED._("common.label.cancel"),
36097
37332
  click: function() {
37333
+ if (onCancel) { onCancel(); }
36098
37334
  RED.tray.close();
36099
37335
  }
36100
37336
  },
@@ -36103,7 +37339,8 @@ RED.editor = (function() {
36103
37339
  text: RED._("common.label.done"),
36104
37340
  class: "primary",
36105
37341
  click: function() {
36106
- onComplete(expressionEditor.getValue(),expressionEditor.getCursorPosition());
37342
+ expressionEditor.saveView();
37343
+ if (onComplete) { onComplete(expressionEditor.getValue(),expressionEditor.getCursorPosition(), expressionEditor); }
36107
37344
  RED.tray.close();
36108
37345
  }
36109
37346
  }
@@ -36122,6 +37359,8 @@ RED.editor = (function() {
36122
37359
  expressionEditor = RED.editor.createEditor({
36123
37360
  id: 'red-ui-editor-type-markdown',
36124
37361
  value: value,
37362
+ stateId: options.stateId,
37363
+ focus: true,
36125
37364
  mode:"ace/mode/markdown",
36126
37365
  expandable: false
36127
37366
  });
@@ -36166,17 +37405,17 @@ RED.editor = (function() {
36166
37405
  });
36167
37406
  RED.popover.tooltip($("#node-btn-markdown-preview"), RED._("markdownEditor.toggle-preview"));
36168
37407
 
36169
- if (options.cursor) {
37408
+ if (options.cursor && !expressionEditor._initState) {
36170
37409
  expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false);
36171
37410
  }
36172
37411
 
36173
37412
  dialogForm.i18n();
36174
37413
  },
36175
37414
  close: function() {
36176
- expressionEditor.destroy();
36177
37415
  if (options.onclose) {
36178
37416
  options.onclose();
36179
37417
  }
37418
+ expressionEditor.destroy();
36180
37419
  },
36181
37420
  show: function() {}
36182
37421
  }
@@ -36191,7 +37430,7 @@ RED.editor = (function() {
36191
37430
  'b': { before:"**", after: "**", tooltip: RED._("markdownEditor.bold")},
36192
37431
  'i': { before:"_", after: "_", tooltip: RED._("markdownEditor.italic")},
36193
37432
  'code': { before:"`", after: "`", tooltip: RED._("markdownEditor.code")},
36194
- 'ol': { before:" * ", newline: true, tooltip: RED._("markdownEditor.ordered-list")},
37433
+ 'ol': { before:" 1. ", newline: true, tooltip: RED._("markdownEditor.ordered-list")},
36195
37434
  'ul': { before:" - ", newline: true, tooltip: RED._("markdownEditor.unordered-list")},
36196
37435
  'bq': { before:"> ", newline: true, tooltip: RED._("markdownEditor.quote")},
36197
37436
  'link': { before:"[", after: "]()", tooltip: RED._("markdownEditor.link")},
@@ -36260,6 +37499,7 @@ RED.editor = (function() {
36260
37499
  var definition = {
36261
37500
  show: function(options) {
36262
37501
  var value = options.value;
37502
+ var onCancel = options.cancel;
36263
37503
  var onComplete = options.complete;
36264
37504
  var type = "_text"
36265
37505
  if ($("script[data-template-name='"+type+"']").length === 0) {
@@ -36267,16 +37507,16 @@ RED.editor = (function() {
36267
37507
  }
36268
37508
  RED.view.state(RED.state.EDITING);
36269
37509
  var expressionEditor;
36270
- var changeTimer;
36271
-
36272
37510
  var trayOptions = {
36273
37511
  title: options.title,
37512
+ focusElement: options.focusElement,
36274
37513
  width: options.width||"inherit",
36275
37514
  buttons: [
36276
37515
  {
36277
37516
  id: "node-dialog-cancel",
36278
37517
  text: RED._("common.label.cancel"),
36279
37518
  click: function() {
37519
+ if(onCancel) { onCancel(); }
36280
37520
  RED.tray.close();
36281
37521
  }
36282
37522
  },
@@ -36285,7 +37525,8 @@ RED.editor = (function() {
36285
37525
  text: RED._("common.label.done"),
36286
37526
  class: "primary",
36287
37527
  click: function() {
36288
- onComplete(expressionEditor.getValue(),expressionEditor.getCursorPosition());
37528
+ expressionEditor.saveView();
37529
+ if (onComplete) { onComplete(expressionEditor.getValue(),expressionEditor.getCursorPosition(),expressionEditor);}
36289
37530
  RED.tray.close();
36290
37531
  }
36291
37532
  }
@@ -36294,31 +37535,27 @@ RED.editor = (function() {
36294
37535
  var rows = $("#dialog-form>div:not(.node-text-editor-row)");
36295
37536
  var editorRow = $("#dialog-form>div.node-text-editor-row");
36296
37537
  var height = $("#dialog-form").height();
36297
- // for (var i=0;i<rows.size();i++) {
36298
- // height -= $(rows[i]).outerHeight(true);
36299
- // }
36300
- // height -= (parseInt($("#dialog-form").css("marginTop"))+parseInt($("#dialog-form").css("marginBottom")));
36301
37538
  $(".node-text-editor").css("height",height+"px");
36302
37539
  expressionEditor.resize();
36303
37540
  },
36304
37541
  open: function(tray) {
36305
- var trayBody = tray.find('.red-ui-tray-body');
36306
37542
  var dialogForm = RED.editor.buildEditForm(tray.find('.red-ui-tray-body'),'dialog-form',type,'editor');
36307
37543
  expressionEditor = RED.editor.createEditor({
36308
37544
  id: 'node-input-text',
36309
- value: "",
36310
- mode:"ace/mode/"+(options.mode||"text")
37545
+ value: value||"",
37546
+ stateId: options.stateId,
37547
+ mode:"ace/mode/"+(options.mode||"text"),
37548
+ focus: true,
36311
37549
  });
36312
- expressionEditor.getSession().setValue(value||"",-1);
36313
- if (options.cursor) {
37550
+ if (options.cursor && !expressionEditor._initState) {
36314
37551
  expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false);
36315
37552
  }
36316
37553
  },
36317
37554
  close: function() {
36318
- expressionEditor.destroy();
36319
37555
  if (options.onclose) {
36320
37556
  options.onclose();
36321
37557
  }
37558
+ expressionEditor.destroy();
36322
37559
  },
36323
37560
  show: function() {}
36324
37561
  }
@@ -36409,6 +37646,9 @@ RED.editor.codeEditor.ace = (function() {
36409
37646
  }
36410
37647
  },100);
36411
37648
  }
37649
+ if (!options.stateId && options.stateId !== false) {
37650
+ options.stateId = RED.editor.generateViewStateId("ace", options, (options.mode || options.title).split("/").pop());
37651
+ }
36412
37652
  if (options.mode === 'ace/mode/markdown') {
36413
37653
  $(el).addClass("red-ui-editor-text-container-toolbar");
36414
37654
  editor.toolbar = RED.editor.customEditTypes['_markdown'].buildToolbar(toolbarRow,editor);
@@ -36421,11 +37661,15 @@ RED.editor.codeEditor.ace = (function() {
36421
37661
  RED.editor.editMarkdown({
36422
37662
  value: value,
36423
37663
  width: "Infinity",
36424
- cursor: editor.getCursorPosition(),
37664
+ stateId: options.stateId,
37665
+ focus: true,
37666
+ cancel: function () {
37667
+ editor.focus();
37668
+ },
36425
37669
  complete: function(v,cursor) {
36426
37670
  editor.setValue(v, -1);
36427
- editor.gotoLine(cursor.row+1,cursor.column,false);
36428
37671
  setTimeout(function() {
37672
+ editor.restoreView();
36429
37673
  editor.focus();
36430
37674
  },300);
36431
37675
  }
@@ -36446,11 +37690,56 @@ RED.editor.codeEditor.ace = (function() {
36446
37690
  editor._destroy = editor.destroy;
36447
37691
  editor.destroy = function() {
36448
37692
  try {
37693
+ editor.saveView();
37694
+ editor._initState = null;
36449
37695
  this._destroy();
36450
37696
  } catch (e) { }
36451
37697
  $(el).remove();
36452
37698
  $(toolbarRow).remove();
36453
37699
  }
37700
+ editor.on("blur", function () {
37701
+ editor.focusMemory = false;
37702
+ editor.saveView();
37703
+ })
37704
+ editor.on("focus", function () {
37705
+ if (editor._initState) {
37706
+ editor.restoreView(editor._initState);
37707
+ editor._initState = null;
37708
+ }
37709
+ })
37710
+ editor.getView = function () {
37711
+ var session = editor.getSession();
37712
+ return {
37713
+ selection: session.selection.toJSON(),
37714
+ scrollTop: session.getScrollTop(),
37715
+ scrollLeft: session.getScrollLeft(),
37716
+ options: session.getOptions()
37717
+ }
37718
+ }
37719
+ editor.saveView = function () {
37720
+ if (!options.stateId) { return; } //only possible if created with a unique stateId
37721
+ window._editorStateAce = window._editorStateAce || {};
37722
+ var state = editor.getView();
37723
+ window._editorStateAce[options.stateId] = state;
37724
+ return state;
37725
+ }
37726
+ editor.restoreView = function (state) {
37727
+ if (!options.stateId) { return; } //only possible if created with a unique stateId
37728
+ window._editorStateAce = window._editorStateAce || {};
37729
+ var _state = state || window._editorStateAce[options.stateId];
37730
+ if (!_state) { return; } //no view state available
37731
+ try {
37732
+ var session = editor.getSession();
37733
+ session.setOptions(_state.options);
37734
+ session.selection.fromJSON(_state.selection);
37735
+ session.setScrollTop(_state.scrollTop);
37736
+ session.setScrollLeft(_state.scrollLeft);
37737
+ editor._initState = _state;
37738
+ } catch (error) {
37739
+ delete window._editorStateMonaco[options.stateId];
37740
+ }
37741
+ };
37742
+ editor.restoreView();
36454
37743
  editor.type = type;
36455
37744
  return editor;
36456
37745
  }
@@ -36652,7 +37941,7 @@ RED.editor.codeEditor.monaco = (function() {
36652
37941
 
36653
37942
  options = options || {};
36654
37943
  window.MonacoEnvironment = window.MonacoEnvironment || {};
36655
- window.MonacoEnvironment.getWorkerUrl = function (moduleId, label) {
37944
+ window.MonacoEnvironment.getWorkerUrl = window.MonacoEnvironment.getWorkerUrl || function (moduleId, label) {
36656
37945
  if (label === 'json') { return './vendor/monaco/dist/json.worker.js'; }
36657
37946
  if (label === 'css' || label === 'scss') { return './vendor/monaco/dist/css.worker.js'; }
36658
37947
  if (label === 'html' || label === 'handlebars') { return './vendor/monaco/dist/html.worker.js'; }
@@ -37058,7 +38347,7 @@ RED.editor.codeEditor.monaco = (function() {
37058
38347
  createMonacoCompletionItem("set (flow context)", 'flow.set("${1:name}", ${1:value});','Set a value in flow context',range),
37059
38348
  createMonacoCompletionItem("get (global context)", 'global.get("${1:name}");','Get a value from global context',range),
37060
38349
  createMonacoCompletionItem("set (global context)", 'global.set("${1:name}", ${1:value});','Set a value in global context',range),
37061
- createMonacoCompletionItem("get (env)", 'env.get("${1:name}");','Get env variable value',range),
38350
+ createMonacoCompletionItem("get (env)", 'env.get("${1|NR_NODE_ID,NR_NODE_NAME,NR_NODE_PATH,NR_GROUP_ID,NR_GROUP_NAME,NR_FLOW_ID,NR_FLOW_NAME|}");','Get env variable value',range),
37062
38351
  createMonacoCompletionItem("cloneMessage (RED.util)", 'RED.util.cloneMessage(${1:msg});',
37063
38352
  ["```typescript",
37064
38353
  "RED.util.cloneMessage<T extends registry.NodeMessage>(msg: T): T",
@@ -37228,13 +38517,25 @@ RED.editor.codeEditor.monaco = (function() {
37228
38517
  mode = "html";
37229
38518
  break;
37230
38519
  case "appcache":
38520
+ case "sh":
38521
+ case "bash":
37231
38522
  mode = "shell";
37232
38523
  break;
38524
+ case "batchfile":
38525
+ mode = "bat";
38526
+ break;
38527
+ case "protobuf":
38528
+ mode = "proto";
38529
+ break;
37233
38530
  //TODO: add other compatability types.
37234
38531
  }
37235
38532
  return mode;
37236
38533
  }
37237
38534
 
38535
+
38536
+ if(!options.stateId && options.stateId !== false) {
38537
+ options.stateId = RED.editor.generateViewStateId("monaco", options, (options.mode || options.title).split("/").pop());
38538
+ }
37238
38539
  var el = options.element || $("#"+options.id)[0];
37239
38540
  var toolbarRow = $("<div>").appendTo(el);
37240
38541
  el = $("<div>").appendTo(el).addClass("red-ui-editor-text-container")[0];
@@ -37579,6 +38880,7 @@ RED.editor.codeEditor.monaco = (function() {
37579
38880
  try {
37580
38881
  var m = this.getModel();
37581
38882
  if(m && !m.isDisposed()) {
38883
+ ed._initState = null;
37582
38884
  m.dispose();
37583
38885
  }
37584
38886
  this.setModel(null);
@@ -37632,7 +38934,7 @@ RED.editor.codeEditor.monaco = (function() {
37632
38934
  try {
37633
38935
  var _model = ed.getModel();
37634
38936
  if (_model !== null) {
37635
- var id = _model.getModeId(); // e.g. javascript
38937
+ var id = _model._languageId; // e.g. javascript
37636
38938
  var ra = _model._associatedResource.authority; //e.g. model
37637
38939
  var rp = _model._associatedResource.path; //e.g. /18
37638
38940
  var rs = _model._associatedResource.scheme; //e.g. inmemory
@@ -37724,14 +39026,7 @@ RED.editor.codeEditor.monaco = (function() {
37724
39026
  //#endregion "ACE compatability"
37725
39027
 
37726
39028
  //final setup
37727
- if (options.cursor) {
37728
- var row = options.cursor.row || options.cursor.lineNumber;
37729
- var col = options.cursor.column || options.cursor.col;
37730
- ed.gotoLine(row, col);
37731
- }
37732
- if (options.focus) {
37733
- ed.focus();
37734
- }
39029
+ ed.focusMemory = options.focus;
37735
39030
  ed._mode = editorOptions.language;
37736
39031
 
37737
39032
  //as models are signleton, consts and let are avialable to other javascript instances
@@ -37743,11 +39038,12 @@ RED.editor.codeEditor.monaco = (function() {
37743
39038
  }
37744
39039
 
37745
39040
  ed.onDidBlurEditorWidget(function() {
39041
+ ed.focusMemory = false;
39042
+ ed.saveView();
37746
39043
  if(isVisible(el) == false) {
37747
39044
  onVisibilityChange(false, 0, el);
37748
39045
  }
37749
39046
  });
37750
-
37751
39047
  ed.onDidFocusEditorWidget(function() {
37752
39048
  onVisibilityChange(true, 10, el);
37753
39049
  });
@@ -37781,17 +39077,33 @@ RED.editor.codeEditor.monaco = (function() {
37781
39077
  }
37782
39078
 
37783
39079
  function onVisibilityChange(visible, delay, element) {
37784
- if(visible) {
37785
- if(ed._mode == "javascript" && ed._tempMode == "text") {
39080
+ delay = delay || 50;
39081
+ if (visible) {
39082
+ if (ed.focusMemory) {
39083
+ setTimeout(function () {
39084
+ if (element.parentElement) { //ensure el is still in DOM
39085
+ ed.focus();
39086
+ }
39087
+ }, 300)
39088
+ }
39089
+ if (ed._initState) {
39090
+ setTimeout(function () {
39091
+ if (element.parentElement) { //ensure el is still in DOM
39092
+ ed.restoreViewState(ed._initState);
39093
+ ed._initState = null;
39094
+ }
39095
+ }, delay);
39096
+ }
39097
+ if (ed._mode == "javascript" && ed._tempMode == "text") {
37786
39098
  ed._tempMode = "";
37787
- setTimeout(function() {
37788
- if(element.parentElement) { //ensure el is still in DOM
39099
+ setTimeout(function () {
39100
+ if (element.parentElement) { //ensure el is still in DOM
37789
39101
  ed.setMode('javascript', undefined, false);
37790
39102
  }
37791
- }, delay || 50);
39103
+ }, delay);
37792
39104
  }
37793
- } else if(ed._mode == "javascript" && ed._tempMode != "text") {
37794
- if(element.parentElement) { //ensure el is still in DOM
39105
+ } else if (ed._mode == "javascript" && ed._tempMode != "text") {
39106
+ if (element.parentElement) { //ensure el is still in DOM
37795
39107
  ed.setMode('text', undefined, false);
37796
39108
  ed._tempMode = "text";
37797
39109
  }
@@ -37810,15 +39122,19 @@ RED.editor.codeEditor.monaco = (function() {
37810
39122
  expandButton.on("click", function (e) {
37811
39123
  e.preventDefault();
37812
39124
  var value = ed.getValue();
39125
+ ed.saveView();
37813
39126
  RED.editor.editMarkdown({
37814
39127
  value: value,
37815
39128
  width: "Infinity",
37816
- cursor: ed.getCursorPosition(),
39129
+ stateId: options.stateId,
39130
+ cancel: function () {
39131
+ ed.focus();
39132
+ },
37817
39133
  complete: function (v, cursor) {
37818
39134
  ed.setValue(v, -1);
37819
- ed.gotoLine(cursor.row + 1, cursor.column, false);
37820
39135
  setTimeout(function () {
37821
39136
  ed.focus();
39137
+ ed.restoreView();
37822
39138
  }, 300);
37823
39139
  }
37824
39140
  })
@@ -37834,7 +39150,37 @@ RED.editor.codeEditor.monaco = (function() {
37834
39150
  autoClose: 50
37835
39151
  });
37836
39152
  }
37837
-
39153
+ ed.getView = function () {
39154
+ return ed.saveViewState();
39155
+ }
39156
+ ed.saveView = function (debuginfo) {
39157
+ if (!options.stateId) { return; } //only possible if created with a unique stateId
39158
+ window._editorStateMonaco = window._editorStateMonaco || {};
39159
+ var state = ed.getView();
39160
+ window._editorStateMonaco[options.stateId] = state;
39161
+ return state;
39162
+ }
39163
+ ed.restoreView = function (state) {
39164
+ if (!options.stateId) { return; } //only possible if created with a unique stateId
39165
+ window._editorStateMonaco = window._editorStateMonaco || {};
39166
+ var _state = state || window._editorStateMonaco[options.stateId];
39167
+ if (!_state) { return; } //no view state available
39168
+ try {
39169
+ if (ed.type) { //is editor already initialised?
39170
+ ed.restoreViewState(_state);
39171
+ } else {
39172
+ ed._initState = _state;
39173
+ }
39174
+ } catch (error) {
39175
+ delete window._editorStateMonaco[options.stateId];
39176
+ }
39177
+ };
39178
+ ed.restoreView();
39179
+ if (options.cursor && !ed._initState) {
39180
+ var row = options.cursor.row || options.cursor.lineNumber;
39181
+ var col = options.cursor.column || options.cursor.col;
39182
+ ed.gotoLine(row, col);
39183
+ }
37838
39184
  ed.type = type;
37839
39185
  return ed;
37840
39186
  }
@@ -38163,7 +39509,13 @@ RED.eventLog = (function() {
38163
39509
  raiseTrayZ();
38164
39510
  handleWindowResize();//cause call to monaco layout
38165
39511
  },200);
38166
- body.find(":focusable:first").trigger("focus");
39512
+ if(!options.hasOwnProperty("focusElement")) {
39513
+ //focusElement is not inside options - default to focusing 1st
39514
+ body.find(":focusable:first").trigger("focus");
39515
+ } else if(options.focusElement !== false) {
39516
+ //focusElement IS specified, focus that instead (if not false)
39517
+ $(options.focusElement).trigger("focus");
39518
+ }
38167
39519
 
38168
39520
  },150);
38169
39521
  el.css({right:0});
@@ -39316,6 +40668,7 @@ RED.clipboard = (function() {
39316
40668
  try {
39317
40669
  RED.view.importNodes(newNodes, importOptions);
39318
40670
  } catch(error) {
40671
+ console.log(error.importConfig)
39319
40672
  // Thrown for import_conflict
39320
40673
  confirmImport(error.importConfig, newNodes, importOptions);
39321
40674
  }
@@ -40864,6 +42217,8 @@ RED.search = (function() {
40864
42217
  var searchHistory = [];
40865
42218
  var index = {};
40866
42219
  var currentResults = [];
42220
+ var activeResults = [];
42221
+ var currentIndex = 0;
40867
42222
  var previousActiveElement;
40868
42223
 
40869
42224
  function indexProperty(node,label,property) {
@@ -40958,6 +42313,7 @@ RED.search = (function() {
40958
42313
  val = extractFlag(val,"config",flags);
40959
42314
  val = extractFlag(val,"subflow",flags);
40960
42315
  val = extractFlag(val,"hidden",flags);
42316
+ val = extractFlag(val,"modified",flags);
40961
42317
  // uses:<node-id>
40962
42318
  val = extractValue(val,"uses",flags);
40963
42319
 
@@ -41003,6 +42359,11 @@ RED.search = (function() {
41003
42359
  continue;
41004
42360
  }
41005
42361
  }
42362
+ if (flags.hasOwnProperty("modified")) {
42363
+ if (!node.node.changed && !node.node.moved) {
42364
+ continue;
42365
+ }
42366
+ }
41006
42367
  if (flags.hasOwnProperty("hidden")) {
41007
42368
  // Only tabs can be hidden
41008
42369
  if (node.node.type !== 'tab') {
@@ -41100,9 +42461,8 @@ RED.search = (function() {
41100
42461
  } else {
41101
42462
  searchResults.editableList('addItem',{});
41102
42463
  }
41103
-
41104
-
41105
- }
42464
+ },
42465
+ options: getSearchOptions()
41106
42466
  });
41107
42467
  var copySearchContainer = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-caret-right"></button>').appendTo(searchDiv).on('click', function(evt) {
41108
42468
  evt.preventDefault();
@@ -41163,7 +42523,8 @@ RED.search = (function() {
41163
42523
  }
41164
42524
  } else if (!$(children[selected]).hasClass("red-ui-search-historyHeader")) {
41165
42525
  if (currentResults.length > 0) {
41166
- reveal(currentResults[Math.max(0,selected)].node);
42526
+ currentIndex = Math.max(0,selected);
42527
+ reveal(currentResults[currentIndex].node);
41167
42528
  }
41168
42529
  }
41169
42530
  }
@@ -41247,6 +42608,7 @@ RED.search = (function() {
41247
42608
 
41248
42609
  div.on("click", function(evt) {
41249
42610
  evt.preventDefault();
42611
+ currentIndex = i;
41250
42612
  reveal(node);
41251
42613
  });
41252
42614
  }
@@ -41262,13 +42624,59 @@ RED.search = (function() {
41262
42624
  if (existingIndex > -1) {
41263
42625
  searchHistory.splice(existingIndex,1);
41264
42626
  }
41265
- searchHistory.unshift(searchInput.val());
41266
- hide();
42627
+ searchHistory.unshift(searchVal);
42628
+ $("#red-ui-view-searchtools-search").data("term", searchVal);
42629
+ activeResults = Object.assign([], currentResults);
42630
+ hide(null, activeResults.length > 0);
41267
42631
  RED.view.reveal(node.id);
41268
42632
  }
41269
42633
 
42634
+ function revealPrev() {
42635
+ if (disabled) {
42636
+ updateSearchToolbar();
42637
+ return;
42638
+ }
42639
+ if (!searchResults || !activeResults.length) {
42640
+ show();
42641
+ return;
42642
+ }
42643
+ if (currentIndex > 0) {
42644
+ currentIndex--;
42645
+ } else {
42646
+ currentIndex = activeResults.length - 1;
42647
+ }
42648
+ const n = activeResults[currentIndex];
42649
+ if (n && n.node && n.node.id) {
42650
+ RED.view.reveal(n.node.id);
42651
+ $("#red-ui-view-searchtools-prev").trigger("focus");
42652
+ }
42653
+ updateSearchToolbar();
42654
+ }
42655
+ function revealNext() {
42656
+ if (disabled) {
42657
+ updateSearchToolbar();
42658
+ return;
42659
+ }
42660
+ if (!searchResults || !activeResults.length) {
42661
+ show();
42662
+ return;
42663
+ }
42664
+ if (currentIndex < activeResults.length - 1) {
42665
+ currentIndex++
42666
+ } else {
42667
+ currentIndex = 0;
42668
+ }
42669
+ const n = activeResults[currentIndex];
42670
+ if (n && n.node && n.node.id) {
42671
+ RED.view.reveal(n.node.id);
42672
+ $("#red-ui-view-searchtools-next").trigger("focus");
42673
+ }
42674
+ updateSearchToolbar();
42675
+ }
42676
+
41270
42677
  function show(v) {
41271
42678
  if (disabled) {
42679
+ updateSearchToolbar();
41272
42680
  return;
41273
42681
  }
41274
42682
  if (!visible) {
@@ -41295,7 +42703,7 @@ RED.search = (function() {
41295
42703
  searchInput.trigger("focus");
41296
42704
  }
41297
42705
 
41298
- function hide() {
42706
+ function hide(el, keepSearchToolbar) {
41299
42707
  if (visible) {
41300
42708
  visible = false;
41301
42709
  $("#red-ui-header-shade").hide();
@@ -41309,13 +42717,37 @@ RED.search = (function() {
41309
42717
  });
41310
42718
  }
41311
42719
  RED.events.emit("search:close");
41312
- if (previousActiveElement) {
42720
+ if (previousActiveElement && (!keepSearchToolbar || !activeResults.length)) {
41313
42721
  $(previousActiveElement).trigger("focus");
41314
- previousActiveElement = null;
41315
42722
  }
42723
+ previousActiveElement = null;
42724
+ }
42725
+ if(!keepSearchToolbar) {
42726
+ clearActiveSearch();
42727
+ }
42728
+ updateSearchToolbar();
42729
+ if(keepSearchToolbar && activeResults.length) {
42730
+ $("#red-ui-view-searchtools-next").trigger("focus");
42731
+ }
42732
+ }
42733
+ function updateSearchToolbar() {
42734
+ if (!disabled && currentIndex >= 0 && activeResults && activeResults.length) {
42735
+ let term = $("#red-ui-view-searchtools-search").data("term") || "";
42736
+ if (term.length > 16) {
42737
+ term = term.substring(0, 12) + "..."
42738
+ }
42739
+ const i18nSearchCounterData = {
42740
+ term: term,
42741
+ result: (currentIndex + 1),
42742
+ count: activeResults.length
42743
+ }
42744
+ $("#red-ui-view-searchtools-counter").text(RED._('actions.search-counter', i18nSearchCounterData));
42745
+ $("#view-search-tools > :not(:first-child)").show(); //show other tools
42746
+ } else {
42747
+ clearActiveSearch();
42748
+ $("#view-search-tools > :not(:first-child)").hide(); //hide all but search button
41316
42749
  }
41317
42750
  }
41318
-
41319
42751
  function clearIndex() {
41320
42752
  index = {};
41321
42753
  }
@@ -41337,9 +42769,29 @@ RED.search = (function() {
41337
42769
  addItemToIndex(item);
41338
42770
  }
41339
42771
 
42772
+ function clearActiveSearch() {
42773
+ activeResults = [];
42774
+ currentIndex = 0;
42775
+ $("#red-ui-view-searchtools-search").data("term", "");
42776
+ }
42777
+
42778
+ function getSearchOptions() {
42779
+ return [
42780
+ {label:RED._("search.options.configNodes"), value:"is:config"},
42781
+ {label:RED._("search.options.unusedConfigNodes"), value:"is:config is:unused"},
42782
+ {label:RED._("search.options.modifiedNodes"), value:"is:modified"},
42783
+ {label:RED._("search.options.invalidNodes"), value: "is:invalid"},
42784
+ {label:RED._("search.options.uknownNodes"), value: "type:unknown"},
42785
+ {label:RED._("search.options.unusedSubflows"), value:"is:subflow is:unused"},
42786
+ {label:RED._("search.options.hiddenFlows"), value:"is:hidden"},
42787
+ ]
42788
+
42789
+ }
41340
42790
 
41341
42791
  function init() {
41342
42792
  RED.actions.add("core:search",show);
42793
+ RED.actions.add("core:search-previous",revealPrev);
42794
+ RED.actions.add("core:search-next",revealNext);
41343
42795
 
41344
42796
  RED.events.on("editor:open",function() { disabled = true; });
41345
42797
  RED.events.on("editor:close",function() { disabled = false; });
@@ -41350,11 +42802,21 @@ RED.search = (function() {
41350
42802
 
41351
42803
  RED.keyboard.add("red-ui-search","escape",hide);
41352
42804
 
42805
+ RED.keyboard.add("view-search-tools","escape",function() {
42806
+ clearActiveSearch();
42807
+ updateSearchToolbar();
42808
+ });
42809
+
41353
42810
  $("#red-ui-header-shade").on('mousedown',hide);
41354
42811
  $("#red-ui-editor-shade").on('mousedown',hide);
41355
42812
  $("#red-ui-palette-shade").on('mousedown',hide);
41356
42813
  $("#red-ui-sidebar-shade").on('mousedown',hide);
41357
42814
 
42815
+ $("#red-ui-view-searchtools-close").on("click", function close() {
42816
+ clearActiveSearch();
42817
+ updateSearchToolbar();
42818
+ });
42819
+ $("#red-ui-view-searchtools-close").trigger("click");
41358
42820
 
41359
42821
  RED.events.on("workspace:clear", clearIndex);
41360
42822
 
@@ -41380,7 +42842,8 @@ RED.search = (function() {
41380
42842
  init: init,
41381
42843
  show: show,
41382
42844
  hide: hide,
41383
- search: search
42845
+ search: search,
42846
+ getSearchOptions: getSearchOptions
41384
42847
  };
41385
42848
 
41386
42849
  })();
@@ -41789,17 +43252,21 @@ RED.actionList = (function() {
41789
43252
  var div = $('<div>',{class:"red-ui-search-result"}).appendTo(container);
41790
43253
 
41791
43254
  var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).appendTo(div);
41792
- var colour = RED.utils.getNodeColor(object.type,def);
43255
+ if (object.type === "junction") {
43256
+ nodeDiv.addClass("red-ui-palette-icon-junction");
43257
+ } else {
43258
+ var colour = RED.utils.getNodeColor(object.type,def);
43259
+ nodeDiv.css('backgroundColor',colour);
43260
+ }
41793
43261
  var icon_url = RED.utils.getNodeIcon(def);
41794
- nodeDiv.css('backgroundColor',colour);
41795
43262
 
41796
43263
  var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
41797
43264
  RED.utils.createIconElement(icon_url, iconContainer, false);
41798
43265
 
41799
- if (def.inputs > 0) {
43266
+ if (object.type !== "junction" && def.inputs > 0) {
41800
43267
  $('<div/>',{class:"red-ui-search-result-node-port"}).appendTo(nodeDiv);
41801
43268
  }
41802
- if (def.outputs > 0) {
43269
+ if (object.type !== "junction" && def.outputs > 0) {
41803
43270
  $('<div/>',{class:"red-ui-search-result-node-port red-ui-search-result-node-output"}).appendTo(nodeDiv);
41804
43271
  }
41805
43272
 
@@ -41931,8 +43398,8 @@ RED.actionList = (function() {
41931
43398
  return !filter ||
41932
43399
  (
41933
43400
  (!filter.type || type === filter.type) &&
41934
- (!filter.input || def.inputs > 0) &&
41935
- (!filter.output || def.outputs > 0)
43401
+ (!filter.input || type === 'junction' || def.inputs > 0) &&
43402
+ (!filter.output || type === 'junction' || def.outputs > 0)
41936
43403
  )
41937
43404
  }
41938
43405
  function refreshTypeList(opts) {
@@ -41941,7 +43408,7 @@ RED.actionList = (function() {
41941
43408
  searchInput.searchBox('value','').focus();
41942
43409
  selected = -1;
41943
43410
  var common = [
41944
- 'inject','debug','function','change','switch'
43411
+ 'inject','debug','function','change','switch','junction'
41945
43412
  ].filter(function(t) { return applyFilter(opts.filter,t,RED.nodes.getType(t)); });
41946
43413
 
41947
43414
  var recentlyUsed = Object.keys(typesUsed);
@@ -41966,6 +43433,9 @@ RED.actionList = (function() {
41966
43433
  var index = 0;
41967
43434
  for(i=0;i<common.length;i++) {
41968
43435
  var itemDef = RED.nodes.getType(common[i]);
43436
+ if (common[i] === 'junction') {
43437
+ itemDef = { inputs:1, outputs: 1, label: 'junction', type: 'junction'}
43438
+ }
41969
43439
  if (itemDef) {
41970
43440
  item = {
41971
43441
  type: common[i],
@@ -43682,8 +45152,10 @@ RED.group = (function() {
43682
45152
  }
43683
45153
  if (n.type === 'group') {
43684
45154
  RED.events.emit("groups:change",n)
43685
- } else {
45155
+ } else if (n.type !== 'junction') {
43686
45156
  RED.events.emit("nodes:change",n)
45157
+ } else {
45158
+ RED.events.emit("junctions:change",n)
43687
45159
  }
43688
45160
  })
43689
45161
  RED.nodes.removeGroup(g);
@@ -43877,8 +45349,10 @@ RED.group = (function() {
43877
45349
  group.h = Math.max(group.h,n.y+n.h/2+25-group.y);
43878
45350
  if (n.type === 'group') {
43879
45351
  RED.events.emit("groups:change",n)
43880
- } else {
45352
+ } else if (n.type !== 'junction') {
43881
45353
  RED.events.emit("nodes:change",n)
45354
+ } else {
45355
+ RED.events.emit("junctions:change",n)
43882
45356
  }
43883
45357
  }
43884
45358
  }
@@ -43913,8 +45387,10 @@ RED.group = (function() {
43913
45387
  }
43914
45388
  if (n.type === 'group') {
43915
45389
  RED.events.emit("groups:change",n)
43916
- } else {
45390
+ } else if (n.type !== 'junction') {
43917
45391
  RED.events.emit("nodes:change",n)
45392
+ } else {
45393
+ RED.events.emit("junctions:change",n)
43918
45394
  }
43919
45395
  }
43920
45396
  markDirty(group);
@@ -46926,6 +48402,7 @@ RED.projects.settings = (function() {
46926
48402
  title: RED._('sidebar.project.editDescription'),
46927
48403
  header: $('<span><i class="fa fa-book"></i> README.md</span>'),
46928
48404
  value: activeProject.description,
48405
+ stateId: "sidebar.project.editDescription",
46929
48406
  complete: function(v) {
46930
48407
  container.empty();
46931
48408
  var spinner = utils.addSpinnerOverlay(container);