mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Compare commits
121 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
34537180c3 | ||
|
cb01920ee6 | ||
|
437b01a0ff | ||
|
075a2abf71 | ||
|
985875cc75 | ||
|
1c45bc615f | ||
|
fa7f2606fb | ||
|
12b95f1c72 | ||
|
a0aee2021d | ||
|
c90fd1e6d8 | ||
|
3b769fd2de | ||
|
71ecb89abc | ||
|
7b6bc1d3bc | ||
|
9c3be40fbe | ||
|
ab87fa9ce4 | ||
|
d1940a023a | ||
|
5a176a037c | ||
|
ec25191c98 | ||
|
20b321f928 | ||
|
425b016d63 | ||
|
b2c7189ce4 | ||
|
f66886dbdb | ||
|
712f8b4680 | ||
|
f626ee060a | ||
|
86aa7c97be | ||
|
30e3525987 | ||
|
ad44f838da | ||
|
2569a35b6c | ||
|
1ee5e50d50 | ||
|
1dbec5eca8 | ||
|
2bc8db308c | ||
|
f196740426 | ||
|
20121b79c5 | ||
|
741a4cfe53 | ||
|
0343de9f34 | ||
|
4772bca14a | ||
|
6ae1a5ba0d | ||
|
217c9718e4 | ||
|
61d7893467 | ||
|
8f26c01f4b | ||
|
61045ddd7f | ||
|
1bf72a0bc3 | ||
|
6d84b1bb8d | ||
|
8abd0b1fdf | ||
|
81e125b7ba | ||
|
d5e1468718 | ||
|
c232bf5ed6 | ||
|
21b25ffaee | ||
|
ca0a93df08 | ||
|
699a22c757 | ||
|
8b2b1669b5 | ||
|
c1e8370916 | ||
|
ddedea8b90 | ||
|
8f414ce458 | ||
|
453f23da20 | ||
|
9e91e42a1b | ||
|
b666734c79 | ||
|
a2297f303d | ||
|
ecde942255 | ||
|
d668d43a0a | ||
|
ca91a5dd95 | ||
|
5f9780d71c | ||
|
ee37464741 | ||
|
8d73f927db | ||
|
4a0222bd1c | ||
|
c0b8f5e3e1 | ||
|
a9a0b263dc | ||
|
f2b73187d8 | ||
|
ef10ade0cc | ||
|
719bb4263e | ||
|
b3602b268e | ||
|
66ec9bae27 | ||
|
d96b6e77c0 | ||
|
da64c018ac | ||
|
f9fb97adf2 | ||
|
8316bc6480 | ||
|
08021e039a | ||
|
cc6e0937a0 | ||
|
c1d694a97c | ||
|
fcf4f40c36 | ||
|
380b03399c | ||
|
c33d02c53f | ||
|
fa5e37993e | ||
|
437b2d506b | ||
|
4ed09f6431 | ||
|
0b98a6acf8 | ||
|
1d73c86cb2 | ||
|
40fe0f3239 | ||
|
d1ea689999 | ||
|
a6644ad5ff | ||
|
658746d2a3 | ||
|
cbdd4de630 | ||
|
00c612485b | ||
|
3a6192bf73 | ||
|
c64b5c2850 | ||
|
fdbf079896 | ||
|
d1a5395727 | ||
|
83a3642c0e | ||
|
9932d34304 | ||
|
7aa37a1976 | ||
|
fa42fbdab8 | ||
|
caa83ac830 | ||
|
3963fa9738 | ||
|
005a98d020 | ||
|
9560dc9408 | ||
|
4ac9a5edf0 | ||
|
37e62597ae | ||
|
90bfe378d0 | ||
|
ce22b494ec | ||
|
f9e0420647 | ||
|
2fe568d9ba | ||
|
2d4979df4d | ||
|
b555b014b8 | ||
|
999b888c54 | ||
|
5193d7bddb | ||
|
6b03379e4e | ||
|
08d687ad60 | ||
|
eb57089f06 | ||
|
a76e4fede1 | ||
|
705d043540 | ||
|
5462e251f8 |
@@ -1,6 +1,5 @@
|
||||
/Gruntfile.js
|
||||
/.git/*
|
||||
*.json
|
||||
/lib/*
|
||||
*.backup
|
||||
/public/*
|
||||
|
14
.travis.yml
14
.travis.yml
@@ -1,7 +1,21 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
env:
|
||||
- CXX="g++-4.8"
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
- gcc-4.8
|
||||
matrix:
|
||||
allow_failures:
|
||||
- node_js: "4"
|
||||
before_install:
|
||||
- npm install -g npm@~1.4.18
|
||||
node_js:
|
||||
- "4"
|
||||
- "0.12"
|
||||
- "0.10"
|
||||
script:
|
||||
|
11
Gruntfile.js
11
Gruntfile.js
@@ -18,6 +18,12 @@ var path = require("path");
|
||||
|
||||
module.exports = function(grunt) {
|
||||
|
||||
var nodemonArgs = ["-v"];
|
||||
var flowFile = grunt.option('flowFile');
|
||||
if (flowFile) {
|
||||
nodemonArgs.push(flowFile);
|
||||
}
|
||||
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
paths: {
|
||||
@@ -106,6 +112,7 @@ module.exports = function(grunt) {
|
||||
"editor/js/ui/menu.js",
|
||||
"editor/js/ui/keyboard.js",
|
||||
"editor/js/ui/tabs.js",
|
||||
"editor/js/ui/popover.js",
|
||||
"editor/js/ui/workspaces.js",
|
||||
"editor/js/ui/view.js",
|
||||
"editor/js/ui/sidebar.js",
|
||||
@@ -228,10 +235,10 @@ module.exports = function(grunt) {
|
||||
dev: {
|
||||
script: 'red.js',
|
||||
options: {
|
||||
args:['-v'],
|
||||
args: nodemonArgs,
|
||||
ext: 'js,html,json',
|
||||
watch: [
|
||||
'red','nodes'
|
||||
'red','nodes','locales'
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@@ -2,8 +2,8 @@
|
||||
|
||||
http://nodered.org
|
||||
|
||||
[](https://travis-ci.org/node-red/node-red)
|
||||
[](https://coveralls.io/r/node-red/node-red?branch=master)
|
||||
[](https://travis-ci.org/node-red/node-red)
|
||||
[](https://coveralls.io/r/node-red/node-red?branch=master)
|
||||
|
||||
A visual tool for wiring the Internet of Things.
|
||||
|
||||
@@ -67,4 +67,3 @@ For more open-source projects from IBM, head over [here](http://ibm.github.io).
|
||||
## Copyright and license
|
||||
|
||||
Copyright 2013, 2015 IBM Corp. under [the Apache 2.0 license](LICENSE).
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 17 KiB |
33
editor/images/node-red-icon.svg
Normal file
33
editor/images/node-red-icon.svg
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="480" width="480" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" viewBox="0 0 480.00002 479.99999">
|
||||
<title>Node-RED Icon</title>
|
||||
<metadata>
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title>Node-RED Icon</dc:title>
|
||||
<cc:license rdf:resource="http://creativecommons.org/licenses/by/3.0/"/>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Nick O'Leary</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
</cc:Work>
|
||||
<cc:License rdf:about="http://creativecommons.org/licenses/by/3.0/">
|
||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/>
|
||||
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/>
|
||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice"/>
|
||||
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution"/>
|
||||
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/>
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g transform="translate(0 -572.36)">
|
||||
<rect style="color-rendering:auto;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;image-rendering:auto" ry="56" height="448" width="448" y="588.36" x="16" fill="#8f0000"/>
|
||||
<g transform="matrix(8.545 0 0 8.545 -786.19 -1949.8)">
|
||||
<path style="color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;text-decoration-style:solid;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none" d="m104.41 321.21c0.0138-2.3846-1.905-4.2806-4.2896-4.2806h-6.243v2.9257h6.243c0.80513 0 1.4808 0.58383 1.4808 1.389v4.2422c0 0.80513-0.67566 1.5075-1.4808 1.5075h-6.243v2.8086h6.243c2.3846 0 4.2895-1.9315 4.2895-4.3162l-0.00005-1.9812c9.8659 0.14125 12.737 2.7065 15.877 5.4519 3.0241 2.6446 6.4153 5.4869 15.252 5.557l0.00046 0.97238c0.001 2.3846 1.9543 4.3803 4.3389 4.3803h6.4273v-3.0427h-6.4273c-0.80514 0-1.4135-0.53255-1.4135-1.3377v-4.2422c0-0.80513 0.60835-1.4418 1.4135-1.4418h6.4273v-2.8086h-6.4273c-2.3846 0-4.3379 1.8658-4.3389 4.2504l-0.00045 1.005c-8.351-0.0276-10.723-2.3434-13.76-4.9992-2.5914-2.2662-5.6368-4.7578-12.346-5.6642 0.0583-0.0501 0.11211-0.0987 0.16838-0.15027 1.2918-1.1846 1.9884-2.6158 2.6699-3.8516 0.68148-1.2357 1.3227-2.267 2.373-2.9879 0.85207-0.58483 2.0639-1.0208 3.926-1.1017l0.00018 0.99192c0.00043 2.3846 1.9236 4.4325 4.3083 4.4325h17.242c2.3846 0 4.3127-2.0479 4.3127-4.4325v-4.2422c0-2.3846-1.9281-4.3153-4.3127-4.3153h-17.242c-2.3846 0-4.3095 1.9306-4.3083 4.3153l0.00051 0.98395c-2.2474 0.0903-3.9508 0.6357-5.2079 1.4985-1.5245 1.0464-2.3662 2.4764-3.0762 3.7637-0.70992 1.2873-1.3108 2.4408-2.2188 3.2734-0.79034 0.72475-1.8834 1.2844-3.658 1.493zm18.468-12.356h17.242c0.80514 0 1.387 0.58455 1.387 1.3897v4.2422c0 0.80514-0.5819 1.3898-1.387 1.3898h-17.242c-0.80514 0-1.4994-0.58462-1.4994-1.3898v-4.2422c0-0.80513 0.69431-1.3897 1.4994-1.3897z" fill="#fff"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.3 KiB |
BIN
editor/images/subflow_tab.png
Normal file
BIN
editor/images/subflow_tab.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 410 B |
@@ -22,6 +22,7 @@ RED.comms = (function() {
|
||||
var subscriptions = {};
|
||||
var ws;
|
||||
var pendingAuth = false;
|
||||
var reconnectAttempts = 0;
|
||||
|
||||
function connectWS() {
|
||||
var path = location.hostname;
|
||||
@@ -46,6 +47,7 @@ RED.comms = (function() {
|
||||
|
||||
ws = new WebSocket(path);
|
||||
ws.onopen = function() {
|
||||
reconnectAttempts = 0;
|
||||
if (errornotification) {
|
||||
clearErrorTimer = setTimeout(function() {
|
||||
errornotification.close();
|
||||
@@ -80,12 +82,13 @@ RED.comms = (function() {
|
||||
}
|
||||
};
|
||||
ws.onclose = function() {
|
||||
if (errornotification == null) {
|
||||
if (reconnectAttempts > 5 && errornotification == null) {
|
||||
errornotification = RED.notify(RED._("notification.error",{message:RED._("notification.errors.lostConnection")}),"error",true);
|
||||
} else if (clearErrorTimer) {
|
||||
clearTimeout(clearErrorTimer);
|
||||
clearErrorTimer = null;
|
||||
}
|
||||
reconnectAttempts++;
|
||||
setTimeout(connectWS,1000);
|
||||
}
|
||||
}
|
||||
|
@@ -15,7 +15,7 @@
|
||||
**/
|
||||
RED.history = (function() {
|
||||
var undo_history = [];
|
||||
|
||||
|
||||
return {
|
||||
//TODO: this function is a placeholder until there is a 'save' event that can be listened to
|
||||
markAllDirty: function() {
|
||||
@@ -33,6 +33,7 @@ RED.history = (function() {
|
||||
var ev = undo_history.pop();
|
||||
var i;
|
||||
var node;
|
||||
var subflow;
|
||||
var modifiedTabs = {};
|
||||
if (ev) {
|
||||
if (ev.t == 'add') {
|
||||
@@ -62,6 +63,23 @@ RED.history = (function() {
|
||||
RED.workspaces.remove(ev.subflows[i]);
|
||||
}
|
||||
}
|
||||
if (ev.subflow) {
|
||||
if (ev.subflow.instances) {
|
||||
ev.subflow.instances.forEach(function(n) {
|
||||
var node = RED.nodes.node(n.id);
|
||||
if (node) {
|
||||
node.changed = n.changed;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (ev.subflow.hasOwnProperty('changed')) {
|
||||
subflow = RED.nodes.subflow(ev.subflow.id);
|
||||
if (subflow) {
|
||||
subflow.changed = ev.subflow.changed;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (ev.t == "delete") {
|
||||
if (ev.workspaces) {
|
||||
for (i=0;i<ev.workspaces.length;i++) {
|
||||
@@ -69,10 +87,9 @@ RED.history = (function() {
|
||||
RED.workspaces.add(ev.workspaces[i]);
|
||||
}
|
||||
}
|
||||
if (ev.subflow) {
|
||||
RED.nodes.addSubflow(ev.subflow);
|
||||
if (ev.subflow && ev.subflow.subflow) {
|
||||
RED.nodes.addSubflow(ev.subflow.subflow);
|
||||
}
|
||||
var subflow;
|
||||
if (ev.subflowInputs && ev.subflowInputs.length > 0) {
|
||||
subflow = RED.nodes.subflow(ev.subflowInputs[0].z);
|
||||
subflow.in.push(ev.subflowInputs[0]);
|
||||
@@ -97,9 +114,17 @@ RED.history = (function() {
|
||||
});
|
||||
}
|
||||
}
|
||||
if (ev.subflow && ev.subflow.hasOwnProperty('instances')) {
|
||||
ev.subflow.instances.forEach(function(n) {
|
||||
var node = RED.nodes.node(n.id);
|
||||
if (node) {
|
||||
node.changed = n.changed;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (subflow) {
|
||||
RED.nodes.filterNodes({type:"subflow:"+subflow.id}).forEach(function(n) {
|
||||
n.changed = true;
|
||||
n.inputs = subflow.in.length;
|
||||
n.outputs = subflow.out.length;
|
||||
while (n.outputs > n.ports.length) {
|
||||
@@ -120,6 +145,22 @@ RED.history = (function() {
|
||||
RED.nodes.addLink(ev.links[i]);
|
||||
}
|
||||
}
|
||||
if (ev.changes) {
|
||||
for (i in ev.changes) {
|
||||
if (ev.changes.hasOwnProperty(i)) {
|
||||
node = RED.nodes.node(i);
|
||||
if (node) {
|
||||
for (var d in ev.changes[i]) {
|
||||
if (ev.changes[i].hasOwnProperty(d)) {
|
||||
node[d] = ev.changes[i][d];
|
||||
}
|
||||
}
|
||||
node.dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} else if (ev.t == "move") {
|
||||
for (i=0;i<ev.nodes.length;i++) {
|
||||
var n = ev.nodes[i];
|
||||
@@ -130,6 +171,17 @@ RED.history = (function() {
|
||||
} else if (ev.t == "edit") {
|
||||
for (i in ev.changes) {
|
||||
if (ev.changes.hasOwnProperty(i)) {
|
||||
if (ev.node._def.defaults[i].type) {
|
||||
// This is a config node property
|
||||
var currentConfigNode = RED.nodes.node(ev.node[i]);
|
||||
if (currentConfigNode) {
|
||||
currentConfigNode.users.splice(currentConfigNode.users.indexOf(ev.node),1);
|
||||
}
|
||||
var newConfigNode = RED.nodes.node(ev.changes[i]);
|
||||
if (newConfigNode) {
|
||||
newConfigNode.users.push(ev.node);
|
||||
}
|
||||
}
|
||||
ev.node[i] = ev.changes[i];
|
||||
}
|
||||
}
|
||||
@@ -148,14 +200,24 @@ RED.history = (function() {
|
||||
ev.node.out = ev.node.out.concat(ev.subflow.outputs);
|
||||
}
|
||||
}
|
||||
if (ev.subflow.hasOwnProperty('instances')) {
|
||||
ev.subflow.instances.forEach(function(n) {
|
||||
var node = RED.nodes.node(n.id);
|
||||
if (node) {
|
||||
node.changed = n.changed;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
RED.nodes.filterNodes({type:"subflow:"+ev.node.id}).forEach(function(n) {
|
||||
n.changed = ev.changed;
|
||||
n.inputs = ev.node.in.length;
|
||||
n.outputs = ev.node.out.length;
|
||||
RED.editor.updateNodeProperties(n);
|
||||
});
|
||||
|
||||
RED.palette.refresh();
|
||||
|
||||
if (ev.node.type === 'subflow') {
|
||||
$("#menu-item-workspace-menu-"+ev.node.id.replace(".","-")).text(ev.node.name);
|
||||
}
|
||||
} else {
|
||||
RED.editor.updateNodeProperties(ev.node);
|
||||
RED.editor.validateNode(ev.node);
|
||||
@@ -169,7 +231,7 @@ RED.history = (function() {
|
||||
ev.node.changed = ev.changed;
|
||||
} else if (ev.t == "createSubflow") {
|
||||
if (ev.nodes) {
|
||||
RED.nodes.filterNodes({z:ev.subflow.id}).forEach(function(n) {
|
||||
RED.nodes.filterNodes({z:ev.subflow.subflow.id}).forEach(function(n) {
|
||||
n.z = ev.activeWorkspace;
|
||||
n.dirty = true;
|
||||
});
|
||||
@@ -182,10 +244,10 @@ RED.history = (function() {
|
||||
RED.nodes.removeLink(ev.links[i]);
|
||||
}
|
||||
}
|
||||
|
||||
RED.nodes.removeSubflow(ev.subflow);
|
||||
RED.workspaces.remove(ev.subflow);
|
||||
|
||||
|
||||
RED.nodes.removeSubflow(ev.subflow.subflow);
|
||||
RED.workspaces.remove(ev.subflow.subflow);
|
||||
|
||||
if (ev.removedLinks) {
|
||||
for (i=0;i<ev.removedLinks.length;i++) {
|
||||
RED.nodes.addLink(ev.removedLinks[i]);
|
||||
@@ -199,10 +261,10 @@ RED.history = (function() {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
RED.nodes.dirty(ev.dirty);
|
||||
RED.view.redraw(true);
|
||||
RED.palette.refresh();
|
||||
RED.workspaces.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -13,9 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
|
||||
RED.i18n = (function() {
|
||||
|
||||
|
||||
return {
|
||||
init: function(done) {
|
||||
i18n.init({
|
||||
@@ -26,7 +26,8 @@ RED.i18n = (function() {
|
||||
namespaces: ["editor","node-red"],
|
||||
defaultNs: "editor"
|
||||
},
|
||||
fallbackLng: ['en-US']
|
||||
fallbackLng: ['en-US'],
|
||||
useCookie: false
|
||||
},function() {
|
||||
done();
|
||||
});
|
||||
@@ -40,6 +41,3 @@ RED.i18n = (function() {
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
|
||||
|
@@ -176,7 +176,7 @@ var RED = (function() {
|
||||
{id:"menu-item-subflow-convert",label:RED._("menu.label.selectionToSubflow"),disabled:true,onselect:RED.subflow.convertToSubflow},
|
||||
]},
|
||||
null,
|
||||
{id:"menu-item-workspace",label:RED._("menu.label.workspaces"),options:[
|
||||
{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},
|
||||
|
@@ -156,7 +156,6 @@ RED.nodes = (function() {
|
||||
}
|
||||
if (n._def.category == "config") {
|
||||
configNodes[n.id] = n;
|
||||
RED.sidebar.config.refresh();
|
||||
} else {
|
||||
n.dirty = true;
|
||||
var updatedConfigNode = false;
|
||||
@@ -176,7 +175,7 @@ RED.nodes = (function() {
|
||||
}
|
||||
}
|
||||
if (updatedConfigNode) {
|
||||
RED.sidebar.config.refresh();
|
||||
// TODO: refresh config tab?
|
||||
}
|
||||
if (n._def.category == "subflows" && typeof n.i === "undefined") {
|
||||
var nextId = 0;
|
||||
@@ -215,7 +214,7 @@ RED.nodes = (function() {
|
||||
if (id in configNodes) {
|
||||
node = configNodes[id];
|
||||
delete configNodes[id];
|
||||
RED.sidebar.config.refresh();
|
||||
RED.workspaces.refresh();
|
||||
} else {
|
||||
node = getNode(id);
|
||||
if (node) {
|
||||
@@ -245,11 +244,11 @@ RED.nodes = (function() {
|
||||
}
|
||||
}
|
||||
if (updatedConfigNode) {
|
||||
RED.sidebar.config.refresh();
|
||||
RED.workspaces.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node._def.onremove) {
|
||||
if (node && node._def.onremove) {
|
||||
node._def.onremove.call(n);
|
||||
}
|
||||
return {links:removedLinks,nodes:removedNodes};
|
||||
@@ -273,15 +272,24 @@ RED.nodes = (function() {
|
||||
var removedNodes = [];
|
||||
var removedLinks = [];
|
||||
var n;
|
||||
var node;
|
||||
for (n=0;n<nodes.length;n++) {
|
||||
var node = nodes[n];
|
||||
node = nodes[n];
|
||||
if (node.z == id) {
|
||||
removedNodes.push(node);
|
||||
}
|
||||
}
|
||||
for(n in configNodes) {
|
||||
if (configNodes.hasOwnProperty(n)) {
|
||||
node = configNodes[n];
|
||||
if (node.z == id) {
|
||||
removedNodes.push(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (n=0;n<removedNodes.length;n++) {
|
||||
var rmlinks = removeNode(removedNodes[n].id);
|
||||
removedLinks = removedLinks.concat(rmlinks);
|
||||
var result = removeNode(removedNodes[n].id);
|
||||
removedLinks = removedLinks.concat(result.links);
|
||||
}
|
||||
return {nodes:removedNodes,links:removedLinks};
|
||||
}
|
||||
@@ -307,6 +315,7 @@ RED.nodes = (function() {
|
||||
subflows[sf.id] = sf;
|
||||
RED.nodes.registerType("subflow:"+sf.id, {
|
||||
defaults:{name:{value:""}},
|
||||
info: sf.info,
|
||||
icon:"subflow.png",
|
||||
category: "subflows",
|
||||
inputs: sf.in.length,
|
||||
@@ -382,6 +391,7 @@ RED.nodes = (function() {
|
||||
var node = {};
|
||||
node.id = n.id;
|
||||
node.type = n.type;
|
||||
node.z = n.z;
|
||||
if (node.type == "unknown") {
|
||||
for (var p in n._orig) {
|
||||
if (n._orig.hasOwnProperty(p)) {
|
||||
@@ -417,7 +427,6 @@ RED.nodes = (function() {
|
||||
if (n._def.category != "config") {
|
||||
node.x = n.x;
|
||||
node.y = n.y;
|
||||
node.z = n.z;
|
||||
node.wires = [];
|
||||
for(var i=0;i<n.outputs;i++) {
|
||||
node.wires.push([]);
|
||||
@@ -438,6 +447,7 @@ RED.nodes = (function() {
|
||||
node.id = n.id;
|
||||
node.type = n.type;
|
||||
node.name = n.name;
|
||||
node.info = n.info;
|
||||
node.in = [];
|
||||
node.out = [];
|
||||
|
||||
@@ -545,8 +555,11 @@ RED.nodes = (function() {
|
||||
return nns;
|
||||
}
|
||||
|
||||
function compareNodes(nodeA,nodeB) {
|
||||
if (nodeA.id != nodeB.id || nodeA.type != nodeB.type) {
|
||||
function compareNodes(nodeA,nodeB,idMustMatch) {
|
||||
if (idMustMatch && nodeA.id != nodeB.id) {
|
||||
return false;
|
||||
}
|
||||
if (nodeA.type != nodeB.type) {
|
||||
return false;
|
||||
}
|
||||
var def = nodeA._def;
|
||||
@@ -685,26 +698,6 @@ RED.nodes = (function() {
|
||||
});
|
||||
new_subflows.push(n);
|
||||
addSubflow(n,createNewIds);
|
||||
} else {
|
||||
def = registry.getNodeType(n.type);
|
||||
if (def && def.category == "config") {
|
||||
var existingConfigNode = RED.nodes.node(n.id);
|
||||
if (!existingConfigNode || !compareNodes(existingConfigNode,n) || existingConfigNode._def.exclusive) {
|
||||
var configNode = {id:n.id,type:n.type,users:[]};
|
||||
for (var d in def.defaults) {
|
||||
if (def.defaults.hasOwnProperty(d)) {
|
||||
configNode[d] = n[d];
|
||||
}
|
||||
}
|
||||
configNode.label = def.label;
|
||||
configNode._def = def;
|
||||
if (existingConfigNode || createNewIds) {
|
||||
configNode.id = getID();
|
||||
}
|
||||
node_map[n.id] = configNode;
|
||||
RED.nodes.add(configNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (defaultWorkspace == null) {
|
||||
@@ -715,6 +708,60 @@ RED.nodes = (function() {
|
||||
activeWorkspace = RED.workspaces.active();
|
||||
}
|
||||
|
||||
for (i=0;i<newNodes.length;i++) {
|
||||
n = newNodes[i];
|
||||
def = registry.getNodeType(n.type);
|
||||
if (def && def.category == "config") {
|
||||
var existingConfigNode = null;
|
||||
if (createNewIds) {
|
||||
if (n.z) {
|
||||
if (subflow_map[n.z]) {
|
||||
n.z = subflow_map[n.z].id;
|
||||
} else {
|
||||
n.z = workspace_map[n.z];
|
||||
if (!workspaces[n.z]) {
|
||||
n.z = activeWorkspace;
|
||||
}
|
||||
}
|
||||
}
|
||||
existingConfigNode = RED.nodes.node(n.id);
|
||||
if (existingConfigNode) {
|
||||
if (n.z && existingConfigNode.z !== n.z) {
|
||||
existingConfigNode = null;
|
||||
// Check the config nodes on n.z
|
||||
for (var cn in configNodes) {
|
||||
if (configNodes.hasOwnProperty(cn)) {
|
||||
if (configNodes[cn].z === n.z && compareNodes(configNodes[cn],n,false)) {
|
||||
existingConfigNode = configNodes[cn];
|
||||
node_map[n.id] = configNodes[cn];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!existingConfigNode) { //} || !compareNodes(existingConfigNode,n,true) || existingConfigNode._def.exclusive || existingConfigNode.z !== n.z) {
|
||||
var configNode = {id:n.id, z:n.z, type:n.type, users:[]};
|
||||
for (var d in def.defaults) {
|
||||
if (def.defaults.hasOwnProperty(d)) {
|
||||
configNode[d] = n[d];
|
||||
}
|
||||
}
|
||||
configNode.label = def.label;
|
||||
configNode._def = def;
|
||||
if (createNewIds) {
|
||||
configNode.id = getID();
|
||||
}
|
||||
node_map[n.id] = configNode;
|
||||
new_nodes.push(configNode);
|
||||
RED.nodes.add(configNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0;i<newNodes.length;i++) {
|
||||
n = newNodes[i];
|
||||
// TODO: remove workspace in next release+1
|
||||
@@ -809,17 +856,19 @@ RED.nodes = (function() {
|
||||
}
|
||||
for (i=0;i<new_nodes.length;i++) {
|
||||
n = new_nodes[i];
|
||||
for (var w1=0;w1<n.wires.length;w1++) {
|
||||
var wires = (n.wires[w1] instanceof Array)?n.wires[w1]:[n.wires[w1]];
|
||||
for (var w2=0;w2<wires.length;w2++) {
|
||||
if (wires[w2] in node_map) {
|
||||
var link = {source:n,sourcePort:w1,target:node_map[wires[w2]]};
|
||||
addLink(link);
|
||||
new_links.push(link);
|
||||
if (n.wires) {
|
||||
for (var w1=0;w1<n.wires.length;w1++) {
|
||||
var wires = (n.wires[w1] instanceof Array)?n.wires[w1]:[n.wires[w1]];
|
||||
for (var w2=0;w2<wires.length;w2++) {
|
||||
if (wires[w2] in node_map) {
|
||||
var link = {source:n,sourcePort:w1,target:node_map[wires[w2]]};
|
||||
addLink(link);
|
||||
new_links.push(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
delete n.wires;
|
||||
}
|
||||
delete n.wires;
|
||||
}
|
||||
for (i=0;i<new_subflows.length;i++) {
|
||||
n = new_subflows[i];
|
||||
@@ -846,6 +895,7 @@ RED.nodes = (function() {
|
||||
});
|
||||
}
|
||||
|
||||
RED.workspaces.refresh();
|
||||
return [new_nodes,new_links,new_workspaces,new_subflows];
|
||||
}
|
||||
|
||||
@@ -947,6 +997,13 @@ RED.nodes = (function() {
|
||||
}
|
||||
}
|
||||
},
|
||||
eachWorkspace: function(cb) {
|
||||
for (var id in workspaces) {
|
||||
if (workspaces.hasOwnProperty(id)) {
|
||||
cb(workspaces[id]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
node: getNode,
|
||||
|
||||
|
@@ -103,7 +103,7 @@ RED.deploy = (function() {
|
||||
],
|
||||
create: function() {
|
||||
$("#node-dialog-confirm-deploy").parent().find("div.ui-dialog-buttonpane")
|
||||
.append('<div style="height:0; vertical-align: middle; display:inline-block;">'+
|
||||
.prepend('<div style="height:0; vertical-align: middle; display:inline-block; margin-top: 13px; float:left;">'+
|
||||
'<input style="vertical-align:top;" type="checkbox" id="node-dialog-confirm-deploy-hide">'+
|
||||
'<label style="display:inline;" for="node-dialog-confirm-deploy-hide"> do not warn about this again</label>'+
|
||||
'<input type="hidden" id="node-dialog-confirm-deploy-type">'+
|
||||
@@ -124,6 +124,36 @@ RED.deploy = (function() {
|
||||
});
|
||||
}
|
||||
|
||||
function getNodeInfo(node) {
|
||||
var tabLabel = "";
|
||||
if (node.z) {
|
||||
var tab = RED.nodes.workspace(node.z);
|
||||
if (!tab) {
|
||||
tab = RED.nodes.subflow(node.z);
|
||||
tabLabel = tab.name;
|
||||
} else {
|
||||
tabLabel = tab.label;
|
||||
}
|
||||
}
|
||||
var label = "";
|
||||
if (typeof node._def.label == "function") {
|
||||
label = node._def.label.call(node);
|
||||
} else {
|
||||
label = node._def.label;
|
||||
}
|
||||
label = label || node.id;
|
||||
return {tab:tabLabel,type:node.type,label:label};
|
||||
}
|
||||
function sortNodeInfo(A,B) {
|
||||
if (A.tab < B.tab) { return -1;}
|
||||
if (A.tab > B.tab) { return 1;}
|
||||
if (A.type < B.type) { return -1;}
|
||||
if (A.type > B.type) { return 1;}
|
||||
if (A.name < B.name) { return -1;}
|
||||
if (A.name > B.name) { return 1;}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function save(force) {
|
||||
if (RED.nodes.dirty()) {
|
||||
//$("#debug-tab-clear").click(); // uncomment this to auto clear debug on deploy
|
||||
@@ -134,8 +164,13 @@ RED.deploy = (function() {
|
||||
var hasUnusedConfig = false;
|
||||
|
||||
var unknownNodes = [];
|
||||
var invalidNodes = [];
|
||||
|
||||
RED.nodes.eachNode(function(node) {
|
||||
hasInvalid = hasInvalid || !node.valid;
|
||||
if (!node.valid) {
|
||||
invalidNodes.push(getNodeInfo(node));
|
||||
}
|
||||
if (node.type === "unknown") {
|
||||
if (unknownNodes.indexOf(node.name) == -1) {
|
||||
unknownNodes.push(node.name);
|
||||
@@ -144,18 +179,10 @@ RED.deploy = (function() {
|
||||
});
|
||||
hasUnknown = unknownNodes.length > 0;
|
||||
|
||||
var unusedConfigNodes = {};
|
||||
var unusedConfigNodes = [];
|
||||
RED.nodes.eachConfig(function(node) {
|
||||
if (node.users.length === 0) {
|
||||
var label = "";
|
||||
if (typeof node._def.label == "function") {
|
||||
label = node._def.label.call(node);
|
||||
} else {
|
||||
label = node._def.label;
|
||||
}
|
||||
label = label || node.id;
|
||||
unusedConfigNodes[node.type] = unusedConfigNodes[node.type] || [];
|
||||
unusedConfigNodes[node.type].push(label);
|
||||
unusedConfigNodes.push(getNodeInfo(node));
|
||||
hasUnusedConfig = true;
|
||||
}
|
||||
});
|
||||
@@ -176,19 +203,18 @@ RED.deploy = (function() {
|
||||
showWarning = true;
|
||||
$( "#node-dialog-confirm-deploy-type" ).val("invalid");
|
||||
$( "#node-dialog-confirm-deploy-config" ).show();
|
||||
invalidNodes.sort(sortNodeInfo);
|
||||
$( "#node-dialog-confirm-deploy-invalid-list" )
|
||||
.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();
|
||||
var unusedNodeLabels = [];
|
||||
var unusedTypes = Object.keys(unusedConfigNodes).sort();
|
||||
unusedTypes.forEach(function(type) {
|
||||
unusedConfigNodes[type].forEach(function(label) {
|
||||
unusedNodeLabels.push(type+": "+label);
|
||||
});
|
||||
});
|
||||
|
||||
unusedConfigNodes.sort(sortNodeInfo);
|
||||
$( "#node-dialog-confirm-deploy-unused-list" )
|
||||
.html("<li>"+unusedNodeLabels.join("</li><li>")+"</li>");
|
||||
.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);
|
||||
|
@@ -15,6 +15,8 @@
|
||||
**/
|
||||
RED.editor = (function() {
|
||||
var editing_node = null;
|
||||
var editing_config_node = null;
|
||||
var subflowEditor;
|
||||
|
||||
function getCredentialsURL(nodeType, nodeID) {
|
||||
var dashedType = nodeType.replace(/\s+/g, '-');
|
||||
@@ -33,7 +35,6 @@ RED.editor = (function() {
|
||||
var subflow;
|
||||
var isValid;
|
||||
var hasChanged;
|
||||
|
||||
if (node.type.indexOf("subflow:")===0) {
|
||||
subflow = RED.nodes.subflow(node.type.substring(8));
|
||||
isValid = subflow.valid;
|
||||
@@ -43,7 +44,7 @@ RED.editor = (function() {
|
||||
hasChanged = subflow.changed;
|
||||
}
|
||||
node.valid = isValid;
|
||||
node.changed = hasChanged;
|
||||
node.changed = node.changed || hasChanged;
|
||||
} else if (node._def) {
|
||||
node.valid = validateNodeProperties(node, node._def.defaults, node);
|
||||
if (node._def._creds) {
|
||||
@@ -65,7 +66,7 @@ RED.editor = (function() {
|
||||
var modifiedTabs = {};
|
||||
for (i=0;i<subflowInstances.length;i++) {
|
||||
subflowInstances[i].valid = node.valid;
|
||||
subflowInstances[i].changed = node.changed;
|
||||
subflowInstances[i].changed = subflowInstances[i].changed || node.changed;
|
||||
subflowInstances[i].dirty = true;
|
||||
modifiedTabs[subflowInstances[i].z] = true;
|
||||
}
|
||||
@@ -270,7 +271,37 @@ RED.editor = (function() {
|
||||
var wasChanged = editing_node.changed;
|
||||
editing_node.changed = true;
|
||||
RED.nodes.dirty(true);
|
||||
RED.history.push({t:'edit',node:editing_node,changes:changes,links:removedLinks,dirty:wasDirty,changed:wasChanged});
|
||||
|
||||
var activeSubflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
var subflowInstances = null;
|
||||
if (activeSubflow) {
|
||||
subflowInstances = [];
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.type == "subflow:"+RED.workspaces.active()) {
|
||||
subflowInstances.push({
|
||||
id:n.id,
|
||||
changed:n.changed
|
||||
});
|
||||
n.changed = true;
|
||||
n.dirty = true;
|
||||
updateNodeProperties(n);
|
||||
}
|
||||
});
|
||||
}
|
||||
var historyEvent = {
|
||||
t:'edit',
|
||||
node:editing_node,
|
||||
changes:changes,
|
||||
links:removedLinks,
|
||||
dirty:wasDirty,
|
||||
changed:wasChanged
|
||||
};
|
||||
if (subflowInstances) {
|
||||
historyEvent.subflow = {
|
||||
instances:subflowInstances
|
||||
}
|
||||
}
|
||||
RED.history.push(historyEvent);
|
||||
}
|
||||
editing_node.dirty = true;
|
||||
validateNode(editing_node);
|
||||
@@ -303,6 +334,28 @@ RED.editor = (function() {
|
||||
if (editing_node._def.oneditcancel) {
|
||||
editing_node._def.oneditcancel.call(editing_node);
|
||||
}
|
||||
|
||||
for (var d in editing_node._def.defaults) {
|
||||
if (editing_node._def.defaults.hasOwnProperty(d)) {
|
||||
var def = editing_node._def.defaults[d];
|
||||
if (def.type) {
|
||||
var configTypeDef = RED.nodes.getType(def.type);
|
||||
if (configTypeDef && configTypeDef.exclusive) {
|
||||
var input = $("#node-input-"+d).val()||"";
|
||||
if (input !== "" && !editing_node[d]) {
|
||||
// This node has an exclusive config node that
|
||||
// has just been added. As the user is cancelling
|
||||
// the edit, need to delete the just-added config
|
||||
// node so that it doesn't get orphaned.
|
||||
RED.nodes.remove(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
@@ -340,7 +393,7 @@ RED.editor = (function() {
|
||||
if (editing_node) {
|
||||
RED.sidebar.info.refresh(editing_node);
|
||||
}
|
||||
RED.sidebar.config.refresh();
|
||||
RED.workspaces.refresh();
|
||||
|
||||
var buttons = $( this ).dialog("option","buttons");
|
||||
if (buttons.length == 3) {
|
||||
@@ -521,10 +574,15 @@ RED.editor = (function() {
|
||||
if (definition.defaults.hasOwnProperty(d)) {
|
||||
if (definition.defaults[d].type) {
|
||||
var configTypeDef = RED.nodes.getType(definition.defaults[d].type);
|
||||
if (configTypeDef.exclusive) {
|
||||
prepareConfigNodeButton(node,d,definition.defaults[d].type);
|
||||
if (configTypeDef) {
|
||||
if (configTypeDef.exclusive) {
|
||||
prepareConfigNodeButton(node,d,definition.defaults[d].type);
|
||||
} else {
|
||||
prepareConfigNodeSelect(node,d,definition.defaults[d].type);
|
||||
}
|
||||
} else {
|
||||
prepareConfigNodeSelect(node,d,definition.defaults[d].type);
|
||||
console.log("Unknown type:", definition.defaults[d].type);
|
||||
preparePropertyEditor(node,d,prefix);
|
||||
}
|
||||
} else {
|
||||
preparePropertyEditor(node,d,prefix);
|
||||
@@ -587,16 +645,20 @@ RED.editor = (function() {
|
||||
}
|
||||
$("#dialog-form").find('[data-i18n]').each(function() {
|
||||
var current = $(this).attr("data-i18n");
|
||||
if (current.indexOf(":") === -1) {
|
||||
var prefix = "";
|
||||
if (current.indexOf("[")===0) {
|
||||
var parts = current.split("]");
|
||||
prefix = parts[0]+"]";
|
||||
current = parts[1];
|
||||
var keys = current.split(";");
|
||||
for (var i=0;i<keys.length;i++) {
|
||||
var key = keys[i];
|
||||
if (key.indexOf(":") === -1) {
|
||||
var prefix = "";
|
||||
if (key.indexOf("[")===0) {
|
||||
var parts = key.split("]");
|
||||
prefix = parts[0]+"]";
|
||||
key = parts[1];
|
||||
}
|
||||
keys[i] = prefix+ns+":"+key;
|
||||
}
|
||||
|
||||
$(this).attr("data-i18n",prefix+ns+":"+current);
|
||||
}
|
||||
$(this).attr("data-i18n",keys.join(";"));
|
||||
});
|
||||
$('<input type="text" style="display: none;" />').appendTo("#dialog-form");
|
||||
prepareEditDialog(node,node._def,"node-input");
|
||||
@@ -607,7 +669,7 @@ RED.editor = (function() {
|
||||
function showEditConfigNodeDialog(name,type,id) {
|
||||
var adding = (id == "_ADD_");
|
||||
var node_def = RED.nodes.getType(type);
|
||||
var configNode = RED.nodes.node(id);
|
||||
editing_config_node = RED.nodes.node(id);
|
||||
|
||||
var ns;
|
||||
if (node_def.set.module === "node-red") {
|
||||
@@ -616,22 +678,28 @@ RED.editor = (function() {
|
||||
ns = node_def.set.id;
|
||||
}
|
||||
|
||||
var activeWorkspace = RED.nodes.workspace(RED.workspaces.active());
|
||||
if (!activeWorkspace) {
|
||||
activeWorkspace = RED.nodes.subflow(RED.workspaces.active());
|
||||
}
|
||||
|
||||
if (configNode == null) {
|
||||
configNode = {
|
||||
if (editing_config_node == null) {
|
||||
editing_config_node = {
|
||||
id: (1+Math.random()*4294967295).toString(16),
|
||||
_def: node_def,
|
||||
type: type
|
||||
type: type,
|
||||
z: activeWorkspace.id,
|
||||
users: []
|
||||
}
|
||||
for (var d in node_def.defaults) {
|
||||
if (node_def.defaults[d].value) {
|
||||
configNode[d] = node_def.defaults[d].value;
|
||||
editing_config_node[d] = node_def.defaults[d].value;
|
||||
}
|
||||
}
|
||||
configNode["_"] = node_def._;
|
||||
editing_config_node["_"] = node_def._;
|
||||
}
|
||||
|
||||
$("#dialog-config-form").html($("script[data-template-name='"+type+"']").html());
|
||||
$("#node-config-dialog-edit-form").html($("script[data-template-name='"+type+"']").html());
|
||||
|
||||
$("#dialog-config-form").find('[data-i18n]').each(function() {
|
||||
var current = $(this).attr("data-i18n");
|
||||
@@ -647,7 +715,7 @@ RED.editor = (function() {
|
||||
});
|
||||
|
||||
|
||||
prepareEditDialog(configNode,node_def,"node-config-input");
|
||||
prepareEditDialog(editing_config_node,node_def,"node-config-input");
|
||||
|
||||
var buttons = $( "#node-config-dialog" ).dialog("option","buttons");
|
||||
if (adding) {
|
||||
@@ -655,7 +723,7 @@ RED.editor = (function() {
|
||||
buttons = buttons.splice(1);
|
||||
}
|
||||
buttons[0].text = "Add";
|
||||
$("#node-config-dialog-user-count").html("").hide();
|
||||
$("#node-config-dialog-user-count").find("span").html("").parent().hide();
|
||||
} else {
|
||||
if (buttons.length == 2) {
|
||||
buttons.unshift({
|
||||
@@ -665,21 +733,34 @@ RED.editor = (function() {
|
||||
var configProperty = $(this).dialog('option','node-property');
|
||||
var configId = $(this).dialog('option','node-id');
|
||||
var configType = $(this).dialog('option','node-type');
|
||||
var configNode = RED.nodes.node(configId);
|
||||
|
||||
var configTypeDef = RED.nodes.getType(configType);
|
||||
|
||||
if (configTypeDef.ondelete) {
|
||||
configTypeDef.ondelete.call(RED.nodes.node(configId));
|
||||
configTypeDef.ondelete.call(editing_config_node);
|
||||
}
|
||||
if (configTypeDef.oneditdelete) {
|
||||
configTypeDef.oneditdelete.call(RED.nodes.node(configId));
|
||||
configTypeDef.oneditdelete.call(editing_config_node);
|
||||
}
|
||||
var historyEvent = {
|
||||
t:'delete',
|
||||
nodes:[editing_config_node],
|
||||
changes: {},
|
||||
dirty: RED.nodes.dirty()
|
||||
}
|
||||
RED.nodes.remove(configId);
|
||||
for (var i=0;i<configNode.users.length;i++) {
|
||||
var user = configNode.users[i];
|
||||
for (var i=0;i<editing_config_node.users.length;i++) {
|
||||
var user = editing_config_node.users[i];
|
||||
historyEvent.changes[user.id] = {
|
||||
changed: user.changed,
|
||||
valid: user.valid
|
||||
};
|
||||
for (var d in user._def.defaults) {
|
||||
if (user._def.defaults.hasOwnProperty(d) && user[d] == configId) {
|
||||
historyEvent.changes[user.id][d] = configId
|
||||
user[d] = "";
|
||||
user.changed = true;
|
||||
user.dirty = true;
|
||||
}
|
||||
}
|
||||
validateNode(user);
|
||||
@@ -688,12 +769,65 @@ RED.editor = (function() {
|
||||
RED.nodes.dirty(true);
|
||||
$( this ).dialog( "close" );
|
||||
RED.view.redraw();
|
||||
RED.history.push(historyEvent);
|
||||
}
|
||||
});
|
||||
}
|
||||
buttons[1].text = "Update";
|
||||
$("#node-config-dialog-user-count").html(RED._("editor.nodesUse", {count:configNode.users.length})).show();
|
||||
$("#node-config-dialog-user-count").find("span").html(RED._("editor.nodesUse", {count:editing_config_node.users.length})).parent().show();
|
||||
}
|
||||
|
||||
if (editing_config_node._def.exclusive) {
|
||||
$("#node-config-dialog-scope").hide();
|
||||
} else {
|
||||
$("#node-config-dialog-scope").show();
|
||||
}
|
||||
$("#node-config-dialog-scope-warning").hide();
|
||||
|
||||
|
||||
var nodeUserFlows = {};
|
||||
editing_config_node.users.forEach(function(n) {
|
||||
nodeUserFlows[n.z] = true;
|
||||
});
|
||||
var flowCount = Object.keys(nodeUserFlows).length;
|
||||
|
||||
var tabSelect = $("#node-config-dialog-scope").empty();
|
||||
tabSelect.off("change");
|
||||
tabSelect.append('<option value=""'+(!editing_config_node.z?" selected":"")+' data-i18n="sidebar.config.global"></option>');
|
||||
tabSelect.append('<option disabled data-i18n="sidebar.config.flows"></option>');
|
||||
RED.nodes.eachWorkspace(function(ws) {
|
||||
var workspaceLabel = ws.label;
|
||||
if (nodeUserFlows[ws.id]) {
|
||||
workspaceLabel = "* "+workspaceLabel;
|
||||
}
|
||||
tabSelect.append('<option value="'+ws.id+'"'+(ws.id==editing_config_node.z?" selected":"")+'>'+workspaceLabel+'</option>');
|
||||
});
|
||||
tabSelect.append('<option disabled data-i18n="sidebar.config.subflows"></option>');
|
||||
RED.nodes.eachSubflow(function(ws) {
|
||||
var workspaceLabel = ws.name;
|
||||
if (nodeUserFlows[ws.id]) {
|
||||
workspaceLabel = "* "+workspaceLabel;
|
||||
}
|
||||
tabSelect.append('<option value="'+ws.id+'"'+(ws.id==editing_config_node.z?" selected":"")+'>'+workspaceLabel+'</option>');
|
||||
});
|
||||
if (flowCount > 0) {
|
||||
tabSelect.on('change',function() {
|
||||
var newScope = $(this).val();
|
||||
if (newScope === '') {
|
||||
// global scope - everyone can use it
|
||||
$("#node-config-dialog-scope-warning").hide();
|
||||
} else if (!nodeUserFlows[newScope] || flowCount > 1) {
|
||||
// a user will loose access to it
|
||||
$("#node-config-dialog-scope-warning").show();
|
||||
} else {
|
||||
$("#node-config-dialog-scope-warning").hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//tabSelect.append('<option value="'+activeWorkspace.id+'"'+(activeWorkspace.id==configNode.z?" selected":"")+'>'+workspaceLabel+'</option>');
|
||||
tabSelect.i18n();
|
||||
|
||||
$( "#node-config-dialog" ).dialog("option","buttons",buttons);
|
||||
|
||||
$("#node-config-dialog").i18n();
|
||||
@@ -701,7 +835,7 @@ RED.editor = (function() {
|
||||
$( "#node-config-dialog" )
|
||||
.dialog("option","node-adding",adding)
|
||||
.dialog("option","node-property",name)
|
||||
.dialog("option","node-id",configNode.id)
|
||||
.dialog("option","node-id",editing_config_node.id)
|
||||
.dialog("option","node-type",type)
|
||||
.dialog("option","title",(adding?RED._("editor.addNewConfig", {type:type}):RED._("editor.editConfig", {type:type})))
|
||||
.dialog( "open" );
|
||||
@@ -721,17 +855,39 @@ RED.editor = (function() {
|
||||
var select = $("#node-input-"+name);
|
||||
var node_def = RED.nodes.getType(type);
|
||||
select.children().remove();
|
||||
|
||||
var activeWorkspace = RED.nodes.workspace(RED.workspaces.active());
|
||||
if (!activeWorkspace) {
|
||||
activeWorkspace = RED.nodes.subflow(RED.workspaces.active());
|
||||
}
|
||||
|
||||
var configNodes = [];
|
||||
|
||||
RED.nodes.eachConfig(function(config) {
|
||||
if (config.type == type) {
|
||||
if (config.type == type && (!config.z || config.z === activeWorkspace.id)) {
|
||||
var label = "";
|
||||
if (typeof node_def.label == "function") {
|
||||
label = node_def.label.call(config);
|
||||
} else {
|
||||
label = node_def.label;
|
||||
}
|
||||
select.append('<option value="'+config.id+'"'+(value==config.id?" selected":"")+'>'+label+'</option>');
|
||||
configNodes.push({id:config.id,label:label});
|
||||
}
|
||||
});
|
||||
|
||||
configNodes.sort(function(A,B) {
|
||||
if (A.label < B.label) {
|
||||
return -1;
|
||||
} else if (A.label > B.label) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
configNodes.forEach(function(cn) {
|
||||
select.append('<option value="'+cn.id+'"'+(value==cn.id?" selected":"")+'>'+cn.label+'</option>');
|
||||
});
|
||||
|
||||
select.append('<option value="_ADD_"'+(value===""?" selected":"")+'>'+RED._("editor.addNewType", {type:type})+'</option>');
|
||||
window.setTimeout(function() { select.change();},50);
|
||||
}
|
||||
@@ -755,53 +911,62 @@ RED.editor = (function() {
|
||||
var configType = $(this).dialog('option','node-type');
|
||||
var configAdding = $(this).dialog('option','node-adding');
|
||||
var configTypeDef = RED.nodes.getType(configType);
|
||||
var configNode;
|
||||
var d;
|
||||
var input;
|
||||
var scope = $("#node-config-dialog-scope").val();
|
||||
for (d in configTypeDef.defaults) {
|
||||
if (configTypeDef.defaults.hasOwnProperty(d)) {
|
||||
input = $("#node-config-input-"+d);
|
||||
if (input.attr('type') === "checkbox") {
|
||||
editing_config_node[d] = input.prop('checked');
|
||||
} else {
|
||||
editing_config_node[d] = input.val();
|
||||
}
|
||||
}
|
||||
}
|
||||
editing_config_node.label = configTypeDef.label;
|
||||
editing_config_node.z = scope;
|
||||
|
||||
if (scope) {
|
||||
editing_config_node.users = editing_config_node.users.filter(function(n) {
|
||||
var keep = true;
|
||||
for (var d in n._def.defaults) {
|
||||
if (n._def.defaults.hasOwnProperty(d)) {
|
||||
if (n._def.defaults[d].type === editing_config_node.type &&
|
||||
n[d] === editing_config_node.id &&
|
||||
n.z !== scope) {
|
||||
keep = false;
|
||||
n[d] = null;
|
||||
n.dirty = true;
|
||||
n.changed = true;
|
||||
validateNode(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
return keep;
|
||||
});
|
||||
}
|
||||
|
||||
if (configAdding) {
|
||||
configNode = {type:configType,id:configId,users:[]};
|
||||
for (d in configTypeDef.defaults) {
|
||||
if (configTypeDef.defaults.hasOwnProperty(d)) {
|
||||
input = $("#node-config-input-"+d);
|
||||
if (input.attr('type') === "checkbox") {
|
||||
configNode[d] = input.prop('checked');
|
||||
} else {
|
||||
configNode[d] = input.val();
|
||||
}
|
||||
}
|
||||
}
|
||||
configNode.label = configTypeDef.label;
|
||||
configNode._def = configTypeDef;
|
||||
RED.nodes.add(configNode);
|
||||
updateConfigNodeSelect(configProperty,configType,configNode.id);
|
||||
} else {
|
||||
configNode = RED.nodes.node(configId);
|
||||
for (d in configTypeDef.defaults) {
|
||||
if (configTypeDef.defaults.hasOwnProperty(d)) {
|
||||
input = $("#node-config-input-"+d);
|
||||
if (input.attr('type') === "checkbox") {
|
||||
configNode[d] = input.prop('checked');
|
||||
} else {
|
||||
configNode[d] = input.val();
|
||||
}
|
||||
}
|
||||
}
|
||||
updateConfigNodeSelect(configProperty,configType,configId);
|
||||
RED.nodes.add(editing_config_node);
|
||||
}
|
||||
|
||||
updateConfigNodeSelect(configProperty,configType,editing_config_node.id);
|
||||
|
||||
if (configTypeDef.credentials) {
|
||||
updateNodeCredentials(configNode,configTypeDef.credentials,"node-config-input");
|
||||
updateNodeCredentials(editing_config_node,configTypeDef.credentials,"node-config-input");
|
||||
}
|
||||
if (configTypeDef.oneditsave) {
|
||||
configTypeDef.oneditsave.call(RED.nodes.node(configId));
|
||||
configTypeDef.oneditsave.call(editing_config_node);
|
||||
}
|
||||
validateNode(configNode);
|
||||
for (var i=0;i<configNode.users.length;i++) {
|
||||
var user = configNode.users[i];
|
||||
validateNode(editing_config_node);
|
||||
for (var i=0;i<editing_config_node.users.length;i++) {
|
||||
var user = editing_config_node.users[i];
|
||||
validateNode(user);
|
||||
}
|
||||
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw(true);
|
||||
$(this).dialog("close");
|
||||
|
||||
}
|
||||
@@ -844,11 +1009,20 @@ RED.editor = (function() {
|
||||
close: function(e) {
|
||||
$(this).dialog('option','width','auto');
|
||||
$(this).dialog('option','height','auto');
|
||||
$("#dialog-config-form").html("");
|
||||
$("#node-config-dialog-edit-form").html("");
|
||||
if (RED.view.state() != RED.state.EDITING) {
|
||||
RED.keyboard.enable();
|
||||
}
|
||||
RED.sidebar.config.refresh();
|
||||
RED.workspaces.refresh();
|
||||
},
|
||||
create: function() {
|
||||
$("#node-config-dialog").parent().find("div.ui-dialog-buttonpane")
|
||||
.prepend('<div id="node-config-dialog-user-count"><i class="fa fa-info-circle"></i> <span></span></div>');
|
||||
|
||||
$("#node-config-dialog").parent().find('.ui-dialog-titlebar').append('<span id="node-config-dialog-scope-container"><span id="node-config-dialog-scope-warning" data-i18n="[title]editor.errors.scopeChange"><i class="fa fa-warning"></i></span><select id="node-config-dialog-scope"></select></span>');
|
||||
$("#node-config-dialog").parent().draggable({
|
||||
cancel: '.ui-dialog-content, .ui-dialog-titlebar-close, #node-config-dialog-scope-container'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -878,15 +1052,29 @@ RED.editor = (function() {
|
||||
changes['name'] = editing_node.name;
|
||||
editing_node.name = newName;
|
||||
changed = true;
|
||||
$("#menu-item-workspace-menu-"+editing_node.id.replace(".","-")).text(RED._("subflow.tabLabel",{name:newName}));
|
||||
$("#menu-item-workspace-menu-"+editing_node.id.replace(".","-")).text(newName);
|
||||
}
|
||||
|
||||
var newDescription = subflowEditor.getValue();
|
||||
|
||||
if (newDescription != editing_node.info) {
|
||||
changes['info'] = editing_node.info;
|
||||
editing_node.info = newDescription;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
RED.palette.refresh();
|
||||
|
||||
if (changed) {
|
||||
var subflowInstances = [];
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.type == "subflow:"+editing_node.id) {
|
||||
subflowInstances.push({
|
||||
id:n.id,
|
||||
changed:n.changed
|
||||
})
|
||||
n.changed = true;
|
||||
n.dirty = true;
|
||||
updateNodeProperties(n);
|
||||
}
|
||||
});
|
||||
@@ -898,7 +1086,10 @@ RED.editor = (function() {
|
||||
node:editing_node,
|
||||
changes:changes,
|
||||
dirty:wasDirty,
|
||||
changed:wasChanged
|
||||
changed:wasChanged,
|
||||
subflow: {
|
||||
instances:subflowInstances
|
||||
}
|
||||
};
|
||||
|
||||
RED.history.push(historyEvent);
|
||||
@@ -918,6 +1109,14 @@ RED.editor = (function() {
|
||||
}
|
||||
}
|
||||
],
|
||||
create: function(e) {
|
||||
$("#subflow-dialog form" ).submit(function(e) { e.preventDefault();});
|
||||
subflowEditor = RED.editor.createEditor({
|
||||
id: 'subflow-input-info-editor',
|
||||
mode: 'ace/mode/markdown',
|
||||
value: ""
|
||||
});
|
||||
},
|
||||
open: function(e) {
|
||||
RED.keyboard.disable();
|
||||
var minWidth = $(this).dialog('option','minWidth');
|
||||
@@ -932,17 +1131,30 @@ RED.editor = (function() {
|
||||
RED.view.state(RED.state.DEFAULT);
|
||||
}
|
||||
RED.sidebar.info.refresh(editing_node);
|
||||
RED.workspaces.refresh();
|
||||
editing_node = null;
|
||||
},
|
||||
resize: function(e) {
|
||||
var rows = $("#subflow-dialog>form>div:not(.node-text-editor-row)");
|
||||
var editorRow = $("#subflow-dialog>form>div.node-text-editor-row");
|
||||
var height = $("#subflow-dialog").height();
|
||||
for (var i=0;i<rows.size();i++) {
|
||||
height -= $(rows[i]).outerHeight(true);
|
||||
}
|
||||
height -= (parseInt($("#subflow-dialog>form").css("marginTop"))+parseInt($("#subflow-dialog>form").css("marginBottom")));
|
||||
$(".node-text-editor").css("height",height+"px");
|
||||
subflowEditor.resize();
|
||||
}
|
||||
});
|
||||
$("#subflow-dialog form" ).submit(function(e) { e.preventDefault();});
|
||||
}
|
||||
|
||||
|
||||
function showEditSubflowDialog(subflow) {
|
||||
editing_node = subflow;
|
||||
RED.view.state(RED.state.EDITING);
|
||||
|
||||
$("#subflow-input-name").val(subflow.name);
|
||||
subflowEditor.getSession().setValue(subflow.info,-1);
|
||||
var userCount = 0;
|
||||
var subflowType = "subflow:"+editing_node.id;
|
||||
|
||||
|
@@ -133,17 +133,6 @@ RED.menu = (function() {
|
||||
if (opt.disabled) {
|
||||
item.addClass("disabled");
|
||||
}
|
||||
if (opt.tip) {
|
||||
item.popover({
|
||||
placement:"left",
|
||||
trigger: "hover",
|
||||
delay: { show: 350, hide: 20 },
|
||||
html: true,
|
||||
container:'body',
|
||||
content: opt.tip
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@@ -17,7 +17,7 @@
|
||||
RED.palette = (function() {
|
||||
|
||||
var exclusion = ['config','unknown','deprecated'];
|
||||
var core = ['subflows', 'input', 'output', 'function', 'social', 'storage', 'analysis', 'advanced'];
|
||||
var core = ['subflows', 'input', 'output', 'function', 'social', 'mobile', 'storage', 'analysis', 'advanced'];
|
||||
|
||||
var categoryContainers = {};
|
||||
|
||||
@@ -60,8 +60,8 @@ RED.palette = (function() {
|
||||
});
|
||||
}
|
||||
|
||||
function setLabel(type, el,label) {
|
||||
var nodeWidth = 80;
|
||||
function setLabel(type, el,label, info) {
|
||||
var nodeWidth = 82;
|
||||
var nodeHeight = 25;
|
||||
var lineHeight = 20;
|
||||
var portHeight = 10;
|
||||
@@ -101,10 +101,9 @@ RED.palette = (function() {
|
||||
if (label != type) {
|
||||
l = "<p><b>"+label+"</b><br/><i>"+type+"</i></p>";
|
||||
}
|
||||
|
||||
popOverContent = $(l+($("script[data-help-name|='"+type+"']").html()||"<p>"+RED._("palette.noInfo")+"</p>").trim())
|
||||
popOverContent = $(l+(info?info:$("script[data-help-name|='"+type+"']").html()||"<p>"+RED._("palette.noInfo")+"</p>").trim())
|
||||
.filter(function(n) {
|
||||
return this.nodeType == 1 || (this.nodeType == 3 && this.textContent.trim().length > 0)
|
||||
return (this.nodeType == 1 && this.nodeName == "P") || (this.nodeType == 3 && this.textContent.trim().length > 0)
|
||||
}).slice(0,2);
|
||||
} catch(err) {
|
||||
// Malformed HTML may cause errors. TODO: need to understand what can break
|
||||
@@ -114,8 +113,7 @@ RED.palette = (function() {
|
||||
popOverContent = "<p><b>"+label+"</b></p><p>"+RED._("palette.noInfo")+"</p>";
|
||||
}
|
||||
|
||||
|
||||
el.data('popover').options.content = popOverContent;
|
||||
el.data('popover').setContent(popOverContent);
|
||||
}
|
||||
|
||||
function escapeNodeType(nt) {
|
||||
@@ -127,7 +125,6 @@ RED.palette = (function() {
|
||||
if ($("#palette_node_"+nodeTypeId).length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (exclusion.indexOf(def.category)===-1) {
|
||||
|
||||
var category = def.category.replace(" ","_");
|
||||
@@ -188,18 +185,30 @@ RED.palette = (function() {
|
||||
$("#palette-"+category).append(d);
|
||||
d.onmousedown = function(e) { e.preventDefault(); };
|
||||
|
||||
$(d).popover({
|
||||
title:d.type,
|
||||
placement:"right",
|
||||
trigger: "hover",
|
||||
delay: { show: 750, hide: 50 },
|
||||
html: true,
|
||||
container:'body'
|
||||
RED.popover.create({
|
||||
target:$(d),
|
||||
content: "hi",
|
||||
delay: { show: 750, hide: 50 }
|
||||
});
|
||||
|
||||
// $(d).popover({
|
||||
// title:d.type,
|
||||
// placement:"right",
|
||||
// trigger: "hover",
|
||||
// delay: { show: 750, hide: 50 },
|
||||
// html: true,
|
||||
// container:'body'
|
||||
// });
|
||||
$(d).click(function() {
|
||||
RED.view.focus();
|
||||
var help = '<div class="node-help">'+($("script[data-help-name|='"+d.type+"']").html()||"")+"</div>";
|
||||
$("#tab-info").html(help);
|
||||
var helpText;
|
||||
if (nt.indexOf("subflow:") === 0) {
|
||||
helpText = marked(RED.nodes.subflow(nt.substring(8)).info||"");
|
||||
} else {
|
||||
helpText = $("script[data-help-name|='"+d.type+"']").html()||"";
|
||||
}
|
||||
var help = '<div class="node-help">'+helpText+"</div>";
|
||||
RED.sidebar.info.set(help);
|
||||
});
|
||||
$(d).draggable({
|
||||
helper: 'clone',
|
||||
@@ -209,14 +218,15 @@ RED.palette = (function() {
|
||||
start: function() {RED.view.focus();}
|
||||
});
|
||||
|
||||
var nodeInfo = null;
|
||||
if (def.category == "subflows") {
|
||||
$(d).dblclick(function(e) {
|
||||
RED.workspaces.show(nt.substring(8));
|
||||
e.preventDefault();
|
||||
});
|
||||
nodeInfo = marked(def.info||"");
|
||||
}
|
||||
|
||||
setLabel(nt,$(d),label);
|
||||
setLabel(nt,$(d),label,nodeInfo);
|
||||
|
||||
var categoryNode = $("#palette-container-"+category);
|
||||
if (categoryNode.find(".palette_node").length === 1) {
|
||||
@@ -269,7 +279,7 @@ RED.palette = (function() {
|
||||
} else if (portOutput.length !== 0 && sf.out.length === 0) {
|
||||
portOutput.remove();
|
||||
}
|
||||
setLabel(sf.type+":"+sf.id,paletteNode,sf.name);
|
||||
setLabel(sf.type+":"+sf.id,paletteNode,sf.name,marked(sf.info||""));
|
||||
});
|
||||
}
|
||||
|
||||
|
79
editor/js/ui/popover.js
Normal file
79
editor/js/ui/popover.js
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* 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.popover = (function() {
|
||||
|
||||
|
||||
function createPopover(options) {
|
||||
var target = options.target;
|
||||
|
||||
var content = options.content;
|
||||
var delay = options.delay;
|
||||
var timer = null;
|
||||
var active;
|
||||
var div;
|
||||
|
||||
var openPopup = function() {
|
||||
if (active) {
|
||||
div = $('<div class="red-ui-popover"></div>').html(content).appendTo("body");
|
||||
var targetPos = target.offset();
|
||||
var targetWidth = target.width();
|
||||
var targetHeight = target.height();
|
||||
|
||||
var divHeight = div.height();
|
||||
div.css({top: targetPos.top+targetHeight/2-divHeight/2-10,left:targetPos.left+targetWidth+17});
|
||||
|
||||
div.fadeIn("fast");
|
||||
}
|
||||
}
|
||||
var closePopup = function() {
|
||||
if (!active) {
|
||||
if (div) {
|
||||
div.fadeOut("fast",function() {
|
||||
$(this).remove();
|
||||
});
|
||||
div = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
target.on('mouseenter',function(e) {
|
||||
clearTimeout(timer);
|
||||
active = true;
|
||||
timer = setTimeout(openPopup,delay.show);
|
||||
});
|
||||
target.on('mouseleave', function(e) {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
active = false;
|
||||
setTimeout(closePopup,delay.hide);
|
||||
});
|
||||
var res = {
|
||||
setContent: function(_content) {
|
||||
content = _content;
|
||||
}
|
||||
}
|
||||
target.data('popover',res);
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
create: createPopover
|
||||
}
|
||||
|
||||
})();
|
@@ -21,8 +21,11 @@ RED.subflow = (function() {
|
||||
return RED.nodes.subflow(RED.workspaces.active());
|
||||
}
|
||||
|
||||
function findAvailableSubflowIOPosition(subflow) {
|
||||
var pos = {x:70,y:70};
|
||||
function findAvailableSubflowIOPosition(subflow,isInput) {
|
||||
var pos = {x:50,y:30};
|
||||
if (!isInput) {
|
||||
pos.x += 110;
|
||||
}
|
||||
for (var i=0;i<subflow.out.length+subflow.in.length;i++) {
|
||||
var port;
|
||||
if (i < subflow.out.length) {
|
||||
@@ -40,7 +43,10 @@ RED.subflow = (function() {
|
||||
|
||||
function addSubflowInput() {
|
||||
var subflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
var position = findAvailableSubflowIOPosition(subflow);
|
||||
if (subflow.in.length === 1) {
|
||||
return;
|
||||
}
|
||||
var position = findAvailableSubflowIOPosition(subflow,true);
|
||||
var newInput = {
|
||||
type:"subflow",
|
||||
direction:"in",
|
||||
@@ -56,31 +62,50 @@ RED.subflow = (function() {
|
||||
var wasDirty = RED.nodes.dirty();
|
||||
var wasChanged = subflow.changed;
|
||||
subflow.changed = true;
|
||||
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.type == "subflow:"+subflow.id) {
|
||||
n.changed = true;
|
||||
n.inputs = subflow.in.length;
|
||||
RED.editor.updateNodeProperties(n);
|
||||
}
|
||||
});
|
||||
var result = refresh(true);
|
||||
var historyEvent = {
|
||||
t:'edit',
|
||||
node:subflow,
|
||||
dirty:wasDirty,
|
||||
changed:wasChanged,
|
||||
subflow: {
|
||||
inputCount: oldInCount
|
||||
inputCount: oldInCount,
|
||||
instances: result.instances
|
||||
}
|
||||
};
|
||||
RED.history.push(historyEvent);
|
||||
$("#workspace-subflow-add-input").toggleClass("disabled",true);
|
||||
RED.view.select();
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw();
|
||||
$("#workspace-subflow-input-add").addClass("active");
|
||||
$("#workspace-subflow-input-remove").removeClass("active");
|
||||
}
|
||||
|
||||
function removeSubflowInput() {
|
||||
var activeSubflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
if (activeSubflow.in.length === 0) {
|
||||
return;
|
||||
}
|
||||
var removedInput = activeSubflow.in[0];
|
||||
var removedInputLinks = [];
|
||||
RED.nodes.eachLink(function(l) {
|
||||
if (l.source.type == "subflow" && l.source.z == activeSubflow.id && l.source.i == removedInput.i) {
|
||||
removedInputLinks.push(l);
|
||||
} else if (l.target.type == "subflow:"+activeSubflow.id) {
|
||||
removedInputLinks.push(l);
|
||||
}
|
||||
});
|
||||
removedInputLinks.forEach(function(l) { RED.nodes.removeLink(l)});
|
||||
activeSubflow.in = [];
|
||||
$("#workspace-subflow-input-add").removeClass("active");
|
||||
$("#workspace-subflow-input-remove").addClass("active");
|
||||
activeSubflow.changed = true;
|
||||
return {subflowInputs: [ removedInput ], links:removedInputLinks};
|
||||
}
|
||||
|
||||
function addSubflowOutput(id) {
|
||||
var subflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
var position = findAvailableSubflowIOPosition(subflow);
|
||||
var position = findAvailableSubflowIOPosition(subflow,false);
|
||||
|
||||
var newOutput = {
|
||||
type:"subflow",
|
||||
@@ -98,45 +123,186 @@ RED.subflow = (function() {
|
||||
var wasChanged = subflow.changed;
|
||||
subflow.changed = true;
|
||||
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.type == "subflow:"+subflow.id) {
|
||||
n.changed = true;
|
||||
n.outputs = subflow.out.length;
|
||||
RED.editor.updateNodeProperties(n);
|
||||
}
|
||||
});
|
||||
var result = refresh(true);
|
||||
|
||||
var historyEvent = {
|
||||
t:'edit',
|
||||
node:subflow,
|
||||
dirty:wasDirty,
|
||||
changed:wasChanged,
|
||||
subflow: {
|
||||
outputCount: oldOutCount
|
||||
outputCount: oldOutCount,
|
||||
instances: result.instances
|
||||
}
|
||||
};
|
||||
RED.history.push(historyEvent);
|
||||
RED.view.select();
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw();
|
||||
$("#workspace-subflow-output .spinner-value").html(subflow.out.length);
|
||||
}
|
||||
|
||||
function init() {
|
||||
function removeSubflowOutput(removedSubflowOutputs) {
|
||||
var activeSubflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
if (activeSubflow.out.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (typeof removedSubflowOutputs === "undefined") {
|
||||
removedSubflowOutputs = [activeSubflow.out[activeSubflow.out.length-1]];
|
||||
}
|
||||
var removedLinks = [];
|
||||
removedSubflowOutputs.sort(function(a,b) { return b.i-a.i});
|
||||
for (i=0;i<removedSubflowOutputs.length;i++) {
|
||||
var output = removedSubflowOutputs[i];
|
||||
activeSubflow.out.splice(output.i,1);
|
||||
var subflowRemovedLinks = [];
|
||||
var subflowMovedLinks = [];
|
||||
RED.nodes.eachLink(function(l) {
|
||||
if (l.target.type == "subflow" && l.target.z == activeSubflow.id && l.target.i == output.i) {
|
||||
subflowRemovedLinks.push(l);
|
||||
}
|
||||
if (l.source.type == "subflow:"+activeSubflow.id) {
|
||||
if (l.sourcePort == output.i) {
|
||||
subflowRemovedLinks.push(l);
|
||||
} else if (l.sourcePort > output.i) {
|
||||
subflowMovedLinks.push(l);
|
||||
}
|
||||
}
|
||||
});
|
||||
subflowRemovedLinks.forEach(function(l) { RED.nodes.removeLink(l)});
|
||||
subflowMovedLinks.forEach(function(l) { l.sourcePort--; });
|
||||
|
||||
removedLinks = removedLinks.concat(subflowRemovedLinks);
|
||||
for (var j=output.i;j<activeSubflow.out.length;j++) {
|
||||
activeSubflow.out[j].i--;
|
||||
activeSubflow.out[j].dirty = true;
|
||||
}
|
||||
}
|
||||
activeSubflow.changed = true;
|
||||
|
||||
return {subflowOutputs: removedSubflowOutputs, links: removedLinks}
|
||||
}
|
||||
|
||||
function refresh(markChange) {
|
||||
var activeSubflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
refreshToolbar(activeSubflow);
|
||||
var subflowInstances = [];
|
||||
if (activeSubflow) {
|
||||
RED.nodes.filterNodes({type:"subflow:"+activeSubflow.id}).forEach(function(n) {
|
||||
subflowInstances.push({
|
||||
id: n.id,
|
||||
changed: n.changed
|
||||
});
|
||||
if (markChange) {
|
||||
n.changed = true;
|
||||
}
|
||||
n.inputs = activeSubflow.in.length;
|
||||
n.outputs = activeSubflow.out.length;
|
||||
while (n.outputs < n.ports.length) {
|
||||
n.ports.pop();
|
||||
}
|
||||
n.resize = true;
|
||||
n.dirty = true;
|
||||
RED.editor.updateNodeProperties(n);
|
||||
});
|
||||
RED.editor.validateNode(activeSubflow);
|
||||
return {
|
||||
instances: subflowInstances
|
||||
}
|
||||
}
|
||||
}
|
||||
function refreshToolbar(activeSubflow) {
|
||||
if (activeSubflow) {
|
||||
$("#workspace-subflow-input-add").toggleClass("active", activeSubflow.in.length !== 0);
|
||||
$("#workspace-subflow-input-remove").toggleClass("active",activeSubflow.in.length === 0);
|
||||
|
||||
$("#workspace-subflow-output .spinner-value").html(activeSubflow.out.length);
|
||||
}
|
||||
}
|
||||
|
||||
function showWorkspaceToolbar(activeSubflow) {
|
||||
var toolbar = $("#workspace-toolbar");
|
||||
toolbar.empty();
|
||||
|
||||
$('<a class="button" id="workspace-subflow-edit" href="#" data-i18n="[append]subflow.editSubflowProperties"><i class="fa fa-pencil"></i> </a>').appendTo(toolbar);
|
||||
$('<span style="margin-left: 5px;" data-i18n="subflow.input"></span> '+
|
||||
'<div style="display: inline-block;" class="button-group">'+
|
||||
'<a id="workspace-subflow-input-remove" class="button active" href="#">0</a>'+
|
||||
'<a id="workspace-subflow-input-add" class="button" href="#">1</a>'+
|
||||
'</div>').appendTo(toolbar);
|
||||
|
||||
$('<span style="margin-left: 5px;" data-i18n="subflow.output"></span> <div id="workspace-subflow-output" style="display: inline-block;" class="button-group spinner-group">'+
|
||||
'<a id="workspace-subflow-output-remove" class="button" href="#"><i class="fa fa-minus"></i></a>'+
|
||||
'<div class="spinner-value">3</div>'+
|
||||
'<a id="workspace-subflow-output-add" class="button" href="#"><i class="fa fa-plus"></i></a>'+
|
||||
'</div>').appendTo(toolbar);
|
||||
|
||||
// $('<a class="button disabled" id="workspace-subflow-add-input" href="#" data-i18n="[append]subflow.input"><i class="fa fa-plus"></i> </a>').appendTo(toolbar);
|
||||
// $('<a class="button" id="workspace-subflow-add-output" href="#" data-i18n="[append]subflow.output"><i class="fa fa-plus"></i> </a>').appendTo(toolbar);
|
||||
$('<a class="button" id="workspace-subflow-delete" href="#" data-i18n="[append]subflow.deleteSubflow"><i class="fa fa-trash"></i> </a>').appendTo(toolbar);
|
||||
toolbar.i18n();
|
||||
|
||||
|
||||
$("#workspace-subflow-output-remove").click(function(event) {
|
||||
event.preventDefault();
|
||||
var wasDirty = RED.nodes.dirty();
|
||||
var wasChanged = activeSubflow.changed;
|
||||
var result = removeSubflowOutput();
|
||||
if (result) {
|
||||
var inst = refresh(true);
|
||||
RED.history.push({
|
||||
t:'delete',
|
||||
links:result.links,
|
||||
subflowOutputs: result.subflowOutputs,
|
||||
changed: wasChanged,
|
||||
dirty:wasDirty,
|
||||
subflow: {
|
||||
instances: inst.instances
|
||||
}
|
||||
});
|
||||
|
||||
RED.view.select();
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
});
|
||||
$("#workspace-subflow-output-add").click(function(event) {
|
||||
event.preventDefault();
|
||||
addSubflowOutput();
|
||||
});
|
||||
|
||||
$("#workspace-subflow-input-add").click(function(event) {
|
||||
event.preventDefault();
|
||||
addSubflowInput();
|
||||
});
|
||||
$("#workspace-subflow-input-remove").click(function(event) {
|
||||
event.preventDefault();
|
||||
var wasDirty = RED.nodes.dirty();
|
||||
var wasChanged = activeSubflow.changed;
|
||||
activeSubflow.changed = true;
|
||||
var result = removeSubflowInput();
|
||||
if (result) {
|
||||
var inst = refresh(true);
|
||||
RED.history.push({
|
||||
t:'delete',
|
||||
links:result.links,
|
||||
changed: wasChanged,
|
||||
subflowInputs: result.subflowInputs,
|
||||
dirty:wasDirty,
|
||||
subflow: {
|
||||
instances: inst.instances
|
||||
}
|
||||
});
|
||||
RED.view.select();
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
});
|
||||
|
||||
$("#workspace-subflow-edit").click(function(event) {
|
||||
RED.editor.editSubflow(RED.nodes.subflow(RED.workspaces.active()));
|
||||
event.preventDefault();
|
||||
});
|
||||
$("#workspace-subflow-add-input").click(function(event) {
|
||||
event.preventDefault();
|
||||
if ($(this).hasClass("disabled")) {
|
||||
return;
|
||||
}
|
||||
addSubflowInput();
|
||||
});
|
||||
$("#workspace-subflow-add-output").click(function(event) {
|
||||
event.preventDefault();
|
||||
if ($(this).hasClass("disabled")) {
|
||||
return;
|
||||
}
|
||||
addSubflowOutput();
|
||||
});
|
||||
|
||||
$("#workspace-subflow-delete").click(function(event) {
|
||||
event.preventDefault();
|
||||
@@ -144,11 +310,18 @@ RED.subflow = (function() {
|
||||
var removedLinks = [];
|
||||
var startDirty = RED.nodes.dirty();
|
||||
|
||||
var activeSubflow = getSubflow();
|
||||
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.type == "subflow:"+getSubflow().id) {
|
||||
if (n.type == "subflow:"+activeSubflow.id) {
|
||||
removedNodes.push(n);
|
||||
}
|
||||
if (n.z == getSubflow().id) {
|
||||
if (n.z == activeSubflow.id) {
|
||||
removedNodes.push(n);
|
||||
}
|
||||
});
|
||||
RED.nodes.eachConfig(function(n) {
|
||||
if (n.z == activeSubflow.id) {
|
||||
removedNodes.push(n);
|
||||
}
|
||||
});
|
||||
@@ -162,15 +335,15 @@ RED.subflow = (function() {
|
||||
// TODO: this whole delete logic should be in RED.nodes.removeSubflow..
|
||||
removedNodes = removedNodes.concat(removedConfigNodes);
|
||||
|
||||
var activeSubflow = getSubflow();
|
||||
|
||||
RED.nodes.removeSubflow(activeSubflow);
|
||||
|
||||
RED.history.push({
|
||||
t:'delete',
|
||||
nodes:removedNodes,
|
||||
links:removedLinks,
|
||||
subflow: activeSubflow,
|
||||
subflow: {
|
||||
subflow: activeSubflow
|
||||
},
|
||||
dirty:startDirty
|
||||
});
|
||||
|
||||
@@ -179,6 +352,26 @@ RED.subflow = (function() {
|
||||
RED.view.redraw();
|
||||
});
|
||||
|
||||
refreshToolbar(activeSubflow);
|
||||
|
||||
$("#chart").css({"margin-top": "40px"});
|
||||
$("#workspace-toolbar").show();
|
||||
}
|
||||
function hideWorkspaceToolbar() {
|
||||
$("#workspace-toolbar").hide().empty();
|
||||
$("#chart").css({"margin-top": "0"});
|
||||
}
|
||||
|
||||
|
||||
function init() {
|
||||
RED.events.on("workspace:change",function(event) {
|
||||
var activeSubflow = RED.nodes.subflow(event.workspace);
|
||||
if (activeSubflow) {
|
||||
showWorkspaceToolbar(activeSubflow);
|
||||
} else {
|
||||
hideWorkspaceToolbar();
|
||||
}
|
||||
});
|
||||
RED.events.on("view:selection-changed",function(selection) {
|
||||
if (!selection.nodes) {
|
||||
RED.menu.setDisabled("menu-item-subflow-convert",true);
|
||||
@@ -205,16 +398,20 @@ RED.subflow = (function() {
|
||||
type:"subflow",
|
||||
id:subflowId,
|
||||
name:name,
|
||||
info:"",
|
||||
in: [],
|
||||
out: []
|
||||
};
|
||||
RED.nodes.addSubflow(subflow);
|
||||
RED.history.push({
|
||||
t:'createSubflow',
|
||||
subflow: subflow,
|
||||
subflow: {
|
||||
subflow:subflow
|
||||
},
|
||||
dirty:RED.nodes.dirty()
|
||||
});
|
||||
RED.workspaces.show(subflowId);
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
|
||||
function convertToSubflow() {
|
||||
@@ -230,6 +427,8 @@ RED.subflow = (function() {
|
||||
|
||||
var candidateInputs = [];
|
||||
var candidateOutputs = [];
|
||||
var candidateInputNodes = {};
|
||||
|
||||
|
||||
var boundingBox = [selection.nodes[0].x,
|
||||
selection.nodes[0].y,
|
||||
@@ -262,6 +461,7 @@ RED.subflow = (function() {
|
||||
if (!nodes[link.source.id] && nodes[link.target.id]) {
|
||||
// An inbound link
|
||||
candidateInputs.push(link);
|
||||
candidateInputNodes[link.target.id] = link.target;
|
||||
removedLinks.push(link);
|
||||
}
|
||||
});
|
||||
@@ -279,15 +479,10 @@ RED.subflow = (function() {
|
||||
});
|
||||
candidateOutputs.sort(function(a,b) { return a.source.y-b.source.y});
|
||||
|
||||
if (candidateInputs.length > 1) {
|
||||
if (Object.keys(candidateInputNodes).length > 1) {
|
||||
RED.notify(RED._("subflow.errors.multipleInputsToSelection"),"error");
|
||||
return;
|
||||
}
|
||||
//if (candidateInputs.length == 0) {
|
||||
// RED.notify("<strong>Cannot create subflow</strong>: no input to selection","error");
|
||||
// return;
|
||||
//}
|
||||
|
||||
|
||||
var lastIndex = 0;
|
||||
RED.nodes.eachSubflow(function(sf) {
|
||||
@@ -304,15 +499,16 @@ RED.subflow = (function() {
|
||||
type:"subflow",
|
||||
id:subflowId,
|
||||
name:name,
|
||||
in: candidateInputs.map(function(v,i) { var index = i; return {
|
||||
info:"",
|
||||
in: Object.keys(candidateInputNodes).map(function(v,i) { var index = i; return {
|
||||
type:"subflow",
|
||||
direction:"in",
|
||||
x:v.target.x-(v.target.w/2)-80,
|
||||
y:v.target.y,
|
||||
x:candidateInputNodes[v].x-(candidateInputNodes[v].w/2)-80,
|
||||
y:candidateInputNodes[v].y,
|
||||
z:subflowId,
|
||||
i:index,
|
||||
id:RED.nodes.id(),
|
||||
wires:[{id:v.target.id}]
|
||||
wires:[{id:candidateInputNodes[v].id}]
|
||||
}}),
|
||||
out: candidateOutputs.map(function(v,i) { var index = i; return {
|
||||
type:"subflow",
|
||||
@@ -325,6 +521,7 @@ RED.subflow = (function() {
|
||||
wires:[{id:v.source.id,port:v.sourcePort}]
|
||||
}})
|
||||
};
|
||||
|
||||
RED.nodes.addSubflow(subflow);
|
||||
|
||||
var subflowInstance = {
|
||||
@@ -383,7 +580,9 @@ RED.subflow = (function() {
|
||||
t:'createSubflow',
|
||||
nodes:[subflowInstance.id],
|
||||
links:new_links,
|
||||
subflow: subflow,
|
||||
subflow: {
|
||||
subflow: subflow
|
||||
},
|
||||
|
||||
activeWorkspace: RED.workspaces.active(),
|
||||
removedLinks: removedLinks,
|
||||
@@ -401,6 +600,9 @@ RED.subflow = (function() {
|
||||
return {
|
||||
init: init,
|
||||
createSubflow: createSubflow,
|
||||
convertToSubflow: convertToSubflow
|
||||
convertToSubflow: convertToSubflow,
|
||||
refresh: refresh,
|
||||
removeInput: removeSubflowInput,
|
||||
removeOutput: removeSubflowOutput
|
||||
}
|
||||
})();
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013 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.
|
||||
@@ -15,12 +15,95 @@
|
||||
**/
|
||||
RED.sidebar.config = (function() {
|
||||
|
||||
var content = document.createElement("div");
|
||||
content.style.paddingTop = "4px";
|
||||
content.style.paddingLeft = "4px";
|
||||
content.style.paddingRight = "4px";
|
||||
|
||||
var list = $("<ul>",{class:"tab-config-list"}).appendTo(content);
|
||||
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);
|
||||
|
||||
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;
|
||||
});
|
||||
list.empty();
|
||||
if (nodes.length === 0) {
|
||||
$('<li class="config_node_none" data-i18n="sidebar.config.none">NONE</li>').i18n().appendTo(list);
|
||||
} else {
|
||||
var currentType = "";
|
||||
nodes.forEach(function(node) {
|
||||
var label = "";
|
||||
if (typeof node._def.label == "function") {
|
||||
label = node._def.label.call(node);
|
||||
} else {
|
||||
label = node._def.label;
|
||||
}
|
||||
label = label || node.id;
|
||||
if (node.type != currentType) {
|
||||
$('<li class="config_node_type">'+node.type+'</li>').appendTo(list);
|
||||
currentType = node.type;
|
||||
}
|
||||
|
||||
var entry = $('<li class="palette_node config_node"></li>').appendTo(list);
|
||||
$('<div class="palette_label"></div>').text(label).appendTo(entry);
|
||||
|
||||
var iconContainer = $('<div/>',{class:"palette_icon_container palette_icon_container_right"}).text(node.users.length).appendTo(entry);
|
||||
if (node.users.length === 0) {
|
||||
entry.addClass("config_node_unused");
|
||||
}
|
||||
entry.on('click',function(e) {
|
||||
RED.sidebar.info.refresh(node);
|
||||
});
|
||||
entry.on('dblclick',function(e) {
|
||||
RED.editor.editConfig("", node.type, node.id);
|
||||
});
|
||||
var userArray = node.users.map(function(n) { return n.id });
|
||||
entry.on('mouseover',function(e) {
|
||||
RED.nodes.eachNode(function(node) {
|
||||
if( userArray.indexOf(node.id) != -1) {
|
||||
node.highlighted = true;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
RED.view.redraw();
|
||||
});
|
||||
|
||||
entry.on('mouseout',function(e) {
|
||||
RED.nodes.eachNode(function(node) {
|
||||
if(node.highlighted) {
|
||||
node.highlighted = false;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
RED.view.redraw();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function refreshConfigNodeList() {
|
||||
|
||||
var localConfigNodes = [];
|
||||
var globalConfigNodes = [];
|
||||
|
||||
RED.nodes.eachConfig(function(cn) {
|
||||
if (cn.z == RED.workspaces.active()) {
|
||||
localConfigNodes.push(cn);
|
||||
} else if (!cn.z) {
|
||||
globalConfigNodes.push(cn);
|
||||
}
|
||||
});
|
||||
createConfigNodeList(localConfigNodes,$("#workspace-config-node-tray-locals"));
|
||||
createConfigNodeList(globalConfigNodes,$("#workspace-config-node-tray-globals"));
|
||||
}
|
||||
|
||||
function init() {
|
||||
RED.sidebar.addTab({
|
||||
@@ -30,63 +113,29 @@ RED.sidebar.config = (function() {
|
||||
content: content,
|
||||
closeable: true,
|
||||
visible: false,
|
||||
onchange: function() { refresh(); }
|
||||
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();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
function show() {
|
||||
refresh();
|
||||
refreshConfigNodeList();
|
||||
RED.sidebar.show("config");
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
list.empty();
|
||||
RED.nodes.eachConfig(function(node) {
|
||||
var li = list.find("#tab-config-list-type-"+node.type);
|
||||
if (li.length === 0) {
|
||||
li = $("<li>",{id:"tab-config-list-type-"+node.type}).appendTo(list);
|
||||
$('<div class="tab-config-list-type">'+node.type+'</div>').appendTo(li);
|
||||
}
|
||||
var label = "";
|
||||
if (typeof node._def.label == "function") {
|
||||
label = node._def.label.call(node);
|
||||
} else {
|
||||
label = node._def.label;
|
||||
}
|
||||
label = label || " ";
|
||||
|
||||
var entry = $('<div class="tab-config-list-entry"></div>').appendTo(li);
|
||||
entry.on('dblclick',function(e) {
|
||||
RED.editor.editConfig("", node.type, node.id);
|
||||
});
|
||||
|
||||
var userArray = node.users.map(function(n) { return n.id });
|
||||
entry.on('mouseover',function(e) {
|
||||
RED.nodes.eachNode(function(node) {
|
||||
if( userArray.indexOf(node.id) != -1) {
|
||||
node.highlighted = true;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
RED.view.redraw();
|
||||
});
|
||||
|
||||
entry.on('mouseout',function(e) {
|
||||
RED.nodes.eachNode(function(node) {
|
||||
if(node.highlighted) {
|
||||
node.highlighted = false;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
RED.view.redraw();
|
||||
});
|
||||
|
||||
$('<div class="tab-config-list-label">'+label+'</div>').appendTo(entry);
|
||||
$('<div class="tab-config-list-users">'+node.users.length+'</div>').appendTo(entry);
|
||||
});
|
||||
}
|
||||
return {
|
||||
init:init,
|
||||
show:show,
|
||||
refresh:refresh
|
||||
refresh:refreshConfigNodeList
|
||||
}
|
||||
})();
|
||||
|
@@ -75,8 +75,8 @@ RED.sidebar.info = (function() {
|
||||
table += "<tr><td>"+RED._("sidebar.info.id")+"</td><td> "+node.id+"</td></tr>";
|
||||
|
||||
var m = /^subflow(:(.+))?$/.exec(node.type);
|
||||
var subflowNode;
|
||||
if (m) {
|
||||
var subflowNode;
|
||||
if (m[2]) {
|
||||
subflowNode = RED.nodes.subflow(m[2]);
|
||||
} else {
|
||||
@@ -135,12 +135,13 @@ RED.sidebar.info = (function() {
|
||||
}
|
||||
}
|
||||
table += "</tbody></table><hr/>";
|
||||
if (node.type != "comment") {
|
||||
if (!subflowNode && node.type != "comment") {
|
||||
var helpText = $("script[data-help-name|='"+node.type+"']").html()||"";
|
||||
table += '<div class="node-help">'+helpText+"</div>";
|
||||
}
|
||||
|
||||
if (node._def && node._def.info) {
|
||||
if (subflowNode) {
|
||||
table += '<div class="node-help">'+marked(subflowNode.info||"")+'</div>';
|
||||
} else if (node._def && node._def.info) {
|
||||
var info = node._def.info;
|
||||
table += '<div class="node-help">'+marked(typeof info === "function" ? info.call(node) : info)+'</div>';
|
||||
//table += '<div class="node-help">'+(typeof info === "function" ? info.call(node) : info)+'</div>';
|
||||
@@ -167,7 +168,11 @@ RED.sidebar.info = (function() {
|
||||
}
|
||||
|
||||
function clear() {
|
||||
$("#tab-info").html("");
|
||||
$(content).html("");
|
||||
}
|
||||
|
||||
function set(html) {
|
||||
$(content).html(html);
|
||||
}
|
||||
|
||||
RED.events.on("view:selection-changed",function(selection) {
|
||||
@@ -194,6 +199,7 @@ RED.sidebar.info = (function() {
|
||||
init: init,
|
||||
show: show,
|
||||
refresh:refresh,
|
||||
clear: clear
|
||||
clear: clear,
|
||||
set: set
|
||||
}
|
||||
})();
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013 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.
|
||||
@@ -80,12 +80,15 @@ RED.tabs = (function() {
|
||||
tabs.css({width:currentTabWidth+"%"});
|
||||
if (tabWidth < 50) {
|
||||
ul.find(".red-ui-tab-close").hide();
|
||||
ul.find(".red-ui-tab-icon").hide();
|
||||
} else {
|
||||
ul.find(".red-ui-tab-close").show();
|
||||
ul.find(".red-ui-tab-icon").show();
|
||||
}
|
||||
if (currentActiveTabWidth !== 0) {
|
||||
ul.find("li.red-ui-tab.active").css({"width":options.minimumActiveTabWidth});
|
||||
ul.find("li.red-ui-tab.active .red-ui-tab-close").show();
|
||||
ul.find("li.red-ui-tab.active .red-ui-tab-icon").show();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -116,13 +119,16 @@ RED.tabs = (function() {
|
||||
tabs[tab.id] = tab;
|
||||
var li = $("<li/>",{class:"red-ui-tab"}).appendTo(ul);
|
||||
var link = $("<a/>",{href:"#"+tab.id, class:"red-ui-tab-label"}).appendTo(li);
|
||||
link.html(tab.label);
|
||||
if (tab.icon) {
|
||||
$('<img src="'+tab.icon+'" class="red-ui-tab-icon"/>').appendTo(link);
|
||||
}
|
||||
$('<span/>').text(tab.label).appendTo(link);
|
||||
|
||||
link.on("click",onTabClick);
|
||||
link.on("dblclick",onTabDblClick);
|
||||
if (tab.closeable) {
|
||||
var closeLink = $("<a/>",{href:"#",class:"red-ui-tab-close"}).appendTo(li);
|
||||
closeLink.html('<i class="fa fa-times" />');
|
||||
closeLink.append('<i class="fa fa-times" />');
|
||||
|
||||
closeLink.on("click",function(event) {
|
||||
removeTab(tab.id);
|
||||
@@ -150,7 +156,7 @@ RED.tabs = (function() {
|
||||
tabs[id].label = label;
|
||||
var tab = ul.find("a[href='#"+id+"']");
|
||||
tab.attr("title",label);
|
||||
tab.text(label);
|
||||
tab.find("span").text(label);
|
||||
updateTabWidths();
|
||||
}
|
||||
|
||||
|
@@ -255,9 +255,6 @@ RED.view = (function() {
|
||||
var scrollStartTop = chart.scrollTop();
|
||||
|
||||
activeSubflow = RED.nodes.subflow(event.workspace);
|
||||
if (activeSubflow) {
|
||||
$("#workspace-subflow-add-input").toggleClass("disabled",activeSubflow.in.length > 0);
|
||||
}
|
||||
|
||||
RED.menu.setDisabled("menu-item-workspace-edit", activeSubflow);
|
||||
RED.menu.setDisabled("menu-item-workspace-delete",RED.workspaces.count() == 1 || activeSubflow);
|
||||
@@ -351,7 +348,23 @@ RED.view = (function() {
|
||||
|
||||
nn.changed = true;
|
||||
nn.h = Math.max(node_height,(nn.outputs||0) * 15);
|
||||
RED.history.push({t:'add',nodes:[nn.id],dirty:RED.nodes.dirty()});
|
||||
var historyEvent = {
|
||||
t:'add',
|
||||
nodes:[nn.id],
|
||||
dirty:RED.nodes.dirty()
|
||||
}
|
||||
if (activeSubflow) {
|
||||
var subflowRefresh = RED.subflow.refresh(true);
|
||||
if (subflowRefresh) {
|
||||
historyEvent.subflow = {
|
||||
id:activeSubflow.id,
|
||||
changed: activeSubflow.changed,
|
||||
instances: subflowRefresh.instances
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RED.history.push(historyEvent);
|
||||
RED.nodes.add(nn);
|
||||
RED.editor.validateNode(nn);
|
||||
RED.nodes.dirty(true);
|
||||
@@ -394,8 +407,8 @@ RED.view = (function() {
|
||||
lasso = vis.append('rect')
|
||||
.attr("ox",point[0])
|
||||
.attr("oy",point[1])
|
||||
.attr("rx",2)
|
||||
.attr("ry",2)
|
||||
.attr("rx",1)
|
||||
.attr("ry",1)
|
||||
.attr("x",point[0])
|
||||
.attr("y",point[1])
|
||||
.attr("width",0)
|
||||
@@ -741,12 +754,15 @@ RED.view = (function() {
|
||||
redraw();
|
||||
}
|
||||
function deleteSelection() {
|
||||
var result;
|
||||
var removedNodes = [];
|
||||
var removedLinks = [];
|
||||
var removedSubflowOutputs = [];
|
||||
var removedSubflowInputs = [];
|
||||
var subflowInstances = [];
|
||||
|
||||
var startDirty = RED.nodes.dirty();
|
||||
var startChanged = false;
|
||||
if (moving_set.length > 0) {
|
||||
for (var i=0;i<moving_set.length;i++) {
|
||||
var node = moving_set[i].n;
|
||||
@@ -769,65 +785,22 @@ RED.view = (function() {
|
||||
}
|
||||
}
|
||||
if (removedSubflowOutputs.length > 0) {
|
||||
removedSubflowOutputs.sort(function(a,b) { return b.i-a.i});
|
||||
for (i=0;i<removedSubflowOutputs.length;i++) {
|
||||
var output = removedSubflowOutputs[i];
|
||||
activeSubflow.out.splice(output.i,1);
|
||||
var subflowRemovedLinks = [];
|
||||
var subflowMovedLinks = [];
|
||||
RED.nodes.eachLink(function(l) {
|
||||
if (l.target.type == "subflow" && l.target.z == activeSubflow.id && l.target.i == output.i) {
|
||||
subflowRemovedLinks.push(l);
|
||||
}
|
||||
if (l.source.type == "subflow:"+activeSubflow.id) {
|
||||
if (l.sourcePort == output.i) {
|
||||
subflowRemovedLinks.push(l);
|
||||
} else if (l.sourcePort > output.i) {
|
||||
subflowMovedLinks.push(l);
|
||||
}
|
||||
}
|
||||
});
|
||||
subflowRemovedLinks.forEach(function(l) { RED.nodes.removeLink(l)});
|
||||
subflowMovedLinks.forEach(function(l) { l.sourcePort--; });
|
||||
|
||||
removedLinks = removedLinks.concat(subflowRemovedLinks);
|
||||
for (var j=output.i;j<activeSubflow.out.length;j++) {
|
||||
activeSubflow.out[j].i--;
|
||||
activeSubflow.out[j].dirty = true;
|
||||
}
|
||||
result = RED.subflow.removeOutput(removedSubflowOutputs);
|
||||
if (result) {
|
||||
removedLinks = removedLinks.concat(result.links);
|
||||
}
|
||||
}
|
||||
// Assume 0/1 inputs
|
||||
if (removedSubflowInputs.length == 1) {
|
||||
var input = removedSubflowInputs[0];
|
||||
var subflowRemovedInputLinks = [];
|
||||
RED.nodes.eachLink(function(l) {
|
||||
if (l.source.type == "subflow" && l.source.z == activeSubflow.id && l.source.i == input.i) {
|
||||
subflowRemovedInputLinks.push(l);
|
||||
} else if (l.target.type == "subflow:"+activeSubflow.id) {
|
||||
subflowRemovedInputLinks.push(l);
|
||||
}
|
||||
});
|
||||
subflowRemovedInputLinks.forEach(function(l) { RED.nodes.removeLink(l)});
|
||||
removedLinks = removedLinks.concat(subflowRemovedInputLinks);
|
||||
activeSubflow.in = [];
|
||||
$("#workspace-subflow-add-input").toggleClass("disabled",false);
|
||||
result = RED.subflow.removeInput();
|
||||
if (result) {
|
||||
removedLinks = removedLinks.concat(result.links);
|
||||
}
|
||||
}
|
||||
|
||||
if (activeSubflow) {
|
||||
RED.nodes.filterNodes({type:"subflow:"+activeSubflow.id}).forEach(function(n) {
|
||||
n.changed = true;
|
||||
n.inputs = activeSubflow.in.length;
|
||||
n.outputs = activeSubflow.out.length;
|
||||
while (n.outputs < n.ports.length) {
|
||||
n.ports.pop();
|
||||
}
|
||||
n.resize = true;
|
||||
n.dirty = true;
|
||||
});
|
||||
RED.editor.validateNode(activeSubflow);
|
||||
var instances = RED.subflow.refresh(true);
|
||||
if (instances) {
|
||||
subflowInstances = instances.instances;
|
||||
}
|
||||
|
||||
moving_set = [];
|
||||
if (removedNodes.length > 0 || removedSubflowOutputs.length > 0 || removedSubflowInputs.length > 0) {
|
||||
RED.nodes.dirty(true);
|
||||
@@ -838,7 +811,18 @@ RED.view = (function() {
|
||||
removedLinks.push(selected_link);
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
RED.history.push({t:'delete',nodes:removedNodes,links:removedLinks,subflowOutputs:removedSubflowOutputs,subflowInputs:removedSubflowInputs,dirty:startDirty});
|
||||
var historyEvent = {
|
||||
t:'delete',
|
||||
nodes:removedNodes,
|
||||
links:removedLinks,
|
||||
subflowOutputs:removedSubflowOutputs,
|
||||
subflowInputs:removedSubflowInputs,
|
||||
subflow: {
|
||||
instances: subflowInstances
|
||||
},
|
||||
dirty:startDirty
|
||||
};
|
||||
RED.history.push(historyEvent);
|
||||
|
||||
selected_link = null;
|
||||
updateActiveNodes();
|
||||
@@ -945,7 +929,22 @@ RED.view = (function() {
|
||||
if (!existingLink) {
|
||||
var link = {source: src, sourcePort:src_port, target: dst};
|
||||
RED.nodes.addLink(link);
|
||||
RED.history.push({t:'add',links:[link],dirty:RED.nodes.dirty()});
|
||||
var historyEvent = {
|
||||
t:'add',
|
||||
links:[link],
|
||||
dirty:RED.nodes.dirty()
|
||||
};
|
||||
if (activeSubflow) {
|
||||
var subflowRefresh = RED.subflow.refresh(true);
|
||||
if (subflowRefresh) {
|
||||
historyEvent.subflow = {
|
||||
id:activeSubflow.id,
|
||||
changed: activeSubflow.changed,
|
||||
instances: subflowRefresh.instances
|
||||
}
|
||||
}
|
||||
}
|
||||
RED.history.push(historyEvent);
|
||||
updateActiveNodes();
|
||||
RED.nodes.dirty(true);
|
||||
} else {
|
||||
@@ -1066,7 +1065,7 @@ RED.view = (function() {
|
||||
function showTouchMenu(obj,pos) {
|
||||
var mdn = mousedown_node;
|
||||
var options = [];
|
||||
options.push({name:"delete",disabled:(moving_set.length===0),onselect:function() {deleteSelection();}});
|
||||
options.push({name:"delete",disabled:(moving_set.length===0 && selected_link === null),onselect:function() {deleteSelection();}});
|
||||
options.push({name:"cut",disabled:(moving_set.length===0),onselect:function() {copySelection();deleteSelection();}});
|
||||
options.push({name:"copy",disabled:(moving_set.length===0),onselect:function() {copySelection();}});
|
||||
options.push({name:"paste",disabled:(clipboard.length===0),onselect:function() {importNodes(clipboard,true);}});
|
||||
@@ -1360,7 +1359,9 @@ RED.view = (function() {
|
||||
var text = node.append('svg:text').attr('class','node_label').attr('x', 38).attr('dy', '.35em').attr('text-anchor','start');
|
||||
if (d._def.align) {
|
||||
text.attr('class','node_label node_label_'+d._def.align);
|
||||
text.attr('text-anchor','end');
|
||||
if (d._def.align === "right") {
|
||||
text.attr('text-anchor','end');
|
||||
}
|
||||
}
|
||||
|
||||
var status = node.append("svg:g").attr("class","node_status_group").style("display","none");
|
||||
@@ -1371,14 +1372,7 @@ RED.view = (function() {
|
||||
|
||||
var statusLabel = status.append("svg:text")
|
||||
.attr("class","node_status_label")
|
||||
.attr('x',20).attr('y',9)
|
||||
.style({
|
||||
'stroke-width': 0,
|
||||
'fill': '#888',
|
||||
'font-size':'9pt',
|
||||
'stroke':'#000',
|
||||
'text-anchor':'start'
|
||||
});
|
||||
.attr('x',20).attr('y',9);
|
||||
|
||||
//node.append("circle").attr({"class":"centerDot","cx":0,"cy":0,"r":5});
|
||||
|
||||
@@ -1616,6 +1610,14 @@ RED.view = (function() {
|
||||
redraw();
|
||||
focusView();
|
||||
d3.event.stopPropagation();
|
||||
|
||||
var obj = d3.select(document.body);
|
||||
var touch0 = d3.event.touches.item(0);
|
||||
var pos = [touch0.pageX,touch0.pageY];
|
||||
touchStartTime = setTimeout(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")
|
||||
@@ -1695,6 +1697,10 @@ RED.view = (function() {
|
||||
*/
|
||||
function importNodes(newNodesStr,touchImport) {
|
||||
try {
|
||||
var activeSubflowChanged;
|
||||
if (activeSubflow) {
|
||||
activeSubflowChanged = activeSubflow.changed;
|
||||
}
|
||||
var result = RED.nodes.import(newNodesStr,true);
|
||||
if (result) {
|
||||
var new_nodes = result[0];
|
||||
@@ -1702,7 +1708,7 @@ RED.view = (function() {
|
||||
var new_workspaces = result[2];
|
||||
var new_subflows = result[3];
|
||||
|
||||
var new_ms = new_nodes.filter(function(n) { return n.z == RED.workspaces.active() }).map(function(n) { return {n:n};});
|
||||
var new_ms = new_nodes.filter(function(n) { return n.hasOwnProperty('x') && n.hasOwnProperty('y') && n.z == RED.workspaces.active() }).map(function(n) { return {n:n};});
|
||||
var new_node_ids = new_nodes.map(function(n){ return n.id; });
|
||||
|
||||
// TODO: pick a more sensible root node
|
||||
@@ -1752,14 +1758,25 @@ RED.view = (function() {
|
||||
moving_set = new_ms;
|
||||
}
|
||||
|
||||
RED.history.push({
|
||||
var historyEvent = {
|
||||
t:'add',
|
||||
nodes:new_node_ids,
|
||||
links:new_links,
|
||||
workspaces:new_workspaces,
|
||||
subflows:new_subflows,
|
||||
dirty:RED.nodes.dirty()
|
||||
});
|
||||
};
|
||||
if (activeSubflow) {
|
||||
var subflowRefresh = RED.subflow.refresh(true);
|
||||
if (subflowRefresh) {
|
||||
historyEvent.subflow = {
|
||||
id:activeSubflow.id,
|
||||
changed: activeSubflowChanged,
|
||||
instances: subflowRefresh.instances
|
||||
}
|
||||
}
|
||||
}
|
||||
RED.history.push(historyEvent);
|
||||
|
||||
updateActiveNodes();
|
||||
redraw();
|
||||
@@ -1788,7 +1805,6 @@ RED.view = (function() {
|
||||
if (updateActive) {
|
||||
updateActiveNodes();
|
||||
}
|
||||
RED.workspaces.refresh();
|
||||
redraw();
|
||||
},
|
||||
focus: focusView,
|
||||
|
@@ -84,19 +84,13 @@ RED.workspaces = (function() {
|
||||
workspace_tabs = RED.tabs.create({
|
||||
id: "workspace-tabs",
|
||||
onchange: function(tab) {
|
||||
if (tab.type == "subflow") {
|
||||
$("#chart").css({"margin-top": "40px"});
|
||||
$("#workspace-toolbar").show();
|
||||
} else {
|
||||
$("#workspace-toolbar").hide();
|
||||
$("#chart").css({"margin-top": "0"});
|
||||
}
|
||||
var event = {
|
||||
old: activeWorkspace
|
||||
}
|
||||
activeWorkspace = tab.id;
|
||||
event.workspace = activeWorkspace;
|
||||
RED.events.emit("workspace:change",event);
|
||||
RED.sidebar.config.refresh();
|
||||
},
|
||||
ondblclick: function(tab) {
|
||||
if (tab.type != "subflow") {
|
||||
@@ -221,6 +215,7 @@ RED.workspaces = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
add: addWorkspace,
|
||||
@@ -242,7 +237,7 @@ RED.workspaces = (function() {
|
||||
if (!workspace_tabs.contains(id)) {
|
||||
var sf = RED.nodes.subflow(id);
|
||||
if (sf) {
|
||||
addWorkspace({type:"subflow",id:id,label:RED._("subflow.tabLabel",{name:sf.name}), closeable: true});
|
||||
addWorkspace({type:"subflow",id:id,icon:"red/images/subflow_tab.png",label:sf.name, closeable: true});
|
||||
}
|
||||
}
|
||||
workspace_tabs.activateTab(id);
|
||||
@@ -250,9 +245,10 @@ RED.workspaces = (function() {
|
||||
refresh: function() {
|
||||
RED.nodes.eachSubflow(function(sf) {
|
||||
if (workspace_tabs.contains(sf.id)) {
|
||||
workspace_tabs.renameTab(sf.id,RED._("subflow.tabLabel",{name:sf.name}));
|
||||
workspace_tabs.renameTab(sf.id,sf.name);
|
||||
}
|
||||
});
|
||||
RED.sidebar.config.refresh();
|
||||
},
|
||||
resize: function() {
|
||||
workspace_tabs.resize();
|
||||
|
@@ -23,8 +23,8 @@
|
||||
border-color: rgb(214, 97, 95) !important;
|
||||
}
|
||||
|
||||
.leftButton {
|
||||
margin-right: 200px !important;
|
||||
.ui-dialog .ui-dialog-buttonpane button.leftButton {
|
||||
margin-right: 40px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
@@ -42,7 +42,6 @@
|
||||
|
||||
.form-tips {
|
||||
background: #ffe;
|
||||
font-size: 12px;
|
||||
padding: 8px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid $secondary-border-color;
|
||||
@@ -67,7 +66,7 @@
|
||||
.editor-button {
|
||||
@include workspace-button;
|
||||
height: 34px;
|
||||
line-height: 30px;
|
||||
line-height: 32px;
|
||||
font-size: 13px;
|
||||
border-radius: 4px;
|
||||
padding: 0 10px;
|
||||
@@ -79,3 +78,29 @@
|
||||
border-radius: 2px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
#node-config-dialog-scope-container {
|
||||
cursor: auto;
|
||||
float: right;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
#node-config-dialog-scope-warning {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
color: #AD1625;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#node-config-dialog-scope {
|
||||
margin: 1px 0 0 0;
|
||||
padding: 0;
|
||||
height: 22px;
|
||||
width: 110px;
|
||||
}
|
||||
#node-config-dialog-user-count {
|
||||
vertical-align: middle;
|
||||
display:inline-block;
|
||||
margin-top: 10px;
|
||||
margin-right: 20px;
|
||||
float:left;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@
|
||||
}
|
||||
|
||||
.lasso {
|
||||
stroke-width: 2px;
|
||||
stroke-width: 1px;
|
||||
stroke: #ff7f0e;
|
||||
fill: rgba(20,125,255,0.1);
|
||||
stroke-dasharray: 10 5;
|
||||
@@ -165,6 +165,15 @@
|
||||
.node_hovered {
|
||||
}
|
||||
|
||||
.node_status_label {
|
||||
@include disable-selection;
|
||||
stroke-width: 0;
|
||||
fill: #888;
|
||||
font-size:9pt;
|
||||
stroke:#000;
|
||||
text-anchor:start;
|
||||
}
|
||||
|
||||
.port_hovered {
|
||||
stroke: $port-selected-color;
|
||||
fill: $port-selected-color;
|
||||
|
@@ -47,7 +47,7 @@ span.logo {
|
||||
font-size: 30px;
|
||||
line-height: 30px;
|
||||
text-decoration: none;
|
||||
|
||||
|
||||
span {
|
||||
vertical-align: middle;
|
||||
font-size: 16px !important;
|
||||
@@ -55,14 +55,14 @@ span.logo {
|
||||
img {
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.header-toolbar {
|
||||
@@ -70,13 +70,13 @@ span.logo {
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
float: right;
|
||||
|
||||
|
||||
> li {
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,19 +97,19 @@ span.logo {
|
||||
vertical-align: middle;
|
||||
border-left: 2px solid #000;
|
||||
border-right: 2px solid #000;
|
||||
|
||||
|
||||
&:hover {
|
||||
border-color: $headerMenuItemHover;
|
||||
}
|
||||
}
|
||||
|
||||
.button-group {
|
||||
#header .button-group {
|
||||
display: inline-block;
|
||||
margin: auto 15px;
|
||||
vertical-align: middle;
|
||||
clear: both;
|
||||
}
|
||||
.button-group > a {
|
||||
#header .button-group > a {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
line-height: 22px;
|
||||
@@ -122,11 +122,11 @@ span.logo {
|
||||
.deploy-button {
|
||||
background: $deployButton;
|
||||
color: #eee !important;
|
||||
|
||||
|
||||
&:hover {
|
||||
background: $deployButtonHover;
|
||||
}
|
||||
|
||||
|
||||
&:active {
|
||||
background: $deployButtonActive;
|
||||
color: #ccc !important;
|
||||
@@ -134,18 +134,18 @@ span.logo {
|
||||
}
|
||||
|
||||
#btn-deploy {
|
||||
|
||||
|
||||
padding: 4px 12px;
|
||||
|
||||
|
||||
&.disabled {
|
||||
cursor: default;
|
||||
background: $deployDisabledButton;
|
||||
color: #999 !important;
|
||||
|
||||
|
||||
img {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
|
||||
&+ #btn-deploy-options {
|
||||
background: $deployDisabledButton;
|
||||
color: #ddd;
|
||||
@@ -157,7 +157,7 @@ span.logo {
|
||||
background: $deployDisabledButton;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
img {
|
||||
margin-right: 8px;
|
||||
}
|
||||
@@ -264,4 +264,3 @@ span.logo {
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
@@ -14,6 +14,15 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
.ui-widget {
|
||||
font-size: 14px !important;
|
||||
font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif !important;
|
||||
}
|
||||
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button {
|
||||
font-size: 14px !important;
|
||||
font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif !important;
|
||||
}
|
||||
|
||||
/* jQuery Theme overrides */
|
||||
.ui-tabs .ui-tabs-panel {
|
||||
padding: 0px;
|
||||
@@ -27,14 +36,16 @@
|
||||
|
||||
.ui-dialog {
|
||||
border-radius: 1px;
|
||||
border: 1px solid #eee;
|
||||
background: #fff;
|
||||
padding: 0;
|
||||
box-shadow: 2px 2px 12px rgba(0,0,0,0.2);
|
||||
@include component-shadow;
|
||||
}
|
||||
.ui-dialog .ui-dialog-content {
|
||||
padding: 25px 25px 10px 25px;
|
||||
}
|
||||
.ui-dialog .ui-dialog-title {
|
||||
width: auto;
|
||||
}
|
||||
.ui-dialog .ui-dialog-titlebar {
|
||||
padding: 10px;
|
||||
background: #f3f3f3;
|
||||
@@ -51,3 +62,14 @@
|
||||
.ui-dialog-no-close .ui-dialog-titlebar-close {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ui-dialog-buttonset {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
|
||||
float: none;
|
||||
}
|
||||
.ui-dialog .ui-dialog-buttonpane {
|
||||
padding: .3em 1em .5em 1em;
|
||||
}
|
||||
|
@@ -50,10 +50,12 @@
|
||||
}
|
||||
&:not(.disabled):focus {
|
||||
color: $workspace-button-color-focus;
|
||||
text-decoration: none;
|
||||
}
|
||||
&:not(.disabled):active {
|
||||
color: $workspace-button-color-active;
|
||||
background: $workspace-button-background-active;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
@mixin component-footer {
|
||||
@@ -75,4 +77,14 @@
|
||||
line-height: 17px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
&.text-button {
|
||||
width: auto;
|
||||
padding: 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin component-shadow {
|
||||
border: 1px solid $secondary-border-color;
|
||||
box-shadow: 1px 1px 4px rgba(0,0,0,0.2);
|
||||
|
||||
}
|
||||
|
@@ -200,7 +200,7 @@
|
||||
left: auto;
|
||||
right: 0;
|
||||
border-right: none;
|
||||
border-left: 2px solid rgba(0,0,0,0.1);
|
||||
border-left: 1px solid rgba(0,0,0,0.1);
|
||||
}
|
||||
.palette_icon {
|
||||
display: inline-block;
|
||||
|
53
editor/sass/popover.scss
Normal file
53
editor/sass/popover.scss
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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-popover {
|
||||
display: none;
|
||||
position: absolute;
|
||||
width: 300px;
|
||||
padding: 10px;
|
||||
height: auto;
|
||||
background: #fff;
|
||||
|
||||
z-index: 1000;
|
||||
font-size: 14px;
|
||||
line-height: 1.4em;
|
||||
@include component-shadow;
|
||||
}
|
||||
.red-ui-popover:after, .red-ui-popover:before {
|
||||
right: 100%;
|
||||
top: 50%;
|
||||
border: solid transparent;
|
||||
content: " ";
|
||||
height: 0;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.red-ui-popover:after {
|
||||
border-color: rgba(136, 183, 213, 0);
|
||||
border-right-color: #fff;
|
||||
border-width: 10px;
|
||||
margin-top: -10px;
|
||||
}
|
||||
.red-ui-popover:before {
|
||||
border-color: rgba(194, 225, 245, 0);
|
||||
border-right-color: $primary-border-color;
|
||||
border-width: 11px;
|
||||
margin-top: -11px;
|
||||
}
|
@@ -38,7 +38,7 @@
|
||||
@import "tabs";
|
||||
@import "tab-config";
|
||||
@import "tab-info";
|
||||
|
||||
@import "popover";
|
||||
@import "flow";
|
||||
|
||||
@import "dragdrop";
|
||||
@@ -47,9 +47,10 @@
|
||||
|
||||
|
||||
body {
|
||||
font: 14px "Helvetica" !important;
|
||||
padding-top: 100px;
|
||||
background: #f3f3f3;
|
||||
font-size: 14px;
|
||||
font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;
|
||||
padding-top: 100px;
|
||||
background: #f3f3f3;
|
||||
}
|
||||
|
||||
#main-container {
|
||||
|
@@ -13,51 +13,57 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
ul.tab-config-list {
|
||||
list-style-type: none;
|
||||
padding: 3px;
|
||||
margin: 0;
|
||||
|
||||
.sidebar-node-config {
|
||||
background: #f3f3f3;
|
||||
height: 100%;
|
||||
overflow-y:auto;
|
||||
@include disable-selection;
|
||||
}
|
||||
|
||||
ul.tab-config-list li {
|
||||
max-width: 400px;
|
||||
font-size: 13px;
|
||||
background: #f3f3f3;
|
||||
margin: 10px auto;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #ccc;
|
||||
padding: 3px 8px;
|
||||
}
|
||||
div.tab-config-list-type {
|
||||
}
|
||||
.config-node-list {
|
||||
margin: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
.config_node {
|
||||
width: 160px;
|
||||
height: 30px;
|
||||
background: #f3f3f3;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
|
||||
div.tab-config-list-entry {
|
||||
position: relative;
|
||||
margin: 4px 0;
|
||||
padding: 8px 4px 8px 10px;
|
||||
background: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
div.tab-config-list-entry:hover {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
|
||||
div.tab-config-list-label {
|
||||
}
|
||||
div.tab-config-list-users {
|
||||
position: absolute;
|
||||
right: 3px;
|
||||
top: 3px;
|
||||
bottom: 3px;
|
||||
line-height: 27px;
|
||||
font-size: 11px;
|
||||
background: #f6f6f6;
|
||||
float: right;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 3px;
|
||||
padding: 1px 5px;
|
||||
}
|
||||
.palette_label {
|
||||
margin-left: 8px;
|
||||
line-height: 24px;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.palette_icon_container {
|
||||
font-size: 12px;
|
||||
line-height: 30px;
|
||||
background-color: #e8e8e8;
|
||||
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;
|
||||
}
|
||||
|
@@ -21,13 +21,10 @@ ul.red-ui-tabs {
|
||||
display: block;
|
||||
height: 35px;
|
||||
box-sizing:border-box;
|
||||
white-space: nowrap;
|
||||
border-bottom: 1px solid $primary-border-color;
|
||||
background: #fff;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
@include disable-selection;
|
||||
}
|
||||
|
||||
ul.red-ui-tabs li {
|
||||
@@ -49,6 +46,7 @@ ul.red-ui-tabs li {
|
||||
|
||||
ul.red-ui-tabs li a.red-ui-tab-label {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
padding-left: 12px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -110,3 +108,16 @@ ul.red-ui-tabs li.active {
|
||||
ul.red-ui-tabs li.active a {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
ul.red-ui-tabs li img {
|
||||
margin-left: -8px;
|
||||
margin-right: 3px;
|
||||
margin-top: -2px;
|
||||
opacity: 0.1;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
ul.red-ui-tabs li.active img {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@
|
||||
left:0px;
|
||||
right:0px;
|
||||
box-sizing:border-box;
|
||||
transition: right 0.2s ease;
|
||||
}
|
||||
|
||||
#chart svg:focus {
|
||||
|
@@ -17,21 +17,60 @@
|
||||
|
||||
#workspace-toolbar {
|
||||
display: none;
|
||||
color: $workspace-button-color;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
position: absolute;
|
||||
top: 35px;
|
||||
left:0;
|
||||
width: 100%;
|
||||
right: 0;
|
||||
padding: 7px;
|
||||
height: 40px;
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid $secondary-border-color;
|
||||
}
|
||||
white-space: nowrap;
|
||||
transition: right 0.2s ease;
|
||||
overflow: hidden;
|
||||
|
||||
.button {
|
||||
@include workspace-button;
|
||||
margin-right: 10px;
|
||||
padding: 2px 8px;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
@include disable-selection;
|
||||
|
||||
.button:first-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&:not(.spinner-group) {
|
||||
.button:not(:first-child) {
|
||||
border-left: none;
|
||||
}
|
||||
}
|
||||
.button.active {
|
||||
background: $workspace-button-background-active;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.spinner-value {
|
||||
width: 25px;
|
||||
color: $workspace-button-color;
|
||||
padding: 0 5px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
border-top: 1px solid $secondary-border-color;
|
||||
border-bottom: 1px solid $secondary-border-color;
|
||||
margin: 0;
|
||||
height: 24px;
|
||||
font-size: 12px;
|
||||
background: #f9f9f9;
|
||||
line-height: 22px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#workspace-toolbar .button {
|
||||
@include workspace-button;
|
||||
line-height: 18px;
|
||||
font-size: 12px;
|
||||
margin-right: 5px;
|
||||
padding: 2px 8px;
|
||||
}
|
||||
|
@@ -58,12 +58,7 @@
|
||||
<ul id="workspace-tabs"></ul>
|
||||
<div id="workspace-add-tab"><a id="btn-workspace-add-tab" href="#"><i class="fa fa-plus"></i></a></div>
|
||||
<div id="chart"></div>
|
||||
<div id="workspace-toolbar">
|
||||
<a class="button" id="workspace-subflow-edit" href="#" data-i18n="[append]subflow.editSubflowName"><i class="fa fa-pencil"></i> </a>
|
||||
<a class="button disabled" id="workspace-subflow-add-input" href="#" data-i18n="[append]subflow.input"><i class="fa fa-plus"></i> </a>
|
||||
<a class="button" id="workspace-subflow-add-output" href="#" data-i18n="[append]subflow.output"><i class="fa fa-plus"></i> </a>
|
||||
<a class="button" id="workspace-subflow-delete" href="#" data-i18n="[append]subflow.deleteSubflow"><i class="fa fa-trash"></i> </a>
|
||||
</div>
|
||||
<div id="workspace-toolbar"></div>
|
||||
<div id="workspace-footer">
|
||||
<a class="workspace-footer-button" id="btn-zoom-out" href="#"><i class="fa fa-minus"></i></a>
|
||||
<a class="workspace-footer-button" id="btn-zoom-zero" href="#"><i class="fa fa-circle-o"></i></a>
|
||||
@@ -84,24 +79,33 @@
|
||||
<div id="dropTarget"><div data-i18n="[append]workspace.dropFlowHere"><br/><i class="fa fa-download"></i></div></div>
|
||||
|
||||
<div id="dialog" class="hide"><form id="dialog-form" class="form-horizontal"></form></div>
|
||||
<div id="node-config-dialog" class="hide"><form id="dialog-config-form" class="form-horizontal"></form><div class="form-tips" id="node-config-dialog-user-count"></div></div>
|
||||
<div id="node-config-dialog" class="hide"><form id="dialog-config-form" class="form-horizontal"><div id="node-config-dialog-edit-form"></div><!--<div id="node-config-dialog-toolbar" class="form-row"><label><span>Node scope</span></label><select id="node-config-dialog-scope"></select></div>--></form></div>
|
||||
<div id="subflow-dialog" class="hide">
|
||||
<form class="form-horizontal">
|
||||
<div class="form-row">
|
||||
<label data-i18n="common.label.name"></label><input type="text" id="subflow-input-name">
|
||||
<label for="subflow-input-name" data-i18n="common.label.name"></label><input type="text" id="subflow-input-name">
|
||||
</div>
|
||||
<div class="form-row" style="margin-bottom: 0px;">
|
||||
<label for="subflow-input-info" data-i18n="subflow.info"></label>
|
||||
<a href="https://help.github.com/articles/markdown-basics/" style="font-size: 0.8em; float: right;" data-i18n="[html]subflow.format"></a>
|
||||
</div>
|
||||
<div class="form-row node-text-editor-row">
|
||||
<div style="height: 250px;" class="node-text-editor" id="subflow-input-info-editor"></div>
|
||||
</div>
|
||||
<div class="form-row form-tips" id="subflow-dialog-user-count"></div>
|
||||
</form>
|
||||
<div class="form-tips" id="subflow-dialog-user-count"></div>
|
||||
</div>
|
||||
|
||||
<div id="node-dialog-confirm-deploy" class="hide">
|
||||
<form class="form-horizontal">
|
||||
<div id="node-dialog-confirm-deploy-config" style="text-align: left; padding-top: 30px;" data-i18n="[prepend]deploy.confirm.improperlyConfigured;[append]deploy.confirm.confirm"> </div>
|
||||
<div id="node-dialog-confirm-deploy-config" style="text-align: left; padding-top: 30px;" data-i18n="[prepend]deploy.confirm.improperlyConfigured;[append]deploy.confirm.confirm">
|
||||
<ul style="font-size: 0.9em; width: 400px; margin: 10px auto; text-align: left;" id="node-dialog-confirm-deploy-invalid-list"></ul>
|
||||
</div>
|
||||
<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>
|
||||
<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>
|
||||
<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/font-awesome/css/font-awesome.min.css
vendored
Normal file → Executable file
4
editor/vendor/font-awesome/css/font-awesome.min.css
vendored
Normal file → Executable file
File diff suppressed because one or more lines are too long
BIN
editor/vendor/font-awesome/fonts/FontAwesome.otf
vendored
Normal file → Executable file
BIN
editor/vendor/font-awesome/fonts/FontAwesome.otf
vendored
Normal file → Executable file
Binary file not shown.
BIN
editor/vendor/font-awesome/fonts/fontawesome-webfont.eot
vendored
Normal file → Executable file
BIN
editor/vendor/font-awesome/fonts/fontawesome-webfont.eot
vendored
Normal file → Executable file
Binary file not shown.
105
editor/vendor/font-awesome/fonts/fontawesome-webfont.svg
vendored
Normal file → Executable file
105
editor/vendor/font-awesome/fonts/fontawesome-webfont.svg
vendored
Normal file → Executable file
@@ -399,7 +399,7 @@
|
||||
<glyph unicode="" d="M1024 960v-640q0 -26 -19 -45t-45 -19q-20 0 -37 12l-448 320q-27 19 -27 52t27 52l448 320q17 12 37 12q26 0 45 -19t19 -45zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5z M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" d="M1024 640q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5 t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1023 349l102 -204q-58 -179 -210 -290t-339 -111q-156 0 -288.5 77.5t-210 210t-77.5 288.5q0 181 104.5 330t274.5 211l17 -131q-122 -54 -195 -165.5t-73 -244.5q0 -185 131.5 -316.5t316.5 -131.5q126 0 232.5 65t165 175.5t49.5 236.5zM1571 249l58 -114l-256 -128 q-13 -7 -29 -7q-40 0 -57 35l-239 477h-472q-24 0 -42.5 16.5t-21.5 40.5l-96 779q-2 16 6 42q14 51 57 82.5t97 31.5q66 0 113 -47t47 -113q0 -69 -52 -117.5t-120 -41.5l37 -289h423v-128h-407l16 -128h455q40 0 57 -35l228 -455z" />
|
||||
<glyph unicode="" d="M1254 899q16 85 -21 132q-52 65 -187 45q-17 -3 -41 -12.5t-57.5 -30.5t-64.5 -48.5t-59.5 -70t-44.5 -91.5q80 7 113.5 -16t26.5 -99q-5 -52 -52 -143q-43 -78 -71 -99q-44 -32 -87 14q-23 24 -37.5 64.5t-19 73t-10 84t-8.5 71.5q-23 129 -34 164q-12 37 -35.5 69 t-50.5 40q-57 16 -127 -25q-54 -32 -136.5 -106t-122.5 -102v-7q16 -8 25.5 -26t21.5 -20q21 -3 54.5 8.5t58 10.5t41.5 -30q11 -18 18.5 -38.5t15 -48t12.5 -40.5q17 -46 53 -187q36 -146 57 -197q42 -99 103 -125q43 -12 85 -1.5t76 31.5q131 77 250 237 q104 139 172.5 292.5t82.5 226.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" d="M1292 898q10 216 -161 222q-231 8 -312 -261q44 19 82 19q85 0 74 -96q-4 -57 -74 -167t-105 -110q-43 0 -82 169q-13 54 -45 255q-30 189 -160 177q-59 -7 -164 -100l-81 -72l-81 -72l52 -67q76 52 87 52q57 0 107 -179q15 -55 45 -164.5t45 -164.5q68 -179 164 -179 q157 0 383 294q220 283 226 444zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1152" d="M1152 704q0 -191 -94.5 -353t-256.5 -256.5t-353 -94.5h-160q-14 0 -23 9t-9 23v611l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v93l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v250q0 14 9 23t23 9h160 q14 0 23 -9t9 -23v-181l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-93l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-487q188 13 318 151t130 328q0 14 9 23t23 9h160q14 0 23 -9t9 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="1408" d="M1152 736v-64q0 -14 -9 -23t-23 -9h-352v-352q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v352h-352q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h352v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-352h352q14 0 23 -9t9 -23zM1280 288v832q0 66 -47 113t-113 47h-832 q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113zM1408 1120v-832q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="2176" d="M620 416q-110 -64 -268 -64h-128v64h-64q-13 0 -22.5 23.5t-9.5 56.5q0 24 7 49q-58 2 -96.5 10.5t-38.5 20.5t38.5 20.5t96.5 10.5q-7 25 -7 49q0 33 9.5 56.5t22.5 23.5h64v64h128q158 0 268 -64h1113q42 -7 106.5 -18t80.5 -14q89 -15 150 -40.5t83.5 -47.5t22.5 -40 t-22.5 -40t-83.5 -47.5t-150 -40.5q-16 -3 -80.5 -14t-106.5 -18h-1113zM1739 668q53 -36 53 -92t-53 -92l81 -30q68 48 68 122t-68 122zM625 400h1015q-217 -38 -456 -80q-57 0 -113 -24t-83 -48l-28 -24l-288 -288q-26 -26 -70.5 -45t-89.5 -19h-96l-93 464h29 q157 0 273 64zM352 816h-29l93 464h96q46 0 90 -19t70 -45l288 -288q4 -4 11 -10.5t30.5 -23t48.5 -29t61.5 -23t72.5 -10.5l456 -80h-1015q-116 64 -273 64z" />
|
||||
@@ -411,8 +411,8 @@
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M1774 700l18 -316q4 -69 -82 -128t-235 -93.5t-323 -34.5t-323 34.5t-235 93.5t-82 128l18 316l574 -181q22 -7 48 -7t48 7zM2304 1024q0 -23 -22 -31l-1120 -352q-4 -1 -10 -1t-10 1l-652 206q-43 -34 -71 -111.5t-34 -178.5q63 -36 63 -109q0 -69 -58 -107l58 -433 q2 -14 -8 -25q-9 -11 -24 -11h-192q-15 0 -24 11q-10 11 -8 25l58 433q-58 38 -58 107q0 73 65 111q11 207 98 330l-333 104q-22 8 -22 31t22 31l1120 352q4 1 10 1t10 -1l1120 -352q22 -8 22 -31z" />
|
||||
<glyph unicode="" d="M859 579l13 -707q-62 11 -105 11q-41 0 -105 -11l13 707q-40 69 -168.5 295.5t-216.5 374.5t-181 287q58 -15 108 -15q43 0 111 15q63 -111 133.5 -229.5t167 -276.5t138.5 -227q37 61 109.5 177.5t117.5 190t105 176t107 189.5q54 -14 107 -14q56 0 114 14v0 q-28 -39 -60 -88.5t-49.5 -78.5t-56.5 -96t-49 -84q-146 -248 -353 -610z" />
|
||||
<glyph unicode="" horiz-adv-x="1280" d="M981 197q0 25 -7 49t-14.5 42t-27 41.5t-29.5 35t-38.5 34.5t-36.5 29t-41.5 30t-36.5 26q-16 2 -49 2q-53 0 -104.5 -7t-107 -25t-97 -46t-68.5 -74.5t-27 -105.5q0 -56 23.5 -102t61 -75.5t87 -50t100 -29t101.5 -8.5q58 0 111.5 13t99 39t73 73t27.5 109zM864 1055 q0 59 -17 125.5t-48 129t-84 103.5t-117 41q-42 0 -82.5 -19.5t-66.5 -52.5q-46 -59 -46 -160q0 -46 10 -97.5t31.5 -103t52 -92.5t75 -67t96.5 -26q37 0 77.5 16.5t65.5 43.5q53 56 53 159zM752 1536h417l-137 -88h-132q75 -63 113 -133t38 -160q0 -72 -24.5 -129.5 t-59.5 -93t-69.5 -65t-59 -61.5t-24.5 -66q0 -36 32 -70.5t77 -68t90.5 -73.5t77.5 -104t32 -142q0 -91 -49 -173q-71 -122 -209.5 -179.5t-298.5 -57.5q-132 0 -246.5 41.5t-172.5 137.5q-36 59 -36 131q0 81 44.5 150t118.5 115q131 82 404 100q-32 41 -47.5 73.5 t-15.5 73.5q0 40 21 85q-46 -4 -68 -4q-148 0 -249.5 96.5t-101.5 244.5q0 82 36 159t99 131q76 66 182 98t218 32z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M1509 107q0 -14 -12 -29q-52 -59 -147.5 -83t-196.5 -24q-252 0 -346 107q-12 15 -12 29q0 17 12 29.5t29 12.5q15 0 30 -12q58 -49 125.5 -66t159.5 -17t160 17t127 66q15 12 30 12q17 0 29 -12.5t12 -29.5zM978 498q0 -61 -43 -104t-104 -43q-60 0 -104.5 43.5 t-44.5 103.5q0 61 44 105t105 44t104 -44t43 -105zM1622 498q0 -61 -43 -104t-104 -43q-60 0 -104.5 43.5t-44.5 103.5q0 61 44 105t105 44t104 -44t43 -105zM415 793q-39 27 -88 27q-66 0 -113 -47t-47 -113q0 -72 54 -121q53 141 194 254zM2020 382q0 222 -249 387 q-128 85 -291.5 126.5t-331.5 41.5t-331.5 -41.5t-292.5 -126.5q-249 -165 -249 -387t249 -387q129 -85 292.5 -126.5t331.5 -41.5t331.5 41.5t291.5 126.5q249 165 249 387zM2137 660q0 66 -47 113t-113 47q-50 0 -93 -30q140 -114 192 -256q61 48 61 126zM1993 1335 q0 49 -34.5 83.5t-82.5 34.5q-49 0 -83.5 -34.5t-34.5 -83.5q0 -48 34.5 -82.5t83.5 -34.5q48 0 82.5 34.5t34.5 82.5zM2220 660q0 -65 -33 -122t-89 -90q5 -35 5 -66q0 -139 -79 -255.5t-208 -201.5q-140 -92 -313.5 -136.5t-354.5 -44.5t-355 44.5t-314 136.5 q-129 85 -208 201.5t-79 255.5q0 36 6 71q-53 33 -83.5 88.5t-30.5 118.5q0 100 71 171.5t172 71.5q91 0 159 -60q265 170 638 177l144 456q10 29 40 29q24 0 384 -90q24 55 74 88t110 33q82 0 141 -59t59 -142t-59 -141.5t-141 -58.5q-83 0 -141.5 58.5t-59.5 140.5 l-339 80l-125 -395q349 -15 603 -179q71 63 163 63q101 0 172 -71.5t71 -171.5z" />
|
||||
<glyph unicode="" d="M950 393q7 7 17.5 7t17.5 -7t7 -18t-7 -18q-65 -64 -208 -64h-1h-1q-143 0 -207 64q-8 7 -8 18t8 18q7 7 17.5 7t17.5 -7q49 -51 172 -51h1h1q122 0 173 51zM671 613q0 -37 -26 -64t-63 -27t-63 27t-26 64t26 63t63 26t63 -26t26 -63zM1214 1049q-29 0 -50 21t-21 50 q0 30 21 51t50 21q30 0 51 -21t21 -51q0 -29 -21 -50t-51 -21zM1216 1408q132 0 226 -94t94 -227v-894q0 -133 -94 -227t-226 -94h-896q-132 0 -226 94t-94 227v894q0 133 94 227t226 94h896zM1321 596q35 14 57 45.5t22 70.5q0 51 -36 87.5t-87 36.5q-60 0 -98 -48 q-151 107 -375 115l83 265l206 -49q1 -50 36.5 -85t84.5 -35q50 0 86 35.5t36 85.5t-36 86t-86 36q-36 0 -66 -20.5t-45 -53.5l-227 54q-9 2 -17.5 -2.5t-11.5 -14.5l-95 -302q-224 -4 -381 -113q-36 43 -93 43q-51 0 -87 -36.5t-36 -87.5q0 -37 19.5 -67.5t52.5 -45.5 q-7 -25 -7 -54q0 -98 74 -181.5t201.5 -132t278.5 -48.5q150 0 277.5 48.5t201.5 132t74 181.5q0 27 -6 54zM971 702q37 0 63 -26t26 -63t-26 -64t-63 -27t-63 27t-26 64t26 63t63 26z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1095 369q16 -16 0 -31q-62 -62 -199 -62t-199 62q-16 15 0 31q6 6 15 6t15 -6q48 -49 169 -49q120 0 169 49q6 6 15 6t15 -6zM788 550q0 -37 -26 -63t-63 -26t-63.5 26t-26.5 63q0 38 26.5 64t63.5 26t63 -26.5t26 -63.5zM1183 550q0 -37 -26.5 -63t-63.5 -26t-63 26 t-26 63t26 63.5t63 26.5t63.5 -26t26.5 -64zM1434 670q0 49 -35 84t-85 35t-86 -36q-130 90 -311 96l63 283l200 -45q0 -37 26 -63t63 -26t63.5 26.5t26.5 63.5t-26.5 63.5t-63.5 26.5q-54 0 -80 -50l-221 49q-19 5 -25 -16l-69 -312q-180 -7 -309 -97q-35 37 -87 37 q-50 0 -85 -35t-35 -84q0 -35 18.5 -64t49.5 -44q-6 -27 -6 -56q0 -142 140 -243t337 -101q198 0 338 101t140 243q0 32 -7 57q30 15 48 43.5t18 63.5zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191 t348 71t348 -71t286 -191t191 -286t71 -348z" />
|
||||
<glyph unicode="" d="M939 407q13 -13 0 -26q-53 -53 -171 -53t-171 53q-13 13 0 26q5 6 13 6t13 -6q42 -42 145 -42t145 42q5 6 13 6t13 -6zM676 563q0 -31 -23 -54t-54 -23t-54 23t-23 54q0 32 22.5 54.5t54.5 22.5t54.5 -22.5t22.5 -54.5zM1014 563q0 -31 -23 -54t-54 -23t-54 23t-23 54 q0 32 22.5 54.5t54.5 22.5t54.5 -22.5t22.5 -54.5zM1229 666q0 42 -30 72t-73 30q-42 0 -73 -31q-113 78 -267 82l54 243l171 -39q1 -32 23.5 -54t53.5 -22q32 0 54.5 22.5t22.5 54.5t-22.5 54.5t-54.5 22.5q-48 0 -69 -43l-189 42q-17 5 -21 -13l-60 -268q-154 -6 -265 -83 q-30 32 -74 32q-43 0 -73 -30t-30 -72q0 -30 16 -55t42 -38q-5 -25 -5 -48q0 -122 120 -208.5t289 -86.5q170 0 290 86.5t120 208.5q0 25 -6 49q25 13 40.5 37.5t15.5 54.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960 q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" d="M866 697l90 27v62q0 79 -58 135t-138 56t-138 -55.5t-58 -134.5v-283q0 -20 -14 -33.5t-33 -13.5t-32.5 13.5t-13.5 33.5v120h-151v-122q0 -82 57.5 -139t139.5 -57q81 0 138.5 56.5t57.5 136.5v280q0 19 13.5 33t33.5 14q19 0 32.5 -14t13.5 -33v-54zM1199 502v122h-150 v-126q0 -20 -13.5 -33.5t-33.5 -13.5q-19 0 -32.5 14t-13.5 33v123l-90 -26l-60 28v-123q0 -80 58 -137t139 -57t138.5 57t57.5 139zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103 t385.5 -103t279.5 -279.5t103 -385.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M1062 824v118q0 42 -30 72t-72 30t-72 -30t-30 -72v-612q0 -175 -126 -299t-303 -124q-178 0 -303.5 125.5t-125.5 303.5v266h328v-262q0 -43 30 -72.5t72 -29.5t72 29.5t30 72.5v620q0 171 126.5 292t301.5 121q176 0 302 -122t126 -294v-136l-195 -58zM1592 602h328 v-266q0 -178 -125.5 -303.5t-303.5 -125.5q-177 0 -303 124.5t-126 300.5v268l131 -61l195 58v-270q0 -42 30 -71.5t72 -29.5t72 29.5t30 71.5v275z" />
|
||||
<glyph unicode="" d="M1472 160v480h-704v704h-480q-93 0 -158.5 -65.5t-65.5 -158.5v-480h704v-704h480q93 0 158.5 65.5t65.5 158.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 t84.5 -203.5z" />
|
||||
@@ -459,7 +459,7 @@
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M874 -102v-66q-208 6 -385 109.5t-283 275.5l58 34q29 -49 73 -99l65 57q148 -168 368 -212l-17 -86q65 -12 121 -13zM276 428l-83 -28q22 -60 49 -112l-57 -33q-98 180 -98 385t98 385l57 -33q-30 -56 -49 -112l82 -28q-35 -100 -35 -212q0 -109 36 -212zM1528 251 l58 -34q-106 -172 -283 -275.5t-385 -109.5v66q56 1 121 13l-17 86q220 44 368 212l65 -57q44 50 73 99zM1377 805l-233 -80q14 -42 14 -85t-14 -85l232 -80q-31 -92 -98 -169l-185 162q-57 -67 -147 -85l48 -241q-52 -10 -98 -10t-98 10l48 241q-90 18 -147 85l-185 -162 q-67 77 -98 169l232 80q-14 42 -14 85t14 85l-233 80q33 93 99 169l185 -162q59 68 147 86l-48 240q44 10 98 10t98 -10l-48 -240q88 -18 147 -86l185 162q66 -76 99 -169zM874 1448v-66q-65 -2 -121 -13l17 -86q-220 -42 -368 -211l-65 56q-38 -42 -73 -98l-57 33 q106 172 282 275.5t385 109.5zM1705 640q0 -205 -98 -385l-57 33q27 52 49 112l-83 28q36 103 36 212q0 112 -35 212l82 28q-19 56 -49 112l57 33q98 -180 98 -385zM1585 1063l-57 -33q-35 56 -73 98l-65 -56q-148 169 -368 211l17 86q-56 11 -121 13v66q209 -6 385 -109.5 t282 -275.5zM1748 640q0 173 -67.5 331t-181.5 272t-272 181.5t-331 67.5t-331 -67.5t-272 -181.5t-181.5 -272t-67.5 -331t67.5 -331t181.5 -272t272 -181.5t331 -67.5t331 67.5t272 181.5t181.5 272t67.5 331zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71 t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
|
||||
<glyph unicode="" d="M582 228q0 -66 -93 -66q-107 0 -107 63q0 64 98 64q102 0 102 -61zM546 694q0 -85 -74 -85q-77 0 -77 84q0 90 77 90q36 0 55 -25.5t19 -63.5zM712 769v125q-78 -29 -135 -29q-50 29 -110 29q-86 0 -145 -57t-59 -143q0 -50 29.5 -102t73.5 -67v-3q-38 -17 -38 -85 q0 -53 41 -77v-3q-113 -37 -113 -139q0 -45 20 -78.5t54 -51t72 -25.5t81 -8q224 0 224 188q0 67 -48 99t-126 46q-27 5 -51.5 20.5t-24.5 39.5q0 44 49 52q77 15 122 70t45 134q0 24 -10 52q37 9 49 13zM771 350h137q-2 27 -2 82v387q0 46 2 69h-137q3 -23 3 -71v-392 q0 -50 -3 -75zM1280 366v121q-30 -21 -68 -21q-53 0 -53 82v225h52q9 0 26.5 -1t26.5 -1v117h-105q0 82 3 102h-140q4 -24 4 -55v-47h-60v-117q36 3 37 3q3 0 11 -0.5t12 -0.5v-2h-2v-217q0 -37 2.5 -64t11.5 -56.5t24.5 -48.5t43.5 -31t66 -12q64 0 108 24zM924 1072 q0 36 -24 63.5t-60 27.5t-60.5 -27t-24.5 -64q0 -36 25 -62.5t60 -26.5t59.5 27t24.5 62zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M595 22q0 100 -165 100q-158 0 -158 -104q0 -101 172 -101q151 0 151 105zM536 777q0 61 -30 102t-89 41q-124 0 -124 -145q0 -135 124 -135q119 0 119 137zM805 1101v-202q-36 -12 -79 -22q16 -43 16 -84q0 -127 -73 -216.5t-197 -112.5q-40 -8 -59.5 -27t-19.5 -58 q0 -31 22.5 -51.5t58 -32t78.5 -22t86 -25.5t78.5 -37.5t58 -64t22.5 -98.5q0 -304 -363 -304q-69 0 -130 12.5t-116 41t-87.5 82t-32.5 127.5q0 165 182 225v4q-67 41 -67 126q0 109 63 137v4q-72 24 -119.5 108.5t-47.5 165.5q0 139 95 231.5t235 92.5q96 0 178 -47 q98 0 218 47zM1123 220h-222q4 45 4 134v609q0 94 -4 128h222q-4 -33 -4 -124v-613q0 -89 4 -134zM1724 442v-196q-71 -39 -174 -39q-62 0 -107 20t-70 50t-39.5 78t-18.5 92t-4 103v351h2v4q-7 0 -19 1t-18 1q-21 0 -59 -6v190h96v76q0 54 -6 89h227q-6 -41 -6 -165h171 v-190q-15 0 -43.5 2t-42.5 2h-85v-365q0 -131 87 -131q61 0 109 33zM1148 1389q0 -58 -39 -101.5t-96 -43.5q-58 0 -98 43.5t-40 101.5q0 59 39.5 103t98.5 44q58 0 96.5 -44.5t38.5 -102.5z" />
|
||||
<glyph unicode="" d="M825 547l343 588h-150q-21 -39 -63.5 -118.5t-68 -128.5t-59.5 -118.5t-60 -128.5h-3q-21 48 -44.5 97t-52 105.5t-46.5 92t-54 104.5t-49 95h-150l323 -589v-435h134v436zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960 q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" d="M809 532l266 499h-112l-157 -312q-24 -48 -44 -92l-42 92l-155 312h-120l263 -493v-324h101v318zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1280" d="M842 964q0 -80 -57 -136.5t-136 -56.5q-60 0 -111 35q-62 -67 -115 -146q-247 -371 -202 -859q1 -22 -12.5 -38.5t-34.5 -18.5h-5q-20 0 -35 13.5t-17 33.5q-14 126 -3.5 247.5t29.5 217t54 186t69 155.5t74 125q61 90 132 165q-16 35 -16 77q0 80 56.5 136.5t136.5 56.5 t136.5 -56.5t56.5 -136.5zM1223 953q0 -158 -78 -292t-212.5 -212t-292.5 -78q-64 0 -131 14q-21 5 -32.5 23.5t-6.5 39.5q5 20 23 31.5t39 7.5q51 -13 108 -13q97 0 186 38t153 102t102 153t38 186t-38 186t-102 153t-153 102t-186 38t-186 -38t-153 -102t-102 -153 t-38 -186q0 -114 52 -218q10 -20 3.5 -40t-25.5 -30t-39.5 -3t-30.5 26q-64 123 -64 265q0 119 46.5 227t124.5 186t186 124t226 46q158 0 292.5 -78t212.5 -212.5t78 -292.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M270 730q-8 19 -8 52q0 20 11 49t24 45q-1 22 7.5 53t22.5 43q0 139 92.5 288.5t217.5 209.5q139 66 324 66q133 0 266 -55q49 -21 90 -48t71 -56t55 -68t42 -74t32.5 -84.5t25.5 -89.5t22 -98l1 -5q55 -83 55 -150q0 -14 -9 -40t-9 -38q0 -1 1.5 -3.5t3.5 -5t2 -3.5 q77 -114 120.5 -214.5t43.5 -208.5q0 -43 -19.5 -100t-55.5 -57q-9 0 -19.5 7.5t-19 17.5t-19 26t-16 26.5t-13.5 26t-9 17.5q-1 1 -3 1l-5 -4q-59 -154 -132 -223q20 -20 61.5 -38.5t69 -41.5t35.5 -65q-2 -4 -4 -16t-7 -18q-64 -97 -302 -97q-53 0 -110.5 9t-98 20 t-104.5 30q-15 5 -23 7q-14 4 -46 4.5t-40 1.5q-41 -45 -127.5 -65t-168.5 -20q-35 0 -69 1.5t-93 9t-101 20.5t-74.5 40t-32.5 64q0 40 10 59.5t41 48.5q11 2 40.5 13t49.5 12q4 0 14 2q2 2 2 4l-2 3q-48 11 -108 105.5t-73 156.5l-5 3q-4 0 -12 -20q-18 -41 -54.5 -74.5 t-77.5 -37.5h-1q-4 0 -6 4.5t-5 5.5q-23 54 -23 100q0 275 252 466z" />
|
||||
<glyph unicode="" horiz-adv-x="2048" d="M580 1075q0 41 -25 66t-66 25q-43 0 -76 -25.5t-33 -65.5q0 -39 33 -64.5t76 -25.5q41 0 66 24.5t25 65.5zM1323 568q0 28 -25.5 50t-65.5 22q-27 0 -49.5 -22.5t-22.5 -49.5q0 -28 22.5 -50.5t49.5 -22.5q40 0 65.5 22t25.5 51zM1087 1075q0 41 -24.5 66t-65.5 25 q-43 0 -76 -25.5t-33 -65.5q0 -39 33 -64.5t76 -25.5q41 0 65.5 24.5t24.5 65.5zM1722 568q0 28 -26 50t-65 22q-27 0 -49.5 -22.5t-22.5 -49.5q0 -28 22.5 -50.5t49.5 -22.5q39 0 65 22t26 51zM1456 965q-31 4 -70 4q-169 0 -311 -77t-223.5 -208.5t-81.5 -287.5 q0 -78 23 -152q-35 -3 -68 -3q-26 0 -50 1.5t-55 6.5t-44.5 7t-54.5 10.5t-50 10.5l-253 -127l72 218q-290 203 -290 490q0 169 97.5 311t264 223.5t363.5 81.5q176 0 332.5 -66t262 -182.5t136.5 -260.5zM2048 404q0 -117 -68.5 -223.5t-185.5 -193.5l55 -181l-199 109 q-150 -37 -218 -37q-169 0 -311 70.5t-223.5 191.5t-81.5 264t81.5 264t223.5 191.5t311 70.5q161 0 303 -70.5t227.5 -192t85.5 -263.5z" />
|
||||
@@ -483,13 +483,13 @@
|
||||
<glyph unicode="" horiz-adv-x="2048" d="M1024 1024h-384v-384h384v384zM1152 384v-128h-640v128h640zM1152 1152v-640h-640v640h640zM1792 384v-128h-512v128h512zM1792 640v-128h-512v128h512zM1792 896v-128h-512v128h512zM1792 1152v-128h-512v128h512zM256 192v960h-128v-960q0 -26 19 -45t45 -19t45 19 t19 45zM1920 192v1088h-1536v-1088q0 -33 -11 -64h1483q26 0 45 19t19 45zM2048 1408v-1216q0 -80 -56 -136t-136 -56h-1664q-80 0 -136 56t-56 136v1088h256v128h1792z" />
|
||||
<glyph unicode="" horiz-adv-x="2048" d="M1024 13q-20 0 -93 73.5t-73 93.5q0 32 62.5 54t103.5 22t103.5 -22t62.5 -54q0 -20 -73 -93.5t-93 -73.5zM1294 284q-2 0 -40 25t-101.5 50t-128.5 25t-128.5 -25t-101 -50t-40.5 -25q-18 0 -93.5 75t-75.5 93q0 13 10 23q78 77 196 121t233 44t233 -44t196 -121 q10 -10 10 -23q0 -18 -75.5 -93t-93.5 -75zM1567 556q-11 0 -23 8q-136 105 -252 154.5t-268 49.5q-85 0 -170.5 -22t-149 -53t-113.5 -62t-79 -53t-31 -22q-17 0 -92 75t-75 93q0 12 10 22q132 132 320 205t380 73t380 -73t320 -205q10 -10 10 -22q0 -18 -75 -93t-92 -75z M1838 827q-11 0 -22 9q-179 157 -371.5 236.5t-420.5 79.5t-420.5 -79.5t-371.5 -236.5q-11 -9 -22 -9q-17 0 -92.5 75t-75.5 93q0 13 10 23q187 186 445 288t527 102t527 -102t445 -288q10 -10 10 -23q0 -18 -75.5 -93t-92.5 -75z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M384 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM384 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5 t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5 t37.5 90.5zM384 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 768q0 53 -37.5 90.5t-90.5 37.5 t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1536 0v384q0 52 -38 90t-90 38t-90 -38t-38 -90v-384q0 -52 38 -90t90 -38t90 38t38 90zM1152 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5z M1536 1088v256q0 26 -19 45t-45 19h-1280q-26 0 -45 -19t-19 -45v-256q0 -26 19 -45t45 -19h1280q26 0 45 19t19 45zM1536 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1408v-1536q0 -52 -38 -90t-90 -38 h-1408q-52 0 -90 38t-38 90v1536q0 52 38 90t90 38h1408q52 0 90 -38t38 -90z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1112 1090q0 159 -237 159h-70q-32 0 -59.5 -21.5t-34.5 -52.5l-63 -276q-2 -5 -2 -16q0 -24 17 -39.5t41 -15.5h53q69 0 128.5 13t112.5 41t83.5 81.5t30.5 126.5zM1716 938q0 -265 -220 -428q-219 -161 -612 -161h-61q-32 0 -59 -21.5t-34 -52.5l-73 -316 q-8 -36 -40.5 -61.5t-69.5 -25.5h-213q-31 0 -53 20t-22 51q0 10 13 65h151q34 0 64 23.5t38 56.5l73 316q8 33 37.5 57t63.5 24h61q390 0 607 160t217 421q0 129 -51 207q183 -92 183 -335zM1533 1123q0 -264 -221 -428q-218 -161 -612 -161h-60q-32 0 -59.5 -22t-34.5 -53 l-73 -315q-8 -36 -40 -61.5t-69 -25.5h-214q-31 0 -52.5 19.5t-21.5 51.5q0 8 2 20l300 1301q8 36 40.5 61.5t69.5 25.5h444q68 0 125 -4t120.5 -15t113.5 -30t96.5 -50.5t77.5 -74t49.5 -103.5t18.5 -136z" />
|
||||
<glyph unicode="" d="M1519 890q18 -84 -4 -204q-87 -444 -565 -444h-44q-25 0 -44 -16.5t-24 -42.5l-4 -19l-55 -346l-2 -15q-5 -26 -24.5 -42.5t-44.5 -16.5h-251q-21 0 -33 15t-9 36q9 56 26.5 168t26.5 168t27 167.5t27 167.5q5 37 43 37h131q133 -2 236 21q175 39 287 144q102 95 155 246 q24 70 35 133q1 6 2.5 7.5t3.5 1t6 -3.5q79 -59 98 -162zM1347 1172q0 -107 -46 -236q-80 -233 -302 -315q-113 -40 -252 -42q0 -1 -90 -1l-90 1q-100 0 -118 -96q-2 -8 -85 -530q-1 -10 -12 -10h-295q-22 0 -36.5 16.5t-11.5 38.5l232 1471q5 29 27.5 48t51.5 19h598 q34 0 97.5 -13t111.5 -32q107 -41 163.5 -123t56.5 -196z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M602 949q19 -61 31 -123.5t17 -141.5t-14 -159t-62 -145q-21 81 -67 157t-95.5 127t-99 90.5t-78.5 57.5t-33 19q-62 34 -81.5 100t14.5 128t101 81.5t129 -14.5q138 -83 238 -177zM927 1236q11 -25 20.5 -46t36.5 -100.5t42.5 -150.5t25.5 -179.5t0 -205.5t-47.5 -209.5 t-105.5 -208.5q-51 -72 -138 -72q-54 0 -98 31q-57 40 -69 109t28 127q60 85 81 195t13 199.5t-32 180.5t-39 128t-22 52q-31 63 -8.5 129.5t85.5 97.5q34 17 75 17q47 0 88.5 -25t63.5 -69zM1248 567q-17 -160 -72 -311q-17 131 -63 246q25 174 -5 361q-27 178 -94 342 q114 -90 212 -211q9 -37 15 -80q26 -179 7 -347zM1520 1440q9 -17 23.5 -49.5t43.5 -117.5t50.5 -178t34 -227.5t5 -269t-47 -300t-112.5 -323.5q-22 -48 -66 -75.5t-95 -27.5q-39 0 -74 16q-67 31 -92.5 100t4.5 136q58 126 90 257.5t37.5 239.5t-3.5 213.5t-26.5 180.5 t-38.5 138.5t-32.5 90t-15.5 32.5q-34 65 -11.5 135.5t87.5 104.5q37 20 81 20q49 0 91.5 -25.5t66.5 -70.5z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M1975 546h-138q14 37 66 179l3 9q4 10 10 26t9 26l12 -55zM531 611l-58 295q-11 54 -75 54h-268l-2 -13q311 -79 403 -336zM710 960l-162 -438l-17 89q-26 70 -85 129.5t-131 88.5l135 -510h175l261 641h-176zM849 318h166l104 642h-166zM1617 944q-69 27 -149 27 q-123 0 -201 -59t-79 -153q-1 -102 145 -174q48 -23 67 -41t19 -39q0 -30 -30 -46t-69 -16q-86 0 -156 33l-22 11l-23 -144q74 -34 185 -34q130 -1 208.5 59t80.5 160q0 106 -140 174q-49 25 -71 42t-22 38q0 22 24.5 38.5t70.5 16.5q70 1 124 -24l15 -8zM2042 960h-128 q-65 0 -87 -54l-246 -588h174l35 96h212q5 -22 20 -96h154zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M671 603h-13q-47 0 -47 -32q0 -22 20 -22q17 0 28 15t12 39zM1066 639h62v3q1 4 0.5 6.5t-1 7t-2 8t-4.5 6.5t-7.5 5t-11.5 2q-28 0 -36 -38zM1606 603h-12q-48 0 -48 -32q0 -22 20 -22q17 0 28 15t12 39zM1925 629q0 41 -30 41q-19 0 -31 -20t-12 -51q0 -42 28 -42 q20 0 32.5 20t12.5 52zM480 770h87l-44 -262h-56l32 201l-71 -201h-39l-4 200l-34 -200h-53l44 262h81l2 -163zM733 663q0 -6 -4 -42q-16 -101 -17 -113h-47l1 22q-20 -26 -58 -26q-23 0 -37.5 16t-14.5 42q0 39 26 60.5t73 21.5q14 0 23 -1q0 3 0.5 5.5t1 4.5t0.5 3 q0 20 -36 20q-29 0 -59 -10q0 4 7 48q38 11 67 11q74 0 74 -62zM889 721l-8 -49q-22 3 -41 3q-27 0 -27 -17q0 -8 4.5 -12t21.5 -11q40 -19 40 -60q0 -72 -87 -71q-34 0 -58 6q0 2 7 49q29 -8 51 -8q32 0 32 19q0 7 -4.5 11.5t-21.5 12.5q-43 20 -43 59q0 72 84 72 q30 0 50 -4zM977 721h28l-7 -52h-29q-2 -17 -6.5 -40.5t-7 -38.5t-2.5 -18q0 -16 19 -16q8 0 16 2l-8 -47q-21 -7 -40 -7q-43 0 -45 47q0 12 8 56q3 20 25 146h55zM1180 648q0 -23 -7 -52h-111q-3 -22 10 -33t38 -11q30 0 58 14l-9 -54q-30 -8 -57 -8q-95 0 -95 95 q0 55 27.5 90.5t69.5 35.5q35 0 55.5 -21t20.5 -56zM1319 722q-13 -23 -22 -62q-22 2 -31 -24t-25 -128h-56l3 14q22 130 29 199h51l-3 -33q14 21 25.5 29.5t28.5 4.5zM1506 763l-9 -57q-28 14 -50 14q-31 0 -51 -27.5t-20 -70.5q0 -30 13.5 -47t38.5 -17q21 0 48 13 l-10 -59q-28 -8 -50 -8q-45 0 -71.5 30.5t-26.5 82.5q0 70 35.5 114.5t91.5 44.5q26 0 61 -13zM1668 663q0 -18 -4 -42q-13 -79 -17 -113h-46l1 22q-20 -26 -59 -26q-23 0 -37 16t-14 42q0 39 25.5 60.5t72.5 21.5q15 0 23 -1q2 7 2 13q0 20 -36 20q-29 0 -59 -10q0 4 8 48 q38 11 67 11q73 0 73 -62zM1809 722q-14 -24 -21 -62q-23 2 -31.5 -23t-25.5 -129h-56l3 14q19 104 29 199h52q0 -11 -4 -33q15 21 26.5 29.5t27.5 4.5zM1950 770h56l-43 -262h-53l3 19q-23 -23 -52 -23q-31 0 -49.5 24t-18.5 64q0 53 27.5 92t64.5 39q31 0 53 -29z M2061 640q0 148 -72.5 273t-198 198t-273.5 73q-181 0 -328 -110q127 -116 171 -284h-50q-44 150 -158 253q-114 -103 -158 -253h-50q44 168 171 284q-147 110 -328 110q-148 0 -273.5 -73t-198 -198t-72.5 -273t72.5 -273t198 -198t273.5 -73q181 0 328 110 q-120 111 -165 264h50q46 -138 152 -233q106 95 152 233h50q-45 -153 -165 -264q147 -110 328 -110q148 0 273.5 73t198 198t72.5 273zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M313 759q0 -51 -36 -84q-29 -26 -89 -26h-17v220h17q61 0 89 -27q36 -31 36 -83zM2089 824q0 -52 -64 -52h-19v101h20q63 0 63 -49zM380 759q0 74 -50 120.5t-129 46.5h-95v-333h95q74 0 119 38q60 51 60 128zM410 593h65v333h-65v-333zM730 694q0 40 -20.5 62t-75.5 42 q-29 10 -39.5 19t-10.5 23q0 16 13.5 26.5t34.5 10.5q29 0 53 -27l34 44q-41 37 -98 37q-44 0 -74 -27.5t-30 -67.5q0 -35 18 -55.5t64 -36.5q37 -13 45 -19q19 -12 19 -34q0 -20 -14 -33.5t-36 -13.5q-48 0 -71 44l-42 -40q44 -64 115 -64q51 0 83 30.5t32 79.5zM1008 604 v77q-37 -37 -78 -37q-49 0 -80.5 32.5t-31.5 82.5q0 48 31.5 81.5t77.5 33.5q43 0 81 -38v77q-40 20 -80 20q-74 0 -125.5 -50.5t-51.5 -123.5t51 -123.5t125 -50.5q42 0 81 19zM2240 0v527q-65 -40 -144.5 -84t-237.5 -117t-329.5 -137.5t-417.5 -134.5t-504 -118h1569 q26 0 45 19t19 45zM1389 757q0 75 -53 128t-128 53t-128 -53t-53 -128t53 -128t128 -53t128 53t53 128zM1541 584l144 342h-71l-90 -224l-89 224h-71l142 -342h35zM1714 593h184v56h-119v90h115v56h-115v74h119v57h-184v-333zM2105 593h80l-105 140q76 16 76 94q0 47 -31 73 t-87 26h-97v-333h65v133h9zM2304 1274v-1268q0 -56 -38.5 -95t-93.5 -39h-2040q-55 0 -93.5 39t-38.5 95v1268q0 56 38.5 95t93.5 39h2040q55 0 93.5 -39t38.5 -95z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M119 854h89l-45 108zM740 328l74 79l-70 79h-163v-49h142v-55h-142v-54h159zM898 406l99 -110v217zM1186 453q0 33 -40 33h-84v-69h83q41 0 41 36zM1475 457q0 29 -42 29h-82v-61h81q43 0 43 32zM1197 923q0 29 -42 29h-82v-60h81q43 0 43 31zM1656 854h89l-44 108z M699 1009v-271h-66v212l-94 -212h-57l-94 212v-212h-132l-25 60h-135l-25 -60h-70l116 271h96l110 -257v257h106l85 -184l77 184h108zM1255 453q0 -20 -5.5 -35t-14 -25t-22.5 -16.5t-26 -10t-31.5 -4.5t-31.5 -1t-32.5 0.5t-29.5 0.5v-91h-126l-80 90l-83 -90h-256v271h260 l80 -89l82 89h207q109 0 109 -89zM964 794v-56h-217v271h217v-57h-152v-49h148v-55h-148v-54h152zM2304 235v-229q0 -55 -38.5 -94.5t-93.5 -39.5h-2040q-55 0 -93.5 39.5t-38.5 94.5v678h111l25 61h55l25 -61h218v46l19 -46h113l20 47v-47h541v99l10 1q10 0 10 -14v-86h279 v23q23 -12 55 -18t52.5 -6.5t63 0.5t51.5 1l25 61h56l25 -61h227v58l34 -58h182v378h-180v-44l-25 44h-185v-44l-23 44h-249q-69 0 -109 -22v22h-172v-22q-24 22 -73 22h-628l-43 -97l-43 97h-198v-44l-22 44h-169l-78 -179v391q0 55 38.5 94.5t93.5 39.5h2040 q55 0 93.5 -39.5t38.5 -94.5v-678h-120q-51 0 -81 -22v22h-177q-55 0 -78 -22v22h-316v-22q-31 22 -87 22h-209v-22q-23 22 -91 22h-234l-54 -58l-50 58h-349v-378h343l55 59l52 -59h211v89h21q59 0 90 13v-102h174v99h8q8 0 10 -2t2 -10v-87h529q57 0 88 24v-24h168 q60 0 95 17zM1546 469q0 -23 -12 -43t-34 -29q25 -9 34 -26t9 -46v-54h-65v45q0 33 -12 43.5t-46 10.5h-69v-99h-65v271h154q48 0 77 -15t29 -58zM1269 936q0 -24 -12.5 -44t-33.5 -29q26 -9 34.5 -25.5t8.5 -46.5v-53h-65q0 9 0.5 26.5t0 25t-3 18.5t-8.5 16t-17.5 8.5 t-29.5 3.5h-70v-98h-64v271l153 -1q49 0 78 -14.5t29 -57.5zM1798 327v-56h-216v271h216v-56h-151v-49h148v-55h-148v-54zM1372 1009v-271h-66v271h66zM2065 357q0 -86 -102 -86h-126v58h126q34 0 34 25q0 16 -17 21t-41.5 5t-49.5 3.5t-42 22.5t-17 55q0 39 26 60t66 21 h130v-57h-119q-36 0 -36 -25q0 -16 17.5 -20.5t42 -4t49 -2.5t42 -21.5t17.5 -54.5zM2304 407v-101q-24 -35 -88 -35h-125v58h125q33 0 33 25q0 13 -12.5 19t-31 5.5t-40 2t-40 8t-31 24t-12.5 48.5q0 39 26.5 60t66.5 21h129v-57h-118q-36 0 -36 -25q0 -20 29 -22t68.5 -5 t56.5 -26zM2139 1008v-270h-92l-122 203v-203h-132l-26 60h-134l-25 -60h-75q-129 0 -129 133q0 138 133 138h63v-59q-7 0 -28 1t-28.5 0.5t-23 -2t-21.5 -6.5t-14.5 -13.5t-11.5 -23t-3 -33.5q0 -38 13.5 -58t49.5 -20h29l92 213h97l109 -256v256h99l114 -188v188h66z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M322 689h-15q-19 0 -19 18q0 28 19 85q5 15 15 19.5t28 4.5q77 0 77 -49q0 -41 -30.5 -59.5t-74.5 -18.5zM664 528q-47 0 -47 29q0 62 123 62l3 -3q-5 -88 -79 -88zM1438 687h-15q-19 0 -19 19q0 28 19 85q5 15 14.5 19t28.5 4q77 0 77 -49q0 -41 -30.5 -59.5 t-74.5 -18.5zM1780 527q-47 0 -47 30q0 62 123 62l3 -3q-5 -89 -79 -89zM373 894h-128q-8 0 -14.5 -4t-8.5 -7.5t-7 -12.5q-3 -7 -45 -190t-42 -192q0 -7 5.5 -12.5t13.5 -5.5h62q25 0 32.5 34.5l15 69t32.5 34.5q47 0 87.5 7.5t80.5 24.5t63.5 52.5t23.5 84.5 q0 36 -14.5 61t-41 36.5t-53.5 15.5t-62 4zM719 798q-38 0 -74 -6q-2 0 -8.5 -1t-9 -1.5l-7.5 -1.5t-7.5 -2t-6.5 -3t-6.5 -4t-5 -5t-4.5 -7t-4 -9q-9 -29 -9 -39t9 -10q5 0 21.5 5t19.5 6q30 8 58 8q74 0 74 -36q0 -11 -10 -14q-8 -2 -18 -3t-21.5 -1.5t-17.5 -1.5 q-38 -4 -64.5 -10t-56.5 -19.5t-45.5 -39t-15.5 -62.5q0 -38 26 -59.5t64 -21.5q24 0 45.5 6.5t33 13t38.5 23.5q-3 -7 -3 -15t5.5 -13.5t12.5 -5.5h56q1 1 7 3.5t7.5 3.5t5 3.5t5 5.5t2.5 8l45 194q4 13 4 30q0 81 -145 81zM1247 793h-74q-22 0 -39 -23q-5 -7 -29.5 -51 t-46.5 -81.5t-26 -38.5l-5 4q0 77 -27 166q-1 5 -3.5 8.5t-6 6.5t-6.5 5t-8.5 3t-8.5 1.5t-9.5 1t-9 0.5h-10h-8.5q-38 0 -38 -21l1 -5q5 -53 25 -151t25 -143q2 -16 2 -24q0 -19 -30.5 -61.5t-30.5 -58.5q0 -13 40 -13q61 0 76 25l245 415q10 20 10 26q0 9 -8 9zM1489 892 h-129q-18 0 -29 -23q-6 -13 -46.5 -191.5t-40.5 -190.5q0 -20 43 -20h7.5h9h9t9.5 1t8.5 2t8.5 3t6.5 4.5t5.5 6t3 8.5l21 91q2 10 10.5 17t19.5 7q47 0 87.5 7t80.5 24.5t63.5 52.5t23.5 84q0 36 -14.5 61t-41 36.5t-53.5 15.5t-62 4zM1835 798q-26 0 -74 -6 q-38 -6 -48 -16q-7 -8 -11 -19q-8 -24 -8 -39q0 -10 8 -10q1 0 41 12q30 8 58 8q74 0 74 -36q0 -12 -10 -14q-4 -1 -57 -7q-38 -4 -64.5 -10t-56.5 -19.5t-45.5 -39t-15.5 -62.5t26 -58.5t64 -21.5q24 0 45 6t34 13t38 24q-3 -15 -3 -16q0 -5 2 -8.5t6.5 -5.5t8 -3.5 t10.5 -2t9.5 -0.5h9.5h8q42 0 48 25l45 194q3 15 3 31q0 81 -145 81zM2157 889h-55q-25 0 -33 -40q-10 -44 -36.5 -167t-42.5 -190v-5q0 -16 16 -18h1h57q10 0 18.5 6.5t10.5 16.5l83 374h-1l1 5q0 7 -5.5 12.5t-13.5 5.5zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048 q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M745 630q0 -37 -25.5 -61.5t-62.5 -24.5q-29 0 -46.5 16t-17.5 44q0 37 25 62.5t62 25.5q28 0 46.5 -16.5t18.5 -45.5zM1530 779q0 -42 -22 -57t-66 -15l-32 -1l17 107q2 11 13 11h18q22 0 35 -2t25 -12.5t12 -30.5zM1881 630q0 -36 -25.5 -61t-61.5 -25q-29 0 -47 16 t-18 44q0 37 25 62.5t62 25.5q28 0 46.5 -16.5t18.5 -45.5zM513 801q0 59 -38.5 85.5t-100.5 26.5h-160q-19 0 -21 -19l-65 -408q-1 -6 3 -11t10 -5h76q20 0 22 19l18 110q1 8 7 13t15 6.5t17 1.5t19 -1t14 -1q86 0 135 48.5t49 134.5zM822 489l41 261q1 6 -3 11t-10 5h-76 q-14 0 -17 -33q-27 40 -95 40q-72 0 -122.5 -54t-50.5 -127q0 -59 34.5 -94t92.5 -35q28 0 58 12t48 32q-4 -12 -4 -21q0 -16 13 -16h69q19 0 22 19zM1269 752q0 5 -4 9.5t-9 4.5h-77q-11 0 -18 -10l-106 -156l-44 150q-5 16 -22 16h-75q-5 0 -9 -4.5t-4 -9.5q0 -2 19.5 -59 t42 -123t23.5 -70q-82 -112 -82 -120q0 -13 13 -13h77q11 0 18 10l255 368q2 2 2 7zM1649 801q0 59 -38.5 85.5t-100.5 26.5h-159q-20 0 -22 -19l-65 -408q-1 -6 3 -11t10 -5h82q12 0 16 13l18 116q1 8 7 13t15 6.5t17 1.5t19 -1t14 -1q86 0 135 48.5t49 134.5zM1958 489 l41 261q1 6 -3 11t-10 5h-76q-14 0 -17 -33q-26 40 -95 40q-72 0 -122.5 -54t-50.5 -127q0 -59 34.5 -94t92.5 -35q29 0 59 12t47 32q0 -1 -2 -9t-2 -12q0 -16 13 -16h69q19 0 22 19zM2176 898v1q0 14 -13 14h-74q-11 0 -13 -11l-65 -416l-1 -2q0 -5 4 -9.5t10 -4.5h66 q19 0 21 19zM392 764q-5 -35 -26 -46t-60 -11l-33 -1l17 107q2 11 13 11h19q40 0 58 -11.5t12 -48.5zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M1597 633q0 -69 -21 -106q-19 -35 -52 -35q-23 0 -41 9v224q29 30 57 30q57 0 57 -122zM2035 669h-110q6 98 56 98q51 0 54 -98zM476 534q0 59 -33 91.5t-101 57.5q-36 13 -52 24t-16 25q0 26 38 26q58 0 124 -33l18 112q-67 32 -149 32q-77 0 -123 -38q-48 -39 -48 -109 q0 -58 32.5 -90.5t99.5 -56.5q39 -14 54.5 -25.5t15.5 -27.5q0 -31 -48 -31q-29 0 -70 12.5t-72 30.5l-18 -113q72 -41 168 -41q81 0 129 37q51 41 51 117zM771 749l19 111h-96v135l-129 -21l-18 -114l-46 -8l-17 -103h62v-219q0 -84 44 -120q38 -30 111 -30q32 0 79 11v118 q-32 -7 -44 -7q-42 0 -42 50v197h77zM1087 724v139q-15 3 -28 3q-32 0 -55.5 -16t-33.5 -46l-10 56h-131v-471h150v306q26 31 82 31q16 0 26 -2zM1124 389h150v471h-150v-471zM1746 638q0 122 -45 179q-40 52 -111 52q-64 0 -117 -56l-8 47h-132v-645l150 25v151 q36 -11 68 -11q83 0 134 56q61 65 61 202zM1278 986q0 33 -23 56t-56 23t-56 -23t-23 -56t23 -56.5t56 -23.5t56 23.5t23 56.5zM2176 629q0 113 -48 176q-50 64 -144 64q-96 0 -151.5 -66t-55.5 -180q0 -128 63 -188q55 -55 161 -55q101 0 160 40l-16 103q-57 -31 -128 -31 q-43 0 -63 19q-23 19 -28 66h248q2 14 2 52zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
|
||||
<glyph unicode="" horiz-adv-x="2048" d="M1558 684q61 -356 298 -556q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-180.5 74.5t-75.5 180.5zM1024 -176q16 0 16 16t-16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5zM2026 1424q8 -10 7.5 -23.5t-10.5 -22.5 l-1872 -1622q-10 -8 -23.5 -7t-21.5 11l-84 96q-8 10 -7.5 23.5t10.5 21.5l186 161q-19 32 -19 66q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q124 -18 219 -82.5t148 -157.5 l418 363q10 8 23.5 7t21.5 -11z" />
|
||||
<glyph unicode="" horiz-adv-x="2048" d="M1040 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM503 315l877 760q-42 88 -132.5 146.5t-223.5 58.5q-93 0 -169.5 -31.5t-121.5 -80.5t-69 -103t-24 -105q0 -384 -137 -645zM1856 128 q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-180.5 74.5t-75.5 180.5l149 129h757q-166 187 -227 459l111 97q61 -356 298 -556zM1942 1520l84 -96q8 -10 7.5 -23.5t-10.5 -22.5l-1872 -1622q-10 -8 -23.5 -7t-21.5 11l-84 96q-8 10 -7.5 23.5t10.5 21.5l186 161 q-19 32 -19 66q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q124 -18 219 -82.5t148 -157.5l418 363q10 8 23.5 7t21.5 -11z" />
|
||||
@@ -523,7 +523,7 @@
|
||||
<glyph unicode="" horiz-adv-x="2048" d="M863 504q0 112 -79.5 191.5t-191.5 79.5t-191 -79.5t-79 -191.5t79 -191t191 -79t191.5 79t79.5 191zM1726 505q0 112 -79 191t-191 79t-191.5 -79t-79.5 -191q0 -113 79.5 -192t191.5 -79t191 79.5t79 191.5zM2048 1314v-1348q0 -44 -31.5 -75.5t-76.5 -31.5h-1832 q-45 0 -76.5 31.5t-31.5 75.5v1348q0 44 31.5 75.5t76.5 31.5h431q44 0 76 -31.5t32 -75.5v-161h754v161q0 44 32 75.5t76 31.5h431q45 0 76.5 -31.5t31.5 -75.5z" />
|
||||
<glyph unicode="" horiz-adv-x="2048" d="M1430 953zM1690 749q148 0 253 -98.5t105 -244.5q0 -157 -109 -261.5t-267 -104.5q-85 0 -162 27.5t-138 73.5t-118 106t-109 126.5t-103.5 132.5t-108.5 126t-117 106t-136 73.5t-159 27.5q-154 0 -251.5 -91.5t-97.5 -244.5q0 -157 104 -250t263 -93q100 0 208 37.5 t193 98.5q5 4 21 18.5t30 24t22 9.5q14 0 24.5 -10.5t10.5 -24.5q0 -24 -60 -77q-101 -88 -234.5 -142t-260.5 -54q-133 0 -245.5 58t-180 165t-67.5 241q0 205 141.5 341t347.5 136q120 0 226.5 -43.5t185.5 -113t151.5 -153t139 -167.5t133.5 -153.5t149.5 -113 t172.5 -43.5q102 0 168.5 61.5t66.5 162.5q0 95 -64.5 159t-159.5 64q-30 0 -81.5 -18.5t-68.5 -18.5q-20 0 -35.5 15t-15.5 35q0 18 8.5 57t8.5 59q0 159 -107.5 263t-266.5 104q-58 0 -111.5 -18.5t-84 -40.5t-55.5 -40.5t-33 -18.5q-15 0 -25.5 10.5t-10.5 25.5 q0 19 25 46q59 67 147 103.5t182 36.5q191 0 318 -125.5t127 -315.5q0 -37 -4 -66q57 15 115 15z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1216 832q0 26 -19 45t-45 19h-128v128q0 26 -19 45t-45 19t-45 -19t-19 -45v-128h-128q-26 0 -45 -19t-19 -45t19 -45t45 -19h128v-128q0 -26 19 -45t45 -19t45 19t19 45v128h128q26 0 45 19t19 45zM640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920 q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1280 832q0 26 -19 45t-45 19t-45 -19l-147 -146v293q0 26 -19 45t-45 19t-45 -19t-19 -45v-293l-147 146q-19 19 -45 19t-45 -19t-19 -45t19 -45l256 -256q19 -19 45 -19t45 19l256 256q19 19 19 45zM640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920 q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="1664" d="M1280 832q0 26 -19 45t-45 19t-45 -19l-147 -146v293q0 26 -19 45t-45 19t-45 -19t-19 -45v-293l-147 146q-19 19 -45 19t-45 -19t-19 -45t19 -45l256 -256q19 -19 45 -19t45 19l256 256q19 19 19 45zM640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920 q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" />
|
||||
<glyph unicode="" horiz-adv-x="2048" d="M212 768l623 -665l-300 665h-323zM1024 -4l349 772h-698zM538 896l204 384h-262l-288 -384h346zM1213 103l623 665h-323zM683 896h682l-204 384h-274zM1510 896h346l-288 384h-262zM1651 1382l384 -512q14 -18 13 -41.5t-17 -40.5l-960 -1024q-18 -20 -47 -20t-47 20 l-960 1024q-16 17 -17 40.5t13 41.5l384 512q18 26 51 26h1152q33 0 51 -26z" />
|
||||
<glyph unicode="" horiz-adv-x="2048" d="M1811 -19q19 19 45 19t45 -19l128 -128l-90 -90l-83 83l-83 -83q-18 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83 q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-128 128l90 90l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83 q19 19 45 19t45 -19l83 -83zM237 19q-19 -19 -45 -19t-45 19l-128 128l90 90l83 -82l83 82q19 19 45 19t45 -19l83 -82l64 64v293l-210 314q-17 26 -7 56.5t40 40.5l177 58v299h128v128h256v128h256v-128h256v-128h128v-299l177 -58q30 -10 40 -40.5t-7 -56.5l-210 -314 v-293l19 18q19 19 45 19t45 -19l83 -82l83 82q19 19 45 19t45 -19l128 -128l-90 -90l-83 83l-83 -83q-18 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83 q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83zM640 1152v-128l384 128l384 -128v128h-128v128h-512v-128h-128z" />
|
||||
<glyph unicode="" d="M576 0l96 448l-96 128l-128 64zM832 0l128 640l-128 -64l-96 -128zM992 1010q-2 4 -4 6q-10 8 -96 8q-70 0 -167 -19q-7 -2 -21 -2t-21 2q-97 19 -167 19q-86 0 -96 -8q-2 -2 -4 -6q2 -18 4 -27q2 -3 7.5 -6.5t7.5 -10.5q2 -4 7.5 -20.5t7 -20.5t7.5 -17t8.5 -17t9 -14 t12 -13.5t14 -9.5t17.5 -8t20.5 -4t24.5 -2q36 0 59 12.5t32.5 30t14.5 34.5t11.5 29.5t17.5 12.5h12q11 0 17.5 -12.5t11.5 -29.5t14.5 -34.5t32.5 -30t59 -12.5q13 0 24.5 2t20.5 4t17.5 8t14 9.5t12 13.5t9 14t8.5 17t7.5 17t7 20.5t7.5 20.5q2 7 7.5 10.5t7.5 6.5 q2 9 4 27zM1408 131q0 -121 -73 -190t-194 -69h-874q-121 0 -194 69t-73 190q0 61 4.5 118t19 125.5t37.5 123.5t63.5 103.5t93.5 74.5l-90 220h214q-22 64 -22 128q0 12 2 32q-194 40 -194 96q0 57 210 99q17 62 51.5 134t70.5 114q32 37 76 37q30 0 84 -31t84 -31t84 31 t84 31q44 0 76 -37q36 -42 70.5 -114t51.5 -134q210 -42 210 -99q0 -56 -194 -96q7 -81 -20 -160h214l-82 -225q63 -33 107.5 -96.5t65.5 -143.5t29 -151.5t8 -148.5z" />
|
||||
@@ -531,18 +531,18 @@
|
||||
<glyph unicode="" d="M1408 0q0 -63 -61.5 -113.5t-164 -81t-225 -46t-253.5 -15.5t-253.5 15.5t-225 46t-164 81t-61.5 113.5q0 49 33 88.5t91 66.5t118 44.5t131 29.5q26 5 48 -10.5t26 -41.5q5 -26 -10.5 -48t-41.5 -26q-58 -10 -106 -23.5t-76.5 -25.5t-48.5 -23.5t-27.5 -19.5t-8.5 -12 q3 -11 27 -26.5t73 -33t114 -32.5t160.5 -25t201.5 -10t201.5 10t160.5 25t114 33t73 33.5t27 27.5q-1 4 -8.5 11t-27.5 19t-48.5 23.5t-76.5 25t-106 23.5q-26 4 -41.5 26t-10.5 48q4 26 26 41.5t48 10.5q71 -12 131 -29.5t118 -44.5t91 -66.5t33 -88.5zM1024 896v-384 q0 -26 -19 -45t-45 -19h-64v-384q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v384h-64q-26 0 -45 19t-19 45v384q0 53 37.5 90.5t90.5 37.5h384q53 0 90.5 -37.5t37.5 -90.5zM928 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5 t158.5 -65.5t65.5 -158.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1280 512h305q-5 -6 -10 -10.5t-9 -7.5l-3 -4l-623 -600q-18 -18 -44 -18t-44 18l-624 602q-5 2 -21 20h369q22 0 39.5 13.5t22.5 34.5l70 281l190 -667q6 -20 23 -33t39 -13q21 0 38 13t23 33l146 485l56 -112q18 -35 57 -35zM1792 940q0 -145 -103 -300h-369l-111 221 q-8 17 -25.5 27t-36.5 8q-45 -5 -56 -46l-129 -430l-196 686q-6 20 -23.5 33t-39.5 13t-39 -13.5t-22 -34.5l-116 -464h-423q-103 155 -103 300q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5q224 0 351 -124 t127 -344z" />
|
||||
<glyph unicode="" horiz-adv-x="1280" d="M1152 960q0 -221 -147.5 -384.5t-364.5 -187.5v-260h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v260q-150 16 -271.5 103t-186 224t-52.5 292 q11 134 80.5 249t182 188t245.5 88q170 19 319 -54t236 -212t87 -306zM128 960q0 -185 131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1280 1504q0 14 9 23t23 9h416q26 0 45 -19t19 -45v-416q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v262l-419 -420q87 -104 129.5 -236.5t30.5 -276.5q-22 -250 -200.5 -431t-428.5 -206q-163 -17 -314 39.5t-256.5 162t-162 256.5t-39.5 314q25 250 206 428.5 t431 200.5q144 12 276.5 -30.5t236.5 -129.5l419 419h-261q-14 0 -23 9t-9 23v64zM704 -128q117 0 223.5 45.5t184 123t123 184t45.5 223.5t-45.5 223.5t-123 184t-184 123t-223.5 45.5t-223.5 -45.5t-184 -123t-123 -184t-45.5 -223.5t45.5 -223.5t123 -184t184 -123 t223.5 -45.5z" />
|
||||
<glyph unicode="" d="M1472 1408q26 0 45 -19t19 -45v-416q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v262l-382 -383q126 -156 126 -359q0 -117 -45.5 -223.5t-123 -184t-184 -123t-223.5 -45.5t-223.5 45.5t-184 123t-123 184t-45.5 223.5t45.5 223.5t123 184t184 123t223.5 45.5 q203 0 359 -126l382 382h-261q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h416zM576 0q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1280" d="M830 1220q145 -72 233.5 -210.5t88.5 -305.5q0 -221 -147.5 -384.5t-364.5 -187.5v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-217 24 -364.5 187.5 t-147.5 384.5q0 167 88.5 305.5t233.5 210.5q-165 96 -228 273q-6 16 3.5 29.5t26.5 13.5h69q21 0 29 -20q44 -106 140 -171t214 -65t214 65t140 171q8 20 37 20h61q17 0 26.5 -13.5t3.5 -29.5q-63 -177 -228 -273zM576 256q185 0 316.5 131.5t131.5 316.5t-131.5 316.5 t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
|
||||
<glyph unicode="" d="M1024 1504q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q126 -158 126 -359q0 -221 -147.5 -384.5t-364.5 -187.5v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64 q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-149 16 -270.5 103t-186.5 223.5t-53 291.5q16 204 160 353.5t347 172.5q118 14 228 -19t198 -103l255 254h-134q-14 0 -23 9t-9 23v64zM576 256q185 0 316.5 131.5t131.5 316.5t-131.5 316.5 t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1280 1504q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q126 -158 126 -359q0 -221 -147.5 -384.5t-364.5 -187.5v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64 q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-217 24 -364.5 187.5t-147.5 384.5q0 201 126 359l-52 53l-101 -111q-9 -10 -22 -10.5t-23 7.5l-48 44q-10 8 -10.5 21.5t8.5 23.5l105 115l-111 112v-134q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9 t-9 23v288q0 26 19 45t45 19h288q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-133l106 -107l86 94q9 10 22 10.5t23 -7.5l48 -44q10 -8 10.5 -21.5t-8.5 -23.5l-90 -99l57 -56q158 126 359 126t359 -126l255 254h-134q-14 0 -23 9t-9 23v64zM832 256q185 0 316.5 131.5 t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1790 1007q12 -155 -52.5 -292t-186 -224t-271.5 -103v-260h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-512v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23 t23 9h224v260q-150 16 -271.5 103t-186 224t-52.5 292q17 206 164.5 356.5t352.5 169.5q206 21 377 -94q171 115 377 94q205 -19 352.5 -169.5t164.5 -356.5zM896 647q128 131 128 313t-128 313q-128 -131 -128 -313t128 -313zM576 512q115 0 218 57q-154 165 -154 391 q0 224 154 391q-103 57 -218 57q-185 0 -316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5zM1152 128v260q-137 15 -256 94q-119 -79 -256 -94v-260h512zM1216 512q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5q-115 0 -218 -57q154 -167 154 -391 q0 -226 -154 -391q103 -57 218 -57z" />
|
||||
<glyph unicode="" horiz-adv-x="1920" d="M1536 1120q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q76 -95 107.5 -214t9.5 -247q-31 -182 -166 -312t-318 -156q-210 -29 -384.5 80t-241.5 300q-117 6 -221 57.5t-177.5 133t-113.5 192.5t-32 230 q9 135 78 252t182 191.5t248 89.5q118 14 227.5 -19t198.5 -103l255 254h-134q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q59 -74 93 -169q182 -9 328 -124l255 254h-134q-14 0 -23 9 t-9 23v64zM1024 704q0 20 -4 58q-162 -25 -271 -150t-109 -292q0 -20 4 -58q162 25 271 150t109 292zM128 704q0 -168 111 -294t276 -149q-3 29 -3 59q0 210 135 369.5t338 196.5q-53 120 -163.5 193t-245.5 73q-185 0 -316.5 -131.5t-131.5 -316.5zM1088 -128 q185 0 316.5 131.5t131.5 316.5q0 168 -111 294t-276 149q3 -29 3 -59q0 -210 -135 -369.5t-338 -196.5q53 -120 163.5 -193t245.5 -73z" />
|
||||
<glyph unicode="" horiz-adv-x="2048" d="M1664 1504q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q76 -95 107.5 -214t9.5 -247q-32 -180 -164.5 -310t-313.5 -157q-223 -34 -409 90q-117 -78 -256 -93v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23 t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-155 17 -279.5 109.5t-187 237.5t-39.5 307q25 187 159.5 322.5t320.5 164.5q224 34 410 -90q146 97 320 97q201 0 359 -126l255 254h-134q-14 0 -23 9 t-9 23v64zM896 391q128 131 128 313t-128 313q-128 -131 -128 -313t128 -313zM128 704q0 -185 131.5 -316.5t316.5 -131.5q117 0 218 57q-154 167 -154 391t154 391q-101 57 -218 57q-185 0 -316.5 -131.5t-131.5 -316.5zM1216 256q185 0 316.5 131.5t131.5 316.5 t-131.5 316.5t-316.5 131.5q-117 0 -218 -57q154 -167 154 -391t-154 -391q101 -57 218 -57z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1728 1536q26 0 45 -19t19 -45v-416q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v262l-229 -230l156 -156q9 -10 9 -23t-9 -22l-46 -46q-9 -9 -22 -9t-23 9l-156 157l-99 -100q87 -104 129.5 -236.5t30.5 -276.5q-22 -250 -200.5 -431t-428.5 -206q-163 -17 -314 39.5 t-256.5 162t-162 256.5t-39.5 314q25 250 206 428.5t431 200.5q144 12 276.5 -30.5t236.5 -129.5l99 99l-156 156q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l156 -156l229 229h-261q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h416zM1280 448q0 117 -45.5 223.5t-123 184t-184 123 t-223.5 45.5t-223.5 -45.5t-184 -123t-123 -184t-45.5 -223.5t45.5 -223.5t123 -184t184 -123t223.5 -45.5t223.5 45.5t184 123t123 184t45.5 223.5z" />
|
||||
<glyph unicode="" d="M1472 1408q26 0 45 -19t19 -45v-416q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v262l-213 -214l140 -140q9 -10 9 -23t-9 -22l-46 -46q-9 -9 -22 -9t-23 9l-140 141l-78 -79q126 -156 126 -359q0 -117 -45.5 -223.5t-123 -184t-184 -123t-223.5 -45.5t-223.5 45.5 t-184 123t-123 184t-45.5 223.5t45.5 223.5t123 184t184 123t223.5 45.5q203 0 359 -126l78 78l-172 172q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l172 -172l213 213h-261q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h416zM576 0q185 0 316.5 131.5t131.5 316.5t-131.5 316.5 t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1280" d="M640 892q217 -24 364.5 -187.5t147.5 -384.5q0 -167 -87 -306t-236 -212t-319 -54q-133 15 -245.5 88t-182 188t-80.5 249q-12 155 52.5 292t186 224t271.5 103v132h-160q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h160v165l-92 -92q-10 -9 -23 -9t-22 9l-46 46q-9 9 -9 22 t9 23l202 201q19 19 45 19t45 -19l202 -201q9 -10 9 -23t-9 -22l-46 -46q-9 -9 -22 -9t-23 9l-92 92v-165h160q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-160v-132zM576 -128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5 t131.5 -316.5t316.5 -131.5z" />
|
||||
<glyph unicode="" horiz-adv-x="2048" d="M2029 685q19 -19 19 -45t-19 -45l-294 -294q-9 -10 -22.5 -10t-22.5 10l-45 45q-10 9 -10 22.5t10 22.5l185 185h-294v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-131q-12 -119 -67 -226t-139 -183.5t-196.5 -121.5t-234.5 -45q-180 0 -330.5 91t-234.5 247 t-74 337q8 162 94 300t226.5 219.5t302.5 85.5q166 4 310.5 -71.5t235.5 -208.5t107 -296h131v224q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-224h294l-185 185q-10 9 -10 22.5t10 22.5l45 45q9 10 22.5 10t22.5 -10zM640 128q104 0 198.5 40.5t163.5 109.5t109.5 163.5 t40.5 198.5t-40.5 198.5t-109.5 163.5t-163.5 109.5t-198.5 40.5t-198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5z" />
|
||||
<glyph unicode="" horiz-adv-x="2048" d="M1901 621q19 -19 19 -45t-19 -45l-294 -294q-9 -10 -22.5 -10t-22.5 10l-45 45q-10 9 -10 22.5t10 22.5l185 185h-294v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-132q-24 -217 -187.5 -364.5t-384.5 -147.5q-167 0 -306 87t-212 236t-54 319q15 133 88 245.5 t188 182t249 80.5q155 12 292 -52.5t224 -186t103 -271.5h132v224q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-224h294l-185 185q-10 9 -10 22.5t10 22.5l45 45q9 10 22.5 10t22.5 -10zM576 128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5 t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1280" d="M1152 960q0 -221 -147.5 -384.5t-364.5 -187.5v-612q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v612q-217 24 -364.5 187.5t-147.5 384.5q0 117 45.5 223.5t123 184t184 123t223.5 45.5t223.5 -45.5t184 -123t123 -184t45.5 -223.5zM576 512q185 0 316.5 131.5 t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1280" d="M1024 576q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1152 576q0 -117 -45.5 -223.5t-123 -184t-184 -123t-223.5 -45.5t-223.5 45.5t-184 123t-123 184t-45.5 223.5t45.5 223.5t123 184t184 123 t223.5 45.5t223.5 -45.5t184 -123t123 -184t45.5 -223.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" d="M1451 1408q35 0 60 -25t25 -60v-1366q0 -35 -25 -60t-60 -25h-391v595h199l30 232h-229v148q0 56 23.5 84t91.5 28l122 1v207q-63 9 -178 9q-136 0 -217.5 -80t-81.5 -226v-171h-200v-232h200v-595h-735q-35 0 -60 25t-25 60v1366q0 35 25 60t60 25h1366z" />
|
||||
@@ -556,10 +556,85 @@
|
||||
<glyph unicode="" d="M1088 1536q185 0 316.5 -93.5t131.5 -226.5v-896q0 -130 -125.5 -222t-305.5 -97l213 -202q16 -15 8 -35t-30 -20h-1056q-22 0 -30 20t8 35l213 202q-180 5 -305.5 97t-125.5 222v896q0 133 131.5 226.5t316.5 93.5h640zM768 192q80 0 136 56t56 136t-56 136t-136 56 t-136 -56t-56 -136t56 -136t136 -56zM1344 768v512h-1152v-512h1152z" />
|
||||
<glyph unicode="" d="M1088 1536q185 0 316.5 -93.5t131.5 -226.5v-896q0 -130 -125.5 -222t-305.5 -97l213 -202q16 -15 8 -35t-30 -20h-1056q-22 0 -30 20t8 35l213 202q-180 5 -305.5 97t-125.5 222v896q0 133 131.5 226.5t316.5 93.5h640zM288 224q66 0 113 47t47 113t-47 113t-113 47 t-113 -47t-47 -113t47 -113t113 -47zM704 768v512h-544v-512h544zM1248 224q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47zM1408 768v512h-576v-512h576z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 204v-209h-642v209h134v926h-6l-314 -1135h-243l-310 1135h-8v-926h135v-209h-538v209h69q21 0 43 19.5t22 37.5v881q0 18 -22 40t-43 22h-69v209h672l221 -821h6l223 821h670v-209h-71q-19 0 -41 -22t-22 -40v-881q0 -18 21.5 -37.5t41.5 -19.5h71z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" d="M809 532l266 499h-112l-157 -312q-24 -48 -44 -92l-42 92l-155 312h-120l263 -493v-324h101v318zM1536 1408v-1536h-1536v1536h1536z" />
|
||||
<glyph unicode="" horiz-adv-x="2296" d="M478 -139q-8 -16 -27 -34.5t-37 -25.5q-25 -9 -51.5 3.5t-28.5 31.5q-1 22 40 55t68 38q23 4 34 -21.5t2 -46.5zM1819 -139q7 -16 26 -34.5t38 -25.5q25 -9 51.5 3.5t27.5 31.5q2 22 -39.5 55t-68.5 38q-22 4 -33 -21.5t-2 -46.5zM1867 -30q13 -27 56.5 -59.5t77.5 -41.5 q45 -13 82 4.5t37 50.5q0 46 -67.5 100.5t-115.5 59.5q-40 5 -63.5 -37.5t-6.5 -76.5zM428 -30q-13 -27 -56 -59.5t-77 -41.5q-45 -13 -82 4.5t-37 50.5q0 46 67.5 100.5t115.5 59.5q40 5 63 -37.5t6 -76.5zM1158 1094h1q-41 0 -76 -15q27 -8 44 -30.5t17 -49.5 q0 -35 -27 -60t-65 -25q-52 0 -80 43q-5 -23 -5 -42q0 -74 56 -126.5t135 -52.5q80 0 136 52.5t56 126.5t-56 126.5t-136 52.5zM1462 1312q-99 109 -220.5 131.5t-245.5 -44.5q27 60 82.5 96.5t118 39.5t121.5 -17t99.5 -74.5t44.5 -131.5zM2212 73q8 -11 -11 -42 q7 -23 7 -40q1 -56 -44.5 -112.5t-109.5 -91.5t-118 -37q-48 -2 -92 21.5t-66 65.5q-687 -25 -1259 0q-23 -41 -66.5 -65t-92.5 -22q-86 3 -179.5 80.5t-92.5 160.5q2 22 7 40q-19 31 -11 42q6 10 31 1q14 22 41 51q-7 29 2 38q11 10 39 -4q29 20 59 34q0 29 13 37 q23 12 51 -16q35 5 61 -2q18 -4 38 -19v73q-11 0 -18 2q-53 10 -97 44.5t-55 87.5q-9 38 0 81q15 62 93 95q2 17 19 35.5t36 23.5t33 -7.5t19 -30.5h13q46 -5 60 -23q3 -3 5 -7q10 1 30.5 3.5t30.5 3.5q-15 11 -30 17q-23 40 -91 43q0 6 1 10q-62 2 -118.5 18.5t-84.5 47.5 q-32 36 -42.5 92t-2.5 112q16 126 90 179q23 16 52 4.5t32 -40.5q0 -1 1.5 -14t2.5 -21t3 -20t5.5 -19t8.5 -10q27 -14 76 -12q48 46 98 74q-40 4 -162 -14l47 46q61 58 163 111q145 73 282 86q-20 8 -41 15.5t-47 14t-42.5 10.5t-47.5 11t-43 10q595 126 904 -139 q98 -84 158 -222q85 -10 121 9h1q5 3 8.5 10t5.5 19t3 19.5t3 21.5l1 14q3 28 32 40t52 -5q73 -52 91 -178q7 -57 -3.5 -113t-42.5 -91q-28 -32 -83.5 -48.5t-115.5 -18.5v-10q-71 -2 -95 -43q-14 -5 -31 -17q11 -1 32 -3.5t30 -3.5q1 4 5 8q16 18 60 23h13q5 18 19 30t33 8 t36 -23t19 -36q79 -32 93 -95q9 -40 1 -81q-12 -53 -56 -88t-97 -44q-10 -2 -17 -2q0 -49 -1 -73q20 15 38 19q26 7 61 2q28 28 51 16q14 -9 14 -37q33 -16 59 -34q27 13 38 4q10 -10 2 -38q28 -30 41 -51q23 8 31 -1zM1937 1025q0 -29 -9 -54q82 -32 112 -132 q4 37 -9.5 98.5t-41.5 90.5q-20 19 -36 17t-16 -20zM1859 925q35 -42 47.5 -108.5t-0.5 -124.5q67 13 97 45q13 14 18 28q-3 64 -31 114.5t-79 66.5q-15 -15 -52 -21zM1822 921q-30 0 -44 1q42 -115 53 -239q21 0 43 3q16 68 1 135t-53 100zM258 839q30 100 112 132 q-9 25 -9 54q0 18 -16.5 20t-35.5 -17q-28 -29 -41.5 -90.5t-9.5 -98.5zM294 737q29 -31 97 -45q-13 58 -0.5 124.5t47.5 108.5v0q-37 6 -52 21q-51 -16 -78.5 -66t-31.5 -115q9 -17 18 -28zM471 683q14 124 73 235q-19 -4 -55 -18l-45 -19v1q-46 -89 -20 -196q25 -3 47 -3z M1434 644q8 -38 16.5 -108.5t11.5 -89.5q3 -18 9.5 -21.5t23.5 4.5q40 20 62 85.5t23 125.5q-24 2 -146 4zM1152 1285q-116 0 -199 -82.5t-83 -198.5q0 -117 83 -199.5t199 -82.5t199 82.5t83 199.5q0 116 -83 198.5t-199 82.5zM1380 646q-106 2 -211 0v1q-1 -27 2.5 -86 t13.5 -66q29 -14 93.5 -14.5t95.5 10.5q9 3 11 39t-0.5 69.5t-4.5 46.5zM1112 447q8 4 9.5 48t-0.5 88t-4 63v1q-212 -3 -214 -3q-4 -20 -7 -62t0 -83t14 -46q34 -15 101 -16t101 10zM718 636q-16 -59 4.5 -118.5t77.5 -84.5q15 -8 24 -5t12 21q3 16 8 90t10 103 q-69 -2 -136 -6zM591 510q3 -23 -34 -36q132 -141 271.5 -240t305.5 -154q172 49 310.5 146t293.5 250q-33 13 -30 34l3 9v1v-1q-17 2 -50 5.5t-48 4.5q-26 -90 -82 -132q-51 -38 -82 1q-5 6 -9 14q-7 13 -17 62q-2 -5 -5 -9t-7.5 -7t-8 -5.5t-9.5 -4l-10 -2.5t-12 -2 l-12 -1.5t-13.5 -1t-13.5 -0.5q-106 -9 -163 11q-4 -17 -10 -26.5t-21 -15t-23 -7t-36 -3.5q-2 0 -3 -0.5t-3 -0.5h-3q-179 -17 -203 40q-2 -63 -56 -54q-47 8 -91 54q-12 13 -20 26q-17 29 -26 65q-58 -6 -87 -10q1 -2 4 -10zM507 -118q3 14 3 30q-17 71 -51 130t-73 70 q-41 12 -101.5 -14.5t-104.5 -80t-39 -107.5q35 -53 100 -93t119 -42q51 -2 94 28t53 79zM510 53q23 -63 27 -119q195 113 392 174q-98 52 -180.5 120t-179.5 165q-6 -4 -29 -13q0 -2 -1 -5t-1 -4q31 -18 22 -37q-12 -23 -56 -34q-10 -13 -29 -24h-1q-2 -83 1 -150 q19 -34 35 -73zM579 -113q532 -21 1145 0q-254 147 -428 196q-76 -35 -156 -57q-8 -3 -16 0q-65 21 -129 49q-208 -60 -416 -188h-1v-1q1 0 1 1zM1763 -67q4 54 28 120q14 38 33 71l-1 -1q3 77 3 153q-15 8 -30 25q-42 9 -56 33q-9 20 22 38q-2 4 -2 9q-16 4 -28 12 q-204 -190 -383 -284q198 -59 414 -176zM2155 -90q5 54 -39 107.5t-104 80t-102 14.5q-38 -11 -72.5 -70.5t-51.5 -129.5q0 -16 3 -30q10 -49 53 -79t94 -28q54 2 119 42t100 93z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M1524 -25q0 -68 -48 -116t-116 -48t-116.5 48t-48.5 116t48.5 116.5t116.5 48.5t116 -48.5t48 -116.5zM775 -25q0 -68 -48.5 -116t-116.5 -48t-116 48t-48 116t48 116.5t116 48.5t116.5 -48.5t48.5 -116.5zM0 1469q57 -60 110.5 -104.5t121 -82t136 -63t166 -45.5 t200 -31.5t250 -18.5t304 -9.5t372.5 -2.5q139 0 244.5 -5t181 -16.5t124 -27.5t71 -39.5t24 -51.5t-19.5 -64t-56.5 -76.5t-89.5 -91t-116 -104.5t-139 -119q-185 -157 -286 -247q29 51 76.5 109t94 105.5t94.5 98.5t83 91.5t54 80.5t13 70t-45.5 55.5t-116.5 41t-204 23.5 t-304 5q-168 -2 -314 6t-256 23t-204.5 41t-159.5 51.5t-122.5 62.5t-91.5 66.5t-68 71.5t-50.5 69.5t-40 68t-36.5 59.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M896 1472q-169 0 -323 -66t-265.5 -177.5t-177.5 -265.5t-66 -323t66 -323t177.5 -265.5t265.5 -177.5t323 -66t323 66t265.5 177.5t177.5 265.5t66 323t-66 323t-177.5 265.5t-265.5 177.5t-323 66zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348 t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM496 704q16 0 16 -16v-480q0 -16 -16 -16h-32q-16 0 -16 16v480q0 16 16 16h32zM896 640q53 0 90.5 -37.5t37.5 -90.5q0 -35 -17.5 -64t-46.5 -46v-114q0 -14 -9 -23 t-23 -9h-64q-14 0 -23 9t-9 23v114q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5zM896 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM544 928v-96 q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v96q0 93 65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5v-96q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v96q0 146 -103 249t-249 103t-249 -103t-103 -249zM1408 192v512q0 26 -19 45t-45 19h-896q-26 0 -45 -19t-19 -45v-512 q0 -26 19 -45t45 -19h896q26 0 45 19t19 45z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M1920 1024v-768h-1664v768h1664zM2048 448h128v384h-128v288q0 14 -9 23t-23 9h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288zM2304 832v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113 v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160q53 0 90.5 -37.5t37.5 -90.5z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M256 256v768h1280v-768h-1280zM2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9 h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M256 256v768h896v-768h-896zM2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9 h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M256 256v768h512v-768h-512zM2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9 h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9h-1856q-14 0 -23 -9t-9 -23 v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
|
||||
<glyph unicode="" horiz-adv-x="1280" d="M1133 493q31 -30 14 -69q-17 -40 -59 -40h-382l201 -476q10 -25 0 -49t-34 -35l-177 -75q-25 -10 -49 0t-35 34l-191 452l-312 -312q-19 -19 -45 -19q-12 0 -24 5q-40 17 -40 59v1504q0 42 40 59q12 5 24 5q27 0 45 -19z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M832 1408q-320 0 -320 -224v-416h128v-128h-128v-544q0 -224 320 -224h64v-128h-64q-272 0 -384 146q-112 -146 -384 -146h-64v128h64q320 0 320 224v544h-128v128h128v416q0 224 -320 224h-64v128h64q272 0 384 -146q112 146 384 146h64v-128h-64z" />
|
||||
<glyph unicode="" horiz-adv-x="2048" d="M2048 1152h-128v-1024h128v-384h-384v128h-1280v-128h-384v384h128v1024h-128v384h384v-128h1280v128h384v-384zM1792 1408v-128h128v128h-128zM128 1408v-128h128v128h-128zM256 -128v128h-128v-128h128zM1664 0v128h128v1024h-128v128h-1280v-128h-128v-1024h128v-128 h1280zM1920 -128v128h-128v-128h128zM1280 896h384v-768h-896v256h-384v768h896v-256zM512 512h640v512h-640v-512zM1536 256v512h-256v-384h-384v-128h640z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M2304 768h-128v-640h128v-384h-384v128h-896v-128h-384v384h128v128h-384v-128h-384v384h128v640h-128v384h384v-128h896v128h384v-384h-128v-128h384v128h384v-384zM2048 1024v-128h128v128h-128zM1408 1408v-128h128v128h-128zM128 1408v-128h128v128h-128zM256 256 v128h-128v-128h128zM1536 384h-128v-128h128v128zM384 384h896v128h128v640h-128v128h-896v-128h-128v-640h128v-128zM896 -128v128h-128v-128h128zM2176 -128v128h-128v-128h128zM2048 128v640h-128v128h-384v-384h128v-384h-384v128h-384v-128h128v-128h896v128h128z" />
|
||||
<glyph unicode="" d="M1024 288v-416h-928q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1344q40 0 68 -28t28 -68v-928h-416q-40 0 -68 -28t-28 -68zM1152 256h381q-15 -82 -65 -132l-184 -184q-50 -50 -132 -65v381z" />
|
||||
<glyph unicode="" d="M1400 256h-248v-248q29 10 41 22l185 185q12 12 22 41zM1120 384h288v896h-1280v-1280h896v288q0 40 28 68t68 28zM1536 1312v-1024q0 -40 -20 -88t-48 -76l-184 -184q-28 -28 -76 -48t-88 -20h-1024q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1344q40 0 68 -28t28 -68 z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M1951 538q0 -26 -15.5 -44.5t-38.5 -23.5q-8 -2 -18 -2h-153v140h153q10 0 18 -2q23 -5 38.5 -23.5t15.5 -44.5zM1933 751q0 -25 -15 -42t-38 -21q-3 -1 -15 -1h-139v129h139q3 0 8.5 -0.5t6.5 -0.5q23 -4 38 -21.5t15 -42.5zM728 587v308h-228v-308q0 -58 -38 -94.5 t-105 -36.5q-108 0 -229 59v-112q53 -15 121 -23t109 -9l42 -1q328 0 328 217zM1442 403v113q-99 -52 -200 -59q-108 -8 -169 41t-61 142t61 142t169 41q101 -7 200 -58v112q-48 12 -100 19.5t-80 9.5l-28 2q-127 6 -218.5 -14t-140.5 -60t-71 -88t-22 -106t22 -106t71 -88 t140.5 -60t218.5 -14q101 4 208 31zM2176 518q0 54 -43 88.5t-109 39.5v3q57 8 89 41.5t32 79.5q0 55 -41 88t-107 36q-3 0 -12 0.5t-14 0.5h-455v-510h491q74 0 121.5 36.5t47.5 96.5zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90 t90 38h2048q52 0 90 -38t38 -90z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M858 295v693q-106 -41 -172 -135.5t-66 -211.5t66 -211.5t172 -134.5zM1362 641q0 117 -66 211.5t-172 135.5v-694q106 41 172 135.5t66 211.5zM1577 641q0 -159 -78.5 -294t-213.5 -213.5t-294 -78.5q-119 0 -227.5 46.5t-187 125t-125 187t-46.5 227.5q0 159 78.5 294 t213.5 213.5t294 78.5t294 -78.5t213.5 -213.5t78.5 -294zM1960 634q0 139 -55.5 261.5t-147.5 205.5t-213.5 131t-252.5 48h-301q-176 0 -323.5 -81t-235 -230t-87.5 -335q0 -171 87 -317.5t236 -231.5t323 -85h301q129 0 251.5 50.5t214.5 135t147.5 202.5t55.5 246z M2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1664 -96v1088q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5v-1088q0 -13 9.5 -22.5t22.5 -9.5h1088q13 0 22.5 9.5t9.5 22.5zM1792 992v-1088q0 -66 -47 -113t-113 -47h-1088q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1088q66 0 113 -47t47 -113 zM1408 1376v-160h-128v160q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5v-1088q0 -13 9.5 -22.5t22.5 -9.5h160v-128h-160q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1088q66 0 113 -47t47 -113z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M1728 1088l-384 -704h768zM448 1088l-384 -704h768zM1269 1280q-14 -40 -45.5 -71.5t-71.5 -45.5v-1291h608q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1344q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h608v1291q-40 14 -71.5 45.5t-45.5 71.5h-491q-14 0 -23 9t-9 23v64 q0 14 9 23t23 9h491q21 57 70 92.5t111 35.5t111 -35.5t70 -92.5h491q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-491zM1088 1264q33 0 56.5 23.5t23.5 56.5t-23.5 56.5t-56.5 23.5t-56.5 -23.5t-23.5 -56.5t23.5 -56.5t56.5 -23.5zM2176 384q0 -73 -46.5 -131t-117.5 -91 t-144.5 -49.5t-139.5 -16.5t-139.5 16.5t-144.5 49.5t-117.5 91t-46.5 131q0 11 35 81t92 174.5t107 195.5t102 184t56 100q18 33 56 33t56 -33q4 -7 56 -100t102 -184t107 -195.5t92 -174.5t35 -81zM896 384q0 -73 -46.5 -131t-117.5 -91t-144.5 -49.5t-139.5 -16.5 t-139.5 16.5t-144.5 49.5t-117.5 91t-46.5 131q0 11 35 81t92 174.5t107 195.5t102 184t56 100q18 33 56 33t56 -33q4 -7 56 -100t102 -184t107 -195.5t92 -174.5t35 -81z" />
|
||||
<glyph unicode="" d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM874 700q77 29 149 92.5t129.5 152.5t92.5 210t35 253h-1024q0 -132 35 -253t92.5 -210t129.5 -152.5t149 -92.5q19 -7 30.5 -23.5t11.5 -36.5t-11.5 -36.5t-30.5 -23.5q-77 -29 -149 -92.5 t-129.5 -152.5t-92.5 -210t-35 -253h1024q0 132 -35 253t-92.5 210t-129.5 152.5t-149 92.5q-19 7 -30.5 23.5t-11.5 36.5t11.5 36.5t30.5 23.5z" />
|
||||
<glyph unicode="" d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM1280 1408h-1024q0 -66 9 -128h1006q9 61 9 128zM1280 -128q0 130 -34 249.5t-90.5 208t-126.5 152t-146 94.5h-230q-76 -31 -146 -94.5t-126.5 -152t-90.5 -208t-34 -249.5h1024z" />
|
||||
<glyph unicode="" d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM1280 1408h-1024q0 -206 85 -384h854q85 178 85 384zM1223 192q-54 141 -145.5 241.5t-194.5 142.5h-230q-103 -42 -194.5 -142.5t-145.5 -241.5h910z" />
|
||||
<glyph unicode="" d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM874 700q77 29 149 92.5t129.5 152.5t92.5 210t35 253h-1024q0 -132 35 -253t92.5 -210t129.5 -152.5t149 -92.5q19 -7 30.5 -23.5t11.5 -36.5t-11.5 -36.5t-30.5 -23.5q-137 -51 -244 -196 h700q-107 145 -244 196q-19 7 -30.5 23.5t-11.5 36.5t11.5 36.5t30.5 23.5z" />
|
||||
<glyph unicode="" d="M1504 -64q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v128q0 14 9 23t23 9h1472zM130 0q3 55 16 107t30 95t46 87t53.5 76t64.5 69.5t66 60t70.5 55t66.5 47.5t65 43q-43 28 -65 43t-66.5 47.5t-70.5 55t-66 60t-64.5 69.5t-53.5 76t-46 87 t-30 95t-16 107h1276q-3 -55 -16 -107t-30 -95t-46 -87t-53.5 -76t-64.5 -69.5t-66 -60t-70.5 -55t-66.5 -47.5t-65 -43q43 -28 65 -43t66.5 -47.5t70.5 -55t66 -60t64.5 -69.5t53.5 -76t46 -87t30 -95t16 -107h-1276zM1504 1536q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9 h-1472q-14 0 -23 9t-9 23v128q0 14 9 23t23 9h1472z" />
|
||||
<glyph unicode="" d="M768 1152q-53 0 -90.5 -37.5t-37.5 -90.5v-128h-32v93q0 48 -32 81.5t-80 33.5q-46 0 -79 -33t-33 -79v-429l-32 30v172q0 48 -32 81.5t-80 33.5q-46 0 -79 -33t-33 -79v-224q0 -47 35 -82l310 -296q39 -39 39 -102q0 -26 19 -45t45 -19h640q26 0 45 19t19 45v25 q0 41 10 77l108 436q10 36 10 77v246q0 48 -32 81.5t-80 33.5q-46 0 -79 -33t-33 -79v-32h-32v125q0 40 -25 72.5t-64 40.5q-14 2 -23 2q-46 0 -79 -33t-33 -79v-128h-32v122q0 51 -32.5 89.5t-82.5 43.5q-5 1 -13 1zM768 1280q84 0 149 -50q57 34 123 34q59 0 111 -27 t86 -76q27 7 59 7q100 0 170 -71.5t70 -171.5v-246q0 -51 -13 -108l-109 -436q-6 -24 -6 -71q0 -80 -56 -136t-136 -56h-640q-84 0 -138 58.5t-54 142.5l-308 296q-76 73 -76 175v224q0 99 70.5 169.5t169.5 70.5q11 0 16 -1q6 95 75.5 160t164.5 65q52 0 98 -21 q72 69 174 69z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M880 1408q-46 0 -79 -33t-33 -79v-656h-32v528q0 46 -33 79t-79 33t-79 -33t-33 -79v-528v-256l-154 205q-38 51 -102 51q-53 0 -90.5 -37.5t-37.5 -90.5q0 -43 26 -77l384 -512q38 -51 102 -51h688q34 0 61 22t34 56l76 405q5 32 5 59v498q0 46 -33 79t-79 33t-79 -33 t-33 -79v-272h-32v528q0 46 -33 79t-79 33t-79 -33t-33 -79v-528h-32v656q0 46 -33 79t-79 33zM880 1536q68 0 125.5 -35.5t88.5 -96.5q19 4 42 4q99 0 169.5 -70.5t70.5 -169.5v-17q105 6 180.5 -64t75.5 -175v-498q0 -40 -8 -83l-76 -404q-14 -79 -76.5 -131t-143.5 -52 h-688q-60 0 -114.5 27.5t-90.5 74.5l-384 512q-51 68 -51 154q0 106 75 181t181 75q78 0 128 -34v434q0 99 70.5 169.5t169.5 70.5q23 0 42 -4q31 61 88.5 96.5t125.5 35.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1073 -128h-177q-163 0 -226 141q-23 49 -23 102v5q-62 30 -98.5 88.5t-36.5 127.5q0 38 5 48h-261q-106 0 -181 75t-75 181t75 181t181 75h113l-44 17q-74 28 -119.5 93.5t-45.5 145.5q0 106 75 181t181 75q46 0 91 -17l628 -239h401q106 0 181 -75t75 -181v-668 q0 -88 -54 -157.5t-140 -90.5l-339 -85q-92 -23 -186 -23zM1024 583l-155 -71l-163 -74q-30 -14 -48 -41.5t-18 -60.5q0 -46 33 -79t79 -33q26 0 46 10l338 154q-49 10 -80.5 50t-31.5 90v55zM1344 272q0 46 -33 79t-79 33q-26 0 -46 -10l-290 -132q-28 -13 -37 -17 t-30.5 -17t-29.5 -23.5t-16 -29t-8 -40.5q0 -50 31.5 -82t81.5 -32q20 0 38 9l352 160q30 14 48 41.5t18 60.5zM1112 1024l-650 248q-24 8 -46 8q-53 0 -90.5 -37.5t-37.5 -90.5q0 -40 22.5 -73t59.5 -47l526 -200v-64h-640q-53 0 -90.5 -37.5t-37.5 -90.5t37.5 -90.5 t90.5 -37.5h535l233 106v198q0 63 46 106l111 102h-69zM1073 0q82 0 155 19l339 85q43 11 70 45.5t27 78.5v668q0 53 -37.5 90.5t-90.5 37.5h-308l-136 -126q-36 -33 -36 -82v-296q0 -46 33 -77t79 -31t79 35t33 81v208h32v-208q0 -70 -57 -114q52 -8 86.5 -48.5t34.5 -93.5 q0 -42 -23 -78t-61 -53l-310 -141h91z" />
|
||||
<glyph unicode="" horiz-adv-x="2048" d="M1151 1536q61 0 116 -28t91 -77l572 -781q118 -159 118 -359v-355q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v177l-286 143h-546q-80 0 -136 56t-56 136v32q0 119 84.5 203.5t203.5 84.5h420l42 128h-686q-100 0 -173.5 67.5t-81.5 166.5q-65 79 -65 182v32 q0 80 56 136t136 56h959zM1920 -64v355q0 157 -93 284l-573 781q-39 52 -103 52h-959q-26 0 -45 -19t-19 -45q0 -32 1.5 -49.5t9.5 -40.5t25 -43q10 31 35.5 50t56.5 19h832v-32h-832q-26 0 -45 -19t-19 -45q0 -44 3 -58q8 -44 44 -73t81 -29h640h91q40 0 68 -28t28 -68 q0 -15 -5 -30l-64 -192q-10 -29 -35 -47.5t-56 -18.5h-443q-66 0 -113 -47t-47 -113v-32q0 -26 19 -45t45 -19h561q16 0 29 -7l317 -158q24 -13 38.5 -36t14.5 -50v-197q0 -26 19 -45t45 -19h384q26 0 45 19t19 45z" />
|
||||
<glyph unicode="" horiz-adv-x="2048" d="M816 1408q-48 0 -79.5 -34t-31.5 -82q0 -14 3 -28l150 -624h-26l-116 482q-9 38 -39.5 62t-69.5 24q-47 0 -79 -34t-32 -81q0 -11 4 -29q3 -13 39 -161t68 -282t32 -138v-227l-307 230q-34 26 -77 26q-52 0 -89.5 -36.5t-37.5 -88.5q0 -67 56 -110l507 -379 q34 -26 76 -26h694q33 0 59 20.5t34 52.5l100 401q8 30 10 88t9 86l116 478q3 12 3 26q0 46 -33 79t-80 33q-38 0 -69 -25.5t-40 -62.5l-99 -408h-26l132 547q3 14 3 28q0 47 -32 80t-80 33q-38 0 -68.5 -24t-39.5 -62l-145 -602h-127l-164 682q-9 38 -39.5 62t-68.5 24z M1461 -256h-694q-85 0 -153 51l-507 380q-50 38 -78.5 94t-28.5 118q0 105 75 179t180 74q25 0 49.5 -5.5t41.5 -11t41 -20.5t35 -23t38.5 -29.5t37.5 -28.5l-123 512q-7 35 -7 59q0 93 60 162t152 79q14 87 80.5 144.5t155.5 57.5q83 0 148 -51.5t85 -132.5l103 -428 l83 348q20 81 85 132.5t148 51.5q87 0 152.5 -54t82.5 -139q93 -10 155 -78t62 -161q0 -30 -7 -57l-116 -477q-5 -22 -5 -67q0 -51 -13 -108l-101 -401q-19 -75 -79.5 -122.5t-137.5 -47.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M640 1408q-53 0 -90.5 -37.5t-37.5 -90.5v-512v-384l-151 202q-41 54 -107 54q-52 0 -89 -38t-37 -90q0 -43 26 -77l384 -512q38 -51 102 -51h718q22 0 39.5 13.5t22.5 34.5l92 368q24 96 24 194v217q0 41 -28 71t-68 30t-68 -28t-28 -68h-32v61q0 48 -32 81.5t-80 33.5 q-46 0 -79 -33t-33 -79v-64h-32v90q0 55 -37 94.5t-91 39.5q-53 0 -90.5 -37.5t-37.5 -90.5v-96h-32v570q0 55 -37 94.5t-91 39.5zM640 1536q107 0 181.5 -77.5t74.5 -184.5v-220q22 2 32 2q99 0 173 -69q47 21 99 21q113 0 184 -87q27 7 56 7q94 0 159 -67.5t65 -161.5 v-217q0 -116 -28 -225l-92 -368q-16 -64 -68 -104.5t-118 -40.5h-718q-60 0 -114.5 27.5t-90.5 74.5l-384 512q-51 68 -51 154q0 105 74.5 180.5t179.5 75.5q71 0 130 -35v547q0 106 75 181t181 75zM768 128v384h-32v-384h32zM1024 128v384h-32v-384h32zM1280 128v384h-32 v-384h32z" />
|
||||
<glyph unicode="" d="M1288 889q60 0 107 -23q141 -63 141 -226v-177q0 -94 -23 -186l-85 -339q-21 -86 -90.5 -140t-157.5 -54h-668q-106 0 -181 75t-75 181v401l-239 628q-17 45 -17 91q0 106 75 181t181 75q80 0 145.5 -45.5t93.5 -119.5l17 -44v113q0 106 75 181t181 75t181 -75t75 -181 v-261q27 5 48 5q69 0 127.5 -36.5t88.5 -98.5zM1072 896q-33 0 -60.5 -18t-41.5 -48l-74 -163l-71 -155h55q50 0 90 -31.5t50 -80.5l154 338q10 20 10 46q0 46 -33 79t-79 33zM1293 761q-22 0 -40.5 -8t-29 -16t-23.5 -29.5t-17 -30.5t-17 -37l-132 -290q-10 -20 -10 -46 q0 -46 33 -79t79 -33q33 0 60.5 18t41.5 48l160 352q9 18 9 38q0 50 -32 81.5t-82 31.5zM128 1120q0 -22 8 -46l248 -650v-69l102 111q43 46 106 46h198l106 233v535q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5v-640h-64l-200 526q-14 37 -47 59.5t-73 22.5 q-53 0 -90.5 -37.5t-37.5 -90.5zM1180 -128q44 0 78.5 27t45.5 70l85 339q19 73 19 155v91l-141 -310q-17 -38 -53 -61t-78 -23q-53 0 -93.5 34.5t-48.5 86.5q-44 -57 -114 -57h-208v32h208q46 0 81 33t35 79t-31 79t-77 33h-296q-49 0 -82 -36l-126 -136v-308 q0 -53 37.5 -90.5t90.5 -37.5h668z" />
|
||||
<glyph unicode="" horiz-adv-x="1973" d="M857 992v-117q0 -13 -9.5 -22t-22.5 -9h-298v-812q0 -13 -9 -22.5t-22 -9.5h-135q-13 0 -22.5 9t-9.5 23v812h-297q-13 0 -22.5 9t-9.5 22v117q0 14 9 23t23 9h793q13 0 22.5 -9.5t9.5 -22.5zM1895 995l77 -961q1 -13 -8 -24q-10 -10 -23 -10h-134q-12 0 -21 8.5 t-10 20.5l-46 588l-189 -425q-8 -19 -29 -19h-120q-20 0 -29 19l-188 427l-45 -590q-1 -12 -10 -20.5t-21 -8.5h-135q-13 0 -23 10q-9 10 -9 24l78 961q1 12 10 20.5t21 8.5h142q20 0 29 -19l220 -520q10 -24 20 -51q3 7 9.5 24.5t10.5 26.5l221 520q9 19 29 19h141 q13 0 22 -8.5t10 -20.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1042 833q0 88 -60 121q-33 18 -117 18h-123v-281h162q66 0 102 37t36 105zM1094 548l205 -373q8 -17 -1 -31q-8 -16 -27 -16h-152q-20 0 -28 17l-194 365h-155v-350q0 -14 -9 -23t-23 -9h-134q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h294q128 0 190 -24q85 -31 134 -109 t49 -180q0 -92 -42.5 -165.5t-115.5 -109.5q6 -10 9 -16zM896 1376q-150 0 -286 -58.5t-234.5 -157t-157 -234.5t-58.5 -286t58.5 -286t157 -234.5t234.5 -157t286 -58.5t286 58.5t234.5 157t157 234.5t58.5 286t-58.5 286t-157 234.5t-234.5 157t-286 58.5zM1792 640 q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M605 303q153 0 257 104q14 18 3 36l-45 82q-6 13 -24 17q-16 2 -27 -11l-4 -3q-4 -4 -11.5 -10t-17.5 -13t-23.5 -14.5t-28.5 -13.5t-33.5 -9.5t-37.5 -3.5q-76 0 -125 50t-49 127q0 76 48 125.5t122 49.5q37 0 71.5 -14t50.5 -28l16 -14q11 -11 26 -10q16 2 24 14l53 78 q13 20 -2 39q-3 4 -11 12t-30 23.5t-48.5 28t-67.5 22.5t-86 10q-148 0 -246 -96.5t-98 -240.5q0 -146 97 -241.5t247 -95.5zM1235 303q153 0 257 104q14 18 4 36l-45 82q-8 14 -25 17q-16 2 -27 -11l-4 -3q-4 -4 -11.5 -10t-17.5 -13t-23.5 -14.5t-28.5 -13.5t-33.5 -9.5 t-37.5 -3.5q-76 0 -125 50t-49 127q0 76 48 125.5t122 49.5q37 0 71.5 -14t50.5 -28l16 -14q11 -11 26 -10q16 2 24 14l53 78q13 20 -2 39q-3 4 -11 12t-30 23.5t-48.5 28t-67.5 22.5t-86 10q-147 0 -245.5 -96.5t-98.5 -240.5q0 -146 97 -241.5t247 -95.5zM896 1376 q-150 0 -286 -58.5t-234.5 -157t-157 -234.5t-58.5 -286t58.5 -286t157 -234.5t234.5 -157t286 -58.5t286 58.5t234.5 157t157 234.5t58.5 286t-58.5 286t-157 234.5t-234.5 157t-286 58.5zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191 t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71z" />
|
||||
<glyph unicode="" horiz-adv-x="2048" d="M736 736l384 -384l-384 -384l-672 672l672 672l168 -168l-96 -96l-72 72l-480 -480l480 -480l193 193l-289 287zM1312 1312l672 -672l-672 -672l-168 168l96 96l72 -72l480 480l-480 480l-193 -193l289 -287l-96 -96l-384 384z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M717 182l271 271l-279 279l-88 -88l192 -191l-96 -96l-279 279l279 279l40 -40l87 87l-127 128l-454 -454zM1075 190l454 454l-454 454l-271 -271l279 -279l88 88l-192 191l96 96l279 -279l-279 -279l-40 40l-87 -88zM1792 640q0 -182 -71 -348t-191 -286t-286 -191 t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M651 539q0 -39 -27.5 -66.5t-65.5 -27.5q-39 0 -66.5 27.5t-27.5 66.5q0 38 27.5 65.5t66.5 27.5q38 0 65.5 -27.5t27.5 -65.5zM1805 540q0 -39 -27.5 -66.5t-66.5 -27.5t-66.5 27.5t-27.5 66.5t27.5 66t66.5 27t66.5 -27t27.5 -66zM765 539q0 79 -56.5 136t-136.5 57 t-136.5 -56.5t-56.5 -136.5t56.5 -136.5t136.5 -56.5t136.5 56.5t56.5 136.5zM1918 540q0 80 -56.5 136.5t-136.5 56.5q-79 0 -136 -56.5t-57 -136.5t56.5 -136.5t136.5 -56.5t136.5 56.5t56.5 136.5zM850 539q0 -116 -81.5 -197.5t-196.5 -81.5q-116 0 -197.5 82t-81.5 197 t82 196.5t197 81.5t196.5 -81.5t81.5 -196.5zM2004 540q0 -115 -81.5 -196.5t-197.5 -81.5q-115 0 -196.5 81.5t-81.5 196.5t81.5 196.5t196.5 81.5q116 0 197.5 -81.5t81.5 -196.5zM1040 537q0 191 -135.5 326.5t-326.5 135.5q-125 0 -231 -62t-168 -168.5t-62 -231.5 t62 -231.5t168 -168.5t231 -62q191 0 326.5 135.5t135.5 326.5zM1708 1110q-254 111 -556 111q-319 0 -573 -110q117 0 223 -45.5t182.5 -122.5t122 -183t45.5 -223q0 115 43.5 219.5t118 180.5t177.5 123t217 50zM2187 537q0 191 -135 326.5t-326 135.5t-326.5 -135.5 t-135.5 -326.5t135.5 -326.5t326.5 -135.5t326 135.5t135 326.5zM1921 1103h383q-44 -51 -75 -114.5t-40 -114.5q110 -151 110 -337q0 -156 -77 -288t-209 -208.5t-287 -76.5q-133 0 -249 56t-196 155q-47 -56 -129 -179q-11 22 -53.5 82.5t-74.5 97.5 q-80 -99 -196.5 -155.5t-249.5 -56.5q-155 0 -287 76.5t-209 208.5t-77 288q0 186 110 337q-9 51 -40 114.5t-75 114.5h365q149 100 355 156.5t432 56.5q224 0 421 -56t348 -157z" />
|
||||
<glyph unicode="" horiz-adv-x="1280" d="M640 629q-188 0 -321 133t-133 320q0 188 133 321t321 133t321 -133t133 -321q0 -187 -133 -320t-321 -133zM640 1306q-92 0 -157.5 -65.5t-65.5 -158.5q0 -92 65.5 -157.5t157.5 -65.5t157.5 65.5t65.5 157.5q0 93 -65.5 158.5t-157.5 65.5zM1163 574q13 -27 15 -49.5 t-4.5 -40.5t-26.5 -38.5t-42.5 -37t-61.5 -41.5q-115 -73 -315 -94l73 -72l267 -267q30 -31 30 -74t-30 -73l-12 -13q-31 -30 -74 -30t-74 30q-67 68 -267 268l-267 -268q-31 -30 -74 -30t-73 30l-12 13q-31 30 -31 73t31 74l267 267l72 72q-203 21 -317 94 q-39 25 -61.5 41.5t-42.5 37t-26.5 38.5t-4.5 40.5t15 49.5q10 20 28 35t42 22t56 -2t65 -35q5 -4 15 -11t43 -24.5t69 -30.5t92 -24t113 -11q91 0 174 25.5t120 50.5l38 25q33 26 65 35t56 2t42 -22t28 -35z" />
|
||||
<glyph unicode="" d="M927 956q0 -66 -46.5 -112.5t-112.5 -46.5t-112.5 46.5t-46.5 112.5t46.5 112.5t112.5 46.5t112.5 -46.5t46.5 -112.5zM1141 593q-10 20 -28 32t-47.5 9.5t-60.5 -27.5q-10 -8 -29 -20t-81 -32t-127 -20t-124 18t-86 36l-27 18q-31 25 -60.5 27.5t-47.5 -9.5t-28 -32 q-22 -45 -2 -74.5t87 -73.5q83 -53 226 -67l-51 -52q-142 -142 -191 -190q-22 -22 -22 -52.5t22 -52.5l9 -9q22 -22 52.5 -22t52.5 22l191 191q114 -115 191 -191q22 -22 52.5 -22t52.5 22l9 9q22 22 22 52.5t-22 52.5l-191 190l-52 52q141 14 225 67q67 44 87 73.5t-2 74.5 zM1092 956q0 134 -95 229t-229 95t-229 -95t-95 -229t95 -229t229 -95t229 95t95 229zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1720" d="M1565 1408q65 0 110 -45.5t45 -110.5v-519q0 -176 -68 -336t-182.5 -275t-274 -182.5t-334.5 -67.5q-176 0 -335.5 67.5t-274.5 182.5t-183 275t-68 336v519q0 64 46 110t110 46h1409zM861 344q47 0 82 33l404 388q37 35 37 85q0 49 -34.5 83.5t-83.5 34.5q-47 0 -82 -33 l-323 -310l-323 310q-35 33 -81 33q-49 0 -83.5 -34.5t-34.5 -83.5q0 -51 36 -85l405 -388q33 -33 81 -33z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M1494 -103l-295 695q-25 -49 -158.5 -305.5t-198.5 -389.5q-1 -1 -27.5 -0.5t-26.5 1.5q-82 193 -255.5 587t-259.5 596q-21 50 -66.5 107.5t-103.5 100.5t-102 43q0 5 -0.5 24t-0.5 27h583v-50q-39 -2 -79.5 -16t-66.5 -43t-10 -64q26 -59 216.5 -499t235.5 -540 q31 61 140 266.5t131 247.5q-19 39 -126 281t-136 295q-38 69 -201 71v50l513 -1v-47q-60 -2 -93.5 -25t-12.5 -69q33 -70 87 -189.5t86 -187.5q110 214 173 363q24 55 -10 79.5t-129 26.5q1 7 1 25v24q64 0 170.5 0.5t180 1t92.5 0.5v-49q-62 -2 -119 -33t-90 -81 l-213 -442q13 -33 127.5 -290t121.5 -274l441 1017q-14 38 -49.5 62.5t-65 31.5t-55.5 8v50l460 -4l1 -2l-1 -44q-139 -4 -201 -145q-526 -1216 -559 -1291h-49z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M949 643q0 -26 -16.5 -45t-41.5 -19q-26 0 -45 16.5t-19 41.5q0 26 17 45t42 19t44 -16.5t19 -41.5zM964 585l350 581q-9 -8 -67.5 -62.5t-125.5 -116.5t-136.5 -127t-117 -110.5t-50.5 -51.5l-349 -580q7 7 67 62t126 116.5t136 127t117 111t50 50.5zM1611 640 q0 -201 -104 -371q-3 2 -17 11t-26.5 16.5t-16.5 7.5q-13 0 -13 -13q0 -10 59 -44q-74 -112 -184.5 -190.5t-241.5 -110.5l-16 67q-1 10 -15 10q-5 0 -8 -5.5t-2 -9.5l16 -68q-72 -15 -146 -15q-199 0 -372 105q1 2 13 20.5t21.5 33.5t9.5 19q0 13 -13 13q-6 0 -17 -14.5 t-22.5 -34.5t-13.5 -23q-113 75 -192 187.5t-110 244.5l69 15q10 3 10 15q0 5 -5.5 8t-10.5 2l-68 -15q-14 72 -14 139q0 206 109 379q2 -1 18.5 -12t30 -19t17.5 -8q13 0 13 12q0 6 -12.5 15.5t-32.5 21.5l-20 12q77 112 189 189t244 107l15 -67q2 -10 15 -10q5 0 8 5.5 t2 10.5l-15 66q71 13 134 13q204 0 379 -109q-39 -56 -39 -65q0 -13 12 -13q11 0 48 64q111 -75 187.5 -186t107.5 -241l-56 -12q-10 -2 -10 -16q0 -5 5.5 -8t9.5 -2l57 13q14 -72 14 -140zM1696 640q0 163 -63.5 311t-170.5 255t-255 170.5t-311 63.5t-311 -63.5 t-255 -170.5t-170.5 -255t-63.5 -311t63.5 -311t170.5 -255t255 -170.5t311 -63.5t311 63.5t255 170.5t170.5 255t63.5 311zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191 t191 -286t71 -348z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M893 1536q240 2 451 -120q232 -134 352 -372l-742 39q-160 9 -294 -74.5t-185 -229.5l-276 424q128 159 311 245.5t383 87.5zM146 1131l337 -663q72 -143 211 -217t293 -45l-230 -451q-212 33 -385 157.5t-272.5 316t-99.5 411.5q0 267 146 491zM1732 962 q58 -150 59.5 -310.5t-48.5 -306t-153 -272t-246 -209.5q-230 -133 -498 -119l405 623q88 131 82.5 290.5t-106.5 277.5zM896 942q125 0 213.5 -88.5t88.5 -213.5t-88.5 -213.5t-213.5 -88.5t-213.5 88.5t-88.5 213.5t88.5 213.5t213.5 88.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M903 -256q-283 0 -504.5 150.5t-329.5 398.5q-58 131 -67 301t26 332.5t111 312t179 242.5l-11 -281q11 14 68 15.5t70 -15.5q42 81 160.5 138t234.5 59q-54 -45 -119.5 -148.5t-58.5 -163.5q25 -8 62.5 -13.5t63 -7.5t68 -4t50.5 -3q15 -5 9.5 -45.5t-30.5 -75.5 q-5 -7 -16.5 -18.5t-56.5 -35.5t-101 -34l15 -189l-139 67q-18 -43 -7.5 -81.5t36 -66.5t65.5 -41.5t81 -6.5q51 9 98 34.5t83.5 45t73.5 17.5q61 -4 89.5 -33t19.5 -65q-1 -2 -2.5 -5.5t-8.5 -12.5t-18 -15.5t-31.5 -10.5t-46.5 -1q-60 -95 -144.5 -135.5t-209.5 -29.5 q74 -61 162.5 -82.5t168.5 -6t154.5 52t128 87.5t80.5 104q43 91 39 192.5t-37.5 188.5t-78.5 125q87 -38 137 -79.5t77 -112.5q15 170 -57.5 343t-209.5 284q265 -77 412 -279.5t151 -517.5q2 -127 -40.5 -255t-123.5 -238t-189 -196t-247.5 -135.5t-288.5 -49.5z" />
|
||||
<glyph unicode="" d="M768 -92q77 0 139.5 63t100.5 166t59 234.5t21 268.5t-21 268.5t-59 234.5t-100.5 166t-139.5 63t-139.5 -63t-100.5 -166t-59 -234.5t-21 -268.5t21 -268.5t59 -234.5t100.5 -166t139.5 -63zM768 -256q-184 0 -333 77t-240 203t-141 287t-50 329t50 329t141 287t240 203 t333 77q148 0 274 -50t214.5 -136t151.5 -201t92.5 -244t29.5 -265t-29.5 -265t-92.5 -244t-151.5 -201t-214.5 -136t-274 -50z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M716 -69q-143 35 -261.5 114t-197.5 191q-139 -300 -17 -398q26 -21 85 -24.5t127.5 9.5t141 41.5t122.5 66.5zM693 762h452q0 108 -61.5 169t-168.5 61q-103 0 -162.5 -62.5t-59.5 -167.5zM1724 1137h-34q26 102 22.5 170t-25 110t-63.5 57t-93.5 11t-115 -26.5 t-128.5 -56.5t-134 -79q129 -37 238.5 -113.5t185 -179t110 -231.5t15.5 -262h-1005q0 -60 10 -106t34 -85t69.5 -60t112.5 -21q87 0 142.5 44t72.5 122h540q-71 -230 -281.5 -377t-477.5 -147q-83 0 -159 15q-35 -40 -151 -94t-248 -78t-219 35q-78 60 -100 159t7 214 t88 242t143.5 248t173.5 226.5t177.5 183.5t156.5 112v24q-120 -37 -258.5 -137.5t-240.5 -207t-159 -195.5q4 106 34 201t80 169t118 135.5t147.5 100.5t168 65.5t180.5 29.5t185 -8q310 186 503 189h7q57 0 103 -18q80 -30 98 -132.5t-30 -248.5z" />
|
||||
<glyph unicode="" horiz-adv-x="2048" d="M1792 288v960q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5zM1920 1248v-960q0 -66 -47 -113t-113 -47h-736v-128h352q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23 v64q0 14 9 23t23 9h352v128h-736q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M138 1408h197q-70 -64 -126 -149q-36 -56 -59 -115t-30 -125.5t-8.5 -120t10.5 -132t21 -126t28 -136.5q4 -19 6 -28q51 -238 81 -329q57 -171 152 -275h-272q-48 0 -82 34t-34 82v1304q0 48 34 82t82 34zM1346 1408h308q48 0 82 -34t34 -82v-1304q0 -48 -34 -82t-82 -34 h-178q212 210 196 565l-469 -101q-2 -45 -12 -82t-31 -72t-59.5 -59.5t-93.5 -36.5q-123 -26 -199 40q-32 27 -53 61t-51.5 129t-64.5 258q-35 163 -45.5 263t-5.5 139t23 77q20 41 62.5 73t102.5 45q45 12 83.5 6.5t67 -17t54 -35t43 -48t34.5 -56.5l468 100 q-68 175 -180 287z" />
|
||||
<glyph unicode="" horiz-adv-x="2304" d="M1391 390v0l-1 1q-15 18 -34.5 37.5t-62.5 57.5t-93.5 62t-95.5 24q-48 0 -83 -21.5t-51 -54t-23 -59t-7 -47.5v0v0q0 -21 7 -48t23 -59t51 -53.5t83 -21.5q45 0 95.5 24t94 62.5t62 57t34.5 37.5zM2103 390q0 21 -7 47.5t-23 59t-51 54t-83 21.5q-45 0 -95.5 -24 t-94 -62.5t-62 -57t-34.5 -37.5l-1 -1v0v0l1 -1q15 -18 34.5 -37.5t62.5 -57.5t93.5 -62t95.5 -24q48 0 83 21.5t51 53.5t23 59t7 48zM2304 393q0 -69 -24 -137.5t-68 -126t-116 -93.5t-159 -36q-68 0 -134 24t-113.5 58.5t-84.5 69.5t-59.5 59t-25.5 24t-22.5 -24 t-54.5 -58.5t-81.5 -69.5t-115 -59t-143.5 -24q-65 0 -123.5 22.5t-96.5 54t-66.5 66.5t-41 59.5t-12.5 32.5q0 -8 -8.5 -26.5t-25 -45.5t-47 -55t-69 -52.5t-96.5 -40t-125 -15.5q-71 0 -130 15.5t-98.5 39.5t-70.5 56.5t-48 63.5t-27.5 63.5t-14 54t-3.5 36.5h217 q0 -55 49 -107.5t126 -52.5q79 0 134.5 67t55.5 148q0 80 -52 136.5t-138 56.5q-5 0 -13 -0.5t-31 -5t-43 -12t-42 -24.5t-34 -40h-195l102 583h602v-174h-445q-27 -159 -41 -248q4 0 16.5 13t31.5 28.5t65 28.5t108 13t114 -20.5t82.5 -49.5t51.5 -58.5t31 -50t11 -20.5 t13 25t36.5 60.5t60.5 71.5t97 61t133 25t140.5 -25t115.5 -60.5t83.5 -71.5t56.5 -61t21 -25q2 0 22 25t56 60.5t83.5 71.5t115.5 61t140 25q92 0 164.5 -35t115.5 -93t65 -125t22 -137z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1551 60q15 6 26 3t11 -17.5t-15 -33.5q-13 -16 -44 -43.5t-95.5 -68t-141 -74t-188 -58t-229.5 -24.5q-119 0 -238 31t-209 76.5t-172.5 104t-132.5 105t-84 87.5q-8 9 -10 16.5t1 12t8 7t11.5 2t11.5 -4.5q192 -117 300 -166q389 -176 799 -90q190 40 391 135z M1758 175q11 -16 2.5 -69.5t-28.5 -102.5q-34 -83 -85 -124q-17 -14 -26 -9t0 24q21 45 44.5 121.5t6.5 98.5q-5 7 -15.5 11.5t-27 6t-29.5 2.5t-35 0t-31.5 -2t-31 -3t-22.5 -2q-6 -1 -13 -1.5t-11 -1t-8.5 -1t-7 -0.5h-5.5h-4.5t-3 0.5t-2 1.5l-1.5 3q-6 16 47 40t103 30 q46 7 108 1t76 -24zM1364 618q0 -31 13.5 -64t32 -58t37.5 -46t33 -32l13 -11l-227 -224q-40 37 -79 75.5t-58 58.5l-19 20q-11 11 -25 33q-38 -59 -97.5 -102.5t-127.5 -63.5t-140 -23t-137.5 21t-117.5 65.5t-83 113t-31 162.5q0 84 28 154t72 116.5t106.5 83t122.5 57 t130 34.5t119.5 18.5t99.5 6.5v127q0 65 -21 97q-34 53 -121 53q-6 0 -16.5 -1t-40.5 -12t-56 -29.5t-56 -59.5t-48 -96l-294 27q0 60 22 119t67 113t108 95t151.5 65.5t190.5 24.5q100 0 181 -25t129.5 -61.5t81 -83t45 -86t12.5 -73.5v-589zM692 597q0 -86 70 -133 q66 -44 139 -22q84 25 114 123q14 45 14 101v162q-59 -2 -111 -12t-106.5 -33.5t-87 -71t-32.5 -114.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1536 1280q52 0 90 -38t38 -90v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128zM1152 1376v-288q0 -14 9 -23t23 -9 h64q14 0 23 9t9 23v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM384 1376v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM1536 -128v1024h-1408v-1024h1408zM896 448h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224 v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v224q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-224z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1152 416v-64q0 -14 -9 -23t-23 -9h-576q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h576q14 0 23 -9t9 -23zM128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23 t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47 t47 -113v-96h128q52 0 90 -38t38 -90z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1111 151l-46 -46q-9 -9 -22 -9t-23 9l-188 189l-188 -189q-10 -9 -23 -9t-22 9l-46 46q-9 9 -9 22t9 23l189 188l-189 188q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l188 -188l188 188q10 9 23 9t22 -9l46 -46q9 -9 9 -22t-9 -23l-188 -188l188 -188q9 -10 9 -23t-9 -22z M128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280 q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1303 572l-512 -512q-10 -9 -23 -9t-23 9l-288 288q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l220 -220l444 444q10 9 23 9t22 -9l46 -46q9 -9 9 -22t-9 -23zM128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23 t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47 t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M448 1536q26 0 45 -19t19 -45v-891l536 429q17 14 40 14q26 0 45 -19t19 -45v-379l536 429q17 14 40 14q26 0 45 -19t19 -45v-1152q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h384z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M512 448q66 0 128 15v-655q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v655q61 -15 128 -15zM512 1536q212 0 362 -150t150 -362t-150 -362t-362 -150t-362 150t-150 362t150 362t362 150zM512 1312q14 0 23 9t9 23t-9 23t-23 9q-146 0 -249 -103t-103 -249 q0 -14 9 -23t23 -9t23 9t9 23q0 119 84.5 203.5t203.5 84.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1745 1239q10 -10 10 -23t-10 -23l-141 -141q-28 -28 -68 -28h-1344q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h576v64q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-64h512q40 0 68 -28zM768 320h256v-512q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v512zM1600 768 q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19h-1344q-40 0 -68 28l-141 141q-10 10 -10 23t10 23l141 141q28 28 68 28h512v192h256v-192h576z" />
|
||||
<glyph unicode="" horiz-adv-x="2048" d="M2020 1525q28 -20 28 -53v-1408q0 -20 -11 -36t-29 -23l-640 -256q-24 -11 -48 0l-616 246l-616 -246q-10 -5 -24 -5q-19 0 -36 11q-28 20 -28 53v1408q0 20 11 36t29 23l640 256q24 11 48 0l616 -246l616 246q32 13 60 -6zM736 1390v-1270l576 -230v1270zM128 1173 v-1270l544 217v1270zM1920 107v1270l-544 -217v-1270z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M512 1536q13 0 22.5 -9.5t9.5 -22.5v-1472q0 -20 -17 -28l-480 -256q-7 -4 -15 -4q-13 0 -22.5 9.5t-9.5 22.5v1472q0 20 17 28l480 256q7 4 15 4zM1760 1536q13 0 22.5 -9.5t9.5 -22.5v-1472q0 -20 -17 -28l-480 -256q-7 -4 -15 -4q-13 0 -22.5 9.5t-9.5 22.5v1472 q0 20 17 28l480 256q7 4 15 4zM640 1536q8 0 14 -3l512 -256q18 -10 18 -29v-1472q0 -13 -9.5 -22.5t-22.5 -9.5q-8 0 -14 3l-512 256q-18 10 -18 29v1472q0 13 9.5 22.5t22.5 9.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M640 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1408 640q0 53 -37.5 90.5t-90.5 37.5 t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-110 0 -211 18q-173 -173 -435 -229q-52 -10 -86 -13q-12 -1 -22 6t-13 18q-4 15 20 37q5 5 23.5 21.5t25.5 23.5t23.5 25.5t24 31.5t20.5 37 t20 48t14.5 57.5t12.5 72.5q-146 90 -229.5 216.5t-83.5 269.5q0 174 120 321.5t326 233t450 85.5t450 -85.5t326 -233t120 -321.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M640 640q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1024 640q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 -53 -37.5 -90.5t-90.5 -37.5 t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM896 1152q-204 0 -381.5 -69.5t-282 -187.5t-104.5 -255q0 -112 71.5 -213.5t201.5 -175.5l87 -50l-27 -96q-24 -91 -70 -172q152 63 275 171l43 38l57 -6q69 -8 130 -8q204 0 381.5 69.5t282 187.5 t104.5 255t-104.5 255t-282 187.5t-381.5 69.5zM1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22h-5q-15 0 -27 10.5t-16 27.5v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51 t27 59t26 76q-157 89 -247.5 220t-90.5 281q0 130 71 248.5t191 204.5t286 136.5t348 50.5t348 -50.5t286 -136.5t191 -204.5t71 -248.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1024" d="M512 345l512 295v-591l-512 -296v592zM0 640v-591l512 296zM512 1527v-591l-512 -296v591zM512 936l512 295v-591z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" d="M1709 1018q-10 -236 -332 -651q-333 -431 -562 -431q-142 0 -240 263q-44 160 -132 482q-72 262 -157 262q-18 0 -127 -76l-77 98q24 21 108 96.5t130 115.5q156 138 241 146q95 9 153 -55.5t81 -203.5q44 -287 66 -373q55 -249 120 -249q51 0 154 161q101 161 109 246 q13 139 -109 139q-57 0 -121 -26q120 393 459 382q251 -8 236 -326z" />
|
||||
<glyph unicode="" d="M0 1408h1536v-1536h-1536v1536zM1085 293l-221 631l221 297h-634l221 -297l-221 -631l317 -304z" />
|
||||
<glyph unicode="" d="M0 1408h1536v-1536h-1536v1536zM908 1088l-12 -33l75 -83l-31 -114l25 -25l107 57l107 -57l25 25l-31 114l75 83l-12 33h-95l-53 96h-32l-53 -96h-95zM641 925q32 0 44.5 -16t11.5 -63l174 21q0 55 -17.5 92.5t-50.5 56t-69 25.5t-85 7q-133 0 -199 -57.5t-66 -182.5v-72 h-96v-128h76q20 0 20 -8v-382q0 -14 -5 -20t-18 -7l-73 -7v-88h448v86l-149 14q-6 1 -8.5 1.5t-3.5 2.5t-0.5 4t1 7t0.5 10v387h191l38 128h-231q-6 0 -2 6t4 9v80q0 27 1.5 40.5t7.5 28t19.5 20t36.5 5.5zM1248 96v86l-54 9q-7 1 -9.5 2.5t-2.5 3t1 7.5t1 12v520h-275 l-23 -101l83 -22q23 -7 23 -27v-370q0 -14 -6 -18.5t-20 -6.5l-70 -9v-86h352z" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
<glyph unicode="" horiz-adv-x="1792" />
|
||||
</font>
|
||||
</defs></svg>
|
Before Width: | Height: | Size: 306 KiB After Width: | Height: | Size: 348 KiB |
BIN
editor/vendor/font-awesome/fonts/fontawesome-webfont.ttf
vendored
Normal file → Executable file
BIN
editor/vendor/font-awesome/fonts/fontawesome-webfont.ttf
vendored
Normal file → Executable file
Binary file not shown.
BIN
editor/vendor/font-awesome/fonts/fontawesome-webfont.woff
vendored
Normal file → Executable file
BIN
editor/vendor/font-awesome/fonts/fontawesome-webfont.woff
vendored
Normal file → Executable file
Binary file not shown.
BIN
editor/vendor/font-awesome/fonts/fontawesome-webfont.woff2
vendored
Normal file → Executable file
BIN
editor/vendor/font-awesome/fonts/fontawesome-webfont.woff2
vendored
Normal file → Executable file
Binary file not shown.
@@ -22,6 +22,7 @@
|
||||
"show": "Toggle Sidebar"
|
||||
},
|
||||
"displayStatus": "Display Node Status",
|
||||
"displayConfig": "Configuration Nodes",
|
||||
"import": "Import",
|
||||
"export": "Export",
|
||||
"clipboard": "Clipboard",
|
||||
@@ -29,7 +30,7 @@
|
||||
"subflows": "Subflows",
|
||||
"createSubflow": "Create Subflow",
|
||||
"selectionToSubflow": "Selection to Subflow",
|
||||
"workspaces": "Workspaces",
|
||||
"flows": "Tabs",
|
||||
"add": "Add",
|
||||
"rename": "Rename",
|
||||
"delete": "Delete",
|
||||
@@ -87,22 +88,23 @@
|
||||
"cancel": "Cancel"
|
||||
},
|
||||
"undeployedChanges": "You have undeployed changes.\n\nLeaving this page will lose these changes.",
|
||||
"improperlyConfigured": "Some of the nodes are not properly configured.",
|
||||
"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?"
|
||||
}
|
||||
},
|
||||
"subflow": {
|
||||
"tabLabel": "Subflow: __name__",
|
||||
"editSubflow": "Edit flow __name__",
|
||||
"edit": "Edit flow",
|
||||
"subflowInstances": "There is __count__ instance of this subflow",
|
||||
"subflowInstances_plural": "There are __count__ instances of this subflow",
|
||||
"editSubflowName": "edit name",
|
||||
"input": "input",
|
||||
"output": "output",
|
||||
"editSubflowProperties": "edit properties",
|
||||
"input": "inputs:",
|
||||
"output": "outputs:",
|
||||
"deleteSubflow": "delete subflow",
|
||||
"info": "Description",
|
||||
"format":"markdown format",
|
||||
"errors": {
|
||||
"noNodesSelected": "<strong>Cannot create subflow</strong>: no nodes selected",
|
||||
"multipleInputsToSelection": "<strong>Cannot create subflow</strong>: multiple inputs to selection"
|
||||
@@ -116,7 +118,10 @@
|
||||
"nodesUse_plural": "__count__ nodes use this config",
|
||||
"addNewConfig": "Add new __type__ config node",
|
||||
"editConfig": "Edit __type__ config node",
|
||||
"addNewType": "Add new __type__..."
|
||||
"addNewType": "Add new __type__...",
|
||||
"errors": {
|
||||
"scopeChange": "Changing the scope will make it unavailable to nodes in other flows that use it"
|
||||
}
|
||||
},
|
||||
"keyboard": {
|
||||
"selectAll": "Select all nodes",
|
||||
@@ -191,7 +196,12 @@
|
||||
},
|
||||
"config": {
|
||||
"name": "Configuration nodes",
|
||||
"label": "config"
|
||||
"label": "config",
|
||||
"local": "on this flow",
|
||||
"global": "on all flows",
|
||||
"none": "none",
|
||||
"subflows": "subflows",
|
||||
"flows": "flows"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -28,7 +28,7 @@
|
||||
<!-- Generally, there should be an input for each property of the node. -->
|
||||
<!-- The for and id attributes identify the corresponding property -->
|
||||
<!-- (with the 'node-input-' prefix). -->
|
||||
<!-- The available icon classes are defined Twitter Bootstrap glyphicons -->
|
||||
<!-- The available icon classes are defined Font Awesome Icons (FA Icons) -->
|
||||
<div class="form-row">
|
||||
<label for="node-input-topic"><i class="fa fa-tasks"></i> Topic</label>
|
||||
<input type="text" id="node-input-topic" placeholder="Topic">
|
||||
|
@@ -81,13 +81,13 @@ module.exports = function(RED) {
|
||||
if (node != null) {
|
||||
try {
|
||||
node.receive();
|
||||
res.send(200);
|
||||
res.sendStatus(200);
|
||||
} catch(err) {
|
||||
res.send(500);
|
||||
res.sendStatus(500);
|
||||
node.error(RED._("inject.failed",{error:err.toString()}));
|
||||
}
|
||||
} else {
|
||||
res.send(404);
|
||||
res.sendStatus(404);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@@ -15,6 +15,25 @@
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-template-name="catch">
|
||||
<div class="form-row">
|
||||
<label style="width: auto" for="node-input-scope" data-i18n="catch.label.source"></label>
|
||||
<select id="node-input-scope-select">
|
||||
<option value="all" data-i18n="catch.scope.all"></option>
|
||||
<option value="target" data-i18n="catch.scope.selected"></options>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row node-input-target-row" style="display: none;">
|
||||
<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;">
|
||||
<div style="display: inline-block;"><a id="node-input-target-sort-label" href="#" data-i18n="[title]catch.label.sortByLabel"><span data-i18n="catch.label.node"></span> <i class="node-input-catch-sort-label-a fa fa-caret-down"></i><i class="node-input-catch-sort-label-d fa fa-caret-up"></i></a></div>
|
||||
<div style="position: absolute; right: 10px; width: 50px; display: inline-block; text-align: right;"><a id="node-input-target-sort-type" href="#" data-i18n="[title]catch.label.sortByType"><i class="node-input-catch-sort-sublabel-a fa fa-caret-down"></i><i class="node-input-catch-sort-sublabel-d fa fa-caret-up"></i> <span data-i18n="catch.label.type"></span></a></div>
|
||||
</div>
|
||||
<div style="background: #fbfbfb; box-sizing: border-box; position:absolute; top:20px;bottom:0;left:0px;right:0px; overflow-y: scroll; overflow-x: hidden;">
|
||||
<ul id="node-input-catch-target-container" style=" list-style-type:none; margin: 0;"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
||||
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
|
||||
@@ -37,26 +56,235 @@
|
||||
<li><code>message</code> : the error message</li>
|
||||
<li><code>source.id</code> : the id of the node that threw the error</li>
|
||||
<li><code>source.type</code> : the type of the node that threw the error</li>
|
||||
<li><code>source.name</code> : the name, if set, of the node that threw the error</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>If the message already had a <code>error</code> property, it is copied to <code>_error</code>.</p>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#node-input-catch-target-container {
|
||||
position: relative;
|
||||
}
|
||||
#node-input-catch-target-container li {
|
||||
padding: 2px 5px;
|
||||
background: none;
|
||||
font-size: 0.8em;
|
||||
margin:0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#node-input-catch-target-container li label {
|
||||
margin-bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
#node-input-catch-target-container li label input {
|
||||
vertical-align: top;
|
||||
width:15px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
#node-input-catch-target-container li:hover,
|
||||
#node-input-catch-target-container li:hover .node-input-target-node-sublabel {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
.node-input-target-node-sublabel {
|
||||
position:absolute;
|
||||
right: 0px;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
font-size: 0.8em;
|
||||
background: #fbfbfb;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('catch',{
|
||||
category: 'input',
|
||||
color:"#e49191",
|
||||
defaults: {
|
||||
name: {value:""}
|
||||
name: {value:""},
|
||||
scope: {value:null}
|
||||
},
|
||||
inputs:0,
|
||||
outputs:1,
|
||||
icon: "alert.png",
|
||||
label: function() {
|
||||
return this.name||this._("catch.catch");
|
||||
return this.name||this.scope?this._("catch.catchNodes",{number:this.scope.length}):this._("catch.catch");
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
var nodeList = $("#node-input-catch-target-container");
|
||||
var node = this;
|
||||
function createNodeList() {
|
||||
var scope = node.scope || [];
|
||||
nodeList.empty();
|
||||
|
||||
var candidateNodes = RED.nodes.filterNodes({z:node.z});
|
||||
var allChecked = true;
|
||||
|
||||
candidateNodes.forEach(function(n) {
|
||||
if (n.id === node.id) {
|
||||
return;
|
||||
}
|
||||
var isChecked = scope.indexOf(n.id) !== -1;
|
||||
|
||||
allChecked = allChecked && isChecked;
|
||||
|
||||
var container = $('<li/>',{class:"node-input-target-node"});
|
||||
var row = $('<label/>',{for:"node-input-target-node-"+n.id}).appendTo(container);
|
||||
$('<input>',{type:"checkbox",class:"node-input-target-node-checkbox",id:"node-input-target-node-"+n.id})
|
||||
.data('node-id',n.id)
|
||||
.prop('checked', isChecked)
|
||||
.appendTo(row);
|
||||
container.on('mouseover',function(e) {
|
||||
n.highlighted = true;
|
||||
n.dirty = true;
|
||||
RED.view.redraw();
|
||||
});
|
||||
container.on('mouseout',function(e) {
|
||||
n.highlighted = false;
|
||||
n.dirty = true;
|
||||
RED.view.redraw();
|
||||
});
|
||||
var labelSpan = $('<span>');
|
||||
var nodeDef = RED.nodes.getType(n.type);
|
||||
var label;
|
||||
var sublabel;
|
||||
if (nodeDef) {
|
||||
var l = nodeDef.label;
|
||||
label = (typeof l === "function" ? l.call(n) : l)||"";
|
||||
sublabel = n.type;
|
||||
if (sublabel.indexOf("subflow:") === 0) {
|
||||
var subflowId = sublabel.substring(8);
|
||||
var subflow = RED.nodes.subflow(subflowId);
|
||||
sublabel = "subflow : "+subflow.name;
|
||||
}
|
||||
}
|
||||
if (!nodeDef || !label) {
|
||||
label = n.type;
|
||||
}
|
||||
$('<span>',{class:"node-input-target-node-label",style:"white-space:nowrap"}).text(label).appendTo(row);
|
||||
if (sublabel) {
|
||||
$('<span>',{class:"node-input-target-node-sublabel"}).text(sublabel).appendTo(row);
|
||||
}
|
||||
|
||||
container.appendTo(nodeList);
|
||||
});
|
||||
|
||||
$(".node-input-target-node-checkbox").change(function() {
|
||||
if (!this.checked) {
|
||||
$("#node-input-target-node-checkbox-all").prop('checked',false);
|
||||
}
|
||||
});
|
||||
|
||||
$("#node-input-target-node-checkbox-all").prop('checked',allChecked);
|
||||
|
||||
sortNodeList('label');
|
||||
}
|
||||
|
||||
function sortNodeList(sortOn) {
|
||||
var currentSort = nodeList.data('currentSort');
|
||||
var currentSortOrder = nodeList.data('currentSortOrder');
|
||||
|
||||
if (!currentSort) {
|
||||
currentSort = sortOn;
|
||||
currentSortOrder = 'a';
|
||||
} else {
|
||||
if (currentSort === sortOn) {
|
||||
currentSortOrder = (currentSortOrder === 'a'?'d':'a');
|
||||
} else {
|
||||
currentSortOrder = 'a';
|
||||
}
|
||||
currentSort = sortOn;
|
||||
}
|
||||
nodeList.data('currentSort',currentSort);
|
||||
nodeList.data('currentSortOrder',currentSortOrder);
|
||||
|
||||
$("#node-input-catch-target-container-div .fa").hide();
|
||||
$(".node-input-catch-sort-"+currentSort+"-"+currentSortOrder).show();
|
||||
|
||||
|
||||
var items = nodeList.find("li").get();
|
||||
items.sort(function(a,b) {
|
||||
var labelA = $(a).find(".node-input-target-node-"+currentSort).text().toLowerCase();
|
||||
var labelB = $(b).find(".node-input-target-node-"+currentSort).text().toLowerCase();
|
||||
if (labelA < labelB) { return currentSortOrder==='a'?-1:1; }
|
||||
if (labelA > labelB) { return currentSortOrder==='a'?1:-1; }
|
||||
return 0;
|
||||
});
|
||||
$.each(items, function(i, li){
|
||||
nodeList.append(li);
|
||||
});
|
||||
}
|
||||
$("#node-input-target-sort-label").click(function(e) {
|
||||
e.preventDefault();
|
||||
sortNodeList('label');
|
||||
});
|
||||
|
||||
$("#node-input-target-sort-type").click(function(e) {
|
||||
e.preventDefault();
|
||||
sortNodeList('sublabel')
|
||||
});
|
||||
$("#node-input-target-node-checkbox-all").change(function() {
|
||||
$(".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") {
|
||||
createNodeList();
|
||||
$(".node-input-target-row").show();
|
||||
} else {
|
||||
$(".node-input-target-row").hide();
|
||||
}
|
||||
});
|
||||
if (this.scope == null) {
|
||||
$("#node-input-scope-select").val("all");
|
||||
} else {
|
||||
$("#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();
|
||||
if (scope === 'all') {
|
||||
this.scope = null;
|
||||
} else {
|
||||
var node = this;
|
||||
node.scope = [];
|
||||
$(".node-input-target-node-checkbox").each(function(n) {
|
||||
if ($(this).prop("checked")) {
|
||||
node.scope.push($(this).data('node-id'));
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@@ -20,6 +20,7 @@ module.exports = function(RED) {
|
||||
function CatchNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
var node = this;
|
||||
this.scope = n.scope;
|
||||
this.on("input",function(msg) {
|
||||
this.send(msg);
|
||||
});
|
||||
|
281
nodes/core/core/25-status.html
Normal file
281
nodes/core/core/25-status.html
Normal file
@@ -0,0 +1,281 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-template-name="status">
|
||||
<div class="form-row">
|
||||
<label style="width: auto" for="node-input-scope" data-i18n="status.label.source"></label>
|
||||
<select id="node-input-scope-select">
|
||||
<option value="all" data-i18n="status.scope.all"></option>
|
||||
<option value="target" data-i18n="status.scope.selected"></options>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row node-input-target-row" style="display: none;">
|
||||
<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;">
|
||||
<div style="display: inline-block;"><a id="node-input-target-sort-label" href="#" data-i18n="[title]status.label.sortByLabel"><span data-i18n="status.label.node"></span> <i class="node-input-status-sort-label-a fa fa-caret-down"></i><i class="node-input-status-sort-label-d fa fa-caret-up"></i></a></div>
|
||||
<div style="position: absolute; right: 10px; width: 50px; display: inline-block; text-align: right;"><a id="node-input-target-sort-type" href="#" data-i18n="[title]status.label.sortByType"><i class="node-input-status-sort-sublabel-a fa fa-caret-down"></i><i class="node-input-status-sort-sublabel-d fa fa-caret-up"></i> <span data-i18n="status.label.type"></span></a></div>
|
||||
</div>
|
||||
<div style="background: #fbfbfb; box-sizing: border-box; position:absolute; top:20px;bottom:0;left:0px;right:0px; overflow-y: scroll; overflow-x: hidden;">
|
||||
<ul id="node-input-status-target-container" style=" list-style-type:none; margin: 0;"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
||||
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
|
||||
</div>
|
||||
</script>
|
||||
<script type="text/x-red" data-help-name="status">
|
||||
<p>Send status messages from other nodes on the same tab.</p>
|
||||
<p>The message sent by this node will have a <code>status</code> property
|
||||
with the following attributes:
|
||||
<ul>
|
||||
<li><code>text</code> : the status text</li>
|
||||
<li><code>source.type</code> : the type of the node that reported status</li>
|
||||
<li><code>source.id</code> : the id of the node that reported status</li>
|
||||
<li><code>source.name</code> : the name, if set, of the node that reported status</li>
|
||||
|
||||
</ul>
|
||||
</p>
|
||||
</script>
|
||||
<style>
|
||||
#node-input-status-target-container {
|
||||
position: relative;
|
||||
}
|
||||
#node-input-status-target-container li {
|
||||
padding: 2px 5px;
|
||||
background: none;
|
||||
font-size: 0.8em;
|
||||
margin:0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#node-input-status-target-container li label {
|
||||
margin-bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
#node-input-status-target-container li label input {
|
||||
vertical-align: top;
|
||||
width:15px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
#node-input-status-target-container li:hover,
|
||||
#node-input-status-target-container li:hover .node-input-target-node-sublabel {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
.node-input-target-node-sublabel {
|
||||
position:absolute;
|
||||
right: 0px;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
font-size: 0.8em;
|
||||
background: #fbfbfb;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('status',{
|
||||
category: 'input',
|
||||
color:"#c0edc0",
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
scope: {value:null}
|
||||
},
|
||||
inputs:0,
|
||||
outputs:1,
|
||||
icon: "alert.png",
|
||||
label: function() {
|
||||
return this.name||this.scope?this._("status.statusNodes",{number:this.scope.length}):this._("status.status");
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
var nodeList = $("#node-input-status-target-container");
|
||||
var node = this;
|
||||
function createNodeList() {
|
||||
var scope = node.scope || [];
|
||||
nodeList.empty();
|
||||
|
||||
var candidateNodes = RED.nodes.filterNodes({z:node.z});
|
||||
var allChecked = true;
|
||||
|
||||
candidateNodes.forEach(function(n) {
|
||||
if (n.id === node.id) {
|
||||
return;
|
||||
}
|
||||
var isChecked = scope.indexOf(n.id) !== -1;
|
||||
|
||||
allChecked = allChecked && isChecked;
|
||||
|
||||
var container = $('<li/>',{class:"node-input-target-node"});
|
||||
var row = $('<label/>',{for:"node-input-target-node-"+n.id}).appendTo(container);
|
||||
$('<input>',{type:"checkbox",class:"node-input-target-node-checkbox",id:"node-input-target-node-"+n.id})
|
||||
.data('node-id',n.id)
|
||||
.prop('checked', isChecked)
|
||||
.appendTo(row);
|
||||
container.on('mouseover',function(e) {
|
||||
n.highlighted = true;
|
||||
n.dirty = true;
|
||||
RED.view.redraw();
|
||||
});
|
||||
container.on('mouseout',function(e) {
|
||||
n.highlighted = false;
|
||||
n.dirty = true;
|
||||
RED.view.redraw();
|
||||
});
|
||||
var labelSpan = $('<span>');
|
||||
var nodeDef = RED.nodes.getType(n.type);
|
||||
var label;
|
||||
var sublabel;
|
||||
if (nodeDef) {
|
||||
var l = nodeDef.label;
|
||||
label = (typeof l === "function" ? l.call(n) : l)||"";
|
||||
sublabel = n.type;
|
||||
if (sublabel.indexOf("subflow:") === 0) {
|
||||
var subflowId = sublabel.substring(8);
|
||||
var subflow = RED.nodes.subflow(subflowId);
|
||||
sublabel = "subflow : "+subflow.name;
|
||||
}
|
||||
}
|
||||
if (!nodeDef || !label) {
|
||||
label = n.type;
|
||||
}
|
||||
$('<span>',{class:"node-input-target-node-label",style:"white-space:nowrap"}).text(label).appendTo(row);
|
||||
if (sublabel) {
|
||||
$('<span>',{class:"node-input-target-node-sublabel"}).text(sublabel).appendTo(row);
|
||||
}
|
||||
|
||||
container.appendTo(nodeList);
|
||||
});
|
||||
|
||||
$(".node-input-target-node-checkbox").change(function() {
|
||||
if (!this.checked) {
|
||||
$("#node-input-target-node-checkbox-all").prop('checked',false);
|
||||
}
|
||||
});
|
||||
|
||||
$("#node-input-target-node-checkbox-all").prop('checked',allChecked);
|
||||
|
||||
sortNodeList('label');
|
||||
}
|
||||
|
||||
function sortNodeList(sortOn) {
|
||||
var currentSort = nodeList.data('currentSort');
|
||||
var currentSortOrder = nodeList.data('currentSortOrder');
|
||||
|
||||
if (!currentSort) {
|
||||
currentSort = sortOn;
|
||||
currentSortOrder = 'a';
|
||||
} else {
|
||||
if (currentSort === sortOn) {
|
||||
currentSortOrder = (currentSortOrder === 'a'?'d':'a');
|
||||
} else {
|
||||
currentSortOrder = 'a';
|
||||
}
|
||||
currentSort = sortOn;
|
||||
}
|
||||
nodeList.data('currentSort',currentSort);
|
||||
nodeList.data('currentSortOrder',currentSortOrder);
|
||||
|
||||
$("#node-input-status-target-container-div .fa").hide();
|
||||
$(".node-input-status-sort-"+currentSort+"-"+currentSortOrder).show();
|
||||
|
||||
|
||||
var items = nodeList.find("li").get();
|
||||
items.sort(function(a,b) {
|
||||
var labelA = $(a).find(".node-input-target-node-"+currentSort).text().toLowerCase();
|
||||
var labelB = $(b).find(".node-input-target-node-"+currentSort).text().toLowerCase();
|
||||
if (labelA < labelB) { return currentSortOrder==='a'?-1:1; }
|
||||
if (labelA > labelB) { return currentSortOrder==='a'?1:-1; }
|
||||
return 0;
|
||||
});
|
||||
$.each(items, function(i, li){
|
||||
nodeList.append(li);
|
||||
});
|
||||
}
|
||||
$("#node-input-target-sort-label").click(function(e) {
|
||||
e.preventDefault();
|
||||
sortNodeList('label');
|
||||
});
|
||||
|
||||
$("#node-input-target-sort-type").click(function(e) {
|
||||
e.preventDefault();
|
||||
sortNodeList('sublabel')
|
||||
});
|
||||
$("#node-input-target-node-checkbox-all").change(function() {
|
||||
$(".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") {
|
||||
createNodeList();
|
||||
$(".node-input-target-row").show();
|
||||
} else {
|
||||
$(".node-input-target-row").hide();
|
||||
}
|
||||
});
|
||||
if (this.scope == null) {
|
||||
$("#node-input-scope-select").val("all");
|
||||
} else {
|
||||
$("#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();
|
||||
if (scope === 'all') {
|
||||
this.scope = null;
|
||||
} else {
|
||||
var node = this;
|
||||
node.scope = [];
|
||||
$(".node-input-target-node-checkbox").each(function(n) {
|
||||
if ($(this).prop("checked")) {
|
||||
node.scope.push($(this).data('node-id'));
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
30
nodes/core/core/25-status.js
Normal file
30
nodes/core/core/25-status.js
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
|
||||
module.exports = function(RED) {
|
||||
"use strict";
|
||||
|
||||
function StatusNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
var node = this;
|
||||
this.scope = n.scope;
|
||||
this.on("input", function(msg) {
|
||||
this.send(msg);
|
||||
});
|
||||
}
|
||||
|
||||
RED.nodes.registerType("status",StatusNode);
|
||||
}
|
@@ -201,7 +201,7 @@
|
||||
msg.className = 'debug-message'+(o.level?(' debug-message-level-'+o.level):'');
|
||||
msg.innerHTML = '<span class="debug-message-date">'+
|
||||
getTimestamp()+'</span>'+
|
||||
(name?'<span class="debug-message-name">['+name+']':'')+
|
||||
(name?'<span class="debug-message-name">'+name:'')+
|
||||
'</span>';
|
||||
// NOTE: relying on function error to have a "type" that all other msgs don't
|
||||
if (o.hasOwnProperty("type") && (o.type === "function")) {
|
||||
@@ -212,11 +212,11 @@
|
||||
errorLvlType = 'warn';
|
||||
}
|
||||
msg.className = 'debug-message debug-message-level-' + errorLvl;
|
||||
msg.innerHTML += '<span class="debug-message-topic">[function] : (' + errorLvlType + ')</span>';
|
||||
msg.innerHTML += '<span class="debug-message-topic">function : (' + errorLvlType + ')</span>';
|
||||
} else {
|
||||
msg.innerHTML += '<span class="debug-message-topic">'+
|
||||
(o.topic?topic+' : ':'')+
|
||||
(o.property?'[msg.'+property+']':'[msg]')+" : "+format+
|
||||
(o.property?'msg.'+property:'msg')+" : "+format+
|
||||
|
||||
'</span>';
|
||||
}
|
||||
@@ -284,7 +284,7 @@
|
||||
.debug-message-topic {
|
||||
display: block;
|
||||
background: #fff;
|
||||
padding: 1px 5px;
|
||||
padding: 1px;
|
||||
font-size: 10px;
|
||||
color: #a66;
|
||||
}
|
||||
|
@@ -82,21 +82,27 @@ module.exports = function(RED) {
|
||||
msg.format = "error";
|
||||
msg.msg = msg.msg.toString();
|
||||
} else if (msg.msg instanceof Buffer) {
|
||||
msg.format = "buffer";
|
||||
msg.format = "buffer ["+msg.msg.length+"]";
|
||||
msg.msg = msg.msg.toString('hex');
|
||||
} else if (typeof msg.msg === 'object') {
|
||||
var seen = [];
|
||||
msg.format = "object";
|
||||
if (util.isArray(msg.msg)) {
|
||||
msg.format = "array";
|
||||
msg.format = msg.msg.constructor.name || "Object";
|
||||
var isArray = util.isArray(msg.msg);
|
||||
if (isArray) {
|
||||
msg.format = "array ["+msg.msg.length+"]";
|
||||
}
|
||||
if (isArray || (msg.format === "Object")) {
|
||||
msg.msg = JSON.stringify(msg.msg, function(key, value) {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (seen.indexOf(value) !== -1) { return "[circular]"; }
|
||||
seen.push(value);
|
||||
}
|
||||
return value;
|
||||
}," ");
|
||||
} else {
|
||||
try { msg.msg = msg.msg.toString(); }
|
||||
catch(e) { msg.msg = "[Type not printable]"; }
|
||||
}
|
||||
msg.msg = JSON.stringify(msg.msg, function(key, value) {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (seen.indexOf(value) !== -1) { return "[circular]"; }
|
||||
seen.push(value);
|
||||
}
|
||||
return value;
|
||||
}," ");
|
||||
seen = null;
|
||||
} else if (typeof msg.msg === "boolean") {
|
||||
msg.format = "boolean";
|
||||
@@ -111,7 +117,7 @@ module.exports = function(RED) {
|
||||
msg.format = (msg.msg === null)?"null":"undefined";
|
||||
msg.msg = "(undefined)";
|
||||
} else {
|
||||
msg.format = "string";
|
||||
msg.format = "string ["+msg.msg.length+"]";
|
||||
msg.msg = msg.msg;
|
||||
}
|
||||
|
||||
@@ -136,15 +142,15 @@ module.exports = function(RED) {
|
||||
if (node !== null && typeof node !== "undefined" ) {
|
||||
if (state === "enable") {
|
||||
node.active = true;
|
||||
res.send(200);
|
||||
res.sendStatus(200);
|
||||
} else if (state === "disable") {
|
||||
node.active = false;
|
||||
res.send(201);
|
||||
res.sendStatus(201);
|
||||
} else {
|
||||
res.send(404);
|
||||
res.sendStatus(404);
|
||||
}
|
||||
} else {
|
||||
res.send(404);
|
||||
res.sendStatus(404);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@@ -37,17 +37,17 @@
|
||||
<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-tips" id="spawnTip"><span data-i18n="[html]exec.tip"></span></div>
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="exec">
|
||||
<p>Calls out to a system command.<br/></p>
|
||||
<p>Provides 3 outputs... stdout, stderr, and return code.</p>
|
||||
<p>By default uses exec() which calls the command, blocks while waiting for completion, and then returns the complete result in one go, along with any errors.</p>
|
||||
<p>Optionally can use spawn() instead, which returns output from stdout and stderr as the command runs (ie one line at a time). On completion it then returns a return code (on the 3rd output).</p>
|
||||
<p>Spawn only expects one command word, with all extra parameters to be comma separated and passed as the append.</p>
|
||||
<p>By default uses exec() which calls the command, then gets a callback on completion, returning the complete result in one message, along with any errors.</p>
|
||||
<p>Optionally can use spawn() instead, which returns the output from stdout and stderr as the command runs (usually one line at a time). On completion it then returns a return code (on the 3rd output).</p>
|
||||
<p>The optional append gets added to the command after the <b>msg.payload</b> - so you can do things like pipe the result to another command.</p>
|
||||
<p>Parameters with spaces should be enclosed in quotes - <i>"This is a single parameter"</i></p>
|
||||
<p>If stdout is binary a <i>buffer</i> is returned - otherwise returns a <i>string</i>.</p>
|
||||
<p>The blue status icon will be visible while the node is active.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -70,15 +70,6 @@
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
$("#node-input-useSpawn").on("change",function() {
|
||||
if ($("#node-input-useSpawn").is(':checked')) {
|
||||
$("#spawnTip").show();
|
||||
} else {
|
||||
$("#spawnTip").hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@@ -30,17 +30,18 @@ module.exports = function(RED) {
|
||||
|
||||
var node = this;
|
||||
this.on("input", function(msg) {
|
||||
node.status({fill:"blue",shape:"dot"});
|
||||
node.status({fill:"blue",shape:"dot",text:" "});
|
||||
if (this.useSpawn === true) {
|
||||
// make the extra args into an array
|
||||
// then prepend with the msg.payload
|
||||
if (typeof(msg.payload !== "string")) { msg.payload = (msg.payload || "").toString(); }
|
||||
var arg = [];
|
||||
if (node.append.length > 0) { arg = node.append.split(","); }
|
||||
if ((node.addpay === true) && (msg.payload.toString().trim() !== "")) { arg.unshift(msg.payload); }
|
||||
if (RED.settings.verbose) { node.log(node.cmd+" ["+arg+"]"); }
|
||||
if (node.cmd.indexOf(" ") == -1) {
|
||||
var ex = spawn(node.cmd,arg);
|
||||
|
||||
var arg = node.cmd+" "+msg.payload+" "+node.append;
|
||||
// slice whole line by spaces (trying to honour quotes);
|
||||
arg = arg.match(/(?:[^\s"]+|"[^"]*")+/g);
|
||||
var cmd = arg.shift();
|
||||
if (RED.settings.verbose) { node.log(cmd+" ["+arg+"]"); }
|
||||
if (cmd.indexOf(" ") == -1) {
|
||||
var ex = spawn(cmd,arg);
|
||||
ex.stdout.on('data', function (data) {
|
||||
//console.log('[exec] stdout: ' + data);
|
||||
if (isUtf8(data)) { msg.payload = data.toString(); }
|
||||
|
@@ -57,11 +57,14 @@ module.exports = function(RED) {
|
||||
"error:__node__.error,"+
|
||||
"warn:__node__.warn,"+
|
||||
"on:__node__.on,"+
|
||||
"status:__node__.status,"+
|
||||
"send:function(msgs){ __node__.send(__msgid__,msgs);}"+
|
||||
"};\n"+
|
||||
this.func+"\n"+
|
||||
"})(msg);";
|
||||
this.topic = n.topic;
|
||||
this.outstandingTimers = [];
|
||||
this.outstandingIntervals = [];
|
||||
var sandbox = {
|
||||
console:console,
|
||||
util:util,
|
||||
@@ -70,24 +73,50 @@ module.exports = function(RED) {
|
||||
log: function() {
|
||||
node.log.apply(node, arguments);
|
||||
},
|
||||
error: function(){
|
||||
error: function() {
|
||||
node.error.apply(node, arguments);
|
||||
},
|
||||
warn: function() {
|
||||
node.warn.apply(node, arguments);
|
||||
},
|
||||
send: function(id,msgs) {
|
||||
sendResults(node,id,msgs);
|
||||
send: function(id, msgs) {
|
||||
sendResults(node, id, msgs);
|
||||
},
|
||||
on: function() {
|
||||
node.on.apply(node,arguments);
|
||||
node.on.apply(node, arguments);
|
||||
},
|
||||
status: function() {
|
||||
node.status.apply(node, arguments);
|
||||
}
|
||||
},
|
||||
context: {
|
||||
global:RED.settings.functionGlobalContext || {}
|
||||
},
|
||||
setTimeout: setTimeout,
|
||||
clearTimeout: clearTimeout
|
||||
setTimeout: function (func,delay) {
|
||||
var timerId = setTimeout(function() {
|
||||
sandbox.clearTimeout(timerId);
|
||||
func();
|
||||
},delay);
|
||||
node.outstandingTimers.push(timerId);
|
||||
},
|
||||
clearTimeout: function(id) {
|
||||
clearTimeout(id);
|
||||
var index = node.outstandingTimers.indexOf(id);
|
||||
if (index > -1) {
|
||||
node.outstandingTimers.splice(index,1);
|
||||
}
|
||||
},
|
||||
setInterval: function(func,delay) {
|
||||
var timerId = setInterval(func,delay);
|
||||
node.outstandingIntervals.push(timerId);
|
||||
},
|
||||
clearInterval: function(id) {
|
||||
clearInterval(id);
|
||||
var index = node.outstandingIntervals.indexOf(id);
|
||||
if (index > -1) {
|
||||
node.outstandingIntervals.splice(index,1);
|
||||
}
|
||||
}
|
||||
};
|
||||
var context = vm.createContext(sandbox);
|
||||
try {
|
||||
@@ -100,7 +129,7 @@ module.exports = function(RED) {
|
||||
sendResults(this,msg._msgid,context.results);
|
||||
|
||||
var duration = process.hrtime(start);
|
||||
var converted = Math.floor((duration[0]* 1e9 + duration[1])/10000)/100;
|
||||
var converted = Math.floor((duration[0] * 1e9 + duration[1])/10000)/100;
|
||||
this.metric("duration", msg, converted);
|
||||
if (process.env.NODE_RED_FUNCTION_TIME) {
|
||||
this.status({fill:"yellow",shape:"dot",text:""+converted});
|
||||
@@ -111,7 +140,7 @@ module.exports = function(RED) {
|
||||
var errorMessage;
|
||||
var stack = err.stack.split(/\r?\n/);
|
||||
if (stack.length > 0) {
|
||||
while(line < stack.length && stack[line].indexOf("ReferenceError") !== 0) {
|
||||
while (line < stack.length && stack[line].indexOf("ReferenceError") !== 0) {
|
||||
line++;
|
||||
}
|
||||
|
||||
@@ -119,9 +148,9 @@ module.exports = function(RED) {
|
||||
errorMessage = stack[line];
|
||||
var m = /:(\d+):(\d+)$/.exec(stack[line+1]);
|
||||
if (m) {
|
||||
var line = Number(m[1])-1;
|
||||
var lineno = Number(m[1])-1;
|
||||
var cha = m[2];
|
||||
errorMessage += " (line "+line+", col "+cha+")";
|
||||
errorMessage += " (line "+lineno+", col "+cha+")";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,6 +160,14 @@ module.exports = function(RED) {
|
||||
this.error(errorMessage, msg);
|
||||
}
|
||||
});
|
||||
this.on("close", function() {
|
||||
while(node.outstandingTimers.length > 0) {
|
||||
clearTimeout(node.outstandingTimers.pop())
|
||||
}
|
||||
while(node.outstandingIntervals.length > 0) {
|
||||
clearInterval(node.outstandingIntervals.pop())
|
||||
}
|
||||
})
|
||||
} catch(err) {
|
||||
// eg SyntaxError - which v8 doesn't include line number information
|
||||
// so we can't do better than this
|
||||
|
@@ -109,23 +109,23 @@
|
||||
if (this.pauseType == "delay") {
|
||||
var units = this.timeoutUnits ? this.timeoutUnits.charAt(0) : "s";
|
||||
if (this.timeoutUnits == "milliseconds") { units = "ms"; }
|
||||
return this.name||this._("delay.label.delay")+" "+this.timeout+" " + units;
|
||||
return this.name||this._("delay.label.delay")+" "+this.timeout+" "+units;
|
||||
} else if (this.pauseType == "rate") {
|
||||
var units = this.rateUnits ? this.rateUnits.charAt(0) : "s";
|
||||
return this.name||this._("delay.label.limit")+" "+this.rate+" msg/"+ units;
|
||||
return this.name||this._("delay.label.limit")+" "+this.rate+" msg/"+units;
|
||||
} else if (this.pauseType == "random") {
|
||||
return this.name || this._("delay.label.random");
|
||||
}
|
||||
else {
|
||||
var units = this.rateUnits ? this.rateUnits.charAt(0) : "s";
|
||||
return this.name || this._("delay.label.queue") +this.rate+" msg/"+ units;
|
||||
return this.name || this._("delay.label.queue")+" "+this.rate+" msg/"+units;
|
||||
}
|
||||
},
|
||||
labelStyle: function() { // sets the class to apply to the label
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
$( "#node-input-timeout" ).spinner({min:1,max:60});
|
||||
$( "#node-input-timeout" ).spinner({min:1});
|
||||
$( "#node-input-rate" ).spinner({min:1});
|
||||
|
||||
$( "#node-input-randomFirst" ).spinner({min:0});
|
||||
|
@@ -87,8 +87,8 @@ module.exports = function(RED) {
|
||||
node.send(msg);
|
||||
}, node.timeout);
|
||||
this.idList.push(id);
|
||||
if ((node.timeout > 1000) && (node.idList.length === 0)) {
|
||||
node.status({fill:"blue",shape:"dot"});
|
||||
if ((node.timeout > 1000) && (node.idList.length !== 0)) {
|
||||
node.status({fill:"blue",shape:"dot",text:" "});
|
||||
}
|
||||
});
|
||||
|
||||
|
@@ -74,6 +74,7 @@
|
||||
never will, and neither can the first be retriggered - so a true one shot.</p>
|
||||
<p>If a <b>msg.reset</b> property is present any timeout currently in progress
|
||||
will be cleared and the second output will not happen.</p>
|
||||
<p>The blue status icon will be visible while the node is active.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
@@ -54,6 +54,7 @@ module.exports = function(RED) {
|
||||
if (msg.hasOwnProperty("reset")) {
|
||||
clearTimeout(tout);
|
||||
tout = null;
|
||||
node.status({});
|
||||
}
|
||||
else {
|
||||
if (!tout) {
|
||||
@@ -64,14 +65,16 @@ module.exports = function(RED) {
|
||||
else if (node.op1Templated) { msg.payload = mustache.render(node.op1,msg); }
|
||||
else { msg.payload = node.op1; }
|
||||
if (node.op1type !== "nul") { node.send(msg); }
|
||||
if (node.duration === 0) { tout = "infinite"; }
|
||||
if (node.duration === 0) { tout = 0; }
|
||||
else {
|
||||
tout = setTimeout(function() {
|
||||
msg.payload = m2;
|
||||
if (node.op2type !== "nul") { node.send(msg); }
|
||||
tout = null;
|
||||
node.status({});
|
||||
},node.duration);
|
||||
}
|
||||
node.status({fill:"blue",shape:"dot",text:" "});
|
||||
}
|
||||
else if ((node.extend === "true" || node.extend === true) && (node.duration > 0)) {
|
||||
clearTimeout(tout);
|
||||
@@ -79,12 +82,16 @@ module.exports = function(RED) {
|
||||
msg.payload = m2;
|
||||
if (node.op2type !== "nul") { node.send(msg); }
|
||||
tout = null;
|
||||
node.status({});
|
||||
},node.duration);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.on("close", function() {
|
||||
if (tout) { clearTimeout(tout); }
|
||||
if (tout) {
|
||||
clearTimeout(tout);
|
||||
node.status({});
|
||||
}
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("trigger",TriggerNode);
|
||||
|
@@ -73,6 +73,7 @@
|
||||
var pinsInUse = {};
|
||||
RED.nodes.registerType('rpi-gpio in',{
|
||||
category: 'Raspberry Pi',
|
||||
label: 'Raspberry Pi',
|
||||
color:"#c6dbef",
|
||||
defaults: {
|
||||
name: { value:"" },
|
||||
@@ -211,6 +212,7 @@
|
||||
var pinsInUse = {};
|
||||
RED.nodes.registerType('rpi-gpio out',{
|
||||
category: 'Raspberry Pi',
|
||||
label: 'Raspberry Pi',
|
||||
color:"#c6dbef",
|
||||
defaults: {
|
||||
name: { value:"" },
|
||||
@@ -330,9 +332,11 @@
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="rpi-mouse">
|
||||
<p>Raspberry Pi mouse button node. Generates a <b>msg.payload</b> with
|
||||
<p>Raspberry Pi mouse button node. Requires a USB mouse.</p>
|
||||
<p>Generates a <b>msg.payload</b> with
|
||||
either a 1 or 0 when the selected mouse button is pressed and released</p>
|
||||
<p>Also sets <b>msg.button</b> to the code value, 1 = left, 2 = right, 4 = middle,
|
||||
<p>Also sets <b>msg.button</b> to the code value
|
||||
<ul><li>1 = left</li><li>2 = right</li><li>4 = middle</li></ul>
|
||||
so you can work out which button or combination was pressed.</p>
|
||||
<p>And sets <b>msg.topic</b> to <i>pi/mouse</i>.</p>
|
||||
</script>
|
||||
@@ -340,6 +344,7 @@
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('rpi-mouse',{
|
||||
category: 'Raspberry Pi',
|
||||
label: 'Raspberry Pi',
|
||||
color:"#c6dbef",
|
||||
defaults: {
|
||||
name: { value:"" },
|
||||
|
@@ -1,3 +1,4 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright 2014 IBM Corp.
|
||||
#
|
||||
|
@@ -1,12 +1,9 @@
|
||||
<!--
|
||||
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.
|
||||
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.
|
||||
@@ -117,24 +114,99 @@
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-template-name="mqtt-broker">
|
||||
<div class="form-row node-input-broker">
|
||||
<label for="node-config-input-broker"><i class="fa fa-globe"></i> <span data-i18n="mqtt.label.broker"></span></label>
|
||||
<input class="input-append-left" type="text" id="node-config-input-broker" placeholder="localhost" style="width: 40%;" >
|
||||
<label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> <span data-i18n="mqtt.label.port"></span></label>
|
||||
<input type="text" id="node-config-input-port" data-i18n="[placeholder]mqtt.label.port" style="width:45px">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-clientid"><i class="fa fa-tag"></i> <span data-i18n="mqtt.label.clientid"></span></label>
|
||||
<input type="text" id="node-config-input-clientid" data-i18n="[placeholder]mqtt.placeholder.clientid">
|
||||
<ul style="background: #fff; min-width: 600px; margin-bottom: 20px;" id="node-config-mqtt-broker-tabs"></ul>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-user"><i class="fa fa-user"></i> <span data-i18n="common.label.username"></span></label>
|
||||
<input type="text" id="node-config-input-user">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-password"><i class="fa fa-lock"></i> <span data-i18n="common.label.password"></span></label>
|
||||
<input type="password" id="node-config-input-password">
|
||||
<div id="node-config-mqtt-broker-tabs-content" style="min-height: 170px;">
|
||||
<div id="mqtt-broker-tab-connection" style="display:none">
|
||||
<div class="form-row node-input-broker">
|
||||
<label for="node-config-input-broker"><i class="fa fa-globe"></i> <span data-i18n="mqtt.label.broker"></span></label>
|
||||
<input class="input-append-left" type="text" id="node-config-input-broker" placeholder="localhost" style="width: 40%;" >
|
||||
<label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> <span data-i18n="mqtt.label.port"></span></label>
|
||||
<input type="text" id="node-config-input-port" data-i18n="[placeholder]mqtt.label.port" style="width:45px">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-clientid"><i class="fa fa-tag"></i> <span data-i18n="mqtt.label.clientid"></span></label>
|
||||
<input type="text" id="node-config-input-clientid" data-i18n="[placeholder]mqtt.placeholder.clientid">
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-keepalive" style="width: auto"><i class="fa fa-clock-o"></i> <span data-i18n="mqtt.label.keepalive"></span></label>
|
||||
<input type="text" id="node-config-input-keepalive" style="width: 50px">
|
||||
<input type="checkbox" id="node-config-input-cleansession" style="margin-left: 30px; height: 1em;display: inline-block; width: auto; vertical-align: middle;">
|
||||
<label for="node-config-input-cleansession" style="width: auto;" data-i18n="mqtt.label.cleansession"></label>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input type="checkbox" id="node-config-input-compatmode" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-config-input-compatmode" style="width: auto;" data-i18n="mqtt.label.compatmode"></label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mqtt-broker-tab-security" style="display:none">
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-user"><i class="fa fa-user"></i> <span data-i18n="common.label.username"></span></label>
|
||||
<input type="text" id="node-config-input-user">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-password"><i class="fa fa-lock"></i> <span data-i18n="common.label.password"></span></label>
|
||||
<input type="password" id="node-config-input-password">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input type="checkbox" id="node-config-input-usetls" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-config-input-usetls" style="width: 70%;" data-i18n="mqtt.label.use-tls"></label>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input type="checkbox" id="node-config-input-verifyservercert" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-config-input-verifyservercert" style="width: 70%;" data-i18n="mqtt.label.verify-server-cert"></label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mqtt-broker-tab-birth" style="display:none">
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-birthTopic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
|
||||
<input type="text" id="node-config-input-birthTopic" data-i18n="[placeholder]mqtt.placeholder.birth-topic">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-birthQos"><i class="fa fa-empire"></i> <span data-i18n="mqtt.label.qos"></span></label>
|
||||
<select id="node-config-input-birthQos" style="width:125px !important">
|
||||
<option value="0">0</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
</select>
|
||||
<i class="fa fa-history"></i> <span data-i18n="mqtt.retain"></span> <select id="node-config-input-birthRetain" style="width:125px !important">
|
||||
<option value="false" data-i18n="mqtt.false"></option>
|
||||
<option value="true" data-i18n="mqtt.true"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-birthPayload"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload"></span></label>
|
||||
<input type="text" id="node-config-input-birthPayload" data-i18n="[placeholder]common.label.payload">
|
||||
</div>
|
||||
</div>
|
||||
<div id="mqtt-broker-tab-will" style="display:none">
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-willTopic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
|
||||
<input type="text" id="node-config-input-willTopic" data-i18n="[placeholder]mqtt.placeholder.will-topic">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-willQos"><i class="fa fa-empire"></i> <span data-i18n="mqtt.label.qos"></span></label>
|
||||
<select id="node-config-input-willQos" style="width:125px !important">
|
||||
<option value="0">0</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
</select>
|
||||
<i class="fa fa-history"></i> <span data-i18n="mqtt.retain"></span> <select id="node-config-input-willRetain" style="width:125px !important">
|
||||
<option value="false" data-i18n="mqtt.false"></option>
|
||||
<option value="true" data-i18n="mqtt.true"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-willPayload"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload"></span></label>
|
||||
<input type="text" id="node-config-input-willPayload" data-i18n="[placeholder]common.label.payload">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -143,7 +215,27 @@
|
||||
defaults: {
|
||||
broker: {value:"",required:true},
|
||||
port: {value:1883,required:true,validate:RED.validators.number()},
|
||||
clientid: { value:"" }
|
||||
clientid: { value:"", validate: function(v) {
|
||||
if ($("#node-config-input-clientid").length) {
|
||||
// Currently editing the node
|
||||
return $("#node-config-input-cleansession").is(":checked") || v.length > 0;
|
||||
} else {
|
||||
return this.cleansession || v.length > 0;
|
||||
}
|
||||
}},
|
||||
usetls: {value: false},
|
||||
verifyservercert: { value: false},
|
||||
compatmode: { value: true},
|
||||
keepalive: {value:15,validate:RED.validators.number()},
|
||||
cleansession: {value: true},
|
||||
willTopic: {value:""},
|
||||
willQos: {value:"0"},
|
||||
willRetain: {value:false},
|
||||
willPayload: {value:""},
|
||||
birthTopic: {value:""},
|
||||
birthQos: {value:"0"},
|
||||
birthRetain: {value:false},
|
||||
birthPayload: {value:""}
|
||||
},
|
||||
credentials: {
|
||||
user: {type:"text"},
|
||||
@@ -152,6 +244,87 @@
|
||||
label: function() {
|
||||
if (this.broker == "") { this.broker = "localhost"; }
|
||||
return (this.clientid?this.clientid+"@":"")+this.broker+":"+this.port;
|
||||
},
|
||||
oneditprepare: function () {
|
||||
var tabs = RED.tabs.create({
|
||||
id: "node-config-mqtt-broker-tabs",
|
||||
onchange: function(tab) {
|
||||
$("#node-config-mqtt-broker-tabs-content").children().hide();
|
||||
$("#" + tab.id).show();
|
||||
}
|
||||
});
|
||||
tabs.addTab({
|
||||
id: "mqtt-broker-tab-connection",
|
||||
label: this._("mqtt.tabs-label.connection")
|
||||
});
|
||||
tabs.addTab({
|
||||
id: "mqtt-broker-tab-security",
|
||||
label: this._("mqtt.tabs-label.security")
|
||||
});
|
||||
tabs.addTab({
|
||||
id: "mqtt-broker-tab-birth",
|
||||
label: this._("mqtt.tabs-label.birth")
|
||||
});
|
||||
tabs.addTab({
|
||||
id: "mqtt-broker-tab-will",
|
||||
label: this._("mqtt.tabs-label.will")
|
||||
});
|
||||
setTimeout(function() { tabs.resize()},0);
|
||||
if (typeof this.cleansession === 'undefined') {
|
||||
this.cleansession = true;
|
||||
$("#node-config-input-cleansession").prop("checked",true);
|
||||
}
|
||||
if (typeof this.usetls === 'undefined'){
|
||||
this.usetls = false;
|
||||
$("#node-config-input-usetls").prop("checked",false);
|
||||
}
|
||||
if (typeof this.verifyservercert === 'undefined'){
|
||||
this.verifyservercert = true;
|
||||
$("#node-config-input-verifyservercert").prop("checked",true);
|
||||
}
|
||||
if (typeof this.compatmode === 'undefined'){
|
||||
this.compatmode = true;
|
||||
$("#node-config-input-compatmode").prop('checked', true);
|
||||
}
|
||||
if (typeof this.keepalive === 'undefined'){
|
||||
this.keepalive = 15;
|
||||
$("#node-config-input-keepalive").val(this.keepalive);
|
||||
}
|
||||
if (typeof this.willQos === 'undefined') {
|
||||
this.willQos = "0";
|
||||
$("#node-config-input-willQos").val("0");
|
||||
}
|
||||
if (typeof this.birthQos === 'undefined') {
|
||||
this.birthQos = "0";
|
||||
$("#node-config-input-birthQos").val("0");
|
||||
}
|
||||
|
||||
function updateTLSOptions() {
|
||||
if ($("#node-config-input-usetls").is(':checked')) {
|
||||
$("#node-config-input-verifyservercert").prop("disabled", false);
|
||||
$("#node-config-input-verifyservercert").next().css("color","");
|
||||
} else {
|
||||
$("#node-config-input-verifyservercert").prop("disabled", true);
|
||||
$("#node-config-input-verifyservercert").next().css("color","#aaa");
|
||||
}
|
||||
}
|
||||
updateTLSOptions();
|
||||
$("#node-config-input-usetls").on("click",function() {
|
||||
updateTLSOptions();
|
||||
});
|
||||
var node = this;
|
||||
function updateClientId() {
|
||||
if ($("#node-config-input-cleansession").is(":checked")) {
|
||||
$("#node-config-input-clientid").attr("placeholder",node._("mqtt.placeholder.clientid"));
|
||||
} else {
|
||||
$("#node-config-input-clientid").attr("placeholder",node._("mqtt.placeholder.clientid-nonclean"));
|
||||
}
|
||||
$("#node-config-input-clientid").change();
|
||||
}
|
||||
setTimeout(updateClientId,0);
|
||||
$("#node-config-input-cleansession").on("click",function() {
|
||||
updateClientId();
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@@ -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.
|
||||
@@ -16,19 +16,268 @@
|
||||
|
||||
module.exports = function(RED) {
|
||||
"use strict";
|
||||
var connectionPool = require("./lib/mqttConnectionPool");
|
||||
var mqtt = require("mqtt");
|
||||
var util = require("util");
|
||||
var events = require("events");
|
||||
var isUtf8 = require('is-utf8');
|
||||
|
||||
function matchTopic(ts,t) {
|
||||
if (ts == "#") {
|
||||
return true;
|
||||
}
|
||||
var re = new RegExp("^"+ts.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
|
||||
return re.test(t);
|
||||
}
|
||||
|
||||
function MQTTBrokerNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
|
||||
// Configuration options passed by Node Red
|
||||
this.broker = n.broker;
|
||||
this.port = n.port;
|
||||
this.clientid = n.clientid;
|
||||
this.usetls = n.usetls;
|
||||
this.verifyservercert = n.verifyservercert;
|
||||
this.compatmode = n.compatmode;
|
||||
this.keepalive = n.keepalive;
|
||||
this.cleansession = n.cleansession;
|
||||
|
||||
// Config node state
|
||||
this.brokerurl = "";
|
||||
this.connected = false;
|
||||
this.connecting = false;
|
||||
this.usecount = 0;
|
||||
this.options = {};
|
||||
this.queue = [];
|
||||
this.subscriptions = {};
|
||||
|
||||
if (n.birthTopic) {
|
||||
this.birthMessage = {
|
||||
topic: n.birthTopic,
|
||||
payload: n.birthPayload || "",
|
||||
qos: Number(n.birthQos||0),
|
||||
retain: n.birthRetain=="true"|| n.birthRetain===true
|
||||
};
|
||||
}
|
||||
events.EventEmitter.call(this);
|
||||
this.setMaxListeners(0);
|
||||
|
||||
if (this.credentials) {
|
||||
this.username = this.credentials.user;
|
||||
this.password = this.credentials.password;
|
||||
}
|
||||
|
||||
// If the config node is missing certain options (it was probably deployed prior to an update to the node code),
|
||||
// select/generate sensible options for the new fields
|
||||
if (typeof this.usetls === 'undefined'){
|
||||
this.usetls = false;
|
||||
}
|
||||
if (typeof this.compatmode === 'undefined'){
|
||||
this.compatmode = true;
|
||||
}
|
||||
if (typeof this.verifyservercert === 'undefined'){
|
||||
this.verifyservercert = false;
|
||||
}
|
||||
if (typeof this.keepalive === 'undefined'){
|
||||
this.keepalive = 15;
|
||||
} else if (typeof this.keepalive === 'string') {
|
||||
this.keepalive = Number(this.keepalive);
|
||||
}
|
||||
if (typeof this.cleansession === 'undefined') {
|
||||
this.cleansession = true;
|
||||
}
|
||||
|
||||
// Create the URL to pass in to the MQTT.js library
|
||||
if (this.brokerurl == "") {
|
||||
if (this.usetls) {
|
||||
this.brokerurl="mqtts://";
|
||||
} else {
|
||||
this.brokerurl="mqtt://";
|
||||
}
|
||||
if (this.broker != "") {
|
||||
this.brokerurl = this.brokerurl+this.broker+":"+this.port;
|
||||
} else {
|
||||
this.brokerurl = this.brokerurl+"localhost:1883";
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.cleansession && !this.clientid) {
|
||||
this.cleansession = true;
|
||||
this.warn(RED._("mqtt.errors.nonclean-missingclientid"));
|
||||
}
|
||||
|
||||
// Build options for passing to the MQTT.js API
|
||||
this.options.clientId = this.clientid || 'mqtt_' + (1+Math.random()*4294967295).toString(16);
|
||||
this.options.username = this.username;
|
||||
this.options.password = this.password;
|
||||
this.options.keepalive = this.keepalive;
|
||||
this.options.clean = this.cleansession;
|
||||
this.options.reconnectPeriod = RED.settings.mqttReconnectTime||5000;
|
||||
if (this.compatmode == "true" || this.compatmode === true){
|
||||
this.options.protocolId = 'MQIsdp';
|
||||
this.options.protocolVersion = 3;
|
||||
}
|
||||
|
||||
this.options.rejectUnauthorized = (this.verifyservercert == "true" || this.verifyservercert === true)
|
||||
|
||||
if (n.willTopic) {
|
||||
this.options.will = {
|
||||
topic: n.willTopic,
|
||||
payload: n.willPayload || "",
|
||||
qos: Number(n.willQos||0),
|
||||
retain: n.willRetain=="true"|| n.willRetain===true
|
||||
};
|
||||
}
|
||||
|
||||
// Define functions called by MQTT in and out nodes
|
||||
var node = this;
|
||||
this.register = function(){
|
||||
node.usecount += 1;
|
||||
};
|
||||
|
||||
this.deregister = function(){
|
||||
node.usecount -= 1;
|
||||
if (node.usecount == 0) {
|
||||
node.client.end();
|
||||
}
|
||||
};
|
||||
|
||||
this.connect = function () {
|
||||
if (!node.connected && !node.connecting) {
|
||||
node.connecting = true;
|
||||
node.client = mqtt.connect(node.brokerurl ,node.options);
|
||||
// Register successful connect or reconnect handler
|
||||
node.client.on('connect', function () {
|
||||
node.connected = true;
|
||||
node.log(RED._("mqtt.state.connected",{broker:(node.clientid?node.clientid+"@":"")+node.brokerurl}));
|
||||
node.emit('connected');
|
||||
|
||||
// Remove any existing listeners before resubscribing to avoid duplicates in the event of a re-connection
|
||||
node.client.removeAllListeners('message');
|
||||
|
||||
// Re-subscribe to stored topics
|
||||
for (var s in node.subscriptions) {
|
||||
var topic = s;
|
||||
var qos = 0;
|
||||
for (var r in node.subscriptions[s]) {
|
||||
qos = Math.max(qos,node.subscriptions[s][r].qos);
|
||||
node.client.on('message',node.subscriptions[s][r].handler);
|
||||
}
|
||||
var options = {qos: qos};
|
||||
node.client.subscribe(topic, options);
|
||||
}
|
||||
|
||||
// Send any birth message
|
||||
if (node.birthMessage) {
|
||||
node.publish(node.birthMessage);
|
||||
}
|
||||
|
||||
// Send any queued messages
|
||||
while(node.queue.length) {
|
||||
var msg = node.queue.shift();
|
||||
//console.log(msg);
|
||||
node.publish(msg);
|
||||
}
|
||||
});
|
||||
|
||||
// Register disconnect handlers
|
||||
node.client.on('close', function () {
|
||||
if (node.connected) {
|
||||
node.connected = false;
|
||||
node.log(RED._("mqtt.state.disconnected",{broker:(node.clientid?node.clientid+"@":"")+node.brokerurl}));
|
||||
node.emit('disconnected');
|
||||
} else if (node.connecting) {
|
||||
node.log(RED._("mqtt.state.connect-failed",{broker:(node.clientid?node.clientid+"@":"")+node.brokerurl}));
|
||||
}
|
||||
});
|
||||
|
||||
// Register connect error handler
|
||||
node.client.on('error', function (error) {
|
||||
console.log("ERROR",error);
|
||||
if (node.connecting) {
|
||||
node.client.end();
|
||||
node.connecting = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.subscribe = function (topic,qos,callback,ref) {
|
||||
ref = ref||0;
|
||||
node.subscriptions[topic] = node.subscriptions[topic]||{};
|
||||
var sub = {
|
||||
topic:topic,
|
||||
qos:qos,
|
||||
handler:function(mtopic,mpayload, mpacket) {
|
||||
if (matchTopic(topic,mtopic)) {
|
||||
callback(mtopic,mpayload, mpacket);
|
||||
}
|
||||
},
|
||||
ref: ref
|
||||
};
|
||||
node.subscriptions[topic][ref] = sub;
|
||||
if (node.connected) {
|
||||
node.client.on('message',sub.handler);
|
||||
var options = {};
|
||||
options.qos = qos;
|
||||
node.client.subscribe(topic, options);
|
||||
}
|
||||
};
|
||||
|
||||
this.unsubscribe = function (topic, ref) {
|
||||
ref = ref||0;
|
||||
var sub = node.subscriptions[topic];
|
||||
if (sub) {
|
||||
if (sub[ref]) {
|
||||
node.client.removeListener('message',sub[ref].handler);
|
||||
delete sub[ref];
|
||||
}
|
||||
if (Object.keys(sub).length == 0) {
|
||||
delete node.subscriptions[topic];
|
||||
if (node.connected){
|
||||
node.client.unsubscribe(topic);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.publish = function (msg) {
|
||||
if (node.connected) {
|
||||
if (!Buffer.isBuffer(msg.payload)) {
|
||||
if (typeof msg.payload === "object") {
|
||||
msg.payload = JSON.stringify(msg.payload);
|
||||
} else if (typeof msg.payload !== "string") {
|
||||
msg.payload = "" + msg.payload;
|
||||
}
|
||||
}
|
||||
|
||||
var options = {
|
||||
qos: msg.qos || 0,
|
||||
retain: msg.retain || false
|
||||
};
|
||||
node.client.publish(msg.topic, msg.payload, options, function (err){return});
|
||||
} else {
|
||||
if (!node.connecting) {
|
||||
node.connect();
|
||||
}
|
||||
node.queue.push(msg);
|
||||
}
|
||||
};
|
||||
|
||||
this.on('close', function(closecomplete) {
|
||||
if (this.connected) {
|
||||
this.on('disconnected', function() {
|
||||
closecomplete();
|
||||
});
|
||||
this.client.end();
|
||||
} else {
|
||||
closecomplete();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
util.inherits(MQTTBrokerNode, events.EventEmitter);
|
||||
|
||||
RED.nodes.registerType("mqtt-broker",MQTTBrokerNode,{
|
||||
credentials: {
|
||||
user: {type:"text"},
|
||||
@@ -40,30 +289,30 @@ module.exports = function(RED) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.topic = n.topic;
|
||||
this.broker = n.broker;
|
||||
this.brokerConfig = RED.nodes.getNode(this.broker);
|
||||
if (this.brokerConfig) {
|
||||
this.brokerConn = RED.nodes.getNode(this.broker);
|
||||
if (this.brokerConn) {
|
||||
this.status({fill:"red",shape:"ring",text:"common.status.disconnected"});
|
||||
this.client = connectionPool.get(this.brokerConfig.broker,this.brokerConfig.port,this.brokerConfig.clientid,this.brokerConfig.username,this.brokerConfig.password);
|
||||
var node = this;
|
||||
node.brokerConn.register();
|
||||
if (this.topic) {
|
||||
this.client.subscribe(this.topic,2,function(topic,payload,qos,retain) {
|
||||
this.brokerConn.subscribe(this.topic,2,function(topic,payload,packet) {
|
||||
if (isUtf8(payload)) { payload = payload.toString(); }
|
||||
var msg = {topic:topic,payload:payload,qos:qos,retain:retain};
|
||||
if ((node.brokerConfig.broker === "localhost")||(node.brokerConfig.broker === "127.0.0.1")) {
|
||||
var msg = {topic:topic,payload:payload, qos: packet.qos, retain: packet.retain};
|
||||
if ((node.brokerConn.broker === "localhost")||(node.brokerConn.broker === "127.0.0.1")) {
|
||||
msg._topic = topic;
|
||||
}
|
||||
node.send(msg);
|
||||
}, this.id);
|
||||
this.client.on("connectionlost",function() {
|
||||
this.brokerConn.on("disconnected",function() {
|
||||
node.status({fill:"red",shape:"ring",text:"common.status.disconnected"});
|
||||
});
|
||||
this.client.on("connect",function() {
|
||||
this.brokerConn.on("connected",function() {
|
||||
node.status({fill:"green",shape:"dot",text:"common.status.connected"});
|
||||
});
|
||||
if (this.client.isConnected()) {
|
||||
if (this.brokerConn.connected) {
|
||||
node.status({fill:"green",shape:"dot",text:"common.status.connected"});
|
||||
} else {
|
||||
this.client.connect();
|
||||
this.brokerConn.connect();
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -73,9 +322,9 @@ module.exports = function(RED) {
|
||||
this.error(RED._("mqtt.errors.missing-config"));
|
||||
}
|
||||
this.on('close', function() {
|
||||
if (this.client) {
|
||||
this.client.unsubscribe(this.topic,this.id);
|
||||
this.client.disconnect();
|
||||
if (this.brokerConn) {
|
||||
this.brokerConn.unsubscribe(this.topic,this.id);
|
||||
node.brokerConn.deregister();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -87,12 +336,12 @@ module.exports = function(RED) {
|
||||
this.qos = n.qos || null;
|
||||
this.retain = n.retain;
|
||||
this.broker = n.broker;
|
||||
this.brokerConfig = RED.nodes.getNode(this.broker);
|
||||
this.brokerConn = RED.nodes.getNode(this.broker);
|
||||
|
||||
if (this.brokerConfig) {
|
||||
if (this.brokerConn) {
|
||||
this.status({fill:"red",shape:"ring",text:"common.status.disconnected"});
|
||||
this.client = connectionPool.get(this.brokerConfig.broker,this.brokerConfig.port,this.brokerConfig.clientid,this.brokerConfig.username,this.brokerConfig.password);
|
||||
var node = this;
|
||||
node.brokerConn.register();
|
||||
this.on("input",function(msg) {
|
||||
if (msg.qos) {
|
||||
msg.qos = parseInt(msg.qos);
|
||||
@@ -108,30 +357,28 @@ module.exports = function(RED) {
|
||||
}
|
||||
if ( msg.hasOwnProperty("payload")) {
|
||||
if (msg.hasOwnProperty("topic") && (typeof msg.topic === "string") && (msg.topic !== "")) { // topic must exist
|
||||
this.client.publish(msg); // send the message
|
||||
this.brokerConn.publish(msg); // send the message
|
||||
}
|
||||
else { node.warn(RED._("mqtt.errors.invalid-topic")); }
|
||||
}
|
||||
});
|
||||
this.client.on("connectionlost",function() {
|
||||
this.brokerConn.on("disconnected",function() {
|
||||
node.status({fill:"red",shape:"ring",text:"common.status.disconnected"});
|
||||
});
|
||||
this.client.on("connect",function() {
|
||||
this.brokerConn.on("connected",function() {
|
||||
node.status({fill:"green",shape:"dot",text:"common.status.connected"});
|
||||
});
|
||||
if (this.client.isConnected()) {
|
||||
if (this.brokerConn.connected) {
|
||||
node.status({fill:"green",shape:"dot",text:"common.status.connected"});
|
||||
} else {
|
||||
this.client.connect();
|
||||
this.brokerConn.connect();
|
||||
}
|
||||
} else {
|
||||
this.error(RED._("mqtt.errors.missing-config"));
|
||||
}
|
||||
this.on('close', function() {
|
||||
if (this.client) {
|
||||
this.client.disconnect();
|
||||
}
|
||||
node.brokerConn.deregister();
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("mqtt out",MQTTOutNode);
|
||||
}
|
||||
};
|
||||
|
@@ -33,7 +33,7 @@
|
||||
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
|
||||
</div>
|
||||
<div class="form-row row-swagger-doc">
|
||||
<label for="node-input-swaggerDoc"><i class="fa fa-tag"></i> <span data-i18n="httpin.label.doc"></span></label>
|
||||
<label for="node-input-swaggerDoc"><i class="fa fa-file-text-o"></i> <span data-i18n="httpin.label.doc"></span></label>
|
||||
<input type="text" id="node-input-swaggerDoc">
|
||||
</div>
|
||||
<div id="node-input-tip" class="form-tips"><span data-i18n="httpin.tip.in"></span><code><span id="node-input-path"></span></code>.</div>
|
||||
|
@@ -19,35 +19,155 @@ module.exports = function(RED) {
|
||||
var http = require("follow-redirects").http;
|
||||
var https = require("follow-redirects").https;
|
||||
var urllib = require("url");
|
||||
var express = require("express");
|
||||
var bodyParser = require("body-parser");
|
||||
var getBody = require('raw-body');
|
||||
var mustache = require("mustache");
|
||||
var querystring = require("querystring");
|
||||
var cors = require('cors');
|
||||
var jsonParser = express.json();
|
||||
var urlencParser = express.urlencoded();
|
||||
var jsonParser = bodyParser.json();
|
||||
var urlencParser = bodyParser.urlencoded({extended:true});
|
||||
var onHeaders = require('on-headers');
|
||||
var typer = require('media-typer');
|
||||
var isUtf8 = require('is-utf8');
|
||||
|
||||
function rawBodyParser(req, res, next) {
|
||||
if (req._body) { return next(); }
|
||||
req.body = "";
|
||||
req._body = true;
|
||||
|
||||
var isText = true;
|
||||
var checkUTF = false;
|
||||
|
||||
if (req.headers['content-type']) {
|
||||
var parsedType = typer.parse(req.headers['content-type'])
|
||||
if (parsedType.type === "text") {
|
||||
isText = true;
|
||||
} else if (parsedType.subtype === "xml" || parsedType.suffix === "xml") {
|
||||
isText = true;
|
||||
} else if (parsedType.type !== "application") {
|
||||
isText = false;
|
||||
} else if (parsedType.subtype !== "octet-stream") {
|
||||
checkUTF = true;
|
||||
}
|
||||
}
|
||||
|
||||
getBody(req, {
|
||||
limit: '1mb',
|
||||
length: req.headers['content-length'],
|
||||
encoding: 'utf8'
|
||||
encoding: isText ? "utf8" : null
|
||||
}, function (err, buf) {
|
||||
if (err) { return next(err); }
|
||||
if (!isText && checkUTF && isUtf8(buf)) {
|
||||
buf = buf.toString()
|
||||
}
|
||||
|
||||
req.body = buf;
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
var corsSetup = false;
|
||||
|
||||
|
||||
function createRequestWrapper(node,req) {
|
||||
var wrapper = {
|
||||
_req: req
|
||||
};
|
||||
var toWrap = [
|
||||
"param",
|
||||
"get",
|
||||
"is",
|
||||
"acceptsCharset",
|
||||
"acceptsLanguage",
|
||||
"app",
|
||||
"baseUrl",
|
||||
"body",
|
||||
"cookies",
|
||||
"fresh",
|
||||
"hostname",
|
||||
"ip",
|
||||
"ips",
|
||||
"originalUrl",
|
||||
"params",
|
||||
"path",
|
||||
"protocol",
|
||||
"query",
|
||||
"route",
|
||||
"secure",
|
||||
"signedCookies",
|
||||
"stale",
|
||||
"subdomains",
|
||||
"xhr",
|
||||
"socket" // TODO: tidy this up
|
||||
];
|
||||
toWrap.forEach(function(f) {
|
||||
if (typeof req[f] === "function") {
|
||||
wrapper[f] = function() {
|
||||
node.warn(RED._("httpin.errors.deprecated-call",{method:"msg.req."+f}));
|
||||
var result = req[f].apply(req,arguments);
|
||||
if (result === res) {
|
||||
return wrapper;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
wrapper[f] = req[f];
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
function createResponseWrapper(node,res) {
|
||||
var wrapper = {
|
||||
_res: res
|
||||
};
|
||||
var toWrap = [
|
||||
"append",
|
||||
"attachment",
|
||||
"cookie",
|
||||
"clearCookie",
|
||||
"download",
|
||||
"end",
|
||||
"format",
|
||||
"get",
|
||||
"json",
|
||||
"jsonp",
|
||||
"links",
|
||||
"location",
|
||||
"redirect",
|
||||
"render",
|
||||
"send",
|
||||
"sendfile",
|
||||
"sendFile",
|
||||
"sendStatus",
|
||||
"set",
|
||||
"status",
|
||||
"type",
|
||||
"vary"
|
||||
];
|
||||
toWrap.forEach(function(f) {
|
||||
wrapper[f] = function() {
|
||||
node.warn(RED._("httpin.errors.deprecated-call",{method:"msg.res."+f}));
|
||||
var result = res[f].apply(res,arguments);
|
||||
if (result === res) {
|
||||
return wrapper;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
});
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
function HTTPIn(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
if (RED.settings.httpNodeRoot !== false) {
|
||||
|
||||
if (!n.url) {
|
||||
this.warn(RED._("httpin.errors.missing-path"));
|
||||
return;
|
||||
}
|
||||
this.url = n.url;
|
||||
this.method = n.method;
|
||||
this.swaggerDoc = n.swaggerDoc;
|
||||
@@ -56,26 +176,27 @@ module.exports = function(RED) {
|
||||
|
||||
this.errorHandler = function(err,req,res,next) {
|
||||
node.warn(err);
|
||||
res.send(500);
|
||||
res.sendStatus(500);
|
||||
};
|
||||
|
||||
this.callback = function(req,res) {
|
||||
var msgid = RED.util.generateId();
|
||||
res._msgid = msgid;
|
||||
if (node.method.match(/(^post$|^delete$|^put$|^options$)/)) {
|
||||
node.send({_msgid:msgid,req:req,res:res,payload:req.body});
|
||||
node.send({_msgid:msgid,req:req,res:createResponseWrapper(node,res),payload:req.body});
|
||||
} else if (node.method == "get") {
|
||||
node.send({_msgid:msgid,req:req,res:res,payload:req.query});
|
||||
node.send({_msgid:msgid,req:req,res:createResponseWrapper(node,res),payload:req.query});
|
||||
} else {
|
||||
node.send({_msgid:msgid,req:req,res:res});
|
||||
node.send({_msgid:msgid,req:req,res:createResponseWrapper(node,res)});
|
||||
}
|
||||
};
|
||||
|
||||
var corsHandler = function(req,res,next) { next(); }
|
||||
|
||||
if (RED.settings.httpNodeCors) {
|
||||
if (RED.settings.httpNodeCors && !corsSetup) {
|
||||
corsHandler = cors(RED.settings.httpNodeCors);
|
||||
RED.httpNode.options(this.url,corsHandler);
|
||||
RED.httpNode.options("*",corsHandler);
|
||||
corsSetup = true;
|
||||
}
|
||||
|
||||
var httpMiddleware = function(req,res,next) { next(); }
|
||||
@@ -117,24 +238,12 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
this.on("close",function() {
|
||||
var routes = RED.httpNode.routes[this.method];
|
||||
for (var i = 0; i<routes.length; i++) {
|
||||
if (routes[i].path == this.url) {
|
||||
var node = this;
|
||||
RED.httpNode._router.stack.forEach(function(route,i,routes) {
|
||||
if (route.route && route.route.path === node.url && route.route.methods[node.method]) {
|
||||
routes.splice(i,1);
|
||||
//break;
|
||||
}
|
||||
}
|
||||
if (RED.settings.httpNodeCors) {
|
||||
var routes = RED.httpNode.routes['options'];
|
||||
if (routes) {
|
||||
for (var j = 0; j<routes.length; j++) {
|
||||
if (routes[j].path == this.url) {
|
||||
routes.splice(j,1);
|
||||
//break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.warn(RED._("httpin.errors.not-created"));
|
||||
@@ -149,13 +258,13 @@ module.exports = function(RED) {
|
||||
this.on("input",function(msg) {
|
||||
if (msg.res) {
|
||||
if (msg.headers) {
|
||||
msg.res.set(msg.headers);
|
||||
msg.res._res.set(msg.headers);
|
||||
}
|
||||
var statusCode = msg.statusCode || 200;
|
||||
if (typeof msg.payload == "object" && !Buffer.isBuffer(msg.payload)) {
|
||||
msg.res.jsonp(statusCode,msg.payload);
|
||||
msg.res._res.status(statusCode).jsonp(msg.payload);
|
||||
} else {
|
||||
if (msg.res.get('content-length') == null) {
|
||||
if (msg.res._res.get('content-length') == null) {
|
||||
var len;
|
||||
if (msg.payload == null) {
|
||||
len = 0;
|
||||
@@ -166,10 +275,10 @@ module.exports = function(RED) {
|
||||
} else {
|
||||
len = Buffer.byteLength(msg.payload);
|
||||
}
|
||||
msg.res.set('content-length', len);
|
||||
msg.res._res.set('content-length', len);
|
||||
}
|
||||
|
||||
msg.res.send(statusCode,msg.payload);
|
||||
msg.res._res.status(statusCode).send(msg.payload);
|
||||
}
|
||||
} else {
|
||||
node.warn(RED._("httpin.errors.no-response"));
|
||||
@@ -214,7 +323,7 @@ module.exports = function(RED) {
|
||||
|
||||
var method = nodeMethod.toUpperCase() || "GET";
|
||||
if (msg.method && n.method && (n.method !== "use")) { // warn if override option not set
|
||||
node.warn(RED._("httpin.errors.not-overridden"));
|
||||
node.warn(RED._("common.errors.nooverride"));
|
||||
}
|
||||
if (msg.method && n.method && (n.method === "use")) {
|
||||
method = msg.method.toUpperCase(); // use the msg parameter
|
||||
@@ -282,6 +391,7 @@ module.exports = function(RED) {
|
||||
opts = urllib.parse(prox);
|
||||
opts.path = opts.pathname = path;
|
||||
opts.headers = heads;
|
||||
opts.method = method;
|
||||
//console.log(opts);
|
||||
urltotest = match[0];
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@
|
||||
module.exports = function(RED) {
|
||||
"use strict";
|
||||
var ws = require("ws");
|
||||
var inspect = require("sys").inspect;
|
||||
var inspect = require("util").inspect;
|
||||
|
||||
// A node red node that sets up a local websocket server
|
||||
function WebSocketListenerNode(n) {
|
||||
@@ -48,7 +48,7 @@ module.exports = function(RED) {
|
||||
if (!node.isServer) { node.emit('opened',''); }
|
||||
});
|
||||
socket.on('close',function() {
|
||||
if (node.isServer) { delete node._clients[id]; node.emit('opened',Object.keys(node._clients).length); }
|
||||
if (node.isServer) { delete node._clients[id]; node.emit('closed',Object.keys(node._clients).length); }
|
||||
else { node.emit('closed'); }
|
||||
if (!node.closing && !node.isServer) {
|
||||
node.tout = setTimeout(function(){ startconn(); }, 3000); // try to reconnect every 3 secs... bit fast ?
|
||||
@@ -115,7 +115,7 @@ module.exports = function(RED) {
|
||||
else {
|
||||
node.closing = true;
|
||||
node.server.close();
|
||||
if (node.tout) { clearTimeout(tout); }
|
||||
if (node.tout) { clearTimeout(node.tout); }
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -126,6 +126,14 @@ module.exports = function(RED) {
|
||||
this._inputNodes.push(handler);
|
||||
}
|
||||
|
||||
WebSocketListenerNode.prototype.removeInputNode = function(/*Node*/handler) {
|
||||
this._inputNodes.forEach(function(node, i, inputNodes) {
|
||||
if (node === handler) {
|
||||
inputNodes.splice(i, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
WebSocketListenerNode.prototype.handleEvent = function(id,/*socket*/socket,/*String*/event,/*Object*/data,/*Object*/flags){
|
||||
var msg;
|
||||
if (this.wholemsg) {
|
||||
@@ -187,6 +195,11 @@ module.exports = function(RED) {
|
||||
} else {
|
||||
this.error(RED._("websocket.errors.missing-conf"));
|
||||
}
|
||||
|
||||
this.on('close', function() {
|
||||
node.serverConfig.removeInputNode(node);
|
||||
});
|
||||
|
||||
}
|
||||
RED.nodes.registerType("websocket in",WebSocketInNode);
|
||||
|
||||
|
@@ -125,10 +125,12 @@ module.exports = function(RED) {
|
||||
});
|
||||
} else {
|
||||
var server = net.createServer(function (socket) {
|
||||
socket.setKeepAlive(true,120000);
|
||||
if (socketTimeout !== null) { socket.setTimeout(socketTimeout); }
|
||||
var id = (1+Math.random()*4294967295).toString(16);
|
||||
connectionPool[id] = socket;
|
||||
node.status({text:RED._("tcpin.status.connections",{count:++count})});
|
||||
count++;
|
||||
node.status({text:RED._("tcpin.status.connections",{count:count})});
|
||||
|
||||
var buffer = (node.datatype == 'buffer')? new Buffer(0):"";
|
||||
socket.on('data', function (data) {
|
||||
@@ -175,7 +177,8 @@ module.exports = function(RED) {
|
||||
});
|
||||
socket.on('close', function() {
|
||||
delete connectionPool[id];
|
||||
node.status({text:RED._("tcpin.status.connections",{count:--count})});
|
||||
count--;
|
||||
node.status({text:RED._("tcpin.status.connections",{count:count})});
|
||||
});
|
||||
socket.on('error',function(err) {
|
||||
node.log(err);
|
||||
@@ -303,11 +306,12 @@ module.exports = function(RED) {
|
||||
var connectedSockets = [];
|
||||
node.status({text:RED._("tcpin.status.connections",{count:0})});
|
||||
var server = net.createServer(function (socket) {
|
||||
socket.setKeepAlive(true,120000);
|
||||
if (socketTimeout !== null) { socket.setTimeout(socketTimeout); }
|
||||
var remoteDetails = socket.remoteAddress+":"+socket.remotePort;
|
||||
node.log(RED._("tcpin.status.connection-from",{host:socket.remoteAddress, port:socket.remotePort}));
|
||||
connectedSockets.push(socket);
|
||||
node.status({text:connectedSockets.length+" "+"tcpin.status.connections"});
|
||||
node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})});
|
||||
socket.on('timeout', function() {
|
||||
node.log(RED._("tcpin.errors.timeout",{port:node.port}));
|
||||
socket.end();
|
||||
@@ -393,7 +397,7 @@ module.exports = function(RED) {
|
||||
}
|
||||
if (!node.connected) {
|
||||
client = net.Socket();
|
||||
client.setTimeout(socketTimeout);
|
||||
if (socketTimeout) { client.setTimeout(socketTimeout); }
|
||||
//node.status({});
|
||||
var host = node.server || msg.host;
|
||||
var port = node.port || msg.port;
|
||||
@@ -432,7 +436,7 @@ module.exports = function(RED) {
|
||||
m = new Buffer(i+1);
|
||||
buf.copy(m,0,0,i+1);
|
||||
node.send({"payload":m});
|
||||
if (client) { client.end(); }
|
||||
//if (client) { client.end(); }
|
||||
}, node.splitc);
|
||||
i = 0;
|
||||
buf[0] = data[j];
|
||||
@@ -445,7 +449,8 @@ module.exports = function(RED) {
|
||||
if ( i >= node.splitc) {
|
||||
m = new Buffer(i);
|
||||
buf.copy(m,0,0,i);
|
||||
if (client) { client.end(); }
|
||||
node.send({"payload":m});
|
||||
//if (client) { client.end(); }
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
@@ -456,7 +461,8 @@ module.exports = function(RED) {
|
||||
if (data[j] == node.splitc) {
|
||||
m = new Buffer(i);
|
||||
buf.copy(m,0,0,i);
|
||||
if (client) { client.end(); }
|
||||
node.send({"payload":m});
|
||||
//if (client) { client.end(); }
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
@@ -472,6 +478,7 @@ module.exports = function(RED) {
|
||||
});
|
||||
|
||||
client.on('close', function() {
|
||||
node.status({});
|
||||
if (node.done) { node.done(); }
|
||||
});
|
||||
|
||||
@@ -501,7 +508,7 @@ module.exports = function(RED) {
|
||||
node.done = done;
|
||||
if (client) {
|
||||
buf = null;
|
||||
client.end();
|
||||
client.destroy();
|
||||
}
|
||||
if (!node.connected) { done(); }
|
||||
});
|
||||
|
@@ -16,7 +16,7 @@
|
||||
var util = require("util");
|
||||
var mqtt = require("mqtt");
|
||||
var events = require("events");
|
||||
//var inspect = require("sys").inspect;
|
||||
//var inspect = require("util").inspect;
|
||||
|
||||
//var Client = module.exports.Client = function(
|
||||
|
||||
|
@@ -68,7 +68,36 @@
|
||||
}
|
||||
},
|
||||
"catch": {
|
||||
"catch": "catch"
|
||||
"catch": "catch all",
|
||||
"catchNodes": "catch (__number__)",
|
||||
"label": {
|
||||
"source": "Catch errors from",
|
||||
"node": "node",
|
||||
"type": "type",
|
||||
"selectAll": "select all",
|
||||
"sortByLabel": "sort by label",
|
||||
"sortByType": "sort by type"
|
||||
},
|
||||
"scope": {
|
||||
"all": "all nodes",
|
||||
"selected": "selected nodes"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"status": "status (all)",
|
||||
"statusNodes": "status (__number__)",
|
||||
"label": {
|
||||
"source": "Report status from",
|
||||
"node": "node",
|
||||
"type": "type",
|
||||
"selectAll": "select all",
|
||||
"sortByLabel": "sort by label",
|
||||
"sortByType": "sort by type"
|
||||
},
|
||||
"scope": {
|
||||
"all": "all nodes",
|
||||
"selected": "selected nodes"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"output": "Output",
|
||||
@@ -182,13 +211,32 @@
|
||||
},
|
||||
"mqtt": {
|
||||
"label": {
|
||||
"broker": "Broker",
|
||||
"broker": "Server",
|
||||
"qos": "QoS",
|
||||
"clientid": "Client ID",
|
||||
"port": "Port"
|
||||
"port": "Port",
|
||||
"keepalive": "Keep alive time (s)",
|
||||
"cleansession": "Use clean session",
|
||||
"use-tls": "Enable secure (SSL/TLS) connection",
|
||||
"verify-server-cert":"Verify server certificate",
|
||||
"compatmode": "Use legacy MQTT 3.1 support"
|
||||
},
|
||||
"tabs-label": {
|
||||
"connection": "Connection",
|
||||
"security": "Security",
|
||||
"will": "Will Message",
|
||||
"birth": "Birth Message"
|
||||
},
|
||||
"placeholder": {
|
||||
"clientid": "Leave blank for auto generated"
|
||||
"clientid": "Leave blank for auto generated",
|
||||
"clientid-nonclean":"Must be set for non-clean sessions",
|
||||
"will-topic": "Leave blank to disable will message",
|
||||
"birth-topic": "Leave blank to disable birth message"
|
||||
},
|
||||
"state": {
|
||||
"connected": "Connected to broker: __broker__",
|
||||
"disconnected": "Disconnected from broker: __broker__",
|
||||
"connect-failed": "Connection failed to broker: __broker__"
|
||||
},
|
||||
"retain": "Retain",
|
||||
"true": "true",
|
||||
@@ -197,7 +245,8 @@
|
||||
"errors": {
|
||||
"not-defined": "topic not defined",
|
||||
"missing-config": "missing broker configuration",
|
||||
"invalid-topic": "Invalid topic specified"
|
||||
"invalid-topic": "Invalid topic specified",
|
||||
"nonclean-missingclientid": "No client ID set, using clean session"
|
||||
}
|
||||
},
|
||||
"httpin": {
|
||||
@@ -220,9 +269,11 @@
|
||||
"httpreq": "http request",
|
||||
"errors": {
|
||||
"not-created": "Cannot create http-in node when httpNodeRoot set to false",
|
||||
"missing-path": "missing path",
|
||||
"no-response": "No response object",
|
||||
"json-error": "JSON parse error",
|
||||
"no-url": "No url specified"
|
||||
"no-url": "No url specified",
|
||||
"deprecated-call":"Deprecated call to __method__"
|
||||
},
|
||||
"status": {
|
||||
"requesting": "requesting"
|
||||
@@ -374,8 +425,10 @@
|
||||
"property": "Property",
|
||||
"rule": "rule"
|
||||
},
|
||||
"and": "and",
|
||||
"checkall": "checking all rules",
|
||||
"stopfirst": "stopping after first match",
|
||||
"ignorecase": "ignore case",
|
||||
"rules": {
|
||||
"btwn":"is between",
|
||||
"cont":"contains",
|
||||
@@ -467,7 +520,8 @@
|
||||
"windows": "Windows (\\r\\n)"
|
||||
},
|
||||
"errors": {
|
||||
"csv_js": "This node only handles csv strings or js objects."
|
||||
"csv_js": "This node only handles csv strings or js objects.",
|
||||
"obj_csv": "No columns template specified for object -> csv."
|
||||
}
|
||||
},
|
||||
"html": {
|
||||
@@ -488,7 +542,8 @@
|
||||
"json": {
|
||||
"errors": {
|
||||
"dropped-object": "Ignored non-object payload",
|
||||
"dropped": "Ignored unsupported payload type"
|
||||
"dropped": "Ignored unsupported payload type",
|
||||
"dropped-error": "Failed to convert payload"
|
||||
}
|
||||
},
|
||||
"xml": {
|
||||
|
@@ -82,10 +82,42 @@
|
||||
{v:"else",t:this._("switch.rules.else")}
|
||||
];
|
||||
|
||||
var andLabel = this._("switch.and");
|
||||
var caseLabel = this._("switch.ignorecase");
|
||||
|
||||
function resizeRule(rule,width) {
|
||||
var selectField = rule.find("select");
|
||||
var type = selectField.children("option:selected").val();
|
||||
var valueField = rule.find(".node-input-rule-value");
|
||||
var btwnField1 = rule.find(".node-input-rule-btwn-value");
|
||||
var btwnField2 = rule.find(".node-input-rule-btwn-value2");
|
||||
var selectWidth;
|
||||
if (type.length < 4) {
|
||||
selectWidth = 60;
|
||||
} else if (type === "regex") {
|
||||
selectWidth = 147;
|
||||
} else {
|
||||
selectWidth = 120;
|
||||
}
|
||||
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);
|
||||
} else {
|
||||
if (type === "true" || type === "false" || type === "null" || type === "nnull" || type === "else") {
|
||||
// valueField.hide();
|
||||
} else {
|
||||
valueField.width(width-selectWidth-120);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function generateRule(i,rule) {
|
||||
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; text-align: right;"}).appendTo(container);
|
||||
var row2 = $('<div/>',{style:"padding-top: 5px; padding-left: 175px;"}).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);
|
||||
@@ -96,7 +128,7 @@
|
||||
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);
|
||||
btwnField.append(" and ");
|
||||
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 finalspan = $('<span/>',{style:"float: right;margin-right: 10px;"}).appendTo(row);
|
||||
@@ -105,15 +137,13 @@
|
||||
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);
|
||||
|
||||
var caseSensitive = $('<input/>',{id:"node-input-rule-case-"+i,class:"node-input-rule-case",type:"checkbox",style:"width:auto;vertical-align:top"}).appendTo(row2);
|
||||
$('<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();
|
||||
if (type.length < 4) {
|
||||
selectField.css({"width":"60px"});
|
||||
} else if (type === "regex") {
|
||||
selectField.css({"width":"147px"});
|
||||
} else {
|
||||
selectField.css({"width":"120px"});
|
||||
}
|
||||
|
||||
if (type === "btwn") {
|
||||
valueField.hide();
|
||||
btwnField.show();
|
||||
@@ -125,9 +155,13 @@
|
||||
valueField.show();
|
||||
}
|
||||
}
|
||||
if (type === "regex") {
|
||||
row2.show();
|
||||
} else {
|
||||
row2.hide();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
deleteButton.click(function() {
|
||||
container.css({"background":"#fee"});
|
||||
container.fadeOut(300, function() {
|
||||
@@ -148,6 +182,11 @@
|
||||
} else if (typeof rule.v != "undefined") {
|
||||
valueField.val(rule.v);
|
||||
}
|
||||
if (rule.case) {
|
||||
caseSensitive.prop('checked',true);
|
||||
} else {
|
||||
caseSensitive.prop('checked',false);
|
||||
}
|
||||
selectField.change();
|
||||
}
|
||||
|
||||
@@ -170,6 +209,12 @@
|
||||
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({
|
||||
@@ -192,6 +237,8 @@
|
||||
$("#dialog").dialog('option','width',size.width);
|
||||
$("#dialog").dialog('option','height',size.height);
|
||||
switchDialogResize();
|
||||
} else {
|
||||
setTimeout(switchDialogResize,10);
|
||||
}
|
||||
});
|
||||
$( "#dialog" ).one("dialogclose", function(ev,ui) {
|
||||
@@ -214,6 +261,9 @@
|
||||
} else {
|
||||
r.v = rule.find(".node-input-rule-value").val();
|
||||
}
|
||||
if (type === "regex") {
|
||||
r.case = rule.find(".node-input-rule-case").prop("checked");
|
||||
}
|
||||
}
|
||||
node.rules.push(r);
|
||||
});
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013 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.
|
||||
@@ -25,7 +25,7 @@ module.exports = function(RED) {
|
||||
'gte': function(a, b) { return a >= b; },
|
||||
'btwn': function(a, b, c) { return a >= b && a <= c; },
|
||||
'cont': function(a, b) { return (a + "").indexOf(b) != -1; },
|
||||
'regex': function(a, b) { return (a + "").match(new RegExp(b)); },
|
||||
'regex': function(a, b, c, d) { return (a + "").match(new RegExp(b,d?'i':'')); },
|
||||
'true': function(a) { return a === true; },
|
||||
'false': function(a) { return a === false; },
|
||||
'null': function(a) { return (typeof a == "undefined" || a === null); },
|
||||
@@ -60,7 +60,7 @@ module.exports = function(RED) {
|
||||
var rule = node.rules[i];
|
||||
var test = prop;
|
||||
if (rule.t == "else") { test = elseflag; elseflag = true; }
|
||||
if (operators[rule.t](test,rule.v, rule.v2)) {
|
||||
if (operators[rule.t](test,rule.v, rule.v2, rule.case)) {
|
||||
onward.push(msg);
|
||||
elseflag = false;
|
||||
if (node.checkall == "false") { break; }
|
||||
|
@@ -102,6 +102,10 @@
|
||||
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) {
|
||||
var container = $('<li/>',{style:"background: #fff; margin:0; padding:8px 0px; border-bottom: 1px solid #ccc;"});
|
||||
|
||||
@@ -143,8 +147,9 @@
|
||||
|
||||
|
||||
selectField.change(function() {
|
||||
var width = $("#node-input-rule-container").width();
|
||||
resizeRule(container,width);
|
||||
var type = $(this).val();
|
||||
|
||||
if (type == "set") {
|
||||
row2.show();
|
||||
row3.hide();
|
||||
@@ -213,6 +218,12 @@
|
||||
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) {
|
||||
@@ -221,6 +232,8 @@
|
||||
$("#dialog").dialog('option','width',size.width);
|
||||
$("#dialog").dialog('option','height',size.height);
|
||||
changeDialogResize();
|
||||
} else {
|
||||
setTimeout(changeDialogResize,10);
|
||||
}
|
||||
});
|
||||
$( "#dialog" ).one("dialogclose", function(ev,ui) {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
|
||||
<!--
|
||||
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.
|
||||
@@ -68,12 +68,15 @@
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="csv">
|
||||
<p>A function that parses the <b>msg.payload</b> to convert csv to/from a javascript object.
|
||||
<p>A function that parses the <b>msg.payload</b> to convert CSV to/from a javascript object.
|
||||
Places the result in the payload.</p>
|
||||
<p>If the input is a string it tries to parse it as CSV and creates a javascript object.</p>
|
||||
<p>If the input is a javascript object it tries to build a CSV string.</p>
|
||||
<p>The columns template should contain an ordered list of column headers. For csv input these become the property names.
|
||||
For csv output these specify the properties to extract from the object and the order for the csv.</p>
|
||||
<p>If the input is a simple array the output is just a CSV generated from that array.</p>
|
||||
<p>If the input is an array of arrays or an array of objects a multiple-line CSV is created.</p>
|
||||
<p>The columns template should contain an ordered list of column headers. For CSV input these become the property names.
|
||||
For CSV output these specify the properties to extract from the object and the order for the CSV.</p>
|
||||
<p>If the input is an array then the columns template does not matter, but can be used to generate a row of column titles.</p>
|
||||
<p><b>Note:</b> the columns should always be specified comma separated - even if another separator is chosen for the data.</p>
|
||||
</script>
|
||||
|
||||
|
@@ -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.
|
||||
@@ -55,25 +55,50 @@ module.exports = function(RED) {
|
||||
}
|
||||
if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; }
|
||||
for (var s = 0; s < msg.payload.length; s++) {
|
||||
for (var t=0; t < node.template.length; t++) {
|
||||
|
||||
// aaargh - resorting to eval here - but fairly contained front and back.
|
||||
var p = RED.util.ensureString(eval("msg.payload[s]."+node.template[t]));
|
||||
|
||||
if (p === "undefined") { p = ""; }
|
||||
if (p.indexOf(node.sep) != -1) { // add quotes if any "commas"
|
||||
ou += node.quo + p + node.quo + node.sep;
|
||||
if ((Array.isArray(msg.payload[s])) || (typeof msg.payload[s] !== "object")) {
|
||||
if (typeof msg.payload[s] !== "object") { msg.payload = [ msg.payload ]; }
|
||||
for (var t = 0; t < msg.payload[s].length; t++) {
|
||||
if (!msg.payload[s][t]) { msg.payload[s][t] = ""; }
|
||||
if (msg.payload[s][t].toString().indexOf(node.quo) !== -1) { // add double quotes if any quotes
|
||||
msg.payload[s][t] = msg.payload[s][t].toString().replace(/"/g, '""');
|
||||
msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
|
||||
}
|
||||
else if (msg.payload[s][t].toString().indexOf(node.sep) !== -1) { // add quotes if any "commas"
|
||||
msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
|
||||
}
|
||||
}
|
||||
ou += msg.payload[s].join(node.sep) + node.ret;
|
||||
}
|
||||
else {
|
||||
if ((node.template.length === 1) && (node.template[0] === '')) {
|
||||
node.warn(RED._("csv.errors.obj_csv"));
|
||||
}
|
||||
else {
|
||||
for (var t=0; t < node.template.length; t++) {
|
||||
if (node.template[t] === '') {
|
||||
ou += node.sep;
|
||||
}
|
||||
else {
|
||||
// aaargh - resorting to eval here - but fairly contained front and back.
|
||||
var p = RED.util.ensureString(eval("msg.payload[s]."+node.template[t]));
|
||||
|
||||
if (p === "undefined") { p = ""; }
|
||||
if (p.indexOf(node.quo) !== -1) { // add double quotes if any quotes
|
||||
p = p.replace(/"/g, '""');
|
||||
ou += node.quo + p + node.quo + node.sep;
|
||||
}
|
||||
else if (p.indexOf(node.sep) !== -1) { // add quotes if any "commas"
|
||||
ou += node.quo + p + node.quo + node.sep;
|
||||
}
|
||||
else { ou += p + node.sep; } // otherwise just add
|
||||
}
|
||||
}
|
||||
ou = ou.slice(0,-1) + node.ret; // remove final "comma" and add "newline"
|
||||
}
|
||||
else if (p.indexOf(node.quo) != -1) { // add double quotes if any quotes
|
||||
p = p.replace(/"/g, '""');
|
||||
ou += node.quo + p + node.quo + node.sep;
|
||||
}
|
||||
else { ou += p + node.sep; } // otherwise just add
|
||||
}
|
||||
ou = ou.slice(0,-1) + node.ret; // remove final "comma" and add "newline"
|
||||
}
|
||||
msg.payload = ou;
|
||||
node.send(msg);
|
||||
if (msg.payload !== '') { node.send(msg); }
|
||||
}
|
||||
catch(e) { node.error(e,msg); }
|
||||
}
|
||||
|
@@ -31,9 +31,14 @@ module.exports = function(RED) {
|
||||
catch(e) { node.error(e.message,msg); }
|
||||
}
|
||||
else if (typeof msg.payload === "object") {
|
||||
if ((!Buffer.isBuffer(msg.payload)) && (!util.isArray(msg.payload))) {
|
||||
msg.payload = JSON.stringify(msg.payload);
|
||||
node.send(msg);
|
||||
if (!Buffer.isBuffer(msg.payload)) {
|
||||
try {
|
||||
msg.payload = JSON.stringify(msg.payload);
|
||||
node.send(msg);
|
||||
}
|
||||
catch(e) {
|
||||
node.error(RED._("json.errors.dropped-error"));
|
||||
}
|
||||
}
|
||||
else { node.warn(RED._("json.errors.dropped-object")); }
|
||||
}
|
||||
|
@@ -23,10 +23,10 @@
|
||||
</div>
|
||||
<div id="advanced-options">
|
||||
<div class="form-row">
|
||||
<i class="fa fa-key"></i> <span data-i18n="xml.label.represent"></span> <input type="text" id="node-input-attr" style="width:20px !important">
|
||||
<i class="fa fa-key"></i> <span data-i18n="xml.label.represent"></span> <input type="text" id="node-input-attr" style="width:20px !important" placeholder="$">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<i class="fa fa-key"></i> <span data-i18n="xml.label.prefix"></span> <input type="text" id="node-input-chr" style="width:20px !important">
|
||||
<i class="fa fa-key"></i> <span data-i18n="xml.label.prefix"></span> <input type="text" id="node-input-chr" style="width:20px !important" placeholder="_">
|
||||
</div>
|
||||
<div class="form-tips"><span data-i18n="xml.tip"></span></div>
|
||||
</div>
|
||||
@@ -36,7 +36,10 @@
|
||||
<p>A function that parses the <b>msg.payload</b> to convert xml to/from a javascript object. Places the result in the payload.</p>
|
||||
<p>If the input is a string it tries to parse it as XML and creates a javascript object.</p>
|
||||
<p>If the input is a javascript object it tries to build an XML string.</p>
|
||||
<p>See <a href="https://github.com/Leonidas-from-XIV/node-xml2js/blob/master/README.md" target="_new">the xml2js docs <i>here</i></a> for more information.</p>
|
||||
<p>You can also pass in a <b>msg.options</b> object to overide all the multitude of parameters. See
|
||||
<a href="https://github.com/Leonidas-from-XIV/node-xml2js/blob/master/README.md" target="_new">the xml2js docs <i>here</i></a>
|
||||
for more information.</p>
|
||||
<p>If set, options in the edit dialogue override those passed in on the msg.options object.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -45,8 +48,8 @@
|
||||
color:"#DEBD5C",
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
attr: {value:'$',required:true},
|
||||
chr: {value:'_',required:true}
|
||||
attr: {value:""},
|
||||
chr: {value:""}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:1,
|
||||
|
@@ -22,17 +22,25 @@ module.exports = function(RED) {
|
||||
|
||||
function XMLNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.attrkey = n.attr || '$';
|
||||
this.charkey = n.chr || '_';
|
||||
this.attrkey = n.attr;
|
||||
this.charkey = n.chr;
|
||||
var node = this;
|
||||
this.on("input", function(msg) {
|
||||
if (msg.hasOwnProperty("payload")) {
|
||||
if (typeof msg.payload == "object") {
|
||||
msg.payload = builder.buildObject(msg.payload);
|
||||
if (typeof msg.payload === "object") {
|
||||
var options = {};
|
||||
if (msg.hasOwnProperty("options") && typeof msg.options === "object") { options = msg.options; }
|
||||
options.async = false;
|
||||
msg.payload = builder.buildObject(msg.payload, options);
|
||||
node.send(msg);
|
||||
}
|
||||
else if (typeof msg.payload == "string") {
|
||||
parseString(msg.payload, {strict:true,async:true,attrkey:node.attrkey,charkey:node.charkey}, function (err, result) {
|
||||
var options = {};
|
||||
if (msg.hasOwnProperty("options") && typeof msg.options === "object") { options = msg.options; }
|
||||
options.async = true;
|
||||
options.attrkey = node.attrkey || options.attrkey || '$';
|
||||
options.charkey = node.charkey || options.charkey || '_';
|
||||
parseString(msg.payload, options, function (err, result) {
|
||||
if (err) { node.error(err, msg); }
|
||||
else {
|
||||
msg.payload = result;
|
||||
|
63
package.json
63
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name" : "node-red",
|
||||
"version" : "0.11.0",
|
||||
"version" : "0.12.1",
|
||||
"description" : "A visual tool for wiring the Internet of Things",
|
||||
"homepage" : "http://nodered.org",
|
||||
"license" : "Apache-2.0",
|
||||
@@ -25,32 +25,35 @@
|
||||
"editor", "messaging", "iot", "m2m", "pi", "arduino", "beaglebone", "ibm", "flow"
|
||||
],
|
||||
"dependencies": {
|
||||
"express": "3.20.3",
|
||||
"when": "3.7.3",
|
||||
"bcryptjs": "2.2.0",
|
||||
"nopt": "3.0.3",
|
||||
"mqtt": "0.3.x",
|
||||
"ws": "0.7.2",
|
||||
"fs-extra": "0.22.1",
|
||||
"express": "4.13.3",
|
||||
"body-parser": "1.14.1",
|
||||
"when": "3.7.4",
|
||||
"bcryptjs": "2.3.0",
|
||||
"nopt": "3.0.4",
|
||||
"mqtt": "1.4.3",
|
||||
"ws": "0.8.0",
|
||||
"fs-extra": "0.26.0",
|
||||
"clone": "1.0.2",
|
||||
"mustache": "2.1.2",
|
||||
"mustache": "2.2.0",
|
||||
"cron":"1.0.9",
|
||||
"raw-body":"2.1.2",
|
||||
"xml2js":"0.4.9",
|
||||
"raw-body":"2.1.4",
|
||||
"xml2js":"0.4.13",
|
||||
"sentiment":"0.2.3",
|
||||
"follow-redirects":"0.0.6",
|
||||
"follow-redirects":"0.0.7",
|
||||
"cors":"2.7.1",
|
||||
"cheerio":"0.19.0",
|
||||
"uglify-js":"2.4.23",
|
||||
"on-headers":"1.0.0",
|
||||
"uglify-js":"2.5.0",
|
||||
"on-headers":"1.0.1",
|
||||
"is-utf8":"0.2.0",
|
||||
"media-typer": "0.3.0",
|
||||
"fs.notify":"0.0.4",
|
||||
"passport":"0.2.2",
|
||||
"passport":"0.3.0",
|
||||
"passport-http-bearer":"1.0.1",
|
||||
"passport-oauth2-client-password":"0.1.2",
|
||||
"oauth2orize":"1.0.1",
|
||||
"i18next":"1.10.2",
|
||||
"semver": "5.0.1",
|
||||
"oauth2orize":"1.1.0",
|
||||
"i18next":"1.10.5",
|
||||
"semver": "5.0.3",
|
||||
"basic-auth": "1.0.3",
|
||||
"node-red-node-feedparser":"0.1.*",
|
||||
"node-red-node-email":"0.1.*",
|
||||
"node-red-node-twitter":"0.1.*",
|
||||
@@ -58,28 +61,28 @@
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"node-red-node-serialport":"0.0.*",
|
||||
"bcrypt":"0.8.3"
|
||||
"bcrypt":"0.8.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "0.4.5",
|
||||
"grunt-chmod": "1.0.3",
|
||||
"grunt-chmod": "1.1.1",
|
||||
"grunt-cli": "0.1.13",
|
||||
"grunt-concurrent":"2.0.0",
|
||||
"grunt-concurrent":"2.0.3",
|
||||
"grunt-contrib-clean":"0.6.0",
|
||||
"grunt-contrib-compress": "0.13.0",
|
||||
"grunt-contrib-compress": "0.14.0",
|
||||
"grunt-contrib-concat":"0.5.1",
|
||||
"grunt-contrib-copy": "0.8.0",
|
||||
"grunt-contrib-jshint": "0.11.2",
|
||||
"grunt-contrib-uglify": "0.9.1",
|
||||
"grunt-contrib-copy": "0.8.2",
|
||||
"grunt-contrib-jshint": "0.11.3",
|
||||
"grunt-contrib-uglify": "0.9.2",
|
||||
"grunt-contrib-watch":"0.6.1",
|
||||
"grunt-jsonlint":"1.0.4",
|
||||
"grunt-jsonlint":"1.0.5",
|
||||
"grunt-nodemon":"0.4.0",
|
||||
"grunt-sass":"1.0.0",
|
||||
"grunt-sass":"1.1.0",
|
||||
"grunt-simple-mocha": "0.4.0",
|
||||
"mocha": "2.2.5",
|
||||
"mocha": "2.3.3",
|
||||
"should": "6.0.3",
|
||||
"sinon": "1.15.4",
|
||||
"supertest": "1.0.1"
|
||||
"sinon": "1.17.2",
|
||||
"supertest": "1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8 <=0.12"
|
||||
|
52
red.js
52
red.js
@@ -21,7 +21,7 @@ var express = require("express");
|
||||
var crypto = require("crypto");
|
||||
var nopt = require("nopt");
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
var fs = require("fs-extra");
|
||||
var RED = require("./red/red.js");
|
||||
var log = require("./red/log");
|
||||
|
||||
@@ -76,13 +76,22 @@ if (parsedArgs.settings) {
|
||||
// NODE_RED_HOME contains user data - use its settings.js
|
||||
settingsFile = path.join(process.env.NODE_RED_HOME,"settings.js");
|
||||
} else {
|
||||
var userSettingsFile = path.join(process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE,".node-red","settings.js");
|
||||
var userDir = path.join(process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE,".node-red");
|
||||
var userSettingsFile = path.join(userDir,"settings.js");
|
||||
if (fs.existsSync(userSettingsFile)) {
|
||||
// $HOME/.node-red/settings.js exists
|
||||
settingsFile = userSettingsFile;
|
||||
} else {
|
||||
// Use default settings.js
|
||||
settingsFile = __dirname+"/settings.js";
|
||||
var defaultSettings = path.join(__dirname,"settings.js");
|
||||
var settingsStat = fs.statSync(defaultSettings);
|
||||
if (settingsStat.mtime.getTime() < settingsStat.ctime.getTime()) {
|
||||
// Default settings file has not been modified - safe to copy
|
||||
fs.copySync(defaultSettings,userSettingsFile);
|
||||
settingsFile = userSettingsFile;
|
||||
} else {
|
||||
// Use default settings.js as it has been modified
|
||||
settingsFile = defaultSettings;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,8 +100,11 @@ try {
|
||||
var settings = require(settingsFile);
|
||||
settings.settingsFile = settingsFile;
|
||||
} catch(err) {
|
||||
console.log("Error loading settings file: "+settingsFile)
|
||||
if (err.code == 'MODULE_NOT_FOUND') {
|
||||
console.log("Unable to load settings file: "+settingsFile);
|
||||
if (err.toString().indexOf(settingsFile) === -1) {
|
||||
console.log(err.toString());
|
||||
}
|
||||
} else {
|
||||
console.log(err);
|
||||
}
|
||||
@@ -166,21 +178,25 @@ try {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function basicAuthMiddleware(user,pass) {
|
||||
var basicAuth = require('basic-auth');
|
||||
return function(req,res,next) {
|
||||
var requestUser = basicAuth(req);
|
||||
if (!requestUser || requestUser.name !== user || crypto.createHash('md5').update(requestUser.pass,'utf8').digest('hex') !== pass) {
|
||||
res.set('WWW-Authenticate', 'Basic realm=Authorization Required');
|
||||
return res.sendStatus(401);
|
||||
}
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.httpAdminRoot !== false && settings.httpAdminAuth) {
|
||||
RED.log.warn(log._("server.httpadminauth-deprecated"));
|
||||
app.use(settings.httpAdminRoot,
|
||||
express.basicAuth(function(user, pass) {
|
||||
return user === settings.httpAdminAuth.user && crypto.createHash('md5').update(pass,'utf8').digest('hex') === settings.httpAdminAuth.pass;
|
||||
})
|
||||
);
|
||||
app.use(settings.httpAdminRoot, basicAuthMiddleware(settings.httpAdminAuth.user,settings.httpAdminAuth.pass));
|
||||
}
|
||||
|
||||
if (settings.httpNodeRoot !== false && settings.httpNodeAuth) {
|
||||
app.use(settings.httpNodeRoot,
|
||||
express.basicAuth(function(user, pass) {
|
||||
return user === settings.httpNodeAuth.user && crypto.createHash('md5').update(pass,'utf8').digest('hex') === settings.httpNodeAuth.pass;
|
||||
})
|
||||
);
|
||||
app.use(settings.httpNodeRoot,basicAuthMiddleware(settings.httpNodeAuth.user,settings.httpNodeAuth.pass));
|
||||
}
|
||||
if (settings.httpAdminRoot !== false) {
|
||||
app.use(settings.httpAdminRoot,RED.httpAdmin);
|
||||
@@ -192,11 +208,7 @@ if (settings.httpNodeRoot !== false) {
|
||||
if (settings.httpStatic) {
|
||||
settings.httpStaticAuth = settings.httpStaticAuth || settings.httpAuth;
|
||||
if (settings.httpStaticAuth) {
|
||||
app.use("/",
|
||||
express.basicAuth(function(user, pass) {
|
||||
return user === settings.httpStaticAuth.user && crypto.createHash('md5').update(pass,'utf8').digest('hex') === settings.httpStaticAuth.pass;
|
||||
})
|
||||
);
|
||||
app.use("/",basicAuthMiddleware(settings.httpStaticAuth.user,settings.httpStaticAuth.pass));
|
||||
}
|
||||
app.use("/",express.static(settings.httpStatic));
|
||||
}
|
||||
|
@@ -55,7 +55,7 @@ function needsPermission(permission) {
|
||||
return next();
|
||||
}
|
||||
log.audit({event: "permission.fail"},req);
|
||||
return res.send(401);
|
||||
return res.status(401).end();
|
||||
});
|
||||
} else {
|
||||
next();
|
||||
@@ -95,7 +95,7 @@ function revoke(req,res) {
|
||||
// TODO: audit log
|
||||
Tokens.revoke(token).then(function() {
|
||||
log.audit({event: "auth.login.revoke"},req);
|
||||
res.send(200);
|
||||
res.status(200).end();
|
||||
});
|
||||
}
|
||||
|
||||
|
48
red/api/credentials.js
Normal file
48
red/api/credentials.js
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 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 log = require("../log");
|
||||
var api = require("../nodes");
|
||||
|
||||
module.exports = {
|
||||
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
|
||||
// registry module that knows about node types.
|
||||
var nodeType = req.params.type;
|
||||
var nodeID = req.params.id;
|
||||
log.audit({event: "credentials.get",type:nodeType,id:nodeID},req);
|
||||
var credentials = api.getCredentials(nodeID);
|
||||
if (!credentials) {
|
||||
res.json({});
|
||||
return;
|
||||
}
|
||||
var definition = api.getCredentialDefinition(nodeType);
|
||||
|
||||
var sendCredentials = {};
|
||||
for (var cred in definition) {
|
||||
if (definition.hasOwnProperty(cred)) {
|
||||
if (definition[cred].type == "password") {
|
||||
var key = 'has_' + cred;
|
||||
sendCredentials[key] = credentials[cred] != null && credentials[cred] !== '';
|
||||
continue;
|
||||
}
|
||||
sendCredentials[cred] = credentials[cred] || '';
|
||||
}
|
||||
}
|
||||
res.json(sendCredentials);
|
||||
}
|
||||
}
|
@@ -29,11 +29,11 @@ module.exports = {
|
||||
var deploymentType = req.get("Node-RED-Deployment-Type")||"full";
|
||||
log.audit({event: "flows.set",type:deploymentType},req);
|
||||
redNodes.setFlows(flows,deploymentType).then(function() {
|
||||
res.send(204);
|
||||
res.status(204).end();
|
||||
}).otherwise(function(err) {
|
||||
log.warn(log._("api.flows.error-save",{message:err.message}));
|
||||
log.warn(err.stack);
|
||||
res.json(500,{error:"unexpected_error", message:err.message});
|
||||
res.status(500).json({error:"unexpected_error", message:err.message});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@
|
||||
**/
|
||||
|
||||
var express = require("express");
|
||||
var bodyParser = require("body-parser");
|
||||
var util = require('util');
|
||||
var path = require('path');
|
||||
var passport = require('passport');
|
||||
@@ -26,6 +27,9 @@ 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 auth = require("./auth");
|
||||
var needsPermission = auth.needsPermission;
|
||||
@@ -33,14 +37,18 @@ var needsPermission = auth.needsPermission;
|
||||
var settings = require("../settings");
|
||||
|
||||
var errorHandler = function(err,req,res,next) {
|
||||
console.log(err.stack);
|
||||
res.json(400,{error:"unexpected_error", message:err.toString()});
|
||||
if (err.message === "request entity too large") {
|
||||
log.error(err);
|
||||
} else {
|
||||
console.log(err.stack);
|
||||
}
|
||||
log.audit({event: "api.error",error:err.code||"unexpected_error",message:err.toString()},req);
|
||||
res.status(400).json({error:"unexpected_error", message:err.toString()});
|
||||
};
|
||||
|
||||
function init(adminApp,storage) {
|
||||
|
||||
auth.init(settings,storage);
|
||||
|
||||
// Editor
|
||||
if (!settings.disableEditor) {
|
||||
ui.init(settings);
|
||||
@@ -53,9 +61,9 @@ function init(adminApp,storage) {
|
||||
editorApp.use("/",ui.editorResources);
|
||||
adminApp.use(editorApp);
|
||||
}
|
||||
|
||||
adminApp.use(express.json());
|
||||
adminApp.use(express.urlencoded());
|
||||
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);
|
||||
|
||||
@@ -86,6 +94,8 @@ function init(adminApp,storage) {
|
||||
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
|
||||
|
@@ -29,7 +29,7 @@ function createLibrary(type) {
|
||||
if (typeof result === "string") {
|
||||
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||
res.write(result);
|
||||
res.end();
|
||||
res.end();
|
||||
} else {
|
||||
res.json(result);
|
||||
}
|
||||
@@ -38,33 +38,33 @@ function createLibrary(type) {
|
||||
log.warn(log._("api.library.error-load-entry",{path:path,message:err.toString()}));
|
||||
if (err.code === 'forbidden') {
|
||||
log.audit({event: "library.get",type:type,error:"forbidden"},req);
|
||||
res.send(403);
|
||||
res.status(403).end();
|
||||
return;
|
||||
}
|
||||
}
|
||||
log.audit({event: "library.get",type:type,error:"not_found"},req);
|
||||
res.send(404);
|
||||
res.status(404).end();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
redApp.post(new RegExp("/library/"+type+"\/(.*)"),needsPermission("library.write"),function(req,res) {
|
||||
var path = req.params[0];
|
||||
var meta = req.body;
|
||||
var text = meta.text;
|
||||
delete meta.text;
|
||||
|
||||
|
||||
storage.saveLibraryEntry(type,path,meta,text).then(function() {
|
||||
log.audit({event: "library.set",type:type},req);
|
||||
res.send(204);
|
||||
res.status(204).end();
|
||||
}).otherwise(function(err) {
|
||||
log.warn(log._("api.library.error-save-entry",{path:path,message:err.toString()}));
|
||||
if (err.code === 'forbidden') {
|
||||
log.audit({event: "library.set",type:type,error:"forbidden"},req);
|
||||
res.send(403);
|
||||
res.status(403).end();
|
||||
return;
|
||||
}
|
||||
log.audit({event: "library.set",type:type,error:"unexpected_error",message:err.toString()},req);
|
||||
res.json(500,{error:"unexpected_error", message:err.toString()});
|
||||
res.status(500).json({error:"unexpected_error", message:err.toString()});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -74,7 +74,7 @@ module.exports = {
|
||||
redApp = app;
|
||||
},
|
||||
register: createLibrary,
|
||||
|
||||
|
||||
getAll: function(req,res) {
|
||||
storage.getAllFlows().then(function(flows) {
|
||||
log.audit({event: "library.get.all",type:"flow"},req);
|
||||
@@ -92,28 +92,28 @@ module.exports = {
|
||||
log.warn(log._("api.library.error-load-flow",{path:req.params[0],message:err.toString()}));
|
||||
if (err.code === 'forbidden') {
|
||||
log.audit({event: "library.get",type:"flow",path:req.params[0],error:"forbidden"},req);
|
||||
res.send(403);
|
||||
res.status(403).end();
|
||||
return;
|
||||
}
|
||||
}
|
||||
log.audit({event: "library.get",type:"flow",path:req.params[0],error:"not_found"},req);
|
||||
res.send(404);
|
||||
res.status(404).end();
|
||||
});
|
||||
},
|
||||
post: function(req,res) {
|
||||
var flow = JSON.stringify(req.body);
|
||||
storage.saveFlow(req.params[0],flow).then(function() {
|
||||
log.audit({event: "library.set",type:"flow",path:req.params[0]},req);
|
||||
res.send(204);
|
||||
res.status(204).end();
|
||||
}).otherwise(function(err) {
|
||||
log.warn(log._("api.library.error-save-flow",{path:req.params[0],message:err.toString()}));
|
||||
if (err.code === 'forbidden') {
|
||||
log.audit({event: "library.set",type:"flow",path:req.params[0],error:"forbidden"},req);
|
||||
res.send(403);
|
||||
res.status(403).end();
|
||||
return;
|
||||
}
|
||||
log.audit({event: "library.set",type:"flow",path:req.params[0],error:"unexpected_error",message:err.toString()},req);
|
||||
res.send(500,{error:"unexpected_error", message:err.toString()});
|
||||
res.status(500).send({error:"unexpected_error", message:err.toString()});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ module.exports = {
|
||||
get: function(req,res) {
|
||||
var namespace = req.params[0];
|
||||
namespace = namespace.replace(/\.json$/,"");
|
||||
var lang = i18n.determineLangFromHeaders(req.acceptedLanguages || []);
|
||||
var lang = i18n.determineLangFromHeaders(req.acceptsLanguages() || []);
|
||||
var prevLang = i18n.i.lng();
|
||||
i18n.i.setLng(lang, function(){
|
||||
var catalog = i18n.catalog(namespace,lang);
|
||||
|
@@ -15,7 +15,6 @@
|
||||
**/
|
||||
var redNodes = require("../nodes");
|
||||
var comms = require("../comms");
|
||||
var server = require("../server");
|
||||
var log = require("../log");
|
||||
var i18n = require("../i18n");
|
||||
|
||||
@@ -29,7 +28,7 @@ module.exports = {
|
||||
log.audit({event: "nodes.list.get"},req);
|
||||
res.json(redNodes.getNodeList());
|
||||
} else {
|
||||
var lang = i18n.determineLangFromHeaders(req.acceptedLanguages);
|
||||
var lang = i18n.determineLangFromHeaders(req.acceptsLanguages());
|
||||
log.audit({event: "nodes.configs.get"},req);
|
||||
res.send(redNodes.getNodeConfigs(lang));
|
||||
}
|
||||
@@ -38,7 +37,7 @@ module.exports = {
|
||||
post: function(req,res) {
|
||||
if (!settings.available()) {
|
||||
log.audit({event: "nodes.install",error:"settings_unavailable"},req);
|
||||
res.json(400,{error:"settings_unavailable", message:"Settings unavailable"});
|
||||
res.status(400).json({error:"settings_unavailable", message:"Settings unavailable"});
|
||||
return;
|
||||
}
|
||||
var node = req.body;
|
||||
@@ -47,35 +46,31 @@ module.exports = {
|
||||
var module = redNodes.getModuleInfo(node.module);
|
||||
if (module) {
|
||||
log.audit({event: "nodes.install",module:node.module,error:"module_already_loaded"},req);
|
||||
res.json(400,{error:"module_already_loaded", message:"Module already loaded"});
|
||||
res.status(400).json({error:"module_already_loaded", message:"Module already loaded"});
|
||||
return;
|
||||
}
|
||||
promise = server.installModule(node.module);
|
||||
} else if (node.file) {
|
||||
promise = server.installNode(node.file);
|
||||
promise = redNodes.installModule(node.module);
|
||||
} else {
|
||||
log.audit({event: "nodes.install",module:node.module,error:"invalid_request"},req);
|
||||
res.json(400,{error:"invalid_request", message:"Invalid request"});
|
||||
res.status(400).json({error:"invalid_request", message:"Invalid request"});
|
||||
return;
|
||||
}
|
||||
promise.then(function(info) {
|
||||
comms.publish("node/added",info.nodes,false);
|
||||
if (node.module) {
|
||||
log.audit({event: "nodes.install",module:node.module},req);
|
||||
res.json(redNodes.getModuleInfo(node.module));
|
||||
} else if (node.file) {
|
||||
log.audit({event: "nodes.install",file:node.file},req);
|
||||
res.json(info.nodes[0]);
|
||||
res.json(info);
|
||||
}
|
||||
}).otherwise(function(err) {
|
||||
if (err.code === 404) {
|
||||
log.audit({event: "nodes.install",module:node.module,file:node.file,error:"not_found"},req);
|
||||
res.send(404);
|
||||
log.audit({event: "nodes.install",module:node.module,error:"not_found"},req);
|
||||
res.status(404).end();
|
||||
} else if (err.code) {
|
||||
log.audit({event: "nodes.install",module:node.module,error:err.code},req);
|
||||
res.json(400,{error:err.code, message:err.message});
|
||||
} else {
|
||||
log.audit({event: "nodes.install",module:node.module,file:node.file,error:err.code||"unexpected_error",message:err.toString()},req);
|
||||
res.json(400,{error:err.code||"unexpected_error", message:err.toString()});
|
||||
res.status(400).json({error:err.code, message:err.message});
|
||||
} else {
|
||||
log.audit({event: "nodes.install",module:node.module,error:err.code||"unexpected_error",message:err.toString()},req);
|
||||
res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -83,7 +78,7 @@ module.exports = {
|
||||
delete: function(req,res) {
|
||||
if (!settings.available()) {
|
||||
log.audit({event: "nodes.remove",error:"settings_unavailable"},req);
|
||||
res.json(400,{error:"settings_unavailable", message:"Settings unavailable"});
|
||||
res.status(400).json({error:"settings_unavailable", message:"Settings unavailable"});
|
||||
return;
|
||||
}
|
||||
var mod = req.params.mod;
|
||||
@@ -92,22 +87,23 @@ module.exports = {
|
||||
var module = redNodes.getModuleInfo(mod);
|
||||
if (!module) {
|
||||
log.audit({event: "nodes.remove",module:mod,error:"not_found"},req);
|
||||
res.send(404);
|
||||
res.status(404).end();
|
||||
return;
|
||||
} else {
|
||||
promise = server.uninstallModule(mod);
|
||||
promise = redNodes.uninstallModule(mod);
|
||||
}
|
||||
|
||||
promise.then(function() {
|
||||
promise.then(function(list) {
|
||||
comms.publish("node/removed",list,false);
|
||||
log.audit({event: "nodes.remove",module:mod},req);
|
||||
res.send(204);
|
||||
res.status(204).end();
|
||||
}).otherwise(function(err) {
|
||||
log.audit({event: "nodes.remove",module:mod,error:err.code||"unexpected_error",message:err.toString()},req);
|
||||
res.json(400,{error:err.code||"unexpected_error", message:err.toString()});
|
||||
res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
|
||||
});
|
||||
} catch(err) {
|
||||
log.audit({event: "nodes.remove",module:mod,error:err.code||"unexpected_error",message:err.toString()},req);
|
||||
res.json(400,{error:err.code||"unexpected_error", message:err.toString()});
|
||||
res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -122,17 +118,17 @@ module.exports = {
|
||||
res.send(result);
|
||||
} else {
|
||||
log.audit({event: "nodes.info.get",id:id,error:"not_found"},req);
|
||||
res.send(404);
|
||||
res.status(404).end();
|
||||
}
|
||||
} else {
|
||||
var lang = i18n.determineLangFromHeaders(req.acceptedLanguages);
|
||||
var lang = i18n.determineLangFromHeaders(req.acceptsLanguages());
|
||||
result = redNodes.getNodeConfig(id,lang);
|
||||
if (result) {
|
||||
log.audit({event: "nodes.config.get",id:id},req);
|
||||
res.send(result);
|
||||
} else {
|
||||
log.audit({event: "nodes.config.get",id:id,error:"not_found"},req);
|
||||
res.send(404);
|
||||
res.status(404).end();
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -145,20 +141,20 @@ module.exports = {
|
||||
res.json(result);
|
||||
} else {
|
||||
log.audit({event: "nodes.module.get",module:module,error:"not_found"},req);
|
||||
res.send(404);
|
||||
res.status(404).end();
|
||||
}
|
||||
},
|
||||
|
||||
putSet: function(req,res) {
|
||||
if (!settings.available()) {
|
||||
log.audit({event: "nodes.info.set",error:"settings_unavailable"},req);
|
||||
res.json(400,{error:"settings_unavailable", message:"Settings unavailable"});
|
||||
res.status(400).json({error:"settings_unavailable", message:"Settings unavailable"});
|
||||
return;
|
||||
}
|
||||
var body = req.body;
|
||||
if (!body.hasOwnProperty("enabled")) {
|
||||
log.audit({event: "nodes.info.set",error:"invalid_request"},req);
|
||||
res.json(400,{error:"invalid_request", message:"Invalid request"});
|
||||
res.status(400).json({error:"invalid_request", message:"Invalid request"});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -167,7 +163,7 @@ module.exports = {
|
||||
var info;
|
||||
if (!node) {
|
||||
log.audit({event: "nodes.info.set",id:id,error:"not_found"},req);
|
||||
res.send(404);
|
||||
res.status(404).end();
|
||||
} else {
|
||||
delete node.loaded;
|
||||
putNode(node, body.enabled).then(function(result) {
|
||||
@@ -177,20 +173,20 @@ module.exports = {
|
||||
}
|
||||
} catch(err) {
|
||||
log.audit({event: "nodes.info.set",id:id,enabled:body.enabled,error:err.code||"unexpected_error",message:err.toString()},req);
|
||||
res.json(400,{error:err.code||"unexpected_error", message:err.toString()});
|
||||
res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
|
||||
}
|
||||
},
|
||||
|
||||
putModule: function(req,res) {
|
||||
if (!settings.available()) {
|
||||
log.audit({event: "nodes.module.set",error:"settings_unavailable"},req);
|
||||
res.json(400,{error:"settings_unavailable", message:"Settings unavailable"});
|
||||
res.status(400).json({error:"settings_unavailable", message:"Settings unavailable"});
|
||||
return;
|
||||
}
|
||||
var body = req.body;
|
||||
if (!body.hasOwnProperty("enabled")) {
|
||||
log.audit({event: "nodes.module.set",error:"invalid_request"},req);
|
||||
res.json(400,{error:"invalid_request", message:"Invalid request"});
|
||||
res.status(400).json({error:"invalid_request", message:"Invalid request"});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -198,7 +194,7 @@ module.exports = {
|
||||
var module = redNodes.getModuleInfo(mod);
|
||||
if (!module) {
|
||||
log.audit({event: "nodes.module.set",module:mod,error:"not_found"},req);
|
||||
return res.send(404);
|
||||
return res.status(404).end();
|
||||
}
|
||||
|
||||
var nodes = module.nodes;
|
||||
@@ -211,7 +207,7 @@ module.exports = {
|
||||
});
|
||||
} catch(err) {
|
||||
log.audit({event: "nodes.module.set",module:mod,enabled:body.enabled,error:err.code||"unexpected_error",message:err.toString()},req);
|
||||
res.json(400,{error:err.code||"unexpected_error", message:err.toString()});
|
||||
res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -227,7 +223,7 @@ function putNode(node, enabled) {
|
||||
} else {
|
||||
promise = redNodes.disableNode(node.id);
|
||||
}
|
||||
|
||||
|
||||
return promise.then(function(info) {
|
||||
if (info.enabled === enabled && !info.err) {
|
||||
comms.publish("node/"+(enabled?"enabled":"disabled"),info,false);
|
||||
|
@@ -43,7 +43,7 @@ function serveFile(app,baseUrl,file) {
|
||||
var url = baseUrl+path.basename(file);
|
||||
//console.log(url,"->",file);
|
||||
app.get(url,function(req, res) {
|
||||
res.sendfile(file);
|
||||
res.sendFile(file);
|
||||
});
|
||||
return "theme"+url;
|
||||
} catch(err) {
|
||||
@@ -58,13 +58,13 @@ module.exports = {
|
||||
var url;
|
||||
themeContext = clone(defaultContext);
|
||||
themeSettings = null;
|
||||
|
||||
|
||||
if (settings.editorTheme) {
|
||||
var theme = settings.editorTheme;
|
||||
themeSettings = {};
|
||||
|
||||
|
||||
var themeApp = express();
|
||||
|
||||
|
||||
if (theme.page) {
|
||||
if (theme.page.css) {
|
||||
var styles = theme.page.css;
|
||||
@@ -72,7 +72,7 @@ module.exports = {
|
||||
styles = [styles];
|
||||
}
|
||||
themeContext.page.css = [];
|
||||
|
||||
|
||||
for (i=0;i<styles.length;i++) {
|
||||
url = serveFile(themeApp,"/css/",styles[i]);
|
||||
if (url) {
|
||||
@@ -80,25 +80,25 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (theme.page.favicon) {
|
||||
url = serveFile(themeApp,"/favicon/",theme.page.favicon)
|
||||
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);
|
||||
@@ -110,7 +110,7 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (theme.deployButton) {
|
||||
if (theme.deployButton.type == "simple") {
|
||||
themeSettings.deployButton = {
|
||||
@@ -127,11 +127,11 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (theme.hasOwnProperty("userMenu")) {
|
||||
themeSettings.userMenu = theme.userMenu;
|
||||
}
|
||||
|
||||
|
||||
if (theme.login) {
|
||||
if (theme.login.image) {
|
||||
url = serveFile(themeApp,"/login/",theme.login.image);
|
||||
@@ -142,11 +142,11 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (theme.hasOwnProperty("menu")) {
|
||||
themeSettings.menu = theme.menu;
|
||||
}
|
||||
|
||||
|
||||
return themeApp;
|
||||
}
|
||||
},
|
||||
|
@@ -42,7 +42,7 @@ module.exports = {
|
||||
editorTemplate = fs.readFileSync(path.join(templateDir,"index.mst"),"utf8");
|
||||
Mustache.parse(editorTemplate);
|
||||
},
|
||||
|
||||
|
||||
ensureSlash: function(req,res,next) {
|
||||
var parts = req.originalUrl.split("?");
|
||||
if (parts[0].slice(-1) != "/") {
|
||||
@@ -55,17 +55,17 @@ module.exports = {
|
||||
},
|
||||
icon: function(req,res) {
|
||||
if (iconCache[req.params.icon]) {
|
||||
res.sendfile(iconCache[req.params.icon]); // if not found, express prints this to the console and serves 404
|
||||
} else {
|
||||
res.sendFile(iconCache[req.params.icon]); // if not found, express prints this to the console and serves 404
|
||||
} else {
|
||||
for (var p=0;p<icon_paths.length;p++) {
|
||||
var iconPath = path.join(icon_paths[p],req.params.icon);
|
||||
if (fs.existsSync(iconPath)) {
|
||||
res.sendfile(iconPath);
|
||||
res.sendFile(iconPath);
|
||||
iconCache[req.params.icon] = iconPath;
|
||||
return;
|
||||
}
|
||||
}
|
||||
res.sendfile(defaultIcon);
|
||||
res.sendFile(defaultIcon);
|
||||
}
|
||||
},
|
||||
editor: function(req,res) {
|
||||
|
10
red/comms.js
10
red/comms.js
@@ -46,7 +46,7 @@ function start() {
|
||||
var path = settings.httpAdminRoot || "/";
|
||||
path = (path.slice(0,1) != "/" ? "/":"") + path + (path.slice(-1) == "/" ? "":"/") + "comms";
|
||||
wsServer = new ws.Server({server:server,path:path});
|
||||
|
||||
|
||||
wsServer.on('connection',function(ws) {
|
||||
log.audit({event: "comms.open"});
|
||||
var pendingAuth = (settings.adminAuth != null);
|
||||
@@ -122,13 +122,13 @@ function start() {
|
||||
log.warn(log._("comms.error",{message:err.toString()}));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
wsServer.on('error', function(err) {
|
||||
log.warn(log._("comms.error-server",{message:err.toString()}));
|
||||
});
|
||||
|
||||
|
||||
lastSentTime = Date.now();
|
||||
|
||||
|
||||
heartbeatTimer = setInterval(function() {
|
||||
var now = Date.now();
|
||||
if (now-lastSentTime > webSocketKeepAliveTime) {
|
||||
@@ -167,6 +167,8 @@ function publishTo(ws,topic,data) {
|
||||
try {
|
||||
ws.send(msg);
|
||||
} catch(err) {
|
||||
removeActiveConnection(ws);
|
||||
removePendingConnection(ws);
|
||||
log.warn(log._("comms.error-send",{message:err.toString()}));
|
||||
}
|
||||
}
|
||||
|
29
red/i18n.js
29
red/i18n.js
@@ -13,14 +13,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
|
||||
var i18n = require("i18next");
|
||||
var when = require("when");
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
|
||||
var defaultLang = "en-US";
|
||||
var supportedLangs = null;
|
||||
var supportedLangs = [];
|
||||
|
||||
var resourceMap = {
|
||||
"runtime": {
|
||||
@@ -56,6 +56,18 @@ var initSupportedLangs = function() {
|
||||
});
|
||||
}
|
||||
|
||||
function mergeCatalog(fallback,catalog) {
|
||||
for (var k in fallback) {
|
||||
if (fallback.hasOwnProperty(k)) {
|
||||
if (!catalog[k]) {
|
||||
catalog[k] = fallback[k];
|
||||
} else if (typeof fallback[k] === 'object') {
|
||||
mergeCatalog(fallback[k],catalog[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var MessageFileLoader = {
|
||||
fetchOne: function(lng, ns, callback) {
|
||||
if (resourceMap[ns]) {
|
||||
@@ -66,10 +78,12 @@ var MessageFileLoader = {
|
||||
callback(err);
|
||||
} else {
|
||||
try {
|
||||
//console.log(">>",ns,file,lng);
|
||||
resourceCache[ns] = resourceCache[ns]||{};
|
||||
resourceCache[ns][lng] = JSON.parse(content.replace(/^\uFEFF/, ''));
|
||||
//console.log(resourceCache[ns][lng]);
|
||||
if (lng !== defaultLang) {
|
||||
mergeCatalog(resourceCache[ns][defaultLang],resourceCache[ns][lng]);
|
||||
}
|
||||
callback(null, resourceCache[ns][lng]);
|
||||
} catch(e) {
|
||||
callback(e);
|
||||
@@ -80,7 +94,7 @@ var MessageFileLoader = {
|
||||
callback(new Error("Unrecognised namespace"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function init() {
|
||||
@@ -109,7 +123,10 @@ function getCatalog(namespace,lang) {
|
||||
if (!result) {
|
||||
var langParts = lang.split("-");
|
||||
if (langParts.length == 2) {
|
||||
result = getCatalog(namespace,langParts[0]);
|
||||
result = resourceCache[namespace][langParts[0]];
|
||||
}
|
||||
if (!result) {
|
||||
return resourceCache[namespace][defaultLang];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,7 +136,7 @@ function getCatalog(namespace,lang) {
|
||||
|
||||
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];
|
||||
|
@@ -1,790 +0,0 @@
|
||||
/**
|
||||
* 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 when = require("when");
|
||||
var clone = require("clone");
|
||||
|
||||
var typeRegistry = require("./registry");
|
||||
var credentials = require("./credentials");
|
||||
var redUtil = require("../util");
|
||||
var events = require("../events");
|
||||
var Log = require("../log");
|
||||
|
||||
function getID() {
|
||||
return (1+Math.random()*4294967295).toString(16);
|
||||
}
|
||||
|
||||
function createNode(type,config) {
|
||||
var nn = null;
|
||||
var nt = typeRegistry.get(type);
|
||||
if (nt) {
|
||||
try {
|
||||
nn = new nt(clone(config));
|
||||
}
|
||||
catch (err) {
|
||||
Log.log({
|
||||
level: Log.ERROR,
|
||||
id:config.id,
|
||||
type: type,
|
||||
msg: err
|
||||
});
|
||||
}
|
||||
} else {
|
||||
Log.error(Log._("nodes.flow.unknown-type", {type:type}));
|
||||
}
|
||||
return nn;
|
||||
}
|
||||
|
||||
|
||||
function createSubflow(sf,sfn,subflows) {
|
||||
//console.log("CREATE SUBFLOW",sf.config.id,sfn.id);
|
||||
var nodes = [];
|
||||
var node_map = {};
|
||||
var newNodes = [];
|
||||
var node;
|
||||
var wires;
|
||||
var i,j,k;
|
||||
|
||||
// Clone all of the subflow node definitions and give them new IDs
|
||||
for (i=0;i<sf.nodes.length;i++) {
|
||||
node = clone(sf.nodes[i].config);
|
||||
var nid = getID();
|
||||
node_map[node.id] = node;
|
||||
node._alias = node.id;
|
||||
node.id = nid;
|
||||
node.z = sfn.id;
|
||||
newNodes.push(node);
|
||||
}
|
||||
// Update all subflow interior wiring to reflect new node IDs
|
||||
for (i=0;i<newNodes.length;i++) {
|
||||
node = newNodes[i];
|
||||
var outputs = node.wires;
|
||||
|
||||
for (j=0;j<outputs.length;j++) {
|
||||
wires = outputs[j];
|
||||
for (k=0;k<wires.length;k++) {
|
||||
outputs[j][k] = node_map[outputs[j][k]].id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a subflow node to accept inbound messages and route appropriately
|
||||
var Node = require("./Node");
|
||||
var subflowInstance = {
|
||||
id: sfn.id,
|
||||
type: sfn.type,
|
||||
z: sfn.z,
|
||||
name: sfn.name,
|
||||
wires: []
|
||||
}
|
||||
if (sf.config.in) {
|
||||
subflowInstance.wires = sf.config.in.map(function(n) { return n.wires.map(function(w) { return node_map[w.id].id;})})
|
||||
subflowInstance._originalWires = clone(subflowInstance.wires);
|
||||
}
|
||||
var subflowNode = new Node(subflowInstance);
|
||||
|
||||
subflowNode.on("input", function(msg) { this.send(msg);});
|
||||
|
||||
|
||||
subflowNode._updateWires = subflowNode.updateWires;
|
||||
|
||||
subflowNode.updateWires = function(newWires) {
|
||||
// Wire the subflow outputs
|
||||
if (sf.config.out) {
|
||||
var node,wires,i,j;
|
||||
// Restore the original wiring to the internal nodes
|
||||
|
||||
subflowInstance.wires = clone(subflowInstance._originalWires);
|
||||
|
||||
for (i=0;i<sf.config.out.length;i++) {
|
||||
wires = sf.config.out[i].wires;
|
||||
for (j=0;j<wires.length;j++) {
|
||||
if (wires[j].id != sf.config.id) {
|
||||
node = node_map[wires[j].id];
|
||||
if (node._originalWires) {
|
||||
node.wires = clone(node._originalWires);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var modifiedNodes = {};
|
||||
var subflowInstanceModified = false;
|
||||
|
||||
for (i=0;i<sf.config.out.length;i++) {
|
||||
wires = sf.config.out[i].wires;
|
||||
for (j=0;j<wires.length;j++) {
|
||||
if (wires[j].id === sf.config.id) {
|
||||
subflowInstance.wires[wires[j].port] = subflowInstance.wires[wires[j].port].concat(newWires[i]);
|
||||
subflowInstanceModified = true;
|
||||
} else {
|
||||
node = node_map[wires[j].id];
|
||||
node.wires[wires[j].port] = node.wires[wires[j].port].concat(newWires[i]);
|
||||
modifiedNodes[node.id] = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
Object.keys(modifiedNodes).forEach(function(id) {
|
||||
var node = modifiedNodes[id];
|
||||
subflowNode.instanceNodes[id].updateWires(node.wires);
|
||||
});
|
||||
if (subflowInstanceModified) {
|
||||
subflowNode._updateWires(subflowInstance.wires);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nodes.push(subflowNode);
|
||||
|
||||
// Wire the subflow outputs
|
||||
if (sf.config.out) {
|
||||
var modifiedNodes = {};
|
||||
for (i=0;i<sf.config.out.length;i++) {
|
||||
wires = sf.config.out[i].wires;
|
||||
for (j=0;j<wires.length;j++) {
|
||||
if (wires[j].id === sf.config.id) {
|
||||
// A subflow input wired straight to a subflow output
|
||||
subflowInstance.wires[wires[j].port] = subflowInstance.wires[wires[j].port].concat(sfn.wires[i])
|
||||
subflowNode._updateWires(subflowInstance.wires);
|
||||
} else {
|
||||
node = node_map[wires[j].id];
|
||||
modifiedNodes[node.id] = node;
|
||||
if (!node._originalWires) {
|
||||
node._originalWires = clone(node.wires);
|
||||
}
|
||||
node.wires[wires[j].port] = (node.wires[wires[j].port]||[]).concat(sfn.wires[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Instantiate the nodes
|
||||
for (i=0;i<newNodes.length;i++) {
|
||||
node = newNodes[i];
|
||||
var type = node.type;
|
||||
|
||||
var m = /^subflow:(.+)$/.exec(type);
|
||||
if (!m) {
|
||||
nodes.push(createNode(type,node));
|
||||
} else {
|
||||
var subflowId = m[1];
|
||||
nodes = nodes.concat(createSubflow(subflows[subflowId],node,subflows));
|
||||
}
|
||||
}
|
||||
|
||||
subflowNode.instanceNodes = {};
|
||||
|
||||
nodes.forEach(function(node) {
|
||||
subflowNode.instanceNodes[node.id] = node;
|
||||
});
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
|
||||
function diffNodeConfigs(oldNode,newNode) {
|
||||
if (oldNode == null) {
|
||||
return true;
|
||||
} else {
|
||||
for (var p in newNode) {
|
||||
if (newNode.hasOwnProperty(p) && p != "x" && p != "y" && p != "wires") {
|
||||
if (!redUtil.compareObjects(oldNode[p],newNode[p])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function createCatchNodeMap(nodes) {
|
||||
var catchNodes = {};
|
||||
var subflowInstances = {};
|
||||
var id;
|
||||
/*
|
||||
- a catchNode with same z as error node
|
||||
- if error occurs on a subflow without catchNode, look at z of subflow instance
|
||||
*/
|
||||
for (id in nodes) {
|
||||
if (nodes.hasOwnProperty(id)) {
|
||||
if (nodes[id].type === "catch") {
|
||||
catchNodes[nodes[id].z] = nodes[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (id in nodes) {
|
||||
if (nodes.hasOwnProperty(id)) {
|
||||
var m = /^subflow:(.+)$/.exec(nodes[id].type);
|
||||
if (m) {
|
||||
subflowInstances[id] = nodes[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (id in subflowInstances) {
|
||||
if (subflowInstances.hasOwnProperty(id)) {
|
||||
var z = id;
|
||||
while(subflowInstances[z]) {
|
||||
var sfi = subflowInstances[z];
|
||||
if (!catchNodes[z]) {
|
||||
z = sfi.z;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (catchNodes[z]) {
|
||||
catchNodes[id] = catchNodes[z];
|
||||
}
|
||||
}
|
||||
}
|
||||
return catchNodes;
|
||||
}
|
||||
|
||||
var subflowInstanceRE = /^subflow:(.+)$/;
|
||||
|
||||
function Flow(config) {
|
||||
|
||||
this.activeNodes = {};
|
||||
this.subflowInstanceNodes = {};
|
||||
this.catchNodeMap = {};
|
||||
this.started = false;
|
||||
|
||||
this.parseConfig(config);
|
||||
|
||||
}
|
||||
|
||||
Flow.prototype.parseConfig = function(config) {
|
||||
var i;
|
||||
var nodeConfig;
|
||||
var nodeType;
|
||||
|
||||
this.config = config;
|
||||
|
||||
this.allNodes = {};
|
||||
|
||||
this.nodes = {};
|
||||
this.subflows = {};
|
||||
|
||||
this.configNodes = {};
|
||||
|
||||
var unknownTypes = {};
|
||||
|
||||
for (i=0;i<this.config.length;i++) {
|
||||
nodeConfig = this.config[i];
|
||||
nodeType = nodeConfig.type;
|
||||
this.allNodes[nodeConfig.id] = nodeConfig;
|
||||
if (nodeType == "subflow") {
|
||||
this.subflows[nodeConfig.id] = {
|
||||
type: "subflow",
|
||||
config: nodeConfig,
|
||||
nodes: []
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//console.log("Known subflows:",Object.keys(this.subflows));
|
||||
for (i=0;i<this.config.length;i++) {
|
||||
nodeConfig = this.config[i];
|
||||
|
||||
|
||||
nodeType = nodeConfig.type;
|
||||
|
||||
if (nodeConfig.credentials) {
|
||||
delete nodeConfig.credentials;
|
||||
}
|
||||
|
||||
if (nodeType != "tab" && nodeType != "subflow") {
|
||||
var m = subflowInstanceRE.exec(nodeType);
|
||||
if ((m && !this.subflows[m[1]]) || (!m && !typeRegistry.get(nodeType))) {
|
||||
// This is an unknown subflow or an unknown type
|
||||
unknownTypes[nodeType] = true;
|
||||
} else {
|
||||
var nodeInfo = {
|
||||
type: nodeType,
|
||||
config:nodeConfig
|
||||
}
|
||||
if (m) {
|
||||
nodeInfo.subflow = m[1];
|
||||
}
|
||||
if (this.subflows[nodeConfig.z]) {
|
||||
this.subflows[nodeConfig.z].nodes.push(nodeInfo);
|
||||
} else {
|
||||
this.nodes[nodeConfig.id] = nodeInfo;
|
||||
}
|
||||
for (var prop in nodeConfig) {
|
||||
if (nodeConfig.hasOwnProperty(prop) &&
|
||||
prop != "type" &&
|
||||
prop != "id" &&
|
||||
prop != "z" &&
|
||||
prop != "wires" &&
|
||||
this.allNodes[nodeConfig[prop]]) {
|
||||
this.configNodes[nodeConfig[prop]] = this.allNodes[nodeConfig[prop]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//console.log("NODES");
|
||||
//for (i in this.nodes) {
|
||||
// if (this.nodes.hasOwnProperty(i)) {
|
||||
// console.log(" ",i,this.nodes[i].type,this.nodes[i].config.name||"");
|
||||
// }
|
||||
//}
|
||||
//console.log("SUBFLOWS");
|
||||
//for (i in this.subflows) {
|
||||
// if (this.subflows.hasOwnProperty(i)) {
|
||||
// console.log(" ",i,this.subflows[i].type,this.subflows[i].config.name||"");
|
||||
// for (var j=0;j<this.subflows[i].nodes.length;j++) {
|
||||
// console.log(" ",this.subflows[i].nodes[j].config.id,this.subflows[i].nodes[j].type,this.subflows[i].nodes[j].config.name||"");
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
this.missingTypes = Object.keys(unknownTypes);
|
||||
}
|
||||
|
||||
Flow.prototype.start = function(configDiff) {
|
||||
if (configDiff) {
|
||||
for (var j=0;j<configDiff.rewire.length;j++) {
|
||||
var rewireNode = this.activeNodes[configDiff.rewire[j]];
|
||||
if (rewireNode) {
|
||||
rewireNode.updateWires(this.allNodes[rewireNode.id].wires);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.started = true;
|
||||
if (this.missingTypes.length > 0) {
|
||||
throw new Error(Log._("nodes.flow.missing-types"));
|
||||
}
|
||||
events.emit("nodes-starting");
|
||||
|
||||
var id;
|
||||
var node;
|
||||
|
||||
for (id in this.configNodes) {
|
||||
if (this.configNodes.hasOwnProperty(id)) {
|
||||
node = this.configNodes[id];
|
||||
if (!this.activeNodes[id]) {
|
||||
this.activeNodes[id] = createNode(node.type,node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (id in this.nodes) {
|
||||
if (this.nodes.hasOwnProperty(id)) {
|
||||
node = this.nodes[id];
|
||||
if (!node.subflow) {
|
||||
if (!this.activeNodes[id]) {
|
||||
this.activeNodes[id] = createNode(node.type,node.config);
|
||||
//console.log(id,"created");
|
||||
} else {
|
||||
//console.log(id,"already running");
|
||||
}
|
||||
} else {
|
||||
if (!this.subflowInstanceNodes[id]) {
|
||||
var nodes = createSubflow(this.subflows[node.subflow],node.config,this.subflows);
|
||||
this.subflowInstanceNodes[id] = nodes.map(function(n) { return n.id});
|
||||
for (var i=0;i<nodes.length;i++) {
|
||||
this.activeNodes[nodes[i].id] = nodes[i];
|
||||
}
|
||||
//console.log(id,"(sf) created");
|
||||
} else {
|
||||
//console.log(id,"(sf) already running");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.catchNodeMap = createCatchNodeMap(this.activeNodes);
|
||||
|
||||
credentials.clean(this.config);
|
||||
events.emit("nodes-started");
|
||||
}
|
||||
|
||||
Flow.prototype.stop = function(configDiff) {
|
||||
var nodeList;
|
||||
|
||||
if (configDiff) {
|
||||
nodeList = configDiff.stop;
|
||||
} else {
|
||||
nodeList = Object.keys(this.activeNodes);
|
||||
}
|
||||
var flow = this;
|
||||
return when.promise(function(resolve) {
|
||||
events.emit("nodes-stopping");
|
||||
var promises = [];
|
||||
for (var i=0;i<nodeList.length;i++) {
|
||||
var node = flow.activeNodes[nodeList[i]];
|
||||
if (node) {
|
||||
try {
|
||||
var p = node.close();
|
||||
if (p) {
|
||||
promises.push(p);
|
||||
}
|
||||
} catch(err) {
|
||||
node.error(err);
|
||||
}
|
||||
delete flow.subflowInstanceNodes[nodeList[i]];
|
||||
delete flow.activeNodes[nodeList[i]];
|
||||
}
|
||||
}
|
||||
when.settle(promises).then(function() {
|
||||
events.emit("nodes-stopped");
|
||||
flow.started = false;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Flow.prototype.getMissingTypes = function() {
|
||||
return this.missingTypes;
|
||||
}
|
||||
|
||||
Flow.prototype.typeRegistered = function(type) {
|
||||
if (this.missingTypes.length > 0) {
|
||||
var i = this.missingTypes.indexOf(type);
|
||||
if (i != -1) {
|
||||
this.missingTypes.splice(i,1);
|
||||
if (this.missingTypes.length === 0 && this.started) {
|
||||
this.start();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
Flow.prototype.getNode = function(id) {
|
||||
return this.activeNodes[id];
|
||||
}
|
||||
|
||||
Flow.prototype.getFlow = function() {
|
||||
//console.log(this.config);
|
||||
return this.config;
|
||||
}
|
||||
|
||||
Flow.prototype.eachNode = function(callback) {
|
||||
for (var id in this.activeNodes) {
|
||||
if (this.activeNodes.hasOwnProperty(id)) {
|
||||
callback(this.activeNodes[id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Flow.prototype.diffConfig = function(config,type) {
|
||||
|
||||
var activeNodesToStop = [];
|
||||
var nodesToRewire = [];
|
||||
|
||||
if (type && type!="full") {
|
||||
var diff = diffFlow(this,config);
|
||||
//var diff = {
|
||||
// deleted:[]
|
||||
// changed:[]
|
||||
// linked:[]
|
||||
// wiringChanged: []
|
||||
//}
|
||||
|
||||
var nodesToStop = [];
|
||||
nodesToRewire = diff.wiringChanged;
|
||||
|
||||
if (type == "nodes") {
|
||||
nodesToStop = diff.deleted.concat(diff.changed);
|
||||
} else if (type == "flows") {
|
||||
nodesToStop = diff.deleted.concat(diff.changed).concat(diff.linked);
|
||||
}
|
||||
|
||||
for (var i=0;i<nodesToStop.length;i++) {
|
||||
var id = nodesToStop[i];
|
||||
if (this.subflowInstanceNodes[id]) {
|
||||
activeNodesToStop = activeNodesToStop.concat(this.subflowInstanceNodes[id]);
|
||||
} else if (this.activeNodes[id]) {
|
||||
activeNodesToStop.push(id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
activeNodesToStop = Object.keys(this.activeNodes);
|
||||
}
|
||||
|
||||
return {
|
||||
type: type,
|
||||
stop: activeNodesToStop,
|
||||
rewire: nodesToRewire,
|
||||
config: config
|
||||
}
|
||||
}
|
||||
|
||||
function diffFlow(flow,config) {
|
||||
|
||||
//if (!flow.started) {
|
||||
// throw new Error("Cannot diff an unstarted flow");
|
||||
//}
|
||||
var flowNodes = {};
|
||||
var changedNodes = {};
|
||||
var deletedNodes = {};
|
||||
var deletedSubflows = {};
|
||||
var deletedTabs = {};
|
||||
var linkChangedNodes = {};
|
||||
|
||||
var activeLinks = {};
|
||||
var newLinks = {};
|
||||
|
||||
var changedSubflowStack = [];
|
||||
var changedSubflows = {};
|
||||
|
||||
var buildNodeLinks = function(nodeLinks,n,nodes) {
|
||||
nodeLinks[n.id] = nodeLinks[n.id] || [];
|
||||
if (n.wires) {
|
||||
for (var j=0;j<n.wires.length;j++) {
|
||||
var wires = n.wires[j];
|
||||
for (var k=0;k<wires.length;k++) {
|
||||
nodeLinks[n.id].push(wires[k]);
|
||||
var nn = nodes[wires[k]];
|
||||
if (nn) {
|
||||
nodeLinks[nn.id] = nodeLinks[nn.id] || [];
|
||||
nodeLinks[nn.id].push(n.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config.forEach(function(node) {
|
||||
flowNodes[node.id] = node;
|
||||
});
|
||||
|
||||
config.forEach(function(node) {
|
||||
var changed = false;
|
||||
if (node.credentials) {
|
||||
changed = true;
|
||||
delete node.credentials;
|
||||
} else {
|
||||
changed = diffNodeConfigs(flow.allNodes[node.id],node);
|
||||
if (!changed) {
|
||||
if (flowNodes[node.z] && flowNodes[node.z].type == "subflow") {
|
||||
var originalNode = flow.allNodes[node.id];
|
||||
if (originalNode && !redUtil.compareObjects(originalNode.wires,node.wires)) {
|
||||
// This is a node in a subflow whose wiring has changed. Mark subflow as changed
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
changedNodes[node.id] = node;
|
||||
}
|
||||
});
|
||||
|
||||
flow.config.forEach(function(node) {
|
||||
if (!flowNodes[node.id]) {
|
||||
if (node.type === "tab") {
|
||||
deletedTabs[node.id] = node;
|
||||
} else if (node.type === "subflow") {
|
||||
deletedSubflows[node.id] = node;
|
||||
} else {
|
||||
deletedNodes[node.id] = node;
|
||||
}
|
||||
}
|
||||
buildNodeLinks(activeLinks,node,flow.allNodes);
|
||||
});
|
||||
|
||||
flow.config.forEach(function(node) {
|
||||
for (var prop in node) {
|
||||
if (node.hasOwnProperty(prop) && prop != "z" && prop != "id" && prop != "wires") {
|
||||
// This node has a property that references a changed node
|
||||
// Assume it is a config node change and mark this node as
|
||||
// changed.
|
||||
if (changedNodes[node[prop]]) {
|
||||
changedNodes[node.id] = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var checkSubflowMembership = function(nodes,id) {
|
||||
var node = nodes[id];
|
||||
if (node) {
|
||||
if (node.type == "subflow") {
|
||||
changedSubflows[id] = node;
|
||||
changedSubflowStack.push(id);
|
||||
} else if (nodes[node.z] && nodes[node.z].type == "subflow") {
|
||||
if (!changedSubflows[node.z]) {
|
||||
changedSubflows[node.z] = nodes[node.z];
|
||||
changedSubflowStack.push(node.z);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Object.keys(changedNodes).forEach(function(n) { checkSubflowMembership(flowNodes,n)});
|
||||
Object.keys(deletedNodes).forEach(function(n) { checkSubflowMembership(flow.allNodes,n)});
|
||||
|
||||
while (changedSubflowStack.length > 0) {
|
||||
var subflowId = changedSubflowStack.pop();
|
||||
|
||||
config.forEach(function(node) {
|
||||
if (node.type == "subflow:"+subflowId) {
|
||||
if (!changedNodes[node.id]) {
|
||||
changedNodes[node.id] = node;
|
||||
checkSubflowMembership(flowNodes,node.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
config.forEach(function(node) {
|
||||
buildNodeLinks(newLinks,node,flowNodes);
|
||||
});
|
||||
|
||||
var markLinkedNodes = function(linkChanged,otherChangedNodes,linkMap,allNodes) {
|
||||
var stack = Object.keys(changedNodes).concat(Object.keys(otherChangedNodes));
|
||||
var visited = {};
|
||||
|
||||
while(stack.length > 0) {
|
||||
var id = stack.pop();
|
||||
var linkedNodes = linkMap[id];
|
||||
if (linkedNodes) {
|
||||
for (var i=0;i<linkedNodes.length;i++) {
|
||||
var linkedNodeId = linkedNodes[i];
|
||||
if (changedNodes[linkedNodeId] || deletedNodes[linkedNodeId] || otherChangedNodes[linkedNodeId] || linkChanged[linkedNodeId]) {
|
||||
// Do nothing - this linked node is already marked as changed, so will get done
|
||||
} else {
|
||||
linkChanged[linkedNodeId] = true;
|
||||
stack.push(linkedNodeId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
markLinkedNodes(linkChangedNodes,{},newLinks,flowNodes);
|
||||
markLinkedNodes(linkChangedNodes,{},activeLinks,flow.allNodes);
|
||||
|
||||
var modifiedLinkNodes = {};
|
||||
|
||||
config.forEach(function(node) {
|
||||
if (!changedNodes[node.id]) {
|
||||
// only concerned about unchanged nodes whose wiring may have changed
|
||||
var newNodeLinks = newLinks[node.id];
|
||||
var oldNodeLinks = activeLinks[node.id];
|
||||
|
||||
var newLinkMap = {};
|
||||
newNodeLinks.forEach(function(l) { newLinkMap[l] = (newLinkMap[l]||0)+1;});
|
||||
|
||||
var oldLinkMap = {};
|
||||
oldNodeLinks.forEach(function(l) { oldLinkMap[l] = (oldLinkMap[l]||0)+1;});
|
||||
|
||||
newNodeLinks.forEach(function(link) {
|
||||
if (newLinkMap[link] != oldLinkMap[link]) {
|
||||
modifiedLinkNodes[node.id] = node;
|
||||
linkChangedNodes[node.id] = node;
|
||||
if (!changedNodes[link] && !deletedNodes[link]) {
|
||||
modifiedLinkNodes[link] = flowNodes[link];
|
||||
linkChangedNodes[link] = flowNodes[link];
|
||||
}
|
||||
}
|
||||
});
|
||||
oldNodeLinks.forEach(function(link) {
|
||||
if (newLinkMap[link] != oldLinkMap[link]) {
|
||||
modifiedLinkNodes[node.id] = node;
|
||||
linkChangedNodes[node.id] = node;
|
||||
if (!changedNodes[link] && !deletedNodes[link]) {
|
||||
modifiedLinkNodes[link] = flowNodes[link];
|
||||
linkChangedNodes[link] = flowNodes[link];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
markLinkedNodes(linkChangedNodes,modifiedLinkNodes,newLinks,flowNodes);
|
||||
|
||||
// config.forEach(function(n) {
|
||||
// console.log((changedNodes[n.id]!=null)?"[C]":"[ ]",(linkChangedNodes[n.id]!=null)?"[L]":"[ ]","[ ]",n.id,n.type,n.name);
|
||||
// });
|
||||
//
|
||||
// Object.keys(deletedNodes).forEach(function(id) {
|
||||
// var n = flow.allNodes[id];
|
||||
// console.log("[ ] [ ] [D]",n.id,n.type);
|
||||
// });
|
||||
var diff = {
|
||||
deleted: Object.keys(deletedNodes).filter(function(id) { return deletedNodes[id].type != "subflow" && (!deletedNodes[id].z || deletedTabs[deletedNodes[id].z] || !(deletedSubflows[deletedNodes[id].z] || flowNodes[deletedNodes[id].z].type == "subflow"))}),
|
||||
changed: Object.keys(changedNodes).filter(function(id) { return changedNodes[id].type != "subflow" && (!changedNodes[id].z || flowNodes[changedNodes[id].z].type != "subflow")}),
|
||||
linked: Object.keys(linkChangedNodes).filter(function(id) { return linkChangedNodes[id].type != "subflow" && (!linkChangedNodes[id].z || flowNodes[linkChangedNodes[id].z].type != "subflow")}),
|
||||
wiringChanged: []
|
||||
}
|
||||
|
||||
config.forEach(function(n) {
|
||||
if (!flowNodes[n.z] || flowNodes[n.z].type != "subflow") {
|
||||
var originalNode = flow.allNodes[n.id];
|
||||
if (originalNode && !redUtil.compareObjects(originalNode.wires,n.wires)) {
|
||||
diff.wiringChanged.push(n.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
|
||||
Flow.prototype.handleError = function(node,logMessage,msg) {
|
||||
var targetCatchNode = null;
|
||||
if (this.catchNodeMap[node.z]) {
|
||||
targetCatchNode = this.catchNodeMap[node.z];
|
||||
} else if (this.activeNodes[node.z] && this.catchNodeMap[this.activeNodes[node.z].z]) {
|
||||
targetCatchNode = this.catchNodeMap[this.activeNodes[node.z].z];
|
||||
}
|
||||
|
||||
if (targetCatchNode) {
|
||||
var count = 1;
|
||||
if (msg && msg.hasOwnProperty("error")) {
|
||||
if (msg.error.hasOwnProperty("source")) {
|
||||
if (msg.error.source.id === node.id) {
|
||||
count = msg.error.source.count+1;
|
||||
if (count === 10) {
|
||||
node.warn(Log._("nodes.flow.error-loop"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var errorMessage;
|
||||
if (msg) {
|
||||
errorMessage = redUtil.cloneMessage(msg);
|
||||
} else {
|
||||
errorMessage = {};
|
||||
}
|
||||
if (errorMessage.hasOwnProperty("error")) {
|
||||
errorMessage._error = errorMessage.error;
|
||||
}
|
||||
errorMessage.error = {
|
||||
message: logMessage.toString(),
|
||||
source: {
|
||||
id: node.id,
|
||||
type: node.type,
|
||||
count: count
|
||||
}
|
||||
};
|
||||
targetCatchNode.receive(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = Flow;
|
@@ -33,12 +33,16 @@ function Node(n) {
|
||||
if (n.name) {
|
||||
this.name = n.name;
|
||||
}
|
||||
if (n._alias) {
|
||||
this._alias = n._alias;
|
||||
}
|
||||
this.updateWires(n.wires);
|
||||
}
|
||||
|
||||
util.inherits(Node, EventEmitter);
|
||||
|
||||
Node.prototype.updateWires = function(wires) {
|
||||
//console.log("UPDATE",this.id);
|
||||
this.wires = wires || [];
|
||||
delete this._wire;
|
||||
|
||||
@@ -189,7 +193,7 @@ Node.prototype.receive = function(msg) {
|
||||
msg._msgid = redUtil.generateId();
|
||||
}
|
||||
this.metric("receive",msg);
|
||||
try {
|
||||
try {
|
||||
this.emit("input", msg);
|
||||
} catch(err) {
|
||||
this.error(err,msg);
|
||||
@@ -247,5 +251,6 @@ Node.prototype.metric = function(eventname, msg, metricValue) {
|
||||
*/
|
||||
Node.prototype.status = function(status) {
|
||||
comms.publish("status/" + this.id, status, true);
|
||||
flows.handleStatus(this,status);
|
||||
};
|
||||
module.exports = Node;
|
||||
|
@@ -23,52 +23,12 @@ var needsPermission = require("../api/auth").needsPermission;
|
||||
var credentialCache = {};
|
||||
var storage = null;
|
||||
var credentialsDef = {};
|
||||
var redApp = null;
|
||||
|
||||
/**
|
||||
* Adds an HTTP endpoint to allow look up of credentials for a given node id.
|
||||
*/
|
||||
function registerEndpoint(type) {
|
||||
redApp.get('/credentials/' + type + '/:id', needsPermission(type+".read"), function (req, res) {
|
||||
// TODO: This could be a generic endpoint with the type value
|
||||
// parameterised.
|
||||
//
|
||||
// TODO: It should verify the given node id is of the type specified -
|
||||
// but that would add a dependency from this module to the
|
||||
// registry module that knows about node types.
|
||||
var nodeType = type;
|
||||
var nodeID = req.params.id;
|
||||
|
||||
var credentials = credentialCache[nodeID];
|
||||
if (credentials === undefined) {
|
||||
res.json({});
|
||||
return;
|
||||
}
|
||||
var definition = credentialsDef[nodeType];
|
||||
|
||||
var sendCredentials = {};
|
||||
for (var cred in definition) {
|
||||
if (definition.hasOwnProperty(cred)) {
|
||||
if (definition[cred].type == "password") {
|
||||
var key = 'has_' + cred;
|
||||
sendCredentials[key] = credentials[cred] != null && credentials[cred] !== '';
|
||||
continue;
|
||||
}
|
||||
sendCredentials[cred] = credentials[cred] || '';
|
||||
}
|
||||
}
|
||||
res.json(sendCredentials);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
init: function (_storage,_app) {
|
||||
init: function (_storage) {
|
||||
storage = _storage;
|
||||
redApp = _app;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Loads the credentials from storage.
|
||||
*/
|
||||
@@ -79,7 +39,7 @@ module.exports = {
|
||||
log.warn(log._("nodes.credentials.error",{message: err}));
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Adds a set of credentials for the given node id.
|
||||
* @param id the node id for the credentials
|
||||
@@ -118,7 +78,7 @@ module.exports = {
|
||||
clean: function (config) {
|
||||
var existingIds = {};
|
||||
config.forEach(function(n) {
|
||||
existingIds[n.id] = true;
|
||||
existingIds[n.id] = true;
|
||||
});
|
||||
var deletedCredentials = false;
|
||||
for (var c in credentialCache) {
|
||||
@@ -135,7 +95,7 @@ module.exports = {
|
||||
return when.resolve();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Registers a node credential definition.
|
||||
* @param type the node type
|
||||
@@ -144,16 +104,15 @@ module.exports = {
|
||||
register: function (type, definition) {
|
||||
var dashedType = type.replace(/\s+/g, '-');
|
||||
credentialsDef[dashedType] = definition;
|
||||
registerEndpoint(dashedType);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Extracts and stores any credential updates in the provided node.
|
||||
* The provided node may have a .credentials property that contains
|
||||
* new credentials for the node.
|
||||
* This function loops through the credentials in the definition for
|
||||
* the node-type and applies any of the updates provided in the node.
|
||||
*
|
||||
*
|
||||
* This function does not save the credentials to disk as it is expected
|
||||
* to be called multiple times when a new flow is deployed.
|
||||
*
|
||||
@@ -171,7 +130,7 @@ module.exports = {
|
||||
log.warn(log._("nodes.credentials.not-registered",{type:nodeType}));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (var cred in definition) {
|
||||
if (definition.hasOwnProperty(cred)) {
|
||||
if (newCreds[cred] === undefined) {
|
||||
@@ -191,7 +150,7 @@ module.exports = {
|
||||
delete node.credentials;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Saves the credentials to storage
|
||||
* @return a promise for the saving of credentials to storage
|
||||
@@ -199,7 +158,7 @@ module.exports = {
|
||||
save: function () {
|
||||
return storage.saveCredentials(credentialCache);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Gets the credential definition for the given node type
|
||||
* @param type the node type
|
||||
|
@@ -1,196 +0,0 @@
|
||||
/**
|
||||
* 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 clone = require("clone");
|
||||
var when = require("when");
|
||||
|
||||
var typeRegistry = require("./registry");
|
||||
var credentials = require("./credentials");
|
||||
var Flow = require("./Flow");
|
||||
var log = require("../log");
|
||||
|
||||
var events = require("../events");
|
||||
var redUtil = require("../util");
|
||||
var storage = null;
|
||||
var settings = null;
|
||||
var deprecated = require("./deprecated");
|
||||
|
||||
var activeFlow = null;
|
||||
|
||||
var nodes = {};
|
||||
var subflows = {};
|
||||
var activeConfig = [];
|
||||
var activeConfigNodes = {};
|
||||
|
||||
events.on('type-registered',function(type) {
|
||||
if (activeFlow && activeFlow.typeRegistered(type)) {
|
||||
log.info(log._("nodes.flows.registered-missing", {type:type}));
|
||||
}
|
||||
});
|
||||
|
||||
var flowNodes = module.exports = {
|
||||
init: function(_settings, _storage) {
|
||||
settings = _settings;
|
||||
storage = _storage;
|
||||
},
|
||||
|
||||
/**
|
||||
* Load the current activeConfig from storage and start it running
|
||||
* @return a promise for the loading of the config
|
||||
*/
|
||||
load: function() {
|
||||
return storage.getFlows().then(function(flows) {
|
||||
return credentials.load().then(function() {
|
||||
activeFlow = new Flow(flows);
|
||||
flowNodes.startFlows();
|
||||
});
|
||||
}).otherwise(function(err) {
|
||||
log.warn(log._("nodes.flows.error",{message:err.toString()}));
|
||||
console.log(err.stack);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a node
|
||||
* @param i the node id
|
||||
* @return the node
|
||||
*/
|
||||
get: function(i) {
|
||||
return activeFlow.getNode(i);
|
||||
},
|
||||
|
||||
eachNode: function(cb) {
|
||||
activeFlow.eachNode(cb);
|
||||
},
|
||||
|
||||
/**
|
||||
* @return the active configuration
|
||||
*/
|
||||
getFlows: function() {
|
||||
return activeFlow.getFlow();
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the current active config.
|
||||
* @param config the configuration to enable
|
||||
* @param type the type of deployment to do: full (default), nodes, flows
|
||||
* @return a promise for the starting of the new flow
|
||||
*/
|
||||
setFlows: function (config,type) {
|
||||
|
||||
type = type||"full";
|
||||
|
||||
var credentialsChanged = false;
|
||||
|
||||
var credentialSavePromise = null;
|
||||
|
||||
|
||||
// Clone config and extract credentials prior to saving
|
||||
// Original config needs to retain credentials so that flow.applyConfig
|
||||
// knows which nodes have had changes.
|
||||
var cleanConfig = clone(config);
|
||||
cleanConfig.forEach(function(node) {
|
||||
if (node.credentials) {
|
||||
credentials.extract(node);
|
||||
credentialsChanged = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (credentialsChanged) {
|
||||
credentialSavePromise = credentials.save();
|
||||
} else {
|
||||
credentialSavePromise = when.resolve();
|
||||
}
|
||||
if (type=="full") {
|
||||
return credentialSavePromise
|
||||
.then(function() { return storage.saveFlows(cleanConfig);})
|
||||
.then(function() { return flowNodes.stopFlows(); })
|
||||
.then(function() { activeFlow = new Flow(config); flowNodes.startFlows();});
|
||||
} else {
|
||||
return credentialSavePromise
|
||||
.then(function() { return storage.saveFlows(cleanConfig);})
|
||||
.then(function() {
|
||||
var configDiff = activeFlow.diffConfig(config,type);
|
||||
return flowNodes.stopFlows(configDiff).then(function() {
|
||||
activeFlow.parseConfig(config);
|
||||
flowNodes.startFlows(configDiff);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
startFlows: function(configDiff) {
|
||||
if (configDiff) {
|
||||
log.info(log._("nodes.flows.starting-modified-"+configDiff.type));
|
||||
} else {
|
||||
log.info(log._("nodes.flows.starting-flows"));
|
||||
}
|
||||
try {
|
||||
activeFlow.start(configDiff);
|
||||
if (configDiff) {
|
||||
log.info(log._("nodes.flows.started-modified-"+configDiff.type));
|
||||
} else {
|
||||
log.info(log._("nodes.flows.started-flows"));
|
||||
}
|
||||
} catch(err) {
|
||||
var missingTypes = activeFlow.getMissingTypes();
|
||||
if (missingTypes.length > 0) {
|
||||
log.info(log._("nodes.flows.missing-types"));
|
||||
var knownUnknowns = 0;
|
||||
for (var i=0;i<missingTypes.length;i++) {
|
||||
var type = missingTypes[i];
|
||||
var info = deprecated.get(type);
|
||||
if (info) {
|
||||
log.info(log._("nodes.flows.missing-type-provided",{type:missingTypes[i],module:info.module}));
|
||||
knownUnknowns += 1;
|
||||
} else {
|
||||
log.info(" - "+missingTypes[i]);
|
||||
}
|
||||
}
|
||||
if (knownUnknowns > 0) {
|
||||
log.info(log._("nodes.flows.missing-type-install-1"));
|
||||
log.info(" npm install <module name>");
|
||||
log.info(log._("nodes.flows.missing-type-install-2"));
|
||||
log.info(" "+settings.userDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
stopFlows: function(configDiff) {
|
||||
if (configDiff) {
|
||||
log.info(log._("nodes.flows.stopping-modified-"+configDiff.type));
|
||||
} else {
|
||||
log.info(log._("nodes.flows.stopping-flows"));
|
||||
}
|
||||
if (activeFlow) {
|
||||
return activeFlow.stop(configDiff).then(function() {
|
||||
if (configDiff) {
|
||||
log.info(log._("nodes.flows.stopped-modified-"+configDiff.type));
|
||||
} else {
|
||||
log.info(log._("nodes.flows.stopped-flows"));
|
||||
}
|
||||
return;
|
||||
});
|
||||
} else {
|
||||
log.info(log._("nodes.flows.stopped"));
|
||||
return;
|
||||
}
|
||||
},
|
||||
handleError: function(node,logMessage,msg) {
|
||||
activeFlow.handleError(node,logMessage,msg);
|
||||
}
|
||||
};
|
||||
|
||||
var activeFlow = null;
|
466
red/nodes/flows/Flow.js
Normal file
466
red/nodes/flows/Flow.js
Normal file
@@ -0,0 +1,466 @@
|
||||
/**
|
||||
* 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 when = require("when");
|
||||
var clone = require("clone");
|
||||
var typeRegistry = require("../registry");
|
||||
var Log = require("../../log");
|
||||
var redUtil = require("../../util");
|
||||
var flowUtil = require("./util");
|
||||
|
||||
function Flow(global,flow) {
|
||||
if (typeof flow === 'undefined') {
|
||||
flow = global;
|
||||
}
|
||||
var activeNodes = {};
|
||||
var subflowInstanceNodes = {};
|
||||
var catchNodeMap = {};
|
||||
var statusNodeMap = {};
|
||||
|
||||
this.start = function(diff) {
|
||||
var node;
|
||||
var id;
|
||||
catchNodeMap = {};
|
||||
statusNodeMap = {};
|
||||
for (id in flow.configs) {
|
||||
if (flow.configs.hasOwnProperty(id)) {
|
||||
node = flow.configs[id];
|
||||
if (!activeNodes[id]) {
|
||||
activeNodes[id] = createNode(node.type,node);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (diff) {
|
||||
for (var j=0;j<diff.rewired.length;j++) {
|
||||
var rewireNode = activeNodes[diff.rewired[j]];
|
||||
if (rewireNode) {
|
||||
rewireNode.updateWires(flow.nodes[rewireNode.id].wires);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (id in flow.nodes) {
|
||||
if (flow.nodes.hasOwnProperty(id)) {
|
||||
node = flow.nodes[id];
|
||||
if (!node.subflow) {
|
||||
if (!activeNodes[id]) {
|
||||
activeNodes[id] = createNode(node.type,node);
|
||||
}
|
||||
} else {
|
||||
if (!subflowInstanceNodes[id]) {
|
||||
try {
|
||||
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];
|
||||
}
|
||||
} catch(err) {
|
||||
console.log(err.stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (id in activeNodes) {
|
||||
if (activeNodes.hasOwnProperty(id)) {
|
||||
node = activeNodes[id];
|
||||
if (node.type === "catch") {
|
||||
catchNodeMap[node.z] = catchNodeMap[node.z] || [];
|
||||
catchNodeMap[node.z].push(node);
|
||||
} else if (node.type === "status") {
|
||||
statusNodeMap[node.z] = statusNodeMap[node.z] || [];
|
||||
statusNodeMap[node.z].push(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.stop = function(stopList) {
|
||||
return when.promise(function(resolve) {
|
||||
var i;
|
||||
if (stopList) {
|
||||
for (i=0;i<stopList.length;i++) {
|
||||
if (subflowInstanceNodes[stopList[i]]) {
|
||||
// The first in the list is the instance node we already
|
||||
// know about
|
||||
stopList = stopList.concat(subflowInstanceNodes[stopList[i]].slice(1))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
stopList = Object.keys(activeNodes);
|
||||
}
|
||||
var promises = [];
|
||||
for (i=0;i<stopList.length;i++) {
|
||||
var node = activeNodes[stopList[i]];
|
||||
if (node) {
|
||||
delete activeNodes[stopList[i]];
|
||||
if (subflowInstanceNodes[stopList[i]]) {
|
||||
delete subflowInstanceNodes[stopList[i]];
|
||||
}
|
||||
try {
|
||||
var p = node.close();
|
||||
if (p) {
|
||||
promises.push(p);
|
||||
}
|
||||
} catch(err) {
|
||||
node.error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
when.settle(promises).then(function() {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
this.update = function(_global,_flow) {
|
||||
global = _global;
|
||||
flow = _flow;
|
||||
}
|
||||
|
||||
this.getNode = function(id) {
|
||||
return activeNodes[id];
|
||||
}
|
||||
|
||||
this.getActiveNodes = function() {
|
||||
return activeNodes;
|
||||
}
|
||||
|
||||
this.handleStatus = function(node,statusMessage) {
|
||||
var targetStatusNodes = null;
|
||||
var reportingNode = node;
|
||||
var handled = false;
|
||||
while(reportingNode && !handled) {
|
||||
targetStatusNodes = statusNodeMap[reportingNode.z];
|
||||
if (targetStatusNodes) {
|
||||
targetStatusNodes.forEach(function(targetStatusNode) {
|
||||
if (targetStatusNode.scope && targetStatusNode.scope.indexOf(node.id) === -1) {
|
||||
return;
|
||||
}
|
||||
var message = {
|
||||
status: {
|
||||
text: "",
|
||||
source: {
|
||||
id: node.id,
|
||||
type: node.type,
|
||||
name: node.name
|
||||
}
|
||||
}
|
||||
};
|
||||
if (statusMessage.text) {
|
||||
message.status.text = statusMessage.text;
|
||||
}
|
||||
targetStatusNode.receive(message);
|
||||
handled = true;
|
||||
});
|
||||
}
|
||||
if (!handled) {
|
||||
reportingNode = activeNodes[reportingNode.z];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.handleError = function(node,logMessage,msg) {
|
||||
var count = 1;
|
||||
if (msg && msg.hasOwnProperty("error")) {
|
||||
if (msg.error.hasOwnProperty("source")) {
|
||||
if (msg.error.source.id === node.id) {
|
||||
count = msg.error.source.count+1;
|
||||
if (count === 10) {
|
||||
node.warn(Log._("nodes.flow.error-loop"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var targetCatchNodes = null;
|
||||
var throwingNode = node;
|
||||
var handled = false;
|
||||
while (throwingNode && !handled) {
|
||||
targetCatchNodes = catchNodeMap[throwingNode.z];
|
||||
if (targetCatchNodes) {
|
||||
targetCatchNodes.forEach(function(targetCatchNode) {
|
||||
if (targetCatchNode.scope && targetCatchNode.scope.indexOf(throwingNode.id) === -1) {
|
||||
return;
|
||||
}
|
||||
var errorMessage;
|
||||
if (msg) {
|
||||
errorMessage = redUtil.cloneMessage(msg);
|
||||
} else {
|
||||
errorMessage = {};
|
||||
}
|
||||
if (errorMessage.hasOwnProperty("error")) {
|
||||
errorMessage._error = errorMessage.error;
|
||||
}
|
||||
errorMessage.error = {
|
||||
message: logMessage.toString(),
|
||||
source: {
|
||||
id: node.id,
|
||||
type: node.type,
|
||||
name: node.name,
|
||||
count: count
|
||||
}
|
||||
};
|
||||
targetCatchNode.receive(errorMessage);
|
||||
handled = true;
|
||||
});
|
||||
}
|
||||
if (!handled) {
|
||||
throwingNode = activeNodes[throwingNode.z];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
function getID() {
|
||||
return (1+Math.random()*4294967295).toString(16);
|
||||
}
|
||||
|
||||
var EnvVarPropertyRE = /^\$\((\S+)\)$/;
|
||||
|
||||
function mapEnvVarProperties(obj,prop) {
|
||||
if (Buffer.isBuffer(obj[prop])) {
|
||||
return;
|
||||
} else if (Array.isArray(obj[prop])) {
|
||||
for (var i=0;i<obj[prop].length;i++) {
|
||||
mapEnvVarProperties(obj[prop],i);
|
||||
}
|
||||
} else if (typeof obj[prop] === 'string') {
|
||||
var m;
|
||||
if ( (m = EnvVarPropertyRE.exec(obj[prop])) !== null) {
|
||||
if (process.env.hasOwnProperty(m[1])) {
|
||||
obj[prop] = process.env[m[1]];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var p in obj[prop]) {
|
||||
if (obj[prop].hasOwnProperty) {
|
||||
mapEnvVarProperties(obj[prop],p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createNode(type,config) {
|
||||
// console.log("CREATE",type,config.id);
|
||||
var nn = null;
|
||||
var nt = typeRegistry.get(type);
|
||||
if (nt) {
|
||||
var conf = clone(config);
|
||||
delete conf.credentials;
|
||||
for (var p in conf) {
|
||||
if (conf.hasOwnProperty(p)) {
|
||||
mapEnvVarProperties(conf,p);
|
||||
}
|
||||
}
|
||||
try {
|
||||
nn = new nt(conf);
|
||||
}
|
||||
catch (err) {
|
||||
Log.log({
|
||||
level: Log.ERROR,
|
||||
id:conf.id,
|
||||
type: type,
|
||||
msg: err
|
||||
});
|
||||
}
|
||||
} else {
|
||||
Log.error(Log._("nodes.flow.unknown-type", {type:type}));
|
||||
}
|
||||
return nn;
|
||||
}
|
||||
|
||||
function createSubflow(sf,sfn,subflows,globalSubflows,activeNodes) {
|
||||
//console.log("CREATE SUBFLOW",sf.id,sfn.id);
|
||||
var nodes = [];
|
||||
var node_map = {};
|
||||
var newNodes = [];
|
||||
var node;
|
||||
var wires;
|
||||
var i,j,k;
|
||||
|
||||
var createNodeInSubflow = function(def) {
|
||||
node = clone(def);
|
||||
var nid = getID();
|
||||
node_map[node.id] = node;
|
||||
node._alias = node.id;
|
||||
node.id = nid;
|
||||
node.z = sfn.id;
|
||||
newNodes.push(node);
|
||||
}
|
||||
|
||||
// Clone all of the subflow node definitions and give them new IDs
|
||||
for (i in sf.configs) {
|
||||
if (sf.configs.hasOwnProperty(i)) {
|
||||
createNodeInSubflow(sf.configs[i]);
|
||||
}
|
||||
}
|
||||
// Clone all of the subflow node definitions and give them new IDs
|
||||
for (i in sf.nodes) {
|
||||
if (sf.nodes.hasOwnProperty(i)) {
|
||||
createNodeInSubflow(sf.nodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Look for any catch/status nodes and update their scope ids
|
||||
// Update all subflow interior wiring to reflect new node IDs
|
||||
for (i=0;i<newNodes.length;i++) {
|
||||
node = newNodes[i];
|
||||
if (node.wires) {
|
||||
var outputs = node.wires;
|
||||
for (j=0;j<outputs.length;j++) {
|
||||
wires = outputs[j];
|
||||
for (k=0;k<wires.length;k++) {
|
||||
outputs[j][k] = node_map[outputs[j][k]].id
|
||||
}
|
||||
}
|
||||
if ((node.type === 'catch' || node.type === 'status') && node.scope) {
|
||||
node.scope = node.scope.map(function(id) {
|
||||
return node_map[id]?node_map[id].id:""
|
||||
})
|
||||
} else {
|
||||
for (var prop in node) {
|
||||
if (node.hasOwnProperty(prop) && prop !== '_alias') {
|
||||
if (node_map[node[prop]]) {
|
||||
//console.log("Mapped",node.type,node.id,prop,node_map[node[prop]].id);
|
||||
node[prop] = node_map[node[prop]].id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a subflow node to accept inbound messages and route appropriately
|
||||
var Node = require("../Node");
|
||||
var subflowInstance = {
|
||||
id: sfn.id,
|
||||
type: sfn.type,
|
||||
z: sfn.z,
|
||||
name: sfn.name,
|
||||
wires: []
|
||||
}
|
||||
if (sf.in) {
|
||||
subflowInstance.wires = sf.in.map(function(n) { return n.wires.map(function(w) { return node_map[w.id].id;})})
|
||||
subflowInstance._originalWires = clone(subflowInstance.wires);
|
||||
}
|
||||
var subflowNode = new Node(subflowInstance);
|
||||
|
||||
subflowNode.on("input", function(msg) { this.send(msg);});
|
||||
|
||||
|
||||
subflowNode._updateWires = subflowNode.updateWires;
|
||||
|
||||
subflowNode.updateWires = function(newWires) {
|
||||
// Wire the subflow outputs
|
||||
if (sf.out) {
|
||||
var node,wires,i,j;
|
||||
// Restore the original wiring to the internal nodes
|
||||
subflowInstance.wires = clone(subflowInstance._originalWires);
|
||||
for (i=0;i<sf.out.length;i++) {
|
||||
wires = sf.out[i].wires;
|
||||
for (j=0;j<wires.length;j++) {
|
||||
if (wires[j].id != sf.id) {
|
||||
node = node_map[wires[j].id];
|
||||
if (node._originalWires) {
|
||||
node.wires = clone(node._originalWires);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var modifiedNodes = {};
|
||||
var subflowInstanceModified = false;
|
||||
|
||||
for (i=0;i<sf.out.length;i++) {
|
||||
wires = sf.out[i].wires;
|
||||
for (j=0;j<wires.length;j++) {
|
||||
if (wires[j].id === sf.id) {
|
||||
subflowInstance.wires[wires[j].port] = subflowInstance.wires[wires[j].port].concat(newWires[i]);
|
||||
subflowInstanceModified = true;
|
||||
} else {
|
||||
node = node_map[wires[j].id];
|
||||
node.wires[wires[j].port] = node.wires[wires[j].port].concat(newWires[i]);
|
||||
modifiedNodes[node.id] = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
Object.keys(modifiedNodes).forEach(function(id) {
|
||||
var node = modifiedNodes[id];
|
||||
subflowNode.instanceNodes[id].updateWires(node.wires);
|
||||
});
|
||||
if (subflowInstanceModified) {
|
||||
subflowNode._updateWires(subflowInstance.wires);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nodes.push(subflowNode);
|
||||
|
||||
// Wire the subflow outputs
|
||||
if (sf.out) {
|
||||
var modifiedNodes = {};
|
||||
for (i=0;i<sf.out.length;i++) {
|
||||
wires = sf.out[i].wires;
|
||||
for (j=0;j<wires.length;j++) {
|
||||
if (wires[j].id === sf.id) {
|
||||
// A subflow input wired straight to a subflow output
|
||||
subflowInstance.wires[wires[j].port] = subflowInstance.wires[wires[j].port].concat(sfn.wires[i])
|
||||
subflowNode._updateWires(subflowInstance.wires);
|
||||
} else {
|
||||
node = node_map[wires[j].id];
|
||||
modifiedNodes[node.id] = node;
|
||||
if (!node._originalWires) {
|
||||
node._originalWires = clone(node.wires);
|
||||
}
|
||||
node.wires[wires[j].port] = (node.wires[wires[j].port]||[]).concat(sfn.wires[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Instantiate the nodes
|
||||
for (i=0;i<newNodes.length;i++) {
|
||||
node = newNodes[i];
|
||||
var type = node.type;
|
||||
|
||||
var m = /^subflow:(.+)$/.exec(type);
|
||||
if (!m) {
|
||||
var newNode = createNode(type,node);
|
||||
activeNodes[node.id] = newNode;
|
||||
nodes.push(newNode);
|
||||
} else {
|
||||
var subflowId = m[1];
|
||||
nodes = nodes.concat(createSubflow(subflows[subflowId]||globalSubflows[subflowId],node,subflows,globalSubflows,activeNodes));
|
||||
}
|
||||
}
|
||||
|
||||
subflowNode.instanceNodes = {};
|
||||
|
||||
nodes.forEach(function(node) {
|
||||
subflowNode.instanceNodes[node.id] = node;
|
||||
});
|
||||
return nodes;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
create: function(global,conf) {
|
||||
return new Flow(global,conf);
|
||||
}
|
||||
}
|
392
red/nodes/flows/index.js
Normal file
392
red/nodes/flows/index.js
Normal file
@@ -0,0 +1,392 @@
|
||||
/**
|
||||
* 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 clone = require("clone");
|
||||
var when = require("when");
|
||||
|
||||
var Flow = require('./Flow');
|
||||
|
||||
var typeRegistry = require("../registry");
|
||||
var credentials = require("../credentials");
|
||||
|
||||
var flowUtil = require("./util");
|
||||
var log = require("../../log");
|
||||
var events = require("../../events");
|
||||
var redUtil = require("../../util");
|
||||
var deprecated = require("../registry/deprecated");
|
||||
|
||||
var storage = null;
|
||||
var settings = null;
|
||||
|
||||
var activeConfig = null;
|
||||
var activeFlowConfig = null;
|
||||
|
||||
var activeFlows = {};
|
||||
var started = false;
|
||||
|
||||
var activeNodesToFlow = {};
|
||||
var subflowInstanceNodeMap = {};
|
||||
|
||||
var typeEventRegistered = false;
|
||||
|
||||
function init(_settings, _storage) {
|
||||
if (started) {
|
||||
throw new Error("Cannot init without a stop");
|
||||
}
|
||||
settings = _settings;
|
||||
storage = _storage;
|
||||
started = false;
|
||||
if (!typeEventRegistered) {
|
||||
events.on('type-registered',function(type) {
|
||||
if (activeFlowConfig && activeFlowConfig.missingTypes.length > 0) {
|
||||
var i = activeFlowConfig.missingTypes.indexOf(type);
|
||||
if (i != -1) {
|
||||
log.info(log._("nodes.flows.registered-missing", {type:type}));
|
||||
activeFlowConfig.missingTypes.splice(i,1);
|
||||
if (activeFlowConfig.missingTypes.length === 0 && started) {
|
||||
start();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
typeEventRegistered = true;
|
||||
}
|
||||
}
|
||||
function load() {
|
||||
return storage.getFlows().then(function(flows) {
|
||||
return credentials.load().then(function() {
|
||||
return setConfig(flows,"load");
|
||||
});
|
||||
}).otherwise(function(err) {
|
||||
log.warn(log._("nodes.flows.error",{message:err.toString()}));
|
||||
console.log(err.stack);
|
||||
});
|
||||
}
|
||||
|
||||
function setConfig(_config,type) {
|
||||
var config = clone(_config);
|
||||
type = type||"full";
|
||||
|
||||
var credentialsChanged = false;
|
||||
var credentialSavePromise = null;
|
||||
var configSavePromise = null;
|
||||
|
||||
var diff;
|
||||
var newFlowConfig = flowUtil.parseConfig(clone(config));
|
||||
if (type !== 'full' && type !== 'load') {
|
||||
diff = flowUtil.diffConfigs(activeFlowConfig,newFlowConfig);
|
||||
}
|
||||
config.forEach(function(node) {
|
||||
if (node.credentials) {
|
||||
credentials.extract(node);
|
||||
credentialsChanged = true;
|
||||
}
|
||||
});
|
||||
if (credentialsChanged) {
|
||||
credentialSavePromise = credentials.save();
|
||||
} else {
|
||||
credentialSavePromise = when.resolve();
|
||||
}
|
||||
if (type === 'load') {
|
||||
configSavePromise = credentialSavePromise;
|
||||
type = 'full';
|
||||
} else {
|
||||
configSavePromise = credentialSavePromise.then(function() {
|
||||
return storage.saveFlows(config);
|
||||
});
|
||||
}
|
||||
|
||||
return configSavePromise
|
||||
.then(function() {
|
||||
activeConfig = config;
|
||||
activeFlowConfig = newFlowConfig;
|
||||
return credentials.clean(activeConfig).then(function() {
|
||||
if (started) {
|
||||
return stop(type,diff).then(function() {
|
||||
start(type,diff);
|
||||
}).otherwise(function(err) {
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getNode(id) {
|
||||
var node;
|
||||
if (activeNodesToFlow[id]) {
|
||||
return activeFlows[activeNodesToFlow[id]].getNode(id);
|
||||
}
|
||||
for (var flowId in activeFlows) {
|
||||
if (activeFlows.hasOwnProperty(flowId)) {
|
||||
node = activeFlows[flowId].getNode(id);
|
||||
if (node) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function eachNode(cb) {
|
||||
for (var id in activeFlowConfig.allNodes) {
|
||||
if (activeFlowConfig.allNodes.hasOwnProperty(id)) {
|
||||
cb(activeFlowConfig.allNodes[id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getConfig() {
|
||||
return activeConfig;
|
||||
}
|
||||
|
||||
function delegateError(node,logMessage,msg) {
|
||||
if (activeFlows[node.z]) {
|
||||
activeFlows[node.z].handleError(node,logMessage,msg);
|
||||
} else if (activeNodesToFlow[node.z]) {
|
||||
activeFlows[activeNodesToFlow[node.z]].handleError(node,logMessage,msg);
|
||||
} else if (activeFlowConfig.subflows[node.z]) {
|
||||
subflowInstanceNodeMap[node.id].forEach(function(n) {
|
||||
delegateError(getNode(n),logMessage,msg);
|
||||
});
|
||||
}
|
||||
}
|
||||
function handleError(node,logMessage,msg) {
|
||||
if (node.z) {
|
||||
delegateError(node,logMessage,msg);
|
||||
} else {
|
||||
if (activeFlowConfig.configs[node.id]) {
|
||||
activeFlowConfig.configs[node.id]._users.forEach(function(id) {
|
||||
var userNode = activeFlowConfig.allNodes[id];
|
||||
delegateError(userNode,logMessage,msg);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function delegateStatus(node,statusMessage) {
|
||||
if (activeFlows[node.z]) {
|
||||
activeFlows[node.z].handleStatus(node,statusMessage);
|
||||
}
|
||||
}
|
||||
function handleStatus(node,statusMessage) {
|
||||
if (node.z) {
|
||||
delegateStatus(node,statusMessage);
|
||||
} else {
|
||||
if (activeFlowConfig.configs[node.id]) {
|
||||
activeFlowConfig.configs[node.id]._users.forEach(function(id) {
|
||||
var userNode = activeFlowConfig.allNodes[id];
|
||||
delegateStatus(userNode,statusMessage);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function start(type,diff) {
|
||||
type = type||"full";
|
||||
started = true;
|
||||
var i;
|
||||
if (activeFlowConfig.missingTypes.length > 0) {
|
||||
log.info(log._("nodes.flows.missing-types"));
|
||||
var knownUnknowns = 0;
|
||||
for (i=0;i<activeFlowConfig.missingTypes.length;i++) {
|
||||
var nodeType = activeFlowConfig.missingTypes[i];
|
||||
var info = deprecated.get(nodeType);
|
||||
if (info) {
|
||||
log.info(log._("nodes.flows.missing-type-provided",{type:activeFlowConfig.missingTypes[i],module:info.module}));
|
||||
knownUnknowns += 1;
|
||||
} else {
|
||||
log.info(" - "+activeFlowConfig.missingTypes[i]);
|
||||
}
|
||||
}
|
||||
if (knownUnknowns > 0) {
|
||||
log.info(log._("nodes.flows.missing-type-install-1"));
|
||||
log.info(" npm install <module name>");
|
||||
log.info(log._("nodes.flows.missing-type-install-2"));
|
||||
log.info(" "+settings.userDir);
|
||||
}
|
||||
return;
|
||||
}
|
||||
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);
|
||||
for (id in activeFlowConfig.flows) {
|
||||
if (activeFlowConfig.flows.hasOwnProperty(id)) {
|
||||
activeFlows[id] = Flow.create(activeFlowConfig,activeFlowConfig.flows[id]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
activeFlows['_GLOBAL_'].update(activeFlowConfig,activeFlowConfig);
|
||||
for (id in activeFlowConfig.flows) {
|
||||
if (activeFlowConfig.flows.hasOwnProperty(id)) {
|
||||
if (activeFlows[id]) {
|
||||
activeFlows[id].update(activeFlowConfig,activeFlowConfig.flows[id]);
|
||||
} else {
|
||||
activeFlows[id] = Flow.create(activeFlowConfig,activeFlowConfig.flows[id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (id in activeFlows) {
|
||||
if (activeFlows.hasOwnProperty(id)) {
|
||||
activeFlows[id].start(diff);
|
||||
var activeNodes = activeFlows[id].getActiveNodes();
|
||||
Object.keys(activeNodes).forEach(function(nid) {
|
||||
activeNodesToFlow[nid] = id;
|
||||
if (activeNodes[nid]._alias) {
|
||||
subflowInstanceNodeMap[activeNodes[nid]._alias] = subflowInstanceNodeMap[activeNodes[nid]._alias] || [];
|
||||
subflowInstanceNodeMap[activeNodes[nid]._alias].push(nid);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
events.emit("nodes-started");
|
||||
if (diff) {
|
||||
log.info(log._("nodes.flows.started-modified-"+type));
|
||||
} else {
|
||||
log.info(log._("nodes.flows.started-flows"));
|
||||
}
|
||||
}
|
||||
|
||||
function stop(type,diff) {
|
||||
type = type||"full";
|
||||
if (diff) {
|
||||
log.info(log._("nodes.flows.stopping-modified-"+type));
|
||||
} else {
|
||||
log.info(log._("nodes.flows.stopping-flows"));
|
||||
}
|
||||
started = false;
|
||||
var promises = [];
|
||||
var stopList;
|
||||
if (type === 'nodes') {
|
||||
stopList = diff.changed.concat(diff.removed);
|
||||
} else if (type === 'flows') {
|
||||
stopList = diff.changed.concat(diff.removed).concat(diff.linked);
|
||||
}
|
||||
for (var id in activeFlows) {
|
||||
if (activeFlows.hasOwnProperty(id)) {
|
||||
promises = promises.concat(activeFlows[id].stop(stopList));
|
||||
if (!diff || diff.removed[id]) {
|
||||
delete activeFlows[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return when.promise(function(resolve,reject) {
|
||||
when.settle(promises).then(function() {
|
||||
for (id in activeNodesToFlow) {
|
||||
if (activeNodesToFlow.hasOwnProperty(id)) {
|
||||
if (!activeFlows[activeNodesToFlow[id]]) {
|
||||
delete activeNodesToFlow[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (stopList) {
|
||||
stopList.forEach(function(id) {
|
||||
delete activeNodesToFlow[id];
|
||||
});
|
||||
}
|
||||
// Ideally we'd prune just what got stopped - but mapping stopList
|
||||
// id to the list of subflow instance nodes is something only Flow
|
||||
// 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"));
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function checkTypeInUse(id) {
|
||||
var nodeInfo = typeRegistry.getNodeInfo(id);
|
||||
if (!nodeInfo) {
|
||||
throw new Error(log._("nodes.index.unrecognised-id", {id:id}));
|
||||
} else {
|
||||
var inUse = {};
|
||||
var config = getConfig();
|
||||
config.forEach(function(n) {
|
||||
inUse[n.type] = (inUse[n.type]||0)+1;
|
||||
});
|
||||
var nodesInUse = [];
|
||||
nodeInfo.types.forEach(function(t) {
|
||||
if (inUse[t]) {
|
||||
nodesInUse.push(t);
|
||||
}
|
||||
});
|
||||
if (nodesInUse.length > 0) {
|
||||
var msg = nodesInUse.join(", ");
|
||||
var err = new Error(log._("nodes.index.type-in-use", {msg:msg}));
|
||||
err.code = "type_in_use";
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: init,
|
||||
|
||||
/**
|
||||
* Load the current flow configuration from storage
|
||||
* @return a promise for the loading of the config
|
||||
*/
|
||||
load: load,
|
||||
|
||||
get:getNode,
|
||||
eachNode: eachNode,
|
||||
|
||||
/**
|
||||
* Gets the current flow configuration
|
||||
*/
|
||||
getFlows: getConfig,
|
||||
|
||||
/**
|
||||
* Sets the current active config.
|
||||
* @param config the configuration to enable
|
||||
* @param type the type of deployment to do: full (default), nodes, flows, load
|
||||
* @return a promise for the saving/starting of the new flow
|
||||
*/
|
||||
setFlows: setConfig,
|
||||
|
||||
/**
|
||||
* Starts the current flow configuration
|
||||
*/
|
||||
startFlows: start,
|
||||
|
||||
/**
|
||||
* Stops the current flow configuration
|
||||
* @return a promise for the stopping of the flow
|
||||
*/
|
||||
stopFlows: stop,
|
||||
|
||||
|
||||
handleError: handleError,
|
||||
handleStatus: handleStatus,
|
||||
|
||||
checkTypeInUse: checkTypeInUse
|
||||
|
||||
};
|
327
red/nodes/flows/util.js
Normal file
327
red/nodes/flows/util.js
Normal file
@@ -0,0 +1,327 @@
|
||||
/**
|
||||
* 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 redUtil = require("../../util");
|
||||
var subflowInstanceRE = /^subflow:(.+)$/;
|
||||
var typeRegistry = require("../registry");
|
||||
|
||||
function diffNodes(oldNode,newNode) {
|
||||
if (oldNode == null) {
|
||||
return true;
|
||||
}
|
||||
var oldKeys = Object.keys(oldNode).filter(function(p) { return p != "x" && p != "y" && p != "wires" });
|
||||
var newKeys = Object.keys(newNode).filter(function(p) { return p != "x" && p != "y" && p != "wires" });
|
||||
if (oldKeys.length != newKeys.length) {
|
||||
return true;
|
||||
}
|
||||
for (var i=0;i<newKeys.length;i++) {
|
||||
var p = newKeys[i];
|
||||
if (!redUtil.compareObjects(oldNode[p],newNode[p])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
diffNodes: diffNodes,
|
||||
|
||||
parseConfig: function(config) {
|
||||
var flow = {};
|
||||
flow.allNodes = {};
|
||||
flow.subflows = {};
|
||||
flow.configs = {};
|
||||
flow.flows = {};
|
||||
flow.missingTypes = [];
|
||||
|
||||
config.forEach(function(n) {
|
||||
flow.allNodes[n.id] = clone(n);
|
||||
if (n.type === 'tab') {
|
||||
flow.flows[n.id] = n;
|
||||
flow.flows[n.id].subflows = {};
|
||||
flow.flows[n.id].configs = {};
|
||||
flow.flows[n.id].nodes = {};
|
||||
}
|
||||
});
|
||||
|
||||
config.forEach(function(n) {
|
||||
if (n.type === 'subflow') {
|
||||
flow.subflows[n.id] = n;
|
||||
flow.subflows[n.id].configs = {};
|
||||
flow.subflows[n.id].nodes = {};
|
||||
flow.subflows[n.id].instances = [];
|
||||
}
|
||||
});
|
||||
|
||||
config.forEach(function(n) {
|
||||
if (n.type !== 'subflow' && n.type !== 'tab') {
|
||||
var subflowDetails = subflowInstanceRE.exec(n.type);
|
||||
|
||||
if ( (subflowDetails && !flow.subflows[subflowDetails[1]]) || (!subflowDetails && !typeRegistry.get(n.type)) ) {
|
||||
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];
|
||||
}
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
if (container) {
|
||||
container.configs[n.id] = n;
|
||||
} else {
|
||||
flow.configs[n.id] = n;
|
||||
flow.configs[n.id]._users = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
config.forEach(function(n) {
|
||||
if (n.type !== 'subflow' && n.type !== 'tab') {
|
||||
for (var prop in n) {
|
||||
if (n.hasOwnProperty(prop) && prop !== 'id' && prop !== 'wires' && prop !== '_users' && flow.configs[n[prop]]) {
|
||||
// This property references a global config node
|
||||
flow.configs[n[prop]]._users.push(n.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return flow;
|
||||
},
|
||||
|
||||
diffConfigs: function(oldConfig, newConfig) {
|
||||
var id;
|
||||
var node;
|
||||
var nn;
|
||||
var wires;
|
||||
var j,k;
|
||||
|
||||
var changedSubflows = {};
|
||||
|
||||
var added = {};
|
||||
var removed = {};
|
||||
var changed = {};
|
||||
var wiringChanged = {};
|
||||
|
||||
var linkMap = {};
|
||||
|
||||
for (id in oldConfig.allNodes) {
|
||||
if (oldConfig.allNodes.hasOwnProperty(id)) {
|
||||
node = oldConfig.allNodes[id];
|
||||
// build the map of what this node was previously wired to
|
||||
if (node.wires) {
|
||||
linkMap[node.id] = linkMap[node.id] || [];
|
||||
for (j=0;j<node.wires.length;j++) {
|
||||
wires = node.wires[j];
|
||||
for (k=0;k<wires.length;k++) {
|
||||
linkMap[node.id].push(wires[k]);
|
||||
nn = oldConfig.allNodes[wires[k]];
|
||||
if (nn) {
|
||||
linkMap[nn.id] = linkMap[nn.id] || [];
|
||||
linkMap[nn.id].push(node.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// This node has been removed
|
||||
if (!newConfig.allNodes.hasOwnProperty(id)) {
|
||||
removed[id] = node;
|
||||
// Mark the container as changed
|
||||
if (newConfig.allNodes[removed[id].z]) {
|
||||
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];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This node has a material configuration change
|
||||
if (diffNodes(node,newConfig.allNodes[id]) || newConfig.allNodes[id].credentials) {
|
||||
changed[id] = newConfig.allNodes[id];
|
||||
if (changed[id].type === "subflow") {
|
||||
changedSubflows[id] = changed[id];
|
||||
}
|
||||
// Mark the container as changed
|
||||
if (newConfig.allNodes[changed[id].z]) {
|
||||
changed[changed[id].z] = newConfig.allNodes[changed[id].z];
|
||||
if (changed[changed[id].z].type === "subflow") {
|
||||
changedSubflows[changed[id].z] = changed[changed[id].z];
|
||||
delete changed[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
// This node's wiring has changed
|
||||
if (!redUtil.compareObjects(node.wires,newConfig.allNodes[id].wires)) {
|
||||
wiringChanged[id] = newConfig.allNodes[id];
|
||||
// Mark the container as changed
|
||||
if (newConfig.allNodes[wiringChanged[id].z]) {
|
||||
changed[wiringChanged[id].z] = newConfig.allNodes[wiringChanged[id].z];
|
||||
if (changed[wiringChanged[id].z].type === "subflow") {
|
||||
changedSubflows[wiringChanged[id].z] = changed[wiringChanged[id].z];
|
||||
delete wiringChanged[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Look for added nodes
|
||||
for (id in newConfig.allNodes) {
|
||||
if (newConfig.allNodes.hasOwnProperty(id)) {
|
||||
node = newConfig.allNodes[id];
|
||||
// build the map of what this node is now wired to
|
||||
if (node.wires) {
|
||||
linkMap[node.id] = linkMap[node.id] || [];
|
||||
for (j=0;j<node.wires.length;j++) {
|
||||
wires = node.wires[j];
|
||||
for (k=0;k<wires.length;k++) {
|
||||
if (linkMap[node.id].indexOf(wires[k]) === -1) {
|
||||
linkMap[node.id].push(wires[k]);
|
||||
}
|
||||
nn = newConfig.allNodes[wires[k]];
|
||||
if (nn) {
|
||||
linkMap[nn.id] = linkMap[nn.id] || [];
|
||||
if (linkMap[nn.id].indexOf(node.id) === -1) {
|
||||
linkMap[nn.id].push(node.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// This node has been added
|
||||
if (!oldConfig.allNodes.hasOwnProperty(id)) {
|
||||
added[id] = node;
|
||||
// Mark the container as changed
|
||||
if (newConfig.allNodes[added[id].z]) {
|
||||
changed[added[id].z] = newConfig.allNodes[added[id].z];
|
||||
if (changed[added[id].z].type === "subflow") {
|
||||
changedSubflows[added[id].z] = changed[added[id].z];
|
||||
delete added[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (id in newConfig.allNodes) {
|
||||
if (newConfig.allNodes.hasOwnProperty(id)) {
|
||||
node = newConfig.allNodes[id];
|
||||
for (var prop in node) {
|
||||
if (node.hasOwnProperty(prop) && prop != "z" && prop != "id" && prop != "wires") {
|
||||
// This node has a property that references a changed/removed node
|
||||
// Assume it is a config node change and mark this node as
|
||||
// changed.
|
||||
if (changed[node[prop]] || removed[node[prop]]) {
|
||||
if (!changed[node.id]) {
|
||||
changed[node.id] = node;
|
||||
if (newConfig.allNodes[node.z]) {
|
||||
changed[node.z] = newConfig.allNodes[node.z];
|
||||
if (changed[node.z].type === "subflow") {
|
||||
changedSubflows[node.z] = changed[node.z];
|
||||
delete changed[node.id];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Recursively mark all instances of changed subflows as changed
|
||||
var changedSubflowStack = Object.keys(changedSubflows);
|
||||
while(changedSubflowStack.length > 0) {
|
||||
var subflowId = changedSubflowStack.pop();
|
||||
for (id in newConfig.allNodes) {
|
||||
if (newConfig.allNodes.hasOwnProperty(id)) {
|
||||
node = newConfig.allNodes[id];
|
||||
if (node.type === 'subflow:'+subflowId) {
|
||||
if (!changed[node.id]) {
|
||||
changed[node.id] = node;
|
||||
if (!changed[changed[node.id].z] && newConfig.allNodes[changed[node.id].z]) {
|
||||
changed[changed[node.id].z] = newConfig.allNodes[changed[node.id].z];
|
||||
if (newConfig.allNodes[changed[node.id].z].type === "subflow") {
|
||||
// This subflow instance is inside a subflow. Add the
|
||||
// containing subflow to the stack to mark
|
||||
changedSubflowStack.push(changed[node.id].z);
|
||||
delete changed[node.id];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var diff = {
|
||||
added:Object.keys(added),
|
||||
changed:Object.keys(changed),
|
||||
removed:Object.keys(removed),
|
||||
rewired:Object.keys(wiringChanged),
|
||||
linked:[]
|
||||
}
|
||||
|
||||
// Traverse the links of all modified nodes to mark the connected nodes
|
||||
var modifiedNodes = diff.added.concat(diff.changed).concat(diff.removed).concat(diff.rewired);
|
||||
var visited = {};
|
||||
while(modifiedNodes.length > 0) {
|
||||
node = modifiedNodes.pop();
|
||||
if (!visited[node]) {
|
||||
visited[node] = true;
|
||||
if (linkMap[node]) {
|
||||
if (!changed[node] && !added[node] && !removed[node] && !wiringChanged[node]) {
|
||||
diff.linked.push(node);
|
||||
}
|
||||
modifiedNodes = modifiedNodes.concat(linkMap[node]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// for (id in newConfig.allNodes) {
|
||||
// console.log(
|
||||
// (added[id]?"+":(changed[id]?"!":" "))+(wiringChanged[id]?"w":" ")+(diff.linked.indexOf(id)!==-1?"~":" "),
|
||||
// id,
|
||||
// newConfig.allNodes[id].type,
|
||||
// newConfig.allNodes[id].name||newConfig.allNodes[id].label||""
|
||||
// );
|
||||
// }
|
||||
// for (id in removed) {
|
||||
// console.log(
|
||||
// "- "+(diff.linked.indexOf(id)!==-1?"~":" "),
|
||||
// id,
|
||||
// oldConfig.allNodes[id].type,
|
||||
// oldConfig.allNodes[id].name||oldConfig.allNodes[id].label||""
|
||||
// );
|
||||
// }
|
||||
|
||||
return diff;
|
||||
}
|
||||
}
|
@@ -13,12 +13,23 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var when = require("when");
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
|
||||
var registry = require("./registry");
|
||||
var credentials = require("./credentials");
|
||||
var flows = require("./flows");
|
||||
var Node = require("./Node");
|
||||
var log = require("../log");
|
||||
|
||||
var events = require("../events");
|
||||
|
||||
var child_process = require('child_process');
|
||||
|
||||
var settings;
|
||||
|
||||
/**
|
||||
* Registers a node constructor
|
||||
* @param type - the string type name
|
||||
@@ -53,59 +64,30 @@ function createNode(node,def) {
|
||||
}
|
||||
}
|
||||
|
||||
function init(_settings,storage,app) {
|
||||
credentials.init(storage,app);
|
||||
function init(_settings,storage) {
|
||||
settings = _settings;
|
||||
credentials.init(storage);
|
||||
flows.init(_settings,storage);
|
||||
registry.init(_settings);
|
||||
}
|
||||
|
||||
function checkTypeInUse(id) {
|
||||
var nodeInfo = registry.getNodeInfo(id);
|
||||
if (!nodeInfo) {
|
||||
throw new Error(log._("nodes.index.unrecognised-id", {id:id}));
|
||||
} else {
|
||||
var inUse = {};
|
||||
var config = flows.getFlows();
|
||||
config.forEach(function(n) {
|
||||
inUse[n.type] = (inUse[n.type]||0)+1;
|
||||
});
|
||||
var nodesInUse = [];
|
||||
nodeInfo.types.forEach(function(t) {
|
||||
if (inUse[t]) {
|
||||
nodesInUse.push(t);
|
||||
}
|
||||
});
|
||||
if (nodesInUse.length > 0) {
|
||||
var msg = nodesInUse.join(", ");
|
||||
var err = new Error(log._("nodes.index.type-in-use", {msg:msg}));
|
||||
err.code = "type_in_use";
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
function disableNode(id) {
|
||||
flows.checkTypeInUse(id);
|
||||
return registry.disableNode(id);
|
||||
}
|
||||
|
||||
function removeNode(id) {
|
||||
checkTypeInUse(id);
|
||||
return registry.removeNode(id);
|
||||
}
|
||||
|
||||
function removeModule(module) {
|
||||
function uninstallModule(module) {
|
||||
var info = registry.getModuleInfo(module);
|
||||
if (!info) {
|
||||
throw new Error(log._("nodes.index.unrecognised-module", {module:module}));
|
||||
} else {
|
||||
for (var i=0;i<info.nodes.length;i++) {
|
||||
checkTypeInUse(module+"/"+info.nodes[i].name);
|
||||
flows.checkTypeInUse(module+"/"+info.nodes[i].name);
|
||||
}
|
||||
return registry.removeModule(module);
|
||||
return registry.uninstallModule(module);
|
||||
}
|
||||
}
|
||||
|
||||
function disableNode(id) {
|
||||
checkTypeInUse(id);
|
||||
return registry.disableNode(id);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
// Lifecycle
|
||||
init: init,
|
||||
@@ -116,9 +98,8 @@ module.exports = {
|
||||
getNode: flows.get,
|
||||
eachNode: flows.eachNode,
|
||||
|
||||
addFile: registry.addFile,
|
||||
addModule: registry.addModule,
|
||||
removeModule: removeModule,
|
||||
installModule: registry.installModule,
|
||||
uninstallModule: uninstallModule,
|
||||
|
||||
enableNode: registry.enableNode,
|
||||
disableNode: disableNode,
|
||||
@@ -140,6 +121,7 @@ module.exports = {
|
||||
|
||||
// Flow handling
|
||||
loadFlows: flows.load,
|
||||
startFlows: flows.startFlows,
|
||||
stopFlows: flows.stopFlows,
|
||||
setFlows: flows.setFlows,
|
||||
getFlows: flows.getFlows,
|
||||
@@ -147,6 +129,6 @@ module.exports = {
|
||||
// Credentials
|
||||
addCredentials: credentials.add,
|
||||
getCredentials: credentials.get,
|
||||
deleteCredentials: credentials.delete
|
||||
deleteCredentials: credentials.delete,
|
||||
getCredentialDefinition: credentials.getDefinition
|
||||
};
|
||||
|
||||
|
@@ -21,11 +21,13 @@ var path = require("path");
|
||||
var events = require("../../events");
|
||||
var registry = require("./registry");
|
||||
var loader = require("./loader");
|
||||
var installer = require("./installer");
|
||||
|
||||
var settings;
|
||||
|
||||
function init(_settings) {
|
||||
settings = _settings;
|
||||
installer.init(settings);
|
||||
loader.init(settings);
|
||||
registry.init(settings,loader);
|
||||
}
|
||||
@@ -36,12 +38,6 @@ function load(defaultNodesDir,disableNodePathScan) {
|
||||
return loader.load(defaultNodesDir,disableNodePathScan);
|
||||
}
|
||||
|
||||
function addFile(file) {
|
||||
var info = "node-red/"+path.basename(file).replace(/^\d+-/,"").replace(/\.js$/,"");
|
||||
return loader.addFile(file).then(function() {
|
||||
return registry.getNodeInfo(info);
|
||||
});
|
||||
}
|
||||
function addModule(module) {
|
||||
return loader.addModule(module).then(function() {
|
||||
return registry.getModuleInfo(module);
|
||||
@@ -79,9 +75,11 @@ module.exports = {
|
||||
enableNode: enableNodeSet,
|
||||
disableNode: registry.disableNodeSet,
|
||||
|
||||
addFile: addFile,
|
||||
addModule: addModule,
|
||||
removeModule: registry.removeModule,
|
||||
|
||||
|
||||
installModule: installer.installModule,
|
||||
uninstallModule: installer.uninstallModule,
|
||||
|
||||
cleanModuleList: registry.cleanModuleList
|
||||
};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user