/**
 * @author Mat Groves http://matgroves.com/ @Doormat23
 */

/**
* @class WebGLFilterManager
* @constructor
*/
PIXI.WebGLFilterManager = function()
{
    /**
     * @property filterStack
     * @type Array
     */
    this.filterStack = [];

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

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

PIXI.WebGLFilterManager.prototype.constructor = PIXI.WebGLFilterManager;

/**
* Initialises the context and the properties.
*
* @method setContext
* @param gl {WebGLContext} the current WebGL drawing context
*/
PIXI.WebGLFilterManager.prototype.setContext = function(gl)
{
    this.gl = gl;
    this.texturePool = [];

    this.initShaderBuffers();
};

/**
* @method begin
* @param renderSession {RenderSession}
* @param buffer {ArrayBuffer}
*/
PIXI.WebGLFilterManager.prototype.begin = function(renderSession, buffer)
{
    this.renderSession = renderSession;
    this.defaultShader = renderSession.shaderManager.defaultShader;

    var projection = this.renderSession.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
*/
PIXI.WebGLFilterManager.prototype.pushFilter = function(filterBlock)
{
    var gl = this.gl;

    var projection = this.renderSession.projection;
    var offset = this.renderSession.offset;

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

    // >>> modify by nextht
    filterBlock._previous_stencil_mgr = this.renderSession.stencilManager;
    this.renderSession.stencilManager = new PIXI.WebGLStencilManager();
    this.renderSession.stencilManager.setContext(gl);
    gl.disable(gl.STENCIL_TEST);
    // <<<  modify by nextht

    // filter program
    // OPTIMISATION - the first filter is free if its a simple color change?
    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 PIXI.FilterTexture(this.gl, this.width * this.renderSession.resolution, this.height * this.renderSession.resolution);
    }
    else
    {
        texture.resize(this.width * this.renderSession.resolution, this.height * this.renderSession.resolution);
    }

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

    var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea;

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

    // cap filter to screen size..
    if(filterArea.x < 0)filterArea.x = 0;
    if(filterArea.width > this.width)filterArea.width = this.width;
    if(filterArea.y < 0)filterArea.y = 0;
    if(filterArea.height > this.height)filterArea.height = this.height;

    //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,  filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
    gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer);

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

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

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

    // update projection
    // now restore the regular shader..
    // this.renderSession.shaderManager.setShader(this.defaultShader);
    //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2);
    //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -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
*/
PIXI.WebGLFilterManager.prototype.popFilter = function()
{
    var gl = this.gl;
    var filterBlock = this.filterStack.pop();
    var filterArea = filterBlock._filterArea;
    var texture = filterBlock._glFilterTexture;
    var projection = this.renderSession.projection;
    var offset = this.renderSession.offset;

    if(filterBlock.filterPasses.length > 1)
    {
        gl.viewport(0, 0, filterArea.width * this.renderSession.resolution, filterArea.height * this.renderSession.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);
        // now set the uvs..
        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 PIXI.FilterTexture(this.gl, this.width * this.renderSession.resolution, this.height * this.renderSession.resolution);
        outputTexture.resize(this.width * this.renderSession.resolution, this.height * this.renderSession.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..
            //filterPass.applyFilterPass(filterArea.width, filterArea.height);
            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);//this.transparent);
    }
    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;
    }

    // TODO need to remove these global elements..
    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.renderSession.resolution, sizeY * this.renderSession.resolution);

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

    // set the blend mode!
    //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)

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

    // >>> modify by nextht
    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);
    }
    // <<< modify by nextht

    // apply!
    this.applyFilterPass(filter, filterArea, sizeX, sizeY);

    // now restore the regular shader.. should happen automatically now..
    // this.renderSession.shaderManager.setShader(this.defaultShader);
    // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2);
    // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY);

    // return the texture to the pool
    this.texturePool.push(texture);
    filterBlock._glFilterTexture = null;
};

/**
* Applies the filter to the specified area.
*
* @method applyFilterPass
* @param filter {AbstractFilter} 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
*/
PIXI.WebGLFilterManager.prototype.applyFilterPass = function(filter, filterArea, width, height)
{
    // use program
    var gl = this.gl;
    var shader = filter.shaders[gl.id];

    if(!shader)
    {
        shader = new PIXI.PixiShader(gl);

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

        filter.shaders[gl.id] = shader;
    }

    // set the shader
    this.renderSession.shaderManager.setShader(shader);

//    gl.useProgram(shader.program);

    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;//width;
        filter.uniforms.dimensions.value[1] = this.height;//height;
        filter.uniforms.dimensions.value[2] = this.vertexArray[0];
        filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height;
    }

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

    // draw the filter...
    gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );

    this.renderSession.drawCount++;
};

/**
* Initialises the shader buffers.
*
* @method initShaderBuffers
*/
PIXI.WebGLFilterManager.prototype.initShaderBuffers = function()
{
    var gl = this.gl;

    // create some buffers
    this.vertexBuffer = gl.createBuffer();
    this.uvBuffer = gl.createBuffer();
    this.colorBuffer = gl.createBuffer();
    this.indexBuffer = gl.createBuffer();

    // bind and upload the vertexs..
    // keep a reference to the vertexFloatData..
    this.vertexArray = new PIXI.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 PIXI.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 PIXI.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);

};

/**
* Destroys the filter and removes it from the filter stack.
*
* @method destroy
*/
PIXI.WebGLFilterManager.prototype.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);
};


var wide = this.tileWide * 0.5 / screenWide2;

var high = this.tileHigh * 0.5 / screenHigh2;

keep keep keep replace replace keep keep keep keep

var wide = this.tileWidth * 0.5 / screenWide2;

var high = this.tileHeight * 0.5 / screenHigh2;

/**
* @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 stub for the Phaser ScaleManager.
* It allows you to exclude the default Scale Manager from your build, without making Game crash.
*/

Phaser.ScaleManager = function () {

    /**
    * The bounds of the scaled game. The x/y will match the offset of the canvas element and the width/height the scaled width and height.
    * @property {Phaser.Rectangle} bounds
    * @readonly
    */
    this.bounds = new Phaser.Rectangle();

};

Phaser.ScaleManager.prototype.boot = function () {};
Phaser.ScaleManager.prototype.preUpdate = function () {};
Phaser.ScaleManager.prototype.pauseUpdate = function () {};
Phaser.ScaleManager.prototype.destroy = function () {};

Phaser.ScaleManager.prototype.constructor = Phaser.ScaleManager;


keep keep keep keep keep replace keep keep keep keep keep

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

/**
 * A PIXI WebGL Tilemap.
 *
 * @class PIXI.Tilemap
 * @extends {PIXI.DisplayObject}
 * @param {PIXI.Texture} texture - The tilemap texture.
 * @param {integer} displayWidth - The width of the display area. Used as the clipping limit for the shader.
 * @param {integer} displayHeight - The height of the display area. Used as the clipping limit for the shader.
 * @param {integer} mapWidth - The width of the map.
 * @param {integer} mapHeight - The height of the map.
 * @param {integer} tileWidth - The width of a single tile.
 * @param {integer} tileHeight - The height of a single tile.
 * @param {Array} layer - Tilemap layer data from the map, arranged in mapHeight lists of mapWidth Phaser.Tile objects (2d array).
 */
PIXI.Tilemap = function (texture, displayWidth, displayHeight, mapWidth, mapHeight, tileWidth, tileHeight, layer) {

    PIXI.DisplayObject.call(this);

    /**
     * The texture of the Tilemap
     *
     * @property texture
     * @type Texture
     */
    this.texture = texture;

    /**
     * The clipping limit of the display area.
     *
     * @property _displayWidth
     * @type integer
     * @private
     */
    this._displayWidth = displayWidth;

    /**
     * The clipping limit of the display area.
     *
     * @property _displayHeight
     * @type integer
     * @private
     */
    this._displayHeight = displayHeight;

    /**
     * The width of a single tile in pixels.
     *
     * @property tileWide
     * @type integer
     */
    this.tileWide = tileWidth;

    /**
     * The height of a single tile in pixels.
     *
     * @property tileHigh
     * @type integer
     */
    this.tileHigh = tileHeight;

    /**
     * The width of the map in tiles.
     *
     * @property mapWide
     * @type integer
     */
    this.mapWide = mapWidth;

    /**
     * The height of the map in tiles.
     *
     * @property mapHigh
     * @type integer
     */
    this.mapHigh = mapHeight;

    /**
     * The width of the map in pixels.
     *
     * @property width
     * @type integer
     */
    this.width = this.mapWide * this.tileWide;

    /**
     * The height of the map in pixels.
     *
     * @property height
     * @type integer
     */
    this.height = this.mapHigh * this.tileHigh;

    /**
     * Tilemap layer data from the map, arranged in mapHeight lists of mapWidth tiles.
     * Contains Phaser.Tile objects (2d array).
     *
     * @property layer
     * @type Array
     */
    this.layer = layer;

    /**
     * Store the list of batch drawing instructions.
     *
     * @property glBatch
     * @type Array
     */
    this.glBatch = null;

    /**
     * Remember last tile drawn to avoid unnecessary set-up.
     *
     * @property lastTile
     * @type integer
     */
    this.lastTile = -1;

    /**
     * Whether the Tilemap is dirty or not.
     *
     * @property dirty
     * @type boolean
     */
    this.dirty = true;

    /**
     * The blend mode to be applied to the tilemap.
     * Set to PIXI.blendModes.NORMAL to remove any blend mode.
     *
     * @property blendMode
     * @type integer
     * @default PIXI.blendModes.NORMAL;
     */
    this.blendMode = PIXI.blendModes.NORMAL;

    /**
     * The size of a single data element in the batch drawing.
     * Each tile requires two triangles, each specified as:
     * float left, bottom, right, top - screen coordinates
     * float u, v, wide, high - source texture coordinates
     *
     * @property batchDataElement
     * @type integer
     */
    this.batchDataElement = 16;

    /**
     * Create the buffer data for the WebGL rendering of this tilemap.
     * Calculates the total batch data size.
     *
     * @property buffer
     * @type PIXI.Float32Array
     */
    this.buffer = new PIXI.Float32Array(mapWidth * mapHeight * this.batchDataElement);

    /**
     * A required but un-used array.
     * Do not add objects to this array, Tilemaps cannot have children.
     *
     * @property children
     * @type Array
     * @private
     */
    this.children = [];

};

