mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Rework Subflow Instance property UI (#2236)
* Add support of Subflow UI definition * new UI definition for env var * fix label * fixed value obtaining * fixed label width * fix checkbox * fix subflow info * remove old subflow ui tests * add tests * merge ui new changes * fix initial open button * fix environment variable edit tab * WIP: cp-1 * Rework subflow ui property * Restrict SF value type according to input selection * Move subflow property UI code to subflow.js * Update subflow ui type select appearance * Present subflow instance properties as table rather than generated UI * Move subflow instance properties to separate tab * Fix subflow property ui element layout issues
This commit is contained in:
parent
c8acc6a12e
commit
880757fb5d
@ -321,6 +321,31 @@
|
|||||||
"description": "Description",
|
"description": "Description",
|
||||||
"show": "Show",
|
"show": "Show",
|
||||||
"hide": "Hide",
|
"hide": "Hide",
|
||||||
|
"locale": "Select UI Language",
|
||||||
|
"icon": "Icon",
|
||||||
|
"inputType": "Input type",
|
||||||
|
"previewUI": "Preview UI",
|
||||||
|
"previewOK": "Preview OK",
|
||||||
|
"types": {
|
||||||
|
"str": "string",
|
||||||
|
"num": "number",
|
||||||
|
"bool": "bool",
|
||||||
|
"json": "json",
|
||||||
|
"bin": "buffer",
|
||||||
|
"env": "env var",
|
||||||
|
"no-value": "no value"
|
||||||
|
},
|
||||||
|
"menu": {
|
||||||
|
"input": "input",
|
||||||
|
"select": "select",
|
||||||
|
"checkbox": "checkbox",
|
||||||
|
"spinner": "spinner",
|
||||||
|
"hidden": "label only"
|
||||||
|
},
|
||||||
|
"spinner": {
|
||||||
|
"min": "min",
|
||||||
|
"max": "max"
|
||||||
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"scopeChange": "Changing the scope will make it unavailable to nodes in other flows that use it",
|
"scopeChange": "Changing the scope will make it unavailable to nodes in other flows that use it",
|
||||||
"invalidProperties": "Invalid properties:"
|
"invalidProperties": "Invalid properties:"
|
||||||
@ -939,9 +964,11 @@
|
|||||||
},
|
},
|
||||||
"editor-tab": {
|
"editor-tab": {
|
||||||
"properties": "Properties",
|
"properties": "Properties",
|
||||||
|
"envProperties": "Environment Variables",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
"appearance": "Appearance",
|
"appearance": "Appearance",
|
||||||
"env": "Environment Variables"
|
"preview": "UI Preview",
|
||||||
|
"defaultValue": "Default value"
|
||||||
},
|
},
|
||||||
"languages" : {
|
"languages" : {
|
||||||
"de": "German",
|
"de": "German",
|
||||||
|
@ -321,6 +321,31 @@
|
|||||||
"description": "詳細",
|
"description": "詳細",
|
||||||
"show": "表示",
|
"show": "表示",
|
||||||
"hide": "非表示",
|
"hide": "非表示",
|
||||||
|
"locale": "UI言語の選択",
|
||||||
|
"icon": "記号",
|
||||||
|
"inputType": "入力形式",
|
||||||
|
"previewUI": "UI確認",
|
||||||
|
"previewOK": "確認OK",
|
||||||
|
"types": {
|
||||||
|
"str": "文字列",
|
||||||
|
"num": "数値",
|
||||||
|
"bool": "真偽",
|
||||||
|
"json": "JSON",
|
||||||
|
"bin": "バッファ",
|
||||||
|
"env": "環境変数",
|
||||||
|
"no-value": "値無し"
|
||||||
|
},
|
||||||
|
"menu": {
|
||||||
|
"input": "入力",
|
||||||
|
"select": "選択",
|
||||||
|
"checkbox": "チェックボックス",
|
||||||
|
"spinner": "数値",
|
||||||
|
"hidden": "ラベルのみ"
|
||||||
|
},
|
||||||
|
"spinner": {
|
||||||
|
"min": "最小",
|
||||||
|
"max": "最大"
|
||||||
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"scopeChange": "スコープの変更は、他のフローで使われているノードを無効にします",
|
"scopeChange": "スコープの変更は、他のフローで使われているノードを無効にします",
|
||||||
"invalidProperties": "プロパティが不正です:"
|
"invalidProperties": "プロパティが不正です:"
|
||||||
@ -940,7 +965,7 @@
|
|||||||
"properties": "プロパティ",
|
"properties": "プロパティ",
|
||||||
"description": "説明",
|
"description": "説明",
|
||||||
"appearance": "外観",
|
"appearance": "外観",
|
||||||
"env": "環境変数"
|
"env": "サブフロープロパティ"
|
||||||
},
|
},
|
||||||
"languages": {
|
"languages": {
|
||||||
"de": "ドイツ語",
|
"de": "ドイツ語",
|
||||||
|
@ -50,6 +50,19 @@ RED.i18n = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
lang: function() {
|
||||||
|
// Gets the active message catalog language. This is based on what
|
||||||
|
// locale the editor is using and what languages are available.
|
||||||
|
//
|
||||||
|
var preferredLangs = i18n.functions.toLanguages(localStorage.getItem("editor-language")||i18n.detectLanguage());
|
||||||
|
var knownLangs = RED.settings.theme("languages")||["en-US"];
|
||||||
|
for (var i=0;i<preferredLangs.length;i++) {
|
||||||
|
if (knownLangs.indexOf(preferredLangs[i]) > -1) {
|
||||||
|
return preferredLangs[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 'end-US'
|
||||||
|
},
|
||||||
loadNodeCatalog: function(namespace,done) {
|
loadNodeCatalog: function(namespace,done) {
|
||||||
var languageList = i18n.functions.toLanguages(localStorage.getItem("editor-language")||i18n.detectLanguage());
|
var languageList = i18n.functions.toLanguages(localStorage.getItem("editor-language")||i18n.detectLanguage());
|
||||||
var toLoad = languageList.length;
|
var toLoad = languageList.length;
|
||||||
|
@ -399,14 +399,14 @@ RED.nodes = (function() {
|
|||||||
inputLabels: function(i) { return sf.inputLabels?sf.inputLabels[i]:null },
|
inputLabels: function(i) { return sf.inputLabels?sf.inputLabels[i]:null },
|
||||||
outputLabels: function(i) { return sf.outputLabels?sf.outputLabels[i]:null },
|
outputLabels: function(i) { return sf.outputLabels?sf.outputLabels[i]:null },
|
||||||
oneditresize: function(size) {
|
oneditresize: function(size) {
|
||||||
var rows = $("#dialog-form>div:not(.node-input-env-container-row)");
|
// var rows = $(".dialog-form>div:not(.node-input-env-container-row)");
|
||||||
var height = size.height;
|
var height = size.height;
|
||||||
for (var i=0; i<rows.size(); i++) {
|
// for (var i=0; i<rows.size(); i++) {
|
||||||
height -= $(rows[i]).outerHeight(true);
|
// height -= $(rows[i]).outerHeight(true);
|
||||||
}
|
// }
|
||||||
var editorRow = $("#dialog-form>div.node-input-env-container-row");
|
// var editorRow = $("#dialog-form>div.node-input-env-container-row");
|
||||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
// height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||||
$("#node-input-env-container").editableList('height',height-80);
|
$("ol.red-ui-editor-subflow-env-list").editableList('height',height);
|
||||||
},
|
},
|
||||||
set:{
|
set:{
|
||||||
module: "node-red"
|
module: "node-red"
|
||||||
|
@ -34,6 +34,8 @@
|
|||||||
* - addItem(itemData)
|
* - addItem(itemData)
|
||||||
* - insertItemAt : function(data,index) - add an item at the specified index
|
* - insertItemAt : function(data,index) - add an item at the specified index
|
||||||
* - removeItem(itemData)
|
* - removeItem(itemData)
|
||||||
|
* - getItemAt(index)
|
||||||
|
* - indexOf(itemData)
|
||||||
* - width(width)
|
* - width(width)
|
||||||
* - height(height)
|
* - height(height)
|
||||||
* - items()
|
* - items()
|
||||||
@ -186,7 +188,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
_destroy: function() {
|
_destroy: function() {
|
||||||
this.topContainer.remove();
|
if (this.topContainer) {
|
||||||
|
var tc = this.topContainer;
|
||||||
|
delete this.topContainer;
|
||||||
|
tc.remove();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
_refreshFilter: function() {
|
_refreshFilter: function() {
|
||||||
var that = this;
|
var that = this;
|
||||||
@ -232,6 +238,23 @@
|
|||||||
this.uiHeight = desiredHeight;
|
this.uiHeight = desiredHeight;
|
||||||
this._resize();
|
this._resize();
|
||||||
},
|
},
|
||||||
|
getItemAt: function(index) {
|
||||||
|
var items = this.items();
|
||||||
|
if (index >= 0 && index < items.length) {
|
||||||
|
return $(items[index]).data('data');
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
indexOf: function(data) {
|
||||||
|
var items = this.items();
|
||||||
|
for (var i=0;i<items.length;i++) {
|
||||||
|
if ($(items[i]).data('data') === data) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
},
|
||||||
insertItemAt: function(data,index) {
|
insertItemAt: function(data,index) {
|
||||||
var that = this;
|
var that = this;
|
||||||
data = data || {};
|
data = data || {};
|
||||||
|
@ -225,7 +225,7 @@ RED.menu = (function() {
|
|||||||
triggerAction(opt.id,state);
|
triggerAction(opt.id,state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!alreadySet) {
|
if (!opt.local && !alreadySet) {
|
||||||
RED.settings.set(opt.setting||("menu-"+opt.id), state);
|
RED.settings.set(opt.setting||("menu-"+opt.id), state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,6 +253,71 @@ RED.popover = (function() {
|
|||||||
content: label,
|
content: label,
|
||||||
delay: { show: 750, hide: 50 }
|
delay: { show: 750, hide: 50 }
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
panel: function(content) {
|
||||||
|
var panel = $('<div class="red-ui-editor-dialog red-ui-popover-panel"></div>');
|
||||||
|
panel.css({ display: "none" });
|
||||||
|
panel.appendTo(document.body);
|
||||||
|
content.appendTo(panel);
|
||||||
|
var closeCallback;
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
$(document).off("mousedown.red-ui-popover-panel-close");
|
||||||
|
panel.hide();
|
||||||
|
panel.css({
|
||||||
|
height: "auto"
|
||||||
|
});
|
||||||
|
panel.remove();
|
||||||
|
}
|
||||||
|
function show(options) {
|
||||||
|
var closeCallback = options.onclose;
|
||||||
|
var target = options.target;
|
||||||
|
var align = options.align || "left";
|
||||||
|
|
||||||
|
var pos = target.offset();
|
||||||
|
var targetWidth = target.width();
|
||||||
|
var targetHeight = target.height();
|
||||||
|
var panelHeight = panel.height();
|
||||||
|
var panelWidth = panel.width();
|
||||||
|
|
||||||
|
var top = (targetHeight+pos.top);
|
||||||
|
if (top+panelHeight > $(window).height()) {
|
||||||
|
top -= (top+panelHeight)-$(window).height() + 5;
|
||||||
|
}
|
||||||
|
if (top < 0) {
|
||||||
|
panelHeight.height(panelHeight+top)
|
||||||
|
top = 0;
|
||||||
|
}
|
||||||
|
if (align === "left") {
|
||||||
|
panel.css({
|
||||||
|
top: top+"px",
|
||||||
|
left: (pos.left)+"px",
|
||||||
|
});
|
||||||
|
} else if(align === "right") {
|
||||||
|
panel.css({
|
||||||
|
top: top+"px",
|
||||||
|
left: (pos.left-panelWidth)+"px",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
panel.slideDown(100);
|
||||||
|
|
||||||
|
$(document).on("mousedown.red-ui-popover-panel-close", function(event) {
|
||||||
|
if(!$(event.target).closest(panel).length && !$(event.target).closest(".red-ui-editor-dialog").length) {
|
||||||
|
if (closeCallback) {
|
||||||
|
closeCallback();
|
||||||
|
}
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
// if ($(event.target).closest(target).length) {
|
||||||
|
// event.preventDefault();
|
||||||
|
// }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
container: panel,
|
||||||
|
show:show,
|
||||||
|
hide:hide
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
re: {value:"re",label:"regular expression",icon:"red/images/typedInput/re.svg"},
|
re: {value:"re",label:"regular expression",icon:"red/images/typedInput/re.svg"},
|
||||||
date: {value:"date",label:"timestamp",hasValue:false},
|
date: {value:"date",label:"timestamp",icon:"fa fa-clock-o",hasValue:false},
|
||||||
jsonata: {
|
jsonata: {
|
||||||
value: "jsonata",
|
value: "jsonata",
|
||||||
label: "expression",
|
label: "expression",
|
||||||
@ -298,7 +298,8 @@
|
|||||||
that.uiSelect.addClass('red-ui-typedInput-focus');
|
that.uiSelect.addClass('red-ui-typedInput-focus');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.optionExpandButton = $('<button tabindex="0" class="red-ui-typedInput-option-expand" style="display:inline-block"><i class="red-ui-typedInput-icon fa fa-ellipsis-h"></i></button>').appendTo(this.uiSelect);
|
this.optionExpandButton = $('<button tabindex="0" class="red-ui-typedInput-option-expand" style="display:inline-block"></button>').appendTo(this.uiSelect);
|
||||||
|
this.optionExpandButtonIcon = $('<i class="red-ui-typedInput-icon fa fa-ellipsis-h"></i>').appendTo(this.optionExpandButton);
|
||||||
this.type(this.options.default||this.typeList[0].value);
|
this.type(this.options.default||this.typeList[0].value);
|
||||||
}catch(err) {
|
}catch(err) {
|
||||||
console.log(err.stack);
|
console.log(err.stack);
|
||||||
@ -336,6 +337,17 @@
|
|||||||
menu.css({
|
menu.css({
|
||||||
height: "auto"
|
height: "auto"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (menu.opts.multiple) {
|
||||||
|
var selected = [];
|
||||||
|
menu.find('input[type="checkbox"]').each(function() {
|
||||||
|
if ($(this).prop("checked")) {
|
||||||
|
selected.push($(this).data('value'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
menu.callback(selected);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.elementDiv.is(":visible")) {
|
if (this.elementDiv.is(":visible")) {
|
||||||
this.input.trigger("focus");
|
this.input.trigger("focus");
|
||||||
} else if (this.optionSelectTrigger.is(":visible")){
|
} else if (this.optionSelectTrigger.is(":visible")){
|
||||||
@ -344,10 +356,12 @@
|
|||||||
this.selectTrigger.trigger("focus");
|
this.selectTrigger.trigger("focus");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_createMenu: function(opts,callback) {
|
_createMenu: function(menuOptions,opts,callback) {
|
||||||
var that = this;
|
var that = this;
|
||||||
var menu = $("<div>").addClass("red-ui-typedInput-options");
|
var menu = $("<div>").addClass("red-ui-typedInput-options red-ui-editor-dialog");
|
||||||
opts.forEach(function(opt) {
|
menu.opts = opts;
|
||||||
|
menu.callback = callback;
|
||||||
|
menuOptions.forEach(function(opt) {
|
||||||
if (typeof opt === 'string') {
|
if (typeof opt === 'string') {
|
||||||
opt = {value:opt,label:opt};
|
opt = {value:opt,label:opt};
|
||||||
}
|
}
|
||||||
@ -369,12 +383,20 @@
|
|||||||
if (!opt.icon && !opt.label) {
|
if (!opt.icon && !opt.label) {
|
||||||
op.text(opt.value);
|
op.text(opt.value);
|
||||||
}
|
}
|
||||||
|
var cb;
|
||||||
|
if (opts.multiple) {
|
||||||
|
cb = $('<input type="checkbox">').css("pointer-events","none").data('value',opt.value).prependTo(op).on("mousedown", function(evt) { evt.preventDefault() });
|
||||||
|
}
|
||||||
|
|
||||||
op.on("click", function(event) {
|
op.on("click", function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
callback(opt.value);
|
if (!opts.multiple) {
|
||||||
that._hideMenu(menu);
|
callback(opt.value);
|
||||||
|
that._hideMenu(menu);
|
||||||
|
} else {
|
||||||
|
cb.prop("checked",!cb.prop("checked"));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
menu.css({
|
menu.css({
|
||||||
@ -398,9 +420,6 @@
|
|||||||
}
|
}
|
||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return menu;
|
return menu;
|
||||||
|
|
||||||
},
|
},
|
||||||
@ -409,11 +428,22 @@
|
|||||||
this.disarmClick = false;
|
this.disarmClick = false;
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (menu.opts.multiple) {
|
||||||
|
var selected = {};
|
||||||
|
this.value().split(",").forEach(function(f) {
|
||||||
|
selected[f] = true;
|
||||||
|
})
|
||||||
|
menu.find('input[type="checkbox"]').each(function() {
|
||||||
|
$(this).prop("checked",selected[$(this).data('value')])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var that = this;
|
var that = this;
|
||||||
var pos = relativeTo.offset();
|
var pos = relativeTo.offset();
|
||||||
var height = relativeTo.height();
|
var height = relativeTo.height();
|
||||||
var menuHeight = menu.height();
|
var menuHeight = menu.height();
|
||||||
var top = (height+pos.top-3);
|
var top = (height+pos.top);
|
||||||
if (top+menuHeight > $(window).height()) {
|
if (top+menuHeight > $(window).height()) {
|
||||||
top -= (top+menuHeight)-$(window).height()+5;
|
top -= (top+menuHeight)-$(window).height()+5;
|
||||||
}
|
}
|
||||||
@ -423,7 +453,7 @@
|
|||||||
}
|
}
|
||||||
menu.css({
|
menu.css({
|
||||||
top: top+"px",
|
top: top+"px",
|
||||||
left: (2+pos.left)+"px",
|
left: (pos.left)+"px",
|
||||||
});
|
});
|
||||||
menu.slideDown(100);
|
menu.slideDown(100);
|
||||||
this._delay(function() {
|
this._delay(function() {
|
||||||
@ -471,7 +501,7 @@
|
|||||||
this._getLabelWidth(this.selectTrigger, function(labelWidth) {
|
this._getLabelWidth(this.selectTrigger, function(labelWidth) {
|
||||||
that.elementDiv.css('left',labelWidth+"px");
|
that.elementDiv.css('left',labelWidth+"px");
|
||||||
that.valueLabelContainer.css('left',labelWidth+"px");
|
that.valueLabelContainer.css('left',labelWidth+"px");
|
||||||
if (that.optionExpandButton.is(":visible")) {
|
if (that.optionExpandButton.shown) {
|
||||||
that.elementDiv.css('right',"22px");
|
that.elementDiv.css('right',"22px");
|
||||||
that.valueLabelContainer.css('right',"22px");
|
that.valueLabelContainer.css('right',"22px");
|
||||||
} else {
|
} else {
|
||||||
@ -496,7 +526,7 @@
|
|||||||
} else {
|
} else {
|
||||||
that.optionSelectLabel.css({'left':'0'})
|
that.optionSelectLabel.css({'left':'0'})
|
||||||
that.optionSelectTrigger.css({'width':'calc( 100% - '+labelWidth+'px )'});
|
that.optionSelectTrigger.css({'width':'calc( 100% - '+labelWidth+'px )'});
|
||||||
if (!that.optionExpandButton.is(":visible")) {
|
if (!that.optionExpandButton.shown) {
|
||||||
that.elementDiv.css({'right':0});
|
that.elementDiv.css({'right':0});
|
||||||
that.input.css({
|
that.input.css({
|
||||||
'border-top-right-radius': '4px',
|
'border-top-right-radius': '4px',
|
||||||
@ -511,25 +541,35 @@
|
|||||||
_updateOptionSelectLabel: function(o) {
|
_updateOptionSelectLabel: function(o) {
|
||||||
var opt = this.typeMap[this.propertyType];
|
var opt = this.typeMap[this.propertyType];
|
||||||
this.optionSelectLabel.empty();
|
this.optionSelectLabel.empty();
|
||||||
if (o.icon) {
|
if (this.typeMap[this.propertyType].valueLabel) {
|
||||||
if (o.icon.indexOf("<") === 0) {
|
if (opt.multiple) {
|
||||||
$(o.icon).prependTo(this.optionSelectLabel);
|
this.typeMap[this.propertyType].valueLabel.call(this,this.optionSelectLabel,o);
|
||||||
} else if (o.icon.indexOf("/") !== -1) {
|
|
||||||
// url
|
|
||||||
$('<img>',{src:mapDeprecatedIcon(o.icon),style:"height: 18px;"}).prependTo(this.optionSelectLabel);
|
|
||||||
} else {
|
} else {
|
||||||
// icon class
|
this.typeMap[this.propertyType].valueLabel.call(this,this.optionSelectLabel,o.value);
|
||||||
$('<i>',{class:"red-ui-typedInput-icon "+o.icon}).prependTo(this.optionSelectLabel);
|
}
|
||||||
|
} else if (!opt.multiple) {
|
||||||
|
if (o.icon) {
|
||||||
|
if (o.icon.indexOf("<") === 0) {
|
||||||
|
$(o.icon).prependTo(this.optionSelectLabel);
|
||||||
|
} else if (o.icon.indexOf("/") !== -1) {
|
||||||
|
// url
|
||||||
|
$('<img>',{src:mapDeprecatedIcon(o.icon),style:"height: 18px;"}).prependTo(this.optionSelectLabel);
|
||||||
|
} else {
|
||||||
|
// icon class
|
||||||
|
$('<i>',{class:"red-ui-typedInput-icon "+o.icon}).prependTo(this.optionSelectLabel);
|
||||||
|
}
|
||||||
|
} else if (o.label) {
|
||||||
|
this.optionSelectLabel.text(o.label);
|
||||||
|
} else {
|
||||||
|
this.optionSelectLabel.text(o.value);
|
||||||
|
}
|
||||||
|
if (opt.hasValue) {
|
||||||
|
this.optionValue = o.value;
|
||||||
|
this._resize();
|
||||||
|
this.input.trigger('change',this.propertyType,this.value());
|
||||||
}
|
}
|
||||||
} else if (o.label) {
|
|
||||||
this.optionSelectLabel.text(o.label);
|
|
||||||
} else {
|
} else {
|
||||||
this.optionSelectLabel.text(o.value);
|
this.optionSelectLabel.text(o.length+" selected");
|
||||||
}
|
|
||||||
if (opt.hasValue) {
|
|
||||||
this.optionValue = o.value;
|
|
||||||
this._resize();
|
|
||||||
this.input.trigger('change',this.propertyType,this.value());
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_destroy: function() {
|
_destroy: function() {
|
||||||
@ -558,7 +598,7 @@
|
|||||||
if (this.menu) {
|
if (this.menu) {
|
||||||
this.menu.remove();
|
this.menu.remove();
|
||||||
}
|
}
|
||||||
this.menu = this._createMenu(this.typeList, function(v) { that.type(v) });
|
this.menu = this._createMenu(this.typeList,{},function(v) { that.type(v) });
|
||||||
if (currentType && !this.typeMap.hasOwnProperty(currentType)) {
|
if (currentType && !this.typeMap.hasOwnProperty(currentType)) {
|
||||||
this.type(this.typeList[0].value);
|
this.type(this.typeList[0].value);
|
||||||
} else {
|
} else {
|
||||||
@ -572,38 +612,51 @@
|
|||||||
this._resize();
|
this._resize();
|
||||||
},
|
},
|
||||||
value: function(value) {
|
value: function(value) {
|
||||||
|
var that = this;
|
||||||
|
var opt = this.typeMap[this.propertyType];
|
||||||
if (!arguments.length) {
|
if (!arguments.length) {
|
||||||
var v = this.input.val();
|
var v = this.input.val();
|
||||||
if (this.typeMap[this.propertyType].export) {
|
if (opt.export) {
|
||||||
v = this.typeMap[this.propertyType].export(v,this.optionValue)
|
v = opt.export(v,this.optionValue)
|
||||||
}
|
}
|
||||||
return v;
|
return v;
|
||||||
} else {
|
} else {
|
||||||
var selectedOption;
|
var selectedOption = [];
|
||||||
if (this.typeMap[this.propertyType].options) {
|
if (opt.options) {
|
||||||
for (var i=0;i<this.typeMap[this.propertyType].options.length;i++) {
|
var checkValues = [value];
|
||||||
var op = this.typeMap[this.propertyType].options[i];
|
if (opt.multiple) {
|
||||||
if (typeof op === "string") {
|
selectedOption = [];
|
||||||
if (op === value) {
|
checkValues = value.split(",");
|
||||||
selectedOption = this.activeOptions[op];
|
}
|
||||||
|
checkValues.forEach(function(value) {
|
||||||
|
for (var i=0;i<opt.options.length;i++) {
|
||||||
|
var op = opt.options[i];
|
||||||
|
if (typeof op === "string") {
|
||||||
|
if (op === value) {
|
||||||
|
selectedOption.push(that.activeOptions[op]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (op.value === value) {
|
||||||
|
selectedOption.push(op);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (op.value === value) {
|
|
||||||
selectedOption = op;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
if (!selectedOption) {
|
|
||||||
selectedOption = {value:""}
|
|
||||||
}
|
|
||||||
this.input.val(value);
|
this.input.val(value);
|
||||||
this._updateOptionSelectLabel(selectedOption)
|
if (!opt.multiple) {
|
||||||
|
if (!selectedOption.length === 0) {
|
||||||
|
selectedOption = [{value:""}];
|
||||||
|
}
|
||||||
|
this._updateOptionSelectLabel(selectedOption[0])
|
||||||
|
} else {
|
||||||
|
this._updateOptionSelectLabel(selectedOption)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.input.val(value);
|
this.input.val(value);
|
||||||
}
|
if (opt.valueLabel) {
|
||||||
if (this.typeMap[this.propertyType].valueLabel) {
|
this.valueLabelContainer.empty();
|
||||||
this.valueLabelContainer.empty();
|
opt.valueLabel.call(this,this.valueLabelContainer,value);
|
||||||
this.typeMap[this.propertyType].valueLabel.call(this,this.valueLabelContainer,value);
|
}
|
||||||
}
|
}
|
||||||
this.input.trigger('change',this.type(),value);
|
this.input.trigger('change',this.type(),value);
|
||||||
}
|
}
|
||||||
@ -621,7 +674,7 @@
|
|||||||
}
|
}
|
||||||
this.selectLabel.empty();
|
this.selectLabel.empty();
|
||||||
var image;
|
var image;
|
||||||
if (opt.icon) {
|
if (opt.icon && opt.showLabel !== false) {
|
||||||
if (opt.icon.indexOf("<") === 0) {
|
if (opt.icon.indexOf("<") === 0) {
|
||||||
$(opt.icon).prependTo(this.selectLabel);
|
$(opt.icon).prependTo(this.selectLabel);
|
||||||
}
|
}
|
||||||
@ -636,7 +689,8 @@
|
|||||||
else {
|
else {
|
||||||
$('<i>',{class:"red-ui-typedInput-icon "+opt.icon}).prependTo(this.selectLabel);
|
$('<i>',{class:"red-ui-typedInput-icon "+opt.icon}).prependTo(this.selectLabel);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
if (opt.hasValue === false || (opt.showLabel !== false && !opt.icon)) {
|
||||||
this.selectLabel.text(opt.label);
|
this.selectLabel.text(opt.label);
|
||||||
}
|
}
|
||||||
if (this.optionMenu) {
|
if (this.optionMenu) {
|
||||||
@ -646,6 +700,7 @@
|
|||||||
if (opt.options) {
|
if (opt.options) {
|
||||||
if (this.optionExpandButton) {
|
if (this.optionExpandButton) {
|
||||||
this.optionExpandButton.hide();
|
this.optionExpandButton.hide();
|
||||||
|
this.optionExpandButton.shown = false;
|
||||||
}
|
}
|
||||||
if (this.optionSelectTrigger) {
|
if (this.optionSelectTrigger) {
|
||||||
this.optionSelectTrigger.show();
|
this.optionSelectTrigger.show();
|
||||||
@ -668,36 +723,50 @@
|
|||||||
if (!that.activeOptions.hasOwnProperty(that.optionValue)) {
|
if (!that.activeOptions.hasOwnProperty(that.optionValue)) {
|
||||||
that.optionValue = null;
|
that.optionValue = null;
|
||||||
}
|
}
|
||||||
this.optionMenu = this._createMenu(opt.options,function(v){
|
|
||||||
that._updateOptionSelectLabel(that.activeOptions[v]);
|
|
||||||
if (!opt.hasValue) {
|
|
||||||
that.value(that.activeOptions[v].value)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var op;
|
var op;
|
||||||
if (!opt.hasValue) {
|
if (!opt.hasValue) {
|
||||||
var currentVal = this.input.val();
|
|
||||||
var validValue = false;
|
var validValue = false;
|
||||||
for (var i=0;i<opt.options.length;i++) {
|
var currentVal = this.input.val();
|
||||||
op = opt.options[i];
|
if (!opt.multiple) {
|
||||||
if (typeof op === "string" && op === currentVal) {
|
for (var i=0;i<opt.options.length;i++) {
|
||||||
that._updateOptionSelectLabel({value:currentVal});
|
op = opt.options[i];
|
||||||
validValue = true;
|
if (typeof op === "string" && op === currentVal) {
|
||||||
break;
|
that._updateOptionSelectLabel({value:currentVal});
|
||||||
} else if (op.value === currentVal) {
|
validValue = true;
|
||||||
that._updateOptionSelectLabel(op);
|
break;
|
||||||
validValue = true;
|
} else if (op.value === currentVal) {
|
||||||
break;
|
that._updateOptionSelectLabel(op);
|
||||||
|
validValue = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (!validValue) {
|
||||||
if (!validValue) {
|
op = opt.options[0];
|
||||||
op = opt.options[0];
|
if (typeof op === "string") {
|
||||||
if (typeof op === "string") {
|
this.value(op);
|
||||||
this.value(op);
|
that._updateOptionSelectLabel({value:op});
|
||||||
that._updateOptionSelectLabel({value:op});
|
} else {
|
||||||
} else {
|
this.value(op.value);
|
||||||
this.value(op.value);
|
that._updateOptionSelectLabel(op);
|
||||||
that._updateOptionSelectLabel(op);
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Check to see if value is a valid csv of
|
||||||
|
// options.
|
||||||
|
var currentValues = {};
|
||||||
|
currentVal.split(",").forEach(function(v) {
|
||||||
|
if (v) {
|
||||||
|
currentValues[v] = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (var i=0;i<opt.options.length;i++) {
|
||||||
|
op = opt.options[i];
|
||||||
|
delete currentValues[op.value||op];
|
||||||
|
}
|
||||||
|
if (!$.isEmptyObject(currentValues)) {
|
||||||
|
// Invalid, set to default/empty
|
||||||
|
this.value((opt.default||[]).join(","));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -733,7 +802,21 @@
|
|||||||
this.optionSelectTrigger.hide();
|
this.optionSelectTrigger.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.optionMenu = this._createMenu(opt.options,opt,function(v){
|
||||||
|
if (!opt.multiple) {
|
||||||
|
that._updateOptionSelectLabel(that.activeOptions[v]);
|
||||||
|
if (!opt.hasValue) {
|
||||||
|
that.value(that.activeOptions[v].value)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
that._updateOptionSelectLabel(v);
|
||||||
|
if (!opt.hasValue) {
|
||||||
|
that.value(v.join(","))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
this._trigger("typechange",null,this.propertyType);
|
||||||
this.input.trigger('change',this.propertyType,this.value());
|
this.input.trigger('change',this.propertyType,this.value());
|
||||||
} else {
|
} else {
|
||||||
if (this.optionSelectTrigger) {
|
if (this.optionSelectTrigger) {
|
||||||
@ -758,17 +841,44 @@
|
|||||||
this.elementDiv.show();
|
this.elementDiv.show();
|
||||||
}
|
}
|
||||||
if (this.optionExpandButton) {
|
if (this.optionExpandButton) {
|
||||||
if (opt.expand && typeof opt.expand === 'function') {
|
if (opt.expand) {
|
||||||
|
if (opt.expand.icon) {
|
||||||
|
this.optionExpandButtonIcon.removeClass().addClass("red-ui-typedInput-icon fa "+opt.expand.icon)
|
||||||
|
} else {
|
||||||
|
this.optionExpandButtonIcon.removeClass().addClass("red-ui-typedInput-icon fa fa-ellipsis-h")
|
||||||
|
}
|
||||||
|
this.optionExpandButton.shown = true;
|
||||||
this.optionExpandButton.show();
|
this.optionExpandButton.show();
|
||||||
this.optionExpandButton.off('click');
|
this.optionExpandButton.off('click');
|
||||||
this.optionExpandButton.on('click',function(evt) {
|
this.optionExpandButton.on('click',function(evt) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
opt.expand.call(that);
|
if (typeof opt.expand === 'function') {
|
||||||
|
opt.expand.call(that);
|
||||||
|
} else {
|
||||||
|
var container = $('<div>');
|
||||||
|
var content = opt.expand.content.call(that,container);
|
||||||
|
var panel = RED.popover.panel(container);
|
||||||
|
panel.container.css({
|
||||||
|
width:that.valueLabelContainer.width()
|
||||||
|
});
|
||||||
|
if (opt.expand.minWidth) {
|
||||||
|
panel.container.css({
|
||||||
|
minWidth: opt.expand.minWidth+"px"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
panel.show({
|
||||||
|
target:that.optionExpandButton,
|
||||||
|
onclose:content.onclose,
|
||||||
|
align: "right"
|
||||||
|
});
|
||||||
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
this.optionExpandButton.shown = false;
|
||||||
this.optionExpandButton.hide();
|
this.optionExpandButton.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this._trigger("typechange",null,this.propertyType);
|
||||||
this.input.trigger('change',this.propertyType,this.value());
|
this.input.trigger('change',this.propertyType,this.value());
|
||||||
}
|
}
|
||||||
if (!image) {
|
if (!image) {
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
*/
|
*/
|
||||||
RED.editor = (function() {
|
RED.editor = (function() {
|
||||||
|
|
||||||
|
|
||||||
var editStack = [];
|
var editStack = [];
|
||||||
var editing_node = null;
|
var editing_node = null;
|
||||||
var editing_config_node = null;
|
var editing_config_node = null;
|
||||||
@ -546,148 +545,7 @@ RED.editor = (function() {
|
|||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildEnvForm(container, node) {
|
function isSameObj(env0, env1) {
|
||||||
var env_container = $('#node-input-env-container');
|
|
||||||
env_container
|
|
||||||
.css({
|
|
||||||
'min-height':'150px',
|
|
||||||
'min-width':'450px'
|
|
||||||
})
|
|
||||||
.editableList({
|
|
||||||
addItem: function(container, i, opt) {
|
|
||||||
var row = $('<div/>').appendTo(container);
|
|
||||||
if (opt.parent) {
|
|
||||||
$('<div/>', {
|
|
||||||
class:"uneditable-input",
|
|
||||||
style: "margin-left: 5px; width: calc(40% - 8px)",
|
|
||||||
}).appendTo(row).text(opt.name);
|
|
||||||
} else {
|
|
||||||
$('<input/>', {
|
|
||||||
class: "node-input-env-name",
|
|
||||||
type: "text",
|
|
||||||
style: "margin-left: 5px; width: calc(40% - 8px)",
|
|
||||||
placeholder: RED._("common.label.name")
|
|
||||||
}).attr("autocomplete","disable").appendTo(row).val(opt.name);
|
|
||||||
}
|
|
||||||
var valueField = $('<input/>',{
|
|
||||||
class: "node-input-env-value",
|
|
||||||
type: "text",
|
|
||||||
style: "margin-left: 5px; width: calc(60% - 8px)"
|
|
||||||
}).attr("autocomplete","disable").appendTo(row)
|
|
||||||
|
|
||||||
valueField.typedInput({default:'str',
|
|
||||||
types:['str','num','bool','json','bin','env']
|
|
||||||
});
|
|
||||||
|
|
||||||
valueField.typedInput('type', opt.parent?(opt.type||opt.parent.type):opt.type);
|
|
||||||
valueField.typedInput('value', opt.parent?((opt.value !== undefined)?opt.value:opt.parent.value):opt.value);
|
|
||||||
|
|
||||||
var actionButton = $('<a/>',{href:"#",class:"red-ui-editableList-item-remove red-ui-button red-ui-button-small"}).appendTo(container);
|
|
||||||
$('<i/>',{class:"fa "+(opt.parent?"fa-reply":"fa-remove")}).appendTo(actionButton);
|
|
||||||
container.parent().addClass("red-ui-editableList-item-removable");
|
|
||||||
if (opt.parent) {
|
|
||||||
if ((opt.value !== undefined) && (opt.value !== opt.parent.value || opt.type !== opt.parent.type)) {
|
|
||||||
actionButton.show();
|
|
||||||
} else {
|
|
||||||
actionButton.hide();
|
|
||||||
}
|
|
||||||
var restoreTip = RED.popover.tooltip(actionButton,RED._("subflow.env.restore"));
|
|
||||||
valueField.on("change", function(evt) {
|
|
||||||
var newType = valueField.typedInput('type');
|
|
||||||
var newValue = valueField.typedInput('value');
|
|
||||||
if (newType === opt.parent.type && newValue === opt.parent.value) {
|
|
||||||
actionButton.hide();
|
|
||||||
} else {
|
|
||||||
actionButton.show();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
actionButton.on("click", function(evt) {
|
|
||||||
evt.preventDefault();
|
|
||||||
restoreTip.close();
|
|
||||||
valueField.typedInput('type', opt.parent.type);
|
|
||||||
valueField.typedInput('value', opt.parent.value);
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
var removeTip = RED.popover.tooltip(actionButton,RED._("subflow.env.remove"));
|
|
||||||
actionButton.on("click", function(evt) {
|
|
||||||
evt.preventDefault();
|
|
||||||
removeTip.close();
|
|
||||||
container.parent().addClass("red-ui-editableList-item-deleting")
|
|
||||||
container.fadeOut(300, function() {
|
|
||||||
env_container.editableList('removeItem',opt);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sortable: false,
|
|
||||||
removable: false
|
|
||||||
});
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
envList.forEach(function(env) {
|
|
||||||
env_container.editableList('addItem', env);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function exportEnvList(list) {
|
|
||||||
if (list) {
|
|
||||||
var env = [];
|
|
||||||
list.each(function(i) {
|
|
||||||
var entry = $(this);
|
|
||||||
var item = entry.data('data');
|
|
||||||
var name = (item.parent?item.name:entry.find(".node-input-env-name").val()).trim();
|
|
||||||
if (name !== "") {
|
|
||||||
var valueInput = entry.find(".node-input-env-value");
|
|
||||||
var value = valueInput.typedInput("value");
|
|
||||||
var type = valueInput.typedInput("type");
|
|
||||||
if (!item.parent || (item.parent.value !== value || item.parent.type !== type)) {
|
|
||||||
var item = {
|
|
||||||
name: name,
|
|
||||||
type: type,
|
|
||||||
value: value
|
|
||||||
};
|
|
||||||
env.push(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return env;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isSameEnv(env0, env1) {
|
|
||||||
return (JSON.stringify(env0) === JSON.stringify(env1));
|
return (JSON.stringify(env0) === JSON.stringify(env1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -713,8 +571,8 @@ RED.editor = (function() {
|
|||||||
$(this).attr("data-i18n",keys.join(";"));
|
$(this).attr("data-i18n",keys.join(";"));
|
||||||
});
|
});
|
||||||
|
|
||||||
if ((type === "subflow") || (type === "subflow-template")) {
|
if (type === "subflow-template" || type === "subflow") {
|
||||||
buildEnvForm(dialogForm, node);
|
RED.subflow.buildEditForm(dialogForm,type,node);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add dummy fields to prevent 'Enter' submitting the form in some
|
// Add dummy fields to prevent 'Enter' submitting the form in some
|
||||||
@ -857,7 +715,7 @@ RED.editor = (function() {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
function showIconPicker(container, node, iconPath, done) {
|
function showIconPicker(container, node, iconPath, faOnly, done) {
|
||||||
var containerPos = container.offset();
|
var containerPos = container.offset();
|
||||||
var pickerBackground = $('<div>').css({
|
var pickerBackground = $('<div>').css({
|
||||||
position: "absolute",top:0,bottom:0,left:0,right:0,zIndex:20
|
position: "absolute",top:0,bottom:0,left:0,right:0,zIndex:20
|
||||||
@ -912,7 +770,14 @@ RED.editor = (function() {
|
|||||||
done(null);
|
done(null);
|
||||||
});
|
});
|
||||||
var iconSets = RED.nodes.getIconSets();
|
var iconSets = RED.nodes.getIconSets();
|
||||||
|
var backgroundColor = node && RED.utils.getNodeColor(node.type, node._def);
|
||||||
|
if (!node && faOnly) {
|
||||||
|
iconList.addClass("red-ui-icon-list-dark");
|
||||||
|
}
|
||||||
Object.keys(iconSets).forEach(function(moduleName) {
|
Object.keys(iconSets).forEach(function(moduleName) {
|
||||||
|
if (faOnly && (moduleName !== "font-awesome")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
var icons = iconSets[moduleName];
|
var icons = iconSets[moduleName];
|
||||||
if (icons.length > 0) {
|
if (icons.length > 0) {
|
||||||
// selectIconModule.append($("<option></option>").val(moduleName).text(moduleName));
|
// selectIconModule.append($("<option></option>").val(moduleName).text(moduleName));
|
||||||
@ -921,10 +786,13 @@ RED.editor = (function() {
|
|||||||
icons.forEach(function(icon) {
|
icons.forEach(function(icon) {
|
||||||
var iconDiv = $('<div>',{class:"red-ui-icon-list-icon"}).appendTo(iconList);
|
var iconDiv = $('<div>',{class:"red-ui-icon-list-icon"}).appendTo(iconList);
|
||||||
var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).appendTo(iconDiv);
|
var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).appendTo(iconDiv);
|
||||||
var colour = RED.utils.getNodeColor(node.type, node._def);
|
|
||||||
var icon_url = RED.settings.apiRootUrl+"icons/"+moduleName+"/"+icon;
|
var icon_url = RED.settings.apiRootUrl+"icons/"+moduleName+"/"+icon;
|
||||||
iconDiv.data('icon',icon_url);
|
iconDiv.data('icon',icon_url);
|
||||||
nodeDiv.css('backgroundColor',colour);
|
if (node) {
|
||||||
|
nodeDiv.css({
|
||||||
|
'backgroundColor': backgroundColor
|
||||||
|
});
|
||||||
|
}
|
||||||
var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
|
var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
|
||||||
RED.utils.createIconElement(icon_url, iconContainer, true);
|
RED.utils.createIconElement(icon_url, iconContainer, true);
|
||||||
|
|
||||||
@ -1049,7 +917,7 @@ RED.editor = (function() {
|
|||||||
} else {
|
} else {
|
||||||
iconPath = RED.utils.getDefaultNodeIcon(node._def, node);
|
iconPath = RED.utils.getDefaultNodeIcon(node._def, node);
|
||||||
}
|
}
|
||||||
showIconPicker(iconRow,node,iconPath,function(newIcon) {
|
showIconPicker(iconRow,node,iconPath,false,function(newIcon) {
|
||||||
$("#red-ui-editor-node-icon").text(newIcon||"");
|
$("#red-ui-editor-node-icon").text(newIcon||"");
|
||||||
var icon_url = RED.utils.getNodeIcon(node._def,{type:node.type,icon:newIcon});
|
var icon_url = RED.utils.getNodeIcon(node._def,{type:node.type,icon:newIcon});
|
||||||
RED.utils.createIconElement(icon_url, iconContainer, true);
|
RED.utils.createIconElement(icon_url, iconContainer, true);
|
||||||
@ -1093,6 +961,7 @@ RED.editor = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function updateLabels(editing_node, changes, outputMap) {
|
function updateLabels(editing_node, changes, outputMap) {
|
||||||
var inputLabels = $("#red-ui-editor-node-label-form-inputs").children().find("input");
|
var inputLabels = $("#red-ui-editor-node-label-form-inputs").children().find("input");
|
||||||
var outputLabels = $("#red-ui-editor-node-label-form-outputs").children().find("input");
|
var outputLabels = $("#red-ui-editor-node-label-form-outputs").children().find("input");
|
||||||
@ -1483,13 +1352,13 @@ RED.editor = (function() {
|
|||||||
|
|
||||||
if (type === "subflow") {
|
if (type === "subflow") {
|
||||||
var old_env = editing_node.env;
|
var old_env = editing_node.env;
|
||||||
var new_env = exportEnvList($("#node-input-env-container").editableList("items"));
|
var new_env = RED.subflow.exportSubflowInstanceEnv(editing_node);
|
||||||
if (!isSameEnv(old_env, new_env)) {
|
if (!isSameObj(old_env, new_env)) {
|
||||||
editing_node.env = new_env;
|
editing_node.env = new_env;
|
||||||
changes.env = editing_node.env;
|
changes.env = editing_node.env;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
var wasChanged = editing_node.changed;
|
var wasChanged = editing_node.changed;
|
||||||
@ -1607,6 +1476,19 @@ RED.editor = (function() {
|
|||||||
buildEditForm(nodePropertiesTab.content,"dialog-form",type,ns,node);
|
buildEditForm(nodePropertiesTab.content,"dialog-form",type,ns,node);
|
||||||
editorTabs.addTab(nodePropertiesTab);
|
editorTabs.addTab(nodePropertiesTab);
|
||||||
|
|
||||||
|
if (/^subflow:/.test(node.type)) {
|
||||||
|
var subflowPropertiesTab = {
|
||||||
|
id: "editor-subflow-envProperties",
|
||||||
|
label: RED._("editor-tab.envProperties"),
|
||||||
|
name: RED._("editor-tab.envProperties"),
|
||||||
|
content: $('<div>', {class:"red-ui-tray-content"}).appendTo(editorContent).hide(),
|
||||||
|
iconClass: "fa fa-list"
|
||||||
|
};
|
||||||
|
|
||||||
|
RED.subflow.buildPropertiesForm(subflowPropertiesTab.content,node);
|
||||||
|
editorTabs.addTab(subflowPropertiesTab);
|
||||||
|
}
|
||||||
|
|
||||||
if (!node._def.defaults || !node._def.defaults.hasOwnProperty('info')) {
|
if (!node._def.defaults || !node._def.defaults.hasOwnProperty('info')) {
|
||||||
var descriptionTab = {
|
var descriptionTab = {
|
||||||
id: "editor-tab-description",
|
id: "editor-tab-description",
|
||||||
@ -1875,7 +1757,6 @@ RED.editor = (function() {
|
|||||||
var configId = editing_config_node.id;
|
var configId = editing_config_node.id;
|
||||||
var configAdding = adding;
|
var configAdding = adding;
|
||||||
var configTypeDef = RED.nodes.getType(configType);
|
var configTypeDef = RED.nodes.getType(configType);
|
||||||
|
|
||||||
if (configTypeDef.oneditcancel) {
|
if (configTypeDef.oneditcancel) {
|
||||||
// TODO: what to pass as this to call
|
// TODO: what to pass as this to call
|
||||||
if (configTypeDef.oneditcancel) {
|
if (configTypeDef.oneditcancel) {
|
||||||
@ -2234,6 +2115,15 @@ RED.editor = (function() {
|
|||||||
editing_node.category = newCategory;
|
editing_node.category = newCategory;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var old_env = editing_node.env;
|
||||||
|
var new_env = RED.subflow.exportSubflowTemplateEnv($("#node-input-env-container").editableList("items"));
|
||||||
|
if (!isSameObj(old_env, new_env)) {
|
||||||
|
editing_node.env = new_env;
|
||||||
|
changes.env = editing_node.env;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
RED.palette.refresh();
|
RED.palette.refresh();
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
@ -2274,15 +2164,18 @@ RED.editor = (function() {
|
|||||||
],
|
],
|
||||||
resize: function(size) {
|
resize: function(size) {
|
||||||
$(".red-ui-tray-content").height(size.height - 50);
|
$(".red-ui-tray-content").height(size.height - 50);
|
||||||
// var form = $(".red-ui-tray-content form").height(size.height - 50 - 40);
|
var envContainer = $("#node-input-env-container");
|
||||||
var rows = $("#dialog-form>div:not(.node-input-env-container-row)");
|
if (envContainer.length) {
|
||||||
var height = size.height;
|
// var form = $(".red-ui-tray-content form").height(size.height - 50 - 40);
|
||||||
for (var i=0; i<rows.size(); i++) {
|
var rows = $("#dialog-form>div:not(#subflow-env-tabs-content)");
|
||||||
height -= $(rows[i]).outerHeight(true);
|
var height = size.height;
|
||||||
|
for (var i=0; i<rows.size(); i++) {
|
||||||
|
height -= $(rows[i]).outerHeight(true);
|
||||||
|
}
|
||||||
|
// var editorRow = $("#dialog-form>div.node-input-env-container-row");
|
||||||
|
// height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||||
|
$("#node-input-env-container").editableList('height',height-95);
|
||||||
}
|
}
|
||||||
var editorRow = $("#dialog-form>div.node-input-env-container-row");
|
|
||||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
|
||||||
$("#node-input-env-container").editableList('height',height-60);
|
|
||||||
},
|
},
|
||||||
open: function(tray) {
|
open: function(tray) {
|
||||||
var trayFooter = tray.find(".red-ui-tray-footer");
|
var trayFooter = tray.find(".red-ui-tray-footer");
|
||||||
@ -2518,6 +2411,8 @@ RED.editor = (function() {
|
|||||||
validateNode: validateNode,
|
validateNode: validateNode,
|
||||||
updateNodeProperties: updateNodeProperties, // TODO: only exposed for edit-undo
|
updateNodeProperties: updateNodeProperties, // TODO: only exposed for edit-undo
|
||||||
|
|
||||||
|
showIconPicker:showIconPicker,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show a type editor.
|
* Show a type editor.
|
||||||
* @param {string} type - the type to display
|
* @param {string} type - the type to display
|
||||||
|
@ -16,16 +16,29 @@
|
|||||||
|
|
||||||
RED.subflow = (function() {
|
RED.subflow = (function() {
|
||||||
|
|
||||||
|
var currentLocale = "en-US";
|
||||||
|
|
||||||
var _subflowEditTemplate = '<script type="text/x-red" data-template-name="subflow">'+
|
var _subflowEditTemplate = '<script type="text/x-red" data-template-name="subflow">'+
|
||||||
'<div class="form-row"><label for="node-input-name" data-i18n="[append]editor:common.label.name"><i class="fa fa-tag"></i> </label><input type="text" id="node-input-name"></div>'+
|
'<div class="form-row"><label for="node-input-name" data-i18n="[append]editor:common.label.name"><i class="fa fa-tag"></i> </label><input type="text" id="node-input-name"></div>'+
|
||||||
'<div class="form-row" style="margin-bottom: 0px;"><label style="width: auto;" data-i18n="[append]editor:editor-tab.env"><i class="fa fa-th-list"></i> </label></div>'+
|
'<div id="subflow-input-ui"></div>'+
|
||||||
'<div class="form-row node-input-env-container-row"><ol id="node-input-env-container"></ol></div>'+
|
|
||||||
'</script>';
|
'</script>';
|
||||||
|
|
||||||
var _subflowTemplateEditTemplate = '<script type="text/x-red" data-template-name="subflow-template">'+
|
var _subflowTemplateEditTemplate = '<script type="text/x-red" data-template-name="subflow-template">'+
|
||||||
'<div class="form-row"><i class="fa fa-tag"></i> <label for="subflow-input-name" data-i18n="common.label.name"></label><input type="text" id="subflow-input-name"></div>'+
|
'<div class="form-row"><label for="subflow-input-name" data-i18n="[append]common.label.name"><i class="fa fa-tag"></i> </label><input type="text" id="subflow-input-name"></div>'+
|
||||||
'<div class="form-row" style="margin-bottom: 0px;"><label style="width: auto;" data-i18n="[append]editor:editor-tab.env"><i class="fa fa-th-list"></i> </label></div>'+
|
'<div class="form-row">'+
|
||||||
'<div class="form-row node-input-env-container-row"><ol id="node-input-env-container"></ol></div>'+
|
'<ul style="margin-bottom: 20px;" id="subflow-env-tabs"></ul>'+
|
||||||
|
'</div>'+
|
||||||
|
'<div id="subflow-env-tabs-content">'+
|
||||||
|
'<div id="subflow-env-tab-edit">'+
|
||||||
|
'<div class="form-row node-input-env-container-row" id="subflow-input-edit-ui">'+
|
||||||
|
'<ol class="red-ui-editor-subflow-env-list" id="node-input-env-container"></ol>'+
|
||||||
|
'<div class="node-input-env-locales-row"><i class="fa fa-language"></i> <select id="subflow-input-env-locale"></select></div>'+
|
||||||
|
'</div>'+
|
||||||
|
'</div>'+
|
||||||
|
'<div id="subflow-env-tab-preview">'+
|
||||||
|
'<div id="subflow-input-ui"/>'+
|
||||||
|
'</div>'+
|
||||||
|
'</div>'+
|
||||||
'</script>';
|
'</script>';
|
||||||
|
|
||||||
function findAvailableSubflowIOPosition(subflow,isInput) {
|
function findAvailableSubflowIOPosition(subflow,isInput) {
|
||||||
@ -747,6 +760,946 @@ RED.subflow = (function() {
|
|||||||
RED.view.redraw(true);
|
RED.view.redraw(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create interface for controlling env var UI definition
|
||||||
|
*/
|
||||||
|
function buildEnvControl(envList) {
|
||||||
|
|
||||||
|
var tabs = RED.tabs.create({
|
||||||
|
id: "subflow-env-tabs",
|
||||||
|
onchange: function(tab) {
|
||||||
|
if (tab.id === "subflow-env-tab-preview") {
|
||||||
|
var inputContainer = $("#subflow-input-ui");
|
||||||
|
var list = envList.editableList("items");
|
||||||
|
var exportedEnv = exportEnvList(list, true);
|
||||||
|
buildEnvUI(inputContainer, exportedEnv);
|
||||||
|
}
|
||||||
|
$("#subflow-env-tabs-content").children().hide();
|
||||||
|
$("#" + tab.id).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tabs.addTab({
|
||||||
|
id: "subflow-env-tab-edit",
|
||||||
|
label: RED._("editor-tab.envProperties")
|
||||||
|
});
|
||||||
|
tabs.addTab({
|
||||||
|
id: "subflow-env-tab-preview",
|
||||||
|
label: RED._("editor-tab.preview")
|
||||||
|
});
|
||||||
|
|
||||||
|
var localesList = RED.settings.theme("languages")
|
||||||
|
.map(function(lc) { var name = RED._("languages."+lc); return {text: (name ? name : lc), val: lc}; })
|
||||||
|
.sort(function(a, b) { return a.text.localeCompare(b.text) });
|
||||||
|
RED.popover.tooltip($(".node-input-env-locales-row i"),RED._("editor.locale"))
|
||||||
|
var locales = $("#subflow-input-env-locale")
|
||||||
|
localesList.forEach(function(item) {
|
||||||
|
var opt = {
|
||||||
|
value: item.val
|
||||||
|
};
|
||||||
|
if (item.val === "en-US") { // make en-US default selected
|
||||||
|
opt.selected = "";
|
||||||
|
}
|
||||||
|
$("<option/>", opt).text(item.text).appendTo(locales);
|
||||||
|
});
|
||||||
|
currentLocale = RED.i18n.lang();
|
||||||
|
locales.val(currentLocale);
|
||||||
|
|
||||||
|
locales.on("change", function() {
|
||||||
|
currentLocale = $(this).val();
|
||||||
|
var items = $("#node-input-env-container").editableList("items");
|
||||||
|
items.each(function (i, item) {
|
||||||
|
var entry = $(this).data('data');
|
||||||
|
var labelField = entry.ui.labelField;
|
||||||
|
labelField.val(lookupLabel(entry.ui.label, "", currentLocale));
|
||||||
|
if (labelField.timeout) {
|
||||||
|
clearTimeout(labelField.timeout);
|
||||||
|
delete labelField.timeout;
|
||||||
|
}
|
||||||
|
labelField.addClass("input-updated");
|
||||||
|
labelField.timeout = setTimeout(function() {
|
||||||
|
delete labelField.timeout
|
||||||
|
labelField.removeClass("input-updated");
|
||||||
|
},3000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create env var edit interface
|
||||||
|
* @param container - container
|
||||||
|
* @param node - subflow node
|
||||||
|
*/
|
||||||
|
function buildPropertiesList(envContainer, node) {
|
||||||
|
|
||||||
|
var isTemplateNode = (node.type === "subflow");
|
||||||
|
|
||||||
|
if (isTemplateNode) {
|
||||||
|
buildEnvControl(envContainer);
|
||||||
|
}
|
||||||
|
envContainer
|
||||||
|
.css({
|
||||||
|
'min-height':'150px',
|
||||||
|
'min-width':'450px'
|
||||||
|
})
|
||||||
|
.editableList({
|
||||||
|
header: isTemplateNode?$('<div><div><div></div><div data-i18n="common.label.name"></div><div data-i18n="editor-tab.defaultValue"></div><div></div></div></div>'):undefined,
|
||||||
|
addItem: function(container, i, opt) {
|
||||||
|
if (isTemplateNode) {
|
||||||
|
container.addClass("red-ui-editor-subflow-env-editable")
|
||||||
|
}
|
||||||
|
|
||||||
|
var envRow = $('<div/>').appendTo(container);
|
||||||
|
var nameField = null;
|
||||||
|
var valueField = null;
|
||||||
|
|
||||||
|
// if (opt.parent) {
|
||||||
|
// buildEnvUIRow(envRow,opt,opt.parent.ui||{})
|
||||||
|
// } else {
|
||||||
|
nameField = $('<input/>', {
|
||||||
|
class: "node-input-env-name",
|
||||||
|
type: "text",
|
||||||
|
placeholder: RED._("common.label.name")
|
||||||
|
}).attr("autocomplete","disable").appendTo(envRow).val(opt.name);
|
||||||
|
valueField = $('<input/>',{
|
||||||
|
style: "width:100%",
|
||||||
|
class: "node-input-env-value",
|
||||||
|
type: "text",
|
||||||
|
}).attr("autocomplete","disable").appendTo(envRow)
|
||||||
|
valueField.typedInput({default:'str',types:['str','num','bool','json','bin','env']});
|
||||||
|
valueField.typedInput('type', opt.parent?(opt.type||opt.parent.type):opt.type);
|
||||||
|
valueField.typedInput('value', opt.parent?((opt.value !== undefined)?opt.value:opt.parent.value):opt.value);
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
opt.nameField = nameField;
|
||||||
|
opt.valueField = valueField;
|
||||||
|
|
||||||
|
if (!opt.parent) {
|
||||||
|
var actionButton = $('<a/>',{href:"#",class:"red-ui-editableList-item-remove red-ui-button red-ui-button-small"}).appendTo(envRow);
|
||||||
|
$('<i/>',{class:"fa "+(opt.parent?"fa-reply":"fa-remove")}).appendTo(actionButton);
|
||||||
|
var removeTip = RED.popover.tooltip(actionButton,RED._("subflow.env.remove"));
|
||||||
|
actionButton.on("click", function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
removeTip.close();
|
||||||
|
container.parent().addClass("red-ui-editableList-item-deleting")
|
||||||
|
container.fadeOut(300, function() {
|
||||||
|
envContainer.editableList('removeItem',opt);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTemplateNode) {
|
||||||
|
// Add the UI customisation row
|
||||||
|
// if `opt.ui` does not exist, then apply defaults. If these
|
||||||
|
// defaults do not change then they will get stripped off
|
||||||
|
// before saving.
|
||||||
|
opt.ui = opt.ui || {
|
||||||
|
icon: "",
|
||||||
|
label: {},
|
||||||
|
type: "input",
|
||||||
|
opts: {types:['str','num','bool','json','bin','env']}
|
||||||
|
}
|
||||||
|
opt.ui.label = opt.ui.label || {};
|
||||||
|
opt.ui.type = opt.ui.type || "input";
|
||||||
|
|
||||||
|
var uiRow = $('<div/>').appendTo(container).hide();
|
||||||
|
// save current info for reverting on cancel
|
||||||
|
// var copy = $.extend(true, {}, ui);
|
||||||
|
|
||||||
|
$('<a href="#"><i class="fa fa-angle-right"></a>').prependTo(envRow).on("click", function (evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
if ($(this).hasClass('expanded')) {
|
||||||
|
uiRow.slideUp();
|
||||||
|
$(this).removeClass('expanded');
|
||||||
|
} else {
|
||||||
|
uiRow.slideDown();
|
||||||
|
$(this).addClass('expanded');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
buildEnvEditRow(uiRow, opt.ui, nameField, valueField);
|
||||||
|
nameField.trigger('change');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sortable: ".red-ui-editableList-item-handle",
|
||||||
|
removable: false
|
||||||
|
});
|
||||||
|
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,
|
||||||
|
ui: env.ui
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
envList.forEach(function(env) {
|
||||||
|
if (env.parent && env.parent.ui && env.parent.ui.type === 'hide') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isTemplateNode && env.parent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
envContainer.editableList('addItem', JSON.parse(JSON.stringify(env)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create UI edit interface for environment variable
|
||||||
|
* @param container - container
|
||||||
|
* @param env - env var definition
|
||||||
|
* @param nameField - name field of env var
|
||||||
|
* @param valueField - value field of env var
|
||||||
|
*/
|
||||||
|
function buildEnvEditRow(container, ui, nameField, valueField) {
|
||||||
|
container.addClass("red-ui-editor-subflow-env-ui-row")
|
||||||
|
var topRow = $('<div></div>').appendTo(container);
|
||||||
|
$('<div></div>').appendTo(topRow);
|
||||||
|
$('<div>').text(RED._("editor.icon")).appendTo(topRow);
|
||||||
|
$('<div>').text(RED._("editor.label")).appendTo(topRow);
|
||||||
|
$('<div>').text(RED._("editor.inputType")).appendTo(topRow);
|
||||||
|
|
||||||
|
var row = $('<div></div>').appendTo(container);
|
||||||
|
$('<div><i class="red-ui-editableList-item-handle fa fa-bars"></i></div>').appendTo(row);
|
||||||
|
|
||||||
|
var typeOptions = {
|
||||||
|
'input': {types:['str','num','bool','json','bin','env']},
|
||||||
|
'select': {opts:[]},
|
||||||
|
'spinner': {}
|
||||||
|
};
|
||||||
|
if (ui.opts) {
|
||||||
|
typeOptions[ui.type] = ui.opts;
|
||||||
|
} else {
|
||||||
|
// Pick up the default values if not otherwise provided
|
||||||
|
ui.opts = typeOptions[ui.type];
|
||||||
|
}
|
||||||
|
var iconCell = $('<div></div>').appendTo(row);
|
||||||
|
|
||||||
|
var iconButton = $('<a href="#"></a>').appendTo(iconCell);
|
||||||
|
iconButton.on("click", function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
var icon = ui.icon || "";
|
||||||
|
var iconPath = (icon ? RED.utils.separateIconPath(icon) : {});
|
||||||
|
RED.editor.showIconPicker(row, null, iconPath, true, function (newIcon) {
|
||||||
|
iconButton.empty();
|
||||||
|
var path = newIcon || "";
|
||||||
|
var newPath = RED.utils.separateIconPath(path);
|
||||||
|
if (newPath) {
|
||||||
|
$('<i class="fa"></i>').addClass(newPath.file).appendTo(iconButton);
|
||||||
|
}
|
||||||
|
ui.icon = path;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
if (ui.icon) {
|
||||||
|
var newPath = RED.utils.separateIconPath(ui.icon);
|
||||||
|
$('<i class="fa '+newPath.file+'"></i>').appendTo(iconButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
var labelCell = $('<div></div>').appendTo(row);
|
||||||
|
|
||||||
|
var label = ui.label && ui.label[currentLocale] || "";
|
||||||
|
var labelInput = $('<input type="text">').val(label).appendTo(labelCell);
|
||||||
|
ui.labelField = labelInput;
|
||||||
|
labelInput.on('change', function(evt) {
|
||||||
|
ui.label = ui.label || {};
|
||||||
|
var val = $(this).val().trim();
|
||||||
|
if (val === "") {
|
||||||
|
delete ui.label[currentLocale];
|
||||||
|
} else {
|
||||||
|
ui.label[currentLocale] = val;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
var labelIcon = $('<span class="red-ui-editor-subflow-env-lang-icon"><i class="fa fa-language"></i></span>').appendTo(labelCell);
|
||||||
|
RED.popover.tooltip(labelIcon,function() {
|
||||||
|
var langs = Object.keys(ui.label);
|
||||||
|
var content = $("<div>");
|
||||||
|
if (langs.indexOf(currentLocale) === -1) {
|
||||||
|
langs.push(currentLocale);
|
||||||
|
langs.sort();
|
||||||
|
}
|
||||||
|
langs.forEach(function(l) {
|
||||||
|
var row = $('<div>').appendTo(content);
|
||||||
|
$('<span>').css({display:"inline-block",width:"50px"}).text(l+(l===currentLocale?"*":"")).appendTo(row);
|
||||||
|
$('<span>').text(ui.label[l]||"").appendTo(row);
|
||||||
|
});
|
||||||
|
return content;
|
||||||
|
})
|
||||||
|
|
||||||
|
nameField.on('change',function(evt) {
|
||||||
|
labelInput.attr("placeholder",$(this).val())
|
||||||
|
});
|
||||||
|
|
||||||
|
var inputCell = $('<div></div>').appendTo(row);
|
||||||
|
var inputCellInput = $('<input type="text">').css("width","100%").appendTo(inputCell);
|
||||||
|
if (ui.type === "input") {
|
||||||
|
inputCellInput.val(ui.opts.types.join(","));
|
||||||
|
}
|
||||||
|
var checkbox;
|
||||||
|
var selectBox;
|
||||||
|
|
||||||
|
inputCellInput.typedInput({
|
||||||
|
types: [
|
||||||
|
{value:"input",
|
||||||
|
label:"input", icon:"fa fa-i-cursor",showLabel:false,multiple:true,options:[
|
||||||
|
{value:"str",label:"string",icon:"red/images/typedInput/az.svg"},
|
||||||
|
{value:"num",label:"number",icon:"red/images/typedInput/09.svg"},
|
||||||
|
{value:"bool",label:"boolean",icon:"red/images/typedInput/bool.svg"},
|
||||||
|
{value:"json",label:"JSON",icon:"red/images/typedInput/json.svg"},
|
||||||
|
{value: "bin",label: "buffer",icon: "red/images/typedInput/bin.svg"},
|
||||||
|
{value: "env",label: "env variable",icon: "red/images/typedInput/env.svg"}
|
||||||
|
],
|
||||||
|
default: ['str','num','bool','json','bin','env'],
|
||||||
|
valueLabel: function(container,value) {
|
||||||
|
container.css("padding",0);
|
||||||
|
var innerContainer = $('<div>').css({
|
||||||
|
"background":"white",
|
||||||
|
"height":"100%",
|
||||||
|
"box-sizing": "border-box"
|
||||||
|
}).appendTo(container);
|
||||||
|
|
||||||
|
var input = $('<div class="placeholder-input">').appendTo(innerContainer);
|
||||||
|
$('<span><i class="fa fa-i-cursor"></i></span>').appendTo(input);
|
||||||
|
if (value.length) {
|
||||||
|
value.forEach(function(v) {
|
||||||
|
$('<img>',{src:v.icon,style:"max-width:14px; padding: 0 3px; margin-top:-4px; margin-left: 3px"}).appendTo(input);
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
$("<span>").css({
|
||||||
|
"color":"#aaa",
|
||||||
|
"padding-left": "4px"
|
||||||
|
}).text("select types...").appendTo(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{value:"select",
|
||||||
|
label:"select", icon:"fa fa-tasks",showLabel:false,
|
||||||
|
valueLabel: function(container,value) {
|
||||||
|
container.css("padding","0");
|
||||||
|
|
||||||
|
selectBox = $('<select></select>').appendTo(container);
|
||||||
|
if (ui.opts && Array.isArray(ui.opts.opts)) {
|
||||||
|
ui.opts.opts.forEach(function(o) {
|
||||||
|
var label = lookupLabel(o.l, o.l["en-US"]||o.v, currentLocale);
|
||||||
|
// $('<option>').val((o.t||'str')+":"+o.v).text(label).appendTo(selectBox);
|
||||||
|
$('<option>').val(o.v).text(label).appendTo(selectBox);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
selectBox.on('change', function(evt) {
|
||||||
|
var v = selectBox.val();
|
||||||
|
// var parts = v.split(":");
|
||||||
|
// var t = parts.shift();
|
||||||
|
// v = parts.join(":");
|
||||||
|
//
|
||||||
|
// valueField.typedInput("type",'str')
|
||||||
|
valueField.typedInput("value",v)
|
||||||
|
});
|
||||||
|
selectBox.val(valueField.typedInput("value"));
|
||||||
|
// selectBox.val(valueField.typedInput('type')+":"+valueField.typedInput("value"));
|
||||||
|
},
|
||||||
|
expand: {
|
||||||
|
icon: "fa-caret-down",
|
||||||
|
minWidth: 400,
|
||||||
|
content: function(container) {
|
||||||
|
var content = $('<div class="red-ui-editor-subflow-ui-edit-panel">').appendTo(container);
|
||||||
|
var optList = $('<ol>').appendTo(content).editableList({
|
||||||
|
header:$("<div><div>Label</div><div>Value</div></div>"),
|
||||||
|
addItem: function(row,index,itemData) {
|
||||||
|
var labelDiv = $('<div>').appendTo(row);
|
||||||
|
var label = lookupLabel(itemData.l, "", currentLocale);
|
||||||
|
itemData.label = $('<input type="text">').val(label).appendTo(labelDiv);
|
||||||
|
itemData.label.on('keydown', function(evt) {
|
||||||
|
if (evt.keyCode === 13) {
|
||||||
|
itemData.input.focus();
|
||||||
|
evt.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var labelIcon = $('<span class="red-ui-editor-subflow-env-lang-icon"><i class="fa fa-language"></i></span>').appendTo(labelDiv);
|
||||||
|
RED.popover.tooltip(labelIcon,function() {
|
||||||
|
return currentLocale;
|
||||||
|
})
|
||||||
|
itemData.input = $('<input type="text">').val(itemData.v).appendTo(row);
|
||||||
|
|
||||||
|
// Problem using a TI here:
|
||||||
|
// - this is in a popout panel
|
||||||
|
// - clicking the expand button in the TI will close the parent edit tray
|
||||||
|
// and open the type editor.
|
||||||
|
// - but it leaves the popout panel over the top.
|
||||||
|
// - there is no way to get back to the popout panel after closing the type editor
|
||||||
|
//.typedInput({default:itemData.t||'str', types:['str','num','bool','json','bin','env']});
|
||||||
|
itemData.input.on('keydown', function(evt) {
|
||||||
|
if (evt.keyCode === 13) {
|
||||||
|
// Enter or Tab
|
||||||
|
var index = optList.editableList('indexOf',itemData);
|
||||||
|
var length = optList.editableList('length');
|
||||||
|
if (index + 1 === length) {
|
||||||
|
var newItem = {};
|
||||||
|
optList.editableList('addItem',newItem);
|
||||||
|
setTimeout(function() {
|
||||||
|
if (newItem.label) {
|
||||||
|
newItem.label.focus();
|
||||||
|
}
|
||||||
|
},100)
|
||||||
|
} else {
|
||||||
|
var nextItem = optList.editableList('getItemAt',index+1);
|
||||||
|
if (nextItem.label) {
|
||||||
|
nextItem.label.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
evt.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
sortable: true,
|
||||||
|
removable: true,
|
||||||
|
height: 160
|
||||||
|
})
|
||||||
|
if (ui.opts.opts.length > 0) {
|
||||||
|
ui.opts.opts.forEach(function(o) {
|
||||||
|
optList.editableList('addItem',$.extend(true,{},o))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
optList.editableList('addItem',{})
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
onclose: function() {
|
||||||
|
var items = optList.editableList('items');
|
||||||
|
var vals = [];
|
||||||
|
items.each(function (i,el) {
|
||||||
|
var data = el.data('data');
|
||||||
|
var l = data.label.val().trim();
|
||||||
|
var v = data.input.val();
|
||||||
|
// var t = data.input.typedInput('type');
|
||||||
|
// var v = data.input.typedInput('value');
|
||||||
|
if (l.length > 0) {
|
||||||
|
data.l = data.l || {};
|
||||||
|
data.l[currentLocale] = l;
|
||||||
|
}
|
||||||
|
data.v = v;
|
||||||
|
|
||||||
|
if (l.length > 0 || v.length > 0) {
|
||||||
|
var val = {l:data.l,v:data.v};
|
||||||
|
// if (t !== 'str') {
|
||||||
|
// val.t = t;
|
||||||
|
// }
|
||||||
|
vals.push(val);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ui.opts.opts = vals;
|
||||||
|
inputCellInput.typedInput('value',Date.now())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{value:"checkbox",
|
||||||
|
label:"checkbox", icon:"fa fa-check-square-o",showLabel:false,
|
||||||
|
valueLabel: function(container,value) {
|
||||||
|
container.css("padding",0);
|
||||||
|
checkbox = $('<input type="checkbox">').appendTo(container);
|
||||||
|
checkbox.on('change', function(evt) {
|
||||||
|
valueField.typedInput('value',$(this).prop('checked')?"true":"false");
|
||||||
|
})
|
||||||
|
checkbox.prop('checked',valueField.typedInput('value')==="true");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{value:"spinner",
|
||||||
|
label:"spinner", icon:"fa fa-sort-numeric-asc", showLabel:false,
|
||||||
|
valueLabel: function(container,value) {
|
||||||
|
container.css("padding",0);
|
||||||
|
var innerContainer = $('<div>').css({
|
||||||
|
"background":"white",
|
||||||
|
"height":"100%",
|
||||||
|
"box-sizing": "border-box"
|
||||||
|
}).appendTo(container);
|
||||||
|
|
||||||
|
var input = $('<div class="placeholder-input">').appendTo(innerContainer);
|
||||||
|
$('<span><i class="fa fa-sort-numeric-asc"></i></span>').appendTo(input);
|
||||||
|
|
||||||
|
var min = ui.opts && ui.opts.min;
|
||||||
|
var max = ui.opts && ui.opts.max;
|
||||||
|
var label = "";
|
||||||
|
if (min !== undefined && max !== undefined) {
|
||||||
|
label = Math.min(min,max)+" - "+Math.max(min,max);
|
||||||
|
} else if (min !== undefined) {
|
||||||
|
label = "> "+min;
|
||||||
|
} else if (max !== undefined) {
|
||||||
|
label = "< "+max;
|
||||||
|
}
|
||||||
|
$('<span>').css("margin-left","15px").text(label).appendTo(input);
|
||||||
|
},
|
||||||
|
expand: {
|
||||||
|
icon: "fa-caret-down",
|
||||||
|
content: function(container) {
|
||||||
|
var content = $('<div class="red-ui-editor-subflow-ui-edit-panel">').appendTo(container);
|
||||||
|
content.css("padding","8px 5px")
|
||||||
|
var min = ui.opts.min;
|
||||||
|
var max = ui.opts.max;
|
||||||
|
var minInput = $('<input type="number" style="margin-bottom:0; width:60px">');
|
||||||
|
minInput.val(min);
|
||||||
|
var maxInput = $('<input type="number" style="margin-bottom:0; width:60px">');
|
||||||
|
maxInput.val(max);
|
||||||
|
$('<div class="form-row" style="margin-bottom:3px"><label>Minimum</label></div>').append(minInput).appendTo(content);
|
||||||
|
$('<div class="form-row" style="margin-bottom:0"><label>Maximum</label></div>').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:"none", icon:"fa fa-times",hasValue:false},
|
||||||
|
{value:"hide",
|
||||||
|
label:"hide property", 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 = $('<label>').appendTo(row);
|
||||||
|
var labelContainer = $('<span></span>').appendTo(label);
|
||||||
|
if (ui.icon) {
|
||||||
|
var newPath = RED.utils.separateIconPath(ui.icon);
|
||||||
|
if (newPath) {
|
||||||
|
$("<i class='fa "+newPath.file +"'/>").appendTo(labelContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ui.type !== "checkbox") {
|
||||||
|
$('<span>').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 = $('<input type="text">').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 = $('<select>').css('width','70%').appendTo(row);
|
||||||
|
if (ui.opts.opts) {
|
||||||
|
ui.opts.opts.forEach(function(o) {
|
||||||
|
$('<option>').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 = $('<label>').css('width','70%').appendTo(row);
|
||||||
|
input = $('<input type="checkbox">').css({
|
||||||
|
marginTop: 0,
|
||||||
|
width: 'auto',
|
||||||
|
height: '34px'
|
||||||
|
}).appendTo(cblabel);
|
||||||
|
labelContainer.css({"padding-left":"5px"}).appendTo(cblabel);
|
||||||
|
$('<span>').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 = $('<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 = $("<div/>", { 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(/\s/g,"_");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 = $('<form class="dialog-form form-horizontal"></form>').appendTo(container);
|
||||||
|
var listContainer = $('<div class="form-row node-input-env-container-row"></div>').appendTo(form);
|
||||||
|
var list = $('<ol id="red-ui-editor-subflow-env-list" class="red-ui-editor-subflow-env-list"></ol>').appendTo(listContainer);
|
||||||
|
buildPropertiesList(list, node);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
init: init,
|
init: init,
|
||||||
createSubflow: createSubflow,
|
createSubflow: createSubflow,
|
||||||
@ -755,6 +1708,14 @@ RED.subflow = (function() {
|
|||||||
refresh: refresh,
|
refresh: refresh,
|
||||||
removeInput: removeSubflowInput,
|
removeInput: removeSubflowInput,
|
||||||
removeOutput: removeSubflowOutput,
|
removeOutput: removeSubflowOutput,
|
||||||
removeStatus: removeSubflowStatus
|
removeStatus: removeSubflowStatus,
|
||||||
|
|
||||||
|
|
||||||
|
buildEditForm: buildEditForm,
|
||||||
|
buildPropertiesForm: buildPropertiesForm,
|
||||||
|
|
||||||
|
exportSubflowTemplateEnv: exportEnvList,
|
||||||
|
exportSubflowInstanceEnv: exportSubflowInstanceEnv
|
||||||
|
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
@ -60,10 +60,12 @@
|
|||||||
.red-ui-icon-picker {
|
.red-ui-icon-picker {
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
color: $primary-text-color;
|
||||||
}
|
}
|
||||||
a:hover,
|
a:hover,
|
||||||
a:focus {
|
a:focus {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
color: $primary-text-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
|
@ -79,6 +79,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
img {
|
||||||
|
max-width: 14px;
|
||||||
|
}
|
||||||
.fa {
|
.fa {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
margin-left: -25px;
|
margin-left: -25px;
|
||||||
|
@ -190,6 +190,10 @@ button.red-ui-tray-resize-button {
|
|||||||
border-color: $form-input-border-error-color !important;
|
border-color: $form-input-border-error-color !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input-updated {
|
||||||
|
border-color: $node-selected-color !important;
|
||||||
|
}
|
||||||
|
|
||||||
.form-row {
|
.form-row {
|
||||||
clear: both;
|
clear: both;
|
||||||
color: $form-text-color;
|
color: $form-text-color;
|
||||||
@ -421,6 +425,15 @@ button.red-ui-button-small
|
|||||||
height: 200px;
|
height: 200px;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
line-height: 0px;
|
line-height: 0px;
|
||||||
|
&.red-ui-icon-list-dark {
|
||||||
|
.red-ui-palette-icon-fa {
|
||||||
|
color: $secondary-text-color;
|
||||||
|
}
|
||||||
|
.red-ui-palette-icon-container {
|
||||||
|
background: $secondary-background;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.red-ui-icon-list-icon {
|
.red-ui-icon-list-icon {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@ -428,6 +441,7 @@ button.red-ui-button-small
|
|||||||
padding: 4px;
|
padding: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: $list-item-background-hover;
|
background: $list-item-background-hover;
|
||||||
}
|
}
|
||||||
@ -579,3 +593,406 @@ button.red-ui-button-small
|
|||||||
button.red-ui-toggleButton.toggle {
|
button.red-ui-toggleButton.toggle {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.red-ui-editor-subflow-env-ui-row {
|
||||||
|
margin-right: 3px;
|
||||||
|
>div {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 16px 40px 35% auto;
|
||||||
|
}
|
||||||
|
>div:first-child {
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: $tertiary-text-color;
|
||||||
|
margin: 3px 0 -4px;
|
||||||
|
>div {
|
||||||
|
padding-left: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>div:last-child {
|
||||||
|
>div {
|
||||||
|
height: 40px;
|
||||||
|
line-height: 30px;
|
||||||
|
display: inline-block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
// border-left: 2px dashed $secondary-border-color;
|
||||||
|
// border-bottom: 2px dashed $secondary-border-color;
|
||||||
|
// border: 1px dashed $secondary-border-color;
|
||||||
|
border-right: none;
|
||||||
|
&:not(:first-child) {
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
// &:last-child {
|
||||||
|
// border-right: 1px dashed $secondary-border-color;
|
||||||
|
// }
|
||||||
|
.placeholder-input {
|
||||||
|
position: relative;
|
||||||
|
padding: 0 3px;
|
||||||
|
line-height: 24px;
|
||||||
|
opacity: 0.8
|
||||||
|
}
|
||||||
|
.red-ui-typedInput-value-label,.red-ui-typedInput-option-label {
|
||||||
|
select,.placeholder-input {
|
||||||
|
margin: 3px;
|
||||||
|
height: 26px;
|
||||||
|
width: calc(100% - 10px);
|
||||||
|
padding-left: 3px;
|
||||||
|
}
|
||||||
|
.placeholder-input {
|
||||||
|
span:first-child {
|
||||||
|
display:inline-block;
|
||||||
|
height: 100%;
|
||||||
|
width: 20px;
|
||||||
|
text-align:center;
|
||||||
|
border-right: 1px solid $secondary-border-color;
|
||||||
|
background: $tertiary-background;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input[type="checkbox"] {
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-top: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>div:nth-child(1) {
|
||||||
|
border: none;
|
||||||
|
padding: 2px;
|
||||||
|
.red-ui-editableList-item-handle {
|
||||||
|
position:relative;
|
||||||
|
top: 0px;
|
||||||
|
color: $tertiary-text-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>div:nth-child(2) {
|
||||||
|
margin: 4px;
|
||||||
|
height: 32px;
|
||||||
|
border: 1px dashed $secondary-border-color;
|
||||||
|
text-align: center;
|
||||||
|
a {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
line-height: 32px;
|
||||||
|
&:hover {
|
||||||
|
background: $secondary-background-hover;
|
||||||
|
}
|
||||||
|
i {
|
||||||
|
height: 100%;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>div:nth-child(3) {
|
||||||
|
position: relative;
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
span.red-ui-editor-subflow-env-lang-icon {
|
||||||
|
position: absolute;
|
||||||
|
display: inline-block;
|
||||||
|
background: $secondary-background;
|
||||||
|
opacity: 0.8;
|
||||||
|
width: 20px;
|
||||||
|
line-height: 32px;
|
||||||
|
height: 32px;
|
||||||
|
text-align: center;
|
||||||
|
top: 4px;
|
||||||
|
right: 4px;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
|
||||||
|
}
|
||||||
|
// .red-ui-editor-subflow-ui-grid {
|
||||||
|
// width: 100%;
|
||||||
|
// .red-ui-editableList-container {
|
||||||
|
// border: none;
|
||||||
|
// border-radius: 0;
|
||||||
|
// }
|
||||||
|
// .red-ui-editableList-container li {
|
||||||
|
// border: none;
|
||||||
|
// padding: 0;
|
||||||
|
// &:not(:first-child) .red-ui-editableList-item-content >div:first-child >div {
|
||||||
|
// border-top: none;
|
||||||
|
// }
|
||||||
|
// &.ui-sortable-helper {
|
||||||
|
// border: 2px dashed $secondary-border-color;
|
||||||
|
// .red-ui-editableList-item-content {
|
||||||
|
// >div {
|
||||||
|
// border: none;
|
||||||
|
// opacity: 0.7
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// .red-ui-editableList-item-content {
|
||||||
|
// >div>div {
|
||||||
|
// display: inline-block;
|
||||||
|
// box-sizing: border-box;
|
||||||
|
// border-left: 1px dashed $secondary-border-color;
|
||||||
|
// border-bottom: 1px dashed $secondary-border-color;
|
||||||
|
// }
|
||||||
|
// >div:first-child {
|
||||||
|
// font-size: 0.9em;
|
||||||
|
// display: grid;
|
||||||
|
// grid-template-columns: 25px auto 20px;
|
||||||
|
// >div {
|
||||||
|
// border-top: 1px dashed $secondary-border-color;
|
||||||
|
// padding: 1px;
|
||||||
|
// }
|
||||||
|
// >div:nth-child(3) {
|
||||||
|
// border-top: none;
|
||||||
|
// border-bottom: none;
|
||||||
|
// // width: 20px;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// >div:last-child {
|
||||||
|
// display: grid;
|
||||||
|
// grid-template-columns: 25px 140px auto 20px;
|
||||||
|
// >div {
|
||||||
|
// height: 48px;
|
||||||
|
// line-height: 30px;
|
||||||
|
// // display: inline-block;
|
||||||
|
// // height: 48px;
|
||||||
|
// // line-height: 30px;
|
||||||
|
// // box-sizing: border-box;
|
||||||
|
// //
|
||||||
|
// // border-left: 2px dashed $secondary-border-color;
|
||||||
|
// border-top: none;
|
||||||
|
// // border-bottom: 2px dashed $secondary-border-color;
|
||||||
|
// &:not(:first-child) {
|
||||||
|
// padding: 6px 3px;
|
||||||
|
// }
|
||||||
|
// .placeholder-input {
|
||||||
|
// position: relative;
|
||||||
|
// padding: 0 3px;
|
||||||
|
// line-height: 24px;
|
||||||
|
// opacity: 0.8
|
||||||
|
// }
|
||||||
|
// .red-ui-typedInput-value-label,.red-ui-typedInput-option-label {
|
||||||
|
// select,.placeholder-input {
|
||||||
|
// margin: 3px;
|
||||||
|
// height: 26px;
|
||||||
|
// width: calc(100% - 10px);
|
||||||
|
// padding-left: 3px;
|
||||||
|
// }
|
||||||
|
// input[type="checkbox"] {
|
||||||
|
// margin-left: 8px;
|
||||||
|
// margin-top: 0;
|
||||||
|
// height: 100%;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// >div:nth-child(1) {
|
||||||
|
// text-align: center;
|
||||||
|
// a {
|
||||||
|
// display: block;
|
||||||
|
// width: 100%;
|
||||||
|
// height: 100%;
|
||||||
|
// line-height: 45px;
|
||||||
|
// &:hover {
|
||||||
|
// background: $secondary-background-hover;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// >div:nth-child(2) {
|
||||||
|
// input {
|
||||||
|
// width: 100%;
|
||||||
|
// }
|
||||||
|
// // width: 140px;
|
||||||
|
// }
|
||||||
|
// >div:nth-child(3) {
|
||||||
|
// position: relative;
|
||||||
|
// .options-button {
|
||||||
|
// position: absolute;
|
||||||
|
// top: calc(50% - 10px);
|
||||||
|
// margin-right: 2px;
|
||||||
|
// right: 2px;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// >div:nth-child(4) {
|
||||||
|
// border-top: none;
|
||||||
|
// border-bottom: none;
|
||||||
|
// // width: 20px;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
.red-ui-editor-subflow-ui-edit-panel {
|
||||||
|
padding-bottom: 3px;
|
||||||
|
background: $primary-background;
|
||||||
|
.red-ui-editableList-border {
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
border-bottom: 1px solid $secondary-border-color;
|
||||||
|
}
|
||||||
|
.red-ui-editableList-container {
|
||||||
|
}
|
||||||
|
.red-ui-editableList-addButton {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
.red-ui-editableList-header {
|
||||||
|
background: $primary-background;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 50% 50%;
|
||||||
|
color: $secondary-text-color;
|
||||||
|
div:first-child {
|
||||||
|
padding-left: 23px;
|
||||||
|
}
|
||||||
|
div:last-child {
|
||||||
|
padding-left: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.red-ui-editableList-container {
|
||||||
|
padding: 0 1px;
|
||||||
|
li {
|
||||||
|
background: $secondary-background;
|
||||||
|
// border-bottom: none;
|
||||||
|
padding: 0;
|
||||||
|
.red-ui-editableList-item-content {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 50% 50%;
|
||||||
|
>div {
|
||||||
|
position:relative;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
margin-bottom: 0;
|
||||||
|
border:none;
|
||||||
|
width: 100%;
|
||||||
|
border-right: 1px solid $secondary-border-color;
|
||||||
|
|
||||||
|
border-radius: 0;
|
||||||
|
&:focus {
|
||||||
|
box-shadow: 0 0 0 1px inset $form-input-focus-color;
|
||||||
|
}
|
||||||
|
&:first-child {
|
||||||
|
border-left: 1px solid $secondary-border-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button.red-ui-typedInput-type-select, button.red-ui-typedInput-option-expand, button.red-ui-typedInput-option-trigger {
|
||||||
|
border-radius: 0;
|
||||||
|
height: 34px;
|
||||||
|
}
|
||||||
|
.red-ui-typedInput-container {
|
||||||
|
border-radius: 0;
|
||||||
|
border: none;
|
||||||
|
input.red-ui-typedInput-input {
|
||||||
|
height: 34px;
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.red-ui-editor-subflow-env-lang-icon {
|
||||||
|
top: 1px;
|
||||||
|
right: 1px;
|
||||||
|
border-top-right-radius:0;
|
||||||
|
border-bottom-right-radius:0;
|
||||||
|
}
|
||||||
|
.red-ui-editableList-item-remove {
|
||||||
|
right: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-input-env-locales-row {
|
||||||
|
position: relative;
|
||||||
|
top: -20px;
|
||||||
|
float: right;
|
||||||
|
select {
|
||||||
|
width: 160px;
|
||||||
|
height: 20px;
|
||||||
|
min-width: 20px;
|
||||||
|
line-height: 18px;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.node-input-env-container-row {
|
||||||
|
min-width: 470px;
|
||||||
|
position: relative;
|
||||||
|
.red-ui-editableList-item-content {
|
||||||
|
label {
|
||||||
|
margin-bottom: 0;
|
||||||
|
line-height: 32px;
|
||||||
|
span {
|
||||||
|
display: inline-block;
|
||||||
|
width: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>div:first-child {
|
||||||
|
display: grid;
|
||||||
|
padding-left: 5px;
|
||||||
|
grid-template-columns: 40% auto 37px;
|
||||||
|
> :first-child {
|
||||||
|
width: calc(100% - 5px);
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
width: calc(100% - 5px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.red-ui-editor-subflow-env-editable {
|
||||||
|
>div:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
grid-template-columns: 24px 40% auto 37px;
|
||||||
|
> a:first-child {
|
||||||
|
text-align: center;
|
||||||
|
line-height: 32px;
|
||||||
|
i.fa-angle-right {
|
||||||
|
transition: all 0.2s linear;
|
||||||
|
}
|
||||||
|
&.expanded {
|
||||||
|
i.fa-angle-right {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.red-ui-editableList-border .red-ui-editableList-header {
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
|
||||||
|
background: $tertiary-background;
|
||||||
|
padding: 0;
|
||||||
|
>div {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 24px 40% auto 37px;
|
||||||
|
>div {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.red-ui-editableList-container {
|
||||||
|
padding: 0;
|
||||||
|
.red-ui-editableList-item-handle {
|
||||||
|
top: 25px;
|
||||||
|
}
|
||||||
|
.red-ui-editableList-item-remove {
|
||||||
|
top: 25px;
|
||||||
|
right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#subflow-input-ui {
|
||||||
|
// .form-row {
|
||||||
|
// display: grid;
|
||||||
|
// grid-template-columns: 120px auto;
|
||||||
|
// label span {
|
||||||
|
// display: inline-block;
|
||||||
|
// width: 20px;
|
||||||
|
// text-align: center;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
@ -150,7 +150,8 @@
|
|||||||
input[type="tel"],
|
input[type="tel"],
|
||||||
input[type="color"],
|
input[type="color"],
|
||||||
div[contenteditable="true"],
|
div[contenteditable="true"],
|
||||||
.uneditable-input {
|
.uneditable-input,
|
||||||
|
.placeholder-input {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 34px;
|
height: 34px;
|
||||||
@ -190,7 +191,8 @@
|
|||||||
input[type="tel"],
|
input[type="tel"],
|
||||||
input[type="color"],
|
input[type="color"],
|
||||||
div[contenteditable="true"],
|
div[contenteditable="true"],
|
||||||
.uneditable-input {
|
.uneditable-input,
|
||||||
|
.placeholder-input {
|
||||||
background-color: $form-input-background;
|
background-color: $form-input-background;
|
||||||
border: 1px solid $form-input-border-color;
|
border: 1px solid $form-input-border-color;
|
||||||
}
|
}
|
||||||
|
@ -192,6 +192,7 @@
|
|||||||
color: $header-menu-color;
|
color: $header-menu-color;
|
||||||
padding: 3px 40px;
|
padding: 3px 40px;
|
||||||
img {
|
img {
|
||||||
|
max-width: 100%;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
border: 3px solid transparent;
|
border: 3px solid transparent;
|
||||||
|
@ -162,3 +162,15 @@
|
|||||||
background: none;
|
background: none;
|
||||||
color: $tertiary-text-color;
|
color: $tertiary-text-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.red-ui-popover-panel {
|
||||||
|
@include component-shadow;
|
||||||
|
font-family: $primary-font;
|
||||||
|
font-size: $primary-font-size;
|
||||||
|
position: absolute;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid $primary-border-color;
|
||||||
|
background: $secondary-background;
|
||||||
|
z-index: 2000;
|
||||||
|
}
|
||||||
|
@ -58,118 +58,6 @@
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
}
|
}
|
||||||
button.red-ui-typedInput-type-select,
|
|
||||||
button.red-ui-typedInput-option-expand,
|
|
||||||
button.red-ui-typedInput-option-trigger
|
|
||||||
{
|
|
||||||
text-align: left;
|
|
||||||
border: none;
|
|
||||||
position: absolute;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border-top-left-radius: 4px;
|
|
||||||
border-bottom-left-radius: 4px;
|
|
||||||
padding: 0 1px 0 5px;
|
|
||||||
display:inline-block;
|
|
||||||
background: $form-button-background;
|
|
||||||
height: 32px;
|
|
||||||
line-height: 32px;
|
|
||||||
vertical-align: middle;
|
|
||||||
color: $form-text-color;
|
|
||||||
i.red-ui-typedInput-icon {
|
|
||||||
margin-left: 1px;
|
|
||||||
margin-right: 2px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
&.disabled {
|
|
||||||
cursor: default;
|
|
||||||
i.red-ui-typedInput-icon {
|
|
||||||
color: $secondary-text-color-disabled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.red-ui-typedInput-type-label,.red-ui-typedInput-option-label {
|
|
||||||
display: inline-block;
|
|
||||||
height: 100%;
|
|
||||||
padding: 0 1px 0 5px;
|
|
||||||
img {
|
|
||||||
max-width: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.disabled):hover {
|
|
||||||
text-decoration: none;
|
|
||||||
background: $workspace-button-background-hover;
|
|
||||||
}
|
|
||||||
&:focus {
|
|
||||||
text-decoration: none;
|
|
||||||
outline: none;
|
|
||||||
box-shadow: inset 0 0 0 1px $form-input-focus-color;
|
|
||||||
}
|
|
||||||
&:not(.disabled):active {
|
|
||||||
background: $workspace-button-background-active;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
&.red-ui-typedInput-full-width {
|
|
||||||
width: 100%;
|
|
||||||
border-top-right-radius: 4px;
|
|
||||||
border-bottom-right-radius: 4px;
|
|
||||||
}
|
|
||||||
&:before {
|
|
||||||
content:'';
|
|
||||||
display: inline-block;
|
|
||||||
height: 100%;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
button.red-ui-typedInput-option-expand {
|
|
||||||
border-top-right-radius: 4px;
|
|
||||||
border-bottom-right-radius: 4px;
|
|
||||||
border-top-left-radius: 0;
|
|
||||||
border-bottom-left-radius: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.red-ui-typedInput-option-trigger {
|
|
||||||
border-top-left-radius: 0px;
|
|
||||||
border-bottom-left-radius: 0px;
|
|
||||||
border-top-right-radius: 4px;
|
|
||||||
border-bottom-right-radius: 4px;
|
|
||||||
padding: 0 0 0 0;
|
|
||||||
position:absolute;
|
|
||||||
right: 0;
|
|
||||||
.red-ui-typedInput-option-label {
|
|
||||||
background:$form-button-background;
|
|
||||||
color: $form-text-color;
|
|
||||||
position:absolute;
|
|
||||||
left:0;
|
|
||||||
right:23px;
|
|
||||||
top: 0;
|
|
||||||
padding: 0 5px 0 8px;
|
|
||||||
i.red-ui-typedInput-icon {
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.red-ui-typedInput-option-caret {
|
|
||||||
top: 0;
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
width: 17px;
|
|
||||||
padding-left: 6px;
|
|
||||||
&:before {
|
|
||||||
content:'';
|
|
||||||
display: inline-block;
|
|
||||||
height: 100%;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:focus {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
&:focus .red-ui-typedInput-option-caret {
|
|
||||||
box-shadow: inset 0 0 0 1px $form-input-focus-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.red-ui-typedInput-options {
|
.red-ui-typedInput-options {
|
||||||
@include component-shadow;
|
@include component-shadow;
|
||||||
@ -180,6 +68,7 @@
|
|||||||
max-height: 350px;
|
max-height: 350px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
border: 1px solid $primary-border-color;
|
border: 1px solid $primary-border-color;
|
||||||
|
box-sizing: border-box;
|
||||||
background: $secondary-background;
|
background: $secondary-background;
|
||||||
z-index: 2000;
|
z-index: 2000;
|
||||||
a {
|
a {
|
||||||
@ -200,8 +89,124 @@
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
background: $workspace-button-background-active;
|
background: $workspace-button-background-active;
|
||||||
}
|
}
|
||||||
|
input[type="checkbox"] {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.red-ui-typedInput-icon {
|
.red-ui-typedInput-icon {
|
||||||
margin-right: 4px;
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button.red-ui-typedInput-type-select,
|
||||||
|
button.red-ui-typedInput-option-expand,
|
||||||
|
button.red-ui-typedInput-option-trigger
|
||||||
|
{
|
||||||
|
text-align: left;
|
||||||
|
border: none;
|
||||||
|
position: absolute;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
padding: 0 1px 0 5px;
|
||||||
|
display:inline-block;
|
||||||
|
background: $form-button-background;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 30px;
|
||||||
|
min-width: 23px;
|
||||||
|
vertical-align: middle;
|
||||||
|
color: $form-text-color;
|
||||||
|
i.red-ui-typedInput-icon {
|
||||||
|
margin-left: 1px;
|
||||||
|
margin-right: 2px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
&.disabled {
|
||||||
|
cursor: default;
|
||||||
|
i.red-ui-typedInput-icon {
|
||||||
|
color: $secondary-text-color-disabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.red-ui-typedInput-type-label,.red-ui-typedInput-option-label {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 1px 0 5px;
|
||||||
|
img {
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.disabled):hover {
|
||||||
|
text-decoration: none;
|
||||||
|
background: $workspace-button-background-hover;
|
||||||
|
}
|
||||||
|
&:focus {
|
||||||
|
text-decoration: none;
|
||||||
|
outline: none;
|
||||||
|
box-shadow: inset 0 0 0 1px $form-input-focus-color;
|
||||||
|
}
|
||||||
|
&:not(.disabled):active {
|
||||||
|
background: $workspace-button-background-active;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
&.red-ui-typedInput-full-width {
|
||||||
|
width: 100%;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
}
|
||||||
|
&:before {
|
||||||
|
content:'';
|
||||||
|
display: inline-block;
|
||||||
|
height: 100%;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button.red-ui-typedInput-option-expand {
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.red-ui-typedInput-option-trigger {
|
||||||
|
border-top-left-radius: 0px;
|
||||||
|
border-bottom-left-radius: 0px;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
padding: 0 0 0 0;
|
||||||
|
position:absolute;
|
||||||
|
right: 0;
|
||||||
|
.red-ui-typedInput-option-label {
|
||||||
|
background:$form-button-background;
|
||||||
|
color: $form-text-color;
|
||||||
|
position:absolute;
|
||||||
|
left:0;
|
||||||
|
right:23px;
|
||||||
|
top: 0;
|
||||||
|
padding: 0 5px 0 8px;
|
||||||
|
i.red-ui-typedInput-icon {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.red-ui-typedInput-option-caret {
|
||||||
|
top: 0;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 17px;
|
||||||
|
padding-left: 5px;
|
||||||
|
&:before {
|
||||||
|
content:'';
|
||||||
|
display: inline-block;
|
||||||
|
height: 100%;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:focus {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
&:focus .red-ui-typedInput-option-caret {
|
||||||
|
box-shadow: inset 0 0 0 1px $form-input-focus-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,54 @@ const flowUtil = require("./util");
|
|||||||
|
|
||||||
var Log;
|
var Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create deep copy of object
|
||||||
|
*/
|
||||||
|
function deepCopy(obj) {
|
||||||
|
return JSON.parse(JSON.stringify(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluate Input Value
|
||||||
|
*/
|
||||||
|
function evaluateInputValue(value, type, node) {
|
||||||
|
if (type === "bool") {
|
||||||
|
return (value === "true") || (value === true);
|
||||||
|
}
|
||||||
|
return redUtil.evaluateNodeProperty(value, type, node, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compose information object for env var
|
||||||
|
*/
|
||||||
|
function composeInfo(info, val) {
|
||||||
|
var result = {
|
||||||
|
name: info.name,
|
||||||
|
type: info.type,
|
||||||
|
value: val,
|
||||||
|
};
|
||||||
|
if (info.ui) {
|
||||||
|
var ui = info.ui;
|
||||||
|
result.ui = {
|
||||||
|
hasUI: ui.hasUI,
|
||||||
|
icon: ui.icon,
|
||||||
|
labels: ui.labels,
|
||||||
|
type: ui.type
|
||||||
|
};
|
||||||
|
var retUI = result.ui;
|
||||||
|
if (ui.type === "input") {
|
||||||
|
retUI.inputTypes = ui.inputTypes;
|
||||||
|
}
|
||||||
|
if (ui.type === "select") {
|
||||||
|
retUI.menu = ui.menu;
|
||||||
|
}
|
||||||
|
if (ui.type === "spinner") {
|
||||||
|
retUI.spinner = ui.spinner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a subflow - which is handled as a special type of Flow
|
* This class represents a subflow - which is handled as a special type of Flow
|
||||||
@ -95,10 +143,19 @@ class Subflow extends Flow {
|
|||||||
|
|
||||||
var env = [];
|
var env = [];
|
||||||
if (this.subflowDef.env) {
|
if (this.subflowDef.env) {
|
||||||
this.subflowDef.env.forEach(e => { env[e.name] = e; });
|
this.subflowDef.env.forEach(e => {
|
||||||
|
env[e.name] = e;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (this.subflowInstance.env) {
|
if (this.subflowInstance.env) {
|
||||||
this.subflowInstance.env.forEach(e => { env[e.name] = e; });
|
this.subflowInstance.env.forEach(e => {
|
||||||
|
var old = env[e.name];
|
||||||
|
var ui = old ? old.ui : null;
|
||||||
|
env[e.name] = e;
|
||||||
|
if (ui) {
|
||||||
|
env[e.name].ui = ui;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
this.env = env;
|
this.env = env;
|
||||||
}
|
}
|
||||||
@ -257,6 +314,7 @@ class Subflow extends Flow {
|
|||||||
super.start(diff);
|
super.start(diff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get environment variable of subflow
|
* Get environment variable of subflow
|
||||||
* @param {String} name name of env var
|
* @param {String} name name of env var
|
||||||
@ -266,8 +324,16 @@ class Subflow extends Flow {
|
|||||||
this.trace("getSetting:"+name);
|
this.trace("getSetting:"+name);
|
||||||
if (!/^\$parent\./.test(name)) {
|
if (!/^\$parent\./.test(name)) {
|
||||||
var env = this.env;
|
var env = this.env;
|
||||||
if (env && env.hasOwnProperty(name)) {
|
var is_info = name.endsWith("_info");
|
||||||
var val = env[name];
|
var is_type = name.endsWith("_type");
|
||||||
|
var ename = (is_info || is_type) ? name.substring(0, name.length -5) : name; // 5 = length of "_info"/"_type"
|
||||||
|
|
||||||
|
if (env && env.hasOwnProperty(ename)) {
|
||||||
|
var val = env[ename];
|
||||||
|
|
||||||
|
if (is_type) {
|
||||||
|
return val ? val.type : undefined;
|
||||||
|
}
|
||||||
// If this is an env type property we need to be careful not
|
// If this is an env type property we need to be careful not
|
||||||
// to get into lookup loops.
|
// to get into lookup loops.
|
||||||
// 1. if the value to lookup is the same as this one, go straight to parent
|
// 1. if the value to lookup is the same as this one, go straight to parent
|
||||||
@ -276,11 +342,15 @@ class Subflow extends Flow {
|
|||||||
// See https://github.com/node-red/node-red/issues/2099
|
// See https://github.com/node-red/node-red/issues/2099
|
||||||
if (val.type !== 'env' || val.value !== name) {
|
if (val.type !== 'env' || val.value !== name) {
|
||||||
let value = val.value;
|
let value = val.value;
|
||||||
if (val.type === 'env') {
|
var type = val.type;
|
||||||
|
if (type === 'env') {
|
||||||
value = value.replace(new RegExp("\\${"+name+"}","g"),"${$parent."+name+"}");
|
value = value.replace(new RegExp("\\${"+name+"}","g"),"${$parent."+name+"}");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
var ret = redUtil.evaluateNodeProperty(value, val.type, this.node, null, null);
|
var ret = evaluateInputValue(value, type, this.node);
|
||||||
|
if (is_info) {
|
||||||
|
return composeInfo(val, ret);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
|
@ -447,4 +447,117 @@ describe('subflow', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should access env var type of subflow instance', function(done) {
|
||||||
|
var flow = [
|
||||||
|
{id:"t0", type:"tab", label:"", disabled:false, info:""},
|
||||||
|
{id:"n1", x:10, y:10, z:"t0", type:"subflow:s1",
|
||||||
|
env: [
|
||||||
|
{name: "K", type: "str", value: "V"}
|
||||||
|
],
|
||||||
|
wires:[["n2"]]},
|
||||||
|
{id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]},
|
||||||
|
// Subflow
|
||||||
|
{id:"s1", type:"subflow", name:"Subflow", info:"",
|
||||||
|
in:[{
|
||||||
|
x:10, y:10,
|
||||||
|
wires:[ {id:"s1-n1"} ]
|
||||||
|
}],
|
||||||
|
out:[{
|
||||||
|
x:10, y:10,
|
||||||
|
wires:[ {id:"s1-n1", port:0} ]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{id:"s1-n1", x:10, y:10, z:"s1", type:"function",
|
||||||
|
func:"msg.V = env.get('K_type'); return msg;",
|
||||||
|
wires:[]}
|
||||||
|
];
|
||||||
|
helper.load(functionNode, flow, function() {
|
||||||
|
var n1 = helper.getNode("n1");
|
||||||
|
var n2 = helper.getNode("n2");
|
||||||
|
n2.on("input", function(msg) {
|
||||||
|
try {
|
||||||
|
msg.should.have.property("V", "str");
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
done(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
n1.receive({payload:"foo"});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should access env var info of subflow instance', function(done) {
|
||||||
|
var flow = [
|
||||||
|
{id:"t0", type:"tab", label:"", disabled:false, info:""},
|
||||||
|
{id:"n1", x:10, y:10, z:"t0", type:"subflow:s1",
|
||||||
|
env: [
|
||||||
|
{name: "K", type: "str", value: "V"}
|
||||||
|
],
|
||||||
|
wires:[["n2"]]},
|
||||||
|
{id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]},
|
||||||
|
// Subflow
|
||||||
|
{id:"s1", type:"subflow", name:"Subflow", info:"",
|
||||||
|
in:[{
|
||||||
|
x:10, y:10,
|
||||||
|
wires:[ {id:"s1-n1"} ]
|
||||||
|
}],
|
||||||
|
out:[{
|
||||||
|
x:10, y:10,
|
||||||
|
wires:[ {id:"s1-n1", port:0} ]
|
||||||
|
}],
|
||||||
|
env:[
|
||||||
|
{
|
||||||
|
name: "K", type: "str", value: "",
|
||||||
|
ui: {
|
||||||
|
hasUI: true,
|
||||||
|
icon: "icon",
|
||||||
|
labels: {
|
||||||
|
"en-US": "label"
|
||||||
|
},
|
||||||
|
type: "input",
|
||||||
|
inputTypes: {
|
||||||
|
str: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{id:"s1-n1", x:10, y:10, z:"s1", type:"function",
|
||||||
|
func:"msg.V = env.get('K_info'); return msg;",
|
||||||
|
wires:[]}
|
||||||
|
];
|
||||||
|
helper.load(functionNode, flow, function() {
|
||||||
|
var n1 = helper.getNode("n1");
|
||||||
|
var n2 = helper.getNode("n2");
|
||||||
|
n2.on("input", function(msg) {
|
||||||
|
try {
|
||||||
|
msg.should.have.property("V");
|
||||||
|
var v = msg.V;
|
||||||
|
v.should.have.property("name", "K");
|
||||||
|
v.should.have.property("value", "V");
|
||||||
|
v.should.have.property("type", "str");
|
||||||
|
v.should.have.property("ui");
|
||||||
|
var ui = v.ui;
|
||||||
|
ui.should.have.property("hasUI", true);
|
||||||
|
ui.should.have.property("icon", "icon");
|
||||||
|
ui.should.have.property("type", "input");
|
||||||
|
ui.should.have.property("labels");
|
||||||
|
var labels = ui.labels;
|
||||||
|
labels.should.have.property("en-US", "label");
|
||||||
|
ui.should.have.property("inputTypes");
|
||||||
|
var types = ui.inputTypes;
|
||||||
|
types.should.have.property("str", true);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
done(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
n1.receive({payload:"foo"});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user