@gregoriusrippenstein/node-red-contrib-introspection 0.0.4 → 0.0.5
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/README.md +5 -2
- package/examples/trigger-and-save-screenshot.json +155 -0
- package/nodes/15-screenshot.html +223 -191
- package/nodes/15-screenshot.js +8 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,13 +46,16 @@ Improvements over [svgexport.io](https://svgexport.io) browser plugin:
|
|
|
46
46
|
Disappointments:
|
|
47
47
|
|
|
48
48
|
- Font-awesome icons, because they use the font-awesome font, aren't inlined and therefore aren't available in external tools. If the font-awesome fonts are installed on the system icons do work in Inkscape and browser.
|
|
49
|
-
- Because a double click is required, any highlights in the workflow are lost (i.e. link-in/out highlights showing the other tab names disappears)
|
|
50
49
|
- Limited testing: Firefox & Opera (on mac), your mileage might vary
|
|
51
50
|
- No error checking - network requests are assumed to work
|
|
52
51
|
|
|
52
|
+
Since version 0.0.5 the Screenshot node takes one input. That input will trigger the node to take a screenshot *without double clicking to open the tray*. The intention is to have an inject trigger regularly so that screenshots are made automagically and flows can have highlights. Screenshots are then posted to the endpoint `/screenshot` (this can be captured with a http-in node) and data is post as a json object `{ d: <svgdata> }`.
|
|
53
|
+
|
|
54
|
+
There is an example [flow](/examples/trigger-and-save-screenshot.json) that demonstrates this feature. The author is aware that this is dangerously close to enabling spyware for flow modification, please see the [LICENSE](/LICENSE) and behaviour.
|
|
55
|
+
|
|
53
56
|
## Examples
|
|
54
57
|
|
|
55
|
-
There
|
|
58
|
+
There are some example flow contained in the package. There are also examples to be found online:
|
|
56
59
|
|
|
57
60
|
- [Orphans](https://demo.openmindmap.org/omm/#flow/3ebb65fdbecb182e) - node is top left of flow or search for `type:Orphans`
|
|
58
61
|
- [Seeker](https://demo.openmindmap.org/omm/#flow/40ea5f2aea6592ae) - top left and the [Sink](https://demo.openmindmap.org/omm/#flow/459c271a96458c7c) - top right
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "e815c52d1e6a0d25",
|
|
4
|
+
"type": "tab",
|
|
5
|
+
"label": "Flow 3",
|
|
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
|
+
]
|
package/nodes/15-screenshot.html
CHANGED
|
@@ -1,4 +1,196 @@
|
|
|
1
1
|
<script type="text/javascript">
|
|
2
|
+
function nr_intro_generate_svg( callbackWithSvgCode ) {
|
|
3
|
+
//
|
|
4
|
+
// begin the SVG conversion code.
|
|
5
|
+
//
|
|
6
|
+
var origSvg = $($('svg')[0]);
|
|
7
|
+
|
|
8
|
+
var hwAttrs = (
|
|
9
|
+
'width="' + origSvg.attr('width') + '" height="' +
|
|
10
|
+
origSvg.attr('height') + '"'
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
var svgHeader = (
|
|
14
|
+
'<?xml version="1.0" standalone="no"?>\r\n' +
|
|
15
|
+
'<svg ' + hwAttrs + ' pointer-events="all" style="cursor: crosshair; '+
|
|
16
|
+
'touch-action: none;" xmlns="http://www.w3.org/2000/svg" '+
|
|
17
|
+
'xmlns:xlink="http://www.w3.org/1999/xlink">\r\n'
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
var svgBody = origSvg.html();
|
|
21
|
+
|
|
22
|
+
var parser = new DOMParser();
|
|
23
|
+
var doc = parser.parseFromString(svgHeader + svgBody + '\r\n</svg>',
|
|
24
|
+
"image/svg+xml");
|
|
25
|
+
|
|
26
|
+
// SVG has very specific requirements for colour specification, baseline
|
|
27
|
+
// all colors to #rrggbb. This converts: 'rgb(rrr, ggg, bbb)' and '#rgb'.
|
|
28
|
+
var rgb2hex = (rgb) => {
|
|
29
|
+
if ( !rgb ) { return "none" }
|
|
30
|
+
if ( rgb === null ) { return "none" }
|
|
31
|
+
|
|
32
|
+
rgb3 = rgb.match(/^#(.)(.)(.)$/);
|
|
33
|
+
if ( rgb3 ) {
|
|
34
|
+
return ("#" +rgb3[1]+rgb3[1]+rgb3[2]+rgb3[2]+rgb3[3]+rgb3[3]);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
rgb3 = rgb.match(/^#......$/);
|
|
38
|
+
if ( rgb3 ) { return rgb; }
|
|
39
|
+
|
|
40
|
+
rgb3 = rgb.match(/^rgb\(([0-9]+),\s+([0-9]+),\s+([0-9]+)/);
|
|
41
|
+
if ( rgb3 === null ) { return rgb; }
|
|
42
|
+
|
|
43
|
+
return "#" +
|
|
44
|
+
("0" + parseInt(rgb3[1],10).toString(16)).slice(-2) +
|
|
45
|
+
("0" + parseInt(rgb3[2],10).toString(16)).slice(-2) +
|
|
46
|
+
("0" + parseInt(rgb3[3],10).toString(16)).slice(-2);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Set everything that might have been set via CSS directly on the
|
|
50
|
+
// element. All styling values must be defined as attributes on the
|
|
51
|
+
// SVG element.
|
|
52
|
+
var convertCssToAttr = function( collection, origElems ) {
|
|
53
|
+
for ( var idx = 0; idx < collection.length; idx++ ) {
|
|
54
|
+
var elem = collection.item(idx);
|
|
55
|
+
var origElem = origElems[idx];
|
|
56
|
+
|
|
57
|
+
[
|
|
58
|
+
"fill", "stroke"
|
|
59
|
+
].forEach( function(attrname) {
|
|
60
|
+
elem.setAttribute(attrname,
|
|
61
|
+
rgb2hex($(origElem).attr(attrname) ||
|
|
62
|
+
$(origElem).css(attrname) ));
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
[
|
|
66
|
+
"stroke-width","fill-opacity","stroke-opacity","opacity"
|
|
67
|
+
].forEach(function(attrname) {
|
|
68
|
+
elem.setAttribute(attrname,
|
|
69
|
+
$(origElem).attr(attrname) ||
|
|
70
|
+
$(origElem).css(attrname));
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if ( $(origElem).hasClass('hide') ) {
|
|
74
|
+
if ( elem.tagName == "g" ) {
|
|
75
|
+
// according to the SVG standard, visibility cannot be applied
|
|
76
|
+
// (or better said: has no effect) on 'g' (group) elements.
|
|
77
|
+
// Inkscape is rather pedantic about this and will show the
|
|
78
|
+
// group, so hack-it by using opacity. Browsers (firefox) doesn't
|
|
79
|
+
// seem to care and will apply visibility to 'g' elements.
|
|
80
|
+
elem.setAttribute("opacity", "0");
|
|
81
|
+
}
|
|
82
|
+
elem.setAttribute("visibility", "hidden");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// include font details on the text elements.
|
|
88
|
+
var convertFontsToAttr = function( collection, origElems ) {
|
|
89
|
+
for ( var idx = 0; idx < collection.length; idx++ ) {
|
|
90
|
+
var elem = collection.item(idx);
|
|
91
|
+
var origElem = origElems[idx];
|
|
92
|
+
|
|
93
|
+
["font-family", "font-size", "font-size-adjust", "font-stretch",
|
|
94
|
+
"font-style", "font-variant", "font-weight"
|
|
95
|
+
].forEach( function(attrname) {
|
|
96
|
+
elem.setAttribute(attrname,
|
|
97
|
+
$(origElem).attr(attrname) ||
|
|
98
|
+
$(origElem).css(attrname) );
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// probably missed some elements ...
|
|
104
|
+
var tagnames = [ "g", "rect", "line", "path", "circle", "image" ];
|
|
105
|
+
tagnames.forEach( function(tagname) {
|
|
106
|
+
convertCssToAttr(doc.getElementsByTagName(tagname),
|
|
107
|
+
origSvg.find(tagname));
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
[ "text" ].forEach( function(tagname) {
|
|
111
|
+
convertCssToAttr(doc.getElementsByTagName(tagname),
|
|
112
|
+
origSvg.find(tagname));
|
|
113
|
+
|
|
114
|
+
convertFontsToAttr(doc.getElementsByTagName(tagname),
|
|
115
|
+
origSvg.find(tagname));
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// inline image data. Image types supported: Jpeg, Png and Svg.
|
|
119
|
+
var imageColl = doc.getElementsByTagName("image");
|
|
120
|
+
|
|
121
|
+
var getDataAndCallbackWhenDone = (elem, cntr, cb) => {
|
|
122
|
+
var postfix = elem.getAttribute("xlink:href").substr(-4,4).toLowerCase();
|
|
123
|
+
|
|
124
|
+
switch(postfix){
|
|
125
|
+
case ".jpg":
|
|
126
|
+
case "jpeg":
|
|
127
|
+
case ".png":
|
|
128
|
+
var fileType = {
|
|
129
|
+
".jpg": "jpeg",
|
|
130
|
+
"jpeg": "jpeg",
|
|
131
|
+
".png": "png"
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
var oReq = new XMLHttpRequest();
|
|
135
|
+
oReq.open("GET", elem.getAttribute( "xlink:href" ), true);
|
|
136
|
+
oReq.responseType = "arraybuffer";
|
|
137
|
+
|
|
138
|
+
var arrayBufferToBase64 = ( buffer ) => {
|
|
139
|
+
var binary = '';
|
|
140
|
+
var bytes = new Uint8Array( buffer );
|
|
141
|
+
var len = bytes.byteLength;
|
|
142
|
+
for (var i = 0; i < len; i++) {
|
|
143
|
+
binary += String.fromCharCode( bytes[ i ] );
|
|
144
|
+
}
|
|
145
|
+
return window.btoa( binary );
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
oReq.onload = function (oEvent) {
|
|
149
|
+
var arrayBuffer = oReq.response; // Note: not oReq.responseText
|
|
150
|
+
if (arrayBuffer) {
|
|
151
|
+
elem.setAttribute("xlink:href",
|
|
152
|
+
"data:image/"+fileType[postfix]+";base64," +
|
|
153
|
+
arrayBufferToBase64(arrayBuffer));
|
|
154
|
+
cb(cntr-1)
|
|
155
|
+
} else {
|
|
156
|
+
cb(cntr-1)
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
oReq.send(null);
|
|
161
|
+
break;
|
|
162
|
+
|
|
163
|
+
case ".svg":
|
|
164
|
+
$.get( elem.getAttribute( "xlink:href" ), function(data) {
|
|
165
|
+
const serializer = new XMLSerializer();
|
|
166
|
+
elem.setAttribute( "xlink:href",
|
|
167
|
+
"data:image/svg+xml;base64," +
|
|
168
|
+
btoa(serializer.serializeToString(data)));
|
|
169
|
+
cb(cntr-1)
|
|
170
|
+
});
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
var cb = (cntr) => {
|
|
176
|
+
if ( cntr < 0 ) {
|
|
177
|
+
const serializer = new XMLSerializer();
|
|
178
|
+
callbackWithSvgCode( serializer.serializeToString(doc) );
|
|
179
|
+
} else {
|
|
180
|
+
getDataAndCallbackWhenDone( imageColl.item(cntr), cntr, cb );
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
if ( imageColl.length > 0 ) {
|
|
185
|
+
getDataAndCallbackWhenDone( imageColl.item(imageColl.length-1),
|
|
186
|
+
imageColl.length-1,
|
|
187
|
+
cb );
|
|
188
|
+
} else {
|
|
189
|
+
const serializer = new XMLSerializer();
|
|
190
|
+
callbackWithSvgCode( serializer.serializeToString(doc) );
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
|
|
2
194
|
RED.nodes.registerType('Screenshot',{
|
|
3
195
|
color: '#e5e4ef',
|
|
4
196
|
icon: "font-awesome/fa-camera",
|
|
@@ -12,7 +204,7 @@
|
|
|
12
204
|
value:""
|
|
13
205
|
},
|
|
14
206
|
},
|
|
15
|
-
inputs:
|
|
207
|
+
inputs:1,
|
|
16
208
|
outputs:0,
|
|
17
209
|
|
|
18
210
|
label: function() {
|
|
@@ -20,6 +212,34 @@
|
|
|
20
212
|
},
|
|
21
213
|
|
|
22
214
|
labelStyle: function() {
|
|
215
|
+
/*
|
|
216
|
+
because there is not a better hook to connect up the event
|
|
217
|
+
listener, misuse the label styling for this. Also don't register a
|
|
218
|
+
new listener each time, maintain only one listener by removing and
|
|
219
|
+
readding.
|
|
220
|
+
*/
|
|
221
|
+
var that = this;
|
|
222
|
+
var functName = "introspect:" + that.id;
|
|
223
|
+
|
|
224
|
+
if ( !window[functName] ) {
|
|
225
|
+
window[functName] = (e,m) => {
|
|
226
|
+
if ( m.msg == "timer-tripped" ) {
|
|
227
|
+
nr_intro_generate_svg( (svgdata) => {
|
|
228
|
+
$.ajax({
|
|
229
|
+
type: "POST",
|
|
230
|
+
url: "/screenshot",
|
|
231
|
+
data: { d: svgdata },
|
|
232
|
+
success: (data) => {},
|
|
233
|
+
dataType: "image/svg+xml;charset=utf-8"
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
RED.comms.unsubscribe(functName, window[functName]);
|
|
241
|
+
RED.comms.subscribe(functName, window[functName] );
|
|
242
|
+
|
|
23
243
|
return this.name?"node_label_italic":"";
|
|
24
244
|
},
|
|
25
245
|
|
|
@@ -54,196 +274,10 @@
|
|
|
54
274
|
|
|
55
275
|
this.editor.setValue( "Please wait, screenshot being prepared ..." );
|
|
56
276
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
//
|
|
60
|
-
var origSvg = $($('svg')[0]);
|
|
61
|
-
|
|
62
|
-
var hwAttrs = (
|
|
63
|
-
'width="' + origSvg.attr('width') + '" height="' +
|
|
64
|
-
origSvg.attr('height') + '"'
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
var svgHeader = (
|
|
68
|
-
'<?xml version="1.0" standalone="no"?>\r\n' +
|
|
69
|
-
'<svg ' + hwAttrs + ' pointer-events="all" style="cursor: crosshair; '+
|
|
70
|
-
'touch-action: none;" xmlns="http://www.w3.org/2000/svg" '+
|
|
71
|
-
'xmlns:xlink="http://www.w3.org/1999/xlink">\r\n'
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
var svgBody = origSvg.html();
|
|
75
|
-
|
|
76
|
-
var parser = new DOMParser();
|
|
77
|
-
var doc = parser.parseFromString(svgHeader + svgBody + '\r\n</svg>',
|
|
78
|
-
"image/svg+xml");
|
|
79
|
-
|
|
80
|
-
// SVG has very specific requirements for colour specification, baseline
|
|
81
|
-
// all colors to #rrggbb. This converts: 'rgb(rrr, ggg, bbb)' and '#rgb'.
|
|
82
|
-
var rgb2hex = (rgb) => {
|
|
83
|
-
if ( !rgb ) { return "none" }
|
|
84
|
-
if ( rgb === null ) { return "none" }
|
|
85
|
-
|
|
86
|
-
rgb3 = rgb.match(/^#(.)(.)(.)$/);
|
|
87
|
-
if ( rgb3 ) {
|
|
88
|
-
return ("#" +rgb3[1]+rgb3[1]+rgb3[2]+rgb3[2]+rgb3[3]+rgb3[3]);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
rgb3 = rgb.match(/^#......$/);
|
|
92
|
-
if ( rgb3 ) { return rgb; }
|
|
93
|
-
|
|
94
|
-
rgb3 = rgb.match(/^rgb\(([0-9]+),\s+([0-9]+),\s+([0-9]+)/);
|
|
95
|
-
if ( rgb3 === null ) { return rgb; }
|
|
96
|
-
|
|
97
|
-
return "#" +
|
|
98
|
-
("0" + parseInt(rgb3[1],10).toString(16)).slice(-2) +
|
|
99
|
-
("0" + parseInt(rgb3[2],10).toString(16)).slice(-2) +
|
|
100
|
-
("0" + parseInt(rgb3[3],10).toString(16)).slice(-2);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Set everything that might have been set via CSS directly on the
|
|
104
|
-
// element. All styling values must be defined as attributes on the
|
|
105
|
-
// SVG element.
|
|
106
|
-
var convertCssToAttr = function( collection, origElems ) {
|
|
107
|
-
for ( var idx = 0; idx < collection.length; idx++ ) {
|
|
108
|
-
var elem = collection.item(idx);
|
|
109
|
-
var origElem = origElems[idx];
|
|
110
|
-
|
|
111
|
-
[
|
|
112
|
-
"fill", "stroke"
|
|
113
|
-
].forEach( function(attrname) {
|
|
114
|
-
elem.setAttribute(attrname,
|
|
115
|
-
rgb2hex($(origElem).attr(attrname) ||
|
|
116
|
-
$(origElem).css(attrname) ));
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
[
|
|
120
|
-
"stroke-width","fill-opacity","stroke-opacity","opacity"
|
|
121
|
-
].forEach(function(attrname) {
|
|
122
|
-
elem.setAttribute(attrname,
|
|
123
|
-
$(origElem).attr(attrname) ||
|
|
124
|
-
$(origElem).css(attrname));
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
if ( $(origElem).hasClass('hide') ) {
|
|
128
|
-
if ( elem.tagName == "g" ) {
|
|
129
|
-
// according to the SVG standard, visibility cannot be applied
|
|
130
|
-
// (or better said: has no effect) on 'g' (group) elements.
|
|
131
|
-
// Inkscape is rather pedantic about this and will show the
|
|
132
|
-
// group, so hack-it by using opacity. Browsers (firefox) doesn't
|
|
133
|
-
// seem to care and will apply visibility to 'g' elements.
|
|
134
|
-
elem.setAttribute("opacity", "0");
|
|
135
|
-
}
|
|
136
|
-
elem.setAttribute("visibility", "hidden");
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// include font details on the text elements.
|
|
142
|
-
var convertFontsToAttr = function( collection, origElems ) {
|
|
143
|
-
for ( var idx = 0; idx < collection.length; idx++ ) {
|
|
144
|
-
var elem = collection.item(idx);
|
|
145
|
-
var origElem = origElems[idx];
|
|
146
|
-
|
|
147
|
-
["font-family", "font-size", "font-size-adjust", "font-stretch",
|
|
148
|
-
"font-style", "font-variant", "font-weight"
|
|
149
|
-
].forEach( function(attrname) {
|
|
150
|
-
elem.setAttribute(attrname,
|
|
151
|
-
$(origElem).attr(attrname) ||
|
|
152
|
-
$(origElem).css(attrname) );
|
|
153
|
-
})
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// probably missed some elements ...
|
|
158
|
-
var tagnames = [ "g", "rect", "line", "path", "circle", "image" ];
|
|
159
|
-
tagnames.forEach( function(tagname) {
|
|
160
|
-
convertCssToAttr(doc.getElementsByTagName(tagname),
|
|
161
|
-
origSvg.find(tagname));
|
|
277
|
+
nr_intro_generate_svg( (svgdata) => {
|
|
278
|
+
that.editor.setValue( svgdata );
|
|
162
279
|
});
|
|
163
280
|
|
|
164
|
-
[ "text" ].forEach( function(tagname) {
|
|
165
|
-
convertCssToAttr(doc.getElementsByTagName(tagname),
|
|
166
|
-
origSvg.find(tagname));
|
|
167
|
-
|
|
168
|
-
convertFontsToAttr(doc.getElementsByTagName(tagname),
|
|
169
|
-
origSvg.find(tagname));
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
// inline image data. Image types supported: Jpeg, Png and Svg.
|
|
173
|
-
var imageColl = doc.getElementsByTagName("image");
|
|
174
|
-
|
|
175
|
-
var getDataAndCallbackWhenDone = (elem, cntr, cb) => {
|
|
176
|
-
var postfix = elem.getAttribute("xlink:href").substr(-4,4).toLowerCase();
|
|
177
|
-
|
|
178
|
-
switch(postfix){
|
|
179
|
-
case ".jpg":
|
|
180
|
-
case "jpeg":
|
|
181
|
-
case ".png":
|
|
182
|
-
var fileType = {
|
|
183
|
-
".jpg": "jpeg",
|
|
184
|
-
"jpeg": "jpeg",
|
|
185
|
-
".png": "png"
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
var oReq = new XMLHttpRequest();
|
|
189
|
-
oReq.open("GET", elem.getAttribute( "xlink:href" ), true);
|
|
190
|
-
oReq.responseType = "arraybuffer";
|
|
191
|
-
|
|
192
|
-
var arrayBufferToBase64 = ( buffer ) => {
|
|
193
|
-
var binary = '';
|
|
194
|
-
var bytes = new Uint8Array( buffer );
|
|
195
|
-
var len = bytes.byteLength;
|
|
196
|
-
for (var i = 0; i < len; i++) {
|
|
197
|
-
binary += String.fromCharCode( bytes[ i ] );
|
|
198
|
-
}
|
|
199
|
-
return window.btoa( binary );
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
oReq.onload = function (oEvent) {
|
|
203
|
-
var arrayBuffer = oReq.response; // Note: not oReq.responseText
|
|
204
|
-
if (arrayBuffer) {
|
|
205
|
-
elem.setAttribute("xlink:href",
|
|
206
|
-
"data:image/"+fileType[postfix]+";base64," +
|
|
207
|
-
arrayBufferToBase64(arrayBuffer));
|
|
208
|
-
cb(cntr-1)
|
|
209
|
-
} else {
|
|
210
|
-
cb(cntr-1)
|
|
211
|
-
}
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
oReq.send(null);
|
|
215
|
-
break;
|
|
216
|
-
|
|
217
|
-
case ".svg":
|
|
218
|
-
$.get( elem.getAttribute( "xlink:href" ), function(data) {
|
|
219
|
-
const serializer = new XMLSerializer();
|
|
220
|
-
elem.setAttribute( "xlink:href",
|
|
221
|
-
"data:image/svg+xml;base64," +
|
|
222
|
-
btoa(serializer.serializeToString(data)));
|
|
223
|
-
cb(cntr-1)
|
|
224
|
-
});
|
|
225
|
-
break;
|
|
226
|
-
}
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
var cb = (cntr) => {
|
|
230
|
-
if ( cntr < 0 ) {
|
|
231
|
-
const serializer = new XMLSerializer();
|
|
232
|
-
that.editor.setValue( serializer.serializeToString(doc) );
|
|
233
|
-
} else {
|
|
234
|
-
getDataAndCallbackWhenDone( imageColl.item(cntr), cntr, cb );
|
|
235
|
-
}
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
if ( imageColl.length > 0 ) {
|
|
239
|
-
getDataAndCallbackWhenDone( imageColl.item(imageColl.length-1),
|
|
240
|
-
imageColl.length-1,
|
|
241
|
-
cb );
|
|
242
|
-
} else {
|
|
243
|
-
const serializer = new XMLSerializer();
|
|
244
|
-
that.editor.setValue( serializer.serializeToString(doc) );
|
|
245
|
-
}
|
|
246
|
-
|
|
247
281
|
// handle the download button under the editor window.
|
|
248
282
|
$('#node-screenshot-download-svg').on("click", function (e) {
|
|
249
283
|
e.preventDefault();
|
|
@@ -257,8 +291,6 @@
|
|
|
257
291
|
document.body.removeChild(downloadLink);
|
|
258
292
|
});
|
|
259
293
|
|
|
260
|
-
// normality resumes here, all the SVG handling is done.
|
|
261
|
-
|
|
262
294
|
$("#node-input-format").on("change", function() {
|
|
263
295
|
var mod = "ace/mode/"+$("#node-input-format").val();
|
|
264
296
|
that.editor.getSession().setMode({
|
package/nodes/15-screenshot.js
CHANGED
|
@@ -9,7 +9,14 @@ module.exports = function(RED) {
|
|
|
9
9
|
node.status({});
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
node.on("input",function(msg, send, done) {
|
|
12
|
+
node.on("input", function(msg, send, done) {
|
|
13
|
+
RED.comms.publish("introspect:" + node.id, RED.util.encodeObject({
|
|
14
|
+
msg: "timer-tripped",
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
node.status({ fill: "green", shape: "dot", text:"Taking screenshot" });
|
|
18
|
+
setTimeout( () => { node.status({}) }, 1432 );
|
|
19
|
+
|
|
13
20
|
send(msg);
|
|
14
21
|
});
|
|
15
22
|
}
|