GUACAMOLE-352: Allow additional elements to be added to a single 
Guacamole.Keyboard.


Project: http://git-wip-us.apache.org/repos/asf/guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/guacamole-client/commit/2d26d24d
Tree: http://git-wip-us.apache.org/repos/asf/guacamole-client/tree/2d26d24d
Diff: http://git-wip-us.apache.org/repos/asf/guacamole-client/diff/2d26d24d

Branch: refs/heads/master
Commit: 2d26d24dda30ba3679c3259e920487fdf28865bf
Parents: 0582290
Author: Michael Jumper <mjum...@apache.org>
Authored: Mon Dec 18 10:58:50 2017 -0800
Committer: Michael Jumper <mjum...@apache.org>
Committed: Tue Jan 16 10:20:41 2018 -0800

----------------------------------------------------------------------
 .../src/main/webapp/modules/Keyboard.js         | 272 ++++++++++++-------
 1 file changed, 180 insertions(+), 92 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/2d26d24d/guacamole-common-js/src/main/webapp/modules/Keyboard.js
----------------------------------------------------------------------
diff --git a/guacamole-common-js/src/main/webapp/modules/Keyboard.js 
b/guacamole-common-js/src/main/webapp/modules/Keyboard.js
index 3cc28f8..c25a1fd 100644
--- a/guacamole-common-js/src/main/webapp/modules/Keyboard.js
+++ b/guacamole-common-js/src/main/webapp/modules/Keyboard.js
@@ -25,9 +25,12 @@ var Guacamole = Guacamole || {};
  * which represent keys as their corresponding X11 keysym.
  * 
  * @constructor
- * @param {Element} element The Element to use to provide keyboard events.
+ * @param {Element} [element]
+ *    The Element to use to provide keyboard events. If omitted, at least one
+ *    Element must be manually provided through the listenTo() function for
+ *    the Guacamole.Keyboard instance to have any effect.
  */
