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 */