Migrating to separate Guacamole javascript library
authorMichael Jumper <zhangmaike@users.sourceforge.net>
Sun, 23 Jan 2011 05:53:57 +0000 (21:53 -0800)
committerMichael Jumper <zhangmaike@users.sourceforge.net>
Sun, 23 Jan 2011 05:53:57 +0000 (21:53 -0800)
23 files changed:
pom.xml
src/main/webapp/guac-web-lib/css/guacamole.css [deleted file]
src/main/webapp/guac-web-lib/images/mouse/blank.cur [deleted file]
src/main/webapp/guac-web-lib/images/mouse/blank.gif [deleted file]
src/main/webapp/guac-web-lib/images/mouse/dot.gif [deleted file]
src/main/webapp/guac-web-lib/images/noimage92.png [deleted file]
src/main/webapp/guac-web-lib/images/spinner92.gif [deleted file]
src/main/webapp/guac-web-lib/javascript/guacamole.js [deleted file]
src/main/webapp/guac-web-lib/javascript/keyboard.js [deleted file]
src/main/webapp/guac-web-lib/javascript/keymap.js [deleted file]
src/main/webapp/guac-web-lib/javascript/layer.js [deleted file]
src/main/webapp/guac-web-lib/javascript/mouse.js [deleted file]
src/main/webapp/guac-web-lib/javascript/oskeyboard.js [deleted file]
src/main/webapp/guacamole.css [deleted file]
src/main/webapp/images/mouse/blank.cur [new file with mode: 0644]
src/main/webapp/images/mouse/blank.gif [new file with mode: 0644]
src/main/webapp/images/mouse/dot.gif [new file with mode: 0644]
src/main/webapp/images/noimage92.png [new file with mode: 0644]
src/main/webapp/images/spinner92.gif [new file with mode: 0644]
src/main/webapp/index.html
src/main/webapp/keyboard.css [deleted file]
src/main/webapp/styles/guacamole.css [new file with mode: 0644]
src/main/webapp/styles/keyboard.css [new file with mode: 0644]

diff --git a/pom.xml b/pom.xml
index 9ba6553..832a318 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -11,6 +11,7 @@
 
     <build>
         <plugins>
+
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
                     <target>1.6</target>
                 </configuration>
             </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <configuration>
+                    <overlays>
+                        <overlay>
+                            <groupId>net.sourceforge.guacamole</groupId>
+                            <artifactId>guacamole-common-js</artifactId>
+                            <type>zip</type>
+                        </overlay>
+                    </overlays>
+                </configuration>
+            </plugin>
+
         </plugins>
 
         <extensions>
             <scope>compile</scope>
         </dependency>
 
+        <dependency>
+            <groupId>net.sourceforge.guacamole</groupId>
+            <artifactId>guacamole-common-js</artifactId>
+            <version>0.3.0-SNAPSHOT</version>
+            <type>zip</type>
+            <scope>runtime</scope>
+        </dependency>
+
     </dependencies>
 
     <repositories>
