Compare commits

..

61 Commits

Author SHA1 Message Date
Dave Conway-Jones
539ca8b84d keep changes in this branch in sync with other trigger changes 2017-12-29 16:57:37 +00:00
Dave Conway-Jones
813a5d2879 add some istanbul ignore to trigger 2017-11-03 19:20:03 +00:00
Dave Conway-Jones
d2b53ebba0 boost trigger node test coverage 2017-09-20 17:35:24 +01:00
Dave Conway-Jones
32d98a7fa3 trigger test - add missing try/catch to all test with callback 2017-09-20 09:31:11 +01:00
Dave Conway-Jones
69946f0be0 test trigger repeat for pass by ref error 2017-09-20 08:42:23 +01:00
Dave Conway-Jones
6e610c0435 Add some tests for trigger by topic 2017-09-19 23:21:50 +01:00
Dave Conway-Jones
cdf9e2c214 ensure trigger node clones repeating message 2017-09-19 22:40:36 +01:00
Dave Conway-Jones
6b672bd9af Let trigger node support per topic mode 2017-09-18 15:44:37 +01:00
btsimonh
b81940351f Allow port zero for Express (#1363)
* Allow uiPort to be 0 (i.e. distinguish from undefined).  When Express runs up, catch the real port number to settings.serverPort, and use that in getListenPath if set, else use uiPort.
2017-09-17 09:30:39 +01:00
Kazuki Nakanishi
a42e99c4aa Fix the appearance of 'is between' rule on switch node property (#1383) 2017-09-17 08:46:47 +01:00
HirokiUchikawa
ff40b521b7 Fix problem with multi-byte character (#1391) 2017-09-17 08:46:14 +01:00
Nick O'Leary
85392496e7 Allow setTimeout in Function node to be promisified in node 8 2017-09-12 15:13:13 +01:00
Jeston Tigchon
29cae9975e Upgrade JSONata to v1.3.0 (#1386) 2017-09-07 21:58:29 +01:00
Kosuke Akizuki
170d6b28f8 Change font family (#1357)
thanks  @k4zzk
2017-08-24 12:14:55 +01:00
Dave Conway-Jones
a844ca161f Spinner fixes (#1371)
* Fix for function node invalid spinner values

to close #1370

* better validation of spinners for inject and delay

(don’t allow negative numbers)

* remove need for declaring local min variable
2017-08-21 22:00:23 +01:00
btsimonh
e09efba313 mqtt: Add 'name' to mqtt-broker node, and label it by this if it is set. (#1364)
This allows you to easily distinguish between broker nodes which are talking to the same server but with different credentials.
2017-08-09 22:22:40 +01:00
Nick O'Leary
96a0dbea2d Don't include subflow meta-port nodes in exported selection
Fixes #1362
2017-08-08 15:48:54 +01:00
Nick O'Leary
5b3b5271ad Remove test diff code 2017-08-07 16:38:15 +01:00
Kazuhito Yokoi
d7d13c12fe Modify messages to refer to language files (#1361) 2017-08-07 10:00:28 +01:00
Nick O'Leary
54220d0e71 Ensure shade elements have a higher z-index than ui elements 2017-08-04 22:20:58 +01:00
Nick O'Leary
4a2e3586f1 Allow delay node in rate-limit mode to be reset
Fixes #1360
2017-08-04 21:09:00 +01:00
Nick O'Leary
f808e85da9 Diff view: subflows can have port labels as well 2017-08-04 14:26:05 +01:00
Nick O'Leary
1671d1f580 Allow expanding diff elements to stay in-sync deeper 2017-08-04 14:23:28 +01:00
Nick O'Leary
7de1bf9d95 Better node properties layout in diff table 2017-08-03 23:04:39 +01:00
Nick O'Leary
7368b0cefb Make diff tool a maximised tray rather than dialog 2017-08-03 09:58:25 +01:00
Nick O'Leary
4af43d676a Include input/output labels in diff view 2017-08-02 21:57:23 +01:00
Nick O'Leary
67dc848b2d getNodeIcon should handle subflow types properly 2017-08-02 21:55:25 +01:00
Nick O'Leary
7ec8f0d26b Do not include tab types in typeSearch dialog 2017-08-02 21:54:58 +01:00
Nick O'Leary
eaf08a9971 Keep local/remote diff objects in sync as they expand 2017-07-31 23:29:36 +01:00
Nick O'Leary
5bdb9e972e Add httpStatic log statement on start up 2017-07-26 11:45:49 -07:00
Nick O'Leary
d4d87054c4 Ensure tab property changes are listed in diff view 2017-07-26 07:55:53 -07:00
Nick O'Leary
0f93929544 Fix diff view node properties table rendering 2017-07-26 07:47:19 -07:00
Nick O'Leary
1c0e794f87 Ensure tabs get their definition object properly attached 2017-07-26 07:46:22 -07:00
Nick O'Leary
f9bce5a5f9 Changelog for 0.17.5 2017-07-23 17:32:27 +01:00
Nick O'Leary
797ae096c8 Add express-session missing dependency for oauth 2017-07-23 17:31:42 +01:00
Nick O'Leary
6d76918424 Fix improper type tests is core test cases 2017-07-22 22:42:35 +01:00
Nick O'Leary
2aced893c6 File node: recreate write stream when file deleted
Fixes #1351
2017-07-22 22:28:45 +01:00
Nick O'Leary
f0373cd789 Add flow stopping trace messages 2017-07-21 11:15:40 +01:00
Kazuki Nakanishi
2f88dc64fc Fix userDir test case when .config.json exists (#1350) 2017-07-21 11:12:04 +01:00
Nick O'Leary
781ca77794 Do not try to send msg after http request error handled
Fixes #1344
2017-07-19 22:37:29 +01:00
Kazuhito Yokoi
c6e453fb00 Fix boundary problem in range node (#1338)
* Fix boundary problem in range node

* Remove duplicated test case

* Empty commit to retry Travis CI
2017-07-19 16:42:39 +01:00
Kazuhito Yokoi
a40b3dd377 Modify messages in node properties to refer messages.json (#1339) 2017-07-19 13:50:34 +01:00
martiall
096b3534d8 Fix settings.js replacing webSocketVerifyClient by webSocketNodeVerifyClient (#1343) 2017-07-14 11:34:29 +01:00
Nick O'Leary
5324244c55 Bump for 0.17.4 2017-07-10 13:16:13 +01:00
Nick O'Leary
993f1dc853 Add request node test case for POSTing 0 2017-07-09 12:18:05 +01:00
Patrik Åkerfeldt
d8a4e9e1ab Allow false and 0 in payload for httprequest (#1334) 2017-07-09 12:17:54 +01:00
Kazuhito Yokoi
b3ffd33507 Add file extension into flow name of library automatically (#1331) 2017-07-09 11:58:17 +01:00
Nick O'Leary
c93870316c Fix accessing global context from jsonata expressions
Fixes #1335
2017-07-09 10:40:23 +01:00
Nick O'Leary
fc9906624e Disable editor whilst a deploy is inflight
Fixes #1332
2017-07-08 21:16:52 +01:00
Nick O'Leary
ba6209ba54 Replace Unknown nodes with their real versions when node loaded 2017-07-08 17:30:17 +01:00
Nick O'Leary
f9769a73fe Retry auto-install of modules that fail
- introduces autoInstallModulesRetry - default 30000
 - backs off interval if repeated failures
 - fixes notification to the editor of an auto-reinstall
2017-07-08 17:30:17 +01:00
Kazuhito Yokoi
3a2f56cb95 Fix column name in link nodes to refer language file (#1330) 2017-07-07 11:43:07 +01:00
Nick O'Leary
a4d33879dc Use namespaces with link node title attributes i18n name
Fixes #1329
2017-07-06 17:57:53 +01:00
Nick O'Leary
e2a91d1ea9 Tidy up GPIO pin table presentation
Fixes #1328
2017-07-06 00:00:08 +01:00
Nick O'Leary
f30f80d117 Join: count of 0 should not send on every msg 2017-07-05 14:12:28 +01:00
Nick O'Leary
266274135e Handle importing only one end of a link node pair 2017-07-04 23:40:37 +01:00
Nick O'Leary
a10439b67c Make sending to Debug synchronous again
Fixes #1323

Being asynchronous meant the msg that was eventually sent to
Debug could be a modified version from later in the flow, if
the flow was other synchronous.
2017-07-04 23:30:51 +01:00
Nick O'Leary
0fd8d0e2bf Make send-error behaviour optional in file node
Existing nodes will have sendError enabled. New instances
will default to it being disabled.
2017-07-04 20:12:53 +01:00
Nick O'Leary
47e2707fd3 Restore File In node behaviour of sending msg on error 2017-07-04 19:55:09 +01:00
Nick O'Leary
f7bb4a7d60 Expose context.keys within Function node 2017-07-04 14:52:14 +01:00
Nick O'Leary
6102a31a31 JSON parser default should be not formatting output
If its a checkbox, then the default value should be a boolean,
not a string. Because "false" is truthy.
2017-07-04 13:44:37 +01:00
77 changed files with 1789 additions and 779 deletions

View File

@@ -1,3 +1,37 @@
#### 0.17.5: Maintenance Release
- Add express-session missing dependency for oauth
- Fix improper type tests is core test cases
- File node: recreate write stream when file deleted Fixes #1351
- Add flow stopping trace messages
- Fix userDir test case when .config.json exists (#1350)
- Do not try to send msg after http request error handled Fixes #1344
- Fix boundary problem in range node (#1338)
- Modify messages in node properties to refer messages.json (#1339)
- Fix settings.js replacing webSocketVerifyClient by webSocketNodeVerifyClient (#1343)
#### 0.17.4: Maintenance Release
- Add request node test case for POSTing 0
- Allow false and 0 in payload for httprequest (#1334)
- Add file extension into flow name of library automatically (#1331)
- Fix accessing global context from jsonata expressions Fixes #1335
- Disable editor whilst a deploy is inflight Fixes #1332
- Replace Unknown nodes with their real versions when node loaded
- Retry auto-install of modules that fail
- Fix column name in link nodes to refer language file (#1330)
- Use namespaces with link node title attributes i18n name Fixes #1329
- Tidy up GPIO pin table presentation Fixes #1328
- Join: count of 0 should not send on every msg
- Handle importing only one end of a link node pair
- Make sending to Debug synchronous again Fixes #1323
- Make send-error behaviour optional in file node
- Restore File In node behaviour of sending msg on error
- Expose context.keys within Function node
- JSON parser default should be not formatting output
#### 0.17.3: Maintenance Release
- Fix flow library in menu to support period characters as flow name (#1320)

View File

@@ -37,7 +37,25 @@ RED.i18n = (function() {
},
loadCatalog: function(namespace,done) {
i18n.loadNamespace(namespace,done);
var languageList = i18n.functions.toLanguages(i18n.detectLanguage());
var toLoad = languageList.length;
languageList.forEach(function(lang) {
$.ajax({
headers: {
"Accept":"application/json"
},
cache: false,
url: 'locales/'+namespace+'?lng='+lang,
success: function(data) {
i18n.addResourceBundle(lang,namespace,data);
toLoad--;
if (toLoad === 0) {
done();
}
}
});
})
},
loadNodeCatalogs: function(done) {

View File

@@ -72,6 +72,10 @@
// handled in ui/deploy.js
return;
}
if (notificationId === "node") {
// handled below
return;
}
if (msg.text) {
var text = RED._(msg.text,{default:msg.text});
if (!persistentNotifications.hasOwnProperty(notificationId)) {
@@ -98,11 +102,11 @@
RED.view.redraw();
}
});
RED.comms.subscribe("node/#",function(topic,msg) {
RED.comms.subscribe("notification/node/#",function(topic,msg) {
var i,m;
var typeList;
var info;
if (topic == "node/added") {
if (topic == "notification/node/added") {
var addedTypes = [];
msg.forEach(function(m) {
var id = m.id;
@@ -118,7 +122,7 @@
typeList = "<ul><li>"+addedTypes.join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeAdded", {count:addedTypes.length})+typeList,"success");
}
} else if (topic == "node/removed") {
} else if (topic == "notification/node/removed") {
for (i=0;i<msg.length;i++) {
m = msg[i];
info = RED.nodes.removeNodeSet(m.id);
@@ -127,7 +131,7 @@
RED.notify(RED._("palette.event.nodeRemoved", {count:m.types.length})+typeList,"success");
}
}
} else if (topic == "node/enabled") {
} else if (topic == "notification/node/enabled") {
if (msg.types) {
info = RED.nodes.getNodeSet(msg.id);
if (info.added) {
@@ -142,7 +146,7 @@
});
}
}
} else if (topic == "node/disabled") {
} else if (topic == "notification/node/disabled") {
if (msg.types) {
RED.nodes.disableNodeSet(msg.id);
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
@@ -215,12 +219,12 @@
menuOptions.push(null);
}
menuOptions.push({id:"menu-item-user-settings",label:RED._("menu.label.userSettings"),onselect:"core:show-user-settings"});
menuOptions.push({id:"menu-item-user-settings",label:RED._("menu.label.settings"),onselect:"core:show-user-settings"});
menuOptions.push(null);
menuOptions.push({id:"menu-item-keyboard-shortcuts",label:RED._("menu.label.keyboardShortcuts"),onselect:"core:show-help"});
menuOptions.push({id:"menu-item-help",
label: RED.settings.theme("menu.menu-item-help.label","Node-RED website"),
label: RED.settings.theme("menu.menu-item-help.label",RED._("menu.label.help")),
href: RED.settings.theme("menu.menu-item-help.url","http://nodered.org/docs")
});
menuOptions.push({id:"menu-item-node-red-version", label:"v"+RED.settings.version, onselect: "core:show-about" });
@@ -249,13 +253,12 @@
RED.deploy.init(RED.settings.theme("deployButton",null));
RED.actions.add("core:show-about", showAbout);
RED.nodes.init();
RED.comms.connect();
$("#main-container").show();
$(".header-toolbar").show();
loadNodeList();
}

View File

@@ -41,6 +41,15 @@ RED.nodes = (function() {
var typeToId = {};
var nodeDefinitions = {};
nodeDefinitions['tab'] = {
defaults: {
label: {value:""},
disabled: {value: false},
info: {value: ""}
}
};
var exports = {
setModulePendingUpdated: function(module,version) {
moduleList[module].pending_version = version;
@@ -274,14 +283,7 @@ RED.nodes = (function() {
function addWorkspace(ws) {
workspaces[ws.id] = ws;
ws._def = {
defaults: {
label: {value:""},
disabled: {value: false},
info: {value: ""}
}
};
ws._def = RED.nodes.getType('tab');
workspacesOrder.push(ws.id);
}
function getWorkspace(id) {
@@ -817,7 +819,7 @@ RED.nodes = (function() {
// Add a tab if there isn't one there already
if (defaultWorkspace == null) {
defaultWorkspace = { type:"tab", id:getID(), label:RED._('workspace.defaultName',{number:1})};
defaultWorkspace = { type:"tab", id:getID(), disabled: false, info:"", label:RED._('workspace.defaultName',{number:1})};
addWorkspace(defaultWorkspace);
RED.workspaces.add(defaultWorkspace);
new_workspaces.push(defaultWorkspace);
@@ -1217,6 +1219,50 @@ RED.nodes = (function() {
}
return {
init: function() {
RED.events.on("registry:node-type-added",function(type) {
var def = registry.getNodeType(type);
var replaced = false;
var replaceNodes = [];
RED.nodes.eachNode(function(n) {
if (n.type === "unknown" && n.name === type) {
replaceNodes.push(n);
}
});
RED.nodes.eachConfig(function(n) {
if (n.type === "unknown" && n.name === type) {
replaceNodes.push(n);
}
});
if (replaceNodes.length > 0) {
var reimportList = [];
replaceNodes.forEach(function(n) {
if (configNodes.hasOwnProperty(n.id)) {
delete configNodes[n.id];
} else {
nodes.splice(nodes.indexOf(n),1);
}
reimportList.push(convertNode(n));
});
RED.view.redraw(true);
var result = importNodes(reimportList,false);
var newNodeMap = {};
result[0].forEach(function(n) {
newNodeMap[n.id] = n;
});
RED.nodes.eachLink(function(l) {
if (newNodeMap.hasOwnProperty(l.source.id)) {
l.source = newNodeMap[l.source.id];
}
if (newNodeMap.hasOwnProperty(l.target.id)) {
l.target = newNodeMap[l.target.id];
}
});
RED.view.redraw(true);
}
});
},
registry:registry,
setNodeList: registry.setNodeList,

View File

@@ -203,7 +203,8 @@ RED.clipboard = (function() {
var nodes = null;
if (type === 'export-range-selected') {
var selection = RED.view.selection();
nodes = RED.nodes.createExportableNodeSet(selection.nodes);
// Don't include the subflow meta-port nodes in the exported selection
nodes = RED.nodes.createExportableNodeSet(selection.nodes.filter(function(n) { return n.type !== 'subflow'}));
} else if (type === 'export-range-flow') {
var activeWorkspace = RED.workspaces.active();
nodes = RED.nodes.filterNodes({z:activeWorkspace});

View File

@@ -85,7 +85,7 @@
if (this.element.css("position") === "absolute") {
["top","left","bottom","right"].forEach(function(s) {
var v = that.element.css(s);
if (s!=="auto" && s!=="") {
if (v!=="auto" && v!=="") {
that.topContainer.css(s,v);
that.uiContainer.css(s,"0");
that.element.css(s,'auto');

View File

@@ -157,8 +157,8 @@ RED.deploy = (function() {
create: function() {
$("#node-dialog-confirm-deploy").parent().find("div.ui-dialog-buttonpane")
.prepend('<div style="height:0; vertical-align: middle; display:inline-block; margin-top: 13px; float:left;">'+
'<input style="vertical-align:top;" type="checkbox" id="node-dialog-confirm-deploy-hide">'+
'<label style="display:inline;" for="node-dialog-confirm-deploy-hide"> do not warn about this again</label>'+
'<input style="vertical-align:top;" type="checkbox" id="node-dialog-confirm-deploy-hide"> '+
'<label style="display:inline;" for="node-dialog-confirm-deploy-hide" data-i18n="deploy.confirm.doNotWarn"></label>'+
'<input type="hidden" id="node-dialog-confirm-deploy-type">'+
'</div>');
},
@@ -361,6 +361,10 @@ RED.deploy = (function() {
}
deployInflight = true;
$("#header-shade").show();
$("#editor-shade").show();
$("#palette-shade").show();
$("#sidebar-shade").show();
$.ajax({
url:"flows",
type: "POST",
@@ -424,6 +428,10 @@ RED.deploy = (function() {
setTimeout(function() {
$(".deploy-button-content").css('opacity',1);
$(".deploy-button-spinner").hide();
$("#header-shade").hide();
$("#editor-shade").hide();
$("#palette-shade").hide();
$("#sidebar-shade").hide();
},delta);
});
}

View File

@@ -1,52 +1,26 @@
RED.diff = (function() {
var currentDiff = {};
var diffVisible = false;
var diffList;
function init() {
// RED.actions.add("core:show-current-diff",showLocalDiff);
RED.actions.add("core:show-remote-diff",showRemoteDiff);
// RED.keyboard.add("*","ctrl-shift-l","core:show-current-diff");
RED.keyboard.add("*","ctrl-shift-r","core:show-remote-diff");
}
var dialog = $('<div id="node-dialog-view-diff" class="hide"><div id="node-dialog-view-diff-headers"></div><ol id="node-dialog-view-diff-diff"></ol></div>').appendTo(document.body);
function buildDiffPanel(container) {
var diffPanel = $('<div id="node-dialog-view-diff"><div id="node-dialog-view-diff-headers"></div><ol id="node-dialog-view-diff-diff"></ol></div>').appendTo(container);
var toolbar = $('<div class="node-diff-toolbar">'+
'<span><span id="node-diff-toolbar-resolved-conflicts"></span></span> '+
'</div>').prependTo(dialog);
'</div>').prependTo(diffPanel);
$("#node-dialog-view-diff").dialog({
title: RED._('deploy.confirm.button.review'),
modal: true,
autoOpen: false,
buttons: [
{
text: RED._("common.label.cancel"),
click: function() {
$( this ).dialog( "close" );
}
},
{
id: "node-diff-view-diff-merge",
text: RED._("deploy.confirm.button.merge"),
class: "primary disabled",
click: function() {
if (!$("#node-diff-view-diff-merge").hasClass('disabled')) {
refreshConflictHeader();
mergeDiff(currentDiff);
$( this ).dialog( "close" );
}
}
}
],
open: function() {
$(this).dialog({width:Math.min($(window).width(),900),height:Math.min($(window).height(),600)});
}
});
var diffList = $("#node-dialog-view-diff-diff").editableList({
diffList = diffPanel.find("#node-dialog-view-diff-diff").editableList({
addButton: false,
scrollOnAdd: false,
addItem: function(container,i,object) {
@@ -77,7 +51,7 @@ RED.diff = (function() {
} else if (tab.type === 'subflow') {
titleSpan.html((tabForLabel.name||tabForLabel.id));
} else {
titleSpan.html("Global nodes");
titleSpan.html(RED._("diff.globalNodes"));
}
var flowStats = {
local: {
@@ -153,7 +127,7 @@ RED.diff = (function() {
}
}
$('<span class="node-diff-chevron"><i class="fa fa-angle-down"></i></span>').appendTo(originalNodeDiv);
$('<span>').html("Flow Properties").appendTo(originalNodeDiv);
$('<span>').html(RED._("diff.flowProperties")).appendTo(originalNodeDiv);
row.click(function(evt) {
evt.preventDefault();
@@ -307,6 +281,7 @@ RED.diff = (function() {
container.i18n();
}
});
return diffPanel;
}
function formatWireProperty(wires,allNodes) {
var result = $("<div>",{class:"node-diff-property-wires"})
@@ -563,6 +538,7 @@ RED.diff = (function() {
return div;
}
function createNodePropertiesTable(def,node,localNodeObj,remoteNodeObj) {
var propertyElements = {};
var localNode = localNodeObj.node;
var remoteNode;
if (remoteNodeObj) {
@@ -571,8 +547,15 @@ RED.diff = (function() {
var nodePropertiesDiv = $("<div>",{class:"node-diff-node-entry-properties"});
var nodePropertiesTable = $("<table>").appendTo(nodePropertiesDiv);
var nodePropertiesTableCols = $('<colgroup><col/><col/></colgroup>').appendTo(nodePropertiesTable);
if (remoteNode !== undefined) {
$("<col/>").appendTo(nodePropertiesTableCols);
}
var nodePropertiesTableBody = $("<tbody>").appendTo(nodePropertiesTable);
var row;
var localCell, remoteCell;
var element;
var currentValue, localValue, remoteValue;
var localChanged = false;
var remoteChanged = false;
@@ -581,13 +564,14 @@ RED.diff = (function() {
var conflict = false;
var status;
row = $("<tr>").appendTo(nodePropertiesTable);
row = $("<tr>").appendTo(nodePropertiesTableBody);
$("<td>",{class:"node-diff-property-cell-label"}).html("id").appendTo(row);
localCell = $("<td>",{class:"node-diff-property-cell node-diff-node-local"}).appendTo(row);
if (localNode) {
localCell.addClass("node-diff-node-unchanged");
$('<span class="node-diff-status"></span>').appendTo(localCell);
RED.utils.createObjectElement(localNode.id).appendTo(localCell);
element = $('<span class="node-diff-element"></span>').appendTo(localCell);
propertyElements['local.id'] = RED.utils.createObjectElement(localNode.id).appendTo(element);
} else {
localCell.addClass("node-diff-empty");
}
@@ -596,7 +580,8 @@ RED.diff = (function() {
remoteCell.addClass("node-diff-node-unchanged");
if (remoteNode) {
$('<span class="node-diff-status"></span>').appendTo(remoteCell);
RED.utils.createObjectElement(remoteNode.id).appendTo(remoteCell);
element = $('<span class="node-diff-element"></span>').appendTo(remoteCell);
propertyElements['remote.id'] = RED.utils.createObjectElement(remoteNode.id).appendTo(element);
} else {
remoteCell.addClass("node-diff-empty");
}
@@ -622,13 +607,24 @@ RED.diff = (function() {
) {
conflict = true;
}
row = $("<tr>").appendTo(nodePropertiesTable);
row = $("<tr>").appendTo(nodePropertiesTableBody);
$("<td>",{class:"node-diff-property-cell-label"}).html("position").appendTo(row);
localCell = $("<td>",{class:"node-diff-property-cell node-diff-node-local"}).appendTo(row);
if (localNode) {
localCell.addClass("node-diff-node-"+(localChanged?"changed":"unchanged"));
$('<span class="node-diff-status">'+(localChanged?'<i class="fa fa-square"></i>':'')+'</span>').appendTo(localCell);
RED.utils.createObjectElement({x:localNode.x,y:localNode.y}).appendTo(localCell);
element = $('<span class="node-diff-element"></span>').appendTo(localCell);
propertyElements['local.position'] = RED.utils.createObjectElement({x:localNode.x,y:localNode.y},
{
path: "position",
exposeApi: true,
ontoggle: function(path,state) {
if (propertyElements['remote.'+path]) {
propertyElements['remote.'+path].prop('expand')(path,state)
}
}
}
).appendTo(element);
} else {
localCell.addClass("node-diff-empty");
}
@@ -638,7 +634,18 @@ RED.diff = (function() {
remoteCell.addClass("node-diff-node-"+(remoteChanged?"changed":"unchanged"));
if (remoteNode) {
$('<span class="node-diff-status">'+(remoteChanged?'<i class="fa fa-square"></i>':'')+'</span>').appendTo(remoteCell);
RED.utils.createObjectElement({x:remoteNode.x,y:remoteNode.y}).appendTo(remoteCell);
element = $('<span class="node-diff-element"></span>').appendTo(remoteCell);
propertyElements['remote.position'] = RED.utils.createObjectElement({x:remoteNode.x,y:remoteNode.y},
{
path: "position",
exposeApi: true,
ontoggle: function(path,state) {
if (propertyElements['local.'+path]) {
propertyElements['local.'+path].prop('expand')(path,state);
}
}
}
).appendTo(element);
} else {
remoteCell.addClass("node-diff-empty");
}
@@ -668,7 +675,7 @@ RED.diff = (function() {
){
conflict = true;
}
row = $("<tr>").appendTo(nodePropertiesTable);
row = $("<tr>").appendTo(nodePropertiesTableBody);
$("<td>",{class:"node-diff-property-cell-label"}).html("wires").appendTo(row);
localCell = $("<td>",{class:"node-diff-property-cell node-diff-node-local"}).appendTo(row);
if (localNode) {
@@ -700,10 +707,14 @@ RED.diff = (function() {
}
}
}
var properties = Object.keys(node).filter(function(p) { return p!='z'&&p!='wires'&&p!=='x'&&p!=='y'&&p!=='id'&&p!=='type'&&(!def.defaults||!def.defaults.hasOwnProperty(p))});
var properties = Object.keys(node).filter(function(p) { return p!='inputLabels'&&p!='outputLabels'&&p!='z'&&p!='wires'&&p!=='x'&&p!=='y'&&p!=='id'&&p!=='type'&&(!def.defaults||!def.defaults.hasOwnProperty(p))});
if (def.defaults) {
properties = properties.concat(Object.keys(def.defaults));
}
if (node.type !== 'tab') {
properties = properties.concat(['inputLabels','outputLabels']);
}
properties.forEach(function(d) {
localChanged = false;
remoteChanged = false;
@@ -731,8 +742,8 @@ RED.diff = (function() {
conflict = true;
}
row = $("<tr>").appendTo(nodePropertiesTable);
$("<td>",{class:"node-diff-property-cell-label"}).html(d).appendTo(row);
row = $("<tr>").appendTo(nodePropertiesTableBody);
var propertyNameCell = $("<td>",{class:"node-diff-property-cell-label"}).html(d).appendTo(row);
localCell = $("<td>",{class:"node-diff-property-cell node-diff-node-local"}).appendTo(row);
if (localNode) {
if (!conflict) {
@@ -742,7 +753,18 @@ RED.diff = (function() {
localCell.addClass("node-diff-node-conflict");
$('<span class="node-diff-status"><i class="fa fa-exclamation"></i></span>').appendTo(localCell);
}
RED.utils.createObjectElement(localNode[d]).appendTo(localCell);
element = $('<span class="node-diff-element"></span>').appendTo(localCell);
propertyElements['local.'+d] = RED.utils.createObjectElement(localNode[d],
{
path: d,
exposeApi: true,
ontoggle: function(path,state) {
if (propertyElements['remote.'+d]) {
propertyElements['remote.'+d].prop('expand')(path,state)
}
}
}
).appendTo(element);
} else {
localCell.addClass("node-diff-empty");
}
@@ -756,7 +778,18 @@ RED.diff = (function() {
remoteCell.addClass("node-diff-node-conflict");
$('<span class="node-diff-status"><i class="fa fa-exclamation"></i></span>').appendTo(remoteCell);
}
RED.utils.createObjectElement(remoteNode[d]).appendTo(remoteCell);
element = $('<span class="node-diff-element"></span>').appendTo(remoteCell);
propertyElements['remote.'+d] = RED.utils.createObjectElement(remoteNode[d],
{
path: d,
exposeApi: true,
ontoggle: function(path,state) {
if (propertyElements['local.'+d]) {
propertyElements['local.'+d].prop('expand')(path,state)
}
}
}
).appendTo(element);
} else {
remoteCell.addClass("node-diff-empty");
}
@@ -1002,186 +1035,225 @@ RED.diff = (function() {
// console.log(conflicted);
return diff;
}
function showDiff(diff) {
if (diffVisible) {
return;
}
var localDiff = diff.localDiff;
var remoteDiff = diff.remoteDiff;
var conflicts = diff.conflicts;
currentDiff = diff;
var list = $("#node-dialog-view-diff-diff");
list.editableList('empty');
if (remoteDiff) {
$("#node-diff-view-diff-merge").show();
if (Object.keys(conflicts).length === 0) {
$("#node-diff-view-diff-merge").removeClass('disabled');
} else {
$("#node-diff-view-diff-merge").addClass('disabled');
}
} else {
$("#node-diff-view-diff-merge").hide();
}
refreshConflictHeader();
$("#node-dialog-view-diff-headers").empty();
// console.log("--------------");
// console.log(localDiff);
// console.log(remoteDiff);
var currentConfig = localDiff.currentConfig;
var newConfig = localDiff.newConfig;
conflicts = conflicts || {};
var el = {
diff: localDiff,
def: {
category: 'config',
color: '#f0f0f0'
var trayOptions = {
title: "Review Changes", //TODO: nls
width: Infinity,
buttons: [
{
text: RED._("common.label.cancel"),
click: function() {
RED.tray.close();
}
},
{
id: "node-diff-view-diff-merge",
text: RED._("deploy.confirm.button.merge"),
class: "primary disabled",
click: function() {
if (!$("#node-diff-view-diff-merge").hasClass('disabled')) {
refreshConflictHeader();
mergeDiff(currentDiff);
RED.tray.close();
}
}
}
],
resize: function(dimensions) {
// trayWidth = dimensions.width;
},
tab: {
n: {},
nodes: currentConfig.globals
},
newTab: {
n: {},
nodes: newConfig.globals
}
};
open: function(tray) {
var trayBody = tray.find('.editor-tray-body');
var diffPanel = buildDiffPanel(trayBody);
if (remoteDiff) {
$("#node-diff-view-diff-merge").show();
if (Object.keys(conflicts).length === 0) {
$("#node-diff-view-diff-merge").removeClass('disabled');
} else {
$("#node-diff-view-diff-merge").addClass('disabled');
}
} else {
$("#node-diff-view-diff-merge").hide();
}
refreshConflictHeader();
if (remoteDiff !== undefined) {
$('#node-dialog-view-diff').addClass('node-diff-three-way');
$("#node-dialog-view-diff-headers").empty();
// console.log("--------------");
// console.log(localDiff);
// console.log(remoteDiff);
var currentConfig = localDiff.currentConfig;
var newConfig = localDiff.newConfig;
conflicts = conflicts || {};
$('<div class="node-diff-node-entry-cell"></div><div class="node-diff-node-entry-cell" data-i18n="diff.local"></div><div class="node-diff-node-entry-cell" data-i18n="diff.remote"></div>').i18n().appendTo("#node-dialog-view-diff-headers");
el.remoteTab = {
n:{},
nodes:remoteDiff.newConfig.globals
};
el.remoteDiff = remoteDiff;
} else {
$('#node-dialog-view-diff').removeClass('node-diff-three-way');
}
list.editableList('addItem',el);
var seenTabs = {};
currentConfig.tabOrder.forEach(function(tabId) {
var tab = currentConfig.tabs[tabId];
var el = {
diff: localDiff,
def: {},
tab:tab
};
if (newConfig.tabs.hasOwnProperty(tabId)) {
el.newTab = newConfig.tabs[tabId];
}
if (remoteDiff !== undefined) {
el.remoteTab = remoteDiff.newConfig.tabs[tabId];
el.remoteDiff = remoteDiff;
}
seenTabs[tabId] = true;
list.editableList('addItem',el)
});
newConfig.tabOrder.forEach(function(tabId) {
if (!seenTabs[tabId]) {
seenTabs[tabId] = true;
var tab = newConfig.tabs[tabId];
var el = {
diff: localDiff,
def: {},
tab:tab,
newTab: tab
def: {
category: 'config',
color: '#f0f0f0'
},
tab: {
n: {},
nodes: currentConfig.globals
},
newTab: {
n: {},
nodes: newConfig.globals
}
};
if (remoteDiff !== undefined) {
diffPanel.addClass('node-diff-three-way');
$('<div data-i18n="diff.local"></div><div data-i18n="diff.remote"></div>').i18n().appendTo("#node-dialog-view-diff-headers");
el.remoteTab = {
n:{},
nodes:remoteDiff.newConfig.globals
};
el.remoteDiff = remoteDiff;
} else {
diffPanel.removeClass('node-diff-three-way');
}
list.editableList('addItem',el)
}
});
if (remoteDiff !== undefined) {
remoteDiff.newConfig.tabOrder.forEach(function(tabId) {
if (!seenTabs[tabId]) {
var tab = remoteDiff.newConfig.tabs[tabId];
// TODO how to recognise this is a remotely added flow
diffList.editableList('addItem',el);
var seenTabs = {};
currentConfig.tabOrder.forEach(function(tabId) {
var tab = currentConfig.tabs[tabId];
var el = {
diff: localDiff,
remoteDiff: remoteDiff,
def: {},
tab:tab,
remoteTab:tab
def: RED.nodes.getType('tab'),
tab:tab
};
list.editableList('addItem',el)
}
});
}
var subflowId;
for (subflowId in currentConfig.subflows) {
if (currentConfig.subflows.hasOwnProperty(subflowId)) {
seenTabs[subflowId] = true;
el = {
diff: localDiff,
def: {
defaults:{},
icon:"subflow.png",
category: "subflows",
color: "#da9"
},
tab:currentConfig.subflows[subflowId]
}
if (newConfig.subflows.hasOwnProperty(subflowId)) {
el.newTab = newConfig.subflows[subflowId];
}
if (remoteDiff !== undefined) {
el.remoteTab = remoteDiff.newConfig.subflows[subflowId];
el.remoteDiff = remoteDiff;
}
list.editableList('addItem',el)
}
}
for (subflowId in newConfig.subflows) {
if (newConfig.subflows.hasOwnProperty(subflowId) && !seenTabs[subflowId]) {
seenTabs[subflowId] = true;
el = {
diff: localDiff,
def: {
defaults:{},
icon:"subflow.png",
category: "subflows",
color: "#da9"
},
tab:newConfig.subflows[subflowId],
newTab:newConfig.subflows[subflowId]
}
if (remoteDiff !== undefined) {
el.remoteDiff = remoteDiff;
}
list.editableList('addItem',el)
}
}
if (remoteDiff !== undefined) {
for (subflowId in remoteDiff.newConfig.subflows) {
if (remoteDiff.newConfig.subflows.hasOwnProperty(subflowId) && !seenTabs[subflowId]) {
el = {
diff: localDiff,
remoteDiff: remoteDiff,
def: {
defaults:{},
icon:"subflow.png",
category: "subflows",
color: "#da9"
},
tab:remoteDiff.newConfig.subflows[subflowId],
remoteTab: remoteDiff.newConfig.subflows[subflowId]
if (newConfig.tabs.hasOwnProperty(tabId)) {
el.newTab = newConfig.tabs[tabId];
}
list.editableList('addItem',el)
if (remoteDiff !== undefined) {
el.remoteTab = remoteDiff.newConfig.tabs[tabId];
el.remoteDiff = remoteDiff;
}
seenTabs[tabId] = true;
diffList.editableList('addItem',el)
});
newConfig.tabOrder.forEach(function(tabId) {
if (!seenTabs[tabId]) {
seenTabs[tabId] = true;
var tab = newConfig.tabs[tabId];
var el = {
diff: localDiff,
def: RED.nodes.getType('tab'),
tab:tab,
newTab: tab
};
if (remoteDiff !== undefined) {
el.remoteDiff = remoteDiff;
}
diffList.editableList('addItem',el)
}
});
if (remoteDiff !== undefined) {
remoteDiff.newConfig.tabOrder.forEach(function(tabId) {
if (!seenTabs[tabId]) {
var tab = remoteDiff.newConfig.tabs[tabId];
// TODO how to recognise this is a remotely added flow
var el = {
diff: localDiff,
remoteDiff: remoteDiff,
def: RED.nodes.getType('tab'),
tab:tab,
remoteTab:tab
};
diffList.editableList('addItem',el)
}
});
}
var subflowId;
for (subflowId in currentConfig.subflows) {
if (currentConfig.subflows.hasOwnProperty(subflowId)) {
seenTabs[subflowId] = true;
el = {
diff: localDiff,
def: {
defaults:{},
icon:"subflow.png",
category: "subflows",
color: "#da9"
},
tab:currentConfig.subflows[subflowId]
}
if (newConfig.subflows.hasOwnProperty(subflowId)) {
el.newTab = newConfig.subflows[subflowId];
}
if (remoteDiff !== undefined) {
el.remoteTab = remoteDiff.newConfig.subflows[subflowId];
el.remoteDiff = remoteDiff;
}
diffList.editableList('addItem',el)
}
}
for (subflowId in newConfig.subflows) {
if (newConfig.subflows.hasOwnProperty(subflowId) && !seenTabs[subflowId]) {
seenTabs[subflowId] = true;
el = {
diff: localDiff,
def: {
defaults:{},
icon:"subflow.png",
category: "subflows",
color: "#da9"
},
tab:newConfig.subflows[subflowId],
newTab:newConfig.subflows[subflowId]
}
if (remoteDiff !== undefined) {
el.remoteDiff = remoteDiff;
}
diffList.editableList('addItem',el)
}
}
if (remoteDiff !== undefined) {
for (subflowId in remoteDiff.newConfig.subflows) {
if (remoteDiff.newConfig.subflows.hasOwnProperty(subflowId) && !seenTabs[subflowId]) {
el = {
diff: localDiff,
remoteDiff: remoteDiff,
def: {
defaults:{},
icon:"subflow.png",
category: "subflows",
color: "#da9"
},
tab:remoteDiff.newConfig.subflows[subflowId],
remoteTab: remoteDiff.newConfig.subflows[subflowId]
}
diffList.editableList('addItem',el)
}
}
}
$("#sidebar-shade").show();
},
close: function() {
diffVisible = false;
$("#sidebar-shade").hide();
},
show: function() {
}
}
$("#node-diff-filter-changed").addClass("selected");
$("#node-diff-filter-all").removeClass("selected");
$("#node-dialog-view-diff").dialog("open");
RED.tray.show(trayOptions);
}
function mergeDiff(diff) {
var currentConfig = diff.localDiff.currentConfig;
var localDiff = diff.localDiff;
@@ -1260,7 +1332,6 @@ RED.diff = (function() {
RED.palette.refresh();
RED.workspaces.refresh();
RED.sidebar.config.refresh();
}
return {
init: init,

View File

@@ -660,7 +660,7 @@ RED.editor = (function() {
function buildLabelRow(type, index, value, placeHolder) {
var result = $('<div>',{class:"node-label-form-row"});
if (type === undefined) {
$('<span>').html("none").appendTo(result);
$('<span>').html(RED._("editor.noDefaultLabel")).appendTo(result);
result.addClass("node-label-form-none");
} else {
result.addClass("");

View File

@@ -89,7 +89,7 @@ RED.keyboard = (function() {
RED.userSettings.add({
id:'keyboard',
title: 'Keyboard',
title: RED._("keyboard.keyboard"),
get: getSettingsPane,
focus: function() {
setTimeout(function() {
@@ -350,7 +350,8 @@ RED.keyboard = (function() {
$(this).toggleClass("input-error",!valid);
})
var scopeSelect = $('<select><option value="*">global</option><option value="workspace">workspace</option></select>').appendTo(scope);
var scopeSelect = $('<select><option value="*" data-i18n="keyboard.global"></option><option value="workspace" data-i18n="keyboard.workspace"></option></select>').appendTo(scope);
scopeSelect.i18n();
scopeSelect.val(object.scope||'*');
var div = $('<div class="keyboard-shortcut-edit button-group-vertical"></div>').appendTo(scope);
@@ -468,9 +469,9 @@ RED.keyboard = (function() {
var pane = $('<div id="user-settings-tab-keyboard"></div>');
$('<div class="keyboard-shortcut-entry keyboard-shortcut-list-header">'+
'<div class="keyboard-shortcut-entry-key keyboard-shortcut-entry-text"><input id="user-settings-tab-keyboard-filter" type="text" placeholder="filter actions"></div>'+
'<div class="keyboard-shortcut-entry-key">shortcut</div>'+
'<div class="keyboard-shortcut-entry-scope">scope</div>'+
'<div class="keyboard-shortcut-entry-key keyboard-shortcut-entry-text"><input id="user-settings-tab-keyboard-filter" type="text" data-i18n="[placeholder]keyboard.filterActions"></div>'+
'<div class="keyboard-shortcut-entry-key" data-i18n="keyboard.shortcut"></div>'+
'<div class="keyboard-shortcut-entry-scope" data-i18n="keyboard.scope"></div>'+
'</div>').appendTo(pane);
pane.find("input").searchBox({

View File

@@ -423,7 +423,7 @@ RED.palette.editor = (function() {
RED.userSettings.add({
id:'palette',
title: 'Palette',
title: RED._("palette.editor.palette"),
get: getSettingsPane,
close: function() {
settingsPane.detach();

View File

@@ -125,7 +125,7 @@ RED.search = (function() {
function createDialog() {
dialog = $("<div>",{id:"red-ui-search",class:"red-ui-search"}).appendTo("#main-container");
var searchDiv = $("<div>",{class:"red-ui-search-container"}).appendTo(dialog);
searchInput = $('<input type="text" placeholder="search your flows">').appendTo(searchDiv).searchBox({
searchInput = $('<input type="text" data-i18n="[placeholder]menu.label.searchInput">').appendTo(searchDiv).searchBox({
delay: 200,
change: function() {
search($(this).val());
@@ -166,6 +166,7 @@ RED.search = (function() {
}
}
});
searchInput.i18n();
var searchResultsDiv = $("<div>",{class:"red-ui-search-results-container"}).appendTo(dialog);
searchResults = $('<ol>',{id:"search-result-list", style:"position: absolute;top: 5px;bottom: 5px;left: 5px;right: 5px;"}).appendTo(searchResultsDiv).editableList({

View File

@@ -50,11 +50,11 @@ RED.sidebar.info = (function() {
}).hide();
nodeSection = sections.add({
title: "Node",
title: RED._("sidebar.info.node"),
collapsible: false
});
infoSection = sections.add({
title: "Information",
title: RED._("sidebar.info.information"),
collapsible: false
});
infoSection.content.css("padding","6px");
@@ -132,15 +132,15 @@ RED.sidebar.info = (function() {
var propRow;
var subflowNode;
if (node.type === "tab") {
nodeSection.title.html("Flow");
propRow = $('<tr class="node-info-node-row"><td>Name</td><td></td></tr>').appendTo(tableBody);
nodeSection.title.html(RED._("sidebar.info.flow"));
propRow = $('<tr class="node-info-node-row"><td>'+RED._("sidebar.info.tabName")+'</td><td></td></tr>').appendTo(tableBody);
$(propRow.children()[1]).html('&nbsp;'+(node.label||""))
propRow = $('<tr class="node-info-node-row"><td>'+RED._("sidebar.info.id")+"</td><td></td></tr>").appendTo(tableBody);
RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]);
propRow = $('<tr class="node-info-node-row"><td>Status</td><td></td></tr>').appendTo(tableBody);
$(propRow.children()[1]).html((!!!node.disabled)?"Enabled":"Disabled")
propRow = $('<tr class="node-info-node-row"><td>'+RED._("sidebar.info.status")+'</td><td></td></tr>').appendTo(tableBody);
$(propRow.children()[1]).html((!!!node.disabled)?RED._("sidebar.info.enabled"):RED._("sidebar.info.disabled"))
} else {
nodeSection.title.html("Node");
nodeSection.title.html(RED._("sidebar.info.node"));
if (node.type !== "subflow" && node.name) {
$('<tr class="node-info-node-row"><td>'+RED._("common.label.name")+'</td><td>&nbsp;<span class="bidiAware" dir="'+RED.text.bidi.resolveBaseTextDir(node.name)+'">'+node.name+'</span></td></tr>').appendTo(tableBody);
}
@@ -188,7 +188,7 @@ RED.sidebar.info = (function() {
}
}
if (count > 0) {
$('<tr class="node-info-property-expand blank"><td colspan="2"><a href="#" class=" node-info-property-header'+(expandedSections.property?" expanded":"")+'"><span class="node-info-property-show-more">show more</span><span class="node-info-property-show-less">show less</span> <i class="fa fa-caret-down"></i></a></td></tr>').appendTo(tableBody);
$('<tr class="node-info-property-expand blank"><td colspan="2"><a href="#" class=" node-info-property-header'+(expandedSections.property?" expanded":"")+'"><span class="node-info-property-show-more">'+RED._("sidebar.info.showMore")+'</span><span class="node-info-property-show-less">'+RED._("sidebar.info.showLess")+'</span> <i class="fa fa-caret-down"></i></a></td></tr>').appendTo(tableBody);
}
}
}

View File

@@ -34,6 +34,10 @@ RED.tray = (function() {
if (options.title) {
$('<div class="editor-tray-titlebar">'+options.title+'</div>').appendTo(header);
}
if (options.width === Infinity) {
options.maximized = true;
resizer.addClass('editor-tray-resize-maximised');
}
var buttonBar = $('<div class="editor-tray-toolbar"></div>').appendTo(header);
var primaryButton;
if (options.buttons) {
@@ -74,7 +78,8 @@ RED.tray = (function() {
};
stack.push(tray);
el.draggable({
if (!options.maximized) {
el.draggable({
handle: resizer,
axis: "x",
start:function(event,ui) {
@@ -103,6 +108,7 @@ RED.tray = (function() {
tray.width = -ui.position.left;
}
});
}
function finishBuild() {
$("#header-shade").show();
@@ -175,7 +181,7 @@ RED.tray = (function() {
var tray = stack[stack.length-1];
var trayHeight = tray.tray.height()-tray.header.outerHeight()-tray.footer.outerHeight();
tray.body.height(trayHeight);
if (tray.width > $("#editor-stack").position().left-8) {
if (tray.options.maximized || tray.width > $("#editor-stack").position().left-8) {
tray.width = $("#editor-stack").position().left-8;
tray.tray.width(tray.width);
// tray.body.parent().width(tray.width);

View File

@@ -236,7 +236,7 @@ RED.typeSearch = (function() {
var items = [];
RED.nodes.registry.getNodeTypes().forEach(function(t) {
var def = RED.nodes.getType(t);
if (def.category !== 'config' && t !== 'unknown') {
if (def.category !== 'config' && t !== 'unknown' && t !== 'tab') {
items.push({type:t,def: def, label:getTypeLabel(t,def)});
}
});

View File

@@ -33,7 +33,7 @@ RED.userSettings = (function() {
var tabContainer;
var trayOptions = {
title: "User Settings",
title: RED._("menu.label.userSettings"),
buttons: [
{
id: "node-dialog-ok",
@@ -100,7 +100,7 @@ RED.userSettings = (function() {
var viewSettings = [
{
title: "Grid",
title: "menu.label.view.grid",
options: [
{setting:"view-show-grid",oldSetting:"menu-menu-item-view-show-grid",label:"menu.label.view.showGrid",toggle:true,onchange:"core:toggle-show-grid"},
{setting:"view-snap-grid",oldSetting:"menu-menu-item-view-snap-grid",label:"menu.label.view.snapGrid",toggle:true,onchange:"core:toggle-snap-grid"},
@@ -108,13 +108,13 @@ RED.userSettings = (function() {
]
},
{
title: "Nodes",
title: "menu.label.nodes",
options: [
{setting:"view-node-status",oldSetting:"menu-menu-item-status",label:"menu.label.displayStatus",default: true, toggle:true,onchange:"core:toggle-status"}
]
},
{
title: "Other",
title: "menu.label.other",
options: [
{setting:"view-show-tips",oldSettings:"menu-menu-item-show-tips",label:"menu.label.showTips",toggle:true,default:true,onchange:"core:toggle-show-tips"}
]
@@ -128,7 +128,7 @@ RED.userSettings = (function() {
var pane = $('<div id="user-settings-tab-view" class="node-help"></div>');
viewSettings.forEach(function(section) {
$('<h3></h3>').text(section.title).appendTo(pane);
$('<h3></h3>').text(RED._(section.title)).appendTo(pane);
section.options.forEach(function(opt) {
var initialState = RED.settings.get(opt.setting);
var row = $('<div class="user-settings-row"></div>').appendTo(pane);
@@ -169,7 +169,7 @@ RED.userSettings = (function() {
addPane({
id:'view',
title: 'View',
title: RED._("menu.label.view.view"),
get: createViewPane,
close: function() {
viewSettings.forEach(function(section) {

View File

@@ -50,24 +50,58 @@ RED.utils = (function() {
}
return result;
}
function makeExpandable(el,onexpand,expand) {
function makeExpandable(el,onbuild,ontoggle,expand) {
el.addClass("debug-message-expandable");
el.prop('toggle',function() {
return function(state) {
var parent = el.parent();
if (parent.hasClass('collapsed')) {
if (state) {
if (onbuild && !parent.hasClass('built')) {
onbuild();
parent.addClass('built');
}
parent.removeClass('collapsed');
return true;
}
} else {
if (!state) {
parent.addClass('collapsed');
return true;
}
}
return false;
}
});
el.click(function(e) {
var parent = $(this).parent();
if (parent.hasClass('collapsed')) {
if (onexpand && !parent.hasClass('built')) {
onexpand();
parent.addClass('built');
var currentState = !parent.hasClass('collapsed');
if ($(this).prop('toggle')(!currentState)) {
if (ontoggle) {
ontoggle(!currentState);
}
parent.removeClass('collapsed');
} else {
parent.addClass('collapsed');
}
// if (parent.hasClass('collapsed')) {
// if (onbuild && !parent.hasClass('built')) {
// onbuild();
// parent.addClass('built');
// }
// if (ontoggle) {
// ontoggle(true);
// }
// parent.removeClass('collapsed');
// } else {
// parent.addClass('collapsed');
// if (ontoggle) {
// ontoggle(false);
// }
// }
e.preventDefault();
});
if (expand) {
el.click();
}
}
var pinnedPaths = {};
@@ -189,10 +223,23 @@ RED.utils = (function() {
}
}
function buildMessageElement(obj,key,typeHint,hideKey,path,sourceId,rootPath,expandPaths) {
function buildMessageElement(obj,options) {
options = options || {};
var key = options.key;
var typeHint = options.typeHint;
var hideKey = options.hideKey;
var path = options.path;
var sourceId = options.sourceId;
var rootPath = options.rootPath;
var expandPaths = options.expandPaths;
var ontoggle = options.ontoggle;
var exposeApi = options.exposeApi;
var subElements = {};
var i;
var e;
var entryObj;
var expandableHeader;
var header;
var headerHead;
var value;
@@ -257,7 +304,7 @@ RED.utils = (function() {
$('<span class="debug-message-type-meta debug-message-object-type-header"></span>').html(typeHint||'string').appendTo(header);
var row = $('<div class="debug-message-object-entry collapsed"></div>').appendTo(element);
$('<pre class="debug-message-type-string"></pre>').text(obj).appendTo(row);
},checkExpanded(strippedKey,expandPaths));
},function(state) {if (ontoggle) { ontoggle(path,state);}}, checkExpanded(strippedKey,expandPaths));
}
e = $('<span class="debug-message-type-string debug-message-object-header"></span>').html('"'+formatString(sanitize(obj))+'"').appendTo(entryObj);
if (/^#[0-9a-f]{6}$/i.test(obj)) {
@@ -356,7 +403,20 @@ RED.utils = (function() {
if (fullLength <= 10) {
for (i=0;i<fullLength;i++) {
row = $('<div class="debug-message-object-entry collapsed"></div>').appendTo(arrayRows);
buildMessageElement(data[i],""+i,type==='buffer'?'hex':false,false,path+"["+i+"]",sourceId,rootPath,expandPaths).appendTo(row);
subElements[path+"["+i+"]"] = buildMessageElement(
data[i],
{
key: ""+i,
typeHint: type==='buffer'?'hex':false,
hideKey: false,
path: path+"["+i+"]",
sourceId: sourceId,
rootPath: rootPath,
expandPaths: expandPaths,
ontoggle: ontoggle,
exposeApi: exposeApi
}
).appendTo(row);
}
} else {
for (i=0;i<fullLength;i+=10) {
@@ -371,17 +431,35 @@ RED.utils = (function() {
return function() {
for (var i=min;i<=max;i++) {
var row = $('<div class="debug-message-object-entry collapsed"></div>').appendTo(parent);
buildMessageElement(data[i],""+i,type==='buffer'?'hex':false,false,path+"["+i+"]",sourceId,rootPath,expandPaths).appendTo(row);
subElements[path+"["+i+"]"] = buildMessageElement(
data[i],
{
key: ""+i,
typeHint: type==='buffer'?'hex':false,
hideKey: false,
path: path+"["+i+"]",
sourceId: sourceId,
rootPath: rootPath,
expandPaths: expandPaths,
ontoggle: ontoggle,
exposeApi: exposeApi
}
).appendTo(row);
}
}
})(),checkExpanded(strippedKey,expandPaths,minRange,Math.min(fullLength-1,(minRange+9))));
})(),
(function() { var path = path+"["+i+"]"; return function(state) {if (ontoggle) { ontoggle(path,state);}}})(),
checkExpanded(strippedKey,expandPaths,minRange,Math.min(fullLength-1,(minRange+9))));
$('<span class="debug-message-object-key"></span>').html("["+minRange+" &hellip; "+Math.min(fullLength-1,(minRange+9))+"]").appendTo(header);
}
if (fullLength < originalLength) {
$('<div class="debug-message-object-entry collapsed"><span class="debug-message-object-key">['+fullLength+' &hellip; '+originalLength+']</span></div>').appendTo(arrayRows);
}
}
},checkExpanded(strippedKey,expandPaths));
},
function(state) {if (ontoggle) { ontoggle(path,state);}},
checkExpanded(strippedKey,expandPaths));
}
} else if (typeof obj === 'object') {
element.addClass('collapsed');
@@ -395,17 +473,35 @@ RED.utils = (function() {
for (i=0;i<keys.length;i++) {
var row = $('<div class="debug-message-object-entry collapsed"></div>').appendTo(element);
var newPath = path;
if (/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(keys[i])) {
newPath += (newPath.length > 0?".":"")+keys[i];
} else {
newPath += "[\""+keys[i].replace(/"/,"\\\"")+"\"]"
if (newPath) {
if (/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(keys[i])) {
newPath += (newPath.length > 0?".":"")+keys[i];
} else {
newPath += "[\""+keys[i].replace(/"/,"\\\"")+"\"]"
}
}
buildMessageElement(obj[keys[i]],keys[i],false,false,newPath,sourceId,rootPath,expandPaths).appendTo(row);
subElements[newPath] = buildMessageElement(
obj[keys[i]],
{
key: keys[i],
typeHint: false,
hideKey: false,
path: newPath,
sourceId: sourceId,
rootPath: rootPath,
expandPaths: expandPaths,
ontoggle: ontoggle,
exposeApi: exposeApi
}
).appendTo(row);
}
if (keys.length === 0) {
$('<div class="debug-message-object-entry debug-message-type-meta collapsed"></div>').text("empty").appendTo(element);
}
},checkExpanded(strippedKey,expandPaths));
},
function(state) {if (ontoggle) { ontoggle(path,state);}},
checkExpanded(strippedKey,expandPaths));
}
if (key) {
$('<span class="debug-message-type-meta"></span>').html('object').appendTo(entryObj);
@@ -432,6 +528,28 @@ RED.utils = (function() {
} else {
$('<span class="debug-message-type-other"></span>').text(""+obj).appendTo(entryObj);
}
if (exposeApi) {
element.prop('expand', function() { return function(targetPath, state) {
if (path === targetPath) {
if (header.prop('toggle')) {
header.prop('toggle')(state);
}
} else if (subElements[targetPath] && subElements[targetPath].prop('expand') ) {
subElements[targetPath].prop('expand')(targetPath,state);
} else {
for (var p in subElements) {
if (subElements.hasOwnProperty(p)) {
if (targetPath.indexOf(p) === 0) {
if (subElements[p].prop('expand') ) {
subElements[p].prop('expand')(targetPath,state);
}
break;
}
}
}
}
}});
}
return element;
}
@@ -578,6 +696,8 @@ RED.utils = (function() {
return "icons/node-red/subflow.png"
} else if (node && node.type === 'unknown') {
return "icons/node-red/alert.png"
} else if (node && node.type === 'subflow') {
return "icons/node-red/subflow.png"
}
var icon_url;
if (typeof def.icon === "function") {

View File

@@ -482,7 +482,7 @@ RED.view = (function() {
try {
nn._def.onadd.call(nn);
} catch(err) {
console.log("onadd:",err);
console.log("Definition error: "+nn.type+".onadd:",err);
}
}
} else {
@@ -2581,7 +2581,7 @@ RED.view = (function() {
try {
node.n._def.onadd.call(node.n);
} catch(err) {
console.log("onadd:",err);
console.log("Definition error: "+node.n.type+".onadd:",err);
}
}

View File

@@ -30,7 +30,7 @@ RED.workspaces = (function() {
workspaceIndex += 1;
} while ($("#workspace-tabs a[title='"+RED._('workspace.defaultName',{number:workspaceIndex})+"']").size() !== 0);
ws = {type:"tab",id:tabId,label:RED._('workspace.defaultName',{number:workspaceIndex})};
ws = {type:"tab",id:tabId,disabled: false,info:"",label:RED._('workspace.defaultName',{number:workspaceIndex})};
RED.nodes.addWorkspace(ws);
workspace_tabs.addTab(ws);
workspace_tabs.activateTab(tabId);

View File

@@ -50,7 +50,7 @@ RED.user = (function() {
for (;i<data.prompts.length;i++) {
var field = data.prompts[i];
var row = $("<div/>",{class:"form-row"});
$('<label for="node-dialog-login-'+field.id+'">'+field.label+':</label><br/>').appendTo(row);
$('<label for="node-dialog-login-'+field.id+'">'+RED._(field.label)+':</label><br/>').appendTo(row);
var input = $('<input style="width: 100%" id="node-dialog-login-'+field.id+'" type="'+field.type+'" tabIndex="'+(i+1)+'"/>').appendTo(row);
if (i<data.prompts.length-1) {

View File

@@ -16,8 +16,6 @@
#node-dialog-view-diff {
height: 600px;
.red-ui-editableList-container {
border-radius:1px;
padding:0;
@@ -35,7 +33,6 @@
border: none;
min-height: 0;
}
}
.red-ui-editableList-item-content {
padding: 5px;
@@ -44,19 +41,23 @@
}
#node-dialog-view-diff-headers {
position: absolute;
left:17px;
right:32px;
left:237px;
right:18px;
top: 55px;
height: 25px;
.node-diff-node-entry-cell:not(:first-child) {
div {
height: 25px;
display: inline-block;
box-sizing: border-box;
width: 50%;
background: #f9f9f9;
text-align: center;
border-top: 1px solid $secondary-border-color;
border-color:$secondary-border-color;
border-left: 1px solid $secondary-border-color;
}
.node-diff-node-entry-cell:last-child {
div:last-child {
border-right: 1px solid $secondary-border-color;
}
}
@@ -106,10 +107,10 @@
font-size: 0.9em;
&:first-child {
border-top: 1px solid #eee;
border-top: 1px solid $secondary-border-color;
}
&:not(:last-child) {
border-bottom: 1px solid #eee;
border-bottom: 1px solid $secondary-border-color;
}
&.collapsed {
@@ -132,14 +133,21 @@
table {
border-collapse: collapse;
table-layout:fixed;
// Fix for table-layout: fixed on safari:
max-width: none;
width: auto;
min-width: 100%;
width: calc(100% - 20px);
margin-left: 20px;
}
col:first-child {
width: 180px;
}
col:not(:first-child) {
width: 100%;
}
td, th {
border: 1px solid $secondary-border-color;
border-top: 1px solid #f3f3f3;
border-left: 1px solid $secondary-border-color;
&:first-child {
border-left: none;
}
padding: 0 0 0 3px;
text-align: left;
overflow-x: auto;
@@ -150,13 +158,12 @@
white-space:nowrap;
overflow:hidden;
}
&:hover {
background: #f9f9f9;
}
}
td:first-child {
width: 140px;
}
td:not(:first-child) {
width: calc( 100% - 140px);
}
td {
.node-diff-status {
margin-left: 0;
@@ -179,8 +186,8 @@
width: 220px;
}
}
td:not(:first-child) {
width: calc( (100% - 140px) / 2);
col:not(:first-child) {
width:50%;
}
.node-diff-node-entry {
@@ -210,6 +217,9 @@
cursor: pointer;
padding: 0;
// background: #f6f6f6;
&:hover {
background: #f9f9f9;
}
}
.node-diff-tab-title-meta {
vertical-align: middle;
@@ -218,6 +228,9 @@
}
.node-diff-node-entry-header {
cursor: pointer;
&:hover {
background: #f9f9f9;
}
}
.node-diff-node-entry-node {
vertical-align: middle;
@@ -247,6 +260,9 @@
}
.node-diff-tab-title {
cursor: default;
&:hover {
background: none;
}
}
}
.node-diff-node-deleted {
@@ -301,7 +317,7 @@
}
}
.node-diff-node-entry-properties {
margin: 5px ;
margin: 0;
color: #666;
}
.node-diff-status {
@@ -313,6 +329,10 @@
margin-bottom: 6px;
text-align: center;
}
.node-diff-element {
display: inline-block;
width: calc(100% - 20px);
}
.node-diff-node-description {
color: $form-text-color;
@@ -346,7 +366,7 @@
box-sizing: border-box;
width: calc( (100% - 20px) / 2);
height: 32px;
border-left: 1px solid #eee;
border-left: 1px solid $secondary-border-color;
padding-top: 2px;
white-space: nowrap;
overflow: hidden;

View File

@@ -132,6 +132,11 @@
cursor: col-resize;
border-left: 1px solid $primary-border-color;
box-shadow: -1px 0 6px rgba(0,0,0,0.1);
&.editor-tray-resize-maximised {
background: $background-color;
cursor: default;
}
}
.editor-tray-resize-button {
@include workspace-button;
@@ -196,7 +201,7 @@
border-radius:5px;
overflow: hidden;
font-size: 14px !important;
font-family: monospace !important;
font-family: Menlo, Consolas, 'DejaVu Sans Mono', Courier, monospace !important;
}
.editor-button {

View File

@@ -224,4 +224,5 @@
bottom: 0;
right: 0;
background: $shade-color;
z-index: 5;
}

View File

@@ -128,6 +128,8 @@
'$map':{ args:[ 'array', 'function' ]},
'$match':{ args:[ 'str', 'pattern', 'limit' ]},
'$max':{ args:[ 'array' ]},
'$merge':{ args:[ 'array' ]},
'$millis':{ args:[ ]},
'$min':{ args:[ 'array' ]},
'$not':{ args:[ 'arg' ]},
'$now':{ args:[ ]},

View File

@@ -88,7 +88,7 @@
<div class="form-row inject-time-row hidden" id="inject-time-row-time">
<span data-i18n="inject.at"></span> <input id="inject-time-time" value="12:00"></input><br/>
<div id="inject-time-time-days" class="inject-time-days">
<div style="display: inline-block; vertical-align: top;margin-right: 5px;">on </div>
<div style="display: inline-block; vertical-align: top;margin-right: 5px;" data-i18n="inject.on"></div>
<div style="display:inline-block;">
<div>
<label><input type='checkbox' checked value='1'/> <span data-i18n="inject.days.0"></span></label>
@@ -181,7 +181,7 @@ If you want every 20 minutes from now - use the <i>"interval"</i> option.</p>
topic: {value:""},
payload: {value:"", validate: RED.validators.typedInput("payloadType")},
payloadType: {value:"date"},
repeat: {value:""},
repeat: {value:"", validate:function(v) { return ((v === "") || (RED.validators.number(v) && (v >= 0))) }},
crontab: {value:""},
once: {value:false}
},

View File

@@ -29,9 +29,7 @@ module.exports = function(RED) {
node.log("\n"+util.inspect(msg, {colors:useColors, depth:10}));
}
if (this.active) {
setTimeout(function() {
sendDebug({id:node.id,name:node.name,topic:msg.topic,msg:msg,_path:msg._path});
},0);
sendDebug({id:node.id,name:node.name,topic:msg.topic,msg:msg,_path:msg._path});
}
} else {
// debug user defined msg property
@@ -55,9 +53,7 @@ module.exports = function(RED) {
}
}
if (this.active) {
setTimeout(function() {
sendDebug({id:node.id,z:node.z,name:node.name,topic:msg.topic,property:property,msg:output,_path:msg._path});
},0);
sendDebug({id:node.id,z:node.z,name:node.name,topic:msg.topic,property:property,msg:output,_path:msg._path});
}
}
});
@@ -188,9 +184,7 @@ module.exports = function(RED) {
DebugNode.logHandler = new events.EventEmitter();
DebugNode.logHandler.on("log",function(msg) {
if (msg.level === RED.log.WARN || msg.level === RED.log.ERROR) {
setTimeout(function() {
sendDebug(msg);
},0);
sendDebug(msg);
}
});
RED.log.addHandler(DebugNode.logHandler);

View File

@@ -118,8 +118,8 @@
$('<div id="node-input-link-container-div" style="min-height: 100px;position: relative; box-sizing: border-box; border-radius: 2px; height: 180px; border: 1px solid #ccc;overflow:hidden; ">'+
' <div style="box-sizing: border-box; line-height: 20px; font-size: 0.8em; border-bottom: 1px solid #ddd; height: 20px;">'+
' <div style="display: inline-block;margin-left: 5px;"><a id="node-input-link-sort-label" href="#" data-i18n="[title]link.label.sortByLabel"><span data-i18n="link.label.node">name</span> <i class="node-input-link-sort-label-a fa fa-caret-down"></i><i class="node-input-link-sort-label-d fa fa-caret-up"></i></a></div>'+
' <div style="position: absolute; right: 10px; width: 50px; display: inline-block; text-align: right;"><a id="node-input-link-sort-type" href="#" data-i18n="[title]link.label.sortByFlow"><i class="node-input-link-sort-sublabel-a fa fa-caret-down"></i><i class="node-input-link-sort-sublabel-d fa fa-caret-up"></i> <span data-i18n="link.label.type">flow</span></a></div>'+
' <div style="display: inline-block;margin-left: 5px;"><a id="node-input-link-sort-label" href="#" data-i18n="[title]node-red:link.label.sortByLabel"><span data-i18n="node-red:link.label.node">name</span> <i class="node-input-link-sort-label-a fa fa-caret-down"></i><i class="node-input-link-sort-label-d fa fa-caret-up"></i></a></div>'+
' <div style="position: absolute; right: 10px; width: 50px; display: inline-block; text-align: right;"><a id="node-input-link-sort-type" href="#" data-i18n="[title]node-red:link.label.sortByFlow"><i class="node-input-link-sort-sublabel-a fa fa-caret-down"></i><i class="node-input-link-sort-sublabel-d fa fa-caret-up"></i> <span data-i18n="node-red:link.label.type">flow</span></a></div>'+
' </div>'+
' <div style="background: #fbfbfb; box-sizing: border-box; position:absolute; top:20px;bottom:0;left:0px;right:0px; overflow-y: scroll; overflow-x: hidden;">'+
' <ul id="node-input-link-container" style=" list-style-type:none; margin: 0;"></ul>'+
@@ -251,7 +251,7 @@
function onAdd() {
for (var i=0;i<this.links.length;i++) {
var n = RED.nodes.node(this.links[i]);
if (n.links.indexOf(this.id) === -1) {
if (n && n.links.indexOf(this.id) === -1) {
n.links.push(this.id);
}
}

View File

@@ -42,7 +42,8 @@
</div>
<div class="form-row">
<label for="node-input-timer"><i class="fa fa-clock-o"></i> <span data-i18n="exec.label.timeout"></span></label>
<input type="text" id="node-input-timer" style="width:65px;" data-i18n="[placeholder]exec.label.timeoutplace"> seconds
<input type="text" id="node-input-timer" style="width:65px;" data-i18n="[placeholder]exec.label.timeoutplace">
<span data-i18n="exec.label.seconds"></span>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>

View File

@@ -73,7 +73,13 @@
oneditprepare: function() {
var that = this;
$( "#node-input-outputs" ).spinner({
min:1
min:1,
change: function(event, ui) {
var value = this.value;
if (!value.match(/^\d+$/)) { value = 1; }
else if (value < this.min) { value = this.min; }
if (value !== this.value) { $(this).spinner("value", value); }
}
});
this.editor = RED.editor.createEditor({

View File

@@ -111,6 +111,9 @@ module.exports = function(RED) {
get: function() {
return node.context().get.apply(node,arguments);
},
keys: function() {
return node.context().keys.apply(node,arguments);
},
get global() {
return node.context().global;
},
@@ -124,6 +127,9 @@ module.exports = function(RED) {
},
get: function() {
return node.context().flow.get.apply(node,arguments);
},
keys: function() {
return node.context().flow.keys.apply(node,arguments);
}
},
global: {
@@ -132,6 +138,9 @@ module.exports = function(RED) {
},
get: function() {
return node.context().global.get.apply(node,arguments);
},
keys: function() {
return node.context().global.keys.apply(node,arguments);
}
},
setTimeout: function () {
@@ -178,6 +187,13 @@ module.exports = function(RED) {
}
}
};
if (util.hasOwnProperty('promisify')) {
sandbox.setTimeout[util.promisify.custom] = function(after, value) {
return new Promise(function(resolve, reject) {
sandbox.setTimeout(function(){ resolve(value) }, after);
});
}
}
var context = vm.createContext(sandbox);
try {
this.script = vm.createScript(functionText);

View File

@@ -30,7 +30,7 @@
<option value="css">CSS</option>
<option value="markdown">Markdown</option>
<option value="yaml">YAML</option>
<option value="text">none</option>
<option value="text" data-i18n="template.label.none"></option>
</select>
</div>
</div>

View File

@@ -46,7 +46,7 @@
<div id="random-details" class="form-row">
<label for="node-input-randomFirst"><i class="fa fa-clock-o"></i> <span data-i18n="delay.between"></span></label>
<input type="text" id="node-input-randomFirst" placeholder="" style="text-align:end; width:30px !important">
&nbsp;&nbsp;&amp;&nbsp;&nbsp;
<span data-i18n="delay.and"></span>
<input type="text" id="node-input-randomLast" placeholder="" style="text-align:end; width:30px !important">
<select id="node-input-randomUnits" style="width:140px !important">
<option value="milliseconds" data-i18n="delay.milisecs"></option>
@@ -129,13 +129,13 @@
defaults: {
name: {value:""},
pauseType: {value:"delay", required:true},
timeout: {value:"5", required:true, validate:RED.validators.number()},
timeout: {value:"5", required:true, validate:function(v) { return RED.validators.number(v) && (v >= 0); }},
timeoutUnits: {value:"seconds"},
rate: {value:"1", required:true, validate:RED.validators.number()},
rate: {value:"1", required:true, validate:function(v) { return RED.validators.number(v) && (v >= 0); }},
nbRateUnits: {value:"1", required:false, validate:RED.validators.regex(/\d+|/)},
rateUnits: {value: "second"},
randomFirst: {value:"1", required:true, validate:RED.validators.number()},
randomLast: {value:"5", required:true, validate:RED.validators.number()},
randomFirst: {value:"1", required:true, validate:function(v) { return RED.validators.number(v) && (v >= 0); }},
randomLast: {value:"5", required:true, validate:function(v) { return RED.validators.number(v) && (v >= 0); }},
randomUnits: {value: "seconds"},
drop: {value:false}
},

View File

@@ -171,6 +171,7 @@ module.exports = function(RED) {
}
if (msg.hasOwnProperty("reset")) {
clearInterval(node.intervalID);
node.intervalID = -1;
node.buffer = [];
node.status({text:"reset"});
}

View File

@@ -56,6 +56,13 @@
</ul>
</div>
<br/>
<div class="form-row">
<label data-i18n="trigger.for" for="node-input-bytopic"></label>
<select id="node-input-bytopic">
<option value="all" data-i18n="trigger.alltopics"></option>
<option value="topic" data-i18n="trigger.bytopics"></option>
</select>
</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"></input>
@@ -85,7 +92,7 @@
<p>If set to a <i>string</i> type, the node supports the mustache template syntax.</p>
<p>If the node receives a message with a <code>reset</code> property, or a <code>payload</code>
that matches that configured in the node, any timeout or repeat currently in
progress will be cleared and no message triggered.</o>
progress will be cleared and no message triggered.</p>
<p>The node can be configured to resend a message at a regular interval until it
is reset by a received message.</p>
</script>
@@ -103,6 +110,7 @@
extend: {value:"false"},
units: {value:"ms"},
reset: {value:""},
bytopic: {value: "all"},
name: {value:""}
},
inputs:1,

View File

@@ -19,6 +19,7 @@ module.exports = function(RED) {
var mustache = require("mustache");
function TriggerNode(n) {
RED.nodes.createNode(this,n);
this.bytopic = n.bytopic || "all";
this.op1 = n.op1 || "1";
this.op2 = n.op2 || "0";
this.op1type = n.op1type || "str";
@@ -47,7 +48,7 @@ module.exports = function(RED) {
this.extend = n.extend || "false";
this.units = n.units || "ms";
this.reset = n.reset || '';
this.duration = parseInt(n.duration);
this.duration = parseFloat(n.duration);
if (isNaN(this.duration)) {
this.duration = 250;
}
@@ -65,29 +66,32 @@ module.exports = function(RED) {
this.op2Templated = (this.op2type === 'str' && this.op2.indexOf("{{") != -1);
if ((this.op1type === "num") && (!isNaN(this.op1))) { this.op1 = Number(this.op1); }
if ((this.op2type === "num") && (!isNaN(this.op2))) { this.op2 = Number(this.op2); }
if (this.op1 == "null") { this.op1 = null; }
if (this.op2 == "null") { this.op2 = null; }
//if (this.op1 == "null") { this.op1 = null; }
//if (this.op2 == "null") { this.op2 = null; }
//try { this.op1 = JSON.parse(this.op1); }
//catch(e) { this.op1 = this.op1; }
//try { this.op2 = JSON.parse(this.op2); }
//catch(e) { this.op2 = this.op2; }
var node = this;
var tout = null;
var m2;
node.topics = {};
this.on("input", function(msg) {
var topic = msg.topic || "_none";
if (node.bytopic === "all") { topic = "_none"; }
node.topics[topic] = node.topics[topic] || {};
if (msg.hasOwnProperty("reset") || ((node.reset !== '') && (msg.payload == node.reset)) ) {
if (node.loop === true) { clearInterval(tout); }
else { clearTimeout(tout); }
tout = null;
if (node.loop === true) { clearInterval(node.topics[topic].tout); }
else { clearTimeout(node.topics[topic].tout); }
delete node.topics[topic];
node.status({});
}
else {
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); }
if (((!node.topics[topic].tout) && (node.topics[topic].tout !== 0)) || (node.loop === true)) {
if (node.op2type === "pay" || node.op2type === "payl") { node.topics[topic].m2 = RED.util.cloneMessage(msg.payload); }
else if (node.op2Templated) { node.topics[topic].m2 = mustache.render(node.op2,msg); }
else if (node.op2type !== "nul") {
m2 = RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg);
node.topics[topic].m2 = RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg);
}
if (node.op1type === "pay") { }
@@ -96,58 +100,67 @@ module.exports = function(RED) {
msg.payload = RED.util.evaluateNodeProperty(node.op1,node.op1type,node,msg);
}
if (node.op1type !== "nul") { node.send(msg); }
if (node.op1type !== "nul") { node.send(RED.util.cloneMessage(msg)); }
if (node.duration === 0) { tout = 0; }
if (node.duration === 0) { node.topics[topic].tout = 0; }
else if (node.loop === true) {
if (tout) { clearInterval(tout); }
/* istanbul ignore else */
if (node.topics[topic].tout) { clearInterval(node.topics[topic].tout); }
/* istanbul ignore else */
if (node.op1type !== "nul") {
var msg2 = RED.util.cloneMessage(msg);
tout = setInterval(function() { node.send(msg2); },node.duration);
node.topics[topic].tout = setInterval(function() { node.send(RED.util.cloneMessage(msg2)); }, node.duration);
}
}
else {
tout = setTimeout(function() {
node.topics[topic].tout = setTimeout(function() {
if (node.op2type !== "nul") {
var msg2 = RED.util.cloneMessage(msg);
if (node.op2type === "flow" || node.op2type === "global") {
m2 = RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg);
node.topics[topic].m2 = RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg);
}
msg2.payload = m2;
msg2.payload = node.topics[topic].m2;
node.send(msg2);
}
tout = null;
delete node.topics[topic];
node.status({});
},node.duration);
}, node.duration);
}
node.status({fill:"blue",shape:"dot",text:" "});
}
else if ((node.extend === "true" || node.extend === true) && (node.duration > 0)) {
if (tout) { clearTimeout(tout); }
if (node.op2type === "payl") { m2 = msg.payload; }
tout = setTimeout(function() {
/* istanbul ignore else */
if (node.op2type === "payl") { node.topics[topic].m2 = RED.util.cloneMessage(msg.payload); }
/* istanbul ignore else */
if (node.topics[topic].tout) { clearTimeout(node.topics[topic].tout); }
node.topics[topic].tout = setTimeout(function() {
if (node.op2type !== "nul") {
var msg2 = RED.util.cloneMessage(msg);
if (node.op2type === "flow" || node.op2type === "global") {
m2 = RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg);
node.topics[topic].m2 = RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg);
}
if (node.topics[topic] !== undefined) {
msg2.payload = node.topics[topic].m2;
node.send(msg2);
}
msg2.payload = m2;
node.send(msg2);
}
tout = null;
delete node.topics[topic];
node.status({});
},node.duration);
}, node.duration);
}
else {
if (node.op2type === "payl") { m2 = msg.payload; }
if (node.op2type === "payl") { node.topics[topic].m2 = RED.util.cloneMessage(msg.payload); }
}
}
});
this.on("close", function() {
if (tout) {
if (node.loop === true) { clearInterval(tout); }
else { clearTimeout(tout); }
tout = null;
for (var t in node.topics) {
/* istanbul ignore else */
if (node.topics[t]) {
if (node.loop === true) { clearInterval(node.topics[t].tout); }
else { clearTimeout(node.topics[t].tout); }
delete node.topics[t];
}
}
node.status({});
});

View File

@@ -41,14 +41,14 @@ RED.debug = (function() {
var content = $("<div>").css({"position":"relative","height":"100%"});
var toolbar = $('<div class="sidebar-header">'+
'<span class="button-group"><a id="debug-tab-filter" class="sidebar-header-button" href="#"><i class="fa fa-filter"></i> <span></span></a></span>'+
'<span class="button-group"><a id="debug-tab-clear" title="clear log" class="sidebar-header-button" href="#"><i class="fa fa-trash"></i></a></span></div>').appendTo(content);
'<span class="button-group"><a id="debug-tab-clear" class="sidebar-header-button" href="#" data-i18n="[title]node-red:debug.sidebar.clearLog"><i class="fa fa-trash"></i></a></span></div>').appendTo(content);
var footerToolbar = $('<div>'+
// '<span class="button-group">'+
// '<a class="sidebar-footer-button-toggle text-button selected" id="debug-tab-view-list" href="#"><span data-i18n="">list</span></a>'+
// '<a class="sidebar-footer-button-toggle text-button" id="debug-tab-view-table" href="#"><span data-i18n="">table</span></a> '+
// '</span>'+
'<span class="button-group"><a id="debug-tab-open" title="open in new window" class="sidebar-footer-button" href="#"><i class="fa fa-desktop"></i></a></span> ' +
'<span class="button-group"><a id="debug-tab-open" class="sidebar-footer-button" href="#" data-i18n="[title]node-red:debug.sidebar.openWindow"><i class="fa fa-desktop"></i></a></span> ' +
'</div>');
messageList = $('<div class="debug-content debug-content-list"/>').appendTo(content);
@@ -70,7 +70,7 @@ RED.debug = (function() {
var debugNodeListRow = $('<div class="debug-filter-row hide"></div>').appendTo(filterDialog);
var flowCheckboxes = {};
var debugNodeListHeader = $('<div><span>Debug nodes</span><span></span></div>');
var debugNodeListHeader = $('<div><span data-i18n="node-red:debug.sidebar.debugNodes"></span><span></span></div>');
var headerCheckbox = $('<input type="checkbox">').appendTo(debugNodeListHeader.find("span")[1]).checkboxSet();
debugNodeList = $('<ol>',{style:"text-align: left; min-height: 250px; max-height: 250px"}).appendTo(debugNodeListRow).editableList({
@@ -441,7 +441,14 @@ RED.debug = (function() {
}
var el = $('<span class="debug-message-payload"></span>').appendTo(msg);
var path = o.property||'';
var debugMessage = RED.utils.createObjectElement(payload,/*true*/null,format,false,path,sourceNode&&sourceNode.id,path);
var debugMessage = RED.utils.createObjectElement(payload, {
key: /*true*/null,
typeHint: format,
hideKey: false,
path: path,
sourceId: sourceNode&&sourceNode.id,
rootPath: path
});
// Do this in a separate step so the element functions aren't stripped
debugMessage.appendTo(el);
// NOTE: relying on function error to have a "type" that all other msgs don't

View File

@@ -1,142 +1,151 @@
<style>
.rpi-gpio-pinTable {
width: 340px;
display: inline-table;
font-size: 13px;
height: 380px;
min-height: 380px;
max-height: 380px;
}
.rpi-gpio-pinTable input[type="radio"] {
width: auto;
margin: 2px 2px;
}
.rpi-gpio-pinTable label {
width: auto;
margin: 0;
display: block;
}
.rpi-gpio-pinTable .pinTableBody {
width: 340px;
display: table-row-group;
line-height: 12px;
}
.rpi-gpio-pinTable .pinTableRow {
width: 340px;
display: table-row;
height: 14px;
}
.rpi-gpio-pinTable .pinTableCellL {
width: 170px;
display: table-cell;
text-align: right;
padding-right: 4px;
vertical-align: top;
border: 1px solid #444;
}
.rpi-gpio-pinTable .pinTableCellR {
width: 170px;
display: table-cell;
text-align: left;
padding-left: 4px;
vertical-align: top;
border: 1px solid #000;
}
.rpi-gpio-pinTable .pinColorPower {
background-color:#FECBCE;
}
.rpi-gpio-pinTable .pinColorGround {
background-color:#DDDDDD;
}
.rpi-gpio-pinTable .pinColorGPIO {
background-color:#BFEBBF;
}
.rpi-gpio-pinTable .pinColorDual {
background-color:#D0E6F4;
}
.rpi-gpio-pinTable .pinColorSD {
background-color:#FFFDD0;
}
</style>
<script type="text/x-red" data-template-name="rpi-gpio in">
<style>
.pinTable {
width: 300px;
display: inline-table;
font-size: 13px;
height: 380px;
min-height: 380px;
max-height: 380px;
}
.pinTableBody {
width: 300px;
display: table-row-group;
line-height: 12px;
}
.pinTableRow {
width: 300;
display: table-row;
height: 14px;
}
.pinTableCellL {
width: 150px;
display: table-cell;
text-align: right;
padding-right: 4px;
vertical-align: top;
border: 1px solid #444;
}
.pinTableCellR {
width: 150px;
display: table-cell;
text-align: left;
padding-left: 4px;
vertical-align: top;
border: 1px solid #000;
}
.pinColorPower {
background-color:#FECBCE;
}
.pinColorGround {
background-color:#DDDDDD;
}
.pinColorGPIO {
background-color:#BFEBBF;
}
.pinColorDual {
background-color:#D0E6F4;
}
.pinColorSD {
background-color:#FFFDD0;
}
</style>
<div class="form-row">
<div class="form-row" style="min-width: 540px">
<label><i class="fa fa-circle"></i> <span data-i18n="rpi-gpio.pinname"></span></label>
<input type="text" id="node-input-pin" style="display:none;">
<div class="pinTable">
<div class="pinTableBody"><form id="pinform" style="height:380px; max-height:380px; margin:initial;">
<div class="rpi-gpio-pinTable">
<div class="pinTableBody" id="pinform">
<div class="pinTableRow">
<div class="pinTableCellL pinColorPower">3.3V Power - 1 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorPower"><input disabled type="radio" name="pins" value="" style="width:auto;"> 2 - 5V Power</div>
<div class="pinTableCellL pinColorPower"><label>3.3V Power - 1 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorPower"><label><input disabled type="radio" name="pins" value=""> 2 - 5V Power</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual">SDA1 - GPIO02 - 3 <input type="radio" name="pins" value="3" style="width:auto;"></div>
<div class="pinTableCellR pinColorPower"><input disabled type="radio" name="pins" value="" style="width:auto;"> 4 - 5V Power</div>
<div class="pinTableCellL pinColorDual"><label for="pinTable-pin-3">SDA1 - GPIO02 - 3 <input id="pinTable-pin-3" type="radio" name="pins" value="3"></label></div>
<div class="pinTableCellR pinColorPower"><label><input disabled type="radio" name="pins" value=""> 4 - 5V Power</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual">SCL1 - GPIO03 - 5 <input type="radio" name="pins" value="5" style="width:auto;"></div>
<div class="pinTableCellR pinColorGround"><input disabled type="radio" name="pins" value="" style="width:auto;"> 6 - Ground</div>
<div class="pinTableCellL pinColorDual"><label for="pinTable-pin-5">SCL1 - GPIO03 - 5 <input id="pinTable-pin-5" type="radio" name="pins" value="5"></label></div>
<div class="pinTableCellR pinColorGround"><label><input disabled type="radio" name="pins" value=""> 6 - Ground</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO04 - 7 <input type="radio" name="pins" value="7" style="width:auto;"></div>
<div class="pinTableCellR pinColorDual"><input type="radio" name="pins" value="8" style="width:auto;"> 8 - GPIO14 - TxD</div>
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-7">GPIO04 - 7 <input id="pinTable-pin-7" type="radio" name="pins" value="7"></label></div>
<div class="pinTableCellR pinColorDual"><label for="pinTable-pin-8"><input id="pinTable-pin-8" type="radio" name="pins" value="8"> 8 - GPIO14 - TxD</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGround">Ground - 9 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorDual"><input type="radio" name="pins" value="10" style="width:auto;"> 10 - GPIO15 - RxD</div>
<div class="pinTableCellL pinColorGround"><label>Ground - 9 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorDual"><label for="pinTable-pin-10"><input id="pinTable-pin-10" type="radio" name="pins" value="10"> 10 - GPIO15 - RxD</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO17 - 11 <input type="radio" name="pins" value="11" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="12" style="width:auto;"> 12 - GPIO18</div>
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-11">GPIO17 - 11 <input id="pinTable-pin-11" type="radio" name="pins" value="11"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-12"><input id="pinTable-pin-12" type="radio" name="pins" value="12"> 12 - GPIO18</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO27 - 13 <input type="radio" name="pins" value="13" style="width:auto;"></div>
<div class="pinTableCellR pinColorGround"><input disabled type="radio" name="pins" value="" style="width:auto;"> 14 - Ground</div>
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-13">GPIO27 - 13 <input id="pinTable-pin-13" type="radio" name="pins" value="13"></label></div>
<div class="pinTableCellR pinColorGround"><label><input disabled type="radio" name="pins" value=""> 14 - Ground</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO22 - 15 <input type="radio" name="pins" value="15" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="16" style="width:auto;"> 16 - GPIO23</div>
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-15">GPIO22 - 15 <input id="pinTable-pin-15" type="radio" name="pins" value="15"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-16"><input id="pinTable-pin-16" type="radio" name="pins" value="16"> 16 - GPIO23</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorPower">3.3V Power - 17 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="18" style="width:auto;"> 18 - GPIO24</div>
<div class="pinTableCellL pinColorPower"><label>3.3V Power - 17 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-18"><input id="pinTable-pin-18" type="radio" name="pins" value="18"> 18 - GPIO24</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual">MOSI - GPIO10 - 19 <input type="radio" name="pins" value="19" style="width:auto;"></div>
<div class="pinTableCellR pinColorGround"><input disabled type="radio" name="pins" value="" style="width:auto;"> 20 - Ground</div>
<div class="pinTableCellL pinColorDual"><label for="pinTable-pin-19">MOSI - GPIO10 - 19 <input id="pinTable-pin-19" type="radio" name="pins" value="19"></label></div>
<div class="pinTableCellR pinColorGround"><label><input disabled type="radio" name="pins" value=""> 20 - Ground</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual">MISO - GPIO09 - 21 <input type="radio" name="pins" value="21" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="22" style="width:auto;"> 22 - GPIO25</div>
<div class="pinTableCellL pinColorDual"><label for="pinTable-pin-21">MISO - GPIO09 - 21 <input id="pinTable-pin-21" type="radio" name="pins" value="21"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-22"><input id="pinTable-pin-22" type="radio" name="pins" value="22"> 22 - GPIO25</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual">SCLK - GPIO11 - 23 <input type="radio" name="pins" value="23" style="width:auto;"></div>
<div class="pinTableCellR pinColorDual"><input type="radio" name="pins" value="24" style="width:auto;"> 24 - GPIO8 - CE0</div>
<div class="pinTableCellL pinColorDual"><label for="pinTable-pin-23">SCLK - GPIO11 - 23 <input id="pinTable-pin-23" type="radio" name="pins" value="23"></label></div>
<div class="pinTableCellR pinColorDual"><label for="pinTable-pin-24"><input id="pinTable-pin-24" type="radio" name="pins" value="24"> 24 - GPIO8 - CE0</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGround">Ground - 25 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorDual"><input type="radio" name="pins" value="26" style="width:auto;"> 26 - GPIO7 - CE1</div>
<div class="pinTableCellL pinColorGround"><label>Ground - 25 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorDual"><label for="pinTable-pin-26"><input id="pinTable-pin-26" type="radio" name="pins" value="26"> 26 - GPIO7 - CE1</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorSD">SD - 27 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorSD"><input disabled type="radio" name="pins" value="" style="width:auto;"> 28 - SC</div>
<div class="pinTableCellL pinColorSD"><label>SD - 27 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorSD"><label><input disabled type="radio" name="pins" value=""> 28 - SC</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO05 - 29 <input type="radio" name="pins" value="29" style="width:auto;"></div>
<div class="pinTableCellR pinColorGround"><input disabled type="radio" name="pins" value="" style="width:auto;"> 30 - Ground</div>
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-29">GPIO05 - 29 <input id="pinTable-pin-29" type="radio" name="pins" value="29"></label></div>
<div class="pinTableCellR pinColorGround"><label><input disabled type="radio" name="pins" value=""> 30 - Ground</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO06 - 31 <input type="radio" name="pins" value="31" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="32" style="width:auto;"> 32 - GPIO12</div>
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-31">GPIO06 - 31 <input id="pinTable-pin-31" type="radio" name="pins" value="31"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-32"><input id="pinTable-pin-32" type="radio" name="pins" value="32"> 32 - GPIO12</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO13 - 33 <input type="radio" name="pins" value="33" style="width:auto;"></div>
<div class="pinTableCellR pinColorGround"><input disabled type="radio" name="pins" value="" style="width:auto;"> 34 - Ground</div>
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-33">GPIO13 - 33 <input id="pinTable-pin-33" type="radio" name="pins" value="33"></label></div>
<div class="pinTableCellR pinColorGround"><label><input disabled type="radio" name="pins" value=""> 34 - Ground</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO19 - 35 <input type="radio" name="pins" value="35" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="36" style="width:auto;"> 36 - GPIO16</div>
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-35">GPIO19 - 35 <input id="pinTable-pin-35" type="radio" name="pins" value="35"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-36"><input id="pinTable-pin-36" type="radio" name="pins" value="36"> 36 - GPIO16</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO26 - 37 <input type="radio" name="pins" value="37" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="38" style="width:auto;"> 38 - GPIO20</div>
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-37">GPIO26 - 37 <input id="pinTable-pin-37" type="radio" name="pins" value="37"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-38"><input id="pinTable-pin-38" type="radio" name="pins" value="38"> 38 - GPIO20</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGround">Ground - 39 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="40" style="width:auto;"> 40 - GPIO21</div>
<div class="pinTableCellL pinColorGround"><label>Ground - 39 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-40"><input id="pinTable-pin-40" type="radio" name="pins" value="40"> 40 - GPIO21</label></div>
</div>
</form></div>
</div>
</div>
</div>
<div class="form-row">
@@ -251,143 +260,92 @@
</script>
<script type="text/x-red" data-template-name="rpi-gpio out">
<style>
.pinTable {
width: 300px;
display: inline-table;
font-size: 13px;
height: 380px;
min-height: 380px;
max-height: 380px;
}
.pinTableBody {
width: 300px;
display: table-row-group;
line-height: 12px;
}
.pinTableRow {
width: 300;
display: table-row;
height: 14px;
}
.pinTableCellL {
width: 150px;
display: table-cell;
text-align: right;
padding-right: 4px;
vertical-align: top;
border: 1px solid #444;
}
.pinTableCellR {
width: 150px;
display: table-cell;
text-align: left;
padding-left: 4px;
vertical-align: top;
border: 1px solid #000;
}
.pinColorPower {
background-color:#FECBCE;
}
.pinColorGround {
background-color:#DDDDDD;
}
.pinColorGPIO {
background-color:#BFEBBF;
}
.pinColorDual {
background-color:#D0E6F4;
}
.pinColorSD {
background-color:#FFFDD0;
}
</style>
<div class="form-row">
<div class="form-row" style="min-width: 540px">
<label><i class="fa fa-circle"></i> <span data-i18n="rpi-gpio.pinname"></span></label>
<input type="text" id="node-input-pin" style="display:none;">
<div class="pinTable">
<div class="pinTableBody"><form id="pinform" style="height:380px; max-height:380px; margin:initial;">
<div class="rpi-gpio-pinTable">
<div class="pinTableBody" id="pinform">
<div class="pinTableRow">
<div class="pinTableCellL pinColorPower">3.3V Power - 1 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorPower"><input disabled type="radio" name="pins" value="" style="width:auto;"> 2 - 5V Power</div>
<div class="pinTableCellL pinColorPower"><label>3.3V Power - 1 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorPower"><label><input disabled type="radio" name="pins" value=""> 2 - 5V Power</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual">SDA1 - GPIO02 - 3 <input type="radio" name="pins" value="3" style="width:auto;"></div>
<div class="pinTableCellR pinColorPower"><input disabled type="radio" name="pins" value="" style="width:auto;"> 4 - 5V Power</div>
<div class="pinTableCellL pinColorDual"><label for="pinTable-pin-3">SDA1 - GPIO02 - 3 <input id="pinTable-pin-3" type="radio" name="pins" value="3"></label></div>
<div class="pinTableCellR pinColorPower"><label><input disabled type="radio" name="pins" value=""> 4 - 5V Power</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual">SCL1 - GPIO03 - 5 <input type="radio" name="pins" value="5" style="width:auto;"></div>
<div class="pinTableCellR pinColorGround"><input disabled type="radio" name="pins" value="" style="width:auto;"> 6 - Ground</div>
<div class="pinTableCellL pinColorDual"><label for="pinTable-pin-5">SCL1 - GPIO03 - 5 <input id="pinTable-pin-5" type="radio" name="pins" value="5"></label></div>
<div class="pinTableCellR pinColorGround"><label><input disabled type="radio" name="pins" value=""> 6 - Ground</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO04 - 7 <input type="radio" name="pins" value="7" style="width:auto;"></div>
<div class="pinTableCellR pinColorDual"><input type="radio" name="pins" value="8" style="width:auto;"> 8 - GPIO14 - TxD</div>
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-7">GPIO04 - 7 <input id="pinTable-pin-7" type="radio" name="pins" value="7"></label></div>
<div class="pinTableCellR pinColorDual"><label for="pinTable-pin-8"><input id="pinTable-pin-8" type="radio" name="pins" value="8"> 8 - GPIO14 - TxD</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGround">Ground - 9 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorDual"><input type="radio" name="pins" value="10" style="width:auto;"> 10 - GPIO15 - RxD</div>
<div class="pinTableCellL pinColorGround"><label>Ground - 9 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorDual"><label for="pinTable-pin-10"><input id="pinTable-pin-10" type="radio" name="pins" value="10"> 10 - GPIO15 - RxD</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO17 - 11 <input type="radio" name="pins" value="11" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="12" style="width:auto;"> 12 - GPIO18</div>
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-11">GPIO17 - 11 <input id="pinTable-pin-11" type="radio" name="pins" value="11"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-12"><input id="pinTable-pin-12" type="radio" name="pins" value="12"> 12 - GPIO18</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO27 - 13 <input type="radio" name="pins" value="13" style="width:auto;"></div>
<div class="pinTableCellR pinColorGround"><input disabled type="radio" name="pins" value="" style="width:auto;"> 14 - Ground</div>
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-13">GPIO27 - 13 <input id="pinTable-pin-13" type="radio" name="pins" value="13"></label></div>
<div class="pinTableCellR pinColorGround"><label><input disabled type="radio" name="pins" value=""> 14 - Ground</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO22 - 15 <input type="radio" name="pins" value="15" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="16" style="width:auto;"> 16 - GPIO23</div>
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-15">GPIO22 - 15 <input id="pinTable-pin-15" type="radio" name="pins" value="15"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-16"><input id="pinTable-pin-16" type="radio" name="pins" value="16"> 16 - GPIO23</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorPower">3.3V Power - 17 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="18" style="width:auto;"> 18 - GPIO24</div>
<div class="pinTableCellL pinColorPower"><label>3.3V Power - 17 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-18"><input id="pinTable-pin-18" type="radio" name="pins" value="18"> 18 - GPIO24</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual">MOSI - GPIO10 - 19 <input type="radio" name="pins" value="19" style="width:auto;"></div>
<div class="pinTableCellR pinColorGround"><input disabled type="radio" name="pins" value="" style="width:auto;"> 20 - Ground</div>
<div class="pinTableCellL pinColorDual"><label for="pinTable-pin-19">MOSI - GPIO10 - 19 <input id="pinTable-pin-19" type="radio" name="pins" value="19"></label></div>
<div class="pinTableCellR pinColorGround"><label><input disabled type="radio" name="pins" value=""> 20 - Ground</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual">MISO - GPIO09 - 21 <input type="radio" name="pins" value="21" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="22" style="width:auto;"> 22 - GPIO25</div>
<div class="pinTableCellL pinColorDual"><label for="pinTable-pin-21">MISO - GPIO09 - 21 <input id="pinTable-pin-21" type="radio" name="pins" value="21"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-22"><input id="pinTable-pin-22" type="radio" name="pins" value="22"> 22 - GPIO25</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorDual">SCLK - GPIO11 - 23 <input type="radio" name="pins" value="23" style="width:auto;"></div>
<div class="pinTableCellR pinColorDual"><input type="radio" name="pins" value="24" style="width:auto;"> 24 - GPIO8 - CE0</div>
<div class="pinTableCellL pinColorDual"><label for="pinTable-pin-23">SCLK - GPIO11 - 23 <input id="pinTable-pin-23" type="radio" name="pins" value="23"></label></div>
<div class="pinTableCellR pinColorDual"><label for="pinTable-pin-24"><input id="pinTable-pin-24" type="radio" name="pins" value="24"> 24 - GPIO8 - CE0</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGround">Ground - 25 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorDual"><input type="radio" name="pins" value="26" style="width:auto;"> 26 - GPIO7 - CE1</div>
<div class="pinTableCellL pinColorGround"><label>Ground - 25 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorDual"><label for="pinTable-pin-26"><input id="pinTable-pin-26" type="radio" name="pins" value="26"> 26 - GPIO7 - CE1</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorSD">SD - 27 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorSD"><input disabled type="radio" name="pins" value="" style="width:auto;"> 28 - SC</div>
<div class="pinTableCellL pinColorSD"><label>SD - 27 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorSD"><label><input disabled type="radio" name="pins" value=""> 28 - SC</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO05 - 29 <input type="radio" name="pins" value="29" style="width:auto;"></div>
<div class="pinTableCellR pinColorGround"><input disabled type="radio" name="pins" value="" style="width:auto;"> 30 - Ground</div>
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-29">GPIO05 - 29 <input id="pinTable-pin-29" type="radio" name="pins" value="29"></label></div>
<div class="pinTableCellR pinColorGround"><label><input disabled type="radio" name="pins" value=""> 30 - Ground</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO06 - 31 <input type="radio" name="pins" value="31" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="32" style="width:auto;"> 32 - GPIO12</div>
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-31">GPIO06 - 31 <input id="pinTable-pin-31" type="radio" name="pins" value="31"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-32"><input id="pinTable-pin-32" type="radio" name="pins" value="32"> 32 - GPIO12</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO13 - 33 <input type="radio" name="pins" value="33" style="width:auto;"></div>
<div class="pinTableCellR pinColorGround"><input disabled type="radio" name="pins" value="" style="width:auto;"> 34 - Ground</div>
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-33">GPIO13 - 33 <input id="pinTable-pin-33" type="radio" name="pins" value="33"></label></div>
<div class="pinTableCellR pinColorGround"><label><input disabled type="radio" name="pins" value=""> 34 - Ground</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO19 - 35 <input type="radio" name="pins" value="35" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="36" style="width:auto;"> 36 - GPIO16</div>
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-35">GPIO19 - 35 <input id="pinTable-pin-35" type="radio" name="pins" value="35"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-36"><input id="pinTable-pin-36" type="radio" name="pins" value="36"> 36 - GPIO16</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGPIO">GPIO26 - 37 <input type="radio" name="pins" value="37" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="38" style="width:auto;"> 38 - GPIO20</div>
<div class="pinTableCellL pinColorGPIO"><label for="pinTable-pin-37">GPIO26 - 37 <input id="pinTable-pin-37" type="radio" name="pins" value="37"></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-38"><input id="pinTable-pin-38" type="radio" name="pins" value="38"> 38 - GPIO20</label></div>
</div>
<div class="pinTableRow">
<div class="pinTableCellL pinColorGround">Ground - 39 <input disabled type="radio" name="pins" value="" style="width:auto;"></div>
<div class="pinTableCellR pinColorGPIO"><input type="radio" name="pins" value="40" style="width:auto;"> 40 - GPIO21</div>
<div class="pinTableCellL pinColorGround"><label>Ground - 39 <input disabled type="radio" name="pins" value=""></label></div>
<div class="pinTableCellR pinColorGPIO"><label for="pinTable-pin-40"><input id="pinTable-pin-40" type="radio" name="pins" value="40"> 40 - GPIO21</label></div>
</div>
</form></div>
</div>
</div>
</div>
<div class="form-row" id="node-set-pwm">

View File

@@ -165,6 +165,10 @@
</script>
<script type="text/x-red" data-template-name="mqtt-broker">
<div class="form-row">
<label for="node-config-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-config-input-name" data-i18n="[placeholder]common.label.name">
</div>
<div class="form-row">
<ul style="background: #fff; min-width: 600px; margin-bottom: 20px;" id="node-config-mqtt-broker-tabs"></ul>
</div>
@@ -172,8 +176,8 @@
<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:20px; width:35px; "> <span data-i18n="mqtt.label.port"></span></label>
<input class="input-append-left" type="text" id="node-config-input-broker" style="width:40%;" data-i18n="[placeholder]mqtt.label.example">
<label for="node-config-input-port" style="margin-left:20px; width:43px; "> <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">
@@ -266,6 +270,7 @@
RED.nodes.registerType('mqtt-broker',{
category: 'config',
defaults: {
name: {value:""},
broker: {value:"",required:true},
port: {value:1883,required:true,validate:RED.validators.number()},
tls: {type:"tls-config",required: false},
@@ -296,9 +301,15 @@
password: {type: "password"}
},
label: function() {
var b = this.broker;
if (b === "") { b = "undefined"; }
return (this.clientid?this.clientid+"@":"")+b+":"+this.port;
var lab = this.name;
if ((lab === undefined) || (lab ==="")) {
var b = this.broker;
if (b === "") { b = "undefined"; }
lab = (this.clientid?this.clientid+"@":"")+b+":"+this.port;
}
return lab;
},
oneditprepare: function () {
var tabs = RED.tabs.create({

View File

@@ -213,7 +213,7 @@ msg.cookies = {
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"}
{value:"other",label:RED._("node-red:httpin.label.other"),icon:"red/images/typedInput/az.png"}
]
var contentTypes = [
{value:"application/json",label:"application/json",hasValue: false},
@@ -223,7 +223,7 @@ msg.cookies = {
{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"}
{value:"other",label:RED._("node-red:httpin.label.other"),icon:"red/images/typedInput/az.png"}
];
RED.nodes.registerType('http response',{

View File

@@ -135,7 +135,7 @@ module.exports = function(RED) {
}
var payload = null;
if (msg.payload && (method == "POST" || method == "PUT" || method == "PATCH" ) ) {
if (typeof msg.payload !== "undefined" && (method == "POST" || method == "PUT" || method == "PATCH" ) ) {
if (typeof msg.payload === "string" || Buffer.isBuffer(msg.payload)) {
payload = msg.payload;
} else if (typeof msg.payload == "number") {
@@ -244,19 +244,25 @@ module.exports = function(RED) {
}
}
// Convert the payload to the required return type
msg.payload = Buffer.concat(msg.payload); // bin
if (node.ret !== "bin") {
msg.payload = msg.payload.toString('utf8'); // txt
// Check that msg.payload is an array - if the req error
// handler has been called, it will have been set to a string
// and the error already handled - so no further action should
// be taken. #1344
if (Array.isArray(msg.payload)) {
// Convert the payload to the required return type
msg.payload = Buffer.concat(msg.payload); // bin
if (node.ret !== "bin") {
msg.payload = msg.payload.toString('utf8'); // txt
if (node.ret === "obj") {
try { msg.payload = JSON.parse(msg.payload); } // obj
catch(e) { node.warn(RED._("httpin.errors.json-error")); }
if (node.ret === "obj") {
try { msg.payload = JSON.parse(msg.payload); } // obj
catch(e) { node.warn(RED._("httpin.errors.json-error")); }
}
}
}
node.send(msg);
node.status({});
node.send(msg);
node.status({});
}
});
});
req.setTimeout(node.reqTimeout, function() {

View File

@@ -212,11 +212,12 @@
<div class="form-row">
<label for="node-input-server"><i class="fa fa-globe"></i> <span data-i18n="tcpin.label.server"></span></label>
<input type="text" id="node-input-server" placeholder="ip.address" style="width:45%">
&nbsp;port <input type="text" id="node-input-port" style="width:60px">
<span data-i18n="tcpin.label.port"></span>
<input type="text" id="node-input-port" style="width:60px">
</div>
<div class="form-row">
<label for="node-input-out"><i class="fa fa-sign-out"></i> <span data-i18n="tcpin.label.return"></span></label>
<select type="text" id="node-input-out" style="width:56%;">
<select type="text" id="node-input-out" style="width:54%;">
<option value="time" data-i18n="tcpin.return.timeout"></option>
<option value="char" data-i18n="tcpin.return.character"></option>
<option value="count" data-i18n="tcpin.return.number"></option>
@@ -272,7 +273,7 @@
}
else if ($("#node-input-out").val() == "time") {
if (previous != "time") { $("#node-input-splitc").val("0"); }
$("#node-units").text("ms");
$("#node-units").text(RED._("node-red:tcpin.label.ms"));
}
else if ($("#node-input-out").val() == "immed") {
if (previous != "immed") { $("#node-input-splitc").val(" "); }
@@ -280,7 +281,7 @@
}
else if ($("#node-input-out").val() == "count") {
if (previous != "count") { $("#node-input-splitc").val("12"); }
$("#node-units").text("chars");
$("#node-units").text(RED._("node-red:tcpin.label.chars"));
}
else {
if (previous != "sit") { $("#node-input-splitc").val(" "); }

View File

@@ -114,7 +114,10 @@
"name": "Debug messages",
"filterAll": "all nodes",
"filterSelected": "selected nodes",
"filterCurrent": "current flow"
"filterCurrent": "current flow",
"debugNodes": "Debug nodes",
"clearLog": "clear log",
"openWindow": "open in new window"
},
"messageMenu": {
"collapseAll": "Collapse all paths",
@@ -127,7 +130,11 @@
"linkIn": "link in",
"linkOut": "link out",
"label": {
"event": "Event name"
"event": "Event name",
"node": "name",
"type": "flow",
"sortByFlow":"Sort by flow",
"sortByLabel": "Sort by name"
}
},
"tls": {
@@ -155,7 +162,8 @@
"append": "Append",
"timeout": "Timeout",
"timeoutplace": "optional",
"return": "Output"
"return": "Output",
"seconds": "seconds"
},
"placeholder": {
"extraparams": "extra input parameters"
@@ -186,7 +194,8 @@
"output": "Output as",
"mustache": "Mustache template",
"plain": "Plain text",
"json": "Parsed JSON"
"json": "Parsed JSON",
"none": "none"
},
"templatevalue": "This is the payload: {{payload}} !"
},
@@ -212,6 +221,7 @@
"days": "Days",
"day": "Day",
"between": "Between",
"and": "&",
"rate": "Rate",
"msgper": "msg(s) per",
"dropmsg": "drop intermediate messages",
@@ -260,6 +270,9 @@
"wait-reset": "wait to be reset",
"wait-for": "wait for",
"wait-loop": "resend it every",
"for": "Handling",
"bytopics": "each msg.topic independently",
"alltopics": "all messages",
"duration": {
"ms": "Milliseconds",
"s": "Seconds",
@@ -293,6 +306,7 @@
"mqtt": {
"label": {
"broker": "Server",
"example": "e.g. localhost",
"qos": "QoS",
"clientid": "Client ID",
"port": "Port",
@@ -339,7 +353,8 @@
"return": "Return",
"upload": "Accept file uploads?",
"status": "Status code",
"headers": "Headers"
"headers": "Headers",
"other": "other"
},
"setby": "- set by msg.method -",
"basicauth": "Use basic authentication",
@@ -410,7 +425,9 @@
"close-connection": "Close connection after each message is sent?",
"decode-base64": "Decode Base64 message?",
"server": "Server",
"return": "Return"
"return": "Return",
"ms": "ms",
"chars": "chars"
},
"type": {
"listen": "Listen on",
@@ -762,6 +779,7 @@
"breakchunks": "Break into chunks",
"breaklines": "Break into lines",
"filelabel": "file",
"sendError": "Send message on error (legacy mode)",
"deletelabel": "delete __file__"
},
"action": {

View File

@@ -90,7 +90,7 @@
"node": "ノード",
"type": "型",
"selectAll": "全て選択",
"sortByLabel": "ノード名で並替え",
"sortByLabel": "ノード名で並替え",
"sortByType": "型で並べ替え"
},
"scope": {
@@ -114,7 +114,10 @@
"name": "デバッグメッセージ",
"filterAll": "全てのフロー",
"filterSelected": "選択したノード",
"filterCurrent": "現在のフロー"
"filterCurrent": "現在のフロー",
"debugNodes": "debugード",
"clearLog": "ログを削除",
"openWindow": "新しいウィンドウで開く"
},
"messageMenu": {
"collapseAll": "全パスを折りたたむ",
@@ -127,7 +130,11 @@
"linkIn": "link in",
"linkOut": "link out",
"label": {
"event": "イベント名"
"event": "イベント名",
"node": "名前",
"type": "フロー",
"sortByFlow": "フロー名で並べ替え",
"sortByLabel": "名前で並べ替え"
}
},
"tls": {
@@ -155,7 +162,8 @@
"append": "引数",
"timeout": "タイムアウト",
"timeoutplace": "任意",
"return": "出力"
"return": "出力",
"seconds": "秒"
},
"placeholder": {
"extraparams": "追加引数"
@@ -164,7 +172,7 @@
"exec": "コマンド終了時 - execモード",
"spawn": "コマンド実行中 - spawnモード"
},
"oldrc": "旧型式の出力を使用(互換モード)"
"oldrc": "旧型式の出力を使用(互換モード)"
},
"function": {
"label": {
@@ -186,7 +194,8 @@
"output": "出力形式",
"mustache": "Mustacheテンプレート",
"plain": "平文",
"json": "JSON"
"json": "JSON",
"none": "なし"
},
"templatevalue": "This is the payload: {{payload}} !"
},
@@ -212,6 +221,7 @@
"days": "日",
"day": "日",
"between": "頻度",
"and": "回/",
"rate": "流量",
"msgper": "メッセージ/",
"dropmsg": "仲介メッセージを削除",
@@ -293,6 +303,7 @@
"mqtt": {
"label": {
"broker": "サーバ",
"example": "例) localhost",
"qos": "QoS",
"clientid": "クライアント",
"port": "ポート",
@@ -339,7 +350,8 @@
"return": "出力形式",
"upload": "ファイルのアップロード",
"status": "状態コード",
"headers": "ヘッダ"
"headers": "ヘッダ",
"other": "その他"
},
"setby": "- msg.methodに定義 -",
"basicauth": "ベーシック認証を使用",
@@ -380,7 +392,7 @@
"tip": {
"path1": "標準では <code>payload</code> がwebsocketから送信、受信されるデータを持ちます。クライアントはJSON形式の文字列としてメッセージ全体を送信、受信するよう設定できます。",
"path2": "This path will be relative to ",
"url1": "URLには ws:&#47;&#47; or wss:&#47;&#47; スキーマを使用して、存在するwebsocketリスナを設定してください。",
"url1": "URLには ws:&#47;&#47; または wss:&#47;&#47; スキーマを使用して、存在するwebsocketリスナを設定してください。",
"url2": "標準では <code>payload</code> がwebsocketから送信、受信されるデータを持ちます。クライアントはJSON形式の文字列としてメッセージ全体を送信、受信するよう設定できます。"
},
"errors": {
@@ -410,7 +422,9 @@
"close-connection": "メッセージを送信するたびに接続を切断",
"decode-base64": "Base64メッセージの復号",
"server": "サーバ",
"return": "戻り値"
"return": "戻り値",
"ms": "ミリ秒",
"chars": "文字"
},
"type": {
"listen": "待ち受け",
@@ -760,6 +774,7 @@
"breakchunks": "チャンクへ分割",
"breaklines": "行へ分割",
"filelabel": "file",
"sendError": "エラーメッセージを送信(互換モード)",
"deletelabel": "delete __file__"
},
"action": {

View File

@@ -199,6 +199,7 @@
} else if (type === "btwn") {
row2.hide();
row3.show();
btwnValue2Field.typedInput('show');
} else {
row2.hide();
row3.hide();

View File

@@ -35,8 +35,8 @@ module.exports = function(RED) {
if (n > node.maxin) { n = node.maxin; }
}
if (node.action == "roll") {
if (n >= node.maxin) { n = (n - node.minin) % (node.maxin - node.minin) + node.minin; }
if (n < node.minin) { n = (n - node.minin) % (node.maxin - node.minin) + node.maxin; }
var divisor = node.maxin - node.minin;
n = ((n - node.minin) % divisor + divisor) % divisor + node.minin;
}
msg.payload = ((n - node.minin) / (node.maxin - node.minin) * (node.maxout - node.minout)) + node.minout;
if (node.round) { msg.payload = Math.round(msg.payload); }

View File

@@ -457,7 +457,7 @@ module.exports = function(RED) {
group.msg = msg;
var tcnt = group.targetCount;
if (msg.hasOwnProperty("parts")) { tcnt = group.targetCount || msg.parts.count; }
if (group.currentCount >= tcnt || msg.hasOwnProperty('complete')) {
if ((tcnt > 0 && group.currentCount >= tcnt) || msg.hasOwnProperty('complete')) {
completeSend(partId);
}
} catch(err) {

View File

@@ -38,7 +38,7 @@
color:"#DEBD5C",
defaults: {
name: {value:""},
pretty: {value:"false"}
pretty: {value:false}
},
inputs:1,
outputs:1,

View File

@@ -40,7 +40,7 @@
<h3>Details</h3>
<p>Each message payload will be added to the end of the file, optionally appending
a newline (\n) character between each one.</p>
<p>If <code>msg.filename</code> is used the file will be closed after every write.
<p>If <code>msg.filename</code> is used the file will be closed after every write.
For best performance use a fixed filename.</p>
<p>It can be configured to overwrite the entire file rather than append. For example,
when writing binary data to a file, such as an image, this option should be used
@@ -62,6 +62,11 @@
<option value="stream" data-i18n="file.output.stream"></option>
</select>
</div>
<div class="form-row">
<label></label>
<input type="checkbox" id="node-input-sendError" style="width:auto">
<label style="width:auto; margin-bottom:0; vertical-align: middle;" for="node-input-sendError" data-i18n="file.label.sendError"></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">
@@ -82,6 +87,12 @@
<dd>The contents of the file as either a string or binary buffer.</dd>
<dt class="optional">filename <span class="property-type">string</span></dt>
<dd>If not configured in the node, this optional property sets the name of the file to be read.</dd>
<dt class="optional">error <span class="property-type">object</span></dt>
<dd><i>deprecated</i>: If enabled in the node, when the node hits an error
reading the file, it will send a message with no <code>payload</code>
and this <code>error</code> property set to the error details. This
mode of behaviour is deprecated and not enabled by default for new
instances of the node. See below for more information.</dd>
</dl>
<h3>Details</h3>
<p>The filename should be an absolute path, otherwise it will be relative to
@@ -89,6 +100,13 @@
<p>On Windows, path separators may need to be escaped, for example: <code>\\Users\\myUser</code>.</p>
<p>Optionally, a text file can be split into lines, outputting one message per line, or a binary file
file into smaller buffer chunks, the chunk size is operating system dependant, but typically 64k (linux/mac) or 41k (Windows).</p>
<h4>Legacy error handling</h4>
<p>Before Node-RED 0.17, if this node hit an error whilst reading the file, it would
send a message with no <code>msg.payload</code> and <code>msg.error</code> set to the
details of the error. This is a deprecated mode of behaviour for the node that new
instances will not do. If required, this mode can be re-enabled within the node
configuration.</p>
<p>Errors should be caught and handled using a Catch node.</p>
</script>
<script type="text/javascript">
@@ -130,7 +148,8 @@
name: {value:""},
filename: {value:""},
format: {value:"utf8"},
chunk: {value:false}
chunk: {value:false},
sendError: {value: false}
},
color:"BurlyWood",
inputs:1,
@@ -146,6 +165,9 @@
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
if (this.sendError === undefined) {
$("#node-input-sendError").prop("checked",true);
}
$("#node-input-format").on("change",function() {
if ($("#node-input-format").val() === "utf8") {
$("#buffer-input-type").hide();

View File

@@ -72,14 +72,53 @@ module.exports = function(RED) {
node.wstream.end(node.data.shift());
}
else {
if ((!node.wstream) || (!node.filename)) {
// Append mode
var recreateStream = !node.wstream || !node.filename;
if (node.wstream && node.wstreamIno) {
// There is already a stream open and we have the inode
// of the file. Check the file hasn't been deleted
// or deleted and recreated.
try {
var stat = fs.statSync(filename);
// File exists - check the inode matches
if (stat.ino !== node.wstreamIno) {
// The file has been recreated. Close the current
// stream and recreate it
recreateStream = true;
node.wstream.end();
delete node.wstream;
delete node.wstreamIno;
}
} catch(err) {
// File does not exist
recreateStream = true;
node.wstream.end();
delete node.wstream;
delete node.wstreamIno;
}
}
if (recreateStream) {
node.wstream = fs.createWriteStream(filename, { encoding:'binary', flags:'a', autoClose:true });
node.wstream.on("open", function(fd) {
try {
var stat = fs.statSync(filename);
node.wstreamIno = stat.ino;
} catch(err) {
}
});
node.wstream.on("error", function(err) {
node.error(RED._("file.errors.appendfail",{error:err.toString()}),msg);
});
}
if (node.filename) { node.wstream.write(node.data.shift()); }
else { node.wstream.end(node.data.shift()); }
if (node.filename) {
// Static filename - write and reuse the stream next time
node.wstream.write(node.data.shift());
} else {
// Dynamic filename - write and close the stream
node.wstream.end(node.data.shift());
delete node.wstream;
delete node.wstreamIno;
}
}
}
}
@@ -98,6 +137,11 @@ module.exports = function(RED) {
this.filename = n.filename;
this.format = n.format;
this.chunk = false;
if (n.sendError === undefined) {
this.sendError = true;
} else {
this.sendError = n.sendError;
}
if (this.format === "lines") { this.chunk = true; }
if (this.format === "stream") { this.chunk = true; }
var node = this;
@@ -172,7 +216,13 @@ module.exports = function(RED) {
}
})
.on('error', function(err) {
node.error('Error while reading file.', msg);
node.error(err, msg);
if (node.sendError) {
var sendMessage = RED.util.cloneMessage(msg);
delete sendMessage.payload;
sendMessage.error = err;
node.send(sendMessage);
}
})
.on('end', function() {
if (node.chunk === false) {

View File

@@ -1,6 +1,6 @@
{
"name" : "node-red",
"version" : "0.17.3",
"version" : "0.17.5",
"description" : "A visual tool for wiring the Internet of Things",
"homepage" : "http://nodered.org",
"license" : "Apache-2.0",
@@ -36,6 +36,7 @@
"cors":"2.8.3",
"cron":"1.2.1",
"express": "4.15.3",
"express-session": "1.15.2",
"follow-redirects":"1.2.4",
"fs-extra": "1.0.0",
"fs.notify":"0.0.4",
@@ -44,7 +45,7 @@
"is-utf8":"0.2.1",
"js-yaml": "3.8.4",
"json-stringify-safe":"5.0.1",
"jsonata":"1.2.6",
"jsonata":"1.3.0",
"media-typer": "0.3.0",
"mqtt": "2.9.0",
"multer": "1.3.0",

19
red.js
View File

@@ -167,7 +167,16 @@ if (settings.httpNodeRoot !== false) {
settings.httpNodeAuth = settings.httpNodeAuth || settings.httpAuth;
}
settings.uiPort = parsedArgs.port||settings.uiPort||1880;
// if we got a port from command line, use it (even if 0)
// replicate (settings.uiPort = parsedArgs.port||settings.uiPort||1880;) but allow zero
if (parsedArgs.port !== undefined){
settings.uiPort = parsedArgs.port;
} else {
if (settings.uiPort === undefined){
settings.uiPort = 1880;
}
}
settings.uiHost = settings.uiHost||"0.0.0.0";
if (flowFile) {
@@ -261,9 +270,14 @@ if (settings.httpStatic) {
}
function getListenPath() {
var port = settings.serverPort;
if (port === undefined){
port = settings.uiPort;
}
var listenPath = 'http'+(settings.https?'s':'')+'://'+
(settings.uiHost == '0.0.0.0'?'127.0.0.1':settings.uiHost)+
':'+settings.uiPort;
':'+port;
if (settings.httpAdminRoot !== false) {
listenPath += settings.httpAdminRoot;
} else if (settings.httpStatic) {
@@ -292,6 +306,7 @@ RED.start().then(function() {
if (settings.httpAdminRoot === false) {
RED.log.info(RED.log._("server.admin-ui-disabled"));
}
settings.serverPort = server.address().port;
process.title = parsedArgs.title || 'node-red';
RED.log.info(RED.log._("server.now-running", {listenpath:getListenPath()}));
});

View File

@@ -84,7 +84,7 @@ function login(req,res) {
if (settings.adminAuth.type === "credentials") {
response = {
"type":"credentials",
"prompts":[{id:"username",type:"text",label:"Username"},{id:"password",type:"password",label:"Password"}]
"prompts":[{id:"username",type:"text",label:"user.username"},{id:"password",type:"password",label:"user.password"}]
}
} else if (settings.adminAuth.type === "strategy") {
response = {

View File

@@ -33,7 +33,7 @@ function handleStatus(event) {
publish("status/"+event.id,event.status,true);
}
function handleRuntimeEvent(event) {
publish("notification/"+event.id,event,true);
publish("notification/"+event.id,event.payload||{},event.retain);
}
function init(_server,runtime) {
server = _server;

View File

@@ -29,6 +29,7 @@
"label": {
"view": {
"view": "View",
"grid": "Grid",
"showGrid": "Show grid",
"snapGrid": "Snap to grid",
"gridSize": "Grid size",
@@ -41,12 +42,15 @@
"sidebar": {
"show": "Show sidebar"
},
"userSettings": "Settings",
"settings": "Settings",
"userSettings": "User Settings",
"nodes": "Nodes",
"displayStatus": "Show node status",
"displayConfig": "Configuration nodes",
"import": "Import",
"export": "Export",
"search": "Search flows",
"searchInput": "search your flows",
"clipboard": "Clipboard",
"library": "Library",
"examples": "Examples",
@@ -61,11 +65,15 @@
"login": "Login",
"logout": "Logout",
"editPalette":"Manage palette",
"showTips": "Show tips"
"other": "Other",
"showTips": "Show tips",
"help": "Node-RED website"
}
},
"user": {
"loggedInAs": "Logged in as __name__",
"username": "Username",
"password": "Password",
"login": "Login",
"loginFailed": "Login failed",
"notAuthorized": "Not authorized"
@@ -145,6 +153,7 @@
"improperlyConfigured": "The workspace contains some nodes that are not properly configured:",
"unknown": "The workspace contains some unknown node types:",
"confirm": "Are you sure you want to deploy?",
"doNotWarn": "do not warn about this again",
"conflict": "The server is running a more recent set of flows.",
"backgroundUpdate": "The flows on the server have been updated.",
"conflictChecking": "Checking to see if the changes can be merged automatically",
@@ -155,6 +164,8 @@
"diff": {
"unresolvedCount": "__count__ unresolved conflict",
"unresolvedCount_plural": "__count__ unresolved conflicts",
"globalNodes": "Global nodes",
"flowProperties": "Flow Properties",
"type": {
"added": "added",
"changed": "changed",
@@ -167,8 +178,8 @@
},
"nodeCount": "__count__ node",
"nodeCount_plural": "__count__ nodes",
"local":"Local",
"remote":"Remote"
"local":"Local changes",
"remote":"Remote changes"
},
"subflow": {
@@ -210,7 +221,13 @@
},
"keyboard": {
"title": "Keyboard Shortcuts",
"keyboard": "Keyboard",
"filterActions": "filter actions",
"shortcut": "shortcut",
"scope": "scope",
"unassigned": "Unassigned",
"global": "global",
"workspace": "workspace",
"selectAll": "Select all nodes",
"selectAllConnected": "Select all connected nodes",
"addRemoveNode": "Add/remove node from selection",
@@ -275,6 +292,7 @@
},
"editor": {
"title": "Manage palette",
"palette": "Palette",
"times": {
"seconds": "seconds ago",
"minutes": "minutes ago",
@@ -353,16 +371,24 @@
"sidebar": {
"info": {
"name": "Node information",
"tabName": "Name",
"label": "info",
"node": "Node",
"type": "Type",
"id": "ID",
"status": "Status",
"enabled": "Enabled",
"disabled": "Disabled",
"subflow": "Subflow",
"instances": "Instances",
"properties": "Properties",
"info": "Information",
"blank": "blank",
"null": "null",
"showMore": "show more",
"showLess": "show less",
"flow": "Flow",
"information": "Information",
"arrayItems": "__count__ items",
"showTips":"You can open the tips from the settings panel"
},
@@ -389,6 +415,7 @@
"re": "regular expression",
"bool": "boolean",
"json": "JSON",
"bin": "buffer",
"date": "timestamp"
}
},

View File

@@ -95,6 +95,10 @@
"args":"",
"desc":"Returns a pseudo random number greater than or equal to zero and less than one."
},
"$millis": {
"args":"",
"desc":"Returns the number of milliseconds since the Unix Epoch (1 January, 1970 UTC) as a number. All invocations of `$millis()` within an evaluation of an expression will all return the same value."
},
"$sum": {
"args": "array",
"desc": "Returns the arithmetic sum of an `array` of numbers. It is an error if the input `array` contains an item which isn't a number."
@@ -160,6 +164,10 @@
"args": "object",
"desc": "Splits an object containing key/value pairs into an array of objects, each of which has a single key/value pair from the input object. If the parameter is an array of objects, then the resultant array contains an object for every key/value pair in every object in the supplied array."
},
"$merge": {
"args": "array&lt;object&gt;",
"desc": "Merges an array of `objects` into a single `object` containing all the key/value pairs from each of the objects in the input array. If any of the input objects contain the same key, then the returned `object` will contain the value of the last one in the array. It is an error if the input array contains an item that is not an object."
},
"$sift": {
"args":"object, function",
"desc":"Returns an object that contains only the key/value pairs from the `object` parameter that satisfy the predicate `function` passed in as the second parameter.\n\nThe `function` that is supplied as the second parameter must have the following signature:\n\n`function(value [, key [, object]])`"

View File

@@ -29,6 +29,7 @@
"label": {
"view": {
"view": "表示",
"grid": "グリッド",
"showGrid": "グリッドを表示",
"snapGrid": "ノードの配置を補助",
"gridSize": "グリッドの大きさ",
@@ -41,12 +42,15 @@
"sidebar": {
"show": "サイドバーを表示"
},
"userSettings": "設定",
"settings": "設定",
"userSettings": "ユーザ設定",
"nodes": "ノード",
"displayStatus": "ノードの状態を表示",
"displayConfig": "ノードの設定",
"import": "読み込み",
"export": "書き出し",
"search": "ノードを検索",
"searchInput": "ノードを検索",
"clipboard": "クリップボード",
"library": "ライブラリ",
"examples": "サンプル",
@@ -61,11 +65,15 @@
"login": "ログイン",
"logout": "ログアウト",
"editPalette": "パレットの管理",
"showTips": "ヒントを表示"
"other": "その他",
"showTips": "ヒントを表示",
"help": "Node-REDウェブサイト"
}
},
"user": {
"loggedInAs": "__name__ としてログインしました",
"username": "ユーザ名",
"password": "パスワード",
"login": "ログイン",
"loginFailed": "ログインに失敗しました",
"notAuthorized": "権限がありません"
@@ -75,7 +83,7 @@
"warnings": {
"undeployedChanges": "ノードの変更をデプロイしていません",
"nodeActionDisabled": "ノードのアクションは、サブフロー内で無効になっています",
"missing-types": "不明なノードが存在するため、フローを停止しました。詳細はログを確認してください",
"missing-types": "不明なノードが存在するため、フローを停止しました。詳細はログを確認してください",
"restartRequired": "更新されたモジュールを有効化するため、Node-REDを再起動する必要があります"
},
"error": "<strong>エラー</strong>: __message__",
@@ -85,7 +93,7 @@
"lostConnectionTry": "すぐに接続",
"cannotAddSubflowToItself": "サブフロー自身を追加できません",
"cannotAddCircularReference": "循環参照を検出したため、サブフローを追加できません",
"unsupportedVersion": "サポートされていないバージョンのNode.jsを使用しています。<br/>最新のNode.js LTSに更新してください"
"unsupportedVersion": "サポートされていないバージョンのNode.jsを使用しています。<br/>最新のNode.js LTSに更新してください"
}
},
"clipboard": {
@@ -144,6 +152,7 @@
"improperlyConfigured": "以下のノードは、正しくプロパティが設定されていません:",
"unknown": "ワークスペースに未知の型のノードがあります。",
"confirm": "このままデプロイしても良いですか?",
"doNotWarn": "この警告を再度表示しない",
"conflict": "フローを編集している間に、他のブラウザがフローをデプロイしました。デプロイを継続すると、他のブラウザがデプロイしたフローが削除されます。",
"backgroundUpdate": "サーバ上のフローが更新されました",
"conflictChecking": "変更を自動的にマージしてよいか確認してください。",
@@ -154,6 +163,8 @@
"diff": {
"unresolvedCount": "未解決の衝突 __count__",
"unresolvedCount_plural": "未解決の衝突 __count__",
"globalNodes": "グローバルノード",
"flowProperties": "フロープロパティ",
"type": {
"added": "追加",
"changed": "変更",
@@ -166,8 +177,8 @@
},
"nodeCount": "ノード数 __count__",
"nodeCount_plural": "ノード数 __count__",
"local": "ローカル",
"remote": "リモート"
"local": "ローカルの変更",
"remote": "リモートの変更"
},
"subflow": {
"editSubflow": "フローのテンプレートを編集: __name__",
@@ -208,7 +219,13 @@
},
"keyboard": {
"title": "キーボードショートカット",
"keyboard": "キーボード",
"filterActions": "動作を検索",
"shortcut": "ショートカット",
"scope": "範囲",
"unassigned": "未割当",
"global": "グローバル",
"workspace": "ワークスペース",
"selectAll": "全てのノードを選択",
"selectAllConnected": "接続された全てのノードを選択",
"addRemoveNode": "ノードの選択、選択解除",
@@ -234,8 +251,8 @@
"exportToLibrary": "ライブラリへフローを書き出す",
"dialogSaveOverwrite": "__libraryName__ という __libraryType__ は既に存在しています 上書きしますか?",
"invalidFilename": "不正なファイル名",
"savedNodes": "保存されたノード",
"savedType": "保存された __type__",
"savedNodes": "フローを保存しました",
"savedType": "__type__ を保存しました",
"saveFailed": "保存に失敗しました: __message__",
"filename": "ファイル名",
"folder": "フォルダ",
@@ -271,6 +288,7 @@
},
"editor": {
"title": "パレットの管理",
"palette": "パレット",
"times": {
"seconds": "秒前",
"minutes": "分前",
@@ -312,12 +330,12 @@
"sortRecent": "日付順",
"more": "+ さらに __count__ 個",
"errors": {
"catalogLoadFailed": "ノードのカタログの読み込みに失敗しました<br>詳細はブラウザのコンソールを確認してください",
"installFailed": "追加処理が失敗しました: __module__<br>__message__<br>詳細はログを確認してください",
"removeFailed": "削除処理が失敗しました: __module__<br>__message__<br>詳細はログを確認してください",
"updateFailed": "更新処理が失敗しました: __module__<br>__message__<br>詳細はログを確認してください",
"enableFailed": "有効化処理が失敗しました: __module__<br>__message__<br>詳細はログを確認してください",
"disableFailed": "無効化処理が失敗しました: __module__<br>__message__<br>詳細はログを確認してください"
"catalogLoadFailed": "ノードのカタログの読み込みに失敗しました<br>詳細はブラウザのコンソールを確認してください",
"installFailed": "追加処理が失敗しました: __module__<br>__message__<br>詳細はログを確認してください",
"removeFailed": "削除処理が失敗しました: __module__<br>__message__<br>詳細はログを確認してください",
"updateFailed": "更新処理が失敗しました: __module__<br>__message__<br>詳細はログを確認してください",
"enableFailed": "有効化処理が失敗しました: __module__<br>__message__<br>詳細はログを確認してください",
"disableFailed": "無効化処理が失敗しました: __module__<br>__message__<br>詳細はログを確認してください"
},
"confirm": {
"install": {
@@ -347,16 +365,24 @@
"sidebar": {
"info": {
"name": "ノードの情報を表示",
"tabName": "名前",
"label": "情報",
"node": "ノード",
"type": "型",
"id": "ID",
"status": "状態",
"enabled": "有効",
"disabled": "無効",
"subflow": "サブフロー",
"instances": "インスタンス",
"properties": "プロパティ",
"info": "情報",
"blank": "ブランク",
"null": "ヌル",
"showMore": "さらに表示",
"showLess": "表示を省略",
"flow": "フロー",
"information": "情報",
"arrayItems": "__count__ 要素",
"showTips": "設定からヒントを表示できます"
},
@@ -383,6 +409,7 @@
"re": "正規表現",
"bool": "真偽",
"json": "JSON",
"bin": "バッファ",
"date": "日時"
}
},

View File

@@ -15,12 +15,12 @@
**/
var when = require("when");
var comms = require("./comms");
var locales = require("./locales");
var redNodes;
var log;
var i18n;
var settings;
var events;
module.exports = {
init: function(runtime) {
@@ -28,6 +28,7 @@ module.exports = {
log = runtime.log;
i18n = runtime.i18n;
settings = runtime.settings;
events = runtime.events;
},
getAll: function(req,res) {
if (req.get("accept") == "application/json") {
@@ -72,9 +73,9 @@ module.exports = {
}
promise.then(function(info) {
if (isUpgrade) {
comms.publish("node/upgraded",{module:node.module,version:node.version},false);
events.emit("runtime-event",{id:"node/upgraded",retain:false,payload:{module:node.module,version:node.version}});
} else {
comms.publish("node/added",info.nodes,false);
events.emit("runtime-event",{id:"node/added",retain:false,payload:info.nodes});
}
if (node.module) {
log.audit({event: "nodes.install",module:node.module,version:node.version},req);
@@ -113,7 +114,7 @@ module.exports = {
}
promise.then(function(list) {
comms.publish("node/removed",list,false);
events.emit("runtime-event",{id:"node/removed",retain:false,payload:list});
log.audit({event: "nodes.remove",module:mod},req);
res.status(204).end();
}).otherwise(function(err) {
@@ -245,7 +246,7 @@ function putNode(node, enabled) {
return promise.then(function(info) {
if (info.enabled === enabled && !info.err) {
comms.publish("node/"+(enabled?"enabled":"disabled"),info,false);
events.emit("runtime-event",{id:"node/"+(enabled?"enabled":"disabled"),retain:false,payload:info});
log.info(" "+log._("api.nodes."+(enabled?"enabled":"disabled")));
for (var i=0;i<info.types.length;i++) {
log.info(" - "+info.types[i]);

View File

@@ -106,7 +106,7 @@ function start() {
log.error("*****************************************************************");
log.error("* "+log._("runtime.unsupported_version",{component:"Node.js",version:process.version,requires: ">=4"})+" *");
log.error("*****************************************************************");
events.emit("runtime-event",{id:"runtime-unsupported-version",type:"error",text:"notification.errors.unsupportedVersion"});
events.emit("runtime-event",{id:"runtime-unsupported-version",payload:{type:"error",text:"notification.errors.unsupportedVersion"},retain:true});
}
log.info(os.type()+" "+os.release()+" "+os.arch()+" "+os.endianness());
return redNodes.load().then(function() {
@@ -133,26 +133,31 @@ function start() {
}
missingModules[missing.module].types = missingModules[missing.module].types.concat(missing.types);
}
var moduleList = [];
var promises = [];
var installingModules = [];
for (i in missingModules) {
if (missingModules.hasOwnProperty(i)) {
log.warn(" - "+i+" ("+missingModules[i].version+"): "+missingModules[i].types.join(", "));
if (settings.autoInstallModules && i != "node-red") {
redNodes.installModule(i,missingModules[i].version).otherwise(function(err) {
// Error already reported. Need the otherwise handler
// to stop the error propagating any further
});
installingModules.push({id:i,version:missingModules[i].version});
}
}
}
if (!settings.autoInstallModules) {
log.info(log._("server.removing-modules"));
redNodes.cleanModuleList();
} else if (installingModules.length > 0) {
reinstallAttempts = 0;
reinstallModules(installingModules);
}
}
if (settings.settingsFile) {
log.info(log._("runtime.paths.settings",{path:settings.settingsFile}));
}
if (settings.httpStatic) {
log.info(log._("runtime.paths.httpStatic",{path:path.resolve(settings.httpStatic)}));
}
redNodes.loadFlows().then(redNodes.startFlows);
started = true;
}).otherwise(function(err) {
@@ -161,6 +166,36 @@ function start() {
});
}
var reinstallAttempts;
var reinstallTimeout;
function reinstallModules(moduleList) {
var promises = [];
var failedModules = [];
for (var i=0;i<moduleList.length;i++) {
if (settings.autoInstallModules && i != "node-red") {
promises.push(redNodes.installModule(moduleList[i].id,moduleList[i].version));
}
}
when.settle(promises).then(function(results) {
var reinstallList = [];
for (var i=0;i<results.length;i++) {
if (results[i].state === 'rejected') {
reinstallList.push(moduleList[i]);
} else {
events.emit("runtime-event",{id:"node/added",retain:false,payload:results[i].value.nodes});
}
}
if (reinstallList.length > 0) {
reinstallAttempts++;
// First 5 at 1x timeout, next 5 at 2x, next 5 at 4x, then 8x
var timeout = (settings.autoInstallModulesRetry||30000) * Math.pow(2,Math.min(Math.floor(reinstallAttempts/5),3));
reinstallTimeout = setTimeout(function() {
reinstallModules(reinstallList);
},timeout);
}
});
}
function reportMetrics() {
var memUsage = process.memoryUsage();
@@ -186,6 +221,9 @@ function stop() {
clearInterval(runtimeMetricInterval);
runtimeMetricInterval = null;
}
if (reinstallTimeout) {
clearTimeout(reinstallTimeout);
}
started = false;
return redNodes.stopFlows();
}

View File

@@ -4,7 +4,8 @@
"version": "__component__ version: __version__",
"unsupported_version": "Unsupported version of __component__. Requires: __requires__ Found: __version__",
"paths": {
"settings": "Settings file : __path__"
"settings": "Settings file : __path__",
"httpStatic": "HTTP Static : __path__"
}
},

View File

@@ -58,7 +58,7 @@ function init(runtime) {
log.info(log._("nodes.flows.registered-missing", {type:type}));
activeFlowConfig.missingTypes.splice(i,1);
if (activeFlowConfig.missingTypes.length === 0 && started) {
events.emit("runtime-event",{id:"runtime-state"});
events.emit("runtime-event",{id:"runtime-state",retain: true});
start();
}
}
@@ -135,13 +135,13 @@ function setFlows(_config,type,muteLog) {
return stop(type,diff,muteLog).then(function() {
context.clean(activeFlowConfig);
start(type,diff,muteLog).then(function() {
events.emit("runtime-event",{id:"runtime-deploy",revision:flowRevision});
events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true});
});
return flowRevision;
}).otherwise(function(err) {
})
} else {
events.emit("runtime-event",{id:"runtime-deploy",revision:flowRevision});
events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true});
}
});
}
@@ -251,7 +251,7 @@ function start(type,diff,muteLog) {
log.info(log._("nodes.flows.missing-type-install-2"));
log.info(" "+settings.userDir);
}
events.emit("runtime-event",{id:"runtime-state",type:"warning",text:"notification.warnings.missing-types"});
events.emit("runtime-event",{id:"runtime-state",payload:{type:"warning",text:"notification.warnings.missing-types"},retain:true});
return when.resolve();
}
if (!muteLog) {
@@ -310,7 +310,7 @@ function start(type,diff,muteLog) {
}
}
events.emit("nodes-started");
events.emit("runtime-event",{id:"runtime-state"});
events.emit("runtime-event",{id:"runtime-state",retain:true});
if (!muteLog) {
if (type !== "full") {
@@ -351,6 +351,7 @@ function stop(type,diff,muteLog) {
for (var id in activeFlows) {
if (activeFlows.hasOwnProperty(id)) {
var flowStateChanged = diff && (diff.added.indexOf(id) !== -1 || diff.removed.indexOf(id) !== -1);
log.debug("red/nodes/flows.stop : stopping flow : "+id);
promises = promises.concat(activeFlows[id].stop(flowStateChanged?null:stopList,removedList));
if (type === "full" || flowStateChanged || diff.removed.indexOf(id)!==-1) {
delete activeFlows[id];

View File

@@ -30,8 +30,6 @@ var library = require("./library");
var events = require("../events");
var child_process = require('child_process');
var settings;
/**

View File

@@ -131,7 +131,7 @@ function installModule(module,version) {
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"});
events.emit("runtime-event",{id:"restart-required",payload:{type:"warning",text:"notification.warnings.restartRequired"},retain:true});
resolve(require("./registry").setModulePendingUpdated(module,version));
}
}

View File

@@ -128,7 +128,7 @@ function saveNodeList() {
}
}
if (hadPending && !hasPending) {
events.emit("runtime-event",{id:"restart-required"});
events.emit("runtime-event",{id:"restart-required",retain: true});
}
if (settings.available()) {
return settings.set("nodes",moduleList);

View File

@@ -45,62 +45,64 @@ function getFileMeta(root,path) {
var meta = {};
var read = 0;
var length = 10;
var remaining = "";
var remaining = Buffer(0);
var buffer = Buffer(length);
var idx = -1;
while(read < size) {
read+=fs.readSync(fd,buffer,0,length);
var data = remaining+buffer.toString();
var parts = data.split("\n");
remaining = parts.splice(-1);
for (var i=0;i<parts.length;i+=1) {
var match = /^\/\/ (\w+): (.*)/.exec(parts[i]);
var data = Buffer.concat([remaining,buffer]);
while((idx = data.indexOf("\n")) != -1){
var part = data.slice(0,idx+1);
var match = /^\/\/ (\w+): (.*)/.exec(part.toString());
if (match) {
meta[match[1]] = match[2];
} else {
read = size;
break;
}
data = data.slice(idx+1);
}
remaining = data;
}
fs.closeSync(fd);
return meta;
}
function getFileBody(root,path) {
var body = "";
var body = Buffer(0);
var fn = fspath.join(root,path);
var fd = fs.openSync(fn,"r");
var size = fs.fstatSync(fd).size;
var scanning = true;
var read = 0;
var length = 50;
var remaining = "";
var remaining = Buffer(0);
var buffer = Buffer(length);
var idx = -1;
while(read < size) {
var thisRead = fs.readSync(fd,buffer,0,length);
read += thisRead;
if (scanning) {
var data = remaining+buffer.slice(0,thisRead).toString();
var parts = data.split("\n");
remaining = parts.splice(-1)[0];
for (var i=0;i<parts.length;i+=1) {
if (! /^\/\/ \w+: /.test(parts[i])) {
var data = Buffer.concat([remaining,buffer.slice(0,thisRead)]);
while((idx = data.indexOf("\n")) != -1){
var part = data.slice(0,idx+1);
if (! /^\/\/ \w+: /.test(part.toString())) {
scanning = false;
body += parts[i]+"\n";
body = Buffer.concat([body,data]);
break;
}
data = data.slice(idx+1);
}
if (! /^\/\/ \w+: /.test(remaining)) {
scanning = false;
}
if (!scanning) {
body += remaining;
remaining = data;
if (scanning && read >= size) {
body = Buffer.concat([body,remaining]);
}
} else {
body += buffer.slice(0,thisRead).toString();
body = Buffer.concat([body,buffer.slice(0,thisRead)]);
}
}
fs.closeSync(fd);
return body;
return body.toString();
}
/**
@@ -419,6 +421,9 @@ var localfilesystem = {
if (settings.readOnly) {
return when.resolve();
}
if (type === "flows" && !path.endsWith(".json")) {
path += ".json";
}
var fn = fspath.join(libDir, type, path);
var headers = "";
for (var i in meta) {

View File

@@ -338,7 +338,7 @@ function prepareJSONataExpression(value,node) {
return node.context().flow.get(val);
});
expr.assign('globalContext',function(val) {
return node.context().global(val);
return node.context().global.get(val);
});
expr._legacyMode = /(^|[^a-zA-Z0-9_'"])msg([^a-zA-Z0-9_'"]|$)/.test(value);
return expr;

View File

@@ -174,7 +174,7 @@ module.exports = {
// 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) {
//webSocketNodeVerifyClient: function(info) {
// // 'info' has three properties:
// // - origin : the value in the Origin header
// // - req : the HTTP request

View File

@@ -75,7 +75,7 @@ describe('inject node', function() {
var n3 = helper.getNode("n3");
n3.on("input", function(msg) {
msg.should.have.property('topic', 't3');
msg.should.have.property('payload').be.a.Number;
msg.should.have.property('payload').be.a.Number();
helper.clearFlows().then(function() {
done();
});

View File

@@ -15,8 +15,10 @@
**/
var should = require("should");
var sinon = require("sinon");
var helper = require("../../helper.js");
var triggerNode = require("../../../../nodes/core/core/89-trigger.js");
var RED = require("../../../../red/red.js");
describe('trigger node', function() {
@@ -81,14 +83,17 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", '1');
c+=1;
}
else {
msg.should.have.a.property("payload", '0');
done();
try {
if (c === 0) {
msg.should.have.a.property("payload", '1');
c+=1;
}
else {
msg.should.have.a.property("payload", '0');
done();
}
}
catch(err) { done(err); }
});
n1.emit("input", {payload:null});
});
@@ -161,6 +166,155 @@ describe('trigger node', function() {
});
});
it('should handle multiple topics as one if not asked to handle', function(done) {
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", bytopic:"all", op1:"1", op2:"0", op1type:"num", op2type:"num", duration:"30", 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) {
try {
c += 1;
if (c === 1) {
msg.should.have.a.property("payload", 1);
msg.should.have.a.property("topic", "A");
}
else if (c === 2) {
msg.should.have.a.property("payload", 0);
msg.should.have.a.property("topic", "A");
done();
}
} catch(err) {
done(err);
}
});
n1.emit("input", {payload:1,topic:"A"});
n1.emit("input", {payload:2,topic:"B"});
n1.emit("input", {payload:3,topic:"C"});
});
});
it('should handle multiple topics individually if asked to do so', function(done) {
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", bytopic:"topic", op1:"1", op2:"0", op1type:"num", op2type:"num", duration:"30", 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) {
try {
c += 1;
if (c === 1) {
msg.should.have.a.property("payload", 1);
msg.should.have.a.property("topic", "A");
}
else if (c === 2) {
msg.should.have.a.property("payload", 1);
msg.should.have.a.property("topic", "B");
}
else if (c === 3) {
msg.should.have.a.property("payload", 1);
msg.should.have.a.property("topic", "C");
}
else if (c === 4) {
msg.should.have.a.property("payload", 0);
msg.should.have.a.property("topic", "A");
}
else if (c === 5) {
msg.should.have.a.property("payload", 0);
msg.should.have.a.property("topic", "B");
}
else if (c === 6) {
msg.should.have.a.property("payload", 0);
msg.should.have.a.property("topic", "C");
done();
}
} catch(err) {
done(err);
}
});
n1.emit("input", {payload:1,topic:"A"});
n1.emit("input", {payload:2,topic:"B"});
n1.emit("input", {payload:3,topic:"C"});
});
});
it('should handle multiple topics individually, and extend one, if asked to do so', function(done) {
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", bytopic:"topic", extend:"true", op1:"1", op2:"0", op1type:"num", op2type:"num", duration:"30", 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) {
try {
c += 1;
if (c === 1) {
msg.should.have.a.property("payload", 1);
msg.should.have.a.property("topic", "A");
}
else if (c === 2) {
msg.should.have.a.property("payload", 1);
msg.should.have.a.property("topic", "B");
}
else if (c === 3) {
msg.should.have.a.property("payload", 1);
msg.should.have.a.property("topic", "C");
}
else if (c === 4) {
msg.should.have.a.property("payload", 0);
msg.should.have.a.property("topic", "A");
}
else if (c === 5) {
msg.should.have.a.property("payload", 0);
msg.should.have.a.property("topic", "C");
}
else if (c === 6) {
msg.should.have.a.property("payload", 0);
msg.should.have.a.property("topic", "B");
done();
}
} catch(err) {
done(err);
}
});
n1.emit("input", {payload:1,topic:"A"});
n1.emit("input", {payload:2,topic:"B"});
n1.emit("input", {payload:3,topic:"C"});
setTimeout( function() { n1.emit("input", {payload:2,topic:"B"})}, 20 );
});
});
it('should be able to return things from flow and global context variables', function(done) {
var spy = sinon.stub(RED.util, 'evaluateNodeProperty',
function(arg1, arg2, arg3, arg4) { return arg1; }
);
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1:"foo", op1type:"flow", op2:"bar", op2type:"global", duration:"20", 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) {
try {
if (c === 0) {
msg.should.have.a.property("payload", "foo");
c+=1;
}
else {
msg.should.have.a.property("payload", "bar");
spy.restore();
done();
}
}
catch(err) { spy.restore(); done(err); }
});
n1.emit("input", {payload:null});
});
});
it('should be able to not output anything on first trigger', function(done) {
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1type:"nul", op1:"true",op2:"false",op2type:"val",duration:"30", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
@@ -187,8 +341,11 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
msg.should.have.a.property("payload", true);
c += 1;
try {
msg.should.have.a.property("payload", true);
c += 1;
}
catch(err) { done(err); }
});
setTimeout( function() {
c.should.equal(1); // should only have had one output.
@@ -199,23 +356,30 @@ describe('trigger node', function() {
});
it('should be able to extend the delay', function(done) {
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"true", op1type:"pay", op1:"false", op2:"true", duration:"100", wires:[["n2"]] },
var spy = sinon.stub(RED.util, 'evaluateNodeProperty',
function(arg1, arg2, arg3, arg4) { return arg1; }
);
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"true", op1type:"flow", op1:"foo", op2:"bar", op2type:"global", duration:"100", 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) {
if (c === 0) {
msg.should.have.a.property("payload", "Hello");
c += 1;
}
else {
msg.should.have.a.property("payload", "true");
//console.log(Date.now() - ss);
(Date.now() - ss).should.be.greaterThan(149);
done();
try {
if (c === 0) {
msg.should.have.a.property("payload", "foo");
c += 1;
}
else {
msg.should.have.a.property("payload", "bar");
//console.log(Date.now() - ss);
(Date.now() - ss).should.be.greaterThan(149);
spy.restore();
done();
}
}
catch(err) { spy.restore(); done(err); }
});
var ss = Date.now();
n1.emit("input", {payload:"Hello"});
@@ -233,16 +397,19 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", "Hello");
c += 1;
}
else {
msg.should.have.a.property("payload", "World");
//console.log(Date.now() - ss);
(Date.now() - ss).should.be.greaterThan(70);
done();
try {
if (c === 0) {
msg.should.have.a.property("payload", "Hello");
c += 1;
}
else {
msg.should.have.a.property("payload", "World");
//console.log(Date.now() - ss);
(Date.now() - ss).should.be.greaterThan(70);
done();
}
}
catch(err) { done(err); }
});
var ss = Date.now();
n1.emit("input", {payload:"Hello"});
@@ -263,15 +430,18 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", "Goodbye");
c += 1;
}
else {
msg.should.have.a.property("payload", "World");
(Date.now() - ss).should.be.greaterThan(70);
done();
try {
if (c === 0) {
msg.should.have.a.property("payload", "Goodbye");
c += 1;
}
else {
msg.should.have.a.property("payload", "World");
(Date.now() - ss).should.be.greaterThan(70);
done();
}
}
catch(err) { done(err); }
});
var ss = Date.now();
n1.emit("input", {payload:"Hello"});
@@ -292,15 +462,18 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", "Goodbye");
c += 1;
}
else {
msg.should.have.a.property("payload", "World");
(Date.now() - ss).should.be.greaterThan(70);
done();
try {
if (c === 0) {
msg.should.have.a.property("payload", "Goodbye");
c += 1;
}
else {
msg.should.have.a.property("payload", "World");
(Date.now() - ss).should.be.greaterThan(70);
done();
}
}
catch(err) { done(err); }
});
var ss = Date.now();
n1.emit("input", {payload:"Hello"});
@@ -321,14 +494,17 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", "Hello");
c+=1;
}
else {
msg.should.have.a.property("payload", "World");
done();
try {
if (c === 0) {
msg.should.have.a.property("payload", "Hello");
c+=1;
}
else {
msg.should.have.a.property("payload", "World");
done();
}
}
catch(err) { done(err); }
});
n1.emit("input", {payload:"Hello",topic:"World"});
});
@@ -342,19 +518,46 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", null);
c+=1;
}
else {
msg.should.have.a.property("payload", "World");
done();
try {
if (c === 0) {
msg.should.have.a.property("payload", null);
c+=1;
}
else {
msg.should.have.a.property("payload", "World");
done();
}
}
catch(err) { done(err); }
});
n1.emit("input", {payload:"World"});
});
});
it('should handle string null as null on op2', function(done) {
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1type:"val", op2type:"val", op1:"null", op2:"null", duration:"40", 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) {
try {
if (c === 0) {
msg.should.have.a.property("payload", null);
c+=1;
}
else {
msg.should.have.a.property("payload", null);
done();
}
}
catch(err) { done(err); }
});
n1.emit("input", {payload:"null"});
});
});
it('should be able to set infinite timeout, and clear timeout', function(done) {
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", duration:"0", extend: false, wires:[["n2"]] },
{id:"n2", type:"helper"} ];
@@ -363,8 +566,11 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
c += 1;
msg.should.have.a.property("payload", 1);
try {
c += 1;
msg.should.have.a.property("payload", "1");
}
catch(err) { done(err); }
});
setTimeout( function() {
if (c === 2) { done(); }
@@ -388,8 +594,11 @@ describe('trigger node', function() {
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
c += 1;
msg.should.have.a.property("payload", 1);
try {
c += 1;
msg.should.have.a.property("payload", "1");
}
catch(err) { done(err); }
});
setTimeout( function() {
if (c === 2) { done(); }
@@ -406,7 +615,7 @@ 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"]] },
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", reset:"boo", op1:"", op1type:"pay", duration:-25, wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(triggerNode, flow, function() {
var n1 = helper.getNode("n1");
@@ -414,9 +623,14 @@ describe('trigger node', function() {
var c = 0;
n2.on("input", function(msg) {
c += 1;
msg.should.have.a.property("payload", "foo");
try {
msg.should.have.property('payload','foo');
msg.payload = "bar"; // try to provoke pass by reference error
}
catch(err) { done(err); }
});
n1.emit("input", {payload:"foo"}); // trigger
n1.emit("input", {payload:"foo"}); // trigger
setTimeout( function() {
n1.emit("input", {reset:true}); // reset
},90);

View File

@@ -328,6 +328,30 @@ describe('HTTP Request Node', function() {
});
});
it('send a payload of 0 as the body of a POST as text/plain', function(done) {
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')},
{id:"n2", type:"helper"}];
helper.load(httpRequestNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
try {
msg.should.have.property('payload');
msg.payload.body.should.eql('0');
msg.payload.headers.should.have.property('content-length','1');
msg.payload.headers.should.have.property('content-type').which.startWith('text/plain');
msg.should.have.property('statusCode',200);
msg.should.have.property('headers');
done();
} catch(err) {
done(err);
}
});
n1.receive({payload:0, headers: { 'content-type': 'text/plain'}});
});
});
it('send an Object payload as the body of a POST', function(done) {
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')},
{id:"n2", type:"helper"}];

View File

@@ -422,7 +422,8 @@ describe('websocket Node', function() {
helper.load(websocketNode, flow, function() {
getSocket('server').on('connection', function(sock) {
sock.on('message', function(msg) {
msg.should.have.length(5).and.be.a.buffer;
Buffer.isBuffer(msg).should.be.true();
msg.should.have.length(5);
done();
});
});

View File

@@ -90,14 +90,14 @@ describe('range Node', function() {
genericRangeTest("roll", 0, 10, 0, 360, true, 12.5, 90, done); // 1/4 around wrap => "one and a quarter turns"
});
it('wraps numbers around say for degree/rotation reading 1/4', function(done) {
genericRangeTest("roll", 0, 10, 0, 360, true, 12.5, 90, done); // 1/4 around wrap => "one and a quarter turns"
});
it('wraps numbers down say for degree/rotation reading 1/4', function(done) {
genericRangeTest("roll", 0, 10, 0, 360, true, -12.5, 270, done); // 1/4 backwards wrap => "one and a quarter turns backwards"
});
it('wraps numbers around say for degree/rotation reading 0', function(done) {
genericRangeTest("roll", 0, 10, 0, 360, true, -10, 0, done);
});
it('clamps numbers within a range - over max', function(done) {
genericRangeTest("clamp", 0, 10, 0, 1000, false, 111, 1000, done);
});

View File

@@ -426,7 +426,7 @@ describe('JOIN node', function() {
});
it('should accumulate a merged object', function(done) {
var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"merged",mode:"custom",accumulate:true},
var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"merged",mode:"custom",accumulate:true, count:1},
{id:"n2", type:"helper"}];
helper.load(joinNode, flow, function() {
var n1 = helper.getNode("n1");
@@ -455,7 +455,7 @@ describe('JOIN node', function() {
});
it('should be able to reset an accumulation', function(done) {
var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"merged",accumulate:true,mode:"custom"},
var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"merged",accumulate:true,mode:"custom", count:1},
{id:"n2", type:"helper"}];
helper.load(joinNode, flow, function() {
var n1 = helper.getNode("n1");
@@ -519,7 +519,7 @@ describe('JOIN node', function() {
});
it('should join strings with a specifed character after a timeout', function(done) {
var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"string", timeout:0.05, count:10, joiner:",",mode:"custom"},
var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"string", timeout:0.05, count:"", joiner:",",mode:"custom"},
{id:"n2", type:"helper"}];
helper.load(joinNode, flow, function() {
var n1 = helper.getNode("n1");
@@ -539,7 +539,7 @@ describe('JOIN node', function() {
});
it('should join strings with a specifed character and complete when told to', function(done) {
var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"string", timeout:5, count:100, joiner:"\n",mode:"custom"},
var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"string", timeout:5, count:0, joiner:"\n",mode:"custom"},
{id:"n2", type:"helper"}];
helper.load(joinNode, flow, function() {
var n1 = helper.getNode("n1");

View File

@@ -67,6 +67,9 @@ describe('file Nodes', function() {
it('should append to a file and add newline', function(done) {
var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":true, "overwriteFile":false}];
try {
fs.unlinkSync(fileToTest);
}catch(err) {}
helper.load(fileNode, flow, function() {
var n1 = helper.getNode("fileNode1");
n1.emit("input", {payload:"test2"}); // string
@@ -94,6 +97,92 @@ describe('file Nodes', function() {
});
});
it('should append to a file after it has been deleted ', function(done) {
var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":false, "overwriteFile":false}];
try {
fs.unlinkSync(fileToTest);
} catch(err) {}
helper.load(fileNode, flow, function() {
var n1 = helper.getNode("fileNode1");
// Send two messages to the file
n1.emit("input", {payload:"one"});
n1.emit("input", {payload:"two"});
setTimeout(function() {
try {
// Check they got appended as expected
var f = fs.readFileSync(fileToTest).toString();
f.should.equal("onetwo");
// Delete the file
fs.unlinkSync(fileToTest);
// Send two more messages to the file
n1.emit("input", {payload:"three"});
n1.emit("input", {payload:"four"});
setTimeout(function() {
// Check the file was updated
try {
var f = fs.readFileSync(fileToTest).toString();
f.should.equal("threefour");
fs.unlinkSync(fileToTest);
done();
} catch(err) {
done(err);
}
},wait);
} catch(err) {
done(err);
}
},wait);
});
});
it('should append to a file after it has been recreated ', function(done) {
var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":false, "overwriteFile":false}];
try {
fs.unlinkSync(fileToTest);
} catch(err) {}
helper.load(fileNode, flow, function() {
var n1 = helper.getNode("fileNode1");
// Send two messages to the file
n1.emit("input", {payload:"one"});
n1.emit("input", {payload:"two"});
setTimeout(function() {
try {
// Check they got appended as expected
var f = fs.readFileSync(fileToTest).toString();
f.should.equal("onetwo");
// Delete the file
fs.unlinkSync(fileToTest);
// Recreate it
fs.writeFileSync(fileToTest,"");
// Send two more messages to the file
n1.emit("input", {payload:"three"});
n1.emit("input", {payload:"four"});
setTimeout(function() {
// Check the file was updated
try {
var f = fs.readFileSync(fileToTest).toString();
f.should.equal("threefour");
fs.unlinkSync(fileToTest);
done();
} catch(err) {
done(err);
}
},wait);
} catch(err) {
done(err);
}
},wait);
});
});
it('should use msg.filename if filename not set in node', function(done) {
var flow = [{id:"fileNode1", type:"file", name: "fileNode", "appendNewline":true, "overwriteFile":true}];
helper.load(fileNode, flow, function() {
@@ -407,7 +496,8 @@ describe('file Nodes', function() {
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('payload');
msg.payload.should.have.length(40).and.be.a.Buffer;
Buffer.isBuffer(msg.payload).should.be.true();
msg.payload.should.have.length(40);
msg.payload.toString().should.equal('File message line 1\nFile message line 2\n');
done();
});
@@ -422,10 +512,15 @@ describe('file Nodes', function() {
var n1 = helper.getNode("fileInNode1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('payload');
msg.payload.should.have.length(40).and.be.a.string;
msg.payload.should.equal("File message line 1\nFile message line 2\n");
done();
try {
msg.should.have.property('payload');
msg.payload.should.be.a.String();
msg.payload.should.have.length(40)
msg.payload.should.equal("File message line 1\nFile message line 2\n");
done();
} catch(err) {
done(err);
}
});
n1.receive({payload:""});
});
@@ -440,7 +535,8 @@ describe('file Nodes', function() {
var c = 0;
n2.on("input", function(msg) {
msg.should.have.property('payload');
msg.payload.should.have.length(19).and.be.a.string;
msg.payload.should.be.a.String();
msg.payload.should.have.length(19);
if (c === 0) {
msg.payload.should.equal("File message line 1");
c++;
@@ -466,7 +562,8 @@ describe('file Nodes', function() {
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
msg.should.have.property('payload');
msg.payload.should.have.length(40).and.be.a.Buffer;
Buffer.isBuffer(msg.payload).should.be.true();
msg.payload.should.have.length(40);
msg.should.have.property('parts');
msg.parts.should.have.property('count',1);
msg.parts.should.have.property('type','buffer');
@@ -495,21 +592,31 @@ describe('file Nodes', function() {
});
it('should handle a file read error', function(done) {
var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", "filename":"badfile", "format":""}];
var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", "filename":"badfile", "format":"", wires:[["n2"]]},
{id:"n2", type:"helper"}
];
helper.load(fileNode, flow, function() {
var n1 = helper.getNode("fileInNode1");
setTimeout(function() {
var logEvents = helper.log().args.filter(function(evt) {
return evt[0].type == "file in";
});
logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg');
logEvents[0][0].msg.toString().should.startWith("Error");
done();
},wait);
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
try {
msg.should.not.have.property('payload');
msg.should.have.property("error");
msg.error.should.have.property("code","ENOENT");
var logEvents = helper.log().args.filter(function(evt) {
return evt[0].type == "file in";
});
logEvents.should.have.length(1);
logEvents[0][0].should.have.a.property('msg');
logEvents[0][0].msg.toString().should.startWith("Error");
done();
} catch(err) {
done(err);
}
});
n1.receive({payload:""});
});
});
});
});

View File

@@ -22,7 +22,6 @@ var sinon = require('sinon');
var when = require('when');
var nodes = require("../../../red/api/nodes");
var comms = require("../../../red/api/comms");
var locales = require("../../../red/api/locales");
describe("nodes api", function() {
@@ -35,6 +34,9 @@ describe("nodes api", function() {
info: function(){},
warn: function(){}
}
runtime.events = {
emit: function(){}
}
nodes.init(runtime);
}
@@ -49,15 +51,11 @@ describe("nodes api", function() {
app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.putModule);
app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.putSet);
app.delete("/nodes/:id",nodes.delete);
sinon.stub(comms,"publish");
sinon.stub(locales,"determineLangFromHeaders", function() {
return "en-US";
});
});
after(function() {
comms.publish.restore();
});
describe('get nodes', function() {
it('returns node list', function(done) {

View File

@@ -141,7 +141,7 @@ describe("runtime", function() {
{ module:"node-red",enabled:true,loaded:false,types:["typeC","typeD"]} // missing
].filter(cb);
});
var serverInstallModule = sinon.stub(redNodes,"installModule",function(name) { return when.resolve();});
var serverInstallModule = sinon.stub(redNodes,"installModule",function(name) { return when.resolve({nodes:[]});});
runtime.init({testSettings: true, autoInstallModules:true, httpAdminRoot:"/", load:function() { return when.resolve();}});
sinon.stub(console,"log");
runtime.start().then(function() {

View File

@@ -121,7 +121,7 @@ describe('LocalFileSystem', function() {
it('should set userDir to USERPROFILE/.node-red',function(done) {
var oldNRH = process.env.NODE_RED_HOME;
process.env.NODE_RED_HOME = "";
process.env.NODE_RED_HOME = path.join(userDir,"NRH");
var oldHOME = process.env.HOME;
process.env.HOME = "";
var oldHOMEPATH = process.env.HOMEPATH;
@@ -587,9 +587,13 @@ describe('LocalFileSystem', function() {
fs.mkdirSync(path.join(objLib,"A"));
fs.mkdirSync(path.join(objLib,"B"));
fs.mkdirSync(path.join(objLib,"B","C"));
fs.writeFileSync(path.join(objLib,"file1.js"),"// abc: def\n// not a metaline \n\n Hi",'utf8');
fs.writeFileSync(path.join(objLib,"B","file2.js"),"// ghi: jkl\n// not a metaline \n\n Hi",'utf8');
fs.writeFileSync(path.join(objLib,"B","flow.json"),"Hi",'utf8');
if (type === "functions" || type === "object") {
fs.writeFileSync(path.join(objLib,"file1.js"),"// abc: def\n// not a metaline \n\n Hi",'utf8');
fs.writeFileSync(path.join(objLib,"B","file2.js"),"// ghi: jkl\n// not a metaline \n\n Hi",'utf8');
}
if (type === "flows" || type === "object") {
fs.writeFileSync(path.join(objLib,"B","flow.json"),"Hi",'utf8');
}
}
it('should return a directory listing of library objects',function(done) {
@@ -644,17 +648,17 @@ describe('LocalFileSystem', function() {
});
});
it('should return a newly saved library object',function(done) {
it('should return a newly saved library function',function(done) {
localfilesystem.init({userDir:userDir}).then(function() {
createObjectLibrary();
localfilesystem.getLibraryEntry('object','B').then(function(flows) {
flows.should.eql([ 'C', { ghi: 'jkl', fn: 'file2.js' }, {fn:'flow.json'} ]);
createObjectLibrary("functions");
localfilesystem.getLibraryEntry('functions','B').then(function(flows) {
flows.should.eql([ 'C', { ghi: 'jkl', fn: 'file2.js' } ]);
var ft = path.join("B","D","file3.js");
localfilesystem.saveLibraryEntry('object',ft,{mno:'pqr'},"// another non meta line\n\n Hi There").then(function() {
localfilesystem.saveLibraryEntry('functions',ft,{mno:'pqr'},"// another non meta line\n\n Hi There").then(function() {
setTimeout(function() {
localfilesystem.getLibraryEntry('object',path.join("B","D")).then(function(flows) {
localfilesystem.getLibraryEntry('functions',path.join("B","D")).then(function(flows) {
flows.should.eql([ { mno: 'pqr', fn: 'file3.js' } ]);
localfilesystem.getLibraryEntry('object',ft).then(function(body) {
localfilesystem.getLibraryEntry('functions',ft).then(function(body) {
body.should.eql("// another non meta line\n\n Hi There");
done();
}).otherwise(function(err) {
@@ -675,4 +679,35 @@ describe('LocalFileSystem', function() {
});
});
it('should return a newly saved library flow',function(done) {
localfilesystem.init({userDir:userDir}).then(function() {
createObjectLibrary("flows");
localfilesystem.getLibraryEntry('flows','B').then(function(flows) {
flows.should.eql([ 'C', {fn:'flow.json'} ]);
var ft = path.join("B","D","file3");
localfilesystem.saveLibraryEntry('flows',ft,{mno:'pqr'},"Hi").then(function() {
setTimeout(function() {
localfilesystem.getLibraryEntry('flows',path.join("B","D")).then(function(flows) {
flows.should.eql([ { mno: 'pqr', fn: 'file3.json' } ]);
localfilesystem.getLibraryEntry('flows',ft+".json").then(function(body) {
body.should.eql("Hi");
done();
}).otherwise(function(err) {
done(err);
});
}).otherwise(function(err) {
done(err);
})}
, 50);
}).otherwise(function(err) {
done(err);
});
}).otherwise(function(err) {
done(err);
});
}).otherwise(function(err) {
done(err);
});
});
});

View File

@@ -381,4 +381,48 @@ describe("red/util", function() {
it('pass http request',function() { normalise("http request", "httpRequest") });
it('pass HttpRequest',function() { normalise("HttpRequest", "httpRequest") });
});
describe('prepareJSONataExpression', function() {
it('prepares an expression', function() {
var result = util.prepareJSONataExpression('payload',{});
result.should.have.property('evaluate');
result.should.have.property('assign');
result.should.have.property('_legacyMode', false);
});
it('prepares a legacyMode expression', function() {
var result = util.prepareJSONataExpression('msg.payload',{});
result.should.have.property('evaluate');
result.should.have.property('assign');
result.should.have.property('_legacyMode', true);
});
});
describe('evaluateJSONataExpression', function() {
it('evaluates an expression', function() {
var expr = util.prepareJSONataExpression('payload',{});
var result = util.evaluateJSONataExpression(expr,{payload:"hello"});
result.should.eql("hello");
});
it('evaluates a legacyMode expression', function() {
var expr = util.prepareJSONataExpression('msg.payload',{});
var result = util.evaluateJSONataExpression(expr,{payload:"hello"});
result.should.eql("hello");
});
it('accesses flow context from an expression', function() {
var expr = util.prepareJSONataExpression('$flowContext("foo")',{context:function() { return {flow:{get: function(key) { return {'foo':'bar'}[key]}}}}});
var result = util.evaluateJSONataExpression(expr,{payload:"hello"});
result.should.eql("bar");
});
it('handles non-existant flow context variable', function() {
var expr = util.prepareJSONataExpression('$flowContext("nonExistant")',{context:function() { return {flow:{get: function(key) { return {'foo':'bar'}[key]}}}}});
var result = util.evaluateJSONataExpression(expr,{payload:"hello"});
should.not.exist(result);
});
it('handles non-existant global context variable', function() {
var expr = util.prepareJSONataExpression('$globalContext("nonExistant")',{context:function() { return {global:{get: function(key) { return {'foo':'bar'}[key]}}}}});
var result = util.evaluateJSONataExpression(expr,{payload:"hello"});
should.not.exist(result);
});
});
});