Add dynamic node api

Closes #322
- nodes modules can be installed/removed dynamically at runtime
- nodes can be enabled/disabled
- onpaletteadd/onpaletteremove api added to node definitions
- initial implementation of nr-cli
This commit is contained in:
Nick O'Leary
2014-08-28 00:35:07 +01:00
parent 00cb8d5bce
commit da61fe12d0
24 changed files with 1540 additions and 381 deletions

View File

@@ -71,9 +71,23 @@ RED.comms = (function() {
}
}
function unsubscribe(topic,callback) {
if (subscriptions.topic) {
for (var i=0;i<subscriptions.topic.length;i++) {
if (subscriptions.topic[i] === callback) {
subscriptions.topic.splice(i,1);
break;
}
}
if (subscriptions.topic.length === 0) {
delete subscriptions.topic;
}
}
}
return {
connect: connectWS,
subscribe: subscribe
subscribe: subscribe,
unsubscribe:unsubscribe
}
})();

View File

@@ -145,17 +145,35 @@ var RED = (function() {
$.get('settings', function(data) {
RED.settings = data;
console.log("Node-RED: "+data.version);
loadNodes();
loadNodeList();
});
}
function loadNodeList() {
$.ajax({
headers: {
"Accept":"application/json"
},
url: 'nodes',
success: function(data) {
RED.nodes.setNodeList(data);
loadNodes();
}
});
}
function loadNodes() {
$.get('nodes', function(data) {
$("body").append(data);
$(".palette-spinner").hide();
$(".palette-scroll").show();
$("#palette-search").show();
loadFlows();
$.ajax({
headers: {
"Accept":"text/html"
},
url: 'nodes',
success: function(data) {
$("body").append(data);
$(".palette-spinner").hide();
$(".palette-scroll").show();
$("#palette-search").show();
loadFlows();
}
});
}
@@ -176,24 +194,56 @@ var RED = (function() {
}
});
RED.comms.subscribe("node/#",function(topic,msg) {
var i;
var i,m;
var typeList;
var info;
if (topic == "node/added") {
var addedTypes = [];
for (i=0;i<msg.length;i++) {
var m = msg[i];
m = msg[i];
var id = m.id;
$.get('nodes/'+id, function(data) {
$("body").append(data);
var typeList = "<ul><li>"+m.types.join("</li><li>")+"</li></ul>";
RED.notify("Node"+(m.types.length!=1 ? "s":"")+" added to palette:"+typeList,"success");
});
RED.nodes.addNodeSet(m);
if (m.loaded) {
addedTypes = addedTypes.concat(m.types);
$.get('nodes/'+id, function(data) {
$("body").append(data);
});
}
}
if (addedTypes.length) {
typeList = "<ul><li>"+addedTypes.join("</li><li>")+"</li></ul>";
RED.notify("Node"+(addedTypes.length!=1 ? "s":"")+" added to palette:"+typeList,"success");
}
} else if (topic == "node/removed") {
if (msg.types) {
for (i=0;i<msg.types.length;i++) {
RED.palette.remove(msg.types[i]);
for (i=0;i<msg.length;i++) {
m = msg[i];
info = RED.nodes.removeNodeSet(m.id);
if (info.added) {
typeList = "<ul><li>"+m.types.join("</li><li>")+"</li></ul>";
RED.notify("Node"+(m.types.length!=1 ? "s":"")+" removed from palette:"+typeList,"success");
}
var typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
RED.notify("Node"+(msg.types.length!=1 ? "s":"")+" removed from palette:"+typeList,"success");
}
} else if (topic == "node/enabled") {
if (msg.types) {
info = RED.nodes.getNodeSet(msg.id);
if (info.added) {
RED.nodes.enableNodeSet(msg.id);
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
RED.notify("Node"+(msg.types.length!=1 ? "s":"")+" enabled:"+typeList,"success");
} else {
$.get('nodes/'+msg.id, function(data) {
$("body").append(data);
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
RED.notify("Node"+(msg.types.length!=1 ? "s":"")+" added to palette:"+typeList,"success");
});
}
}
} else if (topic == "node/disabled") {
if (msg.types) {
RED.nodes.disableNodeSet(msg.id);
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
RED.notify("Node"+(msg.types.length!=1 ? "s":"")+" disabled:"+typeList,"success");
}
}
});

View File

@@ -21,21 +21,101 @@ RED.nodes = (function() {
var links = [];
var defaultWorkspace;
var workspaces = {};
function registerType(nt,def) {
node_defs[nt] = def;
// TODO: too tightly coupled into palette UI
RED.palette.add(nt,def);
}
var registry = (function() {
var nodeList = [];
var nodeSets = {};
var typeToId = {};
var nodeDefinitions = {};
var exports = {
getNodeList: function() {
return nodeList;
},
setNodeList: function(list) {
nodeList = [];
for(var i=0;i<list.length;i++) {
var ns = list[i];
exports.addNodeSet(ns);
}
},
addNodeSet: function(ns) {
ns.added = false;
nodeSets[ns.id] = ns;
for (var j=0;j<ns.types.length;j++) {
typeToId[ns.types[j]] = ns.id;
}
nodeList.push(ns);
},
removeNodeSet: function(id) {
var ns = nodeSets[id];
for (var j=0;j<ns.types.length;j++) {
if (ns.added) {
// TODO: too tightly coupled into palette UI
RED.palette.remove(ns.types[j]);
var def = nodeDefinitions[ns.types[j]];
if (def.onpaletteremove && typeof def.onpaletteremove === "function") {
def.onpaletteremove.call(def);
}
}
delete typeToId[ns.types[j]];
}
delete nodeSets[id];
for (var i=0;i<nodeList.length;i++) {
if (nodeList[i].id == id) {
nodeList.splice(i,1);
break;
}
}
return ns;
},
getNodeSet: function(id) {
return nodeSets[id];
},
enableNodeSet: function(id) {
var ns = nodeSets[id];
ns.enabled = true;
for (var j=0;j<ns.types.length;j++) {
// TODO: too tightly coupled into palette UI
RED.palette.show(ns.types[j]);
var def = nodeDefinitions[ns.types[j]];
if (def.onpaletteadd && typeof def.onpaletteadd === "function") {
def.onpaletteadd.call(def);
}
}
},
disableNodeSet: function(id) {
var ns = nodeSets[id];
ns.enabled = false;
for (var j=0;j<ns.types.length;j++) {
// TODO: too tightly coupled into palette UI
RED.palette.hide(ns.types[j]);
var def = nodeDefinitions[ns.types[j]];
if (def.onpaletteremove && typeof def.onpaletteremove === "function") {
def.onpaletteremove.call(def);
}
}
},
registerNodeType: function(nt,def) {
nodeDefinitions[nt] = def;
nodeSets[typeToId[nt]].added = true;
// TODO: too tightly coupled into palette UI
RED.palette.add(nt,def);
if (def.onpaletteadd && typeof def.onpaletteadd === "function") {
def.onpaletteadd.call(def);
}
},
getNodeType: function(nt) {
return nodeDefinitions[nt];
}
}
return exports;
})();
function getID() {
return (1+Math.random()*4294967295).toString(16);
}
function getType(type) {
return node_defs[type];
}
function addNode(n) {
if (n._def.category == "config") {
configNodes[n.id] = n;
@@ -48,7 +128,7 @@ RED.nodes = (function() {
if (n._def.defaults.hasOwnProperty(d)) {
var property = n._def.defaults[d];
if (property.type) {
var type = getType(property.type)
var type = registry.getNodeType(property.type)
if (type && type.category == "config") {
var configNode = configNodes[n[d]];
if (configNode) {
@@ -101,7 +181,7 @@ RED.nodes = (function() {
if (node._def.defaults.hasOwnProperty(d)) {
var property = node._def.defaults[d];
if (property.type) {
var type = getType(property.type)
var type = registry.getNodeType(property.type)
if (type && type.category == "config") {
var configNode = configNodes[node[d]];
if (configNode) {
@@ -229,7 +309,7 @@ RED.nodes = (function() {
for (var d in node._def.defaults) {
if (node._def.defaults[d].type && node[d] in configNodes) {
var confNode = configNodes[node[d]];
var exportable = getType(node._def.defaults[d].type).exportable;
var exportable = registry.getNodeType(node._def.defaults[d].type).exportable;
if ((exportable == null || exportable)) {
if (!(node[d] in exportedConfigNodes)) {
exportedConfigNodes[node[d]] = true;
@@ -288,7 +368,7 @@ RED.nodes = (function() {
for (i=0;i<newNodes.length;i++) {
n = newNodes[i];
// TODO: remove workspace in next release+1
if (n.type != "workspace" && n.type != "tab" && !getType(n.type)) {
if (n.type != "workspace" && n.type != "tab" && !registry.getNodeType(n.type)) {
// TODO: get this UI thing out of here! (see below as well)
n.name = n.type;
n.type = "unknown";
@@ -347,7 +427,7 @@ RED.nodes = (function() {
n = newNodes[i];
// TODO: remove workspace in next release+1
if (n.type !== "workspace" && n.type !== "tab") {
var def = getType(n.type);
var def = registry.getNodeType(n.type);
if (def && def.category == "config") {
if (!RED.nodes.node(n.id)) {
var configNode = {id:n.id,type:n.type,users:[]};
@@ -424,8 +504,17 @@ RED.nodes = (function() {
}
return {
registerType: registerType,
getType: getType,
registry:registry,
setNodeList: registry.setNodeList,
getNodeSet: registry.getNodeSet,
addNodeSet: registry.addNodeSet,
removeNodeSet: registry.removeNodeSet,
enableNodeSet: registry.enableNodeSet,
disableNodeSet: registry.disableNodeSet,
registerType: registry.registerNodeType,
getType: registry.getNodeType,
convertNode: convertNode,
add: addNode,
addLink: addLink,

View File

@@ -29,14 +29,21 @@ RED.palette = (function() {
'<div id="palette-'+category+'-function"></div>'+
'</div>'+
'</div>');
$("#header-"+category).on('click', function(e) {
$(this).next().slideToggle();
$(this).children("i").toggleClass("expanded");
});
}
core.forEach(createCategoryContainer);
function addNodeType(nt,def) {
if ($("#palette_node_"+nt).length) {
var nodeTypeId = nt.replace(" ","_");
if ($("#palette_node_"+nodeTypeId).length) {
return;
}
@@ -46,7 +53,7 @@ RED.palette = (function() {
var rootCategory = category.split("-")[0];
var d = document.createElement("div");
d.id = "palette_node_"+nt;
d.id = "palette_node_"+nodeTypeId;
d.type = nt;
var label = /^(.*?)([ -]in|[ -]out)?$/.exec(nt)[1];
@@ -106,17 +113,21 @@ RED.palette = (function() {
revert: true,
revertDuration: 50
});
$("#header-"+category[0]).off('click').on('click', function(e) {
$(this).next().slideToggle();
$(this).children("i").toggleClass("expanded");
});
}
}
function removeNodeType(type) {
$("#palette_node_"+type).remove();
function removeNodeType(nt) {
var nodeTypeId = nt.replace(" ","_");
$("#palette_node_"+nodeTypeId).remove();
}
function hideNodeType(nt) {
var nodeTypeId = nt.replace(" ","_");
$("#palette_node_"+nodeTypeId).hide();
}
function showNodeType(nt) {
var nodeTypeId = nt.replace(" ","_");
$("#palette_node_"+nodeTypeId).show();
}
function filterChange() {
@@ -164,6 +175,8 @@ RED.palette = (function() {
return {
add:addNodeType,
remove:removeNodeType
remove:removeNodeType,
hide:hideNodeType,
show:showNodeType
};
})();

View File

@@ -34,6 +34,10 @@ RED.sidebar = (function() {
//$('#sidebar').tabs("refresh");
}
function removeTab(title) {
sidebar_tabs.removeTab("tab-"+title);
}
var sidebarSeparator = {};
$("#sidebar-separator").draggable({
axis: "x",
@@ -141,6 +145,7 @@ RED.sidebar = (function() {
return {
addTab: addTab,
removeTab: removeTab,
show: showSidebar,
containsTab: containsTab,
toggleSidebar: toggleSidebar