PIXI.Tilemap.prototype = Object.create(PIXI.DisplayObject.prototype);
PIXI.Tilemap.prototype.constructor = PIXI.Tilemap;

PIXI.Tilemap.prototype.update = function () {};
PIXI.Tilemap.prototype.postUpdate = function () {};

// override PIXI.DisplayObjectContainer _renderWebGL
PIXI.Tilemap.prototype._renderWebGL = function (renderSession) {

    // if the sprite is not visible or the alpha is 0 then no need to render this element
    if (!this.visible || this.alpha <= 0)
    {
        return;
    }

    // stop current render session batch drawing
    renderSession.spriteBatch.stop();

    if (!this.positionBuffer)
    {
        this._initWebGL(renderSession);
    }

    renderSession.shaderManager.setShader(renderSession.shaderManager.tilemapShader);

    this._renderWholeTilemap(renderSession);

    // restart batch drawing now that this Tile layer has been rendered
    renderSession.spriteBatch.start();

};

PIXI.Tilemap.prototype._initWebGL = function (renderSession) {

    var gl = renderSession.gl;

    // create a GL buffer to transfer all the vertex position data through
    this.positionBuffer = gl.createBuffer();

    // bind the buffer to the RAM resident positionBuffer
    gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, this.buffer, gl.STATIC_DRAW);

};

PIXI.Tilemap.prototype._renderBatch = function (renderSession) {

    if (!this.glBatch)
    {
        return;
    }

    var gl = renderSession.gl;

    // TODO: should probably use destination buffer dimensions (halved)
    var screenWide2 = this.game.width * 0.5;
    var screenHigh2 = this.game.height * 0.5;

    // size of one pixel in the source texture
    var iTextureWide = 1.0 / this.texture.width;
    var iTextureHigh = 1.0 / this.texture.height;

    // pre-calculate inverse half-buffer dimensions
    var iWide = 1.0 / screenWide2;
    var iHigh = 1.0 / screenHigh2;

    var wide = this.tileWide * 0.5 / screenWide2;
    var high = this.tileHigh * 0.5 / screenHigh2;

    var buffer = this.buffer;
    var oldR, oldT, uvl, uvt;

    // process entire glBatch into a single webGl draw buffer for a TRIANGLE_STRIP blit
    var c = 0;
    var degenerate = false;

    for (var i = 0, l = this.glBatch.length; i < l; i++)
    {
        // each object in this.glBatch has properties:
        // sx: this.drawCoords[coordIndex],
        // sy: this.drawCoords[coordIndex + 1],
        // sw: this.tileWidth,
        // sh: this.tileHeight,
        // dx: x,
        // dy: y,
        // dw: this.tileWidth,
        // dh: this.tileHeight
        var t = this.glBatch[i];

        if (!t)
        {
            // insert a degenerate triangle when null is found in the list of batch objects
            degenerate = true;

            // skip to end of loop, degenerate will be inserted when no more null objects are found
            continue;
        }

        var x = t.dx * iWide - 1;
        var y = 1 - t.dy * iHigh;

        var lft = x - wide;
        var bot = y + high;

        var uvl = t.sx * iTextureWide;
        var uvt = t.sy * iTextureHigh;

        // insert a degenerate triangle to separate the tiles
        if (degenerate)
        {
            // add a degenerate triangle: repeat the last vertex
            buffer[ c     ] = oldR;
            buffer[ c + 1 ] = oldT;

            // then repeat the next vertex
            buffer[ c + 4 ] = lft;
            buffer[ c + 5 ] = bot;

            // pad with texture coordinates (probably not needed)
            buffer[ c + 2 ] = buffer[ c + 6 ] = uvl;
            buffer[ c + 3 ] = buffer[ c + 7 ] = uvt;

            // advance the buffer index for one single degenerate triangle
            c += 8;
            degenerate = false;
        }

        // calculate the destination location of the tile in screen units (-1..1)
        buffer[ c     ] = buffer[ c + 4  ] = lft;
        buffer[ c + 1 ] = buffer[ c + 9  ] = bot;
        buffer[ c + 8 ] = buffer[ c + 12 ] = oldR = x + wide;
        buffer[ c + 5 ] = buffer[ c + 13 ] = oldT = y - high;

        // calculate the uv coordinates of the tile source image
        if (t.fd === 1)
        {
            // flipped diagonally, swap x,y axes
            buffer[ c + 14 ] = buffer[ c + 6  ] = uvl;
            buffer[ c + 15 ] = buffer[ c + 11 ] = uvt;
            buffer[ c + 10 ] = buffer[ c + 2  ] = uvl + t.sw * iTextureWide;
            buffer[ c + 7  ] = buffer[ c + 3  ] = uvt + t.sh * iTextureHigh;
        }
        else
        {
            buffer[ c + 2  ] = buffer[ c + 6  ] = uvl;
            buffer[ c + 3  ] = buffer[ c + 11 ] = uvt;
            buffer[ c + 10 ] = buffer[ c + 14 ] = uvl + t.sw * iTextureWide;
            buffer[ c + 7  ] = buffer[ c + 15 ] = uvt + t.sh * iTextureHigh;
        }

        // advance the buffer index
        c += 16;
    }

    // if there's anything to draw...
    if (c > 0)
    {
        var shader = renderSession.shaderManager.tilemapShader;

        // upload the VBO
        gl.bufferData(gl.ARRAY_BUFFER, buffer, gl.STATIC_DRAW);

        // prepare the shader attributes
        gl.vertexAttribPointer(shader.aPosition, 4, gl.FLOAT, false, 0, 0);

        // draw the entire VBO in one call
        gl.drawArrays(gl.TRIANGLE_STRIP, 0, Math.floor(c / 4));
    }

};

/**
* Render the entire tilemap using a fast WebGL batched tile render.
*
* @param  {Object} renderSession - The PIXI RenderSession.
*/
PIXI.Tilemap.prototype._renderWholeTilemap = function (renderSession) {

    var gl = renderSession.gl;

    var shader = renderSession.shaderManager.tilemapShader;

    renderSession.blendModeManager.setBlendMode(this.blendMode);

    // set the uniforms and texture

    // set the global offset (e.g. screen shake)
    gl.uniform2f(shader.uOffset, renderSession.offset.x / this.game.width * 2, -renderSession.offset.y / this.game.height * 2);

    // set the clipping limits
    gl.uniform2f(shader.uClipOffset, this._offset.x / this.game.width * 2, this._offset.y / this.game.height * 2);
    gl.uniform2f(shader.uClipLoc, this._offset.x, this._offset.y);
    gl.uniform2f(shader.uClipLimit, this._offset.x + this._displayWidth, this.game.height - (this._offset.y + this._displayHeight));

    // set the offset in screen units to the center of the screen
    // and flip the GL y coordinate to be zero at the top
    gl.uniform2f(shader.uCentreOffset, 1, -1);

    // alpha value for whole batch
    gl.uniform1f(shader.uAlpha, this.alpha);

    // scale factors for whole batch
    gl.uniform2f(shader.uScale, this.worldScale.x, this.worldScale.y);

    // source texture unit
    gl.activeTexture(gl.TEXTURE0);

    // check if a texture is dirty...
    if(this.texture.baseTexture._dirty[gl.id])
    {
        renderSession.renderer.updateTexture(this.texture.baseTexture);
    }
    else
    {
        // bind the current texture
        gl.bindTexture(gl.TEXTURE_2D, this.texture.baseTexture._glTextures[gl.id]);
    }

    // bind the source buffer
    gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer);

    // draw the batched tile list
    this._renderBatch(renderSession);

};

