@node-red/editor-client 4.0.0-beta.3-1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/locales/en-US/editor.json +5 -1
  2. package/locales/fr/editor.json +3 -0
  3. package/locales/ja/editor.json +2 -0
  4. package/package.json +1 -1
  5. package/public/red/about +53 -657
  6. package/public/red/red.js +252 -133
  7. package/public/red/red.min.js +3 -3
  8. package/public/red/style.min.css +1 -1
  9. package/public/red/tours/welcome.js +55 -60
  10. package/public/types/node-red/util.d.ts +1 -1
  11. package/public/vendor/ace/worker-jsonata.js +1 -1
  12. package/public/vendor/mermaid/mermaid.min.js +705 -318
  13. package/public/vendor/monaco/dist/{0c718f5b7d2bce997c5f.ttf → 8f3abbcbc983396e1f13.ttf} +0 -0
  14. package/public/vendor/monaco/dist/css.worker.js +1 -1
  15. package/public/vendor/monaco/dist/css.worker.js.LICENSE.txt +1 -1
  16. package/public/vendor/monaco/dist/editor.js +1 -1
  17. package/public/vendor/monaco/dist/editor.js.LICENSE.txt +1 -1
  18. package/public/vendor/monaco/dist/editor.worker.js +1 -1
  19. package/public/vendor/monaco/dist/html.worker.js +1 -1
  20. package/public/vendor/monaco/dist/html.worker.js.LICENSE.txt +1 -1
  21. package/public/vendor/monaco/dist/json.worker.js +1 -1
  22. package/public/vendor/monaco/dist/json.worker.js.LICENSE.txt +1 -1
  23. package/public/vendor/monaco/dist/locale/cs.js +227 -139
  24. package/public/vendor/monaco/dist/locale/de.js +229 -141
  25. package/public/vendor/monaco/dist/locale/es.js +225 -137
  26. package/public/vendor/monaco/dist/locale/fr.js +226 -138
  27. package/public/vendor/monaco/dist/locale/it.js +226 -138
  28. package/public/vendor/monaco/dist/locale/ja.js +227 -139
  29. package/public/vendor/monaco/dist/locale/ko.js +226 -138
  30. package/public/vendor/monaco/dist/locale/pl.js +227 -139
  31. package/public/vendor/monaco/dist/locale/pt-br.js +243 -155
  32. package/public/vendor/monaco/dist/locale/qps-ploc.js +229 -141
  33. package/public/vendor/monaco/dist/locale/ru.js +225 -137
  34. package/public/vendor/monaco/dist/locale/tr.js +227 -139
  35. package/public/vendor/monaco/dist/locale/zh-hans.js +225 -137
  36. package/public/vendor/monaco/dist/locale/zh-hant.js +228 -140
  37. package/public/vendor/monaco/dist/ts.worker.js +2 -2
  38. package/public/vendor/vendor.js +1 -1
  39. package/templates/index.mst +2 -2
