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

/**
* New version of PIXI.WebGLFilterManager
*
* @class Phaser.Renderer.Canvas
* @constructor
* @param {Phaser.Game} game - Game reference to the currently running game.
*/
Phaser.Renderer.WebGL.FilterManager = function (renderer)
{
    this.renderer = renderer;

    this.gl = null;

    /**
     * @property filterStack
     * @type Array
     */
    this.filterStack = [];

    this.texturePool = [];

    /**
     * @property offsetX
     * @type Number
     */
    this.offsetX = 0;

    /**
     * @property offsetY
     * @type Number
     */
    this.offsetY = 0;

    this.vertexBuffer = null;
    this.uvBuffer = null;
    this.colorBuffer = null;
    this.indexBuffer = null;

    this.vertexArray = null;
    this.colorArray = null;
    this.uvArray = null;

};

Phaser.Renderer.WebGL.FilterManager.prototype.constructor = Phaser.Renderer.WebGL.FilterManager;

Phaser.Renderer.WebGL.FilterManager.prototype = {

    init: function ()
    {
        this.gl = this.renderer.gl;

        var gl = this.gl;

        this.texturePool = [];

        //  Initialises the shader buffers

        this.vertexBuffer = gl.createBuffer();
        this.uvBuffer = gl.createBuffer();
        this.colorBuffer = gl.createBuffer();
        this.indexBuffer = gl.createBuffer();

        // bind and upload the vertex data
        this.vertexArray = new Float32Array([
            0.0, 0.0,
            1.0, 0.0,
            0.0, 1.0,
            1.0, 1.0
        ]);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW);

        // bind and upload the uv buffer
        this.uvArray = new Float32Array([
            0.0, 0.0,
            1.0, 0.0,
            0.0, 1.0,
            1.0, 1.0
        ]);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW);

        this.colorArray = new Float32Array([
            1.0, 0xFFFFFF,
            1.0, 0xFFFFFF,
            1.0, 0xFFFFFF,
            1.0, 0xFFFFFF
        ]);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW);

        // bind and upload the index
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([ 0, 1, 2, 1, 3, 2 ]), gl.STATIC_DRAW);
    },

    /**
    * @method begin
    * @param renderSession {RenderSession}
    * @param buffer {ArrayBuffer}
    */
    begin: function (buffer)
    {
        this.defaultShader = this.renderer.shaderManager.defaultShader;

        var projection = this.renderer.projection;

        this.width = projection.x * 2;
        this.height = -projection.y * 2;
        this.buffer = buffer;
    },

    /**
    * Applies the filter and adds it to the current filter stack.
    *
    * @method pushFilter
    * @param filterBlock {Object} the filter that will be pushed to the current filter stack
    */
    pushFilter: function (filterBlock)
    {
        var gl = this.gl;

        var projection = this.renderer.projection;
        var offset = this.renderer.offset;

        filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds();

        //  Creates a brand new Stencil Manager each time the filter is pushed
        //  Let's check why

        // filterBlock._previous_stencil_mgr = this.renderSession.stencilManager;
        // this.renderSession.stencilManager = new PIXI.WebGLStencilManager();
        // this.renderSession.stencilManager.setContext(gl);
        // gl.disable(gl.STENCIL_TEST);

        this.filterStack.push(filterBlock);

        var filter = filterBlock.filterPasses[0];

        this.offsetX += filterBlock._filterArea.x;
        this.offsetY += filterBlock._filterArea.y;

        var texture = this.texturePool.pop();

        if (!texture)
        {
            texture = new Phaser.Renderer.WebGL.FilterTexture(this.renderer, this.width * this.renderer.resolution, this.height * this.renderer.resolution);
        }
        else
        {
            texture.resize(this.width * this.renderer.resolution, this.height * this.renderer.resolution);
        }

        gl.bindTexture(gl.TEXTURE_2D, texture.texture);

        var filterArea = filterBlock._filterArea;

        var padding = filter.padding;

        filterArea.x -= padding;
        filterArea.y -= padding;
        filterArea.width += padding * 2;
        filterArea.height += padding * 2;

        // cap filter to screen size..
        filterArea.x = Math.max(0, filterArea.x);
        filterArea.y = Math.max(0, filterArea.y);
        filterArea.width = Math.min(this.width, filterArea.width);
        filterArea.height = Math.min(this.height, filterArea.height);

        gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer);

        // set view port
        gl.viewport(0, 0, filterArea.width * this.renderer.resolution, filterArea.height * this.renderer.resolution);

        projection.x = filterArea.width / 2;
        projection.y = -filterArea.height / 2;

        offset.x = -filterArea.x;
        offset.y = -filterArea.y;

        gl.colorMask(true, true, true, true);
        gl.clearColor(0, 0, 0, 0);
        gl.clear(gl.COLOR_BUFFER_BIT);

        filterBlock._glFilterTexture = texture;
    },

    /**
    * Removes the last filter from the filter stack and doesn't return it.
    *
    * @method popFilter
    */
    popFilter: function ()
    {
        var gl = this.gl;

        var filterBlock = this.filterStack.pop();
        var filterArea = filterBlock._filterArea;

        var texture = filterBlock._glFilterTexture;
        var projection = this.renderer.projection;
        var offset = this.renderer.offset;

        if (filterBlock.filterPasses.length > 1)
        {
            gl.viewport(0, 0, filterArea.width * this.renderer.resolution, filterArea.height * this.renderer.resolution);

            gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);

            this.vertexArray[0] = 0;
            this.vertexArray[1] = filterArea.height;

            this.vertexArray[2] = filterArea.width;
            this.vertexArray[3] = filterArea.height;

            this.vertexArray[4] = 0;
            this.vertexArray[5] = 0;

            this.vertexArray[6] = filterArea.width;
            this.vertexArray[7] = 0;

            gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray);

            gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);

            this.uvArray[2] = filterArea.width / this.width;
            this.uvArray[5] = filterArea.height / this.height;
            this.uvArray[6] = filterArea.width / this.width;
            this.uvArray[7] = filterArea.height / this.height;

            gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray);

            var inputTexture = texture;
            var outputTexture = this.texturePool.pop();

            if (!outputTexture)
            {
                outputTexture = new Phaser.Renderer.WebGL.FilterTexture(this.renderer, this.width * this.renderer.resolution, this.height * this.renderer.resolution);
            }
            else
            {
                outputTexture.resize(this.width * this.renderer.resolution, this.height * this.renderer.resolution);
            }

            // need to clear this FBO as it may have some left over elements from a previous filter.
            gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer);

            gl.clear(gl.COLOR_BUFFER_BIT);

            gl.disable(gl.BLEND);

            for (var i = 0; i < filterBlock.filterPasses.length - 1; i++)
            {
                var filterPass = filterBlock.filterPasses[i];

                gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer);

                // set texture
                gl.activeTexture(gl.TEXTURE0);
                gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture);

                // draw texture..
                this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height);

                // swap the textures..
                var temp = inputTexture;
                inputTexture = outputTexture;
                outputTexture = temp;
            }

            gl.enable(gl.BLEND);

            texture = inputTexture;

            this.texturePool.push(outputTexture);
        }

        var filter = filterBlock.filterPasses[filterBlock.filterPasses.length - 1];

        this.offsetX -= filterArea.x;
        this.offsetY -= filterArea.y;

        var sizeX = this.width;
        var sizeY = this.height;

        var offsetX = 0;
        var offsetY = 0;

        var buffer = this.buffer;

        // time to render the filters texture to the previous scene
        if (this.filterStack.length === 0)
        {
            gl.colorMask(true, true, true, true);
        }
        else
        {
            var currentFilter = this.filterStack[this.filterStack.length - 1];

            filterArea = currentFilter._filterArea;

            sizeX = filterArea.width;
            sizeY = filterArea.height;

            offsetX = filterArea.x;
            offsetY = filterArea.y;

            buffer = currentFilter._glFilterTexture.frameBuffer;
        }

        projection.x = sizeX / 2;
        projection.y = -sizeY / 2;

        offset.x = offsetX;
        offset.y = offsetY;

        filterArea = filterBlock._filterArea;

        var x = filterArea.x - offsetX;
        var y = filterArea.y - offsetY;

        // update the buffers..
        // make sure to flip the y!
        gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);

        this.vertexArray[0] = x;
        this.vertexArray[1] = y + filterArea.height;

        this.vertexArray[2] = x + filterArea.width;
        this.vertexArray[3] = y + filterArea.height;

        this.vertexArray[4] = x;
        this.vertexArray[5] = y;

        this.vertexArray[6] = x + filterArea.width;
        this.vertexArray[7] = y;

        gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);

        this.uvArray[2] = filterArea.width / this.width;
        this.uvArray[5] = filterArea.height / this.height;
        this.uvArray[6] = filterArea.width / this.width;
        this.uvArray[7] = filterArea.height / this.height;

        gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray);

        gl.viewport(0, 0, sizeX * this.renderer.resolution, sizeY * this.renderer.resolution);

        // bind the buffer
        gl.bindFramebuffer(gl.FRAMEBUFFER, buffer);

        // set texture
        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, texture.texture);

        //  Need to consolidate with the entry in the method above

        /*
        if (this.renderSession.stencilManager)
        {
            this.renderSession.stencilManager.destroy();
        }

        this.renderSession.stencilManager = filterBlock._previous_stencil_mgr;

        filterBlock._previous_stencil_mgr = null;

        if (this.renderSession.stencilManager.count > 0)
        {
            gl.enable(gl.STENCIL_TEST);
        }
        else
        {
            gl.disable(gl.STENCIL_TEST);
        }
        */

        this.applyFilterPass(filter, filterArea, sizeX, sizeY);

        this.texturePool.push(texture);

        filterBlock._glFilterTexture = null;
    },

    /**
    * Applies the filter to the specified area.
    *
    * @method applyFilterPass
    * @param filter {Phaser.Filter} the filter that needs to be applied
    * @param filterArea {Texture} TODO - might need an update
    * @param width {Number} the horizontal range of the filter
    * @param height {Number} the vertical range of the filter
    */
    applyFilterPass: function (filter, filterArea, width, height)
    {
        var gl = this.gl;
        var shader = filter.shaders;

        if (!shader)
        {
            shader = new Phaser.Renderer.WebGL.Shaders.Sprite(this.renderer);

            shader.fragmentSrc = filter.fragmentSrc;
            shader.uniforms = filter.uniforms;
            shader.init(true);

            filter.shaders = shader;
        }

        this.renderer.shaderManager.setShader(shader);

        gl.uniform2f(shader.projectionVector, width / 2, -(height / 2));
        gl.uniform2f(shader.offsetVector, 0, 0);

        if (filter.uniforms.dimensions)
        {
            filter.uniforms.dimensions.value[0] = this.width;
            filter.uniforms.dimensions.value[1] = this.height;
            filter.uniforms.dimensions.value[2] = this.vertexArray[0];
            filter.uniforms.dimensions.value[3] = this.vertexArray[5];
        }

        shader.syncUniforms();

        gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
        gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
        gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
        gl.vertexAttribPointer(shader.colorAttribute, 2, gl.FLOAT, false, 0, 0);

        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);

        gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);

        this.renderer.drawCount++;
    },

    /**
    * Destroys the filter and removes it from the filter stack.
    *
    * @method destroy
    */
    destroy: function ()
    {
        var gl = this.gl;

        this.filterStack = null;

        this.offsetX = 0;
        this.offsetY = 0;

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

        this.texturePool = null;

        //  Destroy buffers
        gl.deleteBuffer(this.vertexBuffer);
        gl.deleteBuffer(this.uvBuffer);
        gl.deleteBuffer(this.colorBuffer);
        gl.deleteBuffer(this.indexBuffer);
    }

};


'   t_position.x = a_position.x * transform_a + a_position.y * transform_c + transform_tx;',

'   t_position.y = a_position.x * transform_b + a_position.y * transform_d + transform_ty;',