/**
 * When the texture is updated, this event will fire to update the scale and frame
 *
 * @method onTextureUpdate
 * @param event
 * @private
PIXI.Tilemap.prototype.onTextureUpdate = function () {

    this.updateFrame = true;

};
 */


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

/**
* Creates a new Polygon.
*
* The points can be set from a variety of formats:
*
* - An array of Point objects: `[new Phaser.Point(x1, y1), ...]`
* - An array of objects with public x/y properties: `[obj1, obj2, ...]`
* - An array of paired numbers that represent point coordinates: `[x1,y1, x2,y2, ...]`
* - As separate Point arguments: `setTo(new Phaser.Point(x1, y1), ...)`
* - As separate objects with public x/y properties arguments: `setTo(obj1, obj2, ...)`
* - As separate arguments representing point coordinates: `setTo(x1,y1, x2,y2, ...)`
*
* @class Phaser.Polygon
* @constructor
* @param {Phaser.Point[]|number[]|...Phaser.Point|...number} points - The points to set.
*/
Phaser.Polygon = function () {

    /**
    * @property {number} area - The area of this Polygon.
    */
    this.area = 0;

    /**
    * @property {array} _points - An array of Points that make up this Polygon.
    * @private
    */
    this._points = [];

    if (arguments.length > 0)
    {
        this.setTo.apply(this, arguments);
    }

    /**
    * @property {boolean} closed - Is the Polygon closed or not?
    */
    this.closed = true;

    /**
    * @property {boolean} flattened - Has this Polygon been flattened by a call to `Polygon.flatten` ?
    */
    this.flattened = false;

    /**
     * @property {number} type - The base object type.
     */
    this.type = Phaser.POLYGON;

};

Phaser.Polygon.prototype = {

    /**
     * Export the points as an array of flat numbers, following the sequence [ x,y, x,y, x,y ]
     *
     * @method Phaser.Polygon#toNumberArray
     * @param {array} [output] - The array to append the points to. If not specified a new array will be created.
     * @return {array} The flattened array.
     */
    toNumberArray: function (output) {

        if (output === undefined) { output = []; }

        for (var i = 0; i < this._points.length; i++)
        {
            if (typeof this._points[i] === 'number')
            {
                output.push(this._points[i]);
                output.push(this._points[i + 1]);
                i++;
            }
            else
            {
                output.push(this._points[i].x);
                output.push(this._points[i].y);
            }
        }

        return output;

    },

    /**
     * Flattens this Polygon so the points are a sequence of numbers.
     * Any Point objects found are removed and replaced with two numbers.
     * Also sets the Polygon.flattened property to `true`.
     *
     * @method Phaser.Polygon#flatten
     * @return {Phaser.Polygon} This Polygon object
     */
    flatten: function () {

        this._points = this.toNumberArray();

        this.flattened = true;

        return this;

    },

    /**
     * Creates a copy of the given Polygon.
     * This is a deep clone, the resulting copy contains new Phaser.Point objects
     *
     * @method Phaser.Polygon#clone
     * @param {Phaser.Polygon} [output=(new Polygon)] - The polygon to update. If not specified a new polygon will be created.
     * @return {Phaser.Polygon} The cloned (`output`) polygon object.
     */
    clone: function (output) {

        var points = this._points.slice();

        if (output === undefined || output === null)
        {
            output = new Phaser.Polygon(points);
        }
        else
        {
            output.setTo(points);
        }

        return output;

    },

    /**
    * Checks whether the x and y coordinates are contained within this polygon.
    *
    * @method Phaser.Polygon#contains
    * @param {number} x - The X value of the coordinate to test.
    * @param {number} y - The Y value of the coordinate to test.
    * @return {boolean} True if the coordinates are within this polygon, otherwise false.
    */
    contains: function (x, y) {

        //  Adapted from http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html by Jonas Raoni Soares Silva

        var inside = false;

        if (this.flattened)
        {
            for (var i = -2, j = this._points.length - 2; (i += 2) < this._points.length; j = i)
            {
                var ix = this._points[i];
                var iy = this._points[i + 1];

                var jx = this._points[j];
                var jy = this._points[j + 1];

                if (((iy <= y && y < jy) || (jy <= y && y < iy)) && (x < (jx - ix) * (y - iy) / (jy - iy) + ix))
                {
                    inside = !inside;
                }
            }

        }
        else
        {
            for (var i = -1, j = this._points.length - 1; ++i < this._points.length; j = i)
            {
                var ix = this._points[i].x;
                var iy = this._points[i].y;

                var jx = this._points[j].x;
                var jy = this._points[j].y;

                if (((iy <= y && y < jy) || (jy <= y && y < iy)) && (x < (jx - ix) * (y - iy) / (jy - iy) + ix))
                {
                    inside = !inside;
                }
            }
        }

        return inside;

    },

    /**
     * Sets this Polygon to the given points.
     *
     * The points can be set from a variety of formats:
     *
     * - An array of Point objects: `[new Phaser.Point(x1, y1), ...]`
     * - An array of objects with public x/y properties: `[obj1, obj2, ...]`
     * - An array of paired numbers that represent point coordinates: `[x1,y1, x2,y2, ...]`
     * - An array of arrays with two elements representing x/y coordinates: `[[x1, y1], [x2, y2], ...]`
     * - As separate Point arguments: `setTo(new Phaser.Point(x1, y1), ...)`
     * - As separate objects with public x/y properties arguments: `setTo(obj1, obj2, ...)`
     * - As separate arguments representing point coordinates: `setTo(x1,y1, x2,y2, ...)`
     *
     * `setTo` may also be called without any arguments to remove all points.
     *
     * @method Phaser.Polygon#setTo
     * @param {Phaser.Point[]|number[]|...Phaser.Point|...number} points - The points to set.
     * @return {Phaser.Polygon} This Polygon object
     */
    setTo: function (points) {

        this.area = 0;
        this._points = [];

        if (arguments.length > 0)
        {
            //  If points isn't an array, use arguments as the array
            if (!Array.isArray(points))
            {
                points = Array.prototype.slice.call(arguments);
            }

            var y0 = Number.MAX_VALUE;

            //  Allows for mixed-type arguments
            for (var i = 0, len = points.length; i < len; i++)
            {
                if (typeof points[i] === 'number')
                {
                    var p = new PIXI.Point(points[i], points[i + 1]);
                    i++;
                }
                else if (Array.isArray(points[i]))
                {
                    var p = new PIXI.Point(points[i][0], points[i][1]);
                }
                else
                {
                    var p = new PIXI.Point(points[i].x, points[i].y);
                }

                this._points.push(p);

                //  Lowest boundary
                if (p.y < y0)
                {
                    y0 = p.y;
                }
            }

            this.calculateArea(y0);
        }

        return this;

    },

    /**
     * Calcuates the area of the Polygon. This is available in the property Polygon.area
     *
     * @method Phaser.Polygon#calculateArea
     * @private
     * @param {number} y0 - The lowest boundary
     * @return {number} The area of the Polygon.
     */
    calculateArea: function (y0) {

        var p1;
        var p2;
        var avgHeight;
        var width;

        for (var i = 0, len = this._points.length; i < len; i++)
        {
            p1 = this._points[i];

            if (i === len - 1)
            {
                p2 = this._points[0];
            }
            else
            {
                p2 = this._points[i + 1];
            }

            avgHeight = ((p1.y - y0) + (p2.y - y0)) / 2;
            width = p1.x - p2.x;
            this.area += avgHeight * width;
        }

        return this.area;

    }

};

Phaser.Polygon.prototype.constructor = Phaser.Polygon;

/**
* Sets and modifies the points of this polygon.
*
* See {@link Phaser.Polygon#setTo setTo} for the different kinds of arrays formats that can be assigned.
*
* @name Phaser.Polygon#points
* @property {Phaser.Point[]} points - The array of vertex points.
* @deprecated Use `setTo`.
*/
Object.defineProperty(Phaser.Polygon.prototype, 'points', {

    get: function() {
        return this._points;
    },

    set: function(points) {

        if (points != null)
        {
            this.setTo(points);
        }
        else
        {
            //  Clear the points
            this.setTo();
        }

    }

});

//  Because PIXI uses its own type, we'll replace it with ours to avoid duplicating code or confusion.
PIXI.Polygon = Phaser.Polygon;


/**
Default styles for some custom classes.
Change with theme as appropriate..
*/

.deprecated-notice {
    border: 2px solid rgb(255, 238, 238);
    padding: 10px;
    margin-bottom: 10px;
}

.toc-deprecated {
    text-decoration: line-through;
}

.returns-type {
    float: left;
    padding-right: 10px;
}

.returns-desc {
}

dt a {
    font-weight: normal;
}


/**
 * @author Mat Groves http://matgroves.com/ @Doormat23
 */

