<commit_before>pc.extend(pc, function () {
    function getKey(layer, blendType, isCommand, materialId) {
        // Key definition:
        // Bit
        // 31      : sign bit (leave)
        // 27 - 30 : layer
        // 26      : translucency type (opaque/transparent)
        // 25      : Command bit (1: this key is for a command, 0: it's a mesh instance)
        // 0 - 24  : Material ID (if oqaque) or 0 (if transparent - will be depth)
        return ((layer & 0x0f) << 27) |
               ((blendType===pc.BLEND_NONE? 1 : 0) << 26) |
               ((isCommand ? 1 : 0) << 25) |
               ((materialId & 0x1ffffff) << 0);
    }

    /**
     * @name pc.Mesh
     * @class A graphical primitive. The mesh is defined by a {@link pc.VertexBuffer} and an optional
     * {@link pc.IndexBuffer}. It also contains a primitive definition which controls the type of the
     * primitive and the portion of the vertex or index buffer to use.
     * @description Create a new mesh.
     * @property {pc.VertexBuffer} vertexBuffer The vertex buffer holding the vertex data of the mesh.
     * @property {pc.IndexBuffer[]} indexBuffer An array of index buffers. For unindexed meshes, this array can
     * be empty. The first index buffer in the array is used by {@link pc.MeshInstance}s with a renderStyle
     * property set to pc.RENDERSTYLE_SOLID. The second index buffer in the array is used if renderStyle is
     * set to pc.RENDERSTYLE_WIREFRAME.
     * @property {Object[]} primitive Array of primitive objects defining how vertex (and index) data in the
     * mesh should be interpreted by the graphics device. For details on the primitive object, see
     * {@link pc.GraphicsDevice#draw}. The primitive is ordered based on render style like the indexBuffer property.
     * @property {pc.BoundingBox} aabb The axis-aligned bounding box for the object space vertices of this mesh.
     */
    var Mesh = function () {
        this.vertexBuffer = null;
        this.indexBuffer = [ null ];
        this.primitive = [{
            type: 0,
            base: 0,
            count: 0
        }];
        this.skin = null;

        // AABB for object space mesh vertices
        this.aabb = new pc.BoundingBox();

        // Array of object space AABBs of vertices affected by each bone
        this.boneAabb = null;
    };

    var InstancingData = function (numObjects, dynamic, instanceSize) {
        instanceSize = instanceSize || 16;
        this.buffer = new Float32Array(numObjects * instanceSize);
        this.count = numObjects;
        this.offset = 0;
        this.usage = dynamic? pc.BUFFER_DYNAMIC : pc.BUFFER_STATIC;
        this._buffer = null;
    };

    InstancingData.prototype = {
        update: function () {
            if (this._buffer) {
                this._buffer.setData(this.buffer);
            }
        }
    };

    /**
     * @name pc.MeshInstance
     * @class An instance of a {@link pc.Mesh}. A single mesh can be referenced by many
     * mesh instances that can have different transforms and materials.
     * @description Create a new mesh instance.
     * @param {pc.GraphNode} node The graph node defining the transform for this instance.
     * @param {pc.Mesh} mesh The graphics mesh being instanced.
     * @param {pc.Material} material The material used to render this instance.
     * @example
     * // Create a mesh instance pointing to a 1x1x1 'cube' mesh
     * var mesh = pc.createBox(graphicsDevice);
     * var material = new pc.StandardMaterial();
     * var node = new pc.GraphNode();
     * var meshInstance = new pc.MeshInstance(node, mesh, material);
     * @property {pc.BoundingBox} aabb The world space axis-aligned bounding box for this
     * mesh instance.
     * @property {Boolean} castShadow Controls whether the mesh instances casts shadows.
     * Defaults to false.
     * @property {Boolean} visible Enable rendering for this mesh instance. Use visible property to enable/disable rendering without overhead of removing from scene.
     * But note that the mesh instance is still in the hierarchy and still in the draw call list.
     * @property {Number} layer The layer used by this mesh instance. Can be:
     *

     *
pc.LAYER_WORLD

     *
pc.LAYER_FX

     *
pc.LAYER_GIZMO

     *
pc.LAYER_HUD

     *

     * Defaults to pc.LAYER_WORLD.
     * @property {pc.Material} material The material used by this mesh instance.
     * @property {Number} renderStyle The render style of the mesh instance. Can be:
     *

     *
pc.RENDERSTYLE_SOLID

     *
pc.RENDERSTYLE_WIREFRAME

     *
pc.RENDERSTYLE_POINTS

     *

     * Defaults to pc.RENDERSTYLE_SOLID.
     */
    var MeshInstance = function MeshInstance(node, mesh, material) {
        this._key = [0,0];
        this._shader = [null, null, null, null, null, null];

        this.node = node;           // The node that defines the transform of the mesh instance
        this.mesh = mesh;           // The mesh that this instance renders
        this.material = material;   // The material with which to render this instance

        this._shaderDefs = 256; // 1 byte toggles, 3 bytes light mask; Default value is no toggles and mask = 1
        this._shaderDefs |= mesh.vertexBuffer.format.hasUv0? pc.SHADERDEF_UV0 : 0;
        this._shaderDefs |= mesh.vertexBuffer.format.hasUv1? pc.SHADERDEF_UV1 : 0;
        this._shaderDefs |= mesh.vertexBuffer.format.hasColor? pc.SHADERDEF_VCOLOR : 0;

        // Render options
        this.visible = true;
        this.layer = pc.LAYER_WORLD;
        this.renderStyle = pc.RENDERSTYLE_SOLID;
        this.castShadow = false;
        this._receiveShadow = true;
        this.drawToDepth = true;
        this.cull = true;
        this.pick = true;
        this._updateAabb = true;

        // 64-bit integer key that defines render order of this mesh instance
        this.updateKey();

        this._skinInstance = null;
        this.instancingData = null;

        // World space AABB
        this.aabb = new pc.BoundingBox();
        this.normalMatrix = new pc.Mat3();

        this._boneAabb = null;

        this.parameters = {};
    };

    Object.defineProperty(MeshInstance.prototype, 'aabb', {
        get: function () {

            if (!this._updateAabb) return this._aabb;

            if (this.skinInstance) {
                var numBones = this.mesh.skin.boneNames.length;
                var i;
                // Initialize local bone AABBs if needed
                if (!this.mesh.boneAabb) {

                    this.mesh.boneAabb = [];
                    this.mesh.boneUsed = [];
                    var elems = this.mesh.vertexBuffer.format.elements;
                    var numVerts = this.mesh.vertexBuffer.numVertices;
                    var vertSize = this.mesh.vertexBuffer.format.size;
                    var index;
                    var offsetP, offsetI, offsetW;
                    var j, k;
                    for(i=0; i<elems.length; i++) {
                        if (elems[i].name===pc.SEMANTIC_POSITION) {
                            offsetP = elems[i].offset;
                        } else if (elems[i].name===pc.SEMANTIC_BLENDINDICES) {
                            offsetI = elems[i].offset;
                        } else if (elems[i].name===pc.SEMANTIC_BLENDWEIGHT) {
                            offsetW = elems[i].offset;
                        }
                    }

                    var data8 = new Uint8Array(this.mesh.vertexBuffer.storage);
                    var dataF = new Float32Array(this.mesh.vertexBuffer.storage);
                    var offsetPF = offsetP / 4;
                    var offsetWF = offsetW / 4;
                    var vertSizeF = vertSize / 4;

                    var bMax, bMin;
                    var x, y, z;
                    var boneMin = [];
                    var boneMax = [];
                    var boneUsed = this.mesh.boneUsed;

                    for(i=0; i<numBones; i++) {
                        boneMin[i] = new pc.Vec3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
                        boneMax[i] = new pc.Vec3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
                    }

                    for(j=0; j<numVerts; j++) {
                        for(k=0; k<4; k++) {
                            if (dataF[j * vertSizeF + offsetWF + k] > 0) {
                                index = data8[j * vertSize + offsetI + k];
                                // Vertex j is affected by bone index
                                x = dataF[j * vertSizeF + offsetPF];
                                y = dataF[j * vertSizeF + offsetPF + 1];
                                z = dataF[j * vertSizeF + offsetPF + 2];

                                bMax = boneMax[index];
                                bMin = boneMin[index];

                                if (bMin.x > x) bMin.x = x;
                                if (bMin.y > y) bMin.y = y;
                                if (bMin.z > z) bMin.z = z;

                                if (bMax.x < x) bMax.x = x;
                                if (bMax.y < y) bMax.y = y;
                                if (bMax.z < z) bMax.z = z;

                                boneUsed[index] = true;
                            }
                        }
                    }

                    var aabb;
                    for(i=0; i<numBones; i++) {
                        aabb = new pc.BoundingBox();
                        aabb.setMinMax(boneMin[i], boneMax[i]);
                        this.mesh.boneAabb.push(aabb);
                    }
                }

                // Initialize per-instance AABBs if needed
                if (!this._boneAabb) {
                    this._boneAabb = [];
                    for(i=0; i<this.mesh.boneAabb.length; i++) {
                        this._boneAabb[i] = new pc.BoundingBox();
                    }
                }

                var boneUsed = this.mesh.boneUsed;

                // Update per-instance bone AABBs
                for(i=0; i<this.mesh.boneAabb.length; i++) {
                    if (!boneUsed[i]) continue;
                    this._boneAabb[i].setFromTransformedAabb(this.mesh.boneAabb[i], this.skinInstance.matrices[i]);
                    this._boneAabb[i].center.add(this.skinInstance.rootNode.getPosition());
                }
                // Update full instance AABB
                var first = true;
                for(i=0; i<this.mesh.boneAabb.length; i++) {
                    if (!boneUsed[i]) continue;
                    if (first) {
                        this._aabb.center.copy(this._boneAabb[i].center);
                        this._aabb.halfExtents.copy(this._boneAabb[i].halfExtents);
                        first = false;
                    } else {
                        this._aabb.add(this._boneAabb[i]);
                    }
                }
            } else if (this.node._dirtyAabb) {
                this._aabb.setFromTransformedAabb(this.mesh.aabb, this.node.worldTransform);
                this.node._dirtyAabb = false;
            }
            return this._aabb;
        },
        set: function (aabb) {
            this._aabb = aabb;
        }
    });

    Object.defineProperty(MeshInstance.prototype, 'material', {
        get: function () {
            return this._material;
        },
        set: function (material) {
            this._shader[pc.SHADER_FORWARD] = null;
            this._shader[pc.SHADER_DEPTH] = null;
            this._shader[pc.SHADER_SHADOW] = null;
            this._shader[pc.SHADER_SHADOW + pc.SHADOW_VSM8] = null;
            this._shader[pc.SHADER_SHADOW + pc.SHADOW_VSM16] = null;
            this._shader[pc.SHADER_SHADOW + pc.SHADOW_VSM32] = null;
            // Remove the material's reference to this mesh instance
            if (this._material) {
                var meshInstances = this._material.meshInstances;
                var index = meshInstances.indexOf(this);
                if (index !== -1) {
                    meshInstances.splice(index, 1);
                }
            }

            this._material = material;

            // Record that the material is referenced by this mesh instance
            this._material.meshInstances.push(this);

            this.updateKey();
        }
    });

    Object.defineProperty(MeshInstance.prototype, 'layer', {
        get: function () {
            return this._layer;
        },
        set: function (layer) {
            this._layer = layer;
            this.updateKey();
        }
    });

    Object.defineProperty(MeshInstance.prototype, 'receiveShadow', {
        get: function () {
            return this._receiveShadow;
        },
        set: function (val) {
            this._receiveShadow = val;
            this._shaderDefs = val? (this._shaderDefs & ~pc.SHADERDEF_NOSHADOW) : (this._shaderDefs | pc.SHADERDEF_NOSHADOW);
            this._shader[pc.SHADER_FORWARD] = null;
        }
    });

    Object.defineProperty(MeshInstance.prototype, 'skinInstance', {
        get: function () {
            return this._skinInstance;
        },
        set: function (val) {
            this._skinInstance = val;
            this._shaderDefs = val? (this._shaderDefs | pc.SHADERDEF_SKIN) : (this._shaderDefs & ~pc.SHADERDEF_SKIN);
            this._shader[pc.SHADER_FORWARD] = null;
            this._shader[pc.SHADER_DEPTH] = null;
            this._shader[pc.SHADER_SHADOW] = null;
            this._shader[pc.SHADER_SHADOW + pc.SHADOW_VSM8] = null;
            this._shader[pc.SHADER_SHADOW + pc.SHADOW_VSM16] = null;
            this._shader[pc.SHADER_SHADOW + pc.SHADOW_VSM32] = null;
        }
    });

    Object.defineProperty(MeshInstance.prototype, 'key', {
        get: function () {
            return this._key[pc.SORTKEY_FORWARD];
        },
        set: function (val) {
            this._key[pc.SORTKEY_FORWARD] = val;
        }
    });

    /**
     * @name pc.MeshInstance#mask
     * @type Number
     * @description Mask controlling which {@link pc.LightComponent}s light this mesh instance.
     * To ignore all dynamic lights, set mask to 0. Defaults to 1.
     */
    Object.defineProperty(MeshInstance.prototype, 'mask', {
        get: function () {
            return this._shaderDefs >> 8;
        },
        set: function (val) {
            var toggles = this._shaderDefs & 0x000000FF;
            this._shaderDefs = toggles | (val << 8);
            this._shader[pc.SHADER_FORWARD] = null;
        }
    });

    pc.extend(MeshInstance.prototype, {
        syncAabb: function () {
            // Deprecated
        },

        updateKey: function () {
            var material = this.material;
            this._key[pc.SORTKEY_FORWARD] = getKey(this.layer, material.blendType, false, material.id);
        },

        setParameter : pc.Material.prototype.setParameter,
        setParameters : pc.Material.prototype.setParameters,
        deleteParameter : pc.Material.prototype.deleteParameter,
        getParameter : pc.Material.prototype.getParameter,
        getParameters : pc.Material.prototype.getParameters,
        clearParameters : pc.Material.prototype.clearParameters
    });

    var Command = function (layer, blendType, command) {
        this._key = [];
        this._key[pc.SORTKEY_FORWARD] = getKey(layer, blendType, true, 0);
        this.command = command;
    };

    return {
        Command: Command,
        Mesh: Mesh,
        MeshInstance: MeshInstance,
        InstancingData: InstancingData,
        _getDrawcallSortKey: getKey
    };
}());
<commit_msg>add backwards comp. key for Command<commit_after>pc.extend(pc, function () {
    function getKey(layer, blendType, isCommand, materialId) {
        // Key definition:
        // Bit
        // 31      : sign bit (leave)
        // 27 - 30 : layer
        // 26      : translucency type (opaque/transparent)
        // 25      : Command bit (1: this key is for a command, 0: it's a mesh instance)
        // 0 - 24  : Material ID (if oqaque) or 0 (if transparent - will be depth)
        return ((layer & 0x0f) << 27) |
               ((blendType===pc.BLEND_NONE? 1 : 0) << 26) |
               ((isCommand ? 1 : 0) << 25) |
               ((materialId & 0x1ffffff) << 0);
    }

    /**
     * @name pc.Mesh
     * @class A graphical primitive. The mesh is defined by a {@link pc.VertexBuffer} and an optional
     * {@link pc.IndexBuffer}. It also contains a primitive definition which controls the type of the
     * primitive and the portion of the vertex or index buffer to use.
     * @description Create a new mesh.
     * @property {pc.VertexBuffer} vertexBuffer The vertex buffer holding the vertex data of the mesh.
     * @property {pc.IndexBuffer[]} indexBuffer An array of index buffers. For unindexed meshes, this array can
     * be empty. The first index buffer in the array is used by {@link pc.MeshInstance}s with a renderStyle
     * property set to pc.RENDERSTYLE_SOLID. The second index buffer in the array is used if renderStyle is
     * set to pc.RENDERSTYLE_WIREFRAME.
     * @property {Object[]} primitive Array of primitive objects defining how vertex (and index) data in the
     * mesh should be interpreted by the graphics device. For details on the primitive object, see
     * {@link pc.GraphicsDevice#draw}. The primitive is ordered based on render style like the indexBuffer property.
     * @property {pc.BoundingBox} aabb The axis-aligned bounding box for the object space vertices of this mesh.
     */
    var Mesh = function () {
        this.vertexBuffer = null;
        this.indexBuffer = [ null ];
        this.primitive = [{
            type: 0,
            base: 0,
            count: 0
        }];
        this.skin = null;

        // AABB for object space mesh vertices
        this.aabb = new pc.BoundingBox();

        // Array of object space AABBs of vertices affected by each bone
        this.boneAabb = null;
    };

    var InstancingData = function (numObjects, dynamic, instanceSize) {
        instanceSize = instanceSize || 16;
        this.buffer = new Float32Array(numObjects * instanceSize);
        this.count = numObjects;
        this.offset = 0;
        this.usage = dynamic? pc.BUFFER_DYNAMIC : pc.BUFFER_STATIC;
        this._buffer = null;
    };

    InstancingData.prototype = {
        update: function () {
            if (this._buffer) {
                this._buffer.setData(this.buffer);
            }
        }
    };

    /**
     * @name pc.MeshInstance
     * @class An instance of a {@link pc.Mesh}. A single mesh can be referenced by many
     * mesh instances that can have different transforms and materials.
     * @description Create a new mesh instance.
     * @param {pc.GraphNode} node The graph node defining the transform for this instance.
     * @param {pc.Mesh} mesh The graphics mesh being instanced.
     * @param {pc.Material} material The material used to render this instance.
     * @example
     * // Create a mesh instance pointing to a 1x1x1 'cube' mesh
     * var mesh = pc.createBox(graphicsDevice);
     * var material = new pc.StandardMaterial();
     * var node = new pc.GraphNode();
     * var meshInstance = new pc.MeshInstance(node, mesh, material);
     * @property {pc.BoundingBox} aabb The world space axis-aligned bounding box for this
     * mesh instance.
     * @property {Boolean} castShadow Controls whether the mesh instances casts shadows.
     * Defaults to false.
     * @property {Boolean} visible Enable rendering for this mesh instance. Use visible property to enable/disable rendering without overhead of removing from scene.
     * But note that the mesh instance is still in the hierarchy and still in the draw call list.
     * @property {Number} layer The layer used by this mesh instance. Can be:
     *

     *
pc.LAYER_WORLD

     *
pc.LAYER_FX

     *
pc.LAYER_GIZMO

     *
pc.LAYER_HUD

     *

     * Defaults to pc.LAYER_WORLD.
     * @property {pc.Material} material The material used by this mesh instance.
     * @property {Number} renderStyle The render style of the mesh instance. Can be:
     *

     *
pc.RENDERSTYLE_SOLID

     *
pc.RENDERSTYLE_WIREFRAME

     *
pc.RENDERSTYLE_POINTS

     *

     * Defaults to pc.RENDERSTYLE_SOLID.
     */
    var MeshInstance = function MeshInstance(node, mesh, material) {
        this._key = [0,0];
        this._shader = [null, null, null, null, null, null];

        this.node = node;           // The node that defines the transform of the mesh instance
        this.mesh = mesh;           // The mesh that this instance renders
        this.material = material;   // The material with which to render this instance

        this._shaderDefs = 256; // 1 byte toggles, 3 bytes light mask; Default value is no toggles and mask = 1
        this._shaderDefs |= mesh.vertexBuffer.format.hasUv0? pc.SHADERDEF_UV0 : 0;
        this._shaderDefs |= mesh.vertexBuffer.format.hasUv1? pc.SHADERDEF_UV1 : 0;
        this._shaderDefs |= mesh.vertexBuffer.format.hasColor? pc.SHADERDEF_VCOLOR : 0;

        // Render options
        this.visible = true;
        this.layer = pc.LAYER_WORLD;
        this.renderStyle = pc.RENDERSTYLE_SOLID;
        this.castShadow = false;
        this._receiveShadow = true;
        this.drawToDepth = true;
        this.cull = true;
        this.pick = true;
        this._updateAabb = true;

        // 64-bit integer key that defines render order of this mesh instance
        this.updateKey();

        this._skinInstance = null;
        this.instancingData = null;

        // World space AABB
        this.aabb = new pc.BoundingBox();
        this.normalMatrix = new pc.Mat3();

        this._boneAabb = null;

        this.parameters = {};
    };

    Object.defineProperty(MeshInstance.prototype, 'aabb', {
        get: function () {

            if (!this._updateAabb) return this._aabb;

            if (this.skinInstance) {
                var numBones = this.mesh.skin.boneNames.length;
                var i;
                // Initialize local bone AABBs if needed
                if (!this.mesh.boneAabb) {

                    this.mesh.boneAabb = [];
                    this.mesh.boneUsed = [];
                    var elems = this.mesh.vertexBuffer.format.elements;
                    var numVerts = this.mesh.vertexBuffer.numVertices;
                    var vertSize = this.mesh.vertexBuffer.format.size;
                    var index;
                    var offsetP, offsetI, offsetW;
                    var j, k;
                    for(i=0; i<elems.length; i++) {
                        if (elems[i].name===pc.SEMANTIC_POSITION) {
                            offsetP = elems[i].offset;
                        } else if (elems[i].name===pc.SEMANTIC_BLENDINDICES) {
                            offsetI = elems[i].offset;
                        } else if (elems[i].name===pc.SEMANTIC_BLENDWEIGHT) {
                            offsetW = elems[i].offset;
                        }
                    }

                    var data8 = new Uint8Array(this.mesh.vertexBuffer.storage);
                    var dataF = new Float32Array(this.mesh.vertexBuffer.storage);
                    var offsetPF = offsetP / 4;
                    var offsetWF = offsetW / 4;
                    var vertSizeF = vertSize / 4;

                    var bMax, bMin;
                    var x, y, z;
                    var boneMin = [];
                    var boneMax = [];
                    var boneUsed = this.mesh.boneUsed;

                    for(i=0; i<numBones; i++) {
                        boneMin[i] = new pc.Vec3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
                        boneMax[i] = new pc.Vec3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
                    }

                    for(j=0; j<numVerts; j++) {
                        for(k=0; k<4; k++) {
                            if (dataF[j * vertSizeF + offsetWF + k] > 0) {
                                index = data8[j * vertSize + offsetI + k];
                                // Vertex j is affected by bone index
                                x = dataF[j * vertSizeF + offsetPF];
                                y = dataF[j * vertSizeF + offsetPF + 1];
                                z = dataF[j * vertSizeF + offsetPF + 2];

                                bMax = boneMax[index];
                                bMin = boneMin[index];

                                if (bMin.x > x) bMin.x = x;
                                if (bMin.y > y) bMin.y = y;
                                if (bMin.z > z) bMin.z = z;

                                if (bMax.x < x) bMax.x = x;
                                if (bMax.y < y) bMax.y = y;
                                if (bMax.z < z) bMax.z = z;

                                boneUsed[index] = true;
                            }
                        }
                    }

                    var aabb;
                    for(i=0; i<numBones; i++) {
                        aabb = new pc.BoundingBox();
                        aabb.setMinMax(boneMin[i], boneMax[i]);
                        this.mesh.boneAabb.push(aabb);
                    }
                }

                // Initialize per-instance AABBs if needed
                if (!this._boneAabb) {
                    this._boneAabb = [];
                    for(i=0; i<this.mesh.boneAabb.length; i++) {
                        this._boneAabb[i] = new pc.BoundingBox();
                    }
                }

                var boneUsed = this.mesh.boneUsed;

                // Update per-instance bone AABBs
                for(i=0; i<this.mesh.boneAabb.length; i++) {
                    if (!boneUsed[i]) continue;
                    this._boneAabb[i].setFromTransformedAabb(this.mesh.boneAabb[i], this.skinInstance.matrices[i]);
                    this._boneAabb[i].center.add(this.skinInstance.rootNode.getPosition());
                }
                // Update full instance AABB
                var first = true;
                for(i=0; i<this.mesh.boneAabb.length; i++) {
                    if (!boneUsed[i]) continue;
                    if (first) {
                        this._aabb.center.copy(this._boneAabb[i].center);
                        this._aabb.halfExtents.copy(this._boneAabb[i].halfExtents);
                        first = false;
                    } else {
                        this._aabb.add(this._boneAabb[i]);
                    }
                }
            } else if (this.node._dirtyAabb) {
                this._aabb.setFromTransformedAabb(this.mesh.aabb, this.node.worldTransform);
                this.node._dirtyAabb = false;
            }
            return this._aabb;
        },
        set: function (aabb) {
            this._aabb = aabb;
        }
    });

    Object.defineProperty(MeshInstance.prototype, 'material', {
        get: function () {
            return this._material;
        },
        set: function (material) {
            this._shader[pc.SHADER_FORWARD] = null;
            this._shader[pc.SHADER_DEPTH] = null;
            this._shader[pc.SHADER_SHADOW] = null;
            this._shader[pc.SHADER_SHADOW + pc.SHADOW_VSM8] = null;
            this._shader[pc.SHADER_SHADOW + pc.SHADOW_VSM16] = null;
            this._shader[pc.SHADER_SHADOW + pc.SHADOW_VSM32] = null;
            // Remove the material's reference to this mesh instance
            if (this._material) {
                var meshInstances = this._material.meshInstances;
                var index = meshInstances.indexOf(this);
                if (index !== -1) {
                    meshInstances.splice(index, 1);
                }
            }

            this._material = material;

            // Record that the material is referenced by this mesh instance
            this._material.meshInstances.push(this);

            this.updateKey();
        }
    });

    Object.defineProperty(MeshInstance.prototype, 'layer', {
        get: function () {
            return this._layer;
        },
        set: function (layer) {
            this._layer = layer;
            this.updateKey();
        }
    });

    Object.defineProperty(MeshInstance.prototype, 'receiveShadow', {
        get: function () {
            return this._receiveShadow;
        },
        set: function (val) {
            this._receiveShadow = val;
            this._shaderDefs = val? (this._shaderDefs & ~pc.SHADERDEF_NOSHADOW) : (this._shaderDefs | pc.SHADERDEF_NOSHADOW);
            this._shader[pc.SHADER_FORWARD] = null;
        }
    });

    Object.defineProperty(MeshInstance.prototype, 'skinInstance', {
        get: function () {
            return this._skinInstance;
        },
        set: function (val) {
            this._skinInstance = val;
            this._shaderDefs = val? (this._shaderDefs | pc.SHADERDEF_SKIN) : (this._shaderDefs & ~pc.SHADERDEF_SKIN);
            this._shader[pc.SHADER_FORWARD] = null;
            this._shader[pc.SHADER_DEPTH] = null;
            this._shader[pc.SHADER_SHADOW] = null;
            this._shader[pc.SHADER_SHADOW + pc.SHADOW_VSM8] = null;
            this._shader[pc.SHADER_SHADOW + pc.SHADOW_VSM16] = null;
            this._shader[pc.SHADER_SHADOW + pc.SHADOW_VSM32] = null;
        }
    });

    Object.defineProperty(MeshInstance.prototype, 'key', {
        get: function () {
            return this._key[pc.SORTKEY_FORWARD];
        },
        set: function (val) {
            this._key[pc.SORTKEY_FORWARD] = val;
        }
    });

    /**
     * @name pc.MeshInstance#mask
     * @type Number
     * @description Mask controlling which {@link pc.LightComponent}s light this mesh instance.
     * To ignore all dynamic lights, set mask to 0. Defaults to 1.
     */
    Object.defineProperty(MeshInstance.prototype, 'mask', {
        get: function () {
            return this._shaderDefs >> 8;
        },
        set: function (val) {
            var toggles = this._shaderDefs & 0x000000FF;
            this._shaderDefs = toggles | (val << 8);
            this._shader[pc.SHADER_FORWARD] = null;
        }
    });

    pc.extend(MeshInstance.prototype, {
        syncAabb: function () {
            // Deprecated
        },

        updateKey: function () {
            var material = this.material;
            this._key[pc.SORTKEY_FORWARD] = getKey(this.layer, material.blendType, false, material.id);
        },

        setParameter : pc.Material.prototype.setParameter,
        setParameters : pc.Material.prototype.setParameters,
        deleteParameter : pc.Material.prototype.deleteParameter,
        getParameter : pc.Material.prototype.getParameter,
        getParameters : pc.Material.prototype.getParameters,
        clearParameters : pc.Material.prototype.clearParameters
    });

    var Command = function (layer, blendType, command) {
        this._key = [];
        this._key[pc.SORTKEY_FORWARD] = getKey(layer, blendType, true, 0);
        this.command = command;
    };

    Object.defineProperty(Command.prototype, 'key', {
        get: function () {
            return this._key[pc.SORTKEY_FORWARD];
        },
        set: function (val) {
            this._key[pc.SORTKEY_FORWARD] = val;
        }
    });

    return {
        Command: Command,
        Mesh: Mesh,
        MeshInstance: MeshInstance,
        InstancingData: InstancingData,
        _getDrawcallSortKey: getKey
    };
}());
<|endoftext|>