'   gl_Position = u_view_matrix * vec4(t_position, 1.0, 1.0);',

keep keep keep replace replace keep keep keep keep

'   t_position.x = a_position.x + transform_tx;',

'   t_position.y = a_position.y + transform_ty;',

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

/**
* This is a base Plugin template to use for any Phaser plugin development.
*
* @class Phaser.Plugin
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
* @param {any} parent - The object that owns this plugin, usually Phaser.PluginManager.
*/
Phaser.Plugin = function (game, parent) {

    if (parent === undefined) { parent = null; }

    /**
    * @property {Phaser.Game} game - A reference to the currently running game.
    */
    this.game = game;

    /**
    * @property {any} parent - The parent of this plugin. If added to the PluginManager the parent will be set to that, otherwise it will be null.
    */
    this.parent = parent;

    /**
    * @property {boolean} active - A Plugin with active=true has its preUpdate and update methods called by the parent, otherwise they are skipped.
    * @default
    */
    this.active = false;

    /**
    * @property {boolean} visible - A Plugin with visible=true has its render and postRender methods called by the parent, otherwise they are skipped.
    * @default
    */
    this.visible = false;

    /**
    * @property {boolean} hasPreUpdate - A flag to indicate if this plugin has a preUpdate method.
    * @default
    */
    this.hasPreUpdate = false;

    /**
    * @property {boolean} hasUpdate - A flag to indicate if this plugin has an update method.
    * @default
    */
    this.hasUpdate = false;

    /**
    * @property {boolean} hasPostUpdate - A flag to indicate if this plugin has a postUpdate method.
    * @default
    */
    this.hasPostUpdate = false;

    /**
    * @property {boolean} hasRender - A flag to indicate if this plugin has a render method.
    * @default
    */
    this.hasRender = false;

    /**
    * @property {boolean} hasPostRender - A flag to indicate if this plugin has a postRender method.
    * @default
    */
    this.hasPostRender = false;

};

Phaser.Plugin.prototype = {

    /**
    * Pre-update is called at the very start of the update cycle, before any other subsystems have been updated (including Physics).
    * It is only called if active is set to true.
    * @method Phaser.Plugin#preUpdate
    */
    preUpdate: function () {
    },

    /**
    * Update is called after all the core subsystems (Input, Tweens, Sound, etc) and the State have updated, but before the render.
    * It is only called if active is set to true.
    * @method Phaser.Plugin#update
    */
    update: function () {
    },

    /**
    * Render is called right after the Game Renderer completes, but before the State.render.
    * It is only called if visible is set to true.
    * @method Phaser.Plugin#render
    */
    render: function () {
    },

    /**
    * Post-render is called after the Game Renderer and State.render have run.
    * It is only called if visible is set to true.
    * @method Phaser.Plugin#postRender
    */
    postRender: function () {
    },

    /**
    * Clear down this Plugin and null out references
    * @method Phaser.Plugin#destroy
    */
    destroy: function () {

        this.game = null;
        this.parent = null;
        this.active = false;
        this.visible = false;

    }

};

Phaser.Plugin.prototype.constructor = Phaser.Plugin;


import Point from './Point';
export default Point;


keep keep keep keep keep replace keep keep keep keep keep

var FragmentShader = require('./FragmentShader');
var VertexShader = require('./VertexShader');

var CONST = {

    // VERTEX_SIZE = (sizeof(vec2) * 4) + (sizeof(float) + sizeof(uint32))
    VERTEX_SIZE: 44,
    INDEX_SIZE: 2,
    SPRITE_VERTEX_COUNT: 4,
    SPRITE_INDEX_COUNT: 6,

    // How many 32-bit components does the vertex have.
    SPRITE_VERTEX_COMPONENT_COUNT: 11,

    // Can't be bigger since index are 16-bit
    MAX_SPRITES: 10000,

    VERTEX_SHADER_SOURCE: VertexShader,
    FRAGMENT_SHADER_SOURCE: FragmentShader

};

module.exports = CONST;


function In (v)
{
    return Math.pow(2, 10 * (v - 1)) - 0.001;
}

function Out (v)
{
    return 1 - Math.pow(2, -10 * v);
}

function InOut (v)
{
    if ((v *= 2) < 1)
    {
        return 0.5 * Math.pow(2, 10 * (v - 1));
    }
    else
    {
        return 0.5 * (2 - Math.pow(2, -10 * (v - 1)));
    }
}

module.exports = {

    In: In,
    Out: Out,
    InOut: InOut

};


setBlendMode

Sets-up the given blendMode from WebGL's point of view.

blendMode

the blendMode, should be a Pixi const, such as PIXI.BlendModes.ADD


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

/**
* ArraySet is a Set data structure (items must be unique within the set) that also maintains order.
* This allows specific items to be easily added or removed from the Set.
*
* Item equality (and uniqueness) is determined by the behavior of `Array.indexOf`.
*
* This used primarily by the Input subsystem.
*
* @class Phaser.ArraySet
* @constructor
* @param {any[]} [list=(new array)] - The backing array: if specified the items in the list _must_ be unique, per `Array.indexOf`, and the ownership of the array _should_ be relinquished to the ArraySet.
*/
Phaser.ArraySet = function (list) {

    /**
    * Current cursor position as established by `first` and `next`.
    * @property {integer} position
    * @default
    */
    this.position = 0;

    /**
    * The backing array.
    * @property {any[]} list
    */
    this.list = list || [];

};

Phaser.ArraySet.prototype = {

    /**
    * Adds a new element to the end of the list.
    * If the item already exists in the list it is not moved.
    *
    * @method Phaser.ArraySet#add
    * @param {any} item - The element to add to this list.
    * @return {any} The item that was added.
    */
    add: function (item) {

        if (!this.exists(item))
        {
            this.list.push(item);
        }

        return item;

    },

    /**
    * Gets the index of the item in the list, or -1 if it isn't in the list.
    *
    * @method Phaser.ArraySet#getIndex
    * @param {any} item - The element to get the list index for.
    * @return {integer} The index of the item or -1 if not found.
    */
    getIndex: function (item) {

        return this.list.indexOf(item);

    },

    /**
    * Gets an item from the set based on the property strictly equaling the value given.
    * Returns null if not found.
    *
    * @method Phaser.ArraySet#getByKey
    * @param {string} property - The property to check against the value.
    * @param {any} value - The value to check if the property strictly equals.
    * @return {any} The item that was found, or null if nothing matched.
    */
    getByKey: function (property, value) {

        var i = this.list.length;

        while (i--)
        {
            if (this.list[i][property] === value)
            {
                return this.list[i];
            }
        }

        return null;

    },

    /**
    * Checks for the item within this list.
    *
    * @method Phaser.ArraySet#exists
    * @param {any} item - The element to get the list index for.
    * @return {boolean} True if the item is found in the list, otherwise false.
    */
    exists: function (item) {

        return (this.list.indexOf(item) > -1);

    },

    /**
    * Removes all the items.
    *
    * @method Phaser.ArraySet#reset
    */
    reset: function () {

        this.list.length = 0;

    },

    /**
    * Removes the given element from this list if it exists.
    *
    * @method Phaser.ArraySet#remove
    * @param {any} item - The item to be removed from the list.
    * @return {any} item - The item that was removed.
    */
    remove: function (item) {

        var idx = this.list.indexOf(item);

        if (idx > -1)
        {
            this.list.splice(idx, 1);
            return item;
        }

    },

    /**
    * Sets the property `key` to the given value on all members of this list.
    *
    * @method Phaser.ArraySet#setAll
    * @param {any} key - The property of the item to set.
    * @param {any} value - The value to set the property to.
    */
    setAll: function (key, value) {

        var i = this.list.length;

        while (i--)
        {
            if (this.list[i])
            {
                this.list[i][key] = value;
            }
        }

    },

    /**
    * Calls a function on all members of this list, using the member as the context for the callback.
    *
    * If the `key` property is present it must be a function.
    * The function is invoked using the item as the context.
    *
    * @method Phaser.ArraySet#callAll
    * @param {string} key - The name of the property with the function to call.
    * @param {...*} parameter - Additional parameters that will be passed to the callback.
    */
    callAll: function (key) {

        var args = Array.prototype.slice.call(arguments, 1);

        var i = this.list.length;

        while (i--)
        {
            if (this.list[i] && this.list[i][key])
            {
                this.list[i][key].apply(this.list[i], args);
            }
        }

    },

    /**
    * Removes every member from this ArraySet and optionally destroys it.
    *
    * @method Phaser.ArraySet#removeAll
    * @param {boolean} [destroy=false] - Call `destroy` on each member as it's removed from this set.
    */
    removeAll: function (destroy) {

        if (destroy === undefined) { destroy = false; }

        var i = this.list.length;

        while (i--)
        {
            if (this.list[i])
            {
                var item = this.remove(this.list[i]);

                if (destroy)
                {
                    item.destroy();
                }
            }
        }

        this.position = 0;
        this.list = [];

    }

};

/**
* Number of items in the ArraySet. Same as `list.length`.
*
* @name Phaser.ArraySet#total
* @property {integer} total
*/
Object.defineProperty(Phaser.ArraySet.prototype, "total", {

    get: function () {
        return this.list.length;
    }

});

/**
* Returns the first item and resets the cursor to the start.
*
* @name Phaser.ArraySet#first
* @property {any} first
*/
Object.defineProperty(Phaser.ArraySet.prototype, "first", {

    get: function () {

        this.position = 0;

        if (this.list.length > 0)
        {
            return this.list[0];
        }
        else
        {
            return null;
        }

    }

});

/**
* Returns the the next item (based on the cursor) and advances the cursor.
*
* @name Phaser.ArraySet#next
* @property {any} next
*/
Object.defineProperty(Phaser.ArraySet.prototype, "next", {

    get: function () {

        if (this.position < this.list.length)
        {
            this.position++;

            return this.list[this.position];
        }
        else
        {
            return null;
        }

    }

});

Phaser.ArraySet.prototype.constructor = Phaser.ArraySet;


// Can't be bigger since index are 16-bit

keep keep keep replace keep keep keep keep

var BindVertexArray = require('../../utils/vao/BindVertexArray');
var CreateProgram = require('../../utils/shader/CreateProgram');
var CreateShader = require('../../utils/shader/CreateShader');
var CreateBuffer = require('../../utils/buffer/CreateBuffer');
var CreateAttribDesc = require('../../utils/vao/CreateAttribDesc');
var VertexBuffer = require('../../utils/buffer/VertexBuffer');
var IndexBuffer = require('../../utils/buffer/IndexBuffer');
var VertexArray = require('../../utils/vao/VertexArray');

var PHASER_CONST = require('../../../../const');
var CONST = require('./const');

var SpriteBatch32 = function (game, gl, manager)
{
    gl.getExtension('OES_element_index_uint');
    CONST.INDEX_SIZE = 4;
    CONST.MAX_SPRITES = 100000;

    this.game = game;
    this.type = PHASER_CONST.WEBGL;

    this.view = game.canvas;
    this.resolution = game.config.resolution;
    this.width = game.config.width * game.config.resolution;
    this.height = game.config.height * game.config.resolution;

    this.glContext = gl;

    this.maxSprites = null;

    this.vertShader = null;
    this.fragShader = null;

    this.program = null;

    this.vertexArray = null;
    this.indexBufferObject = null;
    this.vertexDataBuffer = null;
    this.indexDataBuffer = null;

    this.elementCount = 0;

    this.currentTexture2D = null;
    this.viewMatrixLocation = null;

    //   All of these settings will be able to be controlled via the Game Config
    this.config = {
        clearBeforeRender: true,
        transparent: false,
        autoResize: false,
        preserveDrawingBuffer: false,

        WebGLContextOptions: {
            alpha: true,
            antialias: true,
            premultipliedAlpha: true,
            stencil: true,
            preserveDrawingBuffer: false
        }
    };

    this.manager = manager;
    this.dirty = false;

    this.init(this.glContext);
};