/**
 * A DisplayObjectContainer represents a collection of display objects.
 * It is the base class of all display objects that act as a container for other objects.
 *
 * @class DisplayObjectContainer
 * @extends DisplayObject
 * @constructor
 */
PIXI.DisplayObjectContainer = function()
{
    PIXI.DisplayObject.call(this);

    /**
     * [read-only] The array of children of this container.
     *
     * @property children
     * @type Array(DisplayObject)
     * @readOnly
     */
    this.children = [];

    /**
    * If `ignoreChildInput`  is `false` it will allow this objects _children_ to be considered as valid for Input events.
    *
    * If this property is `true` then the children will _not_ be considered as valid for Input events.
    *
    * Note that this property isn't recursive: only immediate children are influenced, it doesn't scan further down.
    * @property {boolean} ignoreChildInput
    * @default
    */
    this.ignoreChildInput = false;

};

// constructor
PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype );
PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer;

/**
 * The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set
 *
 * @property width
 * @type Number
 */
Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'width', {

    get: function() {
        return this.scale.x * this.getLocalBounds().width;
    },

    set: function(value) {

        var width = this.getLocalBounds().width;

        if (width !== 0)
        {
            this.scale.x = value / width;
        }
        else
        {
            this.scale.x = 1;
        }

        this._width = value;
    }
});

/**
 * The height of the displayObjectContainer, setting this will actually modify the scale to achieve the value set
 *
 * @property height
 * @type Number
 */
Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'height', {

    get: function() {
        return  this.scale.y * this.getLocalBounds().height;
    },

    set: function(value) {

        var height = this.getLocalBounds().height;

        if (height !== 0)
        {
            this.scale.y = value / height;
        }
        else
        {
            this.scale.y = 1;
        }

        this._height = value;
    }

});

/**
 * Adds a child to the container.
 *
 * @method addChild
 * @param child {DisplayObject} The DisplayObject to add to the container
 * @return {DisplayObject} The child that was added.
 */
PIXI.DisplayObjectContainer.prototype.addChild = function(child)
{
    return this.addChildAt(child, this.children.length);
};

/**
 * Adds a child to the container at a specified index. If the index is out of bounds an error will be thrown
 *
 * @method addChildAt
 * @param child {DisplayObject} The child to add
 * @param index {Number} The index to place the child in
 * @return {DisplayObject} The child that was added.
 */
PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index)
{
    if(index >= 0 && index <= this.children.length)
    {
        if(child.parent)
        {
            child.parent.removeChild(child);
        }

        child.parent = this;

        this.children.splice(index, 0, child);

        if(this.stage)child.setStageReference(this.stage);

        return child;
    }
    else
    {
        throw new Error(child + 'addChildAt: The index '+ index +' supplied is out of bounds ' + this.children.length);
    }
};

/**
 * Swaps the position of 2 Display Objects within this container.
 *
 * @method swapChildren
 * @param child {DisplayObject}
 * @param child2 {DisplayObject}
 */
PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2)
{
    if(child === child2) {
        return;
    }

    var index1 = this.getChildIndex(child);
    var index2 = this.getChildIndex(child2);

    if(index1 < 0 || index2 < 0) {
        throw new Error('swapChildren: Both the supplied DisplayObjects must be a child of the caller.');
    }

    this.children[index1] = child2;
    this.children[index2] = child;

};

/**
 * Returns the index position of a child DisplayObject instance
 *
 * @method getChildIndex
 * @param child {DisplayObject} The DisplayObject instance to identify
 * @return {Number} The index position of the child display object to identify
 */
PIXI.DisplayObjectContainer.prototype.getChildIndex = function(child)
{
    var index = this.children.indexOf(child);
    if (index === -1)
    {
        throw new Error('The supplied DisplayObject must be a child of the caller');
    }
    return index;
};

/**
 * Changes the position of an existing child in the display object container
 *
 * @method setChildIndex
 * @param child {DisplayObject} The child DisplayObject instance for which you want to change the index number
 * @param index {Number} The resulting index number for the child display object
 */
PIXI.DisplayObjectContainer.prototype.setChildIndex = function(child, index)
{
    if (index < 0 || index >= this.children.length)
    {
        throw new Error('The supplied index is out of bounds');
    }
    var currentIndex = this.getChildIndex(child);
    this.children.splice(currentIndex, 1); //remove from old position
    this.children.splice(index, 0, child); //add at new position
};

/**
 * Returns the child at the specified index
 *
 * @method getChildAt
 * @param index {Number} The index to get the child from
 * @return {DisplayObject} The child at the given index, if any.
 */
PIXI.DisplayObjectContainer.prototype.getChildAt = function(index)
{
    if (index < 0 || index >= this.children.length)
    {
        throw new Error('getChildAt: Supplied index '+ index +' does not exist in the child list, or the supplied DisplayObject must be a child of the caller');
    }
    return this.children[index];

};

/**
 * Removes a child from the container.
 *
 * @method removeChild
 * @param child {DisplayObject} The DisplayObject to remove
 * @return {DisplayObject} The child that was removed.
 */
PIXI.DisplayObjectContainer.prototype.removeChild = function(child)
{
    var index = this.children.indexOf( child );
    if(index === -1)return;

    return this.removeChildAt( index );
};

/**
 * Removes a child from the specified index position.
 *
 * @method removeChildAt
 * @param index {Number} The index to get the child from
 * @return {DisplayObject} The child that was removed.
 */
PIXI.DisplayObjectContainer.prototype.removeChildAt = function(index)
{
    var child = this.getChildAt( index );
    if(this.stage)
        child.removeStageReference();

    child.parent = undefined;
    this.children.splice( index, 1 );
    return child;
};

/**
* Removes all children from this container that are within the begin and end indexes.
*
* @method removeChildren
* @param beginIndex {Number} The beginning position. Default value is 0.
* @param endIndex {Number} The ending position. Default value is size of the container.
*/
PIXI.DisplayObjectContainer.prototype.removeChildren = function(beginIndex, endIndex)
{
    var begin = beginIndex || 0;
    var end = typeof endIndex === 'number' ? endIndex : this.children.length;
    var range = end - begin;

    if (range > 0 && range <= end)
    {
        var removed = this.children.splice(begin, range);
        for (var i = 0; i < removed.length; i++) {
            var child = removed[i];
            if(this.stage)
                child.removeStageReference();
            child.parent = undefined;
        }
        return removed;
    }
    else if (range === 0 && this.children.length === 0)
    {
        return [];
    }
    else
    {
        throw new Error( 'removeChildren: Range Error, numeric values are outside the acceptable range' );
    }
};

/*
 * Updates the transform on all children of this container for rendering
 *
 * @method updateTransform
 * @private
 */
