mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Doc the (new) functions and add the onNodeTypeAdded
function
This commit is contained in:
parent
5d78c546bc
commit
6a2a763288
@ -564,6 +564,11 @@ RED.nodes = (function() {
|
|||||||
return api;
|
return api;
|
||||||
})()
|
})()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a random ID consisting of 8 bytes.
|
||||||
|
*
|
||||||
|
* @returns {string} The generated ID.
|
||||||
|
*/
|
||||||
function generateId() {
|
function generateId() {
|
||||||
var bytes = [];
|
var bytes = [];
|
||||||
for (var i=0;i<8;i++) {
|
for (var i=0;i<8;i++) {
|
||||||
@ -1693,7 +1698,6 @@ RED.nodes = (function() {
|
|||||||
var newConfigNodes = {};
|
var newConfigNodes = {};
|
||||||
var removedNodes = [];
|
var removedNodes = [];
|
||||||
// Figure out what we're being asked to replace - subflows/configNodes
|
// Figure out what we're being asked to replace - subflows/configNodes
|
||||||
// TODO: config nodes
|
|
||||||
newNodes.forEach(function(n) {
|
newNodes.forEach(function(n) {
|
||||||
if (n.type === "subflow") {
|
if (n.type === "subflow") {
|
||||||
newSubflows[n.id] = n;
|
newSubflows[n.id] = n;
|
||||||
@ -1795,10 +1799,11 @@ RED.nodes = (function() {
|
|||||||
* Analyzes the array of nodes passed as an argument to find unknown node types.
|
* Analyzes the array of nodes passed as an argument to find unknown node types.
|
||||||
*
|
*
|
||||||
* Options:
|
* Options:
|
||||||
* - `emitNotification` (boolean) - Emit an notification with unknown types.
|
* - `emitNotification` - Emit an notification with unknown types. Default false.
|
||||||
*
|
*
|
||||||
|
* @remarks Throws an error to be handled.
|
||||||
* @param {Array<object>} nodes An array of nodes to analyse
|
* @param {Array<object>} nodes An array of nodes to analyse
|
||||||
* @param {object} options An options object
|
* @param {{ emitNotification?: boolean; }} options An options object
|
||||||
* @returns {Array<string>} An array with unknown types
|
* @returns {Array<string>} An array with unknown types
|
||||||
*/
|
*/
|
||||||
function identifyUnknowType(nodes, options = {}) {
|
function identifyUnknowType(nodes, options = {}) {
|
||||||
@ -1828,6 +1833,13 @@ RED.nodes = (function() {
|
|||||||
return unknownTypes;
|
return unknownTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Warns the user that the import contains existing nodes that the user need to resolve.
|
||||||
|
*
|
||||||
|
* @remarks Throws an error to be handled.
|
||||||
|
* @param {Array<object>} existingNodes An array containing conflicting nodes.
|
||||||
|
* @param {Array<object>} importedNodes An array containing all imported nodes.
|
||||||
|
*/
|
||||||
function emitExistingNodesNotification(existingNodes, importedNodes) {
|
function emitExistingNodesNotification(existingNodes, importedNodes) {
|
||||||
const errorMessage = RED._("clipboard.importDuplicate", { count: existingNodes.length });
|
const errorMessage = RED._("clipboard.importDuplicate", { count: existingNodes.length });
|
||||||
const maxItemCount = 5; // Max 5 items in the list
|
const maxItemCount = 5; // Max 5 items in the list
|
||||||
@ -1852,6 +1864,15 @@ RED.nodes = (function() {
|
|||||||
throw existingNodesError;
|
throw existingNodesError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a copy of the Config Node received as a parameter.
|
||||||
|
*
|
||||||
|
* @remarks Don't change the ID.
|
||||||
|
* @param {object} configNode The Config Node to copy.
|
||||||
|
* @param {object} def The Config Node definition.
|
||||||
|
* @param {object} options Same options as import
|
||||||
|
* @returns The new Config Node copied.
|
||||||
|
*/
|
||||||
function copyConfigNode(configNode, def, options = {}) {
|
function copyConfigNode(configNode, def, options = {}) {
|
||||||
const newNode = {
|
const newNode = {
|
||||||
id: configNode.id,
|
id: configNode.id,
|
||||||
@ -1899,6 +1920,15 @@ RED.nodes = (function() {
|
|||||||
return newNode;
|
return newNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a copy of the Node received as a parameter.
|
||||||
|
*
|
||||||
|
* @remarks Don't change the ID.
|
||||||
|
* @param {object} node The Node to copy.
|
||||||
|
* @param {object} def The Node definition.
|
||||||
|
* @param {object} options Same options as import
|
||||||
|
* @returns The new Node copied.
|
||||||
|
*/
|
||||||
function copyNode(node, def, options = {}) {
|
function copyNode(node, def, options = {}) {
|
||||||
const newNode = {
|
const newNode = {
|
||||||
id: node.id,
|
id: node.id,
|
||||||
@ -2012,18 +2042,30 @@ RED.nodes = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Handles the import of nodes - these nodes can be copied, replaced or just imported.
|
||||||
|
*
|
||||||
* Options:
|
* Options:
|
||||||
* - generateIds - whether to replace all node ids
|
* - `addFlow` - whether to import nodes to a new tab. Default false.
|
||||||
* - addFlow - whether to import nodes to a new tab
|
* - `generateIds` - whether to replace all node ids. Default false.
|
||||||
* - markChanged - whether to set changed=true on all newly imported objects
|
* - `markChanged` - whether to set `changed` = true on all newly imported nodes.
|
||||||
* - reimport - if node has a .z property, dont overwrite it
|
* - `reimport` - if node has a `z` property, dont overwrite it
|
||||||
* Only applicible when `generateIds` is false
|
* Only applicible when `generateIds` is false. Default false.
|
||||||
* - importMap - how to resolve any conflicts.
|
* - `importMap` - how to resolve any conflicts.
|
||||||
* - id:import - import as-is
|
* - nodeId: `import` - import as-is
|
||||||
* - id:copy - import with new id
|
* - nodeId: `copy` - import with new id
|
||||||
* - id:replace - import over the top of existing
|
* - nodeId: `replace` - repace the existing
|
||||||
|
*
|
||||||
|
* @remarks Only Subflow definition (tab) and Config Node are replaceable!
|
||||||
|
*
|
||||||
|
* @typedef {string} NodeId
|
||||||
|
* @typedef {Record<NodeId, "copy" | "import" | "replace" | "skip">} ImportMap
|
||||||
|
*
|
||||||
|
* @param {string | object | Array<object>} originalNodes A node or array of nodes to import
|
||||||
|
* @param {{ addFlow?: boolean; generateIds?: boolean; importMap?: ImportMap; markChanged?: boolean;
|
||||||
|
* reimport?: boolean; }} options Options to involve on import.
|
||||||
|
* @returns An object containing all the elements created.
|
||||||
*/
|
*/
|
||||||
function importNodes(originalNodes, options) {
|
function importNodes(originalNodes, options = {}) {
|
||||||
const defaultOptions = { generateIds: false, addFlow: false, markChanged: false, reimport: false, importMap: {} };
|
const defaultOptions = { generateIds: false, addFlow: false, markChanged: false, reimport: false, importMap: {} };
|
||||||
options = Object.assign({}, defaultOptions, options);
|
options = Object.assign({}, defaultOptions, options);
|
||||||
|
|
||||||
@ -2075,6 +2117,11 @@ RED.nodes = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure ignored nodes are not imported
|
||||||
|
if (options.importMap[id] === "skip") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -2653,30 +2700,40 @@ RED.nodes = (function() {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update any config nodes referenced by the provided node to ensure their 'users' list is correct
|
/**
|
||||||
function updateConfigNodeUsers(n, options) {
|
* Update any config nodes referenced by the provided node to ensure
|
||||||
|
* their 'users' list is correct.
|
||||||
|
*
|
||||||
|
* Options:
|
||||||
|
* - `action` - Add or remove the node from the Config Node users list. Default `add`.
|
||||||
|
* - `emitEvent` - Emit the `nodes:changes` event. Default true.
|
||||||
|
*
|
||||||
|
* @param {object} node The node in which to check if it contains references
|
||||||
|
* @param {{ action?: "add" | "remove"; emitEvent?: boolean; }} options Options to apply.
|
||||||
|
*/
|
||||||
|
function updateConfigNodeUsers(node, options = {}) {
|
||||||
const defaultOptions = { action: "add", emitEvent: true };
|
const defaultOptions = { action: "add", emitEvent: true };
|
||||||
options = Object.assign({}, defaultOptions, options);
|
options = Object.assign({}, defaultOptions, options);
|
||||||
|
|
||||||
for (var d in n._def.defaults) {
|
for (var d in node._def.defaults) {
|
||||||
if (n._def.defaults.hasOwnProperty(d)) {
|
if (node._def.defaults.hasOwnProperty(d)) {
|
||||||
var property = n._def.defaults[d];
|
var property = node._def.defaults[d];
|
||||||
if (property.type) {
|
if (property.type) {
|
||||||
var type = registry.getNodeType(property.type);
|
var type = registry.getNodeType(property.type);
|
||||||
if (type && type.category == "config") {
|
if (type && type.category == "config") {
|
||||||
var configNode = configNodes[n[d]];
|
var configNode = configNodes[node[d]];
|
||||||
if (configNode) {
|
if (configNode) {
|
||||||
if (options.action === "add") {
|
if (options.action === "add") {
|
||||||
if (configNode.users.indexOf(n) === -1) {
|
if (configNode.users.indexOf(node) === -1) {
|
||||||
configNode.users.push(n);
|
configNode.users.push(node);
|
||||||
if (options.emitEvent) {
|
if (options.emitEvent) {
|
||||||
RED.events.emit('nodes:change', configNode);
|
RED.events.emit('nodes:change', configNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (options.action === "remove") {
|
} else if (options.action === "remove") {
|
||||||
if (configNode.users.indexOf(n) !== -1) {
|
if (configNode.users.indexOf(node) !== -1) {
|
||||||
const users = configNode.users;
|
const users = configNode.users;
|
||||||
users.splice(users.indexOf(n), 1);
|
users.splice(users.indexOf(node), 1);
|
||||||
if (options.emitEvent) {
|
if (options.emitEvent) {
|
||||||
RED.events.emit('nodes:change', configNode);
|
RED.events.emit('nodes:change', configNode);
|
||||||
}
|
}
|
||||||
@ -2952,88 +3009,110 @@ RED.nodes = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
/**
|
||||||
init: function() {
|
* Finds unknown nodes of the same type and replaces them to update their
|
||||||
RED.events.on("registry:node-type-added",function(type) {
|
* definition.
|
||||||
var def = registry.getNodeType(type);
|
*
|
||||||
var replaced = false;
|
* An imported node that has no definition is marked as unknown.
|
||||||
var replaceNodes = {};
|
* When adding a new node type, this function searches for existing nodes
|
||||||
RED.nodes.eachNode(function(n) {
|
* of that type to update their definition.
|
||||||
if (n.type === "unknown" && n.name === type) {
|
*
|
||||||
replaceNodes[n.id] = n;
|
* @param {string} type The type of the Node added
|
||||||
}
|
*/
|
||||||
});
|
function onNodeTypeAdded(type) {
|
||||||
RED.nodes.eachConfig(function(n) {
|
const nodesToReplace = [];
|
||||||
if (n.type === "unknown" && n.name === type) {
|
RED.nodes.eachConfig(function (node) {
|
||||||
replaceNodes[n.id] = n;
|
if (node.type === "unknown" && node.name === type) {
|
||||||
|
nodesToReplace.push(node);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const nodeGroupMap = {}
|
RED.nodes.eachNode(function (node) {
|
||||||
var replaceNodeIds = Object.keys(replaceNodes);
|
if (node.type === "unknown" && node.name === type) {
|
||||||
if (replaceNodeIds.length > 0) {
|
nodesToReplace.push(node);
|
||||||
var reimportList = [];
|
|
||||||
replaceNodeIds.forEach(function(id) {
|
|
||||||
var n = replaceNodes[id];
|
|
||||||
if (configNodes.hasOwnProperty(n.id)) {
|
|
||||||
delete configNodes[n.id];
|
|
||||||
} else {
|
|
||||||
allNodes.removeNode(n);
|
|
||||||
}
|
}
|
||||||
if (n.g) {
|
|
||||||
// reimporting a node *without* including its group object
|
|
||||||
// will cause the g property to be cleared. Cache it
|
|
||||||
// here so we can restore it
|
|
||||||
nodeGroupMap[n.id] = n.g
|
|
||||||
}
|
|
||||||
reimportList.push(convertNode(n));
|
|
||||||
RED.events.emit('nodes:remove',n);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Skip if there is nothing to replace
|
||||||
|
if (!nodesToReplace.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodeGroupMap = {};
|
||||||
|
const removedNodes = [];
|
||||||
|
nodesToReplace.forEach(function (node) {
|
||||||
|
// Create a snapshot of the Node
|
||||||
|
removedNodes.push(convertNode(node));
|
||||||
|
|
||||||
|
// Remove the Node
|
||||||
|
removeNode(node);
|
||||||
|
|
||||||
|
// Reimporting a node *without* including its group object will cause
|
||||||
|
// the g property to be cleared. Cache it here so we can restore it.
|
||||||
|
if (node.g) {
|
||||||
|
nodeGroupMap[node.id] = node.g
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const removedLinks = [];
|
||||||
|
const nodeMap = nodesToReplace.reduce(function (map, node) {
|
||||||
|
map[node.id] = node;
|
||||||
|
return map;
|
||||||
|
}, {});
|
||||||
// Remove any links between nodes that are going to be reimported.
|
// Remove any links between nodes that are going to be reimported.
|
||||||
// This prevents a duplicate link from being added.
|
// This prevents a duplicate link from being added.
|
||||||
var removeLinks = [];
|
RED.nodes.eachLink(function (link) {
|
||||||
RED.nodes.eachLink(function(l) {
|
if (nodeMap.hasOwnProperty(link.source.id) && nodeMap.hasOwnProperty(link.target.id)) {
|
||||||
if (replaceNodes.hasOwnProperty(l.source.id) && replaceNodes.hasOwnProperty(l.target.id)) {
|
removedLinks.push(link);
|
||||||
removeLinks.push(l);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
removeLinks.forEach(removeLink);
|
|
||||||
|
removedLinks.forEach(removeLink);
|
||||||
|
|
||||||
// Force the redraw to be synchronous so the view updates
|
// Force the redraw to be synchronous so the view updates
|
||||||
// *now* and removes the unknown node
|
// *now* and removes the unknown node
|
||||||
RED.view.redraw(true, true);
|
RED.view.redraw(true, true);
|
||||||
var result = importNodes(reimportList,{generateIds:false, reimport: true});
|
|
||||||
var newNodeMap = {};
|
// Re-import removed nodes - now nodes have their definition
|
||||||
result.nodes.forEach(function(n) {
|
const result = importNodes(removedNodes, { generateIds: false, reimport: true });
|
||||||
newNodeMap[n.id] = n;
|
|
||||||
if (nodeGroupMap[n.id]) {
|
const newNodeMap = {};
|
||||||
|
// Rattach the nodes to their group
|
||||||
|
result?.nodes.forEach(function (node) {
|
||||||
|
newNodeMap[node.id] = node;
|
||||||
|
if (nodeGroupMap[node.id]) {
|
||||||
// This node is in a group - need to substitute the
|
// This node is in a group - need to substitute the
|
||||||
// node reference inside the group
|
// node reference inside the group
|
||||||
n.g = nodeGroupMap[n.id]
|
node.g = nodeGroupMap[node.id];
|
||||||
const group = RED.nodes.group(n.g)
|
const group = RED.nodes.group(node.g);
|
||||||
if (group) {
|
if (group) {
|
||||||
var index = group.nodes.findIndex(gn => gn.id === n.id)
|
const index = group.nodes.findIndex((g) => g.id === node.id);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
group.nodes[index] = n
|
group.nodes[index] = node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
RED.nodes.eachLink(function(l) {
|
|
||||||
if (newNodeMap.hasOwnProperty(l.source.id)) {
|
// Relink nodes
|
||||||
l.source = newNodeMap[l.source.id];
|
RED.nodes.eachLink(function (link) {
|
||||||
|
if (newNodeMap.hasOwnProperty(link.source.id)) {
|
||||||
|
link.source = newNodeMap[link.source.id];
|
||||||
}
|
}
|
||||||
if (newNodeMap.hasOwnProperty(l.target.id)) {
|
if (newNodeMap.hasOwnProperty(link.target.id)) {
|
||||||
l.target = newNodeMap[l.target.id];
|
link.target = newNodeMap[link.target.id];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
RED.view.redraw(true);
|
RED.view.redraw(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
init: function () {
|
||||||
|
RED.events.on("registry:node-type-added", onNodeTypeAdded);
|
||||||
|
RED.events.on("deploy", function () {
|
||||||
|
allNodes.clearState();
|
||||||
});
|
});
|
||||||
RED.events.on('deploy', function () {
|
|
||||||
allNodes.clearState()
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
registry:registry,
|
registry:registry,
|
||||||
setNodeList: registry.setNodeList,
|
setNodeList: registry.setNodeList,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user