<commit_before>(function (window) {

    var easingFn = {
        quadratic: {
            transition: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)'
        },
        circular: {
            transition: 'cubic-bezier(0.1, 0.57, 0.1, 1)'
        },
        back: {
            transition: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)'
        },
        bounce: {
            transition: 'cubic-bezier(0,0,.5,1.2)'
        }
    };

    // helpers
    function triggerEvent(type, data) {
        var ev = new CustomEvent(type, {
            detail: data || {},
            bubbles: true,
            cancelable: true
        });

        this.view.dispatchEvent(ev);
    }

    function calculateDistance(start, end) {
        return end - start;
    }

    function calculateVelocity(distance, duration) {
        var velocity = Math.abs(distance) / duration; // px/ms

        return velocity;
    }

    function calculateMomentum(current, direction, velocity, lower, upper, deceleration) {
        var destination, duration, distance;

        deceleration = deceleration || 0.0006;

        duration = velocity / deceleration;
        destination = current + (velocity * (duration * duration) * 0.5 * deceleration * direction);

        //destination = current + (velocity * velocity) / (2 * deceleration) * direction;

        if (destination < lower || destination > upper) {
            distance = Math.abs(destination - current);

            destination = destination < lower ? lower :
                destination > upper ? upper :
                destination;
            duration = Math.sqrt((destination - current) / (velocity * 0.5 * deceleration * direction));

            //destination = upper ? lower - (upper / 2.5 * velocity / 8) : lower;
            //distance = Math.abs(destination - current);
            //duration = distance / velocity;
        }

        return {
            destination: Math.round(destination),
            duration: parseFloat(duration)
        };
    }

    // private functions
    function handleStart(ev) {

        if (!this._enabled || this._curPointer) {
            return;
        }

        var timestamp = new Date().getTime();

        this._curPointer = ev.pointerId; // lock for pointer
        this._hasMoved = false; // if scrolling has started

        this._boundaries = [
            -this.view.scrollWidth + this.view.clientWidth,
            -this.view.scrollHeight + this.view.clientHeight
        ];

        // make sure it is not moving
        moveTo.apply(this, getCurrentPosition.call(this));

        // previous point for delta calculation
        this._lastPoint = {
            timestamp: timestamp,
            x: ev.pageX,
            y: ev.pageY
        };

        // last key frame for velocity caluclation
        this._lastKeyFrame = {
            timestamp: timestamp,
            x: this.x,
            y: this.y
        };

        // bind events
        document.addEventListener('pointerup', this._handleEnd, false);
        document.addEventListener('pointercancel', this._handleEnd, false);
        document.addEventListener('pointermove', this._handleMove, false);
    }

    function handleMove(ev) {
        if (!this._enabled || this._curPointer !== ev.pointerId) {
            return;
        }

        var deltaX = this.options.scrollX ? calculateDistance(this._lastPoint.x, ev.pageX) : 0,
            deltaY = this.options.scrollY ? calculateDistance(this._lastPoint.y, ev.pageY) : 0,
            timestamp = new Date().getTime(),
            newX, newY;

        this._lastPoint = {
            timestamp: timestamp,
            x: ev.pageX,
            y: ev.pageY
        };

        newX = this.x + deltaX;
        newY = this.y + deltaY;

        // TODO add WIGGLE_THRESHOLD
        // We need to move at least 10 pixels for the scrolling to initiate
        if (Math.abs(deltaX) < 10 && Math.abs(deltaY) < 10) {
            return;
        }

        // consider boundaries
        newX = newX > 0 ? // upper
            this.options.bounce ? this.x + deltaX / 3 :
            0 :
            newX;
        newY = newY > 0 ? // upper
            this.options.bounce ? this.y + deltaY / 3 :
            0 :
            newY;
        newX = newX < this._boundaries[0] ? // lower
            this.options.bounce ? this.x + deltaX / 3 :
            this._boundaries[0] :
            newX;
        newY = newY < this._boundaries[1] ? // lower
            this.options.bounce ? this.y + deltaY / 3 :
            this._boundaries[1] :
            newY;

        // initial move
        if (!this._hasMoved) {
            this._hasMoved = true;
            triggerEvent.call(this, 'scrollstart', {
                pointerId: this._curPointer,
                x: this.x,
                y: this.y
            });
        }

        // save new keyframe every 300ms
        if (timestamp - this._lastKeyFrame.timestamp > 300) {
            this._lastKeyFrame = {
                timestamp: timestamp,
                x: newX,
                y: newY
            }
        }

        this.scrollTo(newX, newY);

        triggerEvent.call(this, 'scroll', {
            pointerId: this._curPointer,
            direction: [
                deltaX < 0 ? -1 : deltaX > 0 ? 1 : 0,
                deltaY < 0 ? -1 : deltaY > 0 ? 1 : 0
            ],
            x: newX,
            y: newY
        });
    }

    function handleEnd(ev) {
        if (!this._enabled ||
            this._curPointer !== ev.pointerId) {
            return;
        }

        var duration = new Date().getTime() - this._lastKeyFrame.timestamp,
            deltaX = this.options.scrollX ? calculateDistance(this._lastPoint.x, ev.pageX) : 0,
            deltaY = this.options.scrollY ? calculateDistance(this._lastPoint.y, ev.pageY) : 0,
            distance, velocity, momentum, time,
            newX = this.x + deltaX,
            newY = this.y + deltaY;

        // reset state
        this._curPointer = null;

        document.removeEventListener('pointerup', this._handleEnd, false);
        document.removeEventListener('pointercancel', this._handleEnd, false);
        document.removeEventListener('pointermove', this._handleMove, false);
        this.scrollTo(newX, newY);

        if (!this._hasMoved) { // has never scrolled
            return
        }

        // start momentum animation if needed
        if (this.options.momentum && duration < 300) {
            distance = [
                calculateDistance(this._lastKeyFrame.x, this.x),
                calculateDistance(this._lastKeyFrame.y, this.y)
            ];

            direction = [
                distance[0] < 0 ? -1 : distance[0] > 0 ? 1 : 0,
                distance[1] < 0 ? -1 : distance[1] > 0 ? 1 : 0
            ];

            velocity = [
                calculateVelocity(distance[0], duration),
                calculateVelocity(distance[1], duration)
            ];

            momentum = [ //inertia
                calculateMomentum(this.x, direction[0], velocity[0], this._boundaries[0], 0),
                calculateMomentum(this.y, direction[1], velocity[1], this._boundaries[1], 0)
            ];

            newX = momentum[0].destination;
            newY = momentum[1].destination;
            time = Math.max(momentum[0].duration, momentum[1].duration);
        }

        if (this.x <= 0 && this.x >= this._boundaries[0] && // not already in bouncing state
            this.y <= 0 && this.y >= this._boundaries[1] &&
            (newX != this.x || newY != this.y)) {
            this.scroller.addEventListener('transitionEnd', this._handleTransitionEnd, false);
            this.scroller.addEventListener('webkitTransitionEnd', this._handleTransitionEnd, false);

            this.scrollTo(newX, newY, time);
        } else {
            bounceBack.call(this);
        }

        // TODO wait with eventing for transition
        if (ev.type === 'pointercancel') {
            triggerEvent.call(this, 'scrollcancel', {
                pointerId: this._curPointer
            });
        }

        triggerEvent.call(this, 'scrollend', {
            pointerId: this._curPointer
        });
    }

    function handleTransitionEnd(ev) {
        this.scroller.removeEventListener('transitionEnd', this._handleTransitionEnd, false);
        this.scroller.removeEventListener('webkitTransitionEnd', this._handleTransitionEnd, false);

        bounceBack.call(this);
    }

    function getCurrentPosition() {
        var style = window.getComputedStyle(this.scroller, null),
            x, y;

        style = (style['transform'] || style['webkitTransform']).split(')')[0].split(', ');
        x = +(style[12] || style[4]);
        y = +(style[13] || style[5]);

        return [x, y];
    }

    function moveTo(x, y) {
        this.x = Math.round(x);
        this.y = Math.round(y);

        var transform = 'translate(' + this.x + 'px,' + this.y + 'px)',
            translateZ = ' translateZ(0)';

        this.scroller.style['transform'] =
        this.scroller.style['webkitTransform'] = transform + translateZ;
    }

    function bounceBack() {
        var newX = this.x > 0 ? 0 :
            this.x < this._boundaries[0] ? this._boundaries[0] :
            this.x,
            newY = this.y > 0 ? 0 :
            this.y < this._boundaries[1] ? this._boundaries[1] :
            this.y;

        if (this.options.bounce) {
            this.scrollTo(newX, newY, this.options.bounceTime);
        }
    }

    function Sv(el, options) {
        var opt = options || {};

        this.view = typeof el === 'string' ? document.querySelector(el) : el;
        this.scroller = this.view.children[0];

        this.options = {
            start: opt.start || [0, 0],

            scrollX: opt.scrollX || false,
            scrollY: opt.scrollY || true,

            momentum: opt.momentum || true,

            bounce: opt.bounce || true,
            bounceTime: opt.bounceTime || 600
        };

        // define initial state
        this._enabled = true;
        this._curPointer = false;

        // event handler
        this._handleStart = handleStart.bind(this);
        this._handleMove = handleMove.bind(this);
        this._handleEnd = handleEnd.bind(this);
        this._handleTransitionEnd = handleTransitionEnd.bind(this);

        this.view.addEventListener('pointerdown', this._handleStart, false);

        // inital scroll
        this.scrollTo(this.options.start[0], this.options.start[1]);
    }

    Sv.prototype = {
        version: '0.0.0',

        enable: function (cond) {
            this._enabled = cond === false ? cond : true;
        },

        destroy: function () {

        },

        cancel: function (pointerId) {

        },

        scrollTo: function (x, y, time, easing) {
            var ease = easing || easingFn.circular;

            this.scroller.style['transitionTimingFunction'] =
            this.scroller.style['webkitTransitionTimingFunction'] = ease.transition;
            this.scroller.style['transitionDuration'] =
            this.scroller.style['webkitTransitionDuration'] = (time || 0) + 'ms';

            moveTo.call(this, x, y);
        }
    };

    window.ScrollView = Sv;

}(this));
<commit_msg>create math object<commit_after>(function (window) {

    var easingFn = {
        quadratic: {
            transition: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)'
        },
        circular: {
            transition: 'cubic-bezier(0.1, 0.57, 0.1, 1)'
        },
        back: {
            transition: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)'
        },
        bounce: {
            transition: 'cubic-bezier(0,0,.5,1.2)'
        }
    };

    // helpers
    var math = {
        distance: function (start, end) {
            return end - start;
        },
        velocity: function (distance, duration) {
            return Math.abs(distance) / duration; // px/ms
        },
        direction: function (delta) {
            return delta < 0 ? -1 : delta > 0 ? 1 : 0;
        },
        inertia: function (current, direction, velocity, lower, upper, deceleration) {
            var destination, duration,
                t, a = deceleration || 0.0006;

            t = velocity / deceleration; // a = v / t
            destination = current + (velocity * (t * t) * 0.5 * deceleration * direction);

            if (destination < lower || destination > upper) {
                destination = destination < lower ? lower :
                    destination > upper ? upper :
                    destination;
                t = Math.sqrt(Math.abs(destination - current) / (velocity * 0.5 * deceleration * direction));
            }

            return {
                destination: Math.round(destination),
                duration: parseFloat(t)
            };
        }
    };

    function triggerEvent(type, data) {
        var ev = new CustomEvent(type, {
            detail: data || {},
            bubbles: true,
            cancelable: true
        });

        this.view.dispatchEvent(ev);
    }

    // private functions
    function handleStart(ev) {

        if (!this._enabled || this._curPointer) {
            return;
        }

        var timestamp = new Date().getTime();

        this._curPointer = ev.pointerId; // lock for pointer
        this._hasMoved = false; // if scrolling has started

        this._boundaries = [
            -this.view.scrollWidth + this.view.clientWidth,
            -this.view.scrollHeight + this.view.clientHeight
        ];

        // make sure it is not moving
        moveTo.apply(this, getCurrentPosition.call(this));

        // previous point for delta calculation
        this._lastPoint = {
            timestamp: timestamp,
            x: ev.pageX,
            y: ev.pageY
        };

        // last key frame for velocity caluclation
        this._lastKeyFrame = {
            timestamp: timestamp,
            x: this.x,
            y: this.y
        };

        // bind events
        document.addEventListener('pointerup', this._handleEnd, false);
        document.addEventListener('pointercancel', this._handleEnd, false);
        document.addEventListener('pointermove', this._handleMove, false);
    }

    function handleMove(ev) {
        if (!this._enabled || this._curPointer !== ev.pointerId) {
            return;
        }

        var deltaX = this.options.scrollX ? math.distance(this._lastPoint.x, ev.pageX) : 0,
            deltaY = this.options.scrollY ? math.distance(this._lastPoint.y, ev.pageY) : 0,
            timestamp = new Date().getTime(),
            newX, newY;

        this._lastPoint = {
            timestamp: timestamp,
            x: ev.pageX,
            y: ev.pageY
        };

        newX = this.x + deltaX;
        newY = this.y + deltaY;

        // TODO add WIGGLE_THRESHOLD
        // We need to move at least 10 pixels for the scrolling to initiate
        if (Math.abs(deltaX) < 10 && Math.abs(deltaY) < 10) {
            return;
        }

        // consider boundaries
        newX = newX > 0 ? // upper
            this.options.bounce ? this.x + deltaX / 3 :
            0 :
            newX;
        newY = newY > 0 ? // upper
            this.options.bounce ? this.y + deltaY / 3 :
            0 :
            newY;
        newX = newX < this._boundaries[0] ? // lower
            this.options.bounce ? this.x + deltaX / 3 :
            this._boundaries[0] :
            newX;
        newY = newY < this._boundaries[1] ? // lower
            this.options.bounce ? this.y + deltaY / 3 :
            this._boundaries[1] :
            newY;

        // initial move
        if (!this._hasMoved) {
            this._hasMoved = true;
            triggerEvent.call(this, 'scrollstart', {
                pointerId: this._curPointer,
                x: this.x,
                y: this.y
            });
        }

        // save new keyframe every 300ms
        if (timestamp - this._lastKeyFrame.timestamp > 300) {
            this._lastKeyFrame = {
                timestamp: timestamp,
                x: newX,
                y: newY
            }
        }

        this.scrollTo(newX, newY);

        triggerEvent.call(this, 'scroll', {
            pointerId: this._curPointer,
            direction: [
                math.direction(deltaX),
                math.direction(deltaY)
            ],
            x: newX,
            y: newY
        });
    }

    function handleEnd(ev) {
        if (!this._enabled ||
            this._curPointer !== ev.pointerId) {
            return;
        }

        var duration = new Date().getTime() - this._lastKeyFrame.timestamp,
            deltaX = this.options.scrollX ? math.distance(this._lastPoint.x, ev.pageX) : 0,
            deltaY = this.options.scrollY ? math.distance(this._lastPoint.y, ev.pageY) : 0,
            distance, velocity, momentum, time,
            newX = this.x + deltaX,
            newY = this.y + deltaY;

        // reset state
        this._curPointer = null;

        document.removeEventListener('pointerup', this._handleEnd, false);
        document.removeEventListener('pointercancel', this._handleEnd, false);
        document.removeEventListener('pointermove', this._handleMove, false);
        this.scrollTo(newX, newY);

        if (!this._hasMoved) { // has never scrolled
            return
        }

        // start momentum animation if needed
        if (this.options.momentum && duration < 300) {
            distance = [
                math.distance(this._lastKeyFrame.x, this.x),
                math.distance(this._lastKeyFrame.y, this.y)
            ];

            direction = [
                math.direction(distance[0]),
                math.direction(distance[1])
            ];

            velocity = [
                math.velocity(distance[0], duration),
                math.velocity(distance[1], duration)
            ];

            momentum = [ //inertia
                math.inertia(this.x, direction[0], velocity[0], this._boundaries[0], 0),
                math.inertia(this.y, direction[1], velocity[1], this._boundaries[1], 0)
            ];

            newX = momentum[0].destination;
            newY = momentum[1].destination;
            time = Math.max(momentum[0].duration, momentum[1].duration);
        }

        if (this.x <= 0 && this.x >= this._boundaries[0] && // not already in bouncing state
            this.y <= 0 && this.y >= this._boundaries[1] &&
            (newX != this.x || newY != this.y)) {
            this.scroller.addEventListener('transitionEnd', this._handleTransitionEnd, false);
            this.scroller.addEventListener('webkitTransitionEnd', this._handleTransitionEnd, false);

            this.scrollTo(newX, newY, time);
        } else {
            bounceBack.call(this);
        }

        // TODO wait with eventing for transition
        if (ev.type === 'pointercancel') {
            triggerEvent.call(this, 'scrollcancel', {
                pointerId: this._curPointer
            });
        }

        triggerEvent.call(this, 'scrollend', {
            pointerId: this._curPointer
        });
    }

    function handleTransitionEnd(ev) {
        this.scroller.removeEventListener('transitionEnd', this._handleTransitionEnd, false);
        this.scroller.removeEventListener('webkitTransitionEnd', this._handleTransitionEnd, false);

        bounceBack.call(this);
    }

    function getCurrentPosition() {
        var style = window.getComputedStyle(this.scroller, null),
            x, y;

        style = (style['transform'] || style['webkitTransform']).split(')')[0].split(', ');
        x = +(style[12] || style[4]);
        y = +(style[13] || style[5]);

        return [x, y];
    }

    function moveTo(x, y) {
        this.x = Math.round(x);
        this.y = Math.round(y);

        var transform = 'translate(' + this.x + 'px,' + this.y + 'px)',
            translateZ = ' translateZ(0)';

        this.scroller.style['transform'] =
        this.scroller.style['webkitTransform'] = transform + translateZ;
    }

    function bounceBack() {
        var newX = this.x > 0 ? 0 :
            this.x < this._boundaries[0] ? this._boundaries[0] :
            this.x,
            newY = this.y > 0 ? 0 :
            this.y < this._boundaries[1] ? this._boundaries[1] :
            this.y;

        if (this.options.bounce) {
            this.scrollTo(newX, newY, this.options.bounceTime);
        }
    }

    function Sv(el, options) {
        var opt = options || {};

        this.view = typeof el === 'string' ? document.querySelector(el) : el;
        this.scroller = this.view.children[0];

        this.options = {
            start: opt.start || [0, 0],

            scrollX: opt.scrollX || false,
            scrollY: opt.scrollY || true,

            momentum: opt.momentum || true,

            bounce: opt.bounce || true,
            bounceTime: opt.bounceTime || 600
        };

        // define initial state
        this._enabled = true;
        this._curPointer = false;

        // event handler
        this._handleStart = handleStart.bind(this);
        this._handleMove = handleMove.bind(this);
        this._handleEnd = handleEnd.bind(this);
        this._handleTransitionEnd = handleTransitionEnd.bind(this);

        this.view.addEventListener('pointerdown', this._handleStart, false);

        // inital scroll
        this.scrollTo(this.options.start[0], this.options.start[1]);
    }

    Sv.prototype = {
        version: '0.0.0',

        enable: function (cond) {
            this._enabled = cond === false ? cond : true;
        },

        destroy: function () {

        },

        cancel: function (pointerId) {

        },

        scrollTo: function (x, y, time, easing) {
            var ease = easing || easingFn.circular;

            this.scroller.style['transitionTimingFunction'] =
            this.scroller.style['webkitTransitionTimingFunction'] = ease.transition;
            this.scroller.style['transitionDuration'] =
            this.scroller.style['webkitTransitionDuration'] = (time || 0) + 'ms';

            moveTo.call(this, x, y);
        },

    };

    window.ScrollView = Sv;

}(this));
<|endoftext|>