SpriteBatch32.prototype.constructor = SpriteBatch32;

SpriteBatch32.prototype = {

    init: function (gl)
    {

        var vertexDataBuffer = new VertexBuffer(CONST.VERTEX_SIZE * CONST.SPRITE_VERTEX_COUNT * CONST.MAX_SPRITES);

        var indexDataBuffer = new VertexBuffer(CONST.INDEX_SIZE * CONST.SPRITE_INDEX_COUNT * CONST.MAX_SPRITES);

        var vertShader = CreateShader(gl, CONST.VERTEX_SHADER_SOURCE, gl.VERTEX_SHADER);
        var fragShader = CreateShader(gl, CONST.FRAGMENT_SHADER_SOURCE, gl.FRAGMENT_SHADER);
        var program = CreateProgram(gl, vertShader, fragShader);

        var indexBufferObject = CreateBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, gl.STATIC_DRAW, null, indexDataBuffer.getByteCapacity());

        var attribArray = [
            //CreateAttribDesc(gl, program, 'a_position', 2, gl.FLOAT, false, CONST.VERTEX_SIZE, 0),
            //CreateAttribDesc(gl, program, 'a_tex_coord', 2, gl.FLOAT, false, CONST.VERTEX_SIZE, 8),
            //CreateAttribDesc(gl, program, 'a_translate', 2, gl.FLOAT, false, CONST.VERTEX_SIZE, 16),
            //CreateAttribDesc(gl, program, 'a_scale', 2, gl.FLOAT, false, CONST.VERTEX_SIZE, 24),
            //CreateAttribDesc(gl, program, 'a_rotation', 1, gl.FLOAT, false, CONST.VERTEX_SIZE, 32),
            //CreateAttribDesc(gl, program, 'a_color', 3, 5121, true, CONST.VERTEX_SIZE, 36)
            CreateAttribDesc(gl, program, 'a_position', 2, gl.FLOAT, false, CONST.VERTEX_SIZE, 0),
            CreateAttribDesc(gl, program, 'a_tex_coord', 2, gl.FLOAT, false, CONST.VERTEX_SIZE, 8),
            CreateAttribDesc(gl, program, 'transform_a', 1, gl.FLOAT, false, CONST.VERTEX_SIZE, 16),
            CreateAttribDesc(gl, program, 'transform_b', 1, gl.FLOAT, false, CONST.VERTEX_SIZE, 20),
            CreateAttribDesc(gl, program, 'transform_c', 1, gl.FLOAT, false, CONST.VERTEX_SIZE, 24),
            CreateAttribDesc(gl, program, 'transform_d', 1, gl.FLOAT, false, CONST.VERTEX_SIZE, 28),
            CreateAttribDesc(gl, program, 'transform_tx', 1, gl.FLOAT, false, CONST.VERTEX_SIZE, 32),
            CreateAttribDesc(gl, program, 'transform_ty', 1, gl.FLOAT, false, CONST.VERTEX_SIZE, 36),
            CreateAttribDesc(gl, program, 'a_color', 3, gl.UNSIGNED_BYTE, true, CONST.VERTEX_SIZE, 40)
        ];

        var vertexArray = new VertexArray(CreateBuffer(gl, gl.ARRAY_BUFFER, gl.STREAM_DRAW, null, vertexDataBuffer.getByteCapacity()), attribArray);

        var viewMatrixLocation = gl.getUniformLocation(program, 'u_view_matrix');

        this.vertexDataBuffer = vertexDataBuffer;
        this.indexDataBuffer = indexDataBuffer;

        this.vertShader = vertShader;
        this.fragShader = fragShader;
        this.program = program;

        this.indexBufferObject = indexBufferObject;
        this.vertexArray = vertexArray;

        this.viewMatrixLocation = viewMatrixLocation;

        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBufferObject);

        var indexBuffer = indexDataBuffer.uintView;
        var max = CONST.MAX_SPRITES * CONST.SPRITE_INDEX_COUNT;

            // Populate the index buffer only once
        for (var indexA = 0, indexB = 0; indexA < max; indexA += CONST.SPRITE_INDEX_COUNT, indexB += CONST.SPRITE_VERTEX_COUNT)
        {
            indexBuffer[indexA + 0] = indexB + 0;
            indexBuffer[indexA + 1] = indexB + 1;
            indexBuffer[indexA + 2] = indexB + 2;
            indexBuffer[indexA + 3] = indexB + 0;
            indexBuffer[indexA + 4] = indexB + 2;
            indexBuffer[indexA + 5] = indexB + 3;
        }
        gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, indexBuffer);

        this.bind();

        this.resize(this.width, this.height);

        this.unbind();
    },

    isFull: function ()
    {
        return (this.vertexDataBuffer.getByteLength() >= this.vertexDataBuffer.getByteCapacity());
    },

    add: function (frame, anchorX, anchorY, matrix, vertexColor)
    {
        this.manager.setBatch(this, frame.texture.source[frame.sourceIndex].glTexture);

        // The user must check if the buffers are full before flushing
        // this is to give freedom of when should the renderer flush. var vertexDataBuffer = this.vertexDataBuffer;
        var vertexDataBuffer = this.vertexDataBuffer;
        var vertexBufferF32 = vertexDataBuffer.floatView;
        var vertexBufferU32 = vertexDataBuffer.uintView;
        var vertexOffset = vertexDataBuffer.allocate(CONST.SPRITE_VERTEX_COMPONENT_COUNT * CONST.SPRITE_VERTEX_COUNT);
        var uvs = frame.uvs;
        var width = frame.width;
        var height = frame.height;
        var x = width * -anchorX + frame.x;
        var y = height * -anchorY + frame.y;
        var a = matrix[0];
        var b = matrix[1];
        var c = matrix[2];
        var d = matrix[3];
        var tx = matrix[4];
        var ty = matrix[5];

        vertexBufferF32[vertexOffset++] = x;
        vertexBufferF32[vertexOffset++] = y;
        vertexBufferF32[vertexOffset++] = uvs.x0;
        vertexBufferF32[vertexOffset++] = uvs.y0;
        vertexBufferF32[vertexOffset++] = a;
        vertexBufferF32[vertexOffset++] = b;
        vertexBufferF32[vertexOffset++] = c;
        vertexBufferF32[vertexOffset++] = d;
        vertexBufferF32[vertexOffset++] = tx;
        vertexBufferF32[vertexOffset++] = ty;
        vertexBufferU32[vertexOffset++] = vertexColor.topLeft;

        vertexBufferF32[vertexOffset++] = x;
        vertexBufferF32[vertexOffset++] = y + height;
        vertexBufferF32[vertexOffset++] = uvs.x1;
        vertexBufferF32[vertexOffset++] = uvs.y1;
        vertexBufferF32[vertexOffset++] = a;
        vertexBufferF32[vertexOffset++] = b;
        vertexBufferF32[vertexOffset++] = c;
        vertexBufferF32[vertexOffset++] = d;
        vertexBufferF32[vertexOffset++] = tx;
        vertexBufferF32[vertexOffset++] = ty;
        vertexBufferU32[vertexOffset++] = vertexColor.bottomLeft;

        vertexBufferF32[vertexOffset++] = x + width;
        vertexBufferF32[vertexOffset++] = y + height;
        vertexBufferF32[vertexOffset++] = uvs.x2;
        vertexBufferF32[vertexOffset++] = uvs.y2;
        vertexBufferF32[vertexOffset++] = a;
        vertexBufferF32[vertexOffset++] = b;
        vertexBufferF32[vertexOffset++] = c;
        vertexBufferF32[vertexOffset++] = d;
        vertexBufferF32[vertexOffset++] = tx;
        vertexBufferF32[vertexOffset++] = ty;
        vertexBufferU32[vertexOffset++] = vertexColor.bottomRight;

        vertexBufferF32[vertexOffset++] = x + width;
        vertexBufferF32[vertexOffset++] = y;
        vertexBufferF32[vertexOffset++] = uvs.x3;
        vertexBufferF32[vertexOffset++] = uvs.y3;
        vertexBufferF32[vertexOffset++] = a;
        vertexBufferF32[vertexOffset++] = b;
        vertexBufferF32[vertexOffset++] = c;
        vertexBufferF32[vertexOffset++] = d;
        vertexBufferF32[vertexOffset++] = tx;
        vertexBufferF32[vertexOffset++] = ty;
        vertexBufferU32[vertexOffset++] = vertexColor.topRight;

        this.elementCount += CONST.SPRITE_INDEX_COUNT;
    },

    bind: function ()
    {
        var gl = this.glContext;

        gl.useProgram(this.program);

        gl.clearColor(0, 0, 0, 1);

        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBufferObject);

        BindVertexArray(gl, this.vertexArray);
    },

    unbind: function ()
    {
        var gl = this.glContext;

        gl.useProgram(null);

        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
    },

    flush: function ()
    {
        var gl = this.glContext;
        var vertexDataBuffer = this.vertexDataBuffer;

        gl.bufferSubData(gl.ARRAY_BUFFER, 0, vertexDataBuffer.getUsedBufferAsFloat());

        gl.drawElements(gl.TRIANGLES, this.elementCount, gl.UNSIGNED_INT, 0);

        vertexDataBuffer.clear();

        this.elementCount = 0;
    },

    resize: function (width, height)
    {
        var gl = this.glContext;
        var res = this.game.config.resolution;

        this.width = width * res;
        this.height = height * res;

        this.view.width = this.width;
        this.view.height = this.height;

        if (this.autoResize)
        {
            this.view.style.width = (this.width / res) + 'px';
            this.view.style.height = (this.height / res) + 'px';
        }

        gl.viewport(0, 0, this.width, this.height);

        gl.uniformMatrix4fv(
            this.viewMatrixLocation,
            false,
            new Float32Array([
                2 / this.view.width, 0, 0, 0,
                0, -2 / this.view.height, 0, 0,
                0, 0, 1, 1,
                -1, 1, 0, 0
            ])
        );
    },

    destroy: function ()
    {
        var gl = this.glContext;

        if (gl)
        {
            gl.deleteShader(this.vertShader);
            gl.deleteShader(this.fragShader);
            gl.deleteProgram(this.program);
            gl.deleteBuffer(this.indexBufferObject);
            gl.deleteBuffer(this.vertexArray.buffer);
        }
    }

};

module.exports = SpriteBatch32;


