').appendTo(container);
content.css("padding","8px 5px")
var min = ui.opts.min;
var max = ui.opts.max;
var minInput = $('
');
minInput.val(min);
var maxInput = $('
');
maxInput.val(max);
$('
'+RED._("editor.spinner.min")+'
').append(minInput).appendTo(content);
$('
'+RED._("editor.spinner.max")+'
').append(maxInput).appendTo(content);
return {
onclose: function() {
var min = minInput.val().trim();
var max = maxInput.val().trim();
if (min !== "") {
ui.opts.min = parseInt(min);
} else {
delete ui.opts.min;
}
if (max !== "") {
ui.opts.max = parseInt(max);
} else {
delete ui.opts.max;
}
inputCellInput.typedInput('value',Date.now())
}
}
}
}
},
{
value:"none",
label:RED._("editor.inputs.none"), icon:"fa fa-times",hasValue:false
},
{
value:"hide",
label:RED._("editor.inputs.hidden"), icon:"fa fa-ban",hasValue:false
}
],
default: 'none'
}).on("typedinputtypechange", function(evt,type) {
ui.type = $(this).typedInput("type");
ui.opts = typeOptions[ui.type];
if (ui.type === 'input') {
// In the case of 'input' type, the typedInput uses the multiple-option
// mode. Its value needs to be set to a comma-separately list of the
// selected options.
inputCellInput.typedInput('value',ui.opts.types.join(","))
} else {
// No other type cares about `value`, but doing this will
// force a refresh of the label now that `ui.opts` has
// been updated.
inputCellInput.typedInput('value',Date.now())
}
switch (ui.type) {
case 'input':
valueField.typedInput('types',ui.opts.types);
break;
case 'select':
valueField.typedInput('types',['str']);
break;
case 'checkbox':
valueField.typedInput('types',['bool']);
break;
case 'spinner':
valueField.typedInput('types',['num'])
break;
default:
valueField.typedInput('types',['str','num','bool','json','bin','env'])
}
if (ui.type === 'checkbox') {
valueField.typedInput('type','bool');
} else if (ui.type === 'spinner') {
valueField.typedInput('type','num');
}
if (ui.type !== 'checkbox') {
checkbox = null;
}
}).on("change", function(evt,type) {
if (ui.type === 'input') {
ui.opts.types = inputCellInput.typedInput('value').split(",");
valueField.typedInput('types',ui.opts.types);
}
});
valueField.on("change", function(evt) {
if (checkbox) {
checkbox.prop('checked',$(this).typedInput('value')==="true")
}
})
// Set the input to the right type. This will trigger the 'typedinputtypechange'
// event handler (just above ^^) to update the value if needed
inputCellInput.typedInput('type',ui.type)
}
function buildEnvUIRow(row, tenv, ui) {
ui.label = ui.label||{};
ui.opts = ui.opts||{};
if (!ui.type) {
ui.type = "input";
ui.opts = {types:['str','num','bool','json','bin','env']}
} else {
ui.opts = ui.opts || {};
}
var labels = ui.label || {};
var labelText = lookupLabel(labels, labels["en-US"]||tenv.name, currentLocale);
var label = $('
').appendTo(row);
var labelContainer = $(' ').appendTo(label);
if (ui.icon) {
var newPath = RED.utils.separateIconPath(ui.icon);
if (newPath) {
$(" ").appendTo(labelContainer);
}
}
if (ui.type !== "checkbox") {
$('').css({"padding-left":"5px"}).text(labelText).appendTo(label);
if (ui.type === 'none') {
label.width('100%');
}
}
var input;
var val = {
value: "",
type: "str"
};
if (tenv.parent) {
val.value = tenv.parent.value;
val.type = tenv.parent.type;
}
if (tenv.hasOwnProperty('value')) {
val.value = tenv.value;
}
if (tenv.hasOwnProperty('type')) {
val.type = tenv.type;
}
switch(ui.type) {
case "input":
input = $(' ').css('width','70%').appendTo(row);
if (ui.opts.types && ui.opts.types.length > 0) {
var inputType = val.type;
if (ui.opts.types.indexOf(inputType) === -1) {
inputType = ui.opts.types[0]
}
input.typedInput({
types: ui.opts.types,
default: inputType
})
input.typedInput('value',val.value)
} else {
input.val(val.value)
}
break;
case "select":
input = $('').css('width','70%').appendTo(row);
if (ui.opts.opts) {
ui.opts.opts.forEach(function(o) {
$('').val(o.v).text(lookupLabel(o.l, o.l['en-US']||o.v, currentLocale)).appendTo(input);
})
}
input.val(val.value);
break;
case "checkbox":
label.css("cursor","default");
var cblabel = $('').css('width','70%').appendTo(row);
input = $(' ').css({
marginTop: 0,
width: 'auto',
height: '34px'
}).appendTo(cblabel);
labelContainer.css({"padding-left":"5px"}).appendTo(cblabel);
$('').css({"padding-left":"5px"}).text(labelText).appendTo(cblabel);
var boolVal = false;
if (val.type === 'bool') {
boolVal = val.value === 'true'
} else if (val.type === 'num') {
boolVal = val.value !== "0"
} else {
boolVal = val.value !== ""
}
input.prop("checked",boolVal);
break;
case "spinner":
input = $(' ').css('width','70%').appendTo(row);
var spinnerOpts = {};
if (ui.opts.hasOwnProperty('min')) {
spinnerOpts.min = ui.opts.min;
}
if (ui.opts.hasOwnProperty('max')) {
spinnerOpts.max = ui.opts.max;
}
input.spinner(spinnerOpts).parent().width('70%');
input.val(val.value);
break;
}
if (input) {
input.attr('id',getSubflowEnvPropertyName(tenv.name))
}
}
/**
* Create environment variable input UI
* @param uiContainer - container for UI
* @param envList - env var definitions of template
*/
function buildEnvUI(uiContainer, envList) {
uiContainer.empty();
var elementID = 0;
for (var i = 0; i < envList.length; i++) {
var tenv = envList[i];
if (tenv.ui && tenv.ui.type === 'hide') {
continue;
}
var row = $("
", { class: "form-row" }).appendTo(uiContainer);
buildEnvUIRow(row,tenv, tenv.ui || {});
// console.log(ui);
}
}
// buildEnvUI
function exportEnvList(list, all) {
if (list) {
var env = [];
list.each(function(i) {
var entry = $(this);
var item = entry.data('data');
var name = (item.parent?item.name:item.nameField.val()).trim();
if (name !== "") {
var valueInput = item.valueField;
var value = valueInput.typedInput("value");
var type = valueInput.typedInput("type");
if (all || !item.parent || (item.parent.value !== value || item.parent.type !== type)) {
var envItem = {
name: name,
type: type,
value: value,
};
if (item.ui) {
var ui = {
icon: item.ui.icon,
label: $.extend(true,{},item.ui.label),
type: item.ui.type,
opts: $.extend(true,{},item.ui.opts)
}
// Check to see if this is the default ui definition.
// Delete any defaults to keep it compact
// {
// icon: "",
// label: {},
// type: "input",
// opts: {types:['str','num','bool','json','bin','env']}
// }
if (!ui.icon) {
delete ui.icon;
}
if ($.isEmptyObject(ui.label)) {
delete ui.label;
}
switch (ui.type) {
case "input":
if (JSON.stringify(ui.opts) === JSON.stringify({types:['str','num','bool','json','bin','env']})) {
// This is the default input config. Delete it as it will
// be applied automatically
delete ui.type;
delete ui.opts;
}
break;
case "select":
if (ui.opts && $.isEmptyObject(ui.opts.opts)) {
// This is the default select config.
// Delete it as it will be applied automatically
delete ui.opts;
}
break;
case "spinner":
if ($.isEmptyObject(ui.opts)) {
// This is the default spinner config.
// Delete as it will be applied automatically
delete ui.opts
}
break;
default:
delete ui.opts;
}
if (!$.isEmptyObject(ui)) {
envItem.ui = ui;
}
}
env.push(envItem);
}
}
});
return env;
}
return null;
}
function getSubflowInstanceParentEnv(node) {
var parentEnv = {};
var envList = [];
if (/^subflow:/.test(node.type)) {
var subflowDef = RED.nodes.subflow(node.type.substring(8));
if (subflowDef.env) {
subflowDef.env.forEach(function(env) {
var item = {
name:env.name,
parent: {
type: env.type,
value: env.value
},
ui: env.ui
}
envList.push(item);
parentEnv[env.name] = item;
})
}
}
if (node.env) {
for (var i = 0; i < node.env.length; i++) {
var env = node.env[i];
if (parentEnv.hasOwnProperty(env.name)) {
parentEnv[env.name].type = env.type;
parentEnv[env.name].value = env.value;
} else {
// envList.push({
// name: env.name,
// type: env.type,
// value: env.value,
// });
}
}
}
return envList;
}
function exportSubflowInstanceEnv(node) {
var env = [];
// First, get the values for the SubflowTemplate defined properties
// - these are the ones with custom UI elements
var parentEnv = getSubflowInstanceParentEnv(node);
parentEnv.forEach(function(data) {
var item;
var ui = data.ui || {};
if (!ui.type) {
ui.type = "input";
ui.opts = {types:['str','num','bool','json','bin','env']}
} else {
ui.opts = ui.opts || {};
}
var input = $("#"+getSubflowEnvPropertyName(data.name));
if (input.length) {
item = { name: data.name };
switch(ui.type) {
case "input":
if (ui.opts.types && ui.opts.types.length > 0) {
item.value = input.typedInput('value');
item.type = input.typedInput('type');
} else {
item.value = input.val();
item.type = 'str';
}
break;
case "spinner":
item.value = input.val();
item.type = 'num';
break;
case "select":
item.value = input.val();
item.type = 'str';
break;
case "checkbox":
item.type = 'bool';
item.value = ""+input.prop("checked");
break;
}
if (item.type !== data.parent.type || item.value !== data.parent.value) {
env.push(item);
}
}
})
// Second, get the values from the Properties table tab
var items = $('#red-ui-editor-subflow-env-list').editableList('items');
items.each(function (i,el) {
var data = el.data('data');
var item;
if (data.nameField && data.valueField) {
item = {
name: data.nameField.val(),
value: data.valueField.typedInput("value"),
type: data.valueField.typedInput("type")
}
if (item.name.trim() !== "") {
env.push(item);
}
}
});
return env;
}
function getSubflowEnvPropertyName(name) {
return 'node-input-subflow-env-'+name.replace(/[^a-z0-9-_]/ig,"_");
}
/**
* Lookup text for specific locale
* @param labels - dict of labels
* @param defaultLabel - fallback label if not found
* @param locale - target locale
* @returns {string} text for specified locale
*/
function lookupLabel(labels, defaultLabel, locale) {
if (labels) {
if (labels[locale]) {
return labels[locale];
}
if (locale) {
var lang = locale.substring(0, 2);
if (labels[lang]) {
return labels[lang];
}
}
}
return defaultLabel;
}
function buildEditForm(container,type,node) {
if (type === "subflow-template") {
buildPropertiesList($('#node-input-env-container'), node);
} else if (type === "subflow") {
buildEnvUI($("#subflow-input-ui"), getSubflowInstanceParentEnv(node));
}
}
function buildPropertiesForm(container, node) {
var form = $('').appendTo(container);
var listContainer = $('
').appendTo(form);
var list = $(' ').appendTo(listContainer);
buildPropertiesList(list, node);
}
return {
init: init,
createSubflow: createSubflow,
convertToSubflow: convertToSubflow,
removeSubflow: removeSubflow,
refresh: refresh,
removeInput: removeSubflowInput,
removeOutput: removeSubflowOutput,
removeStatus: removeSubflowStatus,
buildEditForm: buildEditForm,
buildPropertiesForm: buildPropertiesForm,
exportSubflowTemplateEnv: exportEnvList,
exportSubflowInstanceEnv: exportSubflowInstanceEnv
}
})();