<commit_before>const _ = require("lodash");

const spyManager = require("./spy_manager")();

function getItem(list, title){
  let obj = _.find(list, (spy)=>spy.title === title);
  if (_.isEmpty(obj)) return null;
  return obj;
}

function spyCreator(Module, manager){
  return function(mod){
    let title = (_.isString(mod)) ? mod : mod.title;
    let spy    = manager.addSpy(mod).getSpy(title);
    let revert = Module.__set__(title, spy);
    return {revert, title, spy};
  };
}

function resetSpy(spy){
  if (_.isPlainObject(spy)){
    _.forIn(spy, function(v, k){
      v.calls.reset();
    });
  } else {
    spy.calls.reset();
  }
}

module.exports =  function(Module){
  let spies  = [];
  let addSpy = spyCreator(Module, spyManager);
  let obj = {
    add: (modules)=>obj.addSpy(modules)
    , addSpy: (modules)=>{
      if (_.isArray(modules)){
        modules = _.map(modules, (m)=>{
          return addSpy(m);
        });

        spies = spies.concat(modules);
        return obj;
      }

      if (_.isString(modules) ||
        _.isObject(modules)){
        spies.push(addSpy(modules));
      }

      return obj;
    }
    , get: (title)=>obj.getSpy(title)
    , getSpy: (title)=>{
      let obj = getItem(spies, title);
      if (_.isNull(obj)) return null;
      return obj.spy;
    }
    , return: (title, mod_obj)=>{
      let mod = getItem(spies, title);
      if (_.isNull(mod)){
        var new_spy = (mod_obj) ? {title: title, opts: [mod_obj]} : title;
        obj.addSpy(new_spy);
        mod = getItem(spies, title);
      }

      mod = mod.spy;
      if (mod_obj) mod = mod[mod_obj];
      return function(func, value){
        mod.and[func](value);
      };
    }
    , returnObj: (title)=>{
      let mod = getItem(spies, title).spy;
      return function(opts){
        _.forEach(opts, (opt)=>{
          mod[opt.title].and[opt.func](opt.value);
        });
      };
    }
    , revertAll: ()=>{
      _.forEach(spies, (mod)=>{
        resetSpy(mod.spy);
        mod.revert();
        // Module.__ResetDependency__(mod.title);
      });
      spies = [];
    }
    , revertSpy: (title)=>{
      let mod = getItem(spies, title);
      resetSpy(mod.spy);
      mod.revert();
      // Module.__ResetDependency__(mod.title);
      spies = _.reject(spies, (s)=>s.title === title);
      return mod;
    }
    , setSpies: (spy_list)=>{
      _.forEach(spy_list, (sl)=>{
        let mod = getItem(spies, sl.title);
        mod.spy.and[sl.func](sl.value);
      });
    }
  };

  return obj;
};
<commit_msg>fixes to helper<commit_after>const _ = require("lodash");

