mirror of
				https://github.com/gyurix1968/guacamole-client.git
				synced 2025-10-31 17:13:21 +00:00 
			
		
		
		
	Moved guacamole-specific parts into own lib dir
This commit is contained in:
		
							
								
								
									
										601
									
								
								guacamole/web-client/web/guac-web-lib/javascript/guacamole.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										601
									
								
								guacamole/web-client/web/guac-web-lib/javascript/guacamole.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,601 @@ | ||||
|  | ||||
| /* | ||||
|  *  Guacamole - Clientless Remote Desktop | ||||
|  *  Copyright (C) 2010  Michael Jumper | ||||
|  * | ||||
|  *  This program is free software: you can redistribute it and/or modify | ||||
|  *  it under the terms of the GNU Affero General Public License as published by | ||||
|  *  the Free Software Foundation, either version 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  This program is distributed in the hope that it will be useful, | ||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  *  GNU Affero General Public License for more details. | ||||
|  * | ||||
|  *  You should have received a copy of the GNU Affero General Public License | ||||
|  *  along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| function GuacamoleClient(display) { | ||||
|  | ||||
|     var STATE_IDLE          = 0; | ||||
|     var STATE_CONNECTING    = 1; | ||||
|     var STATE_WAITING       = 2; | ||||
|     var STATE_CONNECTED     = 3; | ||||
|     var STATE_DISCONNECTING = 4; | ||||
|     var STATE_DISCONNECTED  = 5; | ||||
|  | ||||
|     var currentState = STATE_IDLE; | ||||
|     var stateChangeHandler = null; | ||||
|  | ||||
|     function setState(state) { | ||||
|         if (state != currentState) { | ||||
|             currentState = state; | ||||
|             if (stateChangeHandler) | ||||
|                 stateChangeHandler(currentState); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     this.setOnStateChangeHandler = function(handler) { | ||||
|         stateChangeHandler = handler; | ||||
|     } | ||||
|  | ||||
|     function isConnected() { | ||||
|         return currentState == STATE_CONNECTED | ||||
|             || currentState == STATE_WAITING; | ||||
|     } | ||||
|  | ||||
|     // Layers | ||||
|     var background = null; | ||||
|     var cursor = null; | ||||
|  | ||||
|     var cursorImage = null; | ||||
|     var cursorHotspotX = 0; | ||||
|     var cursorHotspotY = 0; | ||||
|  | ||||
|     // FIXME: Make object. Clean up. | ||||
|     var cursorRectX = 0; | ||||
|     var cursorRectY = 0; | ||||
|     var cursorRectW = 0; | ||||
|     var cursorRectH = 0; | ||||
|  | ||||
|     var cursorHidden = 0; | ||||
|  | ||||
|     function redrawCursor() { | ||||
|  | ||||
|         // Hide hardware cursor | ||||
|         if (cursorHidden == 0) { | ||||
|             display.className += " guac-hide-cursor"; | ||||
|             cursorHidden = 1; | ||||
|         } | ||||
|  | ||||
|         // Erase old cursor | ||||
|         cursor.clearRect(cursorRectX, cursorRectY, cursorRectW, cursorRectH); | ||||
|  | ||||
|         // Update rect | ||||
|         cursorRectX = mouse.getX() - cursorHotspotX; | ||||
|         cursorRectY = mouse.getY() - cursorHotspotY; | ||||
|         cursorRectW = cursorImage.width; | ||||
|         cursorRectH = cursorImage.height; | ||||
|  | ||||
|         // Draw new cursor | ||||
|         cursor.drawImage(cursorRectX, cursorRectY, cursorImage); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 	/*****************************************/ | ||||
| 	/*** Keyboard                          ***/ | ||||
| 	/*****************************************/ | ||||
|  | ||||
|     var keyboard = new GuacamoleKeyboard(document); | ||||
|  | ||||
|     this.disableKeyboard = function() { | ||||
|         keyboard.setKeyPressedHandler(null); | ||||
|         keyboard.setKeyReleasedHandler(null); | ||||
|     }; | ||||
|  | ||||
|     this.enableKeyboard = function() { | ||||
|         keyboard.setKeyPressedHandler( | ||||
|             function (keysym) { | ||||
|                 sendKeyEvent(1, keysym); | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         keyboard.setKeyReleasedHandler( | ||||
|             function (keysym) { | ||||
|                 sendKeyEvent(0, keysym); | ||||
|             } | ||||
|         ); | ||||
|     }; | ||||
|  | ||||
|     // Enable keyboard by default | ||||
|     this.enableKeyboard(); | ||||
|  | ||||
|     function sendKeyEvent(pressed, keysym) { | ||||
|         // Do not send requests if not connected | ||||
|         if (!isConnected()) | ||||
|             return; | ||||
|  | ||||
|         sendMessage("key:" +  keysym + "," + pressed + ";"); | ||||
|     } | ||||
|  | ||||
|     this.pressKey = function(keysym) { | ||||
|         sendKeyEvent(1, keysym); | ||||
|     }; | ||||
|  | ||||
|     this.releaseKey = function(keysym) { | ||||
|         sendKeyEvent(0, keysym); | ||||
|     }; | ||||
|  | ||||
|  | ||||
| 	/*****************************************/ | ||||
| 	/*** Mouse                             ***/ | ||||
| 	/*****************************************/ | ||||
|  | ||||
|     var mouse = new GuacamoleMouse(display); | ||||
|     mouse.setButtonPressedHandler( | ||||
|         function(mouseState) { | ||||
|             sendMouseState(mouseState); | ||||
|         } | ||||
|     ); | ||||
|  | ||||
|     mouse.setButtonReleasedHandler( | ||||
|         function(mouseState) { | ||||
|             sendMouseState(mouseState); | ||||
|         } | ||||
|     ); | ||||
|  | ||||
|     mouse.setMovementHandler( | ||||
|         function(mouseState) { | ||||
|  | ||||
|             // Draw client-side cursor | ||||
|             if (cursorImage != null) { | ||||
|                 redrawCursor(); | ||||
|             } | ||||
|  | ||||
|             sendMouseState(mouseState); | ||||
|         } | ||||
|     ); | ||||
|  | ||||
|  | ||||
|     function sendMouseState(mouseState) { | ||||
|  | ||||
|         // Do not send requests if not connected | ||||
|         if (!isConnected()) | ||||
|             return; | ||||
|  | ||||
|         // Build mask | ||||
|         var buttonMask = 0; | ||||
|         if (mouseState.getLeft())   buttonMask |= 1; | ||||
|         if (mouseState.getMiddle()) buttonMask |= 2; | ||||
|         if (mouseState.getRight())  buttonMask |= 4; | ||||
|         if (mouseState.getUp())     buttonMask |= 8; | ||||
|         if (mouseState.getDown())   buttonMask |= 16; | ||||
|  | ||||
|         // Send message | ||||
|         sendMessage("mouse:" + mouseState.getX() + "," + mouseState.getY() + "," + buttonMask + ";"); | ||||
|     } | ||||
|  | ||||
|     var sendingMessages = 0; | ||||
|     var outputMessageBuffer = ""; | ||||
|  | ||||
|     function sendMessage(message) { | ||||
|  | ||||
|         // Add event to queue, restart send loop if finished. | ||||
|         outputMessageBuffer += message; | ||||
|         if (sendingMessages == 0) | ||||
|             sendPendingMessages(); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     function sendPendingMessages() { | ||||
|  | ||||
|         if (outputMessageBuffer.length > 0) { | ||||
|  | ||||
|             sendingMessages = 1; | ||||
|  | ||||
|             var message_xmlhttprequest = new XMLHttpRequest(); | ||||
|             message_xmlhttprequest.open("POST", "inbound"); | ||||
|             message_xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); | ||||
|             message_xmlhttprequest.setRequestHeader("Content-length", outputMessageBuffer.length); | ||||
|  | ||||
|             // Once response received, send next queued event. | ||||
|             message_xmlhttprequest.onreadystatechange = function() { | ||||
|                 if (message_xmlhttprequest.readyState == 4) | ||||
|                     sendPendingMessages(); | ||||
|             } | ||||
|  | ||||
|             message_xmlhttprequest.send(outputMessageBuffer); | ||||
|             outputMessageBuffer = ""; // Clear buffer | ||||
|  | ||||
|         } | ||||
|         else | ||||
|             sendingMessages = 0; | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
| 	/*****************************************/ | ||||
| 	/*** Clipboard                         ***/ | ||||
| 	/*****************************************/ | ||||
|  | ||||
|     this.setClipboard = function(data) { | ||||
|  | ||||
|         // Do not send requests if not connected | ||||
|         if (!isConnected()) | ||||
|             return; | ||||
|  | ||||
|         sendMessage("clipboard:" + escapeGuacamoleString(data) + ";"); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     function desaturateFilter(data, width, height) { | ||||
|  | ||||
|         for (var i=0; i<data.length; i+=4) { | ||||
|  | ||||
|             // Get RGB values | ||||
|             var r = data[i]; | ||||
|             var g = data[i+1]; | ||||
|             var b = data[i+2]; | ||||
|  | ||||
|             // Desaturate | ||||
|             var v = Math.max(r, g, b) / 2; | ||||
|             data[i]   = v; | ||||
|             data[i+1] = v; | ||||
|             data[i+2] = v; | ||||
|  | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|     var errorHandler = null; | ||||
|     this.setErrorHandler = function(handler) { | ||||
|         errorHandler = handler; | ||||
|     }; | ||||
|  | ||||
|     var errorEncountered = 0; | ||||
|     function showError(error) { | ||||
|         // Only display first error (avoid infinite error loops) | ||||
|         if (errorEncountered == 0) { | ||||
|             errorEncountered = 1; | ||||
|  | ||||
|             disconnect(); | ||||
|  | ||||
|             // In case nothing has been rendered yet, use error style | ||||
|             display.className += " guac-error"; | ||||
|  | ||||
|             // Show error by desaturating display | ||||
|             if (background) | ||||
|                 background.filter(desaturateFilter); | ||||
|  | ||||
|             if (errorHandler) | ||||
|                 errorHandler(error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function handleErrors(message) { | ||||
|         var errors = message.getErrors(); | ||||
|         for (var errorIndex=0; errorIndex<errors.length; errorIndex++) | ||||
|             showError(errors[errorIndex].getMessage()); | ||||
|     } | ||||
|  | ||||
|     var clipboardHandler = null; | ||||
|     var requests = 0; | ||||
|  | ||||
|     this.setClipboardHandler = function(handler) { | ||||
|         clipboardHandler = handler; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     function handleResponse(xmlhttprequest) { | ||||
|  | ||||
|         var nextRequest = null; | ||||
|  | ||||
|         var instructionStart = 0; | ||||
|         var startIndex = 0; | ||||
|  | ||||
|         function parseResponse() { | ||||
|  | ||||
|             // Start next request as soon as possible | ||||
|             if (xmlhttprequest.readyState >= 2 && nextRequest == null) | ||||
|                 nextRequest = makeRequest(); | ||||
|  | ||||
|             // Parse stream when data is received and when complete. | ||||
|             if (xmlhttprequest.readyState == 3 || | ||||
|                 xmlhttprequest.readyState == 4) { | ||||
|  | ||||
|                 // Halt on error during request | ||||
|                 if (xmlhttprequest.status == 0) { | ||||
|                     showError("Request canceled by browser."); | ||||
|                     return; | ||||
|                 } | ||||
|                 else if (xmlhttprequest.status != 200) { | ||||
|                     showError("Error during request (HTTP " + xmlhttprequest.status + "): " + xmlhttprequest.statusText); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 var current = xmlhttprequest.responseText; | ||||
|                 var instructionEnd; | ||||
|                  | ||||
|                 while ((instructionEnd = current.indexOf(";", startIndex)) != -1) { | ||||
|  | ||||
|                     // Start next search at next instruction | ||||
|                     startIndex = instructionEnd+1; | ||||
|  | ||||
|                     var instruction = current.substr(instructionStart, | ||||
|                             instructionEnd - instructionStart); | ||||
|  | ||||
|                     instructionStart = startIndex; | ||||
|  | ||||
|                     var opcodeEnd = instruction.indexOf(":"); | ||||
|  | ||||
|                     var opcode; | ||||
|                     var parameters; | ||||
|                     if (opcodeEnd == -1) { | ||||
|                         opcode = instruction; | ||||
|                         parameters = new Array(); | ||||
|                     } | ||||
|                     else { | ||||
|                         opcode = instruction.substr(0, opcodeEnd); | ||||
|                         parameters = instruction.substr(opcodeEnd+1).split(","); | ||||
|                     } | ||||
|  | ||||
|                     // If we're done parsing, handle the next response. | ||||
|                     if (opcode.length == 0) { | ||||
|  | ||||
|                         if (isConnected()) { | ||||
|                             delete xmlhttprequest; | ||||
|                             if (nextRequest) | ||||
|                                 handleResponse(nextRequest); | ||||
|                         } | ||||
|  | ||||
|                         break; | ||||
|                     } | ||||
|  | ||||
|                     // Call instruction handler. | ||||
|                     doInstruction(opcode, parameters); | ||||
|                 } | ||||
|  | ||||
|                 // Start search at end of string. | ||||
|                 startIndex = current.length; | ||||
|  | ||||
|                 delete instruction; | ||||
|                 delete parameters; | ||||
|  | ||||
|             } | ||||
|  | ||||
|         } | ||||
|  | ||||
|         xmlhttprequest.onreadystatechange = parseResponse; | ||||
|         parseResponse(); | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|     function makeRequest() { | ||||
|  | ||||
|         // Download self | ||||
|         var xmlhttprequest = new XMLHttpRequest(); | ||||
|         xmlhttprequest.open("POST", "outbound"); | ||||
|         xmlhttprequest.send(null);  | ||||
|  | ||||
|         return xmlhttprequest; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     function escapeGuacamoleString(str) { | ||||
|  | ||||
|         var escapedString = ""; | ||||
|  | ||||
|         for (var i=0; i<str.length; i++) { | ||||
|  | ||||
|             var c = str.charAt(i); | ||||
|             if (c == ",") | ||||
|                 escapedString += "\\c"; | ||||
|             else if (c == ";") | ||||
|                 escapedString += "\\s"; | ||||
|             else if (c == "\\") | ||||
|                 escapedString += "\\\\"; | ||||
|             else | ||||
|                 escapedString += c; | ||||
|  | ||||
|         } | ||||
|  | ||||
|         return escapedString; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     function unescapeGuacamoleString(str) { | ||||
|  | ||||
|         var unescapedString = ""; | ||||
|  | ||||
|         for (var i=0; i<str.length; i++) { | ||||
|  | ||||
|             var c = str.charAt(i); | ||||
|             if (c == "\\" && i<str.length-1) { | ||||
|  | ||||
|                 var escapeChar = str.charAt(++i); | ||||
|                 if (escapeChar == "c") | ||||
|                     unescapedString += ","; | ||||
|                 else if (escapeChar == "s") | ||||
|                     unescapedString += ";"; | ||||
|                 else if (escapeChar == "\\") | ||||
|                     unescapedString += "\\"; | ||||
|                 else | ||||
|                     unescapedString += "\\" + escapeChar; | ||||
|  | ||||
|             } | ||||
|             else | ||||
|                 unescapedString += c; | ||||
|  | ||||
|         } | ||||
|  | ||||
|         return unescapedString; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     var instructionHandlers = { | ||||
|  | ||||
|         "error": function(parameters) { | ||||
|             showError(unescapeGuacamoleString(parameters[0])); | ||||
|         }, | ||||
|  | ||||
|         "name": function(parameters) { | ||||
|             document.title = unescapeGuacamoleString(parameters[0]); | ||||
|         }, | ||||
|  | ||||
|         "clipboard": function(parameters) { | ||||
|             clipboardHandler(unescapeGuacamoleString(parameters[0])); | ||||
|         }, | ||||
|  | ||||
|         "size": function(parameters) { | ||||
|  | ||||
|             var width = parseInt(parameters[0]); | ||||
|             var height = parseInt(parameters[1]); | ||||
|  | ||||
|             // Update (set) display size | ||||
|             if (display && (background == null || cursor == null)) { | ||||
|                 display.style.width = width + "px"; | ||||
|                 display.style.height = height + "px"; | ||||
|  | ||||
|                 background = new Layer(width, height); | ||||
|                 cursor = new Layer(width, height); | ||||
|  | ||||
|                 display.appendChild(background); | ||||
|                 display.appendChild(cursor); | ||||
|             } | ||||
|  | ||||
|         }, | ||||
|  | ||||
|         "rect": function(parameters) { | ||||
|  | ||||
|             var x = parseInt(parameters[0]); | ||||
|             var y = parseInt(parameters[1]); | ||||
|             var w = parseInt(parameters[2]); | ||||
|             var h = parseInt(parameters[3]); | ||||
|             var color = parameters[4]; | ||||
|  | ||||
|             background.drawRect( | ||||
|                 x, | ||||
|                 y, | ||||
|                 w, | ||||
|                 h, | ||||
|                 color | ||||
|             ); | ||||
|  | ||||
|         }, | ||||
|  | ||||
|         "png": function(parameters) { | ||||
|  | ||||
|             var x = parseInt(parameters[0]); | ||||
|             var y = parseInt(parameters[1]); | ||||
|             var data = parameters[2]; | ||||
|  | ||||
|             background.draw( | ||||
|                 x, | ||||
|                 y, | ||||
|                 "data:image/png;base64," + data | ||||
|             ); | ||||
|  | ||||
|             // If received first update, no longer waiting. | ||||
|             if (currentState == STATE_WAITING) | ||||
|                 setState(STATE_CONNECTED); | ||||
|  | ||||
|         }, | ||||
|  | ||||
|         "copy": function(parameters) { | ||||
|  | ||||
|             var srcX = parseInt(parameters[0]); | ||||
|             var srcY = parseInt(parameters[1]); | ||||
|             var srcWidth = parseInt(parameters[2]); | ||||
|             var srcHeight = parseInt(parameters[3]); | ||||
|             var dstX = parseInt(parameters[4]); | ||||
|             var dstY = parseInt(parameters[5]); | ||||
|  | ||||
|             background.copyRect( | ||||
|                 srcX, | ||||
|                 srcY, | ||||
|                 srcWidth,  | ||||
|                 srcHeight,  | ||||
|                 dstX, | ||||
|                 dstY  | ||||
|             ); | ||||
|  | ||||
|         }, | ||||
|  | ||||
|         "cursor": function(parameters) { | ||||
|  | ||||
|             var x = parseInt(parameters[0]); | ||||
|             var y = parseInt(parameters[1]); | ||||
|             var data = parameters[2]; | ||||
|  | ||||
|             // Start cursor image load | ||||
|             var image = new Image(); | ||||
|             image.onload = function() { | ||||
|                 cursorImage = image; | ||||
|                 cursorHotspotX = x; | ||||
|                 cursorHotspotY = y; | ||||
|                 redrawCursor(); | ||||
|             }; | ||||
|             image.src = "data:image/png;base64," + data | ||||
|  | ||||
|         } | ||||
|        | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     function doInstruction(opcode, parameters) { | ||||
|  | ||||
|         var handler = instructionHandlers[opcode]; | ||||
|         if (handler) | ||||
|             handler(parameters); | ||||
|  | ||||
|     } | ||||
|          | ||||
|  | ||||
|     this.connect = function() { | ||||
|  | ||||
|         setState(STATE_CONNECTING); | ||||
|  | ||||
|         // Start tunnel and connect synchronously | ||||
|         var connect_xmlhttprequest = new XMLHttpRequest(); | ||||
|         connect_xmlhttprequest.open("POST", "connect", false); | ||||
|         connect_xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); | ||||
|         connect_xmlhttprequest.setRequestHeader("Content-length", 0); | ||||
|         connect_xmlhttprequest.send(null); | ||||
|  | ||||
|         // Start reading data | ||||
|         setState(STATE_WAITING); | ||||
|         handleResponse(makeRequest()); | ||||
|  | ||||
|     }; | ||||
|  | ||||
|      | ||||
|     function disconnect() { | ||||
|  | ||||
|         // Only attempt disconnection not disconnected. | ||||
|         if (currentState != STATE_DISCONNECTED | ||||
|                 && currentState != STATE_DISCONNECTING) { | ||||
|  | ||||
|             var message = "disconnect;"; | ||||
|             setState(STATE_DISCONNECTING); | ||||
|  | ||||
|             // Send disconnect message (synchronously... as necessary until handoff is implemented) | ||||
|             var disconnect_xmlhttprequest = new XMLHttpRequest(); | ||||
|             disconnect_xmlhttprequest.open("POST", "inbound", false); | ||||
|             disconnect_xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); | ||||
|             disconnect_xmlhttprequest.setRequestHeader("Content-length", message.length); | ||||
|             disconnect_xmlhttprequest.send(message); | ||||
|  | ||||
|             setState(STATE_DISCONNECTED); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     this.disconnect = disconnect; | ||||
|  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user