@gregoriusrippenstein/node-red-contrib-introspection 0.9.20 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/nodes/45-get-flows.html +4 -1
- package/nodes/50-send-flow.html +19 -3
- package/nodes/50-send-flow.js +62 -48
- package/nodes/51-install-package.html +35 -3
- package/nodes/51-install-package.js +65 -43
- package/package.json +1 -1
- package/plugins/sidebar.html +16 -11
package/nodes/45-get-flows.html
CHANGED
|
@@ -114,5 +114,8 @@
|
|
|
114
114
|
</script>
|
|
115
115
|
|
|
116
116
|
<script type="text/html" data-help-name="GetFlows">
|
|
117
|
-
<p>Retrieves the current flow file from
|
|
117
|
+
<p>Retrieves the current flow file from this server. This uses the Node-RED API and is therefore storage-method independent. Payload becomes a Json string.</p>
|
|
118
|
+
|
|
119
|
+
If the response is to sent to another host, then insert a JSON node to convert the response of this node to a Javascript array which can be passed as payload
|
|
120
|
+
to the SendFlow node.
|
|
118
121
|
</script>
|
package/nodes/50-send-flow.html
CHANGED
|
@@ -7,9 +7,15 @@
|
|
|
7
7
|
name: {
|
|
8
8
|
value:"",
|
|
9
9
|
},
|
|
10
|
+
|
|
10
11
|
hostUrl: {
|
|
11
|
-
value:
|
|
12
|
+
value:"hostUrl",
|
|
13
|
+
required:true
|
|
14
|
+
},
|
|
15
|
+
hostUrlType: {
|
|
16
|
+
value:"str"
|
|
12
17
|
},
|
|
18
|
+
|
|
13
19
|
flowVersion: {
|
|
14
20
|
value: "v1"
|
|
15
21
|
},
|
|
@@ -29,6 +35,7 @@
|
|
|
29
35
|
value: "env",
|
|
30
36
|
},
|
|
31
37
|
},
|
|
38
|
+
|
|
32
39
|
inputs:1,
|
|
33
40
|
outputs:1,
|
|
34
41
|
|
|
@@ -39,6 +46,7 @@
|
|
|
39
46
|
labelStyle: function() {
|
|
40
47
|
return this.name?"node_label_italic":"";
|
|
41
48
|
},
|
|
49
|
+
|
|
42
50
|
oneditprepare: function() {
|
|
43
51
|
$("#node-input-apiUsername").typedInput({
|
|
44
52
|
types:["env", "msg", "flow","global", "cred"],
|
|
@@ -46,10 +54,15 @@
|
|
|
46
54
|
});
|
|
47
55
|
|
|
48
56
|
$("#node-input-apiPassword").typedInput({
|
|
49
|
-
types:["env", "msg", "flow","global", "
|
|
57
|
+
types:["env", "msg", "flow","global", "cred"],
|
|
50
58
|
typeField: "#node-input-apiPasswordType"
|
|
51
59
|
});
|
|
52
60
|
|
|
61
|
+
$("#node-input-hostUrl").typedInput({
|
|
62
|
+
types:["str", "msg", "flow", "global", "env"],
|
|
63
|
+
typeField: "#node-input-hostUrlType"
|
|
64
|
+
});
|
|
65
|
+
|
|
53
66
|
if ( $('#node-input-useAuthentication').is(":checked") ) {
|
|
54
67
|
$('#useAuthentication-input-fields').show();
|
|
55
68
|
} else {
|
|
@@ -75,7 +88,8 @@
|
|
|
75
88
|
|
|
76
89
|
<div class="form-row">
|
|
77
90
|
<label for="node-input-hostUrl"><i class="fa fa-tag"></i> Host</label>
|
|
78
|
-
<input type="text" id="node-input-hostUrl" placeholder="
|
|
91
|
+
<input type="text" id="node-input-hostUrl" placeholder="hostUrl">
|
|
92
|
+
<input type="hidden" id="node-input-hostUrlType" value="str">
|
|
79
93
|
</div>
|
|
80
94
|
|
|
81
95
|
<div class="form-row">
|
|
@@ -123,4 +137,6 @@
|
|
|
123
137
|
|
|
124
138
|
<script type="text/html" data-help-name="SendFlow">
|
|
125
139
|
<p>Send flow to another Node-RED instance.</p>
|
|
140
|
+
|
|
141
|
+
Payload is assumed to be a Javascript array containing flow data.
|
|
126
142
|
</script>
|
package/nodes/50-send-flow.js
CHANGED
|
@@ -11,29 +11,37 @@ module.exports = function(RED) {
|
|
|
11
11
|
|
|
12
12
|
node.on("input", function(msg, send, done) {
|
|
13
13
|
var sendFlow = (hdrs, got) => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
14
|
+
|
|
15
|
+
RED.util.evaluateNodeProperty(cfg.hostUrl, cfg.hostUrlType, node, msg, (err, result) => {
|
|
16
|
+
if (err) {
|
|
17
|
+
node.status({ fill: "red", shape: "dot", text: "Failed" });
|
|
18
|
+
node.error("unable to obtain host url", { ...msg, _err: err })
|
|
19
|
+
} else {
|
|
20
|
+
got.post( result + "/flows", {
|
|
21
|
+
headers: {
|
|
22
|
+
"Node-RED-API-Version": cfg.flowVersion,
|
|
23
|
+
"Content-type": "application/json",
|
|
24
|
+
"Node-RED-Deployment-Type": "full",
|
|
25
|
+
...hdrs
|
|
26
|
+
},
|
|
27
|
+
body: JSON.stringify(msg.payload)
|
|
28
|
+
}).then( res => {
|
|
29
|
+
send({
|
|
30
|
+
...msg,
|
|
31
|
+
payload: res.body
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
node.status({fill:"green",shape:"dot",text:"Good"});
|
|
35
|
+
setTimeout( function() {
|
|
36
|
+
node.status({})
|
|
37
|
+
}, 450);
|
|
38
|
+
|
|
39
|
+
}).catch( err => {
|
|
40
|
+
node.status({fill:"red",shape:"dot",text:"Failed"});
|
|
41
|
+
node.error("posting data to host", { ...msg, _err: err})
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
})
|
|
37
45
|
};
|
|
38
46
|
|
|
39
47
|
/**
|
|
@@ -45,19 +53,17 @@ module.exports = function(RED) {
|
|
|
45
53
|
|
|
46
54
|
node.status({fill:"blue",shape:"dot",text:"Requesting token"});
|
|
47
55
|
|
|
48
|
-
RED.util.evaluateNodeProperty(cfg.apiUsername, cfg.apiUsernameType,
|
|
49
|
-
node, msg, (err, result) => {
|
|
56
|
+
RED.util.evaluateNodeProperty(cfg.apiUsername, cfg.apiUsernameType, node, msg, (err, result) => {
|
|
50
57
|
if (err) {
|
|
51
58
|
node.status({fill:"red",shape:"dot",text:"Failed"});
|
|
52
|
-
node.error("
|
|
59
|
+
node.error("unable to obtain api username", { ...msg, _err: err})
|
|
53
60
|
} else {
|
|
54
61
|
username = result;
|
|
55
62
|
|
|
56
|
-
RED.util.evaluateNodeProperty(cfg.apiPassword, cfg.apiPasswordType,
|
|
57
|
-
node, msg, (err, result) => {
|
|
63
|
+
RED.util.evaluateNodeProperty(cfg.apiPassword, cfg.apiPasswordType, node, msg, (err, result) => {
|
|
58
64
|
if (err) {
|
|
59
65
|
node.status({fill:"red",shape:"dot",text:"Failed"});
|
|
60
|
-
node.error("
|
|
66
|
+
node.error("unable to obtain api password", {...msg, _err: err})
|
|
61
67
|
} else {
|
|
62
68
|
password = result;
|
|
63
69
|
|
|
@@ -69,31 +75,39 @@ module.exports = function(RED) {
|
|
|
69
75
|
"password": password
|
|
70
76
|
}
|
|
71
77
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
RED.util.evaluateNodeProperty(cfg.hostUrl, cfg.hostUrlType, node, msg, (err, result) => {
|
|
79
|
+
if (err) {
|
|
80
|
+
node.status({ fill: "red", shape: "dot", text: "Failed" });
|
|
81
|
+
node.error("error occurred", { ...msg, _err: err })
|
|
82
|
+
} else {
|
|
83
|
+
|
|
84
|
+
import('got').then( (module) => {
|
|
85
|
+
module.got.post( result + "/auth/token", {
|
|
86
|
+
json: data
|
|
87
|
+
}).then( res => {
|
|
88
|
+
node.status({
|
|
89
|
+
fill:"blue",
|
|
90
|
+
shape:"dot",
|
|
91
|
+
text:"Sending flow"
|
|
92
|
+
});
|
|
81
93
|
|
|
82
|
-
|
|
94
|
+
var access_token = JSON.parse(res.body).access_token;
|
|
83
95
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
96
|
+
sendFlow({
|
|
97
|
+
"Authorization": "Bearer " + access_token
|
|
98
|
+
}, module.got);
|
|
87
99
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
100
|
+
}).catch((err) => {
|
|
101
|
+
node.status({fill:"red",shape:"dot",text:"Failed"});
|
|
102
|
+
node.error( "unable to obtain api auth token", { ...msg, _err: err });
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
})
|
|
93
107
|
}
|
|
94
108
|
})
|
|
95
109
|
}
|
|
96
|
-
})
|
|
110
|
+
})
|
|
97
111
|
} else {
|
|
98
112
|
/*
|
|
99
113
|
* Authentication free zone...
|
|
@@ -8,9 +8,15 @@
|
|
|
8
8
|
name: {
|
|
9
9
|
value:"",
|
|
10
10
|
},
|
|
11
|
+
|
|
11
12
|
hostUrl: {
|
|
12
|
-
value:
|
|
13
|
+
value:"hostUrl",
|
|
14
|
+
required:true
|
|
15
|
+
},
|
|
16
|
+
hostUrlType: {
|
|
17
|
+
value:"str"
|
|
13
18
|
},
|
|
19
|
+
|
|
14
20
|
useAuthentication: {
|
|
15
21
|
value:false
|
|
16
22
|
},
|
|
@@ -30,6 +36,7 @@
|
|
|
30
36
|
value: false
|
|
31
37
|
}
|
|
32
38
|
},
|
|
39
|
+
|
|
33
40
|
inputs:1,
|
|
34
41
|
outputs:1,
|
|
35
42
|
|
|
@@ -40,6 +47,7 @@
|
|
|
40
47
|
labelStyle: function() {
|
|
41
48
|
return this.name?"node_label_italic":"";
|
|
42
49
|
},
|
|
50
|
+
|
|
43
51
|
oneditprepare: function() {
|
|
44
52
|
$("#node-input-apiUsername").typedInput({
|
|
45
53
|
types:["env", "msg", "flow","global", "cred"],
|
|
@@ -51,6 +59,11 @@
|
|
|
51
59
|
typeField: "#node-input-apiPasswordType"
|
|
52
60
|
});
|
|
53
61
|
|
|
62
|
+
$("#node-input-hostUrl").typedInput({
|
|
63
|
+
types:["str", "msg", "flow", "global", "env"],
|
|
64
|
+
typeField: "#node-input-hostUrlType"
|
|
65
|
+
});
|
|
66
|
+
|
|
54
67
|
if ( $('#node-input-useAuthentication').is(":checked") ) {
|
|
55
68
|
$('#useAuthentication-input-fields').show();
|
|
56
69
|
} else {
|
|
@@ -97,8 +110,9 @@
|
|
|
97
110
|
-->
|
|
98
111
|
|
|
99
112
|
<div class="form-row hostUrl-row">
|
|
100
|
-
|
|
101
|
-
|
|
113
|
+
<label for="node-input-hostUrl"><i class="fa fa-tag"></i> Host</label>
|
|
114
|
+
<input type="text" id="node-input-hostUrl" placeholder="hostUrl">
|
|
115
|
+
<input type="hidden" id="node-input-hostUrlType" value="str">
|
|
102
116
|
</div>
|
|
103
117
|
|
|
104
118
|
<div class="form-row">
|
|
@@ -134,4 +148,22 @@
|
|
|
134
148
|
|
|
135
149
|
<script type="text/html" data-help-name="InstallPackage">
|
|
136
150
|
<p>Install package on another Node-RED instance.</p>
|
|
151
|
+
|
|
152
|
+
<p>This takes two forms, it can either install a package by name and version (defaults to "latest") or it can
|
|
153
|
+
accept a buffer object containing a .tgz package file.</p>
|
|
154
|
+
|
|
155
|
+
<p></p>For the first case:
|
|
156
|
+
<pre>
|
|
157
|
+
msg.payload = {
|
|
158
|
+
module: "@gregoriusrippenstein/node-red-contrib-introspection",
|
|
159
|
+
version: "0.10.0"
|
|
160
|
+
}
|
|
161
|
+
</pre>
|
|
162
|
+
|
|
163
|
+
<p>To install a .tgz file, read it into a Buffer object and set the payload to:</p>
|
|
164
|
+
<pre>
|
|
165
|
+
msg.payload = {
|
|
166
|
+
data: new Buffer("ddd")
|
|
167
|
+
}
|
|
168
|
+
</pre>
|
|
137
169
|
</script>
|
|
@@ -15,10 +15,17 @@ module.exports = function(RED) {
|
|
|
15
15
|
return node.error("msg.payload missing or payload not hash", msg)
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/*
|
|
19
|
+
* This defines the helper function - installPackage - that is called either with
|
|
20
|
+
* an authentication token or without. That is determined below.
|
|
21
|
+
*/
|
|
18
22
|
var installPackage = (hdrs, got) => {
|
|
19
23
|
let body = undefined
|
|
20
24
|
let headers = {}
|
|
21
25
|
|
|
26
|
+
/*
|
|
27
|
+
* Check the Payload - either module name or .tgz buffer.
|
|
28
|
+
*/
|
|
22
29
|
if ( msg.payload.module ) {
|
|
23
30
|
body = JSON.stringify({
|
|
24
31
|
module: msg.payload.module,
|
|
@@ -40,27 +47,35 @@ module.exports = function(RED) {
|
|
|
40
47
|
return node.error("msg.payload not well defined", msg)
|
|
41
48
|
}
|
|
42
49
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
50
|
+
/*
|
|
51
|
+
* Connect and send to host.
|
|
52
|
+
*/
|
|
53
|
+
RED.util.evaluateNodeProperty(cfg.hostUrl, cfg.hostUrlType, node, msg, (err, result) => {
|
|
54
|
+
if (err) {
|
|
55
|
+
node.status({ fill: "red", shape: "dot", text: "Failed" });
|
|
56
|
+
node.error("unable to obtain host url", { ...msg, _err: err })
|
|
57
|
+
} else {
|
|
58
|
+
got.post( result + "/nodes", {
|
|
59
|
+
headers: {
|
|
60
|
+
...headers,
|
|
61
|
+
...hdrs
|
|
62
|
+
},
|
|
63
|
+
body: body
|
|
64
|
+
}).then( res => {
|
|
65
|
+
send({
|
|
66
|
+
...msg,
|
|
67
|
+
payload: JSON.parse(res.body)
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
node.status({fill:"green",shape:"dot",text:"Good"});
|
|
71
|
+
setTimeout( function() { node.status({}) }, 450);
|
|
72
|
+
|
|
73
|
+
}).catch( err => {
|
|
74
|
+
node.status({fill:"red",shape:"dot",text:"Failed"});
|
|
75
|
+
node.error("unable to connect to host", { ...msg, _err: err })
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
})
|
|
64
79
|
};
|
|
65
80
|
|
|
66
81
|
/**
|
|
@@ -76,7 +91,7 @@ module.exports = function(RED) {
|
|
|
76
91
|
node, msg, (err, result) => {
|
|
77
92
|
if (err) {
|
|
78
93
|
node.status({fill:"red",shape:"dot",text:"Failed"});
|
|
79
|
-
node.error("
|
|
94
|
+
node.error("unable to obtain api username", { ...msg, _err: err})
|
|
80
95
|
} else {
|
|
81
96
|
username = result;
|
|
82
97
|
|
|
@@ -84,7 +99,7 @@ module.exports = function(RED) {
|
|
|
84
99
|
node, msg, (err, result) => {
|
|
85
100
|
if (err) {
|
|
86
101
|
node.status({fill:"red",shape:"dot",text:"Failed"});
|
|
87
|
-
node.error("
|
|
102
|
+
node.error("unable to obtain api password", { ...msg, _err: err} )
|
|
88
103
|
} else {
|
|
89
104
|
password = result;
|
|
90
105
|
|
|
@@ -96,27 +111,34 @@ module.exports = function(RED) {
|
|
|
96
111
|
"password": password
|
|
97
112
|
}
|
|
98
113
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
114
|
+
RED.util.evaluateNodeProperty(cfg.hostUrl, cfg.hostUrlType, node, msg, (err, result) => {
|
|
115
|
+
if (err) {
|
|
116
|
+
node.status({ fill: "red", shape: "dot", text: "Failed" });
|
|
117
|
+
node.error("unable to obtain api host url", { ...msg, _err: err })
|
|
118
|
+
} else {
|
|
119
|
+
import('got').then( (module) => {
|
|
120
|
+
module.got.post( result + "/auth/token", {
|
|
121
|
+
json: data
|
|
122
|
+
}).then( res => {
|
|
123
|
+
node.status({
|
|
124
|
+
fill:"blue",
|
|
125
|
+
shape:"dot",
|
|
126
|
+
text:"Installing packages"
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
var access_token = JSON.parse(res.body).access_token;
|
|
130
|
+
|
|
131
|
+
installPackage({
|
|
132
|
+
"Authorization": "Bearer " + access_token
|
|
133
|
+
}, module.got);
|
|
134
|
+
|
|
135
|
+
}).catch((err) => {
|
|
136
|
+
node.status({fill:"red",shape:"dot",text:"Failed"});
|
|
137
|
+
node.error( "error occured", { ...msg, _err: err } );
|
|
138
|
+
});
|
|
107
139
|
});
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
installPackage({
|
|
112
|
-
"Authorization": "Bearer " + access_token
|
|
113
|
-
}, module.got);
|
|
114
|
-
|
|
115
|
-
}).catch((err) => {
|
|
116
|
-
node.status({fill:"red",shape:"dot",text:"Failed"});
|
|
117
|
-
node.error( "error occured", { ...msg, _err: err } );
|
|
118
|
-
});
|
|
119
|
-
});
|
|
140
|
+
}
|
|
141
|
+
})
|
|
120
142
|
}
|
|
121
143
|
})
|
|
122
144
|
}
|
package/package.json
CHANGED
package/plugins/sidebar.html
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
function setupTreelist(){var e=collectOrphans();if(0==e.length){RED.notify("No Orphans Found",{type:"success",timeout:2e3});try{$("#node-input-orphan-target-container-div").treeList("empty")}catch(e){}}else{try{$("#node-input-orphan-target-container-div").treeList("empty")}catch(e){$("#node-input-orphan-target-container-div").css({width:"100%",height:"calc(100%)"}).treeList({multi:!1}).on("treelistitemmouseover",function(e,t){}).on("treelistitemmouseout",function(e,t){}).on("treelistselect",function(e,t){t.node&&(RED.workspaces.show(t.node.z,!1,!1,!0),RED.view.reveal(t.node.id,!0),RED.view.redraw())}).on("treelistconfirm",function(e,t){var n;t.node&&(n=t.node.id,setTimeout(()=>{var e=RED.nodes.node(n);e&&(RED.view.reveal(e.id),RED.view.select(e.id),RED.editor.edit(e)),n==RED.workspaces.active()&&RED.workspaces.edit()},50))}),$("#node-input-orphan-target-filter").show();var n=$("#node-input-orphan-target-filter").searchBox({style:"compact",delay:300,change:function(){var e,t=$(this).val().trim().toLowerCase();""===t?($("#node-input-orphan-target-container-div").treeList("filter",null),n.searchBox("count","")):(e=$("#node-input-orphan-target-container-div").treeList("filter",function(e){return-1<e.label.toLowerCase().indexOf(t)||-1<e.node.type.toLowerCase().indexOf(t)}),n.searchBox("count",e+" / "+$("#node-input-orphan-target-container-div").treeList("data").length))}})}$("#node-input-orphan-target-container-div").treeList("data",e.sort((e,t)=>e.node.z>t.node.z))}}function collectOrphans(){let t=new Set;var n=[],i=(RED.nodes.eachLink(e=>{t.add(e.source),t.add(e.target)}),RED.nodes.eachNode(e=>{(!t.has(e)||"link out"==e.type&&"link"==e.mode&&0==e.links.reduce((e,t)=>e||!!RED.nodes.node(t),!1))&&n.push(e)}),[]),r={};return n.forEach(function(e){var t=RED.nodes.getType(e.type);if(t){var n=t.label,n=("function"==typeof n?n.call(e):n)||"",o=e.type;if(0===o.indexOf("subflow:"))return}t&&n||(n=e.type),r[e.id]={node:e,label:n,sublabel:o,selected:!1,checkbox:!1},i.push(r[e.id])}),i}
|
|
8
8
|
|
|
9
|
-
function nr_intro_generate_svg_4_0(r,t){return e=>{try{handleSvgObject($($("#red-ui-workspace-chart").find("svg")[0]),e,t)}catch(t){var n="Error Generating SVG: "+JSON.stringify(t);r.notify(n,{type:"error"}),e('<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg width="1000" height="1000" viewBox="0 0 1000 1000" pointer-events="all" style="cursor: crosshair; touch-action: none;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><style>.small { font: bold 20px sans-serif; fill: red;}</style><text x="10" y="30" class="small">'+n+"</text></svg>")}}}function nr_intro_generate_svg_3_1(r,t){return e=>{try{handleSvgObject($($("#red-ui-workspace-chart").find("svg")[0]),e,t)}catch(t){var n="Error Generating SVG: "+JSON.stringify(t);r.notify(n,{type:"error"}),e('<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg width="1000" height="1000" viewBox="0 0 1000 1000" pointer-events="all" style="cursor: crosshair; touch-action: none;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><style>.small { font: bold 20px sans-serif; fill: red;}</style><text x="10" y="30" class="small">'+n+"</text></svg>")}}}function nr_intro_generate_svg_3_0(r,t){return e=>{try{handleSvgObject($($("svg")[0]),e,t)}catch(t){var n="Error Generating SVG: "+JSON.stringify(t);r.notify(n,{type:"error"}),e('<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg width="1000" height="1000" viewBox="0 0 1000 1000" pointer-events="all" style="cursor: crosshair; touch-action: none;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><style>.small { font: bold 20px sans-serif; fill: red;}</style><text x="10" y="30" class="small">'+n+"</text></svg>")}}}function handleSvgObject(
|
|
9
|
+
function nr_intro_generate_svg_4_0(r,t){return e=>{try{handleSvgObject($($("#red-ui-workspace-chart").find("svg")[0]),e,t)}catch(t){var n="Error Generating SVG: "+JSON.stringify(t);r.notify(n,{type:"error"}),e('<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg width="1000" height="1000" viewBox="0 0 1000 1000" pointer-events="all" style="cursor: crosshair; touch-action: none;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><style>.small { font: bold 20px sans-serif; fill: red;}</style><text x="10" y="30" class="small">'+n+"</text></svg>")}}}function nr_intro_generate_svg_3_1(r,t){return e=>{try{handleSvgObject($($("#red-ui-workspace-chart").find("svg")[0]),e,t)}catch(t){var n="Error Generating SVG: "+JSON.stringify(t);r.notify(n,{type:"error"}),e('<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg width="1000" height="1000" viewBox="0 0 1000 1000" pointer-events="all" style="cursor: crosshair; touch-action: none;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><style>.small { font: bold 20px sans-serif; fill: red;}</style><text x="10" y="30" class="small">'+n+"</text></svg>")}}}function nr_intro_generate_svg_3_0(r,t){return e=>{try{handleSvgObject($($("svg")[0]),e,t)}catch(t){var n="Error Generating SVG: "+JSON.stringify(t);r.notify(n,{type:"error"}),e('<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg width="1000" height="1000" viewBox="0 0 1000 1000" pointer-events="all" style="cursor: crosshair; touch-action: none;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><style>.small { font: bold 20px sans-serif; fill: red;}</style><text x="10" y="30" class="small">'+n+"</text></svg>")}}}function handleSvgObject(o,t,e){var n=o.clone(),r=(e.removeforeignobjects&&n.find("foreignObject").remove(),n.find("svg.__screenshot").remove(),'width="'+o.attr("width")+'" height="'+o.attr("height")+'"'),s=RED.settings.version.split("."),i=parseInt(s[0]),s=parseInt(s[1]);if(3<=i&&1<=s||4<=i){var a=$($($(o).children("g")[0]).children("g")[0]).children("g"),l={x:8e3,y:8e3,w:-1,h:-1};for(let t=1;t<a.length;t++){var g=a[t].getBBox();0==g.width&&0==g.height||(l.x=Math.min(g.x,l.x),l.y=Math.min(g.y,l.y),l.w=Math.max(g.width,l.w),l.h=Math.max(g.height,l.h))}r+=` viewBox='${l.x} ${l.y} ${l.w} ${l.h}'`}function c(t,e){for(var n=0;n<t.length;n++){var s=t.item(n),i=e[n];["stroke-width","fill-opacity","stroke-opacity","opacity","stroke-dasharray"].forEach(function(t){s.setAttribute(t,$(i).attr(t)||$(i).css(t))}),["fill","stroke"].forEach(function(t){var e,n,r=(e=$(i).attr(t)||$(i).css(t))&&null!==e&&"none"!=e?(n=e.match(/^#(.)(.)(.)$/))?"#"+n[1]+n[1]+n[2]+n[2]+n[3]+n[3]:(n=e.match(/^#......$/))?e:null===(n=e.match(/^rgb\(([0-9]+),\s+([0-9]+),\s+([0-9]+)/))?(r=e.match(/^rgba\(([0-9]+),\s+([0-9]+),\s+([0-9]+),\s+([0-9]+)/))?{clr:"#"+("0"+parseInt(r[1],10).toString(16)).slice(-2)+("0"+parseInt(r[2],10).toString(16)).slice(-2)+("0"+parseInt(r[3],10).toString(16)).slice(-2),opa:r[4]}:(console.log("Screenshot node: returned unknown color: "+e),e):"#"+("0"+parseInt(n[1],10).toString(16)).slice(-2)+("0"+parseInt(n[2],10).toString(16)).slice(-2)+("0"+parseInt(n[3],10).toString(16)).slice(-2):"none";"object"==typeof r?(s.setAttribute(t+"-opacity",r.opa),s.setAttribute(t,r.clr)):s.setAttribute(t,r)}),$(i).hasClass("hide")&&("g"==s.tagName&&s.setAttribute("opacity","0"),s.setAttribute("visibility","hidden"))}}var s='<?xml version="1.0" standalone="no"?>\r\n<svg '+r+' pointer-events="all" style="cursor: crosshair; touch-action: none;" xmlns="http://www.w3.org/2000/svg" class="__screenshot" xmlns:xlink="http://www.w3.org/1999/xlink">\r\n',i=n.html(),h=(new DOMParser).parseFromString(s+i+"\r\n</svg>","image/svg+xml"),v=t=>t,f=(e.rmidsandclasses&&(v=e=>(["g","rect","line","path","circle","image","text"].forEach(t=>{$(e.getElementsByTagName(t)).each((t,e)=>{e.setAttribute("class",""),e.setAttribute("id","")})}),e)),["g","rect","line","path","circle","image"].forEach(function(t){c(h.getElementsByTagName(t),o.find(t))}),["text"].forEach(function(t){c(h.getElementsByTagName(t),o.find(t));for(var e=h.getElementsByTagName(t),n=o.find(t),r=0;r<e.length;r++){var s=e.item(r),i=n[r];["font-family","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-weight","text-anchor","dominant-baseline"].forEach(function(t){s.setAttribute(t,$(i).attr(t)||$(i).css(t))})}}),h.getElementsByTagName("image")),w={},m=(n,r,s)=>{var i=n.getAttribute("xlink:href")||n.getAttribute("href"),o=i.substr(-4,4).toLowerCase(),a={".jpg":"jpeg",jpeg:"jpeg",".png":"png",".svg":"svg+xml"};if(w[i])return n.setAttribute("xlink:href","data:image/"+a[o]+";base64,"+w[i]),s(r-1);switch(o){case".jpg":case"jpeg":case".png":var l=new XMLHttpRequest;l.open("GET",i,!0),l.responseType="arraybuffer";l.onerror=function(t){s(r-1)},l.onload=function(t){var e=l.response;e&&(e=(t=>{for(var e="",n=new Uint8Array(t),r=n.byteLength,s=0;s<r;s++)e+=String.fromCharCode(n[s]);return window.btoa(e)})(e),w[i]=e,n.setAttribute("xlink:href","data:image/"+a[o]+";base64,"+e)),s(r-1)},l.send(null);break;case".svg":$.get(i,function(t){var e=new XMLSerializer,e=btoa(e.serializeToString(t));w[i]=e,n.setAttribute("xlink:href","data:image/svg+xml;base64,"+e),s(r-1)});break;default:console.log("SVG Capture ignoring file prefix: "+o),s(r-1)}},d=e=>{if(e<0)t((new XMLSerializer).serializeToString(v(h)));else try{m(f.item(e),e,d)}catch(t){d(e-1)}};0<f.length?m(f.item(f.length-1),f.length-1,d):t((new XMLSerializer).serializeToString(v(h)))}function generatorFunctionForVersion(t,e){var n,r=t.settings.version.split("."),s=r[0],r=r[1];if("3"==s){if("0"==r)return nr_intro_generate_svg_3_0(t,e);if("1"==r)return nr_intro_generate_svg_3_1(t,e)}return"4"==s&&"0"==r?nr_intro_generate_svg_4_0(t,e):(n=t,t=>{var e="Node-RED version ("+n.settings.version+") not supported";n.notify(e,{type:"error"}),t&&t('<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg width="1000" height="1000" viewBox="0 0 1000 1000" pointer-events="all" style="cursor: crosshair; touch-action: none;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><style>.small { font: bold 20px sans-serif; fill: red;}</style><text x="10" y="30" class="small">'+e+"</text></svg>")})}function addPanZoom(){var t=d3.select("#node-input-screenshot-svgcontainer svg"),e=(t.html("<g>"+t.html()+"</g>"),setTimeout(()=>{var t=$("#node-input-screenshot-svgcontainer svg"),e=$(t).attr("viewBox").split(" ");t.animate({height:parseInt(e[3]),width:parseInt(e[2])},800,"swing")},100),t.select("g")),n=d3.behavior.zoom().scaleExtent([.1,200]).on("zoom",function(t){e.attr({transform:"translate("+n.translate()+") scale("+n.scale()+")"})});t.call(n)}
|
|
10
10
|
|
|
11
11
|
function setupTreelistInfoness(){var e=collectUndocumentedNodes();if(0==e.length){RED.notify("All nodes documented",{type:"success",timeout:2e3});try{$("#node-input-orphan-target-container-div").treeList("empty")}catch(e){}}else{try{$("#node-input-orphan-target-container-div").treeList("empty")}catch(e){$("#node-input-orphan-target-container-div").css({width:"100%",height:"calc(100%)"}).treeList({multi:!1}).on("treelistitemmouseover",function(e,t){}).on("treelistitemmouseout",function(e,t){}).on("treelistselect",function(e,t){t.node&&(RED.workspaces.show(t.node.z,!1,!1,!0),RED.view.reveal(t.node.id,!0),RED.view.redraw())}).on("treelistconfirm",function(e,t){var i;t.node&&(i=t.node.id,setTimeout(()=>{var e=RED.nodes.node(i);e&&(RED.view.reveal(e.id),RED.view.select(e.id),RED.editor.edit(e,"editor-tab-description")),i==RED.workspaces.active()&&RED.workspaces.edit()},50))}),$("#node-input-orphan-target-filter").show();var i=$("#node-input-orphan-target-filter").searchBox({style:"compact",delay:300,change:function(){var e,t=$(this).val().trim().toLowerCase();""===t?($("#node-input-orphan-target-container-div").treeList("filter",null),i.searchBox("count","")):(e=$("#node-input-orphan-target-container-div").treeList("filter",function(e){return-1<e.label.toLowerCase().indexOf(t)||-1<e.node.type.toLowerCase().indexOf(t)}),i.searchBox("count",e+" / "+$("#node-input-orphan-target-container-div").treeList("data").length))}})}$("#node-input-orphan-target-container-div").treeList("data",e.sort((e,t)=>e.node.z>t.node.z))}}function collectUndocumentedNodes(){let t=[];RED.nodes.eachNode(e=>{if($("#"+e.id).find(".red-ui-info-available-indicator").remove(),e.info&&e.info.trim()&&$("#"+e.id)[0]){var i=document.createElementNS("http://www.w3.org/2000/svg","g"),n=(i.setAttribute("class","red-ui-info-available-indicator"),i.setAttribute("transform","translate(20,10)"),i.setAttribute("id","infoclk-"+e.id),document.createElementNS("http://www.w3.org/2000/svg","g")),o=(n.setAttribute("class","tip"),n.setAttribute("fill","lightyellow"),document.createElementNS("http://www.w3.org/2000/svg","rect")),o=(o.setAttribute("width","30"),o.setAttribute("height","20"),o.setAttribute("x",$("#"+e.id)[0].getBBox().width-60),o.setAttribute("y","-15"),o.setAttribute("rx","2"),o.setAttribute("style","cursor: pointer;"),n.append(o),document.createElementNS("http://www.w3.org/2000/svg","text"));o.setAttribute("x",$("#"+e.id)[0].getBBox().width-55),o.setAttribute("y","-6"),o.appendChild(document.createTextNode("docs")),n.append(o),i.append(n),$(i).insertBefore($("#"+e.id).find(".red-ui-flow-node"));let t=e.id;$("#infoclk-"+e.id).on("click",e=>{e&&e.preventDefault();e=RED.nodes.node(t);RED.editor.edit(e,"editor-tab-description"),RED.sidebar.show("info")})}else e.z==RED.workspaces.active()&&["link in","link out","link call"].indexOf(e.type)<0&&t.push(e)});var o=[],r={};return t.forEach(function(e){var t=RED.nodes.getType(e.type);if(t){var i,n=t.label,n=("function"==typeof n?n.call(e):n)||"";if(0===(i=e.type).indexOf("subflow:"))return}t&&n||(n=e.type),r[e.id]={node:e,label:n,sublabel:i,selected:!1,checkbox:!1},o.push(r[e.id])}),o}
|
|
12
12
|
|
|
@@ -114,6 +114,7 @@
|
|
|
114
114
|
$('#node-screenshot-capture-btn').on("click", function (e) {
|
|
115
115
|
if ( e ) { e.preventDefault() }
|
|
116
116
|
|
|
117
|
+
$('#node-screenshot-capture-btn').prop('disabled',true)
|
|
117
118
|
$('#node-input-screenshot-svgcontainer').html( "" );
|
|
118
119
|
|
|
119
120
|
RED.notify(
|
|
@@ -122,15 +123,22 @@
|
|
|
122
123
|
}
|
|
123
124
|
);
|
|
124
125
|
|
|
126
|
+
/*
|
|
127
|
+
foreignObjects can be used to embed audio and videos into flow SVGs however
|
|
128
|
+
they can't be exported since they cause an XML parsing error - unfortunately.
|
|
129
|
+
*/
|
|
125
130
|
let opts = {
|
|
126
|
-
rmidsandclasses: $('#node-input-screenshot-remove-classes-and-ids').is('checked')
|
|
131
|
+
rmidsandclasses: $('#node-input-screenshot-remove-classes-and-ids').is('checked'),
|
|
132
|
+
removeforeignobjects: true // $('#node-input-screenshot-remove-foreign-objects').is(":checked")
|
|
127
133
|
}
|
|
134
|
+
|
|
128
135
|
generatorFunctionForVersion(RED, opts)( (svgdata) => {
|
|
129
136
|
$('#node-input-screenshot-svgcontainer').html(svgdata);
|
|
130
137
|
globalRefToSvgData = svgdata;
|
|
131
138
|
setTimeout(() => {
|
|
132
139
|
addPanZoom()
|
|
133
140
|
},150)
|
|
141
|
+
$('#node-screenshot-capture-btn').prop('disabled',false)
|
|
134
142
|
});
|
|
135
143
|
})
|
|
136
144
|
|
|
@@ -372,21 +380,18 @@
|
|
|
372
380
|
<div class="form-row" style="margin-left: 10px; margin-top: 30px;">
|
|
373
381
|
<button type="button" id="node-screenshot-capture-btn"
|
|
374
382
|
class="red-ui-button red-ui-button-large"><i class="fa fa-camera"></i> Capture</button>
|
|
383
|
+
</div>
|
|
375
384
|
|
|
376
|
-
|
|
385
|
+
<div class="form-row" style="margin-left: 10px;">
|
|
386
|
+
<label for="node-input-screenshot-remove-classes-and-ids" style="width: 200px">
|
|
377
387
|
<span>Remove Classes and Ids?</span>
|
|
378
388
|
</label>
|
|
379
389
|
<input type="checkbox" checked="checked" id="node-input-screenshot-remove-classes-and-ids"
|
|
380
|
-
|
|
381
|
-
</div>
|
|
382
|
-
|
|
383
|
-
<div class="form-row" style="margin-left: 10px; margin-top: 30px;">
|
|
384
|
-
Step 1: Capture a screen shot, Step 2: download it, copy it or post off.
|
|
390
|
+
style="display:inline-block; width:15px; vertical-align:baseline;">
|
|
385
391
|
</div>
|
|
386
|
-
|
|
387
|
-
|
|
392
|
+
|
|
388
393
|
<div class="form-row"
|
|
389
|
-
style="min-height: 300px; height:
|
|
394
|
+
style="min-height: 300px; height: 65vh; overflow: hidden; margin-left: 10px; margin-right: 15px; border: 1px rgb(196, 196, 196) solid; border-radius: 5px;">
|
|
390
395
|
<div id="node-input-screenshot-svgcontainer"></div>
|
|
391
396
|
</div>
|
|
392
397
|
|