const spyManager = require("./spy_manager")();

function getItem(list, title){
  let obj = _.find(list, (spy)=>spy.title === title);
  if (_.isEmpty(obj)) return null;
  return obj;
}

function spyCreator(Module, manager){
  return function(mod){
    let title = (_.isString(mod)) ? mod : mod.title;
    let spy    = manager.addSpy(mod).getSpy(title);
    let revert = Module.__set__(title, spy);
    return {revert, title, spy};
  };
}

function resetSpy(spy){
  if (_.isPlainObject(spy)){
    _.forIn(spy, function(v, k){
      v.calls.reset();
    });
  } else {
    spy.calls.reset();
  }
}

module.exports =  function(Module){
  let spies  = [];
  let addSpy = spyCreator(Module, spyManager);
  let obj = {
    add: (modules)=>obj.addSpy(modules)
    , addSpy: (modules)=>{
      if (_.isArray(modules)){
        modules = _.map(modules, (m)=>{
          return addSpy(m);
        });

        spies = spies.concat(modules);
        return obj;
      }

      if (_.isString(modules) ||
        _.isObject(modules)){
        spies.push(addSpy(modules));
      }

      return obj;
    }
    , get: (title)=>obj.getSpy(title)
    , getSpy: (title)=>{
      let obj = getItem(spies, title);
      if (_.isNull(obj)) return null;
      return obj.spy;
    }
    , return: (title, mod_obj)=>{
      let mod = getItem(spies, title);
      if (_.isNull(mod)){
        var new_spy = (mod_obj) ? {title: title, opts: [mod_obj]} : title;
        obj.addSpy(new_spy);
        mod = getItem(spies, title);
      }

      mod = mod.spy;
      if (mod_obj) mod = mod[mod_obj];
      return function(func, value){
        mod.and[func](value);
      };
    }
    , returnObj: (title)=>{
      let mod = getItem(spies, title).spy;
      return function(opts){
        _.forEach(opts, (opt)=>{
          mod[opt.title].and[opt.func](opt.value);
        });
      };
    }
    , revertAll: ()=>{
      _.forEach(spies, (mod)=>{
        resetSpy(mod.spy);
        // mod.revert();
        Module.__ResetDependency__(mod.title);
      });
      spies = [];
    }
    , revertSpy: (title)=>{
      let mod = getItem(spies, title);
      resetSpy(mod.spy);
      mod.revert();
      Module.__ResetDependency__(mod.title);
      // spies = _.reject(spies, (s)=>s.title === title);
      return mod;
    }
    , setSpies: (spy_list)=>{
      _.forEach(spy_list, (sl)=>{
        let mod = getItem(spies, sl.title);
        mod.spy.and[sl.func](sl.value);
      });
    }
  };

  return obj;
};
<|endoftext|>


