Merge branch 'node-red:dev' into dev

This commit is contained in:
ralphwetzel
2022-01-13 23:36:23 +01:00
committed by GitHub
84 changed files with 1920 additions and 3479 deletions

View File

@@ -894,6 +894,8 @@
"addTitle": "add an item"
},
"search": {
"history": "Search history",
"clear": "clear all",
"empty": "No matches found",
"addNode": "add a node..."
},

View File

@@ -59,6 +59,8 @@
"hideOtherFlows": "他のフローを非表示",
"showAllFlows": "全てのフローを表示",
"hideAllFlows": "全てのフローを非表示",
"hiddenFlows": "__count__ 個の非表示のフロー一覧",
"hiddenFlows_plural": "__count__ 個の非表示のフロー一覧",
"showLastHiddenFlow": "最後に非表示にしたフローを表示",
"listFlows": "フロー一覧",
"listSubflows": "サブフロー一覧",
@@ -669,7 +671,8 @@
"unusedConfigNodes": "未使用の設定ノード",
"invalidNodes": "不正なノード",
"uknownNodes": "未知のノード",
"unusedSubflows": "未使用のサブフロー"
"unusedSubflows": "未使用のサブフロー",
"hiddenFlows": "非表示のフロー"
}
},
"help": {

View File

@@ -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 = [];

View File

@@ -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",

View File

@@ -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,

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -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));

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -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);

View File

@@ -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() })
},

View File

@@ -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;
}

View File