<!DOCTYPE html>

	<meta charset="utf-8">
	<title>Phaser Source: src/utils/DOM.js</title>

	<link type="text/css" rel="stylesheet" href="styles/default.css">

	<link type="text/css" rel="stylesheet" href="styles/sunlight.default.css">

	<link type="text/css" rel="stylesheet" href="styles/site.cerulean.css">

			Phaser API

					Namespaces

							Phaser

							PIXI

					Classes

							Event

							EventTarget

							Animation

							AnimationManager

							AnimationParser

							ArraySet

							ArrayUtils

							AudioSprite

							BitmapData

							BitmapText

							Bullet

							Button

							Cache

							Camera

							Canvas

							Circle

							Color

							Angle

							Animation

							AutoCull

							Bounds

							BringToTop

							Core

							Crop

							Delta

							Destroy

							FixedToCamera

							Health

							InCamera

							InputEnabled

							InWorld

							LifeSpan

							LoadTexture

							Overlap

							PhysicsBody

							Reset

							ScaleMinMax

							Smoothed

							Create

							Creature

							Device

							DeviceButton

							DOM

							Easing

							Back

							Bounce

							Circular

							Cubic

							Elastic

							Exponential

							Linear

							Quadratic

							Quartic

							Quintic

							Sinusoidal

							Ellipse

							Events

							Filter

							FlexGrid

							FlexLayer

							Frame

							FrameData

							Game

							GameObjectCreator

							GameObjectFactory

							Gamepad

							Graphics

							Group

							Hermite

							Image

							ImageCollection

							Input

							InputHandler

							Key

							Keyboard

							KeyCode

							Line

							LinkedList

							Loader

							LoaderParser

							Math

							Matrix

							Mouse

							MSPointer

							Net

							Particle

							Particles

							Arcade

							Emitter

							Path

							PathFollower

							PathPoint

							Physics

							Arcade

							Body

							TilemapCollision

							Ninja

							AABB

							Body

							Circle

							Tile

							P2

							Body

							BodyDebug

							CollisionGroup

							ContactMaterial

							DistanceConstraint

							FixtureList

							GearConstraint

							InversePointProxy

							LockConstraint

							Material

							PointProxy

							PrismaticConstraint

							RevoluteConstraint

							RotationalSpring

							Spring

							Plugin

							PathManager

							PluginManager

							Point

							Pointer

							PointerMode

							Polygon

							QuadTree

							RandomDataGenerator

							Rectangle

							RenderTexture

							RequestAnimationFrame

							RetroFont

							Rope

							RoundedRectangle

							ScaleManager

							Signal

							SignalBinding

							SinglePad

							Sound

							SoundManager

							Sprite

							SpriteBatch

							Stage

							State

							StateManager

							Text

							Tile

							Tilemap

							TilemapLayer

							TilemapParser

							Tileset

							TileSprite

							Time

							Timer

							TimerEvent

							Touch

							Tween

							TweenData

							TweenManager

							Utils

							Debug

							Video

							Weapon

							World

							AbstractFilter

							BaseTexture

							CanvasBuffer

							CanvasGraphics

							CanvasMaskManager

							CanvasPool

							CanvasRenderer

							CanvasTinter

							ComplexPrimitiveShader

							DisplayObjectContainer

							EarCut

							Event

							EventTarget

							FilterTexture

							Graphics

							GraphicsData

							PIXI

							DisplayObject

							PixiFastShader

							PixiShader

							PrimitiveShader

							RenderTexture

							Rope

							Sprite

							SpriteBatch

							Strip

							StripShader

							Texture

							TilingSprite

							WebGLBlendModeManager

							WebGLFastSpriteBatch

							WebGLFilterManager

							WebGLRenderer

					Global

							ANGLE_DOWN

							ANGLE_LEFT

							ANGLE_NORTH_EAST

							ANGLE_NORTH_WEST

							ANGLE_RIGHT

							ANGLE_SOUTH_EAST

							ANGLE_SOUTH_WEST

							ANGLE_UP

							AUTO

							BITMAPDATA

							BITMAPTEXT

							blendModes

							BOTTOM_CENTER

							BOTTOM_LEFT

							BOTTOM_RIGHT

							BUTTON

							CANVAS

							CANVAS_FILTER

							CENTER

							CIRCLE

							CREATURE

							displayList

							DOWN

							ELLIPSE

							emit

							EMITTER

							GAMES

							GRAPHICS

							GROUP

							HEADLESS

							HORIZONTAL

							IMAGE

							LANDSCAPE

							LEFT

							LEFT_BOTTOM

							LEFT_CENTER

							LEFT_TOP

							LINE

							listeners

							MATRIX

							mixin

							NONE

							off

							on

							once

							PENDING_ATLAS

							Phaser.Path#numPoints
return {number} The total number of PathPoints in this Path.

							POINT

							POINTER

							POLYGON

							PORTRAIT

							RECTANGLE

							removeAllListeners

							RENDERTEXTURE

							RETROFONT

							RIGHT

							RIGHT_BOTTOM

							RIGHT_CENTER

							RIGHT_TOP

							ROPE

							ROUNDEDRECTANGLE

							scaleModes

							SPRITE

							SPRITEBATCH

							stopImmediatePropagation

							stopPropagation

							TEXT

							TILEMAP

							TILEMAPLAYER

							TILESPRITE

							TOP_CENTER

							TOP_LEFT

							TOP_RIGHT

							UP

							VERSION

							VERTICAL

							VIDEO

							WEBGL

							WEBGL_FILTER

					Core

Game

Group

World

Loader

Cache

Time

Camera

State Manager

Tween Manager

Sound Manager

Input Manager

Scale Manager

					Game Objects

Factory (game.add)

Creator (game.make)

Sprite

Image

Sound

Video

Particle Emitter

Particle

Text

Tween

BitmapText

Tilemap

BitmapData

RetroFont

Button

Animation

Graphics

RenderTexture

TileSprite

					Geometry

Circle

Ellipse

Line

Matrix

Point

Polygon

Rectangle

Rounded Rectangle

					Physics

Arcade Physics

Body

Weapon

P2 Physics

Body

Spring

CollisionGroup

ContactMaterial

Ninja Physics

Body

					Input

Input Handler

Pointer

Device Button

Mouse

Keyboard

Key

Key Codes

Gamepad

					Community

Phaser Web Site

Phaser Github

Phaser Examples

Phaser Plugins

Forum

Stack Overflow

Tutorials

Newsletter

Twitter

Slack

Donate

Texture Packer

Source: src/utils/DOM.js

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

/**
* DOM utility class.
*
* Provides a useful Window and Element functions as well as cross-browser compatibility buffer.
*
* Some code originally derived from {@link https://github.com/ryanve/verge verge}.
* Some parts were inspired by the research of Ryan Van Etten, released under MIT License 2013.
*
* @class Phaser.DOM
* @static
*/
Phaser.DOM = {

    /**
    * Get the [absolute] position of the element relative to the Document.
    *
    * The value may vary slightly as the page is scrolled due to rounding errors.
    *
    * @method Phaser.DOM.getOffset
    * @param {DOMElement} element - The targeted element that we want to retrieve the offset.
    * @param {Phaser.Point} [point] - The point we want to take the x/y values of the offset.
    * @return {Phaser.Point} - A point objet with the offsetX and Y as its properties.
    */
    getOffset: function (element, point) {

        point = point || new Phaser.Point();

        var box = element.getBoundingClientRect();

        var scrollTop = Phaser.DOM.scrollY;
        var scrollLeft = Phaser.DOM.scrollX;
        var clientTop = document.documentElement.clientTop;
        var clientLeft = document.documentElement.clientLeft;

        point.x = box.left + scrollLeft - clientLeft;
        point.y = box.top + scrollTop - clientTop;

        return point;

    },

    /**
    * A cross-browser element.getBoundingClientRect method with optional cushion.
    *
    * Returns a plain object containing the properties `top/bottom/left/right/width/height` with respect to the top-left corner of the current viewport.
    * Its properties match the native rectangle.
    * The cushion parameter is an amount of pixels (+/-) to cushion the element.
    * It adjusts the measurements such that it is possible to detect when an element is near the viewport.
    *
    * @method Phaser.DOM.getBounds
    * @param {DOMElement|Object} element - The element or stack (uses first item) to get the bounds for.
    * @param {number} [cushion] - A +/- pixel adjustment amount.
    * @return {Object|boolean} A plain object containing the properties `top/bottom/left/right/width/height` or `false` if a non-valid element is given.
    */
    getBounds: function (element, cushion) {

        if (cushion === undefined) { cushion = 0; }

        element = element && !element.nodeType ? element[0] : element;

        if (!element || element.nodeType !== 1)
        {
            return false;
        }
        else
        {
            return this.calibrate(element.getBoundingClientRect(), cushion);
        }

    },

    /**
    * Calibrates element coordinates for `inLayoutViewport` checks.
    *
    * @method Phaser.DOM.calibrate
    * @private
    * @param {object} coords - An object containing the following properties: `{top: number, right: number, bottom: number, left: number}`
    * @param {number} [cushion] - A value to adjust the coordinates by.
    * @return {object} The calibrated element coordinates
    */
    calibrate: function (coords, cushion) {

        cushion = +cushion || 0;

        var output = { width: 0, height: 0, left: 0, right: 0, top: 0, bottom: 0 };

        output.width = (output.right = coords.right + cushion) - (output.left = coords.left - cushion);
        output.height = (output.bottom = coords.bottom + cushion) - (output.top = coords.top - cushion);

        return output;

    },

    /**
    * Get the Visual viewport aspect ratio (or the aspect ratio of an object or element)
    *
    * @method Phaser.DOM.getAspectRatio
    * @param {(DOMElement|Object)} [object=(visualViewport)] - The object to determine the aspect ratio for. Must have public `width` and `height` properties or methods.
    * @return {number} The aspect ratio.
    */
    getAspectRatio: function (object) {

        object = null == object ? this.visualBounds : 1 === object.nodeType ? this.getBounds(object) : object;

        var w = object['width'];
        var h = object['height'];

        if (typeof w === 'function')
        {
            w = w.call(object);
        }

        if (typeof h === 'function')
        {
            h = h.call(object);
        }

        return w / h;

    },

    /**
    * Tests if the given DOM element is within the Layout viewport.
    *
    * The optional cushion parameter allows you to specify a distance.
    *
    * inLayoutViewport(element, 100) is `true` if the element is in the viewport or 100px near it.
    * inLayoutViewport(element, -100) is `true` if the element is in the viewport or at least 100px near it.
    *
    * @method Phaser.DOM.inLayoutViewport
    * @param {DOMElement|Object} element - The DOM element to check. If no element is given it defaults to the Phaser game canvas.
    * @param {number} [cushion] - The cushion allows you to specify a distance within which the element must be within the viewport.
    * @return {boolean} True if the element is within the viewport, or within `cushion` distance from it.
    */
    inLayoutViewport: function (element, cushion) {

        var r = this.getBounds(element, cushion);

        return !!r && r.bottom >= 0 && r.right >= 0 && r.top <= this.layoutBounds.width && r.left <= this.layoutBounds.height;

    },

    /**
    * Returns the device screen orientation.
    *
    * Orientation values: 'portrait-primary', 'landscape-primary', 'portrait-secondary', 'landscape-secondary'.
    *
    * Order of resolving:
    * - Screen Orientation API, or variation of - Future track. Most desktop and mobile browsers.
    * - Screen size ratio check - If fallback is 'screen', suited for desktops.
    * - Viewport size ratio check - If fallback is 'viewport', suited for mobile.
    * - window.orientation - If fallback is 'window.orientation', works iOS and probably most Android; non-recommended track.
    * - Media query
    * - Viewport size ratio check (probably only IE9 and legacy mobile gets here..)
    *
    * See
    * - https://w3c.github.io/screen-orientation/ (conflicts with mozOrientation/msOrientation)
    * - https://developer.mozilla.org/en-US/docs/Web/API/Screen.orientation (mozOrientation)
    * - http://msdn.microsoft.com/en-us/library/ie/dn342934(v=vs.85).aspx
    * - https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Testing_media_queries
    * - http://stackoverflow.com/questions/4917664/detect-viewport-orientation
    * - http://www.matthewgifford.com/blog/2011/12/22/a-misconception-about-window-orientation
    *
    * @method Phaser.DOM.getScreenOrientation
    * @protected
    * @param {string} [primaryFallback=(none)] - Specify 'screen', 'viewport', or 'window.orientation'.
    */
    getScreenOrientation: function (primaryFallback) {

        var screen = window.screen;
        var orientation = screen.orientation || screen.mozOrientation || screen.msOrientation;

        if (orientation && typeof orientation.type === 'string')
        {
            // Screen Orientation API specification
            return orientation.type;
        }
        else if (typeof orientation === 'string')
        {
            // moz/ms-orientation are strings
            return orientation;
        }

        var PORTRAIT = 'portrait-primary';
        var LANDSCAPE = 'landscape-primary';

        if (primaryFallback === 'screen')
        {
            return (screen.height > screen.width) ? PORTRAIT : LANDSCAPE;
        }
        else if (primaryFallback === 'viewport')
        {
            return (this.visualBounds.height > this.visualBounds.width) ? PORTRAIT : LANDSCAPE;
        }
        else if (primaryFallback === 'window.orientation' && typeof window.orientation === 'number')
        {
            // This may change by device based on "natural" orientation.
            return (window.orientation === 0 || window.orientation === 180) ? PORTRAIT : LANDSCAPE;
        }
        else if (window.matchMedia)
        {
            if (window.matchMedia("(orientation: portrait)").matches)
            {
                return PORTRAIT;
            }
            else if (window.matchMedia("(orientation: landscape)").matches)
            {
                return LANDSCAPE;
            }
        }

        return (this.visualBounds.height > this.visualBounds.width) ? PORTRAIT : LANDSCAPE;

    },

    /**
    * The bounds of the Visual viewport, as discussed in
    * {@link http://www.quirksmode.org/mobile/viewports.html A tale of two viewports — part one}
    * with one difference: the viewport size _excludes_ scrollbars, as found on some desktop browsers.
    *
    * Supported mobile:
    *   iOS/Safari, Android 4, IE10, Firefox OS (maybe not Firefox Android), Opera Mobile 16
    *
    * The properties change dynamically.
    *
    * @type {Phaser.Rectangle}
    * @property {number} x - Scroll, left offset - eg. "scrollX"
    * @property {number} y - Scroll, top offset - eg. "scrollY"
    * @property {number} width - Viewport width in pixels.
    * @property {number} height - Viewport height in pixels.
    * @readonly
    */
    visualBounds: new Phaser.Rectangle(),

    /**
    * The bounds of the Layout viewport, as discussed in
    * {@link http://www.quirksmode.org/mobile/viewports2.html A tale of two viewports — part two};
    * but honoring the constraints as specified applicable viewport meta-tag.
    *
    * The bounds returned are not guaranteed to be fully aligned with CSS media queries (see
    * {@link http://www.matanich.com/2013/01/07/viewport-size/ What size is my viewport?}).
    *
    * This is _not_ representative of the Visual bounds: in particular the non-primary axis will
    * generally be significantly larger than the screen height on mobile devices when running with a
    * constrained viewport.
    *
    * The properties change dynamically.
    *
    * @type {Phaser.Rectangle}
    * @property {number} width - Viewport width in pixels.
    * @property {number} height - Viewport height in pixels.
    * @readonly
    */
    layoutBounds: new Phaser.Rectangle(),

    /**
    * The size of the document / Layout viewport.
    *
    * This incorrectly reports the dimensions in IE.
    *
    * The properties change dynamically.
    *
    * @type {Phaser.Rectangle}
    * @property {number} width - Document width in pixels.
    * @property {number} height - Document height in pixels.
    * @readonly
    */
    documentBounds: new Phaser.Rectangle()

};

