diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js b/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js
index bb82ecea3..eb3b7b6de 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js
@@ -26,7 +26,8 @@ RED.clipboard = (function() {
var currentPopoverError;
var activeTab;
var libraryBrowser;
- var examplesBrowser;
+
+ var activeLibraries = {};
var pendingImportConfig;
@@ -93,7 +94,7 @@ RED.clipboard = (function() {
$( this ).dialog( "close" );
} else {
var flowToExport = $("#red-ui-clipboard-dialog-export-text").val();
- var selectedPath = libraryBrowser.getSelected();
+ var selectedPath = activeLibraries[activeTab].getSelected();
if (!selectedPath.children) {
selectedPath = selectedPath.parent;
}
@@ -159,12 +160,7 @@ RED.clipboard = (function() {
if (activeTab === "red-ui-clipboard-dialog-import-tab-clipboard") {
importNodes($("#red-ui-clipboard-dialog-import-text").val(),addNewFlow);
} else {
- var selectedPath;
- if (activeTab === "red-ui-clipboard-dialog-import-tab-library") {
- selectedPath = libraryBrowser.getSelected();
- } else {
- selectedPath = examplesBrowser.getSelected();
- }
+ var selectedPath = activeLibraries[activeTab].getSelected();
if (selectedPath.path) {
$.get('library/'+selectedPath.library+'/'+selectedPath.type+'/'+selectedPath.path, function(data) {
importNodes(data,addNewFlow);
@@ -254,11 +250,8 @@ RED.clipboard = (function() {
''+
''+
''+
- '
'+
@@ -414,7 +405,7 @@ RED.clipboard = (function() {
}
},100);
} else {
- var file = libraryBrowser.getSelected();
+ var file = activeLibraries[activeTab].getSelected();
if (file && file.label && !file.children) {
$("#red-ui-clipboard-dialog-ok").button("enable");
} else {
@@ -446,7 +437,7 @@ RED.clipboard = (function() {
if (tab.id === "red-ui-clipboard-dialog-import-tab-clipboard") {
$("#red-ui-clipboard-dialog-import-text").trigger("focus");
} else {
- libraryBrowser.focus();
+ activeLibraries[tab.id].focus();
}
validateImport();
}
@@ -455,54 +446,43 @@ RED.clipboard = (function() {
id: "red-ui-clipboard-dialog-import-tab-clipboard",
label: RED._("clipboard.clipboard")
});
- tabs.addTab({
- id: "red-ui-clipboard-dialog-import-tab-library",
- label: RED._("library.library")
- });
- tabs.addTab({
- id: "red-ui-clipboard-dialog-import-tab-examples",
- label: RED._("library.types.examples")
- });
+
+ var libraries = RED.settings.libraries || [];
+ libraries.forEach(function(lib) {
+ var tabId = "red-ui-clipboard-dialog-import-tab-library-"+lib.id
+ tabs.addTab({
+ id: tabId,
+ label: RED._(lib.label||lib.id)
+ })
+
+ var content = $('
')
+ .attr("id",tabId)
+ .hide()
+ .appendTo("#red-ui-clipboard-dialog-import-tabs-content");
+
+ var browser = RED.library.createBrowser({
+ container: content,
+ onselect: function(file) {
+ if (file && file.label && !file.children) {
+ $("#red-ui-clipboard-dialog-ok").button("enable");
+ } else {
+ $("#red-ui-clipboard-dialog-ok").button("disable");
+ }
+ },
+ onconfirm: function(item) {
+ if (item && item.label && !item.children) {
+ $("#red-ui-clipboard-dialog-ok").trigger("click");
+ }
+ }
+ })
+ loadFlowLibrary(browser,lib);
+ activeLibraries[tabId] = browser;
+ })
$("#red-ui-clipboard-dialog-tab-library-name").on("keyup", validateExportFilename);
$("#red-ui-clipboard-dialog-tab-library-name").on('paste',function() { setTimeout(validateExportFilename,10)});
$("#red-ui-clipboard-dialog-export").button("enable");
- libraryBrowser = RED.library.createBrowser({
- container: $("#red-ui-clipboard-dialog-import-tab-library"),
- onselect: function(file) {
- if (file && file.label && !file.children) {
- $("#red-ui-clipboard-dialog-ok").button("enable");
- } else {
- $("#red-ui-clipboard-dialog-ok").button("disable");
- }
- },
- onconfirm: function(item) {
- if (item && item.label && !item.children) {
- $("#red-ui-clipboard-dialog-ok").trigger("click");
- }
- }
- })
- loadFlowLibrary(libraryBrowser,"local",RED._("library.types.local"));
-
- examplesBrowser = RED.library.createBrowser({
- container: $("#red-ui-clipboard-dialog-import-tab-examples"),
- onselect: function(file) {
- if (file && file.label && !file.children) {
- $("#red-ui-clipboard-dialog-ok").button("enable");
- } else {
- $("#red-ui-clipboard-dialog-ok").button("disable");
- }
- },
- onconfirm: function(item) {
- if (item && item.label && !item.children) {
- $("#red-ui-clipboard-dialog-ok").trigger("click");
- }
- }
- })
- loadFlowLibrary(examplesBrowser,"_examples_",RED._("library.types.examples"));
-
-
dialogContainer.i18n();
$("#red-ui-clipboard-dialog-ok").show();
@@ -582,10 +562,12 @@ RED.clipboard = (function() {
if (tab.id === "red-ui-clipboard-dialog-export-tab-clipboard") {
$("#red-ui-clipboard-dialog-export").button("option","label", RED._("clipboard.export.copy"))
$("#red-ui-clipboard-dialog-download").show();
+ $("#red-ui-clipboard-dialog-export-tab-library-filename").hide();
} else {
$("#red-ui-clipboard-dialog-export").button("option","label", RED._("clipboard.export.export"))
$("#red-ui-clipboard-dialog-download").hide();
- libraryBrowser.focus();
+ $("#red-ui-clipboard-dialog-export-tab-library-filename").show();
+ activeLibraries[activeTab].focus();
}
}
@@ -594,26 +576,45 @@ RED.clipboard = (function() {
id: "red-ui-clipboard-dialog-export-tab-clipboard",
label: RED._("clipboard.clipboard")
});
- tabs.addTab({
- id: "red-ui-clipboard-dialog-export-tab-library",
- label: RED._("library.library")
- });
+
+
+ var libraries = RED.settings.libraries || [];
+
+ libraries.forEach(function(lib) {
+ if (lib.readOnly) {
+ return
+ }
+ var tabId = "red-ui-clipboard-dialog-export-tab-library-"+lib.id
+ tabs.addTab({
+ id: tabId,
+ label: RED._(lib.label||lib.id)
+ })
+
+ var content = $('
')
+ .attr("id",tabId)
+ .hide()
+ .insertBefore("#red-ui-clipboard-dialog-export-tab-library-filename");
+
+ var browser = RED.library.createBrowser({
+ container: content,
+ folderTools: true,
+ onselect: function(file) {
+ if (file && file.label && !file.children) {
+ $("#red-ui-clipboard-dialog-tab-library-name").val(file.label);
+ }
+ },
+ })
+ loadFlowLibrary(browser,lib);
+ activeLibraries[tabId] = browser;
+ })
+
+
+
$("#red-ui-clipboard-dialog-tab-library-name").on("keyup", validateExportFilename);
$("#red-ui-clipboard-dialog-tab-library-name").on('paste',function() { setTimeout(validateExportFilename,10)});
$("#red-ui-clipboard-dialog-export").button("enable");
- libraryBrowser = RED.library.createBrowser({
- container: $("#red-ui-clipboard-dialog-export-tab-library-browser"),
- folderTools: true,
- onselect: function(file) {
- if (file && file.label && !file.children) {
- $("#red-ui-clipboard-dialog-tab-library-name").val(file.label);
- }
- }
- })
- loadFlowLibrary(libraryBrowser,"local",RED._("library.types.local"));
-
var clipboardTabs = RED.tabs.create({
id: "red-ui-clipboard-dialog-export-tab-clipboard-tabs",
onchange: function(tab) {
@@ -852,35 +853,28 @@ RED.clipboard = (function() {
$("#red-ui-clipboard-dialog-export-tab-clipboard-preview-list").treeList('data',treeData);
}
- function loadFlowLibrary(browser,library,label) {
- // if (includeExamples) {
- // listing.push({
- // library: "_examples_",
- // type: "flows",
- // icon: 'fa fa-hdd-o',
- // label: RED._("library.types.examples"),
- // path: "",
- // children: function(done,item) {
- // RED.library.loadLibraryFolder("_examples_","flows","",function(children) {
- // item.children = children;
- // done(children);
- // })
- // }
- // })
- // }
+ function loadFlowLibrary(browser,library) {
browser.data([{
- library: library,
+ library: library.id,
type: "flows",
- icon: 'fa fa-hdd-o',
- label: label,
+ icon: library.icon || 'fa fa-hdd-o',
+ label: RED._(library.label||library.id),
path: "",
expanded: true,
- children: function(done, item) {
- RED.library.loadLibraryFolder(library,"flows","",function(children) {
- item.children = children;
- done(children);
- })
- }
+ children: [{
+ library: library.id,
+ type: "flows",
+ icon: 'fa fa-cube',
+ label: "flows",
+ path: "",
+ expanded: true,
+ children: function(done, item) {
+ RED.library.loadLibraryFolder(library.id,"flows","",function(children) {
+ item.children = children;
+ done(children);
+ })
+ }
+ }]
}], true);
}
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/library.js b/packages/node_modules/@node-red/editor-client/src/js/ui/library.js
index 54d73c022..3872a38ea 100755
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/library.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/library.js
@@ -216,31 +216,7 @@ RED.library = (function() {
{ id:'node-input-'+options.type+'-menu-open-library',
label: RED._("library.openLibrary"),
onselect: function() {
- activeLibrary = options;
- loadLibraryFolder("local",options.url, "", function(items) {
- var listing = [{
- library: "local",
- type: options.url,
- icon: 'fa fa-hdd-o',
- label: RED._("library.types.local"),
- path: "",
- expanded: true,
- writable: false,
- children: [{
- library: "local",
- type: options.url,
- icon: 'fa fa-cube',
- label: options.type,
- path: "",
- expanded: true,
- children: items
- }]
- }]
- loadLibraryBrowser.data(listing);
- setTimeout(function() {
- loadLibraryBrowser.select(listing[0].children[0]);
- },200);
- });
+
libraryEditor = ace.edit('red-ui-library-dialog-load-preview-text',{
useWorker: false
});
@@ -256,6 +232,43 @@ RED.library = (function() {
libraryEditor.renderer.$cursorLayer.element.style.opacity=0;
libraryEditor.$blockScrolling = Infinity;
+ activeLibrary = options;
+ var listing = [];
+ var libraries = RED.settings.libraries || [];
+ libraries.forEach(function(lib) {
+ if (lib.types && lib.types.indexOf(options.url) === -1) {
+ return;
+ }
+ listing.push({
+ library: lib.id,
+ type: options.url,
+ icon: lib.icon || 'fa fa-hdd-o',
+ label: RED._(lib.label||lib.id),
+ path: "",
+ expanded: true,
+ writable: false,
+ children: [{
+ library: lib.id,
+ type: options.url,
+ icon: 'fa fa-cube',
+ label: options.type,
+ path: "",
+ expanded: false,
+ children: function(done, item) {
+ loadLibraryFolder(lib.id, options.url, "", function(children) {
+ item.children = children;
+ done(children);
+ })
+ }
+ }]
+ })
+ });
+ loadLibraryBrowser.data(listing);
+ setTimeout(function() {
+ loadLibraryBrowser.select(listing[0].children[0]);
+ },200);
+
+
var dialogHeight = 400;
var winHeight = $(window).height();
if (winHeight < 570) {
@@ -278,30 +291,40 @@ RED.library = (function() {
}
$("#red-ui-library-dialog-save-filename").attr("value",filename+"."+(options.ext||"txt"));
- loadLibraryFolder("local",options.url, "", function(items) {
- var listing = [{
- library: "local",
+ var listing = [];
+ var libraries = RED.settings.libraries || [];
+ libraries.forEach(function(lib) {
+ if (lib.types && lib.types.indexOf(options.url) === -1) {
+ return;
+ }
+ listing.push({
+ library: lib.id,
type: options.url,
- icon: 'fa fa-hdd-o',
- label: RED._("library.types.local"),
+ icon: lib.icon || 'fa fa-hdd-o',
+ label: RED._(lib.label||lib.id),
path: "",
expanded: true,
writable: false,
children: [{
- library: "local",
+ library: lib.id,
type: options.url,
icon: 'fa fa-cube',
label: options.type,
path: "",
- expanded: true,
- children: items
+ expanded: false,
+ children: function(done, item) {
+ loadLibraryFolder(lib.id, options.url, "", function(children) {
+ item.children = children;
+ done(children);
+ })
+ }
}]
- }]
- saveLibraryBrowser.data(listing);
- setTimeout(function() {
- saveLibraryBrowser.select(listing[0].children[0]);
- },200);
+ })
});
+ saveLibraryBrowser.data(listing);
+ setTimeout(function() {
+ saveLibraryBrowser.select(listing[0].children[0]);
+ },200);
var dialogHeight = 400;
var winHeight = $(window).height();
diff --git a/packages/node_modules/@node-red/editor-client/src/sass/library.scss b/packages/node_modules/@node-red/editor-client/src/sass/library.scss
index 89109f357..3ddf44a95 100644
--- a/packages/node_modules/@node-red/editor-client/src/sass/library.scss
+++ b/packages/node_modules/@node-red/editor-client/src/sass/library.scss
@@ -93,10 +93,9 @@
border:1px solid $primary-border-color;
}
-.red-ui-clipboard-dialog-tab-library {
- .form-row {
- margin-left: 10px;
- }
+#red-ui-clipboard-dialog-export-tab-library-filename {
+ height: auto !important;
+ margin-left: 10px;
}
#red-ui-clipboard-dialog {
@@ -110,7 +109,7 @@
#red-ui-clipboard-dialog-tab-library-name {
width: calc(100% - 120px);
}
-#red-ui-clipboard-dialog-export-tab-library-browser {
+.red-ui-clipboard-dialog-tabs-content>div.red-ui-clipboard-dialog-export-tab-library-browser {
height: calc(100% - 60px);
margin-bottom: 13px;
border-bottom: 1px solid $primary-border-color;
diff --git a/packages/node_modules/@node-red/runtime/lib/api/settings.js b/packages/node_modules/@node-red/runtime/lib/api/settings.js
index d96ec5b58..07d09e379 100644
--- a/packages/node_modules/@node-red/runtime/lib/api/settings.js
+++ b/packages/node_modules/@node-red/runtime/lib/api/settings.js
@@ -81,7 +81,7 @@ var api = module.exports = {
if (!runtime.settings.disableEditor) {
safeSettings.context = runtime.nodes.listContextStores();
-
+ safeSettings.libraries = runtime.library.getLibraries();
if (util.isArray(runtime.settings.paletteCategories)) {
safeSettings.paletteCategories = runtime.settings.paletteCategories;
}
diff --git a/packages/node_modules/@node-red/runtime/lib/index.js b/packages/node_modules/@node-red/runtime/lib/index.js
index 0be1c6511..42e053baf 100644
--- a/packages/node_modules/@node-red/runtime/lib/index.js
+++ b/packages/node_modules/@node-red/runtime/lib/index.js
@@ -74,7 +74,6 @@ function init(userSettings,httpServer,_adminApi) {
adminApi = _adminApi;
}
redNodes.init(runtime);
- library.init(runtime);
externalAPI.init(runtime);
}
@@ -104,6 +103,7 @@ function start() {
return i18n.registerMessageCatalog("runtime",path.resolve(path.join(__dirname,"..","locales")),"runtime.json")
.then(function() { return storage.init(runtime)})
.then(function() { return settings.load(storage)})
+ .then(function() { return library.init(runtime)})
.then(function() {
if (log.metric()) {
diff --git a/packages/node_modules/@node-red/runtime/lib/library/index.js b/packages/node_modules/@node-red/runtime/lib/library/index.js
index 8a4e6518e..293745f2d 100644
--- a/packages/node_modules/@node-red/runtime/lib/library/index.js
+++ b/packages/node_modules/@node-red/runtime/lib/library/index.js
@@ -15,21 +15,54 @@
**/
-var knownTypes = {};
+const {events} = require("@node-red/util")
+const knownTypes = {};
+const libraries = {};
+const libraryPlugins = {};
-var libraries = {};
+// Libraries defined in the settings file. Their configurations
+// cannot be modified in the editor.
+let runtimeLibraries = [];
+// Libraries defined by the user in the editor.
+let userLibraries = [];
function init(runtime) {
- knownTypes = {
- 'flows': 'node-red'
- };
- libraries["_examples_"] = require("./examples");
- libraries["_examples_"].init(runtime);
+ events.on("registry:plugin-added", function(id) {
+ const plugin = runtime.plugins.getPlugin(id);
+ if (plugin.type === "node-red-library-source") {
+ libraryPlugins[plugin.id] = plugin;
+
+ runtimeLibraries.forEach(library => {
+ if (library.type === id) {
+ library.local = false;
+ libraries[library.id] = new plugin.class(library)
+ if (libraries[library.id].init) {
+ libraries[library.id].init();
+ }
+ }
+ })
+
+
+ }
+ })
+
+ knownTypes.flows = 'node-red';
+
+ libraries["examples"] = require("./examples");
+ libraries["examples"].init(runtime);
libraries["local"] = require("./local");
libraries["local"].init(runtime);
+ try {
+ runtimeLibraries = runtime.settings.editorTheme.library.sources;
+ } catch(err) {
+ runtimeLibraries = [];
+ }
+ // userLibraries = runtime.settings.get("library")
+
+
}
function registerType(id,type) {
@@ -56,7 +89,7 @@ function saveEntry(library,type,path,meta,body) {
throw new Error(`Unknown library type '${type}'`);
}
if (libraries.hasOwnProperty(library)) {
- if (libraries[library].hasOwnProperty("saveEntry")) {
+ if (libraries[library].saveEntry) {
return libraries[library].saveEntry(type,path,meta,body);
} else {
throw new Error(`Library '${library}' is read-only`);
@@ -66,8 +99,43 @@ function saveEntry(library,type,path,meta,body) {
}
}
+function getLibraries() {
+ const libraryList = [
+ {
+ id: "local",
+ label: "editor:library.types.local",
+ user: false,
+ icon: "fa fa-bath"
+ },
+ {
+ id: "examples",
+ label: "editor:library.types.examples",
+ user: false,
+ readOnly: true,
+ types: ['flows']
+ }
+ ];
+
+ for (let id in libraries) {
+ if (libraries.hasOwnProperty(id)) {
+ if (id !== 'local' && id !== 'examples') {
+ libraryList.push({
+ id: id,
+ label: libraries[id].name || id,
+ user: false,
+ icon: libraries[id].icon
+ })
+ }
+ }
+ }
+
+ return libraryList;
+
+}
+
module.exports = {
init: init,
+ getLibraries: getLibraries,
register: registerType,
getEntry: getEntry,
saveEntry: saveEntry
diff --git a/test/resources/plugin/test-plugin/library-filestore.html b/test/resources/plugin/test-plugin/library-filestore.html
new file mode 100644
index 000000000..b4f1c0946
--- /dev/null
+++ b/test/resources/plugin/test-plugin/library-filestore.html
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/test/resources/plugin/test-plugin/library-filestore.js b/test/resources/plugin/test-plugin/library-filestore.js
new file mode 100644
index 000000000..61c8d9803
--- /dev/null
+++ b/test/resources/plugin/test-plugin/library-filestore.js
@@ -0,0 +1,32 @@
+
+module.exports = function(RED) {
+
+ class FileStorePlugin {
+ constructor(config) {
+ this.id = config.id;
+ this.name = config.name;
+ this.config = config;
+ this.icon = config.icon;
+
+ console.log("FileStorePlugin",config)
+ }
+ async init() {
+ console.log("FileStorePlugin.init")
+
+ }
+ async getEntry(type,path) {
+ console.log("FileStorePlugin.getLibraryEntry",type,path)
+ return []
+ }
+ async saveEntry(type,path,meta,body) {
+ console.log("FileStorePlugin.saveLibraryEntry",type,path)
+
+ }
+ }
+
+
+ RED.plugins.registerPlugin("node-red-library-filestore", {
+ type: "node-red-library-source",
+ class: FileStorePlugin
+ })
+}
\ No newline at end of file
diff --git a/test/resources/plugin/test-plugin/package.json b/test/resources/plugin/test-plugin/package.json
index 1077042de..65487c656 100644
--- a/test/resources/plugin/test-plugin/package.json
+++ b/test/resources/plugin/test-plugin/package.json
@@ -6,7 +6,8 @@
"plugins": {
"test": "test.js",
"test-editor-plugin": "test-editor-plugin.html",
- "test-runtime-plugin": "test-runtime-plugin.js"
+ "test-runtime-plugin": "test-runtime-plugin.js",
+ "library-filestore": "library-filestore.js"
}
}
}