mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Compare commits
81 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
03558b012c | ||
|
3288efdad6 | ||
|
3902a343f3 | ||
|
882b7d0391 | ||
|
81f082825d | ||
|
392fd6fed3 | ||
|
51afed4041 | ||
|
17e3b71d9c | ||
|
6e75089f3a | ||
|
6dc640b129 | ||
|
27cbaac343 | ||
|
fa4006619e | ||
|
cb8fe8462a | ||
|
abd51a5511 | ||
|
a0cc1e6b0c | ||
|
50399c6bfa | ||
|
de48c1be44 | ||
|
0786ec4b66 | ||
|
db319e0ebc | ||
|
4fc568856a | ||
|
4c6771669b | ||
|
9bca2a91c9 | ||
|
66eaaf5a48 | ||
|
9837f0e2e1 | ||
|
6b8ffb4c68 | ||
|
f35dd34da9 | ||
|
ed19e4fa08 | ||
|
661e1a4f90 | ||
|
5826de76ca | ||
|
05888740e5 | ||
|
41f3b0c333 | ||
|
70f3e72a20 | ||
|
e873afd40b | ||
|
2777c2937a | ||
|
798903e4cc | ||
|
58622ba18f | ||
|
c368dcd5b7 | ||
|
0b4c652ce7 | ||
|
dbaacc411a | ||
|
1850185d1e | ||
|
2e9d445d36 | ||
|
aed89d82fb | ||
|
231adac6d8 | ||
|
587c4e5915 | ||
|
55f1cbf18f | ||
|
38168a545b | ||
|
43c6df49d7 | ||
|
f1c59faf72 | ||
|
5f7019325c | ||
|
fe4dae8518 | ||
|
1f848b205b | ||
|
742c470d81 | ||
|
5ead3342cc | ||
|
b95dc2ecce | ||
|
4d0950215f | ||
|
da0ce9fe0d | ||
|
ca62e720b5 | ||
|
c4b1795396 | ||
|
fd2e47ed73 | ||
|
d5f2255a68 | ||
|
05b58e9263 | ||
|
4a91c27e4b | ||
|
3a03d46d8d | ||
|
f03aff7006 | ||
|
043b8a3105 | ||
|
1dd9984521 | ||
|
d2be7f8c8f | ||
|
88dc202db2 | ||
|
083d54b008 | ||
|
87d77efa57 | ||
|
35c4a41d7b | ||
|
1ca3ca07d5 | ||
|
d673846e3d | ||
|
f62b7afede | ||
|
e65770a53a | ||
|
a92a741932 | ||
|
45f67191ba | ||
|
93f5da325b | ||
|
8fb955e182 | ||
|
9f5e6a4b37 | ||
|
f43738446e |
18
Gruntfile.js
18
Gruntfile.js
@@ -124,14 +124,15 @@ module.exports = function(grunt) {
|
||||
"editor/js/ui/library.js",
|
||||
"editor/js/ui/notifications.js",
|
||||
"editor/js/ui/subflow.js",
|
||||
"editor/js/ui/touch/radialMenu.js"
|
||||
"editor/js/ui/touch/radialMenu.js",
|
||||
"editor/js/ui/typedInput.js"
|
||||
],
|
||||
dest: "public/red/red.js"
|
||||
},
|
||||
vendor: {
|
||||
files: {
|
||||
"public/vendor/vendor.js": [
|
||||
"editor/vendor/jquery/js/jquery-1.11.1.min.js",
|
||||
"editor/vendor/jquery/js/jquery-1.11.3.min.js",
|
||||
"editor/vendor/bootstrap/js/bootstrap.min.js",
|
||||
"editor/vendor/jquery/js/jquery-ui-1.10.3.custom.min.js",
|
||||
"editor/vendor/jquery/js/jquery.ui.touch-punch.min.js",
|
||||
@@ -174,8 +175,8 @@ module.exports = function(grunt) {
|
||||
messages: {
|
||||
src: [
|
||||
'nodes/core/locales/en-US/messages.json',
|
||||
'locales/en-US/editor.json',
|
||||
'locales/en-US/runtime.json'
|
||||
'red/api/locales/en-US/editor.json',
|
||||
'red/runtime/locales/en-US/runtime.json'
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -223,8 +224,8 @@ module.exports = function(grunt) {
|
||||
json: {
|
||||
files: [
|
||||
'nodes/core/locales/en-US/messages.json',
|
||||
'locales/en-US/editor.json',
|
||||
'locales/en-US/runtime.json'
|
||||
'red/api/locales/en-US/editor.json',
|
||||
'red/runtime/locales/en-US/runtime.json'
|
||||
],
|
||||
tasks: ['jsonlint:messages']
|
||||
}
|
||||
@@ -238,7 +239,7 @@ module.exports = function(grunt) {
|
||||
args: nodemonArgs,
|
||||
ext: 'js,html,json',
|
||||
watch: [
|
||||
'red','nodes','locales'
|
||||
'red','nodes'
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -302,8 +303,7 @@ module.exports = function(grunt) {
|
||||
'red/**',
|
||||
'public/**',
|
||||
'editor/templates/**',
|
||||
'bin/**',
|
||||
'locales/**'
|
||||
'bin/**'
|
||||
],
|
||||
dest: path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>')
|
||||
}]
|
||||
|
BIN
editor/images/typedInput/09.png
Normal file
BIN
editor/images/typedInput/09.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 638 B |
BIN
editor/images/typedInput/az.png
Normal file
BIN
editor/images/typedInput/az.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 546 B |
BIN
editor/images/typedInput/bool.png
Normal file
BIN
editor/images/typedInput/bool.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 646 B |
BIN
editor/images/typedInput/json.png
Normal file
BIN
editor/images/typedInput/json.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 588 B |
BIN
editor/images/typedInput/re.png
Normal file
BIN
editor/images/typedInput/re.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 502 B |
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013, 2015 IBM Corp.
|
||||
* Copyright 2013, 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -80,6 +80,12 @@ RED.history = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ev.removedLinks) {
|
||||
for (i=0;i<ev.removedLinks.length;i++) {
|
||||
RED.nodes.addLink(ev.removedLinks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (ev.t == "delete") {
|
||||
if (ev.workspaces) {
|
||||
for (i=0;i<ev.workspaces.length;i++) {
|
||||
@@ -168,6 +174,17 @@ RED.history = (function() {
|
||||
n.n.y = n.oy;
|
||||
n.n.dirty = true;
|
||||
}
|
||||
// A move could have caused a link splice
|
||||
if (ev.links) {
|
||||
for (i=0;i<ev.links.length;i++) {
|
||||
RED.nodes.removeLink(ev.links[i]);
|
||||
}
|
||||
}
|
||||
if (ev.removedLinks) {
|
||||
for (i=0;i<ev.removedLinks.length;i++) {
|
||||
RED.nodes.addLink(ev.removedLinks[i]);
|
||||
}
|
||||
}
|
||||
} else if (ev.t == "edit") {
|
||||
for (i in ev.changes) {
|
||||
if (ev.changes.hasOwnProperty(i)) {
|
||||
@@ -265,6 +282,7 @@ RED.history = (function() {
|
||||
RED.view.redraw(true);
|
||||
RED.palette.refresh();
|
||||
RED.workspaces.refresh();
|
||||
RED.sidebar.config.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -156,11 +156,13 @@ var RED = (function() {
|
||||
function loadEditor() {
|
||||
RED.menu.init({id:"btn-sidemenu",
|
||||
options: [
|
||||
{id:"menu-item-sidebar-menu",label:RED._("menu.label.sidebar.sidebar"),options:[
|
||||
{id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:RED.sidebar.toggleSidebar, selected: true},
|
||||
null
|
||||
{id:"menu-item-view-menu",label:RED._("menu.label.view.view"),options:[
|
||||
{id:"menu-item-view-show-grid",label:RED._("menu.label.view.showGrid"),toggle:true,onselect:RED.view.toggleShowGrid},
|
||||
{id:"menu-item-view-snap-grid",label:RED._("menu.label.view.snapGrid"),toggle:true,onselect:RED.view.toggleSnapGrid},
|
||||
{id:"menu-item-status",label:RED._("menu.label.displayStatus"),toggle:true,onselect:toggleStatus, selected: true},
|
||||
null,
|
||||
{id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:RED.sidebar.toggleSidebar, selected: true}
|
||||
]},
|
||||
{id:"menu-item-status",label:RED._("menu.label.displayStatus"),toggle:true,onselect:toggleStatus, selected: true},
|
||||
null,
|
||||
{id:"menu-item-import",label:RED._("menu.label.import"),options:[
|
||||
{id:"menu-item-import-clipboard",label:RED._("menu.label.clipboard"),onselect:RED.clipboard.import},
|
||||
@@ -171,23 +173,24 @@ var RED = (function() {
|
||||
{id:"menu-item-export-library",label:RED._("menu.label.library"),disabled:true,onselect:RED.library.export}
|
||||
]},
|
||||
null,
|
||||
{id:"menu-item-subflow",label:RED._("menu.label.subflows"), options: [
|
||||
{id:"menu-item-subflow-create",label:RED._("menu.label.createSubflow"),onselect:RED.subflow.createSubflow},
|
||||
{id:"menu-item-subflow-convert",label:RED._("menu.label.selectionToSubflow"),disabled:true,onselect:RED.subflow.convertToSubflow},
|
||||
]},
|
||||
null,
|
||||
{id:"menu-item-config-nodes",label:RED._("menu.label.displayConfig"),onselect:function(){}},
|
||||
{id:"menu-item-workspace",label:RED._("menu.label.flows"),options:[
|
||||
{id:"menu-item-workspace-add",label:RED._("menu.label.add"),onselect:RED.workspaces.add},
|
||||
{id:"menu-item-workspace-edit",label:RED._("menu.label.rename"),onselect:RED.workspaces.edit},
|
||||
{id:"menu-item-workspace-delete",label:RED._("menu.label.delete"),onselect:RED.workspaces.remove},
|
||||
null
|
||||
]},
|
||||
{id:"menu-item-subflow",label:RED._("menu.label.subflows"), options: [
|
||||
{id:"menu-item-subflow-create",label:RED._("menu.label.createSubflow"),onselect:RED.subflow.createSubflow},
|
||||
{id:"menu-item-subflow-convert",label:RED._("menu.label.selectionToSubflow"),disabled:true,onselect:RED.subflow.convertToSubflow},
|
||||
]},
|
||||
null,
|
||||
{id:"menu-item-keyboard-shortcuts",label:RED._("menu.label.keyboardShortcuts"),onselect:RED.keyboard.showHelp},
|
||||
{id:"menu-item-help",
|
||||
label: RED.settings.theme("menu.menu-item-help.label","Node-RED Website"),
|
||||
href: RED.settings.theme("menu.menu-item-help.url","http://nodered.org/docs")
|
||||
}
|
||||
},
|
||||
{id:"menu-item-node-red-version", label:"v"+RED.settings.version}
|
||||
]
|
||||
});
|
||||
|
||||
|
@@ -16,9 +16,9 @@
|
||||
|
||||
|
||||
RED.settings = (function () {
|
||||
|
||||
|
||||
var loadedSettings = {};
|
||||
|
||||
|
||||
var hasLocalStorage = function () {
|
||||
try {
|
||||
return 'localStorage' in window && window['localStorage'] !== null;
|
||||
@@ -74,7 +74,7 @@ RED.settings = (function () {
|
||||
RED.settings.set("auth-tokens",{access_token: accessToken});
|
||||
window.location.search = "";
|
||||
}
|
||||
|
||||
|
||||
$.ajaxSetup({
|
||||
beforeSend: function(jqXHR,settings) {
|
||||
// Only attach auth header for requests to relative paths
|
||||
@@ -89,7 +89,7 @@ RED.settings = (function () {
|
||||
|
||||
load(done);
|
||||
}
|
||||
|
||||
|
||||
var load = function(done) {
|
||||
$.ajax({
|
||||
headers: {
|
||||
@@ -141,7 +141,7 @@ RED.settings = (function () {
|
||||
set: set,
|
||||
get: get,
|
||||
remove: remove,
|
||||
|
||||
|
||||
theme: theme
|
||||
}
|
||||
})
|
||||
|
@@ -22,7 +22,7 @@ RED.clipboard = (function() {
|
||||
var exportNodesDialog;
|
||||
var importNodesDialog;
|
||||
|
||||
function setupDialogs(){
|
||||
function setupDialogs() {
|
||||
dialog = $('<div id="clipboard-dialog" class="hide"><form class="dialog-form form-horizontal"></form></div>')
|
||||
.appendTo("body")
|
||||
.dialog({
|
||||
@@ -61,7 +61,7 @@ RED.clipboard = (function() {
|
||||
close: function(e) {
|
||||
RED.keyboard.enable();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
dialogContainer = dialog.children(".dialog-form");
|
||||
|
||||
@@ -83,9 +83,11 @@ RED.clipboard = (function() {
|
||||
function validateImport() {
|
||||
var importInput = $("#clipboard-import");
|
||||
var v = importInput.val();
|
||||
v = v.substring(v.indexOf('['),v.lastIndexOf(']')+1);
|
||||
try {
|
||||
JSON.parse(v);
|
||||
importInput.removeClass("input-error");
|
||||
importInput.val(v);
|
||||
$("#clipboard-dialog-ok").button("enable");
|
||||
} catch(err) {
|
||||
if (v !== "") {
|
||||
@@ -153,8 +155,6 @@ RED.clipboard = (function() {
|
||||
RED.keyboard.add(/* e */ 69,{ctrl:true},function(){exportNodes();d3.event.preventDefault();});
|
||||
RED.keyboard.add(/* i */ 73,{ctrl:true},function(){importNodes();d3.event.preventDefault();});
|
||||
|
||||
|
||||
|
||||
$('#chart').on("dragenter",function(event) {
|
||||
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
|
||||
$("#dropTarget").css({display:'table'});
|
||||
@@ -173,18 +173,13 @@ RED.clipboard = (function() {
|
||||
.on("drop",function(event) {
|
||||
var data = event.originalEvent.dataTransfer.getData("text/plain");
|
||||
hideDropTarget();
|
||||
data = data.substring(data.indexOf('['),data.lastIndexOf(']')+1);
|
||||
RED.view.importNodes(data);
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
|
||||
},
|
||||
import: importNodes,
|
||||
export: exportNodes
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
})();
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2015 IBM Corp.
|
||||
* Copyright 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -208,13 +208,13 @@ RED.deploy = (function() {
|
||||
.html("<li>"+invalidNodes.map(function(A) { return (A.tab?"["+A.tab+"] ":"")+A.label+" ("+A.type+")"}).join("</li><li>")+"</li>");
|
||||
|
||||
} else if (hasUnusedConfig && !ignoreDeployWarnings.unusedConfig) {
|
||||
showWarning = true;
|
||||
$( "#node-dialog-confirm-deploy-type" ).val("unusedConfig");
|
||||
$( "#node-dialog-confirm-deploy-unused" ).show();
|
||||
|
||||
unusedConfigNodes.sort(sortNodeInfo);
|
||||
$( "#node-dialog-confirm-deploy-unused-list" )
|
||||
.html("<li>"+unusedConfigNodes.map(function(A) { return (A.tab?"["+A.tab+"] ":"")+A.label+" ("+A.type+")"}).join("</li><li>")+"</li>");
|
||||
// showWarning = true;
|
||||
// $( "#node-dialog-confirm-deploy-type" ).val("unusedConfig");
|
||||
// $( "#node-dialog-confirm-deploy-unused" ).show();
|
||||
//
|
||||
// unusedConfigNodes.sort(sortNodeInfo);
|
||||
// $( "#node-dialog-confirm-deploy-unused-list" )
|
||||
// .html("<li>"+unusedConfigNodes.map(function(A) { return (A.tab?"["+A.tab+"] ":"")+A.label+" ("+A.type+")"}).join("</li><li>")+"</li>");
|
||||
}
|
||||
if (showWarning) {
|
||||
$( "#node-dialog-confirm-deploy-hide" ).prop("checked",false);
|
||||
@@ -241,7 +241,13 @@ RED.deploy = (function() {
|
||||
"Node-RED-Deployment-Type":deploymentType
|
||||
}
|
||||
}).done(function(data,textStatus,xhr) {
|
||||
RED.notify(RED._("deploy.successfulDeploy"),"success");
|
||||
if (hasUnusedConfig) {
|
||||
RED.notify(
|
||||
'<p>'+RED._("deploy.successfulDeploy")+'</p>'+
|
||||
'<p>'+RED._("deploy.unusedConfigNodes")+' <a href="#" onclick="RED.sidebar.config.show(true); return false;">'+RED._("deploy.unusedConfigNodesLink")+'</a></p>',"success",false,6000);
|
||||
} else {
|
||||
RED.notify(RED._("deploy.successfulDeploy"),"success");
|
||||
}
|
||||
RED.nodes.eachNode(function(node) {
|
||||
if (node.changed) {
|
||||
node.dirty = true;
|
||||
|
@@ -348,6 +348,10 @@ RED.editor = (function() {
|
||||
resize: function(e,ui) {
|
||||
if (editing_node) {
|
||||
$(this).dialog('option',"sizeCache-"+editing_node.type,ui.size);
|
||||
if (editing_node._def.oneditresize) {
|
||||
var form = $("#dialog-form");
|
||||
editing_node._def.oneditresize.call(editing_node,{width:form.width(),height:form.height()});
|
||||
}
|
||||
}
|
||||
},
|
||||
open: function(e) {
|
||||
@@ -364,6 +368,12 @@ RED.editor = (function() {
|
||||
$(this).dialog('option','width',size.width);
|
||||
$(this).dialog('option','height',size.height);
|
||||
}
|
||||
if (editing_node._def.oneditresize) {
|
||||
setTimeout(function() {
|
||||
var form = $("#dialog-form");
|
||||
editing_node._def.oneditresize.call(editing_node,{width:form.width(),height:form.height()});
|
||||
},0);
|
||||
}
|
||||
}
|
||||
},
|
||||
close: function(e) {
|
||||
@@ -385,6 +395,12 @@ RED.editor = (function() {
|
||||
}
|
||||
editing_node = null;
|
||||
}
|
||||
}).parent().on('keydown', function(evt) {
|
||||
if (evt.keyCode === $.ui.keyCode.ESCAPE && (evt.metaKey || evt.ctrlKey)) {
|
||||
$("#node-dialog-cancel").click();
|
||||
} else if (evt.keyCode === $.ui.keyCode.ENTER && (evt.metaKey || evt.ctrlKey)) {
|
||||
$("#node-dialog-ok").click();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1008,6 +1024,12 @@ RED.editor = (function() {
|
||||
cancel: '.ui-dialog-content, .ui-dialog-titlebar-close, #node-config-dialog-scope-container'
|
||||
});
|
||||
}
|
||||
}).parent().on('keydown', function(evt) {
|
||||
if (evt.keyCode === $.ui.keyCode.ESCAPE && (evt.metaKey || evt.ctrlKey)) {
|
||||
$("#node-config-dialog-cancel").click();
|
||||
} else if (evt.keyCode === $.ui.keyCode.ENTER && (evt.metaKey || evt.ctrlKey)) {
|
||||
$("#node-config-dialog-ok").click();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1129,6 +1151,12 @@ RED.editor = (function() {
|
||||
$(".node-text-editor").css("height",height+"px");
|
||||
subflowEditor.resize();
|
||||
}
|
||||
}).parent().on('keydown', function(evt) {
|
||||
if (evt.keyCode === $.ui.keyCode.ESCAPE && (evt.metaKey || evt.ctrlKey)) {
|
||||
$("#subflow-dialog-cancel").click();
|
||||
} else if (evt.keyCode === $.ui.keyCode.ENTER && (evt.metaKey || evt.ctrlKey)) {
|
||||
$("#subflow-dialog-ok").click();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
* Copyright 2013, 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -49,6 +49,13 @@ RED.notify = (function() {
|
||||
};
|
||||
})();
|
||||
if (!fixed) {
|
||||
$(n).click((function() {
|
||||
var nn = n;
|
||||
return function() {
|
||||
nn.close();
|
||||
window.clearTimeout(nn.timeoutid);
|
||||
};
|
||||
})());
|
||||
n.timeoutid = window.setTimeout(n.close,timeout||3000);
|
||||
}
|
||||
currentNotifications.push(n);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013, 2015 IBM Corp.
|
||||
* Copyright 2013, 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -210,12 +210,83 @@ RED.palette = (function() {
|
||||
var help = '<div class="node-help">'+helpText+"</div>";
|
||||
RED.sidebar.info.set(help);
|
||||
});
|
||||
var chart = $("#chart");
|
||||
var chartOffset = chart.offset();
|
||||
var chartSVG = $("#chart>svg").get(0);
|
||||
var activeSpliceLink;
|
||||
var mouseX;
|
||||
var mouseY;
|
||||
var spliceTimer;
|
||||
$(d).draggable({
|
||||
helper: 'clone',
|
||||
appendTo: 'body',
|
||||
revert: true,
|
||||
revertDuration: 50,
|
||||
start: function() {RED.view.focus();}
|
||||
start: function() {RED.view.focus();},
|
||||
stop: function() { d3.select('.link_splice').classed('link_splice',false); if (spliceTimer) { clearTimeout(spliceTimer); spliceTimer = null;}},
|
||||
drag: function(e,ui) {
|
||||
// TODO: this is the margin-left of palette node. Hard coding
|
||||
// it here makes me sad
|
||||
ui.position.left += 17.5;
|
||||
if (def.inputs > 0 && def.outputs > 0) {
|
||||
mouseX = e.clientX - chartOffset.left+chart.scrollLeft();
|
||||
mouseY = e.clientY-chartOffset.top +chart.scrollTop();
|
||||
|
||||
if (!spliceTimer) {
|
||||
spliceTimer = setTimeout(function() {
|
||||
var nodes = [];
|
||||
var bestDistance = Infinity;
|
||||
var bestLink = null;
|
||||
if (chartSVG.getIntersectionList) {
|
||||
var svgRect = chartSVG.createSVGRect();
|
||||
svgRect.x = mouseX;
|
||||
svgRect.y = mouseY;
|
||||
svgRect.width = 1;
|
||||
svgRect.height = 1;
|
||||
nodes = chartSVG.getIntersectionList(svgRect,chartSVG);
|
||||
mouseX /= RED.view.scale();
|
||||
mouseY /= RED.view.scale();
|
||||
} else {
|
||||
// Firefox doesn't do getIntersectionList and that
|
||||
// makes us sad
|
||||
mouseX /= RED.view.scale();
|
||||
mouseY /= RED.view.scale();
|
||||
nodes = RED.view.getLinksAtPoint(mouseX,mouseY);
|
||||
}
|
||||
for (var i=0;i<nodes.length;i++) {
|
||||
if (d3.select(nodes[i]).classed('link_background')) {
|
||||
var length = nodes[i].getTotalLength();
|
||||
for (var j=0;j<length;j+=10) {
|
||||
var p = nodes[i].getPointAtLength(j);
|
||||
var d2 = ((p.x-mouseX)*(p.x-mouseX))+((p.y-mouseY)*(p.y-mouseY));
|
||||
if (d2 < 200 && d2 < bestDistance) {
|
||||
bestDistance = d2;
|
||||
bestLink = nodes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (activeSpliceLink && activeSpliceLink !== bestLink) {
|
||||
d3.select(activeSpliceLink.parentNode).classed('link_splice',false);
|
||||
}
|
||||
if (bestLink) {
|
||||
d3.select(bestLink.parentNode).classed('link_splice',true)
|
||||
} else {
|
||||
d3.select('.link_splice').classed('link_splice',false);
|
||||
}
|
||||
if (activeSpliceLink !== bestLink) {
|
||||
if (bestLink) {
|
||||
$(ui.helper).data('splice',d3.select(bestLink).data()[0]);
|
||||
} else {
|
||||
$(ui.helper).removeData('splice');
|
||||
}
|
||||
}
|
||||
activeSpliceLink = bestLink;
|
||||
spliceTimer = null;
|
||||
},200);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var nodeInfo = null;
|
||||
@@ -292,7 +363,7 @@ RED.palette = (function() {
|
||||
}
|
||||
|
||||
var re = new RegExp(val,'i');
|
||||
$(".palette_node").each(function(i,el) {
|
||||
$("#palette-container .palette_node").each(function(i,el) {
|
||||
var currentLabel = $(el).find(".palette_label").text();
|
||||
if (val === "" || re.test(el.id) || re.test(currentLabel)) {
|
||||
$(this).show();
|
||||
|
@@ -20,10 +20,14 @@ RED.sidebar = (function() {
|
||||
id:"sidebar-tabs",
|
||||
onchange:function(tab) {
|
||||
$("#sidebar-content").children().hide();
|
||||
$("#sidebar-footer").children().hide();
|
||||
if (tab.onchange) {
|
||||
tab.onchange.call(tab);
|
||||
}
|
||||
$(tab.content).show();
|
||||
if (tab.toolbar) {
|
||||
$(tab.toolbar).show();
|
||||
}
|
||||
},
|
||||
onremove: function(tab) {
|
||||
$(tab.content).hide();
|
||||
@@ -58,10 +62,15 @@ RED.sidebar = (function() {
|
||||
|
||||
$("#sidebar-content").append(options.content);
|
||||
$(options.content).hide();
|
||||
if (options.toolbar) {
|
||||
$("#sidebar-footer").append(options.toolbar);
|
||||
$(options.toolbar).hide();
|
||||
}
|
||||
$(options.content).hide();
|
||||
var id = options.id;
|
||||
|
||||
RED.menu.addItem("menu-item-sidebar-menu",{
|
||||
id:"menu-item-sidebar-menu-"+options.id,
|
||||
RED.menu.addItem("menu-item-view-menu",{
|
||||
id:"menu-item-view-menu-"+options.id,
|
||||
label:options.name,
|
||||
onselect:function() {
|
||||
showSidebar(options.id);
|
||||
@@ -80,7 +89,7 @@ RED.sidebar = (function() {
|
||||
sidebar_tabs.removeTab(id);
|
||||
$(knownTabs[id].content).remove();
|
||||
delete knownTabs[id];
|
||||
RED.menu.removeItem("menu-item-sidebar-menu-"+id);
|
||||
RED.menu.removeItem("menu-item-view-menu-"+id);
|
||||
}
|
||||
|
||||
var sidebarSeparator = {};
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013, 2015 IBM Corp.
|
||||
* Copyright 2013, 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,24 +19,115 @@ RED.sidebar.config = (function() {
|
||||
var content = document.createElement("div");
|
||||
content.className = "sidebar-node-config"
|
||||
|
||||
$('<div class="palette-category">'+
|
||||
'<div class="workspace-config-node-tray-header palette-header"><i class="fa fa-angle-down expanded"></i><span data-i18n="sidebar.config.local"></span></div>'+
|
||||
'<ul id="workspace-config-node-tray-locals" class="palette-content config-node-list"></ul>'+
|
||||
'</div>'+
|
||||
'<div class="palette-category">'+
|
||||
'<div class="workspace-config-node-tray-header palette-header"><i class="fa fa-angle-down expanded"></i><span data-i18n="sidebar.config.global"></span></div>'+
|
||||
'<ul id="workspace-config-node-tray-globals" class="palette-content config-node-list"></ul>'+
|
||||
'</div>').appendTo(content);
|
||||
$('<div class="button-group sidebar-header">'+
|
||||
'<a class="sidebar-header-button selected" id="workspace-config-node-filter-all" href="#"><span data-i18n="sidebar.config.filterAll"></span></a>'+
|
||||
'<a class="sidebar-header-button" id="workspace-config-node-filter-unused" href="#"><span data-i18n="sidebar.config.filterUnused"></span></a> '+
|
||||
'</div>'
|
||||
).appendTo(content);
|
||||
|
||||
|
||||
var toolbar = $('<div>'+
|
||||
'<a class="sidebar-footer-button" id="workspace-config-node-collapse-all" href="#"><i class="fa fa-angle-double-up"></i></a> '+
|
||||
'<a class="sidebar-footer-button" id="workspace-config-node-expand-all" href="#"><i class="fa fa-angle-double-down"></i></a>'+
|
||||
'</div>');
|
||||
|
||||
var globalCategories = $("<div>").appendTo(content);
|
||||
var flowCategories = $("<div>").appendTo(content);
|
||||
var subflowCategories = $("<div>").appendTo(content);
|
||||
|
||||
var showUnusedOnly = false;
|
||||
|
||||
var categories = {};
|
||||
|
||||
function getOrCreateCategory(name,parent,label) {
|
||||
name = name.replace(/\./i,"-");
|
||||
if (!categories[name]) {
|
||||
var container = $('<div class="palette-category workspace-config-node-category" id="workspace-config-node-category-'+name+'"></div>').appendTo(parent);
|
||||
var header = $('<div class="workspace-config-node-tray-header palette-header"><i class="fa fa-angle-down expanded"></i></div>').appendTo(container);
|
||||
if (label) {
|
||||
$('<span class="config-node-label"/>').text(label).appendTo(header);
|
||||
} else {
|
||||
$('<span class="config-node-label" data-i18n="sidebar.config.'+name+'">').appendTo(header);
|
||||
}
|
||||
$('<span class="config-node-filter-info"></span>').appendTo(header);
|
||||
category = $('<ul class="palette-content config-node-list"></ul>').appendTo(container);
|
||||
container.i18n();
|
||||
var icon = header.find("i");
|
||||
var result = {
|
||||
label: label,
|
||||
list: category,
|
||||
size: function() {
|
||||
return result.list.find("li:not(.config_node_none)").length
|
||||
},
|
||||
open: function(snap) {
|
||||
if (!icon.hasClass("expanded")) {
|
||||
icon.addClass("expanded");
|
||||
if (snap) {
|
||||
result.list.show();
|
||||
} else {
|
||||
result.list.slideDown();
|
||||
}
|
||||
}
|
||||
},
|
||||
close: function(snap) {
|
||||
if (icon.hasClass("expanded")) {
|
||||
icon.removeClass("expanded");
|
||||
if (snap) {
|
||||
result.list.hide();
|
||||
} else {
|
||||
result.list.slideUp();
|
||||
}
|
||||
}
|
||||
},
|
||||
isOpen: function() {
|
||||
return icon.hasClass("expanded");
|
||||
}
|
||||
};
|
||||
|
||||
header.on('click', function(e) {
|
||||
if (result.isOpen()) {
|
||||
result.close();
|
||||
} else {
|
||||
result.open();
|
||||
}
|
||||
});
|
||||
categories[name] = result;
|
||||
} else {
|
||||
if (categories[name].label !== label) {
|
||||
categories[name].list.parent().find('.config-node-label').text(label);
|
||||
categories[name].label = label;
|
||||
}
|
||||
}
|
||||
return categories[name];
|
||||
}
|
||||
|
||||
function createConfigNodeList(id,nodes) {
|
||||
var category = getOrCreateCategory(id.replace(/\./i,"-"))
|
||||
var list = category.list;
|
||||
|
||||
function createConfigNodeList(nodes,list) {
|
||||
nodes.sort(function(A,B) {
|
||||
if (A.type < B.type) { return -1;}
|
||||
if (A.type > B.type) { return 1;}
|
||||
return 0;
|
||||
});
|
||||
if (showUnusedOnly) {
|
||||
var hiddenCount = nodes.length;
|
||||
nodes = nodes.filter(function(n) {
|
||||
return n.users.length === 0;
|
||||
})
|
||||
hiddenCount = hiddenCount - nodes.length;
|
||||
if (hiddenCount > 0) {
|
||||
list.parent().find('.config-node-filter-info').text(RED._('sidebar.config.filtered',{count:hiddenCount})).show();
|
||||
} else {
|
||||
list.parent().find('.config-node-filter-info').hide();
|
||||
}
|
||||
} else {
|
||||
list.parent().find('.config-node-filter-info').hide();
|
||||
}
|
||||
list.empty();
|
||||
if (nodes.length === 0) {
|
||||
$('<li class="config_node_none" data-i18n="sidebar.config.none">NONE</li>').i18n().appendTo(list);
|
||||
category.close(true);
|
||||
} else {
|
||||
var currentType = "";
|
||||
nodes.forEach(function(node) {
|
||||
@@ -86,23 +177,46 @@ RED.sidebar.config = (function() {
|
||||
RED.view.redraw();
|
||||
});
|
||||
});
|
||||
category.open(true);
|
||||
}
|
||||
}
|
||||
|
||||
function refreshConfigNodeList() {
|
||||
var validList = {"global":true};
|
||||
|
||||
var localConfigNodes = [];
|
||||
getOrCreateCategory("global",globalCategories);
|
||||
|
||||
RED.nodes.eachWorkspace(function(ws) {
|
||||
validList[ws.id.replace(/\./g,"-")] = true;
|
||||
getOrCreateCategory(ws.id,flowCategories,ws.label);
|
||||
})
|
||||
RED.nodes.eachSubflow(function(sf) {
|
||||
validList[sf.id.replace(/\./g,"-")] = true;
|
||||
getOrCreateCategory(sf.id,subflowCategories,sf.name);
|
||||
})
|
||||
$(".workspace-config-node-category").each(function() {
|
||||
var id = $(this).attr('id').substring("workspace-config-node-category-".length);
|
||||
if (!validList[id]) {
|
||||
$(this).remove();
|
||||
delete categories[id];
|
||||
}
|
||||
})
|
||||
var globalConfigNodes = [];
|
||||
|
||||
var configList = {};
|
||||
RED.nodes.eachConfig(function(cn) {
|
||||
if (cn.z == RED.workspaces.active()) {
|
||||
localConfigNodes.push(cn);
|
||||
if (cn.z) {//} == RED.workspaces.active()) {
|
||||
configList[cn.z.replace(/\./g,"-")] = configList[cn.z.replace(/\./g,"-")]||[];
|
||||
configList[cn.z.replace(/\./g,"-")].push(cn);
|
||||
} else if (!cn.z) {
|
||||
globalConfigNodes.push(cn);
|
||||
}
|
||||
});
|
||||
createConfigNodeList(localConfigNodes,$("#workspace-config-node-tray-locals"));
|
||||
createConfigNodeList(globalConfigNodes,$("#workspace-config-node-tray-globals"));
|
||||
for (var id in validList) {
|
||||
if (validList.hasOwnProperty(id)) {
|
||||
createConfigNodeList(id,configList[id]||[]);
|
||||
}
|
||||
}
|
||||
createConfigNodeList('global',globalConfigNodes);
|
||||
}
|
||||
|
||||
function init() {
|
||||
@@ -111,25 +225,63 @@ RED.sidebar.config = (function() {
|
||||
label: RED._("sidebar.config.label"),
|
||||
name: RED._("sidebar.config.name"),
|
||||
content: content,
|
||||
toolbar: toolbar,
|
||||
closeable: true,
|
||||
visible: false,
|
||||
onchange: function() { refreshConfigNodeList(); }
|
||||
});
|
||||
|
||||
$(".workspace-config-node-tray-header").on('click', function(e) {
|
||||
var icon = $(this).find("i");
|
||||
if (icon.hasClass("expanded")) {
|
||||
icon.removeClass("expanded");
|
||||
$(this).next().slideUp();
|
||||
} else {
|
||||
icon.addClass("expanded");
|
||||
$(this).next().slideDown();
|
||||
}
|
||||
RED.menu.setAction('menu-item-config-nodes',function() {
|
||||
RED.sidebar.show('config');
|
||||
})
|
||||
|
||||
$("#workspace-config-node-collapse-all").on("click", function(e) {
|
||||
e.preventDefault();
|
||||
for (var cat in categories) {
|
||||
if (categories.hasOwnProperty(cat)) {
|
||||
categories[cat].close();
|
||||
}
|
||||
}
|
||||
});
|
||||
$("#workspace-config-node-expand-all").on("click", function(e) {
|
||||
e.preventDefault();
|
||||
for (var cat in categories) {
|
||||
if (categories.hasOwnProperty(cat)) {
|
||||
if (categories[cat].size() > 0) {
|
||||
categories[cat].open();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
$('#workspace-config-node-filter-all').on("click",function(e) {
|
||||
e.preventDefault();
|
||||
if (showUnusedOnly) {
|
||||
$(this).addClass('selected');
|
||||
$('#workspace-config-node-filter-unused').removeClass('selected');
|
||||
showUnusedOnly = !showUnusedOnly;
|
||||
refreshConfigNodeList();
|
||||
}
|
||||
});
|
||||
$('#workspace-config-node-filter-unused').on("click",function(e) {
|
||||
e.preventDefault();
|
||||
if (!showUnusedOnly) {
|
||||
$(this).addClass('selected');
|
||||
$('#workspace-config-node-filter-all').removeClass('selected');
|
||||
showUnusedOnly = !showUnusedOnly;
|
||||
refreshConfigNodeList();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
function show() {
|
||||
function show(unused) {
|
||||
if (unused !== undefined) {
|
||||
if (unused) {
|
||||
$('#workspace-config-node-filter-unused').click();
|
||||
} else {
|
||||
$('#workspace-config-node-filter-all').click();
|
||||
}
|
||||
}
|
||||
refreshConfigNodeList();
|
||||
RED.sidebar.show("config");
|
||||
}
|
||||
|
306
editor/js/ui/typedInput.js
Normal file
306
editor/js/ui/typedInput.js
Normal file
@@ -0,0 +1,306 @@
|
||||
/**
|
||||
* Copyright 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
(function($) {
|
||||
var allOptions = {
|
||||
msg: {value:"msg",label:"msg.",validate:/^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]+)*/i},
|
||||
flow: {value:"flow",label:"flow.",validate:/^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]+)*/i},
|
||||
global: {value:"global",label:"global.",validate:/^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]+)*/i},
|
||||
str: {value:"str",label:"string",icon:"red/images/typedInput/az.png"},
|
||||
num: {value:"num",label:"number",icon:"red/images/typedInput/09.png",validate:/^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/},
|
||||
bool: {value:"bool",label:"boolean",icon:"red/images/typedInput/bool.png",options:["true","false"]},
|
||||
json: {value:"json",label:"JSON",icon:"red/images/typedInput/json.png", validate: function(v) { try{JSON.parse(v);return true;}catch(e){return false;}}},
|
||||
re: {value:"re",label:"regular expression",icon:"red/images/typedInput/re.png"}
|
||||
};
|
||||
var nlsd = false;
|
||||
|
||||
$.widget( "nodered.typedInput", {
|
||||
_create: function() {
|
||||
if (!nlsd && RED && RED._) {
|
||||
for (var i in allOptions) {
|
||||
if (allOptions.hasOwnProperty(i)) {
|
||||
allOptions[i].label = RED._("typedInput.type."+i,{defaultValue:allOptions[i].label});
|
||||
}
|
||||
}
|
||||
}
|
||||
nlsd = true;
|
||||
var that = this;
|
||||
|
||||
this.disarmClick = false;
|
||||
this.element.addClass('red-ui-typedInput');
|
||||
this.uiWidth = this.element.width();
|
||||
this.uiSelect = this.element
|
||||
.wrap( "<div>" )
|
||||
.parent();
|
||||
|
||||
["Right","Left"].forEach(function(d) {
|
||||
var m = that.element.css("margin"+d);
|
||||
that.uiSelect.css("margin"+d,m);
|
||||
that.element.css("margin"+d,0);
|
||||
});
|
||||
this.uiSelect.addClass("red-ui-typedInput-container");
|
||||
|
||||
this.options.types = this.options.types||Object.keys(allOptions);
|
||||
|
||||
var hasSubOptions = false;
|
||||
this.typeMap = {};
|
||||
this.types = this.options.types.map(function(opt) {
|
||||
var result;
|
||||
if (typeof opt === 'string') {
|
||||
result = allOptions[opt];
|
||||
} else {
|
||||
result = opt;
|
||||
}
|
||||
that.typeMap[result.value] = result;
|
||||
if (result.options) {
|
||||
hasSubOptions = true;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
if (this.options.typeField) {
|
||||
this.typeField = $(this.options.typeField).hide();
|
||||
var t = this.typeField.val();
|
||||
if (t && this.typeMap[t]) {
|
||||
this.options.default = t;
|
||||
}
|
||||
} else {
|
||||
this.typeField = $("<input>",{type:'hidden'}).appendTo(this.uiSelect);
|
||||
}
|
||||
|
||||
this.selectTrigger = $('<a href="#"><i class="fa fa-sort-desc"></i></a>').prependTo(this.uiSelect);
|
||||
this.selectLabel = $('<span></span>').appendTo(this.selectTrigger);
|
||||
|
||||
this.element.on('focus', function() {
|
||||
that.uiSelect.addClass('red-ui-typedInput-focus');
|
||||
});
|
||||
this.element.on('blur', function() {
|
||||
that.uiSelect.removeClass('red-ui-typedInput-focus');
|
||||
});
|
||||
this.element.on('change', function() {
|
||||
that.validate();
|
||||
})
|
||||
|
||||
this.selectTrigger.click(function(event) {
|
||||
event.preventDefault();
|
||||
that._showMenu(that.menu,that.selectTrigger);
|
||||
});
|
||||
|
||||
|
||||
if (hasSubOptions) {
|
||||
// explicitly set optionSelectTrigger display to inline-block otherwise jQ sets it to 'inline'
|
||||
this.optionSelectTrigger = $('<a href="#" class="red-ui-typedInput-option-trigger" style="display:inline-block"><i class="fa fa-sort-desc"></i></a>').appendTo(this.uiSelect);
|
||||
this.optionSelectLabel = $('<span></span>').prependTo(this.optionSelectTrigger);
|
||||
this.optionSelectTrigger.click(function(event) {
|
||||
event.preventDefault();
|
||||
if (that.optionMenu) {
|
||||
that.optionMenu.css({
|
||||
minWidth:that.optionSelectLabel.width()
|
||||
});
|
||||
|
||||
that._showMenu(that.optionMenu,that.optionSelectLabel)
|
||||
}
|
||||
});
|
||||
}
|
||||
this.menu = this._createMenu(this.types, function(v) { that.type(v) });
|
||||
this.type(this.options.default||this.types[0].value);
|
||||
},
|
||||
_hideMenu: function(menu) {
|
||||
$(document).off("mousedown.close-property-select");
|
||||
menu.hide();
|
||||
this.element.focus();
|
||||
},
|
||||
_createMenu: function(opts,callback) {
|
||||
var that = this;
|
||||
var menu = $("<div>").addClass("red-ui-typedInput-options");
|
||||
opts.forEach(function(opt) {
|
||||
if (typeof opt === 'string') {
|
||||
opt = {value:opt,label:opt};
|
||||
}
|
||||
var op = $('<a href="#">').attr("value",opt.value).appendTo(menu);
|
||||
if (opt.label) {
|
||||
op.text(opt.label);
|
||||
}
|
||||
if (opt.icon) {
|
||||
$('<img>',{src:opt.icon,style:"margin-right: 4px; height: 18px;"}).prependTo(op);
|
||||
} else {
|
||||
op.css({paddingLeft: "18px"});
|
||||
}
|
||||
|
||||
op.click(function(event) {
|
||||
event.preventDefault();
|
||||
callback(opt.value);
|
||||
that._hideMenu(menu);
|
||||
});
|
||||
});
|
||||
menu.css({
|
||||
display: "none",
|
||||
});
|
||||
menu.appendTo(document.body);
|
||||
return menu;
|
||||
|
||||
},
|
||||
_showMenu: function(menu,relativeTo) {
|
||||
if (this.disarmClick) {
|
||||
this.disarmClick = false;
|
||||
return
|
||||
}
|
||||
var that = this;
|
||||
var pos = relativeTo.offset();
|
||||
var height = relativeTo.height();
|
||||
menu.css({
|
||||
top: (height+pos.top-3)+"px",
|
||||
left: (2+pos.left)+"px",
|
||||
});
|
||||
menu.slideDown(100);
|
||||
this._delay(function() {
|
||||
that.uiSelect.addClass('red-ui-typedInput-focus');
|
||||
$(document).on("mousedown.close-property-select", function(event) {
|
||||
if(!$(event.target).closest(menu).length) {
|
||||
that._hideMenu(menu);
|
||||
}
|
||||
if ($(event.target).closest(relativeTo).length) {
|
||||
that.disarmClick = true;
|
||||
event.preventDefault();
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
_getLabelWidth: function(label) {
|
||||
var labelWidth = label.width();
|
||||
if (labelWidth === 0) {
|
||||
var newTrigger = label.clone();
|
||||
newTrigger.css({
|
||||
position:"absolute",
|
||||
top:0,
|
||||
left:-1000
|
||||
}).appendTo(document.body);
|
||||
labelWidth = newTrigger.width()+4;
|
||||
newTrigger.remove();
|
||||
}
|
||||
return labelWidth;
|
||||
},
|
||||
_resize: function() {
|
||||
|
||||
if (this.typeMap[this.propertyType] && this.typeMap[this.propertyType].hasValue === false) {
|
||||
this.selectTrigger.width(this.uiWidth+5);
|
||||
} else {
|
||||
this.selectTrigger.width('auto');
|
||||
var labelWidth = this._getLabelWidth(this.selectTrigger);
|
||||
|
||||
var newWidth = this.uiWidth-labelWidth+4;
|
||||
this.element.width(newWidth);
|
||||
|
||||
if (this.optionSelectTrigger) {
|
||||
var triggerWidth = this._getLabelWidth(this.optionSelectTrigger);
|
||||
labelWidth = this._getLabelWidth(this.optionSelectLabel)-4;
|
||||
this.optionSelectLabel.width(labelWidth+(newWidth-triggerWidth));
|
||||
}
|
||||
}
|
||||
},
|
||||
_destroy: function() {
|
||||
this.menu.remove();
|
||||
},
|
||||
width: function(desiredWidth) {
|
||||
this.uiWidth = desiredWidth;
|
||||
this._resize();
|
||||
},
|
||||
value: function(value) {
|
||||
if (!arguments.length) {
|
||||
return this.element.val();
|
||||
} else {
|
||||
if (this.typeMap[this.propertyType].options) {
|
||||
if (this.typeMap[this.propertyType].options.indexOf(value) === -1) {
|
||||
value = "";
|
||||
}
|
||||
this.optionSelectLabel.text(value);
|
||||
}
|
||||
this.element.val(value);
|
||||
this.element.trigger('change');
|
||||
}
|
||||
},
|
||||
type: function(type) {
|
||||
if (!arguments.length) {
|
||||
return this.propertyType;
|
||||
} else {
|
||||
var opt = this.typeMap[type];
|
||||
if (opt && this.propertyType !== type) {
|
||||
this.propertyType = type;
|
||||
this.typeField.val(type);
|
||||
this.selectLabel.empty();
|
||||
if (opt.icon) {
|
||||
$('<img>',{src:opt.icon,style:"margin-right: 4px;height: 18px;"}).prependTo(this.selectLabel);
|
||||
} else {
|
||||
this.selectLabel.text(opt.label);
|
||||
}
|
||||
if (opt.options) {
|
||||
if (this.optionSelectTrigger) {
|
||||
this.optionSelectTrigger.show();
|
||||
this.element.hide();
|
||||
var that = this;
|
||||
this.optionMenu = this._createMenu(opt.options,function(v){
|
||||
that.optionSelectLabel.text(v);
|
||||
that.value(v);
|
||||
});
|
||||
var currentVal = this.element.val();
|
||||
if (opt.options.indexOf(currentVal) !== -1) {
|
||||
this.optionSelectLabel.text(currentVal);
|
||||
} else {
|
||||
this.value(opt.options[0]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this.optionMenu) {
|
||||
this.optionMenu.remove();
|
||||
this.optionMenu = null;
|
||||
}
|
||||
if (this.optionSelectTrigger) {
|
||||
this.optionSelectTrigger.hide();
|
||||
}
|
||||
if (opt.hasValue === false) {
|
||||
this.element.val("");
|
||||
this.element.hide();
|
||||
} else {
|
||||
this.element.show();
|
||||
}
|
||||
this.element.trigger('change');
|
||||
}
|
||||
this._resize();
|
||||
}
|
||||
}
|
||||
},
|
||||
validate: function() {
|
||||
var result;
|
||||
var value = this.value();
|
||||
var type = this.type();
|
||||
if (this.typeMap[type] && this.typeMap[type].validate) {
|
||||
var val = this.typeMap[type].validate;
|
||||
if (typeof val === 'function') {
|
||||
result = val(value);
|
||||
} else {
|
||||
result = val.test(value);
|
||||
}
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
if (result) {
|
||||
this.uiSelect.removeClass('input-error');
|
||||
} else {
|
||||
this.uiSelect.addClass('input-error');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
})(jQuery);
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013, 2015 IBM Corp.
|
||||
* Copyright 2013, 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -31,6 +31,12 @@ RED.view = (function() {
|
||||
|
||||
var workspaceScrollPositions = {};
|
||||
|
||||
var gridSize = 20;
|
||||
var snapGrid = false;
|
||||
|
||||
var activeSpliceLink;
|
||||
var spliceActive = false;
|
||||
var spliceTimer;
|
||||
|
||||
var activeSubflow = null;
|
||||
var activeNodes = [];
|
||||
@@ -196,40 +202,54 @@ RED.view = (function() {
|
||||
.attr('height', space_height)
|
||||
.attr('fill','#fff');
|
||||
|
||||
//var gridScale = d3.scale.linear().range([0,2000]).domain([0,2000]);
|
||||
//var grid = vis.append('g');
|
||||
//
|
||||
//grid.selectAll("line.horizontal").data(gridScale.ticks(100)).enter()
|
||||
// .append("line")
|
||||
// .attr(
|
||||
// {
|
||||
// "class":"horizontal",
|
||||
// "x1" : 0,
|
||||
// "x2" : 2000,
|
||||
// "y1" : function(d){ return gridScale(d);},
|
||||
// "y2" : function(d){ return gridScale(d);},
|
||||
// "fill" : "none",
|
||||
// "shape-rendering" : "crispEdges",
|
||||
// "stroke" : "#eee",
|
||||
// "stroke-width" : "1px"
|
||||
// });
|
||||
//grid.selectAll("line.vertical").data(gridScale.ticks(100)).enter()
|
||||
// .append("line")
|
||||
// .attr(
|
||||
// {
|
||||
// "class":"vertical",
|
||||
// "y1" : 0,
|
||||
// "y2" : 2000,
|
||||
// "x1" : function(d){ return gridScale(d);},
|
||||
// "x2" : function(d){ return gridScale(d);},
|
||||
// "fill" : "none",
|
||||
// "shape-rendering" : "crispEdges",
|
||||
// "stroke" : "#eee",
|
||||
// "stroke-width" : "1px"
|
||||
// });
|
||||
var gridScale = d3.scale.linear().range([0,space_width]).domain([0,space_width]);
|
||||
var grid = vis.append('g');
|
||||
|
||||
grid.selectAll("line.horizontal").data(gridScale.ticks(space_width/gridSize)).enter()
|
||||
.append("line")
|
||||
.attr(
|
||||
{
|
||||
"class":"horizontal",
|
||||
"x1" : 0,
|
||||
"x2" : space_width,
|
||||
"y1" : function(d){ return gridScale(d);},
|
||||
"y2" : function(d){ return gridScale(d);},
|
||||
"fill" : "none",
|
||||
"shape-rendering" : "crispEdges",
|
||||
"stroke" : "#eee",
|
||||
"stroke-width" : "1px"
|
||||
});
|
||||
grid.selectAll("line.vertical").data(gridScale.ticks(space_width/gridSize)).enter()
|
||||
.append("line")
|
||||
.attr(
|
||||
{
|
||||
"class":"vertical",
|
||||
"y1" : 0,
|
||||
"y2" : space_width,
|
||||
"x1" : function(d){ return gridScale(d);},
|
||||
"x2" : function(d){ return gridScale(d);},
|
||||
"fill" : "none",
|
||||
"shape-rendering" : "crispEdges",
|
||||
"stroke" : "#eee",
|
||||
"stroke-width" : "1px"
|
||||
});
|
||||
grid.style("visibility","hidden");
|
||||
|
||||
var drag_line = vis.append("svg:path").attr("class", "drag_line");
|
||||
var dragGroup = vis.append('g');
|
||||
var drag_lines = [];
|
||||
|
||||
function showDragLines(nodes) {
|
||||
for (var i=0;i<nodes.length;i++) {
|
||||
var node = nodes[i];
|
||||
node.el = dragGroup.append("svg:path").attr("class", "drag_line");
|
||||
drag_lines.push(node);
|
||||
}
|
||||
}
|
||||
function hideDragLines() {
|
||||
while(drag_lines.length) {
|
||||
(drag_lines.pop()).el.remove();
|
||||
}
|
||||
}
|
||||
|
||||
function updateActiveNodes() {
|
||||
var activeWorkspace = RED.workspaces.active();
|
||||
@@ -300,7 +320,6 @@ RED.view = (function() {
|
||||
drop: function( event, ui ) {
|
||||
d3.event = event;
|
||||
var selected_tool = ui.draggable[0].type;
|
||||
|
||||
var m = /^subflow:(.+)$/.exec(selected_tool);
|
||||
|
||||
if (activeSubflow && m) {
|
||||
@@ -313,16 +332,9 @@ RED.view = (function() {
|
||||
RED.notify(RED._("notification.error",{message: RED._("notification.errors.cannotAddCircularReference")}),"error");
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var mousePos = d3.touches(this)[0]||d3.mouse(this);
|
||||
mousePos[1] += this.scrollTop;
|
||||
mousePos[0] += this.scrollLeft;
|
||||
mousePos[1] /= scaleFactor;
|
||||
mousePos[0] /= scaleFactor;
|
||||
|
||||
var nn = { id:(1+Math.random()*4294967295).toString(16),x: mousePos[0],y:mousePos[1],w:node_width,z:RED.workspaces.active()};
|
||||
var nn = { id:(1+Math.random()*4294967295).toString(16),z:RED.workspaces.active()};
|
||||
|
||||
nn.type = selected_tool;
|
||||
nn._def = RED.nodes.getType(nn.type);
|
||||
@@ -347,7 +359,10 @@ RED.view = (function() {
|
||||
}
|
||||
|
||||
nn.changed = true;
|
||||
|
||||
nn.w = node_width;
|
||||
nn.h = Math.max(node_height,(nn.outputs||0) * 15);
|
||||
|
||||
var historyEvent = {
|
||||
t:'add',
|
||||
nodes:[nn.id],
|
||||
@@ -364,6 +379,41 @@ RED.view = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
var helperOffset = d3.touches(ui.helper.get(0))[0]||d3.mouse(ui.helper.get(0));
|
||||
var mousePos = d3.touches(this)[0]||d3.mouse(this);
|
||||
|
||||
mousePos[1] += this.scrollTop + ((nn.h/2)-helperOffset[1]);
|
||||
mousePos[0] += this.scrollLeft + ((nn.w/2)-helperOffset[0]);
|
||||
mousePos[1] /= scaleFactor;
|
||||
mousePos[0] /= scaleFactor;
|
||||
|
||||
if (snapGrid) {
|
||||
mousePos[0] = gridSize*(Math.ceil(mousePos[0]/gridSize));
|
||||
mousePos[1] = gridSize*(Math.ceil(mousePos[1]/gridSize));
|
||||
}
|
||||
nn.x = mousePos[0];
|
||||
nn.y = mousePos[1];
|
||||
|
||||
var spliceLink = $(ui.helper).data('splice');
|
||||
if (spliceLink) {
|
||||
// TODO: DRY - canvasMouseUp
|
||||
RED.nodes.removeLink(spliceLink);
|
||||
var link1 = {
|
||||
source:spliceLink.source,
|
||||
sourcePort:spliceLink.sourcePort,
|
||||
target: nn
|
||||
};
|
||||
var link2 = {
|
||||
source:nn,
|
||||
sourcePort:0,
|
||||
target: spliceLink.target
|
||||
};
|
||||
RED.nodes.addLink(link1);
|
||||
RED.nodes.addLink(link2);
|
||||
historyEvent.links = [link1,link2];
|
||||
historyEvent.removedLinks = [spliceLink];
|
||||
}
|
||||
|
||||
RED.history.push(historyEvent);
|
||||
RED.nodes.add(nn);
|
||||
RED.editor.validateNode(nn);
|
||||
@@ -420,6 +470,8 @@ RED.view = (function() {
|
||||
}
|
||||
|
||||
function canvasMouseMove() {
|
||||
var i;
|
||||
var node;
|
||||
mouse_position = d3.touches(this)[0]||d3.mouse(this);
|
||||
// Prevent touch scrolling...
|
||||
//if (d3.touches(this)[0]) {
|
||||
@@ -466,36 +518,73 @@ RED.view = (function() {
|
||||
var mousePos;
|
||||
if (mouse_mode == RED.state.JOINING) {
|
||||
// update drag line
|
||||
drag_line.attr("class", "drag_line");
|
||||
mousePos = mouse_position;
|
||||
var numOutputs = (mousedown_port_type === 0)?(mousedown_node.outputs || 1):1;
|
||||
var sourcePort = mousedown_port_index;
|
||||
var portY = -((numOutputs-1)/2)*13 +13*sourcePort;
|
||||
|
||||
var sc = (mousedown_port_type === 0)?1:-1;
|
||||
|
||||
var dy = mousePos[1]-(mousedown_node.y+portY);
|
||||
var dx = mousePos[0]-(mousedown_node.x+sc*mousedown_node.w/2);
|
||||
var delta = Math.sqrt(dy*dy+dx*dx);
|
||||
var scale = lineCurveScale;
|
||||
var scaleY = 0;
|
||||
|
||||
if (delta < node_width) {
|
||||
scale = 0.75-0.75*((node_width-delta)/node_width);
|
||||
}
|
||||
if (dx*sc < 0) {
|
||||
scale += 2*(Math.min(5*node_width,Math.abs(dx))/(5*node_width));
|
||||
if (Math.abs(dy) < 3*node_height) {
|
||||
scaleY = ((dy>0)?0.5:-0.5)*(((3*node_height)-Math.abs(dy))/(3*node_height))*(Math.min(node_width,Math.abs(dx))/(node_width)) ;
|
||||
if (drag_lines.length === 0) {
|
||||
if (d3.event.shiftKey) {
|
||||
// Get all the wires we need to detach.
|
||||
var links = [];
|
||||
var filter;
|
||||
if (mousedown_port_type === 0) {
|
||||
filter = {
|
||||
source:mousedown_node,
|
||||
sourcePort: mousedown_port_index
|
||||
}
|
||||
} else {
|
||||
filter = {
|
||||
target: mousedown_node
|
||||
}
|
||||
}
|
||||
var existingLinks = RED.nodes.filterLinks(filter);
|
||||
for (i=0;i<existingLinks.length;i++) {
|
||||
var link = existingLinks[i];
|
||||
RED.nodes.removeLink(link);
|
||||
links.push({
|
||||
link:link,
|
||||
node: (mousedown_port_type===0)?link.target:link.source,
|
||||
port: (mousedown_port_type===0)?0:link.sourcePort,
|
||||
portType: (mousedown_port_type===0)?1:0
|
||||
})
|
||||
}
|
||||
showDragLines(links);
|
||||
mouse_mode = 0;
|
||||
updateActiveNodes();
|
||||
redraw();
|
||||
mouse_mode = RED.state.JOINING;
|
||||
} else {
|
||||
showDragLines([{node:mousedown_node,port:mousedown_port_index,portType:mousedown_port_type}]);
|
||||
}
|
||||
}
|
||||
mousePos = mouse_position;
|
||||
for (i=0;i<drag_lines.length;i++) {
|
||||
var drag_line = drag_lines[i];
|
||||
var numOutputs = (drag_line.portType === 0)?(drag_line.node.outputs || 1):1;
|
||||
var sourcePort = drag_line.port;
|
||||
var portY = -((numOutputs-1)/2)*13 +13*sourcePort;
|
||||
|
||||
drag_line.attr("d",
|
||||
"M "+(mousedown_node.x+sc*mousedown_node.w/2)+" "+(mousedown_node.y+portY)+
|
||||
" C "+(mousedown_node.x+sc*(mousedown_node.w/2+node_width*scale))+" "+(mousedown_node.y+portY+scaleY*node_height)+" "+
|
||||
(mousePos[0]-sc*(scale)*node_width)+" "+(mousePos[1]-scaleY*node_height)+" "+
|
||||
mousePos[0]+" "+mousePos[1]
|
||||
);
|
||||
var sc = (drag_line.portType === 0)?1:-1;
|
||||
|
||||
var dy = mousePos[1]-(drag_line.node.y+portY);
|
||||
var dx = mousePos[0]-(drag_line.node.x+sc*drag_line.node.w/2);
|
||||
var delta = Math.sqrt(dy*dy+dx*dx);
|
||||
var scale = lineCurveScale;
|
||||
var scaleY = 0;
|
||||
|
||||
if (delta < node_width) {
|
||||
scale = 0.75-0.75*((node_width-delta)/node_width);
|
||||
}
|
||||
if (dx*sc < 0) {
|
||||
scale += 2*(Math.min(5*node_width,Math.abs(dx))/(5*node_width));
|
||||
if (Math.abs(dy) < 3*node_height) {
|
||||
scaleY = ((dy>0)?0.5:-0.5)*(((3*node_height)-Math.abs(dy))/(3*node_height))*(Math.min(node_width,Math.abs(dx))/(node_width)) ;
|
||||
}
|
||||
}
|
||||
|
||||
drag_line.el.attr("d",
|
||||
"M "+(drag_line.node.x+sc*drag_line.node.w/2)+" "+(drag_line.node.y+portY)+
|
||||
" C "+(drag_line.node.x+sc*(drag_line.node.w/2+node_width*scale))+" "+(drag_line.node.y+portY+scaleY*node_height)+" "+
|
||||
(mousePos[0]-sc*(scale)*node_width)+" "+(mousePos[1]-scaleY*node_height)+" "+
|
||||
mousePos[0]+" "+mousePos[1]
|
||||
);
|
||||
}
|
||||
d3.event.preventDefault();
|
||||
} else if (mouse_mode == RED.state.MOVING) {
|
||||
mousePos = d3.mouse(document.body);
|
||||
@@ -506,11 +595,17 @@ RED.view = (function() {
|
||||
if (d > 3) {
|
||||
mouse_mode = RED.state.MOVING_ACTIVE;
|
||||
clickElapsed = 0;
|
||||
spliceActive = false;
|
||||
if (moving_set.length === 1) {
|
||||
node = moving_set[0];
|
||||
spliceActive = node.n._def.inputs > 0 &&
|
||||
node.n._def.outputs > 0 &&
|
||||
RED.nodes.filterLinks({ source: node.n }).length === 0 &&
|
||||
RED.nodes.filterLinks({ target: node.n }).length === 0;
|
||||
}
|
||||
}
|
||||
} else if (mouse_mode == RED.state.MOVING_ACTIVE || mouse_mode == RED.state.IMPORT_DRAGGING) {
|
||||
mousePos = mouse_position;
|
||||
var node;
|
||||
var i;
|
||||
var minX = 0;
|
||||
var minY = 0;
|
||||
for (var n = 0; n<moving_set.length; n++) {
|
||||
@@ -532,11 +627,11 @@ RED.view = (function() {
|
||||
node.n.y -= minY;
|
||||
}
|
||||
}
|
||||
if (d3.event.shiftKey && moving_set.length > 0) {
|
||||
var gridOffset = [0,0];
|
||||
if (snapGrid != d3.event.shiftKey && moving_set.length > 0) {
|
||||
var gridOffset = [0,0];
|
||||
node = moving_set[0];
|
||||
gridOffset[0] = node.n.x-(20*Math.floor((node.n.x-node.n.w/2)/20)+node.n.w/2);
|
||||
gridOffset[1] = node.n.y-(20*Math.floor(node.n.y/20));
|
||||
gridOffset[0] = node.n.x-(gridSize*Math.floor((node.n.x-node.n.w/2)/gridSize)+node.n.w/2);
|
||||
gridOffset[1] = node.n.y-(gridSize*Math.floor(node.n.y/gridSize));
|
||||
if (gridOffset[0] !== 0 || gridOffset[1] !== 0) {
|
||||
for (i = 0; i<moving_set.length; i++) {
|
||||
node = moving_set[i];
|
||||
@@ -548,6 +643,57 @@ RED.view = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mouse_mode == RED.state.MOVING_ACTIVE && moving_set.length === 1) {
|
||||
node = moving_set[0];
|
||||
if (spliceActive) {
|
||||
if (!spliceTimer) {
|
||||
spliceTimer = setTimeout(function() {
|
||||
var nodes = [];
|
||||
var bestDistance = Infinity;
|
||||
var bestLink = null;
|
||||
var mouseX = mousePos[0];
|
||||
var mouseY = mousePos[1];
|
||||
if (outer[0][0].getIntersectionList) {
|
||||
var svgRect = outer[0][0].createSVGRect();
|
||||
svgRect.x = mouseX;
|
||||
svgRect.y = mouseY;
|
||||
svgRect.width = 1;
|
||||
svgRect.height = 1;
|
||||
nodes = outer[0][0].getIntersectionList(svgRect, outer[0][0]);
|
||||
} else {
|
||||
// Firefox doesn't do getIntersectionList and that
|
||||
// makes us sad
|
||||
nodes = RED.view.getLinksAtPoint(mouseX,mouseY);
|
||||
}
|
||||
for (var i=0;i<nodes.length;i++) {
|
||||
if (d3.select(nodes[i]).classed('link_background')) {
|
||||
var length = nodes[i].getTotalLength();
|
||||
for (var j=0;j<length;j+=10) {
|
||||
var p = nodes[i].getPointAtLength(j);
|
||||
var d2 = ((p.x-mouseX)*(p.x-mouseX))+((p.y-mouseY)*(p.y-mouseY));
|
||||
if (d2 < 200 && d2 < bestDistance) {
|
||||
bestDistance = d2;
|
||||
bestLink = nodes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (activeSpliceLink && activeSpliceLink !== bestLink) {
|
||||
d3.select(activeSpliceLink.parentNode).classed('link_splice',false);
|
||||
}
|
||||
if (bestLink) {
|
||||
d3.select(bestLink.parentNode).classed('link_splice',true)
|
||||
} else {
|
||||
d3.select('.link_splice').classed('link_splice',false);
|
||||
}
|
||||
activeSpliceLink = bestLink;
|
||||
spliceTimer = null;
|
||||
},100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
if (mouse_mode !== 0) {
|
||||
redraw();
|
||||
@@ -555,8 +701,22 @@ RED.view = (function() {
|
||||
}
|
||||
|
||||
function canvasMouseUp() {
|
||||
var i;
|
||||
var historyEvent;
|
||||
if (mousedown_node && mouse_mode == RED.state.JOINING) {
|
||||
drag_line.attr("class", "drag_line_hidden");
|
||||
var removedLinks = [];
|
||||
for (i=0;i<drag_lines.length;i++) {
|
||||
if (drag_lines[i].link) {
|
||||
removedLinks.push(drag_lines[i].link)
|
||||
}
|
||||
}
|
||||
historyEvent = {
|
||||
t:'delete',
|
||||
links: removedLinks,
|
||||
dirty:RED.nodes.dirty()
|
||||
};
|
||||
RED.history.push(historyEvent);
|
||||
hideDragLines();
|
||||
}
|
||||
if (lasso) {
|
||||
var x = parseInt(lasso.attr("x"));
|
||||
@@ -594,7 +754,7 @@ RED.view = (function() {
|
||||
updateSelection();
|
||||
lasso.remove();
|
||||
lasso = null;
|
||||
} else if (mouse_mode == RED.state.DEFAULT && mousedown_link == null && !d3.event.ctrlKey ) {
|
||||
} else if (mouse_mode == RED.state.DEFAULT && mousedown_link == null && !d3.event.ctrlKey&& !d3.event.metaKey ) {
|
||||
clearSelection();
|
||||
updateSelection();
|
||||
}
|
||||
@@ -604,11 +764,33 @@ RED.view = (function() {
|
||||
for (var j=0;j<moving_set.length;j++) {
|
||||
ns.push({n:moving_set[j].n,ox:moving_set[j].ox,oy:moving_set[j].oy});
|
||||
}
|
||||
RED.history.push({t:'move',nodes:ns,dirty:RED.nodes.dirty()});
|
||||
historyEvent = {t:'move',nodes:ns,dirty:RED.nodes.dirty()};
|
||||
if (activeSpliceLink) {
|
||||
// TODO: DRY - droppable
|
||||
var spliceLink = d3.select(activeSpliceLink).data()[0];
|
||||
RED.nodes.removeLink(spliceLink);
|
||||
var link1 = {
|
||||
source:spliceLink.source,
|
||||
sourcePort:spliceLink.sourcePort,
|
||||
target: moving_set[0].n
|
||||
};
|
||||
var link2 = {
|
||||
source:moving_set[0].n,
|
||||
sourcePort:0,
|
||||
target: spliceLink.target
|
||||
};
|
||||
RED.nodes.addLink(link1);
|
||||
RED.nodes.addLink(link2);
|
||||
historyEvent.links = [link1,link2];
|
||||
historyEvent.removedLinks = [spliceLink];
|
||||
updateActiveNodes();
|
||||
}
|
||||
RED.nodes.dirty(true);
|
||||
RED.history.push(historyEvent);
|
||||
}
|
||||
}
|
||||
if (mouse_mode == RED.state.MOVING || mouse_mode == RED.state.MOVING_ACTIVE) {
|
||||
for (var i=0;i<moving_set.length;i++) {
|
||||
for (i=0;i<moving_set.length;i++) {
|
||||
delete moving_set[i].ox;
|
||||
delete moving_set[i].oy;
|
||||
}
|
||||
@@ -724,6 +906,7 @@ RED.view = (function() {
|
||||
delete moving_set[i].oy;
|
||||
}
|
||||
RED.history.push({t:'move',nodes:ns,dirty:RED.nodes.dirty()});
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
function moveSelection(dx,dy) {
|
||||
var minX = 0;
|
||||
@@ -876,6 +1059,13 @@ RED.view = (function() {
|
||||
mousedown_link = null;
|
||||
mouse_mode = 0;
|
||||
mousedown_port_type = 0;
|
||||
activeSpliceLink = null;
|
||||
spliceActive = false;
|
||||
d3.select('.link_splice').classed('link_splice',false);
|
||||
if (spliceTimer) {
|
||||
clearTimeout(spliceTimer);
|
||||
spliceTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function portMouseDown(d,portType,portIndex) {
|
||||
@@ -892,46 +1082,59 @@ RED.view = (function() {
|
||||
}
|
||||
|
||||
function portMouseUp(d,portType,portIndex) {
|
||||
var i;
|
||||
document.body.style.cursor = "";
|
||||
if (mouse_mode == RED.state.JOINING && mousedown_node) {
|
||||
if (mouse_mode == RED.state.JOINING && drag_lines.length > 0) {
|
||||
if (typeof TouchEvent != "undefined" && d3.event instanceof TouchEvent) {
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.z == RED.workspaces.active()) {
|
||||
var hw = n.w/2;
|
||||
var hh = n.h/2;
|
||||
if (n.x-hw<mouse_position[0] && n.x+hw> mouse_position[0] &&
|
||||
n.y-hh<mouse_position[1] && n.y+hh>mouse_position[1]) {
|
||||
mouseup_node = n;
|
||||
portType = mouseup_node.inputs>0?1:0;
|
||||
portIndex = 0;
|
||||
}
|
||||
if (n.z == RED.workspaces.active()) {
|
||||
var hw = n.w/2;
|
||||
var hh = n.h/2;
|
||||
if (n.x-hw<mouse_position[0] && n.x+hw> mouse_position[0] &&
|
||||
n.y-hh<mouse_position[1] && n.y+hh>mouse_position[1]) {
|
||||
mouseup_node = n;
|
||||
portType = mouseup_node.inputs>0?1:0;
|
||||
portIndex = 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
mouseup_node = d;
|
||||
}
|
||||
if (portType == mousedown_port_type || mouseup_node === mousedown_node) {
|
||||
drag_line.attr("class", "drag_line_hidden");
|
||||
resetMouseVars();
|
||||
return;
|
||||
var addedLinks = [];
|
||||
var removedLinks = [];
|
||||
|
||||
for (i=0;i<drag_lines.length;i++) {
|
||||
if (drag_lines[i].link) {
|
||||
removedLinks.push(drag_lines[i].link)
|
||||
}
|
||||
}
|
||||
var src,dst,src_port;
|
||||
if (mousedown_port_type === 0) {
|
||||
src = mousedown_node;
|
||||
src_port = mousedown_port_index;
|
||||
dst = mouseup_node;
|
||||
} else if (mousedown_port_type == 1) {
|
||||
src = mouseup_node;
|
||||
dst = mousedown_node;
|
||||
src_port = portIndex;
|
||||
for (i=0;i<drag_lines.length;i++) {
|
||||
if (portType != drag_lines[i].portType && mouseup_node !== drag_lines[i].node) {
|
||||
var drag_line = drag_lines[i];
|
||||
var src,dst,src_port;
|
||||
if (drag_line.portType === 0) {
|
||||
src = drag_line.node;
|
||||
src_port = drag_line.port;
|
||||
dst = mouseup_node;
|
||||
} else if (drag_line.portType == 1) {
|
||||
src = mouseup_node;
|
||||
dst = drag_line.node;
|
||||
src_port = portIndex;
|
||||
}
|
||||
var existingLink = RED.nodes.filterLinks({source:src,target:dst,sourcePort: src_port}).length !== 0;
|
||||
if (!existingLink) {
|
||||
var link = {source: src, sourcePort:src_port, target: dst};
|
||||
RED.nodes.addLink(link);
|
||||
addedLinks.push(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
var existingLink = RED.nodes.filterLinks({source:src,target:dst,sourcePort: src_port}).length !== 0;
|
||||
if (!existingLink) {
|
||||
var link = {source: src, sourcePort:src_port, target: dst};
|
||||
RED.nodes.addLink(link);
|
||||
if (addedLinks.length > 0 || removedLinks.length > 0) {
|
||||
var historyEvent = {
|
||||
t:'add',
|
||||
links:[link],
|
||||
links:addedLinks,
|
||||
removedLinks: removedLinks,
|
||||
dirty:RED.nodes.dirty()
|
||||
};
|
||||
if (activeSubflow) {
|
||||
@@ -947,8 +1150,9 @@ RED.view = (function() {
|
||||
RED.history.push(historyEvent);
|
||||
updateActiveNodes();
|
||||
RED.nodes.dirty(true);
|
||||
} else {
|
||||
}
|
||||
resetMouseVars();
|
||||
hideDragLines();
|
||||
selected_link = null;
|
||||
redraw();
|
||||
}
|
||||
@@ -994,10 +1198,10 @@ RED.view = (function() {
|
||||
|
||||
var i;
|
||||
|
||||
if (d.selected && d3.event.ctrlKey) {
|
||||
d.selected = false;
|
||||
if (d.selected && (d3.event.ctrlKey||d3.event.metaKey)) {
|
||||
mousedown_node.selected = false;
|
||||
for (i=0;i<moving_set.length;i+=1) {
|
||||
if (moving_set[i].n === d) {
|
||||
if (moving_set[i].n === mousedown_node) {
|
||||
moving_set.splice(i,1);
|
||||
break;
|
||||
}
|
||||
@@ -1012,7 +1216,7 @@ RED.view = (function() {
|
||||
moving_set.push({n:cnodes[n]});
|
||||
}
|
||||
} else if (!d.selected) {
|
||||
if (!d3.event.ctrlKey) {
|
||||
if (!d3.event.ctrlKey && !d3.event.metaKey) {
|
||||
clearSelection();
|
||||
}
|
||||
mousedown_node.selected = true;
|
||||
@@ -1123,7 +1327,7 @@ RED.view = (function() {
|
||||
.on("touchstart", function(d,i){portMouseDown(d,1,0);} )
|
||||
.on("mouseup", function(d,i){portMouseUp(d,1,0);})
|
||||
.on("touchend",function(d,i){portMouseUp(d,1,0);} )
|
||||
.on("mouseover",function(d,i) { var port = d3.select(this); port.classed("port_hovered",(mouse_mode!=RED.state.JOINING || mousedown_port_type !== 0 ));})
|
||||
.on("mouseover",function(d,i) { var port = d3.select(this); port.classed("port_hovered",(mouse_mode!=RED.state.JOINING || (drag_lines.length > 0 && drag_lines[0].portType !== 1)));})
|
||||
.on("mouseout",function(d,i) { var port = d3.select(this); port.classed("port_hovered",false);});
|
||||
|
||||
outGroup.append("svg:text").attr('class','port_label').attr('x',20).attr('y',8).style("font-size","10px").text("output");
|
||||
@@ -1166,7 +1370,7 @@ RED.view = (function() {
|
||||
.on("touchstart", function(d,i){portMouseDown(d,0,i);} )
|
||||
.on("mouseup", function(d,i){portMouseUp(d,0,i);})
|
||||
.on("touchend",function(d,i){portMouseUp(d,0,i);} )
|
||||
.on("mouseover",function(d,i) { var port = d3.select(this); port.classed("port_hovered",(mouse_mode!=RED.state.JOINING || mousedown_port_type !== 0 ));})
|
||||
.on("mouseover",function(d,i) { var port = d3.select(this); port.classed("port_hovered",(mouse_mode!=RED.state.JOINING || (drag_lines.length > 0 && drag_lines[0].portType !== 0) ));})
|
||||
.on("mouseout",function(d,i) { var port = d3.select(this); port.classed("port_hovered",false);});
|
||||
inGroup.append("svg:text").attr('class','port_label').attr('x',18).attr('y',20).style("font-size","10px").text("input");
|
||||
|
||||
@@ -1205,7 +1409,7 @@ RED.view = (function() {
|
||||
node.attr("id",d.id);
|
||||
var l = d._def.label;
|
||||
l = (typeof l === "function" ? l.call(d) : l)||"";
|
||||
d.w = Math.max(node_width,calculateTextWidth(l, "node_label", 50)+(d._def.inputs>0?7:0) );
|
||||
d.w = Math.max(node_width,gridSize*(Math.ceil((calculateTextWidth(l, "node_label", 50)+(d._def.inputs>0?7:0))/gridSize)) );
|
||||
d.h = Math.max(node_height,(d.outputs||0) * 15);
|
||||
|
||||
if (d._def.badge) {
|
||||
@@ -1388,8 +1592,10 @@ RED.view = (function() {
|
||||
if (d.resize) {
|
||||
var l = d._def.label;
|
||||
l = (typeof l === "function" ? l.call(d) : l)||"";
|
||||
d.w = Math.max(node_width,calculateTextWidth(l, "node_label", 50)+(d._def.inputs>0?7:0) );
|
||||
var ow = d.w;
|
||||
d.w = Math.max(node_width,gridSize*(Math.ceil((calculateTextWidth(l, "node_label", 50)+(d._def.inputs>0?7:0))/gridSize)) );
|
||||
d.h = Math.max(node_height,(d.outputs||0) * 15);
|
||||
d.x += (d.w-ow)/2;
|
||||
d.resize = false;
|
||||
}
|
||||
var thisNode = d3.select(this);
|
||||
@@ -1423,7 +1629,7 @@ RED.view = (function() {
|
||||
.on("touchstart",function(d){portMouseDown(d,1,0);})
|
||||
.on("mouseup",function(d){portMouseUp(d,1,0);} )
|
||||
.on("touchend",function(d){portMouseUp(d,1,0);} )
|
||||
.on("mouseover",function(d) { var port = d3.select(this); port.classed("port_hovered",(mouse_mode!=RED.state.JOINING || mousedown_port_type != 1 ));})
|
||||
.on("mouseover",function(d) { var port = d3.select(this); port.classed("port_hovered",(mouse_mode!=RED.state.JOINING || (drag_lines.length > 0 && drag_lines[0].portType !== 1) ));})
|
||||
.on("mouseout",function(d) { var port = d3.select(this); port.classed("port_hovered",false);})
|
||||
}
|
||||
|
||||
@@ -1438,7 +1644,7 @@ RED.view = (function() {
|
||||
.on("touchstart",(function(){var node = d; return function(d,i){portMouseDown(node,0,i);}})() )
|
||||
.on("mouseup",(function(){var node = d; return function(d,i){portMouseUp(node,0,i);}})() )
|
||||
.on("touchend",(function(){var node = d; return function(d,i){portMouseUp(node,0,i);}})() )
|
||||
.on("mouseover",function(d,i) { var port = d3.select(this); port.classed("port_hovered",(mouse_mode!=RED.state.JOINING || mousedown_port_type !== 0 ));})
|
||||
.on("mouseover",function(d,i) { var port = d3.select(this); port.classed("port_hovered",(mouse_mode!=RED.state.JOINING || (drag_lines.length > 0 && drag_lines[0].portType !== 0) ));})
|
||||
.on("mouseout",function(d,i) { var port = d3.select(this); port.classed("port_hovered",false);});
|
||||
|
||||
d._ports.exit().remove();
|
||||
@@ -1618,7 +1824,7 @@ RED.view = (function() {
|
||||
touchStartTime = null;
|
||||
showTouchMenu(obj,pos);
|
||||
},touchLongPressTimeout);
|
||||
});
|
||||
})
|
||||
l.append("svg:path").attr("class","link_outline link_path");
|
||||
l.append("svg:path").attr("class","link_line link_path")
|
||||
.classed("link_subflow", function(d) { return activeSubflow && (d.source.type === "subflow" || d.target.type === "subflow") });
|
||||
@@ -1844,6 +2050,31 @@ RED.view = (function() {
|
||||
selection.link = selected_link;
|
||||
}
|
||||
return selection;
|
||||
},
|
||||
toggleShowGrid: function(state) {
|
||||
if (state) {
|
||||
grid.style("visibility","visible");
|
||||
} else {
|
||||
grid.style("visibility","hidden");
|
||||
}
|
||||
},
|
||||
toggleSnapGrid: function(state) {
|
||||
snapGrid = state;
|
||||
redraw();
|
||||
},
|
||||
scale: function() {
|
||||
return scaleFactor;
|
||||
},
|
||||
getLinksAtPoint: function(x,y) {
|
||||
var result = [];
|
||||
var links = outer.selectAll(".link_background")[0];
|
||||
for (var i=0;i<links.length;i++) {
|
||||
var bb = links[i].getBBox();
|
||||
if (x >= bb.x && y >= bb.y && x <= bb.x+bb.width && y <= bb.y+bb.height) {
|
||||
result.push(links[i])
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
@@ -28,7 +28,6 @@ RED.workspaces = (function() {
|
||||
var tabId = RED.nodes.id();
|
||||
do {
|
||||
workspaceIndex += 1;
|
||||
//TODO: nls of Sheet
|
||||
} while($("#workspace-tabs a[title='"+RED._('workspace.defaultName',{number:workspaceIndex})+"']").size() !== 0);
|
||||
|
||||
ws = {type:"tab",id:tabId,label:RED._('workspace.defaultName',{number:workspaceIndex})};
|
||||
@@ -55,6 +54,7 @@ RED.workspaces = (function() {
|
||||
historyEvent.workspaces = [ws];
|
||||
RED.history.push(historyEvent);
|
||||
RED.nodes.dirty(true);
|
||||
RED.sidebar.config.refresh();
|
||||
} else {
|
||||
$( "#node-dialog-delete-workspace" ).dialog('option','workspace',ws);
|
||||
$( "#node-dialog-delete-workspace-content" ).text(RED._("workspace.delete",{label:ws.label}));
|
||||
@@ -141,8 +141,8 @@ RED.workspaces = (function() {
|
||||
if (workspace.label != label) {
|
||||
workspace_tabs.renameTab(workspace.id,label);
|
||||
RED.nodes.dirty(true);
|
||||
RED.sidebar.config.refresh();
|
||||
$("#menu-item-workspace-menu-"+workspace.id.replace(".","-")).text(label);
|
||||
// TODO: update entry in menu
|
||||
}
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
|
@@ -43,3 +43,8 @@ $workspace-button-color-disabled: #ccc;
|
||||
$workspace-button-color-focus: #999;
|
||||
$workspace-button-color-hover: #666;
|
||||
$workspace-button-color-active: #666;
|
||||
$workspace-button-color-selected: #AAA;
|
||||
|
||||
$typedInput-button-background: #efefef;
|
||||
$typedInput-button-background-hover: #ddd;
|
||||
$typedInput-button-background-active: #e3e3e3;
|
||||
|
@@ -186,7 +186,7 @@
|
||||
|
||||
.drag_line {
|
||||
stroke: $node-selected-color;
|
||||
stroke-width: 4;
|
||||
stroke-width: 3;
|
||||
fill: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -225,6 +225,9 @@
|
||||
cursor: crosshair;
|
||||
fill: none;
|
||||
}
|
||||
.link_splice > .link_line {
|
||||
stroke-dasharray: 15,8;
|
||||
}
|
||||
|
||||
g.link_selected path.link_line {
|
||||
stroke: $node-selected-color;
|
||||
|
@@ -22,6 +22,9 @@
|
||||
font-size: 14px !important;
|
||||
font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif !important;
|
||||
}
|
||||
.ui-widget input {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* jQuery Theme overrides */
|
||||
.ui-tabs .ui-tabs-panel {
|
||||
|
@@ -57,7 +57,17 @@
|
||||
background: $workspace-button-background-active;
|
||||
text-decoration: none;
|
||||
}
|
||||
&.selected:not(.disabled) {
|
||||
color: $workspace-button-color-selected;
|
||||
background: $workspace-button-background-active;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.button-group &:not(:first-child) {
|
||||
border-left: none;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin component-footer {
|
||||
border-top: 1px solid $primary-border-color;
|
||||
background: #f3f3f3;
|
||||
|
@@ -33,6 +33,12 @@
|
||||
border: 1px solid #325C80;
|
||||
border-left-width: 16px;
|
||||
}
|
||||
.notification a {
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.notification-success {
|
||||
border-color: #4B8400;
|
||||
|
@@ -114,11 +114,16 @@
|
||||
}
|
||||
|
||||
.palette-header {
|
||||
position: relative;
|
||||
background: $palette-header-background;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
padding: 9px;
|
||||
font-weight: bold;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
}
|
||||
.palette-header i {
|
||||
margin: 3px 10px 3px 3px;
|
||||
|
@@ -37,7 +37,6 @@
|
||||
right: 0;
|
||||
bottom: 25px;
|
||||
left: 0px;
|
||||
padding-top: 3px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@@ -63,6 +62,27 @@
|
||||
padding: 2px 8px;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
color: #666;
|
||||
text-align: right;
|
||||
padding: 8px 10px;
|
||||
background: #f3f3f3;
|
||||
border-bottom: 1px solid $secondary-border-color;
|
||||
}
|
||||
|
||||
#sidebar-footer {
|
||||
@include component-footer;
|
||||
}
|
||||
|
||||
.sidebar-footer-button {
|
||||
@include component-footer-button;
|
||||
}
|
||||
.sidebar-header-button {
|
||||
@include workspace-button;
|
||||
font-size: 13px;
|
||||
line-height: 13px;
|
||||
padding: 5px 8px;
|
||||
}
|
||||
.sidebar-header-button:not(:first-child) {
|
||||
border-left: none;
|
||||
}
|
||||
|
@@ -41,6 +41,8 @@
|
||||
@import "popover";
|
||||
@import "flow";
|
||||
|
||||
@import "typedInput";
|
||||
|
||||
@import "dragdrop";
|
||||
|
||||
@import "keyboard";
|
||||
|
@@ -47,23 +47,35 @@
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
}
|
||||
.config_node_type {
|
||||
color: #999;
|
||||
text-align: right;
|
||||
padding-right: 3px;
|
||||
&:not(:first-child) {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
.config_node_none {
|
||||
color: #ddd;
|
||||
text-align:right;
|
||||
padding-right: 3px;
|
||||
}
|
||||
.config_node_unused {
|
||||
border-color: #aaa;
|
||||
background: #f9f9f9;
|
||||
border-style: dashed;
|
||||
color: #aaa;
|
||||
}
|
||||
}
|
||||
.config_node_type {
|
||||
color: #999;
|
||||
text-align: right;
|
||||
padding-right: 3px;
|
||||
&:not(:first-child) {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
.config_node_none {
|
||||
color: #ddd;
|
||||
text-align:right;
|
||||
padding-right: 3px;
|
||||
}
|
||||
.config_node_unused {
|
||||
border-color: #aaa;
|
||||
background: #f9f9f9;
|
||||
border-style: dashed;
|
||||
color: #aaa;
|
||||
}
|
||||
.config-node-filter-info {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right:0;
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
padding: 0 8px;
|
||||
background: $palette-header-background;
|
||||
font-size: 0.8em;
|
||||
color: #999;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
122
editor/sass/typedInput.scss
Normal file
122
editor/sass/typedInput.scss
Normal file
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* Copyright 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
.red-ui-typedInput-container {
|
||||
border: 1px solid $form-input-border-color;
|
||||
border-radius: 4px;
|
||||
height: 34px;
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
vertical-align: middle;
|
||||
box-sizing: border-box;
|
||||
overflow:hidden;
|
||||
|
||||
input {
|
||||
padding: 0 0 0 1px;
|
||||
margin:0;
|
||||
height: 32px;
|
||||
border:none;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
box-shadow: none;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&.red-ui-typedInput-focus:not(.input-error) {
|
||||
border-color: $form-input-focus-color !important;
|
||||
}
|
||||
|
||||
a {
|
||||
border-top-left-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
padding: 0 1px 0 5px;
|
||||
display:inline-block;
|
||||
background: $typedInput-button-background;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
vertical-align: middle;
|
||||
color: #555;
|
||||
i {
|
||||
position:relative;
|
||||
top:-3px;
|
||||
margin-right:4px;
|
||||
margin-top: 1px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
padding: 0 1px 0 5px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
background: $typedInput-button-background-hover;
|
||||
}
|
||||
&:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
&:active {
|
||||
background: $typedInput-button-background-active;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
a.red-ui-typedInput-option-trigger {
|
||||
border-top-left-radius: 0px;
|
||||
border-bottom-left-radius: 0px;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
padding: 0 5px 0 0;
|
||||
|
||||
i {
|
||||
margin-right: 0;
|
||||
margin-left: 4px;
|
||||
}
|
||||
span {
|
||||
background:#fff;
|
||||
padding: 0 5px 0 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.red-ui-typedInput-options {
|
||||
@include component-shadow;
|
||||
position: absolute;
|
||||
border: 1px solid $primary-border-color;
|
||||
background: #fff;
|
||||
z-index: 2000;
|
||||
a {
|
||||
padding: 6px 18px 6px 6px;
|
||||
display: block;
|
||||
border-bottom: 1px solid $secondary-border-color;
|
||||
color: #333;
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
background: $typedInput-button-background-hover;
|
||||
}
|
||||
&:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
&:active {
|
||||
background: $typedInput-button-background-active;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@@ -35,7 +35,7 @@
|
||||
</head>
|
||||
<body spellcheck="false">
|
||||
<div id="header">
|
||||
<span class="logo">{{#header.url}}<a href="{{.}}">{{/header.url}}{{#header.image}}<img src="{{.}}">{{/header.image}} <span>{{ header.title }}</span>{{#header.url}}</a>{{/header.url}}</span>
|
||||
<span class="logo">{{#header.url}}<a href="{{.}}">{{/header.url}}{{#header.image}}<img src="{{.}}" title="{{version}}">{{/header.image}} <span>{{ header.title }}</span>{{#header.url}}</a>{{/header.url}}</span>
|
||||
<ul class="header-toolbar hide">
|
||||
<li><a id="btn-sidemenu" class="button" data-toggle="dropdown" href="#"><i class="fa fa-bars"></i></a></li>
|
||||
<ul>
|
||||
@@ -104,9 +104,6 @@
|
||||
<div id="node-dialog-confirm-deploy-unknown" style="text-align: left; padding-top: 10px;" data-i18n="[prepend]deploy.confirm.unknown;[append]deploy.confirm.confirm">
|
||||
<ul style="font-size: 0.9em; width: 400px; margin: 10px auto; text-align: left;" id="node-dialog-confirm-deploy-unknown-list"></ul>
|
||||
</div>
|
||||
<div id="node-dialog-confirm-deploy-unused" style="text-align: left; padding-top: 10px;" data-i18n="[prepend]deploy.confirm.unusedConfig;[append]deploy.confirm.confirm">
|
||||
<ul style="font-size: 0.9em; width: 400px; margin: 10px auto; text-align: left;" id="node-dialog-confirm-deploy-unused-list"></ul>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
4
editor/vendor/jquery/js/jquery-1.11.1.min.js
vendored
4
editor/vendor/jquery/js/jquery-1.11.1.min.js
vendored
File diff suppressed because one or more lines are too long
5
editor/vendor/jquery/js/jquery-1.11.3.min.js
vendored
Normal file
5
editor/vendor/jquery/js/jquery-1.11.3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -15,18 +15,10 @@
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-template-name="inject">
|
||||
<div class="form-row node-input-payload">
|
||||
<label for="node-input-payloadType"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload"></span></label>
|
||||
<select id="node-input-payloadType" style="width:73%">
|
||||
<option value="date" data-i18n="inject.timestamp"></option>
|
||||
<option value="string" data-i18n="inject.string"></option>
|
||||
<option value="none" data-i18n="inject.blank"></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-row" id="node-input-row-payload">
|
||||
<label for="node-input-payload"></label>
|
||||
<input type="text" id="node-input-payload" style="width:70%">
|
||||
<div class="form-row">
|
||||
<label for="node-input-payload"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload"></span></label>
|
||||
<input type="text" id="node-input-payload" style="width:300px">
|
||||
<input type="hidden" id="node-input-payloadType">
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
@@ -196,23 +188,51 @@
|
||||
outputs:1,
|
||||
icon: "inject.png",
|
||||
label: function() {
|
||||
if (this.payloadType === "string") {
|
||||
if (this.name) {
|
||||
return this.name;
|
||||
} else if (this.payloadType === "string" ||
|
||||
this.payloadType === "str" ||
|
||||
this.payloadType === "num" ||
|
||||
this.payloadType === "bool" ||
|
||||
this.payloadType === "json") {
|
||||
if ((this.topic !== "") && ((this.topic.length + this.payload.length) <= 32)) {
|
||||
return this.name||this.topic + ":" + this.payload;
|
||||
}
|
||||
else if (this.payload.length < 24) {
|
||||
return this.name||this.payload;
|
||||
return this.topic + ":" + this.payload;
|
||||
} else if (this.payload.length > 0 && this.payload.length < 24) {
|
||||
return this.payload;
|
||||
} else {
|
||||
return this._("inject.inject");
|
||||
}
|
||||
} else if (this.payloadType === 'date') {
|
||||
return this._("inject.timestamp")
|
||||
} else if (this.payloadType === 'flow' && this.payload.length < 19) {
|
||||
return 'flow.'+this.payload;
|
||||
} else if (this.payloadType === 'global' && this.payload.length < 17) {
|
||||
return 'global.'+this.payload;
|
||||
} else {
|
||||
return this._("inject.inject");
|
||||
}
|
||||
if ((this.topic.length < 24) && (this.topic.length > 0)) {
|
||||
return this.name||this.topic;
|
||||
}
|
||||
else { return this.name||(this.payloadType==="date"?this._("inject.timestamp"):null)||this._("inject.inject"); }
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
if (this.payloadType == null) {
|
||||
if (this.payload == "") {
|
||||
this.payloadType = "date";
|
||||
} else {
|
||||
this.payloadType = "str";
|
||||
}
|
||||
} else if (this.payloadType === 'string' || this.payloadType === 'none') {
|
||||
this.payloadType = "str";
|
||||
}
|
||||
$("#node-input-payloadType").val(this.payloadType);
|
||||
|
||||
$("#node-input-payload").typedInput({
|
||||
default: 'str',
|
||||
typeField: $("#node-input-payloadType"),
|
||||
types:['flow','global','str','num','bool','json',{value:"date",label:this._("inject.timestamp"),hasValue:false}]
|
||||
});
|
||||
|
||||
$("#inject-time-type-select").change(function() {
|
||||
$("#node-input-crontab").val('');
|
||||
var id = $("#inject-time-type-select option:selected").val();
|
||||
@@ -373,24 +393,8 @@
|
||||
$("#inject-time-type-select option").filter(function() {return $(this).val() == repeattype;}).attr('selected',true);
|
||||
$("#inject-time-row-"+repeattype).show();
|
||||
|
||||
if (this.payloadType == null) {
|
||||
if (this.payload == "") {
|
||||
this.payloadType = "date";
|
||||
} else {
|
||||
this.payloadType = "string";
|
||||
}
|
||||
}
|
||||
$("#node-input-payload").typedInput('type',this.payloadType);
|
||||
|
||||
$("#node-input-payloadType").change(function() {
|
||||
var id = $("#node-input-payloadType option:selected").val();
|
||||
if (id === "string") {
|
||||
$("#node-input-row-payload").show();
|
||||
} else {
|
||||
$("#node-input-row-payload").hide();
|
||||
}
|
||||
});
|
||||
$("#node-input-payloadType").val(this.payloadType);
|
||||
$("#node-input-payloadType").change();
|
||||
$("#inject-time-type-select").change();
|
||||
$("#inject-time-interval-time-start").change();
|
||||
|
||||
|
@@ -53,10 +53,12 @@ module.exports = function(RED) {
|
||||
var msg = {topic:this.topic};
|
||||
if ( (this.payloadType == null && this.payload === "") || this.payloadType === "date") {
|
||||
msg.payload = Date.now();
|
||||
} else if (this.payloadType == null || this.payloadType === "string") {
|
||||
} else if (this.payloadType == null) {
|
||||
msg.payload = this.payload;
|
||||
} else {
|
||||
} else if (this.payloadType == 'none') {
|
||||
msg.payload = "";
|
||||
} else {
|
||||
msg.payload = RED.util.evaluateNodeProperty(this.payload,this.payloadType,this,msg)||"";
|
||||
}
|
||||
this.send(msg);
|
||||
msg = null;
|
||||
|
@@ -22,7 +22,7 @@
|
||||
<option value="target" data-i18n="catch.scope.selected"></options>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row node-input-target-row" style="display: none;">
|
||||
<div class="form-row node-input-target-row" style="display: none;min-height: 100px;">
|
||||
<div id="node-input-catch-target-container-div" style="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;">
|
||||
<input type="checkbox" data-i18n="[title]catch.label.selectAll" id="node-input-target-node-checkbox-all" style="width: 30px; margin: 0 2px 1px 2px;">
|
||||
@@ -114,6 +114,17 @@
|
||||
oneditprepare: function() {
|
||||
var nodeList = $("#node-input-catch-target-container");
|
||||
var node = this;
|
||||
this.resize = function() {
|
||||
var rows = $("#dialog-form>div:not(.node-input-target-row)");
|
||||
var height = $("#dialog-form").height();
|
||||
for (var i=0;i<rows.size();i++) {
|
||||
height -= $(rows[i]).outerHeight(true);
|
||||
}
|
||||
var editorRow = $("#dialog-form>div.node-input-target-row");
|
||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||
$("#node-input-catch-target-container-div").css("height",height+"px");
|
||||
}
|
||||
|
||||
function createNodeList() {
|
||||
var scope = node.scope || [];
|
||||
nodeList.empty();
|
||||
@@ -238,6 +249,7 @@
|
||||
} else {
|
||||
$(".node-input-target-row").hide();
|
||||
}
|
||||
node.resize();
|
||||
});
|
||||
if (this.scope == null) {
|
||||
$("#node-input-scope-select").val("all");
|
||||
@@ -245,31 +257,6 @@
|
||||
$("#node-input-scope-select").val("target");
|
||||
}
|
||||
$("#node-input-scope-select").change();
|
||||
|
||||
function dialogResize() {
|
||||
var rows = $("#dialog-form>div:not(.node-input-target-row)");
|
||||
var height = $("#dialog-form").height();
|
||||
for (var i=0;i<rows.size();i++) {
|
||||
height -= $(rows[i]).outerHeight(true);
|
||||
}
|
||||
var editorRow = $("#dialog-form>div.node-input-target-row");
|
||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||
$("#node-input-catch-target-container-div").css("height",height+"px");
|
||||
};
|
||||
|
||||
$( "#dialog" ).on("dialogresize", dialogResize);
|
||||
$( "#dialog" ).one("dialogopen", function(ev) {
|
||||
var size = $( "#dialog" ).dialog('option','sizeCache-catch');
|
||||
if (size) {
|
||||
$("#dialog").dialog('option','width',size.width);
|
||||
$("#dialog").dialog('option','height',size.height);
|
||||
dialogResize();
|
||||
}
|
||||
});
|
||||
$( "#dialog" ).one("dialogclose", function(ev,ui) {
|
||||
$( "#dialog" ).off("dialogresize",dialogResize);
|
||||
});
|
||||
|
||||
},
|
||||
oneditsave: function() {
|
||||
var scope = $("#node-input-scope-select").children("option:selected").val();
|
||||
@@ -283,8 +270,10 @@
|
||||
node.scope.push($(this).data('node-id'));
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
},
|
||||
oneditresize: function(size) {
|
||||
this.resize();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@@ -22,7 +22,7 @@
|
||||
<option value="target" data-i18n="status.scope.selected"></options>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row node-input-target-row" style="display: none;">
|
||||
<div class="form-row node-input-target-row" style="display: none; min-height: 100px;">
|
||||
<div id="node-input-status-target-container-div" style="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;">
|
||||
<input type="checkbox" data-i18n="[title]status.label.selectAll" id="node-input-target-node-checkbox-all" style="width: 30px; margin: 0 2px 1px 2px;">
|
||||
@@ -105,6 +105,17 @@
|
||||
oneditprepare: function() {
|
||||
var nodeList = $("#node-input-status-target-container");
|
||||
var node = this;
|
||||
this.resize = function() {
|
||||
var rows = $("#dialog-form>div:not(.node-input-target-row)");
|
||||
var height = $("#dialog-form").height();
|
||||
for (var i=0;i<rows.size();i++) {
|
||||
height -= $(rows[i]).outerHeight(true);
|
||||
}
|
||||
var editorRow = $("#dialog-form>div.node-input-target-row");
|
||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||
$("#node-input-status-target-container-div").css("height",height+"px");
|
||||
}
|
||||
|
||||
function createNodeList() {
|
||||
var scope = node.scope || [];
|
||||
nodeList.empty();
|
||||
@@ -219,8 +230,6 @@
|
||||
$(".node-input-target-node-checkbox").prop('checked',this.checked);
|
||||
})
|
||||
|
||||
|
||||
|
||||
$("#node-input-scope-select").change(function(e) {
|
||||
var scope = $(this).children("option:selected").val();
|
||||
if (scope === "target") {
|
||||
@@ -229,6 +238,7 @@
|
||||
} else {
|
||||
$(".node-input-target-row").hide();
|
||||
}
|
||||
node.resize();
|
||||
});
|
||||
if (this.scope == null) {
|
||||
$("#node-input-scope-select").val("all");
|
||||
@@ -236,31 +246,6 @@
|
||||
$("#node-input-scope-select").val("target");
|
||||
}
|
||||
$("#node-input-scope-select").change();
|
||||
|
||||
function dialogResize() {
|
||||
var rows = $("#dialog-form>div:not(.node-input-target-row)");
|
||||
var height = $("#dialog-form").height();
|
||||
for (var i=0;i<rows.size();i++) {
|
||||
height -= $(rows[i]).outerHeight(true);
|
||||
}
|
||||
var editorRow = $("#dialog-form>div.node-input-target-row");
|
||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||
$("#node-input-status-target-container-div").css("height",height+"px");
|
||||
};
|
||||
|
||||
$( "#dialog" ).on("dialogresize", dialogResize);
|
||||
$( "#dialog" ).one("dialogopen", function(ev) {
|
||||
var size = $( "#dialog" ).dialog('option','sizeCache-status');
|
||||
if (size) {
|
||||
$("#dialog").dialog('option','width',size.width);
|
||||
$("#dialog").dialog('option','height',size.height);
|
||||
dialogResize();
|
||||
}
|
||||
});
|
||||
$( "#dialog" ).one("dialogclose", function(ev,ui) {
|
||||
$( "#dialog" ).off("dialogresize",dialogResize);
|
||||
});
|
||||
|
||||
},
|
||||
oneditsave: function() {
|
||||
var scope = $("#node-input-scope-select").children("option:selected").val();
|
||||
@@ -276,6 +261,9 @@
|
||||
})
|
||||
|
||||
}
|
||||
},
|
||||
oneditresize: function(size) {
|
||||
this.resize();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@@ -127,17 +127,10 @@
|
||||
}
|
||||
},
|
||||
onpaletteadd: function() {
|
||||
var content = document.createElement("div");
|
||||
$(content).css({"position":"relative","height":"100%"});
|
||||
var toolbar = document.createElement("div");
|
||||
toolbar.id = "debug-toolbar";
|
||||
content.appendChild(toolbar);
|
||||
var content = $("<div>").css({"position":"relative","height":"100%"});
|
||||
var toolbar = $('<div class="sidebar-header"><a id="debug-tab-clear" title="clear log" class="button" href="#"><i class="fa fa-trash"></i></a></div>').appendTo(content);
|
||||
|
||||
toolbar.innerHTML = '<div class="pull-right"><a id="debug-tab-clear" title="clear log" class="button" href="#"><i class="fa fa-trash"></i></a></div> ';
|
||||
|
||||
var messages = document.createElement("div");
|
||||
messages.id = "debug-content";
|
||||
content.appendChild(messages);
|
||||
var messages = $('<div id="debug-content"/>').appendTo(content);
|
||||
|
||||
RED.sidebar.addTab({
|
||||
id: "debug",
|
||||
@@ -221,9 +214,9 @@
|
||||
'</span>';
|
||||
}
|
||||
msg.innerHTML += '<span class="debug-message-payload">'+ payload+ '</span>';
|
||||
var atBottom = (sbc.scrollHeight-messages.offsetHeight-sbc.scrollTop) < 5;
|
||||
var atBottom = (sbc.scrollHeight-messages.height()-sbc.scrollTop) < 5;
|
||||
messageCount++;
|
||||
$(messages).append(msg);
|
||||
messages.append(msg);
|
||||
|
||||
if (messageCount > 200) {
|
||||
$("#debug-content .debug-message:first").remove();
|
||||
@@ -257,17 +250,12 @@
|
||||
<style>
|
||||
#debug-content {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
top: 43px;
|
||||
bottom: 0px;
|
||||
left:0px;
|
||||
right: 0px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
#debug-toolbar {
|
||||
padding: 3px 10px;
|
||||
height: 24px;
|
||||
background: #f3f3f3;
|
||||
}
|
||||
.debug-message {
|
||||
cursor: pointer;
|
||||
border-bottom: 1px solid #eee;
|
||||
|
@@ -124,7 +124,6 @@ module.exports = function(RED) {
|
||||
if (msg.msg.length > debuglength) {
|
||||
msg.msg = msg.msg.substr(0,debuglength) +" ....";
|
||||
}
|
||||
|
||||
RED.comms.publish("debug",msg);
|
||||
}
|
||||
|
||||
|
@@ -88,31 +88,6 @@
|
||||
min:1
|
||||
});
|
||||
|
||||
function functionDialogResize() {
|
||||
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
|
||||
var height = $("#dialog-form").height();
|
||||
for (var i=0;i<rows.size();i++) {
|
||||
height -= $(rows[i]).outerHeight(true);
|
||||
}
|
||||
var editorRow = $("#dialog-form>div.node-text-editor-row");
|
||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||
$(".node-text-editor").css("height",height+"px");
|
||||
that.editor.resize();
|
||||
}
|
||||
$( "#dialog" ).on("dialogresize", functionDialogResize);
|
||||
$( "#dialog" ).one("dialogopen", function(ev) {
|
||||
var size = $( "#dialog" ).dialog('option','sizeCache-function');
|
||||
if (size) {
|
||||
$("#dialog").dialog('option','width',size.width);
|
||||
$("#dialog").dialog('option','height',size.height);
|
||||
functionDialogResize();
|
||||
}
|
||||
});
|
||||
$( "#dialog" ).one("dialogclose", function(ev,ui) {
|
||||
var height = $( "#dialog" ).dialog('option','height');
|
||||
$( "#dialog" ).off("dialogresize",functionDialogResize);
|
||||
});
|
||||
|
||||
this.editor = RED.editor.createEditor({
|
||||
id: 'node-input-func-editor',
|
||||
mode: 'ace/mode/javascript',
|
||||
@@ -141,6 +116,17 @@
|
||||
}
|
||||
$("#node-input-func").val(this.editor.getValue());
|
||||
delete this.editor;
|
||||
},
|
||||
oneditresize: function(size) {
|
||||
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
|
||||
var height = $("#dialog-form").height();
|
||||
for (var i=0;i<rows.size();i++) {
|
||||
height -= $(rows[i]).outerHeight(true);
|
||||
}
|
||||
var editorRow = $("#dialog-form>div.node-text-editor-row");
|
||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||
$(".node-text-editor").css("height",height+"px");
|
||||
this.editor.resize();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@@ -90,7 +90,34 @@ module.exports = function(RED) {
|
||||
}
|
||||
},
|
||||
context: {
|
||||
global:RED.settings.functionGlobalContext || {}
|
||||
set: function() {
|
||||
node.context().set.apply(node,arguments);
|
||||
},
|
||||
get: function() {
|
||||
return node.context().get.apply(node,arguments);
|
||||
},
|
||||
get global() {
|
||||
return node.context().global;
|
||||
},
|
||||
get flow() {
|
||||
return node.context().flow;
|
||||
}
|
||||
},
|
||||
flow: {
|
||||
set: function() {
|
||||
node.context().flow.set.apply(node,arguments);
|
||||
},
|
||||
get: function() {
|
||||
return node.context().flow.get.apply(node,arguments);
|
||||
}
|
||||
},
|
||||
global: {
|
||||
set: function() {
|
||||
node.context().global.set.apply(node,arguments);
|
||||
},
|
||||
get: function() {
|
||||
return node.context().global.get.apply(node,arguments);
|
||||
}
|
||||
},
|
||||
setTimeout: function () {
|
||||
var func = arguments[0];
|
||||
|
@@ -19,28 +19,32 @@
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
||||
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-field"><i class="fa fa-edit"></i> <span data-i18n="template.label.property"></span></label>
|
||||
<input type="text" id="node-input-field" placeholder="payload" style="width:250px;">
|
||||
<input type="hidden" id="node-input-fieldType">
|
||||
</div>
|
||||
<div class="form-row" style="margin-bottom: 0px;">
|
||||
<label for="node-input-template"><i class="fa fa-file-code-o"></i> <span data-i18n="template.label.template"></span></label>
|
||||
<input type="hidden" id="node-input-template" autofocus="autofocus">
|
||||
<select id="node-input-format" style=" font-size: 0.8em; margin-bottom: 3px; width:110px; float:right;">
|
||||
<option value="handlebars">mustache</option>
|
||||
<option value="html">HTML</option>
|
||||
<option value="json">JSON</option>
|
||||
<option value="markdown">Markdown</option>
|
||||
<option value="text">none</option>
|
||||
</select>
|
||||
<div style="float:right;margin-bottom: 3px; font-size: 0.8em;">
|
||||
<span data-i18n="template.label.format"></span>:
|
||||
<select id="node-input-format" style=" height: 20px; font-size: 1em !important; width:110px;">
|
||||
<option value="handlebars">mustache</option>
|
||||
<option value="html">HTML</option>
|
||||
<option value="json">JSON</option>
|
||||
<option value="markdown">Markdown</option>
|
||||
<option value="text">none</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row node-text-editor-row">
|
||||
<div style="height: 250px;" class="node-text-editor" id="node-input-template-editor" ></div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-field"><i class="fa fa-edit"></i> <span data-i18n="template.label.property"></span></label>
|
||||
msg.<input type="text" id="node-input-field" placeholder="payload" style="width:170px;">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="template">
|
||||
<p>Creates a new message based on the provided template.</p>
|
||||
<p>Sets a property based on the provided template.</p>
|
||||
<p>This uses the <i><a href="http://mustache.github.io/mustache.5.html" target="_new">mustache</a></i> format.</p>
|
||||
<p>For example, when a template of:
|
||||
<pre>Hello {{name}}. Today is {{date}}</pre>
|
||||
@@ -50,8 +54,10 @@
|
||||
date: "Monday"
|
||||
payload: ...
|
||||
}</pre>
|
||||
<p>The resulting payload will be:
|
||||
<p>The resulting property will be:
|
||||
<pre>Hello Fred. Today is Monday</pre>
|
||||
<p>By default, mustache will escape any HTML entities in the values it substitutes.
|
||||
To prevent this, use <code>{{{triple}}}</code> braces.
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -61,6 +67,7 @@
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
field: {value:"payload"},
|
||||
fieldType: {value:"msg"},
|
||||
format: {value:"handlebars"},
|
||||
template: {value:"This is the payload: {{payload}} !"},
|
||||
},
|
||||
@@ -72,30 +79,15 @@
|
||||
},
|
||||
oneditprepare: function() {
|
||||
var that = this;
|
||||
function templateDialogResize() {
|
||||
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
|
||||
var height = $("#dialog-form").height();
|
||||
for (var i=0;i<rows.size();i++) {
|
||||
height -= $(rows[i]).outerHeight(true);
|
||||
}
|
||||
var editorRow = $("#dialog-form>div.node-text-editor-row");
|
||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||
$(".node-text-editor").css("height",height+"px");
|
||||
that.editor.resize();
|
||||
};
|
||||
$( "#dialog" ).on("dialogresize", templateDialogResize);
|
||||
$( "#dialog" ).one("dialogopen", function(ev) {
|
||||
var size = $( "#dialog" ).dialog('option','sizeCache-template');
|
||||
if (size) {
|
||||
$("#dialog").dialog('option','width',size.width);
|
||||
$("#dialog").dialog('option','height',size.height);
|
||||
templateDialogResize();
|
||||
}
|
||||
});
|
||||
$( "#dialog" ).one("dialogclose", function(ev,ui) {
|
||||
var height = $( "#dialog" ).dialog('option','height');
|
||||
$( "#dialog" ).off("dialogresize",templateDialogResize);
|
||||
if (!this.fieldType) {
|
||||
this.fieldType = 'msg';
|
||||
}
|
||||
$("#node-input-field").typedInput({
|
||||
default: 'msg',
|
||||
types: ['msg','flow','global'],
|
||||
typeField: $("#node-input-fieldType")
|
||||
});
|
||||
|
||||
this.editor = RED.editor.createEditor({
|
||||
id: 'node-input-template-editor',
|
||||
mode: 'ace/mode/html',
|
||||
@@ -108,7 +100,7 @@
|
||||
fields:['name','outputs']
|
||||
});
|
||||
this.editor.focus();
|
||||
|
||||
|
||||
$("#node-input-format").change(function() {
|
||||
var mod = "ace/mode/"+$("#node-input-format").val();
|
||||
that.editor.getSession().setMode({
|
||||
@@ -120,6 +112,17 @@
|
||||
oneditsave: function() {
|
||||
$("#node-input-template").val(this.editor.getValue())
|
||||
delete this.editor;
|
||||
},
|
||||
oneditresize: function(size) {
|
||||
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
|
||||
var height = $("#dialog-form").height();
|
||||
for (var i=0;i<rows.size();i++) {
|
||||
height -= $(rows[i]).outerHeight(true);
|
||||
}
|
||||
var editorRow = $("#dialog-form>div.node-text-editor-row");
|
||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||
$(".node-text-editor").css("height",height+"px");
|
||||
this.editor.resize();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@@ -23,33 +23,20 @@ module.exports = function(RED) {
|
||||
this.name = n.name;
|
||||
this.field = n.field || "payload";
|
||||
this.template = n.template;
|
||||
this.fieldType = n.fieldType || "msg";
|
||||
|
||||
var node = this;
|
||||
|
||||
var b = node.field.split(".");
|
||||
var i = 0;
|
||||
var m = null;
|
||||
var rec = function(obj) {
|
||||
i += 1;
|
||||
if ((i < b.length) && (typeof obj[b[i-1]] === "object")) {
|
||||
rec(obj[b[i-1]]); // not there yet - carry on digging
|
||||
}
|
||||
else {
|
||||
if (i === b.length) { // we've finished so assign the value
|
||||
obj[b[i-1]] = mustache.render(node.template,m);
|
||||
node.send(m);
|
||||
}
|
||||
else {
|
||||
obj[b[i-1]] = {}; // needs to be a new object so create it
|
||||
rec(obj[b[i-1]]); // and carry on digging
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node.on("input", function(msg) {
|
||||
try {
|
||||
m = msg;
|
||||
i = 0;
|
||||
rec(msg);
|
||||
var value = mustache.render(node.template,msg);
|
||||
if (node.fieldType === 'msg') {
|
||||
RED.util.setMessageProperty(msg,node.field,value);
|
||||
} else if (node.fieldType === 'flow') {
|
||||
node.context().flow.set(node.field,value);
|
||||
} else if (node.fieldType === 'global') {
|
||||
node.context().global.set(node.field,value);
|
||||
}
|
||||
node.send(msg);
|
||||
} catch(err) {
|
||||
node.error(err.message);
|
||||
}
|
||||
|
@@ -58,30 +58,6 @@
|
||||
$( "#node-input-outputs" ).spinner({
|
||||
min:1
|
||||
});
|
||||
function functionDialogResize() {
|
||||
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
|
||||
var height = $("#dialog-form").height();
|
||||
for (var i=0;i<rows.size();i++) {
|
||||
height -= $(rows[i]).outerHeight(true);
|
||||
}
|
||||
var editorRow = $("#dialog-form>div.node-text-editor-row");
|
||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||
$(".node-text-editor").css("height",height+"px");
|
||||
that.editor.resize();
|
||||
};
|
||||
$( "#dialog" ).on("dialogresize", functionDialogResize);
|
||||
$( "#dialog" ).one("dialogopen", function(ev) {
|
||||
var size = $( "#dialog" ).dialog('option','sizeCache-function');
|
||||
if (size) {
|
||||
$("#dialog").dialog('option','width',size.width);
|
||||
$("#dialog").dialog('option','height',size.height);
|
||||
functionDialogResize();
|
||||
}
|
||||
});
|
||||
$( "#dialog" ).one("dialogclose", function(ev,ui) {
|
||||
var height = $( "#dialog" ).dialog('option','height');
|
||||
$( "#dialog" ).off("dialogresize",functionDialogResize);
|
||||
});
|
||||
this.editor = RED.editor.createEditor({
|
||||
id: 'node-input-info-editor',
|
||||
mode: 'ace/mode/markdown',
|
||||
@@ -92,6 +68,17 @@
|
||||
oneditsave: function() {
|
||||
$("#node-input-info").val(this.editor.getValue());
|
||||
delete this.editor;
|
||||
},
|
||||
oneditresize: function(size) {
|
||||
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
|
||||
var height = $("#dialog-form").height();
|
||||
for (var i=0;i<rows.size();i++) {
|
||||
height -= $(rows[i]).outerHeight(true);
|
||||
}
|
||||
var editorRow = $("#dialog-form>div.node-text-editor-row");
|
||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||
$(".node-text-editor").css("height",height+"px");
|
||||
this.editor.resize();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@@ -22,17 +22,21 @@ module.exports = function(RED) {
|
||||
|
||||
var gpioCommand = __dirname+'/nrgpio.py';
|
||||
|
||||
if (!fs.existsSync("/dev/ttyAMA0")) { // unlikely if not on a Pi
|
||||
try {
|
||||
fs.statSync("/dev/ttyAMA0"); // unlikely if not on a Pi
|
||||
} catch(err) {
|
||||
//RED.log.info(RED._("rpi-gpio.errors.ignorenode"));
|
||||
throw "Info : "+RED._("rpi-gpio.errors.ignorenode");
|
||||
}
|
||||
|
||||
if (!fs.existsSync("/usr/share/doc/python-rpi.gpio")) {
|
||||
try {
|
||||
fs.statSync("/usr/share/doc/python-rpi.gpio");
|
||||
} catch(err) {
|
||||
RED.log.warn(RED._("rpi-gpio.errors.libnotfound"));
|
||||
throw "Warning : "+RED._("rpi-gpio.errors.libnotfound");
|
||||
}
|
||||
|
||||
if ( !(1 & parseInt ((fs.statSync(gpioCommand).mode & parseInt ("777", 8)).toString (8)[0]) )) {
|
||||
if ( !(1 & parseInt((fs.statSync(gpioCommand).mode & parseInt("777", 8)).toString(8)[0]) )) {
|
||||
RED.log.error(RED._("rpi-gpio.errors.needtobeexecutable",{command:gpioCommand}));
|
||||
throw "Error : "+RED._("rpi-gpio.errors.mustbeexecutable");
|
||||
}
|
||||
|
@@ -98,7 +98,7 @@ module.exports = function(RED) {
|
||||
wrapper[f] = function() {
|
||||
node.warn(RED._("httpin.errors.deprecated-call",{method:"msg.req."+f}));
|
||||
var result = req[f].apply(req,arguments);
|
||||
if (result === res) {
|
||||
if (result === req) {
|
||||
return wrapper;
|
||||
} else {
|
||||
return result;
|
||||
@@ -154,6 +154,13 @@ module.exports = function(RED) {
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
var corsHandler = function(req,res,next) { next(); }
|
||||
|
||||
if (RED.settings.httpNodeCors) {
|
||||
corsHandler = cors(RED.settings.httpNodeCors);
|
||||
RED.httpNode.options("*",corsHandler);
|
||||
}
|
||||
|
||||
function HTTPIn(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
if (RED.settings.httpNodeRoot !== false) {
|
||||
@@ -177,22 +184,14 @@ module.exports = function(RED) {
|
||||
var msgid = RED.util.generateId();
|
||||
res._msgid = msgid;
|
||||
if (node.method.match(/(^post$|^delete$|^put$|^options$)/)) {
|
||||
node.send({_msgid:msgid,req:req,res:createResponseWrapper(node,res),payload:req.body});
|
||||
node.send({_msgid:msgid,req:createRequestWrapper(node,req),res:createResponseWrapper(node,res),payload:req.body});
|
||||
} else if (node.method == "get") {
|
||||
node.send({_msgid:msgid,req:req,res:createResponseWrapper(node,res),payload:req.query});
|
||||
node.send({_msgid:msgid,req:createRequestWrapper(node,req),res:createResponseWrapper(node,res),payload:req.query});
|
||||
} else {
|
||||
node.send({_msgid:msgid,req:req,res:createResponseWrapper(node,res)});
|
||||
node.send({_msgid:msgid,req:createRequestWrapper(node,req),res:createResponseWrapper(node,res)});
|
||||
}
|
||||
};
|
||||
|
||||
var corsHandler = function(req,res,next) { next(); }
|
||||
|
||||
if (RED.settings.httpNodeCors && !corsSetup) {
|
||||
corsHandler = cors(RED.settings.httpNodeCors);
|
||||
RED.httpNode.options("*",corsHandler);
|
||||
corsSetup = true;
|
||||
}
|
||||
|
||||
var httpMiddleware = function(req,res,next) { next(); }
|
||||
|
||||
if (RED.settings.httpNodeMiddleware) {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<!--
|
||||
Copyright 2013 IBM Corp.
|
||||
Copyright 2013,2016 IBM Corp.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -52,6 +52,7 @@
|
||||
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
|
||||
</div>
|
||||
<div class="form-tips"><span data-i18n="udp.tip.in"></span></div>
|
||||
<div class="form-tips" id="udpporttip"><span data-i18n="[html]udp.tip.port"></span></div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="udp in">
|
||||
@@ -98,6 +99,21 @@
|
||||
}
|
||||
});
|
||||
$("#node-input-multicast").change();
|
||||
|
||||
var porttip = this._("udp.tip.port");
|
||||
var alreadyused = this._("udp.errors.alreadyused");
|
||||
var portsInUse = {};
|
||||
$.getJSON('udp-ports/'+this.id,function(data) {
|
||||
portsInUse = data || {};
|
||||
$('#udpporttip').html(porttip + Object.keys(data||{}));
|
||||
});
|
||||
$("#node-input-port").change(function() {
|
||||
var portnew = $("#node-input-port").val();
|
||||
if (portsInUse.hasOwnProperty($("#node-input-port").val())) {
|
||||
console.log($("#node-input-port").val());
|
||||
RED.notify(alreadyused+" "+$("#node-input-port").val(),"warn");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013 IBM Corp.
|
||||
* Copyright 2013,2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,7 @@
|
||||
module.exports = function(RED) {
|
||||
"use strict";
|
||||
var dgram = require('dgram');
|
||||
var udpInputPortsInUse = {};
|
||||
|
||||
// The Input Node
|
||||
function UDPin(n) {
|
||||
@@ -28,6 +29,12 @@ module.exports = function(RED) {
|
||||
this.multicast = n.multicast;
|
||||
this.ipv = n.ipv || "udp4";
|
||||
var node = this;
|
||||
if (!udpInputPortsInUse.hasOwnProperty(this.port)) {
|
||||
udpInputPortsInUse[this.port] = n.id;
|
||||
}
|
||||
else {
|
||||
node.warn(RED._("udp.errors.alreadyused",node.port));
|
||||
}
|
||||
|
||||
var opts = {type:node.ipv, reuseAddr:true};
|
||||
if (process.version.indexOf("v0.10") === 0) { opts = node.ipv; }
|
||||
@@ -76,6 +83,10 @@ module.exports = function(RED) {
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
console.log("ID=",node.id);
|
||||
if (udpInputPortsInUse[node.port] === node.id) {
|
||||
delete udpInputPortsInUse[node.port];
|
||||
}
|
||||
try {
|
||||
server.close();
|
||||
node.log(RED._("udp.status.listener-stopped"));
|
||||
@@ -86,9 +97,11 @@ module.exports = function(RED) {
|
||||
|
||||
server.bind(node.port,node.iface);
|
||||
}
|
||||
RED.httpAdmin.get('/udp-ports/:id', RED.auth.needsPermission('udp-in.read'), function(req,res) {
|
||||
res.json(udpInputPortsInUse);
|
||||
});
|
||||
RED.nodes.registerType("udp in",UDPin);
|
||||
|
||||
|
||||
// The Output Node
|
||||
function UDPout(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
|
@@ -37,8 +37,6 @@
|
||||
"repeat": "Repeat"
|
||||
},
|
||||
"timestamp": "timestamp",
|
||||
"string": "string",
|
||||
"blank": "blank",
|
||||
"none": "none",
|
||||
"interval": "interval",
|
||||
"interval-time": "interval between times",
|
||||
@@ -47,6 +45,7 @@
|
||||
"minutes": "minutes",
|
||||
"hours": "hours",
|
||||
"between": "between",
|
||||
"previous": "previous value",
|
||||
"at": "at",
|
||||
"and": "and",
|
||||
"every": "every",
|
||||
@@ -138,7 +137,8 @@
|
||||
"template": {
|
||||
"label": {
|
||||
"template": "Template",
|
||||
"property": "Property"
|
||||
"property": "Property",
|
||||
"format": "Syntax Highlight"
|
||||
},
|
||||
"templatevalue": "This is the payload: {{payload}} !"
|
||||
},
|
||||
@@ -398,7 +398,8 @@
|
||||
},
|
||||
"tip": {
|
||||
"in": "Tip: Make sure your firewall will allow the data in.",
|
||||
"out": "Tip: leave address and port blank if you want to set using <b>msg.ip</b> and <b>msg.port</b>."
|
||||
"out": "Tip: leave address and port blank if you want to set using <b>msg.ip</b> and <b>msg.port</b>.",
|
||||
"port": "Ports already in use: "
|
||||
},
|
||||
"status": {
|
||||
"listener-at": "udp listener at __host__:__port__",
|
||||
@@ -417,7 +418,8 @@
|
||||
"interface": "Must be ip address of the required interface",
|
||||
"ip-notset": "udp: ip address not set",
|
||||
"port-notset": "udp: port not set",
|
||||
"port-invalid": "udp: port number not valid"
|
||||
"port-invalid": "udp: port number not valid",
|
||||
"alreadyused": "udp: port already in use"
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
@@ -459,7 +461,8 @@
|
||||
"replace": "Replace with"
|
||||
},
|
||||
"errors": {
|
||||
"invalid-from": "Invalid 'from' property: __error__"
|
||||
"invalid-from": "Invalid 'from' property: __error__",
|
||||
"invalid-json": "Invalid 'to' JSON property"
|
||||
}
|
||||
},
|
||||
"range": {
|
||||
|
@@ -20,7 +20,7 @@
|
||||
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<span data-i18n="switch.label.property"></span> msg.<input type="text" id="node-input-property" style="width: 200px;"/>
|
||||
<span data-i18n="switch.label.property"></span> <input type="text" id="node-input-property" style="width: 300px;"/>
|
||||
</div>
|
||||
<div class="form-row node-input-rule-container-row" style="margin-bottom: 0px;">
|
||||
<div id="node-input-rule-container-div" style="box-sizing: border-box; border-radius: 5px; height: 310px; padding: 5px; border: 1px solid #ccc; overflow-y:scroll;">
|
||||
@@ -39,7 +39,7 @@
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="switch">
|
||||
<p>A simple function node to route messages based on its properties.</p>
|
||||
<p>A node to route messages based on property values.</p>
|
||||
<p>When a message arrives, the selected property is evaluated against each
|
||||
of the defined rules. The message is then sent to the output of <i>all</i>
|
||||
rules that pass.</p>
|
||||
@@ -53,6 +53,7 @@
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
property: {value:"payload", required:true},
|
||||
propertyType: { value:"msg" },
|
||||
rules: {value:[{t:"eq", v:""}]},
|
||||
checkall: {value:"true", required:true},
|
||||
outputs: {value:1}
|
||||
@@ -64,7 +65,10 @@
|
||||
return this.name||"switch";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
var node = this;
|
||||
var previousValueType = {value:"prev",label:this._("inject.previous"),hasValue:false};
|
||||
|
||||
$("#node-input-property").typedInput({default:this.propertyType||'msg',types:['msg','flow','global']});
|
||||
var operators = [
|
||||
{v:"eq",t:"=="},
|
||||
{v:"neq",t:"!="},
|
||||
@@ -85,7 +89,7 @@
|
||||
var andLabel = this._("switch.and");
|
||||
var caseLabel = this._("switch.ignorecase");
|
||||
|
||||
function resizeRule(rule,width) {
|
||||
this.resizeRule = function(rule,newWidth) {
|
||||
var selectField = rule.find("select");
|
||||
var type = selectField.children("option:selected").val();
|
||||
var valueField = rule.find(".node-input-rule-value");
|
||||
@@ -102,14 +106,14 @@
|
||||
selectField.width(selectWidth);
|
||||
|
||||
if (type === "btwn") {
|
||||
var labelWidth = rule.find(".node-input-tule-btwn-label").width();
|
||||
btwnField1.width((width-256-labelWidth)/2);
|
||||
btwnField2.width((width-256-labelWidth)/2);
|
||||
var labelWidth = rule.find(".node-input-rule-btwn-label").width();
|
||||
btwnField1.typedInput("width",(newWidth-selectWidth-120));
|
||||
btwnField2.typedInput("width",(newWidth-selectWidth-120));
|
||||
} else {
|
||||
if (type === "true" || type === "false" || type === "null" || type === "nnull" || type === "else") {
|
||||
// valueField.hide();
|
||||
} else {
|
||||
valueField.width(width-selectWidth-120);
|
||||
valueField.typedInput("width",(newWidth-selectWidth-120));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,6 +122,8 @@
|
||||
var container = $('<li/>',{style:"background: #fff; margin:0; padding:8px 0px; border-bottom: 1px solid #ccc;"});
|
||||
var row = $('<div/>').appendTo(container);
|
||||
var row2 = $('<div/>',{style:"padding-top: 5px; padding-left: 175px;"}).appendTo(container);
|
||||
var row3 = $('<div/>',{style:"padding-top: 5px; padding-left: 120px;"}).appendTo(container);
|
||||
|
||||
$('<i style="color: #eee; cursor: move;" class="node-input-rule-handle fa fa-bars"></i>').appendTo(row);
|
||||
|
||||
var selectField = $('<select/>',{style:"width:120px; margin-left: 5px; text-align: center;"}).appendTo(row);
|
||||
@@ -125,11 +131,10 @@
|
||||
selectField.append($("<option></option>").val(operators[d].v).text(operators[d].t));
|
||||
}
|
||||
|
||||
var valueField = $('<input/>',{class:"node-input-rule-value",type:"text",style:"margin-left: 5px; width: 145px;"}).appendTo(row);
|
||||
var btwnField = $('<span/>').appendTo(row);
|
||||
var btwnValueField = $('<input/>',{class:"node-input-rule-btwn-value",type:"text",style:"margin-left: 5px; width: 50px;"}).appendTo(btwnField);
|
||||
var btwnAndLabel = $('<span/>',{class:"node-input-tule-btwn-label"}).text(" "+andLabel+" ").appendTo(btwnField);
|
||||
var btwnValue2Field = $('<input/>',{class:"node-input-rule-btwn-value2",type:"text",style:"width: 50px;margin-left:2px;"}).appendTo(btwnField);
|
||||
var valueField = $('<input/>',{class:"node-input-rule-value",type:"text",style:"margin-left: 5px; width: 145px;"}).appendTo(row).typedInput({default:'str',types:['msg','flow','global','str','num',previousValueType]});
|
||||
var btwnValueField = $('<input/>',{class:"node-input-rule-btwn-value",type:"text",style:"margin-left: 5px;"}).appendTo(row).typedInput({default:'num',types:['msg','flow','global','str','num',previousValueType]});
|
||||
var btwnAndLabel = $('<span/>',{class:"node-input-rule-btwn-label"}).text(" "+andLabel+" ").appendTo(row3);
|
||||
var btwnValue2Field = $('<input/>',{class:"node-input-rule-btwn-value2",type:"text",style:"margin-left:2px;"}).appendTo(row3).typedInput({default:'num',types:['msg','flow','global','str','num',previousValueType]});
|
||||
|
||||
var finalspan = $('<span/>',{style:"float: right;margin-right: 10px;"}).appendTo(row);
|
||||
finalspan.append(' → <span class="node-input-rule-index">'+i+'</span> ');
|
||||
@@ -141,24 +146,28 @@
|
||||
$('<label/>',{for:"node-input-rule-case-"+i,style:"margin-left: 3px;"}).text(caseLabel).appendTo(row2);
|
||||
selectField.change(function() {
|
||||
var width = $("#node-input-rule-container").width();
|
||||
resizeRule(container,width);
|
||||
var type = selectField.children("option:selected").val();
|
||||
|
||||
node.resizeRule(container,width);
|
||||
if (type === "btwn") {
|
||||
valueField.hide();
|
||||
btwnField.show();
|
||||
valueField.parent().hide();
|
||||
btwnValueField.parent().show();
|
||||
} else {
|
||||
btwnField.hide();
|
||||
btwnValueField.parent().hide();
|
||||
if (type === "true" || type === "false" || type === "null" || type === "nnull" || type === "else") {
|
||||
valueField.hide();
|
||||
valueField.parent().hide();
|
||||
} else {
|
||||
valueField.show();
|
||||
valueField.parent().show();
|
||||
}
|
||||
}
|
||||
if (type === "regex") {
|
||||
row2.show();
|
||||
row3.hide();
|
||||
} else if (type === "btwn"){
|
||||
row2.hide();
|
||||
row3.show();
|
||||
} else {
|
||||
row2.hide();
|
||||
row3.hide();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -177,10 +186,13 @@
|
||||
|
||||
selectField.find("option").filter(function() {return $(this).val() == rule.t;}).attr('selected',true);
|
||||
if (rule.t == "btwn") {
|
||||
btwnValueField.val(rule.v);
|
||||
btwnValue2Field.val(rule.v2);
|
||||
btwnValueField.typedInput('value',rule.v);
|
||||
btwnValueField.typedInput('type',rule.vt||'num');
|
||||
btwnValue2Field.typedInput('value',rule.v2);
|
||||
btwnValue2Field.typedInput('type',rule.v2t||'num');
|
||||
} else if (typeof rule.v != "undefined") {
|
||||
valueField.val(rule.v);
|
||||
valueField.typedInput('value',rule.v);
|
||||
valueField.typedInput('type',rule.vt||'str');
|
||||
}
|
||||
if (rule.case) {
|
||||
caseSensitive.prop('checked',true);
|
||||
@@ -200,23 +212,6 @@
|
||||
generateRule(i+1,rule);
|
||||
}
|
||||
|
||||
function switchDialogResize() {
|
||||
var rows = $("#dialog-form>div:not(.node-input-rule-container-row)");
|
||||
var height = $("#dialog-form").height();
|
||||
for (var i=0;i<rows.size();i++) {
|
||||
height -= $(rows[i]).outerHeight(true);
|
||||
}
|
||||
var editorRow = $("#dialog-form>div.node-input-rule-container-row");
|
||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||
$("#node-input-rule-container-div").css("height",height+"px");
|
||||
|
||||
var rules = $("#node-input-rule-container").children();
|
||||
var newWidth = $("#node-input-rule-container").width();
|
||||
rules.each(function(i) {
|
||||
resizeRule($(this),newWidth);
|
||||
})
|
||||
};
|
||||
|
||||
$( "#node-input-rule-container" ).sortable({
|
||||
axis: "y",
|
||||
update: function( event, ui ) {
|
||||
@@ -229,21 +224,6 @@
|
||||
cursor: "move"
|
||||
});
|
||||
$( "#node-input-rule-container .node-input-rule-handle" ).disableSelection();
|
||||
|
||||
$( "#dialog" ).on("dialogresize", switchDialogResize);
|
||||
$( "#dialog" ).one("dialogopen", function(ev) {
|
||||
var size = $( "#dialog" ).dialog('option','sizeCache-switch');
|
||||
if (size) {
|
||||
$("#dialog").dialog('option','width',size.width);
|
||||
$("#dialog").dialog('option','height',size.height);
|
||||
switchDialogResize();
|
||||
} else {
|
||||
setTimeout(switchDialogResize,10);
|
||||
}
|
||||
});
|
||||
$( "#dialog" ).one("dialogclose", function(ev,ui) {
|
||||
$( "#dialog" ).off("dialogresize",switchDialogResize);
|
||||
});
|
||||
},
|
||||
oneditsave: function() {
|
||||
var rules = $("#node-input-rule-container").children();
|
||||
@@ -256,10 +236,13 @@
|
||||
var r = {t:type};
|
||||
if (!(type === "true" || type === "false" || type === "null" || type === "nnull" || type === "else")) {
|
||||
if (type === "btwn") {
|
||||
r.v = rule.find(".node-input-rule-btwn-value").val();
|
||||
r.v2 = rule.find(".node-input-rule-btwn-value2").val();
|
||||
r.v = rule.find(".node-input-rule-btwn-value").typedInput('value');
|
||||
r.vt = rule.find(".node-input-rule-btwn-value").typedInput('type');
|
||||
r.v2 = rule.find(".node-input-rule-btwn-value2").typedInput('value');
|
||||
r.v2t = rule.find(".node-input-rule-btwn-value2").typedInput('type');
|
||||
} else {
|
||||
r.v = rule.find(".node-input-rule-value").val();
|
||||
r.v = rule.find(".node-input-rule-value").typedInput('value');
|
||||
r.vt = rule.find(".node-input-rule-value").typedInput('type');
|
||||
}
|
||||
if (type === "regex") {
|
||||
r.case = rule.find(".node-input-rule-case").prop("checked");
|
||||
@@ -267,7 +250,25 @@
|
||||
}
|
||||
node.rules.push(r);
|
||||
});
|
||||
node.outputs = node.rules.length;
|
||||
this.outputs = node.rules.length;
|
||||
this.propertyType = $("#node-input-property").typedInput('type');
|
||||
},
|
||||
oneditresize: function(size) {
|
||||
var rows = $("#dialog-form>div:not(.node-input-rule-container-row)");
|
||||
var height = size.height;
|
||||
for (var i=0;i<rows.size();i++) {
|
||||
height -= $(rows[i]).outerHeight(true);
|
||||
}
|
||||
var editorRow = $("#dialog-form>div.node-input-rule-container-row");
|
||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||
$("#node-input-rule-container-div").css("height",height+"px");
|
||||
|
||||
var rules = $("#node-input-rule-container").children();
|
||||
var newWidth = $("#node-input-rule-container").width();
|
||||
var node = this;
|
||||
rules.each(function(i) {
|
||||
node.resizeRule($(this),newWidth);
|
||||
})
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@@ -37,30 +37,55 @@ module.exports = function(RED) {
|
||||
RED.nodes.createNode(this, n);
|
||||
this.rules = n.rules || [];
|
||||
this.property = n.property;
|
||||
this.propertyType = n.propertyType || "msg";
|
||||
this.checkall = n.checkall || "true";
|
||||
var propertyParts = (n.property || "payload").split(".");
|
||||
this.previousValue = null;
|
||||
var node = this;
|
||||
|
||||
for (var i=0; i<this.rules.length; i+=1) {
|
||||
var rule = this.rules[i];
|
||||
if (!isNaN(Number(rule.v))) {
|
||||
rule.v = Number(rule.v);
|
||||
rule.v2 = Number(rule.v2);
|
||||
if (!rule.vt) {
|
||||
rule.vt = 'str';
|
||||
}
|
||||
if (rule.vt === 'str' || rule.vt === 'num') {
|
||||
if (!isNaN(Number(rule.v))) {
|
||||
rule.v = Number(rule.v);
|
||||
}
|
||||
}
|
||||
if (typeof rule.v2 !== 'undefined') {
|
||||
if (!rule.v2t) {
|
||||
rule.v2t = 'str';
|
||||
}
|
||||
if (rule.v2t === 'str' || rule.v2t === 'num') {
|
||||
if (!isNaN(Number(rule.v2))) {
|
||||
rule.v2 = Number(rule.v2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.on('input', function (msg) {
|
||||
var onward = [];
|
||||
try {
|
||||
var prop = propertyParts.reduce(function (obj, i) {
|
||||
return obj[i]
|
||||
}, msg);
|
||||
var prop = RED.util.evaluateNodeProperty(node.property,node.propertyType,node,msg);
|
||||
var elseflag = true;
|
||||
for (var i=0; i<node.rules.length; i+=1) {
|
||||
var rule = node.rules[i];
|
||||
var test = prop;
|
||||
var v1,v2;
|
||||
if (rule.vt === 'prev') {
|
||||
v1 = node.previousValue;
|
||||
} else {
|
||||
v1 = RED.util.evaluateNodeProperty(rule.v,rule.vt,node,msg);
|
||||
}
|
||||
v2 = rule.v2;
|
||||
if (rule.v2t === 'prev') {
|
||||
v2 = node.previousValue;
|
||||
} else if (typeof v2 !== 'undefined') {
|
||||
v2 = RED.util.evaluateNodeProperty(rule.v2,rule.v2t,node,msg);
|
||||
}
|
||||
node.previousValue = prop;
|
||||
if (rule.t == "else") { test = elseflag; elseflag = true; }
|
||||
if (operators[rule.t](test,rule.v, rule.v2, rule.case)) {
|
||||
if (operators[rule.t](test,v1,v2,rule.case)) {
|
||||
onward.push(msg);
|
||||
elseflag = false;
|
||||
if (node.checkall == "false") { break; }
|
||||
|
@@ -17,7 +17,7 @@
|
||||
<script type="text/x-red" data-template-name="change">
|
||||
<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 type="text" id="node-input-name" style="width: 370px;" data-i18n="[placeholder]common.label.name">
|
||||
</div>
|
||||
<div class="form-row" style="margin-bottom:0;">
|
||||
<label><i class="fa fa-list"></i> <span data-i18n="change.label.rules"></span></label>
|
||||
@@ -33,12 +33,12 @@
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="change">
|
||||
<p>Set, change or delete properties of a message.</p>
|
||||
<p>The node can specify multiple rules that will be applied to the message in turn.</p>
|
||||
<p>Set, change or delete properties of a message, flow context or global context.</p>
|
||||
<p>The node can specify multiple rules that will be applied in turn.</p>
|
||||
<p>The available operations are:</p>
|
||||
<ul>
|
||||
<li><b>Set</b> - set a property. The <b>to</b> property can either be a string value, or reference
|
||||
another message property by name, for example: <code>msg.topic</code>.</li>
|
||||
<li><b>Set</b> - set a property. The value can be a variety of different types, or
|
||||
can be taken from an existing message or context property.</li>
|
||||
<li><b>Change</b> - search & replace parts of the property. If regular expressions
|
||||
are enabled, the <b>replace with</b> property can include capture groups, for example <code>$1</code></li>
|
||||
<li><b>Delete</b> - delete a property.</li>
|
||||
@@ -51,7 +51,7 @@
|
||||
category: 'function',
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
rules:{value:[{t:"set",p:"payload",to:""}]},
|
||||
rules:{value:[{t:"set",p:"payload",pt:"msg",to:"",tot:"str"}]},
|
||||
// legacy
|
||||
action: {value:""},
|
||||
property: {value:""},
|
||||
@@ -77,11 +77,11 @@
|
||||
} else {
|
||||
if (this.rules.length == 1) {
|
||||
if (this.rules[0].t === "set") {
|
||||
return this._("change.label.set",{property:"msg."+this.rules[0].p});
|
||||
return this._("change.label.set",{property:(this.rules[0].pt||"msg")+"."+this.rules[0].p});
|
||||
} else if (this.rules[0].t === "change") {
|
||||
return this._("change.label.change",{property:"msg."+this.rules[0].p});
|
||||
return this._("change.label.change",{property:(this.rules[0].pt||"msg")+"."+this.rules[0].p});
|
||||
} else {
|
||||
return this._("change.label.delete",{property:"msg."+this.rules[0].p});
|
||||
return this._("change.label.delete",{property:(this.rules[0].pt||"msg")+"."+this.rules[0].p});
|
||||
}
|
||||
} else {
|
||||
return this._("change.label.changeCount",{count:this.rules.length});
|
||||
@@ -99,14 +99,22 @@
|
||||
var search = this._("change.action.search");
|
||||
var replace = this._("change.action.replace");
|
||||
var regex = this._("change.label.regex");
|
||||
if (this.reg === null) { $("#node-input-reg").prop('checked', true); }
|
||||
$("#node-input-action").change();
|
||||
|
||||
function resizeRule(rule,width) {
|
||||
rule.find('input[type="text"]').width(width-220);
|
||||
}
|
||||
|
||||
function generateRule(rule) {
|
||||
|
||||
if (rule.t === "change" && rule.re) {
|
||||
rule.fromt = 're';
|
||||
delete rule.re;
|
||||
}
|
||||
if (rule.t === "set" && !rule.tot) {
|
||||
if (rule.to.indexOf("msg.") === 0 && !rule.tot) {
|
||||
rule.to = rule.to.substring(4);
|
||||
rule.tot = "msg";
|
||||
} else {
|
||||
rule.tot = "str";
|
||||
}
|
||||
}
|
||||
|
||||
var container = $('<li/>',{style:"background: #fff; margin:0; padding:8px 0px; border-bottom: 1px solid #ccc;"});
|
||||
|
||||
var row1 = $('<div/>').appendTo(container);
|
||||
@@ -114,41 +122,30 @@
|
||||
var row2 = $('<div/>',{style:"margin-top:8px;"}).appendTo(container);
|
||||
var row3 = $('<div/>',{style:"margin-top:8px;"}).appendTo(container);
|
||||
|
||||
var selectField = $('<select/>',{class:"node-input-rule-type",style:"width: 100px"}).appendTo(row1);
|
||||
var selectField = $('<select/>',{class:"node-input-rule-type",style:"width: 110px; margin-right: 10px;"}).appendTo(row1);
|
||||
var selectOptions = [{v:"set",l:set},{v:"change",l:change},{v:"delete",l:del}];
|
||||
for (var i=0;i<3;i++) {
|
||||
selectField.append($("<option></option>").val(selectOptions[i].v).text(selectOptions[i].l));
|
||||
}
|
||||
|
||||
$('<div/>',{style:"display:inline-block; width: 50px; text-align: right;"}).text("msg.").appendTo(row1);
|
||||
var propertyName = $('<input/>',{style:"width: 220px",class:"node-input-rule-property-name",type:"text"}).appendTo(row1);
|
||||
|
||||
|
||||
var propertyName = $('<input/>',{style:"width: 250px",class:"node-input-rule-property-name",type:"text"}).appendTo(row1).typedInput({types:['msg','flow','global']});
|
||||
var finalspan = $('<span/>',{style:"float: right; margin-right: 10px;"}).appendTo(row1);
|
||||
var deleteButton = $('<a/>',{href:"#",class:"editor-button editor-button-small", style:"margin-top: 7px; margin-left: 5px;"}).appendTo(finalspan);
|
||||
$('<i/>',{class:"fa fa-remove"}).appendTo(deleteButton);
|
||||
|
||||
|
||||
$('<div/>',{style:"display: inline-block;text-align:right; width:150px;padding-right: 10px; box-sizing: border-box;"}).text(to).appendTo(row2);
|
||||
var propertyValue = $('<input/>',{style:"width: 220px",class:"node-input-rule-property-value",type:"text"}).appendTo(row2);
|
||||
|
||||
$('<div/>',{style:"display: inline-block;text-align:right; width:120px;padding-right: 10px; box-sizing: border-box;"}).text(to).appendTo(row2);
|
||||
var propertyValue = $('<input/>',{style:"width: 250px",class:"node-input-rule-property-value",type:"text"}).appendTo(row2).typedInput({default:'str',types:['msg','flow','global','str','num','bool','json']});
|
||||
var row3_1 = $('<div/>').appendTo(row3);
|
||||
$('<div/>',{style:"display: inline-block;text-align:right; width:150px;padding-right: 10px; box-sizing: border-box;"}).text(search).appendTo(row3_1);
|
||||
var fromValue = $('<input/>',{style:"width: 220px",class:"node-input-rule-property-search-value",type:"text"}).appendTo(row3_1);
|
||||
$('<div/>',{style:"display: inline-block;text-align:right; width:120px;padding-right: 10px; box-sizing: border-box;"}).text(search).appendTo(row3_1);
|
||||
var fromValue = $('<input/>',{style:"width: 250px",class:"node-input-rule-property-search-value",type:"text"}).appendTo(row3_1).typedInput({default:'str',types:['msg','flow','global','str','re']});
|
||||
|
||||
var row3_2 = $('<div/>',{style:"margin-top:8px;"}).appendTo(row3);
|
||||
$('<div/>',{style:"display: inline-block;text-align:right; width:150px;padding-right: 10px; box-sizing: border-box;"}).text(replace).appendTo(row3_2);
|
||||
var toValue = $('<input/>',{style:"width: 220px",class:"node-input-rule-property-replace-value",type:"text"}).appendTo(row3_2);
|
||||
|
||||
var row3_3 = $('<div/>',{style:"margin-top:8px;"}).appendTo(row3);
|
||||
var id = "node-input-rule-property-regex-"+Math.floor(Math.random()*10000);
|
||||
var useRegExp = $('<input/>',{id:id,class:"node-input-rule-property-re",type:"checkbox", style:"margin-left: 150px; margin-right: 10px; display: inline-block; width: auto; vertical-align: top;"}).appendTo(row3_3);
|
||||
$('<label/>',{for:id,style:"width: auto;"}).text(regex).appendTo(row3_3);
|
||||
|
||||
$('<div/>',{style:"display: inline-block;text-align:right; width:120px;padding-right: 10px; box-sizing: border-box;"}).text(replace).appendTo(row3_2);
|
||||
var toValue = $('<input/>',{style:"width: 250px",class:"node-input-rule-property-replace-value",type:"text"}).appendTo(row3_2).typedInput({default:'str',types:['msg','flow','global','str','num','json']});
|
||||
|
||||
selectField.change(function() {
|
||||
var width = $("#node-input-rule-container").width();
|
||||
resizeRule(container,width);
|
||||
var type = $(this).val();
|
||||
if (type == "set") {
|
||||
row2.show();
|
||||
@@ -169,11 +166,14 @@
|
||||
});
|
||||
|
||||
selectField.find("option").filter(function() {return $(this).val() == rule.t;}).attr('selected',true);
|
||||
propertyName.val(rule.p);
|
||||
propertyValue.val(rule.to);
|
||||
fromValue.val(rule.from);
|
||||
toValue.val(rule.to);
|
||||
useRegExp.prop('checked', rule.re);
|
||||
propertyName.typedInput('value',rule.p);
|
||||
propertyName.typedInput('type',rule.pt)
|
||||
propertyValue.typedInput('value',rule.to);
|
||||
propertyValue.typedInput('type',rule.tot)
|
||||
fromValue.typedInput('value',rule.from);
|
||||
fromValue.typedInput('type',rule.fromt)
|
||||
toValue.typedInput('value',rule.to);
|
||||
toValue.typedInput('type',rule.tot)
|
||||
selectField.change();
|
||||
|
||||
$("#node-input-rule-container").append(container);
|
||||
@@ -185,7 +185,8 @@
|
||||
if (!this.rules) {
|
||||
var rule = {
|
||||
t:(this.action=="replace"?"set":this.action),
|
||||
p:this.property
|
||||
p:this.property,
|
||||
pt:"msg"
|
||||
}
|
||||
|
||||
if (rule.t === "set") {
|
||||
@@ -208,37 +209,6 @@
|
||||
for (var i=0;i<this.rules.length;i++) {
|
||||
generateRule(this.rules[i]);
|
||||
}
|
||||
|
||||
function changeDialogResize() {
|
||||
var rows = $("#dialog-form>div:not(.node-input-rule-container-row)");
|
||||
var height = $("#dialog-form").height();
|
||||
for (var i=0;i<rows.size();i++) {
|
||||
height -= $(rows[i]).outerHeight(true);
|
||||
}
|
||||
var editorRow = $("#dialog-form>div.node-input-rule-container-row");
|
||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||
$("#node-input-rule-container-div").css("height",height+"px");
|
||||
|
||||
var rules = $("#node-input-rule-container").children();
|
||||
var newWidth = $("#node-input-rule-container").width();
|
||||
rules.each(function(i) {
|
||||
resizeRule($(this),newWidth);
|
||||
})
|
||||
};
|
||||
$( "#dialog" ).on("dialogresize", changeDialogResize);
|
||||
$( "#dialog" ).one("dialogopen", function(ev) {
|
||||
var size = $( "#dialog" ).dialog('option','sizeCache-change');
|
||||
if (size) {
|
||||
$("#dialog").dialog('option','width',size.width);
|
||||
$("#dialog").dialog('option','height',size.height);
|
||||
changeDialogResize();
|
||||
} else {
|
||||
setTimeout(changeDialogResize,10);
|
||||
}
|
||||
});
|
||||
$( "#dialog" ).one("dialogclose", function(ev,ui) {
|
||||
$( "#dialog" ).off("dialogresize",changeDialogResize);
|
||||
});
|
||||
},
|
||||
oneditsave: function() {
|
||||
var rules = $("#node-input-rule-container").children();
|
||||
@@ -250,17 +220,37 @@
|
||||
var type = rule.find(".node-input-rule-type option:selected").val();
|
||||
var r = {
|
||||
t:type,
|
||||
p:rule.find(".node-input-rule-property-name").val()
|
||||
p:rule.find(".node-input-rule-property-name").typedInput('value'),
|
||||
pt:rule.find(".node-input-rule-property-name").typedInput('type')
|
||||
};
|
||||
if (type === "set") {
|
||||
r.to = rule.find(".node-input-rule-property-value").val();
|
||||
r.to = rule.find(".node-input-rule-property-value").typedInput('value');
|
||||
r.tot = rule.find(".node-input-rule-property-value").typedInput('type');
|
||||
} else if (type === "change") {
|
||||
r.from = rule.find(".node-input-rule-property-search-value").val();
|
||||
r.to = rule.find(".node-input-rule-property-replace-value").val();
|
||||
r.re = rule.find(".node-input-rule-property-re").prop('checked');
|
||||
r.from = rule.find(".node-input-rule-property-search-value").typedInput('value');
|
||||
r.fromt = rule.find(".node-input-rule-property-search-value").typedInput('type');
|
||||
r.to = rule.find(".node-input-rule-property-replace-value").typedInput('value');
|
||||
r.tot = rule.find(".node-input-rule-property-replace-value").typedInput('type');
|
||||
}
|
||||
node.rules.push(r);
|
||||
});
|
||||
},
|
||||
oneditresize: function(size) {
|
||||
var rows = $("#dialog-form>div:not(.node-input-rule-container-row)");
|
||||
var height = size.height;
|
||||
for (var i=0;i<rows.size();i++) {
|
||||
height -= $(rows[i]).outerHeight(true);
|
||||
}
|
||||
var editorRow = $("#dialog-form>div.node-input-rule-container-row");
|
||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||
$("#node-input-rule-container-div").css("height",height+"px");
|
||||
|
||||
var rules = $("#node-input-rule-container").children();
|
||||
var newWidth = $("#node-input-rule-container").width();
|
||||
rules.each(function(i) {
|
||||
$(this).find('.red-ui-typedInput').typedInput("width",newWidth-180);
|
||||
})
|
||||
$("#node-input-name").width(newWidth-130);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@@ -19,15 +19,15 @@ module.exports = function(RED) {
|
||||
|
||||
function ChangeNode(n) {
|
||||
RED.nodes.createNode(this, n);
|
||||
|
||||
|
||||
this.rules = n.rules;
|
||||
|
||||
|
||||
if (!this.rules) {
|
||||
var rule = {
|
||||
t:(n.action=="replace"?"set":n.action),
|
||||
p:n.property||""
|
||||
}
|
||||
|
||||
|
||||
if (rule.t === "set") {
|
||||
rule.to = n.to||"";
|
||||
} else if (rule.t === "change") {
|
||||
@@ -37,15 +37,35 @@ module.exports = function(RED) {
|
||||
}
|
||||
this.rules = [rule];
|
||||
}
|
||||
|
||||
|
||||
this.actions = [];
|
||||
|
||||
var valid = true;
|
||||
|
||||
|
||||
for (var i=0;i<this.rules.length;i++) {
|
||||
var rule = this.rules[i];
|
||||
// Migrate to type-aware rules
|
||||
if (!rule.pt) {
|
||||
rule.pt = "msg";
|
||||
}
|
||||
if (rule.t === "change" && rule.re) {
|
||||
rule.fromt = 're';
|
||||
delete rule.re;
|
||||
}
|
||||
if (rule.t === "set" && !rule.tot) {
|
||||
if (rule.to.indexOf("msg.") === 0 && !rule.tot) {
|
||||
rule.to = rule.to.substring(4);
|
||||
rule.tot = "msg";
|
||||
}
|
||||
}
|
||||
if (!rule.tot) {
|
||||
rule.tot = "str";
|
||||
}
|
||||
if (!rule.fromt) {
|
||||
rule.fromt = "str";
|
||||
}
|
||||
if (rule.t === "change") {
|
||||
if (rule.re === false) {
|
||||
if (rule.fromt !== 're') {
|
||||
rule.from = rule.from.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
||||
}
|
||||
try {
|
||||
@@ -55,57 +75,68 @@ module.exports = function(RED) {
|
||||
this.error(RED._("change.errors.invalid-from",{error:e.message}));
|
||||
}
|
||||
}
|
||||
if (rule.tot === 'num') {
|
||||
rule.to = Number(rule.to);
|
||||
} else if (rule.tot === 'json') {
|
||||
try {
|
||||
rule.to = JSON.parse(rule.to);
|
||||
} catch(e2) {
|
||||
valid = false;
|
||||
this.error(RED._("change.errors.invalid-json"));
|
||||
}
|
||||
} else if (rule.tot === 'bool') {
|
||||
rule.to = /^true$/i.test(rule.to);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function applyRule(msg,rule) {
|
||||
var propertyParts;
|
||||
var depth = 0;
|
||||
|
||||
propertyParts = rule.p.split(".");
|
||||
try {
|
||||
propertyParts.reduce(function(obj, i) {
|
||||
var to = rule.to;
|
||||
// Set msg from property to another msg property
|
||||
if (rule.t === "set" && rule.to.indexOf("msg.") === 0) {
|
||||
var parts = to.substring(4);
|
||||
var msgPropParts = parts.split(".");
|
||||
try {
|
||||
msgPropParts.reduce(function(ob, j) {
|
||||
to = (typeof ob[j] !== "undefined" ? ob[j] : undefined);
|
||||
return to;
|
||||
}, msg);
|
||||
} catch (err) {}
|
||||
var property = rule.p;
|
||||
var value = rule.to;
|
||||
if (rule.tot === "msg") {
|
||||
value = RED.util.getMessageProperty(msg,rule.to);
|
||||
} else if (rule.tot === 'flow') {
|
||||
value = node.context().flow.get(rule.to);
|
||||
} else if (rule.tot === 'global') {
|
||||
value = node.context().global.get(rule.to);
|
||||
}
|
||||
if (rule.pt === 'msg') {
|
||||
if (rule.t === 'delete') {
|
||||
RED.util.setMessageProperty(msg,property,undefined);
|
||||
} else if (rule.t === 'set') {
|
||||
RED.util.setMessageProperty(msg,property,value);
|
||||
} else if (rule.t === 'change') {
|
||||
var current = RED.util.getMessageProperty(msg,property);
|
||||
if (typeof current === 'string') {
|
||||
current = current.replace(rule.from,value);
|
||||
RED.util.setMessageProperty(msg,property,current);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var target;
|
||||
if (rule.pt === 'flow') {
|
||||
target = node.context().flow;
|
||||
} else if (rule.pt === 'global') {
|
||||
target = node.context().global;
|
||||
}
|
||||
if (target) {
|
||||
if (rule.t === 'delete') {
|
||||
target.set(property,undefined);
|
||||
} else if (rule.t === 'set') {
|
||||
target.set(property,value);
|
||||
} else if (rule.t === 'change') {
|
||||
var current = target.get(msg,property);
|
||||
if (typeof current === 'string') {
|
||||
current = current.replace(rule.from,value);
|
||||
target.set(property,current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (++depth === propertyParts.length) {
|
||||
if (rule.t === "change") {
|
||||
if (typeof obj[i] === "string") {
|
||||
obj[i] = obj[i].replace(rule.from, rule.to);
|
||||
}
|
||||
} else if (rule.t === "set") {
|
||||
if (typeof to === "undefined") {
|
||||
delete(obj[i]);
|
||||
} else {
|
||||
obj[i] = to;
|
||||
}
|
||||
} else if (rule.t === "delete") {
|
||||
delete(obj[i]);
|
||||
}
|
||||
} else {
|
||||
// to property doesn't exist, don't create empty object
|
||||
if (typeof to === "undefined") {
|
||||
return;
|
||||
// setting a non-existent multilevel object, create empty parent
|
||||
} else if (!obj[i]) {
|
||||
obj[i] = {};
|
||||
}
|
||||
return obj[i];
|
||||
}
|
||||
}, msg);
|
||||
} catch (err) {}
|
||||
}
|
||||
} catch(err) {console.log(err.stack)}
|
||||
return msg;
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
var node = this;
|
||||
this.on('input', function(msg) {
|
||||
|
10
package.json
10
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name" : "node-red",
|
||||
"version" : "0.12.5",
|
||||
"version" : "0.13.0",
|
||||
"description" : "A visual tool for wiring the Internet of Things",
|
||||
"homepage" : "http://nodered.org",
|
||||
"license" : "Apache-2.0",
|
||||
@@ -22,7 +22,7 @@
|
||||
{"name": "Dave Conway-Jones"}
|
||||
],
|
||||
"keywords": [
|
||||
"editor", "messaging", "iot", "m2m", "pi", "arduino", "beaglebone", "ibm", "flow"
|
||||
"editor", "messaging", "iot", "ibm", "flow"
|
||||
],
|
||||
"dependencies": {
|
||||
"basic-auth": "1.0.3",
|
||||
@@ -49,11 +49,11 @@
|
||||
"passport-oauth2-client-password":"0.1.2",
|
||||
"raw-body":"2.1.5",
|
||||
"semver": "5.1.0",
|
||||
"sentiment":"1.0.4",
|
||||
"sentiment":"1.0.5",
|
||||
"uglify-js":"2.6.1",
|
||||
"when": "3.7.7",
|
||||
"ws": "0.8.1",
|
||||
"xml2js":"0.4.15",
|
||||
"xml2js":"0.4.16",
|
||||
"node-red-node-feedparser":"0.1.*",
|
||||
"node-red-node-email":"0.1.*",
|
||||
"node-red-node-twitter":"0.1.*",
|
||||
@@ -72,7 +72,7 @@
|
||||
"grunt-contrib-compress": "0.14.0",
|
||||
"grunt-contrib-concat":"0.5.1",
|
||||
"grunt-contrib-copy": "0.8.2",
|
||||
"grunt-contrib-jshint": "0.11.3",
|
||||
"grunt-contrib-jshint": "0.12.0",
|
||||
"grunt-contrib-uglify": "0.11.0",
|
||||
"grunt-contrib-watch":"0.6.1",
|
||||
"grunt-jsonlint":"1.0.7",
|
||||
|
17
red.js
17
red.js
@@ -25,7 +25,6 @@ var nopt = require("nopt");
|
||||
var path = require("path");
|
||||
var fs = require("fs-extra");
|
||||
var RED = require("./red/red.js");
|
||||
var log = require("./red/log");
|
||||
|
||||
var server;
|
||||
var app = express();
|
||||
@@ -205,7 +204,7 @@ function basicAuthMiddleware(user,pass) {
|
||||
}
|
||||
|
||||
if (settings.httpAdminRoot !== false && settings.httpAdminAuth) {
|
||||
RED.log.warn(log._("server.httpadminauth-deprecated"));
|
||||
RED.log.warn(RED.log._("server.httpadminauth-deprecated"));
|
||||
app.use(settings.httpAdminRoot, basicAuthMiddleware(settings.httpAdminAuth.user,settings.httpAdminAuth.pass));
|
||||
}
|
||||
|
||||
@@ -243,10 +242,10 @@ RED.start().then(function() {
|
||||
if (settings.httpAdminRoot !== false || settings.httpNodeRoot !== false || settings.httpStatic) {
|
||||
server.on('error', function(err) {
|
||||
if (err.errno === "EADDRINUSE") {
|
||||
RED.log.error(log._("server.unable-to-listen", {listenpath:getListenPath()}));
|
||||
RED.log.error(log._("server.port-in-use"));
|
||||
RED.log.error(RED.log._("server.unable-to-listen", {listenpath:getListenPath()}));
|
||||
RED.log.error(RED.log._("server.port-in-use"));
|
||||
} else {
|
||||
RED.log.error(log._("server.uncaught-exception"));
|
||||
RED.log.error(RED.log._("server.uncaught-exception"));
|
||||
if (err.stack) {
|
||||
RED.log.error(err.stack);
|
||||
} else {
|
||||
@@ -257,16 +256,16 @@ RED.start().then(function() {
|
||||
});
|
||||
server.listen(settings.uiPort,settings.uiHost,function() {
|
||||
if (settings.httpAdminRoot === false) {
|
||||
RED.log.info(log._("server.admin-ui-disabled"));
|
||||
RED.log.info(RED.log._("server.admin-ui-disabled"));
|
||||
}
|
||||
process.title = 'node-red';
|
||||
RED.log.info(log._("server.now-running", {listenpath:getListenPath()}));
|
||||
RED.log.info(RED.log._("server.now-running", {listenpath:getListenPath()}));
|
||||
});
|
||||
} else {
|
||||
RED.log.info(log._("server.headless-mode"));
|
||||
RED.log.info(RED.log._("server.headless-mode"));
|
||||
}
|
||||
}).otherwise(function(err) {
|
||||
RED.log.error(log._("server.failed-to-start"));
|
||||
RED.log.error(RED.log._("server.failed-to-start"));
|
||||
if (err.stack) {
|
||||
RED.log.error(err.stack);
|
||||
} else {
|
||||
|
@@ -25,7 +25,7 @@ var permissions = require("./permissions");
|
||||
var theme = require("../theme");
|
||||
|
||||
var settings = null;
|
||||
var log = require("../../log");
|
||||
var log = null
|
||||
|
||||
|
||||
passport.use(strategies.bearerStrategy.BearerStrategy);
|
||||
@@ -36,11 +36,13 @@ var server = oauth2orize.createServer();
|
||||
|
||||
server.exchange(oauth2orize.exchange.password(strategies.passwordTokenExchange));
|
||||
|
||||
function init(_settings,storage) {
|
||||
settings = _settings;
|
||||
function init(runtime) {
|
||||
settings = runtime.settings;
|
||||
log = runtime.log;
|
||||
if (settings.adminAuth) {
|
||||
Users.init(settings.adminAuth);
|
||||
Tokens.init(settings.adminAuth,storage);
|
||||
Tokens.init(settings.adminAuth,runtime.storage);
|
||||
strategies.init(runtime);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -26,7 +26,7 @@ var Users = require("./users");
|
||||
var Clients = require("./clients");
|
||||
var permissions = require("./permissions");
|
||||
|
||||
var log = require("../../log");
|
||||
var log;
|
||||
|
||||
var bearerStrategy = function (accessToken, done) {
|
||||
// is this a valid token?
|
||||
@@ -124,6 +124,9 @@ AnonymousStrategy.prototype.authenticate = function(req) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: function(runtime) {
|
||||
log = runtime.log;
|
||||
},
|
||||
bearerStrategy: bearerStrategy,
|
||||
clientPasswordStrategy: clientPasswordStrategy,
|
||||
passwordTokenExchange: passwordTokenExchange,
|
||||
|
@@ -15,7 +15,7 @@
|
||||
**/
|
||||
|
||||
var ws = require("ws");
|
||||
var log = require("./log");
|
||||
var log;
|
||||
|
||||
var server;
|
||||
var settings;
|
||||
@@ -29,17 +29,23 @@ var retained = {};
|
||||
var heartbeatTimer;
|
||||
var lastSentTime;
|
||||
|
||||
|
||||
function init(_server,_settings) {
|
||||
server = _server;
|
||||
settings = _settings;
|
||||
function handleStatus(event) {
|
||||
publish("status/"+event.id,event.status,true);
|
||||
}
|
||||
|
||||
function init(_server,runtime) {
|
||||
server = _server;
|
||||
settings = runtime.settings;
|
||||
log = runtime.log;
|
||||
|
||||
runtime.events.removeListener("node-status",handleStatus);
|
||||
runtime.events.on("node-status",handleStatus);
|
||||
}
|
||||
|
||||
function start() {
|
||||
var Tokens = require("./api/auth/tokens");
|
||||
var Users = require("./api/auth/users");
|
||||
var Permissions = require("./api/auth/permissions");
|
||||
var Tokens = require("./auth/tokens");
|
||||
var Users = require("./auth/users");
|
||||
var Permissions = require("./auth/permissions");
|
||||
if (!settings.disableEditor) {
|
||||
Users.default().then(function(anonymousUser) {
|
||||
var webSocketKeepAliveTime = settings.webSocketKeepAliveTime || 15000;
|
||||
@@ -151,15 +157,17 @@ function stop() {
|
||||
}
|
||||
|
||||
function publish(topic,data,retain) {
|
||||
if (retain) {
|
||||
retained[topic] = data;
|
||||
} else {
|
||||
delete retained[topic];
|
||||
if (server) {
|
||||
if (retain) {
|
||||
retained[topic] = data;
|
||||
} else {
|
||||
delete retained[topic];
|
||||
}
|
||||
lastSentTime = Date.now();
|
||||
activeConnections.forEach(function(conn) {
|
||||
publishTo(conn,topic,data);
|
||||
});
|
||||
}
|
||||
lastSentTime = Date.now();
|
||||
activeConnections.forEach(function(conn) {
|
||||
publishTo(conn,topic,data);
|
||||
});
|
||||
}
|
||||
|
||||
function publishTo(ws,topic,data) {
|
@@ -14,10 +14,14 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var log = require("../log");
|
||||
var api = require("../nodes");
|
||||
var log;
|
||||
var api;
|
||||
|
||||
module.exports = {
|
||||
init: function(runtime) {
|
||||
log = runtime.log;
|
||||
api = runtime.nodes;
|
||||
},
|
||||
get: function (req, res) {
|
||||
// TODO: It should verify the given node id is of the type specified -
|
||||
// but that would add a dependency from this module to the
|
||||
|
89
red/api/flow.js
Normal file
89
red/api/flow.js
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Copyright 2014, 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var log;
|
||||
var redNodes;
|
||||
var settings;
|
||||
|
||||
module.exports = {
|
||||
init: function(runtime) {
|
||||
settings = runtime.settings;
|
||||
redNodes = runtime.nodes;
|
||||
log = runtime.log;
|
||||
},
|
||||
get: function(req,res) {
|
||||
var id = req.params.id;
|
||||
var flow = redNodes.getFlow(id);
|
||||
if (flow) {
|
||||
log.audit({event: "flow.get",id:id},req);
|
||||
res.json(flow);
|
||||
} else {
|
||||
log.audit({event: "flow.get",id:id,error:"not_found"},req);
|
||||
res.status(404).end();
|
||||
}
|
||||
},
|
||||
post: function(req,res) {
|
||||
var flow = req.body;
|
||||
redNodes.addFlow(flow).then(function(id) {
|
||||
log.audit({event: "flow.add",id:id},req);
|
||||
res.json({id:id});
|
||||
}).otherwise(function(err) {
|
||||
log.audit({event: "flow.add",error:err.code||"unexpected_error",message:err.toString()},req);
|
||||
res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
|
||||
})
|
||||
|
||||
},
|
||||
put: function(req,res) {
|
||||
var id = req.params.id;
|
||||
var flow = req.body;
|
||||
try {
|
||||
redNodes.updateFlow(id,flow).then(function() {
|
||||
log.audit({event: "flow.update",id:id},req);
|
||||
res.json({id:id});
|
||||
}).otherwise(function(err) {
|
||||
console.log(err.stack);
|
||||
log.audit({event: "flow.update",error:err.code||"unexpected_error",message:err.toString()},req);
|
||||
res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
|
||||
})
|
||||
} catch(err) {
|
||||
if (err.code === 404) {
|
||||
log.audit({event: "flow.update",id:id,error:"not_found"},req);
|
||||
res.status(404).end();
|
||||
} else {
|
||||
console.log(err.stack);
|
||||
log.audit({event: "flow.update",error:err.code||"unexpected_error",message:err.toString()},req);
|
||||
res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
|
||||
}
|
||||
}
|
||||
},
|
||||
delete: function(req,res) {
|
||||
var id = req.params.id;
|
||||
try {
|
||||
redNodes.removeFlow(id).then(function() {
|
||||
log.audit({event: "flow.remove",id:id},req);
|
||||
res.status(204).end();
|
||||
})
|
||||
} catch(err) {
|
||||
if (err.code === 404) {
|
||||
log.audit({event: "flow.remove",id:id,error:"not_found"},req);
|
||||
res.status(404).end();
|
||||
} else {
|
||||
log.audit({event: "flow.remove",id:id,error:err.code||"unexpected_error",message:err.toString()},req);
|
||||
res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,12 +14,16 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var log = require("../log");
|
||||
|
||||
var redNodes = require("../nodes");
|
||||
var settings = require("../settings");
|
||||
var log;
|
||||
var redNodes;
|
||||
var settings;
|
||||
|
||||
module.exports = {
|
||||
init: function(runtime) {
|
||||
settings = runtime.settings;
|
||||
redNodes = runtime.nodes;
|
||||
log = runtime.log;
|
||||
},
|
||||
get: function(req,res) {
|
||||
log.audit({event: "flows.get"},req);
|
||||
res.json(redNodes.getFlows());
|
||||
@@ -28,12 +32,22 @@ module.exports = {
|
||||
var flows = req.body;
|
||||
var deploymentType = req.get("Node-RED-Deployment-Type")||"full";
|
||||
log.audit({event: "flows.set",type:deploymentType},req);
|
||||
redNodes.setFlows(flows,deploymentType).then(function() {
|
||||
res.status(204).end();
|
||||
}).otherwise(function(err) {
|
||||
log.warn(log._("api.flows.error-save",{message:err.message}));
|
||||
log.warn(err.stack);
|
||||
res.status(500).json({error:"unexpected_error", message:err.message});
|
||||
});
|
||||
if (deploymentType === 'reload') {
|
||||
redNodes.loadFlows().then(function() {
|
||||
res.status(204).end();
|
||||
}).otherwise(function(err) {
|
||||
log.warn(log._("api.flows.error-reload",{message:err.message}));
|
||||
log.warn(err.stack);
|
||||
res.status(500).json({error:"unexpected_error", message:err.message});
|
||||
});
|
||||
} else {
|
||||
redNodes.setFlows(flows,deploymentType).then(function() {
|
||||
res.status(204).end();
|
||||
}).otherwise(function(err) {
|
||||
log.warn(log._("api.flows.error-save",{message:err.message}));
|
||||
log.warn(err.stack);
|
||||
res.status(500).json({error:"unexpected_error", message:err.message});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
182
red/api/index.js
182
red/api/index.js
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2014 IBM Corp.
|
||||
* Copyright 2014, 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,22 +19,27 @@ var bodyParser = require("body-parser");
|
||||
var util = require('util');
|
||||
var path = require('path');
|
||||
var passport = require('passport');
|
||||
var when = require('when');
|
||||
|
||||
var ui = require("./ui");
|
||||
var nodes = require("./nodes");
|
||||
var flows = require("./flows");
|
||||
var flow = require("./flow");
|
||||
var library = require("./library");
|
||||
var info = require("./info");
|
||||
var theme = require("./theme");
|
||||
var locales = require("./locales");
|
||||
var credentials = require("./credentials");
|
||||
|
||||
var log = require("../log");
|
||||
var comms = require("./comms");
|
||||
|
||||
var auth = require("./auth");
|
||||
var needsPermission = auth.needsPermission;
|
||||
|
||||
var settings = require("../settings");
|
||||
var i18n;
|
||||
var log;
|
||||
var adminApp;
|
||||
var nodeApp;
|
||||
var server;
|
||||
|
||||
var errorHandler = function(err,req,res,next) {
|
||||
if (err.message === "request entity too large") {
|
||||
@@ -46,71 +51,116 @@ var errorHandler = function(err,req,res,next) {
|
||||
res.status(400).json({error:"unexpected_error", message:err.toString()});
|
||||
};
|
||||
|
||||
function init(adminApp,storage) {
|
||||
function init(_server,runtime) {
|
||||
server = _server;
|
||||
var settings = runtime.settings;
|
||||
i18n = runtime.i18n;
|
||||
log = runtime.log;
|
||||
if (settings.httpNodeRoot !== false) {
|
||||
nodeApp = express();
|
||||
}
|
||||
if (settings.httpAdminRoot !== false) {
|
||||
comms.init(server,runtime);
|
||||
adminApp = express();
|
||||
auth.init(runtime);
|
||||
credentials.init(runtime);
|
||||
flows.init(runtime);
|
||||
flow.init(runtime);
|
||||
info.init(runtime);
|
||||
library.init(adminApp,runtime);
|
||||
locales.init(runtime);
|
||||
nodes.init(runtime);
|
||||
|
||||
auth.init(settings,storage);
|
||||
// Editor
|
||||
if (!settings.disableEditor) {
|
||||
ui.init(settings);
|
||||
var editorApp = express();
|
||||
editorApp.get("/",ui.ensureSlash,ui.editor);
|
||||
editorApp.get("/icons/:icon",ui.icon);
|
||||
if (settings.editorTheme) {
|
||||
editorApp.use("/theme",theme.init(settings));
|
||||
// Editor
|
||||
if (!settings.disableEditor) {
|
||||
ui.init(runtime);
|
||||
var editorApp = express();
|
||||
editorApp.get("/",ui.ensureSlash,ui.editor);
|
||||
editorApp.get("/icons/:icon",ui.icon);
|
||||
theme.init(runtime);
|
||||
if (settings.editorTheme) {
|
||||
editorApp.use("/theme",theme.app());
|
||||
}
|
||||
editorApp.use("/",ui.editorResources);
|
||||
adminApp.use(editorApp);
|
||||
}
|
||||
editorApp.use("/",ui.editorResources);
|
||||
adminApp.use(editorApp);
|
||||
var maxApiRequestSize = settings.apiMaxLength || '1mb';
|
||||
adminApp.use(bodyParser.json({limit:maxApiRequestSize}));
|
||||
adminApp.use(bodyParser.urlencoded({limit:maxApiRequestSize,extended:true}));
|
||||
|
||||
adminApp.get("/auth/login",auth.login,errorHandler);
|
||||
|
||||
if (settings.adminAuth) {
|
||||
//TODO: all passport references ought to be in ./auth
|
||||
adminApp.use(passport.initialize());
|
||||
adminApp.post("/auth/token",
|
||||
auth.ensureClientSecret,
|
||||
auth.authenticateClient,
|
||||
auth.getToken,
|
||||
auth.errorHandler
|
||||
);
|
||||
adminApp.post("/auth/revoke",needsPermission(""),auth.revoke,errorHandler);
|
||||
}
|
||||
|
||||
// Flows
|
||||
adminApp.get("/flows",needsPermission("flows.read"),flows.get,errorHandler);
|
||||
adminApp.post("/flows",needsPermission("flows.write"),flows.post,errorHandler);
|
||||
|
||||
adminApp.get("/flow/:id",needsPermission("flows.read"),flow.get,errorHandler);
|
||||
adminApp.post("/flow",needsPermission("flows.write"),flow.post,errorHandler);
|
||||
adminApp.delete("/flow/:id",needsPermission("flows.write"),flow.delete,errorHandler);
|
||||
adminApp.put("/flow/:id",needsPermission("flows.write"),flow.put,errorHandler);
|
||||
|
||||
// Nodes
|
||||
adminApp.get("/nodes",needsPermission("nodes.read"),nodes.getAll,errorHandler);
|
||||
adminApp.post("/nodes",needsPermission("nodes.write"),nodes.post,errorHandler);
|
||||
|
||||
adminApp.get("/nodes/:mod",needsPermission("nodes.read"),nodes.getModule,errorHandler);
|
||||
adminApp.put("/nodes/:mod",needsPermission("nodes.write"),nodes.putModule,errorHandler);
|
||||
adminApp.delete("/nodes/:mod",needsPermission("nodes.write"),nodes.delete,errorHandler);
|
||||
|
||||
adminApp.get("/nodes/:mod/:set",needsPermission("nodes.read"),nodes.getSet,errorHandler);
|
||||
adminApp.put("/nodes/:mod/:set",needsPermission("nodes.write"),nodes.putSet,errorHandler);
|
||||
|
||||
adminApp.get('/credentials/:type/:id', needsPermission("credentials.read"),credentials.get,errorHandler);
|
||||
|
||||
adminApp.get(/locales\/(.+)\/?$/,locales.get,errorHandler);
|
||||
|
||||
// Library
|
||||
adminApp.post(new RegExp("/library/flows\/(.*)"),needsPermission("library.write"),library.post,errorHandler);
|
||||
adminApp.get("/library/flows",needsPermission("library.read"),library.getAll,errorHandler);
|
||||
adminApp.get(new RegExp("/library/flows\/(.*)"),needsPermission("library.read"),library.get,errorHandler);
|
||||
|
||||
// Settings
|
||||
adminApp.get("/settings",needsPermission("settings.read"),info.settings,errorHandler);
|
||||
|
||||
// Error Handler
|
||||
//adminApp.use(errorHandler);
|
||||
}
|
||||
var maxApiRequestSize = settings.apiMaxLength || '1mb';
|
||||
adminApp.use(bodyParser.json({limit:maxApiRequestSize}));
|
||||
adminApp.use(bodyParser.urlencoded({limit:maxApiRequestSize,extended:true}));
|
||||
|
||||
adminApp.get("/auth/login",auth.login);
|
||||
|
||||
if (settings.adminAuth) {
|
||||
//TODO: all passport references ought to be in ./auth
|
||||
adminApp.use(passport.initialize());
|
||||
adminApp.post("/auth/token",
|
||||
auth.ensureClientSecret,
|
||||
auth.authenticateClient,
|
||||
auth.getToken,
|
||||
auth.errorHandler
|
||||
);
|
||||
adminApp.post("/auth/revoke",needsPermission(""),auth.revoke);
|
||||
}
|
||||
|
||||
// Flows
|
||||
adminApp.get("/flows",needsPermission("flows.read"),flows.get);
|
||||
adminApp.post("/flows",needsPermission("flows.write"),flows.post);
|
||||
|
||||
// Nodes
|
||||
adminApp.get("/nodes",needsPermission("nodes.read"),nodes.getAll);
|
||||
adminApp.post("/nodes",needsPermission("nodes.write"),nodes.post);
|
||||
|
||||
adminApp.get("/nodes/:mod",needsPermission("nodes.read"),nodes.getModule);
|
||||
adminApp.put("/nodes/:mod",needsPermission("nodes.write"),nodes.putModule);
|
||||
adminApp.delete("/nodes/:mod",needsPermission("nodes.write"),nodes.delete);
|
||||
|
||||
adminApp.get("/nodes/:mod/:set",needsPermission("nodes.read"),nodes.getSet);
|
||||
adminApp.put("/nodes/:mod/:set",needsPermission("nodes.write"),nodes.putSet);
|
||||
|
||||
adminApp.get('/credentials/:type/:id', needsPermission("credentials.read"),credentials.get);
|
||||
|
||||
adminApp.get(/locales\/(.+)\/?$/,locales.get);
|
||||
|
||||
// Library
|
||||
library.init(adminApp);
|
||||
adminApp.post(new RegExp("/library/flows\/(.*)"),needsPermission("library.write"),library.post);
|
||||
adminApp.get("/library/flows",needsPermission("library.read"),library.getAll);
|
||||
adminApp.get(new RegExp("/library/flows\/(.*)"),needsPermission("library.read"),library.get);
|
||||
|
||||
// Settings
|
||||
adminApp.get("/settings",needsPermission("settings.read"),info.settings);
|
||||
|
||||
// Error Handler
|
||||
adminApp.use(errorHandler);
|
||||
}
|
||||
|
||||
function start() {
|
||||
return i18n.registerMessageCatalog("editor",path.resolve(path.join(__dirname,"locales")),"editor.json").then(function(){
|
||||
comms.start();
|
||||
});
|
||||
}
|
||||
function stop() {
|
||||
comms.stop();
|
||||
return when.resolve();
|
||||
}
|
||||
module.exports = {
|
||||
init: init
|
||||
init: init,
|
||||
start: start,
|
||||
stop: stop,
|
||||
library: {
|
||||
register: library.register
|
||||
},
|
||||
auth: {
|
||||
needsPermission: auth.needsPermission
|
||||
},
|
||||
comms: {
|
||||
publish: comms.publish
|
||||
},
|
||||
get adminApp() { return adminApp; },
|
||||
get nodeApp() { return nodeApp; },
|
||||
get server() { return server; }
|
||||
};
|
||||
|
@@ -13,28 +13,30 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
var settings = require('../settings');
|
||||
var theme = require("./theme");
|
||||
|
||||
var util = require('util');
|
||||
var settings;
|
||||
|
||||
module.exports = {
|
||||
init: function(runtime) {
|
||||
settings = runtime.settings;
|
||||
},
|
||||
settings: function(req,res) {
|
||||
var safeSettings = {
|
||||
httpNodeRoot: settings.httpNodeRoot,
|
||||
version: settings.version,
|
||||
user: req.user
|
||||
}
|
||||
|
||||
|
||||
var themeSettings = theme.settings();
|
||||
if (themeSettings) {
|
||||
safeSettings.editorTheme = themeSettings;
|
||||
}
|
||||
|
||||
|
||||
if (util.isArray(settings.paletteCategories)) {
|
||||
safeSettings.paletteCategories = settings.paletteCategories;
|
||||
}
|
||||
|
||||
|
||||
res.json(safeSettings);
|
||||
}
|
||||
}
|
||||
|
@@ -15,9 +15,8 @@
|
||||
**/
|
||||
|
||||
var redApp = null;
|
||||
var storage = require("../storage");
|
||||
var log = require("../log");
|
||||
|
||||
var storage;
|
||||
var log;
|
||||
var needsPermission = require("./auth").needsPermission;
|
||||
|
||||
function createLibrary(type) {
|
||||
@@ -70,8 +69,10 @@ function createLibrary(type) {
|
||||
}
|
||||
}
|
||||
module.exports = {
|
||||
init: function(app) {
|
||||
init: function(app,runtime) {
|
||||
redApp = app;
|
||||
log = runtime.log;
|
||||
storage = runtime.storage;
|
||||
},
|
||||
register: createLibrary,
|
||||
|
||||
@@ -90,7 +91,7 @@ module.exports = {
|
||||
}).otherwise(function(err) {
|
||||
if (err) {
|
||||
log.warn(log._("api.library.error-load-flow",{path:req.params[0],message:err.toString()}));
|
||||
if (err.code === 'forbidden') {
|
||||
if (err.code === 'forbidden') {
|
||||
log.audit({event: "library.get",type:"flow",path:req.params[0],error:"forbidden"},req);
|
||||
res.status(403).end();
|
||||
return;
|
||||
|
@@ -13,18 +13,52 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
var i18n = require("../i18n");
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var i18n;
|
||||
var supportedLangs = [];
|
||||
|
||||
var apiLocalDir = path.resolve(path.join(__dirname,"locales"));
|
||||
|
||||
var initSupportedLangs = function() {
|
||||
fs.readdir(apiLocalDir, function(err,files) {
|
||||
if(!err) {
|
||||
supportedLangs = files;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function determineLangFromHeaders(acceptedLanguages){
|
||||
var lang = i18n.defaultLang;
|
||||
acceptedLanguages = acceptedLanguages || [];
|
||||
for (var i=0;i<acceptedLanguages.length;i++){
|
||||
if (supportedLangs.indexOf(acceptedLanguages[i]) !== -1){
|
||||
lang = acceptedLanguages[i];
|
||||
break;
|
||||
// check the language without the country code
|
||||
} else if (supportedLangs.indexOf(acceptedLanguages[i].split("-")[0]) !== -1) {
|
||||
lang = acceptedLanguages[i].split("-")[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return lang;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: function(runtime) {
|
||||
i18n = runtime.i18n;
|
||||
initSupportedLangs();
|
||||
},
|
||||
get: function(req,res) {
|
||||
var namespace = req.params[0];
|
||||
namespace = namespace.replace(/\.json$/,"");
|
||||
var lang = i18n.determineLangFromHeaders(req.acceptsLanguages() || []);
|
||||
var lang = determineLangFromHeaders(req.acceptsLanguages() || []);
|
||||
var prevLang = i18n.i.lng();
|
||||
i18n.i.setLng(lang, function(){
|
||||
var catalog = i18n.catalog(namespace,lang);
|
||||
res.json(catalog||{});
|
||||
});
|
||||
i18n.i.setLng(prevLang);
|
||||
}
|
||||
},
|
||||
determineLangFromHeaders: determineLangFromHeaders
|
||||
}
|
||||
|
@@ -9,20 +9,24 @@
|
||||
}
|
||||
},
|
||||
"workspace": {
|
||||
"defaultName": "Sheet __number__",
|
||||
"renameSheet": "Rename sheet",
|
||||
"defaultName": "Flow __number__",
|
||||
"renameSheet": "Rename flow",
|
||||
"confirmDelete": "Confirm delete",
|
||||
"delete": "Are you sure you want to delete '__label__'?",
|
||||
"dropFlowHere": "Drop the flow here"
|
||||
},
|
||||
"menu": {
|
||||
"label": {
|
||||
"sidebar": {
|
||||
"sidebar": "Sidebar",
|
||||
"show": "Toggle Sidebar"
|
||||
"view": {
|
||||
"view": "View",
|
||||
"showGrid": "Show grid",
|
||||
"snapGrid": "Snap to grid"
|
||||
},
|
||||
"displayStatus": "Display Node Status",
|
||||
"displayConfig": "Configuration Nodes",
|
||||
"sidebar": {
|
||||
"show": "Show sidebar"
|
||||
},
|
||||
"displayStatus": "Show node status",
|
||||
"displayConfig": "Configuration nodes",
|
||||
"import": "Import",
|
||||
"export": "Export",
|
||||
"clipboard": "Clipboard",
|
||||
@@ -30,7 +34,7 @@
|
||||
"subflows": "Subflows",
|
||||
"createSubflow": "Create Subflow",
|
||||
"selectionToSubflow": "Selection to Subflow",
|
||||
"flows": "Tabs",
|
||||
"flows": "Flows",
|
||||
"add": "Add",
|
||||
"rename": "Rename",
|
||||
"delete": "Delete",
|
||||
@@ -78,7 +82,9 @@
|
||||
"modifiedFlowsDesc": "Only deploys flows that contain changed nodes",
|
||||
"modifiedNodes": "Modified Nodes",
|
||||
"modifiedNodesDesc": "Only deploys nodes that have changed",
|
||||
"successfulDeploy": "Successfully Deployed",
|
||||
"successfulDeploy": "Successfully deployed",
|
||||
"unusedConfigNodes":"You have some unused configuration nodes.",
|
||||
"unusedConfigNodesLink":"Click here to see them",
|
||||
"errors": {
|
||||
"noResponse": "no response from server"
|
||||
},
|
||||
@@ -90,7 +96,6 @@
|
||||
"undeployedChanges": "You have undeployed changes.\n\nLeaving this page will lose these changes.",
|
||||
"improperlyConfigured": "The workspace contains some nodes that are not properly configured:",
|
||||
"unknown": "The workspace contains some unknown node types:",
|
||||
"unusedConfig": "The workspace contains some unused configuration nodes:",
|
||||
"confirm": "Are you sure you want to deploy?"
|
||||
}
|
||||
},
|
||||
@@ -197,11 +202,22 @@
|
||||
"config": {
|
||||
"name": "Configuration nodes",
|
||||
"label": "config",
|
||||
"local": "on this flow",
|
||||
"global": "on all flows",
|
||||
"global": "Global",
|
||||
"none": "none",
|
||||
"subflows": "subflows",
|
||||
"flows": "flows"
|
||||
"flows": "flows",
|
||||
"filterUnused":"unused",
|
||||
"filterAll":"all",
|
||||
"filtered": "__count__ hidden"
|
||||
}
|
||||
},
|
||||
"typedInput": {
|
||||
"type": {
|
||||
"str": "string",
|
||||
"num": "number",
|
||||
"re": "regular expression",
|
||||
"bool": "boolean",
|
||||
"json": "JSON"
|
||||
}
|
||||
}
|
||||
}
|
@@ -13,22 +13,28 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
var redNodes = require("../nodes");
|
||||
var comms = require("../comms");
|
||||
var log = require("../log");
|
||||
var i18n = require("../i18n");
|
||||
|
||||
var when = require("when");
|
||||
|
||||
var settings = require("../settings");
|
||||
var comms = require("./comms");
|
||||
var locales = require("./locales");
|
||||
var redNodes;
|
||||
var log;
|
||||
var i18n;
|
||||
var settings;
|
||||
|
||||
module.exports = {
|
||||
init: function(runtime) {
|
||||
redNodes = runtime.nodes;
|
||||
log = runtime.log;
|
||||
i18n = runtime.i18n;
|
||||
settings = runtime.settings;
|
||||
},
|
||||
getAll: function(req,res) {
|
||||
if (req.get("accept") == "application/json") {
|
||||
log.audit({event: "nodes.list.get"},req);
|
||||
res.json(redNodes.getNodeList());
|
||||
} else {
|
||||
var lang = i18n.determineLangFromHeaders(req.acceptsLanguages());
|
||||
var lang = locales.determineLangFromHeaders(req.acceptsLanguages());
|
||||
log.audit({event: "nodes.configs.get"},req);
|
||||
res.send(redNodes.getNodeConfigs(lang));
|
||||
}
|
||||
@@ -121,7 +127,7 @@ module.exports = {
|
||||
res.status(404).end();
|
||||
}
|
||||
} else {
|
||||
var lang = i18n.determineLangFromHeaders(req.acceptsLanguages());
|
||||
var lang = locales.determineLangFromHeaders(req.acceptsLanguages());
|
||||
result = redNodes.getNodeConfig(id,lang);
|
||||
if (result) {
|
||||
log.audit({event: "nodes.config.get",id:id},req);
|
||||
@@ -157,8 +163,8 @@ module.exports = {
|
||||
res.status(400).json({error:"invalid_request", message:"Invalid request"});
|
||||
return;
|
||||
}
|
||||
var id = req.params.mod + "/" + req.params.set;
|
||||
try {
|
||||
var id = req.params.mod + "/" + req.params.set;
|
||||
var node = redNodes.getNodeInfo(id);
|
||||
var info;
|
||||
if (!node) {
|
||||
@@ -189,8 +195,8 @@ module.exports = {
|
||||
res.status(400).json({error:"invalid_request", message:"Invalid request"});
|
||||
return;
|
||||
}
|
||||
var mod = req.params.mod;
|
||||
try {
|
||||
var mod = req.params.mod;
|
||||
var module = redNodes.getModuleInfo(mod);
|
||||
if (!module) {
|
||||
log.audit({event: "nodes.module.set",module:mod,error:"not_found"},req);
|
||||
|
168
red/api/theme.js
168
red/api/theme.js
@@ -34,6 +34,7 @@ var defaultContext = {
|
||||
}
|
||||
};
|
||||
|
||||
var theme = null;
|
||||
var themeContext = clone(defaultContext);
|
||||
var themeSettings = null;
|
||||
|
||||
@@ -53,102 +54,107 @@ function serveFile(app,baseUrl,file) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: function(settings) {
|
||||
init: function(runtime) {
|
||||
var settings = runtime.settings;
|
||||
themeContext = clone(defaultContext);
|
||||
if (runtime.version) {
|
||||
themeContext.version = runtime.version();
|
||||
}
|
||||
themeSettings = null;
|
||||
theme = settings.editorTheme;
|
||||
|
||||
},
|
||||
|
||||
app: function() {
|
||||
var i;
|
||||
var url;
|
||||
themeContext = clone(defaultContext);
|
||||
themeSettings = null;
|
||||
themeSettings = {};
|
||||
|
||||
if (settings.editorTheme) {
|
||||
var theme = settings.editorTheme;
|
||||
themeSettings = {};
|
||||
var themeApp = express();
|
||||
|
||||
var themeApp = express();
|
||||
|
||||
if (theme.page) {
|
||||
if (theme.page.css) {
|
||||
var styles = theme.page.css;
|
||||
if (!util.isArray(styles)) {
|
||||
styles = [styles];
|
||||
}
|
||||
themeContext.page.css = [];
|
||||
|
||||
for (i=0;i<styles.length;i++) {
|
||||
url = serveFile(themeApp,"/css/",styles[i]);
|
||||
if (url) {
|
||||
themeContext.page.css.push(url);
|
||||
}
|
||||
}
|
||||
if (theme.page) {
|
||||
if (theme.page.css) {
|
||||
var styles = theme.page.css;
|
||||
if (!util.isArray(styles)) {
|
||||
styles = [styles];
|
||||
}
|
||||
themeContext.page.css = [];
|
||||
|
||||
if (theme.page.favicon) {
|
||||
url = serveFile(themeApp,"/favicon/",theme.page.favicon)
|
||||
for (i=0;i<styles.length;i++) {
|
||||
url = serveFile(themeApp,"/css/",styles[i]);
|
||||
if (url) {
|
||||
themeContext.page.favicon = url;
|
||||
}
|
||||
}
|
||||
|
||||
themeContext.page.title = theme.page.title || themeContext.page.title;
|
||||
}
|
||||
|
||||
if (theme.header) {
|
||||
|
||||
themeContext.header.title = theme.header.title || themeContext.header.title;
|
||||
|
||||
if (theme.header.hasOwnProperty("url")) {
|
||||
themeContext.header.url = theme.header.url;
|
||||
}
|
||||
|
||||
if (theme.header.hasOwnProperty("image")) {
|
||||
if (theme.header.image) {
|
||||
url = serveFile(themeApp,"/header/",theme.header.image);
|
||||
if (url) {
|
||||
themeContext.header.image = url;
|
||||
}
|
||||
} else {
|
||||
themeContext.header.image = null;
|
||||
themeContext.page.css.push(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (theme.deployButton) {
|
||||
if (theme.deployButton.type == "simple") {
|
||||
themeSettings.deployButton = {
|
||||
type: "simple"
|
||||
}
|
||||
if (theme.deployButton.label) {
|
||||
themeSettings.deployButton.label = theme.deployButton.label;
|
||||
}
|
||||
if (theme.deployButton.icon) {
|
||||
url = serveFile(themeApp,"/deploy/",theme.deployButton.icon);
|
||||
if (url) {
|
||||
themeSettings.deployButton.icon = url;
|
||||
}
|
||||
}
|
||||
if (theme.page.favicon) {
|
||||
url = serveFile(themeApp,"/favicon/",theme.page.favicon)
|
||||
if (url) {
|
||||
themeContext.page.favicon = url;
|
||||
}
|
||||
}
|
||||
|
||||
if (theme.hasOwnProperty("userMenu")) {
|
||||
themeSettings.userMenu = theme.userMenu;
|
||||
}
|
||||
|
||||
if (theme.login) {
|
||||
if (theme.login.image) {
|
||||
url = serveFile(themeApp,"/login/",theme.login.image);
|
||||
if (url) {
|
||||
themeContext.login = {
|
||||
image: url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (theme.hasOwnProperty("menu")) {
|
||||
themeSettings.menu = theme.menu;
|
||||
}
|
||||
|
||||
return themeApp;
|
||||
themeContext.page.title = theme.page.title || themeContext.page.title;
|
||||
}
|
||||
|
||||
if (theme.header) {
|
||||
|
||||
themeContext.header.title = theme.header.title || themeContext.header.title;
|
||||
|
||||
if (theme.header.hasOwnProperty("url")) {
|
||||
themeContext.header.url = theme.header.url;
|
||||
}
|
||||
|
||||
if (theme.header.hasOwnProperty("image")) {
|
||||
if (theme.header.image) {
|
||||
url = serveFile(themeApp,"/header/",theme.header.image);
|
||||
if (url) {
|
||||
themeContext.header.image = url;
|
||||
}
|
||||
} else {
|
||||
themeContext.header.image = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (theme.deployButton) {
|
||||
if (theme.deployButton.type == "simple") {
|
||||
themeSettings.deployButton = {
|
||||
type: "simple"
|
||||
}
|
||||
if (theme.deployButton.label) {
|
||||
themeSettings.deployButton.label = theme.deployButton.label;
|
||||
}
|
||||
if (theme.deployButton.icon) {
|
||||
url = serveFile(themeApp,"/deploy/",theme.deployButton.icon);
|
||||
if (url) {
|
||||
themeSettings.deployButton.icon = url;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (theme.hasOwnProperty("userMenu")) {
|
||||
themeSettings.userMenu = theme.userMenu;
|
||||
}
|
||||
|
||||
if (theme.login) {
|
||||
if (theme.login.image) {
|
||||
url = serveFile(themeApp,"/login/",theme.login.image);
|
||||
if (url) {
|
||||
themeContext.login = {
|
||||
image: url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (theme.hasOwnProperty("menu")) {
|
||||
themeSettings.menu = theme.menu;
|
||||
}
|
||||
|
||||
return themeApp;
|
||||
},
|
||||
context: function() {
|
||||
return themeContext;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013, 2014 IBM Corp.
|
||||
* Copyright 2013, 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,26 +21,26 @@ var theme = require("./theme");
|
||||
|
||||
var Mustache = require("mustache");
|
||||
|
||||
var events = require("../events");
|
||||
var settings;
|
||||
|
||||
var icon_paths = [path.resolve(__dirname + '/../../public/icons')];
|
||||
var iconCache = {};
|
||||
//TODO: create a default icon
|
||||
var defaultIcon = path.resolve(__dirname + '/../../public/icons/arrow-in.png');
|
||||
|
||||
events.on("node-icon-dir",function(dir) {
|
||||
icon_paths.push(path.resolve(dir));
|
||||
});
|
||||
|
||||
var templateDir = path.resolve(__dirname+"/../../editor/templates");
|
||||
var editorTemplate;
|
||||
|
||||
function nodeIconDir(dir) {
|
||||
icon_paths.push(path.resolve(dir));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: function(_settings) {
|
||||
settings = _settings;
|
||||
init: function(runtime) {
|
||||
editorTemplate = fs.readFileSync(path.join(templateDir,"index.mst"),"utf8");
|
||||
Mustache.parse(editorTemplate);
|
||||
// TODO: this allows init to be called multiple times without
|
||||
// registering multiple instances of the listener.
|
||||
// It isn't.... ideal.
|
||||
runtime.events.removeListener("node-icon-dir",nodeIconDir);
|
||||
runtime.events.on("node-icon-dir",nodeIconDir);
|
||||
},
|
||||
|
||||
ensureSlash: function(req,res,next) {
|
||||
@@ -59,10 +59,13 @@ module.exports = {
|
||||
} else {
|
||||
for (var p=0;p<icon_paths.length;p++) {
|
||||
var iconPath = path.join(icon_paths[p],req.params.icon);
|
||||
if (fs.existsSync(iconPath)) {
|
||||
try {
|
||||
fs.statSync(iconPath);
|
||||
res.sendFile(iconPath);
|
||||
iconCache[req.params.icon] = iconPath;
|
||||
return;
|
||||
} catch(err) {
|
||||
// iconPath doesn't exist
|
||||
}
|
||||
}
|
||||
res.sendFile(defaultIcon);
|
||||
|
105
red/red.js
105
red/red.js
@@ -14,22 +14,19 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var server = require("./server");
|
||||
var nodes = require("./nodes");
|
||||
var library = require("./api/library");
|
||||
var comms = require("./comms");
|
||||
var log = require("./log");
|
||||
var util = require("./util");
|
||||
var i18n = require("./i18n");
|
||||
var fs = require("fs");
|
||||
var settings = require("./settings");
|
||||
var credentials = require("./nodes/credentials");
|
||||
var auth = require("./api/auth");
|
||||
var path = require('path');
|
||||
var events = require("events");
|
||||
|
||||
var runtime = require("./runtime");
|
||||
var api = require("./api");
|
||||
|
||||
process.env.NODE_RED_HOME = process.env.NODE_RED_HOME || path.resolve(__dirname+"/..");
|
||||
|
||||
var nodeApp = null;
|
||||
var adminApp = null;
|
||||
var server = null;
|
||||
var apiEnabled = false;
|
||||
|
||||
function checkBuild() {
|
||||
var editorFile = path.resolve(path.join(__dirname,"..","public","red","red.min.js"));
|
||||
try {
|
||||
@@ -41,42 +38,60 @@ function checkBuild() {
|
||||
}
|
||||
}
|
||||
|
||||
var RED = {
|
||||
module.exports = {
|
||||
init: function(httpServer,userSettings) {
|
||||
if (!userSettings) {
|
||||
userSettings = httpServer;
|
||||
httpServer = null;
|
||||
}
|
||||
|
||||
if (!userSettings.SKIP_BUILD_CHECK) {
|
||||
checkBuild();
|
||||
}
|
||||
userSettings.version = this.version();
|
||||
log.init(userSettings);
|
||||
settings.init(userSettings);
|
||||
server.init(httpServer,settings);
|
||||
return server.app;
|
||||
},
|
||||
start: server.start,
|
||||
stop: server.stop,
|
||||
nodes: nodes,
|
||||
library: { register: library.register },
|
||||
credentials: credentials,
|
||||
events: events,
|
||||
log: log,
|
||||
comms: comms,
|
||||
settings:settings,
|
||||
util: util,
|
||||
auth: {
|
||||
needsPermission: auth.needsPermission
|
||||
},
|
||||
version: function () {
|
||||
var p = require(path.join(process.env.NODE_RED_HOME,"package.json")).version;
|
||||
/* istanbul ignore else */
|
||||
if (fs.existsSync(path.join(process.env.NODE_RED_HOME,".git"))) {
|
||||
p += "-git";
|
||||
}
|
||||
return p;
|
||||
},
|
||||
get app() { console.log("Deprecated use of RED.app - use RED.httpAdmin instead"); return server.app },
|
||||
get httpAdmin() { return server.app },
|
||||
get httpNode() { return server.nodeApp },
|
||||
get server() { return server.server }
|
||||
};
|
||||
|
||||
module.exports = RED;
|
||||
if (!userSettings.coreNodesDir) {
|
||||
userSettings.coreNodesDir = path.resolve(path.join(__dirname,"..","nodes"));
|
||||
}
|
||||
|
||||
if (userSettings.httpAdminRoot !== false || userSettings.httpNodeRoot !== false) {
|
||||
runtime.init(userSettings,api);
|
||||
api.init(httpServer,runtime);
|
||||
apiEnabled = true;
|
||||
} else {
|
||||
runtime.init(userSettings);
|
||||
apiEnabled = false;
|
||||
}
|
||||
adminApp = runtime.adminApi.adminApp;
|
||||
nodeApp = runtime.adminApi.nodeApp;
|
||||
server = runtime.adminApi.server;
|
||||
return;
|
||||
},
|
||||
start: function() {
|
||||
return runtime.start().then(function() {
|
||||
if (apiEnabled) {
|
||||
return api.start();
|
||||
}
|
||||
});
|
||||
},
|
||||
stop: function() {
|
||||
return runtime.stop().then(function() {
|
||||
if (apiEnabled) {
|
||||
return api.stop();
|
||||
}
|
||||
})
|
||||
},
|
||||
nodes: runtime.nodes,
|
||||
log: runtime.log,
|
||||
settings:runtime.settings,
|
||||
util: runtime.util,
|
||||
version: runtime.version,
|
||||
|
||||
comms: api.comms,
|
||||
library: api.library,
|
||||
auth: api.auth,
|
||||
|
||||
get app() { console.log("Deprecated use of RED.app - use RED.httpAdmin instead"); return runtime.app },
|
||||
get httpAdmin() { return adminApp },
|
||||
get httpNode() { return nodeApp },
|
||||
get server() { return server }
|
||||
};
|
||||
|
@@ -20,19 +20,9 @@ var path = require("path");
|
||||
var fs = require("fs");
|
||||
|
||||
var defaultLang = "en-US";
|
||||
var supportedLangs = [];
|
||||
|
||||
var resourceMap = {
|
||||
"runtime": {
|
||||
basedir: path.resolve(__dirname+"/../locales"),
|
||||
file:"runtime.json"
|
||||
},
|
||||
"editor": {
|
||||
basedir: path.resolve(__dirname+"/../locales"),
|
||||
file: "editor.json"
|
||||
}
|
||||
}
|
||||
var resourceCache = {}
|
||||
var resourceMap = {};
|
||||
var resourceCache = {};
|
||||
|
||||
function registerMessageCatalog(namespace,dir,file) {
|
||||
return when.promise(function(resolve,reject) {
|
||||
@@ -43,19 +33,6 @@ function registerMessageCatalog(namespace,dir,file) {
|
||||
});
|
||||
}
|
||||
|
||||
var initSupportedLangs = function() {
|
||||
return when.promise(function(resolve,reject) {
|
||||
fs.readdir(resourceMap.editor.basedir, function(err,files) {
|
||||
if(err) {
|
||||
reject(err);
|
||||
} else {
|
||||
supportedLangs = files;
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function mergeCatalog(fallback,catalog) {
|
||||
for (var k in fallback) {
|
||||
if (fallback.hasOwnProperty(k)) {
|
||||
@@ -102,21 +79,17 @@ function init() {
|
||||
i18n.backend(MessageFileLoader);
|
||||
i18n.init({
|
||||
ns: {
|
||||
namespaces: ["runtime","editor"],
|
||||
namespaces: [],
|
||||
defaultNs: "runtime"
|
||||
},
|
||||
fallbackLng: ['en-US']
|
||||
fallbackLng: [defaultLang]
|
||||
},function() {
|
||||
initSupportedLangs().then(function() {
|
||||
resolve();
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getCatalog(namespace,lang) {
|
||||
//console.log("+",namespace,lang);
|
||||
//console.log(resourceCache[namespace][lang]);
|
||||
var result = null;
|
||||
if (resourceCache.hasOwnProperty(namespace)) {
|
||||
result = resourceCache[namespace][lang];
|
||||
@@ -130,32 +103,15 @@ function getCatalog(namespace,lang) {
|
||||
}
|
||||
}
|
||||
}
|
||||
//console.log(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
function determineLangFromHeaders(acceptedLanguages){
|
||||
var lang = "en-US";
|
||||
acceptedLanguages = acceptedLanguages || [];
|
||||
for (var i=0;i<acceptedLanguages.length;i++){
|
||||
if (supportedLangs.indexOf(acceptedLanguages[i]) !== -1){
|
||||
lang = acceptedLanguages[i];
|
||||
break;
|
||||
// check the language without the country code
|
||||
} else if (supportedLangs.indexOf(acceptedLanguages[i].split("-")[0]) !== -1) {
|
||||
lang = acceptedLanguages[i].split("-")[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return lang;
|
||||
}
|
||||
|
||||
var obj = module.exports = {
|
||||
init: init,
|
||||
registerMessageCatalog: registerMessageCatalog,
|
||||
catalog: getCatalog,
|
||||
i: i18n,
|
||||
determineLangFromHeaders: determineLangFromHeaders
|
||||
defaultLang:defaultLang
|
||||
}
|
||||
|
||||
obj['_'] = function() {
|
@@ -14,41 +14,75 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var express = require('express');
|
||||
var when = require('when');
|
||||
|
||||
var redNodes = require("./nodes");
|
||||
var comms = require("./comms");
|
||||
var storage = require("./storage");
|
||||
var log = require("./log");
|
||||
var i18n = require("./i18n");
|
||||
|
||||
var app = null;
|
||||
var nodeApp = null;
|
||||
var server = null;
|
||||
var settings = null;
|
||||
var events = require("./events");
|
||||
var settings = require("./settings");
|
||||
var path = require('path');
|
||||
var fs = require("fs");
|
||||
|
||||
var runtimeMetricInterval = null;
|
||||
|
||||
var stubbedExpressApp = {
|
||||
get: function() {},
|
||||
post: function() {},
|
||||
put: function() {},
|
||||
delete: function(){}
|
||||
}
|
||||
var adminApi = {
|
||||
library: {
|
||||
register: function(){}
|
||||
},
|
||||
auth: {
|
||||
needsPermission: function(){}
|
||||
},
|
||||
comms: {
|
||||
publish: function(){}
|
||||
},
|
||||
adminApp: stubbedExpressApp,
|
||||
nodeApp: stubbedExpressApp,
|
||||
server: {}
|
||||
}
|
||||
|
||||
function init(_server,_settings) {
|
||||
server = _server;
|
||||
settings = _settings;
|
||||
function init(userSettings,_adminApi) {
|
||||
userSettings.version = getVersion();
|
||||
log.init(userSettings);
|
||||
settings.init(userSettings);
|
||||
if (_adminApi) {
|
||||
adminApi = _adminApi;
|
||||
}
|
||||
redNodes.init(runtime);
|
||||
|
||||
comms.init(_server,_settings);
|
||||
}
|
||||
|
||||
nodeApp = express();
|
||||
app = express();
|
||||
var version;
|
||||
|
||||
function getVersion() {
|
||||
if (!version) {
|
||||
version = require(path.join(__dirname,"..","..","package.json")).version;
|
||||
/* istanbul ignore else */
|
||||
try {
|
||||
fs.statSync(path.join(__dirname,"..","..",".git"));
|
||||
version += "-git";
|
||||
} catch(err) {
|
||||
// No git directory
|
||||
}
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
function start() {
|
||||
return i18n.init()
|
||||
.then(function() {
|
||||
return i18n.registerMessageCatalog("runtime",path.resolve(path.join(__dirname,"locales")),"runtime.json")
|
||||
})
|
||||
.then(function() { return storage.init(settings)})
|
||||
.then(function() { return settings.load(storage)})
|
||||
.then(function() {
|
||||
if (settings.httpAdminRoot !== false) {
|
||||
require("./api").init(app,storage);
|
||||
}
|
||||
|
||||
if (log.metric()) {
|
||||
runtimeMetricInterval = setInterval(function() {
|
||||
@@ -61,7 +95,6 @@ function start() {
|
||||
}
|
||||
log.info(log._("runtime.version",{component:"Node.js ",version:process.version}));
|
||||
log.info(log._("server.loading"));
|
||||
redNodes.init(settings,storage);
|
||||
return redNodes.load().then(function() {
|
||||
|
||||
var i;
|
||||
@@ -103,9 +136,10 @@ function start() {
|
||||
redNodes.cleanModuleList();
|
||||
}
|
||||
}
|
||||
log.info(log._("runtime.paths.settings",{path:settings.settingsFile}));
|
||||
if (settings.settingsFile) {
|
||||
log.info(log._("runtime.paths.settings",{path:settings.settingsFile}));
|
||||
}
|
||||
redNodes.loadFlows().then(redNodes.startFlows);
|
||||
comms.start();
|
||||
}).otherwise(function(err) {
|
||||
console.log(err);
|
||||
});
|
||||
@@ -137,16 +171,22 @@ function stop() {
|
||||
clearInterval(runtimeMetricInterval);
|
||||
runtimeMetricInterval = null;
|
||||
}
|
||||
redNodes.stopFlows();
|
||||
comms.stop();
|
||||
return redNodes.stopFlows();
|
||||
}
|
||||
|
||||
var serverAPI = module.exports = {
|
||||
var runtime = module.exports = {
|
||||
init: init,
|
||||
start: start,
|
||||
stop: stop,
|
||||
|
||||
get app() { return app },
|
||||
get nodeApp() { return nodeApp },
|
||||
get server() { return server }
|
||||
version: getVersion,
|
||||
|
||||
log: log,
|
||||
i18n: i18n,
|
||||
settings: settings,
|
||||
storage: storage,
|
||||
events: events,
|
||||
nodes: redNodes,
|
||||
util: require("./util"),
|
||||
get adminApi() { return adminApi }
|
||||
}
|
@@ -41,7 +41,8 @@
|
||||
|
||||
"api": {
|
||||
"flows": {
|
||||
"error-save": "Error saving flows: __message__"
|
||||
"error-save": "Error saving flows: __message__",
|
||||
"error-reload": "Error reloading flows: __message__"
|
||||
},
|
||||
"library": {
|
||||
"error-load-entry": "Error loading library entry '__path__': __message__",
|
||||
@@ -88,6 +89,9 @@
|
||||
"stopped-modified-flows": "Stopped modified flows",
|
||||
"stopped-flows": "Stopped flows",
|
||||
"stopped": "Stopped",
|
||||
"added-flow": "Adding flow: __label__",
|
||||
"updated-flow": "Updated flow: __label__",
|
||||
"removed-flow": "Removed flow: __label__",
|
||||
"missing-types": "Waiting for missing types to be registered:",
|
||||
"missing-type-provided": " - __type__ (provided by npm module __module__)",
|
||||
"missing-type-install-1": "To install any of these missing modules, run:",
|
@@ -50,9 +50,9 @@ var LogHandler = function(settings) {
|
||||
this.logLevel = settings ? levels[settings.level]||levels.info : levels.info;
|
||||
this.metricsOn = settings ? settings.metrics||false : false;
|
||||
this.auditOn = settings ? settings.audit||false : false;
|
||||
|
||||
|
||||
metricsEnabled = metricsEnabled || this.metricsOn;
|
||||
|
||||
|
||||
this.handler = (settings && settings.handler) ? settings.handler(settings) : consoleLogger;
|
||||
this.on("log",function(msg) {
|
||||
if (this.shouldReportMessage(msg.level)) {
|
||||
@@ -134,7 +134,7 @@ var log = module.exports = {
|
||||
metric: function() {
|
||||
return metricsEnabled;
|
||||
},
|
||||
|
||||
|
||||
audit: function(msg,req) {
|
||||
msg.level = log.AUDIT;
|
||||
if (req) {
|
@@ -20,9 +20,8 @@ var when = require("when");
|
||||
|
||||
var redUtil = require("../util");
|
||||
var Log = require("../log");
|
||||
|
||||
var context = require("./context");
|
||||
var flows = require("./flows");
|
||||
var comms = require("../comms");
|
||||
|
||||
function Node(n) {
|
||||
this.id = n.id;
|
||||
@@ -64,6 +63,12 @@ Node.prototype.updateWires = function(wires) {
|
||||
}
|
||||
|
||||
}
|
||||
Node.prototype.context = function() {
|
||||
if (!this._context) {
|
||||
this._context = context.get(this._alias||this.id,this.z);
|
||||
}
|
||||
return this._context;
|
||||
}
|
||||
|
||||
Node.prototype._on = Node.prototype.on;
|
||||
|
||||
@@ -250,7 +255,6 @@ Node.prototype.metric = function(eventname, msg, metricValue) {
|
||||
* status: { fill:"red|green", shape:"dot|ring", text:"blah" }
|
||||
*/
|
||||
Node.prototype.status = function(status) {
|
||||
comms.publish("status/" + this.id, status, true);
|
||||
flows.handleStatus(this,status);
|
||||
};
|
||||
module.exports = Node;
|
80
red/runtime/nodes/context.js
Normal file
80
red/runtime/nodes/context.js
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Copyright 2015 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var clone = require("clone");
|
||||
var when = require("when");
|
||||
var util = require("../util");
|
||||
|
||||
function createContext(id,seed) {
|
||||
var data = seed || {};
|
||||
var obj = seed || {};
|
||||
obj.get = function get(key) {
|
||||
return util.getMessageProperty(data,key);
|
||||
};
|
||||
obj.set = function set(key, value) {
|
||||
util.setMessageProperty(data,key,value);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
var contexts = {};
|
||||
var globalContext = null;
|
||||
|
||||
function getContext(localId,flowId) {
|
||||
var contextId = localId;
|
||||
if (flowId) {
|
||||
contextId = localId+":"+flowId;
|
||||
}
|
||||
if (contexts[contextId]) {
|
||||
return contexts[contextId];
|
||||
}
|
||||
var newContext = createContext(contextId);
|
||||
if (flowId) {
|
||||
newContext.flow = getContext(flowId);
|
||||
if (globalContext) {
|
||||
newContext.global = globalContext;
|
||||
}
|
||||
}
|
||||
contexts[contextId] = newContext;
|
||||
return newContext;
|
||||
}
|
||||
function deleteContext(id,flowId) {
|
||||
var contextId = id;
|
||||
if (flowId) {
|
||||
contextId = id+":"+flowId;
|
||||
}
|
||||
delete contexts[contextId];
|
||||
}
|
||||
function clean(flowConfig) {
|
||||
var activeIds = {};
|
||||
var contextId;
|
||||
var node;
|
||||
for (var id in contexts) {
|
||||
if (contexts.hasOwnProperty(id)) {
|
||||
var idParts = id.split(":");
|
||||
if (!flowConfig.allNodes[idParts[0]]) {
|
||||
delete contexts[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = {
|
||||
init: function(settings) {
|
||||
globalContext = createContext("global",settings.functionGlobalContext || {});
|
||||
},
|
||||
get: getContext,
|
||||
clean:clean
|
||||
};
|
@@ -18,8 +18,6 @@ var when = require("when");
|
||||
|
||||
var log = require("../log");
|
||||
|
||||
var needsPermission = require("../api/auth").needsPermission;
|
||||
|
||||
var credentialCache = {};
|
||||
var storage = null;
|
||||
var credentialsDef = {};
|
||||
@@ -123,6 +121,7 @@ module.exports = {
|
||||
var nodeType = node.type;
|
||||
var newCreds = node.credentials;
|
||||
if (newCreds) {
|
||||
delete node.credentials;
|
||||
var savedCredentials = credentialCache[nodeID] || {};
|
||||
var dashedType = nodeType.replace(/\s+/g, '-');
|
||||
var definition = credentialsDef[dashedType];
|
||||
@@ -147,7 +146,6 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
credentialCache[nodeID] = savedCredentials;
|
||||
delete node.credentials;
|
||||
}
|
||||
},
|
||||
|
@@ -32,6 +32,7 @@ function Flow(global,flow) {
|
||||
|
||||
this.start = function(diff) {
|
||||
var node;
|
||||
var newNode;
|
||||
var id;
|
||||
catchNodeMap = {};
|
||||
statusNodeMap = {};
|
||||
@@ -39,11 +40,14 @@ function Flow(global,flow) {
|
||||
if (flow.configs.hasOwnProperty(id)) {
|
||||
node = flow.configs[id];
|
||||
if (!activeNodes[id]) {
|
||||
activeNodes[id] = createNode(node.type,node);
|
||||
newNode = createNode(node.type,node);
|
||||
if (newNode) {
|
||||
activeNodes[id] = newNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (diff) {
|
||||
if (diff && diff.rewired) {
|
||||
for (var j=0;j<diff.rewired.length;j++) {
|
||||
var rewireNode = activeNodes[diff.rewired[j]];
|
||||
if (rewireNode) {
|
||||
@@ -57,7 +61,10 @@ function Flow(global,flow) {
|
||||
node = flow.nodes[id];
|
||||
if (!node.subflow) {
|
||||
if (!activeNodes[id]) {
|
||||
activeNodes[id] = createNode(node.type,node);
|
||||
newNode = createNode(node.type,node);
|
||||
if (newNode) {
|
||||
activeNodes[id] = newNode;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!subflowInstanceNodes[id]) {
|
||||
@@ -65,7 +72,9 @@ function Flow(global,flow) {
|
||||
var nodes = createSubflow(flow.subflows[node.subflow]||global.subflows[node.subflow],node,flow.subflows,global.subflows,activeNodes);
|
||||
subflowInstanceNodes[id] = nodes.map(function(n) { return n.id});
|
||||
for (var i=0;i<nodes.length;i++) {
|
||||
activeNodes[nodes[i].id] = nodes[i];
|
||||
if (nodes[i]) {
|
||||
activeNodes[nodes[i].id] = nodes[i];
|
||||
}
|
||||
}
|
||||
} catch(err) {
|
||||
console.log(err.stack)
|
||||
@@ -227,11 +236,6 @@ function Flow(global,flow) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
function getID() {
|
||||
return (1+Math.random()*4294967295).toString(16);
|
||||
}
|
||||
|
||||
var EnvVarPropertyRE = /^\$\((\S+)\)$/;
|
||||
|
||||
function mapEnvVarProperties(obj,prop) {
|
||||
@@ -258,7 +262,6 @@ function mapEnvVarProperties(obj,prop) {
|
||||
}
|
||||
|
||||
function createNode(type,config) {
|
||||
// console.log("CREATE",type,config.id);
|
||||
var nn = null;
|
||||
var nt = typeRegistry.get(type);
|
||||
if (nt) {
|
||||
@@ -297,7 +300,7 @@ function createSubflow(sf,sfn,subflows,globalSubflows,activeNodes) {
|
||||
|
||||
var createNodeInSubflow = function(def) {
|
||||
node = clone(def);
|
||||
var nid = getID();
|
||||
var nid = redUtil.generateId();
|
||||
node_map[node.id] = node;
|
||||
node._alias = node.id;
|
||||
node.id = nid;
|
||||
@@ -443,8 +446,10 @@ function createSubflow(sf,sfn,subflows,globalSubflows,activeNodes) {
|
||||
var m = /^subflow:(.+)$/.exec(type);
|
||||
if (!m) {
|
||||
var newNode = createNode(type,node);
|
||||
activeNodes[node.id] = newNode;
|
||||
nodes.push(newNode);
|
||||
if (newNode) {
|
||||
activeNodes[node.id] = newNode;
|
||||
nodes.push(newNode);
|
||||
}
|
||||
} else {
|
||||
var subflowId = m[1];
|
||||
nodes = nodes.concat(createSubflow(subflows[subflowId]||globalSubflows[subflowId],node,subflows,globalSubflows,activeNodes));
|
@@ -20,6 +20,7 @@ var when = require("when");
|
||||
var Flow = require('./Flow');
|
||||
|
||||
var typeRegistry = require("../registry");
|
||||
var context = require("../context")
|
||||
var credentials = require("../credentials");
|
||||
|
||||
var flowUtil = require("./util");
|
||||
@@ -76,7 +77,7 @@ function load() {
|
||||
});
|
||||
}
|
||||
|
||||
function setConfig(_config,type) {
|
||||
function setConfig(_config,type,muteLog) {
|
||||
var config = clone(_config);
|
||||
type = type||"full";
|
||||
|
||||
@@ -115,8 +116,9 @@ function setConfig(_config,type) {
|
||||
activeFlowConfig = newFlowConfig;
|
||||
return credentials.clean(activeConfig).then(function() {
|
||||
if (started) {
|
||||
return stop(type,diff).then(function() {
|
||||
start(type,diff);
|
||||
return stop(type,diff,muteLog).then(function() {
|
||||
context.clean(activeFlowConfig);
|
||||
start(type,diff,muteLog);
|
||||
}).otherwise(function(err) {
|
||||
})
|
||||
}
|
||||
@@ -182,6 +184,10 @@ function delegateStatus(node,statusMessage) {
|
||||
}
|
||||
}
|
||||
function handleStatus(node,statusMessage) {
|
||||
events.emit("node-status",{
|
||||
id: node.id,
|
||||
status:statusMessage
|
||||
});
|
||||
if (node.z) {
|
||||
delegateStatus(node,statusMessage);
|
||||
} else {
|
||||
@@ -195,7 +201,8 @@ function handleStatus(node,statusMessage) {
|
||||
}
|
||||
|
||||
|
||||
function start(type,diff) {
|
||||
function start(type,diff,muteLog) {
|
||||
//dumpActiveNodes();
|
||||
type = type||"full";
|
||||
started = true;
|
||||
var i;
|
||||
@@ -218,23 +225,29 @@ function start(type,diff) {
|
||||
log.info(log._("nodes.flows.missing-type-install-2"));
|
||||
log.info(" "+settings.userDir);
|
||||
}
|
||||
return;
|
||||
return when.resolve();
|
||||
}
|
||||
if (diff) {
|
||||
log.info(log._("nodes.flows.starting-modified-"+type));
|
||||
} else {
|
||||
log.info(log._("nodes.flows.starting-flows"));
|
||||
if (!muteLog) {
|
||||
if (diff) {
|
||||
log.info(log._("nodes.flows.starting-modified-"+type));
|
||||
} else {
|
||||
log.info(log._("nodes.flows.starting-flows"));
|
||||
}
|
||||
}
|
||||
var id;
|
||||
if (!diff) {
|
||||
activeFlows['_GLOBAL_'] = Flow.create(activeFlowConfig);
|
||||
if (!activeFlows['global']) {
|
||||
activeFlows['global'] = Flow.create(activeFlowConfig);
|
||||
}
|
||||
for (id in activeFlowConfig.flows) {
|
||||
if (activeFlowConfig.flows.hasOwnProperty(id)) {
|
||||
activeFlows[id] = Flow.create(activeFlowConfig,activeFlowConfig.flows[id]);
|
||||
if (!activeFlows[id]) {
|
||||
activeFlows[id] = Flow.create(activeFlowConfig,activeFlowConfig.flows[id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
activeFlows['_GLOBAL_'].update(activeFlowConfig,activeFlowConfig);
|
||||
activeFlows['global'].update(activeFlowConfig,activeFlowConfig);
|
||||
for (id in activeFlowConfig.flows) {
|
||||
if (activeFlowConfig.flows.hasOwnProperty(id)) {
|
||||
if (activeFlows[id]) {
|
||||
@@ -261,19 +274,24 @@ function start(type,diff) {
|
||||
}
|
||||
}
|
||||
events.emit("nodes-started");
|
||||
if (diff) {
|
||||
log.info(log._("nodes.flows.started-modified-"+type));
|
||||
} else {
|
||||
log.info(log._("nodes.flows.started-flows"));
|
||||
if (!muteLog) {
|
||||
if (diff) {
|
||||
log.info(log._("nodes.flows.started-modified-"+type));
|
||||
} else {
|
||||
log.info(log._("nodes.flows.started-flows"));
|
||||
}
|
||||
}
|
||||
return when.resolve();
|
||||
}
|
||||
|
||||
function stop(type,diff) {
|
||||
function stop(type,diff,muteLog) {
|
||||
type = type||"full";
|
||||
if (diff) {
|
||||
log.info(log._("nodes.flows.stopping-modified-"+type));
|
||||
} else {
|
||||
log.info(log._("nodes.flows.stopping-flows"));
|
||||
if (!muteLog) {
|
||||
if (diff) {
|
||||
log.info(log._("nodes.flows.stopping-modified-"+type));
|
||||
} else {
|
||||
log.info(log._("nodes.flows.stopping-flows"));
|
||||
}
|
||||
}
|
||||
started = false;
|
||||
var promises = [];
|
||||
@@ -311,10 +329,12 @@ function stop(type,diff) {
|
||||
// can do... so cheat by wiping the map knowing it'll be rebuilt
|
||||
// in start()
|
||||
subflowInstanceNodeMap = {};
|
||||
if (diff) {
|
||||
log.info(log._("nodes.flows.stopped-modified-"+type));
|
||||
} else {
|
||||
log.info(log._("nodes.flows.stopped-flows"));
|
||||
if (!muteLog) {
|
||||
if (diff) {
|
||||
log.info(log._("nodes.flows.stopped-modified-"+type));
|
||||
} else {
|
||||
log.info(log._("nodes.flows.stopped-flows"));
|
||||
}
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
@@ -347,6 +367,208 @@ function checkTypeInUse(id) {
|
||||
}
|
||||
}
|
||||
|
||||
function updateMissingTypes() {
|
||||
var subflowInstanceRE = /^subflow:(.+)$/;
|
||||
activeFlowConfig.missingTypes = [];
|
||||
|
||||
for (var id in activeFlowConfig.allNodes) {
|
||||
if (activeFlowConfig.allNodes.hasOwnProperty(id)) {
|
||||
var node = activeFlowConfig.allNodes[id];
|
||||
if (node.type !== 'tab' && node.type !== 'subflow') {
|
||||
var subflowDetails = subflowInstanceRE.exec(node.type);
|
||||
if ( (subflowDetails && !activeFlowConfig.subflows[subflowDetails[1]]) || (!subflowDetails && !typeRegistry.get(node.type)) ) {
|
||||
if (activeFlowConfig.missingTypes.indexOf(node.type) === -1) {
|
||||
activeFlowConfig.missingTypes.push(node.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addFlow(flow) {
|
||||
var i,node;
|
||||
if (!flow.hasOwnProperty('nodes')) {
|
||||
throw new Error('missing nodes property');
|
||||
}
|
||||
flow.id = redUtil.generateId();
|
||||
|
||||
var nodes = [{
|
||||
type:'tab',
|
||||
label:flow.label,
|
||||
id:flow.id
|
||||
}];
|
||||
|
||||
for (i=0;i<flow.nodes.length;i++) {
|
||||
node = flow.nodes[i];
|
||||
if (activeFlowConfig.allNodes[node.id]) {
|
||||
// TODO nls
|
||||
return when.reject(new Error('duplicate id'));
|
||||
}
|
||||
if (node.type === 'tab' || node.type === 'subflow') {
|
||||
return when.reject(new Error('invalid node type: '+node.type));
|
||||
}
|
||||
node.z = flow.id;
|
||||
nodes.push(node);
|
||||
}
|
||||
if (flow.configs) {
|
||||
for (i=0;i<flow.configs.length;i++) {
|
||||
node = flow.configs[i];
|
||||
if (activeFlowConfig.allNodes[node.id]) {
|
||||
// TODO nls
|
||||
return when.reject(new Error('duplicate id'));
|
||||
}
|
||||
if (node.type === 'tab' || node.type === 'subflow') {
|
||||
return when.reject(new Error('invalid node type: '+node.type));
|
||||
}
|
||||
node.z = flow.id;
|
||||
nodes.push(node);
|
||||
}
|
||||
}
|
||||
var newConfig = clone(activeConfig);
|
||||
newConfig = newConfig.concat(nodes);
|
||||
|
||||
return setConfig(newConfig,'flows',true).then(function() {
|
||||
log.info(log._("nodes.flows.added-flow",{label:(flow.label?flow.label+" ":"")+"["+flow.id+"]"}));
|
||||
return flow.id;
|
||||
});
|
||||
}
|
||||
|
||||
function getFlow(id) {
|
||||
var flow;
|
||||
if (id === 'global') {
|
||||
flow = activeFlowConfig;
|
||||
} else {
|
||||
flow = activeFlowConfig.flows[id];
|
||||
}
|
||||
if (!flow) {
|
||||
return null;
|
||||
}
|
||||
var result = {
|
||||
id: id
|
||||
};
|
||||
if (flow.label) {
|
||||
result.label = flow.label;
|
||||
}
|
||||
if (id !== 'global') {
|
||||
result.nodes = [];
|
||||
}
|
||||
if (flow.nodes) {
|
||||
var nodeIds = Object.keys(flow.nodes);
|
||||
if (nodeIds.length > 0) {
|
||||
result.nodes = nodeIds.map(function(nodeId) {
|
||||
return clone(flow.nodes[nodeId]);
|
||||
})
|
||||
}
|
||||
}
|
||||
if (flow.configs) {
|
||||
var configIds = Object.keys(flow.configs);
|
||||
result.configs = configIds.map(function(configId) {
|
||||
return clone(flow.configs[configId]);
|
||||
})
|
||||
if (result.configs.length === 0) {
|
||||
delete result.configs;
|
||||
}
|
||||
}
|
||||
if (flow.subflows) {
|
||||
var subflowIds = Object.keys(flow.subflows);
|
||||
result.subflows = subflowIds.map(function(subflowId) {
|
||||
var subflow = clone(flow.subflows[subflowId]);
|
||||
var nodeIds = Object.keys(subflow.nodes);
|
||||
subflow.nodes = nodeIds.map(function(id) {
|
||||
return subflow.nodes[id];
|
||||
});
|
||||
if (subflow.configs) {
|
||||
var configIds = Object.keys(subflow.configs);
|
||||
subflow.configs = configIds.map(function(id) {
|
||||
return subflow.configs[id];
|
||||
})
|
||||
}
|
||||
delete subflow.instances;
|
||||
return subflow;
|
||||
});
|
||||
if (result.subflows.length === 0) {
|
||||
delete result.subflows;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function updateFlow(id,newFlow) {
|
||||
var label = id;
|
||||
if (id !== 'global') {
|
||||
if (!activeFlowConfig.flows[id]) {
|
||||
var e = new Error();
|
||||
e.code = 404;
|
||||
throw e;
|
||||
}
|
||||
label = activeFlowConfig.flows[id].label;
|
||||
}
|
||||
var newConfig = clone(activeConfig);
|
||||
var nodes;
|
||||
|
||||
if (id === 'global') {
|
||||
// Remove all nodes whose z is not a known flow
|
||||
// When subflows can be owned by a flow, this logic will have to take
|
||||
// that into account
|
||||
newConfig = newConfig.filter(function(node) {
|
||||
return node.type === 'tab' || (node.hasOwnProperty('z') && activeFlowConfig.flows.hasOwnProperty(node.z));
|
||||
})
|
||||
|
||||
// Add in the new config nodes
|
||||
nodes = newFlow.configs||[];
|
||||
if (newFlow.subflows) {
|
||||
// Add in the new subflows
|
||||
newFlow.subflows.forEach(function(sf) {
|
||||
nodes = nodes.concat(sf.nodes||[]).concat(sf.configs||[]);
|
||||
delete sf.nodes;
|
||||
delete sf.configs;
|
||||
nodes.push(sf);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
newConfig = newConfig.filter(function(node) {
|
||||
return node.z !== id && node.id !== id;
|
||||
});
|
||||
var tabNode = {
|
||||
type:'tab',
|
||||
label:newFlow.label,
|
||||
id:id
|
||||
}
|
||||
nodes = [tabNode].concat(newFlow.nodes||[]).concat(newFlow.configs||[]);
|
||||
nodes.forEach(function(n) {
|
||||
n.z = id;
|
||||
});
|
||||
}
|
||||
|
||||
newConfig = newConfig.concat(nodes);
|
||||
return setConfig(newConfig,'flows',true).then(function() {
|
||||
log.info(log._("nodes.flows.updated-flow",{label:(label?label+" ":"")+"["+id+"]"}));
|
||||
})
|
||||
}
|
||||
|
||||
function removeFlow(id) {
|
||||
if (id === 'global') {
|
||||
// TODO: nls + error code
|
||||
throw new Error('not allowed to remove global');
|
||||
}
|
||||
var flow = activeFlowConfig.flows[id];
|
||||
if (!flow) {
|
||||
var e = new Error();
|
||||
e.code = 404;
|
||||
throw e;
|
||||
}
|
||||
|
||||
var newConfig = clone(activeConfig);
|
||||
newConfig = newConfig.filter(function(node) {
|
||||
return node.z !== id && node.id !== id;
|
||||
});
|
||||
|
||||
return setConfig(newConfig,'flows',true).then(function() {
|
||||
log.info(log._("nodes.flows.removed-flow",{label:(flow.label?flow.label+" ":"")+"["+flow.id+"]"}));
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: init,
|
||||
|
||||
@@ -383,10 +605,18 @@ module.exports = {
|
||||
*/
|
||||
stopFlows: stop,
|
||||
|
||||
get started() { return started },
|
||||
|
||||
handleError: handleError,
|
||||
handleStatus: handleStatus,
|
||||
|
||||
checkTypeInUse: checkTypeInUse
|
||||
checkTypeInUse: checkTypeInUse,
|
||||
|
||||
addFlow: addFlow,
|
||||
getFlow: getFlow,
|
||||
updateFlow: updateFlow,
|
||||
removeFlow: removeFlow,
|
||||
disableFlow:null,
|
||||
enableFlow:null
|
||||
|
||||
};
|
@@ -76,29 +76,28 @@ module.exports = {
|
||||
if (flow.missingTypes.indexOf(n.type) === -1) {
|
||||
flow.missingTypes.push(n.type);
|
||||
}
|
||||
} else {
|
||||
var container = null;
|
||||
if (flow.flows[n.z]) {
|
||||
container = flow.flows[n.z];
|
||||
} else if (flow.subflows[n.z]) {
|
||||
container = flow.subflows[n.z];
|
||||
}
|
||||
var container = null;
|
||||
if (flow.flows[n.z]) {
|
||||
container = flow.flows[n.z];
|
||||
} else if (flow.subflows[n.z]) {
|
||||
container = flow.subflows[n.z];
|
||||
}
|
||||
if (n.hasOwnProperty('x') && n.hasOwnProperty('y')) {
|
||||
if (subflowDetails) {
|
||||
var subflowType = subflowDetails[1]
|
||||
n.subflow = subflowType;
|
||||
flow.subflows[subflowType].instances.push(n)
|
||||
}
|
||||
if (n.hasOwnProperty('x') && n.hasOwnProperty('y')) {
|
||||
if (subflowDetails) {
|
||||
var subflowType = subflowDetails[1]
|
||||
n.subflow = subflowType;
|
||||
flow.subflows[subflowType].instances.push(n)
|
||||
}
|
||||
if (container) {
|
||||
container.nodes[n.id] = n;
|
||||
}
|
||||
if (container) {
|
||||
container.nodes[n.id] = n;
|
||||
}
|
||||
} else {
|
||||
if (container) {
|
||||
container.configs[n.id] = n;
|
||||
} else {
|
||||
if (container) {
|
||||
container.configs[n.id] = n;
|
||||
} else {
|
||||
flow.configs[n.id] = n;
|
||||
flow.configs[n.id]._users = [];
|
||||
}
|
||||
flow.configs[n.id] = n;
|
||||
flow.configs[n.id]._users = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,7 +158,7 @@ module.exports = {
|
||||
changed[removed[id].z] = newConfig.allNodes[removed[id].z];
|
||||
if (changed[removed[id].z].type === "subflow") {
|
||||
changedSubflows[removed[id].z] = changed[removed[id].z];
|
||||
delete removed[id];
|
||||
//delete removed[id];
|
||||
}
|
||||
}
|
||||
} else {
|
@@ -21,6 +21,7 @@ var fs = require("fs");
|
||||
var registry = require("./registry");
|
||||
var credentials = require("./credentials");
|
||||
var flows = require("./flows");
|
||||
var context = require("./context");
|
||||
var Node = require("./Node");
|
||||
var log = require("../log");
|
||||
|
||||
@@ -64,11 +65,12 @@ function createNode(node,def) {
|
||||
}
|
||||
}
|
||||
|
||||
function init(_settings,storage) {
|
||||
settings = _settings;
|
||||
credentials.init(storage);
|
||||
flows.init(_settings,storage);
|
||||
registry.init(_settings);
|
||||
function init(runtime) {
|
||||
settings = runtime.settings;
|
||||
credentials.init(runtime.storage);
|
||||
flows.init(runtime.settings,runtime.storage);
|
||||
registry.init(runtime);
|
||||
context.init(runtime.settings);
|
||||
}
|
||||
|
||||
function disableNode(id) {
|
||||
@@ -120,11 +122,19 @@ module.exports = {
|
||||
cleanModuleList: registry.cleanModuleList,
|
||||
|
||||
// Flow handling
|
||||
loadFlows: flows.load,
|
||||
loadFlows: flows.load,
|
||||
startFlows: flows.startFlows,
|
||||
stopFlows: flows.stopFlows,
|
||||
setFlows: flows.setFlows,
|
||||
getFlows: flows.getFlows,
|
||||
stopFlows: flows.stopFlows,
|
||||
setFlows: flows.setFlows,
|
||||
getFlows: flows.getFlows,
|
||||
|
||||
addFlow: flows.addFlow,
|
||||
getFlow: flows.getFlow,
|
||||
updateFlow: flows.updateFlow,
|
||||
removeFlow: flows.removeFlow,
|
||||
// disableFlow: flows.disableFlow,
|
||||
// enableFlow: flows.enableFlow,
|
||||
|
||||
|
||||
// Credentials
|
||||
addCredentials: credentials.add,
|
@@ -25,17 +25,11 @@ var installer = require("./installer");
|
||||
|
||||
var settings;
|
||||
|
||||
function init(_settings) {
|
||||
settings = _settings;
|
||||
installer.init(settings);
|
||||
loader.init(settings);
|
||||
registry.init(settings,loader);
|
||||
}
|
||||
//TODO: defaultNodesDir/disableNodePathScan are to make testing easier.
|
||||
// When the tests are componentized to match the new registry structure,
|
||||
// these flags belong on localfilesystem.load, not here.
|
||||
function load(defaultNodesDir,disableNodePathScan) {
|
||||
return loader.load(defaultNodesDir,disableNodePathScan);
|
||||
function init(runtime) {
|
||||
settings = runtime.settings;
|
||||
installer.init(runtime.settings);
|
||||
loader.init(runtime);
|
||||
registry.init(runtime.settings,loader);
|
||||
}
|
||||
|
||||
function addModule(module) {
|
||||
@@ -58,7 +52,7 @@ function enableNodeSet(typeOrId) {
|
||||
|
||||
module.exports = {
|
||||
init:init,
|
||||
load:load,
|
||||
load:loader.load,
|
||||
clear: registry.clear,
|
||||
registerType: registry.registerNodeConstructor,
|
||||
|
@@ -40,7 +40,7 @@ function checkModulePath(folder) {
|
||||
var err;
|
||||
var fullPath = path.resolve(folder);
|
||||
var packageFile = path.join(fullPath,'package.json');
|
||||
if (fs.existsSync(packageFile)) {
|
||||
try {
|
||||
var pkg = require(packageFile);
|
||||
moduleName = pkg.name;
|
||||
if (!pkg['node-red']) {
|
||||
@@ -49,7 +49,7 @@ function checkModulePath(folder) {
|
||||
err.code = 'invalid_module';
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
} catch(err2) {
|
||||
err = new Error("Module not found");
|
||||
err.code = 404;
|
||||
throw err;
|
||||
@@ -151,7 +151,10 @@ function uninstallModule(module) {
|
||||
}
|
||||
var installDir = settings.userDir || process.env.NODE_RED_HOME || ".";
|
||||
var moduleDir = path.join(installDir,"node_modules",module);
|
||||
if (!fs.existsSync(moduleDir)) {
|
||||
|
||||
try {
|
||||
fs.statSync(moduleDir);
|
||||
} catch(err) {
|
||||
return reject(new Error(log._("server.install.uninstall-failed",{name:module})));
|
||||
}
|
||||
|
@@ -19,26 +19,16 @@ var fs = require("fs");
|
||||
var path = require("path");
|
||||
var semver = require("semver");
|
||||
|
||||
var events = require("../../events");
|
||||
|
||||
var localfilesystem = require("./localfilesystem");
|
||||
var registry = require("./registry");
|
||||
|
||||
var RED;
|
||||
var settings;
|
||||
var runtime;
|
||||
|
||||
var i18n = require("../../i18n");
|
||||
|
||||
events.on("node-locales-dir", function(info) {
|
||||
i18n.registerMessageCatalog(info.namespace,info.dir,info.file);
|
||||
});
|
||||
|
||||
function init(_settings) {
|
||||
settings = _settings;
|
||||
localfilesystem.init(settings);
|
||||
|
||||
RED = require('../../red');
|
||||
|
||||
function init(_runtime) {
|
||||
runtime = _runtime;
|
||||
settings = runtime.settings;
|
||||
localfilesystem.init(runtime);
|
||||
}
|
||||
|
||||
function load(defaultNodesDir,disableNodePathScan) {
|
||||
@@ -51,13 +41,73 @@ function load(defaultNodesDir,disableNodePathScan) {
|
||||
return loadNodeFiles(nodeFiles);
|
||||
}
|
||||
|
||||
function copyObjectProperties(src,dst,copyList,blockList) {
|
||||
if (!src) {
|
||||
return;
|
||||
}
|
||||
if (copyList && !blockList) {
|
||||
copyList.forEach(function(i) {
|
||||
if (src.hasOwnProperty(i)) {
|
||||
var propDescriptor = Object.getOwnPropertyDescriptor(src,i);
|
||||
Object.defineProperty(dst,i,propDescriptor);
|
||||
}
|
||||
});
|
||||
} else if (!copyList && blockList) {
|
||||
for (var i in src) {
|
||||
if (src.hasOwnProperty(i) && blockList.indexOf(i) === -1) {
|
||||
var propDescriptor = Object.getOwnPropertyDescriptor(src,i);
|
||||
Object.defineProperty(dst,i,propDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createNodeApi(node) {
|
||||
var red = {
|
||||
nodes: {},
|
||||
log: {},
|
||||
settings: {},
|
||||
util: runtime.util,
|
||||
version: runtime.version,
|
||||
}
|
||||
copyObjectProperties(runtime.nodes,red.nodes,["createNode","getNode","eachNode","registerType","addCredentials","getCredentials","deleteCredentials" ]);
|
||||
copyObjectProperties(runtime.log,red.log,null,["init"]);
|
||||
copyObjectProperties(runtime.settings,red.settings,null,["init","load","reset"]);
|
||||
if (runtime.adminApi) {
|
||||
red.comms = runtime.adminApi.comms;
|
||||
red.library = runtime.adminApi.library;
|
||||
red.auth = runtime.adminApi.auth;
|
||||
red.httpAdmin = runtime.adminApi.adminApp;
|
||||
red.httpNode = runtime.adminApi.nodeApp;
|
||||
red.server = runtime.adminApi.server;
|
||||
} else {
|
||||
red.comms = {
|
||||
publish: function(){}
|
||||
};
|
||||
red.library = {
|
||||
register: function(){}
|
||||
};
|
||||
red.auth = {
|
||||
needsPermission: function() {}
|
||||
};
|
||||
// TODO: stub out httpAdmin/httpNode/server
|
||||
}
|
||||
red["_"] = function() {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
args[0] = node.namespace+":"+args[0];
|
||||
return runtime.i18n._.apply(null,args);
|
||||
}
|
||||
return red;
|
||||
}
|
||||
|
||||
|
||||
function loadNodeFiles(nodeFiles) {
|
||||
var promises = [];
|
||||
for (var module in nodeFiles) {
|
||||
/* istanbul ignore else */
|
||||
if (nodeFiles.hasOwnProperty(module)) {
|
||||
if (nodeFiles[module].redVersion &&
|
||||
!semver.satisfies(RED.version().replace("-git",""), nodeFiles[module].redVersion)) {
|
||||
!semver.satisfies(runtime.version().replace("-git",""), nodeFiles[module].redVersion)) {
|
||||
//TODO: log it
|
||||
continue;
|
||||
}
|
||||
@@ -172,7 +222,7 @@ function loadNodeConfig(fileInfo) {
|
||||
index = regExp.lastIndex;
|
||||
var help = content.substring(regExp.lastIndex-match[1].length,regExp.lastIndex);
|
||||
|
||||
var lang = "en-US";
|
||||
var lang = runtime.i18n.defaultLang;
|
||||
if ((match = langRegExp.exec(help)) !== null) {
|
||||
lang = match[1];
|
||||
}
|
||||
@@ -197,7 +247,7 @@ function loadNodeConfig(fileInfo) {
|
||||
fs.stat(path.join(path.dirname(file),"locales"),function(err,stat) {
|
||||
if (!err) {
|
||||
node.namespace = node.id;
|
||||
i18n.registerMessageCatalog(node.id,
|
||||
runtime.i18n.registerMessageCatalog(node.id,
|
||||
path.join(path.dirname(file),"locales"),
|
||||
path.basename(file,".js")+".json")
|
||||
.then(function() {
|
||||
@@ -213,20 +263,6 @@ function loadNodeConfig(fileInfo) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//function getAPIForNode(node) {
|
||||
// var red = {
|
||||
// nodes: RED.nodes,
|
||||
// library: RED.library,
|
||||
// credentials: RED.credentials,
|
||||
// events: RED.events,
|
||||
// log: RED.log,
|
||||
//
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
||||
|
||||
/**
|
||||
* Loads the specified node into the runtime
|
||||
* @param node a node info object - see loadNodeConfig
|
||||
@@ -247,18 +283,7 @@ function loadNodeSet(node) {
|
||||
var r = require(node.file);
|
||||
if (typeof r === "function") {
|
||||
|
||||
var red = {};
|
||||
for (var i in RED) {
|
||||
if (RED.hasOwnProperty(i) && !/^(init|start|stop)$/.test(i)) {
|
||||
var propDescriptor = Object.getOwnPropertyDescriptor(RED,i);
|
||||
Object.defineProperty(red,i,propDescriptor);
|
||||
}
|
||||
}
|
||||
red["_"] = function() {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
args[0] = node.namespace+":"+args[0];
|
||||
return i18n._.apply(null,args);
|
||||
}
|
||||
var red = createNodeApi(node);
|
||||
var promise = r(red);
|
||||
if (promise != null && typeof promise.then === "function") {
|
||||
loadPromise = promise.then(function() {
|
||||
@@ -346,7 +371,7 @@ function getNodeHelp(node,lang) {
|
||||
if (help) {
|
||||
node.help[lang] = help;
|
||||
} else {
|
||||
node.help[lang] = node.help["en-US"];
|
||||
node.help[lang] = node.help[runtime.i18n.defaultLang];
|
||||
}
|
||||
|
||||
}
|
@@ -18,21 +18,18 @@ var when = require("when");
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
|
||||
var events = require("../../events");
|
||||
var log = require("../../log");
|
||||
var events;
|
||||
var log;
|
||||
var i18n;
|
||||
|
||||
var settings;
|
||||
var defaultNodesDir = path.resolve(path.join(__dirname,"..","..","..","nodes"));
|
||||
var disableNodePathScan = false;
|
||||
|
||||
function init(_settings,_defaultNodesDir,_disableNodePathScan) {
|
||||
settings = _settings;
|
||||
if (_disableNodePathScan) {
|
||||
disableNodePathScan = _disableNodePathScan;
|
||||
}
|
||||
if (_defaultNodesDir) {
|
||||
defaultNodesDir = path.resolve(_defaultNodesDir);
|
||||
}
|
||||
function init(runtime) {
|
||||
settings = runtime.settings;
|
||||
events = runtime.events;
|
||||
log = runtime.log;
|
||||
i18n = runtime.i18n;
|
||||
}
|
||||
|
||||
function isExcluded(name) {
|
||||
@@ -49,16 +46,17 @@ function getLocalFile(file) {
|
||||
if (isExcluded(path.basename(file))) {
|
||||
return null;
|
||||
}
|
||||
if (fs.existsSync(file.replace(/\.js$/,".html"))) {
|
||||
try {
|
||||
fs.statSync(file.replace(/\.js$/,".html"));
|
||||
return {
|
||||
file: file,
|
||||
module: "node-red",
|
||||
name: path.basename(file).replace(/^\d+-/,"").replace(/\.js$/,""),
|
||||
version: settings.version
|
||||
};
|
||||
} catch(err) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -133,7 +131,7 @@ function scanDirForNodesModules(dir,moduleName) {
|
||||
* @return a list of node modules: {dir,package}
|
||||
*/
|
||||
function scanTreeForNodesModules(moduleName) {
|
||||
var dir = __dirname+"/../../nodes";
|
||||
var dir = settings.coreNodesDir;
|
||||
var results = [];
|
||||
var userDir;
|
||||
|
||||
@@ -142,14 +140,16 @@ function scanTreeForNodesModules(moduleName) {
|
||||
results = results.concat(scanDirForNodesModules(userDir,moduleName));
|
||||
}
|
||||
|
||||
var up = path.resolve(path.join(dir,".."));
|
||||
while (up !== dir) {
|
||||
var pm = path.join(dir,"node_modules");
|
||||
if (pm != userDir) {
|
||||
results = results.concat(scanDirForNodesModules(pm,moduleName));
|
||||
if (dir) {
|
||||
var up = path.resolve(path.join(dir,".."));
|
||||
while (up !== dir) {
|
||||
var pm = path.join(dir,"node_modules");
|
||||
if (pm != userDir) {
|
||||
results = results.concat(scanDirForNodesModules(pm,moduleName));
|
||||
}
|
||||
dir = up;
|
||||
up = path.resolve(path.join(dir,".."));
|
||||
}
|
||||
dir = up;
|
||||
up = path.resolve(path.join(dir,".."));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
@@ -175,9 +175,11 @@ function getModuleNodeFiles(module) {
|
||||
});
|
||||
var iconDir = path.join(moduleDir,path.dirname(nodes[n]),"icons");
|
||||
if (iconDirs.indexOf(iconDir) == -1) {
|
||||
if (fs.existsSync(iconDir)) {
|
||||
try {
|
||||
fs.statSync(iconDir);
|
||||
events.emit("node-icon-dir",iconDir);
|
||||
iconDirs.push(iconDir);
|
||||
} catch(err) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,24 +187,16 @@ function getModuleNodeFiles(module) {
|
||||
return results;
|
||||
}
|
||||
|
||||
function getNodeFiles(_defaultNodesDir,disableNodePathScan) {
|
||||
|
||||
if (_defaultNodesDir) {
|
||||
defaultNodesDir = _defaultNodesDir;
|
||||
}
|
||||
|
||||
function getNodeFiles(disableNodePathScan) {
|
||||
var dir;
|
||||
// Find all of the nodes to load
|
||||
//console.log(defaultNodesDir);
|
||||
var nodeFiles = getLocalNodeFiles(path.resolve(defaultNodesDir));
|
||||
//console.log(nodeFiles);
|
||||
var nodeFiles = [];
|
||||
|
||||
var defaultLocalesPath = path.resolve(path.join(defaultNodesDir,"core","locales"));
|
||||
events.emit("node-locales-dir", {
|
||||
namespace:"node-red",
|
||||
dir: defaultLocalesPath,
|
||||
file: "messages.json"
|
||||
});
|
||||
if (settings.coreNodesDir) {
|
||||
nodeFiles = getLocalNodeFiles(path.resolve(settings.coreNodesDir));
|
||||
var defaultLocalesPath = path.resolve(path.join(settings.coreNodesDir,"core","locales"));
|
||||
i18n.registerMessageCatalog("node-red",defaultLocalesPath,"messages.json");
|
||||
}
|
||||
|
||||
if (settings.userDir) {
|
||||
dir = path.join(settings.userDir,"nodes");
|
||||
@@ -246,6 +240,8 @@ function getNodeFiles(_defaultNodesDir,disableNodePathScan) {
|
||||
});
|
||||
nodeFiles = nodeFiles.concat(nodeModuleFiles);
|
||||
});
|
||||
} else {
|
||||
console.log("node path scan disabled");
|
||||
}
|
||||
return nodeList;
|
||||
}
|
@@ -256,7 +256,7 @@ function getFullNodeInfo(typeOrId) {
|
||||
var module = moduleConfigs[getModule(id)];
|
||||
if (module) {
|
||||
return module.nodes[getNode(id)];
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -292,7 +292,7 @@ function getModuleList() {
|
||||
//}
|
||||
//return list;
|
||||
return moduleConfigs;
|
||||
|
||||
|
||||
}
|
||||
|
||||
function getModuleInfo(module) {
|
||||
@@ -314,6 +314,18 @@ function getModuleInfo(module) {
|
||||
}
|
||||
}
|
||||
|
||||
function getCaller(){
|
||||
var orig = Error.prepareStackTrace;
|
||||
Error.prepareStackTrace = function(_, stack){ return stack; };
|
||||
var err = new Error();
|
||||
Error.captureStackTrace(err, arguments.callee);
|
||||
var stack = err.stack;
|
||||
Error.prepareStackTrace = orig;
|
||||
stack.shift();
|
||||
stack.shift();
|
||||
return stack[0].getFileName();
|
||||
}
|
||||
|
||||
function registerNodeConstructor(type,constructor) {
|
||||
if (nodeConstructors[type]) {
|
||||
throw new Error(type+" already registered");
|
||||
@@ -358,7 +370,7 @@ function getNodeConfig(id,lang) {
|
||||
if (config) {
|
||||
var result = config.config;
|
||||
result += loader.getNodeHelp(config,lang||"en-US")
|
||||
|
||||
|
||||
//if (config.script) {
|
||||
// result += '<script type="text/javascript">'+config.script+'</script>';
|
||||
//}
|
||||
@@ -490,9 +502,9 @@ var registry = module.exports = {
|
||||
addNodeSet: addNodeSet,
|
||||
enableNodeSet: enableNodeSet,
|
||||
disableNodeSet: disableNodeSet,
|
||||
|
||||
|
||||
removeModule: removeModule,
|
||||
|
||||
|
||||
getNodeInfo: getNodeInfo,
|
||||
getFullNodeInfo: getFullNodeInfo,
|
||||
getNodeList: getNodeList,
|
@@ -28,7 +28,7 @@ var persistentSettings = {
|
||||
userSettings = settings;
|
||||
for (var i in settings) {
|
||||
/* istanbul ignore else */
|
||||
if (settings.hasOwnProperty(i)) {
|
||||
if (settings.hasOwnProperty(i) && typeof settings[i] !== "function") {
|
||||
(function() {
|
||||
var j = i;
|
||||
persistentSettings.__defineGetter__(j,function() { return userSettings[j]; });
|
@@ -25,6 +25,7 @@ var log = require("../log");
|
||||
|
||||
var promiseDir = nodeFn.lift(mkdirp);
|
||||
|
||||
var initialFlowLoadComplete = false;
|
||||
var settings;
|
||||
var flowsFile;
|
||||
var flowsFullPath;
|
||||
@@ -128,9 +129,10 @@ var localfilesystem = {
|
||||
var promises = [];
|
||||
|
||||
if (!settings.userDir) {
|
||||
if (fs.existsSync(fspath.join(process.env.NODE_RED_HOME,".config.json"))) {
|
||||
try {
|
||||
fs.statSync(fspath.join(process.env.NODE_RED_HOME,".config.json"));
|
||||
settings.userDir = process.env.NODE_RED_HOME;
|
||||
} else {
|
||||
} catch(err) {
|
||||
settings.userDir = fspath.join(process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE || process.env.NODE_RED_HOME,".node-red");
|
||||
if (!settings.readOnly) {
|
||||
promises.push(promiseDir(settings.userDir));
|
||||
@@ -148,10 +150,11 @@ var localfilesystem = {
|
||||
// Relative to cwd
|
||||
flowsFullPath = fspath.join(process.cwd(),flowsFile);
|
||||
} else {
|
||||
if (fs.existsSync(fspath.join(process.cwd(),flowsFile))) {
|
||||
try {
|
||||
fs.statSync(fspath.join(process.cwd(),flowsFile));
|
||||
// Found in cwd
|
||||
flowsFullPath = fspath.join(process.cwd(),flowsFile);
|
||||
} else {
|
||||
} catch(err) {
|
||||
// Use userDir
|
||||
flowsFullPath = fspath.join(settings.userDir,flowsFile);
|
||||
}
|
||||
@@ -189,17 +192,17 @@ var localfilesystem = {
|
||||
|
||||
getFlows: function() {
|
||||
return when.promise(function(resolve) {
|
||||
log.info(log._("storage.localfilesystem.user-dir",{path:settings.userDir}));
|
||||
log.info(log._("storage.localfilesystem.flows-file",{path:flowsFullPath}));
|
||||
fs.exists(flowsFullPath, function(exists) {
|
||||
if (exists) {
|
||||
resolve(nodeFn.call(fs.readFile,flowsFullPath,'utf8').then(function(data) {
|
||||
return JSON.parse(data);
|
||||
}));
|
||||
} else {
|
||||
log.info(log._("storage.localfilesystem.create"));
|
||||
resolve([]);
|
||||
if (!initialFlowLoadComplete) {
|
||||
initialFlowLoadComplete = true;
|
||||
log.info(log._("storage.localfilesystem.user-dir",{path:settings.userDir}));
|
||||
log.info(log._("storage.localfilesystem.flows-file",{path:flowsFullPath}));
|
||||
}
|
||||
fs.readFile(flowsFullPath,'utf8',function(err,data) {
|
||||
if (!err) {
|
||||
return resolve(JSON.parse(data));
|
||||
}
|
||||
log.info(log._("storage.localfilesystem.create"));
|
||||
resolve([]);
|
||||
});
|
||||
});
|
||||
},
|
||||
@@ -209,8 +212,9 @@ var localfilesystem = {
|
||||
return when.resolve();
|
||||
}
|
||||
|
||||
if (fs.existsSync(flowsFullPath)) {
|
||||
try {
|
||||
fs.renameSync(flowsFullPath,flowsFileBackup);
|
||||
} catch(err) {
|
||||
}
|
||||
|
||||
var flowData;
|
||||
@@ -225,17 +229,13 @@ var localfilesystem = {
|
||||
|
||||
getCredentials: function() {
|
||||
return when.promise(function(resolve) {
|
||||
fs.exists(credentialsFile, function(exists) {
|
||||
if (exists) {
|
||||
resolve(nodeFn.call(fs.readFile, credentialsFile, 'utf8').then(function(data) {
|
||||
return JSON.parse(data)
|
||||
}));
|
||||
fs.readFile(credentialsFile,'utf8',function(err,data) {
|
||||
if (!err) {
|
||||
resolve(JSON.parse(data));
|
||||
} else {
|
||||
fs.exists(oldCredentialsFile, function(exists) {
|
||||
if (exists) {
|
||||
resolve(nodeFn.call(fs.readFile, oldCredentialsFile, 'utf8').then(function(data) {
|
||||
return JSON.parse(data)
|
||||
}));
|
||||
fs.readFile(oldCredentialsFile,'utf8',function(err,data) {
|
||||
if (!err) {
|
||||
resolve(JSON.parse(data));
|
||||
} else {
|
||||
resolve({});
|
||||
}
|
||||
@@ -250,8 +250,9 @@ var localfilesystem = {
|
||||
return when.resolve();
|
||||
}
|
||||
|
||||
if (fs.existsSync(credentialsFile)) {
|
||||
try {
|
||||
fs.renameSync(credentialsFile,credentialsFileBackup);
|
||||
} catch(err) {
|
||||
}
|
||||
var credentialData;
|
||||
if (settings.flowFilePretty) {
|
||||
@@ -263,21 +264,18 @@ var localfilesystem = {
|
||||
},
|
||||
|
||||
getSettings: function() {
|
||||
if (fs.existsSync(globalSettingsFile)) {
|
||||
return nodeFn.call(fs.readFile,globalSettingsFile,'utf8').then(function(data) {
|
||||
if (data) {
|
||||
return when.promise(function(resolve,reject) {
|
||||
fs.readFile(globalSettingsFile,'utf8',function(err,data) {
|
||||
if (!err) {
|
||||
try {
|
||||
return JSON.parse(data);
|
||||
} catch(err) {
|
||||
return resolve(JSON.parse(data));
|
||||
} catch(err2) {
|
||||
log.trace("Corrupted config detected - resetting");
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
});
|
||||
}
|
||||
return when.resolve({});
|
||||
return resolve({});
|
||||
})
|
||||
})
|
||||
},
|
||||
saveSettings: function(settings) {
|
||||
if (settings.readOnly) {
|
||||
@@ -286,21 +284,18 @@ var localfilesystem = {
|
||||
return writeFile(globalSettingsFile,JSON.stringify(settings,null,1));
|
||||
},
|
||||
getSessions: function() {
|
||||
if (fs.existsSync(sessionsFile)) {
|
||||
return nodeFn.call(fs.readFile,sessionsFile,'utf8').then(function(data) {
|
||||
if (data) {
|
||||
return when.promise(function(resolve,reject) {
|
||||
fs.readFile(sessionsFile,'utf8',function(err,data){
|
||||
if (!err) {
|
||||
try {
|
||||
return JSON.parse(data);
|
||||
} catch(err) {
|
||||
return resolve(JSON.parse(data));
|
||||
} catch(err2) {
|
||||
log.trace("Corrupted sessions file - resetting");
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
});
|
||||
}
|
||||
return when.resolve({});
|
||||
resolve({});
|
||||
})
|
||||
});
|
||||
},
|
||||
saveSessions: function(sessions) {
|
||||
if (settings.readOnly) {
|
@@ -103,10 +103,81 @@ function compareObjects(obj1,obj2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function getMessageProperty(msg,expr) {
|
||||
var result = null;
|
||||
if (expr.indexOf('msg.')===0) {
|
||||
expr = expr.substring(4);
|
||||
}
|
||||
var msgPropParts = expr.split(".");
|
||||
msgPropParts.reduce(function(obj, i) {
|
||||
result = (typeof obj[i] !== "undefined" ? obj[i] : undefined);
|
||||
return result;
|
||||
}, msg);
|
||||
return result;
|
||||
}
|
||||
|
||||
function setMessageProperty(msg,prop,value,createMissing) {
|
||||
if (typeof createMissing === 'undefined') {
|
||||
createMissing = (typeof value !== 'undefined');
|
||||
}
|
||||
if (prop.indexOf('msg.')===0) {
|
||||
prop = prop.substring(4);
|
||||
}
|
||||
var msgPropParts = prop.split(".");
|
||||
var depth = 0;
|
||||
msgPropParts.reduce(function(obj, i) {
|
||||
if (obj === null) {
|
||||
return null;
|
||||
}
|
||||
depth++;
|
||||
if (depth === msgPropParts.length) {
|
||||
if (typeof value === "undefined") {
|
||||
delete obj[i];
|
||||
} else {
|
||||
obj[i] = value;
|
||||
}
|
||||
} else {
|
||||
if (!obj[i]) {
|
||||
if (createMissing) {
|
||||
obj[i] = {};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return obj[i];
|
||||
}
|
||||
}, msg);
|
||||
}
|
||||
|
||||
function evaluateNodeProperty(value, type, node, msg) {
|
||||
if (type === 'str') {
|
||||
return ""+value;
|
||||
} else if (type === 'num') {
|
||||
return Number(value);
|
||||
} else if (type === 'json') {
|
||||
return JSON.parse(value);
|
||||
} else if (type === 're') {
|
||||
return new RegExp(value);
|
||||
} else if (type === 'msg' && msg) {
|
||||
return getMessageProperty(msg,value);
|
||||
} else if (type === 'flow' && node) {
|
||||
return node.context().flow.get(value);
|
||||
} else if (type === 'global' && node) {
|
||||
return node.context().global.get(value);
|
||||
} else if (type === 'bool') {
|
||||
return /^true$/i.test(value)
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
ensureString: ensureString,
|
||||
ensureBuffer: ensureBuffer,
|
||||
cloneMessage: cloneMessage,
|
||||
compareObjects: compareObjects,
|
||||
generateId: generateId
|
||||
generateId: generateId,
|
||||
getMessageProperty: getMessageProperty,
|
||||
setMessageProperty: setMessageProperty,
|
||||
evaluateNodeProperty: evaluateNodeProperty
|
||||
};
|
@@ -148,7 +148,7 @@ module.exports = {
|
||||
|
||||
functionGlobalContext: {
|
||||
// os:require('os'),
|
||||
// bonescript:require('bonescript'),
|
||||
// octalbonescript:require('octalbonescript'),
|
||||
// jfive:require("johnny-five"),
|
||||
// j5board:require("johnny-five").Board({repl:false})
|
||||
},
|
||||
|
@@ -68,7 +68,7 @@ describe('template node', function() {
|
||||
msg.topic.foo.should.have.a.property('bar', 'payload=foo');
|
||||
done();
|
||||
});
|
||||
n1.receive({payload:{doh:{rei:{me:"foo"}}}, topic:"bar"});
|
||||
n1.receive({payload:{doh:{rei:{me:"foo"}}}});
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -19,9 +19,6 @@ var should = require("should");
|
||||
var changeNode = require("../../../../nodes/core/logic/15-change.js");
|
||||
var helper = require("../../helper.js");
|
||||
|
||||
var Log = require("../../../../red/log.js");
|
||||
|
||||
|
||||
describe('change Node', function() {
|
||||
|
||||
beforeEach(function(done) {
|
||||
@@ -37,7 +34,7 @@ describe('change Node', function() {
|
||||
var flow = [{ id: "c1", type: "change", name:"change1" }];
|
||||
helper.load(changeNode, flow, function() {
|
||||
helper.getNode("c1").should.have.property("name", "change1");
|
||||
helper.getNode("c1").should.have.property("rules", [{t:undefined,p:''}]);
|
||||
helper.getNode("c1").should.have.property("rules", [{fromt:'str',pt:'msg',tot:'str',t:undefined,p:''}]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -45,7 +42,7 @@ describe('change Node', function() {
|
||||
var flow = [{ id: "c1", type: "change", name:"change1", action:"replace" }];
|
||||
helper.load(changeNode, flow, function() {
|
||||
helper.getNode("c1").should.have.property("name", "change1");
|
||||
helper.getNode("c1").should.have.property("rules", [ { p: '', t: 'set', to: '' } ]);
|
||||
helper.getNode("c1").should.have.property("rules", [ {fromt: 'str', p: '', pt: 'msg', t: 'set', to: '', tot: 'str'} ]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -54,12 +51,12 @@ describe('change Node', function() {
|
||||
helper.load(changeNode, flow, function() {
|
||||
//console.log(helper.getNode("c1"));
|
||||
helper.getNode("c1").should.have.property("name", "change1");
|
||||
helper.getNode("c1").should.have.property("rules", [ { from: /(?:)/g, p: '', re: undefined, t: 'change', to: '' } ]);
|
||||
helper.getNode("c1").should.have.property("rules", [ { from: /(?:)/g,fromt: 'str', p: '',pt: 'msg', re: undefined, t: 'change', to: '',tot: 'str' } ]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#replace' , function() {
|
||||
describe('#set' , function() {
|
||||
|
||||
it('sets the value of the message property', function(done) {
|
||||
var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"payload","from":"","to":"changed","reg":false,"name":"changeNode","wires":[["helperNode1"]]},
|
||||
@@ -67,6 +64,7 @@ describe('change Node', function() {
|
||||
helper.load(changeNode, flow, function() {
|
||||
var changeNode1 = helper.getNode("changeNode1");
|
||||
var helperNode1 = helper.getNode("helperNode1");
|
||||
var rule = helper.getNode("changeNode1").rules[0];
|
||||
helperNode1.on("input", function(msg) {
|
||||
try {
|
||||
msg.payload.should.equal("changed");
|
||||
@@ -121,6 +119,9 @@ describe('change Node', function() {
|
||||
helper.load(changeNode, flow, function() {
|
||||
var changeNode1 = helper.getNode("changeNode1");
|
||||
var helperNode1 = helper.getNode("helperNode1");
|
||||
var rule = helper.getNode("changeNode1").rules[0];
|
||||
rule.t.should.eql('set');
|
||||
rule.tot.should.eql('msg');
|
||||
helperNode1.on("input", function(msg) {
|
||||
try {
|
||||
msg.foo.should.equal("bar");
|
||||
@@ -240,6 +241,44 @@ describe('change Node', function() {
|
||||
changeNode1.receive({pay:{load:"changeMe"}});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('changes the value to a number', function(done) {
|
||||
var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"123","tot":"num"}],"name":"changeNode","wires":[["helperNode1"]]},
|
||||
{id:"helperNode1", type:"helper", wires:[]}];
|
||||
helper.load(changeNode, flow, function() {
|
||||
var changeNode1 = helper.getNode("changeNode1");
|
||||
var helperNode1 = helper.getNode("helperNode1");
|
||||
helperNode1.on("input", function(msg) {
|
||||
try {
|
||||
msg.payload.should.eql(123);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
changeNode1.receive({payload:""});
|
||||
});
|
||||
});
|
||||
it('changes the value to a js object', function(done) {
|
||||
var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":'{"a":123}',"tot":"json"}],"name":"changeNode","wires":[["helperNode1"]]},
|
||||
{id:"helperNode1", type:"helper", wires:[]}];
|
||||
helper.load(changeNode, flow, function() {
|
||||
var changeNode1 = helper.getNode("changeNode1");
|
||||
var helperNode1 = helper.getNode("helperNode1");
|
||||
helperNode1.on("input", function(msg) {
|
||||
try {
|
||||
msg.payload.should.eql({a:123});
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
changeNode1.receive({payload:""});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
describe('#change', function() {
|
||||
it('changes the value of the message property', function(done) {
|
||||
@@ -350,7 +389,7 @@ describe('change Node', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('Reports invalid regex', function(done) {
|
||||
it('reports invalid regex', function(done) {
|
||||
var sinon = require('sinon');
|
||||
var flow = [{"id":"changeNode1","type":"change","action":"change","property":"payload","from":"\\+**+","to":"NUMBER","reg":true,"name":"changeNode","wires":[["helperNode1"]]},
|
||||
{id:"helperNode1", type:"helper", wires:[]}];
|
||||
@@ -366,6 +405,43 @@ describe('change Node', function() {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
it('supports regex groups - new rule format', function(done) {
|
||||
var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"(Hello)","to":"$1-$1-$1","fromt":"re","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]},
|
||||
{id:"helperNode1", type:"helper", wires:[]}];
|
||||
helper.load(changeNode, flow, function() {
|
||||
var changeNode1 = helper.getNode("changeNode1");
|
||||
var helperNode1 = helper.getNode("helperNode1");
|
||||
helperNode1.on("input", function(msg) {
|
||||
try {
|
||||
msg.payload.should.equal("Hello-Hello-Hello World");
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
changeNode1.receive({payload:"Hello World"});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('changes the value - new rule format', function(done) {
|
||||
var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"ABC","to":"123","fromt":"str","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]},
|
||||
{id:"helperNode1", type:"helper", wires:[]}];
|
||||
helper.load(changeNode, flow, function() {
|
||||
var changeNode1 = helper.getNode("changeNode1");
|
||||
var helperNode1 = helper.getNode("helperNode1");
|
||||
helperNode1.on("input", function(msg) {
|
||||
try {
|
||||
msg.payload.should.equal("abc123abc");
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
changeNode1.receive({payload:"abcABCabc"});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#delete", function() {
|
||||
|
@@ -77,7 +77,6 @@ describe('JSON node', function() {
|
||||
var jn1 = helper.getNode("jn1");
|
||||
var jn2 = helper.getNode("jn2");
|
||||
jn2.on("input", function(msg) {
|
||||
console.log(msg);
|
||||
should.equal(msg.payload, '[1,2,3]');
|
||||
done();
|
||||
});
|
||||
@@ -118,7 +117,6 @@ describe('JSON node', function() {
|
||||
var logEvents = helper.log().args.filter(function(evt) {
|
||||
return evt[0].type == "json";
|
||||
});
|
||||
console.log(logEvents);
|
||||
logEvents.should.have.length(3);
|
||||
logEvents[0][0].should.have.a.property('msg');
|
||||
logEvents[0][0].msg.toString().should.eql('json.errors.dropped');
|
||||
|
@@ -20,7 +20,6 @@ var fs = require('fs-extra');
|
||||
var sinon = require("sinon");
|
||||
var fileNode = require("../../../../nodes/core/storage/50-file.js");
|
||||
var helper = require("../../helper.js");
|
||||
var log = require("../../../../red/log");
|
||||
|
||||
describe('file Nodes', function() {
|
||||
|
||||
|
@@ -30,11 +30,11 @@ if (!process.version.match(/^v0\.[0-9]\./)) {
|
||||
}
|
||||
}
|
||||
var RED = require("../../red/red.js");
|
||||
var redNodes = require("../../red/nodes");
|
||||
var flows = require("../../red/nodes/flows");
|
||||
var credentials = require("../../red/nodes/credentials");
|
||||
var comms = require("../../red/comms.js");
|
||||
var log = require("../../red/log.js");
|
||||
var redNodes = require("../../red/runtime/nodes");
|
||||
var flows = require("../../red/runtime/nodes/flows");
|
||||
var credentials = require("../../red/runtime/nodes/credentials");
|
||||
var comms = require("../../red/api/comms.js");
|
||||
var log = require("../../red/runtime/log.js");
|
||||
|
||||
var http = require('http');
|
||||
var express = require('express');
|
||||
@@ -99,7 +99,7 @@ module.exports = {
|
||||
return messageId;
|
||||
};
|
||||
|
||||
redNodes.init(settings, storage);
|
||||
redNodes.init({settings:settings, storage:storage});
|
||||
credentials.init(storage,express());
|
||||
RED.nodes.registerType("helper", helperNode);
|
||||
if (Array.isArray(testNode)) {
|
||||
|
@@ -24,12 +24,14 @@ var auth = require("../../../../red/api/auth");
|
||||
var Users = require("../../../../red/api/auth/users");
|
||||
var Tokens = require("../../../../red/api/auth/tokens");
|
||||
|
||||
var settings = require("../../../../red/settings");
|
||||
|
||||
|
||||
describe("api auth middleware",function() {
|
||||
|
||||
|
||||
|
||||
describe("ensureClientSecret", function() {
|
||||
before(function() {
|
||||
auth.init({settings:{},log:{audit:function(){}}})
|
||||
});
|
||||
it("leaves client_secret alone if not present",function(done) {
|
||||
var req = {
|
||||
body: {
|
||||
@@ -83,7 +85,7 @@ describe("api auth middleware",function() {
|
||||
Users.init.restore();
|
||||
});
|
||||
it("returns login details - credentials", function(done) {
|
||||
auth.init({adminAuth:{}},null);
|
||||
auth.init({settings:{adminAuth:{}},log:{audit:function(){}}})
|
||||
auth.login(null,{json: function(resp) {
|
||||
resp.should.have.a.property("type","credentials");
|
||||
resp.should.have.a.property("prompts");
|
||||
@@ -92,7 +94,7 @@ describe("api auth middleware",function() {
|
||||
}});
|
||||
});
|
||||
it("returns login details - none", function(done) {
|
||||
auth.init({},null);
|
||||
auth.init({settings:{},log:{audit:function(){}}})
|
||||
auth.login(null,{json: function(resp) {
|
||||
resp.should.eql({});
|
||||
done();
|
||||
|
@@ -24,8 +24,10 @@ var Tokens = require("../../../../red/api/auth/tokens");
|
||||
var Clients = require("../../../../red/api/auth/clients");
|
||||
|
||||
describe("Auth strategies", function() {
|
||||
before(function() {
|
||||
strategies.init({log:{audit:function(){}}})
|
||||
});
|
||||
describe("Password Token Exchange", function() {
|
||||
|
||||
var userAuthentication;
|
||||
afterEach(function() {
|
||||
if (userAuthentication) {
|
||||
@@ -49,7 +51,7 @@ describe("Auth strategies", function() {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('Handles scope overreach',function(done) {
|
||||
userAuthentication = sinon.stub(Users,"authenticate",function(username,password) {
|
||||
return when.resolve({username:"user",permissions:"read"});
|
||||
|
@@ -23,21 +23,26 @@ var express = require('express');
|
||||
var app = express();
|
||||
var WebSocket = require('ws');
|
||||
|
||||
var comms = require("../../red/comms.js");
|
||||
var Users = require("../../red/api/auth/users");
|
||||
var Tokens = require("../../red/api/auth/tokens");
|
||||
var comms = require("../../../red/api/comms");
|
||||
var Users = require("../../../red/api/auth/users");
|
||||
var Tokens = require("../../../red/api/auth/tokens");
|
||||
|
||||
var address = '127.0.0.1';
|
||||
var listenPort = 0; // use ephemeral port
|
||||
|
||||
describe("comms", function() {
|
||||
describe("api/comms", function() {
|
||||
describe("with default keepalive", function() {
|
||||
var server;
|
||||
var url;
|
||||
var port;
|
||||
before(function(done) {
|
||||
sinon.stub(Users,"default",function() { return when.resolve(null);});
|
||||
server = http.createServer(function(req,res){app(req,res)});
|
||||
comms.init(server, {});
|
||||
comms.init(server, {
|
||||
settings:{},
|
||||
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
|
||||
events:{on:function(){},removeListener:function(){}}
|
||||
});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
@@ -46,11 +51,12 @@ describe("comms", function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
after(function() {
|
||||
Users.default.restore();
|
||||
comms.stop();
|
||||
});
|
||||
|
||||
|
||||
it('accepts connection', function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
ws.on('open', function() {
|
||||
@@ -58,7 +64,7 @@ describe("comms", function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('publishes message after subscription', function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
ws.on('open', function() {
|
||||
@@ -71,7 +77,7 @@ describe("comms", function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('publishes retained message for subscription', function(done) {
|
||||
comms.publish('topic2', 'bar', true);
|
||||
var ws = new WebSocket(url);
|
||||
@@ -84,7 +90,7 @@ describe("comms", function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('retained message is deleted by non-retained message', function(done) {
|
||||
comms.publish('topic3', 'retained', true);
|
||||
comms.publish('topic3', 'non-retained');
|
||||
@@ -99,7 +105,7 @@ describe("comms", function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('malformed messages are ignored',function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
ws.on('open', function() {
|
||||
@@ -114,34 +120,39 @@ describe("comms", function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// The following test currently fails due to minimum viable
|
||||
// implementation. More test should be written to test topic
|
||||
// matching once this one is passing
|
||||
|
||||
if (0) {
|
||||
it('receives message on correct topic', function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
ws.on('open', function() {
|
||||
ws.send('{"subscribe":"topic4"}');
|
||||
comms.publish('topic5', 'foo');
|
||||
comms.publish('topic4', 'bar');
|
||||
});
|
||||
ws.on('message', function(msg) {
|
||||
msg.should.equal('{"topic":"topic4","data":"bar"}');
|
||||
ws.close();
|
||||
done();
|
||||
});
|
||||
|
||||
it.skip('receives message on correct topic', function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
ws.on('open', function() {
|
||||
ws.send('{"subscribe":"topic4"}');
|
||||
comms.publish('topic5', 'foo');
|
||||
comms.publish('topic4', 'bar');
|
||||
});
|
||||
}
|
||||
ws.on('message', function(msg) {
|
||||
msg.should.equal('{"topic":"topic4","data":"bar"}');
|
||||
ws.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('listens for node/status events');
|
||||
});
|
||||
describe("disabled editor", function() {
|
||||
var server;
|
||||
var url;
|
||||
var port;
|
||||
before(function(done) {
|
||||
sinon.stub(Users,"default",function() { return when.resolve(null);});
|
||||
server = http.createServer(function(req,res){app(req,res)});
|
||||
comms.init(server, {disableEditor:true});
|
||||
comms.init(server, {
|
||||
settings:{disableEditor:true},
|
||||
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
|
||||
events:{on:function(){},removeListener:function(){}}
|
||||
});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
@@ -150,11 +161,12 @@ describe("comms", function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
after(function() {
|
||||
Users.default.restore();
|
||||
comms.stop();
|
||||
});
|
||||
|
||||
|
||||
it('rejects websocket connections',function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
ws.on('open', function() {
|
||||
@@ -164,17 +176,22 @@ describe("comms", function() {
|
||||
ws.on('error', function() {
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("non-default httpAdminRoot set: /adminPath", function() {
|
||||
var server;
|
||||
var url;
|
||||
var port;
|
||||
before(function(done) {
|
||||
sinon.stub(Users,"default",function() { return when.resolve(null);});
|
||||
server = http.createServer(function(req,res){app(req,res)});
|
||||
comms.init(server, {httpAdminRoot:"/adminPath"});
|
||||
comms.init(server, {
|
||||
settings:{httpAdminRoot:"/adminPath"},
|
||||
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
|
||||
events:{on:function(){},removeListener:function(){}}
|
||||
});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
@@ -183,11 +200,12 @@ describe("comms", function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
after(function() {
|
||||
Users.default.restore();
|
||||
comms.stop();
|
||||
});
|
||||
|
||||
|
||||
it('accepts connections',function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
ws.on('open', function() {
|
||||
@@ -197,17 +215,22 @@ describe("comms", function() {
|
||||
ws.on('error', function() {
|
||||
done(new Error("Socket connection failed"));
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("non-default httpAdminRoot set: /adminPath/", function() {
|
||||
var server;
|
||||
var url;
|
||||
var port;
|
||||
before(function(done) {
|
||||
sinon.stub(Users,"default",function() { return when.resolve(null);});
|
||||
server = http.createServer(function(req,res){app(req,res)});
|
||||
comms.init(server, {httpAdminRoot:"/adminPath/"});
|
||||
comms.init(server,{
|
||||
settings:{httpAdminRoot:"/adminPath"},
|
||||
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
|
||||
events:{on:function(){},removeListener:function(){}}
|
||||
});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
@@ -216,11 +239,12 @@ describe("comms", function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
after(function() {
|
||||
Users.default.restore();
|
||||
comms.stop();
|
||||
});
|
||||
|
||||
|
||||
it('accepts connections',function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
ws.on('open', function() {
|
||||
@@ -230,17 +254,22 @@ describe("comms", function() {
|
||||
ws.on('error', function() {
|
||||
done(new Error("Socket connection failed"));
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("non-default httpAdminRoot set: adminPath", function() {
|
||||
var server;
|
||||
var url;
|
||||
var port;
|
||||
before(function(done) {
|
||||
sinon.stub(Users,"default",function() { return when.resolve(null);});
|
||||
server = http.createServer(function(req,res){app(req,res)});
|
||||
comms.init(server, {httpAdminRoot:"adminPath"});
|
||||
comms.init(server, {
|
||||
settings:{httpAdminRoot:"adminPath"},
|
||||
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
|
||||
events:{on:function(){},removeListener:function(){}}
|
||||
});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
@@ -249,11 +278,12 @@ describe("comms", function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
after(function() {
|
||||
Users.default.restore();
|
||||
comms.stop();
|
||||
});
|
||||
|
||||
|
||||
it('accepts connections',function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
ws.on('open', function() {
|
||||
@@ -263,17 +293,22 @@ describe("comms", function() {
|
||||
ws.on('error', function() {
|
||||
done(new Error("Socket connection failed"));
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("keep alives", function() {
|
||||
var server;
|
||||
var url;
|
||||
var port;
|
||||
before(function(done) {
|
||||
sinon.stub(Users,"default",function() { return when.resolve(null);});
|
||||
server = http.createServer(function(req,res){app(req,res)});
|
||||
comms.init(server, {webSocketKeepAliveTime: 100});
|
||||
comms.init(server, {
|
||||
settings:{webSocketKeepAliveTime: 100},
|
||||
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
|
||||
events:{on:function(){},removeListener:function(){}}
|
||||
});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
@@ -283,6 +318,7 @@ describe("comms", function() {
|
||||
});
|
||||
});
|
||||
after(function() {
|
||||
Users.default.restore();
|
||||
comms.stop();
|
||||
});
|
||||
it('are sent', function(done) {
|
||||
@@ -325,7 +361,7 @@ describe("comms", function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('authentication required, no anonymous',function() {
|
||||
var server;
|
||||
var url;
|
||||
@@ -351,10 +387,14 @@ describe("comms", function() {
|
||||
return when.resolve(null);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
server = http.createServer(function(req,res){app(req,res)});
|
||||
comms.init(server, {adminAuth:{}});
|
||||
comms.init(server,{
|
||||
settings:{adminAuth:{}},
|
||||
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
|
||||
events:{on:function(){},removeListener:function(){}}
|
||||
});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
@@ -369,7 +409,7 @@ describe("comms", function() {
|
||||
getToken.restore();
|
||||
comms.stop();
|
||||
});
|
||||
|
||||
|
||||
it('prevents connections that do not authenticate',function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
var count = 0;
|
||||
@@ -381,7 +421,7 @@ describe("comms", function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('allows connections that do authenticate',function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
var received = 0;
|
||||
@@ -399,7 +439,7 @@ describe("comms", function() {
|
||||
ws.close();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
ws.on('close', function() {
|
||||
try {
|
||||
received.should.equal(2);
|
||||
@@ -409,7 +449,7 @@ describe("comms", function() {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('rejects connections for non-existant token',function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
var received = 0;
|
||||
@@ -440,7 +480,11 @@ describe("comms", function() {
|
||||
before(function(done) {
|
||||
getDefaultUser = sinon.stub(Users,"default",function() { return when.resolve({permissions:"read"});});
|
||||
server = http.createServer(function(req,res){app(req,res)});
|
||||
comms.init(server, {adminAuth:{}});
|
||||
comms.init(server, {
|
||||
settings:{adminAuth:{}},
|
||||
log:{warn:function(){},_:function(){},trace:function(){},audit:function(){}},
|
||||
events:{on:function(){},removeListener:function(){}}
|
||||
});
|
||||
server.listen(listenPort, address);
|
||||
server.on('listening', function() {
|
||||
port = server.address().port;
|
||||
@@ -453,7 +497,7 @@ describe("comms", function() {
|
||||
getDefaultUser.restore();
|
||||
comms.stop();
|
||||
});
|
||||
|
||||
|
||||
it('allows anonymous connections that do not authenticate',function(done) {
|
||||
var ws = new WebSocket(url);
|
||||
var count = 0;
|
||||
@@ -480,5 +524,5 @@ describe("comms", function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user