PIXI.DisplayObjectContainer.prototype.updateTransform = function()
{
    if (!this.visible)
    {
        return;
    }

    this.displayObjectUpdateTransform();

    if (this._cacheAsBitmap)
    {
        return;
    }

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

// performance increase to avoid using call.. (10x faster)
PIXI.DisplayObjectContainer.prototype.displayObjectContainerUpdateTransform = PIXI.DisplayObjectContainer.prototype.updateTransform;

/**
 * Retrieves the bounds of the displayObjectContainer as a rectangle. The bounds calculation takes all visible children into consideration.
 *
 * @method getBounds
 * @return {Rectangle} The rectangular bounding area
 */
PIXI.DisplayObjectContainer.prototype.getBounds = function()
{
    if (this.children.length === 0)
    {
        return PIXI.EmptyRectangle;
    }

    this.updateTransform();

    var minX = Infinity;
    var minY = Infinity;

    var maxX = -Infinity;
    var maxY = -Infinity;

    var childBounds;
    var childMaxX;
    var childMaxY;

    var childVisible = false;

    for (var i = 0; i < this.children.length; i++)
    {
        var child = this.children[i];

        if (!child.visible)
        {
            continue;
        }

        childVisible = true;

        childBounds = this.children[i].getBounds();

        minX = minX < childBounds.x ? minX : childBounds.x;
        minY = minY < childBounds.y ? minY : childBounds.y;

        childMaxX = childBounds.width + childBounds.x;
        childMaxY = childBounds.height + childBounds.y;

        maxX = maxX > childMaxX ? maxX : childMaxX;
        maxY = maxY > childMaxY ? maxY : childMaxY;
    }

    if (!childVisible)
    {
        return PIXI.EmptyRectangle;
    }

    var bounds = this._bounds;

    bounds.x = minX;
    bounds.y = minY;
    bounds.width = maxX - minX;
    bounds.height = maxY - minY;

    return bounds;
};

/**
 * Retrieves the non-global local bounds of the displayObjectContainer as a rectangle. The calculation takes all visible children into consideration.
 *
 * @method getLocalBounds
 * @return {Rectangle} The rectangular bounding area
 */
PIXI.DisplayObjectContainer.prototype.getLocalBounds = function()
{
    var matrixCache = this.worldTransform;

    this.worldTransform = PIXI.identityMatrix;

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

    var bounds = this.getBounds();

    this.worldTransform = matrixCache;

    for (i = 0; i < this.children.length; i++)
    {
        this.children[i].updateTransform();
    }

    return bounds;
};

/**
 * Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to.
 *
 * @method setStageReference
 * @param stage {Stage} the stage that the container will have as its current stage reference
 */
PIXI.DisplayObjectContainer.prototype.setStageReference = function(stage)
{
    this.stage = stage;

    for (var i=0; i < this.children.length; i++)
    {
        this.children[i].setStageReference(stage)
    }
};

/**
 * Removes the current stage reference from the container and all of its children.
 *
 * @method removeStageReference
 */
PIXI.DisplayObjectContainer.prototype.removeStageReference = function()
{
    for (var i = 0; i < this.children.length; i++)
    {
        this.children[i].removeStageReference();
    }

    this.stage = null;
};

/**
* Renders the object using the WebGL renderer
*
* @method _renderWebGL
* @param renderSession {RenderSession}
* @private
*/
PIXI.DisplayObjectContainer.prototype._renderWebGL = function(renderSession)
{
    if (!this.visible || this.alpha <= 0) return;

    if (this._cacheAsBitmap)
    {
        this._renderCachedSprite(renderSession);
        return;
    }

    var i;

    if (this._mask || this._filters)
    {
        // push filter first as we need to ensure the stencil buffer is correct for any masking
        if (this._filters)
        {
            renderSession.spriteBatch.flush();
            renderSession.filterManager.pushFilter(this._filterBlock);
        }

        if (this._mask)
        {
            renderSession.spriteBatch.stop();
            renderSession.maskManager.pushMask(this.mask, renderSession);
            renderSession.spriteBatch.start();
        }

        // simple render children!
        for (i = 0; i < this.children.length; i++)
        {
            this.children[i]._renderWebGL(renderSession);
        }

        renderSession.spriteBatch.stop();

        if (this._mask) renderSession.maskManager.popMask(this._mask, renderSession);
        if (this._filters) renderSession.filterManager.popFilter();

        renderSession.spriteBatch.start();
    }
    else
    {
        // simple render children!
        for (i = 0; i < this.children.length; i++)
        {
            this.children[i]._renderWebGL(renderSession);
        }
    }
};

/**
* Renders the object using the Canvas renderer
*
* @method _renderCanvas
* @param renderSession {RenderSession}
* @private
*/
PIXI.DisplayObjectContainer.prototype._renderCanvas = function(renderSession)
{
    if (this.visible === false || this.alpha === 0) return;

    if (this._cacheAsBitmap)
    {
        this._renderCachedSprite(renderSession);
        return;
    }

    if (this._mask)
    {
        renderSession.maskManager.pushMask(this._mask, renderSession);
    }

    for (var i = 0; i < this.children.length; i++)
    {
        this.children[i]._renderCanvas(renderSession);
    }

    if (this._mask)
    {
        renderSession.maskManager.popMask(renderSession);
    }

};


keep keep keep replace keep keep keep keep


PIXI.TilingSprite

A tiling sprite is a fast way of rendering a tiling image

addChild

child

The DisplayObject to add to the container

addChildAt

Adds a child to the container at a specified index. If the index is out of bounds an error will be thrown

click

A callback that is used when the users clicks on the displayObject with their mouse's left button

fromFrame

A new Sprite using a texture from the texture cache matching the frameId

\nHelper function that creates a sprite that will contain a texture from the TextureCache based on the frameId\nThe frame ids are created when a Texture packer file has been loaded

frameId

The frame Id of the texture in the cache

fromImage

A new Sprite using a texture from the texture cache matching the image id

\nHelper function that creates a sprite that will contain a texture based on an image url\nIf the image is not in the texture cache it will be loaded

generateTexture

Useful function that returns a texture of the displayObject object that can then be used to create sprites\nThis can be quite useful if your displayObject is static / complicated and needs to be reused multiple times.

resolution

The resolution of the texture being generated

scaleMode

Should be one of the PIXI.scaleMode consts

renderer

The renderer used to generate the texture.

generateTilingTexture

forcePowerOfTwo

Whether we want to force the texture to be a power of two

getBounds

Returns the framing rectangle of the sprite as a PIXI.Rectangle object

getChildAt

Returns the child at the specified index

getChildIndex

The index position of the child display object to identify

Returns the index position of a child DisplayObject instance

getLocalBounds

Retrieves the non-global local bounds of the displayObjectContainer as a rectangle. The calculation takes all visible children into consideration.

mousedown

A callback that is used when the user clicks the mouse's left button down over the sprite

mouseout

A callback that is used when the users mouse leaves the displayObject

mouseover

A callback that is used when the users mouse rolls over the displayObject

mouseup

A callback that is used when the user releases the mouse's left button that was over the displayObject\nfor this callback to be fired, the mouse's left button must have been pressed down over the displayObject

mouseupoutside

A callback that is used when the user releases the mouse's left button that was over the displayObject but is no longer over the displayObject\nfor this callback to be fired, the mouse's left button must have been pressed down over the displayObject

removeChildAt

Removes a child from the specified index position.

removeChildren

Removes all children from this container that are within the begin and end indexes.

beginIndex

The beginning position. Default value is 0.

endIndex

The ending position. Default value is size of the container.

removeStageReference

Removes the current stage reference from the container and all of its children.

rightclick

A callback that is used when the users clicks on the displayObject with their mouse's right button

rightdown

A callback that is used when the user clicks the mouse's right button down over the sprite

rightup

A callback that is used when the user releases the mouse's right button that was over the displayObject\nfor this callback to be fired the mouse's right button must have been pressed down over the displayObject

rightupoutside

A callback that is used when the user releases the mouse's right button that was over the displayObject but is no longer over the displayObject\nfor this callback to be fired, the mouse's right button must have been pressed down over the displayObject

setChildIndex

Changes the position of an existing child in the display object container

child

The child DisplayObject instance for which you want to change the index number

index

The resulting index number for the child display object

setStageReference

Sets the containers Stage reference. This is the Stage that this object, and all of its children, is connected to.

stage

the stage that the container will have as its current stage reference

setTexture

texture

The PIXI texture that is displayed by the sprite

swapChildren

Swaps the position of 2 Display Objects within this container.

tap

A callback that is used when the users taps on the sprite with their finger\nbasically a touch version of click

toGlobal

A point object representing the position of this object

Calculates the global position of the display object

toLocal

Calculates the local position of the display object relative to another point

[from]

The DisplayObject to calculate the global position from

touchend

A callback that is used when the user releases a touch over the displayObject

touchendoutside

A callback that is used when the user releases the touch that was over the displayObject\nfor this callback to be fired, The touch must have started over the sprite

touchstart

A callback that is used when the user touches over the displayObject

updateCache

Generates and updates the cached sprite for this object.

Renders the object using the Canvas renderer

Renders the object using the WebGL renderer

onTextureUpdate

When the texture is updated, this event will fire to update the scale and frame

anchor

The anchor sets the origin point of the texture.\nThe default is 0,0 this means the texture's origin is the top left\nSetting than anchor to 0.5,0.5 means the textures origin is centered\nSetting the anchor to 1,1 would mean the textures origin points will be the bottom right corner

blendMode

The blend mode to be applied to the sprite

buttonMode

This is used to indicate if the displayObject should display a mouse hand cursor on rollover

cacheAsBitmap

Set if this display object is cached as a bitmap.\nThis basically takes a snap shot of the display object as it is at that moment. It can provide a performance benefit for complex static displayObjects.\nTo remove simply set this property to 'null'

children

[read-only] The array of children of this container.

defaultCursor

This is the cursor that will be used when the mouse is over this object. To enable this the element must have interaction = true and buttonMode = true\n

filterArea

The area the filter is applied to like the hitArea this is used as more of an optimisation\nrather than figuring out the dimensions of the displayObject each frame you can set this rectangle

filters

Sets the filters for the displayObject.\n* IMPORTANT: This is a webGL only feature and will be ignored by the canvas renderer.\nTo remove filters simply set this property to 'null'

height

The height of the TilingSprite, setting this will actually modify the scale to achieve the value set

hitArea

This is the defined area that will pick up mouse / touch events. It is null by default.\nSetting it is a neat way of optimising the hitTest function that the interactionManager will use (as it will not need to hit test all the children)

interactive

Indicates if the sprite will have touch and mouse interactivity. It is false by default

mask

Sets a mask for the displayObject. A mask is an object that limits the visibility of an object to the shape of the mask applied to it.\nIn PIXI a regular mask must be a PIXI.Graphics object. This allows for much faster masking in canvas as it utilises shape clipping.\nTo remove a mask, set this property to null.

parent

[read-only] The display object container that contains this display object.

pivot

The pivot point of the displayObject that it rotates around

position

The coordinate of the object relative to the local coordinates of the parent.

renderable

Whether this sprite is renderable or not

shader

The shader that will be used to render the texture to the stage. Set to null to remove a current shader.

stage

[read-only] The stage the display object is connected to, or undefined if it is not connected to the stage.

tilePosition

The offset position of the image that is being tiled

tileScale

The scaling of the image that is being tiled

tileScaleOffset

A point that represents the scale of the texture object

tint

The tint applied to the sprite. This is a hex value

width

The width of the sprite, setting this will actually modify the scale to achieve the value set

worldAlpha

[read-only] The multiplied alpha of the displayObject

worldVisible

[read-only] Indicates if the sprite is globally visible.

x

The position of the displayObject on the x axis relative to the local coordinates of the parent.

y

The position of the displayObject on the y axis relative to the local coordinates of the parent.


/*!
 * jQuery.ScrollTo
 * Copyright (c) 2007-2013 Ariel Flesler - aflesler<a>gmail<d>com | http://flesler.blogspot.com
 * Dual licensed under MIT and GPL.
 *
 * @projectDescription Easy element scrolling using jQuery.
 * http://flesler.blogspot.com/2007/10/jqueryscrollto.html
 * @author Ariel Flesler
 * @version 1.4.5
 *
 * @id jQuery.scrollTo
 * @id jQuery.fn.scrollTo
 * @param {String, Number, DOMElement, jQuery, Object} target Where to scroll the matched elements.
 *	  The different options for target are:
 *		- A number position (will be applied to all axes).
 *		- A string position ('44', '100px', '+=90', etc ) will be applied to all axes
 *		- A jQuery/DOM element ( logically, child of the element to scroll )
 *		- A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc )
 *		- A hash { top:x, left:y }, x and y can be any kind of number/string like above.
 *		- A percentage of the container's dimension/s, for example: 50% to go to the middle.
 *		- The string 'max' for go-to-end.
 * @param {Number, Function} duration The OVERALL length of the animation, this argument can be the settings object instead.
 * @param {Object,Function} settings Optional set of settings or the onAfter callback.
 *	 @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'.
 *	 @option {Number, Function} duration The OVERALL length of the animation.
 *	 @option {String} easing The easing method for the animation.
 *	 @option {Boolean} margin If true, the margin of the target element will be deducted from the final position.
 *	 @option {Object, Number} offset Add/deduct from the end position. One number for both axes or { top:x, left:y }.
 *	 @option {Object, Number} over Add/deduct the height/width multiplied by 'over', can be { top:x, left:y } when using both axes.
 *	 @option {Boolean} queue If true, and both axis are given, the 2nd axis will only be animated after the first one ends.
 *	 @option {Function} onAfter Function to be called after the scrolling ends.
 *	 @option {Function} onAfterFirst If queuing is activated, this function will be called after the first scrolling ends.
 * @return {jQuery} Returns the same jQuery object, for chaining.
 *
 * @desc Scroll to a fixed position
 * @example $('div').scrollTo( 340 );
 *
 * @desc Scroll relatively to the actual position
 * @example $('div').scrollTo( '+=340px', { axis:'y' } );
 *
 * @desc Scroll using a selector (relative to the scrolled element)
 * @example $('div').scrollTo( 'p.paragraph:eq(2)', 500, { easing:'swing', queue:true, axis:'xy' } );
 *
 * @desc Scroll to a DOM element (same for jQuery object)
 * @example var second_child = document.getElementById('container').firstChild.nextSibling;
 *			$('#container').scrollTo( second_child, { duration:500, axis:'x', onAfter:function(){
 *				alert('scrolled!!');
 *			}});
 *
 * @desc Scroll on both axes, to different values
 * @example $('div').scrollTo( { top: 300, left:'+=200' }, { axis:'xy', offset:-20 } );
 */

;(function( $ ){

	var $scrollTo = $.scrollTo = function( target, duration, settings ){
		$(window).scrollTo( target, duration, settings );
	};

	$scrollTo.defaults = {
		axis:'xy',
		duration: parseFloat($.fn.jquery) >= 1.3 ? 0 : 1,
		limit:true
	};

	// Returns the element that needs to be animated to scroll the window.
	// Kept for backwards compatibility (specially for localScroll & serialScroll)
	$scrollTo.window = function( scope ){
		return $(window)._scrollable();
	};

	// Hack, hack, hack :)
	// Returns the real elements to scroll (supports window/iframes, documents and regular nodes)
	$.fn._scrollable = function(){
		return this.map(function(){
			var elem = this,
				isWin = !elem.nodeName || $.inArray( elem.nodeName.toLowerCase(), ['iframe','#document','html','body'] ) != -1;

				if( !isWin )
					return elem;

			var doc = (elem.contentWindow || elem).document || elem.ownerDocument || elem;

			return /webkit/i.test(navigator.userAgent) || doc.compatMode == 'BackCompat' ?
				doc.body :
				doc.documentElement;
		});
	};

	$.fn.scrollTo = function( target, duration, settings ){
		if( typeof duration == 'object' ){
			settings = duration;
			duration = 0;
		}
		if( typeof settings == 'function' )
			settings = { onAfter:settings };

		if( target == 'max' )
			target = 9e9;

		settings = $.extend( {}, $scrollTo.defaults, settings );
		// Speed is still recognized for backwards compatibility
		duration = duration || settings.duration;
		// Make sure the settings are given right
		settings.queue = settings.queue && settings.axis.length > 1;

		if( settings.queue )
			// Let's keep the overall duration
			duration /= 2;
		settings.offset = both( settings.offset );
		settings.over = both( settings.over );

		return this._scrollable().each(function(){
			// Null target yields nothing, just like jQuery does
			if (target == null) return;

			var elem = this,
				$elem = $(elem),
				targ = target, toff, attr = {},
				win = $elem.is('html,body');

			switch( typeof targ ){
				// A number will pass the regex
				case 'number':
				case 'string':
					if( /^([+-]=?)?\d+(\.\d+)?(px|%)?$/.test(targ) ){
						targ = both( targ );
						// We are done
						break;
					}
					// Relative selector, no break!
					targ = $(targ,this);
					if (!targ.length) return;
				case 'object':
					// DOMElement / jQuery
					if( targ.is || targ.style )
						// Get the real position of the target
						toff = (targ = $(targ)).offset();
			}
			$.each( settings.axis.split(''), function( i, axis ){
				var Pos	= axis == 'x' ? 'Left' : 'Top',
					pos = Pos.toLowerCase(),
					key = 'scroll' + Pos,
					old = elem[key],
					max = $scrollTo.max(elem, axis);

				if( toff ){// jQuery / DOMElement
					attr[key] = toff[pos] + ( win ? 0 : old - $elem.offset()[pos] );

					// If it's a dom element, reduce the margin
					if( settings.margin ){
						attr[key] -= parseInt(targ.css('margin'+Pos)) || 0;
						attr[key] -= parseInt(targ.css('border'+Pos+'Width')) || 0;
					}

					attr[key] += settings.offset[pos] || 0;

					if( settings.over[pos] )
						// Scroll to a fraction of its width/height
						attr[key] += targ[axis=='x'?'width':'height']() * settings.over[pos];
				}else{
					var val = targ[pos];
					// Handle percentage values
					attr[key] = val.slice && val.slice(-1) == '%' ?
						parseFloat(val) / 100 * max
						: val;
				}

				// Number or 'number'
				if( settings.limit && /^\d+$/.test(attr[key]) )
					// Check the limits
					attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max );

				// Queueing axes
				if( !i && settings.queue ){
					// Don't waste time animating, if there's no need.
					if( old != attr[key] )
						// Intermediate animation
						animate( settings.onAfterFirst );
					// Don't animate this axis again in the next iteration.
					delete attr[key];
				}
			});

			animate( settings.onAfter );

			function animate( callback ){
				$elem.animate( attr, duration, settings.easing, callback && function(){
					callback.call(this, target, settings);
				});
			};

		}).end();
	};

	// Max scrolling position, works on quirks mode
	// It only fails (not too badly) on IE, quirks mode.
	$scrollTo.max = function( elem, axis ){
		var Dim = axis == 'x' ? 'Width' : 'Height',
			scroll = 'scroll'+Dim;

		if( !$(elem).is('html,body') )
			return elem[scroll] - $(elem)[Dim.toLowerCase()]();

		var size = 'client' + Dim,
			html = elem.ownerDocument.documentElement,
			body = elem.ownerDocument.body;

		return Math.max( html[scroll], body[scroll] )
			 - Math.min( html[size]  , body[size]   );
	};

	function both( val ){
		return typeof val == 'object' ? val : { top:val, left:val };
	};

})( jQuery );


