mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Compare commits
41 Commits
0.16.1
...
nodeSettin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b3d696e45 | ||
|
|
34089aec70 | ||
|
|
fca77a868f | ||
|
|
4794fe495c | ||
|
|
869fdbcc6a | ||
|
|
702e6d3b51 | ||
|
|
5f1e37b7fa | ||
|
|
ec0209b175 | ||
|
|
a17dcbde0f | ||
|
|
fbd159a23a | ||
|
|
599a6bf050 | ||
|
|
185b16a858 | ||
|
|
e7e3ed4923 | ||
|
|
47df5476ba | ||
|
|
d7c516ab00 | ||
|
|
50838970ec | ||
|
|
1d15ee7034 | ||
|
|
7029541b4f | ||
|
|
ada8e447cc | ||
|
|
1841fc18fa | ||
|
|
f2235dacdc | ||
|
|
50017c28da | ||
|
|
85b2a03a42 | ||
|
|
829087550d | ||
|
|
dd6f71fe85 | ||
|
|
92a928680c | ||
|
|
d008b1970c | ||
|
|
4affbb8c6b | ||
|
|
ddb2ea4b5f | ||
|
|
a69683183f | ||
|
|
8d34f87667 | ||
|
|
128c4fe222 | ||
|
|
b10141d71f | ||
|
|
68e0b35364 | ||
|
|
e27f5d0460 | ||
|
|
0720128bd4 | ||
|
|
b8888a5d46 | ||
|
|
0857f979ff | ||
|
|
11f4ae019c | ||
|
|
64daaeb310 | ||
|
|
0646b0060e |
@@ -116,6 +116,7 @@ module.exports = function(grunt) {
|
||||
"editor/js/ui/common/popover.js",
|
||||
"editor/js/ui/common/searchBox.js",
|
||||
"editor/js/ui/common/tabs.js",
|
||||
"editor/js/ui/common/stack.js",
|
||||
"editor/js/ui/common/typedInput.js",
|
||||
"editor/js/ui/actions.js",
|
||||
"editor/js/ui/deploy.js",
|
||||
|
||||
@@ -191,7 +191,7 @@ RED.history = (function() {
|
||||
} else if (ev.t == "edit") {
|
||||
for (i in ev.changes) {
|
||||
if (ev.changes.hasOwnProperty(i)) {
|
||||
if (ev.node._def.defaults[i].type) {
|
||||
if (ev.node._def.defaults[i] && ev.node._def.defaults[i].type) {
|
||||
// This is a config node property
|
||||
var currentConfigNode = RED.nodes.node(ev.node[i]);
|
||||
if (currentConfigNode) {
|
||||
@@ -239,7 +239,7 @@ RED.history = (function() {
|
||||
if (ev.outputMap) {
|
||||
outputMap = {};
|
||||
for (var port in ev.outputMap) {
|
||||
if (ev.outputMap.hasOwnProperty(port) && ev.outputMap[port] !== -1) {
|
||||
if (ev.outputMap.hasOwnProperty(port) && ev.outputMap[port] !== "-1") {
|
||||
outputMap[ev.outputMap[port]] = port;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
url: 'nodes',
|
||||
success: function(data) {
|
||||
RED.nodes.setNodeList(data);
|
||||
|
||||
var nsCount = 0;
|
||||
for (var i=0;i<data.length;i++) {
|
||||
var ns = data[i];
|
||||
@@ -158,6 +157,9 @@
|
||||
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
||||
RED.notify(RED._("palette.event.nodeDisabled", {count:msg.types.length})+typeList,"success");
|
||||
}
|
||||
} else if (topic == "node/upgraded") {
|
||||
RED.notify(RED._("palette.event.nodeUpgraded", {module:msg.module,version:msg.version}),"success");
|
||||
RED.nodes.registry.setModulePendingUpdated(msg.module,msg.version);
|
||||
}
|
||||
// Refresh flow library to ensure any examples are updated
|
||||
RED.library.loadFlowLibrary();
|
||||
|
||||
@@ -42,6 +42,10 @@ RED.nodes = (function() {
|
||||
var nodeDefinitions = {};
|
||||
|
||||
var exports = {
|
||||
setModulePendingUpdated: function(module,version) {
|
||||
moduleList[module].pending_version = version;
|
||||
RED.events.emit("registry:module-updated",{module:module,version:version});
|
||||
},
|
||||
getModule: function(module) {
|
||||
return moduleList[module];
|
||||
},
|
||||
@@ -78,6 +82,9 @@ RED.nodes = (function() {
|
||||
local:ns.local,
|
||||
sets:{}
|
||||
};
|
||||
if (ns.pending_version) {
|
||||
moduleList[ns.module].pending_version = ns.pending_version;
|
||||
}
|
||||
moduleList[ns.module].sets[ns.name] = ns;
|
||||
RED.events.emit("registry:node-set-added",ns);
|
||||
},
|
||||
@@ -115,6 +122,7 @@ RED.nodes = (function() {
|
||||
},
|
||||
registerNodeType: function(nt,def) {
|
||||
nodeDefinitions[nt] = def;
|
||||
def.type = nt;
|
||||
if (def.category != "subflows") {
|
||||
def.set = nodeSets[typeToId[nt]];
|
||||
nodeSets[typeToId[nt]].added = true;
|
||||
@@ -128,10 +136,15 @@ RED.nodes = (function() {
|
||||
}
|
||||
def["_"] = function() {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
var original = args[0];
|
||||
if (args[0].indexOf(":") === -1) {
|
||||
args[0] = ns+":"+args[0];
|
||||
}
|
||||
return RED._.apply(null,args);
|
||||
var result = RED._.apply(null,args);
|
||||
if (result === args[0]) {
|
||||
result = original;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: too tightly coupled into palette UI
|
||||
@@ -160,6 +173,8 @@ RED.nodes = (function() {
|
||||
function addNode(n) {
|
||||
if (n.type.indexOf("subflow") !== 0) {
|
||||
n["_"] = n._def._;
|
||||
} else {
|
||||
n["_"] = RED._;
|
||||
}
|
||||
if (n._def.category == "config") {
|
||||
configNodes[n.id] = n;
|
||||
@@ -316,14 +331,6 @@ RED.nodes = (function() {
|
||||
});
|
||||
sf.name = subflowName;
|
||||
}
|
||||
sf._def = {
|
||||
defaults:{},
|
||||
icon:"subflow.png",
|
||||
category: "subflows",
|
||||
color: "#da9",
|
||||
inputs: sf.in.length,
|
||||
outputs: sf.out.length
|
||||
}
|
||||
subflows[sf.id] = sf;
|
||||
RED.nodes.registerType("subflow:"+sf.id, {
|
||||
defaults:{name:{value:""}},
|
||||
@@ -336,12 +343,13 @@ RED.nodes = (function() {
|
||||
label: function() { return this.name||RED.nodes.subflow(sf.id).name },
|
||||
labelStyle: function() { return this.name?"node_label_italic":""; },
|
||||
paletteLabel: function() { return RED.nodes.subflow(sf.id).name },
|
||||
inputLabels: function(i) { return sf.inputLabels?sf.inputLabels[i]:null },
|
||||
outputLabels: function(i) { return sf.outputLabels?sf.outputLabels[i]:null },
|
||||
set:{
|
||||
module: "node-red"
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
sf._def = RED.nodes.getType("subflow:"+sf.id);
|
||||
}
|
||||
function getSubflow(id) {
|
||||
return subflows[id];
|
||||
@@ -418,6 +426,7 @@ RED.nodes = (function() {
|
||||
node.id = n.id;
|
||||
node.type = n.type;
|
||||
node.z = n.z;
|
||||
|
||||
if (node.type == "unknown") {
|
||||
for (var p in n._orig) {
|
||||
if (n._orig.hasOwnProperty(p)) {
|
||||
@@ -465,6 +474,13 @@ RED.nodes = (function() {
|
||||
node.wires[w.sourcePort].push(w.target.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (n.inputs > 0 && n.inputLabels && !/^\s*$/.test(n.inputLabels.join(""))) {
|
||||
node.inputLabels = n.inputLabels.slice();
|
||||
}
|
||||
if (n.outputs > 0 && n.outputLabels && !/^\s*$/.test(n.outputLabels.join(""))) {
|
||||
node.outputLabels = n.outputLabels.slice();
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
@@ -502,6 +518,13 @@ RED.nodes = (function() {
|
||||
node.out.push(nOut);
|
||||
});
|
||||
|
||||
if (node.in.length > 0 && n.inputLabels && !/^\s*$/.test(n.inputLabels.join(""))) {
|
||||
node.inputLabels = n.inputLabels.slice();
|
||||
}
|
||||
if (node.out.length > 0 && n.outputLabels && !/^\s*$/.test(n.outputLabels.join(""))) {
|
||||
node.outputLabels = n.outputLabels.slice();
|
||||
}
|
||||
|
||||
|
||||
return node;
|
||||
}
|
||||
@@ -880,7 +903,17 @@ RED.nodes = (function() {
|
||||
if (n.type !== "workspace" && n.type !== "tab" && n.type !== "subflow") {
|
||||
def = registry.getNodeType(n.type);
|
||||
if (!def || def.category != "config") {
|
||||
var node = {x:n.x,y:n.y,z:n.z,type:0,wires:n.wires,changed:false,_config:{}};
|
||||
var node = {
|
||||
x:n.x,
|
||||
y:n.y,
|
||||
z:n.z,
|
||||
type:0,
|
||||
wires:n.wires,
|
||||
inputLabels: n.inputLabels,
|
||||
outputLabels: n.outputLabels,
|
||||
changed:false,
|
||||
_config:{}
|
||||
};
|
||||
if (createNewIds) {
|
||||
if (subflow_blacklist[n.z]) {
|
||||
continue;
|
||||
|
||||
@@ -298,14 +298,16 @@ RED.clipboard = (function() {
|
||||
|
||||
|
||||
$('#chart').on("dragenter",function(event) {
|
||||
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
|
||||
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1 ||
|
||||
$.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
|
||||
$("#dropTarget").css({display:'table'});
|
||||
RED.keyboard.add("*", "escape" ,hideDropTarget);
|
||||
}
|
||||
});
|
||||
|
||||
$('#dropTarget').on("dragover",function(event) {
|
||||
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
|
||||
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1 ||
|
||||
$.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
|
||||
event.preventDefault();
|
||||
}
|
||||
})
|
||||
@@ -313,10 +315,24 @@ RED.clipboard = (function() {
|
||||
hideDropTarget();
|
||||
})
|
||||
.on("drop",function(event) {
|
||||
var data = event.originalEvent.dataTransfer.getData("text/plain");
|
||||
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);
|
||||
RED.view.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) {
|
||||
RED.view.importNodes(e.target.result);
|
||||
};
|
||||
})(file);
|
||||
reader.readAsText(file);
|
||||
}
|
||||
}
|
||||
hideDropTarget();
|
||||
data = data.substring(data.indexOf('['),data.lastIndexOf(']')+1);
|
||||
RED.view.importNodes(data);
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
|
||||
95
editor/js/ui/common/stack.js
Normal file
95
editor/js/ui/common/stack.js
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
RED.stack = (function() {
|
||||
function createStack(options) {
|
||||
var container = options.container;
|
||||
|
||||
var entries = [];
|
||||
|
||||
return {
|
||||
add: function(entry) {
|
||||
|
||||
entries.push(entry);
|
||||
var entryContainer = $('<div class="palette-category">').appendTo(container);
|
||||
|
||||
var header = $('<div class="palette-header"></div>').appendTo(entryContainer);
|
||||
var icon = $('<i class="fa fa-angle-down"></i>').appendTo(header);
|
||||
$('<span></span>').html(entry.title).appendTo(header);
|
||||
entry.content = $('<div class="editor-tray-content"></div>').appendTo(entryContainer);
|
||||
|
||||
if (entry.expanded) {
|
||||
icon.addClass("expanded");
|
||||
} else {
|
||||
entry.content.hide();
|
||||
}
|
||||
|
||||
header.click(function() {
|
||||
if (options.singleExpanded) {
|
||||
if (!entry.isExpanded()) {
|
||||
for (var i=0;i<entries.length;i++) {
|
||||
if (entries[i].isExpanded()) {
|
||||
entries[i].collapse();
|
||||
}
|
||||
}
|
||||
entry.expand();
|
||||
}
|
||||
} else {
|
||||
entry.toggle();
|
||||
}
|
||||
});
|
||||
entry.toggle = function() {
|
||||
if (entry.isExpanded()) {
|
||||
entry.collapse();
|
||||
return false;
|
||||
} else {
|
||||
entry.expand();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
entry.expand = function() {
|
||||
if (!entry.isExpanded()) {
|
||||
if (entry.onexpand) {
|
||||
entry.onexpand.call(entry);
|
||||
}
|
||||
icon.addClass("expanded");
|
||||
entry.content.slideDown(200);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
entry.collapse = function() {
|
||||
if (entry.isExpanded()) {
|
||||
icon.removeClass("expanded");
|
||||
entry.content.slideUp(200);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
entry.isExpanded = function() {
|
||||
return icon.hasClass("expanded");
|
||||
};
|
||||
|
||||
return entry;
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
create: createStack
|
||||
}
|
||||
})();
|
||||
@@ -73,6 +73,9 @@ RED.tabs = (function() {
|
||||
ul.children().addClass("red-ui-tab");
|
||||
|
||||
function onTabClick() {
|
||||
if (options.onclick) {
|
||||
options.onclick(tabs[$(this).attr('href').slice(1)]);
|
||||
}
|
||||
activateTab($(this));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -212,18 +212,7 @@ RED.deploy = (function() {
|
||||
tabLabel = tab.label;
|
||||
}
|
||||
}
|
||||
var label = "";
|
||||
if (typeof node._def.label == "function") {
|
||||
try {
|
||||
label = node._def.label.call(node);
|
||||
} catch(err) {
|
||||
console.log("Definition error: "+node_def.type+".label",err);
|
||||
label = node_def.type;
|
||||
}
|
||||
} else {
|
||||
label = node._def.label;
|
||||
}
|
||||
label = label || node.id;
|
||||
var label = RED.utils.getNodeLabel(node,node.id);
|
||||
return {tab:tabLabel,type:node.type,label:label};
|
||||
}
|
||||
function sortNodeInfo(A,B) {
|
||||
|
||||
@@ -342,21 +342,14 @@ RED.diff = (function() {
|
||||
function createNodeIcon(node,def) {
|
||||
var nodeDiv = $("<div>",{class:"node-diff-node-entry-node"});
|
||||
var colour = def.color;
|
||||
var icon_url = "arrow-in.png";
|
||||
var icon_url = RED.utils.getNodeIcon(def,node);
|
||||
if (node.type === 'tab') {
|
||||
colour = "#C0DEED";
|
||||
icon_url = "subflow.png";
|
||||
} else if (def.category === 'config') {
|
||||
icon_url = "cog.png";
|
||||
} else if (node.type === 'unknown') {
|
||||
icon_url = "alert.png";
|
||||
} else {
|
||||
icon_url = def.icon;
|
||||
}
|
||||
nodeDiv.css('backgroundColor',colour);
|
||||
|
||||
var iconContainer = $('<div/>',{class:"palette_icon_container"}).appendTo(nodeDiv);
|
||||
$('<div/>',{class:"palette_icon",style:"background-image: url(icons/"+icon_url+")"}).appendTo(iconContainer);
|
||||
$('<div/>',{class:"palette_icon",style:"background-image: url("+icon_url+")"}).appendTo(iconContainer);
|
||||
|
||||
return nodeDiv;
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@ RED.editor = (function() {
|
||||
if (outputMap) {
|
||||
RED.nodes.eachLink(function(l) {
|
||||
if (l.source === node && outputMap.hasOwnProperty(l.sourcePort)) {
|
||||
if (outputMap[l.sourcePort] === -1) {
|
||||
if (outputMap[l.sourcePort] === "-1") {
|
||||
removedLinks.push(l);
|
||||
} else {
|
||||
l.sourcePort = outputMap[l.sourcePort];
|
||||
@@ -265,17 +265,8 @@ RED.editor = (function() {
|
||||
var configNode = RED.nodes.node(node[property]);
|
||||
var node_def = RED.nodes.getType(type);
|
||||
|
||||
if (configNode && node_def.label) {
|
||||
if (typeof node_def.label == "function") {
|
||||
try {
|
||||
label = node_def.label.call(configNode);
|
||||
} catch(err) {
|
||||
console.log("Definition error: "+node_def.type+".label",err);
|
||||
label = node_def.type;
|
||||
}
|
||||
} else {
|
||||
label = node_def.label;
|
||||
}
|
||||
if (configNode) {
|
||||
label = RED.utils.getNodeLabel(configNode,configNode.id);
|
||||
}
|
||||
input.val(label);
|
||||
}
|
||||
@@ -527,9 +518,8 @@ RED.editor = (function() {
|
||||
return title;
|
||||
}
|
||||
|
||||
function buildEditForm(tray,formId,type,ns) {
|
||||
var trayBody = tray.find('.editor-tray-body');
|
||||
var dialogForm = $('<form id="'+formId+'" class="form-horizontal" autocomplete="off"></form>').appendTo(trayBody);
|
||||
function buildEditForm(container,formId,type,ns) {
|
||||
var dialogForm = $('<form id="'+formId+'" class="form-horizontal" autocomplete="off"></form>').appendTo(container);
|
||||
dialogForm.html($("script[data-template-name='"+type+"']").html());
|
||||
ns = ns||"node-red";
|
||||
dialogForm.find('[data-i18n]').each(function() {
|
||||
@@ -558,6 +548,118 @@ RED.editor = (function() {
|
||||
return dialogForm;
|
||||
}
|
||||
|
||||
function refreshLabelForm(container,node) {
|
||||
|
||||
var inputPlaceholder = node._def.inputLabels?RED._("editor.defaultLabel"):RED._("editor.noDefaultLabel");
|
||||
var outputPlaceholder = node._def.outputLabels?RED._("editor.defaultLabel"):RED._("editor.noDefaultLabel");
|
||||
|
||||
var inputsDiv = $("#node-label-form-inputs");
|
||||
var outputsDiv = $("#node-label-form-outputs");
|
||||
|
||||
var inputCount = node.inputs || node._def.inputs || 0;
|
||||
var children = inputsDiv.children();
|
||||
if (children.length < inputCount) {
|
||||
for (i = children.length;i<inputCount;i++) {
|
||||
buildLabelRow("input",i,"",inputPlaceholder).appendTo(inputsDiv);
|
||||
}
|
||||
} else if (children.length > inputCount) {
|
||||
for (i=inputCount;i<children.length;i++) {
|
||||
$(children[i]).remove();
|
||||
}
|
||||
}
|
||||
|
||||
var outputCount;
|
||||
var i;
|
||||
var formOutputs = $("#node-input-outputs").val();
|
||||
|
||||
if (formOutputs === undefined) {
|
||||
outputCount = node.outputs || node._def.outputs || 0;
|
||||
} else if (isNaN(formOutputs)) {
|
||||
var outputMap = JSON.parse(formOutputs);
|
||||
var keys = Object.keys(outputMap);
|
||||
outputCount = 0;
|
||||
var rows = [];
|
||||
keys.forEach(function(p) {
|
||||
var row = $("#node-label-form-output-"+p).parent();
|
||||
if (row.length === 0 && outputMap[p] !== -1) {
|
||||
row = buildLabelRow("output",p,"",outputPlaceholder);
|
||||
}
|
||||
if (outputMap[p] !== -1) {
|
||||
outputCount++;
|
||||
rows.push({i:parseInt(outputMap[p]),r:row});
|
||||
}
|
||||
});
|
||||
rows.sort(function(A,B) {
|
||||
return A.i-B.i;
|
||||
})
|
||||
outputsDiv.children().detach();
|
||||
rows.forEach(function(r,i) {
|
||||
r.r.find("label").html((i+1)+".");
|
||||
r.r.appendTo(outputsDiv);
|
||||
})
|
||||
} else {
|
||||
outputCount = Math.max(0,parseInt(formOutputs));
|
||||
}
|
||||
children = outputsDiv.children();
|
||||
if (children.length < outputCount) {
|
||||
for (i = children.length;i<outputCount;i++) {
|
||||
buildLabelRow("output",i,"").appendTo(outputsDiv);
|
||||
}
|
||||
} else if (children.length > outputCount) {
|
||||
for (i=outputCount;i<children.length;i++) {
|
||||
$(children[i]).remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
function buildLabelRow(type, index, value, placeHolder) {
|
||||
var result = $('<div>',{style:"margin: 5px 0px"});
|
||||
var id = "node-label-form-"+type+"-"+index;
|
||||
$('<label>',{for:id,style:"margin-right: 20px; text-align: right; width: 30px;"}).html((index+1)+".").appendTo(result);
|
||||
var input = $('<input>',{type:"text",id:id, placeholder: placeHolder}).val(value).appendTo(result);
|
||||
var clear = $('<button class="editor-button editor-button-small" style="margin-left: 10px"><i class="fa fa-times"></i></button>').appendTo(result);
|
||||
clear.click(function(evt) {
|
||||
evt.preventDefault();
|
||||
input.val("");
|
||||
})
|
||||
return result;
|
||||
}
|
||||
function buildLabelForm(container,node) {
|
||||
var dialogForm = $('<form class="dialog-form form-horizontal" autocomplete="off"></form>').appendTo(container);
|
||||
|
||||
var inputCount = node.inputs || node._def.inputs || 0;
|
||||
var outputCount = node.outputs || node._def.outputs || 0;
|
||||
if (node.type === 'subflow') {
|
||||
inputCount = node.in.length;
|
||||
outputCount = node.out.length;
|
||||
}
|
||||
|
||||
var inputLabels = node.inputLabels || [];
|
||||
var outputLabels = node.outputLabels || [];
|
||||
|
||||
var inputPlaceholder = node._def.inputLabels?RED._("editor.defaultLabel"):RED._("editor.noDefaultLabel");
|
||||
var outputPlaceholder = node._def.outputLabels?RED._("editor.defaultLabel"):RED._("editor.noDefaultLabel");
|
||||
|
||||
var i,row;
|
||||
$('<div class="form-row"><i class="fa fa-tag"></i> <span data-i18n="editor.labelInputs"></span><div id="node-label-form-inputs"></div></div>').appendTo(dialogForm);
|
||||
var inputsDiv = $("#node-label-form-inputs");
|
||||
if (inputCount > 0) {
|
||||
for (i=0;i<inputCount;i++) {
|
||||
buildLabelRow("input",i,inputLabels[i],inputPlaceholder).appendTo(inputsDiv);
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
$('<div class="form-row"><i class="fa fa-tag"></i> <span data-i18n="editor.labelOutputs"></span><div id="node-label-form-outputs"></div></div>').appendTo(dialogForm);
|
||||
var outputsDiv = $("#node-label-form-outputs");
|
||||
if (outputCount > 0) {
|
||||
for (i=0;i<outputCount;i++) {
|
||||
buildLabelRow("output",i,outputLabels[i],outputPlaceholder).appendTo(outputsDiv);
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function showEditDialog(node) {
|
||||
var editing_node = node;
|
||||
editStack.push(node);
|
||||
@@ -680,11 +782,11 @@ RED.editor = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
var newValue;
|
||||
if (editing_node._def.defaults) {
|
||||
for (d in editing_node._def.defaults) {
|
||||
if (editing_node._def.defaults.hasOwnProperty(d)) {
|
||||
var input = $("#node-input-"+d);
|
||||
var newValue;
|
||||
if (input.attr('type') === "checkbox") {
|
||||
newValue = input.prop('checked');
|
||||
} else if ("format" in editing_node._def.defaults[d] && editing_node._def.defaults[d].format !== "" && input[0].nodeName === "DIV") {
|
||||
@@ -693,8 +795,42 @@ RED.editor = (function() {
|
||||
newValue = input.val();
|
||||
}
|
||||
if (newValue != null) {
|
||||
if (d === "outputs" && (newValue.trim() === "" || isNaN(newValue))) {
|
||||
continue;
|
||||
if (d === "outputs") {
|
||||
if (newValue.trim() === "") {
|
||||
continue;
|
||||
}
|
||||
if (isNaN(newValue)) {
|
||||
outputMap = JSON.parse(newValue);
|
||||
var outputCount = 0;
|
||||
var outputsChanged = false;
|
||||
var keys = Object.keys(outputMap);
|
||||
keys.forEach(function(p) {
|
||||
if (isNaN(p)) {
|
||||
// New output;
|
||||
outputCount ++;
|
||||
delete outputMap[p];
|
||||
} else {
|
||||
outputMap[p] = outputMap[p]+"";
|
||||
if (outputMap[p] !== "-1") {
|
||||
outputCount++;
|
||||
if (outputMap[p] !== p) {
|
||||
// Output moved
|
||||
outputsChanged = true;
|
||||
} else {
|
||||
delete outputMap[p];
|
||||
}
|
||||
} else {
|
||||
// Output removed
|
||||
outputsChanged = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
newValue = outputCount;
|
||||
if (outputsChanged) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (editing_node[d] != newValue) {
|
||||
if (editing_node._def.defaults[d].type) {
|
||||
@@ -726,14 +862,31 @@ RED.editor = (function() {
|
||||
var credsChanged = updateNodeCredentials(editing_node,credDefinition,prefix);
|
||||
changed = changed || credsChanged;
|
||||
}
|
||||
if (editing_node.hasOwnProperty("_outputs")) {
|
||||
outputMap = editing_node._outputs;
|
||||
delete editing_node._outputs;
|
||||
if (Object.keys(outputMap).length > 0) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
// if (editing_node.hasOwnProperty("_outputs")) {
|
||||
// outputMap = editing_node._outputs;
|
||||
// delete editing_node._outputs;
|
||||
// if (Object.keys(outputMap).length > 0) {
|
||||
// changed = true;
|
||||
// }
|
||||
// }
|
||||
var removedLinks = updateNodeProperties(editing_node,outputMap);
|
||||
|
||||
var inputLabels = $("#node-label-form-inputs").children().find("input");
|
||||
var outputLabels = $("#node-label-form-outputs").children().find("input");
|
||||
|
||||
newValue = inputLabels.map(function() { return $(this).val();}).toArray().slice(0,editing_node.inputs);
|
||||
if (JSON.stringify(newValue) !== JSON.stringify(editing_node.inputLabels)) {
|
||||
changes.inputLabels = editing_node.inputLabels;
|
||||
editing_node.inputLabels = newValue;
|
||||
changed = true;
|
||||
}
|
||||
newValue = outputLabels.map(function() { return $(this).val();}).toArray().slice(0,editing_node.outputs);
|
||||
if (JSON.stringify(newValue) !== JSON.stringify(editing_node.outputLabels)) {
|
||||
changes.outputLabels = editing_node.outputLabels;
|
||||
editing_node.outputLabels = newValue;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
var wasChanged = editing_node.changed;
|
||||
editing_node.changed = true;
|
||||
@@ -782,8 +935,9 @@ RED.editor = (function() {
|
||||
],
|
||||
resize: function(dimensions) {
|
||||
editTrayWidthCache[type] = dimensions.width;
|
||||
$(".editor-tray-content").height(dimensions.height - 78);
|
||||
var form = $(".editor-tray-content form").height(dimensions.height - 78 - 40);
|
||||
if (editing_node && editing_node._def.oneditresize) {
|
||||
var form = $("#dialog-form");
|
||||
try {
|
||||
editing_node._def.oneditresize.call(editing_node,{width:form.width(),height:form.height()});
|
||||
} catch(err) {
|
||||
@@ -792,6 +946,25 @@ RED.editor = (function() {
|
||||
}
|
||||
},
|
||||
open: function(tray) {
|
||||
var trayFooter = tray.find(".editor-tray-footer");
|
||||
var trayBody = tray.find('.editor-tray-body');
|
||||
trayBody.parent().css('overflow','hidden');
|
||||
|
||||
var stack = RED.stack.create({
|
||||
container: trayBody,
|
||||
singleExpanded: true
|
||||
});
|
||||
var nodeProperties = stack.add({
|
||||
title: RED._("editor.nodeProperties"),
|
||||
expanded: true
|
||||
});
|
||||
var portLabels = stack.add({
|
||||
title: RED._("editor.portLabels"),
|
||||
onexpand: function() {
|
||||
refreshLabelForm(this.content,node);
|
||||
}
|
||||
});
|
||||
|
||||
if (editing_node) {
|
||||
RED.sidebar.info.refresh(editing_node);
|
||||
}
|
||||
@@ -801,9 +974,11 @@ RED.editor = (function() {
|
||||
} else {
|
||||
ns = node._def.set.id;
|
||||
}
|
||||
var dialogForm = buildEditForm(tray,"dialog-form",type,ns);
|
||||
buildEditForm(nodeProperties.content,"dialog-form",type,ns);
|
||||
buildLabelForm(portLabels.content,node);
|
||||
|
||||
prepareEditDialog(node,node._def,"node-input");
|
||||
dialogForm.i18n();
|
||||
trayBody.i18n();
|
||||
},
|
||||
close: function() {
|
||||
if (RED.view.state() != RED.state.IMPORT_DRAGGING) {
|
||||
@@ -901,7 +1076,7 @@ RED.editor = (function() {
|
||||
}
|
||||
trayFooter.append('<span id="node-config-dialog-scope-container"><span id="node-config-dialog-scope-warning" data-i18n="[title]editor.errors.scopeChange"><i class="fa fa-warning"></i></span><select id="node-config-dialog-scope"></select></span>');
|
||||
|
||||
var dialogForm = buildEditForm(tray,"node-config-dialog-edit-form",type,ns);
|
||||
var dialogForm = buildEditForm(tray.find('.editor-tray-body'),"node-config-dialog-edit-form",type,ns);
|
||||
|
||||
prepareEditDialog(editing_config_node,node_def,"node-config-input");
|
||||
if (editing_config_node._def.exclusive) {
|
||||
@@ -1207,17 +1382,7 @@ RED.editor = (function() {
|
||||
|
||||
RED.nodes.eachConfig(function(config) {
|
||||
if (config.type == type && (!config.z || config.z === activeWorkspace.id)) {
|
||||
var label = "";
|
||||
if (typeof node_def.label == "function") {
|
||||
try {
|
||||
label = node_def.label.call(config);
|
||||
} catch(err) {
|
||||
console.log("Definition error: "+node_def.type+".label",err);
|
||||
label = node_def.type;
|
||||
}
|
||||
} else {
|
||||
label = node_def.label;
|
||||
}
|
||||
var label = RED.utils.getNodeLabel(config,config.id);
|
||||
config.__label__ = label;
|
||||
configNodes.push(config);
|
||||
}
|
||||
@@ -1284,6 +1449,21 @@ RED.editor = (function() {
|
||||
editing_node.info = newDescription;
|
||||
changed = true;
|
||||
}
|
||||
var inputLabels = $("#node-label-form-inputs").children().find("input");
|
||||
var outputLabels = $("#node-label-form-outputs").children().find("input");
|
||||
|
||||
var newValue = inputLabels.map(function() { return $(this).val();}).toArray().slice(0,editing_node.inputs);
|
||||
if (JSON.stringify(newValue) !== JSON.stringify(editing_node.inputLabels)) {
|
||||
changes.inputLabels = editing_node.inputLabels;
|
||||
editing_node.inputLabels = newValue;
|
||||
changed = true;
|
||||
}
|
||||
newValue = outputLabels.map(function() { return $(this).val();}).toArray().slice(0,editing_node.outputs);
|
||||
if (JSON.stringify(newValue) !== JSON.stringify(editing_node.outputLabels)) {
|
||||
changes.outputLabels = editing_node.outputLabels;
|
||||
editing_node.outputLabels = newValue;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
RED.palette.refresh();
|
||||
|
||||
@@ -1321,7 +1501,10 @@ RED.editor = (function() {
|
||||
}
|
||||
}
|
||||
],
|
||||
resize: function() {
|
||||
resize: function(dimensions) {
|
||||
$(".editor-tray-content").height(dimensions.height - 78);
|
||||
var form = $(".editor-tray-content form").height(dimensions.height - 78 - 40);
|
||||
|
||||
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
|
||||
var editorRow = $("#dialog-form>div.node-text-editor-row");
|
||||
var height = $("#dialog-form").height();
|
||||
@@ -1333,10 +1516,28 @@ RED.editor = (function() {
|
||||
subflowEditor.resize();
|
||||
},
|
||||
open: function(tray) {
|
||||
var trayFooter = tray.find(".editor-tray-footer");
|
||||
var trayBody = tray.find('.editor-tray-body');
|
||||
trayBody.parent().css('overflow','hidden');
|
||||
|
||||
var stack = RED.stack.create({
|
||||
container: trayBody,
|
||||
singleExpanded: true
|
||||
});
|
||||
var nodeProperties = stack.add({
|
||||
title: RED._("editor.nodeProperties"),
|
||||
expanded: true
|
||||
});
|
||||
var portLabels = stack.add({
|
||||
title: RED._("editor.portLabels")
|
||||
});
|
||||
|
||||
|
||||
|
||||
if (editing_node) {
|
||||
RED.sidebar.info.refresh(editing_node);
|
||||
}
|
||||
var dialogForm = buildEditForm(tray,"dialog-form","subflow-template");
|
||||
var dialogForm = buildEditForm(nodeProperties.content,"dialog-form","subflow-template");
|
||||
subflowEditor = RED.editor.createEditor({
|
||||
id: 'subflow-input-info-editor',
|
||||
mode: 'ace/mode/markdown',
|
||||
@@ -1355,7 +1556,9 @@ RED.editor = (function() {
|
||||
}
|
||||
});
|
||||
$("#subflow-dialog-user-count").html(RED._("subflow.subflowInstances", {count:userCount})).show();
|
||||
dialogForm.i18n();
|
||||
|
||||
buildLabelForm(portLabels.content,subflow);
|
||||
trayBody.i18n();
|
||||
},
|
||||
close: function() {
|
||||
if (RED.view.state() != RED.state.IMPORT_DRAGGING) {
|
||||
@@ -1417,7 +1620,7 @@ RED.editor = (function() {
|
||||
},
|
||||
open: function(tray) {
|
||||
var trayBody = tray.find('.editor-tray-body');
|
||||
var dialogForm = buildEditForm(tray,'dialog-form','_expression','editor');
|
||||
var dialogForm = buildEditForm(tray.find('.editor-tray-body'),'dialog-form','_expression','editor');
|
||||
var funcSelect = $("#node-input-expression-func");
|
||||
Object.keys(jsonata.functions).forEach(function(f) {
|
||||
funcSelect.append($("<option></option>").val(f).text(f));
|
||||
|
||||
@@ -31,6 +31,17 @@ RED.palette.editor = (function() {
|
||||
var eventTimers = {};
|
||||
var activeFilter = "";
|
||||
|
||||
function semVerCompare(A,B) {
|
||||
var aParts = A.split(".").map(function(m) { return parseInt(m);});
|
||||
var bParts = B.split(".").map(function(m) { return parseInt(m);});
|
||||
for (var i=0;i<3;i++) {
|
||||
var j = aParts[i]-bParts[i];
|
||||
if (j<0) { return -1 }
|
||||
if (j>0) { return 1 }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function delayCallback(start,callback) {
|
||||
var delta = Date.now() - start;
|
||||
if (delta < 300) {
|
||||
@@ -64,14 +75,21 @@ RED.palette.editor = (function() {
|
||||
});
|
||||
})
|
||||
}
|
||||
function installNodeModule(id,shade,callback) {
|
||||
function installNodeModule(id,version,shade,callback) {
|
||||
var requestBody = {
|
||||
module: id
|
||||
};
|
||||
if (callback === undefined) {
|
||||
callback = shade;
|
||||
shade = version;
|
||||
} else {
|
||||
requestBody.version = version;
|
||||
}
|
||||
shade.show();
|
||||
$.ajax({
|
||||
url:"nodes",
|
||||
type: "POST",
|
||||
data: JSON.stringify({
|
||||
module: id
|
||||
}),
|
||||
data: JSON.stringify(requestBody),
|
||||
contentType: "application/json; charset=utf-8"
|
||||
}).done(function(data,textStatus,xhr) {
|
||||
shade.hide();
|
||||
@@ -266,19 +284,19 @@ RED.palette.editor = (function() {
|
||||
nodeEntry.container.toggleClass("disabled",(activeTypeCount === 0));
|
||||
}
|
||||
}
|
||||
|
||||
nodeEntry.updateButton.hide();
|
||||
// if (loadedIndex.hasOwnProperty(module)) {
|
||||
// if (moduleInfo.version !== loadedIndex[module].version) {
|
||||
// nodeEntry.updateButton.show();
|
||||
// nodeEntry.updateButton.html(RED._('palette.editor.update',{version:loadedIndex[module].version}));
|
||||
// } else {
|
||||
// nodeEntry.updateButton.hide();
|
||||
// }
|
||||
//
|
||||
// } else {
|
||||
// nodeEntry.updateButton.hide();
|
||||
// }
|
||||
if (moduleInfo.pending_version) {
|
||||
nodeEntry.versionSpan.html(moduleInfo.version+' <i class="fa fa-long-arrow-right"></i> '+moduleInfo.pending_version).appendTo(nodeEntry.metaRow)
|
||||
nodeEntry.updateButton.html(RED._('palette.editor.updated')).addClass('disabled').show();
|
||||
} else if (loadedIndex.hasOwnProperty(module)) {
|
||||
if (semVerCompare(loadedIndex[module].version,moduleInfo.version) === 1) {
|
||||
nodeEntry.updateButton.show();
|
||||
nodeEntry.updateButton.html(RED._('palette.editor.update',{version:loadedIndex[module].version}));
|
||||
} else {
|
||||
nodeEntry.updateButton.hide();
|
||||
}
|
||||
} else {
|
||||
nodeEntry.updateButton.hide();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -515,7 +533,7 @@ RED.palette.editor = (function() {
|
||||
var titleRow = $('<div class="palette-module-meta palette-module-name"><i class="fa fa-cube"></i></div>').appendTo(headerRow);
|
||||
$('<span>').html(entry.name).appendTo(titleRow);
|
||||
var metaRow = $('<div class="palette-module-meta palette-module-version"><i class="fa fa-tag"></i></div>').appendTo(headerRow);
|
||||
$('<span>').html(entry.version).appendTo(metaRow);
|
||||
var versionSpan = $('<span>').html(entry.version).appendTo(metaRow);
|
||||
var buttonRow = $('<div>',{class:"palette-module-meta"}).appendTo(headerRow);
|
||||
var setButton = $('<a href="#" class="editor-button editor-button-small palette-module-set-button"><i class="fa fa-angle-right palette-module-node-chevron"></i> </a>').appendTo(buttonRow);
|
||||
var setCount = $('<span>').appendTo(setButton);
|
||||
@@ -524,6 +542,27 @@ RED.palette.editor = (function() {
|
||||
var updateButton = $('<a href="#" class="editor-button editor-button-small"></a>').html(RED._('palette.editor.update')).appendTo(buttonGroup);
|
||||
updateButton.click(function(evt) {
|
||||
evt.preventDefault();
|
||||
if ($(this).hasClass('disabled')) {
|
||||
return;
|
||||
}
|
||||
$("#palette-module-install-confirm").data('module',entry.name);
|
||||
$("#palette-module-install-confirm").data('version',loadedIndex[entry.name].version);
|
||||
$("#palette-module-install-confirm").data('shade',shade);
|
||||
|
||||
$("#palette-module-install-confirm-body").html(entry.local?
|
||||
RED._("palette.editor.confirm.update.body"):
|
||||
RED._("palette.editor.confirm.cannotUpdate.body")
|
||||
);
|
||||
$(".palette-module-install-confirm-button-install").hide();
|
||||
$(".palette-module-install-confirm-button-remove").hide();
|
||||
if (entry.local) {
|
||||
$(".palette-module-install-confirm-button-update").show();
|
||||
} else {
|
||||
$(".palette-module-install-confirm-button-update").hide();
|
||||
}
|
||||
$("#palette-module-install-confirm")
|
||||
.dialog('option', 'title',RED._("palette.editor.confirm.update.title"))
|
||||
.dialog('open');
|
||||
})
|
||||
|
||||
|
||||
@@ -536,6 +575,7 @@ RED.palette.editor = (function() {
|
||||
$("#palette-module-install-confirm-body").html(RED._("palette.editor.confirm.remove.body"));
|
||||
$(".palette-module-install-confirm-button-install").hide();
|
||||
$(".palette-module-install-confirm-button-remove").show();
|
||||
$(".palette-module-install-confirm-button-update").hide();
|
||||
$("#palette-module-install-confirm")
|
||||
.dialog('option', 'title', RED._("palette.editor.confirm.remove.title"))
|
||||
.dialog('open');
|
||||
@@ -555,6 +595,7 @@ RED.palette.editor = (function() {
|
||||
setCount: setCount,
|
||||
container: container,
|
||||
shade: shade,
|
||||
versionSpan: versionSpan,
|
||||
sets: {}
|
||||
}
|
||||
setButton.click(function(evt) {
|
||||
@@ -727,11 +768,13 @@ RED.palette.editor = (function() {
|
||||
e.preventDefault();
|
||||
if (!$(this).hasClass('disabled')) {
|
||||
$("#palette-module-install-confirm").data('module',entry.id);
|
||||
$("#palette-module-install-confirm").data('version',entry.version);
|
||||
$("#palette-module-install-confirm").data('url',entry.url);
|
||||
$("#palette-module-install-confirm").data('shade',shade);
|
||||
$("#palette-module-install-confirm-body").html(RED._("palette.editor.confirm.install.body"));
|
||||
$(".palette-module-install-confirm-button-install").show();
|
||||
$(".palette-module-install-confirm-button-remove").hide();
|
||||
$(".palette-module-install-confirm-button-update").hide();
|
||||
$("#palette-module-install-confirm")
|
||||
.dialog('option', 'title', RED._("palette.editor.confirm.install.title"))
|
||||
.dialog('open');
|
||||
@@ -780,8 +823,9 @@ RED.palette.editor = (function() {
|
||||
class: "primary palette-module-install-confirm-button-install",
|
||||
click: function() {
|
||||
var id = $(this).data('module');
|
||||
var version = $(this).data('version');
|
||||
var shade = $(this).data('shade');
|
||||
installNodeModule(id,shade,function(xhr) {
|
||||
installNodeModule(id,version,shade,function(xhr) {
|
||||
if (xhr) {
|
||||
if (xhr.responseJSON) {
|
||||
RED.notify(RED._('palette.editor.errors.installFailed',{module: id,message:xhr.responseJSON.message}));
|
||||
@@ -807,12 +851,34 @@ RED.palette.editor = (function() {
|
||||
}
|
||||
})
|
||||
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
text: RED._("palette.editor.confirm.button.update"),
|
||||
class: "primary palette-module-install-confirm-button-update",
|
||||
click: function() {
|
||||
var id = $(this).data('module');
|
||||
var version = $(this).data('version');
|
||||
var shade = $(this).data('shade');
|
||||
shade.show();
|
||||
installNodeModule(id,version,shade,function(xhr) {
|
||||
if (xhr) {
|
||||
if (xhr.responseJSON) {
|
||||
RED.notify(RED._('palette.editor.errors.updateFailed',{module: id,message:xhr.responseJSON.message}));
|
||||
}
|
||||
}
|
||||
});
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
RED.events.on('registry:module-updated', function(ns) {
|
||||
refreshNodeModule(ns.module);
|
||||
});
|
||||
RED.events.on('registry:node-set-enabled', function(ns) {
|
||||
refreshNodeModule(ns.module);
|
||||
});
|
||||
|
||||
@@ -149,14 +149,9 @@ RED.palette = (function() {
|
||||
|
||||
|
||||
if (def.icon) {
|
||||
var icon_url = "arrow-in.png";
|
||||
try {
|
||||
icon_url = (typeof def.icon === "function" ? def.icon.call({}) : def.icon);
|
||||
} catch(err) {
|
||||
console.log("Definition error: "+nt+".icon",err);
|
||||
}
|
||||
var icon_url = RED.utils.getNodeIcon(def);
|
||||
var iconContainer = $('<div/>',{class:"palette_icon_container"+(def.align=="right"?" palette_icon_container_right":"")}).appendTo(d);
|
||||
$('<div/>',{class:"palette_icon",style:"background-image: url(icons/"+icon_url+")"}).appendTo(iconContainer);
|
||||
$('<div/>',{class:"palette_icon",style:"background-image: url("+icon_url+")"}).appendTo(iconContainer);
|
||||
}
|
||||
|
||||
d.style.backgroundColor = def.color;
|
||||
@@ -236,7 +231,7 @@ RED.palette = (function() {
|
||||
// it here makes me sad
|
||||
//console.log(ui.helper.position());
|
||||
ui.position.left += 17.5;
|
||||
|
||||
|
||||
if (def.inputs > 0 && def.outputs > 0) {
|
||||
mouseX = ui.position.left+(ui.helper.width()/2) - chartOffset.left + chart.scrollLeft();
|
||||
mouseY = ui.position.top+(ui.helper.height()/2) - chartOffset.top + chart.scrollTop();
|
||||
|
||||
@@ -27,19 +27,11 @@ RED.search = (function() {
|
||||
var results = [];
|
||||
|
||||
function indexNode(n) {
|
||||
var l = "";
|
||||
if (n._def && n._def.label) {
|
||||
l = n._def.label;
|
||||
try {
|
||||
l = (typeof l === "function" ? l.call(n) : l);
|
||||
if (l) {
|
||||
l = (""+l).toLowerCase();
|
||||
index[l] = index[l] || {};
|
||||
index[l][n.id] = {node:n,label:l}
|
||||
}
|
||||
} catch(err) {
|
||||
console.log("Definition error: "+n.type+".label",err);
|
||||
}
|
||||
var l = RED.utils.getNodeLabel(n);
|
||||
if (l) {
|
||||
l = (""+l).toLowerCase();
|
||||
index[l] = index[l] || {};
|
||||
index[l][n.id] = {node:n,label:l}
|
||||
}
|
||||
l = l||n.label||n.name||n.id||"";
|
||||
|
||||
@@ -189,25 +181,14 @@ RED.search = (function() {
|
||||
|
||||
var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).appendTo(div);
|
||||
var colour = def.color;
|
||||
var icon_url = "arrow-in.png";
|
||||
var icon_url = RED.utils.getNodeIcon(def,node);
|
||||
if (node.type === 'tab') {
|
||||
colour = "#C0DEED";
|
||||
icon_url = "subflow.png";
|
||||
} else if (def.category === 'config') {
|
||||
icon_url = "cog.png";
|
||||
} else if (node.type === 'unknown') {
|
||||
icon_url = "alert.png";
|
||||
} else {
|
||||
try {
|
||||
icon_url = (typeof def.icon === "function" ? def.icon.call({}) : def.icon);
|
||||
} catch(err) {
|
||||
console.log("Definition error: "+nt+".icon",err);
|
||||
}
|
||||
}
|
||||
nodeDiv.css('backgroundColor',colour);
|
||||
|
||||
var iconContainer = $('<div/>',{class:"palette_icon_container"}).appendTo(nodeDiv);
|
||||
$('<div/>',{class:"palette_icon",style:"background-image: url(icons/"+icon_url+")"}).appendTo(iconContainer);
|
||||
$('<div/>',{class:"palette_icon",style:"background-image: url("+icon_url+")"}).appendTo(iconContainer);
|
||||
|
||||
var contentDiv = $('<div>',{class:"red-ui-search-result-description"}).appendTo(div);
|
||||
if (node.z) {
|
||||
|
||||
@@ -131,19 +131,7 @@ RED.sidebar.config = (function() {
|
||||
} else {
|
||||
var currentType = "";
|
||||
nodes.forEach(function(node) {
|
||||
var label = "";
|
||||
if (typeof node._def.label == "function") {
|
||||
try {
|
||||
label = node._def.label.call(node);
|
||||
} catch(err) {
|
||||
console.log("Definition error: "+node._def.type+".label",err);
|
||||
label = node._def.type;
|
||||
}
|
||||
|
||||
} else {
|
||||
label = node._def.label;
|
||||
}
|
||||
label = label || node.id;
|
||||
var label = RED.utils.getNodeLabel(node,node.id);
|
||||
if (node.type != currentType) {
|
||||
$('<li class="config_node_type">'+node.type+'</li>').appendTo(list);
|
||||
currentType = node.type;
|
||||
|
||||
@@ -189,7 +189,7 @@ RED.tray = (function() {
|
||||
if (stack.length > 0) {
|
||||
var tray = stack[stack.length-1];
|
||||
var trayHeight = tray.tray.height()-tray.header.outerHeight()-tray.footer.outerHeight();
|
||||
tray.body.height(trayHeight-40);
|
||||
tray.body.height(trayHeight);
|
||||
if (tray.width > $("#editor-stack").position().left-8) {
|
||||
tray.width = $("#editor-stack").position().left-8;
|
||||
tray.tray.width(tray.width);
|
||||
@@ -200,7 +200,7 @@ RED.tray = (function() {
|
||||
// tray.body.parent().width(tray.width);
|
||||
}
|
||||
if (tray.options.resize) {
|
||||
tray.options.resize({width:tray.width});
|
||||
tray.options.resize({width:tray.width, height:trayHeight});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,20 +110,11 @@ RED.typeSearch = (function() {
|
||||
|
||||
var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).appendTo(div);
|
||||
var colour = def.color;
|
||||
var icon_url = "arrow-in.png";
|
||||
if (def.category === 'config') {
|
||||
icon_url = "cog.png";
|
||||
} else {
|
||||
try {
|
||||
icon_url = (typeof def.icon === "function" ? def.icon.call({}) : def.icon);
|
||||
} catch(err) {
|
||||
console.log("Definition error: "+object.type+".icon",err);
|
||||
}
|
||||
}
|
||||
var icon_url = RED.utils.getNodeIcon(def);
|
||||
nodeDiv.css('backgroundColor',colour);
|
||||
|
||||
var iconContainer = $('<div/>',{class:"palette_icon_container"}).appendTo(nodeDiv);
|
||||
$('<div/>',{class:"palette_icon",style:"background-image: url(icons/"+icon_url+")"}).appendTo(iconContainer);
|
||||
$('<div/>',{class:"palette_icon",style:"background-image: url("+icon_url+")"}).appendTo(iconContainer);
|
||||
|
||||
if (def.inputs > 0) {
|
||||
$('<div/>',{class:"red-ui-search-result-node-port"}).appendTo(nodeDiv);
|
||||
|
||||
@@ -146,6 +146,9 @@ RED.utils = (function() {
|
||||
if (originalLength === undefined) {
|
||||
originalLength = data.length;
|
||||
}
|
||||
if (data.__encoded__) {
|
||||
data = data.data;
|
||||
}
|
||||
type = obj.type.toLowerCase();
|
||||
} else if (/buffer/.test(typeHint)) {
|
||||
type = 'buffer';
|
||||
@@ -364,8 +367,44 @@ RED.utils = (function() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function getNodeIcon(def,node) {
|
||||
if (def.category === 'config') {
|
||||
return "icons/node-red/cog.png"
|
||||
} else if (node && node.type === 'tab') {
|
||||
return "icons/node-red/subflow.png"
|
||||
} else if (node && node.type === 'unknown') {
|
||||
return "icons/node-red/alert.png"
|
||||
}
|
||||
var icon_url;
|
||||
if (typeof def.icon === "function") {
|
||||
try {
|
||||
icon_url = def.icon.call(node);
|
||||
} catch(err) {
|
||||
console.log("Definition error: "+def.type+".icon",err);
|
||||
icon_url = "arrow-in.png";
|
||||
}
|
||||
} else {
|
||||
icon_url = def.icon;
|
||||
}
|
||||
return "icons/"+def.set.module+"/"+icon_url;
|
||||
}
|
||||
|
||||
function getNodeLabel(node,defaultLabel) {
|
||||
defaultLabel = defaultLabel||"";
|
||||
var l = node._def.label;
|
||||
try {
|
||||
l = (typeof l === "function" ? l.call(node) : l)||defaultLabel;
|
||||
} catch(err) {
|
||||
console.log("Definition error: "+node.type+".label",err);
|
||||
l = defaultLabel;
|
||||
}
|
||||
return RED.text.bidi.enforceTextDirectionWithUCC(l);
|
||||
}
|
||||
|
||||
return {
|
||||
createObjectElement: buildMessageElement,
|
||||
validatePropertyExpression: validatePropertyExpression
|
||||
validatePropertyExpression: validatePropertyExpression,
|
||||
getNodeIcon: getNodeIcon,
|
||||
getNodeLabel: getNodeLabel
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -70,6 +70,9 @@ RED.view = (function() {
|
||||
"grey": "#d3d3d3"
|
||||
}
|
||||
|
||||
var PORT_TYPE_INPUT = 1;
|
||||
var PORT_TYPE_OUTPUT = 0;
|
||||
|
||||
var outer = d3.select("#chart")
|
||||
.append("svg:svg")
|
||||
.attr("width", space_width)
|
||||
@@ -84,6 +87,7 @@ RED.view = (function() {
|
||||
.append("svg:g")
|
||||
.on("dblclick.zoom", null)
|
||||
.append("svg:g")
|
||||
.attr('class','innerCanvas')
|
||||
.on("mousemove", canvasMouseMove)
|
||||
.on("mousedown", canvasMouseDown)
|
||||
.on("mouseup", canvasMouseUp)
|
||||
@@ -384,6 +388,12 @@ RED.view = (function() {
|
||||
}
|
||||
}
|
||||
});
|
||||
$("#chart").focus(function() {
|
||||
$("#workspace-tabs").addClass("workspace-focussed")
|
||||
});
|
||||
$("#chart").blur(function() {
|
||||
$("#workspace-tabs").removeClass("workspace-focussed")
|
||||
});
|
||||
|
||||
RED.actions.add("core:copy-selection-to-internal-clipboard",copySelection);
|
||||
RED.actions.add("core:cut-selection-to-internal-clipboard",function(){copySelection();deleteSelection();});
|
||||
@@ -542,11 +552,11 @@ RED.view = (function() {
|
||||
var drag_line = drag_lines[0];
|
||||
var src = null,dst,src_port;
|
||||
|
||||
if (drag_line.portType === 0 && nn.inputs > 0) {
|
||||
if (drag_line.portType === PORT_TYPE_OUTPUT && nn.inputs > 0) {
|
||||
src = drag_line.node;
|
||||
src_port = drag_line.port;
|
||||
dst = nn;
|
||||
} else if (drag_line.portType === 1 && nn.outputs > 0) {
|
||||
} else if (drag_line.portType === PORT_TYPE_INPUT && nn.outputs > 0) {
|
||||
src = nn;
|
||||
dst = drag_line.node;
|
||||
src_port = 0;
|
||||
@@ -556,10 +566,10 @@ RED.view = (function() {
|
||||
RED.nodes.addLink(link);
|
||||
historyEvent.links = [link];
|
||||
hideDragLines();
|
||||
if (drag_line.portType === 0 && nn.outputs > 0) {
|
||||
showDragLines([{node:nn,port:0,portType:0}]);
|
||||
} else if (drag_line.portType === 1 && nn.inputs > 0) {
|
||||
showDragLines([{node:nn,port:0,portType:1}]);
|
||||
if (drag_line.portType === PORT_TYPE_OUTPUT && nn.outputs > 0) {
|
||||
showDragLines([{node:nn,port:0,portType:PORT_TYPE_OUTPUT}]);
|
||||
} else if (drag_line.portType === PORT_TYPE_INPUT && nn.inputs > 0) {
|
||||
showDragLines([{node:nn,port:0,portType:PORT_TYPE_INPUT}]);
|
||||
} else {
|
||||
resetMouseVars();
|
||||
}
|
||||
@@ -569,9 +579,9 @@ RED.view = (function() {
|
||||
}
|
||||
} else {
|
||||
if (nn.outputs > 0) {
|
||||
showDragLines([{node:nn,port:0,portType:0}]);
|
||||
showDragLines([{node:nn,port:0,portType:PORT_TYPE_OUTPUT}]);
|
||||
} else if (nn.inputs > 0) {
|
||||
showDragLines([{node:nn,port:0,portType:1}]);
|
||||
showDragLines([{node:nn,port:0,portType:PORT_TYPE_INPUT}]);
|
||||
} else {
|
||||
resetMouseVars();
|
||||
}
|
||||
@@ -671,18 +681,18 @@ RED.view = (function() {
|
||||
var links = [];
|
||||
var existingLinks = [];
|
||||
if (selected_link &&
|
||||
((mousedown_port_type === 0 &&
|
||||
((mousedown_port_type === PORT_TYPE_OUTPUT &&
|
||||
selected_link.source === mousedown_node &&
|
||||
selected_link.sourcePort === mousedown_port_index
|
||||
) ||
|
||||
(mousedown_port_type === 1 &&
|
||||
(mousedown_port_type === PORT_TYPE_INPUT &&
|
||||
selected_link.target === mousedown_node
|
||||
))
|
||||
) {
|
||||
existingLinks = [selected_link];
|
||||
} else {
|
||||
var filter;
|
||||
if (mousedown_port_type === 0) {
|
||||
if (mousedown_port_type === PORT_TYPE_OUTPUT) {
|
||||
filter = {
|
||||
source:mousedown_node,
|
||||
sourcePort: mousedown_port_index
|
||||
@@ -699,9 +709,9 @@ RED.view = (function() {
|
||||
RED.nodes.removeLink(link);
|
||||
links.push({
|
||||
link:link,
|
||||
node: (mousedown_port_type===0)?link.target:link.source,
|
||||
port: (mousedown_port_type===0)?0:link.sourcePort,
|
||||
portType: (mousedown_port_type===0)?1:0
|
||||
node: (mousedown_port_type===PORT_TYPE_OUTPUT)?link.target:link.source,
|
||||
port: (mousedown_port_type===PORT_TYPE_OUTPUT)?0:link.sourcePort,
|
||||
portType: (mousedown_port_type===PORT_TYPE_OUTPUT)?PORT_TYPE_INPUT:PORT_TYPE_OUTPUT
|
||||
})
|
||||
}
|
||||
if (links.length === 0) {
|
||||
@@ -722,11 +732,11 @@ RED.view = (function() {
|
||||
mousePos = mouse_position;
|
||||
for (i=0;i<drag_lines.length;i++) {
|
||||
var drag_line = drag_lines[i];
|
||||
var numOutputs = (drag_line.portType === 0)?(drag_line.node.outputs || 1):1;
|
||||
var numOutputs = (drag_line.portType === PORT_TYPE_OUTPUT)?(drag_line.node.outputs || 1):1;
|
||||
var sourcePort = drag_line.port;
|
||||
var portY = -((numOutputs-1)/2)*13 +13*sourcePort;
|
||||
|
||||
var sc = (drag_line.portType === 0)?1:-1;
|
||||
var sc = (drag_line.portType === PORT_TYPE_OUTPUT)?1:-1;
|
||||
|
||||
var dy = mousePos[1]-(drag_line.node.y+portY);
|
||||
var dx = mousePos[0]-(drag_line.node.x+sc*drag_line.node.w/2);
|
||||
@@ -1293,6 +1303,10 @@ RED.view = (function() {
|
||||
|
||||
|
||||
function calculateTextWidth(str, className, offset) {
|
||||
return calculateTextDimensions(str,className,offset,0)[0];
|
||||
}
|
||||
|
||||
function calculateTextDimensions(str,className,offsetW,offsetH) {
|
||||
var sp = document.createElement("span");
|
||||
sp.className = className;
|
||||
sp.style.position = "absolute";
|
||||
@@ -1300,8 +1314,9 @@ RED.view = (function() {
|
||||
sp.innerHTML = (str||"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
|
||||
document.body.appendChild(sp);
|
||||
var w = sp.offsetWidth;
|
||||
var h = sp.offsetHeight;
|
||||
document.body.removeChild(sp);
|
||||
return offset+w;
|
||||
return [offsetW+w,offsetH+h];
|
||||
}
|
||||
|
||||
function resetMouseVars() {
|
||||
@@ -1309,7 +1324,7 @@ RED.view = (function() {
|
||||
mouseup_node = null;
|
||||
mousedown_link = null;
|
||||
mouse_mode = 0;
|
||||
mousedown_port_type = 0;
|
||||
mousedown_port_type = PORT_TYPE_OUTPUT;
|
||||
activeSpliceLink = null;
|
||||
spliceActive = false;
|
||||
d3.select(".link_splice").classed("link_splice",false);
|
||||
@@ -1366,7 +1381,7 @@ RED.view = (function() {
|
||||
if (n.x-hw<mouse_position[0] && n.x+hw> mouse_position[0] &&
|
||||
n.y-hh<mouse_position[1] && n.y+hh>mouse_position[1]) {
|
||||
mouseup_node = n;
|
||||
portType = mouseup_node.inputs>0?1:0;
|
||||
portType = mouseup_node.inputs>0?PORT_TYPE_INPUT:PORT_TYPE_OUTPUT;
|
||||
portIndex = 0;
|
||||
}
|
||||
}
|
||||
@@ -1386,11 +1401,11 @@ RED.view = (function() {
|
||||
if (portType != drag_lines[i].portType && mouseup_node !== drag_lines[i].node) {
|
||||
var drag_line = drag_lines[i];
|
||||
var src,dst,src_port;
|
||||
if (drag_line.portType === 0) {
|
||||
if (drag_line.portType === PORT_TYPE_OUTPUT) {
|
||||
src = drag_line.node;
|
||||
src_port = drag_line.port;
|
||||
dst = mouseup_node;
|
||||
} else if (drag_line.portType == 1) {
|
||||
} else if (drag_line.portType === PORT_TYPE_INPUT) {
|
||||
src = mouseup_node;
|
||||
dst = drag_line.node;
|
||||
src_port = portIndex;
|
||||
@@ -1427,10 +1442,10 @@ RED.view = (function() {
|
||||
if (mouse_mode === RED.state.QUICK_JOINING) {
|
||||
if (addedLinks.length > 0) {
|
||||
hideDragLines();
|
||||
if (portType === 1 && d.outputs > 0) {
|
||||
showDragLines([{node:d,port:0,portType:0}]);
|
||||
} else if (portType === 0 && d.inputs > 0) {
|
||||
showDragLines([{node:d,port:0,portType:1}]);
|
||||
if (portType === PORT_TYPE_INPUT && d.outputs > 0) {
|
||||
showDragLines([{node:d,port:0,portType:PORT_TYPE_OUTPUT}]);
|
||||
} else if (portType === PORT_TYPE_OUTPUT && d.inputs > 0) {
|
||||
showDragLines([{node:d,port:0,portType:PORT_TYPE_INPUT}]);
|
||||
} else {
|
||||
resetMouseVars();
|
||||
}
|
||||
@@ -1446,6 +1461,106 @@ RED.view = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
var portLabelHoverTimeout = null;
|
||||
var portLabelHover = null;
|
||||
|
||||
|
||||
function getElementPosition(node) {
|
||||
var d3Node = d3.select(node);
|
||||
if (d3Node.attr('class') === 'innerCanvas') {
|
||||
return [0,0];
|
||||
}
|
||||
var result = [];
|
||||
var localPos = [0,0];
|
||||
if (node.nodeName.toLowerCase() === 'g') {
|
||||
var transform = d3Node.attr("transform");
|
||||
if (transform) {
|
||||
localPos = d3.transform(transform).translate;
|
||||
}
|
||||
} else {
|
||||
localPos = [d3Node.attr("x")||0,d3Node.attr("y")||0];
|
||||
}
|
||||
var parentPos = getElementPosition(node.parentNode);
|
||||
return [localPos[0]+parentPos[0],localPos[1]+parentPos[1]]
|
||||
|
||||
}
|
||||
|
||||
function getPortLabel(node,portType,portIndex) {
|
||||
var result;
|
||||
var nodePortLabels = (portType === PORT_TYPE_INPUT)?node.inputLabels:node.outputLabels;
|
||||
if (nodePortLabels && nodePortLabels[portIndex]) {
|
||||
return nodePortLabels[portIndex];
|
||||
}
|
||||
var portLabels = (portType === PORT_TYPE_INPUT)?node._def.inputLabels:node._def.outputLabels;
|
||||
if (typeof portLabels === 'string') {
|
||||
result = portLabels;
|
||||
} else if (typeof portLabels === 'function') {
|
||||
try {
|
||||
result = portLabels.call(node,portIndex);
|
||||
} catch(err) {
|
||||
console.log("Definition error: "+node.type+"."+((portType === PORT_TYPE_INPUT)?"inputLabels":"outputLabels"),err);
|
||||
result = null;
|
||||
}
|
||||
} else if ($.isArray(portLabels)) {
|
||||
result = portLabels[portIndex];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function portMouseOver(port,d,portType,portIndex) {
|
||||
clearTimeout(portLabelHoverTimeout);
|
||||
var active = (mouse_mode!=RED.state.JOINING || (drag_lines.length > 0 && drag_lines[0].portType !== portType));
|
||||
if (active && ((portType === PORT_TYPE_INPUT && (d._def.inputLabels||d.inputLabels)) || (portType === PORT_TYPE_OUTPUT && (d._def.outputLabels||d.outputLabels)))) {
|
||||
portLabelHoverTimeout = setTimeout(function() {
|
||||
var tooltip = getPortLabel(d,portType,portIndex);
|
||||
if (!tooltip) {
|
||||
return;
|
||||
}
|
||||
var pos = getElementPosition(port.node());
|
||||
portLabelHoverTimeout = null;
|
||||
portLabelHover = vis.append("g")
|
||||
.attr("transform","translate("+(pos[0]+(portType===PORT_TYPE_INPUT?-2:12))+","+(pos[1]+5)+")")
|
||||
.attr("class","port_tooltip");
|
||||
var lines = tooltip.split("\n");
|
||||
var labelWidth = 0;
|
||||
var labelHeight = 4;
|
||||
var labelHeights = [];
|
||||
lines.forEach(function(l) {
|
||||
var labelDimensions = calculateTextDimensions(l, "port_tooltip_label", 8,0);
|
||||
labelWidth = Math.max(labelWidth,labelDimensions[0]);
|
||||
labelHeights.push(0.8*labelDimensions[1]);
|
||||
labelHeight += 0.8*labelDimensions[1];
|
||||
});
|
||||
|
||||
var labelHeight1 = (labelHeight/2)-5-2;
|
||||
var labelHeight2 = labelHeight - 4;
|
||||
portLabelHover.append("path").attr("d",
|
||||
portType===PORT_TYPE_INPUT?
|
||||
"M0 0 l -5 -5 v -"+(labelHeight1)+" q 0 -2 -2 -2 h -"+labelWidth+" q -2 0 -2 2 v "+(labelHeight2)+" q 0 2 2 2 h "+labelWidth+" q 2 0 2 -2 v -"+(labelHeight1)+" l 5 -5"
|
||||
:
|
||||
"M0 0 l 5 -5 v -"+(labelHeight1)+" q 0 -2 2 -2 h "+labelWidth+" q 2 0 2 2 v "+(labelHeight2)+" q 0 2 -2 2 h -"+labelWidth+" q -2 0 -2 -2 v -"+(labelHeight1)+" l -5 -5"
|
||||
);
|
||||
var y = -labelHeight/2-2;
|
||||
lines.forEach(function(l,i) {
|
||||
y += labelHeights[i];
|
||||
portLabelHover.append("svg:text").attr("class","port_tooltip_label")
|
||||
.attr("x", portType===PORT_TYPE_INPUT?-10:10)
|
||||
.attr("y", y)
|
||||
.attr("text-anchor",portType===PORT_TYPE_INPUT?"end":"start")
|
||||
.text(l)
|
||||
});
|
||||
},500);
|
||||
}
|
||||
port.classed("port_hovered",active);
|
||||
}
|
||||
function portMouseOut(port,d,portType,portIndex) {
|
||||
clearTimeout(portLabelHoverTimeout);
|
||||
if (portLabelHover) {
|
||||
portLabelHover.remove();
|
||||
portLabelHover = null;
|
||||
}
|
||||
port.classed("port_hovered",false);
|
||||
}
|
||||
|
||||
function nodeMouseUp(d) {
|
||||
if (dblClickPrimed && mousedown_node == d && clickElapsed > 0 && clickElapsed < 750) {
|
||||
mouse_mode = RED.state.DEFAULT;
|
||||
@@ -1640,13 +1755,13 @@ RED.view = (function() {
|
||||
nodeMouseUp.call(this,d);
|
||||
});
|
||||
|
||||
outGroup.append("rect").attr("class","port").attr("rx",3).attr("ry",3).attr("width",10).attr("height",10).attr("x",-5).attr("y",15)
|
||||
.on("mousedown", function(d,i){portMouseDown(d,1,0);} )
|
||||
.on("touchstart", function(d,i){portMouseDown(d,1,0);} )
|
||||
.on("mouseup", function(d,i){portMouseUp(d,1,0);})
|
||||
.on("touchend",function(d,i){portMouseUp(d,1,0);} )
|
||||
.on("mouseover",function(d,i) { var port = d3.select(this); port.classed("port_hovered",(mouse_mode!=RED.state.JOINING || (drag_lines.length > 0 && drag_lines[0].portType !== 1)));})
|
||||
.on("mouseout",function(d,i) { var port = d3.select(this); port.classed("port_hovered",false);});
|
||||
outGroup.append("g").attr('transform','translate(-5,15)').append("rect").attr("class","port").attr("rx",3).attr("ry",3).attr("width",10).attr("height",10)
|
||||
.on("mousedown", function(d,i){portMouseDown(d,PORT_TYPE_INPUT,0);} )
|
||||
.on("touchstart", function(d,i){portMouseDown(d,PORT_TYPE_INPUT,0);} )
|
||||
.on("mouseup", function(d,i){portMouseUp(d,PORT_TYPE_INPUT,0);})
|
||||
.on("touchend",function(d,i){portMouseUp(d,PORT_TYPE_INPUT,0);} )
|
||||
.on("mouseover",function(d){portMouseOver(d3.select(this),d,PORT_TYPE_INPUT,0);})
|
||||
.on("mouseout",function(d){portMouseOut(d3.select(this),d,PORT_TYPE_INPUT,0);});
|
||||
|
||||
outGroup.append("svg:text").attr("class","port_label").attr("x",20).attr("y",8).style("font-size","10px").text("output");
|
||||
outGroup.append("svg:text").attr("class","port_label port_index").attr("x",20).attr("y",24).text(function(d,i){ return i+1});
|
||||
@@ -1683,13 +1798,15 @@ RED.view = (function() {
|
||||
nodeMouseUp.call(this,d);
|
||||
});
|
||||
|
||||
inGroup.append("rect").attr("class","port").attr("rx",3).attr("ry",3).attr("width",10).attr("height",10).attr("x",35).attr("y",15)
|
||||
.on("mousedown", function(d,i){portMouseDown(d,0,i);} )
|
||||
.on("touchstart", function(d,i){portMouseDown(d,0,i);} )
|
||||
.on("mouseup", function(d,i){portMouseUp(d,0,i);})
|
||||
.on("touchend",function(d,i){portMouseUp(d,0,i);} )
|
||||
.on("mouseover",function(d,i) { var port = d3.select(this); port.classed("port_hovered",(mouse_mode!=RED.state.JOINING || (drag_lines.length > 0 && drag_lines[0].portType !== 0) ));})
|
||||
.on("mouseout",function(d,i) { var port = d3.select(this); port.classed("port_hovered",false);});
|
||||
inGroup.append("g").attr('transform','translate(35,15)').append("rect").attr("class","port").attr("rx",3).attr("ry",3).attr("width",10).attr("height",10)
|
||||
.on("mousedown", function(d,i){portMouseDown(d,PORT_TYPE_OUTPUT,i);} )
|
||||
.on("touchstart", function(d,i){portMouseDown(d,PORT_TYPE_OUTPUT,i);} )
|
||||
.on("mouseup", function(d,i){portMouseUp(d,PORT_TYPE_OUTPUT,i);})
|
||||
.on("touchend",function(d,i){portMouseUp(d,PORT_TYPE_OUTPUT,i);} )
|
||||
.on("mouseover",function(d){portMouseOver(d3.select(this),d,PORT_TYPE_OUTPUT,0);})
|
||||
.on("mouseout",function(d) {portMouseOut(d3.select(this),d,PORT_TYPE_OUTPUT,0);});
|
||||
|
||||
|
||||
inGroup.append("svg:text").attr("class","port_label").attr("x",18).attr("y",20).style("font-size","10px").text("input");
|
||||
|
||||
|
||||
@@ -1730,14 +1847,7 @@ RED.view = (function() {
|
||||
var node = d3.select(this);
|
||||
var isLink = d.type === "link in" || d.type === "link out";
|
||||
node.attr("id",d.id);
|
||||
var l = d._def.label;
|
||||
try {
|
||||
l = (typeof l === "function" ? l.call(d) : l)||"";
|
||||
} catch(err) {
|
||||
console.log("Definition error: "+d.type+".label",err);
|
||||
l = d.type;
|
||||
}
|
||||
|
||||
var l = RED.utils.getNodeLabel(d);
|
||||
if (isLink) {
|
||||
d.w = node_height;
|
||||
} else {
|
||||
@@ -1832,7 +1942,7 @@ RED.view = (function() {
|
||||
//node.append("rect").attr("class", "node-gradient-bottom").attr("rx", 6).attr("ry", 6).attr("height",30).attr("stroke","none").attr("fill","url(#gradient-bottom)").style("pointer-events","none");
|
||||
|
||||
if (d._def.icon) {
|
||||
|
||||
var icon_url = RED.utils.getNodeIcon(d._def,d);
|
||||
var icon_group = node.append("g")
|
||||
.attr("class","node_icon_group")
|
||||
.attr("x",0).attr("y",0);
|
||||
@@ -1847,7 +1957,7 @@ RED.view = (function() {
|
||||
.attr("height",function(d){return Math.min(50,d.h-4);});
|
||||
|
||||
var icon = icon_group.append("image")
|
||||
.attr("xlink:href","icons/"+d._def.icon)
|
||||
.attr("xlink:href",icon_url)
|
||||
.attr("class","node_icon")
|
||||
.attr("x",0)
|
||||
.attr("width","30")
|
||||
@@ -1878,7 +1988,7 @@ RED.view = (function() {
|
||||
//}
|
||||
|
||||
var img = new Image();
|
||||
img.src = "icons/"+d._def.icon;
|
||||
img.src = icon_url;
|
||||
img.onload = function() {
|
||||
icon.attr("width",Math.min(img.width,30));
|
||||
icon.attr("height",Math.min(img.height,30));
|
||||
@@ -1915,8 +2025,10 @@ RED.view = (function() {
|
||||
//node.append("circle").attr({"class":"centerDot","cx":0,"cy":0,"r":5});
|
||||
|
||||
//node.append("path").attr("class","node_error").attr("d","M 3,-3 l 10,0 l -5,-8 z");
|
||||
node.append("image").attr("class","node_error hidden").attr("xlink:href","icons/node-error.png").attr("x",0).attr("y",-6).attr("width",10).attr("height",9);
|
||||
node.append("image").attr("class","node_changed hidden").attr("xlink:href","icons/node-changed.png").attr("x",12).attr("y",-6).attr("width",10).attr("height",10);
|
||||
|
||||
//TODO: these ought to be SVG
|
||||
node.append("image").attr("class","node_error hidden").attr("xlink:href","icons/node-red/node-error.png").attr("x",0).attr("y",-6).attr("width",10).attr("height",9);
|
||||
node.append("image").attr("class","node_changed hidden").attr("xlink:href","icons/node-red/node-changed.png").attr("x",12).attr("y",-6).attr("width",10).attr("height",10);
|
||||
});
|
||||
|
||||
node.each(function(d,i) {
|
||||
@@ -1925,13 +2037,7 @@ RED.view = (function() {
|
||||
dirtyNodes[d.id] = d;
|
||||
//if (d.x < -50) deleteSelection(); // Delete nodes if dragged back to palette
|
||||
if (!isLink && d.resize) {
|
||||
var l = d._def.label;
|
||||
try {
|
||||
l = (typeof l === "function" ? l.call(d) : l)||"";
|
||||
} catch(err) {
|
||||
console.log("Definition error: "+d.type+".label",err);
|
||||
l = d.type;
|
||||
}
|
||||
var l = RED.utils.getNodeLabel(d);
|
||||
var ow = d.w;
|
||||
d.w = Math.max(node_width,gridSize*(Math.ceil((calculateTextWidth(l, "node_label", 50)+(d._def.inputs>0?7:0))/gridSize)) );
|
||||
d.h = Math.max(node_height,(d.outputs||0) * 15);
|
||||
@@ -1965,12 +2071,12 @@ RED.view = (function() {
|
||||
} else if (d.inputs === 1 && inputPorts.empty()) {
|
||||
var inputGroup = thisNode.append("g").attr("class","port_input");
|
||||
inputGroup.append("rect").attr("class","port").attr("rx",3).attr("ry",3).attr("width",10).attr("height",10)
|
||||
.on("mousedown",function(d){portMouseDown(d,1,0);})
|
||||
.on("touchstart",function(d){portMouseDown(d,1,0);})
|
||||
.on("mouseup",function(d){portMouseUp(d,1,0);} )
|
||||
.on("touchend",function(d){portMouseUp(d,1,0);} )
|
||||
.on("mouseover",function(d) { var port = d3.select(this); port.classed("port_hovered",(mouse_mode!=RED.state.JOINING || (drag_lines.length > 0 && drag_lines[0].portType !== 1) ));})
|
||||
.on("mouseout",function(d) { var port = d3.select(this); port.classed("port_hovered",false);})
|
||||
.on("mousedown",function(d){portMouseDown(d,PORT_TYPE_INPUT,0);})
|
||||
.on("touchstart",function(d){portMouseDown(d,PORT_TYPE_INPUT,0);})
|
||||
.on("mouseup",function(d){portMouseUp(d,PORT_TYPE_INPUT,0);} )
|
||||
.on("touchend",function(d){portMouseUp(d,PORT_TYPE_INPUT,0);} )
|
||||
.on("mouseover",function(d){portMouseOver(d3.select(this),d,PORT_TYPE_INPUT,0);})
|
||||
.on("mouseout",function(d) {portMouseOut(d3.select(this),d,PORT_TYPE_INPUT,0);});
|
||||
}
|
||||
|
||||
var numOutputs = d.outputs;
|
||||
@@ -1980,12 +2086,12 @@ RED.view = (function() {
|
||||
var output_group = d._ports.enter().append("g").attr("class","port_output");
|
||||
|
||||
output_group.append("rect").attr("class","port").attr("rx",3).attr("ry",3).attr("width",10).attr("height",10)
|
||||
.on("mousedown",(function(){var node = d; return function(d,i){portMouseDown(node,0,i);}})() )
|
||||
.on("touchstart",(function(){var node = d; return function(d,i){portMouseDown(node,0,i);}})() )
|
||||
.on("mouseup",(function(){var node = d; return function(d,i){portMouseUp(node,0,i);}})() )
|
||||
.on("touchend",(function(){var node = d; return function(d,i){portMouseUp(node,0,i);}})() )
|
||||
.on("mouseover",function(d,i) { var port = d3.select(this); port.classed("port_hovered",(mouse_mode!=RED.state.JOINING || (drag_lines.length > 0 && drag_lines[0].portType !== 0) ));})
|
||||
.on("mouseout",function(d,i) { var port = d3.select(this); port.classed("port_hovered",false);});
|
||||
.on("mousedown",(function(){var node = d; return function(d,i){portMouseDown(node,PORT_TYPE_OUTPUT,i);}})() )
|
||||
.on("touchstart",(function(){var node = d; return function(d,i){portMouseDown(node,PORT_TYPE_OUTPUT,i);}})() )
|
||||
.on("mouseup",(function(){var node = d; return function(d,i){portMouseUp(node,PORT_TYPE_OUTPUT,i);}})() )
|
||||
.on("touchend",(function(){var node = d; return function(d,i){portMouseUp(node,PORT_TYPE_OUTPUT,i);}})() )
|
||||
.on("mouseover",(function(){var node = d; return function(d,i){portMouseOver(d3.select(this),node,PORT_TYPE_OUTPUT,i);}})())
|
||||
.on("mouseout",(function(){var node = d; return function(d,i) {portMouseOut(d3.select(this),node,PORT_TYPE_OUTPUT,i);}})());
|
||||
|
||||
d._ports.exit().remove();
|
||||
if (d._ports) {
|
||||
@@ -2032,21 +2138,11 @@ RED.view = (function() {
|
||||
if (d._def.icon) {
|
||||
icon = thisNode.select(".node_icon");
|
||||
var current_url = icon.attr("xlink:href");
|
||||
var icon_url;
|
||||
if (typeof d._def.icon == "function") {
|
||||
try {
|
||||
icon_url = d._def.icon.call(d);
|
||||
} catch(err) {
|
||||
console.log("icon",err);
|
||||
icon_url = "arrow-in.png";
|
||||
}
|
||||
} else {
|
||||
icon_url = d._def.icon;
|
||||
}
|
||||
if ("icons/"+icon_url != current_url) {
|
||||
icon.attr("xlink:href","icons/"+icon_url);
|
||||
var new_url = RED.utils.getNodeIcon(d._def,d);
|
||||
if (new_url !== current_url) {
|
||||
icon.attr("xlink:href",new_url);
|
||||
var img = new Image();
|
||||
img.src = "icons/"+d._def.icon;
|
||||
img.src = new_url;
|
||||
img.onload = function() {
|
||||
icon.attr("width",Math.min(img.width,30));
|
||||
icon.attr("height",Math.min(img.height,30));
|
||||
|
||||
@@ -39,6 +39,7 @@ RED.workspaces = (function() {
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
}
|
||||
RED.view.focus();
|
||||
return ws;
|
||||
}
|
||||
function deleteWorkspace(ws) {
|
||||
@@ -139,6 +140,10 @@ RED.workspaces = (function() {
|
||||
RED.events.emit("workspace:change",event);
|
||||
window.location.hash = 'flow/'+tab.id;
|
||||
RED.sidebar.config.refresh();
|
||||
RED.view.focus();
|
||||
},
|
||||
onclick: function(tab) {
|
||||
RED.view.focus();
|
||||
},
|
||||
ondblclick: function(tab) {
|
||||
if (tab.type != "subflow") {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
RED.validators = {
|
||||
number: function(){return function(v) { return v!=='' && !isNaN(v);}},
|
||||
number: function(blankAllowed){return function(v) { return (blankAllowed&&(v===''||v===undefined)) || (v!=='' && !isNaN(v));}},
|
||||
regex: function(re){return function(v) { return re.test(v);}},
|
||||
typedInput: function(ptypeName,isConfig) { return function(v) {
|
||||
var ptype = $("#node-"+(isConfig?"config-":"")+"input-"+ptypeName).val() || this[ptypeName];
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: none;
|
||||
z-index:100;
|
||||
}
|
||||
#dropTarget div {
|
||||
display: table-cell;
|
||||
@@ -34,4 +35,3 @@
|
||||
#dropTarget div i {
|
||||
font-size: 80px;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,12 @@
|
||||
overflow: auto;
|
||||
}
|
||||
.editor-tray-body {
|
||||
margin: 20px;
|
||||
form {
|
||||
margin: 20px;
|
||||
}
|
||||
}
|
||||
.editor-tray-content {
|
||||
overflow: auto;
|
||||
}
|
||||
.editor-tray-header {
|
||||
@include disable-selection;
|
||||
@@ -65,6 +70,13 @@
|
||||
.editor-tray-footer {
|
||||
@include component-footer;
|
||||
height: 35px;
|
||||
|
||||
button {
|
||||
@include editor-button;
|
||||
padding: 3px 7px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.editor-tray-toolbar {
|
||||
@@ -72,48 +84,9 @@
|
||||
padding: 6px;
|
||||
|
||||
button {
|
||||
@include workspace-button;
|
||||
font-size: 14px;
|
||||
padding: 6px 14px;
|
||||
margin-right: 8px;
|
||||
color: $editor-button-color !important;
|
||||
background: $editor-button-background;
|
||||
|
||||
&.primary {
|
||||
border-color: $editor-button-background-primary;
|
||||
color: $editor-button-color-primary !important;
|
||||
background: $editor-button-background-primary;
|
||||
&.disabled, &.ui-state-disabled {
|
||||
background: none;
|
||||
color: $editor-button-color !important;
|
||||
border-color: $form-input-border-color;
|
||||
}
|
||||
&:not(.disabled):not(.ui-button-disabled):hover {
|
||||
border-color: $editor-button-background-primary-hover;
|
||||
background: $editor-button-background-primary-hover;
|
||||
color: $editor-button-color-primary !important;
|
||||
}
|
||||
}
|
||||
&:not(.disabled):hover {
|
||||
//color: $editor-button-color;
|
||||
}
|
||||
&.disabled {
|
||||
background: none;
|
||||
}
|
||||
&.disabled:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
|
||||
&.leftButton {
|
||||
float: left;
|
||||
margin-top: 1px;
|
||||
}
|
||||
&:not(.leftButton):not(:last-child) {
|
||||
margin-right: 16px;
|
||||
}
|
||||
&.ui-state-disabled {
|
||||
opacity: 1;
|
||||
@include editor-button;
|
||||
&.toggle {
|
||||
@include workspace-button-toggle;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,7 +152,6 @@
|
||||
|
||||
|
||||
.dialog-form,#dialog-form, #dialog-config-form {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -244,17 +216,6 @@
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.dialog-form {
|
||||
.button-group {
|
||||
.editor-button {
|
||||
&:first-child {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#node-config-dialog-scope-container {
|
||||
cursor: auto;
|
||||
float: right;
|
||||
|
||||
@@ -272,3 +272,25 @@ g.link_unknown path.link_line {
|
||||
stroke-width: 2;
|
||||
stroke-dasharray: 10, 4;
|
||||
}
|
||||
|
||||
@keyframes port_tooltip_fadeIn { from { opacity:0; } to { opacity:1; } }
|
||||
|
||||
.port_tooltip {
|
||||
opacity:0;
|
||||
animation: 0.1s ease-in 0s 1 normal forwards port_tooltip_fadeIn;
|
||||
pointer-events: none;
|
||||
|
||||
path {
|
||||
fill: white;
|
||||
stroke: #999;
|
||||
stroke-width: 1;
|
||||
}
|
||||
}
|
||||
.port_tooltip_label {
|
||||
stroke-width: 0;
|
||||
fill: #666;
|
||||
font-size: 12px;
|
||||
pointer-events: none;
|
||||
-webkit-touch-callout: none;
|
||||
@include disable-selection;
|
||||
}
|
||||
|
||||
@@ -108,7 +108,51 @@
|
||||
color: $workspace-button-toggle-color-disabled !important;
|
||||
}
|
||||
}
|
||||
@mixin editor-button {
|
||||
@include workspace-button;
|
||||
font-size: 14px;
|
||||
padding: 6px 14px;
|
||||
margin-right: 8px;
|
||||
color: $editor-button-color !important;
|
||||
background: $editor-button-background;
|
||||
|
||||
&.primary {
|
||||
border-color: $editor-button-background-primary;
|
||||
color: $editor-button-color-primary !important;
|
||||
background: $editor-button-background-primary;
|
||||
&.disabled, &.ui-state-disabled {
|
||||
background: none;
|
||||
color: $editor-button-color !important;
|
||||
border-color: $form-input-border-color;
|
||||
}
|
||||
&:not(.disabled):not(.ui-button-disabled):hover {
|
||||
border-color: $editor-button-background-primary-hover;
|
||||
background: $editor-button-background-primary-hover;
|
||||
color: $editor-button-color-primary !important;
|
||||
}
|
||||
}
|
||||
&:not(.disabled):hover {
|
||||
//color: $editor-button-color;
|
||||
}
|
||||
&.disabled {
|
||||
background: none;
|
||||
}
|
||||
&.disabled:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
|
||||
&.leftButton {
|
||||
float: left;
|
||||
margin-top: 1px;
|
||||
}
|
||||
&:not(.leftButton):not(:last-child) {
|
||||
margin-right: 16px;
|
||||
}
|
||||
&.ui-state-disabled {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin component-footer {
|
||||
border-top: 1px solid $primary-border-color;
|
||||
|
||||
@@ -51,3 +51,10 @@
|
||||
#workspace-footer {
|
||||
@include component-footer;
|
||||
}
|
||||
|
||||
#workspace-tabs:not(.workspace-focussed) {
|
||||
opacity:0.8;
|
||||
li.red-ui-tab.active a {
|
||||
color:#666;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,8 +55,10 @@
|
||||
things like pipe the result to another command.</p>
|
||||
<p>Commands or parameters with spaces should be enclosed in quotes - <i>"This is a single parameter"</i></p>
|
||||
<p>If stdout is binary a <i>buffer</i> is returned - otherwise returns a <i>string</i>.</p>
|
||||
<p>The blue status icon will be visible while the node is active.</p>
|
||||
<p>If running a Python app you may need to use the <code>-u</code> parameter to stop the output being buffered.</p>
|
||||
<p>The blue status icon and PID will be visible while the node is active.</p>
|
||||
<p>Sending <code>msg.kill</code> will kill a single active process. If there is more than one process running then
|
||||
<code>msg.kill</code> must be set with the value of the PID to be killed.</p>
|
||||
<p>Tip: If running a Python app you may need to use the <code>-u</code> parameter to stop the output being buffered.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -73,6 +75,7 @@
|
||||
},
|
||||
inputs:1,
|
||||
outputs:3,
|
||||
outputLabels: ["stdout","stderr","rc"],
|
||||
icon: "arrow-in.png",
|
||||
align: "right",
|
||||
label: function() {
|
||||
|
||||
@@ -38,88 +38,103 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
this.on("input", function(msg) {
|
||||
var child;
|
||||
node.status({fill:"blue",shape:"dot",text:" "});
|
||||
if (this.useSpawn === true) {
|
||||
// make the extra args into an array
|
||||
// then prepend with the msg.payload
|
||||
var arg = node.cmd;
|
||||
if ((node.addpay === true) && msg.hasOwnProperty("payload")) { arg += " "+msg.payload; }
|
||||
if (node.append.trim() !== "") { arg += " "+node.append; }
|
||||
// slice whole line by spaces (trying to honour quotes);
|
||||
arg = arg.match(/(?:[^\s"]+|"[^"]*")+/g);
|
||||
var cmd = arg.shift();
|
||||
if (/^".*"$/.test(cmd)) { cmd = cmd.slice(1,-1); }
|
||||
/* istanbul ignore else */
|
||||
if (RED.settings.verbose) { node.log(cmd+" ["+arg+"]"); }
|
||||
child = spawn(cmd,arg);
|
||||
var unknownCommand = (child.pid === undefined);
|
||||
if (node.timer !== 0) {
|
||||
child.tout = setTimeout(function() { cleanup(child.pid); }, node.timer);
|
||||
if (msg.hasOwnProperty("kill")) {
|
||||
if (node.activeProcesses.hasOwnProperty(msg.kill) ) {
|
||||
node.activeProcesses[msg.kill].kill();
|
||||
node.status({fill:"red",shape:"dot",text:"killed"});
|
||||
}
|
||||
node.activeProcesses[child.pid] = child;
|
||||
child.stdout.on('data', function (data) {
|
||||
if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) {
|
||||
// console.log('[exec] stdout: ' + data,child.pid);
|
||||
if (isUtf8(data)) { msg.payload = data.toString(); }
|
||||
else { msg.payload = data; }
|
||||
node.send([RED.util.cloneMessage(msg),null,null]);
|
||||
else {
|
||||
if (Object.keys(node.activeProcesses).length === 1) {
|
||||
node.activeProcesses[Object.keys(node.activeProcesses)[0]].kill();
|
||||
node.status({fill:"red",shape:"dot",text:"killed"});
|
||||
}
|
||||
});
|
||||
child.stderr.on('data', function (data) {
|
||||
if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) {
|
||||
if (isUtf8(data)) { msg.payload = data.toString(); }
|
||||
else { msg.payload = new Buffer(data); }
|
||||
node.send([null,RED.util.cloneMessage(msg),null]);
|
||||
}
|
||||
});
|
||||
child.on('close', function (code) {
|
||||
if (unknownCommand || (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null)) {
|
||||
delete node.activeProcesses[child.pid];
|
||||
if (child.tout) { clearTimeout(child.tout); }
|
||||
msg.payload = code;
|
||||
if (code === 0) { node.status({}); }
|
||||
if (code === null) { node.status({fill:"red",shape:"dot",text:"timeout"}); }
|
||||
else if (code < 0) { node.status({fill:"red",shape:"dot",text:"rc: "+code}); }
|
||||
else { node.status({fill:"yellow",shape:"dot",text:"rc: "+code}); }
|
||||
node.send([null,null,RED.util.cloneMessage(msg)]);
|
||||
}
|
||||
});
|
||||
child.on('error', function (code) {
|
||||
if (child.tout) { clearTimeout(child.tout); }
|
||||
delete node.activeProcesses[child.pid];
|
||||
if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) {
|
||||
node.error(code,RED.util.cloneMessage(msg));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
var cl = node.cmd;
|
||||
if ((node.addpay === true) && msg.hasOwnProperty("payload")) { cl += " "+msg.payload; }
|
||||
if (node.append.trim() !== "") { cl += " "+node.append; }
|
||||
/* istanbul ignore else */
|
||||
if (RED.settings.verbose) { node.log(cl); }
|
||||
child = exec(cl, {encoding: 'binary', maxBuffer:10000000}, function (error, stdout, stderr) {
|
||||
msg.payload = new Buffer(stdout,"binary");
|
||||
if (isUtf8(msg.payload)) { msg.payload = msg.payload.toString(); }
|
||||
var msg2 = {payload:stderr};
|
||||
var msg3 = null;
|
||||
//console.log('[exec] stdout: ' + stdout);
|
||||
//console.log('[exec] stderr: ' + stderr);
|
||||
if (error !== null) {
|
||||
msg3 = {payload:error};
|
||||
//console.log('[exec] error: ' + error);
|
||||
var child;
|
||||
if (this.useSpawn === true) {
|
||||
// make the extra args into an array
|
||||
// then prepend with the msg.payload
|
||||
var arg = node.cmd;
|
||||
if ((node.addpay === true) && msg.hasOwnProperty("payload")) { arg += " "+msg.payload; }
|
||||
if (node.append.trim() !== "") { arg += " "+node.append; }
|
||||
// slice whole line by spaces (trying to honour quotes);
|
||||
arg = arg.match(/(?:[^\s"]+|"[^"]*")+/g);
|
||||
var cmd = arg.shift();
|
||||
if (/^".*"$/.test(cmd)) { cmd = cmd.slice(1,-1); }
|
||||
/* istanbul ignore else */
|
||||
if (RED.settings.verbose) { node.log(cmd+" ["+arg+"]"); }
|
||||
child = spawn(cmd,arg);
|
||||
node.status({fill:"blue",shape:"dot",text:"pid:"+child.pid});
|
||||
var unknownCommand = (child.pid === undefined);
|
||||
if (node.timer !== 0) {
|
||||
child.tout = setTimeout(function() { cleanup(child.pid); }, node.timer);
|
||||
}
|
||||
node.status({});
|
||||
node.send([msg,msg2,msg3]);
|
||||
if (child.tout) { clearTimeout(child.tout); }
|
||||
delete node.activeProcesses[child.pid];
|
||||
});
|
||||
child.on('error',function() {});
|
||||
if (node.timer !== 0) {
|
||||
child.tout = setTimeout(function() { cleanup(child.pid); }, node.timer);
|
||||
node.activeProcesses[child.pid] = child;
|
||||
child.stdout.on('data', function (data) {
|
||||
if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) {
|
||||
// console.log('[exec] stdout: ' + data,child.pid);
|
||||
if (isUtf8(data)) { msg.payload = data.toString(); }
|
||||
else { msg.payload = data; }
|
||||
node.send([RED.util.cloneMessage(msg),null,null]);
|
||||
}
|
||||
});
|
||||
child.stderr.on('data', function (data) {
|
||||
if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) {
|
||||
if (isUtf8(data)) { msg.payload = data.toString(); }
|
||||
else { msg.payload = new Buffer(data); }
|
||||
node.send([null,RED.util.cloneMessage(msg),null]);
|
||||
}
|
||||
});
|
||||
child.on('close', function (code) {
|
||||
if (unknownCommand || (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null)) {
|
||||
delete node.activeProcesses[child.pid];
|
||||
if (child.tout) { clearTimeout(child.tout); }
|
||||
msg.payload = code;
|
||||
if (code === 0) { node.status({}); }
|
||||
if (code === null) { node.status({fill:"red",shape:"dot",text:"timeout"}); }
|
||||
else if (code < 0) { node.status({fill:"red",shape:"dot",text:"rc:"+code}); }
|
||||
else { node.status({fill:"yellow",shape:"dot",text:"rc:"+code}); }
|
||||
node.send([null,null,RED.util.cloneMessage(msg)]);
|
||||
}
|
||||
});
|
||||
child.on('error', function (code) {
|
||||
if (child.tout) { clearTimeout(child.tout); }
|
||||
delete node.activeProcesses[child.pid];
|
||||
if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) {
|
||||
node.error(code,RED.util.cloneMessage(msg));
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
var cl = node.cmd;
|
||||
if ((node.addpay === true) && msg.hasOwnProperty("payload")) { cl += " "+msg.payload; }
|
||||
if (node.append.trim() !== "") { cl += " "+node.append; }
|
||||
/* istanbul ignore else */
|
||||
if (RED.settings.verbose) { node.log(cl); }
|
||||
child = exec(cl, {encoding: 'binary', maxBuffer:10000000}, function (error, stdout, stderr) {
|
||||
msg.payload = new Buffer(stdout,"binary");
|
||||
if (isUtf8(msg.payload)) { msg.payload = msg.payload.toString(); }
|
||||
var msg2 = {payload:stderr};
|
||||
var msg3 = null;
|
||||
//console.log('[exec] stdout: ' + stdout);
|
||||
//console.log('[exec] stderr: ' + stderr);
|
||||
if (error !== null) {
|
||||
msg3 = {payload:error};
|
||||
//console.log('[exec] error: ' + error);
|
||||
}
|
||||
if (!msg3) { node.status({}); }
|
||||
node.send([msg,msg2,msg3]);
|
||||
if (child.tout) { clearTimeout(child.tout); }
|
||||
delete node.activeProcesses[child.pid];
|
||||
});
|
||||
node.status({fill:"blue",shape:"dot",text:"pid:"+child.pid});
|
||||
child.on('error',function() {});
|
||||
if (node.timer !== 0) {
|
||||
child.tout = setTimeout(function() { cleanup(child.pid); }, node.timer);
|
||||
}
|
||||
node.activeProcesses[child.pid] = child;
|
||||
}
|
||||
node.activeProcesses[child.pid] = child;
|
||||
}
|
||||
});
|
||||
this.on('close',function() {
|
||||
|
||||
@@ -24,6 +24,14 @@
|
||||
<input type="text" id="node-input-field" placeholder="payload" style="width:250px;">
|
||||
<input type="hidden" id="node-input-fieldType">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-syntax"><i class="fa fa-code"></i> <span data-i18n="template.label.syntax"></span></label>
|
||||
<select id="node-input-syntax" style="width:180px;">
|
||||
<option value="mustache" data-i18n="template.label.mustache"></option>
|
||||
<option value="plain" data-i18n="template.label.plain"></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-row" style="position: relative; margin-bottom: 0px;">
|
||||
<label for="node-input-template"><i class="fa fa-file-code-o"></i> <span data-i18n="template.label.template"></span></label>
|
||||
<input type="hidden" id="node-input-template" autofocus="autofocus">
|
||||
@@ -45,12 +53,13 @@
|
||||
<div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-template-editor" ></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-syntax"><i class="fa fa-code"></i> <span data-i18n="template.label.syntax"></span></label>
|
||||
<select id="node-input-syntax" style="width:180px;">
|
||||
<option value="mustache" data-i18n="template.label.mustache"></option>
|
||||
<option value="plain" data-i18n="template.label.plain"></option>
|
||||
<label for="node-input-output"><i class="fa fa-long-arrow-right"></i> <span data-i18n="template.label.output"></span></label>
|
||||
<select id="node-input-output" style="width:180px;">
|
||||
<option value="str" data-i18n="template.label.plain"></option>
|
||||
<option value="json" data-i18n="template.label.json"></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="template">
|
||||
@@ -83,6 +92,7 @@
|
||||
format: {value:"handlebars"},
|
||||
syntax: {value:"mustache"},
|
||||
template: {value:"This is the payload: {{payload}} !"},
|
||||
output: {value:"str"}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:1,
|
||||
|
||||
@@ -19,8 +19,8 @@ module.exports = function(RED) {
|
||||
var mustache = require("mustache");
|
||||
|
||||
/**
|
||||
* Custom Mustache Context capable to resolve message property and node
|
||||
* flow and global context
|
||||
* Custom Mustache Context capable to resolve message property and node
|
||||
* flow and global context
|
||||
*/
|
||||
function NodeContext(msg, nodeContext) {
|
||||
this.msgContext = new mustache.Context(msg);
|
||||
@@ -58,6 +58,7 @@ module.exports = function(RED) {
|
||||
this.template = n.template;
|
||||
this.syntax = n.syntax || "mustache";
|
||||
this.fieldType = n.fieldType || "msg";
|
||||
this.outputFormat = n.output || "str";
|
||||
|
||||
var node = this;
|
||||
node.on("input", function(msg) {
|
||||
@@ -68,6 +69,10 @@ module.exports = function(RED) {
|
||||
} else {
|
||||
value = node.template;
|
||||
}
|
||||
if (node.outputFormat === "json") {
|
||||
value = JSON.parse(value);
|
||||
}
|
||||
|
||||
if (node.fieldType === 'msg') {
|
||||
RED.util.setMessageProperty(msg,node.field,value);
|
||||
} else if (node.fieldType === 'flow') {
|
||||
|
||||
@@ -25,9 +25,10 @@
|
||||
<select id="node-then-type" style="width:70%;">
|
||||
<option value="block" data-i18n="trigger.wait-reset"></option>
|
||||
<option value="wait" data-i18n="trigger.wait-for"></option>
|
||||
<option value="loop" data-i18n="trigger.wait-loop"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row node-type-wait">
|
||||
<div class="form-row node-type-duration">
|
||||
<label></label>
|
||||
<input type="text" id="node-input-duration" style="text-align:end; width:70px !important">
|
||||
<select id="node-input-units" style="width:140px !important">
|
||||
@@ -67,16 +68,17 @@
|
||||
<p>The two output states can be specified as can the duration of the timer.
|
||||
Either output can be set to a value, or templated from the inbound
|
||||
<code>msg</code> using mustache syntax. <pre>The payload is {{payload}}</pre></p>
|
||||
<p>If the <code>msg.payload</code> is an object then setting the output to
|
||||
<i>existing payload</i> will pass the complete payload object through.</p>
|
||||
<p>Or you can pass through either the <i>original msg</i> or the <i>latest msg</i> to arrive.</p>
|
||||
<p>Optionally the timer can be extended by being retriggered... or not.</p>
|
||||
<p>By setting the first output to <i>nothing</i>, and selecting extend timer - a watchdog timer can be created.
|
||||
No output will happen as long as repeated inputs occur within the timeout period.</p>
|
||||
<p>Setting the timer to 0 creates an "infinite" timeout - the first output will happen but the second
|
||||
never will, and neither can the first be retriggered - so a true one shot.</p>
|
||||
<p>If a <code>msg.reset</code> property is present, or the <code>msg.payload</code>
|
||||
matches the optional reset value, any timeout currently in progress
|
||||
matches the optional reset value, any timeout or repeat currently in progress
|
||||
will be cleared and the second output will not happen.</p>
|
||||
<p>The node can be set to repeat the input <code>msg</code> at regular intervals until the input changes,
|
||||
or the node is reset.</p>
|
||||
<p>The blue status icon will be visible while the node is active.</p>
|
||||
</script>
|
||||
|
||||
@@ -102,6 +104,9 @@
|
||||
if (this.duration > 0) {
|
||||
return this.name|| this._("trigger.label.trigger")+" "+this.duration+this.units;
|
||||
}
|
||||
if (this.duration < 0) {
|
||||
return this.name|| this._("trigger.label.trigger-loop")+" "+(this.duration * -1)+this.units;
|
||||
}
|
||||
else {
|
||||
return this.name|| this._("trigger.label.trigger-block");
|
||||
}
|
||||
@@ -113,8 +118,14 @@
|
||||
$("#node-then-type").change(function() {
|
||||
if ($(this).val() == "block") {
|
||||
$(".node-type-wait").hide();
|
||||
$(".node-type-duration").hide();
|
||||
}
|
||||
else if ($(this).val() == "loop") {
|
||||
$(".node-type-wait").hide();
|
||||
$(".node-type-duration").show();
|
||||
} else {
|
||||
$(".node-type-wait").show();
|
||||
$(".node-type-duration").show();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -127,7 +138,6 @@
|
||||
|
||||
var optionNothing = {value:"nul",label:this._("trigger.output.nothing"),hasValue:false};
|
||||
var optionPayload = {value:"pay",label:this._("trigger.output.existing"),hasValue:false}
|
||||
|
||||
var optionOriginalPayload = {value:"pay",label:this._("trigger.output.original"),hasValue:false}
|
||||
var optionLatestPayload = {value:"payl",label:this._("trigger.output.latest"),hasValue:false}
|
||||
|
||||
@@ -151,6 +161,11 @@
|
||||
|
||||
if (this.duration == "0") {
|
||||
$("#node-then-type").val("block");
|
||||
}
|
||||
else if ((this.duration * 1) < 0) {
|
||||
$("#node-then-type").val("loop");
|
||||
this.duration = this.duration * -1;
|
||||
$("#node-input-duration").val(this.duration);
|
||||
} else {
|
||||
$("#node-then-type").val("wait");
|
||||
}
|
||||
@@ -167,6 +182,9 @@
|
||||
if ($("#node-then-type").val() == "block") {
|
||||
$("#node-input-duration").val("0");
|
||||
}
|
||||
if ($("#node-then-type").val() == "loop") {
|
||||
$("#node-input-duration").val($("#node-input-duration").val() * -1);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -48,12 +48,16 @@ module.exports = function(RED) {
|
||||
this.units = n.units || "ms";
|
||||
this.reset = n.reset || '';
|
||||
this.duration = n.duration || 250;
|
||||
if (this.duration <= 0) { this.duration = 0; }
|
||||
else {
|
||||
if (this.units == "s") { this.duration = this.duration * 1000; }
|
||||
if (this.units == "min") { this.duration = this.duration * 1000 * 60; }
|
||||
if (this.units == "hr") { this.duration = this.duration * 1000 *60 * 60; }
|
||||
if (this.duration < 0) {
|
||||
this.loop = true;
|
||||
this.duration = this.duration * -1;
|
||||
this.extend = false;
|
||||
}
|
||||
|
||||
if (this.units == "s") { this.duration = this.duration * 1000; }
|
||||
if (this.units == "min") { this.duration = this.duration * 1000 * 60; }
|
||||
if (this.units == "hr") { this.duration = this.duration * 1000 *60 * 60; }
|
||||
|
||||
this.op1Templated = (this.op1type === 'str' && this.op1.indexOf("{{") != -1);
|
||||
this.op2Templated = (this.op2type === 'str' && this.op2.indexOf("{{") != -1);
|
||||
if ((this.op1type === "num") && (!isNaN(this.op1))) { this.op1 = Number(this.op1); }
|
||||
@@ -69,13 +73,14 @@ module.exports = function(RED) {
|
||||
var tout = null;
|
||||
var m2;
|
||||
this.on("input", function(msg) {
|
||||
if (msg.hasOwnProperty("reset") || ((node.reset !== '')&&(msg.payload == node.reset)) ) {
|
||||
clearTimeout(tout);
|
||||
if (msg.hasOwnProperty("reset") || ((node.reset !== '') && (msg.payload == node.reset)) ) {
|
||||
if (node.loop === true) { clearInterval(tout); }
|
||||
else { clearTimeout(tout); }
|
||||
tout = null;
|
||||
node.status({});
|
||||
}
|
||||
else {
|
||||
if ((!tout) && (tout !== 0)) {
|
||||
if (((!tout) && (tout !== 0)) || (node.loop === true)) {
|
||||
if (node.op2type === "pay" || node.op2type === "payl") { m2 = msg.payload; }
|
||||
else if (node.op2Templated) { m2 = mustache.render(node.op2,msg); }
|
||||
else if (node.op2type !== "nul") {
|
||||
@@ -91,6 +96,13 @@ module.exports = function(RED) {
|
||||
if (node.op1type !== "nul") { node.send(msg); }
|
||||
|
||||
if (node.duration === 0) { tout = 0; }
|
||||
else if (node.loop === true) {
|
||||
if (tout) { clearInterval(tout); }
|
||||
if (node.op1type !== "nul") {
|
||||
var msg2 = RED.util.cloneMessage(msg);
|
||||
tout = setInterval(function() { node.send(msg2); },node.duration);
|
||||
}
|
||||
}
|
||||
else {
|
||||
tout = setTimeout(function() {
|
||||
if (node.op2type !== "nul") {
|
||||
@@ -108,7 +120,7 @@ module.exports = function(RED) {
|
||||
node.status({fill:"blue",shape:"dot",text:" "});
|
||||
}
|
||||
else if ((node.extend === "true" || node.extend === true) && (node.duration > 0)) {
|
||||
clearTimeout(tout);
|
||||
if (tout) { clearTimeout(tout); }
|
||||
if (node.op2type === "payl") { m2 = msg.payload; }
|
||||
tout = setTimeout(function() {
|
||||
if (node.op2type !== "nul") {
|
||||
@@ -122,14 +134,17 @@ module.exports = function(RED) {
|
||||
tout = null;
|
||||
node.status({});
|
||||
},node.duration);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (node.op2type === "payl") { m2 = msg.payload; }
|
||||
}
|
||||
}
|
||||
});
|
||||
this.on("close", function() {
|
||||
if (tout) {
|
||||
clearTimeout(tout);
|
||||
if (node.loop === true) { clearInterval(tout); }
|
||||
else { clearTimeout(tout); }
|
||||
tout = null;
|
||||
}
|
||||
node.status({});
|
||||
});
|
||||
|
||||
@@ -218,7 +218,6 @@
|
||||
var pinsInUse = {};
|
||||
RED.nodes.registerType('rpi-gpio out',{
|
||||
category: 'Raspberry Pi',
|
||||
label: 'Raspberry Pi',
|
||||
color:"#c6dbef",
|
||||
defaults: {
|
||||
name: { value:"" },
|
||||
@@ -351,7 +350,6 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('rpi-mouse',{
|
||||
category: 'Raspberry Pi',
|
||||
label: 'Raspberry Pi',
|
||||
color:"#c6dbef",
|
||||
defaults: {
|
||||
name: { value:"" },
|
||||
@@ -390,7 +388,6 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('rpi-keyboard',{
|
||||
category: 'Raspberry Pi',
|
||||
label: 'Raspberry Pi',
|
||||
color:"#c6dbef",
|
||||
defaults: {
|
||||
name: { value:"" }
|
||||
|
||||
@@ -37,8 +37,13 @@ module.exports = function(RED) {
|
||||
fs.statSync("/usr/lib/python2.7/site-packages/RPi/GPIO"); // test on Arch
|
||||
}
|
||||
catch(err) {
|
||||
RED.log.warn(RED._("rpi-gpio.errors.libnotfound"));
|
||||
throw "Warning : "+RED._("rpi-gpio.errors.libnotfound");
|
||||
try {
|
||||
fs.statSync("/usr/lib/python2.7/dist-packages/RPi/GPIO"); // test on Hypriot
|
||||
}
|
||||
catch(err) {
|
||||
RED.log.warn(RED._("rpi-gpio.errors.libnotfound"));
|
||||
throw "Warning : "+RED._("rpi-gpio.errors.libnotfound");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,14 @@ module.exports = function(RED) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
RED.nodes.registerType("tls-config",TLSConfig);
|
||||
RED.nodes.registerType("tls-config",TLSConfig,{
|
||||
settings: {
|
||||
tlsConfigDisableLocalFiles: {
|
||||
value: true,
|
||||
exportable: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
TLSConfig.prototype.addTLSOptions = function(opts) {
|
||||
if (this.valid) {
|
||||
|
||||
@@ -147,9 +147,9 @@
|
||||
<div id="mqtt-broker-tab-connection" style="display:none">
|
||||
<div class="form-row node-input-broker">
|
||||
<label for="node-config-input-broker"><i class="fa fa-globe"></i> <span data-i18n="mqtt.label.broker"></span></label>
|
||||
<input class="input-append-left" type="text" id="node-config-input-broker" placeholder="e.g. localhost" style="width: 40%;" >
|
||||
<label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> <span data-i18n="mqtt.label.port"></span></label>
|
||||
<input type="text" id="node-config-input-port" data-i18n="[placeholder]mqtt.label.port" style="width:45px">
|
||||
<input class="input-append-left" type="text" id="node-config-input-broker" placeholder="e.g. localhost" style="width:40%;" >
|
||||
<label for="node-config-input-port" style="margin-left:20px; width:35px; "> <span data-i18n="mqtt.label.port"></span></label>
|
||||
<input type="text" id="node-config-input-port" data-i18n="[placeholder]mqtt.label.port" style="width:55px">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input type="checkbox" id="node-config-input-usetls" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
|
||||
@@ -25,6 +25,11 @@
|
||||
<option value="patch">PATCH</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row form-row-http-in-upload hide">
|
||||
<label> </label>
|
||||
<input type="checkbox" id="node-input-upload" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-input-upload" style="width: 70%;" data-i18n="httpin.label.upload"></label>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-url"><i class="fa fa-globe"></i> <span data-i18n="httpin.label.url"></span></label>
|
||||
<input id="node-input-url" type="text" placeholder="/url">
|
||||
@@ -59,6 +64,9 @@
|
||||
To send JSON encoded data to the node, the content-type header of the request must be set to
|
||||
<code>application/json</code>.
|
||||
</p>
|
||||
<p>
|
||||
If file uploads are enabled for POST requests, the files will be available under
|
||||
<code>msg.req.files</code>.
|
||||
<p>
|
||||
<b>Note: </b>This node does not send any response to the http request.
|
||||
This should be done with a subsequent HTTP Response node.
|
||||
@@ -71,6 +79,16 @@
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
||||
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-statusCode"><i class="fa fa-long-arrow-left"></i> <span data-i18n="httpin.label.status"></span></label>
|
||||
<input type="text" id="node-input-statusCode" placeholder="msg.statusCode">
|
||||
</div>
|
||||
<div class="form-row" style="margin-bottom:0;">
|
||||
<label><i class="fa fa-list"></i> <span data-i18n="httpin.label.headers"></span></label>
|
||||
</div>
|
||||
<div class="form-row node-input-headers-container-row">
|
||||
<ol id="node-input-headers-container"></ol>
|
||||
</div>
|
||||
<div class="form-tips"><span data-i18n="[html]httpin.tip.res"></span></div>
|
||||
</script>
|
||||
|
||||
@@ -84,6 +102,9 @@
|
||||
pairs to be added as response headers.</li>
|
||||
<li><code>cookies</code>, if set, can be used to set or delete cookies.
|
||||
</ul>
|
||||
<p>The <code>statusCode</code> and <code>headers</code> can also be set within
|
||||
the node itself. If a property is set within the node, it cannot be overridden
|
||||
by the corresponding message property.</p>
|
||||
<h3>Cookie handling</h3>
|
||||
<p>The <code>cookies</code> property must be an object of name/value pairs.
|
||||
The value can be either a string to set the value of the cookie with default
|
||||
@@ -112,6 +133,7 @@ msg.cookies = {
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
RED.nodes.registerType('http in',{
|
||||
category: 'input',
|
||||
color:"rgb(231, 231, 174)",
|
||||
@@ -119,6 +141,7 @@ msg.cookies = {
|
||||
name: {value:""},
|
||||
url: {value:"",required:true},
|
||||
method: {value:"get",required:true},
|
||||
upload: {value:false},
|
||||
swaggerDoc: {type:"swagger-doc", required:false}
|
||||
},
|
||||
inputs:0,
|
||||
@@ -159,25 +182,165 @@ msg.cookies = {
|
||||
if(!RED.nodes.getType("swagger-doc")){
|
||||
$('.row-swagger-doc').hide();
|
||||
}
|
||||
$("#node-input-method").change(function() {
|
||||
if ($(this).val() === "post") {
|
||||
$(".form-row-http-in-upload").show();
|
||||
} else {
|
||||
$(".form-row-http-in-upload").hide();
|
||||
}
|
||||
}).change();
|
||||
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
var headerTypes = [
|
||||
{value:"content-type",label:"Content-Type",hasValue: false},
|
||||
{value:"location",label:"Location",hasValue: false},
|
||||
{value:"other",label:"other",icon:"red/images/typedInput/az.png"}
|
||||
]
|
||||
var contentTypes = [
|
||||
{value:"application/json",label:"application/json",hasValue: false},
|
||||
{value:"application/xml",label:"application/xml",hasValue: false},
|
||||
{value:"text/css",label:"text/css",hasValue: false},
|
||||
{value:"text/html",label:"text/html",hasValue: false},
|
||||
{value:"text/plain",label:"text/plain",hasValue: false},
|
||||
{value:"image/gif",label:"image/gif",hasValue: false},
|
||||
{value:"image/png",label:"image/png",hasValue: false},
|
||||
{value:"other",label:"other",icon:"red/images/typedInput/az.png"}
|
||||
];
|
||||
|
||||
RED.nodes.registerType('http response',{
|
||||
category: 'output',
|
||||
color:"rgb(231, 231, 174)",
|
||||
defaults: {
|
||||
name: {value:""}
|
||||
name: {value:""},
|
||||
statusCode: {value:"",validate: RED.validators.number(true)},
|
||||
headers: {value:{}}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:0,
|
||||
align: "right",
|
||||
icon: "white-globe.png",
|
||||
label: function() {
|
||||
return this.name||"http";
|
||||
return this.name||("http"+(this.statusCode?" ("+this.statusCode+")":""));
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
function resizeRule(rule) {
|
||||
var newWidth = rule.width();
|
||||
rule.find('.red-ui-typedInput').typedInput("width",(newWidth-15)/2);
|
||||
|
||||
}
|
||||
var headerList = $("#node-input-headers-container").css('min-height','150px').css('min-width','450px').editableList({
|
||||
addItem: function(container,i,header) {
|
||||
var row = $('<div/>').appendTo(container);
|
||||
var propertyName = $('<input/>',{class:"node-input-header-name",type:"text"})
|
||||
.appendTo(row)
|
||||
.typedInput({types:headerTypes});
|
||||
|
||||
var propertyValue = $('<input/>',{class:"node-input-header-value",type:"text",style:"margin-left: 10px"})
|
||||
.appendTo(row)
|
||||
.typedInput({types:
|
||||
header.h === 'content-type'?contentTypes:[{value:"other",label:"other",icon:"red/images/typedInput/az.png"}]
|
||||
});
|
||||
|
||||
var matchedType = headerTypes.filter(function(ht) {
|
||||
return ht.value === header.h
|
||||
});
|
||||
if (matchedType.length === 0) {
|
||||
propertyName.typedInput('type','other');
|
||||
propertyName.typedInput('value',header.h);
|
||||
propertyValue.typedInput('value',header.v);
|
||||
} else {
|
||||
propertyName.typedInput('type',header.h);
|
||||
|
||||
if (header.h === "content-type") {
|
||||
matchedType = contentTypes.filter(function(ct) {
|
||||
return ct.value === header.v;
|
||||
});
|
||||
if (matchedType.length === 0) {
|
||||
propertyValue.typedInput('type','other');
|
||||
propertyValue.typedInput('value',header.v);
|
||||
} else {
|
||||
propertyValue.typedInput('type',header.v);
|
||||
}
|
||||
} else {
|
||||
propertyValue.typedInput('value',header.v);
|
||||
}
|
||||
}
|
||||
|
||||
matchedType = headerTypes.filter(function(ht) {
|
||||
return ht.value === header.h
|
||||
});
|
||||
if (matchedType.length === 0) {
|
||||
propertyName.typedInput('type','other');
|
||||
propertyName.typedInput('value',header.h);
|
||||
} else {
|
||||
propertyName.typedInput('type',header.h);
|
||||
}
|
||||
|
||||
propertyName.on('change',function(event) {
|
||||
var type = propertyName.typedInput('type');
|
||||
if (type === 'content-type') {
|
||||
propertyValue.typedInput('types',contentTypes);
|
||||
} else {
|
||||
propertyValue.typedInput('types',[{value:"other",label:"other",icon:"red/images/typedInput/az.png"}]);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
resizeRule(container);
|
||||
},
|
||||
resizeItem: resizeRule,
|
||||
removable: true
|
||||
});
|
||||
|
||||
if (this.headers) {
|
||||
for (var key in this.headers) {
|
||||
if (this.headers.hasOwnProperty(key)) {
|
||||
headerList.editableList('addItem',{h:key,v:this.headers[key]});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
oneditsave: function() {
|
||||
var headers = $("#node-input-headers-container").editableList('items');
|
||||
var node = this;
|
||||
node.headers = {};
|
||||
headers.each(function(i) {
|
||||
var header = $(this);
|
||||
var keyType = header.find(".node-input-header-name").typedInput('type');
|
||||
var keyValue = header.find(".node-input-header-name").typedInput('value');
|
||||
var valueType = header.find(".node-input-header-value").typedInput('type');
|
||||
var valueValue = header.find(".node-input-header-value").typedInput('value');
|
||||
var key = keyType;
|
||||
var value = valueType;
|
||||
if (keyType === 'other') {
|
||||
key = keyValue;
|
||||
}
|
||||
if (valueType === 'other') {
|
||||
value = valueValue;
|
||||
}
|
||||
if (key !== '') {
|
||||
node.headers[key] = value;
|
||||
}
|
||||
});
|
||||
},
|
||||
oneditresize: function(size) {
|
||||
var rows = $("#dialog-form>div:not(.node-input-headers-container-row)");
|
||||
var height = size.height;
|
||||
for (var i=0; i<rows.size(); i++) {
|
||||
height -= $(rows[i]).outerHeight(true);
|
||||
}
|
||||
var editorRow = $("#dialog-form>div.node-input-headers-container-row");
|
||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||
|
||||
$("#node-input-headers-container").editableList('height',height);
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
module.exports = function(RED) {
|
||||
"use strict";
|
||||
var bodyParser = require("body-parser");
|
||||
var multer = require("multer");
|
||||
var cookieParser = require("cookie-parser");
|
||||
var getBody = require('raw-body');
|
||||
var cors = require('cors');
|
||||
@@ -177,6 +178,7 @@ module.exports = function(RED) {
|
||||
}
|
||||
this.url = n.url;
|
||||
this.method = n.method;
|
||||
this.upload = n.upload;
|
||||
this.swaggerDoc = n.swaggerDoc;
|
||||
|
||||
var node = this;
|
||||
@@ -225,10 +227,21 @@ module.exports = function(RED) {
|
||||
};
|
||||
}
|
||||
|
||||
var multipartParser = function(req,res,next) { next(); }
|
||||
if (this.upload) {
|
||||
var mp = multer({ storage: multer.memoryStorage() }).any();
|
||||
multipartParser = function(req,res,next) {
|
||||
mp(req,res,function(err) {
|
||||
req._body = true;
|
||||
next(err);
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
if (this.method == "get") {
|
||||
RED.httpNode.get(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,this.callback,this.errorHandler);
|
||||
} else if (this.method == "post") {
|
||||
RED.httpNode.post(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,jsonParser,urlencParser,rawBodyParser,this.callback,this.errorHandler);
|
||||
RED.httpNode.post(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,jsonParser,urlencParser,multipartParser,rawBodyParser,this.callback,this.errorHandler);
|
||||
} else if (this.method == "put") {
|
||||
RED.httpNode.put(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,jsonParser,urlencParser,rawBodyParser,this.callback,this.errorHandler);
|
||||
} else if (this.method == "patch") {
|
||||
@@ -255,10 +268,20 @@ module.exports = function(RED) {
|
||||
function HTTPOut(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
var node = this;
|
||||
this.headers = n.headers||{};
|
||||
this.statusCode = n.statusCode;
|
||||
this.on("input",function(msg) {
|
||||
if (msg.res) {
|
||||
var headers = RED.util.cloneMessage(node.headers);
|
||||
if (msg.headers) {
|
||||
msg.res._res.set(msg.headers);
|
||||
for (var h in msg.headers) {
|
||||
if (msg.headers.hasOwnProperty(h) && !headers.hasOwnProperty(h)) {
|
||||
headers[h] = msg.headers[h];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Object.keys(headers).length > 0) {
|
||||
msg.res._res.set(headers);
|
||||
}
|
||||
if (msg.cookies) {
|
||||
for (var name in msg.cookies) {
|
||||
@@ -277,7 +300,7 @@ module.exports = function(RED) {
|
||||
}
|
||||
}
|
||||
}
|
||||
var statusCode = msg.statusCode || 200;
|
||||
var statusCode = node.statusCode || msg.statusCode || 200;
|
||||
if (typeof msg.payload == "object" && !Buffer.isBuffer(msg.payload)) {
|
||||
msg.res._res.status(statusCode).jsonp(msg.payload);
|
||||
} else {
|
||||
|
||||
@@ -82,16 +82,15 @@ module.exports = function(RED) {
|
||||
|
||||
RED.server.addListener('newListener',storeListener);
|
||||
|
||||
// Create a WebSocket Server
|
||||
node.server = new ws.Server({
|
||||
var serverOptions = {
|
||||
server:RED.server,
|
||||
path:path,
|
||||
// Disable the deflate option due to this issue
|
||||
// https://github.com/websockets/ws/pull/632
|
||||
// that is fixed in the 1.x release of the ws module
|
||||
// that we cannot currently pickup as it drops node 0.10 support
|
||||
perMessageDeflate: false
|
||||
});
|
||||
path:path
|
||||
}
|
||||
if (RED.settings.webSocketNodeVerifyClient) {
|
||||
serverOptions.verifyClient = RED.settings.webSocketNodeVerifyClient;
|
||||
}
|
||||
// Create a WebSocket Server
|
||||
node.server = new ws.Server(serverOptions);
|
||||
|
||||
// Workaround https://github.com/einaros/ws/pull/253
|
||||
// Stop listening for new listener events
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
<label for="node-input-files"><i class="fa fa-file"></i> <span data-i18n="watch.label.files"></span></label>
|
||||
<input id="node-input-files" type="text" tabindex="1" data-i18n="[placeholder]watch.placeholder.files">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label> </label>
|
||||
<input type="checkbox" id="node-input-recursive" style="display:inline-block; width:auto; vertical-align:top;">
|
||||
<label for="node-input-recursive" style="width:70%;"> <span data-i18n="watch.label.recursive"></span></label>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
||||
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
|
||||
@@ -46,7 +51,8 @@
|
||||
category: 'advanced-input',
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
files: {value:"",required:true}
|
||||
files: {value:"",required:true},
|
||||
recursive: {value:""}
|
||||
},
|
||||
color:"BurlyWood",
|
||||
inputs:0,
|
||||
|
||||
@@ -19,10 +19,23 @@ module.exports = function(RED) {
|
||||
var Notify = require("fs.notify");
|
||||
var fs = require("fs");
|
||||
var sep = require("path").sep;
|
||||
var path = require("path");
|
||||
|
||||
var getAllDirs = function (dir, filelist) {
|
||||
filelist = filelist || [];
|
||||
fs.readdirSync(dir).forEach(file => {
|
||||
if (fs.statSync(path.join(dir, file)).isDirectory() ) {
|
||||
filelist.push(path.join(dir, file));
|
||||
getAllDirs(path.join(dir, file), filelist);
|
||||
}
|
||||
});
|
||||
return filelist;
|
||||
}
|
||||
|
||||
function WatchNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
|
||||
this.recursive = n.recursive || false;
|
||||
this.files = (n.files || "").split(",");
|
||||
for (var f=0; f < this.files.length; f++) {
|
||||
this.files[f] = this.files[f].trim();
|
||||
@@ -30,6 +43,14 @@ module.exports = function(RED) {
|
||||
this.p = (this.files.length === 1) ? this.files[0] : JSON.stringify(this.files);
|
||||
var node = this;
|
||||
|
||||
if (node.recursive) {
|
||||
for (var fi in node.files) {
|
||||
if (node.files.hasOwnProperty(fi)) {
|
||||
node.files = node.files.concat(getAllDirs( node.files[fi]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var notifications = new Notify(node.files);
|
||||
notifications.on('change', function (file, event, path) {
|
||||
var stat;
|
||||
@@ -41,11 +62,17 @@ module.exports = function(RED) {
|
||||
var msg = { payload:path, topic:node.p, file:file };
|
||||
if (stat) {
|
||||
if (stat.isFile()) { type = "file"; msg.size = stat.size; }
|
||||
else if (stat.isDirectory()) { type = "directory"; }
|
||||
else if (stat.isBlockDevice()) { type = "blockdevice"; }
|
||||
else if (stat.isCharacterDevice()) { type = "characterdevice"; }
|
||||
else if (stat.isSocket()) { type = "socket"; }
|
||||
else if (stat.isFIFO()) { type = "fifo"; }
|
||||
else if (stat.isDirectory()) {
|
||||
type = "directory";
|
||||
if (node.recursive) {
|
||||
notifications.add([path]);
|
||||
notifications.add(getAllDirs(path));
|
||||
}
|
||||
}
|
||||
else { type = "n/a"; }
|
||||
}
|
||||
msg.type = type;
|
||||
|
||||
@@ -168,8 +168,10 @@
|
||||
"property": "Set property",
|
||||
"format": "Syntax Highlight",
|
||||
"syntax": "Format",
|
||||
"output": "Output as",
|
||||
"mustache": "Mustache template",
|
||||
"plain": "Plain text"
|
||||
"plain": "Plain text",
|
||||
"json": "Parsed JSON"
|
||||
},
|
||||
"templatevalue": "This is the payload: {{payload}} !"
|
||||
},
|
||||
@@ -231,13 +233,14 @@
|
||||
"output": {
|
||||
"string": "the string",
|
||||
"number": "the number",
|
||||
"existing": "the existing msg.payload",
|
||||
"original": "the original msg.payload",
|
||||
"latest": "the latest msg.payload",
|
||||
"existing": "the existing msg object",
|
||||
"original": "the original msg object",
|
||||
"latest": "the latest msg object",
|
||||
"nothing": "nothing"
|
||||
},
|
||||
"wait-reset": "wait to be reset",
|
||||
"wait-for": "wait for",
|
||||
"wait-loop": "resend it every",
|
||||
"duration": {
|
||||
"ms": "Milliseconds",
|
||||
"s": "Seconds",
|
||||
@@ -248,6 +251,7 @@
|
||||
"label": {
|
||||
"trigger": "trigger",
|
||||
"trigger-block": "trigger & block",
|
||||
"trigger-loop": "resend every",
|
||||
"reset": "Reset the trigger if:",
|
||||
"resetMessage":"msg.reset is set",
|
||||
"resetPayload":"msg.payload equals",
|
||||
@@ -313,7 +317,10 @@
|
||||
"method": "Method",
|
||||
"url": "URL",
|
||||
"doc": "Docs",
|
||||
"return": "Return"
|
||||
"return": "Return",
|
||||
"upload": "Accept file uploads?",
|
||||
"status": "Status code",
|
||||
"headers": "Headers"
|
||||
},
|
||||
"setby": "- set by msg.method -",
|
||||
"basicauth": "Use basic authentication",
|
||||
@@ -364,7 +371,8 @@
|
||||
},
|
||||
"watch": {
|
||||
"label": {
|
||||
"files": "File(s)"
|
||||
"files": "File(s)",
|
||||
"recursive": "Watch sub-directories recursively"
|
||||
},
|
||||
"placeholder": {
|
||||
"files": "Comma-separated list of files and/or directories"
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<div class="form-row">
|
||||
<label data-i18n="switch.label.property"></label>
|
||||
<input type="text" id="node-input-property" style="width: 70%"/>
|
||||
<input type="hidden" id="node-input-outputs"/>
|
||||
</div>
|
||||
<div class="form-row node-input-rule-container-row">
|
||||
<ol id="node-input-rule-container"></ol>
|
||||
@@ -43,6 +44,38 @@
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
var operators = [
|
||||
{v:"eq",t:"=="},
|
||||
{v:"neq",t:"!="},
|
||||
{v:"lt",t:"<"},
|
||||
{v:"lte",t:"<="},
|
||||
{v:"gt",t:">"},
|
||||
{v:"gte",t:">="},
|
||||
{v:"btwn",t:"switch.rules.btwn"},
|
||||
{v:"cont",t:"switch.rules.cont"},
|
||||
{v:"regex",t:"switch.rules.regex"},
|
||||
{v:"true",t:"switch.rules.true"},
|
||||
{v:"false",t:"switch.rules.false"},
|
||||
{v:"null",t:"switch.rules.null"},
|
||||
{v:"nnull",t:"switch.rules.nnull"},
|
||||
{v:"else",t:"switch.rules.else"}
|
||||
];
|
||||
|
||||
function clipValueLength(v) {
|
||||
if (v.length > 15) {
|
||||
return v.substring(0,15)+"...";
|
||||
}
|
||||
return v;
|
||||
}
|
||||
function getValueLabel(t,v) {
|
||||
if (t === 'str') {
|
||||
return '"'+clipValueLength(v)+'"';
|
||||
} else if (t === 'msg' || t==='flow' || t==='global') {
|
||||
return t+"."+clipValueLength(v);
|
||||
}
|
||||
return clipValueLength(v);
|
||||
}
|
||||
RED.nodes.registerType('switch', {
|
||||
color: "#E2D96E",
|
||||
category: 'function',
|
||||
@@ -56,6 +89,24 @@
|
||||
},
|
||||
inputs: 1,
|
||||
outputs: 1,
|
||||
outputLabels: function(index) {
|
||||
var rule = this.rules[index];
|
||||
var label = "";
|
||||
if (rule) {
|
||||
for (var i=0;i<operators.length;i++) {
|
||||
if (operators[i].v === rule.t) {
|
||||
label = /^switch/.test(operators[i].t)?this._(operators[i].t):operators[i].t
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rule.t === 'btwn') {
|
||||
label += " "+getValueLabel(rule.vt,rule.v)+" & "+getValueLabel(rule.v2t,rule.v2);
|
||||
} else if (rule.t !== 'true' && rule.t !== 'false' && rule.t !== 'null' && rule.t !== 'nnull' && rule.t !== 'else' ) {
|
||||
label += " "+getValueLabel(rule.vt,rule.v);
|
||||
}
|
||||
return label;
|
||||
}
|
||||
},
|
||||
icon: "switch.png",
|
||||
label: function() {
|
||||
return this.name||"switch";
|
||||
@@ -65,22 +116,7 @@
|
||||
var previousValueType = {value:"prev",label:this._("inject.previous"),hasValue:false};
|
||||
|
||||
$("#node-input-property").typedInput({default:this.propertyType||'msg',types:['msg','flow','global','jsonata']});
|
||||
var operators = [
|
||||
{v:"eq",t:"=="},
|
||||
{v:"neq",t:"!="},
|
||||
{v:"lt",t:"<"},
|
||||
{v:"lte",t:"<="},
|
||||
{v:"gt",t:">"},
|
||||
{v:"gte",t:">="},
|
||||
{v:"btwn",t:this._("switch.rules.btwn")},
|
||||
{v:"cont",t:this._("switch.rules.cont")},
|
||||
{v:"regex",t:this._("switch.rules.regex")},
|
||||
{v:"true",t:this._("switch.rules.true")},
|
||||
{v:"false",t:this._("switch.rules.false")},
|
||||
{v:"null",t:this._("switch.rules.null")},
|
||||
{v:"nnull",t:this._("switch.rules.nnull")},
|
||||
{v:"else",t:this._("switch.rules.else")}
|
||||
];
|
||||
var outputCount = $("#node-input-outputs").val("{}");
|
||||
|
||||
var andLabel = this._("switch.and");
|
||||
var caseLabel = this._("switch.ignorecase");
|
||||
@@ -122,12 +158,15 @@
|
||||
if (!rule.hasOwnProperty('t')) {
|
||||
rule.t = 'eq';
|
||||
}
|
||||
if (!opt.hasOwnProperty('i')) {
|
||||
opt._i = Math.floor((0x99999-0x10000)*Math.random()).toString(16)
|
||||
}
|
||||
var row = $('<div/>').appendTo(container);
|
||||
var row2 = $('<div/>',{style:"padding-top: 5px; padding-left: 175px;"}).appendTo(container);
|
||||
var row3 = $('<div/>',{style:"padding-top: 5px; padding-left: 102px;"}).appendTo(container);
|
||||
var selectField = $('<select/>',{style:"width:120px; margin-left: 5px; text-align: center;"}).appendTo(row);
|
||||
for (var d in operators) {
|
||||
selectField.append($("<option></option>").val(operators[d].v).text(operators[d].t));
|
||||
selectField.append($("<option></option>").val(operators[d].v).text(/^switch/.test(operators[d].t)?node._(operators[d].t):operators[d].t));
|
||||
}
|
||||
var valueField = $('<input/>',{class:"node-input-rule-value",type:"text",style:"margin-left: 5px;"}).appendTo(row).typedInput({default:'str',types:['msg','flow','global','str','num','jsonata',previousValueType]});
|
||||
var btwnValueField = $('<input/>',{class:"node-input-rule-btwn-value",type:"text",style:"margin-left: 5px;"}).appendTo(row).typedInput({default:'num',types:['msg','flow','global','str','num','jsonata',previousValueType]});
|
||||
@@ -178,21 +217,36 @@
|
||||
caseSensitive.prop('checked',false);
|
||||
}
|
||||
selectField.change();
|
||||
|
||||
var currentOutputs = JSON.parse(outputCount.val()||"{}");
|
||||
currentOutputs[opt.hasOwnProperty('i')?opt.i:opt._i] = i;
|
||||
outputCount.val(JSON.stringify(currentOutputs));
|
||||
},
|
||||
removeItem: function(opt) {
|
||||
var currentOutputs = JSON.parse(outputCount.val()||"{}");
|
||||
if (opt.hasOwnProperty('i')) {
|
||||
var removedList = $("#node-input-rule-container").data('removedList')||[];
|
||||
removedList.push(opt.i);
|
||||
$("#node-input-rule-container").data('removedList',removedList);
|
||||
currentOutputs[opt.i] = -1;
|
||||
} else {
|
||||
delete currentOutputs[opt._i];
|
||||
}
|
||||
|
||||
var rules = $("#node-input-rule-container").editableList('items');
|
||||
rules.each(function(i) { $(this).find(".node-input-rule-index").html(i+1); });
|
||||
rules.each(function(i) {
|
||||
$(this).find(".node-input-rule-index").html(i+1);
|
||||
var data = $(this).data('data');
|
||||
currentOutputs[data.hasOwnProperty('i')?data.i:data._i] = i;
|
||||
});
|
||||
outputCount.val(JSON.stringify(currentOutputs));
|
||||
},
|
||||
resizeItem: resizeRule,
|
||||
sortItems: function(rules) {
|
||||
var currentOutputs = JSON.parse(outputCount.val()||"{}");
|
||||
var rules = $("#node-input-rule-container").editableList('items');
|
||||
rules.each(function(i) { $(this).find(".node-input-rule-index").html(i+1); });
|
||||
rules.each(function(i) {
|
||||
$(this).find(".node-input-rule-index").html(i+1);
|
||||
var data = $(this).data('data');
|
||||
currentOutputs[data.hasOwnProperty('i')?data.i:data._i] = i;
|
||||
});
|
||||
outputCount.val(JSON.stringify(currentOutputs));
|
||||
},
|
||||
sortable: true,
|
||||
removable: true
|
||||
@@ -208,11 +262,6 @@
|
||||
var ruleset;
|
||||
var node = this;
|
||||
node.rules = [];
|
||||
var changedOutputs = {};
|
||||
var removedList = $("#node-input-rule-container").data('removedList')||[];
|
||||
removedList.forEach(function(i) {
|
||||
changedOutputs[i] = -1;
|
||||
});
|
||||
rules.each(function(i) {
|
||||
var ruleData = $(this).data('data');
|
||||
var rule = $(this);
|
||||
@@ -232,15 +281,8 @@
|
||||
r.case = rule.find(".node-input-rule-case").prop("checked");
|
||||
}
|
||||
}
|
||||
if (ruleData.hasOwnProperty('i')) {
|
||||
if (ruleData.i !== i) {
|
||||
changedOutputs[ruleData.i] = i;
|
||||
}
|
||||
}
|
||||
node.rules.push(r);
|
||||
});
|
||||
this._outputs = changedOutputs;
|
||||
this.outputs = node.rules.length;
|
||||
this.propertyType = $("#node-input-property").typedInput('type');
|
||||
},
|
||||
oneditresize: function(size) {
|
||||
@@ -254,4 +296,5 @@
|
||||
$("#node-input-rule-container").editableList('height',height);
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
@@ -28,6 +28,7 @@ module.exports = function(RED) {
|
||||
this.hdrin = n.hdrin || false;
|
||||
this.hdrout = n.hdrout || false;
|
||||
this.goodtmpl = true;
|
||||
var tmpwarn = true;
|
||||
var node = this;
|
||||
|
||||
// pass in an array of column names to be trimed, de-quoted and retrimed
|
||||
@@ -71,7 +72,19 @@ module.exports = function(RED) {
|
||||
}
|
||||
else {
|
||||
if ((node.template.length === 1) && (node.template[0] === '')) {
|
||||
node.warn(RED._("csv.errors.obj_csv"));
|
||||
if (tmpwarn === true) { // just warn about missing template once
|
||||
node.warn(RED._("csv.errors.obj_csv"));
|
||||
tmpwarn = false;
|
||||
}
|
||||
ou = "";
|
||||
for (var p in msg.payload[0]) {
|
||||
if (msg.payload[0].hasOwnProperty(p)) {
|
||||
if (typeof msg.payload[0][p] !== "object") {
|
||||
ou += msg.payload[0][p] + ",";
|
||||
}
|
||||
}
|
||||
}
|
||||
ou = ou.slice(0,-1) + node.ret;
|
||||
}
|
||||
else {
|
||||
for (var t=0; t < node.template.length; t++) {
|
||||
@@ -112,7 +125,7 @@ module.exports = function(RED) {
|
||||
var first = true; // is this the first line
|
||||
var line = msg.payload;
|
||||
var tmp = "";
|
||||
var reg = new RegExp("^[-]?[0-9.]*[\.]?[0-9]*$");
|
||||
var reg = /^[-]?[0-9]*\.?[0-9]+$/;
|
||||
|
||||
// For now we are just going to assume that any \r or \n means an end of line...
|
||||
// got to be a weird csv that has singleton \r \n in it for another reason...
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
"jsonata":"1.0.10",
|
||||
"media-typer": "0.3.0",
|
||||
"mqtt": "2.2.1",
|
||||
"multer": "1.2.1",
|
||||
"mustache": "2.3.0",
|
||||
"nopt": "3.0.6",
|
||||
"oauth2orize":"1.7.0",
|
||||
|
||||
@@ -94,7 +94,7 @@ function init(_server,_runtime) {
|
||||
});
|
||||
}
|
||||
editorApp.get("/",ensureRuntimeStarted,ui.ensureSlash,ui.editor);
|
||||
editorApp.get("/icons/:icon",ui.icon);
|
||||
editorApp.get("/icons/:module/:icon",ui.icon);
|
||||
theme.init(runtime);
|
||||
if (settings.editorTheme) {
|
||||
editorApp.use("/theme",theme.app());
|
||||
|
||||
@@ -48,6 +48,8 @@ module.exports = {
|
||||
safeSettings.editorTheme.palette = safeSettings.editorTheme.palette || {};
|
||||
safeSettings.editorTheme.palette.editable = false;
|
||||
}
|
||||
|
||||
settings.exportNodeSettings(safeSettings);
|
||||
|
||||
res.json(safeSettings);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ var when = require('when');
|
||||
var redApp = null;
|
||||
var storage;
|
||||
var log;
|
||||
var redNodes;
|
||||
var needsPermission = require("./auth").needsPermission;
|
||||
|
||||
function createLibrary(type) {
|
||||
@@ -72,80 +73,22 @@ function createLibrary(type) {
|
||||
}
|
||||
}
|
||||
|
||||
var exampleRoots = {};
|
||||
var exampleFlows = {d:{}};
|
||||
var exampleCount = 0;
|
||||
|
||||
function getFlowsFromPath(path) {
|
||||
return when.promise(function(resolve,reject) {
|
||||
var result = {};
|
||||
fs.readdir(path,function(err,files) {
|
||||
var promises = [];
|
||||
var validFiles = [];
|
||||
files.forEach(function(file) {
|
||||
var fullPath = fspath.join(path,file);
|
||||
var stats = fs.lstatSync(fullPath);
|
||||
if (stats.isDirectory()) {
|
||||
validFiles.push(file);
|
||||
promises.push(getFlowsFromPath(fullPath));
|
||||
} else if (/\.json$/.test(file)){
|
||||
validFiles.push(file);
|
||||
exampleCount++;
|
||||
promises.push(when.resolve(file.split(".")[0]))
|
||||
}
|
||||
})
|
||||
var i=0;
|
||||
when.all(promises).then(function(results) {
|
||||
results.forEach(function(r) {
|
||||
if (typeof r === 'string') {
|
||||
result.f = result.f||[];
|
||||
result.f.push(r);
|
||||
} else {
|
||||
result.d = result.d||{};
|
||||
result.d[validFiles[i]] = r;
|
||||
}
|
||||
i++;
|
||||
})
|
||||
|
||||
resolve(result);
|
||||
})
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function addNodeExamplesDir(module) {
|
||||
exampleRoots[module.name] = module.path;
|
||||
getFlowsFromPath(module.path).then(function(result) {
|
||||
exampleFlows.d[module.name] = result;
|
||||
});
|
||||
}
|
||||
function removeNodeExamplesDir(module) {
|
||||
delete exampleRoots[module];
|
||||
delete exampleFlows.d[module];
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: function(app,runtime) {
|
||||
redApp = app;
|
||||
log = runtime.log;
|
||||
storage = runtime.storage;
|
||||
// TODO: this allows init to be called multiple times without
|
||||
// registering multiple instances of the listener.
|
||||
// It isn't.... ideal.
|
||||
runtime.events.removeListener("node-examples-dir",addNodeExamplesDir);
|
||||
runtime.events.on("node-examples-dir",addNodeExamplesDir);
|
||||
runtime.events.removeListener("node-module-uninstalled",removeNodeExamplesDir);
|
||||
runtime.events.on("node-module-uninstalled",removeNodeExamplesDir);
|
||||
|
||||
redNodes = runtime.nodes;
|
||||
},
|
||||
register: createLibrary,
|
||||
|
||||
getAll: function(req,res) {
|
||||
storage.getAllFlows().then(function(flows) {
|
||||
log.audit({event: "library.get.all",type:"flow"},req);
|
||||
if (exampleCount > 0) {
|
||||
var examples = redNodes.getNodeExampleFlows();
|
||||
if (examples) {
|
||||
flows.d = flows.d||{};
|
||||
flows.d._examples_ = exampleFlows;
|
||||
flows.d._examples_ = redNodes.getNodeExampleFlows();
|
||||
}
|
||||
res.json(flows);
|
||||
});
|
||||
@@ -155,9 +98,9 @@ module.exports = {
|
||||
var m = /^_examples_\/([^\/]+)\/(.*)$/.exec(req.params[0]);
|
||||
if (m) {
|
||||
var module = m[1];
|
||||
var path = m[2]+".json";
|
||||
if (exampleRoots[module]) {
|
||||
var fullPath = fspath.join(exampleRoots[module],path);
|
||||
var path = m[2];
|
||||
var fullPath = redNodes.getNodeExampleFlowPath(module,path);
|
||||
if (fullPath) {
|
||||
try {
|
||||
fs.statSync(fullPath);
|
||||
log.audit({event: "library.get",type:"flow",path:req.params[0]},req);
|
||||
|
||||
@@ -68,7 +68,8 @@
|
||||
"warnings": {
|
||||
"undeployedChanges": "node has undeployed changes",
|
||||
"nodeActionDisabled": "node actions disabled within subflow",
|
||||
"missing-types": "Flows stopped due to missing node types. Check logs for details."
|
||||
"missing-types": "Flows stopped due to missing node types. Check logs for details.",
|
||||
"restartRequired": "Node-RED must be restarted to enable upgraded modules"
|
||||
},
|
||||
|
||||
"error": "<strong>Error</strong>: __message__",
|
||||
@@ -184,6 +185,12 @@
|
||||
"editNode": "Edit __type__ node",
|
||||
"editConfig": "Edit __type__ config node",
|
||||
"addNewType": "Add new __type__...",
|
||||
"nodeProperties": "node properties",
|
||||
"portLabels": "port labels",
|
||||
"labelInputs": "Inputs",
|
||||
"labelOutputs": "Outputs",
|
||||
"noDefaultLabel": "none",
|
||||
"defaultLabel": "use default label",
|
||||
"errors": {
|
||||
"scopeChange": "Changing the scope will make it unavailable to nodes in other flows that use it"
|
||||
}
|
||||
@@ -250,7 +257,8 @@
|
||||
"nodeEnabled": "Node enabled:",
|
||||
"nodeEnabled_plural": "Nodes enabled:",
|
||||
"nodeDisabled": "Node disabled:",
|
||||
"nodeDisabled_plural": "Nodes disabled:"
|
||||
"nodeDisabled_plural": "Nodes disabled:",
|
||||
"nodeUpgraded": "Node module __module__ upgraded to version __version__"
|
||||
},
|
||||
"editor": {
|
||||
"title": "Manage palette",
|
||||
@@ -283,6 +291,7 @@
|
||||
"disable": "disable",
|
||||
"remove": "remove",
|
||||
"update": "update to __version__",
|
||||
"updated": "updated",
|
||||
"install": "install",
|
||||
"installed": "installed",
|
||||
"loading": "Loading catalogues...",
|
||||
@@ -295,7 +304,8 @@
|
||||
"errors": {
|
||||
"catalogLoadFailed": "Failed to load node catalogue.<br>Check the browser console for more information",
|
||||
"installFailed": "Failed to install: __module__<br>__message__<br>Check the log for more information",
|
||||
"removeFailed": "Failed to remove: __module__<br>__message__<br>Check the log for more information"
|
||||
"removeFailed": "Failed to remove: __module__<br>__message__<br>Check the log for more information",
|
||||
"updateFailed": "Failed to update: __module__<br>__message__<br>Check the log for more information"
|
||||
},
|
||||
"confirm": {
|
||||
"install": {
|
||||
@@ -306,10 +316,18 @@
|
||||
"body":"Removing the node will uninstall it from Node-RED. The node may continue to use resources until Node-RED is restarted.",
|
||||
"title": "Remove nodes"
|
||||
},
|
||||
"update": {
|
||||
"body":"Updating the node will require a restart of Node-RED to complete the update. This must be done manually.",
|
||||
"title": "Update nodes"
|
||||
},
|
||||
"cannotUpdate": {
|
||||
"body":"An update for this node is available, but it is not installed in a location that the palette manager can update.<br/><br/>Please refer to the documentation for how to update this node."
|
||||
},
|
||||
"button": {
|
||||
"review": "Open node information",
|
||||
"install": "Install",
|
||||
"remove": "Remove"
|
||||
"remove": "Remove",
|
||||
"update": "Update"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,34 +48,47 @@ module.exports = {
|
||||
}
|
||||
var node = req.body;
|
||||
var promise;
|
||||
var isUpgrade = false;
|
||||
if (node.module) {
|
||||
var module = redNodes.getModuleInfo(node.module);
|
||||
if (module) {
|
||||
log.audit({event: "nodes.install",module:node.module,error:"module_already_loaded"},req);
|
||||
res.status(400).json({error:"module_already_loaded", message:"Module already loaded"});
|
||||
return;
|
||||
if (!node.version || module.version === node.version) {
|
||||
log.audit({event: "nodes.install",module:node.module, version:node.version, error:"module_already_loaded"},req);
|
||||
res.status(400).json({error:"module_already_loaded", message:"Module already loaded"});
|
||||
return;
|
||||
}
|
||||
if (!module.local) {
|
||||
log.audit({event: "nodes.install",module:node.module, version:node.version, error:"module_not_local"},req);
|
||||
res.status(400).json({error:"module_not_local", message:"Module not locally installed"});
|
||||
return;
|
||||
}
|
||||
isUpgrade = true;
|
||||
}
|
||||
promise = redNodes.installModule(node.module);
|
||||
promise = redNodes.installModule(node.module,node.version);
|
||||
} else {
|
||||
log.audit({event: "nodes.install",module:node.module,error:"invalid_request"},req);
|
||||
res.status(400).json({error:"invalid_request", message:"Invalid request"});
|
||||
return;
|
||||
}
|
||||
promise.then(function(info) {
|
||||
comms.publish("node/added",info.nodes,false);
|
||||
if (isUpgrade) {
|
||||
comms.publish("node/upgraded",{module:node.module,version:node.version},false);
|
||||
} else {
|
||||
comms.publish("node/added",info.nodes,false);
|
||||
}
|
||||
if (node.module) {
|
||||
log.audit({event: "nodes.install",module:node.module},req);
|
||||
log.audit({event: "nodes.install",module:node.module,version:node.version},req);
|
||||
res.json(info);
|
||||
}
|
||||
}).otherwise(function(err) {
|
||||
if (err.code === 404) {
|
||||
log.audit({event: "nodes.install",module:node.module,error:"not_found"},req);
|
||||
log.audit({event: "nodes.install",module:node.module,version:node.version,error:"not_found"},req);
|
||||
res.status(404).end();
|
||||
} else if (err.code) {
|
||||
log.audit({event: "nodes.install",module:node.module,error:err.code},req);
|
||||
log.audit({event: "nodes.install",module:node.module,version:node.version,error:err.code},req);
|
||||
res.status(400).json({error:err.code, message:err.message});
|
||||
} else {
|
||||
log.audit({event: "nodes.install",module:node.module,error:err.code||"unexpected_error",message:err.toString()},req);
|
||||
log.audit({event: "nodes.install",module:node.module,version:node.version,error:err.code||"unexpected_error",message:err.toString()},req);
|
||||
res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -16,31 +16,20 @@
|
||||
var express = require('express');
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
var Mustache = require("mustache");
|
||||
|
||||
var theme = require("./theme");
|
||||
|
||||
var Mustache = require("mustache");
|
||||
var redNodes;
|
||||
|
||||
var icon_paths = [path.resolve(__dirname + '/../../public/icons')];
|
||||
var iconCache = {};
|
||||
//TODO: create a default icon
|
||||
var defaultIcon = path.resolve(__dirname + '/../../public/icons/arrow-in.png');
|
||||
var templateDir = path.resolve(__dirname+"/../../editor/templates");
|
||||
var editorTemplate;
|
||||
|
||||
function nodeIconDir(dir) {
|
||||
icon_paths.push(path.resolve(dir));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: function(runtime) {
|
||||
redNodes = runtime.nodes;
|
||||
editorTemplate = fs.readFileSync(path.join(templateDir,"index.mst"),"utf8");
|
||||
Mustache.parse(editorTemplate);
|
||||
// TODO: this allows init to be called multiple times without
|
||||
// registering multiple instances of the listener.
|
||||
// It isn't.... ideal.
|
||||
runtime.events.removeListener("node-icon-dir",nodeIconDir);
|
||||
runtime.events.on("node-icon-dir",nodeIconDir);
|
||||
},
|
||||
|
||||
ensureSlash: function(req,res,next) {
|
||||
@@ -54,22 +43,10 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
icon: function(req,res) {
|
||||
if (iconCache[req.params.icon]) {
|
||||
res.sendFile(iconCache[req.params.icon]); // if not found, express prints this to the console and serves 404
|
||||
} else {
|
||||
for (var p=0;p<icon_paths.length;p++) {
|
||||
var iconPath = path.join(icon_paths[p],req.params.icon);
|
||||
try {
|
||||
fs.statSync(iconPath);
|
||||
res.sendFile(iconPath);
|
||||
iconCache[req.params.icon] = iconPath;
|
||||
return;
|
||||
} catch(err) {
|
||||
// iconPath doesn't exist
|
||||
}
|
||||
}
|
||||
res.sendFile(defaultIcon);
|
||||
}
|
||||
var icon = req.params.icon;
|
||||
var module = req.params.module;
|
||||
var iconPath = redNodes.getNodeIconPath(module,icon);
|
||||
res.sendFile(iconPath);
|
||||
},
|
||||
editor: function(req,res) {
|
||||
res.send(Mustache.render(editorTemplate,theme.context()));
|
||||
|
||||
@@ -126,14 +126,19 @@ function start() {
|
||||
var missingModules = {};
|
||||
for (i=0;i<nodeMissing.length;i++) {
|
||||
var missing = nodeMissing[i];
|
||||
missingModules[missing.module] = (missingModules[missing.module]||[]).concat(missing.types);
|
||||
missingModules[missing.module] = missingModules[missing.module]||{
|
||||
module:missing.module,
|
||||
version:missing.pending_version||missing.version,
|
||||
types:[]
|
||||
}
|
||||
missingModules[missing.module].types = missingModules[missing.module].types.concat(missing.types);
|
||||
}
|
||||
var promises = [];
|
||||
for (i in missingModules) {
|
||||
if (missingModules.hasOwnProperty(i)) {
|
||||
log.warn(" - "+i+": "+missingModules[i].join(", "));
|
||||
log.warn(" - "+i+" ("+missingModules[i].version+"): "+missingModules[i].types.join(", "));
|
||||
if (settings.autoInstallModules && i != "node-red") {
|
||||
redNodes.installModule(i).otherwise(function(err) {
|
||||
redNodes.installModule(i,missingModules[i].version).otherwise(function(err) {
|
||||
// Error already reported. Need the otherwise handler
|
||||
// to stop the error propagating any further
|
||||
});
|
||||
|
||||
@@ -24,12 +24,14 @@
|
||||
"removed-types": "Removed node types:",
|
||||
"install": {
|
||||
"invalid": "Invalid module name",
|
||||
"installing": "Installing module: __name__",
|
||||
"installing": "Installing module: __name__, version: __version__",
|
||||
"installed": "Installed module: __name__",
|
||||
"install-failed": "Install failed",
|
||||
"install-failed-long": "Installation of module __name__ failed:",
|
||||
"install-failed-not-found": "$t(install-failed-long) module not found",
|
||||
|
||||
"upgrading": "Upgrading module: __name__ to version: __version__",
|
||||
"upgraded": "Upgraded module: __name__. Restart Node-RED to use the new version",
|
||||
"upgrade-failed-not-found": "$t(server.install.install-failed-long) version not found",
|
||||
"uninstalling": "Uninstalling module: __name__",
|
||||
"uninstall-failed": "Uninstall failed",
|
||||
"uninstall-failed-long": "Uninstall of module __name__ failed:",
|
||||
|
||||
@@ -25,6 +25,7 @@ var flowUtil = require("./flows/util")
|
||||
var context = require("./context");
|
||||
var Node = require("./Node");
|
||||
var log = require("../log");
|
||||
var library = require("./library");
|
||||
|
||||
var events = require("../events");
|
||||
|
||||
@@ -49,8 +50,13 @@ function registerType(nodeSet,type,constructor,opts) {
|
||||
type = nodeSet;
|
||||
nodeSet = "";
|
||||
}
|
||||
if (opts && opts.credentials) {
|
||||
credentials.register(type,opts.credentials);
|
||||
if (opts) {
|
||||
if (opts.credentials) {
|
||||
credentials.register(type,opts.credentials);
|
||||
}
|
||||
if (opts.settings) {
|
||||
settings.registerNodeSettings(type,opts.settings);
|
||||
}
|
||||
}
|
||||
registry.registerType(nodeSet,type,constructor);
|
||||
}
|
||||
@@ -88,6 +94,7 @@ function init(runtime) {
|
||||
flows.init(runtime);
|
||||
registry.init(runtime);
|
||||
context.init(runtime.settings);
|
||||
library.init(runtime);
|
||||
}
|
||||
|
||||
function disableNode(id) {
|
||||
@@ -135,6 +142,9 @@ module.exports = {
|
||||
|
||||
getNodeConfigs: registry.getNodeConfigs,
|
||||
getNodeConfig: registry.getNodeConfig,
|
||||
getNodeIconPath: registry.getNodeIconPath,
|
||||
getNodeExampleFlows: library.getExampleFlows,
|
||||
getNodeExampleFlowPath: library.getExampleFlowPath,
|
||||
|
||||
clearRegistry: registry.clear,
|
||||
cleanModuleList: registry.cleanModuleList,
|
||||
|
||||
108
red/runtime/nodes/library.js
Normal file
108
red/runtime/nodes/library.js
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var fs = require('fs');
|
||||
var fspath = require('path');
|
||||
var when = require('when');
|
||||
|
||||
var runtime;
|
||||
|
||||
var exampleRoots = {};
|
||||
var exampleFlows = null;
|
||||
|
||||
function getFlowsFromPath(path) {
|
||||
return when.promise(function(resolve,reject) {
|
||||
var result = {};
|
||||
fs.readdir(path,function(err,files) {
|
||||
var promises = [];
|
||||
var validFiles = [];
|
||||
files.forEach(function(file) {
|
||||
var fullPath = fspath.join(path,file);
|
||||
var stats = fs.lstatSync(fullPath);
|
||||
if (stats.isDirectory()) {
|
||||
validFiles.push(file);
|
||||
promises.push(getFlowsFromPath(fullPath));
|
||||
} else if (/\.json$/.test(file)){
|
||||
validFiles.push(file);
|
||||
promises.push(when.resolve(file.split(".")[0]))
|
||||
}
|
||||
})
|
||||
var i=0;
|
||||
when.all(promises).then(function(results) {
|
||||
results.forEach(function(r) {
|
||||
if (typeof r === 'string') {
|
||||
result.f = result.f||[];
|
||||
result.f.push(r);
|
||||
} else {
|
||||
result.d = result.d||{};
|
||||
result.d[validFiles[i]] = r;
|
||||
}
|
||||
i++;
|
||||
})
|
||||
|
||||
resolve(result);
|
||||
})
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function addNodeExamplesDir(module) {
|
||||
exampleRoots[module.name] = module.path;
|
||||
getFlowsFromPath(module.path).then(function(result) {
|
||||
exampleFlows = exampleFlows||{d:{}};
|
||||
exampleFlows.d[module.name] = result;
|
||||
});
|
||||
}
|
||||
function removeNodeExamplesDir(module) {
|
||||
delete exampleRoots[module];
|
||||
if (exampleFlows && exampleFlows.d) {
|
||||
delete exampleFlows.d[module];
|
||||
}
|
||||
if (exampleFlows && Object.keys(exampleFlows.d).length === 0) {
|
||||
exampleFlows = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function init(_runtime) {
|
||||
|
||||
runtime = _runtime;
|
||||
|
||||
exampleRoots = {};
|
||||
exampleFlows = null;
|
||||
|
||||
runtime.events.removeListener("node-examples-dir",addNodeExamplesDir);
|
||||
runtime.events.on("node-examples-dir",addNodeExamplesDir);
|
||||
runtime.events.removeListener("node-module-uninstalled",removeNodeExamplesDir);
|
||||
runtime.events.on("node-module-uninstalled",removeNodeExamplesDir);
|
||||
}
|
||||
|
||||
function getExampleFlows() {
|
||||
return exampleFlows;
|
||||
}
|
||||
|
||||
function getExampleFlowPath(module,path) {
|
||||
if (exampleRoots[module]) {
|
||||
return fspath.join(exampleRoots[module],path)+".json";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: init,
|
||||
getExampleFlows: getExampleFlows,
|
||||
getExampleFlowPath: getExampleFlowPath
|
||||
}
|
||||
@@ -70,6 +70,7 @@ module.exports = {
|
||||
|
||||
getNodeConfigs: registry.getAllNodeConfigs,
|
||||
getNodeConfig: registry.getNodeConfig,
|
||||
getNodeIconPath: registry.getNodeIconPath,
|
||||
|
||||
enableNode: enableNodeSet,
|
||||
disableNode: registry.disableNodeSet,
|
||||
|
||||
@@ -59,45 +59,63 @@ function checkModulePath(folder) {
|
||||
return moduleName;
|
||||
}
|
||||
|
||||
function checkExistingModule(module) {
|
||||
if (registry.getModuleInfo(module)) {
|
||||
// TODO: nls
|
||||
var err = new Error("Module already loaded");
|
||||
err.code = "module_already_loaded";
|
||||
throw err;
|
||||
function checkExistingModule(module,version) {
|
||||
var info = registry.getModuleInfo(module);
|
||||
if (info) {
|
||||
if (!version || info.version === version) {
|
||||
var err = new Error("Module already loaded");
|
||||
err.code = "module_already_loaded";
|
||||
throw err;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function installModule(module) {
|
||||
function installModule(module,version) {
|
||||
//TODO: ensure module is 'safe'
|
||||
return when.promise(function(resolve,reject) {
|
||||
var installName = module;
|
||||
|
||||
var isUpgrade = false;
|
||||
try {
|
||||
if (moduleRe.test(module)) {
|
||||
// Simple module name - assume it can be npm installed
|
||||
if (version) {
|
||||
installName += "@"+version;
|
||||
}
|
||||
} else if (slashRe.test(module)) {
|
||||
// A path - check if there's a valid package.json
|
||||
installName = module;
|
||||
module = checkModulePath(module);
|
||||
}
|
||||
checkExistingModule(module);
|
||||
isUpgrade = checkExistingModule(module,version);
|
||||
} catch(err) {
|
||||
return reject(err);
|
||||
}
|
||||
log.info(log._("server.install.installing",{name: module}));
|
||||
if (!isUpgrade) {
|
||||
log.info(log._("server.install.installing",{name: module,version: version||"latest"}));
|
||||
} else {
|
||||
log.info(log._("server.install.upgrading",{name: module,version: version||"latest"}));
|
||||
}
|
||||
|
||||
var installDir = settings.userDir || process.env.NODE_RED_HOME || ".";
|
||||
var child = child_process.execFile(npmCommand,['install','--production',installName],
|
||||
var child = child_process.execFile(npmCommand,['install','--save','--save-exact','--production',installName],
|
||||
{
|
||||
cwd: installDir
|
||||
},
|
||||
function(err, stdin, stdout) {
|
||||
if (err) {
|
||||
var lookFor404 = new RegExp(" 404 .*"+installName+"$","m");
|
||||
var e;
|
||||
var lookFor404 = new RegExp(" 404 .*"+module+"$","m");
|
||||
var lookForVersionNotFound = new RegExp("version not found: "+module+"@"+version,"m");
|
||||
if (lookFor404.test(stdout)) {
|
||||
log.warn(log._("server.install.install-failed-not-found",{name:module}));
|
||||
var e = new Error("Module not found");
|
||||
e = new Error("Module not found");
|
||||
e.code = 404;
|
||||
reject(e);
|
||||
} else if (isUpgrade && lookForVersionNotFound.test(stdout)) {
|
||||
log.warn(log._("server.install.upgrade-failed-not-found",{name:module}));
|
||||
e = new Error("Module not found");
|
||||
e.code = 404;
|
||||
reject(e);
|
||||
} else {
|
||||
@@ -108,8 +126,14 @@ function installModule(module) {
|
||||
reject(new Error(log._("server.install.install-failed")));
|
||||
}
|
||||
} else {
|
||||
log.info(log._("server.install.installed",{name:module}));
|
||||
resolve(require("./index").addModule(module).then(reportAddedModules));
|
||||
if (!isUpgrade) {
|
||||
log.info(log._("server.install.installed",{name:module}));
|
||||
resolve(require("./index").addModule(module).then(reportAddedModules));
|
||||
} else {
|
||||
log.info(log._("server.install.upgraded",{name:module, version:version}));
|
||||
events.emit("runtime-event",{id:"restart-required",type:"warning",text:"notification.warnings.restartRequired"});
|
||||
resolve(require("./registry").setModulePendingUpdated(module,version));
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -162,7 +186,7 @@ function uninstallModule(module) {
|
||||
|
||||
var list = registry.removeModule(module);
|
||||
log.info(log._("server.install.uninstalling",{name:module}));
|
||||
var child = child_process.execFile(npmCommand,['remove',module],
|
||||
var child = child_process.execFile(npmCommand,['remove','--save',module],
|
||||
{
|
||||
cwd: installDir
|
||||
},
|
||||
|
||||
@@ -89,7 +89,7 @@ function getLocalNodeFiles(dir) {
|
||||
if (!/^(\..*|lib|icons|node_modules|test|locales)$/.test(fn)) {
|
||||
result = result.concat(getLocalNodeFiles(path.join(dir,fn)));
|
||||
} else if (fn === "icons") {
|
||||
events.emit("node-icon-dir",path.join(dir,fn));
|
||||
events.emit("node-icon-dir",{name:'node-red',path:path.join(dir,fn)});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -182,7 +182,7 @@ function getModuleNodeFiles(module) {
|
||||
if (iconDirs.indexOf(iconDir) == -1) {
|
||||
try {
|
||||
fs.statSync(iconDir);
|
||||
events.emit("node-icon-dir",iconDir);
|
||||
events.emit("node-icon-dir",{name:pkg.name,path:iconDir});
|
||||
iconDirs.push(iconDir);
|
||||
} catch(err) {
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
//var UglifyJS = require("uglify-js");
|
||||
var util = require("util");
|
||||
var when = require("when");
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
|
||||
var events = require("../../events");
|
||||
|
||||
var settings;
|
||||
@@ -41,6 +44,9 @@ function init(_settings,_loader) {
|
||||
nodeList = [];
|
||||
nodeConfigCache = null;
|
||||
Node = require("../Node");
|
||||
events.removeListener("node-icon-dir",nodeIconDir);
|
||||
events.on("node-icon-dir",nodeIconDir);
|
||||
|
||||
}
|
||||
|
||||
function load() {
|
||||
@@ -82,7 +88,8 @@ function getNode(id) {
|
||||
|
||||
function saveNodeList() {
|
||||
var moduleList = {};
|
||||
|
||||
var hadPending = false;
|
||||
var hasPending = false;
|
||||
for (var module in moduleConfigs) {
|
||||
/* istanbul ignore else */
|
||||
if (moduleConfigs.hasOwnProperty(module)) {
|
||||
@@ -94,6 +101,15 @@ function saveNodeList() {
|
||||
local: moduleConfigs[module].local||false,
|
||||
nodes: {}
|
||||
};
|
||||
if (moduleConfigs[module].hasOwnProperty('pending_version')) {
|
||||
hadPending = true;
|
||||
if (moduleConfigs[module].pending_version !== moduleConfigs[module].version) {
|
||||
moduleList[module].pending_version = moduleConfigs[module].pending_version;
|
||||
hasPending = true;
|
||||
} else {
|
||||
delete moduleConfigs[module].pending_version;
|
||||
}
|
||||
}
|
||||
}
|
||||
var nodes = moduleConfigs[module].nodes;
|
||||
for(var node in nodes) {
|
||||
@@ -111,6 +127,9 @@ function saveNodeList() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hadPending && !hasPending) {
|
||||
events.emit("runtime-event",{id:"restart-required"});
|
||||
}
|
||||
if (settings.available()) {
|
||||
return settings.set("nodes",moduleList);
|
||||
} else {
|
||||
@@ -280,6 +299,9 @@ function getNodeList(filter) {
|
||||
if (nodes.hasOwnProperty(node)) {
|
||||
var nodeInfo = filterNodeInfo(nodes[node]);
|
||||
nodeInfo.version = moduleConfigs[module].version;
|
||||
if (moduleConfigs[module].pending_version) {
|
||||
nodeInfo.pending_version = moduleConfigs[module].pending_version;
|
||||
}
|
||||
if (!filter || filter(nodes[node])) {
|
||||
list.push(nodeInfo);
|
||||
}
|
||||
@@ -471,6 +493,7 @@ function enableNodeSet(typeOrId) {
|
||||
delete config.err;
|
||||
config.enabled = true;
|
||||
nodeConfigCache = null;
|
||||
settings.enableNodeSettings(config.types);
|
||||
return saveNodeList().then(function() {
|
||||
return filterNodeInfo(config);
|
||||
});
|
||||
@@ -493,6 +516,7 @@ function disableNodeSet(typeOrId) {
|
||||
// TODO: persist setting
|
||||
config.enabled = false;
|
||||
nodeConfigCache = null;
|
||||
settings.disableNodeSettings(config.types);
|
||||
return saveNodeList().then(function() {
|
||||
return filterNodeInfo(config);
|
||||
});
|
||||
@@ -539,6 +563,46 @@ function cleanModuleList() {
|
||||
saveNodeList();
|
||||
}
|
||||
}
|
||||
function setModulePendingUpdated(module,version) {
|
||||
moduleConfigs[module].pending_version = version;
|
||||
return saveNodeList().then(function() {
|
||||
return getModuleInfo(module);
|
||||
});
|
||||
}
|
||||
|
||||
var icon_paths = {
|
||||
"node-red":[path.resolve(__dirname + '/../../../../public/icons')]
|
||||
};
|
||||
var iconCache = {};
|
||||
var defaultIcon = path.resolve(__dirname + '/../../../../public/icons/arrow-in.png');
|
||||
|
||||
function nodeIconDir(dir) {
|
||||
icon_paths[dir.name] = icon_paths[dir.name] || [];
|
||||
icon_paths[dir.name].push(path.resolve(dir.path));
|
||||
}
|
||||
|
||||
function getNodeIconPath(module,icon) {
|
||||
var iconName = module+"/"+icon;
|
||||
if (iconCache[iconName]) {
|
||||
return iconCache[iconName];
|
||||
} else {
|
||||
var paths = icon_paths[module];
|
||||
if (paths) {
|
||||
for (var p=0;p<paths.length;p++) {
|
||||
var iconPath = path.join(paths[p],icon);
|
||||
try {
|
||||
fs.statSync(iconPath);
|
||||
iconCache[iconName] = iconPath;
|
||||
return iconPath;
|
||||
} catch(err) {
|
||||
// iconPath doesn't exist
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return defaultIcon;
|
||||
}
|
||||
}
|
||||
|
||||
var registry = module.exports = {
|
||||
init: init,
|
||||
@@ -552,6 +616,7 @@ var registry = module.exports = {
|
||||
enableNodeSet: enableNodeSet,
|
||||
disableNodeSet: disableNodeSet,
|
||||
|
||||
setModulePendingUpdated: setModulePendingUpdated,
|
||||
removeModule: removeModule,
|
||||
|
||||
getNodeInfo: getNodeInfo,
|
||||
@@ -560,6 +625,7 @@ var registry = module.exports = {
|
||||
getModuleList: getModuleList,
|
||||
getModuleInfo: getModuleInfo,
|
||||
|
||||
getNodeIconPath: getNodeIconPath,
|
||||
/**
|
||||
* Gets all of the node template configs
|
||||
* @return all of the node templates in a single string
|
||||
|
||||
@@ -18,9 +18,12 @@ var when = require("when");
|
||||
var clone = require("clone");
|
||||
var assert = require("assert");
|
||||
var log = require("./log");
|
||||
var util = require("./util");
|
||||
|
||||
var userSettings = null;
|
||||
var globalSettings = null;
|
||||
var nodeSettings = null;
|
||||
var disableNodeSettings = null;
|
||||
var storage = null;
|
||||
|
||||
var persistentSettings = {
|
||||
@@ -38,6 +41,8 @@ var persistentSettings = {
|
||||
}
|
||||
}
|
||||
globalSettings = null;
|
||||
nodeSettings = {};
|
||||
disableNodeSettings = {};
|
||||
},
|
||||
load: function(_storage) {
|
||||
storage = _storage;
|
||||
@@ -99,6 +104,53 @@ var persistentSettings = {
|
||||
userSettings = null;
|
||||
globalSettings = null;
|
||||
storage = null;
|
||||
},
|
||||
registerNodeSettings: function(type, opts) {
|
||||
try {
|
||||
for (var property in opts) {
|
||||
if (opts.hasOwnProperty(property)) {
|
||||
var normalisedType = util.normaliseNodeTypeName(type);
|
||||
if (!property.startsWith(normalisedType)) {
|
||||
throw new Error("The name of node setting property " + property + " must start with \"" + normalisedType + "\" (case sensitive).");
|
||||
}
|
||||
}
|
||||
}
|
||||
nodeSettings[type] = opts;
|
||||
} catch (err) {
|
||||
console.log(err.toString());
|
||||
}
|
||||
},
|
||||
exportNodeSettings: function(safeSettings) {
|
||||
safeSettings["nodeSettings"] = {};
|
||||
for (var type in nodeSettings) {
|
||||
if (nodeSettings.hasOwnProperty(type) && !disableNodeSettings[type]) {
|
||||
var nodeTypeSettings = nodeSettings[type];
|
||||
for (var property in nodeTypeSettings) {
|
||||
if (nodeTypeSettings.hasOwnProperty(property)) {
|
||||
var setting = nodeTypeSettings[property];
|
||||
if (setting.exportable) {
|
||||
if (userSettings.hasOwnProperty(property)) {
|
||||
safeSettings["nodeSettings"][property] = userSettings[property];
|
||||
} else {
|
||||
safeSettings["nodeSettings"][property] = setting.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return safeSettings;
|
||||
},
|
||||
enableNodeSettings: function(types) {
|
||||
types.forEach(function(type) {
|
||||
disableNodeSettings[type] = false;
|
||||
});
|
||||
},
|
||||
disableNodeSettings: function(types) {
|
||||
types.forEach(function(type) {
|
||||
disableNodeSettings[type] = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -230,11 +230,25 @@ var localfilesystem = {
|
||||
|
||||
globalSettingsFile = fspath.join(settings.userDir,".config.json");
|
||||
|
||||
var packageFile = fspath.join(settings.userDir,"package.json");
|
||||
var packagePromise = when.resolve();
|
||||
if (!settings.readOnly) {
|
||||
promises.push(promiseDir(libFlowsDir));
|
||||
packagePromise = function() {
|
||||
try {
|
||||
fs.statSync(packageFile);
|
||||
} catch(err) {
|
||||
var defaultPackage = {
|
||||
"name": "node-red-project",
|
||||
"description": "A Node-RED Project",
|
||||
"version": "0.0.1"
|
||||
};
|
||||
return writeFile(packageFile,JSON.stringify(defaultPackage,"",4));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return when.all(promises);
|
||||
return when.all(promises).then(packagePromise);
|
||||
},
|
||||
|
||||
getFlows: function() {
|
||||
|
||||
@@ -328,6 +328,18 @@ function evaluateNodeProperty(value, type, node, msg) {
|
||||
return value;
|
||||
}
|
||||
|
||||
function normaliseNodeTypeName(name) {
|
||||
var result = name.replace(/[^a-zA-Z0-9]/g, " ");
|
||||
result = result.trim();
|
||||
result = result.replace(/ +/g, " ");
|
||||
result = result.replace(/ ./g,
|
||||
function(s) {
|
||||
return s.charAt(1).toUpperCase();
|
||||
}
|
||||
);
|
||||
result = result.charAt(0).toLowerCase() + result.slice(1);
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ensureString: ensureString,
|
||||
@@ -338,5 +350,6 @@ module.exports = {
|
||||
getMessageProperty: getMessageProperty,
|
||||
setMessageProperty: setMessageProperty,
|
||||
evaluateNodeProperty: evaluateNodeProperty,
|
||||
normalisePropertyExpression: normalisePropertyExpression
|
||||
normalisePropertyExpression: normalisePropertyExpression,
|
||||
normaliseNodeTypeName: normaliseNodeTypeName
|
||||
};
|
||||
|
||||
19
settings.js
19
settings.js
@@ -167,6 +167,25 @@ module.exports = {
|
||||
// next();
|
||||
//},
|
||||
|
||||
// The following property can be used to verify websocket connection attempts.
|
||||
// This allows, for example, the HTTP request headers to be checked to ensure
|
||||
// they include valid authentication information.
|
||||
//webSocketVerifyClient: function(info) {
|
||||
// // 'info' has three properties:
|
||||
// // - origin : the value in the Origin header
|
||||
// // - req : the HTTP request
|
||||
// // - secure : true if req.connection.authorized or req.connection.encrypted is set
|
||||
// //
|
||||
// // The function should return true if the connection should be accepted, false otherwise.
|
||||
// //
|
||||
// // Alternatively, if this function is defined to accept a second argument, callback,
|
||||
// // it can be used to verify the client asynchronously.
|
||||
// // The callback takes three arguments:
|
||||
// // - result : boolean, whether to accept the connection or not
|
||||
// // - code : if result is false, the HTTP error status to return
|
||||
// // - reason: if result is false, the HTTP reason string to return
|
||||
//},
|
||||
|
||||
// Anything in this hash is globally available to all functions.
|
||||
// It is accessed as context.global.
|
||||
// eg:
|
||||
|
||||
@@ -58,42 +58,42 @@ describe('exec node', function() {
|
||||
arg3(null,arg1,arg1.toUpperCase());
|
||||
});
|
||||
|
||||
helper.load(execNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
var n3 = helper.getNode("n3");
|
||||
var n4 = helper.getNode("n4");
|
||||
var received = 0;
|
||||
var messages = [null,null];
|
||||
var completeTest = function() {
|
||||
received++;
|
||||
if (received < 2) {
|
||||
return;
|
||||
}
|
||||
try{
|
||||
var msg = messages[0];
|
||||
msg.should.have.property("payload");
|
||||
msg.payload.should.be.a.String();
|
||||
msg.payload.should.equal("echo");
|
||||
helper.load(execNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
var n3 = helper.getNode("n3");
|
||||
var n4 = helper.getNode("n4");
|
||||
var received = 0;
|
||||
var messages = [null,null];
|
||||
var completeTest = function() {
|
||||
received++;
|
||||
if (received < 2) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var msg = messages[0];
|
||||
msg.should.have.property("payload");
|
||||
msg.payload.should.be.a.String();
|
||||
msg.payload.should.equal("echo");
|
||||
|
||||
msg = messages[1];
|
||||
msg.should.have.property("payload");
|
||||
msg.payload.should.be.a.String,
|
||||
msg.payload.should.equal("ECHO");
|
||||
child_process.exec.restore();
|
||||
done();
|
||||
} catch(err) {
|
||||
child_process.exec.restore();
|
||||
done(err);
|
||||
}
|
||||
};
|
||||
n2.on("input", function(msg) {
|
||||
messages[0] = msg;
|
||||
completeTest();
|
||||
});
|
||||
n3.on("input", function(msg) {
|
||||
messages[1] = msg;
|
||||
completeTest();
|
||||
msg = messages[1];
|
||||
msg.should.have.property("payload");
|
||||
msg.payload.should.be.a.String;
|
||||
msg.payload.should.equal("ECHO");
|
||||
child_process.exec.restore();
|
||||
done();
|
||||
} catch(err) {
|
||||
child_process.exec.restore();
|
||||
done(err);
|
||||
}
|
||||
};
|
||||
n2.on("input", function(msg) {
|
||||
messages[0] = msg;
|
||||
completeTest();
|
||||
});
|
||||
n3.on("input", function(msg) {
|
||||
messages[1] = msg;
|
||||
completeTest();
|
||||
});
|
||||
n1.receive({payload:"and"});
|
||||
});
|
||||
@@ -121,7 +121,7 @@ describe('exec node', function() {
|
||||
if (received < 2) {
|
||||
return;
|
||||
}
|
||||
try{
|
||||
try {
|
||||
var msg = messages[0];
|
||||
msg.should.have.property("payload");
|
||||
msg.payload.should.be.a.String();
|
||||
@@ -210,6 +210,27 @@ describe('exec node', function() {
|
||||
n1.receive({});
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to kill a long running command', function(done) {
|
||||
var flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"sleep", addpay:false, append:"1", timer:"2"},
|
||||
{id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}];
|
||||
helper.load(execNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
var n3 = helper.getNode("n3");
|
||||
var n4 = helper.getNode("n4");
|
||||
n4.on("input", function(msg) {
|
||||
msg.should.have.property("payload");
|
||||
msg.payload.should.have.property("killed",true);
|
||||
msg.payload.should.have.property("signal","SIGTERM");
|
||||
done();
|
||||
});
|
||||
setTimeout(function() {
|
||||
n1.receive({kill:true});
|
||||
},150);
|
||||
n1.receive({});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('calling spawn', function() {
|
||||
@@ -297,7 +318,7 @@ describe('exec node', function() {
|
||||
if (received < 2) {
|
||||
return;
|
||||
}
|
||||
try{
|
||||
try {
|
||||
var msg = messages[0];
|
||||
msg.should.have.property("payload");
|
||||
msg.payload.should.be.a.String();
|
||||
@@ -401,5 +422,26 @@ describe('exec node', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to kill a long running command', function(done) {
|
||||
var flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"sleep", addpay:false, append:"1", timer:"2"},
|
||||
{id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}];
|
||||
helper.load(execNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
var n3 = helper.getNode("n3");
|
||||
var n4 = helper.getNode("n4");
|
||||
n4.on("input", function(msg) {
|
||||
msg.should.have.property("payload");
|
||||
msg.payload.should.have.property("killed",true);
|
||||
msg.payload.should.have.property("signal","SIGTERM");
|
||||
done();
|
||||
});
|
||||
setTimeout(function() {
|
||||
n1.receive({kill:true});
|
||||
},150);
|
||||
n1.receive({});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -356,7 +356,7 @@ describe('trigger node', function() {
|
||||
});
|
||||
|
||||
it('should be able to set infinite timeout, and clear timeout', function(done) {
|
||||
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", duration:-5, wires:[["n2"]] },
|
||||
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", duration:0, wires:[["n2"]] },
|
||||
{id:"n2", type:"helper"} ];
|
||||
helper.load(triggerNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
@@ -378,7 +378,7 @@ describe('trigger node', function() {
|
||||
});
|
||||
|
||||
it('should be able to set infinite timeout, and clear timeout by message', function(done) {
|
||||
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", reset:"boo", duration:-5, wires:[["n2"]] },
|
||||
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", reset:"boo", duration:0, wires:[["n2"]] },
|
||||
{id:"n2", type:"helper"} ];
|
||||
helper.load(triggerNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
@@ -399,4 +399,26 @@ describe('trigger node', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to set a repeat, and clear loop by reset', function(done) {
|
||||
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", reset:"boo", duration:-25, wires:[["n2"]] },
|
||||
{id:"n2", type:"helper"} ];
|
||||
helper.load(triggerNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
var c = 0;
|
||||
n2.on("input", function(msg) {
|
||||
c += 1;
|
||||
msg.should.have.a.property("payload", "foo");
|
||||
});
|
||||
n1.emit("input", {payload:"foo"}); // trigger
|
||||
setTimeout( function() {
|
||||
n1.emit("input", {reset:true}); // reset
|
||||
},90);
|
||||
setTimeout( function() {
|
||||
c.should.equal(4); // should send foo 4 times.
|
||||
done();
|
||||
},150);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -179,6 +179,21 @@ describe('CSV node', function() {
|
||||
n1.emit("input", {payload:testString});
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle numbers in strings but not IP addresses', function(done) {
|
||||
var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e", wires:[["n2"]] },
|
||||
{id:"n2", type:"helper"} ];
|
||||
helper.load(csvNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
n2.on("input", function(msg) {
|
||||
msg.should.have.property('payload', { a: "a", b: "127.0.0.1", c: 56.7, d: -32.8, e: "+76.22C" });
|
||||
done();
|
||||
});
|
||||
var testString = "a,127.0.0.1,56.7,-32.8,+76.22C";
|
||||
n1.emit("input", {payload:testString});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('json object to csv', function() {
|
||||
|
||||
@@ -36,6 +36,7 @@ var credentials = require("../../red/runtime/nodes/credentials");
|
||||
var comms = require("../../red/api/comms.js");
|
||||
var log = require("../../red/runtime/log.js");
|
||||
var context = require("../../red/runtime/nodes/context.js");
|
||||
var events = require("../../red/runtime/events.js");
|
||||
|
||||
var http = require('http');
|
||||
var express = require('express');
|
||||
@@ -92,7 +93,7 @@ module.exports = {
|
||||
return messageId;
|
||||
};
|
||||
|
||||
redNodes.init({settings:settings, storage:storage,log:log});
|
||||
redNodes.init({events:events,settings:settings, storage:storage,log:log,});
|
||||
RED.nodes.registerType("helper", helperNode);
|
||||
if (Array.isArray(testNode)) {
|
||||
for (i = 0; i < testNode.length; i++) {
|
||||
|
||||
@@ -29,7 +29,7 @@ describe("api index", function() {
|
||||
describe("disables editor", function() {
|
||||
before(function() {
|
||||
api.init({},{
|
||||
settings:{httpNodeRoot:true, httpAdminRoot: true,disableEditor:true},
|
||||
settings:{httpNodeRoot:true, httpAdminRoot: true,disableEditor:true, exportNodeSettings: function() {}},
|
||||
events: {on:function(){},removeListener: function(){}},
|
||||
log: {info:function(){},_:function(){}},
|
||||
nodes: {paletteEditorEnabled: function(){return true}}
|
||||
|
||||
@@ -42,7 +42,10 @@ describe("info api", function() {
|
||||
foo: 123,
|
||||
httpNodeRoot: "testHttpNodeRoot",
|
||||
version: "testVersion",
|
||||
paletteCategories :["red","blue","green"]
|
||||
paletteCategories :["red","blue","green"],
|
||||
exportNodeSettings: function(obj) {
|
||||
obj.testNodeSetting = "helloWorld";
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
paletteEditorEnabled: function() { return true; }
|
||||
@@ -59,7 +62,9 @@ describe("info api", function() {
|
||||
res.body.should.have.property("version","testVersion");
|
||||
res.body.should.have.property("paletteCategories",["red","blue","green"]);
|
||||
res.body.should.have.property("editorTheme",{test:456});
|
||||
res.body.should.have.property("testNodeSetting","helloWorld");
|
||||
res.body.should.not.have.property("foo",123);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -68,8 +73,8 @@ describe("info api", function() {
|
||||
settings: {
|
||||
httpNodeRoot: "testHttpNodeRoot",
|
||||
version: "testVersion",
|
||||
paletteCategories :["red","blue","green"]
|
||||
|
||||
paletteCategories :["red","blue","green"],
|
||||
exportNodeSettings: function() {}
|
||||
},
|
||||
nodes: {
|
||||
paletteEditorEnabled: function() { return false; }
|
||||
|
||||
@@ -27,7 +27,7 @@ var auth = require("../../../red/api/auth");
|
||||
|
||||
describe("library api", function() {
|
||||
|
||||
function initLibrary(_flows,_libraryEntries) {
|
||||
function initLibrary(_flows,_libraryEntries,_examples) {
|
||||
var flows = _flows;
|
||||
var libraryEntries = _libraryEntries;
|
||||
library.init(app,{
|
||||
@@ -84,6 +84,11 @@ describe("library api", function() {
|
||||
events: {
|
||||
on: function(){},
|
||||
removeListener: function(){}
|
||||
},
|
||||
nodes: {
|
||||
getNodeExampleFlows: function() {
|
||||
return _examples;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -179,6 +184,22 @@ describe("library api", function() {
|
||||
.expect(403)
|
||||
.end(done);
|
||||
});
|
||||
it('includes examples flows if set', function(done) {
|
||||
var examples = {"d":{"node-module":{"f":["example-one"]}}};
|
||||
initLibrary({},{},examples);
|
||||
request(app)
|
||||
.get('/library/flows')
|
||||
.expect(200)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property('d');
|
||||
res.body.d.should.have.property('_examples_');
|
||||
should.deepEqual(res.body.d._examples_,examples);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("type", function() {
|
||||
|
||||
@@ -29,7 +29,14 @@ describe("ui api", function() {
|
||||
var app;
|
||||
|
||||
before(function() {
|
||||
ui.init({events:events});
|
||||
ui.init({
|
||||
events:events,
|
||||
nodes: {
|
||||
getNodeIconPath: function(module,icon) {
|
||||
return path.resolve(__dirname+'/../../../public/icons/arrow-in.png');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
describe("slash handler", function() {
|
||||
before(function() {
|
||||
@@ -64,17 +71,17 @@ describe("ui api", function() {
|
||||
describe("icon handler", function() {
|
||||
before(function() {
|
||||
app = express();
|
||||
app.get("/icons/:icon",ui.icon);
|
||||
app.get("/icons/:module/:icon",ui.icon);
|
||||
});
|
||||
|
||||
function binaryParser(res, callback) {
|
||||
res.setEncoding('binary');
|
||||
res.data = '';
|
||||
res.on('data', function (chunk) {
|
||||
res.data += chunk;
|
||||
res.data += chunk;
|
||||
});
|
||||
res.on('end', function () {
|
||||
callback(null, new Buffer(res.data, 'binary'));
|
||||
callback(null, new Buffer(res.data, 'binary'));
|
||||
});
|
||||
}
|
||||
function compareBuffers(b1,b2) {
|
||||
@@ -83,11 +90,10 @@ describe("ui api", function() {
|
||||
b1[i].should.equal(b2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
it('returns the default icon when getting an unknown icon', function(done) {
|
||||
it('returns the requested icon', function(done) {
|
||||
var defaultIcon = fs.readFileSync(path.resolve(__dirname+'/../../../public/icons/arrow-in.png'));
|
||||
request(app)
|
||||
.get("/icons/youwonthaveme.png")
|
||||
.get("/icons/module/icon.png")
|
||||
.expect("Content-Type", /image\/png/)
|
||||
.expect(200)
|
||||
.parse(binaryParser)
|
||||
@@ -101,40 +107,6 @@ describe("ui api", function() {
|
||||
});
|
||||
|
||||
});
|
||||
it('returns a known icon', function(done) {
|
||||
var injectIcon = fs.readFileSync(path.resolve(__dirname+'/../../../public/icons/inject.png'));
|
||||
request(app)
|
||||
.get("/icons/inject.png")
|
||||
.expect("Content-Type", /image\/png/)
|
||||
.expect(200)
|
||||
.parse(binaryParser)
|
||||
.end(function(err, res){
|
||||
if (err){
|
||||
return done(err);
|
||||
}
|
||||
Buffer.isBuffer(res.body).should.be.true();
|
||||
compareBuffers(res.body,injectIcon);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns a registered icon' , function(done) {
|
||||
var testIcon = fs.readFileSync(path.resolve(__dirname+'/../../resources/icons/test_icon.png'));
|
||||
events.emit("node-icon-dir", path.resolve(__dirname+'/../../resources/icons'));
|
||||
request(app)
|
||||
.get("/icons/test_icon.png")
|
||||
.expect("Content-Type", /image\/png/)
|
||||
.expect(200)
|
||||
.parse(binaryParser)
|
||||
.end(function(err, res){
|
||||
if (err){
|
||||
return done(err);
|
||||
}
|
||||
Buffer.isBuffer(res.body).should.be.true();
|
||||
compareBuffers(res.body,testIcon);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("editor ui handler", function() {
|
||||
@@ -174,8 +146,4 @@ describe("ui api", function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
@@ -56,10 +56,12 @@ describe("red/nodes/index", function() {
|
||||
get: function() { return false }
|
||||
};
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var runtime = {
|
||||
settings: settings,
|
||||
storage: storage,
|
||||
log: {debug:function() {}, warn:function() {}}
|
||||
log: {debug:function() {}, warn:function() {}},
|
||||
events: new EventEmitter()
|
||||
};
|
||||
|
||||
function TestNode(n) {
|
||||
|
||||
69
test/red/runtime/nodes/library_spec.js
Normal file
69
test/red/runtime/nodes/library_spec.js
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var events = new EventEmitter();
|
||||
|
||||
var should = require("should");
|
||||
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
|
||||
var library = require("../../../../red/runtime/nodes/library")
|
||||
|
||||
describe("library api", function() {
|
||||
it('returns null list when no modules have been registered', function() {
|
||||
library.init({events:events});
|
||||
should.not.exist(library.getExampleFlows());
|
||||
});
|
||||
it('returns null path when module is not known', function() {
|
||||
library.init({events:events});
|
||||
should.not.exist(library.getExampleFlowPath('foo','bar'));
|
||||
});
|
||||
|
||||
it('returns a valid example path', function(done) {
|
||||
library.init({events:events});
|
||||
events.emit('node-examples-dir',{
|
||||
name: "test-module",
|
||||
path: path.resolve(__dirname+'/../../../resources/examples')
|
||||
});
|
||||
setTimeout(function() {
|
||||
try {
|
||||
var flows = library.getExampleFlows();
|
||||
flows.should.deepEqual({"d":{"test-module":{"f":["one"]}}});
|
||||
|
||||
var examplePath = library.getExampleFlowPath('test-module','one');
|
||||
examplePath.should.eql(path.resolve(__dirname+'/../../../resources/examples/one.json'))
|
||||
|
||||
|
||||
events.emit('node-module-uninstalled', 'test-module');
|
||||
|
||||
setTimeout(function() {
|
||||
try {
|
||||
should.not.exist(library.getExampleFlows());
|
||||
should.not.exist(library.getExampleFlowPath('test-module','one'));
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
},20);
|
||||
}catch(err) {
|
||||
done(err);
|
||||
}
|
||||
},20);
|
||||
|
||||
})
|
||||
});
|
||||
@@ -46,6 +46,9 @@ describe('nodes/registry/installer', function() {
|
||||
if (registry.getModuleInfo.restore) {
|
||||
registry.getModuleInfo.restore();
|
||||
}
|
||||
if (typeRegistry.getModuleInfo.restore) {
|
||||
typeRegistry.getModuleInfo.restore();
|
||||
}
|
||||
|
||||
if (require('fs').statSync.restore) {
|
||||
require('fs').statSync.restore();
|
||||
@@ -64,6 +67,32 @@ describe('nodes/registry/installer', function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("rejects when npm does not find specified version", function(done) {
|
||||
sinon.stub(child_process,"execFile",function(cmd,args,opt,cb) {
|
||||
cb(new Error(),""," version not found: this_wont_exist@0.1.2");
|
||||
});
|
||||
sinon.stub(typeRegistry,"getModuleInfo", function() {
|
||||
return {
|
||||
version: "0.1.1"
|
||||
}
|
||||
});
|
||||
|
||||
installer.installModule("this_wont_exist","0.1.2").otherwise(function(err) {
|
||||
err.code.should.be.eql(404);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("rejects when update requested to existing version", function(done) {
|
||||
sinon.stub(typeRegistry,"getModuleInfo", function() {
|
||||
return {
|
||||
version: "0.1.1"
|
||||
}
|
||||
});
|
||||
installer.installModule("this_wont_exist","0.1.1").otherwise(function(err) {
|
||||
err.code.should.be.eql('module_already_loaded');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("rejects with generic error", function(done) {
|
||||
sinon.stub(child_process,"execFile",function(cmd,args,opt,cb) {
|
||||
cb(new Error("test_error"),"","");
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
var should = require("should");
|
||||
var when = require("when");
|
||||
var sinon = require("sinon");
|
||||
var path = require("path");
|
||||
|
||||
var typeRegistry = require("../../../../../red/runtime/nodes/registry/registry");
|
||||
|
||||
@@ -483,4 +484,19 @@ describe("red/nodes/registry/registry",function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getNodeIconPath', function() {
|
||||
it('returns the default icon when getting an unknown icon', function() {
|
||||
var defaultIcon = path.resolve(__dirname+'/../../../../../public/icons/arrow-in.png');
|
||||
var iconPath = typeRegistry.getNodeIconPath('random-module','youwonthaveme.png');
|
||||
iconPath.should.eql(defaultIcon);
|
||||
});
|
||||
|
||||
it('returns a registered icon' , function() {
|
||||
var testIcon = path.resolve(__dirname+'/../../../../resources/icons/test_icon.png');
|
||||
events.emit("node-icon-dir",{name:"test-module", path: path.resolve(__dirname+'/../../../../resources/icons')});
|
||||
var iconPath = typeRegistry.getNodeIconPath('test-module','test_icon.png');
|
||||
iconPath.should.eql(testIcon);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -142,4 +142,87 @@ describe("red/settings", function() {
|
||||
settings.should.not.have.property("c");
|
||||
|
||||
});
|
||||
|
||||
it('registers node settings and exports them', function() {
|
||||
var userSettings = {};
|
||||
settings.init(userSettings);
|
||||
settings.registerNodeSettings("inject", {injectColor:{value:"red", exportable:true}, injectSize:{value:"100", exportable:true}} );
|
||||
settings.registerNodeSettings("mqtt", {mqttColor:{value:"purple", exportable:false}, mqttSize:{value:"50", exportable:true}} );
|
||||
settings.registerNodeSettings("http request", {httpRequest1:{value:"a1", exportable:true}} );
|
||||
settings.registerNodeSettings(" http--request<> ", {httpRequest2:{value:"a2", exportable:true}} );
|
||||
settings.registerNodeSettings("_http_request_", {httpRequest3:{value:"a3", exportable:true}} );
|
||||
settings.registerNodeSettings("mQtT", {mQtTColor:{value:"purple", exportable:true}} );
|
||||
settings.registerNodeSettings("abc123", {abc123:{value:"def456", exportable:true}} );
|
||||
var safeSettings = {};
|
||||
settings.exportNodeSettings(safeSettings);
|
||||
safeSettings["nodeSettings"].should.have.property("injectColor", "red");
|
||||
safeSettings["nodeSettings"].should.have.property("injectSize", "100");
|
||||
safeSettings["nodeSettings"].should.not.have.property("mqttColor");
|
||||
safeSettings["nodeSettings"].should.have.property("mqttSize", "50");
|
||||
safeSettings["nodeSettings"].should.have.property("httpRequest1", "a1");
|
||||
safeSettings["nodeSettings"].should.have.property("httpRequest2", "a2");
|
||||
safeSettings["nodeSettings"].should.have.property("httpRequest3", "a3");
|
||||
safeSettings["nodeSettings"].should.have.property("mQtTColor", "purple");
|
||||
safeSettings["nodeSettings"].should.have.property("abc123", "def456");
|
||||
});
|
||||
|
||||
it('prohibits registering the property whose name do not start with type name', function() {
|
||||
var userSettings = {};
|
||||
settings.init(userSettings);
|
||||
settings.registerNodeSettings("inject", {color:{value:"red", exportable:true}} );
|
||||
settings.registerNodeSettings("_a_b_1_", {ab1Color:{value:"red", exportable:true}} );
|
||||
settings.registerNodeSettings("AB2", {AB2Color:{value:"red", exportable:true}} );
|
||||
settings.registerNodeSettings("abcDef", {abcColor:{value:"red", exportable:true}} );
|
||||
var safeSettings = {};
|
||||
settings.exportNodeSettings(safeSettings);
|
||||
safeSettings["nodeSettings"].should.not.have.property("color");
|
||||
safeSettings["nodeSettings"].should.not.have.property("ab1Color", "blue");
|
||||
safeSettings["nodeSettings"].should.not.have.property("AB2Color");
|
||||
safeSettings["nodeSettings"].should.not.have.property("abcColor");
|
||||
});
|
||||
|
||||
it('overwrites node settings with user settings', function() {
|
||||
var userSettings = {
|
||||
injectColor: "green",
|
||||
mqttColor: "yellow",
|
||||
abColor: [1,2,3]
|
||||
}
|
||||
settings.init(userSettings);
|
||||
settings.registerNodeSettings("inject", {injectColor:{value:"red", exportable:true}} );
|
||||
settings.registerNodeSettings("ab", {abColor:{value:"red", exportable:false}} );
|
||||
var safeSettings = {};
|
||||
settings.exportNodeSettings(safeSettings);
|
||||
safeSettings["nodeSettings"].should.have.property("injectColor", "green");
|
||||
safeSettings["nodeSettings"].should.not.have.property("mqttColor");
|
||||
safeSettings["nodeSettings"].should.not.have.property("abColor");
|
||||
});
|
||||
|
||||
it('disables/enables node settings', function() {
|
||||
var userSettings = {};
|
||||
settings.init(userSettings);
|
||||
|
||||
var safeSettings = {};
|
||||
settings.registerNodeSettings("inject", {injectColor:{value:"red", exportable:true}} );
|
||||
settings.registerNodeSettings("mqtt", {mqttColor:{value:"purple", exportable:true}} );
|
||||
settings.registerNodeSettings("http request", {httpRequestColor:{value:"yellow", exportable:true}} );
|
||||
settings.exportNodeSettings(safeSettings);
|
||||
safeSettings["nodeSettings"].should.have.property("injectColor", "red");
|
||||
safeSettings["nodeSettings"].should.have.property("mqttColor", "purple");
|
||||
safeSettings["nodeSettings"].should.have.property("httpRequestColor", "yellow");
|
||||
|
||||
var types = ["inject", "mqtt"];
|
||||
settings.disableNodeSettings(types);
|
||||
settings.exportNodeSettings(safeSettings);
|
||||
safeSettings["nodeSettings"].should.not.have.property("injectColor");
|
||||
safeSettings["nodeSettings"].should.not.have.property("mqttColor");
|
||||
safeSettings["nodeSettings"].should.have.property("httpRequestColor", "yellow");
|
||||
|
||||
types = ["inject"];
|
||||
settings.enableNodeSettings(types);
|
||||
settings.exportNodeSettings(safeSettings);
|
||||
safeSettings["nodeSettings"].should.have.property("injectColor", "red");
|
||||
safeSettings["nodeSettings"].should.not.have.property("mqttColor");
|
||||
safeSettings["nodeSettings"].should.have.property("httpRequestColor", "yellow");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -364,4 +364,21 @@ describe("red/util", function() {
|
||||
it("fail <blank>",function() { testInvalid("");})
|
||||
|
||||
});
|
||||
|
||||
describe('normaliseNodeTypeName', function() {
|
||||
function normalise(input, expected) {
|
||||
var result = util.normaliseNodeTypeName(input);
|
||||
result.should.eql(expected);
|
||||
}
|
||||
|
||||
it('pass blank',function() { normalise("", "") });
|
||||
it('pass ab1',function() { normalise("ab1", "ab1") });
|
||||
it('pass AB1',function() { normalise("AB1", "aB1") });
|
||||
it('pass a b 1',function() { normalise("a b 1", "aB1") });
|
||||
it('pass a-b-1',function() { normalise("a-b-1", "aB1") });
|
||||
it('pass ab1 ',function() { normalise(" ab1 ", "ab1") });
|
||||
it('pass _a_b_1_',function() { normalise("_a_b_1_", "aB1") });
|
||||
it('pass http request',function() { normalise("http request", "httpRequest") });
|
||||
it('pass HttpRequest',function() { normalise("HttpRequest", "httpRequest") });
|
||||
});
|
||||
});
|
||||
|
||||
0
test/resources/examples/one.json
Normal file
0
test/resources/examples/one.json
Normal file
Reference in New Issue
Block a user