<commit_before>import {
  Array as SVGArray,
  Container,
  Defs,
  Element,
  extend,
  find,
  namespaces as ns,
  nodeOrNew,
  utils,
  wrapWithAttrCheck
} from '@svgdotjs/svg.js'

export default class Filter extends Element {
  constructor (node) {
    super(nodeOrNew('filter', node), node)

    this.$source = 'SourceGraphic'
    this.$sourceAlpha = 'SourceAlpha'
    this.$background = 'BackgroundImage'
    this.$backgroundAlpha = 'BackgroundAlpha'
    this.$fill = 'FillPaint'
    this.$stroke = 'StrokePaint'
    this.$autoSetIn = true
  }

  put (element, i) {
    element = super.put(element, i)

    if (!element.attr('in') && this.$autoSetIn) {
      element.attr('in', this.$source)
    }
    if (!element.attr('result')) {
      element.attr('result', element.id())
    }

    return element
  }

  // Unmask all masked elements and remove itself
  remove () {
    // unmask all targets
    this.targets().each('unfilter')

    // remove mask from parent
    return super.remove()
  }

  targets () {
    return find('svg [filter*="' + this.id() + '"]')
  }

  toString () {
    return 'url(#' + this.id() + ')'
  }
}

// Create Effect class
class Effect extends Element {
  constructor (node) {
    super(node, node)
    this.result(this.id())
  }
  in (effect) {
    // Act as getter
    if (effect == null) {
      const _in = this.attr('in')
      const ref = this.parent() && this.parent().find(`[result="${_in}"]`)[0]
      return ref || _in
    }

    // Avr as setter
    return this.attr('in', effect)
  }
  // Named result
  result (result) {
    return this.attr('result', result)
  }
  // Stringification
  toString () {
    return this.result()
  }
}