Phaser.Device.whenReady(function (device) {

    // All target browsers should support page[XY]Offset.
    var scrollX = window && ('pageXOffset' in window) ?
        function () { return window.pageXOffset; } :
        function () { return document.documentElement.scrollLeft; };

    var scrollY = window && ('pageYOffset' in window) ?
        function () { return window.pageYOffset; } :
        function () { return document.documentElement.scrollTop; };

    /**
    * A cross-browser window.scrollX.
    *
    * @name Phaser.DOM.scrollX
    * @property {number} scrollX
    * @readonly
    * @protected
    */
    Object.defineProperty(Phaser.DOM, "scrollX", {
        get: scrollX
    });

    /**
    * A cross-browser window.scrollY.
    *
    * @name Phaser.DOM.scrollY
    * @property {number} scrollY
    * @readonly
    * @protected
    */
    Object.defineProperty(Phaser.DOM, "scrollY", {
        get: scrollY
    });

    Object.defineProperty(Phaser.DOM.visualBounds, "x", {
        get: scrollX
    });

    Object.defineProperty(Phaser.DOM.visualBounds, "y", {
        get: scrollY
    });

    Object.defineProperty(Phaser.DOM.layoutBounds, "x", {
        value: 0
    });

    Object.defineProperty(Phaser.DOM.layoutBounds, "y", {
        value: 0
    });

    var treatAsDesktop = device.desktop &&
        (document.documentElement.clientWidth <= window.innerWidth) &&
        (document.documentElement.clientHeight <= window.innerHeight);

    // Desktop browsers align the layout viewport with the visual viewport.
    // This differs from mobile browsers with their zooming design.
    // Ref. http://quirksmode.org/mobile/tableViewport.html
    if (treatAsDesktop)
    {

        // PST- When scrollbars are not included this causes upstream issues in ScaleManager.
        // So reverted to the old "include scrollbars."
        var clientWidth = function () {
            return Math.max(window.innerWidth, document.documentElement.clientWidth);
        };
        var clientHeight = function () {
            return Math.max(window.innerHeight, document.documentElement.clientHeight);
        };

        // Interested in area sans-scrollbar
        Object.defineProperty(Phaser.DOM.visualBounds, "width", {
            get: clientWidth
        });

        Object.defineProperty(Phaser.DOM.visualBounds, "height", {
            get: clientHeight
        });

        Object.defineProperty(Phaser.DOM.layoutBounds, "width", {
            get: clientWidth
        });

        Object.defineProperty(Phaser.DOM.layoutBounds, "height", {
            get: clientHeight
        });

    } else {

        Object.defineProperty(Phaser.DOM.visualBounds, "width", {
            get: function () {
                return window.innerWidth;
            }
        });

        Object.defineProperty(Phaser.DOM.visualBounds, "height", {
            get: function () {
                return window.innerHeight;
            }
        });

        Object.defineProperty(Phaser.DOM.layoutBounds, "width", {

            get: function () {
                var a = document.documentElement.clientWidth;
                var b = window.innerWidth;

                return a < b ? b : a; // max
            }

        });

        Object.defineProperty(Phaser.DOM.layoutBounds, "height", {

            get: function () {
                var a = document.documentElement.clientHeight;
                var b = window.innerHeight;

                return a < b ? b : a; // max
            }

        });

    }

    // For Phaser.DOM.documentBounds
    // Ref. http://www.quirksmode.org/mobile/tableViewport_desktop.html

    Object.defineProperty(Phaser.DOM.documentBounds, "x", {
        value: 0
    });

    Object.defineProperty(Phaser.DOM.documentBounds, "y", {
        value: 0
    });

    Object.defineProperty(Phaser.DOM.documentBounds, "width", {

        get: function () {
            var d = document.documentElement;
            return Math.max(d.clientWidth, d.offsetWidth, d.scrollWidth);
        }

    });

    Object.defineProperty(Phaser.DOM.documentBounds, "height", {

        get: function () {
            var d = document.documentElement;
            return Math.max(d.clientHeight, d.offsetHeight, d.scrollHeight);
        }

    });

}, null, true);

		Phaser Copyright © 2012-2016 Photon Storm Ltd.

		Documentation generated by JSDoc 3.4.0
		on Fri Aug 26 2016 01:16:09 GMT+0100 (BST) using the DocStrap template.


export default function (line1: any, line2: any, asSegment: any, out: any): any;


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

/**
* A Tile set is a combination of an image containing the tiles and collision data per tile.
*
* Tilesets are normally created automatically when Tiled data is loaded.
*
* @class Phaser.Tileset
* @constructor
* @param {string} name - The name of the tileset in the map data.
* @param {integer} firstgid - The first tile index this tileset contains.
* @param {integer} [width=32] - Width of each tile (in pixels).
* @param {integer} [height=32] - Height of each tile (in pixels).
* @param {integer} [margin=0] - The margin around all tiles in the sheet (in pixels).
* @param {integer} [spacing=0] - The spacing between each tile in the sheet (in pixels).
* @param {object} [properties={}] - Custom Tileset properties.
*/
Phaser.Tileset = function (name, firstgid, width, height, margin, spacing, properties) {

    if (width === undefined || width <= 0) { width = 32; }
    if (height === undefined || height <= 0) { height = 32; }
    if (margin === undefined) { margin = 0; }
    if (spacing === undefined) { spacing = 0; }

    /**
    * The name of the Tileset.
    * @property {string} name
    */
    this.name = name;

    /**
    * The Tiled firstgid value.
    * This is the starting index of the first tile index this Tileset contains.
    * @property {integer} firstgid
    */
    this.firstgid = firstgid | 0;

    /**
    * The width of each tile (in pixels).
    * @property {integer} tileWidth
    * @readonly
    */
    this.tileWidth = width | 0;

    /**
    * The height of each tile (in pixels).
    * @property {integer} tileHeight
    * @readonly
    */
    this.tileHeight = height | 0;

    /**
    * The margin around the tiles in the sheet (in pixels).
    * Use `setSpacing` to change.
    * @property {integer} tileMarge
    * @readonly
    */
    // Modified internally
    this.tileMargin = margin | 0;

    /**
    * The spacing between each tile in the sheet (in pixels).
    * Use `setSpacing` to change.
    * @property {integer} tileSpacing
    * @readonly
    */
    this.tileSpacing = spacing | 0;

    /**
    * Tileset-specific properties that are typically defined in the Tiled editor.
    * @property {object} properties
    */
    this.properties = properties || {};

    /**
    * The cached image that contains the individual tiles. Use {@link Phaser.Tileset.setImage setImage} to set.
    * @property {?object} image
    * @readonly
    */
    // Modified internally
    this.image = null;

    /**
    * The number of tile rows in the the tileset.
    * @property {integer}
    * @readonly
    */
    // Modified internally
    this.rows = 0;

    /**
    * The number of tile columns in the tileset.
    * @property {integer} columns
    * @readonly
    */
    // Modified internally
    this.columns = 0;

    /**
    * The total number of tiles in the tileset.
    * @property {integer} total
    * @readonly
    */
    // Modified internally
    this.total = 0;

    /**
    * The look-up table to specific tile image offsets.
    * The coordinates are interlaced such that it is [x0, y0, x1, y1 .. xN, yN] and the tile with the index of firstgid is found at indices 0/1.
    * @property {integer[]} drawCoords
    * @private
    */
    this.drawCoords = [];

};

Phaser.Tileset.prototype = {

    /**
    * Draws a tile from this Tileset at the given coordinates on the context.
    *
    * @method Phaser.Tileset#draw
    * @public
    * @param {CanvasRenderingContext2D} context - The context to draw the tile onto.
    * @param {number} x - The x coordinate to draw to.
    * @param {number} y - The y coordinate to draw to.
    * @param {integer} index - The index of the tile within the set to draw.
    */
    draw: function (context, x, y, index) {

        //  Correct the tile index for the set and bias for interlacing
        var coordIndex = (index - this.firstgid) << 1;

        if (coordIndex >= 0 && (coordIndex + 1) < this.drawCoords.length)
        {
            context.drawImage(
                this.image,
                this.drawCoords[coordIndex],
                this.drawCoords[coordIndex + 1],
                this.tileWidth,
                this.tileHeight,
                x,
                y,
                this.tileWidth,
                this.tileHeight
            );
        }

    },

    /**
    * Returns true if and only if this tileset contains the given tile index.
    *
    * @method Phaser.Tileset#containsTileIndex
    * @public
    * @return {boolean} True if this tileset contains the given index.
    */
    containsTileIndex: function (tileIndex) {

        return (
            tileIndex >= this.firstgid &&
            tileIndex < (this.firstgid + this.total)
        );

    },

    /**
    * Set the image associated with this Tileset and update the tile data.
    *
    * @method Phaser.Tileset#setImage
    * @public
    * @param {Image} image - The image that contains the tiles.
    */
    setImage: function (image) {

        this.image = image;
        this.updateTileData(image.width, image.height);

    },

    /**
    * Sets tile spacing and margins.
    *
    * @method Phaser.Tileset#setSpacing
    * @public
    * @param {integer} [margin=0] - The margin around the tiles in the sheet (in pixels).
    * @param {integer} [spacing=0] - The spacing between the tiles in the sheet (in pixels).
    */
    setSpacing: function (margin, spacing) {

        this.tileMargin = margin | 0;
        this.tileSpacing = spacing | 0;

        if (this.image)
        {
            this.updateTileData(this.image.width, this.image.height);
        }

    },

    /**
    * Updates tile coordinates and tileset data.
    *
    * @method Phaser.Tileset#updateTileData
    * @private
    * @param {integer} imageWidth - The (expected) width of the image to slice.
    * @param {integer} imageHeight - The (expected) height of the image to slice.
    */
    updateTileData: function (imageWidth, imageHeight) {

        // May be fractional values
        var rowCount = (imageHeight - this.tileMargin * 2 + this.tileSpacing) / (this.tileHeight + this.tileSpacing);
        var colCount = (imageWidth - this.tileMargin * 2 + this.tileSpacing) / (this.tileWidth + this.tileSpacing);

        if (rowCount % 1 !== 0 || colCount % 1 !== 0)
        {
            console.warn("Phaser.Tileset - " + this.name + " image tile area is not an even multiple of tile size");
        }

        // In Tiled a tileset image that is not an even multiple of the tile dimensions
        // is truncated - hence the floor when calculating the rows/columns.
        rowCount = Math.floor(rowCount);
        colCount = Math.floor(colCount);

        if ((this.rows && this.rows !== rowCount) || (this.columns && this.columns !== colCount))
        {
            console.warn("Phaser.Tileset - actual and expected number of tile rows and columns differ");
        }

        this.rows = rowCount;
        this.columns = colCount;
        this.total = rowCount * colCount;

        this.drawCoords.length = 0;

        var tx = this.tileMargin;
        var ty = this.tileMargin;

        for (var y = 0; y < this.rows; y++)
        {
            for (var x = 0; x < this.columns; x++)
            {
                this.drawCoords.push(tx);
                this.drawCoords.push(ty);
                tx += this.tileWidth + this.tileSpacing;
            }

            tx = this.tileMargin;
            ty += this.tileHeight + this.tileSpacing;
        }

    }

};

