GUACAMOLE-352: Add Guacamole.Keyboard.InputSink object to serve as a reliable default destination for input events.
Project: http://git-wip-us.apache.org/repos/asf/guacamole-client/repo Commit: http://git-wip-us.apache.org/repos/asf/guacamole-client/commit/f9a639d2 Tree: http://git-wip-us.apache.org/repos/asf/guacamole-client/tree/f9a639d2 Diff: http://git-wip-us.apache.org/repos/asf/guacamole-client/diff/f9a639d2 Branch: refs/heads/master Commit: f9a639d2014815c8e68bdebb17dd78283d31ee68 Parents: 3d6a3aa Author: Michael Jumper <[email protected]> Authored: Sun Dec 17 22:38:10 2017 -0800 Committer: Michael Jumper <[email protected]> Committed: Tue Jan 16 09:50:54 2018 -0800 ---------------------------------------------------------------------- .../src/main/webapp/modules/Keyboard.js | 88 ++++++++++++++++++++ 1 file changed, 88 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/f9a639d2/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 a11050f..ea7128b 100644 --- a/guacamole-common-js/src/main/webapp/modules/Keyboard.js +++ b/guacamole-common-js/src/main/webapp/modules/Keyboard.js @@ -1324,3 +1324,91 @@ Guacamole.Keyboard.ModifierState.fromKeyboardEvent = function(e) { return state; }; + +/** + * A hidden input field which attempts to keep itself focused at all times, + * except when another input field has been intentionally focused, whether + * programatically or by the user. The actual underlying input field, returned + * by getElement(), may be used as a reliable source of keyboard-related events, + * particularly composition and input events which may require a focused input + * field to be dispatched at all. + * + * @constructor + */ +Guacamole.Keyboard.InputSink = function InputSink() { + + /** + * Reference to this instance of Guacamole.Keyboard.InputSink. + * + * @private + * @type {Guacamole.Keyboard.InputSink} + */ + var sink = this; + + /** + * The underlying input field, styled to be invisible. + * + * @private + * @type {Element} + */ + var field = document.createElement('textarea'); + field.setAttribute('autofocus', 'autofocus'); + field.style.position = 'fixed'; + field.style.border = 'none'; + field.style.width = '10px'; + field.style.height = '10px'; + field.style.left = '-10px'; + field.style.top = '-10px'; + + /** + * Clears the contents of the underlying field. The actual clearing of the + * field is deferred, occurring asynchronously after the call completes. + * + * @private + */ + var clear = function clear() { + window.setTimeout(function deferClear() { + field.value = ''; + }, 0); + }; + + // Keep internal field contents clear + field.addEventListener("change", clear, false); + field.addEventListener("input", clear, false); + + /** + * Attempts to focus the underlying input field. The focus attempt occurs + * asynchronously, and may silently fail depending on browser restrictions. + */ + this.focus = function focus() { + window.setTimeout(function deferRefocus() { + field.focus(); // Focus must be deferred to work reliably across browsers + }, 0); + }; + + /** + * Returns the underlying input field. This input field MUST be manually + * added to the DOM for the Guacamole.Keyboard.InputSink to have any + * effect. + * + * @returns {Element} + */ + this.getElement = function getElement() { + return field; + }; + + // Automatically refocus input sink if part of DOM + document.addEventListener("click", function refocusSink(e) { + + // Do not refocus if focus is on an input field + var focused = document.activeElement; + if (focused && focused !== document.body) + return; + + // Refocus input sink instead of handling click + sink.focus(); + e.preventDefault(); + + }, true); + +};
