/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2018 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

var Class = require('../utils/Class');

/**
 * @classdesc
 * A MultiFile is a special kind of parent that contains two, or more, Files as children and looks after
 * the loading and processing of them all. It is commonly extended and used as a base class for file types such as AtlasJSON or BitmapFont.
 *
 * You shouldn't create an instance of a MultiFile directly, but should extend it with your own class, setting a custom type and processing methods.
 *
 * @class MultiFile
 * @memberof Phaser.Loader
 * @constructor
 * @since 3.7.0
 *
 * @param {Phaser.Loader.LoaderPlugin} loader - The Loader that is going to load this File.
 * @param {string} type - The file type string for sorting within the Loader.
 * @param {string} key - The key of the file within the loader.
 * @param {Phaser.Loader.File[]} files - An array of Files that make-up this MultiFile.
 */
var MultiFile = new Class({

    initialize:

    function MultiFile (loader, type, key, files)
    {
        /**
         * A reference to the Loader that is going to load this file.
         *
         * @name Phaser.Loader.MultiFile#loader
         * @type {Phaser.Loader.LoaderPlugin}
         * @since 3.7.0
         */
        this.loader = loader;

        /**
         * The file type string for sorting within the Loader.
         *
         * @name Phaser.Loader.MultiFile#type
         * @type {string}
         * @since 3.7.0
         */
        this.type = type;

        /**
         * Unique cache key (unique within its file type)
         *
         * @name Phaser.Loader.MultiFile#key
         * @type {string}
         * @since 3.7.0
         */
        this.key = key;

        /**
         * Array of files that make up this MultiFile.
         *
         * @name Phaser.Loader.MultiFile#files
         * @type {Phaser.Loader.File[]}
         * @since 3.7.0
         */
        this.files = files;

        /**
         * The completion status of this MultiFile.
         *
         * @name Phaser.Loader.MultiFile#complete
         * @type {boolean}
         * @default false
         * @since 3.7.0
         */
        this.complete = false;

        /**
         * The number of files to load.
         *
         * @name Phaser.Loader.MultiFile#pending
         * @type {integer}
         * @since 3.7.0
         */

        this.pending = files.length;

        /**
         * The number of files that failed to load.
         *
         * @name Phaser.Loader.MultiFile#failed
         * @type {integer}
         * @default 0
         * @since 3.7.0
         */
        this.failed = 0;

        /**
         * A storage container for transient data that the loading files need.
         *
         * @name Phaser.Loader.MultiFile#config
         * @type {any}
         * @since 3.7.0
         */
        this.config = {};

        //  Link the files
        for (var i = 0; i < files.length; i++)
        {
            files[i].multiFile = this;
        }
    },

    /**
     * Checks if this MultiFile is ready to process its children or not.
     *
     * @method Phaser.Loader.MultiFile#isReadyToProcess
     * @since 3.7.0
     *
     * @return {boolean} `true` if all children of this MultiFile have loaded, otherwise `false`.
     */
    isReadyToProcess: function ()
    {
        return (this.pending === 0 && this.failed === 0 && !this.complete);
    },

    /**
     * Adds another child to this MultiFile, increases the pending count and resets the completion status.
     *
     * @method Phaser.Loader.MultiFile#addToMultiFile
     * @since 3.7.0
     *
     * @param {Phaser.Loader.File} files - The File to add to this MultiFile.
     *
     * @return {Phaser.Loader.MultiFile} This MultiFile instance.
     */
    addToMultiFile: function (file)
    {
        this.files.push(file);

        file.multiFile = this;

        this.pending++;

        this.complete = false;

        return this;
    },

    /**
     * Called by each File when it finishes loading.
     *
     * @method Phaser.Loader.MultiFile#onFileComplete
     * @since 3.7.0
     *
     * @param {Phaser.Loader.File} file - The File that has completed processing.
     */
    onFileComplete: function (file)
    {
        var index = this.files.indexOf(file);

        if (index !== -1)
        {
            this.pending--;
        }
    },

    /**
     * Called by each File that fails to load.
     *
     * @method Phaser.Loader.MultiFile#onFileFailed
     * @since 3.7.0
     *
     * @param {Phaser.Loader.File} file - The File that has failed to load.
     */
    onFileFailed: function (file)
    {
        var index = this.files.indexOf(file);

        if (index !== -1)
        {
            this.failed++;
        }
    }

});

module.exports = MultiFile;


/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2018 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

var Class = require('../utils/Class');
var CONST = require('./const');
var EventEmitter = require('eventemitter3');
var Keyboard = require('./keyboard/KeyboardManager');
var Mouse = require('./mouse/MouseManager');
var Pointer = require('./Pointer');
var Rectangle = require('../geom/rectangle/Rectangle');
var Touch = require('./touch/TouchManager');
var TransformMatrix = require('../gameobjects/components/TransformMatrix');
var TransformXY = require('../math/TransformXY');

/**
 * @classdesc
 * The Input Manager is responsible for handling the pointer related systems in a single Phaser Game instance.
 *
 * Based on the Game Config it will create handlers for mouse and touch support.
 *
 * Keyboard and Gamepad are plugins, handled directly by the InputPlugin class.
 *
 * It then manages the event queue, pointer creation and general hit test related operations.
 *
 * You rarely need to interact with the Input Manager directly, and as such, all of its properties and methods
 * should be considered private. Instead, you should use the Input Plugin, which is a Scene level system, responsible
 * for dealing with all input events for a Scene.
 *
 * @class InputManager
 * @memberof Phaser.Input
 * @constructor
 * @since 3.0.0
 *
 * @param {Phaser.Game} game - The Game instance that owns the Input Manager.
 * @param {object} config - The Input Configuration object, as set in the Game Config.
 */
