    /*

    Siesta 5.3.0
    Copyright(c) 2009-2019 Bryntum AB
    https://bryntum.com/contact
    https://bryntum.com/products/siesta/license

    */
    /**
    @class Siesta.Test.UserAgent.Keyboard

    This is a mixin, providing the keyboard events simulation functionality.

    */

    Role('Siesta.Test.UserAgent.Keyboard', {

        requires        : [ 'normalizeElement', 'runPromiseAsync', 'activeElement' ],

        does : [
            Siesta.Util.Role.CanFormatStrings,
            Siesta.Test.Browser.Role.CanWorkWithKeyboard
        ],

        has : {
        },

        methods: {

            /**
             * This method will simulate keyboard typing on either a provided DOM element, or, if omitted, on the currently focuced DOM element.
             * Simulation of certain special keys such as ENTER, ESC, LEFT etc is supported.
             * You can type these special keys by using the all uppercase name the key inside square brackets: `[ENTER]`, `[BACKSPACE]`.
             * See {@link Siesta.Test.UserAgent.KeyCodes} for a list of key names.
             *
             * For example:
             *
        t.type(el, 'Foo bar[ENTER]', function () {
            ...
        })
             *
             * To type the `[ENTER]` as plain text and not as a special character - use double square brackets: `[[ENTER]]`
             *
             * To specify a control key like "shift/control/alt" of to be pressed during typing, use the `options` argument, for example:

        t.type(el, 'Foo bar[ENTER]', callback, scope, { shiftKey : true, ctrlKey : true, altKey : true });

             *
             * This method returns a `Promise` which is resolved once the click has completed:
             *
             *      t.type('#someEl', 'someText').then(function () {
             *          return t.type('#anotherEl', 'someText')
             *      }).then(function () {
             *          return t.type('#yetAnotherEl', 'someText')
             *      })
             *
             * See also {@link Siesta.Test#chain chain} method for slimer chaining notation.
             *
             * @param {Siesta.Test.ActionTarget} el The element to type into. If not provided, currently focused element will be used as target.
             * @param {String} text The text to type, including any names of special keys in square brackets.
             * @param {Function} callback A function to be called after the type operation is completed.
             * @param {Object} scope The scope for the callback
             * @param {Object} options (optional) any extra options used to configure the DOM key events (like holding shiftKey, ctrlKey, altKey etc).
             * @param {Boolean} clearExisting (optional) true to clear existing text in the target before typing
             *
             * @return {Promise} Returns a promise resolved once the action has completed
             */
            type : function (el, text, callback, scope, options, clearExisting, performTargetCheck) {
                if (text == null) throw 'Must supply a string to type';

                // Skip target check if user is simply targeting whatever is focused
                if (!el) performTargetCheck = false;

                el              = el || this.activeElement();

                var targetCheckPromise =
                    performTargetCheck !== false
                        ?
                    this.waitForTargetAndSyncMousePosition(el, null, 'Type: ' + text, [], false, false)
                        :
                    Promise.resolve()

                var me          = this

                return me.runPromiseAsync(targetCheckPromise.then(function () {
                    el              = me.normalizeElement(el);

                    if (!el || el.disabled) {
                        return;
                    }

                    if (clearExisting) {
                        el.value    = ''
                    }

                    var queue       = new Siesta.Util.Queue({
                        deferer         : me.originalSetTimeout,
                        deferClearer    : me.originalClearTimeout,

                        interval        : me.simulator.actionDelay,
                        callbackDelay   : me.simulator.afterActionDelay,

                        observeTest     : me
                    })

                    // Manually focus the element to type into first
                    queue.addStep({
                        processor       : function () {
                            me.focus(el)
                        }
                    })

                    // focus the element one more time for IE - me seems to fix the weird sporadic failures in 042_keyevent_simulation3.t.js
                    // failures are caused by the field "blur" immediately after 1st focus
                    // no Ext "focus/blur" methods seems to be called, so it can be a browser behavior
                    bowser.msie && queue.addStep({
                        processor       : function () {
                            me.focus(el)
                        }
                    })

                    queue.addStep({
                        isAsync         : true,

                        processor       : function (stepData) {
                            var promise     = me.simulator.simulateType(text, options, { el : el })

                            me.runPromiseAsync(promise, '', stepData.next)
                        }
                    })

                    return new Promise(function (resolve, reject) {
                        queue.run(function () {
                            resolve()
                        })
                    })
                }), 'type : `' + text + '`', callback, scope)
            },

            /**
             * @param {Siesta.Test.ActionTarget} el
             * @param {String} key
             * @param {Object} options any extra options used to configure the DOM event. This argument can be omitted.
             * @param {Function} callback A function to be called after the type operation is completed.
             * @param {Object} scope The scope for the callback
             *
             * @return {Promise} Returns a promise resolved once the action has completed
             *
             * This method will simluate the key press, translated to the specified DOM element. It is generally exactly equivalent of
             * typing a single character, special characters are supported in the same way as in {@link #type} method.
             */
            keyPress: function (el, key, options, callback, scope) {
                if (typeof options === 'function') {
                    callback        = options;
                    options         = undefined;
                }

                return this.type(el, key, callback, scope, options)
            }
        }
    });