diff --git a/src/main/webapp/guac-web-lib/css/guacamole.css b/src/main/webapp/guac-web-lib/css/guacamole.css
deleted file mode 100644 (file)
index 471c2fb..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-
-/*
- *  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/>.
- */
-
-.guac-display.guac-loading {
-    border: 1px dotted gray;
-    background-image: url('../images/spinner92.gif');
-    background-position: center;
-    background-repeat: no-repeat;
-}
-
-.guac-display.guac-error {
-    border: 1px dotted red;
-    background-image: url('../images/noimage92.png');
-    background-position: center;
-    background-repeat: no-repeat;
-}
-
-.guac-hide-cursor {
-    cursor: url('../images/mouse/dot.gif'),url('../images/mouse/blank.cur'),default;
-}
-
diff --git a/src/main/webapp/guac-web-lib/images/mouse/blank.cur b/src/main/webapp/guac-web-lib/images/mouse/blank.cur
deleted file mode 100644 (file)
index dfcdea8..0000000
Binary files a/src/main/webapp/guac-web-lib/images/mouse/blank.cur and /dev/null differ
diff --git a/src/main/webapp/guac-web-lib/images/mouse/blank.gif b/src/main/webapp/guac-web-lib/images/mouse/blank.gif
deleted file mode 100644 (file)
index ec5c17c..0000000
Binary files a/src/main/webapp/guac-web-lib/images/mouse/blank.gif and /dev/null differ
diff --git a/src/main/webapp/guac-web-lib/images/mouse/dot.gif b/src/main/webapp/guac-web-lib/images/mouse/dot.gif
deleted file mode 100644 (file)
index 9ac8a00..0000000
Binary files a/src/main/webapp/guac-web-lib/images/mouse/dot.gif and /dev/null differ
diff --git a/src/main/webapp/guac-web-lib/images/noimage92.png b/src/main/webapp/guac-web-lib/images/noimage92.png
deleted file mode 100644 (file)
index 657d85e..0000000
Binary files a/src/main/webapp/guac-web-lib/images/noimage92.png and /dev/null differ
diff --git a/src/main/webapp/guac-web-lib/images/spinner92.gif b/src/main/webapp/guac-web-lib/images/spinner92.gif
deleted file mode 100644 (file)
index 0dfe069..0000000
Binary files a/src/main/webapp/guac-web-lib/images/spinner92.gif and /dev/null differ
diff --git a/src/main/webapp/guac-web-lib/javascript/guacamole.js b/src/main/webapp/guac-web-lib/javascript/guacamole.js
deleted file mode 100644 (file)
index 020e88a..0000000
+++ /dev/null
@@ -1,601 +0,0 @@
-
-/*
- *  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;
-
-}
diff --git a/src/main/webapp/guac-web-lib/javascript/keyboard.js b/src/main/webapp/guac-web-lib/javascript/keyboard.js
deleted file mode 100644 (file)
index b5e6e93..0000000
+++ /dev/null
@@ -1,270 +0,0 @@
-
-/*
- *  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 GuacamoleKeyboard(element) {
-
-       /*****************************************/
-       /*** Keyboard Handler                  ***/
-       /*****************************************/
-
-       // Single key state/modifier buffer
-       var modShift = 0;
-       var modCtrl = 0;
-       var modAlt = 0;
-
-    var keydownChar = new Array();
-
-
-    // ID of routine repeating keystrokes. -1 = not repeating.
-    var repeatKeyTimeoutId = -1;
-    var repeatKeyIntervalId = -1;
-
-       // Starts repeating keystrokes
-       function startRepeat(keySym) {
-               repeatKeyIntervalId = setInterval(function() {
-            sendKeyReleased(keySym);
-            sendKeyPressed(keySym);
-        }, 50);
-       }
-
-       // Stops repeating keystrokes
-       function stopRepeat() {
-               if (repeatKeyTimeoutId != -1) clearInterval(repeatKeyTimeoutId);
-               if (repeatKeyIntervalId != -1) clearInterval(repeatKeyIntervalId);
-       }
-
-
-    function getKeySymFromKeyIdentifier(shifted, keyIdentifier) {
-
-            var unicodePrefixLocation = keyIdentifier.indexOf("U+");
-            if (unicodePrefixLocation >= 0) {
-
-                var hex = keyIdentifier.substring(unicodePrefixLocation+2);
-                var codepoint = parseInt(hex, 16);
-                var typedCharacter;
-
-                // Convert case if shifted
-                if (shifted == 0)
-                    typedCharacter = String.fromCharCode(codepoint).toLowerCase();
-                else
-                    typedCharacter = String.fromCharCode(codepoint).toUpperCase();
-
-                // Get codepoint
-                codepoint = typedCharacter.charCodeAt(0);
-
-                return getKeySymFromCharCode(codepoint);
-
-            }
-
-            return null;
-
-    }
-
-    function getKeySymFromCharCode(keyCode) {
-
-        if (keyCode >= 0x0000 && keyCode <= 0x00FF)
-            return keyCode;
-
-        if (keyCode >= 0x0100 && keyCode <= 0x10FFFF)
-            return 0x01000000 | keyCode;
-
-        return null;
-
-    }
-
-    function getKeySymFromKeyCode(keyCode) {
-
-        var keysym = null;
-               if (modShift == 0) keysym = unshiftedKeySym[keyCode];
-               else {
-            keysym = shiftedKeySym[keyCode];
-            if (keysym == null) keysym = unshiftedKeySym[keyCode];
-        }
-
-        return keysym;
-
-    }
-
-
-       // Sends a single keystroke over the network
-       function sendKeyPressed(keysym) {
-               if (keysym != null && keyPressedHandler)
-                       keyPressedHandler(keysym);
-       }
-
-       // Sends a single keystroke over the network
-       function sendKeyReleased(keysym) {
-               if (keysym != null)
-                       keyReleasedHandler(keysym);
-       }
-
-
-    var KEYDOWN = 1;
-    var KEYPRESS = 2;
-
-    var keySymSource = null;
-
-       // When key pressed
-    var keydownCode = null;
-       element.onkeydown = function(e) {
-
-        // Only intercept if handler set
-        if (!keyPressedHandler) return true;
-
-               var keynum;
-               if (window.event) keynum = window.event.keyCode;
-               else if (e.which) keynum = e.which;
-
-               // Ctrl/Alt/Shift
-               if (keynum == 16)
-                       modShift = 1;
-               else if (keynum == 17)
-                       modCtrl = 1;
-               else if (keynum == 18)
-                       modAlt = 1;
-
-        var keysym = getKeySymFromKeyCode(keynum);
-        if (keysym) {
-            // Get keysyms and events from KEYDOWN
-            keySymSource = KEYDOWN;
-        }
-
-        // If modifier keys are held down, and we have keyIdentifier
-        else if ((modCtrl == 1 || modAlt == 1) && e.keyIdentifier) {
-
-            // Get keysym from keyIdentifier
-            keysym = getKeySymFromKeyIdentifier(modShift, e.keyIdentifier);
-
-            // Get keysyms and events from KEYDOWN
-            keySymSource = KEYDOWN;
-
-        }
-
-        else
-            // Get keysyms and events from KEYPRESS
-            keySymSource = KEYPRESS;
-
-        keydownCode = keynum;
-
-        // Ignore key if we don't need to use KEYPRESS.
-        // Send key event here
-        if (keySymSource == KEYDOWN) {
-
-            if (keydownChar[keynum] != keysym) {
-
-                // Send event
-                keydownChar[keynum] = keysym;
-                sendKeyPressed(keysym);
-
-                // Clear old key repeat, if any.
-                stopRepeat();
-
-                // Start repeating (if not a modifier key) after a short delay
-                if (keynum != 16 && keynum != 17 && keynum != 18)
-                    repeatKeyTimeoutId = setTimeout(function() { startRepeat(keysym); }, 500);
-            }
-
-            return false;
-        }
-
-       };
-
-       // When key pressed
-    element.onkeypress = function(e) {
-
-        // Only intercept if handler set
-        if (!keyPressedHandler) return true;
-
-        if (keySymSource != KEYPRESS) return false;
-
-               var keynum;
-               if (window.event) keynum = window.event.keyCode;
-               else if (e.which) keynum = e.which;
-
-        var keysym = getKeySymFromCharCode(keynum);
-        if (keysym && keydownChar[keynum] != keysym) {
-
-            // If this button already pressed, release first
-            var lastKeyDownChar = keydownChar[keydownCode];
-            if (lastKeyDownChar)
-                sendKeyReleased(lastKeyDownChar);
-
-            keydownChar[keydownCode] = keysym;
-
-            // Clear old key repeat, if any.
-            stopRepeat();
-
-            // Send key event
-            sendKeyPressed(keysym);
-
-            // Start repeating (if not a modifier key) after a short delay
-            repeatKeyTimeoutId = setTimeout(function() { startRepeat(keysym); }, 500);
-        }
-
-        return false;
-       };
-
-       // When key released
-       element.onkeyup = function(e) {
-
-        // Only intercept if handler set
-        if (!keyReleasedHandler) return true;
-
-               var keynum;
-               if (window.event) keynum = window.event.keyCode;
-               else if (e.which) keynum = e.which;
-               
-               // Ctrl/Alt/Shift
-               if (keynum == 16)
-                       modShift = 0;
-               else if (keynum == 17)
-                       modCtrl = 0;
-               else if (keynum == 18)
-                       modAlt = 0;
-        else
-            stopRepeat();
-
-        // Get corresponding character
-        var lastKeyDownChar = keydownChar[keynum];
-
-        // Clear character record
-        keydownChar[keynum] = null;
-
-        // Send release event
-        sendKeyReleased(lastKeyDownChar);
-
-               return false;
-       };
-
-       // When focus is lost, clear modifiers.
-       var docOnblur = element.onblur;
-       element.onblur = function() {
-               modAlt = 0;
-               modCtrl = 0;
-               modShift = 0;
-               if (docOnblur != null) docOnblur();
-       };
-
-       var keyPressedHandler = null;
-       var keyReleasedHandler = null;
-
-       this.setKeyPressedHandler = function(kh) { keyPressedHandler = kh; };
-       this.setKeyReleasedHandler = function(kh) { keyReleasedHandler = kh; };
-
-}
diff --git a/src/main/webapp/guac-web-lib/javascript/keymap.js b/src/main/webapp/guac-web-lib/javascript/keymap.js
deleted file mode 100644 (file)
index 016bf59..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-
-/*
- *  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/>.
- */
-
-
-// Keymap
-
-var unshiftedKeySym = new Array();
-unshiftedKeySym[8]   = 0xFF08; // backspace
-unshiftedKeySym[9]   = 0xFF09; // tab
-unshiftedKeySym[13]  = 0xFF0D; // enter
-unshiftedKeySym[16]  = 0xFFE1; // shift
-unshiftedKeySym[17]  = 0xFFE3; // ctrl
-unshiftedKeySym[18]  = 0xFFE9; // alt
-unshiftedKeySym[19]  = 0xFF13; // pause/break
-unshiftedKeySym[20]  = 0xFFE5; // caps lock
-unshiftedKeySym[27]  = 0xFF1B; // escape
-unshiftedKeySym[33]  = 0xFF55; // page up
-unshiftedKeySym[34]  = 0xFF56; // page down
-unshiftedKeySym[35]  = 0xFF57; // end
-unshiftedKeySym[36]  = 0xFF50; // home
-unshiftedKeySym[37]  = 0xFF51; // left arrow
-unshiftedKeySym[38]  = 0xFF52; // up arrow
-unshiftedKeySym[39]  = 0xFF53; // right arrow
-unshiftedKeySym[40]  = 0xFF54; // down arrow
-unshiftedKeySym[45]  = 0xFF63; // insert
-unshiftedKeySym[46]  = 0xFFFF; // delete
-unshiftedKeySym[91]  = 0xFFEB; // left window key (super_l)
-unshiftedKeySym[92]  = 0xFF67; // right window key (menu key?)
-unshiftedKeySym[93]  = null; // select key
-unshiftedKeySym[112] = 0xFFBE; // f1
-unshiftedKeySym[113] = 0xFFBF; // f2
-unshiftedKeySym[114] = 0xFFC0; // f3
-unshiftedKeySym[115] = 0xFFC1; // f4
-unshiftedKeySym[116] = 0xFFC2; // f5
-unshiftedKeySym[117] = 0xFFC3; // f6
-unshiftedKeySym[118] = 0xFFC4; // f7
-unshiftedKeySym[119] = 0xFFC5; // f8
-unshiftedKeySym[120] = 0xFFC6; // f9
-unshiftedKeySym[121] = 0xFFC7; // f10
-unshiftedKeySym[122] = 0xFFC8; // f11
-unshiftedKeySym[123] = 0xFFC9; // f12
-unshiftedKeySym[144] = 0xFF7F; // num lock
-unshiftedKeySym[145] = 0xFF14; // scroll lock
-
-// Shifted versions, IF DIFFERENT FROM UNSHIFTED!
-// If any of these are null, the unshifted one will be used.
-var shiftedKeySym  = new Array();
-shiftedKeySym[18]  = 0xFFE7; // alt
-
-// Constants for keysyms for special keys
-var KEYSYM_CTRL = 65507;
-var KEYSYM_ALT = 65513;
-var KEYSYM_DELETE = 65535;
-var KEYSYM_SHIFT = 65505;
-
-
diff --git a/src/main/webapp/guac-web-lib/javascript/layer.js b/src/main/webapp/guac-web-lib/javascript/layer.js
deleted file mode 100644 (file)
index d6ac25e..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-
-/*
- *  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 Layer(width, height) {
-
-    // Off-screen buffer
-    var display = document.createElement("canvas");
-
-    display.style.position = "absolute";
-    display.style.left = "0px";
-    display.style.right = "0px";
-
-    display.width = width;
-    display.height = height;
-
-    var displayContext = display.getContext("2d");
-
-    var readyHandler = null;
-    var nextUpdateToDraw = 0;
-    var currentUpdate = 0;
-    var updates = new Array();
-
-    // Given an update ID, either call the provided update callback, or
-    // schedule the update for later.
-    function setUpdate(updateId, update) {
-
-        // If this update is the next to draw...
-        if (updateId == nextUpdateToDraw) {
-
-            // Call provided update handler.
-            update();
-
-            // Draw all pending updates.
-            var updateCallback;
-            while ((updateCallback = updates[++nextUpdateToDraw])) {
-                updateCallback();
-                delete updates[nextUpdateToDraw];
-            }
-
-            // If done with updates, call ready handler
-            if (display.isReady() && readyHandler != null)
-                readyHandler();
-
-        }
-
-        // If not next to draw, set callback and wait.
-        else
-            updates[updateId] = update;
-
-    }
-
-    display.isReady = function() {
-        return currentUpdate == nextUpdateToDraw;
-    }
-
-    display.setReadyHandler = function(handler) {
-        readyHandler = handler;
-    }
-
-
-    display.drawImage = function(x, y, image) {
-        var updateId = currentUpdate++;
-
-        setUpdate(updateId, function() {
-            displayContext.drawImage(image, x, y);
-        });
-
-    }
-
-
-    display.draw = function(x, y, url) {
-        var updateId = currentUpdate++;
-
-        var image = new Image();
-        image.onload = function() {
-            setUpdate(updateId, function() {
-                displayContext.drawImage(image, x, y);
-            });
-        };
-        image.src = url;
-    };
-
-
-    display.copyRect = function(srcx, srcy, w, h, x, y) {
-        var updateId = currentUpdate++;
-    
-        setUpdate(updateId, function() {
-            displayContext.drawImage(display, srcx, srcy, w, h, x, y, w, h);
-        });
-
-    };
-
-    display.drawRect = function(x, y, w, h, color) {
-        var updateId = currentUpdate++;
-
-        setUpdate(updateId, function() {
-            displayContext.fillStyle = color;
-            displayContext.fillRect(x, y, w, h);
-        });
-
-    };
-
-    display.clearRect = function(x, y, w, h) {
-        var updateId = currentUpdate++;
-
-        setUpdate(updateId, function() {
-            displayContext.clearRect(x, y, w, h);
-        });
-
-    };
-
-    display.filter = function(filter) {
-        var updateId = currentUpdate++;
-
-        setUpdate(updateId, function() {
-            var imageData = displayContext.getImageData(0, 0, width, height);
-            filter(imageData.data, width, height);
-            displayContext.putImageData(imageData, 0, 0);
-        });
-
-    };
-
-    return display;
-
-}
-
diff --git a/src/main/webapp/guac-web-lib/javascript/mouse.js b/src/main/webapp/guac-web-lib/javascript/mouse.js
deleted file mode 100644 (file)
index 3a1234a..0000000
+++ /dev/null
@@ -1,205 +0,0 @@
-
-/*
- *  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 GuacamoleMouse(element) {
-
-       /*****************************************/
-       /*** Mouse Handler                     ***/
-       /*****************************************/
-
-
-    var mouseIndex = 0;
-
-    var mouseLeftButton   = 0;
-    var mouseMiddleButton = 0;
-    var mouseRightButton  = 0;
-
-    var mouseX = 0;
-    var mouseY = 0;
-
-    var absoluteMouseX = 0;
-    var absoluteMouseY = 0;
-
-
-    function getMouseState(up, down) {
-        var mouseState = new MouseEvent(mouseX, mouseY,
-                mouseLeftButton, mouseMiddleButton, mouseRightButton, up, down);
-
-        return mouseState;
-    }
-
-
-    // Block context menu so right-click gets sent properly
-    element.oncontextmenu = function(e) {return false;};
-
-    element.onmousemove = function(e) {
-
-        e.stopPropagation();
-
-        absoluteMouseX = e.pageX;
-        absoluteMouseY = e.pageY;
-
-        mouseX = absoluteMouseX - element.offsetLeft;
-        mouseY = absoluteMouseY - element.offsetTop;
-
-        // This is all JUST so we can get the mouse position within the element
-        var parent = element.offsetParent;
-        while (parent) {
-            if (parent.offsetLeft && parent.offsetTop) {
-                mouseX -= parent.offsetLeft;
-                mouseY -= parent.offsetTop;
-            }
-            parent = parent.offsetParent;
-        }
-
-        movementHandler(getMouseState(0, 0));
-    };
-
-
-    element.onmousedown = function(e) {
-
-        e.stopPropagation();
-
-        switch (e.button) {
-            case 0:
-                mouseLeftButton = 1;
-                break;
-            case 1:
-                mouseMiddleButton = 1;
-                break;
-            case 2:
-                mouseRightButton = 1;
-                break;
-        }
-
-        buttonPressedHandler(getMouseState(0, 0));
-    };
-
-
-    element.onmouseup = function(e) {
-
-        e.stopPropagation();
-
-        switch (e.button) {
-            case 0:
-                mouseLeftButton = 0;
-                break;
-            case 1:
-                mouseMiddleButton = 0;
-                break;
-            case 2:
-                mouseRightButton = 0;
-                break;
-        }
-
-        buttonReleasedHandler(getMouseState(0, 0));
-    };
-
-    // Override selection on mouse event element.
-    element.onselectstart = function() {
-        return false;
-    };
-
-    // Scroll wheel support
-    function handleScroll(e) {
-
-        var delta = 0;
-        if (e.detail)
-            delta = e.detail;
-        else if (e.wheelDelta)
-            delta = -event.wheelDelta;
-
-        // Up
-        if (delta < 0) {
-            buttonPressedHandler(getMouseState(1, 0));
-            buttonReleasedHandler(getMouseState(0, 0));
-        }
-
-        // Down
-        if (delta > 0) {
-            buttonPressedHandler(getMouseState(0, 1));
-            buttonReleasedHandler(getMouseState(0, 0));
-        }
-
-        if (e.preventDefault)
-            e.preventDefault();
-
-        e.returnValue = false;
-    }
-
-    element.addEventListener('DOMMouseScroll', handleScroll, false);
-
-    element.onmousewheel = function(e) {
-        handleScroll(e);
-    }
-
-    function MouseEvent(x, y, left, middle, right, up, down) {
-
-        this.getX = function() {
-            return x;
-        };
-
-        this.getY = function() {
-            return y;
-        };
-
-        this.getLeft = function() {
-            return left;
-        };
-
-        this.getMiddle = function() {
-            return middle;
-        };
-
-        this.getRight = function() {
-            return right;
-        };
-
-        this.getUp = function() {
-            return up;
-        };
-
-        this.getDown = function() {
-            return down;
-        };
-
-        this.toString = function() {
-            return (mouseIndex++) + "," + x + "," + y + "," + left + "," + middle + "," + right + "," + up + "," + down;
-        };
-
-    }
-
-
-       var buttonPressedHandler = null;
-       var buttonReleasedHandler = null;
-       var movementHandler = null;
-
-       this.setButtonPressedHandler  = function(mh) {buttonPressedHandler = mh;};
-       this.setButtonReleasedHandler = function(mh) {buttonReleasedHandler = mh;};
-       this.setMovementHandler = function(mh) {movementHandler = mh;};
-
-
-    this.getX = function() {return mouseX;};
-    this.getY = function() {return mouseY;};
-    this.getLeftButton = function() {return mouseLeftButton;};
-    this.getMiddleButton = function() {return mouseMiddleButton;};
-    this.getRightButton = function() {return mouseRightButton;};
-
-}
diff --git a/src/main/webapp/guac-web-lib/javascript/oskeyboard.js b/src/main/webapp/guac-web-lib/javascript/oskeyboard.js
deleted file mode 100644 (file)
index 872bac2..0000000
+++ /dev/null
@@ -1,487 +0,0 @@
-
-/*
- *  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 GuacamoleOnScreenKeyboard(url) {
-
-    var tabIndex = 1;
-    var allKeys = new Array();
-    var modifierState = new function() {};
-
-    function getKeySize(size) {
-        return (5*size) + "ex";
-    }
-
-    function getCapSize(size) {
-        return (5*size - 0.5) + "ex";
-    }
-
-    function clearModifiers() {
-
-        // Send key release events for all pressed modifiers
-        for (var k=0; k<allKeys.length; k++) {
-
-            var key = allKeys[k];
-            var cap = key.getCap();
-            var modifier = cap.getModifier();
-
-            if (modifier && isModifierActive(modifier) && !cap.isSticky() && key.isPressed())
-                key.release();
-
-        }
-
-    }
-
-    function setModifierReleased(modifier) {
-        if (isModifierActive(modifier))
-            modifierState[modifier]--;
-    }
-
-    function setModifierPressed(modifier) {
-        if (modifierState[modifier] == null)
-            modifierState[modifier] = 1;
-        else
-            modifierState[modifier]++;
-    }
-
-    function isModifierActive(modifier) {
-        if (modifierState[modifier] > 0)
-            return true;
-
-        return false;
-    }
-
-    function toggleModifierPressed(modifier) {
-        if (isModifierActive(modifier))
-            setModifierReleased(modifier);
-        else
-            setModifierPressed(modifier);
-    }
-
-    function refreshAllKeysState() {
-        for (var k=0; k<allKeys.length; k++)
-            allKeys[k].refreshState();
-    }
-
-    function Key(key) {
-
-        function Cap(cap) {
-
-            // Displayed text
-            var displayText = cap.textContent;
-            
-            // Keysym
-            var keysym = null;
-            if (cap.attributes["keysym"])
-                keysym = parseInt(cap.attributes["keysym"].value);
-
-            // If keysym not specified, get keysym from display text.
-            else if (displayText.length == 1) {
-
-                var charCode = displayText.charCodeAt(0);
-
-                if (charCode >= 0x0000 && charCode <= 0x00FF)
-                    keysym = charCode;
-
-                else if (charCode >= 0x0100 && charCode <= 0x10FFFF)
-                    keysym = 0x01000000 | charCode;
-            }
-
-            // Required modifiers for this keycap
-            var reqMod = null;
-            if (cap.attributes["if"])
-                reqMod = cap.attributes["if"].value.split(",");
-
-
-            // Modifier represented by this keycap
-            var modifier = null;
-            if (cap.attributes["modifier"])
-                modifier = cap.attributes["modifier"].value;
-            
-
-            // Whether this key is sticky (toggles)
-            // Currently only valid for modifiers.
-            var sticky = false;
-            if (cap.attributes["sticky"] && cap.attributes["sticky"].value == "true")
-                sticky = true;
-
-            this.getDisplayText = function() {
-                return cap.textContent;
-            };
-
-            this.getKeySym = function() {
-                return keysym;
-            };
-
-            this.getRequiredModifiers = function() {
-                return reqMod;
-            };
-
-            this.getModifier = function() {
-                return modifier;
-            };
-
-            this.isSticky = function() {
-                return sticky;
-            };
-
-        }
-
-        var size = null;
-        if (key.attributes["size"])
-            size = parseFloat(key.attributes["size"].value);
-
-        var caps = key.getElementsByTagName("cap");
-        var keycaps = new Array();
-        for (var i=0; i<caps.length; i++)
-            keycaps.push(new Cap(caps[i]));
-
-        var rowKey = document.createElement("div");
-        rowKey.className = "key";
-
-        var keyCap = document.createElement("div");
-        keyCap.className = "cap";
-        rowKey.appendChild(keyCap);
-
-
-        var STATE_RELEASED = 0;
-        var STATE_PRESSED = 1;
-        var state = STATE_RELEASED;
-
-        rowKey.isPressed = function() {
-            return state == STATE_PRESSED;
-        }
-
-        var currentCap = null;
-        function refreshState(modifier) {
-
-            // Find current cap
-            currentCap = null;
-            for (var j=0; j<keycaps.length; j++) {
-
-                var keycap = keycaps[j];
-                var required = keycap.getRequiredModifiers();
-
-                var matches = true;
-
-                // If modifiers required, make sure all modifiers are active
-                if (required) {
-
-                    for (var k=0; k<required.length; k++) {
-                        if (!isModifierActive(required[k])) {
-                            matches = false;
-                            break;
-                        }
-                    }
-
-                }
-
-                if (matches)
-                    currentCap = keycap;
-
-            }
-
-            rowKey.className = "key";
-
-            if (currentCap.getModifier())
-                rowKey.className += " modifier";
-
-            if (currentCap.isSticky())
-                rowKey.className += " sticky";
-
-            if (isModifierActive(currentCap.getModifier()))
-                rowKey.className += " active";
-
-            if (state == STATE_PRESSED)
-                rowKey.className += " pressed";
-
-            keyCap.textContent = currentCap.getDisplayText();
-        }
-        rowKey.refreshState = refreshState;
-
-        rowKey.getCap = function() {
-            return currentCap;
-        };
-
-        refreshState();
-
-        // Set size
-        if (size) {
-            rowKey.style.width = getKeySize(size);
-            keyCap.style.width = getCapSize(size);
-        }
-
-
-
-        // Set pressed, if released
-        function press() {
-
-            if (state == STATE_RELEASED) {
-
-                state = STATE_PRESSED;
-
-                var keysym = currentCap.getKeySym();
-                var modifier = currentCap.getModifier();
-                var sticky = currentCap.isSticky();
-
-                if (keyPressedHandler && keysym)
-                    keyPressedHandler(keysym);
-
-                if (modifier) {
-
-                    // If sticky modifier, toggle
-                    if (sticky) 
-                        toggleModifierPressed(modifier);
-
-                    // Otherwise, just set on.
-                    else 
-                        setModifierPressed(modifier);
-
-                    refreshAllKeysState();
-                }
-                else
-                    refreshState();
-            }
-
-        }
-        rowKey.press = press;
-
-
-        // Set released, if pressed 
-        function release() {
-
-            if (state == STATE_PRESSED) {
-
-                state = STATE_RELEASED;
-
-                var keysym = currentCap.getKeySym();
-                var modifier = currentCap.getModifier();
-                var sticky = currentCap.isSticky();
-
-                if (keyReleasedHandler && keysym)
-                    keyReleasedHandler(keysym);
-
-                if (modifier) {
-
-                    // If not sticky modifier, release modifier
-                    if (!sticky) {
-                        setModifierReleased(modifier);
-                        refreshAllKeysState();
-                    }
-                    else
-                        refreshState();
-
-                }
-                else {
-                    refreshState();
-
-                    // If not a modifier, also release all pressed modifiers
-                    clearModifiers();
-                }
-
-            }
-
-        }
-        rowKey.release = release;
-
-        // Toggle press/release states
-        function toggle() {
-            if (state == STATE_PRESSED)
-                release();
-            else
-                press();
-        }
-
-
-        // Send key press on mousedown
-        rowKey.onmousedown = function(e) {
-
-            e.stopPropagation();
-
-            var modifier = currentCap.getModifier();
-            var sticky = currentCap.isSticky();
-
-            // Toggle non-sticky modifiers
-            if (modifier && !sticky)
-                toggle();
-
-            // Press all others
-            else
-                press();
-
-            return false;
-        };
-
-        // Send key release on mouseup/out
-        rowKey.onmouseout =
-        rowKey.onmouseout =
-        rowKey.onmouseup = function(e) {
-
-            e.stopPropagation();
-
-            var modifier = currentCap.getModifier();
-            var sticky = currentCap.isSticky();
-
-            // Release non-modifiers and sticky modifiers
-            if (!modifier || sticky)
-                release();
-
-            return false;
-        };
-
-        rowKey.onselectstart = function() { return false; };
-
-        return rowKey;
-
-    }
-
-    function Gap(gap) {
-
-        var keyboardGap = document.createElement("div");
-        keyboardGap.className = "gap";
-        keyboardGap.textContent = " ";
-
-        var size = null;
-        if (gap.attributes["size"])
-            size = parseFloat(gap.attributes["size"].value);
-
-        if (size) {
-            keyboardGap.style.width = getKeySize(size);
-            keyboardGap.style.height = getKeySize(size);
-        }
-
-        return keyboardGap;
-
-    }
-
-    function Row(row) {
-
-        var keyboardRow = document.createElement("div");
-        keyboardRow.className = "row";
-
-        var children = row.childNodes;
-        for (var j=0; j<children.length; j++) {
-            var child = children[j];
-
-            // <row> can contain <key> or <column>
-            if (child.tagName == "key") {
-                var key = new Key(child);
-                keyboardRow.appendChild(key);
-                allKeys.push(key);
-            }
-            else if (child.tagName == "gap") {
-                var gap = new Gap(child);
-                keyboardRow.appendChild(gap);
-            }
-            else if (child.tagName == "column") {
-                var col = new Column(child);
-                keyboardRow.appendChild(col);
-            }
-
-        }
-
-        return keyboardRow;
-
-    }
-
-    function Column(col) {
-
-        var keyboardCol = document.createElement("div");
-        keyboardCol.className = "col";
-
-        var align = null;
-        if (col.attributes["align"])
-            align = col.attributes["align"].value;
-
-        var children = col.childNodes;
-        for (var j=0; j<children.length; j++) {
-            var child = children[j];
-
-            // <column> can only contain <row> 
-            if (child.tagName == "row") {
-                var row = new Row(child);
-                keyboardCol.appendChild(row);
-            }
-
-        }
-
-        if (align)
-            keyboardCol.style.textAlign = align;
-
-        return keyboardCol;
-
-    }
-
-
-
-    // Create keyboard
-    var keyboard = document.createElement("div");
-    keyboard.className = "keyboard";
-
-
-    // Retrieve keyboard XML
-    var xmlhttprequest = new XMLHttpRequest();
-    xmlhttprequest.open("GET", url, false);
-    xmlhttprequest.send(null);
-
-    var xml = xmlhttprequest.responseXML;
-
-    if (xml) {
-
-        // Parse document
-        var root = xml.documentElement;
-        if (root) {
-
-            var children = root.childNodes;
-            for (var i=0; i<children.length; i++) {
-                var child = children[i];
-
-                // <keyboard> can contain <row> or <column>
-                if (child.tagName == "row") {
-                    keyboard.appendChild(new Row(child));
-                }
-                else if (child.tagName == "column") {
-                    keyboard.appendChild(new Column(child));
-                }
-
-            }
-
-        }
-
-    }
-
-    var keyPressedHandler = null;
-    var keyReleasedHandler = null;
-
-    keyboard.setKeyPressedHandler = function(kh) { keyPressedHandler = kh; };
-    keyboard.setKeyReleasedHandler = function(kh) { keyReleasedHandler = kh; };
-
-    // Do not allow selection or mouse movement to propagate/register.
-    keyboard.onselectstart =
-    keyboard.onmousemove   =
-    keyboard.onmouseup     =
-    keyboard.onmousedown   =
-    function(e) {
-        e.stopPropagation();
-        return false;
-    };
-
-    return keyboard;
-}
-
diff --git a/src/main/webapp/guacamole.css b/src/main/webapp/guacamole.css
deleted file mode 100644 (file)
index 3e5b541..0000000
+++ /dev/null
@@ -1,236 +0,0 @@
-
-/*
- *  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/>.
- */
-
-body {
-    background: black;
-    font-family: sans-serif;
-    padding: 0;
-    margin: 0;
-}
-
-div#login-ui {
-    background: silver;
-    height: 100%;
-    width: 100%;
-    position: fixed;
-    left: 0;
-    top: 0;
-    display: table;
-}
-
-p#login-error {
-    text-align: center;
-    background: #FDD;
-    color: red;
-    margin: 0.2em;
-}
-
-div#login-logo {
-    position: relative;
-    bottom: 0;
-    display: inline-block;
-    vertical-align: middle;
-}
-
-div#login-dialog-middle {
-    width: 100%;
-    display: table-cell;
-    vertical-align: middle;
-    text-align: center;
-}
-
-div#login-dialog {
-
-    background: white;
-    vertical-align: middle;
-
-    padding: 1em;
-    border: 0.2em solid black;
-
-    -khtml-border-radius: 0.5em;
-    -webkit-border-radius: 0.5em;
-    -moz-border-radius: 0.5em;
-    border-radius: 0.5em;
-
-    max-width: 75%;
-    text-align: left;
-
-    display: inline-block;
-}
-
-div#login-dialog h1 {
-    margin-top: 0;
-    margin-bottom: 0em;
-    text-align: center;
-}
-
-div#login-dialog #buttons {
-    padding-top: 0.5em;
-    text-align: center;
-}
-
-div#login-dialog #login-fields {
-    border-top: 1px solid silver;
-    border-bottom: 1px solid silver;
-    padding-top: 0.5em;
-    padding-bottom: 0.5em;
-}
-
-div.errorDialogOuter {
-    display: table;
-    height: 100%;
-    width: 100%;
-    position: fixed;
-    left: 0;
-    top: 0;
-
-    visibility: hidden;
-}
-
-div.errorDialogMiddle {
-    width: 100%;
-    text-align: center;
-    display: table-cell;
-    vertical-align: middle;
-}
-
-div.errorDialog {
-
-    background: #D44;
-    border: 1px solid black;
-    padding: 1em;
-
-    -khtml-border-radius: 0.5em;
-    -webkit-border-radius: 0.5em;
-    -moz-border-radius: 0.5em;
-    border-radius: 0.5em;
-
-    -moz-box-shadow: 0 0.25em 0.75em black, inset 0.1em 0.1em 0.5em #F99, inset -0.1em -0.1em 0.75em #800;
-    -webkit-box-shadow: 0 0.25em 0.75em black, inset 0.1em 0.1em 0.5em #F99, inset -0.1em -0.1em 0.75em #800;
-    -khtml-box-shadow: 0 0.25em 0.75em black, inset 0.1em 0.1em 0.5em #F99, inset -0.1em -0.1em 0.75em #800;
-    box-shadow: 0 0.25em 0.75em black, inset 0.1em 0.1em 0.5em #F99, inset -0.1em -0.1em 0.75em #800;
-
-    max-width: 75%;
-    text-align: left;
-
-    display: inline-block;
-}
-
-div.errorDialog h1 {
-    margin: 0;
-    margin-bottom: 0.25em;
-    text-align: center;
-    text-shadow: 0 0 0.25em white;
-}
-
-div.errorDialog div.buttons {
-    margin: 0;
-    margin-top: 0.5em;
-    text-align: center;
-}
-
-div.errorDialog p {
-    margin: 0;
-}
-
-
-#menu {
-    margin-left: auto;
-    margin-right: auto;
-    margin-bottom: 1em;
-    font-size: 0.8em;
-    background: #FEA;
-    border: 1px solid black;
-    position: fixed;
-    left: 0;
-    top: 0;
-    width: 100%;
-    z-index: 1;
-}
-
-#menu.connected {
-    opacity: 0.5;
-}
-
-#menu.connected:hover {
-    opacity: 1;
-}
-
-#menu.error {
-    background: #D44;
-}
-
-.error #state {
-    font-weight: bold;
-}
-
-img {
-    border: none;
-}
-
-img#license {
-    border: none;
-    float: right;
-    margin: 2px;
-}
-
-div#display {
-    position: relative;
-    width: 640px;
-    height: 480px;
-    margin-left: auto;
-    margin-right: auto;
-}
-
-#menu img {
-    vertical-align: middle;
-}
-
-#menu button {
-    vertical-align: middle;
-}
-
-#menu span {
-    vertical-align: middle;
-}
-
-div#clipboardDiv {
-    display: none;
-    position: absolute;
-    background: #FA5;
-    padding: 1em;
-
-    border: 1px solid black;
-    -khtml-border-radius: 0.5em;
-    -webkit-border-radius: 0.5em;
-    -moz-border-radius: 0.5em;
-    border-radius: 0.5em;
-
-    width: 50em;
-}
-
-div#clipboardDiv h2 {
-    margin: 0;
-    font-size: 1em;
-}
-
-div#clipboardDiv textarea {
-    width: 100%;
-}
-
diff --git a/src/main/webapp/images/mouse/blank.cur b/src/main/webapp/images/mouse/blank.cur
new file mode 100644 (file)
index 0000000..dfcdea8
Binary files /dev/null and b/src/main/webapp/images/mouse/blank.cur differ
diff --git a/src/main/webapp/images/mouse/blank.gif b/src/main/webapp/images/mouse/blank.gif
new file mode 100644 (file)
index 0000000..ec5c17c
Binary files /dev/null and b/src/main/webapp/images/mouse/blank.gif differ
diff --git a/src/main/webapp/images/mouse/dot.gif b/src/main/webapp/images/mouse/dot.gif
new file mode 100644 (file)
index 0000000..9ac8a00
Binary files /dev/null and b/src/main/webapp/images/mouse/dot.gif differ
diff --git a/src/main/webapp/images/noimage92.png b/src/main/webapp/images/noimage92.png
new file mode 100644 (file)
index 0000000..657d85e
Binary files /dev/null and b/src/main/webapp/images/noimage92.png differ
diff --git a/src/main/webapp/images/spinner92.gif b/src/main/webapp/images/spinner92.gif
new file mode 100644 (file)
index 0000000..0dfe069
Binary files /dev/null and b/src/main/webapp/images/spinner92.gif differ
index 8c3e816..c2087cd 100644 (file)
@@ -22,9 +22,8 @@
 
     <head>
         <link rel="icon" type="image/png" href="images/guacamole-icon-64.png"/>