var InputManager = new Class({

    initialize:

    function InputManager (game, config)
    {
        /**
         * The Game instance that owns the Input Manager.
         * A Game only maintains on instance of the Input Manager at any time.
         *
         * @name Phaser.Input.InputManager#game
         * @type {Phaser.Game}
         * @readonly
         * @since 3.0.0
         */
        this.game = game;

        /**
         * The Canvas that is used for all DOM event input listeners.
         *
         * @name Phaser.Input.InputManager#canvas
         * @type {HTMLCanvasElement}
         * @since 3.0.0
         */
        this.canvas;

        /**
         * The Game Configuration object, as set during the game boot.
         *
         * @name Phaser.Input.InputManager#config
         * @type {Phaser.Boot.Config}
         * @since 3.0.0
         */
        this.config = config;

        /**
         * If set, the Input Manager will run its update loop every frame.
         *
         * @name Phaser.Input.InputManager#enabled
         * @type {boolean}
         * @default true
         * @since 3.0.0
         */
        this.enabled = true;

        /**
         * The Event Emitter instance that the Input Manager uses to emit events from.
         *
         * @name Phaser.Input.InputManager#events
         * @type {Phaser.Events.EventEmitter}
         * @since 3.0.0
         */
        this.events = new EventEmitter();

        /**
         * A standard FIFO queue for the native DOM events waiting to be handled by the Input Manager.
         *
         * @name Phaser.Input.InputManager#queue
         * @type {array}
         * @default []
         * @since 3.0.0
         */
        this.queue = [];

        /**
         * DOM Callbacks container.
         *
         * @name Phaser.Input.InputManager#domCallbacks
         * @private
         * @type {object}
         * @since 3.10.0
         */
        this.domCallbacks = { up: [], down: [], move: [], upOnce: [], downOnce: [], moveOnce: [] };

        /**
         * Are any mouse or touch pointers currently over the game canvas?
         * This is updated automatically by the canvas over and out handlers.
         *
         * @name Phaser.Input.InputManager#isOver
         * @type {boolean}
         * @readonly
         * @since 3.16.0
         */
        this.isOver = true;

        /**
         * isOver state change property.
         *
         * @name Phaser.Input.InputManager#_emitIsOverEvent
         * @type {boolean}
         * @private
         * @since 3.16.0
         */
        this._emitIsOverEvent = false;

        /**
         * Are there any up callbacks defined?
         *
         * @name Phaser.Input.InputManager#_hasUpCallback
         * @private
         * @type {boolean}
         * @since 3.10.0
         */
        this._hasUpCallback = false;

        /**
         * Are there any down callbacks defined?
         *
         * @name Phaser.Input.InputManager#_hasDownCallback
         * @private
         * @type {boolean}
         * @since 3.10.0
         */
        this._hasDownCallback = false;

        /**
         * Are there any move callbacks defined?
         *
         * @name Phaser.Input.InputManager#_hasMoveCallback
         * @private
         * @type {boolean}
         * @since 3.10.0
         */
        this._hasMoveCallback = false;

        /**
         * Is a custom cursor currently set? (desktop only)
         *
         * @name Phaser.Input.InputManager#_customCursor
         * @private
         * @type {string}
         * @since 3.10.0
         */
        this._customCursor = '';

        /**
         * Custom cursor tracking value.
         *
         * 0 - No change.
         * 1 - Set new cursor.
         * 2 - Reset cursor.
         *
         * @name Phaser.Input.InputManager#_setCursor
         * @private
         * @type {integer}
         * @since 3.10.0
         */
        this._setCursor = 0;

        /**
         * The default CSS cursor to be used when interacting with your game.
         *
         * See the `setDefaultCursor` method for more details.
         *
         * @name Phaser.Input.InputManager#defaultCursor
         * @type {string}
         * @since 3.10.0
         */
        this.defaultCursor = '';

        /**
         * A reference to the Keyboard Manager class, if enabled via the `input.keyboard` Game Config property.
         *
         * @name Phaser.Input.InputManager#keyboard
         * @type {?Phaser.Input.Keyboard.KeyboardManager}
         * @since 3.16.0
         */
        this.keyboard = (config.inputKeyboard) ? new Keyboard(this) : null;

        /**
         * A reference to the Mouse Manager class, if enabled via the `input.mouse` Game Config property.
         *
         * @name Phaser.Input.InputManager#mouse
         * @type {?Phaser.Input.Mouse.MouseManager}
         * @since 3.0.0
         */
        this.mouse = (config.inputMouse) ? new Mouse(this) : null;

        /**
         * A reference to the Touch Manager class, if enabled via the `input.touch` Game Config property.
         *
         * @name Phaser.Input.InputManager#touch
         * @type {Phaser.Input.Touch.TouchManager}
         * @since 3.0.0
         */
        this.touch = (config.inputTouch) ? new Touch(this) : null;

        /**
         * An array of Pointers that have been added to the game.
         * The first entry is reserved for the Mouse Pointer, the rest are Touch Pointers.
         *
         * By default there is 1 touch pointer enabled. If you need more use the `addPointer` method to start them,
         * or set the `input.activePointers` property in the Game Config.
         *
         * @name Phaser.Input.InputManager#pointers
         * @type {Phaser.Input.Pointer[]}
         * @since 3.10.0
         */
        this.pointers = [];

        /**
         * The number of touch objects activated and being processed each update.
         *
         * You can change this by either calling `addPointer` at run-time, or by
         * setting the `input.activePointers` property in the Game Config.
         *
         * @name Phaser.Input.InputManager#pointersTotal
         * @type {integer}
         * @readonly
         * @since 3.10.0
         */
        this.pointersTotal = config.inputActivePointers;

        if (config.inputTouch && this.pointersTotal === 1)
        {
            this.pointersTotal = 2;
        }

        for (var i = 0; i <= this.pointersTotal; i++)
        {
            var pointer = new Pointer(this, i);

            pointer.smoothFactor = config.inputSmoothFactor;

            this.pointers.push(pointer);
        }

        /**
         * The mouse has its own unique Pointer object, which you can reference directly if making a _desktop specific game_.
         * If you are supporting both desktop and touch devices then do not use this property, instead use `activePointer`
         * which will always map to the most recently interacted pointer.
         *
         * @name Phaser.Input.InputManager#mousePointer
         * @type {?Phaser.Input.Pointer}
         * @since 3.10.0
         */
        this.mousePointer = (config.inputMouse) ? this.pointers[0] : null;

        /**
         * The most recently active Pointer object.
         *
         * If you've only 1 Pointer in your game then this will accurately be either the first finger touched, or the mouse.
         *
         * If your game doesn't need to support multi-touch then you can safely use this property in all of your game
         * code and it will adapt to be either the mouse or the touch, based on device.
         *
         * @name Phaser.Input.InputManager#activePointer
         * @type {Phaser.Input.Pointer}
         * @since 3.0.0
         */
        this.activePointer = this.pointers[0];

        /**
         * Reset every frame. Set to `true` if any of the Pointers are dirty this frame.
         *
         * @name Phaser.Input.InputManager#dirty
         * @type {boolean}
         * @since 3.10.0
         */
        this.dirty = false;

        /**
         * The Scale factor being applied to input coordinates.
         *
         * @name Phaser.Input.InputManager#scale
         * @type { { x:number, y:number } }
         * @since 3.0.0
         */
        this.scale = { x: 1, y: 1 };

        /**
         * If the top-most Scene in the Scene List receives an input it will stop input from
         * propagating any lower down the scene list, i.e. if you have a UI Scene at the top
         * and click something on it, that click will not then be passed down to any other
         * Scene below. Disable this to have input events passed through all Scenes, all the time.
         *
         * @name Phaser.Input.InputManager#globalTopOnly
         * @type {boolean}
         * @default true
         * @since 3.0.0
         */
        this.globalTopOnly = true;

        /**
         * An internal flag that controls if the Input Manager will ignore or process native DOM events this frame.
         * Set via the InputPlugin.stopPropagation method.
         *
         * @name Phaser.Input.InputManager#ignoreEvents
         * @type {boolean}
         * @default false
         * @since 3.0.0
         */
        this.ignoreEvents = false;

        /**
         * The bounds of the Input Manager, used for pointer hit test calculations.
         *
         * @name Phaser.Input.InputManager#bounds
         * @type {Phaser.Geom.Rectangle}
         * @since 3.0.0
         */
        this.bounds = new Rectangle();

        /**
         * A re-cycled point-like object to store hit test values in.
         *
         * @name Phaser.Input.InputManager#_tempPoint
         * @type {{x:number,y:number}}
         * @private
         * @since 3.0.0
         */
        this._tempPoint = { x: 0, y: 0 };

        /**
         * A re-cycled array to store hit results in.
         *
         * @name Phaser.Input.InputManager#_tempHitTest
         * @type {array}
         * @private
         * @default []
         * @since 3.0.0
         */
        this._tempHitTest = [];

        /**
         * A re-cycled matrix used in hit test calculations.
         *
         * @name Phaser.Input.InputManager#_tempMatrix
         * @type {Phaser.GameObjects.Components.TransformMatrix}
         * @private
         * @since 3.4.0
         */
        this._tempMatrix = new TransformMatrix();

        /**
         * A re-cycled matrix used in hit test calculations.
         *
         * @name Phaser.Input.InputManager#_tempMatrix2
         * @type {Phaser.GameObjects.Components.TransformMatrix}
         * @private
         * @since 3.12.0
         */
        this._tempMatrix2 = new TransformMatrix();

        game.events.once('boot', this.boot, this);
    },

    /**
     * The Boot handler is called by Phaser.Game when it first starts up.
     * The renderer is available by now.
     *
     * @method Phaser.Input.InputManager#boot
     * @protected
     * @since 3.0.0
     */
    boot: function ()
    {
        this.canvas = this.game.canvas;

        this.updateBounds();

        this.events.emit('boot');

        this.game.events.on('prestep', this.update, this);
        this.game.events.on('poststep', this.postUpdate, this);
        this.game.events.once('destroy', this.destroy, this);
    },

    /**
     * Updates the Input Manager bounds rectangle to match the bounding client rectangle of the
     * canvas element being used to track input events.
     *
     * @method Phaser.Input.InputManager#updateBounds
     * @since 3.0.0
     */
    updateBounds: function ()
    {
        var bounds = this.bounds;

        var clientRect = this.canvas.getBoundingClientRect();

        bounds.x = clientRect.left + window.pageXOffset - document.documentElement.clientLeft;
        bounds.y = clientRect.top + window.pageYOffset - document.documentElement.clientTop;
        bounds.width = clientRect.width;
        bounds.height = clientRect.height;
    },

    /**
     * Resizes the Input Manager internal values, including the bounds and scale factor.
     *
     * @method Phaser.Input.InputManager#resize
     * @since 3.2.0
     */
    resize: function ()
    {
        this.updateBounds();

        //  Game config size
        var gw = this.game.config.width;
        var gh = this.game.config.height;

        //  Actual canvas size
        var bw = this.bounds.width;
        var bh = this.bounds.height;

        //  Scale factor
        this.scale.x = gw / bw;
        this.scale.y = gh / bh;
    },

    /**
     * Internal canvas state change, called automatically by the Mouse Manager.
     *
     * @method Phaser.Input.InputManager#setCanvasOver
     * @private
     * @since 3.16.0
     *
     * @param {number} event - The DOM Event.
     */
    setCanvasOver: function (event)
    {
        this.isOver = true;

        this._emitIsOverEvent = event;
    },

    /**
     * Internal canvas state change, called automatically by the Mouse Manager.
     *
     * @method Phaser.Input.InputManager#setCanvasOut
     * @private
     * @since 3.16.0
     *
     * @param {number} event - The DOM Event.
     */
    setCanvasOut: function (event)
    {
        this.isOver = false;

        this._emitIsOverEvent = event;
    },

    /**
     * Internal update loop, called automatically by the Game Step.
     *
     * @method Phaser.Input.InputManager#update
     * @private
     * @since 3.0.0
     *
     * @param {number} time - The time stamp value of this game step.
     */
    update: function (time)
    {
        var i;

        this._setCursor = 0;

        this.events.emit('update');

        this.ignoreEvents = false;

        this.dirty = false;

        var len = this.queue.length;

        var pointers = this.pointers;

        for (i = 0; i < this.pointersTotal; i++)
        {
            pointers[i].reset(time);
        }

        if (!this.enabled || len === 0)
        {
            for (i = 0; i < this.pointersTotal; i++)
            {
                pointers[i].updateMotion();
            }

            return;
        }

        this.dirty = true;

        this.updateBounds();

        this.scale.x = this.game.config.width / this.bounds.width;
        this.scale.y = this.game.config.height / this.bounds.height;

        //  Clears the queue array, and also means we don't work on array data that could potentially
        //  be modified during the processing phase
        var queue = this.queue.splice(0, len);
        var mouse = this.mousePointer;

        //  Process the event queue, dispatching all of the events that have stored up
        for (i = 0; i < len; i += 2)
        {
            var type = queue[i];
            var event = queue[i + 1];

            switch (type)
            {
                case CONST.MOUSE_DOWN:
                    mouse.down(event, time);
                    break;

                case CONST.MOUSE_MOVE:
                    mouse.move(event, time);
                    break;

                case CONST.MOUSE_UP:
                    mouse.up(event, time);
                    break;

                case CONST.TOUCH_START:
                    this.startPointer(event, time);
                    break;

                case CONST.TOUCH_MOVE:
                    this.updatePointer(event, time);
                    break;

                case CONST.TOUCH_END:
                    this.stopPointer(event, time);
                    break;

                case CONST.TOUCH_CANCEL:
                    this.cancelPointer(event, time);
                    break;

                case CONST.POINTER_LOCK_CHANGE:
                    this.events.emit('pointerlockchange', event, this.mouse.locked);
                    break;
            }
        }

        for (i = 0; i < this.pointersTotal; i++)
        {
            pointers[i].updateMotion();
        }
    },

    /**
     * Internal post-update, called automatically by the Game step.
     *
     * @method Phaser.Input.InputManager#postUpdate
     * @private
     * @since 3.10.0
     */
    postUpdate: function ()
    {
        if (this._setCursor === 1)
        {
            this.canvas.style.cursor = this._customCursor;
        }
        else if (this._setCursor === 2)
        {
            this.canvas.style.cursor = this.defaultCursor;
        }

        //  Reset the isOver event
        this._emitIsOverEvent = null;
    },

    /**
     * Tells the Input system to set a custom cursor.
     *
     * This cursor will be the default cursor used when interacting with the game canvas.
     *
     * If an Interactive Object also sets a custom cursor, this is the cursor that is reset after its use.
     *
     * Any valid CSS cursor value is allowed, including paths to image files, i.e.:
     *
     * ```javascript
     * this.input.setDefaultCursor('url(assets/cursors/sword.cur), pointer');
     * ```
     *
     * Please read about the differences between browsers when it comes to the file formats and sizes they support:
     *
     * https://developer.mozilla.org/en-US/docs/Web/CSS/cursor
     * https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_User_Interface/Using_URL_values_for_the_cursor_property
     *
     * It's up to you to pick a suitable cursor format that works across the range of browsers you need to support.
     *
     * @method Phaser.Input.InputManager#setDefaultCursor
     * @since 3.10.0
     *
     * @param {string} cursor - The CSS to be used when setting the default cursor.
     */
    setDefaultCursor: function (cursor)
    {
        this.defaultCursor = cursor;

        if (this.canvas.style.cursor !== cursor)
        {
            this.canvas.style.cursor = cursor;
        }
    },

    /**
     * Called by the InputPlugin when processing over and out events.
     *
     * Tells the Input Manager to set a custom cursor during its postUpdate step.
     *
     * https://developer.mozilla.org/en-US/docs/Web/CSS/cursor
     *
     * @method Phaser.Input.InputManager#setCursor
     * @private
     * @since 3.10.0
     *
     * @param {Phaser.Input.InteractiveObject} interactiveObject - The Interactive Object that called this method.
     */
    setCursor: function (interactiveObject)
    {
        if (interactiveObject.cursor)
        {
            this._setCursor = 1;
            this._customCursor = interactiveObject.cursor;
        }
    },

    /**
     * Called by the InputPlugin when processing over and out events.
     *
     * Tells the Input Manager to clear the hand cursor, if set, during its postUpdate step.
     *
     * @method Phaser.Input.InputManager#resetCursor
     * @private
     * @since 3.10.0
     *
     * @param {Phaser.Input.InteractiveObject} interactiveObject - The Interactive Object that called this method.
     */
    resetCursor: function (interactiveObject)
    {
        if (interactiveObject.cursor)
        {
            this._setCursor = 2;
        }
    },

    //  event.targetTouches = list of all touches on the TARGET ELEMENT (i.e. game dom element)
    //  event.touches = list of all touches on the ENTIRE DOCUMENT, not just the target element
    //  event.changedTouches = the touches that CHANGED in this event, not the total number of them

    /**
     * Called by the main update loop when a Touch Start Event is received.
     *
     * @method Phaser.Input.InputManager#startPointer
     * @private
     * @since 3.10.0
     *
     * @param {TouchEvent} event - The native DOM event to be processed.
     * @param {number} time - The time stamp value of this game step.
     */
    startPointer: function (event, time)
    {
        var pointers = this.pointers;

        for (var c = 0; c < event.changedTouches.length; c++)
        {
            var changedTouch = event.changedTouches[c];

            for (var i = 1; i < this.pointersTotal; i++)
            {
                var pointer = pointers[i];

                if (!pointer.active)
                {
                    pointer.touchstart(changedTouch, time);
                    this.activePointer = pointer;
                    break;
                }
            }
        }
    },

    /**
     * Called by the main update loop when a Touch Move Event is received.
     *
     * @method Phaser.Input.InputManager#updatePointer
     * @private
     * @since 3.10.0
     *
     * @param {TouchEvent} event - The native DOM event to be processed.
     * @param {number} time - The time stamp value of this game step.
     */
    updatePointer: function (event, time)
    {
        var pointers = this.pointers;

        for (var c = 0; c < event.changedTouches.length; c++)
        {
            var changedTouch = event.changedTouches[c];

            for (var i = 1; i < this.pointersTotal; i++)
            {
                var pointer = pointers[i];

                if (pointer.active && pointer.identifier === changedTouch.identifier)
                {
                    pointer.touchmove(changedTouch, time);
                    this.activePointer = pointer;
                    break;
                }
            }
        }
    },

    //  For touch end its a list of the touch points that have been removed from the surface
    //  https://developer.mozilla.org/en-US/docs/DOM/TouchList
    //  event.changedTouches = the touches that CHANGED in this event, not the total number of them

    /**
     * Called by the main update loop when a Touch End Event is received.
     *
     * @method Phaser.Input.InputManager#stopPointer
     * @private
     * @since 3.10.0
     *
     * @param {TouchEvent} event - The native DOM event to be processed.
     * @param {number} time - The time stamp value of this game step.
     */
    stopPointer: function (event, time)
    {
        var pointers = this.pointers;

        for (var c = 0; c < event.changedTouches.length; c++)
        {
            var changedTouch = event.changedTouches[c];

            for (var i = 1; i < this.pointersTotal; i++)
            {
                var pointer = pointers[i];

                if (pointer.active && pointer.identifier === changedTouch.identifier)
                {
                    pointer.touchend(changedTouch, time);
                    break;
                }
            }
        }
    },

    /**
     * Called by the main update loop when a Touch Cancel Event is received.
     *
     * @method Phaser.Input.InputManager#cancelPointer
     * @private
     * @since 3.15.0
     *
     * @param {TouchEvent} event - The native DOM event to be processed.
     * @param {number} time - The time stamp value of this game step.
     */
    cancelPointer: function (event, time)
    {
        var pointers = this.pointers;

        for (var c = 0; c < event.changedTouches.length; c++)
        {
            var changedTouch = event.changedTouches[c];

            for (var i = 1; i < this.pointersTotal; i++)
            {
                var pointer = pointers[i];

                if (pointer.active && pointer.identifier === changedTouch.identifier)
                {
                    pointer.touchend(changedTouch, time);
                    break;
                }
            }
        }
    },

    /**
     * Adds new Pointer objects to the Input Manager.
     *
     * By default Phaser creates 2 pointer objects: `mousePointer` and `pointer1`.
     *
     * You can create more either by calling this method, or by setting the `input.activePointers` property
     * in the Game Config, up to a maximum of 10 pointers.
     *
     * The first 10 pointers are available via the `InputPlugin.pointerX` properties, once they have been added
     * via this method.
     *
     * @method Phaser.Input.InputManager#addPointer
     * @since 3.10.0
     *
     * @param {integer} [quantity=1] The number of new Pointers to create. A maximum of 10 is allowed in total.
     *
     * @return {Phaser.Input.Pointer[]} An array containing all of the new Pointer objects that were created.
     */
    addPointer: function (quantity)
    {
        if (quantity === undefined) { quantity = 1; }

        var output = [];

        if (this.pointersTotal + quantity > 10)
        {
            quantity = 10 - this.pointersTotal;
        }

        for (var i = 0; i < quantity; i++)
        {
            var id = this.pointers.length;

            var pointer = new Pointer(this, id);

            pointer.smoothFactor = this.config.inputSmoothFactor;

            this.pointers.push(pointer);

            this.pointersTotal++;

            output.push(pointer);
        }

        return output;
    },

    /**
     * Process any pending DOM callbacks.
     *
     * @method Phaser.Input.InputManager#processDomCallbacks
     * @private
     * @since 3.10.0
     *
     * @param {array} once - The isOnce callbacks to invoke.
     * @param {array} every - The every frame callbacks to invoke.
     * @param {any} event - The native DOM event that is passed to the callbacks.
     *
     * @return {boolean} `true` if there are callbacks still in the list, otherwise `false`.
     */
    processDomCallbacks: function (once, every, event)
    {
        var i = 0;

        for (i = 0; i < once.length; i++)
        {
            once[i](event);
        }

        for (i = 0; i < every.length; i++)
        {
            every[i](event);
        }

        once = [];

        return (every.length > 0);
    },

    /**
     * Queues a touch start event, as passed in by the TouchManager.
     * Also dispatches any DOM callbacks for this event.
     *
     * @method Phaser.Input.InputManager#queueTouchStart
     * @private
     * @since 3.10.0
     *
     * @param {TouchEvent} event - The native DOM Touch event.
     */
    queueTouchStart: function (event)
    {
        this.queue.push(CONST.TOUCH_START, event);

        if (this._hasDownCallback)
        {
            var callbacks = this.domCallbacks;

            this._hasDownCallback = this.processDomCallbacks(callbacks.downOnce, callbacks.down, event);
        }
    },

    /**
     * Queues a touch move event, as passed in by the TouchManager.
     * Also dispatches any DOM callbacks for this event.
     *
     * @method Phaser.Input.InputManager#queueTouchMove
     * @private
     * @since 3.10.0
     *
     * @param {TouchEvent} event - The native DOM Touch event.
     */
    queueTouchMove: function (event)
    {
        this.queue.push(CONST.TOUCH_MOVE, event);

        if (this._hasMoveCallback)
        {
            var callbacks = this.domCallbacks;

            this._hasMoveCallback = this.processDomCallbacks(callbacks.moveOnce, callbacks.move, event);
        }
    },

    /**
     * Queues a touch end event, as passed in by the TouchManager.
     * Also dispatches any DOM callbacks for this event.
     *
     * @method Phaser.Input.InputManager#queueTouchEnd
     * @private
     * @since 3.10.0
     *
     * @param {TouchEvent} event - The native DOM Touch event.
     */
    queueTouchEnd: function (event)
    {
        this.queue.push(CONST.TOUCH_END, event);

        if (this._hasUpCallback)
        {
            var callbacks = this.domCallbacks;

            this._hasUpCallback = this.processDomCallbacks(callbacks.upOnce, callbacks.up, event);
        }
    },

    /**
     * Queues a touch cancel event, as passed in by the TouchManager.
     * Also dispatches any DOM callbacks for this event.
     *
     * @method Phaser.Input.InputManager#queueTouchCancel
     * @private
     * @since 3.15.0
     *
     * @param {TouchEvent} event - The native DOM Touch event.
     */
    queueTouchCancel: function (event)
    {
        this.queue.push(CONST.TOUCH_CANCEL, event);
    },

    /**
     * Queues a mouse down event, as passed in by the MouseManager.
     * Also dispatches any DOM callbacks for this event.
     *
     * @method Phaser.Input.InputManager#queueMouseDown
     * @private
     * @since 3.10.0
     *
     * @param {MouseEvent} event - The native DOM Mouse event.
     */
    queueMouseDown: function (event)
    {
        this.queue.push(CONST.MOUSE_DOWN, event);

        if (this._hasDownCallback)
        {
            var callbacks = this.domCallbacks;

            this._hasDownCallback = this.processDomCallbacks(callbacks.downOnce, callbacks.down, event);
        }
    },

    /**
     * Queues a mouse move event, as passed in by the MouseManager.
     * Also dispatches any DOM callbacks for this event.
     *
     * @method Phaser.Input.InputManager#queueMouseMove
     * @private
     * @since 3.10.0
     *
     * @param {MouseEvent} event - The native DOM Mouse event.
     */
    queueMouseMove: function (event)
    {
        this.queue.push(CONST.MOUSE_MOVE, event);

        if (this._hasMoveCallback)
        {
            var callbacks = this.domCallbacks;

            this._hasMoveCallback = this.processDomCallbacks(callbacks.moveOnce, callbacks.move, event);
        }
    },

    /**
     * Queues a mouse up event, as passed in by the MouseManager.
     * Also dispatches any DOM callbacks for this event.
     *
     * @method Phaser.Input.InputManager#queueMouseUp
     * @private
     * @since 3.10.0
     *
     * @param {MouseEvent} event - The native DOM Mouse event.
     */
    queueMouseUp: function (event)
    {
        this.queue.push(CONST.MOUSE_UP, event);

        if (this._hasUpCallback)
        {
            var callbacks = this.domCallbacks;

            this._hasUpCallback = this.processDomCallbacks(callbacks.upOnce, callbacks.up, event);
        }
    },

    /**
     * Adds a callback to be invoked whenever the native DOM `mouseup` or `touchend` events are received.
     * By setting the `isOnce` argument you can control if the callback is called once,
     * or every time the DOM event occurs.
     *
     * Callbacks passed to this method are invoked _immediately_ when the DOM event happens,
     * within the scope of the DOM event handler. Therefore, they are considered as 'native'
     * from the perspective of the browser. This means they can be used for tasks such as
     * opening new browser windows, or anything which explicitly requires user input to activate.
     * However, as a result of this, they come with their own risks, and as such should not be used
     * for general game input, but instead be reserved for special circumstances.
     *
     * If all you're trying to do is execute a callback when a pointer is released, then
     * please use the internal Input event system instead.
     *
     * Please understand that these callbacks are invoked when the browser feels like doing so,
     * which may be entirely out of the normal flow of the Phaser Game Loop. Therefore, you should absolutely keep
     * Phaser related operations to a minimum in these callbacks. For example, don't destroy Game Objects,
     * change Scenes or manipulate internal systems, otherwise you run a very real risk of creating
     * heisenbugs (https://en.wikipedia.org/wiki/Heisenbug) that prove a challenge to reproduce, never mind
     * solve.
     *
     * @method Phaser.Input.InputManager#addUpCallback
     * @since 3.10.0
     *
     * @param {function} callback - The callback to be invoked on this dom event.
     * @param {boolean} [isOnce=true] - `true` if the callback will only be invoked once, `false` to call every time this event happens.
     *
     * @return {this} The Input Manager.
     */
    addUpCallback: function (callback, isOnce)
    {
        if (isOnce === undefined) { isOnce = true; }

        if (isOnce)
        {
            this.domCallbacks.upOnce.push(callback);
        }
        else
        {
            this.domCallbacks.up.push(callback);
        }

        this._hasUpCallback = true;

        return this;
    },

    /**
     * Adds a callback to be invoked whenever the native DOM `mousedown` or `touchstart` events are received.
     * By setting the `isOnce` argument you can control if the callback is called once,
     * or every time the DOM event occurs.
     *
     * Callbacks passed to this method are invoked _immediately_ when the DOM event happens,
     * within the scope of the DOM event handler. Therefore, they are considered as 'native'
     * from the perspective of the browser. This means they can be used for tasks such as
     * opening new browser windows, or anything which explicitly requires user input to activate.
     * However, as a result of this, they come with their own risks, and as such should not be used
     * for general game input, but instead be reserved for special circumstances.
     *
     * If all you're trying to do is execute a callback when a pointer is down, then
     * please use the internal Input event system instead.
     *
     * Please understand that these callbacks are invoked when the browser feels like doing so,
     * which may be entirely out of the normal flow of the Phaser Game Loop. Therefore, you should absolutely keep
     * Phaser related operations to a minimum in these callbacks. For example, don't destroy Game Objects,
     * change Scenes or manipulate internal systems, otherwise you run a very real risk of creating
     * heisenbugs (https://en.wikipedia.org/wiki/Heisenbug) that prove a challenge to reproduce, never mind
     * solve.
     *
     * @method Phaser.Input.InputManager#addDownCallback
     * @since 3.10.0
     *
     * @param {function} callback - The callback to be invoked on this dom event.
     * @param {boolean} [isOnce=true] - `true` if the callback will only be invoked once, `false` to call every time this event happens.
     *
     * @return {this} The Input Manager.
     */
    addDownCallback: function (callback, isOnce)
    {
        if (isOnce === undefined) { isOnce = true; }

        if (isOnce)
        {
            this.domCallbacks.downOnce.push(callback);
        }
        else
        {
            this.domCallbacks.down.push(callback);
        }

        this._hasDownCallback = true;

        return this;
    },

    /**
     * Adds a callback to be invoked whenever the native DOM `mousemove` or `touchmove` events are received.
     * By setting the `isOnce` argument you can control if the callback is called once,
     * or every time the DOM event occurs.
     *
     * Callbacks passed to this method are invoked _immediately_ when the DOM event happens,
     * within the scope of the DOM event handler. Therefore, they are considered as 'native'
     * from the perspective of the browser. This means they can be used for tasks such as
     * opening new browser windows, or anything which explicitly requires user input to activate.
     * However, as a result of this, they come with their own risks, and as such should not be used
     * for general game input, but instead be reserved for special circumstances.
     *
     * If all you're trying to do is execute a callback when a pointer is moved, then
     * please use the internal Input event system instead.
     *
     * Please understand that these callbacks are invoked when the browser feels like doing so,
     * which may be entirely out of the normal flow of the Phaser Game Loop. Therefore, you should absolutely keep
     * Phaser related operations to a minimum in these callbacks. For example, don't destroy Game Objects,
     * change Scenes or manipulate internal systems, otherwise you run a very real risk of creating
     * heisenbugs (https://en.wikipedia.org/wiki/Heisenbug) that prove a challenge to reproduce, never mind
     * solve.
     *
     * @method Phaser.Input.InputManager#addMoveCallback
     * @since 3.10.0
     *
     * @param {function} callback - The callback to be invoked on this dom event.
     * @param {boolean} [isOnce=false] - `true` if the callback will only be invoked once, `false` to call every time this event happens.
     *
     * @return {this} The Input Manager.
     */
    addMoveCallback: function (callback, isOnce)
    {
        if (isOnce === undefined) { isOnce = false; }

        if (isOnce)
        {
            this.domCallbacks.moveOnce.push(callback);
        }
        else
        {
            this.domCallbacks.move.push(callback);
        }

        this._hasMoveCallback = true;

        return this;
    },

    /**
     * Checks if the given Game Object should be considered as a candidate for input or not.
     *
     * Checks if the Game Object has an input component that is enabled, that it will render,
     * and finally, if it has a parent, that the parent parent, or any ancestor, is visible or not.
     *
     * @method Phaser.Input.InputManager#inputCandidate
     * @private
     * @since 3.10.0
     *
     * @param {Phaser.GameObjects.GameObject} gameObject - The Game Object to test.
     * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera which is being tested against.
     *
     * @return {boolean} `true` if the Game Object should be considered for input, otherwise `false`.
     */
    inputCandidate: function (gameObject, camera)
    {
        var input = gameObject.input;

        if (!input || !input.enabled || !gameObject.willRender(camera))
        {
            return false;
        }

        var visible = true;
        var parent = gameObject.parentContainer;

        if (parent)
        {
            do
            {
                if (!parent.willRender(camera))
                {
                    visible = false;
                    break;
                }

                parent = parent.parentContainer;

            } while (parent);
        }

        return visible;
    },

    /**
     * Performs a hit test using the given Pointer and camera, against an array of interactive Game Objects.
     *
     * The Game Objects are culled against the camera, and then the coordinates are translated into the local camera space
     * and used to determine if they fall within the remaining Game Objects hit areas or not.
     *
     * If nothing is matched an empty array is returned.
     *
     * This method is called automatically by InputPlugin.hitTestPointer and doesn't usually need to be invoked directly.
     *
     * @method Phaser.Input.InputManager#hitTest
     * @since 3.0.0
     *
     * @param {Phaser.Input.Pointer} pointer - The Pointer to test against.
     * @param {array} gameObjects - An array of interactive Game Objects to check.
     * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera which is being tested against.
     * @param {array} [output] - An array to store the results in. If not given, a new empty array is created.
     *
     * @return {array} An array of the Game Objects that were hit during this hit test.
     */
    hitTest: function (pointer, gameObjects, camera, output)
    {
        if (output === undefined) { output = this._tempHitTest; }

        var tempPoint = this._tempPoint;

        var csx = camera.scrollX;
        var csy = camera.scrollY;

        output.length = 0;

        var x = pointer.x;
        var y = pointer.y;

        if (camera.resolution !== 1)
        {
            x += camera._x;
            y += camera._y;
        }

        //  Stores the world point inside of tempPoint
        camera.getWorldPoint(x, y, tempPoint);

        pointer.worldX = tempPoint.x;
        pointer.worldY = tempPoint.y;

        var point = { x: 0, y: 0 };

        var matrix = this._tempMatrix;
        var parentMatrix = this._tempMatrix2;

        for (var i = 0; i < gameObjects.length; i++)
        {
            var gameObject = gameObjects[i];

            //  Checks if the Game Object can receive input (isn't being ignored by the camera, invisible, etc)
            //  and also checks all of its parents, if any
            if (!this.inputCandidate(gameObject, camera))
            {
                continue;
            }

            var px = tempPoint.x + (csx * gameObject.scrollFactorX) - csx;
            var py = tempPoint.y + (csy * gameObject.scrollFactorY) - csy;

            if (gameObject.parentContainer)
            {
                gameObject.getWorldTransformMatrix(matrix, parentMatrix);

                matrix.applyInverse(px, py, point);
            }
            else
            {
                TransformXY(px, py, gameObject.x, gameObject.y, gameObject.rotation, gameObject.scaleX, gameObject.scaleY, point);
            }

            if (this.pointWithinHitArea(gameObject, point.x, point.y))
            {
                output.push(gameObject);
            }
        }

        return output;
    },

    /**
     * Checks if the given x and y coordinate are within the hit area of the Game Object.
     *
     * This method assumes that the coordinate values have already been translated into the space of the Game Object.
     *
     * If the coordinates are within the hit area they are set into the Game Objects Input `localX` and `localY` properties.
     *
     * @method Phaser.Input.InputManager#pointWithinHitArea
     * @since 3.0.0
     *
     * @param {Phaser.GameObjects.GameObject} gameObject - The interactive Game Object to check against.
     * @param {number} x - The translated x coordinate for the hit test.
     * @param {number} y - The translated y coordinate for the hit test.
     *
     * @return {boolean} `true` if the coordinates were inside the Game Objects hit area, otherwise `false`.
     */
    pointWithinHitArea: function (gameObject, x, y)
    {
        //  Normalize the origin
        x += gameObject.displayOriginX;
        y += gameObject.displayOriginY;

        var input = gameObject.input;

        if (input && input.hitAreaCallback(input.hitArea, x, y, gameObject))
        {
            input.localX = x;
            input.localY = y;

            return true;
        }
        else
        {
            return false;
        }
    },

    /**
     * Checks if the given x and y coordinate are within the hit area of the Interactive Object.
     *
     * This method assumes that the coordinate values have already been translated into the space of the Interactive Object.
     *
     * If the coordinates are within the hit area they are set into the Interactive Objects Input `localX` and `localY` properties.
     *
     * @method Phaser.Input.InputManager#pointWithinInteractiveObject
     * @since 3.0.0
     *
     * @param {Phaser.Input.InteractiveObject} object - The Interactive Object to check against.
     * @param {number} x - The translated x coordinate for the hit test.
     * @param {number} y - The translated y coordinate for the hit test.
     *
     * @return {boolean} `true` if the coordinates were inside the Game Objects hit area, otherwise `false`.
     */
    pointWithinInteractiveObject: function (object, x, y)
    {
        if (!object.hitArea)
        {
            return false;
        }

        //  Normalize the origin
        x += object.gameObject.displayOriginX;
        y += object.gameObject.displayOriginY;

        object.localX = x;
        object.localY = y;

        return object.hitAreaCallback(object.hitArea, x, y, object);
    },

    /**
     * Transforms the pageX and pageY values of a Pointer into the scaled coordinate space of the Input Manager.
     *
     * @method Phaser.Input.InputManager#transformPointer
     * @since 3.10.0
     *
     * @param {Phaser.Input.Pointer} pointer - The Pointer to transform the values for.
     * @param {number} pageX - The Page X value.
     * @param {number} pageY - The Page Y value.
     * @param {boolean} wasMove - Are we transforming the Pointer from a move event, or an up / down event?
     */
    transformPointer: function (pointer, pageX, pageY, wasMove)
    {
        var p0 = pointer.position;
        var p1 = pointer.prevPosition;

        //  Store previous position
        p1.x = p0.x;
        p1.y = p0.y;

        //  Translate coordinates
        var x = (pageX - this.bounds.left) * this.scale.x;
        var y = (pageY - this.bounds.top) * this.scale.y;

        var a = pointer.smoothFactor;

        if (!wasMove || a === 0)
        {
            //  Set immediately
            p0.x = x;
            p0.y = y;
        }
        else
        {
            //  Apply smoothing
            p0.x = x * a + p1.x * (1 - a);
            p0.y = y * a + p1.y * (1 - a);
        }
    },

    /**
     * Transforms the pageX value into the scaled coordinate space of the Input Manager.
     *
     * @method Phaser.Input.InputManager#transformX
     * @since 3.0.0
     *
     * @param {number} pageX - The DOM pageX value.
     *
     * @return {number} The translated value.
     */
    transformX: function (pageX)
    {
        return (pageX - this.bounds.left) * this.scale.x;
    },

    /**
     * Transforms the pageY value into the scaled coordinate space of the Input Manager.
     *
     * @method Phaser.Input.InputManager#transformY
     * @since 3.0.0
     *
     * @param {number} pageY - The DOM pageY value.
     *
     * @return {number} The translated value.
     */
    transformY: function (pageY)
    {
        return (pageY - this.bounds.top) * this.scale.y;
    },

    /**
     * Returns the left offset of the Input bounds.
     *
     * @method Phaser.Input.InputManager#getOffsetX
     * @since 3.0.0
     *
     * @return {number} The left bounds value.
     */
    getOffsetX: function ()
    {
        return this.bounds.left;
    },

    /**
     * Returns the top offset of the Input bounds.
     *
     * @method Phaser.Input.InputManager#getOffsetY
     * @since 3.0.0
     *
     * @return {number} The top bounds value.
     */
    getOffsetY: function ()
    {
        return this.bounds.top;
    },

    /**
     * Returns the horizontal Input Scale value.
     *
     * @method Phaser.Input.InputManager#getScaleX
     * @since 3.0.0
     *
     * @return {number} The horizontal scale factor of the input.
     */
    getScaleX: function ()
    {
        return this.game.config.width / this.bounds.width;
    },

    /**
     * Returns the vertical Input Scale value.
     *
     * @method Phaser.Input.InputManager#getScaleY
     * @since 3.0.0
     *
     * @return {number} The vertical scale factor of the input.
     */
    getScaleY: function ()
    {
        return this.game.config.height / this.bounds.height;
    },

    /**
     * Destroys the Input Manager and all of its systems.
     *
     * There is no way to recover from doing this.
     *
     * @method Phaser.Input.InputManager#destroy
     * @since 3.0.0
     */
    destroy: function ()
    {
        this.events.removeAllListeners();

        if (this.keyboard)
        {
            this.keyboard.destroy();
        }

        if (this.mouse)
        {
            this.mouse.destroy();
        }

        if (this.touch)
        {
            this.touch.destroy();
        }

        for (var i = 0; i < this.pointers.length; i++)
        {
            this.pointers[i].destroy();
        }

        this.domCallbacks = {};
        this.pointers = [];
        this.queue = [];
        this._tempHitTest = [];
        this._tempMatrix.destroy();
        this.canvas = null;
        this.game = null;
    }

});