package/public/red/red.js CHANGED
@@ -6757,6 +6757,13 @@ RED.nodes = (function() {
6757
6757
  } else {
6758
6758
  delete n.g
6759
6759
  }
6760
+ // If importing into a subflow, ensure an outbound-link doesn't get added
6761
+ if (activeSubflow && /^link /.test(n.type) && n.links) {
6762
+ n.links = n.links.filter(function(id) {
6763
+ const otherNode = node_map[id] || RED.nodes.node(id);
6764
+ return (otherNode && otherNode.z === activeWorkspace);
6765
+ });
6766
+ }
6760
6767
  for (var d3 in n._def.defaults) {
6761
6768
  if (n._def.defaults.hasOwnProperty(d3)) {
6762
6769
  if (n._def.defaults[d3].type) {
@@ -6780,14 +6787,6 @@ RED.nodes = (function() {
6780
6787
  }
6781
6788
  }
6782
6789
  }
6783
- // If importing into a subflow, ensure an outbound-link doesn't
6784
- // get added
6785
- if (activeSubflow && /^link /.test(n.type) && n.links) {
6786
- n.links = n.links.filter(function(id) {
6787
- const otherNode = node_map[id] || RED.nodes.node(id);
6788
- return (otherNode && otherNode.z === activeWorkspace)
6789
- });
6790
- }
6791
6790
  }
6792
6791
  for (i=0;i<new_subflows.length;i++) {
6793
6792
  n = new_subflows[i];
@@ -10129,11 +10128,25 @@ RED.utils = (function() {
10129
10128
  return parts;
10130
10129
  }
10131
10130
 
10132
- function validatePropertyExpression(str) {
10131
+ /**
10132
+ * Validate a property expression
10133
+ * @param {*} str - the property value
10134
+ * @returns {boolean|string} whether the node proprty is valid. `true`: valid `false|String`: invalid
10135
+ */
10136
+ function validatePropertyExpression(str, opt) {
10133
10137
  try {
10134
- var parts = normalisePropertyExpression(str);
10138
+ const parts = normalisePropertyExpression(str);
10135
10139
  return true;
10136
10140
  } catch(err) {
10141
+ // If the validator has opt, it is a 3.x validator that
10142
+ // can return a String to mean 'invalid' and provide a reason
10143
+ if (opt) {
10144
+ if (opt.label) {
10145
+ return opt.label + ': ' + err.message;
10146
+ }
10147
+ return err.message;
10148
+ }
10149
+ // Otherwise, a 2.x returns a false value
10137
10150
  return false;
10138
10151
  }
10139
10152
  }
@@ -10151,22 +10164,24 @@ RED.utils = (function() {
10151
10164
  // Allow ${ENV_VAR} value
10152
10165
  return true
10153
10166
  }
10154
- let error
10167
+ let error;
10155
10168
  if (propertyType === 'json') {
10156
10169
  try {
10157
10170
  JSON.parse(propertyValue);
10158
10171
  } catch(err) {
10159
10172
  error = RED._("validator.errors.invalid-json", {
10160
10173
  error: err.message
10161
- })
10174
+ });
10162
10175
  }
10163
10176
  } else if (propertyType === 'msg' || propertyType === 'flow' || propertyType === 'global' ) {
10164
- if (!RED.utils.validatePropertyExpression(propertyValue)) {
10165
- error = RED._("validator.errors.invalid-prop")
10177
+ // To avoid double label
10178
+ const valid = RED.utils.validatePropertyExpression(propertyValue, opt ? {} : null);
10179
+ if (valid !== true) {
10180
+ error = opt ? valid : RED._("validator.errors.invalid-prop");
10166
10181
  }
10167
10182
  } else if (propertyType === 'num') {
10168
10183
  if (!/^NaN$|^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$|^[+-]?(0b|0B)[01]+$|^[+-]?(0o|0O)[0-7]+$|^[+-]?(0x|0X)[0-9a-fA-F]+$/.test(propertyValue)) {
10169
- error = RED._("validator.errors.invalid-num")
10184
+ error = RED._("validator.errors.invalid-num");
10170
10185
  }
10171
10186
  } else if (propertyType === 'jsonata') {
10172
10187
  try {
@@ -10174,16 +10189,16 @@ RED.utils = (function() {
10174
10189
  } catch(err) {
10175
10190
  error = RED._("validator.errors.invalid-expr", {
10176
10191
  error: err.message
10177
- })
10192
+ });
10178
10193
  }
10179
10194
  }
10180
10195
  if (error) {
10181
10196
  if (opt && opt.label) {
10182
- return opt.label+': '+error
10197
+ return opt.label + ': ' + error;
10183
10198
  }
10184
- return error
10199
+ return error;
10185
10200
  }
10186
- return true
10201
+ return true;
10187
10202
  }
10188
10203
 
10189
10204
  function getMessageProperty(msg,expr) {
@@ -15231,61 +15246,64 @@ RED.stack = (function() {
15231
15246
  { value: "_session", source: ["websocket out","tcp out"] },
15232
15247
  ]
15233
15248
  var allOptions = {
15234
- msg: {value:"msg",label:"msg.",validate:RED.utils.validatePropertyExpression, autoComplete: msgAutoComplete(msgCompletions)},
15235
- flow: {value:"flow",label:"flow.",hasValue:true,
15236
- options:[],
15237
- validate:RED.utils.validatePropertyExpression,
15249
+ msg: { value: "msg", label: "msg.", validate: RED.utils.validatePropertyExpression, autoComplete: msgAutoComplete(msgCompletions) },
15250
+ flow: { value: "flow", label: "flow.", hasValue: true,
15251
+ options: [],
15252
+ validate: RED.utils.validatePropertyExpression,
15238
15253
  parse: contextParse,
15239
15254
  export: contextExport,
15240
15255
  valueLabel: contextLabel,
15241
15256
  autoComplete: contextAutoComplete
15242
15257
  },
15243
- global: {value:"global",label:"global.",hasValue:true,
15244
- options:[],
15245
- validate:RED.utils.validatePropertyExpression,
15258
+ global: {
15259
+ value: "global", label: "global.", hasValue: true,
15260
+ options: [],
15261
+ validate: RED.utils.validatePropertyExpression,
15246
15262
  parse: contextParse,
15247
15263
  export: contextExport,
15248
15264
  valueLabel: contextLabel,
15249
15265
  autoComplete: contextAutoComplete
15250
15266
  },
15251
- str: {value:"str",label:"string",icon:"red/images/typedInput/az.svg"},
15252
- num: {value:"num",label:"number",icon:"red/images/typedInput/09.svg",validate: function(v) {
15253
- return (true === RED.utils.validateTypedProperty(v, "num"));
15267
+ str: { value: "str", label: "string", icon: "red/images/typedInput/az.svg" },
15268
+ num: { value: "num", label: "number", icon: "red/images/typedInput/09.svg", validate: function (v, o) {
15269
+ return RED.utils.validateTypedProperty(v, "num", o);
15254
15270
  } },
15255
- bool: {value:"bool",label:"boolean",icon:"red/images/typedInput/bool.svg",options:["true","false"]},
15271
+ bool: { value: "bool", label: "boolean", icon: "red/images/typedInput/bool.svg", options: ["true", "false"] },
15256
15272
  json: {
15257
- value:"json",
15258
- label:"JSON",
15259
- icon:"red/images/typedInput/json.svg",
15260
- validate: function(v) { try{JSON.parse(v);return true;}catch(e){return false;}},
15261
- expand: function() {
15273
+ value: "json",
15274
+ label: "JSON",
15275
+ icon: "red/images/typedInput/json.svg",
15276
+ validate: function (v, o) {
15277
+ return RED.utils.validateTypedProperty(v, "json", o);
15278
+ },
15279
+ expand: function () {
15262
15280
  var that = this;
15263
15281
  var value = this.value();
15264
15282
  try {
15265
- value = JSON.stringify(JSON.parse(value),null,4);
15266
- } catch(err) {
15283
+ value = JSON.stringify(JSON.parse(value), null, 4);
15284
+ } catch (err) {
15267
15285
  }
15268
15286
  RED.editor.editJSON({
15269
15287
  value: value,
15270
15288
  stateId: RED.editor.generateViewStateId("typedInput", that, "json"),
15271
15289
  focus: true,
15272
- complete: function(v) {
15290
+ complete: function (v) {
15273
15291
  var value = v;
15274
15292
  try {
15275
15293
  value = JSON.stringify(JSON.parse(v));
15276
- } catch(err) {
15294
+ } catch (err) {
15277
15295
  }
15278
15296
  that.value(value);
15279
15297
  }
15280
15298
  })
15281
15299
  }
15282
15300
  },
15283
- re: {value:"re",label:"regular expression",icon:"red/images/typedInput/re.svg"},
15301
+ re: { value: "re", label: "regular expression", icon: "red/images/typedInput/re.svg" },
15284
15302
  date: {
15285
- value:"date",
15286
- label:"timestamp",
15287
- icon:"fa fa-clock-o",
15288
- options:[
15303
+ value: "date",
15304
+ label: "timestamp",
15305
+ icon: "fa fa-clock-o",
15306
+ options: [
15289
15307
  {
15290
15308
  label: 'milliseconds since epoch',
15291
15309
  value: ''
@@ -15304,15 +15322,17 @@ RED.stack = (function() {
15304
15322
  value: "jsonata",
15305
15323
  label: "expression",
15306
15324
  icon: "red/images/typedInput/expr.svg",
15307
- validate: function(v) { try{jsonata(v);return true;}catch(e){return false;}},
15308
- expand:function() {
15325
+ validate: function (v, o) {
15326
+ return RED.utils.validateTypedProperty(v, "jsonata", o);
15327
+ },
15328
+ expand: function () {
15309
15329
  var that = this;
15310
15330
  RED.editor.editExpression({
15311
- value: this.value().replace(/\t/g,"\n"),
15331
+ value: this.value().replace(/\t/g, "\n"),
15312
15332
  stateId: RED.editor.generateViewStateId("typedInput", that, "jsonata"),
15313
15333
  focus: true,
15314
- complete: function(v) {
15315
- that.value(v.replace(/\n/g,"\t"));
15334
+ complete: function (v) {
15335
+ that.value(v.replace(/\n/g, "\t"));
15316
15336
  }
15317
15337
  })
15318
15338
  }
@@ -15321,13 +15341,13 @@ RED.stack = (function() {
15321
15341
  value: "bin",
15322
15342
  label: "buffer",
15323
15343
  icon: "red/images/typedInput/bin.svg",
15324
- expand: function() {
15344
+ expand: function () {
15325
15345
  var that = this;
15326
15346
  RED.editor.editBuffer({
15327
15347
  value: this.value(),
15328
15348
  stateId: RED.editor.generateViewStateId("typedInput", that, "bin"),
15329
15349
  focus: true,
15330
- complete: function(v) {
15350
+ complete: function (v) {
15331
15351
  that.value(v);
15332
15352
  }
15333
15353
  })
@@ -15343,9 +15363,9 @@ RED.stack = (function() {
15343
15363
  value: "node",
15344
15364
  label: "node",
15345
15365
  icon: "red/images/typedInput/target.svg",
15346
- valueLabel: function(container,value) {
15366
+ valueLabel: function (container, value) {
15347
15367
  var node = RED.nodes.node(value);
15348
- var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).css({
15368
+ var nodeDiv = $('<div>', { class: "red-ui-search-result-node" }).css({
15349
15369
  "margin-top": "2px",
15350
15370
  "margin-left": "3px"
15351
15371
  }).appendTo(container);
@@ -15354,117 +15374,117 @@ RED.stack = (function() {
15354
15374
  "margin-left": "6px"
15355
15375
  }).appendTo(container);
15356
15376
  if (node) {
15357
- var colour = RED.utils.getNodeColor(node.type,node._def);
15358
- var icon_url = RED.utils.getNodeIcon(node._def,node);
15377
+ var colour = RED.utils.getNodeColor(node.type, node._def);
15378
+ var icon_url = RED.utils.getNodeIcon(node._def, node);
15359
15379
  if (node.type === 'tab') {
15360
15380
  colour = "#C0DEED";
15361
15381
  }
15362
- nodeDiv.css('backgroundColor',colour);
15363
- var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
15382
+ nodeDiv.css('backgroundColor', colour);
15383
+ var iconContainer = $('<div/>', { class: "red-ui-palette-icon-container" }).appendTo(nodeDiv);
15364
15384
  RED.utils.createIconElement(icon_url, iconContainer, true);
15365
- var l = RED.utils.getNodeLabel(node,node.id);
15385
+ var l = RED.utils.getNodeLabel(node, node.id);
15366
15386
  nodeLabel.text(l);
15367
15387
  } else {
15368
15388
  nodeDiv.css({
15369
15389
  'backgroundColor': '#eee',
15370
- 'border-style' : 'dashed'
15390
+ 'border-style': 'dashed'
15371
15391
  });
15372
15392
 
15373
15393
  }
15374
15394
  },
15375
- expand: function() {
15395
+ expand: function () {
15376
15396
  var that = this;
15377
15397
  RED.tray.hide();
15378
15398
  RED.view.selectNodes({
15379
15399
  single: true,
15380
15400
  selected: [that.value()],
15381
- onselect: function(selection) {
15401
+ onselect: function (selection) {
15382
15402
  that.value(selection.id);
15383
15403
  RED.tray.show();
15384
15404
  },
15385
- oncancel: function() {
15405
+ oncancel: function () {
15386
15406
  RED.tray.show();
15387
15407
  }
15388
15408
  })
15389
15409
  }
15390
15410
  },
15391
- cred:{
15392
- value:"cred",
15393
- label:"credential",
15394
- icon:"fa fa-lock",
15411
+ cred: {
15412
+ value: "cred",
15413
+ label: "credential",
15414
+ icon: "fa fa-lock",
15395
15415
  inputType: "password",
15396
- valueLabel: function(container,value) {
15416
+ valueLabel: function (container, value) {
15397
15417
  var that = this;
15398
- container.css("pointer-events","none");
15399
- container.css("flex-grow",0);
15418
+ container.css("pointer-events", "none");
15419
+ container.css("flex-grow", 0);
15400
15420
  this.elementDiv.hide();
15401
15421
  var buttons = $('<div>').css({
15402
15422
  position: "absolute",
15403
- right:"6px",
15423
+ right: "6px",
15404
15424
  top: "6px",
15405
- "pointer-events":"all"
15425
+ "pointer-events": "all"
15406
15426
  }).appendTo(container);
15407
15427
  var eyeButton = $('<button type="button" class="red-ui-button red-ui-button-small"></button>').css({
15408
- width:"20px"
15409
- }).appendTo(buttons).on("click", function(evt) {
15428
+ width: "20px"
15429
+ }).appendTo(buttons).on("click", function (evt) {
15410
15430
  evt.preventDefault();
15411
15431
  var cursorPosition = that.input[0].selectionStart;
15412
15432
  var currentType = that.input.attr("type");
15413
15433
  if (currentType === "text") {
15414
- that.input.attr("type","password");
15434
+ that.input.attr("type", "password");
15415
15435
  eyeCon.removeClass("fa-eye-slash").addClass("fa-eye");
15416
- setTimeout(function() {
15436
+ setTimeout(function () {
15417
15437
  that.input.focus();
15418
15438
  that.input[0].setSelectionRange(cursorPosition, cursorPosition);
15419
- },50);
15439
+ }, 50);
15420
15440
  } else {
15421
- that.input.attr("type","text");
15441
+ that.input.attr("type", "text");
15422
15442
  eyeCon.removeClass("fa-eye").addClass("fa-eye-slash");
15423
- setTimeout(function() {
15443
+ setTimeout(function () {
15424
15444
  that.input.focus();
15425
15445
  that.input[0].setSelectionRange(cursorPosition, cursorPosition);
15426
- },50);
15446
+ }, 50);
15427
15447
  }
15428
15448
  }).hide();
15429
- var eyeCon = $('<i class="fa fa-eye"></i>').css("margin-left","-2px").appendTo(eyeButton);
15449
+ var eyeCon = $('<i class="fa fa-eye"></i>').css("margin-left", "-2px").appendTo(eyeButton);
15430
15450
 
15431
15451
  if (value === "__PWRD__") {
15432
15452
  var innerContainer = $('<div><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i></div>').css({
15433
- padding:"6px 6px",
15434
- borderRadius:"4px"
15453
+ padding: "6px 6px",
15454
+ borderRadius: "4px"
15435
15455
  }).addClass("red-ui-typedInput-value-label-inactive").appendTo(container);
15436
- var editButton = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-pencil"></i></button>').appendTo(buttons).on("click", function(evt) {
15456
+ var editButton = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-pencil"></i></button>').appendTo(buttons).on("click", function (evt) {
15437
15457
  evt.preventDefault();
15438
15458
  innerContainer.hide();
15439
- container.css("background","none");
15440
- container.css("pointer-events","none");
15459
+ container.css("background", "none");
15460
+ container.css("pointer-events", "none");
15441
15461
  that.input.val("");
15442
15462
  that.element.val("");
15443
15463
  that.elementDiv.show();
15444
15464
  editButton.hide();
15445
15465
  cancelButton.show();
15446
15466
  eyeButton.show();
15447
- setTimeout(function() {
15467
+ setTimeout(function () {
15448
15468
  that.input.focus();
15449
- },50);
15469
+ }, 50);
15450
15470
  });
15451
- var cancelButton = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-times"></i></button>').css("margin-left","3px").appendTo(buttons).on("click", function(evt) {
15471
+ var cancelButton = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-times"></i></button>').css("margin-left", "3px").appendTo(buttons).on("click", function (evt) {
15452
15472
  evt.preventDefault();
15453
15473
  innerContainer.show();
15454
- container.css("background","");
15474
+ container.css("background", "");
15455
15475
  that.input.val("__PWRD__");
15456
15476
  that.element.val("__PWRD__");
15457
15477
  that.elementDiv.hide();
15458
15478
  editButton.show();
15459
15479
  cancelButton.hide();
15460
15480
  eyeButton.hide();
15461
- that.input.attr("type","password");
15481
+ that.input.attr("type", "password");
15462
15482
  eyeCon.removeClass("fa-eye-slash").addClass("fa-eye");
15463
15483
 
15464
15484
  }).hide();
15465
15485
  } else {
15466
- container.css("background","none");
15467
- container.css("pointer-events","none");
15486
+ container.css("background", "none");
15487
+ container.css("pointer-events", "none");
15468
15488
  this.elementDiv.show();
15469
15489
  eyeButton.show();
15470
15490
  }
@@ -15882,12 +15902,12 @@ RED.stack = (function() {
15882
15902
  }
15883
15903
  if (menu.opts.multiple) {
15884
15904
  var selected = {};
15885
- this.value().split(",").forEach(function(f) {
15886
- selected[f] = true;
15887
- })
15905
+ this.value().split(",").forEach(function(f) {
15906
+ selected[f] = true;
15907
+ });
15888
15908
  menu.find('input[type="checkbox"]').each(function() {
15889
- $(this).prop("checked",selected[$(this).data('value')])
15890
- })
15909
+ $(this).prop("checked", selected[$(this).data('value')] || false);
15910
+ });
15891
15911
  }
15892
15912
 
15893
15913
 
@@ -15978,7 +15998,7 @@ RED.stack = (function() {
15978
15998
  this.input.trigger('change',[this.propertyType,this.value()]);
15979
15999
  }
15980
16000
  } else {
15981
- this.optionSelectLabel.text(o.length+" selected");
16001
+ this.optionSelectLabel.text(RED._("typedInput.selected", { count: o.length }));
15982
16002
  }
15983
16003
  }
15984
16004
  },
@@ -16411,26 +16431,48 @@ RED.stack = (function() {
16411
16431
  }
16412
16432
  }
16413
16433
  },
16414
- validate: function() {
16415
- var result;
16416
- var value = this.value();
16417
- var type = this.type();
16434
+ validate: function(options) {
16435
+ let valid = true;
16436
+ const value = this.value();
16437
+ const type = this.type();
16418
16438
  if (this.typeMap[type] && this.typeMap[type].validate) {
16419
- var val = this.typeMap[type].validate;
16420
- if (typeof val === 'function') {
16421
- result = val(value);
16439
+ const validate = this.typeMap[type].validate;
16440
+ if (typeof validate === 'function') {
16441
+ valid = validate(value, {});
16422
16442
  } else {
16423
- result = val.test(value);
16443
+ // Regex
16444
+ valid = validate.test(value);
16445
+ if (!valid) {
16446
+ valid = RED._("validator.errors.invalid-regexp");
16447
+ }
16424
16448
  }
16425
- } else {
16426
- result = true;
16427
16449
  }
16428
- if (result) {
16429
- this.uiSelect.removeClass('input-error');
16450
+ if ((typeof valid === "string") || !valid) {
16451
+ this.element.addClass("input-error");
16452
+ this.uiSelect.addClass("input-error");
16453
+ if (typeof valid === "string") {
16454
+ let tooltip = this.element.data("tooltip");
16455
+ if (tooltip) {
16456
+ tooltip.setContent(valid);
16457
+ } else {
16458
+ tooltip = RED.popover.tooltip(this.elementDiv, valid);
16459
+ this.element.data("tooltip", tooltip);
16460
+ }
16461
+ }
16430
16462
  } else {
16431
- this.uiSelect.addClass('input-error');
16463
+ this.element.removeClass("input-error");
16464
+ this.uiSelect.removeClass("input-error");
16465
+ const tooltip = this.element.data("tooltip");
16466
+ if (tooltip) {
16467
+ this.element.data("tooltip", null);
16468
+ tooltip.delete();
16469
+ }
16432
16470
  }
16433
- return result;
16471
+ if (options?.returnErrorMessage === true) {
16472
+ return valid;
16473
+ }
16474
+ // Must return a boolean for no 3.x validator
16475
+ return (typeof valid === "string") ? false : valid;
16434
16476
  },
16435
16477
  show: function() {
16436
16478
  this.uiSelect.show();
@@ -16867,7 +16909,7 @@ RED.deploy = (function() {
16867
16909
  '<img src="red/images/spin.svg"/>'+
16868
16910
  '</span>'+
16869
16911
  '</a>'+
16870
- '<a id="red-ui-header-button-deploy-options" class="red-ui-deploy-button" href="#"><i class="fa fa-caret-down"></i></a>'+
16912
+ '<a id="red-ui-header-button-deploy-options" class="red-ui-deploy-button" href="#"><i class="fa fa-caret-down"></i><i class="fa fa-lock"></i></a>'+
16871
16913
  '</span></li>').prependTo(".red-ui-header-toolbar");
16872
16914
  const mainMenuItems = [
16873
16915
  {id:"deploymenu-item-full",toggle:"deploy-type",icon:"red/images/deploy-full.svg",label:RED._("deploy.full"),sublabel:RED._("deploy.fullDesc"),selected: true, onselect:function(s) { if(s){changeDeploymentType("full")}}},
@@ -16928,6 +16970,9 @@ RED.deploy = (function() {
16928
16970
  })
16929
16971
 
16930
16972
  RED.events.on('workspace:dirty',function(state) {
16973
+ if (RED.settings.user?.permissions === 'read') {
16974
+ return
16975
+ }
16931
16976
  if (state.dirty) {
16932
16977
  // window.onbeforeunload = function() {
16933
16978
  // return
@@ -16973,6 +17018,22 @@ RED.deploy = (function() {
16973
17018
  activeBackgroundDeployNotification.update(message, options)
16974
17019
  }
16975
17020
  });
17021
+
17022
+
17023
+ updateLockedState()
17024
+ RED.events.on('login', updateLockedState)
17025
+ }
17026
+
17027
+ function updateLockedState() {
17028
+ if (RED.settings.user?.permissions === 'read') {
17029
+ $(".red-ui-deploy-button-group").addClass("readOnly");
17030
+ $("#red-ui-header-button-deploy").addClass("disabled");
17031
+ } else {
17032
+ $(".red-ui-deploy-button-group").removeClass("readOnly");
17033
+ if (RED.nodes.dirty()) {
17034
+ $("#red-ui-header-button-deploy").removeClass("disabled");
17035
+ }
17036
+ }
16976
17037
  }
16977
17038
 
16978
17039
  function getNodeInfo(node) {
@@ -17447,6 +17508,7 @@ RED.deploy = (function() {
17447
17508
  // Once deployed, cannot undo back to a clean state
17448
17509
  RED.history.markAllDirty();
17449
17510
  RED.view.redraw();
17511
+ RED.sidebar.config.refresh();
17450
17512
  RED.events.emit("deploy");
17451
17513
  }).fail(function (xhr, textStatus, err) {
17452
17514
  RED.nodes.dirty(true);
@@ -22935,6 +22997,7 @@ RED.view = (function() {
22935
22997
 
22936
22998
  if (d3.event.button === 1) {
22937
22999
  // Middle Click pan
23000
+ d3.event.preventDefault();
22938
23001
  mouse_mode = RED.state.PANNING;
22939
23002
  mouse_position = [d3.event.pageX,d3.event.pageY]
22940
23003
  scroll_position = [chart.scrollLeft(),chart.scrollTop()];
@@ -32406,7 +32469,13 @@ RED.sidebar.help = (function() {
32406
32469
  toolbar = $("<div>", {class:"red-ui-sidebar-header red-ui-info-toolbar"}).appendTo(content);
32407
32470
  $('<span class="button-group"><a id="red-ui-sidebar-help-show-toc" class="red-ui-button red-ui-button-small selected" href="#"><i class="fa fa-list-ul"></i></a></span>').appendTo(toolbar)
32408
32471
  var showTOCButton = toolbar.find('#red-ui-sidebar-help-show-toc')
32409
- RED.popover.tooltip(showTOCButton,RED._("sidebar.help.showTopics"));
32472
+ RED.popover.tooltip(showTOCButton, function () {
32473
+ if ($(showTOCButton).hasClass('selected')) {
32474
+ return RED._("sidebar.help.hideTopics");
32475
+ } else {
32476
+ return RED._("sidebar.help.showTopics");
32477
+ }
32478
+ });
32410
32479
  showTOCButton.on("click",function(e) {
32411
32480
  e.preventDefault();
32412
32481
  if ($(this).hasClass('selected')) {
@@ -33250,9 +33319,11 @@ RED.sidebar.config = (function() {
33250
33319
  refreshConfigNodeList();
33251
33320
  }
33252
33321
  });
33322
+
33253
33323
  RED.popover.tooltip($('#red-ui-sidebar-config-filter-all'), RED._("sidebar.config.showAllConfigNodes"));
33254
33324
  RED.popover.tooltip($('#red-ui-sidebar-config-filter-unused'), RED._("sidebar.config.showAllUnusedConfigNodes"));
33255
-
33325
+ RED.popover.tooltip($('#red-ui-sidebar-config-collapse-all'), RED._("palette.actions.collapse-all"));
33326
+ RED.popover.tooltip($('#red-ui-sidebar-config-expand-all'), RED._("palette.actions.expand-all"));
33256
33327
  }
33257
33328
 
33258
33329
  function flashConfigNode(el) {
@@ -35309,7 +35380,10 @@ RED.editor = (function() {
35309
35380
  const input = $("#"+prefix+"-"+property);
35310
35381
  const isTypedInput = input.length > 0 && input.next(".red-ui-typedInput-container").length > 0;
35311
35382
  if (isTypedInput) {
35312
- valid = input.typedInput("validate");
35383
+ valid = input.typedInput("validate", { returnErrorMessage: true });
35384
+ if (typeof valid === "string") {
35385
+ return label ? label + ": " + valid : valid;
35386
+ }
35313
35387
  }
35314
35388
  }
35315
35389
  }
@@ -35367,6 +35441,8 @@ RED.editor = (function() {
35367
35441
  var value = input.val();
35368
35442
  if (defaults[property].hasOwnProperty("format") && defaults[property].format !== "" && input[0].nodeName === "DIV") {
35369
35443
  value = input.text();
35444
+ } else if (input.attr("type") === "checkbox") {
35445
+ value = input.prop("checked");
35370
35446
  }
35371
35447
  var valid = validateNodeProperty(node, defaults, property,value);
35372
35448
  if (((typeof valid) === "string") || !valid) {
@@ -35920,9 +35996,16 @@ RED.editor = (function() {
35920
35996
  }
35921
35997
 
35922
35998
  try {
35923
- var rc = editing_node._def.oneditsave.call(editing_node);
35999
+ const rc = editing_node._def.oneditsave.call(editing_node);
35924
36000
  if (rc === true) {
35925
36001
  editState.changed = true;
36002
+ } else if (typeof rc === 'object' && rc !== null ) {
36003
+ if (rc.changed === true) {
36004
+ editState.changed = true
36005
+ }
36006
+ if (Array.isArray(rc.history) && rc.history.length > 0) {
36007
+ editState.history = rc.history
36008
+ }
35926
36009
  }
35927
36010
  } catch(err) {
35928
36011
  console.warn("oneditsave",editing_node.id,editing_node.type,err.toString());
@@ -36124,6 +36207,17 @@ RED.editor = (function() {
36124
36207
  dirty: startDirty
36125
36208
  }
36126
36209
 
36210
+ if (editing_node.g) {
36211
+ const group = RED.nodes.group(editing_node.g);
36212
+ // Don't use RED.group.removeFromGroup as that emits
36213
+ // a change event on the node - but we're deleting it
36214
+ const index = group?.nodes.indexOf(editing_node) ?? -1;
36215
+ if (index > -1) {
36216
+ group.nodes.splice(index, 1);
36217
+ RED.group.markDirty(group);
36218
+ }
36219
+ }
36220
+
36127
36221
  RED.nodes.dirty(true);
36128
36222
  RED.view.redraw(true);
36129
36223
  RED.history.push(historyEvent);
@@ -36225,7 +36319,7 @@ RED.editor = (function() {
36225
36319
  }
36226
36320
  });
36227
36321
  }
36228
- var historyEvent = {
36322
+ let historyEvent = {
36229
36323
  t:'edit',
36230
36324
  node:editing_node,
36231
36325
  changes:editState.changes,
@@ -36241,6 +36335,15 @@ RED.editor = (function() {
36241
36335
  instances:subflowInstances
36242
36336
  }
36243
36337
  }
36338
+
36339
+ if (editState.history) {
36340
+ historyEvent = {
36341
+ t: 'multi',
36342
+ events: [ historyEvent, ...editState.history ],
36343
+ dirty: wasDirty
36344
+ }
36345
+ }
36346
+
36244
36347
  RED.history.push(historyEvent);
36245
36348
  }
36246
36349
  editing_node.dirty = true;
@@ -43515,6 +43618,7 @@ RED.clipboard = (function() {
43515
43618
  var currentPopoverError;
43516
43619
  var activeTab;
43517
43620
  var libraryBrowser;
43621
+ var clipboardTabs;
43518
43622
 
43519
43623
  var activeLibraries = {};
43520
43624
 
@@ -43704,6 +43808,13 @@ RED.clipboard = (function() {
43704
43808
  open: function( event, ui ) {
43705
43809
  RED.keyboard.disable();
43706
43810
  },
43811
+ beforeClose: function(e) {
43812
+ if (clipboardTabs && activeTab === "red-ui-clipboard-dialog-export-tab-clipboard") {
43813
+ const jsonTabIndex = clipboardTabs.getTabIndex('red-ui-clipboard-dialog-export-tab-clipboard-json')
43814
+ const activeTabIndex = clipboardTabs.activeIndex()
43815
+ RED.settings.set("editor.dialog.export.json-view", activeTabIndex === jsonTabIndex )
43816
+ }
43817
+ },
43707
43818
  close: function(e) {
43708
43819
  RED.keyboard.enable();
43709
43820
  if (popover) {
@@ -43717,12 +43828,23 @@ RED.clipboard = (function() {
43717
43828
 
43718
43829
  exportNodesDialog =
43719
43830
  '<div class="form-row">'+
43720
- '<label style="width:auto;margin-right: 10px;" data-i18n="common.label.export"></label>'+
43721
- '<span id="red-ui-clipboard-dialog-export-rng-group" class="button-group">'+
43722
- '<a id="red-ui-clipboard-dialog-export-rng-selected" class="red-ui-button toggle" href="#" data-i18n="clipboard.export.selected"></a>'+
43723
- '<a id="red-ui-clipboard-dialog-export-rng-flow" class="red-ui-button toggle" href="#" data-i18n="clipboard.export.current"></a>'+
43724
- '<a id="red-ui-clipboard-dialog-export-rng-full" class="red-ui-button toggle" href="#" data-i18n="clipboard.export.all"></a>'+
43725
- '</span>'+
43831
+ '<div style="display: flex; justify-content: space-between;">'+
43832
+ '<div class="form-row">'+
43833
+ '<label style="width:auto;margin-right: 10px;" data-i18n="common.label.export"></label>'+
43834
+ '<span id="red-ui-clipboard-dialog-export-rng-group" class="button-group">'+
43835
+ '<a id="red-ui-clipboard-dialog-export-rng-selected" class="red-ui-button toggle" href="#" data-i18n="clipboard.export.selected"></a>'+
43836
+ '<a id="red-ui-clipboard-dialog-export-rng-flow" class="red-ui-button toggle" href="#" data-i18n="clipboard.export.current"></a>'+
43837
+ '<a id="red-ui-clipboard-dialog-export-rng-full" class="red-ui-button toggle" href="#" data-i18n="clipboard.export.all"></a>'+
43838
+ '</span>'+
43839
+ '</div>'+
43840
+ '<div class="form-row">'+
43841
+ '<label style="width:auto;margin-right: 10px;" data-i18n="common.label.format"></label>'+
43842
+ '<span id="red-ui-clipboard-dialog-export-fmt-group" class="button-group">'+
43843
+ '<a id="red-ui-clipboard-dialog-export-fmt-mini" class="red-ui-button red-ui-button toggle" href="#" data-i18n="clipboard.export.compact"></a>'+
43844
+ '<a id="red-ui-clipboard-dialog-export-fmt-full" class="red-ui-button red-ui-button toggle" href="#" data-i18n="clipboard.export.formatted"></a>'+
43845
+ '</span>'+
43846
+ '</div>'+
43847
+ '</div>'+
43726
43848
  '</div>'+
43727
43849
  '<div class="red-ui-clipboard-dialog-box">'+
43728
43850
  '<div class="red-ui-clipboard-dialog-tabs">'+
@@ -43737,15 +43859,9 @@ RED.clipboard = (function() {
43737
43859
  '<div id="red-ui-clipboard-dialog-export-tab-clipboard-preview-list"></div>'+
43738
43860
  '</div>'+
43739
43861
  '<div class="red-ui-clipboard-dialog-export-tab-clipboard-tab" id="red-ui-clipboard-dialog-export-tab-clipboard-json">'+
43740
- '<div class="form-row" style="height:calc(100% - 40px)">'+
43862
+ '<div class="form-row" style="height:calc(100% - 10px)">'+
43741
43863
  '<textarea readonly id="red-ui-clipboard-dialog-export-text"></textarea>'+
43742
43864
  '</div>'+
43743
- '<div class="form-row" style="text-align: right;">'+
43744
- '<span id="red-ui-clipboard-dialog-export-fmt-group" class="button-group">'+
43745
- '<a id="red-ui-clipboard-dialog-export-fmt-mini" class="red-ui-button red-ui-button-small toggle" href="#" data-i18n="clipboard.export.compact"></a>'+
43746
- '<a id="red-ui-clipboard-dialog-export-fmt-full" class="red-ui-button red-ui-button-small toggle" href="#" data-i18n="clipboard.export.formatted"></a>'+
43747
- '</span>'+
43748
- '</div>'+
43749
43865
  '</div>'+
43750
43866
  '</div>'+
43751
43867
  '<div class="form-row" id="red-ui-clipboard-dialog-export-tab-library-filename">'+
@@ -44058,7 +44174,7 @@ RED.clipboard = (function() {
44058
44174
 
44059
44175
  dialogContainer.empty();
44060
44176
  dialogContainer.append($(exportNodesDialog));
44061
-
44177
+ clipboardTabs = null
44062
44178
  var tabs = RED.tabs.create({
44063
44179
  id: "red-ui-clipboard-dialog-export-tabs",
44064
44180
  vertical: true,
@@ -44119,7 +44235,7 @@ RED.clipboard = (function() {
44119
44235
  $("#red-ui-clipboard-dialog-tab-library-name").on('paste',function() { setTimeout(validateExportFilename,10)});
44120
44236
  $("#red-ui-clipboard-dialog-export").button("enable");
44121
44237
 
44122
- var clipboardTabs = RED.tabs.create({
44238
+ clipboardTabs = RED.tabs.create({
44123
44239
  id: "red-ui-clipboard-dialog-export-tab-clipboard-tabs",
44124
44240
  onchange: function(tab) {
44125
44241
  $(".red-ui-clipboard-dialog-export-tab-clipboard-tab").hide();
@@ -44136,6 +44252,9 @@ RED.clipboard = (function() {
44136
44252
  id: "red-ui-clipboard-dialog-export-tab-clipboard-json",
44137
44253
  label: RED._("editor.types.json")
44138
44254
  });
44255
+ if (RED.settings.get("editor.dialog.export.json-view") === true) {
44256
+ clipboardTabs.activateTab("red-ui-clipboard-dialog-export-tab-clipboard-json");
44257
+ }
44139
44258
 
44140
44259
  var previewList = $("#red-ui-clipboard-dialog-export-tab-clipboard-preview-list").css({position:"absolute",top:0,right:0,bottom:0,left:0}).treeList({
44141
44260
  data: []