mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Merge branch 'dev' into pr_2042
This commit is contained in:
@@ -273,6 +273,7 @@
|
||||
"editSubflowProperties": "edit properties",
|
||||
"input": "inputs:",
|
||||
"output": "outputs:",
|
||||
"status": "status node",
|
||||
"deleteSubflow": "delete subflow",
|
||||
"info": "Description",
|
||||
"category": "Category",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/editor-client",
|
||||
"version": "0.20.0-beta.3",
|
||||
"version": "0.20.0-beta.4",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@@ -125,14 +125,20 @@ RED.history = (function() {
|
||||
});
|
||||
}
|
||||
}
|
||||
if (ev.subflow && ev.subflow.hasOwnProperty('instances')) {
|
||||
ev.subflow.instances.forEach(function(n) {
|
||||
var node = RED.nodes.node(n.id);
|
||||
if (node) {
|
||||
node.changed = n.changed;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
if (ev.subflow) {
|
||||
if (ev.subflow.hasOwnProperty('instances')) {
|
||||
ev.subflow.instances.forEach(function(n) {
|
||||
var node = RED.nodes.node(n.id);
|
||||
if (node) {
|
||||
node.changed = n.changed;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (ev.subflow.hasOwnProperty('status')) {
|
||||
subflow = RED.nodes.subflow(ev.subflow.id);
|
||||
subflow.status = ev.subflow.status;
|
||||
}
|
||||
}
|
||||
if (subflow) {
|
||||
RED.nodes.filterNodes({type:"subflow:"+subflow.id}).forEach(function(n) {
|
||||
@@ -232,6 +238,11 @@ RED.history = (function() {
|
||||
}
|
||||
});
|
||||
}
|
||||
if (ev.subflow.hasOwnProperty('status')) {
|
||||
if (ev.subflow.status) {
|
||||
delete ev.node.status;
|
||||
}
|
||||
}
|
||||
RED.editor.validateNode(ev.node);
|
||||
RED.nodes.filterNodes({type:"subflow:"+ev.node.id}).forEach(function(n) {
|
||||
n.inputs = ev.node.in.length;
|
||||
@@ -262,6 +273,8 @@ RED.history = (function() {
|
||||
} else if (ev.t == "createSubflow") {
|
||||
if (ev.nodes) {
|
||||
RED.nodes.filterNodes({z:ev.subflow.subflow.id}).forEach(function(n) {
|
||||
n.x += ev.subflow.offsetX;
|
||||
n.y += ev.subflow.offsetY;
|
||||
n.z = ev.activeWorkspace;
|
||||
n.dirty = true;
|
||||
});
|
||||
@@ -288,6 +301,7 @@ RED.history = (function() {
|
||||
RED.workspaces.order(ev.order);
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(modifiedTabs).forEach(function(id) {
|
||||
var subflow = RED.nodes.subflow(id);
|
||||
if (subflow) {
|
||||
@@ -301,6 +315,7 @@ RED.history = (function() {
|
||||
RED.palette.refresh();
|
||||
RED.workspaces.refresh();
|
||||
RED.sidebar.config.refresh();
|
||||
RED.subflow.refresh();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -575,6 +575,18 @@ RED.nodes = (function() {
|
||||
node.icon = n.icon;
|
||||
}
|
||||
}
|
||||
if (n.status) {
|
||||
node.status = {x: n.status.x, y: n.status.y, wires:[]};
|
||||
links.forEach(function(d) {
|
||||
if (d.target === n.status) {
|
||||
if (d.source.type != "subflow") {
|
||||
node.status.wires.push({id:d.source.id, port:d.sourcePort})
|
||||
} else {
|
||||
node.status.wires.push({id:n.id, port:0})
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
@@ -855,6 +867,12 @@ RED.nodes = (function() {
|
||||
output.i = i;
|
||||
output.id = getID();
|
||||
});
|
||||
if (n.status) {
|
||||
n.status.type = "subflow";
|
||||
n.status.direction = "status";
|
||||
n.status.z = n.id;
|
||||
n.status.id = getID();
|
||||
}
|
||||
new_subflows.push(n);
|
||||
addSubflow(n,createNewIds);
|
||||
}
|
||||
@@ -1194,6 +1212,19 @@ RED.nodes = (function() {
|
||||
});
|
||||
delete output.wires;
|
||||
});
|
||||
if (n.status) {
|
||||
n.status.wires.forEach(function(wire) {
|
||||
var link;
|
||||
if (subflow_map[wire.id] && subflow_map[wire.id].id == n.id) {
|
||||
link = {source:n.in[wire.port], sourcePort:wire.port,target:n.status};
|
||||
} else {
|
||||
link = {source:node_map[wire.id]||subflow_map[wire.id], sourcePort:wire.port,target:n.status};
|
||||
}
|
||||
addLink(link);
|
||||
new_links.push(link);
|
||||
});
|
||||
delete n.status.wires;
|
||||
}
|
||||
}
|
||||
|
||||
RED.workspaces.refresh();
|
||||
|
@@ -687,7 +687,7 @@ RED.diff = (function() {
|
||||
diff: remoteDiff
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var selectState = "";
|
||||
|
||||
if (conflicted) {
|
||||
@@ -1158,19 +1158,19 @@ RED.diff = (function() {
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
var diff = {
|
||||
currentConfig: currentConfig,
|
||||
newConfig: newConfig,
|
||||
added: added,
|
||||
deleted: deleted,
|
||||
changed: changed,
|
||||
moved: moved
|
||||
}
|
||||
};
|
||||
return diff;
|
||||
}
|
||||
function resolveDiffs(localDiff,remoteDiff) {
|
||||
var conflicted = {};
|
||||
var resolutions = {};
|
||||
|
||||
var diff = {
|
||||
localDiff: localDiff,
|
||||
remoteDiff: remoteDiff,
|
||||
@@ -1348,7 +1348,7 @@ RED.diff = (function() {
|
||||
if (node) {
|
||||
nodeChangedStates[id] = node.changed;
|
||||
}
|
||||
localChangedStates[id] = true;
|
||||
localChangedStates[id] = 1;
|
||||
newConfig.push(remoteDiff.newConfig.all[id]);
|
||||
}
|
||||
} else {
|
||||
@@ -1363,7 +1363,7 @@ RED.diff = (function() {
|
||||
nodeChangedStates[id] = node.changed;
|
||||
}
|
||||
if (!localDiff.added.hasOwnProperty(id)) {
|
||||
localChangedStates[id] = true;
|
||||
localChangedStates[id] = 2;
|
||||
newConfig.push(remoteDiff.newConfig.all[id]);
|
||||
}
|
||||
}
|
||||
@@ -1376,24 +1376,42 @@ RED.diff = (function() {
|
||||
}
|
||||
|
||||
function mergeDiff(diff) {
|
||||
//console.log(diff);
|
||||
var appliedDiff = applyDiff(diff);
|
||||
|
||||
var newConfig = appliedDiff.config;
|
||||
var nodeChangedStates = appliedDiff.nodeChangedStates;
|
||||
var localChangedStates = appliedDiff.localChangedStates;
|
||||
|
||||
var isDirty = RED.nodes.dirty();
|
||||
|
||||
var historyEvent = {
|
||||
t:"replace",
|
||||
config: RED.nodes.createCompleteNodeSet(),
|
||||
changed: nodeChangedStates,
|
||||
dirty: RED.nodes.dirty(),
|
||||
dirty: isDirty,
|
||||
rev: RED.nodes.version()
|
||||
}
|
||||
|
||||
RED.history.push(historyEvent);
|
||||
|
||||
var originalFlow = RED.nodes.originalFlow();
|
||||
// originalFlow is what the editor things it loaded
|
||||
// - add any newly added nodes from remote diff as they are now part of the record
|
||||
for (var id in diff.remoteDiff.added) {
|
||||
if (diff.remoteDiff.added.hasOwnProperty(id)) {
|
||||
if (diff.remoteDiff.newConfig.all.hasOwnProperty(id)) {
|
||||
originalFlow.push(JSON.parse(JSON.stringify(diff.remoteDiff.newConfig.all[id])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RED.nodes.clear();
|
||||
var imported = RED.nodes.import(newConfig);
|
||||
|
||||
// Restore the original flow so subsequent merge resolutions can properly
|
||||
// identify new-vs-old
|
||||
RED.nodes.originalFlow(originalFlow);
|
||||
imported[0].forEach(function(n) {
|
||||
if (nodeChangedStates[n.id] || localChangedStates[n.id]) {
|
||||
n.changed = true;
|
||||
@@ -1402,11 +1420,16 @@ RED.diff = (function() {
|
||||
|
||||
RED.nodes.version(diff.remoteDiff.rev);
|
||||
|
||||
if (isDirty) {
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
|
||||
RED.view.redraw(true);
|
||||
RED.palette.refresh();
|
||||
RED.workspaces.refresh();
|
||||
RED.sidebar.config.refresh();
|
||||
}
|
||||
|
||||
function showTestFlowDiff(index) {
|
||||
if (index === 1) {
|
||||
var localFlow = RED.nodes.createCompleteNodeSet();
|
||||
|
@@ -16,12 +16,12 @@
|
||||
|
||||
RED.subflow = (function() {
|
||||
|
||||
|
||||
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" 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 node-input-env-container-row"><ol id="node-input-env-container"></ol></div>'+
|
||||
'</script>';
|
||||
|
||||
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"><i class="fa fa-folder-o"></i> <label for="subflow-input-category" data-i18n="editor:subflow.category"></label><select style="width: 250px;" id="subflow-input-category"></select><input style="display:none; margin-left: 10px; width:calc(100% - 250px)" type="text" id="subflow-input-custom-category"></div>'+
|
||||
@@ -30,26 +30,22 @@ RED.subflow = (function() {
|
||||
'<div class="form-row form-tips" id="subflow-dialog-user-count"></div>'+
|
||||
'</script>';
|
||||
|
||||
|
||||
function getSubflow() {
|
||||
return RED.nodes.subflow(RED.workspaces.active());
|
||||
}
|
||||
|
||||
function findAvailableSubflowIOPosition(subflow,isInput) {
|
||||
var pos = {x:50,y:30};
|
||||
if (!isInput) {
|
||||
pos.x += 110;
|
||||
}
|
||||
for (var i=0;i<subflow.out.length+subflow.in.length;i++) {
|
||||
var port;
|
||||
if (i < subflow.out.length) {
|
||||
port = subflow.out[i];
|
||||
} else {
|
||||
port = subflow.in[i-subflow.out.length];
|
||||
}
|
||||
var ports = [].concat(subflow.out).concat(subflow.in);
|
||||
if (subflow.status) {
|
||||
ports.push(subflow.status);
|
||||
}
|
||||
ports.sort(function(A,B) {
|
||||
return A.x-B.x;
|
||||
});
|
||||
for (var i=0; i<ports.length; i++) {
|
||||
var port = ports[i];
|
||||
if (port.x == pos.x && port.y == pos.y) {
|
||||
pos.x += 55;
|
||||
i=0;
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
@@ -197,6 +193,61 @@ RED.subflow = (function() {
|
||||
return {subflowOutputs: removedSubflowOutputs, links: removedLinks}
|
||||
}
|
||||
|
||||
function addSubflowStatus() {
|
||||
var subflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
if (subflow.status) {
|
||||
return;
|
||||
}
|
||||
var position = findAvailableSubflowIOPosition(subflow,false);
|
||||
var statusNode = {
|
||||
type:"subflow",
|
||||
direction:"status",
|
||||
z:subflow.id,
|
||||
x:position.x,
|
||||
y:position.y,
|
||||
id:RED.nodes.id()
|
||||
};
|
||||
subflow.status = statusNode;
|
||||
subflow.dirty = true;
|
||||
var wasDirty = RED.nodes.dirty();
|
||||
var wasChanged = subflow.changed;
|
||||
subflow.changed = true;
|
||||
var result = refresh(true);
|
||||
var historyEvent = {
|
||||
t:'edit',
|
||||
node:subflow,
|
||||
dirty:wasDirty,
|
||||
changed:wasChanged,
|
||||
subflow: { status: true }
|
||||
};
|
||||
RED.history.push(historyEvent);
|
||||
RED.view.select();
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw();
|
||||
$("#workspace-subflow-status").prop("checked",!!subflow.status);
|
||||
$("#workspace-subflow-status").parent().parent().toggleClass("active",!!subflow.status);
|
||||
}
|
||||
|
||||
function removeSubflowStatus() {
|
||||
var subflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
if (!subflow.status) {
|
||||
return;
|
||||
}
|
||||
var subflowRemovedLinks = [];
|
||||
RED.nodes.eachLink(function(l) {
|
||||
if (l.target.type == "subflow" && l.target.z == subflow.id && l.target.direction == "status") {
|
||||
subflowRemovedLinks.push(l);
|
||||
}
|
||||
});
|
||||
subflowRemovedLinks.forEach(function(l) { RED.nodes.removeLink(l)});
|
||||
delete subflow.status;
|
||||
|
||||
$("#workspace-subflow-status").prop("checked",!!subflow.status);
|
||||
$("#workspace-subflow-status").parent().parent().toggleClass("active",!!subflow.status);
|
||||
|
||||
return { links: subflowRemovedLinks }
|
||||
}
|
||||
|
||||
function refresh(markChange) {
|
||||
var activeSubflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
refreshToolbar(activeSubflow);
|
||||
@@ -225,12 +276,17 @@ RED.subflow = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function refreshToolbar(activeSubflow) {
|
||||
if (activeSubflow) {
|
||||
$("#workspace-subflow-input-add").toggleClass("active", activeSubflow.in.length !== 0);
|
||||
$("#workspace-subflow-input-remove").toggleClass("active",activeSubflow.in.length === 0);
|
||||
|
||||
$("#workspace-subflow-output .spinner-value").text(activeSubflow.out.length);
|
||||
|
||||
$("#workspace-subflow-status").prop("checked",!!activeSubflow.status);
|
||||
$("#workspace-subflow-status").parent().parent().toggleClass("active",!!activeSubflow.status);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,22 +294,32 @@ RED.subflow = (function() {
|
||||
var toolbar = $("#workspace-toolbar");
|
||||
toolbar.empty();
|
||||
|
||||
// Edit properties
|
||||
$('<a class="button" id="workspace-subflow-edit" href="#" data-i18n="[append]subflow.editSubflowProperties"><i class="fa fa-pencil"></i> </a>').appendTo(toolbar);
|
||||
|
||||
// Inputs
|
||||
$('<span style="margin-left: 5px;" data-i18n="subflow.input"></span> '+
|
||||
'<div style="display: inline-block;" class="button-group">'+
|
||||
'<a id="workspace-subflow-input-remove" class="button active" href="#">0</a>'+
|
||||
'<a id="workspace-subflow-input-add" class="button" href="#">1</a>'+
|
||||
'</div>').appendTo(toolbar);
|
||||
|
||||
// Outputs
|
||||
$('<span style="margin-left: 5px;" data-i18n="subflow.output"></span> <div id="workspace-subflow-output" style="display: inline-block;" class="button-group spinner-group">'+
|
||||
'<a id="workspace-subflow-output-remove" class="button" href="#"><i class="fa fa-minus"></i></a>'+
|
||||
'<div class="spinner-value">3</div>'+
|
||||
'<a id="workspace-subflow-output-add" class="button" href="#"><i class="fa fa-plus"></i></a>'+
|
||||
'</div>').appendTo(toolbar);
|
||||
|
||||
// Status
|
||||
$('<span class="button-group"><span class="button" style="padding:0"><label for="workspace-subflow-status"><input id="workspace-subflow-status" type="checkbox"> <span data-i18n="subflow.status"></span></label></span></span>').appendTo(toolbar);
|
||||
|
||||
// $('<a class="button disabled" id="workspace-subflow-add-input" href="#" data-i18n="[append]subflow.input"><i class="fa fa-plus"></i> </a>').appendTo(toolbar);
|
||||
// $('<a class="button" id="workspace-subflow-add-output" href="#" data-i18n="[append]subflow.output"><i class="fa fa-plus"></i> </a>').appendTo(toolbar);
|
||||
|
||||
// Delete
|
||||
$('<a class="button" id="workspace-subflow-delete" href="#" data-i18n="[append]subflow.deleteSubflow"><i class="fa fa-trash"></i> </a>').appendTo(toolbar);
|
||||
|
||||
toolbar.i18n();
|
||||
|
||||
|
||||
@@ -280,6 +346,7 @@ RED.subflow = (function() {
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
});
|
||||
|
||||
$("#workspace-subflow-output-add").click(function(event) {
|
||||
event.preventDefault();
|
||||
addSubflowOutput();
|
||||
@@ -289,6 +356,7 @@ RED.subflow = (function() {
|
||||
event.preventDefault();
|
||||
addSubflowInput();
|
||||
});
|
||||
|
||||
$("#workspace-subflow-input-remove").click(function(event) {
|
||||
event.preventDefault();
|
||||
var wasDirty = RED.nodes.dirty();
|
||||
@@ -313,6 +381,33 @@ RED.subflow = (function() {
|
||||
}
|
||||
});
|
||||
|
||||
$("#workspace-subflow-status").change(function(evt) {
|
||||
if (this.checked) {
|
||||
addSubflowStatus();
|
||||
} else {
|
||||
var currentStatus = activeSubflow.status;
|
||||
var wasChanged = activeSubflow.changed;
|
||||
var result = removeSubflowStatus();
|
||||
if (result) {
|
||||
activeSubflow.changed = true;
|
||||
var wasDirty = RED.nodes.dirty();
|
||||
RED.history.push({
|
||||
t:'delete',
|
||||
links:result.links,
|
||||
changed: wasChanged,
|
||||
dirty:wasDirty,
|
||||
subflow: {
|
||||
id: activeSubflow.id,
|
||||
status: currentStatus
|
||||
}
|
||||
});
|
||||
RED.view.select();
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw();
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
$("#workspace-subflow-edit").click(function(event) {
|
||||
RED.editor.editSubflow(RED.nodes.subflow(RED.workspaces.active()));
|
||||
event.preventDefault();
|
||||
@@ -334,6 +429,7 @@ RED.subflow = (function() {
|
||||
$("#chart").css({"margin-top": "40px"});
|
||||
$("#workspace-toolbar").show();
|
||||
}
|
||||
|
||||
function hideWorkspaceToolbar() {
|
||||
$("#workspace-toolbar").hide().empty();
|
||||
$("#chart").css({"margin-top": "0"});
|
||||
@@ -379,6 +475,7 @@ RED.subflow = (function() {
|
||||
subflows: [activeSubflow]
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
RED.events.on("workspace:change",function(event) {
|
||||
var activeSubflow = RED.nodes.subflow(event.workspace);
|
||||
@@ -436,6 +533,13 @@ RED.subflow = (function() {
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
|
||||
function snapToGrid(x) {
|
||||
if (RED.settings.get("editor").view['view-snap-grid']) {
|
||||
x = Math.round(x / RED.view.gridSize()) * RED.view.gridSize();
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
function convertToSubflow() {
|
||||
var selection = RED.view.selection();
|
||||
if (!selection.nodes) {
|
||||
@@ -451,7 +555,6 @@ RED.subflow = (function() {
|
||||
var candidateOutputs = [];
|
||||
var candidateInputNodes = {};
|
||||
|
||||
|
||||
var boundingBox = [selection.nodes[0].x,
|
||||
selection.nodes[0].y,
|
||||
selection.nodes[0].x,
|
||||
@@ -467,8 +570,14 @@ RED.subflow = (function() {
|
||||
Math.max(boundingBox[3],n.y)
|
||||
]
|
||||
}
|
||||
var offsetX = snapToGrid(boundingBox[0] - 200);
|
||||
var offsetY = snapToGrid(boundingBox[1] - 80);
|
||||
|
||||
var center = [(boundingBox[2]+boundingBox[0]) / 2,(boundingBox[3]+boundingBox[1]) / 2];
|
||||
|
||||
var center = [
|
||||
snapToGrid((boundingBox[2]+boundingBox[0]) / 2),
|
||||
snapToGrid((boundingBox[3]+boundingBox[1]) / 2)
|
||||
];
|
||||
|
||||
RED.nodes.eachLink(function(link) {
|
||||
if (nodes[link.source.id] && nodes[link.target.id]) {
|
||||
@@ -525,8 +634,8 @@ RED.subflow = (function() {
|
||||
in: Object.keys(candidateInputNodes).map(function(v,i) { var index = i; return {
|
||||
type:"subflow",
|
||||
direction:"in",
|
||||
x:candidateInputNodes[v].x-(candidateInputNodes[v].w/2)-80,
|
||||
y:candidateInputNodes[v].y,
|
||||
x:snapToGrid(candidateInputNodes[v].x-(candidateInputNodes[v].w/2)-80 - offsetX),
|
||||
y:snapToGrid(candidateInputNodes[v].y - offsetY),
|
||||
z:subflowId,
|
||||
i:index,
|
||||
id:RED.nodes.id(),
|
||||
@@ -535,8 +644,8 @@ RED.subflow = (function() {
|
||||
out: candidateOutputs.map(function(v,i) { var index = i; return {
|
||||
type:"subflow",
|
||||
direction:"in",
|
||||
x:v.source.x+(v.source.w/2)+80,
|
||||
y:v.source.y,
|
||||
x:snapToGrid(v.source.x+(v.source.w/2)+80 - offsetX),
|
||||
y:snapToGrid(v.source.y - offsetY),
|
||||
z:subflowId,
|
||||
i:index,
|
||||
id:RED.nodes.id(),
|
||||
@@ -611,6 +720,8 @@ RED.subflow = (function() {
|
||||
return isLocalLink;
|
||||
});
|
||||
}
|
||||
n.x -= offsetX;
|
||||
n.y -= offsetY;
|
||||
n.z = subflow.id;
|
||||
}
|
||||
|
||||
@@ -619,7 +730,9 @@ RED.subflow = (function() {
|
||||
nodes:[subflowInstance.id],
|
||||
links:new_links,
|
||||
subflow: {
|
||||
subflow: subflow
|
||||
subflow: subflow,
|
||||
offsetX: offsetX,
|
||||
offsetY: offsetY
|
||||
},
|
||||
|
||||
activeWorkspace: RED.workspaces.active(),
|
||||
@@ -633,8 +746,6 @@ RED.subflow = (function() {
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return {
|
||||
init: init,
|
||||
createSubflow: createSubflow,
|
||||
@@ -642,6 +753,7 @@ RED.subflow = (function() {
|
||||
removeSubflow: removeSubflow,
|
||||
refresh: refresh,
|
||||
removeInput: removeSubflowInput,
|
||||
removeOutput: removeSubflowOutput
|
||||
removeOutput: removeSubflowOutput,
|
||||
removeStatus: removeSubflowStatus
|
||||
}
|
||||
})();
|
||||
|
@@ -1261,6 +1261,13 @@ RED.view = (function() {
|
||||
moving_set.push({n:n});
|
||||
}
|
||||
});
|
||||
if (activeSubflow.status) {
|
||||
activeSubflow.status.selected = (activeSubflow.status.x > x && activeSubflow.status.x < x2 && activeSubflow.status.y > y && activeSubflow.status.y < y2);
|
||||
if (activeSubflow.status.selected) {
|
||||
activeSubflow.status.dirty = true;
|
||||
moving_set.push({n:activeSubflow.status});
|
||||
}
|
||||
}
|
||||
}
|
||||
updateSelection();
|
||||
lasso.remove();
|
||||
@@ -1367,6 +1374,13 @@ RED.view = (function() {
|
||||
moving_set.push({n:n});
|
||||
}
|
||||
});
|
||||
if (activeSubflow.status) {
|
||||
if (!activeSubflow.status.selected) {
|
||||
activeSubflow.status.selected = true;
|
||||
activeSubflow.status.dirty = true;
|
||||
moving_set.push({n:activeSubflow.status});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selected_link = null;
|
||||
@@ -1552,6 +1566,7 @@ RED.view = (function() {
|
||||
var removedLinks = [];
|
||||
var removedSubflowOutputs = [];
|
||||
var removedSubflowInputs = [];
|
||||
var removedSubflowStatus = undefined;
|
||||
var subflowInstances = [];
|
||||
|
||||
var startDirty = RED.nodes.dirty();
|
||||
@@ -1573,6 +1588,8 @@ RED.view = (function() {
|
||||
removedSubflowOutputs.push(node);
|
||||
} else if (node.direction === "in") {
|
||||
removedSubflowInputs.push(node);
|
||||
} else if (node.direction === "status") {
|
||||
removedSubflowStatus = node;
|
||||
}
|
||||
node.dirty = true;
|
||||
}
|
||||
@@ -1590,12 +1607,19 @@ RED.view = (function() {
|
||||
removedLinks = removedLinks.concat(result.links);
|
||||
}
|
||||
}
|
||||
if (removedSubflowStatus) {
|
||||
result = RED.subflow.removeStatus();
|
||||
if (result) {
|
||||
removedLinks = removedLinks.concat(result.links);
|
||||
}
|
||||
}
|
||||
|
||||
var instances = RED.subflow.refresh(true);
|
||||
if (instances) {
|
||||
subflowInstances = instances.instances;
|
||||
}
|
||||
moving_set = [];
|
||||
if (removedNodes.length > 0 || removedSubflowOutputs.length > 0 || removedSubflowInputs.length > 0) {
|
||||
if (removedNodes.length > 0 || removedSubflowOutputs.length > 0 || removedSubflowInputs.length > 0 || removedSubflowStatus) {
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
}
|
||||
@@ -1651,10 +1675,14 @@ RED.view = (function() {
|
||||
subflowOutputs:removedSubflowOutputs,
|
||||
subflowInputs:removedSubflowInputs,
|
||||
subflow: {
|
||||
id: activeSubflow?activeSubflow.id:undefined,
|
||||
instances: subflowInstances
|
||||
},
|
||||
dirty:startDirty
|
||||
};
|
||||
if (removedSubflowStatus) {
|
||||
historyEvent.subflow.status = removedSubflowStatus;
|
||||
}
|
||||
}
|
||||
RED.history.push(historyEvent);
|
||||
|
||||
@@ -2420,6 +2448,49 @@ RED.view = (function() {
|
||||
|
||||
inGroup.append("svg:text").attr("class","port_label").attr("x",18).attr("y",20).style("font-size","10px").text("input");
|
||||
|
||||
var subflowStatus = nodeLayer.selectAll(".subflowstatus").data(activeSubflow.status?[activeSubflow.status]:[],function(d,i){ return d.id;});
|
||||
subflowStatus.exit().remove();
|
||||
|
||||
var statusGroup = subflowStatus.enter().insert("svg:g").attr("class","node subflowstatus").attr("transform",function(d) { return "translate("+(d.x-20)+","+(d.y-20)+")"});
|
||||
statusGroup.each(function(d,i) {
|
||||
d.w=40;
|
||||
d.h=40;
|
||||
});
|
||||
statusGroup.append("rect").attr("class","subflowport").attr("rx",8).attr("ry",8).attr("width",40).attr("height",40)
|
||||
// TODO: This is exactly the same set of handlers used for regular nodes - DRY
|
||||
.on("mouseup",nodeMouseUp)
|
||||
.on("mousedown",nodeMouseDown)
|
||||
.on("touchstart",function(d) {
|
||||
var obj = d3.select(this);
|
||||
var touch0 = d3.event.touches.item(0);
|
||||
var pos = [touch0.pageX,touch0.pageY];
|
||||
startTouchCenter = [touch0.pageX,touch0.pageY];
|
||||
startTouchDistance = 0;
|
||||
touchStartTime = setTimeout(function() {
|
||||
showTouchMenu(obj,pos);
|
||||
},touchLongPressTimeout);
|
||||
nodeMouseDown.call(this,d)
|
||||
})
|
||||
.on("touchend", function(d) {
|
||||
clearTimeout(touchStartTime);
|
||||
touchStartTime = null;
|
||||
if (RED.touch.radialMenu.active()) {
|
||||
d3.event.stopPropagation();
|
||||
return;
|
||||
}
|
||||
nodeMouseUp.call(this,d);
|
||||
});
|
||||
|
||||
statusGroup.append("g").attr('transform','translate(-5,15)').append("rect").attr("class","port").attr("rx",3).attr("ry",3).attr("width",10).attr("height",10)
|
||||
.on("mousedown", function(d,i){portMouseDown(d,PORT_TYPE_INPUT,0);} )
|
||||
.on("touchstart", function(d,i){portMouseDown(d,PORT_TYPE_INPUT,0);} )
|
||||
.on("mouseup", function(d,i){portMouseUp(d,PORT_TYPE_INPUT,0);})
|
||||
.on("touchend",function(d,i){portMouseUp(d,PORT_TYPE_INPUT,0);} )
|
||||
.on("mouseover",function(d){portMouseOver(d3.select(this),d,PORT_TYPE_INPUT,0);})
|
||||
.on("mouseout",function(d){portMouseOut(d3.select(this),d,PORT_TYPE_INPUT,0);});
|
||||
|
||||
statusGroup.append("svg:text").attr("class","port_label").attr("x",22).attr("y",20).style("font-size","10px").text("status");
|
||||
|
||||
subflowOutputs.each(function(d,i) {
|
||||
if (d.dirty) {
|
||||
var output = d3.select(this);
|
||||
@@ -2439,9 +2510,22 @@ RED.view = (function() {
|
||||
d.dirty = false;
|
||||
}
|
||||
});
|
||||
subflowStatus.each(function(d,i) {
|
||||
if (d.dirty) {
|
||||
var output = d3.select(this);
|
||||
output.selectAll(".subflowport").classed("node_selected",function(d) { return d.selected; })
|
||||
output.selectAll(".port_index").text(function(d){ return d.i+1});
|
||||
output.attr("transform", function(d) { return "translate(" + (d.x-d.w/2) + "," + (d.y-d.h/2) + ")"; });
|
||||
dirtyNodes[d.id] = d;
|
||||
d.dirty = false;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
} else {
|
||||
nodeLayer.selectAll(".subflowoutput").remove();
|
||||
nodeLayer.selectAll(".subflowinput").remove();
|
||||
nodeLayer.selectAll(".subflowstatus").remove();
|
||||
}
|
||||
|
||||
var node = nodeLayer.selectAll(".nodegroup").data(activeNodes,function(d){return d.id});
|
||||
@@ -2703,6 +2787,8 @@ RED.view = (function() {
|
||||
d.resize = false;
|
||||
}
|
||||
var thisNode = d3.select(this);
|
||||
thisNode.classed("node_subflow",function(d) { return activeSubflow != null; })
|
||||
|
||||
//thisNode.selectAll(".centerDot").attr({"cx":function(d) { return d.w/2;},"cy":function(d){return d.h/2}});
|
||||
thisNode.attr("transform", function(d) { return "translate(" + (d.x-d.w/2) + "," + (d.y-d.h/2) + ")"; });
|
||||
if (mouse_mode != RED.state.MOVING_ACTIVE) {
|
||||
@@ -3004,6 +3090,9 @@ RED.view = (function() {
|
||||
links.each(function(d) {
|
||||
var link = d3.select(this);
|
||||
if (d.added || d===selected_link || d.selected || dirtyNodes[d.source.id] || dirtyNodes[d.target.id]) {
|
||||
if (/link_line/.test(link.attr('class'))) {
|
||||
link.classed("link_subflow", function(d) { return !d.link && activeSubflow });
|
||||
}
|
||||
link.attr("d",function(d){
|
||||
var numOutputs = d.source.outputs || 1;
|
||||
var sourcePort = d.sourcePort || 0;
|
||||
@@ -3017,8 +3106,11 @@ RED.view = (function() {
|
||||
// " C "+(d.x1+scale*node_width)+" "+(d.y1+scaleY*node_height)+" "+
|
||||
// (d.x2-scale*node_width)+" "+(d.y2-scaleY*node_height)+" "+
|
||||
// d.x2+" "+d.y2;
|
||||
|
||||
return generateLinkPath(d.x1,d.y1,d.x2,d.y2,1);
|
||||
var path = generateLinkPath(d.x1,d.y1,d.x2,d.y2,1);
|
||||
if (/NaN/.test(path)) {
|
||||
return ""
|
||||
}
|
||||
return path;
|
||||
});
|
||||
}
|
||||
})
|
||||
|
@@ -33,6 +33,15 @@
|
||||
transition: right 0.2s ease;
|
||||
overflow: hidden;
|
||||
|
||||
label {
|
||||
padding: 1px 8px;
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
input[type="checkbox"] {
|
||||
margin: 0 3px 0 0 ;
|
||||
padding: 0;
|
||||
}
|
||||
.button {
|
||||
@include workspace-button;
|
||||
margin-right: 10px;
|
||||
|
Reference in New Issue
Block a user