@node-red/editor-client 3.1.7 → 4.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.
- package/locales/en-US/editor.json +8 -1
- package/package.json +1 -1
- package/public/red/about +31 -0
- package/public/red/red.js +677 -121
- package/public/red/red.min.js +3 -3
- package/public/red/style.min.css +2 -2
- package/public/red/tours/3.1/welcome.js +231 -0
- package/public/red/tours/images/nr4-auto-complete.png +0 -0
- package/public/red/tours/images/nr4-sf-config.png +0 -0
- package/public/red/tours/images/nr4-timestamp-formatting.png +0 -0
- package/public/red/tours/welcome.js +36 -186
- /package/public/red/tours/{images → 3.1/images}/context-menu.png +0 -0
- /package/public/red/tours/{images → 3.1/images}/global-env-vars.png +0 -0
- /package/public/red/tours/{images → 3.1/images}/hiding-flows.png +0 -0
- /package/public/red/tours/{images → 3.1/images}/locking-flows.png +0 -0
- /package/public/red/tours/{images → 3.1/images}/mermaid.png +0 -0
- /package/public/red/tours/{images → 3.1/images}/node-help.png +0 -0
- /package/public/red/tours/{images → 3.1/images}/tab-changes.png +0 -0
package/public/red/red.js
CHANGED
|
@@ -3808,6 +3808,31 @@ RED.nodes = (function() {
|
|
|
3808
3808
|
getNodeTypes: function() {
|
|
3809
3809
|
return Object.keys(nodeDefinitions);
|
|
3810
3810
|
},
|
|
3811
|
+
/**
|
|
3812
|
+
* Get an array of node definitions
|
|
3813
|
+
* @param {Object} options - options object
|
|
3814
|
+
* @param {boolean} [options.configOnly] - if true, only return config nodes
|
|
3815
|
+
* @param {function} [options.filter] - a filter function to apply to the list of nodes
|
|
3816
|
+
* @returns array of node definitions
|
|
3817
|
+
*/
|
|
3818
|
+
getNodeDefinitions: function(options) {
|
|
3819
|
+
const result = []
|
|
3820
|
+
const configOnly = (options && options.configOnly)
|
|
3821
|
+
const filter = (options && options.filter)
|
|
3822
|
+
const keys = Object.keys(nodeDefinitions)
|
|
3823
|
+
for (const key of keys) {
|
|
3824
|
+
const def = nodeDefinitions[key]
|
|
3825
|
+
if(!def) { continue }
|
|
3826
|
+
if (configOnly && def.category !== "config") {
|
|
3827
|
+
continue
|
|
3828
|
+
}
|
|
3829
|
+
if (filter && !filter(nodeDefinitions[key])) {
|
|
3830
|
+
continue
|
|
3831
|
+
}
|
|
3832
|
+
result.push(nodeDefinitions[key])
|
|
3833
|
+
}
|
|
3834
|
+
return result
|
|
3835
|
+
},
|
|
3811
3836
|
setNodeList: function(list) {
|
|
3812
3837
|
nodeList = [];
|
|
3813
3838
|
for(var i=0;i<list.length;i++) {
|
|
@@ -8996,6 +9021,16 @@ RED.utils = (function() {
|
|
|
8996
9021
|
$('<span class="red-ui-debug-msg-type-string-swatch"></span>').css('backgroundColor',obj).appendTo(e);
|
|
8997
9022
|
}
|
|
8998
9023
|
|
|
9024
|
+
let n = RED.nodes.node(obj) ?? RED.nodes.workspace(obj);
|
|
9025
|
+
if (n) {
|
|
9026
|
+
if (options.nodeSelector && "function" == typeof options.nodeSelector) {
|
|
9027
|
+
e.css('cursor', 'pointer').on("click", function(evt) {
|
|
9028
|
+
evt.preventDefault();
|
|
9029
|
+
options.nodeSelector(n.id);
|
|
9030
|
+
})
|
|
9031
|
+
}
|
|
9032
|
+
}
|
|
9033
|
+
|
|
8999
9034
|
} else if (typeof obj === 'number') {
|
|
9000
9035
|
e = $('<span class="red-ui-debug-msg-type-number"></span>').appendTo(entryObj);
|
|
9001
9036
|
|
|
@@ -9102,6 +9137,7 @@ RED.utils = (function() {
|
|
|
9102
9137
|
exposeApi: exposeApi,
|
|
9103
9138
|
// tools: tools // Do not pass tools down as we
|
|
9104
9139
|
// keep them attached to the top-level header
|
|
9140
|
+
nodeSelector: options.nodeSelector,
|
|
9105
9141
|
}
|
|
9106
9142
|
).appendTo(row);
|
|
9107
9143
|
}
|
|
@@ -9132,6 +9168,7 @@ RED.utils = (function() {
|
|
|
9132
9168
|
exposeApi: exposeApi,
|
|
9133
9169
|
// tools: tools // Do not pass tools down as we
|
|
9134
9170
|
// keep them attached to the top-level header
|
|
9171
|
+
nodeSelector: options.nodeSelector,
|
|
9135
9172
|
}
|
|
9136
9173
|
).appendTo(row);
|
|
9137
9174
|
}
|
|
@@ -9188,6 +9225,7 @@ RED.utils = (function() {
|
|
|
9188
9225
|
exposeApi: exposeApi,
|
|
9189
9226
|
// tools: tools // Do not pass tools down as we
|
|
9190
9227
|
// keep them attached to the top-level header
|
|
9228
|
+
nodeSelector: options.nodeSelector,
|
|
9191
9229
|
}
|
|
9192
9230
|
).appendTo(row);
|
|
9193
9231
|
}
|
|
@@ -10172,12 +10210,24 @@ RED.utils = (function() {
|
|
|
10172
10210
|
this.uiContainer.width(m[1]);
|
|
10173
10211
|
}
|
|
10174
10212
|
if (this.options.sortable) {
|
|
10213
|
+
var isCanceled = false; // Flag to track if an item has been canceled from being dropped into a different list
|
|
10214
|
+
var noDrop = false; // Flag to track if an item is being dragged into a different list
|
|
10175
10215
|
var handle = (typeof this.options.sortable === 'string')?
|
|
10176
10216
|
this.options.sortable :
|
|
10177
10217
|
".red-ui-editableList-item-handle";
|
|
10178
10218
|
var sortOptions = {
|
|
10179
10219
|
axis: "y",
|
|
10180
10220
|
update: function( event, ui ) {
|
|
10221
|
+
// dont trigger update if the item is being canceled
|
|
10222
|
+
const targetList = $(event.target);
|
|
10223
|
+
const draggedItem = ui.item;
|
|
10224
|
+
const draggedItemParent = draggedItem.parent();
|
|
10225
|
+
if (!targetList.is(draggedItemParent) && draggedItem.hasClass("red-ui-editableList-item-constrained")) {
|
|
10226
|
+
noDrop = true;
|
|
10227
|
+
}
|
|
10228
|
+
if (isCanceled || noDrop) {
|
|
10229
|
+
return;
|
|
10230
|
+
}
|
|
10181
10231
|
if (that.options.sortItems) {
|
|
10182
10232
|
that.options.sortItems(that.items());
|
|
10183
10233
|
}
|
|
@@ -10187,8 +10237,32 @@ RED.utils = (function() {
|
|
|
10187
10237
|
tolerance: "pointer",
|
|
10188
10238
|
forcePlaceholderSize:true,
|
|
10189
10239
|
placeholder: "red-ui-editabelList-item-placeholder",
|
|
10190
|
-
start: function(
|
|
10191
|
-
|
|
10240
|
+
start: function (event, ui) {
|
|
10241
|
+
isCanceled = false;
|
|
10242
|
+
ui.placeholder.height(ui.item.height() - 4);
|
|
10243
|
+
ui.item.css('cursor', 'grabbing'); // TODO: this doesn't seem to work, use a class instead?
|
|
10244
|
+
},
|
|
10245
|
+
stop: function (event, ui) {
|
|
10246
|
+
ui.item.css('cursor', 'auto');
|
|
10247
|
+
},
|
|
10248
|
+
receive: function (event, ui) {
|
|
10249
|
+
if (ui.item.hasClass("red-ui-editableList-item-constrained")) {
|
|
10250
|
+
isCanceled = true;
|
|
10251
|
+
$(ui.sender).sortable('cancel');
|
|
10252
|
+
}
|
|
10253
|
+
},
|
|
10254
|
+
over: function (event, ui) {
|
|
10255
|
+
// if the dragged item is constrained, prevent it from being dropped into a different list
|
|
10256
|
+
const targetList = $(event.target);
|
|
10257
|
+
const draggedItem = ui.item;
|
|
10258
|
+
const draggedItemParent = draggedItem.parent();
|
|
10259
|
+
if (!targetList.is(draggedItemParent) && draggedItem.hasClass("red-ui-editableList-item-constrained")) {
|
|
10260
|
+
noDrop = true;
|
|
10261
|
+
draggedItem.css('cursor', 'no-drop'); // TODO: this doesn't seem to work, use a class instead?
|
|
10262
|
+
} else {
|
|
10263
|
+
noDrop = false;
|
|
10264
|
+
draggedItem.css('cursor', 'grabbing'); // TODO: this doesn't seem to work, use a class instead?
|
|
10265
|
+
}
|
|
10192
10266
|
}
|
|
10193
10267
|
};
|
|
10194
10268
|
if (this.options.connectWith) {
|
|
@@ -14152,25 +14226,26 @@ RED.stack = (function() {
|
|
|
14152
14226
|
return icon;
|
|
14153
14227
|
}
|
|
14154
14228
|
|
|
14155
|
-
|
|
14156
|
-
|
|
14157
|
-
|
|
14158
|
-
|
|
14159
|
-
|
|
14160
|
-
|
|
14161
|
-
|
|
14162
|
-
|
|
14163
|
-
|
|
14164
|
-
|
|
14165
|
-
|
|
14166
|
-
|
|
14167
|
-
|
|
14168
|
-
|
|
14169
|
-
|
|
14170
|
-
|
|
14171
|
-
|
|
14172
|
-
|
|
14173
|
-
|
|
14229
|
+
function getMatch(value, searchValue) {
|
|
14230
|
+
const idx = value.toLowerCase().indexOf(searchValue.toLowerCase());
|
|
14231
|
+
const len = idx > -1 ? searchValue.length : 0;
|
|
14232
|
+
return {
|
|
14233
|
+
index: idx,
|
|
14234
|
+
found: idx > -1,
|
|
14235
|
+
pre: value.substring(0,idx),
|
|
14236
|
+
match: value.substring(idx,idx+len),
|
|
14237
|
+
post: value.substring(idx+len),
|
|
14238
|
+
}
|
|
14239
|
+
}
|
|
14240
|
+
function generateSpans(match) {
|
|
14241
|
+
const els = [];
|
|
14242
|
+
if(match.pre) { els.push($('<span/>').text(match.pre)); }
|
|
14243
|
+
if(match.match) { els.push($('<span/>',{style:"font-weight: bold; color: var(--red-ui-text-color-link);"}).text(match.match)); }
|
|
14244
|
+
if(match.post) { els.push($('<span/>').text(match.post)); }
|
|
14245
|
+
return els;
|
|
14246
|
+
}
|
|
14247
|
+
|
|
14248
|
+
const msgAutoComplete = function(options) {
|
|
14174
14249
|
return function(val) {
|
|
14175
14250
|
var matches = [];
|
|
14176
14251
|
options.forEach(opt => {
|
|
@@ -14200,6 +14275,197 @@ RED.stack = (function() {
|
|
|
14200
14275
|
}
|
|
14201
14276
|
}
|
|
14202
14277
|
|
|
14278
|
+
function getEnvVars (obj, envVars = {}) {
|
|
14279
|
+
contextKnownKeys.env = contextKnownKeys.env || {}
|
|
14280
|
+
if (contextKnownKeys.env[obj.id]) {
|
|
14281
|
+
return contextKnownKeys.env[obj.id]
|
|
14282
|
+
}
|
|
14283
|
+
let parent
|
|
14284
|
+
if (obj.type === 'tab' || obj.type === 'subflow') {
|
|
14285
|
+
RED.nodes.eachConfig(function (conf) {
|
|
14286
|
+
if (conf.type === "global-config") {
|
|
14287
|
+
parent = conf;
|
|
14288
|
+
}
|
|
14289
|
+
})
|
|
14290
|
+
} else if (obj.g) {
|
|
14291
|
+
parent = RED.nodes.group(obj.g)
|
|
14292
|
+
} else if (obj.z) {
|
|
14293
|
+
parent = RED.nodes.workspace(obj.z) || RED.nodes.subflow(obj.z)
|
|
14294
|
+
}
|
|
14295
|
+
if (parent) {
|
|
14296
|
+
getEnvVars(parent, envVars)
|
|
14297
|
+
}
|
|
14298
|
+
if (obj.env) {
|
|
14299
|
+
obj.env.forEach(env => {
|
|
14300
|
+
envVars[env.name] = obj
|
|
14301
|
+
})
|
|
14302
|
+
}
|
|
14303
|
+
contextKnownKeys.env[obj.id] = envVars
|
|
14304
|
+
return envVars
|
|
14305
|
+
}
|
|
14306
|
+
|
|
14307
|
+
const envAutoComplete = function (val) {
|
|
14308
|
+
const editStack = RED.editor.getEditStack()
|
|
14309
|
+
if (editStack.length === 0) {
|
|
14310
|
+
done([])
|
|
14311
|
+
return
|
|
14312
|
+
}
|
|
14313
|
+
const editingNode = editStack.pop()
|
|
14314
|
+
if (!editingNode) {
|
|
14315
|
+
return []
|
|
14316
|
+
}
|
|
14317
|
+
const envVarsMap = getEnvVars(editingNode)
|
|
14318
|
+
const envVars = Object.keys(envVarsMap)
|
|
14319
|
+
const matches = []
|
|
14320
|
+
const i = val.lastIndexOf('${')
|
|
14321
|
+
let searchKey = val
|
|
14322
|
+
let isSubkey = false
|
|
14323
|
+
if (i > -1) {
|
|
14324
|
+
if (val.lastIndexOf('}') < i) {
|
|
14325
|
+
searchKey = val.substring(i+2)
|
|
14326
|
+
isSubkey = true
|
|
14327
|
+
}
|
|
14328
|
+
}
|
|
14329
|
+
envVars.forEach(v => {
|
|
14330
|
+
let valMatch = getMatch(v, searchKey);
|
|
14331
|
+
if (valMatch.found) {
|
|
14332
|
+
const optSrc = envVarsMap[v]
|
|
14333
|
+
const element = $('<div>',{style: "display: flex"});
|
|
14334
|
+
const valEl = $('<div/>',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"});
|
|
14335
|
+
valEl.append(generateSpans(valMatch))
|
|
14336
|
+
valEl.appendTo(element)
|
|
14337
|
+
|
|
14338
|
+
if (optSrc) {
|
|
14339
|
+
const optEl = $('<div>').css({ "font-size": "0.8em" });
|
|
14340
|
+
let label
|
|
14341
|
+
if (optSrc.type === 'global-config') {
|
|
14342
|
+
label = RED._('sidebar.context.global')
|
|
14343
|
+
} else if (optSrc.type === 'group') {
|
|
14344
|
+
label = RED.utils.getNodeLabel(optSrc) || (RED._('sidebar.info.group') + ': '+optSrc.id)
|
|
14345
|
+
} else {
|
|
14346
|
+
label = RED.utils.getNodeLabel(optSrc) || optSrc.id
|
|
14347
|
+
}
|
|
14348
|
+
|
|
14349
|
+
optEl.append(generateSpans({ match: label }));
|
|
14350
|
+
optEl.appendTo(element);
|
|
14351
|
+
}
|
|
14352
|
+
matches.push({
|
|
14353
|
+
value: isSubkey ? val + v + '}' : v,
|
|
14354
|
+
label: element,
|
|
14355
|
+
i: valMatch.index
|
|
14356
|
+
});
|
|
14357
|
+
}
|
|
14358
|
+
})
|
|
14359
|
+
matches.sort(function(A,B){return A.i-B.i})
|
|
14360
|
+
return matches
|
|
14361
|
+
}
|
|
14362
|
+
|
|
14363
|
+
let contextKnownKeys = {}
|
|
14364
|
+
let contextCache = {}
|
|
14365
|
+
if (RED.events) {
|
|
14366
|
+
RED.events.on("editor:close", function () {
|
|
14367
|
+
contextCache = {}
|
|
14368
|
+
contextKnownKeys = {}
|
|
14369
|
+
});
|
|
14370
|
+
}
|
|
14371
|
+
|
|
14372
|
+
const contextAutoComplete = function() {
|
|
14373
|
+
const that = this
|
|
14374
|
+
const getContextKeysFromRuntime = function(scope, store, searchKey, done) {
|
|
14375
|
+
contextKnownKeys[scope] = contextKnownKeys[scope] || {}
|
|
14376
|
+
contextKnownKeys[scope][store] = contextKnownKeys[scope][store] || new Set()
|
|
14377
|
+
if (searchKey.length > 0) {
|
|
14378
|
+
try {
|
|
14379
|
+
RED.utils.normalisePropertyExpression(searchKey)
|
|
14380
|
+
} catch (err) {
|
|
14381
|
+
// Not a valid context key, so don't try looking up
|
|
14382
|
+
done()
|
|
14383
|
+
return
|
|
14384
|
+
}
|
|
14385
|
+
}
|
|
14386
|
+
const url = `context/${scope}/${encodeURIComponent(searchKey)}?store=${store}&keysOnly`
|
|
14387
|
+
if (contextCache[url]) {
|
|
14388
|
+
// console.log('CACHED', url)
|
|
14389
|
+
done()
|
|
14390
|
+
} else {
|
|
14391
|
+
// console.log('GET', url)
|
|
14392
|
+
$.getJSON(url, function(data) {
|
|
14393
|
+
// console.log(data)
|
|
14394
|
+
contextCache[url] = true
|
|
14395
|
+
const result = data[store] || {}
|
|
14396
|
+
const keys = result.keys || []
|
|
14397
|
+
const keyPrefix = searchKey + (searchKey.length > 0 ? '.' : '')
|
|
14398
|
+
keys.forEach(key => {
|
|
14399
|
+
if (/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(key)) {
|
|
14400
|
+
contextKnownKeys[scope][store].add(keyPrefix + key)
|
|
14401
|
+
} else {
|
|
14402
|
+
contextKnownKeys[scope][store].add(searchKey + "[\""+key.replace(/"/,"\\\"")+"\"]")
|
|
14403
|
+
}
|
|
14404
|
+
})
|
|
14405
|
+
done()
|
|
14406
|
+
})
|
|
14407
|
+
}
|
|
14408
|
+
}
|
|
14409
|
+
const getContextKeys = function(key, done) {
|
|
14410
|
+
const keyParts = key.split('.')
|
|
14411
|
+
const partialKey = keyParts.pop()
|
|
14412
|
+
let scope = that.propertyType
|
|
14413
|
+
if (scope === 'flow') {
|
|
14414
|
+
// Get the flow id of the node we're editing
|
|
14415
|
+
const editStack = RED.editor.getEditStack()
|
|
14416
|
+
if (editStack.length === 0) {
|
|
14417
|
+
done([])
|
|
14418
|
+
return
|
|
14419
|
+
}
|
|
14420
|
+
const editingNode = editStack.pop()
|
|
14421
|
+
if (editingNode.z) {
|
|
14422
|
+
scope = `${scope}/${editingNode.z}`
|
|
14423
|
+
} else {
|
|
14424
|
+
done([])
|
|
14425
|
+
return
|
|
14426
|
+
}
|
|
14427
|
+
}
|
|
14428
|
+
const store = (contextStoreOptions.length === 1) ? contextStoreOptions[0].value : that.optionValue
|
|
14429
|
+
const searchKey = keyParts.join('.')
|
|
14430
|
+
|
|
14431
|
+
getContextKeysFromRuntime(scope, store, searchKey, function() {
|
|
14432
|
+
if (contextKnownKeys[scope][store].has(key) || key.endsWith(']')) {
|
|
14433
|
+
getContextKeysFromRuntime(scope, store, key, function() {
|
|
14434
|
+
done(contextKnownKeys[scope][store])
|
|
14435
|
+
})
|
|
14436
|
+
}
|
|
14437
|
+
done(contextKnownKeys[scope][store])
|
|
14438
|
+
})
|
|
14439
|
+
}
|
|
14440
|
+
|
|
14441
|
+
return function(val, done) {
|
|
14442
|
+
getContextKeys(val, function (keys) {
|
|
14443
|
+
const matches = []
|
|
14444
|
+
keys.forEach(v => {
|
|
14445
|
+
let optVal = v
|
|
14446
|
+
let valMatch = getMatch(optVal, val);
|
|
14447
|
+
if (!valMatch.found && val.length > 0 && val.endsWith('.')) {
|
|
14448
|
+
// Search key ends in '.' - but doesn't match. Check again
|
|
14449
|
+
// with [" at the end instead so we match bracket notation
|
|
14450
|
+
valMatch = getMatch(optVal, val.substring(0, val.length - 1) + '["')
|
|
14451
|
+
}
|
|
14452
|
+
if (valMatch.found) {
|
|
14453
|
+
const element = $('<div>',{style: "display: flex"});
|
|
14454
|
+
const valEl = $('<div/>',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"});
|
|
14455
|
+
valEl.append(generateSpans(valMatch))
|
|
14456
|
+
valEl.appendTo(element)
|
|
14457
|
+
matches.push({
|
|
14458
|
+
value: optVal,
|
|
14459
|
+
label: element,
|
|
14460
|
+
});
|
|
14461
|
+
}
|
|
14462
|
+
})
|
|
14463
|
+
matches.sort(function(a, b) { return a.value.localeCompare(b.value) });
|
|
14464
|
+
done(matches);
|
|
14465
|
+
})
|
|
14466
|
+
}
|
|
14467
|
+
}
|
|
14468
|
+
|
|
14203
14469
|
// This is a hand-generated list of completions for the core nodes (based on the node help html).
|
|
14204
14470
|
var msgCompletions = [
|
|
14205
14471
|
{ value: "payload" },
|
|
@@ -14264,20 +14530,22 @@ RED.stack = (function() {
|
|
|
14264
14530
|
{ value: "_session", source: ["websocket out","tcp out"] },
|
|
14265
14531
|
]
|
|
14266
14532
|
var allOptions = {
|
|
14267
|
-
msg: {value:"msg",label:"msg.",validate:RED.utils.validatePropertyExpression, autoComplete:
|
|
14533
|
+
msg: {value:"msg",label:"msg.",validate:RED.utils.validatePropertyExpression, autoComplete: msgAutoComplete(msgCompletions)},
|
|
14268
14534
|
flow: {value:"flow",label:"flow.",hasValue:true,
|
|
14269
14535
|
options:[],
|
|
14270
14536
|
validate:RED.utils.validatePropertyExpression,
|
|
14271
14537
|
parse: contextParse,
|
|
14272
14538
|
export: contextExport,
|
|
14273
|
-
valueLabel: contextLabel
|
|
14539
|
+
valueLabel: contextLabel,
|
|
14540
|
+
autoComplete: contextAutoComplete
|
|
14274
14541
|
},
|
|
14275
14542
|
global: {value:"global",label:"global.",hasValue:true,
|
|
14276
14543
|
options:[],
|
|
14277
14544
|
validate:RED.utils.validatePropertyExpression,
|
|
14278
14545
|
parse: contextParse,
|
|
14279
14546
|
export: contextExport,
|
|
14280
|
-
valueLabel: contextLabel
|
|
14547
|
+
valueLabel: contextLabel,
|
|
14548
|
+
autoComplete: contextAutoComplete
|
|
14281
14549
|
},
|
|
14282
14550
|
str: {value:"str",label:"string",icon:"red/images/typedInput/az.svg"},
|
|
14283
14551
|
num: {value:"num",label:"number",icon:"red/images/typedInput/09.svg",validate: function(v) {
|
|
@@ -14312,7 +14580,25 @@ RED.stack = (function() {
|
|
|
14312
14580
|
}
|
|
14313
14581
|
},
|
|
14314
14582
|
re: {value:"re",label:"regular expression",icon:"red/images/typedInput/re.svg"},
|
|
14315
|
-
date: {
|
|
14583
|
+
date: {
|
|
14584
|
+
value:"date",
|
|
14585
|
+
label:"timestamp",
|
|
14586
|
+
icon:"fa fa-clock-o",
|
|
14587
|
+
options:[
|
|
14588
|
+
{
|
|
14589
|
+
label: 'milliseconds since epoch',
|
|
14590
|
+
value: ''
|
|
14591
|
+
},
|
|
14592
|
+
{
|
|
14593
|
+
label: 'YYYY-MM-DDTHH:mm:ss.sssZ',
|
|
14594
|
+
value: 'iso'
|
|
14595
|
+
},
|
|
14596
|
+
{
|
|
14597
|
+
label: 'JavaScript Date Object',
|
|
14598
|
+
value: 'object'
|
|
14599
|
+
}
|
|
14600
|
+
]
|
|
14601
|
+
},
|
|
14316
14602
|
jsonata: {
|
|
14317
14603
|
value: "jsonata",
|
|
14318
14604
|
label: "expression",
|
|
@@ -14349,7 +14635,8 @@ RED.stack = (function() {
|
|
|
14349
14635
|
env: {
|
|
14350
14636
|
value: "env",
|
|
14351
14637
|
label: "env variable",
|
|
14352
|
-
icon: "red/images/typedInput/env.svg"
|
|
14638
|
+
icon: "red/images/typedInput/env.svg",
|
|
14639
|
+
autoComplete: envAutoComplete
|
|
14353
14640
|
},
|
|
14354
14641
|
node: {
|
|
14355
14642
|
value: "node",
|
|
@@ -14481,18 +14768,75 @@ RED.stack = (function() {
|
|
|
14481
14768
|
eyeButton.show();
|
|
14482
14769
|
}
|
|
14483
14770
|
}
|
|
14771
|
+
},
|
|
14772
|
+
'conf-types': {
|
|
14773
|
+
value: "conf-types",
|
|
14774
|
+
label: "config",
|
|
14775
|
+
icon: "fa fa-cog",
|
|
14776
|
+
// hasValue: false,
|
|
14777
|
+
valueLabel: function (container, value) {
|
|
14778
|
+
// get the selected option (for access to the "name" and "module" properties)
|
|
14779
|
+
const _options = this._optionsCache || this.typeList.find(opt => opt.value === value)?.options || []
|
|
14780
|
+
const selectedOption = _options.find(opt => opt.value === value) || {
|
|
14781
|
+
title: '',
|
|
14782
|
+
name: '',
|
|
14783
|
+
module: ''
|
|
14784
|
+
}
|
|
14785
|
+
container.attr("title", selectedOption.title) // set tooltip to the full path/id of the module/node
|
|
14786
|
+
container.text(selectedOption.name) // apply the "name" of the selected option
|
|
14787
|
+
// set "line-height" such as to make the "name" appear further up, giving room for the "module" to be displayed below the value
|
|
14788
|
+
container.css("line-height", "1.4em")
|
|
14789
|
+
// add the module name in smaller, lighter font below the value
|
|
14790
|
+
$('<div></div>').text(selectedOption.module).css({
|
|
14791
|
+
// "font-family": "var(--red-ui-monospace-font)",
|
|
14792
|
+
color: "var(--red-ui-tertiary-text-color)",
|
|
14793
|
+
"font-size": "0.8em",
|
|
14794
|
+
"line-height": "1em",
|
|
14795
|
+
opacity: 0.8
|
|
14796
|
+
}).appendTo(container);
|
|
14797
|
+
},
|
|
14798
|
+
// hasValue: false,
|
|
14799
|
+
options: function () {
|
|
14800
|
+
if (this._optionsCache) {
|
|
14801
|
+
return this._optionsCache
|
|
14802
|
+
}
|
|
14803
|
+
const configNodes = RED.nodes.registry.getNodeDefinitions({configOnly: true, filter: (def) => def.type !== "global-config"}).map((def) => {
|
|
14804
|
+
// create a container with with 2 rows (row 1 for the name, row 2 for the module name in smaller, lighter font)
|
|
14805
|
+
const container = $('<div style="display: flex; flex-direction: column; justify-content: space-between; row-gap: 1px;">')
|
|
14806
|
+
const row1Name = $('<div>').text(def.type)
|
|
14807
|
+
const row2Module = $('<div style="font-size: 0.8em; color: var(--red-ui-tertiary-text-color);">').text(def.set.module)
|
|
14808
|
+
container.append(row1Name, row2Module)
|
|
14809
|
+
|
|
14810
|
+
return {
|
|
14811
|
+
value: def.type,
|
|
14812
|
+
name: def.type,
|
|
14813
|
+
enabled: def.set.enabled ?? true,
|
|
14814
|
+
local: def.set.local,
|
|
14815
|
+
title: def.set.id, // tooltip e.g. "node-red-contrib-foo/bar"
|
|
14816
|
+
module: def.set.module,
|
|
14817
|
+
icon: container[0].outerHTML.trim(), // the typeInput will interpret this as html text and render it in the anchor
|
|
14818
|
+
}
|
|
14819
|
+
})
|
|
14820
|
+
this._optionsCache = configNodes
|
|
14821
|
+
return configNodes
|
|
14822
|
+
}
|
|
14484
14823
|
}
|
|
14485
14824
|
};
|
|
14486
14825
|
|
|
14826
|
+
|
|
14487
14827
|
// For a type with options, check value is a valid selection
|
|
14488
14828
|
// If !opt.multiple, returns the valid option object
|
|
14489
14829
|
// if opt.multiple, returns an array of valid option objects
|
|
14490
14830
|
// If not valid, returns null;
|
|
14491
14831
|
|
|
14492
14832
|
function isOptionValueValid(opt, currentVal) {
|
|
14833
|
+
let _options = opt.options
|
|
14834
|
+
if (typeof _options === "function") {
|
|
14835
|
+
_options = _options.call(this)
|
|
14836
|
+
}
|
|
14493
14837
|
if (!opt.multiple) {
|
|
14494
|
-
for (var i=0;i<
|
|
14495
|
-
op =
|
|
14838
|
+
for (var i=0;i<_options.length;i++) {
|
|
14839
|
+
op = _options[i];
|
|
14496
14840
|
if (typeof op === "string" && op === currentVal) {
|
|
14497
14841
|
return {value:currentVal}
|
|
14498
14842
|
} else if (op.value === currentVal) {
|
|
@@ -14509,8 +14853,8 @@ RED.stack = (function() {
|
|
|
14509
14853
|
currentValues[v] = true;
|
|
14510
14854
|
}
|
|
14511
14855
|
});
|
|
14512
|
-
for (var i=0;i<
|
|
14513
|
-
op =
|
|
14856
|
+
for (var i=0;i<_options.length;i++) {
|
|
14857
|
+
op = _options[i];
|
|
14514
14858
|
var val = typeof op === "string" ? op : op.value;
|
|
14515
14859
|
if (currentValues.hasOwnProperty(val)) {
|
|
14516
14860
|
delete currentValues[val];
|
|
@@ -14525,6 +14869,7 @@ RED.stack = (function() {
|
|
|
14525
14869
|
}
|
|
14526
14870
|
|
|
14527
14871
|
var nlsd = false;
|
|
14872
|
+
let contextStoreOptions;
|
|
14528
14873
|
|
|
14529
14874
|
$.widget( "nodered.typedInput", {
|
|
14530
14875
|
_create: function() {
|
|
@@ -14536,7 +14881,7 @@ RED.stack = (function() {
|
|
|
14536
14881
|
}
|
|
14537
14882
|
}
|
|
14538
14883
|
var contextStores = RED.settings.context.stores;
|
|
14539
|
-
|
|
14884
|
+
contextStoreOptions = contextStores.map(function(store) {
|
|
14540
14885
|
return {value:store,label: store, icon:'<i class="red-ui-typedInput-icon fa fa-database"></i>'}
|
|
14541
14886
|
}).sort(function(A,B) {
|
|
14542
14887
|
if (A.value === RED.settings.context.default) {
|
|
@@ -14547,13 +14892,17 @@ RED.stack = (function() {
|
|
|
14547
14892
|
return A.value.localeCompare(B.value);
|
|
14548
14893
|
}
|
|
14549
14894
|
})
|
|
14550
|
-
if (
|
|
14895
|
+
if (contextStoreOptions.length < 2) {
|
|
14551
14896
|
allOptions.flow.options = [];
|
|
14552
14897
|
allOptions.global.options = [];
|
|
14553
14898
|
} else {
|
|
14554
|
-
allOptions.flow.options =
|
|
14555
|
-
allOptions.global.options =
|
|
14899
|
+
allOptions.flow.options = contextStoreOptions;
|
|
14900
|
+
allOptions.global.options = contextStoreOptions;
|
|
14556
14901
|
}
|
|
14902
|
+
// Translate timestamp options
|
|
14903
|
+
allOptions.date.options.forEach(opt => {
|
|
14904
|
+
opt.label = RED._("typedInput.date.format." + (opt.value || 'timestamp'), {defaultValue: opt.label})
|
|
14905
|
+
})
|
|
14557
14906
|
}
|
|
14558
14907
|
nlsd = true;
|
|
14559
14908
|
var that = this;
|
|
@@ -14642,7 +14991,7 @@ RED.stack = (function() {
|
|
|
14642
14991
|
that.element.trigger('paste',evt);
|
|
14643
14992
|
});
|
|
14644
14993
|
this.input.on('keydown', function(evt) {
|
|
14645
|
-
if (that.typeMap[that.propertyType].autoComplete) {
|
|
14994
|
+
if (that.typeMap[that.propertyType].autoComplete || that.input.hasClass('red-ui-autoComplete')) {
|
|
14646
14995
|
return
|
|
14647
14996
|
}
|
|
14648
14997
|
if (evt.keyCode >= 37 && evt.keyCode <= 40) {
|
|
@@ -14936,7 +15285,9 @@ RED.stack = (function() {
|
|
|
14936
15285
|
if (this.optionMenu) {
|
|
14937
15286
|
this.optionMenu.remove();
|
|
14938
15287
|
}
|
|
14939
|
-
this.menu
|
|
15288
|
+
if (this.menu) {
|
|
15289
|
+
this.menu.remove();
|
|
15290
|
+
}
|
|
14940
15291
|
this.uiSelect.remove();
|
|
14941
15292
|
},
|
|
14942
15293
|
types: function(types) {
|
|
@@ -14969,7 +15320,7 @@ RED.stack = (function() {
|
|
|
14969
15320
|
this.menu = this._createMenu(this.typeList,{},function(v) { that.type(v) });
|
|
14970
15321
|
if (currentType && !this.typeMap.hasOwnProperty(currentType)) {
|
|
14971
15322
|
if (!firstCall) {
|
|
14972
|
-
this.type(this.typeList[0]
|
|
15323
|
+
this.type(this.typeList[0]?.value || ""); // permit empty typeList
|
|
14973
15324
|
}
|
|
14974
15325
|
} else {
|
|
14975
15326
|
this.propertyType = null;
|
|
@@ -15006,6 +15357,11 @@ RED.stack = (function() {
|
|
|
15006
15357
|
var selectedOption = [];
|
|
15007
15358
|
var valueToCheck = value;
|
|
15008
15359
|
if (opt.options) {
|
|
15360
|
+
let _options = opt.options
|
|
15361
|
+
if (typeof opt.options === "function") {
|
|
15362
|
+
_options = opt.options.call(this)
|
|
15363
|
+
}
|
|
15364
|
+
|
|
15009
15365
|
if (opt.hasValue && opt.parse) {
|
|
15010
15366
|
var parts = opt.parse(value);
|
|
15011
15367
|
if (this.options.debug) { console.log(this.identifier,"new parse",parts) }
|
|
@@ -15019,8 +15375,8 @@ RED.stack = (function() {
|
|
|
15019
15375
|
checkValues = valueToCheck.split(",");
|
|
15020
15376
|
}
|
|
15021
15377
|
checkValues.forEach(function(valueToCheck) {
|
|
15022
|
-
for (var i=0;i<
|
|
15023
|
-
var op =
|
|
15378
|
+
for (var i=0;i<_options.length;i++) {
|
|
15379
|
+
var op = _options[i];
|
|
15024
15380
|
if (typeof op === "string") {
|
|
15025
15381
|
if (op === valueToCheck || op === ""+valueToCheck) {
|
|
15026
15382
|
selectedOption.push(that.activeOptions[op]);
|
|
@@ -15055,7 +15411,7 @@ RED.stack = (function() {
|
|
|
15055
15411
|
},
|
|
15056
15412
|
type: function(type) {
|
|
15057
15413
|
if (!arguments.length) {
|
|
15058
|
-
return this.propertyType;
|
|
15414
|
+
return this.propertyType || this.options?.default || '';
|
|
15059
15415
|
} else {
|
|
15060
15416
|
var that = this;
|
|
15061
15417
|
if (this.options.debug) { console.log(this.identifier,"----- SET TYPE -----",type) }
|
|
@@ -15065,6 +15421,9 @@ RED.stack = (function() {
|
|
|
15065
15421
|
// If previousType is !null, then this is a change of the type, rather than the initialisation
|
|
15066
15422
|
var previousType = this.typeMap[this.propertyType];
|
|
15067
15423
|
previousValue = this.input.val();
|
|
15424
|
+
if (this.input.hasClass('red-ui-autoComplete')) {
|
|
15425
|
+
this.input.autoComplete("destroy");
|
|
15426
|
+
}
|
|
15068
15427
|
|
|
15069
15428
|
if (previousType && this.typeChanged) {
|
|
15070
15429
|
if (this.options.debug) { console.log(this.identifier,"typeChanged",{previousType,previousValue}) }
|
|
@@ -15111,7 +15470,9 @@ RED.stack = (function() {
|
|
|
15111
15470
|
this.input.val(this.oldValues.hasOwnProperty("_")?this.oldValues["_"]:(opt.default||""))
|
|
15112
15471
|
}
|
|
15113
15472
|
if (previousType.autoComplete) {
|
|
15114
|
-
this.input.autoComplete
|
|
15473
|
+
if (this.input.hasClass('red-ui-autoComplete')) {
|
|
15474
|
+
this.input.autoComplete("destroy");
|
|
15475
|
+
}
|
|
15115
15476
|
}
|
|
15116
15477
|
}
|
|
15117
15478
|
this.propertyType = type;
|
|
@@ -15151,6 +15512,10 @@ RED.stack = (function() {
|
|
|
15151
15512
|
this.optionMenu = null;
|
|
15152
15513
|
}
|
|
15153
15514
|
if (opt.options) {
|
|
15515
|
+
let _options = opt.options
|
|
15516
|
+
if (typeof _options === "function") {
|
|
15517
|
+
_options = opt.options.call(this);
|
|
15518
|
+
}
|
|
15154
15519
|
if (this.optionExpandButton) {
|
|
15155
15520
|
this.optionExpandButton.hide();
|
|
15156
15521
|
this.optionExpandButton.shown = false;
|
|
@@ -15167,7 +15532,7 @@ RED.stack = (function() {
|
|
|
15167
15532
|
this.valueLabelContainer.hide();
|
|
15168
15533
|
}
|
|
15169
15534
|
this.activeOptions = {};
|
|
15170
|
-
|
|
15535
|
+
_options.forEach(function(o) {
|
|
15171
15536
|
if (typeof o === 'string') {
|
|
15172
15537
|
that.activeOptions[o] = {label:o,value:o};
|
|
15173
15538
|
} else {
|
|
@@ -15187,7 +15552,7 @@ RED.stack = (function() {
|
|
|
15187
15552
|
if (validValues) {
|
|
15188
15553
|
that._updateOptionSelectLabel(validValues)
|
|
15189
15554
|
} else {
|
|
15190
|
-
op =
|
|
15555
|
+
op = _options[0] || {value:""}; // permit zero options
|
|
15191
15556
|
if (typeof op === "string") {
|
|
15192
15557
|
this.value(op);
|
|
15193
15558
|
that._updateOptionSelectLabel({value:op});
|
|
@@ -15206,7 +15571,7 @@ RED.stack = (function() {
|
|
|
15206
15571
|
that._updateOptionSelectLabel(validValues);
|
|
15207
15572
|
}
|
|
15208
15573
|
} else {
|
|
15209
|
-
var selectedOption = this.optionValue||
|
|
15574
|
+
var selectedOption = this.optionValue||_options[0];
|
|
15210
15575
|
if (opt.parse) {
|
|
15211
15576
|
var selectedOptionObj = typeof selectedOption === "string"?{value:selectedOption}:selectedOption
|
|
15212
15577
|
var parts = opt.parse(this.input.val(),selectedOptionObj);
|
|
@@ -15239,8 +15604,18 @@ RED.stack = (function() {
|
|
|
15239
15604
|
} else {
|
|
15240
15605
|
this.optionSelectTrigger.hide();
|
|
15241
15606
|
}
|
|
15607
|
+
if (opt.autoComplete) {
|
|
15608
|
+
let searchFunction = opt.autoComplete
|
|
15609
|
+
if (searchFunction.length === 0) {
|
|
15610
|
+
searchFunction = opt.autoComplete.call(this)
|
|
15611
|
+
}
|
|
15612
|
+
this.input.autoComplete({
|
|
15613
|
+
search: searchFunction,
|
|
15614
|
+
minLength: 0
|
|
15615
|
+
})
|
|
15616
|
+
}
|
|
15242
15617
|
}
|
|
15243
|
-
this.optionMenu = this._createMenu(
|
|
15618
|
+
this.optionMenu = this._createMenu(_options,opt,function(v){
|
|
15244
15619
|
if (!opt.multiple) {
|
|
15245
15620
|
that._updateOptionSelectLabel(that.activeOptions[v]);
|
|
15246
15621
|
if (!opt.hasValue) {
|
|
@@ -15281,8 +15656,12 @@ RED.stack = (function() {
|
|
|
15281
15656
|
this.valueLabelContainer.hide();
|
|
15282
15657
|
this.elementDiv.show();
|
|
15283
15658
|
if (opt.autoComplete) {
|
|
15659
|
+
let searchFunction = opt.autoComplete
|
|
15660
|
+
if (searchFunction.length === 0) {
|
|
15661
|
+
searchFunction = opt.autoComplete.call(this)
|
|
15662
|
+
}
|
|
15284
15663
|
this.input.autoComplete({
|
|
15285
|
-
search:
|
|
15664
|
+
search: searchFunction,
|
|
15286
15665
|
minLength: 0
|
|
15287
15666
|
})
|
|
15288
15667
|
}
|
|
@@ -26791,6 +27170,10 @@ RED.view = (function() {
|
|
|
26791
27170
|
}
|
|
26792
27171
|
})
|
|
26793
27172
|
}
|
|
27173
|
+
if (selection.links) {
|
|
27174
|
+
selectedLinks.clear();
|
|
27175
|
+
selection.links.forEach(selectedLinks.add);
|
|
27176
|
+
}
|
|
26794
27177
|
}
|
|
26795
27178
|
}
|
|
26796
27179
|
updateSelection();
|
|
@@ -33948,47 +34331,78 @@ RED.editor = (function() {
|
|
|
33948
34331
|
|
|
33949
34332
|
/**
|
|
33950
34333
|
* Create a config-node select box for this property
|
|
33951
|
-
* @param node - the node being edited
|
|
33952
|
-
* @param property - the name of the
|
|
33953
|
-
* @param type - the type of the config-node
|
|
34334
|
+
* @param {Object} node - the node being edited
|
|
34335
|
+
* @param {String} property - the name of the node property
|
|
34336
|
+
* @param {String} type - the type of the config-node
|
|
34337
|
+
* @param {"node-config-input"|"node-input"|"node-input-subflow-env"} prefix - the prefix to use in the input element ids
|
|
34338
|
+
* @param {Function} [filter] - a function to filter the list of config nodes
|
|
34339
|
+
* @param {Object} [env] - the environment variable object (only used for subflow env vars)
|
|
33954
34340
|
*/
|
|
33955
|
-
function prepareConfigNodeSelect(node,property,type,prefix,filter) {
|
|
33956
|
-
|
|
33957
|
-
if (
|
|
34341
|
+
function prepareConfigNodeSelect(node, property, type, prefix, filter, env) {
|
|
34342
|
+
let nodeValue
|
|
34343
|
+
if (prefix === 'node-input-subflow-env') {
|
|
34344
|
+
nodeValue = env?.value
|
|
34345
|
+
} else {
|
|
34346
|
+
nodeValue = node[property]
|
|
34347
|
+
}
|
|
34348
|
+
|
|
34349
|
+
const buttonId = `${prefix}-lookup-${property}`
|
|
34350
|
+
const selectId = prefix + '-' + property
|
|
34351
|
+
const input = $(`#${selectId}`);
|
|
34352
|
+
if (input.length === 0) {
|
|
33958
34353
|
return;
|
|
33959
34354
|
}
|
|
33960
|
-
|
|
33961
|
-
|
|
33962
|
-
|
|
34355
|
+
const attrStyle = input.attr('style');
|
|
34356
|
+
let newWidth;
|
|
34357
|
+
let m;
|
|
33963
34358
|
if ((m = /(^|\s|;)width\s*:\s*([^;]+)/i.exec(attrStyle)) !== null) {
|
|
33964
34359
|
newWidth = m[2].trim();
|
|
33965
34360
|
} else {
|
|
33966
34361
|
newWidth = "70%";
|
|
33967
34362
|
}
|
|
33968
|
-
|
|
34363
|
+
const outerWrap = $("<div></div>").css({
|
|
33969
34364
|
width: newWidth,
|
|
33970
|
-
display:'inline-flex'
|
|
34365
|
+
display: 'inline-flex'
|
|
33971
34366
|
});
|
|
33972
|
-
|
|
34367
|
+
const select = $('<select id="' + selectId + '"></select>').appendTo(outerWrap);
|
|
33973
34368
|
input.replaceWith(outerWrap);
|
|
33974
34369
|
// set the style attr directly - using width() on FF causes a value of 114%...
|
|
33975
34370
|
select.css({
|
|
33976
34371
|
'flex-grow': 1
|
|
33977
34372
|
});
|
|
33978
|
-
updateConfigNodeSelect(property,type,
|
|
33979
|
-
|
|
33980
|
-
.
|
|
34373
|
+
updateConfigNodeSelect(property, type, nodeValue, prefix, filter);
|
|
34374
|
+
const disableButton = function(disabled) {
|
|
34375
|
+
btn.prop( "disabled", !!disabled)
|
|
34376
|
+
btn.toggleClass("disabled", !!disabled)
|
|
34377
|
+
}
|
|
34378
|
+
// create the edit button
|
|
34379
|
+
const btn = $('<a id="' + buttonId + '" class="red-ui-button"><i class="fa fa-pencil"></i></a>')
|
|
34380
|
+
.css({ "margin-left": "10px" })
|
|
33981
34381
|
.appendTo(outerWrap);
|
|
33982
|
-
|
|
33983
|
-
|
|
34382
|
+
|
|
34383
|
+
// add the click handler
|
|
34384
|
+
btn.on("click", function (e) {
|
|
34385
|
+
const selectedOpt = select.find(":selected")
|
|
34386
|
+
if (selectedOpt.data('env')) { return } // don't show the dialog for env vars items (MVP. Future enhancement: lookup the env, if present, show the associated edit dialog)
|
|
34387
|
+
if (btn.prop("disabled")) { return }
|
|
34388
|
+
showEditConfigNodeDialog(property, type, selectedOpt.val(), prefix, node);
|
|
33984
34389
|
e.preventDefault();
|
|
33985
34390
|
});
|
|
34391
|
+
|
|
34392
|
+
// dont permit the user to click the button if the selected option is an env var
|
|
34393
|
+
select.on("change", function () {
|
|
34394
|
+
const selectedOpt = select.find(":selected")
|
|
34395
|
+
if (selectedOpt?.data('env')) {
|
|
34396
|
+
disableButton(true)
|
|
34397
|
+
} else {
|
|
34398
|
+
disableButton(false)
|
|
34399
|
+
}
|
|
34400
|
+
});
|
|
33986
34401
|
var label = "";
|
|
33987
|
-
var configNode = RED.nodes.node(
|
|
33988
|
-
var node_def = RED.nodes.getType(type);
|
|
34402
|
+
var configNode = RED.nodes.node(nodeValue);
|
|
33989
34403
|
|
|
33990
34404
|
if (configNode) {
|
|
33991
|
-
label = RED.utils.getNodeLabel(configNode,configNode.id);
|
|
34405
|
+
label = RED.utils.getNodeLabel(configNode, configNode.id);
|
|
33992
34406
|
}
|
|
33993
34407
|
input.val(label);
|
|
33994
34408
|
}
|
|
@@ -34390,12 +34804,9 @@ RED.editor = (function() {
|
|
|
34390
34804
|
}
|
|
34391
34805
|
|
|
34392
34806
|
function defaultConfigNodeSort(A,B) {
|
|
34393
|
-
|
|
34394
|
-
|
|
34395
|
-
|
|
34396
|
-
return 1;
|
|
34397
|
-
}
|
|
34398
|
-
return 0;
|
|
34807
|
+
// sort case insensitive so that `[env] node-name` items are at the top and
|
|
34808
|
+
// not mixed inbetween the the lower and upper case items
|
|
34809
|
+
return (A.__label__ || '').localeCompare((B.__label__ || ''), undefined, {sensitivity: 'base'})
|
|
34399
34810
|
}
|
|
34400
34811
|
|
|
34401
34812
|
function updateConfigNodeSelect(name,type,value,prefix,filter) {
|
|
@@ -34410,7 +34821,7 @@ RED.editor = (function() {
|
|
|
34410
34821
|
}
|
|
34411
34822
|
$("#"+prefix+"-"+name).val(value);
|
|
34412
34823
|
} else {
|
|
34413
|
-
|
|
34824
|
+
let inclSubflowEnvvars = false
|
|
34414
34825
|
var select = $("#"+prefix+"-"+name);
|
|
34415
34826
|
var node_def = RED.nodes.getType(type);
|
|
34416
34827
|
select.children().remove();
|
|
@@ -34418,6 +34829,7 @@ RED.editor = (function() {
|
|
|
34418
34829
|
var activeWorkspace = RED.nodes.workspace(RED.workspaces.active());
|
|
34419
34830
|
if (!activeWorkspace) {
|
|
34420
34831
|
activeWorkspace = RED.nodes.subflow(RED.workspaces.active());
|
|
34832
|
+
inclSubflowEnvvars = true
|
|
34421
34833
|
}
|
|
34422
34834
|
|
|
34423
34835
|
var configNodes = [];
|
|
@@ -34433,6 +34845,31 @@ RED.editor = (function() {
|
|
|
34433
34845
|
}
|
|
34434
34846
|
}
|
|
34435
34847
|
});
|
|
34848
|
+
|
|
34849
|
+
// as includeSubflowEnvvars is true, this is a subflow.
|
|
34850
|
+
// include any 'conf-types' env vars as a list of avaiable configs
|
|
34851
|
+
// in the config dropdown as `[env] node-name`
|
|
34852
|
+
if (inclSubflowEnvvars && activeWorkspace.env) {
|
|
34853
|
+
const parentEnv = activeWorkspace.env.filter(env => env.ui?.type === 'conf-types' && env.type === type)
|
|
34854
|
+
if (parentEnv && parentEnv.length > 0) {
|
|
34855
|
+
const locale = RED.i18n.lang()
|
|
34856
|
+
for (let i = 0; i < parentEnv.length; i++) {
|
|
34857
|
+
const tenv = parentEnv[i]
|
|
34858
|
+
const ui = tenv.ui || {}
|
|
34859
|
+
const labels = ui.label || {}
|
|
34860
|
+
const labelText = RED.editor.envVarList.lookupLabel(labels, labels["en-US"] || tenv.name, locale)
|
|
34861
|
+
const config = {
|
|
34862
|
+
env: tenv,
|
|
34863
|
+
id: '${' + parentEnv[0].name + '}',
|
|
34864
|
+
type: type,
|
|
34865
|
+
label: labelText,
|
|
34866
|
+
__label__: `[env] ${labelText}`
|
|
34867
|
+
}
|
|
34868
|
+
configNodes.push(config)
|
|
34869
|
+
}
|
|
34870
|
+
}
|
|
34871
|
+
}
|
|
34872
|
+
|
|
34436
34873
|
var configSortFn = defaultConfigNodeSort;
|
|
34437
34874
|
if (typeof node_def.sort == "function") {
|
|
34438
34875
|
configSortFn = node_def.sort;
|
|
@@ -34444,7 +34881,10 @@ RED.editor = (function() {
|
|
|
34444
34881
|
}
|
|
34445
34882
|
|
|
34446
34883
|
configNodes.forEach(function(cn) {
|
|
34447
|
-
$('<option value="'+cn.id+'"'+(value==cn.id?" selected":"")+'></option>').text(RED.text.bidi.enforceTextDirectionWithUCC(cn.__label__)).appendTo(select);
|
|
34884
|
+
const option = $('<option value="'+cn.id+'"'+(value==cn.id?" selected":"")+'></option>').text(RED.text.bidi.enforceTextDirectionWithUCC(cn.__label__)).appendTo(select);
|
|
34885
|
+
if (cn.env) {
|
|
34886
|
+
option.data('env', cn.env) // set a data attribute to indicate this is an env var (to inhibit the edit button)
|
|
34887
|
+
}
|
|
34448
34888
|
delete cn.__label__;
|
|
34449
34889
|
});
|
|
34450
34890
|
|
|
@@ -35105,9 +35545,16 @@ RED.editor = (function() {
|
|
|
35105
35545
|
}
|
|
35106
35546
|
RED.tray.close(function() {
|
|
35107
35547
|
var filter = null;
|
|
35108
|
-
|
|
35109
|
-
|
|
35110
|
-
|
|
35548
|
+
// when editing a config via subflow edit panel, the `configProperty` will not
|
|
35549
|
+
// necessarily be a property of the editContext._def.defaults object
|
|
35550
|
+
// Also, when editing via dashboard sidebar, editContext can be null
|
|
35551
|
+
// so we need to guard both scenarios
|
|
35552
|
+
if (editContext?._def) {
|
|
35553
|
+
const isSubflow = (editContext._def.type === 'subflow' || /subflow:.*/.test(editContext._def.type))
|
|
35554
|
+
if (editContext && !isSubflow && typeof editContext._def.defaults?.[configProperty]?.filter === 'function') {
|
|
35555
|
+
filter = function(n) {
|
|
35556
|
+
return editContext._def.defaults[configProperty].filter.call(editContext,n);
|
|
35557
|
+
}
|
|
35111
35558
|
}
|
|
35112
35559
|
}
|
|
35113
35560
|
updateConfigNodeSelect(configProperty,configType,editing_config_node.id,prefix,filter);
|
|
@@ -35168,7 +35615,7 @@ RED.editor = (function() {
|
|
|
35168
35615
|
RED.history.push(historyEvent);
|
|
35169
35616
|
RED.tray.close(function() {
|
|
35170
35617
|
var filter = null;
|
|
35171
|
-
if (editContext && typeof editContext._def.defaults[configProperty]
|
|
35618
|
+
if (editContext && typeof editContext._def.defaults[configProperty]?.filter === 'function') {
|
|
35172
35619
|
filter = function(n) {
|
|
35173
35620
|
return editContext._def.defaults[configProperty].filter.call(editContext,n);
|
|
35174
35621
|
}
|
|
@@ -35709,6 +36156,7 @@ RED.editor = (function() {
|
|
|
35709
36156
|
}
|
|
35710
36157
|
},
|
|
35711
36158
|
editBuffer: function(options) { showTypeEditor("_buffer", options) },
|
|
36159
|
+
getEditStack: function () { return [...editStack] },
|
|
35712
36160
|
buildEditForm: buildEditForm,
|
|
35713
36161
|
validateNode: validateNode,
|
|
35714
36162
|
updateNodeProperties: updateNodeProperties,
|
|
@@ -35753,7 +36201,8 @@ RED.editor = (function() {
|
|
|
35753
36201
|
filteredEditPanes[type] = filter
|
|
35754
36202
|
}
|
|
35755
36203
|
editPanes[type] = definition;
|
|
35756
|
-
}
|
|
36204
|
+
},
|
|
36205
|
+
prepareConfigNodeSelect: prepareConfigNodeSelect,
|
|
35757
36206
|
}
|
|
35758
36207
|
})();
|
|
35759
36208
|
;;(function() {
|
|
@@ -37407,8 +37856,9 @@ RED.editor = (function() {
|
|
|
37407
37856
|
;RED.editor.envVarList = (function() {
|
|
37408
37857
|
|
|
37409
37858
|
var currentLocale = 'en-US';
|
|
37410
|
-
|
|
37411
|
-
|
|
37859
|
+
const DEFAULT_ENV_TYPE_LIST = ['str','num','bool','json','bin','env'];
|
|
37860
|
+
const DEFAULT_ENV_TYPE_LIST_INC_CONFTYPES = ['str','num','bool','json','bin','env','conf-types'];
|
|
37861
|
+
const DEFAULT_ENV_TYPE_LIST_INC_CRED = ['str','num','bool','json','bin','env','cred','jsonata'];
|
|
37412
37862
|
|
|
37413
37863
|
/**
|
|
37414
37864
|
* Create env var edit interface
|
|
@@ -37416,8 +37866,8 @@ RED.editor = (function() {
|
|
|
37416
37866
|
* @param node - subflow node
|
|
37417
37867
|
*/
|
|
37418
37868
|
function buildPropertiesList(envContainer, node) {
|
|
37419
|
-
|
|
37420
|
-
|
|
37869
|
+
if(RED.editor.envVarList.debug) { console.log('envVarList: buildPropertiesList', envContainer, node) }
|
|
37870
|
+
const isTemplateNode = (node.type === "subflow");
|
|
37421
37871
|
|
|
37422
37872
|
envContainer
|
|
37423
37873
|
.css({
|
|
@@ -37489,7 +37939,14 @@ RED.editor = (function() {
|
|
|
37489
37939
|
// if `opt.ui` does not exist, then apply defaults. If these
|
|
37490
37940
|
// defaults do not change then they will get stripped off
|
|
37491
37941
|
// before saving.
|
|
37492
|
-
if (opt.type === '
|
|
37942
|
+
if (opt.type === 'conf-types') {
|
|
37943
|
+
opt.ui = opt.ui || {
|
|
37944
|
+
icon: "fa fa-cog",
|
|
37945
|
+
type: "conf-types",
|
|
37946
|
+
opts: {opts:[]}
|
|
37947
|
+
}
|
|
37948
|
+
opt.ui.type = "conf-types";
|
|
37949
|
+
} else if (opt.type === 'cred') {
|
|
37493
37950
|
opt.ui = opt.ui || {
|
|
37494
37951
|
icon: "",
|
|
37495
37952
|
type: "cred"
|
|
@@ -37525,7 +37982,7 @@ RED.editor = (function() {
|
|
|
37525
37982
|
}
|
|
37526
37983
|
});
|
|
37527
37984
|
|
|
37528
|
-
buildEnvEditRow(uiRow, opt
|
|
37985
|
+
buildEnvEditRow(uiRow, opt, nameField, valueField);
|
|
37529
37986
|
nameField.trigger('change');
|
|
37530
37987
|
}
|
|
37531
37988
|
},
|
|
@@ -37587,21 +38044,23 @@ RED.editor = (function() {
|
|
|
37587
38044
|
* @param nameField - name field of env var
|
|
37588
38045
|
* @param valueField - value field of env var
|
|
37589
38046
|
*/
|
|
37590
|
-
function buildEnvEditRow(container,
|
|
38047
|
+
function buildEnvEditRow(container, opt, nameField, valueField) {
|
|
38048
|
+
const ui = opt.ui
|
|
38049
|
+
if(RED.editor.envVarList.debug) { console.log('envVarList: buildEnvEditRow', container, ui, nameField, valueField) }
|
|
37591
38050
|
container.addClass("red-ui-editor-subflow-env-ui-row")
|
|
37592
38051
|
var topRow = $('<div></div>').appendTo(container);
|
|
37593
38052
|
$('<div></div>').appendTo(topRow);
|
|
37594
38053
|
$('<div>').text(RED._("editor.icon")).appendTo(topRow);
|
|
37595
38054
|
$('<div>').text(RED._("editor.label")).appendTo(topRow);
|
|
37596
|
-
$('<div>').text(RED._("editor.inputType")).appendTo(topRow);
|
|
38055
|
+
$('<div class="red-env-ui-input-type-col">').text(RED._("editor.inputType")).appendTo(topRow);
|
|
37597
38056
|
|
|
37598
38057
|
var row = $('<div></div>').appendTo(container);
|
|
37599
38058
|
$('<div><i class="red-ui-editableList-item-handle fa fa-bars"></i></div>').appendTo(row);
|
|
37600
38059
|
var typeOptions = {
|
|
37601
|
-
|
|
37602
|
-
|
|
37603
|
-
|
|
37604
|
-
|
|
38060
|
+
'input': {types:DEFAULT_ENV_TYPE_LIST_INC_CONFTYPES},
|
|
38061
|
+
'select': {opts:[]},
|
|
38062
|
+
'spinner': {},
|
|
38063
|
+
'cred': {}
|
|
37605
38064
|
};
|
|
37606
38065
|
if (ui.opts) {
|
|
37607
38066
|
typeOptions[ui.type] = ui.opts;
|
|
@@ -37666,15 +38125,16 @@ RED.editor = (function() {
|
|
|
37666
38125
|
labelInput.attr("placeholder",$(this).val())
|
|
37667
38126
|
});
|
|
37668
38127
|
|
|
37669
|
-
var inputCell = $('<div></div>').appendTo(row);
|
|
37670
|
-
var
|
|
38128
|
+
var inputCell = $('<div class="red-env-ui-input-type-col"></div>').appendTo(row);
|
|
38129
|
+
var uiInputTypeInput = $('<input type="text">').css("width","100%").appendTo(inputCell);
|
|
37671
38130
|
if (ui.type === "input") {
|
|
37672
|
-
|
|
38131
|
+
uiInputTypeInput.val(ui.opts.types.join(","));
|
|
37673
38132
|
}
|
|
37674
38133
|
var checkbox;
|
|
37675
38134
|
var selectBox;
|
|
37676
38135
|
|
|
37677
|
-
|
|
38136
|
+
// the options presented in the UI section for an "input" type selection
|
|
38137
|
+
uiInputTypeInput.typedInput({
|
|
37678
38138
|
types: [
|
|
37679
38139
|
{
|
|
37680
38140
|
value:"input",
|
|
@@ -37835,7 +38295,7 @@ RED.editor = (function() {
|
|
|
37835
38295
|
}
|
|
37836
38296
|
});
|
|
37837
38297
|
ui.opts.opts = vals;
|
|
37838
|
-
|
|
38298
|
+
uiInputTypeInput.typedInput('value',Date.now())
|
|
37839
38299
|
}
|
|
37840
38300
|
}
|
|
37841
38301
|
}
|
|
@@ -37902,12 +38362,13 @@ RED.editor = (function() {
|
|
|
37902
38362
|
} else {
|
|
37903
38363
|
delete ui.opts.max;
|
|
37904
38364
|
}
|
|
37905
|
-
|
|
38365
|
+
uiInputTypeInput.typedInput('value',Date.now())
|
|
37906
38366
|
}
|
|
37907
38367
|
}
|
|
37908
38368
|
}
|
|
37909
38369
|
}
|
|
37910
38370
|
},
|
|
38371
|
+
'conf-types',
|
|
37911
38372
|
{
|
|
37912
38373
|
value:"none",
|
|
37913
38374
|
label:RED._("editor.inputs.none"), icon:"fa fa-times",hasValue:false
|
|
@@ -37925,14 +38386,20 @@ RED.editor = (function() {
|
|
|
37925
38386
|
// In the case of 'input' type, the typedInput uses the multiple-option
|
|
37926
38387
|
// mode. Its value needs to be set to a comma-separately list of the
|
|
37927
38388
|
// selected options.
|
|
37928
|
-
|
|
38389
|
+
uiInputTypeInput.typedInput('value',ui.opts.types.join(","))
|
|
38390
|
+
} else if (ui.type === 'conf-types') {
|
|
38391
|
+
// In the case of 'conf-types' type, the typedInput will be populated
|
|
38392
|
+
// with a list of all config nodes types installed.
|
|
38393
|
+
// Restore the value to the last selected type
|
|
38394
|
+
uiInputTypeInput.typedInput('value', opt.type)
|
|
37929
38395
|
} else {
|
|
37930
38396
|
// No other type cares about `value`, but doing this will
|
|
37931
38397
|
// force a refresh of the label now that `ui.opts` has
|
|
37932
38398
|
// been updated.
|
|
37933
|
-
|
|
38399
|
+
uiInputTypeInput.typedInput('value',Date.now())
|
|
37934
38400
|
}
|
|
37935
38401
|
|
|
38402
|
+
if(RED.editor.envVarList.debug) { console.log('envVarList: inputCellInput on:typedinputtypechange. ui.type = ' + ui.type) }
|
|
37936
38403
|
switch (ui.type) {
|
|
37937
38404
|
case 'input':
|
|
37938
38405
|
valueField.typedInput('types',ui.opts.types);
|
|
@@ -37950,7 +38417,7 @@ RED.editor = (function() {
|
|
|
37950
38417
|
valueField.typedInput('types',['cred']);
|
|
37951
38418
|
break;
|
|
37952
38419
|
default:
|
|
37953
|
-
valueField.typedInput('types',DEFAULT_ENV_TYPE_LIST)
|
|
38420
|
+
valueField.typedInput('types', DEFAULT_ENV_TYPE_LIST);
|
|
37954
38421
|
}
|
|
37955
38422
|
if (ui.type === 'checkbox') {
|
|
37956
38423
|
valueField.typedInput('type','bool');
|
|
@@ -37962,8 +38429,46 @@ RED.editor = (function() {
|
|
|
37962
38429
|
}
|
|
37963
38430
|
|
|
37964
38431
|
}).on("change", function(evt,type) {
|
|
37965
|
-
|
|
37966
|
-
|
|
38432
|
+
const selectedType = $(this).typedInput('type') // the UI typedInput type
|
|
38433
|
+
if(RED.editor.envVarList.debug) { console.log('envVarList: inputCellInput on:change. selectedType = ' + selectedType) }
|
|
38434
|
+
if (selectedType === 'conf-types') {
|
|
38435
|
+
const selectedConfigType = $(this).typedInput('value') || opt.type
|
|
38436
|
+
let activeWorkspace = RED.nodes.workspace(RED.workspaces.active());
|
|
38437
|
+
if (!activeWorkspace) {
|
|
38438
|
+
activeWorkspace = RED.nodes.subflow(RED.workspaces.active());
|
|
38439
|
+
}
|
|
38440
|
+
|
|
38441
|
+
// get a list of all config nodes matching the selectedValue
|
|
38442
|
+
const configNodes = [];
|
|
38443
|
+
RED.nodes.eachConfig(function(config) {
|
|
38444
|
+
if (config.type == selectedConfigType && (!config.z || config.z === activeWorkspace.id)) {
|
|
38445
|
+
const modulePath = config._def?.set?.id || ''
|
|
38446
|
+
let label = RED.utils.getNodeLabel(config, config.id) || config.id;
|
|
38447
|
+
label += config.d ? ' ['+RED._('workspace.disabled')+']' : '';
|
|
38448
|
+
const _config = {
|
|
38449
|
+
_type: selectedConfigType,
|
|
38450
|
+
value: config.id,
|
|
38451
|
+
label: label,
|
|
38452
|
+
title: modulePath ? modulePath + ' - ' + label : label,
|
|
38453
|
+
enabled: config.d !== true,
|
|
38454
|
+
disabled: config.d === true,
|
|
38455
|
+
}
|
|
38456
|
+
configNodes.push(_config);
|
|
38457
|
+
}
|
|
38458
|
+
});
|
|
38459
|
+
const tiTypes = {
|
|
38460
|
+
value: selectedConfigType,
|
|
38461
|
+
label: "config",
|
|
38462
|
+
icon: "fa fa-cog",
|
|
38463
|
+
options: configNodes,
|
|
38464
|
+
}
|
|
38465
|
+
valueField.typedInput('types', [tiTypes]);
|
|
38466
|
+
valueField.typedInput('type', selectedConfigType);
|
|
38467
|
+
valueField.typedInput('value', opt.value);
|
|
38468
|
+
|
|
38469
|
+
|
|
38470
|
+
} else if (ui.type === 'input') {
|
|
38471
|
+
var types = uiInputTypeInput.typedInput('value');
|
|
37967
38472
|
ui.opts.types = (types === "") ? ["str"] : types.split(",");
|
|
37968
38473
|
valueField.typedInput('types',ui.opts.types);
|
|
37969
38474
|
}
|
|
@@ -37975,7 +38480,7 @@ RED.editor = (function() {
|
|
|
37975
38480
|
})
|
|
37976
38481
|
// Set the input to the right type. This will trigger the 'typedinputtypechange'
|
|
37977
38482
|
// event handler (just above ^^) to update the value if needed
|
|
37978
|
-
|
|
38483
|
+
uiInputTypeInput.typedInput('type',ui.type)
|
|
37979
38484
|
}
|
|
37980
38485
|
|
|
37981
38486
|
function setLocale(l, list) {
|
|
@@ -46896,17 +47401,19 @@ RED.subflow = (function() {
|
|
|
46896
47401
|
|
|
46897
47402
|
|
|
46898
47403
|
/**
|
|
46899
|
-
*
|
|
47404
|
+
* Build the edit dialog for a subflow template (creating/modifying a subflow template)
|
|
47405
|
+
* @param {Object} uiContainer - the jQuery container for the environment variable list
|
|
47406
|
+
* @param {Object} node - the subflow template node
|
|
46900
47407
|
*/
|
|
46901
|
-
function buildEnvControl(
|
|
47408
|
+
function buildEnvControl(uiContainer,node) {
|
|
46902
47409
|
var tabs = RED.tabs.create({
|
|
46903
47410
|
id: "subflow-env-tabs",
|
|
46904
47411
|
onchange: function(tab) {
|
|
46905
47412
|
if (tab.id === "subflow-env-tab-preview") {
|
|
46906
47413
|
var inputContainer = $("#subflow-input-ui");
|
|
46907
|
-
var list =
|
|
47414
|
+
var list = uiContainer.editableList("items");
|
|
46908
47415
|
var exportedEnv = exportEnvList(list, true);
|
|
46909
|
-
buildEnvUI(inputContainer, exportedEnv,node);
|
|
47416
|
+
buildEnvUI(inputContainer, exportedEnv, node);
|
|
46910
47417
|
}
|
|
46911
47418
|
$("#subflow-env-tabs-content").children().hide();
|
|
46912
47419
|
$("#" + tab.id).show();
|
|
@@ -46944,12 +47451,33 @@ RED.subflow = (function() {
|
|
|
46944
47451
|
RED.editor.envVarList.setLocale(locale);
|
|
46945
47452
|
}
|
|
46946
47453
|
|
|
46947
|
-
|
|
46948
|
-
|
|
47454
|
+
/**
|
|
47455
|
+
* Build a UI row for a subflow instance environment variable
|
|
47456
|
+
* Also used to build the UI row for subflow template preview
|
|
47457
|
+
* @param {JQuery} row - A form row element
|
|
47458
|
+
* @param {Object} tenv - A template environment variable
|
|
47459
|
+
* @param {String} tenv.name - The name of the environment variable
|
|
47460
|
+
* @param {String} tenv.type - The type of the environment variable
|
|
47461
|
+
* @param {String} tenv.value - The value set for this environment variable
|
|
47462
|
+
* @param {Object} tenv.parent - The parent environment variable
|
|
47463
|
+
* @param {String} tenv.parent.value - The value set for the parent environment variable
|
|
47464
|
+
* @param {String} tenv.parent.type - The type of the parent environment variable
|
|
47465
|
+
* @param {Object} tenv.ui - The UI configuration for the environment variable
|
|
47466
|
+
* @param {String} tenv.ui.icon - The icon for the environment variable
|
|
47467
|
+
* @param {Object} tenv.ui.label - The label for the environment variable
|
|
47468
|
+
* @param {String} tenv.ui.type - The type of the UI control for the environment variable
|
|
47469
|
+
* @param {Object} node - The subflow instance node
|
|
47470
|
+
*/
|
|
47471
|
+
function buildEnvUIRow(row, tenv, node) {
|
|
47472
|
+
if(RED.subflow.debug) { console.log("buildEnvUIRow", tenv) }
|
|
47473
|
+
const ui = tenv.ui || {}
|
|
46949
47474
|
ui.label = ui.label||{};
|
|
46950
47475
|
if ((tenv.type === "cred" || (tenv.parent && tenv.parent.type === "cred")) && !ui.type) {
|
|
46951
47476
|
ui.type = "cred";
|
|
46952
47477
|
ui.opts = {};
|
|
47478
|
+
} else if (tenv.type === "conf-types") {
|
|
47479
|
+
ui.type = "conf-types"
|
|
47480
|
+
ui.opts = { types: ['conf-types'] }
|
|
46953
47481
|
} else if (!ui.type) {
|
|
46954
47482
|
ui.type = "input";
|
|
46955
47483
|
ui.opts = { types: RED.editor.envVarList.DEFAULT_ENV_TYPE_LIST }
|
|
@@ -46993,9 +47521,10 @@ RED.subflow = (function() {
|
|
|
46993
47521
|
if (tenv.hasOwnProperty('type')) {
|
|
46994
47522
|
val.type = tenv.type;
|
|
46995
47523
|
}
|
|
47524
|
+
const elId = getSubflowEnvPropertyName(tenv.name)
|
|
46996
47525
|
switch(ui.type) {
|
|
46997
47526
|
case "input":
|
|
46998
|
-
input = $('<input type="text">').css('width','70%').appendTo(row);
|
|
47527
|
+
input = $('<input type="text">').css('width','70%').attr('id', elId).appendTo(row);
|
|
46999
47528
|
if (ui.opts.types && ui.opts.types.length > 0) {
|
|
47000
47529
|
var inputType = val.type;
|
|
47001
47530
|
if (ui.opts.types.indexOf(inputType) === -1) {
|
|
@@ -47022,7 +47551,7 @@ RED.subflow = (function() {
|
|
|
47022
47551
|
}
|
|
47023
47552
|
break;
|
|
47024
47553
|
case "select":
|
|
47025
|
-
input = $('<select>').css('width','70%').appendTo(row);
|
|
47554
|
+
input = $('<select>').css('width','70%').attr('id', elId).appendTo(row);
|
|
47026
47555
|
if (ui.opts.opts) {
|
|
47027
47556
|
ui.opts.opts.forEach(function(o) {
|
|
47028
47557
|
$('<option>').val(o.v).text(RED.editor.envVarList.lookupLabel(o.l, o.l['en-US']||o.v, locale)).appendTo(input);
|
|
@@ -47033,7 +47562,7 @@ RED.subflow = (function() {
|
|
|
47033
47562
|
case "checkbox":
|
|
47034
47563
|
label.css("cursor","default");
|
|
47035
47564
|
var cblabel = $('<label>').css('width','70%').appendTo(row);
|
|
47036
|
-
input = $('<input type="checkbox">').css({
|
|
47565
|
+
input = $('<input type="checkbox">').attr('id', elId).css({
|
|
47037
47566
|
marginTop: 0,
|
|
47038
47567
|
width: 'auto',
|
|
47039
47568
|
height: '34px'
|
|
@@ -47051,7 +47580,7 @@ RED.subflow = (function() {
|
|
|
47051
47580
|
input.prop("checked",boolVal);
|
|
47052
47581
|
break;
|
|
47053
47582
|
case "spinner":
|
|
47054
|
-
input = $('<input>').css('width','70%').appendTo(row);
|
|
47583
|
+
input = $('<input>').css('width','70%').attr('id', elId).appendTo(row);
|
|
47055
47584
|
var spinnerOpts = {};
|
|
47056
47585
|
if (ui.opts.hasOwnProperty('min')) {
|
|
47057
47586
|
spinnerOpts.min = ui.opts.min;
|
|
@@ -47080,18 +47609,25 @@ RED.subflow = (function() {
|
|
|
47080
47609
|
default: 'cred'
|
|
47081
47610
|
})
|
|
47082
47611
|
break;
|
|
47083
|
-
|
|
47084
|
-
|
|
47085
|
-
|
|
47612
|
+
case "conf-types":
|
|
47613
|
+
// let clsId = 'config-node-input-' + val.type + '-' + val.value + '-' + Math.floor(Math.random() * 100000);
|
|
47614
|
+
// clsId = clsId.replace(/\W/g, '-');
|
|
47615
|
+
// input = $('<input>').css('width','70%').addClass(clsId).attr('id', elId).appendTo(row);
|
|
47616
|
+
input = $('<input>').css('width','70%').attr('id', elId).appendTo(row);
|
|
47617
|
+
const _type = tenv.parent?.type || tenv.type;
|
|
47618
|
+
RED.editor.prepareConfigNodeSelect(node, tenv.name, _type, 'node-input-subflow-env', null, tenv);
|
|
47619
|
+
break;
|
|
47086
47620
|
}
|
|
47087
47621
|
}
|
|
47088
47622
|
|
|
47089
47623
|
/**
|
|
47090
|
-
*
|
|
47624
|
+
* Build the edit form for a subflow instance
|
|
47625
|
+
* Also used to build the preview form in the subflow template edit dialog
|
|
47091
47626
|
* @param uiContainer - container for UI
|
|
47092
47627
|
* @param envList - env var definitions of template
|
|
47093
47628
|
*/
|
|
47094
47629
|
function buildEnvUI(uiContainer, envList, node) {
|
|
47630
|
+
if(RED.subflow.debug) { console.log("buildEnvUI",envList) }
|
|
47095
47631
|
uiContainer.empty();
|
|
47096
47632
|
for (var i = 0; i < envList.length; i++) {
|
|
47097
47633
|
var tenv = envList[i];
|
|
@@ -47099,7 +47635,7 @@ RED.subflow = (function() {
|
|
|
47099
47635
|
continue;
|
|
47100
47636
|
}
|
|
47101
47637
|
var row = $("<div/>", { class: "form-row" }).appendTo(uiContainer);
|
|
47102
|
-
buildEnvUIRow(row,
|
|
47638
|
+
buildEnvUIRow(row, tenv, node);
|
|
47103
47639
|
}
|
|
47104
47640
|
}
|
|
47105
47641
|
// buildEnvUI
|
|
@@ -47172,6 +47708,9 @@ RED.subflow = (function() {
|
|
|
47172
47708
|
delete ui.opts
|
|
47173
47709
|
}
|
|
47174
47710
|
break;
|
|
47711
|
+
case "conf-types":
|
|
47712
|
+
delete ui.opts;
|
|
47713
|
+
break;
|
|
47175
47714
|
default:
|
|
47176
47715
|
delete ui.opts;
|
|
47177
47716
|
}
|
|
@@ -47194,8 +47733,9 @@ RED.subflow = (function() {
|
|
|
47194
47733
|
if (/^subflow:/.test(node.type)) {
|
|
47195
47734
|
var subflowDef = RED.nodes.subflow(node.type.substring(8));
|
|
47196
47735
|
if (subflowDef.env) {
|
|
47197
|
-
subflowDef.env.forEach(function(env) {
|
|
47736
|
+
subflowDef.env.forEach(function(env, i) {
|
|
47198
47737
|
var item = {
|
|
47738
|
+
index: i,
|
|
47199
47739
|
name:env.name,
|
|
47200
47740
|
parent: {
|
|
47201
47741
|
type: env.type,
|
|
@@ -47260,6 +47800,7 @@ RED.subflow = (function() {
|
|
|
47260
47800
|
}
|
|
47261
47801
|
|
|
47262
47802
|
function exportSubflowInstanceEnv(node) {
|
|
47803
|
+
if(RED.subflow.debug) { console.log("exportSubflowInstanceEnv",node) }
|
|
47263
47804
|
var env = [];
|
|
47264
47805
|
// First, get the values for the SubflowTemplate defined properties
|
|
47265
47806
|
// - these are the ones with custom UI elements
|
|
@@ -47306,6 +47847,9 @@ RED.subflow = (function() {
|
|
|
47306
47847
|
item.type = 'bool';
|
|
47307
47848
|
item.value = ""+input.prop("checked");
|
|
47308
47849
|
break;
|
|
47850
|
+
case "conf-types":
|
|
47851
|
+
item.value = input.val()
|
|
47852
|
+
item.type = data.parent.value;
|
|
47309
47853
|
}
|
|
47310
47854
|
if (ui.type === "cred" || item.type !== data.parent.type || item.value !== data.parent.value) {
|
|
47311
47855
|
env.push(item);
|
|
@@ -47319,8 +47863,15 @@ RED.subflow = (function() {
|
|
|
47319
47863
|
return 'node-input-subflow-env-'+name.replace(/[^a-z0-9-_]/ig,"_");
|
|
47320
47864
|
}
|
|
47321
47865
|
|
|
47322
|
-
|
|
47866
|
+
|
|
47867
|
+
/**
|
|
47868
|
+
* Build the subflow edit form
|
|
47869
|
+
* Called by subflow.oneditprepare for both instances and templates
|
|
47870
|
+
* @param {"subflow"|"subflow-template"} type - the type of subflow being edited
|
|
47871
|
+
* @param {Object} node - the node being edited
|
|
47872
|
+
*/
|
|
47323
47873
|
function buildEditForm(type,node) {
|
|
47874
|
+
if(RED.subflow.debug) { console.log("buildEditForm",type,node) }
|
|
47324
47875
|
if (type === "subflow-template") {
|
|
47325
47876
|
// This is the tabbed UI that offers the env list - with UI options
|
|
47326
47877
|
// plus the preview tab
|
|
@@ -54982,10 +55533,15 @@ RED.touch.radialMenu = (function() {
|
|
|
54982
55533
|
|
|
54983
55534
|
function listTour() {
|
|
54984
55535
|
return [
|
|
55536
|
+
{
|
|
55537
|
+
id: "4_0",
|
|
55538
|
+
label: "4.0",
|
|
55539
|
+
path: "./tours/welcome.js"
|
|
55540
|
+
},
|
|
54985
55541
|
{
|
|
54986
55542
|
id: "3_1",
|
|
54987
55543
|
label: "3.1",
|
|
54988
|
-
path: "./tours/welcome.js"
|
|
55544
|
+
path: "./tours/3.1/welcome.js"
|
|
54989
55545
|
},
|
|
54990
55546
|
{
|
|
54991
55547
|
id: "3_0",
|