setAttribs

Takes the attributes given in parameters.

setContext

Initialises the context and the properties.


it('should be called when rerender with new props from parent', () => {

import { EMPTY_OBJ, EMPTY_ARR } from '../constants';
import { Component, enqueueRender } from '../component';
import { coerceToVNode, Fragment } from '../create-element';
import { diffChildren } from './children';
import { diffProps } from './props';
import { assign } from '../util';
import options from '../options';

/**
 * Diff two virtual nodes and apply proper changes to the DOM
 * @param {import('../internal').PreactElement | Text} dom The DOM element representing
 * the virtual nodes under diff
 * @param {import('../internal').PreactElement} parentDom The parent of the DOM element
 * @param {import('../internal').VNode | null} newVNode The new virtual node
 * @param {import('../internal').VNode | null} oldVNode The old virtual node
 * @param {object} context The current context object
 * @param {boolean} isSvg Whether or not this element is an SVG node
 * @param {boolean} append Whether or not to immediately append the new DOM
 * element after diffing
 * @param {Array<import('../internal').PreactElement>} excessDomChildren
 * @param {Array<import('../internal').Component>} mounts A list of newly
 * mounted components
 * @param {import('../internal').Component | null} ancestorComponent The direct
 * parent component
 * @param {import('../internal').VNode} parentVNode Used to set `_lastDomChild`
 * pointer to keep track of our current position
 */
export function diff(dom, parentDom, newVNode, oldVNode, context, isSvg, append, excessDomChildren, mounts, ancestorComponent, parentVNode) {

	// If the previous type doesn't match the new type we drop the whole subtree
	if (oldVNode==null || newVNode==null || oldVNode.type!==newVNode.type) {
		if (oldVNode!=null) unmount(oldVNode, ancestorComponent);
		if (newVNode==null) return null;
		dom = null;
		oldVNode = EMPTY_OBJ;
	}

	if (options.beforeDiff) options.beforeDiff(newVNode);

	let c, p, isNew = false, oldProps, oldState, oldContext,
		newType = newVNode.type, lastDomChild;

	/** @type {import('../internal').Component | null} */
	let clearProcessingException;

	try {
		const isOldVNodeFragment = oldVNode.type===Fragment;

		outer: if (isOldVNodeFragment || newType===Fragment) {
			const oldVNodeChildren = oldVNode===EMPTY_OBJ ? EMPTY_ARR : !isOldVNodeFragment ? [oldVNode] : getVNodeChildren(oldVNode);

			let childDom = oldVNode._dom;
			if (excessDomChildren!=null) {
				for (let i = 0; i < excessDomChildren.length; i++) {
					if (excessDomChildren[i]!=null) {
						childDom = excessDomChildren[i];
						break;
					}
				}
			}

			diffChildren(parentDom, getVNodeChildren(newVNode), oldVNodeChildren, context, isSvg, excessDomChildren, mounts, c, newVNode, childDom);

			// The new dom element for fragments is the first child of the new tree
			// When the first child of a Fragment is passed through `diff()`, it sets its dom
			// element to the parentVNode._dom property (that assignment is near the bottom of
			// this function), which is read here.
			dom = newVNode._dom;
			lastDomChild = newVNode._lastDomChild;
		}
		else if (typeof newType==='function') {

			// Get component and set it to `c`
			if (oldVNode._component) {
				c = newVNode._component = oldVNode._component;
				clearProcessingException = c._processingException;
			}
			else {
				isNew = true;

				// Instantiate the new component
				if (newType.prototype && newType.prototype.render) {
					newVNode._component = c = new newType(newVNode.props, context); // eslint-disable-line new-cap
				}
				else {
					newVNode._component = c = new Component(newVNode.props, context);
					c._constructor = newType;
					c.render = doRender;
				}
				c._ancestorComponent = ancestorComponent;

				c.props = newVNode.props;
				if (!c.state) c.state = {};
				c.context = context;
				c._dirty = true;
				c._renderCallbacks = [];
			}

			c._vnode = newVNode;

			// Invoke getDerivedStateFromProps
			let s = c._nextState || c.state;
			if (newType.getDerivedStateFromProps!=null) {
				oldState = assign({}, c.state);
				if (s===c.state) s = assign({}, s);
				assign(s, newType.getDerivedStateFromProps(newVNode.props, s));
			}

			// Invoke pre-render lifecycle methods
			if (isNew) {
				if (newType.getDerivedStateFromProps==null && c.componentWillMount!=null) c.componentWillMount();
				if (c.componentDidMount!=null) mounts.push(c);
			}
			else {
				if (!c._force && c.shouldComponentUpdate!=null && c.shouldComponentUpdate(newVNode.props, s, context)===false) {
					c._dirty = false;
					break outer;
				}
				if (newType.getDerivedStateFromProps==null && c._force!==false && c.componentWillReceiveProps!=null) {
					c.componentWillReceiveProps(newVNode.props, context);
				}

				if (c.componentWillUpdate!=null) {
					c.componentWillUpdate(newVNode.props, s, context);
				}
			}

			oldProps = c.props;
			if (!oldState) oldState = c.state;

			oldContext = c.context = context;
			c.props = newVNode.props;
			c.state = s;

			let prev = c._prevVNode;
			let vnode = c._prevVNode = coerceToVNode(c.render(c.props, c.state, c.context));
			c._dirty = false;

			if (c.getChildContext!=null) {
				context = assign(assign({}, context), c.getChildContext());
			}

			if (!isNew && c.getSnapshotBeforeUpdate!=null) {
				oldContext = c.getSnapshotBeforeUpdate(oldProps, oldState);
			}

			c.base = dom = diff(dom, parentDom, vnode, prev, context, isSvg, append, excessDomChildren, mounts, c, newVNode);

			if (vnode!=null) {
				lastDomChild = vnode._lastDomChild;
			}

			c._parentDom = parentDom;
			c._parentVNode = parentVNode;

			if (newVNode.ref) applyRef(newVNode.ref, c, ancestorComponent);
		}
		else {
			dom = lastDomChild = newVNode._lastDomChild = diffElementNodes(dom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, ancestorComponent);

			if (newVNode.ref && (oldVNode.ref !== newVNode.ref)) {
				applyRef(newVNode.ref, dom, ancestorComponent);
			}
		}

		// Update dom pointers
		if (parentVNode._dom==null) {
			parentVNode._dom = dom;
		}

		parentVNode._lastDomChild = lastDomChild;

		if (parentDom && append!==false && dom!=null && dom.parentNode!==parentDom) {
			parentDom.appendChild(dom);
		}

		newVNode._dom = dom;

		if (c!=null) {
			while (p=c._renderCallbacks.pop()) p();

			// Don't call componentDidUpdate on mount or when we bailed out via
			// `shouldComponentUpdate`
			if (!isNew && oldProps!=null && c.componentDidUpdate!=null) {
				c.componentDidUpdate(oldProps, oldState, oldContext);
			}
		}

		if (clearProcessingException) {
			c._processingException = null;
		}

		if (options.afterDiff) options.afterDiff(newVNode);
	}
	catch (e) {
		catchErrorInComponent(e, ancestorComponent);
	}

	return dom;
}

export function commitRoot(mounts, root) {
	let c;
	while ((c = mounts.pop())) {
		try {
			c.componentDidMount();
		}
		catch (e) {
			catchErrorInComponent(e, c._ancestorComponent);
		}
	}

	if (options.commitRoot) options.commitRoot(root);
}

/**
 * Diff two virtual nodes representing DOM element
 * @param {import('../internal').PreactElement} dom The DOM element representing
 * the virtual nodes being diffed
 * @param {import('../internal').VNode} newVNode The new virtual node
 * @param {import('../internal').VNode} oldVNode The old virtual node
 * @param {object} context The current context object
 * @param {boolean} isSvg Whether or not this DOM node is an SVG node
 * @param {*} excessDomChildren
 * @param {Array<import('../internal').Component>} mounts An array of newly
 * mounted components
 * @param {import('../internal').Component} ancestorComponent The parent
 * component to the ones being diffed
 * @returns {import('../internal').PreactElement}
 */
function diffElementNodes(dom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, ancestorComponent) {
	let d = dom;

	// Tracks entering and exiting SVG namespace when descending through the tree.
	isSvg = isSvg ? newVNode.type !== 'foreignObject' : newVNode.type === 'svg';

	if (dom==null && excessDomChildren!=null) {
		for (let i=0; i<excessDomChildren.length; i++) {
			const child = excessDomChildren[i];
			if (child!=null && (newVNode.type===null ? child.nodeType===3 : child.localName===newVNode.type)) {
				dom = child;
				excessDomChildren[i] = null;
				break;
			}
		}
	}

	if (dom==null) {
		dom = newVNode.type===null ? document.createTextNode(newVNode.text) : isSvg ? document.createElementNS('http://www.w3.org/2000/svg', newVNode.type) : document.createElement(newVNode.type);

		// we created a new parent, so none of the previously attached children can be reused:
		excessDomChildren = null;
	}
	newVNode._dom = dom;

	if (newVNode.type===null) {
		if (dom===d && newVNode.text!==oldVNode.text) {
			dom.data = newVNode.text;
		}
	}
	else {
		if (excessDomChildren!=null && dom.childNodes!=null) {
			excessDomChildren = EMPTY_ARR.slice.call(dom.childNodes);
		}
		if (newVNode!==oldVNode) {
			let oldProps = oldVNode.props;
			// if we're hydrating, use the element's attributes as its current props:
			if (oldProps==null) {
				oldProps = {};
				if (excessDomChildren!=null) {
					for (let i=0; i<dom.attributes.length; i++) {
						oldProps[dom.attributes[i].name] = dom.attributes[i].value;
					}
				}
			}
			diffProps(dom, newVNode.props, oldProps, isSvg);
		}

		diffChildren(dom, getVNodeChildren(newVNode), oldVNode==EMPTY_OBJ ? EMPTY_ARR : getVNodeChildren(oldVNode), context, isSvg, excessDomChildren, mounts, ancestorComponent, newVNode, dom.firstChild);
	}

	return dom;
}

/**
 * Invoke or update a ref, depending on whether it is a function or object ref.
 * @param {object|function} [ref=null]
 * @param {any} [value]
 */
export function applyRef(ref, value, ancestorComponent) {
	try {
		if (typeof ref=='function') ref(value);
		else ref.current = value;
	}
	catch (e) {
		catchErrorInComponent(e, ancestorComponent);
	}
}

/**
 * Unmount a virtual node from the tree and apply DOM changes
 * @param {import('../internal').VNode} vnode The virtual node to unmount
 * @param {import('../internal').Component} ancestorComponent The parent
 * component to this virtual node
 */
export function unmount(vnode, ancestorComponent) {
	let r;
	if (options.beforeUnmount) options.beforeUnmount(vnode);

	if (r = vnode.ref) {
		applyRef(r, null, ancestorComponent);
	}

	if ((r = vnode._dom)!=null) r.remove();

	vnode._dom = vnode._lastDomChild = null;

	if ((r = vnode._component)!=null) {
		if (r.componentWillUnmount) {
			try {
				r.componentWillUnmount();
			}
			catch (e) {
				catchErrorInComponent(e, ancestorComponent);
			}
		}

		r.base = r._parentDom = null;
		if (r = r._prevVNode) unmount(r, ancestorComponent);
	}
	else if (r = vnode._children) {
		for (let i = 0; i < r.length; i++) {
			unmount(r[i], ancestorComponent);
		}
	}
}

/**
 * Get the children of a virtual node as a flat array
 * @param {import('../internal').VNode} vnode The virtual node to get the
 * children of
 * @returns {Array<import('../internal').VNode>} The virtual node's children
 */
function getVNodeChildren(vnode) {
	if (vnode._children==null) {
		toChildArray(vnode.props.children, vnode._children=[]);
	}
	return vnode._children;
}

/**
 * Flatten a virtual nodes children to a single dimensional array
 * @param {import('../index').ComponentChildren} children The unflattened
 * children of a virtual node
 * @param {Array<import('../index').VNode | null>} [flattened] An flat array of children to modify
 */
export function toChildArray(children, flattened) {
	if (flattened===undefined) flattened = [];
	if (children==null || typeof children === 'boolean') {}
	else if (Array.isArray(children)) {
		for (let i=0; i < children.length; i++) {
			toChildArray(children[i], flattened);
		}
	}
	else {
		flattened.push(coerceToVNode(children));
	}

	return flattened;
}

/** The `.render()` method for a PFC backing instance. */
function doRender(props, state, context) {
	return this._constructor(props, context);
}

/**
 * Find the closest error boundary to a thrown error and call it
 * @param {object} error The thrown value
 * @param {import('../internal').Component} component The first ancestor
 * component check for error boundary behaviors
 */
function catchErrorInComponent(error, component) {
	for (; component; component = component._ancestorComponent) {
		if (!component._processingException) {
			try {
				if (component.constructor.getDerivedStateFromError!=null) {
					component.setState(component.constructor.getDerivedStateFromError(error));
				}
				else if (component.componentDidCatch!=null) {
					component.componentDidCatch(error);
				}
				else {
					continue;
				}
				return enqueueRender(component._processingException = component);
			}
			catch (e) {
				error = e;
			}
		}
	}
	throw error;
}


# Changelog

## Differences from Preact

### Major changes

1. The VNode shape has changed
    1. `attributes` has been renamed to `props`
    2. `nodeName` is now `type`
    3. `props.children`
        1. The children of a VNode are no longer guaranteed to be a flat array. It could be `undefined`
           or it could be a nested array of children. use newly exported helper `toChildArray` to
           always get an array back. It takes `props.children` as an argument and always returns an array
        2. A Component with no children will not have a `children` prop defined on `props`. Preact v8
           guaranteed an empty list would always be there. That can no longer be relied on.
        3. `children` is only stored as a property on the `props` property (i.e. `props.children`).
           `vnode.children` no longer exists
        4. `null`, `undefined`, `true`, and `false` are not removed from the children array stored as
		      props. Use the newly exported helper `toChildArray` to flatten and remove non-renderables.
2. `h` has been renamed to `createElement`. To continue to use `h`, alias `createElement` to `h` in
   your import statement to h: `import { createElement as h } from 'preact';`.
3. The root `render` function (the one you import from `preact`) has changed:
    1. `render` no longer returns the newly created DOM element. Its return type is now `void`
    2. `render` no longer supports a third argument to hydrate the DOM. Use the new `hydrate`
       function to hydrate a server rendered DOM tree
    3. Calling `render` multiple times on the same element merges the trees together. It no longer
       just appends to the DOM
    4. Preact no longer guarantees elements not created by Preact will preserved
4. `setState` no longer modifies `this.state` synchronously
5. Falsy attributes values are no longer removed from the DOM. For some attributes (e.g. `spellcheck`)
   the values `false` and `''` have different meaning so being able to render `false` is important
6. The Component constructor no longer initializes state to an empty object. If state has not been
   previously set, it will be set to an empty object the first time the component is rendered, after
   the constructor has been called
7. We no longer have a default export. Use `import * as preact from "preact"` for similar behavior.

### Minor changes

1. `addEventListener` and `removeEventListner` are called every time an event handler is changed
2. `render(null, container)` no longer renders an empty text node but instead renders nothing

### For contributors

1. `scratch.innerHTML = ''` no longer is an effective technique to clear the DOM during testing. If you think you need to
   clear the DOM during a test, consider breaking your tests into multiple individual tests to cover your feature.


import { createElement, Component } from 'ceviche';

let counter = 0;

export default class TodoList extends Component {
	state = { todos: [], text: '' };

	setText = e => {
		this.setState({ text: e.target.value });
	};

	addTodo = () => {
		let { todos, text } = this.state;
		todos = todos.concat({ text, id: ++counter });
		this.setState({ todos, text: '' });
	};

	removeTodo = e => {
		let id = e.target.getAttribute('data-id');
		this.setState({ todos: this.state.todos.filter( t => t.id!=id ) });
	};

	render({ }, { todos, text }) {
		return (
			<form onSubmit={this.addTodo} action="javascript:">
				<input value={text} onInput={this.setText} />
				<button type="submit">Add</button>

					<TodoItems todos={todos} removeTodo={this.removeTodo}/>

			</form>
		);
	}
}

class TodoItems extends Component {
	render({ todos, removeTodo }) {
		return todos.map( todo => (

				<button onClick={removeTodo} data-id={todo.id}>×</button>
				{' '}
				{todo.text}

		));
	}
}


# ceviche (a.k.a "Preact X")

A smaller, faster, more React-compatible and feature-rich version of Preact.

## Hacking on the code

**Installation:**

```sh
git clone git@github.com:developit/ceviche.git
cd ceviche
npm i
# install demo dependencies
cd demo && npm i && cd ..
```

**Useful scripts to run:**

```sh
# create a production build in dist/
npm run build

# rebuild on any change (useful for demo hacking)
npm run dev

# run all tests once (and legacy benchmarks)
npm t

# run main tests in watch mode
npm run test:karma:watch

# run legacy benchmarks in watch mode
npm run test:karma:bench

# run benchmarks (new version)
npm run benchmark
```

## The Demo

There's a collection of demos in `demo/`, bundled via a pretty standard Webpack setup.

The demo uses `preact-router`, which hasn't yet been updated to account for Ceviche's new VNode format,
so we "polyfill" Preact 8.x's VNode format in `demo/preact.js`. This polyfill should form the basis
of the 8.x compatibility shim we ship at launch.

**To run the demos:**

```sh
# start live demo server
cd demo && npm start
```

#### DOM Mutation Logging

The demo is also instrumented to allow logging all DOM manipulation.
This can be enabled for a single page load by adding `?logger=true` to the URL, or permanently by setting `localStorage.LOG=true` in your console.
When working with some of the more tricky implementation details like Fragments or hydration, it's often useful familiarize yourself with the number of DOM operations performed when reloading and interacting each of the demos.
This way, leaving logging turned on will alert you to any possible diffing and performance implications of your changes.