-Guacamole.Keyboard = function(element) {
+Guacamole.Keyboard = function Keyboard(element) {
 
     /**
      * Reference to this Guacamole.Keyboard.
@@ -36,6 +39,25 @@ Guacamole.Keyboard = function(element) {
     var guac_keyboard = this;
 
     /**
+     * An integer value which uniquely identifies this Guacamole.Keyboard
+     * instance with respect to other Guacamole.Keyboard instances.
+     *
+     * @private
+     * @type {Number}
+     */
+    var guacKeyboardID = Guacamole.Keyboard._nextID++;
+
+    /**
+     * The name of the property which is added to event objects via markEvent()
+     * to note that they have already been handled by this Guacamole.Keyboard.
+     *
+     * @private
+     * @constant
+     * @type {String}
+     */
+    var EVENT_MARKER = '_GUAC_KEYBOARD_HANDLED_BY_' + guacKeyboardID;
+
+    /**
      * Fired whenever the user presses a key with the element associated
      * with this Guacamole.Keyboard in focus.
      * 
@@ -1134,131 +1156,197 @@ Guacamole.Keyboard = function(element) {
 
     };
 
-    // When key pressed
-    element.addEventListener("keydown", function(e) {
+    /**
+     * Attempts to mark the given Event as having been handled by this
+     * Guacamole.Keyboard. If the Event has already been marked as handled,
+     * false is returned.
+     *
+     * @param {Event} e
+     *     The Event to mark.
+     *
+     * @returns {Boolean}
+     *     true if the given Event was successfully marked, false if the given
+     *     Event was already marked.
+     */
+    var markEvent = function markEvent(e) {
+
+        // Fail if event is already marked
+        if (e[EVENT_MARKER])
+            return false;
+
+        // Mark event otherwise
+        e[EVENT_MARKER] = true;
+        return true;
+
+    };
+
+    /**
+     * Attaches event listeners to the given Element, automatically translating
+     * received key, input, and composition events into simple keydown/keyup
+     * events signalled through this Guacamole.Keyboard's onkeydown and
+     * onkeyup handlers.
+     *
+     * @param {Element} element
+     *     The Element to attach event listeners to for the sake of handling
+     *     key or input events.
+     */
+    this.listenTo = function listenTo(element) {
+
+        // When key pressed
+        element.addEventListener("keydown", function(e) {
 
-        // Only intercept if handler set
-        if (!guac_keyboard.onkeydown) return;
+            // Only intercept if handler set
+            if (!guac_keyboard.onkeydown) return;
 
-        var keyCode;
-        if (window.event) keyCode = window.event.keyCode;
-        else if (e.which) keyCode = e.which;
+            // Ignore events which have already been handled
+            if (!markEvent(e)) return;
 
-        // Fix modifier states
-        syncModifierStates(e);
+            var keyCode;
+            if (window.event) keyCode = window.event.keyCode;
+            else if (e.which) keyCode = e.which;
 
-        // Ignore (but do not prevent) the "composition" keycode sent by some
-        // browsers when an IME is in use (see: 
http://lists.w3.org/Archives/Public/www-dom/2010JulSep/att-0182/keyCode-spec.html)
-        if (keyCode === 229)
-            return;
+            // Fix modifier states
+            syncModifierStates(e);
 
-        // Log event
-        var keydownEvent = new KeydownEvent(keyCode, e.keyIdentifier, e.key, 
getEventLocation(e));
-        eventLog.push(keydownEvent);
+            // Ignore (but do not prevent) the "composition" keycode sent by 
some
+            // browsers when an IME is in use (see: 
http://lists.w3.org/Archives/Public/www-dom/2010JulSep/att-0182/keyCode-spec.html)
+            if (keyCode === 229)
+                return;
 
-        // Interpret as many events as possible, prevent default if indicated
-        if (interpret_events())
-            e.preventDefault();
+            // Log event
+            var keydownEvent = new KeydownEvent(keyCode, e.keyIdentifier, 
e.key, getEventLocation(e));
+            eventLog.push(keydownEvent);
 
-    }, true);
+            // Interpret as many events as possible, prevent default if 
indicated
+            if (interpret_events())
+                e.preventDefault();
+
+        }, true);
+
+        // When key pressed
+        element.addEventListener("keypress", function(e) {
+
+            // Only intercept if handler set
+            if (!guac_keyboard.onkeydown && !guac_keyboard.onkeyup) return;
+
+            // Ignore events which have already been handled
+            if (!markEvent(e)) return;
 
-    // When key pressed
-    element.addEventListener("keypress", function(e) {
+            var charCode;
+            if (window.event) charCode = window.event.keyCode;
+            else if (e.which) charCode = e.which;
 
-        // Only intercept if handler set
-        if (!guac_keyboard.onkeydown && !guac_keyboard.onkeyup) return;
+            // Fix modifier states
+            syncModifierStates(e);
 
-        var charCode;
-        if (window.event) charCode = window.event.keyCode;
-        else if (e.which) charCode = e.which;
+            // Log event
+            var keypressEvent = new KeypressEvent(charCode);
+            eventLog.push(keypressEvent);
 
-        // Fix modifier states
-        syncModifierStates(e);
+            // Interpret as many events as possible, prevent default if 
indicated
+            if (interpret_events())
+                e.preventDefault();
 
-        // Log event
-        var keypressEvent = new KeypressEvent(charCode);
-        eventLog.push(keypressEvent);
+        }, true);
+
+        // When key released
+        element.addEventListener("keyup", function(e) {
+
+            // Only intercept if handler set
+            if (!guac_keyboard.onkeyup) return;
+
+            // Ignore events which have already been handled
+            if (!markEvent(e)) return;
 
-        // Interpret as many events as possible, prevent default if indicated
-        if (interpret_events())
             e.preventDefault();
 
-    }, true);
+            var keyCode;
+            if (window.event) keyCode = window.event.keyCode;
+            else if (e.which) keyCode = e.which;
 
-    // When key released
-    element.addEventListener("keyup", function(e) {
+            // Fix modifier states
+            syncModifierStates(e);
 
-        // Only intercept if handler set
-        if (!guac_keyboard.onkeyup) return;
+            // Log event, call for interpretation
+            var keyupEvent = new KeyupEvent(keyCode, e.keyIdentifier, e.key, 
getEventLocation(e));
+            eventLog.push(keyupEvent);
+            interpret_events();
 
-        e.preventDefault();
+        }, true);
 
-        var keyCode;
-        if (window.event) keyCode = window.event.keyCode;
-        else if (e.which) keyCode = e.which;
-        
-        // Fix modifier states
-        syncModifierStates(e);
+        /**
+         * Handles the given "input" event, typing the data within the input 
text.
+         * If the event is complete (text is provided), handling of 
"compositionend"
+         * events is suspended, as such events may conflict with input events.
+         *
+         * @private
+         * @param {InputEvent} e
+         *     The "input" event to handle.
+         */
+        var handleInput = function handleInput(e) {
 
-        // Log event, call for interpretation
-        var keyupEvent = new KeyupEvent(keyCode, e.keyIdentifier, e.key, 
getEventLocation(e));
-        eventLog.push(keyupEvent);
-        interpret_events();
+            // Only intercept if handler set
+            if (!guac_keyboard.onkeydown && !guac_keyboard.onkeyup) return;
 
-    }, true);
+            // Ignore events which have already been handled
+            if (!markEvent(e)) return;
 
-    /**
-     * Handles the given "input" event, typing the data within the input text.
-     * If the event is complete (text is provided), handling of 
"compositionend"
-     * events is suspended, as such events may conflict with input events.
-     *
-     * @private
-     * @param {InputEvent} e
-     *     The "input" event to handle.
-     */
-    var handleInput = function handleInput(e) {
+            // Type all content written
+            if (e.data && !e.isComposing) {
+                element.removeEventListener("compositionend", 
handleComposition, true);
+                guac_keyboard.type(e.data);
+            }
 
-        // Only intercept if handler set
-        if (!guac_keyboard.onkeydown && !guac_keyboard.onkeyup) return;
+        };
 
-        // Type all content written
-        if (e.data && !e.isComposing) {
-            element.removeEventListener("compositionend", handleComposition, 
false);
-            guac_keyboard.type(e.data);
-        }
+        /**
+         * Handles the given "compositionend" event, typing the data within the
+         * composed text. If the event is complete (composed text is provided),
+         * handling of "input" events is suspended, as such events may conflict
+         * with composition events.
+         *
+         * @private
+         * @param {CompositionEvent} e
+         *     The "compositionend" event to handle.
+         */
+        var handleComposition = function handleComposition(e) {
 
-    };
+            // Only intercept if handler set
+            if (!guac_keyboard.onkeydown && !guac_keyboard.onkeyup) return;
 
-    /**
-     * Handles the given "compositionend" event, typing the data within the
-     * composed text. If the event is complete (composed text is provided),
-     * handling of "input" events is suspended, as such events may conflict
-     * with composition events.
-     *
-     * @private
-     * @param {CompositionEvent} e
-     *     The "compositionend" event to handle.
-     */
-    var handleComposition = function handleComposition(e) {
+            // Ignore events which have already been handled
+            if (!markEvent(e)) return;
 
-        // Only intercept if handler set
-        if (!guac_keyboard.onkeydown && !guac_keyboard.onkeyup) return;
+            // Type all content written
+            if (e.data) {
+                element.removeEventListener("input", handleInput, true);
+                guac_keyboard.type(e.data);
+            }
 
-        // Type all content written
-        if (e.data) {
-            element.removeEventListener("input", handleInput, false);
-            guac_keyboard.type(e.data);
-        }
+        };
+
+        // Automatically type text entered into the wrapped field
+        element.addEventListener("input", handleInput, true);
+        element.addEventListener("compositionend", handleComposition, true);
 
     };
 
-    // Automatically type text entered into the wrapped element
-    element.addEventListener("input", handleInput, false);
-    element.addEventListener("compositionend", handleComposition, false);
+    // Listen to given element, if any
+    if (element)
+        guac_keyboard.listenTo(element);
 
 };
 
 /**
+ * The unique numerical identifier to assign to the next Guacamole.Keyboard
+ * instance.
+ *
+ * @private
+ * @type {Number}
+ */
+Guacamole.Keyboard._nextID = 0;
+
+/**
  * The state of all supported keyboard modifiers.
  * @constructor
  */

Reply via email to