module.exports = InputManager;


/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2018 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

var GetBottom = require('../../bounds/GetBottom');
var GetLeft = require('../../bounds/GetLeft');
var SetBottom = require('../../bounds/SetBottom');
var SetLeft = require('../../bounds/SetLeft');

/**
 * Takes given Game Object and aligns it so that it is positioned in the bottom left of the other.
 *
 * @function Phaser.Display.Align.In.BottomLeft
 * @since 3.0.0
 *
 * @generic {Phaser.GameObjects.GameObject} G - [gameObject,$return]
 *
 * @param {Phaser.GameObjects.GameObject} gameObject - The Game Object that will be positioned.
 * @param {Phaser.GameObjects.GameObject} alignIn - The Game Object to base the alignment position on.
 * @param {number} [offsetX=0] - Optional horizontal offset from the position.
 * @param {number} [offsetY=0] - Optional vertical offset from the position.
 *
 * @return {Phaser.GameObjects.GameObject} The Game Object that was aligned.
 */
var BottomLeft = function (gameObject, alignIn, offsetX, offsetY)
{
    if (offsetX === undefined) { offsetX = 0; }
    if (offsetY === undefined) { offsetY = 0; }

    SetLeft(gameObject, GetLeft(alignIn) - offsetX);
    SetBottom(gameObject, GetBottom(alignIn) + offsetY);

    return gameObject;
};