Phaser.Tileset.prototype.constructor = Phaser.Tileset;


// All cleanups then all effects (+4.5 B)

// 	component.__hooks._pendingLayoutEffects = [];

keep keep keep keep keep replace replace replace replace replace replace replace replace replace replace keep keep keep

import { setupRerender, act } from 'preact/test-utils';
import { createElement as h, render, Component } from 'preact';
import { setupScratch, teardown } from '../../../test/_util/helpers';
import { useState, useReducer, useEffect, useLayoutEffect, useRef } from '../../src';
import { scheduleEffectAssert } from '../_util/useEffectUtil';

/** @jsx h */

describe('combinations', () => {

	/** @type {HTMLDivElement} */
	let scratch;

	/** @type {() => void} */
	let rerender;

	beforeEach(() => {
		scratch = setupScratch();
		rerender = setupRerender();
	});

	afterEach(() => {
		teardown(scratch);
	});

	it('can mix useState hooks', () => {
		const states = {};
		const setStates = {};

		function Parent() {
			const [state1, setState1] = useState(1);
			const [state2, setState2] = useState(2);

			Object.assign(states, { state1, state2 });
			Object.assign(setStates, { setState1, setState2 });

			return <Child />;
		}

		function Child() {
			const [state3, setState3] = useState(3);
			const [state4, setState4] = useState(4);

			Object.assign(states, { state3, state4 });
			Object.assign(setStates, { setState3, setState4 });

			return null;
		}

		render(<Parent />, scratch);
		expect(states).to.deep.equal({ state1: 1, state2: 2, state3: 3, state4: 4 });

		setStates.setState2(n => n * 10);
		setStates.setState3(n => n * 10);
		rerender();
		expect(states).to.deep.equal({ state1: 1, state2: 20, state3: 30, state4: 4 });
	});

	it('can rerender asynchronously from within an effect', () => {
		const didRender = sinon.spy();

		function Comp() {
			const [counter, setCounter] = useState(0);

			useEffect(() => { if (counter === 0) setCounter(1); });

			didRender(counter);
			return null;
		}

		render(<Comp />, scratch);

		return scheduleEffectAssert(() => {
			rerender();
			expect(didRender).to.have.been.calledTwice.and.calledWith(1);
		});
	});

	it('can rerender synchronously from within a layout effect', () => {
		const didRender = sinon.spy();

		function Comp() {
			const [counter, setCounter] = useState(0);

			useLayoutEffect(() => { if (counter === 0) setCounter(1); });

			didRender(counter);
			return null;
		}

		render(<Comp />, scratch);
		rerender();

		expect(didRender).to.have.been.calledTwice.and.calledWith(1);
	});

	it('can access refs from within a layout effect callback', () => {
		let refAtLayoutTime;

		function Comp() {
			const input = useRef();

			useLayoutEffect(() => {
				refAtLayoutTime = input.current;
			});

			return <input ref={input} value="hello" />;
		}

		render(<Comp />, scratch);

		expect(refAtLayoutTime.value).to.equal('hello');
	});

	it('can use multiple useState and useReducer hooks', () => {
		let states = [];
		let dispatchState4;

		function reducer1(state, action) {
			switch (action.type) {
				case 'increment': return state + action.count;
			}
		}

		function reducer2(state, action) {
			switch (action.type) {
				case 'increment': return state + action.count * 2;
			}
		}

		function Comp() {
			const [state1] = useState(0);
			const [state2] = useReducer(reducer1, 10);
			const [state3] = useState(1);
			const [state4, dispatch] = useReducer(reducer2, 20);

			dispatchState4 = dispatch;
			states.push(state1, state2, state3, state4);

			return null;
		}

		render(<Comp />, scratch);

		expect(states).to.deep.equal([0, 10, 1, 20]);

		states = [];

		dispatchState4({ type: 'increment', count: 10 });
		rerender();

		expect(states).to.deep.equal([0, 10, 1, 40]);
	});

	it('ensures useEffect always schedule after the next paint following a redraw effect, when using the default debounce strategy', () => {
		let effectCount = 0;

		function Comp() {
			const [counter, setCounter] = useState(0);

			useEffect(() => {
				if (counter === 0) setCounter(1);
				effectCount++;
			});

			return null;
		}

		render(<Comp />, scratch);

		return scheduleEffectAssert(() => {
			expect(effectCount).to.equal(1);
		});
	});

	it('should not reuse functional components with hooks', () => {
		let updater = { first: undefined, second: undefined };
		function Foo(props) {
			let [v, setter] = useState(0);
			updater[props.id] = () => setter(++v);
			return
{v}
;
		}

		let updateParent;
		class App extends Component {
			constructor(props) {
				super(props);
				this.state = { active: true };
				updateParent = () => this.setState(p => ({ active: !p.active }));
			}

			render() {
				return (

						{this.state.active && <Foo id="first" />}
						<Foo id="second" />

				);
			}
		}

		render(<App />, scratch);
		act(() => updater.second());
		expect(scratch.textContent).to.equal('01');

		updateParent();
		rerender();
		expect(scratch.textContent).to.equal('1');

		updateParent();
		rerender();

		expect(scratch.textContent).to.equal('01');
	});

	it('should have a right call order with correct dom ref', () => {
		let i = 0, set;
		const calls = [];

		function Inner() {
			useLayoutEffect(() => {
				calls.push('layout inner call ' + scratch.innerHTML);
				return () => calls.push('layout inner dispose ' + scratch.innerHTML);
			});
			useEffect(() => {
				calls.push('effect inner call ' + scratch.innerHTML);
				return () => calls.push('effect inner dispose ' + scratch.innerHTML);
			});
			return hello {i};
		}

		function Outer() {
			i++;
			const [state, setState] = useState(false);
			set = () => setState(!state);
			useLayoutEffect(() => {
				calls.push('layout outer call ' + scratch.innerHTML);
				return () => calls.push('layout outer dispose ' + scratch.innerHTML);
			});
			useEffect(() => {
				calls.push('effect outer call ' + scratch.innerHTML);
				return () => calls.push('effect outer dispose ' + scratch.innerHTML);
			});
			return <Inner />;
		}

		act(() => render(<Outer />, scratch));
		expect(calls).to.deep.equal([
			'layout inner call hello 1',
			'layout outer call hello 1',
			'effect inner call hello 1',
			'effect outer call hello 1'
		]);

		act(() => set());
		expect(calls).to.deep.equal([
			'layout inner call hello 1',
			'layout outer call hello 1',
			'effect inner call hello 1',
			'effect outer call hello 1',
			'layout inner dispose hello 2',
			'layout outer dispose hello 2',
			'layout inner call hello 2',
			'layout outer call hello 2',
			'effect inner dispose hello 2',
			'effect outer dispose hello 2',
			'effect inner call hello 2',
			'effect outer call hello 2'
		]);
	});
});


import Compat from '../../src';
// eslint-disable-next-line no-duplicate-imports
import * as Named from '../../src';

describe('exports', () => {
	it('should have a default export', () => {
		expect(Compat.createElement).to.be.a('function');
		expect(Compat.Component).to.be.a('function');
		expect(Compat.Fragment).to.exist;
		expect(Compat.render).to.be.a('function');
		expect(Compat.hydrate).to.be.a('function');
		expect(Compat.cloneElement).to.be.a('function');
		expect(Compat.createContext).to.be.a('function');
		expect(Compat.createRef).to.be.a('function');

		// Hooks
		expect(Compat.useState).to.be.a('function');
		expect(Compat.useReducer).to.be.a('function');
		expect(Compat.useEffect).to.be.a('function');
		expect(Compat.useLayoutEffect).to.be.a('function');
		expect(Compat.useRef).to.be.a('function');
		expect(Compat.useMemo).to.be.a('function');
		expect(Compat.useCallback).to.be.a('function');
		expect(Compat.useContext).to.be.a('function');

		// Suspense
		expect(Compat.Suspense).to.be.a('function');
		expect(Compat.lazy).to.be.a('function');

		// Compat specific
		expect(Compat.PureComponent).to.exist.and.be.a('function');
		expect(Compat.createPortal).to.exist.and.be.a('function');
		expect(Compat.createFactory).to.exist.and.be.a('function');
		expect(Compat.isValidElement).to.exist.and.be.a('function');
		expect(Compat.findDOMNode).to.exist.and.be.a('function');
		expect(Compat.Children.map).to.exist.and.be.a('function');
		expect(Compat.Children.forEach).to.exist.and.be.a('function');
		expect(Compat.Children.count).to.exist.and.be.a('function');
		expect(Compat.Children.toArray).to.exist.and.be.a('function');
		expect(Compat.Children.only).to.exist.and.be.a('function');
		expect(Compat.unmountComponentAtNode).to.exist.and.be.a('function');
		expect(Compat.unstable_batchedUpdates).to.exist.and.be.a('function');
		expect(Compat.version).to.exist.and.be.a('string');
	});

	it('should have named exports', () => {
		expect(Named.createElement).to.be.a('function');
		expect(Named.Component).to.be.a('function');
		expect(Named.Fragment).to.exist;
		expect(Named.render).to.be.a('function');
		expect(Named.hydrate).to.be.a('function');
		expect(Named.cloneElement).to.be.a('function');
		expect(Named.createContext).to.be.a('function');
		expect(Named.createRef).to.be.a('function');

		// Hooks
		expect(Named.useState).to.be.a('function');
		expect(Named.useReducer).to.be.a('function');
		expect(Named.useEffect).to.be.a('function');
		expect(Named.useLayoutEffect).to.be.a('function');
		expect(Named.useRef).to.be.a('function');
		expect(Named.useMemo).to.be.a('function');
		expect(Named.useCallback).to.be.a('function');
		expect(Named.useContext).to.be.a('function');

		// Suspense
		expect(Named.Suspense).to.be.a('function');
		expect(Named.lazy).to.be.a('function');

		// Compat specific
		expect(Named.PureComponent).to.exist.and.be.a('function');
		expect(Named.createPortal).to.exist.and.be.a('function');
		expect(Named.createFactory).to.exist.and.be.a('function');
		expect(Named.isValidElement).to.exist.and.be.a('function');
		expect(Named.findDOMNode).to.exist.and.be.a('function');
		expect(Named.Children.map).to.exist.and.be.a('function');
		expect(Named.Children.forEach).to.exist.and.be.a('function');
		expect(Named.Children.count).to.exist.and.be.a('function');
		expect(Named.Children.toArray).to.exist.and.be.a('function');
		expect(Named.Children.only).to.exist.and.be.a('function');
		expect(Named.unmountComponentAtNode).to.exist.and.be.a('function');
		expect(Named.unstable_batchedUpdates).to.exist.and.be.a('function');
		expect(Named.version).to.exist.and.be.a('string');
	});
});