// This function takes an array with attr keys and sets for every key the
// attribute to the value of one paramater
// getAttrSetter(['a', 'b']) becomes this.attr({a: param1, b: param2})
const getAttrSetter = (params) => {
  return function (...args) {
    for (let i = params.length; i--;) {
      if (args[i] != null) {
        this.attr(params[i], args[i])
      }
    }
  }
}

const updateFunctions = {
  blend: getAttrSetter(['in', 'in2', 'mode']),
  // ColorMatrix effect
  colorMatrix: getAttrSetter(['type', 'values']),
  // Composite effect
  composite: getAttrSetter(['in', 'in2', 'operator']),
  // ConvolveMatrix effect
  convolveMatrix: function (matrix) {
    matrix = new SVGArray(matrix).toString()

    this.attr({
      order: Math.sqrt(matrix.split(' ').length),
      kernelMatrix: matrix
    })
  },
  // DiffuseLighting effect
  diffuseLighting: getAttrSetter(['surfaceScale', 'lightingColor', 'diffuseConstant', 'kernelUnitLength']),
  // DisplacementMap effect
  displacementMap: getAttrSetter(['in', 'in2', 'scale', 'xChannelSelector', 'yChannelSelector']),
  // DropShadow effect
  dropShadow: getAttrSetter(['in', 'dx', 'dy', 'stdDeviation']),
  // Flood effect
  flood: getAttrSetter(['color', 'opacity']),
  // Gaussian Blur effect
  gaussianBlur: function (x = 0, y = x) {
    this.attr('stdDeviation', x + ' ' + y)
  },
  // Image effect
  image: function (src) {
    this.attr('href', src, ns.xlink)
  },
  // Morphology effect
  morphology: getAttrSetter(['operator', 'radius']),
  // Offset effect
  offset: getAttrSetter(['dx', 'dy']),
  // SpecularLighting effect
  specularLighting: getAttrSetter(['surfaceScale', 'lightingColor', 'diffuseConstant', 'specularExponent', 'kernelUnitLength']),
  // Tile effect
  tile: getAttrSetter([]),
  // Turbulence effect
  turbulence: getAttrSetter(['baseFrequency', 'numOctaves', 'seed', 'stitchTiles', 'type'])
}

const filterNames = [
  'blend',
  'colorMatrix',
  'componentTransfer',
  'composite',
  'convolveMatrix',
  'diffuseLighting',
  'displacementMap',
  'dropShadow',
  'flood',
  'gaussianBlur',
  'image',
  'merge',
  'morphology',
  'offset',
  'specularLighting',
  'tile',
  'turbulence'
]

// For every filter create a class
filterNames.forEach((effect) => {
  const name = utils.capitalize(effect)
  const fn = updateFunctions[effect]

  Filter[name + 'Effect'] = class extends Effect {
    constructor (node) {
      super(nodeOrNew('fe' + name, node), node)
    }

    // This function takes all parameters from the factory call
    // and updates the attributes according to the updateFunctions
    update (args) {
      fn.apply(this, args)
      return this
    }
  }

  // Add factory function to filter
  // Allow to pass a function or object
  // The attr object is catched from "wrapWithAttrCheck"
  Filter.prototype[effect] = wrapWithAttrCheck(function (fn, ...args) {
    const effect = new Filter[name + 'Effect']()

    if (fn == null) return this.put(effect)

    // For Effects which can take children, a function is allowed
    if (typeof fn === 'function') {
      fn.call(effect, effect)
    } else {
      // In case it is not a function, add it to arguments
      args.unshift(fn)
    }
    return this.put(effect).update(args)
  })
})

// Correct factories which are not that simple
extend(Filter, {
  merge (arrayOrFn) {
    const node = this.put(new Filter.MergeEffect())

    // If a function was passed, execute it
    // That makes stuff like this possible:
    // filter.merge((mergeEffect) => mergeEffect.mergeNode(in))
    if (typeof arrayOrFn === 'function') {
      arrayOrFn.call(node, node)
      return node
    }

    // Check if first child is an array, otherwise use arguments as array
    var children = arrayOrFn instanceof Array ? arrayOrFn : [...arguments]

    children.forEach((child) => {
      if (child instanceof Filter.MergeNode) {
        node.put(child)
      } else {
        node.mergeNode(child)
      }
    })

    return node
  },
  componentTransfer (components = {}) {
    const node = this.put(new Filter.ComponentTransferEffect())

    if (typeof components === 'function') {
      components.call(node, node)
      return node
    }

    // If no component is set, we use the given object for all components
    if (!components.r && !components.g && !components.b && !components.a) {
      const temp = components
      components = {
        r: temp, g: temp, b: temp, a: temp
      }
    }

    for (let c in components) {
      // components[c] has to hold an attributes object
      node.add(new Filter['Func' + c.toUpperCase()](components[c]))
    }

    return node
  }
})

const filterChildNodes = [
  'distantLight',
  'pointLight',
  'spotLight',
  'mergeNode',
  'FuncR',
  'FuncG',
  'FuncB',
  'FuncA'
]

filterChildNodes.forEach((child) => {
  const name = utils.capitalize(child)
  Filter[name] = class extends Effect {
    constructor (node) {
      super(nodeOrNew('fe' + name, node), node)
    }
  }
})

const componentFuncs = [
  'funcR',
  'funcG',
  'funcB',
  'funcA'
]

// Add an update function for componentTransfer-children
componentFuncs.forEach(function (c) {
  const _class = Filter[utils.capitalize(c)]
  const fn = wrapWithAttrCheck(function () {
    return this.put(new _class())
  })

  Filter.ComponentTransferEffect.prototype[c] = fn
})

const lights = [
  'distantLight',
  'pointLight',
  'spotLight'
]

// Add light sources factories to lightining effects
lights.forEach((light) => {
  const _class = Filter[utils.capitalize(light)]
  const fn = wrapWithAttrCheck(function () {
    return this.put(new _class())
  })

  Filter.DiffuseLightingEffect.prototype[light] = fn
  Filter.SpecularLightingEffect.prototype[light] = fn
})

extend(Filter.MergeEffect, {
  mergeNode (_in) {
    return this.put(new Filter.MergeNode()).attr('in', _in)
  }
})

// add .filter function
extend(Defs, {
  // Define filter
  filter: function (block) {
    var filter = this.put(new Filter())

    /* invoke passed block */
    if (typeof block === 'function') { block.call(filter, filter) }

    return filter
  }
})

extend(Container, {
  // Define filter on defs
  filter: function (block) {
    return this.defs().filter(block)
  }
})

extend(Element, {
  // Create filter element in defs and store reference
  filterWith: function (block) {
    const filter = block instanceof Filter
      ? block : this.defs().filter(block)

    return this.attr('filter', filter)
  },
  // Remove filter
  unfilter: function (remove) {
    /* remove filter attribute */
    return this.attr('filter', null)
  },
  filterer () {
    return this.reference('filter')
  }
})

// chaining
var chainingEffects = {
  // Blend effect
  blend: function (in2, mode) {
    return this.parent() && this.parent().blend(this, in2, mode) // pass this as the first input
  },
  // ColorMatrix effect
  colorMatrix: function (type, values) {
    return this.parent() && this.parent().colorMatrix(type, values).in(this)
  },
  // ComponentTransfer effect
  componentTransfer: function (components) {
    return this.parent() && this.parent().componentTransfer(components).in(this)
  },
  // Composite effect
  composite: function (in2, operator) {
    return this.parent() && this.parent().composite(this, in2, operator) // pass this as the first input
  },
  // ConvolveMatrix effect
  convolveMatrix: function (matrix) {
    return this.parent() && this.parent().convolveMatrix(matrix).in(this)
  },
  // DiffuseLighting effect
  diffuseLighting: function (surfaceScale, lightingColor, diffuseConstant, kernelUnitLength) {
    return this.parent() && this.parent().diffuseLighting(surfaceScale, diffuseConstant, kernelUnitLength).in(this)
  },
  // DisplacementMap effect
  displacementMap: function (in2, scale, xChannelSelector, yChannelSelector) {
    return this.parent() && this.parent().displacementMap(this, in2, scale, xChannelSelector, yChannelSelector) // pass this as the first input
  },
  // DisplacementMap effect
  dropShadow: function (x, y, stdDeviation) {
    return this.parent() && this.parent().dropShadow(this, x, y, stdDeviation).in(this) // pass this as the first input
  },
  // Flood effect
  flood: function (color, opacity) {
    return this.parent() && this.parent().flood(color, opacity) // this effect dont have inputs
  },
  // Gaussian Blur effect
  gaussianBlur: function (x, y) {
    return this.parent() && this.parent().gaussianBlur(x, y).in(this)
  },
  // Image effect
  image: function (src) {
    return this.parent() && this.parent().image(src) // this effect dont have inputs
  },
  // Merge effect
  merge: function (arg) {
    arg = arg instanceof Array ? arg : [...arg]
    return this.parent() && this.parent().merge(this, ...arg) // pass this as the first argument
  },
  // Morphology effect
  morphology: function (operator, radius) {
    return this.parent() && this.parent().morphology(operator, radius).in(this)
  },
  // Offset effect
  offset: function (dx, dy) {
    return this.parent() && this.parent().offset(dx, dy).in(this)
  },
  // SpecularLighting effect
  specularLighting: function (surfaceScale, lightingColor, diffuseConstant, specularExponent, kernelUnitLength) {
    return this.parent() && this.parent().specularLighting(surfaceScale, diffuseConstant, specularExponent, kernelUnitLength).in(this)
  },
  // Tile effect
  tile: function () {
    return this.parent() && this.parent().tile().in(this)
  },
  // Turbulence effect
  turbulence: function (baseFrequency, numOctaves, seed, stitchTiles, type) {
    return this.parent() && this.parent().turbulence(baseFrequency, numOctaves, seed, stitchTiles, type).in(this)
  }
}

extend(Effect, chainingEffects)

// Effect-specific extensions
extend(Filter.MergeEffect, {
  in: function (effect) {
    if (effect instanceof Filter.MergeNode) {
      this.add(effect, 0)
    } else {
      this.add(new Filter.MergeNode().in(effect), 0)
    }

    return this
  }
})