module.exports = BottomLeft;


/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2018 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

//  Source object
//  The key as a string, or an array of keys, i.e. 'banner', or 'banner.hideBanner'
//  The default value to use if the key doesn't exist

/**
 * Retrieves a value from an object.
 *
 * @function Phaser.Utils.Objects.GetValue
 * @since 3.0.0
 *
 * @param {object} source - The object to retrieve the value from.
 * @param {string} key - The name of the property to retrieve from the object. If a property is nested, the names of its preceding properties should be separated by a dot (`.`) - `banner.hideBanner` would return the value of the `hideBanner` property from the object stored in the `banner` property of the `source` object.
 * @param {*} defaultValue - The value to return if the `key` isn't found in the `source` object.
 *
 * @return {*} The value of the requested key.
 */
var GetValue = function (source, key, defaultValue)
{
    if (!source || typeof source === 'number')
    {
        return defaultValue;
    }
    else if (source.hasOwnProperty(key))
    {
        return source[key];
    }
    else if (key.indexOf('.'))
    {
        var keys = key.split('.');
        var parent = source;
        var value = defaultValue;

        //  Use for loop here so we can break early
        for (var i = 0; i < keys.length; i++)
        {
            if (parent.hasOwnProperty(keys[i]))
            {
                //  Yes it has a key property, let's carry on down
                value = parent[keys[i]];

                parent = parent[keys[i]];
            }
            else
            {
                //  Can't go any further, so reset to default
                value = defaultValue;
                break;
            }
        }

        return value;
    }
    else
    {
        return defaultValue;
    }
};

