EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
agent-desktop-0.0.2.js
Go to the documentation of this file.
1/**
2* @description Remote Desktop
3* @author Ylian Saint-Hilaire
4* @version v0.0.2g
5*/
6
7// Polyfill Uint8Array.slice() for IE
8if (!Uint8Array.prototype.slice) { Object.defineProperty(Uint8Array.prototype, 'slice', { value: function (begin, end) { return new Uint8Array(Array.prototype.slice.call(this, begin, end)); } }); }
9
10function isWindowsBrowser() {
11 return navigator && !!(/win/i).exec(navigator.platform);
12}
13
14// Construct a MeshServer object
15var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
16 var obj = {}
17 obj.CanvasId = canvasid;
18 if (typeof canvasid === 'string') obj.CanvasId = Q(canvasid);
19 obj.Canvas = obj.CanvasId.getContext('2d');
20 obj.scrolldiv = scrolldiv;
21 obj.State = 0;
22 obj.PendingOperations = [];
23 obj.tilesReceived = 0;
24 obj.TilesDrawn = 0;
25 obj.KillDraw = 0;
26 obj.ipad = false;
27 obj.tabletKeyboardVisible = false;
28 obj.LastX = 0;
29 obj.LastY = 0;
30 obj.touchenabled = 0;
31 obj.submenuoffset = 0;
32 obj.touchtimer = null;
33 obj.TouchArray = {};
34 obj.connectmode = 0; // 0 = HTTP, 1 = WebSocket, 2 = WebRTC
35 obj.connectioncount = 0;
36 obj.rotation = 0;
37 obj.protocol = 2; // KVM
38 obj.debugmode = 0;
39 obj.firstUpKeys = [];
40 obj.stopInput = false;
41 obj.localKeyMap = true;
42 obj.remoteKeyMap = false; // If false, the remote keyboard mapping is not used.
43 obj.pressedKeys = [];
44 obj._altGrArmed = false; // Windows AltGr detection
45 obj._altGrTimeout = 0;
46 obj.isWindowsBrowser = isWindowsBrowser();
47
48 obj.sessionid = 0;
49 obj.username;
50 obj.oldie = false;
51 obj.ImageType = 1; // 1 = JPEG, 2 = PNG, 3 = TIFF, 4 = WebP
52 obj.CompressionLevel = 50;
53 obj.ScalingLevel = 1024;
54 obj.FrameRateTimer = 100;
55 obj.SwapMouse = false;
56 obj.UseExtendedKeyFlag = true;
57 obj.FirstDraw = false;
58
59 // Remote user mouse and keyboard lock
60 obj.onRemoteInputLockChanged = null;
61 obj.RemoteInputLock = null;
62
63 // Remote keyboard state
64 obj.onKeyboardStateChanged = null;
65 obj.KeyboardState = 0; // 1 = NumLock, 2 = ScrollLock, 4 = CapsLock
66
67 obj.ScreenWidth = 960;
68 obj.ScreenHeight = 701;
69 obj.width = 960;
70 obj.height = 960;
71
72 obj.displays = null;
73 obj.selectedDisplay = null;
74
75 obj.onScreenSizeChange = null;
76 obj.onMessage = null;
77 obj.onConnectCountChanged = null;
78 obj.onDebugMessage = null;
79 obj.onTouchEnabledChanged = null;
80 obj.onDisplayinfo = null;
81 obj.accumulator = null;
82
83 var xMouseCursorActive = true;
84 var xMouseCursorCurrent = 'default';
85 obj.mouseCursorActive = function (x) { if (xMouseCursorActive == x) return; xMouseCursorActive = x; obj.CanvasId.style.cursor = ((x == true) ? xMouseCursorCurrent : 'default'); }
86 var mouseCursors = ['default', 'progress', 'crosshair', 'pointer', 'help', 'text', 'no-drop', 'move', 'nesw-resize', 'ns-resize', 'nwse-resize', 'w-resize', 'alias', 'wait', 'none', 'not-allowed', 'col-resize', 'row-resize', 'copy', 'zoom-in', 'zoom-out'];
87
88 obj.Start = function () {
89 obj.State = 0;
90 obj.accumulator = null;
91 }
92
93 obj.Stop = function () {
94 obj.setRotation(0);
95 obj.UnGrabKeyInput();
96 obj.UnGrabMouseInput();
97 obj.touchenabled = 0;
98 if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId); }
99 obj.Canvas.clearRect(0, 0, obj.CanvasId.width, obj.CanvasId.height);
100 }
101
102 obj.xxStateChange = function (newstate) {
103 if (obj.State == newstate) return;
104 obj.State = newstate;
105 obj.CanvasId.style.cursor = 'default';
106 //console.log('xxStateChange', newstate);
107 switch (newstate) {
108 case 0: {
109 // Disconnect
110 obj.Stop();
111 break;
112 }
113 case 3: {
114 // Websocket connected
115
116 break;
117 }
118 }
119 }
120
121 obj.send = function (x) {
122 if (obj.debugmode > 2) { console.log('KSend(' + x.length + '): ' + rstr2hex(x)); }
123 if (obj.parent != null) { obj.parent.send(x); }
124 }
125
126 // KVM Control.
127 // Routines for processing incoming packets from the AJAX server, and handling individual messages.
128 obj.ProcessPictureMsg = function (data, X, Y) {
129 //if (obj.targetnode != null) obj.Debug("ProcessPictureMsg " + X + "," + Y + " - " + obj.targetnode.substring(0, 8));
130 var tile = new Image();
131 tile.xcount = obj.tilesReceived++;
132 var r = obj.tilesReceived, tdata = data.slice(4), ptr = 0, strs = [];
133 // String.fromCharCode.apply() can't handle very large argument count, so we have to split like this.
134 while ((tdata.byteLength - ptr) > 50000) { strs.push(String.fromCharCode.apply(null, tdata.slice(ptr, ptr + 50000))); ptr += 50000; }
135 if (ptr > 0) { strs.push(String.fromCharCode.apply(null, tdata.slice(ptr))); } else { strs.push(String.fromCharCode.apply(null, tdata)); }
136 tile.src = 'data:image/jpeg;base64,' + btoa(strs.join(''));
137 tile.onload = function () {
138 //console.log('DecodeTile #' + this.xcount);
139 if ((obj.Canvas != null) && (obj.KillDraw < r) && (obj.State != 0)) {
140 obj.PendingOperations.push([r, 2, tile, X, Y]);
141 while (obj.DoPendingOperations()) { }
142 } else {
143 obj.PendingOperations.push([r, 0]);
144 }
145 }
146 tile.error = function () { console.log('DecodeTileError'); }
147 }
148
149 obj.DoPendingOperations = function () {
150 if (obj.PendingOperations.length == 0) return false;
151 for (var i = 0; i < obj.PendingOperations.length; i++) { // && KillDraw < tilesDrawn
152 var Msg = obj.PendingOperations[i];
153 if (Msg[0] == (obj.TilesDrawn + 1)) {
154 if (obj.onPreDrawImage != null) obj.onPreDrawImage(); // Notify that we are about to draw on the canvas.
155 if (Msg[1] == 1) { obj.ProcessCopyRectMsg(Msg[2]); }
156 else if (Msg[1] == 2) { obj.Canvas.drawImage(Msg[2], obj.rotX(Msg[3], Msg[4]), obj.rotY(Msg[3], Msg[4])); delete Msg[2]; }
157 obj.PendingOperations.splice(i, 1);
158 obj.TilesDrawn++;
159 if ((obj.TilesDrawn == obj.tilesReceived) && (obj.KillDraw < obj.TilesDrawn)) { obj.KillDraw = obj.TilesDrawn = obj.tilesReceived = 0; }
160 return true;
161 }
162 }
163 if (obj.oldie && obj.PendingOperations.length > 0) { obj.TilesDrawn++; }
164 return false;
165 }
166
167 obj.ProcessCopyRectMsg = function (str) {
168 var SX = ((str.charCodeAt(0) & 0xFF) << 8) + (str.charCodeAt(1) & 0xFF);
169 var SY = ((str.charCodeAt(2) & 0xFF) << 8) + (str.charCodeAt(3) & 0xFF);
170 var DX = ((str.charCodeAt(4) & 0xFF) << 8) + (str.charCodeAt(5) & 0xFF);
171 var DY = ((str.charCodeAt(6) & 0xFF) << 8) + (str.charCodeAt(7) & 0xFF);
172 var WIDTH = ((str.charCodeAt(8) & 0xFF) << 8) + (str.charCodeAt(9) & 0xFF);
173 var HEIGHT = ((str.charCodeAt(10) & 0xFF) << 8) + (str.charCodeAt(11) & 0xFF);
174 obj.Canvas.drawImage(Canvas.canvas, SX, SY, WIDTH, HEIGHT, DX, DY, WIDTH, HEIGHT);
175 }
176
177 obj.SendUnPause = function () {
178 if (obj.debugmode > 1) { console.log('SendUnPause'); }
179 //obj.xxStateChange(3);
180 obj.send(String.fromCharCode(0x00, 0x08, 0x00, 0x05, 0x00));
181 }
182
183 obj.SendPause = function () {
184 if (obj.debugmode > 1) { console.log('SendPause'); }
185 //obj.xxStateChange(2);
186 obj.send(String.fromCharCode(0x00, 0x08, 0x00, 0x05, 0x01));
187 }
188
189 obj.SendCompressionLevel = function (type, level, scaling, frametimer) { // Type: 1 = JPEG, 2 = PNG, 3 = TIFF, 4 = WebP
190 obj.ImageType = type;
191 if (level) { obj.CompressionLevel = level; }
192 if (scaling) { obj.ScalingLevel = scaling; }
193 if (frametimer) { obj.FrameRateTimer = frametimer; }
194 obj.send(String.fromCharCode(0x00, 0x05, 0x00, 0x0A, type, obj.CompressionLevel) + obj.shortToStr(obj.ScalingLevel) + obj.shortToStr(obj.FrameRateTimer));
195 }
196
197 obj.SendRefresh = function () {
198 obj.send(String.fromCharCode(0x00, 0x06, 0x00, 0x04));
199 }
200
201 obj.ProcessScreenMsg = function (width, height) {
202 if (obj.debugmode > 0) { console.log('ScreenSize: ' + width + ' x ' + height); }
203 if ((obj.ScreenWidth == width) && (obj.ScreenHeight == height)) return; // Ignore change if screen is same size.
204 obj.Canvas.setTransform(1, 0, 0, 1, 0, 0);
205 obj.rotation = 0;
206 obj.FirstDraw = true;
207 obj.ScreenWidth = obj.width = width;
208 obj.ScreenHeight = obj.height = height;
209 obj.KillDraw = obj.tilesReceived;
210 while (obj.PendingOperations.length > 0) { obj.PendingOperations.shift(); }
211 obj.SendCompressionLevel(obj.ImageType);
212 obj.SendUnPause();
213 obj.SendRemoteInputLock(2); // Query input lock state
214 // No need to event the display size change now, it will be evented on first draw.
215 if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId); }
216 }
217
218 obj.ProcessBinaryCommand = function (cmd, cmdsize, view) {
219 var X, Y;
220 if ((cmd == 3) || (cmd == 4) || (cmd == 7)) { X = (view[4] << 8) + view[5]; Y = (view[6] << 8) + view[7]; }
221 if (obj.debugmode > 2) { console.log('CMD', cmd, cmdsize, X, Y); }
222
223 // Fix for view being too large for String.fromCharCode.apply()
224 var chunkSize = 10000;
225 let result = '';
226 for (let i = 0; i < view.length; i += chunkSize) { result += String.fromCharCode.apply(null, view.slice(i, i + chunkSize)); }
227 // Record the command if needed
228 if (obj.recordedData != null) {
229 if (cmdsize > 65000) {
230 obj.recordedData.push(recordingEntry(2, 1, obj.shortToStr(27) + obj.shortToStr(8) + obj.intToStr(cmdsize) + obj.shortToStr(cmd) + obj.shortToStr(0) + obj.shortToStr(0) + obj.shortToStr(0) + result));
231 } else {
232 obj.recordedData.push(recordingEntry(2, 1, result));
233 }
234 }
235
236 switch (cmd) {
237 case 3: // Tile
238 if (obj.FirstDraw) obj.onResize();
239 //console.log('TILE', X, Y, cmdsize);
240 obj.ProcessPictureMsg(view.slice(4), X, Y);
241 break;
242 case 7: // Screen size
243 obj.ProcessScreenMsg(X, Y);
244 obj.SendKeyMsgKC(obj.KeyAction.UP, 16); // Shift
245 obj.SendKeyMsgKC(obj.KeyAction.UP, 17); // Ctrl
246 obj.SendKeyMsgKC(obj.KeyAction.UP, 18); // Alt
247 obj.SendKeyMsgKC(obj.KeyAction.UP, 91); // Left-Windows
248 obj.SendKeyMsgKC(obj.KeyAction.UP, 92); // Right-Windows
249 obj.SendKeyMsgKC(obj.KeyAction.UP, 16); // Shift
250 obj.send(String.fromCharCode(0x00, 0x0E, 0x00, 0x04));
251 break;
252 case 11: // GetDisplays (TODO)
253 var selectedDisplay = 0, displays = {}, dcount = (view[4] << 8) + view[5];
254 if (dcount > 0) {
255 // Many displays present
256 selectedDisplay = (view[6 + (dcount * 2)] << 8) + view[7 + (dcount * 2)];
257 for (var i = 0; i < dcount; i++) {
258 var disp = (view[6 + (i * 2)] << 8) + view[7 + (i * 2)];
259 if (disp == 65535) { displays[disp] = 'All Displays'; } else { displays[disp] = 'Display ' + disp; }
260 }
261 }
262 //console.log('Get Displays', displays, selectedDisplay, rstr2hex(str));
263 obj.displays = displays; obj.selectedDisplay = selectedDisplay;
264 if (obj.onDisplayinfo != null) { obj.onDisplayinfo(obj, displays, selectedDisplay); }
265 break;
266 case 12: // SetDisplay
267 //console.log('SetDisplayConfirmed');
268 break;
269 case 14: // KVM_INIT_TOUCH
270 obj.touchenabled = 1;
271 obj.TouchArray = {};
272 if (obj.onTouchEnabledChanged != null) obj.onTouchEnabledChanged(obj.touchenabled);
273 break;
274 case 15: // KVM_TOUCH
275 obj.TouchArray = {};
276 break;
277 case 17: // MNG_KVM_MESSAGE
278 var str = String.fromCharCode.apply(null, view.slice(4));
279 console.log('Got KVM Message: ' + str);
280 if (obj.onMessage != null) obj.onMessage(str, obj);
281 break;
282 case 18: // MNG_KVM_KEYSTATE
283 if ((cmdsize != 5) || (obj.KeyboardState == view[4])) break;
284 obj.KeyboardState = view[4]; // 1 = NumLock, 2 = ScrollLock, 4 = CapsLock
285 if (obj.onKeyboardStateChanged) { obj.onKeyboardStateChanged(obj, obj.KeyboardState); }
286 console.log('MNG_KVM_KEYSTATE:' + ((obj.KeyboardState & 1) ? ' NumLock' : '') + ((obj.KeyboardState & 2) ? ' ScrollLock' : '') + ((obj.KeyboardState & 4) ? ' CapsLock' : ''));
287 break;
288 case 65: // Alert
289 var str = String.fromCharCode.apply(null, view.slice(4));
290 if (str[0] != '.') {
291 console.log(str); //alert('KVM: ' + str);
292 if (obj.parent && obj.parent.setConsoleMessage) { obj.parent.setConsoleMessage(str); }
293 } else {
294 console.log('KVM: ' + str.substring(1));
295 }
296 break;
297 case 82: // DISPLAY LOCATION & SIZE
298 if ((cmdsize < 4) || (((cmdsize - 4) % 10) != 0)) break;
299 var screenCount = ((cmdsize - 4) / 10), screenInfo = {}, ptr = 4;
300 for (var i = 0; i < screenCount; i++) { screenInfo[(view[ptr + 0] << 8) + view[ptr + 1]] = { x: ((view[ptr + 2] << 8) + view[ptr + 3]), y: ((view[ptr + 4] << 8) + view[ptr + 5]), w: ((view[ptr + 6] << 8) + view[ptr + 7]), h: ((view[ptr + 8] << 8) + view[ptr + 9]) }; ptr += 10; }
301 //console.log('ScreenInfo', JSON.stringify(screenInfo, null, 2));
302 break;
303 case 87: // MNG_KVM_INPUT_LOCK
304 if (cmdsize != 5) break;
305 if ((obj.RemoteInputLock == null) || (obj.RemoteInputLock !== (view[4] != 0))) {
306 obj.RemoteInputLock = (view[4] != 0);
307 if (obj.onRemoteInputLockChanged) { obj.onRemoteInputLockChanged(obj, obj.RemoteInputLock); }
308 }
309 break;
310 case 88: // MNG_KVM_MOUSE_CURSOR
311 if ((cmdsize != 5) || (obj.stopInput)) break;
312 var cursorNum = view[4];
313 if (cursorNum > mouseCursors.length) { cursorNum = 0; }
314 xMouseCursorCurrent = mouseCursors[cursorNum];
315 if (xMouseCursorActive) { obj.CanvasId.style.cursor = xMouseCursorCurrent; }
316 break;
317 default:
318 console.log('Unknown command', cmd, cmdsize);
319 break;
320 }
321
322 }
323
324 // Keyboard and Mouse I/O.
325 obj.MouseButton = { "NONE": 0x00, "LEFT": 0x02, "RIGHT": 0x08, "MIDDLE": 0x20 };
326 obj.KeyAction = { "NONE": 0, "DOWN": 1, "UP": 2, "SCROLL": 3, "EXUP": 4, "EXDOWN": 5, "DBLCLICK": 6 };
327 obj.InputType = { "KEY": 1, "MOUSE": 2, "CTRLALTDEL": 10, "TOUCH": 15, "KEYUNICODE": 85 };
328 obj.Alternate = 0;
329
330 var convertKeyCodeTable = {
331 "Pause": 19,
332 "CapsLock": 20,
333 "Space": 32,
334 "Quote": 222,
335 "Minus": 189,
336 "NumpadMultiply": 106,
337 "NumpadAdd": 107,
338 "PrintScreen": 44,
339 "Comma": 188,
340 "NumpadSubtract": 109,
341 "NumpadDecimal": 110,
342 "Period": 190,
343 "Slash": 191,
344 "NumpadDivide": 111,
345 "Semicolon": 186,
346 "Equal": 187,
347 "OSLeft": 91,
348 "BracketLeft": 219,
349 "OSRight": 91,
350 "Backslash": 220,
351 "BracketRight": 221,
352 "ContextMenu": 93,
353 "Backquote": 192,
354 "NumLock": 144,
355 "ScrollLock": 145,
356 "Backspace": 8,
357 "Tab": 9,
358 "Enter": 13,
359 "NumpadEnter": 13,
360 "Escape": 27,
361 "Delete": 46,
362 "Home": 36,
363 "PageUp": 33,
364 "PageDown": 34,
365 "ArrowLeft": 37,
366 "ArrowUp": 38,
367 "ArrowRight": 39,
368 "ArrowDown": 40,
369 "End": 35,
370 "Insert": 45,
371 "F1": 112,
372 "F2": 113,
373 "F3": 114,
374 "F4": 115,
375 "F5": 116,
376 "F6": 117,
377 "F7": 118,
378 "F8": 119,
379 "F9": 120,
380 "F10": 121,
381 "F11": 122,
382 "F12": 123,
383 "ShiftLeft": 16,
384 "ShiftRight": 16,
385 "ControlLeft": 17,
386 "ControlRight": 17,
387 "AltLeft": 18,
388 "AltRight": 18,
389 "MetaLeft": 91,
390 "MetaRight": 92,
391 "VolumeMute": 181
392 //"LaunchMail":
393 //"LaunchApp1":
394 //"LaunchApp2":
395 //"BrowserStop":
396 //"MediaStop":
397 //"MediaTrackPrevious":
398 //"MediaTrackNext":
399 //"MediaPlayPause":
400 //"MediaSelect":
401 }
402
403 function convertKeyCode(e) {
404 if (e.code.startsWith('Key') && e.code.length == 4) { return e.code.charCodeAt(3); }
405 if (e.code.startsWith('Digit') && e.code.length == 6) { return e.code.charCodeAt(5); }
406 if (e.code.startsWith('Numpad') && e.code.length == 7) { return e.code.charCodeAt(6) + 48; }
407 return convertKeyCodeTable[e.code];
408 }
409
410 var extendedKeyTable = ['ShiftRight', 'AltRight', 'ControlRight', 'Home', 'End', 'Insert', 'Delete', 'PageUp', 'PageDown', 'NumpadDivide', 'NumpadEnter', 'NumLock', 'Pause'];
411 obj.SendKeyMsg = function (action, event) {
412 if (action == null) return;
413 if (!event) { event = window.event; }
414
415 var extendedKey = false; // Test feature, add ?extkeys=1 to url to use.
416
417 if ((obj.UseExtendedKeyFlag || (urlargs.extkeys == 1)) && (typeof event.code == 'string') && (event.code.startsWith('Arrow') || (extendedKeyTable.indexOf(event.code) >= 0))) {
418 extendedKey = true;
419 }
420
421 if (obj.isWindowsBrowser) {
422 if( obj.checkAltGr(obj, event, action) ) {
423 return;
424 };
425 }
426
427 if ((extendedKey == false) && event.code && (event.code.startsWith('NumPad') == false) && (obj.localKeyMap == false)) {
428 // Convert "event.code" into a scancode. This works the same regardless of the keyboard language.
429 // Older browsers will not support this.
430 var kc = convertKeyCode(event);
431 if (kc != null) { obj.SendKeyMsgKC(action, kc, extendedKey); }
432 } else {
433 // Use this keycode, this works best with "US-EN" keyboards.
434 // Older browser support this.
435 var kc = event.keyCode;
436 if (kc == 0x3B) { kc = 0xBA; } // Fix the ';' key
437 else if (kc == 173) { kc = 189; } // Fix the '-' key for Firefox
438 else if (kc == 61) { kc = 187; } // Fix the '=' key for Firefox
439 obj.SendKeyMsgKC(action, kc, extendedKey);
440 }
441 }
442
443 const ControlLeftKc = 17;
444 const AltGrKc = 225;
445 //return true: Key is alredy handled.
446 obj.checkAltGr = function (obj, event, action) {
447 // Windows doesn't have a proper AltGr, but handles it using
448 // fake Ctrl+Alt. However the remote end might not be Windows,
449 // so we need to merge those into a single AltGr event. We
450 // detect this case by seeing the two key events directly after
451 // each other with a very short time between them (<50ms).
452 if (obj._altGrArmed) {
453 obj._altGrArmed = false;
454 clearTimeout(obj._altGrTimeout);
455
456 if ((event.code === "AltRight") && ((event.timeStamp - obj._altGrCtrlTime) < 50)) {
457 //AltGr detected.
458 obj.SendKeyMsgKC( action, AltGrKc, false);
459 return true;
460 }
461 }
462
463 // Possible start of AltGr sequence?
464 if ((event.code === "ControlLeft") && !(ControlLeftKc in obj.pressedKeys)) {
465 obj._altGrArmed = true;
466 obj._altGrCtrlTime = event.timeStamp;
467 if( action == 1 ) {
468 obj._altGrTimeout = setTimeout(obj._handleAltGrTimeout.bind(obj), 100);
469 return true;
470 }
471 }
472 return false;
473 }
474
475 obj._handleAltGrTimeout = function () { //Windows and no Ctrl+Alt -> send only Ctrl.
476 obj._altGrArmed = false;
477 clearTimeout(obj._altGrTimeout);
478 obj.SendKeyMsgKC( 1, ControlLeftKc, false); // (KeyDown, "ControlLeft", false)
479 }
480
481 // Send remote input lock. 0 = Unlock, 1 = Lock, 2 = Query
482 obj.SendRemoteInputLock = function (code) { obj.send(String.fromCharCode(0x00, 87, 0x00, 0x05, code)); }
483
484 obj.SendMessage = function (msg) {
485 if (obj.State == 3) obj.send(String.fromCharCode(0x00, 0x11) + obj.shortToStr(4 + msg.length) + msg); // 0x11 = 17 MNG_KVM_MESSAGE
486 }
487
488 obj.SendKeyMsgKC = function (action, kc, extendedKey) {
489 if (obj.State != 3) return;
490 if (typeof action == 'object') { for (var i in action) { obj.SendKeyMsgKC(action[i][0], action[i][1], action[i][2]); } }
491 else {
492 if (action == 1) { // Key Down
493 if (obj.pressedKeys.indexOf(kc) == -1) { obj.pressedKeys.unshift(kc); } // Add key press to start of array
494 } else if (action == 2) { // Key Up
495 var i = obj.pressedKeys.indexOf(kc);
496 if (i != -1) { obj.pressedKeys.splice(i, 1); } // Remove the key press from the pressed array
497 }
498 if (obj.debugmode > 0) { console.log('Sending Key ' + kc + ', action ' + action); }
499
500 var up = (action - 1);
501 if (extendedKey) { if (up == 1) { up = 3; } else { up = 4; } }
502 obj.send(String.fromCharCode(0x00, obj.InputType.KEY, 0x00, 0x06, up, kc));
503 }
504 }
505
506 obj.SendStringUnicode = function (str) {
507 if (obj.State != 3) return;
508 for (var i = 0; i < str.length; i++) {
509 obj.send(String.fromCharCode(0x00, obj.InputType.KEYUNICODE, 0x00, 0x07, 0) + ShortToStr(str.charCodeAt(i)));
510 obj.send(String.fromCharCode(0x00, obj.InputType.KEYUNICODE, 0x00, 0x07, 1) + ShortToStr(str.charCodeAt(i)));
511 }
512 }
513
514 obj.SendKeyUnicode = function (action, val) {
515 if (obj.State != 3) return;
516 if (obj.debugmode > 0) { console.log('Sending UnicodeKey ' + val + ', action ' + action); }
517 obj.send(String.fromCharCode(0x00, obj.InputType.KEYUNICODE, 0x00, 0x07, (action - 1)) + ShortToStr(val));
518 }
519
520 obj.sendcad = function() { obj.SendCtrlAltDelMsg(); }
521
522 obj.SendCtrlAltDelMsg = function () {
523 if (obj.State == 3) { obj.send(String.fromCharCode(0x00, obj.InputType.CTRLALTDEL, 0x00, 0x04)); }
524 }
525
526 obj.SendEscKey = function () {
527 if (obj.State == 3) obj.send(String.fromCharCode(0x00, obj.InputType.KEY, 0x00, 0x06, 0x00, 0x1B, 0x00, obj.InputType.KEY, 0x00, 0x06, 0x01, 0x1B));
528 }
529
530 obj.SendStartMsg = function () {
531 obj.SendKeyMsgKC(obj.KeyAction.EXDOWN, 0x5B); // L-Windows
532 obj.SendKeyMsgKC(obj.KeyAction.EXUP, 0x5B); // L-Windows
533 }
534
535 obj.SendCharmsMsg = function () {
536 obj.SendKeyMsgKC(obj.KeyAction.EXDOWN, 0x5B); // L-Windows
537 obj.SendKeyMsgKC(obj.KeyAction.DOWN, 67); // C
538 obj.SendKeyMsgKC(obj.KeyAction.UP, 67); // C
539 obj.SendKeyMsgKC(obj.KeyAction.EXUP, 0x5B); // L-Windows
540 }
541
542 obj.SendTouchMsg1 = function (id, flags, x, y) {
543 if (obj.State == 3) obj.send(String.fromCharCode(0x00, obj.InputType.TOUCH) + obj.shortToStr(14) + String.fromCharCode(0x01, id) + obj.intToStr(flags) + obj.shortToStr(x) + obj.shortToStr(y));
544 }
545
546 obj.SendTouchMsg2 = function (id, flags) {
547 var msg = '';
548 var flags2;
549 var str = "TOUCHSEND: ";
550 for (var k in obj.TouchArray) {
551 if (k == id) { flags2 = flags; } else {
552 if (obj.TouchArray[k].f == 1) { flags2 = 0x00010000 | 0x00000002 | 0x00000004; obj.TouchArray[k].f = 3; str += "START" + k; } // POINTER_FLAG_DOWN
553 else if (obj.TouchArray[k].f == 2) { flags2 = 0x00040000; str += "STOP" + k; } // POINTER_FLAG_UP
554 else flags2 = 0x00000002 | 0x00000004 | 0x00020000; // POINTER_FLAG_UPDATE
555 }
556 msg += String.fromCharCode(k) + obj.intToStr(flags2) + obj.shortToStr(obj.TouchArray[k].x) + obj.shortToStr(obj.TouchArray[k].y);
557 if (obj.TouchArray[k].f == 2) delete obj.TouchArray[k];
558 }
559 if (obj.State == 3) obj.send(String.fromCharCode(0x00, obj.InputType.TOUCH) + obj.shortToStr(5 + msg.length) + String.fromCharCode(0x02) + msg);
560 if (Object.keys(obj.TouchArray).length == 0 && obj.touchtimer != null) { clearInterval(obj.touchtimer); obj.touchtimer = null; }
561 }
562
563 obj.SendMouseMsg = function (Action, event) {
564 if (obj.State != 3) return;
565 if (Action != null && obj.Canvas != null) {
566 if (!event) { var event = window.event; }
567
568 var ScaleFactorHeight = (obj.Canvas.canvas.height / obj.CanvasId.clientHeight);
569 var ScaleFactorWidth = (obj.Canvas.canvas.width / obj.CanvasId.clientWidth);
570 var Offsets = obj.GetPositionOfControl(obj.Canvas.canvas);
571 var X = ((event.pageX - Offsets[0]) * ScaleFactorWidth);
572 var Y = ((event.pageY - Offsets[1]) * ScaleFactorHeight);
573 if (event.addx) { X += event.addx; }
574 if (event.addy) { Y += event.addy; }
575
576 if (X >= 0 && X <= obj.Canvas.canvas.width && Y >= 0 && Y <= obj.Canvas.canvas.height) {
577 var Button = 0;
578 var Delta = 0;
579 if (Action == obj.KeyAction.UP || Action == obj.KeyAction.DOWN) {
580 if (event.which) { ((event.which == 1) ? (Button = obj.MouseButton.LEFT) : ((event.which == 2) ? (Button = obj.MouseButton.MIDDLE) : (Button = obj.MouseButton.RIGHT))); }
581 else if (typeof event.button == 'number') { ((event.button == 0) ? (Button = obj.MouseButton.LEFT) : ((event.button == 1) ? (Button = obj.MouseButton.MIDDLE) : (Button = obj.MouseButton.RIGHT))); }
582 }
583 else if (Action == obj.KeyAction.SCROLL) {
584 if (event.detail) { Delta = (-1 * (event.detail * 120)); } else if (event.wheelDelta) { Delta = (event.wheelDelta * 3); }
585 }
586
587 // Swap mouse buttons if needed
588 if (obj.SwapMouse === true) {
589 if (Button == obj.MouseButton.LEFT) { Button = obj.MouseButton.RIGHT; }
590 else if (Button == obj.MouseButton.RIGHT) { Button = obj.MouseButton.LEFT; }
591 }
592
593 // Reverse mouse wheel if needed
594 if (obj.ReverseMouseWheel) { Delta = -1 * Delta; }
595
596 var MouseMsg = "";
597 if (Action == obj.KeyAction.DBLCLICK) {
598 MouseMsg = String.fromCharCode(0x00, obj.InputType.MOUSE, 0x00, 0x0A, 0x00, 0x88, ((X / 256) & 0xFF), (X & 0xFF), ((Y / 256) & 0xFF), (Y & 0xFF));
599 } else if (Action == obj.KeyAction.SCROLL) {
600 var deltaHigh = 0, deltaLow = 0;
601 if (Delta < 0) { deltaHigh = (255 - (Math.abs(Delta) >> 8)); deltaLow = (255 - (Math.abs(Delta) & 0xFF)); } else { deltaHigh = (Delta >> 8); deltaLow = (Delta & 0xFF); }
602 MouseMsg = String.fromCharCode(0x00, obj.InputType.MOUSE, 0x00, 0x0C, 0x00, 0x00, ((X / 256) & 0xFF), (X & 0xFF), ((Y / 256) & 0xFF), (Y & 0xFF), deltaHigh, deltaLow);
603 } else {
604 MouseMsg = String.fromCharCode(0x00, obj.InputType.MOUSE, 0x00, 0x0A, 0x00, ((Action == obj.KeyAction.DOWN) ? Button : ((Button * 2) & 0xFF)), ((X / 256) & 0xFF), (X & 0xFF), ((Y / 256) & 0xFF), (Y & 0xFF));
605 }
606
607 if (obj.Action == obj.KeyAction.NONE) {
608 if (obj.Alternate == 0 || obj.ipad) { obj.send(MouseMsg); obj.Alternate = 1; } else { obj.Alternate = 0; }
609 } else {
610 obj.send(MouseMsg);
611 }
612 }
613 }
614 }
615
616 obj.GetDisplayNumbers = function () { obj.send(String.fromCharCode(0x00, 0x0B, 0x00, 0x04)); } // Get Terminal display
617 obj.SetDisplay = function (number) { /*console.log('Set display', number);*/ obj.send(String.fromCharCode(0x00, 0x0C, 0x00, 0x06, number >> 8, number & 0xFF)); } // Set Terminal display
618 obj.intToStr = function (x) { return String.fromCharCode((x >> 24) & 0xFF, (x >> 16) & 0xFF, (x >> 8) & 0xFF, x & 0xFF); }
619 obj.shortToStr = function (x) { return String.fromCharCode((x >> 8) & 0xFF, x & 0xFF); }
620
621 obj.onResize = function () {
622 if (obj.ScreenWidth == 0 || obj.ScreenHeight == 0) return;
623 if ((obj.Canvas.canvas.width == obj.ScreenWidth) && (obj.Canvas.canvas.height == obj.ScreenHeight)) return;
624 if (obj.FirstDraw) {
625 obj.Canvas.canvas.width = obj.ScreenWidth;
626 obj.Canvas.canvas.height = obj.ScreenHeight;
627 obj.Canvas.fillRect(0, 0, obj.ScreenWidth, obj.ScreenHeight);
628 if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId); }
629 }
630 obj.FirstDraw = false;
631 if (obj.debugmode > 1) { console.log("onResize: " + obj.ScreenWidth + " x " + obj.ScreenHeight); }
632 }
633
634 obj.xxMouseInputGrab = false;
635 obj.xxKeyInputGrab = false;
636 obj.xxMouseMove = function (e) { if (obj.State == 3) obj.SendMouseMsg(obj.KeyAction.NONE, e); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
637 obj.xxMouseUp = function (e) { if (obj.State == 3) obj.SendMouseMsg(obj.KeyAction.UP, e); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
638 obj.xxMouseDown = function (e) { if (obj.State == 3) obj.SendMouseMsg(obj.KeyAction.DOWN, e); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
639 obj.xxMouseDblClick = function (e) { if (obj.State == 3) obj.SendMouseMsg(obj.KeyAction.DBLCLICK, e); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
640 obj.xxDOMMouseScroll = function (e) { if (obj.State == 3) { obj.SendMouseMsg(obj.KeyAction.SCROLL, e); return false; } return true; }
641 obj.xxMouseWheel = function (e) { if (obj.State == 3) { obj.SendMouseMsg(obj.KeyAction.SCROLL, e); return false; } return true; }
642 obj.xxKeyUp = function (e) {
643 if ((e.key != 'Dead') && (obj.State == 3)) {
644 if ((typeof e.key == 'string') && (e.key.length == 1) && (e.ctrlKey != true) && (e.altKey != true) && (obj.remoteKeyMap == false)) {
645 obj.SendKeyUnicode(obj.KeyAction.UP, e.key.charCodeAt(0));
646 } else {
647 obj.SendKeyMsg(obj.KeyAction.UP, e);
648 }
649 }
650 if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false;
651 }
652 obj.xxKeyDown = function (e) {
653 if ((e.key != 'Dead') && (obj.State == 3)) {
654 if (!((typeof e.key == 'string') && (e.key.length == 1) && (e.ctrlKey != true) && (e.altKey != true) && (obj.remoteKeyMap == false))) {
655 obj.SendKeyMsg(obj.KeyAction.DOWN, e);
656 if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false;
657 }
658 }
659 }
660 obj.xxKeyPress = function (e) {
661 if ((e.key != 'Dead') && (obj.State == 3)) {
662 if ((typeof e.key == 'string') && (e.key.length == 1) && (e.ctrlKey != true) && (e.altKey != true) && (obj.remoteKeyMap == false)) {
663 obj.SendKeyUnicode(obj.KeyAction.DOWN, e.key.charCodeAt(0));
664 } // else { obj.SendKeyMsg(obj.KeyAction.DOWN, e); }
665 }
666 if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false;
667 }
668
669 // Key handlers
670 obj.handleKeys = function (e) {
671 //console.log('keypress', e.code, e.key, e.keyCode, (e.key.length == 1) ? e.key.charCodeAt(0) : 0);
672 if (obj.stopInput == true || desktop.State != 3) return false;
673 return obj.xxKeyPress(e);
674 }
675 obj.handleKeyUp = function (e) {
676 //console.log('keyup', e.code, e.key, e.keyCode, (e.key.length == 1)?e.key.charCodeAt(0):0);
677 if (obj.stopInput == true || desktop.State != 3) return false;
678 if (obj.firstUpKeys.length < 5) {
679 obj.firstUpKeys.push(e.keyCode);
680 if ((obj.firstUpKeys.length == 5)) { var j = obj.firstUpKeys.join(','); if ((j == '16,17,91,91,16') || (j == '16,17,18,91,92')) { obj.stopInput = true; } }
681 }
682 return obj.xxKeyUp(e);
683 }
684 obj.handleKeyDown = function (e) {
685 //console.log('keydown', e.code, e.key, e.keyCode, (e.key.length == 1) ? e.key.charCodeAt(0) : 0);
686 if (obj.stopInput == true || desktop.State != 3) return false;
687 return obj.xxKeyDown(e);
688 }
689
690 // Release the CTRL, ALT, SHIFT keys if they are pressed.
691 obj.handleReleaseKeys = function () {
692 var p = JSON.parse(JSON.stringify(obj.pressedKeys)); // Clone the pressed array
693 for (var i in p) { obj.SendKeyMsgKC(obj.KeyAction.UP, p[i]); } // Release all keys
694 }
695
696 // Mouse handlers
697 obj.mousedblclick = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseDblClick(e); }
698 obj.mousedown = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseDown(e); }
699 obj.mouseup = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseUp(e); }
700 obj.mousemove = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseMove(e); }
701 obj.mousewheel = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseWheel(e); }
702
703 obj.xxMsTouchEvent = function (evt) {
704 if (evt.originalEvent.pointerType == 4) return; // If this is a mouse pointer, ignore this event. Touch & pen are ok.
705 if (evt.preventDefault) evt.preventDefault();
706 if (evt.stopPropagation) evt.stopPropagation();
707 if (evt.type == 'MSPointerDown' || evt.type == 'MSPointerMove' || evt.type == 'MSPointerUp') {
708 var flags = 0;
709 var id = evt.originalEvent.pointerId % 256;
710 var X = evt.offsetX * (Canvas.canvas.width / obj.CanvasId.clientWidth);
711 var Y = evt.offsetY * (Canvas.canvas.height / obj.CanvasId.clientHeight);
712
713 if (evt.type == 'MSPointerDown') flags = 0x00010000 | 0x00000002 | 0x00000004; // POINTER_FLAG_DOWN
714 else if (evt.type == 'MSPointerMove') {
715 //if (obj.TouchArray[id] && MuchTheSame(obj.TouchArray[id].x, X) && MuchTheSame(obj.TouchArray[id].y, Y)) return;
716 flags = 0x00020000 | 0x00000002 | 0x00000004; // POINTER_FLAG_UPDATE
717 }
718 else if (evt.type == 'MSPointerUp') flags = 0x00040000; // POINTER_FLAG_UP
719
720 if (!obj.TouchArray[id]) obj.TouchArray[id] = { x: X, y : Y };
721 obj.SendTouchMsg2(id, flags)
722 if (evt.type == 'MSPointerUp') delete obj.TouchArray[id];
723 } else {
724 alert(evt.type);
725 }
726 return true;
727 }
728
729 obj.xxTouchStart = function (e) {
730 if (obj.State != 3) return;
731 if (e.preventDefault) e.preventDefault();
732 if (obj.touchenabled == 0 || obj.touchenabled == 1) {
733 if (e.originalEvent.touches.length > 1) return;
734 var t = e.originalEvent.touches[0];
735 e.which = 1;
736 obj.LastX = e.pageX = t.pageX;
737 obj.LastY = e.pageY = t.pageY;
738 obj.SendMouseMsg(KeyAction.DOWN, e);
739 } else {
740 var Offsets = obj.GetPositionOfControl(Canvas.canvas);
741 for (var i in e.originalEvent.changedTouches) {
742 if (!e.originalEvent.changedTouches[i].identifier) continue;
743 var id = e.originalEvent.changedTouches[i].identifier % 256;
744 if (!obj.TouchArray[id]) { obj.TouchArray[id] = { x: (e.originalEvent.touches[i].pageX - Offsets[0]) * (Canvas.canvas.width / obj.CanvasId.clientWidth), y: (e.originalEvent.touches[i].pageY - Offsets[1]) * (Canvas.canvas.height / obj.CanvasId.clientHeight), f: 1 }; }
745 }
746 if (Object.keys(obj.TouchArray).length > 0 && touchtimer == null) { obj.touchtimer = setInterval(function () { obj.SendTouchMsg2(256, 0); }, 50); }
747 }
748 }
749
750 obj.xxTouchMove = function (e) {
751 if (obj.State != 3) return;
752 if (e.preventDefault) e.preventDefault();
753 if (obj.touchenabled == 0 || obj.touchenabled == 1) {
754 if (e.originalEvent.touches.length > 1) return;
755 var t = e.originalEvent.touches[0];
756 e.which = 1;
757 obj.LastX = e.pageX = t.pageX;
758 obj.LastY = e.pageY = t.pageY;
759 obj.SendMouseMsg(obj.KeyAction.NONE, e);
760 } else {
761 var Offsets = obj.GetPositionOfControl(Canvas.canvas);
762 for (var i in e.originalEvent.changedTouches) {
763 if (!e.originalEvent.changedTouches[i].identifier) continue;
764 var id = e.originalEvent.changedTouches[i].identifier % 256;
765 if (obj.TouchArray[id]) {
766 obj.TouchArray[id].x = (e.originalEvent.touches[i].pageX - Offsets[0]) * (obj.Canvas.canvas.width / obj.CanvasId.clientWidth);
767 obj.TouchArray[id].y = (e.originalEvent.touches[i].pageY - Offsets[1]) * (obj.Canvas.canvas.height / obj.CanvasId.clientHeight);
768 }
769 }
770 }
771 }
772
773 obj.xxTouchEnd = function (e) {
774 if (obj.State != 3) return;
775 if (e.preventDefault) e.preventDefault();
776 if (obj.touchenabled == 0 || obj.touchenabled == 1) {
777 if (e.originalEvent.touches.length > 1) return;
778 e.which = 1;
779 e.pageX = LastX;
780 e.pageY = LastY;
781 obj.SendMouseMsg(KeyAction.UP, e);
782 } else {
783 for (var i in e.originalEvent.changedTouches) {
784 if (!e.originalEvent.changedTouches[i].identifier) continue;
785 var id = e.originalEvent.changedTouches[i].identifier % 256;
786 if (obj.TouchArray[id]) obj.TouchArray[id].f = 2;
787 }
788 }
789 }
790
791 obj.GrabMouseInput = function () {
792 if (obj.xxMouseInputGrab == true) return;
793 var c = obj.CanvasId;
794 c.onmousemove = obj.xxMouseMove;
795 c.onmouseup = obj.xxMouseUp;
796 c.onmousedown = obj.xxMouseDown;
797 c.touchstart = obj.xxTouchStart;
798 c.touchmove = obj.xxTouchMove;
799 c.touchend = obj.xxTouchEnd;
800 c.MSPointerDown = obj.xxMsTouchEvent;
801 c.MSPointerMove = obj.xxMsTouchEvent;
802 c.MSPointerUp = obj.xxMsTouchEvent;
803 if (navigator.userAgent.match(/mozilla/i)) c.DOMMouseScroll = obj.xxDOMMouseScroll; else c.onmousewheel = obj.xxMouseWheel;
804 obj.xxMouseInputGrab = true;
805 }
806
807 obj.UnGrabMouseInput = function () {
808 if (obj.xxMouseInputGrab == false) return;
809 var c = obj.CanvasId;
810 c.onmousemove = null;
811 c.onmouseup = null;
812 c.onmousedown = null;
813 c.touchstart = null;
814 c.touchmove = null;
815 c.touchend = null;
816 c.MSPointerDown = null;
817 c.MSPointerMove = null;
818 c.MSPointerUp = null;
819 if (navigator.userAgent.match(/mozilla/i)) c.DOMMouseScroll = null; else c.onmousewheel = null;
820 obj.xxMouseInputGrab = false;
821 }
822
823 obj.GrabKeyInput = function () {
824 if (obj.xxKeyInputGrab == true) return;
825 document.onkeyup = obj.xxKeyUp;
826 document.onkeydown = obj.xxKeyDown;
827 document.onkeypress = obj.xxKeyPress;
828 obj.xxKeyInputGrab = true;
829 }
830
831 obj.UnGrabKeyInput = function () {
832 if (obj.xxKeyInputGrab == false) return;
833 document.onkeyup = null;
834 document.onkeydown = null;
835 document.onkeypress = null;
836 obj.xxKeyInputGrab = false;
837 }
838
839 obj.GetPositionOfControl = function (Control) {
840 var Position = Array(2);
841 Position[0] = Position[1] = 0;
842 while (Control) { Position[0] += Control.offsetLeft; Position[1] += Control.offsetTop; Control = Control.offsetParent; }
843 return Position;
844 }
845
846 obj.crotX = function (x, y) {
847 if (obj.rotation == 0) return x;
848 if (obj.rotation == 1) return y;
849 if (obj.rotation == 2) return obj.Canvas.canvas.width - x;
850 if (obj.rotation == 3) return obj.Canvas.canvas.height - y;
851 }
852
853 obj.crotY = function (x, y) {
854 if (obj.rotation == 0) return y;
855 if (obj.rotation == 1) return obj.Canvas.canvas.width - x;
856 if (obj.rotation == 2) return obj.Canvas.canvas.height - y;
857 if (obj.rotation == 3) return x;
858 }
859
860 obj.rotX = function (x, y) {
861 if (obj.rotation == 0 || obj.rotation == 1) return x;
862 if (obj.rotation == 2) return x - obj.Canvas.canvas.width;
863 if (obj.rotation == 3) return x - obj.Canvas.canvas.height;
864 }
865
866 obj.rotY = function (x, y) {
867 if (obj.rotation == 0 || obj.rotation == 3) return y;
868 if (obj.rotation == 1) return y - obj.Canvas.canvas.width;
869 if (obj.rotation == 2) return y - obj.Canvas.canvas.height;
870 }
871
872 obj.tcanvas = null;
873 obj.setRotation = function (x) {
874 while (x < 0) { x += 4; }
875 var newrotation = x % 4;
876 if (newrotation == obj.rotation) return true;
877 var rw = obj.Canvas.canvas.width;
878 var rh = obj.Canvas.canvas.height;
879 if (obj.rotation == 1 || obj.rotation == 3) { rw = obj.Canvas.canvas.height; rh = obj.Canvas.canvas.width; }
880
881 // Copy the canvas, put it back in the correct direction
882 if (obj.tcanvas == null) obj.tcanvas = document.createElement('canvas');
883 var tcanvasctx = obj.tcanvas.getContext('2d');
884 tcanvasctx.setTransform(1, 0, 0, 1, 0, 0);
885 tcanvasctx.canvas.width = rw;
886 tcanvasctx.canvas.height = rh;
887 tcanvasctx.rotate((obj.rotation * -90) * Math.PI / 180);
888 if (obj.rotation == 0) tcanvasctx.drawImage(obj.Canvas.canvas, 0, 0);
889 if (obj.rotation == 1) tcanvasctx.drawImage(obj.Canvas.canvas, -obj.Canvas.canvas.width, 0);
890 if (obj.rotation == 2) tcanvasctx.drawImage(obj.Canvas.canvas, -obj.Canvas.canvas.width, -obj.Canvas.canvas.height);
891 if (obj.rotation == 3) tcanvasctx.drawImage(obj.Canvas.canvas, 0, -obj.Canvas.canvas.height);
892
893 // Change the size and orientation and copy the canvas back into the rotation
894 if (obj.rotation == 0 || obj.rotation == 2) { obj.Canvas.canvas.height = rw; obj.Canvas.canvas.width = rh; }
895 if (obj.rotation == 1 || obj.rotation == 3) { obj.Canvas.canvas.height = rh; obj.Canvas.canvas.width = rw; }
896 obj.Canvas.setTransform(1, 0, 0, 1, 0, 0);
897 obj.Canvas.rotate((newrotation * 90) * Math.PI / 180);
898 obj.rotation = newrotation;
899 obj.Canvas.drawImage(obj.tcanvas, obj.rotX(0, 0), obj.rotY(0, 0));
900
901 obj.ScreenWidth = obj.Canvas.canvas.width;
902 obj.ScreenHeight = obj.Canvas.canvas.height;
903 if (obj.onScreenSizeChange != null) { console.log('s4', obj.ScreenWidth, obj.ScreenHeight); obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId); }
904 return true;
905 }
906
907 obj.StartRecording = function () {
908 if (obj.recordedData != null) return;
909 // Take a screen shot and save it to file
910 obj.CanvasId['toBlob'](function (blob) {
911 var fileReader = new FileReader();
912 fileReader.readAsArrayBuffer(blob);
913 fileReader.onload = function (event) {
914 // This is an ArrayBuffer, convert it to a string array
915 var binary = '', bytes = new Uint8Array(fileReader.result), length = bytes.byteLength;
916 for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
917 obj.recordedData = [];
918 obj.recordedStart = Date.now();
919 obj.recordedSize = 0;
920 obj.recordedData.push(recordingEntry(1, 0, JSON.stringify({ magic: 'MeshCentralRelaySession', ver: 1, time: new Date().toLocaleString(), protocol: 2 }))); // Metadata (nodeid: obj.nodeid)
921 obj.recordedData.push(recordingEntry(2, 1, obj.shortToStr(7) + obj.shortToStr(8) + obj.shortToStr(obj.ScreenWidth) + obj.shortToStr(obj.ScreenHeight))); // Screen width and height
922 // Save a screenshot
923 var cmdlen = (8 + binary.length);
924 if (cmdlen > 65000) {
925 // Jumbo Packet
926 obj.recordedData.push(recordingEntry(2, 1, obj.shortToStr(27) + obj.shortToStr(8) + obj.intToStr(cmdlen) + obj.shortToStr(3) + obj.shortToStr(0) + obj.shortToStr(0) + obj.shortToStr(0) + binary));
927 } else {
928 // Normal packet
929 obj.recordedData.push(recordingEntry(2, 1, obj.shortToStr(3) + obj.shortToStr(cmdlen) + obj.shortToStr(0) + obj.shortToStr(0) + binary));
930 }
931 };
932 });
933 }
934
935 obj.StopRecording = function () {
936 if (obj.recordedData == null) return;
937 var r = obj.recordedData;
938 r.push(recordingEntry(3, 0, 'MeshCentralMCREC'));
939 delete obj.recordedData;
940 delete obj.recordedStart;
941 delete obj.recordedSize;
942 return r;
943 }
944
945 function recordingEntry(type, flags, data) {
946 // Header: Type (2) + Flags (2) + Size(4) + Time(8)
947 // Type (1 = Header, 2 = Network Data), Flags (1 = Binary, 2 = User), Size (4 bytes), Time (8 bytes)
948 var now = Date.now();
949 if (typeof data == 'number') {
950 obj.recordedSize += data;
951 return obj.shortToStr(type) + obj.shortToStr(flags) + obj.intToStr(data) + obj.intToStr(now >> 32) + obj.intToStr(now & 32);
952 } else {
953 obj.recordedSize += data.length;
954 return obj.shortToStr(type) + obj.shortToStr(flags) + obj.intToStr(data.length) + obj.intToStr(now >> 32) + obj.intToStr(now & 32) + data;
955 }
956 }
957
958 // Private method
959 obj.MuchTheSame = function (a, b) { return (Math.abs(a - b) < 4); }
960 obj.Debug = function (msg) { console.log(msg); }
961 obj.getIEVersion = function () { var r = -1; if (navigator.appName == 'Microsoft Internet Explorer') { var ua = navigator.userAgent; var re = new RegExp("MSIE ([0-9]{1,}[.0-9]{0,})"); if (re.exec(ua) != null) r = parseFloat(RegExp.$1); } return r; }
962 obj.haltEvent = function (e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
963
964 return obj;
965}