mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Merge branch 'node-red:dev' into dev
This commit is contained in:
@@ -684,6 +684,13 @@ RED.history = (function() {
|
||||
peek: function() {
|
||||
return undoHistory[undoHistory.length-1];
|
||||
},
|
||||
replace: function(ev) {
|
||||
if (undoHistory.length === 0) {
|
||||
RED.history.push(ev);
|
||||
} else {
|
||||
undoHistory[undoHistory.length-1] = ev;
|
||||
}
|
||||
},
|
||||
clear: function() {
|
||||
undoHistory = [];
|
||||
redoHistory = [];
|
||||
|
@@ -38,7 +38,9 @@
|
||||
},
|
||||
"red-ui-workspace": {
|
||||
"backspace": "core:delete-selection",
|
||||
"ctrl-backspace": "core:delete-selection-and-reconnect",
|
||||
"delete": "core:delete-selection",
|
||||
"ctrl-delete": "core:delete-selection-and-reconnect",
|
||||
"enter": "core:edit-selected-node",
|
||||
"ctrl-enter": "core:go-to-selection",
|
||||
"ctrl-c": "core:copy-selection-to-internal-clipboard",
|
||||
|
@@ -15,6 +15,9 @@
|
||||
**/
|
||||
RED.nodes = (function() {
|
||||
|
||||
var PORT_TYPE_INPUT = 1;
|
||||
var PORT_TYPE_OUTPUT = 0;
|
||||
|
||||
var node_defs = {};
|
||||
var linkTabMap = {};
|
||||
|
||||
@@ -2458,6 +2461,144 @@ RED.nodes = (function() {
|
||||
return helpContent;
|
||||
}
|
||||
|
||||
function getNodeIslands(nodes) {
|
||||
var selectedNodes = new Set(nodes);
|
||||
// Maps node => island index
|
||||
var nodeToIslandIndex = new Map();
|
||||
// Maps island index => [nodes in island]
|
||||
var islandIndexToNodes = new Map();
|
||||
var internalLinks = new Set();
|
||||
nodes.forEach((node, index) => {
|
||||
nodeToIslandIndex.set(node,index);
|
||||
islandIndexToNodes.set(index, [node]);
|
||||
var inboundLinks = RED.nodes.getNodeLinks(node, PORT_TYPE_INPUT);
|
||||
var outboundLinks = RED.nodes.getNodeLinks(node, PORT_TYPE_OUTPUT);
|
||||
inboundLinks.forEach(l => {
|
||||
if (selectedNodes.has(l.source)) {
|
||||
internalLinks.add(l)
|
||||
}
|
||||
})
|
||||
outboundLinks.forEach(l => {
|
||||
if (selectedNodes.has(l.target)) {
|
||||
internalLinks.add(l)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
internalLinks.forEach(l => {
|
||||
let source = l.source;
|
||||
let target = l.target;
|
||||
if (nodeToIslandIndex.get(source) !== nodeToIslandIndex.get(target)) {
|
||||
let sourceIsland = nodeToIslandIndex.get(source);
|
||||
let islandToMove = nodeToIslandIndex.get(target);
|
||||
let nodesToMove = islandIndexToNodes.get(islandToMove);
|
||||
nodesToMove.forEach(n => {
|
||||
nodeToIslandIndex.set(n,sourceIsland);
|
||||
islandIndexToNodes.get(sourceIsland).push(n);
|
||||
})
|
||||
islandIndexToNodes.delete(islandToMove);
|
||||
}
|
||||
})
|
||||
const result = [];
|
||||
islandIndexToNodes.forEach((nodes,index) => {
|
||||
result.push(nodes);
|
||||
})
|
||||
return result;
|
||||
}
|
||||
|
||||
function detachNodes(nodes) {
|
||||
let allSelectedNodes = [];
|
||||
nodes.forEach(node => {
|
||||
if (node.type === 'group') {
|
||||
let groupNodes = RED.group.getNodes(node,true,true);
|
||||
allSelectedNodes = allSelectedNodes.concat(groupNodes);
|
||||
} else {
|
||||
allSelectedNodes.push(node);
|
||||
}
|
||||
})
|
||||
if (allSelectedNodes.length > 0 ) {
|
||||
const nodeIslands = RED.nodes.getNodeIslands(allSelectedNodes);
|
||||
let removedLinks = [];
|
||||
let newLinks = [];
|
||||
let createdLinkIds = new Set();
|
||||
|
||||
nodeIslands.forEach(nodes => {
|
||||
let selectedNodes = new Set(nodes);
|
||||
let allInboundLinks = [];
|
||||
let allOutboundLinks = [];
|
||||
// Identify links that enter or exit this island of nodes
|
||||
nodes.forEach(node => {
|
||||
var inboundLinks = RED.nodes.getNodeLinks(node, PORT_TYPE_INPUT);
|
||||
var outboundLinks = RED.nodes.getNodeLinks(node, PORT_TYPE_OUTPUT);
|
||||
inboundLinks.forEach(l => {
|
||||
if (!selectedNodes.has(l.source)) {
|
||||
allInboundLinks.push(l)
|
||||
}
|
||||
})
|
||||
outboundLinks.forEach(l => {
|
||||
if (!selectedNodes.has(l.target)) {
|
||||
allOutboundLinks.push(l)
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
// Identify the links to restore
|
||||
allInboundLinks.forEach(inLink => {
|
||||
// For Each inbound link,
|
||||
// - get source node.
|
||||
// - trace through to all outbound links
|
||||
let sourceNode = inLink.source;
|
||||
let targetNodes = new Set();
|
||||
let visited = new Set();
|
||||
let stack = [inLink.target];
|
||||
while (stack.length > 0) {
|
||||
let node = stack.pop(stack);
|
||||
visited.add(node)
|
||||
let links = RED.nodes.getNodeLinks(node, PORT_TYPE_OUTPUT);
|
||||
links.forEach(l => {
|
||||
if (visited.has(l.target)) {
|
||||
return
|
||||
}
|
||||
visited.add(l.target);
|
||||
if (selectedNodes.has(l.target)) {
|
||||
// internal link
|
||||
stack.push(l.target)
|
||||
} else {
|
||||
targetNodes.add(l.target)
|
||||
}
|
||||
})
|
||||
}
|
||||
targetNodes.forEach(target => {
|
||||
let linkId = `${sourceNode.id}[${inLink.sourcePort}] -> ${target.id}`
|
||||
if (!createdLinkIds.has(linkId)) {
|
||||
createdLinkIds.add(linkId);
|
||||
let link = {
|
||||
source: sourceNode,
|
||||
sourcePort: inLink.sourcePort,
|
||||
target: target
|
||||
}
|
||||
let existingLinks = RED.nodes.filterLinks(link)
|
||||
if (existingLinks.length === 0) {
|
||||
newLinks.push(link);
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// 2. delete all those links
|
||||
allInboundLinks.forEach(l => { RED.nodes.removeLink(l); removedLinks.push(l)})
|
||||
allOutboundLinks.forEach(l => { RED.nodes.removeLink(l); removedLinks.push(l)})
|
||||
})
|
||||
|
||||
newLinks.forEach(l => RED.nodes.addLink(l));
|
||||
return {
|
||||
newLinks,
|
||||
removedLinks
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
RED.events.on("registry:node-type-added",function(type) {
|
||||
@@ -2539,7 +2680,7 @@ RED.nodes = (function() {
|
||||
add: addNode,
|
||||
remove: removeNode,
|
||||
clear: clear,
|
||||
|
||||
detachNodes: detachNodes,
|
||||
moveNodesForwards: moveNodesForwards,
|
||||
moveNodesBackwards: moveNodesBackwards,
|
||||
moveNodesToFront: moveNodesToFront,
|
||||
@@ -2551,7 +2692,20 @@ RED.nodes = (function() {
|
||||
|
||||
addLink: addLink,
|
||||
removeLink: removeLink,
|
||||
|
||||
getNodeLinks: function(id, portType) {
|
||||
if (typeof id !== 'string') {
|
||||
id = id.id;
|
||||
}
|
||||
if (nodeLinks[id]) {
|
||||
if (portType === 1) {
|
||||
// Return cloned arrays so they can be safely modified by caller
|
||||
return [].concat(nodeLinks[id].in)
|
||||
} else {
|
||||
return [].concat(nodeLinks[id].out)
|
||||
}
|
||||
}
|
||||
return [];
|
||||
},
|
||||
addWorkspace: addWorkspace,
|
||||
removeWorkspace: removeWorkspace,
|
||||
getWorkspaceOrder: function() { return workspacesOrder },
|
||||
@@ -2625,6 +2779,7 @@ RED.nodes = (function() {
|
||||
getAllFlowNodes: getAllFlowNodes,
|
||||
getAllUpstreamNodes: getAllUpstreamNodes,
|
||||
getAllDownstreamNodes: getAllDownstreamNodes,
|
||||
getNodeIslands: getNodeIslands,
|
||||
createExportableNodeSet: createExportableNodeSet,
|
||||
createCompleteNodeSet: createCompleteNodeSet,
|
||||
updateConfigNodeUsers: updateConfigNodeUsers,
|
||||
|
@@ -71,6 +71,7 @@ RED.clipboard = (function() {
|
||||
text: RED._("common.label.cancel"),
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
RED.view.focus();
|
||||
}
|
||||
},
|
||||
{ // red-ui-clipboard-dialog-download
|
||||
@@ -81,6 +82,7 @@ RED.clipboard = (function() {
|
||||
var data = $("#red-ui-clipboard-dialog-export-text").val();
|
||||
downloadData("flows.json", data);
|
||||
$( this ).dialog( "close" );
|
||||
RED.view.focus();
|
||||
}
|
||||
},
|
||||
{ // red-ui-clipboard-dialog-export
|
||||
@@ -95,6 +97,7 @@ RED.clipboard = (function() {
|
||||
$( this ).dialog( "close" );
|
||||
copyText(flowData);
|
||||
RED.notify(RED._("clipboard.nodesExported"),{id:"clipboard"});
|
||||
RED.view.focus();
|
||||
} else {
|
||||
var flowToExport = $("#red-ui-clipboard-dialog-export-text").val();
|
||||
var selectedPath = activeLibraries[activeTab].getSelected();
|
||||
@@ -110,6 +113,7 @@ RED.clipboard = (function() {
|
||||
contentType: "application/json; charset=utf-8"
|
||||
}).done(function() {
|
||||
$(dialog).dialog( "close" );
|
||||
RED.view.focus();
|
||||
RED.notify(RED._("library.exportedToLibrary"),"success");
|
||||
}).fail(function(xhr,textStatus,err) {
|
||||
if (xhr.status === 401) {
|
||||
@@ -171,6 +175,7 @@ RED.clipboard = (function() {
|
||||
}
|
||||
}
|
||||
$( this ).dialog( "close" );
|
||||
RED.view.focus();
|
||||
}
|
||||
},
|
||||
{ // red-ui-clipboard-dialog-import-conflict
|
||||
@@ -203,6 +208,7 @@ RED.clipboard = (function() {
|
||||
// console.table(pendingImportConfig.importNodes.map(function(n) { return {id:n.id,type:n.type,result:importMap[n.id]}}))
|
||||
RED.view.importNodes(newNodes, pendingImportConfig.importOptions);
|
||||
$( this ).dialog( "close" );
|
||||
RED.view.focus();
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -940,28 +946,25 @@ RED.clipboard = (function() {
|
||||
if (truncated) {
|
||||
msg += "_truncated";
|
||||
}
|
||||
$("#red-ui-clipboard-hidden").val(value).focus().select();
|
||||
var result = document.execCommand("copy");
|
||||
if (result && element) {
|
||||
var popover = RED.popover.create({
|
||||
target: element,
|
||||
direction: 'left',
|
||||
size: 'small',
|
||||
content: RED._(msg)
|
||||
});
|
||||
setTimeout(function() {
|
||||
popover.close();
|
||||
},1000);
|
||||
popover.open();
|
||||
}
|
||||
$("#red-ui-clipboard-hidden").val("");
|
||||
if (currentFocus) {
|
||||
$(currentFocus).focus();
|
||||
}
|
||||
return result;
|
||||
navigator.clipboard.writeText(value).then(function () {
|
||||
if (element) {
|
||||
var popover = RED.popover.create({
|
||||
target: element,
|
||||
direction: 'left',
|
||||
size: 'small',
|
||||
content: RED._(msg)
|
||||
});
|
||||
setTimeout(function() {
|
||||
popover.close();
|
||||
},1000);
|
||||
popover.open();
|
||||
}
|
||||
if (currentFocus) {
|
||||
$(currentFocus).focus();
|
||||
}
|
||||
}).catch(err => { console.error("Failed to copy:",err) });
|
||||
}
|
||||
|
||||
|
||||
function importNodes(nodesStr,addFlow) {
|
||||
var newNodes = nodesStr;
|
||||
if (typeof nodesStr === 'string') {
|
||||
@@ -1236,8 +1239,6 @@ RED.clipboard = (function() {
|
||||
init: function() {
|
||||
setupDialogs();
|
||||
|
||||
$('<textarea type="text" id="red-ui-clipboard-hidden" tabIndex="-1">').appendTo("#red-ui-editor");
|
||||
|
||||
RED.actions.add("core:show-export-dialog",showExportNodes);
|
||||
RED.actions.add("core:show-import-dialog",showImportNodes);
|
||||
|
||||
|
@@ -578,7 +578,7 @@ RED.tabs = (function() {
|
||||
|
||||
function findPreviousVisibleTab(li) {
|
||||
if (!li) {
|
||||
li = ul.find("li.active").parent();
|
||||
li = ul.find("li.active");
|
||||
}
|
||||
var previous = li.prev();
|
||||
while(previous.length > 0 && previous.hasClass("hide-tab")) {
|
||||
@@ -588,9 +588,9 @@ RED.tabs = (function() {
|
||||
}
|
||||
function findNextVisibleTab(li) {
|
||||
if (!li) {
|
||||
li = ul.find("li.active").parent();
|
||||
li = ul.find("li.active");
|
||||
}
|
||||
var next = ul.find("li.active").next();
|
||||
var next = li.next();
|
||||
while(next.length > 0 && next.hasClass("hide-tab")) {
|
||||
next = next.next();
|
||||
}
|
||||
|
@@ -333,6 +333,16 @@ RED.deploy = (function() {
|
||||
var unknownNodes = [];
|
||||
var invalidNodes = [];
|
||||
|
||||
RED.nodes.eachConfig(function(node) {
|
||||
if (!node.valid && !node.d) {
|
||||
invalidNodes.push(getNodeInfo(node));
|
||||
}
|
||||
if (node.type === "unknown") {
|
||||
if (unknownNodes.indexOf(node.name) == -1) {
|
||||
unknownNodes.push(node.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
RED.nodes.eachNode(function(node) {
|
||||
if (!node.valid && !node.d) {
|
||||
invalidNodes.push(getNodeInfo(node));
|
||||
|
@@ -247,7 +247,7 @@
|
||||
var currentExpression = expressionEditor.getValue();
|
||||
var expr;
|
||||
var usesContext = false;
|
||||
var legacyMode = /(^|[^a-zA-Z0-9_'"])msg([^a-zA-Z0-9_'"]|$)/.test(currentExpression);
|
||||
var legacyMode = /(^|[^a-zA-Z0-9_'".])msg([^a-zA-Z0-9_'"]|$)/.test(currentExpression);
|
||||
$(".red-ui-editor-type-expression-legacy").toggle(legacyMode);
|
||||
try {
|
||||
expr = jsonata(currentExpression);
|
||||
|
@@ -81,7 +81,8 @@
|
||||
clearTimeout: true,
|
||||
setInterval: true,
|
||||
clearInterval: true
|
||||
}
|
||||
},
|
||||
extraLibs: options.extraLibs
|
||||
});
|
||||
if (options.cursor) {
|
||||
expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false);
|
||||
|
@@ -55,7 +55,9 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!isSameObj(old_env, new_env)) {
|
||||
if (!old_env && new_env.length === 0) {
|
||||
delete node.env;
|
||||
} else if (!isSameObj(old_env, new_env)) {
|
||||
editState.changes.env = node.env;
|
||||
if (new_env.length === 0) {
|
||||
delete node.env;
|
||||
|
@@ -590,12 +590,14 @@ RED.group = (function() {
|
||||
markDirty(group);
|
||||
}
|
||||
|
||||
function getNodes(group,recursive) {
|
||||
function getNodes(group,recursive,excludeGroup) {
|
||||
var nodes = [];
|
||||
group.nodes.forEach(function(n) {
|
||||
nodes.push(n);
|
||||
if (n.type !== 'group' || !excludeGroup) {
|
||||
nodes.push(n);
|
||||
}
|
||||
if (recursive && n.type === 'group') {
|
||||
nodes = nodes.concat(getNodes(n,recursive))
|
||||
nodes = nodes.concat(getNodes(n,recursive,excludeGroup))
|
||||
}
|
||||
})
|
||||
return nodes;
|
||||
|
@@ -22,6 +22,7 @@ RED.search = (function() {
|
||||
var selected = -1;
|
||||
var visible = false;
|
||||
|
||||
var searchHistory = [];
|
||||
var index = {};
|
||||
var currentResults = [];
|
||||
var previousActiveElement;
|
||||
@@ -205,6 +206,20 @@ RED.search = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
function populateSearchHistory() {
|
||||
if (searchHistory.length > 0) {
|
||||
searchResults.editableList('addItem',{
|
||||
historyHeader: true
|
||||
});
|
||||
searchHistory.forEach(function(entry) {
|
||||
searchResults.editableList('addItem',{
|
||||
history: true,
|
||||
value: entry
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
function createDialog() {
|
||||
dialog = $("<div>",{id:"red-ui-search",class:"red-ui-search"}).appendTo("#red-ui-main-container");
|
||||
var searchDiv = $("<div>",{class:"red-ui-search-container"}).appendTo(dialog);
|
||||
@@ -213,7 +228,12 @@ RED.search = (function() {
|
||||
change: function() {
|
||||
searchResults.editableList('empty');
|
||||
selected = -1;
|
||||
currentResults = search($(this).val());
|
||||
var value = $(this).val();
|
||||
if (value === "") {
|
||||
populateSearchHistory();
|
||||
return;
|
||||
}
|
||||
currentResults = search(value);
|
||||
if (currentResults.length > 0) {
|
||||
for (i=0;i<Math.min(currentResults.length,25);i++) {
|
||||
searchResults.editableList('addItem',currentResults[i])
|
||||
@@ -285,7 +305,12 @@ RED.search = (function() {
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} if ($(children[selected]).hasClass("red-ui-search-history")) {
|
||||
var object = $(children[selected]).find(".red-ui-editableList-item-content").data('data');
|
||||
if (object) {
|
||||
searchInput.searchBox('value',object.value)
|
||||
}
|
||||
} else if (!$(children[selected]).hasClass("red-ui-search-historyHeader")) {
|
||||
if (currentResults.length > 0) {
|
||||
reveal(currentResults[Math.max(0,selected)].node);
|
||||
}
|
||||
@@ -301,7 +326,32 @@ RED.search = (function() {
|
||||
addItem: function(container,i,object) {
|
||||
var node = object.node;
|
||||
var div;
|
||||
if (object.more) {
|
||||
if (object.historyHeader) {
|
||||
container.parent().addClass("red-ui-search-historyHeader")
|
||||
$('<div>',{class:"red-ui-search-empty"}).text(RED._("search.history")).appendTo(container);
|
||||
$('<button type="button" class="red-ui-button red-ui-button-small"></button>').text(RED._("search.clear")).appendTo(container).on("click", function(evt) {
|
||||
evt.preventDefault();
|
||||
searchHistory = [];
|
||||
searchResults.editableList('empty');
|
||||
});
|
||||
} else if (object.history) {
|
||||
container.parent().addClass("red-ui-search-history")
|
||||
div = $('<a>',{href:'#',class:"red-ui-search-result"}).appendTo(container);
|
||||
div.text(object.value);
|
||||
div.on("click", function(evt) {
|
||||
evt.preventDefault();
|
||||
searchInput.searchBox('value',object.value)
|
||||
searchInput.focus();
|
||||
})
|
||||
$('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-remove"></i></button>').appendTo(container).on("click", function(evt) {
|
||||
evt.preventDefault();
|
||||
var index = searchHistory.indexOf(object.value);
|
||||
searchHistory.splice(index,1);
|
||||
searchResults.editableList('removeItem', object);
|
||||
});
|
||||
|
||||
|
||||
} else if (object.more) {
|
||||
container.parent().addClass("red-ui-search-more")
|
||||
div = $('<a>',{href:'#',class:"red-ui-search-result red-ui-search-empty"}).appendTo(container);
|
||||
div.text(RED._("palette.editor.more",{count:object.more.results.length-object.more.start}));
|
||||
@@ -356,6 +406,12 @@ RED.search = (function() {
|
||||
}
|
||||
|
||||
function reveal(node) {
|
||||
var searchVal = searchInput.val();
|
||||
var existingIndex = searchHistory.indexOf(searchVal);
|
||||
if (existingIndex > -1) {
|
||||
searchHistory.splice(existingIndex,1);
|
||||
}
|
||||
searchHistory.unshift(searchInput.val());
|
||||
hide();
|
||||
RED.view.reveal(node.id);
|
||||
}
|
||||
@@ -374,9 +430,14 @@ RED.search = (function() {
|
||||
|
||||
if (dialog === null) {
|
||||
createDialog();
|
||||
} else {
|
||||
searchResults.editableList('empty');
|
||||
}
|
||||
dialog.slideDown(300);
|
||||
searchInput.searchBox('value',v)
|
||||
if (!v || v === "") {
|
||||
populateSearchHistory();
|
||||
}
|
||||
RED.events.emit("search:open");
|
||||
visible = true;
|
||||
}
|
||||
|
@@ -27,5 +27,7 @@ RED.state = {
|
||||
PANNING: 10,
|
||||
SELECTING_NODE: 11,
|
||||
GROUP_DRAGGING: 12,
|
||||
GROUP_RESIZE: 13
|
||||
GROUP_RESIZE: 13,
|
||||
DETACHED_DRAGGING: 14,
|
||||
SLICING: 15
|
||||
}
|
||||
|
@@ -256,6 +256,10 @@ RED.tourGuide = (function() {
|
||||
}
|
||||
$('<div>').css("text-align","left").html(getLocaleText(step.description)).appendTo(stepDescription);
|
||||
|
||||
if (step.image) {
|
||||
$(`<img src="red/tours/${step.image}" />`).appendTo(stepDescription)
|
||||
}
|
||||
|
||||
var stepToolbar = $('<div>',{class:"red-ui-tourGuide-toolbar"}).appendTo(stepContent);
|
||||
|
||||
// var breadcrumbs = $('<div>',{class:"red-ui-tourGuide-breadcrumbs"}).appendTo(stepToolbar);
|
||||
|
@@ -725,6 +725,90 @@ RED.view.tools = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function wireSeriesOfNodes() {
|
||||
var selection = RED.view.selection();
|
||||
if (selection.nodes) {
|
||||
if (selection.nodes.length > 1) {
|
||||
var i = 0;
|
||||
var newLinks = [];
|
||||
while (i < selection.nodes.length - 1) {
|
||||
var nodeA = selection.nodes[i];
|
||||
var nodeB = selection.nodes[i+1];
|
||||
if (nodeA.outputs > 0 && nodeB.inputs > 0) {
|
||||
var existingLinks = RED.nodes.filterLinks({
|
||||
source: nodeA,
|
||||
target: nodeB,
|
||||
sourcePort: 0
|
||||
})
|
||||
if (existingLinks.length === 0) {
|
||||
var newLink = {
|
||||
source: nodeA,
|
||||
target: nodeB,
|
||||
sourcePort: 0
|
||||
}
|
||||
RED.nodes.addLink(newLink);
|
||||
newLinks.push(newLink);
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (newLinks.length > 0) {
|
||||
RED.history.push({
|
||||
t: 'add',
|
||||
links: newLinks,
|
||||
dirty: RED.nodes.dirty()
|
||||
})
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function wireNodeToMultiple() {
|
||||
var selection = RED.view.selection();
|
||||
if (selection.nodes) {
|
||||
if (selection.nodes.length > 1) {
|
||||
var sourceNode = selection.nodes[0];
|
||||
if (sourceNode.outputs === 0) {
|
||||
return;
|
||||
}
|
||||
var i = 1;
|
||||
var newLinks = [];
|
||||
while (i < selection.nodes.length) {
|
||||
var targetNode = selection.nodes[i];
|
||||
if (targetNode.inputs > 0) {
|
||||
var existingLinks = RED.nodes.filterLinks({
|
||||
source: sourceNode,
|
||||
target: targetNode,
|
||||
sourcePort: Math.min(sourceNode.outputs-1,i-1)
|
||||
})
|
||||
if (existingLinks.length === 0) {
|
||||
var newLink = {
|
||||
source: sourceNode,
|
||||
target: targetNode,
|
||||
sourcePort: Math.min(sourceNode.outputs-1,i-1)
|
||||
}
|
||||
RED.nodes.addLink(newLink);
|
||||
newLinks.push(newLink);
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (newLinks.length > 0) {
|
||||
RED.history.push({
|
||||
t: 'add',
|
||||
links: newLinks,
|
||||
dirty: RED.nodes.dirty()
|
||||
})
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
RED.actions.add("core:show-selected-node-labels", function() { setSelectedNodeLabelState(true); })
|
||||
@@ -783,7 +867,8 @@ RED.view.tools = (function() {
|
||||
RED.actions.add("core:distribute-selection-horizontally", function() { distributeSelection('h') })
|
||||
RED.actions.add("core:distribute-selection-vertically", function() { distributeSelection('v') })
|
||||
|
||||
|
||||
RED.actions.add("core:wire-series-of-nodes", function() { wireSeriesOfNodes() })
|
||||
RED.actions.add("core:wire-node-to-multiple", function() { wireNodeToMultiple() })
|
||||
|
||||
// RED.actions.add("core:add-node", function() { addNode() })
|
||||
},
|
||||
|
@@ -63,7 +63,6 @@ RED.view = (function() {
|
||||
var activeGroups = [];
|
||||
var dirtyGroups = {};
|
||||
|
||||
var selected_link = null;
|
||||
var mousedown_link = null;
|
||||
var mousedown_node = null;
|
||||
var mousedown_group = null;
|
||||
@@ -75,6 +74,8 @@ RED.view = (function() {
|
||||
var mouse_mode = 0;
|
||||
var mousedown_group_handle = null;
|
||||
var lasso = null;
|
||||
var slicePath = null;
|
||||
var slicePathLast = null;
|
||||
var ghostNode = null;
|
||||
var showStatus = false;
|
||||
var lastClickNode = null;
|
||||
@@ -129,6 +130,14 @@ RED.view = (function() {
|
||||
if (!setIds.has(node.id)) {
|
||||
set.push({n:node});
|
||||
setIds.add(node.id);
|
||||
var links = RED.nodes.getNodeLinks(node.id,PORT_TYPE_INPUT).concat(RED.nodes.getNodeLinks(node.id,PORT_TYPE_OUTPUT))
|
||||
for (var i=0,l=links.length;i<l;i++) {
|
||||
var link = links[i]
|
||||
if (link.source === node && setIds.has(link.target.id) ||
|
||||
link.target === node && setIds.has(link.source.id)) {
|
||||
selectedLinks.add(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -145,6 +154,10 @@ RED.view = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
var links = RED.nodes.getNodeLinks(node.id,PORT_TYPE_INPUT).concat(RED.nodes.getNodeLinks(node.id,PORT_TYPE_OUTPUT))
|
||||
for (var i=0,l=links.length;i<l;i++) {
|
||||
selectedLinks.remove(links[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
clear: function() {
|
||||
@@ -159,6 +172,31 @@ RED.view = (function() {
|
||||
return api;
|
||||
})();
|
||||
|
||||
var selectedLinks = (function() {
|
||||
var links = new Set();
|
||||
return {
|
||||
add: function(link) {
|
||||
links.add(link);
|
||||
link.selected = true;
|
||||
},
|
||||
remove: function(link) {
|
||||
links.delete(link);
|
||||
link.selected = false;
|
||||
},
|
||||
clear: function() {
|
||||
links.forEach(function(link) { link.selected = false })
|
||||
links.clear();
|
||||
},
|
||||
length: function() {
|
||||
return links.size;
|
||||
},
|
||||
forEach: function(func) { links.forEach(func) },
|
||||
has: function(link) { return links.has(link) },
|
||||
toArray: function() { return Array.from(links) }
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
function init() {
|
||||
|
||||
chart = $("#red-ui-workspace-chart");
|
||||
@@ -193,6 +231,12 @@ RED.view = (function() {
|
||||
}
|
||||
} else if (mouse_mode === RED.state.PANNING && d3.event.buttons !== 4) {
|
||||
resetMouseVars();
|
||||
} else if (slicePath) {
|
||||
if (d3.event.buttons !== 2) {
|
||||
slicePath.remove();
|
||||
slicePath = null;
|
||||
resetMouseVars()
|
||||
}
|
||||
}
|
||||
})
|
||||
.on("touchend", function() {
|
||||
@@ -412,26 +456,56 @@ RED.view = (function() {
|
||||
var historyEvent = result.historyEvent;
|
||||
var nn = result.node;
|
||||
|
||||
RED.nodes.add(nn);
|
||||
|
||||
var showLabel = RED.utils.getMessageProperty(RED.settings.get('editor'),"view.view-node-show-label");
|
||||
if (showLabel !== undefined && (nn._def.hasOwnProperty("showLabel")?nn._def.showLabel:true) && !nn._def.defaults.hasOwnProperty("l")) {
|
||||
nn.l = showLabel;
|
||||
}
|
||||
|
||||
var helperOffset = d3.touches(ui.helper.get(0))[0]||d3.mouse(ui.helper.get(0));
|
||||
var helperWidth = ui.helper.width();
|
||||
var helperHeight = ui.helper.height();
|
||||
var mousePos = d3.touches(this)[0]||d3.mouse(this);
|
||||
|
||||
mousePos[1] += this.scrollTop + ((nn.h/2)-helperOffset[1]);
|
||||
mousePos[0] += this.scrollLeft + ((nn.w/2)-helperOffset[0]);
|
||||
try {
|
||||
var isLink = (nn.type === "link in" || nn.type === "link out")
|
||||
var hideLabel = nn.hasOwnProperty('l')?!nn.l : isLink;
|
||||
|
||||
var label = RED.utils.getNodeLabel(nn, nn.type);
|
||||
var labelParts = getLabelParts(label, "red-ui-flow-node-label");
|
||||
if (hideLabel) {
|
||||
nn.w = node_height;
|
||||
nn.h = Math.max(node_height,(nn.outputs || 0) * 15);
|
||||
} else {
|
||||
nn.w = Math.max(node_width,20*(Math.ceil((labelParts.width+50+(nn._def.inputs>0?7:0))/20)) );
|
||||
nn.h = Math.max(6+24*labelParts.lines.length,(nn.outputs || 0) * 15, 30);
|
||||
}
|
||||
} catch(err) {
|
||||
}
|
||||
|
||||
mousePos[1] += this.scrollTop + ((helperHeight/2)-helperOffset[1]);
|
||||
mousePos[0] += this.scrollLeft + ((helperWidth/2)-helperOffset[0]);
|
||||
mousePos[1] /= scaleFactor;
|
||||
mousePos[0] /= scaleFactor;
|
||||
|
||||
if (snapGrid) {
|
||||
mousePos[0] = gridSize*(Math.ceil(mousePos[0]/gridSize));
|
||||
mousePos[1] = gridSize*(Math.ceil(mousePos[1]/gridSize));
|
||||
}
|
||||
nn.x = mousePos[0];
|
||||
nn.y = mousePos[1];
|
||||
|
||||
if (snapGrid) {
|
||||
var gridOffset = [0,0];
|
||||
var offsetLeft = nn.x-(gridSize*Math.round((nn.x-nn.w/2)/gridSize)+nn.w/2);
|
||||
var offsetRight = nn.x-(gridSize*Math.round((nn.x+nn.w/2)/gridSize)-nn.w/2);
|
||||
if (Math.abs(offsetLeft) < Math.abs(offsetRight)) {
|
||||
gridOffset[0] = offsetLeft
|
||||
} else {
|
||||
gridOffset[0] = offsetRight
|
||||
}
|
||||
gridOffset[1] = nn.y-(gridSize*Math.round(nn.y/gridSize));
|
||||
nn.x -= gridOffset[0];
|
||||
nn.y -= gridOffset[1];
|
||||
}
|
||||
|
||||
var spliceLink = $(ui.helper).data("splice");
|
||||
if (spliceLink) {
|
||||
// TODO: DRY - droppable/nodeMouseDown/canvasMouseUp/showQuickAddDialog
|
||||
@@ -452,7 +526,6 @@ RED.view = (function() {
|
||||
historyEvent.removedLinks = [spliceLink];
|
||||
}
|
||||
|
||||
RED.nodes.add(nn);
|
||||
|
||||
var group = $(ui.helper).data("group");
|
||||
if (group) {
|
||||
@@ -502,6 +575,8 @@ RED.view = (function() {
|
||||
RED.actions.add("core:cut-selection-to-internal-clipboard",function(){copySelection();deleteSelection();});
|
||||
RED.actions.add("core:paste-from-internal-clipboard",function(){importNodes(clipboard,{generateIds: true});});
|
||||
|
||||
RED.actions.add("core:detach-selected-nodes", function() { detachSelectedNodes() })
|
||||
|
||||
RED.events.on("view:selection-changed", function(selection) {
|
||||
var hasSelection = (selection.nodes && selection.nodes.length > 0);
|
||||
var hasMultipleSelection = hasSelection && selection.nodes.length > 1;
|
||||
@@ -524,6 +599,7 @@ RED.view = (function() {
|
||||
})
|
||||
|
||||
RED.actions.add("core:delete-selection",deleteSelection);
|
||||
RED.actions.add("core:delete-selection-and-reconnect",function() { deleteSelection(true) });
|
||||
RED.actions.add("core:edit-selected-node",editSelection);
|
||||
RED.actions.add("core:go-to-selection",function() {
|
||||
if (movingSet.length() > 0) {
|
||||
@@ -909,7 +985,7 @@ RED.view = (function() {
|
||||
return;
|
||||
}
|
||||
if (!mousedown_node && !mousedown_link && !mousedown_group) {
|
||||
selected_link = null;
|
||||
selectedLinks.clear();
|
||||
updateSelection();
|
||||
}
|
||||
if (mouse_mode === 0) {
|
||||
@@ -918,19 +994,18 @@ RED.view = (function() {
|
||||
lasso = null;
|
||||
}
|
||||
}
|
||||
if (mouse_mode === 0 || mouse_mode === RED.state.QUICK_JOINING) {
|
||||
if (d3.event.metaKey || d3.event.ctrlKey) {
|
||||
d3.event.stopPropagation();
|
||||
clearSelection();
|
||||
point = d3.mouse(this);
|
||||
var clickedGroup = getGroupAt(point[0],point[1]);
|
||||
if (drag_lines.length > 0) {
|
||||
clickedGroup = clickedGroup || RED.nodes.group(drag_lines[0].node.g)
|
||||
}
|
||||
showQuickAddDialog({position:point, group:clickedGroup});
|
||||
if ((mouse_mode === 0 || mouse_mode === RED.state.QUICK_JOINING) && (d3.event.touches || d3.event.button === 0) && (d3.event.metaKey || d3.event.ctrlKey)) {
|
||||
// Trigger quick add dialog
|
||||
d3.event.stopPropagation();
|
||||
clearSelection();
|
||||
point = d3.mouse(this);
|
||||
var clickedGroup = getGroupAt(point[0],point[1]);
|
||||
if (drag_lines.length > 0) {
|
||||
clickedGroup = clickedGroup || RED.nodes.group(drag_lines[0].node.g)
|
||||
}
|
||||
}
|
||||
if (mouse_mode === 0 && !(d3.event.metaKey || d3.event.ctrlKey)) {
|
||||
showQuickAddDialog({position:point, group:clickedGroup});
|
||||
} else if (mouse_mode === 0 && (d3.event.touches || d3.event.button === 0) && !(d3.event.metaKey || d3.event.ctrlKey)) {
|
||||
// Tigger lasso
|
||||
if (!touchStartTime) {
|
||||
point = d3.mouse(this);
|
||||
lasso = eventLayer.append("rect")
|
||||
@@ -945,6 +1020,13 @@ RED.view = (function() {
|
||||
.attr("class","nr-ui-view-lasso");
|
||||
d3.event.preventDefault();
|
||||
}
|
||||
} else if (mouse_mode === 0 && d3.event.button === 2 && (d3.event.metaKey || d3.event.ctrlKey)) {
|
||||
clearSelection();
|
||||
mouse_mode = RED.state.SLICING;
|
||||
point = d3.mouse(this);
|
||||
slicePath = eventLayer.append("path").attr("class","nr-ui-view-slice").attr("d",`M${point[0]} ${point[1]}`)
|
||||
slicePathLast = point;
|
||||
RED.view.redraw();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1341,6 +1423,17 @@ RED.view = (function() {
|
||||
.attr("height",h)
|
||||
;
|
||||
return;
|
||||
} else if (mouse_mode === RED.state.SLICING) {
|
||||
if (slicePath) {
|
||||
var delta = Math.max(1,Math.abs(slicePathLast[0]-mouse_position[0]))*Math.max(1,Math.abs(slicePathLast[1]-mouse_position[1]))
|
||||
if (delta > 20) {
|
||||
var currentPath = slicePath.attr("d")
|
||||
currentPath += " L"+mouse_position[0]+" "+mouse_position[1]
|
||||
slicePath.attr("d",currentPath);
|
||||
slicePathLast = mouse_position
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (mouse_mode === RED.state.SELECTING_NODE) {
|
||||
@@ -1348,7 +1441,7 @@ RED.view = (function() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mouse_mode != RED.state.QUICK_JOINING && mouse_mode != RED.state.IMPORT_DRAGGING && !mousedown_node && !mousedown_group && selected_link == null) {
|
||||
if (mouse_mode != RED.state.QUICK_JOINING && mouse_mode != RED.state.IMPORT_DRAGGING && mouse_mode != RED.state.DETACHED_DRAGGING && !mousedown_node && !mousedown_group && selectedLinks.length() === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1372,16 +1465,18 @@ RED.view = (function() {
|
||||
// Get all the wires we need to detach.
|
||||
var links = [];
|
||||
var existingLinks = [];
|
||||
if (selected_link &&
|
||||
((mousedown_port_type === PORT_TYPE_OUTPUT &&
|
||||
selected_link.source === mousedown_node &&
|
||||
selected_link.sourcePort === mousedown_port_index
|
||||
) ||
|
||||
(mousedown_port_type === PORT_TYPE_INPUT &&
|
||||
selected_link.target === mousedown_node
|
||||
))
|
||||
) {
|
||||
existingLinks = [selected_link];
|
||||
if (selectedLinks.length() > 0) {
|
||||
selectedLinks.forEach(function(link) {
|
||||
if (((mousedown_port_type === PORT_TYPE_OUTPUT &&
|
||||
link.source === mousedown_node &&
|
||||
link.sourcePort === mousedown_port_index
|
||||
) ||
|
||||
(mousedown_port_type === PORT_TYPE_INPUT &&
|
||||
link.target === mousedown_node
|
||||
))) {
|
||||
existingLinks.push(link);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
var filter;
|
||||
if (mousedown_port_type === PORT_TYPE_OUTPUT) {
|
||||
@@ -1419,7 +1514,7 @@ RED.view = (function() {
|
||||
} else if (mousedown_node && !quickAddLink) {
|
||||
showDragLines([{node:mousedown_node,port:mousedown_port_index,portType:mousedown_port_type}]);
|
||||
}
|
||||
selected_link = null;
|
||||
selectedLinks.clear();
|
||||
}
|
||||
mousePos = mouse_position;
|
||||
for (i=0;i<drag_lines.length;i++) {
|
||||
@@ -1451,7 +1546,7 @@ RED.view = (function() {
|
||||
RED.nodes.filterLinks({ target: node.n }).length === 0;
|
||||
}
|
||||
}
|
||||
} else if (mouse_mode == RED.state.MOVING_ACTIVE || mouse_mode == RED.state.IMPORT_DRAGGING) {
|
||||
} else if (mouse_mode == RED.state.MOVING_ACTIVE || mouse_mode == RED.state.IMPORT_DRAGGING || mouse_mode == RED.state.DETACHED_DRAGGING) {
|
||||
mousePos = mouse_position;
|
||||
var minX = 0;
|
||||
var minY = 0;
|
||||
@@ -1515,8 +1610,16 @@ RED.view = (function() {
|
||||
gridOffset[0] = node.n.x-(gridSize*Math.floor(node.n.x/gridSize))-gridSize/2;
|
||||
gridOffset[1] = node.n.y-(gridSize*Math.floor(node.n.y/gridSize))-gridSize/2;
|
||||
} else {
|
||||
gridOffset[0] = node.n.x-(gridSize*Math.floor((node.n.x-node.n.w/2)/gridSize)+node.n.w/2);
|
||||
gridOffset[1] = node.n.y-(gridSize*Math.floor(node.n.y/gridSize));
|
||||
var offsetLeft = node.n.x-(gridSize*Math.round((node.n.x-node.n.w/2)/gridSize)+node.n.w/2);
|
||||
var offsetRight = node.n.x-(gridSize*Math.round((node.n.x+node.n.w/2)/gridSize)-node.n.w/2);
|
||||
// gridOffset[0] = node.n.x-(gridSize*Math.floor((node.n.x-node.n.w/2)/gridSize)+node.n.w/2);
|
||||
if (Math.abs(offsetLeft) < Math.abs(offsetRight)) {
|
||||
gridOffset[0] = offsetLeft
|
||||
} else {
|
||||
gridOffset[0] = offsetRight
|
||||
}
|
||||
gridOffset[1] = node.n.y-(gridSize*Math.round(node.n.y/gridSize));
|
||||
// console.log(offsetLeft, offsetRight);
|
||||
}
|
||||
if (gridOffset[0] !== 0 || gridOffset[1] !== 0) {
|
||||
for (i = 0; i<movingSet.length(); i++) {
|
||||
@@ -1736,6 +1839,11 @@ RED.view = (function() {
|
||||
} else if (mouse_mode == RED.state.DEFAULT && mousedown_link == null && !d3.event.ctrlKey && !d3.event.metaKey ) {
|
||||
clearSelection();
|
||||
updateSelection();
|
||||
} else if (slicePath) {
|
||||
deleteSelection();
|
||||
slicePath.remove();
|
||||
slicePath = null;
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
if (mouse_mode == RED.state.MOVING_ACTIVE) {
|
||||
if (movingSet.length() > 0) {
|
||||
@@ -1807,10 +1915,30 @@ RED.view = (function() {
|
||||
// movingSet.add(mousedown_node);
|
||||
// }
|
||||
// }
|
||||
if (mouse_mode == RED.state.MOVING || mouse_mode == RED.state.MOVING_ACTIVE) {
|
||||
if (mouse_mode == RED.state.MOVING || mouse_mode == RED.state.MOVING_ACTIVE || mouse_mode == RED.state.DETACHED_DRAGGING) {
|
||||
// if (mousedown_node) {
|
||||
// delete mousedown_node.gSelected;
|
||||
// }
|
||||
if (mouse_mode === RED.state.DETACHED_DRAGGING) {
|
||||
var ns = [];
|
||||
for (var j=0;j<movingSet.length();j++) {
|
||||
var n = movingSet.get(j);
|
||||
if (n.ox !== n.n.x || n.oy !== n.n.y) {
|
||||
ns.push({n:n.n,ox:n.ox,oy:n.oy,moved:n.n.moved});
|
||||
n.n.dirty = true;
|
||||
n.n.moved = true;
|
||||
}
|
||||
}
|
||||
var detachEvent = RED.history.peek();
|
||||
// The last event in the stack *should* be a multi-event from
|
||||
// where the links were added/removed
|
||||
var historyEvent = {t:"move",nodes:ns,dirty:RED.nodes.dirty()}
|
||||
if (detachEvent.t === "multi") {
|
||||
detachEvent.events.push(historyEvent)
|
||||
} else {
|
||||
RED.history.push(historyEvent)
|
||||
}
|
||||
}
|
||||
for (i=0;i<movingSet.length();i++) {
|
||||
var node = movingSet.get(i);
|
||||
delete node.ox;
|
||||
@@ -1855,10 +1983,29 @@ RED.view = (function() {
|
||||
if (mouse_mode === RED.state.MOVING || mouse_mode === RED.state.MOVING_ACTIVE) {
|
||||
return;
|
||||
}
|
||||
if (mouse_mode === RED.state.IMPORT_DRAGGING) {
|
||||
if (mouse_mode === RED.state.DETACHED_DRAGGING) {
|
||||
for (var j=0;j<movingSet.length();j++) {
|
||||
var n = movingSet.get(j);
|
||||
n.n.x = n.ox;
|
||||
n.n.y = n.oy;
|
||||
}
|
||||
clearSelection();
|
||||
RED.history.pop();
|
||||
mouse_mode = 0;
|
||||
} else if (mouse_mode === RED.state.IMPORT_DRAGGING) {
|
||||
clearSelection();
|
||||
RED.history.pop();
|
||||
mouse_mode = 0;
|
||||
} else if (mouse_mode === RED.state.SLICING) {
|
||||
if (slicePath) {
|
||||
slicePath.remove();
|
||||
slicePath = null;
|
||||
resetMouseVars()
|
||||
}
|
||||
clearSelection();
|
||||
} else if (lasso) {
|
||||
lasso.remove();
|
||||
lasso = null;
|
||||
} else if (activeGroup) {
|
||||
exitActiveGroup()
|
||||
} else {
|
||||
@@ -1870,6 +2017,7 @@ RED.view = (function() {
|
||||
if (mouse_mode === RED.state.SELECTING_NODE && selectNodesOptions.single) {
|
||||
return;
|
||||
}
|
||||
selectedLinks.clear();
|
||||
|
||||
if (activeGroup) {
|
||||
var ag = activeGroup;
|
||||
@@ -1941,7 +2089,6 @@ RED.view = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
selected_link = null;
|
||||
if (mouse_mode !== RED.state.SELECTING_NODE) {
|
||||
updateSelection();
|
||||
}
|
||||
@@ -1956,7 +2103,7 @@ RED.view = (function() {
|
||||
n.n.selected = false;
|
||||
}
|
||||
movingSet.clear();
|
||||
selected_link = null;
|
||||
selectedLinks.clear();
|
||||
if (activeGroup) {
|
||||
activeGroup.active = false
|
||||
activeGroup.dirty = true;
|
||||
@@ -2050,12 +2197,16 @@ RED.view = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (activeFlowLinks.length === 0 && selected_link !== null && selected_link.link) {
|
||||
activeLinks.push(selected_link);
|
||||
activeLinkNodes[selected_link.source.id] = selected_link.source;
|
||||
selected_link.source.dirty = true;
|
||||
activeLinkNodes[selected_link.target.id] = selected_link.target;
|
||||
selected_link.target.dirty = true;
|
||||
if (activeFlowLinks.length === 0 && selectedLinks.length() > 0) {
|
||||
selectedLinks.forEach(function(link) {
|
||||
if (link.link) {
|
||||
activeLinks.push(link);
|
||||
activeLinkNodes[link.source.id] = link.source;
|
||||
link.source.dirty = true;
|
||||
activeLinkNodes[link.target.id] = link.target;
|
||||
link.target.dirty = true;
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
selection.flows = workspaceSelection;
|
||||
@@ -2066,6 +2217,10 @@ RED.view = (function() {
|
||||
return value.map(function(n) { return n.id })
|
||||
} else if (key === 'link') {
|
||||
return value.source.id+":"+value.sourcePort+":"+value.target.id;
|
||||
} else if (key === 'links') {
|
||||
return value.map(function(link) {
|
||||
return link.source.id+":"+link.sourcePort+":"+link.target.id;
|
||||
});
|
||||
}
|
||||
return value;
|
||||
});
|
||||
@@ -2087,7 +2242,7 @@ RED.view = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
function deleteSelection() {
|
||||
function deleteSelection(reconnectWires) {
|
||||
if (mouse_mode === RED.state.SELECTING_NODE) {
|
||||
return;
|
||||
}
|
||||
@@ -2135,7 +2290,7 @@ RED.view = (function() {
|
||||
updateActiveNodes();
|
||||
updateSelection();
|
||||
redraw();
|
||||
} else if (movingSet.length() > 0 || selected_link != null) {
|
||||
} else if (movingSet.length() > 0 || selectedLinks.length() > 0) {
|
||||
var result;
|
||||
var node;
|
||||
var removedNodes = [];
|
||||
@@ -2145,6 +2300,16 @@ RED.view = (function() {
|
||||
var removedSubflowInputs = [];
|
||||
var removedSubflowStatus;
|
||||
var subflowInstances = [];
|
||||
var historyEvents = [];
|
||||
|
||||
if (reconnectWires) {
|
||||
var reconnectResult = RED.nodes.detachNodes(movingSet.nodes())
|
||||
var addedLinks = reconnectResult.newLinks;
|
||||
if (addedLinks.length > 0) {
|
||||
historyEvents.push({ t:'add', links: addedLinks })
|
||||
}
|
||||
removedLinks = removedLinks.concat(reconnectResult.removedLinks)
|
||||
}
|
||||
|
||||
var startDirty = RED.nodes.dirty();
|
||||
var startChanged = false;
|
||||
@@ -2234,71 +2399,71 @@ RED.view = (function() {
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
}
|
||||
var historyEvent;
|
||||
|
||||
if (selected_link && selected_link.link) {
|
||||
var sourceId = selected_link.source.id;
|
||||
var targetId = selected_link.target.id;
|
||||
var sourceIdIndex = selected_link.target.links.indexOf(sourceId);
|
||||
var targetIdIndex = selected_link.source.links.indexOf(targetId);
|
||||
|
||||
historyEvent = {
|
||||
t:"multi",
|
||||
events: [
|
||||
{
|
||||
if (selectedLinks.length() > 0) {
|
||||
selectedLinks.forEach(function(link) {
|
||||
if (link.link) {
|
||||
var sourceId = link.source.id;
|
||||
var targetId = link.target.id;
|
||||
var sourceIdIndex = link.target.links.indexOf(sourceId);
|
||||
var targetIdIndex = link.source.links.indexOf(targetId);
|
||||
historyEvents.push({
|
||||
t: "edit",
|
||||
node: selected_link.source,
|
||||
changed: selected_link.source.changed,
|
||||
node: link.source,
|
||||
changed: link.source.changed,
|
||||
changes: {
|
||||
links: $.extend(true,{},{v:selected_link.source.links}).v
|
||||
links: $.extend(true,{},{v:link.source.links}).v
|
||||
}
|
||||
},
|
||||
{
|
||||
})
|
||||
historyEvents.push({
|
||||
t: "edit",
|
||||
node: selected_link.target,
|
||||
changed: selected_link.target.changed,
|
||||
node: link.target,
|
||||
changed: link.target.changed,
|
||||
changes: {
|
||||
links: $.extend(true,{},{v:selected_link.target.links}).v
|
||||
links: $.extend(true,{},{v:link.target.links}).v
|
||||
}
|
||||
}
|
||||
})
|
||||
link.source.changed = true;
|
||||
link.target.changed = true;
|
||||
link.target.links.splice(sourceIdIndex,1);
|
||||
link.source.links.splice(targetIdIndex,1);
|
||||
link.source.dirty = true;
|
||||
link.target.dirty = true;
|
||||
|
||||
],
|
||||
dirty:RED.nodes.dirty()
|
||||
}
|
||||
RED.nodes.dirty(true);
|
||||
selected_link.source.changed = true;
|
||||
selected_link.target.changed = true;
|
||||
selected_link.target.links.splice(sourceIdIndex,1);
|
||||
selected_link.source.links.splice(targetIdIndex,1);
|
||||
selected_link.source.dirty = true;
|
||||
selected_link.target.dirty = true;
|
||||
|
||||
} else {
|
||||
if (selected_link) {
|
||||
RED.nodes.removeLink(selected_link);
|
||||
removedLinks.push(selected_link);
|
||||
}
|
||||
RED.nodes.dirty(true);
|
||||
historyEvent = {
|
||||
t:"delete",
|
||||
nodes:removedNodes,
|
||||
links:removedLinks,
|
||||
groups: removedGroups,
|
||||
subflowOutputs:removedSubflowOutputs,
|
||||
subflowInputs:removedSubflowInputs,
|
||||
subflow: {
|
||||
id: activeSubflow?activeSubflow.id:undefined,
|
||||
instances: subflowInstances
|
||||
},
|
||||
dirty:startDirty
|
||||
};
|
||||
if (removedSubflowStatus) {
|
||||
historyEvent.subflow.status = removedSubflowStatus;
|
||||
}
|
||||
} else {
|
||||
RED.nodes.removeLink(link);
|
||||
removedLinks.push(link);
|
||||
}
|
||||
})
|
||||
}
|
||||
RED.nodes.dirty(true);
|
||||
var historyEvent = {
|
||||
t:"delete",
|
||||
nodes:removedNodes,
|
||||
links:removedLinks,
|
||||
groups: removedGroups,
|
||||
subflowOutputs:removedSubflowOutputs,
|
||||
subflowInputs:removedSubflowInputs,
|
||||
subflow: {
|
||||
id: activeSubflow?activeSubflow.id:undefined,
|
||||
instances: subflowInstances
|
||||
},
|
||||
dirty:startDirty
|
||||
};
|
||||
if (removedSubflowStatus) {
|
||||
historyEvent.subflow.status = removedSubflowStatus;
|
||||
}
|
||||
if (historyEvents.length > 0) {
|
||||
historyEvents.unshift(historyEvent);
|
||||
RED.history.push({
|
||||
t:"multi",
|
||||
events: historyEvents
|
||||
})
|
||||
} else {
|
||||
RED.history.push(historyEvent);
|
||||
}
|
||||
RED.history.push(historyEvent);
|
||||
|
||||
selected_link = null;
|
||||
selectedLinks.clear();
|
||||
updateActiveNodes();
|
||||
updateSelection();
|
||||
redraw();
|
||||
@@ -2375,6 +2540,28 @@ RED.view = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function detachSelectedNodes() {
|
||||
var selection = RED.view.selection();
|
||||
if (selection.nodes) {
|
||||
const {newLinks, removedLinks} = RED.nodes.detachNodes(selection.nodes);
|
||||
if (removedLinks.length || newLinks.length) {
|
||||
RED.history.push({
|
||||
t: "multi",
|
||||
events: [
|
||||
{ t:'delete', links: removedLinks },
|
||||
{ t:'add', links: newLinks }
|
||||
],
|
||||
dirty: RED.nodes.dirty()
|
||||
})
|
||||
RED.nodes.dirty(true)
|
||||
}
|
||||
prepareDrag([selection.nodes[0].x,selection.nodes[0].y]);
|
||||
mouse_mode = RED.state.DETACHED_DRAGGING;
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
}
|
||||
|
||||
function calculateTextWidth(str, className) {
|
||||
var result = convertLineBreakCharacter(str);
|
||||
var width = 0;
|
||||
@@ -2461,7 +2648,7 @@ RED.view = (function() {
|
||||
activeHoverGroup.hovered = false;
|
||||
activeHoverGroup = null;
|
||||
}
|
||||
d3.select(".red-ui-flow-link-splice").classed("red-ui-flow-link-splice",false);
|
||||
d3.selectAll(".red-ui-flow-link-splice").classed("red-ui-flow-link-splice",false);
|
||||
if (spliceTimer) {
|
||||
clearTimeout(spliceTimer);
|
||||
spliceTimer = null;
|
||||
@@ -2709,10 +2896,13 @@ RED.view = (function() {
|
||||
} else {
|
||||
resetMouseVars();
|
||||
}
|
||||
selected_link = select_link;
|
||||
mousedown_link = select_link;
|
||||
if (select_link) {
|
||||
selectedLinks.clear();
|
||||
selectedLinks.add(select_link);
|
||||
updateSelection();
|
||||
} else {
|
||||
selectedLinks.clear();
|
||||
}
|
||||
}
|
||||
redraw();
|
||||
@@ -2721,7 +2911,10 @@ RED.view = (function() {
|
||||
|
||||
resetMouseVars();
|
||||
hideDragLines();
|
||||
selected_link = select_link;
|
||||
if (select_link) {
|
||||
selectedLinks.clear();
|
||||
selectedLinks.add(select_link);
|
||||
}
|
||||
mousedown_link = select_link;
|
||||
if (select_link) {
|
||||
updateSelection();
|
||||
@@ -2894,10 +3087,13 @@ RED.view = (function() {
|
||||
msn.dx = msn.n.x-mouse[0];
|
||||
msn.dy = msn.n.y-mouse[1];
|
||||
}
|
||||
|
||||
mouse_offset = d3.mouse(document.body);
|
||||
if (isNaN(mouse_offset[0])) {
|
||||
mouse_offset = d3.touches(document.body)[0];
|
||||
try {
|
||||
mouse_offset = d3.mouse(document.body);
|
||||
if (isNaN(mouse_offset[0])) {
|
||||
mouse_offset = d3.touches(document.body)[0];
|
||||
}
|
||||
} catch(err) {
|
||||
mouse_offset = [0,0]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2978,7 +3174,7 @@ RED.view = (function() {
|
||||
//var touch0 = d3.event;
|
||||
//var pos = [touch0.pageX,touch0.pageY];
|
||||
//RED.touch.radialMenu.show(d3.select(this),pos);
|
||||
if (mouse_mode == RED.state.IMPORT_DRAGGING) {
|
||||
if (mouse_mode == RED.state.IMPORT_DRAGGING || mouse_mode == RED.state.DETACHED_DRAGGING) {
|
||||
var historyEvent = RED.history.peek();
|
||||
if (activeSpliceLink) {
|
||||
// TODO: DRY - droppable/nodeMouseDown/canvasMouseUp
|
||||
@@ -3016,6 +3212,18 @@ RED.view = (function() {
|
||||
activeHoverGroup = null;
|
||||
}
|
||||
|
||||
if (mouse_mode == RED.state.DETACHED_DRAGGING) {
|
||||
var ns = [];
|
||||
for (var j=0;j<movingSet.length();j++) {
|
||||
var n = movingSet.get(j);
|
||||
if (n.ox !== n.n.x || n.oy !== n.n.y) {
|
||||
ns.push({n:n.n,ox:n.ox,oy:n.oy,moved:n.n.moved});
|
||||
n.n.dirty = true;
|
||||
n.n.moved = true;
|
||||
}
|
||||
}
|
||||
RED.history.replace({t:"multi",events:[historyEvent,{t:"move",nodes:ns}],dirty: historyEvent.dirty})
|
||||
}
|
||||
|
||||
updateSelection();
|
||||
RED.nodes.dirty(true);
|
||||
@@ -3184,7 +3392,9 @@ RED.view = (function() {
|
||||
// }
|
||||
// } else
|
||||
if (d3.event.shiftKey) {
|
||||
clearSelection();
|
||||
if (!(d3.event.ctrlKey||d3.event.metaKey)) {
|
||||
clearSelection();
|
||||
}
|
||||
var clickPosition = (d3.event.offsetX/scaleFactor - mousedown_node.x)
|
||||
var edgeDelta = (mousedown_node.w/2) - Math.abs(clickPosition);
|
||||
var cnodes;
|
||||
@@ -3212,7 +3422,7 @@ RED.view = (function() {
|
||||
mousedown_node.selected = true;
|
||||
movingSet.add(mousedown_node);
|
||||
}
|
||||
selected_link = null;
|
||||
// selectedLinks.clear();
|
||||
if (d3.event.button != 2) {
|
||||
mouse_mode = RED.state.MOVING;
|
||||
var mouse = d3.touches(this)[0]||d3.mouse(this);
|
||||
@@ -3338,19 +3548,35 @@ RED.view = (function() {
|
||||
d3.event.stopPropagation();
|
||||
return;
|
||||
}
|
||||
mousedown_link = d;
|
||||
clearSelection();
|
||||
selected_link = mousedown_link;
|
||||
updateSelection();
|
||||
redraw();
|
||||
focusView();
|
||||
d3.event.stopPropagation();
|
||||
if (d3.event.metaKey || d3.event.ctrlKey) {
|
||||
d3.select(this).classed("red-ui-flow-link-splice",true);
|
||||
var point = d3.mouse(this);
|
||||
var clickedGroup = getGroupAt(point[0],point[1]);
|
||||
showQuickAddDialog({position:point, splice:selected_link, group:clickedGroup});
|
||||
}
|
||||
if (d3.event.button === 2) {
|
||||
return
|
||||
}
|
||||
mousedown_link = d;
|
||||
|
||||
if (!(d3.event.metaKey || d3.event.ctrlKey)) {
|
||||
clearSelection();
|
||||
}
|
||||
if (d3.event.metaKey || d3.event.ctrlKey) {
|
||||
if (!selectedLinks.has(mousedown_link)) {
|
||||
selectedLinks.add(mousedown_link);
|
||||
} else {
|
||||
if (selectedLinks.length() !== 1) {
|
||||
selectedLinks.remove(mousedown_link);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
selectedLinks.add(mousedown_link);
|
||||
}
|
||||
updateSelection();
|
||||
redraw();
|
||||
focusView();
|
||||
d3.event.stopPropagation();
|
||||
if (!mousedown_link.link && movingSet.length() === 0 && (d3.event.touches || d3.event.button === 0) && selectedLinks.length() === 1 && selectedLinks.has(mousedown_link) && (d3.event.metaKey || d3.event.ctrlKey)) {
|
||||
d3.select(this).classed("red-ui-flow-link-splice",true);
|
||||
var point = d3.mouse(this);
|
||||
var clickedGroup = getGroupAt(point[0],point[1]);
|
||||
showQuickAddDialog({position:point, splice:mousedown_link, group:clickedGroup});
|
||||
}
|
||||
}
|
||||
function linkTouchStart(d) {
|
||||
if (mouse_mode === RED.state.SELECTING_NODE) {
|
||||
@@ -3359,7 +3585,8 @@ RED.view = (function() {
|
||||
}
|
||||
mousedown_link = d;
|
||||
clearSelection();
|
||||
selected_link = mousedown_link;
|
||||
selectedLinks.clear();
|
||||
selectedLinks.add(mousedown_link);
|
||||
updateSelection();
|
||||
redraw();
|
||||
focusView();
|
||||
@@ -3595,7 +3822,7 @@ RED.view = (function() {
|
||||
function showTouchMenu(obj,pos) {
|
||||
var mdn = mousedown_node;
|
||||
var options = [];
|
||||
options.push({name:"delete",disabled:(movingSet.length()===0 && selected_link === null),onselect:function() {deleteSelection();}});
|
||||
options.push({name:"delete",disabled:(movingSet.length()===0 && selectedLinks.length() === 0),onselect:function() {deleteSelection();}});
|
||||
options.push({name:"cut",disabled:(movingSet.length()===0),onselect:function() {copySelection();deleteSelection();}});
|
||||
options.push({name:"copy",disabled:(movingSet.length()===0),onselect:function() {copySelection();}});
|
||||
options.push({name:"paste",disabled:(clipboard.length===0),onselect:function() {importNodes(clipboard, {generateIds: true, touchImport: true});}});
|
||||
@@ -4471,6 +4698,13 @@ RED.view = (function() {
|
||||
d3.select(pathBack)
|
||||
.on("mousedown",linkMouseDown)
|
||||
.on("touchstart",linkTouchStart)
|
||||
.on("mousemove", function(d) {
|
||||
if (mouse_mode === RED.state.SLICING) {
|
||||
selectedLinks.add(d)
|
||||
l.classed("red-ui-flow-link-splice",true)
|
||||
redraw()
|
||||
}
|
||||
})
|
||||
|
||||
var pathOutline = document.createElementNS("http://www.w3.org/2000/svg","path");
|
||||
pathOutline.__data__ = d;
|
||||
@@ -4491,7 +4725,7 @@ RED.view = (function() {
|
||||
link.exit().remove();
|
||||
link.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 (d.added || d.selected || dirtyNodes[d.source.id] || dirtyNodes[d.target.id]) {
|
||||
var numOutputs = d.source.outputs || 1;
|
||||
var sourcePort = d.sourcePort || 0;
|
||||
var y = -((numOutputs-1)/2)*13 +13*sourcePort;
|
||||
@@ -4514,7 +4748,8 @@ RED.view = (function() {
|
||||
this.__pathLine__.classList.toggle("red-ui-flow-node-disabled",!!(d.source.d || d.target.d));
|
||||
this.__pathLine__.classList.toggle("red-ui-flow-subflow-link", !d.link && activeSubflow);
|
||||
}
|
||||
this.classList.toggle("red-ui-flow-link-selected", !!(d===selected_link||d.selected));
|
||||
|
||||
this.classList.toggle("red-ui-flow-link-selected", !!d.selected);
|
||||
|
||||
var connectedToUnknown = !!(d.target.type == "unknown" || d.source.type == "unknown");
|
||||
this.classList.toggle("red-ui-flow-link-unknown",!!(d.target.type == "unknown" || d.source.type == "unknown"))
|
||||
@@ -5222,8 +5457,9 @@ RED.view = (function() {
|
||||
if (allNodes.size > 0) {
|
||||
selection.nodes = Array.from(allNodes);
|
||||
}
|
||||
if (selected_link != null) {
|
||||
selection.link = selected_link;
|
||||
if (selectedLinks.length() > 0) {
|
||||
selection.links = selectedLinks.toArray();
|
||||
selection.link = selection.links[0];
|
||||
}
|
||||
return selection;
|
||||
}
|
||||
|
@@ -66,7 +66,7 @@ RED.workspaces = (function() {
|
||||
var tabId = RED.nodes.id();
|
||||
do {
|
||||
workspaceIndex += 1;
|
||||
} while ($("#red-ui-workspace-tabs a[title='"+RED._('workspace.defaultName',{number:workspaceIndex})+"']").size() !== 0);
|
||||
} while ($("#red-ui-workspace-tabs li[flowname='"+RED._('workspace.defaultName',{number:workspaceIndex})+"']").size() !== 0);
|
||||
|
||||
ws = {
|
||||
type: "tab",
|
||||
@@ -79,12 +79,15 @@ RED.workspaces = (function() {
|
||||
};
|
||||
RED.nodes.addWorkspace(ws,targetIndex);
|
||||
workspace_tabs.addTab(ws,targetIndex);
|
||||
|
||||
workspace_tabs.activateTab(tabId);
|
||||
if (!skipHistoryEntry) {
|
||||
RED.history.push({t:'add',workspaces:[ws],dirty:RED.nodes.dirty()});
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
}
|
||||
$("#red-ui-tab-"+(ws.id.replace(".","-"))).attr("flowname",ws.label)
|
||||
|
||||
RED.view.focus();
|
||||
return ws;
|
||||
}
|
||||
@@ -208,10 +211,20 @@ RED.workspaces = (function() {
|
||||
},
|
||||
onhide: function(tab) {
|
||||
hideStack.push(tab.id);
|
||||
|
||||
var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
|
||||
hiddenTabs[tab.id] = true;
|
||||
RED.settings.setLocal("hiddenTabs",JSON.stringify(hiddenTabs));
|
||||
|
||||
RED.events.emit("workspace:hide",{workspace: tab.id})
|
||||
},
|
||||
onshow: function(tab) {
|
||||
removeFromHideStack(tab.id);
|
||||
|
||||
var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
|
||||
delete hiddenTabs[tab.id];
|
||||
RED.settings.setLocal("hiddenTabs",JSON.stringify(hiddenTabs));
|
||||
|
||||
RED.events.emit("workspace:show",{workspace: tab.id})
|
||||
},
|
||||
minimumActiveTabWidth: 150,
|
||||
@@ -542,9 +555,6 @@ RED.workspaces = (function() {
|
||||
}
|
||||
if (workspace_tabs.contains(id)) {
|
||||
workspace_tabs.hideTab(id);
|
||||
var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
|
||||
hiddenTabs[id] = true;
|
||||
RED.settings.setLocal("hiddenTabs",JSON.stringify(hiddenTabs));
|
||||
}
|
||||
},
|
||||
isHidden: function(id) {
|
||||
@@ -572,14 +582,11 @@ RED.workspaces = (function() {
|
||||
}
|
||||
workspace_tabs.activateTab(id);
|
||||
}
|
||||
var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
|
||||
delete hiddenTabs[id];
|
||||
RED.settings.setLocal("hiddenTabs",JSON.stringify(hiddenTabs));
|
||||
},
|
||||
refresh: function() {
|
||||
RED.nodes.eachWorkspace(function(ws) {
|
||||
workspace_tabs.renameTab(ws.id,ws.label);
|
||||
|
||||
$("#red-ui-tab-"+(ws.id.replace(".","-"))).attr("flowname",ws.label)
|
||||
})
|
||||
RED.nodes.eachSubflow(function(sf) {
|
||||
if (workspace_tabs.contains(sf.id)) {
|
||||
|
Reference in New Issue
Block a user