import { createElement as h, render } from '../../src/index';
import { setupScratch, teardown, sortAttributes } from '../_util/helpers';

/** @jsx h */

describe('svg', () => {
	let scratch;

	beforeEach(() => {
		scratch = setupScratch();
	});

	afterEach(() => {
		teardown(scratch);
	});

	it('should render SVG to string', () => {
		render((
			<svg viewBox="0 0 360 360">
				<path stroke="white" fill="black" d="M 347.1 357.9 L 183.3 256.5 L 13 357.9 V 1.7 h 334.1 v 356.2 Z M 58.5 47.2 v 231.4 l 124.8 -74.1 l 118.3 72.8 V 47.2 H 58.5 Z" />
			</svg>
		), scratch);

		let html = sortAttributes(String(scratch.innerHTML).replace(' xmlns="http://www.w3.org/2000/svg"', ''));
		expect(html).to.equal(sortAttributes(`
			<svg viewBox="0 0 360 360">
				<path d="M 347.1 357.9 L 183.3 256.5 L 13 357.9 V 1.7 h 334.1 v 356.2 Z M 58.5 47.2 v 231.4 l 124.8 -74.1 l 118.3 72.8 V 47.2 H 58.5 Z" fill="black" stroke="white"></path>
			</svg>
		`.replace(/[\n\t]+/g,'')));
	});

	it('should support svg attributes', () => {
		const Demo = ({ url }) => (
			<svg viewBox="0 0 360 360" xlinkHref={url}>
				<path d="M 347.1 357.9 L 183.3 256.5 L 13 357.9 V 1.7 h 334.1 v 356.2 Z M 58.5 47.2 v 231.4 l 124.8 -74.1 l 118.3 72.8 V 47.2 H 58.5 Z" fill="black" stroke="white" />
			</svg>
		);
		render(<Demo url="www.preact.com" />, scratch);

		let html = String(scratch.innerHTML).replace(' xmlns="http://www.w3.org/2000/svg"', '');
		html = sortAttributes(html.replace(' xmlns:xlink="http://www.w3.org/1999/xlink"', ''));
		expect(html).to.equal(sortAttributes(`
			<svg viewBox="0 0 360 360" xlink:href="www.preact.com">
				<path d="M 347.1 357.9 L 183.3 256.5 L 13 357.9 V 1.7 h 334.1 v 356.2 Z M 58.5 47.2 v 231.4 l 124.8 -74.1 l 118.3 72.8 V 47.2 H 58.5 Z" fill="black" stroke="white"></path>
			</svg>
		`.replace(/[\n\t]+/g,'')));
		render(<Demo />, scratch);

		html = String(scratch.innerHTML).replace(' xmlns="http://www.w3.org/2000/svg"', '');
		html = sortAttributes(html.replace(' xmlns:xlink="http://www.w3.org/1999/xlink"', ''));
		expect(html).to.equal(sortAttributes(`
			<svg viewBox="0 0 360 360">
				<path d="M 347.1 357.9 L 183.3 256.5 L 13 357.9 V 1.7 h 334.1 v 356.2 Z M 58.5 47.2 v 231.4 l 124.8 -74.1 l 118.3 72.8 V 47.2 H 58.5 Z" fill="black" stroke="white"></path>
			</svg>
		`.replace(/[\n\t]+/g,'')));
	});

	it('should render SVG to DOM', () => {
		const Demo = () => (
			<svg viewBox="0 0 360 360">
				<path d="M 347.1 357.9 L 183.3 256.5 L 13 357.9 V 1.7 h 334.1 v 356.2 Z M 58.5 47.2 v 231.4 l 124.8 -74.1 l 118.3 72.8 V 47.2 H 58.5 Z" fill="black" stroke="white" />
			</svg>
		);
		render(<Demo />, scratch);

		let html = sortAttributes(String(scratch.innerHTML).replace(' xmlns="http://www.w3.org/2000/svg"', ''));
		expect(html).to.equal(sortAttributes('<svg viewBox="0 0 360 360"><path stroke="white" fill="black" d="M 347.1 357.9 L 183.3 256.5 L 13 357.9 V 1.7 h 334.1 v 356.2 Z M 58.5 47.2 v 231.4 l 124.8 -74.1 l 118.3 72.8 V 47.2 H 58.5 Z"></path></svg>'));
	});

	it('should render with the correct namespace URI', () => {
		render(<svg />, scratch);

		let namespace = scratch.querySelector('svg').namespaceURI;

		expect(namespace).to.equal('http://www.w3.org/2000/svg');
	});

	it('should use attributes for className', () => {
		const Demo = ({ c }) => (
			<svg viewBox="0 0 360 360" {...(c ? { class: 'foo_'+c } : {})}>
				<path class={c && ('bar_'+c)} stroke="white" fill="black" d="M347.1 357.9L183.3 256.5 13 357.9V1.7h334.1v356.2zM58.5 47.2v231.4l124.8-74.1 118.3 72.8V47.2H58.5z" />
			</svg>
		);
		render(<Demo c="1" />, scratch);
		let root = scratch.firstChild;
		sinon.spy(root, 'removeAttribute');
		render(<Demo />, scratch);
		expect(root.removeAttribute)
			.to.have.been.calledOnce
			.and.calledWith('class');

		root.removeAttribute.restore();

		render(
, scratch);
		render(<Demo />, scratch);
		root = scratch.firstChild;
		sinon.spy(root, 'setAttribute');
		render(<Demo c="2" />, scratch);
		expect(root.setAttribute).to.have.been.calledOnce.and.calledWith('class', 'foo_2');

		root.setAttribute.restore();
	});

	it('should still support class attribute', () => {
		render((
			<svg viewBox="0 0 1 1" class="foo bar" />
		), scratch);

		expect(scratch.innerHTML).to.contain(` class="foo bar"`);
	});

	it('should still support className attribute', () => {
		render((
			<svg viewBox="0 0 1 1" className="foo bar" />
		), scratch);

		expect(scratch.innerHTML).to.contain(` class="foo bar"`);
	});

	it('should switch back to HTML for <foreignObject>', () => {
		render((
			<svg>
				<g>
					<foreignObject>
						test
					</foreignObject>
				</g>
			</svg>
		), scratch);

		expect(scratch.getElementsByTagName('a'))
			.to.have.property('0')
			.that.is.a('HTMLAnchorElement');
	});

	it('should render foreignObject as an svg element', () => {
		render((
			<svg>
				<g>
					<foreignObject>
						test
					</foreignObject>
				</g>
			</svg>
		), scratch);

		expect(scratch.querySelector('foreignObject').localName).to.equal('foreignObject');
	});

	it('should transition from DOM to SVG and back', () => {
		render((

				<svg id="svg1923" width="700" xmlns="http://www.w3.org/2000/svg" height="700">
					<circle cy="333" cx="333" r="333" />
					<circle cy="333" cx="333" r="333" fill="#fede58" />
				</svg>

		), scratch);

		expect(scratch.firstChild).to.be.an('HTMLDivElement');
		expect(scratch.firstChild.firstChild).to.be.an('SVGSVGElement');
	});
});


test-utils

Test-utils for Preact


keep keep keep keep keep replace replace replace replace replace replace replace replace replace replace replace replace keep keep keep keep keep

import { options } from 'preact';

/** @type {number} */
let currentIndex;

/** @type {import('./internal').Component} */
let currentComponent;

/** @type {Array<import('./internal').Component>} */
let afterPaintEffects = [];

/** @type {Array<import('./internal').Component>} */
let layoutEffects = [];

let oldBeforeRender = options._render;
options._render = vnode => {
	if (oldBeforeRender) oldBeforeRender(vnode);

	currentComponent = vnode._component;
	currentIndex = 0;

	if (currentComponent.__hooks) {
		currentComponent.__hooks._pendingEffects.forEach(invokeCleanup);
		currentComponent.__hooks._pendingEffects.forEach(invokeEffect);
		currentComponent.__hooks._pendingEffects = [];
	}
};

let oldAfterDiff = options.diffed;
options.diffed = vnode => {
	if (oldAfterDiff) oldAfterDiff(vnode);

	const c = vnode._component;
	if (!c) return;

	const hooks = c.__hooks;
	if (hooks) {
		if(hooks._pendingLayoutEffects.length) {
			layoutEffects.push(c);
		}

		if (hooks._pendingEffects.length) {
			afterPaint(afterPaintEffects.push(c));
		}
	}
};

let oldCommit = options._commit;
options._commit = vnode => {
	if (oldCommit) oldCommit(vnode);
	flushLayoutEffects();
}

let oldBeforeUnmount = options.unmount;
options.unmount = vnode => {
	if (oldBeforeUnmount) oldBeforeUnmount(vnode);

	const c = vnode._component;
	if (!c) return;

	const hooks = c.__hooks;
	if (hooks) {
		hooks._list.forEach(hook => hook._cleanup && hook._cleanup());
	}
};

/**
 * Get a hook's state from the currentComponent
 * @param {number} index The index of the hook to get
 * @returns {import('./internal').HookState}
 */
function getHookState(index) {
	if (options._hook) options._hook(currentComponent);
	// Largely inspired by:
	// * https://github.com/michael-klein/funcy.js/blob/f6be73468e6ec46b0ff5aa3cc4c9baf72a29025a/src/hooks/core_hooks.mjs
	// * https://github.com/michael-klein/funcy.js/blob/650beaa58c43c33a74820a3c98b3c7079cf2e333/src/renderer.mjs
	// Other implementations to look at:
	// * https://codesandbox.io/s/mnox05qp8
	const hooks = currentComponent.__hooks || (currentComponent.__hooks = { _list: [], _pendingEffects: [], _pendingLayoutEffects: [] });

	if (index >= hooks._list.length) {
		hooks._list.push({});
	}
	return hooks._list[index];
}

/**
 * @param {import('./index').StateUpdater<any>} initialState
 */
export function useState(initialState) {
	return useReducer(invokeOrReturn, initialState);
}

/**
 * @param {import('./index').Reducer<any, any>} reducer
 * @param {import('./index').StateUpdater<any>} initialState
 * @param {(initialState: any) => void} [init]
 * @returns {[ any, (state: any) => void ]}
 */
export function useReducer(reducer, initialState, init) {

	/** @type {import('./internal').ReducerHookState} */
	const hookState = getHookState(currentIndex++);
	if (!hookState._component) {
		hookState._component = currentComponent;

		hookState._value = [
			!init ? invokeOrReturn(undefined, initialState) : init(initialState),

			action => {
				const nextValue = reducer(hookState._value[0], action);
				if (hookState._value[0]!==nextValue) {
					hookState._value[0] = nextValue;
					hookState._component.setState({});
				}
			}
		];
	}

	return hookState._value;
}

/**
 * @param {import('./internal').Effect} callback
 * @param {any[]} args
 */
export function useEffect(callback, args) {

	/** @type {import('./internal').EffectHookState} */
	const state = getHookState(currentIndex++);
	if (argsChanged(state._args, args)) {
		state._value = callback;
		state._args = args;

		currentComponent.__hooks._pendingEffects.push(state);
	}
}

/**
 * @param {import('./internal').Effect} callback
 * @param {any[]} args
 */
export function useLayoutEffect(callback, args) {

	/** @type {import('./internal').EffectHookState} */
	const state = getHookState(currentIndex++);
	if (argsChanged(state._args, args)) {
		state._value = callback;
		state._args = args;

		currentComponent.__hooks._pendingLayoutEffects.push(state);
	}
}