@@ -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)) {

View File

@@ -356,10 +356,6 @@ button.red-ui-button-small
background: $secondary-background;
}
#red-ui-clipboard-hidden {
position: absolute;
top: -3000px;
}
.form-row .red-ui-editor-node-label-form-row {
margin: 5px 0 0 50px;
label {

View File

@@ -21,6 +21,13 @@
stroke-dasharray: 10 5;
}
.nr-ui-view-slice {
stroke-width: 1px;
stroke: $view-lasso-stroke;
fill: none;
stroke-dasharray: 10 5;
}
.node_label_italic, // deprecated: use red-ui-flow-node-label-italic
.red-ui-flow-node-label-italic {
font-style: italic;

View File

@@ -204,6 +204,28 @@
font-style: italic;
color: $form-placeholder-color;
}
.red-ui-search-history {
button {
display: none;
position: absolute;
top: 8px;
right: 7px;
}
&:hover button {
display: inline;
}
}
.red-ui-search-historyHeader {
button {
position: absolute;
top: 10px;
right: 7px;
}
}
.red-ui-search-history-result {
}
.red-ui-search-result-action {
color: $primary-text-color;

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View File

@@ -1,12 +1,12 @@
export default {
version: "2.1.0",
version: "2.2.0",
steps: [
{
titleIcon: "fa fa-map-o",
title: {
"en-US": "Welcome to Node-RED 2.1!",
"ja": "Node-RED 2.1へようこそ!"
},
"en-US": "Welcome to Node-RED 2.2!",
"ja": "Node-RED 2.2へようこそ!"
},
description: {
"en-US": "Let's take a moment to discover the new features in this release.",
"ja": "本リリースの新機能を見つけてみましょう。"
@@ -14,215 +14,84 @@ export default {
},
{
title: {
"en-US": "A new Tour Guide",
"ja": "新しいツアーガイド"
"en-US": "Search history",
},
description: {
"en-US": "<p>First, as you've already found, we now have this tour of new features. We'll only show the tour the first time you open the editor for each new version of Node-RED.</p>" +
"<p>You can choose not to see this tour in the future by disabling it under the View tab of User Settings.</p>",
"ja": "<p>最初に、既に見つけている様に、新機能の本ツアーがあります。本ツアーは、新バージョンのNode-REDフローエディタを初めて開いた時のみ表示されます。</p>" +
"<p>ユーザ設定の表示タブの中で、この機能を無効化することで、本ツアーを表示しないようにすることもできます。</p>"
}
},
{
title: {
"en-US": "New Edit menu",
"ja": "新しい編集メニュー"
"en-US": "<p>The Search dialog now keeps a history of your searches, making it easier to go back to a previous search.</p>"
},
prepare() {
$("#red-ui-header-button-sidemenu").trigger("click");
$("#menu-item-edit-menu").parent().addClass("open");
element: "#red-ui-search .red-ui-searchBox-form",
prepare(done) {
RED.search.show();
setTimeout(done,400);
},
complete() {
$("#menu-item-edit-menu").parent().removeClass("open");
},
element: "#menu-item-edit-menu-submenu",
interactive: false,
direction: "left",
description: {
"en-US": "<p>The main menu has been updated with a new 'Edit' section. This includes all of the familar options, like cut/paste and undo/redo.</p>" +
"<p>The menu now displays keyboard shortcuts for the options.</p>",
"ja": "<p>メインメニューに「編集」セクションが追加されました。本セクションには、切り取り/貼り付けや、変更操作を戻す/やり直しの様な使い慣れたオプションが含まれています。</p>" +
"<p>本メニューには、オプションのためのキーボードショートカットも表示されるようになりました。</p>"
}
},
{
title: {
"en-US": "Arranging nodes",
"ja": "ノードの配置"
},
prepare() {
$("#red-ui-header-button-sidemenu").trigger("click");
$("#menu-item-arrange-menu").parent().addClass("open");
},
complete() {
$("#menu-item-arrange-menu").parent().removeClass("open");
},
element: "#menu-item-arrange-menu-submenu",
interactive: false,
direction: "left",
description: {
"en-US": "<p>The new 'Arrange' section of the menu provides new options to help arrange your nodes. You can align them to a common edge, spread them out evenly or change their order.</p>",
"ja": "<p>メニューの新しい「配置」セクションには、ノードの配置を助ける新しいオプションが提供されています。ノードの端を揃えたり、均等に配置したり、表示順序を変更したりできます。</p>"
}
},
{
title: {
"en-US": "Hiding tabs",
"ja": "タブの非表示"
},
element: "#red-ui-workspace-tabs > li.active",
description: {
"en-US": '<p>Tabs can now be hidden by clicking their <i class="fa fa-times"></i> icon.</p><p>The Info Sidebar will still list all of your tabs, and tell you which ones are currently hidden.',
"ja": '<p><i class="fa fa-times"></i> アイコンをクリックすることで、タブを非表示にできます。</p><p>情報サイドバーには、全てのタブが一覧表示されており、現在非表示になっているタブを確認できます。'
},
interactive: false,
prepare() {
$("#red-ui-workspace-tabs > li.active .red-ui-tab-close").css("display","block");
},
complete() {
$("#red-ui-workspace-tabs > li.active .red-ui-tab-close").css("display","");
}
},
{
title: {
"en-US": "Tab menu",
"ja": "タブメニュー"
},
element: "#red-ui-workspace-tabs-menu",
description: {
"en-US": "<p>The new tab menu also provides lots of new options for your tabs.</p>",
"ja": "<p>新しいタブメニューには、タブに関する沢山の新しいオプションが提供されています。</p>"
},
interactive: false,
direction: "left",
prepare() {
$("#red-ui-workspace > .red-ui-tabs > .red-ui-tabs-menu a").trigger("click");
},
complete() {
$(document).trigger("click");
}
},
{
title: {
"en-US": "Flow and Group level environment variables",
"ja": "フローとグループの環境変数"
},
element: "#red-ui-workspace-tabs > li.active",
interactive: false,
description: {
"en-US": "<p>Flows and Groups can now have their own environment variables that can be referenced by nodes inside them.</p>",
"ja": "<p>フローとグループには、内部のノードから参照できる環境変数を設定できるようになりました。</p>"
}
},
{
prepare(done) {
RED.editor.editFlow(RED.nodes.workspace(RED.workspaces.active()),"editor-tab-envProperties");
setTimeout(done,700);
},
element: "#red-ui-tab-editor-tab-envProperties-link-button",
description: {
"en-US": "<p>Their edit dialogs have a new Environment Variables section.</p>",
"ja": "<p>編集ダイアログに環境変数セクションが追加されました。</p>"
}
},
{
element: ".node-input-env-container-row",
direction: "left",
description: {
"en-US": '<p>The environment variables are listed in this table and new ones can be added by clicking the <i class="fa fa-plus"></i> button.</p>',
"ja": '<p>この表に環境変数が一覧表示されており、<i class="fa fa-plus"></i>ボタンをクリックすることで新しい変数を追加できます。</p>'
},
complete(done) {
$("#node-dialog-cancel").trigger("click");
setTimeout(done,500);
}
},
{
title: {
"en-US": "Link Call node added",
"ja": "Link Callードを追加"
},
prepare(done) {
this.paletteWasClosed = $("#red-ui-main-container").hasClass("red-ui-palette-closed");
RED.actions.invoke("core:toggle-palette",true)
$('[data-palette-type="link call"]')[0].scrollIntoView({block:"center"})
setTimeout(done,100);
},
element: '[data-palette-type="link call"]',
direction: "right",
description: {
"en-US": "<p>The <code>Link Call</code> node lets you call another flow that begins with a <code>Link In</code> node and get the result back when the message reaches a <code>Link Out</code> node.</p>",
"ja": "<p><code>Link Call</code>ノードを用いることで、<code>Link In</code>ノードから始まるフローを呼び出し、<code>Link Out</code>ノードに到達した時に、結果を取得できます。</p>"
}
},
{
title: {
"en-US": "MQTT nodes support dynamic connections",
"ja": "MQTTードが動的接続をサポート"
},
prepare(done) {
$('[data-palette-type="mqtt out"]')[0].scrollIntoView({block:"center"})
setTimeout(done,100);
},
element: '[data-palette-type="mqtt out"]',
direction: "right",
description: {
"en-US": '<p>The <code>MQTT</code> nodes now support creating their connections and subscriptions dynamically.</p>',
"ja": '<p><code>MQTT</code>ノードは、動的な接続や購読ができるようになりました。</p>'
RED.search.hide();
},
},
{
title: {
"en-US": "File nodes renamed",
"ja": "ファイルノードの名前変更"
"en-US": "New wiring actions",
},
prepare(done) {
$('[data-palette-type="file"]')[0].scrollIntoView({block:"center"});
setTimeout(done,100);
},
complete() {
if (this.paletteWasClosed) {
RED.actions.invoke("core:toggle-palette",false)
}
},
element: '[data-palette-type="file"]',
direction: "right",
// image: "images/",
description: {
"en-US": "<p>The file nodes have been renamed to make it clearer which node does what.</p>",
"ja": "<p>fileードの名前が変更され、どのードが何を行うかが明確になりました。</p>"
"en-US": `<p>A pair of new actions have been added to help with wiring nodes together:</p>
<ul>
<li><b><code>Wire Series Of Nodes</code></b> - adds a wire (if necessary) between each pair of nodes in the order they were selected.</li>
<li><b><code>Wire Node To Multiple</code></b> - wires the first node selected to all of the other selected nodes.</li>
</ul>
<p>Actions can be accessed from the Action List in the main menu.</p>`
},
},
{
title: {
"en-US": "Deleting nodes and reconnecting wires",
},
image: "images/delete-repair.gif",
description: {
"en-US": `<p>It is now possible to delete a selection of nodes and automatically repair the wiring behind them.</p>
<p>This is really useful if you want to remove a node from the middle of the flow.</p>
<p>Hold the Ctrl (or Cmd) key when you press Delete and the nodes will be gone and the wires repaired.</p>
`
}
},
{
title: {
"en-US": "Deep copy option on Change node",
"ja": "Changeードのディープコピーオプション"
},
prepare(done) {
var def = RED.nodes.getType('change');
RED.editor.edit({id:"test",type:"change",rules:[{t:"set",p:"payload",pt:"msg", tot:"msg",to:"anotherProperty"}],_def:def, _:def._});
setTimeout(done,700);
},
complete(done) {
$("#node-dialog-cancel").trigger("click");
setTimeout(done,500);
},
element: function() {
return $(".node-input-rule-property-deepCopy").next();
"en-US": "Detaching nodes from a flow",
},
image: "images/detach-repair.gif",
description: {
"en-US": "<p>The Set rule has a new option to create a deep copy of the value. This ensures a complete copy is made, rather than using a reference.</p>",
"ja": "<p>値を代入に、値のディープコピーを作成するオプションが追加されました。これによって参照ではなく、完全なコピーが作成されます。</p>"
"en-US": `<p>If you want to remove a node from a flow without deleting it,
you can use the <b><code>Detach Selected Nodes</code></b> action.</p>
<p>The nodes will be removed from their flow, the wiring repaired behind them, and then attached to the mouse
so you can drop them wherever you want in the workspace.</p>
<p>There isn't a default keyboard shortcut assigned for this new action, but
you can add your own via the Keyboard pane of the main Settings dialog.</p>`
}
},
{
title: {
"en-US": "And that's not all...",
"ja": "これが全てではありません..."
"en-US": "More wiring tricks",
},
image: "images/slice.gif",
description: {
"en-US": "<p>There are many more smaller changes, including:</p><ul><li>Auto-complete suggestions in the <code>msg</code> TypedInput.</li><li>Support for <code>msg.resetTimeout</code> in the <code>Join</code> node.</li><li>Pushing messages to the front of the queue in the <code>Delay</code> node's rate limiting mode.</li><li>An optional second output on the <code>Delay</code> node for rate limited messages.</li></ul>",
"ja": "<p>以下の様な小さな変更が沢山あります:</p><ul><li><code>msg</code> TypedInputの自動補完提案</li><li><code>Join</code>ノードで<code>msg.resetTimeout</code>のサポート</li><li><code>Delay</code>ノードの流量制御モードにおいて先頭メッセージをキューに追加</li><li><code>Delay</code>ードで流量制限されたメッセージ向けの任意の2つ目の出力</li></ul>"
"en-US": `<p>A couple more wiring tricks to share.</p>
<p>You can now select multiple wires by holding the Ctrl (or Cmd) key
when clicking on a wire. This makes it easier to delete multiple wires in one go.</p>
<p>If you hold the Ctrl (or Cmd) key, then click and drag with the right-hand mouse button,
you can slice through wires to remove them.</p>`
}
},
{
title: {
"en-US": "Node Updates",
},
// image: "images/",
description: {
"en-US": `<ul>
<li>The JSON node will now handle parsing Buffer payloads</li>
<li>The TCP Client nodes support TLS connections</li>
<li>The WebSocket node allows you to specify a sub-protocol when connecting</li>
</ul>`
}
}
]