-        <link rel="stylesheet" type="text/css" href="guacamole.css"/>
-        <link rel="stylesheet" type="text/css" href="guac-web-lib/css/guacamole.css"/>
-        <link rel="stylesheet" type="text/css" href="keyboard.css"/>
+        <link rel="stylesheet" type="text/css" href="styles/guacamole.css"/>
+        <link rel="stylesheet" type="text/css" href="styles/keyboard.css"/>
         <title>Guacamole</title>
     </head>
 
 
 
         <!-- Scripts -->
-        <script type="text/javascript" src="guac-web-lib/javascript/keymap.js"></script>
-        <script type="text/javascript" src="guac-web-lib/javascript/keyboard.js"></script>
-        <script type="text/javascript" src="guac-web-lib/javascript/mouse.js"></script>
-        <script type="text/javascript" src="guac-web-lib/javascript/layer.js"></script>
-        <script type="text/javascript" src="guac-web-lib/javascript/guacamole.js"></script>
-        <script type="text/javascript" src="guac-web-lib/javascript/oskeyboard.js"></script>
+        <script type="text/javascript" src="guacamole-common-js/keymap.js"></script>
+        <script type="text/javascript" src="guacamole-common-js/keyboard.js"></script>
+        <script type="text/javascript" src="guacamole-common-js/mouse.js"></script>
+        <script type="text/javascript" src="guacamole-common-js/layer.js"></script>
+        <script type="text/javascript" src="guacamole-common-js/guacamole.js"></script>
+        <script type="text/javascript" src="guacamole-common-js/oskeyboard.js"></script>
 
         <!-- Init -->
         <script type="text/javascript"> /* <![CDATA[ */