export function useRef(initialValue) {
	return useMemo(() => ({ current: initialValue }), []);
}

/**
 * @param {object} ref
 * @param {() => object} createHandle
 * @param {any[]} args
 */
export function useImperativeHandle(ref, createHandle, args) {
	useLayoutEffect(() => {
		if (typeof ref === 'function') ref(createHandle());
		else if (ref) ref.current = createHandle();
	}, args == null ? args : args.concat(ref));
}

/**
 * @param {() => any} callback
 * @param {any[]} args
 */
export function useMemo(callback, args) {

	/** @type {import('./internal').MemoHookState} */
	const state = getHookState(currentIndex++);
	if (argsChanged(state._args, args)) {
		state._args = args;
		state._callback = callback;
		return state._value = callback();
	}

	return state._value;
}

/**
 * @param {() => void} callback
 * @param {any[]} args
 */
export function useCallback(callback, args) {
	return useMemo(() => callback, args);
}

/**
 * @param {import('./internal').PreactContext} context
 */
export function useContext(context) {
	const provider = currentComponent.context[context._id];
	if (!provider) return context._defaultValue;
	const state = getHookState(currentIndex++);
	// This is probably not safe to convert to "!"
	if (state._value == null) {
		state._value = true;
		provider.sub(currentComponent);
	}
	return provider.props.value;
}

/**
 * Display a custom label for a custom hook for the devtools panel
 * @type {<T>(value: T, cb?: (value: T) => string | number) => void}
 */
export function useDebugValue(value, formatter) {
	if (options.useDebugValue) {
		options.useDebugValue(formatter ? formatter(value) : value);
	}
}

// Note: if someone used Component.debounce = requestAnimationFrame,
// then effects will ALWAYS run on the NEXT frame instead of the current one, incurring a ~16ms delay.
// Perhaps this is not such a big deal.
/**
 * Schedule afterPaintEffects flush after the browser paints
 * @type {(newQueueLength: number) => void}
 */
/* istanbul ignore next */
let afterPaint = () => {};

/**
 * Layout effects consumer
 */
function flushLayoutEffects() {
	// All cleanups then all effects (+4.5 B)
	// layoutEffects.some(component => {
	// 	component.__hooks._pendingLayoutEffects.forEach(invokeCleanup);
	// });
	// layoutEffects.some(component => {
	// 	component.__hooks._pendingLayoutEffects.forEach(invokeEffect);
	// 	component.__hooks._pendingLayoutEffects = [];
	// });
	// layoutEffects = [];

	layoutEffects.some(component => {
		component.__hooks._pendingLayoutEffects.forEach(invokeCleanup);
		component.__hooks._pendingLayoutEffects.forEach(invokeEffect);
		component.__hooks._pendingLayoutEffects = [];
	});
	layoutEffects = [];
}

/**
 * After paint effects consumer.
 */
function flushAfterPaintEffects() {
	// All cleanups then all effects (+4.5 B)
	// afterPaintEffects.some(component => {
	// 	component.__hooks._pendingEffects.forEach(invokeCleanup);
	// });
	// afterPaintEffects.some(component => {
	// 	if (component._parentDom) {
	// 		component.__hooks._pendingEffects.forEach(invokeEffect);
	// 		component.__hooks._pendingEffects = [];
	// 	}
	// });
	// afterPaintEffects = [];

	afterPaintEffects.some(component => {
		if (component._parentDom) {
			component.__hooks._pendingEffects.forEach(invokeCleanup);
			component.__hooks._pendingEffects.forEach(invokeEffect);
			component.__hooks._pendingEffects = [];
		}
	});
	afterPaintEffects = [];
}

const RAF_TIMEOUT = 100;

/**
 * Schedule a callback to be invoked after the browser has a chance to paint a new frame.
 * Do this by combining requestAnimationFrame (rAF) + setTimeout to invoke a callback after
 * the next browser frame.
 *
 * Also, schedule a timeout in parallel to the the rAF to ensure the callback is invoked
 * even if RAF doesn't fire (for example if the browser tab is not visible)
 */
function afterNextFrame(callback) {
	const done = () => {
		clearTimeout(timeout);
		cancelAnimationFrame(raf);
		setTimeout(callback);
	};
	const timeout = setTimeout(done, RAF_TIMEOUT);
	const raf = requestAnimationFrame(done);
}

/* istanbul ignore else */
if (typeof window !== 'undefined') {
	let prevRaf = options.requestAnimationFrame;
	afterPaint = (newQueueLength) => {
		if (newQueueLength === 1 || prevRaf !== options.requestAnimationFrame) {
			prevRaf = options.requestAnimationFrame;

			/* istanbul ignore next */
			(prevRaf || afterNextFrame)(flushAfterPaintEffects);
		}
	};
}

/**
 * @param {import('./internal').EffectHookState} hook
 */
function invokeCleanup(hook) {
	if (hook._cleanup) hook._cleanup();
}

/**
 * Invoke a Hook's effect
 * @param {import('./internal').EffectHookState} hook
 */
function invokeEffect(hook) {
	const result = hook._value();
	if (typeof result === 'function') hook._cleanup = result;
}

/**
 * @param {any[]} oldArgs
 * @param {any[]} newArgs
 */
function argsChanged(oldArgs, newArgs) {
	return !oldArgs || newArgs.some((arg, index) => arg !== oldArgs[index]);
}

function invokeOrReturn(arg, f) {
	return typeof f === 'function' ? f(arg) : f;
}


import { createElement as h } from '../../src/index';
// import { VNode } from '../../src/vnode';
import { expect } from 'chai';

/*eslint-env browser, mocha */

/** @jsx h */

// const buildVNode = (nodeName, attributes, children=[]) => ({
// 	nodeName,
// 	children,
// 	attributes,
// 	key: attributes && attributes.key
// });

describe('createElement(jsx)', () => {
	it('should return a VNode', () => {
		let r;
		expect( () => r = h('foo') ).not.to.throw();
		expect(r).to.be.an('object');
		// expect(r).to.be.an.instanceof(VNode);
		expect(r).to.have.property('type', 'foo');
		expect(r).to.have.property('props').that.eql({});
		// expect(r).to.have.deep.property('props.children').that.eql(null);
	});

	it('should set VNode#type property', () => {
		expect(
).to.have.property('type', 'div');
		function Test() {
			return
;
		}
		expect(<Test />).to.have.property('type', Test);
	});

	it('should set VNode.constructor property to prevent json injection', () => {
		const vnode = ;
		expect(vnode.constructor).to.equal(undefined);
	});

	it('should set VNode#props property', () => {
		const props = {};
		expect(h('div', props).props).to.deep.equal(props);
	});

	it('should set VNode#key property', () => {
		expect(
).to.have.property('key').that.is.undefined;
		expect(
).to.have.property('key').that.is.undefined;
		expect(
).to.have.property('key', '1');
	});

	it('should not set VNode#props.key property', () => {
		expect(
).to.not.have.nested.property('props.key');
		expect(
).to.not.have.nested.property('props.key');
		expect(
).to.not.have.nested.property('props.key');
		expect(
).to.not.have.nested.property('props.key');
	});

	it('should set VNode#ref property', () => {
		expect(
).to.have.property('ref').that.is.undefined;
		expect(
).to.have.property('ref').that.is.undefined;
		const emptyFunction = () => {};
		expect(
).to.have.property('ref', emptyFunction);
	});

	it('should not set VNode#props.ref property', () => {
		expect(
).to.not.have.nested.property('props.ref');
		expect(
 {}} />).to.not.have.nested.property('props.ref');
	});

	it('should have ordered VNode properties', () => {
		expect(Object.keys(
).filter(key => !/^_/.test(key))).to.deep.equal(['type', 'props', 'key', 'ref', 'constructor']);
	});

	it('should preserve raw props', () => {
		let props = { foo: 'bar', baz: 10, func: () => {} },
			r = h('foo', props);
		expect(r).to.be.an('object')
			.with.property('props')
			.that.deep.equals(props);
	});

	it('should support element children', () => {
		let kid1 = h('bar');
		let kid2 = h('baz');
		let r = h(
			'foo',
			null,
			kid1,
			kid2
		);

		expect(r).to.be.an('object')
			.with.nested.deep.property('props.children', [
				kid1,
				kid2
			]);
	});

	it('should support multiple element children, given as arg list', () => {
		let kid1 = h('bar');
		let kid3 = h('test');
		let kid2 = h('baz', null, kid3);

		let r = h(
			'foo',
			null,
			kid1,
			kid2
		);

		expect(r).to.be.an('object')
			.with.nested.deep.property('props.children', [
				kid1,
				kid2
			]);
	});

	it('should handle multiple element children, given as an array', () => {
		let kid1 = h('bar');
		let kid3 = h('test');
		let kid2 = h('baz', null, kid3);

		let r = h(
			'foo',
			null,
			[
				kid1,
				kid2
			]
		);

		expect(r).to.be.an('object')
			.with.nested.deep.property('props.children', [
				kid1,
				kid2
			]);
	});

	it('should support nested children', () => {
		const m = x => h(x);
		expect(
			h('foo', null, m('a'), [m('b'), m('c')], m('d'))
		).to.have.nested.property('props.children')
			.that.eql([m('a'), [m('b'), m('c')], m('d')]);

		expect(
			h('foo', null, [m('a'), [m('b'), m('c')], m('d')])
		).to.have.nested.property('props.children')
			.that.eql([m('a'), [m('b'), m('c')], m('d')]);

		expect(
			h('foo', { children: [m('a'), [m('b'), m('c')], m('d')] })
		).to.have.nested.property('props.children')
			.that.eql([m('a'), [m('b'), m('c')], m('d')]);

		expect(
			h('foo', { children: [[m('a'), [m('b'), m('c')], m('d')]] })
		).to.have.nested.property('props.children')
			.that.eql([[m('a'), [m('b'), m('c')], m('d')]]);

		expect(
			h('foo', { children: m('a') })
		).to.have.nested.property('props.children').that.eql(m('a'));

		expect(
			h('foo', { children: 'a' })
		).to.have.nested.property('props.children').that.eql('a');
	});

	it('should support text children', () => {
		let r = h(
			'foo',
			null,
			'textstuff'
		);

		expect(r).to.be.an('object')
			.with.nested.property('props.children')
			.that.equals('textstuff');
	});

	it('should NOT merge adjacent text children', () => {
		let r = h(
			'foo',
			null,
			'one',
			'two',
			h('bar'),
			'three',
			h('baz'),
			h('baz'),
			'four',
			null,
			'five',
			'six'
		);

		expect(r).to.be.an('object')
			.with.nested.property('props.children')
			.that.deep.equals([
				'one',
				'two',
				h('bar'),
				'three',
				h('baz'),
				h('baz'),
				'four',
				null,
				'five',
				'six'
			]);
	});

	it('should not merge nested adjacent text children', () => {
		let r = h(
			'foo',
			null,
			'one',
			['two', null, 'three'],
			null,
			['four', null, 'five', null],
			'six',
			null
		);

		expect(r).to.be.an('object')
			.with.nested.property('props.children')
			.that.deep.equals([
				'one',
				['two', null, 'three'],
				null,
				['four', null, 'five', null],
				'six',
				null
			]);
	});
	it('should not merge children that are boolean values', () => {
		let r = h(
			'foo',
			null,
			'one',
			true,
			'two',
			false,
			'three'
		);

		expect(r).to.be.an('object')
			.with.nested.property('props.children')
			.that.deep.equals(['one',true,'two',false,'three']);
	});

	it('should not merge children of components', () => {
		let Component = ({ children }) => children;
		let r = h(Component, null, 'x', 'y');

		expect(r).to.be.an('object')
			.with.nested.property('props.children')
			.that.deep.equals(['x', 'y']);
	});

	it('should ignore props.children if children are manually specified', () => {
		expect(

c

		).to.eql(

c

		);
	});
});