/*

Siesta 5.6.1
Copyright(c) 2009-2022 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) {
                    if ('value' in el) {
                        el.value    = ''
                    }
                    // IE11 reports true for input´s `isContentEditable`, need to check this after 'value' check
                    else if (el.isContentEditable) {
                        el.innerHTML    = ''
                    }
                    else {
                        // What's up here...?
                    }
                }

                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)
        }
    }
});