module.exports = GetValue;


/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2018 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

/**
 * Replaces an element of the array with the new element.
 * The new element cannot already be a member of the array.
 * The array is modified in-place.
 *
 * @function Phaser.Utils.Array.Replace
 * @since 3.4.0
 *
 * @param {*} oldChild - The element in the array that will be replaced.
 * @param {*} newChild - The element to be inserted into the array at the position of `oldChild`.
 *
 * @return {boolean} Returns true if the oldChild was successfully replaced, otherwise returns false.
 */
var Replace = function (array, oldChild, newChild)
{
    var index1 = array.indexOf(oldChild);
    var index2 = array.indexOf(newChild);

    if (index1 !== -1 && index2 === -1)
    {
        array[index1] = newChild;

        return true;
    }
    else
    {
        return false;
    }
};

module.exports = Replace;


My thanks to the following for helping with the Phaser 3 Examples, Docs and TypeScript definitions, either by reporting errors, fixing them or helping author the docs:

## Version 3.16.0 / 3.16.1 - Ishikawa - 5th February 2019

Phaser 3.16 is a massive update. The single largest in the history of Phaser 3 and it contains _breaking changes_. If you're upgrading from an earlier version please do check the log entries below.

keep keep keep replace keep keep keep keep keep

/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2019 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

var AddToDOM = require('../dom/AddToDOM');
var AnimationManager = require('../animations/AnimationManager');
var CacheManager = require('../cache/CacheManager');
var CanvasPool = require('../display/canvas/CanvasPool');
var Class = require('../utils/Class');
var Config = require('./Config');
var CreateRenderer = require('./CreateRenderer');
var DataManager = require('../data/DataManager');
var DebugHeader = require('./DebugHeader');
var Device = require('../device');
var DOMContentLoaded = require('../dom/DOMContentLoaded');
var EventEmitter = require('eventemitter3');
var Events = require('./events');
var InputManager = require('../input/InputManager');
var PluginCache = require('../plugins/PluginCache');
var PluginManager = require('../plugins/PluginManager');
var ScaleManager = require('../scale/ScaleManager');
var SceneManager = require('../scene/SceneManager');
var SoundManagerCreator = require('../sound/SoundManagerCreator');
var TextureEvents = require('../textures/events');
var TextureManager = require('../textures/TextureManager');
var TimeStep = require('./TimeStep');
var VisibilityHandler = require('./VisibilityHandler');

if (typeof EXPERIMENTAL)
{
    var CreateDOMContainer = require('../dom/CreateDOMContainer');
}

if (typeof PLUGIN_FBINSTANT)
{
    var FacebookInstantGamesPlugin = require('../../plugins/fbinstant/src/FacebookInstantGamesPlugin');
}

/**
 * @classdesc
 * The Phaser.Game instance is the main controller for the entire Phaser game. It is responsible
 * for handling the boot process, parsing the configuration values, creating the renderer,
 * and setting-up all of the global Phaser systems, such as sound and input.
 * Once that is complete it will start the Scene Manager and then begin the main game loop.
 *
 * You should generally avoid accessing any of the systems created by Game, and instead use those
 * made available to you via the Phaser.Scene Systems class instead.
 *
 * @class Game
 * @memberof Phaser
 * @constructor
 * @fires Phaser.Core.Events#BLUR
 * @fires Phaser.Core.Events#FOCUS
 * @fires Phaser.Core.Events#HIDDEN
 * @fires Phaser.Core.Events#VISIBLE
 * @since 3.0.0
 *
 * @param {GameConfig} [GameConfig] - The configuration object for your Phaser Game instance.
 */
