diff --git a/guacamole-common-js/src/main/resources/guacamole.js b/guacamole-common-js/src/main/resources/guacamole.js
index 8aaef9612..a4ce995c7 100644
--- a/guacamole-common-js/src/main/resources/guacamole.js
+++ b/guacamole-common-js/src/main/resources/guacamole.js
@@ -14,14 +14,9 @@
* 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 .
*/
-function GuacamoleClient(display, tunnelURL) {
-
- var TUNNEL_CONNECT = tunnelURL + "?connect";
- var TUNNEL_READ = tunnelURL + "?read";
- var TUNNEL_WRITE = tunnelURL + "?write";
+function GuacamoleClient(display, tunnel) {
var STATE_IDLE = 0;
var STATE_CONNECTING = 1;
@@ -32,7 +27,8 @@ function GuacamoleClient(display, tunnelURL) {
var currentState = STATE_IDLE;
var stateChangeHandler = null;
- var pollResponse = 1; // Default to polling - will be turned off automatically if not needed
+
+ tunnel.setInstructionHandler(doInstruction);
// Display must be relatively positioned for mouse to be handled properly
display.style.position = "relative";
@@ -66,7 +62,7 @@ function GuacamoleClient(display, tunnelURL) {
var cursorHidden = 0;
- function redrawCursor() {
+ function redrawCursor(x, y) {
// Hide hardware cursor
if (cursorHidden == 0) {
@@ -78,8 +74,8 @@ function GuacamoleClient(display, tunnelURL) {
cursor.clearRect(cursorRectX, cursorRectY, cursorRectW, cursorRectH);
// Update rect
- cursorRectX = mouse.getX() - cursorHotspotX;
- cursorRectY = mouse.getY() - cursorHotspotY;
+ cursorRectX = x - cursorHotspotX;
+ cursorRectY = y - cursorHotspotY;
cursorRectW = cursorImage.width;
cursorRectH = cursorImage.height;
@@ -87,90 +83,28 @@ function GuacamoleClient(display, tunnelURL) {
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) {
+ this.sendKeyEvent = function(pressed, keysym) {
// Do not send requests if not connected
if (!isConnected())
return;
- sendMessage("key:" + keysym + "," + pressed + ";");
+ tunnel.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) {
+ this.sendMouseState = function(mouseState) {
// Do not send requests if not connected
if (!isConnected())
return;
+ // Draw client-side cursor
+ if (cursorImage != null) {
+ redrawCursor(
+ mouseState.getX(),
+ mouseState.getY()
+ );
+ }
+
// Build mask
var buttonMask = 0;
if (mouseState.getLeft()) buttonMask |= 1;
@@ -180,80 +114,19 @@ function GuacamoleClient(display, tunnelURL) {
if (mouseState.getDown()) buttonMask |= 16;
// Send message
- sendMessage("mouse:" + mouseState.getX() + "," + mouseState.getY() + "," + buttonMask + ";");
+ tunnel.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", TUNNEL_WRITE);
- 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) + ";");
+ tunnel.sendMessage("clipboard:" + tunnel.escapeGuacamoleString(data) + ";");
}
-
- function desaturateFilter(data, width, height) {
-
- for (var i=0; i= 2 && nextRequest == null)
- nextRequest = makeRequest();
-
- // Parse stream when data is received and when complete.
- if (xmlhttprequest.readyState == 3 ||
- xmlhttprequest.readyState == 4) {
-
- // Also poll every 30ms (some browsers don't repeatedly call onreadystatechange for new data)
- if (pollResponse == 1) {
- if (xmlhttprequest.readyState == 3 && interval == null)
- interval = setInterval(parseResponse, 30);
- else if (xmlhttprequest.readyState == 4 && interval != null)
- clearInterval(interval);
- }
-
- // 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;
-
- }
-
- }
-
- // If response polling enabled, attempt to detect if still
- // necessary (via wrapping parseResponse())
- if (pollResponse == 1) {
- xmlhttprequest.onreadystatechange = function() {
-
- // If we receive two or more readyState==3 events,
- // there is no need to poll.
- if (xmlhttprequest.readyState == 3) {
- dataUpdateEvents++;
- if (dataUpdateEvents >= 2) {
- pollResponse = 0;
- xmlhttprequest.onreadystatechange = parseResponse;
- }
- }
-
- parseResponse();
- }
- }
-
- // Otherwise, just parse
- else
- xmlhttprequest.onreadystatechange = parseResponse;
-
- parseResponse();
-
- }
-
-
- function makeRequest() {
-
- // Download self
- var xmlhttprequest = new XMLHttpRequest();
- xmlhttprequest.open("POST", TUNNEL_READ);
- xmlhttprequest.send(null);
-
- return xmlhttprequest;
-
- }
-
- function escapeGuacamoleString(str) {
-
- var escapedString = "";
-
- for (var i=0; i 0) {
+
+ sendingMessages = 1;
+
+ var message_xmlhttprequest = new XMLHttpRequest();
+ message_xmlhttprequest.open("POST", TUNNEL_WRITE);
+ 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;
+
+ }
+
+
+ function handleResponse(xmlhttprequest) {
+
+ var interval = null;
+ var nextRequest = null;
+
+ var dataUpdateEvents = 0;
+ 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) {
+
+ // Also poll every 30ms (some browsers don't repeatedly call onreadystatechange for new data)
+ if (pollResponse == 1) {
+ if (xmlhttprequest.readyState == 3 && interval == null)
+ interval = setInterval(parseResponse, 30);
+ else if (xmlhttprequest.readyState == 4 && interval != null)
+ clearInterval(interval);
+ }
+
+ // 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) {
+
+ delete xmlhttprequest;
+ if (nextRequest)
+ handleResponse(nextRequest);
+
+ break;
+ }
+
+ // Call instruction handler.
+ if (instructionHandler != null)
+ instructionHandler(opcode, parameters);
+ }
+
+ // Start search at end of string.
+ startIndex = current.length;
+
+ delete instruction;
+ delete parameters;
+
+ }
+
+ }
+
+ // If response polling enabled, attempt to detect if still
+ // necessary (via wrapping parseResponse())
+ if (pollResponse == 1) {
+ xmlhttprequest.onreadystatechange = function() {
+
+ // If we receive two or more readyState==3 events,
+ // there is no need to poll.
+ if (xmlhttprequest.readyState == 3) {
+ dataUpdateEvents++;
+ if (dataUpdateEvents >= 2) {
+ pollResponse = 0;
+ xmlhttprequest.onreadystatechange = parseResponse;
+ }
+ }
+
+ parseResponse();
+ }
+ }
+
+ // Otherwise, just parse
+ else
+ xmlhttprequest.onreadystatechange = parseResponse;
+
+ parseResponse();
+
+ }
+
+
+ function makeRequest() {
+
+ // Download self
+ var xmlhttprequest = new XMLHttpRequest();
+ xmlhttprequest.open("POST", TUNNEL_READ);
+ xmlhttprequest.send(null);
+
+ return xmlhttprequest;
+
+ }
+
+ function connect() {
+
+ // Start tunnel and connect synchronously
+ var connect_xmlhttprequest = new XMLHttpRequest();
+ connect_xmlhttprequest.open("POST", TUNNEL_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
+ handleResponse(makeRequest());
+
+ };
+
+ // External API
+ this.connect = connect;
+ this.sendMessage = sendMessage;
+ this.setInstructionHandler = function(handler) {
+ instructionHandler = handler;
+ }
+
+ this.escapeGuacamoleString = function(str) {
+
+ var escapedString = "";
+
+ for (var i=0; i