mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Merge pull request #4320 from node-red/dev
Sync `dev` to `master` for 3.1.0 release
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
/**
|
||||
/**
|
||||
* An API for undo / redo history buffer
|
||||
* @namespace RED.history
|
||||
*/
|
||||
@@ -378,7 +378,8 @@ RED.history = (function() {
|
||||
if (ev.addToGroup) {
|
||||
RED.group.removeFromGroup(ev.addToGroup,ev.nodes.map(function(n) { return n.n }),false);
|
||||
inverseEv.removeFromGroup = ev.addToGroup;
|
||||
} else if (ev.removeFromGroup) {
|
||||
}
|
||||
if (ev.removeFromGroup) {
|
||||
RED.group.addToGroup(ev.removeFromGroup,ev.nodes.map(function(n) { return n.n }));
|
||||
inverseEv.addToGroup = ev.removeFromGroup;
|
||||
}
|
||||
@@ -421,6 +422,9 @@ RED.history = (function() {
|
||||
ev.node[i] = ev.changes[i];
|
||||
}
|
||||
}
|
||||
ev.node.dirty = true;
|
||||
ev.node.changed = ev.changed;
|
||||
|
||||
var eventType;
|
||||
switch(ev.node.type) {
|
||||
case 'tab': eventType = "flows"; break;
|
||||
@@ -434,7 +438,9 @@ RED.history = (function() {
|
||||
|
||||
if (ev.node.type === 'tab' && ev.changes.hasOwnProperty('disabled')) {
|
||||
$("#red-ui-tab-"+(ev.node.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!ev.node.disabled);
|
||||
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!ev.node.disabled);
|
||||
}
|
||||
if (ev.node.type === 'tab' && ev.changes.hasOwnProperty('locked')) {
|
||||
$("#red-ui-tab-"+(ev.node.id.replace(".","-"))).toggleClass('red-ui-workspace-locked',!!ev.node.locked);
|
||||
}
|
||||
if (ev.subflow) {
|
||||
inverseEv.subflow = {};
|
||||
@@ -509,8 +515,6 @@ RED.history = (function() {
|
||||
inverseEv.links.push(ev.createdLinks[i]);
|
||||
}
|
||||
}
|
||||
ev.node.dirty = true;
|
||||
ev.node.changed = ev.changed;
|
||||
} else if (ev.t == "createSubflow") {
|
||||
inverseEv = {
|
||||
t: "deleteSubflow",
|
||||
@@ -646,6 +650,12 @@ RED.history = (function() {
|
||||
ev.groups[i].nodes = [];
|
||||
RED.nodes.addGroup(ev.groups[i]);
|
||||
RED.group.addToGroup(ev.groups[i],nodes);
|
||||
if (ev.groups[i].g) {
|
||||
const parentGroup = RED.nodes.group(ev.groups[i].g)
|
||||
if (parentGroup) {
|
||||
RED.group.addToGroup(parentGroup, ev.groups[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (ev.t == "addToGroup") {
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
* @namespace RED.nodes
|
||||
*/
|
||||
RED.nodes = (function() {
|
||||
|
||||
var PORT_TYPE_INPUT = 1;
|
||||
var PORT_TYPE_OUTPUT = 0;
|
||||
|
||||
@@ -47,6 +46,9 @@ RED.nodes = (function() {
|
||||
|
||||
function setDirty(d) {
|
||||
dirty = d;
|
||||
if (!d) {
|
||||
allNodes.clearState()
|
||||
}
|
||||
RED.events.emit("workspace:dirty",{dirty:dirty});
|
||||
}
|
||||
|
||||
@@ -63,12 +65,12 @@ RED.nodes = (function() {
|
||||
defaults: {
|
||||
label: {value:""},
|
||||
disabled: {value: false},
|
||||
locked: {value: false},
|
||||
info: {value: ""},
|
||||
env: {value: []}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var exports = {
|
||||
setModulePendingUpdated: function(module,version) {
|
||||
moduleList[module].pending_version = version;
|
||||
@@ -238,22 +240,72 @@ RED.nodes = (function() {
|
||||
|
||||
// allNodes holds information about the Flow nodes.
|
||||
var allNodes = (function() {
|
||||
// Map node.id -> node
|
||||
var nodes = {};
|
||||
// Map tab.id -> Array of nodes on that tab
|
||||
var tabMap = {};
|
||||
// Map tab.id -> Set of dirty object ids on that tab
|
||||
var tabDirtyMap = {};
|
||||
// Map tab.id -> Set of object ids of things deleted from the tab that weren't otherwise dirty
|
||||
var tabDeletedNodesMap = {};
|
||||
// Set of object ids of things added to a tab after initial import
|
||||
var addedDirtyObjects = new Set()
|
||||
|
||||
function changeCollectionDepth(tabNodes, toMove, direction, singleStep) {
|
||||
const result = []
|
||||
const moved = new Set();
|
||||
const startIndex = direction ? tabNodes.length - 1 : 0
|
||||
const endIndex = direction ? -1 : tabNodes.length
|
||||
const step = direction ? -1 : 1
|
||||
let target = startIndex // Only used for all-the-way moves
|
||||
for (let i = startIndex; i != endIndex; i += step) {
|
||||
if (toMove.size === 0) {
|
||||
break;
|
||||
}
|
||||
const n = tabNodes[i]
|
||||
if (toMove.has(n)) {
|
||||
if (singleStep) {
|
||||
if (i !== startIndex && !moved.has(tabNodes[i - step])) {
|
||||
tabNodes.splice(i, 1)
|
||||
tabNodes.splice(i - step, 0, n)
|
||||
n._reordered = true
|
||||
result.push(n)
|
||||
}
|
||||
} else {
|
||||
if (i !== target) {
|
||||
tabNodes.splice(i, 1)
|
||||
tabNodes.splice(target, 0, n)
|
||||
n._reordered = true
|
||||
result.push(n)
|
||||
}
|
||||
target += step
|
||||
}
|
||||
toMove.delete(n);
|
||||
moved.add(n);
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
var api = {
|
||||
addTab: function(id) {
|
||||
tabMap[id] = [];
|
||||
tabDirtyMap[id] = new Set();
|
||||
tabDeletedNodesMap[id] = new Set();
|
||||
},
|
||||
hasTab: function(z) {
|
||||
return tabMap.hasOwnProperty(z)
|
||||
},
|
||||
removeTab: function(id) {
|
||||
delete tabMap[id];
|
||||
delete tabDirtyMap[id];
|
||||
delete tabDeletedNodesMap[id];
|
||||
},
|
||||
addNode: function(n) {
|
||||
nodes[n.id] = n;
|
||||
if (tabMap.hasOwnProperty(n.z)) {
|
||||
tabMap[n.z].push(n);
|
||||
api.addObjectToWorkspace(n.z, n.id, n.changed || n.moved)
|
||||
} else {
|
||||
console.warn("Node added to unknown tab/subflow:",n);
|
||||
tabMap["_"] = tabMap["_"] || [];
|
||||
@@ -267,8 +319,37 @@ RED.nodes = (function() {
|
||||
if (i > -1) {
|
||||
tabMap[n.z].splice(i,1);
|
||||
}
|
||||
api.removeObjectFromWorkspace(n.z, n.id)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Add an object to our dirty/clean tracking state
|
||||
* @param {String} z
|
||||
* @param {String} id
|
||||
* @param {Boolean} isDirty
|
||||
*/
|
||||
addObjectToWorkspace: function (z, id, isDirty) {
|
||||
if (isDirty) {
|
||||
addedDirtyObjects.add(id)
|
||||
}
|
||||
if (tabDeletedNodesMap[z].has(id)) {
|
||||
tabDeletedNodesMap[z].delete(id)
|
||||
}
|
||||
api.markNodeDirty(z, id, isDirty)
|
||||
},
|
||||
/**
|
||||
* Remove an object from our dirty/clean tracking state
|
||||
* @param {String} z
|
||||
* @param {String} id
|
||||
*/
|
||||
removeObjectFromWorkspace: function (z, id) {
|
||||
if (!addedDirtyObjects.has(id)) {
|
||||
tabDeletedNodesMap[z].add(id)
|
||||
} else {
|
||||
addedDirtyObjects.delete(id)
|
||||
}
|
||||
api.markNodeDirty(z, id, false)
|
||||
},
|
||||
hasNode: function(id) {
|
||||
return nodes.hasOwnProperty(id);
|
||||
},
|
||||
@@ -280,152 +361,54 @@ RED.nodes = (function() {
|
||||
n.z = newZ;
|
||||
api.addNode(n)
|
||||
},
|
||||
moveNodesForwards: function(nodes) {
|
||||
var result = [];
|
||||
/**
|
||||
* @param {array} nodes
|
||||
* @param {boolean} direction true:forwards false:back
|
||||
* @param {boolean} singleStep true:single-step false:all-the-way
|
||||
*/
|
||||
changeDepth: function(nodes, direction, singleStep) {
|
||||
if (!Array.isArray(nodes)) {
|
||||
nodes = [nodes]
|
||||
}
|
||||
// Can only do this for nodes on the same tab.
|
||||
// Use nodes[0] to get the z
|
||||
var tabNodes = tabMap[nodes[0].z];
|
||||
var toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
|
||||
var moved = new Set();
|
||||
for (var i = tabNodes.length-1; i >= 0; i--) {
|
||||
if (toMove.size === 0) {
|
||||
break;
|
||||
}
|
||||
var n = tabNodes[i];
|
||||
if (toMove.has(n)) {
|
||||
// This is a node to move.
|
||||
if (i < tabNodes.length-1 && !moved.has(tabNodes[i+1])) {
|
||||
// Remove from current position
|
||||
tabNodes.splice(i,1);
|
||||
// Add it back one position higher
|
||||
tabNodes.splice(i+1,0,n);
|
||||
n._reordered = true;
|
||||
result.push(n);
|
||||
}
|
||||
toMove.delete(n);
|
||||
moved.add(n);
|
||||
let result = []
|
||||
const tabNodes = tabMap[nodes[0].z];
|
||||
const toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
|
||||
if (toMove.size > 0) {
|
||||
result = result.concat(changeCollectionDepth(tabNodes, toMove, direction, singleStep))
|
||||
if (result.length > 0) {
|
||||
RED.events.emit('nodes:reorder',{
|
||||
z: nodes[0].z,
|
||||
nodes: result
|
||||
});
|
||||
}
|
||||
}
|
||||
if (result.length > 0) {
|
||||
RED.events.emit('nodes:reorder',{
|
||||
z: nodes[0].z,
|
||||
nodes: result
|
||||
});
|
||||
|
||||
const groupNodes = groupsByZ[nodes[0].z] || []
|
||||
const groupsToMove = new Set(nodes.filter(function(n) { return n.type === 'group'}))
|
||||
if (groupsToMove.size > 0) {
|
||||
const groupResult = changeCollectionDepth(groupNodes, groupsToMove, direction, singleStep)
|
||||
if (groupResult.length > 0) {
|
||||
result = result.concat(groupResult)
|
||||
RED.events.emit('groups:reorder',{
|
||||
z: nodes[0].z,
|
||||
nodes: groupResult
|
||||
});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
RED.view.redraw(true)
|
||||
return result
|
||||
},
|
||||
moveNodesForwards: function(nodes) {
|
||||
return api.changeDepth(nodes, true, true)
|
||||
},
|
||||
moveNodesBackwards: function(nodes) {
|
||||
var result = [];
|
||||
if (!Array.isArray(nodes)) {
|
||||
nodes = [nodes]
|
||||
}
|
||||
// Can only do this for nodes on the same tab.
|
||||
// Use nodes[0] to get the z
|
||||
var tabNodes = tabMap[nodes[0].z];
|
||||
var toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
|
||||
var moved = new Set();
|
||||
for (var i = 0; i < tabNodes.length; i++) {
|
||||
if (toMove.size === 0) {
|
||||
break;
|
||||
}
|
||||
var n = tabNodes[i];
|
||||
if (toMove.has(n)) {
|
||||
// This is a node to move.
|
||||
if (i > 0 && !moved.has(tabNodes[i-1])) {
|
||||
// Remove from current position
|
||||
tabNodes.splice(i,1);
|
||||
// Add it back one position lower
|
||||
tabNodes.splice(i-1,0,n);
|
||||
n._reordered = true;
|
||||
result.push(n);
|
||||
}
|
||||
toMove.delete(n);
|
||||
moved.add(n);
|
||||
}
|
||||
}
|
||||
if (result.length > 0) {
|
||||
RED.events.emit('nodes:reorder',{
|
||||
z: nodes[0].z,
|
||||
nodes: result
|
||||
});
|
||||
}
|
||||
return result;
|
||||
return api.changeDepth(nodes, false, true)
|
||||
},
|
||||
moveNodesToFront: function(nodes) {
|
||||
var result = [];
|
||||
if (!Array.isArray(nodes)) {
|
||||
nodes = [nodes]
|
||||
}
|
||||
// Can only do this for nodes on the same tab.
|
||||
// Use nodes[0] to get the z
|
||||
var tabNodes = tabMap[nodes[0].z];
|
||||
var toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
|
||||
var target = tabNodes.length-1;
|
||||
for (var i = tabNodes.length-1; i >= 0; i--) {
|
||||
if (toMove.size === 0) {
|
||||
break;
|
||||
}
|
||||
var n = tabNodes[i];
|
||||
if (toMove.has(n)) {
|
||||
// This is a node to move.
|
||||
if (i < target) {
|
||||
// Remove from current position
|
||||
tabNodes.splice(i,1);
|
||||
tabNodes.splice(target,0,n);
|
||||
n._reordered = true;
|
||||
result.push(n);
|
||||
}
|
||||
target--;
|
||||
toMove.delete(n);
|
||||
}
|
||||
}
|
||||
if (result.length > 0) {
|
||||
RED.events.emit('nodes:reorder',{
|
||||
z: nodes[0].z,
|
||||
nodes: result
|
||||
});
|
||||
}
|
||||
return result;
|
||||
return api.changeDepth(nodes, true, false)
|
||||
},
|
||||
moveNodesToBack: function(nodes) {
|
||||
var result = [];
|
||||
if (!Array.isArray(nodes)) {
|
||||
nodes = [nodes]
|
||||
}
|
||||
// Can only do this for nodes on the same tab.
|
||||
// Use nodes[0] to get the z
|
||||
var tabNodes = tabMap[nodes[0].z];
|
||||
var toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
|
||||
var target = 0;
|
||||
for (var i = 0; i < tabNodes.length; i++) {
|
||||
if (toMove.size === 0) {
|
||||
break;
|
||||
}
|
||||
var n = tabNodes[i];
|
||||
if (toMove.has(n)) {
|
||||
// This is a node to move.
|
||||
if (i > target) {
|
||||
// Remove from current position
|
||||
tabNodes.splice(i,1);
|
||||
// Add it back one position lower
|
||||
tabNodes.splice(target,0,n);
|
||||
n._reordered = true;
|
||||
result.push(n);
|
||||
}
|
||||
target++;
|
||||
toMove.delete(n);
|
||||
}
|
||||
}
|
||||
if (result.length > 0) {
|
||||
RED.events.emit('nodes:reorder',{
|
||||
z: nodes[0].z,
|
||||
nodes: result
|
||||
});
|
||||
}
|
||||
return result;
|
||||
return api.changeDepth(nodes, false, false)
|
||||
},
|
||||
getNodes: function(z) {
|
||||
return tabMap[z];
|
||||
@@ -433,6 +416,33 @@ RED.nodes = (function() {
|
||||
clear: function() {
|
||||
nodes = {};
|
||||
tabMap = {};
|
||||
tabDirtyMap = {};
|
||||
tabDeletedNodesMap = {};
|
||||
addedDirtyObjects = new Set();
|
||||
},
|
||||
/**
|
||||
* Clear all internal state on what is dirty.
|
||||
*/
|
||||
clearState: function () {
|
||||
// Called when a deploy happens, we can forget about added/remove
|
||||
// items as they have now been deployed.
|
||||
addedDirtyObjects = new Set()
|
||||
const flowsToCheck = new Set()
|
||||
for (const [z, set] of Object.entries(tabDeletedNodesMap)) {
|
||||
if (set.size > 0) {
|
||||
set.clear()
|
||||
flowsToCheck.add(z)
|
||||
}
|
||||
}
|
||||
for (const [z, set] of Object.entries(tabDirtyMap)) {
|
||||
if (set.size > 0) {
|
||||
set.clear()
|
||||
flowsToCheck.add(z)
|
||||
}
|
||||
}
|
||||
for (const z of flowsToCheck) {
|
||||
api.checkTabState(z)
|
||||
}
|
||||
},
|
||||
eachNode: function(cb) {
|
||||
var nodeList,i,j;
|
||||
@@ -498,7 +508,7 @@ RED.nodes = (function() {
|
||||
return result;
|
||||
},
|
||||
getNodeOrder: function(z) {
|
||||
return tabMap[z].map(function(n) { return n.id })
|
||||
return (groupsByZ[z] || []).concat(tabMap[z]).map(n => n.id)
|
||||
},
|
||||
setNodeOrder: function(z, order) {
|
||||
var orderMap = {};
|
||||
@@ -510,6 +520,41 @@ RED.nodes = (function() {
|
||||
B._reordered = true;
|
||||
return orderMap[A.id] - orderMap[B.id];
|
||||
})
|
||||
if (groupsByZ[z]) {
|
||||
groupsByZ[z].sort(function(A,B) {
|
||||
return orderMap[A.id] - orderMap[B.id];
|
||||
})
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Update our records if an object is dirty or not
|
||||
* @param {String} z tab id
|
||||
* @param {String} id object id
|
||||
* @param {Boolean} dirty whether the object is dirty or not
|
||||
*/
|
||||
markNodeDirty: function(z, id, dirty) {
|
||||
if (tabDirtyMap[z]) {
|
||||
if (dirty) {
|
||||
tabDirtyMap[z].add(id)
|
||||
} else {
|
||||
tabDirtyMap[z].delete(id)
|
||||
}
|
||||
api.checkTabState(z)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Check if a tab should update its contentsChange flag
|
||||
* @param {String} z tab id
|
||||
*/
|
||||
checkTabState: function (z) {
|
||||
const ws = workspaces[z]
|
||||
if (ws) {
|
||||
const contentsChanged = tabDirtyMap[z].size > 0 || tabDeletedNodesMap[z].size > 0
|
||||
if (Boolean(ws.contentsChanged) !== contentsChanged) {
|
||||
ws.contentsChanged = contentsChanged
|
||||
RED.events.emit("flows:change", ws);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return api;
|
||||
@@ -575,15 +620,53 @@ RED.nodes = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
const nodeProxyHandler = {
|
||||
get(node, prop) {
|
||||
if (prop === '__isProxy__') {
|
||||
return true
|
||||
} else if (prop == '__node__') {
|
||||
return node
|
||||
}
|
||||
return node[prop]
|
||||
},
|
||||
set(node, prop, value) {
|
||||
if (node.z && (RED.nodes.workspace(node.z)?.locked || RED.nodes.subflow(node.z)?.locked)) {
|
||||
if (
|
||||
node._def.defaults[prop] ||
|
||||
prop === 'z' ||
|
||||
prop === 'l' ||
|
||||
prop === 'd' ||
|
||||
(prop === 'changed' && (!!node.changed) !== (!!value)) || // jshint ignore:line
|
||||
((prop === 'x' || prop === 'y') && !node.resize && node.type !== 'group')
|
||||
) {
|
||||
throw new Error(`Cannot modified property '${prop}' of locked object '${node.type}:${node.id}'`)
|
||||
}
|
||||
}
|
||||
if (node.z && (prop === 'changed' || prop === 'moved')) {
|
||||
setTimeout(() => {
|
||||
allNodes.markNodeDirty(node.z, node.id, node.changed || node.moved)
|
||||
}, 0)
|
||||
}
|
||||
node[prop] = value;
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
function addNode(n) {
|
||||
let newNode
|
||||
if (!n.__isProxy__) {
|
||||
newNode = new Proxy(n, nodeProxyHandler)
|
||||
} else {
|
||||
newNode = n
|
||||
}
|
||||
|
||||
if (n.type.indexOf("subflow") !== 0) {
|
||||
n["_"] = n._def._;
|
||||
} else {
|
||||
var subflowId = n.type.substring(8);
|
||||
var sf = RED.nodes.subflow(subflowId);
|
||||
if (sf) {
|
||||
sf.instances.push(sf);
|
||||
sf.instances.push(newNode);
|
||||
}
|
||||
n["_"] = RED._;
|
||||
}
|
||||
@@ -600,12 +683,13 @@ RED.nodes = (function() {
|
||||
});
|
||||
n.i = nextId+1;
|
||||
}
|
||||
allNodes.addNode(n);
|
||||
allNodes.addNode(newNode);
|
||||
if (!nodeLinks[n.id]) {
|
||||
nodeLinks[n.id] = {in:[],out:[]};
|
||||
}
|
||||
}
|
||||
RED.events.emit('nodes:add',n);
|
||||
RED.events.emit('nodes:add',newNode);
|
||||
return newNode
|
||||
}
|
||||
function addLink(l) {
|
||||
if (nodeLinks[l.source.id]) {
|
||||
@@ -632,10 +716,16 @@ RED.nodes = (function() {
|
||||
}
|
||||
if (l.source.z === l.target.z && linkTabMap[l.source.z]) {
|
||||
linkTabMap[l.source.z].push(l);
|
||||
allNodes.addObjectToWorkspace(l.source.z, getLinkId(l), true)
|
||||
}
|
||||
RED.events.emit("links:add",l);
|
||||
}
|
||||
|
||||
function getLinkId(link) {
|
||||
return link.source.id + ':' + link.sourcePort + ':' + link.target.id
|
||||
}
|
||||
|
||||
|
||||
function getNode(id) {
|
||||
if (id in configNodes) {
|
||||
return configNodes[id];
|
||||
@@ -830,6 +920,7 @@ RED.nodes = (function() {
|
||||
if (index !== -1) {
|
||||
linkTabMap[l.source.z].splice(index,1)
|
||||
}
|
||||
allNodes.removeObjectFromWorkspace(l.source.z, getLinkId(l))
|
||||
}
|
||||
}
|
||||
RED.events.emit("links:remove",l);
|
||||
@@ -999,6 +1090,11 @@ RED.nodes = (function() {
|
||||
return false;
|
||||
}
|
||||
|
||||
function getDownstreamNodes(node) {
|
||||
const downstreamLinks = nodeLinks[node.id].out
|
||||
const downstreamNodes = new Set(downstreamLinks.map(l => l.target))
|
||||
return Array.from(downstreamNodes)
|
||||
}
|
||||
function getAllDownstreamNodes(node) {
|
||||
return getAllFlowNodes(node,'down').filter(function(n) { return n !== node });
|
||||
}
|
||||
@@ -1046,6 +1142,9 @@ RED.nodes = (function() {
|
||||
node.type = n.type;
|
||||
for (var d in n._def.defaults) {
|
||||
if (n._def.defaults.hasOwnProperty(d)) {
|
||||
if (d === 'locked' && !n.locked) {
|
||||
continue
|
||||
}
|
||||
node[d] = n[d];
|
||||
}
|
||||
}
|
||||
@@ -1369,7 +1468,7 @@ RED.nodes = (function() {
|
||||
}
|
||||
}
|
||||
if (node.type !== "subflow") {
|
||||
var convertedNode = RED.nodes.convertNode(node);
|
||||
var convertedNode = RED.nodes.convertNode(node, { credentials: false });
|
||||
for (var d in node._def.defaults) {
|
||||
if (node._def.defaults[d].type) {
|
||||
var nodeList = node[d];
|
||||
@@ -1402,7 +1501,7 @@ RED.nodes = (function() {
|
||||
nns = nns.concat(createExportableNodeSet(node.nodes, exportedIds, exportedSubflows, exportedConfigNodes));
|
||||
}
|
||||
} else {
|
||||
var convertedSubflow = convertSubflow(node);
|
||||
var convertedSubflow = convertSubflow(node, { credentials: false });
|
||||
nns.push(convertedSubflow);
|
||||
}
|
||||
}
|
||||
@@ -1651,6 +1750,7 @@ RED.nodes = (function() {
|
||||
* Options:
|
||||
* - generateIds - whether to replace all node ids
|
||||
* - addFlow - whether to import nodes to a new tab
|
||||
* - markChanged - whether to set changed=true on all newly imported objects
|
||||
* - reimport - if node has a .z property, dont overwrite it
|
||||
* Only applicible when `generateIds` is false
|
||||
* - importMap - how to resolve any conflicts.
|
||||
@@ -1659,7 +1759,7 @@ RED.nodes = (function() {
|
||||
* - id:replace - import over the top of existing
|
||||
*/
|
||||
function importNodes(newNodesObj,options) { // createNewIds,createMissingWorkspace) {
|
||||
const defOpts = { generateIds: false, addFlow: false, reimport: false, importMap: {} }
|
||||
const defOpts = { generateIds: false, addFlow: false, markChanged: false, reimport: false, importMap: {} }
|
||||
options = Object.assign({}, defOpts, options)
|
||||
options.importMap = options.importMap || {}
|
||||
const createNewIds = options.generateIds;
|
||||
@@ -1685,7 +1785,7 @@ RED.nodes = (function() {
|
||||
newNodes = newNodesObj;
|
||||
}
|
||||
|
||||
if (!$.isArray(newNodes)) {
|
||||
if (!Array.isArray(newNodes)) {
|
||||
newNodes = [newNodes];
|
||||
}
|
||||
|
||||
@@ -1983,6 +2083,9 @@ RED.nodes = (function() {
|
||||
if (!n.z) {
|
||||
delete configNode.z;
|
||||
}
|
||||
if (options.markChanged) {
|
||||
configNode.changed = true
|
||||
}
|
||||
if (n.hasOwnProperty('d')) {
|
||||
configNode.d = n.d;
|
||||
}
|
||||
@@ -2045,6 +2148,9 @@ RED.nodes = (function() {
|
||||
if (n.hasOwnProperty('g')) {
|
||||
node.g = n.g;
|
||||
}
|
||||
if (options.markChanged) {
|
||||
node.changed = true
|
||||
}
|
||||
if (createNewIds || options.importMap[n.id] === "copy") {
|
||||
if (subflow_denylist[n.z]) {
|
||||
continue;
|
||||
@@ -2276,7 +2382,7 @@ RED.nodes = (function() {
|
||||
// get added
|
||||
if (activeSubflow && /^link /.test(n.type) && n.links) {
|
||||
n.links = n.links.filter(function(id) {
|
||||
var otherNode = RED.nodes.node(id);
|
||||
const otherNode = node_map[id] || RED.nodes.node(id);
|
||||
return (otherNode && otherNode.z === activeWorkspace)
|
||||
});
|
||||
}
|
||||
@@ -2326,19 +2432,6 @@ RED.nodes = (function() {
|
||||
if (n.g && !new_group_set.has(n.g)) {
|
||||
delete n.g;
|
||||
}
|
||||
n.nodes = n.nodes.map(function(id) {
|
||||
return node_map[id];
|
||||
})
|
||||
// Just in case the group references a node that doesn't exist for some reason
|
||||
n.nodes = n.nodes.filter(function(v) {
|
||||
if (v) {
|
||||
// Repair any nodes that have forgotten they are in this group
|
||||
if (v.g !== n.id) {
|
||||
v.g = n.id;
|
||||
}
|
||||
}
|
||||
return !!v
|
||||
});
|
||||
if (!n.g) {
|
||||
groupDepthMap[n.id] = 0;
|
||||
}
|
||||
@@ -2361,21 +2454,22 @@ RED.nodes = (function() {
|
||||
return groupDepthMap[A.id] - groupDepthMap[B.id];
|
||||
});
|
||||
for (i=0;i<new_groups.length;i++) {
|
||||
n = new_groups[i];
|
||||
addGroup(n);
|
||||
new_groups[i] = addGroup(new_groups[i]);
|
||||
node_map[new_groups[i].id] = new_groups[i]
|
||||
}
|
||||
|
||||
for (i=0;i<new_junctions.length;i++) {
|
||||
var junction = new_junctions[i];
|
||||
addJunction(junction);
|
||||
new_junctions[i] = addJunction(new_junctions[i]);
|
||||
node_map[new_junctions[i].id] = new_junctions[i]
|
||||
}
|
||||
|
||||
|
||||
// Now the nodes have been fully updated, add them.
|
||||
for (i=0;i<new_nodes.length;i++) {
|
||||
var node = new_nodes[i];
|
||||
addNode(node);
|
||||
new_nodes[i] = addNode(new_nodes[i])
|
||||
node_map[new_nodes[i].id] = new_nodes[i]
|
||||
}
|
||||
|
||||
// Finally validate them all.
|
||||
// This has to be done after everything is added so that any checks for
|
||||
// dependent config nodes will pass
|
||||
@@ -2383,6 +2477,39 @@ RED.nodes = (function() {
|
||||
var node = new_nodes[i];
|
||||
RED.editor.validateNode(node);
|
||||
}
|
||||
const lookupNode = (id) => {
|
||||
const mappedNode = node_map[id]
|
||||
if (!mappedNode) {
|
||||
return null
|
||||
}
|
||||
if (mappedNode.__isProxy__) {
|
||||
return mappedNode
|
||||
} else {
|
||||
return node_map[mappedNode.id]
|
||||
}
|
||||
}
|
||||
// Update groups to reference proxy node objects
|
||||
for (i=0;i<new_groups.length;i++) {
|
||||
n = new_groups[i];
|
||||
// bypass the proxy in case the flow is locked
|
||||
n.__node__.nodes = n.nodes.map(lookupNode)
|
||||
// Just in case the group references a node that doesn't exist for some reason
|
||||
n.__node__.nodes = n.nodes.filter(function(v) {
|
||||
if (v) {
|
||||
// Repair any nodes that have forgotten they are in this group
|
||||
if (v.g !== n.id) {
|
||||
v.g = n.id;
|
||||
}
|
||||
}
|
||||
return !!v
|
||||
});
|
||||
}
|
||||
|
||||
// Update links to use proxy node objects
|
||||
for (i=0;i<new_links.length;i++) {
|
||||
new_links[i].source = lookupNode(new_links[i].source.id) || new_links[i].source
|
||||
new_links[i].target = lookupNode(new_links[i].target.id) || new_links[i].target
|
||||
}
|
||||
|
||||
RED.workspaces.refresh();
|
||||
|
||||
@@ -2511,11 +2638,17 @@ RED.nodes = (function() {
|
||||
junctions = {};
|
||||
junctionsByZ = {};
|
||||
|
||||
var workspaceIds = Object.keys(workspaces);
|
||||
// Ensure all workspaces are unlocked so we don't get any edit-protection
|
||||
// preventing removal
|
||||
workspaceIds.forEach(function(id) {
|
||||
workspaces[id].locked = false
|
||||
});
|
||||
|
||||
var subflowIds = Object.keys(subflows);
|
||||
subflowIds.forEach(function(id) {
|
||||
RED.subflow.removeSubflow(id)
|
||||
});
|
||||
var workspaceIds = Object.keys(workspaces);
|
||||
workspaceIds.forEach(function(id) {
|
||||
RED.workspaces.remove(workspaces[id]);
|
||||
});
|
||||
@@ -2536,10 +2669,15 @@ RED.nodes = (function() {
|
||||
}
|
||||
|
||||
function addGroup(group) {
|
||||
if (!group.__isProxy__) {
|
||||
group = new Proxy(group, nodeProxyHandler)
|
||||
}
|
||||
groupsByZ[group.z] = groupsByZ[group.z] || [];
|
||||
groupsByZ[group.z].push(group);
|
||||
groups[group.id] = group;
|
||||
allNodes.addObjectToWorkspace(group.z, group.id, group.changed || group.moved)
|
||||
RED.events.emit("groups:add",group);
|
||||
return group
|
||||
}
|
||||
function removeGroup(group) {
|
||||
var i = groupsByZ[group.z].indexOf(group);
|
||||
@@ -2554,19 +2692,28 @@ RED.nodes = (function() {
|
||||
}
|
||||
}
|
||||
RED.group.markDirty(group);
|
||||
|
||||
allNodes.removeObjectFromWorkspace(group.z, group.id)
|
||||
delete groups[group.id];
|
||||
RED.events.emit("groups:remove",group);
|
||||
}
|
||||
function getGroupOrder(z) {
|
||||
const groups = groupsByZ[z]
|
||||
return groups.map(g => g.id)
|
||||
}
|
||||
|
||||
function addJunction(junction) {
|
||||
if (!junction.__isProxy__) {
|
||||
junction = new Proxy(junction, nodeProxyHandler)
|
||||
}
|
||||
junctionsByZ[junction.z] = junctionsByZ[junction.z] || []
|
||||
junctionsByZ[junction.z].push(junction)
|
||||
junctions[junction.id] = junction;
|
||||
if (!nodeLinks[junction.id]) {
|
||||
nodeLinks[junction.id] = {in:[],out:[]};
|
||||
}
|
||||
allNodes.addObjectToWorkspace(junction.z, junction.id, junction.changed || junction.moved)
|
||||
RED.events.emit("junctions:add", junction)
|
||||
return junction
|
||||
}
|
||||
function removeJunction(junction) {
|
||||
var i = junctionsByZ[junction.z].indexOf(junction)
|
||||
@@ -2576,6 +2723,7 @@ RED.nodes = (function() {
|
||||
}
|
||||
delete junctions[junction.id]
|
||||
delete nodeLinks[junction.id];
|
||||
allNodes.removeObjectFromWorkspace(junction.z, junction.id)
|
||||
RED.events.emit("junctions:remove", junction)
|
||||
|
||||
var removedLinks = links.filter(function(l) { return (l.source === junction) || (l.target === junction); });
|
||||
@@ -2813,6 +2961,9 @@ RED.nodes = (function() {
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
});
|
||||
RED.events.on('deploy', function () {
|
||||
allNodes.clearState()
|
||||
})
|
||||
},
|
||||
registry:registry,
|
||||
setNodeList: registry.setNodeList,
|
||||
@@ -2861,7 +3012,7 @@ RED.nodes = (function() {
|
||||
},
|
||||
addWorkspace: addWorkspace,
|
||||
removeWorkspace: removeWorkspace,
|
||||
getWorkspaceOrder: function() { return workspacesOrder },
|
||||
getWorkspaceOrder: function() { return [...workspacesOrder] },
|
||||
setWorkspaceOrder: function(order) { workspacesOrder = order; },
|
||||
workspace: getWorkspace,
|
||||
|
||||
@@ -2915,6 +3066,20 @@ RED.nodes = (function() {
|
||||
}
|
||||
}
|
||||
},
|
||||
eachGroup: function(cb) {
|
||||
for (var group of Object.values(groups)) {
|
||||
if (cb(group) === false) {
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
eachJunction: function(cb) {
|
||||
for (var junction of Object.values(junctions)) {
|
||||
if (cb(junction) === false) {
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
node: getNode,
|
||||
|
||||
@@ -2937,6 +3102,7 @@ RED.nodes = (function() {
|
||||
getAllFlowNodes: getAllFlowNodes,
|
||||
getAllUpstreamNodes: getAllUpstreamNodes,
|
||||
getAllDownstreamNodes: getAllDownstreamNodes,
|
||||
getDownstreamNodes: getDownstreamNodes,
|
||||
getNodeIslands: getNodeIslands,
|
||||
createExportableNodeSet: createExportableNodeSet,
|
||||
createCompleteNodeSet: createCompleteNodeSet,
|
||||
|
||||
@@ -249,8 +249,37 @@ var RED = (function() {
|
||||
RED.nodes.import(nodes.flows);
|
||||
RED.nodes.dirty(false);
|
||||
RED.view.redraw(true);
|
||||
if (/^#flow\/.+$/.test(currentHash)) {
|
||||
RED.workspaces.show(currentHash.substring(6),true);
|
||||
if (/^#(flow|node|group)\/.+$/.test(currentHash)) {
|
||||
const hashParts = currentHash.split('/')
|
||||
const showEditDialog = hashParts.length > 2 && hashParts[2] === 'edit'
|
||||
if (hashParts[0] === '#flow') {
|
||||
RED.workspaces.show(hashParts[1], true);
|
||||
if (showEditDialog) {
|
||||
RED.workspaces.edit()
|
||||
}
|
||||
} else if (hashParts[0] === '#node') {
|
||||
const nodeToShow = RED.nodes.node(hashParts[1])
|
||||
if (nodeToShow) {
|
||||
setTimeout(() => {
|
||||
RED.view.reveal(nodeToShow.id)
|
||||
window.location.hash = currentHash
|
||||
RED.view.select(nodeToShow.id)
|
||||
if (showEditDialog) {
|
||||
RED.editor.edit(nodeToShow)
|
||||
}
|
||||
}, 50)
|
||||
}
|
||||
} else if (hashParts[0] === '#group') {
|
||||
const nodeToShow = RED.nodes.group(hashParts[1])
|
||||
if (nodeToShow) {
|
||||
RED.view.reveal(nodeToShow.id)
|
||||
window.location.hash = currentHash
|
||||
RED.view.select(nodeToShow.id)
|
||||
if (showEditDialog) {
|
||||
RED.editor.editGroup(nodeToShow)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (RED.workspaces.count() > 0) {
|
||||
const hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
|
||||
@@ -643,11 +672,6 @@ var RED = (function() {
|
||||
]});
|
||||
|
||||
menuOptions.push({id:"menu-item-arrange-menu", label:RED._("menu.label.arrange"), options: [
|
||||
{id: "menu-item-view-tools-move-to-back", label:RED._("menu.label.moveToBack"), disabled: true, onselect: "core:move-selection-to-back"},
|
||||
{id: "menu-item-view-tools-move-to-front", label:RED._("menu.label.moveToFront"), disabled: true, onselect: "core:move-selection-to-front"},
|
||||
{id: "menu-item-view-tools-move-backwards", label:RED._("menu.label.moveBackwards"), disabled: true, onselect: "core:move-selection-backwards"},
|
||||
{id: "menu-item-view-tools-move-forwards", label:RED._("menu.label.moveForwards"), disabled: true, onselect: "core:move-selection-forwards"},
|
||||
null,
|
||||
{id: "menu-item-view-tools-align-left", label:RED._("menu.label.alignLeft"), disabled: true, onselect: "core:align-selection-to-left"},
|
||||
{id: "menu-item-view-tools-align-center", label:RED._("menu.label.alignCenter"), disabled: true, onselect: "core:align-selection-to-center"},
|
||||
{id: "menu-item-view-tools-align-right", label:RED._("menu.label.alignRight"), disabled: true, onselect: "core:align-selection-to-right"},
|
||||
@@ -657,7 +681,12 @@ var RED = (function() {
|
||||
{id: "menu-item-view-tools-align-bottom", label:RED._("menu.label.alignBottom"), disabled: true, onselect: "core:align-selection-to-bottom"},
|
||||
null,
|
||||
{id: "menu-item-view-tools-distribute-horizontally", label:RED._("menu.label.distributeHorizontally"), disabled: true, onselect: "core:distribute-selection-horizontally"},
|
||||
{id: "menu-item-view-tools-distribute-veritcally", label:RED._("menu.label.distributeVertically"), disabled: true, onselect: "core:distribute-selection-vertically"}
|
||||
{id: "menu-item-view-tools-distribute-veritcally", label:RED._("menu.label.distributeVertically"), disabled: true, onselect: "core:distribute-selection-vertically"},
|
||||
null,
|
||||
{id: "menu-item-view-tools-move-to-back", label:RED._("menu.label.moveToBack"), disabled: true, onselect: "core:move-selection-to-back"},
|
||||
{id: "menu-item-view-tools-move-to-front", label:RED._("menu.label.moveToFront"), disabled: true, onselect: "core:move-selection-to-front"},
|
||||
{id: "menu-item-view-tools-move-backwards", label:RED._("menu.label.moveBackwards"), disabled: true, onselect: "core:move-selection-backwards"},
|
||||
{id: "menu-item-view-tools-move-forwards", label:RED._("menu.label.moveForwards"), disabled: true, onselect: "core:move-selection-forwards"}
|
||||
]});
|
||||
|
||||
menuOptions.push(null);
|
||||
@@ -702,7 +731,7 @@ var RED = (function() {
|
||||
}
|
||||
menuOptions.push({id:"menu-item-help",
|
||||
label: RED.settings.theme("menu.menu-item-help.label",RED._("menu.label.help")),
|
||||
href: RED.settings.theme("menu.menu-item-help.url","http://nodered.org/docs")
|
||||
href: RED.settings.theme("menu.menu-item-help.url","https://nodered.org/docs")
|
||||
});
|
||||
menuOptions.push({id:"menu-item-node-red-version", label:"v"+RED.settings.version, onselect: "core:show-about" });
|
||||
|
||||
@@ -750,6 +779,7 @@ var RED = (function() {
|
||||
RED.deploy.init(RED.settings.theme("deployButton",null));
|
||||
|
||||
RED.keyboard.init(buildMainMenu);
|
||||
RED.envVar.init();
|
||||
|
||||
RED.nodes.init();
|
||||
RED.runtime.init()
|
||||
|
||||
@@ -33,8 +33,8 @@ RED.settings = (function () {
|
||||
if (!hasLocalStorage()) {
|
||||
return;
|
||||
}
|
||||
if (key === "auth-tokens") {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
if (key.startsWith("auth-tokens")) {
|
||||
localStorage.setItem(key+this.authTokensSuffix, JSON.stringify(value));
|
||||
} else {
|
||||
RED.utils.setMessageProperty(userSettings,key,value);
|
||||
saveUserSettings();
|
||||
@@ -52,8 +52,8 @@ RED.settings = (function () {
|
||||
if (!hasLocalStorage()) {
|
||||
return undefined;
|
||||
}
|
||||
if (key === "auth-tokens") {
|
||||
return JSON.parse(localStorage.getItem(key));
|
||||
if (key.startsWith("auth-tokens")) {
|
||||
return JSON.parse(localStorage.getItem(key+this.authTokensSuffix));
|
||||
} else {
|
||||
var v;
|
||||
try { v = RED.utils.getMessageProperty(userSettings,key); } catch(err) {}
|
||||
@@ -71,8 +71,8 @@ RED.settings = (function () {
|
||||
if (!hasLocalStorage()) {
|
||||
return;
|
||||
}
|
||||
if (key === "auth-tokens") {
|
||||
localStorage.removeItem(key);
|
||||
if (key.startsWith("auth-tokens")) {
|
||||
localStorage.removeItem(key+this.authTokensSuffix);
|
||||
} else {
|
||||
delete userSettings[key];
|
||||
saveUserSettings();
|
||||
@@ -99,6 +99,8 @@ RED.settings = (function () {
|
||||
|
||||
var init = function (options, done) {
|
||||
var accessTokenMatch = /[?&]access_token=(.*?)(?:$|&)/.exec(window.location.search);
|
||||
var path=window.location.pathname.slice(0,-1);
|
||||
RED.settings.authTokensSuffix=path.replace(/\//g, '-');
|
||||
if (accessTokenMatch) {
|
||||
var accessToken = accessTokenMatch[1];
|
||||
RED.settings.set("auth-tokens",{access_token: accessToken});
|
||||
|
||||
@@ -423,11 +423,10 @@ RED.clipboard = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
function showImportNodes(mode) {
|
||||
function showImportNodes(library = 'clipboard') {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
mode = mode || "clipboard";
|
||||
|
||||
dialogContainer.empty();
|
||||
dialogContainer.append($(importNodesDialog));
|
||||
@@ -504,7 +503,7 @@ RED.clipboard = (function() {
|
||||
$("#red-ui-clipboard-dialog-import-text").on("keyup", validateImport);
|
||||
$("#red-ui-clipboard-dialog-import-text").on('paste',function() { setTimeout(validateImport,10)});
|
||||
|
||||
if (RED.workspaces.active() === 0) {
|
||||
if (RED.workspaces.active() === 0 || RED.workspaces.isLocked()) {
|
||||
$("#red-ui-clipboard-dialog-import-opt-current").addClass('disabled').removeClass("selected");
|
||||
$("#red-ui-clipboard-dialog-import-opt-new").addClass("selected");
|
||||
} else {
|
||||
@@ -533,8 +532,8 @@ RED.clipboard = (function() {
|
||||
$("#red-ui-clipboard-dialog-import-file-upload").trigger("click");
|
||||
})
|
||||
|
||||
tabs.activateTab("red-ui-clipboard-dialog-import-tab-"+mode);
|
||||
if (mode === 'clipboard') {
|
||||
tabs.activateTab("red-ui-clipboard-dialog-import-tab-"+library);
|
||||
if (library === 'clipboard') {
|
||||
setTimeout(function() {
|
||||
$("#red-ui-clipboard-dialog-import-text").trigger("focus");
|
||||
},100)
|
||||
@@ -558,13 +557,16 @@ RED.clipboard = (function() {
|
||||
});
|
||||
}
|
||||
|
||||
function showExportNodes(mode) {
|
||||
/**
|
||||
* Show the export dialog
|
||||
* @params library which export destination to show
|
||||
* @params mode whether to default to 'auto' (default) or 'flow'
|
||||
**/
|
||||
function showExportNodes(library = 'clipboard', mode = 'auto' ) {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
mode = mode || "clipboard";
|
||||
|
||||
dialogContainer.empty();
|
||||
dialogContainer.append($(exportNodesDialog));
|
||||
|
||||
@@ -654,7 +656,12 @@ RED.clipboard = (function() {
|
||||
$("#red-ui-clipboard-dialog-tab-library-name").val("flows.json").select();
|
||||
|
||||
dialogContainer.i18n();
|
||||
|
||||
var format = RED.settings.flowFilePretty ? "red-ui-clipboard-dialog-export-fmt-full" : "red-ui-clipboard-dialog-export-fmt-mini";
|
||||
const userFormat = RED.settings.get("editor.dialog.export.pretty")
|
||||
if (userFormat === false || userFormat === true) {
|
||||
format = userFormat ? "red-ui-clipboard-dialog-export-fmt-full" : "red-ui-clipboard-dialog-export-fmt-mini";
|
||||
}
|
||||
|
||||
$("#red-ui-clipboard-dialog-export-fmt-group > a").on("click", function(evt) {
|
||||
evt.preventDefault();
|
||||
@@ -670,7 +677,8 @@ RED.clipboard = (function() {
|
||||
var nodes = JSON.parse(flow);
|
||||
|
||||
format = $(this).attr('id');
|
||||
if (format === 'red-ui-clipboard-dialog-export-fmt-full') {
|
||||
const pretty = format === "red-ui-clipboard-dialog-export-fmt-full";
|
||||
if (pretty) {
|
||||
flow = JSON.stringify(nodes,null,4);
|
||||
} else {
|
||||
flow = JSON.stringify(nodes);
|
||||
@@ -679,6 +687,7 @@ RED.clipboard = (function() {
|
||||
setTimeout(function() { $("#red-ui-clipboard-dialog-export-text").scrollTop(0); },50);
|
||||
|
||||
$("#red-ui-clipboard-dialog-export-text").trigger("focus");
|
||||
RED.settings.set("editor.dialog.export.pretty", pretty)
|
||||
}
|
||||
});
|
||||
|
||||
@@ -722,7 +731,7 @@ RED.clipboard = (function() {
|
||||
nodes.unshift(parentNode);
|
||||
nodes = RED.nodes.createExportableNodeSet(nodes);
|
||||
} else if (type === 'full') {
|
||||
nodes = RED.nodes.createCompleteNodeSet(false);
|
||||
nodes = RED.nodes.createCompleteNodeSet({ credentials: false });
|
||||
}
|
||||
if (nodes !== null) {
|
||||
if (format === "red-ui-clipboard-dialog-export-fmt-full") {
|
||||
@@ -766,12 +775,15 @@ RED.clipboard = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mode === 'flow' && !$("#red-ui-clipboard-dialog-export-rng-flow").hasClass('disabled')) {
|
||||
$("#red-ui-clipboard-dialog-export-rng-flow").trigger("click");
|
||||
}
|
||||
if (format === "red-ui-clipboard-dialog-export-fmt-full") {
|
||||
$("#red-ui-clipboard-dialog-export-fmt-full").trigger("click");
|
||||
} else {
|
||||
$("#red-ui-clipboard-dialog-export-fmt-mini").trigger("click");
|
||||
}
|
||||
tabs.activateTab("red-ui-clipboard-dialog-export-tab-"+mode);
|
||||
tabs.activateTab("red-ui-clipboard-dialog-export-tab-"+library);
|
||||
|
||||
var dialogHeight = 400;
|
||||
var winHeight = $(window).height();
|
||||
@@ -1266,15 +1278,17 @@ RED.clipboard = (function() {
|
||||
RED.keyboard.add("#red-ui-drop-target", "escape" ,hideDropTarget);
|
||||
|
||||
$('#red-ui-workspace-chart').on("dragenter",function(event) {
|
||||
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1 ||
|
||||
$.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
|
||||
if (!RED.workspaces.isLocked() && (
|
||||
$.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1 ||
|
||||
$.inArray("Files",event.originalEvent.dataTransfer.types) != -1)) {
|
||||
$("#red-ui-drop-target").css({display:'table'}).focus();
|
||||
}
|
||||
});
|
||||
|
||||
$('#red-ui-drop-target').on("dragover",function(event) {
|
||||
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1 ||
|
||||
$.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
|
||||
$.inArray("Files",event.originalEvent.dataTransfer.types) != -1 ||
|
||||
RED.workspaces.isLocked()) {
|
||||
event.preventDefault();
|
||||
}
|
||||
})
|
||||
@@ -1282,27 +1296,29 @@ RED.clipboard = (function() {
|
||||
hideDropTarget();
|
||||
})
|
||||
.on("drop",function(event) {
|
||||
try {
|
||||
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
|
||||
var data = event.originalEvent.dataTransfer.getData("text/plain");
|
||||
data = data.substring(data.indexOf('['),data.lastIndexOf(']')+1);
|
||||
importNodes(data);
|
||||
} else if ($.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
|
||||
var files = event.originalEvent.dataTransfer.files;
|
||||
if (files.length === 1) {
|
||||
var file = files[0];
|
||||
var reader = new FileReader();
|
||||
reader.onload = (function(theFile) {
|
||||
return function(e) {
|
||||
importNodes(e.target.result);
|
||||
};
|
||||
})(file);
|
||||
reader.readAsText(file);
|
||||
if (!RED.workspaces.isLocked()) {
|
||||
try {
|
||||
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
|
||||
var data = event.originalEvent.dataTransfer.getData("text/plain");
|
||||
data = data.substring(data.indexOf('['),data.lastIndexOf(']')+1);
|
||||
importNodes(data);
|
||||
} else if ($.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
|
||||
var files = event.originalEvent.dataTransfer.files;
|
||||
if (files.length === 1) {
|
||||
var file = files[0];
|
||||
var reader = new FileReader();
|
||||
reader.onload = (function(theFile) {
|
||||
return function(e) {
|
||||
importNodes(e.target.result);
|
||||
};
|
||||
})(file);
|
||||
reader.readAsText(file);
|
||||
}
|
||||
}
|
||||
} catch(err) {
|
||||
// Ensure any errors throw above doesn't stop the drop target from
|
||||
// being hidden.
|
||||
}
|
||||
} catch(err) {
|
||||
// Ensure any errors throw above doesn't stop the drop target from
|
||||
// being hidden.
|
||||
}
|
||||
hideDropTarget();
|
||||
event.preventDefault();
|
||||
|
||||
@@ -417,6 +417,9 @@
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
cancel: function() {
|
||||
this.element.sortable("cancel");
|
||||
}
|
||||
});
|
||||
})(jQuery);
|
||||
|
||||
@@ -94,8 +94,8 @@ RED.menu = (function() {
|
||||
|
||||
var link = $(linkContent).appendTo(item);
|
||||
opt.link = link;
|
||||
if (typeof opt.onselect === 'string') {
|
||||
var shortcut = RED.keyboard.getShortcut(opt.onselect);
|
||||
if (typeof opt.onselect === 'string' || opt.shortcut) {
|
||||
var shortcut = opt.shortcut || RED.keyboard.getShortcut(opt.onselect);
|
||||
if (shortcut && shortcut.key) {
|
||||
opt.shortcutSpan = $('<span class="red-ui-popover-key">'+RED.keyboard.formatKey(shortcut.key, true)+'</span>').appendTo(link.find(".red-ui-menu-label"));
|
||||
}
|
||||
|
||||
@@ -141,7 +141,29 @@ RED.tabs = (function() {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
if (options.contextmenu) {
|
||||
wrapper.on('contextmenu', function(evt) {
|
||||
let clickedTab
|
||||
let target = evt.target
|
||||
while(target.nodeName !== 'A' && target.nodeName !== 'UL' && target.nodeName !== 'BODY') {
|
||||
target = target.parentNode
|
||||
}
|
||||
if (target.nodeName === 'A') {
|
||||
const href = target.getAttribute('href')
|
||||
if (href) {
|
||||
clickedTab = tabs[href.slice(1)]
|
||||
}
|
||||
}
|
||||
evt.preventDefault()
|
||||
evt.stopPropagation()
|
||||
RED.contextMenu.show({
|
||||
x:evt.clientX-5,
|
||||
y:evt.clientY-5,
|
||||
options: options.contextmenu(clickedTab)
|
||||
})
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
var scrollLeft;
|
||||
var scrollRight;
|
||||
@@ -161,7 +183,7 @@ RED.tabs = (function() {
|
||||
// Assume this is wheel event which might not trigger
|
||||
// the scroll event, so do things manually
|
||||
var sl = scrollContainer.scrollLeft();
|
||||
sl -= evt.originalEvent.deltaY;
|
||||
sl += evt.originalEvent.deltaY;
|
||||
scrollContainer.scrollLeft(sl);
|
||||
}
|
||||
})
|
||||
@@ -807,23 +829,22 @@ RED.tabs = (function() {
|
||||
event.preventDefault();
|
||||
removeTab(tab.id);
|
||||
});
|
||||
RED.popover.tooltip(closeLink,RED._("workspace.hideFlow"));
|
||||
}
|
||||
if (tab.hideable) {
|
||||
li.addClass("red-ui-tabs-closeable")
|
||||
var closeLink = $("<a/>",{href:"#",class:"red-ui-tab-close red-ui-tab-hide"}).appendTo(li);
|
||||
closeLink.append('<i class="fa fa-eye" />');
|
||||
closeLink.append('<i class="fa fa-eye-slash" />');
|
||||
closeLink.on("click",function(event) {
|
||||
event.preventDefault();
|
||||
hideTab(tab.id);
|
||||
});
|
||||
RED.popover.tooltip(closeLink,RED._("workspace.hideFlow"));
|
||||
RED.popover.tooltip(closeLink,RED._("workspace.closeFlow"));
|
||||
}
|
||||
// if (tab.hideable) {
|
||||
// li.addClass("red-ui-tabs-closeable")
|
||||
// var closeLink = $("<a/>",{href:"#",class:"red-ui-tab-close red-ui-tab-hide"}).appendTo(li);
|
||||
// closeLink.append('<i class="fa fa-eye" />');
|
||||
// closeLink.append('<i class="fa fa-eye-slash" />');
|
||||
// closeLink.on("click",function(event) {
|
||||
// event.preventDefault();
|
||||
// hideTab(tab.id);
|
||||
// });
|
||||
// RED.popover.tooltip(closeLink,RED._("workspace.hideFlow"));
|
||||
// }
|
||||
|
||||
var badges = $('<span class="red-ui-tabs-badges"></span>').appendTo(li);
|
||||
if (options.onselect) {
|
||||
$('<i class="red-ui-tabs-badge-changed fa fa-circle"></i>').appendTo(badges);
|
||||
$('<i class="red-ui-tabs-badge-selected fa fa-check-circle"></i>').appendTo(badges);
|
||||
}
|
||||
|
||||
@@ -938,6 +959,9 @@ RED.tabs = (function() {
|
||||
activeIndex: function() {
|
||||
return ul.find("li.active").index()
|
||||
},
|
||||
getTabIndex: function (id) {
|
||||
return ul.find("a[href='#"+id+"']").parent().index()
|
||||
},
|
||||
contains: function(id) {
|
||||
return ul.find("a[href='#"+id+"']").length > 0;
|
||||
},
|
||||
|
||||
@@ -1,21 +1,6 @@
|
||||
RED.contextMenu = (function () {
|
||||
|
||||
let menu;
|
||||
function createMenu() {
|
||||
// menu = RED.popover.menu({
|
||||
// options: [
|
||||
// {
|
||||
// label: 'delete selection',
|
||||
// onselect: function() {
|
||||
// RED.actions.invoke('core:delete-selection')
|
||||
// RED.view.focus()
|
||||
// }
|
||||
// },
|
||||
// { label: 'world' }
|
||||
// ],
|
||||
// width: 200,
|
||||
// })
|
||||
}
|
||||
|
||||
function disposeMenu() {
|
||||
$(document).off("mousedown.red-ui-workspace-context-menu");
|
||||
@@ -28,114 +13,172 @@ RED.contextMenu = (function () {
|
||||
if (menu) {
|
||||
menu.remove()
|
||||
}
|
||||
let menuItems = []
|
||||
if (options.options) {
|
||||
menuItems = options.options
|
||||
} else if (options.type === 'workspace') {
|
||||
const selection = RED.view.selection()
|
||||
const noSelection = !selection || Object.keys(selection).length === 0
|
||||
const hasSelection = (selection.nodes && selection.nodes.length > 0);
|
||||
const hasMultipleSelection = hasSelection && selection.nodes.length > 1;
|
||||
const virtulLinks = (selection.links && selection.links.filter(e => !!e.link)) || [];
|
||||
const wireLinks = (selection.links && selection.links.filter(e => !e.link)) || [];
|
||||
const hasLinks = wireLinks.length > 0;
|
||||
const isSingleLink = !hasSelection && hasLinks && wireLinks.length === 1
|
||||
const isMultipleLinks = !hasSelection && hasLinks && wireLinks.length > 1
|
||||
const canDelete = hasSelection || hasLinks
|
||||
const isGroup = hasSelection && selection.nodes.length === 1 && selection.nodes[0].type === 'group'
|
||||
const canEdit = !RED.workspaces.isLocked()
|
||||
const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g
|
||||
const isAllGroups = hasSelection && selection.nodes.filter(n => n.type !== 'group').length === 0
|
||||
const hasGroup = hasSelection && selection.nodes.filter(n => n.type === 'group' ).length > 0
|
||||
const offset = $("#red-ui-workspace-chart").offset()
|
||||
|
||||
const selection = RED.view.selection()
|
||||
const noSelection = !selection || Object.keys(selection).length === 0
|
||||
const hasSelection = (selection.nodes && selection.nodes.length > 0);
|
||||
const hasMultipleSelection = hasSelection && selection.nodes.length > 1;
|
||||
const virtulLinks = (selection.links && selection.links.filter(e => !!e.link)) || [];
|
||||
const wireLinks = (selection.links && selection.links.filter(e => !e.link)) || [];
|
||||
const hasLinks = wireLinks.length > 0;
|
||||
const isSingleLink = !hasSelection && hasLinks && wireLinks.length === 1
|
||||
const isMultipleLinks = !hasSelection && hasLinks && wireLinks.length > 1
|
||||
const canDelete = hasSelection || hasLinks
|
||||
const isGroup = hasSelection && selection.nodes.length === 1 && selection.nodes[0].type === 'group'
|
||||
|
||||
const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g
|
||||
const offset = $("#red-ui-workspace-chart").offset()
|
||||
|
||||
// addX/addY must be the position in the workspace accounting for both scroll and scale
|
||||
// The +5 is because we display the contextMenu -5,-5 to actual click position
|
||||
let addX = (options.x + 5 - offset.left + $("#red-ui-workspace-chart").scrollLeft()) / RED.view.scale()
|
||||
let addY = (options.y + 5 - offset.top + $("#red-ui-workspace-chart").scrollTop()) / RED.view.scale()
|
||||
|
||||
const menuItems = [
|
||||
{ onselect: 'core:show-action-list', onpostselect: function () { } },
|
||||
{
|
||||
label: RED._("contextMenu.insert"),
|
||||
options: [
|
||||
{
|
||||
label: RED._("contextMenu.node"),
|
||||
onselect: function () {
|
||||
RED.view.showQuickAddDialog({
|
||||
position: [addX, addY],
|
||||
touchTrigger: true,
|
||||
splice: isSingleLink ? selection.links[0] : undefined,
|
||||
// spliceMultiple: isMultipleLinks
|
||||
})
|
||||
},
|
||||
onpostselect: function() {
|
||||
// ensure quick add dialog search input has focus
|
||||
$('#red-ui-type-search-input').trigger('focus')
|
||||
}
|
||||
},
|
||||
(hasLinks) ? { // has least 1 wire selected
|
||||
label: RED._("contextMenu.junction"),
|
||||
onselect: 'core:split-wires-with-junctions',
|
||||
disabled: !hasLinks
|
||||
} : {
|
||||
label: RED._("contextMenu.junction"),
|
||||
onselect: function () {
|
||||
const nn = {
|
||||
_def: { defaults: {} },
|
||||
type: 'junction',
|
||||
z: RED.workspaces.active(),
|
||||
id: RED.nodes.id(),
|
||||
x: addX,
|
||||
y: addY,
|
||||
w: 0, h: 0,
|
||||
outputs: 1,
|
||||
inputs: 1,
|
||||
dirty: true
|
||||
}
|
||||
const historyEvent = {
|
||||
dirty: RED.nodes.dirty(),
|
||||
t: 'add',
|
||||
junctions: [nn]
|
||||
}
|
||||
RED.nodes.addJunction(nn);
|
||||
RED.history.push(historyEvent);
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.select({nodes: [nn] });
|
||||
RED.view.redraw(true)
|
||||
}
|
||||
},
|
||||
{
|
||||
label: RED._("contextMenu.linkNodes"),
|
||||
onselect: 'core:split-wire-with-link-nodes',
|
||||
disabled: !hasLinks
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
let addX = options.x - offset.left + $("#red-ui-workspace-chart").scrollLeft()
|
||||
let addY = options.y - offset.top + $("#red-ui-workspace-chart").scrollTop()
|
||||
|
||||
if (RED.view.snapGrid) {
|
||||
const gridSize = RED.view.gridSize()
|
||||
addX = gridSize * Math.floor(addX / gridSize)
|
||||
addY = gridSize * Math.floor(addY / gridSize)
|
||||
}
|
||||
|
||||
menuItems.push(
|
||||
{ onselect: 'core:show-action-list', onpostselect: function () { } }
|
||||
)
|
||||
|
||||
const insertOptions = []
|
||||
menuItems.push({ label: RED._("contextMenu.insert"), options: insertOptions })
|
||||
insertOptions.push(
|
||||
{
|
||||
label: RED._("contextMenu.node"),
|
||||
onselect: function () {
|
||||
RED.view.showQuickAddDialog({
|
||||
position: [addX, addY],
|
||||
touchTrigger: true,
|
||||
splice: isSingleLink ? selection.links[0] : undefined,
|
||||
// spliceMultiple: isMultipleLinks
|
||||
})
|
||||
},
|
||||
disabled: !canEdit
|
||||
},
|
||||
(hasLinks) ? { // has least 1 wire selected
|
||||
label: RED._("contextMenu.junction"),
|
||||
onselect: 'core:split-wires-with-junctions',
|
||||
disabled: !canEdit || !hasLinks
|
||||
} : {
|
||||
label: RED._("contextMenu.junction"),
|
||||
onselect: function () {
|
||||
const nn = {
|
||||
_def: { defaults: {} },
|
||||
type: 'junction',
|
||||
z: RED.workspaces.active(),
|
||||
id: RED.nodes.id(),
|
||||
x: addX,
|
||||
y: addY,
|
||||
w: 0, h: 0,
|
||||
outputs: 1,
|
||||
inputs: 1,
|
||||
dirty: true,
|
||||
moved: true
|
||||
}
|
||||
const junction = RED.nodes.addJunction(nn);
|
||||
const historyEvent = {
|
||||
dirty: RED.nodes.dirty(),
|
||||
t: 'add',
|
||||
junctions: [junction]
|
||||
}
|
||||
RED.history.push(historyEvent);
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.select({nodes: [junction] });
|
||||
RED.view.redraw(true)
|
||||
},
|
||||
disabled: !canEdit
|
||||
},
|
||||
{
|
||||
label: RED._("contextMenu.linkNodes"),
|
||||
onselect: 'core:split-wire-with-link-nodes',
|
||||
disabled: !canEdit || !hasLinks
|
||||
},
|
||||
null,
|
||||
{ onselect: 'core:show-import-dialog', label: RED._('common.label.import')},
|
||||
{ onselect: 'core:show-examples-import-dialog', label: RED._('menu.label.importExample') }
|
||||
)
|
||||
if (hasSelection && canEdit) {
|
||||
const nodeOptions = []
|
||||
if (!hasMultipleSelection && !isGroup) {
|
||||
nodeOptions.push(
|
||||
{ onselect: 'core:show-node-help' },
|
||||
null
|
||||
)
|
||||
}
|
||||
nodeOptions.push(
|
||||
{ onselect: 'core:enable-selected-nodes' },
|
||||
{ onselect: 'core:disable-selected-nodes' },
|
||||
null,
|
||||
{ onselect: 'core:show-selected-node-labels' },
|
||||
{ onselect: 'core:hide-selected-node-labels' }
|
||||
)
|
||||
menuItems.push({
|
||||
label: RED._('sidebar.info.node'),
|
||||
options: nodeOptions
|
||||
})
|
||||
menuItems.push({
|
||||
label: RED._('sidebar.info.group'),
|
||||
options: [
|
||||
{ onselect: 'core:group-selection' },
|
||||
{ onselect: 'core:ungroup-selection', disabled: !hasGroup },
|
||||
]
|
||||
})
|
||||
if (hasGroup) {
|
||||
menuItems[menuItems.length - 1].options.push(
|
||||
{ onselect: 'core:merge-selection-to-group', label: RED._("menu.label.groupMergeSelection") }
|
||||
)
|
||||
|
||||
}
|
||||
if (canRemoveFromGroup) {
|
||||
menuItems[menuItems.length - 1].options.push(
|
||||
{ onselect: 'core:remove-selection-from-group', label: RED._("menu.label.groupRemoveSelection") }
|
||||
)
|
||||
}
|
||||
menuItems[menuItems.length - 1].options.push(
|
||||
null,
|
||||
{ onselect: 'core:copy-group-style', disabled: !hasGroup },
|
||||
{ onselect: 'core:paste-group-style', disabled: !hasGroup}
|
||||
)
|
||||
}
|
||||
if (canEdit && hasMultipleSelection) {
|
||||
menuItems.push({
|
||||
label: RED._('menu.label.arrange'),
|
||||
options: [
|
||||
{ label:RED._("menu.label.alignLeft"), onselect: "core:align-selection-to-left"},
|
||||
{ label:RED._("menu.label.alignCenter"), onselect: "core:align-selection-to-center"},
|
||||
{ label:RED._("menu.label.alignRight"), onselect: "core:align-selection-to-right"},
|
||||
null,
|
||||
{ label:RED._("menu.label.alignTop"), onselect: "core:align-selection-to-top"},
|
||||
{ label:RED._("menu.label.alignMiddle"), onselect: "core:align-selection-to-middle"},
|
||||
{ label:RED._("menu.label.alignBottom"), onselect: "core:align-selection-to-bottom"},
|
||||
null,
|
||||
{ label:RED._("menu.label.distributeHorizontally"), onselect: "core:distribute-selection-horizontally"},
|
||||
{ label:RED._("menu.label.distributeVertically"), onselect: "core:distribute-selection-vertically"}
|
||||
]
|
||||
})
|
||||
}
|
||||
]
|
||||
|
||||
menuItems.push(
|
||||
null,
|
||||
{ onselect: 'core:undo', disabled: RED.history.list().length === 0 },
|
||||
{ onselect: 'core:redo', disabled: RED.history.listRedo().length === 0 },
|
||||
null,
|
||||
{ onselect: 'core:cut-selection-to-internal-clipboard', label: RED._("keyboard.cutNode"), disabled: !hasSelection },
|
||||
{ onselect: 'core:copy-selection-to-internal-clipboard', label: RED._("keyboard.copyNode"), disabled: !hasSelection },
|
||||
{ onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !RED.view.clipboard() },
|
||||
{ onselect: 'core:delete-selection', disabled: !canDelete },
|
||||
{ onselect: 'core:show-export-dialog', label: RED._("menu.label.export") },
|
||||
{ onselect: 'core:select-all-nodes' }
|
||||
)
|
||||
|
||||
if (hasSelection) {
|
||||
menuItems.push(
|
||||
null,
|
||||
isGroup ?
|
||||
{ onselect: 'core:ungroup-selection', disabled: !isGroup }
|
||||
: { onselect: 'core:group-selection', disabled: !hasSelection }
|
||||
{ onselect: 'core:undo', disabled: RED.history.list().length === 0 },
|
||||
{ onselect: 'core:redo', disabled: RED.history.listRedo().length === 0 },
|
||||
null,
|
||||
{ onselect: 'core:cut-selection-to-internal-clipboard', label: RED._("keyboard.cutNode"), disabled: !canEdit || !hasSelection },
|
||||
{ onselect: 'core:copy-selection-to-internal-clipboard', label: RED._("keyboard.copyNode"), disabled: !hasSelection },
|
||||
{ onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !canEdit || !RED.view.clipboard() },
|
||||
{ onselect: 'core:delete-selection', disabled: !canEdit || !canDelete },
|
||||
{ onselect: 'core:delete-selection-and-reconnect', label: RED._('keyboard.deleteReconnect'), disabled: !canEdit || !canDelete },
|
||||
{ onselect: 'core:show-export-dialog', label: RED._("menu.label.export") },
|
||||
{ onselect: 'core:select-all-nodes' },
|
||||
)
|
||||
if (canRemoveFromGroup) {
|
||||
menuItems.push({ onselect: 'core:remove-selection-from-group', label: RED._("menu.label.groupRemoveSelection") })
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var direction = "right";
|
||||
|
||||
@@ -557,7 +557,17 @@ RED.deploy = (function() {
|
||||
} else {
|
||||
RED.notify('<p>' + RED._("deploy.successfulDeploy") + '</p>', "success");
|
||||
}
|
||||
const flowsToLock = new Set()
|
||||
function ensureUnlocked(id) {
|
||||
const flow = id && (RED.nodes.workspace(id) || RED.nodes.subflow(id) || null);
|
||||
const isLocked = flow ? flow.locked : false;
|
||||
if (flow && isLocked) {
|
||||
flow.locked = false;
|
||||
flowsToLock.add(flow)
|
||||
}
|
||||
}
|
||||
RED.nodes.eachNode(function (node) {
|
||||
ensureUnlocked(node.z)
|
||||
if (node.changed) {
|
||||
node.dirty = true;
|
||||
node.changed = false;
|
||||
@@ -570,7 +580,32 @@ RED.deploy = (function() {
|
||||
delete node.credentials;
|
||||
}
|
||||
});
|
||||
RED.nodes.eachGroup(function (node) {
|
||||
ensureUnlocked(node.z)
|
||||
if (node.changed) {
|
||||
node.dirty = true;
|
||||
node.changed = false;
|
||||
}
|
||||
if (node.moved) {
|
||||
node.dirty = true;
|
||||
node.moved = false;
|
||||
}
|
||||
})
|
||||
RED.nodes.eachJunction(function (node) {
|
||||
ensureUnlocked(node.z)
|
||||
if (node.changed) {
|
||||
node.dirty = true;
|
||||
node.changed = false;
|
||||
}
|
||||
if (node.moved) {
|
||||
node.dirty = true;
|
||||
node.moved = false;
|
||||
}
|
||||
})
|
||||
RED.nodes.eachConfig(function (confNode) {
|
||||
if (confNode.z) {
|
||||
ensureUnlocked(confNode.z)
|
||||
}
|
||||
confNode.changed = false;
|
||||
if (confNode.credentials) {
|
||||
delete confNode.credentials;
|
||||
@@ -580,8 +615,16 @@ RED.deploy = (function() {
|
||||
subflow.changed = false;
|
||||
});
|
||||
RED.nodes.eachWorkspace(function (ws) {
|
||||
ws.changed = false;
|
||||
if (ws.changed || ws.added) {
|
||||
ensureUnlocked(ws.z)
|
||||
ws.changed = false;
|
||||
delete ws.added
|
||||
RED.events.emit("flows:change", ws)
|
||||
}
|
||||
});
|
||||
flowsToLock.forEach(flow => {
|
||||
flow.locked = true
|
||||
})
|
||||
// Once deployed, cannot undo back to a clean state
|
||||
RED.history.markAllDirty();
|
||||
RED.view.redraw();
|
||||
|
||||
@@ -720,7 +720,10 @@ RED.editor = (function() {
|
||||
if (typeof editing_node[d] === "string" || typeof editing_node[d] === "number") {
|
||||
oldValues[d] = editing_node[d];
|
||||
} else {
|
||||
oldValues[d] = $.extend(true,{},{v:editing_node[d]}).v;
|
||||
// Dont clone the group node `nodes` array
|
||||
if (editing_node.type !== 'group' || d !== "nodes") {
|
||||
oldValues[d] = $.extend(true,{},{v:editing_node[d]}).v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -862,6 +865,7 @@ RED.editor = (function() {
|
||||
function showEditDialog(node, defaultTab) {
|
||||
if (buildingEditDialog) { return }
|
||||
buildingEditDialog = true;
|
||||
if (node.z && RED.workspaces.isLocked(node.z)) { return }
|
||||
var editing_node = node;
|
||||
var removeInfoEditorOnClose = false;
|
||||
var skipInfoRefreshOnClose = false;
|
||||
@@ -1047,6 +1051,13 @@ RED.editor = (function() {
|
||||
|
||||
var trayFooterLeft = $('<div class="red-ui-tray-footer-left"></div>').appendTo(trayFooter)
|
||||
|
||||
var helpButton = $('<button type="button" class="red-ui-button"><i class="fa fa-book"></button>').on("click", function(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
RED.sidebar.help.show(editing_node.type);
|
||||
}).appendTo(trayFooterLeft);
|
||||
RED.popover.tooltip(helpButton, RED._("sidebar.help.showHelp"));
|
||||
|
||||
$('<input id="node-input-node-disabled" type="checkbox">').prop("checked",!!node.d).appendTo(trayFooterLeft).toggleButton({
|
||||
enabledIcon: "fa-circle-thin",
|
||||
disabledIcon: "fa-ban",
|
||||
@@ -1150,6 +1161,8 @@ RED.editor = (function() {
|
||||
var editing_config_node = RED.nodes.node(id);
|
||||
var activeEditPanes = [];
|
||||
|
||||
if (editing_config_node && editing_config_node.z && RED.workspaces.isLocked(editing_config_node.z)) { return }
|
||||
|
||||
var configNodeScope = ""; // default to global
|
||||
var activeSubflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
if (activeSubflow) {
|
||||
@@ -1192,6 +1205,13 @@ RED.editor = (function() {
|
||||
|
||||
var trayFooterLeft = $('<div class="red-ui-tray-footer-left"></div>').appendTo(trayFooter)
|
||||
|
||||
var helpButton = $('<button type="button" class="red-ui-button"><i class="fa fa-book"></button>').on("click", function(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
RED.sidebar.help.show(editing_config_node.type);
|
||||
}).appendTo(trayFooterLeft);
|
||||
RED.popover.tooltip(helpButton, RED._("sidebar.help.showHelp"));
|
||||
|
||||
$('<input id="node-config-input-node-disabled" type="checkbox">').prop("checked",!!editing_config_node.d).appendTo(trayFooterLeft).toggleButton({
|
||||
enabledIcon: "fa-circle-thin",
|
||||
disabledIcon: "fa-ban",
|
||||
@@ -1696,6 +1716,7 @@ RED.editor = (function() {
|
||||
function showEditGroupDialog(group, defaultTab) {
|
||||
if (buildingEditDialog) { return }
|
||||
buildingEditDialog = true;
|
||||
if (group.z && RED.workspaces.isLocked(group.z)) { return }
|
||||
var editing_node = group;
|
||||
editStack.push(group);
|
||||
RED.view.state(RED.state.EDITING);
|
||||
@@ -1855,11 +1876,15 @@ RED.editor = (function() {
|
||||
workspace.disabled = disabled;
|
||||
|
||||
$("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!workspace.disabled);
|
||||
if (workspace.id === RED.workspaces.active()) {
|
||||
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!workspace.disabled);
|
||||
}
|
||||
}
|
||||
|
||||
var locked = $("#node-input-locked").prop("checked");
|
||||
if (workspace.locked !== locked) {
|
||||
editState.changes.locked = workspace.locked;
|
||||
editState.changed = true;
|
||||
workspace.locked = locked;
|
||||
$("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-locked',!!workspace.locked);
|
||||
}
|
||||
if (editState.changed) {
|
||||
var historyEvent = {
|
||||
t: "edit",
|
||||
@@ -1900,6 +1925,7 @@ RED.editor = (function() {
|
||||
var trayBody = tray.find('.red-ui-tray-body');
|
||||
trayBody.parent().css('overflow','hidden');
|
||||
var trayFooterLeft = $('<div class="red-ui-tray-footer-left"></div>').appendTo(trayFooter)
|
||||
var trayFooterRight = $('<div class="red-ui-tray-footer-right"></div>').appendTo(trayFooter)
|
||||
|
||||
var nodeEditPanes = [
|
||||
'editor-tab-flow-properties',
|
||||
@@ -1914,6 +1940,18 @@ RED.editor = (function() {
|
||||
disabledIcon: "fa-ban",
|
||||
invertState: true
|
||||
})
|
||||
|
||||
if (!workspace.hasOwnProperty("locked")) {
|
||||
workspace.locked = false;
|
||||
}
|
||||
$('<input id="node-input-locked" type="checkbox">').prop("checked",workspace.locked).appendTo(trayFooterRight).toggleButton({
|
||||
enabledLabel: RED._("common.label.unlocked"),
|
||||
enabledIcon: "fa-unlock-alt",
|
||||
disabledLabel: RED._("common.label.locked"),
|
||||
disabledIcon: "fa-lock",
|
||||
invertState: true
|
||||
})
|
||||
|
||||
prepareEditDialog(trayBody, nodeEditPanes, workspace, {}, "node-input", defaultTab, function(_activeEditPanes) {
|
||||
activeEditPanes = _activeEditPanes;
|
||||
trayBody.i18n();
|
||||
|
||||
@@ -45,6 +45,9 @@
|
||||
selectedCodeEditor = RED.editor.codeEditor[defaultEditor];
|
||||
initialised = selectedCodeEditor.init();
|
||||
}
|
||||
|
||||
$('<div id="red-ui-image-drop-target"><div data-i18n="[append]workspace.dropImageHere"><i class="fa fa-download"></i><br></div></div>').appendTo('#red-ui-editor');
|
||||
$("#red-ui-image-drop-target").hide();
|
||||
}
|
||||
|
||||
function create(options) {
|
||||
@@ -64,6 +67,7 @@
|
||||
options = {};
|
||||
}
|
||||
|
||||
var editor = null;
|
||||
if (this.editor.type === MONACO) {
|
||||
// compatibility (see above note)
|
||||
if (!options.element && !options.id) {
|
||||
@@ -74,10 +78,14 @@
|
||||
console.warn("createEditor() options.element or options.id is not valid", options);
|
||||
$("#dialog-form").append('<div id="' + options.id + '" style="display: none;" />');
|
||||
}
|
||||
return this.editor.create(options);
|
||||
editor = this.editor.create(options);
|
||||
} else {
|
||||
return this.editor.create(options);//fallback to ACE
|
||||
editor = this.editor.create(options);//fallback to ACE
|
||||
}
|
||||
if (options.mode === "ace/mode/markdown") {
|
||||
RED.editor.customEditTypes['_markdown'].postInit(editor, options);
|
||||
}
|
||||
return editor;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -59,18 +59,21 @@ RED.editor.codeEditor.monaco = (function() {
|
||||
//TODO: get from externalModules.js For now this is enough for feature parity with ACE (and then some).
|
||||
const knownModules = {
|
||||
"assert": {package: "node", module: "assert", path: "node/assert.d.ts" },
|
||||
"assert/strict": {package: "node", module: "assert/strict", path: "node/assert/strict.d.ts" },
|
||||
"async_hooks": {package: "node", module: "async_hooks", path: "node/async_hooks.d.ts" },
|
||||
"buffer": {package: "node", module: "buffer", path: "node/buffer.d.ts" },
|
||||
"child_process": {package: "node", module: "child_process", path: "node/child_process.d.ts" },
|
||||
"cluster": {package: "node", module: "cluster", path: "node/cluster.d.ts" },
|
||||
"console": {package: "node", module: "console", path: "node/console.d.ts" },
|
||||
"constants": {package: "node", module: "constants", path: "node/constants.d.ts" },
|
||||
"crypto": {package: "node", module: "crypto", path: "node/crypto.d.ts" },
|
||||
"dgram": {package: "node", module: "dgram", path: "node/dgram.d.ts" },
|
||||
"diagnostics_channel.d": {package: "node", module: "diagnostics_channel", path: "node/diagnostics_channel.d.ts" },
|
||||
"dns": {package: "node", module: "dns", path: "node/dns.d.ts" },
|
||||
"dns/promises": {package: "node", module: "dns/promises", path: "node/dns/promises.d.ts" },
|
||||
"domain": {package: "node", module: "domain", path: "node/domain.d.ts" },
|
||||
"events": {package: "node", module: "events", path: "node/events.d.ts" },
|
||||
"fs": {package: "node", module: "fs", path: "node/fs.d.ts" },
|
||||
"fs/promises": {package: "node", module: "fs/promises", path: "node/fs/promises.d.ts" },
|
||||
"globals": {package: "node", module: "globals", path: "node/globals.d.ts" },
|
||||
"http": {package: "node", module: "http", path: "node/http.d.ts" },
|
||||
"http2": {package: "node", module: "http2", path: "node/http2.d.ts" },
|
||||
@@ -84,8 +87,13 @@ RED.editor.codeEditor.monaco = (function() {
|
||||
"querystring": {package: "node", module: "querystring", path: "node/querystring.d.ts" },
|
||||
"readline": {package: "node", module: "readline", path: "node/readline.d.ts" },
|
||||
"stream": {package: "node", module: "stream", path: "node/stream.d.ts" },
|
||||
"stream/consumers": {package: "node", module: "stream/consumers", path: "node/stream/consumers.d.ts" },
|
||||
"stream/promises": {package: "node", module: "stream/promises", path: "node/stream/promises.d.ts" },
|
||||
"stream/web": {package: "node", module: "stream/web", path: "node/stream/web.d.ts" },
|
||||
"string_decoder": {package: "node", module: "string_decoder", path: "node/string_decoder.d.ts" },
|
||||
"test": {package: "node", module: "test", path: "node/test.d.ts" },
|
||||
"timers": {package: "node", module: "timers", path: "node/timers.d.ts" },
|
||||
"timers/promises": {package: "node", module: "timers/promises", path: "node/timers/promises.d.ts" },
|
||||
"tls": {package: "node", module: "tls", path: "node/tls.d.ts" },
|
||||
"trace_events": {package: "node", module: "trace_events", path: "node/trace_events.d.ts" },
|
||||
"tty": {package: "node", module: "tty", path: "node/tty.d.ts" },
|
||||
@@ -100,7 +108,7 @@ RED.editor.codeEditor.monaco = (function() {
|
||||
"node-red-util": {package: "node-red", module: "util", path: "node-red/util.d.ts" },
|
||||
"node-red-func": {package: "node-red", module: "func", path: "node-red/func.d.ts" },
|
||||
}
|
||||
const defaultServerSideTypes = [ knownModules["node-red-util"], knownModules["node-red-func"], knownModules["globals"], knownModules["console"], knownModules["buffer"] , knownModules["util"] ];
|
||||
const defaultServerSideTypes = [ knownModules["node-red-util"], knownModules["node-red-func"], knownModules["globals"], knownModules["console"], knownModules["buffer"], knownModules["timers"] , knownModules["util"] ];
|
||||
|
||||
const modulesCache = {};
|
||||
|
||||
@@ -1160,19 +1168,19 @@ RED.editor.codeEditor.monaco = (function() {
|
||||
// Warning: 4
|
||||
// Error: 8
|
||||
ed.getAnnotations = function getAnnotations() {
|
||||
var aceCompatibleMarkers = [];
|
||||
let aceCompatibleMarkers;
|
||||
try {
|
||||
var _model = ed.getModel();
|
||||
const _model = ed.getModel();
|
||||
if (_model !== null) {
|
||||
var id = _model._languageId; // e.g. javascript
|
||||
var ra = _model._associatedResource.authority; //e.g. model
|
||||
var rp = _model._associatedResource.path; //e.g. /18
|
||||
var rs = _model._associatedResource.scheme; //e.g. inmemory
|
||||
var modelMarkers = monaco.editor.getModelMarkers(_model) || [];
|
||||
var thisEditorsMarkers = modelMarkers.filter(function (marker) {
|
||||
var _ra = marker.resource.authority; //e.g. model
|
||||
var _rp = marker.resource.path; //e.g. /18
|
||||
var _rs = marker.resource.scheme; //e.g. inmemory
|
||||
const id = _model.getLanguageId(); // e.g. javascript
|
||||
const ra = _model.uri.authority; // e.g. model
|
||||
const rp = _model.uri.path; // e.g. /18
|
||||
const rs = _model.uri.scheme; // e.g. inmemory
|
||||
const modelMarkers = monaco.editor.getModelMarkers(_model) || [];
|
||||
const thisEditorsMarkers = modelMarkers.filter(function (marker) {
|
||||
const _ra = marker.resource.authority; // e.g. model
|
||||
const _rp = marker.resource.path; // e.g. /18
|
||||
const _rs = marker.resource.scheme; // e.g. inmemory
|
||||
return marker.owner == id && _ra === ra && _rp === rp && _rs === rs;
|
||||
})
|
||||
aceCompatibleMarkers = thisEditorsMarkers.map(function (marker) {
|
||||
|
||||
@@ -2,7 +2,7 @@ RED.editor.envVarList = (function() {
|
||||
|
||||
var currentLocale = 'en-US';
|
||||
var DEFAULT_ENV_TYPE_LIST = ['str','num','bool','json','bin','env'];
|
||||
var DEFAULT_ENV_TYPE_LIST_INC_CRED = ['str','num','bool','json','bin','env','cred'];
|
||||
var DEFAULT_ENV_TYPE_LIST_INC_CRED = ['str','num','bool','json','bin','env','cred','jsonata'];
|
||||
|
||||
/**
|
||||
* Create env var edit interface
|
||||
|
||||
@@ -294,32 +294,37 @@
|
||||
}
|
||||
|
||||
try {
|
||||
var result = expr.evaluate(legacyMode?{msg:parsedData}:parsedData);
|
||||
if (usesContext) {
|
||||
testResultEditor.setValue(RED._("expressionEditor.errors.context-unsupported"),-1);
|
||||
return;
|
||||
}
|
||||
if (usesEnv) {
|
||||
testResultEditor.setValue(RED._("expressionEditor.errors.env-unsupported"),-1);
|
||||
return;
|
||||
}
|
||||
if (usesMoment) {
|
||||
testResultEditor.setValue(RED._("expressionEditor.errors.moment-unsupported"),-1);
|
||||
return;
|
||||
}
|
||||
if (usesClone) {
|
||||
testResultEditor.setValue(RED._("expressionEditor.errors.clone-unsupported"),-1);
|
||||
return;
|
||||
}
|
||||
|
||||
var formattedResult;
|
||||
if (result !== undefined) {
|
||||
formattedResult = JSON.stringify(result,null,4);
|
||||
} else {
|
||||
formattedResult = RED._("expressionEditor.noMatch");
|
||||
}
|
||||
testResultEditor.setValue(formattedResult,-1);
|
||||
} catch(err) {
|
||||
expr.evaluate(legacyMode?{msg:parsedData}:parsedData, null, (err, result) => {
|
||||
if (err) {
|
||||
testResultEditor.setValue(RED._("expressionEditor.errors.eval",{message:err.message}),-1);
|
||||
} else {
|
||||
if (usesContext) {
|
||||
testResultEditor.setValue(RED._("expressionEditor.errors.context-unsupported"),-1);
|
||||
return;
|
||||
}
|
||||
if (usesEnv) {
|
||||
testResultEditor.setValue(RED._("expressionEditor.errors.env-unsupported"),-1);
|
||||
return;
|
||||
}
|
||||
if (usesMoment) {
|
||||
testResultEditor.setValue(RED._("expressionEditor.errors.moment-unsupported"),-1);
|
||||
return;
|
||||
}
|
||||
if (usesClone) {
|
||||
testResultEditor.setValue(RED._("expressionEditor.errors.clone-unsupported"),-1);
|
||||
return;
|
||||
}
|
||||
|
||||
var formattedResult;
|
||||
if (result !== undefined) {
|
||||
formattedResult = JSON.stringify(result,null,4);
|
||||
} else {
|
||||
formattedResult = RED._("expressionEditor.noMatch");
|
||||
}
|
||||
testResultEditor.setValue(formattedResult,-1);
|
||||
}
|
||||
});
|
||||
} catch(err) {
|
||||
testResultEditor.setValue(RED._("expressionEditor.errors.eval",{message:err.message}),-1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,61 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
(function() {
|
||||
/**
|
||||
* Converts dropped image file to date URL
|
||||
*/
|
||||
function file2base64Image(file, cb) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = (function (fd) {
|
||||
return function (e) {
|
||||
cb(e.target.result);
|
||||
};
|
||||
})(file);
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
var initialized = false;
|
||||
var currentEditor = null;
|
||||
/**
|
||||
* Initialize handler for image file drag events
|
||||
*/
|
||||
function initImageDrag(elem, editor) {
|
||||
$(elem).on("dragenter", function (ev) {
|
||||
ev.preventDefault();
|
||||
$("#red-ui-image-drop-target").css({display:'table'}).focus();
|
||||
currentEditor = editor;
|
||||
});
|
||||
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
$("#red-ui-image-drop-target").on("dragover", function (ev) {
|
||||
ev.preventDefault();
|
||||
}).on("dragleave", function (ev) {
|
||||
$("#red-ui-image-drop-target").hide();
|
||||
}).on("drop", function (ev) {
|
||||
ev.preventDefault();
|
||||
if ($.inArray("Files",ev.originalEvent.dataTransfer.types) != -1) {
|
||||
var files = ev.originalEvent.dataTransfer.files;
|
||||
if (files.length === 1) {
|
||||
var file = files[0];
|
||||
var name = file.name.toLowerCase();
|
||||
|
||||
if (name.match(/\.(apng|avif|gif|jpeg|png|svg|webp)$/)) {
|
||||
file2base64Image(file, function (image) {
|
||||
var session = currentEditor.getSession();
|
||||
var img = `<img src="${image}"/>\n`;
|
||||
var pos = session.getCursorPosition();
|
||||
session.insert(pos, img);
|
||||
$("#red-ui-image-drop-target").hide();
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
$("#red-ui-image-drop-target").hide();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var toolbarTemplate = '<div style="margin-bottom: 5px">'+
|
||||
'<span class="button-group">'+
|
||||
@@ -114,6 +169,7 @@
|
||||
var currentScrollTop = $(".red-ui-editor-type-markdown-panel-preview").scrollTop();
|
||||
$(".red-ui-editor-type-markdown-panel-preview").html(RED.utils.renderMarkdown(expressionEditor.getValue()));
|
||||
$(".red-ui-editor-type-markdown-panel-preview").scrollTop(currentScrollTop);
|
||||
mermaid.init();
|
||||
},200);
|
||||
})
|
||||
if (options.header) {
|
||||
@@ -122,6 +178,7 @@
|
||||
|
||||
if (value) {
|
||||
$(".red-ui-editor-type-markdown-panel-preview").html(RED.utils.renderMarkdown(expressionEditor.getValue()));
|
||||
mermaid.init();
|
||||
}
|
||||
panels = RED.panels.create({
|
||||
id:"red-ui-editor-type-markdown-panels",
|
||||
@@ -148,10 +205,14 @@
|
||||
});
|
||||
RED.popover.tooltip($("#node-btn-markdown-preview"), RED._("markdownEditor.toggle-preview"));
|
||||
|
||||
if (options.cursor && !expressionEditor._initState) {
|
||||
expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false);
|
||||
}
|
||||
|
||||
if(!expressionEditor._initState) {
|
||||
if (options.cursor) {
|
||||
expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false);
|
||||
}
|
||||
else {
|
||||
expressionEditor.gotoLine(0, 0, false);
|
||||
}
|
||||
}
|
||||
dialogForm.i18n();
|
||||
},
|
||||
close: function() {
|
||||
@@ -215,7 +276,11 @@
|
||||
}
|
||||
})
|
||||
return toolbar;
|
||||
}
|
||||
},
|
||||
postInit: function (editor, options) {
|
||||
var elem = $("#"+options.id);
|
||||
initImageDrag(elem, editor);
|
||||
}
|
||||
}
|
||||
RED.editor.registerTypeEditor("_markdown", definition);
|
||||
})();
|
||||
|
||||
@@ -52,8 +52,6 @@
|
||||
node.info = info;
|
||||
}
|
||||
$("#red-ui-tab-"+(node.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!node.disabled);
|
||||
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!node.disabled);
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
189
packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js
vendored
Normal file
189
packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
RED.envVar = (function() {
|
||||
function saveEnvList(list) {
|
||||
const items = list.editableList("items")
|
||||
const new_env = [];
|
||||
items.each(function (i,el) {
|
||||
var data = el.data('data');
|
||||
var item;
|
||||
if (data.nameField && data.valueField) {
|
||||
item = {
|
||||
name: data.nameField.val(),
|
||||
value: data.valueField.typedInput("value"),
|
||||
type: data.valueField.typedInput("type")
|
||||
};
|
||||
new_env.push(item);
|
||||
}
|
||||
});
|
||||
return new_env;
|
||||
}
|
||||
|
||||
function getGlobalConf(create) {
|
||||
var gconf = null;
|
||||
RED.nodes.eachConfig(function (conf) {
|
||||
if (conf.type === "global-config") {
|
||||
gconf = conf;
|
||||
}
|
||||
});
|
||||
if ((gconf === null) && create) {
|
||||
var cred = {
|
||||
_ : {},
|
||||
map: {}
|
||||
};
|
||||
gconf = {
|
||||
id: RED.nodes.id(),
|
||||
type: "global-config",
|
||||
env: [],
|
||||
name: "global-config",
|
||||
label: "",
|
||||
hasUsers: false,
|
||||
users: [],
|
||||
credentials: cred,
|
||||
_def: RED.nodes.getType("global-config"),
|
||||
};
|
||||
RED.nodes.add(gconf);
|
||||
}
|
||||
return gconf;
|
||||
}
|
||||
|
||||
function applyChanges(list) {
|
||||
var gconf = getGlobalConf(false);
|
||||
var new_env = [];
|
||||
var items = list.editableList('items');
|
||||
var credentials = gconf ? gconf.credentials : null;
|
||||
if (!gconf && list.editableList('length') === 0) {
|
||||
// No existing global-config node and nothing in the list,
|
||||
// so no need to do anything more
|
||||
return
|
||||
}
|
||||
if (!credentials) {
|
||||
credentials = {
|
||||
_ : {},
|
||||
map: {}
|
||||
};
|
||||
}
|
||||
items.each(function (i,el) {
|
||||
var data = el.data('data');
|
||||
if (data.nameField && data.valueField) {
|
||||
var item = {
|
||||
name: data.nameField.val(),
|
||||
value: data.valueField.typedInput("value"),
|
||||
type: data.valueField.typedInput("type")
|
||||
};
|
||||
if (item.name.trim() !== "") {
|
||||
new_env.push(item);
|
||||
if ((item.type === "cred") && (item.value !== "__PWRD__")) {
|
||||
credentials.map[item.name] = item.value;
|
||||
credentials.map["has_"+item.name] = (item.value !== "");
|
||||
item.value = "__PWRD__";
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (gconf === null) {
|
||||
gconf = getGlobalConf(true);
|
||||
}
|
||||
if (!gconf.credentials) {
|
||||
gconf.credentials = {
|
||||
_ : {},
|
||||
map: {}
|
||||
};
|
||||
}
|
||||
if ((JSON.stringify(new_env) !== JSON.stringify(gconf.env)) ||
|
||||
(JSON.stringify(credentials) !== JSON.stringify(gconf.credentials))) {
|
||||
gconf.env = new_env;
|
||||
gconf.credentials = credentials;
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
}
|
||||
|
||||
function getSettingsPane() {
|
||||
var gconf = getGlobalConf(false);
|
||||
var env = gconf ? gconf.env : [];
|
||||
var cred = gconf ? gconf.credentials : null;
|
||||
if (!cred) {
|
||||
cred = {
|
||||
_ : {},
|
||||
map: {}
|
||||
};
|
||||
}
|
||||
|
||||
var pane = $("<div/>", {
|
||||
id: "red-ui-settings-tab-envvar",
|
||||
class: "form-horizontal"
|
||||
});
|
||||
var content = $("<div/>", {
|
||||
class: "form-row node-input-env-container-row"
|
||||
}).css({
|
||||
"margin": "10px"
|
||||
}).appendTo(pane);
|
||||
|
||||
var label = $("<label></label>").css({
|
||||
width: "100%"
|
||||
}).appendTo(content);
|
||||
$("<i/>", {
|
||||
class: "fa fa-list"
|
||||
}).appendTo(label);
|
||||
$("<span/>").text(" "+RED._("env-var.header")).appendTo(label);
|
||||
|
||||
var list = $("<ol/>", {
|
||||
id: "node-input-env-container"
|
||||
}).appendTo(content);
|
||||
var node = {
|
||||
type: "",
|
||||
env: env,
|
||||
credentials: cred.map,
|
||||
};
|
||||
RED.editor.envVarList.create(list, node);
|
||||
|
||||
var buttons = $("<div/>").css({
|
||||
"text-align": "right",
|
||||
}).appendTo(content);
|
||||
var revertButton = $("<button/>", {
|
||||
class: "red-ui-button"
|
||||
}).css({
|
||||
}).text(RED._("env-var.revert")).appendTo(buttons);
|
||||
|
||||
var items = saveEnvList(list);
|
||||
revertButton.on("click", function (ev) {
|
||||
list.editableList("empty");
|
||||
list.editableList("addItems", items);
|
||||
});
|
||||
|
||||
return pane;
|
||||
}
|
||||
|
||||
function init(done) {
|
||||
if (!RED.user.hasPermission("settings.write")) {
|
||||
RED.notify(RED._("user.errors.settings"),"error");
|
||||
return;
|
||||
}
|
||||
RED.userSettings.add({
|
||||
id:'envvar',
|
||||
title: RED._("env-var.environment"),
|
||||
get: getSettingsPane,
|
||||
focus: function() {
|
||||
var height = $("#red-ui-settings-tab-envvar").parent().height();
|
||||
$("#node-input-env-container").editableList("height", (height -100));
|
||||
},
|
||||
close: function() {
|
||||
var list = $("#node-input-env-container");
|
||||
try {
|
||||
applyChanges(list);
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e);
|
||||
console.log(e.stack);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
RED.actions.add("core:show-global-env", function() {
|
||||
RED.userSettings.show('envvar');
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -188,6 +188,8 @@ RED.group = (function() {
|
||||
var activateMerge = false;
|
||||
var activateRemove = false;
|
||||
var singleGroupSelected = false;
|
||||
var locked = RED.workspaces.isLocked()
|
||||
|
||||
if (activateGroup) {
|
||||
singleGroupSelected = selection.nodes.length === 1 && selection.nodes[0].type === 'group';
|
||||
selection.nodes.forEach(function (n) {
|
||||
@@ -202,12 +204,12 @@ RED.group = (function() {
|
||||
activateMerge = (selection.nodes.length > 1);
|
||||
}
|
||||
}
|
||||
RED.menu.setDisabled("menu-item-group-group", !activateGroup);
|
||||
RED.menu.setDisabled("menu-item-group-ungroup", !activateUngroup);
|
||||
RED.menu.setDisabled("menu-item-group-merge", !activateMerge);
|
||||
RED.menu.setDisabled("menu-item-group-remove", !activateRemove);
|
||||
RED.menu.setDisabled("menu-item-group-group", locked || !activateGroup);
|
||||
RED.menu.setDisabled("menu-item-group-ungroup", locked || !activateUngroup);
|
||||
RED.menu.setDisabled("menu-item-group-merge", locked || !activateMerge);
|
||||
RED.menu.setDisabled("menu-item-group-remove", locked || !activateRemove);
|
||||
RED.menu.setDisabled("menu-item-edit-copy-group-style", !singleGroupSelected);
|
||||
RED.menu.setDisabled("menu-item-edit-paste-group-style", !activateUngroup);
|
||||
RED.menu.setDisabled("menu-item-edit-paste-group-style", locked || !activateUngroup);
|
||||
});
|
||||
|
||||
RED.actions.add("core:group-selection", function() { groupSelection() })
|
||||
@@ -264,6 +266,7 @@ RED.group = (function() {
|
||||
}
|
||||
}
|
||||
function pasteGroupStyle() {
|
||||
if (RED.workspaces.isLocked()) { return }
|
||||
if (RED.view.state() !== RED.state.DEFAULT) { return }
|
||||
if (groupStyleClipboard) {
|
||||
var selection = RED.view.selection();
|
||||
@@ -298,6 +301,7 @@ RED.group = (function() {
|
||||
}
|
||||
|
||||
function groupSelection() {
|
||||
if (RED.workspaces.isLocked()) { return }
|
||||
if (RED.view.state() !== RED.state.DEFAULT) { return }
|
||||
var selection = RED.view.selection();
|
||||
if (selection.nodes) {
|
||||
@@ -316,11 +320,12 @@ RED.group = (function() {
|
||||
}
|
||||
}
|
||||
function ungroupSelection() {
|
||||
if (RED.workspaces.isLocked()) { return }
|
||||
if (RED.view.state() !== RED.state.DEFAULT) { return }
|
||||
var selection = RED.view.selection();
|
||||
if (selection.nodes) {
|
||||
var newSelection = [];
|
||||
groups = selection.nodes.filter(function(n) { return n.type === "group" });
|
||||
let groups = selection.nodes.filter(function(n) { return n.type === "group" });
|
||||
|
||||
var historyEvent = {
|
||||
t:"ungroup",
|
||||
@@ -339,6 +344,7 @@ RED.group = (function() {
|
||||
}
|
||||
|
||||
function ungroup(g) {
|
||||
if (RED.workspaces.isLocked()) { return }
|
||||
var nodes = [];
|
||||
var parentGroup = RED.nodes.group(g.g);
|
||||
g.nodes.forEach(function(n) {
|
||||
@@ -365,6 +371,7 @@ RED.group = (function() {
|
||||
}
|
||||
|
||||
function mergeSelection() {
|
||||
if (RED.workspaces.isLocked()) { return }
|
||||
if (RED.view.state() !== RED.state.DEFAULT) { return }
|
||||
var selection = RED.view.selection();
|
||||
if (selection.nodes) {
|
||||
@@ -394,7 +401,7 @@ RED.group = (function() {
|
||||
}
|
||||
}
|
||||
var existingGroup;
|
||||
|
||||
var mergedEnv = {}
|
||||
// Second pass, ungroup any groups in the selection and add their contents
|
||||
// to the selection
|
||||
for (var i=0; i<selection.nodes.length; i++) {
|
||||
@@ -403,6 +410,11 @@ RED.group = (function() {
|
||||
if (!existingGroup) {
|
||||
existingGroup = n;
|
||||
}
|
||||
if (n.env && n.env.length > 0) {
|
||||
n.env.forEach(env => {
|
||||
mergedEnv[env.name] = env
|
||||
})
|
||||
}
|
||||
ungroupHistoryEvent.groups.push(n);
|
||||
nodes = nodes.concat(ungroup(n));
|
||||
} else {
|
||||
@@ -420,6 +432,7 @@ RED.group = (function() {
|
||||
group.style = existingGroup.style;
|
||||
group.name = existingGroup.name;
|
||||
}
|
||||
group.env = Object.values(mergedEnv)
|
||||
RED.view.select({nodes:[group]})
|
||||
}
|
||||
historyEvent.events.push({
|
||||
@@ -434,6 +447,7 @@ RED.group = (function() {
|
||||
}
|
||||
|
||||
function removeSelection() {
|
||||
if (RED.workspaces.isLocked()) { return }
|
||||
if (RED.view.state() !== RED.state.DEFAULT) { return }
|
||||
var selection = RED.view.selection();
|
||||
if (selection.nodes) {
|
||||
@@ -461,12 +475,21 @@ RED.group = (function() {
|
||||
}
|
||||
}
|
||||
function createGroup(nodes) {
|
||||
if (RED.workspaces.isLocked()) { return }
|
||||
if (nodes.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (nodes.filter(function(n) { return n.type === "subflow" }).length > 0) {
|
||||
RED.notify(RED._("group.errors.cannotAddSubflowPorts"),"error");
|
||||
return;
|
||||
const existingGroup = nodes[0].g
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const n = nodes[i]
|
||||
if (n.type === 'subflow') {
|
||||
RED.notify(RED._("group.errors.cannotAddSubflowPorts"),"error");
|
||||
return;
|
||||
}
|
||||
if (n.g !== existingGroup) {
|
||||
console.warn("Cannot add nooes with different z properties")
|
||||
return
|
||||
}
|
||||
}
|
||||
// nodes is an array
|
||||
// each node must be on the same tab (z)
|
||||
@@ -479,11 +502,16 @@ RED.group = (function() {
|
||||
y: Number.POSITIVE_INFINITY,
|
||||
w: 0,
|
||||
h: 0,
|
||||
_def: RED.group.def
|
||||
_def: RED.group.def,
|
||||
changed: true
|
||||
}
|
||||
|
||||
group.z = nodes[0].z;
|
||||
RED.nodes.addGroup(group);
|
||||
group = RED.nodes.addGroup(group);
|
||||
|
||||
if (existingGroup) {
|
||||
addToGroup(RED.nodes.group(existingGroup), group)
|
||||
}
|
||||
|
||||
try {
|
||||
addToGroup(group,nodes);
|
||||
@@ -508,7 +536,7 @@ RED.group = (function() {
|
||||
if (!z) {
|
||||
z = n.z;
|
||||
} else if (z !== n.z) {
|
||||
throw new Error("Cannot add nooes with different z properties")
|
||||
throw new Error("Cannot add nodes with different z properties")
|
||||
}
|
||||
if (n.g) {
|
||||
// This is already in a group.
|
||||
@@ -525,14 +553,10 @@ RED.group = (function() {
|
||||
throw new Error(RED._("group.errors.cannotCreateDiffGroups"))
|
||||
}
|
||||
}
|
||||
// The nodes are already in a group. The assumption is they should be
|
||||
// wrapped in the newly provided group, and that group added to in their
|
||||
// place to the existing containing group.
|
||||
// The nodes are already in a group - so we need to remove them first
|
||||
if (g) {
|
||||
g = RED.nodes.group(g);
|
||||
g.nodes.push(group);
|
||||
g.dirty = true;
|
||||
group.g = g.id;
|
||||
}
|
||||
// Second pass - add them to the group
|
||||
for (i=0;i<nodes.length;i++) {
|
||||
@@ -566,6 +590,7 @@ RED.group = (function() {
|
||||
markDirty(group);
|
||||
}
|
||||
function removeFromGroup(group, nodes, reparent) {
|
||||
if (RED.workspaces.isLocked()) { return }
|
||||
if (!Array.isArray(nodes)) {
|
||||
nodes = [nodes];
|
||||
}
|
||||
@@ -583,7 +608,7 @@ RED.group = (function() {
|
||||
n.dirty = true;
|
||||
var index = group.nodes.indexOf(n);
|
||||
group.nodes.splice(index,1);
|
||||
if (reparent && group.g) {
|
||||
if (reparent && parentGroup) {
|
||||
n.g = group.g
|
||||
parentGroup.nodes.push(n);
|
||||
} else {
|
||||
|
||||
46
packages/node_modules/@node-red/editor-client/src/js/ui/mermaid.js
vendored
Normal file
46
packages/node_modules/@node-red/editor-client/src/js/ui/mermaid.js
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
// Mermaid diagram stub library for on-demand dynamic loading
|
||||
// Will be overwritten after script loading by $.getScript
|
||||
var mermaid = (function () {
|
||||
var enabled /* = undefined */;
|
||||
|
||||
var initializing = false;
|
||||
var initCalled = false;
|
||||
|
||||
function initialize(opt) {
|
||||
if (enabled === undefined) {
|
||||
if (RED.settings.markdownEditor &&
|
||||
RED.settings.markdownEditor.mermaid) {
|
||||
enabled = RED.settings.markdownEditor.mermaid.enabled;
|
||||
}
|
||||
else {
|
||||
enabled = true;
|
||||
}
|
||||
}
|
||||
if (enabled) {
|
||||
initializing = true;
|
||||
$.getScript("vendor/mermaid/mermaid.min.js",
|
||||
function (data, stat, jqxhr) {
|
||||
$(".mermaid").show();
|
||||
// invoke loaded mermaid API
|
||||
initializing = false;
|
||||
mermaid.initialize(opt);
|
||||
if (initCalled) {
|
||||
mermaid.init();
|
||||
initCalled = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
if (initializing) {
|
||||
$(".mermaid").hide();
|
||||
initCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
initialize: initialize,
|
||||
init: init,
|
||||
};
|
||||
})();
|
||||
@@ -16,15 +16,17 @@
|
||||
RED.palette.editor = (function() {
|
||||
|
||||
var disabled = false;
|
||||
|
||||
let catalogues = []
|
||||
const loadedCatalogs = []
|
||||
var editorTabs;
|
||||
var filterInput;
|
||||
var searchInput;
|
||||
var nodeList;
|
||||
var packageList;
|
||||
var loadedList = [];
|
||||
var filteredList = [];
|
||||
var loadedIndex = {};
|
||||
let filterInput;
|
||||
let searchInput;
|
||||
let nodeList;
|
||||
let packageList;
|
||||
let fullList = []
|
||||
let loadedList = [];
|
||||
let filteredList = [];
|
||||
let loadedIndex = {};
|
||||
|
||||
var typesInUse = {};
|
||||
var nodeEntries = {};
|
||||
@@ -162,7 +164,6 @@ RED.palette.editor = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getContrastingBorder(rgbColor){
|
||||
var parts = /^rgba?\(\s*(\d+),\s*(\d+),\s*(\d+)[,)]/.exec(rgbColor);
|
||||
if (parts) {
|
||||
@@ -369,10 +370,10 @@ RED.palette.editor = (function() {
|
||||
var activeSort = sortModulesRelevance;
|
||||
|
||||
function handleCatalogResponse(err,catalog,index,v) {
|
||||
const url = catalog.url
|
||||
catalogueLoadStatus.push(err||v);
|
||||
if (!err) {
|
||||
if (v.modules) {
|
||||
var a = false;
|
||||
v.modules = v.modules.filter(function(m) {
|
||||
if (RED.utils.checkModuleAllowed(m.id,m.version,installAllowList,installDenyList)) {
|
||||
loadedIndex[m.id] = m;
|
||||
@@ -389,13 +390,14 @@ RED.palette.editor = (function() {
|
||||
m.timestamp = 0;
|
||||
}
|
||||
m.index = m.index.join(",").toLowerCase();
|
||||
m.catalog = catalog;
|
||||
m.catalogIndex = index;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
loadedList = loadedList.concat(v.modules);
|
||||
}
|
||||
searchInput.searchBox('count',loadedList.length);
|
||||
} else {
|
||||
catalogueLoadErrors = true;
|
||||
}
|
||||
@@ -404,7 +406,7 @@ RED.palette.editor = (function() {
|
||||
}
|
||||
if (catalogueLoadStatus.length === catalogueCount) {
|
||||
if (catalogueLoadErrors) {
|
||||
RED.notify(RED._('palette.editor.errors.catalogLoadFailed',{url: catalog}),"error",false,8000);
|
||||
RED.notify(RED._('palette.editor.errors.catalogLoadFailed',{url: url}),"error",false,8000);
|
||||
}
|
||||
var delta = 250-(Date.now() - catalogueLoadStart);
|
||||
setTimeout(function() {
|
||||
@@ -416,12 +418,13 @@ RED.palette.editor = (function() {
|
||||
|
||||
function initInstallTab() {
|
||||
if (loadedList.length === 0) {
|
||||
fullList = [];
|
||||
loadedList = [];
|
||||
loadedIndex = {};
|
||||
packageList.editableList('empty');
|
||||
|
||||
$(".red-ui-palette-module-shade-status").text(RED._('palette.editor.loading'));
|
||||
var catalogues = RED.settings.theme('palette.catalogues')||['https://catalogue.nodered.org/catalogue.json'];
|
||||
|
||||
catalogueLoadStatus = [];
|
||||
catalogueLoadErrors = false;
|
||||
catalogueCount = catalogues.length;
|
||||
@@ -431,27 +434,97 @@ RED.palette.editor = (function() {
|
||||
$("#red-ui-palette-module-install-shade").show();
|
||||
catalogueLoadStart = Date.now();
|
||||
var handled = 0;
|
||||
catalogues.forEach(function(catalog,index) {
|
||||
$.getJSON(catalog, {_: new Date().getTime()},function(v) {
|
||||
handleCatalogResponse(null,catalog,index,v);
|
||||
loadedCatalogs.length = 0; // clear the loadedCatalogs array
|
||||
for (let index = 0; index < catalogues.length; index++) {
|
||||
const url = catalogues[index];
|
||||
$.getJSON(url, {_: new Date().getTime()},function(v) {
|
||||
loadedCatalogs.push({ index: index, url: url, name: v.name, updated_at: v.updated_at, modules_count: (v.modules || []).length })
|
||||
handleCatalogResponse(null,{ url: url, name: v.name},index,v);
|
||||
refreshNodeModuleList();
|
||||
}).fail(function(jqxhr, textStatus, error) {
|
||||
console.warn("Error loading catalog",catalog,":",error);
|
||||
handleCatalogResponse(jqxhr,catalog,index);
|
||||
console.warn("Error loading catalog",url,":",error);
|
||||
handleCatalogResponse(jqxhr,url,index);
|
||||
}).always(function() {
|
||||
handled++;
|
||||
if (handled === catalogueCount) {
|
||||
searchInput.searchBox('change');
|
||||
//sort loadedCatalogs by e.index ascending
|
||||
loadedCatalogs.sort((a, b) => a.index - b.index)
|
||||
updateCatalogFilter(loadedCatalogs)
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the catalog filter dropdown and updates local variables
|
||||
* @param {[{url:String, name:String, updated_at:String, modules_count:Number}]} catalogEntries
|
||||
*/
|
||||
function updateCatalogFilter(catalogEntries, maxRetry = 3) {
|
||||
// clean up existing filters
|
||||
const catalogSelection = $('#red-catalogue-filter-select')
|
||||
if (catalogSelection.length === 0) {
|
||||
// sidebar not yet loaded (red-catalogue-filter-select is not in dom)
|
||||
if (maxRetry > 0) {
|
||||
// console.log("updateCatalogFilter: sidebar not yet loaded, retrying in 100ms")
|
||||
// try again in 100ms
|
||||
setTimeout(() => {
|
||||
updateCatalogFilter(catalogEntries, maxRetry - 1)
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
return; // give up
|
||||
}
|
||||
catalogSelection.off("change") // remove any existing event handlers
|
||||
catalogSelection.attr('disabled', 'disabled')
|
||||
catalogSelection.empty()
|
||||
catalogSelection.append($('<option>', { value: "loading", text: RED._('palette.editor.loading'), disabled: true, selected: true }));
|
||||
|
||||
fullList = loadedList.slice()
|
||||
catalogSelection.empty() // clear the select list
|
||||
|
||||
// loop through catalogTypes, and an option entry per catalog
|
||||
for (let index = 0; index < catalogEntries.length; index++) {
|
||||
const catalog = catalogEntries[index];
|
||||
catalogSelection.append(`<option value="${catalog.name}">${catalog.name}</option>`)
|
||||
}
|
||||
// select the 1st option in the select list
|
||||
catalogSelection.val(catalogSelection.find('option:first').val())
|
||||
|
||||
// if there is only 1 catalog, hide the select
|
||||
if (catalogEntries.length > 1) {
|
||||
catalogSelection.prepend(`<option value="all">${RED._('palette.editor.allCatalogs')}</option>`)
|
||||
catalogSelection.val('all')
|
||||
catalogSelection.removeAttr('disabled') // permit the user to select a catalog
|
||||
}
|
||||
// refresh the searchInput counter and trigger a change
|
||||
filterByCatalog(catalogSelection.val())
|
||||
searchInput.searchBox('change');
|
||||
|
||||
// hook up the change event handler
|
||||
catalogSelection.on("change", function() {
|
||||
const selectedCatalog = $(this).val();
|
||||
filterByCatalog(selectedCatalog);
|
||||
searchInput.searchBox('change');
|
||||
})
|
||||
}
|
||||
|
||||
function filterByCatalog(selectedCatalog) {
|
||||
if (loadedCatalogs.length <= 1 || selectedCatalog === "all") {
|
||||
loadedList = fullList.slice();
|
||||
} else {
|
||||
loadedList = fullList.filter(function(m) {
|
||||
return (m.catalog.name === selectedCatalog);
|
||||
})
|
||||
}
|
||||
refreshFilteredItems();
|
||||
searchInput.searchBox('count',filteredList.length+" / "+loadedList.length);
|
||||
}
|
||||
|
||||
function refreshFilteredItems() {
|
||||
packageList.editableList('empty');
|
||||
var currentFilter = searchInput.searchBox('value').trim();
|
||||
if (currentFilter === ""){
|
||||
if (currentFilter === "" && loadedList.length > 20){
|
||||
packageList.editableList('addItem',{count:loadedList.length})
|
||||
return;
|
||||
}
|
||||
@@ -462,7 +535,6 @@ RED.palette.editor = (function() {
|
||||
if (filteredList.length === 0) {
|
||||
packageList.editableList('addItem',{});
|
||||
}
|
||||
|
||||
if (filteredList.length > 10) {
|
||||
packageList.editableList('addItem',{start:10,more:filteredList.length-10})
|
||||
}
|
||||
@@ -492,6 +564,7 @@ RED.palette.editor = (function() {
|
||||
var updateDenyList = [];
|
||||
|
||||
function init() {
|
||||
catalogues = RED.settings.theme('palette.catalogues')||['https://catalogue.nodered.org/catalogue.json']
|
||||
if (RED.settings.get('externalModules.palette.allowInstall', true) === false) {
|
||||
return;
|
||||
}
|
||||
@@ -669,7 +742,8 @@ RED.palette.editor = (function() {
|
||||
});
|
||||
|
||||
|
||||
nodeList = $('<ol>',{id:"red-ui-palette-module-list", style:"position: absolute;top: 35px;bottom: 0;left: 0;right: 0px;"}).appendTo(modulesTab).editableList({
|
||||
nodeList = $('<ol>',{id:"red-ui-palette-module-list"}).appendTo(modulesTab).editableList({
|
||||
class: "scrollable",
|
||||
addButton: false,
|
||||
scrollOnAdd: false,
|
||||
sort: function(A,B) {
|
||||
@@ -800,28 +874,27 @@ RED.palette.editor = (function() {
|
||||
$('<div>',{class:"red-ui-search-empty"}).text(RED._('search.empty')).appendTo(container);
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function createInstallTab(content) {
|
||||
var installTab = $('<div>',{class:"red-ui-palette-editor-tab hide"}).appendTo(content);
|
||||
|
||||
const installTab = $('<div>',{class:"red-ui-palette-editor-tab", style: "display: none;"}).appendTo(content);
|
||||
editorTabs.addTab({
|
||||
id: 'install',
|
||||
label: RED._('palette.editor.tab-install'),
|
||||
content: installTab
|
||||
})
|
||||
|
||||
var toolBar = $('<div>',{class:"red-ui-palette-editor-toolbar"}).appendTo(installTab);
|
||||
|
||||
var searchDiv = $('<div>',{class:"red-ui-palette-search"}).appendTo(installTab);
|
||||
const toolBar = $('<div>',{class:"red-ui-palette-editor-toolbar"}).appendTo(installTab);
|
||||
|
||||
const searchDiv = $('<div>',{class:"red-ui-palette-search"}).appendTo(installTab);
|
||||
searchInput = $('<input type="text" data-i18n="[placeholder]palette.search"></input>')
|
||||
.appendTo(searchDiv)
|
||||
.searchBox({
|
||||
delay: 300,
|
||||
change: function() {
|
||||
var searchTerm = $(this).val().trim().toLowerCase();
|
||||
if (searchTerm.length > 0) {
|
||||
if (searchTerm.length > 0 || loadedList.length < 20) {
|
||||
filteredList = loadedList.filter(function(m) {
|
||||
return (m.index.indexOf(searchTerm) > -1);
|
||||
}).map(function(f) { return {info:f}});
|
||||
@@ -831,19 +904,26 @@ RED.palette.editor = (function() {
|
||||
searchInput.searchBox('count',loadedList.length);
|
||||
packageList.editableList('empty');
|
||||
packageList.editableList('addItem',{count:loadedList.length});
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$('<span>').text(RED._("palette.editor.sort")+' ').appendTo(toolBar);
|
||||
var sortGroup = $('<span class="button-group"></span>').appendTo(toolBar);
|
||||
var sortRelevance = $('<a href="#" class="red-ui-palette-editor-install-sort-option red-ui-sidebar-header-button-toggle selected"><i class="fa fa-sort-amount-desc"></i></a>').appendTo(sortGroup);
|
||||
var sortAZ = $('<a href="#" class="red-ui-palette-editor-install-sort-option red-ui-sidebar-header-button-toggle" data-i18n="palette.editor.sortAZ"></a>').appendTo(sortGroup);
|
||||
var sortRecent = $('<a href="#" class="red-ui-palette-editor-install-sort-option red-ui-sidebar-header-button-toggle" data-i18n="palette.editor.sortRecent"></a>').appendTo(sortGroup);
|
||||
const catalogSelection = $('<select id="red-catalogue-filter-select">').appendTo(toolBar);
|
||||
catalogSelection.addClass('red-ui-palette-editor-catalogue-filter');
|
||||
|
||||
const toolBarActions = $('<div>',{class:"red-ui-palette-editor-toolbar-actions"}).appendTo(toolBar);
|
||||
|
||||
$('<span>').text(RED._("palette.editor.sort")+' ').appendTo(toolBarActions);
|
||||
const sortGroup = $('<span class="button-group"></span>').appendTo(toolBarActions);
|
||||
const sortRelevance = $('<a href="#" class="red-ui-palette-editor-install-sort-option red-ui-sidebar-header-button-toggle selected"><i class="fa fa-sort-amount-desc"></i></a>').appendTo(sortGroup);
|
||||
const sortAZ = $('<a href="#" class="red-ui-palette-editor-install-sort-option red-ui-sidebar-header-button-toggle"><i class="fa fa-sort-alpha-asc"></i></a>').appendTo(sortGroup);
|
||||
const sortRecent = $('<a href="#" class="red-ui-palette-editor-install-sort-option red-ui-sidebar-header-button-toggle"><i class="fa fa-calendar"></i></a>').appendTo(sortGroup);
|
||||
RED.popover.tooltip(sortRelevance,RED._("palette.editor.sortRelevance"));
|
||||
RED.popover.tooltip(sortAZ,RED._("palette.editor.sortAZ"));
|
||||
RED.popover.tooltip(sortRecent,RED._("palette.editor.sortRecent"));
|
||||
|
||||
|
||||
var sortOpts = [
|
||||
const sortOpts = [
|
||||
{button: sortRelevance, func: sortModulesRelevance},
|
||||
{button: sortAZ, func: sortModulesAZ},
|
||||
{button: sortRecent, func: sortModulesRecent}
|
||||
@@ -861,7 +941,7 @@ RED.palette.editor = (function() {
|
||||
});
|
||||
});
|
||||
|
||||
var refreshSpan = $('<span>').appendTo(toolBar);
|
||||
var refreshSpan = $('<span>').appendTo(toolBarActions);
|
||||
var refreshButton = $('<a href="#" class="red-ui-sidebar-header-button"><i class="fa fa-refresh"></i></a>').appendTo(refreshSpan);
|
||||
refreshButton.on("click", function(e) {
|
||||
e.preventDefault();
|
||||
@@ -871,7 +951,8 @@ RED.palette.editor = (function() {
|
||||
})
|
||||
RED.popover.tooltip(refreshButton,RED._("palette.editor.refresh"));
|
||||
|
||||
packageList = $('<ol>',{style:"position: absolute;top: 79px;bottom: 0;left: 0;right: 0px;"}).appendTo(installTab).editableList({
|
||||
packageList = $('<ol>').appendTo(installTab).editableList({
|
||||
class: "scrollable",
|
||||
addButton: false,
|
||||
scrollOnAdd: false,
|
||||
addItem: function(container,i,object) {
|
||||
@@ -906,6 +987,9 @@ RED.palette.editor = (function() {
|
||||
var metaRow = $('<div class="red-ui-palette-module-meta"></div>').appendTo(headerRow);
|
||||
$('<span class="red-ui-palette-module-version"><i class="fa fa-tag"></i> '+entry.version+'</span>').appendTo(metaRow);
|
||||
$('<span class="red-ui-palette-module-updated"><i class="fa fa-calendar"></i> '+formatUpdatedAt(entry.updated_at)+'</span>').appendTo(metaRow);
|
||||
if (loadedCatalogs.length > 1) {
|
||||
$('<span class="red-ui-palette-module-updated"><i class="fa fa-cubes"></i>' + (entry.catalog.name || entry.catalog.url) + '</span>').appendTo(metaRow);
|
||||
}
|
||||
|
||||
var duplicateType = false;
|
||||
if (entry.types && entry.types.length > 0) {
|
||||
@@ -952,9 +1036,10 @@ RED.palette.editor = (function() {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (RED.settings.get('externalModules.palette.allowUpload', true) !== false) {
|
||||
var uploadSpan = $('<span class="button-group">').prependTo(toolBar);
|
||||
var uploadSpan = $('<span class="button-group">').prependTo(toolBarActions);
|
||||
var uploadButton = $('<button type="button" class="red-ui-sidebar-header-button red-ui-palette-editor-upload-button"><label><i class="fa fa-upload"></i><form id="red-ui-palette-editor-upload-form" enctype="multipart/form-data"><input name="tarball" type="file" accept=".tgz"></label></button>').appendTo(uploadSpan);
|
||||
|
||||
var uploadInput = uploadButton.find('input[type="file"]');
|
||||
|
||||
@@ -171,23 +171,15 @@ RED.palette = (function() {
|
||||
}
|
||||
metaData += type;
|
||||
|
||||
const safeType = type.replace(/'/g,"\\'");
|
||||
const searchType = type.indexOf(' ') > -1 ? '"' + type + '"' : type
|
||||
|
||||
if (/^subflow:/.test(type)) {
|
||||
$('<button type="button" onclick="RED.workspaces.show(\''+type.substring(8).replace(/'/g,"\\'")+'\'); return false;" class="red-ui-button red-ui-button-small" style="float: right; margin-left: 5px;"><i class="fa fa-pencil"></i></button>').appendTo(popOverContent)
|
||||
}
|
||||
|
||||
const safeType = type.replace(/'/g,"\\'");
|
||||
const wrapStr = function (str) {
|
||||
if(str.indexOf(' ') >= 0) {
|
||||
return '"' + str + '"'
|
||||
}
|
||||
return str
|
||||
}
|
||||
$('<button type="button" onclick="RED.search.show(\'type:'+searchType+'\'); return false;" class="red-ui-button red-ui-button-small" style="float: right; margin-left: 5px;"><i class="fa fa-search"></i></button>').appendTo(popOverContent)
|
||||
|
||||
$('<button type="button"; return false;" class="red-ui-button red-ui-button-small" style="float: right; margin-left: 5px;"><i class="fa fa-search"></i></button>')
|
||||
.appendTo(popOverContent)
|
||||
.on('click', function() {
|
||||
RED.search.show('type:' + wrapStr(safeType))
|
||||
})
|
||||
$('<button type="button" onclick="RED.sidebar.help.show(\''+safeType+'\'); return false;" class="red-ui-button red-ui-button-small" style="float: right; margin-left: 5px;"><i class="fa fa-book"></i></button>').appendTo(popOverContent)
|
||||
|
||||
$('<p>',{style:"font-size: 0.8em"}).text(metaData).appendTo(popOverContent);
|
||||
@@ -292,6 +284,7 @@ RED.palette = (function() {
|
||||
var hoverGroup;
|
||||
var paletteWidth;
|
||||
var paletteTop;
|
||||
var dropEnabled;
|
||||
$(d).draggable({
|
||||
helper: 'clone',
|
||||
appendTo: '#red-ui-editor',
|
||||
@@ -299,6 +292,7 @@ RED.palette = (function() {
|
||||
revertDuration: 200,
|
||||
containment:'#red-ui-main-container',
|
||||
start: function() {
|
||||
dropEnabled = !(RED.nodes.workspace(RED.workspaces.active())?.locked);
|
||||
paletteWidth = $("#red-ui-palette").width();
|
||||
paletteTop = $("#red-ui-palette").parent().position().top + $("#red-ui-palette-container").position().top;
|
||||
hoverGroup = null;
|
||||
@@ -309,96 +303,100 @@ RED.palette = (function() {
|
||||
RED.view.focus();
|
||||
},
|
||||
stop: function() {
|
||||
d3.select('.red-ui-flow-link-splice').classed('red-ui-flow-link-splice',false);
|
||||
if (hoverGroup) {
|
||||
document.getElementById("group_select_"+hoverGroup.id).classList.remove("red-ui-flow-group-hovered");
|
||||
if (dropEnabled) {
|
||||
d3.select('.red-ui-flow-link-splice').classed('red-ui-flow-link-splice',false);
|
||||
if (hoverGroup) {
|
||||
document.getElementById("group_select_"+hoverGroup.id).classList.remove("red-ui-flow-group-hovered");
|
||||
}
|
||||
if (activeGroup) {
|
||||
document.getElementById("group_select_"+activeGroup.id).classList.remove("red-ui-flow-group-active-hovered");
|
||||
}
|
||||
if (spliceTimer) { clearTimeout(spliceTimer); spliceTimer = null; }
|
||||
if (groupTimer) { clearTimeout(groupTimer); groupTimer = null; }
|
||||
}
|
||||
if (activeGroup) {
|
||||
document.getElementById("group_select_"+activeGroup.id).classList.remove("red-ui-flow-group-active-hovered");
|
||||
}
|
||||
if (spliceTimer) { clearTimeout(spliceTimer); spliceTimer = null; }
|
||||
if (groupTimer) { clearTimeout(groupTimer); groupTimer = null; }
|
||||
},
|
||||
drag: function(e,ui) {
|
||||
var paletteNode = getPaletteNode(nt);
|
||||
ui.originalPosition.left = paletteNode.offset().left;
|
||||
mouseX = ui.position.left - paletteWidth + (ui.helper.width()/2) + chart.scrollLeft();
|
||||
mouseY = ui.position.top - paletteTop + (ui.helper.height()/2) + chart.scrollTop() + 10;
|
||||
if (!groupTimer) {
|
||||
groupTimer = setTimeout(function() {
|
||||
var mx = mouseX / RED.view.scale();
|
||||
var my = mouseY / RED.view.scale();
|
||||
var group = RED.view.getGroupAtPoint(mx,my);
|
||||
if (group !== hoverGroup) {
|
||||
if (hoverGroup) {
|
||||
document.getElementById("group_select_"+hoverGroup.id).classList.remove("red-ui-flow-group-hovered");
|
||||
}
|
||||
if (group) {
|
||||
document.getElementById("group_select_"+group.id).classList.add("red-ui-flow-group-hovered");
|
||||
}
|
||||
hoverGroup = group;
|
||||
if (hoverGroup) {
|
||||
$(ui.helper).data('group',hoverGroup);
|
||||
} else {
|
||||
$(ui.helper).removeData('group');
|
||||
}
|
||||
}
|
||||
groupTimer = null;
|
||||
|
||||
},200)
|
||||
}
|
||||
if (def.inputs > 0 && def.outputs > 0) {
|
||||
if (!spliceTimer) {
|
||||
spliceTimer = setTimeout(function() {
|
||||
var nodes = [];
|
||||
var bestDistance = Infinity;
|
||||
var bestLink = null;
|
||||
if (chartSVG.getIntersectionList) {
|
||||
var svgRect = chartSVG.createSVGRect();
|
||||
svgRect.x = mouseX;
|
||||
svgRect.y = mouseY;
|
||||
svgRect.width = 1;
|
||||
svgRect.height = 1;
|
||||
nodes = chartSVG.getIntersectionList(svgRect,chartSVG);
|
||||
} else {
|
||||
// Firefox doesn't do getIntersectionList and that
|
||||
// makes us sad
|
||||
nodes = RED.view.getLinksAtPoint(mouseX,mouseY);
|
||||
}
|
||||
if (dropEnabled) {
|
||||
mouseX = ui.position.left - paletteWidth + (ui.helper.width()/2) + chart.scrollLeft();
|
||||
mouseY = ui.position.top - paletteTop + (ui.helper.height()/2) + chart.scrollTop() + 10;
|
||||
if (!groupTimer) {
|
||||
groupTimer = setTimeout(function() {
|
||||
var mx = mouseX / RED.view.scale();
|
||||
var my = mouseY / RED.view.scale();
|
||||
for (var i=0;i<nodes.length;i++) {
|
||||
var node = d3.select(nodes[i]);
|
||||
if (node.classed('red-ui-flow-link-background') && !node.classed('red-ui-flow-link-link')) {
|
||||
var length = nodes[i].getTotalLength();
|
||||
for (var j=0;j<length;j+=10) {
|
||||
var p = nodes[i].getPointAtLength(j);
|
||||
var d2 = ((p.x-mx)*(p.x-mx))+((p.y-my)*(p.y-my));
|
||||
if (d2 < 200 && d2 < bestDistance) {
|
||||
bestDistance = d2;
|
||||
bestLink = nodes[i];
|
||||
var group = RED.view.getGroupAtPoint(mx,my);
|
||||
if (group !== hoverGroup) {
|
||||
if (hoverGroup) {
|
||||
document.getElementById("group_select_"+hoverGroup.id).classList.remove("red-ui-flow-group-hovered");
|
||||
}
|
||||
if (group) {
|
||||
document.getElementById("group_select_"+group.id).classList.add("red-ui-flow-group-hovered");
|
||||
}
|
||||
hoverGroup = group;
|
||||
if (hoverGroup) {
|
||||
$(ui.helper).data('group',hoverGroup);
|
||||
} else {
|
||||
$(ui.helper).removeData('group');
|
||||
}
|
||||
}
|
||||
groupTimer = null;
|
||||
|
||||
},200)
|
||||
}
|
||||
if (def.inputs > 0 && def.outputs > 0) {
|
||||
if (!spliceTimer) {
|
||||
spliceTimer = setTimeout(function() {
|
||||
var nodes = [];
|
||||
var bestDistance = Infinity;
|
||||
var bestLink = null;
|
||||
if (chartSVG.getIntersectionList) {
|
||||
var svgRect = chartSVG.createSVGRect();
|
||||
svgRect.x = mouseX;
|
||||
svgRect.y = mouseY;
|
||||
svgRect.width = 1;
|
||||
svgRect.height = 1;
|
||||
nodes = chartSVG.getIntersectionList(svgRect,chartSVG);
|
||||
} else {
|
||||
// Firefox doesn't do getIntersectionList and that
|
||||
// makes us sad
|
||||
nodes = RED.view.getLinksAtPoint(mouseX,mouseY);
|
||||
}
|
||||
var mx = mouseX / RED.view.scale();
|
||||
var my = mouseY / RED.view.scale();
|
||||
for (var i=0;i<nodes.length;i++) {
|
||||
var node = d3.select(nodes[i]);
|
||||
if (node.classed('red-ui-flow-link-background') && !node.classed('red-ui-flow-link-link')) {
|
||||
var length = nodes[i].getTotalLength();
|
||||
for (var j=0;j<length;j+=10) {
|
||||
var p = nodes[i].getPointAtLength(j);
|
||||
var d2 = ((p.x-mx)*(p.x-mx))+((p.y-my)*(p.y-my));
|
||||
if (d2 < 200 && d2 < bestDistance) {
|
||||
bestDistance = d2;
|
||||
bestLink = nodes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (activeSpliceLink && activeSpliceLink !== bestLink) {
|
||||
d3.select(activeSpliceLink.parentNode).classed('red-ui-flow-link-splice',false);
|
||||
}
|
||||
if (bestLink) {
|
||||
d3.select(bestLink.parentNode).classed('red-ui-flow-link-splice',true)
|
||||
} else {
|
||||
d3.select('.red-ui-flow-link-splice').classed('red-ui-flow-link-splice',false);
|
||||
}
|
||||
if (activeSpliceLink !== bestLink) {
|
||||
if (bestLink) {
|
||||
$(ui.helper).data('splice',d3.select(bestLink).data()[0]);
|
||||
} else {
|
||||
$(ui.helper).removeData('splice');
|
||||
if (activeSpliceLink && activeSpliceLink !== bestLink) {
|
||||
d3.select(activeSpliceLink.parentNode).classed('red-ui-flow-link-splice',false);
|
||||
}
|
||||
}
|
||||
activeSpliceLink = bestLink;
|
||||
spliceTimer = null;
|
||||
},200);
|
||||
if (bestLink) {
|
||||
d3.select(bestLink.parentNode).classed('red-ui-flow-link-splice',true)
|
||||
} else {
|
||||
d3.select('.red-ui-flow-link-splice').classed('red-ui-flow-link-splice',false);
|
||||
}
|
||||
if (activeSpliceLink !== bestLink) {
|
||||
if (bestLink) {
|
||||
$(ui.helper).data('splice',d3.select(bestLink).data()[0]);
|
||||
} else {
|
||||
$(ui.helper).removeData('splice');
|
||||
}
|
||||
}
|
||||
activeSpliceLink = bestLink;
|
||||
spliceTimer = null;
|
||||
},200);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,6 +165,9 @@ RED.projects.settings = (function() {
|
||||
}
|
||||
var description = addTargetToExternalLinks($('<span class="red-ui-text-bidi-aware" dir=\"'+RED.text.bidi.resolveBaseTextDir(desc)+'">'+desc+'</span>')).appendTo(container);
|
||||
description.find(".red-ui-text-bidi-aware").contents().filter(function() { return this.nodeType === 3 && this.textContent.trim() !== "" }).wrap( "<span></span>" );
|
||||
setTimeout(function () {
|
||||
mermaid.init();
|
||||
}, 200);
|
||||
}
|
||||
|
||||
function editSummary(activeProject, summary, container, version, versionContainer) {
|
||||
|
||||
@@ -46,7 +46,9 @@ RED.subflow = (function() {
|
||||
'</script>';
|
||||
|
||||
function findAvailableSubflowIOPosition(subflow,isInput) {
|
||||
var pos = {x:50,y:30};
|
||||
const scrollPos = RED.view.scroll()
|
||||
const scaleFactor = RED.view.scale()
|
||||
var pos = { x: (scrollPos[0]/scaleFactor)+50, y: (scrollPos[1]/scaleFactor)+30 };
|
||||
if (!isInput) {
|
||||
pos.x += 110;
|
||||
}
|
||||
@@ -273,6 +275,11 @@ RED.subflow = (function() {
|
||||
var subflowInstances = [];
|
||||
if (activeSubflow) {
|
||||
RED.nodes.filterNodes({type:"subflow:"+activeSubflow.id}).forEach(function(n) {
|
||||
const parentFlow = RED.nodes.workspace(n.z)
|
||||
const wasLocked = parentFlow && parentFlow.locked
|
||||
if (wasLocked) {
|
||||
parentFlow.locked = false
|
||||
}
|
||||
subflowInstances.push({
|
||||
id: n.id,
|
||||
changed: n.changed
|
||||
@@ -285,6 +292,9 @@ RED.subflow = (function() {
|
||||
n.resize = true;
|
||||
n.dirty = true;
|
||||
RED.editor.updateNodeProperties(n);
|
||||
if (wasLocked) {
|
||||
parentFlow.locked = true
|
||||
}
|
||||
});
|
||||
RED.editor.validateNode(activeSubflow);
|
||||
return {
|
||||
@@ -431,44 +441,7 @@ RED.subflow = (function() {
|
||||
|
||||
$("#red-ui-subflow-delete").on("click", function(event) {
|
||||
event.preventDefault();
|
||||
var subflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
if (subflow.instances.length > 0) {
|
||||
var msg = $('<div>')
|
||||
$('<p>').text(RED._("subflow.subflowInstances",{count: subflow.instances.length})).appendTo(msg);
|
||||
$('<p>').text(RED._("subflow.confirmDelete")).appendTo(msg);
|
||||
var confirmDeleteNotification = RED.notify(msg, {
|
||||
modal: true,
|
||||
fixed: true,
|
||||
buttons: [
|
||||
{
|
||||
text: RED._('common.label.cancel'),
|
||||
click: function() {
|
||||
confirmDeleteNotification.close();
|
||||
}
|
||||
},
|
||||
{
|
||||
text: RED._('workspace.confirmDelete'),
|
||||
class: "primary",
|
||||
click: function() {
|
||||
confirmDeleteNotification.close();
|
||||
completeDelete();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
return;
|
||||
} else {
|
||||
completeDelete();
|
||||
}
|
||||
function completeDelete() {
|
||||
var startDirty = RED.nodes.dirty();
|
||||
var historyEvent = removeSubflow(RED.workspaces.active());
|
||||
historyEvent.t = 'delete';
|
||||
historyEvent.dirty = startDirty;
|
||||
RED.history.push(historyEvent);
|
||||
}
|
||||
|
||||
RED.subflow.delete(RED.workspaces.active())
|
||||
});
|
||||
|
||||
refreshToolbar(activeSubflow);
|
||||
@@ -481,7 +454,51 @@ RED.subflow = (function() {
|
||||
$("#red-ui-workspace-toolbar").hide().empty();
|
||||
$("#red-ui-workspace-chart").css({"margin-top": "0"});
|
||||
}
|
||||
function deleteSubflow(id) {
|
||||
const subflow = RED.nodes.subflow(id || RED.workspaces.active());
|
||||
if (!subflow) {
|
||||
return
|
||||
}
|
||||
if (subflow.instances.length > 0) {
|
||||
if (subflow.instances.some(sf => { const ws = RED.nodes.workspace(sf.z); return ws?ws.locked:false })) {
|
||||
return
|
||||
}
|
||||
const msg = $('<div>')
|
||||
$('<p>').text(RED._("subflow.subflowInstances",{count: subflow.instances.length})).appendTo(msg);
|
||||
$('<p>').text(RED._("subflow.confirmDelete")).appendTo(msg);
|
||||
const confirmDeleteNotification = RED.notify(msg, {
|
||||
modal: true,
|
||||
fixed: true,
|
||||
buttons: [
|
||||
{
|
||||
text: RED._('common.label.cancel'),
|
||||
click: function() {
|
||||
confirmDeleteNotification.close();
|
||||
}
|
||||
},
|
||||
{
|
||||
text: RED._('workspace.confirmDelete'),
|
||||
class: "primary",
|
||||
click: function() {
|
||||
confirmDeleteNotification.close();
|
||||
completeDelete();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
return;
|
||||
} else {
|
||||
completeDelete();
|
||||
}
|
||||
function completeDelete() {
|
||||
const startDirty = RED.nodes.dirty();
|
||||
const historyEvent = removeSubflow(subflow.id);
|
||||
historyEvent.t = 'delete';
|
||||
historyEvent.dirty = startDirty;
|
||||
RED.history.push(historyEvent);
|
||||
}
|
||||
}
|
||||
function removeSubflow(id, keepInstanceNodes) {
|
||||
// TODO: A lot of this logic is common with RED.nodes.removeWorkspace
|
||||
var removedNodes = [];
|
||||
@@ -558,7 +575,7 @@ RED.subflow = (function() {
|
||||
}
|
||||
});
|
||||
RED.events.on("view:selection-changed",function(selection) {
|
||||
if (!selection.nodes) {
|
||||
if (!selection.nodes || RED.workspaces.isLocked()) {
|
||||
RED.menu.setDisabled("menu-item-subflow-convert",true);
|
||||
} else {
|
||||
RED.menu.setDisabled("menu-item-subflow-convert",false);
|
||||
@@ -621,6 +638,9 @@ RED.subflow = (function() {
|
||||
}
|
||||
|
||||
function convertToSubflow() {
|
||||
if (RED.workspaces.isLocked()) {
|
||||
return
|
||||
}
|
||||
var selection = RED.view.selection();
|
||||
if (!selection.nodes) {
|
||||
RED.notify(RED._("subflow.errors.noNodesSelected"),"error");
|
||||
@@ -775,7 +795,7 @@ RED.subflow = (function() {
|
||||
}
|
||||
subflowInstance._def = RED.nodes.getType(subflowInstance.type);
|
||||
RED.editor.validateNode(subflowInstance);
|
||||
RED.nodes.add(subflowInstance);
|
||||
subflowInstance = RED.nodes.add(subflowInstance);
|
||||
|
||||
if (containingGroup) {
|
||||
RED.group.addToGroup(containingGroup, subflowInstance);
|
||||
@@ -1329,7 +1349,10 @@ RED.subflow = (function() {
|
||||
init: init,
|
||||
createSubflow: createSubflow,
|
||||
convertToSubflow: convertToSubflow,
|
||||
// removeSubflow: Internal function to remove subflow
|
||||
removeSubflow: removeSubflow,
|
||||
// delete: Prompt user for confirmation
|
||||
delete: deleteSubflow,
|
||||
refresh: refresh,
|
||||
removeInput: removeSubflowInput,
|
||||
removeOutput: removeSubflowOutput,
|
||||
|
||||
@@ -43,12 +43,15 @@ RED.sidebar.config = (function() {
|
||||
|
||||
var categories = {};
|
||||
|
||||
function getOrCreateCategory(name,parent,label) {
|
||||
function getOrCreateCategory(name,parent,label,isLocked) {
|
||||
name = name.replace(/\./i,"-");
|
||||
if (!categories[name]) {
|
||||
var container = $('<div class="red-ui-palette-category red-ui-sidebar-config-category" id="red-ui-sidebar-config-category-'+name+'"></div>').appendTo(parent);
|
||||
var header = $('<div class="red-ui-sidebar-config-tray-header red-ui-palette-header"><i class="fa fa-angle-down expanded"></i></div>').appendTo(container);
|
||||
let lockIcon
|
||||
if (label) {
|
||||
lockIcon = $('<span style="margin-right: 5px"><i class="fa fa-lock"/></span>').appendTo(header)
|
||||
lockIcon.toggle(!!isLocked)
|
||||
$('<span class="red-ui-palette-node-config-label"/>').text(label).appendTo(header);
|
||||
} else {
|
||||
$('<span class="red-ui-palette-node-config-label" data-i18n="sidebar.config.'+name+'">').appendTo(header);
|
||||
@@ -62,6 +65,7 @@ RED.sidebar.config = (function() {
|
||||
var icon = header.find("i");
|
||||
var result = {
|
||||
label: label,
|
||||
lockIcon,
|
||||
list: category,
|
||||
size: function() {
|
||||
return result.list.find("li:not(.red-ui-palette-node-config-none)").length
|
||||
@@ -100,6 +104,9 @@ RED.sidebar.config = (function() {
|
||||
});
|
||||
categories[name] = result;
|
||||
} else {
|
||||
if (isLocked !== undefined && categories[name].lockIcon) {
|
||||
categories[name].lockIcon.toggle(!!isLocked)
|
||||
}
|
||||
if (categories[name].label !== label) {
|
||||
categories[name].list.parent().find('.red-ui-palette-node-config-label').text(label);
|
||||
categories[name].label = label;
|
||||
@@ -138,17 +145,19 @@ RED.sidebar.config = (function() {
|
||||
} else {
|
||||
var currentType = "";
|
||||
nodes.forEach(function(node) {
|
||||
var label = RED.utils.getNodeLabel(node,node.id);
|
||||
var labelText = RED.utils.getNodeLabel(node,node.id);
|
||||
if (node.type != currentType) {
|
||||
$('<li class="red-ui-palette-node-config-type">'+node.type+'</li>').appendTo(list);
|
||||
currentType = node.type;
|
||||
}
|
||||
|
||||
if (node.changed) {
|
||||
labelText += "!!"
|
||||
}
|
||||
var entry = $('<li class="red-ui-palette-node_id_'+node.id.replace(/\./g,"-")+'"></li>').appendTo(list);
|
||||
var nodeDiv = $('<div class="red-ui-palette-node-config red-ui-palette-node"></div>').appendTo(entry);
|
||||
entry.data('node',node.id);
|
||||
nodeDiv.data('node',node.id);
|
||||
var label = $('<div class="red-ui-palette-label"></div>').text(label).appendTo(nodeDiv);
|
||||
var label = $('<div class="red-ui-palette-label"></div>').text(labelText).appendTo(nodeDiv);
|
||||
if (node.d) {
|
||||
nodeDiv.addClass("red-ui-palette-node-config-disabled");
|
||||
$('<i class="fa fa-ban"></i>').prependTo(label);
|
||||
@@ -216,7 +225,7 @@ RED.sidebar.config = (function() {
|
||||
|
||||
RED.nodes.eachWorkspace(function(ws) {
|
||||
validList[ws.id.replace(/\./g,"-")] = true;
|
||||
getOrCreateCategory(ws.id,flowCategories,ws.label);
|
||||
getOrCreateCategory(ws.id,flowCategories,ws.label, ws.locked);
|
||||
})
|
||||
RED.nodes.eachSubflow(function(sf) {
|
||||
validList[sf.id.replace(/\./g,"-")] = true;
|
||||
@@ -274,6 +283,15 @@ RED.sidebar.config = (function() {
|
||||
changes: {},
|
||||
dirty: RED.nodes.dirty()
|
||||
}
|
||||
for (let i = 0; i < selectedNodes.length; i++) {
|
||||
let node = RED.nodes.node(selectedNodes[i])
|
||||
if (node.z) {
|
||||
let ws = RED.nodes.workspace(node.z)
|
||||
if (ws && ws.locked) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
selectedNodes.forEach(function(id) {
|
||||
var node = RED.nodes.node(id);
|
||||
try {
|
||||
|
||||
@@ -218,11 +218,11 @@ RED.sidebar.context = (function() {
|
||||
var obj = $(propRow.children()[0]);
|
||||
obj.text(k);
|
||||
var tools = $('<span class="button-group"></span>');
|
||||
|
||||
const urlSafeK = encodeURIComponent(k)
|
||||
var refreshItem = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-refresh"></i></button>').appendTo(tools).on("click", function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
$.getJSON(baseUrl+"/"+k+"?store="+v.store, function(data) {
|
||||
$.getJSON(baseUrl+"/"+urlSafeK+"?store="+v.store, function(data) {
|
||||
if (data.msg !== payload || data.format !== format) {
|
||||
payload = data.msg;
|
||||
format = data.format;
|
||||
@@ -258,11 +258,12 @@ RED.sidebar.context = (function() {
|
||||
$('<button class="red-ui-button primary" data-i18n="common.label.delete"></button>').appendTo(bg).on("click", function(e) {
|
||||
e.preventDefault();
|
||||
popover.close();
|
||||
const urlSafeK = encodeURIComponent(k)
|
||||
$.ajax({
|
||||
url: baseUrl+"/"+k+"?store="+v.store,
|
||||
url: baseUrl+"/"+urlSafeK+"?store="+v.store,
|
||||
type: "DELETE"
|
||||
}).done(function(data,textStatus,xhr) {
|
||||
$.getJSON(baseUrl+"/"+k+"?store="+v.store, function(data) {
|
||||
$.getJSON(baseUrl+"/"+urlSafeK+"?store="+v.store, function(data) {
|
||||
if (data.format === 'undefined') {
|
||||
propRow.remove();
|
||||
if (container.children().length === 0) {
|
||||
|
||||
@@ -141,7 +141,8 @@ RED.sidebar.help = (function() {
|
||||
RED.events.on('registry:node-type-removed', queueRefresh);
|
||||
RED.events.on('subflows:change', refreshSubflow);
|
||||
|
||||
RED.actions.add("core:show-help-tab",show);
|
||||
RED.actions.add("core:show-help-tab", show);
|
||||
RED.actions.add("core:show-node-help", showNodeHelp)
|
||||
|
||||
}
|
||||
|
||||
@@ -338,6 +339,19 @@ RED.sidebar.help = (function() {
|
||||
resizeStack();
|
||||
}
|
||||
|
||||
function showNodeHelp(node) {
|
||||
if (!node) {
|
||||
const selection = RED.view.selection()
|
||||
if (selection.nodes && selection.nodes.length > 0) {
|
||||
node = selection.nodes.find(n => n.type !== 'group' && n.type !== 'junction')
|
||||
}
|
||||
}
|
||||
if (node) {
|
||||
show(node.type, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: DRY - projects.js
|
||||
function addTargetToExternalLinks(el) {
|
||||
$(el).find("a").each(function(el) {
|
||||
|
||||
@@ -225,6 +225,22 @@ RED.sidebar.info.outliner = (function() {
|
||||
} else {
|
||||
$('<div class="red-ui-info-outline-item-control-spacer">').appendTo(controls)
|
||||
}
|
||||
if (n.type === 'tab') {
|
||||
var lockToggleButton = $('<button type="button" class="red-ui-info-outline-item-control-lock red-ui-button red-ui-button-small"><i class="fa fa-unlock-alt"></i><i class="fa fa-lock"></i></button>').appendTo(controls).on("click",function(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
if (n.locked) {
|
||||
RED.workspaces.unlock(n.id)
|
||||
} else {
|
||||
RED.workspaces.lock(n.id)
|
||||
}
|
||||
})
|
||||
RED.popover.tooltip(lockToggleButton,function() {
|
||||
return RED._("common.label."+(n.locked?"unlock":"lock"));
|
||||
});
|
||||
} else {
|
||||
$('<div class="red-ui-info-outline-item-control-spacer">').appendTo(controls)
|
||||
}
|
||||
controls.find("button").on("dblclick", function(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
@@ -368,6 +384,8 @@ RED.sidebar.info.outliner = (function() {
|
||||
flowList.treeList.addChild(objects[ws.id])
|
||||
objects[ws.id].element.toggleClass("red-ui-info-outline-item-disabled", !!ws.disabled)
|
||||
objects[ws.id].treeList.container.toggleClass("red-ui-info-outline-item-disabled", !!ws.disabled)
|
||||
objects[ws.id].element.toggleClass("red-ui-info-outline-item-locked", !!ws.locked)
|
||||
objects[ws.id].treeList.container.toggleClass("red-ui-info-outline-item-locked", !!ws.locked)
|
||||
updateSearch();
|
||||
|
||||
}
|
||||
@@ -382,6 +400,8 @@ RED.sidebar.info.outliner = (function() {
|
||||
existingObject.element.find(".red-ui-info-outline-item-label").text(label);
|
||||
existingObject.element.toggleClass("red-ui-info-outline-item-disabled", !!n.disabled)
|
||||
existingObject.treeList.container.toggleClass("red-ui-info-outline-item-disabled", !!n.disabled)
|
||||
existingObject.element.toggleClass("red-ui-info-outline-item-locked", !!n.locked)
|
||||
existingObject.treeList.container.toggleClass("red-ui-info-outline-item-locked", !!n.locked)
|
||||
updateSearch();
|
||||
}
|
||||
function onFlowsReorder(order) {
|
||||
|
||||
@@ -25,6 +25,7 @@ RED.sidebar.info = (function() {
|
||||
var propertiesPanelHeaderLabel;
|
||||
var propertiesPanelHeaderReveal;
|
||||
var propertiesPanelHeaderHelp;
|
||||
var propertiesPanelHeaderCopyLink;
|
||||
|
||||
var selectedObject;
|
||||
|
||||
@@ -67,10 +68,20 @@ RED.sidebar.info = (function() {
|
||||
|
||||
propertiesPanelHeaderIcon = $("<span>").appendTo(propertiesPanelHeader);
|
||||
propertiesPanelHeaderLabel = $("<span>").appendTo(propertiesPanelHeader);
|
||||
propertiesPanelHeaderHelp = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-book"></button>').css({
|
||||
|
||||
propertiesPanelHeaderCopyLink = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-link"></button>').css({
|
||||
position: 'absolute',
|
||||
top: '12px',
|
||||
right: '32px'
|
||||
}).on("click", function(evt) {
|
||||
RED.actions.invoke('core:copy-item-url',selectedObject)
|
||||
}).appendTo(propertiesPanelHeader);
|
||||
RED.popover.tooltip(propertiesPanelHeaderCopyLink,RED._("sidebar.info.copyItemUrl"));
|
||||
|
||||
propertiesPanelHeaderHelp = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-book"></button>').css({
|
||||
position: 'absolute',
|
||||
top: '12px',
|
||||
right: '56px'
|
||||
}).on("click", function(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
@@ -80,8 +91,7 @@ RED.sidebar.info = (function() {
|
||||
}).appendTo(propertiesPanelHeader);
|
||||
RED.popover.tooltip(propertiesPanelHeaderHelp,RED._("sidebar.help.showHelp"));
|
||||
|
||||
|
||||
propertiesPanelHeaderReveal = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-search"></button>').css({
|
||||
propertiesPanelHeaderReveal = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-search"></button>').css({
|
||||
position: 'absolute',
|
||||
top: '12px',
|
||||
right: '8px'
|
||||
@@ -185,6 +195,7 @@ RED.sidebar.info = (function() {
|
||||
propertiesPanelHeaderLabel.text("");
|
||||
propertiesPanelHeaderReveal.hide();
|
||||
propertiesPanelHeaderHelp.hide();
|
||||
propertiesPanelHeaderCopyLink.hide();
|
||||
return;
|
||||
} else if (Array.isArray(node)) {
|
||||
// Multiple things selected
|
||||
@@ -196,6 +207,7 @@ RED.sidebar.info = (function() {
|
||||
propertiesPanelHeaderLabel.text("Selection");
|
||||
propertiesPanelHeaderReveal.hide();
|
||||
propertiesPanelHeaderHelp.hide();
|
||||
propertiesPanelHeaderCopyLink.hide();
|
||||
selectedObject = null;
|
||||
|
||||
var types = {
|
||||
@@ -277,9 +289,11 @@ RED.sidebar.info = (function() {
|
||||
if (node.type === "tab" || node.type === "subflow") {
|
||||
// If nothing is selected, but we're on a flow or subflow tab.
|
||||
propertiesPanelHeaderHelp.hide();
|
||||
propertiesPanelHeaderCopyLink.show();
|
||||
|
||||
} else if (node.type === "group") {
|
||||
propertiesPanelHeaderHelp.hide();
|
||||
propertiesPanelHeaderCopyLink.show();
|
||||
|
||||
propRow = $('<tr class="red-ui-help-info-row"><td> </td><td></td></tr>').appendTo(tableBody);
|
||||
|
||||
@@ -304,8 +318,10 @@ RED.sidebar.info = (function() {
|
||||
}
|
||||
} else if (node.type === 'junction') {
|
||||
propertiesPanelHeaderHelp.hide();
|
||||
propertiesPanelHeaderCopyLink.hide();
|
||||
} else {
|
||||
propertiesPanelHeaderHelp.show();
|
||||
propertiesPanelHeaderCopyLink.show();
|
||||
|
||||
if (!subflowRegex) {
|
||||
propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("sidebar.info.type")+'</td><td></td></tr>').appendTo(tableBody);
|
||||
@@ -447,7 +463,8 @@ RED.sidebar.info = (function() {
|
||||
el = el.next();
|
||||
}
|
||||
$(this).toggleClass('expanded',!isExpanded);
|
||||
})
|
||||
});
|
||||
mermaid.init();
|
||||
}
|
||||
|
||||
var tips = (function() {
|
||||
|
||||
@@ -435,10 +435,15 @@ RED.tourGuide = (function() {
|
||||
|
||||
function listTour() {
|
||||
return [
|
||||
{
|
||||
id: "3_1",
|
||||
label: "3.1",
|
||||
path: "./tours/welcome.js"
|
||||
},
|
||||
{
|
||||
id: "3_0",
|
||||
label: "3.0",
|
||||
path: "./tours/welcome.js"
|
||||
path: "./tours/3.0/welcome.js"
|
||||
},
|
||||
{
|
||||
id: "2_2",
|
||||
|
||||
@@ -362,6 +362,7 @@ RED.typeSearch = (function() {
|
||||
items.push({type:t,def: def, label:getTypeLabel(t,def)});
|
||||
}
|
||||
});
|
||||
items.push({ type: 'junction', def: { inputs:1, outputs: 1, label: 'junction', type: 'junction'}, label: 'junction' })
|
||||
items.sort(sortTypeLabels);
|
||||
|
||||
var commonCount = 0;
|
||||
|
||||
@@ -96,6 +96,37 @@ RED.utils = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
var mermaidIsInitialized = false;
|
||||
var mermaidIsEnabled /* = undefined */;
|
||||
|
||||
renderer.code = function (code, lang) {
|
||||
if(lang === "mermaid") {
|
||||
// mermaid diagram rendering
|
||||
if (mermaidIsEnabled === undefined) {
|
||||
if (RED.settings.markdownEditor &&
|
||||
RED.settings.markdownEditor.mermaid) {
|
||||
mermaidIsEnabled = RED.settings.markdownEditor.mermaid.enabled;
|
||||
}
|
||||
else {
|
||||
mermaidIsEnabled = true;
|
||||
}
|
||||
}
|
||||
if (mermaidIsEnabled) {
|
||||
if (!mermaidIsInitialized) {
|
||||
mermaidIsInitialized = true;
|
||||
mermaid.initialize({startOnLoad:false});
|
||||
}
|
||||
return `<pre class='mermaid'>${code}</pre>`;
|
||||
}
|
||||
else {
|
||||
return `<details><summary>${RED._("markdownEditor.mermaid.summary")}</summary><pre><code>${code}</code></pre></details>`;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return "<pre><code>" +code +"</code></pre>";
|
||||
}
|
||||
};
|
||||
|
||||
window._marked.setOptions({
|
||||
renderer: renderer,
|
||||
gfm: true,
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
|
||||
RED.view.navigator = (function() {
|
||||
|
||||
var nav_scale = 25;
|
||||
var nav_width = 5000/nav_scale;
|
||||
var nav_height = 5000/nav_scale;
|
||||
var nav_scale = 50;
|
||||
var nav_width = 8000/nav_scale;
|
||||
var nav_height = 8000/nav_scale;
|
||||
|
||||
var navContainer;
|
||||
var navBox;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
**/
|
||||
|
||||
RED.view.tools = (function() {
|
||||
|
||||
'use strict';
|
||||
function selectConnected(type) {
|
||||
var selection = RED.view.selection();
|
||||
var visited = new Set();
|
||||
@@ -39,6 +39,9 @@ RED.view.tools = (function() {
|
||||
}
|
||||
|
||||
function alignToGrid() {
|
||||
if (RED.workspaces.isLocked()) {
|
||||
return
|
||||
}
|
||||
var selection = RED.view.selection();
|
||||
if (selection.nodes) {
|
||||
var changedNodes = [];
|
||||
@@ -87,6 +90,9 @@ RED.view.tools = (function() {
|
||||
}
|
||||
|
||||
function moveSelection(dx,dy) {
|
||||
if (RED.workspaces.isLocked()) {
|
||||
return
|
||||
}
|
||||
if (moving_set === null) {
|
||||
moving_set = [];
|
||||
var selection = RED.view.selection();
|
||||
@@ -153,6 +159,9 @@ RED.view.tools = (function() {
|
||||
}
|
||||
|
||||
function setSelectedNodeLabelState(labelShown) {
|
||||
if (RED.workspaces.isLocked()) {
|
||||
return
|
||||
}
|
||||
var selection = RED.view.selection();
|
||||
var historyEvents = [];
|
||||
var nodes = [];
|
||||
@@ -439,6 +448,9 @@ RED.view.tools = (function() {
|
||||
}
|
||||
|
||||
function alignSelectionToEdge(direction) {
|
||||
if (RED.workspaces.isLocked()) {
|
||||
return;
|
||||
}
|
||||
var selection = RED.view.selection();
|
||||
|
||||
if (selection.nodes && selection.nodes.length > 1) {
|
||||
@@ -539,8 +551,10 @@ RED.view.tools = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function distributeSelection(direction) {
|
||||
if (RED.workspaces.isLocked()) {
|
||||
return
|
||||
}
|
||||
var selection = RED.view.selection();
|
||||
|
||||
if (selection.nodes && selection.nodes.length > 2) {
|
||||
@@ -699,14 +713,16 @@ RED.view.tools = (function() {
|
||||
}
|
||||
|
||||
function reorderSelection(dir) {
|
||||
if (RED.workspaces.isLocked()) {
|
||||
return
|
||||
}
|
||||
var selection = RED.view.selection();
|
||||
if (selection.nodes) {
|
||||
var nodesToMove = [];
|
||||
selection.nodes.forEach(function(n) {
|
||||
if (n.type === "group") {
|
||||
nodesToMove = nodesToMove.concat(RED.group.getNodes(n, true).filter(function(n) {
|
||||
return n.type !== "group";
|
||||
}))
|
||||
nodesToMove.push(n)
|
||||
nodesToMove = nodesToMove.concat(RED.group.getNodes(n, true))
|
||||
} else if (n.type !== "subflow"){
|
||||
nodesToMove.push(n);
|
||||
}
|
||||
@@ -734,8 +750,10 @@ RED.view.tools = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function wireSeriesOfNodes() {
|
||||
if (RED.workspaces.isLocked()) {
|
||||
return
|
||||
}
|
||||
var selection = RED.view.selection();
|
||||
if (selection.nodes) {
|
||||
if (selection.nodes.length > 1) {
|
||||
@@ -776,6 +794,9 @@ RED.view.tools = (function() {
|
||||
}
|
||||
|
||||
function wireNodeToMultiple() {
|
||||
if (RED.workspaces.isLocked()) {
|
||||
return
|
||||
}
|
||||
var selection = RED.view.selection();
|
||||
if (selection.nodes) {
|
||||
if (selection.nodes.length > 1) {
|
||||
@@ -818,11 +839,72 @@ RED.view.tools = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
function wireMultipleToNode() {
|
||||
if (RED.workspaces.isLocked()) {
|
||||
return
|
||||
}
|
||||
var selection = RED.view.selection();
|
||||
if (selection.nodes) {
|
||||
if (selection.nodes.length > 1) {
|
||||
var targetNode = selection.nodes[selection.nodes.length - 1];
|
||||
if (targetNode.inputs === 0) {
|
||||
return;
|
||||
}
|
||||
var i = 0;
|
||||
var newLinks = [];
|
||||
for (i = 0; i < selection.nodes.length - 1; i++) {
|
||||
var sourceNode = selection.nodes[i];
|
||||
if (sourceNode.outputs > 0) {
|
||||
|
||||
// Wire the first output to the target that has no link to the target yet.
|
||||
// This allows for connecting all combinations of inputs/outputs.
|
||||
// The user may then delete links quickly that aren't needed.
|
||||
var sourceConnectedOutports = RED.nodes.filterLinks({
|
||||
source: sourceNode,
|
||||
target: targetNode
|
||||
});
|
||||
|
||||
// Get outport indices that have no link yet
|
||||
var sourceOutportIndices = Array.from({ length: sourceNode.outputs }, (_, i) => i);
|
||||
var sourceConnectedOutportIndices = sourceConnectedOutports.map( x => x.sourcePort );
|
||||
var sourceFreeOutportIndices = sourceOutportIndices.filter(x => !sourceConnectedOutportIndices.includes(x));
|
||||
|
||||
// Does an unconnected source port exist?
|
||||
if (sourceFreeOutportIndices.length == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Connect the first free outport to the target
|
||||
var newLink = {
|
||||
source: sourceNode,
|
||||
target: targetNode,
|
||||
sourcePort: sourceFreeOutportIndices[0]
|
||||
}
|
||||
RED.nodes.addLink(newLink);
|
||||
newLinks.push(newLink);
|
||||
}
|
||||
}
|
||||
if (newLinks.length > 0) {
|
||||
RED.history.push({
|
||||
t: 'add',
|
||||
links: newLinks,
|
||||
dirty: RED.nodes.dirty()
|
||||
})
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits selected wires and re-joins them with link-out+link-in
|
||||
* @param {Object || Object[]} wires The wire(s) to split and replace with link-out, link-in nodes.
|
||||
*/
|
||||
function splitWiresWithLinkNodes(wires) {
|
||||
if (RED.workspaces.isLocked()) {
|
||||
return
|
||||
}
|
||||
let wiresToSplit = wires || (RED.view.selection().links && RED.view.selection().links.filter(e => !e.link));
|
||||
if (!wiresToSplit) {
|
||||
return
|
||||
@@ -877,7 +959,6 @@ RED.view.tools = (function() {
|
||||
if(!nnLinkOut) {
|
||||
const nLinkOut = RED.view.createNode("link out"); //create link node
|
||||
nnLinkOut = nLinkOut.node;
|
||||
nodeSrcMap[linkOutMapId] = nnLinkOut;
|
||||
let yOffset = 0;
|
||||
if(nSrc.outputs > 1) {
|
||||
|
||||
@@ -892,7 +973,8 @@ RED.view.tools = (function() {
|
||||
updateNewNodePosXY(nSrc, nnLinkOut, false, RED.view.snapGrid, yOffset);
|
||||
}
|
||||
//add created node
|
||||
RED.nodes.add(nnLinkOut);
|
||||
nnLinkOut = RED.nodes.add(nnLinkOut);
|
||||
nodeSrcMap[linkOutMapId] = nnLinkOut;
|
||||
RED.editor.validateNode(nnLinkOut);
|
||||
history.events.push(nLinkOut.historyEvent);
|
||||
//connect node to link node
|
||||
@@ -913,10 +995,10 @@ RED.view.tools = (function() {
|
||||
if(!nnLinkIn) {
|
||||
const nLinkIn = RED.view.createNode("link in"); //create link node
|
||||
nnLinkIn = nLinkIn.node;
|
||||
nodeTrgMap[nTrg.id] = nnLinkIn;
|
||||
updateNewNodePosXY(nTrg, nnLinkIn, true, RED.view.snapGrid, 0);
|
||||
//add created node
|
||||
RED.nodes.add(nnLinkIn);
|
||||
nnLinkIn = RED.nodes.add(nnLinkIn);
|
||||
nodeTrgMap[nTrg.id] = nnLinkIn;
|
||||
RED.editor.validateNode(nnLinkIn);
|
||||
history.events.push(nLinkIn.historyEvent);
|
||||
//connect node to link node
|
||||
@@ -991,6 +1073,9 @@ RED.view.tools = (function() {
|
||||
* @param {{ renameBlank: boolean, renameClash: boolean, generateHistory: boolean }} options Possible options are `renameBlank`, `renameClash` and `generateHistory`
|
||||
*/
|
||||
function generateNodeNames(node, options) {
|
||||
if (RED.workspaces.isLocked()) {
|
||||
return
|
||||
}
|
||||
options = Object.assign({
|
||||
renameBlank: true,
|
||||
renameClash: true,
|
||||
@@ -1061,6 +1146,9 @@ RED.view.tools = (function() {
|
||||
}
|
||||
|
||||
function addJunctionsToWires(wires) {
|
||||
if (RED.workspaces.isLocked()) {
|
||||
return
|
||||
}
|
||||
let wiresToSplit = wires || (RED.view.selection().links && RED.view.selection().links.filter(e => !e.link));
|
||||
if (!wiresToSplit) {
|
||||
return
|
||||
@@ -1102,7 +1190,8 @@ RED.view.tools = (function() {
|
||||
w: 0, h: 0,
|
||||
outputs: 1,
|
||||
inputs: 1,
|
||||
dirty: true
|
||||
dirty: true,
|
||||
moved: true
|
||||
}
|
||||
links = links.filter(function(l) { return !removedLinks.has(l) })
|
||||
if (links.length === 0) {
|
||||
@@ -1131,7 +1220,7 @@ RED.view.tools = (function() {
|
||||
|
||||
var nodeGroups = new Set()
|
||||
|
||||
RED.nodes.addJunction(junction)
|
||||
junction = RED.nodes.addJunction(junction)
|
||||
addedJunctions.push(junction)
|
||||
let newLink
|
||||
if (gid === links[0].source.id+":"+links[0].sourcePort) {
|
||||
@@ -1192,6 +1281,63 @@ RED.view.tools = (function() {
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
|
||||
function copyItemUrl(node, isEdit) {
|
||||
if (!node) {
|
||||
const selection = RED.view.selection();
|
||||
if (selection.nodes && selection.nodes.length > 0) {
|
||||
node = selection.nodes[0]
|
||||
}
|
||||
}
|
||||
if (node) {
|
||||
let thingType = 'node'
|
||||
if (node.type === 'group') {
|
||||
thingType = 'group'
|
||||
} else if (node.type === 'tab' || node.type === 'subflow') {
|
||||
thingType = 'flow'
|
||||
}
|
||||
let url = `${window.location.origin}${window.location.pathname}#${thingType}/${node.id}`
|
||||
if (isEdit) {
|
||||
url += '/edit'
|
||||
}
|
||||
if (RED.clipboard.copyText(url)) {
|
||||
RED.notify(RED._("sidebar.info.copyURL2Clipboard"), { timeout: 2000 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a point is within a node
|
||||
* @param {*} node - A Node or Junction node
|
||||
* @param {[Number,Number]} mouse_position The x,y position of the mouse
|
||||
* @param {Number} [marginX=0] - A margin to add or deduct from the x position (to increase the hit area)
|
||||
* @param {Number} [marginY=0] - A margin to add or deduct from the y position (to increase the hit area)
|
||||
* @returns
|
||||
*/
|
||||
function isPointInNode (node, [x, y], marginX, marginY) {
|
||||
marginX = marginX || 0
|
||||
marginY = marginY || 0
|
||||
|
||||
let w = node.w || 10 // junctions dont have any w or h value
|
||||
let h = node.h || 10
|
||||
let x1, x2, y1, y2
|
||||
|
||||
if (node.type === "junction" || node.type === "group") {
|
||||
// x/y is the top left of the node
|
||||
x1 = node.x
|
||||
y1 = node.y
|
||||
x2 = node.x + w
|
||||
y2 = node.y + h
|
||||
} else {
|
||||
// x/y is the center of the node
|
||||
const [xMid, yMid] = [w/2, h/2]
|
||||
x1 = node.x - xMid
|
||||
y1 = node.y - yMid
|
||||
x2 = node.x + xMid
|
||||
y2 = node.y + yMid
|
||||
}
|
||||
return (x >= (x1 - marginX) && x <= (x2 + marginX) && y >= (y1 - marginY) && y <= (y2 + marginY))
|
||||
}
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
RED.actions.add("core:show-selected-node-labels", function() { setSelectedNodeLabelState(true); })
|
||||
@@ -1252,12 +1398,16 @@ RED.view.tools = (function() {
|
||||
|
||||
RED.actions.add("core:wire-series-of-nodes", function() { wireSeriesOfNodes() })
|
||||
RED.actions.add("core:wire-node-to-multiple", function() { wireNodeToMultiple() })
|
||||
RED.actions.add("core:wire-multiple-to-node", function() { wireMultipleToNode() })
|
||||
|
||||
RED.actions.add("core:split-wire-with-link-nodes", function () { splitWiresWithLinkNodes() });
|
||||
RED.actions.add("core:split-wires-with-junctions", function () { addJunctionsToWires() });
|
||||
|
||||
RED.actions.add("core:generate-node-names", generateNodeNames )
|
||||
|
||||
RED.actions.add("core:copy-item-url", function (node) { copyItemUrl(node) })
|
||||
RED.actions.add("core:copy-item-edit-url", function (node) { copyItemUrl(node, true) })
|
||||
|
||||
// RED.actions.add("core:add-node", function() { addNode() })
|
||||
},
|
||||
/**
|
||||
@@ -1270,7 +1420,8 @@ RED.view.tools = (function() {
|
||||
* @param {Number} dy
|
||||
*/
|
||||
moveSelection: moveSelection,
|
||||
calculateGridSnapOffsets: calculateGridSnapOffsets
|
||||
calculateGridSnapOffsets: calculateGridSnapOffsets,
|
||||
isPointInNode: isPointInNode
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -58,6 +58,9 @@ RED.workspaces = (function() {
|
||||
if (!ws.closeable) {
|
||||
ws.hideable = true;
|
||||
}
|
||||
if (!ws.hasOwnProperty('locked')) {
|
||||
ws.locked = false
|
||||
}
|
||||
workspace_tabs.addTab(ws,targetIndex);
|
||||
|
||||
var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
|
||||
@@ -75,11 +78,15 @@ RED.workspaces = (function() {
|
||||
type: "tab",
|
||||
id: tabId,
|
||||
disabled: false,
|
||||
locked: false,
|
||||
info: "",
|
||||
label: RED._('workspace.defaultName',{number:workspaceIndex}),
|
||||
env: [],
|
||||
hideable: true
|
||||
hideable: true,
|
||||
};
|
||||
if (!skipHistoryEntry) {
|
||||
ws.added = true
|
||||
}
|
||||
RED.nodes.addWorkspace(ws,targetIndex);
|
||||
workspace_tabs.addTab(ws,targetIndex);
|
||||
|
||||
@@ -89,8 +96,7 @@ RED.workspaces = (function() {
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
}
|
||||
$("#red-ui-tab-"+(ws.id.replace(".","-"))).attr("flowname",ws.label)
|
||||
|
||||
$("#red-ui-tab-"+(ws.id.replace(".","-"))).attr("flowname",ws.label).toggleClass('red-ui-workspace-changed',!!(ws.contentsChanged || ws.changed || ws.added));
|
||||
RED.view.focus();
|
||||
return ws;
|
||||
}
|
||||
@@ -99,6 +105,9 @@ RED.workspaces = (function() {
|
||||
if (workspaceTabCount === 1) {
|
||||
return;
|
||||
}
|
||||
if (ws.locked) {
|
||||
return
|
||||
}
|
||||
var workspaceOrder = RED.nodes.getWorkspaceOrder();
|
||||
ws._index = workspaceOrder.indexOf(ws.id);
|
||||
removeWorkspace(ws);
|
||||
@@ -119,13 +128,206 @@ RED.workspaces = (function() {
|
||||
RED.editor.editSubflow(subflow);
|
||||
}
|
||||
} else {
|
||||
RED.editor.editFlow(workspace);
|
||||
if (!workspace.locked) {
|
||||
RED.editor.editFlow(workspace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var workspace_tabs;
|
||||
var workspaceTabCount = 0;
|
||||
|
||||
function getMenuItems(isMenuButton, tab) {
|
||||
let hiddenFlows = new Set()
|
||||
for (let i = 0; i < hideStack.length; i++) {
|
||||
let ids = hideStack[i]
|
||||
if (!Array.isArray(ids)) {
|
||||
ids = [ids]
|
||||
}
|
||||
ids.forEach(id => {
|
||||
if (RED.nodes.workspace(id)) {
|
||||
hiddenFlows.add(id)
|
||||
}
|
||||
})
|
||||
}
|
||||
const hiddenflowCount = hiddenFlows.size;
|
||||
let activeWorkspace = tab || RED.nodes.workspace(RED.workspaces.active()) || RED.nodes.subflow(RED.workspaces.active())
|
||||
let isFlowDisabled = activeWorkspace ? activeWorkspace.disabled : false
|
||||
const currentTabs = workspace_tabs.listTabs();
|
||||
let flowCount = 0;
|
||||
currentTabs.forEach(tab => {
|
||||
if (RED.nodes.workspace(tab)) {
|
||||
flowCount++;
|
||||
}
|
||||
});
|
||||
|
||||
let isCurrentLocked = RED.workspaces.isLocked()
|
||||
if (tab) {
|
||||
isCurrentLocked = tab.locked
|
||||
}
|
||||
|
||||
var menuItems = []
|
||||
if (isMenuButton) {
|
||||
menuItems.push({
|
||||
id:"red-ui-tabs-menu-option-search-flows",
|
||||
label: RED._("workspace.listFlows"),
|
||||
onselect: "core:list-flows"
|
||||
},
|
||||
{
|
||||
id:"red-ui-tabs-menu-option-search-subflows",
|
||||
label: RED._("workspace.listSubflows"),
|
||||
onselect: "core:list-subflows"
|
||||
},
|
||||
null)
|
||||
}
|
||||
menuItems.push(
|
||||
{
|
||||
id:"red-ui-tabs-menu-option-add-flow",
|
||||
label: RED._("workspace.addFlow"),
|
||||
onselect: "core:add-flow"
|
||||
}
|
||||
)
|
||||
if (isMenuButton || !!tab) {
|
||||
menuItems.push(
|
||||
{
|
||||
id:"red-ui-tabs-menu-option-add-flow-right",
|
||||
label: RED._("workspace.addFlowToRight"),
|
||||
shortcut: RED.keyboard.getShortcut("core:add-flow-to-right"),
|
||||
onselect: function() {
|
||||
RED.actions.invoke("core:add-flow-to-right", tab)
|
||||
}
|
||||
},
|
||||
null
|
||||
)
|
||||
if (activeWorkspace && activeWorkspace.type === 'tab') {
|
||||
menuItems.push(
|
||||
isFlowDisabled ? {
|
||||
label: RED._("workspace.enableFlow"),
|
||||
shortcut: RED.keyboard.getShortcut("core:enable-flow"),
|
||||
onselect: function() {
|
||||
RED.actions.invoke("core:enable-flow", tab?tab.id:undefined)
|
||||
},
|
||||
disabled: isCurrentLocked
|
||||
} : {
|
||||
label: RED._("workspace.disableFlow"),
|
||||
shortcut: RED.keyboard.getShortcut("core:disable-flow"),
|
||||
onselect: function() {
|
||||
RED.actions.invoke("core:disable-flow", tab?tab.id:undefined)
|
||||
},
|
||||
disabled: isCurrentLocked
|
||||
},
|
||||
isCurrentLocked? {
|
||||
label: RED._("workspace.unlockFlow"),
|
||||
shortcut: RED.keyboard.getShortcut("core:unlock-flow"),
|
||||
onselect: function() {
|
||||
RED.actions.invoke('core:unlock-flow', tab?tab.id:undefined)
|
||||
}
|
||||
} : {
|
||||
label: RED._("workspace.lockFlow"),
|
||||
shortcut: RED.keyboard.getShortcut("core:lock-flow"),
|
||||
onselect: function() {
|
||||
RED.actions.invoke('core:lock-flow', tab?tab.id:undefined)
|
||||
}
|
||||
},
|
||||
null
|
||||
)
|
||||
}
|
||||
const activeIndex = currentTabs.findIndex(id => (activeWorkspace && (id === activeWorkspace.id)));
|
||||
menuItems.push(
|
||||
{
|
||||
label: RED._("workspace.moveToStart"),
|
||||
shortcut: RED.keyboard.getShortcut("core:move-flow-to-start"),
|
||||
onselect: function() {
|
||||
RED.actions.invoke("core:move-flow-to-start", tab?tab.id:undefined)
|
||||
},
|
||||
disabled: activeIndex === 0
|
||||
},
|
||||
{
|
||||
label: RED._("workspace.moveToEnd"),
|
||||
shortcut: RED.keyboard.getShortcut("core:move-flow-to-end"),
|
||||
onselect: function() {
|
||||
RED.actions.invoke("core:move-flow-to-end", tab?tab.id:undefined)
|
||||
},
|
||||
disabled: activeIndex === currentTabs.length - 1
|
||||
}
|
||||
)
|
||||
}
|
||||
menuItems.push(null)
|
||||
if (isMenuButton || !!tab) {
|
||||
menuItems.push(
|
||||
{
|
||||
id:"red-ui-tabs-menu-option-add-hide-flows",
|
||||
label: RED._("workspace.hideFlow"),
|
||||
shortcut: RED.keyboard.getShortcut("core:hide-flow"),
|
||||
onselect: function() {
|
||||
RED.actions.invoke("core:hide-flow", tab)
|
||||
}
|
||||
},
|
||||
{
|
||||
id:"red-ui-tabs-menu-option-add-hide-other-flows",
|
||||
label: RED._("workspace.hideOtherFlows"),
|
||||
shortcut: RED.keyboard.getShortcut("core:hide-other-flows"),
|
||||
onselect: function() {
|
||||
RED.actions.invoke("core:hide-other-flows", tab)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
menuItems.push(
|
||||
{
|
||||
id:"red-ui-tabs-menu-option-add-hide-all-flows",
|
||||
label: RED._("workspace.hideAllFlows"),
|
||||
onselect: "core:hide-all-flows",
|
||||
disabled: (hiddenflowCount === flowCount)
|
||||
},
|
||||
{
|
||||
id:"red-ui-tabs-menu-option-add-show-all-flows",
|
||||
disabled: hiddenflowCount === 0,
|
||||
label: RED._("workspace.showAllFlows", { count: hiddenflowCount }),
|
||||
onselect: "core:show-all-flows"
|
||||
},
|
||||
{
|
||||
id:"red-ui-tabs-menu-option-add-show-last-flow",
|
||||
disabled: hideStack.length === 0,
|
||||
label: RED._("workspace.showLastHiddenFlow"),
|
||||
onselect: "core:show-last-hidden-flow"
|
||||
}
|
||||
)
|
||||
if (tab) {
|
||||
menuItems.push(
|
||||
null,
|
||||
{
|
||||
label: RED._("common.label.delete"),
|
||||
onselect: function() {
|
||||
if (tab.type === 'tab') {
|
||||
RED.workspaces.delete(tab)
|
||||
} else if (tab.type === 'subflow') {
|
||||
RED.subflow.delete(tab.id)
|
||||
}
|
||||
},
|
||||
disabled: isCurrentLocked || (workspaceTabCount === 1)
|
||||
},
|
||||
{
|
||||
label: RED._("menu.label.export"),
|
||||
shortcut: RED.keyboard.getShortcut("core:show-export-dialog"),
|
||||
onselect: function() {
|
||||
RED.workspaces.show(tab.id)
|
||||
RED.actions.invoke('core:show-export-dialog', null, 'flow')
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
// if (isMenuButton && hiddenflowCount > 0) {
|
||||
// menuItems.unshift({
|
||||
// label: RED._("workspace.hiddenFlows",{count: hiddenflowCount}),
|
||||
// onselect: "core:list-hidden-flows"
|
||||
// })
|
||||
// }
|
||||
return menuItems;
|
||||
}
|
||||
function createWorkspaceTabs() {
|
||||
workspace_tabs = RED.tabs.create({
|
||||
id: "red-ui-workspace-tabs",
|
||||
@@ -137,8 +339,9 @@ RED.workspaces = (function() {
|
||||
$("#red-ui-workspace-chart").show();
|
||||
activeWorkspace = tab.id;
|
||||
window.location.hash = 'flow/'+tab.id;
|
||||
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!tab.disabled);
|
||||
} else {
|
||||
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled", !!tab.disabled);
|
||||
$("#red-ui-workspace").toggleClass("red-ui-workspace-locked", !!tab.locked);
|
||||
} else {
|
||||
$("#red-ui-workspace-chart").hide();
|
||||
activeWorkspace = 0;
|
||||
window.location.hash = '';
|
||||
@@ -169,6 +372,18 @@ RED.workspaces = (function() {
|
||||
if (tab.disabled) {
|
||||
$("#red-ui-tab-"+(tab.id.replace(".","-"))).addClass('red-ui-workspace-disabled');
|
||||
}
|
||||
$('<span class="red-ui-workspace-locked-icon"><i class="fa fa-lock"></i> </span>').prependTo("#red-ui-tab-"+(tab.id.replace(".","-"))+" .red-ui-tab-label");
|
||||
if (tab.locked) {
|
||||
$("#red-ui-tab-"+(tab.id.replace(".","-"))).addClass('red-ui-workspace-locked');
|
||||
}
|
||||
|
||||
const changeBadgeContainer = $('<svg class="red-ui-flow-tab-changed red-ui-flow-node-changed" width="10" height="10" viewBox="-1 -1 12 12"></svg>').appendTo("#red-ui-tab-"+(tab.id.replace(".","-")))
|
||||
const changeBadge = document.createElementNS("http://www.w3.org/2000/svg","circle");
|
||||
changeBadge.setAttribute("cx",5);
|
||||
changeBadge.setAttribute("cy",5);
|
||||
changeBadge.setAttribute("r",5);
|
||||
changeBadgeContainer.append(changeBadge)
|
||||
|
||||
RED.menu.setDisabled("menu-item-workspace-delete",activeWorkspace === 0 || workspaceTabCount <= 1);
|
||||
if (workspaceTabCount === 1) {
|
||||
showWorkspace();
|
||||
@@ -189,13 +404,19 @@ RED.workspaces = (function() {
|
||||
RED.history.push({
|
||||
t:'reorder',
|
||||
workspaces: {
|
||||
from:oldOrder,
|
||||
to:newOrder
|
||||
from: oldOrder,
|
||||
to: newOrder
|
||||
},
|
||||
dirty:RED.nodes.dirty()
|
||||
});
|
||||
RED.nodes.dirty(true);
|
||||
setWorkspaceOrder(newOrder);
|
||||
// Only mark flows dirty if flow-order has changed (excluding subflows)
|
||||
const filteredOldOrder = oldOrder.filter(id => !!RED.nodes.workspace(id))
|
||||
const filteredNewOrder = newOrder.filter(id => !!RED.nodes.workspace(id))
|
||||
|
||||
if (JSON.stringify(filteredOldOrder) !== JSON.stringify(filteredNewOrder)) {
|
||||
RED.nodes.dirty(true);
|
||||
setWorkspaceOrder(newOrder);
|
||||
}
|
||||
},
|
||||
onselect: function(selectedTabs) {
|
||||
RED.view.select(false)
|
||||
@@ -214,12 +435,12 @@ 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})
|
||||
if (tab.type === "tab") {
|
||||
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);
|
||||
@@ -234,77 +455,8 @@ RED.workspaces = (function() {
|
||||
scrollable: true,
|
||||
addButton: "core:add-flow",
|
||||
addButtonCaption: RED._("workspace.addFlow"),
|
||||
menu: function() {
|
||||
var menuItems = [
|
||||
{
|
||||
id:"red-ui-tabs-menu-option-search-flows",
|
||||
label: RED._("workspace.listFlows"),
|
||||
onselect: "core:list-flows"
|
||||
},
|
||||
{
|
||||
id:"red-ui-tabs-menu-option-search-subflows",
|
||||
label: RED._("workspace.listSubflows"),
|
||||
onselect: "core:list-subflows"
|
||||
},
|
||||
null,
|
||||
{
|
||||
id:"red-ui-tabs-menu-option-add-flow",
|
||||
label: RED._("workspace.addFlow"),
|
||||
onselect: "core:add-flow"
|
||||
},
|
||||
{
|
||||
id:"red-ui-tabs-menu-option-add-flow-right",
|
||||
label: RED._("workspace.addFlowToRight"),
|
||||
onselect: "core:add-flow-to-right"
|
||||
},
|
||||
null,
|
||||
{
|
||||
id:"red-ui-tabs-menu-option-add-hide-flows",
|
||||
label: RED._("workspace.hideFlow"),
|
||||
onselect: "core:hide-flow"
|
||||
},
|
||||
{
|
||||
id:"red-ui-tabs-menu-option-add-hide-other-flows",
|
||||
label: RED._("workspace.hideOtherFlows"),
|
||||
onselect: "core:hide-other-flows"
|
||||
},
|
||||
{
|
||||
id:"red-ui-tabs-menu-option-add-show-all-flows",
|
||||
label: RED._("workspace.showAllFlows"),
|
||||
onselect: "core:show-all-flows"
|
||||
},
|
||||
{
|
||||
id:"red-ui-tabs-menu-option-add-hide-all-flows",
|
||||
label: RED._("workspace.hideAllFlows"),
|
||||
onselect: "core:hide-all-flows"
|
||||
},
|
||||
{
|
||||
id:"red-ui-tabs-menu-option-add-show-last-flow",
|
||||
label: RED._("workspace.showLastHiddenFlow"),
|
||||
onselect: "core:show-last-hidden-flow"
|
||||
}
|
||||
]
|
||||
let hiddenFlows = new Set()
|
||||
for (let i = 0; i < hideStack.length; i++) {
|
||||
let ids = hideStack[i]
|
||||
if (!Array.isArray(ids)) {
|
||||
ids = [ids]
|
||||
}
|
||||
ids.forEach(id => {
|
||||
if (RED.nodes.workspace(id)) {
|
||||
hiddenFlows.add(id)
|
||||
}
|
||||
})
|
||||
}
|
||||
const flowCount = hiddenFlows.size;
|
||||
if (flowCount > 0) {
|
||||
menuItems.unshift({
|
||||
label: RED._("workspace.hiddenFlows",{count: flowCount}),
|
||||
onselect: "core:list-hidden-flows"
|
||||
})
|
||||
}
|
||||
return menuItems;
|
||||
}
|
||||
menu: function() { return getMenuItems(true) },
|
||||
contextmenu: function(tab) { return getMenuItems(false, tab) }
|
||||
});
|
||||
workspaceTabCount = 0;
|
||||
}
|
||||
@@ -355,16 +507,33 @@ RED.workspaces = (function() {
|
||||
});
|
||||
|
||||
RED.actions.add("core:add-flow",function(opts) { addWorkspace(undefined,undefined,opts?opts.index:undefined)});
|
||||
RED.actions.add("core:add-flow-to-right",function(opts) { addWorkspace(undefined,undefined,workspace_tabs.activeIndex()+1)});
|
||||
RED.actions.add("core:add-flow-to-right",function(workspace) {
|
||||
let index
|
||||
if (workspace) {
|
||||
index = workspace_tabs.getTabIndex(workspace.id)+1
|
||||
} else {
|
||||
index = workspace_tabs.activeIndex()+1
|
||||
}
|
||||
addWorkspace(undefined,undefined,index)
|
||||
});
|
||||
RED.actions.add("core:edit-flow",editWorkspace);
|
||||
RED.actions.add("core:remove-flow",removeWorkspace);
|
||||
RED.actions.add("core:enable-flow",enableWorkspace);
|
||||
RED.actions.add("core:disable-flow",disableWorkspace);
|
||||
RED.actions.add("core:lock-flow",lockWorkspace);
|
||||
RED.actions.add("core:unlock-flow",unlockWorkspace);
|
||||
RED.actions.add("core:move-flow-to-start", function(id) { moveWorkspace(id, 'start') });
|
||||
RED.actions.add("core:move-flow-to-end", function(id) { moveWorkspace(id, 'end') });
|
||||
|
||||
RED.actions.add("core:hide-flow", function() {
|
||||
var selection = workspace_tabs.selection();
|
||||
if (selection.length === 0) {
|
||||
selection = [{id:activeWorkspace}]
|
||||
RED.actions.add("core:hide-flow", function(workspace) {
|
||||
let selection
|
||||
if (workspace) {
|
||||
selection = [workspace]
|
||||
} else {
|
||||
selection = workspace_tabs.selection();
|
||||
if (selection.length === 0) {
|
||||
selection = [{id:activeWorkspace}]
|
||||
}
|
||||
}
|
||||
var hiddenTabs = [];
|
||||
selection.forEach(function(ws) {
|
||||
@@ -378,10 +547,15 @@ RED.workspaces = (function() {
|
||||
workspace_tabs.clearSelection();
|
||||
})
|
||||
|
||||
RED.actions.add("core:hide-other-flows", function() {
|
||||
var selection = workspace_tabs.selection();
|
||||
if (selection.length === 0) {
|
||||
selection = [{id:activeWorkspace}]
|
||||
RED.actions.add("core:hide-other-flows", function(workspace) {
|
||||
let selection
|
||||
if (workspace) {
|
||||
selection = [workspace]
|
||||
} else {
|
||||
selection = workspace_tabs.selection();
|
||||
if (selection.length === 0) {
|
||||
selection = [{id:activeWorkspace}]
|
||||
}
|
||||
}
|
||||
var selected = new Set(selection.map(function(ws) { return ws.id }))
|
||||
|
||||
@@ -471,6 +645,11 @@ RED.workspaces = (function() {
|
||||
RED.workspaces.show(viewStack[++viewStackPos],true);
|
||||
}
|
||||
})
|
||||
|
||||
RED.events.on("flows:change", (ws) => {
|
||||
$("#red-ui-tab-"+(ws.id.replace(".","-"))).toggleClass('red-ui-workspace-changed',!!(ws.contentsChanged || ws.changed || ws.added));
|
||||
})
|
||||
|
||||
hideWorkspace();
|
||||
}
|
||||
|
||||
@@ -486,7 +665,7 @@ RED.workspaces = (function() {
|
||||
}
|
||||
function setWorkspaceState(id,disabled) {
|
||||
var workspace = RED.nodes.workspace(id||activeWorkspace);
|
||||
if (!workspace) {
|
||||
if (!workspace || workspace.locked) {
|
||||
return;
|
||||
}
|
||||
if (workspace.disabled !== disabled) {
|
||||
@@ -521,11 +700,47 @@ RED.workspaces = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
function lockWorkspace(id) {
|
||||
setWorkspaceLockState(id,true);
|
||||
}
|
||||
function unlockWorkspace(id) {
|
||||
setWorkspaceLockState(id,false);
|
||||
}
|
||||
function setWorkspaceLockState(id,locked) {
|
||||
var workspace = RED.nodes.workspace(id||activeWorkspace);
|
||||
if (!workspace) {
|
||||
return;
|
||||
}
|
||||
if (workspace.locked !== locked) {
|
||||
var changes = { locked: workspace.locked };
|
||||
workspace.locked = locked;
|
||||
$("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-locked',!!workspace.locked);
|
||||
if (!id || (id === activeWorkspace)) {
|
||||
$("#red-ui-workspace").toggleClass("red-ui-workspace-locked",!!workspace.locked);
|
||||
}
|
||||
var historyEvent = {
|
||||
t: "edit",
|
||||
changes:changes,
|
||||
node: workspace,
|
||||
dirty: RED.nodes.dirty()
|
||||
}
|
||||
workspace.changed = true;
|
||||
RED.history.push(historyEvent);
|
||||
RED.events.emit("flows:change",workspace);
|
||||
RED.nodes.dirty(true);
|
||||
RED.nodes.filterNodes({z:workspace.id}).forEach(n => n.dirty = true)
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
}
|
||||
|
||||
function removeWorkspace(ws) {
|
||||
if (!ws) {
|
||||
deleteWorkspace(RED.nodes.workspace(activeWorkspace));
|
||||
ws = RED.nodes.workspace(activeWorkspace)
|
||||
if (ws && !ws.locked) {
|
||||
deleteWorkspace(RED.nodes.workspace(activeWorkspace));
|
||||
}
|
||||
} else {
|
||||
if (ws.locked) { return }
|
||||
if (workspace_tabs.contains(ws.id)) {
|
||||
workspace_tabs.removeTab(ws.id);
|
||||
}
|
||||
@@ -535,16 +750,46 @@ RED.workspaces = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
function moveWorkspace(id, direction) {
|
||||
const workspace = RED.nodes.workspace(id||activeWorkspace) || RED.nodes.subflow(id||activeWorkspace);
|
||||
if (!workspace) {
|
||||
return;
|
||||
}
|
||||
const currentOrder = workspace_tabs.listTabs()
|
||||
const oldOrder = [...currentOrder]
|
||||
const currentIndex = currentOrder.findIndex(id => id === workspace.id)
|
||||
currentOrder.splice(currentIndex, 1)
|
||||
if (direction === 'start') {
|
||||
currentOrder.unshift(workspace.id)
|
||||
} else if (direction === 'end') {
|
||||
currentOrder.push(workspace.id)
|
||||
}
|
||||
const newOrder = setWorkspaceOrder(currentOrder)
|
||||
if (JSON.stringify(newOrder) !== JSON.stringify(oldOrder)) {
|
||||
RED.history.push({
|
||||
t:'reorder',
|
||||
workspaces: {
|
||||
from:oldOrder,
|
||||
to:newOrder
|
||||
},
|
||||
dirty:RED.nodes.dirty()
|
||||
});
|
||||
const filteredOldOrder = oldOrder.filter(id => !!RED.nodes.workspace(id))
|
||||
const filteredNewOrder = newOrder.filter(id => !!RED.nodes.workspace(id))
|
||||
if (JSON.stringify(filteredOldOrder) !== JSON.stringify(filteredNewOrder)) {
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
function setWorkspaceOrder(order) {
|
||||
var newOrder = order.filter(function(id) {
|
||||
return RED.nodes.workspace(id) !== undefined;
|
||||
})
|
||||
var newOrder = order.filter(id => !!RED.nodes.workspace(id))
|
||||
var currentOrder = RED.nodes.getWorkspaceOrder();
|
||||
if (JSON.stringify(newOrder) !== JSON.stringify(currentOrder)) {
|
||||
RED.nodes.setWorkspaceOrder(newOrder);
|
||||
RED.events.emit("flows:reorder",newOrder);
|
||||
}
|
||||
workspace_tabs.order(order);
|
||||
return newOrder
|
||||
}
|
||||
|
||||
function flashTab(tabId) {
|
||||
@@ -590,6 +835,11 @@ RED.workspaces = (function() {
|
||||
active: function() {
|
||||
return activeWorkspace
|
||||
},
|
||||
isLocked: function(id) {
|
||||
id = id || activeWorkspace
|
||||
var ws = RED.nodes.workspace(id) || RED.nodes.subflow(id)
|
||||
return ws && ws.locked
|
||||
},
|
||||
selection: function() {
|
||||
return workspace_tabs.selection();
|
||||
},
|
||||
@@ -646,6 +896,8 @@ RED.workspaces = (function() {
|
||||
workspace_tabs.resize();
|
||||
},
|
||||
enable: enableWorkspace,
|
||||
disable: disableWorkspace
|
||||
disable: disableWorkspace,
|
||||
lock: lockWorkspace,
|
||||
unlock: unlockWorkspace
|
||||
}
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user