var Game = new Class({

    initialize:

    function Game (config)
    {
        /**
         * The parsed Game Configuration object.
         *
         * The values stored within this object are read-only and should not be changed at run-time.
         *
         * @name Phaser.Game#config
         * @type {Phaser.Core.Config}
         * @readonly
         * @since 3.0.0
         */
        this.config = new Config(config);

        /**
         * A reference to either the Canvas or WebGL Renderer that this Game is using.
         *
         * @name Phaser.Game#renderer
         * @type {(Phaser.Renderer.Canvas.CanvasRenderer|Phaser.Renderer.WebGL.WebGLRenderer)}
         * @since 3.0.0
         */
        this.renderer = null;

        if (typeof EXPERIMENTAL)
        {
            /**
             * A reference to an HTML Div Element used as a DOM Element Container.
             *
             * Only set if `createDOMContainer` is `true` in the game config (by default it is `false`) and
             * if you provide a parent element to insert the Phaser Game inside.
             *
             * See the DOM Element Game Object for more details.
             *
             * @name Phaser.Game#domContainer
             * @type {HTMLDivElement}
             * @since 3.12.0
             */
            this.domContainer = null;
        }

        /**
         * A reference to the HTML Canvas Element that Phaser uses to render the game.
         * This is created automatically by Phaser unless you provide a `canvas` property
         * in your Game Config.
         *
         * @name Phaser.Game#canvas
         * @type {HTMLCanvasElement}
         * @since 3.0.0
         */
        this.canvas = null;

        /**
         * A reference to the Rendering Context belonging to the Canvas Element this game is rendering to.
         * If the game is running under Canvas it will be a 2d Canvas Rendering Context.
         * If the game is running under WebGL it will be a WebGL Rendering Context.
         * This context is created automatically by Phaser unless you provide a `context` property
         * in your Game Config.
         *
         * @name Phaser.Game#context
         * @type {(CanvasRenderingContext2D|WebGLRenderingContext)}
         * @since 3.0.0
         */
        this.context = null;

        /**
         * A flag indicating when this Game instance has finished its boot process.
         *
         * @name Phaser.Game#isBooted
         * @type {boolean}
         * @readonly
         * @since 3.0.0
         */
        this.isBooted = false;

        /**
         * A flag indicating if this Game is currently running its game step or not.
         *
         * @name Phaser.Game#isRunning
         * @type {boolean}
         * @readonly
         * @since 3.0.0
         */
        this.isRunning = false;

        /**
         * An Event Emitter which is used to broadcast game-level events from the global systems.
         *
         * @name Phaser.Game#events
         * @type {Phaser.Events.EventEmitter}
         * @since 3.0.0
         */
        this.events = new EventEmitter();

        /**
         * An instance of the Animation Manager.
         *
         * The Animation Manager is a global system responsible for managing all animations used within your game.
         *
         * @name Phaser.Game#anims
         * @type {Phaser.Animations.AnimationManager}
         * @since 3.0.0
         */
        this.anims = new AnimationManager(this);

        /**
         * An instance of the Texture Manager.
         *
         * The Texture Manager is a global system responsible for managing all textures being used by your game.
         *
         * @name Phaser.Game#textures
         * @type {Phaser.Textures.TextureManager}
         * @since 3.0.0
         */
        this.textures = new TextureManager(this);

        /**
         * An instance of the Cache Manager.
         *
         * The Cache Manager is a global system responsible for caching, accessing and releasing external game assets.
         *
         * @name Phaser.Game#cache
         * @type {Phaser.Cache.CacheManager}
         * @since 3.0.0
         */
        this.cache = new CacheManager(this);

        /**
         * An instance of the Data Manager
         *
         * @name Phaser.Game#registry
         * @type {Phaser.Data.DataManager}
         * @since 3.0.0
         */
        this.registry = new DataManager(this);

        /**
         * An instance of the Input Manager.
         *
         * The Input Manager is a global system responsible for the capture of browser-level input events.
         *
         * @name Phaser.Game#input
         * @type {Phaser.Input.InputManager}
         * @since 3.0.0
         */
        this.input = new InputManager(this, this.config);

        /**
         * An instance of the Scene Manager.
         *
         * The Scene Manager is a global system responsible for creating, modifying and updating the Scenes in your game.
         *
         * @name Phaser.Game#scene
         * @type {Phaser.Scenes.SceneManager}
         * @since 3.0.0
         */
        this.scene = new SceneManager(this, this.config.sceneConfig);

        /**
         * A reference to the Device inspector.
         *
         * Contains information about the device running this game, such as OS, browser vendor and feature support.
         * Used by various systems to determine capabilities and code paths.
         *
         * @name Phaser.Game#device
         * @type {Phaser.DeviceConf}
         * @since 3.0.0
         */
        this.device = Device;

        /**
         * An instance of the Scale Manager.
         *
         * The Scale Manager is a global system responsible for handling scaling of the game canvas.
         *
         * @name Phaser.Game#scale
         * @type {Phaser.Scale.ScaleManager}
         * @since 3.16.0
         */
        this.scale = new ScaleManager(this, this.config);

        /**
         * An instance of the base Sound Manager.
         *
         * The Sound Manager is a global system responsible for the playback and updating of all audio in your game.
         *
         * @name Phaser.Game#sound
         * @type {Phaser.Sound.BaseSoundManager}
         * @since 3.0.0
         */
        this.sound = SoundManagerCreator.create(this);

        /**
         * An instance of the Time Step.
         *
         * The Time Step is a global system responsible for setting-up and responding to the browser frame events, processing
         * them and calculating delta values. It then automatically calls the game step.
         *
         * @name Phaser.Game#loop
         * @type {Phaser.Core.TimeStep}
         * @since 3.0.0
         */
        this.loop = new TimeStep(this, this.config.fps);

        /**
         * An instance of the Plugin Manager.
         *
         * The Plugin Manager is a global system that allows plugins to register themselves with it, and can then install
         * those plugins into Scenes as required.
         *
         * @name Phaser.Game#plugins
         * @type {Phaser.Plugins.PluginManager}
         * @since 3.0.0
         */
        this.plugins = new PluginManager(this, this.config);

        if (typeof PLUGIN_FBINSTANT)
        {
            /**
             * An instance of the Facebook Instant Games Plugin.
             *
             * This will only be available if the plugin has been built into Phaser,
             * or you're using the special Facebook Instant Games custom build.
             *
             * @name Phaser.Game#facebook
             * @type {Phaser.FacebookInstantGamesPlugin}
             * @since 3.13.0
             */
            this.facebook = new FacebookInstantGamesPlugin(this);
        }

        /**
         * Is this Game pending destruction at the start of the next frame?
         *
         * @name Phaser.Game#pendingDestroy
         * @type {boolean}
         * @private
         * @since 3.5.0
         */
        this.pendingDestroy = false;

        /**
         * Remove the Canvas once the destroy is over?
         *
         * @name Phaser.Game#removeCanvas
         * @type {boolean}
         * @private
         * @since 3.5.0
         */
        this.removeCanvas = false;

        /**
         * Remove everything when the game is destroyed.
         * You cannot create a new Phaser instance on the same web page after doing this.
         *
         * @name Phaser.Game#noReturn
         * @type {boolean}
         * @private
         * @since 3.12.0
         */
        this.noReturn = false;

        /**
         * Does the window the game is running in currently have focus or not?
         * This is modified by the VisibilityHandler.
         *
         * @name Phaser.Game#hasFocus
         * @type {boolean}
         * @readonly
         * @since 3.9.0
         */
        this.hasFocus = false;

        //  Wait for the DOM Ready event, then call boot.
        DOMContentLoaded(this.boot.bind(this));
    },

    /**
     * This method is called automatically when the DOM is ready. It is responsible for creating the renderer,
     * displaying the Debug Header, adding the game canvas to the DOM and emitting the 'boot' event.
     * It listens for a 'ready' event from the base systems and once received it will call `Game.start`.
     *
     * @method Phaser.Game#boot
     * @protected
     * @fires Phaser.Core.Events#BOOT
     * @listens Phaser.Textures.Events#READY
     * @since 3.0.0
     */
    boot: function ()
    {
        if (!PluginCache.hasCore('EventEmitter'))
        {
            console.warn('Aborting. Core Plugins missing.');
            return;
        }

        this.isBooted = true;

        this.config.preBoot(this);

        this.scale.preBoot();

        CreateRenderer(this);

        if (typeof EXPERIMENTAL)
        {
            CreateDOMContainer(this);
        }

        DebugHeader(this);

        AddToDOM(this.canvas, this.config.parent);

        //  The Texture Manager has to wait on a couple of non-blocking events before it's fully ready.
        //  So it will emit this internal event when done:
        this.textures.once(TextureEvents.READY, this.texturesReady, this);

        this.events.emit(Events.BOOT);
    },

    /**
     * Called automatically when the Texture Manager has finished setting up and preparing the
     * default textures.
     *
     * @method Phaser.Game#texturesReady
     * @private
     * @fires Phaser.Game#ready
     * @since 3.12.0
     */
    texturesReady: function ()
    {
        //  Start all the other systems
        this.events.emit(Events.READY);

        this.start();
    },

    /**
     * Called automatically by Game.boot once all of the global systems have finished setting themselves up.
     * By this point the Game is now ready to start the main loop running.
     * It will also enable the Visibility Handler.
     *
     * @method Phaser.Game#start
     * @protected
     * @since 3.0.0
     */
    start: function ()
    {
        this.isRunning = true;

        this.config.postBoot(this);

        if (this.renderer)
        {
            this.loop.start(this.step.bind(this));
        }
        else
        {
            this.loop.start(this.headlessStep.bind(this));
        }

        VisibilityHandler(this);

        var eventEmitter = this.events;

        eventEmitter.on(Events.HIDDEN, this.onHidden, this);
        eventEmitter.on(Events.VISIBLE, this.onVisible, this);
        eventEmitter.on(Events.BLUR, this.onBlur, this);
        eventEmitter.on(Events.FOCUS, this.onFocus, this);
    },

    /**
     * The main Game Step. Called automatically by the Time Step, once per browser frame (typically as a result of
     * Request Animation Frame, or Set Timeout on very old browsers.)
     *
     * The step will update the global managers first, then proceed to update each Scene in turn, via the Scene Manager.
     *
     * It will then render each Scene in turn, via the Renderer. This process emits `prerender` and `postrender` events.
     *
     * @method Phaser.Game#step
     * @fires Phaser.Core.Events#PRE_STEP_EVENT
     * @fires Phaser.Core.Events#STEP_EVENT
     * @fires Phaser.Core.Events#POST_STEP_EVENT
     * @fires Phaser.Core.Events#PRE_RENDER_EVENT
     * @fires Phaser.Core.Events#POST_RENDER_EVENT
     * @since 3.0.0
     *
     * @param {number} time - The current time. Either a High Resolution Timer value if it comes from Request Animation Frame, or Date.now if using SetTimeout.
     * @param {number} delta - The delta time in ms since the last frame. This is a smoothed and capped value based on the FPS rate.
     */
    step: function (time, delta)
    {
        if (this.pendingDestroy)
        {
            return this.runDestroy();
        }

        var eventEmitter = this.events;

        //  Global Managers like Input and Sound update in the prestep

        eventEmitter.emit(Events.PRE_STEP, time, delta);

        //  This is mostly meant for user-land code and plugins

        eventEmitter.emit(Events.STEP, time, delta);

        //  Update the Scene Manager and all active Scenes

        this.scene.update(time, delta);

        //  Our final event before rendering starts

        eventEmitter.emit(Events.POST_STEP, time, delta);

        var renderer = this.renderer;

        //  Run the Pre-render (clearing the canvas, setting background colors, etc)

        renderer.preRender();

        eventEmitter.emit(Events.PRE_RENDER, renderer, time, delta);

        //  The main render loop. Iterates all Scenes and all Cameras in those scenes, rendering to the renderer instance.

        this.scene.render(renderer);

        //  The Post-Render call. Tidies up loose end, takes snapshots, etc.

        renderer.postRender();

        //  The final event before the step repeats. Your last chance to do anything to the canvas before it all starts again.

        eventEmitter.emit(Events.POST_RENDER, renderer, time, delta);
    },

    /**
     * A special version of the Game Step for the HEADLESS renderer only.
     *
     * The main Game Step. Called automatically by the Time Step, once per browser frame (typically as a result of
     * Request Animation Frame, or Set Timeout on very old browsers.)
     *
     * The step will update the global managers first, then proceed to update each Scene in turn, via the Scene Manager.
     *
     * This process emits `prerender` and `postrender` events, even though nothing actually displays.
     *
     * @method Phaser.Game#headlessStep
     * @fires Phaser.Game#prerenderEvent
     * @fires Phaser.Game#postrenderEvent
     * @since 3.2.0
     *
     * @param {number} time - The current time. Either a High Resolution Timer value if it comes from Request Animation Frame, or Date.now if using SetTimeout.
     * @param {number} delta - The delta time in ms since the last frame. This is a smoothed and capped value based on the FPS rate.
     */
    headlessStep: function (time, delta)
    {
        var eventEmitter = this.events;

        //  Global Managers

        eventEmitter.emit(Events.PRE_STEP, time, delta);

        eventEmitter.emit(Events.STEP, time, delta);

        //  Scenes

        this.scene.update(time, delta);

        eventEmitter.emit(Events.POST_STEP, time, delta);

        //  Render

        eventEmitter.emit(Events.PRE_RENDER);

        eventEmitter.emit(Events.POST_RENDER);
    },

    /**
     * Called automatically by the Visibility Handler.
     * This will pause the main loop and then emit a pause event.
     *
     * @method Phaser.Game#onHidden
     * @protected
     * @fires Phaser.Core.Events#PAUSE
     * @since 3.0.0
     */
    onHidden: function ()
    {
        this.loop.pause();

        this.events.emit(Events.PAUSE);
    },

    /**
     * Called automatically by the Visibility Handler.
     * This will resume the main loop and then emit a resume event.
     *
     * @method Phaser.Game#onVisible
     * @protected
     * @fires Phaser.Core.Events#RESUME
     * @since 3.0.0
     */
    onVisible: function ()
    {
        this.loop.resume();

        this.events.emit(Events.RESUME);
    },

    /**
     * Called automatically by the Visibility Handler.
     * This will set the main loop into a 'blurred' state, which pauses it.
     *
     * @method Phaser.Game#onBlur
     * @protected
     * @since 3.0.0
     */
    onBlur: function ()
    {
        this.hasFocus = false;

        this.loop.blur();
    },

    /**
     * Called automatically by the Visibility Handler.
     * This will set the main loop into a 'focused' state, which resumes it.
     *
     * @method Phaser.Game#onFocus
     * @protected
     * @since 3.0.0
     */
    onFocus: function ()
    {
        this.hasFocus = true;

        this.loop.focus();
    },

    /**
     * Returns the current game frame.
     * When the game starts running, the frame is incremented every time Request Animation Frame, or Set Timeout, fires.
     *
     * @method Phaser.Game#getFrame
     * @since 3.16.0
     *
     * @return {number} The current game frame.
     */
    getFrame: function ()
    {
        return this.loop.frame;
    },

    /**
     * Returns the current game timestamp.
     * When the game starts running, the frame is incremented every time Request Animation Frame, or Set Timeout, fires.
     *
     * @method Phaser.Game#getTime
     * @since 3.16.0
     *
     * @return {number} The current game timestamp.
     */
    getTime: function ()
    {
        return this.loop.frame.time;
    },

    /**
     * Flags this Game instance as needing to be destroyed on the next frame.
     * It will wait until the current frame has completed and then call `runDestroy` internally.
     *
     * If you **do not** need to run Phaser again on the same web page you can set the `noReturn` argument to `true` and it will free-up
     * memory being held by the core Phaser plugins. If you do need to create another game instance on the same page, leave this as `false`.
     *
     * @method Phaser.Game#destroy
     * @fires Phaser.Core.Events#DESTROY
     * @since 3.0.0
     *
     * @param {boolean} removeCanvas - Set to `true` if you would like the parent canvas element removed from the DOM, or `false` to leave it in place.
     * @param {boolean} [noReturn=false] - If `true` all the core Phaser plugins are destroyed. You cannot create another instance of Phaser on the same web page if you do this.
     */
    destroy: function (removeCanvas, noReturn)
    {
        if (noReturn === undefined) { noReturn = false; }

        this.pendingDestroy = true;

        this.removeCanvas = removeCanvas;
        this.noReturn = noReturn;
    },

    /**
     * Destroys this Phaser.Game instance, all global systems, all sub-systems and all Scenes.
     *
     * @method Phaser.Game#runDestroy
     * @private
     * @since 3.5.0
     */
    runDestroy: function ()
    {
        this.events.emit(Events.DESTROY);

        this.events.removeAllListeners();

        this.scene.destroy();

        if (this.renderer)
        {
            this.renderer.destroy();
        }

        if (this.removeCanvas && this.canvas)
        {
            CanvasPool.remove(this.canvas);

            if (this.canvas.parentNode)
            {
                this.canvas.parentNode.removeChild(this.canvas);
            }
        }

        if (typeof EXPERIMENTAL)
        {
            if (this.domContainer)
            {
                this.domContainer.parentNode.removeChild(this.domContainer);
            }
        }

        this.loop.destroy();

        this.pendingDestroy = false;
    }

});

