diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json
index d1375f5b2..5367d85d7 100644
--- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json
+++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json
@@ -620,6 +620,8 @@
"pluginCount_plural": "__count__ plugins",
"moduleCount": "__count__ module available",
"moduleCount_plural": "__count__ modules available",
+ "updateCount": "__count__ update available",
+ "updateCount_plural": "__count__ updates available",
"inuse": "in use",
"enableall": "enable all",
"disableall": "disable all",
diff --git a/packages/node_modules/@node-red/editor-client/src/js/keymap.json b/packages/node_modules/@node-red/editor-client/src/js/keymap.json
index 1bedab6e8..4cf28d227 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/keymap.json
+++ b/packages/node_modules/@node-red/editor-client/src/js/keymap.json
@@ -92,7 +92,6 @@
"ctrl-+": "core:zoom-in",
"ctrl--": "core:zoom-out",
"ctrl-0": "core:zoom-reset"
-
},
"red-ui-editor-stack": {
"ctrl-enter": "core:confirm-edit-tray",
diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js
index 2a7b440f2..f569b6d5a 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js
@@ -3013,7 +3013,12 @@ RED.nodes = (function() {
});
RED.events.on('deploy', function () {
allNodes.clearState()
- })
+ });
+ RED.actions.add("core:trigger-selected-nodes-action", function () {
+ const selectedNodes = RED.view.selection().nodes || [];
+ // Triggers the button action of the selected nodes
+ selectedNodes.forEach((node) => RED.view.clickNodeButton(node));
+ });
},
registry:registry,
setNodeList: registry.setNodeList,
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js
index 42de7978a..8e6491bed 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js
@@ -15,26 +15,35 @@
**/
RED.palette.editor = (function() {
- var disabled = false;
let catalogues = []
const loadedCatalogs = []
- var editorTabs;
- let filterInput;
- let searchInput;
- let nodeList;
- let packageList;
- let fullList = []
+
+ // Loaded modules
let loadedList = [];
- let filteredList = [];
let loadedIndex = {};
- var typesInUse = {};
- var nodeEntries = {};
- var eventTimers = {};
- var activeFilter = "";
+ // Module list
+ let fullList = [];
+ let filteredList = [];
- var semverre = /^(\d+)(\.(\d+))?(\.(\d+))?(-([0-9A-Za-z-]+))?(\.([0-9A-Za-z-.]+))?$/;
- var NUMBERS_ONLY = /^\d+$/;
+ // Modules installed
+ let nodeEntries = {};
+
+ // EditableList
+ let nodeList;
+ let packageList;
+
+ // Nodes tab - filter
+ let activeFilter = "";
+ // Nodes tab - search input
+ let filterInput;
+ // Install tab - search input
+ let searchInput;
+
+ const typesInUse = {};
+
+ const semverre = /^(\d+)(\.(\d+))?(\.(\d+))?(-([0-9A-Za-z-]+))?(\.([0-9A-Za-z-.]+))?$/;
+ const NUMBERS_ONLY = /^\d+$/;
function SemVerPart(part) {
this.number = 0;
@@ -115,8 +124,65 @@ RED.palette.editor = (function() {
});
})
}
- function installNodeModule(id,version,url,callback) {
- var requestBody = {
+
+ const moduleQueue = [];
+ const processQueue = function () {
+ if (moduleQueue.length === 0) {
+ return;
+ }
+
+ const { type, body, callback } = moduleQueue[0];
+ if (type === "install") {
+ RED.eventLog.startEvent(RED._("palette.editor.confirm.button.install") + ` : ${body.module} ${body.version}`);
+ $.ajax({
+ url: "nodes",
+ type: "POST",
+ data: JSON.stringify(body),
+ contentType: "application/json; charset=utf-8"
+ }).done(function(_data, _textStatus, _xhr) {
+ callback();
+ }).fail(function(xhr, textStatus, err) {
+ callback(xhr, textStatus, err);
+ }).always(function () {
+ // Remove the task from the list
+ moduleQueue.shift();
+ // Process the next task
+ processQueue();
+ });
+ } else if (type === "remove") {
+ RED.eventLog.startEvent(RED._("palette.editor.confirm.button.remove") + ` : ${body.id}`);
+ $.ajax({
+ url: "nodes/" + body.id,
+ type: "DELETE"
+ }).done(function(_data, _textStatus, _xhr) {
+ callback();
+ }).fail(function(xhr, _textStatus, _err) {
+ callback(xhr);
+ }).always(function () {
+ // Remove the task from the list
+ moduleQueue.shift();
+ // Process the next task
+ processQueue();
+ });
+ }
+ };
+
+ /**
+ * Adds a module to the processing queue to install or remove it
+ * @param {string} type the type of request to apply to the module
+ * @param {Object} body an object with module info
+ * @param {(xhr?: JQuery.jqXHR, textStatus?: JQuery.Ajax.ErrorTextStatus, err?: string) => void} callback a callback function called when the request is done
+ */
+ function addModuleToQueue(type, body, callback) {
+ moduleQueue.push({ type, body, callback });
+
+ if (moduleQueue.length === 1) {
+ processQueue();
+ }
+ }
+
+ function installNodeModule(id, version, url, callback) {
+ const requestBody = {
module: id
};
if (version) {
@@ -125,26 +191,10 @@ RED.palette.editor = (function() {
if (url) {
requestBody.url = url;
}
- $.ajax({
- url:"nodes",
- type: "POST",
- data: JSON.stringify(requestBody),
- contentType: "application/json; charset=utf-8"
- }).done(function(data,textStatus,xhr) {
- callback();
- }).fail(function(xhr,textStatus,err) {
- callback(xhr,textStatus,err);
- });
+ addModuleToQueue("install", requestBody, callback);
}
- function removeNodeModule(id,callback) {
- $.ajax({
- url:"nodes/"+id,
- type: "DELETE"
- }).done(function(data,textStatus,xhr) {
- callback();
- }).fail(function(xhr,textStatus,err) {
- callback(xhr);
- })
+ function removeNodeModule(id, callback) {
+ addModuleToQueue("remove", { id: id }, callback);
}
function refreshNodeModuleList() {
@@ -155,6 +205,7 @@ RED.palette.editor = (function() {
}
}
+ const eventTimers = {};
function refreshNodeModule(module) {
if (!eventTimers.hasOwnProperty(module)) {
eventTimers[module] = setTimeout(function() {
@@ -231,7 +282,6 @@ RED.palette.editor = (function() {
}
}
-
function _refreshNodeModule(module) {
if (!nodeEntries.hasOwnProperty(module)) {
nodeEntries[module] = {info:RED.nodes.registry.getModule(module)};
@@ -381,58 +431,60 @@ RED.palette.editor = (function() {
}
}
+ let activeSort = sortModulesRelevance;
- var catalogueCount;
- var catalogueLoadStatus = [];
- var catalogueLoadStart;
- var catalogueLoadErrors = false;
-
- var activeSort = sortModulesRelevance;
-
- function handleCatalogResponse(err,catalog,index,v) {
- const url = catalog.url
- catalogueLoadStatus.push(err||v);
- if (!err) {
- if (v.modules) {
- v.modules = v.modules.filter(function(m) {
- if (RED.utils.checkModuleAllowed(m.id,m.version,installAllowList,installDenyList)) {
- loadedIndex[m.id] = m;
- m.index = [m.id];
- if (m.keywords) {
- m.index = m.index.concat(m.keywords);
- }
- if (m.types) {
- m.index = m.index.concat(m.types);
- }
- if (m.updated_at) {
- m.timestamp = new Date(m.updated_at).getTime();
- } else {
- m.timestamp = 0;
- }
- m.index = m.index.join(",").toLowerCase();
- m.catalog = catalog;
- m.catalogIndex = index;
- return true;
+ function refreshCatalogues (done) {
+ const catalogueCount = catalogues.length;
+ loadedList = []
+ loadedIndex = {}
+ loadedCatalogs.length = 0
+ let handled = 0
+ 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({ url: url, name: v.name},index,v);
+ }).fail(function(_jqxhr, _textStatus, error) {
+ console.warn("Error loading catalog",url,":",error);
+ }).always(function() {
+ handled++;
+ if (handled === catalogueCount) {
+ //sort loadedCatalogs by e.index ascending
+ loadedCatalogs.sort((a, b) => a.index - b.index)
+ refreshUpdateStatus();
+ if (done) {
+ done()
}
- return false;
- })
- loadedList = loadedList.concat(v.modules);
- }
- } else {
- catalogueLoadErrors = true;
+ }
+ })
}
- if (catalogueCount > 1) {
- $(".red-ui-palette-module-shade-status").html(RED._('palette.editor.loading')+"
"+catalogueLoadStatus.length+"/"+catalogueCount);
- }
- if (catalogueLoadStatus.length === catalogueCount) {
- if (catalogueLoadErrors) {
- RED.notify(RED._('palette.editor.errors.catalogLoadFailed',{url: url}),"error",false,8000);
- }
- var delta = 250-(Date.now() - catalogueLoadStart);
- setTimeout(function() {
- $("#red-ui-palette-module-install-shade").hide();
- },Math.max(delta,0));
+ }
+ function handleCatalogResponse(catalog,index,v) {
+ if (v.modules) {
+ v.modules = v.modules.filter(function(m) {
+ if (RED.utils.checkModuleAllowed(m.id,m.version,installAllowList,installDenyList)) {
+ loadedIndex[m.id] = m;
+ m.index = [m.id];
+ if (m.keywords) {
+ m.index = m.index.concat(m.keywords);
+ }
+ if (m.types) {
+ m.index = m.index.concat(m.types);
+ }
+ if (m.updated_at) {
+ m.timestamp = new Date(m.updated_at).getTime();
+ } else {
+ m.timestamp = 0;
+ }
+ m.index = m.index.join(",").toLowerCase();
+ m.catalog = catalog;
+ m.catalogIndex = index;
+ return true;
+ }
+ return false;
+ })
+ loadedList = loadedList.concat(v.modules);
}
}
@@ -444,36 +496,20 @@ RED.palette.editor = (function() {
packageList.editableList('empty');
$(".red-ui-palette-module-shade-status").text(RED._('palette.editor.loading'));
-
- catalogueLoadStatus = [];
- catalogueLoadErrors = false;
- catalogueCount = catalogues.length;
- if (catalogues.length > 1) {
- $(".red-ui-palette-module-shade-status").html(RED._('palette.editor.loading')+"
0/"+catalogues.length);
- }
$("#red-ui-palette-module-install-shade").show();
- catalogueLoadStart = Date.now();
- var handled = 0;
- 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",url,":",error);
- handleCatalogResponse(jqxhr,url,index);
- }).always(function() {
- handled++;
- if (handled === catalogueCount) {
- //sort loadedCatalogs by e.index ascending
- loadedCatalogs.sort((a, b) => a.index - b.index)
- updateCatalogFilter(loadedCatalogs)
- }
- })
- }
+ const catalogueLoadStart = Date.now()
+ refreshCatalogues(function () {
+ refreshNodeModuleList();
+ updateCatalogFilter(loadedCatalogs)
+ const delta = 250-(Date.now() - catalogueLoadStart);
+ setTimeout(function() {
+ $("#red-ui-palette-module-install-shade").hide();
+ },Math.max(delta,0));
+ })
} else {
+ refreshNodeModuleList();
+ updateCatalogFilter(loadedCatalogs)
+
const updateAllButton = $(nodesTabFooter).find("#red-ui-palette-button-updateAll");
const progressRow = $(nodesTabFooter).find(".red-ui-palette-footer-progress");
@@ -504,7 +540,6 @@ RED.palette.editor = (function() {
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)
@@ -595,36 +630,43 @@ RED.palette.editor = (function() {
return -1 * (A.info.timestamp-B.info.timestamp);
}
- var installAllowList = ['*'];
- var installDenyList = [];
- var updateAllowed = true;
- var updateAllowList = ['*'];
- var updateDenyList = [];
+ let installAllowList = ['*'];
+ let installDenyList = [];
+ let updateAllowed = true;
+ let updateAllowList = ['*'];
+ let updateDenyList = [];
+
+ let editorTabs;
+ let settingsPane;
+ let nodesTabFooter;
function init() {
- catalogues = RED.settings.theme('palette.catalogues')||['https://catalogue.nodered.org/catalogue.json']
if (RED.settings.get('externalModules.palette.allowInstall', true) === false) {
return;
}
- var settingsAllowList = RED.settings.get("externalModules.palette.allowList")
- var settingsDenyList = RED.settings.get("externalModules.palette.denyList")
+
+ const settingsAllowList = RED.settings.get("externalModules.palette.allowList")
+ const settingsDenyList = RED.settings.get("externalModules.palette.denyList")
+ const settingsUpdateAllowList = RED.settings.get("externalModules.palette.allowUpdateList")
+ const settingsUpdateDenyList = RED.settings.get("externalModules.palette.denyUpdateList")
+
if (settingsAllowList || settingsDenyList) {
installAllowList = settingsAllowList;
- installDenyList = settingsDenyList
+ installDenyList = settingsDenyList;
}
- installAllowList = RED.utils.parseModuleList(installAllowList);
- installDenyList = RED.utils.parseModuleList(installDenyList);
-
- var settingsUpdateAllowList = RED.settings.get("externalModules.palette.allowUpdateList")
- var settingsUpdateDenyList = RED.settings.get("externalModules.palette.denyUpdateList")
if (settingsUpdateAllowList || settingsUpdateDenyList) {
updateAllowList = settingsUpdateAllowList;
updateDenyList = settingsUpdateDenyList;
}
+
+ installAllowList = RED.utils.parseModuleList(installAllowList);
+ installDenyList = RED.utils.parseModuleList(installDenyList);
+
+ updateAllowed = RED.settings.get("externalModules.palette.allowUpdate", true);
updateAllowList = RED.utils.parseModuleList(updateAllowList);
updateDenyList = RED.utils.parseModuleList(updateDenyList);
- updateAllowed = RED.settings.get("externalModules.palette.allowUpdate",true);
+ catalogues = RED.settings.theme('palette.catalogues') || ['https://catalogue.nodered.org/catalogue.json']
createSettingsPane();
@@ -643,7 +685,10 @@ RED.palette.editor = (function() {
}
})
- getSettingsPane();
+ // Add the update status to the status bar
+ addUpdateInfoToStatusBar();
+ // Load catalogues
+ refreshCatalogues()
RED.actions.add("core:manage-palette",function() {
RED.userSettings.show('palette');
@@ -651,6 +696,7 @@ RED.palette.editor = (function() {
RED.events.on('registry:module-updated', function(ns) {
refreshNodeModule(ns.module);
+ refreshUpdateStatus();
});
RED.events.on('registry:node-set-enabled', function(ns) {
refreshNodeModule(ns.module);
@@ -662,12 +708,14 @@ RED.palette.editor = (function() {
if (!/^subflow:/.test(nodeType)) {
var ns = RED.nodes.registry.getNodeSetForType(nodeType);
refreshNodeModule(ns.module);
+ refreshUpdateStatus();
}
});
RED.events.on('registry:node-type-removed', function(nodeType) {
if (!/^subflow:/.test(nodeType)) {
var ns = RED.nodes.registry.getNodeSetForType(nodeType);
refreshNodeModule(ns.module);
+ refreshUpdateStatus();
}
});
RED.events.on('registry:node-set-added', function(ns) {
@@ -736,6 +784,7 @@ RED.palette.editor = (function() {
_refreshNodeModule(module);
}
+ refreshUpdateStatus();
for (var i=0;i