node-red/packages/node_modules/@node-red/nodes/core/common/60-link.html

386 lines
14 KiB
HTML

<script type="text/html" data-template-name="link in">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
</div>
<div style="position:relative; height: 30px; text-align: right;"><div style="display:inline-block"><input type="text" id="node-input-link-target-filter"></div></div>
<div class="form-row node-input-link-row"></div>
</script>
<script type="text/html" data-template-name="link out">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
</div>
<div class="form-row">
<label for="node-input-mode"><span data-i18n="link.outMode"></span></label>
<select id="node-input-mode" style="width: 70%">
<option value="link" selected data-i18n="link.sendToAll"></option>
<option value="return" data-i18n="link.returnToCaller"></option>
</select>
</div>
<div class="node-input-link-rows" style="position:relative; height: 30px; text-align: right;"><div style="display:inline-block"><input type="text" id="node-input-link-target-filter"></div></div>
<div class="form-row node-input-link-row node-input-link-rows"></div>
</script>
<script type="text/html" data-template-name="link call">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
</div>
<div class="form-row">
<label for="node-input-timeout"><span data-i18n="exec.label.timeout"></span></label>
<input type="text" id="node-input-timeout" placeholder="30" style="width: 70px; margin-right: 5px;"><span data-i18n="inject.seconds"></span>
</div>
<div class="form-row">
<label for="node-input-linkType" data-i18n="link.linkCallType"></label>
<select id="node-input-linkType" style="width: 70%">
<option value="static" data-i18n="link.staticLinkCall"></option>
<option value="dynamic" data-i18n="link.dynamicLinkCall"></option>
</select>
</div>
<div class="link-call-target-tree" style="position:relative; height: 30px; text-align: right;">
<div style="display:inline-block"><input type="text" id="node-input-link-target-filter"></div>
</div>
<div class="form-row node-input-link-row link-call-target-tree"></div>
</script>
<script type="text/javascript">
(function() {
let treeList;
function onEditPrepare(node,targetType) {
if (!node.links) {
node.links = [];
}
node.oldLinks = [];
const activeSubflow = RED.nodes.subflow(node.z);
treeList = $("<div>")
.css({width: "100%", height: "100%"})
.appendTo(".node-input-link-row")
.treeList({autoSelect:false})
.on('treelistitemmouseover',function(e,item) {
if (item.node) {
item.node.highlighted = true;
item.node.dirty = true;
RED.view.redraw();
}
})
.on('treelistitemmouseout',function(e,item) {
if (item.node) {
item.node.highlighted = false;
item.node.dirty = true;
RED.view.redraw();
}
});
const candidateNodes = RED.nodes.filterNodes({type:targetType});
let candidateNodesCount = 0;
const search = $("#node-input-link-target-filter").searchBox({
style: "compact",
delay: 300,
change: function() {
var val = $(this).val().trim().toLowerCase();
if (val === "") {
treeList.treeList("filter", null);
search.searchBox("count","");
} else {
const count = treeList.treeList("filter", function(item) {
return item.label.toLowerCase().indexOf(val) > -1 || (item.node && item.node.type.toLowerCase().indexOf(val) > -1)
});
search.searchBox("count",count+" / "+candidateNodesCount);
}
}
});
const flows = [];
const flowMap = {};
if (activeSubflow) {
flowMap[activeSubflow.id] = {
id: activeSubflow.id,
class: 'red-ui-palette-header',
label: "Subflow : " + (activeSubflow.name || activeSubflow.id),
expanded: true,
children: []
};
flows.push(flowMap[activeSubflow.id])
}
if (!activeSubflow || node.type === "link call") {
// Only "Link Call" can look outside of its own subflow
// Link In and Link Out nodes outside of a subflow should be ignored
RED.nodes.eachWorkspace(function (ws) {
flowMap[ws.id] = {
id: ws.id,
class: 'red-ui-palette-header',
label: (ws.label || ws.id) + (node.z === ws.id ? " *" : ""),
expanded: true,
children: []
}
flows.push(flowMap[ws.id])
})
}
candidateNodes.forEach(function (n) {
if (flowMap[n.z]) {
if (targetType === "link out" && n.mode === 'return') {
// Link In nodes looking for Link Out nodes should not
// include return-mode nodes.
return;
}
const isChecked = (node.links.indexOf(n.id) !== -1) || (n.links || []).indexOf(node.id) !== -1;
if (isChecked) {
node.oldLinks.push(n.id);
}
flowMap[n.z].children.push({
id: n.id,
node: n,
label: n.name || n.id,
selected: isChecked,
checkbox: node.type !== "link call",
radio: node.type === "link call"
})
candidateNodesCount++;
}
});
const flowsFiltered = flows.filter(function(f) { return f.children.length > 0 })
treeList.treeList('data',flowsFiltered);
setTimeout(function() {
treeList.treeList('show',node.z);
},100);
}
function resizeNodeList() {
var rows = $("#dialog-form>div:not(.node-input-link-row)");
var height = $("#dialog-form").height();
for (var i=0;i<rows.length;i++) {
height -= $(rows[i]).outerHeight(true);
}
var editorRow = $("#dialog-form>div.node-input-link-row");
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
$(".node-input-link-row").css("height",height+"px");
}
function onEditSave(node) {
var flows = treeList.treeList('data');
node.links = [];
if (node.type !== "link out" || $("#node-input-mode").val() === 'link') {
flows.forEach(function(f) {
f.children.forEach(function(n) {
if (n.selected) {
node.links.push(n.id);
}
})
})
}
node.oldLinks.sort();
node.links.sort();
if (node.type === "link call") {
return
}
var nodeMap = {};
var length = Math.max(node.oldLinks.length,node.links.length);
for (var i=0;i<length;i++) {
if (i<node.oldLinks.length) {
nodeMap[node.oldLinks[i]] = nodeMap[node.oldLinks[i]]||{};
nodeMap[node.oldLinks[i]].old = true;
}
if (i<node.links.length) {
nodeMap[node.links[i]] = nodeMap[node.links[i]]||{};
nodeMap[node.links[i]].new = true;
}
}
var n;
for (var id in nodeMap) {
if (nodeMap.hasOwnProperty(id)) {
n = RED.nodes.node(id);
if (n) {
if (nodeMap[id].old && !nodeMap[id].new) {
// Removed id
i = n.links.indexOf(node.id);
if (i > -1) {
n.links.splice(i,1);
}
} else if (!nodeMap[id].old && nodeMap[id].new) {
// Added id
i = n.links.indexOf(id);
if (i === -1) {
n.links.push(node.id);
}
}
}
}
}
}
function onAdd() {
if (this.name === '_DEFAULT_') {
this.name = ''
RED.actions.invoke("core:generate-node-names", this, {generateHistory: false})
}
for (var i=0;i<this.links.length;i++) {
var n = RED.nodes.node(this.links[i]);
if (n && n.links.indexOf(this.id) === -1) {
n.links.push(this.id);
}
}
}
RED.nodes.registerType('link in',{
category: 'common',
color:"#ddd",//"#87D8CF",
defaults: {
name: { value: "_DEFAULT_" },
links: { value: [], type:"link out[]" }
},
inputs:0,
outputs:1,
icon: "link-out.svg",
outputLabels: function(i) {
return this.name||this._("link.linkIn");
},
showLabel: false,
label: function() {
return this.name||this._("link.linkIn");
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
onEditPrepare(this,"link out");
},
oneditsave: function() {
onEditSave(this);
// In case the name has changed, ensure any link call nodes on this
// tab are redrawn with the updated name
var localCallNodes = RED.nodes.filterNodes({z:RED.workspaces.active(), type:"link call"});
localCallNodes.forEach(function(node) {
node.dirty = true;
});
},
onadd: onAdd,
oneditresize: resizeNodeList
});
RED.nodes.registerType('link call',{
category: 'common',
color:"#ddd",//"#87D8CF",
defaults: {
name: { value: "" },
links: {
value: [],
type: "link in[]",
validate: function (v, opt) {
if ((this.linkType === "static" && v.length > 0)
|| this.linkType === "dynamic") {
return true;
}
return RED._("node-red:link.errors.linkUndefined");
}
},
linkType: { value:"static" },
timeout: {
value: "30",
label: RED._("node-red:link.timeout"),
validate:RED.validators.number(true)
}
},
inputs: 1,
outputs: 1,
icon: "link-call.svg",
inputLabels: function(i) {
return this.name||this._("link.linkCall");
},
label: function() {
if (this.name) {
return this.name;
}
if (this.linkType === "dynamic") {
return this._("link.dynamicLinkLabel");
} else if (this.links.length > 0) {
var targetNode = RED.nodes.node(this.links[0]);
return targetNode && (targetNode.name || this._("link.linkCall"));
}
return this._("inject.none");
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
const updateVisibility = function() {
const static = $('#node-input-linkType').val() !== "dynamic";
if(static) {
$("div.link-call-target-tree").show();
} else {
$("div.link-call-target-tree").hide();
}
}
$("#node-input-linkType").on("change",function(d){
updateVisibility();
});
if (["static","dynamic"].indexOf(this.linkType) < 0) {
$("#node-input-linkType").val('static');
}
updateVisibility();
onEditPrepare(this,"link in");
},
oneditsave: function() {
onEditSave(this);
},
oneditresize: resizeNodeList
});
RED.nodes.registerType('link out',{
category: 'common',
color:"#ddd",//"#87D8CF",
defaults: {
name: { value:"_DEFAULT_" },
mode: { value: "link" },// link || return
links: { value: [], type:"link in[]" }
},
align:"right",
inputs:1,
outputs:0,
icon: function() {
if (this.mode === "return") {
return "link-return.svg";
} else {
return "link-out.svg";
}
},
inputLabels: function(i) {
return this.name||(this.mode === "return" ?this._("link.linkOutReturn"):this._("link.linkOut"));
},
showLabel: false,
label: function() {
return this.name||(this.mode === "return" ?this._("link.linkOutReturn"):this._("link.linkOut"));
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
onEditPrepare(this,"link in");
$("#node-input-mode").on("change", function() {
$(".node-input-link-rows").toggle(this.value === "link")
})
if (!this.mode) {
$("#node-input-mode").val('link').trigger("change");
}
},
oneditsave: function() {
onEditSave(this);
},
onadd: onAdd,
oneditresize: resizeNodeList
});
})();
</script>