/**
 * "Computers are good at following instructions, but not at reading your mind." - Donald Knuth
 */

module.exports = Game;


/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2019 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

var FillStyleCanvas = require('../FillStyleCanvas');
var LineStyleCanvas = require('../LineStyleCanvas');
var SetTransform = require('../../../renderer/canvas/utils/SetTransform');

/**
 * Renders this Game Object with the Canvas Renderer to the given Camera.
 * The object will not render if any of its renderFlags are set or it is being actively filtered out by the Camera.
 * This method should not be called directly. It is a utility function of the Render module.
 *
 * @method Phaser.GameObjects.Polygon#renderCanvas
 * @since 3.13.0
 * @private
 *
 * @param {Phaser.Renderer.Canvas.CanvasRenderer} renderer - A reference to the current active Canvas renderer.
 * @param {Phaser.GameObjects.Polygon} src - The Game Object being rendered in this call.
 * @param {number} interpolationPercentage - Reserved for future use and custom pipelines.
 * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera that is rendering the Game Object.
 * @param {Phaser.GameObjects.Components.TransformMatrix} parentMatrix - This transform matrix is defined if the game object is nested
 */
var PolygonCanvasRenderer = function (renderer, src, interpolationPercentage, camera, parentMatrix)
{
    var ctx = renderer.currentContext;

    if (SetTransform(renderer, ctx, src, camera, parentMatrix))
    {
        var dx = src._displayOriginX;
        var dy = src._displayOriginY;

        var path = src.pathData;
        var pathLength = path.length - 1;

        var px1 = path[0] - dx;
        var py1 = path[1] - dy;

        ctx.beginPath();

        ctx.moveTo(px1, py1);

        if (!src.closePath)
        {
            pathLength -= 2;
        }

        for (var i = 2; i < pathLength; i += 2)
        {
            var px2 = path[i] - dx;
            var py2 = path[i + 1] - dy;

            ctx.lineTo(px2, py2);
        }

        ctx.closePath();

        if (src.isFilled)
        {
            FillStyleCanvas(ctx, src);

            ctx.fill();
        }

        if (src.isStroked)
        {
            LineStyleCanvas(ctx, src);

            ctx.stroke();
        }

        //  Restore the context saved in SetTransform
        ctx.restore();
    }
};

module.exports = PolygonCanvasRenderer;


/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2019 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

module.exports = {

    ARC: 0,
    BEGIN_PATH: 1,
    CLOSE_PATH: 2,
    FILL_RECT: 3,
    LINE_TO: 4,
    MOVE_TO: 5,
    LINE_STYLE: 6,
    FILL_STYLE: 7,
    FILL_PATH: 8,
    STROKE_PATH: 9,
    FILL_TRIANGLE: 10,
    STROKE_TRIANGLE: 11,
    LINE_FX_TO: 12,
    MOVE_FX_TO: 13,
    SAVE: 14,
    RESTORE: 15,
    TRANSLATE: 16,
    SCALE: 17,
    ROTATE: 18,
    SET_TEXTURE: 19,
    CLEAR_TEXTURE: 20,
    GRADIENT_FILL_STYLE: 21,
    GRADIENT_LINE_STYLE: 22

};


/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2019 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

var BuildGameObject = require('../BuildGameObject');
var GameObjectCreator = require('../GameObjectCreator');
var GetAdvancedValue = require('../../utils/object/GetAdvancedValue');
var RenderTexture = require('./RenderTexture');

/**
 * @typedef {object} RenderTextureConfig
 *
 * @property {number} [x=0] - The x coordinate of the RenderTexture's position.
 * @property {number} [y=0] - The y coordinate of the RenderTexture's position.
 * @property {number} [width=32] - The width of the RenderTexture.
 * @property {number} [height=32] - The height of the RenderTexture.
 */

/**
 * Creates a new Render Texture Game Object and returns it.
 *
 * Note: This method will only be available if the Render Texture Game Object has been built into Phaser.
 *
 * @method Phaser.GameObjects.GameObjectCreator#renderTexture
 * @since 3.2.0
 *
 * @param {RenderTextureConfig} config - The configuration object this Game Object will use to create itself.
 * @param {boolean} [addToScene] - Add this Game Object to the Scene after creating it? If set this argument overrides the `add` property in the config object.
 *
 * @return {Phaser.GameObjects.RenderTexture} The Game Object that was created.
 */
GameObjectCreator.register('renderTexture', function (config, addToScene)
{
    if (config === undefined) { config = {}; }

    var x = GetAdvancedValue(config, 'x', 0);
    var y = GetAdvancedValue(config, 'y', 0);
    var width = GetAdvancedValue(config, 'width', 32);
    var height = GetAdvancedValue(config, 'height', 32);

    var renderTexture = new RenderTexture(this.scene, x, y, width, height);

    if (addToScene !== undefined)
    {
        config.add = addToScene;
    }

    BuildGameObject(this.scene, renderTexture, config);

    return renderTexture;
});


keep keep keep keep replace keep keep keep keep keep

export default function Grid({ swiper, extendParams }) {
  extendParams({
    grid: {
      rows: 1,
      fill: 'column',
    },
  });

  let slidesNumberEvenToRows;
  let slidesPerRow;
  let numFullColumns;

  const getSpaceBetween = () => {
    let spaceBetween = swiper.params.spaceBetween;
    if (typeof spaceBetween === 'string' && spaceBetween.indexOf('%') >= 0) {
      spaceBetween = (parseFloat(spaceBetween.replace('%', '')) / 100) * swiper.size;
    } else if (typeof spaceBetween === 'string') {
      spaceBetween = parseFloat(spaceBetween);
    }
    return spaceBetween;
  };

  const initSlides = (slidesLength) => {
    const { slidesPerView } = swiper.params;
    const { rows, fill } = swiper.params.grid;
    slidesPerRow = slidesNumberEvenToRows / rows;
    numFullColumns = Math.floor(slidesLength / rows);
    if (Math.floor(slidesLength / rows) === slidesLength / rows) {
      slidesNumberEvenToRows = slidesLength;
    } else {
      slidesNumberEvenToRows = Math.ceil(slidesLength / rows) * rows;
    }
    if (slidesPerView !== 'auto' && fill === 'row') {
      slidesNumberEvenToRows = Math.max(slidesNumberEvenToRows, slidesPerView * rows);
    }
  };

  const updateSlide = (i, slide, slidesLength, getDirectionLabel) => {
    const { slidesPerGroup } = swiper.params;
    const spaceBetween = getSpaceBetween();
    const { rows, fill } = swiper.params.grid;
    // Set slides order
    let newSlideOrderIndex;
    let column;
    let row;
    if (fill === 'row' && slidesPerGroup > 1) {
      const groupIndex = Math.floor(i / (slidesPerGroup * rows));
      const slideIndexInGroup = i - rows * slidesPerGroup * groupIndex;
      const columnsInGroup =
        groupIndex === 0
          ? slidesPerGroup
          : Math.min(
              Math.ceil((slidesLength - groupIndex * rows * slidesPerGroup) / rows),
              slidesPerGroup,
            );
      row = Math.floor(slideIndexInGroup / columnsInGroup);
      column = slideIndexInGroup - row * columnsInGroup + groupIndex * slidesPerGroup;

      newSlideOrderIndex = column + (row * slidesNumberEvenToRows) / rows;
      slide.style.order = newSlideOrderIndex;
    } else if (fill === 'column') {
      column = Math.floor(i / rows);
      row = i - column * rows;
      if (column > numFullColumns || (column === numFullColumns && row === rows - 1)) {
        row += 1;
        if (row >= rows) {
          row = 0;
          column += 1;
        }
      }
    } else {
      row = Math.floor(i / slidesPerRow);
      column = i - row * slidesPerRow;
    }
    slide.style[getDirectionLabel('margin-top')] =
      row !== 0 ? spaceBetween && `${spaceBetween}px` : '';
  };

  const updateWrapperSize = (slideSize, snapGrid, getDirectionLabel) => {
    const { centeredSlides, roundLengths } = swiper.params;
    const spaceBetween = getSpaceBetween();
    const { rows } = swiper.params.grid;
    swiper.virtualSize = (slideSize + spaceBetween) * slidesNumberEvenToRows;
    swiper.virtualSize = Math.ceil(swiper.virtualSize / rows) - spaceBetween;
    swiper.wrapperEl.style[getDirectionLabel('width')] = `${swiper.virtualSize + spaceBetween}px`;
    if (centeredSlides) {
      const newSlidesGrid = [];
      for (let i = 0; i < snapGrid.length; i += 1) {
        let slidesGridItem = snapGrid[i];
        if (roundLengths) slidesGridItem = Math.floor(slidesGridItem);
        if (snapGrid[i] < swiper.virtualSize + snapGrid[0]) newSlidesGrid.push(slidesGridItem);
      }
      snapGrid.splice(0, snapGrid.length);
      snapGrid.push(...newSlidesGrid);
    }
  };

  swiper.grid = {
    initSlides,
    updateSlide,
    updateWrapperSize,
  };
}


