    /*

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

    */
    /**
    @class Siesta.Test.Action.Role.HasTarget

    This is a mixin, allowing the action to have "target" attribute, also aliased as "el". Target will be also passed
    further on chain, as the argument after "next":

        t.chain(
            {
                action      : 'click',
                target      : buttonComp
            },
            function (next, buttonComp) {
                // target is available as 2nd argument

                next()
            },
            {
                action      : 'click',
                target      : '>>button',
                offset      : [10, 20]
            },
            function (next, buttonComp) {
                // various queries will be resolved down to Ext.Component instance or DOM element

                next()
            },
            ...
        )

    If needed, this behavior can be disabled with {@link #passTargetToNext} option.

    */
    Role('Siesta.Test.Action.Role.HasTarget', {

        has : {
            /**
             * @cfg {Siesta.Test.ActionTarget/Function} [target=undefined]
             *
             * A target for action. See {@link Siesta.Test.ActionTarget} for various values that can be provided.
             *
             * **Important.** If the function is provided for this config, it will be called and returning value used as actual target.
             * This is useful, since sometimes target for the action depends from the previous step and
             * is not yet available during `t.chain` call.
             *
             * For example, you want to click on the button which opens a window and then click on something in the window. Compare:
             *

        t.chain(
            // clicking on button opens the window
            {
                action      : 'click',
                target      : buttonComp
            },
            // FRAGILE: `windowComp` could not be rendered yet - `buttonComp` is not yet clicked!
            {
                action      : 'click',
                target      : windowComp.el.down('.clickArea')
            }

            // MORE ROBUST: taking the "el" right before this action starts
            {
                action      : 'click',
                target      : function () {
                    return windowComp.el.down('.clickArea')
                }
            }
        )
             *
             * Target will be available in the next step as the 2nd argument. See {@link Siesta.Test.Action.Role.HasTarget}
             *
             * This config option can also be provided as "el"
             */
            target              : { required : false },

            normalizedTarget    : null,
            cachedTarget        : null,

            /**
             * @cfg {Object} el
             *
             * An alias for {@link #target}
             */

            /**
             * @cfg {Boolean} passTargetToNext Whether to pass the target further on chain as the first argument
             */
            passTargetToNext    : true,

            /**
             * @cfg {Object} options
             *
             * Any options that will be used when simulating the event. For information about possible
             * config options, please see: <https://developer.mozilla.org/en-US/docs/DOM/event.initMouseEvent>
             */
            options             : null,

            /**
             * @cfg {Array} offset
             *
             * An offset in X, Y coordinates from the targeted element. The offset can be expressed in 3 different ways.
             *
             * - Integers             : `[10, 20]`       -  offset 10px from left, 20px from top
             * - Percent              : `["10%", 20]`    -  offset 10% from left, 20px from top
             * - Percent + offset     : `["100%-2", 20]` -  offset -2px from right, 20px from top
             */
            offset              : null,

            waitForTarget       : true
        },

        after : {
            initialize : function () {
                if (!this.passTargetToNext) return

                var me          = this
                var prevNext    = this.next

                this.next       = function () {
                    prevNext.call(this, me.normalizedTarget);
                }
            }
        },

        methods : {

            BUILD : function (config) {
                // allow "el" as synonym for "target"
                if (config.el && !config.target) config.target = config.el

                return config
            },

            getTarget : function () {
                if (this.cachedTarget) return this.cachedTarget

                var test        = this.test;
                var target      = this.target || test.getCursorPagePosition()

                if (test.typeOf(target) === 'Function') target = target.call(test, this);

                this.normalizedTarget   = test.normalizeActionTarget(target, true)

                return this.cachedTarget   = target
            }
        }
    });