/*

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

*/
/**
 @class Siesta.Recorder.Role.CanRecordScroll

 A mixin, providing the feature of recording the "scroll" event.

 */
Role('Siesta.Recorder.Role.CanRecordScroll', {

    has : {
        /**
         * @cfg {Boolean} recordScroll Set this option to `true` to enable recording of `scroll` events.
         * This even occurs, when some element on the page, or the page itself, is scrolled by user.
         * Several consequent `scroll` events on the same element will be merged into one `scrollTo` action.
         *
         * Note, that when replaying scripts, recorded with this option enabled, you might want to disable
         * the {@link Siesta.Project.Browser#autoScrollElementsIntoView autoScrollElementsIntoView} option,
         * as it will be interfering with the recorded actions.
         */
        recordScroll        : false
    },

    override : {

        initialize : function () {
            var me = this;

            me.SUPERARG(arguments);

            me.onScrollEvent    = me.onScrollEvent.bind(me);
        },


        onStart : function () {
            this.SUPERARG(arguments);

            var win = this.window;
            var doc = win.document;

            // Observe scroll events
            if (this.recordScroll) {
                doc.addEventListener('scroll', this.onScrollEvent, true);
            }
        },


        onStop : function () {
            this.SUPERARG(arguments);

            var win = this.window;
            var doc = win.document;

            if (this.recordScroll) {
                doc.removeEventListener('scroll', this.onScrollEvent, true);
            }
        },

        // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign
        sign : Math.sign || function(x) {
            // If x is NaN, the result is NaN.
            // If x is -0, the result is -0.
            // If x is +0, the result is +0.
            // If x is negative and not -0, the result is -1.
            // If x is positive and not +0, the result is +1.
            return ((x > 0) - (x < 0)) || +x;
        },

        isSameScrollDirection : function (scrollEvent) {
            var me         = this;
            var lastAction = this.getLastAction()
            var win        = me.window
            var doc        = win.document
            var target     = scrollEvent.target;

            if (target === doc) target = this.getElForPageScroll(win);

            var xSign = this.sign(target.scrollLeft - lastAction.value[0]);
            var ySign = this.sign(target.scrollTop - lastAction.value[1]);

            return lastAction.xSign === this.sign(target.scrollLeft - lastAction.value[0]) &&
             lastAction.ySign === this.sign(target.scrollTop - lastAction.value[1]);
        },

        onScrollEvent : function (scrollEvent) {
            var me     = this;
            var target = scrollEvent.target;

            var win = me.window
            var doc = win.document

            var lastAction = this.getLastAction()

            // note that "target" here is unchanged and can be a document
            if (lastAction && lastAction.action === 'scrollTo' && target === lastAction.sourceEvent.target && (typeof lastAction.xSign !== 'number' || this.isSameScrollDirection(scrollEvent))) {
                // repeated scroll on the same target
                if (target === doc) target = this.getElForPageScroll(win);

                lastAction.xSign = this.sign(target.scrollLeft - lastAction.value[0]);
                lastAction.ySign = this.sign(target.scrollTop - lastAction.value[1]);

                lastAction.value[0] = target.scrollLeft;
                lastAction.value[1] = target.scrollTop;

                this.fireEvent('actionupdate', lastAction)
            } else {
                var isPageScroll;
                // scroll on the new target

                // page scroll events are fired with document as target, switch it to use body element
                if (target === doc) {
                    isPageScroll = true;
                    target       = this.getElForPageScroll(win);
                }

                var cursorPosition  = this.cursorPosition

                // For page scroll we have no way of knowing if it was triggered by direct scrolling or side effect
                // So always record it
                if (cursorPosition.length === 2 && !isPageScroll) {
                    var viewportX       = this.pageXtoViewportX(cursorPosition[ 0 ], win)
                    var viewportY       = this.pageYtoViewportY(cursorPosition[ 1 ], win)

                    // Ignore scroll if cursor is outside of scroll target
                    // we consider such scrolls as happened because of side-effect of some other user action
                    if (!this.isPointWithinElement(viewportX, viewportY, target)) return
                }

                this.addAction({
                    action          : 'scrollTo',

                    target          : this.getPossibleTargets(scrollEvent, false, target),
                    value           : [ target.scrollLeft, target.scrollTop ],

                    sourceEvent     : Siesta.Recorder.Event.fromDomEvent(scrollEvent)
                })
            }
        }
    }
});