diff --git a/src/main/webapp/keyboard.css b/src/main/webapp/keyboard.css
deleted file mode 100644 (file)
index 690c14f..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-
-/*
- *  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/>.
- */
-
-div#keyboardContainer {
-    position: fixed;
-    left: 0px;
-    bottom: 0px;
-    text-align: center;
-    width: 100%;
-    display: none;
-    z-index: 1;
-}
-
-div.keyboard {
-
-    font-size: 10pt;
-    white-space: pre;
-
-    border: 1px solid black;
-    -moz-border-radius: 0.5em;
-    -webkit-border-radius: 0.5em;
-    -khtml-border-radius: 0.5em;
-    border-radius: 0.5em;
-    background: #222;
-
-    cursor: default;
-
-    opacity: 0.85;
-
-    padding: 0.25em;
-    margin: 0.25em;
-
-    display: inline-block;
-    text-align: left;
-}
-
-div.key {
-
-    margin: 0;
-    padding: 0;
-
-    width: 5ex;
-    height: 5ex;
-
-    display: inline-block;
-    text-align: center;
-}
-
-div.cap {
-
-    border: 1px solid #888;
-    -moz-border-radius: 0.25em;
-    -webkit-border-radius: 0.25em;
-    -khtml-border-radius: 0.25em;
-    border-radius: 0.25em;
-    background: #444;
-
-    margin: 0;
-    padding: 0;
-
-    color: white;
-    text-shadow: 0 0.1em 0.1em black;
-
-    width: 4.5ex;
-    height: 4.5ex;
-    line-height: 4.5ex;
-    white-space: pre;
-
-    display: inline-block;
-    text-align: center;
-}
-
-div.key:hover div.cap {
-    background: #666;
-    border-color: #666;
-    cursor: pointer;
-}
-
-div.key.pressed div.cap {
-    background: #822;
-    border-color: #D44;
-}
-
-div.key.sticky.active div.cap {
-    background: #882;
-    border-color: #DD4;
-}
-
-div.gap {
-    margin: 0;
-    padding: 0;
-
-    width: 5ex;
-    height: 5ex;
-
-    display: inline-block;
-}
-
-div.row {
-    vertical-align: top;
-}
-
-div.col {
-    display: inline-block;
-    vertical-align: top;
-}
-
diff --git a/src/main/webapp/styles/guacamole.css b/src/main/webapp/styles/guacamole.css
new file mode 100644 (file)
index 0000000..6f2b9a3
--- /dev/null
@@ -0,0 +1,254 @@
+
+/*
+ *  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/>.
+ */
+
+body {
+    background: black;
+    font-family: sans-serif;
+    padding: 0;
+    margin: 0;
+}
+
+div#login-ui {
+    background: silver;
+    height: 100%;
+    width: 100%;
+    position: fixed;
+    left: 0;
+    top: 0;
+    display: table;
+}
+
+p#login-error {
+    text-align: center;
+    background: #FDD;
+    color: red;
+    margin: 0.2em;
+}
+
+div#login-logo {
+    position: relative;
+    bottom: 0;
+    display: inline-block;
+    vertical-align: middle;
+}
+
+div#login-dialog-middle {
+    width: 100%;
+    display: table-cell;
+    vertical-align: middle;
+    text-align: center;
+}
+
+div#login-dialog {
+
+    background: white;
+    vertical-align: middle;
+
+    padding: 1em;
+    border: 0.2em solid black;
+
+    -khtml-border-radius: 0.5em;
+    -webkit-border-radius: 0.5em;
+    -moz-border-radius: 0.5em;
+    border-radius: 0.5em;
+
+    max-width: 75%;
+    text-align: left;
+
+    display: inline-block;
+}
+
+div#login-dialog h1 {
+    margin-top: 0;
+    margin-bottom: 0em;
+    text-align: center;
+}
+
+div#login-dialog #buttons {
+    padding-top: 0.5em;
+    text-align: center;
+}
+
+div#login-dialog #login-fields {
+    border-top: 1px solid silver;
+    border-bottom: 1px solid silver;
+    padding-top: 0.5em;
+    padding-bottom: 0.5em;
+}
+
+div.errorDialogOuter {
+    display: table;
+    height: 100%;
+    width: 100%;
+    position: fixed;
+    left: 0;
+    top: 0;
+
+    visibility: hidden;
+}
+
+div.errorDialogMiddle {
+    width: 100%;
+    text-align: center;
+    display: table-cell;
+    vertical-align: middle;
+}
+
+div.errorDialog {
+
+    background: #D44;
+    border: 1px solid black;
+    padding: 1em;
+
+    -khtml-border-radius: 0.5em;
+    -webkit-border-radius: 0.5em;
+    -moz-border-radius: 0.5em;
+    border-radius: 0.5em;
+
+    -moz-box-shadow: 0 0.25em 0.75em black, inset 0.1em 0.1em 0.5em #F99, inset -0.1em -0.1em 0.75em #800;
+    -webkit-box-shadow: 0 0.25em 0.75em black, inset 0.1em 0.1em 0.5em #F99, inset -0.1em -0.1em 0.75em #800;
+    -khtml-box-shadow: 0 0.25em 0.75em black, inset 0.1em 0.1em 0.5em #F99, inset -0.1em -0.1em 0.75em #800;
+    box-shadow: 0 0.25em 0.75em black, inset 0.1em 0.1em 0.5em #F99, inset -0.1em -0.1em 0.75em #800;
+
+    max-width: 75%;
+    text-align: left;
+
+    display: inline-block;
+}
+
+div.errorDialog h1 {
+    margin: 0;
+    margin-bottom: 0.25em;
+    text-align: center;
+    text-shadow: 0 0 0.25em white;
+}
+
+div.errorDialog div.buttons {
+    margin: 0;
+    margin-top: 0.5em;
+    text-align: center;
+}
+
+div.errorDialog p {
+    margin: 0;
+}
+
+
+#menu {
+    margin-left: auto;
+    margin-right: auto;
+    margin-bottom: 1em;
+    font-size: 0.8em;
+    background: #FEA;
+    border: 1px solid black;
+    position: fixed;
+    left: 0;
+    top: 0;
+    width: 100%;
+    z-index: 1;
+}
+
+#menu.connected {
+    opacity: 0.5;
+}
+
+#menu.connected:hover {
+    opacity: 1;
+}
+
+#menu.error {
+    background: #D44;
+}
+
+.error #state {
+    font-weight: bold;
+}
+
+img {
+    border: none;
+}
+
+img#license {
+    border: none;
+    float: right;
+    margin: 2px;
+}
+
+div#display {
+    position: relative;
+    width: 640px;
+    height: 480px;
+    margin-left: auto;
+    margin-right: auto;
+}
+
+#menu img {
+    vertical-align: middle;
+}
+
+#menu button {
+    vertical-align: middle;
+}
+
+#menu span {
+    vertical-align: middle;
+}
+
+div#clipboardDiv {
+    display: none;
+    position: absolute;
+    background: #FA5;
+    padding: 1em;
+
+    border: 1px solid black;
+    -khtml-border-radius: 0.5em;
+    -webkit-border-radius: 0.5em;
+    -moz-border-radius: 0.5em;
+    border-radius: 0.5em;
+
+    width: 50em;
+}
+
+div#clipboardDiv h2 {
+    margin: 0;
+    font-size: 1em;
+}
+
+div#clipboardDiv textarea {
+    width: 100%;
+}
+
+.guac-display.guac-loading {
+    border: 1px dotted gray;
+    background-image: url('../images/spinner92.gif');
+    background-position: center;
+    background-repeat: no-repeat;
+}
+
+.guac-display.guac-error {
+    border: 1px dotted red;
+    background-image: url('../images/noimage92.png');
+    background-position: center;
+    background-repeat: no-repeat;
+}
+
+.guac-hide-cursor {
+    cursor: url('../images/mouse/dot.gif'),url('../images/mouse/blank.cur'),default;
+}
+
diff --git a/src/main/webapp/styles/keyboard.css b/src/main/webapp/styles/keyboard.css
new file mode 100644 (file)
index 0000000..690c14f
--- /dev/null
@@ -0,0 +1,123 @@
+
+/*
+ *  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/>.
+ */
+
+div#keyboardContainer {
+    position: fixed;
+    left: 0px;
+    bottom: 0px;
+    text-align: center;
+    width: 100%;
+    display: none;
+    z-index: 1;
+}
+
+div.keyboard {
+
+    font-size: 10pt;
+    white-space: pre;
+
+    border: 1px solid black;
+    -moz-border-radius: 0.5em;
+    -webkit-border-radius: 0.5em;
+    -khtml-border-radius: 0.5em;
+    border-radius: 0.5em;
+    background: #222;
+
+    cursor: default;
+
+    opacity: 0.85;
+
+    padding: 0.25em;
+    margin: 0.25em;
+
+    display: inline-block;
+    text-align: left;
+}
+
+div.key {
+
+    margin: 0;
+    padding: 0;
+
+    width: 5ex;
+    height: 5ex;
+
+    display: inline-block;
+    text-align: center;
+}
+
+div.cap {
+
+    border: 1px solid #888;
+    -moz-border-radius: 0.25em;
+    -webkit-border-radius: 0.25em;
+    -khtml-border-radius: 0.25em;
+    border-radius: 0.25em;
+    background: #444;
+
+    margin: 0;
+    padding: 0;
+
+    color: white;
+    text-shadow: 0 0.1em 0.1em black;
+
+    width: 4.5ex;
+    height: 4.5ex;
+    line-height: 4.5ex;
+    white-space: pre;
+
+    display: inline-block;
+    text-align: center;
+}
+
+div.key:hover div.cap {
+    background: #666;
+    border-color: #666;
+    cursor: pointer;
+}
+
+div.key.pressed div.cap {
+    background: #822;
+    border-color: #D44;
+}
+
+div.key.sticky.active div.cap {
+    background: #882;
+    border-color: #DD4;
+}
+
+div.gap {
+    margin: 0;
+    padding: 0;
+
+    width: 5ex;
+    height: 5ex;
+
+    display: inline-block;
+}
+
+div.row {
+    vertical-align: top;
+}
+
+div.col {
+    display: inline-block;
+    vertical-align: top;
+}
+