extend([Filter.CompositeEffect, Filter.BlendEffect, Filter.DisplacementMapEffect], {
  in2: function (effect) {
    if (effect == null) {
      const in2 = this.attr('in2')
      const ref = this.parent() && this.parent().find(`[result="${in2}"]`)[0]
      return ref || in2
    }
    return this.attr('in2', effect)
  }
})

// Presets
Filter.filter = {
  sepiatone: [ 0.343, 0.669, 0.119, 0, 0,
    0.249, 0.626, 0.130, 0, 0,
    0.172, 0.334, 0.111, 0, 0,
    0.000, 0.000, 0.000, 1, 0 ]
}
<commit_msg>fix flood filter<commit_after>import {
  Array as SVGArray,
  Container,
  Defs,
  Element,
  extend,
  find,
  namespaces as ns,
  nodeOrNew,
  utils,
  wrapWithAttrCheck
} from '@svgdotjs/svg.js'

export default class Filter extends Element {
  constructor (node) {
    super(nodeOrNew('filter', node), node)

    this.$source = 'SourceGraphic'
    this.$sourceAlpha = 'SourceAlpha'
    this.$background = 'BackgroundImage'
    this.$backgroundAlpha = 'BackgroundAlpha'
    this.$fill = 'FillPaint'
    this.$stroke = 'StrokePaint'
    this.$autoSetIn = true
  }

  put (element, i) {
    element = super.put(element, i)

    if (!element.attr('in') && this.$autoSetIn) {
      element.attr('in', this.$source)
    }
    if (!element.attr('result')) {
      element.attr('result', element.id())
    }

    return element
  }

  // Unmask all masked elements and remove itself
  remove () {
    // unmask all targets
    this.targets().each('unfilter')

    // remove mask from parent
    return super.remove()
  }

  targets () {
    return find('svg [filter*="' + this.id() + '"]')
  }

  toString () {
    return 'url(#' + this.id() + ')'
  }
}

// Create Effect class
class Effect extends Element {
  constructor (node) {
    super(node, node)
    this.result(this.id())
  }
  in (effect) {
    // Act as getter
    if (effect == null) {
      const _in = this.attr('in')
      const ref = this.parent() && this.parent().find(`[result="${_in}"]`)[0]
      return ref || _in
    }

    // Avr as setter
    return this.attr('in', effect)
  }
  // Named result
  result (result) {
    return this.attr('result', result)
  }
  // Stringification
  toString () {
    return this.result()
  }
}

// This function takes an array with attr keys and sets for every key the
// attribute to the value of one paramater
// getAttrSetter(['a', 'b']) becomes this.attr({a: param1, b: param2})
const getAttrSetter = (params) => {
  return function (...args) {
    for (let i = params.length; i--;) {
      if (args[i] != null) {
        this.attr(params[i], args[i])
      }
    }
  }
}

const updateFunctions = {
  blend: getAttrSetter(['in', 'in2', 'mode']),
  // ColorMatrix effect
  colorMatrix: getAttrSetter(['type', 'values']),
  // Composite effect
  composite: getAttrSetter(['in', 'in2', 'operator']),
  // ConvolveMatrix effect
  convolveMatrix: function (matrix) {
    matrix = new SVGArray(matrix).toString()

    this.attr({
      order: Math.sqrt(matrix.split(' ').length),
      kernelMatrix: matrix
    })
  },
  // DiffuseLighting effect
  diffuseLighting: getAttrSetter(['surfaceScale', 'lightingColor', 'diffuseConstant', 'kernelUnitLength']),
  // DisplacementMap effect
  displacementMap: getAttrSetter(['in', 'in2', 'scale', 'xChannelSelector', 'yChannelSelector']),
  // DropShadow effect
  dropShadow: getAttrSetter(['in', 'dx', 'dy', 'stdDeviation']),
  // Flood effect
  flood: getAttrSetter(['flood-color', 'flood-opacity']),
  // Gaussian Blur effect
  gaussianBlur: function (x = 0, y = x) {
    this.attr('stdDeviation', x + ' ' + y)
  },
  // Image effect
  image: function (src) {
    this.attr('href', src, ns.xlink)
  },
  // Morphology effect
  morphology: getAttrSetter(['operator', 'radius']),
  // Offset effect
  offset: getAttrSetter(['dx', 'dy']),
  // SpecularLighting effect
  specularLighting: getAttrSetter(['surfaceScale', 'lightingColor', 'diffuseConstant', 'specularExponent', 'kernelUnitLength']),
  // Tile effect
  tile: getAttrSetter([]),
  // Turbulence effect
  turbulence: getAttrSetter(['baseFrequency', 'numOctaves', 'seed', 'stitchTiles', 'type'])
}

const filterNames = [
  'blend',
  'colorMatrix',
  'componentTransfer',
  'composite',
  'convolveMatrix',
  'diffuseLighting',
  'displacementMap',
  'dropShadow',
  'flood',
  'gaussianBlur',
  'image',
  'merge',
  'morphology',
  'offset',
  'specularLighting',
  'tile',
  'turbulence'
]

// For every filter create a class
filterNames.forEach((effect) => {
  const name = utils.capitalize(effect)
  const fn = updateFunctions[effect]

  Filter[name + 'Effect'] = class extends Effect {
    constructor (node) {
      super(nodeOrNew('fe' + name, node), node)
    }

    // This function takes all parameters from the factory call
    // and updates the attributes according to the updateFunctions
    update (args) {
      fn.apply(this, args)
      return this
    }
  }

  // Add factory function to filter
  // Allow to pass a function or object
  // The attr object is catched from "wrapWithAttrCheck"
  Filter.prototype[effect] = wrapWithAttrCheck(function (fn, ...args) {
    const effect = new Filter[name + 'Effect']()

    if (fn == null) return this.put(effect)

    // For Effects which can take children, a function is allowed
    if (typeof fn === 'function') {
      fn.call(effect, effect)
    } else {
      // In case it is not a function, add it to arguments
      args.unshift(fn)
    }
    return this.put(effect).update(args)
  })
})

// Correct factories which are not that simple
extend(Filter, {
  merge (arrayOrFn) {
    const node = this.put(new Filter.MergeEffect())

    // If a function was passed, execute it
    // That makes stuff like this possible:
    // filter.merge((mergeEffect) => mergeEffect.mergeNode(in))
    if (typeof arrayOrFn === 'function') {
      arrayOrFn.call(node, node)
      return node
    }

    // Check if first child is an array, otherwise use arguments as array
    var children = arrayOrFn instanceof Array ? arrayOrFn : [...arguments]

    children.forEach((child) => {
      if (child instanceof Filter.MergeNode) {
        node.put(child)
      } else {
        node.mergeNode(child)
      }
    })

    return node
  },
  componentTransfer (components = {}) {
    const node = this.put(new Filter.ComponentTransferEffect())

    if (typeof components === 'function') {
      components.call(node, node)
      return node
    }

    // If no component is set, we use the given object for all components
    if (!components.r && !components.g && !components.b && !components.a) {
      const temp = components
      components = {
        r: temp, g: temp, b: temp, a: temp
      }
    }

    for (let c in components) {
      // components[c] has to hold an attributes object
      node.add(new Filter['Func' + c.toUpperCase()](components[c]))
    }

    return node
  }
})

const filterChildNodes = [
  'distantLight',
  'pointLight',
  'spotLight',
  'mergeNode',
  'FuncR',
  'FuncG',
  'FuncB',
  'FuncA'
]

filterChildNodes.forEach((child) => {
  const name = utils.capitalize(child)
  Filter[name] = class extends Effect {
    constructor (node) {
      super(nodeOrNew('fe' + name, node), node)
    }
  }
})

const componentFuncs = [
  'funcR',
  'funcG',
  'funcB',
  'funcA'
]

// Add an update function for componentTransfer-children
componentFuncs.forEach(function (c) {
  const _class = Filter[utils.capitalize(c)]
  const fn = wrapWithAttrCheck(function () {
    return this.put(new _class())
  })

  Filter.ComponentTransferEffect.prototype[c] = fn
})

const lights = [
  'distantLight',
  'pointLight',
  'spotLight'
]

// Add light sources factories to lightining effects
lights.forEach((light) => {
  const _class = Filter[utils.capitalize(light)]
  const fn = wrapWithAttrCheck(function () {
    return this.put(new _class())
  })

  Filter.DiffuseLightingEffect.prototype[light] = fn
  Filter.SpecularLightingEffect.prototype[light] = fn
})

extend(Filter.MergeEffect, {
  mergeNode (_in) {
    return this.put(new Filter.MergeNode()).attr('in', _in)
  }
})

// add .filter function
extend(Defs, {
  // Define filter
  filter: function (block) {
    var filter = this.put(new Filter())

    /* invoke passed block */
    if (typeof block === 'function') { block.call(filter, filter) }

    return filter
  }
})

extend(Container, {
  // Define filter on defs
  filter: function (block) {
    return this.defs().filter(block)
  }
})

extend(Element, {
  // Create filter element in defs and store reference
  filterWith: function (block) {
    const filter = block instanceof Filter
      ? block : this.defs().filter(block)

    return this.attr('filter', filter)
  },
  // Remove filter
  unfilter: function (remove) {
    /* remove filter attribute */
    return this.attr('filter', null)
  },
  filterer () {
    return this.reference('filter')
  }
})

// chaining
var chainingEffects = {
  // Blend effect
  blend: function (in2, mode) {
    return this.parent() && this.parent().blend(this, in2, mode) // pass this as the first input
  },
  // ColorMatrix effect
  colorMatrix: function (type, values) {
    return this.parent() && this.parent().colorMatrix(type, values).in(this)
  },
  // ComponentTransfer effect
  componentTransfer: function (components) {
    return this.parent() && this.parent().componentTransfer(components).in(this)
  },
  // Composite effect
  composite: function (in2, operator) {
    return this.parent() && this.parent().composite(this, in2, operator) // pass this as the first input
  },
  // ConvolveMatrix effect
  convolveMatrix: function (matrix) {
    return this.parent() && this.parent().convolveMatrix(matrix).in(this)
  },
  // DiffuseLighting effect
  diffuseLighting: function (surfaceScale, lightingColor, diffuseConstant, kernelUnitLength) {
    return this.parent() && this.parent().diffuseLighting(surfaceScale, diffuseConstant, kernelUnitLength).in(this)
  },
  // DisplacementMap effect
  displacementMap: function (in2, scale, xChannelSelector, yChannelSelector) {
    return this.parent() && this.parent().displacementMap(this, in2, scale, xChannelSelector, yChannelSelector) // pass this as the first input
  },
  // DisplacementMap effect
  dropShadow: function (x, y, stdDeviation) {
    return this.parent() && this.parent().dropShadow(this, x, y, stdDeviation).in(this) // pass this as the first input
  },
  // Flood effect
  flood: function (color, opacity) {
    return this.parent() && this.parent().flood(color, opacity) // this effect dont have inputs
  },
  // Gaussian Blur effect
  gaussianBlur: function (x, y) {
    return this.parent() && this.parent().gaussianBlur(x, y).in(this)
  },
  // Image effect
  image: function (src) {
    return this.parent() && this.parent().image(src) // this effect dont have inputs
  },
  // Merge effect
  merge: function (arg) {
    arg = arg instanceof Array ? arg : [...arg]
    return this.parent() && this.parent().merge(this, ...arg) // pass this as the first argument
  },
  // Morphology effect
  morphology: function (operator, radius) {
    return this.parent() && this.parent().morphology(operator, radius).in(this)
  },
  // Offset effect
  offset: function (dx, dy) {
    return this.parent() && this.parent().offset(dx, dy).in(this)
  },
  // SpecularLighting effect
  specularLighting: function (surfaceScale, lightingColor, diffuseConstant, specularExponent, kernelUnitLength) {
    return this.parent() && this.parent().specularLighting(surfaceScale, diffuseConstant, specularExponent, kernelUnitLength).in(this)
  },
  // Tile effect
  tile: function () {
    return this.parent() && this.parent().tile().in(this)
  },
  // Turbulence effect
  turbulence: function (baseFrequency, numOctaves, seed, stitchTiles, type) {
    return this.parent() && this.parent().turbulence(baseFrequency, numOctaves, seed, stitchTiles, type).in(this)
  }
}

