@gregoriusrippenstein/node-red-contrib-introspection 0.4.5 → 0.5.0

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.
@@ -43,6 +43,7 @@
43
43
 
44
44
  var nodeid = data.nodeid;
45
45
  var _msg = data._msg;
46
+ var msg = data._msg;
46
47
 
47
48
  var node = {
48
49
  send: (dt) => {
@@ -202,4 +203,33 @@
202
203
 
203
204
  <script type="text/html" data-help-name="ClientCode">
204
205
  <p>Execute Javascript code in the Node-RED frontend, triggered from the server.</p>
206
+
207
+ The code is executed in the users browser and has the following enviornment defined:
208
+ <pre>
209
+ <code>
210
+ Variables:
211
+ nodeid - id of this node
212
+ _msg - a reference to the msg object passed to this node
213
+ msg - alias for _msg
214
+ node.id - id of this node, same as `nodeid` above
215
+ payload - a reference to msg.payload as it was received by this node
216
+ topic - a reference to the msg.topic as it was recieved by this node
217
+
218
+ Functionality:
219
+ node.send({..}) - send data to the output port of this node
220
+ node.error("msg") - generate an error for the node
221
+ </code>
222
+ </pre>
223
+
224
+ <p>
225
+ This might be confusing since the code in the node <b>is not</b>
226
+ executed on the Node-RED server, rather it is executed in the client
227
+ browser.
228
+ <p>
229
+ This of course makes no sense for flows that are executed "head less"
230
+ on the server in the backgrond on some dark and stormy night. The ClientCode
231
+ node is intended for immediate, frontend-only use.
232
+ <p>
233
+ An example flow with some use cases is included and can be accessed either via
234
+ the Import Flow dialog or accessed online <a style="color: blue" href="https://flowhub.org/f/e02ba6e534f7a0f4" target="_blank">at here <i class="fa fa-external-link"></i></a>.
205
235
  </script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gregoriusrippenstein/node-red-contrib-introspection",
3
- "version": "0.4.5",
3
+ "version": "0.5.0",
4
4
  "dependencies": {
5
5
  "got": "latest"
6
6
  },
@@ -15,14 +15,13 @@
15
15
  },
16
16
  "node-red": {
17
17
  "version": ">=2.0.0",
18
+ "plugins": {
19
+ "screenshot": "plugins/screenshot.html",
20
+ "orphans": "plugins/orphans.html"
21
+ },
18
22
  "nodes": {
19
23
  "seeker": "nodes/05-seeker.js",
20
24
  "sink": "nodes/10-sink.js",
21
- "screenshot": "nodes/15-screenshot.js",
22
- "orphans": "nodes/20-orphans.js",
23
- "ismobile": "nodes/25-ismobile.js",
24
- "navigator": "nodes/30-navigator.js",
25
- "drawsvg": "nodes/40-drawsvg.js",
26
25
  "getflows": "nodes/45-get-flows.js",
27
26
  "sendflow": "nodes/50-send-flow.js",
28
27
  "clientcode": "nodes/60-client-code.js"
@@ -0,0 +1,45 @@
1
+ <script type="text/javascript">
2
+ (function() {
3
+ function setupTreelist(){var e=collectOrphans();if(0==e.length){RED.notify("No Orphans Found",{type:"warning",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:"400px"}).treeList({multi:!1}).on("treelistitemmouseover",function(e,t){t.node&&t.node.z==RED.workspaces.active()&&(RED.view.reveal(t.node.id,!0),RED.view.redraw())}).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())}),$("#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(){const t=new Set;var n=[],r=(RED.nodes.eachLink(e=>{t.add(e.source),t.add(e.target)}),RED.nodes.eachNode(e=>{t.has(e)||n.push(e)}),[]),i={};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),i[e.id]={node:e,label:n,sublabel:o,selected:!1,checkbox:!1},r.push(i[e.id])}),r}
4
+
5
+ var initialiseSidebarOrphanNodeOnce = () => {
6
+ RED.events.off('runtime-state', initialiseSidebarOrphanNodeOnce);
7
+
8
+ // The html content of the sidebar has been specified below as a data-template, from where it can be loaded:
9
+ var content = $($('script[type="text/x-red"][data-template-name="Orphans"]').i18n().html());
10
+
11
+ // Add a "Your sidebar" tabsheet to the right sidebar panel, in which this sidebar panel can be displayed
12
+ // --> more details: https://nodered.org/docs/api/ui/sidebar/
13
+ RED.sidebar.addTab({
14
+ id: "Orphans",
15
+ label: "Orphans", // short name for the tab
16
+ name: "Orphan Nodes", // long name for the menu
17
+ content: content,
18
+ enableOnEdit: true,
19
+ iconClass: "fa fa-life-ring" // your fontawesome icon
20
+ });
21
+
22
+ // When the user has entered new data in the sidebar, then store it into the config node
23
+ $("#node-input-orphan-find-btn").on("click", function(e) {
24
+ if ( e ) { e.preventDefault() }
25
+ setupTreelist();
26
+ })
27
+ };
28
+ RED.events.on('runtime-state', initialiseSidebarOrphanNodeOnce);
29
+ })();
30
+ </script>
31
+
32
+ <!-- The html for the right sidebar plugin screen -->
33
+ <script type="text/x-red" data-template-name="Orphans">
34
+ <div class="form-row node-input-target-row" style="margin-left: 10px; margin-top: 30px">
35
+ <button id="node-input-orphan-find-btn"
36
+ class="red-ui-button">Find Orphans</button>
37
+ </div>
38
+
39
+ <div class="form-row node-input-target-row node-input-target-list-row" style="margin-left: 10px; position: relative; min-height: 200px; margin-right: 15px;">
40
+ <div style="margin-bottom: 5px; width: 35%; padding-left: 60%;">
41
+ <input type="text" id="node-input-orphan-target-filter" style="display: none;">
42
+ </div>
43
+ <div id="node-input-orphan-target-container-div"></div>
44
+ </div>
45
+ </script>
@@ -0,0 +1,74 @@
1
+ <script type="text/javascript">
2
+ (function() {
3
+ var globalRefToSvgData;
4
+
5
+ function nr_intro_generate_svg_3_1(r){return e=>{try{handleSvgObject($($("svg[width=8000]")[0]),e)}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){return e=>{try{handleSvgObject($($("svg")[0]),e)}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,e){var t=o.clone();t.find("foreignObject").remove(),t.find("svg.__screenshot").remove();function a(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 n='<?xml version="1.0" standalone="no"?>\r\n<svg '+('width="'+o.attr("width")+'" height="'+o.attr("height")+'"')+' 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',t=t.html(),l=(new DOMParser).parseFromString(n+t+"\r\n</svg>","image/svg+xml"),r=e=>(["g","rect","line","path","circle","image","text"].forEach(t=>{$(e.getElementsByTagName(t)).each((t,e)=>{e.setAttribute("class",""),e.setAttribute("id","")})}),e),s=(["g","rect","line","path","circle","image"].forEach(function(t){a(l.getElementsByTagName(t),o.find(t))}),["text"].forEach(function(t){a(l.getElementsByTagName(t),o.find(t));for(var e=l.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))})}}),l.getElementsByTagName("image")),g={},i=(n,r,s)=>{var i=n.getAttribute("xlink:href"),o=i.substr(-4,4).toLowerCase(),a={".jpg":"jpeg",jpeg:"jpeg",".png":"png",".svg":"svg+xml"};if(g[i])return n.setAttribute("xlink:href","data:image/"+a[o]+";base64,"+g[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.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),g[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));g[i]=e,n.setAttribute("xlink:href","data:image/svg+xml;base64,"+e),s(r-1)})}},c=t=>{t<0?e((new XMLSerializer).serializeToString(r(l))):i(s.item(t),t,c)};0<s.length?i(s.item(s.length-1),s.length-1,c):e((new XMLSerializer).serializeToString(r(l)))}function generatorFunctionForVersion(t){var n,e=t.settings.version.split("."),r=e[0],e=e[1];if("3"==r){if("0"==e)return nr_intro_generate_svg_3_0(t);if("1"==e)return nr_intro_generate_svg_3_1(t)}return 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>")}}
6
+
7
+ // Add your plugin as a new tabsheet in the right sidebar AFTER the flow editor is completely started
8
+ var initialiseConfigNodeOnce = () => {
9
+ RED.events.off('runtime-state', initialiseConfigNodeOnce);
10
+
11
+ // The html content of the sidebar has been specified below as a data-template, from where it can be loaded:
12
+ var content = $($('script[type="text/x-red"][data-template-name="Screenshot"]').i18n().html());
13
+
14
+ // Add a "Your sidebar" tabsheet to the right sidebar panel, in which this sidebar panel can be displayed
15
+ // --> more details: https://nodered.org/docs/api/ui/sidebar/
16
+ RED.sidebar.addTab({
17
+ id: "Screenshot",
18
+ label: "Screenshot", // short name for the tab
19
+ name: "Screenshot", // long name for the menu
20
+ content: content,
21
+ enableOnEdit: true,
22
+ iconClass: "fa fa-camera" // your fontawesome icon
23
+ });
24
+
25
+ $('#node-screenshot-capture-btn').on("click", function (e) {
26
+ if ( e ) { e.preventDefault() }
27
+
28
+ $('#node-input-screenshot-svgcontainer').html( "Please wait, screenshot being prepared ..." );
29
+
30
+ generatorFunctionForVersion(RED)( (svgdata) => {
31
+ $('#node-input-screenshot-svgcontainer').html(svgdata);
32
+ globalRefToSvgData = svgdata;
33
+ });
34
+ })
35
+
36
+ // handle the download button under the editor window.
37
+ $('#node-screenshot-download-svg').on("click", function (e) {
38
+ if (e) { e.preventDefault(); }
39
+ var svgBlob = new Blob([globalRefToSvgData], {type:"image/svg+xml;charset=utf-8"});
40
+ var svgUrl = URL.createObjectURL(svgBlob);
41
+ var downloadLink = document.createElement("a");
42
+ downloadLink.href = svgUrl;
43
+ downloadLink.download = "screenshot.svg";
44
+ document.body.appendChild(downloadLink);
45
+ downloadLink.click();
46
+ document.body.removeChild(downloadLink);
47
+ });
48
+
49
+ $('#node-screenshot-copy-to-clipboard-svg').on("click", function (e) {
50
+ if (e) { e.preventDefault(); }
51
+ if ( RED.clipboard.copyText(globalRefToSvgData) ) {
52
+ RED.notify("SVG copied to clipboard", { type: "success" });
53
+ }
54
+ });
55
+ };
56
+ RED.events.on('runtime-state', initialiseConfigNodeOnce);
57
+ })();
58
+ </script>
59
+
60
+ <!-- The html for the right sidebar plugin screen -->
61
+ <script type="text/x-red" data-template-name="Screenshot">
62
+ <div class="form-row" style="margin-left: 10px; margin-top: 30px;">
63
+ <button type="button" id="node-screenshot-capture-btn" class="red-ui-button red-ui-button-large"><i class="fa fa-camera"></i> Capture</button>
64
+ </div>
65
+
66
+ <div class="form-row" style="min-height: 300px; overflow: scroll; margin-left: 10px; margin-right: 15px; height: 300px; border: 1px rgb(196, 196, 196) solid; border-radius: 5px;">
67
+ <div id="node-input-screenshot-svgcontainer"></div>
68
+ </div>
69
+
70
+ <div class="form-row" style="margin-left: 10px; margin-top: 5px;">
71
+ <button type="button" id="node-screenshot-download-svg" class="red-ui-button red-ui-button-large"><i class="fa fa-download"></i>Download</button>
72
+ <button type="button" id="node-screenshot-copy-to-clipboard-svg" class="red-ui-button red-ui-button-large"><i class="fa fa-clipboard"></i>Copy to Clipboard</button>
73
+ </div>
74
+ </script>
@@ -1,155 +0,0 @@
1
- [
2
- {
3
- "id": "e815c52d1e6a0d25",
4
- "type": "tab",
5
- "label": "Trigger Screenshot Example",
6
- "disabled": false,
7
- "info": "",
8
- "env": []
9
- },
10
- {
11
- "id": "03f322255fd8cad1",
12
- "type": "http in",
13
- "z": "e815c52d1e6a0d25",
14
- "name": "[POST] screenshot",
15
- "url": "/screenshot",
16
- "method": "post",
17
- "upload": false,
18
- "swaggerDoc": "",
19
- "x": 388,
20
- "y": 305,
21
- "wires": [
22
- [
23
- "ddabce426445e225",
24
- "a9f9ce494ec4edc1",
25
- "c81d27b03cf13c69"
26
- ]
27
- ]
28
- },
29
- {
30
- "id": "ddabce426445e225",
31
- "type": "debug",
32
- "z": "e815c52d1e6a0d25",
33
- "name": "debug 1",
34
- "active": false,
35
- "tosidebar": true,
36
- "console": false,
37
- "tostatus": false,
38
- "complete": "true",
39
- "targetType": "full",
40
- "statusVal": "",
41
- "statusType": "auto",
42
- "x": 1175,
43
- "y": 307,
44
- "wires": []
45
- },
46
- {
47
- "id": "e62ae897ba4c777f",
48
- "type": "http response",
49
- "z": "e815c52d1e6a0d25",
50
- "name": "",
51
- "statusCode": "",
52
- "headers": {
53
- "content-type": "application/json"
54
- },
55
- "x": 817,
56
- "y": 624,
57
- "wires": []
58
- },
59
- {
60
- "id": "a9f9ce494ec4edc1",
61
- "type": "function",
62
- "z": "e815c52d1e6a0d25",
63
- "name": "respond with ok",
64
- "func": "msg.payload = {\n status: \"ok\",\n data: {\n }\n};\nmsg.statusCode = 200;\nreturn msg;\n",
65
- "outputs": 1,
66
- "noerr": 0,
67
- "initialize": "",
68
- "finalize": "",
69
- "libs": [],
70
- "x": 622,
71
- "y": 517,
72
- "wires": [
73
- [
74
- "e62ae897ba4c777f"
75
- ]
76
- ]
77
- },
78
- {
79
- "id": "c81d27b03cf13c69",
80
- "type": "function",
81
- "z": "e815c52d1e6a0d25",
82
- "name": "set filename & payload",
83
- "func": "msg.filename = \"/data/content/screenshot-\" + Date.now() + \".svg\";\n\nmsg.payload = msg.payload[\"d\"];\n\nreturn msg;",
84
- "outputs": 1,
85
- "noerr": 0,
86
- "initialize": "",
87
- "finalize": "",
88
- "libs": [],
89
- "x": 744,
90
- "y": 410,
91
- "wires": [
92
- [
93
- "00c967aaf678c503"
94
- ]
95
- ]
96
- },
97
- {
98
- "id": "00c967aaf678c503",
99
- "type": "file",
100
- "z": "e815c52d1e6a0d25",
101
- "name": "",
102
- "filename": "filename",
103
- "filenameType": "msg",
104
- "appendNewline": false,
105
- "createDir": true,
106
- "overwriteFile": "true",
107
- "encoding": "none",
108
- "x": 1012,
109
- "y": 505,
110
- "wires": [
111
- [
112
- "ddabce426445e225"
113
- ]
114
- ]
115
- },
116
- {
117
- "id": "32cde1648d31e9b4",
118
- "type": "Screenshot",
119
- "z": "e815c52d1e6a0d25",
120
- "name": "",
121
- "screenshot": "",
122
- "x": 461,
123
- "y": 108,
124
- "wires": []
125
- },
126
- {
127
- "id": "3957fb7da9d89393",
128
- "type": "inject",
129
- "z": "e815c52d1e6a0d25",
130
- "name": "",
131
- "props": [
132
- {
133
- "p": "payload"
134
- },
135
- {
136
- "p": "topic",
137
- "vt": "str"
138
- }
139
- ],
140
- "repeat": "600",
141
- "crontab": "",
142
- "once": true,
143
- "onceDelay": "2",
144
- "topic": "",
145
- "payload": "",
146
- "payloadType": "date",
147
- "x": 213,
148
- "y": 108,
149
- "wires": [
150
- [
151
- "32cde1648d31e9b4"
152
- ]
153
- ]
154
- }
155
- ]