Fix jsdoc, add missing documentation.
[guacamole-common-js.git] / src / main / resources / keyboard.js
index 6787af8..8c08f39 100644 (file)
  *
  * ***** END LICENSE BLOCK ***** */
 
-// Guacamole namespace
+/**
+ * Namespace for all Guacamole JavaScript objects.
+ * @namespace
+ */
 var Guacamole = Guacamole || {};
 
 /**
@@ -60,9 +63,6 @@ Guacamole.Keyboard = function(element) {
      * 
      * @event
      * @param {Number} keysym The keysym of the key being pressed.
-     * @returns {Boolean} true if the originating event of this keypress should
-     *                    be allowed through to the browser, false or undefined
-     *                    otherwise.
      */
     this.onkeydown = null;
 
@@ -72,9 +72,6 @@ Guacamole.Keyboard = function(element) {
      * 
      * @event
      * @param {Number} keysym The keysym of the key being released.
-     * @returns {Boolean} true if the originating event of this key release 
-     *                    should be allowed through to the browser, false or
-     *                    undefined otherwise.
      */
     this.onkeyup = null;
 
@@ -93,6 +90,7 @@ Guacamole.Keyboard = function(element) {
         19:  0xFF13, // pause/break
         20:  0xFFE5, // caps lock
         27:  0xFF1B, // escape
+        32:  0x0020, // space
         33:  0xFF55, // page up
         34:  0xFF56, // page down
         35:  0xFF57, // end
@@ -261,7 +259,7 @@ Guacamole.Keyboard = function(element) {
 
     // Stops repeating keystrokes
     function stopRepeat() {
-        if (repeatKeyTimeoutId != -1) clearInterval(repeatKeyTimeoutId);
+        if (repeatKeyTimeoutId != -1) clearTimeout(repeatKeyTimeoutId);
         if (repeatKeyIntervalId != -1) clearInterval(repeatKeyIntervalId);
     }
 
@@ -292,13 +290,22 @@ Guacamole.Keyboard = function(element) {
 
     }
 
-    function getKeySymFromCharCode(keyCode) {
+    function isControlCharacter(codepoint) {
+        return codepoint <= 0x1F || (codepoint >= 0x7F && codepoint <= 0x9F);
+    }
+
+    function getKeySymFromCharCode(codepoint) {
 
-        if (keyCode >= 0x0000 && keyCode <= 0x00FF)
-            return keyCode;
+        // Keysyms for control characters
+        if (isControlCharacter(codepoint)) return 0xFF00 | codepoint;
 
-        if (keyCode >= 0x0100 && keyCode <= 0x10FFFF)
-            return 0x01000000 | keyCode;
+        // Keysyms for ASCII chars
+        if (codepoint >= 0x0000 && codepoint <= 0x00FF)
+            return codepoint;
+
+        // Keysyms for Unicode
+        if (codepoint >= 0x0100 && codepoint <= 0x10FFFF)
+            return 0x01000000 | codepoint;
 
         return null;
 
@@ -326,9 +333,7 @@ Guacamole.Keyboard = function(element) {
 
         // Send key event
         if (keysym != null && guac_keyboard.onkeydown)
-            return guac_keyboard.onkeydown(keysym) != false;
-        
-        return true;
+            guac_keyboard.onkeydown(keysym);
 
     }
 
@@ -340,20 +345,19 @@ Guacamole.Keyboard = function(element) {
 
         // Send key event
         if (keysym != null && guac_keyboard.onkeyup)
-            return guac_keyboard.onkeyup(keysym) != false;
-
-        return true;
+            guac_keyboard.onkeyup(keysym);
 
     }
 
 
+    var expect_keypress = true;
     var keydown_code = null;
 
     var deferred_keypress = null;
     var keydown_keysym = null;
     var keypress_keysym = null;
 
-    function fireKeyPress() {
+    function handleKeyEvents() {
 
         // Prefer keysym from keypress
         var keysym = keypress_keysym || keydown_keysym;
@@ -399,8 +403,7 @@ Guacamole.Keyboard = function(element) {
         var codepoint = parseInt(hex, 16);
 
         // If control character, not typable
-        if (codepoint <= 0x1F) return false;
-        if (codepoint >= 0x7F && codepoint <= 0x9F) return false;
+        if (isControlCharacter(codepoint)) return false;
 
         // Otherwise, typable
         return true;
@@ -408,7 +411,7 @@ Guacamole.Keyboard = function(element) {
     }
 
     // When key pressed
-    element.onkeydown = function(e) {
+    element.addEventListener("keydown", function(e) {
 
         // Only intercept if handler set
         if (!guac_keyboard.onkeydown) return;
@@ -417,6 +420,14 @@ Guacamole.Keyboard = function(element) {
         if (window.event) keynum = window.event.keyCode;
         else if (e.which) keynum = e.which;
 
+        // Ignore any unknown key events
+        if (keynum == 0 && !e.keyIdentifier) {
+            e.preventDefault();
+            return;
+        }
+
+        expect_keypress = true;
+
         // Ctrl/Alt/Shift
         if (keynum == 16)      guac_keyboard.modifiers.shift = true;
         else if (keynum == 17) guac_keyboard.modifiers.ctrl  = true;
@@ -425,6 +436,10 @@ Guacamole.Keyboard = function(element) {
         // Try to get keysym from keycode
         keydown_keysym = getKeySymFromKeyCode(keynum);
 
+        // If key is known from keycode, prevent default
+        if (keydown_keysym)
+            expect_keypress = false;
+        
         // Also try to get get keysym from keyIdentifier
         if (e.keyIdentifier) {
 
@@ -439,25 +454,39 @@ Guacamole.Keyboard = function(element) {
             if (!isTypable(e.keyIdentifier)
                     || ( guac_keyboard.modifiers.ctrl && !guac_keyboard.modifiers.alt)
                     || (!guac_keyboard.modifiers.ctrl &&  guac_keyboard.modifiers.alt))
-                e.preventDefault();
+                expect_keypress = false;
             
         }
 
         // Set keycode which will be associated with any future keypress
         keydown_code = keynum;
 
-        // Defer handling of event until after any other pending
-        // key events.
-        if (!deferred_keypress)
-            deferred_keypress = window.setTimeout(fireKeyPress, 0);
+        // If we expect to handle via keypress, set failsafe timeout and
+        // wait for keypress.
+        if (expect_keypress) {
+            if (!deferred_keypress)
+                deferred_keypress = window.setTimeout(handleKeyEvents, 0);
+        }
 
-    };
+        // Otherwise, handle now
+        else {
+            e.preventDefault();
+            handleKeyEvents();
+        }
+
+    }, true);
 
     // When key pressed
-    element.onkeypress = function(e) {
+    element.addEventListener("keypress", function(e) {
 
         // Only intercept if handler set
-        if (!guac_keyboard.onkeydown) return true;
+        if (!guac_keyboard.onkeydown) return;
+
+        e.preventDefault();
+
+        // Do not handle if we weren't expecting this event (will have already
+        // been handled by keydown)
+        if (!expect_keypress) return;
 
         var keynum;
         if (window.event) keynum = window.event.keyCode;
@@ -465,25 +494,29 @@ Guacamole.Keyboard = function(element) {
 
         keypress_keysym = getKeySymFromCharCode(keynum);
 
-        // If event identified as a typable character (keypress involved)
-        // then release Ctrl and Alt (if pressed)
-        if (guac_keyboard.modifiers.ctrl) sendKeyReleased(0xFFE3);
-        if (guac_keyboard.modifiers.alt)  sendKeyReleased(0xFFE9);
+        // If event identified as a typable character, and we're holding Ctrl+Alt,
+        // assume Ctrl+Alt is actually AltGr, and release both.
+        if (!isControlCharacter(keynum) && guac_keyboard.modifiers.ctrl && guac_keyboard.modifiers.alt) {
+            sendKeyReleased(0xFFE3);
+            sendKeyReleased(0xFFE9);
+        }
 
-        // Defer handling of event until after any other pending
-        // key events.
-        if (!deferred_keypress)
-            deferred_keypress = window.setTimeout(fireKeyPress, 0);
+        // Clear timeout, if any
+        if (deferred_keypress)
+            window.clearTimeout(deferred_keypress);
 
-        return false;
+        // Handle event with all aggregated data
+        handleKeyEvents();
 
-    };
+    }, true);
 
     // When key released
-    element.onkeyup = function(e) {
+    element.addEventListener("keyup", function(e) {
 
         // Only intercept if handler set
-        if (!guac_keyboard.onkeyup) return true;
+        if (!guac_keyboard.onkeyup) return;
+
+        e.preventDefault();
 
         var keynum;
         if (window.event) keynum = window.event.keyCode;
@@ -503,15 +536,15 @@ Guacamole.Keyboard = function(element) {
         keydownChar[keynum] = null;
 
         // Send release event
-        return sendKeyReleased(lastKeyDownChar);
+        sendKeyReleased(lastKeyDownChar);
 
-    };
+    }, true);
 
     // When focus is lost, clear modifiers.
-    element.onblur = function() {
+    element.addEventListener("blur", function() {
         guac_keyboard.modifiers.alt = false;
         guac_keyboard.modifiers.ctrl = false;
         guac_keyboard.modifiers.shift = false;
-    };
+    }, true);
 
 };