.swiper-fade {
  &.swiper-free-mode {
    .swiper-slide {
      transition-timing-function: ease-out;
    }
  }
  .swiper-slide {
    pointer-events: none;
    transition-property: opacity;
    .swiper-slide {
      pointer-events: none;
    }
  }
  .swiper-slide-active {
    &,
    & .swiper-slide-active {
      pointer-events: auto;
    }
  }
}


import { Swiper } from './swiper.js';
import { SwiperSlide } from './swiper-slide.js';

export { useSwiperSlide, useSwiper } from './context.js';

export { Swiper, SwiperSlide };


if (slidesPerView !== 'auto' && fill === 'row') {

slidesNumberEvenToRows = Math.max(slidesNumberEvenToRows, slidesPerView * rows);

const updateSlide = (i, slide, slidesLength, getDirectionLabel) => {

const { slidesPerGroup } = swiper.params;

keep keep keep keep add keep keep keep keep keep

slidesPerRow = slidesNumberEvenToRows / rows;

export const processLazyPreloader = (swiper, imageEl) => {
  if (!swiper || swiper.destroyed || !swiper.params) return;
  const slideSelector = () => (swiper.isElement ? `swiper-slide` : `.${swiper.params.slideClass}`);
  const slideEl = imageEl.closest(slideSelector());
  if (slideEl) {
    const lazyEl = slideEl.querySelector(`.${swiper.params.lazyPreloaderClass}`);
    if (lazyEl) lazyEl.remove();
  }
};

const unlazy = (swiper, index) => {
  if (!swiper.slides[index]) return;
  const imageEl = swiper.slides[index].querySelector('[loading="lazy"]');
  if (imageEl) imageEl.removeAttribute('loading');
};

export const preload = (swiper) => {
  if (!swiper || swiper.destroyed || !swiper.params) return;
  let amount = swiper.params.lazyPreloadPrevNext;
  const len = swiper.slides.length;
  if (!len || !amount || amount < 0) return;
  amount = Math.min(amount, len);
  const slidesPerView =
    swiper.params.slidesPerView === 'auto'
      ? swiper.slidesPerViewDynamic()
      : Math.ceil(swiper.params.slidesPerView);
  const activeIndex = swiper.activeIndex;
  const slideIndexLastInView = activeIndex + slidesPerView - 1;
  if (swiper.params.rewind || swiper.params.loop) {
    for (let i = activeIndex - amount; i <= slideIndexLastInView + amount; i += 1) {
      const realIndex = ((i % len) + len) % len;
      if (realIndex < activeIndex || realIndex > slideIndexLastInView) unlazy(swiper, realIndex);
    }
  } else {
    for (
      let i = Math.max(slideIndexLastInView - amount, 0);
      i <= Math.min(slideIndexLastInView + amount, len - 1);
      i += 1
    ) {
      if (i !== activeIndex && i > slideIndexLastInView) unlazy(swiper, i);
    }
  }
};


export default function onScroll() {
  const swiper = this;
  const { wrapperEl, rtlTranslate, enabled } = swiper;
  if (!enabled) return;
  swiper.previousTranslate = swiper.translate;
  if (swiper.isHorizontal()) {
    swiper.translate = -wrapperEl.scrollLeft;
  } else {
    swiper.translate = -wrapperEl.scrollTop;
  }
  // eslint-disable-next-line
  if (swiper.translate === 0) swiper.translate = 0;

  swiper.updateActiveIndex();
  swiper.updateSlidesClasses();

  let newProgress;
  const translatesDiff = swiper.maxTranslate() - swiper.minTranslate();
  if (translatesDiff === 0) {
    newProgress = 0;
  } else {
    newProgress = (swiper.translate - swiper.minTranslate()) / translatesDiff;
  }
  if (newProgress !== swiper.progress) {
    swiper.updateProgress(rtlTranslate ? -swiper.translate : swiper.translate);
  }

  swiper.emit('setTranslate', swiper.translate, false);
}


<!DOCTYPE html>

  <meta charset="utf-8">
  <title>Swiper demo</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">

Slide 1

Slide 2

Slide 3

Slide 4

Slide 5

Slide 6

Slide 7

Slide 8

Slide 9

Slide 10


export default function slideToLoop(
  index = 0,
  speed = this.params.speed,
  runCallbacks = true,
  internal,
) {
  if (typeof index === 'string') {
    const indexAsNumber = parseInt(index, 10);

    index = indexAsNumber;
  }

  const swiper = this;
  let newIndex = index;
  if (swiper.params.loop) {
    if (swiper.virtual && swiper.params.virtual.enabled) {
      // eslint-disable-next-line
      newIndex = newIndex + swiper.virtual.slidesBefore;
    } else {
      newIndex = swiper.getSlideIndexByData(newIndex);
    }
  }

  return swiper.slideTo(newIndex, speed, runCallbacks, internal);
}


row !== 0 ? spaceBetween && `${spaceBetween}px` : '';


const slideIndexLastInView = activeIndex + slidesPerView - 1;

if (swiper.params.rewind || swiper.params.loop) {

for (let i = activeIndex - amount; i <= slideIndexLastInView + amount; i += 1) {

const realIndex = ((i % len) + len) % len;

if (swiper.params.grid && swiper.params.grid.rows > 1) {

const preloadColumns = [activeColumn - amount];

...Array.from({ length: amount }).map((_, i) => {

return activeColumn + slidesPerView + i;

if (preloadColumns.includes(slideEl.column)) unlazy(swiper, i);


:root {
  /*
  --swiper-pagination-color: var(--swiper-theme-color);
  --swiper-pagination-left: auto;
  --swiper-pagination-right: 8px;
  --swiper-pagination-bottom: 8px;
  --swiper-pagination-top: auto;
  --swiper-pagination-fraction-color: inherit;
  --swiper-pagination-progressbar-bg-color: rgba(0,0,0,0.25);
  --swiper-pagination-progressbar-size: 4px;
  --swiper-pagination-bullet-size: 8px;
  --swiper-pagination-bullet-width: 8px;
  --swiper-pagination-bullet-height: 8px;
  --swiper-pagination-bullet-border-radius: 50%;
  --swiper-pagination-bullet-inactive-color: #000;
  --swiper-pagination-bullet-inactive-opacity: 0.2;
  --swiper-pagination-bullet-opacity: 1;
  --swiper-pagination-bullet-horizontal-gap: 4px;
  --swiper-pagination-bullet-vertical-gap: 6px;
  */
}
.swiper-pagination {
  position: absolute;
  text-align: center;
  transition: 300ms opacity;
  transform: translate3d(0, 0, 0);
  z-index: 10;
  &.swiper-pagination-hidden {
    opacity: 0;
  }
  .swiper-pagination-disabled > &,
  &.swiper-pagination-disabled {
    display: none !important;
  }
}
/* Common Styles */
.swiper-pagination-fraction,
.swiper-pagination-custom,
.swiper-horizontal > .swiper-pagination-bullets,
.swiper-pagination-bullets.swiper-pagination-horizontal {
  bottom: var(--swiper-pagination-bottom, 8px);
  top: var(--swiper-pagination-top, auto);
  left: 0;
  width: 100%;
}
/* Bullets */
.swiper-pagination-bullets-dynamic {
  overflow: hidden;
  font-size: 0;
  .swiper-pagination-bullet {
    transform: scale(0.33);
    position: relative;
  }
  .swiper-pagination-bullet-active {
    transform: scale(1);
  }
  .swiper-pagination-bullet-active-main {
    transform: scale(1);
  }
  .swiper-pagination-bullet-active-prev {
    transform: scale(0.66);
  }
  .swiper-pagination-bullet-active-prev-prev {
    transform: scale(0.33);
  }
  .swiper-pagination-bullet-active-next {
    transform: scale(0.66);
  }
  .swiper-pagination-bullet-active-next-next {
    transform: scale(0.33);
  }
}
.swiper-pagination-bullet {
  width: var(--swiper-pagination-bullet-width, var(--swiper-pagination-bullet-size, 8px));
  height: var(--swiper-pagination-bullet-height, var(--swiper-pagination-bullet-size, 8px));
  display: inline-block;
  border-radius: var(--swiper-pagination-bullet-border-radius, 50%);
  background: var(--swiper-pagination-bullet-inactive-color, #000);
  opacity: var(--swiper-pagination-bullet-inactive-opacity, 0.2);
  button& {
    border: none;
    margin: 0;
    padding: 0;
    box-shadow: none;
    appearance: none;
  }
  .swiper-pagination-clickable & {
    cursor: pointer;
  }

  &:only-child {
    display: none !important;
  }
}
.swiper-pagination-bullet-active {
  opacity: var(--swiper-pagination-bullet-opacity, 1);
  background: var(--swiper-pagination-color, var(--swiper-theme-color));
}

.swiper-vertical > .swiper-pagination-bullets,
.swiper-pagination-vertical.swiper-pagination-bullets {
  right: var(--swiper-pagination-right, 8px);
  left: var(--swiper-pagination-left, auto);
  top: 50%;
  transform: translate3d(0px, -50%, 0);
  .swiper-pagination-bullet {
    margin: var(--swiper-pagination-bullet-vertical-gap, 6px) 0;
    display: block;
  }
  &.swiper-pagination-bullets-dynamic {
    top: 50%;
    transform: translateY(-50%);
    width: 8px;
    .swiper-pagination-bullet {
      display: inline-block;
      transition: 200ms transform, 200ms top;
    }
  }
}
.swiper-horizontal > .swiper-pagination-bullets,
.swiper-pagination-horizontal.swiper-pagination-bullets {
  .swiper-pagination-bullet {
    margin: 0 var(--swiper-pagination-bullet-horizontal-gap, 4px);
  }
  &.swiper-pagination-bullets-dynamic {
    left: 50%;
    transform: translateX(-50%);
    white-space: nowrap;
    .swiper-pagination-bullet {
      transition: 200ms transform, 200ms left;
    }
  }
}
.swiper-horizontal.swiper-rtl > .swiper-pagination-bullets-dynamic .swiper-pagination-bullet,
:host(.swiper-horizontal.swiper-rtl) .swiper-pagination-bullets-dynamic .swiper-pagination-bullet {
  transition: 200ms transform, 200ms right;
}
/* Fraction */
.swiper-pagination-fraction {
  color: var(--swiper-pagination-fraction-color, inherit);
}
/* Progress */
.swiper-pagination-progressbar {
  background: var(--swiper-pagination-progressbar-bg-color, rgba(0, 0, 0, 0.25));
  position: absolute;
  .swiper-pagination-progressbar-fill {
    background: var(--swiper-pagination-color, var(--swiper-theme-color));
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    transform: scale(0);
    transform-origin: left top;
  }
  /*ADD_HOST*/
  .swiper-rtl & .swiper-pagination-progressbar-fill {
    transform-origin: right top;
  }
  .swiper-horizontal > &,
  &.swiper-pagination-horizontal,
  .swiper-vertical > &.swiper-pagination-progressbar-opposite,
  &.swiper-pagination-vertical.swiper-pagination-progressbar-opposite {
    width: 100%;
    height: var(--swiper-pagination-progressbar-size, 4px);
    left: 0;
    top: 0;
  }
  .swiper-vertical > &,
  &.swiper-pagination-vertical,
  .swiper-horizontal > &.swiper-pagination-progressbar-opposite,
  &.swiper-pagination-horizontal.swiper-pagination-progressbar-opposite {
    width: var(--swiper-pagination-progressbar-size, 4px);
    height: 100%;
    left: 0;
    top: 0;
  }
}
.swiper-pagination-lock {
  display: none;
}