extend(Effect, chainingEffects)

// Effect-specific extensions
extend(Filter.MergeEffect, {
  in: function (effect) {
    if (effect instanceof Filter.MergeNode) {
      this.add(effect, 0)
    } else {
      this.add(new Filter.MergeNode().in(effect), 0)
    }

    return this
  }
})

extend([Filter.CompositeEffect, Filter.BlendEffect, Filter.DisplacementMapEffect], {
  in2: function (effect) {
    if (effect == null) {
      const in2 = this.attr('in2')
      const ref = this.parent() && this.parent().find(`[result="${in2}"]`)[0]
      return ref || in2
    }
    return this.attr('in2', effect)
  }
})

// Presets
Filter.filter = {
  sepiatone: [ 0.343, 0.669, 0.119, 0, 0,
    0.249, 0.626, 0.130, 0, 0,
    0.172, 0.334, 0.111, 0, 0,
    0.000, 0.000, 0.000, 1, 0 ]
}
<|endoftext|>


<commit_before>if('serviceWorker' in navigator) {
  navigator.serviceWorker.register('./sw.js').then(function(registration) {
    // Registration was successful 😊
    console.log('ServiceWorker registration successful with scope: ',
      registration.scope);
  }).catch(function(err) {
    // registration failed :(
    console.log('ServiceWorker registration failed: ', err);
  });
}
<commit_msg>Disable service worker on localhost<commit_after>(function() {
  if (location.hostname === 'localhost') {
    console.log('service worker disabled on localhost');
    return;
  }
  if(!('serviceWorker' in navigator)) {
    console.log('service worker not supported');
    return;
  }
  navigator.serviceWorker.register('./sw.js').then(function(registration) {
    // Registration was successful 😊
    console.log('ServiceWorker registration successful with scope: ',
      registration.scope);
  }).catch(function(err) {
    // registration failed :(
    console.log('ServiceWorker registration failed: ', err);
  });
})();
<|endoftext|>


<commit_before>'use strict';

import { expect } from 'chai';
import { parse } from '../autosuggest-highlight';

const testCases = [
  {
    it: 'supports no matches',
    input: [
      'Hello world',
      []
    ],
    output: [
      {
        text: 'Hello world',
        highlight: false
      }
    ]
  },
  {
    it: 'highlights a single match',
    input: [
      'Hello world',
      [[0, 4]]
    ],
    output: [
      {
        text: 'Hell',
        highlight: true
      },
      {
        text: 'o world',
        highlight: false
      }
    ]
  },
  {
    it: 'highlights multiple matches',
    input: [
      'Hello world',
      [[2, 4], [6, 8]]
    ],
    output: [
      {
        text: 'He',
        highlight: false
      },
      {
        text: 'll',
        highlight: true
      },
      {
        text: 'o ',
        highlight: false
      },
      {
        text: 'wo',
        highlight: true
      },
      {
        text: 'rld',
        highlight: false
      }
    ]
  }
];

testCases.forEach(testCase => {
  it(testCase.it, () => {
    expect(parse.apply(null, testCase.input)).to.deep.equal(testCase.output);
  });
});
<commit_msg>Clean up tests<commit_after>'use strict';

import { expect } from 'chai';
import { parse } from '../autosuggest-highlight';

const testCases = [
  {
    it: 'supports no matches',
    params: [
      'Hello world',
      []
    ],
    result: [
      {
        text: 'Hello world',
        highlight: false
      }
    ]
  },
  {
    it: 'highlights a single match',
    params: [
      'Hello world',
      [[0, 4]]
    ],
    result: [
      {
        text: 'Hell',
        highlight: true
      },
      {
        text: 'o world',
        highlight: false
      }
    ]
  },
  {
    it: 'highlights multiple matches',
    params: [
      'Hello world',
      [[2, 4], [6, 8]]
    ],
    result: [
      {
        text: 'He',
        highlight: false
      },
      {
        text: 'll',
        highlight: true
      },
      {
        text: 'o ',
        highlight: false
      },
      {
        text: 'wo',
        highlight: true
      },
      {
        text: 'rld',
        highlight: false
      }
    ]
  }
];

testCases.forEach(testCase => {
  it(testCase.it, () => {
    expect(parse(...testCase.params)).to.deep.equal(testCase.result);
  });
});
<|endoftext|>


<commit_before>/*
 * Traceur and Babel transpile hook for Loader
 */
(function(Loader) {
  var g = __global;

  function getTranspilerModule(loader, globalName) {
    return loader.newModule({ 'default': g[globalName], __useDefault: true });
  }
  var firstRun = true;

  // use Traceur by default
  Loader.prototype.transpiler = 'traceur';

  Loader.prototype.transpile = function(load) {
    var self = this;

    // pick up Transpiler modules from existing globals on first run if set
    if (firstRun) {
      if (g.traceur && !self.has('traceur'))
        self.set('traceur', getTranspilerModule(self, 'traceur'));
      if (g.babel && !self.has('babel'))
        self.set('babel', getTranspilerModule(self, 'babel'));
      firstRun = false;
    }

    return self['import'](self.transpiler).then(function(transpiler) {
      if (transpiler.__useDefault)
        transpiler = transpiler['default'];
      return 'var __moduleAddress = "' + load.address + '";' + (transpiler.Compiler ? traceurTranspile : babelTranspile).call(self, load, transpiler);
    });
  };

  Loader.prototype.instantiate = function(load) {
    var self = this;
    return Promise.resolve(self.normalize(self.transpiler))
    .then(function(transpilerNormalized) {
      // load transpiler as a global (avoiding System clobbering)
      if (load.name === transpilerNormalized) {
        return {
          deps: [],
          execute: function() {
            var curSystem = g.System;
            var curLoader = g.Reflect.Loader;
            // ensure not detected as CommonJS
            __eval('(function(require,exports,module){' + load.source + '})();', g, load);
            g.System = curSystem;
            g.Reflect.Loader = curLoader;
            return getTranspilerModule(self, load.name);
          }
        };
      }
    });
  };

  function traceurTranspile(load, traceur) {
    var options = this.traceurOptions || {};
    options.modules = 'instantiate';
    options.script = false;
    options.sourceMaps = 'inline';
    options.filename = load.address;
    options.inputSourceMap = load.metadata.sourceMap;
    options.moduleName = false;

    var compiler = new traceur.Compiler(options);
    var source = doTraceurCompile(load.source, compiler, options.filename);

    // add "!eval" to end of Traceur sourceURL
    // I believe this does something?
    source += '!eval';

    return source;
  }
  function doTraceurCompile(source, compiler, filename) {
    try {
      return compiler.compile(source, filename);
    }
    catch(e) {
      // traceur throws an error array
      throw e[0];
    }
  }

  function babelTranspile(load, babel) {
    var options = this.babelOptions || {};
    options.modules = 'system';
    options.sourceMap = 'inline';
    options.filename = load.address;
    options.code = true;
    options.ast = false;

    if (!options.blacklist)
      options.blacklist = ['react'];

    var source = babel.transform(load.source, options).code;

    // add "!eval" to end of Babel sourceURL
    // I believe this does something?
    return source + '\n//# sourceURL=' + load.address + '!eval';
  }

})(__global.LoaderPolyfill);<commit_msg>note subclassing caveat<commit_after>/*
 * Traceur and Babel transpile hook for Loader
 */
(function(Loader) {
  var g = __global;

  function getTranspilerModule(loader, globalName) {
    return loader.newModule({ 'default': g[globalName], __useDefault: true });
  }
  // NB this does not support sub-classing well
  var firstRun = true;

  // use Traceur by default
  Loader.prototype.transpiler = 'traceur';

  Loader.prototype.transpile = function(load) {
    var self = this;

    // pick up Transpiler modules from existing globals on first run if set
    if (firstRun) {
      if (g.traceur && !self.has('traceur'))
        self.set('traceur', getTranspilerModule(self, 'traceur'));
      if (g.babel && !self.has('babel'))
        self.set('babel', getTranspilerModule(self, 'babel'));
      firstRun = false;
    }

    return self['import'](self.transpiler).then(function(transpiler) {
      if (transpiler.__useDefault)
        transpiler = transpiler['default'];
      return 'var __moduleAddress = "' + load.address + '";' + (transpiler.Compiler ? traceurTranspile : babelTranspile).call(self, load, transpiler);
    });
  };

  Loader.prototype.instantiate = function(load) {
    var self = this;
    return Promise.resolve(self.normalize(self.transpiler))
    .then(function(transpilerNormalized) {
      // load transpiler as a global (avoiding System clobbering)
      if (load.name === transpilerNormalized) {
        return {
          deps: [],
          execute: function() {
            var curSystem = g.System;
            var curLoader = g.Reflect.Loader;
            // ensure not detected as CommonJS
            __eval('(function(require,exports,module){' + load.source + '})();', g, load);
            g.System = curSystem;
            g.Reflect.Loader = curLoader;
            return getTranspilerModule(self, load.name);
          }
        };
      }
    });
  };

  function traceurTranspile(load, traceur) {
    var options = this.traceurOptions || {};
    options.modules = 'instantiate';
    options.script = false;
    options.sourceMaps = 'inline';
    options.filename = load.address;
    options.inputSourceMap = load.metadata.sourceMap;
    options.moduleName = false;

    var compiler = new traceur.Compiler(options);
    var source = doTraceurCompile(load.source, compiler, options.filename);

    // add "!eval" to end of Traceur sourceURL
    // I believe this does something?
    source += '!eval';

    return source;
  }
  function doTraceurCompile(source, compiler, filename) {
    try {
      return compiler.compile(source, filename);
    }
    catch(e) {
      // traceur throws an error array
      throw e[0];
    }
  }

  function babelTranspile(load, babel) {
    var options = this.babelOptions || {};
    options.modules = 'system';
    options.sourceMap = 'inline';
    options.filename = load.address;
    options.code = true;
    options.ast = false;

    if (!options.blacklist)
      options.blacklist = ['react'];

    var source = babel.transform(load.source, options).code;

    // add "!eval" to end of Babel sourceURL
    // I believe this does something?
    return source + '\n//# sourceURL=' + load.address + '!eval';
  }

})(__global.LoaderPolyfill);<|endoftext|>